use defmt::{export::fetch_string_index, write, Debug2Format, Display2Format, Format, Formatter};
fn inc(index: u16, n: u16) -> u16 {
index.wrapping_add(n)
}
fn write_format<T: Format + ?Sized>(val: &T) {
defmt::export::istr(&T::_format_tag());
val._format_data();
}
macro_rules! check {
([$($x:expr),* $(,)?]) => {
{
let mut v = Vec::<u8>::new();
$(
v.extend(&($x).to_le_bytes());
)*
assert_eq!(defmt::export::fetch_bytes(), v);
}
};
}
macro_rules! check_format {
($format:expr, [$($x:expr),* $(,)?] $(,)?) => {
{
let mut v = Vec::<u8>::new();
$(
v.extend(&($x).to_le_bytes());
)*
write_format($format);
assert_eq!(defmt::export::fetch_bytes(), v);
}
}
}
#[test]
fn write() {
let index = fetch_string_index();
let g = defmt::export::make_formatter();
write!(g, "The answer is {=u8}", 42);
check!([
index, 42u8, ]);
let g = defmt::export::make_formatter();
write!(g, "The answer is {=?}", 42u8);
check!([
inc(index, 1), inc(index, 2), 42u8, ]);
}
#[test]
fn bitfields_mixed() {
let index = fetch_string_index();
let g = defmt::export::make_formatter();
write!(
g,
"bitfields {0=7..12}, {1=0..5}",
0b1110_0101_1111_0000u16, 0b1111_0000u8
);
check!([
index, 0b1111_0000u8,
0b1110_0101u8, 0b1111_0000u8, ]);
}
#[test]
fn bitfields_across_octets() {
let index = fetch_string_index();
let g = defmt::export::make_formatter();
write!(g, "bitfields {0=0..7} {0=9..14}", 0b0110_0011_1101_0010u16);
check!([
index, 0b1101_0010u8,
0b0110_0011u8, ]);
}
#[test]
fn bitfields_truncate_lower() {
let index = fetch_string_index();
let g = defmt::export::make_formatter();
write!(
g,
"bitfields {0=9..14}",
0b0000_0000_0000_1111_0110_0011_1101_0010u32
);
check!([
index, 0b0110_0011u8, ]);
}
#[test]
fn bitfields_assert_range_exclusive() {
let index = fetch_string_index();
let g = defmt::export::make_formatter();
write!(g, "bitfields {0=6..8}", 0b1010_0101u8,);
check!([
index, 0b1010_0101u8
]);
}
#[test]
fn debug_attr_struct() {
#[derive(Debug)]
struct DebugOnly {
_a: i32,
}
#[derive(Format)]
struct X {
y: bool,
#[defmt(Debug2Format)]
d: DebugOnly,
}
let index = fetch_string_index();
check_format!(
&X {
y: false,
d: DebugOnly { _a: 3 }
},
[
index, 0b0u8, inc(index, 1), b'D', b'e',
b'b',
b'u',
b'g',
b'O',
b'n',
b'l',
b'y',
b' ',
b'{',
b' ',
b'_',
b'a',
b':',
b' ',
b'3',
b' ',
b'}',
0xffu8
],
)
}
#[test]
fn display_attr_enum() {
use std::fmt;
struct DisplayOnly {}
impl fmt::Display for DisplayOnly {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Display")
}
}
#[derive(Format)]
enum X {
#[allow(dead_code)]
Bool(bool),
Display(#[defmt(Display2Format)] DisplayOnly),
}
let index = fetch_string_index();
check_format!(
&X::Display(DisplayOnly {}),
[
index, 0b1u8, inc(index, 1), b'D', b'i',
b's',
b'p',
b'l',
b'a',
b'y',
0xffu8
],
)
}
#[test]
fn boolean_struct() {
#[derive(Format)]
struct X {
y: bool,
z: bool,
}
let index = fetch_string_index();
check_format!(
&X { y: false, z: true },
[
index, 0b0u8, 0b1u8, ],
)
}
#[test]
fn single_struct() {
#[derive(Format)]
struct X {
y: u8,
z: u16,
}
let index = fetch_string_index();
check_format!(
&X { y: 1, z: 2 },
[
index, 1u8, 2u8, 0u8, ],
)
}
#[test]
fn single_struct_manual() {
struct X {
y: u8,
z: u16,
}
impl Format for X {
fn format(&self, f: Formatter) {
defmt::write!(f, "X {{ y: {=u8}, z: {=u16} }}", self.y, self.z)
}
}
let index = fetch_string_index();
check_format!(
&X { y: 1, z: 2 },
[
index, inc(index, 1), 1u8, 2u16, 0u16, ],
)
}
#[test]
fn single_struct_manual_multiwrite() {
struct X {
y: u8,
z: u16,
}
impl Format for X {
fn format(&self, f: Formatter) {
defmt::write!(f, "y={=u8}", self.y);
defmt::write!(f, "z={=u16}", self.z);
}
}
let index = fetch_string_index();
check_format!(
&X { y: 1, z: 2 },
[
index, inc(index, 1), 1u8, inc(index, 2), 2u16, 0u16, ],
)
}
#[test]
fn slice_struct_manual_multiwrite() {
struct X {
y: u8,
z: u16,
}
impl Format for X {
fn format(&self, f: Formatter) {
defmt::write!(f, "y={=u8}", self.y);
defmt::write!(f, "z={=u16}", self.z);
}
}
let index = fetch_string_index();
check_format!(
&[X { y: 1, z: 2 }, X { y: 3, z: 4 }][..],
[
index, 2u32, inc(index, 1), inc(index, 2), 1u8, inc(index, 3), 2u16, 0u16, inc(index, 4), 3u8, inc(index, 5), 4u16, 0u16, ],
)
}
#[test]
fn nested_struct() {
#[derive(Format)]
struct X {
y: Y,
}
#[derive(Format)]
struct Y {
z: u8,
}
let val = 42u8;
let index = fetch_string_index();
check_format!(
&X { y: Y { z: val } },
[
index, inc(index, 1), val,
],
);
}
#[test]
fn tuple_struct() {
#[derive(Format)]
struct Struct(u8, u16);
let index = fetch_string_index();
check_format!(
&Struct(0x1f, 0xaaaa),
[
index, 0x1fu8, 0xaaaau16, ],
);
}
#[test]
fn c_like_enum() {
#[derive(Format)]
#[allow(dead_code)]
enum Enum {
A,
B,
C,
}
let index = fetch_string_index();
check_format!(
&Enum::A,
[
index, 0u8, ],
);
let index = fetch_string_index();
check_format!(
&Enum::B,
[
index, 1u8, ],
);
}
#[test]
fn uninhabited_enum() {
#[derive(Format)]
enum Void {}
}
#[test]
fn univariant_enum() {
#[derive(Format)]
enum NoData {
Variant,
}
let index = fetch_string_index();
check_format!(
&NoData::Variant,
[
index, ],
);
#[derive(Format)]
enum Data {
Variant(u8, u16),
}
let index = fetch_string_index();
check_format!(
&Data::Variant(0x1f, 0xaaaa),
[
index, 0x1fu8, 0xaaaau16, ],
);
}
#[test]
fn nested_enum() {
#[derive(Format)]
#[allow(dead_code)]
enum CLike {
A,
B,
C,
}
#[derive(Format)]
enum Inner {
A(CLike, u8),
_B,
}
#[derive(Format)]
enum Outer {
Variant1 { pre: u8, inner: Inner, post: u8 },
Variant2,
Variant3(Inner),
}
let index = fetch_string_index();
check_format!(
&Outer::Variant1 {
pre: 0xEE,
inner: Inner::A(CLike::B, 0x07),
post: 0xAB,
},
[
index, 0u8, 0xEEu8, inc(index, 1), 0u8, inc(index, 2), 1u8, 0x07u8, 0xABu8, ],
);
let index = fetch_string_index();
check_format!(
&Outer::Variant2,
[
index, 1u8, ],
);
let index = fetch_string_index();
check_format!(
&Outer::Variant3(Inner::A(CLike::B, 0x07)),
[
index, 2u8, inc(index, 1), 0u8, inc(index, 2), 1u8, 0x07u8, ],
);
}
#[test]
fn slice() {
let index = fetch_string_index();
let val: &[u8] = &[23u8, 42u8];
check_format!(
val,
[
index, val.len() as u32, inc(index, 1), 23u8, 42u8, ],
)
}
#[test]
fn slice_of_usize() {
let index = fetch_string_index();
let val: &[usize] = &[23usize, 42];
check_format!(
val,
[
index, val.len() as u32, inc(index, 1), 23u32, 42u32, ],
)
}
#[test]
fn slice_of_bools() {
let index = fetch_string_index();
let val: &[bool] = &[true, true, false];
check_format!(
val,
[
index, val.len() as u32, inc(index, 1), 0b1u8,
0b1u8,
0b0u8,
],
)
}
#[test]
fn format_primitives() {
let index = fetch_string_index();
check_format!(
&42u8,
[
index, 42u8,
],
);
check_format!(
&42u16,
[
inc(index, 1), 42u16,
],
);
check_format!(
&513u16,
[
inc(index, 2), 513u16,
],
);
check_format!(
&42u32,
[
inc(index, 3), 42u32,
],
);
check_format!(
&513u32,
[
inc(index, 4), 513u32,
],
);
check_format!(
&5.13f32,
[
inc(index, 5), 246u8,
40u8,
164u8,
64u8,
],
);
check_format!(
&42i8,
[
inc(index, 6), 42u8,
],
);
check_format!(
&-42i8,
[
inc(index, 7), -42i8 as u8,
],
);
check_format!(
&None::<u8>,
[
inc(index, 8), 0u8, ],
);
check_format!(
&Some(42u8),
[
inc(index, 9), 1u8, inc(index, 10), 42u8, ],
);
check_format!(&-1isize, [inc(index, 11), -1i32]);
check_format!(&-128isize, [inc(index, 12), -128i32]);
check_format!(
&true,
[
inc(index, 13), 0b1u8,
],
);
check_format!(
&513u64,
[
inc(index, 14), 513u64,
],
);
check_format!(
&-2i64,
[
inc(index, 15), -2i64,
],
);
check_format!(
&'a',
[
inc(index, 16), 0x61u8,
0x00u8,
0x00u8,
0x00u8,
],
);
}
#[test]
fn istr() {
let index = fetch_string_index();
let interned = defmt::intern!("interned string contents");
check_format!(
&interned,
[
inc(index, 1), index,
],
);
}
#[test]
fn format_arrays() {
let index = fetch_string_index();
let array: [u16; 0] = [];
check_format!(
&array,
[
index, inc(index, 1), ]
);
let index = fetch_string_index();
let array: [u16; 3] = [1, 256, 257];
check_format!(
&array,
[
index, inc(index, 1), 1u16, 256u16, 257u16, ],
);
}
#[test]
fn format_slice_of_primitives() {
let index = fetch_string_index();
let slice: &[u16] = &[1, 256, 257];
check_format!(
slice,
[
index, slice.len() as u32, inc(index, 1), 1u16, 256u16, 257u16, ],
);
}
#[test]
fn format_slice_of_structs() {
#[derive(Format)]
struct X {
y: Y,
}
#[derive(Format)]
struct Y {
z: u8,
}
let index = fetch_string_index();
let slice: &[_] = &[X { y: Y { z: 42 } }, X { y: Y { z: 24 } }];
check_format!(
slice,
[
index, slice.len() as u32, inc(index, 1), inc(index, 2), 42u8, inc(index, 3), 24u8, ],
);
}
#[test]
fn format_slice_of_slices() {
let index = fetch_string_index();
let slice: &[&[u16]] = &[&[256, 257], &[258, 259, 260]];
check_format!(
slice,
[
index, slice.len() as u32, inc(index, 1), slice[0].len() as u32,
inc(index, 2), 256u16, 257u16, slice[1].len() as u32,
inc(index, 3), 258u16, 259u16, 260u16, ],
);
}
#[test]
fn format_slice_enum_slice() {
let index = fetch_string_index();
let slice: &[Option<&[u8]>] = &[None, Some(&[42, 43])];
check_format!(
slice,
[
index, slice.len() as u32, inc(index, 1), 0u8, 1u8, inc(index, 2), 2u32, inc(index, 3), 42u8,
43u8,
],
);
}
#[test]
fn format_slice_enum_generic_struct() {
#[derive(Format)]
struct S<T> {
x: u8,
y: T,
}
let index = fetch_string_index();
let slice: &[Option<S<u8>>] = &[None, Some(S { x: 42, y: 43 })];
check_format!(
slice,
[
index, slice.len() as u32, inc(index, 1), 0u8, 1u8, inc(index, 2), 42u8, inc(index, 3), 43u8, ],
);
}
#[test]
fn derive_with_bounds() {
#[derive(Format)]
struct S<T: Copy> {
val: T,
}
#[derive(Format)]
struct S2<'a: 'b, 'b> {
a: &'a u8,
b: &'b u8,
}
let index = fetch_string_index();
check_format!(
&S { val: 0 },
[
index, inc(index, 1), 0u32,
],
);
let index = fetch_string_index();
check_format!(
&S2 { a: &1, b: &2 },
[
index, 1u8, 2u8,
],
);
}
#[test]
fn format_bools() {
#[derive(Format)]
struct A(bool);
#[derive(Format)]
struct B(bool);
let index = fetch_string_index();
check_format!(
&(A(true), B(true)),
[
index, inc(index, 1), 0b1u8, inc(index, 2), 0b1u8, ],
);
}
#[test]
fn enum_variants() {
#[allow(dead_code)]
#[derive(Format)]
enum EnumSmall {
A0,
A1,
A2,
}
#[rustfmt::skip]
#[allow(dead_code)]
#[derive(Format)]
enum EnumLarge {
A000, A001, A002, A003, A004, A005, A006, A007, A008, A009, A010, A011, A012, A013, A014, A015, A016, A017,
A018, A019, A020, A021, A022, A023, A024, A025, A026, A027, A028, A029, A030, A031, A032, A033, A034, A035,
A036, A037, A038, A039, A040, A041, A042, A043, A044, A045, A046, A047, A048, A049, A050, A051, A052, A053,
A054, A055, A056, A057, A058, A059, A060, A061, A062, A063, A064, A065, A066, A067, A068, A069, A070, A071,
A072, A073, A074, A075, A076, A077, A078, A079, A080, A081, A082, A083, A084, A085, A086, A087, A088, A089,
A090, A091, A092, A093, A094, A095, A096, A097, A098, A099, A100, A101, A102, A103, A104, A105, A106, A107,
A108, A109, A110, A111, A112, A113, A114, A115, A116, A117, A118, A119, A120, A121, A122, A123, A124, A125,
A126, A127, A128, A129, A130, A131, A132, A133, A134, A135, A136, A137, A138, A139, A140, A141, A142, A143,
A144, A145, A146, A147, A148, A149, A150, A151, A152, A153, A154, A155, A156, A157, A158, A159, A160, A161,
A162, A163, A164, A165, A166, A167, A168, A169, A170, A171, A172, A173, A174, A175, A176, A177, A178, A179,
A180, A181, A182, A183, A184, A185, A186, A187, A188, A189, A190, A191, A192, A193, A194, A195, A196, A197,
A198, A199, A200, A201, A202, A203, A204, A205, A206, A207, A208, A209, A210, A211, A212, A213, A214, A215,
A216, A217, A218, A219, A220, A221, A222, A223, A224, A225, A226, A227, A228, A229, A230, A231, A232, A233,
A234, A235, A236, A237, A238, A239, A240, A241, A242, A243, A244, A245, A246, A247, A248, A249, A250, A251,
A252, A253, A254, A255, A256, A257, A258, A259, A260, A261, A262, A263, A264, A265, A266, A267, A268, A269,
}
let e = EnumSmall::A2;
let index = fetch_string_index();
check_format!(&e, [index, 2u8]);
let e = EnumLarge::A002;
let index = fetch_string_index();
check_format!(&e, [index, 2u16]);
let e = EnumLarge::A269;
let index = fetch_string_index();
check_format!(&e, [index, 269u16]);
}
#[test]
fn derive_str() {
#[derive(Format)]
struct S {
x: &'static str,
}
let s = S { x: "hi" };
let index = fetch_string_index();
check_format!(
&s,
[
index, 2u32, 104u8, 105u8, ],
);
}
#[test]
fn core_fmt_adapters() {
let index = fetch_string_index();
check_format!(&Debug2Format(&123u8), [index, b'1', b'2', b'3', 0xffu8]);
let index = fetch_string_index();
check_format!(&Display2Format(&123u8), [index, b'1', b'2', b'3', 0xffu8]);
}