saffron-data 0.1.1

Custom JSON parser for Saffron HTTP client
Documentation
use std::collections::HashMap;

use crate::error::ParseError;
use crate::tokenizer::TokenKind;
use crate::{parse::Parse, token_stream::TokenStream, tokenizer::Tokenizer};

#[derive(Debug, Clone, PartialEq)]
pub enum JsonElement {
    Number(f64),
    String(String),
    Boolean(bool),
    Array(Vec<JsonElement>),
    Object(HashMap<String, JsonElement>),
    Null,
}

pub struct Json {
    pub root: JsonElement,
}

impl Json {
    fn parse_tokens(mut tokens: TokenStream) -> Result<JsonElement, ParseError> {
        fn parse_value(tokens: &mut TokenStream) -> Result<JsonElement, ParseError> {
            use TokenKind::*;

            let tk = tokens.current();

            match tk.kind {
                String => {
                    let t = tokens.advance();
                    Ok(JsonElement::String(t.lexeme))
                }
                Number => {
                    let t = tokens.advance();
                    let n = t
                        .lexeme
                        .parse::<f64>()
                        .map_err(|_e| ParseError::new(format!("Invalid number '{}'", t.lexeme)))?;
                    Ok(JsonElement::Number(n))
                }
                Boolean => {
                    let t = tokens.advance();
                    let b = match t.lexeme.as_str() {
                        "true" => true,
                        "false" => false,
                        other => {
                            return Err(ParseError::new(format!(
                                "Invalid boolean literal '{}'",
                                other
                            )));
                        }
                    };
                    Ok(JsonElement::Boolean(b))
                }
                Null => {
                    tokens.advance();
                    Ok(JsonElement::Null)
                }
                LeftBrace => parse_object(tokens),
                LeftBracket => parse_array(tokens),
                _ => Err(ParseError::new(format!("Unexpected token: {:?}", tk.kind))),
            }
        }

        fn parse_object(tokens: &mut TokenStream) -> Result<JsonElement, ParseError> {
            use TokenKind::*;

            let start = tokens.current();
            if start.kind != LeftBrace {
                return Err(ParseError::new("Expected '{' at start of object"));
            }
            tokens.advance();

            let mut map = HashMap::new();

            if tokens.current().kind == RightBrace {
                tokens.advance();
                return Ok(JsonElement::Object(map));
            }

            loop {
                let key_token = tokens.current();
                if key_token.kind != String {
                    return Err(ParseError::new(format!(
                        "Expected string key in object, found {:?}",
                        key_token.kind
                    )));
                }
                let key = tokens.advance().lexeme;

                if tokens.current().kind != Colon {
                    return Err(ParseError::new("Expected ':' after object key"));
                }
                tokens.advance();

                let value = parse_value(tokens)?;
                map.insert(key, value);

                match tokens.current().kind {
                    Comma => {
                        tokens.advance();
                        continue;
                    }
                    RightBrace => {
                        tokens.advance();
                        break;
                    }
                    other => {
                        return Err(ParseError::new(format!(
                            "Expected ',' or '}}' in object, found {:?}",
                            other
                        )));
                    }
                }
            }

            Ok(JsonElement::Object(map))
        }

        fn parse_array(tokens: &mut TokenStream) -> Result<JsonElement, ParseError> {
            use TokenKind::*;

            let start = tokens.current();
            if start.kind != LeftBracket {
                return Err(ParseError::new("Expected '[' at start of array"));
            }
            tokens.advance();

            let mut items = Vec::new();

            if tokens.current().kind == RightBracket {
                tokens.advance();
                return Ok(JsonElement::Array(items));
            }

            loop {
                let value = parse_value(tokens)?;
                items.push(value);

                match tokens.current().kind {
                    Comma => {
                        tokens.advance();
                        continue;
                    }
                    RightBracket => {
                        tokens.advance();
                        break;
                    }
                    other => {
                        return Err(ParseError::new(format!(
                            "Expected ',' or ']' in array, found {:?}",
                            other
                        )));
                    }
                }
            }

            Ok(JsonElement::Array(items))
        }

        let value = parse_value(&mut tokens)?;
        Ok(value)
    }

    fn _parse(source: impl Into<String>) -> Result<Self, ParseError> {
        let mut tokenizer = Tokenizer::new(source.into());
        let tokens = tokenizer.scan_tokens()?;
        let root = Self::parse_tokens(tokens)?;

        Ok(Json { root })
    }
}

impl Parse for Json {
    fn parse(source: impl Into<String>) -> Result<Self, ParseError>
    where
        Self: Sized,
    {
        Self::_parse(source)
    }
}