ips/src/actions/mod.rs

201 lines
5.5 KiB
Rust
Raw Normal View History

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
}