1use std::path::Path;
2use yaml_schema::{Engine, RootSchema};
3
4pub mod config;
5pub mod checks;
6pub mod report;
7pub mod run;
8mod io;
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 =
41 RootSchema::load_from_str(SCHEMA_YAML).map_err(|err| {
42 Box::new(ConfigError(format!(
43 "failed to load embedded schema: {err}"
44 )))
45 })?;
46
47 let yaml_contents = std::fs::read_to_string(config_path)?;
48 let context = Engine::evaluate(&root_schema, &yaml_contents, false)
49 .map_err(|err| Box::new(ConfigError(format!("schema validation failed: {err}"))))?;
50
51 if context.has_errors() {
52 let errors = context.errors.borrow();
53 let message = errors
54 .iter()
55 .map(|error| error.to_string())
56 .collect::<Vec<_>>()
57 .join("\n");
58 return Err(Box::new(ConfigError(message)));
59 }
60 let config = config::parse_config(config_path)?;
61 if !options.entities.is_empty() {
62 run::validate_entities(&config, &options.entities)?;
63 }
64
65 Ok(())
66}
67
68pub fn load_config(config_path: &Path) -> FloeResult<config::RootConfig> {
69 config::parse_config(config_path)
70}