From 3b3e7721db03dc70031bd905292b915258c7548b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20S=C3=A1=20de=20Mello?= Date: Sun, 26 Jul 2026 10:32:22 +0100 Subject: [PATCH 1/2] Fix stale deprecation version on ResolveError alias The `ResolveError` alias was annotated `#[deprecated(since = "0.7.2")]`, but 0.7.2 was never published (the releases went 0.7.0 [yanked] -> 0.7.1 -> 0.8.0). It first ships deprecated in 0.8.0, so correct the `since`. Claude-Session: https://claude.ai/code/session_01XGvHsB4a2dxDVUQDGBScnW --- src/resolve.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resolve.rs b/src/resolve.rs index aa1fdb2..e453ee8 100644 --- a/src/resolve.rs +++ b/src/resolve.rs @@ -77,7 +77,7 @@ pub trait ResolveMut { } /// Alias for [`Error`]. -#[deprecated(since = "0.7.2", note = "renamed to `Error`")] +#[deprecated(since = "0.8.0", note = "renamed to `Error`")] pub type ResolveError = Error; /// Indicates that the `Pointer` could not be resolved. From 7e08ec383db0dac43fc94c6fc01e985cd64de4a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20S=C3=A1=20de=20Mello?= Date: Sun, 26 Jul 2026 11:32:33 +0100 Subject: [PATCH 2/2] Deprecate Pointer::split_at, add Pointer::split_at_offset Folds in the substance of #89 (authored by chanced), rebased onto current main and finished off: - `split_at` is offset-based, but the rest of the API (`get`, etc.) is index-based. Rename the offset-based split to `split_at_offset` and deprecate `split_at`, reserving that name to be reintroduced as an index-based split by 1.0. - Point the internal `strip`/parent caller at `split_at_offset` so the crate doesn't warn on its own deprecation. - Add unit tests for `split_at_offset` (separator and non-separator offsets, plus out-of-bounds offsets that must return None rather than panic) and a test confirming the deprecated `split_at` still delegates. These cover the lines #89 left untested. - Fix the doctest that #89 left calling the deprecated `split_at`. Claude-Session: https://claude.ai/code/session_01XGvHsB4a2dxDVUQDGBScnW --- CHANGELOG.md | 9 ++++++++ src/pointer.rs | 56 ++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e2a63b..8fc909f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,9 +9,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.8.0] 2026-07-26 +### Added + +- Adds `Pointer::split_at_offset`, the byte-offset-based replacement for the + now-deprecated `Pointer::split_at`. + ### Deprecated - Deprecated `ResolveError` name. +- Deprecated `Pointer::split_at` in favor of `Pointer::split_at_offset`. The + `split_at` name is being reserved so it can be reintroduced as a position + (index) based split by 1.0, consistent with the rest of the API (e.g. + `Pointer::get`). ### Fixed diff --git a/src/pointer.rs b/src/pointer.rs index eb1139b..a2714e6 100644 --- a/src/pointer.rs +++ b/src/pointer.rs @@ -206,7 +206,7 @@ impl Pointer { .into() } - /// Splits the `Pointer` at the given index if the character at the index is + /// Splits the `Pointer` at the given offset if the character at the offset is /// a separator slash (`'/'`), returning `Some((head, tail))`. Otherwise, /// returns `None`. /// @@ -217,19 +217,19 @@ impl Pointer { /// ↑ ↑ ↑ /// 0 4 8 /// ``` - /// All other indices will return `None`. + /// All other offsets will return `None`. /// /// ## Example /// /// ```rust /// # use jsonptr::Pointer; /// let ptr = Pointer::from_static("/foo/bar/baz"); - /// let (head, tail) = ptr.split_at(4).unwrap(); + /// let (head, tail) = ptr.split_at_offset(4).unwrap(); /// assert_eq!(head, Pointer::from_static("/foo")); /// assert_eq!(tail, Pointer::from_static("/bar/baz")); - /// assert_eq!(ptr.split_at(3), None); + /// assert_eq!(ptr.split_at_offset(3), None); /// ``` - pub fn split_at(&self, offset: usize) -> Option<(&Self, &Self)> { + pub fn split_at_offset(&self, offset: usize) -> Option<(&Self, &Self)> { if self.0.as_bytes().get(offset).copied() != Some(b'/') { return None; } @@ -238,6 +238,22 @@ impl Pointer { unsafe { Some((Self::new_unchecked(head), Self::new_unchecked(tail))) } } + /// Splits the `Pointer` at the given offset if the character at the offset is + /// a separator slash (`'/'`), returning `Some((head, tail))`. Otherwise, + /// returns `None`. + /// + /// This method is deprecated in favor of [`Pointer::split_at_offset`]. The + /// `split_at` name is being reserved so that it can be reintroduced as a + /// position (index) based split by 1.0, matching the rest of the API (e.g. + /// [`Pointer::get`]). + #[deprecated( + since = "0.8.0", + note = "renamed to `split_at_offset` - `split_at` will become position (index) based by 1.0" + )] + pub fn split_at(&self, offset: usize) -> Option<(&Self, &Self)> { + self.split_at_offset(offset) + } + /// Splits the `Pointer` into the parent path and the last `Token`. pub fn split_back(&self) -> Option<(&Self, Token)> { self.0.rsplit_once('/').map(|(front, back)| { @@ -388,7 +404,7 @@ impl Pointer { } idx += a.encoded().len() + 1; } - self.split_at(idx).map_or(self, |(head, _)| head) + self.split_at_offset(idx).map_or(self, |(head, _)| head) } /// Attempts to delete a `serde_json::Value` based upon the path in this @@ -1434,6 +1450,34 @@ mod tests { ); } + #[test] + fn split_at_offset() { + let ptr = Pointer::from_static("/foo/bar/baz"); + // valid separator offsets split into (head, tail) + for (offset, head, tail) in [ + (0, "", "/foo/bar/baz"), + (4, "/foo", "/bar/baz"), + (8, "/foo/bar", "/baz"), + ] { + let (h, t) = ptr.split_at_offset(offset).unwrap(); + assert_eq!(h, Pointer::from_static(head)); + assert_eq!(t, Pointer::from_static(tail)); + } + // offsets that don't land on a separator return None + assert_eq!(ptr.split_at_offset(3), None); + // offset past the end returns None rather than panicking + assert_eq!(ptr.split_at_offset(ptr.as_str().len()), None); + assert_eq!(ptr.split_at_offset(usize::MAX), None); + } + + #[test] + #[allow(deprecated)] + fn split_at_delegates_to_split_at_offset() { + let ptr = Pointer::from_static("/foo/bar/baz"); + assert_eq!(ptr.split_at(4), ptr.split_at_offset(4)); + assert_eq!(ptr.split_at(3), None); + } + #[test] fn strip_suffix() { let p = Pointer::from_static("/example/pointer/to/some/value");