Compare commits
No commits in common. "e2954297f94ac5ea5536f30482f8681f6264777e" and "d3423bdf29cc75b3bd3aa5fd4d54509152046a5f" have entirely different histories.
e2954297f9
...
d3423bdf29
15 changed files with 302 additions and 836 deletions
108
Cargo.lock
generated
108
Cargo.lock
generated
|
|
@ -17,15 +17,6 @@ version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aho-corasick"
|
|
||||||
version = "1.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.79"
|
version = "1.0.79"
|
||||||
|
|
@ -445,10 +436,10 @@ dependencies = [
|
||||||
"colored",
|
"colored",
|
||||||
"hex",
|
"hex",
|
||||||
"home",
|
"home",
|
||||||
"regex",
|
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"sha1",
|
"sha1",
|
||||||
"strong-xml",
|
"strong-xml",
|
||||||
|
"tokio",
|
||||||
"toml",
|
"toml",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -485,6 +476,16 @@ version = "0.4.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"
|
checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.20"
|
version = "0.4.20"
|
||||||
|
|
@ -610,6 +611,29 @@ dependencies = [
|
||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
"parking_lot_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot_core"
|
||||||
|
version = "0.9.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
"smallvec",
|
||||||
|
"windows-targets 0.48.5",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.3.1"
|
version = "2.3.1"
|
||||||
|
|
@ -661,35 +685,6 @@ dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex"
|
|
||||||
version = "1.10.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
|
|
||||||
dependencies = [
|
|
||||||
"aho-corasick",
|
|
||||||
"memchr",
|
|
||||||
"regex-automata",
|
|
||||||
"regex-syntax",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex-automata"
|
|
||||||
version = "0.4.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3b7fa1134405e2ec9353fd416b17f8dacd46c473d7d3fd1cf202706a14eb792a"
|
|
||||||
dependencies = [
|
|
||||||
"aho-corasick",
|
|
||||||
"memchr",
|
|
||||||
"regex-syntax",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex-syntax"
|
|
||||||
version = "0.8.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest"
|
name = "reqwest"
|
||||||
version = "0.11.23"
|
version = "0.11.23"
|
||||||
|
|
@ -762,6 +757,12 @@ dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "security-framework"
|
name = "security-framework"
|
||||||
version = "2.9.2"
|
version = "2.9.2"
|
||||||
|
|
@ -848,6 +849,15 @@ dependencies = [
|
||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-registry"
|
||||||
|
version = "1.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.9"
|
version = "0.4.9"
|
||||||
|
|
@ -857,6 +867,12 @@ dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.5.5"
|
version = "0.5.5"
|
||||||
|
|
@ -973,11 +989,25 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"mio",
|
"mio",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
|
"parking_lot",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
"signal-hook-registry",
|
||||||
"socket2",
|
"socket2",
|
||||||
|
"tokio-macros",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-macros"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.48",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-native-tls"
|
name = "tokio-native-tls"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@ anyhow = "1.0"
|
||||||
strong-xml = "0.6"
|
strong-xml = "0.6"
|
||||||
colored = "2.0"
|
colored = "2.0"
|
||||||
reqwest = {version = "0.11", features = ["blocking"]}
|
reqwest = {version = "0.11", features = ["blocking"]}
|
||||||
|
tokio = { version = "1", features = ["full"] }
|
||||||
bytes = "1.5"
|
bytes = "1.5"
|
||||||
home = "0.5"
|
home = "0.5"
|
||||||
sha1 = "0.10"
|
sha1 = "0.10"
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
regex="1.10"
|
|
||||||
12
README.md
12
README.md
|
|
@ -1,17 +1,5 @@
|
||||||
**Jargo**
|
**Jargo**
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**edit:**
|
|
||||||
|
|
||||||
work is stalled, since the xmlparser (hard-xml) does not allow xml entities that are not known upfront, ie the <properties> element has them for every property.
|
|
||||||
I don't even think it should support them and/or the regular java libs like jaxb would do that.
|
|
||||||
So ... thinking of forking, but the code is really hard (declarative macro's) and not documented...
|
|
||||||
|
|
||||||
background: we need to parse maven xml to be able to use remote maven repositories.
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
An experimental build tool for Java taking inspiration from Cargo.
|
An experimental build tool for Java taking inspiration from Cargo.
|
||||||
|
|
||||||
And it's called *Jargo*. I do not wish to put a J in front of anything, as is the java tradition,
|
And it's called *Jargo*. I do not wish to put a J in front of anything, as is the java tradition,
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,11 @@ use std::fs;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
use crate::compile::PathNode::*;
|
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
|
use crate::compile::PathNode::*;
|
||||||
|
|
||||||
use crate::project::Project;
|
use crate::Project;
|
||||||
|
|
||||||
const SOURCES: &str = "src/main/java";
|
const SOURCES: &str = "src/main/java";
|
||||||
const TESTSOURCES: &str = "src/test/java";
|
const TESTSOURCES: &str = "src/test/java";
|
||||||
|
|
@ -16,6 +16,7 @@ const TESTRESOURCES: &str = "src/main/java";
|
||||||
const TARGET_MAIN: &str = "target/classes";
|
const TARGET_MAIN: &str = "target/classes";
|
||||||
const TARGET_TEST: &str = "target/test-classes";
|
const TARGET_TEST: &str = "target/test-classes";
|
||||||
|
|
||||||
|
|
||||||
/// internal view of the src filesystem
|
/// internal view of the src filesystem
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum PathNode {
|
enum PathNode {
|
||||||
|
|
@ -25,25 +26,21 @@ enum PathNode {
|
||||||
|
|
||||||
/// runs the compile stage
|
/// runs the compile stage
|
||||||
pub fn run(project: &Project) -> Result<(), Error> {
|
pub fn run(project: &Project) -> Result<(), Error> {
|
||||||
println!(
|
println!("{} {}.{}-{}", "Compiling".green(), project.group, project.name, project.version);
|
||||||
"{} {}.{}-{}",
|
|
||||||
"Compiling".green(),
|
|
||||||
project.group,
|
|
||||||
project.name,
|
|
||||||
project.version
|
|
||||||
);
|
|
||||||
|
|
||||||
let root = PathBuf::from(&project.project_root).join(SOURCES);
|
let root = PathBuf::from(&project.project_root).join(SOURCES);
|
||||||
|
|
||||||
let mut src_tree = DirNode(root.clone(), Vec::new(), Vec::new());
|
let mut src_tree = DirNode(root.clone(), Vec::new(), Vec::new());
|
||||||
determine_src_tree(root, &mut src_tree)?;
|
determine_src_tree(root, &mut src_tree)?;
|
||||||
compile_source_dir(project, &mut src_tree)?;
|
println!("{:?}", src_tree);
|
||||||
|
|
||||||
|
compile_sourcedir(project, &mut src_tree)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// walks the source tree and compiles any java files
|
/// walks the source tree and compiles any java files
|
||||||
fn compile_source_dir(project: &Project, src_tree: &mut PathNode) -> Result<(), Error> {
|
fn compile_sourcedir(project: &Project, src_tree: &mut PathNode) -> Result<(), Error> {
|
||||||
if let DirNode(dir_name, subdirs, contents) = src_tree {
|
if let DirNode(dir_name, subdirs, contents) = src_tree {
|
||||||
if !contents.is_empty() {
|
if !contents.is_empty() {
|
||||||
let mut javac = if cfg!(target_os = "windows") {
|
let mut javac = if cfg!(target_os = "windows") {
|
||||||
|
|
@ -81,7 +78,7 @@ fn compile_source_dir(project: &Project, src_tree: &mut PathNode) -> Result<(),
|
||||||
println!("{}", String::from_utf8(output.stdout)?);
|
println!("{}", String::from_utf8(output.stdout)?);
|
||||||
}
|
}
|
||||||
for subdir in subdirs {
|
for subdir in subdirs {
|
||||||
compile_source_dir(project, subdir)?;
|
compile_sourcedir(project, subdir)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@ 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: String,
|
pub maven_central: String,
|
||||||
|
pub user_home: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub static CONFIG: OnceLock<Config> = OnceLock::new();
|
pub static CONFIG: OnceLock<Config> = OnceLock::new();
|
||||||
|
|
@ -12,11 +13,11 @@ 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().map(|p|p.to_str().unwrap().to_owned())
|
let user_home = home::home_dir().unwrap();
|
||||||
.expect("Can not find $HOME in environment");
|
|
||||||
Config {
|
Config {
|
||||||
cache_location: format!("{}/jargo/repo", user_home).into(), //TODO make it '.jargo'
|
cache_location: format!("{}/jargo/repo", user_home.to_str().unwrap()).into(),//TODO make '.jargo'
|
||||||
user_home,
|
user_home,
|
||||||
|
maven_central: "https://repo.maven.apache.org/maven2".into()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
CONFIG.get().unwrap()
|
CONFIG.get().unwrap()
|
||||||
|
|
|
||||||
296
src/deploader.rs
296
src/deploader.rs
|
|
@ -1,5 +1,5 @@
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::fs::{create_dir, create_dir_all, File};
|
use std::fs::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;
|
||||||
|
|
@ -9,13 +9,10 @@ use bytes::Bytes;
|
||||||
use sha1::{Digest, Sha1};
|
use sha1::{Digest, Sha1};
|
||||||
use strong_xml::XmlRead;
|
use strong_xml::XmlRead;
|
||||||
|
|
||||||
|
use crate::Artifact;
|
||||||
use crate::config::config;
|
use crate::config::config;
|
||||||
use crate::maven::metadata::Metadata;
|
use crate::pom::model::Pom;
|
||||||
use crate::maven::pom::Pom;
|
|
||||||
use crate::project::{Artifact, Project};
|
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
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
|
||||||
///
|
///
|
||||||
|
|
@ -28,284 +25,100 @@ use crate::maven::pom_view::PomView;
|
||||||
/// 7. if not downloads it from a repo (now mavencentral only)
|
/// 7. if not downloads it from a repo (now mavencentral only)
|
||||||
/// 8. verifies the SHA1 as for the jar
|
/// 8. verifies the SHA1 as for the jar
|
||||||
/// 9. extracts the transitive dependencies from the pom and recurses to (1) for the list of dependencies
|
/// 9. extracts the transitive dependencies from the pom and recurses to (1) for the list of dependencies
|
||||||
pub fn load(project: &Project) -> Result<(), Error> {
|
pub fn load_artifacts(artifacts: &[Artifact]) -> Result<(), Error> {
|
||||||
load_artifacts(project, &project.main_dependencies)?;
|
|
||||||
load_artifacts(project, &project.test_dependencies)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_artifacts(project: &Project, artifacts: &Vec<Artifact>) -> Result<(), Error> {
|
|
||||||
for art in artifacts {
|
for art in artifacts {
|
||||||
load_artifact(project, art)?;
|
load_artifact(art)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// loads the artifact (all data and metadata for 1 artifact)
|
fn load_artifact(art: &Artifact) -> Result<(), Error> {
|
||||||
/// 1. create dir in local cache if necessary
|
let artifact_path = format!("{}/{}/{}", art.group.replace(".", "/"), art.name, art.version);
|
||||||
/// 2. look up the pom
|
|
||||||
/// 3. look up the jar
|
|
||||||
fn load_artifact(project: &Project, artifact: &Artifact) -> Result<(), Error> {
|
|
||||||
// check/create artifact directory
|
// check/create artifact directory
|
||||||
let local_artifact_loc = format!("{}/{}", config().cache_location, artifact.path);
|
let local_artifact_loc = format!("{}/{}", config().cache_location, artifact_path);
|
||||||
if !exists(&local_artifact_loc) {
|
if !exists(&local_artifact_loc) {
|
||||||
fs::create_dir_all(&local_artifact_loc)?;
|
fs::create_dir_all(&local_artifact_loc)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// download remote pom if not in cache
|
|
||||||
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!("{}/{}-{}.jar", local_artifact_loc, art.name, art.version);
|
||||||
"{}/{}-{}.jar",
|
|
||||||
local_artifact_loc, artifact.name, artifact.version
|
|
||||||
);
|
|
||||||
if !exists(&local_artifact_jar_path) {
|
if !exists(&local_artifact_jar_path) {
|
||||||
lookup_verified_jar(
|
download_verify_artifact_jar(art, &artifact_path, &local_artifact_jar_path)?;
|
||||||
project,
|
|
||||||
artifact,
|
|
||||||
&local_artifact_jar_path,
|
|
||||||
&pom_lookup.resolved_repo.unwrap(),
|
|
||||||
&pom_lookup.resolved_version.unwrap(),
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("{}", pom_lookup.pom_xml);
|
// download remote pom if not in cache //TODO check its SHA-1 checksum
|
||||||
|
let pom_xml = download_verify_pom(art, artifact_path, local_artifact_loc)?;
|
||||||
|
|
||||||
// parse pom file
|
// parse pom file
|
||||||
let pom = Pom::from_str(&pom_lookup.pom_xml).unwrap();
|
let pom = Pom::from_str(&pom_xml).unwrap();
|
||||||
let pom = PomView::new(pom, project)?;
|
|
||||||
|
|
||||||
//TODO exclusions
|
|
||||||
|
|
||||||
let artifacts = pom.dependencies().into_iter().map(|d| d.into()).collect();
|
|
||||||
load_artifacts(project, &artifacts)?;
|
|
||||||
|
|
||||||
|
let dependencies: Vec<Artifact> = pom.dependencies.value.into_iter().map(|d| d.into()).collect();
|
||||||
|
load_artifacts(&dependencies)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// main function to download and verify the pom xml.
|
fn download_verify_pom(art: &Artifact, artifact_path: String, local_artifact_loc: String) -> Result<String, Error> {
|
||||||
/// 1. check if file is locally cached and load if it is
|
let local_artifact_pom_path = &format!("{}/{}-{}.pom", local_artifact_loc, art.name, art.version);
|
||||||
/// 2. or find a suitable repo (deferred to find_pom)
|
let remote_artifact_pom_url = format!("{}/{}/{}-{}.pom", config().maven_central, artifact_path, art.name, art.version);
|
||||||
/// 3. this function returns the downloaded pom, together with the repo it was found in and the "resolved version'
|
let pom_xml = if !exists(local_artifact_pom_path) {
|
||||||
/// this is only applicable to SNAPSHOT's where x-SNAPSHOT is resolved to x-<timestamp>-<build_nr>
|
println!("{} {}", "Downloading".green(), remote_artifact_pom_url);
|
||||||
/// 4. download the SHA1 file from the same location
|
let body = reqwest::blocking::get(&remote_artifact_pom_url)?.text().unwrap();
|
||||||
/// 5. validate if the checksum equals the checksum calculated from the pom
|
println!("{} {}", "Downloaded".green(), remote_artifact_pom_url);
|
||||||
///
|
write_text(local_artifact_pom_path, &body)?;
|
||||||
/// The result from find_pom is passed on to the caller so that the information can be used
|
body
|
||||||
/// for subsequent requests.
|
} else {
|
||||||
pub(crate) fn lookup_verified_pom(
|
|
||||||
project: &Project,
|
|
||||||
artifact: &Artifact,
|
|
||||||
) -> Result<PomLookupResult, Error> {
|
|
||||||
let local_artifact_pom_path = &format!(
|
|
||||||
"{}/{}/{}-{}.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)?
|
read_file_to_string(local_artifact_pom_path)?
|
||||||
} else {
|
|
||||||
find_pom(project, artifact, local_artifact_pom_path)?
|
|
||||||
};
|
};
|
||||||
if result.is_none() {
|
|
||||||
panic!("Could not find pom for {}", artifact.path)
|
|
||||||
} else {
|
|
||||||
let result = result.unwrap();
|
|
||||||
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);
|
let local_artifact_pom_sha1_path = format!("{}.sha1", local_artifact_pom_path);
|
||||||
|
|
||||||
// verify jarfile with SHA1 checksum (which is hex encoded)
|
// verify jarfile with SHA1 checksum (which is hex encoded)
|
||||||
let checksum = if !exists(&local_artifact_pom_sha1_path) {
|
let checksum = hex::decode(
|
||||||
download_checksum(repo_with_pom, &local_artifact_pom_sha1_path)?
|
if !exists(&local_artifact_pom_sha1_path) {
|
||||||
|
download_checksum(&remote_artifact_pom_url, &local_artifact_pom_sha1_path)?
|
||||||
} else {
|
} else {
|
||||||
read_file_to_bytes(local_artifact_pom_sha1_path)?
|
read_file_to_bytes(local_artifact_pom_sha1_path)?
|
||||||
};
|
})?;
|
||||||
if let Some(checksum) = checksum {
|
let validated = validate_checksum_text(&pom_xml, checksum);
|
||||||
let validated = validate_checksum_text(&pom_xml, hex::decode(checksum)?);
|
if !validated {
|
||||||
return if !validated {
|
Err(anyhow!("SHA1 checksum for {} is not valid", remote_artifact_pom_url))
|
||||||
Err(anyhow!("SHA1 checksum for {} is not valid", artifact.path))
|
} else { Ok(pom_xml) }
|
||||||
} else {
|
|
||||||
Ok(result.clone()) // SHA1 ok
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// no SHA1 found
|
|
||||||
Ok(result.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub(crate) struct PomLookupResult {
|
|
||||||
pub(crate) pom_xml: String,
|
|
||||||
pub(crate) resolved_repo: Option<String>,
|
|
||||||
pub(crate) resolved_version: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_pom(
|
|
||||||
project: &Project,
|
|
||||||
artifact: &Artifact,
|
|
||||||
local_artifact_pom_path: &str,
|
|
||||||
) -> Result<Option<PomLookupResult>, Error> {
|
|
||||||
for repo in &project.repositories {
|
|
||||||
let resolved_version = resolve_version(&artifact, repo)?;
|
|
||||||
let r = download_pom(artifact, &resolved_version, local_artifact_pom_path, &repo)?;
|
|
||||||
if r.is_some() {
|
|
||||||
return Ok(Some(PomLookupResult {
|
|
||||||
pom_xml: r.unwrap(),
|
|
||||||
resolved_repo: Some(repo.clone()),
|
|
||||||
resolved_version: Some(resolved_version),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// returns the pom and the repo where it was found
|
|
||||||
fn download_pom(
|
|
||||||
artifact: &Artifact,
|
|
||||||
resolved_version: &str,
|
|
||||||
local_artifact_pom_path: &str,
|
|
||||||
repo: &str,
|
|
||||||
) -> 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!(
|
|
||||||
"{}/{}/{}-{}.pom",
|
|
||||||
repo, artifact.path, artifact.name, resolved_version
|
|
||||||
);
|
|
||||||
|
|
||||||
println!("{} {}", "Downloading".green(), remote_artifact_pom_url);
|
|
||||||
let response = reqwest::blocking::get(&remote_artifact_pom_url)?;
|
|
||||||
if response.status().is_success() {
|
|
||||||
let body = response.text().unwrap();
|
|
||||||
println!("{} {}", "Downloaded".green(), remote_artifact_pom_url);
|
|
||||||
write_text(local_artifact_pom_path, &body)?;
|
|
||||||
Ok(Some((body)))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Download jar from remote repo and check its signature
|
/// Download jar from remote repo and check its signature
|
||||||
/// For now it's a blocking call, because async and recursion add unwanted complexity/I don't understand that
|
/// For now it's a blocking call, because async and recursion add unwanted complexity/I don't understand that
|
||||||
/// TODO add progress bar
|
/// TODO add progress bar
|
||||||
fn lookup_verified_jar(
|
fn download_verify_artifact_jar(art: &Artifact, artifact_path: &str, local_artifact_jar_path: &str) -> Result<(), Error> {
|
||||||
project: &Project,
|
let remote_artifact_jar_url = format!("{}/{}/{}-{}.jar", config().maven_central, artifact_path, art.name, art.version);
|
||||||
artifact: &Artifact,
|
|
||||||
local_artifact_jar_path: &str,
|
|
||||||
resolved_repo: &str,
|
|
||||||
resolved_version: &str,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let remote_artifact_jar_url = format!(
|
|
||||||
"{}/{}/{}-{}.jar",
|
|
||||||
resolved_repo, artifact.path, artifact.name, resolved_version
|
|
||||||
);
|
|
||||||
|
|
||||||
println!("{} {}", "Downloading".green(), remote_artifact_jar_url);
|
println!("{} {}", "Downloading".green(), remote_artifact_jar_url);
|
||||||
let response = reqwest::blocking::get(&remote_artifact_jar_url)?;
|
let jar = reqwest::blocking::get(&remote_artifact_jar_url)?.bytes().unwrap();
|
||||||
if response.status().is_success() {
|
|
||||||
let jar = response.bytes().unwrap();
|
|
||||||
println!("{} {}", "Downloaded".green(), remote_artifact_jar_url);
|
println!("{} {}", "Downloaded".green(), remote_artifact_jar_url);
|
||||||
write_bytes_to_file(&local_artifact_jar_path, &jar)?;
|
write_bytes_to_file(&local_artifact_jar_path, &jar)?;
|
||||||
|
|
||||||
let local_artifact_jar_sha1_path = format!("{}.sha1", local_artifact_jar_path);
|
let local_artifact_jar_sha1_path = format!("{}.sha1", local_artifact_jar_path);
|
||||||
|
|
||||||
// verify jarfile with SHA1 checksum (which is hex encoded)
|
// verify jarfile with SHA1 checksum (which is hex encoded)
|
||||||
let checksum = if !exists(&local_artifact_jar_sha1_path) {
|
let checksum = hex::decode(
|
||||||
download_checksum(
|
if !exists(&local_artifact_jar_sha1_path) {
|
||||||
Some(&remote_artifact_jar_url),
|
download_checksum(&remote_artifact_jar_url, &local_artifact_jar_sha1_path)?
|
||||||
&local_artifact_jar_sha1_path,
|
|
||||||
)?
|
|
||||||
} else {
|
} else {
|
||||||
read_file_to_bytes(local_artifact_jar_sha1_path)?
|
read_file_to_bytes(local_artifact_jar_sha1_path)?
|
||||||
};
|
})?;
|
||||||
return if let Some(checksum) = checksum {
|
let validated = validate_checksum_bytes(&jar, checksum);
|
||||||
let validated = validate_checksum_bytes(&jar, hex::decode(checksum)?);
|
|
||||||
if !validated {
|
if !validated {
|
||||||
Err(anyhow!(
|
Err(anyhow!("SHA1 checksum for {} is not valid", remote_artifact_jar_url))
|
||||||
"SHA1 checksum for {} is not valid",
|
} else { Ok(()) }
|
||||||
remote_artifact_jar_url
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Ok(()) // checksum found and ok
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(()) // no checksum found
|
|
||||||
};
|
|
||||||
}
|
|
||||||
panic!(
|
|
||||||
"Artifact {} not found in remote repository {}",
|
|
||||||
artifact.path, resolved_repo
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_version(artifact: &Artifact, repo: &String) -> Result<String, Error> {
|
|
||||||
Ok(if artifact.is_snapshot() {
|
|
||||||
let build_nr = load_snapshot_build_nr(&artifact.path, repo)?;
|
|
||||||
if let Some(build_nr) = build_nr {
|
|
||||||
artifact.version.replace("SNAPSHOT", &build_nr)
|
|
||||||
} else {
|
|
||||||
artifact.version.clone()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
artifact.version.clone()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Snapshots in a maven repo can be in the form
|
fn download_checksum(remote_artifact_url: &String, local_artifact_jar_sha1_path: &String) -> Result<Vec<u8>, Error> {
|
||||||
/// 'spring-boot-starter-web-3.0.0-20221124.170206-1099.jar'
|
|
||||||
/// while we ask for
|
|
||||||
/// 'spring-boot-starter-web-3.0.0-SNAPSHOT.jar'
|
|
||||||
/// the metadata xml contains the info on what snapshot to download
|
|
||||||
/// so we download and parse it
|
|
||||||
fn load_snapshot_build_nr(artifact_path: &str, repo: &String) -> Result<Option<String>, Error> {
|
|
||||||
let metadata_url = format!("{}/{}/maven-metadata.xml", repo, artifact_path);
|
|
||||||
let response = reqwest::blocking::get(&metadata_url)?;
|
|
||||||
if response.status().is_success() {
|
|
||||||
let body = response.text().unwrap();
|
|
||||||
write_text(
|
|
||||||
format!(
|
|
||||||
"{}/{}/maven-metadata.xml",
|
|
||||||
config().cache_location,
|
|
||||||
artifact_path
|
|
||||||
)
|
|
||||||
.as_str(),
|
|
||||||
&body,
|
|
||||||
)?;
|
|
||||||
let metadata = Metadata::from_str(&body)?;
|
|
||||||
Ok(Some(format!(
|
|
||||||
"{}-{}",
|
|
||||||
metadata.versioning.snapshot.timestamp.value,
|
|
||||||
metadata.versioning.snapshot.build_number.value
|
|
||||||
)))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn download_checksum(
|
|
||||||
remote_artifact_url: Option<&String>,
|
|
||||||
local_artifact_jar_sha1_path: &str,
|
|
||||||
) -> Result<Option<Vec<u8>>, Error> {
|
|
||||||
if let Some(remote_artifact_url) = remote_artifact_url {
|
|
||||||
let remote_artifact_jar_sha1_url = format!("{}.sha1", remote_artifact_url);
|
let remote_artifact_jar_sha1_url = format!("{}.sha1", remote_artifact_url);
|
||||||
let response = reqwest::blocking::get(&remote_artifact_jar_sha1_url)?;
|
let jar_checksum = reqwest::blocking::get(&remote_artifact_jar_sha1_url)?.bytes().unwrap();
|
||||||
if response.status() == StatusCode::OK {
|
|
||||||
let jar_checksum = response.bytes().unwrap();
|
|
||||||
write_bytes_to_file(&local_artifact_jar_sha1_path, &jar_checksum)?;
|
write_bytes_to_file(&local_artifact_jar_sha1_path, &jar_checksum)?;
|
||||||
Ok(Some(jar_checksum.to_vec()))
|
Ok(jar_checksum.to_vec())
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_checksum_bytes(jar: &Bytes, checksum: Vec<u8>) -> bool {
|
fn validate_checksum_bytes(jar: &Bytes, checksum: Vec<u8>) -> bool {
|
||||||
|
|
@ -326,6 +139,7 @@ fn exists(path: &str) -> bool {
|
||||||
Path::new(path).exists()
|
Path::new(path).exists()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn write_bytes_to_file(jar_path: &str, bytes: &Bytes) -> Result<(), Error> {
|
fn write_bytes_to_file(jar_path: &str, bytes: &Bytes) -> Result<(), Error> {
|
||||||
let mut file = File::create(jar_path)?;
|
let mut file = File::create(jar_path)?;
|
||||||
file.write_all(bytes)?;
|
file.write_all(bytes)?;
|
||||||
|
|
@ -338,20 +152,16 @@ fn write_text(path: &str, contents: &String) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_file_to_string(local_artifact_pom_path: &str) -> Result<Option<PomLookupResult>, Error> {
|
fn read_file_to_string(local_artifact_pom_path: &str) -> Result<String, Error> {
|
||||||
let mut file = File::open(local_artifact_pom_path)?;
|
let mut file = File::open(local_artifact_pom_path)?;
|
||||||
let mut contents = String::new();
|
let mut contents = String::new();
|
||||||
file.read_to_string(&mut contents)?;
|
file.read_to_string(&mut contents)?;
|
||||||
Ok(Some(PomLookupResult {
|
Ok(contents)
|
||||||
pom_xml: contents,
|
|
||||||
resolved_repo: None,
|
|
||||||
resolved_version: None,
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_file_to_bytes(local_artifact_jar_sha1_path: String) -> Result<Option<Vec<u8>>, Error> {
|
fn read_file_to_bytes(local_artifact_jar_sha1_path: String) -> Result<Vec<u8>, Error> {
|
||||||
let mut file = File::open(local_artifact_jar_sha1_path)?;
|
let mut file = File::open(local_artifact_jar_sha1_path)?;
|
||||||
let mut contents = Vec::new();
|
let mut contents = Vec::new();
|
||||||
file.read_to_end(&mut contents)?;
|
file.read_to_end(&mut contents)?;
|
||||||
Ok(Some(contents))
|
Ok(contents)
|
||||||
}
|
}
|
||||||
105
src/lib.rs
105
src/lib.rs
|
|
@ -1,5 +1,102 @@
|
||||||
pub mod compile;
|
use std::fs;
|
||||||
pub mod config;
|
use std::path::Path;
|
||||||
|
|
||||||
|
use anyhow::{anyhow, Error};
|
||||||
|
use toml::{Table, Value};
|
||||||
|
|
||||||
|
pub mod pom;
|
||||||
|
mod config;
|
||||||
pub mod deploader;
|
pub mod deploader;
|
||||||
pub mod maven;
|
pub mod compile;
|
||||||
pub mod project;
|
|
||||||
|
/// Top struct for jargo project data
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Project {
|
||||||
|
pub group: String,
|
||||||
|
pub name: String,
|
||||||
|
pub version: String,
|
||||||
|
pub dependencies: Vec<Artifact>,
|
||||||
|
pub test_dependencies: Vec<Artifact>,
|
||||||
|
pub project_root: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The identifier for any released bundle (jar, war etc) like in maven
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Artifact {
|
||||||
|
pub group: String,
|
||||||
|
pub name: String,
|
||||||
|
pub version: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert from XML view
|
||||||
|
impl From<pom::model::Dependency> for Artifact {
|
||||||
|
fn from(value: pom::model::Dependency) -> Self {
|
||||||
|
Artifact {
|
||||||
|
group: value.group_id.value,
|
||||||
|
name: value.artifact_id.value,
|
||||||
|
version: value.version.value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Artifact {
|
||||||
|
|
||||||
|
/// Convert from TOML view
|
||||||
|
pub fn from_table_entry(name_group: &str, version: String) -> Result<Self, Error>{
|
||||||
|
let name_group_split: Vec<&str> = name_group.split(":").collect();
|
||||||
|
if 2 != name_group_split.len(){
|
||||||
|
return Err(anyhow!("dependency {} not well formatted", name_group));
|
||||||
|
}
|
||||||
|
let group = name_group_split[0].into();
|
||||||
|
let name = name_group_split[1].into();
|
||||||
|
|
||||||
|
Ok(Self{
|
||||||
|
group,
|
||||||
|
name,
|
||||||
|
version: version[1..version.len()-1].to_owned(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// loads the project from the TOML file
|
||||||
|
pub fn load_project(jargo_file: Option<&str>) -> Result<Project, Error> {
|
||||||
|
let jargo = Path::new(jargo_file.unwrap_or("./Jargo.toml"));
|
||||||
|
|
||||||
|
let project_table = fs::read_to_string(jargo)?.parse::<Table>()?;
|
||||||
|
let package = project_table.get("package").expect("package info missing");
|
||||||
|
|
||||||
|
let dependencies = get_dependencies(project_table.get("dependencies"))?;
|
||||||
|
|
||||||
|
let test_dependencies = get_dependencies(project_table.get("test-dependencies"))?;
|
||||||
|
|
||||||
|
|
||||||
|
Ok(Project {
|
||||||
|
group: strip_first_last(package.get("group").unwrap().to_string()),
|
||||||
|
name: strip_first_last(package.get("name").unwrap().to_string()),
|
||||||
|
version: strip_first_last(package.get("version").unwrap().to_string()),
|
||||||
|
dependencies,
|
||||||
|
test_dependencies,
|
||||||
|
project_root: jargo.parent().map(Path::to_str).unwrap().expect(&format!("projectroot {:?} not usable", jargo)).into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// convert dependencies from the TOML view
|
||||||
|
fn get_dependencies(table: Option<&Value>) -> Result<Vec<Artifact>, Error> {
|
||||||
|
let mut dependencies = vec![];
|
||||||
|
if let Some(table) = table {
|
||||||
|
let table = table.as_table();
|
||||||
|
if let Some(table) = table {
|
||||||
|
for dep in table {
|
||||||
|
dependencies.push(
|
||||||
|
Artifact::from_table_entry(dep.0, dep.1.to_string())?
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(dependencies)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Because strings in the toml are surrounded by double quotes
|
||||||
|
fn strip_first_last(text: String) -> String{
|
||||||
|
text[1..text.len()-1].into()
|
||||||
|
}
|
||||||
11
src/main.rs
11
src/main.rs
|
|
@ -1,16 +1,9 @@
|
||||||
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);
|
let project = jargo::load_project(Some("tests/sample_project/Jargo.toml"))?;
|
||||||
std::fs::remove_dir_all(&repo)?;
|
jargo::deploader::load_artifacts(&project.test_dependencies)?;
|
||||||
std::fs::create_dir(repo)?;
|
|
||||||
|
|
||||||
let project = jargo::project::load_project(Some("tests/sample_project/Jargo.toml"))?;
|
|
||||||
println!("{:?}", project);
|
|
||||||
jargo::deploader::load(&project)?;
|
|
||||||
jargo::compile::run(&project)?;
|
jargo::compile::run(&project)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,107 +0,0 @@
|
||||||
use strong_xml::XmlRead;
|
|
||||||
|
|
||||||
use crate::maven::pom::{ArtifactId, GroupId, Version};
|
|
||||||
|
|
||||||
/// The Maven variant to parse poms
|
|
||||||
/// These structs is directly modelled after the XML because that is what strong-xml plugin requires
|
|
||||||
#[derive(XmlRead, PartialEq, Debug)]
|
|
||||||
#[xml(tag = "metadata")]
|
|
||||||
pub struct Metadata {
|
|
||||||
#[xml(child = "groupId")]
|
|
||||||
pub group_id: GroupId,
|
|
||||||
#[xml(child = "artifactId")]
|
|
||||||
pub artifact_id: ArtifactId,
|
|
||||||
#[xml(child = "version")]
|
|
||||||
pub version: Version,
|
|
||||||
#[xml(child = "versioning")]
|
|
||||||
pub versioning: Versioning,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(XmlRead, PartialEq, Debug)]
|
|
||||||
#[xml(tag = "versioning")]
|
|
||||||
pub struct Versioning {
|
|
||||||
#[xml(child = "snapshot")]
|
|
||||||
pub snapshot: Snapshot,
|
|
||||||
#[xml(child = "lastUpdated")]
|
|
||||||
pub last_updated: LastUpdated,
|
|
||||||
#[xml(child = "snapshotVersions")]
|
|
||||||
pub snapshot_versions: SnapshotVersions,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(XmlRead, PartialEq, Debug)]
|
|
||||||
#[xml(tag = "snapshot")]
|
|
||||||
pub struct Snapshot {
|
|
||||||
#[xml(child = "timestamp")]
|
|
||||||
pub timestamp: Timestamp,
|
|
||||||
#[xml(child = "buildNumber")]
|
|
||||||
pub build_number: BuildNumber,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(XmlRead, PartialEq, Debug)]
|
|
||||||
#[xml(tag = "snapshotVersions")]
|
|
||||||
pub struct SnapshotVersions {
|
|
||||||
#[xml(child = "snapshotVersion")]
|
|
||||||
pub snapshot_versions: Vec<SnapshotVersion>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(XmlRead, PartialEq, Debug)]
|
|
||||||
#[xml(tag = "snapshotVersion")]
|
|
||||||
pub struct SnapshotVersion {
|
|
||||||
#[xml(child = "classifier")]
|
|
||||||
pub classifier: Option<Classifier>,
|
|
||||||
#[xml(child = "extension")]
|
|
||||||
pub extension: Extension,
|
|
||||||
#[xml(child = "value")]
|
|
||||||
pub value: Value,
|
|
||||||
#[xml(child = "updated")]
|
|
||||||
pub updated: Updated,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(XmlRead, PartialEq, Debug)]
|
|
||||||
#[xml(tag = "timestamp")]
|
|
||||||
pub struct Timestamp {
|
|
||||||
#[xml(text)]
|
|
||||||
pub value: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(XmlRead, PartialEq, Debug)]
|
|
||||||
#[xml(tag = "buildNumber")]
|
|
||||||
pub struct BuildNumber {
|
|
||||||
#[xml(text)]
|
|
||||||
pub value: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(XmlRead, PartialEq, Debug)]
|
|
||||||
#[xml(tag = "lastUpdated")]
|
|
||||||
pub struct LastUpdated {
|
|
||||||
#[xml(text)]
|
|
||||||
pub value: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(XmlRead, PartialEq, Debug)]
|
|
||||||
#[xml(tag = "updated")]
|
|
||||||
pub struct Updated {
|
|
||||||
#[xml(text)]
|
|
||||||
pub value: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(XmlRead, PartialEq, Debug)]
|
|
||||||
#[xml(tag = "extension")]
|
|
||||||
pub struct Extension {
|
|
||||||
#[xml(text)]
|
|
||||||
pub value: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(XmlRead, PartialEq, Debug)]
|
|
||||||
#[xml(tag = "classifier")]
|
|
||||||
pub struct Classifier {
|
|
||||||
#[xml(text)]
|
|
||||||
pub value: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(XmlRead, PartialEq, Debug)]
|
|
||||||
#[xml(tag = "value")]
|
|
||||||
pub struct Value {
|
|
||||||
#[xml(text)]
|
|
||||||
pub value: String,
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
pub mod metadata;
|
|
||||||
pub mod pom;
|
|
||||||
pub mod pom_view;
|
|
||||||
|
|
@ -1,182 +0,0 @@
|
||||||
use anyhow::Error;
|
|
||||||
use regex::Regex;
|
|
||||||
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(crate) 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())
|
|
||||||
.map(|d|d.value)
|
|
||||||
.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())
|
|
||||||
.map(|v| resolve_props(parent, v))
|
|
||||||
} 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 resolve_props(pom: &PomView, version: String) -> String {
|
|
||||||
let p = Regex::new("\\$\\{(?<prop>.+?)}").expect("wrong regex"); //TODO instantiate once
|
|
||||||
let v2 = if let Some(capture) = p.captures(&version) {
|
|
||||||
match &capture["prop"] {
|
|
||||||
"project.version" => pom.version(),
|
|
||||||
_ => pom.
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
version
|
|
||||||
};
|
|
||||||
println!("{}", v2);
|
|
||||||
v2
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) 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,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1
src/pom/mod.rs
Normal file
1
src/pom/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
pub mod model;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use strong_xml::XmlRead;
|
use strong_xml::{XmlRead};
|
||||||
|
|
||||||
/// 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
|
||||||
|
|
@ -6,187 +6,165 @@ use strong_xml::XmlRead;
|
||||||
#[xml(tag = "project")]
|
#[xml(tag = "project")]
|
||||||
pub struct Pom {
|
pub struct Pom {
|
||||||
#[xml(child = "modelVersion")]
|
#[xml(child = "modelVersion")]
|
||||||
pub(crate) model_version: ModelVersion,
|
pub model_version: ModelVersion,
|
||||||
#[xml(child = "parent")]
|
|
||||||
pub(crate) parent: Option<Parent>,
|
|
||||||
#[xml(child = "groupId")]
|
#[xml(child = "groupId")]
|
||||||
pub(crate) group_id: Option<GroupId>,
|
pub group_id: GroupId,
|
||||||
#[xml(child = "artifactId")]
|
#[xml(child = "artifactId")]
|
||||||
pub(crate) artifact_id: ArtifactId,
|
pub artifact_id: ArtifactId,
|
||||||
#[xml(child = "version")]
|
#[xml(child = "version")]
|
||||||
pub(crate) version: Option<Version>,
|
pub version: Version,
|
||||||
#[xml(child = "name")]
|
#[xml(child = "name")]
|
||||||
pub(crate) name: Name,
|
pub name: Name,
|
||||||
#[xml(child = "packaging")]
|
#[xml(child = "packaging")]
|
||||||
pub(crate) packaging: Option<Packaging>,
|
pub packaging: Packaging,
|
||||||
#[xml(child = "url")]
|
#[xml(child = "url")]
|
||||||
pub(crate) url: Option<Url>,
|
pub url: Url,
|
||||||
#[xml(child = "description")]
|
#[xml(child = "description")]
|
||||||
pub(crate) description: Description,
|
pub description: Description,
|
||||||
#[xml(child = "licenses")]
|
#[xml(child = "licenses")]
|
||||||
pub(crate) licences: Option<Licenses>,
|
pub licences: Licenses,
|
||||||
#[xml(child = "scm")]
|
#[xml(child = "scm")]
|
||||||
pub(crate) scm: Option<Scm>,
|
pub scm: Scm,
|
||||||
#[xml(child = "developers")]
|
#[xml(child = "developers")]
|
||||||
pub(crate) developers: Option<Developers>,
|
pub developers: Developers,
|
||||||
#[xml(child = "dependencies")]
|
#[xml(child = "dependencies")]
|
||||||
pub(crate) dependencies: Option<Dependencies>,
|
pub dependencies: Dependencies,
|
||||||
#[xml(child = "dependencyManagement")]
|
|
||||||
pub(crate) dependency_management: Option<DependencyManagement>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlRead, PartialEq, Debug)]
|
#[derive(XmlRead, PartialEq, Debug)]
|
||||||
#[xml(tag = "modelVersion")]
|
#[xml(tag = "modelVersion")]
|
||||||
pub struct ModelVersion {
|
pub struct ModelVersion {
|
||||||
#[xml(text)]
|
#[xml(text)]
|
||||||
pub value: String,
|
value: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlRead, PartialEq, Debug, Clone)]
|
#[derive(XmlRead, PartialEq, Debug)]
|
||||||
#[xml(tag = "groupId")]
|
#[xml(tag = "groupId")]
|
||||||
pub struct GroupId {
|
pub struct GroupId {
|
||||||
#[xml(text)]
|
#[xml(text)]
|
||||||
pub(crate) value: String,
|
pub value: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlRead, PartialEq, Debug, Clone)]
|
#[derive(XmlRead, PartialEq, Debug)]
|
||||||
#[xml(tag = "artifactId")]
|
#[xml(tag = "artifactId")]
|
||||||
pub struct ArtifactId {
|
pub struct ArtifactId {
|
||||||
#[xml(text)]
|
#[xml(text)]
|
||||||
pub(crate) value: String,
|
pub value: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlRead, PartialEq, Debug, Clone)]
|
#[derive(XmlRead, PartialEq, Debug)]
|
||||||
#[xml(tag = "version")]
|
#[xml(tag = "version")]
|
||||||
pub struct Version {
|
pub struct Version {
|
||||||
#[xml(text)]
|
#[xml(text)]
|
||||||
pub(crate) value: String,
|
pub 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)]
|
||||||
pub(crate) value: String,
|
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)]
|
||||||
pub(crate) value: String,
|
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)]
|
||||||
pub(crate) value: String,
|
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)]
|
||||||
pub(crate) value: String,
|
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)]
|
||||||
pub(crate) value: String,
|
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")]
|
||||||
pub(crate) licenses: Vec<License>,
|
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)]
|
||||||
pub(crate) value: String,
|
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")]
|
||||||
pub(crate) name: Name,
|
name: Name,
|
||||||
#[xml(child = "url")]
|
#[xml(child = "url")]
|
||||||
pub(crate) url: Url,
|
url: Url,
|
||||||
#[xml(child = "distribution")]
|
#[xml(child = "distribution")]
|
||||||
pub(crate) distribution: Option<Distribution>,
|
distribution: Option<Distribution>,
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(XmlRead, PartialEq, Debug)]
|
|
||||||
#[xml(tag = "parent")]
|
|
||||||
pub struct Parent {
|
|
||||||
#[xml(child = "groupId")]
|
|
||||||
pub(crate) group_id: GroupId,
|
|
||||||
#[xml(child = "artifactId")]
|
|
||||||
pub(crate) artifact_id: ArtifactId,
|
|
||||||
#[xml(child = "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")]
|
||||||
pub(crate) url: Url,
|
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")]
|
||||||
pub(crate) developers: Vec<Developer>,
|
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")]
|
||||||
pub(crate) id: Option<Id>,
|
id: Option<Id>,
|
||||||
#[xml(child = "name")]
|
#[xml(child = "name")]
|
||||||
pub(crate) name: Name,
|
name: Name,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlRead, PartialEq, Debug, Clone)]
|
#[derive(XmlRead, PartialEq, Debug)]
|
||||||
#[xml(tag = "dependencies")]
|
#[xml(tag = "dependencies")]
|
||||||
pub struct Dependencies {
|
pub struct Dependencies {
|
||||||
#[xml(child = "dependency")]
|
#[xml(child = "developer")]
|
||||||
pub(crate) value: Vec<Dependency>,
|
pub value: Vec<Dependency>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlRead, PartialEq, Debug, Clone)]
|
#[derive(XmlRead, PartialEq, Debug)]
|
||||||
#[xml(tag = "dependencyManagement")]
|
|
||||||
pub struct DependencyManagement {
|
|
||||||
#[xml(child = "dependencies")]
|
|
||||||
pub(crate) value: Dependencies,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(XmlRead, PartialEq, Debug, Clone)]
|
|
||||||
#[xml(tag = "dependency")]
|
#[xml(tag = "dependency")]
|
||||||
pub struct Dependency {
|
pub struct Dependency {
|
||||||
#[xml(child = "groupId")]
|
#[xml(child = "groupId")]
|
||||||
pub(crate) group_id: GroupId,
|
pub group_id: GroupId,
|
||||||
#[xml(child = "artifactId")]
|
#[xml(child = "artifactId")]
|
||||||
pub(crate) artifact_id: ArtifactId,
|
pub artifact_id: ArtifactId,
|
||||||
#[xml(child = "version")]
|
#[xml(child = "version")]
|
||||||
pub(crate) version: Option<Version>,
|
pub version: Version,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use strong_xml::XmlRead;
|
use strong_xml::XmlRead;
|
||||||
|
|
||||||
use crate::maven::pom::Pom;
|
use crate::pom::model::Pom;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_should_not_fail() {
|
fn parse_should_not_fail() {
|
||||||
132
src/project.rs
132
src/project.rs
|
|
@ -1,132 +0,0 @@
|
||||||
use std::fs;
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use crate::maven::pom::Dependency;
|
|
||||||
use anyhow::{anyhow, Error};
|
|
||||||
use toml::{Table, Value};
|
|
||||||
|
|
||||||
/// Top struct for jargo project data
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Project {
|
|
||||||
pub group: String,
|
|
||||||
pub name: String,
|
|
||||||
pub version: String,
|
|
||||||
pub main_dependencies: Vec<Artifact>,
|
|
||||||
pub test_dependencies: Vec<Artifact>,
|
|
||||||
pub project_root: String,
|
|
||||||
pub repositories: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The identifier for any released bundle (jar, war etc) like in maven
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Artifact {
|
|
||||||
pub group: String,
|
|
||||||
pub name: String,
|
|
||||||
pub version: String,
|
|
||||||
pub path: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Artifact {
|
|
||||||
pub fn new(group: &str, name: &str, version: &str) -> Self {
|
|
||||||
Self {
|
|
||||||
group: group.into(),
|
|
||||||
name: name.into(),
|
|
||||||
version: version.into(),
|
|
||||||
path: format!("{}/{}/{}", group.replace(".", "/"), name, version),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_snapshot(&self) -> bool {
|
|
||||||
self.version.ends_with("-SNAPSHOT")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert from XML view
|
|
||||||
impl From<Dependency> for Artifact {
|
|
||||||
fn from(value: Dependency) -> Self {
|
|
||||||
Self::new(
|
|
||||||
&value.group_id.value,
|
|
||||||
&value.artifact_id.value,
|
|
||||||
&value.version.unwrap().value,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Artifact {
|
|
||||||
/// Convert from TOML view
|
|
||||||
pub fn from_table_entry(name_group: &str, version: String) -> Result<Self, Error> {
|
|
||||||
let name_group_split: Vec<&str> = name_group.split(":").collect();
|
|
||||||
if 2 != name_group_split.len() {
|
|
||||||
return Err(anyhow!("dependency {} not well formatted", name_group));
|
|
||||||
}
|
|
||||||
let group = name_group_split[0].into();
|
|
||||||
let name = name_group_split[1].into();
|
|
||||||
|
|
||||||
Ok(Self::new(
|
|
||||||
group,
|
|
||||||
name,
|
|
||||||
version[1..version.len() - 1].to_owned().as_str(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// loads the project from the TOML file
|
|
||||||
pub fn load_project(jargo_file: Option<&str>) -> Result<Project, Error> {
|
|
||||||
let jargo = Path::new(jargo_file.unwrap_or("./Jargo.toml"));
|
|
||||||
|
|
||||||
let project_table = fs::read_to_string(jargo)?.parse::<Table>()?;
|
|
||||||
let package = project_table.get("package").expect("package info missing");
|
|
||||||
|
|
||||||
let repositories = repositories(project_table.get("repositories"))?;
|
|
||||||
let main_dependencies = dependencies(project_table.get("dependencies"))?;
|
|
||||||
let test_dependencies = dependencies(project_table.get("test-dependencies"))?;
|
|
||||||
|
|
||||||
Ok(Project {
|
|
||||||
group: strip_first_last(package.get("group").unwrap().to_string()),
|
|
||||||
name: strip_first_last(package.get("name").unwrap().to_string()),
|
|
||||||
version: strip_first_last(package.get("version").unwrap().to_string()),
|
|
||||||
repositories,
|
|
||||||
main_dependencies,
|
|
||||||
test_dependencies,
|
|
||||||
project_root: jargo
|
|
||||||
.parent()
|
|
||||||
.map(Path::to_str)
|
|
||||||
.unwrap()
|
|
||||||
.expect(&format!("projectroot {:?} not usable", jargo))
|
|
||||||
.into(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn repositories(table: Option<&Value>) -> Result<Vec<String>, Error> {
|
|
||||||
let mut repositories = vec!["https://repo.maven.apache.org/maven2".to_owned()];
|
|
||||||
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 {
|
|
||||||
if let Some(Value::String(url)) = repo_details.get("url") {
|
|
||||||
repositories.push(url.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(repositories)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// convert dependencies from the TOML view
|
|
||||||
fn dependencies(table: Option<&Value>) -> Result<Vec<Artifact>, Error> {
|
|
||||||
let mut dependencies = vec![];
|
|
||||||
if let Some(table) = table {
|
|
||||||
let table = table.as_table();
|
|
||||||
if let Some(table) = table {
|
|
||||||
for dep in table {
|
|
||||||
dependencies.push(Artifact::from_table_entry(dep.0, dep.1.to_string())?);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(dependencies)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Because strings in the toml are surrounded by double quotes
|
|
||||||
fn strip_first_last(text: String) -> String {
|
|
||||||
text[1..text.len() - 1].into()
|
|
||||||
}
|
|
||||||
|
|
@ -3,12 +3,7 @@ group = "nl.sander"
|
||||||
name = "sample_project"
|
name = "sample_project"
|
||||||
version = "0.1-SNAPSHOT"
|
version = "0.1-SNAPSHOT"
|
||||||
|
|
||||||
[repositories]
|
|
||||||
spring-milestones = {url = "https://repo.spring.io/milestone"}
|
|
||||||
spring-snapshots = {url = "https://repo.spring.io/snapshot"}
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
"org.springframework.boot:spring-boot-starter-web" = "3.0.0-SNAPSHOT"
|
|
||||||
|
|
||||||
[test-dependencies]
|
[test-dependencies]
|
||||||
"junit:junit" = "4.8.2"
|
"junit:junit" = "4.8.2"
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue