diff --git a/CHANGELOG.md b/CHANGELOG.md index e29af64..d1e6b2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,12 +4,32 @@ #### 💥 Breaking +##### Config + - Refactored the internals of how validation errors work. - Removed `Config::META` and `ConfigError::META`. Use `Schematic::schema_name()` instead. - Removed `url` as a default Cargo feature. - Renamed `valid_*` Cargo features to `validate_*`. - Renamed some error enum variants. +##### Schema + +- Updated `SchemaRenderer::render` to receive an owned copy of the schemas. +- Removed references from `SchemaRenderer::render`. Use `schemas` keys instead. +- Removed generics from `SchemaGenerator` and `SchemaRenderer`. + +```rust +# Before +fn render( + &mut self, + schemas: &'gen IndexMap, + references: &'gen HashSet, +) -> RenderResult; + +# After +fn render(&mut self, schemas: IndexMap) -> RenderResult; +``` + #### 🚀 Updates - Added a `env` Cargo feature for toggling environment variable functionality. Enabled by default. diff --git a/crates/schematic/src/schema/generator.rs b/crates/schematic/src/schema/generator.rs index 87c4042..dfbcee7 100644 --- a/crates/schematic/src/schema/generator.rs +++ b/crates/schematic/src/schema/generator.rs @@ -2,21 +2,17 @@ use super::SchemaRenderer; use indexmap::IndexMap; use miette::IntoDiagnostic; use schematic_types::*; -use std::collections::HashSet; use std::fs; use std::path::Path; /// A generator collects [`Schema`]s and renders them to a specific file, /// using a renderer that implements [`SchemaRenderer`]. #[derive(Debug, Default)] -pub struct SchemaGenerator<'gen> { - references: HashSet, +pub struct SchemaGenerator { schemas: IndexMap, - - _marker: std::marker::PhantomData<&'gen ()>, } -impl<'gen> SchemaGenerator<'gen> { +impl SchemaGenerator { /// Add a [`Schema`] to be rendered, derived from the provided [`Schematic`]. pub fn add(&mut self) { let schema = SchemaBuilder::build_root::(); @@ -57,8 +53,6 @@ impl<'gen> SchemaGenerator<'gen> { // Store the name so that we can use it as a reference for other types if let Some(name) = &schema.name { - self.references.insert(name.to_owned()); - // Types without a name cannot be rendered at the root if !self.schemas.contains_key(name) { self.schemas.insert(name.to_owned(), schema); @@ -68,14 +62,14 @@ impl<'gen> SchemaGenerator<'gen> { /// Generate an output by rendering all collected [`Schema`]s using the provided /// [`SchemaRenderer`], and finally write to the provided file path. - pub fn generate, O, R: SchemaRenderer<'gen, O>>( - &'gen self, + pub fn generate, O, R: SchemaRenderer>( + &self, output_file: P, mut renderer: R, ) -> miette::Result<()> { let output_file = output_file.as_ref(); - let mut output = renderer.render(&self.schemas, &self.references)?; + let mut output = renderer.render(self.schemas.clone())?; output.push('\n'); if let Some(parent) = output_file.parent() { diff --git a/crates/schematic/src/schema/mod.rs b/crates/schematic/src/schema/mod.rs index f57d7e7..8879f45 100644 --- a/crates/schematic/src/schema/mod.rs +++ b/crates/schematic/src/schema/mod.rs @@ -3,7 +3,7 @@ mod renderer; mod renderers; pub use generator::*; -pub use indexmap::*; +pub use indexmap; pub use renderer::*; pub use schematic_types::*; diff --git a/crates/schematic/src/schema/renderer.rs b/crates/schematic/src/schema/renderer.rs index 3cc6c89..3758997 100644 --- a/crates/schematic/src/schema/renderer.rs +++ b/crates/schematic/src/schema/renderer.rs @@ -1,12 +1,11 @@ use indexmap::IndexMap; use schematic_types::*; -use std::collections::HashSet; pub type RenderResult = miette::Result; /// Renders [`SchemaType`]s to a distinct format (derived from generic `O`) /// for use within a [`SchemaGenerator`]. -pub trait SchemaRenderer<'gen, O = String> { +pub trait SchemaRenderer { /// Return true of the provided name is a referenced type. fn is_reference(&self, name: &str) -> bool; @@ -86,10 +85,6 @@ pub trait SchemaRenderer<'gen, O = String> { } /// Render the list of [`Schema`]s to a string, in the order they are listed. - /// References between types can be resolved using the provided `references` set. - fn render( - &mut self, - schemas: &'gen IndexMap, - references: &'gen HashSet, - ) -> RenderResult; + /// References between types can be resolved using the provided `schemas` map. + fn render(&mut self, schemas: IndexMap) -> RenderResult; } diff --git a/crates/schematic/src/schema/renderers/json_schema.rs b/crates/schematic/src/schema/renderers/json_schema.rs index cef7801..4e5eafc 100644 --- a/crates/schematic/src/schema/renderers/json_schema.rs +++ b/crates/schematic/src/schema/renderers/json_schema.rs @@ -52,9 +52,9 @@ impl Default for JsonSchemaOptions { /// Renders JSON schema documents from a schema. #[derive(Default)] -pub struct JsonSchemaRenderer<'gen> { +pub struct JsonSchemaRenderer { options: JsonSchemaOptions, - references: Option<&'gen HashSet>, + references: HashSet, } fn clean_comment(comment: String, allow_newlines: bool) -> String { @@ -132,11 +132,11 @@ fn lit_to_value(lit: &LiteralValue) -> Value { } } -impl<'gen> JsonSchemaRenderer<'gen> { +impl JsonSchemaRenderer { pub fn new(options: JsonSchemaOptions) -> Self { Self { options, - references: None, + references: HashSet::default(), } } @@ -191,9 +191,9 @@ impl<'gen> JsonSchemaRenderer<'gen> { } } -impl<'gen> SchemaRenderer<'gen, JsonSchema> for JsonSchemaRenderer<'gen> { +impl SchemaRenderer for JsonSchemaRenderer { fn is_reference(&self, name: &str) -> bool { - self.references.is_some_and(|refs| refs.contains(name)) + self.references.contains(name) } fn render_array(&mut self, array: &ArrayType, schema: &Schema) -> RenderResult { @@ -528,12 +528,8 @@ impl<'gen> SchemaRenderer<'gen, JsonSchema> for JsonSchemaRenderer<'gen> { })) } - fn render( - &mut self, - schemas: &'gen IndexMap, - references: &'gen HashSet, - ) -> RenderResult { - self.references = Some(references); + fn render(&mut self, schemas: IndexMap) -> RenderResult { + self.references = HashSet::from_iter(schemas.keys().cloned()); let mut root_schema = RootSchema { meta_schema: self.options.meta_schema.clone(), diff --git a/crates/schematic/src/schema/renderers/json_template.rs b/crates/schematic/src/schema/renderers/json_template.rs index 4e4cd8b..d6c0e93 100644 --- a/crates/schematic/src/schema/renderers/json_template.rs +++ b/crates/schematic/src/schema/renderers/json_template.rs @@ -9,11 +9,11 @@ use std::mem; pub struct JsonTemplateRenderer; impl JsonTemplateRenderer { - pub fn default<'gen>() -> JsoncTemplateRenderer<'gen> { + pub fn default() -> JsoncTemplateRenderer { Self::new(TemplateOptions::default()) } - pub fn new<'gen>(mut options: TemplateOptions) -> JsoncTemplateRenderer<'gen> { + pub fn new(mut options: TemplateOptions) -> JsoncTemplateRenderer { options.comments = false; options .hide_fields diff --git a/crates/schematic/src/schema/renderers/jsonc_template.rs b/crates/schematic/src/schema/renderers/jsonc_template.rs index a4c311d..977d4bc 100644 --- a/crates/schematic/src/schema/renderers/jsonc_template.rs +++ b/crates/schematic/src/schema/renderers/jsonc_template.rs @@ -3,15 +3,14 @@ use crate::format::Format; use crate::schema::{RenderResult, SchemaRenderer}; use indexmap::IndexMap; use schematic_types::*; -use std::collections::HashSet; /// Renders JSON config templates with comments. -pub struct JsoncTemplateRenderer<'gen> { +pub struct JsoncTemplateRenderer { ctx: TemplateContext, - schemas: Option<&'gen IndexMap>, + schemas: IndexMap, } -impl<'gen> JsoncTemplateRenderer<'gen> { +impl JsoncTemplateRenderer { #[allow(clippy::should_implement_trait)] pub fn default() -> Self { JsoncTemplateRenderer::new(TemplateOptions::default()) @@ -20,12 +19,12 @@ impl<'gen> JsoncTemplateRenderer<'gen> { pub fn new(options: TemplateOptions) -> Self { JsoncTemplateRenderer { ctx: TemplateContext::new(Format::Json, options), - schemas: None, + schemas: IndexMap::default(), } } } -impl<'gen> SchemaRenderer<'gen, String> for JsoncTemplateRenderer<'gen> { +impl SchemaRenderer for JsoncTemplateRenderer { fn is_reference(&self, _name: &str) -> bool { false } @@ -99,10 +98,8 @@ impl<'gen> SchemaRenderer<'gen, String> for JsoncTemplateRenderer<'gen> { } fn render_reference(&mut self, reference: &str, _schema: &Schema) -> RenderResult { - if let Some(schemas) = &self.schemas { - if let Some(schema) = schemas.get(reference) { - return self.render_schema_without_reference(schema); - } + if let Some(schema) = self.schemas.get(reference) { + return self.render_schema_without_reference(&schema.to_owned()); } render_reference(reference) @@ -160,14 +157,10 @@ impl<'gen> SchemaRenderer<'gen, String> for JsoncTemplateRenderer<'gen> { render_unknown() } - fn render( - &mut self, - schemas: &'gen IndexMap, - _references: &'gen HashSet, - ) -> RenderResult { - self.schemas = Some(schemas); + fn render(&mut self, schemas: IndexMap) -> RenderResult { + self.schemas = schemas; - let root = validate_root(schemas)?; + let root = validate_root(&self.schemas)?; let mut template = self.render_schema_without_reference(&root)?; // Inject the header and footer diff --git a/crates/schematic/src/schema/renderers/template.rs b/crates/schematic/src/schema/renderers/template.rs index 15a903b..909a4cf 100644 --- a/crates/schematic/src/schema/renderers/template.rs +++ b/crates/schematic/src/schema/renderers/template.rs @@ -247,20 +247,14 @@ impl TemplateContext { self.stack.pop_back(); } - pub fn resolve_schema<'gen>( - &self, - initial: &'gen Schema, - schemas: &Option<&'gen IndexMap>, - ) -> &'gen Schema { + pub fn resolve_schema(&self, initial: &Schema, schemas: &IndexMap) -> Schema { if let SchemaType::Reference(name) = &initial.ty { - if let Some(schemas) = schemas { - if let Some(schema) = schemas.get(name) { - return schema; - } + if let Some(schema) = schemas.get(name) { + return schema.to_owned(); } } - initial + initial.to_owned() } } diff --git a/crates/schematic/src/schema/renderers/toml_template.rs b/crates/schematic/src/schema/renderers/toml_template.rs index 9aa13b1..c647e80 100644 --- a/crates/schematic/src/schema/renderers/toml_template.rs +++ b/crates/schematic/src/schema/renderers/toml_template.rs @@ -3,7 +3,7 @@ use crate::format::Format; use crate::schema::{RenderResult, SchemaRenderer}; use indexmap::IndexMap; use schematic_types::*; -use std::collections::{BTreeMap, HashSet}; +use std::collections::BTreeMap; use std::mem; struct Section { @@ -12,15 +12,15 @@ struct Section { } /// Renders TOML config templates. -pub struct TomlTemplateRenderer<'gen> { +pub struct TomlTemplateRenderer { ctx: TemplateContext, - schemas: Option<&'gen IndexMap>, + schemas: IndexMap, arrays: BTreeMap, tables: BTreeMap, } -impl<'gen> TomlTemplateRenderer<'gen> { +impl TomlTemplateRenderer { #[allow(clippy::should_implement_trait)] pub fn default() -> Self { TomlTemplateRenderer::new(TemplateOptions::default()) @@ -29,7 +29,7 @@ impl<'gen> TomlTemplateRenderer<'gen> { pub fn new(options: TemplateOptions) -> Self { TomlTemplateRenderer { ctx: TemplateContext::new(Format::Toml, options), - schemas: None, + schemas: IndexMap::default(), arrays: BTreeMap::new(), tables: BTreeMap::new(), } @@ -91,7 +91,7 @@ impl<'gen> TomlTemplateRenderer<'gen> { } } -impl<'gen> SchemaRenderer<'gen, String> for TomlTemplateRenderer<'gen> { +impl SchemaRenderer for TomlTemplateRenderer { fn is_reference(&self, _name: &str) -> bool { false } @@ -105,7 +105,7 @@ impl<'gen> SchemaRenderer<'gen, String> for TomlTemplateRenderer<'gen> { let items_type = self.ctx.resolve_schema(&array.items_type, &self.schemas); - Ok(format!("[{}]", self.render_schema(items_type)?)) + Ok(format!("[{}]", self.render_schema(&items_type)?)) } fn render_boolean(&mut self, boolean: &BooleanType, _schema: &Schema) -> RenderResult { @@ -145,7 +145,7 @@ impl<'gen> SchemaRenderer<'gen, String> for TomlTemplateRenderer<'gen> { // Objects are inline, so we can't show comments self.ctx.options.comments = false; - let value = self.render_schema(value_type)?; + let value = self.render_schema(&value_type)?; let mut key = self.render_schema(&object.key_type)?; if key == EMPTY_STRING { @@ -158,10 +158,8 @@ impl<'gen> SchemaRenderer<'gen, String> for TomlTemplateRenderer<'gen> { } fn render_reference(&mut self, reference: &str, _schema: &Schema) -> RenderResult { - if let Some(schemas) = &self.schemas { - if let Some(schema) = schemas.get(reference) { - return self.render_schema_without_reference(schema); - } + if let Some(schema) = self.schemas.get(reference) { + return self.render_schema_without_reference(&schema.to_owned()); } render_reference(reference) @@ -205,14 +203,10 @@ impl<'gen> SchemaRenderer<'gen, String> for TomlTemplateRenderer<'gen> { render_unknown() } - fn render( - &mut self, - schemas: &'gen IndexMap, - _references: &'gen HashSet, - ) -> RenderResult { - self.schemas = Some(schemas); + fn render(&mut self, schemas: IndexMap) -> RenderResult { + self.schemas = schemas; - let mut root = validate_root(schemas)?; + let mut root = validate_root(&self.schemas)?; let fake_schema = Schema::default(); // Recursively extract all sections (arrays, objects) diff --git a/crates/schematic/src/schema/renderers/typescript.rs b/crates/schematic/src/schema/renderers/typescript.rs index 495c371..29982b4 100644 --- a/crates/schematic/src/schema/renderers/typescript.rs +++ b/crates/schematic/src/schema/renderers/typescript.rs @@ -68,18 +68,18 @@ pub struct TypeScriptOptions { /// Renders TypeScript types from a schema. #[derive(Default)] -pub struct TypeScriptRenderer<'gen> { +pub struct TypeScriptRenderer { depth: usize, options: TypeScriptOptions, - references: Option<&'gen HashSet>, + references: HashSet, } -impl<'gen> TypeScriptRenderer<'gen> { +impl TypeScriptRenderer { pub fn new(options: TypeScriptOptions) -> Self { Self { depth: 0, options, - references: None, + references: HashSet::default(), } } @@ -309,13 +309,13 @@ impl<'gen> TypeScriptRenderer<'gen> { } } -impl<'gen> SchemaRenderer<'gen, String> for TypeScriptRenderer<'gen> { +impl SchemaRenderer for TypeScriptRenderer { fn is_reference(&self, name: &str) -> bool { if self.options.disable_references { return false; } - if self.references.is_some_and(|refs| refs.contains(name)) { + if self.references.contains(name) { return true; } @@ -492,12 +492,8 @@ impl<'gen> SchemaRenderer<'gen, String> for TypeScriptRenderer<'gen> { Ok("unknown".into()) } - fn render( - &mut self, - schemas: &'gen IndexMap, - references: &'gen HashSet, - ) -> RenderResult { - self.references = Some(references); + fn render(&mut self, schemas: IndexMap) -> RenderResult { + self.references = HashSet::from_iter(schemas.keys().cloned()); let mut outputs = vec![ "// Automatically generated by schematic. DO NOT MODIFY!".to_string(), @@ -520,7 +516,7 @@ impl<'gen> SchemaRenderer<'gen, String> for TypeScriptRenderer<'gen> { outputs.push(imports.join("\n")); } - for (name, schema) in schemas { + for (name, schema) in &schemas { if self.is_excluded(name) { continue; } diff --git a/crates/schematic/src/schema/renderers/yaml_template.rs b/crates/schematic/src/schema/renderers/yaml_template.rs index fd8c9b6..945341d 100644 --- a/crates/schematic/src/schema/renderers/yaml_template.rs +++ b/crates/schematic/src/schema/renderers/yaml_template.rs @@ -3,15 +3,14 @@ use crate::format::Format; use crate::schema::{RenderResult, SchemaRenderer}; use indexmap::IndexMap; use schematic_types::*; -use std::collections::HashSet; /// Renders YAML config templates. -pub struct YamlTemplateRenderer<'gen> { +pub struct YamlTemplateRenderer { ctx: TemplateContext, - schemas: Option<&'gen IndexMap>, + schemas: IndexMap, } -impl<'gen> YamlTemplateRenderer<'gen> { +impl YamlTemplateRenderer { #[allow(clippy::should_implement_trait)] pub fn default() -> Self { YamlTemplateRenderer::new(TemplateOptions::default()) @@ -20,12 +19,12 @@ impl<'gen> YamlTemplateRenderer<'gen> { pub fn new(options: TemplateOptions) -> Self { YamlTemplateRenderer { ctx: TemplateContext::new(Format::Yaml, options), - schemas: None, + schemas: IndexMap::default(), } } } -impl<'gen> SchemaRenderer<'gen, String> for YamlTemplateRenderer<'gen> { +impl SchemaRenderer for YamlTemplateRenderer { fn is_reference(&self, _name: &str) -> bool { false } @@ -40,12 +39,12 @@ impl<'gen> SchemaRenderer<'gen, String> for YamlTemplateRenderer<'gen> { let items_type = self.ctx.resolve_schema(&array.items_type, &self.schemas); if !items_type.is_struct() { - return Ok(format!("[{}]", self.render_schema(items_type)?)); + return Ok(format!("[{}]", self.render_schema(&items_type)?)); } self.ctx.depth += 2; - let mut item = self.render_schema(items_type)?; + let mut item = self.render_schema(&items_type)?; self.ctx.depth -= 2; @@ -88,7 +87,7 @@ impl<'gen> SchemaRenderer<'gen, String> for YamlTemplateRenderer<'gen> { self.ctx.depth += 2; - let value = self.render_schema(value_type)?; + let value = self.render_schema(&value_type)?; self.ctx.depth -= 1; @@ -106,10 +105,8 @@ impl<'gen> SchemaRenderer<'gen, String> for YamlTemplateRenderer<'gen> { } fn render_reference(&mut self, reference: &str, _schema: &Schema) -> RenderResult { - if let Some(schemas) = &self.schemas { - if let Some(schema) = schemas.get(reference) { - return self.render_schema_without_reference(schema); - } + if let Some(schema) = self.schemas.get(reference) { + return self.render_schema_without_reference(&schema.to_owned()); } render_reference(reference) @@ -172,14 +169,10 @@ impl<'gen> SchemaRenderer<'gen, String> for YamlTemplateRenderer<'gen> { render_unknown() } - fn render( - &mut self, - schemas: &'gen IndexMap, - _references: &'gen HashSet, - ) -> RenderResult { - self.schemas = Some(schemas); + fn render(&mut self, schemas: IndexMap) -> RenderResult { + self.schemas = schemas; - let root = validate_root(schemas)?; + let root = validate_root(&self.schemas)?; let mut template = self.render_schema_without_reference(&root)?; // Inject the header and footer diff --git a/crates/schematic/tests/generator_test.rs b/crates/schematic/tests/generator_test.rs index 80434db..6dddcfb 100644 --- a/crates/schematic/tests/generator_test.rs +++ b/crates/schematic/tests/generator_test.rs @@ -139,13 +139,13 @@ struct TemplateConfig { expand_object_primitive: HashMap, } -fn create_generator() -> SchemaGenerator<'static> { +fn create_generator() -> SchemaGenerator { let mut generator = SchemaGenerator::default(); generator.add::(); generator } -fn create_template_generator() -> SchemaGenerator<'static> { +fn create_template_generator() -> SchemaGenerator { let mut generator = SchemaGenerator::default(); generator.add::(); generator diff --git a/crates/schematic/tests/macro_enum_test.rs b/crates/schematic/tests/macro_enum_test.rs index ccadec1..36a38ee 100644 --- a/crates/schematic/tests/macro_enum_test.rs +++ b/crates/schematic/tests/macro_enum_test.rs @@ -149,7 +149,7 @@ enum AdjacentTagged { Qux(SomeConfig), } -fn create_gen() -> schema::SchemaGenerator<'static> { +fn create_gen() -> schema::SchemaGenerator { let mut generator = schema::SchemaGenerator::default(); generator.add::(); generator.add::();