Skip to main content

systemprompt_config/services/
schema_validation.rs

1//! `JsonSchema`-driven validation helpers used by `build.rs` scripts
2//! and the `systemprompt cloud config` command.
3
4use std::path::Path;
5
6use schemars::JsonSchema;
7use serde::de::DeserializeOwned;
8use thiserror::Error;
9
10#[derive(Debug, Error)]
11#[non_exhaustive]
12pub enum ConfigValidationError {
13    #[error("Failed to read config file: {0}")]
14    Read(#[from] std::io::Error),
15
16    #[error("Failed to parse YAML config: {0}")]
17    Parse(#[from] serde_yaml::Error),
18
19    #[error("Schema validation failed: {0}")]
20    Schema(String),
21}
22
23pub fn validate_config<T: DeserializeOwned + JsonSchema>(
24    yaml_path: impl AsRef<Path>,
25) -> Result<T, ConfigValidationError> {
26    let content = std::fs::read_to_string(yaml_path)?;
27    let config: T = serde_yaml::from_str(&content)?;
28    Ok(config)
29}
30
31pub fn generate_schema<T: JsonSchema>() -> Result<serde_json::Value, serde_json::Error> {
32    let schema = schemars::schema_for!(T);
33    serde_json::to_value(schema)
34}
35
36pub fn validate_yaml_str<T: DeserializeOwned>(yaml: &str) -> Result<T, ConfigValidationError> {
37    let config: T = serde_yaml::from_str(yaml)?;
38    Ok(config)
39}
40
41pub type ConfigValidatorFn = fn(&str) -> Result<(), String>;
42
43pub fn build_validate_configs(configs: &[(&str, ConfigValidatorFn)]) {
44    for (path, validator) in configs {
45        emit_rerun(path);
46        if let Err(e) = validator(path) {
47            emit_failure(path, &e);
48        }
49    }
50}
51
52#[expect(
53    clippy::print_stdout,
54    reason = "build.rs requires writing cargo:rerun-if-changed directives to stdout"
55)]
56fn emit_rerun(path: &str) {
57    println!("cargo:rerun-if-changed={path}");
58}
59
60#[expect(
61    clippy::print_stderr,
62    clippy::exit,
63    reason = "build.rs failure path must write a diagnostic to stderr and abort with status 1"
64)]
65fn emit_failure(path: &str, message: &str) -> ! {
66    eprintln!("Config validation failed for {path}: {message}");
67    std::process::exit(1);
68}
69
70pub fn validate_yaml_file(
71    path: impl AsRef<Path>,
72) -> Result<serde_yaml::Value, ConfigValidationError> {
73    let content = std::fs::read_to_string(path)?;
74    let value: serde_yaml::Value = serde_yaml::from_str(&content)?;
75    Ok(value)
76}