infoterm 0.1.1

ncurses-compatible terminfo parsing library
Documentation
//! Parameterized string expansion.
//!
//! See [`expand!`][crate::expand!] for more information.

mod exec;
mod op;
mod parser;
#[cfg(test)]
mod tests;
mod value;

pub use value::Value;

/// Expands a parameterized capability string using the provided arguments.
///
/// The list of supported operators can be found [here][tparm-string].
/// The macro is a convenience wrapper for the actual expansion function to convert the arguments
/// to the `Value` wrapper type.
///
/// Permitted types are `i32`, `char`, `bool`, `&[u8]`, `&str`.
///
/// Missing arguments will be substituted with 0.
///
/// [tparm-string]: https://man.archlinux.org/man/core/ncurses/terminfo.5.en#Parameterized_Strings
#[macro_export]
macro_rules! expand {
    ($e:expr) => { $crate::expand::expand($e, &[]) };
    ($e:expr, $($rest:tt)*) => { $crate::expand::expand($e, $crate::expand_args!($($rest)*)) };
}

#[doc(hidden)]
#[macro_export]
macro_rules! expand_args {
    ($($e:expr),+ $(,)?) => { &[$($crate::expand::Value::from($e)),+] };
}

/// The error type for string expansion.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum Error {
    /// Parsing the capability string failed.
    ParseFailed,
    /// Division by zero occured.
    DivisionByZero,
    /// An operand had the wrong type for an operation.
    InvalidOperandType,
    /// An operation tried to pop from an empty stack.
    StackUnderflow,
    /// Value conversion caused overflow.
    ValueOutOfRange,
    /// A conditional section is invalid.
    BrokenConditional,
}

#[doc(hidden)]
pub fn expand(str: impl AsRef<[u8]>, args: &[Value]) -> Result<Vec<u8>, Error> {
    expand_inner(str.as_ref(), args)
}

// Inner function to avoid monomorphization bloat
fn expand_inner(str: &[u8], args: &[Value]) -> Result<Vec<u8>, Error> {
    let ops = parser::parse(str)?;
    let mut state = exec::State::new(args);
    let mut out = Vec::with_capacity(str.len());

    for op in ops {
        state.execute(op, &mut out)?;
    }

    state.execute(op::Op::End, &mut out)?;

    out.shrink_to_fit();

    Ok(out)
}