added settings.xml

This commit is contained in:
Hautvast, S. (Sander) 2025-09-11 20:38:08 +02:00
parent 2deae73132
commit f6e39067c8
11 changed files with 634 additions and 61 deletions

24
index.html Normal file
View file

@ -0,0 +1,24 @@
<!DOCTYPE html><html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Project Dependencies</title>
<style>
body {
font-family: sans-serif;
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
}
</style>
</head>
<body><h1>Project Dependencies</h1><table><thead><tr><th>Group ID</th><th>Artifact ID</th><th>Version</th></tr></thead><tbody></tbody></table></body></html>

View file

@ -1,7 +1,8 @@
use std::path::PathBuf;
use std::env;
use std::{env, fs};
use undeepend::maven::project::parse_project;
use undeepend::maven::reporter::report;
use undeepend::maven::settings::get_settings;
fn main() {
let args = std::env::args().collect::<Vec<String>>();
@ -11,15 +12,12 @@ fn main() {
PathBuf::from(&args[1])
};
let project = parse_project(&dir).unwrap();
// //
// // fs::write(
// // PathBuf::from("index.html"),
// // project.generate_dependency_html(),
// // )
// // .unwrap();
//
// report(&project);
for pom in project.iter(){
println!("{:?}", pom);
}
get_settings().unwrap();
fs::write(
PathBuf::from("index.html"),
project.generate_dependency_html(),
)
.unwrap();
report(&project);
}

View file

@ -1,5 +1,14 @@
use std::{env, sync::LazyLock};
pub mod metadata;
pub mod pom;
pub mod pom_parser;
pub mod project;
pub mod reporter;
pub mod settings;
pub const HOME: LazyLock<String> = LazyLock::new(|| env::var("HOME").unwrap());
pub const MAVEN_HOME: LazyLock<String> =
LazyLock::new(|| env::var("MAVEN_HOME").unwrap_or("".to_string()));
pub const CUSTOM_SETTINGS_LOCATION: LazyLock<String> =
LazyLock::new(|| env::var("SETTINGS_PATH").unwrap_or("".to_string()));

View file

