systemprompt_models/config/
validation.rs1use 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}