Implement dependency action

This commit is contained in:
Till Wegmueller 2020-08-06 00:06:18 +02:00
parent c2867684d1
commit d2a414fb4a
3 changed files with 150 additions and 7 deletions

View file

@ -18,3 +18,4 @@ keywords = ["packaging", "illumos"]
[dependencies] [dependencies]
regex = "1.3.7" regex = "1.3.7"
failure = "0.1.8" failure = "0.1.8"
maplit = "0.1.6"

View file

@ -3,6 +3,8 @@
// MPL was not distributed with this file, You can // MPL was not distributed with this file, You can
// obtain one at https://mozilla.org/MPL/2.0/. // obtain one at https://mozilla.org/MPL/2.0/.
// Source https://docs.oracle.com/cd/E23824_01/html/E21796/pkg-5.html
use regex::{RegexSet, Regex}; use regex::{RegexSet, Regex};
use std::collections::HashSet; use std::collections::HashSet;
use std::fs::File as OsFile; use std::fs::File as OsFile;
@ -98,6 +100,27 @@ impl FacetedAction for File {
} }
} }
//TODO implement multiple FMRI for require-any
#[derive(Debug, Default)]
pub struct Dependency {
pub fmri: String, //TODO make FMRI
pub dependency_type: String, //TODO make enum
pub predicate: String, //TODO make FMRI
pub root_image: String, //TODO make boolean
pub optional: Vec<Property>,
pub facets: HashSet<Facet>,
}
impl FacetedAction for Dependency {
fn add_facet(&mut self, facet: Facet) -> bool {
return self.facets.insert(facet)
}
fn remove_facet(&mut self, facet: Facet) -> bool {
return self.facets.remove(&facet)
}
}
#[derive(Hash, Eq, PartialEq, Debug, Default)] #[derive(Hash, Eq, PartialEq, Debug, Default)]
pub struct Facet { pub struct Facet {
pub name: String, pub name: String,
@ -122,6 +145,7 @@ pub struct Manifest {
pub attributes: Vec<Attr>, pub attributes: Vec<Attr>,
pub directories: Vec<Dir>, pub directories: Vec<Dir>,
pub files: Vec<File>, pub files: Vec<File>,
pub dependencies: Vec<Dependency>,
} }
impl Manifest { impl Manifest {
@ -130,6 +154,7 @@ impl Manifest {
attributes: Vec::new(), attributes: Vec::new(),
directories: Vec::new(), directories: Vec::new(),
files: Vec::new(), files: Vec::new(),
dependencies: Vec::new(),
}; };
} }
} }
@ -202,7 +227,7 @@ fn handle_manifest_line(manifest: &mut Manifest, line: &str, line_nr: usize) ->
manifest.files.push(parse_file_action(String::from(line), line_nr)?); manifest.files.push(parse_file_action(String::from(line), line_nr)?);
} }
ActionKind::Dependency => { ActionKind::Dependency => {
manifest.dependencies.push(parse_depend_action(String::from(line),line_nr)?);
} }
ActionKind::User => { ActionKind::User => {
@ -230,21 +255,23 @@ fn handle_manifest_line(manifest: &mut Manifest, line: &str, line_nr: usize) ->
} }
fn add_facet_to_action<T: FacetedAction>(action: &mut T, facet_string: String, line: String, line_nr: usize) -> Result<(), ManifestError> { fn add_facet_to_action<T: FacetedAction>(action: &mut T, facet_string: String, line: String, line_nr: usize) -> Result<(), ManifestError> {
let facet_key = match facet_string.find(".") { let mut facet_key = match facet_string.find(".") {
Some(idx) => { Some(idx) => {
facet_string.clone().split_off(idx+1) facet_string.clone().split_off(idx+1)
}, },
None => return Err(ManifestError::InvalidAction{action: line, line: line_nr, message: String::from("separation dot not found but string contains facet.")})? None => return Err(ManifestError::InvalidAction{action: line, line: line_nr, message: String::from("separation dot not found but string contains facet.")})?
}; };
let value = match facet_string.find("=") { let value = match facet_key.find("=") {
Some(idx) => { Some(idx) => {
facet_string.clone().split_off(idx+1) facet_key.split_off(idx+1)
}, },
None => return Err(ManifestError::InvalidAction{action: line, line: line_nr, message: String::from("no value present for facet")})? None => return Err(ManifestError::InvalidAction{action: line, line: line_nr, message: String::from("no value present for facet")})?
}; };
if !action.add_facet(Facet{name: facet_key, value}) { facet_key.truncate(facet_key.len() - 1);
if !action.add_facet(Facet{name: clean_string_value(facet_key.as_str()), value: clean_string_value(value.as_str())}) {
return Err(ManifestError::InvalidAction{action: line, line: line_nr, message: String::from("double declaration of facet")})? return Err(ManifestError::InvalidAction{action: line, line: line_nr, message: String::from("double declaration of facet")})?
} }
@ -290,6 +317,43 @@ fn string_to_bool(orig: &str) -> Result<bool, String> {
} }
} }
fn parse_depend_action(line: String, line_nr: usize) -> Result<Dependency, Error> {
let mut act = Dependency::default();
let regex_set = RegexSet::new(&[
r#"([^ ]+)=([^"][^ ]+[^"])"#,
r#"([^ ]+)="(.+)"#
])?;
for (pat, _) in regex_set.matches(line.trim_start()).into_iter().map(|match_idx| (&regex_set.patterns()[match_idx], match_idx)) {
let regex = Regex::new(&pat)?;
for cap in regex.captures_iter(line.clone().trim_start()) {
let full_cap_idx = 0;
let key_cap_idx = 1;
let val_cap_idx = 2;
match &cap[key_cap_idx] {
"fmri" => act.fmri = clean_string_value(&cap[val_cap_idx]),
"type" => act.dependency_type = clean_string_value(&cap[val_cap_idx]),
"predicate" => act.predicate = clean_string_value(&cap[val_cap_idx]),
"root-image" => act.root_image = clean_string_value(&cap[val_cap_idx]),
_ => {
let key_val_string = String::from(&cap[full_cap_idx]);
if key_val_string.contains("facet.") {
match add_facet_to_action(&mut act, key_val_string, line.clone(), line_nr) {
Ok(_) => continue,
Err(e) => return Err(e)?,
}
} else {
act.optional.push(Property{key: clean_string_value(&cap[key_cap_idx]), value: clean_string_value(&cap[val_cap_idx])});
}
}
}
}
}
Ok(act)
}
fn parse_file_action(line: String, line_nr: usize) -> Result<File, Error> { fn parse_file_action(line: String, line_nr: usize) -> Result<File, Error> {
let mut act = File::default(); let mut act = File::default();
let regex_set = RegexSet::new(&[ let regex_set = RegexSet::new(&[

View file

@ -8,16 +8,18 @@ mod digest;
mod payload; mod payload;
#[macro_use] extern crate failure; #[macro_use] extern crate failure;
#[macro_use] extern crate maplit;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::actions::{Manifest, Property, Dir, File}; use crate::actions::{Manifest, Property, Dir, File, Dependency, Facet};
use crate::actions::{parse_manifest_string, Attr}; use crate::actions::{parse_manifest_string, Attr};
use std::collections::HashSet; use std::collections::HashSet;
use crate::payload::Payload; use crate::payload::Payload;
use crate::digest::{Digest, DigestAlgorithm, DigestSource}; use crate::digest::{Digest, DigestAlgorithm, DigestSource};
use std::str::FromStr; use std::str::FromStr;
use failure::_core::ptr::hash;
#[test] #[test]
fn parse_attributes() { fn parse_attributes() {
@ -828,4 +830,80 @@ file 6d5f820bb1d67594c7b757c79ef6f9242df49e98 chash=3ab17dde089f1eac7abd37d8efd7
} }
} }
} }
#[test]
fn parse_dependency_actions() {
let manifest_string = String::from("depend fmri=pkg:/system/library@0.5.11-2020.0.1.19563 type=require
depend fmri=pkg:/system/file-system/nfs@0.5.11,5.11-2020.0.1.19951 type=incorporate
depend facet.version-lock.system/data/hardware-registry=true fmri=pkg:/system/data/hardware-registry@2020.2.22,5.11-2020.0.1.19951 type=incorporate
depend facet.version-lock.xvm=true fmri=xvm@0.5.11-2015.0.2.0 type=incorporate
depend facet.version-lock.system/mozilla-nss=true fmri=system/mozilla-nss@3.51.1-2020.0.1.0 type=incorporate");
let test_results = vec![
Dependency{
fmri: "pkg:/system/library@0.5.11-2020.0.1.19563".to_string(),
dependency_type: "require".to_string(),
..Dependency::default()
},
Dependency{
fmri: "pkg:/system/file-system/nfs@0.5.11,5.11-2020.0.1.19951".to_string(),
dependency_type: "incorporate".to_string(),
..Dependency::default()
},
Dependency{
fmri: "pkg:/system/data/hardware-registry@2020.2.22,5.11-2020.0.1.19951".to_string(),
dependency_type: "incorporate".to_string(),
facets: hashset!{
Facet{
name: "version-lock.system/data/hardware-registry".to_string(),
value: "true".to_string(),
}
},
..Dependency::default()
},
Dependency{
fmri: "xvm@0.5.11-2015.0.2.0".to_string(),
dependency_type: "incorporate".to_string(),
facets: hashset!{
Facet{
name: "version-lock.xvm".to_string(),
value: "true".to_string(),
}
},
..Dependency::default()
},
Dependency{
fmri: "system/mozilla-nss@3.51.1-2020.0.1.0".to_string(),
dependency_type: "incorporate".to_string(),
facets: hashset!{
Facet{
name: "version-lock.system/mozilla-nss".to_string(),
value: "true".to_string(),
}
},
..Dependency::default()
},
];
let mut manifest = Manifest::new();
let res = parse_manifest_string(manifest_string);
assert!(res.is_ok(), "error during Manifest parsing: {:?}", res);
let manifest = res.unwrap();
assert_eq!(manifest.dependencies.len(), test_results.len());
for (pos, dependency) in manifest.dependencies.iter().enumerate() {
assert_eq!(dependency.fmri, test_results[pos].fmri);
assert_eq!(dependency.dependency_type, test_results[pos].dependency_type);
for (vpos, facet) in dependency.facets.iter().enumerate() {
let fres = test_results[pos].facets.get(facet);
assert!(fres.is_some(), "error no facet with name: {:?} found", facet.name);
let f = fres.unwrap();
assert_eq!(facet.name, f.name);
assert_eq!(facet.value, f.value);
}
}
}
} }