glue 0.8.7

Glue is a parser combinator framework for parsing text based formats, it is easy to use and relatively fast too.
Documentation
//! Parser combinators for matching literals.

use crate::types::*;

#[inline]
/// Match an empty portion of the input.
///
/// ```
/// # use glue::prelude::*;
/// // Match "foo bar" or "foobar":
/// let mut parser = find_any((
///     find_all((is("foo"), is(" "), is("bar"))),
///     find_all((is("foo"), empty(), is("bar"))),
/// ));
///
/// assert_eq!(
///     parser.parse("foobar"),
///     Ok((
///         ParserContext {
///             input: "foobar",
///             bounds: 0..6
///         },
///         ("foo", "", "bar"),
///     ))
/// );
/// assert_eq!(
///     parser.parse("foo bar"),
///     Ok((
///         ParserContext {
///             input: "foo bar",
///             bounds: 0..7
///         },
///         ("foo", " ", "bar"),
///     ))
/// );
/// ```
pub fn empty<'a>() -> impl Parser<'a, &'a str> {
    move |ctx: ParserContext<'a>| Ok(ctx.select_empty().with_str())
}

#[inline]
/// Match the end of input.
///
/// ```
/// # use glue::prelude::*;
/// // Match the "foo" in "foo" or "foobar":
/// let mut parser = find_any((
///     find_all((is("foo"), eoi())),
///     find_all((is("foo"), empty())),
/// ));
///
/// assert_eq!(
///     parser.parse("foo"),
///     Ok((
///         ParserContext {
///             input: "foo",
///             bounds: 0..3,
///         },
///         ("foo", ""),
///     ))
/// );
/// assert_eq!(
///     parser.parse("foobar"),
///     Ok((
///         ParserContext {
///             input: "foobar",
///             bounds: 0..3,
///         },
///         ("foo", "")
///     ))
/// );
/// ```
pub fn eoi<'a>() -> impl Parser<'a, &'a str> {
    move |ctx: ParserContext<'a>| {
        if ctx.end_of_input() {
            Ok(ctx.select_empty().with_str())
        } else {
            let error = SourceError::Range(ctx.bounds.clone());

            Err(ctx.with_data(error))
        }
    }
}

#[inline]
/// Match using a string or character literal, callback or implementation of [`MatchParser`](crate::testers::MatchParser).
///
/// Can be used to match from a list of characters:
///
/// ```
/// # use glue::prelude::*;
/// assert!(is(one_of("abcdef")).test("foobar"));
/// // Or:
/// assert!(is("abcdef".chars()).test("foobar"));
/// ```
///
/// A function that takes a character as input:
///
/// ```
/// # use glue::prelude::*;
/// assert!(is(alphabetic).test("foobar"));
/// ```
///
/// A character literal:
///
/// ```
/// # use glue::prelude::*;
/// assert!(is('f').test("foobar"));
/// ```
///
/// A string literal:
///
/// ```
/// # use glue::prelude::*;
/// assert_eq!(
///     is("foobar").parse("foobar"),
///     Ok((
///         ParserContext {
///             input: "foobar",
///             bounds: 0..6,
///         },
///         "foobar"
///     ))
/// );
/// ```
pub fn is<'a, Par>(parser: Par) -> impl Parser<'a, &'a str>
where
    Par: MatchParser,
{
    move |ctx| parser.is(ctx)
}

#[inline]
/// Match negatively using a string or character literal, callback or implementation of [`MatchParser`](crate::testers::MatchParser).
///
/// The same as [`is`] but with the test negated.
///
/// Can be used to match from a list of characters:
///
/// ```
/// # use glue::prelude::*;
/// assert!(isnt(one_of("xyz")).test("foobar"));
/// // Or:
/// assert!(isnt("xyz".chars()).test("foobar"));
/// ```
///
/// A function that takes a character as input:
///
/// ```
/// # use glue::prelude::*;
/// assert!(isnt(numeric).test("foobar"));
/// ```
///
/// A character literal:
///
/// ```
/// # use glue::prelude::*;
/// assert!(isnt('z').test("foobar"));
/// ```
///
/// A string literal:
///
/// ```
/// # use glue::prelude::*;
/// assert!(isnt("boofar").test("foobar"));
/// ```
pub fn isnt<'a, Par>(parser: Par) -> impl Parser<'a, &'a str>
where
    Par: MatchParser,
{
    move |ctx| parser.isnt(ctx)
}

#[inline]
/// Match the next character against one of these characters.
///
/// ```
/// # use glue::prelude::*;
/// assert!(is(one_of("abcdef")).test("foobar"));
/// ```
pub fn one_of(characters: &'static str) -> impl MatchParser {
    let characters = characters.chars();

    move |character: char| characters.clone().any(|x| x == character)
}

#[inline]
/// Match the start of input.
///
/// ```
/// # use glue::prelude::*;
/// assert_eq!(soi().test("foobar"), true);
/// assert_eq!(find_all((is("foobar"), soi())).test("foobar"), false);
/// ```
pub fn soi<'a>() -> impl Parser<'a, &'a str> {
    move |ctx: ParserContext<'a>| {
        if ctx.start_of_input() {
            Ok(ctx.select_empty().with_str())
        } else {
            let error = SourceError::Range(ctx.bounds.clone());

            Err(ctx.with_data(error))
        }
    }
}