Reputation: 434
I have following struct and want to test implementation of Display trait:
use core::fmt::{Display, Formatter, Result};
struct B {}
impl Display for B {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
write!(f, "A {} C", "B")
}
}
#[test]
fn it_works() {
assert_eq!(format!("{}", B {}), "A B C")
}
it works in general case, however in #![no_std]
environment it yields an error due to the fact that format!
macro is missing (it allocates, so cannot be used in no_std
).
Is there idiomatic way to test core::std::Display
trait in no_std
scenarios?
I've tried to create my own core::std::Formatter
instance with custom buffer implementation to somehow circumvent it, and test fmt
method directly, but instancing this type is considered compiler internal.
Upvotes: 1
Views: 1157
Reputation: 434
It's possible to do it without allocation, and without any buffer, just by implementing core::fmt::Write
trait, as @Masklinn mentioned.
Here is sample implementation:
#![no_std]
use core::fmt::{Display, Formatter, Result, Write};
struct B {}
impl Display for B {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
write!(f, "A {} C", "B")
}
}
struct Comparator<'a> {
valid: bool,
to_compare: &'a str
}
impl<'a> Comparator<'a> {
fn new(s: &'a str) -> Self {
Self { valid: true, to_compare: s }
}
fn is_valid(self) -> bool {
self.valid && self.to_compare.is_empty()
}
}
impl<'a> Write for Comparator<'a> {
fn write_str(&mut self, s: &str) -> Result {
if s.eq(self.to_compare) {
self.valid = self.valid && true;
self.to_compare = "";
return Ok(());
}
if self.to_compare.starts_with(s) && self.to_compare.len() >= s.len() {
self.to_compare = &self.to_compare[s.len()..];
} else {
self.valid = false
}
Ok(())
}
}
#[test]
fn it_works() {
let mut cmp = Comparator::new("A B C");
let _ = write!(&mut cmp, "{}", B{});
assert!(cmp.is_valid());
}
#[test]
fn too_short() {
let mut cmp = Comparator::new("A B");
let _ = write!(&mut cmp, "{}", B{});
assert!(!cmp.is_valid());
}
#[test]
fn too_long() {
let mut cmp = Comparator::new("A B C D");
let _ = write!(&mut cmp, "{}", B{});
assert!(!cmp.is_valid());
}
EDIT: fixed bug in the code, was:
if self.to_compare.starts_with(s) && self.to_compare.len() >= s.len() {
self.to_compare = &self.to_compare[s.len()..];
self.valid = true
} else {
self.valid = false
}
fixed:
if self.to_compare.starts_with(s) && self.to_compare.len() >= s.len() {
self.to_compare = &self.to_compare[s.len()..];
} else {
self.valid = false
}
Upvotes: 1
Reputation: 42462
Why not just implement core::fmt::Write
on your custom buffer and use core::fmt::write!
to it?
format!
is basically a write!
to a string buffer, both really call Write::write_fmt
, with format!
having the conveniences that it provides its own (string) buffer and panics on error.
macro_rules! write {
($dst:expr, $($arg:tt)*) => {
$dst.write_fmt($crate::format_args!($($arg)*))
};
}
macro_rules! format {
($($arg:tt)*) => {{
let res = $crate::fmt::format($crate::__export::format_args!($($arg)*));
res
}}
}
pub fn format(args: Arguments<'_>) -> string::String {
let capacity = args.estimated_capacity();
let mut output = string::String::with_capacity(capacity);
output.write_fmt(args).expect("a formatting trait implementation returned an error");
output
}
Upvotes: 2
Reputation: 71210
The best option is to enable std for tests:
#![cfg_attr(no_std, not(test))]
If you cannot do it, and you know an upper limit for the format size, you can implement core::fmt::Write
for a u8
array wrapper and using it. The arrayvec
has already got you covered with ArrayString
:
let mut formatted = ArrayString::<10>::new();
use core::fmt::Write;
write!(formatted, "{}", B {}).expect("failed to format, probably buffer too small");
assert_eq!(&formatted, "A B C")
Upvotes: 1