1use anyhow::{Context, Result};
2use proc_macro2::{LineColumn, Span};
3use syn::{
4 parse_str, File, Item, ItemEnum, ItemStruct,
5 Fields, Field, spanned::Spanned, Arm, ExprMatch, ExprStruct,
6 visit_mut::VisitMut, Expr,
7};
8use quote::ToTokens;
9
10use crate::operations::*;
11use prettyplease;
12
13pub struct RustEditor {
14 content: String,
15 syntax_tree: File,
16 line_offsets: Vec<usize>, }
18
19impl RustEditor {
20 pub fn new(content: &str) -> Result<Self> {
21 let syntax_tree: File = syn::parse_str(content)
22 .context("Failed to parse Rust code")?;
23
24 let line_offsets = Self::compute_line_offsets(content);
25
26 Ok(Self {
27 content: content.to_string(),
28 syntax_tree,
29 line_offsets,
30 })
31 }
32
33 fn format_field(field: &Field) -> String {
35 let mut result = String::new();
36
37 if let syn::Visibility::Public(_) = field.vis {
39 result.push_str("pub ");
40 }
41
42 if let Some(ident) = &field.ident {
44 result.push_str(&ident.to_string());
45 }
46
47 result.push_str(": ");
49
50 let type_str = field.ty.to_token_stream().to_string();
52 let type_str = type_str.replace(" < ", "<").replace(" >", ">");
53 result.push_str(&type_str);
54
55 result
56 }
57
58 fn compute_line_offsets(content: &str) -> Vec<usize> {
59 let mut offsets = vec![0];
60 for (i, ch) in content.char_indices() {
61 if ch == '\n' {
62 offsets.push(i + 1);
63 }
64 }
65 offsets
66 }
67
68 pub fn apply_operation(&mut self, op: &Operation) -> Result<ModificationResult> {
69 match op {
70 Operation::AddStructField(op) => self.add_struct_field(op),
71 Operation::UpdateStructField(op) => self.update_struct_field(op),
72 Operation::RemoveStructField(op) => self.remove_struct_field(op),
73 Operation::AddStructLiteralField(op) => self.add_struct_literal_field(op),
74 Operation::AddEnumVariant(op) => self.add_enum_variant(op),
75 Operation::UpdateEnumVariant(op) => self.update_enum_variant(op),
76 Operation::RemoveEnumVariant(op) => self.remove_enum_variant(op),
77 Operation::AddMatchArm(op) => self.add_match_arm(op),
78 Operation::UpdateMatchArm(op) => self.update_match_arm(op),
79 Operation::RemoveMatchArm(op) => self.remove_match_arm(op),
80 Operation::AddImplMethod(op) => self.add_impl_method(op),
81 Operation::AddUseStatement(op) => self.add_use_statement(op),
82 Operation::AddDerive(op) => self.add_derive(op),
83 }
84 }
85
86 pub(crate) fn add_struct_field(&mut self, op: &AddStructFieldOp) -> Result<ModificationResult> {
87 let item_struct = self.syntax_tree.items.iter()
89 .find_map(|item| {
90 if let Item::Struct(s) = item {
91 if s.ident == op.struct_name {
92 return Some(s.clone());
93 }
94 }
95 None
96 })
97 .ok_or_else(|| anyhow::anyhow!("Struct '{}' not found", op.struct_name))?;
98
99 if let Some(ref where_filter) = op.where_filter {
101 if !self.matches_where_filter(&item_struct.attrs, where_filter)? {
102 return Ok(ModificationResult {
104 changed: false,
105 modified_nodes: vec![],
106 });
107 }
108 }
109
110 let backup_node = BackupNode {
112 node_type: "ItemStruct".to_string(),
113 identifier: op.struct_name.clone(),
114 original_content: self.unparse_item(&Item::Struct(item_struct.clone())),
115 location: self.span_to_location(item_struct.span()),
116 };
117
118 let modified = self.insert_struct_field(&item_struct, op)
120 .context("Failed to add field to struct definition")?;
121
122 if !modified {
123 return Ok(ModificationResult {
124 changed: false,
125 modified_nodes: vec![],
126 });
127 }
128
129 let mut modified_nodes = vec![backup_node];
130
131 if let Some(ref literal_default) = op.literal_default {
133 self.syntax_tree = syn::parse_str(&self.content)
135 .context("Failed to re-parse content after adding struct field")?;
136 self.line_offsets = Self::compute_line_offsets(&self.content);
137
138 let field_name = op.field_def.split(':')
140 .next()
141 .map(|s| s.trim().to_string())
142 .context("Failed to extract field name from field definition")?;
143
144 let literal_op = AddStructLiteralFieldOp {
146 struct_name: op.struct_name.clone(),
147 field_def: format!("{}: {}", field_name, literal_default),
148 position: op.position.clone(),
149 };
150
151 let literal_result = self.add_struct_literal_field(&literal_op)
153 .context("Failed to update struct literals")?;
154 modified_nodes.extend(literal_result.modified_nodes);
155 }
156
157 Ok(ModificationResult {
158 changed: true,
159 modified_nodes,
160 })
161 }
162
163 fn insert_struct_field(&mut self, item_struct: &ItemStruct, op: &AddStructFieldOp) -> Result<bool> {
164 if let Fields::Named(ref fields) = item_struct.fields {
165 let field_code = format!("struct Dummy {{ {} }}", op.field_def);
167 let dummy: ItemStruct = parse_str(&field_code)
168 .context("Failed to parse field definition")?;
169
170 let new_field = if let Fields::Named(ref nf) = dummy.fields {
171 nf.named.first()
172 .context("No field found in definition")?
173 .clone()
174 } else {
175 anyhow::bail!("Expected named field");
176 };
177
178 let new_field_name = new_field.ident.as_ref()
180 .map(|i| i.to_string())
181 .context("Field must have a name")?;
182
183 if fields.named.iter().any(|f| {
184 f.ident.as_ref().map(|i| i.to_string()) == Some(new_field_name.clone())
185 }) {
186 return Ok(false);
188 }
189
190 let insert_pos = match &op.position {
192 InsertPosition::First => {
193 if let Some(first_field) = fields.named.first() {
194 self.span_to_byte_offset(first_field.span().start())
195 } else {
196 let brace_pos = self.span_to_byte_offset(fields.brace_token.span.join().start());
198 brace_pos + 1
199 }
200 }
201 InsertPosition::Last => {
202 if let Some(last_field) = fields.named.last() {
203 let end = self.span_to_byte_offset(last_field.span().end());
204 self.find_after_field_end(end)
206 } else {
207 let brace_pos = self.span_to_byte_offset(fields.brace_token.span.join().start());
209 brace_pos + 1
210 }
211 }
212 InsertPosition::After(name) => {
213 let field = fields.named.iter()
214 .find(|f| f.ident.as_ref().map(|i| i.to_string()) == Some(name.clone()))
215 .with_context(|| format!("Field '{}' not found", name))?;
216 let end = self.span_to_byte_offset(field.span().end());
217 self.find_after_field_end(end)
218 }
219 InsertPosition::Before(name) => {
220 let field = fields.named.iter()
221 .find(|f| f.ident.as_ref().map(|i| i.to_string()) == Some(name.clone()))
222 .with_context(|| format!("Field '{}' not found", name))?;
223 self.span_to_byte_offset(field.span().start())
224 }
225 };
226
227 let indent = self.get_indentation(insert_pos);
229 let field_str = Self::format_field(&new_field);
230 let insert_text = if matches!(op.position, InsertPosition::First) {
231 format!("\n{}{},", indent, field_str)
232 } else {
233 format!("\n{}{},", indent, field_str)
234 };
235
236 self.content.insert_str(insert_pos, &insert_text);
237 return Ok(true);
238 }
239
240 anyhow::bail!("Struct '{}' does not have named fields", op.struct_name)
241 }
242
243 pub(crate) fn update_struct_field(&mut self, op: &UpdateStructFieldOp) -> Result<ModificationResult> {
244 let item_struct = self.syntax_tree.items.iter()
246 .find_map(|item| {
247 if let Item::Struct(s) = item {
248 if s.ident == op.struct_name {
249 return Some(s.clone());
250 }
251 }
252 None
253 })
254 .ok_or_else(|| anyhow::anyhow!("Struct '{}' not found", op.struct_name))?;
255
256 if let Some(ref where_filter) = op.where_filter {
258 if !self.matches_where_filter(&item_struct.attrs, where_filter)? {
259 return Ok(ModificationResult {
261 changed: false,
262 modified_nodes: vec![],
263 });
264 }
265 }
266
267 let backup_node = BackupNode {
269 node_type: "ItemStruct".to_string(),
270 identifier: op.struct_name.clone(),
271 original_content: self.unparse_item(&Item::Struct(item_struct.clone())),
272 location: self.span_to_location(item_struct.span()),
273 };
274
275 let modified = self.replace_struct_field(&item_struct, op)?;
276
277 Ok(ModificationResult {
278 changed: modified,
279 modified_nodes: if modified { vec![backup_node] } else { vec![] },
280 })
281 }
282
283 fn replace_struct_field(&mut self, item_struct: &ItemStruct, op: &UpdateStructFieldOp) -> Result<bool> {
284 if let Fields::Named(ref fields) = item_struct.fields {
285 let field_code = format!("struct Dummy {{ {} }}", op.field_def);
287 let dummy: ItemStruct = parse_str(&field_code)
288 .context("Failed to parse field definition")?;
289
290 let new_field = if let Fields::Named(ref nf) = dummy.fields {
291 nf.named.first()
292 .context("No field found in definition")?
293 .clone()
294 } else {
295 anyhow::bail!("Expected named field");
296 };
297
298 let field_name = new_field.ident.as_ref()
300 .map(|i| i.to_string())
301 .context("Field must have a name")?;
302
303 let existing_field = fields.named.iter()
305 .find(|f| f.ident.as_ref().map(|i| i.to_string()) == Some(field_name.clone()))
306 .ok_or_else(|| anyhow::anyhow!("Field '{}' not found in struct '{}'", field_name, op.struct_name))?;
307
308 let start = self.span_to_byte_offset(existing_field.span().start());
310 let end = self.span_to_byte_offset(existing_field.span().end());
311
312 let new_field_str = Self::format_field(&new_field);
314
315 self.content.replace_range(start..end, &new_field_str);
317
318 return Ok(true);
319 }
320
321 anyhow::bail!("Struct '{}' does not have named fields", op.struct_name)
322 }
323
324 pub(crate) fn remove_struct_field(&mut self, op: &RemoveStructFieldOp) -> Result<ModificationResult> {
325 let item_struct = self.syntax_tree.items.iter()
327 .find_map(|item| {
328 if let Item::Struct(s) = item {
329 if s.ident == op.struct_name {
330 return Some(s.clone());
331 }
332 }
333 None
334 })
335 .ok_or_else(|| anyhow::anyhow!("Struct '{}' not found", op.struct_name))?;
336
337 if let Some(ref where_filter) = op.where_filter {
339 if !self.matches_where_filter(&item_struct.attrs, where_filter)? {
340 return Ok(ModificationResult {
342 changed: false,
343 modified_nodes: vec![],
344 });
345 }
346 }
347
348 let backup_node = BackupNode {
350 node_type: "ItemStruct".to_string(),
351 identifier: op.struct_name.clone(),
352 original_content: self.unparse_item(&Item::Struct(item_struct.clone())),
353 location: self.span_to_location(item_struct.span()),
354 };
355
356 if let Fields::Named(ref fields) = item_struct.fields {
357 let field_to_remove = fields.named.iter()
359 .find(|f| f.ident.as_ref().map(|i| i.to_string()) == Some(op.field_name.clone()))
360 .ok_or_else(|| anyhow::anyhow!("Field '{}' not found in struct '{}'", op.field_name, op.struct_name))?;
361
362 let start = self.span_to_byte_offset(field_to_remove.span().start());
364 let mut end = self.span_to_byte_offset(field_to_remove.span().end());
365
366 while end < self.content.len() {
368 match self.content.as_bytes()[end] as char {
369 ',' => {
370 end += 1;
371 if end < self.content.len() && self.content.as_bytes()[end] == b'\n' {
373 end += 1;
374 }
375 break;
376 }
377 ' ' | '\t' => end += 1,
378 '\n' => {
379 end += 1;
380 break;
381 }
382 _ => break,
383 }
384 }
385
386 let mut line_start = start;
388 while line_start > 0 && self.content.as_bytes()[line_start - 1] != b'\n' {
389 line_start -= 1;
390 }
391
392 let before_field = &self.content[line_start..start];
394 if before_field.trim().is_empty() {
395 self.content.replace_range(line_start..end, "");
397 } else {
398 self.content.replace_range(start..end, "");
400 }
401
402 return Ok(ModificationResult {
403 changed: true,
404 modified_nodes: vec![backup_node],
405 });
406 }
407
408 anyhow::bail!("Struct '{}' does not have named fields", op.struct_name)
409 }
410
411 pub(crate) fn add_struct_literal_field(&mut self, op: &AddStructLiteralFieldOp) -> Result<ModificationResult> {
412 let field_name = op.field_def.split(':')
414 .next()
415 .map(|s| s.trim().to_string())
416 .context("Field definition must contain ':'")?;
417
418 let backup_nodes = self.collect_struct_literal_backups(&op.struct_name);
420
421 let mut visitor = StructLiteralFieldAdder {
423 struct_name: op.struct_name.clone(),
424 field_def: op.field_def.clone(),
425 field_name,
426 position: op.position.clone(),
427 modified: false,
428 };
429
430 visitor.visit_file_mut(&mut self.syntax_tree);
431
432 if visitor.modified {
433 self.content = prettyplease::unparse(&self.syntax_tree);
435 Ok(ModificationResult {
436 changed: true,
437 modified_nodes: backup_nodes,
438 })
439 } else {
440 Ok(ModificationResult {
441 changed: false,
442 modified_nodes: vec![],
443 })
444 }
445 }
446
447 fn collect_struct_literal_backups(&self, struct_name: &str) -> Vec<BackupNode> {
449 use syn::visit::Visit;
450
451 struct LiteralCollector {
452 struct_name: String,
453 backups: Vec<BackupNode>,
454 counter: usize,
455 }
456
457 impl<'ast> Visit<'ast> for LiteralCollector {
458 fn visit_expr(&mut self, node: &'ast Expr) {
459 if let Expr::Struct(expr_struct) = node {
460 if let Some(last_seg) = expr_struct.path.segments.last() {
461 if last_seg.ident.to_string() == self.struct_name {
462 self.backups.push(BackupNode {
463 node_type: "ExprStruct".to_string(),
464 identifier: format!("{}#{}", self.struct_name, self.counter),
465 original_content: expr_struct.to_token_stream().to_string(),
466 location: NodeLocation {
467 line: 0, column: 0,
469 end_line: 0,
470 end_column: 0,
471 },
472 });
473 self.counter += 1;
474 }
475 }
476 }
477 syn::visit::visit_expr(self, node);
478 }
479 }
480
481 let mut collector = LiteralCollector {
482 struct_name: struct_name.to_string(),
483 backups: Vec::new(),
484 counter: 0,
485 };
486
487 collector.visit_file(&self.syntax_tree);
488 collector.backups
489 }
490
491 pub(crate) fn add_enum_variant(&mut self, op: &AddEnumVariantOp) -> Result<ModificationResult> {
492 let item_enum = self.syntax_tree.items.iter()
494 .find_map(|item| {
495 if let Item::Enum(e) = item {
496 if e.ident == op.enum_name {
497 return Some(e.clone());
498 }
499 }
500 None
501 })
502 .ok_or_else(|| anyhow::anyhow!("Enum '{}' not found", op.enum_name))?;
503
504 if let Some(ref where_filter) = op.where_filter {
506 if !self.matches_where_filter(&item_enum.attrs, where_filter)? {
507 return Ok(ModificationResult {
509 changed: false,
510 modified_nodes: vec![],
511 });
512 }
513 }
514
515 let backup_node = BackupNode {
517 node_type: "ItemEnum".to_string(),
518 identifier: op.enum_name.clone(),
519 original_content: self.unparse_item(&Item::Enum(item_enum.clone())),
520 location: self.span_to_location(item_enum.span()),
521 };
522
523 let modified = self.insert_enum_variant(&item_enum, op)?;
524
525 Ok(ModificationResult {
526 changed: modified,
527 modified_nodes: if modified { vec![backup_node] } else { vec![] },
528 })
529 }
530
531 fn insert_enum_variant(&mut self, item_enum: &ItemEnum, op: &AddEnumVariantOp) -> Result<bool> {
532 let variant_code = format!("enum Dummy {{ {} }}", op.variant_def);
534 let dummy: ItemEnum = parse_str(&variant_code)
535 .context("Failed to parse variant definition")?;
536
537 let new_variant = dummy.variants.first()
538 .context("No variant found in definition")?
539 .clone();
540
541 let variant_name = new_variant.ident.to_string();
543 if item_enum.variants.iter().any(|v| v.ident.to_string() == variant_name) {
544 return Ok(false);
546 }
547
548 let insert_pos = match &op.position {
550 InsertPosition::First => {
551 if let Some(first_var) = item_enum.variants.first() {
552 self.span_to_byte_offset(first_var.span().start())
553 } else {
554 let brace_pos = self.span_to_byte_offset(item_enum.brace_token.span.join().start());
555 brace_pos + 1
556 }
557 }
558 InsertPosition::Last => {
559 if let Some(last_var) = item_enum.variants.last() {
560 let end = self.span_to_byte_offset(last_var.span().end());
561 self.find_after_field_end(end)
562 } else {
563 let brace_pos = self.span_to_byte_offset(item_enum.brace_token.span.join().start());
564 brace_pos + 1
565 }
566 }
567 InsertPosition::After(name) => {
568 let variant = item_enum.variants.iter()
569 .find(|v| v.ident.to_string() == *name)
570 .with_context(|| format!("Variant '{}' not found", name))?;
571 let end = self.span_to_byte_offset(variant.span().end());
572 self.find_after_field_end(end)
573 }
574 InsertPosition::Before(name) => {
575 let variant = item_enum.variants.iter()
576 .find(|v| v.ident.to_string() == *name)
577 .with_context(|| format!("Variant '{}' not found", name))?;
578 self.span_to_byte_offset(variant.span().start())
579 }
580 };
581
582 let indent = self.get_indentation(insert_pos);
583 let variant_str = new_variant.to_token_stream().to_string();
584 let insert_text = format!("\n{}{},", indent, variant_str);
585
586 self.content.insert_str(insert_pos, &insert_text);
587 Ok(true)
588 }
589
590 fn update_enum_variant(&mut self, op: &UpdateEnumVariantOp) -> Result<ModificationResult> {
591 let item_enum = self.syntax_tree.items.iter()
593 .find_map(|item| {
594 if let Item::Enum(e) = item {
595 if e.ident == op.enum_name {
596 return Some(e.clone());
597 }
598 }
599 None
600 })
601 .ok_or_else(|| anyhow::anyhow!("Enum '{}' not found", op.enum_name))?;
602
603 if let Some(ref where_filter) = op.where_filter {
605 if !self.matches_where_filter(&item_enum.attrs, where_filter)? {
606 return Ok(ModificationResult {
608 changed: false,
609 modified_nodes: vec![],
610 });
611 }
612 }
613
614 let backup_node = BackupNode {
616 node_type: "ItemEnum".to_string(),
617 identifier: op.enum_name.clone(),
618 original_content: self.unparse_item(&Item::Enum(item_enum.clone())),
619 location: self.span_to_location(item_enum.span()),
620 };
621
622 let variant_code = format!("enum Dummy {{ {} }}", op.variant_def);
624 let dummy: ItemEnum = parse_str(&variant_code)
625 .context("Failed to parse variant definition")?;
626
627 let new_variant = dummy.variants.first()
628 .context("No variant found in definition")?
629 .clone();
630
631 let variant_name = new_variant.ident.to_string();
632
633 let existing_variant = item_enum.variants.iter()
635 .find(|v| v.ident.to_string() == variant_name)
636 .ok_or_else(|| anyhow::anyhow!("Variant '{}' not found in enum '{}'", variant_name, op.enum_name))?;
637
638 let start = self.span_to_byte_offset(existing_variant.span().start());
640 let end = self.span_to_byte_offset(existing_variant.span().end());
641
642 let variant_str = new_variant.to_token_stream().to_string();
644 self.content.replace_range(start..end, &variant_str);
645
646 Ok(ModificationResult {
647 changed: true,
648 modified_nodes: vec![backup_node],
649 })
650 }
651
652 pub(crate) fn remove_enum_variant(&mut self, op: &RemoveEnumVariantOp) -> Result<ModificationResult> {
653 let item_enum = self.syntax_tree.items.iter()
655 .find_map(|item| {
656 if let Item::Enum(e) = item {
657 if e.ident == op.enum_name {
658 return Some(e.clone());
659 }
660 }
661 None
662 })
663 .ok_or_else(|| anyhow::anyhow!("Enum '{}' not found", op.enum_name))?;
664
665 if let Some(ref where_filter) = op.where_filter {
667 if !self.matches_where_filter(&item_enum.attrs, where_filter)? {
668 return Ok(ModificationResult {
670 changed: false,
671 modified_nodes: vec![],
672 });
673 }
674 }
675
676 let backup_node = BackupNode {
678 node_type: "ItemEnum".to_string(),
679 identifier: op.enum_name.clone(),
680 original_content: self.unparse_item(&Item::Enum(item_enum.clone())),
681 location: self.span_to_location(item_enum.span()),
682 };
683
684 let variant_to_remove = item_enum.variants.iter()
686 .find(|v| v.ident.to_string() == op.variant_name)
687 .ok_or_else(|| anyhow::anyhow!("Variant '{}' not found in enum '{}'", op.variant_name, op.enum_name))?;
688
689 let start = self.span_to_byte_offset(variant_to_remove.span().start());
691 let mut end = self.span_to_byte_offset(variant_to_remove.span().end());
692
693 while end < self.content.len() {
695 match self.content.as_bytes()[end] as char {
696 ',' => {
697 end += 1;
698 if end < self.content.len() && self.content.as_bytes()[end] == b'\n' {
699 end += 1;
700 }
701 break;
702 }
703 ' ' | '\t' => end += 1,
704 '\n' => {
705 end += 1;
706 break;
707 }
708 _ => break,
709 }
710 }
711
712 let mut line_start = start;
714 while line_start > 0 && self.content.as_bytes()[line_start - 1] != b'\n' {
715 line_start -= 1;
716 }
717
718 let before_variant = &self.content[line_start..start];
719 if before_variant.trim().is_empty() {
720 self.content.replace_range(line_start..end, "");
721 } else {
722 self.content.replace_range(start..end, "");
723 }
724
725 Ok(ModificationResult {
726 changed: true,
727 modified_nodes: vec![backup_node],
728 })
729 }
730
731 pub(crate) fn add_match_arm(&mut self, op: &AddMatchArmOp) -> Result<ModificationResult> {
732 if op.auto_detect {
733 self.add_missing_match_arms(op)
735 } else {
736 self.add_single_match_arm(op)
738 }
739 }
740
741 fn add_single_match_arm(&mut self, op: &AddMatchArmOp) -> Result<ModificationResult> {
742 let dummy_match = format!("match () {{ {} => {}, }}", op.pattern, op.body);
744 let expr: syn::Expr = parse_str(&dummy_match)
745 .with_context(|| format!("Failed to parse pattern/body: {} => {}", op.pattern, op.body))?;
746
747 let arm = if let syn::Expr::Match(match_expr) = expr {
749 match_expr.arms.into_iter().next()
750 .context("Failed to extract arm from dummy match")?
751 } else {
752 anyhow::bail!("Expected match expression");
753 };
754
755 let backup_node = if let Some(ref fn_name) = op.function_name {
757 self.get_function_backup(fn_name)?
758 } else {
759 BackupNode {
762 node_type: "Unknown".to_string(),
763 identifier: "match_expression".to_string(),
764 original_content: String::new(),
765 location: NodeLocation {
766 line: 0,
767 column: 0,
768 end_line: 0,
769 end_column: 0,
770 },
771 }
772 };
773
774 let mut visitor = MatchArmAdder {
776 target_function: op.function_name.clone(),
777 arm_to_add: arm,
778 modified: false,
779 current_function: None,
780 modified_function: None,
781 };
782
783 visitor.visit_file_mut(&mut self.syntax_tree);
784
785 if visitor.modified {
786 self.replace_modified_functions(&visitor.modified_function)?;
788 Ok(ModificationResult {
789 changed: true,
790 modified_nodes: vec![backup_node],
791 })
792 } else {
793 Ok(ModificationResult {
794 changed: false,
795 modified_nodes: vec![],
796 })
797 }
798 }
799
800 fn unparse_item(&self, item: &Item) -> String {
802 let temp_file = syn::File {
803 shebang: None,
804 attrs: Vec::new(),
805 items: vec![item.clone()],
806 };
807 prettyplease::unparse(&temp_file).trim().to_string()
808 }
809
810 fn get_function_backup(&self, fn_name: &str) -> Result<BackupNode> {
812 for item in &self.syntax_tree.items {
813 if let Item::Fn(f) = item {
814 if f.sig.ident == fn_name {
815 return Ok(BackupNode {
816 node_type: "ItemFn".to_string(),
817 identifier: fn_name.to_string(),
818 original_content: self.unparse_item(&Item::Fn(f.clone())),
819 location: self.span_to_location(f.span()),
820 });
821 }
822 }
823 }
824 anyhow::bail!("Function '{}' not found", fn_name)
825 }
826
827 fn add_missing_match_arms(&mut self, op: &AddMatchArmOp) -> Result<ModificationResult> {
828 let enum_name = op.enum_name.as_ref()
830 .ok_or_else(|| anyhow::anyhow!("enum_name is required for auto-detect"))?;
831
832 let enum_variants = self.find_enum_variants(enum_name)?;
834
835 if enum_variants.is_empty() {
836 anyhow::bail!("Enum '{}' not found or has no variants", enum_name);
837 }
838
839 let existing_patterns = self.find_existing_match_patterns(&op.function_name);
841
842 let mut missing_variants = Vec::new();
844 for variant in &enum_variants {
845 let pattern = format!("{}::{}", enum_name, variant);
846 let pattern_normalized = pattern.replace(" ", "");
847
848 let exists = existing_patterns.iter().any(|p| {
849 p.replace(" ", "") == pattern_normalized
850 });
851
852 if !exists {
853 missing_variants.push(variant.clone());
854 }
855 }
856
857 if missing_variants.is_empty() {
858 println!("All enum variants already covered in match expressions");
859 return Ok(ModificationResult {
860 changed: false,
861 modified_nodes: vec![],
862 });
863 }
864
865 let backup_node = if let Some(ref fn_name) = op.function_name {
867 self.get_function_backup(fn_name)?
868 } else {
869 BackupNode {
870 node_type: "Unknown".to_string(),
871 identifier: "match_expression".to_string(),
872 original_content: String::new(),
873 location: NodeLocation {
874 line: 0,
875 column: 0,
876 end_line: 0,
877 end_column: 0,
878 },
879 }
880 };
881
882 let mut arms_to_add = Vec::new();
884 for variant in &missing_variants {
885 let pattern = format!("{}::{}", enum_name, variant);
886 let dummy_match = format!("match () {{ {} => {}, }}", pattern, op.body);
887 let expr: syn::Expr = parse_str(&dummy_match)
888 .with_context(|| format!("Failed to parse pattern/body: {} => {}", pattern, op.body))?;
889
890 if let syn::Expr::Match(match_expr) = expr {
891 if let Some(arm) = match_expr.arms.into_iter().next() {
892 arms_to_add.push((pattern.clone(), arm));
893 }
894 }
895 }
896
897 let mut visitor = MultiMatchArmAdder {
899 target_function: op.function_name.clone(),
900 arms_to_add,
901 modified: false,
902 current_function: None,
903 modified_function: None,
904 };
905
906 visitor.visit_file_mut(&mut self.syntax_tree);
907
908 if visitor.modified {
909 for variant in &missing_variants {
911 println!("Added match arm for: {}::{}", enum_name, variant);
912 }
913
914 self.replace_modified_functions(&visitor.modified_function)?;
916 Ok(ModificationResult {
917 changed: true,
918 modified_nodes: vec![backup_node],
919 })
920 } else {
921 Ok(ModificationResult {
922 changed: false,
923 modified_nodes: vec![],
924 })
925 }
926 }
927
928 fn find_enum_variants(&self, enum_name: &str) -> Result<Vec<String>> {
929 for item in &self.syntax_tree.items {
931 if let Item::Enum(e) = item {
932 if e.ident == enum_name {
933 let variants: Vec<String> = e.variants.iter()
934 .map(|v| v.ident.to_string())
935 .collect();
936 return Ok(variants);
937 }
938 }
939 }
940
941 Ok(Vec::new())
942 }
943
944 fn find_existing_match_patterns(&self, function_name: &Option<String>) -> Vec<String> {
945 use syn::visit::Visit;
946
947 struct PatternCollector {
948 target_function: Option<String>,
949 current_function: Option<String>,
950 patterns: Vec<String>,
951 }
952
953 impl<'ast> Visit<'ast> for PatternCollector {
954 fn visit_item_fn(&mut self, node: &'ast syn::ItemFn) {
955 let prev_fn = self.current_function.clone();
956 self.current_function = Some(node.sig.ident.to_string());
957 syn::visit::visit_item_fn(self, node);
958 self.current_function = prev_fn;
959 }
960
961 fn visit_expr_match(&mut self, node: &'ast ExprMatch) {
962 if let Some(ref target) = self.target_function {
964 if self.current_function.as_ref() != Some(target) {
965 syn::visit::visit_expr_match(self, node);
966 return;
967 }
968 }
969
970 for arm in &node.arms {
972 self.patterns.push(arm.pat.to_token_stream().to_string());
973 }
974
975 syn::visit::visit_expr_match(self, node);
976 }
977 }
978
979 let mut collector = PatternCollector {
980 target_function: function_name.clone(),
981 current_function: None,
982 patterns: Vec::new(),
983 };
984
985 collector.visit_file(&self.syntax_tree);
986 collector.patterns
987 }
988
989 pub(crate) fn update_match_arm(&mut self, op: &UpdateMatchArmOp) -> Result<ModificationResult> {
990 let backup_node = if let Some(ref fn_name) = op.function_name {
992 self.get_function_backup(fn_name)?
993 } else {
994 BackupNode {
995 node_type: "Unknown".to_string(),
996 identifier: "match_expression".to_string(),
997 original_content: String::new(),
998 location: NodeLocation {
999 line: 0,
1000 column: 0,
1001 end_line: 0,
1002 end_column: 0,
1003 },
1004 }
1005 };
1006
1007 let new_body: syn::Expr = parse_str(&op.new_body)
1009 .with_context(|| format!("Failed to parse new body: {}", op.new_body))?;
1010
1011 let mut visitor = MatchArmUpdater {
1013 target_function: op.function_name.clone(),
1014 pattern_to_match: op.pattern.clone(),
1015 new_body,
1016 modified: false,
1017 current_function: None,
1018 modified_function: None,
1019 };
1020
1021 visitor.visit_file_mut(&mut self.syntax_tree);
1022
1023 if visitor.modified {
1024 self.replace_modified_functions(&visitor.modified_function)?;
1026 Ok(ModificationResult {
1027 changed: true,
1028 modified_nodes: vec![backup_node],
1029 })
1030 } else {
1031 anyhow::bail!("Pattern '{}' not found in any match expression", op.pattern)
1032 }
1033 }
1034
1035 pub(crate) fn remove_match_arm(&mut self, op: &RemoveMatchArmOp) -> Result<ModificationResult> {
1036 let backup_node = if let Some(ref fn_name) = op.function_name {
1038 self.get_function_backup(fn_name)?
1039 } else {
1040 BackupNode {
1041 node_type: "Unknown".to_string(),
1042 identifier: "match_expression".to_string(),
1043 original_content: String::new(),
1044 location: NodeLocation {
1045 line: 0,
1046 column: 0,
1047 end_line: 0,
1048 end_column: 0,
1049 },
1050 }
1051 };
1052
1053 let mut visitor = MatchArmRemover {
1055 target_function: op.function_name.clone(),
1056 pattern_to_remove: op.pattern.clone(),
1057 modified: false,
1058 current_function: None,
1059 modified_function: None,
1060 };
1061
1062 visitor.visit_file_mut(&mut self.syntax_tree);
1063
1064 if visitor.modified {
1065 self.replace_modified_functions(&visitor.modified_function)?;
1067 Ok(ModificationResult {
1068 changed: true,
1069 modified_nodes: vec![backup_node],
1070 })
1071 } else {
1072 anyhow::bail!("Pattern '{}' not found in any match expression", op.pattern)
1073 }
1074 }
1075
1076 pub(crate) fn add_impl_method(&mut self, op: &AddImplMethodOp) -> Result<ModificationResult> {
1077 let method_code = format!("impl Dummy {{ {} }}", op.method_def);
1079 let dummy: syn::ItemImpl = parse_str(&method_code)
1080 .context("Failed to parse method definition")?;
1081
1082 let new_method = dummy.items.first()
1083 .context("No method found in definition")?
1084 .clone();
1085
1086 let method_name = match &new_method {
1088 syn::ImplItem::Fn(f) => f.sig.ident.to_string(),
1089 _ => anyhow::bail!("Only method definitions are supported"),
1090 };
1091
1092 let impl_index = self.syntax_tree.items.iter().position(|item| {
1094 if let Item::Impl(impl_block) = item {
1095 if let syn::Type::Path(type_path) = &*impl_block.self_ty {
1097 if let Some(segment) = type_path.path.segments.last() {
1098 return segment.ident == op.target;
1099 }
1100 }
1101 }
1102 false
1103 }).ok_or_else(|| anyhow::anyhow!("impl block for '{}' not found", op.target))?;
1104
1105 let impl_block = match &self.syntax_tree.items[impl_index] {
1107 Item::Impl(i) => i,
1108 _ => unreachable!(),
1109 };
1110
1111 let method_exists = impl_block.items.iter().any(|item| {
1112 if let syn::ImplItem::Fn(f) = item {
1113 f.sig.ident == method_name
1114 } else {
1115 false
1116 }
1117 });
1118
1119 if method_exists {
1120 return Ok(ModificationResult {
1121 changed: false,
1122 modified_nodes: vec![],
1123 });
1124 }
1125
1126 let backup_node = BackupNode {
1128 node_type: "ItemImpl".to_string(),
1129 identifier: op.target.clone(),
1130 original_content: self.unparse_item(&self.syntax_tree.items[impl_index].clone()),
1131 location: self.span_to_location(impl_block.span()),
1132 };
1133
1134 let impl_span = impl_block.span();
1136
1137 match &mut self.syntax_tree.items[impl_index] {
1139 Item::Impl(impl_block) => {
1140 match &op.position {
1142 InsertPosition::First => {
1143 impl_block.items.insert(0, new_method);
1144 }
1145 InsertPosition::Last => {
1146 impl_block.items.push(new_method);
1147 }
1148 InsertPosition::After(name) => {
1149 let pos = impl_block.items.iter().position(|item| {
1150 if let syn::ImplItem::Fn(f) = item {
1151 f.sig.ident == name
1152 } else {
1153 false
1154 }
1155 }).with_context(|| format!("Method '{}' not found", name))?;
1156 impl_block.items.insert(pos + 1, new_method);
1157 }
1158 InsertPosition::Before(name) => {
1159 let pos = impl_block.items.iter().position(|item| {
1160 if let syn::ImplItem::Fn(f) = item {
1161 f.sig.ident == name
1162 } else {
1163 false
1164 }
1165 }).with_context(|| format!("Method '{}' not found", name))?;
1166 impl_block.items.insert(pos, new_method);
1167 }
1168 }
1169 }
1170 _ => unreachable!(),
1171 }
1172
1173 self.replace_formatted_item(impl_index, impl_span)?;
1175
1176 Ok(ModificationResult {
1177 changed: true,
1178 modified_nodes: vec![backup_node],
1179 })
1180 }
1181
1182 pub(crate) fn add_use_statement(&mut self, op: &AddUseStatementOp) -> Result<ModificationResult> {
1183 let use_code = format!("use {};", op.use_path);
1185 let use_item: syn::ItemUse = parse_str(&use_code)
1186 .context("Failed to parse use statement")?;
1187
1188 let use_exists = self.syntax_tree.items.iter().any(|item| {
1190 if let Item::Use(existing_use) = item {
1191 existing_use.tree.to_token_stream().to_string() ==
1193 use_item.tree.to_token_stream().to_string()
1194 } else {
1195 false
1196 }
1197 });
1198
1199 if use_exists {
1200 return Ok(ModificationResult {
1201 changed: false,
1202 modified_nodes: vec![],
1203 });
1204 }
1205
1206 let backup_node = BackupNode {
1208 node_type: "ItemUse".to_string(),
1209 identifier: op.use_path.clone(),
1210 original_content: format!("use {};", op.use_path),
1211 location: NodeLocation {
1212 line: 0,
1213 column: 0,
1214 end_line: 0,
1215 end_column: 0,
1216 },
1217 };
1218
1219 let insert_index = match &op.position {
1221 InsertPosition::First => 0,
1222 InsertPosition::Last => {
1223 self.syntax_tree.items.iter()
1225 .rposition(|item| matches!(item, Item::Use(_)))
1226 .map(|i| i + 1)
1227 .unwrap_or(0)
1228 }
1229 InsertPosition::After(path) => {
1230 let pos = self.syntax_tree.items.iter().position(|item| {
1232 if let Item::Use(u) = item {
1233 u.tree.to_token_stream().to_string().contains(path)
1234 } else {
1235 false
1236 }
1237 }).with_context(|| format!("Use statement for '{}' not found", path))?;
1238 pos + 1
1239 }
1240 InsertPosition::Before(path) => {
1241 self.syntax_tree.items.iter().position(|item| {
1243 if let Item::Use(u) = item {
1244 u.tree.to_token_stream().to_string().contains(path)
1245 } else {
1246 false
1247 }
1248 }).with_context(|| format!("Use statement for '{}' not found", path))?
1249 }
1250 };
1251
1252 self.syntax_tree.items.insert(insert_index, Item::Use(use_item));
1254
1255 let insert_line_pos = if insert_index == 0 {
1258 0
1260 } else {
1261 let prev_item = &self.syntax_tree.items[insert_index - 1];
1263 let span = prev_item.span();
1264 let end_pos = self.span_to_byte_offset(span.end());
1265
1266 let mut line_end = end_pos;
1268 while line_end < self.content.len() && self.content.as_bytes()[line_end] != b'\n' {
1269 line_end += 1;
1270 }
1271 if line_end < self.content.len() {
1273 line_end + 1
1274 } else {
1275 self.content.push('\n');
1277 self.content.len()
1278 }
1279 };
1280
1281 let use_str = format!("use {};\n", op.use_path);
1283
1284 self.content.insert_str(insert_line_pos, &use_str);
1286
1287 Ok(ModificationResult {
1288 changed: true,
1289 modified_nodes: vec![backup_node],
1290 })
1291 }
1292
1293 pub(crate) fn add_derive(&mut self, op: &AddDeriveOp) -> Result<ModificationResult> {
1294 let item_index = self.syntax_tree.items.iter().position(|item| {
1296 match (&op.target_type as &str, item) {
1297 ("struct", Item::Struct(s)) => s.ident == op.target_name,
1298 ("enum", Item::Enum(e)) => e.ident == op.target_name,
1299 _ => false,
1300 }
1301 }).ok_or_else(|| anyhow::anyhow!("{} '{}' not found", op.target_type, op.target_name))?;
1302
1303 let (existing_derives, item_span, item_attrs) = match &self.syntax_tree.items[item_index] {
1305 Item::Struct(s) => (Self::extract_derives(&s.attrs), s.span(), &s.attrs),
1306 Item::Enum(e) => (Self::extract_derives(&e.attrs), e.span(), &e.attrs),
1307 _ => (Vec::new(), proc_macro2::Span::call_site(), &Vec::new() as &Vec<syn::Attribute>),
1308 };
1309
1310 if let Some(ref where_filter) = op.where_filter {
1312 if !self.matches_where_filter(item_attrs, where_filter)? {
1313 return Ok(ModificationResult {
1315 changed: false,
1316 modified_nodes: vec![],
1317 });
1318 }
1319 }
1320
1321 let backup_node = BackupNode {
1323 node_type: if op.target_type == "struct" { "ItemStruct" } else { "ItemEnum" }.to_string(),
1324 identifier: op.target_name.clone(),
1325 original_content: self.unparse_item(&self.syntax_tree.items[item_index].clone()),
1326 location: self.span_to_location(item_span),
1327 };
1328
1329 let new_derives: Vec<String> = op.derives.iter()
1331 .filter(|d| !existing_derives.contains(&d.to_string()))
1332 .cloned()
1333 .collect();
1334
1335 if new_derives.is_empty() {
1336 return Ok(ModificationResult {
1338 changed: false,
1339 modified_nodes: vec![],
1340 });
1341 }
1342
1343 let mut all_derives = existing_derives;
1345 all_derives.extend(new_derives);
1346
1347 let all_derives_refs: Vec<&str> = all_derives.iter().map(|s| s.as_str()).collect();
1349
1350 match &mut self.syntax_tree.items[item_index] {
1352 Item::Struct(s) => {
1353 Self::update_derive_attr(&mut s.attrs, &all_derives_refs)?;
1354 }
1355 Item::Enum(e) => {
1356 Self::update_derive_attr(&mut e.attrs, &all_derives_refs)?;
1357 }
1358 _ => unreachable!(),
1359 }
1360
1361 self.replace_formatted_item(item_index, item_span)?;
1363
1364 Ok(ModificationResult {
1365 changed: true,
1366 modified_nodes: vec![backup_node],
1367 })
1368 }
1369
1370 fn replace_formatted_item(&mut self, item_index: usize, original_span: Span) -> Result<()> {
1372 let item_start_pos = self.span_to_byte_offset(original_span.start());
1374 let item_end_pos = self.span_to_byte_offset(original_span.end());
1375
1376 let mut actual_start = item_start_pos;
1378
1379 let mut temp_pos = item_start_pos;
1381 while temp_pos > 0 {
1382 temp_pos = temp_pos.saturating_sub(1);
1384 let mut line_start = temp_pos;
1385 while line_start > 0 && self.content.as_bytes()[line_start - 1] != b'\n' {
1386 line_start -= 1;
1387 }
1388
1389 let line = if temp_pos < self.content.len() {
1390 &self.content[line_start..temp_pos + 1]
1391 } else {
1392 &self.content[line_start..]
1393 };
1394 let trimmed = line.trim();
1395
1396 if trimmed.starts_with("#[") {
1397 actual_start = line_start;
1398 temp_pos = line_start;
1399 } else if trimmed.is_empty() {
1400 temp_pos = line_start;
1401 } else {
1402 break;
1403 }
1404
1405 if line_start == 0 {
1406 break;
1407 }
1408 }
1409
1410 let item_clone = self.syntax_tree.items[item_index].clone();
1412 let temp_file = syn::File {
1413 shebang: None,
1414 attrs: Vec::new(),
1415 items: vec![item_clone],
1416 };
1417
1418 let formatted = prettyplease::unparse(&temp_file);
1420 let formatted = formatted.trim();
1421
1422 self.content.replace_range(actual_start..item_end_pos, formatted);
1424
1425 Ok(())
1426 }
1427
1428 fn extract_derives(attrs: &[syn::Attribute]) -> Vec<String> {
1430 for attr in attrs {
1431 if attr.path().is_ident("derive") {
1432 if let Ok(syn::Meta::List(meta_list)) = attr.meta.clone().try_into() {
1433 let tokens_str = meta_list.tokens.to_string();
1434 return tokens_str
1435 .split(',')
1436 .map(|s| s.trim().to_string())
1437 .collect();
1438 }
1439 }
1440 }
1441 Vec::new()
1442 }
1443
1444 fn matches_where_filter(&self, attrs: &[syn::Attribute], where_filter: &str) -> Result<bool> {
1449 if let Some(filter_value) = where_filter.strip_prefix("derives_trait:") {
1451 let required_traits: Vec<&str> = filter_value.split(',').map(|s| s.trim()).collect();
1452 let existing_derives = Self::extract_derives(attrs);
1453
1454 for required_trait in required_traits {
1456 if existing_derives.iter().any(|d| d == required_trait) {
1457 return Ok(true);
1458 }
1459 }
1460 return Ok(false);
1461 }
1462
1463 Ok(true)
1465 }
1466
1467 fn update_derive_attr(attrs: &mut Vec<syn::Attribute>, derives: &[&str]) -> Result<()> {
1469 let derive_str = derives.join(", ");
1470
1471 let dummy = format!("#[derive({})]\nstruct Dummy;", derive_str);
1473 let parsed: syn::ItemStruct = parse_str(&dummy)
1474 .context("Failed to parse derive attribute")?;
1475
1476 let new_attr = parsed.attrs.into_iter()
1477 .find(|a| a.path().is_ident("derive"))
1478 .context("Failed to extract derive attribute")?;
1479
1480 if let Some(pos) = attrs.iter().position(|a| a.path().is_ident("derive")) {
1482 attrs[pos] = new_attr;
1483 } else {
1484 attrs.insert(0, new_attr);
1486 }
1487
1488 Ok(())
1489 }
1490
1491 fn replace_modified_functions(&mut self, modified_function: &Option<String>) -> Result<()> {
1493 if modified_function.is_none() {
1495 self.content = prettyplease::unparse(&self.syntax_tree);
1496 return Ok(());
1497 }
1498
1499 let original_syntax_tree: File = syn::parse_str(&self.content)
1501 .context("Failed to re-parse original content")?;
1502
1503 let function_name = modified_function.as_ref().unwrap();
1504
1505 let original_fn = original_syntax_tree.items.iter()
1507 .find_map(|item| {
1508 if let Item::Fn(f) = item {
1509 if f.sig.ident == function_name {
1510 return Some(f.clone());
1511 }
1512 }
1513 None
1514 })
1515 .ok_or_else(|| anyhow::anyhow!("Function '{}' not found in original", function_name))?;
1516
1517 let start = self.span_to_byte_offset(original_fn.span().start());
1519 let end = self.span_to_byte_offset(original_fn.span().end());
1520
1521 let modified_fn = self.syntax_tree.items.iter()
1523 .find_map(|item| {
1524 if let Item::Fn(f) = item {
1525 if f.sig.ident == function_name {
1526 return Some(f.clone());
1527 }
1528 }
1529 None
1530 })
1531 .ok_or_else(|| anyhow::anyhow!("Function '{}' not found in modified AST", function_name))?;
1532
1533 let dummy_file = syn::File {
1535 shebang: None,
1536 attrs: Vec::new(),
1537 items: vec![Item::Fn(modified_fn)],
1538 };
1539
1540 let formatted_fn = prettyplease::unparse(&dummy_file);
1541
1542 let formatted_fn = formatted_fn.trim();
1544
1545 self.content.replace_range(start..end, formatted_fn);
1547
1548 Ok(())
1549 }
1550
1551 fn span_to_byte_offset(&self, pos: LineColumn) -> usize {
1552 let line_idx = pos.line.saturating_sub(1);
1553 if line_idx < self.line_offsets.len() {
1554 self.line_offsets[line_idx] + pos.column
1555 } else {
1556 self.content.len()
1557 }
1558 }
1559
1560 fn find_after_field_end(&self, pos: usize) -> usize {
1561 let mut i = pos;
1563 while i < self.content.len() {
1564 match self.content.as_bytes()[i] as char {
1565 ',' => return i + 1,
1566 '\n' => return i + 1,
1567 _ => i += 1,
1568 }
1569 }
1570 pos
1571 }
1572
1573 fn get_indentation(&self, pos: usize) -> String {
1574 let mut line_start = pos;
1576 while line_start > 0 && self.content.as_bytes()[line_start - 1] != b'\n' {
1577 line_start -= 1;
1578 }
1579
1580 let mut indent = String::new();
1582 let mut i = line_start;
1583 while i < self.content.len() {
1584 match self.content.as_bytes()[i] as char {
1585 ' ' | '\t' => {
1586 indent.push(self.content.as_bytes()[i] as char);
1587 i += 1;
1588 }
1589 _ => break,
1590 }
1591 }
1592
1593 if indent.is_empty() {
1595 " ".to_string()
1596 } else {
1597 indent
1598 }
1599 }
1600
1601 pub fn to_string(&self) -> String {
1602 self.content.clone()
1603 }
1604
1605 pub(crate) fn inspect(&self, node_type: &str, name_filter: Option<&str>) -> Result<Vec<crate::operations::InspectResult>> {
1607 use syn::visit::Visit;
1608 use crate::operations::InspectResult;
1609
1610 let mut results = Vec::new();
1611
1612 match node_type {
1613 "struct-literal" => {
1614 struct StructLiteralVisitor<'a> {
1616 results: &'a mut Vec<InspectResult>,
1617 name_filter: Option<&'a str>,
1618 editor: &'a RustEditor,
1619 }
1620
1621 impl<'ast, 'a> Visit<'ast> for StructLiteralVisitor<'a> {
1622 fn visit_expr_struct(&mut self, node: &'ast syn::ExprStruct) {
1623 let struct_name = if let Some(ident) = node.path.get_ident() {
1625 ident.to_string()
1626 } else {
1627 node.path.segments.last()
1629 .map(|seg| seg.ident.to_string())
1630 .unwrap_or_default()
1631 };
1632
1633 if let Some(filter) = self.name_filter {
1635 if struct_name != filter {
1636 syn::visit::visit_expr_struct(self, node);
1637 return;
1638 }
1639 }
1640
1641 let snippet = self.editor.format_expr_struct(node);
1643 let location = self.editor.span_to_location(node.span());
1644
1645 self.results.push(InspectResult {
1646 file_path: String::new(), node_type: "ExprStruct".to_string(),
1648 identifier: struct_name,
1649 location,
1650 snippet,
1651 });
1652
1653 syn::visit::visit_expr_struct(self, node);
1655 }
1656 }
1657
1658 let mut visitor = StructLiteralVisitor {
1659 results: &mut results,
1660 name_filter,
1661 editor: self,
1662 };
1663
1664 for item in &self.syntax_tree.items {
1666 syn::visit::visit_item(&mut visitor, item);
1667 }
1668 }
1669 _ => anyhow::bail!("Unsupported node type: {}", node_type),
1670 }
1671
1672 Ok(results)
1673 }
1674
1675 fn format_expr_struct(&self, expr: &syn::ExprStruct) -> String {
1677 let start = self.span_to_byte_offset(expr.span().start());
1679 let end = self.span_to_byte_offset(expr.span().end());
1680
1681 let original = &self.content[start..end];
1683
1684 original.split_whitespace().collect::<Vec<_>>().join(" ")
1686 }
1687
1688 #[allow(dead_code)]
1690 pub(crate) fn find_item_index(&self, node_type: &str, name: &str) -> Result<usize> {
1691 for (index, item) in self.syntax_tree.items.iter().enumerate() {
1692 match (node_type, item) {
1693 ("struct", Item::Struct(s)) if s.ident == name => {
1694 return Ok(index);
1695 }
1696 ("enum", Item::Enum(e)) if e.ident == name => {
1697 return Ok(index);
1698 }
1699 ("fn", Item::Fn(f)) if f.sig.ident == name => {
1700 return Ok(index);
1701 }
1702 ("impl", Item::Impl(impl_block)) => {
1703 if let syn::Type::Path(type_path) = &*impl_block.self_ty {
1705 if let Some(segment) = type_path.path.segments.last() {
1706 if segment.ident == name {
1707 return Ok(index);
1708 }
1709 }
1710 }
1711 }
1712 _ => {}
1713 }
1714 }
1715
1716 anyhow::bail!("Item '{}' of type '{}' not found", name, node_type)
1717 }
1718
1719 #[allow(dead_code)]
1721 pub(crate) fn replace_item_at_index(&mut self, index: usize, new_item: Item) -> Result<()> {
1722 if index >= self.syntax_tree.items.len() {
1723 anyhow::bail!("Index {} out of bounds", index);
1724 }
1725
1726 self.syntax_tree.items[index] = new_item;
1728
1729 self.content = prettyplease::unparse(&self.syntax_tree);
1731
1732 self.line_offsets = Self::compute_line_offsets(&self.content);
1734
1735 Ok(())
1736 }
1737
1738 pub fn find_node(&self, node_type: &str, name: &str) -> Result<Vec<NodeLocation>> {
1739 let mut locations = Vec::new();
1740
1741 for item in &self.syntax_tree.items {
1742 match (node_type, item) {
1743 ("struct", Item::Struct(s)) if s.ident == name => {
1744 locations.push(self.span_to_location(s.span()));
1745 }
1746 ("enum", Item::Enum(e)) if e.ident == name => {
1747 locations.push(self.span_to_location(e.span()));
1748 }
1749 ("fn", Item::Fn(f)) if f.sig.ident == name => {
1750 locations.push(self.span_to_location(f.span()));
1751 }
1752 _ => {}
1753 }
1754 }
1755
1756 if locations.is_empty() {
1757 anyhow::bail!("Node '{}' of type '{}' not found", name, node_type);
1758 }
1759
1760 Ok(locations)
1761 }
1762
1763 fn span_to_location(&self, span: Span) -> NodeLocation {
1764 let start = span.start();
1765 let end = span.end();
1766
1767 NodeLocation {
1768 line: start.line,
1769 column: start.column,
1770 end_line: end.line,
1771 end_column: end.column,
1772 }
1773 }
1774}
1775
1776struct MatchArmAdder {
1778 target_function: Option<String>,
1779 arm_to_add: Arm,
1780 modified: bool,
1781 current_function: Option<String>,
1782 modified_function: Option<String>,
1783}
1784
1785impl VisitMut for MatchArmAdder {
1786 fn visit_item_fn_mut(&mut self, node: &mut syn::ItemFn) {
1787 let prev_fn = self.current_function.clone();
1788 self.current_function = Some(node.sig.ident.to_string());
1789
1790 syn::visit_mut::visit_item_fn_mut(self, node);
1792
1793 self.current_function = prev_fn;
1794 }
1795
1796 fn visit_expr_match_mut(&mut self, node: &mut ExprMatch) {
1797 if let Some(ref target) = self.target_function {
1799 if self.current_function.as_ref() != Some(target) {
1800 syn::visit_mut::visit_expr_match_mut(self, node);
1802 return;
1803 }
1804 }
1805
1806 let pattern_str = self.arm_to_add.pat.to_token_stream().to_string();
1808 let already_exists = node.arms.iter().any(|arm| {
1809 arm.pat.to_token_stream().to_string() == pattern_str
1810 });
1811
1812 if !already_exists {
1813 node.arms.push(self.arm_to_add.clone());
1815 self.modified = true;
1816 self.modified_function = self.current_function.clone();
1817 }
1818
1819 syn::visit_mut::visit_expr_match_mut(self, node);
1821 }
1822}
1823
1824struct MatchArmUpdater {
1826 target_function: Option<String>,
1827 pattern_to_match: String,
1828 new_body: syn::Expr,
1829 modified: bool,
1830 current_function: Option<String>,
1831 modified_function: Option<String>,
1832}
1833
1834impl VisitMut for MatchArmUpdater {
1835 fn visit_item_fn_mut(&mut self, node: &mut syn::ItemFn) {
1836 let prev_fn = self.current_function.clone();
1837 self.current_function = Some(node.sig.ident.to_string());
1838
1839 syn::visit_mut::visit_item_fn_mut(self, node);
1840
1841 self.current_function = prev_fn;
1842 }
1843
1844 fn visit_expr_match_mut(&mut self, node: &mut ExprMatch) {
1845 if let Some(ref target) = self.target_function {
1847 if self.current_function.as_ref() != Some(target) {
1848 syn::visit_mut::visit_expr_match_mut(self, node);
1849 return;
1850 }
1851 }
1852
1853 for arm in &mut node.arms {
1855 let pattern_str = arm.pat.to_token_stream().to_string();
1856 let pattern_normalized = pattern_str.replace(" ", "");
1858 let target_normalized = self.pattern_to_match.replace(" ", "");
1859
1860 if pattern_normalized == target_normalized {
1861 arm.body = Box::new(self.new_body.clone());
1862 self.modified = true;
1863 self.modified_function = self.current_function.clone();
1864 break;
1865 }
1866 }
1867
1868 syn::visit_mut::visit_expr_match_mut(self, node);
1869 }
1870}
1871
1872struct MatchArmRemover {
1874 target_function: Option<String>,
1875 pattern_to_remove: String,
1876 modified: bool,
1877 current_function: Option<String>,
1878 modified_function: Option<String>,
1879}
1880
1881impl VisitMut for MatchArmRemover {
1882 fn visit_item_fn_mut(&mut self, node: &mut syn::ItemFn) {
1883 let prev_fn = self.current_function.clone();
1884 self.current_function = Some(node.sig.ident.to_string());
1885
1886 syn::visit_mut::visit_item_fn_mut(self, node);
1887
1888 self.current_function = prev_fn;
1889 }
1890
1891 fn visit_expr_match_mut(&mut self, node: &mut ExprMatch) {
1892 if let Some(ref target) = self.target_function {
1894 if self.current_function.as_ref() != Some(target) {
1895 syn::visit_mut::visit_expr_match_mut(self, node);
1896 return;
1897 }
1898 }
1899
1900 let mut index_to_remove = None;
1902 for (i, arm) in node.arms.iter().enumerate() {
1903 let pattern_str = arm.pat.to_token_stream().to_string();
1904 let pattern_normalized = pattern_str.replace(" ", "");
1906 let target_normalized = self.pattern_to_remove.replace(" ", "");
1907
1908 if pattern_normalized == target_normalized {
1909 index_to_remove = Some(i);
1910 break;
1911 }
1912 }
1913
1914 if let Some(index) = index_to_remove {
1915 node.arms.remove(index);
1916 self.modified = true;
1917 self.modified_function = self.current_function.clone();
1918 }
1919
1920 syn::visit_mut::visit_expr_match_mut(self, node);
1921 }
1922}
1923
1924struct MultiMatchArmAdder {
1926 target_function: Option<String>,
1927 arms_to_add: Vec<(String, Arm)>, modified: bool,
1929 current_function: Option<String>,
1930 modified_function: Option<String>,
1931}
1932
1933impl VisitMut for MultiMatchArmAdder {
1934 fn visit_item_fn_mut(&mut self, node: &mut syn::ItemFn) {
1935 let prev_fn = self.current_function.clone();
1936 self.current_function = Some(node.sig.ident.to_string());
1937
1938 syn::visit_mut::visit_item_fn_mut(self, node);
1939
1940 self.current_function = prev_fn;
1941 }
1942
1943 fn visit_expr_match_mut(&mut self, node: &mut ExprMatch) {
1944 if let Some(ref target) = self.target_function {
1946 if self.current_function.as_ref() != Some(target) {
1947 syn::visit_mut::visit_expr_match_mut(self, node);
1948 return;
1949 }
1950 }
1951
1952 for (pattern_str, arm) in &self.arms_to_add {
1954 let already_exists = node.arms.iter().any(|existing_arm| {
1956 existing_arm.pat.to_token_stream().to_string() == *pattern_str
1957 });
1958
1959 if !already_exists {
1960 node.arms.push(arm.clone());
1961 self.modified = true;
1962 self.modified_function = self.current_function.clone();
1963 }
1964 }
1965
1966 syn::visit_mut::visit_expr_match_mut(self, node);
1967 }
1968}
1969
1970struct StructLiteralFieldAdder {
1972 struct_name: String,
1973 field_def: String,
1974 field_name: String,
1975 position: InsertPosition,
1976 modified: bool,
1977}
1978
1979impl VisitMut for StructLiteralFieldAdder {
1980 fn visit_expr_mut(&mut self, node: &mut Expr) {
1981 if let Expr::Struct(expr_struct) = node {
1983 let struct_name = expr_struct.path.segments.last()
1985 .map(|seg| seg.ident.to_string());
1986
1987 if struct_name.as_ref() == Some(&self.struct_name) {
1988 let field_exists = expr_struct.fields.iter().any(|fv| {
1990 fv.member.to_token_stream().to_string() == self.field_name
1991 });
1992
1993 if !field_exists {
1994 let field_value_code = format!("{{ {} }}", self.field_def);
1997 if let Ok(expr) = parse_str::<ExprStruct>(&format!("Dummy {}", field_value_code)) {
1998 if let Some(new_fv) = expr.fields.first() {
1999 match &self.position {
2001 InsertPosition::First => {
2002 expr_struct.fields.insert(0, new_fv.clone());
2003 self.modified = true;
2004 }
2005 InsertPosition::Last => {
2006 expr_struct.fields.push(new_fv.clone());
2007 self.modified = true;
2008 }
2009 InsertPosition::After(after_field) => {
2010 if let Some(pos) = expr_struct.fields.iter().position(|fv| {
2012 fv.member.to_token_stream().to_string() == *after_field
2013 }) {
2014 expr_struct.fields.insert(pos + 1, new_fv.clone());
2015 self.modified = true;
2016 }
2017 }
2018 InsertPosition::Before(before_field) => {
2019 if let Some(pos) = expr_struct.fields.iter().position(|fv| {
2021 fv.member.to_token_stream().to_string() == *before_field
2022 }) {
2023 expr_struct.fields.insert(pos, new_fv.clone());
2024 self.modified = true;
2025 }
2026 }
2027 }
2028 }
2029 }
2030 }
2031 }
2032 }
2033
2034 syn::visit_mut::visit_expr_mut(self, node);
2037 }
2038}