attr_parser_fn/meta/
conflicts.rs

1use impl_variadics::impl_variadics;
2use proc_macro2::Span;
3use syn::{meta::ParseNestedMeta, Error, Result};
4
5use super::ParseMeta;
6
7pub fn conflicts<T>(group: T) -> Conflicts<T>
8where
9    T: ConflictGroup,
10{
11    Conflicts {
12        parser: group,
13        selected: None,
14    }
15}
16
17pub struct Conflicts<T>
18where
19    T: ConflictGroup,
20{
21    parser: T,
22    selected: Option<(String, u8)>,
23}
24
25pub trait ConflictGroup: Sized {
26    type Output;
27
28    fn parse_meta_conflict_alternative_arm(&self, f: &mut dyn std::fmt::Write) -> std::fmt::Result;
29    fn parse(&mut self, nested: &ParseNestedMeta) -> Result<Option<u8>>;
30    fn finish(self, index: u8) -> Result<<Self as ConflictGroup>::Output>;
31}
32
33impl_variadics! {
34    1..21 "T*" => {
35        impl<Out, #(#T0,)*> ConflictGroup for (#(#T0,)*)
36        where
37            #(#T0: ParseMeta<Output = Out>,)*
38        {
39            type Output = Out;
40
41            fn parse_meta_conflict_alternative_arm(&self, f: &mut dyn std::fmt::Write) -> std::fmt::Result {
42                self.conflict_alternative_arm(f)
43            }
44
45            fn parse(&mut self, nested: &ParseNestedMeta) -> Result<Option<u8>> {
46                #(if self.#index.parse(nested)? {
47                    Ok(Some(#index))
48                } else)* {
49                    Ok(None)
50                }
51            }
52
53            fn finish(self, index: u8) -> Result<<Self as ConflictGroup>::Output> {
54                match index {
55                    #(#index => self.#index.finish(),)*
56                    _ => unreachable!("invalid index")
57                }
58            }
59        }
60    }
61}
62
63impl<T> ParseMeta for Conflicts<T>
64where
65    T: ConflictGroup,
66{
67    type Output = T::Output;
68
69    fn conflict_alternative_arm(&self, f: &mut dyn std::fmt::Write) -> std::fmt::Result {
70        write!(f, "(conflict group: ")?;
71        self.parser.parse_meta_conflict_alternative_arm(f)?;
72        write!(f, ")")
73    }
74
75    fn parse(&mut self, nested: &ParseNestedMeta) -> Result<bool> {
76        match self.parser.parse(nested)? {
77            Some(index) => {
78                let new_name = nested.path.require_ident()?.to_string();
79                match &self.selected {
80                    Some((name, _)) => Err(Error::new_spanned(
81                        &nested.path,
82                        format!("attribute `{new_name}` is conflicts with `{name}`"),
83                    )),
84
85                    None => {
86                        self.selected = Some((new_name, index));
87                        Ok(true)
88                    }
89                }
90            }
91
92            None => Ok(false),
93        }
94    }
95
96    fn finish(self) -> Result<Self::Output> {
97        match self.selected {
98            Some((_, index)) => self.parser.finish(index),
99            None => Err(Error::new(Span::call_site(), {
100                let mut msg = "one of following attributes must be provided: ".to_string();
101                self.parser
102                    .parse_meta_conflict_alternative_arm(&mut msg)
103                    .unwrap();
104                msg
105            })),
106        }
107    }
108
109    fn ok_to_finish(&self) -> bool {
110        self.selected.is_some()
111    }
112}