Skip to main content

systemprompt_logging/services/cli/
module.rs

1#![allow(clippy::print_stdout)]
2
3use crate::services::cli::display::{CollectionDisplay, Display, DisplayUtils, ModuleItemDisplay};
4use crate::services::cli::prompts::Prompts;
5use crate::services::cli::theme::{ItemStatus, MessageLevel, ModuleType, Theme};
6use anyhow::Result;
7#[derive(Debug, Copy, Clone)]
8pub struct ModuleDisplay;
9
10impl ModuleDisplay {
11    pub fn missing_schemas(module_name: &str, schemas: &[(String, String)]) {
12        if schemas.is_empty() {
13            return;
14        }
15
16        DisplayUtils::module_status(
17            module_name,
18            &format!("{} schemas need application", schemas.len()),
19        );
20
21        DisplayUtils::count_message(MessageLevel::Warning, schemas.len(), "schemas");
22
23        let items: Vec<ModuleItemDisplay> = schemas
24            .iter()
25            .map(|(file, table)| {
26                ModuleItemDisplay::new(ModuleType::Schema, file, table, ItemStatus::Missing)
27            })
28            .collect();
29
30        for item in &items {
31            item.display();
32        }
33    }
34
35    pub fn missing_seeds(module_name: &str, seeds: &[(String, String)]) {
36        if seeds.is_empty() {
37            return;
38        }
39
40        DisplayUtils::module_status(
41            module_name,
42            &format!("{} seeds need application", seeds.len()),
43        );
44
45        DisplayUtils::count_message(MessageLevel::Warning, seeds.len(), "seeds");
46
47        let items: Vec<ModuleItemDisplay> = seeds
48            .iter()
49            .map(|(file, table)| {
50                ModuleItemDisplay::new(ModuleType::Seed, file, table, ItemStatus::Missing)
51            })
52            .collect();
53
54        for item in &items {
55            item.display();
56        }
57    }
58
59    pub fn prompt_apply_schemas(module_name: &str, schemas: &[(String, String)]) -> Result<bool> {
60        if schemas.is_empty() {
61            return Ok(false);
62        }
63
64        Self::missing_schemas(module_name, schemas);
65        println!();
66        Prompts::confirm_schemas()
67    }
68
69    pub fn prompt_apply_seeds(module_name: &str, seeds: &[(String, String)]) -> Result<bool> {
70        if seeds.is_empty() {
71            return Ok(false);
72        }
73
74        Self::missing_seeds(module_name, seeds);
75        println!();
76        Prompts::confirm_seeds()
77    }
78}
79
80#[derive(Debug, Clone)]
81pub struct ModuleUpdate {
82    pub name: String,
83    pub old_version: String,
84    pub new_version: String,
85    pub changes: Vec<String>,
86}
87
88impl ModuleUpdate {
89    pub fn new(
90        name: impl Into<String>,
91        old_version: impl Into<String>,
92        new_version: impl Into<String>,
93    ) -> Self {
94        Self {
95            name: name.into(),
96            old_version: old_version.into(),
97            new_version: new_version.into(),
98            changes: Vec::new(),
99        }
100    }
101
102    #[must_use]
103    pub fn with_change(mut self, change: impl Into<String>) -> Self {
104        self.changes.push(change.into());
105        self
106    }
107
108    #[must_use]
109    pub fn with_changes(mut self, changes: Vec<String>) -> Self {
110        self.changes = changes;
111        self
112    }
113}
114
115impl Display for ModuleUpdate {
116    fn display(&self) {
117        println!(
118            "   {} {} {}",
119            Theme::icon(crate::services::cli::theme::ActionType::Update),
120            Theme::color(&self.name, crate::services::cli::theme::EmphasisType::Bold),
121            Theme::color(
122                &format!("{} → {}", self.old_version, self.new_version),
123                crate::services::cli::theme::EmphasisType::Dim
124            )
125        );
126
127        for change in &self.changes {
128            println!(
129                "     • {}",
130                Theme::color(change, crate::services::cli::theme::EmphasisType::Dim)
131            );
132        }
133    }
134}
135
136#[derive(Debug, Clone)]
137pub struct ModuleInstall {
138    pub name: String,
139    pub version: String,
140    pub description: Option<String>,
141}
142
143impl ModuleInstall {
144    pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
145        Self {
146            name: name.into(),
147            version: version.into(),
148            description: None,
149        }
150    }
151
152    #[must_use]
153    pub fn with_description(mut self, description: impl Into<String>) -> Self {
154        self.description = Some(description.into());
155        self
156    }
157}
158
159impl Display for ModuleInstall {
160    fn display(&self) {
161        let detail = self.description.as_ref().map_or_else(
162            || format!("v{}", self.version),
163            |desc| format!("v{} - {}", self.version, desc),
164        );
165
166        println!(
167            "   {} {} {}",
168            Theme::icon(crate::services::cli::theme::ActionType::Install),
169            Theme::color(&self.name, crate::services::cli::theme::EmphasisType::Bold),
170            Theme::color(&detail, crate::services::cli::theme::EmphasisType::Dim)
171        );
172    }
173}
174
175#[derive(Debug, Copy, Clone)]
176pub struct BatchModuleOperations;
177
178impl BatchModuleOperations {
179    pub fn prompt_install_multiple(modules: &[ModuleInstall]) -> Result<bool> {
180        if modules.is_empty() {
181            return Ok(false);
182        }
183
184        let collection =
185            CollectionDisplay::new("New modules available for installation", modules.to_vec());
186        collection.display();
187
188        Prompts::confirm("Install all these modules?", false)
189    }
190
191    pub fn prompt_update_multiple(updates: &[ModuleUpdate]) -> Result<bool> {
192        if updates.is_empty() {
193            return Ok(false);
194        }
195
196        let collection = CollectionDisplay::new("Module updates available", updates.to_vec());
197        collection.display();
198
199        Prompts::confirm("Update all these modules?", false)
200    }
201}