risingwave_common_proc_macro/
serde_prefix_all.rs1use proc_macro::TokenStream;
41use proc_macro2::Span;
42use quote::ToTokens;
43use syn::spanned::Spanned;
44use syn::{
45 Attribute, AttributeArgs, Data, DataEnum, DataStruct, DeriveInput, Error, Lit, Meta,
46 MetaNameValue, NestedMeta, parse_quote,
47};
48
49#[derive(Copy, Clone)]
50enum Mode {
51 Rename,
52 Alias,
53}
54
55struct ParsedArgs {
56 prefix: String,
57 prefix_span: Span,
58 mode: Mode,
59}
60
61fn parse_args(args: AttributeArgs) -> syn::Result<ParsedArgs> {
62 let mut prefix: Option<(String, Span)> = None;
63 let mut mode: Option<Mode> = None;
64
65 for arg in args {
66 match arg {
67 NestedMeta::Lit(Lit::Str(lit_str)) if prefix.is_none() => {
68 prefix = Some((lit_str.value(), lit_str.span()));
69 }
70 NestedMeta::Lit(Lit::Str(lit_str)) => {
71 return Err(Error::new(
72 lit_str.span(),
73 "Prefix is already specified; duplicate string literal",
74 ));
75 }
76 NestedMeta::Lit(lit) => {
77 return Err(Error::new(lit.span(), "The attribute is not a string"));
78 }
79 NestedMeta::Meta(Meta::NameValue(MetaNameValue { path, lit, .. }))
80 if path.is_ident("mode") =>
81 {
82 if mode.is_some() {
83 return Err(Error::new(path.span(), "mode is already specified"));
84 }
85
86 let parsed_mode = match lit {
87 Lit::Str(lit_str) => match lit_str.value().as_str() {
88 "rename" => Mode::Rename,
89 "alias" => Mode::Alias,
90 _ => {
91 return Err(Error::new(
92 lit_str.span(),
93 "mode must be \"rename\" or \"alias\"",
94 ));
95 }
96 },
97 _ => return Err(Error::new(lit.span(), "mode must be a string literal")),
98 };
99
100 mode = Some(parsed_mode);
101 }
102 NestedMeta::Meta(meta) => {
103 return Err(Error::new(
104 meta.span(),
105 "Unsupported attribute argument, expected a string literal prefix or mode",
106 ));
107 }
108 }
109 }
110
111 let (prefix, span) =
112 prefix.ok_or_else(|| Error::new(Span::call_site(), "Missing prefix string argument"))?;
113 Ok(ParsedArgs {
114 prefix,
115 prefix_span: span,
116 mode: mode.unwrap_or(Mode::Rename),
117 })
118}
119
120pub(crate) fn try_prefix_all(
121 args: AttributeArgs,
122 mut input: DeriveInput,
123) -> syn::Result<TokenStream> {
124 let ParsedArgs {
125 prefix,
126 prefix_span,
127 mode,
128 } = parse_args(args)?;
129
130 match &mut input.data {
131 Data::Enum(item_enum) => handle_enum(item_enum, &prefix[..], mode)?,
132 Data::Struct(item_struct) => handle_struct(item_struct, &prefix[..], mode)?,
133 _ => {
134 return Err(Error::new(
135 prefix_span,
136 "You can't use the macro on this type",
137 ));
138 }
139 };
140
141 Ok(input.to_token_stream().into())
142}
143
144fn create_attribute(prefix: &str, field_name: &str, mode: Mode) -> Attribute {
145 let attr_prefix = format!("{prefix}{field_name}");
146 match mode {
147 Mode::Rename => parse_quote! { #[serde(rename = #attr_prefix)] },
148 Mode::Alias => parse_quote! { #[serde(alias = #attr_prefix)] },
149 }
150}
151
152fn take_skip_attr(attrs: &mut Vec<Attribute>) -> syn::Result<bool> {
153 let mut found_skip = false;
154 let mut filtered = Vec::with_capacity(attrs.len());
155
156 for attr in attrs.drain(..) {
157 if !attr.path.is_ident("serde_prefix_all") {
158 filtered.push(attr);
159 continue;
160 }
161
162 let meta = attr.parse_meta()?;
163 match meta {
164 Meta::List(list) => {
165 let mut has_skip = false;
166 for nested in &list.nested {
167 match nested {
168 NestedMeta::Meta(Meta::Path(path)) if path.is_ident("skip") => {
169 has_skip = true;
170 }
171 _ => {
172 return Err(Error::new(
173 nested.span(),
174 "Expected #[serde_prefix_all(skip)]",
175 ));
176 }
177 }
178 }
179
180 if !has_skip {
181 return Err(Error::new(
182 list.span(),
183 "Expected #[serde_prefix_all(skip)]",
184 ));
185 }
186
187 found_skip = true;
188 }
189 _ => {
190 return Err(Error::new(
191 meta.span(),
192 "Expected #[serde_prefix_all(skip)]",
193 ));
194 }
195 }
196 }
197
198 *attrs = filtered;
199 Ok(found_skip)
200}
201
202fn handle_enum(input: &mut DataEnum, prefix: &str, mode: Mode) -> syn::Result<()> {
203 let variants = &mut input.variants;
204 for variant in variants.iter_mut() {
205 if take_skip_attr(&mut variant.attrs)? {
206 continue;
207 }
208
209 let field_name = variant.ident.to_string();
210 let attr = create_attribute(prefix, &field_name[..], mode);
211 variant.attrs.push(attr);
212 }
213
214 Ok(())
215}
216
217fn handle_struct(input: &mut DataStruct, prefix: &str, mode: Mode) -> syn::Result<()> {
218 let fields = &mut input.fields;
219 for field in fields.iter_mut() {
220 if take_skip_attr(&mut field.attrs)? {
221 continue;
222 }
223
224 let field_name = field.ident.as_ref().unwrap().to_string();
225 let attr = create_attribute(prefix, &field_name[..], mode);
226 field.attrs.push(attr);
227 }
228
229 Ok(())
230}