use std::{
borrow::Cow,
cell::LazyCell,
str::FromStr
};
use crate::{Macro, MacroError};
#[derive(Debug, Clone, PartialEq, Eq, Default, Hash)]
pub struct TextMacro {
pub pattern: String
}
impl TextMacro {
#[inline]
pub fn new(pattern: impl Into<String>) -> Self {
Self { pattern: pattern.into() }
}
#[inline]
pub fn boxed(pattern: impl Into<String>) -> Box<dyn Macro> {
Box::new(Self { pattern: pattern.into() })
}
}
impl From<String> for TextMacro {
fn from(pattern: String) -> Self {
Self {pattern}
}
}
impl From<TextMacro> for String {
fn from(mac: TextMacro) -> String {
mac.pattern
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum Substring {
Count,
Index(usize)
}
impl Macro for TextMacro {
fn apply(&self, arguments: Vec<&str>) -> Result<String, MacroError> {
let amount = LazyCell::new(|| arguments.len().to_string());
let joined = LazyCell::new(|| arguments.join("/"));
let mut target = self.pattern.clone();
target = target.replace(r"\$", "\u{FFFF}");
let mut none_found = false;
let mut subs = Vec::new();
while !none_found {
none_found = true;
subs.clear();
let mut passed = target.clone();
for (idx, _) in passed.rmatch_indices('$') {
let after_index = &passed[idx+1..];
let (sub, end) = match after_index.chars().next() {
Some('#') => (Substring::Count, idx + 2),
Some('0') => (Substring::Index(0), idx + 2),
Some(c) if c.is_ascii_digit() => {
let end = after_index.find(|c: char| !c.is_ascii_digit()).unwrap_or(after_index.len());
let Some(index) = usize::from_str(&after_index[..end])
.ok()
.filter(|v| arguments.len() >= *v)
else { continue };
(Substring::Index(index), end + 1)
},
_ => { continue }
};
subs.push((idx .. idx + end, sub));
}
for (range, substring) in subs.drain(..) {
let repl: Cow<'_, str> = match substring {
Substring::Count => Cow::Borrowed(&*amount),
Substring::Index(0) => Cow::Borrowed(&*joined),
Substring::Index(n) => if let Some(arg) = arguments.get(n-1) {
Cow::Borrowed(&**arg)
} else { continue }
};
none_found = false;
dbg!(&passed, &range, &repl);
passed.replace_range(range, &repl);
}
target = passed;
}
target = target.replace('\u{FFFF}', "$");
Ok(target)
}
}