From 60e2f32f43af22d57ad1ade2c0e4da71efe75ffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20S=C3=A1=20de=20Mello?= Date: Sun, 16 Feb 2025 22:13:49 +0000 Subject: [PATCH 1/6] make `PointerBuf::parse` error basic by default --- src/diagnostic.rs | 2 +- src/lib.rs | 2 +- src/pointer.rs | 23 ++++++++-------------- src/pointer/strcow.rs | 45 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 17 deletions(-) create mode 100644 src/pointer/strcow.rs diff --git a/src/diagnostic.rs b/src/diagnostic.rs index 69dcb10..8b03c55 100644 --- a/src/diagnostic.rs +++ b/src/diagnostic.rs @@ -220,7 +220,7 @@ where F: FnOnce() -> S, S: Into<::Subject>, { - self.diagnose(f()) + self.map_err(|error| error.into_report(f())) } } diff --git a/src/lib.rs b/src/lib.rs index 1339c83..0b84c63 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -68,7 +68,7 @@ pub mod diagnostic; pub use diagnostic::{Diagnose, Report}; mod pointer; -pub use pointer::{ParseError, Pointer, PointerBuf, RichParseError}; +pub use pointer::{ParseError, Pointer, PointerBuf}; mod token; pub use token::{EncodingError, InvalidEncoding, Token, Tokens}; diff --git a/src/pointer.rs b/src/pointer.rs index be0be7a..392faf1 100644 --- a/src/pointer.rs +++ b/src/pointer.rs @@ -1,5 +1,5 @@ use crate::{ - diagnostic::{diagnostic_url, Diagnostic, Label, Report}, + diagnostic::{diagnostic_url, Diagnostic, Label}, token::EncodingError, Components, InvalidEncoding, Token, Tokens, }; @@ -12,8 +12,10 @@ use alloc::{ }; use core::{borrow::Borrow, cmp::Ordering, iter::once, ops::Deref, str::FromStr}; use slice::PointerIndex; +use strcow::StrCow; mod slice; +mod strcow; /// A JSON Pointer is a string containing a sequence of zero or more reference /// [`Token`]s, each prefixed by a `'/'` character. @@ -923,11 +925,11 @@ impl PointerBuf { /// /// ## Errors /// Returns a [`RichParseError`] if the string is not a valid JSON Pointer. - pub fn parse(s: impl Into) -> Result { - let s = s.into(); - match validate(&s) { - Ok(_) => Ok(Self(s)), - Err(err) => Err(err.into_report(s)), + pub fn parse(s: impl StrCow) -> Result { + // must explicitly match due to borrow checker limitations + match validate(s.as_ref()) { + Ok(_) => Ok(Self(s.into_owned())), + Err(err) => Err(err), } } @@ -1328,9 +1330,6 @@ impl std::error::Error for ParseError { } } -/// A rich error type that includes the original string that failed to parse. -pub type RichParseError = Report; - /// Returned from [`PointerBuf::replace`] when the provided index is out of /// bounds. #[derive(Debug, PartialEq, Eq)] @@ -2343,10 +2342,4 @@ mod tests { let unboxed = boxed.into_buf(); assert_eq!(subjectal, unboxed); } - - #[test] - fn quick_miette_spike() { - let err = PointerBuf::parse("hello-world").unwrap_err(); - println!("{:?}", miette::Report::from(err)); - } } diff --git a/src/pointer/strcow.rs b/src/pointer/strcow.rs new file mode 100644 index 0000000..83be0ce --- /dev/null +++ b/src/pointer/strcow.rs @@ -0,0 +1,45 @@ +use alloc::borrow::Cow; + +pub trait StrCow: private::Sealed { + fn as_ref(&self) -> &str; + fn into_owned(self) -> String; +} + +impl StrCow for &str { + fn as_ref(&self) -> &str { + self + } + + fn into_owned(self) -> String { + self.to_owned() + } +} + +impl StrCow for String { + fn as_ref(&self) -> &str { + self.as_str() + } + + fn into_owned(self) -> String { + self + } +} + +impl StrCow for Cow<'_, str> { + fn as_ref(&self) -> &str { + AsRef::as_ref(self) + } + + fn into_owned(self) -> String { + self.into_owned() + } +} + +mod private { + use alloc::borrow::Cow; + + pub trait Sealed {} + impl Sealed for &str {} + impl Sealed for String {} + impl Sealed for Cow<'_, str> {} +} From fef6e3acb492c77242a1427a0cf11905fa152b25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20S=C3=A1=20de=20Mello?= Date: Sun, 16 Feb 2025 22:51:47 +0000 Subject: [PATCH 2/6] nostd fixes --- src/pointer/strcow.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pointer/strcow.rs b/src/pointer/strcow.rs index 83be0ce..008cf8d 100644 --- a/src/pointer/strcow.rs +++ b/src/pointer/strcow.rs @@ -1,4 +1,7 @@ -use alloc::borrow::Cow; +use alloc::{ + borrow::{Cow, ToOwned}, + string::String, +}; pub trait StrCow: private::Sealed { fn as_ref(&self) -> &str; From de92ae361c23e2448fbf3bc26974c8956b56ddd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20S=C3=A1=20de=20Mello?= Date: Sun, 16 Feb 2025 23:00:45 +0000 Subject: [PATCH 3/6] coverage --- src/pointer/strcow.rs | 44 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/pointer/strcow.rs b/src/pointer/strcow.rs index 008cf8d..955e2b0 100644 --- a/src/pointer/strcow.rs +++ b/src/pointer/strcow.rs @@ -46,3 +46,47 @@ mod private { impl Sealed for String {} impl Sealed for Cow<'_, str> {} } + +#[cfg(test)] +mod tests { + use super::StrCow; + use alloc::{borrow::Cow, string::String}; + + #[test] + fn sanity() { + let str_ref = "apple"; + let str_own = String::from("apple"); + let cow_ref = Cow::Borrowed("apple"); + let cow_own = Cow::<'static, str>::Owned(String::from("apple")); + assert_eq!(StrCow::as_ref(&str_ref), StrCow::as_ref(&str_own)); + assert_eq!(StrCow::as_ref(&str_ref), StrCow::as_ref(&cow_ref)); + assert_eq!(StrCow::as_ref(&str_ref), StrCow::as_ref(&cow_own)); + assert_eq!(StrCow::as_ref(&str_own), StrCow::as_ref(&cow_ref)); + assert_eq!(StrCow::as_ref(&str_own), StrCow::as_ref(&cow_own)); + assert_eq!(StrCow::as_ref(&cow_ref), StrCow::as_ref(&cow_own)); + assert_eq!( + StrCow::into_owned(str_ref), + StrCow::into_owned(str_own.clone()) + ); + assert_eq!( + StrCow::into_owned(str_ref), + StrCow::into_owned(cow_ref.clone()) + ); + assert_eq!( + StrCow::into_owned(str_ref), + StrCow::into_owned(cow_own.clone()) + ); + assert_eq!( + StrCow::into_owned(str_own.clone()), + StrCow::into_owned(cow_ref.clone()) + ); + assert_eq!( + StrCow::into_owned(str_own.clone()), + StrCow::into_owned(cow_own.clone()) + ); + assert_eq!( + StrCow::into_owned(cow_ref.clone()), + StrCow::into_owned(cow_own.clone()) + ); + } +} From 50092c66ae5230cddc9bb9bd06b9e30c32d453e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20S=C3=A1=20de=20Mello?= Date: Sun, 16 Feb 2025 23:02:49 +0000 Subject: [PATCH 4/6] nostd pt2 --- src/pointer/strcow.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pointer/strcow.rs b/src/pointer/strcow.rs index 955e2b0..a1d02b6 100644 --- a/src/pointer/strcow.rs +++ b/src/pointer/strcow.rs @@ -39,7 +39,7 @@ impl StrCow for Cow<'_, str> { } mod private { - use alloc::borrow::Cow; + use alloc::{borrow::Cow, string::String}; pub trait Sealed {} impl Sealed for &str {} From f96e4cccba0dab1a000354af462d071d5c696063 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20S=C3=A1=20de=20Mello?= Date: Sun, 16 Feb 2025 23:22:51 +0000 Subject: [PATCH 5/6] fix doctest --- README.md | 13 ++++--------- src/pointer/strcow.rs | 11 +++++++++++ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index a971337..3455e45 100644 --- a/README.md +++ b/README.md @@ -150,22 +150,17 @@ and the [`String`] which failed to parse or the [`PointerBuf`] which failed to resolve or assign. ```rust - use jsonptr::{Pointer, Diagnose}; + use jsonptr::{Pointer, PointerBuf, Diagnose}; let ptr_str = "foo/bar"; let err /* Result<&Pointer, Report> */ = Pointer::parse(ptr_str).diagnose(ptr_str).unwrap_err(); assert!(err.original().is_no_leading_slash()); -``` - -In the case of [`PointerBuf::parse`], the [`ParseError`] is always wrapped in a -[`Report`] so that the input `String` is not dropped. -```rust - use jsonptr::{PointerBuf}; - let ptr_str = "foo/bar"; - let err /* Result<&PointerBuf, Report> */ = PointerBuf::parse(ptr_str).unwrap_err(); + let err /* Result> */ = PointerBuf::parse(ptr_str).diagnose_with(|| ptr_str.to_owned()).unwrap_err(); assert!(err.original().is_no_leading_slash()); ``` +Note that when using this feature, allocation is unavoidable when parsing. + ## Feature Flags | Flag | Description | Enables | Default | diff --git a/src/pointer/strcow.rs b/src/pointer/strcow.rs index a1d02b6..46e2ece 100644 --- a/src/pointer/strcow.rs +++ b/src/pointer/strcow.rs @@ -28,6 +28,16 @@ impl StrCow for String { } } +impl StrCow for &String { + fn as_ref(&self) -> &str { + self.as_str() + } + + fn into_owned(self) -> String { + self.clone() + } +} + impl StrCow for Cow<'_, str> { fn as_ref(&self) -> &str { AsRef::as_ref(self) @@ -44,6 +54,7 @@ mod private { pub trait Sealed {} impl Sealed for &str {} impl Sealed for String {} + impl Sealed for &String {} impl Sealed for Cow<'_, str> {} } From d191e242a7c2c37414a7dc73e72fb200da369b8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20S=C3=A1=20de=20Mello?= Date: Mon, 17 Feb 2025 00:11:45 +0000 Subject: [PATCH 6/6] get rid of strcow --- src/pointer.rs | 10 ++-- src/pointer/strcow.rs | 103 ------------------------------------------ 2 files changed, 4 insertions(+), 109 deletions(-) delete mode 100644 src/pointer/strcow.rs diff --git a/src/pointer.rs b/src/pointer.rs index 392faf1..c1e5a16 100644 --- a/src/pointer.rs +++ b/src/pointer.rs @@ -12,10 +12,8 @@ use alloc::{ }; use core::{borrow::Borrow, cmp::Ordering, iter::once, ops::Deref, str::FromStr}; use slice::PointerIndex; -use strcow::StrCow; mod slice; -mod strcow; /// A JSON Pointer is a string containing a sequence of zero or more reference /// [`Token`]s, each prefixed by a `'/'` character. @@ -370,7 +368,6 @@ impl Pointer { /// [`ResolveError`]: `crate::resolve::ResolveError` /// [`Token`]: `crate::Token` /// [`Index`]: `crate::index::Index` - #[cfg(feature = "resolve")] pub fn resolve_mut<'v, R: crate::ResolveMut>( &self, @@ -681,7 +678,7 @@ impl PartialEq<&str> for Pointer { &&self.0 == other } } -impl<'p> PartialEq for &'p Pointer { +impl PartialEq for &Pointer { fn eq(&self, other: &String) -> bool { self.0.eq(other) } @@ -871,7 +868,7 @@ impl PartialOrd<&str> for PointerBuf { } } -impl<'p> PartialOrd for &'p Pointer { +impl PartialOrd for &Pointer { fn partial_cmp(&self, other: &PointerBuf) -> Option { self.0.partial_cmp(other.0.as_str()) } @@ -925,7 +922,8 @@ impl PointerBuf { /// /// ## Errors /// Returns a [`RichParseError`] if the string is not a valid JSON Pointer. - pub fn parse(s: impl StrCow) -> Result { + pub fn parse<'s>(s: impl Into>) -> Result { + let s = s.into(); // must explicitly match due to borrow checker limitations match validate(s.as_ref()) { Ok(_) => Ok(Self(s.into_owned())), diff --git a/src/pointer/strcow.rs b/src/pointer/strcow.rs deleted file mode 100644 index 46e2ece..0000000 --- a/src/pointer/strcow.rs +++ /dev/null @@ -1,103 +0,0 @@ -use alloc::{ - borrow::{Cow, ToOwned}, - string::String, -}; - -pub trait StrCow: private::Sealed { - fn as_ref(&self) -> &str; - fn into_owned(self) -> String; -} - -impl StrCow for &str { - fn as_ref(&self) -> &str { - self - } - - fn into_owned(self) -> String { - self.to_owned() - } -} - -impl StrCow for String { - fn as_ref(&self) -> &str { - self.as_str() - } - - fn into_owned(self) -> String { - self - } -} - -impl StrCow for &String { - fn as_ref(&self) -> &str { - self.as_str() - } - - fn into_owned(self) -> String { - self.clone() - } -} - -impl StrCow for Cow<'_, str> { - fn as_ref(&self) -> &str { - AsRef::as_ref(self) - } - - fn into_owned(self) -> String { - self.into_owned() - } -} - -mod private { - use alloc::{borrow::Cow, string::String}; - - pub trait Sealed {} - impl Sealed for &str {} - impl Sealed for String {} - impl Sealed for &String {} - impl Sealed for Cow<'_, str> {} -} - -#[cfg(test)] -mod tests { - use super::StrCow; - use alloc::{borrow::Cow, string::String}; - - #[test] - fn sanity() { - let str_ref = "apple"; - let str_own = String::from("apple"); - let cow_ref = Cow::Borrowed("apple"); - let cow_own = Cow::<'static, str>::Owned(String::from("apple")); - assert_eq!(StrCow::as_ref(&str_ref), StrCow::as_ref(&str_own)); - assert_eq!(StrCow::as_ref(&str_ref), StrCow::as_ref(&cow_ref)); - assert_eq!(StrCow::as_ref(&str_ref), StrCow::as_ref(&cow_own)); - assert_eq!(StrCow::as_ref(&str_own), StrCow::as_ref(&cow_ref)); - assert_eq!(StrCow::as_ref(&str_own), StrCow::as_ref(&cow_own)); - assert_eq!(StrCow::as_ref(&cow_ref), StrCow::as_ref(&cow_own)); - assert_eq!( - StrCow::into_owned(str_ref), - StrCow::into_owned(str_own.clone()) - ); - assert_eq!( - StrCow::into_owned(str_ref), - StrCow::into_owned(cow_ref.clone()) - ); - assert_eq!( - StrCow::into_owned(str_ref), - StrCow::into_owned(cow_own.clone()) - ); - assert_eq!( - StrCow::into_owned(str_own.clone()), - StrCow::into_owned(cow_ref.clone()) - ); - assert_eq!( - StrCow::into_owned(str_own.clone()), - StrCow::into_owned(cow_own.clone()) - ); - assert_eq!( - StrCow::into_owned(cow_ref.clone()), - StrCow::into_owned(cow_own.clone()) - ); - } -}