use ryo_mutations::basic::{AddDeriveMutation, RemoveDeriveMutation};
use ryo_mutations::MutationResult;
use ryo_source::pure::{PureAttrMeta, PureAttribute, PureItem};
use crate::engine::{ASTMutationContext, ASTRegApply, ModificationType, MutationEvent};
impl ASTRegApply for AddDeriveMutation {
fn apply_to_registry(&self, ctx: &mut ASTMutationContext) -> MutationResult {
let target_id = self.symbol_id;
let item = match ctx.ast_registry.get(target_id) {
Some(item) => item.clone(),
None => {
return MutationResult {
mutation_type: "AddDerive".to_string(),
changes: 0,
description: format!("AST not found for SymbolId({:?})", target_id),
};
}
};
let (new_item, changes) = match item {
PureItem::Struct(mut s) => {
let changes = add_derive_to_attrs(&mut s.attrs, &self.derives);
(PureItem::Struct(s), changes)
}
PureItem::Enum(mut e) => {
let mut changes = add_derive_to_attrs(&mut e.attrs, &self.derives);
if self.derives.contains(&"Default".to_string()) {
if let Some(first_variant) = e.variants.first_mut() {
let has_default_attr =
first_variant.attrs.iter().any(|a| a.path == "default");
if !has_default_attr {
first_variant.attrs.push(PureAttribute {
path: "default".to_string(),
meta: PureAttrMeta::Path,
is_inner: false,
});
changes += 1;
}
}
}
(PureItem::Enum(e), changes)
}
_ => {
return MutationResult {
mutation_type: "AddDerive".to_string(),
changes: 0,
description: format!("SymbolId({:?}) is not a struct or enum", target_id),
};
}
};
if changes > 0 {
ctx.set_ast(target_id, new_item);
for derive in &self.derives {
ctx.emit(MutationEvent::SymbolModified {
id: target_id,
modification: ModificationType::DeriveAdded(derive.clone()),
});
}
}
MutationResult {
mutation_type: "AddDerive".to_string(),
changes,
description: if changes > 0 {
format!(
"Added derive({}) to SymbolId({:?})",
self.derives.join(", "),
target_id
)
} else {
"Derives already present".to_string()
},
}
}
}
impl ASTRegApply for RemoveDeriveMutation {
fn apply_to_registry(&self, ctx: &mut ASTMutationContext) -> MutationResult {
let target_id = self.symbol_id;
let item = match ctx.ast_registry.get(target_id) {
Some(item) => item.clone(),
None => {
return MutationResult {
mutation_type: "RemoveDerive".to_string(),
changes: 0,
description: format!("AST not found for SymbolId({:?})", target_id),
};
}
};
let (new_item, changes) = match item {
PureItem::Struct(mut s) => {
let changes = remove_derive_from_attrs(&mut s.attrs, &self.derives);
(PureItem::Struct(s), changes)
}
PureItem::Enum(mut e) => {
let changes = remove_derive_from_attrs(&mut e.attrs, &self.derives);
(PureItem::Enum(e), changes)
}
_ => {
return MutationResult {
mutation_type: "RemoveDerive".to_string(),
changes: 0,
description: format!("SymbolId({:?}) is not a struct or enum", target_id),
};
}
};
if changes > 0 {
ctx.set_ast(target_id, new_item);
for derive in &self.derives {
ctx.emit(MutationEvent::SymbolModified {
id: target_id,
modification: ModificationType::DeriveRemoved(derive.clone()),
});
}
}
MutationResult {
mutation_type: "RemoveDerive".to_string(),
changes,
description: if changes > 0 {
format!(
"Removed derive({}) from SymbolId({:?})",
self.derives.join(", "),
target_id
)
} else {
"Derive not found".to_string()
},
}
}
}
fn add_derive_to_attrs(attrs: &mut Vec<PureAttribute>, derives: &[String]) -> usize {
let existing_derive_idx = attrs.iter().position(|a| a.path == "derive");
if let Some(idx) = existing_derive_idx {
let existing_args = match &attrs[idx].meta {
PureAttrMeta::List(args) => args.clone(),
_ => String::new(),
};
let mut all_derives: Vec<String> = existing_args
.split(',')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect();
let mut added = 0;
for d in derives {
if !all_derives.contains(d) {
all_derives.push(d.clone());
added += 1;
}
}
if added > 0 {
attrs[idx].meta = PureAttrMeta::List(all_derives.join(", "));
}
added
} else {
attrs.insert(
0,
PureAttribute {
path: "derive".to_string(),
meta: PureAttrMeta::List(derives.join(", ")),
is_inner: false,
},
);
derives.len()
}
}
fn remove_derive_from_attrs(attrs: &mut Vec<PureAttribute>, derives: &[String]) -> usize {
let existing_derive_idx = attrs.iter().position(|a| a.path == "derive");
if let Some(idx) = existing_derive_idx {
let existing_args = match &attrs[idx].meta {
PureAttrMeta::List(args) => args.clone(),
_ => String::new(),
};
let remaining: Vec<String> = existing_args
.split(',')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty() && !derives.contains(s))
.collect();
let original_count = existing_args
.split(',')
.filter(|s| !s.trim().is_empty())
.count();
let removed = original_count - remaining.len();
if remaining.is_empty() {
attrs.remove(idx);
} else {
attrs[idx].meta = PureAttrMeta::List(remaining.join(", "));
}
removed
} else {
0
}
}