1use std::{collections::BTreeMap, path::PathBuf};
2
3use crate::reserved_keywords::{CACHE_COMMAND_DOC, check_reserved_keyword};
4use adana_db::{DbOp, SCRIPT_CACHE_KEY};
5use adana_script::print_ast;
6use adana_script_core::primitive::RefPrimitive;
7use anyhow::Context;
8use nom::error::ErrorKind;
9use nu_ansi_term::Color::*;
10use regex::Regex;
11
12use super::{
13 CacheCommand, cache::*, clear_terminal, os_command::exec_command,
14 parser::parse_command,
15};
16
17const BACKUP_FILE_NAME: &str = "adanadb.json";
18
19pub fn process_command(
20 db: &mut impl DbOp<String, String>,
21 script_context: &mut BTreeMap<String, RefPrimitive>,
22 current_cache: &mut String,
23 previous_dir: &mut PathBuf,
24 line: &str,
25) -> anyhow::Result<()> {
26 match parse_command(line) {
27 Ok((_, command)) => match command {
28 CacheCommand::Put { aliases, value } => {
29 if check_reserved_keyword(&aliases) {
30 return Err(anyhow::Error::msg(
31 format!("{}",Red.paint("You cannot use a reserved keyword name as an alias.")),
32 ));
33 } else if let Some(key) =
34 insert_value(db, current_cache, aliases, value, false)
35 {
36 println!(
37 "added {} with keys {}",
38 Yellow.paint(value),
39 Red.paint(key)
40 );
41 } else {
42 return Err(anyhow::Error::msg(format!(
43 "{}",
44 Red.paint("could not insert! Key already exists")
45 )));
46 }
47 }
48 CacheCommand::Alias((left, right)) => {
49 if check_reserved_keyword(&[right]) {
50 return Err(anyhow::Error::msg(
51 format!("{}",Red.paint("You cannot use a reserved keyword name as an alias.")),
52 ));
53 }
54 match (
55 get_value(db, current_cache, left),
56 get_value(db, current_cache, right),
57 ) {
58 (Some(value), None) => {
59 if let Some(key) = insert_value(
60 db,
61 current_cache,
62 vec![right],
63 &value,
64 false,
65 ) {
66 println!(
67 "aliased {} with keys {}",
68 Yellow.paint(value),
69 Red.paint(key)
70 );
71 } else {
72 return Err(anyhow::Error::msg(format!(
73 "{}",
74 Red.paint(
75 "could not alias! Right Key already exists"
76 )
77 )));
78 }
79 }
80 _ => {
81 return Err(anyhow::Error::msg(format!(
82 "{}",
83 Red.paint("could not alias! Wrong combination")
84 )));
85 }
86 }
87 }
88 CacheCommand::Del(key) => {
89 if let Some(v) = remove_value(db, current_cache, key, false) {
90 println!(
91 "removed {} with hash key {}",
92 Yellow.paint(v),
93 Red.paint(key.to_string())
94 );
95 } else {
96 return Err(anyhow::Error::msg(format!(
97 "key {key} not found in current cache {current_cache}"
98 )));
99 }
100 }
101 CacheCommand::Get(key) => {
102 if let Some(value) = get_value(db, current_cache, key) {
103 println!("found '{}'", Yellow.paint(value));
104 } else {
105 return Err(anyhow::Error::msg(format!("{key} not found")));
106 }
107 }
108 CacheCommand::Clip(key) => {
109 if let Some(value) = get_value(db, current_cache, key) {
110 let mut clipboard = arboard::Clipboard::new()?;
111 clipboard.set_text(&value)?;
112 println!("Copied.");
113 } else {
114 return Err(anyhow::Error::msg(format!("{key} not found")));
115 }
116 }
117 CacheCommand::Exec { key, args } => {
118 if let Some(value) = get_value(db, current_cache, key) {
119 let _ = exec_command(&value, &args, false)
120 .map_err(|e| anyhow::Error::msg(e.to_string()))?;
121 } else if !key.trim().is_empty() {
122 exec_command(key, &args, true)
123 .map_err(|e| anyhow::Error::msg(e.to_string()))?;
124 }
126 }
127 CacheCommand::Using(key) => {
128 if set_default_cache(db, key).is_some() {
129 current_cache.clear();
134 current_cache.push_str(key);
135 }
136 }
137 CacheCommand::ListCache => {
138 println!(
139 "{}",
140 get_cache_names(db)
141 .iter()
142 .map(|c| Red.bold().paint(c).to_string())
143 .collect::<Vec<_>>()
144 .join("\n")
145 );
146 }
147 CacheCommand::CurrentCache => {
148 println!(
149 "{}",
150 LightBlue.bold().paint(current_cache.to_string())
151 );
152 }
153 CacheCommand::Merge(key) if key == current_cache => {
154 return Err(anyhow::Error::msg(
155 "You cannot merge a cache with itself!",
156 ));
157 }
158 CacheCommand::Merge(key) => {
159 if merge(db, key, current_cache).is_some() {
160 println!(
161 "cache {} has been merged with cache {}.",
162 Red.bold().paint(current_cache.to_string()),
163 Yellow.bold().paint(key)
164 );
165 } else {
166 return Err(anyhow::Error::msg("something went wrong!"));
167 }
168 }
169 CacheCommand::Dump(key) => {
170 if let Some(json) = dump(db, key) {
171 println!("{json}");
172 } else {
173 return Err(anyhow::Error::msg("cache doesn't exist!"));
174 }
175 }
176 CacheCommand::DeleteCache(key) => {
177 if let Some(cache_name) = key {
178 if cache_name != current_cache {
179 println!(
180 "remove {cache_name}: {}",
181 remove_cache(db, cache_name, false)
182 .unwrap_or(false)
183 );
184 } else {
185 clear_values(db, current_cache, false);
186 println!("clear all values from {current_cache}",);
187 }
188 }
189 }
190 CacheCommand::Describe(regex) => {
191 fn print_fn(
192 values: Vec<(String, String)>,
193 pred: impl Fn(&str) -> bool,
194 ) {
195 for (key, value) in values {
196 if pred(&key) {
197 println!(
198 "{}\n{}",
199 Red.bold().underline().paint(key),
200 LightCyan.italic().paint(value)
201 );
202 }
203 }
204 }
205
206 if let Some(values) = list_values(db, current_cache) {
207 if let Some(regex) = regex {
208 let re = Regex::new(regex)?;
209 print_fn(values, |s| re.is_match(s));
210 } else {
211 print_fn(values, |_| true);
212 }
213 }
214 }
215 CacheCommand::Backup => {
216 let backup_path =
217 std::env::current_dir()?.join(BACKUP_FILE_NAME);
218 let backup_path = backup_path.as_path();
219 if let Some(()) = backup(db, backup_path) {
220 println!(
221 "db backed up to {}",
222 Red.paint(backup_path.to_string_lossy())
223 );
224 }
225 }
226 CacheCommand::Flush => match flush(db) {
227 Ok(msg) => println!("{msg}"),
228 Err(err) => eprintln!("Error: {err:?}"),
229 },
230 CacheCommand::Restore => {
231 let backup_path =
232 std::env::current_dir()?.join(BACKUP_FILE_NAME);
233 let backup_path = backup_path.as_path();
234 if let Some(()) = restore(db, backup_path) {
235 println!(
236 "db restored from {}",
237 Red.paint(backup_path.to_string_lossy())
238 );
239 }
240 }
241 CacheCommand::Cd(cdt) => {
242 let path_buf = {
243 match cdt {
244 super::ChangeDirectoryType::HomeDirectory(path) => {
245 path.and_then(|p| {
246 dirs::home_dir().map(|hd| hd.join(p))
247 })
248 .or_else(dirs::home_dir)
249 .context(
250 "could not change directory. path {path:?} not found!",
251 )?
252 }
253 super::ChangeDirectoryType::Path(path) => {
254 PathBuf::from(path)
255 }
256 super::ChangeDirectoryType::Previous => {
257 previous_dir.clone()
258 },
259 }
260 };
261 if path_buf.exists() {
262 let current_dir = std::env::current_dir()?;
263 if current_dir != path_buf {
264 *previous_dir = current_dir;
265 }
266 std::env::set_current_dir(path_buf.as_path())?;
267 } else {
268 return Err(anyhow::Error::msg(format!(
269 "path {} doesn't exist",
270 Red.paint(path_buf.to_string_lossy())
271 )));
272 }
273 }
274 CacheCommand::Help => {
275 for doc in CACHE_COMMAND_DOC {
276 let (command, doc) = doc;
277 println!(
278 "{}\n{}",
279 command
280 .iter()
281 .map(|c| Yellow
282 .bold()
283 .underline()
284 .paint(*c)
285 .to_string())
286 .collect::<Vec<_>>()
287 .join("/"),
288 White.italic().paint(*doc)
289 );
290 }
291 }
292 CacheCommand::Clear => {
293 clear_terminal();
294 }
295 CacheCommand::PrintScriptContext => {
296 let json = serde_json::to_string_pretty(&script_context)?;
297 println!("{json}")
298 }
299 CacheCommand::StoreScriptContext(name) => {
300 let name = name.unwrap_or("latest.json");
301 let binary = bincode::serialize(&script_context)?;
302 remove_value(db, SCRIPT_CACHE_KEY, name, true);
303
304 if insert_value(
305 db,
306 SCRIPT_CACHE_KEY,
307 vec![name],
308 &String::from_utf8_lossy(&binary),
309 true,
310 )
311 .is_none()
312 {
313 return Err(anyhow::Error::msg(format!(
314 "{}",
315 Red.paint("could not insert/update script context!")
316 )));
317 } else {
318 println!("Script context stored with key {name}");
319 }
320 }
321 CacheCommand::LoadScriptContext(name) => {
322 let name = name.unwrap_or("latest.json");
323 let value = get_value(db, SCRIPT_CACHE_KEY, name);
324 if let Some(value) = value.and_then(|v| {
325 bincode::deserialize::<BTreeMap<String, RefPrimitive>>(
326 v.as_bytes(),
327 )
328 .ok()
329 }) {
330 *script_context = value;
331 println!(
332 "{}",
333 Green.paint("Script context restored from cache.")
334 );
335 }
336 }
337 CacheCommand::PrintAst(script) => {
338 print_ast(script)?;
339 }
340 },
341 Err(e) => match e {
342 nom::Err::Failure(failure) if failure.code == ErrorKind::Verify => {
343 return Err(anyhow::Error::msg(format!(
344 "invalid command: {}",
345 Red.paint(failure.to_string())
346 )));
347 }
348 nom::Err::Error(err) if err.input.trim().is_empty() => {}
349 _ => {
350 return Err(anyhow::Error::msg(format!(
351 "error parsing command: {}",
352 Red.paint(e.to_string())
353 )));
354 }
355 },
356 }
357 Ok(())
358}