@@ -12,3 +12,4 @@ serde = { version = "1", features = ["derive"] }
|
||||
thiserror = "2"
|
||||
tokio = { version = "1", features = ["process", "time"] }
|
||||
tracing = "0.1"
|
||||
db = { path = "../db" }
|
||||
|
||||
+32
-49
@@ -2,12 +2,12 @@ use crate::{
|
||||
Error,
|
||||
parser::{Action, OnError, Rule, RuleFile, Str, When},
|
||||
};
|
||||
use db::DbSection;
|
||||
use regex::Regex;
|
||||
use rustc_hash::FxHashMap;
|
||||
use std::{
|
||||
path::Path,
|
||||
process::{ExitStatus, Stdio},
|
||||
sync::{Arc, RwLock},
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
use tokio::{io::AsyncReadExt, process::Child};
|
||||
@@ -29,29 +29,25 @@ impl RuleSet {
|
||||
);
|
||||
continue;
|
||||
};
|
||||
for file in read_dir {
|
||||
let Ok(file) = file else {
|
||||
tracing::warn!(
|
||||
"Error while reading directory \"{}\" for rule files.",
|
||||
dir.as_ref().display()
|
||||
);
|
||||
break;
|
||||
};
|
||||
if file.file_name().as_encoded_bytes().starts_with(b".") {
|
||||
let mut files: Vec<_> = read_dir.filter_map(|x| x.ok()).map(|x| x.path()).collect();
|
||||
files.sort_unstable();
|
||||
for file in files {
|
||||
if file
|
||||
.file_name()
|
||||
.unwrap()
|
||||
.as_encoded_bytes()
|
||||
.starts_with(b".")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let rule_file = match RuleFile::parse(file.path()) {
|
||||
let rule_file = match RuleFile::parse(&file) {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
tracing::warn!(
|
||||
"Failed to parse rule file \"{}\": {}",
|
||||
file.path().display(),
|
||||
e,
|
||||
);
|
||||
tracing::warn!("Failed to parse rule file \"{}\": {}", file.display(), e,);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
tracing::debug!("Activating rule file \"{}\"", file.path().display());
|
||||
tracing::debug!("Activating rule file \"{}\"", file.display());
|
||||
rule_files.push(rule_file);
|
||||
}
|
||||
}
|
||||
@@ -61,31 +57,20 @@ impl RuleSet {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Engine {
|
||||
properties: RwLock<FxHashMap<String, String>>,
|
||||
pub properties: DbSection,
|
||||
command_policy: CommandPolicy,
|
||||
}
|
||||
impl Engine {
|
||||
pub fn new() -> Self {
|
||||
pub fn with_properties(properties: DbSection) -> Self {
|
||||
Self {
|
||||
properties: Default::default(),
|
||||
properties,
|
||||
command_policy: CommandPolicy::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_property(&self, key: &str) -> Option<String> {
|
||||
self.properties.read().unwrap().get(key).cloned()
|
||||
}
|
||||
|
||||
pub fn set_property(&self, key: String, val: String) {
|
||||
self.properties.write().unwrap().insert(key, val);
|
||||
}
|
||||
|
||||
pub fn remove_property(&self, key: &str) {
|
||||
self.properties.write().unwrap().remove(key);
|
||||
}
|
||||
|
||||
pub fn get_list(&self, key: &str) -> Vec<String> {
|
||||
self.get_property(key)
|
||||
self.properties
|
||||
.get(key)
|
||||
.unwrap_or_default()
|
||||
.split('\0')
|
||||
.filter(|x| !x.is_empty())
|
||||
@@ -93,13 +78,15 @@ impl Engine {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn list_add(&self, key: String, val: &str) {
|
||||
let orig = self.get_property(&key).unwrap_or_default();
|
||||
self.set_property(key, format!("{orig}{val}\0"));
|
||||
pub fn list_add(&self, key: &str, val: &str) {
|
||||
let orig = self.properties.get(&key).unwrap_or_default();
|
||||
self.properties.insert(&key, format!("{orig}{val}\0"));
|
||||
}
|
||||
|
||||
pub async fn exec(&self, rule_set: &RuleSet) {
|
||||
for rule_file in rule_set.0.iter() {
|
||||
self.properties
|
||||
.insert("RULEFILE", rule_file.path.display().to_string());
|
||||
for rule in rule_file.rules.iter() {
|
||||
if let Err(err) = self.exec_rule(rule).await {
|
||||
if !matches!(rule.on_error, OnError::Continue) {
|
||||
@@ -149,7 +136,7 @@ impl Engine {
|
||||
let regex = self.bake_str(regex)?;
|
||||
let regex = format!("^{regex}$");
|
||||
let regex = Regex::new(®ex).map_err(Error::Regex)?;
|
||||
let Some(val) = self.get_property(&key) else {
|
||||
let Some(val) = self.properties.get(&key) else {
|
||||
return Ok(false);
|
||||
};
|
||||
Ok(regex.is_match(&val))
|
||||
@@ -158,7 +145,7 @@ impl Engine {
|
||||
let key = self.bake_str(key)?;
|
||||
let regex = self.bake_str(regex)?;
|
||||
let regex = Regex::new(®ex).map_err(Error::Regex)?;
|
||||
let Some(val) = self.get_property(&key) else {
|
||||
let Some(val) = self.properties.get(&key) else {
|
||||
return Ok(false);
|
||||
};
|
||||
Ok(regex.is_match(&val))
|
||||
@@ -171,15 +158,15 @@ impl Engine {
|
||||
Action::Set(key, val) => {
|
||||
let key = self.bake_str(key)?;
|
||||
let val = self.bake_str(val)?;
|
||||
self.set_property(key, val);
|
||||
self.properties.insert(&key, val);
|
||||
}
|
||||
Action::Add(key, val) => {
|
||||
let key = self.bake_str(key)?;
|
||||
let val = self.bake_str(val)?;
|
||||
self.list_add(key, &val);
|
||||
self.list_add(&key, &val);
|
||||
}
|
||||
Action::Unset(key) => {
|
||||
self.remove_property(&self.bake_str(key)?);
|
||||
self.properties.remove(&self.bake_str(key)?);
|
||||
}
|
||||
Action::Command(cmd) => {
|
||||
let cmd = self.bake_str(cmd)?;
|
||||
@@ -189,7 +176,7 @@ impl Engine {
|
||||
let key = self.bake_str(key)?;
|
||||
let cmd = self.bake_str(cmd)?;
|
||||
let val = self.command_policy.run_provider(&cmd).await?;
|
||||
self.set_property(key, val);
|
||||
self.properties.insert(&key, val);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -216,7 +203,8 @@ impl Engine {
|
||||
current_property = None;
|
||||
} else if ch == '}' {
|
||||
let value = self
|
||||
.get_property(property)
|
||||
.properties
|
||||
.get(property)
|
||||
.ok_or_else(|| Error::PropertyNotFound(property.clone()))?;
|
||||
result.push_str(&value);
|
||||
current_property = None;
|
||||
@@ -239,11 +227,6 @@ impl Engine {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Default for Engine {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CommandPolicy {
|
||||
|
||||
Reference in New Issue
Block a user