Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add attributes for Format derive to use Debug2Format on specific fields #662

Merged
merged 8 commits into from
Feb 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions book/src/format.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,5 +79,32 @@ If you quickly want to get some code running and do not care about it being effi

Note that this always uses `{:?}` to format the contained value, meaning that any provided defmt display hints will be ignored.

When using `#[derive(Format)]` you may use the `#[defmt()]` attribute on specific fields to use these adapter types.
Example below:

``` rust
# extern crate defmt;
# use defmt::Format;
# mod serde_json {
# #[derive(Debug)]
# pub enum Error {}
# }
#[derive(Format)]
enum Error {
Serde(#[defmt(Debug2Format)] serde_json::Error),
ResponseTooLarge,
}

# struct Celsius();
# impl std::fmt::Display for Celsius {
# fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { Ok(()) }
# }
#[derive(Format)]
struct Reading {
#[defmt(Display2Format)]
temperature: Celsius,
}
```

[`Display2Format`]: https://docs.rs/defmt/*/defmt/struct.Display2Format.html
[`Debug2Format`]: https://docs.rs/defmt/*/defmt/struct.Debug2Format.html
89 changes: 87 additions & 2 deletions defmt/tests/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,91 @@ fn bitfields_assert_range_exclusive() {
]);
}

#[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, // "X {{ y: {=bool}, d: {=?} }}"
0b0u8, // y
inc(index, 1), // DebugOnly's format string
b'D', // Text of the Debug output
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, // "Bool({=bool})|Display({=?})"
0b1u8, // Variant: Display
inc(index, 1), // DisplayOnly's format string
b'D', // Text of the Display output
b'i',
b's',
b'p',
b'l',
b'a',
b'y',
0xffu8
],
)
}

#[test]
fn boolean_struct() {
#[derive(Format)]
Expand All @@ -163,7 +248,7 @@ fn boolean_struct() {
check_format!(
&X { y: false, z: true },
[
index, // "X {{ x: {=bool}, y: {=bool} }}"
index, // "X {{ y: {=bool}, z: {=bool} }}"
0b0u8, // y
0b1u8, // z
],
Expand All @@ -182,7 +267,7 @@ fn single_struct() {
check_format!(
&X { y: 1, z: 2 },
[
index, // "X {{ x: {=u8}, y: {=u16} }}"
index, // "X {{ y: {=u8}, z: {=u16} }}"
1u8, // x
2u8, // y.low
0u8, // y.high
Expand Down
7 changes: 7 additions & 0 deletions defmt/tests/ui/derive-empty-attr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#[derive(defmt::Format)]
struct S {
#[defmt()]
f: bool,
}

fn main() {}
5 changes: 5 additions & 0 deletions defmt/tests/ui/derive-empty-attr.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error: expected 1 attribute argument
--> $DIR/derive-empty-attr.rs:3:7
|
3 | #[defmt()]
| ^^^^^^^
7 changes: 7 additions & 0 deletions defmt/tests/ui/derive-invalid-attr-arg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#[derive(defmt::Format)]
struct S {
#[defmt(FooBar)]
f: bool,
}

fn main() {}
5 changes: 5 additions & 0 deletions defmt/tests/ui/derive-invalid-attr-arg.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error: expected `Debug2Format` or `Display2Format`
--> $DIR/derive-invalid-attr-arg.rs:3:13
|
3 | #[defmt(FooBar)]
| ^^^^^^
8 changes: 8 additions & 0 deletions defmt/tests/ui/derive-multi-attr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#[derive(defmt::Format)]
struct S {
#[defmt(Debug2Format)]
#[defmt()]
f: bool,
}

fn main() {}
7 changes: 7 additions & 0 deletions defmt/tests/ui/derive-multi-attr.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
error: multiple `defmt` attributes not supported
--> $DIR/derive-multi-attr.rs:3:5
|
3 | / #[defmt(Debug2Format)]
4 | | #[defmt()]
5 | | f: bool,
| |___________^
7 changes: 6 additions & 1 deletion macros/src/derives/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,17 @@ pub(crate) fn expand(input: TokenStream) -> TokenStream {
let mut input = parse_macro_input!(input as DeriveInput);

let ident = &input.ident;
let codegen::EncodeData { format_tag, stmts } = match &input.data {
let encode_data = match &input.data {
Data::Enum(data) => codegen::encode_enum_data(ident, data),
Data::Struct(data) => codegen::encode_struct_data(ident, data),
Data::Union(_) => abort_call_site!("`#[derive(Format)]` does not support unions"),
};

let codegen::EncodeData { format_tag, stmts } = match encode_data {
Ok(data) => data,
Err(e) => return e.into_compile_error().into(),
};

let codegen::Generics {
impl_generics,
type_generics,
Expand Down
6 changes: 3 additions & 3 deletions macros/src/derives/format/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ pub(crate) struct EncodeData {
pub(crate) stmts: Vec<TokenStream2>,
}

pub(crate) fn encode_struct_data(ident: &Ident, data: &DataStruct) -> EncodeData {
pub(crate) fn encode_struct_data(ident: &Ident, data: &DataStruct) -> syn::Result<EncodeData> {
let mut format_string = ident.to_string();
let mut stmts = vec![];
let mut field_patterns = vec![];

let encode_fields_stmts =
fields::codegen(&data.fields, &mut format_string, &mut field_patterns);
fields::codegen(&data.fields, &mut format_string, &mut field_patterns)?;

stmts.push(quote!(match self {
Self { #(#field_patterns),* } => {
Expand All @@ -29,7 +29,7 @@ pub(crate) fn encode_struct_data(ident: &Ident, data: &DataStruct) -> EncodeData
}));

let format_tag = construct::interned_string(&format_string, "derived", false);
EncodeData { format_tag, stmts }
Ok(EncodeData { format_tag, stmts })
}

pub(crate) struct Generics<'a> {
Expand Down
10 changes: 5 additions & 5 deletions macros/src/derives/format/codegen/enum_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ use crate::construct;

use super::EncodeData;

pub(crate) fn encode(ident: &Ident, data: &DataEnum) -> EncodeData {
pub(crate) fn encode(ident: &Ident, data: &DataEnum) -> syn::Result<EncodeData> {
if data.variants.is_empty() {
return EncodeData {
return Ok(EncodeData {
stmts: vec![quote!(match *self {})],
format_tag: construct::interned_string("!", "derived", false),
};
});
}

let mut format_string = String::new();
Expand All @@ -33,7 +33,7 @@ pub(crate) fn encode(ident: &Ident, data: &DataEnum) -> EncodeData {

let mut field_patterns = vec![];
let encode_fields_stmts =
super::fields::codegen(&variant.fields, &mut format_string, &mut field_patterns);
super::fields::codegen(&variant.fields, &mut format_string, &mut field_patterns)?;
let pattern = quote!( { #(#field_patterns),* } );

let encode_discriminant_stmt = discriminant_encoder.encode(index);
Expand All @@ -51,7 +51,7 @@ pub(crate) fn encode(ident: &Ident, data: &DataEnum) -> EncodeData {
#(#match_arms)*
})];

EncodeData { format_tag, stmts }
Ok(EncodeData { format_tag, stmts })
}

enum DiscriminantEncoder {
Expand Down
Loading