adana_cache_command/
process.rs

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                    //return Err(anyhow::Error::msg(format!("{key} not found")));
125                }
126            }
127            CacheCommand::Using(key) => {
128                if set_default_cache(db, key).is_some() {
129                    // println!(
130                    //     "previous: {}",
131                    //     LightCyan.paint(current_cache.as_str())
132                    // );
133                    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}