Skip to content

Commit

Permalink
breaking: Rework generator/renderer APIs. (#132)
Browse files Browse the repository at this point in the history
  • Loading branch information
milesj authored Jul 28, 2024
1 parent 17e8407 commit a7819e1
Show file tree
Hide file tree
Showing 13 changed files with 91 additions and 116 deletions.
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, Schema>,
references: &'gen HashSet<String>,
) -> RenderResult;

# After
fn render(&mut self, schemas: IndexMap<String, Schema>) -> RenderResult;
```

#### 🚀 Updates

- Added a `env` Cargo feature for toggling environment variable functionality. Enabled by default.
Expand Down
16 changes: 5 additions & 11 deletions crates/schematic/src/schema/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>,
pub struct SchemaGenerator {
schemas: IndexMap<String, Schema>,

_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<T: Schematic>(&mut self) {
let schema = SchemaBuilder::build_root::<T>();
Expand Down Expand Up @@ -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);
Expand All @@ -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<P: AsRef<Path>, O, R: SchemaRenderer<'gen, O>>(
&'gen self,
pub fn generate<P: AsRef<Path>, O, R: SchemaRenderer<O>>(
&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() {
Expand Down
2 changes: 1 addition & 1 deletion crates/schematic/src/schema/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ mod renderer;
mod renderers;

pub use generator::*;
pub use indexmap::*;
pub use indexmap;
pub use renderer::*;
pub use schematic_types::*;

Expand Down
11 changes: 3 additions & 8 deletions crates/schematic/src/schema/renderer.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use indexmap::IndexMap;
use schematic_types::*;
use std::collections::HashSet;

pub type RenderResult<T = String> = miette::Result<T>;

/// 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<O = String> {
/// Return true of the provided name is a referenced type.
fn is_reference(&self, name: &str) -> bool;

Expand Down Expand Up @@ -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<String, Schema>,
references: &'gen HashSet<String>,
) -> RenderResult;
/// References between types can be resolved using the provided `schemas` map.
fn render(&mut self, schemas: IndexMap<String, Schema>) -> RenderResult;
}
20 changes: 8 additions & 12 deletions crates/schematic/src/schema/renderers/json_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>>,
references: HashSet<String>,
}

fn clean_comment(comment: String, allow_newlines: bool) -> String {
Expand Down Expand Up @@ -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(),
}
}

Expand Down Expand Up @@ -191,9 +191,9 @@ impl<'gen> JsonSchemaRenderer<'gen> {
}
}

impl<'gen> SchemaRenderer<'gen, JsonSchema> for JsonSchemaRenderer<'gen> {
impl SchemaRenderer<JsonSchema> 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<JsonSchema> {
Expand Down Expand Up @@ -528,12 +528,8 @@ impl<'gen> SchemaRenderer<'gen, JsonSchema> for JsonSchemaRenderer<'gen> {
}))
}

fn render(
&mut self,
schemas: &'gen IndexMap<String, Schema>,
references: &'gen HashSet<String>,
) -> RenderResult {
self.references = Some(references);
fn render(&mut self, schemas: IndexMap<String, Schema>) -> RenderResult {
self.references = HashSet::from_iter(schemas.keys().cloned());

let mut root_schema = RootSchema {
meta_schema: self.options.meta_schema.clone(),
Expand Down
4 changes: 2 additions & 2 deletions crates/schematic/src/schema/renderers/json_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 10 additions & 17 deletions crates/schematic/src/schema/renderers/jsonc_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, Schema>>,
schemas: IndexMap<String, Schema>,
}

impl<'gen> JsoncTemplateRenderer<'gen> {
impl JsoncTemplateRenderer {
#[allow(clippy::should_implement_trait)]
pub fn default() -> Self {
JsoncTemplateRenderer::new(TemplateOptions::default())
Expand All @@ -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<String> for JsoncTemplateRenderer {
fn is_reference(&self, _name: &str) -> bool {
false
}
Expand Down Expand Up @@ -99,10 +98,8 @@ impl<'gen> SchemaRenderer<'gen, String> for JsoncTemplateRenderer<'gen> {
}

fn render_reference(&mut self, reference: &str, _schema: &Schema) -> RenderResult<String> {
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)
Expand Down Expand Up @@ -160,14 +157,10 @@ impl<'gen> SchemaRenderer<'gen, String> for JsoncTemplateRenderer<'gen> {
render_unknown()
}

fn render(
&mut self,
schemas: &'gen IndexMap<String, Schema>,
_references: &'gen HashSet<String>,
) -> RenderResult {
self.schemas = Some(schemas);
fn render(&mut self, schemas: IndexMap<String, Schema>) -> 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
Expand Down
14 changes: 4 additions & 10 deletions crates/schematic/src/schema/renderers/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,20 +247,14 @@ impl TemplateContext {
self.stack.pop_back();
}

pub fn resolve_schema<'gen>(
&self,
initial: &'gen Schema,
schemas: &Option<&'gen IndexMap<String, Schema>>,
) -> &'gen Schema {
pub fn resolve_schema(&self, initial: &Schema, schemas: &IndexMap<String, Schema>) -> 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()
}
}

Expand Down
32 changes: 13 additions & 19 deletions crates/schematic/src/schema/renderers/toml_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -12,15 +12,15 @@ struct Section {
}

/// Renders TOML config templates.
pub struct TomlTemplateRenderer<'gen> {
pub struct TomlTemplateRenderer {
ctx: TemplateContext,
schemas: Option<&'gen IndexMap<String, Schema>>,
schemas: IndexMap<String, Schema>,

arrays: BTreeMap<String, Section>,
tables: BTreeMap<String, Section>,
}

impl<'gen> TomlTemplateRenderer<'gen> {
impl TomlTemplateRenderer {
#[allow(clippy::should_implement_trait)]
pub fn default() -> Self {
TomlTemplateRenderer::new(TemplateOptions::default())
Expand All @@ -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(),
}
Expand Down Expand Up @@ -91,7 +91,7 @@ impl<'gen> TomlTemplateRenderer<'gen> {
}
}

impl<'gen> SchemaRenderer<'gen, String> for TomlTemplateRenderer<'gen> {
impl SchemaRenderer<String> for TomlTemplateRenderer {
fn is_reference(&self, _name: &str) -> bool {
false
}
Expand All @@ -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<String> {
Expand Down Expand Up @@ -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 {
Expand All @@ -158,10 +158,8 @@ impl<'gen> SchemaRenderer<'gen, String> for TomlTemplateRenderer<'gen> {
}

fn render_reference(&mut self, reference: &str, _schema: &Schema) -> RenderResult<String> {
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)
Expand Down Expand Up @@ -205,14 +203,10 @@ impl<'gen> SchemaRenderer<'gen, String> for TomlTemplateRenderer<'gen> {
render_unknown()
}

fn render(
&mut self,
schemas: &'gen IndexMap<String, Schema>,
_references: &'gen HashSet<String>,
) -> RenderResult {
self.schemas = Some(schemas);
fn render(&mut self, schemas: IndexMap<String, Schema>) -> 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)
Expand Down
Loading

0 comments on commit a7819e1

Please sign in to comment.