diff --git a/CHANGELOG.md b/CHANGELOG.md index a4a919d..90482aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Adds `Pointer::starts_with` and `Pointer::ends_with` for prefix and suffix matching. - Adds new `ParseIndexError` variant to express the presence non-digit characters in the token. - Adds `Token::is_next` for checking if a token represents the `-` character. -- Adds `ParseBufError`, returned as the `Err` variant of `PointerBuf::parse`, which includes the input `String` (from `Into::into`). +- Adds `InvalidEncoding` to represent the two possible encoding errors when decoding a token. +- Adds `diagnostic` mod, including: +- Adds `diagnotic::Diagnostic` trait to facilitate error reporting and + `miette` integration. All errors intended for usage with `assign::Assign` or + `resolve::Resolve` must implement this trait. +- Adds `"miette"` featureflag to enable `miette` integration for error reporting. ### Changed @@ -24,11 +29,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `Pointer::get` now accepts ranges and can produce `Pointer` segments as output (similar to `slice::get`). - `PointerBuf::parse` now returns `BufParseError` instead of `ParseError`. +- Adds field `position` to variants of `resolve::Error` and `assign::Error` to indicate the + token index of where the error occurred. +- Renames `ParseError::NoLeadingBackslash` to `ParseError::NoLeadingSlash`. ### Fixed - Make validation of array indices conform to RFC 6901 in the presence of non-digit characters. +### Deprecated + +- `assign::AssignError` renamed to `assign::Error` +- `resolve::ResolveError` renamed to `resolve::Error` +- `InvalidEncodingError` renamed to `EncodingError` + ## [0.6.2] 2024-09-30 ### Added diff --git a/Cargo.lock b/Cargo.lock index 614e52f..f22b580 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,7 +38,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -172,9 +172,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "miette" -version = "7.2.0" +version = "7.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4edc8853320c2a0dab800fbda86253c8938f6ea88510dc92c5f1ed20e794afc1" +checksum = "317f146e2eb7021892722af37cf1b971f0a70c8406f487e24952667616192c64" dependencies = [ "backtrace", "backtrace-ext", @@ -192,9 +192,9 @@ dependencies = [ [[package]] name = "miette-derive" -version = "7.2.0" +version = "7.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" +checksum = "23c9b935fbe1d6cbd1dac857b54a688145e2d93f48db36010514d0f612d0ad67" dependencies = [ "proc-macro2", "quote", @@ -378,12 +378,6 @@ dependencies = [ "serde", ] -[[package]] -name = "smawk" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" - [[package]] name = "supports-color" version = "3.0.1" @@ -429,12 +423,12 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.3.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9" dependencies = [ "rustix", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -443,7 +437,6 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" dependencies = [ - "smawk", "unicode-linebreak", "unicode-width", ] @@ -526,37 +519,22 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] -name = "windows-targets" -version = "0.48.5" +name = "windows-sys" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] @@ -565,46 +543,28 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -617,48 +577,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" diff --git a/Cargo.toml b/Cargo.toml index f658d1d..b137ae6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ rust-version = "1.79.0" version = "0.6.3" [dependencies] -miette = { version = "7.2.0", optional = true, features = ["fancy"] } +miette = { version = "7.4.0", optional = true, features = ["fancy"] } serde = { version = "1.0.203", optional = true, features = ["alloc"] } serde_json = { version = "1.0.119", optional = true, features = ["alloc"] } toml = { version = "0.8", optional = true } diff --git a/src/assign.rs b/src/assign.rs index 187f209..f8f4803 100644 --- a/src/assign.rs +++ b/src/assign.rs @@ -37,26 +37,16 @@ //! use crate::{ - diagnostic::{impl_diagnostic_url, Diagnostic, Label}, + diagnostic::{diagnostic_url, Diagnostic, Label}, index::{OutOfBoundsError, ParseIndexError}, - Pointer, + Pointer, PointerBuf, }; -use alloc::borrow::Cow; +use alloc::boxed::Box; use core::{ fmt::{self, Debug}, iter::once, }; -/* -░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -╔══════════════════════════════════════════════════════════════════════════════╗ -║ ║ -║ Assign ║ -║ ¯¯¯¯¯¯¯¯ ║ -╚══════════════════════════════════════════════════════════════════════════════╝ -░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -*/ - /// Implemented by types which can internally assign a /// ([`Value`](`Assign::Value`)) at a path represented by a JSON [`Pointer`]. /// @@ -134,30 +124,10 @@ pub trait Assign { V: Into; } -/* -░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -╔══════════════════════════════════════════════════════════════════════════════╗ -║ ║ -║ AssignError ║ -║ ¯¯¯¯¯¯¯¯¯¯¯¯¯ ║ -╚══════════════════════════════════════════════════════════════════════════════╝ -░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -*/ - /// Alias for [`Error`] - indicates a value assignment failed. #[deprecated(since = "0.7.0", note = "renamed to `Error`")] pub type AssignError = Error; -/* -░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -╔══════════════════════════════════════════════════════════════════════════════╗ -║ ║ -║ Error ║ -║ ¯¯¯¯¯¯¯ ║ -╚══════════════════════════════════════════════════════════════════════════════╝ -░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -*/ - /// Possible error returned from [`Assign`] implementations for /// [`serde_json::Value`] and /// [`toml::Value`](https://docs.rs/toml/0.8.14/toml/index.html). @@ -189,7 +159,7 @@ pub enum Error { } impl Error { - /// Returns the position (index) of the [`Token`](crate::Token) which was out of bounds + /// The position (token index) of the [`Token`](crate::Token) which was out of bounds pub fn position(&self) -> usize { match self { Self::OutOfBounds { position, .. } | Self::FailedToParseIndex { position, .. } => { @@ -197,42 +167,52 @@ impl Error { } } } - + /// Offset (in bytes) of the partial pointer starting with the invalid token. pub fn offset(&self) -> usize { match self { Self::OutOfBounds { offset, .. } | Self::FailedToParseIndex { offset, .. } => *offset, } } + + /// Returns `true` if the error is [`OutOfBounds`]. + /// + /// [`OutOfBounds`]: Error::OutOfBounds + #[must_use] + pub fn is_out_of_bounds(&self) -> bool { + matches!(self, Self::OutOfBounds { .. }) + } + + /// Returns `true` if the error is [`FailedToParseIndex`]. + /// + /// [`FailedToParseIndex`]: Error::FailedToParseIndex + #[must_use] + pub fn is_failed_to_parse_index(&self) -> bool { + matches!(self, Self::FailedToParseIndex { .. }) + } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::FailedToParseIndex { .. } => { - write!( - f, - "value failed to be assigned by json pointer; \ - array index for token at offset {} is not a valid integer or '-'", - self.offset() - ) - } - Self::OutOfBounds { .. } => { + Self::FailedToParseIndex { offset, .. } => { write!( f, - "value failed to be assigned by json pointer; \ - array index for token at offset {} is out of bounds", - self.offset() + "assign failed: json pointer token at offset {offset} failed to parse as an array index" ) } + Self::OutOfBounds { offset, .. } => write!( + f, + "assign failed: json pointer token at offset {offset} is out of bounds", + ), } } } -impl<'s> Diagnostic<'s> for Error { - type Subject = Cow<'s, Pointer>; +impl Diagnostic for Error { + type Subject = PointerBuf; fn url() -> &'static str { - impl_diagnostic_url!(enum assign::Error) + diagnostic_url!(enum assign::Error) } fn labels(&self, origin: &Self::Subject) -> Option>> { @@ -244,18 +224,20 @@ impl<'s> Diagnostic<'s> for Error { self.offset() }; let len = token.encoded().len(); - Some(match self { - Error::FailedToParseIndex { .. } => Box::new(once(Label::new( - format!("\"{}\" is an invalid array index", token.decoded()), - offset, - len, - ))), - Error::OutOfBounds { source, .. } => Box::new(once(Label::new( - format!("{} is out of bounds (len: {})", source.index, source.length), - offset, - len, - ))), - }) + let text = match self { + Error::FailedToParseIndex { .. } => "expected array index or '-'".to_string(), + Error::OutOfBounds { source, .. } => { + format!("{} is out of bounds (len: {})", source.index, source.length) + } + }; + Some(Box::new(once(Label::new(text, offset, len)))) + } +} + +#[cfg(feature = "miette")] +impl miette::Diagnostic for Error { + fn url<'a>(&'a self) -> Option> { + Some(Box::new(::url())) } } @@ -269,16 +251,6 @@ impl std::error::Error for Error { } } -/* -░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -╔══════════════════════════════════════════════════════════════════════════════╗ -║ ║ -║ json impl ║ -║ ¯¯¯¯¯¯¯¯¯¯¯ ║ -╚══════════════════════════════════════════════════════════════════════════════╝ -░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -*/ - #[cfg(feature = "json")] mod json { use super::{Assign, Assigned, Error}; @@ -454,16 +426,6 @@ mod json { } } -/* -░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -╔══════════════════════════════════════════════════════════════════════════════╗ -║ ║ -║ toml impl ║ -║ ¯¯¯¯¯¯¯¯¯¯¯ ║ -╚══════════════════════════════════════════════════════════════════════════════╝ -░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -*/ - #[cfg(feature = "toml")] mod toml { use super::{Assign, Assigned, Error}; @@ -638,16 +600,6 @@ enum Assigned<'v, V> { Continue { next_dest: &'v mut V, same_value: V }, } -/* -░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -╔══════════════════════════════════════════════════════════════════════════════╗ -║ ║ -║ Tests ║ -║ ¯¯¯¯¯¯¯ ║ -╚══════════════════════════════════════════════════════════════════════════════╝ -░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -*/ - #[cfg(test)] #[allow(clippy::too_many_lines)] mod tests { @@ -694,12 +646,6 @@ mod tests { } } - /* - ╔═══════════════════════════════════════════════════╗ - ║ json ║ - ╚═══════════════════════════════════════════════════╝ - */ - #[test] #[cfg(feature = "json")] fn assign_json() { @@ -890,12 +836,6 @@ mod tests { .for_each(|(i, t)| t.run(i)); } - /* - ╔══════════════════════════════════════════════════╗ - ║ toml ║ - ╚══════════════════════════════════════════════════╝ - */ - #[test] #[cfg(feature = "toml")] fn assign_toml() { diff --git a/src/diagnostic.rs b/src/diagnostic.rs index 354232d..e450e77 100644 --- a/src/diagnostic.rs +++ b/src/diagnostic.rs @@ -1,18 +1,17 @@ //! Error reporting data structures and miette integration. +//! -use core::fmt; +use alloc::{boxed::Box, string::String}; +use core::{fmt, ops::Deref}; /// Implemented by errors which can be converted into a [`Report`]. -pub trait Diagnostic<'s>: Sized + private::Sealed { +pub trait Diagnostic: Sized { /// The value which caused the error. - type Subject: private::IntoOwned; + type Subject: Deref; /// Combine the error with its subject to generate a [`Report`]. - fn into_report(self, subject: impl Into) -> Report { - Report { - source: self, - subject: subject.into(), - } + fn into_report(self, subject: impl Into) -> Report { + Report::new(self, subject.into()) } /// The docs.rs URL for this error @@ -23,6 +22,7 @@ pub trait Diagnostic<'s>: Sized + private::Sealed { } /// A label for a span within a json pointer or malformed string. +/// #[derive(Debug, PartialEq, Eq, Clone)] pub struct Label { text: String, @@ -45,80 +45,66 @@ impl From