diesel_derives/
diesel_public_if.rs
1use quote::quote;
2use syn::Token;
3use syn::{punctuated::Punctuated, DeriveInput};
4
5pub(crate) fn expand(cfg: CfgInput, item: EntryWithVisibility) -> proc_macro2::TokenStream {
6 item.hide_for_cfg(cfg.cfg, cfg.field_list)
7}
8
9pub struct CfgInput {
10 cfg: syn::Meta,
11 field_list: Vec<syn::Ident>,
12}
13
14impl syn::parse::Parse for CfgInput {
15 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
16 let mut cfg = Punctuated::<syn::Meta, Token![,]>::parse_terminated(input)?;
17 if cfg.len() == 1 {
18 Ok(Self {
19 cfg: cfg
20 .pop()
21 .expect("There is exactly one element")
22 .into_value(),
23 field_list: Vec::new(),
24 })
25 } else if cfg.len() == 2 {
26 let value_1 = cfg
27 .pop()
28 .expect("There is exactly one element")
29 .into_value();
30 let value_2 = cfg
31 .pop()
32 .expect("There is exactly one element")
33 .into_value();
34 let (cfg, fields) = if matches!(&value_1, syn::Meta::List(v) if v.path.is_ident("public_fields"))
35 {
36 (value_2, value_1)
37 } else if matches!(&value_2, syn::Meta::List(v) if v.path.is_ident("public_fields")) {
38 (value_1, value_2)
39 } else {
40 panic!(
41 "Incompatible argument list detected. `__diesel_public_if` \
42 expects a cfg argument and a optional public_fields"
43 )
44 };
45 let field_list = if let syn::Meta::List(v) = fields {
46 use syn::parse::Parser;
47 let parser =
48 syn::punctuated::Punctuated::<syn::Ident, syn::Token![,]>::parse_terminated;
49 let idents = parser.parse2(v.tokens)?;
50 idents.into_iter().collect()
51 } else {
52 unreachable!()
53 };
54 Ok(Self { cfg, field_list })
55 } else {
56 panic!(
57 "Incompatible argument list detected. `__diesel_public_if` \
58 expects a cfg argument and an optional public_fields"
59 )
60 }
61 }
62}
63
64#[derive(Clone)]
65pub enum EntryWithVisibility {
66 TraitFunction {
67 meta: Vec<syn::Attribute>,
68 tail: proc_macro2::TokenStream,
69 },
70 Item {
71 meta: Vec<syn::Attribute>,
72 vis: syn::Visibility,
73 tail: proc_macro2::TokenStream,
74 },
75 Struct {
76 meta: Vec<syn::Attribute>,
77 vis: syn::Visibility,
78 def: syn::DataStruct,
79 ident: syn::Ident,
80 generics: syn::Generics,
81 },
82}
83
84impl syn::parse::Parse for EntryWithVisibility {
85 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
86 let meta = syn::Attribute::parse_outer(input)?;
87 if input.peek(Token![fn]) || input.peek(Token![type]) {
88 let tail = input.parse()?;
89 Ok(Self::TraitFunction { meta, tail })
90 } else {
91 let vis = input.parse()?;
92 if input.peek(Token![struct]) {
93 let s = DeriveInput::parse(input)?;
94 if let syn::Data::Struct(def) = s.data {
95 Ok(Self::Struct {
96 meta,
97 vis,
98 def,
99 generics: s.generics,
100 ident: s.ident,
101 })
102 } else {
103 unreachable!()
104 }
105 } else {
106 let tail = input.parse()?;
107 Ok(Self::Item { meta, vis, tail })
108 }
109 }
110 }
111}
112
113impl EntryWithVisibility {
114 fn hide_for_cfg(
115 &self,
116 cfg: syn::Meta,
117 field_list: Vec<syn::Ident>,
118 ) -> proc_macro2::TokenStream {
119 match self {
120 EntryWithVisibility::TraitFunction { meta, tail } if field_list.is_empty() => quote! {
121 #(#meta)*
122 #[cfg_attr(not(#cfg), doc(hidden))]
123 #[cfg_attr(docsrs, doc(cfg(#cfg)))]
124 #tail
125 },
126 EntryWithVisibility::Item { meta, vis, tail } if field_list.is_empty() => {
127 quote! {
128 #(#meta)*
129 #[cfg(not(#cfg))]
130 #vis #tail
131
132 #(#meta)*
133 #[cfg(#cfg)]
134 pub #tail
135 }
136 }
137 EntryWithVisibility::Struct {
138 meta,
139 vis,
140 def,
141 ident,
142 generics,
143 } => {
144 let fields1 = def.fields.iter();
145 let fields2 = def.fields.iter().map(|f| {
146 let mut ret = f.clone();
147 if ret
148 .ident
149 .as_ref()
150 .map(|i| field_list.contains(i))
151 .unwrap_or(false)
152 {
153 ret.vis = syn::Visibility::Public(Default::default());
154 }
155 ret
156 });
157
158 quote! {
159 #(#meta)*
160 #[cfg(not(#cfg))]
161 #vis struct #ident #generics {
162 #(#fields1,)*
163 }
164
165 #(#meta)*
166 #[cfg(#cfg)]
167 #[non_exhaustive]
168 pub struct #ident #generics {
169 #(#fields2,)*
170 }
171 }
172 }
173 EntryWithVisibility::TraitFunction { .. } | EntryWithVisibility::Item { .. } => {
174 panic!("Public field list is only supported for structs")
175 }
176 }
177 }
178}