diff --git a/CHANGELOG.md b/CHANGELOG.md index f9686e3..42bcffe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,29 +5,52 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - ## [Unreleased] ### Added -- Adds method `into_buf` for `Box` and `impl From for Box`. +- Adds method `into_buf` for `Box` and `impl From for Box`. - Adds unsafe associated methods `Pointer::new_unchecked` and `PointerBuf::new_unchecked` for external zero-cost construction. - 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 `InvalidEncoding` to represent the two possible encoding errors when decoding a token. +- 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 `diagnostic::Report` to capture the input for `PointerBuf::parse` + and to facilitate `miette` integration for all errors. +- Adds `"miette"` feature flag to enable `miette` integration for error reporting. ### Changed -- Changed signature of `PathBuf::parse` to avoid requiring allocation. -- Bumps minimum Rust version to 1.79. - `Pointer::get` now accepts ranges and can produce `Pointer` segments as output (similar to `slice::get`). +- Bumps minimum Rust version to 1.79. +- `PointerBuf::parse` now returns `RichParseError`, an alias to + `Report` which contains the allocated string as well as the + error. Use `Report::original` for matches or `Report:: +- Renames `ParseError::NoLeadingBackslash` to `ParseError::NoLeadingSlash` + (sorry for the churn, I spaced hard - @chanced). +- Adds field `position` to variants of `resolve::Error` and `assign::Error` to indicate the + token index of where the error occurred. +- Renames `ParseError::is_no_leading_backslash` to `ParseError::is_no_leading_slash`. +- Renames `assign::AssignError` to `assign::Error` +- Renames `resolve::ResolveError` to `resolve::Error` +- Renames `InvalidEncodingError` to `EncodingError` ### Fixed - Make validation of array indices conform to RFC 6901 in the presence of non-digit characters. +### Deprecated + +- `ParseError::is_no_leading_backslash` renamed to `ParseError::is_no_leading_slash`. +- `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 dd2668a..b85f907 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,26 +2,71 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "aho-corasick" -version = "0.7.6" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "backtrace-ext" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50" +dependencies = [ + "backtrace", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + [[package]] name = "cfg-if" -version = "0.1.2" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "env_logger" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e" +checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" dependencies = [ "log", "regex", @@ -29,47 +74,70 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] [[package]] name = "getrandom" -version = "0.2.0" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee8025cf36f917e6a52cce185b7c7177689b838b7ec138364e50cc2277a56cf4" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", "wasi", ] +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" [[package]] name = "indexmap" -version = "2.0.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", "hashbrown", ] +[[package]] +name = "is_ci" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" + [[package]] name = "itoa" -version = "1.0.0" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f3e61cf687687b30c9e6ddf0fc36cf15f035e66d491e6da968fa49ffa9a378" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jsonptr" -version = "0.6.3" +version = "0.7.0" dependencies = [ + "miette", "quickcheck", "quickcheck_macros", "serde", @@ -79,37 +147,89 @@ dependencies = [ ] [[package]] -name = "lazy_static" -version = "1.0.0" +name = "libc" +version = "0.2.162" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" [[package]] -name = "libc" -version = "0.2.64" +name = "linux-raw-sys" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74dfca3d9957906e8d1e6a0b641dc9a59848e793f1da2165889fd4f62d10d79c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "log" -version = "0.4.8" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "miette" +version = "7.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317f146e2eb7021892722af37cf1b971f0a70c8406f487e24952667616192c64" dependencies = [ + "backtrace", + "backtrace-ext", "cfg-if", + "miette-derive", + "owo-colors", + "supports-color", + "supports-hyperlinks", + "supports-unicode", + "terminal_size", + "textwrap", + "thiserror", + "unicode-width", ] [[package]] -name = "memchr" -version = "2.5.0" +name = "miette-derive" +version = "7.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "23c9b935fbe1d6cbd1dac857b54a688145e2d93f48db36010514d0f612d0ad67" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "object" +version = "0.36.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +dependencies = [ + "memchr", +] + +[[package]] +name = "owo-colors" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb37767f6569cd834a413442455e0f066d0d522de8630436e2a1761d9726ba56" [[package]] name = "proc-macro2" -version = "1.0.74" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2de98502f212cfcea8d0bb305bd0f49d7ebdd75b64ba0a68f937d888f4e0d6db" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] @@ -138,95 +258,147 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.35" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] [[package]] name = "rand" -version = "0.8.0" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76330fb486679b4ace3670f117bbc9e16204005c4bde9c4bd372f45bed34f12" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "rand_core", ] [[package]] name = "rand_core" -version = "0.6.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[package]] name = "regex" -version = "1.3.0" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc98360d9e6ad383647702acc90f80b0582eac3ea577ab47d96325d3575de908" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", "regex-syntax", - "thread_local", ] [[package]] name = "regex-syntax" -version = "0.6.12" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "0.38.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] [[package]] name = "ryu" -version = "1.0.0" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "serde" -version = "1.0.203" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.87", ] [[package]] name = "serde_json" -version = "1.0.119" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8eddb61f0697cc3989c5d64b452f5488e2b8a60fd7d5076a3045076ffef8cb0" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] [[package]] name = "serde_spanned" -version = "0.6.3" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] +[[package]] +name = "supports-color" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8775305acf21c96926c900ad056abeef436701108518cf890020387236ac5a77" +dependencies = [ + "is_ci", +] + +[[package]] +name = "supports-hyperlinks" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c0a1e5168041f5f3ff68ff7d95dcb9c8749df29f6e7e89ada40dd4c9de404ee" + +[[package]] +name = "supports-unicode" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" + [[package]] name = "syn" version = "1.0.109" @@ -240,9 +412,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.46" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89456b690ff72fddcecf231caedbe615c59480c93358a93dfae7fc29e3ebbf0e" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -250,19 +422,50 @@ dependencies = [ ] [[package]] -name = "thread_local" -version = "0.3.6" +name = "terminal_size" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" +checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9" dependencies = [ - "lazy_static", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "textwrap" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" +dependencies = [ + "unicode-linebreak", + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", ] [[package]] name = "toml" -version = "0.8.0" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c226a7bba6d859b63c92c4b4fe69c5b6b72d0cb897dbc8e6012298e6154cb56e" +checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" dependencies = [ "serde", "serde_spanned", @@ -272,18 +475,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.20.0" +version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ff63e60a958cefbb518ae1fd6566af80d9d4be430a33f3723dfc47d1d411d95" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ "indexmap", "serde", @@ -294,21 +497,115 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.0" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] +name = "unicode-width" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" +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.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[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.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.5.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fac9742fd1ad1bd9643b991319f72dd031016d44b77039a26977eb667141e7" +checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index e4857e2..6ef6b2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,9 +13,10 @@ license = "MIT OR Apache-2.0" name = "jsonptr" repository = "https://github.com/chanced/jsonptr" rust-version = "1.79.0" -version = "0.6.3" +version = "0.7.0" [dependencies] +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 } @@ -30,10 +31,20 @@ quickcheck_macros = "1.0.0" syn = { version = "1.0.109", optional = true } [features] -assign = [] -default = ["std", "serde", "json", "resolve", "assign", "delete"] -delete = ["resolve"] -json = ["dep:serde_json", "serde"] +assign = [] +default = [ + "std", + "serde", + "json", + "toml", # TODO: remove + "resolve", + "assign", + "delete", + "miette", # TODO: remove +] +delete = ["resolve"] +json = ["dep:serde_json", "serde"] +miette = ["dep:miette", "std"] resolve = [] -std = ["serde/std", "serde_json?/std"] -toml = ["dep:toml", "serde", "std"] +std = ["serde/std", "serde_json?/std"] +toml = ["dep:toml", "serde", "std"] diff --git a/src/assign.rs b/src/assign.rs index a48bbb2..8b4e3bf 100644 --- a/src/assign.rs +++ b/src/assign.rs @@ -37,20 +37,15 @@ //! use crate::{ + diagnostic::{diagnostic_url, Diagnostic, Label}, index::{OutOfBoundsError, ParseIndexError}, - Pointer, + Pointer, PointerBuf, +}; +use alloc::{boxed::Box, string::ToString}; +use core::{ + fmt::{self, Debug}, + iter::once, }; -use core::fmt::{self, Debug}; - -/* -░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -╔══════════════════════════════════════════════════════════════════════════════╗ -║ ║ -║ Assign ║ -║ ¯¯¯¯¯¯¯¯ ║ -╚══════════════════════════════════════════════════════════════════════════════╝ -░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -*/ /// Implemented by types which can internally assign a /// ([`Value`](`Assign::Value`)) at a path represented by a JSON [`Pointer`]. @@ -129,31 +124,33 @@ 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; /// Possible error returned from [`Assign`] implementations for /// [`serde_json::Value`] and /// [`toml::Value`](https://docs.rs/toml/0.8.14/toml/index.html). #[derive(Debug, PartialEq, Eq)] -pub enum AssignError { - /// A `Token` within the `Pointer` failed to be parsed as an array index. +pub enum Error { + /// A [`Token`](crate::Token) within the [`Pointer`] failed to be parsed as + /// an array index. FailedToParseIndex { + /// Position (index) of the token which failed to parse as an [`Index`](crate::index::Index) + position: usize, /// Offset of the partial pointer starting with the invalid index. offset: usize, /// The source [`ParseIndexError`] source: ParseIndexError, }, - /// target array. + /// A [`Token`](crate::Token) within the [`Pointer`] contains an + /// [`Index`](crate::index::Index) which is out of bounds. + /// + /// The current or resulting array's length is less than the index. OutOfBounds { + /// Position (index) of the token which failed to parse as an [`Index`](crate::index::Index) + position: usize, /// Offset of the partial pointer starting with the invalid index. offset: usize, /// The source [`OutOfBoundsError`] @@ -161,27 +158,91 @@ pub enum AssignError { }, } -impl fmt::Display for AssignError { +impl Error { + /// 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, .. } => { + *position + } + } + } + /// 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 { offset, .. } => { write!( f, - "assignment failed due to an invalid index at offset {offset}" - ) - } - Self::OutOfBounds { offset, .. } => { - write!( - f, - "assignment failed due to index at offset {offset} being out of bounds" + "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 Diagnostic for Error { + type Subject = PointerBuf; + + fn url() -> &'static str { + diagnostic_url!(enum assign::Error) + } + + fn labels(&self, origin: &Self::Subject) -> Option>> { + let position = self.position(); + let token = origin.get(position)?; + let offset = if self.offset() + 1 < origin.as_str().len() { + self.offset() + 1 + } else { + self.offset() + }; + let len = token.encoded().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())) + } +} + #[cfg(feature = "std")] -impl std::error::Error for AssignError { +impl std::error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { Self::FailedToParseIndex { source, .. } => Some(source), @@ -190,24 +251,9 @@ impl std::error::Error for AssignError { } } -enum Assigned<'v, V> { - Done(Option), - Continue { next_dest: &'v mut V, same_value: V }, -} - -/* -░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -╔══════════════════════════════════════════════════════════════════════════════╗ -║ ║ -║ json impl ║ -║ ¯¯¯¯¯¯¯¯¯¯¯ ║ -╚══════════════════════════════════════════════════════════════════════════════╝ -░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -*/ - #[cfg(feature = "json")] mod json { - use super::{Assign, AssignError, Assigned}; + use super::{Assign, Assigned, Error}; use crate::{Pointer, Token}; use alloc::{ string::{String, ToString}, @@ -235,7 +281,7 @@ mod json { } impl Assign for Value { type Value = Value; - type Error = AssignError; + type Error = Error; fn assign(&mut self, ptr: &Pointer, value: V) -> Result, Self::Error> where V: Into, @@ -248,14 +294,15 @@ mod json { mut ptr: &Pointer, mut dest: &mut Value, mut value: Value, - ) -> Result, AssignError> { + ) -> Result, Error> { let mut offset = 0; + let mut position = 0; while let Some((token, tail)) = ptr.split_front() { let tok_len = token.encoded().len(); let assigned = match dest { - Value::Array(array) => assign_array(token, tail, array, value, offset)?, + Value::Array(array) => assign_array(token, tail, array, value, position, offset)?, Value::Object(obj) => assign_object(token, tail, obj, value), _ => assign_scalar(ptr, dest, value), }; @@ -273,6 +320,7 @@ mod json { } } offset += 1 + tok_len; + position += 1; } // Pointer is root, we can replace `dest` directly @@ -285,14 +333,23 @@ mod json { remaining: &Pointer, array: &'v mut Vec, src: Value, + position: usize, offset: usize, - ) -> Result, AssignError> { + ) -> Result, Error> { // parsing the index let idx = token .to_index() - .map_err(|source| AssignError::FailedToParseIndex { offset, source })? + .map_err(|source| Error::FailedToParseIndex { + position, + offset, + source, + })? .for_len_incl(array.len()) - .map_err(|source| AssignError::OutOfBounds { offset, source })?; + .map_err(|source| Error::OutOfBounds { + position, + offset, + source, + })?; debug_assert!(idx <= array.len()); @@ -369,19 +426,9 @@ mod json { } } -/* -░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -╔══════════════════════════════════════════════════════════════════════════════╗ -║ ║ -║ toml impl ║ -║ ¯¯¯¯¯¯¯¯¯¯¯ ║ -╚══════════════════════════════════════════════════════════════════════════════╝ -░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -*/ - #[cfg(feature = "toml")] mod toml { - use super::{Assign, AssignError, Assigned}; + use super::{Assign, Assigned, Error}; use crate::{Pointer, Token}; use alloc::{string::String, vec, vec::Vec}; use core::mem; @@ -406,7 +453,7 @@ mod toml { impl Assign for Value { type Value = Value; - type Error = AssignError; + type Error = Error; fn assign(&mut self, ptr: &Pointer, value: V) -> Result, Self::Error> where V: Into, @@ -419,14 +466,15 @@ mod toml { mut ptr: &Pointer, mut dest: &mut Value, mut value: Value, - ) -> Result, AssignError> { + ) -> Result, Error> { let mut offset = 0; + let mut position = 0; while let Some((token, tail)) = ptr.split_front() { let tok_len = token.encoded().len(); let assigned = match dest { - Value::Array(array) => assign_array(token, tail, array, value, offset)?, + Value::Array(array) => assign_array(token, tail, array, value, position, offset)?, Value::Table(tbl) => assign_object(token, tail, tbl, value), _ => assign_scalar(ptr, dest, value), }; @@ -444,6 +492,7 @@ mod toml { } } offset += 1 + tok_len; + position += 1; } // Pointer is root, we can replace `dest` directly @@ -457,14 +506,23 @@ mod toml { remaining: &Pointer, array: &'v mut Vec, src: Value, + position: usize, offset: usize, - ) -> Result, AssignError> { + ) -> Result, Error> { // parsing the index let idx = token .to_index() - .map_err(|source| AssignError::FailedToParseIndex { offset, source })? + .map_err(|source| Error::FailedToParseIndex { + position, + offset, + source, + })? .for_len_incl(array.len()) - .map_err(|source| AssignError::OutOfBounds { offset, source })?; + .map_err(|source| Error::OutOfBounds { + position, + offset, + source, + })?; debug_assert!(idx <= array.len()); @@ -537,20 +595,15 @@ mod toml { } } -/* -░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -╔══════════════════════════════════════════════════════════════════════════════╗ -║ ║ -║ Tests ║ -║ ¯¯¯¯¯¯¯ ║ -╚══════════════════════════════════════════════════════════════════════════════╝ -░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ -*/ +enum Assigned<'v, V> { + Done(Option), + Continue { next_dest: &'v mut V, same_value: V }, +} #[cfg(test)] #[allow(clippy::too_many_lines)] mod tests { - use super::{Assign, AssignError}; + use super::{Assign, Error}; use crate::{ index::{InvalidCharacterError, OutOfBoundsError, ParseIndexError}, Pointer, @@ -574,9 +627,6 @@ mod tests { V::Error: Debug + PartialEq, Result, V::Error>: PartialEq, V::Error>>, { - fn all(tests: impl IntoIterator>) { - tests.into_iter().enumerate().for_each(|(i, t)| t.run(i)); - } fn run(self, i: usize) { let Test { ptr, @@ -596,18 +646,11 @@ mod tests { } } - /* - ╔═══════════════════════════════════════════════════╗ - ║ json ║ - ╚═══════════════════════════════════════════════════╝ - */ - #[test] #[cfg(feature = "json")] fn assign_json() { use serde_json::json; - - Test::all([ + [ Test { ptr: "/foo", data: json!({}), @@ -731,7 +774,8 @@ mod tests { ptr: "/1", data: json!([]), assign: json!("foo"), - expected: Err(AssignError::OutOfBounds { + expected: Err(Error::OutOfBounds { + position: 0, offset: 0, source: OutOfBoundsError { index: 1, @@ -751,7 +795,8 @@ mod tests { ptr: "/12a", data: json!([]), assign: json!("foo"), - expected: Err(AssignError::FailedToParseIndex { + expected: Err(Error::FailedToParseIndex { + position: 0, offset: 0, source: ParseIndexError::InvalidCharacter(InvalidCharacterError { source: "12a".into(), @@ -764,7 +809,8 @@ mod tests { ptr: "/002", data: json!([]), assign: json!("foo"), - expected: Err(AssignError::FailedToParseIndex { + expected: Err(Error::FailedToParseIndex { + position: 0, offset: 0, source: ParseIndexError::LeadingZeros, }), @@ -774,7 +820,8 @@ mod tests { ptr: "/+23", data: json!([]), assign: json!("foo"), - expected: Err(AssignError::FailedToParseIndex { + expected: Err(Error::FailedToParseIndex { + position: 0, offset: 0, source: ParseIndexError::InvalidCharacter(InvalidCharacterError { source: "+23".into(), @@ -783,21 +830,17 @@ mod tests { }), expected_data: json!([]), }, - ]); + ] + .into_iter() + .enumerate() + .for_each(|(i, t)| t.run(i)); } - /* - ╔══════════════════════════════════════════════════╗ - ║ toml ║ - ╚══════════════════════════════════════════════════╝ - */ - #[test] #[cfg(feature = "toml")] fn assign_toml() { use toml::{toml, Table, Value}; - - Test::all([ + [ Test { data: Value::Table(toml::Table::new()), ptr: "/foo", @@ -916,7 +959,8 @@ mod tests { data: Value::Array(vec![]), ptr: "/1", assign: "foo".into(), - expected: Err(AssignError::OutOfBounds { + expected: Err(Error::OutOfBounds { + position: 0, offset: 0, source: OutOfBoundsError { index: 1, @@ -929,7 +973,8 @@ mod tests { data: Value::Array(vec![]), ptr: "/a", assign: "foo".into(), - expected: Err(AssignError::FailedToParseIndex { + expected: Err(Error::FailedToParseIndex { + position: 0, offset: 0, source: ParseIndexError::InvalidCharacter(InvalidCharacterError { source: "a".into(), @@ -938,6 +983,9 @@ mod tests { }), expected_data: Value::Array(vec![]), }, - ]); + ] + .into_iter() + .enumerate() + .for_each(|(i, t)| t.run(i)); } } diff --git a/src/diagnostic.rs b/src/diagnostic.rs new file mode 100644 index 0000000..69dcb10 --- /dev/null +++ b/src/diagnostic.rs @@ -0,0 +1,279 @@ +//! Error reporting data structures and miette integration. +//! + +use alloc::{boxed::Box, string::String}; +use core::{fmt, ops::Deref}; + +/// Implemented by errors which can be converted into a [`Report`]. +pub trait Diagnostic: Sized { + /// The value which caused the error. + type Subject: Deref; + + /// Combine the error with its subject to generate a [`Report`]. + fn into_report(self, subject: impl Into) -> Report { + Report::new(self, subject.into()) + } + + /// The docs.rs URL for this error + fn url() -> &'static str; + + /// Returns the label for the given [`Subject`] if applicable. + fn labels(&self, subject: &Self::Subject) -> Option>>; +} + +/// A label for a span within a json pointer or malformed string. +/// +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Label { + text: String, + offset: usize, + len: usize, +} + +impl Label { + /// Creates a new instance of a [`Label`] from its parts + pub fn new(text: String, offset: usize, len: usize) -> Self { + Self { text, offset, len } + } +} + +#[cfg(feature = "miette")] +impl From