use std::sync::LazyLock;
use regex::{CaptureMatches, Captures, Regex};
use crate::model::Variable;
const VARIABLE_REGEX: &str = r"\{\{((?:\{[^}]+\}|[^}]+))\}\}";
const ALT_VARIABLE_REGEX: &str = r"<([\w.*-]+)>";
static ALT_REGEX: LazyLock<Regex> = LazyLock::new(|| Regex::new(ALT_VARIABLE_REGEX).unwrap());
pub static COMMAND_VARIABLE_REGEX: LazyLock<Regex> = LazyLock::new(|| Regex::new(VARIABLE_REGEX).unwrap());
pub static COMMAND_VARIABLE_REGEX_ALT: LazyLock<Regex> =
LazyLock::new(|| Regex::new(&format!(r#"{VARIABLE_REGEX}|{ALT_VARIABLE_REGEX}"#)).unwrap());
pub static COMMAND_VARIABLE_REGEX_QUOTES: LazyLock<Regex> =
LazyLock::new(|| Regex::new(&format!(r#"'{VARIABLE_REGEX}'|"{VARIABLE_REGEX}"|{VARIABLE_REGEX}"#)).unwrap());
pub fn convert_alt_to_regular(command: &str) -> String {
ALT_REGEX.replace_all(command, "{{$1}}").into_owned()
}
pub fn extract_variables(command: &str) -> Vec<Variable> {
COMMAND_VARIABLE_REGEX
.captures_iter(command)
.filter_map(|cap| cap.get(1))
.map(|v| v.as_str())
.map(Variable::parse)
.collect()
}
pub struct SplitCaptures<'r, 't> {
finder: CaptureMatches<'r, 't>,
text: &'t str,
last: usize,
caps: Option<Captures<'t>>,
}
impl<'r, 't> SplitCaptures<'r, 't> {
pub fn new(regex: &'r Regex, text: &'t str) -> SplitCaptures<'r, 't> {
SplitCaptures {
finder: regex.captures_iter(text),
text,
last: 0,
caps: None,
}
}
}
#[derive(Debug)]
pub enum SplitItem<'t> {
Unmatched(&'t str),
Captured(Captures<'t>),
}
impl<'t> Iterator for SplitCaptures<'_, 't> {
type Item = SplitItem<'t>;
fn next(&mut self) -> Option<SplitItem<'t>> {
if let Some(caps) = self.caps.take() {
return Some(SplitItem::Captured(caps));
}
match self.finder.next() {
None => {
if self.last >= self.text.len() {
None
} else {
let s = &self.text[self.last..];
self.last = self.text.len();
Some(SplitItem::Unmatched(s))
}
}
Some(caps) => {
let m = caps.get(0).unwrap();
let unmatched = &self.text[self.last..m.start()];
self.last = m.end();
self.caps = Some(caps);
Some(SplitItem::Unmatched(unmatched))
}
}
}
}