empress 1.6.0

A D-Bus MPRIS daemon for controlling media players.
use nom::error::Error as NomError;

mod ast;
mod ffi;
mod functions;
mod interp;
mod lexer;

mod __parser {
    #![allow(clippy::all, warnings)]

    lalrpop_util::lalrpop_mod!(pub parser, "/format/parser.rs");
}

pub(self) use __parser::parser;

type ParseError<T> = lalrpop_util::ParseError<lexer::Pos, T, &'static str>;

#[derive(Debug, thiserror::Error)]
pub enum Error {
    #[error("Failed to parse format string")]
    Lex(#[from] NomError<String>),
    #[error("Failed to parse format string")]
    Parse(#[from] ParseError<String>),
    #[error("Failed to load context into the interpreter")]
    Values(#[from] anyhow::Error),
    #[error("Failed to evaluate format string")]
    Interpret(#[from] interp::Error),
}

impl<'a> From<NomError<&'a str>> for Error {
    fn from(NomError { input, code }: NomError<&str>) -> Self {
        Self::Lex(NomError {
            input: input.into(),
            code,
        })
    }
}

impl<'a> From<ParseError<lexer::Token<'a>>> for Error {
    fn from(e: ParseError<lexer::Token>) -> Self {
        Self::Parse(e.map_token(|t| format!("{:?}", t)))
    }
}

pub fn eval(fmt: impl AsRef<str>, values: impl serde::Serialize) -> Result<String, Error> {
    use interp::StreamAll;

    let toks = lexer::scan(fmt.as_ref());
    log::trace!("{:?}", toks);
    let toks = toks?;

    let ast = parser::FormatParser::new().parse(toks);
    log::trace!("{:?}", ast);
    let ast = ast?;

    let values = match serde_json::to_value(values) {
        Ok(serde_json::Value::Object(m)) => Ok(m),
        Ok(v) => Err(Error::Values(anyhow::anyhow!(
            "Value provided was not a JSON map ({:?})",
            v
        ))),
        Err(e) => Err(Error::Values(e.into())),
    }?;

    let mut out = String::new();
    ast.stream_all(
        &interp::Context {
            values,
            functions: functions::all(),
        },
        &mut out,
    )?;

    Ok(out)
}