1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
//! Libc s(n)printf clone written in Rust, so you can use printf-style
//! formatting without a libc (e.g. in WebAssembly).
//!
//! **Note:** *You're probably better off using standard Rust string formatting
//! instead of thie crate unless you specificaly need printf compatibility.*
//!
//! It follows the standard C semantics, except:
//!
//! * Locale-aware UNIX extensions (`'` and GNU’s `I`) are not supported.
//! * `%a`/`%A` (hexadecimal floating point) are currently not implemented.
//! * Length modifiers (`h`, `l`, etc.) are checked, but ignored. The passed
//! type is used instead.
//!
//! Usage example:
//!
//! use sprintf::sprintf;
//! let s = sprintf!("%d + %d = %d\n", 3, 9, 3+9).unwrap();
//! assert_eq!(s, "3 + 9 = 12\n");
//!
//! The types of the arguments are checked at runtime.
//!
mod format;
mod parser;
pub use format::Printf;
use parser::{parse_format_string, FormatElement};
pub use parser::{ConversionSpecifier, ConversionType, NumericParam};
/// Error type
#[derive(Debug, Clone, Copy)]
pub enum PrintfError {
/// Error parsing the format string
ParseError,
/// Incorrect type passed as an argument
WrongType,
/// Too many arguments passed
TooManyArgs,
/// Too few arguments passed
NotEnoughArgs,
/// Other error (should never happen)
Unknown,
}
pub type Result<T> = std::result::Result<T, PrintfError>;
/// Format a string. (Roughly equivalent to `vsnprintf` or `vasprintf` in C)
///
/// Takes a printf-style format string `format` and a slice of dynamically
/// typed arguments, `args`.
///
/// use sprintf::{vsprintf, Printf};
/// let n = 16;
/// let args: Vec<&dyn Printf> = vec![&n];
/// let s = vsprintf("%#06x", &args).unwrap();
/// assert_eq!(s, "0x0010");
///
/// See also: [sprintf]
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)
}
}
/// Format a string. (Roughly equivalent to `snprintf` or `asprintf` in C)
///
/// Takes a printf-style format string `format` and a variable number of
/// additional arguments.
///
/// use sprintf::sprintf;
/// let s = sprintf!("%s = %*d", "forty-two", 4, 42).unwrap();
/// assert_eq!(s, "forty-two = 42");
///
/// Wrapper around [vsprintf].
#[macro_export]
macro_rules! sprintf {
($fmt:expr, $($arg:expr),*) => {
sprintf::vsprintf($fmt, &[$( &($arg) as &dyn sprintf::Printf),* ][..])
};
}