Skip to main content

Evaluator

Struct Evaluator 

Source
pub struct Evaluator<L, G> { /* private fields */ }
Available on crate feature pointer only.
Expand description

Evaluates JSON Pointers against a stream of JSON text.

An Evaluator wraps a syntax::Parser in a lightweight, stream-oriented, JSON Pointer evaluation layer.

§Events

Unlike lexical::Analyzer and syntax::Parser, an evaluator produces Event values instead of raw lexical tokens.

An Event connects a lexical token with an optional JSON Pointer match event. If [ or { triggers Event::Enter, all subsequent tokens, until the matching ] or } triggers the corresponding Event::Exit, are known to be contained within the connected pointer. If a primitive token triggers Event::Match, that single token matches the paired pointer.

§Escape expansion

Object member name strings can possibly include JSON escape sequences (e.g., \n, \", \u1234, and so on). When it compares object member names against its JSON Pointer group, Evaluator can optionally either expand the JSON escape sequences (e.g., \t becomes the tab character, and so on); or it can do a literal comparison (e.g., \t is treated as the two characters \ and t). This is controlled by the unescape parameter in new. There is a slight performance hit for escape expansion.

§Case sensitivity

JSON Pointer evaluation is case-sensitive by default, but it can optionally be switched to case-insensitive by constructing a case-insensitive pointer group. Refer to the Group documentation for more.

§Performance considerations

Evaluator is a very lightweight type. Its performance, allocation behavior, and memory consumption are almost entirely determined by the wrapped parser, which is in turn determined by the parser’s wrapped lexical analyzer. An evaluator is as performant as its underlying lexer implementation.

The next method only triggers allocation if:

  1. The underlying parser’s next method allocates.
  2. A { or [ in a matchable path causes the parser’s current nesting level to exceed about 3-4. This is only possible for pointers with more than 3 levels (e.g. /1/2/3/4) and then only if there is a viable path into such a pointer.
  3. Member name escape expansion is turned on (unescape = true) and the escape expansion buffer needs to grow. This should be rare in practice, since the buffer is reused.

The next method’s companion methods, next_non_white and next_meaningful have the same underlying behavior as next.

§Memory considerations

The content method passes through the underlying lexical analyzer’s content. The content value may contain references to internal buffers that will not be deallocated until the content value is dropped. Refer to the specific lexical analyzer’s documentation for more.

§Examples

Create an evaluator with new:

use bufjson::{lexical::fixed::FixedAnalyzer, pointer::{Evaluator, Group, Pointer}};

// Create the underlying lexer and parser.
let parser = FixedAnalyzer::new(&b"[1, 2, 3]"[..]).into_parser();

// Create the JSON Pointer group.
let group = Group::from_pointers([Pointer::from_static(""), Pointer::from_static("/1")]);

// Create an evaluator that does expand escape sequences in member names.
let mut evaluator = Evaluator::new(parser, group, /* unescape */ true);

// Use the evaluator...

Extract the JSON text that matches the pointer /foo/2.

use bufjson::{lexical::fixed::FixedAnalyzer, pointer::{Evaluator, Event, Group, Pointer}};

// Create the evaluator.
let parser = FixedAnalyzer::new(&br#"{"foo":[null, 1, {"bar":true}]}"#[..]).into_parser();
let group = Group::from_pointer(Pointer::from_static("/foo/2"));
let mut evaluator = Evaluator::new(parser, group, /* unescape */ false);

// Extract the tokens that match.
let mut extract = String::new();
let mut in_match = false;
loop {
   let event = evaluator.next();
   match event {
       Event::Enter(t, _) | Event::Exit(t, _) => {
           in_match = event.is_enter();
           extract.push_str(&format!("{}", evaluator.content()));
       },
       _ if in_match && !event.token().is_terminal() => {
           extract.push_str(&format!("{}", evaluator.content()));
       },
       _ if event.token().is_terminal() => break,
       _ => (),
   }
}

assert_eq!(r#"{"bar":true}"#, extract);

Implementations§

Source§

impl<L, G> Evaluator<L, G>
where L: Analyzer, L::Error: 'static, G: AsRef<Group>,

Source

pub fn new(parser: Parser<L>, group: G, unescape: bool) -> Self

Constructs a new evaluator wrapping an underlying parser and JSON Pointer group.

The parser and group can be unwrapped using into_parts.

The group can be an owned Group or anything that is AsRef<Group>. Since Group is immutable, the latter approach allows a single group to be shared across many evaluators.

§Example
use bufjson::{lexical::fixed::FixedAnalyzer, pointer::{Evaluator, Group, Pointer}};

// Create the underlying lexer and parser.
let parser = FixedAnalyzer::new(&b"[1, 2, 3]"[..]).into_parser();

// Create the JSON Pointer group.
let group = Group::from_pointers([Pointer::from_static(""), Pointer::from_static("/1")]);

// Create an evaluator that does expand escape sequences in member names.
let mut evaluator = Evaluator::new(parser, group, /* unescape */ true);

// Use the evaluator...
Source

pub fn next(&mut self) -> Event<&Pointer>

Returns the next evaluation event.

The event contains the next syntactically valid lexical token. For enter, exit, and match events it also contains the matched JSON Pointer.

If a lexical or syntax error is detected, the event returned is Event::Nil containing a Token::Err and the specific error can be obtained from err. Otherwise, the event contains the next non-error token and the token content can be obtained from content.

§Example
use bufjson::{lexical::fixed::FixedAnalyzer, pointer::{Evaluator, Event, Group, Pointer}};

// Create the evaluator.
let parser = FixedAnalyzer::new(&br#"{"foo":[null, 1, {"bar":true}]}"#[..]).into_parser();
let group = Group::from_pointer(Pointer::from_static("/foo/2"));
let mut evaluator = Evaluator::new(parser, group, false);

// Extract the tokens that match.
let mut extract = String::new();
let mut in_match = false;
loop {
   let event = evaluator.next();
   match event {
       Event::Enter(t, _) | Event::Exit(t, _) => {
           in_match = event.is_enter();
           extract.push_str(&format!("{}", evaluator.content()));
       },
       _ if in_match && !event.token().is_terminal() => {
           extract.push_str(&format!("{}", evaluator.content()));
       },
       _ if event.token().is_terminal() => break,
       _ => (),
   }
}

assert_eq!(r#"{"bar":true}"#, extract);
Source

pub fn next_non_white(&mut self) -> Event<&Pointer>

Returns the next evaluation event containing a non-whitespace token, i.e. next but skips whitespace.

This is a convenience method to simplify evaluation in use cases where whitespace does not need to be preserved.

See also next_meaningful.

§Example
use bufjson::{
    lexical::{Token, fixed::FixedAnalyzer},
    pointer::{Evaluator, Event, Group, Pointer}
};

// Create the evaluator.
let parser = FixedAnalyzer::new(&br#"   "hello, world"    "#[..]).into_parser();
let root = Pointer::default();
let group = Group::from_pointer(root.clone());
let mut evaluator = Evaluator::new(parser, group, false);

// Get the evaluation events, skipping the leading and trailing whitespace.
assert!(matches!(evaluator.next_non_white(), Event::Match(Token::Str, p) if *p == root));
assert!(matches!(evaluator.next_non_white(), Event::Nil(Token::Eof)));
Source

pub fn next_meaningful(&mut self) -> Event<&Pointer>

Returns the next evaluation event containing a meaningful token.

This method skips whitespace like next_non_white but also skips past the following meaningless punctuation characters:

  1. : or Token::NameSep;
  2. , or Token::ValueSep.

The colon : and comma , are meaningless because, even though they are required by the JSON spec (and sometimes necessary for tokenization), they don’t add any meaning to the stream of lexical tokens.

See also next_non_white.

§Example
use bufjson::{
    lexical::{Token, fixed::FixedAnalyzer},
    pointer::{Evaluator, Event, Group, Pointer}
};

// Create the evaluator.
let parser = FixedAnalyzer::new(&br#"   {"foo": "bar"}    "#[..]).into_parser();
let ptr = Pointer::from_static("/foo");
let group = Group::from_pointer(ptr.clone());
let mut evaluator = Evaluator::new(parser, group, false);

// Get the evaluation events, skipping meaningless tokens.
assert!(matches!(evaluator.next_meaningful(), Event::Nil(Token::ObjBegin)));    // {
assert!(matches!(evaluator.next_meaningful(), Event::Nil(Token::Str)));         // "foo"
assert!(                                                                        // "bar"
    matches!(evaluator.next_meaningful(),
    Event::Match(Token::Str, p) if *p == ptr)
);
assert!(matches!(evaluator.next_meaningful(), Event::Nil(Token::ObjEnd)));      // }
assert!(matches!(evaluator.next_meaningful(), Event::Nil(Token::Eof)));
Source

pub fn content(&self) -> L::Content

Fetches the text content for the current non-error token.

The current token is the token contained in the event most recently returned by next, next_non_white, or next_meaningful.

This method does not allocate unless the underlying lexical analyzer’s try_content method allocates.

§Panics

Panics if the current token is Token::Err.

§Example
use bufjson::{
    lexical::{Token, fixed::FixedAnalyzer},
    pointer::{Evaluator, Event, Group, Pointer}
};

let parser = FixedAnalyzer::new(&b"123"[..]).into_parser();
let group = Group::from_pointer(Pointer::from_static("/foo"));
let mut evaluator = Evaluator::new(parser, group, false);

assert!(matches!(evaluator.next(), Event::Nil(Token::Num)));
assert_eq!("123", evaluator.content().literal());
Source

pub fn err(&self) -> Error

Fetches the error value associated with the current error token.

The current token is the token contained in the event most recently returned by next, next_non_white, or next_meaningful.

§Panics

Panics if the current token is not Token::Err.

§Example
use bufjson::{
    lexical::{Token, fixed::FixedAnalyzer},
    pointer::{Evaluator, Event, Group, Pointer},
    syntax::ErrorKind,
};

let parser = FixedAnalyzer::new(&b"{]"[..]).into_parser();
let group = Group::from_pointer(Pointer::from_static("/foo"));
let mut evaluator = Evaluator::new(parser, group, false);

assert!(matches!(evaluator.next(), Event::Nil(Token::ObjBegin)));   // {
assert!(matches!(evaluator.next(), Event::Nil(Token::Err)));        // ]
assert!(matches!(evaluator.err().kind(), ErrorKind::Syntax { .. }));
Source

pub fn pos(&self) -> &Pos

Returns the position of the current lexical token.

The current token is the token contained in the event most recently returned by next, next_non_white, or next_meaningful.

Source

pub fn try_content(&self) -> Result<L::Content, Error>

Fetches the content or error associated with the current token.

The current token is the token contained in event most recently returned by next, next_non_white, or next_meaningful.

If the current token is Token::Err, an Err result is returned. Otherwise, an Ok result containing the text content of the recognized lexical token is returned.

This method does not allocate unless the underlying lexical analyzer’s try_content method allocates.

§Examples

An Ok value is returned as long as the evaluator isn’t in an error state.

let parser = FixedAnalyzer::new(&b"[123"[..]).into_parser();
let group = Group::from_pointer(Pointer::from_static("/foo"));
let mut evaluator = Evaluator::new(parser, group, false);
assert!(matches!(evaluator.next(), Event::Nil(Token::ArrBegin)));
assert!(matches!(evaluator.next(), Event::Nil(Token::Num)));
assert!(matches!(evaluator.try_content(), Ok(c) if c.literal() == "123"));

Once the evaluator detects an error, it will return an Err value describing the error.

use bufjson::{
    lexical::{Token, fixed::FixedAnalyzer},
    pointer::{Evaluator, Event, Group, Pointer},
    syntax::ErrorKind,
};

let parser = FixedAnalyzer::new(&b"[123"[..]).into_parser();
let group = Group::from_pointer(Pointer::from_static("/foo"));
let mut evaluator = Evaluator::new(parser, group, false);
assert!(matches!(evaluator.next(), Event::Nil(Token::ArrBegin)));
assert!(matches!(evaluator.next(), Event::Nil(Token::Num)));
assert!(matches!(evaluator.next(), Event::Nil(Token::Err)));
let error_kind = evaluator.try_content().unwrap_err().kind().clone();
assert!(matches!(error_kind, ErrorKind::Syntax { context: _, token: Token::Eof }));
Source

pub fn context(&self) -> &Context

Returns the current parse context, which includes the nesting state and next expected token.

Refer to the Parser::context documentation for more.

Source

pub fn level(&self) -> usize

Returns the current nesting level of the parse.

This is a convenience method that returns the level of the parse context obtainable via the context method.

Source

pub fn into_parts(self) -> (Parser<L>, G)

Returns the contained parser and JSON Pointer group, consuming the self value.

§Examples
use bufjson::{
    lexical::{Token, fixed::FixedAnalyzer},
    pointer::{Evaluator, Event, Group, Pointer}
};

// Create the evaluator.
let parser = FixedAnalyzer::new(&b"{}"[..]).into_parser();
let group = Group::from_pointer(Pointer::from_static(""));
let mut evaluator = Evaluator::new(parser, group, false);

// Read next event from evaluator.
assert!(matches!(evaluator.next(), Event::Enter(Token::ObjBegin, _)));

// Unwrap the parser and group.
let (parser, group) = evaluator.into_parts();

// Continue working with the parser and group...

Auto Trait Implementations§

§

impl<L, G> Freeze for Evaluator<L, G>
where L: Freeze, G: Freeze,

§

impl<L, G> !RefUnwindSafe for Evaluator<L, G>

§

impl<L, G> Send for Evaluator<L, G>
where L: Send, G: Send,

§

impl<L, G> Sync for Evaluator<L, G>
where L: Sync, G: Sync,

§

impl<L, G> Unpin for Evaluator<L, G>
where L: Unpin, G: Unpin,

§

impl<L, G> UnsafeUnpin for Evaluator<L, G>
where L: UnsafeUnpin, G: UnsafeUnpin,

§

impl<L, G> !UnwindSafe for Evaluator<L, G>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> Conv for T

Source§

fn conv<T>(self) -> T
where Self: Into<T>,

Converts self into T using Into<T>. Read more
Source§

impl<T> FmtForward for T

Source§

fn fmt_binary(self) -> FmtBinary<Self>
where Self: Binary,

Causes self to use its Binary implementation when Debug-formatted.
Source§

fn fmt_display(self) -> FmtDisplay<Self>
where Self: Display,

Causes self to use its Display implementation when Debug-formatted.
Source§

fn fmt_lower_exp(self) -> FmtLowerExp<Self>
where Self: LowerExp,

Causes self to use its LowerExp implementation when Debug-formatted.
Source§

fn fmt_lower_hex(self) -> FmtLowerHex<Self>
where Self: LowerHex,

Causes self to use its LowerHex implementation when Debug-formatted.
Source§

fn fmt_octal(self) -> FmtOctal<Self>
where Self: Octal,

Causes self to use its Octal implementation when Debug-formatted.
Source§

fn fmt_pointer(self) -> FmtPointer<Self>
where Self: Pointer,

Causes self to use its Pointer implementation when Debug-formatted.
Source§

fn fmt_upper_exp(self) -> FmtUpperExp<Self>
where Self: UpperExp,

Causes self to use its UpperExp implementation when Debug-formatted.
Source§

fn fmt_upper_hex(self) -> FmtUpperHex<Self>
where Self: UpperHex,

Causes self to use its UpperHex implementation when Debug-formatted.
Source§

fn fmt_list(self) -> FmtList<Self>
where &'a Self: for<'a> IntoIterator,

Formats each item in a sequence. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Pipe for T
where T: ?Sized,

Source§

fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> R
where Self: Sized,

Pipes by value. This is generally the method you want to use. Read more
Source§

fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> R
where R: 'a,

Borrows self and passes that borrow into the pipe function. Read more
Source§

fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> R
where R: 'a,

Mutably borrows self and passes that borrow into the pipe function. Read more
Source§

fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
where Self: Borrow<B>, B: 'a + ?Sized, R: 'a,

Borrows self, then passes self.borrow() into the pipe function. Read more
Source§

fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
where Self: BorrowMut<B>, B: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.borrow_mut() into the pipe function. Read more
Source§

fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
where Self: AsRef<U>, U: 'a + ?Sized, R: 'a,

Borrows self, then passes self.as_ref() into the pipe function.
Source§

fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
where Self: AsMut<U>, U: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.as_mut() into the pipe function.
Source§

fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
where Self: Deref<Target = T>, T: 'a + ?Sized, R: 'a,

Borrows self, then passes self.deref() into the pipe function.
Source§

fn pipe_deref_mut<'a, T, R>( &'a mut self, func: impl FnOnce(&'a mut T) -> R, ) -> R
where Self: DerefMut<Target = T> + Deref, T: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.deref_mut() into the pipe function.
Source§

impl<T> Tap for T

Source§

fn tap(self, func: impl FnOnce(&Self)) -> Self

Immutable access to a value. Read more
Source§

fn tap_mut(self, func: impl FnOnce(&mut Self)) -> Self

Mutable access to a value. Read more
Source§

fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Immutable access to the Borrow<B> of a value. Read more
Source§

fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Mutable access to the BorrowMut<B> of a value. Read more
Source§

fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Immutable access to the AsRef<R> view of a value. Read more
Source§

fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Mutable access to the AsMut<R> view of a value. Read more
Source§

fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Immutable access to the Deref::Target of a value. Read more
Source§

fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Mutable access to the Deref::Target of a value. Read more
Source§

fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self

Calls .tap() only in debug builds, and is erased in release builds.
Source§

fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self

Calls .tap_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Calls .tap_borrow() only in debug builds, and is erased in release builds.
Source§

fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Calls .tap_borrow_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Calls .tap_ref() only in debug builds, and is erased in release builds.
Source§

fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Calls .tap_ref_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Calls .tap_deref() only in debug builds, and is erased in release builds.
Source§

fn tap_deref_mut_dbg<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Calls .tap_deref_mut() only in debug builds, and is erased in release builds.
Source§

impl<T> TryConv for T

Source§

fn try_conv<T>(self) -> Result<T, Self::Error>
where Self: TryInto<T>,

Attempts to convert self into T using TryInto<T>. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.