drone_macros_core/
cfg_cond.rs1use crate::parse_ident;
2use proc_macro2::TokenStream;
3use quote::quote;
4use std::collections::HashMap;
5use syn::{
6 bracketed, parenthesized,
7 parse::{Parse, ParseStream, Result},
8 Ident, LitStr, Token,
9};
10
11#[derive(Default, Clone, Debug)]
13pub struct CfgCond {
14 clauses: Vec<Vec<(Ident, LitStr)>>,
16 inverse: bool,
17}
18
19impl Parse for CfgCond {
20 fn parse(input: ParseStream<'_>) -> Result<Self> {
21 let mut clauses = Vec::new();
22 if input.peek(Token![#]) {
23 input.parse::<Token![#]>()?;
24 let input2;
25 bracketed!(input2 in input);
26 parse_ident!(input2, "cfg");
27 let input3;
28 parenthesized!(input3 in input2);
29 let ident = input3.parse::<Ident>()?;
30 if ident == "any" {
31 let input4;
32 parenthesized!(input4 in input3);
33 let mut last_comma = true;
34 while last_comma && !input4.is_empty() {
35 let ident = input4.parse::<Ident>()?;
36 input4.parse::<Token![=]>()?;
37 clauses.push((ident, input4.parse()?));
38 last_comma = input4.parse::<Option<Token![,]>>()?.is_some();
39 }
40 } else {
41 input3.parse::<Token![=]>()?;
42 clauses.push((ident, input3.parse()?));
43 if !input3.is_empty() {
44 return Err(input3.error("Unsupported attribute"));
45 }
46 }
47 }
48 Ok(Self {
49 clauses: if clauses.is_empty() { vec![] } else { vec![clauses] },
50 inverse: false,
51 })
52 }
53}
54
55impl CfgCond {
56 pub fn add_clause(&mut self, rhs: &Self) {
62 assert!(!self.inverse);
63 assert!(!rhs.inverse);
64 self.clauses.append(&mut rhs.clauses.clone());
65 }
66
67 pub fn attrs(&self) -> TokenStream {
69 let Self { clauses, inverse } = self;
70 let tokens = clauses
71 .iter()
72 .map(|clauses| {
73 clauses.iter().map(|(key, value)| quote!(#key = #value)).collect::<Vec<_>>()
74 })
75 .collect::<Vec<_>>();
76 if tokens.is_empty() {
77 quote!()
78 } else if *inverse {
79 quote!(#[cfg(not(any(#(all(#(#tokens),*)),*)))])
80 } else {
81 quote!(#(#[cfg(any(#(#tokens),*))])*)
82 }
83 }
84
85 fn to_dnf(&self) -> Vec<Vec<(Ident, LitStr)>> {
87 assert!(!self.inverse);
88 match self.clauses.iter().map(Vec::as_slice).collect::<Vec<_>>().as_slice() {
89 [] | [[]] | [[], []] => Vec::new(),
90 [x] | [x, []] | [[], x] => x.iter().map(|x| vec![x.clone()]).collect(),
91 [x, y] => {
92 let mut dnf = Vec::new();
93 for x in x.iter() {
94 for y in y.iter() {
95 dnf.push(vec![x.clone(), y.clone()]);
96 }
97 }
98 dnf
99 }
100 x => panic!("{} clauses of CNF is unsupported", x.len()),
101 }
102 }
103}
104
105pub trait CfgCondExt<T: Clone> {
107 fn transpose(self) -> Vec<(CfgCond, Vec<T>)>;
110}
111
112impl<T: Clone> CfgCondExt<T> for &[(CfgCond, T)] {
113 fn transpose(self) -> Vec<(CfgCond, Vec<T>)> {
114 let mut map: HashMap<_, Vec<_>> = HashMap::new();
115 let mut default = Vec::new();
116 for (clauses, item) in self {
117 let clauses = clauses.to_dnf();
118 if clauses.is_empty() {
119 default.push(item.clone());
120 } else {
121 for cond in clauses {
122 map.entry(cond).or_default().push(item.clone());
123 }
124 }
125 }
126 let mut result = Vec::new();
127 for (clauses, mut items) in map {
128 let clauses = CfgCond { clauses: vec![clauses], inverse: false };
129 items.append(&mut default.clone());
130 result.push((clauses, items));
131 }
132 let clauses = result.iter().flat_map(|(x, _)| x.clauses.clone()).collect();
133 let clauses = CfgCond { clauses, inverse: true };
134 result.push((clauses, default));
135 result
136 }
137}