1use clap::Args;
2use color_eyre::Result;
3use comfy_table::{Table, presets::UTF8_FULL};
4use envx_core::{EnvVarManager, env::split_wildcard_pattern};
5
6#[derive(Args)]
7pub struct ReplaceArgs {
8 pub pattern: String,
10
11 pub value: String,
13
14 #[arg(long)]
16 pub dry_run: bool,
17}
18
19#[derive(Args)]
20pub struct FindReplaceArgs {
21 pub search: String,
23
24 pub replacement: String,
26
27 #[arg(short = 'p', long)]
29 pub pattern: Option<String>,
30
31 #[arg(long)]
33 pub dry_run: bool,
34}
35
36pub fn handle_replace(args: &ReplaceArgs) -> Result<()> {
46 let mut manager = EnvVarManager::new();
47 manager.load_all()?;
48
49 if args.dry_run {
50 let preview = preview_replace(&manager, &args.pattern)?;
52
53 if preview.is_empty() {
54 println!("No variables match the pattern '{}'", args.pattern);
55 } else {
56 println!("Would update {} variable(s):", preview.len());
57
58 let mut table = Table::new();
59 table.load_preset(UTF8_FULL);
60 table.set_header(vec!["Variable", "Current Value", "New Value"]);
61
62 for (name, current) in preview {
63 table.add_row(vec![name, current, args.value.clone()]);
64 }
65
66 println!("{table}");
67 println!("\nUse without --dry-run to apply changes");
68 }
69 } else {
70 let replaced = manager.replace(&args.pattern, &args.value)?;
71
72 if replaced.is_empty() {
73 println!("No variables match the pattern '{}'", args.pattern);
74 } else {
75 println!("ā
Updated {} variable(s):", replaced.len());
76
77 let mut table = Table::new();
78 table.load_preset(UTF8_FULL);
79 table.set_header(vec!["Variable", "Old Value", "New Value"]);
80
81 for (name, old, new) in &replaced {
82 let display_old = if old.len() > 50 {
84 format!("{}...", &old[..47])
85 } else {
86 old.clone()
87 };
88 let display_new = if new.len() > 50 {
89 format!("{}...", &new[..47])
90 } else {
91 new.clone()
92 };
93 table.add_row(vec![name.clone(), display_old, display_new]);
94 }
95
96 println!("{table}");
97
98 #[cfg(windows)]
99 println!("\nš Note: You may need to restart your terminal for changes to take effect");
100 }
101 }
102
103 Ok(())
104}
105
106fn preview_replace(manager: &EnvVarManager, pattern: &str) -> Result<Vec<(String, String)>> {
107 let mut preview = Vec::new();
108
109 if pattern.contains('*') {
110 let (prefix, suffix) = split_wildcard_pattern(pattern)?;
111
112 for var in manager.list() {
113 if var.name.starts_with(&prefix)
114 && var.name.ends_with(&suffix)
115 && var.name.len() >= prefix.len() + suffix.len()
116 {
117 preview.push((var.name.clone(), var.value.clone()));
118 }
119 }
120 } else if let Some(var) = manager.get(pattern) {
121 preview.push((var.name.clone(), var.value.clone()));
122 }
123
124 Ok(preview)
125}
126
127pub fn handle_find_replace(args: &FindReplaceArgs) -> Result<()> {
138 let mut manager = EnvVarManager::new();
139 manager.load_all()?;
140
141 if args.dry_run {
142 let preview = preview_find_replace(&manager, &args.search, &args.replacement, args.pattern.as_deref())?;
144
145 if preview.is_empty() {
146 println!("No variables contain '{}'", args.search);
147 } else {
148 println!("Would update {} variable(s):", preview.len());
149
150 let mut table = Table::new();
151 table.load_preset(UTF8_FULL);
152 table.set_header(vec!["Variable", "Current Value", "New Value"]);
153
154 for (name, old, new) in preview {
155 table.add_row(vec![name, old, new]);
156 }
157
158 println!("{table}");
159 println!("\nUse without --dry-run to apply changes");
160 }
161 } else {
162 let replaced = manager.find_replace(&args.search, &args.replacement, args.pattern.as_deref())?;
163
164 if replaced.is_empty() {
165 println!("No variables contain '{}'", args.search);
166 } else {
167 println!("ā
Updated {} variable(s):", replaced.len());
168
169 let mut table = Table::new();
170 table.load_preset(UTF8_FULL);
171 table.set_header(vec!["Variable", "Old Value", "New Value"]);
172
173 for (name, old, new) in &replaced {
174 let display_old = if old.len() > 50 {
176 format!("{}...", &old[..47])
177 } else {
178 old.clone()
179 };
180 let display_new = if new.len() > 50 {
181 format!("{}...", &new[..47])
182 } else {
183 new.clone()
184 };
185 table.add_row(vec![name.clone(), display_old, display_new]);
186 }
187
188 println!("{table}");
189
190 #[cfg(windows)]
191 println!("\nš Note: You may need to restart your terminal for changes to take effect");
192 }
193 }
194
195 Ok(())
196}
197
198fn preview_find_replace(
199 manager: &EnvVarManager,
200 search: &str,
201 replacement: &str,
202 pattern: Option<&str>,
203) -> Result<Vec<(String, String, String)>> {
204 let mut preview = Vec::new();
205
206 for var in manager.list() {
207 let matches_pattern = if let Some(pat) = pattern {
209 if pat.contains('*') {
210 let (prefix, suffix) = split_wildcard_pattern(pat)?;
211 var.name.starts_with(&prefix)
212 && var.name.ends_with(&suffix)
213 && var.name.len() >= prefix.len() + suffix.len()
214 } else {
215 var.name == pat
216 }
217 } else {
218 true
219 };
220
221 if matches_pattern && var.value.contains(search) {
222 let new_value = var.value.replace(search, replacement);
223 preview.push((var.name.clone(), var.value.clone(), new_value));
224 }
225 }
226
227 Ok(preview)
228}