use crate::{
config::Config,
constraints::{exchange_factors::RawExchangeFactorsFile, generic::RawGenericConstraintsFile},
extensions::{
production_models::RawProductionModelFile, scalar_parameters::ScalarParametersFile,
},
initial_conditions::RawInitialConditions,
penalties::RawPenalties,
scenarios::{
correlation::RawCorrelationFile, load_factors::RawLoadFactorsFile,
non_controllable_factors::RawNcsFactorsFile,
},
stages::RawStagesFile,
system::{
buses::RawBusFile, energy_contracts::RawContractFile, hydros::RawHydroFile,
lines::RawLineFile, non_controllable::RawNcsFile, pumping_stations::RawPumpingFile,
thermals::RawThermalFile,
},
};
use serde_json::Error;
use serde_json::Value;
pub fn generate_schemas() -> Result<Vec<(String, Value)>, Error> {
let pairs: Vec<(&str, schemars::Schema)> = vec![
("config.schema.json", schemars::schema_for!(Config)),
("buses.schema.json", schemars::schema_for!(RawBusFile)),
("hydros.schema.json", schemars::schema_for!(RawHydroFile)),
(
"thermals.schema.json",
schemars::schema_for!(RawThermalFile),
),
("lines.schema.json", schemars::schema_for!(RawLineFile)),
(
"energy_contracts.schema.json",
schemars::schema_for!(RawContractFile),
),
(
"non_controllable_sources.schema.json",
schemars::schema_for!(RawNcsFile),
),
(
"pumping_stations.schema.json",
schemars::schema_for!(RawPumpingFile),
),
("stages.schema.json", schemars::schema_for!(RawStagesFile)),
("penalties.schema.json", schemars::schema_for!(RawPenalties)),
(
"generic_constraints.schema.json",
schemars::schema_for!(RawGenericConstraintsFile),
),
(
"exchange_factors.schema.json",
schemars::schema_for!(RawExchangeFactorsFile),
),
(
"load_factors.schema.json",
schemars::schema_for!(RawLoadFactorsFile),
),
(
"non_controllable_factors.schema.json",
schemars::schema_for!(RawNcsFactorsFile),
),
(
"correlation.schema.json",
schemars::schema_for!(RawCorrelationFile),
),
(
"initial_conditions.schema.json",
schemars::schema_for!(RawInitialConditions),
),
(
"production_models.schema.json",
schemars::schema_for!(RawProductionModelFile),
),
(
"scalar_parameters.schema.json",
schemars::schema_for!(ScalarParametersFile),
),
];
pairs
.into_iter()
.map(|(name, schema)| {
let value = serde_json::to_value(schema)?;
Ok((name.to_string(), value))
})
.collect()
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::panic)]
mod tests {
use super::*;
#[test]
fn test_generate_schemas_returns_expected_count() {
let schemas = generate_schemas().unwrap();
assert!(
schemas.len() >= 17,
"expected at least 17 schema entries, got {}",
schemas.len()
);
}
#[test]
fn test_all_schema_filenames_and_values_non_empty() {
let schemas = generate_schemas().unwrap();
for (name, value) in &schemas {
assert!(!name.is_empty(), "schema filename must not be empty");
assert!(!value.is_null(), "schema value must not be null for {name}");
}
}
#[test]
fn test_all_schemas_are_objects() {
let schemas = generate_schemas().unwrap();
for (name, value) in &schemas {
assert!(
value.is_object(),
"schema for {name} must be a JSON object, got: {value}"
);
}
}
#[test]
fn test_all_schemas_have_structure_keys() {
let schemas = generate_schemas().unwrap();
for (name, value) in &schemas {
let obj = value.as_object().unwrap_or_else(|| {
panic!("schema for {name} is not an object");
});
let has_properties = obj.contains_key("properties");
let has_one_of = obj.contains_key("oneOf");
let has_any_of = obj.contains_key("anyOf");
let has_defs = obj.contains_key("$defs");
assert!(
has_properties || has_one_of || has_any_of || has_defs,
"schema for {name} has no expected structural keys (properties/oneOf/anyOf/$defs)"
);
}
}
#[test]
fn test_config_schema_contains_expected_fields() {
let schemas = generate_schemas().unwrap();
let (_, config_schema) = schemas
.iter()
.find(|(name, _)| name == "config.schema.json")
.unwrap_or_else(|| panic!("config.schema.json not found in schemas"));
let props = config_schema
.pointer("/properties")
.unwrap_or_else(|| panic!("config schema has no /properties"));
let obj = props.as_object().unwrap_or_else(|| {
panic!("config schema /properties is not an object");
});
for expected_field in &["training", "simulation", "exports"] {
assert!(
obj.contains_key(*expected_field),
"config schema /properties should contain '{expected_field}'"
);
}
}
#[test]
fn test_buses_schema_contains_buses_array() {
let schemas = generate_schemas().unwrap();
let (_, buses_schema) = schemas
.iter()
.find(|(name, _)| name == "buses.schema.json")
.unwrap_or_else(|| panic!("buses.schema.json not found in schemas"));
let props = buses_schema
.pointer("/properties")
.unwrap_or_else(|| panic!("buses schema has no /properties"));
let obj = props.as_object().unwrap_or_else(|| {
panic!("buses schema /properties is not an object");
});
assert!(
obj.contains_key("buses"),
"buses schema /properties should contain 'buses'"
);
}
#[test]
fn test_all_expected_schema_filenames_present() {
let schemas = generate_schemas().unwrap();
let names: Vec<&str> = schemas.iter().map(|(n, _)| n.as_str()).collect();
let expected = [
"config.schema.json",
"buses.schema.json",
"hydros.schema.json",
"thermals.schema.json",
"lines.schema.json",
"energy_contracts.schema.json",
"non_controllable_sources.schema.json",
"pumping_stations.schema.json",
"stages.schema.json",
"penalties.schema.json",
"generic_constraints.schema.json",
"exchange_factors.schema.json",
"load_factors.schema.json",
"non_controllable_factors.schema.json",
];
for name in &expected {
assert!(
names.contains(name),
"expected schema '{name}' not found; got: {names:?}"
);
}
}
}