blosc2 0.2.2

Safe Rust bindings for blosc2 - a fast, compressed, persistent binary data store library
Documentation
pub(crate) enum Node {
    Literal(String),
    Str(String),
    List(Vec<Node>),
    Tuple(Vec<Node>),
    Dict(Vec<(Node, Node)>),
}

pub(crate) fn parse_ast(s: &str) -> Result<Node, ParseError> {
    parse_ast_impl(&mut Chars(s.chars())).map_err(|e| ParseError {
        msg: e.msg,
        pos: s.len() - e.pos_from_end,
    })
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct ParseError {
    pub msg: &'static str,
    pub pos: usize,
}
impl std::fmt::Display for ParseError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "AST parse error at position {}: {}", self.pos, self.msg)
    }
}

struct ParseErrorInternal {
    pub msg: &'static str,
    pub pos_from_end: usize,
}

struct Chars<'a>(std::str::Chars<'a>);
impl Chars<'_> {
    fn peek(&mut self) -> Option<char> {
        self.0.clone().next()
    }
    fn next(&mut self) -> Option<char> {
        self.0.next()
    }
    fn skip_whitespace(&mut self) {
        while let Some(c) = self.peek() {
            if c.is_whitespace() {
                self.next();
            } else {
                break;
            }
        }
    }
    fn err(&self, msg: &'static str) -> ParseErrorInternal {
        ParseErrorInternal {
            msg,
            pos_from_end: self.0.as_str().len(),
        }
    }
}

fn parse_ast_impl(s: &mut Chars) -> Result<Node, ParseErrorInternal> {
    let first_char = s.next().ok_or(s.err("Unexpected end of input"))?;
    match first_char {
        '[' | '(' => {
            let expected_end_char = if first_char == '[' { ']' } else { ')' };
            let mut children = Vec::new();

            s.skip_whitespace();
            if s.peek() == Some(expected_end_char) {
                s.next();
            } else {
                loop {
                    children.push(parse_ast_impl(s)?);
                    s.skip_whitespace();

                    match s.next().ok_or(s.err("tuple/list was never closed"))? {
                        ',' => {
                            s.skip_whitespace();
                            if s.peek() == Some(expected_end_char) {
                                s.next();
                                break;
                            }
                        }
                        c if c == expected_end_char => break,
                        _ => return Err(s.err("Expected ',' or end of list/tuple")),
                    }
                }
            }

            Ok(if expected_end_char == ')' {
                Node::Tuple(children)
            } else {
                Node::List(children)
            })
        }

        '{' => {
            let mut children = Vec::new();
            s.skip_whitespace();
            if s.peek() == Some('}') {
                s.next();
            } else {
                loop {
                    let key = parse_ast_impl(s)?;
                    s.skip_whitespace();
                    if s.next() != Some(':') {
                        return Err(s.err("Expected ':'"));
                    }
                    s.skip_whitespace();
                    let value = parse_ast_impl(s)?;
                    children.push((key, value));
                    s.skip_whitespace();

                    match s.next().ok_or(s.err("'{' was never closed"))? {
                        ',' => {
                            s.skip_whitespace();
                            if s.peek() == Some('}') {
                                s.next();
                                break;
                            }
                        }
                        '}' => break,
                        _ => return Err(s.err("Expected ',' or '}'")),
                    }
                }
            }
            Ok(Node::Dict(children))
        }

        '"' | '\'' => {
            let end_char = if first_char == '"' { '"' } else { '\'' };
            let mut value = String::new();
            let mut escape = false;
            loop {
                let char = s.next().ok_or(s.err("unterminated string literal"))?;
                if escape {
                    value.push(char);
                    escape = false;
                } else if char == '\\' {
                    escape = true;
                } else if char == end_char {
                    break;
                } else {
                    value.push(char);
                }
            }
            Ok(Node::Str(value))
        }

        'a'..='z' | 'A'..='Z' | '0'..='9' | '_' => {
            let mut value = String::from(first_char);
            loop {
                let next_char = s.peek();
                if next_char.is_none()
                    || !matches!(next_char.unwrap(), 'a'..='z' | 'A'..='Z' | '0'..='9' | '_')
                {
                    break;
                }
                value.push(next_char.unwrap());
                s.next();
            }
            Ok(Node::Literal(value))
        }

        _ => Err(s.err("Unexpected token")),
    }
}