Skip to main content

mai_cli/cli/
list_cmd.rs

1use crate::core::{InstallScope, MaiConfig};
2use crate::error::Result;
3use crate::storage::{Registry, XdgPaths};
4use clap::Args;
5
6#[derive(Debug, Args)]
7pub struct ListCommand {
8    /// Filter by tool name
9    #[arg(short, long)]
10    pub tool: Option<String>,
11
12    /// List all tools
13    #[arg(short, long)]
14    pub all: bool,
15
16    /// List only global packs
17    #[arg(long, conflicts_with = "local")]
18    pub global: bool,
19
20    /// List only local packs
21    #[arg(long, conflicts_with = "global")]
22    pub local: bool,
23}
24
25impl ListCommand {
26    pub fn run(&self) -> Result<()> {
27        let scope = if self.global {
28            Some(InstallScope::Global)
29        } else if self.local {
30            Some(InstallScope::Local)
31        } else {
32            None
33        };
34
35        let paths = XdgPaths::new();
36        let registry = Registry::new(&paths);
37
38        if let Some(ref tool) = self.tool {
39            self.list_tool_packs(&registry, tool, scope)?;
40        } else if self.all {
41            self.list_all(&registry, scope)?;
42        } else {
43            self.list_active(&paths, &registry, scope)?;
44        }
45
46        Ok(())
47    }
48
49    fn list_active(
50        &self,
51        paths: &XdgPaths,
52        registry: &Registry,
53        scope: Option<InstallScope>,
54    ) -> Result<()> {
55        let config_path = paths.config_file();
56        let config = if config_path.exists() {
57            let content = std::fs::read_to_string(&config_path)?;
58            toml::from_str::<MaiConfig>(&content).unwrap_or_default()
59        } else {
60            MaiConfig::new()
61        };
62
63        match config.active_tool() {
64            Some(tool) => {
65                println!("Active tool: {}", tool);
66                self.list_tool_packs(registry, tool, scope)?;
67            }
68            None => {
69                println!("No active tool. Use 'mai use <tool>' to set one.");
70            }
71        }
72        Ok(())
73    }
74
75    fn list_tool_packs(
76        &self,
77        registry: &Registry,
78        tool: &str,
79        scope: Option<InstallScope>,
80    ) -> Result<()> {
81        let packs = registry.list_packs(Some(tool), scope)?;
82        if packs.is_empty() {
83            let scope_msg = match scope {
84                Some(InstallScope::Global) => "global ",
85                Some(InstallScope::Local) => "local ",
86                None => "",
87            };
88            println!("No {}packs installed for tool: {}", scope_msg, tool);
89        } else {
90            println!("Packs for {}:", tool);
91            for pack in packs {
92                let metadata_info = pack
93                    .metadata
94                    .description
95                    .as_ref()
96                    .map(|d| format!(" - {}", d))
97                    .unwrap_or_default();
98                println!(
99                    "  [{}] {} {} ({}){}",
100                    pack.scope, pack.name, pack.version, pack.pack_type, metadata_info
101                );
102            }
103        }
104        Ok(())
105    }
106
107    fn list_all(&self, registry: &Registry, scope: Option<InstallScope>) -> Result<()> {
108        let packs = registry.list_packs(None, scope)?;
109        if packs.is_empty() {
110            println!("No packs installed.");
111        } else {
112            println!("Installed packs:");
113            for pack in packs {
114                let metadata_info = pack
115                    .metadata
116                    .description
117                    .as_ref()
118                    .map(|d| format!(" - {}", d))
119                    .unwrap_or_default();
120                println!(
121                    "  [{}] {} {}{}",
122                    pack.scope,
123                    pack.id(),
124                    pack.pack_type,
125                    metadata_info
126                );
127            }
128        }
129        Ok(())
130    }
131}