trivet 3.1.0

The trivet Parser Library
Documentation
use std::collections::HashMap;

/// Define the JSON structures.
#[derive(Debug)]
pub enum JSON {
    Object(HashMap<String, JSON>),
    Array(Vec<JSON>),
    String(String),
    Number(f64),
    Boolean(bool),
    Null,
}

/// Parse a JSON value.  On entry the parser is assumed to be pointing at the first
/// character of the value.  Trailing whitespace is consumed.
pub fn parse_value_ws(parser: &mut trivet::Parser) -> trivet::errors::ParseResult<JSON> {
    match parser.peek() {
        // Check for an object.
        '{' => parse_object_ws(parser),

        // Check for an array.
        '[' => parse_array_ws(parser),

        // Check for a string.
        '"' => Ok(JSON::String(parser.parse_string_match_delimiter_ws()?)),

        // Check for a number.
        ch if ch == '-' || ch.is_ascii_digit() => Ok(JSON::Number(parser.parse_f64_decimal_ws()?)),

        // Check for Boolean true.
        't' if parser.peek_and_consume_str_ws("true") => Ok(JSON::Boolean(true)),

        // Check for Boolean false.
        'f' if parser.peek_and_consume_str_ws("false") => Ok(JSON::Boolean(false)),

        // Check for null.
        'n' if parser.peek_and_consume_str_ws("null") => Ok(JSON::Null),

        // Error.
        ch => Err(trivet::errors::unexpected_text_error(
            parser.loc(),
            "the start of a JSON value",
            &ch.to_string(),
        )),
    }
}

/// Parse a JSON object.  On entry the parser is assumed to be pointing to the first
/// character of the object (a curly brace `{`).  Trailing whitespace is consumed.
fn parse_object_ws(parser: &mut trivet::Parser) -> trivet::errors::ParseResult<JSON> {
    parser.consume(); // Consume opening brace.
    parser.consume_ws();
    let mut first = true;
    let mut map = HashMap::new();
    while !parser.peek_and_consume_ws('}') {
        // If not the first element, then we expect to have a comma here.  JSON requires
        // a comma between members of an object.
        if first {
            first = false;
        } else if !parser.peek_and_consume_ws(',') {
            return Err(trivet::errors::syntax_error(
                parser.loc(),
                "There must be a comma (,) between members of an object.",
            ));
        }

        // Look for the quotation mark for a string.
        if parser.peek() != '"' {
            return Err(trivet::errors::syntax_error(
                parser.loc(),
                "Names in a JSON object must be quoted strings.",
            ));
        }
        let name = parser.parse_string_match_delimiter_ws()?;

        // Look for the colon.
        if !parser.peek_and_consume_ws(':') {
            return Err(trivet::errors::syntax_error(
                parser.loc(),
                "Names in a JSON object must be followed by a colon (:).",
            ));
        }

        // Get the value.
        let value = parse_value_ws(parser)?;

        // Add the name, value pair to the map.
        map.insert(name, value);
    }
    Ok(JSON::Object(map))
}

/// Parse a JSON array.  On entry the parser is assumed to be pointing to the first
/// character of the array (a square bracket `[`).  Trailing whitespace is consumed.
fn parse_array_ws(parser: &mut trivet::Parser) -> trivet::errors::ParseResult<JSON> {
    parser.consume(); // Consume opening bracket.
    parser.consume_ws_only();
    let mut first = true;
    let mut array = vec![];
    while !parser.peek_and_consume_ws(']') {
        // If not the first element, then we expect to have a comma here.  JSON requires
        // a comma between members of an array.
        if first {
            first = false;
        } else if !parser.peek_and_consume_ws(',') {
            return Err(trivet::errors::syntax_error(
                parser.loc(),
                "There must be a comma (,) between members of an array.",
            ));
        }

        // Get the value.
        let value = parse_value_ws(parser)?;

        // Add the value to the array.
        array.push(value);
    }
    Ok(JSON::Array(array))
}

/// Read JSON from standard input and then write JSON to standard output.
pub fn main() {
    let mut parser = trivet::parse_from_stdin();
    parser.parse_comments = false;
    let numpar = parser.borrow_number_parser();
    numpar.settings.permit_binary = false;
    numpar.settings.permit_hexadecimal = false;
    numpar.settings.permit_octal = false;
    numpar.settings.permit_underscores = false;
    numpar.settings.decimal_only_floats = true;
    numpar.settings.permit_plus = false;
    numpar.settings.permit_leading_zero = false;
    numpar.settings.permit_empty_whole = false;
    numpar.settings.permit_empty_fraction = false;
    let strpar = parser.borrow_string_parser();
    strpar.set(trivet::strings::StringStandard::JSON);
    let _ = parser
        .borrow_core()
        .replace_whitespace_test(Box::new(|ch| [' ', '\n', '\r', '\t'].contains(&ch)));
    parser.consume_ws();
    let result = parse_value_ws(&mut parser);
    match result {
        Err(error) => {
            println!("ERROR: {}", error);
            std::process::exit(1);
        }
        Ok(json) => {
            // If there is any trailing stuff that is not whitespace, then this is not a valid
            // JSON file.
            if parser.is_at_eof() {
                // Print the JSON value.
                println!("{:?}", json);
                std::process::exit(0)
            } else {
                println!("Found unexpected trailing characters after JSON value.");
                std::process::exit(1);
            }
        }
    }
}