intelli_shell/process/
search.rs1use std::time::Duration;
2
3use indicatif::{ProgressBar, ProgressStyle};
4use itertools::Itertools;
5use tracing::instrument;
6
7use super::{InteractiveProcess, Process, ProcessOutput};
8use crate::{
9 cli::SearchCommandsProcess,
10 component::{Component, search::SearchCommandsComponent},
11 config::Config,
12 errors::{AppError, UserFacingError},
13 format_error,
14 service::IntelliShellService,
15 widgets::SPINNER_CHARS,
16};
17
18impl Process for SearchCommandsProcess {
19 #[instrument(skip_all)]
20 async fn execute(self, config: Config, service: IntelliShellService) -> color_eyre::Result<ProcessOutput> {
21 if self.ai {
23 let prompt = self.query.as_deref().unwrap_or_default();
25 if prompt.trim().is_empty() {
26 return Ok(ProcessOutput::fail().stderr(format_error!(
27 config.theme,
28 "{}",
29 UserFacingError::AiEmptyCommand
30 )));
31 }
32
33 let pb = ProgressBar::new_spinner();
35 pb.set_style(
36 ProgressStyle::with_template("{spinner:.blue} {wide_msg}")
37 .unwrap()
38 .tick_strings(&SPINNER_CHARS),
39 );
40 pb.enable_steady_tick(Duration::from_millis(100));
41 pb.set_message("Thinking ...");
42
43 let res = service.suggest_commands(prompt).await;
45
46 pb.finish_and_clear();
48
49 match res {
51 Ok(commands) => Ok(ProcessOutput::success().stdout(commands.into_iter().map(|c| c.cmd).join("\n"))),
52 Err(AppError::UserFacing(err)) => {
53 Ok(ProcessOutput::fail().stderr(format_error!(config.theme, "{err}")))
54 }
55 Err(AppError::Unexpected(report)) => Err(report),
56 }
57 } else {
58 let (config, query) = merge_config(self, config);
60
61 match service
63 .search_commands(config.search.mode, config.search.user_only, &query)
64 .await
65 {
66 Ok((commands, _)) => {
67 Ok(ProcessOutput::success().stdout(commands.into_iter().map(|c| c.cmd).join("\n")))
68 }
69 Err(AppError::UserFacing(err)) => {
70 Ok(ProcessOutput::fail().stderr(format_error!(config.theme, "{err}")))
71 }
72 Err(AppError::Unexpected(report)) => Err(report),
73 }
74 }
75 }
76}
77
78impl InteractiveProcess for SearchCommandsProcess {
79 #[instrument(skip_all)]
80 fn into_component(
81 self,
82 config: Config,
83 service: IntelliShellService,
84 inline: bool,
85 ) -> color_eyre::Result<Box<dyn Component>> {
86 let ai = self.ai;
87 let (config, query) = merge_config(self, config);
88 Ok(Box::new(SearchCommandsComponent::new(
89 service, config, inline, query, ai,
90 )))
91 }
92}
93
94fn merge_config(p: SearchCommandsProcess, mut config: Config) -> (Config, String) {
95 let SearchCommandsProcess {
96 query,
97 mode,
98 user_only,
99 ai: _,
100 } = p;
101 config.search.mode = mode.unwrap_or(config.search.mode);
102 config.search.user_only = user_only || config.search.user_only;
103 (config, query.unwrap_or_default())
104}