luau_printf/lib.rs
1/** Musl snprintf-compatible byte formatting for Luau, forked from `fish-printf`. */
2pub use bstr::{BStr, BString};
3
4mod arg;
5pub use arg::{Arg, ToArg};
6
7mod fmt_fp;
8mod printf_impl;
9pub use printf_impl::{Error, printf_locale_to_slice, sprintf_locale};
10pub mod locale;
11
12#[cfg(test)]
13mod tests;
14
15/// A macro to format a byte string with C-locale formatting rules.
16///
17/// # Examples
18///
19/// ```
20/// use luau_printf::sprintf;
21///
22/// // Create a `BString` from a format string.
23/// let s = sprintf!("%0.5g", 123456.0);
24/// assert_eq!(s, "1.2346e+05");
25///
26/// // Write to an existing byte sink.
27/// let mut s = Vec::new();
28/// sprintf!(=> &mut s, "%0.5g", 123456.0);
29/// assert_eq!(s.as_slice(), b"1.2346e+05");
30/// ```
31#[macro_export]
32macro_rules! sprintf {
33 // Write to a newly allocated BString, and return it.
34 // This panics if the format string or arguments are invalid.
35 (
36 $fmt:expr // Format string, as bytes.
37 $(, $($arg:expr),*)? // arguments
38 ) => {
39 {
40 let mut target = ::std::vec::Vec::new();
41 $crate::sprintf!(=> &mut target, $fmt $(, $($arg),*)?);
42 $crate::BString::from(target)
43 }
44 };
45
46 // Variant which writes to a target.
47 // The target should implement std::io::Write.
48 (
49 => $target:expr, // target string
50 $fmt:expr // format string
51 $(, $($arg:expr),*)? // arguments
52 ) => {
53 {
54 // May be no args!
55 #[allow(unused_imports)]
56 use $crate::ToArg as _;
57 let fmt = $crate::BStr::new(::std::convert::AsRef::<[u8]>::as_ref(&$fmt));
58 $crate::printf_c_locale(
59 $target,
60 fmt,
61 &mut [$( $($arg.to_arg()),* )?],
62 ).unwrap()
63 }
64 };
65}
66
67/// Formats a byte string using the provided format specifiers and arguments, using the C locale.
68///
69/// # Parameters
70/// - `f`: The receiver of formatted output.
71/// - `fmt`: The format string being parsed.
72/// - `args`: Iterator over the arguments to format.
73///
74/// # Returns
75/// A `Result` which is `Ok` containing the number of bytes written on success, or an `Error`.
76///
77/// # Example
78///
79/// ```
80/// use luau_printf::{printf_c_locale, ToArg};
81///
82/// let mut output = Vec::new();
83/// let fmt = luau_printf::BStr::new("%0.5g");
84/// let mut args = [123456.0_f64.to_arg()];
85///
86/// let result = printf_c_locale(&mut output, fmt, &mut args);
87///
88/// assert_eq!(result, Ok(10));
89/// assert_eq!(output.as_slice(), b"1.2346e+05");
90/// ```
91pub fn printf_c_locale<W: std::io::Write + ?Sized>(
92 f: &mut W,
93 fmt: &BStr,
94 args: &mut [Arg],
95) -> Result<usize, Error> {
96 sprintf_locale(f, fmt, &locale::C_LOCALE, args)
97}
98
99/// Formats a byte string into a fixed-size byte slice using C-locale formatting rules.
100///
101/// The returned count is the number of bytes that would have been written
102/// without truncation. Bytes that do not fit in `buffer` are discarded.
103pub fn printf_c_locale_to_slice(
104 buffer: &mut [u8],
105 fmt: &BStr,
106 args: &mut [Arg],
107) -> Result<usize, Error> {
108 printf_locale_to_slice(buffer, fmt, &locale::C_LOCALE, args)
109}