From 2c714a211cac883c51120b01119b353c1638e8ff Mon Sep 17 00:00:00 2001 From: Shautvast Date: Sun, 21 Jan 2024 15:57:46 +0100 Subject: [PATCH] WIP loading from maven repo's --- src/config.rs | 7 +- src/deploader.rs | 42 ++++++----- src/lib.rs | 2 +- src/main.rs | 6 ++ src/maven/mod.rs | 1 + src/maven/pom.rs | 87 +++++++++++----------- src/maven/pom_view.rs | 165 ++++++++++++++++++++++++++++++++++++++++++ src/project.rs | 5 +- 8 files changed, 246 insertions(+), 69 deletions(-) create mode 100644 src/maven/pom_view.rs diff --git a/src/config.rs b/src/config.rs index 504a577..4ab654d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -4,7 +4,7 @@ use std::sync::OnceLock; /// Contains any config elements pub struct Config { pub cache_location: String, - pub user_home: PathBuf, + pub user_home: String, } pub static CONFIG: OnceLock = OnceLock::new(); @@ -12,9 +12,10 @@ pub static CONFIG: OnceLock = OnceLock::new(); /// default config pub fn config() -> &'static Config { CONFIG.get_or_init(|| { - let user_home = home::home_dir().unwrap(); + let user_home = home::home_dir().map(|p|p.to_str().unwrap().to_owned()) + .expect("Can not find $HOME in environment"); Config { - cache_location: format!("{}/jargo/repo", user_home.to_str().unwrap()).into(), //TODO make it '.jargo' + cache_location: format!("{}/jargo/repo", user_home).into(), //TODO make it '.jargo' user_home, } }); diff --git a/src/deploader.rs b/src/deploader.rs index 285bcad..564e540 100644 --- a/src/deploader.rs +++ b/src/deploader.rs @@ -1,5 +1,5 @@ use std::fs; -use std::fs::File; +use std::fs::{create_dir, create_dir_all, File}; use std::io::prelude::*; use std::io::Write; use std::path::Path; @@ -10,12 +10,12 @@ use sha1::{Digest, Sha1}; use strong_xml::XmlRead; use crate::config::config; -use crate::maven; use crate::maven::metadata::Metadata; use crate::maven::pom::Pom; use crate::project::{Artifact, Project}; use colored::Colorize; use reqwest::StatusCode; +use crate::maven::pom_view::PomView; /// Loads a list of artifacts from remote repo or local cache /// @@ -53,7 +53,7 @@ fn load_artifact(project: &Project, artifact: &Artifact) -> Result<(), Error> { } // download remote pom if not in cache - let pom_lookup = lookup_verified_pom(project, artifact, &local_artifact_loc)?; + let pom_lookup = lookup_verified_pom(project, artifact)?; // download remote jar if not in cache and check its SHA-1 checksum let local_artifact_jar_path = format!( @@ -73,13 +73,13 @@ fn load_artifact(project: &Project, artifact: &Artifact) -> Result<(), Error> { println!("{}", pom_lookup.pom_xml); // parse pom file let pom = Pom::from_str(&pom_lookup.pom_xml).unwrap(); + let pom = PomView::new(pom, project)?; //TODO exclusions - //TODO parents - if let Some(dependencies) = pom.dependencies { - let artifacts = dependencies.value.into_iter().map(|d| d.into()).collect(); - load_artifacts(project, &artifacts)?; - } + + let artifacts = pom.dependencies().into_iter().map(|d| d.into()).collect(); + load_artifacts(project, &artifacts)?; + Ok(()) } @@ -93,15 +93,15 @@ fn load_artifact(project: &Project, artifact: &Artifact) -> Result<(), Error> { /// /// The result from find_pom is passed on to the caller so that the information can be used /// for subsequent requests. -fn lookup_verified_pom( +pub(crate) fn lookup_verified_pom( project: &Project, artifact: &Artifact, - local_artifact_loc: &str, ) -> Result { let local_artifact_pom_path = &format!( - "{}/{}-{}.pom", - local_artifact_loc, artifact.name, artifact.version + "{}/{}/{}-{}.pom", + config().cache_location, artifact.path, artifact.name, artifact.version ); + // get pom from local or remote let result = if exists(local_artifact_pom_path) { read_file_to_string(local_artifact_pom_path)? } else { @@ -111,7 +111,7 @@ fn lookup_verified_pom( panic!("Could not find pom for {}", artifact.path) } else { let result = result.unwrap(); - let repo_with_pom = result.resolved_repo.as_ref(); //TODO replace tuple with struct + let repo_with_pom = result.resolved_repo.as_ref(); let pom_xml = &result.pom_xml; let local_artifact_pom_sha1_path = format!("{}.sha1", local_artifact_pom_path); @@ -137,10 +137,10 @@ fn lookup_verified_pom( } #[derive(Debug, Clone)] -struct PomLookupResult { - pom_xml: String, - resolved_repo: Option, - resolved_version: Option, +pub(crate) struct PomLookupResult { + pub(crate) pom_xml: String, + pub(crate) resolved_repo: Option, + pub(crate) resolved_version: Option, } fn find_pom( @@ -169,6 +169,12 @@ fn download_pom( local_artifact_pom_path: &str, repo: &str, ) -> Result, Error> { + // can't assume it exists + let local_artifact_dir = format!("{}/{}", config().cache_location, artifact.path); + + if !exists(&local_artifact_dir){ + create_dir_all(local_artifact_dir)?; + } let remote_artifact_pom_url = format!( "{}/{}/{}-{}.pom", repo, artifact.path, artifact.name, resolved_version @@ -269,7 +275,7 @@ fn load_snapshot_build_nr(artifact_path: &str, repo: &String) -> Result anyhow::Result<(), Error> { + let repo = format!("{}/jargo/repo",config().user_home); + std::fs::remove_dir_all(&repo)?; + std::fs::create_dir(repo)?; + let project = jargo::project::load_project(Some("tests/sample_project/Jargo.toml"))?; println!("{:?}", project); jargo::deploader::load(&project)?; diff --git a/src/maven/mod.rs b/src/maven/mod.rs index 341e379..178200b 100644 --- a/src/maven/mod.rs +++ b/src/maven/mod.rs @@ -1,2 +1,3 @@ pub mod metadata; pub mod pom; +pub mod pom_view; diff --git a/src/maven/pom.rs b/src/maven/pom.rs index 95f1c47..08369a5 100644 --- a/src/maven/pom.rs +++ b/src/maven/pom.rs @@ -1,5 +1,4 @@ use strong_xml::XmlRead; -use crate::maven::metadata::Versioning; /// The Maven variant to parse poms /// These structs is directly modelled after the XML because that is what strong-xml plugin requires @@ -7,171 +6,173 @@ use crate::maven::metadata::Versioning; #[xml(tag = "project")] pub struct Pom { #[xml(child = "modelVersion")] - pub model_version: ModelVersion, + pub(crate) model_version: ModelVersion, #[xml(child = "parent")] - pub parent: Option, + pub(crate) parent: Option, #[xml(child = "groupId")] - pub group_id: Option, + pub(crate) group_id: Option, #[xml(child = "artifactId")] - pub artifact_id: ArtifactId, + pub(crate) artifact_id: ArtifactId, #[xml(child = "version")] - pub version: Option, + pub(crate) version: Option, #[xml(child = "name")] - pub name: Name, + pub(crate) name: Name, #[xml(child = "packaging")] - pub packaging: Option, + pub(crate) packaging: Option, #[xml(child = "url")] - pub url: Option, + pub(crate) url: Option, #[xml(child = "description")] - pub description: Description, + pub(crate) description: Description, #[xml(child = "licenses")] - pub licences: Option, + pub(crate) licences: Option, #[xml(child = "scm")] - pub scm: Option, + pub(crate) scm: Option, #[xml(child = "developers")] - pub developers: Option, + pub(crate) developers: Option, #[xml(child = "dependencies")] - pub dependencies: Option, + pub(crate) dependencies: Option, + #[xml(child = "dependencyManagement")] + pub(crate) dependency_management: Option, } #[derive(XmlRead, PartialEq, Debug)] #[xml(tag = "modelVersion")] pub struct ModelVersion { #[xml(text)] - value: String, + pub value: String, } -#[derive(XmlRead, PartialEq, Debug)] +#[derive(XmlRead, PartialEq, Debug, Clone)] #[xml(tag = "groupId")] pub struct GroupId { #[xml(text)] - pub value: String, + pub(crate) value: String, } -#[derive(XmlRead, PartialEq, Debug)] +#[derive(XmlRead, PartialEq, Debug, Clone)] #[xml(tag = "artifactId")] pub struct ArtifactId { #[xml(text)] - pub value: String, + pub(crate) value: String, } -#[derive(XmlRead, PartialEq, Debug)] +#[derive(XmlRead, PartialEq, Debug, Clone)] #[xml(tag = "version")] pub struct Version { #[xml(text)] - pub value: String, + pub(crate) value: String, } #[derive(XmlRead, PartialEq, Debug)] #[xml(tag = "name")] pub struct Name { #[xml(text)] - value: String, + pub(crate) value: String, } #[derive(XmlRead, PartialEq, Debug)] #[xml(tag = "id")] pub struct Id { #[xml(text)] - value: String, + pub(crate) value: String, } #[derive(XmlRead, PartialEq, Debug)] #[xml(tag = "packaging")] pub struct Packaging { #[xml(text)] - value: String, + pub(crate) value: String, } #[derive(XmlRead, PartialEq, Debug)] #[xml(tag = "url")] pub struct Url { #[xml(text)] - value: String, + pub(crate) value: String, } #[derive(XmlRead, PartialEq, Debug)] #[xml(tag = "description")] pub struct Description { #[xml(text)] - value: String, + pub(crate) value: String, } #[derive(XmlRead, PartialEq, Debug)] #[xml(tag = "licenses")] pub struct Licenses { #[xml(child = "license")] - licenses: Vec, + pub(crate) licenses: Vec, } #[derive(XmlRead, PartialEq, Debug)] #[xml(tag = "distribution")] pub struct Distribution { #[xml(text)] - value: String, + pub(crate) value: String, } #[derive(XmlRead, PartialEq, Debug)] #[xml(tag = "license")] pub struct License { #[xml(child = "name")] - name: Name, + pub(crate) name: Name, #[xml(child = "url")] - url: Url, + pub(crate) url: Url, #[xml(child = "distribution")] - distribution: Option, + pub(crate) distribution: Option, } #[derive(XmlRead, PartialEq, Debug)] #[xml(tag = "parent")] pub struct Parent { #[xml(child = "groupId")] - group_id: GroupId, + pub(crate) group_id: GroupId, #[xml(child = "artifactId")] - artifact_id: ArtifactId, + pub(crate) artifact_id: ArtifactId, #[xml(child = "version")] - version: Version, + pub(crate) version: Version, } #[derive(XmlRead, PartialEq, Debug)] #[xml(tag = "scm")] pub struct Scm { #[xml(child = "url")] - url: Url, + pub(crate) url: Url, } #[derive(XmlRead, PartialEq, Debug)] #[xml(tag = "developers")] pub struct Developers { #[xml(child = "developer")] - developers: Vec, + pub(crate) developers: Vec, } #[derive(XmlRead, PartialEq, Debug)] #[xml(tag = "developer")] struct Developer { #[xml(child = "id")] - id: Option, + pub(crate) id: Option, #[xml(child = "name")] - name: Name, + pub(crate) name: Name, } #[derive(XmlRead, PartialEq, Debug)] #[xml(tag = "dependencies")] pub struct Dependencies { #[xml(child = "dependency")] - pub value: Vec, + pub(crate) value: Vec, } -#[derive(XmlRead, PartialEq, Debug)] +#[derive(XmlRead, PartialEq, Debug, Clone)] #[xml(tag = "dependency")] pub struct Dependency { #[xml(child = "groupId")] - pub group_id: GroupId, + pub(crate) group_id: GroupId, #[xml(child = "artifactId")] - pub artifact_id: ArtifactId, + pub(crate) artifact_id: ArtifactId, #[xml(child = "version")] - pub version: Option, + pub(crate) version: Option, } #[cfg(test)] diff --git a/src/maven/pom_view.rs b/src/maven/pom_view.rs new file mode 100644 index 0000000..c12f15b --- /dev/null +++ b/src/maven/pom_view.rs @@ -0,0 +1,165 @@ +use anyhow::Error; +use strong_xml::XmlRead; + +use crate::deploader; +use crate::maven::pom::{Dependency, Parent, Pom}; +use crate::project::{Artifact, Project}; + +/// offers a (non-mutable) view on the pom-as-xml-representation +/// the main use of this is that it resolves the parent information when needed +pub struct PomView<'a> { + pom: Pom, + project: &'a Project, + parent: Option>>, +} + +impl<'a> PomView<'a> { + pub fn new(pom: Pom, project: &'a Project) -> Result { + // recursively lookup the parents + if let Some(parent) = &pom.parent { + let parent_artifact = Artifact::new( + &parent.group_id.value, + &parent.artifact_id.value, + &parent.version.value, + ); + + let parent_pom = Pom::from_str( + &deploader::lookup_verified_pom(project, &parent_artifact)? + .pom_xml, + )?; + + Ok(Self { + pom, + project, + parent: Some(Box::new(PomView::new(parent_pom, project)?)), + }) + } else { + Ok(Self { + pom, + project, + parent: None, + }) + } + } + pub fn model_version(&self) -> String { + self.pom.model_version.value.clone() + } + pub fn parent(&self) -> Option { + self.pom.parent.as_ref().map(|p| ParentView { parent: &p }) + } + pub fn group_id(&self) -> Option { + //TODO get value from parent + self.pom.group_id.as_ref().map(|g| g.value.clone()) + } + pub fn artifact_id(&self) -> &String { + &self.pom.artifact_id.value + } + pub fn version(&self) -> String { + let mut version = &self.pom.version; + while version.is_none() { + if let Some(parent) = &self.parent { + version = &parent.pom.version; + } + } + version.as_ref().map(|v| v.value.clone()).unwrap() + // unwrap? This is assuming there is always a version, + // which sounds plausible but is unproven + // TODO resolve properties + } + pub fn name(&self) -> &String { + &self.pom.name.value + } + pub fn packaging(&self) -> Option { + self.pom.packaging.as_ref().map(|v| v.value.clone()) + } + pub fn url(&self) -> Option { + self.pom.url.as_ref().map(|v| v.value.clone()) + } + pub fn description(&self) -> &String { + &self.pom.description.value + } + + pub fn dependency_management(&self) -> DependencyManagementView { + DependencyManagementView { + dependencies: self + .pom + .dependency_management + .as_ref() + .map(|d| d.value.clone()) + .unwrap_or(vec![]), + } + } + + pub fn dependencies(&self) -> Vec { + let mut resolved_deps = vec![]; + if let Some(deps) = &self.pom.dependencies { + for dep in &deps.value { + let version = if let Some(version) = &dep.version { + Some(version.value.clone()) + } else { + if let Some(parent) = &self.parent { + search_version(dep, parent.dependency_management()) + } else { + None + } + }; + resolved_deps.push(DependencyView { + group_id: &dep.group_id.value, + artifact_id: &dep.artifact_id.value, + version: version.expect(&format!( + "Could not find version for {}:{}", + dep.group_id.value, dep.artifact_id.value + )), + }) + } + } + resolved_deps + } +} + +fn search_version(dep: &Dependency, depman: DependencyManagementView) -> Option { + for mandep in depman.dependencies { + if mandep.group_id == dep.group_id && mandep.artifact_id == dep.artifact_id { + return mandep.version.map(|v| v.value.clone()); + } + } + None +} + +struct ParentView<'a> { + parent: &'a Parent, +} + +impl<'a> ParentView<'a> { + pub fn group_id(&self) -> &'a String { + &self.parent.group_id.value + } + + pub fn artifact_id(&self) -> &'a String { + &self.parent.artifact_id.value + } + pub fn version(&self) -> &'a String { + &self.parent.version.value + } +} + +// a copied view +pub(crate) struct DependencyView<'a> { + artifact_id: &'a String, + group_id: &'a String, + version: String, +} + +struct DependencyManagementView { + dependencies: Vec, +} + +impl<'a> From> for Artifact { + fn from(value: DependencyView) -> Self { + Artifact::new( + value.group_id, + value.artifact_id, + &value.version, + ) + } +} \ No newline at end of file diff --git a/src/project.rs b/src/project.rs index 7395104..d61c840 100644 --- a/src/project.rs +++ b/src/project.rs @@ -99,9 +99,7 @@ pub fn load_project(jargo_file: Option<&str>) -> Result { fn repositories(table: Option<&Value>) -> Result, Error> { let mut repositories = vec!["https://repo.maven.apache.org/maven2".to_owned()]; - if let Some(table) = table { - let table = table.as_table(); - if let Some(table) = table { + if let Some(Some(table)) = table.map(|t|t.as_table()) { for repo in table { let repo_details = repo.1.clone(); if let Value::Table(repo_details) = repo_details { @@ -109,7 +107,6 @@ fn repositories(table: Option<&Value>) -> Result, Error> { repositories.push(url.into()); } } - } } } Ok(repositories)