intelli_shell/process/
new.rs1use std::time::Duration;
2
3use indicatif::{ProgressBar, ProgressStyle};
4use tokio_util::sync::CancellationToken;
5use tracing::instrument;
6
7use super::{InteractiveProcess, Process, ProcessOutput};
8use crate::{
9 cli::BookmarkCommandProcess,
10 component::{
11 Component,
12 edit::{EditCommandComponent, EditCommandComponentMode},
13 },
14 config::Config,
15 errors::{AppError, UserFacingError},
16 format_error, format_msg,
17 model::{CATEGORY_USER, Command, SOURCE_USER},
18 service::IntelliShellService,
19 widgets::SPINNER_CHARS,
20};
21
22impl Process for BookmarkCommandProcess {
23 #[instrument(skip_all)]
24 async fn execute(
25 self,
26 config: Config,
27 service: IntelliShellService,
28 cancellation_token: CancellationToken,
29 ) -> color_eyre::Result<ProcessOutput> {
30 let BookmarkCommandProcess {
31 mut command,
32 alias,
33 mut description,
34 ai,
35 } = self;
36
37 if ai {
39 let cmd = command.clone().unwrap_or_default();
40 let desc = description.clone().unwrap_or_default();
41
42 if cmd.trim().is_empty() && desc.trim().is_empty() {
43 return Ok(ProcessOutput::fail().stderr(format_error!(
44 config.theme,
45 "{}",
46 UserFacingError::AiEmptyCommand
47 )));
48 }
49
50 let pb = ProgressBar::new_spinner();
52 pb.set_style(
53 ProgressStyle::with_template("{spinner:.blue} {wide_msg}")
54 .unwrap()
55 .tick_strings(&SPINNER_CHARS),
56 );
57 pb.enable_steady_tick(Duration::from_millis(100));
58 pb.set_message("Thinking ...");
59
60 let res = service.suggest_command(cmd, desc, cancellation_token).await;
62
63 pb.finish_and_clear();
65
66 match res {
68 Ok(Some(suggestion)) => {
69 command = Some(suggestion.cmd);
70 description = suggestion.description;
71 }
72 Ok(None) => {
73 return Ok(
74 ProcessOutput::fail().stderr(format_error!(config.theme, "AI didn't generate any command"))
75 );
76 }
77 Err(AppError::UserFacing(err)) => {
78 return Ok(ProcessOutput::fail().stderr(format_error!(config.theme, "{err}")));
79 }
80 Err(AppError::Unexpected(report)) => return Err(report),
81 }
82 }
83
84 let command = Command::new(CATEGORY_USER, SOURCE_USER, command.unwrap_or_default())
85 .with_alias(alias)
86 .with_description(description);
87
88 match service.insert_command(command).await {
89 Ok(command) => Ok(ProcessOutput::success()
90 .stderr(format_msg!(
91 config.theme,
92 "Command stored: {}",
93 config.theme.secondary.apply(&command.cmd)
94 ))
95 .fileout(command.cmd)),
96 Err(AppError::UserFacing(err)) => Ok(ProcessOutput::fail().stderr(format_error!(config.theme, "{err}"))),
97 Err(AppError::Unexpected(report)) => Err(report),
98 }
99 }
100}
101
102impl InteractiveProcess for BookmarkCommandProcess {
103 #[instrument(skip_all)]
104 fn into_component(
105 self,
106 config: Config,
107 service: IntelliShellService,
108 inline: bool,
109 cancellation_token: CancellationToken,
110 ) -> color_eyre::Result<Box<dyn Component>> {
111 let BookmarkCommandProcess {
112 command,
113 alias,
114 description,
115 ai,
116 } = self;
117
118 let command = Command::new(CATEGORY_USER, SOURCE_USER, command.unwrap_or_default())
119 .with_alias(alias)
120 .with_description(description);
121
122 Ok(Box::new(EditCommandComponent::new(
123 service,
124 config.theme,
125 inline,
126 command,
127 EditCommandComponentMode::New { ai },
128 cancellation_token,
129 )))
130 }
131}