Skip to content

Commit 938f474

Browse files
committed
add format macro implementation
1 parent 644653b commit 938f474

File tree

2 files changed

+73
-3
lines changed

2 files changed

+73
-3
lines changed

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ mod histbuf;
101101
mod indexmap;
102102
mod indexset;
103103
mod linear_map;
104-
mod string;
104+
pub mod string;
105105
mod vec;
106106

107107
#[cfg(feature = "serde")]

src/string.rs

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use core::{cmp::Ordering, fmt, fmt::Write, hash, iter, ops, str};
1+
//! A fixed capacity [`String`](https://doc.rust-lang.org/std/string/struct.String.html)
2+
3+
use core::{cmp::Ordering, fmt, fmt::{Arguments, Write}, hash, iter, ops, str};
24

35
use crate::Vec;
46

@@ -290,7 +292,7 @@ impl<const N: usize> Default for String<N> {
290292
impl<'a, const N: usize> From<&'a str> for String<N> {
291293
fn from(s: &'a str) -> Self {
292294
let mut new = String::new();
293-
new.push_str(s).unwrap();
295+
new.push_str(s).expect("capacity exceeded");
294296
new
295297
}
296298
}
@@ -474,6 +476,53 @@ impl<const N: usize> Ord for String<N> {
474476
}
475477
}
476478

479+
/// Equivalent to [`format`](https://doc.rust-lang.org/std/fmt/fn.format.html).
480+
///
481+
/// Please note that using [`format!`] might be preferable.
482+
///
483+
/// [`format!`]: crate::format!
484+
pub fn format<const N: usize>(args: Arguments<'_>) -> String<N> {
485+
fn format_inner<const N: usize>(args: Arguments<'_>) -> String<N> {
486+
let mut output = String::new();
487+
output
488+
.write_fmt(args)
489+
// cannot differentiate between these error cases because fmt::Error is empty
490+
.expect("capacity exceeded or a formatting trait implementation returned an error");
491+
output
492+
}
493+
494+
args.as_str()
495+
.map_or_else(|| format_inner(args), String::from)
496+
}
497+
498+
/// Macro that creates a fixed capacity [`String`]. Equivalent to [`format!`](https://doc.rust-lang.org/std/macro.format.html).
499+
///
500+
/// The first argument is the capacity of the `String`. The following arguments work in the same way as the regular macro.
501+
///
502+
/// # Panics
503+
///
504+
/// `format!` panics if the formatted `String` would exceeded its capacity.
505+
/// `format!` also panics if a formatting trait implementation returns an error (same as the regular macro).
506+
///
507+
/// # Examples
508+
///
509+
/// ```
510+
/// use heapless::format;
511+
///
512+
/// format!(4, "test");
513+
/// format!(15, "hello {}", "world!");
514+
/// format!(20, "x = {}, y = {y}", 10, y = 30);
515+
/// let (x, y) = (1, 2);
516+
/// format!(12, "{x} + {y} = 3");
517+
/// ```
518+
#[macro_export]
519+
macro_rules! format {
520+
($max:literal, $($arg:tt)*) => {{
521+
let res = $crate::string::format::<$max>(core::format_args!($($arg)*));
522+
res
523+
}}
524+
}
525+
477526
macro_rules! impl_from_num {
478527
($num:ty, $size:expr) => {
479528
impl<const N: usize> From<$num> for String<N> {
@@ -708,4 +757,25 @@ mod tests {
708757
assert_eq!(0, s.len());
709758
assert_eq!(8, s.capacity());
710759
}
760+
761+
#[test]
762+
fn format() {
763+
let number = 5;
764+
let float = 3.12;
765+
let formatted = format!(15, "{:0>3} plus {float}", number);
766+
assert_eq!(formatted, "005 plus 3.12")
767+
}
768+
769+
#[test]
770+
#[should_panic]
771+
fn format_overflow() {
772+
let i = 1234567;
773+
format!(4, "13{}", i);
774+
}
775+
776+
#[test]
777+
#[should_panic]
778+
fn format_plain_string_overflow() {
779+
format!(2, "123");
780+
}
711781
}

0 commit comments

Comments
 (0)