use crate::models::context::ContextModel;
use anyhow::Result;
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ModifierKey {
Ast,
Contains,
Empty,
Equals,
IsDir,
IsFile,
Join,
Lower,
Resolve,
Reverse,
Size,
Sort,
Split,
Take,
Trim,
Upper,
}
impl ModifierKey {
pub fn from_str(s: &str) -> Option<Self> {
match s {
"ast" => Some(Self::Ast),
"contains" => Some(Self::Contains),
"empty" => Some(Self::Empty),
"equals" => Some(Self::Equals),
"is_dir" => Some(Self::IsDir),
"is_file" => Some(Self::IsFile),
"join" => Some(Self::Join),
"lower" => Some(Self::Lower),
"resolve" => Some(Self::Resolve),
"reverse" => Some(Self::Reverse),
"size" => Some(Self::Size),
"sort" => Some(Self::Sort),
"split" => Some(Self::Split),
"take" => Some(Self::Take),
"trim" => Some(Self::Trim),
"upper" => Some(Self::Upper),
_ => None,
}
}
}
pub trait Modifier: Send + Sync {
fn key(&self) -> ModifierKey;
fn apply(&self, value: &ContextModel, arg: &str) -> Result<ContextModel>;
}
pub struct ModifierRegistry {
modifiers: HashMap<ModifierKey, Box<dyn Modifier>>,
}
impl ModifierRegistry {
pub fn new() -> Self {
let mut registry = Self {
modifiers: HashMap::new(),
};
registry.register(Box::new(super::ast::AstModifier));
registry.register(Box::new(super::contains::ContainsModifier));
registry.register(Box::new(super::empty::EmptyModifier));
registry.register(Box::new(super::equals::EqualsModifier));
registry.register(Box::new(super::is_dir::IsDirModifier));
registry.register(Box::new(super::is_file::IsFileModifier));
registry.register(Box::new(super::join::JoinModifier));
registry.register(Box::new(super::lower::LowerModifier));
registry.register(Box::new(super::resolve::ResolveModifier));
registry.register(Box::new(super::reverse::ReverseModifier));
registry.register(Box::new(super::size::SizeModifier));
registry.register(Box::new(super::sort::SortModifier));
registry.register(Box::new(super::split::SplitModifier));
registry.register(Box::new(super::take::TakeModifier));
registry.register(Box::new(super::trim::TrimModifier));
registry.register(Box::new(super::upper::UpperModifier));
registry
}
pub fn register(&mut self, modifier: Box<dyn Modifier>) {
self.modifiers.insert(modifier.key(), modifier);
}
pub fn get(&self, key: ModifierKey) -> Option<&dyn Modifier> {
self.modifiers.get(&key).map(|m| m.as_ref())
}
pub fn apply_modifier(&self, modifier: &str, value: &ContextModel) -> Result<ContextModel> {
if modifier.is_empty() {
return Ok(value.clone());
}
let mut current = value.clone();
for part in modifier.split('|') {
let (name, arg) = part.split_once(':').unwrap_or((part, ""));
match ModifierKey::from_str(name) {
Some(key) => {
if let Some(modifier) = self.get(key) {
current = modifier.apply(¤t, arg)?;
}
}
None => {}
}
}
Ok(current)
}
}
pub fn invert_bool(value: ContextModel) -> ContextModel {
match value {
ContextModel::Bool(b) => ContextModel::Bool(!b),
ContextModel::List(items) => {
let inverted: Vec<ContextModel> = items
.iter()
.map(|i| match i {
ContextModel::Bool(b) => ContextModel::Bool(!b),
_ => i.clone(),
})
.collect();
ContextModel::List(inverted)
}
_ => value,
}
}