From 441a47d384f837d7cbeb7077314ae634e8ea5b7a Mon Sep 17 00:00:00 2001 From: Till Wegmueller Date: Mon, 18 May 2020 14:57:55 +0200 Subject: [PATCH] Add default derive Add Fast path parsing which catches the case of unescaped values with spaces. --- src/actions/mod.rs | 48 +++++++++++++++++++++++++++++++++++++++++++++- src/lib.rs | 34 ++++++++++++++++++++++++++++---- 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/src/actions/mod.rs b/src/actions/mod.rs index 16cd064..5f6d608 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; +use regex::{Regex, Captures}; use std::collections::HashSet; use std::error; use std::fmt; @@ -11,6 +11,7 @@ use std::fs::File; use std::io::BufRead; use std::io::BufReader; +#[derive(Debug, Default)] pub struct Dir { pub path: String, pub group: String, @@ -18,6 +19,7 @@ pub struct Dir { pub mode: String, //TODO implement as bitmask } +#[derive(Debug, Default)] pub struct Attr { pub key: String, pub values: Vec, @@ -30,6 +32,7 @@ pub struct Property { pub value: String, } +#[derive(Debug, Default)] pub struct Manifest { pub attributes: Vec, } @@ -130,6 +133,49 @@ fn is_attr_action(line: &String) -> bool { } pub 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 + // unescaped spaces are never valid but sadly present in the wild. + // Fast path will fail if a value has multiple values or a '=' sign in the values + let full_line_regex = match Regex::new(r"^set name=([^ ]+) value=(.+)$") { + Ok(re) => re, + Err(e) => return Err(ManifestError::Regex(e)), + }; + + if full_line_regex.is_match(line.trim_start()) { + match full_line_regex.captures(line.trim_start()) { + Some(captures) => { + let mut fast_path_fail = false; + let mut val = String::from(&captures[2]); + + if val.contains("=") { + fast_path_fail = true; + } + + if val.contains("value=") { + fast_path_fail = true; + } + + if val.contains("name=") { + fast_path_fail = true; + } + + val = val.replace(&['"', '\\'][..], ""); + + if !fast_path_fail{ + return Ok(Attr{ + key: String::from(&captures[1]), + values: vec![val], + ..Attr::default() + }); + } + } + None => (), + }; + } + + //Todo move regex initialisation out of for loop into static area let name_regex = match Regex::new(r"name=([^ ]+) value=") { Ok(re) => re, diff --git a/src/lib.rs b/src/lib.rs index 345c6aa..aa15ec9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,7 @@ mod actions; #[cfg(test)] mod tests { - use crate::actions::Manifest; + use crate::actions::{Manifest, Property}; use crate::actions::{parse_manifest_string, Attr}; use std::collections::HashSet; @@ -25,7 +25,15 @@ mod tests { set name=info.source-url value=http://nginx.org/download/nginx-1.18.0.tar.gz set name=org.opensolaris.consolidation value=userland set name=com.oracle.info.version value=1.18.0 - set name=variant.arch value=i386"); + set name=pkg.summary value=\\\"provided mouse accessibility enhancements\\\" + set name=info.upstream value=X.Org Foundation + set name=pkg.description value=Latvian language support's extra files + set name=variant.arch value=i386 optional=testing optionalWithString=\"test ing\""); + + let mut optional_hash = HashSet::new(); + optional_hash.insert(Property{key: String::from("optional"), value:String::from("testing")}); + optional_hash.insert(Property{key: String::from("optionalWithString"), value:String::from("test ing")}); + let test_results = vec![ Attr{ key: String::from("pkg.fmri"), @@ -82,10 +90,25 @@ mod tests { values: vec![String::from("1.18.0")], properties: HashSet::new(), }, + Attr{ + key: String::from("pkg.summary"), + values: vec![String::from("provided mouse accessibility enhancements")], + properties: HashSet::new(), + }, + Attr{ + key: String::from("info.upstream"), + values: vec![String::from("X.Org Foundation")], + properties: HashSet::new(), + }, + Attr{ + key: String::from("pkg.description"), + values: vec![String::from("Latvian language support's extra files")], + properties: HashSet::new(), + }, Attr{ key: String::from("variant.arch"), values: vec![String::from("i386")], - properties: HashSet::new(), + properties: optional_hash, } ]; @@ -94,9 +117,12 @@ mod tests { Ok(m) => manifest = m, Err(_) => assert!(false, "caught error"), }; - assert_eq!(manifest.attributes.len(), 12); + + assert_eq!(manifest.attributes.len(), 15); + for (pos, attr) in manifest.attributes.iter().enumerate() { assert_eq!(attr.key, test_results[pos].key); + for (vpos, val) in attr.values.iter().enumerate() { assert_eq!(val, &test_results[pos].values[vpos]); }