rs_hack/
operations.rs

1use serde::{Deserialize, Serialize};
2use std::path::PathBuf;
3
4/// Edit mode for operations - controls how changes are applied to source files
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
6#[serde(rename_all = "lowercase")]
7pub enum EditMode {
8    /// Surgical mode: preserve all formatting, only change specific locations
9    /// This is the recommended default for minimal diffs
10    Surgical,
11    /// Reformat mode: use prettyplease to reformat the entire file
12    /// Use this if you want consistent formatting across the file
13    Reformat,
14}
15
16impl Default for EditMode {
17    fn default() -> Self {
18        EditMode::Surgical
19    }
20}
21
22impl std::fmt::Display for EditMode {
23    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24        match self {
25            EditMode::Surgical => write!(f, "surgical"),
26            EditMode::Reformat => write!(f, "reformat"),
27        }
28    }
29}
30
31impl std::str::FromStr for EditMode {
32    type Err = String;
33
34    fn from_str(s: &str) -> Result<Self, Self::Err> {
35        match s.to_lowercase().as_str() {
36            "surgical" => Ok(EditMode::Surgical),
37            "reformat" => Ok(EditMode::Reformat),
38            _ => Err(format!("Invalid edit mode: {}. Valid values are 'surgical' or 'reformat'", s)),
39        }
40    }
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize)]
44#[serde(tag = "type")]
45pub enum Operation {
46    AddStructField(AddStructFieldOp),
47    UpdateStructField(UpdateStructFieldOp),
48    RemoveStructField(RemoveStructFieldOp),
49    AddStructLiteralField(AddStructLiteralFieldOp),
50    AddEnumVariant(AddEnumVariantOp),
51    UpdateEnumVariant(UpdateEnumVariantOp),
52    RemoveEnumVariant(RemoveEnumVariantOp),
53    AddMatchArm(AddMatchArmOp),
54    UpdateMatchArm(UpdateMatchArmOp),
55    RemoveMatchArm(RemoveMatchArmOp),
56    AddImplMethod(AddImplMethodOp),
57    AddUseStatement(AddUseStatementOp),
58    AddDerive(AddDeriveOp),
59    Transform(TransformOp),
60    RenameEnumVariant(RenameEnumVariantOp),
61    RenameFunction(RenameFunctionOp),
62    AddDocComment(AddDocCommentOp),
63    UpdateDocComment(UpdateDocCommentOp),
64    RemoveDocComment(RemoveDocCommentOp),
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct AddStructFieldOp {
69    pub struct_name: String,
70    pub field_def: String, // e.g., "new_field: Option<String>" or just "new_field" if literal_default is provided
71    pub position: InsertPosition,
72    #[serde(default)]
73    pub literal_default: Option<String>, // If provided: tries to add to definition (idempotent), always updates literals
74    #[serde(default)]
75    pub where_filter: Option<String>, // Optional: filter targets (e.g., "derives_trait:Clone")
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct UpdateStructFieldOp {
80    pub struct_name: String,
81    pub field_def: String, // e.g., "field_name: NewType" (field name is parsed from this)
82    #[serde(default)]
83    pub where_filter: Option<String>, // Optional: filter targets (e.g., "derives_trait:Clone")
84}
85
86#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct RemoveStructFieldOp {
88    pub struct_name: String,
89    pub field_name: String, // Name of the field to remove
90    #[serde(default)]
91    pub literal_only: bool, // If true, only remove from struct literals, not the definition
92    #[serde(default)]
93    pub where_filter: Option<String>, // Optional: filter targets (e.g., "derives_trait:Clone")
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct AddStructLiteralFieldOp {
98    pub struct_name: String,
99    pub field_def: String, // e.g., "return_type: None"
100    pub position: InsertPosition,
101    #[serde(default)]
102    pub struct_path: Option<String>,  // Optional canonical path (e.g., "crate::types::Rectangle")
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct AddEnumVariantOp {
107    pub enum_name: String,
108    pub variant_def: String, // e.g., "NewVariant" or "NewVariant { x: i32 }"
109    pub position: InsertPosition,
110    #[serde(default)]
111    pub where_filter: Option<String>, // Optional: filter targets (e.g., "derives_trait:Clone")
112}
113
114#[derive(Debug, Clone, Serialize, Deserialize)]
115pub struct UpdateEnumVariantOp {
116    pub enum_name: String,
117    pub variant_def: String, // e.g., "UpdatedVariant { new_field: Type }" (variant name parsed from this)
118    #[serde(default)]
119    pub where_filter: Option<String>, // Optional: filter targets (e.g., "derives_trait:Clone")
120}
121
122#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct RemoveEnumVariantOp {
124    pub enum_name: String,
125    pub variant_name: String, // Name of the variant to remove
126    #[serde(default)]
127    pub where_filter: Option<String>, // Optional: filter targets (e.g., "derives_trait:Clone")
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize)]
131pub struct AddMatchArmOp {
132    pub pattern: String, // e.g., "MyEnum::NewVariant"
133    pub body: String,    // e.g., "todo!()"
134    pub function_name: Option<String>, // Optional: specific function containing match
135    #[serde(default)]
136    pub auto_detect: bool, // Auto-detect missing enum variants
137    pub enum_name: Option<String>, // Enum name for auto-detection
138}
139
140#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct UpdateMatchArmOp {
142    pub pattern: String, // Pattern to find (e.g., "MyEnum::Variant")
143    pub new_body: String, // New body for the arm
144    pub function_name: Option<String>, // Optional: specific function containing match
145}
146
147#[derive(Debug, Clone, Serialize, Deserialize)]
148pub struct RemoveMatchArmOp {
149    pub pattern: String, // Pattern to remove (e.g., "MyEnum::Variant")
150    pub function_name: Option<String>, // Optional: specific function containing match
151}
152
153#[derive(Debug, Clone, Serialize, Deserialize)]
154pub struct AddImplMethodOp {
155    pub target: String, // e.g., "MyStruct" or "impl MyTrait for MyStruct"
156    pub method_def: String, // Full method definition
157    pub position: InsertPosition,
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize)]
161pub struct AddUseStatementOp {
162    pub use_path: String, // e.g., "std::collections::HashMap"
163    pub position: InsertPosition,
164}
165
166#[derive(Debug, Clone, Serialize, Deserialize)]
167pub struct AddDeriveOp {
168    pub target_name: String, // Name of struct or enum
169    pub target_type: String, // "struct" or "enum"
170    pub derives: Vec<String>, // e.g., ["Clone", "Debug", "Serialize"]
171    #[serde(default)]
172    pub where_filter: Option<String>, // Optional: filter targets (e.g., "derives_trait:Clone")
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize)]
176pub enum InsertPosition {
177    First,
178    Last,
179    After(String),  // After named item
180    Before(String), // Before named item
181}
182
183#[derive(Debug, Serialize, Deserialize)]
184pub struct BatchSpec {
185    pub base_path: PathBuf,
186    pub operations: Vec<Operation>,
187}
188
189#[derive(Debug, Serialize, Deserialize, Clone)]
190pub struct NodeLocation {
191    pub line: usize,
192    pub column: usize,
193    pub end_line: usize,
194    pub end_column: usize,
195}
196
197/// Backup of a single AST node before modification
198#[derive(Debug, Clone, Serialize, Deserialize)]
199pub struct BackupNode {
200    pub node_type: String,        // "ItemStruct", "ItemEnum", "ItemImpl", "ExprStruct", "ExprMatch"
201    pub identifier: String,        // "User", "Status::Draft", "process_event", etc.
202    pub original_content: String,  // Original AST node as formatted code
203    pub location: NodeLocation,
204}
205
206/// Result of applying an operation
207#[derive(Debug)]
208pub struct ModificationResult {
209    pub changed: bool,
210    pub modified_nodes: Vec<BackupNode>,
211    /// Unmatched qualified paths (only populated for struct literal operations with simple names)
212    /// Maps fully qualified path to count of instances found but not matched
213    pub unmatched_qualified_paths: Option<std::collections::HashMap<String, usize>>,
214}
215
216/// Result of inspecting/listing AST nodes
217#[derive(Debug, Serialize, Deserialize)]
218pub struct InspectResult {
219    pub file_path: String,
220    pub node_type: String,      // "ExprStruct", "ExprMatch", etc.
221    pub identifier: String,      // "Shadow", "Config", etc.
222    pub location: NodeLocation,
223    pub snippet: String,         // Formatted code snippet
224    #[serde(skip_serializing_if = "Option::is_none")]
225    pub preceding_comment: Option<String>,  // Doc comments + regular comments before the node
226}
227
228/// Generic transformation operation
229#[derive(Debug, Clone, Serialize, Deserialize)]
230pub struct TransformOp {
231    pub node_type: String,           // "macro-call", "method-call", etc.
232    pub name_filter: Option<String>, // Filter by name (e.g., "eprintln")
233    pub content_filter: Option<String>, // Filter by content (e.g., "[SHADOW RENDER]")
234    pub action: TransformAction,     // What to do with matching nodes
235}
236
237/// Actions that can be performed on AST nodes
238#[derive(Debug, Clone, Serialize, Deserialize)]
239#[serde(tag = "type")]
240pub enum TransformAction {
241    Comment,                    // Wrap in // comment
242    Remove,                     // Delete the node entirely
243    Replace { with: String },   // Replace with provided code
244}
245
246/// Rename an enum variant across the codebase
247#[derive(Debug, Clone, Serialize, Deserialize)]
248pub struct RenameEnumVariantOp {
249    pub enum_name: String,      // Name of the enum (e.g., "IRValue")
250    pub old_variant: String,    // Current variant name (e.g., "HashMapV2")
251    pub new_variant: String,    // New variant name (e.g., "HashMap")
252    #[serde(default)]
253    pub enum_path: Option<String>,  // Optional canonical path (e.g., "crate::compiler::types::IRValue")
254    #[serde(default)]
255    pub edit_mode: EditMode,    // How to apply changes (surgical vs reformat)
256}
257
258/// Rename a function across the codebase
259#[derive(Debug, Clone, Serialize, Deserialize)]
260pub struct RenameFunctionOp {
261    pub old_name: String,       // Current function name (e.g., "process_v2")
262    pub new_name: String,       // New function name (e.g., "process")
263    #[serde(default)]
264    pub function_path: Option<String>,  // Optional canonical path (e.g., "crate::utils::process_v2")
265    #[serde(default)]
266    pub edit_mode: EditMode,    // How to apply changes (surgical vs reformat)
267}
268
269/// Add documentation comment to an item
270#[derive(Debug, Clone, Serialize, Deserialize)]
271pub struct AddDocCommentOp {
272    pub target_type: String,    // "struct", "enum", "function", "field", "variant"
273    pub name: String,           // Name of the target (e.g., "User", "Status::Draft")
274    pub doc_comment: String,    // Documentation text (without /// prefix)
275    #[serde(default)]
276    pub style: DocCommentStyle, // Line (///) or Block (/** */)
277}
278
279/// Update existing documentation comment
280#[derive(Debug, Clone, Serialize, Deserialize)]
281pub struct UpdateDocCommentOp {
282    pub target_type: String,    // "struct", "enum", "function", "field", "variant"
283    pub name: String,           // Name of the target
284    pub doc_comment: String,    // New documentation text
285}
286
287/// Remove documentation comment from an item
288#[derive(Debug, Clone, Serialize, Deserialize)]
289pub struct RemoveDocCommentOp {
290    pub target_type: String,    // "struct", "enum", "function", "field", "variant"
291    pub name: String,           // Name of the target
292}
293
294/// Documentation comment style
295#[derive(Debug, Clone, Serialize, Deserialize)]
296#[serde(rename_all = "lowercase")]
297pub enum DocCommentStyle {
298    Line,   // /// or //!
299    Block,  // /** */ or /*! */
300}
301
302impl Default for DocCommentStyle {
303    fn default() -> Self {
304        DocCommentStyle::Line
305    }
306}
307
308impl std::str::FromStr for DocCommentStyle {
309    type Err = String;
310
311    fn from_str(s: &str) -> Result<Self, Self::Err> {
312        match s.to_lowercase().as_str() {
313            "line" => Ok(DocCommentStyle::Line),
314            "block" => Ok(DocCommentStyle::Block),
315            _ => Err(format!("Invalid doc comment style: {}. Valid values are 'line' or 'block'", s)),
316        }
317    }
318}
319
320/// Location of a field in the codebase
321#[derive(Debug, Clone, Serialize, Deserialize)]
322pub struct FieldLocation {
323    pub file_path: String,
324    pub line: usize,
325    pub context: FieldContext,
326}
327
328/// Context in which a field appears
329#[derive(Debug, Clone, Serialize, Deserialize)]
330#[serde(tag = "type")]
331pub enum FieldContext {
332    StructDefinition {
333        struct_name: String,
334        field_type: String,
335    },
336    EnumVariantDefinition {
337        enum_name: String,
338        variant_name: String,
339        field_type: String,
340    },
341    StructLiteral {
342        struct_name: String,
343    },
344}