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:BinOp::Eq)
14}
15
16pub(crate) fn ne(args: TokenStream) -> TokenStream {
17 expand(args, binop: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}
91