1use std::path::Path;
2use yaml_schema::{Engine, RootSchema};
3
4pub mod checks;
5pub mod config;
6mod io;
7pub mod report;
8pub mod run;
9
10pub use checks as check;
11pub use run::run;
12
13pub type FloeResult<T> = Result<T, Box<dyn std::error::Error + Send + Sync>>;
14
15const SCHEMA_YAML: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/config.schema.yaml"));
16
17#[derive(Debug)]
18pub(crate) struct ConfigError(pub(crate) String);
19
20impl std::fmt::Display for ConfigError {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 write!(f, "{}", self.0)
23 }
24}
25
26impl std::error::Error for ConfigError {}
27
28#[derive(Debug, Default)]
29pub struct ValidateOptions {
30 pub entities: Vec<String>,
31}
32
33#[derive(Debug, Default)]
34pub struct RunOptions {
35 pub run_id: Option<String>,
36 pub entities: Vec<String>,
37}
38
39pub fn validate(config_path: &Path, options: ValidateOptions) -> FloeResult<()> {
40 let root_schema = RootSchema::load_from_str(SCHEMA_YAML).map_err(|err| {
41 Box::new(ConfigError(format!(
42 "failed to load embedded schema: {err}"
43 )))
44 })?;
45
46 let yaml_contents = std::fs::read_to_string(config_path)?;
47 let context = Engine::evaluate(&root_schema, &yaml_contents, false)
48 .map_err(|err| Box::new(ConfigError(format!("schema validation failed: {err}"))))?;
49
50 if context.has_errors() {
51 let errors = context.errors.borrow();
52 let message = errors
53 .iter()
54 .map(|error| error.to_string())
55 .collect::<Vec<_>>()
56 .join("\n");
57 return Err(Box::new(ConfigError(message)));
58 }
59 let config = config::parse_config(config_path)?;
60 if !options.entities.is_empty() {
61 run::validate_entities(&config, &options.entities)?;
62 }
63
64 Ok(())
65}
66
67pub fn load_config(config_path: &Path) -> FloeResult<config::RootConfig> {
68 config::parse_config(config_path)
69}