mi6_cli/commands/
status.rs

1//! Status command - show mi6 setup and data information.
2
3use std::collections::HashMap;
4use std::path::Path;
5
6use anyhow::{Context, Result};
7use serde::Serialize;
8
9use mi6_core::{Config, all_adapters};
10
11use crate::display::{bold_green, bold_red, bold_white, dark_grey, format_bytes};
12
13/// Status output structure for JSON serialization
14#[derive(Serialize)]
15struct StatusOutput {
16    version: String,
17    #[serde(skip_serializing_if = "Option::is_none")]
18    commit: Option<String>,
19    frameworks: HashMap<String, FrameworkStatus>,
20    data: DataStatus,
21}
22
23#[derive(Serialize)]
24struct FrameworkStatus {
25    activated: bool,
26}
27
28#[derive(Serialize)]
29struct DataStatus {
30    storage_bytes: u64,
31    storage_human: String,
32    path: String,
33}
34
35/// Get the total size of a directory in bytes
36fn get_dir_size(path: &Path) -> std::io::Result<u64> {
37    let mut total = 0;
38
39    if path.is_file() {
40        return Ok(path.metadata()?.len());
41    }
42
43    if path.is_dir() {
44        for entry in std::fs::read_dir(path)? {
45            let entry = entry?;
46            let path = entry.path();
47            if path.is_file() {
48                total += path.metadata()?.len();
49            } else if path.is_dir() {
50                total += get_dir_size(&path)?;
51            }
52        }
53    }
54
55    Ok(total)
56}
57
58/// Get git commit information (short hash)
59fn get_git_commit() -> Option<String> {
60    // Compile-time env var (set by build.rs or CI)
61    option_env!("MI6_GIT_COMMIT").map(String::from)
62}
63
64/// Run the mi6 status command.
65pub fn run_status_command(json: bool) -> Result<()> {
66    let config = Config::load().context("failed to load config")?;
67
68    // Gather data
69    let version = env!("CARGO_PKG_VERSION").to_string();
70    let commit = get_git_commit();
71
72    let mut frameworks = HashMap::new();
73    for adapter in all_adapters() {
74        let activated = adapter.has_mi6_hooks(false, false);
75        frameworks.insert(adapter.name().to_string(), FrameworkStatus { activated });
76    }
77
78    let db_path = config
79        .db_path()
80        .context("failed to determine database path")?;
81    let storage_bytes = if db_path.exists() {
82        get_dir_size(&db_path).unwrap_or(0)
83    } else {
84        0
85    };
86
87    if json {
88        // JSON output
89        let output = StatusOutput {
90            version,
91            commit,
92            frameworks,
93            data: DataStatus {
94                storage_bytes,
95                storage_human: format_bytes(storage_bytes),
96                path: db_path.display().to_string(),
97            },
98        };
99        println!("{}", serde_json::to_string_pretty(&output)?);
100    } else {
101        // Human-readable output
102        print!("mi6 {}", bold_white(&version));
103        if let Some(ref c) = commit {
104            print!(" ({})", bold_white(c));
105        }
106        println!();
107        println!();
108
109        println!("Setup");
110        for adapter in all_adapters() {
111            let enabled = frameworks.get(adapter.name()).is_some_and(|f| f.activated);
112            let status = if enabled {
113                bold_green("ENABLED")
114            } else {
115                bold_red("NOT ENABLED")
116            };
117            println!("  {}: {}", adapter.display_name(), status);
118        }
119        println!(
120            "{}{}{}",
121            dark_grey("(enable using "),
122            bold_white("mi6 enable"),
123            dark_grey(")")
124        );
125        println!();
126
127        println!("Data");
128        println!("  storage: {}", bold_white(&format_bytes(storage_bytes)));
129        println!("  path: {}", bold_white(&db_path.display().to_string()));
130    }
131
132    Ok(())
133}