Skip to main content

systemprompt_cli/commands/admin/config/
paths.rs

1//! `admin config paths` command: show and validate the configured filesystem
2//! paths.
3//!
4//! [`PathsCommands`] reports the system, services, bin, web, storage, and
5//! `GeoIP` paths from the active profile and checks whether each required path
6//! exists.
7
8use anyhow::Result;
9use clap::Subcommand;
10use std::path::Path;
11use systemprompt_config::ProfileBootstrap;
12use systemprompt_logging::CliService;
13
14use super::types::{PathInfo, PathValidation, PathsConfigOutput, PathsValidateOutput};
15use crate::CliConfig;
16use crate::cli_settings::OutputFormat;
17use crate::shared::{CommandOutput, render_result};
18
19#[derive(Debug, Clone, Copy, Subcommand)]
20pub enum PathsCommands {
21    #[command(about = "Show paths configuration", alias = "list")]
22    Show,
23
24    #[command(about = "Validate that all configured paths exist")]
25    Validate,
26}
27
28pub fn execute(command: PathsCommands, config: &CliConfig) -> Result<()> {
29    match command {
30        PathsCommands::Show => execute_show(),
31        PathsCommands::Validate => execute_validate(config),
32    }
33}
34
35pub(super) fn execute_show() -> Result<()> {
36    let profile = ProfileBootstrap::get()?;
37
38    let output = PathsConfigOutput {
39        system: PathInfo {
40            path: profile.paths.system.clone(),
41            exists: Path::new(&profile.paths.system).exists(),
42        },
43        services: PathInfo {
44            path: profile.paths.services.clone(),
45            exists: Path::new(&profile.paths.services).exists(),
46        },
47        bin: PathInfo {
48            path: profile.paths.bin.clone(),
49            exists: Path::new(&profile.paths.bin).exists(),
50        },
51        web_path: profile.paths.web_path.as_ref().map(|p| PathInfo {
52            path: p.clone(),
53            exists: Path::new(p).exists(),
54        }),
55        storage: profile.paths.storage.as_ref().map(|p| PathInfo {
56            path: p.clone(),
57            exists: Path::new(p).exists(),
58        }),
59        geoip_database: profile.paths.geoip_database.as_ref().map(|p| PathInfo {
60            path: p.clone(),
61            exists: Path::new(p).exists(),
62        }),
63    };
64
65    render_result(&CommandOutput::card_value("Paths Configuration", &output));
66
67    Ok(())
68}
69
70pub(super) fn execute_validate(config: &CliConfig) -> Result<()> {
71    let profile = ProfileBootstrap::get()?;
72
73    let mut validations: Vec<PathValidation> = Vec::new();
74
75    validations.push(PathValidation {
76        name: "system".to_owned(),
77        path: profile.paths.system.clone(),
78        exists: Path::new(&profile.paths.system).exists(),
79        required: true,
80    });
81
82    validations.push(PathValidation {
83        name: "services".to_owned(),
84        path: profile.paths.services.clone(),
85        exists: Path::new(&profile.paths.services).exists(),
86        required: true,
87    });
88
89    validations.push(PathValidation {
90        name: "bin".to_owned(),
91        path: profile.paths.bin.clone(),
92        exists: Path::new(&profile.paths.bin).exists(),
93        required: true,
94    });
95
96    if let Some(web_path) = &profile.paths.web_path {
97        validations.push(PathValidation {
98            name: "web_path".to_owned(),
99            path: web_path.clone(),
100            exists: Path::new(web_path).exists(),
101            required: false,
102        });
103    }
104
105    if let Some(storage) = &profile.paths.storage {
106        validations.push(PathValidation {
107            name: "storage".to_owned(),
108            path: storage.clone(),
109            exists: Path::new(storage).exists(),
110            required: false,
111        });
112    }
113
114    if let Some(geoip) = &profile.paths.geoip_database {
115        validations.push(PathValidation {
116            name: "geoip_database".to_owned(),
117            path: geoip.clone(),
118            exists: Path::new(geoip).exists(),
119            required: false,
120        });
121    }
122
123    let config_path = profile.paths.config();
124    validations.push(PathValidation {
125        name: "config".to_owned(),
126        path: config_path.clone(),
127        exists: Path::new(&config_path).exists(),
128        required: false,
129    });
130
131    let ai_config_path = profile.paths.ai_config();
132    validations.push(PathValidation {
133        name: "ai_config".to_owned(),
134        path: ai_config_path.clone(),
135        exists: Path::new(&ai_config_path).exists(),
136        required: false,
137    });
138
139    let content_config_path = profile.paths.content_config();
140    validations.push(PathValidation {
141        name: "content_config".to_owned(),
142        path: content_config_path.clone(),
143        exists: Path::new(&content_config_path).exists(),
144        required: false,
145    });
146
147    let valid = validations.iter().filter(|v| v.required).all(|v| v.exists);
148
149    let output = PathsValidateOutput {
150        valid,
151        paths: validations,
152    };
153
154    render_result(
155        &CommandOutput::table_of(vec!["name", "path", "exists", "required"], &output.paths)
156            .with_title("Paths Validation"),
157    );
158
159    if config.output_format() == OutputFormat::Table {
160        if valid {
161            CliService::success("All required paths exist");
162        } else {
163            CliService::error("Some required paths are missing");
164        }
165    }
166
167    Ok(())
168}