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//!
8//! Copyright (c) systemprompt.io — Business Source License 1.1.
9//! See <https://systemprompt.io> for licensing details.
10
11use anyhow::Result;
12use clap::Subcommand;
13use std::path::Path;
14use systemprompt_config::ProfileBootstrap;
15use systemprompt_logging::CliService;
16use systemprompt_models::Profile;
17
18use super::types::{PathInfo, PathValidation, PathsConfigOutput, PathsValidateOutput};
19use crate::CliConfig;
20use crate::cli_settings::OutputFormat;
21use crate::shared::{CommandOutput, render_result};
22
23#[derive(Debug, Clone, Copy, Subcommand)]
24pub enum PathsCommands {
25    #[command(about = "Show paths configuration", alias = "list")]
26    Show,
27
28    #[command(about = "Validate that all configured paths exist")]
29    Validate,
30}
31
32pub fn execute(command: PathsCommands, config: &CliConfig) -> Result<()> {
33    match command {
34        PathsCommands::Show => execute_show(config),
35        PathsCommands::Validate => execute_validate(config),
36    }
37}
38
39pub(super) fn execute_show(config: &CliConfig) -> Result<()> {
40    let profile = ProfileBootstrap::get()?;
41
42    let output = PathsConfigOutput {
43        system: PathInfo {
44            path: profile.paths.system.clone(),
45            exists: Path::new(&profile.paths.system).exists(),
46        },
47        services: PathInfo {
48            path: profile.paths.services.clone(),
49            exists: Path::new(&profile.paths.services).exists(),
50        },
51        bin: PathInfo {
52            path: profile.paths.bin.clone(),
53            exists: Path::new(&profile.paths.bin).exists(),
54        },
55        web_path: profile.paths.web_path.as_ref().map(|p| PathInfo {
56            path: p.clone(),
57            exists: Path::new(p).exists(),
58        }),
59        storage: profile.paths.storage.as_ref().map(|p| PathInfo {
60            path: p.clone(),
61            exists: Path::new(p).exists(),
62        }),
63        geoip_database: profile.paths.geoip_database.as_ref().map(|p| PathInfo {
64            path: p.clone(),
65            exists: Path::new(p).exists(),
66        }),
67    };
68
69    render_result(
70        &CommandOutput::card_value("Paths Configuration", &output),
71        config,
72    );
73
74    Ok(())
75}
76
77pub(super) fn execute_validate(config: &CliConfig) -> Result<()> {
78    let profile = ProfileBootstrap::get()?;
79
80    let validations = collect_path_validations(profile);
81    let valid = validations.iter().filter(|v| v.required).all(|v| v.exists);
82
83    let output = PathsValidateOutput {
84        valid,
85        paths: validations,
86    };
87
88    render_result(
89        &CommandOutput::table_of(vec!["name", "path", "exists", "required"], &output.paths)
90            .with_title("Paths Validation"),
91        config,
92    );
93
94    if config.output_format() == OutputFormat::Table {
95        if valid {
96            CliService::success("All required paths exist");
97        } else {
98            CliService::error("Some required paths are missing");
99        }
100    }
101
102    Ok(())
103}
104
105fn collect_path_validations(profile: &Profile) -> Vec<PathValidation> {
106    let mut validations = vec![
107        path_validation("system", &profile.paths.system, true),
108        path_validation("services", &profile.paths.services, true),
109        path_validation("bin", &profile.paths.bin, true),
110    ];
111
112    if let Some(web_path) = &profile.paths.web_path {
113        validations.push(path_validation("web_path", web_path, false));
114    }
115
116    if let Some(storage) = &profile.paths.storage {
117        validations.push(path_validation("storage", storage, false));
118    }
119
120    if let Some(geoip) = &profile.paths.geoip_database {
121        validations.push(path_validation("geoip_database", geoip, false));
122    }
123
124    validations.push(path_validation("config", &profile.paths.config(), false));
125    validations.push(path_validation(
126        "ai_config",
127        &profile.paths.ai_config(),
128        false,
129    ));
130    validations.push(path_validation(
131        "content_config",
132        &profile.paths.content_config(),
133        false,
134    ));
135
136    validations
137}
138
139fn path_validation(name: &str, path: &str, required: bool) -> PathValidation {
140    PathValidation {
141        name: name.to_owned(),
142        path: path.to_owned(),
143        exists: Path::new(path).exists(),
144        required,
145    }
146}