jsonparser 0.2.1

A Rust crate for efficient parsing and validation of JSON data into strongly typed Rust data structures, enabling data integrity checks.
Documentation
#[derive(Clone)]
pub struct Token {
    pub kind: TokenKind,
    pub text: Option<String>
}

impl std::fmt::Debug for Token {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match &self.text {
            Some(text) => write!(f, "{}", text),
            None => write!(f, "{:?}", self.kind)
        }
    }
}

#[derive(Debug, PartialEq, Clone, Copy)]
pub enum TokenKind {
    QuotedString,
    Number,
    OpenBracket,
    CloseBracket,
    Colon,
    Comma,
    OpenParen,
    CloseParen,
    OpenBrace,
    CloseBrace,
    Keyword
}

pub struct Lexer<'a> {
    chars: std::iter::Peekable<std::str::Chars<'a>>
}

impl<'a> Lexer<'a> {
    pub fn new(input: &'a str) -> Self {
        Self {
            chars: input.chars().peekable()
        }
    }

    pub fn next_token(&mut self) -> Option<Token> {
        while let Some(&c) = self.chars.peek() {
            return if c == '"' {
                self.chars.next();
                let text = self.consume_while(|c| c != '"');

                self.chars.next();
                Some(Token {
                    kind: TokenKind::QuotedString,
                    text: Some(text)
                })
            } else if c == '-' || c.is_numeric() {
                Some(Token {
                    kind: TokenKind::Number,
                    text: Some(self.consume_while(|c| c.is_numeric() || c == '.' || c == '-'))
                })
            } else if c == '(' {
                self.chars.next();
                Some(Token {
                    kind: TokenKind::OpenParen,
                    text: None
                })
            } else if c == ')' {
                self.chars.next();
                Some(Token {
                    kind: TokenKind::CloseParen,
                    text: None
                })
            } else if c == '[' {
                self.chars.next();
                Some(Token {
                    kind: TokenKind::OpenBracket,
                    text: None
                })
            } else if c == ']' {
                self.chars.next();
                Some(Token {
                    kind: TokenKind::CloseBracket,
                    text: None
                })
            } else if c == '{' {
                self.chars.next();
                Some(Token {
                    kind: TokenKind::OpenBrace,
                    text: None
                })
            } else if c == '}' {
                self.chars.next();
                Some(Token {
                    kind: TokenKind::CloseBrace,
                    text: None
                })
            } else if c == ':' {
                self.chars.next();
                Some(Token {
                    kind: TokenKind::Colon,
                    text: None
                })
            } else if c == ',' {
                self.chars.next();
                Some(Token {
                    kind: TokenKind::Comma,
                    text: None
                })
            } else if c.is_whitespace() {
                self.chars.next();
                continue;
            } else {
                return Some(Token {
                    kind: TokenKind::Keyword,
                    text: Some(self.consume_while(|c| c.is_alphabetic()))
                })
            };
        }

        None
    }

    fn consume_while(&mut self, predicate: impl Fn(char) -> bool) -> String {
        let mut result = String::new();

        while let Some(&c) = self.chars.peek() {
            if predicate(c) {
                result.push(c);
                self.chars.next();
            } else {
                break;
            }
        }

        result
    }

    pub fn lex(&mut self) -> Result<Vec<Token>, String> {
        let mut tokens = Vec::new();

        while let Some(token) = self.next_token() {
            tokens.push(token);
        }

        Ok(tokens)
    }
}