use crate::aaml::AAML;
use crate::commands::Command;
use crate::error::AamlError;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DeriveCommand;
fn parse_derive_arg(raw: &str) -> (&str, Vec<&str>) {
let (path_raw, rest) = if raw.starts_with('"') || raw.starts_with('\'') {
let q = raw.chars().next().unwrap();
match raw[1..].find(q) {
Some(end) => {
let path = &raw[1..end + 1];
let after = raw[end + 2..].trim_start_matches(':').trim();
(path, after)
}
None => (raw, ""),
}
} else {
match raw.find("::") {
Some(pos) => (&raw[..pos], &raw[pos + 2..]),
None => (raw, ""),
}
};
let selectors = rest
.split("::")
.map(str::trim)
.filter(|s| !s.is_empty())
.collect();
(path_raw.trim(), selectors)
}
impl Command for DeriveCommand {
fn name(&self) -> &str {
"derive"
}
fn execute(&self, aaml: &mut AAML, args: &str) -> Result<(), AamlError> {
let raw = args.trim();
if raw.is_empty() {
return Err(AamlError::DirectiveError(
"derive".into(),
"Missing file path".into(),
));
}
let child_schema_names: Vec<String> = aaml.get_schemas_mut().keys().cloned().collect();
let (path, selectors) = parse_derive_arg(raw);
let mut base = AAML::load(path)?;
if selectors.is_empty() {
for (name, schema) in base.get_schemas_mut().drain() {
aaml.get_schemas_mut().entry(name).or_insert(schema);
}
} else {
for selector in &selectors {
let schema = base.get_schemas_mut().remove(*selector).ok_or_else(|| {
AamlError::DirectiveError(
"derive".into(),
format!("Schema '{selector}' not found in '{path}'"),
)
})?;
aaml.get_schemas_mut()
.entry(selector.to_string())
.or_insert(schema);
}
}
for (k, v) in base.get_map_mut().drain() {
aaml.get_map_mut().entry(k).or_insert(v);
}
let names: Vec<&str> = child_schema_names.iter().map(|s| s.as_str()).collect();
aaml.validate_schemas_completeness_for(&names)?;
Ok(())
}
}