From c548449753a9c110e1007cba5d318fea92bdb5dc Mon Sep 17 00:00:00 2001 From: Miles Johnson Date: Mon, 5 Feb 2024 15:34:25 -0800 Subject: [PATCH] Add miette feature. --- CHANGELOG.md | 3 ++ crates/schematic/Cargo.toml | 9 ++-- crates/schematic/src/config/errors.rs | 57 +++++++++++++----------- crates/schematic/src/config/format.rs | 11 +++-- crates/schematic/src/config/validator.rs | 7 +-- crates/schematic/src/schema/generator.rs | 25 ++++++++--- crates/schematic/src/schema/renderer.rs | 4 ++ crates/test-app/Cargo.toml | 1 + 8 files changed, 75 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df15a393..055f94b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ #### 💥 Breaking +- Moved the `miette` diagnostics and functionality behind a `miette` feature flag. + - Miette will also be enabled when using a renderer. - Removed `type_version_spec` and `type_warpgate` features (use the `schematic` feature on the crate instead). - Renamed renderer related features: @@ -23,6 +25,7 @@ #### ⚙️ Internal - Updated `garde` (validation) to v0.18. +- Updated `miette` to v7. ## 0.13.7 diff --git a/crates/schematic/Cargo.toml b/crates/schematic/Cargo.toml index a6838920..403e86be 100644 --- a/crates/schematic/Cargo.toml +++ b/crates/schematic/Cargo.toml @@ -19,7 +19,7 @@ all-features = true [dependencies] schematic_macros = { version = "0.13.6", path = "../macros" } schematic_types = { version = "0.5.5", path = "../types" } -miette = { workspace = true } +miette = { workspace = true, optional = true } thiserror = "1.0.56" tracing = "0.1.40" @@ -60,14 +60,15 @@ config = [ "schematic_macros/config", ] json = ["dep:serde_json"] +miette = ["dep:miette"] schema = ["dep:indexmap", "schematic_macros/schema"] toml = ["dep:toml"] url = ["dep:reqwest"] yaml = ["dep:serde_yaml"] -renderer_json_schema = ["dep:schemars", "json", "schema"] -renderer_template = [] -renderer_typescript = ["schema"] +renderer_json_schema = ["dep:schemars", "json", "miette", "schema"] +renderer_template = ["miette"] +renderer_typescript = ["miette", "schema"] type_chrono = ["schematic_types/chrono"] type_indexmap = ["schematic_types/indexmap"] diff --git a/crates/schematic/src/config/errors.rs b/crates/schematic/src/config/errors.rs index 46c03276..8cf9eb9d 100644 --- a/crates/schematic/src/config/errors.rs +++ b/crates/schematic/src/config/errors.rs @@ -1,52 +1,55 @@ use crate::config::validator::ValidatorError; -use miette::{Diagnostic, SourceSpan}; use starbase_styles::{Style, Stylize}; use std::path::PathBuf; use thiserror::Error; /// All configuration based errors. -#[derive(Error, Debug, Diagnostic)] +#[derive(Error, Debug)] +#[cfg_attr(feature = "miette", derive(miette::Diagnostic))] pub enum ConfigError { #[error("{0}")] Message(String), - #[diagnostic(code(config::enums::invalid_fallback))] + #[cfg_attr(feature = "miette", diagnostic(code(config::enums::invalid_fallback)))] #[error("Invalid fallback variant {}, unable to parse type.", .0.style(Style::Symbol))] EnumInvalidFallback(String), - #[diagnostic(code(config::enums::unknown_variant))] + #[cfg_attr(feature = "miette", diagnostic(code(config::enums::unknown_variant)))] #[error("Unknown enum variant {}.", .0.style(Style::Id))] EnumUnknownVariant(String), - #[diagnostic(code(config::code::extends))] + #[cfg_attr(feature = "miette", diagnostic(code(config::code::extends)))] #[error("Unable to extend, expected a file path or URL.")] ExtendsFromNoCode, - #[diagnostic(code(config::file::extends))] + #[cfg_attr(feature = "miette", diagnostic(code(config::file::extends)))] #[error("Extending from a file is only allowed if the parent source is also a file.")] ExtendsFromParentFileOnly, - #[diagnostic(code(config::code::invalid))] + #[cfg_attr(feature = "miette", diagnostic(code(config::code::invalid)))] #[error("Invalid raw code used as a source.")] InvalidCode, - #[diagnostic(code(config::default::invalid))] + #[cfg_attr(feature = "miette", diagnostic(code(config::default::invalid)))] #[error("Invalid default value. {0}")] InvalidDefault(String), - #[diagnostic(code(config::env::invalid))] + #[cfg_attr(feature = "miette", diagnostic(code(config::env::invalid)))] #[error("Invalid environment variable {}. {1}", .0.style(Style::Symbol))] InvalidEnvVar(String, String), - #[diagnostic(code(config::file::invalid))] + #[cfg_attr(feature = "miette", diagnostic(code(config::file::invalid)))] #[error("Invalid file path used as a source.")] InvalidFile, - #[diagnostic(code(config::file::missing), help("Is the path absolute?"))] + #[cfg_attr( + feature = "miette", + diagnostic(code(config::file::missing), help("Is the path absolute?")) + )] #[error("File path {} does not exist.", .0.style(Style::Path))] MissingFile(PathBuf), - #[diagnostic(code(config::file::read_failed))] + #[cfg_attr(feature = "miette", diagnostic(code(config::file::read_failed)))] #[error("Failed to read file {}.", .path.style(Style::Path))] ReadFileFailed { path: PathBuf, @@ -54,12 +57,12 @@ pub enum ConfigError { error: std::io::Error, }, - #[diagnostic(code(config::url::invalid))] + #[cfg_attr(feature = "miette", diagnostic(code(config::url::invalid)))] #[error("Invalid URL used as a source.")] InvalidUrl, #[cfg(feature = "url")] - #[diagnostic(code(config::url::read_failed))] + #[cfg_attr(feature = "miette", diagnostic(code(config::url::read_failed)))] #[error("Failed to read URL {}.", .url.style(Style::Url))] ReadUrlFailed { url: String, @@ -67,40 +70,40 @@ pub enum ConfigError { error: reqwest::Error, }, - #[diagnostic(code(config::url::https_only))] + #[cfg_attr(feature = "miette", diagnostic(code(config::url::https_only)))] #[error("Only secure URLs are allowed, received {}.", .0.style(Style::Url))] HttpsOnly(String), - #[diagnostic(code(config::format::unsupported))] + #[cfg_attr(feature = "miette", diagnostic(code(config::format::unsupported)))] #[error("Unsupported format for {0}, expected {1}.")] UnsupportedFormat(String, String), // Parser - #[diagnostic(code(config::parse::failed))] + #[cfg_attr(feature = "miette", diagnostic(code(config::parse::failed)))] #[error("Failed to parse {}.", .config.style(Style::File))] Parser { config: String, - #[diagnostic_source] + #[cfg_attr(feature = "miette", diagnostic_source)] #[source] error: ParserError, - #[help] + #[cfg_attr(feature = "miette", help)] help: Option, }, // Validator - #[diagnostic(code(config::validate::failed))] + #[cfg_attr(feature = "miette", diagnostic(code(config::validate::failed)))] #[error("Failed to validate {}.", .config.style(Style::File))] Validator { config: String, // This includes the vertical red line which we don't want! - // #[diagnostic_source] + // #[cfg_attr(feature = "miette", diagnostic_source] #[source] error: ValidatorError, - #[help] + #[cfg_attr(feature = "miette", help)] help: Option, }, } @@ -146,17 +149,19 @@ impl ConfigError { } /// Error related to serde parsing. -#[derive(Error, Debug, Diagnostic)] +#[derive(Error, Debug)] #[error("{}{} {message}", .path.style(Style::Id), ":".style(Style::MutedLight))] -#[diagnostic(severity(Error))] +#[cfg_attr(feature = "miette", derive(miette::Diagnostic))] +#[cfg_attr(feature = "miette", diagnostic(severity(Error)))] pub struct ParserError { - #[source_code] + #[cfg_attr(feature = "miette", source_code)] pub content: String, // NamedSource, pub message: String, pub path: String, + #[cfg(feature = "miette")] #[label("Fix this")] - pub span: Option, + pub span: Option, } diff --git a/crates/schematic/src/config/format.rs b/crates/schematic/src/config/format.rs index 0a8c1e60..1dd07454 100644 --- a/crates/schematic/src/config/format.rs +++ b/crates/schematic/src/config/format.rs @@ -1,11 +1,11 @@ use crate::config::errors::{ConfigError, ParserError}; -use miette::{SourceOffset, SourceSpan}; use serde::de::DeserializeOwned; pub use crate::format::Format; -fn create_span(content: &str, line: usize, column: usize) -> SourceSpan { - let offset = SourceOffset::from_location(content, line, column).offset(); +#[cfg(feature = "miette")] +fn create_span(content: &str, line: usize, column: usize) -> miette::SourceSpan { + let offset = miette::SourceOffset::from_location(content, line, column).offset(); let length = 0; (offset, length).into() @@ -75,6 +75,7 @@ impl Format { // content: NamedSource::new(location, content.to_owned()), content: content.to_owned(), path: error.path().to_string(), + #[cfg(feature = "miette")] span: Some(create_span( &content, error.inner().line(), @@ -92,6 +93,7 @@ impl Format { // content: NamedSource::new(location, content.to_owned()), content: content.to_owned(), path: error.path().to_string(), + #[cfg(feature = "miette")] span: error.inner().span().map(|s| s.into()), message: error.inner().message().to_owned(), })? @@ -108,6 +110,7 @@ impl Format { // content: NamedSource::new(location, content.to_owned()), content: content.to_owned(), path: error.path().to_string(), + #[cfg(feature = "miette")] span: error .inner() .location() @@ -120,6 +123,7 @@ impl Format { // content: NamedSource::new(location, content.to_owned()), content: content.to_owned(), path: String::new(), + #[cfg(feature = "miette")] span: error.location().map(|s| (s.line(), s.column()).into()), message: error.to_string(), })?; @@ -131,6 +135,7 @@ impl Format { // content: NamedSource::new(location, content.to_owned()), content: content.to_owned(), path: error.path().to_string(), + #[cfg(feature = "miette")] span: error .inner() .location() diff --git a/crates/schematic/src/config/validator.rs b/crates/schematic/src/config/validator.rs index 8f33e0f1..4120a96e 100644 --- a/crates/schematic/src/config/validator.rs +++ b/crates/schematic/src/config/validator.rs @@ -1,12 +1,12 @@ use crate::config::path::{Path, PathSegment}; -use miette::Diagnostic; use starbase_styles::color; use starbase_styles::{Style, Stylize}; use std::fmt::{self, Display}; use thiserror::Error; /// Error for a single validation failure. -#[derive(Clone, Debug, Diagnostic, Error)] +#[derive(Clone, Debug, Error)] +#[cfg_attr(feature = "miette", derive(miette::Diagnostic))] #[error("{}{} {message}", .path.to_string().style(Style::Id), ":".style(Style::MutedLight))] pub struct ValidateError { /// Failure message. @@ -120,7 +120,8 @@ impl ValidateErrorType { } /// Error that contains multiple validation errors, for each setting that failed. -#[derive(Clone, Debug, Diagnostic, Error)] +#[derive(Clone, Debug, Error)] +#[cfg_attr(feature = "miette", derive(miette::Diagnostic))] pub struct ValidatorError { /// When nested, the path to the setting that contains the nested error. pub path: Path, diff --git a/crates/schematic/src/schema/generator.rs b/crates/schematic/src/schema/generator.rs index 0940e6fd..01b73c0a 100644 --- a/crates/schematic/src/schema/generator.rs +++ b/crates/schematic/src/schema/generator.rs @@ -1,6 +1,5 @@ -use super::SchemaRenderer; +use super::{RenderResult, SchemaRenderer}; use indexmap::IndexMap; -use miette::IntoDiagnostic; use schematic_types::*; use std::collections::HashSet; use std::fs; @@ -91,17 +90,31 @@ impl SchemaGenerator { &self, output_file: P, mut renderer: R, - ) -> miette::Result<()> { + ) -> RenderResult<()> { let output_file = output_file.as_ref(); let mut output = renderer.render(&self.schemas, &self.references)?; output.push('\n'); - if let Some(parent) = output_file.parent() { - fs::create_dir_all(parent).into_diagnostic()?; + #[cfg(feature = "miette")] + { + use miette::IntoDiagnostic; + + if let Some(parent) = output_file.parent() { + fs::create_dir_all(parent).into_diagnostic()?; + } + + fs::write(output_file, output).into_diagnostic()?; } - fs::write(output_file, output).into_diagnostic()?; + #[cfg(not(feature = "miette"))] + { + if let Some(parent) = output_file.parent() { + fs::create_dir_all(parent)?; + } + + fs::write(output_file, output)?; + } Ok(()) } diff --git a/crates/schematic/src/schema/renderer.rs b/crates/schematic/src/schema/renderer.rs index 6c5d0a4c..38d645be 100644 --- a/crates/schematic/src/schema/renderer.rs +++ b/crates/schematic/src/schema/renderer.rs @@ -2,8 +2,12 @@ use indexmap::IndexMap; use schematic_types::*; use std::collections::HashSet; +#[cfg(feature = "miette")] pub type RenderResult = miette::Result; +#[cfg(not(feature = "miette"))] +pub type RenderResult = Result>; + /// Renders [`SchemaType`]s to a distinct format (derived from generic `O`) /// for use within a [`SchemaGenerator`]. pub trait SchemaRenderer { diff --git a/crates/test-app/Cargo.toml b/crates/test-app/Cargo.toml index 9f659d43..55214eb7 100644 --- a/crates/test-app/Cargo.toml +++ b/crates/test-app/Cargo.toml @@ -7,6 +7,7 @@ publish = false [dependencies] schematic = { path = "../schematic", features = [ "json", + "miette", "schema", "type_chrono", "type_regex",