Skip to main content

systemprompt_models/config/
validation.rs

1use systemprompt_traits::validation_report::{
2    ValidationError, ValidationReport, ValidationWarning,
3};
4
5use crate::errors::ConfigError;
6use crate::profile::Profile;
7
8pub fn validate_profile_paths(profile: &Profile, _profile_path: &str) -> ValidationReport {
9    let mut report = ValidationReport::new("paths");
10
11    validate_required_path(&mut report, "system", &profile.paths.system);
12    validate_required_path(&mut report, "services", &profile.paths.services);
13    validate_required_path(&mut report, "bin", &profile.paths.bin);
14
15    validate_required_path(&mut report, "skills", &profile.paths.skills());
16    validate_required_path(&mut report, "config", &profile.paths.config());
17    validate_required_path(&mut report, "web_path", &profile.paths.web_path_resolved());
18    validate_required_path(&mut report, "web_config", &profile.paths.web_config());
19    validate_required_path(&mut report, "web_metadata", &profile.paths.web_metadata());
20    validate_required_path(
21        &mut report,
22        "content_config",
23        &profile.paths.content_config(),
24    );
25
26    validate_optional_path(
27        &mut report,
28        "geoip_database",
29        profile.paths.geoip_database.as_ref(),
30    );
31    validate_optional_path(&mut report, "storage", profile.paths.storage.as_ref());
32
33    report
34}
35
36pub fn validate_required_path(report: &mut ValidationReport, field: &str, path: &str) {
37    if path.is_empty() {
38        report.add_error(
39            ValidationError::new(format!("paths.{}", field), "Required path not configured")
40                .with_suggestion(format!(
41                    "Add paths.{} to your profile or run 'systemprompt cloud config'",
42                    field
43                )),
44        );
45        return;
46    }
47
48    let path_buf = std::path::Path::new(path);
49    if !path_buf.exists() {
50        report.add_error(
51            ValidationError::new(format!("paths.{}", field), "Path does not exist")
52                .with_path(path)
53                .with_suggestion("Create the directory/file or update the path in your profile"),
54        );
55    }
56}
57
58pub fn validate_required_optional_path(
59    report: &mut ValidationReport,
60    field: &str,
61    path: Option<&String>,
62) {
63    match path {
64        None => {
65            report.add_error(
66                ValidationError::new(format!("paths.{}", field), "Required path not configured")
67                    .with_suggestion(format!(
68                        "Add paths.{} to your profile or run 'systemprompt cloud config'",
69                        field
70                    )),
71            );
72        },
73        Some(p) if p.is_empty() => {
74            report.add_error(
75                ValidationError::new(format!("paths.{}", field), "Path is empty").with_suggestion(
76                    format!("Set a valid path for paths.{} in your profile", field),
77                ),
78            );
79        },
80        Some(p) => {
81            let path_buf = std::path::Path::new(p);
82            if !path_buf.exists() {
83                report.add_error(
84                    ValidationError::new(format!("paths.{}", field), "Path does not exist")
85                        .with_path(p)
86                        .with_suggestion("Create the directory/file or update the path"),
87                );
88            }
89        },
90    }
91}
92
93pub fn validate_optional_path(report: &mut ValidationReport, field: &str, path: Option<&String>) {
94    if let Some(p) = path {
95        if !p.is_empty() {
96            let path_buf = std::path::Path::new(p);
97            if !path_buf.exists() {
98                report.add_warning(
99                    ValidationWarning::new(
100                        format!("paths.{}", field),
101                        format!("Path does not exist: {}", p),
102                    )
103                    .with_suggestion("Create the path or remove the config entry"),
104                );
105            }
106        }
107    }
108}
109
110pub fn format_path_errors(report: &ValidationReport, profile_path: &str) -> String {
111    let mut output = String::new();
112    output.push_str("Profile Path Validation Failed\n\n");
113    output.push_str(&format!("Profile: {}\n\n", profile_path));
114    output.push_str("ERRORS:\n\n");
115
116    for error in &report.errors {
117        output.push_str(&format!("[{}] {}\n", report.domain, error.field));
118        output.push_str(&format!("  {}\n", error.message));
119        if let Some(ref path) = error.path {
120            output.push_str(&format!("  Path: {}\n", path.display()));
121        }
122        if let Some(ref suggestion) = error.suggestion {
123            output.push_str(&format!("  To fix: {}\n", suggestion));
124        }
125        output.push('\n');
126    }
127
128    if !report.warnings.is_empty() {
129        output.push_str("WARNINGS:\n\n");
130        for warning in &report.warnings {
131            output.push_str(&format!("[{}] {}\n", report.domain, warning.field));
132            output.push_str(&format!("  {}\n", warning.message));
133            if let Some(ref suggestion) = warning.suggestion {
134                output.push_str(&format!("  To enable: {}\n", suggestion));
135            }
136            output.push('\n');
137        }
138    }
139
140    output
141}
142
143pub fn validate_postgres_url(url: &str) -> Result<(), ConfigError> {
144    if !url.starts_with("postgres://") && !url.starts_with("postgresql://") {
145        return Err(ConfigError::InvalidPostgresUrl);
146    }
147    Ok(())
148}