intelli_shell/service/
command.rs1use tracing::instrument;
2use uuid::Uuid;
3
4use super::IntelliShellService;
5use crate::{
6 errors::{Result, UserFacingError},
7 model::{CATEGORY_USER, CATEGORY_WORKSPACE, Command, SearchCommandsFilter, SearchMode},
8 utils::{extract_tags_and_cleaned_text, extract_tags_with_editing_and_cleaned_text, get_working_dir},
9};
10
11type Tag = (String, u64, bool);
13
14impl IntelliShellService {
15 #[instrument(skip_all)]
17 pub async fn is_storage_empty(&self) -> Result<bool> {
18 self.storage.is_empty().await
19 }
20
21 #[instrument(skip_all)]
23 pub async fn insert_command(&self, command: Command) -> Result<Command> {
24 if command.cmd.is_empty() {
26 return Err(UserFacingError::EmptyCommand.into());
27 }
28
29 tracing::info!("Bookmarking command: {}", command.cmd);
31 self.storage.insert_command(command).await
32 }
33
34 #[instrument(skip_all)]
36 pub async fn update_command(&self, command: Command) -> Result<Command> {
37 if command.cmd.is_empty() {
39 return Err(UserFacingError::EmptyCommand.into());
40 }
41
42 tracing::info!("Updating command '{}': {}", command.id, command.cmd);
44 self.storage.update_command(command).await
45 }
46
47 #[instrument(skip_all)]
49 pub async fn increment_command_usage(&self, command_id: Uuid) -> Result<i32> {
50 tracing::info!("Increasing usage for command '{command_id}'");
51 self.storage
52 .increment_command_usage(command_id, get_working_dir())
53 .await
54 }
55
56 #[instrument(skip_all)]
58 pub async fn delete_command(&self, id: Uuid) -> Result<()> {
59 tracing::info!("Deleting command: {}", id);
60 self.storage.delete_command(id).await
61 }
62
63 #[instrument(skip_all)]
65 pub async fn search_tags(
66 &self,
67 mode: SearchMode,
68 user_only: bool,
69 query: &str,
70 cursor_pos: usize,
71 ) -> Result<Option<Vec<Tag>>> {
72 let Some((editing_tag, other_tags, cleaned_text)) =
73 extract_tags_with_editing_and_cleaned_text(query, cursor_pos)
74 else {
75 return Ok(None);
76 };
77
78 tracing::info!(
79 "Searching for tags{} [{mode:?}]: {query}",
80 if user_only { " (user only)" } else { "" }
81 );
82 tracing::trace!("Editing: {editing_tag} Other: {other_tags:?}");
83
84 let filter = SearchCommandsFilter {
85 category: user_only.then(|| vec![CATEGORY_USER.to_string()]),
86 source: None,
87 tags: Some(other_tags),
88 search_mode: mode,
89 search_term: Some(cleaned_text),
90 };
91
92 Ok(Some(
93 self.storage
94 .find_tags(filter, Some(editing_tag), &self.tuning.commands)
95 .await?,
96 ))
97 }
98
99 #[instrument(skip_all)]
101 pub async fn search_commands(
102 &self,
103 mode: SearchMode,
104 user_only: bool,
105 query: &str,
106 ) -> Result<(Vec<Command>, bool)> {
107 tracing::info!(
108 "Searching for commands{} [{mode:?}]: {query}",
109 if user_only { " (user only)" } else { "" }
110 );
111
112 let query = query.trim();
113 let filter = if query.is_empty() {
114 SearchCommandsFilter {
116 category: Some(if user_only {
117 vec![CATEGORY_USER.to_string()]
118 } else {
119 vec![CATEGORY_USER.to_string(), CATEGORY_WORKSPACE.to_string()]
120 }),
121 search_mode: mode,
122 ..Default::default()
123 }
124 } else {
125 let (tags, search_term) = match extract_tags_and_cleaned_text(query) {
127 Some((tags, cleaned_query)) => (Some(tags), Some(cleaned_query)),
128 None => (None, Some(query.to_string())),
129 };
130
131 SearchCommandsFilter {
133 category: user_only.then(|| vec![CATEGORY_USER.to_string()]),
134 source: None,
135 tags,
136 search_mode: mode,
137 search_term,
138 }
139 };
140
141 self.storage
143 .find_commands(filter, get_working_dir(), &self.tuning.commands)
144 .await
145 }
146}