Skip to content

Commit

Permalink
Add miette feature.
Browse files Browse the repository at this point in the history
  • Loading branch information
milesj committed Feb 5, 2024
1 parent 94db4bc commit c548449
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 42 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -23,6 +25,7 @@
#### ⚙️ Internal

- Updated `garde` (validation) to v0.18.
- Updated `miette` to v7.

## 0.13.7

Expand Down
9 changes: 5 additions & 4 deletions crates/schematic/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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"]
Expand Down
57 changes: 31 additions & 26 deletions crates/schematic/src/config/errors.rs
Original file line number Diff line number Diff line change
@@ -1,106 +1,109 @@
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,
#[source]
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,
#[source]
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<String>,
},

// 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<String>,
},
}
Expand Down Expand Up @@ -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<SourceSpan>,
pub span: Option<miette::SourceSpan>,
}
11 changes: 8 additions & 3 deletions crates/schematic/src/config/format.rs
Original file line number Diff line number Diff line change
@@ -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()
Expand Down Expand Up @@ -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(),
Expand All @@ -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(),
})?
Expand All @@ -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()
Expand All @@ -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(),
})?;
Expand All @@ -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()
Expand Down
7 changes: 4 additions & 3 deletions crates/schematic/src/config/validator.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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,
Expand Down
25 changes: 19 additions & 6 deletions crates/schematic/src/schema/generator.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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(())
}
Expand Down
4 changes: 4 additions & 0 deletions crates/schematic/src/schema/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ use indexmap::IndexMap;
use schematic_types::*;
use std::collections::HashSet;

#[cfg(feature = "miette")]
pub type RenderResult<T = String> = miette::Result<T>;

#[cfg(not(feature = "miette"))]
pub type RenderResult<T = String> = Result<T, Box<dyn std::error::Error>>;

/// Renders [`SchemaType`]s to a distinct format (derived from generic `O`)
/// for use within a [`SchemaGenerator`].
pub trait SchemaRenderer<O = String> {
Expand Down
1 change: 1 addition & 0 deletions crates/test-app/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ publish = false
[dependencies]
schematic = { path = "../schematic", features = [
"json",
"miette",
"schema",
"type_chrono",
"type_regex",
Expand Down

0 comments on commit c548449

Please sign in to comment.