jsn 0.14.0

A library for querying streaming JSON tokens
Documentation
#![warn(missing_docs)]

//! This crate implements a queryable, streaming, JSON pull parser.
//!
//! - __Pull parser?__: The [parser](crate::TokenReader) is implemented as iterator that emits
//! [tokens](crate::Token).
//! - __Streaming?__: The JSON document being parsed is never fully loaded into memory. It is read
//! & validated byte by byte. This makes it ideal for dealing with large JSON documents
//! - __Queryable?__ You can [configure the parser](crate::TokenReader::with_mask) to only emit & allocate
//! tokens for the parts of the input you are interested in.
//!
//! JSON is expected to conform to [RFC
//! 8259](https://datatracker.ietf.org/doc/html/rfc8259).
//! However, [newline-delimited JSON](https://github.com/ndjson/ndjson-spec) and [concatenated
//! json](https://en.wikipedia.org/wiki/JSON_streaming#Concatenated_JSON) formats are also
//! [supported](crate::TokenReader::with_format).
//!
//! Input can be anything that implements the [`Read`](std::io::Read) trait (e.g. a file, byte
//! slice, network socket etc..)
//!
//! ## Basic Usage
//!
//! ```
//! use jsn::{TokenReader, mask::*, Format};
//! use std::error::Error;
//!
//! fn main() -> Result<(), Box<dyn Error>> {
//!     let data = r#"
//!         {
//!             "name": "John Doe",
//!             "age": 43,
//!             "nicknames": [ "joe" ],
//!             "phone": {
//!                 "carrier": "Verizon",
//!                 "numbers": [ "+44 1234567", "+44 2345678" ]
//!             }
//!         }
//!         {
//!             "name": "Jane Doe",
//!             "age": 32,
//!             "nicknames": [ "J" ],
//!             "phone": {
//!                 "carrier": "AT&T",
//!                 "numbers": ["+33 38339"]
//!             }
//!         }
//!     "#;
//!
//!     let mask = key("numbers").and(index(0))
//!         .or(key("name"))
//!         .or(key("age"));
//!     let mut iter = TokenReader::new(data.as_bytes())
//!         .with_mask(mask)
//!         .with_format(Format::Concatenated)
//!         .into_iter();
//!
//!     assert_eq!(iter.next().unwrap()?, "John Doe");
//!     assert_eq!(iter.next().unwrap()?, 43);
//!     assert_eq!(iter.next().unwrap()?, "+44 1234567");
//!     assert_eq!(iter.next().unwrap()?, "Jane Doe");
//!     assert_eq!(iter.next().unwrap()?, 32);
//!     assert_eq!(iter.next().unwrap()?, "+33 38339");
//!     assert_eq!(iter.next(), None);
//!
//!     Ok(())
//! }
//! ```
//!
//! __A few things to notice and whet your appetite:__
//! - This is new-line delimited JSON
//! - We only pay for heap allocating the tokens we extracted (i.e. the first index in the
//! "numbers" array and the "name" and "age" values).
//! - You can compare tokens to native rust types
//! - Token masks match anywhere in json; Though the top-level value was an object, we used the
//! `index` mask to match a token that was at index 0 in an array nested in the "phones" object.

mod error;
mod input;
mod iter;
pub mod mask;
mod raw_token;
mod scan;
mod structure;
mod token;

pub use error::{JsonError, Position, Reason};
pub use iter::{DryRun, Format, TokenReader, Tokens};
pub use token::{FromJson, Token};

#[doc = include_str!("../README.md")]
#[cfg(doctest)]
pub struct ReadmeDocTests;