defmt_macros/function_like/
assert_binop.rs

1use defmt_parser::Level;
2use proc_macro::TokenStream;
3use quote::quote;
4use syn::{parse_macro_input, punctuated::Punctuated};
5
6use crate::{construct, function_like::log};
7
8use self::args::Args;
9
10mod args;
11
12pub(crate) fn eq(args: TokenStream) -> TokenStream {
13    expand(args, BinOp::Eq)
14}
15
16pub(crate) fn ne(args: TokenStream) -> TokenStream {
17    expand(args, BinOp::Ne)
18}
19
20fn expand(args: TokenStream, binop: BinOp) -> TokenStream {
21    let args = parse_macro_input!(args as Args);
22
23    let left = args.left;
24    let right = args.right;
25
26    let mut formatting_args = Punctuated::new();
27
28    let extra_string = if let Some(log_args) = args.log_args {
29        if let Some(args) = log_args.formatting_args {
30            formatting_args.extend(args);
31        }
32        format!(": {}", log_args.format_string.value())
33    } else {
34        String::new()
35    };
36
37    let vals = match binop {
38        BinOp::Eq => &["left_val", "right_val"][..],
39        BinOp::Ne => &["left_val"][..],
40    };
41
42    for val in vals {
43        formatting_args.push(construct::variable(val));
44    }
45
46    let panic_msg = match binop {
47        BinOp::Eq => format!(
48            "panicked at 'assertion failed: `(left == right)`{}'
49 left: `{{:?}}`
50right: `{{:?}}`",
51            extra_string
52        ),
53        BinOp::Ne => format!(
54            "panicked at 'assertion failed: `(left != right)`{}'
55left/right: `{{:?}}`",
56            extra_string
57        ),
58    };
59
60    let log_args = log::Args {
61        format_string: construct::string_literal(&panic_msg),
62        formatting_args: Some(formatting_args),
63    };
64    let log_stmt = log::expand_parsed(Level::Error, log_args);
65
66    let mut cond = quote!(*left_val == *right_val);
67    if binop == BinOp::Eq {
68        cond = quote!(!(#cond));
69    }
70
71    quote!(
72        // evaluate arguments first
73        match (&(#left), &(#right)) {
74            (left_val, right_val) => {
75                // following `core::assert_eq!`
76                if #cond {
77                    #log_stmt;
78                    defmt::export::panic()
79                }
80            }
81        }
82    )
83    .into()
84}
85
86#[derive(PartialEq)]
87enum BinOp {
88    Eq,
89    Ne,
90}