mod format;
mod parser;
pub use format::Printf;
use parser::{parse_format_string, FormatElement};
pub use parser::{ConversionSpecifier, ConversionType, NumericParam};
#[derive(Debug, Clone, Copy)]
pub enum PrintfError {
ParseError,
WrongType,
TooManyArgs,
NotEnoughArgs,
Unknown,
}
pub type Result<T> = std::result::Result<T, PrintfError>;
pub fn vsprintf(format: &str, args: &[&dyn Printf]) -> Result<String> {
vsprintfp(&parse_format_string(format)?, args)
}
fn vsprintfp(format: &[FormatElement], args: &[&dyn Printf]) -> Result<String> {
let mut res = String::new();
let mut args = args;
let mut pop_arg = || {
if args.is_empty() {
Err(PrintfError::NotEnoughArgs)
} else {
let a = args[0];
args = &args[1..];
Ok(a)
}
};
for elem in format {
match elem {
FormatElement::Verbatim(s) => {
res.push_str(s);
}
FormatElement::Format(spec) => {
if spec.conversion_type == ConversionType::PercentSign {
res.push('%');
} else {
let mut completed_spec = *spec;
if spec.width == NumericParam::FromArgument {
completed_spec.width = NumericParam::Literal(
pop_arg()?.as_int().ok_or(PrintfError::WrongType)?,
)
}
if spec.precision == NumericParam::FromArgument {
completed_spec.precision = NumericParam::Literal(
pop_arg()?.as_int().ok_or(PrintfError::WrongType)?,
)
}
res.push_str(&pop_arg()?.format(&completed_spec)?);
}
}
}
}
if args.is_empty() {
Ok(res)
} else {
Err(PrintfError::TooManyArgs)
}
}
#[macro_export]
macro_rules! sprintf {
($fmt:expr, $($arg:expr),*) => {
sprintf::vsprintf($fmt, &[$( &($arg) as &dyn sprintf::Printf),* ][..])
};
}