Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

modifies components + adds walk #62

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 75 additions & 23 deletions src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,59 @@ use crate::{Pointer, Token, Tokens};

/// A single [`Token`] or the root of a JSON Pointer
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Component<'t> {
pub enum Component<'p> {
/// The document root
Root,
/// A segment of a JSON Pointer
Token(Token<'t>),
}
impl<'t> From<Token<'t>> for Component<'t> {
fn from(token: Token<'t>) -> Self {
Self::Token(token)
}
/// A token of a [`Pointer`]
Token {
/// the [`Token`]
token: Token<'p>,
/// The full location of this `Token`, including the `Token` itself
pointer: &'p Pointer,
/// index of the token in the pointer
index: usize,
/// offset of the [`Token`] in the pointer
offset: usize,
},
}

/// An iterator over the [`Component`]s of a JSON Pointer
#[derive(Debug)]
pub struct Components<'t> {
tokens: Tokens<'t>,
sent_root: bool,
pub struct Components<'p> {
pointer: &'p Pointer,
tokens: Tokens<'p>,
sent: usize,
offset: usize,
}

impl<'t> Iterator for Components<'t> {
type Item = Component<'t>;
impl<'p> Iterator for Components<'p> {
type Item = Component<'p>;
fn next(&mut self) -> Option<Self::Item> {
if !self.sent_root {
self.sent_root = true;
if self.sent == 0 {
self.sent += 1;
return Some(Component::Root);
}
self.tokens.next().map(Component::Token)
let token = self.tokens.next()?;
let offset = self.offset;
let index = self.sent - 1;

self.offset += 1 + token.encoded().len();
self.sent += 1;
Some(Component::Token {
token,
offset,
index,
pointer: self.pointer.partial(index).unwrap(),
})
}
}

impl<'t> From<&'t Pointer> for Components<'t> {
fn from(pointer: &'t Pointer) -> Self {
Self {
sent_root: false,
pointer,
offset: 0,
sent: 0,
tokens: pointer.tokens(),
}
}
Expand All @@ -55,7 +74,15 @@ mod tests {
let components = ptr.components().collect::<Vec<_>>();
assert_eq!(
components,
vec![Component::Root, Component::Token("foo".into())]
vec![
Component::Root,
Component::Token {
token: "foo".into(),
index: 0,
offset: 0,
pointer: Pointer::from_static("/foo")
}
]
);

let ptr = Pointer::from_static("/foo/bar/-/0/baz");
Expand All @@ -64,11 +91,36 @@ mod tests {
components,
vec![
Component::Root,
Component::from(Token::from("foo")),
Component::Token("bar".into()),
Component::Token("-".into()),
Component::Token("0".into()),
Component::Token("baz".into())
Component::Token {
token: "foo".into(),
offset: 0,
index: 0,
pointer: Pointer::from_static("/foo"),
},
Component::Token {
token: "bar".into(),
index: 1,
offset: 4,
pointer: Pointer::from_static("/foo/bar")
},
Component::Token {
token: "-".into(),
index: 2,
offset: 8,
pointer: Pointer::from_static("/foo/bar/-")
},
Component::Token {
token: "0".into(),
index: 3,
offset: 10,
pointer: Pointer::from_static("/foo/bar/-/0")
},
Component::Token {
token: "baz".into(),
index: 4,
offset: 12,
pointer: Pointer::from_static("/foo/bar/-/0/baz")
}
]
);
}
Expand Down
41 changes: 37 additions & 4 deletions src/pointer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,17 @@ impl Pointer {
unsafe { &*(core::ptr::from_ref::<str>(s) as *const Self) }
}

/// Returns the length in bytes of the encoded string representation of this
/// `Pointer`.
pub fn len(&self) -> usize {
self.0.len()
}
/// Returns `true` if `self` is a root pointer, meaning it has a length of
/// zero bytes.
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}

/// The encoded string representation of this `Pointer`
pub fn as_str(&self) -> &str {
&self.0
Expand Down Expand Up @@ -125,6 +136,27 @@ impl Pointer {
self.0.is_empty()
}

/// Returns the partial pointer which includes all tokens up to and
/// including the token at the the given index.
// TODO: this needs a better name
pub(crate) fn partial(&self, index: usize) -> Option<&Self> {
let mut split = self.0.split('/').skip(1).enumerate();
let mut offset = 0;

#[allow(clippy::while_let_loop, clippy::while_let_on_iterator)]
while let Some((i, tok)) = split.next() {
if i == index {
let Some((_, tok)) = split.next() else {
return Some(self);
};
offset = offset + tok.len() + 1;
return Some(Self::new(&self.0[..offset]));
}
offset += tok.len() + 1;
}
None
}

/// Returns a `serde_json::Value` representation of this `Pointer`
#[cfg(feature = "json")]
pub fn to_json_value(&self) -> serde_json::Value {
Expand Down Expand Up @@ -2006,9 +2038,10 @@ mod tests {
}

#[test]
fn borrow() {
let ptr = PointerBuf::from_tokens(["foo", "bar"]);
let borrowed: &Pointer = ptr.borrow();
assert_eq!(borrowed, "/foo/bar");
fn partial() {
let p = Pointer::from_static("/foo/bar/baz");
assert_eq!(p.partial(0).unwrap(), "/foo");
assert_eq!(p.partial(1).unwrap(), "/foo/bar");
assert_eq!(p.partial(2).unwrap(), "/foo/bar/baz");
}
}
71 changes: 69 additions & 2 deletions src/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@
//! | TOML | `toml::Value` | `"toml"` | |
//!
//!
use core::{borrow::Borrow, fmt, ops::ControlFlow};

Check warning on line 36 in src/resolve.rs

View workflow job for this annotation

GitHub Actions / clippy

[clippy] src/resolve.rs#L36

warning: unused import: `borrow::Borrow` --> src/resolve.rs:36:12 | 36 | use core::{borrow::Borrow, fmt, ops::ControlFlow}; | ^^^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default
Raw output
src/resolve.rs:36:12:w:warning: unused import: `borrow::Borrow`
  --> src/resolve.rs:36:12
   |
36 | use core::{borrow::Borrow, fmt, ops::ControlFlow};
   |            ^^^^^^^^^^^^^^
   |
   = note: `#[warn(unused_imports)]` on by default


__END__

use crate::{
index::{OutOfBoundsError, ParseIndexError},
Pointer, Token,
Component, Pointer, Token,
};

/*
Expand All @@ -55,14 +57,59 @@
type Value;

/// Error associated with `Resolve`
type Error;
type Error: fmt::Debug;

/// Resolve a reference to `Self::Value` based on the path in a [Pointer].
///
/// ## Errors
/// Returns a [`Self::Error`](Resolve::Error) if the [`Pointer`] can not
/// be resolved.
fn resolve(&self, ptr: &Pointer) -> Result<&Self::Value, Self::Error>;

/// TODO
/// ## Errors
fn walk_to<F, B>(&self, ptr: &Pointer, f: F) -> Result<ControlFlow<B>, Self::Error>
where
F: Fn(Component, &Self::Value) -> ControlFlow<B>,
{
let _ = self.resolve(ptr)?;
let root = self.resolve(Pointer::root()).unwrap();
let mut last = None;
for component in ptr.components() {
match component {
Component::Root => {
last = Some(f(Component::Root, root));
}
Component::Token {
token,
index,
offset,
pointer,
} => {
let value = self.resolve(pointer).unwrap();
let tok_len = token.encoded().len();
let ctrl_flow = f(
Component::Token {
token,
pointer,
index,
offset,
},
value,
);
if offset + tok_len >= ptr.len() {
// last token
return Ok(ctrl_flow);
}
if ctrl_flow.is_break() {
return Ok(ctrl_flow);
}
last = Some(ctrl_flow);
}
}
}
Ok(last.unwrap())
}
}

/*
Expand Down Expand Up @@ -724,6 +771,26 @@
]);
}

#[test]
#[cfg(feature = "json")]
fn json_walk_to() {
use core::ops::ControlFlow;

use serde_json::json;

let value = json!({
"foo": {
"bar": {
"baz": {
"qux": [0,1,2,3]
}
}
}
});

let ptr = Pointer::from_static("/foo/bar/baz/qux/2");
value.walk_to(ptr, |c, v| {});
}
/*
╔═══════════════════════════════════════════════════╗
║ toml ║
Expand Down
Loading