potenv 0.2.0

Rust implementation of the POSIX-compliant dotenv file format specification.
Documentation
pub mod ast;
#[cfg(test)]
mod tests;

use std::{iter::Peekable, path::PathBuf};

use thiserror::Error;

use self::ast::*;
use crate::tokenizer::{
    err::SyntaxError,
    token::{Token, TokenKind},
    Tokenizer,
    TokenizerResult,
};

#[derive(Debug, Error, PartialEq, Eq)]
#[non_exhaustive]
pub enum ParseError {
    #[error("Unexpected end of input")]
    Eof,
    #[error("Unexpected token {0:?}")]
    Unexpected(Token),
    #[error("Unknown expansion operator '{0}'")]
    UnknownOperator(String),
    #[error(transparent)]
    Syntax(#[from] SyntaxError),
}

pub type ParseResult<T> = Result<T, ParseError>;

pub fn parse(input: &str, filename: Option<PathBuf>) -> ParseResult<Vec<Assignment>> {
    let tokenizer = Tokenizer::new(input.chars(), filename);
    Parser::new(tokenizer).parse()
}

macro_rules! match_kind {
    ($($kind:ident)|+) => {
        $( Some(Ok(Token {kind: TokenKind::$kind, ..})) )|+
    };
}

pub struct Parser<I>
where
    I: Iterator<Item = TokenizerResult>,
{
    tokens: Peekable<I>,
}

impl<I> Parser<I>
where
    I: Iterator<Item = TokenizerResult>,
{
    pub fn new(tokenizer: I) -> Self {
        Self {
            tokens: tokenizer.peekable(),
        }
    }

    pub fn parse(&mut self) -> ParseResult<Vec<Assignment>> {
        let mut nodes = Vec::with_capacity(16);
        loop {
            match self.tokens.peek() {
                None => return Err(ParseError::Eof),
                Some(Err(_)) => return self.take_err(),
                match_kind!(Eof) => return Ok(nodes),
                match_kind!(Assign) => nodes.push(self.parse_assignment()?),
                Some(Ok(_)) => {
                    return Err(ParseError::Unexpected(self.take_cur()?));
                }
            };
        }
    }

    fn parse_assignment(&mut self) -> ParseResult<Assignment> {
        let name = self.take_cur()?.value;
        let value = self.parse_assignment_value()?;
        Ok(Assignment::new(name, value))
    }

    fn parse_assignment_value(&mut self) -> ParseResult<Vec<Expression>> {
        let mut nodes = Vec::new();
        loop {
            match self.tokens.peek() {
                None => return Err(ParseError::Eof),
                Some(Err(_)) => return self.take_err(),
                match_kind!(Eof | Assign) => return Ok(nodes),
                match_kind!(Characters) => {
                    nodes.push(Expression::Characters(self.take_cur()?.value));
                }
                match_kind!(SimpleExpansion) => {
                    nodes.push(Expression::Expansion(Expansion::new(
                        self.take_cur()?.value,
                        Operator::IfUnset,
                        vec![],
                    )));
                }
                match_kind!(StartExpansion) => {
                    let name = self.take_cur()?.value;
                    let operator = self.parse_operator()?;
                    let rhs = self.parse_expansion_value()?;
                    nodes.push(Expression::Expansion(Expansion::new(name, operator, rhs)));
                }
                Some(Ok(_)) => {
                    return Err(ParseError::Unexpected(self.take_cur()?));
                }
            };
        }
    }

    fn parse_expansion_value(&mut self) -> ParseResult<Vec<Expression>> {
        let mut nodes = Vec::new();
        loop {
            match self.tokens.peek() {
                None => return Err(ParseError::Eof),
                Some(Err(_)) => return self.take_err(),
                match_kind!(EndExpansion) => {
                    self.tokens.next();
                    return Ok(nodes);
                }
                match_kind!(Characters) => {
                    nodes.push(Expression::Characters(self.take_cur()?.value));
                }
                match_kind!(SimpleExpansion) => {
                    nodes.push(Expression::Expansion(Expansion::new(
                        self.take_cur()?.value,
                        Operator::IfUnset,
                        vec![],
                    )));
                }
                match_kind!(StartExpansion) => {
                    let name = self.take_cur()?.value;
                    let operator = self.parse_operator()?;
                    let rhs = self.parse_expansion_value()?;
                    nodes.push(Expression::Expansion(Expansion::new(name, operator, rhs)));
                }
                Some(Ok(_)) => {
                    return Err(ParseError::Unexpected(self.take_cur()?));
                }
            };
        }
    }

    fn parse_operator(&mut self) -> ParseResult<Operator> {
        let token = self.expect(TokenKind::ExpansionOperator)?;
        match token.value.as_str() {
            "-" => Ok(Operator::IfUnset),
            ":-" => Ok(Operator::IfUnsetOrNull),
            "=" => Ok(Operator::AssignIfUnset),
            ":=" => Ok(Operator::AssignIfUnsetOrNull),
            "+" => Ok(Operator::IfSet),
            ":+" => Ok(Operator::IfSetAndNotNull),
            "?" => Ok(Operator::ErrorIfUnset),
            ":?" => Ok(Operator::ErrorIfUnsetOrNull),
            op => Err(ParseError::UnknownOperator(op.to_owned())),
        }
    }

    fn expect(&mut self, kind: TokenKind) -> ParseResult<Token> {
        match self.tokens.next() {
            None => Err(ParseError::Eof),
            Some(Ok(token)) if token.kind == kind => Ok(token),
            Some(Ok(token)) => Err(ParseError::Unexpected(token)),
            Some(Err(e)) => Err(ParseError::Syntax(e)),
        }
    }

    fn take_cur(&mut self) -> ParseResult<Token> {
        Ok(self.tokens.next().unwrap()?)
    }

    fn take_err<T>(&mut self) -> ParseResult<T> {
        Err(ParseError::Syntax(self.tokens.next().unwrap().unwrap_err()))
    }
}