use crate::engine::ASTRegApply;
use crate::executor::registry::converters::ResolveTargetSymbol;
use crate::executor::registry::{ConvertError, MutationConverter};
use crate::executor::spec::MutationSpec;
use ryo_analysis::AnalysisContext;
use ryo_mutations::{AddItemMutation, RemoveItemMutation};
use ryo_source::ItemKind;
#[derive(Debug, Clone, Default)]
pub struct SpecAliasConverter;
impl SpecAliasConverter {
pub fn new() -> Self {
Self
}
}
impl ResolveTargetSymbol for SpecAliasConverter {}
impl MutationConverter for SpecAliasConverter {
fn spec_kinds(&self) -> &'static [&'static str] {
&["AddSpec", "RemoveSpec", "ValidateSpec"]
}
fn convert_v2(
&self,
spec: &MutationSpec,
ctx: &AnalysisContext,
) -> Result<Vec<Box<dyn ASTRegApply>>, ConvertError> {
match spec {
MutationSpec::AddSpec {
type_id,
module_id,
group,
alias_name,
relations,
} => {
let wrapped_name = ctx
.registry
.resolve(*type_id)
.map(|p| p.name().to_string())
.ok_or_else(|| {
ConvertError::TargetNotFound(format!(
"AddSpec wrapped type SymbolId {:?} not in registry",
type_id
))
})?;
let alias = alias_name
.clone()
.unwrap_or_else(|| format!("{}Spec", wrapped_name));
let content = if relations.is_empty() {
format!("pub type {} = Spec<{}, {}>;", alias, group, wrapped_name)
} else {
let rel_terms: Vec<String> = relations
.iter()
.map(|r| format!("{}<{}>", r.kind.as_type_name(), r.target))
.collect();
let rel_block = if rel_terms.len() == 1 {
rel_terms.into_iter().next().expect("len 1 above")
} else {
format!("({})", rel_terms.join(", "))
};
format!(
"pub type {} = SpecWith<{}, {}, {}>;",
alias, group, rel_block, wrapped_name
)
};
let mutation = AddItemMutation::new(*module_id, content);
Ok(vec![Box::new(mutation)])
}
MutationSpec::RemoveSpec { type_id, .. } => {
let mutation = RemoveItemMutation::new(*type_id, ItemKind::TypeAlias);
Ok(vec![Box::new(mutation)])
}
MutationSpec::ValidateSpec { .. } => {
Ok(vec![])
}
_ => Err(ConvertError::TypeMismatch {
expected: "AddSpec | RemoveSpec | ValidateSpec",
actual: spec.kind_name().to_string(),
}),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_spec_alias_converter_spec_kinds() {
let converter = SpecAliasConverter::new();
assert_eq!(
converter.spec_kinds(),
&["AddSpec", "RemoveSpec", "ValidateSpec"]
);
}
}