Compare commits
10 commits
d3423bdf29
...
e2954297f9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2954297f9 | ||
|
|
9d22596199 | ||
|
|
77499aec4e | ||
|
|
d4f9b086b0 | ||
|
|
14a25044d6 | ||
|
|
8d45a66522 | ||
|
|
869dac9d38 | ||
|
|
2c714a211c | ||
|
|
3b81dd314b | ||
|
|
8371757c71 |
15 changed files with 832 additions and 298 deletions
108
Cargo.lock
generated
108
Cargo.lock
generated
|
|
@ -17,6 +17,15 @@ 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"
|
||||||
|
|
@ -436,10 +445,10 @@ dependencies = [
|
||||||
"colored",
|
"colored",
|
||||||
"hex",
|
"hex",
|
||||||
"home",
|
"home",
|
||||||
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"sha1",
|
"sha1",
|
||||||
"strong-xml",
|
"strong-xml",
|
||||||
"tokio",
|
|
||||||
"toml",
|
"toml",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -476,16 +485,6 @@ 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"
|
||||||
|
|
@ -611,29 +610,6 @@ 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"
|
||||||
|
|
@ -685,6 +661,35 @@ 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"
|
||||||
|
|
@ -757,12 +762,6 @@ 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"
|
||||||
|
|
@ -849,15 +848,6 @@ 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"
|
||||||
|
|
@ -867,12 +857,6 @@ 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"
|
||||||
|
|
@ -989,25 +973,11 @@ 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,5 +1,17 @@
|
||||||
**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;
|
use crate::project::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,7 +16,6 @@ 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 {
|
||||||
|
|
@ -26,21 +25,25 @@ 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!("{} {}.{}-{}", "Compiling".green(), project.group, project.name, project.version);
|
println!(
|
||||||
|
"{} {}.{}-{}",
|
||||||
|
"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)?;
|
||||||
println!("{:?}", src_tree);
|
compile_source_dir(project, &mut 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_sourcedir(project: &Project, src_tree: &mut PathNode) -> Result<(), Error> {
|
fn compile_source_dir(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") {
|
||||||
|
|
@ -78,7 +81,7 @@ fn compile_sourcedir(project: &Project, src_tree: &mut PathNode) -> Result<(), E
|
||||||
println!("{}", String::from_utf8(output.stdout)?);
|
println!("{}", String::from_utf8(output.stdout)?);
|
||||||
}
|
}
|
||||||
for subdir in subdirs {
|
for subdir in subdirs {
|
||||||
compile_sourcedir(project, subdir)?;
|
compile_source_dir(project, subdir)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -108,4 +111,4 @@ fn determine_src_tree(parent: PathBuf, parent_node: &mut PathNode) -> Result<(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +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 maven_central: String,
|
pub user_home: String,
|
||||||
pub user_home: PathBuf,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub static CONFIG: OnceLock<Config> = OnceLock::new();
|
pub static CONFIG: OnceLock<Config> = OnceLock::new();
|
||||||
|
|
@ -13,12 +12,12 @@ 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 '.jargo'
|
cache_location: format!("{}/jargo/repo", user_home).into(), //TODO make it '.jargo'
|
||||||
user_home,
|
user_home,
|
||||||
maven_central: "https://repo.maven.apache.org/maven2".into()
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
CONFIG.get().unwrap()
|
CONFIG.get().unwrap()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
316
src/deploader.rs
316
src/deploader.rs
|
|
@ -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;
|
||||||
|
|
@ -9,10 +9,13 @@ 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::pom::model::Pom;
|
use crate::maven::metadata::Metadata;
|
||||||
|
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
|
||||||
///
|
///
|
||||||
|
|
@ -25,100 +28,284 @@ use colored::Colorize;
|
||||||
/// 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_artifacts(artifacts: &[Artifact]) -> Result<(), Error> {
|
pub fn load(project: &Project) -> 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(art)?;
|
load_artifact(project, art)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_artifact(art: &Artifact) -> Result<(), Error> {
|
/// loads the artifact (all data and metadata for 1 artifact)
|
||||||
let artifact_path = format!("{}/{}/{}", art.group.replace(".", "/"), art.name, art.version);
|
/// 1. create dir in local cache if necessary
|
||||||
|
/// 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!("{}/{}-{}.jar", local_artifact_loc, art.name, art.version);
|
let local_artifact_jar_path = format!(
|
||||||
|
"{}/{}-{}.jar",
|
||||||
|
local_artifact_loc, artifact.name, artifact.version
|
||||||
|
);
|
||||||
if !exists(&local_artifact_jar_path) {
|
if !exists(&local_artifact_jar_path) {
|
||||||
download_verify_artifact_jar(art, &artifact_path, &local_artifact_jar_path)?;
|
lookup_verified_jar(
|
||||||
|
project,
|
||||||
|
artifact,
|
||||||
|
&local_artifact_jar_path,
|
||||||
|
&pom_lookup.resolved_repo.unwrap(),
|
||||||
|
&pom_lookup.resolved_version.unwrap(),
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// download remote pom if not in cache //TODO check its SHA-1 checksum
|
println!("{}", pom_lookup.pom_xml);
|
||||||
let pom_xml = download_verify_pom(art, artifact_path, local_artifact_loc)?;
|
|
||||||
|
|
||||||
// parse pom file
|
// parse pom file
|
||||||
let pom = Pom::from_str(&pom_xml).unwrap();
|
let pom = Pom::from_str(&pom_lookup.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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn download_verify_pom(art: &Artifact, artifact_path: String, local_artifact_loc: String) -> Result<String, Error> {
|
/// main function to download and verify the pom xml.
|
||||||
let local_artifact_pom_path = &format!("{}/{}-{}.pom", local_artifact_loc, art.name, art.version);
|
/// 1. check if file is locally cached and load if it is
|
||||||
let remote_artifact_pom_url = format!("{}/{}/{}-{}.pom", config().maven_central, artifact_path, art.name, art.version);
|
/// 2. or find a suitable repo (deferred to find_pom)
|
||||||
let pom_xml = if !exists(local_artifact_pom_path) {
|
/// 3. this function returns the downloaded pom, together with the repo it was found in and the "resolved version'
|
||||||
println!("{} {}", "Downloading".green(), remote_artifact_pom_url);
|
/// this is only applicable to SNAPSHOT's where x-SNAPSHOT is resolved to x-<timestamp>-<build_nr>
|
||||||
let body = reqwest::blocking::get(&remote_artifact_pom_url)?.text().unwrap();
|
/// 4. download the SHA1 file from the same location
|
||||||
println!("{} {}", "Downloaded".green(), remote_artifact_pom_url);
|
/// 5. validate if the checksum equals the checksum calculated from the pom
|
||||||
write_text(local_artifact_pom_path, &body)?;
|
///
|
||||||
body
|
/// The result from find_pom is passed on to the caller so that the information can be used
|
||||||
} else {
|
/// for subsequent requests.
|
||||||
|
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 = hex::decode(
|
let checksum = if !exists(&local_artifact_pom_sha1_path) {
|
||||||
if !exists(&local_artifact_pom_sha1_path) {
|
download_checksum(repo_with_pom, &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)?
|
||||||
})?;
|
};
|
||||||
let validated = validate_checksum_text(&pom_xml, checksum);
|
if let Some(checksum) = checksum {
|
||||||
if !validated {
|
let validated = validate_checksum_text(&pom_xml, hex::decode(checksum)?);
|
||||||
Err(anyhow!("SHA1 checksum for {} is not valid", remote_artifact_pom_url))
|
return if !validated {
|
||||||
} else { Ok(pom_xml) }
|
Err(anyhow!("SHA1 checksum for {} is not valid", artifact.path))
|
||||||
|
} 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 download_verify_artifact_jar(art: &Artifact, artifact_path: &str, local_artifact_jar_path: &str) -> Result<(), Error> {
|
fn lookup_verified_jar(
|
||||||
let remote_artifact_jar_url = format!("{}/{}/{}-{}.jar", config().maven_central, artifact_path, art.name, art.version);
|
project: &Project,
|
||||||
|
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 jar = reqwest::blocking::get(&remote_artifact_jar_url)?.bytes().unwrap();
|
let response = reqwest::blocking::get(&remote_artifact_jar_url)?;
|
||||||
println!("{} {}", "Downloaded".green(), remote_artifact_jar_url);
|
if response.status().is_success() {
|
||||||
write_bytes_to_file(&local_artifact_jar_path, &jar)?;
|
let jar = response.bytes().unwrap();
|
||||||
|
println!("{} {}", "Downloaded".green(), remote_artifact_jar_url);
|
||||||
|
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 = hex::decode(
|
let checksum = if !exists(&local_artifact_jar_sha1_path) {
|
||||||
if !exists(&local_artifact_jar_sha1_path) {
|
download_checksum(
|
||||||
download_checksum(&remote_artifact_jar_url, &local_artifact_jar_sha1_path)?
|
Some(&remote_artifact_jar_url),
|
||||||
|
&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)?
|
||||||
})?;
|
};
|
||||||
let validated = validate_checksum_bytes(&jar, checksum);
|
return if let Some(checksum) = checksum {
|
||||||
if !validated {
|
let validated = validate_checksum_bytes(&jar, hex::decode(checksum)?);
|
||||||
Err(anyhow!("SHA1 checksum for {} is not valid", remote_artifact_jar_url))
|
if !validated {
|
||||||
} else { Ok(()) }
|
Err(anyhow!(
|
||||||
|
"SHA1 checksum for {} is not valid",
|
||||||
|
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()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn download_checksum(remote_artifact_url: &String, local_artifact_jar_sha1_path: &String) -> Result<Vec<u8>, Error> {
|
/// Snapshots in a maven repo can be in the form
|
||||||
let remote_artifact_jar_sha1_url = format!("{}.sha1", remote_artifact_url);
|
/// 'spring-boot-starter-web-3.0.0-20221124.170206-1099.jar'
|
||||||
let jar_checksum = reqwest::blocking::get(&remote_artifact_jar_sha1_url)?.bytes().unwrap();
|
/// while we ask for
|
||||||
write_bytes_to_file(&local_artifact_jar_sha1_path, &jar_checksum)?;
|
/// 'spring-boot-starter-web-3.0.0-SNAPSHOT.jar'
|
||||||
Ok(jar_checksum.to_vec())
|
/// 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 response = reqwest::blocking::get(&remote_artifact_jar_sha1_url)?;
|
||||||
|
if response.status() == StatusCode::OK {
|
||||||
|
let jar_checksum = response.bytes().unwrap();
|
||||||
|
write_bytes_to_file(&local_artifact_jar_sha1_path, &jar_checksum)?;
|
||||||
|
Ok(Some(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 {
|
||||||
|
|
@ -139,7 +326,6 @@ 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)?;
|
||||||
|
|
@ -152,16 +338,20 @@ fn write_text(path: &str, contents: &String) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_file_to_string(local_artifact_pom_path: &str) -> Result<String, Error> {
|
fn read_file_to_string(local_artifact_pom_path: &str) -> Result<Option<PomLookupResult>, 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(contents)
|
Ok(Some(PomLookupResult {
|
||||||
|
pom_xml: contents,
|
||||||
|
resolved_repo: None,
|
||||||
|
resolved_version: None,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_file_to_bytes(local_artifact_jar_sha1_path: String) -> Result<Vec<u8>, Error> {
|
fn read_file_to_bytes(local_artifact_jar_sha1_path: String) -> Result<Option<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(contents)
|
Ok(Some(contents))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
105
src/lib.rs
105
src/lib.rs
|
|
@ -1,102 +1,5 @@
|
||||||
use std::fs;
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use anyhow::{anyhow, Error};
|
|
||||||
use toml::{Table, Value};
|
|
||||||
|
|
||||||
pub mod pom;
|
|
||||||
mod config;
|
|
||||||
pub mod deploader;
|
|
||||||
pub mod compile;
|
pub mod compile;
|
||||||
|
pub mod config;
|
||||||
/// Top struct for jargo project data
|
pub mod deploader;
|
||||||
#[derive(Debug)]
|
pub mod maven;
|
||||||
pub struct Project {
|
pub mod 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,9 +1,16 @@
|
||||||
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 project = jargo::load_project(Some("tests/sample_project/Jargo.toml"))?;
|
let repo = format!("{}/jargo/repo",config().user_home);
|
||||||
jargo::deploader::load_artifacts(&project.test_dependencies)?;
|
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)?;
|
||||||
jargo::compile::run(&project)?;
|
jargo::compile::run(&project)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
107
src/maven/metadata.rs
Normal file
107
src/maven/metadata.rs
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
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,
|
||||||
|
}
|
||||||
3
src/maven/mod.rs
Normal file
3
src/maven/mod.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
pub mod metadata;
|
||||||
|
pub mod pom;
|
||||||
|
pub mod pom_view;
|
||||||
|
|
@ -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,165 +6,187 @@ use strong_xml::{XmlRead};
|
||||||
#[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")]
|
||||||
|
pub(crate) parent: Option<Parent>,
|
||||||
#[xml(child = "groupId")]
|
#[xml(child = "groupId")]
|
||||||
pub group_id: 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: 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: Packaging,
|
pub(crate) packaging: Option<Packaging>,
|
||||||
#[xml(child = "url")]
|
#[xml(child = "url")]
|
||||||
pub url: 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: Licenses,
|
pub(crate) licences: Option<Licenses>,
|
||||||
#[xml(child = "scm")]
|
#[xml(child = "scm")]
|
||||||
pub scm: Scm,
|
pub(crate) scm: Option<Scm>,
|
||||||
#[xml(child = "developers")]
|
#[xml(child = "developers")]
|
||||||
pub developers: Developers,
|
pub(crate) developers: Option<Developers>,
|
||||||
#[xml(child = "dependencies")]
|
#[xml(child = "dependencies")]
|
||||||
pub dependencies: Dependencies,
|
pub(crate) dependencies: Option<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)]
|
||||||
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)]
|
||||||
|
#[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")]
|
||||||
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, Clone)]
|
||||||
#[xml(tag = "dependencies")]
|
#[xml(tag = "dependencies")]
|
||||||
pub struct Dependencies {
|
pub struct Dependencies {
|
||||||
#[xml(child = "developer")]
|
#[xml(child = "dependency")]
|
||||||
pub value: Vec<Dependency>,
|
pub(crate) value: Vec<Dependency>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlRead, PartialEq, Debug)]
|
#[derive(XmlRead, PartialEq, Debug, Clone)]
|
||||||
|
#[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 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: Version,
|
pub(crate) version: Option<Version>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use strong_xml::XmlRead;
|
use strong_xml::XmlRead;
|
||||||
|
|
||||||
use crate::pom::model::Pom;
|
use crate::maven::pom::Pom;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_should_not_fail() {
|
fn parse_should_not_fail() {
|
||||||
|
|
@ -210,4 +232,4 @@ mod test {
|
||||||
</project>
|
</project>
|
||||||
"#).unwrap();
|
"#).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
182
src/maven/pom_view.rs
Normal file
182
src/maven/pom_view.rs
Normal file
|
|
@ -0,0 +1,182 @@
|
||||||
|
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 +0,0 @@
|
||||||
pub mod model;
|
|
||||||
132
src/project.rs
Normal file
132
src/project.rs
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
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,8 +3,13 @@ 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"
|
||||||
"org.mockito:mockito-core" = "1.9.5"
|
"org.mockito:mockito-core" = "1.9.5"
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue