From 90e42b3ed2f91f15600296195288b5028af0e07b Mon Sep 17 00:00:00 2001 From: James Tomlinson Date: Thu, 25 Apr 2024 11:55:59 +0100 Subject: [PATCH] feat: Add functionality to export a JSON schema of PywrModel. Use the schemars crate to export a JSON schema via the CLI. This needs implementing for the remaining model types. --- Cargo.toml | 1 + pywr-cli/Cargo.toml | 5 +++-- pywr-cli/src/main.rs | 19 ++++++++++++++++++- pywr-schema/Cargo.toml | 2 +- pywr-schema/src/data_tables/mod.rs | 11 ++++++----- pywr-schema/src/edge.rs | 4 +++- pywr-schema/src/error.rs | 2 ++ pywr-schema/src/metric.rs | 11 ++++++----- pywr-schema/src/metric_sets/mod.rs | 9 +++++---- pywr-schema/src/model.rs | 15 ++++++++------- .../src/nodes/annual_virtual_storage.rs | 16 ++++++++-------- pywr-schema/src/nodes/core.rs | 19 ++++++++++--------- pywr-schema/src/nodes/delay.rs | 3 ++- pywr-schema/src/nodes/loss_link.rs | 3 ++- pywr-schema/src/nodes/mod.rs | 9 +++++---- .../src/nodes/monthly_virtual_storage.rs | 5 +++-- pywr-schema/src/nodes/piecewise_link.rs | 5 +++-- pywr-schema/src/nodes/piecewise_storage.rs | 5 +++-- pywr-schema/src/nodes/river.rs | 3 ++- pywr-schema/src/nodes/river_gauge.rs | 3 ++- .../src/nodes/river_split_with_gauge.rs | 3 ++- .../src/nodes/rolling_virtual_storage.rs | 5 +++-- pywr-schema/src/nodes/virtual_storage.rs | 3 ++- .../src/nodes/water_treatment_works.rs | 3 ++- pywr-schema/src/outputs/csv.rs | 7 ++++--- pywr-schema/src/outputs/hdf.rs | 3 ++- pywr-schema/src/outputs/memory.rs | 5 +++-- pywr-schema/src/outputs/mod.rs | 3 ++- pywr-schema/src/parameters/aggregated.rs | 9 +++++---- .../src/parameters/asymmetric_switch.rs | 3 ++- pywr-schema/src/parameters/control_curves.rs | 9 +++++---- pywr-schema/src/parameters/core.rs | 15 ++++++++------- pywr-schema/src/parameters/delay.rs | 3 ++- pywr-schema/src/parameters/discount_factor.rs | 3 ++- pywr-schema/src/parameters/indexed_array.rs | 3 ++- pywr-schema/src/parameters/interpolated.rs | 3 ++- pywr-schema/src/parameters/mod.rs | 17 +++++++++-------- pywr-schema/src/parameters/offset.rs | 3 ++- pywr-schema/src/parameters/polynomial.rs | 3 ++- pywr-schema/src/parameters/profiles.rs | 17 +++++++++-------- pywr-schema/src/parameters/python.rs | 7 ++++--- pywr-schema/src/parameters/tables.rs | 3 ++- pywr-schema/src/parameters/thresholds.rs | 5 +++-- pywr-schema/src/timeseries/mod.rs | 5 +++-- pywr-schema/src/timeseries/polars_dataset.rs | 3 ++- 45 files changed, 177 insertions(+), 116 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e1967fd7..aa87edc0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,3 +48,4 @@ csv = "1.1" hdf5 = { git = "https://github.com/aldanor/hdf5-rust.git", package = "hdf5", features = ["static", "zlib"] } pywr-v1-schema = { git = "https://github.com/pywr/pywr-schema/", tag = "v0.12.0", package = "pywr-schema" } chrono = { version = "0.4.34" } +schemars = { version = "0.8.16", features = ["chrono"] } diff --git a/pywr-cli/Cargo.toml b/pywr-cli/Cargo.toml index 480d8f5f..c66dae7f 100644 --- a/pywr-cli/Cargo.toml +++ b/pywr-cli/Cargo.toml @@ -13,15 +13,16 @@ categories = ["science", "simulation"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -clap = { version="4.0", features=["derive"] } +clap = { version = "4.0", features = ["derive"] } anyhow = "1.0.69" tracing = { workspace = true } -tracing-subscriber = { version ="0.3.17", features=["env-filter"] } +tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } rand = "0.8.5" rand_chacha = "0.3.1" serde = { workspace = true } serde_json = { workspace = true } pywr-v1-schema = { workspace = true } +schemars = { workspace = true } pywr-core = { path = "../pywr-core" } pywr-schema = { path = "../pywr-schema" } diff --git a/pywr-cli/src/main.rs b/pywr-cli/src/main.rs index 4b4f3642..154d0840 100644 --- a/pywr-cli/src/main.rs +++ b/pywr-cli/src/main.rs @@ -2,7 +2,7 @@ mod tracing; use crate::tracing::setup_tracing; use ::tracing::info; -use anyhow::Result; +use anyhow::{Context, Result}; use clap::{Parser, Subcommand, ValueEnum}; #[cfg(feature = "ipm-ocl")] use pywr_core::solvers::{ClIpmF32Solver, ClIpmF64Solver, ClIpmSolverSettings}; @@ -15,6 +15,7 @@ use pywr_core::test_utils::make_random_model; use pywr_schema::model::{PywrModel, PywrMultiNetworkModel}; use rand::SeedableRng; use rand_chacha::ChaCha8Rng; +use schemars::schema_for; use std::fmt::{Display, Formatter}; use std::path::{Path, PathBuf}; @@ -109,6 +110,10 @@ enum Commands { #[arg(short, long, default_value_t=Solver::Clp)] solver: Solver, }, + ExportSchema { + /// Path to save the JSON schema. + out: PathBuf, + }, } fn main() -> Result<()> { @@ -140,6 +145,7 @@ fn main() -> Result<()> { num_scenarios, solver, } => run_random(*num_systems, *density, *num_scenarios, solver), + Commands::ExportSchema { out } => export_schema(out)?, }, None => {} } @@ -254,3 +260,14 @@ fn run_random(num_systems: usize, density: usize, num_scenarios: usize, solver: } .unwrap(); } + +fn export_schema(out_path: &Path) -> Result<()> { + let schema = schema_for!(PywrModel); + std::fs::write( + out_path, + serde_json::to_string_pretty(&schema).with_context(|| "Failed serialise Pywr schema".to_string())?, + ) + .with_context(|| format!("Failed to write file: {:?}", out_path))?; + + Ok(()) +} diff --git a/pywr-schema/Cargo.toml b/pywr-schema/Cargo.toml index 22184a9f..0a7b750c 100644 --- a/pywr-schema/Cargo.toml +++ b/pywr-schema/Cargo.toml @@ -20,7 +20,7 @@ pyo3 = { workspace = true, optional = true } pyo3-polars = { workspace = true, optional = true } strum = "0.26" strum_macros = "0.26" - +schemars = { workspace = true } hdf5 = { workspace = true, optional = true } csv = { workspace = true, optional = true } tracing = { workspace = true, optional = true } diff --git a/pywr-schema/src/data_tables/mod.rs b/pywr-schema/src/data_tables/mod.rs index 411e5935..da0f89e1 100644 --- a/pywr-schema/src/data_tables/mod.rs +++ b/pywr-schema/src/data_tables/mod.rs @@ -10,6 +10,7 @@ use pywr_v1_schema::parameters::TableDataRef as TableDataRefV1; use scalar::{ load_csv_row2_scalar_table_one, load_csv_row_col_scalar_table_one, load_csv_row_scalar_table_one, LoadedScalarTable, }; +use schemars::JsonSchema; #[cfg(feature = "core")] use std::collections::HashMap; use std::path::{Path, PathBuf}; @@ -19,7 +20,7 @@ use tracing::{debug, info}; #[cfg(feature = "core")] use vec::{load_csv_row2_vec_table_one, load_csv_row_vec_table_one, LoadedVecTable}; -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] #[serde(rename_all = "lowercase")] pub enum DataTableType { Scalar, @@ -31,7 +32,7 @@ pub enum DataTableFormat { CSV, } -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] #[serde(tag = "format", rename_all = "lowercase")] pub enum DataTable { CSV(CsvDataTable), @@ -52,7 +53,7 @@ impl DataTable { } } -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] #[serde(rename_all = "lowercase")] pub enum CsvDataTableLookup { Row(usize), @@ -61,7 +62,7 @@ pub enum CsvDataTableLookup { } /// An external table of data that can be referenced -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct CsvDataTable { pub name: String, #[serde(rename = "type")] @@ -234,7 +235,7 @@ impl LoadedTableCollection { } } -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct TableDataRef { pub table: String, pub column: Option, diff --git a/pywr-schema/src/edge.rs b/pywr-schema/src/edge.rs index 56df9da2..5b6b6acb 100644 --- a/pywr-schema/src/edge.rs +++ b/pywr-schema/src/edge.rs @@ -1,4 +1,6 @@ -#[derive(serde::Deserialize, serde::Serialize, Clone)] +use schemars::JsonSchema; + +#[derive(serde::Deserialize, serde::Serialize, Clone, JsonSchema)] pub struct Edge { pub from_node: String, pub to_node: String, diff --git a/pywr-schema/src/error.rs b/pywr-schema/src/error.rs index 76ef6434..530796ef 100644 --- a/pywr-schema/src/error.rs +++ b/pywr-schema/src/error.rs @@ -58,6 +58,8 @@ pub enum SchemaError { Timeseries(#[from] TimeseriesError), #[error("The output of literal constant values is not supported. This is because they do not have a unique identifier such as a name. If you would like to output a constant value please use a `Constant` parameter.")] LiteralConstantOutputNotSupported, + #[error("Chrono out of range error: {0}")] + OutOfRange(#[from] chrono::OutOfRange), } #[cfg(feature = "core")] diff --git a/pywr-schema/src/metric.rs b/pywr-schema/src/metric.rs index 16bd6e59..728e5243 100644 --- a/pywr-schema/src/metric.rs +++ b/pywr-schema/src/metric.rs @@ -13,11 +13,12 @@ use crate::ConversionError; #[cfg(feature = "core")] use pywr_core::{metric::MetricF64, models::MultiNetworkTransferIndex, recorders::OutputMetric}; use pywr_v1_schema::parameters::ParameterValue as ParameterValueV1; +use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use strum_macros::Display; /// Output metrics that can be recorded from a model run. -#[derive(Deserialize, Serialize, Clone, Debug, Display)] +#[derive(Deserialize, Serialize, Clone, Debug, Display, JsonSchema)] #[serde(tag = "type")] pub enum Metric { Constant { @@ -226,14 +227,14 @@ impl TryFromV1Parameter for Metric { } } -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] #[serde(tag = "type", content = "name")] pub enum TimeseriesColumns { Scenario(String), Column(String), } -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct TimeseriesReference { name: String, columns: TimeseriesColumns, @@ -249,7 +250,7 @@ impl TimeseriesReference { } } -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct NodeReference { /// The name of the node pub name: String, @@ -295,7 +296,7 @@ impl NodeReference { } } -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct ParameterReference { /// The name of the parameter pub name: String, diff --git a/pywr-schema/src/metric_sets/mod.rs b/pywr-schema/src/metric_sets/mod.rs index 30ed5757..ffe192fb 100644 --- a/pywr-schema/src/metric_sets/mod.rs +++ b/pywr-schema/src/metric_sets/mod.rs @@ -3,11 +3,12 @@ use crate::error::SchemaError; use crate::metric::Metric; #[cfg(feature = "core")] use crate::model::LoadArgs; +use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::num::NonZeroUsize; /// Aggregation function to apply over metric values. -#[derive(serde::Deserialize, serde::Serialize, Debug, Copy, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Copy, Clone, JsonSchema)] #[serde(tag = "type")] pub enum MetricAggFunc { Sum, @@ -30,7 +31,7 @@ impl From for pywr_core::recorders::AggregationFunction { } } -#[derive(serde::Deserialize, serde::Serialize, Debug, Copy, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Copy, Clone, JsonSchema)] #[serde(tag = "type")] pub enum MetricAggFrequency { Monthly, @@ -58,7 +59,7 @@ impl From for pywr_core::recorders::AggregationFrequency { /// /// If the metric set has a child aggregator then the aggregation will be performed over the /// aggregated values of the child aggregator. -#[derive(Deserialize, Serialize, Clone)] +#[derive(Deserialize, Serialize, Clone, JsonSchema)] pub struct MetricAggregator { /// Optional aggregation frequency. pub freq: Option, @@ -84,7 +85,7 @@ impl From for pywr_core::recorders::Aggregator { /// A metric set can optionally have an aggregator, which will apply an aggregation function /// over metrics set. If the aggregator has a defined frequency then the aggregation will result /// in multiple values (i.e. per each period implied by the frequency). -#[derive(Deserialize, Serialize, Clone)] +#[derive(Deserialize, Serialize, Clone, JsonSchema)] pub struct MetricSet { pub name: String, pub metrics: Vec, diff --git a/pywr-schema/src/model.rs b/pywr-schema/src/model.rs index 524c33f1..43901471 100644 --- a/pywr-schema/src/model.rs +++ b/pywr-schema/src/model.rs @@ -17,10 +17,11 @@ use chrono::NaiveTime; use chrono::{NaiveDate, NaiveDateTime}; #[cfg(feature = "core")] use pywr_core::{models::ModelDomain, timestep::TimestepDuration, PywrError}; +use schemars::JsonSchema; use std::path::{Path, PathBuf}; use std::str::FromStr; -#[derive(serde::Deserialize, serde::Serialize, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Clone, JsonSchema)] pub struct Metadata { pub title: String, pub description: Option, @@ -51,7 +52,7 @@ impl TryFrom for Metadata { } } -#[derive(serde::Deserialize, serde::Serialize, Clone, Debug)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Debug, JsonSchema)] #[serde(untagged)] pub enum Timestep { Days(i64), @@ -67,7 +68,7 @@ impl From for Timestep { } } -#[derive(serde::Deserialize, serde::Serialize, Clone, Copy, Debug)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Copy, Debug, JsonSchema)] #[serde(untagged)] pub enum DateType { Date(NaiveDate), @@ -83,7 +84,7 @@ impl From for DateType { } } -#[derive(serde::Deserialize, serde::Serialize, Clone, Debug)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Debug, JsonSchema)] pub struct Timestepper { pub start: DateType, pub end: DateType, @@ -132,7 +133,7 @@ impl From for pywr_core::timestep::Timestepper { } } -#[derive(serde::Deserialize, serde::Serialize, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Clone, JsonSchema)] pub struct Scenario { pub name: String, pub size: usize, @@ -150,7 +151,7 @@ pub struct LoadArgs<'a> { pub inter_network_transfers: &'a [PywrMultiNetworkTransfer], } -#[derive(serde::Deserialize, serde::Serialize, Clone, Default)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Default, JsonSchema)] pub struct PywrNetwork { pub nodes: Vec, pub edges: Vec, @@ -370,7 +371,7 @@ pub enum PywrNetworkRef { /// /// /// -#[derive(serde::Deserialize, serde::Serialize, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Clone, JsonSchema)] pub struct PywrModel { pub metadata: Metadata, pub timestepper: Timestepper, diff --git a/pywr-schema/src/nodes/annual_virtual_storage.rs b/pywr-schema/src/nodes/annual_virtual_storage.rs index fb04fddc..9e7529b8 100644 --- a/pywr-schema/src/nodes/annual_virtual_storage.rs +++ b/pywr-schema/src/nodes/annual_virtual_storage.rs @@ -13,12 +13,13 @@ use pywr_core::{ }; use pywr_schema_macros::PywrNode; use pywr_v1_schema::nodes::AnnualVirtualStorageNode as AnnualVirtualStorageNodeV1; +use schemars::JsonSchema; use std::collections::HashMap; -#[derive(serde::Deserialize, serde::Serialize, Clone, Debug)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Debug, JsonSchema)] pub struct AnnualReset { pub day: u8, - pub month: chrono::Month, + pub month: u8, pub use_initial_volume: bool, } @@ -26,13 +27,13 @@ impl Default for AnnualReset { fn default() -> Self { Self { day: 1, - month: chrono::Month::January, + month: 1, use_initial_volume: false, } } } -#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode, JsonSchema)] pub struct AnnualVirtualStorageNode { #[serde(flatten)] pub meta: NodeMeta, @@ -85,9 +86,10 @@ impl AnnualVirtualStorageNode { .map(|name| network.get_node_index_by_name(name.as_str(), None)) .collect::, _>>()?; + let reset_month = self.reset.month.try_into()?; let reset = VirtualStorageReset::DayOfYear { day: self.reset.day as u32, - month: self.reset.month, + month: reset_month, }; network.add_virtual_storage_node( @@ -168,8 +170,6 @@ impl TryFrom for AnnualVirtualStorageNode { }); }; - let month = chrono::Month::try_from(v1.reset_month as u8)?; - let n = Self { meta, nodes: v1.nodes, @@ -180,7 +180,7 @@ impl TryFrom for AnnualVirtualStorageNode { initial_volume, reset: AnnualReset { day: v1.reset_day as u8, - month, + month: v1.reset_month as u8, use_initial_volume: v1.reset_to_initial_volume, }, }; diff --git a/pywr-schema/src/nodes/core.rs b/pywr-schema/src/nodes/core.rs index 5efca2c1..7e0c28ed 100644 --- a/pywr-schema/src/nodes/core.rs +++ b/pywr-schema/src/nodes/core.rs @@ -19,9 +19,10 @@ use pywr_v1_schema::nodes::{ CatchmentNode as CatchmentNodeV1, InputNode as InputNodeV1, LinkNode as LinkNodeV1, OutputNode as OutputNodeV1, ReservoirNode as ReservoirNodeV1, StorageNode as StorageNodeV1, }; +use schemars::JsonSchema; use std::collections::HashMap; -#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode, JsonSchema)] pub struct InputNode { #[serde(flatten)] pub meta: NodeMeta, @@ -131,7 +132,7 @@ impl TryFrom for InputNode { } } -#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode, JsonSchema)] pub struct LinkNode { #[serde(flatten)] pub meta: NodeMeta, @@ -241,7 +242,7 @@ impl TryFrom for LinkNode { } } -#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode, JsonSchema)] pub struct OutputNode { #[serde(flatten)] pub meta: NodeMeta, @@ -356,7 +357,7 @@ impl TryFrom for OutputNode { } } -#[derive(serde::Deserialize, serde::Serialize, Clone, PartialEq, Copy, Debug)] +#[derive(serde::Deserialize, serde::Serialize, Clone, PartialEq, Copy, Debug, JsonSchema)] pub enum StorageInitialVolume { Absolute(f64), Proportional(f64), @@ -378,7 +379,7 @@ impl From for CoreStorageInitialVolume { } } -#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode, JsonSchema)] pub struct StorageNode { #[serde(flatten)] pub meta: NodeMeta, @@ -582,7 +583,7 @@ impl TryFrom for StorageNode { /// ``` /// )] -#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode, JsonSchema)] pub struct CatchmentNode { #[serde(flatten)] pub meta: NodeMeta, @@ -678,14 +679,14 @@ impl TryFrom for CatchmentNode { } } -#[derive(serde::Deserialize, serde::Serialize, Clone, Debug)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Debug, JsonSchema)] #[serde(tag = "type")] pub enum Factors { Proportion { factors: Vec }, Ratio { factors: Vec }, } -#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode, JsonSchema)] pub struct AggregatedNode { #[serde(flatten)] pub meta: NodeMeta, @@ -830,7 +831,7 @@ impl TryFrom for AggregatedNode { } } -#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode, JsonSchema)] pub struct AggregatedStorageNode { #[serde(flatten)] pub meta: NodeMeta, diff --git a/pywr-schema/src/nodes/delay.rs b/pywr-schema/src/nodes/delay.rs index 6fb7b3ef..860aaf20 100644 --- a/pywr-schema/src/nodes/delay.rs +++ b/pywr-schema/src/nodes/delay.rs @@ -10,6 +10,7 @@ use crate::parameters::ConstantValue; use pywr_core::metric::MetricF64; use pywr_schema_macros::PywrNode; use pywr_v1_schema::nodes::DelayNode as DelayNodeV1; +use schemars::JsonSchema; use std::collections::HashMap; #[doc = svgbobdoc::transform!( @@ -31,7 +32,7 @@ use std::collections::HashMap; /// ``` /// )] -#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode, JsonSchema)] pub struct DelayNode { #[serde(flatten)] pub meta: NodeMeta, diff --git a/pywr-schema/src/nodes/loss_link.rs b/pywr-schema/src/nodes/loss_link.rs index c501f906..18f3f444 100644 --- a/pywr-schema/src/nodes/loss_link.rs +++ b/pywr-schema/src/nodes/loss_link.rs @@ -10,6 +10,7 @@ use crate::parameters::TryIntoV2Parameter; use pywr_core::metric::MetricF64; use pywr_schema_macros::PywrNode; use pywr_v1_schema::nodes::LossLinkNode as LossLinkNodeV1; +use schemars::JsonSchema; use std::collections::HashMap; #[doc = svgbobdoc::transform!( @@ -29,7 +30,7 @@ use std::collections::HashMap; /// ``` /// )] -#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode, JsonSchema)] pub struct LossLinkNode { #[serde(flatten)] pub meta: NodeMeta, diff --git a/pywr-schema/src/nodes/mod.rs b/pywr-schema/src/nodes/mod.rs index 11946763..2d6e413d 100644 --- a/pywr-schema/src/nodes/mod.rs +++ b/pywr-schema/src/nodes/mod.rs @@ -41,12 +41,13 @@ use pywr_v1_schema::parameters::{ }; pub use river_gauge::RiverGaugeNode; pub use river_split_with_gauge::RiverSplitWithGaugeNode; +use schemars::JsonSchema; use std::collections::HashMap; use strum_macros::{Display, EnumDiscriminants, EnumString, IntoStaticStr, VariantNames}; pub use virtual_storage::VirtualStorageNode; pub use water_treatment_works::WaterTreatmentWorks; -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, Copy)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, Copy, JsonSchema)] pub struct NodePosition { #[serde(skip_serializing_if = "Option::is_none")] pub schematic: Option<(f32, f32)>, @@ -63,7 +64,7 @@ impl From for NodePosition { } } -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, Default)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, Default, JsonSchema)] pub struct NodeMeta { pub name: String, #[serde(skip_serializing_if = "Option::is_none")] @@ -85,7 +86,7 @@ impl From for NodeMeta { /// All possible attributes that could be produced by a node. /// /// -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, Copy, Display)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, Copy, Display, JsonSchema)] pub enum NodeAttribute { Inflow, Outflow, @@ -226,7 +227,7 @@ impl NodeBuilder { } } -#[derive(serde::Deserialize, serde::Serialize, Clone, EnumDiscriminants, Debug)] +#[derive(serde::Deserialize, serde::Serialize, Clone, EnumDiscriminants, Debug, JsonSchema)] #[serde(tag = "type")] #[strum_discriminants(derive(Display, IntoStaticStr, EnumString, VariantNames))] // This creates a separate enum called `NodeType` that is available in this module. diff --git a/pywr-schema/src/nodes/monthly_virtual_storage.rs b/pywr-schema/src/nodes/monthly_virtual_storage.rs index 0dc47139..ac5b36b2 100644 --- a/pywr-schema/src/nodes/monthly_virtual_storage.rs +++ b/pywr-schema/src/nodes/monthly_virtual_storage.rs @@ -13,9 +13,10 @@ use pywr_core::{ }; use pywr_schema_macros::PywrNode; use pywr_v1_schema::nodes::MonthlyVirtualStorageNode as MonthlyVirtualStorageNodeV1; +use schemars::JsonSchema; use std::collections::HashMap; -#[derive(serde::Deserialize, serde::Serialize, Clone, Debug)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Debug, JsonSchema)] pub struct NumberOfMonthsReset { pub months: u8, } @@ -26,7 +27,7 @@ impl Default for NumberOfMonthsReset { } } -#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode, JsonSchema)] pub struct MonthlyVirtualStorageNode { #[serde(flatten)] pub meta: NodeMeta, diff --git a/pywr-schema/src/nodes/piecewise_link.rs b/pywr-schema/src/nodes/piecewise_link.rs index 5d324eb4..600fc789 100644 --- a/pywr-schema/src/nodes/piecewise_link.rs +++ b/pywr-schema/src/nodes/piecewise_link.rs @@ -10,9 +10,10 @@ use crate::parameters::TryIntoV2Parameter; use pywr_core::metric::MetricF64; use pywr_schema_macros::PywrNode; use pywr_v1_schema::nodes::PiecewiseLinkNode as PiecewiseLinkNodeV1; +use schemars::JsonSchema; use std::collections::HashMap; -#[derive(serde::Deserialize, serde::Serialize, Clone, Debug)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Debug, JsonSchema)] pub struct PiecewiseLinkStep { pub max_flow: Option, pub min_flow: Option, @@ -41,7 +42,7 @@ pub struct PiecewiseLinkStep { /// ``` /// )] -#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode, JsonSchema)] pub struct PiecewiseLinkNode { #[serde(flatten)] pub meta: NodeMeta, diff --git a/pywr-schema/src/nodes/piecewise_storage.rs b/pywr-schema/src/nodes/piecewise_storage.rs index 6f580416..2a32c87f 100644 --- a/pywr-schema/src/nodes/piecewise_storage.rs +++ b/pywr-schema/src/nodes/piecewise_storage.rs @@ -12,9 +12,10 @@ use pywr_core::{ parameters::VolumeBetweenControlCurvesParameter, }; use pywr_schema_macros::PywrNode; +use schemars::JsonSchema; use std::collections::HashMap; -#[derive(serde::Deserialize, serde::Serialize, Clone, Debug)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Debug, JsonSchema)] pub struct PiecewiseStore { pub control_curve: Metric, pub cost: Option, @@ -46,7 +47,7 @@ pub struct PiecewiseStore { /// ``` /// )] -#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode, JsonSchema)] pub struct PiecewiseStorageNode { #[serde(flatten)] pub meta: NodeMeta, diff --git a/pywr-schema/src/nodes/river.rs b/pywr-schema/src/nodes/river.rs index 2b7bb4fc..340cc515 100644 --- a/pywr-schema/src/nodes/river.rs +++ b/pywr-schema/src/nodes/river.rs @@ -7,9 +7,10 @@ use crate::nodes::{NodeAttribute, NodeMeta}; use pywr_core::metric::MetricF64; use pywr_schema_macros::PywrNode; use pywr_v1_schema::nodes::LinkNode as LinkNodeV1; +use schemars::JsonSchema; use std::collections::HashMap; -#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode, JsonSchema)] pub struct RiverNode { #[serde(flatten)] pub meta: NodeMeta, diff --git a/pywr-schema/src/nodes/river_gauge.rs b/pywr-schema/src/nodes/river_gauge.rs index daa09998..b268272a 100644 --- a/pywr-schema/src/nodes/river_gauge.rs +++ b/pywr-schema/src/nodes/river_gauge.rs @@ -10,6 +10,7 @@ use crate::parameters::TryIntoV2Parameter; use pywr_core::metric::MetricF64; use pywr_schema_macros::PywrNode; use pywr_v1_schema::nodes::RiverGaugeNode as RiverGaugeNodeV1; +use schemars::JsonSchema; use std::collections::HashMap; #[doc = svgbobdoc::transform!( @@ -27,7 +28,7 @@ use std::collections::HashMap; /// ``` /// )] -#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode, JsonSchema)] pub struct RiverGaugeNode { #[serde(flatten)] pub meta: NodeMeta, diff --git a/pywr-schema/src/nodes/river_split_with_gauge.rs b/pywr-schema/src/nodes/river_split_with_gauge.rs index 95ad979c..e5a91721 100644 --- a/pywr-schema/src/nodes/river_split_with_gauge.rs +++ b/pywr-schema/src/nodes/river_split_with_gauge.rs @@ -10,6 +10,7 @@ use crate::parameters::TryIntoV2Parameter; use pywr_core::{aggregated_node::Factors, metric::MetricF64, node::NodeIndex}; use pywr_schema_macros::PywrNode; use pywr_v1_schema::nodes::RiverSplitWithGaugeNode as RiverSplitWithGaugeNodeV1; +use schemars::JsonSchema; use std::collections::HashMap; #[doc = svgbobdoc::transform!( @@ -34,7 +35,7 @@ use std::collections::HashMap; /// ``` /// )] -#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode, JsonSchema)] pub struct RiverSplitWithGaugeNode { #[serde(flatten)] pub meta: NodeMeta, diff --git a/pywr-schema/src/nodes/rolling_virtual_storage.rs b/pywr-schema/src/nodes/rolling_virtual_storage.rs index 3c637c92..c082fc85 100644 --- a/pywr-schema/src/nodes/rolling_virtual_storage.rs +++ b/pywr-schema/src/nodes/rolling_virtual_storage.rs @@ -16,13 +16,14 @@ use pywr_core::{ }; use pywr_schema_macros::PywrNode; use pywr_v1_schema::nodes::RollingVirtualStorageNode as RollingVirtualStorageNodeV1; +use schemars::JsonSchema; use std::collections::HashMap; use std::num::NonZeroUsize; /// The length of the rolling window. /// /// This can be specified in either days or time-steps. -#[derive(serde::Deserialize, serde::Serialize, Clone, Debug)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Debug, JsonSchema)] pub enum RollingWindow { Days(NonZeroUsize), Timesteps(NonZeroUsize), @@ -68,7 +69,7 @@ impl RollingWindow { /// The rolling virtual storage node is useful for representing rolling licences. For example, a 30-day or 90-day /// licence on a water abstraction. /// -#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode, JsonSchema)] pub struct RollingVirtualStorageNode { #[serde(flatten)] pub meta: NodeMeta, diff --git a/pywr-schema/src/nodes/virtual_storage.rs b/pywr-schema/src/nodes/virtual_storage.rs index 3291ad3b..5f5461fb 100644 --- a/pywr-schema/src/nodes/virtual_storage.rs +++ b/pywr-schema/src/nodes/virtual_storage.rs @@ -13,9 +13,10 @@ use pywr_core::{ }; use pywr_schema_macros::PywrNode; use pywr_v1_schema::nodes::VirtualStorageNode as VirtualStorageNodeV1; +use schemars::JsonSchema; use std::collections::HashMap; -#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode, JsonSchema)] pub struct VirtualStorageNode { #[serde(flatten)] pub meta: NodeMeta, diff --git a/pywr-schema/src/nodes/water_treatment_works.rs b/pywr-schema/src/nodes/water_treatment_works.rs index 10128700..a75a7716 100644 --- a/pywr-schema/src/nodes/water_treatment_works.rs +++ b/pywr-schema/src/nodes/water_treatment_works.rs @@ -9,6 +9,7 @@ use num::Zero; #[cfg(feature = "core")] use pywr_core::{aggregated_node::Factors, metric::MetricF64}; use pywr_schema_macros::PywrNode; +use schemars::JsonSchema; use std::collections::HashMap; #[doc = svgbobdoc::transform!( @@ -38,7 +39,7 @@ use std::collections::HashMap; /// ``` /// )] -#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode)] +#[derive(serde::Deserialize, serde::Serialize, Clone, Default, Debug, PywrNode, JsonSchema)] pub struct WaterTreatmentWorks { /// Node metadata #[serde(flatten)] diff --git a/pywr-schema/src/outputs/csv.rs b/pywr-schema/src/outputs/csv.rs index 2269ea06..88f0e694 100644 --- a/pywr-schema/src/outputs/csv.rs +++ b/pywr-schema/src/outputs/csv.rs @@ -2,11 +2,12 @@ use crate::error::SchemaError; #[cfg(feature = "core")] use pywr_core::recorders::{CsvLongFmtOutput, CsvWideFmtOutput, Recorder}; +use schemars::JsonSchema; #[cfg(feature = "core")] use std::path::Path; use std::path::PathBuf; -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, Default)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, Default, JsonSchema)] #[serde(rename_all = "lowercase")] pub enum CsvFormat { Wide, @@ -14,7 +15,7 @@ pub enum CsvFormat { Long, } -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] #[serde(untagged)] pub enum CsvMetricSet { Single(String), @@ -32,7 +33,7 @@ pub enum CsvMetricSet { /// The long format supports either a single metric set or a list of metric sets. However, /// the wide format only supports a single metric set. /// -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct CsvOutput { pub name: String, pub filename: PathBuf, diff --git a/pywr-schema/src/outputs/hdf.rs b/pywr-schema/src/outputs/hdf.rs index 1564debb..13af281b 100644 --- a/pywr-schema/src/outputs/hdf.rs +++ b/pywr-schema/src/outputs/hdf.rs @@ -2,11 +2,12 @@ use crate::error::SchemaError; #[cfg(feature = "core")] use pywr_core::recorders::HDF5Recorder; +use schemars::JsonSchema; #[cfg(feature = "core")] use std::path::Path; use std::path::PathBuf; -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct Hdf5Output { pub name: String, pub filename: PathBuf, diff --git a/pywr-schema/src/outputs/memory.rs b/pywr-schema/src/outputs/memory.rs index 85536bf8..d4b27157 100644 --- a/pywr-schema/src/outputs/memory.rs +++ b/pywr-schema/src/outputs/memory.rs @@ -3,8 +3,9 @@ use crate::metric_sets::MetricAggFunc; use crate::SchemaError; #[cfg(feature = "core")] use pywr_core::recorders::MemoryRecorder; +use schemars::JsonSchema; -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct MemoryAggregation { pub time: Option, pub scenario: Option, @@ -22,7 +23,7 @@ impl From for pywr_core::recorders::Aggregation { } } -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct MemoryOutput { pub name: String, pub metric_set: String, diff --git a/pywr-schema/src/outputs/mod.rs b/pywr-schema/src/outputs/mod.rs index 8affc249..0804af8d 100644 --- a/pywr-schema/src/outputs/mod.rs +++ b/pywr-schema/src/outputs/mod.rs @@ -7,10 +7,11 @@ pub use self::csv::CsvOutput; use crate::error::SchemaError; pub use hdf::Hdf5Output; pub use memory::MemoryOutput; +use schemars::JsonSchema; #[cfg(feature = "core")] use std::path::Path; -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] #[serde(tag = "type")] pub enum Output { CSV(CsvOutput), diff --git a/pywr-schema/src/parameters/aggregated.rs b/pywr-schema/src/parameters/aggregated.rs index b49d7f00..9ed75634 100644 --- a/pywr-schema/src/parameters/aggregated.rs +++ b/pywr-schema/src/parameters/aggregated.rs @@ -11,10 +11,11 @@ use pywr_v1_schema::parameters::{ AggFunc as AggFuncV1, AggregatedIndexParameter as AggregatedIndexParameterV1, AggregatedParameter as AggregatedParameterV1, IndexAggFunc as IndexAggFuncV1, }; +use schemars::JsonSchema; use std::collections::HashMap; // TODO complete these -#[derive(serde::Deserialize, serde::Serialize, Debug, Copy, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Copy, Clone, JsonSchema)] #[serde(rename_all = "lowercase")] pub enum AggFunc { Sum, @@ -69,7 +70,7 @@ impl From for AggFunc { #[doc = include_str!("doc_examples/aggregated_1.json")] /// ``` -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct AggregatedParameter { #[serde(flatten)] pub meta: ParameterMeta, @@ -122,7 +123,7 @@ impl TryFromV1Parameter for AggregatedParameter { } // TODO complete these -#[derive(serde::Deserialize, serde::Serialize, Debug, Copy, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Copy, Clone, JsonSchema)] #[serde(rename_all = "lowercase")] pub enum IndexAggFunc { Sum, @@ -160,7 +161,7 @@ impl From for IndexAggFunc { } } -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct AggregatedIndexParameter { #[serde(flatten)] pub meta: ParameterMeta, diff --git a/pywr-schema/src/parameters/asymmetric_switch.rs b/pywr-schema/src/parameters/asymmetric_switch.rs index d8505c4d..d68a7f52 100644 --- a/pywr-schema/src/parameters/asymmetric_switch.rs +++ b/pywr-schema/src/parameters/asymmetric_switch.rs @@ -9,9 +9,10 @@ use crate::parameters::{ #[cfg(feature = "core")] use pywr_core::parameters::ParameterIndex; use pywr_v1_schema::parameters::AsymmetricSwitchIndexParameter as AsymmetricSwitchIndexParameterV1; +use schemars::JsonSchema; use std::collections::HashMap; -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct AsymmetricSwitchIndexParameter { #[serde(flatten)] pub meta: ParameterMeta, diff --git a/pywr-schema/src/parameters/control_curves.rs b/pywr-schema/src/parameters/control_curves.rs index 9c7e883c..7475eb19 100644 --- a/pywr-schema/src/parameters/control_curves.rs +++ b/pywr-schema/src/parameters/control_curves.rs @@ -14,8 +14,9 @@ use pywr_v1_schema::parameters::{ ControlCurveParameter as ControlCurveParameterV1, ControlCurvePiecewiseInterpolatedParameter as ControlCurvePiecewiseInterpolatedParameterV1, }; +use schemars::JsonSchema; -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct ControlCurveInterpolatedParameter { #[serde(flatten)] pub meta: ParameterMeta, @@ -116,7 +117,7 @@ impl TryFromV1Parameter for ControlCurveInt } } -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct ControlCurveIndexParameter { #[serde(flatten)] pub meta: ParameterMeta, @@ -223,7 +224,7 @@ impl TryFromV1Parameter for ControlCurveIndexParameter } } -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct ControlCurveParameter { #[serde(flatten)] pub meta: ParameterMeta, @@ -312,7 +313,7 @@ impl TryFromV1Parameter for ControlCurveParameter { } } -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct ControlCurvePiecewiseInterpolatedParameter { #[serde(flatten)] pub meta: ParameterMeta, diff --git a/pywr-schema/src/parameters/core.rs b/pywr-schema/src/parameters/core.rs index 15c69d3e..77d49fad 100644 --- a/pywr-schema/src/parameters/core.rs +++ b/pywr-schema/src/parameters/core.rs @@ -14,6 +14,7 @@ use pywr_v1_schema::parameters::{ MinParameter as MinParameterV1, NegativeMaxParameter as NegativeMaxParameterV1, NegativeMinParameter as NegativeMinParameterV1, NegativeParameter as NegativeParameterV1, }; +use schemars::JsonSchema; use std::collections::HashMap; /// Activation function or transformation to apply to variable value. @@ -146,7 +147,7 @@ pub struct VariableSettings { #[doc = include_str!("doc_examples/constant_variable.json")] /// ``` /// -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct ConstantParameter { /// Meta-data. /// @@ -204,7 +205,7 @@ impl TryFromV1Parameter for ConstantParameter { } } -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct MaxParameter { #[serde(flatten)] pub meta: ParameterMeta, @@ -269,7 +270,7 @@ impl TryFromV1Parameter for MaxParameter { /// ```json #[doc = include_str!("doc_examples/division.json")] /// ``` -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct DivisionParameter { #[serde(flatten)] pub meta: ParameterMeta, @@ -330,7 +331,7 @@ impl TryFromV1Parameter for DivisionParameter { /// ```json #[doc = include_str!("doc_examples/min.json")] /// ``` -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct MinParameter { #[serde(flatten)] pub meta: ParameterMeta, @@ -380,7 +381,7 @@ impl TryFromV1Parameter for MinParameter { } } -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct NegativeParameter { #[serde(flatten)] pub meta: ParameterMeta, @@ -443,7 +444,7 @@ impl TryFromV1Parameter for NegativeParameter { /// ``` /// In January this parameter returns 2, in February 4. /// -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct NegativeMaxParameter { #[serde(flatten)] pub meta: ParameterMeta, @@ -503,7 +504,7 @@ impl TryFromV1Parameter for NegativeMaxParameter { /// ``` /// In January this parameter returns 1, in February 2. /// -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct NegativeMinParameter { #[serde(flatten)] pub meta: ParameterMeta, diff --git a/pywr-schema/src/parameters/delay.rs b/pywr-schema/src/parameters/delay.rs index 911d75c8..b5fc9b67 100644 --- a/pywr-schema/src/parameters/delay.rs +++ b/pywr-schema/src/parameters/delay.rs @@ -6,10 +6,11 @@ use crate::model::LoadArgs; use crate::parameters::{DynamicFloatValueType, ParameterMeta}; #[cfg(feature = "core")] use pywr_core::parameters::ParameterIndex; +use schemars::JsonSchema; use std::collections::HashMap; /// A parameter that delays a value from the network by a number of time-steps. -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct DelayParameter { #[serde(flatten)] pub meta: ParameterMeta, diff --git a/pywr-schema/src/parameters/discount_factor.rs b/pywr-schema/src/parameters/discount_factor.rs index 6c133915..1820cc95 100644 --- a/pywr-schema/src/parameters/discount_factor.rs +++ b/pywr-schema/src/parameters/discount_factor.rs @@ -8,10 +8,11 @@ use crate::ConversionError; #[cfg(feature = "core")] use pywr_core::parameters::ParameterIndex; use pywr_v1_schema::parameters::DiscountFactorParameter as DiscountFactorParameterV1; +use schemars::JsonSchema; use std::collections::HashMap; /// A parameter that returns the current discount factor for a given time-step. -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct DiscountFactorParameter { #[serde(flatten)] pub meta: ParameterMeta, diff --git a/pywr-schema/src/parameters/indexed_array.rs b/pywr-schema/src/parameters/indexed_array.rs index 53e621c9..e667d3d1 100644 --- a/pywr-schema/src/parameters/indexed_array.rs +++ b/pywr-schema/src/parameters/indexed_array.rs @@ -10,9 +10,10 @@ use crate::parameters::{ #[cfg(feature = "core")] use pywr_core::parameters::ParameterIndex; use pywr_v1_schema::parameters::IndexedArrayParameter as IndexedArrayParameterV1; +use schemars::JsonSchema; use std::collections::HashMap; -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct IndexedArrayParameter { #[serde(flatten)] pub meta: ParameterMeta, diff --git a/pywr-schema/src/parameters/interpolated.rs b/pywr-schema/src/parameters/interpolated.rs index fe780b5b..dd220460 100644 --- a/pywr-schema/src/parameters/interpolated.rs +++ b/pywr-schema/src/parameters/interpolated.rs @@ -13,13 +13,14 @@ use pywr_v1_schema::parameters::{ InterpolatedFlowParameter as InterpolatedFlowParameterV1, InterpolatedVolumeParameter as InterpolatedVolumeParameterV1, }; +use schemars::JsonSchema; use std::collections::HashMap; /// A parameter that interpolates a value to a function with given discrete data points. /// /// Internally this is implemented as a piecewise linear interpolation via /// [`pywr_core::parameters::InterpolatedParameter`]. -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct InterpolatedParameter { #[serde(flatten)] pub meta: ParameterMeta, diff --git a/pywr-schema/src/parameters/mod.rs b/pywr-schema/src/parameters/mod.rs index f0a2b4b2..30c5244b 100644 --- a/pywr-schema/src/parameters/mod.rs +++ b/pywr-schema/src/parameters/mod.rs @@ -62,10 +62,11 @@ use pywr_v1_schema::parameters::{ Parameter as ParameterV1, ParameterMeta as ParameterMetaV1, ParameterValue as ParameterValueV1, ParameterVec, TableIndex as TableIndexV1, TableIndexEntry as TableIndexEntryV1, }; +use schemars::JsonSchema; use std::path::PathBuf; use strum_macros::{Display, EnumDiscriminants, EnumString, IntoStaticStr, VariantNames}; -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct ParameterMeta { pub name: String, #[serde(skip_serializing_if = "Option::is_none")] @@ -144,7 +145,7 @@ impl FromV1Parameter> for ParameterMeta { } } -#[derive(serde::Deserialize, serde::Serialize, Debug, EnumDiscriminants, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, EnumDiscriminants, Clone, JsonSchema)] #[serde(tag = "type")] #[strum_discriminants(derive(Display, IntoStaticStr, EnumString, VariantNames))] // This creates a separate enum called `NodeType` that is available in this module. @@ -508,7 +509,7 @@ impl TryFromV1Parameter for ParameterOrTimeseries { /// An non-variable constant floating-point (f64) value /// /// This value can be a literal float or an external reference to an input table. -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] #[serde(untagged)] pub enum ConstantValue { Literal(T), @@ -560,7 +561,7 @@ impl TryFrom for ConstantValue { } /// An integer (i64) value from another parameter -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] #[serde(untagged)] pub enum ParameterIndexValue { Reference(String), @@ -603,7 +604,7 @@ impl ParameterIndexValue { /// /// This value can be a constant (literal or otherwise) or a dynamic value provided /// by another parameter. -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] #[serde(untagged)] pub enum DynamicIndexValue { Constant(ConstantValue), @@ -660,7 +661,7 @@ impl TryFromV1Parameter for DynamicIndexValue { /// An non-variable vector of constant floating-point (f64) values /// /// This value can be a literal vector of floats or an external reference to an input table. -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] #[serde(untagged)] pub enum ConstantFloatVec { Literal(Vec), @@ -680,7 +681,7 @@ impl ConstantFloatVec { } } -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct ExternalDataRef { url: PathBuf, column: Option, @@ -706,7 +707,7 @@ impl TryFrom for ExternalDataRef { } } -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] #[serde(untagged)] pub enum TableIndex { Single(String), diff --git a/pywr-schema/src/parameters/offset.rs b/pywr-schema/src/parameters/offset.rs index e347eb96..56e11630 100644 --- a/pywr-schema/src/parameters/offset.rs +++ b/pywr-schema/src/parameters/offset.rs @@ -6,6 +6,7 @@ use crate::model::LoadArgs; use crate::parameters::{ConstantValue, DynamicFloatValueType, ParameterMeta}; #[cfg(feature = "core")] use pywr_core::parameters::ParameterIndex; +use schemars::JsonSchema; use std::collections::HashMap; /// A parameter that returns a fixed delta from another metric. @@ -22,7 +23,7 @@ use std::collections::HashMap; #[doc = include_str!("doc_examples/offset_variable.json")] /// ``` /// -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct OffsetParameter { /// Meta-data. /// diff --git a/pywr-schema/src/parameters/polynomial.rs b/pywr-schema/src/parameters/polynomial.rs index 5731e1bf..8665c00e 100644 --- a/pywr-schema/src/parameters/polynomial.rs +++ b/pywr-schema/src/parameters/polynomial.rs @@ -5,9 +5,10 @@ use crate::parameters::{DynamicFloatValueType, IntoV2Parameter, ParameterMeta, T #[cfg(feature = "core")] use pywr_core::parameters::ParameterIndex; use pywr_v1_schema::parameters::Polynomial1DParameter as Polynomial1DParameterV1; +use schemars::JsonSchema; use std::collections::HashMap; -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct Polynomial1DParameter { #[serde(flatten)] pub meta: ParameterMeta, diff --git a/pywr-schema/src/parameters/profiles.rs b/pywr-schema/src/parameters/profiles.rs index 1fae272e..d38635c0 100644 --- a/pywr-schema/src/parameters/profiles.rs +++ b/pywr-schema/src/parameters/profiles.rs @@ -14,9 +14,10 @@ use pywr_v1_schema::parameters::{ UniformDrawdownProfileParameter as UniformDrawdownProfileParameterV1, WeeklyProfileParameter as WeeklyProfileParameterV1, }; +use schemars::JsonSchema; use std::collections::HashMap; -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct DailyProfileParameter { #[serde(flatten)] pub meta: ParameterMeta, @@ -73,7 +74,7 @@ impl TryFromV1Parameter for DailyProfileParameter { } } -#[derive(serde::Deserialize, serde::Serialize, Debug, Copy, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Copy, Clone, JsonSchema)] pub enum MonthlyInterpDay { First, Last, @@ -89,7 +90,7 @@ impl From for pywr_core::parameters::MonthlyInterpDay { } } -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct MonthlyProfileParameter { #[serde(flatten)] pub meta: ParameterMeta, @@ -165,7 +166,7 @@ impl TryFromV1Parameter for MonthlyProfileParameter { } } -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct UniformDrawdownProfileParameter { #[serde(flatten)] pub meta: ParameterMeta, @@ -235,7 +236,7 @@ impl TryFromV1Parameter for UniformDrawdownPr } /// Distance functions for radial basis function interpolation. -#[derive(serde::Deserialize, serde::Serialize, Debug, Copy, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Copy, Clone, JsonSchema)] pub enum RadialBasisFunction { Linear, Cubic, @@ -361,7 +362,7 @@ impl From for pywr_core::parameters::RbfProfileVaria #[doc = include_str!("doc_examples/rbf_2.json")] /// ``` /// -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct RbfProfileParameter { #[serde(flatten)] pub meta: ParameterMeta, @@ -469,7 +470,7 @@ impl TryFromV1Parameter for RbfProfileParameter { } } -#[derive(serde::Deserialize, serde::Serialize, Debug, Copy, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Copy, Clone, JsonSchema)] pub enum WeeklyInterpDay { First, Last, @@ -546,7 +547,7 @@ impl From for pywr_core::parameters::WeeklyInterpDay { /// The values in the last week are interpolated between `10` and `12` (i.e the value on 31st /// December). /// -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct WeeklyProfileParameter { #[serde(flatten)] pub meta: ParameterMeta, diff --git a/pywr-schema/src/parameters/python.rs b/pywr-schema/src/parameters/python.rs index bc087708..150ccd99 100644 --- a/pywr-schema/src/parameters/python.rs +++ b/pywr-schema/src/parameters/python.rs @@ -15,12 +15,13 @@ use pyo3::types::{PyDict, PyTuple}; use pyo3::{IntoPy, PyErr, PyObject, Python}; #[cfg(feature = "core")] use pywr_core::parameters::{ParameterType, PyParameter}; +use schemars::JsonSchema; #[cfg(feature = "core")] use serde_json::Value; use std::collections::HashMap; use std::path::PathBuf; -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] #[serde(rename_all = "lowercase")] pub enum PythonModule { Module(String), @@ -28,7 +29,7 @@ pub enum PythonModule { } /// The expected return type of the Python parameter. -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, Default)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, Default, JsonSchema)] #[serde(rename_all = "lowercase")] pub enum PythonReturnType { #[default] @@ -78,7 +79,7 @@ pub enum PythonReturnType { /// /// let parameter: Parameter = serde_json::from_str(data).unwrap(); /// ``` -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct PythonParameter { #[serde(flatten)] pub meta: ParameterMeta, diff --git a/pywr-schema/src/parameters/tables.rs b/pywr-schema/src/parameters/tables.rs index da496eda..8d372cb9 100644 --- a/pywr-schema/src/parameters/tables.rs +++ b/pywr-schema/src/parameters/tables.rs @@ -9,10 +9,11 @@ use ndarray::s; #[cfg(feature = "core")] use pywr_core::parameters::ParameterIndex; use pywr_v1_schema::parameters::TablesArrayParameter as TablesArrayParameterV1; +use schemars::JsonSchema; use std::collections::HashMap; use std::path::PathBuf; -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct TablesArrayParameter { #[serde(flatten)] pub meta: ParameterMeta, diff --git a/pywr-schema/src/parameters/thresholds.rs b/pywr-schema/src/parameters/thresholds.rs index 0e5160a2..161df19f 100644 --- a/pywr-schema/src/parameters/thresholds.rs +++ b/pywr-schema/src/parameters/thresholds.rs @@ -12,9 +12,10 @@ use pywr_core::parameters::ParameterIndex; use pywr_v1_schema::parameters::{ ParameterThresholdParameter as ParameterThresholdParameterV1, Predicate as PredicateV1, }; +use schemars::JsonSchema; use std::collections::HashMap; -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, Copy)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, Copy, JsonSchema)] pub enum Predicate { #[serde(alias = "<")] LT, @@ -53,7 +54,7 @@ impl From for pywr_core::parameters::Predicate { } } -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct ParameterThresholdParameter { #[serde(flatten)] pub meta: ParameterMeta, diff --git a/pywr-schema/src/timeseries/mod.rs b/pywr-schema/src/timeseries/mod.rs index d01bb152..a8f12fdb 100644 --- a/pywr-schema/src/timeseries/mod.rs +++ b/pywr-schema/src/timeseries/mod.rs @@ -18,6 +18,7 @@ use pywr_core::{ PywrError, }; use pywr_v1_schema::tables::TableVec; +use schemars::JsonSchema; use std::collections::HashMap; #[cfg(feature = "core")] use std::path::Path; @@ -51,14 +52,14 @@ pub enum TimeseriesError { PywrCore(#[from] PywrError), } -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] #[serde(tag = "type")] enum TimeseriesProvider { Pandas, Polars(PolarsDataset), } -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct Timeseries { #[serde(flatten)] meta: ParameterMeta, diff --git a/pywr-schema/src/timeseries/polars_dataset.rs b/pywr-schema/src/timeseries/polars_dataset.rs index 88b9cd4b..495dd6c2 100644 --- a/pywr-schema/src/timeseries/polars_dataset.rs +++ b/pywr-schema/src/timeseries/polars_dataset.rs @@ -1,6 +1,7 @@ +use schemars::JsonSchema; use std::path::PathBuf; -#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)] +#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, JsonSchema)] pub struct PolarsDataset { time_col: Option, url: PathBuf,