Skip to main content

spacetime_bindings_macro_input/
util.rs

1use proc_macro2::Span;
2use syn::Ident;
3
4pub trait ErrorSource {
5    fn error(self, msg: impl std::fmt::Display) -> syn::Error;
6}
7impl ErrorSource for Span {
8    fn error(self, msg: impl std::fmt::Display) -> syn::Error {
9        syn::Error::new(self, msg)
10    }
11}
12impl ErrorSource for &syn::meta::ParseNestedMeta<'_> {
13    fn error(self, msg: impl std::fmt::Display) -> syn::Error {
14        self.error(msg)
15    }
16}
17
18/// Ensures that `x` is `None` or returns an error.
19pub fn check_duplicate<T>(x: &Option<T>, src: impl ErrorSource) -> syn::Result<()> {
20    check_duplicate_msg(x, src, "duplicate attribute")
21}
22pub fn check_duplicate_msg<T>(
23    x: &Option<T>,
24    src: impl ErrorSource,
25    msg: impl std::fmt::Display,
26) -> syn::Result<()> {
27    if x.is_none() {
28        Ok(())
29    } else {
30        Err(src.error(msg))
31    }
32}
33
34pub fn one_of(options: &[super::sym::Symbol]) -> String {
35    match options {
36        [] => "unexpected attribute".to_owned(),
37        [a] => {
38            format!("expected `{a}`")
39        }
40        [a, b] => {
41            format!("expected `{a}` or `{b}`")
42        }
43        _ => {
44            let join = options.join("`, `");
45            format!("expected one of: `{}`", join)
46        }
47    }
48}
49
50#[macro_export]
51macro_rules! match_meta {
52    (match $meta:ident { $($matches:tt)* }) => {{
53        let meta: &syn::meta::ParseNestedMeta = &$meta;
54        match_meta!(@match (), (), meta { $($matches)* })
55    }};
56
57    (@match $acc:tt, $comparisons:tt, $meta:ident { $sym:path => $body:block $($rest:tt)* }) => {
58        match_meta!(@case $acc, $comparisons, $meta, _, $sym, $body, { $($rest)* })
59    };
60    (@match $acc:tt, $comparisons:tt, $meta:ident { $sym:path => $body:expr, $($rest:tt)* }) => {
61        match_meta!(@case $acc, $comparisons, $meta, _, $sym, $body, { $($rest)* })
62    };
63
64    (@match ($($acc:tt)*), ($($comparisons:expr),*), $meta:ident {}) => {
65        match () {
66            $($acc)*
67            _ => return Err($meta.error($crate::util::one_of(&[$($comparisons),*]))),
68        }
69    };
70
71    (@case ($($acc:tt)*), ($($comparisons:expr),*), $meta:ident, $binding:tt, $sym:path, $body:expr, { $($rest:tt)* }) => {
72        match_meta!(@match (
73            $($acc)*
74            _ if $meta.path == $sym => $body,
75        ), ($($comparisons,)* $sym), $meta { $($rest)* })
76    };
77}
78pub use match_meta;
79
80pub fn ident_to_litstr(ident: &Ident) -> syn::LitStr {
81    syn::LitStr::new(&ident.to_string(), ident.span())
82}