Skip to main content

evo/
evo.rs

1use crate::domain::validation::{validate_env_key, validate_name};
2use crate::domain::{
3    CommandDetails, CommandListEntry, CommandPayload, CommandSpec, CommandSummary, EvoPaths,
4};
5use crate::error::Result;
6use crate::infra::process::{preflight_command, run_command};
7use crate::infra::search::SearchIndex;
8use crate::infra::store::CommandStore;
9use std::collections::BTreeMap;
10use std::ffi::OsString;
11use std::process::ExitStatus;
12
13#[derive(Clone, Debug)]
14pub struct Evo {
15    paths: EvoPaths,
16    store: CommandStore,
17    search_index: SearchIndex,
18}
19
20impl Evo {
21    pub fn new(paths: EvoPaths) -> Self {
22        Self {
23            store: CommandStore::new(paths.commands_dir.clone()),
24            search_index: SearchIndex::new(paths.index_db.clone()),
25            paths,
26        }
27    }
28
29    pub fn paths(&self) -> &EvoPaths {
30        &self.paths
31    }
32
33    pub fn command_spec(&self, name: &str) -> CommandSpec {
34        self.paths.command_spec(name)
35    }
36
37    pub fn resolve_command(&self, name: &str) -> Result<CommandSpec> {
38        self.store.resolve_command(name)
39    }
40
41    pub fn create(&self, name: &str, payload: CommandPayload) -> Result<CommandSpec> {
42        let spec = self.store.create_command(name, payload)?;
43        self.search_index.rebuild(&self.store)?;
44        Ok(spec)
45    }
46
47    pub fn update(&self, name: &str, payload: CommandPayload) -> Result<CommandSpec> {
48        let spec = self.store.update_command(name, payload)?;
49        self.search_index.rebuild(&self.store)?;
50        Ok(spec)
51    }
52
53    pub fn list(&self) -> Result<Vec<CommandListEntry>> {
54        self.store.list_command_entries()
55    }
56
57    pub fn show(&self, name: &str) -> Result<CommandDetails> {
58        self.store.load_command_details(name)
59    }
60
61    pub fn remove(&self, name: &str) -> Result<()> {
62        self.store.remove_command(name)?;
63        self.search_index.rebuild(&self.store)
64    }
65
66    pub fn run(
67        &self,
68        name: &str,
69        args: &[OsString],
70        stdin_bytes: Option<Vec<u8>>,
71    ) -> Result<ExitStatus> {
72        let spec = self.store.resolve_command(name)?;
73        preflight_command(&self.store, &spec, name)?;
74        run_command(&self.store, &spec, name, args, stdin_bytes)
75    }
76
77    pub fn list_env_keys(&self, name: &str) -> Result<Vec<String>> {
78        let spec = self.store.resolve_command(name)?;
79        let env_values = self.store.load_env_values(&spec.env_path, name)?;
80        Ok(env_values.into_keys().collect())
81    }
82
83    pub fn set_env(&self, name: &str, key: String, value: String) -> Result<()> {
84        validate_env_key(&key)?;
85        let spec = self.store.resolve_command(name)?;
86        let mut env_values = self.store.load_env_values(&spec.env_path, name)?;
87        env_values.insert(key, value);
88        self.store.write_env_values(&spec.env_path, &env_values)
89    }
90
91    pub fn unset_env(&self, name: &str, keys: Vec<String>) -> Result<()> {
92        let spec = self.store.resolve_command(name)?;
93        let mut env_values = self.store.load_env_values(&spec.env_path, name)?;
94        for key in keys {
95            validate_env_key(&key)?;
96            env_values.remove(&key);
97        }
98        self.store.write_env_values(&spec.env_path, &env_values)
99    }
100
101    pub fn ensure_search_index(&self) -> Result<()> {
102        self.search_index.ensure(&self.store)
103    }
104
105    pub fn reindex(&self) -> Result<()> {
106        self.search_index.rebuild(&self.store)
107    }
108
109    pub fn search(&self, query: &str, limit: usize) -> Result<Vec<CommandSummary>> {
110        self.search_index.ensure(&self.store)?;
111        self.search_index.search(&self.store, query, limit)
112    }
113
114    pub fn list_summaries(&self) -> Result<Vec<CommandSummary>> {
115        self.store.list_command_summaries()
116    }
117
118    pub fn validate_name(&self, name: &str) -> Result<()> {
119        validate_name(name)
120    }
121
122    pub fn load_env_values(
123        &self,
124        spec: &CommandSpec,
125        command_name: &str,
126    ) -> Result<BTreeMap<String, String>> {
127        self.store.load_env_values(&spec.env_path, command_name)
128    }
129}
130
131impl Default for Evo {
132    fn default() -> Self {
133        Self::new(EvoPaths::default())
134    }
135}