diff --git a/CHANGELOG.md b/CHANGELOG.md index 02f225f..16b68f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,14 +19,14 @@ - Removed generics from `SchemaGenerator` and `SchemaRenderer`. ```rust -# Before +// Before fn render( &mut self, schemas: &'gen IndexMap, references: &'gen HashSet, ) -> RenderResult; -# After +// After fn render(&mut self, schemas: IndexMap) -> RenderResult; ``` @@ -35,6 +35,7 @@ fn render(&mut self, schemas: IndexMap) -> RenderResult; - Added a `env` Cargo feature for toggling environment variable functionality. Enabled by default. - Added a `extends` Cargo feature for config extending functionality. Enabled by default. - Added a `validate` Cargo feature for toggling validation functionality. Enabled by default. +- Added a `schema_serde` Cargo feature for allowing the `Schema` to be serialized. - Reworked how parser and validator errors are rendered in the terminal. #### ⚙️ Internal diff --git a/Cargo.lock b/Cargo.lock index 873ad0f..d650b60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1628,6 +1628,7 @@ dependencies = [ "rust_decimal", "schematic_types", "semver", + "serde", "serde_json", "serde_yaml", "toml", diff --git a/crates/schematic/Cargo.toml b/crates/schematic/Cargo.toml index b01664e..a4aca9a 100644 --- a/crates/schematic/Cargo.toml +++ b/crates/schematic/Cargo.toml @@ -61,6 +61,7 @@ config = [ "schematic_macros/config", ] schema = ["dep:indexmap", "schematic_macros/schema"] +schema_serde = ["schema", "schematic_types/serde"] tracing = ["schematic_macros/tracing"] # Features @@ -102,6 +103,7 @@ schematic = { path = ".", features = [ "extends", "json", "schema", + "schema_serde", "toml", "renderer_json_schema", "renderer_template", diff --git a/crates/types/Cargo.toml b/crates/types/Cargo.toml index 410dcd8..2f5450c 100644 --- a/crates/types/Cargo.toml +++ b/crates/types/Cargo.toml @@ -15,6 +15,7 @@ rust_decimal = { workspace = true, optional = true } relative-path = { workspace = true, optional = true } url = { workspace = true, optional = true } semver = { workspace = true, optional = true } +serde = { workspace = true, optional = true } serde_json = { workspace = true, optional = true } serde_yaml = { workspace = true, optional = true } toml = { workspace = true, optional = true } @@ -27,6 +28,7 @@ schematic_types = { path = ".", features = [ "relative_path", "rust_decimal", "semver", + "serde", "serde_json", "serde_toml", "serde_yaml", @@ -41,6 +43,7 @@ regex = ["dep:regex"] relative_path = ["dep:relative-path"] rust_decimal = ["dep:rust_decimal"] semver = ["dep:semver"] +serde = ["dep:serde"] serde_json = ["dep:serde_json"] serde_toml = ["dep:toml"] serde_yaml = ["dep:serde_yaml"] diff --git a/crates/types/src/arrays.rs b/crates/types/src/arrays.rs index 6b6fbbb..90d7e71 100644 --- a/crates/types/src/arrays.rs +++ b/crates/types/src/arrays.rs @@ -2,13 +2,26 @@ use crate::*; use std::collections::{BTreeSet, HashSet}; #[derive(Clone, Debug, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct ArrayType { + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub contains: Option, + pub items_type: Box, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub max_contains: Option, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub max_length: Option, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub min_contains: Option, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub min_length: Option, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub unique: Option, } diff --git a/crates/types/src/bools.rs b/crates/types/src/bools.rs index 0f8d425..888046b 100644 --- a/crates/types/src/bools.rs +++ b/crates/types/src/bools.rs @@ -1,7 +1,9 @@ use crate::*; #[derive(Clone, Debug, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct BooleanType { + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub default: Option, } diff --git a/crates/types/src/enums.rs b/crates/types/src/enums.rs index 253c69f..76d6014 100644 --- a/crates/types/src/enums.rs +++ b/crates/types/src/enums.rs @@ -2,9 +2,14 @@ use crate::*; pub use indexmap::IndexMap; #[derive(Clone, Debug, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct EnumType { + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub default_index: Option, + pub values: Vec, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub variants: Option>>, } diff --git a/crates/types/src/literals.rs b/crates/types/src/literals.rs index eece264..6192652 100644 --- a/crates/types/src/literals.rs +++ b/crates/types/src/literals.rs @@ -1,4 +1,6 @@ #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", serde(tag = "type", content = "value"))] pub enum LiteralValue { Bool(bool), F32(f32), @@ -9,8 +11,11 @@ pub enum LiteralValue { } #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct LiteralType { + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub format: Option, + pub value: LiteralValue, } diff --git a/crates/types/src/numbers.rs b/crates/types/src/numbers.rs index 29e3146..6cd6294 100644 --- a/crates/types/src/numbers.rs +++ b/crates/types/src/numbers.rs @@ -1,6 +1,7 @@ use crate::*; #[derive(Clone, Debug, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum IntegerKind { Isize, I8, @@ -27,15 +28,32 @@ impl IntegerKind { } #[derive(Clone, Debug, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct IntegerType { + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub default: Option, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub enum_values: Option>, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub format: Option, + pub kind: IntegerKind, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub max: Option, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub max_exclusive: Option, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub min: Option, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub min_exclusive: Option, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub multiple_of: Option, } @@ -96,6 +114,7 @@ impl_int!(i64, IntegerKind::I64); impl_int!(i128, IntegerKind::I128); #[derive(Clone, Debug, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum FloatKind { #[default] F32, @@ -103,16 +122,35 @@ pub enum FloatKind { } #[derive(Clone, Debug, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct FloatType { + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub default: Option, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub enum_values: Option>, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub format: Option, + pub kind: FloatKind, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub max: Option, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub max_exclusive: Option, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub min: Option, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub min_exclusive: Option, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub multiple_of: Option, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub name: Option, } diff --git a/crates/types/src/objects.rs b/crates/types/src/objects.rs index fde8b34..532a43c 100644 --- a/crates/types/src/objects.rs +++ b/crates/types/src/objects.rs @@ -2,11 +2,19 @@ use crate::*; use std::collections::{BTreeMap, HashMap}; #[derive(Clone, Debug, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct ObjectType { pub key_type: Box, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub max_length: Option, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub min_length: Option, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub required: Option>, + pub value_type: Box, } diff --git a/crates/types/src/schema.rs b/crates/types/src/schema.rs index c77a6ec..cbbeda3 100644 --- a/crates/types/src/schema.rs +++ b/crates/types/src/schema.rs @@ -3,11 +3,20 @@ use std::ops::{Deref, DerefMut}; /// Describes the metadata and shape of a type. #[derive(Clone, Debug, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct Schema { + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub deprecated: Option, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub description: Option, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub name: Option, + + #[cfg_attr(feature = "serde", serde(skip_serializing))] pub nullable: bool, + pub ty: SchemaType, } @@ -172,11 +181,19 @@ impl From for SchemaType { /// Describes the metadata and shape of a field within a struct or enum. #[derive(Clone, Debug, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct SchemaField { + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub comment: Option, + pub schema: Schema, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub deprecated: Option, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub env_var: Option, + pub hidden: bool, pub nullable: bool, pub optional: bool, diff --git a/crates/types/src/schema_type.rs b/crates/types/src/schema_type.rs index bb22711..8f696f2 100644 --- a/crates/types/src/schema_type.rs +++ b/crates/types/src/schema_type.rs @@ -12,6 +12,8 @@ use crate::unions::*; /// All possible types within a schema. #[derive(Clone, Debug, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", serde(tag = "type", content = "value"))] pub enum SchemaType { Null, #[default] diff --git a/crates/types/src/strings.rs b/crates/types/src/strings.rs index 590e4d4..ed3a292 100644 --- a/crates/types/src/strings.rs +++ b/crates/types/src/strings.rs @@ -4,12 +4,24 @@ use std::path::{Path, PathBuf}; use std::time::{Duration, SystemTime}; #[derive(Clone, Debug, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct StringType { + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub default: Option, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub enum_values: Option>, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub format: Option, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub max_length: Option, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub min_length: Option, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub pattern: Option, } diff --git a/crates/types/src/structs.rs b/crates/types/src/structs.rs index bcd0a18..dfc8413 100644 --- a/crates/types/src/structs.rs +++ b/crates/types/src/structs.rs @@ -2,11 +2,15 @@ use crate::schema::SchemaField; use std::collections::BTreeMap; #[derive(Clone, Debug, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct StructType { pub fields: BTreeMap>, + // The type is a partial nested config, like `PartialConfig`. // This doesn't mean it's been partialized. pub partial: bool, + + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub required: Option>, } diff --git a/crates/types/src/tuples.rs b/crates/types/src/tuples.rs index 2b77ec1..87bba76 100644 --- a/crates/types/src/tuples.rs +++ b/crates/types/src/tuples.rs @@ -1,6 +1,7 @@ use crate::*; #[derive(Clone, Debug, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct TupleType { pub items_types: Vec>, } diff --git a/crates/types/src/unions.rs b/crates/types/src/unions.rs index 956a0a9..69b2f49 100644 --- a/crates/types/src/unions.rs +++ b/crates/types/src/unions.rs @@ -1,6 +1,7 @@ use crate::*; #[derive(Clone, Debug, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum UnionOperator { #[default] AnyOf, @@ -8,10 +9,15 @@ pub enum UnionOperator { } #[derive(Clone, Debug, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct UnionType { + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub default_index: Option, + pub partial: bool, + pub operator: UnionOperator, + pub variants_types: Vec>, }