Compare commits
No commits in common. "bb5517470e3bee7865708ed76ca00673429da643" and "3b55dd3536c3fefd1a8f711d8979adbabb61ab94" have entirely different histories.
bb5517470e
...
3b55dd3536
22 changed files with 95 additions and 3123 deletions
1996
Cargo.lock
generated
1996
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -6,7 +6,4 @@ edition = "2024"
|
|||
[dependencies]
|
||||
log = "0.4"
|
||||
env_logger = "0.11"
|
||||
regex="1.11"
|
||||
maud = "*"
|
||||
zip = "4.3"
|
||||
reqwest = { version = "0.12", features = ["blocking", "rustls-tls"] }
|
||||
regex="1.11"
|
||||
|
|
@ -4,13 +4,7 @@ COPY . .
|
|||
RUN cargo install --path .
|
||||
|
||||
FROM debian:bullseye-slim
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
pkg-config \
|
||||
libssl-dev \
|
||||
build-essential \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN apt-get update && rm -rf /var/lib/apt/lists/*
|
||||
COPY --from=builder /usr/local/cargo/bin/undeepend /usr/local/bin/undeepend
|
||||
CMD ["undeepend"]
|
||||
|
||||
|
|
|
|||
41
README.md
41
README.md
|
|
@ -1,48 +1,15 @@
|
|||
**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 dom parser to get a generic xml representation
|
||||
* V a pom reader to get a maven specific representation
|
||||
* V to find out what dependencies you have
|
||||
* V try default localRepository ~/.m2/repository
|
||||
* try default localRepository ~/.m2/repository
|
||||
* load settings.xml
|
||||
* V search dependency in localRepository
|
||||
* V download dependency from remote repo's
|
||||
* search dependency in localRepository
|
||||
* download dependency from remote repo's
|
||||
|
||||
Why rust and not a maven plugin?
|
||||
* faster
|
||||
* more challenges
|
||||
* 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
24
index.html
|
|
@ -1,24 +0,0 @@
|
|||
<!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
Normal file
17
main.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
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,3 +1,2 @@
|
|||
pub mod maven;
|
||||
pub mod xml;
|
||||
mod report;
|
||||
20
src/main.rs
20
src/main.rs
|
|
@ -1,24 +1,12 @@
|
|||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
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>>();
|
||||
let dir = if args.len() == 1 {
|
||||
let dir = if args.len() ==1 {
|
||||
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();
|
||||
|
||||
fs::write(
|
||||
PathBuf::from("index.html"),
|
||||
project.generate_dependency_html(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
report(&project);
|
||||
println!("{:?}", project.get_dependencies(&project.root));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,79 +0,0 @@
|
|||
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,15 +1,4 @@
|
|||
use std::{env, sync::LazyLock};
|
||||
|
||||
pub mod common_model;
|
||||
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()));
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
use std::collections::HashMap;
|
||||
use std::fmt::Display;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// the maven object model
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct Pom {
|
||||
pub parent: Option<Parent>,
|
||||
pub group_id: Option<String>,
|
||||
|
|
@ -19,10 +18,11 @@ pub struct Pom {
|
|||
pub module_names: Vec<String>,
|
||||
pub modules: Vec<Pom>,
|
||||
pub directory: PathBuf,
|
||||
pub repositories: Vec<Repository>,
|
||||
}
|
||||
|
||||
impl Pom {}
|
||||
impl Pom {
|
||||
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct License {
|
||||
|
|
@ -49,55 +49,4 @@ pub struct Dependency {
|
|||
pub group_id: String,
|
||||
pub artifact_id: 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,4 +1,3 @@
|
|||
use crate::maven::common_model::get_repositories;
|
||||
use crate::maven::pom::{Dependency, Developer, Parent, Pom};
|
||||
use crate::xml::SaxError;
|
||||
use crate::xml::dom_parser::{Node, get_document};
|
||||
|
|
@ -6,9 +5,9 @@ use std::collections::HashMap;
|
|||
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> {
|
||||
pub fn get_pom(xml: impl Into<String>) -> Result<Pom, SaxError> {
|
||||
let mut group_id = None;
|
||||
let mut artifact_id = None;
|
||||
let mut artefact_id = None;
|
||||
let mut parent = None;
|
||||
let mut version = None;
|
||||
let mut name = None;
|
||||
|
|
@ -18,12 +17,11 @@ pub fn get_pom(home_dir: PathBuf, xml: impl Into<String>) -> Result<Pom, SaxErro
|
|||
let mut dependency_management = vec![];
|
||||
let mut properties = HashMap::new(); // useless assignments...
|
||||
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 {
|
||||
match child.name.as_str() {
|
||||
"groupId" => group_id = child.text,
|
||||
"artifactId" => artifact_id = child.text,
|
||||
"artifactId" => artefact_id = child.text,
|
||||
"parent" => parent = Some(get_parent(&child)),
|
||||
"version" => version = child.text,
|
||||
"name" => name = child.text,
|
||||
|
|
@ -33,20 +31,13 @@ pub fn get_pom(home_dir: PathBuf, xml: impl Into<String>) -> Result<Pom, SaxErro
|
|||
"dependencyManagement" => dependency_management = get_dependency_mgmt(child),
|
||||
"properties" => properties = get_properties(child),
|
||||
"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 {
|
||||
parent,
|
||||
group_id,
|
||||
artifact_id: artifact_id.unwrap(),
|
||||
artifact_id: artefact_id.unwrap(),
|
||||
version,
|
||||
name,
|
||||
packaging,
|
||||
|
|
@ -56,12 +47,11 @@ pub fn get_pom(home_dir: PathBuf, xml: impl Into<String>) -> Result<Pom, SaxErro
|
|||
properties,
|
||||
module_names,
|
||||
modules: vec![],
|
||||
directory: home_dir,
|
||||
repositories,
|
||||
directory: PathBuf::new(), // resolved later, make optional?
|
||||
})
|
||||
}
|
||||
|
||||
fn add_modules(element: Node, modules: &mut Vec<String>) {
|
||||
fn add_modules(element: Node, modules: &mut Vec<String>){
|
||||
for module in element.children {
|
||||
modules.push(module.text.expect("Cannot read module name"));
|
||||
}
|
||||
|
|
@ -101,19 +91,19 @@ fn get_dependencies(element: Node) -> Vec<Dependency> {
|
|||
|
||||
fn get_dependency(element: Node) -> Dependency {
|
||||
let mut grouo_id = None;
|
||||
let mut artifact_id = None;
|
||||
let mut artefact_id = None;
|
||||
let mut version = None;
|
||||
for node in element.children {
|
||||
match node.name.as_str() {
|
||||
"groupId" => grouo_id = node.text,
|
||||
"artifactId" => artifact_id = node.text,
|
||||
"artifactId" => artefact_id = node.text,
|
||||
"version" => version = node.text,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Dependency {
|
||||
group_id: grouo_id.unwrap(),
|
||||
artifact_id: artifact_id.unwrap(),
|
||||
artifact_id: artefact_id.unwrap(),
|
||||
version,
|
||||
}
|
||||
}
|
||||
|
|
@ -146,19 +136,19 @@ fn get_developer(element: Node) -> Developer {
|
|||
|
||||
fn get_parent(element: &Node) -> Parent {
|
||||
let mut group_id = None;
|
||||
let mut artifact_id = None;
|
||||
let mut artefact_id = None;
|
||||
let mut version = None;
|
||||
for child in &element.children {
|
||||
match child.name.as_str() {
|
||||
"groupId" => group_id = child.text.clone(),
|
||||
"artifactId" => artifact_id = child.text.clone(),
|
||||
"artefactId" => artefact_id = child.text.clone(),
|
||||
"version" => version = child.text.clone(),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Parent {
|
||||
group_id: group_id.unwrap(),
|
||||
artifact_id: artifact_id.unwrap(),
|
||||
artifact_id: artefact_id.unwrap(),
|
||||
version: version.unwrap(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,10 @@
|
|||
use crate::maven::common_model::Repository;
|
||||
use crate::maven::pom::{Dependency, Pom};
|
||||
use crate::maven::pom_parser::get_pom;
|
||||
use crate::maven::settings::{Settings, get_settings};
|
||||
use regex::Regex;
|
||||
use reqwest::blocking::Client;
|
||||
use std::fs;
|
||||
use std::fs::File;
|
||||
use std::io::{BufWriter, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::Path;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
const MAVEN_CENTRAL: &str = "https://repo1.maven.org/maven2/";
|
||||
|
||||
static PROPERTY_EXPR: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"\$\{(.+)}").unwrap());
|
||||
|
||||
/// Loads all poms from a given project directory.
|
||||
|
|
@ -25,41 +18,15 @@ pub fn parse_project(project_dir: &Path) -> Result<Project, String> {
|
|||
|
||||
let mut pom_file = project_dir.to_path_buf();
|
||||
pom_file.push(Path::new("pom.xml"));
|
||||
if !pom_file.exists() {
|
||||
return Err(format!(
|
||||
"Directory {} does not contain pom.xml",
|
||||
project_dir.to_str().unwrap()
|
||||
));
|
||||
if !pom_file.exists(){
|
||||
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 mut root = get_pom(project_dir.to_path_buf(), pom_file).map_err(|e| e.to_string())?;
|
||||
let mut root = get_pom(pom_file).map_err(|e| e.to_string())?;
|
||||
|
||||
resolve_modules(project_dir, &mut 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)
|
||||
Ok(Project { root })
|
||||
}
|
||||
|
||||
// examines modules in pom and loads them
|
||||
|
|
@ -84,23 +51,21 @@ fn read_module_pom(project_dir: &Path, module: &String) -> Pom {
|
|||
let module_pom =
|
||||
fs::read_to_string(module_file).expect(format!("Cannot read file {}", module).as_str());
|
||||
|
||||
get_pom(module_dir, module_pom).expect(format!("Cannot create module pom {}", module).as_str())
|
||||
let mut pom =
|
||||
get_pom(module_pom).expect(format!("Cannot create module pom {}", module).as_str());
|
||||
pom.directory = module_dir;
|
||||
pom
|
||||
}
|
||||
|
||||
//main entry to project
|
||||
//the (root) pom holds the child references to modules
|
||||
#[derive(Debug)]
|
||||
pub struct Project {
|
||||
pub settings: Settings,
|
||||
pub project_home: String,
|
||||
pub root: Pom,
|
||||
pub repositories: Vec<Repository>,
|
||||
}
|
||||
|
||||
impl 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> {
|
||||
pom.dependencies
|
||||
.iter()
|
||||
|
|
@ -127,127 +92,52 @@ impl Project {
|
|||
.find(|d| d.group_id == group_id && d.artifact_id == artifact_id)
|
||||
// extract the version
|
||||
.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(|| {
|
||||
// version not set, try dependencyManagement
|
||||
self.collect_managed_dependencies(pom, group_id, artifact_id)
|
||||
// TODO also search super poms
|
||||
pom.dependency_management
|
||||
.iter()
|
||||
.find(|d| d.version.is_some())
|
||||
.find(|d| d.group_id == group_id && d.artifact_id == artifact_id)
|
||||
.and_then(|d| d.version.clone())
|
||||
.and_then(|version| {
|
||||
if PROPERTY_EXPR.is_match(&version) {
|
||||
let property_name = &PROPERTY_EXPR.captures(&version).unwrap()[1];
|
||||
self.get_property(pom, property_name)
|
||||
} else {
|
||||
Some(version)
|
||||
}
|
||||
})
|
||||
})
|
||||
.and_then(|v| {
|
||||
if PROPERTY_EXPR.is_match(v.as_str()) {
|
||||
let property_name = &PROPERTY_EXPR.captures(&v).unwrap()[1];
|
||||
// search property in project hierarchy
|
||||
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
|
||||
fn get_property(&self, pom: &Pom, name: &str) -> Result<String, String> {
|
||||
fn get_property(&self, pom: &Pom, name: &str) -> Option<String> {
|
||||
if pom.properties.contains_key(name) {
|
||||
pom.properties
|
||||
.get(name)
|
||||
.cloned()
|
||||
.ok_or(format!("Unknown property {}", name))
|
||||
pom.properties.get(name).cloned()
|
||||
} else if let Some(parent) = &pom.parent {
|
||||
if let Some(parent_pom) = self.get_pom(&parent.group_id, &parent.artifact_id) {
|
||||
self.get_property(parent_pom, name)
|
||||
} else {
|
||||
Err(format!("Unknown property {}", name))
|
||||
None
|
||||
}
|
||||
} else {
|
||||
Err(format!("Unknown property {}", name))
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// look up a pom in the project
|
||||
fn get_pom<'a>(&'a self, group_id: &str, artifact_id: &str) -> Option<&'a Pom> {
|
||||
|
||||
// inner function to match poms (by artifactId and groupId)
|
||||
// (extract if needed elsewhere)
|
||||
fn is_same(pom: &Pom, group_id: &str, artifact_id: &str) -> bool {
|
||||
|
|
@ -276,36 +166,5 @@ impl Project {
|
|||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,71 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,475 +0,0 @@
|
|||
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>,
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
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};
|
||||
|
||||
/// get a generic XML object (Document) from the xml contents. This is called DOM parsing
|
||||
pub fn get_document(xml: impl Into<String>) -> Result<Document, SaxError> {
|
||||
pub fn get_document(xml: &str) -> Result<Document, SaxError> {
|
||||
let mut dom_hax_handler = DomSaxHandler::new();
|
||||
parse_string(&xml.into(), Box::new(&mut dom_hax_handler))?;
|
||||
parse_string(xml, Box::new(&mut dom_hax_handler))?;
|
||||
|
||||
Ok(dom_hax_handler.into_doc())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ 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)>,
|
||||
|
|
@ -36,7 +35,6 @@ impl<'a> SAXParser<'a> {
|
|||
xml: xml.chars().collect(),
|
||||
handler,
|
||||
position: 0,
|
||||
current_line: 0,
|
||||
current: '\0',
|
||||
char_buffer: Vec::new(),
|
||||
namespace_stack: Vec::new(),
|
||||
|
|
@ -46,10 +44,10 @@ impl<'a> SAXParser<'a> {
|
|||
|
||||
fn parse(&mut self) -> Result<(), SaxError> {
|
||||
self.advance()?;
|
||||
// self.expect(
|
||||
// "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
|
||||
// "Content is not allowed in prolog.",
|
||||
// ).unwrap_or_default(); // not fatal TODO
|
||||
self.expect(
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
|
||||
"Content is not allowed in prolog.",
|
||||
)?;
|
||||
self.skip_whitespace()?;
|
||||
self.handler.start_document();
|
||||
self.parse_elements()
|
||||
|
|
@ -63,12 +61,6 @@ impl<'a> SAXParser<'a> {
|
|||
self.char_buffer.clear();
|
||||
}
|
||||
self.advance()?;
|
||||
if self.current == '?' {
|
||||
self.expect(
|
||||
"?xml version=\"1.0\" encoding=\"UTF-8\"?>",
|
||||
"Content is not allowed in prolog.",
|
||||
)?;
|
||||
}
|
||||
if self.current == '!' {
|
||||
self.skip_comment()?;
|
||||
} else if self.current != '/' {
|
||||
|
|
@ -119,9 +111,6 @@ impl<'a> SAXParser<'a> {
|
|||
|
||||
while c.is_whitespace() {
|
||||
self.skip_whitespace()?;
|
||||
if self.current == '/' {
|
||||
break;
|
||||
}
|
||||
atts.push(self.parse_attribute()?);
|
||||
c = self.advance()?;
|
||||
}
|
||||
|
|
@ -169,21 +158,15 @@ impl<'a> SAXParser<'a> {
|
|||
let att_name = self.read_until("=")?;
|
||||
self.skip_whitespace()?;
|
||||
self.expect("=", "Expected =")?;
|
||||
self.skip_whitespace()?;
|
||||
self.expect(
|
||||
r#"""#,
|
||||
&format!(
|
||||
"Expected start of attribute value at line {}. Instead found [{}]",
|
||||
self.current_line, self.current
|
||||
),
|
||||
)?;
|
||||
self.expect("\"", "Expected start of attribute value")?;
|
||||
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" {
|
||||
|
|
@ -262,10 +245,6 @@ impl<'a> SAXParser<'a> {
|
|||
} else {
|
||||
'\0'
|
||||
};
|
||||
// print!("{}", self.current);
|
||||
if self.current == '\n' {
|
||||
self.current_line += 1;
|
||||
}
|
||||
Ok(self.current)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,2 @@
|
|||
mod pom_parser_test;
|
||||
mod project_parser_test;
|
||||
mod settings_test;
|
||||
mod project_parser_test;
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
use std::path::PathBuf;
|
||||
use undeepend::maven::pom_parser::get_pom;
|
||||
|
||||
#[test]
|
||||
fn test_pom_parser_is_correct() {
|
||||
let test_xml = include_str!("../maven/resources/pom.xml");
|
||||
let pom = get_pom(PathBuf::from("../maven/resources"), test_xml).expect("failed to get document");
|
||||
let pom = get_pom(test_xml).expect("failed to get document");
|
||||
assert_eq!(Some("Mockito".to_string()),pom.name);
|
||||
assert_eq!(Some("org.mockito".to_string()),pom.group_id);
|
||||
assert_eq!("mockito-core",pom.artifact_id);
|
||||
|
|
@ -28,7 +27,7 @@ fn test_pom_parser_is_correct() {
|
|||
assert_eq!("objenesis", objenesis.artifact_id);
|
||||
assert_eq!(Some("1.0".to_string()), objenesis.version);
|
||||
|
||||
assert_eq!(2, pom.module_names.len());
|
||||
assert_eq!(2, pom.modules.len());
|
||||
assert_eq!("a", pom.module_names[0]);
|
||||
assert_eq!("b", pom.module_names[1]);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,47 +0,0 @@
|
|||
<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>
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
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