raz_config/
override_config.rs1use indexmap::IndexMap;
2use serde::{Deserialize, Serialize};
3use std::path::PathBuf;
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct CommandOverride {
7 pub key: String,
8 pub file_path: Option<PathBuf>,
9 pub function_name: Option<String>,
10 pub line_range: Option<(usize, usize)>,
11 pub env: IndexMap<String, String>,
12 pub cargo_options: Vec<String>,
13 pub rustc_options: Vec<String>,
14 pub args: Vec<String>,
15 pub mode: OverrideMode,
16 pub created_at: chrono::DateTime<chrono::Utc>,
17 pub description: Option<String>,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
21#[serde(rename_all = "lowercase")]
22pub enum OverrideMode {
23 Replace,
24 Append,
25 Prepend,
26 Remove,
27}
28
29impl Default for OverrideMode {
30 fn default() -> Self {
31 Self::Replace
32 }
33}
34
35impl CommandOverride {
36 pub fn new(key: String) -> Self {
37 Self {
38 key,
39 file_path: None,
40 function_name: None,
41 line_range: None,
42 env: IndexMap::new(),
43 cargo_options: Vec::new(),
44 rustc_options: Vec::new(),
45 args: Vec::new(),
46 mode: OverrideMode::default(),
47 created_at: chrono::Utc::now(),
48 description: None,
49 }
50 }
51
52 pub fn with_file(mut self, path: PathBuf) -> Self {
53 self.file_path = Some(path);
54 self
55 }
56
57 pub fn with_function(mut self, name: String) -> Self {
58 self.function_name = Some(name);
59 self
60 }
61
62 pub fn with_line_range(mut self, start: usize, end: usize) -> Self {
63 self.line_range = Some((start, end));
64 self
65 }
66
67 pub fn with_env(mut self, key: String, value: String) -> Self {
68 self.env.insert(key, value);
69 self
70 }
71
72 pub fn with_cargo_option(mut self, option: String) -> Self {
73 self.cargo_options.push(option);
74 self
75 }
76
77 pub fn with_rustc_option(mut self, option: String) -> Self {
78 self.rustc_options.push(option);
79 self
80 }
81
82 pub fn with_arg(mut self, arg: String) -> Self {
83 self.args.push(arg);
84 self
85 }
86
87 pub fn with_mode(mut self, mode: OverrideMode) -> Self {
88 self.mode = mode;
89 self
90 }
91
92 pub fn with_description(mut self, desc: String) -> Self {
93 self.description = Some(desc);
94 self
95 }
96
97 pub fn generate_key(&self) -> String {
98 let mut parts = Vec::new();
99
100 if let Some(path) = &self.file_path {
101 parts.push(path.to_string_lossy().to_string());
102 }
103
104 if let Some(func) = &self.function_name {
105 parts.push(func.clone());
106 }
107
108 if let Some((start, end)) = self.line_range {
109 parts.push(format!("L{start}-{end}"));
110 }
111
112 if parts.is_empty() {
113 self.key.clone()
114 } else {
115 parts.join(":")
116 }
117 }
118
119 pub fn matches_context(
120 &self,
121 file: Option<&PathBuf>,
122 function: Option<&str>,
123 line: Option<usize>,
124 ) -> bool {
125 if let Some(f) = &self.file_path {
126 if let Some(current_file) = file {
127 if f != current_file {
128 return false;
129 }
130 } else {
131 return false;
132 }
133 }
134
135 if let Some(func) = &self.function_name {
136 if let Some(current_func) = function {
137 if func != current_func {
138 return false;
139 }
140 } else {
141 return false;
142 }
143 }
144
145 if let Some((start, end)) = self.line_range {
146 if let Some(current_line) = line {
147 if current_line < start || current_line > end {
148 return false;
149 }
150 } else {
151 return false;
152 }
153 }
154
155 true
156 }
157}
158
159#[derive(Debug, Clone, Serialize, Deserialize)]
160pub struct OverrideCollection {
161 pub overrides: IndexMap<String, CommandOverride>,
162}
163
164impl OverrideCollection {
165 pub fn new() -> Self {
166 Self {
167 overrides: IndexMap::new(),
168 }
169 }
170
171 pub fn add(&mut self, override_config: CommandOverride) {
172 let key = override_config.generate_key();
173 self.overrides.insert(key, override_config);
174 }
175
176 pub fn remove(&mut self, key: &str) -> Option<CommandOverride> {
177 self.overrides.shift_remove(key)
178 }
179
180 pub fn find_best_match(
181 &self,
182 file: Option<&PathBuf>,
183 function: Option<&str>,
184 line: Option<usize>,
185 ) -> Option<&CommandOverride> {
186 self.overrides
187 .values()
188 .filter(|o| o.matches_context(file, function, line))
189 .max_by_key(|o| {
190 let mut score = 0;
191 if o.file_path.is_some() {
192 score += 100;
193 }
194 if o.function_name.is_some() {
195 score += 50;
196 }
197 if o.line_range.is_some() {
198 score += 25;
199 }
200 score
201 })
202 }
203
204 pub fn get_all_for_file(&self, file: &PathBuf) -> Vec<&CommandOverride> {
205 self.overrides
206 .values()
207 .filter(|o| o.file_path.as_ref() == Some(file))
208 .collect()
209 }
210}
211
212impl Default for OverrideCollection {
213 fn default() -> Self {
214 Self::new()
215 }
216}
217
218#[derive(Debug, Clone, Serialize, Deserialize)]
220pub struct OverrideSettings {
221 #[serde(default = "default_validate_before_save")]
223 pub validate_before_save: bool,
224
225 #[serde(default = "default_max_backups")]
227 pub max_backups: usize,
228
229 #[serde(default = "default_prompt_rollback_on_failure")]
231 pub prompt_rollback_on_failure: bool,
232
233 #[serde(default = "default_auto_rollback_threshold")]
235 pub auto_rollback_threshold: u32,
236}
237
238impl Default for OverrideSettings {
239 fn default() -> Self {
240 Self {
241 validate_before_save: true,
242 max_backups: 5,
243 prompt_rollback_on_failure: true,
244 auto_rollback_threshold: 3,
245 }
246 }
247}
248
249fn default_validate_before_save() -> bool {
250 true
251}
252
253fn default_max_backups() -> usize {
254 5
255}
256
257fn default_prompt_rollback_on_failure() -> bool {
258 true
259}
260
261fn default_auto_rollback_threshold() -> u32 {
262 3
263}