Skip to main content

ryo_executor/executor/registry/converters/
remove.rs

1//! RemoveConverter: Converts MutationSpec::RemoveItem and MutationSpec::ReplaceCode
2//!
3//! `ReplaceCode` is grouped with `RemoveItem` because both target a single
4//! existing top-level item by SymbolId; they differ only in what replaces it.
5
6use crate::engine::ASTRegApply;
7use crate::executor::registry::converters::ResolveTargetSymbol;
8use crate::executor::registry::{ConvertError, MutationConverter};
9use crate::executor::spec::MutationSpec;
10use ryo_analysis::AnalysisContext;
11use ryo_mutations::{RemoveItemMutation, ReplaceCodeMutation};
12
13/// Converter for RemoveItem and ReplaceCode mutations
14#[derive(Debug, Clone, Default)]
15pub struct RemoveConverter;
16
17impl RemoveConverter {
18    pub fn new() -> Self {
19        Self
20    }
21}
22
23// RemoveConverter uses the default implementation of ResolveTargetSymbol
24impl ResolveTargetSymbol for RemoveConverter {}
25
26impl MutationConverter for RemoveConverter {
27    fn spec_kinds(&self) -> &'static [&'static str] {
28        &["RemoveItem", "ReplaceCode"]
29    }
30
31    fn convert_v2(
32        &self,
33        spec: &MutationSpec,
34        ctx: &AnalysisContext,
35    ) -> Result<Vec<Box<dyn ASTRegApply>>, ConvertError> {
36        match spec {
37            MutationSpec::RemoveItem {
38                target: target_symbol,
39                item_kind,
40            } => {
41                // Resolve target_symbol to SymbolId
42                let symbol_id = self.resolve_target_symbol(target_symbol, ctx)?;
43                let mutation = RemoveItemMutation::new(symbol_id, *item_kind);
44                Ok(vec![Box::new(mutation)])
45            }
46            MutationSpec::ReplaceCode {
47                target: target_symbol,
48                raw,
49            } => {
50                let symbol_id = self.resolve_target_symbol(target_symbol, ctx)?;
51                let mutation = ReplaceCodeMutation::new(symbol_id, raw.clone());
52                Ok(vec![Box::new(mutation)])
53            }
54            _ => Err(ConvertError::TypeMismatch {
55                expected: "RemoveItem | ReplaceCode",
56                actual: spec.kind_name().to_string(),
57            }),
58        }
59    }
60}
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65    use ryo_source::ItemKind;
66    use ryo_symbol::SymbolId;
67
68    #[test]
69    fn test_remove_converter_spec_kinds() {
70        let converter = RemoveConverter::new();
71        assert_eq!(converter.spec_kinds(), &["RemoveItem", "ReplaceCode"]);
72    }
73
74    #[test]
75    fn test_remove_converter_can_handle() {
76        let converter = RemoveConverter::new();
77
78        let spec = MutationSpec::RemoveItem {
79            target: crate::executor::spec::MutationTargetSymbol::ById(SymbolId::default()),
80            item_kind: ItemKind::Struct,
81        };
82        assert!(converter.can_handle(&spec));
83    }
84}