2020-05-17 21:52:39 +02:00
|
|
|
// This Source Code Form is subject to the terms of
|
|
|
|
|
// the Mozilla Public License, v. 2.0. If a copy of the
|
|
|
|
|
// MPL was not distributed with this file, You can
|
|
|
|
|
// obtain one at https://mozilla.org/MPL/2.0/.
|
|
|
|
|
|
2020-05-18 10:32:16 +02:00
|
|
|
use regex::Regex;
|
|
|
|
|
use std::collections::HashSet;
|
|
|
|
|
use std::error;
|
|
|
|
|
use std::fmt;
|
2020-05-17 01:17:19 +02:00
|
|
|
use std::fs::File;
|
|
|
|
|
use std::io::BufRead;
|
|
|
|
|
use std::io::BufReader;
|
2020-05-17 21:52:39 +02:00
|
|
|
|
|
|
|
|
pub struct Dir {
|
|
|
|
|
pub path: String,
|
|
|
|
|
pub group: String,
|
|
|
|
|
pub owner: String,
|
|
|
|
|
pub mode: String, //TODO implement as bitmask
|
|
|
|
|
}
|
2020-05-17 01:17:19 +02:00
|
|
|
|
|
|
|
|
pub struct Attr {
|
2020-05-17 21:52:39 +02:00
|
|
|
pub key: String,
|
|
|
|
|
pub values: Vec<String>,
|
|
|
|
|
pub properties: HashSet<Property>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Hash, Eq, PartialEq, Debug)]
|
|
|
|
|
pub struct Property {
|
|
|
|
|
pub key: String,
|
|
|
|
|
pub value: String,
|
2020-05-17 01:17:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct Manifest {
|
2020-05-17 21:52:39 +02:00
|
|
|
pub attributes: Vec<Attr>,
|
2020-05-17 01:17:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Manifest {
|
|
|
|
|
pub fn new() -> Manifest {
|
2020-05-18 10:32:16 +02:00
|
|
|
return Manifest {
|
2020-05-17 21:52:39 +02:00
|
|
|
attributes: Vec::new(),
|
2020-05-17 01:17:19 +02:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-17 21:52:39 +02:00
|
|
|
enum ActionKind {
|
|
|
|
|
Attr,
|
|
|
|
|
Dir,
|
|
|
|
|
File,
|
|
|
|
|
Dependency,
|
|
|
|
|
User,
|
|
|
|
|
Group,
|
|
|
|
|
Driver,
|
|
|
|
|
License,
|
|
|
|
|
Link,
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-17 01:17:19 +02:00
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub enum ManifestError {
|
|
|
|
|
EmptyVec,
|
|
|
|
|
// We will defer to the parse error implementation for their error.
|
|
|
|
|
// Supplying extra info requires adding more data to the type.
|
|
|
|
|
Read(std::io::Error),
|
|
|
|
|
Regex(regex::Error),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl fmt::Display for ManifestError {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
match *self {
|
2020-05-18 10:32:16 +02:00
|
|
|
ManifestError::EmptyVec => write!(f, "please use a vector with at least one element"),
|
2020-05-17 01:17:19 +02:00
|
|
|
// This is a wrapper, so defer to the underlying types' implementation of `fmt`.
|
|
|
|
|
ManifestError::Read(ref e) => e.fmt(f),
|
|
|
|
|
ManifestError::Regex(ref e) => e.fmt(f),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl error::Error for ManifestError {
|
|
|
|
|
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
|
|
|
|
match *self {
|
|
|
|
|
ManifestError::EmptyVec => None,
|
|
|
|
|
// The cause is the underlying implementation error type. Is implicitly
|
|
|
|
|
// cast to the trait object `&error::Error`. This works because the
|
|
|
|
|
// underlying type already implements the `Error` trait.
|
|
|
|
|
ManifestError::Read(ref e) => Some(e),
|
|
|
|
|
ManifestError::Regex(ref e) => Some(e),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-17 21:52:39 +02:00
|
|
|
pub fn parse_manifest_file(filename: String) -> Result<Manifest, ManifestError> {
|
2020-05-17 01:17:19 +02:00
|
|
|
let mut manifest = Manifest::new();
|
|
|
|
|
let f = match File::open(filename) {
|
|
|
|
|
Ok(file) => file,
|
|
|
|
|
Err(e) => return Err(ManifestError::Read(e)),
|
|
|
|
|
};
|
|
|
|
|
let file = BufReader::new(&f);
|
2020-05-18 10:32:16 +02:00
|
|
|
for line_read in file.lines() {
|
|
|
|
|
let line = match line_read {
|
2020-05-17 01:17:19 +02:00
|
|
|
Ok(l) => l,
|
|
|
|
|
Err(e) => return Err(ManifestError::Read(e)),
|
|
|
|
|
};
|
2020-05-17 21:52:39 +02:00
|
|
|
if is_attr_action(&line) {
|
|
|
|
|
match parse_attr_action(line) {
|
|
|
|
|
Ok(attr) => manifest.attributes.push(attr),
|
2020-05-18 10:32:16 +02:00
|
|
|
Err(e) => return Err(e),
|
2020-05-17 01:17:19 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-05-18 10:32:16 +02:00
|
|
|
}
|
2020-05-17 01:17:19 +02:00
|
|
|
return Ok(manifest);
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-17 21:52:39 +02:00
|
|
|
pub fn parse_manifest_string(manifest: String) -> Result<Manifest, ManifestError> {
|
2020-05-17 01:17:19 +02:00
|
|
|
let mut m = Manifest::new();
|
|
|
|
|
for line in manifest.lines() {
|
2020-05-17 21:52:39 +02:00
|
|
|
if is_attr_action(&String::from(line)) {
|
2020-05-18 10:32:16 +02:00
|
|
|
match parse_attr_action(String::from(line)) {
|
2020-05-17 21:52:39 +02:00
|
|
|
Ok(attr) => m.attributes.push(attr),
|
2020-05-18 10:32:16 +02:00
|
|
|
Err(e) => return Err(e),
|
2020-05-17 01:17:19 +02:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-18 10:32:16 +02:00
|
|
|
return Ok(m);
|
2020-05-17 01:17:19 +02:00
|
|
|
}
|
|
|
|
|
|
2020-05-17 21:52:39 +02:00
|
|
|
fn is_attr_action(line: &String) -> bool {
|
2020-05-17 01:17:19 +02:00
|
|
|
if line.trim().starts_with("set ") {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-17 21:52:39 +02:00
|
|
|
pub fn parse_attr_action(line: String) -> Result<Attr, ManifestError> {
|
|
|
|
|
//Todo move regex initialisation out of for loop into static area
|
2020-05-17 01:17:19 +02:00
|
|
|
let name_regex = match Regex::new(r"name=([^ ]+) value=") {
|
|
|
|
|
Ok(re) => re,
|
2020-05-18 10:32:16 +02:00
|
|
|
Err(e) => return Err(ManifestError::Regex(e)),
|
2020-05-17 01:17:19 +02:00
|
|
|
};
|
2020-05-17 21:52:39 +02:00
|
|
|
let mut key = String::new();
|
2020-05-17 01:17:19 +02:00
|
|
|
for cap in name_regex.captures_iter(line.trim_start()) {
|
2020-05-17 21:52:39 +02:00
|
|
|
key = String::from(&cap[1]);
|
2020-05-17 01:17:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut values = Vec::new();
|
|
|
|
|
let value_no_space_regex = match Regex::new(r#"value="(.+)""#) {
|
|
|
|
|
Ok(re) => re,
|
|
|
|
|
Err(e) => return Err(ManifestError::Regex(e)),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let value_space_regex = match Regex::new(r#"value=([^"][^ ]+[^"])"#) {
|
|
|
|
|
Ok(re) => re,
|
|
|
|
|
Err(e) => return Err(ManifestError::Regex(e)),
|
|
|
|
|
};
|
|
|
|
|
|
2020-05-17 21:52:39 +02:00
|
|
|
let mut properties = HashSet::new();
|
|
|
|
|
let optionals_regex_no_quotes = match Regex::new(r#"([^ ]+)=([^"][^ ]+[^"])"#) {
|
|
|
|
|
Ok(re) => re,
|
2020-05-18 10:32:16 +02:00
|
|
|
Err(e) => return Err(ManifestError::Regex(e)),
|
2020-05-17 21:52:39 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let optionals_regex_quotes = match Regex::new(r#"([^ ]+)=([^"][^ ]+[^"])"#) {
|
|
|
|
|
Ok(re) => re,
|
2020-05-18 10:32:16 +02:00
|
|
|
Err(e) => return Err(ManifestError::Regex(e)),
|
2020-05-17 21:52:39 +02:00
|
|
|
};
|
|
|
|
|
|
2020-05-17 01:17:19 +02:00
|
|
|
for cap in value_no_space_regex.captures_iter(line.trim_start()) {
|
2020-05-17 21:52:39 +02:00
|
|
|
values.push(String::from(cap[1].trim()));
|
2020-05-17 01:17:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for cap in value_space_regex.captures_iter(line.trim_start()) {
|
2020-05-17 21:52:39 +02:00
|
|
|
values.push(String::from(cap[1].trim()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for cap in optionals_regex_quotes.captures_iter(line.trim_start()) {
|
|
|
|
|
if cap[1].trim().starts_with("name") || cap[1].trim().starts_with("value") {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-18 10:32:16 +02:00
|
|
|
properties.insert(Property {
|
2020-05-17 22:02:35 +02:00
|
|
|
key: String::from(cap[1].trim()),
|
|
|
|
|
value: String::from(cap[2].trim()),
|
2020-05-17 21:52:39 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for cap in optionals_regex_no_quotes.captures_iter(line.trim_start()) {
|
|
|
|
|
if cap[1].trim().starts_with("name") || cap[1].trim().starts_with("value") {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-18 10:32:16 +02:00
|
|
|
properties.insert(Property {
|
2020-05-17 22:02:35 +02:00
|
|
|
key: String::from(cap[1].trim()),
|
|
|
|
|
value: String::from(cap[2].trim()),
|
2020-05-17 21:52:39 +02:00
|
|
|
});
|
2020-05-17 01:17:19 +02:00
|
|
|
}
|
|
|
|
|
|
2020-05-18 10:32:16 +02:00
|
|
|
Ok(Attr {
|
2020-05-17 21:52:39 +02:00
|
|
|
key,
|
|
|
|
|
values,
|
|
|
|
|
properties,
|
2020-05-17 01:17:19 +02:00
|
|
|
})
|
2020-05-18 10:32:16 +02:00
|
|
|
}
|