From c3661145adf87c9e64a0f6df965655c4ee2304f3 Mon Sep 17 00:00:00 2001 From: ynfeng Date: Sat, 6 Mar 2021 11:11:56 +0800 Subject: [PATCH] feat(psa): parse maven dependencies from pom.xml. --- Cargo.lock | 42 ++++++++++--- psa/Cargo.toml | 4 +- psa/src/dependency_analyzer.rs | 4 +- psa/src/jvm/maven_dependency.rs | 105 ++++++++++++++++++++++++++------ psa/src/jvm/psa_jvm.rs | 2 +- psa/src/lib.rs | 2 + 6 files changed, 130 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0f13db66..99a6686f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2341,6 +2341,12 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +[[package]] +name = "peresil" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f658886ed52e196e850cfbbfddab9eaa7f6d90dd0929e264c31e5cec07e09e57" + [[package]] name = "pest" version = "2.1.3" @@ -2574,8 +2580,9 @@ dependencies = [ "serde", "serde_derive", "serde_json", + "sxd-document", + "sxd-xpath", "walkdir", - "xml-rs", ] [[package]] @@ -3320,6 +3327,27 @@ dependencies = [ "syn 1.0.58", ] +[[package]] +name = "sxd-document" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94d82f37be9faf1b10a82c4bd492b74f698e40082f0f40de38ab275f31d42078" +dependencies = [ + "peresil", + "typed-arena", +] + +[[package]] +name = "sxd-xpath" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36e39da5d30887b5690e29de4c5ebb8ddff64ebd9933f98a01daaa4fd11b36ea" +dependencies = [ + "peresil", + "quick-error", + "sxd-document", +] + [[package]] name = "symbolic-common" version = "7.5.0" @@ -3786,6 +3814,12 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +[[package]] +name = "typed-arena" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9b2228007eba4120145f785df0f6c92ea538f5a3635a612ecf4e334c8c1446d" + [[package]] name = "typenum" version = "1.12.0" @@ -4146,12 +4180,6 @@ dependencies = [ "winapi-build", ] -[[package]] -name = "xml-rs" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a" - [[package]] name = "yaml-rust" version = "0.4.5" diff --git a/psa/Cargo.toml b/psa/Cargo.toml index 4b29718a..ef525588 100644 --- a/psa/Cargo.toml +++ b/psa/Cargo.toml @@ -34,6 +34,6 @@ regex = "1" pathdiff = "0.2.0" -xml-rs = "0.8" - +sxd-xpath= "0.4.2" +sxd-document= "0.3.2" \ No newline at end of file diff --git a/psa/src/dependency_analyzer.rs b/psa/src/dependency_analyzer.rs index bd0e6c7e..582da0df 100644 --- a/psa/src/dependency_analyzer.rs +++ b/psa/src/dependency_analyzer.rs @@ -6,7 +6,7 @@ pub trait DependencyAnalyzer { fn analysis(&self, module_path: &str) -> Vec { let build_file = self.get_build_file(module_path); match build_file { - Some(build_file) => self.analysis_dependencies(build_file.as_str()), + Some(build_file) => self.analysis_dependencies(module_path, build_file.as_str()), _ => vec![], } } @@ -21,5 +21,5 @@ pub trait DependencyAnalyzer { fn is_build_file(&self, file: &str) -> bool; - fn analysis_dependencies(&self, build_file: &str) -> Vec; + fn analysis_dependencies(&self, module_path: &str, build_file: &str) -> Vec; } diff --git a/psa/src/jvm/maven_dependency.rs b/psa/src/jvm/maven_dependency.rs index a4ca13c7..b1e867ad 100644 --- a/psa/src/jvm/maven_dependency.rs +++ b/psa/src/jvm/maven_dependency.rs @@ -1,6 +1,8 @@ -extern crate xml; - use crate::{Dependency, DependencyAnalyzer, DependencyScope}; +use std::fs::read_to_string; +use std::path::PathBuf; +use sxd_document::parser; +use sxd_xpath::{Context, Factory, Value}; pub struct MavenDependencyAnalyzer {} @@ -12,20 +14,89 @@ impl DependencyAnalyzer for MavenDependencyAnalyzer { } } - fn analysis_dependencies(&self, _build_file: &str) -> Vec { - let mut dependencies = Vec::new(); - dependencies.push(Dependency { - group: "org.springframework.boot".to_string(), - name: "spring-boot-starter-web".to_string(), - version: "2.0.0.RELEASE".to_string(), - scope: DependencyScope::Test, - }); - dependencies.push(Dependency { - group: "org.springframework.boot".to_string(), - name: "spring-boot-starter-logging".to_string(), - version: "1.0.0.RELEASE".to_string(), - scope: DependencyScope::Compile, - }); - dependencies + fn analysis_dependencies(&self, module_path: &str, _build_file: &str) -> Vec { + let build_file_path = PathBuf::from(module_path) + .join(_build_file) + .display() + .to_string(); + let build_file_content = + read_to_string(build_file_path.as_str()).expect("can not open build file"); + match !build_file_content.is_empty() { + true => parse_deps(build_file_content.as_str()), + _ => vec![], + } } } + +fn parse_deps(xml_content: &str) -> Vec { + let mut deps = vec![]; + let project = parser::parse(xml_content).unwrap(); + let document = project.as_document(); + let factory = Factory::new(); + let mut context = Context::new(); + context.set_namespace("ns", "http://maven.apache.org/POM/4.0.0"); + + let xpath = factory + .build("count(/ns:project/ns:dependencies/ns:dependency)") + .unwrap() + .unwrap(); + let num_of_deps = xpath.evaluate(&context, document.root()).unwrap(); + if let Value::Number(ref count) = num_of_deps { + for i in 0..(count.round() as i64) { + let group_id_expression = format!( + "/ns:project/ns:dependencies/ns:dependency[{}]/ns:groupId", + i + 1 + ); + let group_id_xpath = factory + .build(group_id_expression.as_str()) + .unwrap() + .unwrap(); + let group = group_id_xpath + .evaluate(&context, document.root()) + .unwrap() + .string(); + + let artifact_id_expression = format!( + "/ns:project/ns:dependencies/ns:dependency[{}]/ns:artifactId", + i + 1 + ); + let artifact_id_xpath = factory + .build(artifact_id_expression.as_str()) + .unwrap() + .unwrap(); + let name = artifact_id_xpath + .evaluate(&context, document.root()) + .unwrap() + .string(); + + let version_expression = format!( + "/ns:project/ns:dependencies/ns:dependency[{}]/ns:version", + i + 1 + ); + let version_xpath = factory.build(version_expression.as_str()).unwrap().unwrap(); + let version = version_xpath + .evaluate(&context, document.root()) + .unwrap() + .string(); + + let scope_expression = format!( + "/ns:project/ns:dependencies/ns:dependency[{}]/ns:scope", + i + 1 + ); + let scope_xpath = factory.build(scope_expression.as_str()).unwrap().unwrap(); + let scope_content = scope_xpath.evaluate(&context, document.root()).unwrap(); + let scope = match scope_content.string().as_str() { + "test" => DependencyScope::Test, + _ => DependencyScope::Compile, + }; + + deps.push(Dependency { + group, + name, + version, + scope, + }); + } + }; + deps +} diff --git a/psa/src/jvm/psa_jvm.rs b/psa/src/jvm/psa_jvm.rs index 8d5bb98e..45911e96 100644 --- a/psa/src/jvm/psa_jvm.rs +++ b/psa/src/jvm/psa_jvm.rs @@ -182,7 +182,7 @@ mod tests { let dep2 = project_dependencies.get(1).unwrap(); assert_eq!(dep2.name, "spring-boot-starter-logging"); assert_eq!(dep2.group, "org.springframework.boot"); - assert_eq!(dep2.version, "1.0.0.RELEASE"); + assert_eq!(dep2.version, "${spring-boot-starter.version}"); assert_eq!(dep2.scope, DependencyScope::Compile); } diff --git a/psa/src/lib.rs b/psa/src/lib.rs index 4df30943..4877bbb9 100644 --- a/psa/src/lib.rs +++ b/psa/src/lib.rs @@ -12,6 +12,8 @@ pub use psa_project::Project; #[macro_use] extern crate serde_derive; extern crate serde; +extern crate sxd_document; +extern crate sxd_xpath; pub mod dependency_analyzer; pub mod files;