intelli_shell/process/
import.rs

1use std::time::Duration;
2
3use color_eyre::Result;
4use futures_util::StreamExt;
5use indicatif::{ProgressBar, ProgressStyle};
6use tokio_util::sync::CancellationToken;
7
8use super::{Process, ProcessOutput};
9use crate::{
10    cli::ImportItemsProcess,
11    component::{
12        Component,
13        pick::{ImportExportPickerComponent, ImportExportPickerComponentMode},
14    },
15    config::Config,
16    errors::AppError,
17    format_error,
18    process::InteractiveProcess,
19    service::IntelliShellService,
20    widgets::SPINNER_CHARS,
21};
22
23impl Process for ImportItemsProcess {
24    async fn execute(
25        self,
26        config: Config,
27        service: IntelliShellService,
28        cancellation_token: CancellationToken,
29    ) -> color_eyre::Result<ProcessOutput> {
30        let dry_run = self.dry_run;
31
32        // If AI is enabled
33        let res = if self.ai {
34            // Setup the progress bar
35            let pb = ProgressBar::new_spinner();
36            pb.set_style(
37                ProgressStyle::with_template("{spinner:.blue} {wide_msg}")
38                    .unwrap()
39                    .tick_strings(&SPINNER_CHARS),
40            );
41            pb.enable_steady_tick(Duration::from_millis(100));
42            pb.set_message("Thinking ...");
43
44            // Retrieve items using AI
45            let res = service
46                .get_items_from_location(self, config.gist, cancellation_token)
47                .await;
48
49            // Clear the spinner
50            pb.finish_and_clear();
51
52            res
53        } else {
54            // If no AI, it's fast enough to not require a spinner
55            service
56                .get_items_from_location(self, config.gist, cancellation_token)
57                .await
58        };
59
60        // Check retrieval result
61        let mut items = match res {
62            Ok(s) => s,
63            Err(AppError::UserFacing(err)) => {
64                return Ok(ProcessOutput::fail().stderr(format_error!(config.theme, "{err}")));
65            }
66            Err(AppError::Unexpected(report)) => return Err(report),
67        };
68
69        // Print them out when in dry-run
70        if dry_run {
71            let mut stdout = String::new();
72            while let Some(item) = items.next().await {
73                stdout += &item.map_err(AppError::into_report)?.to_string();
74                stdout += "\n";
75            }
76            if stdout.is_empty() {
77                Ok(ProcessOutput::fail().stderr(format_error!(&config.theme, "No commands or completions were found")))
78            } else {
79                Ok(ProcessOutput::success().stdout(stdout))
80            }
81        } else {
82            // Or import them when not dry-run
83            match service.import_items(items, false).await {
84                Ok(stats) => Ok(stats.into_output(&config.theme)),
85                Err(AppError::UserFacing(err)) => {
86                    Ok(ProcessOutput::fail().stderr(format_error!(config.theme, "{err}")))
87                }
88                Err(AppError::Unexpected(report)) => Err(report),
89            }
90        }
91    }
92}
93
94impl InteractiveProcess for ImportItemsProcess {
95    fn into_component(
96        self,
97        config: Config,
98        service: IntelliShellService,
99        inline: bool,
100        cancellation_token: CancellationToken,
101    ) -> Result<Box<dyn Component>> {
102        Ok(Box::new(ImportExportPickerComponent::new(
103            service,
104            config,
105            inline,
106            ImportExportPickerComponentMode::Import { input: self },
107            cancellation_token,
108        )))
109    }
110}