1use ryo_mutations::basic::{AddStructLiteralFieldMutation, RemoveStructLiteralFieldMutation};
36use ryo_mutations::MutationResult;
37use ryo_source::pure::macro_utils;
38use ryo_source::pure::{PureBlock, PureExpr, PureImplItem, PureItem, PureStmt, ToSynError};
39
40use crate::engine::{ASTMutationContext, ASTRegApply, ModificationType};
41
42impl ASTRegApply for AddStructLiteralFieldMutation {
47 fn apply_to_registry(&self, ctx: &mut ASTMutationContext) -> MutationResult {
48 let struct_name = ctx
50 .symbol_registry
51 .path(self.struct_id)
52 .map(|p| p.name().to_string())
53 .unwrap_or_else(|| format!("{:?}", self.struct_id));
54
55 let mut total_changes = 0;
56
57 let symbol_ids: Vec<_> = ctx.symbol_registry.iter().map(|(id, _)| id).collect();
59
60 for id in symbol_ids {
61 if let Some(kind) = ctx.symbol_registry.kind(id) {
63 if kind == ryo_symbol::SymbolKind::Method {
64 continue;
65 }
66 }
67
68 let ast = match ctx.get_ast_mut(id) {
69 Some(ast) => ast,
70 None => continue,
71 };
72
73 let changes = match ast {
74 PureItem::Fn(f) => match walk_and_add_field(
75 &mut f.body,
76 &struct_name,
77 &self.field_name,
78 &self.value,
79 None,
80 ) {
81 Ok(c) => c,
82 Err(e) => {
83 return MutationResult {
84 mutation_type: "AddStructLiteralField".to_string(),
85 changes: 0,
86 description: format!("Failed to serialize macro tokens: {}", e),
87 };
88 }
89 },
90 PureItem::Impl(impl_block) => {
91 let self_ty = Some(impl_block.self_ty.as_str());
92 let mut count = 0;
93 for item in &mut impl_block.items {
94 if let PureImplItem::Fn(method) = item {
95 match walk_and_add_field(
96 &mut method.body,
97 &struct_name,
98 &self.field_name,
99 &self.value,
100 self_ty,
101 ) {
102 Ok(c) => count += c,
103 Err(e) => {
104 return MutationResult {
105 mutation_type: "AddStructLiteralField".to_string(),
106 changes: 0,
107 description: format!(
108 "Failed to serialize macro tokens: {}",
109 e
110 ),
111 };
112 }
113 }
114 }
115 }
116 count
117 }
118 _ => 0,
119 };
120
121 if changes > 0 {
122 ctx.emit_modified(
123 id,
124 ModificationType::Other("StructLiteralFieldAdded".into()),
125 );
126 total_changes += changes;
127 }
128 }
129
130 MutationResult {
131 mutation_type: "AddStructLiteralField".to_string(),
132 changes: total_changes,
133 description: if total_changes > 0 {
134 format!(
135 "Added field '{}' to {} struct literal(s) of '{}'",
136 self.field_name, total_changes, struct_name
137 )
138 } else {
139 format!(
140 "No struct literals of '{}' found or field already exists",
141 struct_name
142 )
143 },
144 }
145 }
146}
147
148impl ASTRegApply for RemoveStructLiteralFieldMutation {
153 fn apply_to_registry(&self, ctx: &mut ASTMutationContext) -> MutationResult {
154 let struct_name = ctx
156 .symbol_registry
157 .path(self.struct_id)
158 .map(|p| p.name().to_string())
159 .unwrap_or_else(|| format!("{:?}", self.struct_id));
160
161 let mut total_changes = 0;
162
163 let symbol_ids: Vec<_> = ctx.symbol_registry.iter().map(|(id, _)| id).collect();
165
166 for id in symbol_ids {
167 if let Some(kind) = ctx.symbol_registry.kind(id) {
169 if kind == ryo_symbol::SymbolKind::Method {
170 continue;
171 }
172 }
173
174 let ast = match ctx.get_ast_mut(id) {
175 Some(ast) => ast,
176 None => continue,
177 };
178
179 let changes = match ast {
180 PureItem::Fn(f) => {
181 match walk_and_remove_field(&mut f.body, &struct_name, &self.field_name, None) {
182 Ok(c) => c,
183 Err(e) => {
184 return MutationResult {
185 mutation_type: "RemoveStructLiteralField".to_string(),
186 changes: 0,
187 description: format!("Failed to serialize macro tokens: {}", e),
188 };
189 }
190 }
191 }
192 PureItem::Impl(impl_block) => {
193 let self_ty = Some(impl_block.self_ty.as_str());
194 let mut count = 0;
195 for item in &mut impl_block.items {
196 if let PureImplItem::Fn(method) = item {
197 match walk_and_remove_field(
198 &mut method.body,
199 &struct_name,
200 &self.field_name,
201 self_ty,
202 ) {
203 Ok(c) => count += c,
204 Err(e) => {
205 return MutationResult {
206 mutation_type: "RemoveStructLiteralField".to_string(),
207 changes: 0,
208 description: format!(
209 "Failed to serialize macro tokens: {}",
210 e
211 ),
212 };
213 }
214 }
215 }
216 }
217 count
218 }
219 _ => 0,
220 };
221
222 if changes > 0 {
223 ctx.emit_modified(
224 id,
225 ModificationType::Other("StructLiteralFieldRemoved".into()),
226 );
227 total_changes += changes;
228 }
229 }
230
231 MutationResult {
232 mutation_type: "RemoveStructLiteralField".to_string(),
233 changes: total_changes,
234 description: if total_changes > 0 {
235 format!(
236 "Removed field '{}' from {} struct literal(s) of '{}'",
237 self.field_name, total_changes, struct_name
238 )
239 } else {
240 format!(
241 "No struct literals of '{}' with field '{}' found",
242 struct_name, self.field_name
243 )
244 },
245 }
246 }
247}
248
249fn matches_struct(path: &str, struct_name: &str, self_ty: Option<&str>) -> bool {
255 if path.ends_with(struct_name) || path == struct_name {
257 return true;
258 }
259 if path == "Self" {
261 if let Some(ty) = self_ty {
262 return ty.ends_with(struct_name) || ty == struct_name;
263 }
264 }
265 false
266}
267
268fn parse_value(value: &str) -> PureExpr {
270 let value = value.trim();
271
272 if value == "None" {
274 return PureExpr::Path("None".to_string());
275 }
276
277 if value.starts_with("Some(") && value.ends_with(')') {
279 let inner = &value[5..value.len() - 1];
280 return PureExpr::Call {
281 func: Box::new(PureExpr::Path("Some".to_string())),
282 args: vec![PureExpr::Other(inner.to_string())],
283 };
284 }
285
286 if value == "Default::default()" {
288 return PureExpr::Call {
289 func: Box::new(PureExpr::Path("Default::default".to_string())),
290 args: vec![],
291 };
292 }
293
294 if value.parse::<i64>().is_ok() || value.parse::<f64>().is_ok() {
296 return PureExpr::Lit(value.to_string());
297 }
298
299 PureExpr::Other(value.to_string())
301}
302
303fn walk_and_add_field(
305 block: &mut PureBlock,
306 struct_name: &str,
307 field_name: &str,
308 value: &str,
309 self_ty: Option<&str>,
310) -> Result<usize, ToSynError> {
311 let mut count = 0;
312 for stmt in &mut block.stmts {
313 count += walk_stmt_and_add_field(stmt, struct_name, field_name, value, self_ty)?;
314 }
315 Ok(count)
316}
317
318fn walk_and_remove_field(
320 block: &mut PureBlock,
321 struct_name: &str,
322 field_name: &str,
323 self_ty: Option<&str>,
324) -> Result<usize, ToSynError> {
325 let mut count = 0;
326 for stmt in &mut block.stmts {
327 count += walk_stmt_and_remove_field(stmt, struct_name, field_name, self_ty)?;
328 }
329 Ok(count)
330}
331
332fn walk_stmt_and_add_field(
333 stmt: &mut PureStmt,
334 struct_name: &str,
335 field_name: &str,
336 value: &str,
337 self_ty: Option<&str>,
338) -> Result<usize, ToSynError> {
339 match stmt {
340 PureStmt::Local {
341 init: Some(expr), ..
342 } => {
343 return walk_expr_and_add_field(expr, struct_name, field_name, value, self_ty);
344 }
345 PureStmt::Expr(expr) | PureStmt::Semi(expr) => {
346 return walk_expr_and_add_field(expr, struct_name, field_name, value, self_ty);
347 }
348 _ => {}
349 }
350 Ok(0)
351}
352
353fn walk_stmt_and_remove_field(
354 stmt: &mut PureStmt,
355 struct_name: &str,
356 field_name: &str,
357 self_ty: Option<&str>,
358) -> Result<usize, ToSynError> {
359 match stmt {
360 PureStmt::Local {
361 init: Some(expr), ..
362 } => {
363 return walk_expr_and_remove_field(expr, struct_name, field_name, self_ty);
364 }
365 PureStmt::Expr(expr) | PureStmt::Semi(expr) => {
366 return walk_expr_and_remove_field(expr, struct_name, field_name, self_ty);
367 }
368 _ => {}
369 }
370 Ok(0)
371}
372
373fn walk_expr_and_add_field(
374 expr: &mut PureExpr,
375 struct_name: &str,
376 field_name: &str,
377 value: &str,
378 self_ty: Option<&str>,
379) -> Result<usize, ToSynError> {
380 let mut count = 0;
381
382 if let PureExpr::Struct { path, fields } = expr {
384 if matches_struct(path, struct_name, self_ty) {
385 if !fields.iter().any(|(name, _)| name == field_name) {
387 fields.push((field_name.to_string(), parse_value(value)));
388 count += 1;
389 }
390 }
391 }
392
393 count += walk_expr_children_and_add_field(expr, struct_name, field_name, value, self_ty)?;
395 Ok(count)
396}
397
398fn walk_expr_and_remove_field(
399 expr: &mut PureExpr,
400 struct_name: &str,
401 field_name: &str,
402 self_ty: Option<&str>,
403) -> Result<usize, ToSynError> {
404 let mut count = 0;
405
406 if let PureExpr::Struct { path, fields } = expr {
408 if matches_struct(path, struct_name, self_ty) {
409 let original_len = fields.len();
410 fields.retain(|(name, _)| name != field_name);
411 if fields.len() < original_len {
412 count += 1;
413 }
414 }
415 }
416
417 count += walk_expr_children_and_remove_field(expr, struct_name, field_name, self_ty)?;
419 Ok(count)
420}
421
422fn walk_expr_children_and_add_field(
423 expr: &mut PureExpr,
424 struct_name: &str,
425 field_name: &str,
426 value: &str,
427 self_ty: Option<&str>,
428) -> Result<usize, ToSynError> {
429 match expr {
430 PureExpr::Block { block, .. } => {
431 walk_and_add_field(block, struct_name, field_name, value, self_ty)
432 }
433 PureExpr::If {
434 cond,
435 then_branch,
436 else_branch,
437 } => {
438 let mut count = walk_expr_and_add_field(cond, struct_name, field_name, value, self_ty)?;
439 count += walk_and_add_field(then_branch, struct_name, field_name, value, self_ty)?;
440 if let Some(else_expr) = else_branch {
441 count +=
442 walk_expr_and_add_field(else_expr, struct_name, field_name, value, self_ty)?;
443 }
444 Ok(count)
445 }
446 PureExpr::Match { expr: e, arms } => {
447 let mut count = walk_expr_and_add_field(e, struct_name, field_name, value, self_ty)?;
448 for arm in arms {
449 count += walk_expr_and_add_field(
450 &mut arm.body,
451 struct_name,
452 field_name,
453 value,
454 self_ty,
455 )?;
456 }
457 Ok(count)
458 }
459 PureExpr::Loop { body: block, .. } | PureExpr::Unsafe(block) => {
460 walk_and_add_field(block, struct_name, field_name, value, self_ty)
461 }
462 PureExpr::While { cond, body, .. } => {
463 Ok(
464 walk_expr_and_add_field(cond, struct_name, field_name, value, self_ty)?
465 + walk_and_add_field(body, struct_name, field_name, value, self_ty)?,
466 )
467 }
468 PureExpr::For { expr: e, body, .. } => {
469 Ok(
470 walk_expr_and_add_field(e, struct_name, field_name, value, self_ty)?
471 + walk_and_add_field(body, struct_name, field_name, value, self_ty)?,
472 )
473 }
474 PureExpr::Async { body, .. } => {
475 walk_and_add_field(body, struct_name, field_name, value, self_ty)
476 }
477 PureExpr::Closure { body, .. } => {
478 walk_expr_and_add_field(body, struct_name, field_name, value, self_ty)
479 }
480 PureExpr::Call { func, args } => {
481 let mut count = walk_expr_and_add_field(func, struct_name, field_name, value, self_ty)?;
482 for arg in args {
483 count += walk_expr_and_add_field(arg, struct_name, field_name, value, self_ty)?;
484 }
485 Ok(count)
486 }
487 PureExpr::MethodCall { receiver, args, .. } => {
488 let mut count =
489 walk_expr_and_add_field(receiver, struct_name, field_name, value, self_ty)?;
490 for arg in args {
491 count += walk_expr_and_add_field(arg, struct_name, field_name, value, self_ty)?;
492 }
493 Ok(count)
494 }
495 PureExpr::Binary { left, right, .. } => {
496 Ok(
497 walk_expr_and_add_field(left, struct_name, field_name, value, self_ty)?
498 + walk_expr_and_add_field(right, struct_name, field_name, value, self_ty)?,
499 )
500 }
501 PureExpr::Unary { expr: e, .. }
502 | PureExpr::Field { expr: e, .. }
503 | PureExpr::Await(e)
504 | PureExpr::Try(e) => walk_expr_and_add_field(e, struct_name, field_name, value, self_ty),
505 PureExpr::Index { expr: e, index } => {
506 Ok(
507 walk_expr_and_add_field(e, struct_name, field_name, value, self_ty)?
508 + walk_expr_and_add_field(index, struct_name, field_name, value, self_ty)?,
509 )
510 }
511 PureExpr::Tuple(exprs) | PureExpr::Array(exprs) => {
512 let mut count = 0;
513 for e in exprs {
514 count += walk_expr_and_add_field(e, struct_name, field_name, value, self_ty)?;
515 }
516 Ok(count)
517 }
518 PureExpr::Return(Some(e)) | PureExpr::Break { expr: Some(e), .. } => {
519 walk_expr_and_add_field(e, struct_name, field_name, value, self_ty)
520 }
521 PureExpr::Let { expr: e, .. }
522 | PureExpr::Cast { expr: e, .. }
523 | PureExpr::Ref { expr: e, .. } => {
524 walk_expr_and_add_field(e, struct_name, field_name, value, self_ty)
525 }
526 PureExpr::Struct { fields, .. } => {
527 let mut count = 0;
529 for (_, field_expr) in fields {
530 count +=
531 walk_expr_and_add_field(field_expr, struct_name, field_name, value, self_ty)?;
532 }
533 Ok(count)
534 }
535 PureExpr::Range { start, end, .. } => {
536 let mut count = 0;
537 if let Some(s) = start {
538 count += walk_expr_and_add_field(s, struct_name, field_name, value, self_ty)?;
539 }
540 if let Some(e) = end {
541 count += walk_expr_and_add_field(e, struct_name, field_name, value, self_ty)?;
542 }
543 Ok(count)
544 }
545 PureExpr::Repeat { expr: e, len } => {
546 Ok(
547 walk_expr_and_add_field(e, struct_name, field_name, value, self_ty)?
548 + walk_expr_and_add_field(len, struct_name, field_name, value, self_ty)?,
549 )
550 }
551 PureExpr::Macro { name, tokens, .. } => {
552 if let Some(mut exprs) = macro_utils::try_extract_exprs(name, tokens) {
554 let mut count = 0;
555 for e in &mut exprs {
556 count += walk_expr_and_add_field(e, struct_name, field_name, value, self_ty)?;
557 }
558 if count > 0 {
559 *tokens = macro_utils::exprs_to_tokens(&exprs)?;
560 }
561 Ok(count)
562 } else {
563 Ok(0)
564 }
565 }
566 _ => Ok(0),
567 }
568}
569
570fn walk_expr_children_and_remove_field(
571 expr: &mut PureExpr,
572 struct_name: &str,
573 field_name: &str,
574 self_ty: Option<&str>,
575) -> Result<usize, ToSynError> {
576 match expr {
577 PureExpr::Block { block, .. } => {
578 walk_and_remove_field(block, struct_name, field_name, self_ty)
579 }
580 PureExpr::If {
581 cond,
582 then_branch,
583 else_branch,
584 } => {
585 let mut count = walk_expr_and_remove_field(cond, struct_name, field_name, self_ty)?;
586 count += walk_and_remove_field(then_branch, struct_name, field_name, self_ty)?;
587 if let Some(else_expr) = else_branch {
588 count += walk_expr_and_remove_field(else_expr, struct_name, field_name, self_ty)?;
589 }
590 Ok(count)
591 }
592 PureExpr::Match { expr: e, arms } => {
593 let mut count = walk_expr_and_remove_field(e, struct_name, field_name, self_ty)?;
594 for arm in arms {
595 count +=
596 walk_expr_and_remove_field(&mut arm.body, struct_name, field_name, self_ty)?;
597 }
598 Ok(count)
599 }
600 PureExpr::Loop { body: block, .. } | PureExpr::Unsafe(block) => {
601 walk_and_remove_field(block, struct_name, field_name, self_ty)
602 }
603 PureExpr::While { cond, body, .. } => {
604 Ok(
605 walk_expr_and_remove_field(cond, struct_name, field_name, self_ty)?
606 + walk_and_remove_field(body, struct_name, field_name, self_ty)?,
607 )
608 }
609 PureExpr::For { expr: e, body, .. } => {
610 Ok(
611 walk_expr_and_remove_field(e, struct_name, field_name, self_ty)?
612 + walk_and_remove_field(body, struct_name, field_name, self_ty)?,
613 )
614 }
615 PureExpr::Async { body, .. } => {
616 walk_and_remove_field(body, struct_name, field_name, self_ty)
617 }
618 PureExpr::Closure { body, .. } => {
619 walk_expr_and_remove_field(body, struct_name, field_name, self_ty)
620 }
621 PureExpr::Call { func, args } => {
622 let mut count = walk_expr_and_remove_field(func, struct_name, field_name, self_ty)?;
623 for arg in args {
624 count += walk_expr_and_remove_field(arg, struct_name, field_name, self_ty)?;
625 }
626 Ok(count)
627 }
628 PureExpr::MethodCall { receiver, args, .. } => {
629 let mut count = walk_expr_and_remove_field(receiver, struct_name, field_name, self_ty)?;
630 for arg in args {
631 count += walk_expr_and_remove_field(arg, struct_name, field_name, self_ty)?;
632 }
633 Ok(count)
634 }
635 PureExpr::Binary { left, right, .. } => {
636 Ok(
637 walk_expr_and_remove_field(left, struct_name, field_name, self_ty)?
638 + walk_expr_and_remove_field(right, struct_name, field_name, self_ty)?,
639 )
640 }
641 PureExpr::Unary { expr: e, .. }
642 | PureExpr::Field { expr: e, .. }
643 | PureExpr::Await(e)
644 | PureExpr::Try(e) => walk_expr_and_remove_field(e, struct_name, field_name, self_ty),
645 PureExpr::Index { expr: e, index } => {
646 Ok(
647 walk_expr_and_remove_field(e, struct_name, field_name, self_ty)?
648 + walk_expr_and_remove_field(index, struct_name, field_name, self_ty)?,
649 )
650 }
651 PureExpr::Tuple(exprs) | PureExpr::Array(exprs) => {
652 let mut count = 0;
653 for e in exprs {
654 count += walk_expr_and_remove_field(e, struct_name, field_name, self_ty)?;
655 }
656 Ok(count)
657 }
658 PureExpr::Return(Some(e)) | PureExpr::Break { expr: Some(e), .. } => {
659 walk_expr_and_remove_field(e, struct_name, field_name, self_ty)
660 }
661 PureExpr::Let { expr: e, .. }
662 | PureExpr::Cast { expr: e, .. }
663 | PureExpr::Ref { expr: e, .. } => {
664 walk_expr_and_remove_field(e, struct_name, field_name, self_ty)
665 }
666 PureExpr::Struct { fields, .. } => {
667 let mut count = 0;
669 for (_, field_expr) in fields {
670 count += walk_expr_and_remove_field(field_expr, struct_name, field_name, self_ty)?;
671 }
672 Ok(count)
673 }
674 PureExpr::Range { start, end, .. } => {
675 let mut count = 0;
676 if let Some(s) = start {
677 count += walk_expr_and_remove_field(s, struct_name, field_name, self_ty)?;
678 }
679 if let Some(e) = end {
680 count += walk_expr_and_remove_field(e, struct_name, field_name, self_ty)?;
681 }
682 Ok(count)
683 }
684 PureExpr::Repeat { expr: e, len } => {
685 Ok(
686 walk_expr_and_remove_field(e, struct_name, field_name, self_ty)?
687 + walk_expr_and_remove_field(len, struct_name, field_name, self_ty)?,
688 )
689 }
690 PureExpr::Macro { name, tokens, .. } => {
691 if let Some(mut exprs) = macro_utils::try_extract_exprs(name, tokens) {
693 let mut count = 0;
694 for e in &mut exprs {
695 count += walk_expr_and_remove_field(e, struct_name, field_name, self_ty)?;
696 }
697 if count > 0 {
698 *tokens = macro_utils::exprs_to_tokens(&exprs)?;
699 }
700 Ok(count)
701 } else {
702 Ok(0)
703 }
704 }
705 _ => Ok(0),
706 }
707}