use anyhow::Result;
use regex::Regex;
use std::collections::{HashMap, HashSet};
use crate::models::action::ActionRun;
use crate::models::context::ContextModel;
use crate::modifier::modifier::ModifierRegistry;
use crate::utils::constants;
pub struct ExpandedTemplate {
pub raw: String,
pub items: Vec<String>,
pub is_list: bool,
}
pub struct Context {
values: HashMap<String, ContextModel>,
}
impl Context {
pub fn new() -> Self {
Self {
values: HashMap::new(),
}
}
pub fn set(&mut self, tag: &str, value: ContextModel) {
self.values.insert(tag.to_string(), value);
}
pub fn get(&self, tag: &str) -> Option<&ContextModel> {
self.values.get(tag)
}
pub fn fill(
&self,
raw: &str,
run: &ActionRun,
modifier: &ModifierRegistry,
) -> Result<ExpandedTemplate> {
let is_encode = run == &ActionRun::Cmd;
let re = Regex::new(constants::TAG_PLACEHOLDER_PATTERN).unwrap();
let clean_text = Self::strip_all_template_quotes(&raw, is_encode);
let mut processed_placeholders = HashSet::new();
let mut list_expanded_tags: HashSet<String> = HashSet::new();
let mut results = vec![clean_text.clone()];
let mut is_list = false;
for cap in re.captures_iter(&clean_text) {
let placeholder = cap.get(0).unwrap().as_str();
let tag_name = cap.get(1).unwrap().as_str();
if processed_placeholders.contains(placeholder) {
continue;
}
if let Some(context_value) = self.values.get(tag_name) {
let modifier_value = cap.get(2).map(|m| m.as_str()).unwrap_or_default();
if modifier_value.is_empty() && placeholder.contains('|') {
anyhow::bail!("Empty modifier not allowed in '{}'", placeholder);
}
let processed_value = modifier.apply_modifier(modifier_value, context_value)?;
match processed_value {
ContextModel::List(items) => {
if list_expanded_tags.contains(tag_name) {
for (i, current) in results.iter_mut().enumerate() {
let idx = i % items.len();
let val = items[idx].to_string();
if is_encode {
*current = current.replace(
placeholder,
&shell_words::quote(&val).to_string(),
);
} else {
*current = current.replace(placeholder, &val);
}
}
} else {
is_list = true;
list_expanded_tags.insert(tag_name.to_string());
let mut next = Vec::new();
for item in items {
let mut s = item.to_string();
if is_encode {
s = shell_words::quote(&s).to_string();
}
for current in &results {
next.push(current.replace(placeholder, &s));
}
}
results = next;
}
}
_ => {
let mut final_string = processed_value.to_string();
if is_encode {
final_string = shell_words::quote(&final_string).to_string();
}
for current in results.iter_mut() {
*current = current.replace(placeholder, &final_string);
}
}
}
}
processed_placeholders.insert(placeholder.to_string());
}
Ok(ExpandedTemplate {
raw: raw.to_string(),
items: results,
is_list,
})
}
fn strip_all_template_quotes(text: &str, escape: bool) -> String {
if !escape {
return text.to_string();
}
let re = regex::Regex::new(constants::TAG_PLACEHOLDER_PATTERN).unwrap();
let mut result = text.to_string();
for cap in re.captures_iter(text) {
let placeholder = cap.get(0).unwrap().as_str();
for quote in &["'", "\""] {
let quoted = format!("{}{}{}", quote, placeholder, quote);
if result.contains("ed) {
result = result.replace("ed, placeholder);
}
}
}
result
}
}