Compare commits
10 commits
3b55dd3536
...
bb5517470e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb5517470e | ||
|
|
3f30afa689 | ||
|
|
ab651b9b97 | ||
|
|
935bfb4139 | ||
|
|
f6e39067c8 | ||
|
|
2deae73132 | ||
|
|
e7dee59605 | ||
|
|
25a5bde49c | ||
|
|
098cb5e0bc | ||
|
|
856961b8f5 |
22 changed files with 3123 additions and 95 deletions
1996
Cargo.lock
generated
1996
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -7,3 +7,6 @@ edition = "2024"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
env_logger = "0.11"
|
env_logger = "0.11"
|
||||||
regex="1.11"
|
regex="1.11"
|
||||||
|
maud = "*"
|
||||||
|
zip = "4.3"
|
||||||
|
reqwest = { version = "0.12", features = ["blocking", "rustls-tls"] }
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,13 @@ COPY . .
|
||||||
RUN cargo install --path .
|
RUN cargo install --path .
|
||||||
|
|
||||||
FROM debian:bullseye-slim
|
FROM debian:bullseye-slim
|
||||||
RUN apt-get update && rm -rf /var/lib/apt/lists/*
|
RUN apt-get update && \
|
||||||
|
apt-get install -y --no-install-recommends \
|
||||||
|
pkg-config \
|
||||||
|
libssl-dev \
|
||||||
|
build-essential \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
COPY --from=builder /usr/local/cargo/bin/undeepend /usr/local/bin/undeepend
|
COPY --from=builder /usr/local/cargo/bin/undeepend /usr/local/bin/undeepend
|
||||||
CMD ["undeepend"]
|
CMD ["undeepend"]
|
||||||
|
|
||||||
|
|
|
||||||
41
README.md
41
README.md
|
|
@ -1,15 +1,48 @@
|
||||||
currently implementing in rust:
|
**currently implementing in rust:**
|
||||||
* V a sax parser to read xml files (and existing xml binding in rust has trouble reading maven properties)
|
* V a sax parser to read xml files (and existing xml binding in rust has trouble reading maven properties)
|
||||||
* V a dom parser to get a generic xml representation
|
* V a dom parser to get a generic xml representation
|
||||||
* V a pom reader to get a maven specific representation
|
* V a pom reader to get a maven specific representation
|
||||||
* V to find out what dependencies you have
|
* V to find out what dependencies you have
|
||||||
* try default localRepository ~/.m2/repository
|
* V try default localRepository ~/.m2/repository
|
||||||
* load settings.xml
|
* load settings.xml
|
||||||
* search dependency in localRepository
|
* V search dependency in localRepository
|
||||||
* download dependency from remote repo's
|
* V download dependency from remote repo's
|
||||||
|
|
||||||
Why rust and not a maven plugin?
|
Why rust and not a maven plugin?
|
||||||
* faster
|
* faster
|
||||||
* more challenges
|
* more challenges
|
||||||
* run it in docker as a separate step
|
* run it in docker as a separate step
|
||||||
|
|
||||||
|
|
||||||
|
* report in html
|
||||||
|
* list dependencies in descending 'should-I-use-it-score' order (below)
|
||||||
|
* drill down to code usage in project
|
||||||
|
|
||||||
|
**gradle**
|
||||||
|
* probably easiest to run gradle itself to get the dependency list
|
||||||
|
* maybe should've done that with maven as well...
|
||||||
|
* but currently it's working rather well (as a POC, it's still missing essential features)
|
||||||
|
|
||||||
|
**elaborating**
|
||||||
|
* deciding if you should ditch a dependency, likely involves other factors:
|
||||||
|
* (dependency) project quality, as defined by:
|
||||||
|
* date of last commit
|
||||||
|
* date of highest version on mavencentral
|
||||||
|
* java version in bytecode (pre/post java11, I would say)
|
||||||
|
* nr of collaborators
|
||||||
|
* nr of issues (ratio open/solved vs total)
|
||||||
|
* nr of superseded transitive dependencies
|
||||||
|
* reported vulnerabilities
|
||||||
|
* in some weighted sum(s), yielding a 'should-I-use-it score'
|
||||||
|
* and replaceability score: how much work to replace it
|
||||||
|
* how many occurrences of usage?c
|
||||||
|
* lib or framework?
|
||||||
|
* this is going to be a large database,
|
||||||
|
* incrementally populated with data
|
||||||
|
* what stack?
|
||||||
|
|
||||||
|
**Another idea**
|
||||||
|
* compute amount of (dependency) code that is reachable from the application
|
||||||
|
* count references (traverse all)
|
||||||
|
* what to do with dynamically loaded code?
|
||||||
|
|
||||||
24
index.html
Normal file
24
index.html
Normal 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>
|
||||||
17
main.rs
17
main.rs
|
|
@ -1,17 +0,0 @@
|
||||||
fn main() {
|
|
||||||
println!("Hello, Rust!");
|
|
||||||
|
|
||||||
// Example: Simple calculation
|
|
||||||
let x = 5;
|
|
||||||
let y = 10;
|
|
||||||
let sum = x + y;
|
|
||||||
|
|
||||||
println!("The sum of {} and {} is {}", x, y, sum);
|
|
||||||
|
|
||||||
// Example: Vector operations
|
|
||||||
let numbers = vec![1, 2, 3, 4, 5];
|
|
||||||
let doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect();
|
|
||||||
|
|
||||||
println!("Original: {:?}", numbers);
|
|
||||||
println!("Doubled: {:?}", doubled);
|
|
||||||
}
|
|
||||||
|
|
@ -1,2 +1,3 @@
|
||||||
pub mod maven;
|
pub mod maven;
|
||||||
pub mod xml;
|
pub mod xml;
|
||||||
|
mod report;
|
||||||
20
src/main.rs
20
src/main.rs
|
|
@ -1,12 +1,24 @@
|
||||||
use std::env;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::{env, fs};
|
||||||
use undeepend::maven::project::parse_project;
|
use undeepend::maven::project::parse_project;
|
||||||
|
use undeepend::maven::reporter::report;
|
||||||
|
use undeepend::maven::settings::get_settings;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args = std::env::args().collect::<Vec<String>>();
|
let args = std::env::args().collect::<Vec<String>>();
|
||||||
let dir = if args.len() ==1 {
|
let dir = if args.len() == 1 {
|
||||||
env::current_dir().expect("Could not access current directory")
|
env::current_dir().expect("Could not access current directory")
|
||||||
} else { PathBuf::from(&args[1]) };
|
} else {
|
||||||
|
PathBuf::from(&args[1])
|
||||||
|
};
|
||||||
|
|
||||||
let project = parse_project(&dir).unwrap();
|
let project = parse_project(&dir).unwrap();
|
||||||
println!("{:?}", project.get_dependencies(&project.root));
|
|
||||||
|
fs::write(
|
||||||
|
PathBuf::from("index.html"),
|
||||||
|
project.generate_dependency_html(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
report(&project);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
79
src/maven/common_model.rs
Normal file
79
src/maven/common_model.rs
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
use crate::xml::dom_parser::Node;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Repository {
|
||||||
|
pub releases: Option<RepositoryPolicy>,
|
||||||
|
pub snapshots: Option<RepositoryPolicy>,
|
||||||
|
pub id: Option<String>,
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub url: Option<String>,
|
||||||
|
pub layout: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct RepositoryPolicy {
|
||||||
|
pub enabled: bool,
|
||||||
|
pub update_policy: Option<String>,
|
||||||
|
pub checksum_policy: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,15 @@
|
||||||
|
use std::{env, sync::LazyLock};
|
||||||
|
|
||||||
|
pub mod common_model;
|
||||||
pub mod metadata;
|
pub mod metadata;
|
||||||
pub mod pom;
|
pub mod pom;
|
||||||
pub mod pom_parser;
|
pub mod pom_parser;
|
||||||
pub mod project;
|
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()));
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::{Path, PathBuf};
|
use std::fmt::Display;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
/// the maven object model
|
/// the maven object model
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Pom {
|
pub struct Pom {
|
||||||
pub parent: Option<Parent>,
|
pub parent: Option<Parent>,
|
||||||
pub group_id: Option<String>,
|
pub group_id: Option<String>,
|
||||||
|
|
@ -18,11 +19,10 @@ pub struct Pom {
|
||||||
pub module_names: Vec<String>,
|
pub module_names: Vec<String>,
|
||||||
pub modules: Vec<Pom>,
|
pub modules: Vec<Pom>,
|
||||||
pub directory: PathBuf,
|
pub directory: PathBuf,
|
||||||
|
pub repositories: Vec<Repository>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pom {
|
impl Pom {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
pub struct License {
|
pub struct License {
|
||||||
|
|
@ -49,4 +49,55 @@ pub struct Dependency {
|
||||||
pub group_id: String,
|
pub group_id: String,
|
||||||
pub artifact_id: String,
|
pub artifact_id: String,
|
||||||
pub version: Option<String>,
|
pub version: Option<String>,
|
||||||
|
// pub scope: Option<String>, // TODO need this?
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dependency {
|
||||||
|
/// returns a relative path to the dependency location
|
||||||
|
pub fn to_jar_path(&self) -> PathBuf {
|
||||||
|
let mut path = PathBuf::new();
|
||||||
|
path.push(self.group_id.replace(".", "/"));
|
||||||
|
path.push(&self.artifact_id);
|
||||||
|
let version = self.version.clone().unwrap_or_else(|| "latest".to_string());
|
||||||
|
path.push(&version);
|
||||||
|
path.push(format!("{}-{}.jar", &self.artifact_id, &version));
|
||||||
|
path
|
||||||
|
// why is the version (in the filename) wrong when I use PathBuf::set_extension("jar") ???
|
||||||
|
}
|
||||||
|
|
||||||
|
/// returns an absolute path based on the default maven localRepository location
|
||||||
|
// useful?
|
||||||
|
pub fn to_absolute_jar_path(&self) -> PathBuf {
|
||||||
|
let mut absolute_path = PathBuf::from(HOME.as_str());
|
||||||
|
absolute_path.push(".m2/repository");
|
||||||
|
absolute_path.push(self.to_jar_path());
|
||||||
|
absolute_path
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_snapshot(&self) -> bool {
|
||||||
|
self.version
|
||||||
|
.as_ref()
|
||||||
|
.map(|v| v.ends_with("SNAPSHOT"))
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use crate::maven::HOME;
|
||||||
|
use crate::maven::common_model::Repository;
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::maven::common_model::get_repositories;
|
||||||
use crate::maven::pom::{Dependency, Developer, Parent, Pom};
|
use crate::maven::pom::{Dependency, Developer, Parent, Pom};
|
||||||
use crate::xml::SaxError;
|
use crate::xml::SaxError;
|
||||||
use crate::xml::dom_parser::{Node, get_document};
|
use crate::xml::dom_parser::{Node, get_document};
|
||||||
|
|
@ -5,9 +6,9 @@ use std::collections::HashMap;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
/// parse the pom.xml into a Pom object (struct)
|
/// parse the pom.xml into a Pom object (struct)
|
||||||
pub fn get_pom(xml: impl Into<String>) -> Result<Pom, SaxError> {
|
pub fn get_pom(home_dir: PathBuf, xml: impl Into<String>) -> Result<Pom, SaxError> {
|
||||||
let mut group_id = None;
|
let mut group_id = None;
|
||||||
let mut artefact_id = None;
|
let mut artifact_id = None;
|
||||||
let mut parent = None;
|
let mut parent = None;
|
||||||
let mut version = None;
|
let mut version = None;
|
||||||
let mut name = None;
|
let mut name = None;
|
||||||
|
|
@ -17,11 +18,12 @@ pub fn get_pom(xml: impl Into<String>) -> Result<Pom, SaxError> {
|
||||||
let mut dependency_management = vec![];
|
let mut dependency_management = vec![];
|
||||||
let mut properties = HashMap::new(); // useless assignments...
|
let mut properties = HashMap::new(); // useless assignments...
|
||||||
let mut module_names = vec![]; // not useless assignment...
|
let mut module_names = vec![]; // not useless assignment...
|
||||||
|
let mut repositories = vec![]; // not useless assignment...
|
||||||
|
|
||||||
for child in get_document(xml.into().as_str())?.root.children {
|
for child in get_document(xml.into().as_str())?.root.children {
|
||||||
match child.name.as_str() {
|
match child.name.as_str() {
|
||||||
"groupId" => group_id = child.text,
|
"groupId" => group_id = child.text,
|
||||||
"artifactId" => artefact_id = child.text,
|
"artifactId" => artifact_id = child.text,
|
||||||
"parent" => parent = Some(get_parent(&child)),
|
"parent" => parent = Some(get_parent(&child)),
|
||||||
"version" => version = child.text,
|
"version" => version = child.text,
|
||||||
"name" => name = child.text,
|
"name" => name = child.text,
|
||||||
|
|
@ -31,13 +33,20 @@ pub fn get_pom(xml: impl Into<String>) -> Result<Pom, SaxError> {
|
||||||
"dependencyManagement" => dependency_management = get_dependency_mgmt(child),
|
"dependencyManagement" => dependency_management = get_dependency_mgmt(child),
|
||||||
"properties" => properties = get_properties(child),
|
"properties" => properties = get_properties(child),
|
||||||
"modules" => add_modules(child, &mut module_names),
|
"modules" => add_modules(child, &mut module_names),
|
||||||
|
"repositories" => repositories = get_repositories(child),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO before returning, calculate all
|
||||||
|
// * dependency versions
|
||||||
|
// * repositories
|
||||||
|
// maybe put that in a separate model struct
|
||||||
|
|
||||||
Ok(Pom {
|
Ok(Pom {
|
||||||
parent,
|
parent,
|
||||||
group_id,
|
group_id,
|
||||||
artifact_id: artefact_id.unwrap(),
|
artifact_id: artifact_id.unwrap(),
|
||||||
version,
|
version,
|
||||||
name,
|
name,
|
||||||
packaging,
|
packaging,
|
||||||
|
|
@ -47,11 +56,12 @@ pub fn get_pom(xml: impl Into<String>) -> Result<Pom, SaxError> {
|
||||||
properties,
|
properties,
|
||||||
module_names,
|
module_names,
|
||||||
modules: vec![],
|
modules: vec![],
|
||||||
directory: PathBuf::new(), // resolved later, make optional?
|
directory: home_dir,
|
||||||
|
repositories,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_modules(element: Node, modules: &mut Vec<String>){
|
fn add_modules(element: Node, modules: &mut Vec<String>) {
|
||||||
for module in element.children {
|
for module in element.children {
|
||||||
modules.push(module.text.expect("Cannot read module name"));
|
modules.push(module.text.expect("Cannot read module name"));
|
||||||
}
|
}
|
||||||
|
|
@ -91,19 +101,19 @@ fn get_dependencies(element: Node) -> Vec<Dependency> {
|
||||||
|
|
||||||
fn get_dependency(element: Node) -> Dependency {
|
fn get_dependency(element: Node) -> Dependency {
|
||||||
let mut grouo_id = None;
|
let mut grouo_id = None;
|
||||||
let mut artefact_id = None;
|
let mut artifact_id = None;
|
||||||
let mut version = None;
|
let mut version = None;
|
||||||
for node in element.children {
|
for node in element.children {
|
||||||
match node.name.as_str() {
|
match node.name.as_str() {
|
||||||
"groupId" => grouo_id = node.text,
|
"groupId" => grouo_id = node.text,
|
||||||
"artifactId" => artefact_id = node.text,
|
"artifactId" => artifact_id = node.text,
|
||||||
"version" => version = node.text,
|
"version" => version = node.text,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Dependency {
|
Dependency {
|
||||||
group_id: grouo_id.unwrap(),
|
group_id: grouo_id.unwrap(),
|
||||||
artifact_id: artefact_id.unwrap(),
|
artifact_id: artifact_id.unwrap(),
|
||||||
version,
|
version,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -136,19 +146,19 @@ fn get_developer(element: Node) -> Developer {
|
||||||
|
|
||||||
fn get_parent(element: &Node) -> Parent {
|
fn get_parent(element: &Node) -> Parent {
|
||||||
let mut group_id = None;
|
let mut group_id = None;
|
||||||
let mut artefact_id = None;
|
let mut artifact_id = None;
|
||||||
let mut version = None;
|
let mut version = None;
|
||||||
for child in &element.children {
|
for child in &element.children {
|
||||||
match child.name.as_str() {
|
match child.name.as_str() {
|
||||||
"groupId" => group_id = child.text.clone(),
|
"groupId" => group_id = child.text.clone(),
|
||||||
"artefactId" => artefact_id = child.text.clone(),
|
"artifactId" => artifact_id = child.text.clone(),
|
||||||
"version" => version = child.text.clone(),
|
"version" => version = child.text.clone(),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Parent {
|
Parent {
|
||||||
group_id: group_id.unwrap(),
|
group_id: group_id.unwrap(),
|
||||||
artifact_id: artefact_id.unwrap(),
|
artifact_id: artifact_id.unwrap(),
|
||||||
version: version.unwrap(),
|
version: version.unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,17 @@
|
||||||
|
use crate::maven::common_model::Repository;
|
||||||
use crate::maven::pom::{Dependency, Pom};
|
use crate::maven::pom::{Dependency, Pom};
|
||||||
use crate::maven::pom_parser::get_pom;
|
use crate::maven::pom_parser::get_pom;
|
||||||
|
use crate::maven::settings::{Settings, get_settings};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
use reqwest::blocking::Client;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::Path;
|
use std::fs::File;
|
||||||
|
use std::io::{BufWriter, Write};
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
|
const MAVEN_CENTRAL: &str = "https://repo1.maven.org/maven2/";
|
||||||
|
|
||||||
static PROPERTY_EXPR: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"\$\{(.+)}").unwrap());
|
static PROPERTY_EXPR: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"\$\{(.+)}").unwrap());
|
||||||
|
|
||||||
/// Loads all poms from a given project directory.
|
/// Loads all poms from a given project directory.
|
||||||
|
|
@ -18,15 +25,41 @@ pub fn parse_project(project_dir: &Path) -> Result<Project, String> {
|
||||||
|
|
||||||
let mut pom_file = project_dir.to_path_buf();
|
let mut pom_file = project_dir.to_path_buf();
|
||||||
pom_file.push(Path::new("pom.xml"));
|
pom_file.push(Path::new("pom.xml"));
|
||||||
if !pom_file.exists(){
|
if !pom_file.exists() {
|
||||||
return Err(format!("Directory {} does not contain pom.xml", project_dir.to_str().unwrap()));
|
return Err(format!(
|
||||||
|
"Directory {} does not contain pom.xml",
|
||||||
|
project_dir.to_str().unwrap()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let pom_file = fs::read_to_string(pom_file).map_err(|e| e.to_string())?;
|
let pom_file = fs::read_to_string(pom_file).map_err(|e| e.to_string())?;
|
||||||
let mut root = get_pom(pom_file).map_err(|e| e.to_string())?;
|
let mut root = get_pom(project_dir.to_path_buf(), pom_file).map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
resolve_modules(project_dir, &mut root);
|
resolve_modules(project_dir, &mut root);
|
||||||
Ok(Project { root })
|
let project_home = project_dir.to_str().unwrap_or_else(|| "?").to_string();
|
||||||
|
let settings = get_settings()?;
|
||||||
|
|
||||||
|
let mut project = Project {
|
||||||
|
settings,
|
||||||
|
project_home,
|
||||||
|
root,
|
||||||
|
repositories: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
let repositories = project.get_repositories();
|
||||||
|
project.repositories = repositories; // well this is convoluted
|
||||||
|
|
||||||
|
for pom in &project.root.modules {
|
||||||
|
for dep in &project.get_dependencies(pom) {
|
||||||
|
let path = PathBuf::from(dep.to_absolute_jar_path());
|
||||||
|
if !path.exists() {
|
||||||
|
project
|
||||||
|
.download(dep)
|
||||||
|
.expect(&format!("Can't download jar file {}", dep));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(project)
|
||||||
}
|
}
|
||||||
|
|
||||||
// examines modules in pom and loads them
|
// examines modules in pom and loads them
|
||||||
|
|
@ -51,21 +84,23 @@ fn read_module_pom(project_dir: &Path, module: &String) -> Pom {
|
||||||
let module_pom =
|
let module_pom =
|
||||||
fs::read_to_string(module_file).expect(format!("Cannot read file {}", module).as_str());
|
fs::read_to_string(module_file).expect(format!("Cannot read file {}", module).as_str());
|
||||||
|
|
||||||
let mut pom =
|
get_pom(module_dir, module_pom).expect(format!("Cannot create module pom {}", module).as_str())
|
||||||
get_pom(module_pom).expect(format!("Cannot create module pom {}", module).as_str());
|
|
||||||
pom.directory = module_dir;
|
|
||||||
pom
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//main entry to project
|
//main entry to project
|
||||||
//the (root) pom holds the child references to modules
|
//the (root) pom holds the child references to modules
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Project {
|
pub struct Project {
|
||||||
|
pub settings: Settings,
|
||||||
|
pub project_home: String,
|
||||||
pub root: Pom,
|
pub root: Pom,
|
||||||
|
pub repositories: Vec<Repository>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Project {
|
impl Project {
|
||||||
/// get a list of dependencies for a pom in the project
|
/// get a list of dependencies for a pom in the project
|
||||||
|
///
|
||||||
|
/// Note to self: maybe calculating the versions should be done earlier
|
||||||
pub fn get_dependencies(&self, pom: &Pom) -> Vec<Dependency> {
|
pub fn get_dependencies(&self, pom: &Pom) -> Vec<Dependency> {
|
||||||
pom.dependencies
|
pom.dependencies
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -92,52 +127,127 @@ impl Project {
|
||||||
.find(|d| d.group_id == group_id && d.artifact_id == artifact_id)
|
.find(|d| d.group_id == group_id && d.artifact_id == artifact_id)
|
||||||
// extract the version
|
// extract the version
|
||||||
.and_then(|d| d.version.clone())
|
.and_then(|d| d.version.clone())
|
||||||
// is it a property?
|
|
||||||
.and_then(|version| {
|
|
||||||
if PROPERTY_EXPR.is_match(&version) {
|
|
||||||
let property_name = &PROPERTY_EXPR.captures(&version).unwrap()[1];
|
|
||||||
// search property in project hierarchy
|
|
||||||
self.get_property(pom, property_name)
|
|
||||||
} else {
|
|
||||||
Some(version)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
// version not set, try dependencyManagement
|
// version not set, try dependencyManagement
|
||||||
// TODO also search super poms
|
self.collect_managed_dependencies(pom, group_id, artifact_id)
|
||||||
pom.dependency_management
|
|
||||||
.iter()
|
.iter()
|
||||||
.find(|d| d.group_id == group_id && d.artifact_id == artifact_id)
|
.find(|d| d.version.is_some())
|
||||||
.and_then(|d| d.version.clone())
|
.and_then(|d| d.version.clone())
|
||||||
.and_then(|version| {
|
})
|
||||||
if PROPERTY_EXPR.is_match(&version) {
|
.and_then(|v| {
|
||||||
let property_name = &PROPERTY_EXPR.captures(&version).unwrap()[1];
|
if PROPERTY_EXPR.is_match(v.as_str()) {
|
||||||
self.get_property(pom, property_name)
|
let property_name = &PROPERTY_EXPR.captures(&v).unwrap()[1];
|
||||||
} else {
|
// search property in project hierarchy
|
||||||
Some(version)
|
self.get_property(pom, property_name).ok()
|
||||||
}
|
} else {
|
||||||
})
|
Some(v)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn download(&self, dep: &Dependency) -> Result<(), String> {
|
||||||
|
// self.repositories has all repos,
|
||||||
|
// but mirrors are not yet taken into account
|
||||||
|
|
||||||
|
let url = format!("{}{}.jar", MAVEN_CENTRAL, dep);
|
||||||
|
|
||||||
|
let client = Client::builder()
|
||||||
|
.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")
|
||||||
|
.send()
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
if response.status().is_success() {
|
||||||
|
let bytes = response.bytes().map_err(|e| e.to_string())?;
|
||||||
|
let mut buf_writer = BufWriter::new(
|
||||||
|
File::create(dep.to_absolute_jar_path()).map_err(|e| e.to_string())?,
|
||||||
|
);
|
||||||
|
|
||||||
|
buf_writer.write_all(&bytes).map_err(|e| e.to_string())?;
|
||||||
|
buf_writer.flush().map_err(|e| e.to_string())?;
|
||||||
|
println!("Downloaded {}", &url);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_repositories(&self) -> Vec<Repository> {
|
||||||
|
let mut repositories = vec![];
|
||||||
|
for pom in &self.root.modules {
|
||||||
|
repositories.append(&mut pom.repositories.to_vec());
|
||||||
|
}
|
||||||
|
self.add_repositories(&self.root, &mut repositories);
|
||||||
|
repositories.append(&mut self.settings.get_repositories().to_vec());
|
||||||
|
repositories
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_repositories(&self, pom: &Pom, repositories: &mut Vec<Repository>) {
|
||||||
|
repositories.append(&mut pom.repositories.to_vec());
|
||||||
|
if let Some(parent) = &pom.parent {
|
||||||
|
if let Some(parent_pom) = self.get_pom(&parent.group_id, &parent.artifact_id) {
|
||||||
|
self.add_repositories(parent_pom, repositories);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// searches in managed_dependencies for dependencies
|
||||||
|
fn collect_managed_dependencies<'a>(
|
||||||
|
&self,
|
||||||
|
pom: &'a Pom,
|
||||||
|
group_id: &str,
|
||||||
|
artifact_id: &str,
|
||||||
|
) -> Vec<Dependency> {
|
||||||
|
fn collect<'a>(
|
||||||
|
project: &'a Project,
|
||||||
|
pom: &'a Pom,
|
||||||
|
deps: &mut Vec<Dependency>,
|
||||||
|
group_id: &str,
|
||||||
|
artifact_id: &str,
|
||||||
|
) {
|
||||||
|
deps.append(
|
||||||
|
&mut pom
|
||||||
|
.dependency_management
|
||||||
|
.iter()
|
||||||
|
.filter(|d| d.group_id == group_id && d.artifact_id == artifact_id)
|
||||||
|
.map(|d| d.clone())
|
||||||
|
.collect::<Vec<Dependency>>(),
|
||||||
|
);
|
||||||
|
if let Some(parent) = &pom.parent {
|
||||||
|
if let Some(parent_pom) = project.get_pom(&parent.group_id, &parent.artifact_id) {
|
||||||
|
collect(project, parent_pom, deps, group_id, artifact_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut dependencies = Vec::new();
|
||||||
|
collect(self, pom, &mut dependencies, group_id, artifact_id);
|
||||||
|
dependencies
|
||||||
|
}
|
||||||
|
|
||||||
// recursively searches a property going up the chain towards parents
|
// recursively searches a property going up the chain towards parents
|
||||||
fn get_property(&self, pom: &Pom, name: &str) -> Option<String> {
|
fn get_property(&self, pom: &Pom, name: &str) -> Result<String, String> {
|
||||||
if pom.properties.contains_key(name) {
|
if pom.properties.contains_key(name) {
|
||||||
pom.properties.get(name).cloned()
|
pom.properties
|
||||||
|
.get(name)
|
||||||
|
.cloned()
|
||||||
|
.ok_or(format!("Unknown property {}", name))
|
||||||
} else if let Some(parent) = &pom.parent {
|
} else if let Some(parent) = &pom.parent {
|
||||||
if let Some(parent_pom) = self.get_pom(&parent.group_id, &parent.artifact_id) {
|
if let Some(parent_pom) = self.get_pom(&parent.group_id, &parent.artifact_id) {
|
||||||
self.get_property(parent_pom, name)
|
self.get_property(parent_pom, name)
|
||||||
} else {
|
} else {
|
||||||
None
|
Err(format!("Unknown property {}", name))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
Err(format!("Unknown property {}", name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// look up a pom in the project
|
// look up a pom in the project
|
||||||
fn get_pom<'a>(&'a self, group_id: &str, artifact_id: &str) -> Option<&'a Pom> {
|
fn get_pom<'a>(&'a self, group_id: &str, artifact_id: &str) -> Option<&'a Pom> {
|
||||||
|
|
||||||
// inner function to match poms (by artifactId and groupId)
|
// inner function to match poms (by artifactId and groupId)
|
||||||
// (extract if needed elsewhere)
|
// (extract if needed elsewhere)
|
||||||
fn is_same(pom: &Pom, group_id: &str, artifact_id: &str) -> bool {
|
fn is_same(pom: &Pom, group_id: &str, artifact_id: &str) -> bool {
|
||||||
|
|
@ -166,5 +276,36 @@ impl Project {
|
||||||
|
|
||||||
get_project_pom(&self.root, group_id, artifact_id)
|
get_project_pom(&self.root, group_id, artifact_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn iter<'a>(&'a self) -> PomIterator<'a> {
|
||||||
|
PomIterator {
|
||||||
|
project: self,
|
||||||
|
idx: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct PomIterator<'a> {
|
||||||
|
project: &'a Project,
|
||||||
|
idx: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> PomIterator<'a> {
|
||||||
|
pub fn new(project: &'a Project) -> Self {
|
||||||
|
PomIterator { project, idx: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for PomIterator<'a> {
|
||||||
|
type Item = &'a Pom;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.idx < self.project.root.modules.len() {
|
||||||
|
let module = &self.project.root.modules[self.idx];
|
||||||
|
self.idx += 1;
|
||||||
|
Some(module)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
71
src/maven/reporter.rs
Normal file
71
src/maven/reporter.rs
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
use crate::maven::pom::{Dependency, Pom};
|
||||||
|
use crate::maven::project::Project;
|
||||||
|
use regex::Regex;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::fs::File;
|
||||||
|
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::sync::LazyLock;
|
||||||
|
use zip::ZipArchive;
|
||||||
|
|
||||||
|
static CLASS_EXPR: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"(.+)/.+\.class").unwrap());
|
||||||
|
|
||||||
|
// TODO should not be downloading dependencies
|
||||||
|
pub fn report(project: &Project) {
|
||||||
|
let pom = &project.root; // TODO other modules
|
||||||
|
for dep in &project.get_dependencies(pom) {
|
||||||
|
let jar_file = File::open(dep.to_absolute_jar_path()).expect("Can't open jar file");
|
||||||
|
let mut archive = ZipArchive::new(jar_file).expect("Can't read jar file");
|
||||||
|
|
||||||
|
let mut packages = HashSet::new();
|
||||||
|
|
||||||
|
for i in 0..archive.len() {
|
||||||
|
let file = archive.by_index(i).expect("Can't read file");
|
||||||
|
let name = file.name();
|
||||||
|
if CLASS_EXPR.is_match(name) {
|
||||||
|
let package = &CLASS_EXPR.captures(name).unwrap()[1];
|
||||||
|
packages.insert(package.replace("/", ".").to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
analyse_source(&packages, &new_path(&pom.directory, "src/main/java"));
|
||||||
|
analyse_source(&packages, &new_path(&pom.directory, "src/test/java")); //TODO other src dirs, generated src
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_path(dir: &PathBuf, child: &str) -> PathBuf {
|
||||||
|
let mut new_dir = dir.clone();
|
||||||
|
new_dir.push(child);
|
||||||
|
new_dir
|
||||||
|
}
|
||||||
|
|
||||||
|
fn analyse_source(packages: &HashSet<String>, dir: &Path) {
|
||||||
|
if dir.exists() {
|
||||||
|
for entry in dir.read_dir().unwrap() {
|
||||||
|
let entry = entry.unwrap();
|
||||||
|
let path = entry.path();
|
||||||
|
if path.is_dir() {
|
||||||
|
analyse_source(packages, &path);
|
||||||
|
} else {
|
||||||
|
if path.extension().unwrap() == "java" {
|
||||||
|
analyse(packages, &path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO deal with import wildcards
|
||||||
|
fn analyse(packages: &HashSet<String>, path: &Path) {
|
||||||
|
let content = std::fs::read_to_string(path).unwrap();
|
||||||
|
let lines = content.lines();
|
||||||
|
for line in lines {
|
||||||
|
if line.contains("import") {
|
||||||
|
for package in packages {
|
||||||
|
if line.contains(package) {
|
||||||
|
println!("{:?}: {}", path, line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
475
src/maven/settings.rs
Normal file
475
src/maven/settings.rs
Normal file
|
|
@ -0,0 +1,475 @@
|
||||||
|
use std::{fs, path::PathBuf, str::FromStr};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
maven::{
|
||||||
|
CUSTOM_SETTINGS_LOCATION, HOME, MAVEN_HOME,
|
||||||
|
common_model::{Repository, get_repositories},
|
||||||
|
},
|
||||||
|
xml::dom_parser::{Node, get_document},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn get_settings() -> Result<Settings, String> {
|
||||||
|
let settings_path = get_settings_path().map_err(|e| e.to_string())?;
|
||||||
|
get_settings_from_path(settings_path)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_settings_from_path(settings_path: PathBuf) -> Result<Settings, String> {
|
||||||
|
let settings = fs::read_to_string(settings_path).map_err(|e| e.to_string())?;
|
||||||
|
get_settings_from_string(settings)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_settings_from_string(settings: String) -> 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 root = get_document(settings).map_err(|err| err.to_string())?.root;
|
||||||
|
for child in 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_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)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Settings {
|
||||||
|
pub fn get_active_profiles(&self) -> Vec<&Profile> {
|
||||||
|
self.profiles
|
||||||
|
.iter()
|
||||||
|
.filter(|p| {
|
||||||
|
if let Some(activation) = &p.activation {
|
||||||
|
activation.active_by_default //TODO other activation types are possible
|
||||||
|
} else if let Some(id) = &p.id {
|
||||||
|
self.active_profiles.contains(id)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_repositories(&self) -> Vec<Repository> {
|
||||||
|
self.get_active_profiles()
|
||||||
|
.iter()
|
||||||
|
.map(|p| &p.repositories)
|
||||||
|
.flatten()
|
||||||
|
.cloned()
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_plugin_repositories(&self) -> Vec<Repository> {
|
||||||
|
self.get_active_profiles()
|
||||||
|
.iter()
|
||||||
|
.map(|p| &p.plugin_repositories)
|
||||||
|
.flatten()
|
||||||
|
.cloned()
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Settings {
|
||||||
|
pub local_repository: Option<String>,
|
||||||
|
pub interactive_mode: bool,
|
||||||
|
pub use_plugin_registry: bool,
|
||||||
|
pub offline: bool,
|
||||||
|
pub proxies: Vec<Proxy>,
|
||||||
|
pub servers: Vec<Server>,
|
||||||
|
pub mirrors: Vec<Mirror>,
|
||||||
|
pub profiles: Vec<Profile>,
|
||||||
|
pub active_profiles: Vec<String>,
|
||||||
|
pub plugin_groups: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Server {
|
||||||
|
pub id: Option<String>,
|
||||||
|
pub username: Option<String>,
|
||||||
|
pub password: Option<String>,
|
||||||
|
pub private_key: Option<String>,
|
||||||
|
pub passphrase: Option<String>,
|
||||||
|
pub file_permissions: Option<String>,
|
||||||
|
pub directory_permissions: Option<String>,
|
||||||
|
pub configuration: Option<Node>, //xsd:any
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Mirror {
|
||||||
|
pub id: Option<String>,
|
||||||
|
pub mirror_of: Option<String>,
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub url: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Proxy {
|
||||||
|
pub active: bool,
|
||||||
|
pub protocol: String,
|
||||||
|
pub username: Option<String>,
|
||||||
|
pub password: Option<String>,
|
||||||
|
pub port: usize,
|
||||||
|
pub host: Option<String>,
|
||||||
|
pub non_proxy_hosts: Option<String>,
|
||||||
|
pub id: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Profile {
|
||||||
|
pub id: Option<String>,
|
||||||
|
pub activation: Option<Activation>,
|
||||||
|
pub properties: Vec<Property>,
|
||||||
|
pub repositories: Vec<Repository>,
|
||||||
|
pub plugin_repositories: Vec<Repository>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Activation {
|
||||||
|
pub active_by_default: bool,
|
||||||
|
pub jdk: Option<String>,
|
||||||
|
pub os: Option<ActivationOs>,
|
||||||
|
pub property: Option<ActivationProperty>,
|
||||||
|
pub file: Option<ActivationFile>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ActivationOs {
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub family: Option<String>,
|
||||||
|
pub arch: Option<String>,
|
||||||
|
pub version: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ActivationProperty {
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub value: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ActivationFile {
|
||||||
|
pub missing: Option<String>,
|
||||||
|
pub exists: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Property {
|
||||||
|
pub name: String,
|
||||||
|
pub value: Option<String>,
|
||||||
|
}
|
||||||
58
src/report.rs
Normal file
58
src/report.rs
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
use crate::maven::project::Project;
|
||||||
|
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">
|
||||||
|
<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"}
|
||||||
|
table{
|
||||||
|
thead{
|
||||||
|
tr {
|
||||||
|
th{"Group ID"}
|
||||||
|
th{"Artifact ID"}
|
||||||
|
th{"Version"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tbody{
|
||||||
|
@for dependency in &self.get_dependencies(&self.root) {
|
||||||
|
tr {
|
||||||
|
td { (dependency.group_id) }
|
||||||
|
td { (dependency.artifact_id) }
|
||||||
|
td { (dependency.version.clone().unwrap()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
html.into_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,9 +2,9 @@ use crate::xml::sax_parser::parse_string;
|
||||||
use crate::xml::{Attribute, SaxError, SaxHandler};
|
use crate::xml::{Attribute, SaxError, SaxHandler};
|
||||||
|
|
||||||
/// get a generic XML object (Document) from the xml contents. This is called DOM parsing
|
/// 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();
|
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())
|
Ok(dom_hax_handler.into_doc())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ struct SAXParser<'a> {
|
||||||
xml: Vec<char>,
|
xml: Vec<char>,
|
||||||
handler: Box<&'a mut dyn SaxHandler>,
|
handler: Box<&'a mut dyn SaxHandler>,
|
||||||
position: usize,
|
position: usize,
|
||||||
|
current_line: usize,
|
||||||
current: char,
|
current: char,
|
||||||
char_buffer: Vec<char>,
|
char_buffer: Vec<char>,
|
||||||
namespace_stack: Vec<(String, isize)>,
|
namespace_stack: Vec<(String, isize)>,
|
||||||
|
|
@ -35,6 +36,7 @@ impl<'a> SAXParser<'a> {
|
||||||
xml: xml.chars().collect(),
|
xml: xml.chars().collect(),
|
||||||
handler,
|
handler,
|
||||||
position: 0,
|
position: 0,
|
||||||
|
current_line: 0,
|
||||||
current: '\0',
|
current: '\0',
|
||||||
char_buffer: Vec::new(),
|
char_buffer: Vec::new(),
|
||||||
namespace_stack: Vec::new(),
|
namespace_stack: Vec::new(),
|
||||||
|
|
@ -44,10 +46,10 @@ impl<'a> SAXParser<'a> {
|
||||||
|
|
||||||
fn parse(&mut self) -> Result<(), SaxError> {
|
fn parse(&mut self) -> Result<(), SaxError> {
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
self.expect(
|
// self.expect(
|
||||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
|
// "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
|
||||||
"Content is not allowed in prolog.",
|
// "Content is not allowed in prolog.",
|
||||||
)?;
|
// ).unwrap_or_default(); // not fatal TODO
|
||||||
self.skip_whitespace()?;
|
self.skip_whitespace()?;
|
||||||
self.handler.start_document();
|
self.handler.start_document();
|
||||||
self.parse_elements()
|
self.parse_elements()
|
||||||
|
|
@ -61,6 +63,12 @@ impl<'a> SAXParser<'a> {
|
||||||
self.char_buffer.clear();
|
self.char_buffer.clear();
|
||||||
}
|
}
|
||||||
self.advance()?;
|
self.advance()?;
|
||||||
|
if self.current == '?' {
|
||||||
|
self.expect(
|
||||||
|
"?xml version=\"1.0\" encoding=\"UTF-8\"?>",
|
||||||
|
"Content is not allowed in prolog.",
|
||||||
|
)?;
|
||||||
|
}
|
||||||
if self.current == '!' {
|
if self.current == '!' {
|
||||||
self.skip_comment()?;
|
self.skip_comment()?;
|
||||||
} else if self.current != '/' {
|
} else if self.current != '/' {
|
||||||
|
|
@ -111,6 +119,9 @@ impl<'a> SAXParser<'a> {
|
||||||
|
|
||||||
while c.is_whitespace() {
|
while c.is_whitespace() {
|
||||||
self.skip_whitespace()?;
|
self.skip_whitespace()?;
|
||||||
|
if self.current == '/' {
|
||||||
|
break;
|
||||||
|
}
|
||||||
atts.push(self.parse_attribute()?);
|
atts.push(self.parse_attribute()?);
|
||||||
c = self.advance()?;
|
c = self.advance()?;
|
||||||
}
|
}
|
||||||
|
|
@ -158,15 +169,21 @@ impl<'a> SAXParser<'a> {
|
||||||
let att_name = self.read_until("=")?;
|
let att_name = self.read_until("=")?;
|
||||||
self.skip_whitespace()?;
|
self.skip_whitespace()?;
|
||||||
self.expect("=", "Expected =")?;
|
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("\"")?;
|
let att_value = self.read_until("\"")?;
|
||||||
|
|
||||||
if att_name.starts_with("xmlns:") {
|
if att_name.starts_with("xmlns:") {
|
||||||
let prefix = att_name[6..].to_string();
|
let prefix = att_name[6..].to_string();
|
||||||
self.prefix_mapping
|
self.prefix_mapping
|
||||||
.insert(prefix.clone(), att_value.to_string());
|
.insert(prefix.clone(), att_value.to_string());
|
||||||
self.handler
|
self.handler.start_prefix_mapping(&prefix, &att_value);
|
||||||
.start_prefix_mapping(&prefix, &att_value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let namespace = if att_name == "xmlns" {
|
let namespace = if att_name == "xmlns" {
|
||||||
|
|
@ -245,6 +262,10 @@ impl<'a> SAXParser<'a> {
|
||||||
} else {
|
} else {
|
||||||
'\0'
|
'\0'
|
||||||
};
|
};
|
||||||
|
// print!("{}", self.current);
|
||||||
|
if self.current == '\n' {
|
||||||
|
self.current_line += 1;
|
||||||
|
}
|
||||||
Ok(self.current)
|
Ok(self.current)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,3 @@
|
||||||
mod pom_parser_test;
|
mod pom_parser_test;
|
||||||
mod project_parser_test;
|
mod project_parser_test;
|
||||||
|
mod settings_test;
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
use undeepend::maven::pom_parser::get_pom;
|
use undeepend::maven::pom_parser::get_pom;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pom_parser_is_correct() {
|
fn test_pom_parser_is_correct() {
|
||||||
let test_xml = include_str!("../maven/resources/pom.xml");
|
let test_xml = include_str!("../maven/resources/pom.xml");
|
||||||
let pom = get_pom(test_xml).expect("failed to get document");
|
let pom = get_pom(PathBuf::from("../maven/resources"), test_xml).expect("failed to get document");
|
||||||
assert_eq!(Some("Mockito".to_string()),pom.name);
|
assert_eq!(Some("Mockito".to_string()),pom.name);
|
||||||
assert_eq!(Some("org.mockito".to_string()),pom.group_id);
|
assert_eq!(Some("org.mockito".to_string()),pom.group_id);
|
||||||
assert_eq!("mockito-core",pom.artifact_id);
|
assert_eq!("mockito-core",pom.artifact_id);
|
||||||
|
|
@ -27,7 +28,7 @@ fn test_pom_parser_is_correct() {
|
||||||
assert_eq!("objenesis", objenesis.artifact_id);
|
assert_eq!("objenesis", objenesis.artifact_id);
|
||||||
assert_eq!(Some("1.0".to_string()), objenesis.version);
|
assert_eq!(Some("1.0".to_string()), objenesis.version);
|
||||||
|
|
||||||
assert_eq!(2, pom.modules.len());
|
assert_eq!(2, pom.module_names.len());
|
||||||
assert_eq!("a", pom.module_names[0]);
|
assert_eq!("a", pom.module_names[0]);
|
||||||
assert_eq!("b", pom.module_names[1]);
|
assert_eq!("b", pom.module_names[1]);
|
||||||
|
|
||||||
|
|
|
||||||
47
tests/maven/resources/settings.xml
Normal file
47
tests/maven/resources/settings.xml
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
|
||||||
|
http://maven.apache.org/xsd/settings-1.0.0.xsd">
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>github</id>
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>central</id>
|
||||||
|
<url>https://repo1.maven.org/maven2</url>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>github</id>
|
||||||
|
<url>https://maven.pkg.github.com/shautvast/JsonToy</url>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
</profile>
|
||||||
|
<profile>
|
||||||
|
<id>reflective</id>
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>central</id>
|
||||||
|
<url>https://repo1.maven.org/maven2</url>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>github</id>
|
||||||
|
<url>https://maven.pkg.github.com/shautvast/reflective</url>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
</snapshots>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
|
|
||||||
|
<servers>
|
||||||
|
<server>
|
||||||
|
<id>github</id>
|
||||||
|
<username>shautvast</username>
|
||||||
|
<password>foobar</password>
|
||||||
|
</server>
|
||||||
|
</servers>
|
||||||
|
</settings>
|
||||||
8
tests/maven/settings_test.rs
Normal file
8
tests/maven/settings_test.rs
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
use undeepend::maven::settings::get_settings_from_string;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
let settings = include_str!("../maven/resources/settings.xml").to_string();
|
||||||
|
let settings = get_settings_from_string(settings).expect("no fail");
|
||||||
|
assert!(!settings.profiles.is_empty());
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue