Skip to main content

ryo_mutations/basic/
item.rs

1//! AddItemMutation, RemoveItemMutation, and MoveItemMutation
2//!
3//! Add, remove, or move arbitrary items from source code.
4
5use crate::Mutation;
6use ryo_source::pure::PureItem;
7use ryo_source::ItemKind;
8use ryo_symbol::{SymbolId, SymbolPath};
9
10/// Add an item from raw source code content
11#[derive(Debug, Clone)]
12pub struct AddItemMutation {
13    /// Parent module SymbolId where the item should be added
14    pub parent: SymbolId,
15    /// Raw source code content to parse and add
16    pub content: String,
17}
18
19impl AddItemMutation {
20    pub fn new(parent: SymbolId, content: impl Into<String>) -> Self {
21        Self {
22            parent,
23            content: content.into(),
24        }
25    }
26}
27
28impl Mutation for AddItemMutation {
29    fn mutation_type(&self) -> &'static str {
30        "AddItem"
31    }
32
33    fn describe(&self) -> String {
34        format!("Add item to {}", self.parent)
35    }
36
37    fn box_clone(&self) -> Box<dyn Mutation> {
38        Box::new(self.clone())
39    }
40}
41
42/// Add multiple items directly from AST (no parsing overhead)
43///
44/// Unlike AddItemMutation which parses string content, this mutation
45/// takes pre-built PureItem AST nodes. Used by Duplicate operations
46/// to avoid AST → String → AST round-trips.
47#[derive(Debug, Clone)]
48pub struct AddPureItemsMutation {
49    /// Parent module SymbolId where items should be added
50    pub parent: SymbolId,
51    /// Pre-built AST items to add
52    pub items: Vec<PureItem>,
53}
54
55impl AddPureItemsMutation {
56    pub fn new(parent: SymbolId, items: Vec<PureItem>) -> Self {
57        Self { parent, items }
58    }
59}
60
61impl Mutation for AddPureItemsMutation {
62    fn mutation_type(&self) -> &'static str {
63        "AddPureItems"
64    }
65
66    fn describe(&self) -> String {
67        format!("Add {} items to {}", self.items.len(), self.parent)
68    }
69
70    fn box_clone(&self) -> Box<dyn Mutation> {
71        Box::new(self.clone())
72    }
73}
74
75/// Remove an item by SymbolId
76#[derive(Debug, Clone)]
77pub struct RemoveItemMutation {
78    /// SymbolId of the item to remove (required, O(1) lookup)
79    pub symbol_id: SymbolId,
80    /// Kind of item to remove
81    pub item_kind: ItemKind,
82}
83
84impl RemoveItemMutation {
85    pub fn new(symbol_id: SymbolId, item_kind: ItemKind) -> Self {
86        Self {
87            symbol_id,
88            item_kind,
89        }
90    }
91}
92
93impl Mutation for RemoveItemMutation {
94    fn mutation_type(&self) -> &'static str {
95        "RemoveItem"
96    }
97
98    fn describe(&self) -> String {
99        format!("Remove {:?} ({})", self.item_kind, self.symbol_id)
100    }
101
102    fn box_clone(&self) -> Box<dyn Mutation> {
103        Box::new(self.clone())
104    }
105}
106
107/// Move an item from one module to another
108#[derive(Debug, Clone)]
109pub struct MoveItemMutation {
110    /// Source module as SymbolPath
111    pub source: SymbolPath,
112    /// Target module as SymbolPath
113    pub target: SymbolPath,
114    /// Item name to move
115    pub item_name: String,
116    /// Kind of item to move
117    pub item_kind: ItemKind,
118    /// Whether to add use statement in source file
119    pub add_use: bool,
120}
121
122impl MoveItemMutation {
123    pub fn new(
124        source: SymbolPath,
125        target: SymbolPath,
126        item_name: impl Into<String>,
127        item_kind: ItemKind,
128        add_use: bool,
129    ) -> Self {
130        Self {
131            source,
132            target,
133            item_name: item_name.into(),
134            item_kind,
135            add_use,
136        }
137    }
138}
139
140impl Mutation for MoveItemMutation {
141    fn mutation_type(&self) -> &'static str {
142        "MoveItem"
143    }
144
145    fn describe(&self) -> String {
146        format!(
147            "Move {:?} '{}' from {} to {}",
148            self.item_kind, self.item_name, self.source, self.target
149        )
150    }
151
152    fn box_clone(&self) -> Box<dyn Mutation> {
153        Box::new(self.clone())
154    }
155}
156
157#[cfg(test)]
158mod tests {
159    use super::*;
160
161    #[test]
162    fn test_add_item_mutation_describe() {
163        let mutation = AddItemMutation::new(SymbolId::default(), "pub struct Foo {}");
164        assert!(mutation.describe().contains("Add item"));
165    }
166
167    #[test]
168    fn test_remove_item_mutation_describe() {
169        let mutation = RemoveItemMutation::new(SymbolId::default(), ItemKind::Struct);
170        assert!(mutation.describe().contains("Struct"));
171    }
172}