1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
//! This module contains data types and functions to be used for multi-error reporting.
//!
//! Unlike [`MacroError`](crate::single::MacroError) type, [`MultiMacroErrors`] **is**
//! intended to be used directly by users, there are no macros to help with this (yet).
//!
//! # Example
//!
//! ```rust,ignore
//! fn parse_field(field: syn::FieldsNamed) -> Result<(), MacroError> {
//!     // ...
//! }
//!
//! #[proc_macro_derive(Hello)]
//! fn hello(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
//!     filter_macro_errors! {
//!         let err_storage = MultiMacroErrors::new();
//!
//!         // ... retrieve fields struct ...
//!         let fields = parse_struct(input);
//!         for field in fields {
//!             match parse_field(fields) {
//!                 Ok(a) => process(),
//!                 Err(err) => err_storage.add(err)
//!             }
//!         }
//!
//!         // abort if some errors had occurred
//!         err_storage.trigger_on_error()
//!     }
//! }
//! ```

use crate::ResultExt;
use crate::{MacroError, Payload};
use proc_macro2::{Span, TokenStream};
use std::iter::FromIterator;
use std::ops::{Deref, DerefMut};

/// This type represents a container for multiple errors. Each error has it's own span
/// location.
pub struct MultiMacroErrors(Vec<MacroError>);

impl MultiMacroErrors {
    /// Create an empty errors storage.
    pub fn new() -> Self {
        MultiMacroErrors(Vec::new())
    }

    /// Test if the storage contains no errors.
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    /// Add an error to the list of errors.
    pub fn add(&mut self, err: MacroError) {
        self.0.push(err)
    }

    /// Shortcut for `.add(MacroError::new(span, msg))`
    pub fn add_span_msg(&mut self, span: Span, msg: String) {
        self.add(MacroError::new(span, msg))
    }

    /// Convert this storage into [`TokenStream`] consisting of `compile_error!(msg1); compile_error!(msg2);`...
    /// sequence. Each `compile_error!` invocation gets the span attached to the particular message.
    pub fn into_compile_errors(self) -> TokenStream {
        let errs = self.0.into_iter().flat_map(MacroError::into_compile_error);
        TokenStream::from_iter(errs)
    }

    /// Abort the proc-macro's execution and display the errors contained.
    ///
    /// ### Note:
    ///
    /// This function aborts even no errors are present! It's very much possibly that
    /// [`trigger_on_error`][MultiMacroErrors::trigger_on_error] is what you really want.
    pub fn trigger(self) -> ! {
        panic!(Payload(self))
    }

    /// If this storage is not empty abort the proc-macro's execution and display the errors contained.
    pub fn trigger_on_error(self) {
        if !self.is_empty() {
            self.trigger()
        }
    }
}

impl<T> ResultExt for Result<T, MultiMacroErrors> {
    type Ok = T;

    fn unwrap_or_exit(self) -> T {
        match self {
            Ok(res) => res,
            Err(e) => e.trigger(),
        }
    }

    fn expect_or_exit(self, message: &str) -> T {
        match self {
            Ok(res) => res,
            Err(mut e) => {
                for MacroError { msg, .. } in e.0.iter_mut() {
                    *msg = format!("{}: {}", message, msg);
                }
                e.trigger()
            }
        }
    }
}

impl From<MultiMacroErrors> for TokenStream {
    fn from(errors: MultiMacroErrors) -> Self {
        errors.into_compile_errors()
    }
}

impl From<MultiMacroErrors> for proc_macro::TokenStream {
    fn from(errors: MultiMacroErrors) -> Self {
        errors.into_compile_errors().into()
    }
}

impl FromIterator<MacroError> for MultiMacroErrors {
    fn from_iter<I>(iter: I) -> Self
    where
        I: IntoIterator<Item = MacroError>,
    {
        MultiMacroErrors(Vec::from_iter(iter))
    }
}

impl IntoIterator for MultiMacroErrors {
    type Item = MacroError;
    type IntoIter = <Vec<MacroError> as IntoIterator>::IntoIter;

    fn into_iter(self) -> Self::IntoIter {
        self.0.into_iter()
    }
}

impl Deref for MultiMacroErrors {
    type Target = [MacroError];

    fn deref(&self) -> &[MacroError] {
        &self.0
    }
}

impl DerefMut for MultiMacroErrors {
    fn deref_mut(&mut self) -> &mut [MacroError] {
        &mut self.0
    }
}