Skip to main content

ryo_executor/executor/registry/converters/
field.rs

1//! FieldConverter: Converts MutationSpec::AddField and RemoveField
2//!
3//! Uses MutationTargetSymbol-based resolution to find the target struct and its file.
4
5use crate::engine::ASTRegApply;
6use crate::executor::registry::converters::ResolveTargetSymbol;
7use crate::executor::registry::{ConvertError, MutationConverter};
8use crate::executor::spec::{MutationSpec, Visibility};
9use ryo_analysis::AnalysisContext;
10use ryo_mutations::{AddFieldMutation, RemoveFieldMutation};
11
12/// Converter for Field mutations (AddField, RemoveField)
13#[derive(Debug, Clone, Default)]
14pub struct FieldConverter;
15
16impl FieldConverter {
17    pub fn new() -> Self {
18        Self
19    }
20}
21
22// FieldConverter uses the default implementation of ResolveTargetSymbol
23impl ResolveTargetSymbol for FieldConverter {}
24
25impl MutationConverter for FieldConverter {
26    fn spec_kinds(&self) -> &'static [&'static str] {
27        &["AddField", "RemoveField"]
28    }
29
30    fn convert_v2(
31        &self,
32        spec: &MutationSpec,
33        ctx: &AnalysisContext,
34    ) -> Result<Vec<Box<dyn ASTRegApply>>, ConvertError> {
35        match spec {
36            MutationSpec::AddField {
37                target,
38                field_name,
39                field_type,
40                visibility,
41            } => {
42                // Resolve MutationTargetSymbol to SymbolId using trait method
43                let symbol_id = self.resolve_target_symbol(target, ctx)?;
44
45                let mut mutation = AddFieldMutation::new(symbol_id, field_name, field_type);
46                if *visibility == Visibility::Pub {
47                    mutation = mutation.public();
48                }
49                Ok(vec![Box::new(mutation)])
50            }
51            MutationSpec::RemoveField { target, field_name } => {
52                // Resolve MutationTargetSymbol to SymbolId using trait method
53                let symbol_id = self.resolve_target_symbol(target, ctx)?;
54
55                let mutation = RemoveFieldMutation::new(symbol_id, field_name);
56                Ok(vec![Box::new(mutation)])
57            }
58            _ => Err(ConvertError::TypeMismatch {
59                expected: "AddField or RemoveField",
60                actual: spec.kind_name().to_string(),
61            }),
62        }
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test]
71    fn test_field_converter_spec_kinds() {
72        let converter = FieldConverter::new();
73        assert_eq!(converter.spec_kinds(), &["AddField", "RemoveField"]);
74    }
75
76    #[test]
77    fn test_field_converter_can_handle_add() {
78        use crate::executor::spec::MutationTargetSymbol;
79        use ryo_symbol::{SymbolKind, SymbolPath, SymbolRegistry};
80
81        let converter = FieldConverter::new();
82        let mut registry = SymbolRegistry::new();
83        let path = SymbolPath::parse("test_crate::Config").unwrap();
84        let symbol_id = registry.register(path, SymbolKind::Struct).unwrap();
85
86        let spec = MutationSpec::AddField {
87            target: MutationTargetSymbol::by_id(symbol_id),
88            field_name: "timeout".into(),
89            field_type: "u64".into(),
90            visibility: Visibility::Pub,
91        };
92        assert!(converter.can_handle(&spec));
93    }
94
95    #[test]
96    fn test_field_converter_can_handle_remove() {
97        use crate::executor::spec::MutationTargetSymbol;
98        use ryo_symbol::{SymbolKind, SymbolPath, SymbolRegistry};
99
100        let converter = FieldConverter::new();
101        let mut registry = SymbolRegistry::new();
102        let path = SymbolPath::parse("test_crate::Config").unwrap();
103        let symbol_id = registry.register(path, SymbolKind::Struct).unwrap();
104
105        let spec = MutationSpec::RemoveField {
106            target: MutationTargetSymbol::by_id(symbol_id),
107            field_name: "timeout".into(),
108        };
109        assert!(converter.can_handle(&spec));
110    }
111}