Skip to content

Commit

Permalink
Add Pointer::starts_with and Pointer::ends_with (#81)
Browse files Browse the repository at this point in the history
* add starts_with and ends_with

* update changelog
  • Loading branch information
asmello authored Oct 21, 2024
1 parent b423489 commit 7742c3b
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Adds method `into_buf` for `Box<Pointer>` and `impl From<PathBuf> for Box<Pointer>`.
- 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.

Expand Down
74 changes: 70 additions & 4 deletions src/pointer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,12 +280,26 @@ impl Pointer {
.map(|s| unsafe { Self::new_unchecked(s) })
}

/// Attempts to get a `Token` or a segment of the `Pointer`, depending on
/// the type of index.
/// Returns whether `self` has a suffix of `other`.
///
/// Returns `None` if the index is out of bounds.
/// Note that `Pointer::root` is only a valid suffix of itself.
pub fn ends_with(&self, other: &Self) -> bool {
(self.is_root() && other.is_root())
|| (!other.is_root() && self.as_str().ends_with(&other.0))
}

/// Returns whether `self` has a prefix of `other.`
///
/// Note that this operation is O(n).
/// Note that `Pointer::root` is a valid prefix of any `Pointer` (including
/// itself).
pub fn starts_with(&self, other: &Self) -> bool {
self.as_str().starts_with(&other.0)
// ensure we end at a token boundary
&& (other.len() == self.len() || self.0.as_bytes()[other.len()] == b'/')
}

/// Attempts to get a `Token` by the index. Returns `None` if the index is
/// out of bounds.
///
/// ## Example
/// ```rust
Expand Down Expand Up @@ -1372,6 +1386,58 @@ mod tests {
assert_eq!(stripped, "/to/some/value");
}

#[test]
fn ends_with() {
// positive cases
let p = Pointer::from_static("/foo/bar");
let q = Pointer::from_static("/bar");
assert!(p.ends_with(q));
let q = Pointer::from_static("/foo/bar");
assert!(p.ends_with(q));

// negative cases
let q = Pointer::from_static("/barz");
assert!(!p.ends_with(q));
let q = Pointer::from_static("/");
assert!(!p.ends_with(q));
let q = Pointer::from_static("");
assert!(!p.ends_with(q));
let q = Pointer::from_static("/qux/foo/bar");
assert!(!p.ends_with(q));

// edge case - both root
let p = Pointer::root();
let q = Pointer::root();
assert!(p.ends_with(q));
}

#[test]
fn starts_with() {
// positive cases
let p = Pointer::from_static("/foo/bar");
let q = Pointer::from_static("/foo");
assert!(p.starts_with(q));
let q = Pointer::from_static("/foo/bar");
assert!(p.starts_with(q));

// negative cases
let q = Pointer::from_static("/");
assert!(!p.starts_with(q));
let q = Pointer::from_static("/fo");
assert!(!p.starts_with(q));
let q = Pointer::from_static("/foo/");
assert!(!p.starts_with(q));

// edge cases: other is root
let p = Pointer::root();
let q = Pointer::root();
assert!(p.starts_with(q));
let p = Pointer::from_static("/");
assert!(p.starts_with(q));
let p = Pointer::from_static("/any/thing");
assert!(p.starts_with(q));
}

#[test]
fn parse_error_is_no_leading_backslash() {
let err = ParseError::NoLeadingBackslash;
Expand Down

0 comments on commit 7742c3b

Please sign in to comment.