use defmt::{export::fetch_string_index, write, Format, Formatter};
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 = Formatter::new();
val.format(&mut f);
f.finalize();
assert_eq!(f.bytes(), expected_encoding);
}
#[test]
fn write() {
let index = fetch_string_index();
let ref mut f = Formatter::new();
write!(f, "The answer is {:u8}", 42);
assert_eq!(
f.bytes(),
&[
index, 42, ]
);
let ref mut f = Formatter::new();
write!(f, "The answer is {:?}", 42u8);
assert_eq!(
f.bytes(),
&[
inc(index, 1), inc(index, 2), 42, ]
);
}
#[test]
fn booleans_max_num_bool_flags() {
let index = fetch_string_index();
let ref mut f = Formatter::new();
write!(
f,
"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 ref mut f = Formatter::new();
write!(
f,
"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 ref mut f = Formatter::new();
write!(f, "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 ref mut f = Formatter::new();
write!(
f,
"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 ref mut f = Formatter::new();
write!(f, "encode mixed bools {:bool} {:u8}", false, 42);
assert_eq!(
f.bytes(),
&[
index, 42u8, 0b0, ]
);
}
#[test]
fn bitfields_mixed() {
let index = fetch_string_index();
let ref mut f = Formatter::new();
write!(
f,
"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 ref mut f = Formatter::new();
write!(f, "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 ref mut f = Formatter::new();
write!(
f,
"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 ref mut f = Formatter::new();
write!(f, "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 ref mut f = Formatter::new();
write!(
f,
"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: &mut 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,
],
);
}
#[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, inc(index, 1), 1,
inc(index, 2), 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, ],
);
}