use defmt::{
export::fetch_string_index, write, Debug2Format, Display2Format, Format, Formatter,
InternalFormatter,
};
fn inc(index: u8, n: u8) -> u8 {
index.wrapping_add(n) & 0x7F
}
fn check_format_implementation(val: &(impl Format + ?Sized), expected_encoding: &[u8]) {
let mut f = InternalFormatter::new();
let g = Formatter { inner: &mut f };
val.format(g);
f.finalize();
assert_eq!(f.bytes(), expected_encoding);
}
#[test]
fn write() {
let index = fetch_string_index();
let f = &mut InternalFormatter::new();
let g = Formatter { inner: f };
write!(g, "The answer is {=u8}", 42);
assert_eq!(
f.bytes(),
&[
index, 42, ]
);
let f2 = &mut InternalFormatter::new();
let g2 = Formatter { inner: f2 };
write!(g2, "The answer is {=?}", 42u8);
assert_eq!(
f2.bytes(),
&[
inc(index, 1), inc(index, 2), 42, ]
);
}
#[test]
fn booleans_max_num_bool_flags() {
let index = fetch_string_index();
let f = &mut InternalFormatter::new();
let g = Formatter { inner: f };
write!(
g,
"encode 8 bools {=bool} {=bool} {=bool} {=bool} {=bool} {=bool} {=bool} {=bool}",
false, true, true, false, true, false, true, true
);
assert_eq!(
f.bytes(),
&[
index, 0b0110_1011, ]
);
}
#[test]
fn booleans_less_than_max_num_bool_flags() {
let index = fetch_string_index();
let f = &mut InternalFormatter::new();
let g = Formatter { inner: f };
write!(
g,
"encode 3 bools {=bool} {=bool} {=bool}",
false, true, true
);
assert_eq!(
f.bytes(),
&[
index, 0b011, ]
);
}
#[test]
fn booleans_more_than_max_num_bool_flags() {
let index = fetch_string_index();
let f = &mut InternalFormatter::new();
let g = Formatter { inner: f };
write!(g, "encode 9 bools {=bool} {=bool} {=bool} {=bool} {=bool} {=bool} {=bool} {=bool} {=bool} {=bool}",
false, true, true, false, true, false, true, true, false, true);
assert_eq!(
f.bytes(),
&[
index, 0b0110_1011, 0b01, ]
);
}
#[test]
fn booleans_mixed() {
let index = fetch_string_index();
let f = &mut InternalFormatter::new();
let g = Formatter { inner: f };
write!(
g,
"encode mixed bools {=bool} {=bool} {=u8} {=bool}",
true, false, 42, true
);
assert_eq!(
f.bytes(),
&[
index, 42u8, 0b101, ]
);
}
#[test]
fn booleans_mixed_no_trailing_bool() {
let index = fetch_string_index();
let f = &mut InternalFormatter::new();
let g = Formatter { inner: f };
write!(g, "encode mixed bools {=bool} {=u8}", false, 42);
assert_eq!(
f.bytes(),
&[
index, 42u8, 0b0, ]
);
}
#[test]
fn bitfields_mixed() {
let index = fetch_string_index();
let f = &mut InternalFormatter::new();
let g = Formatter { inner: f };
write!(
g,
"bitfields {0=7..12}, {1=0..5}",
0b1110_0101_1111_0000u16, 0b1111_0000u8
);
assert_eq!(
f.bytes(),
&[
index, 0b1111_0000,
0b1110_0101, 0b1111_0000u8, ]
);
}
#[test]
fn bitfields_across_octets() {
let index = fetch_string_index();
let f = &mut InternalFormatter::new();
let g = Formatter { inner: f };
write!(g, "bitfields {0=0..7} {0=9..14}", 0b0110_0011_1101_0010u16);
assert_eq!(
f.bytes(),
&[
index, 0b1101_0010,
0b0110_0011, ]
);
}
#[test]
fn bitfields_truncate_lower() {
let index = fetch_string_index();
let f = &mut InternalFormatter::new();
let g = Formatter { inner: f };
write!(
g,
"bitfields {0=9..14}",
0b0000_0000_0000_1111_0110_0011_1101_0010u32
);
assert_eq!(
f.bytes(),
&[
index, 0b0110_0011, ]
);
}
#[test]
fn bitfields_assert_range_exclusive() {
let index = fetch_string_index();
let f = &mut InternalFormatter::new();
let g = Formatter { inner: f };
write!(g, "bitfields {0=6..8}", 0b1010_0101u8,);
assert_eq!(
f.bytes(),
&[
index, 0b1010_0101
]
);
}
#[test]
fn boolean_struct() {
#[derive(Format)]
struct X {
y: bool,
z: bool,
}
let index = fetch_string_index();
check_format_implementation(
&X { y: false, z: true },
&[
index, 0b01, ],
)
}
#[test]
fn boolean_struct_mixed() {
#[derive(Format)]
struct X {
y: bool,
z: bool,
}
let index = fetch_string_index();
let f = &mut InternalFormatter::new();
let g = Formatter { inner: f };
write!(
g,
"mixed formats {=bool} {=?}",
true,
X { y: false, z: true }
);
assert_eq!(
f.bytes(),
&[
index, inc(index, 1), 0b101, ]
);
}
#[test]
fn single_struct() {
#[derive(Format)]
struct X {
y: u8,
z: u16,
}
let index = fetch_string_index();
check_format_implementation(
&X { y: 1, z: 2 },
&[
index, 1, 2, 0, ],
)
}
#[test]
fn single_struct_manual() {
struct X {
y: u8,
z: u16,
}
impl Format for X {
fn format(&self, f: Formatter) {
defmt::write!(f, "X {{ x: {=u8}, y: {=u16} }}", self.y, self.z)
}
}
let index = fetch_string_index();
check_format_implementation(
&X { y: 1, z: 2 },
&[
index, 1, 2, 0, ],
)
}
#[test]
fn nested_struct() {
#[derive(Format)]
struct X {
y: Y,
}
#[derive(Format)]
struct Y {
z: u8,
}
let val = 42;
let index = fetch_string_index();
check_format_implementation(
&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_implementation(
&Struct(0x1f, 0xaaaa),
&[
index, 0x1f, 0xaa, 0xaa, ],
);
}
#[test]
fn c_like_enum() {
#[derive(Format)]
#[allow(dead_code)]
enum Enum {
A,
B,
C,
}
let index = fetch_string_index();
check_format_implementation(
&Enum::A,
&[
index, 0, ],
);
let index = fetch_string_index();
check_format_implementation(
&Enum::B,
&[
index, 1, ],
);
}
#[test]
fn uninhabited_enum() {
#[derive(Format)]
enum Void {}
}
#[test]
fn univariant_enum() {
#[derive(Format)]
enum NoData {
Variant,
}
let index = fetch_string_index();
check_format_implementation(
&NoData::Variant,
&[
index, ],
);
#[derive(Format)]
enum Data {
Variant(u8, u16),
}
let index = fetch_string_index();
check_format_implementation(
&Data::Variant(0x1f, 0xaaaa),
&[
index, 0x1f, 0xaa, 0xaa, ],
);
}
#[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_implementation(
&Outer::Variant1 {
pre: 0xEE,
inner: Inner::A(CLike::B, 0x07),
post: 0xAB,
},
&[
index, 0, 0xEE, inc(index, 1), 0, inc(index, 2), 1, 0x07, 0xAB, ],
);
let index = fetch_string_index();
check_format_implementation(
&Outer::Variant2,
&[
index, 1, ],
);
let index = fetch_string_index();
check_format_implementation(
&Outer::Variant3(Inner::A(CLike::B, 0x07)),
&[
index, 2, inc(index, 1), 0, inc(index, 2), 1, 0x07, ],
);
}
#[test]
fn slice() {
let index = fetch_string_index();
let val: &[u8] = &[23u8, 42u8];
check_format_implementation(
val,
&[
index, val.len() as u8, inc(index, 1), 23, 42, ],
)
}
#[test]
fn slice_of_usize() {
let index = fetch_string_index();
let val: &[usize] = &[23usize, 42];
check_format_implementation(
val,
&[
index, val.len() as u8, inc(index, 1), 23, 42, ],
)
}
#[test]
fn slice_of_bools() {
let index = fetch_string_index();
let val: &[bool] = &[true, true, false];
check_format_implementation(
val,
&[
index, val.len() as u8, inc(index, 1), 0b110, ],
)
}
#[test]
fn format_primitives() {
let index = fetch_string_index();
check_format_implementation(
&42u8,
&[
index, 42,
],
);
check_format_implementation(
&42u16,
&[
inc(index, 1), 42,
0,
],
);
check_format_implementation(
&513u16,
&[
inc(index, 2), 1,
2,
],
);
check_format_implementation(
&42u32,
&[
inc(index, 3), 42,
0,
0,
0,
],
);
check_format_implementation(
&513u32,
&[
inc(index, 4), 1,
2,
0,
0,
],
);
check_format_implementation(
&5.13f32,
&[
inc(index, 5), 246,
40,
164,
64,
],
);
check_format_implementation(
&42i8,
&[
inc(index, 6), 42,
],
);
check_format_implementation(
&-42i8,
&[
inc(index, 7), -42i8 as u8,
],
);
check_format_implementation(
&None::<u8>,
&[
inc(index, 8), 0, ],
);
check_format_implementation(
&Some(42u8),
&[
inc(index, 9), 1, inc(index, 10), 42, ],
);
check_format_implementation(&-1isize, &[inc(index, 11), 0b0000_0001]);
check_format_implementation(&-128isize, &[inc(index, 12), 0xff, 0b0000_0001]);
check_format_implementation(
&true,
&[
inc(index, 13), 0b1,
],
);
check_format_implementation(
&513u64,
&[
inc(index, 14), 1,
2,
0,
0,
0,
0,
0,
0,
],
);
check_format_implementation(
&-2i64,
&[
inc(index, 15), 0xFE,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
0xFF,
],
);
check_format_implementation(
&'a',
&[
inc(index, 16), 0x61,
0x00,
0x00,
0x00,
],
);
}
#[test]
fn istr() {
let index = fetch_string_index();
let interned = defmt::intern!("interned string contents");
check_format_implementation(
&interned,
&[
inc(index, 1), index,
],
);
}
#[test]
fn format_arrays() {
let index = fetch_string_index();
let array: [u16; 0] = [];
check_format_implementation(&array, &[index]);
let index = fetch_string_index();
let array: [u16; 3] = [1, 256, 257];
check_format_implementation(
&array,
&[
index, inc(index, 1), 1, 0, 0, 1, 1, 1, ],
);
}
#[test]
fn format_slice_of_primitives() {
let index = fetch_string_index();
let slice: &[u16] = &[1, 256, 257];
check_format_implementation(
slice,
&[
index, slice.len() as u8, inc(index, 1), 1, 0, 0, 1, 1, 1, ],
);
}
#[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_implementation(
slice,
&[
index, slice.len() as u8, inc(index, 1), inc(index, 2), 42, 24, ],
);
}
#[test]
fn format_slice_of_slices() {
let index = fetch_string_index();
let slice: &[&[u16]] = &[&[256, 257], &[258, 259, 260]];
check_format_implementation(
slice,
&[
index, slice.len() as u8, inc(index, 1), slice[0].len() as u8,
inc(index, 2), 0, 1, 1, 1, slice[1].len() as u8,
2, 1, 3, 1, 4, 1, ],
);
}
#[test]
fn format_slice_enum_slice() {
let index = fetch_string_index();
let slice: &[Option<&[u8]>] = &[None, Some(&[42, 43])];
check_format_implementation(
slice,
&[
index, slice.len() as u8, inc(index, 1), 0, 1, inc(index, 2), 2, inc(index, 3), 42,
43,
],
);
}
#[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_implementation(
slice,
&[
index, slice.len() as u8, inc(index, 1), 0, 1, inc(index, 2), 42, inc(index, 3), 43, ],
);
}
#[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_implementation(
&S { val: 0 },
&[
index, inc(index, 1), 0,
0,
0,
0,
],
);
let index = fetch_string_index();
check_format_implementation(
&S2 { a: &1, b: &2 },
&[
index, 1, 2,
],
);
}
#[test]
fn format_bools() {
#[derive(Format)]
struct A(bool);
#[derive(Format)]
struct B(bool);
let index = fetch_string_index();
check_format_implementation(
&(A(true), B(true)),
&[
index, inc(index, 1), inc(index, 2), 0b11, ],
);
}
#[test]
fn issue_208() {
#[derive(Format)]
struct DhcpReprMin {
pub broadcast: bool,
pub a: [u8; 2],
}
let dhcp_repr = DhcpReprMin {
broadcast: true,
a: [10, 10],
};
let index = fetch_string_index();
check_format_implementation(
&dhcp_repr,
&[
index, inc(index, 1), inc(index, 2), 10, 10, 1, ],
);
}
#[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_implementation(&e, &[index, 2]);
let e = EnumLarge::A269;
let index = fetch_string_index();
check_format_implementation(&e, &[index, 13, 1]);
}
#[test]
fn derive_str() {
#[derive(Format)]
struct S {
x: &'static str,
}
let s = S { x: "hi" };
let index = fetch_string_index();
check_format_implementation(
&s,
&[
index, 2, 104, 105, ],
);
}
#[test]
fn core_fmt_adapters() {
let index = fetch_string_index();
check_format_implementation(&Debug2Format(&123u8), &[index, b'1', b'2', b'3', 0xff]);
let index = fetch_string_index();
check_format_implementation(&Display2Format(&123u8), &[index, b'1', b'2', b'3', 0xff]);
}