Skip to main content

ryo_executor/engine/impls/
use_stmt.rs

1//! ASTRegApply implementation for use statement mutations
2
3use ryo_mutations::basic::{AddUseMutation, RemoveUseMutation};
4use ryo_mutations::MutationResult;
5use ryo_source::pure::{PureItem, PureUse, PureUseTree, PureVis};
6use ryo_symbol::SymbolKind;
7
8use crate::engine::{ASTMutationContext, ASTRegApply};
9
10impl ASTRegApply for AddUseMutation {
11    fn apply_to_registry(&self, ctx: &mut ASTMutationContext) -> MutationResult {
12        // Find crate root module
13        let module_id = ctx
14            .symbol_registry
15            .iter()
16            .filter(|(id, _)| ctx.symbol_registry.kind(*id) == Some(SymbolKind::Mod))
17            .find(|(_, path)| path.to_string() == "crate")
18            .map(|(id, _)| id);
19
20        let module_id = match module_id {
21            Some(id) => id,
22            None => {
23                return MutationResult {
24                    mutation_type: "AddUse".to_string(),
25                    changes: 0,
26                    description: "Crate root module not found".to_string(),
27                };
28            }
29        };
30
31        // Check if use statement already exists
32        let exists = ctx
33            .symbol_registry
34            .iter()
35            .filter(|(id, _)| ctx.symbol_registry.kind(*id) == Some(SymbolKind::Use))
36            .any(|(_, path)| path.name() == self.path);
37
38        if exists {
39            return MutationResult {
40                mutation_type: "AddUse".to_string(),
41                changes: 0,
42                description: format!("Use statement '{}' already exists", self.path),
43            };
44        }
45
46        // Parse path into PureUseTree
47        let tree = parse_path_to_tree(&self.path);
48
49        // Create the use statement
50        let use_item = PureUse {
51            vis: PureVis::Private,
52            tree,
53        };
54
55        // Use the path as the name for registration
56        let use_path = ctx
57            .symbol_registry
58            .path(module_id)
59            .and_then(|p| p.child(&self.path).ok());
60
61        if let Some(path) = use_path {
62            if let Ok(use_id) = ctx.symbol_registry.register(path, SymbolKind::Use) {
63                ctx.ast_registry.set(use_id, PureItem::Use(use_item));
64
65                return MutationResult {
66                    mutation_type: "AddUse".to_string(),
67                    changes: 1,
68                    description: format!("Added use statement '{}'", self.path),
69                };
70            }
71        }
72
73        MutationResult {
74            mutation_type: "AddUse".to_string(),
75            changes: 0,
76            description: format!("Failed to register use statement '{}'", self.path),
77        }
78    }
79}
80
81impl ASTRegApply for RemoveUseMutation {
82    fn apply_to_registry(&self, ctx: &mut ASTMutationContext) -> MutationResult {
83        let use_id = ctx
84            .symbol_registry
85            .iter()
86            .filter(|(id, _)| ctx.symbol_registry.kind(*id) == Some(SymbolKind::Use))
87            .find(|(_, path)| {
88                if self.path.contains("::") {
89                    path.to_string() == self.path || path.name() == self.path
90                } else {
91                    path.name() == self.path
92                }
93            })
94            .map(|(id, _)| id);
95
96        let use_id = match use_id {
97            Some(id) => id,
98            None => {
99                return MutationResult {
100                    mutation_type: "RemoveUse".to_string(),
101                    changes: 0,
102                    description: format!("Use statement '{}' not found", self.path),
103                };
104            }
105        };
106
107        ctx.ast_registry.remove(use_id);
108
109        MutationResult {
110            mutation_type: "RemoveUse".to_string(),
111            changes: 1,
112            description: format!("Removed use statement '{}'", self.path),
113        }
114    }
115}
116
117/// Parse a path string into PureUseTree
118fn parse_path_to_tree(path: &str) -> PureUseTree {
119    let parts: Vec<&str> = path.split("::").collect();
120    if parts.is_empty() {
121        return PureUseTree::Name(path.to_string());
122    }
123
124    // Build tree from right to left
125    let mut tree = PureUseTree::Name(
126        parts
127            .last()
128            .expect("is_empty() guard above guarantees a last element")
129            .to_string(),
130    );
131    for part in parts.iter().rev().skip(1) {
132        tree = PureUseTree::Path {
133            path: part.to_string(),
134            tree: Box::new(tree),
135        };
136    }
137    tree
138}