WIP loading from maven repo's

This commit is contained in:
Shautvast 2024-01-21 15:57:46 +01:00
parent 3b81dd314b
commit 2c714a211c
8 changed files with 246 additions and 69 deletions

View file

@ -4,7 +4,7 @@ use std::sync::OnceLock;
/// Contains any config elements /// Contains any config elements
pub struct Config { pub struct Config {
pub cache_location: String, pub cache_location: String,
pub user_home: PathBuf, pub user_home: String,
} }
pub static CONFIG: OnceLock<Config> = OnceLock::new(); pub static CONFIG: OnceLock<Config> = OnceLock::new();
@ -12,9 +12,10 @@ pub static CONFIG: OnceLock<Config> = OnceLock::new();
/// default config /// default config
pub fn config() -> &'static Config { pub fn config() -> &'static Config {
CONFIG.get_or_init(|| { 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 { 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, user_home,
} }
}); });

View file

@ -1,5 +1,5 @@
use std::fs; use std::fs;
use std::fs::File; use std::fs::{create_dir, create_dir_all, File};
use std::io::prelude::*; use std::io::prelude::*;
use std::io::Write; use std::io::Write;
use std::path::Path; use std::path::Path;
@ -10,12 +10,12 @@ use sha1::{Digest, Sha1};
use strong_xml::XmlRead; use strong_xml::XmlRead;
use crate::config::config; use crate::config::config;
use crate::maven;
use crate::maven::metadata::Metadata; use crate::maven::metadata::Metadata;
use crate::maven::pom::Pom; use crate::maven::pom::Pom;
use crate::project::{Artifact, Project}; use crate::project::{Artifact, Project};
use colored::Colorize; use colored::Colorize;
use reqwest::StatusCode; use reqwest::StatusCode;
use crate::maven::pom_view::PomView;
/// Loads a list of artifacts from remote repo or local cache /// 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 // 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 // download remote jar if not in cache and check its SHA-1 checksum
let local_artifact_jar_path = format!( let local_artifact_jar_path = format!(
@ -73,13 +73,13 @@ fn load_artifact(project: &Project, artifact: &Artifact) -> Result<(), Error> {
println!("{}", pom_lookup.pom_xml); println!("{}", pom_lookup.pom_xml);
// parse pom file // parse pom file
let pom = Pom::from_str(&pom_lookup.pom_xml).unwrap(); let pom = Pom::from_str(&pom_lookup.pom_xml).unwrap();
let pom = PomView::new(pom, project)?;
//TODO exclusions //TODO exclusions
//TODO parents
if let Some(dependencies) = pom.dependencies { let artifacts = pom.dependencies().into_iter().map(|d| d.into()).collect();
let artifacts = dependencies.value.into_iter().map(|d| d.into()).collect(); load_artifacts(project, &artifacts)?;
load_artifacts(project, &artifacts)?;
}
Ok(()) 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 /// The result from find_pom is passed on to the caller so that the information can be used
/// for subsequent requests. /// for subsequent requests.
fn lookup_verified_pom( pub(crate) fn lookup_verified_pom(
project: &Project, project: &Project,
artifact: &Artifact, artifact: &Artifact,
local_artifact_loc: &str,
) -> Result<PomLookupResult, Error> { ) -> Result<PomLookupResult, Error> {
let local_artifact_pom_path = &format!( let local_artifact_pom_path = &format!(
"{}/{}-{}.pom", "{}/{}/{}-{}.pom",
local_artifact_loc, artifact.name, artifact.version config().cache_location, artifact.path, artifact.name, artifact.version
); );
// get pom from local or remote
let result = if exists(local_artifact_pom_path) { let result = if exists(local_artifact_pom_path) {
read_file_to_string(local_artifact_pom_path)? read_file_to_string(local_artifact_pom_path)?
} else { } else {
@ -111,7 +111,7 @@ fn lookup_verified_pom(
panic!("Could not find pom for {}", artifact.path) panic!("Could not find pom for {}", artifact.path)
} else { } else {
let result = result.unwrap(); 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 pom_xml = &result.pom_xml;
let local_artifact_pom_sha1_path = format!("{}.sha1", local_artifact_pom_path); let local_artifact_pom_sha1_path = format!("{}.sha1", local_artifact_pom_path);
@ -137,10 +137,10 @@ fn lookup_verified_pom(
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct PomLookupResult { pub(crate) struct PomLookupResult {
pom_xml: String, pub(crate) pom_xml: String,
resolved_repo: Option<String>, pub(crate) resolved_repo: Option<String>,
resolved_version: Option<String>, pub(crate) resolved_version: Option<String>,
} }
fn find_pom( fn find_pom(
@ -169,6 +169,12 @@ fn download_pom(
local_artifact_pom_path: &str, local_artifact_pom_path: &str,
repo: &str, repo: &str,
) -> Result<Option<String>, Error> { ) -> Result<Option<String>, 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!( let remote_artifact_pom_url = format!(
"{}/{}/{}-{}.pom", "{}/{}/{}-{}.pom",
repo, artifact.path, artifact.name, resolved_version repo, artifact.path, artifact.name, resolved_version
@ -269,7 +275,7 @@ fn load_snapshot_build_nr(artifact_path: &str, repo: &String) -> Result<Option<S
config().cache_location, config().cache_location,
artifact_path artifact_path
) )
.as_str(), .as_str(),
&body, &body,
)?; )?;
let metadata = Metadata::from_str(&body)?; let metadata = Metadata::from_str(&body)?;

View file

@ -1,5 +1,5 @@
pub mod compile; pub mod compile;
mod config; pub mod config;
pub mod deploader; pub mod deploader;
pub mod maven; pub mod maven;
pub mod project; pub mod project;

View file

@ -1,7 +1,13 @@
use anyhow::Error; use anyhow::Error;
use jargo::config::config;
/// sample main that will be replaced by a generic one later /// sample main that will be replaced by a generic one later
fn main() -> anyhow::Result<(), Error> { fn main() -> 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"))?; let project = jargo::project::load_project(Some("tests/sample_project/Jargo.toml"))?;
println!("{:?}", project); println!("{:?}", project);
jargo::deploader::load(&project)?; jargo::deploader::load(&project)?;

View file

@ -1,2 +1,3 @@
pub mod metadata; pub mod metadata;
pub mod pom; pub mod pom;
pub mod pom_view;

View file

@ -1,5 +1,4 @@
use strong_xml::XmlRead; use strong_xml::XmlRead;
use crate::maven::metadata::Versioning;
/// The Maven variant to parse poms /// The Maven variant to parse poms
/// These structs is directly modelled after the XML because that is what strong-xml plugin requires /// 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")] #[xml(tag = "project")]
pub struct Pom { pub struct Pom {
#[xml(child = "modelVersion")] #[xml(child = "modelVersion")]
pub model_version: ModelVersion, pub(crate) model_version: ModelVersion,
#[xml(child = "parent")] #[xml(child = "parent")]
pub parent: Option<Parent>, pub(crate) parent: Option<Parent>,
#[xml(child = "groupId")] #[xml(child = "groupId")]
pub group_id: Option<GroupId>, pub(crate) group_id: Option<GroupId>,
#[xml(child = "artifactId")] #[xml(child = "artifactId")]
pub artifact_id: ArtifactId, pub(crate) artifact_id: ArtifactId,
#[xml(child = "version")] #[xml(child = "version")]
pub version: Option<Version>, pub(crate) version: Option<Version>,
#[xml(child = "name")] #[xml(child = "name")]
pub name: Name, pub(crate) name: Name,
#[xml(child = "packaging")] #[xml(child = "packaging")]
pub packaging: Option<Packaging>, pub(crate) packaging: Option<Packaging>,
#[xml(child = "url")] #[xml(child = "url")]
pub url: Option<Url>, pub(crate) url: Option<Url>,
#[xml(child = "description")] #[xml(child = "description")]
pub description: Description, pub(crate) description: Description,
#[xml(child = "licenses")] #[xml(child = "licenses")]
pub licences: Option<Licenses>, pub(crate) licences: Option<Licenses>,
#[xml(child = "scm")] #[xml(child = "scm")]
pub scm: Option<Scm>, pub(crate) scm: Option<Scm>,
#[xml(child = "developers")] #[xml(child = "developers")]
pub developers: Option<Developers>, pub(crate) developers: Option<Developers>,
#[xml(child = "dependencies")] #[xml(child = "dependencies")]
pub dependencies: Option<Dependencies>, pub(crate) dependencies: Option<Dependencies>,
#[xml(child = "dependencyManagement")]
pub(crate) dependency_management: Option<Dependencies>,
} }
#[derive(XmlRead, PartialEq, Debug)] #[derive(XmlRead, PartialEq, Debug)]
#[xml(tag = "modelVersion")] #[xml(tag = "modelVersion")]
pub struct ModelVersion { pub struct ModelVersion {
#[xml(text)] #[xml(text)]
value: String, pub value: String,
} }
#[derive(XmlRead, PartialEq, Debug)] #[derive(XmlRead, PartialEq, Debug, Clone)]
#[xml(tag = "groupId")] #[xml(tag = "groupId")]
pub struct GroupId { pub struct GroupId {
#[xml(text)] #[xml(text)]
pub value: String, pub(crate) value: String,
} }
#[derive(XmlRead, PartialEq, Debug)] #[derive(XmlRead, PartialEq, Debug, Clone)]
#[xml(tag = "artifactId")] #[xml(tag = "artifactId")]
pub struct ArtifactId { pub struct ArtifactId {
#[xml(text)] #[xml(text)]
pub value: String, pub(crate) value: String,
} }
#[derive(XmlRead, PartialEq, Debug)] #[derive(XmlRead, PartialEq, Debug, Clone)]
#[xml(tag = "version")] #[xml(tag = "version")]
pub struct Version { pub struct Version {
#[xml(text)] #[xml(text)]
pub value: String, pub(crate) value: String,
} }
#[derive(XmlRead, PartialEq, Debug)] #[derive(XmlRead, PartialEq, Debug)]
#[xml(tag = "name")] #[xml(tag = "name")]
pub struct Name { pub struct Name {
#[xml(text)] #[xml(text)]
value: String, pub(crate) value: String,
} }
#[derive(XmlRead, PartialEq, Debug)] #[derive(XmlRead, PartialEq, Debug)]
#[xml(tag = "id")] #[xml(tag = "id")]
pub struct Id { pub struct Id {
#[xml(text)] #[xml(text)]
value: String, pub(crate) value: String,
} }
#[derive(XmlRead, PartialEq, Debug)] #[derive(XmlRead, PartialEq, Debug)]
#[xml(tag = "packaging")] #[xml(tag = "packaging")]
pub struct Packaging { pub struct Packaging {
#[xml(text)] #[xml(text)]
value: String, pub(crate) value: String,
} }
#[derive(XmlRead, PartialEq, Debug)] #[derive(XmlRead, PartialEq, Debug)]
#[xml(tag = "url")] #[xml(tag = "url")]
pub struct Url { pub struct Url {
#[xml(text)] #[xml(text)]
value: String, pub(crate) value: String,
} }
#[derive(XmlRead, PartialEq, Debug)] #[derive(XmlRead, PartialEq, Debug)]
#[xml(tag = "description")] #[xml(tag = "description")]
pub struct Description { pub struct Description {
#[xml(text)] #[xml(text)]
value: String, pub(crate) value: String,
} }
#[derive(XmlRead, PartialEq, Debug)] #[derive(XmlRead, PartialEq, Debug)]
#[xml(tag = "licenses")] #[xml(tag = "licenses")]
pub struct Licenses { pub struct Licenses {
#[xml(child = "license")] #[xml(child = "license")]
licenses: Vec<License>, pub(crate) licenses: Vec<License>,
} }
#[derive(XmlRead, PartialEq, Debug)] #[derive(XmlRead, PartialEq, Debug)]
#[xml(tag = "distribution")] #[xml(tag = "distribution")]
pub struct Distribution { pub struct Distribution {
#[xml(text)] #[xml(text)]
value: String, pub(crate) value: String,
} }
#[derive(XmlRead, PartialEq, Debug)] #[derive(XmlRead, PartialEq, Debug)]
#[xml(tag = "license")] #[xml(tag = "license")]
pub struct License { pub struct License {
#[xml(child = "name")] #[xml(child = "name")]
name: Name, pub(crate) name: Name,
#[xml(child = "url")] #[xml(child = "url")]
url: Url, pub(crate) url: Url,
#[xml(child = "distribution")] #[xml(child = "distribution")]
distribution: Option<Distribution>, pub(crate) distribution: Option<Distribution>,
} }
#[derive(XmlRead, PartialEq, Debug)] #[derive(XmlRead, PartialEq, Debug)]
#[xml(tag = "parent")] #[xml(tag = "parent")]
pub struct Parent { pub struct Parent {
#[xml(child = "groupId")] #[xml(child = "groupId")]
group_id: GroupId, pub(crate) group_id: GroupId,
#[xml(child = "artifactId")] #[xml(child = "artifactId")]
artifact_id: ArtifactId, pub(crate) artifact_id: ArtifactId,
#[xml(child = "version")] #[xml(child = "version")]
version: Version, pub(crate) version: Version,
} }
#[derive(XmlRead, PartialEq, Debug)] #[derive(XmlRead, PartialEq, Debug)]
#[xml(tag = "scm")] #[xml(tag = "scm")]
pub struct Scm { pub struct Scm {
#[xml(child = "url")] #[xml(child = "url")]
url: Url, pub(crate) url: Url,
} }
#[derive(XmlRead, PartialEq, Debug)] #[derive(XmlRead, PartialEq, Debug)]
#[xml(tag = "developers")] #[xml(tag = "developers")]
pub struct Developers { pub struct Developers {
#[xml(child = "developer")] #[xml(child = "developer")]
developers: Vec<Developer>, pub(crate) developers: Vec<Developer>,
} }
#[derive(XmlRead, PartialEq, Debug)] #[derive(XmlRead, PartialEq, Debug)]
#[xml(tag = "developer")] #[xml(tag = "developer")]
struct Developer { struct Developer {
#[xml(child = "id")] #[xml(child = "id")]
id: Option<Id>, pub(crate) id: Option<Id>,
#[xml(child = "name")] #[xml(child = "name")]
name: Name, pub(crate) name: Name,
} }
#[derive(XmlRead, PartialEq, Debug)] #[derive(XmlRead, PartialEq, Debug)]
#[xml(tag = "dependencies")] #[xml(tag = "dependencies")]
pub struct Dependencies { pub struct Dependencies {
#[xml(child = "dependency")] #[xml(child = "dependency")]
pub value: Vec<Dependency>, pub(crate) value: Vec<Dependency>,
} }
#[derive(XmlRead, PartialEq, Debug)] #[derive(XmlRead, PartialEq, Debug, Clone)]
#[xml(tag = "dependency")] #[xml(tag = "dependency")]
pub struct Dependency { pub struct Dependency {
#[xml(child = "groupId")] #[xml(child = "groupId")]
pub group_id: GroupId, pub(crate) group_id: GroupId,
#[xml(child = "artifactId")] #[xml(child = "artifactId")]
pub artifact_id: ArtifactId, pub(crate) artifact_id: ArtifactId,
#[xml(child = "version")] #[xml(child = "version")]
pub version: Option<Version>, pub(crate) version: Option<Version>,
} }
#[cfg(test)] #[cfg(test)]

165
src/maven/pom_view.rs Normal file
View file

@ -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<Box<PomView<'a>>>,
}
impl<'a> PomView<'a> {
pub fn new(pom: Pom, project: &'a Project) -> Result<Self, Error> {
// 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<ParentView> {
self.pom.parent.as_ref().map(|p| ParentView { parent: &p })
}
pub fn group_id(&self) -> Option<String> {
//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<String> {
self.pom.packaging.as_ref().map(|v| v.value.clone())
}
pub fn url(&self) -> Option<String> {
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<DependencyView> {
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<String> {
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<Dependency>,
}
impl<'a> From<DependencyView<'a>> for Artifact {
fn from(value: DependencyView) -> Self {
Artifact::new(
value.group_id,
value.artifact_id,
&value.version,
)
}
}

View file

@ -99,9 +99,7 @@ pub fn load_project(jargo_file: Option<&str>) -> Result<Project, Error> {
fn repositories(table: Option<&Value>) -> Result<Vec<String>, Error> { fn repositories(table: Option<&Value>) -> Result<Vec<String>, Error> {
let mut repositories = vec!["https://repo.maven.apache.org/maven2".to_owned()]; let mut repositories = vec!["https://repo.maven.apache.org/maven2".to_owned()];
if let Some(table) = table { if let Some(Some(table)) = table.map(|t|t.as_table()) {
let table = table.as_table();
if let Some(table) = table {
for repo in table { for repo in table {
let repo_details = repo.1.clone(); let repo_details = repo.1.clone();
if let Value::Table(repo_details) = repo_details { if let Value::Table(repo_details) = repo_details {
@ -109,7 +107,6 @@ fn repositories(table: Option<&Value>) -> Result<Vec<String>, Error> {
repositories.push(url.into()); repositories.push(url.into());
} }
} }
}
} }
} }
Ok(repositories) Ok(repositories)