diff options
Diffstat (limited to 'macros/src/util')
| -rw-r--r-- | macros/src/util/error.rs | 116 | ||||
| -rw-r--r-- | macros/src/util/iterator_ext.rs | 42 | ||||
| -rw-r--r-- | macros/src/util/mod.rs | 23 | 
3 files changed, 169 insertions, 12 deletions
| diff --git a/macros/src/util/error.rs b/macros/src/util/error.rs new file mode 100644 index 0000000..d068661 --- /dev/null +++ b/macros/src/util/error.rs @@ -0,0 +1,116 @@ +/// Used to create a error enum that converts into a [`Diagnostic`]. +/// +/// [`Diagnostic`]: proc_macro_error::Diagnostic +macro_rules! diagnostic_error_enum { +    ($(#[$meta: meta])* $visibility: vis enum $name: ident { +        $( +            #[error($($error: tt)*), span = $error_span: expr] +            $(#[note($($note: tt)*)$(, span = $note_span: expr)?])* +            $(#[help($($help: tt)*)$(, span = $help_span: expr)?])* +            $(#[err($($err: tt)*)$(, span = $err_span: expr)?])* +            $(#[source($source: ident)])? +            $variant: ident { +                $($variant_field: ident: $variant_field_type: ty),* +            }, +        )* +    }) => { +        $(#[$meta])* +        #[derive(Debug, Clone)] +        $visibility enum $name +        { +            $( +                $variant { +                    $($variant_field: $variant_field_type),* +                }, +            )* +        } + +        impl From<$name> for ::proc_macro_error::Diagnostic +        { +            #[must_use] +            fn from(err: $name) -> Self +            { +                let (error, span, notes, helps, errs, source): ( +                    String, +                    ::proc_macro2::Span, +                    Vec<(String, ::proc_macro2::Span)>, +                    Vec<(String, ::proc_macro2::Span)>, +                    Vec<(String, ::proc_macro2::Span)>, +                    Option<::proc_macro_error::Diagnostic> +                ) = match err { +                    $( +                        $name::$variant { +                            $($variant_field),* +                        } => { +                            ( +                                format!($($error)*), +                                $error_span, +                                vec![$( +                                    ( +                                        format!($($note)*), +                                        $crate::util::or!( +                                            ($($note_span)?) +                                            else (::proc_macro2::Span::call_site()) +                                        ) +                                    ) +                                ),*], +                                vec![$( +                                    ( +                                        format!($($help)*), +                                        $crate::util::or!( +                                            ($($help_span)?) +                                            else (::proc_macro2::Span::call_site()) +                                        ) +                                    ) +                                ),*], +                                vec![$( +                                    ( +                                        format!($($err)*), +                                        $crate::util::or!( +                                            ($($err_span)?) +                                            else (::proc_macro2::Span::call_site()) +                                        ) +                                    ) +                                ),*], +                                $crate::util::to_option!($($source.into())?) +                            ) +                        } +                    ),* +                }; + +                if let Some(source_diagnostic) = source { +                    source_diagnostic.emit(); +                } + +                let mut diagnostic = ::proc_macro_error::Diagnostic::spanned( +                    span, +                    ::proc_macro_error::Level::Error, +                    error +                ); + +                if !notes.is_empty() { +                    for (note, note_span) in notes { +                        diagnostic = diagnostic.span_note(note_span, note); +                    } +                } + +                if !helps.is_empty() { +                    for (help, help_span) in helps { +                        diagnostic = diagnostic.span_help(help_span, help); +                    } +                } + +                if !errs.is_empty() { +                    for (err, err_span) in errs { +                        diagnostic = diagnostic.span_error(err_span, err); +                    } +                } + +                diagnostic +            } +        } + +    }; +} + +pub(crate) use diagnostic_error_enum; diff --git a/macros/src/util/iterator_ext.rs b/macros/src/util/iterator_ext.rs index 5001068..17482ae 100644 --- a/macros/src/util/iterator_ext.rs +++ b/macros/src/util/iterator_ext.rs @@ -1,26 +1,39 @@ -use std::collections::HashMap; +use std::collections::HashSet;  use std::hash::Hash; +/// [`Iterator`] extension trait.  pub trait IteratorExt<Item> +where +    Item: Eq + Hash,  { -    fn find_duplicate(&mut self) -> Option<Item>; +    /// Finds the first occurance of a duplicate item. +    /// +    /// This function is short-circuiting. So it will immedietly return `Some` when +    /// it comes across a item it has already seen. +    /// +    /// The returned tuple contains the first item occurance & the second item occurance. +    /// In that specific order. +    /// +    /// Both items are returned in the case of the hash not being representative of the +    /// whole item. +    fn find_duplicate(self) -> Option<(Item, Item)>;  }  impl<Iter> IteratorExt<Iter::Item> for Iter  where      Iter: Iterator, -    Iter::Item: Eq + Hash + Clone, +    Iter::Item: Eq + Hash,  { -    fn find_duplicate(&mut self) -> Option<Iter::Item> +    fn find_duplicate(self) -> Option<(Iter::Item, Iter::Item)>      { -        let mut iterated_item_map = HashMap::<Iter::Item, ()>::new(); +        let mut iterated_item_map = HashSet::<Iter::Item>::new();          for item in self { -            if iterated_item_map.contains_key(&item) { -                return Some(item); +            if let Some(equal_item) = iterated_item_map.take(&item) { +                return Some((item, equal_item));              } -            iterated_item_map.insert(item, ()); +            iterated_item_map.insert(item);          }          None @@ -33,7 +46,7 @@ mod tests      use super::*;      #[test] -    fn can_find_duplicate() +    fn can_find_dupe()      {          #[derive(Debug, PartialEq, Eq, Clone, Hash)]          struct Fruit @@ -58,9 +71,14 @@ mod tests              ]              .iter()              .find_duplicate(), -            Some(&Fruit { -                name: "Apple".to_string() -            }) +            Some(( +                &Fruit { +                    name: "Apple".to_string() +                }, +                &Fruit { +                    name: "Apple".to_string() +                } +            ))          );          assert_eq!( diff --git a/macros/src/util/mod.rs b/macros/src/util/mod.rs index 0705853..d3edb67 100644 --- a/macros/src/util/mod.rs +++ b/macros/src/util/mod.rs @@ -1,4 +1,27 @@ +pub mod error;  pub mod item_impl;  pub mod iterator_ext;  pub mod string;  pub mod syn_path; + +macro_rules! to_option { +    ($($tokens: tt)+) => { +        Some($($tokens)+) +    }; + +    () => { +        None +    }; +} + +macro_rules! or { +    (($($tokens: tt)+) else ($($default: tt)*)) => { +        $($tokens)* +    }; + +    (() else ($($default: tt)*)) => { +        $($default)* +    }; +} + +pub(crate) use {or, to_option}; | 