@ -1,13 +1,9 @@
use std::collections::HashMap;
use std::env;
use std::fmt::Display;
use std::path::PathBuf;
use std::sync::LazyLock;
/// the maven object model
const HOME: LazyLock<String> = LazyLock::new(|| env::var("HOME").unwrap());
#[derive(PartialEq, Debug)]
pub struct Pom {
pub parent: Option<Parent>,
@ -80,13 +76,19 @@ impl Dependency {
use std::fmt;
use crate::maven::HOME;
impl Display for Dependency {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let version = self.version.clone().unwrap_or_else(|| "latest".to_string());
write!(
f,
"{}/{}/{}/{}-{}",
self.group_id.replace(".","/"), self.artifact_id, version, self.artifact_id, version
self.group_id.replace(".", "/"),
self.artifact_id,
version,
self.artifact_id,
version
)
}
}

View file

@ -7,7 +7,7 @@ use std::path::PathBuf;
/// parse the pom.xml into a Pom object (struct)
pub fn get_pom(home_dir: PathBuf, xml: impl Into<String>) -> Result<Pom, SaxError> {
let mut group_id = None;
let mut artefact_id = None;
let mut artifact_id = None;
let mut parent = None;
let mut version = None;
let mut name = None;
@ -21,7 +21,7 @@ pub fn get_pom(home_dir: PathBuf, xml: impl Into<String>) -> Result<Pom, SaxErro
for child in get_document(xml.into().as_str())?.root.children {
match child.name.as_str() {
"groupId" => group_id = child.text,
"artifactId" => artefact_id = child.text,
"artifactId" => artifact_id = child.text,
"parent" => parent = Some(get_parent(&child)),
"version" => version = child.text,
"name" => name = child.text,
@ -37,7 +37,7 @@ pub fn get_pom(home_dir: PathBuf, xml: impl Into<String>) -> Result<Pom, SaxErro
Ok(Pom {
parent,
group_id,
artifact_id: artefact_id.unwrap(),
artifact_id: artifact_id.unwrap(),
version,
name,
packaging,
@ -91,19 +91,19 @@ fn get_dependencies(element: Node) -> Vec<Dependency> {
fn get_dependency(element: Node) -> Dependency {
let mut grouo_id = None;
let mut artefact_id = None;
let mut artifact_id = None;
let mut version = None;
for node in element.children {
match node.name.as_str() {
"groupId" => grouo_id = node.text,
"artifactId" => artefact_id = node.text,
"artifactId" => artifact_id = node.text,
"version" => version = node.text,
_ => {}
}
}
Dependency {
group_id: grouo_id.unwrap(),
artifact_id: artefact_id.unwrap(),
artifact_id: artifact_id.unwrap(),
version,
}
}
@ -136,19 +136,19 @@ fn get_developer(element: Node) -> Developer {
fn get_parent(element: &Node) -> Parent {
let mut group_id = None;
let mut artefact_id = None;
let mut artifact_id = None;
let mut version = None;
for child in &element.children {
match child.name.as_str() {
"groupId" => group_id = child.text.clone(),
"artefactId" => artefact_id = child.text.clone(),
"artifactId" => artifact_id = child.text.clone(),
"version" => version = child.text.clone(),
_ => {}
}
}
Parent {
group_id: group_id.unwrap(),
artifact_id: artefact_id.unwrap(),
artifact_id: artifact_id.unwrap(),
version: version.unwrap(),
}
}

View file

@ -196,7 +196,7 @@ impl Project {
get_project_pom(&self.root, group_id, artifact_id)
}
pub fn iter(&self) -> PomIterator{
pub fn iter<'a>(&'a self) -> PomIterator<'a> {
PomIterator {
project: self,
idx: 0,
@ -204,7 +204,6 @@ impl Project {
}
}
pub struct PomIterator<'a> {
project: &'a Project,
idx: usize,
@ -212,10 +211,7 @@ pub struct PomIterator<'a> {
impl<'a> PomIterator<'a> {
pub fn new(project: &'a Project) -> Self {
PomIterator {
project,
idx: 0,
}
PomIterator { project, idx: 0 }
}
}

View file

@ -3,7 +3,7 @@ use crate::maven::project::Project;
use regex::Regex;
use std::collections::HashSet;
use std::fs::File;
use std::io::{BufWriter, Read, Write};
use std::io::{BufWriter, Write};
use std::path::{Path, PathBuf};
use std::sync::LazyLock;
use zip::ZipArchive;
@ -11,6 +11,7 @@ use zip::ZipArchive;
static CLASS_EXPR: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"(.+)/.+\.class").unwrap());
const MAVEN_CENTRAL: &str = "https://repo1.maven.org/maven2/";
// TODO should not be downloading dependencies
pub fn report(project: &Project) {
let pom = &project.root;
for dep in &project.get_dependencies(pom) {
@ -52,14 +53,14 @@ fn download(dep: &Dependency) -> Result<(), String> {
let url = format!("{}{}.jar", MAVEN_CENTRAL, dep);
let client = Client::builder()
.timeout(std::time::Duration::from_secs(30)) // Ruime timeout instellen
.timeout(std::time::Duration::from_secs(30))
.build()
.map_err(|e| e.to_string())?;
println!("Downloading {}", &url);
let response = client
.get(&url)
.header("User-Agent", "Maven/1.0") // Goede practice om een User-Agent te sturen
.header("User-Agent", "Maven/1.0")
.send()
.map_err(|e| e.to_string())?;
if response.status().is_success() {

523
src/maven/settings.rs Normal file
View file

@ -0,0 +1,523 @@
use std::{fs, path::PathBuf, str::FromStr};
use crate::{
maven::{CUSTOM_SETTINGS_LOCATION, HOME, MAVEN_HOME},
xml::dom_parser::{Node, get_document},
};
pub fn get_settings() -> Result<Settings, String> {
let mut local_repository = None;
let mut interactive_mode = true;
let mut use_plugin_registry = false;
let mut offline = false;
let mut proxies = vec![];
let mut servers = vec![];
let mut mirrors = vec![];
let mut profiles = vec![];
let mut active_profiles = vec![];
let mut plugin_groups = vec![];
let settings_path = get_settings_path();
let settings_file = fs::read_to_string(settings_path?).map_err(|e| e.to_string())?;
for child in get_document(settings_file)
.map_err(|err| err.to_string())?
.root
.children
{
match child.name.as_str() {
"localRepository" => local_repository = child.text,
"interactiveMode" => interactive_mode = child.text.map(|b| b == "true").unwrap_or(true),
"usePluginRegistry" => {
use_plugin_registry = child.text.map(|b| b == "true").unwrap_or(false)
}
"offline" => offline = child.text.map(|b| b == "true").unwrap_or(false),
"proxies" => proxies = get_proxies(child),
"servers" => servers = get_servers(child),
"mirrors" => mirrors = get_mirrors(child),
"profiles" => profiles = get_profiles(child),
"activeProfiles" => active_profiles = get_active_profiles(child),
"pluginGroups" => plugin_groups = get_plugin_groups(child),
_ => {}
};
}
Ok(Settings {
local_repository,
interactive_mode,
use_plugin_registry,
offline,
proxies,
servers,
mirrors,
profiles,
active_profiles,
plugin_groups,
})
}
fn get_proxies(element: Node) -> Vec<Proxy> {
let mut proxies = vec![];
for child in element.children {
proxies.push(get_proxy(child));
}
proxies
}
fn get_active_profiles(element: Node) -> Vec<String> {
let mut active_profiles = vec![];
for child in element.children {
if let Some(active_profile) = child.text {
active_profiles.push(active_profile);
}
}
active_profiles
}
fn get_plugin_groups(element: Node) -> Vec<String> {
let mut plugin_groups = vec![];
for child in element.children {
if let Some(plugin_group) = child.text {
plugin_groups.push(plugin_group);
}
}
plugin_groups
}
fn get_servers(servers_element: Node) -> Vec<Server> {
let mut servers = vec![];
for server_element in servers_element.children {
servers.push(get_server(server_element));
}
servers
}
fn get_mirrors(mirrors_element: Node) -> Vec<Mirror> {
let mut mirrors = vec![];
for mirror_element in mirrors_element.children {
mirrors.push(get_mirror(mirror_element));
}
mirrors
}
fn get_profiles(profiles_element: Node) -> Vec<Profile> {
let mut profiles = vec![];
for mirror_element in profiles_element.children {
profiles.push(get_profile(mirror_element));
}
profiles
}
fn get_server(server_element: Node) -> Server {
let mut id = None;
let mut username = None;
let mut password = None;
let mut private_key = None;
let mut passphrase = None;
let mut file_permissions = None;
let mut directory_permissions = None;
let mut configuration = None;
for child in server_element.children {
match child.name.as_str() {
"id" => id = child.text,
"username" => username = child.text,
"password" => password = child.text,
"private_key" => private_key = child.text,
"passphrase" => passphrase = child.text,
"filePermissions" => file_permissions = child.text,
"directoryPermissions" => directory_permissions = child.text,
"configuration" => configuration = Some(child),
_ => {}
}
}
Server {
id,
username,
password,
private_key,
passphrase,
file_permissions,
directory_permissions,
configuration,
}
}
fn get_proxy(element: Node) -> Proxy {
let mut active = false;
let mut protocol = "http".to_owned();
let mut username = None;
let mut password = None;
let mut port: usize = 8080;
let mut host = None;
let mut non_proxy_hosts = None;
let mut id = None;
for child in element.children {
match child.name.as_str() {
"active" => active = child.text.map(|b| b == "true").unwrap_or(false),
"protocol" => protocol = child.text.unwrap_or("http".to_owned()),
"username" => username = child.text,
"password" => password = child.text,
"port" => {
port = child
.text
.map(|i| {
usize::from_str(&i).expect(&format!("Illegal value for port: '{}'", i))
})
.unwrap_or(8080)
}
"host" => host = child.text,
"non_proxy_hosts" => non_proxy_hosts = child.text,
"id" => id = child.text,
_ => {}
}
}
Proxy {
active,
protocol,
username,
password,
port,
host,
non_proxy_hosts,
id,
}
}
fn get_mirror(mirror_element: Node) -> Mirror {
let mut id = None;
let mut mirror_of = None;
let mut url = None;
let mut name = None;
for child in mirror_element.children {
match child.name.as_str() {
"id" => id = child.text,
"mirror_of" => mirror_of = child.text,
"url" => url = child.text,
"name" => name = child.text,
_ => {}
}
}
Mirror {
id,
mirror_of,
url,
name,
}
}
fn get_profile(profile_element: Node) -> Profile {
let mut id = None;
let mut activation = None;
let mut properties = vec![];
let mut repositories = vec![];
let mut plugin_repositories = vec![];
for child in profile_element.children {
match child.name.as_str() {
"id" => id = child.text,
"activation" => activation = Some(get_activation(child)),
"properties" => properties.append(&mut get_properties(child)),
"repositories" => repositories = get_repositories(child),
"pluginRepositories" => plugin_repositories = get_repositories(child),
_ => {}
}
}
Profile {
id,
activation,
properties,
repositories,
plugin_repositories,
}
}
fn get_activation(activation_element: Node) -> Activation {
let mut active_by_default = false;
let mut jdk = None;
let mut os = None;
let mut property = None;
let mut file = None;
for child in activation_element.children {
match child.name.as_str() {
"activeByDefault" => {
active_by_default = child.text.map(|b| b == "true").unwrap_or(false)
}
"jdk" => jdk = child.text,
"os" => os = Some(get_activation_os(child)),
"property" => property = Some(get_activation_property(child)),
"file" => file = Some(get_activation_file(child)),
_ => {}
}
}
Activation {
active_by_default,
jdk,
os,
property,
file,
}
}
fn get_properties(element: Node) -> Vec<Property> {
let mut properties = vec![];
for child in element.children {
properties.push(Property {
name: child.name,
value: child.text,
});
}
properties
}
fn get_repositories(element: Node) -> Vec<Repository> {
let mut repositories = vec![];
for child in element.children {
match child.name.as_str() {
"repository" => repositories.push(get_repository(child)),
_ => {}
}
}
repositories
}
fn get_repository(element: Node) -> Repository {
let mut releases = None;
let mut snapshots = None;
let mut id = None;
let mut name = None;
let mut url = None;
let mut layout = "default".to_owned();
for child in element.children {
match child.name.as_str() {
"releases" => releases = Some(get_update_policy(child)),
"snapshots" => snapshots = Some(get_update_policy(child)),
"id" => id = child.text,
"name" => name = child.text,
"url" => url = child.text,
"layout" => layout = child.text.unwrap_or("default".to_owned()),
_ => {}
}
}
Repository {
releases,
snapshots,
id,
name,
url,
layout,
}
}
fn get_update_policy(element: Node) -> RepositoryPolicy {
let mut enabled = true;
let mut update_policy = None;
let mut checksum_policy = None;
for child in element.children {
match child.name.as_str() {
"enabled" => enabled = child.text.map(|b| b == "true").unwrap_or(true),
"update_policy" => update_policy = child.text,
"checksum_policy" => checksum_policy = child.text,
_ => {}
}
}
RepositoryPolicy {
enabled,
update_policy,
checksum_policy,
}
}
fn get_activation_os(element: Node) -> ActivationOs {
let mut name = None;
let mut family = None;
let mut arch = None;
let mut version = None;
for child in element.children {
match child.name.as_str() {
"name" => name = child.text,
"family" => family = child.text,
"arch" => arch = child.text,
"version" => version = child.text,
_ => {}
}
}
ActivationOs {
name,
family,
arch,
version,
}
}
fn get_activation_property(element: Node) -> ActivationProperty {
let mut name = None;
let mut value = None;
for child in element.children {
match child.name.as_str() {
"name" => name = child.text,
"value" => value = child.text,
_ => {}
}
}
ActivationProperty { name, value }
}
fn get_activation_file(element: Node) -> ActivationFile {
let mut missing = None;
let mut exists = None;
for child in element.children {
match child.name.as_str() {
"missing" => missing = child.text,
"exists" => exists = child.text,
_ => {}
}
}
ActivationFile { missing, exists }
}
fn get_settings_path() -> Result<PathBuf, String> {
let mut settings = PathBuf::from_str(HOME.as_str()).map_err(|e| e.to_string())?;
settings.push(".m2/settings.xml");
if !settings.exists() {
settings = PathBuf::from_str(MAVEN_HOME.as_str()).map_err(|e| e.to_string())?;
settings.push("conf/settings.xml");
}
if !settings.exists() {
settings =
PathBuf::from_str(CUSTOM_SETTINGS_LOCATION.as_str()).map_err(|e| e.to_string())?;
if settings.is_dir() {
settings.push("settings.xml");
}
}
Ok(settings)
}
#[derive(Debug)]
pub struct Settings {
local_repository: Option<String>,
interactive_mode: bool,
use_plugin_registry: bool,
offline: bool,
proxies: Vec<Proxy>,
servers: Vec<Server>,
mirrors: Vec<Mirror>,
profiles: Vec<Profile>,
active_profiles: Vec<String>,
plugin_groups: Vec<String>,
}
#[derive(Debug)]
struct Server {
id: Option<String>,
username: Option<String>,
password: Option<String>,
private_key: Option<String>,
passphrase: Option<String>,
file_permissions: Option<String>,
directory_permissions: Option<String>,
configuration: Option<Node>, //xsd:any
}
#[derive(Debug)]
struct Mirror {
id: Option<String>,
mirror_of: Option<String>,
name: Option<String>,
url: Option<String>,
}
#[derive(Debug)]
struct Proxy {
active: bool,
protocol: String,
username: Option<String>,
password: Option<String>,
port: usize,
host: Option<String>,
non_proxy_hosts: Option<String>,
id: Option<String>,
}
#[derive(Debug)]
struct Profile {
id: Option<String>,
activation: Option<Activation>,
properties: Vec<Property>,
repositories: Vec<Repository>,
plugin_repositories: Vec<Repository>,
}
#[derive(Debug)]
struct Activation {
active_by_default: bool,
jdk: Option<String>,
os: Option<ActivationOs>,
property: Option<ActivationProperty>,
file: Option<ActivationFile>,
}
#[derive(Debug)]
struct ActivationOs {
name: Option<String>,
family: Option<String>,
arch: Option<String>,
version: Option<String>,
}
#[derive(Debug)]
struct ActivationProperty {
name: Option<String>,
value: Option<String>,
}
#[derive(Debug)]
struct ActivationFile {
missing: Option<String>,
exists: Option<String>,
}
#[derive(Debug)]
struct Property {
name: String,
value: Option<String>,
}
#[derive(Debug)]
struct Repository {
releases: Option<RepositoryPolicy>,
snapshots: Option<RepositoryPolicy>,
id: Option<String>,
name: Option<String>,
url: Option<String>,
layout: String,
}
#[derive(Debug)]
struct RepositoryPolicy {
enabled: bool,
update_policy: Option<String>,
checksum_policy: Option<String>,
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test() {
let settings = get_settings().expect("no fail");
println!("{:?}", settings);
}
}

View file

@ -1,9 +1,11 @@
use crate::maven::project::Project;
use maud::{PreEscaped, html};
use maud::{DOCTYPE, PreEscaped, html};
impl Project {
pub fn generate_dependency_html(&self) -> String {
let html = html! {
(DOCTYPE)
html {
(PreEscaped(r#"
<head>
<meta charset="UTF-8">
@ -28,6 +30,7 @@ impl Project {
</style>
</head>
"#))
body{
h1{"Project Dependencies"}
table{
thead{
@ -47,6 +50,8 @@ impl Project {
}
}
}
}
}
};
html.into_string()
}

View file

@ -2,9 +2,9 @@ use crate::xml::sax_parser::parse_string;
use crate::xml::{Attribute, SaxError, SaxHandler};
/// get a generic XML object (Document) from the xml contents. This is called DOM parsing
pub fn get_document(xml: &str) -> Result<Document, SaxError> {
pub fn get_document(xml: impl Into<String>) -> Result<Document, SaxError> {
let mut dom_hax_handler = DomSaxHandler::new();
parse_string(xml, Box::new(&mut dom_hax_handler))?;
parse_string(&xml.into(), Box::new(&mut dom_hax_handler))?;
Ok(dom_hax_handler.into_doc())
}

View file

@ -22,6 +22,7 @@ struct SAXParser<'a> {
xml: Vec<char>,
handler: Box<&'a mut dyn SaxHandler>,
position: usize,
current_line: usize,
current: char,
char_buffer: Vec<char>,
namespace_stack: Vec<(String, isize)>,
@ -35,6 +36,7 @@ impl<'a> SAXParser<'a> {
xml: xml.chars().collect(),
handler,
position: 0,
current_line: 0,
current: '\0',
char_buffer: Vec::new(),
namespace_stack: Vec::new(),
@ -111,6 +113,9 @@ impl<'a> SAXParser<'a> {
while c.is_whitespace() {
self.skip_whitespace()?;
if self.current == '/' {
break;
}
atts.push(self.parse_attribute()?);
c = self.advance()?;
}
@ -158,15 +163,21 @@ impl<'a> SAXParser<'a> {
let att_name = self.read_until("=")?;
self.skip_whitespace()?;
self.expect("=", "Expected =")?;
self.expect("\"", "Expected start of attribute value")?;
self.skip_whitespace()?;
self.expect(
r#"""#,
&format!(
"Expected start of attribute value at line {}. Instead found [{}]",
self.current_line, self.current
),
)?;
let att_value = self.read_until("\"")?;
if att_name.starts_with("xmlns:") {
let prefix = att_name[6..].to_string();
self.prefix_mapping
.insert(prefix.clone(), att_value.to_string());
self.handler
.start_prefix_mapping(&prefix, &att_value);
self.handler.start_prefix_mapping(&prefix, &att_value);
}
let namespace = if att_name == "xmlns" {
@ -245,6 +256,10 @@ impl<'a> SAXParser<'a> {
} else {
'\0'
};
// print!("{}", self.current);
if self.current == '\n' {
self.current_line += 1;
}
Ok(self.current)
}