Skip to main content

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}