Skip to main content

ryo_executor/engine/impls/
default.rs

1//! V2 ASTRegApply implementations for Default trait mutations
2//!
3//! - DefaultMutation: Add #[derive(Default)] or impl Default for structs
4//! - DeriveDefaultMutation: Convert manual Default impl to #[derive(Default)]
5
6use ryo_mutations::idiom::{DefaultMutation, DeriveDefaultMutation};
7use ryo_mutations::MutationResult;
8use ryo_source::pure::PureItem;
9use ryo_symbol::SymbolKind;
10
11use crate::engine::{ASTMutationContext, ASTRegApply};
12
13impl ASTRegApply for DefaultMutation {
14    fn apply_to_registry(&self, ctx: &mut ASTMutationContext) -> MutationResult {
15        let mut total_changes = 0;
16
17        // Find structs that need Default
18        let struct_ids: Vec<_> = ctx
19            .symbol_registry
20            .iter()
21            .filter(|(id, _)| ctx.symbol_registry.kind(*id) == Some(SymbolKind::Struct))
22            .map(|(id, path)| (id, path.name().to_string()))
23            .collect();
24
25        for (struct_id, struct_name) in struct_ids {
26            // Apply target filter
27            if let Some(ref target) = self.target_struct {
28                if &struct_name != target {
29                    continue;
30                }
31            }
32
33            if let Some(PureItem::Struct(s)) = ctx.ast_registry.get(struct_id) {
34                // Skip if already has derive(Default)
35                let has_default = s.attrs.iter().any(|attr| {
36                    attr.path == "derive"
37                        && matches!(&attr.meta, ryo_source::pure::PureAttrMeta::List(args) if args.contains("Default"))
38                });
39
40                if has_default {
41                    continue;
42                }
43
44                // Only named-field structs
45                let has_named_fields = matches!(&s.fields, ryo_source::pure::PureFields::Named(_));
46                if !has_named_fields {
47                    continue;
48                }
49
50                let mut new_struct = s.clone();
51
52                if self.use_derive {
53                    // Add derive(Default)
54                    let derive_attr_idx = new_struct
55                        .attrs
56                        .iter()
57                        .position(|attr| attr.path == "derive");
58
59                    if let Some(idx) = derive_attr_idx {
60                        if let ryo_source::pure::PureAttrMeta::List(ref args) =
61                            new_struct.attrs[idx].meta
62                        {
63                            if !args.contains("Default") {
64                                new_struct.attrs[idx].meta = ryo_source::pure::PureAttrMeta::List(
65                                    format!("{}, Default", args),
66                                );
67                            }
68                        }
69                    } else {
70                        new_struct.attrs.push(ryo_source::pure::PureAttribute {
71                            path: "derive".to_string(),
72                            meta: ryo_source::pure::PureAttrMeta::List("Default".to_string()),
73                            is_inner: false,
74                        });
75                    }
76
77                    ctx.set_ast(struct_id, PureItem::Struct(new_struct));
78                    total_changes += 1;
79                }
80                // Note: impl Default generation would require more complex code
81                // For now, only derive is supported in V2
82            }
83        }
84
85        MutationResult {
86            mutation_type: "Default".to_string(),
87            changes: total_changes,
88            description: if total_changes > 0 {
89                format!("Added #[derive(Default)] to {} struct(s)", total_changes)
90            } else {
91                "No Default implementations added".to_string()
92            },
93        }
94    }
95}
96
97impl ASTRegApply for DeriveDefaultMutation {
98    fn apply_to_registry(&self, ctx: &mut ASTMutationContext) -> MutationResult {
99        // Find Default impl blocks that can be converted to derive
100        let mut types_to_derive: Vec<(ryo_symbol::SymbolId, String)> = Vec::new();
101
102        let impl_ids: Vec<_> = ctx
103            .symbol_registry
104            .iter()
105            .filter(|(id, _)| ctx.symbol_registry.kind(*id) == Some(SymbolKind::Impl))
106            .map(|(id, _)| id)
107            .collect();
108
109        for impl_id in impl_ids {
110            if let Some(PureItem::Impl(imp)) = ctx.ast_registry.get(impl_id) {
111                // Check if this is a Default impl
112                if imp.trait_.as_deref() != Some("Default") {
113                    continue;
114                }
115
116                if let Some(ref target) = self.target_type {
117                    if &imp.self_ty != target {
118                        continue;
119                    }
120                }
121
122                // For simplicity, just mark for derive if it's a Default impl
123                // A full implementation would check if all fields use default values
124                types_to_derive.push((impl_id, imp.self_ty.clone()));
125            }
126        }
127
128        if types_to_derive.is_empty() {
129            return MutationResult {
130                mutation_type: "DeriveDefault".to_string(),
131                changes: 0,
132                description: "No derivable Default implementations found".to_string(),
133            };
134        }
135
136        let mut changes = 0;
137
138        // Add derive(Default) to structs and remove impl blocks
139        for (impl_id, type_name) in &types_to_derive {
140            // Find the struct
141            let struct_id = ctx
142                .symbol_registry
143                .iter()
144                .filter(|(id, _)| ctx.symbol_registry.kind(*id) == Some(SymbolKind::Struct))
145                .find(|(_, path)| path.name() == type_name)
146                .map(|(id, _)| id);
147
148            if let Some(struct_id) = struct_id {
149                if let Some(PureItem::Struct(s)) = ctx.ast_registry.get(struct_id) {
150                    let mut new_struct = s.clone();
151
152                    // Check if derive(Default) already exists
153                    let has_default = new_struct.attrs.iter().any(|attr| {
154                        attr.path == "derive"
155                            && matches!(&attr.meta, ryo_source::pure::PureAttrMeta::List(args) if args.contains("Default"))
156                    });
157
158                    if !has_default {
159                        // Add derive(Default) - merge with existing derive if present
160                        let derive_attr_idx = new_struct
161                            .attrs
162                            .iter()
163                            .position(|attr| attr.path == "derive");
164
165                        if let Some(idx) = derive_attr_idx {
166                            if let ryo_source::pure::PureAttrMeta::List(ref args) =
167                                new_struct.attrs[idx].meta
168                            {
169                                new_struct.attrs[idx].meta = ryo_source::pure::PureAttrMeta::List(
170                                    format!("{}, Default", args),
171                                );
172                            }
173                        } else {
174                            new_struct.attrs.push(ryo_source::pure::PureAttribute {
175                                path: "derive".to_string(),
176                                meta: ryo_source::pure::PureAttrMeta::List("Default".to_string()),
177                                is_inner: false,
178                            });
179                        }
180
181                        ctx.set_ast(struct_id, PureItem::Struct(new_struct));
182                        changes += 1;
183                    }
184                }
185            }
186
187            // Remove the Default impl block
188            ctx.ast_registry.remove(*impl_id);
189            changes += 1;
190        }
191
192        MutationResult {
193            mutation_type: "DeriveDefault".to_string(),
194            changes,
195            description: format!(
196                "Converted {} manual Default impl(s) to #[derive(Default)]",
197                types_to_derive.len()
198            ),
199        }
200    }
201}