easy_macros_helpers/expr_error_wrap.rs
1use syn::{Block, Expr, ExprBlock, spanned::Spanned};
2
3/// Collect and provide error information for [`expr_error_wrap`].
4///
5/// Types implementing this trait can accumulate error messages
6/// and then show errors at the problematic code block by using [`expr_error_wrap`].
7///
8/// # Examples
9///
10/// ## Basic Usage with `Vec<String>`
11///
12#[doc = docify::embed!("src/examples.rs", error_data_basic_usage)]
13///
14/// ## Custom Implementation
15///
16#[doc = docify::embed!("src/examples.rs", error_data_custom_implementation)]
17pub trait CompileErrorProvider {
18 /// Returns `true` if there are no errors, `false` otherwise.
19 ///
20 /// This method is used to check whether error wrapping is needed.
21 fn no_errors(&self) -> bool;
22
23 /// Removes and returns all error data from the collection.
24 ///
25 /// After calling this method, the error collection should be empty.
26 /// The returned vector contains all accumulated error messages.
27 ///
28 /// # Note
29 ///
30 /// `Vec<String>` implements this trait, so you can use it directly
31 /// for simple error collection.
32 ///
33 /// # Returns
34 ///
35 /// A vector of error messages that were accumulated
36 fn error_data(&mut self) -> Vec<String>;
37}
38
39impl CompileErrorProvider for Vec<String> {
40 fn no_errors(&self) -> bool {
41 self.is_empty()
42 }
43
44 fn error_data(&mut self) -> Vec<String> {
45 let mut data = Vec::new();
46 std::mem::swap(self, &mut data);
47 data
48 }
49}
50
51/// Wraps an expression in a block that includes compile-time error messages.
52///
53/// This function is useful when you want to show that expression is problematic
54/// It transforms a single expression into a block containing `compile_error!`
55/// calls followed by the original expression.
56///
57/// # Arguments
58///
59/// * `expr` - The expression to wrap (will be modified in place)
60/// * `error_info` - A struct or collection implementing [`CompileErrorProvider`]
61///
62/// # Behavior
63///
64/// If there are no errors in `error_info`, the expression is left unchanged.
65/// If there are errors, the expression is wrapped in a block containing:
66/// 1. `compile_error!` statements for each error message
67/// 2. The original expression as the final statement
68///
69/// # Examples
70///
71/// ## Basic Usage
72///
73#[doc = docify::embed!("src/examples.rs", expr_error_wrap_basic_usage)]
74///
75/// ## Using Custom CompileErrorProvider Implementation
76///
77#[doc = docify::embed!("src/examples.rs", expr_error_wrap_custom_validator)]
78///
79/// # Use Cases
80///
81/// - Warning about deprecated or problematic usage patterns
82/// - Validating macro input and reporting multiple issues at once
83/// - Creating compile-time assertions with custom messages
84pub fn expr_error_wrap(expr: &mut Expr, error_info: &mut impl CompileErrorProvider) {
85 if !error_info.no_errors() {
86 let errors = error_info.error_data();
87
88 let span = expr.span();
89
90 let mut error_calls = errors
91 .iter()
92 .map(|error| {
93 let error: syn::Stmt = syn::parse_quote_spanned! {span=>
94 compile_error!(#error);
95 };
96 error
97 })
98 .collect::<Vec<_>>();
99
100 replace_with::replace_with_or_abort(expr, |ex| {
101 error_calls.push(syn::Stmt::Expr(ex, None));
102
103 Expr::Block(ExprBlock {
104 attrs: vec![],
105 label: None,
106 block: Block {
107 brace_token: Default::default(),
108 stmts: error_calls,
109 },
110 })
111 });
112 }
113}