parson 1.1.0

A crate for parsing JSON into Rust types
Documentation
use crate::{
    json_err,
    token::{Token, TokenType},
    JSONError,
};

pub struct Lexer {
    pub json: String,
}

impl Lexer {
    pub fn new(json: String) -> Self {
        Lexer { json }
    }

    pub fn get_tokens(&self) -> Result<Vec<Token>, JSONError> {
        let mut tokens = vec![];
        let mut json = self.json.clone();
        let mut line = 1;
        let mut column = 1;

        while json.len() > 0 {
            if let Some(result) = self.lex_string(&mut json, line, &mut column) {
                if let Ok(string_token) = result {
                    tokens.push(string_token);
                    continue;
                } else {
                    json_err!(result.unwrap_err());
                }
            }

            if let Some(result) = self.lex_number(&mut json, line, &mut column) {
                if let Ok(number_token) = result {
                    tokens.push(number_token);
                    continue;
                } else {
                    json_err!(result.unwrap_err());
                }
            }

            if let Some(result) = self.lex_boolean(&mut json, line, &mut column) {
                if let Ok(boolean_token) = result {
                    tokens.push(boolean_token);
                    continue;
                } else {
                    json_err!(result.unwrap_err());
                }
            }

            if let Some(result) = self.lex_null(&mut json, line, &mut column) {
                if let Ok(null_token) = result {
                    tokens.push(null_token);
                    continue;
                } else {
                    json_err!(result.unwrap_err());
                }
            }

            let first_char = json[..1].to_string();
            match &first_char[..] {
                " " | "\t" => {
                    json = json[1..].to_string();
                    column += 1;
                }
                "\r" => {
                    json = json[1..].to_string();
                }
                "\n" => {
                    json = json[1..].to_string();
                    line += 1;
                    column = 1;
                }
                "{" => {
                    json = json[1..].to_string();
                    tokens.push(Token::new(TokenType::OpenCurlyBracket, line, column));
                    column += 1;
                }
                "}" => {
                    json = json[1..].to_string();
                    tokens.push(Token::new(TokenType::CloseCurlyBracket, line, column));
                    column += 1;
                }
                "[" => {
                    json = json[1..].to_string();
                    tokens.push(Token::new(TokenType::OpenSquareBracket, line, column));
                    column += 1;
                }
                "]" => {
                    json = json[1..].to_string();
                    tokens.push(Token::new(TokenType::CloseSquareBracket, line, column));
                    column += 1;
                }
                "," => {
                    json = json[1..].to_string();
                    tokens.push(Token::new(TokenType::Comma, line, column));
                    column += 1;
                }
                ":" => {
                    json = json[1..].to_string();
                    tokens.push(Token::new(TokenType::Colon, line, column));
                    column += 1;
                }
                "\"" => {
                    json_err!("Unexpected end of file <{}>", json; line, column);
                }
                char => {
                    json_err!("Unexpected character <{}>", char; line, column);
                }
            }
        }

        Ok(tokens)
    }

    fn lex_string(
        &self,
        string: &mut String,
        line: usize,
        column: &mut usize,
    ) -> Option<Result<Token, JSONError>> {
        let mut data = String::new();

        if &string[..1] != "\"" {
            return None;
        }

        let mut escape = false;

        *column += 1;
        for char in string[1..].chars() {
            if escape {
                if ['r', 'n', 't', '\\', '\"'].contains(&char) {
                    *column += 1;
                    data.push(char);
                    escape = false;
                } else {
                    json_err!(
                        Some; "Invalid escape of character <{}>", char.encode_utf8(&mut [0, 0]); line, *column
                    )
                }
            } else {
                if char == '\\' {
                    *column += 1;
                    data.push(char);
                    escape = true;
                } else if char == '"' {
                    let length = data.len();
                    *column += 1;
                    *string = string[length + 2..].to_string();
                    return Some(Ok(Token::new(
                        TokenType::String(data),
                        line,
                        *column - length,
                    )));
                } else {
                    *column += 1;
                    data.push(char);
                }
            }
        }

        json_err!(Some; "Unexpected end of string"; line, *column)
    }

    fn lex_number(
        &self,
        string: &mut String,
        line: usize,
        column: &mut usize,
    ) -> Option<Result<Token, JSONError>> {
        let mut data = String::new();

        for char in string.chars() {
            let last_char = data.chars().last();
            if char.is_digit(10) {
                *column += 1;
                data.push(char);
                continue;
            }

            if char == '-' {
                if data == "" || last_char.unwrap() == 'e' || last_char.unwrap() == 'E' {
                    *column += 1;
                    data.push(char);
                    continue;
                }
                json_err!(
                    Some;
                    "Invalid character in number <->";
                    line,
                    *column
                );
            }

            if char == 'e' || char == 'E' {
                if data != ""
                    && last_char.unwrap().is_digit(10)
                    && !data.contains('e')
                    && !data.contains('E')
                {
                    *column += 1;
                    data.push(char);
                    continue;
                }
                json_err!(
                    Some;
                    "Invalid character in number <e>";
                    line,
                    *column
                );
            }

            if char == '.' {
                if data != ""
                    && last_char.unwrap().is_digit(10)
                    && !data.contains("e")
                    && !data.contains('E')
                {
                    *column += 1;
                    data.push(char);
                    continue;
                }
                json_err!(
                    Some;
                    "Invalid character in number <.>";
                    line,
                    *column
                );
            }

            break;
        }

        if data.len() == 0 {
            return None;
        }

        *string = string[data.len()..].to_string();
        Some(Ok(Token::new(
            TokenType::Number(data.parse().unwrap()),
            line,
            *column - data.len(),
        )))
    }

    fn lex_boolean(
        &self,
        string: &mut String,
        line: usize,
        column: &mut usize,
    ) -> Option<Result<Token, JSONError>> {
        if string.len() > 4 && string.starts_with("true") {
            *column += 4;
            *string = string[4..].to_string();
            Some(Ok(Token::new(TokenType::Boolean(true), line, *column)))
        } else if string.len() > 5 && string.starts_with("false") {
            *column += 5;
            *string = string[5..].to_string();
            Some(Ok(Token::new(TokenType::Boolean(false), line, *column)))
        } else {
            None
        }
    }

    fn lex_null(
        &self,
        string: &mut String,
        line: usize,
        column: &mut usize,
    ) -> Option<Result<Token, JSONError>> {
        if string.len() > 4 && string.starts_with("null") {
            *column += 4;
            *string = string[4..].to_string();
            Some(Ok(Token::new(TokenType::Null, line, *column)))
        } else {
            None
        }
    }
}