diff --git a/src/actions/mod.rs b/src/actions/mod.rs index e0678c3..dd8221f 100644 --- a/src/actions/mod.rs +++ b/src/actions/mod.rs @@ -3,7 +3,7 @@ // MPL was not distributed with this file, You can // obtain one at https://mozilla.org/MPL/2.0/. -use regex::{Regex, Captures}; +use regex::{RegexSet, Regex}; use std::collections::HashSet; use std::error; use std::fmt; @@ -18,6 +18,15 @@ pub struct Dir { pub group: String, pub owner: String, pub mode: String, //TODO implement as bitmask + pub revert_tag: String, + pub salvage_from: String, + pub facets: HashSet, +} + +#[derive(Hash, Eq, PartialEq, Debug, Default)] +pub struct Facet { + pub name: String, + pub value: String, } #[derive(Debug, Default)] @@ -27,7 +36,7 @@ pub struct Attr { pub properties: HashSet, } -#[derive(Hash, Eq, PartialEq, Debug)] +#[derive(Hash, Eq, PartialEq, Debug, Default)] pub struct Property { pub key: String, pub value: String, @@ -36,12 +45,14 @@ pub struct Property { #[derive(Debug, Default)] pub struct Manifest { pub attributes: Vec, + pub directories: Vec, } impl Manifest { pub fn new() -> Manifest { return Manifest { attributes: Vec::new(), + directories: Vec::new(), }; } } @@ -68,6 +79,12 @@ pub enum ManifestError { line: usize, action: String, }, + #[fail(display = "action string \"{}\" at line {} is invalid: {}", action, line, message)] + InvalidAction { + line: usize, + action: String, + message: String, + }, } pub fn parse_manifest_file(filename: String) -> Result { @@ -97,7 +114,7 @@ fn handle_manifest_line(manifest: &mut Manifest, line: &str, line_nr: usize) -> manifest.attributes.push(parse_attr_action(String::from(line))?); } ActionKind::Dir => { - + manifest.directories.push(parse_dir_action(String::from(line), line_nr)?); } ActionKind::File => { @@ -155,7 +172,47 @@ fn determine_action_kind(line: &str) -> ActionKind { } } -pub fn parse_attr_action(line: String) -> Result { +fn parse_dir_action(line: String, line_nr: usize) -> Result { + let mut act = Dir::default(); + let regex = Regex::new(r#"(([^ ]+)=([^"][^ ]+[^"])|([^ ]+)=([^"][^ ]+[^"]))"#)?; + + for cap in regex.captures_iter(line.trim_start()) { + match &cap[1] { + "path" => act.path = String::from(&cap[2]).replace(&['"', '\\'][..], ""), + "owner" => act.owner = String::from(&cap[2]).replace(&['"', '\\'][..], ""), + "group" => act.group = String::from(&cap[2]).replace(&['"', '\\'][..], ""), + "mode" => act.mode = String::from(&cap[2]).replace(&['"', '\\'][..], ""), + "revert-tag" => act.revert_tag = String::from(&cap[2]).replace(&['"', '\\'][..], ""), + "salvage-from" => act.salvage_from = String::from(&cap[2]).replace(&['"', '\\'][..], ""), + _ => { + let key_val_string = String::from(&cap[1]).replace(&['"', '\\'][..], ""); + if key_val_string.contains("facet.") { + let key = match key_val_string.find(".") { + Some(idx) => { + key_val_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.")})? + }; + + let value = match key_val_string.find("=") { + Some(idx) => { + key_val_string.clone().split_off(idx+1) + }, + None => return Err(ManifestError::InvalidAction{action: line, line: line_nr, message: String::from("no value present for facet")})? + }; + + if !act.facets.insert(Facet{name: key, value: value}) { + return Err(ManifestError::InvalidAction{action: line, line: line_nr, message: String::from("double declaration of facet")})? + } + } + } + } + } + + Ok(act) +} + +fn parse_attr_action(line: String) -> Result { // Do a full line match to see if we can fast path this. // This also catches values with spaces, that have not been properly escaped. // Note: values with spaces must be properly escaped or the rest here will fail. Strings with diff --git a/src/lib.rs b/src/lib.rs index ebbae27..ea47f1f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,12 +10,12 @@ mod actions; #[cfg(test)] mod tests { - use crate::actions::{Manifest, Property}; + use crate::actions::{Manifest, Property, Dir}; use crate::actions::{parse_manifest_string, Attr}; use std::collections::HashSet; #[test] - fn parse_manifest() { + fn parse_attributes() { let manifest_string = String::from("set name=pkg.fmri value=pkg://openindiana.org/web/server/nginx@1.18.0,5.11-2020.0.1.0:20200421T195136Z set name=com.oracle.info.name value=nginx value=test set name=userland.info.git-remote value=git://github.com/OpenIndiana/oi-userland.git @@ -145,4 +145,69 @@ mod tests { } } } + + #[test] + fn parse_direcory_actions() { + let manifest_string = String::from("dir group=bin mode=0755 owner=root path=etc/nginx + dir group=bin mode=0755 owner=root path=usr/share/nginx + dir group=bin mode=0755 owner=root path=usr/share/nginx/html + dir group=bin mode=0755 owner=root path=\"var/nginx\" + dir group=bin mode=0755 owner=webservd path=var/nginx/logs"); + + let test_results = vec![ + Dir{ + group: String::from("bin"), + mode: String::from("0755"), + owner: String::from("root"), + path: String::from("etc/nginx"), + ..Dir::default() + },Dir{ + group: String::from("bin"), + mode: String::from("0755"), + owner: String::from("root"), + path: String::from("usr/share/nginx"), + ..Dir::default() + },Dir{ + group: String::from("bin"), + mode: String::from("0755"), + owner: String::from("root"), + path: String::from("usr/share/nginx/html"), + ..Dir::default() + },Dir{ + group: String::from("bin"), + mode: String::from("0755"), + owner: String::from("root"), + path: String::from("\"var/nginx\""), + ..Dir::default() + },Dir{ + group: String::from("bin"), + mode: String::from("0755"), + owner: String::from("root"), + path: String::from("var/nginx/logs"), + ..Dir::default() + }, + ]; + + let mut manifest = Manifest::new(); + match parse_manifest_string(manifest_string) { + Ok(m) => manifest = m, + Err(e) => { + println!("{}", e); + assert!(false, "caught error"); + } + }; + + assert_eq!(manifest.directories.len(), test_results.len()); + + for (pos, attr) in manifest.directories.iter().enumerate() { + assert_eq!(attr.group, test_results[pos].group); + assert_eq!(attr.mode, test_results[pos].mode); + assert_eq!(attr.owner, test_results[pos].owner); + assert_eq!(attr.path, test_results[pos].path); + + //for (vpos, val) in attr.facets.iter().enumerate() { + // assert_eq!(val, &test_results[pos].facets.); + //} + } + } }