Skip to content

Commit

Permalink
docs wip
Browse files Browse the repository at this point in the history
  • Loading branch information
chanced committed Jul 5, 2024
1 parent 0d9f4bb commit 55dffef
Show file tree
Hide file tree
Showing 9 changed files with 226 additions and 249 deletions.
151 changes: 55 additions & 96 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,131 +1,86 @@
# jsonptr - JSON Pointers for Rust
<!-- TODO: this will become the doc comments -->

# jsonptr - JSON Pointers (RFC 6901)

[<img alt="github" src="https://img.shields.io/badge/github-chanced/jsonptr-62D1FC?style=for-the-badge&labelColor=777&logo=github" height="21">](https://github.com/chanced/jsonptr)
[<img alt="crates.io" src="https://img.shields.io/crates/v/jsonptr.svg?style=for-the-badge&color=fc8d62&logo=rust" height="21">](https://crates.io/crates/jsonptr)
[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-jsonptr-f0f0f0?style=for-the-badge&labelColor=777&logo=docs.rs" height="21">](https://docs.rs/jsonptr)
[<img alt="build status" src="https://img.shields.io/github/actions/workflow/status/chanced/jsonptr/test.yml?branch=main&style=for-the-badge" height="21">](https://github.com/chanced/jsonptr/actions?query=branch%3Amain)
[<img alt="code coverage" src="https://img.shields.io/codecov/c/github/chanced/jsonptr?style=for-the-badge&color=CBB88D" height="21">](https://codecov.io/gh/chanced/jsonptr)

Data structures and logic for resolving, assigning, and deleting by JSON
Pointers ([RFC 6901](https://datatracker.ietf.org/doc/html/rfc6901)).
This crate offers two types, [`Pointer`] and [`PointerBuf`] (akin to
[`Path`](std::path::Path) and [`PathBuf`](std::path::PathBuf)), for working with
JSON Pointers ([RFC 6901](https://datatracker.ietf.org/doc/html/rfc6901)),
abstractly.

A [`Pointer`] is composed of zero or more [`Token`]s, single segments which
represent a field of an object or an [`Index`] of an array, and are bounded by
either `'/'` or the end of the string. [`Token`]s are lightly encoded, where
`'~'` is encoded as `"~0"` and `'/'` is encoded as `"~1"`. Combined, the
[`Pointer`] is able to identify a specific location within a JSON, or similar,
document.

[`Token`]s can be iterated over using either [`Tokens`], returned from the
[`tokens`](`Pointer::tokens`) method of a pointer or [`Components`], returned
from the [`components`](`Pointer::components`). The difference being that
[`Tokens`] iterates over each [`Token`] in the pointer, while a [`Component`]
can represent the [`Root`](Component::Root) document or a single
[`Token`](Component::Token).

Operations [`resolve`], [`assign`] and [`delete`] are provided as traits with
corresponding methods on [`Pointer`]. Implementations of each trait are provided
for [`serde_json::Value`] and [`toml::Value`](https://docs.rs/toml/0.8).

## Feature Flags
| Flag | Description | Enables | Default |
| :---------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------- | :-----: |
| `"std"` | Implements `std::error::Error` for error types | `"serde/std"`, `"serde_json?/std"` ||
| `"json"` | Implements [`Resolve`], [`Assign`], [`Delete`] for [`serde_json::Value`] + a helper methods on `Pointer` | `serde_json` ||
| `"toml"` | Implements [`Resolve`], [`Assign`], [`Delete`] for [`toml::Value`] | `"std"`, `toml` | |
| `"assign"` | Enables the [`Assign`] trait and [`Pointer::assign`] for assigning values based on the location specified by a JSON Pointer | ||
| `"resolve"` | Enables the [`Resolve`] & [`ResolveMut`] trait and [`Pointer::resolve`], [`Pointer::resolve_mut`] for resolving values based on the location specified by a JSON Pointer | ||
| `"delete"` | Enables the [`Delete`] trait and [`Pointer::delete`] for deleting values based on the location specified by a JSON Pointer | `"resolve"` ||

### Resolve values

#### `Pointer::resolve`
| Flag | Description | Enables | Default |
| :---------: | ----------------------------------------------------------------------------------------------------------------------------------------- | --------------- | :-----: |
| `"std"` | Implements `std::error::Error` for error types | ||
| `"serde"` | Enables [`serde`] support for types | ||
| `"json"` | Implements ops for [`serde_json::Value`] | `"serde"` ||
| `"toml"` | Implements ops for [`toml::Value`](https://docs.rs/toml/0.8) | `"std"`, `toml` | |
| `"assign"` | Enables the [`assign`] module and related pointer methods, providing a means to assign a value to a specific location within a document | ||
| `"resolve"` | Enables the [`resolve`] module and related pointer methods, providing a means to resolve a value at a specific location within a document | ||
| `"delete"` | Enables the [`delete`] module and related pointer methods, providing a means to delete a value at a specific location within a document | `"resolve"` ||

```rust
use jsonptr::Pointer;
use serde_json::json;
## Usage

let mut data = json!({ "foo": { "bar": "baz" } });
let ptr = Pointer::from_static("/foo/bar");
let bar = ptr.resolve(&data).unwrap();
assert_eq!(bar, "baz");
```
### Resolving a value

#### `Resolve::resolve`
Using the `resolve` method, you can resolve a pointer against a value type that
implements [`Resolve`].

```rust
use jsonptr::{ Pointer, resolve::Resolve };
use serde_json::json;

let mut data = json!({ "foo": { "bar": "baz" } });
let ptr = Pointer::from_static("/foo/bar");
let bar = data.resolve(&ptr).unwrap();
assert_eq!(bar, "baz");

# use jsonptr::{Pointer};
let ptr = Pointer::parse("/foo/bar").unwrap();
let bar = ptr.resolve(&json!({"foo": {"bar": 34}})).unwrap();
assert_eq!(bar, &json!(34));
```

#### `ResolveMut::resolve_mut`

```rust
use jsonptr::{Pointer, resolve::ResolveMut};
use serde_json::json;

let ptr = Pointer::from_static("/foo/bar");
let mut data = json!({ "foo": { "bar": "baz" }});
let mut bar = data.resolve_mut(&ptr).unwrap();
assert_eq!(bar, "baz");
```

### Assign

#### `Pointer::assign`
### Resolve

```rust
use jsonptr::Pointer;
use serde_json::json;

let ptr = Pointer::from_static("/foo/bar");
let mut data = json!({});
let _previous = ptr.assign(&mut data, "qux").unwrap();
assert_eq!(data, json!({"foo": { "bar": "qux" }}))
```

#### `Assign::asign`

```rust
use jsonptr::{assign::Assign, Pointer};
use serde_json::json;

let ptr = Pointer::from_static("/foo/bar");
let mut data = json!({});
let _previous = data.assign(&ptr, "qux").unwrap();
assert_eq!(data, json!({ "foo": { "bar": "qux" }}))
let ptr = Pointer::parse("/foo/bar").unwrap();
let bar = ptr.resolve(&json!({"foo": {"bar": 34}})).unwrap();
assert_eq!(bar, &json!(34));
```

### Delete

#### `Pointer::delete`
### Assign

```rust
use jsonptr::Pointer;
use serde_json::json;

let mut data = json!({ "foo": { "bar": { "baz": "qux" } } });
let ptr = Pointer::from_static("/foo/bar/baz");
assert_eq!(ptr.delete(&mut data), Some("qux".into()));
assert_eq!(data, json!({ "foo": { "bar": {} } }));

// unresolved pointers return None
let ptr = Pointer::parse("/foo/bar").unwrap();
let mut data = json!({});
assert_eq!(ptr.delete(&mut data), None);
let res = ptr.assign(&mut data, json!({"baz": 34}));
assert_eq!(res, Ok(None));
assert_eq!(data, json!({"foo": {"bar": {"baz": 34}}}));
```

#### `Delete::delete`

```rust
use jsonptr::{ Pointer, delete::Delete };
use serde_json::json;

let mut data = json!({ "foo": { "bar": { "baz": "qux" } } });
let ptr = Pointer::from_static("/foo/bar/baz");
assert_eq!(ptr.delete(&mut data), Some("qux".into()));
assert_eq!(data, json!({ "foo": { "bar": {} } }));

// replacing a root pointer replaces data with `Value::Null`
let ptr = Pointer::root();
let deleted = json!({ "foo": { "bar": {} } });
assert_eq!(data.delete(&ptr), Some(deleted));
assert!(data.is_null());
```

## Feature Flags

| Flag | Enables |
| :-----: | ----------------------------------------- |
| `"std"` | implements `std::error::Error` for errors |

## Contributions / Issues

Contributions and feedback are always welcome and appreciated.
Expand All @@ -135,3 +90,7 @@ If you find an issue, please open a ticket or a pull request.
## License

MIT or Apache 2.0.

```
```
41 changes: 41 additions & 0 deletions src/component.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use crate::{Pointer, Token, Tokens};

pub enum Component<'t> {

Check warning on line 3 in src/component.rs

View workflow job for this annotation

GitHub Actions / clippy

[clippy] src/component.rs#L3

warning: missing documentation for an enum --> src/component.rs:3:1 | 3 | pub enum Component<'t> { | ^^^^^^^^^^^^^^^^^^^^^^
Raw output
src/component.rs:3:1:w:warning: missing documentation for an enum
 --> src/component.rs:3:1
  |
3 | pub enum Component<'t> {
  | ^^^^^^^^^^^^^^^^^^^^^^


__END__
/// 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)
}
}

pub struct Components<'t> {

Check warning on line 15 in src/component.rs

View workflow job for this annotation

GitHub Actions / clippy

[clippy] src/component.rs#L15

warning: missing documentation for a struct --> src/component.rs:15:1 | 15 | pub struct Components<'t> { | ^^^^^^^^^^^^^^^^^^^^^^^^^
Raw output
src/component.rs:15:1:w:warning: missing documentation for a struct
  --> src/component.rs:15:1
   |
15 | pub struct Components<'t> {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^


__END__
tokens: Tokens<'t>,
}
impl<'t> Components<'t> {
pub(crate) fn new(tokens: Tokens<'t>) -> Self {
Self { tokens }
}
}

impl<'t> Iterator for Components<'t> {
type Item = Component<'t>;
fn next(&mut self) -> Option<Self::Item> {
if !self.tokens.has_sent {
self.tokens.has_sent = true;
return Some(Component::Root);
}
self.tokens.next().map(Component::Token)
}
}

impl<'t> From<&'t Pointer> for Components<'t> {
fn from(pointer: &'t Pointer) -> Self {
Self {
tokens: pointer.tokens(),
}
}
}
71 changes: 36 additions & 35 deletions src/index.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,3 @@
//! Abstract index representation for RFC 6901.
//!
//! [RFC 6901](https://datatracker.ietf.org/doc/html/rfc6901) defines two valid
//! ways to represent array indices as Pointer tokens: non-negative integers,
//! and the character `-`, which stands for the index after the last existing
//! array member. While attempting to use `-` to access an array is an error,
//! the token can be useful when paired with [RFC
//! 6902](https://datatracker.ietf.org/∑doc/html/rfc6902) as a way to express
//! where to put the new element when extending an array.
//!
//! While this crate doesn't implement RFC 6902, it still must consider
//! non-numerical indices as valid, and provide a mechanism for manipulating
//! them. This is what this module provides.
//!
//! The main use of the `Index` type is when resolving a [`Token`] instance as a
//! concrete index for a given array length:
//!
//! ```
//! # use jsonptr::{Index, Token};
//! assert_eq!(Token::new("1").to_index(), Ok(Index::Num(1)));
//! assert_eq!(Token::new("-").to_index(), Ok(Index::Next));
//! assert!(Token::new("a").to_index().is_err());
//!
//! assert_eq!(Index::Num(0).for_len(1), Ok(0));
//! assert!(Index::Num(1).for_len(1).is_err());
//! assert!(Index::Next.for_len(1).is_err());
//!
//! assert_eq!(Index::Num(1).for_len_incl(1), Ok(1));
//! assert_eq!(Index::Next.for_len_incl(1), Ok(1));
//! assert!(Index::Num(2).for_len_incl(1).is_err());
//!
//! assert_eq!(Index::Num(42).for_len_unchecked(30), 42);
//! assert_eq!(Index::Next.for_len_unchecked(30), 30);
//! ````
use crate::Token;
use alloc::string::String;
use core::{
Expand All @@ -45,6 +10,42 @@ use core::{
///
/// If provided an upper bound with [`Self::for_len`] or [`Self::for_len_incl`],
/// will produce a concrete numerical index.
///
/// [RFC 6901](https://datatracker.ietf.org/doc/html/rfc6901) defines two valid
/// ways to represent array indices as Pointer tokens: non-negative integers,
/// and the character `-`, which stands for the index after the last existing
/// array member. While attempting to use `-` to access an array is an error,
/// the token can be useful when paired with [RFC
/// 6902](https://datatracker.ietf.org/∑doc/html/rfc6902) as a way to express
/// where to put the new element when extending an array.
///
/// While this crate doesn't implement RFC 6902, it still must consider
/// non-numerical indices as valid, and provide a mechanism for manipulating
/// them. This is what this module provides.
///
/// The main use of the `Index` type is when resolving a [`Token`] instance as a
/// concrete index for a given array length:
///
/// ## Example
/// ```
/// # use jsonptr::{Index, Token};
/// assert_eq!(Token::new("1").to_index(), Ok(Index::Num(1)));
/// assert_eq!(Token::new("-").to_index(), Ok(Index::Next));
/// assert!(Token::new("a").to_index().is_err());
///
/// assert_eq!(Index::Num(0).for_len(1), Ok(0));
/// assert!(Index::Num(1).for_len(1).is_err());
/// assert!(Index::Next.for_len(1).is_err());
///
/// assert_eq!(Index::Num(1).for_len_incl(1), Ok(1));
/// assert_eq!(Index::Next.for_len_incl(1), Ok(1));
/// assert!(Index::Num(2).for_len_incl(1).is_err());
///
/// assert_eq!(Index::Num(42).for_len_unchecked(30), 42);
/// assert_eq!(Index::Next.for_len_unchecked(30), 30);
/// ```
///
///
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Index {
/// A non-negative integer value
Expand Down
59 changes: 8 additions & 51 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,45 +1,4 @@
//! # jsonptr - JSON Pointers (RFC 6901)
//! [<img alt="github"
//! src="https://img.shields.io/badge/github-chanced/jsonptr-62D1FC?style=for-the-badge&labelColor=777&logo=github"
//! height="24">](https://github.com/chanced/jsonptr) [<img alt="docs.rs"
//! src="https://img.shields.io/badge/docs.rs-jsonptr-f0f0f0?style=for-the-badge&labelColor=777&logo=docs.rs"
//! height="24">](https://docs.rs/jsonptr) [<img alt="crates.io"
//! src="https://img.shields.io/crates/v/jsonptr.svg?style=for-the-badge&color=fc8d62&logo=rust"
//! height="24">](https://crates.io/crates/jsonptr) [<img alt="build status"
//! src="https://img.shields.io/github/actions/workflow/status/chanced/jsonptr/test.yml?branch=main&style=for-the-badge"
//! height="24">](https://github.com/chanced/jsonptr/actions?query=branch%3Amain)
//! [<img alt="code coverage"
//! src="https://img.shields.io/codecov/c/github/chanced/jsonptr?style=for-the-badge&color=CBB88D"
//! height="24">](https://codecov.io/gh/chanced/jsonptr)
//!
//! JSON Pointers are a lightly encoded strings that provide a syntax for
//! identifying a specific value within a JSON document. A properly formatted
//! JSON Pointer is composed of either an empty string, indicating the root
//! document, or a series of tokens, each with a leading backslash (`'/'`) and
//! encoded with the following rules:
//! - `'~'` is encoded as `'~0'`
//! - `'/'` is encoded as `'~1'`
//!
//! This module provides data structures and logic for resolving, assigning, and
//! deleting by JSON Pointers ([RFC
//! 6901](https://datatracker.ietf.org/doc/html/rfc6901)). Two types,
//! [`PointerBuf`] and [`Pointer`] (akin to [`String`] and [`str`]), are
//! available for representing JSON Pointers while the operations ([`Resolve`],
//! [`ResolveMut`], [`Assign`], [`Delete`]) are exposed as traits and
//! implemented for [`serde_json::Value`] and [`toml::Value`] if the respective
//! features are enabled.
//!
//! ## Feature Flags
//!
//! | Flag | Description | Enables | Default |
//! | :------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------- | :-----: |
//! | `"std"` | Implements `std::error::Error` for error types | `"serde/std"`, `"serde_json?/std"` | ✓ |
//! | `"json"` | Implements [`Resolve`], [`Assign`], [`Delete`] for [`serde_json::Value`] | `serde_json` | ✓ |
//! | `"toml"` | Implements [`Resolve`], [`Assign`], [`Delete`] for [`toml::Value`] | `"std"`, `toml` | |
//! | `"assign"` | Enables the [`Assign`] trait and [`Pointer::assign`] for assigning values based on the location specified by a JSON Pointer | | ✓ |
//! | `"resolve"` | Enables the [`Resolve`] & [`ResolveMut`] trait and [`Pointer::resolve`], [`Pointer::resolve_mut`] for resolving values based on the location specified by a JSON Pointer | | ✓ |
//! | `"delete"` | Enables the [`Delete`] trait and [`Pointer::delete`] for deleting values based on the location specified by a JSON Pointer | `"resolve"` | ✓ |
//!
#![doc = include_str!("../README.md")]
#![warn(missing_docs)]
#![deny(clippy::all, clippy::pedantic)]
#![cfg_attr(not(feature = "std"), no_std)]
Expand All @@ -55,8 +14,6 @@
#[cfg_attr(not(feature = "std"), macro_use)]
extern crate alloc;

pub mod prelude;

#[cfg(feature = "assign")]
pub mod assign;
#[cfg(feature = "assign")]
Expand All @@ -72,17 +29,17 @@ pub mod resolve;
#[cfg(feature = "resolve")]
pub use resolve::{Resolve, ResolveMut};

mod tokens;
pub use tokens::*;

mod pointer;
pub use pointer::*;
pub use pointer::{ParseError, Pointer, PointerBuf, ReplaceTokenError};

mod token;
pub use token::*;
pub use token::{InvalidEncodingError, Token, Tokens};

mod index;
pub use index::{Index, OutOfBoundsError, ParseIndexError};

pub mod index;
pub use index::Index;
mod component;
pub use component::{Component, Components};

#[cfg(test)]
mod arbitrary;
Loading

0 comments on commit 55dffef

Please sign in to comment.