1extern crate proc_macro;
9
10use quote::{format_ident, quote, ToTokens};
11use syn::{
12 parse::ParseStream, parse_macro_input, parse_quote, punctuated::Punctuated, Error, Expr,
13 ExprArray, ExprPath, ExprStruct, ExprTuple, FieldValue, Ident, Member, Path, QSelf, Result,
14 Stmt, Token, Type, TypePath,
15};
16
17#[proc_macro]
47pub fn proto_proc(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
48 let result = parse_macro_input!(input with parse_and_expand_top_level_struct);
49 result.to_token_stream().into()
50}
51
52fn parse_and_expand_top_level_struct(input: ParseStream) -> Result<Expr> {
53 expand_struct(input.parse()?, EnclosingContext::TopLevel)
54}
55
56#[derive(Clone)]
63enum EnclosingContext {
64 TopLevel,
67
68 Struct {
70 field: Ident,
72 },
73
74 Array {
76 repeated_field: Ident,
78 },
79
80 Map { map_field: Ident },
82}
83
84impl EnclosingContext {
85 pub fn repeated_field(&self) -> Ident {
86 match self {
87 EnclosingContext::Array { repeated_field } => repeated_field.clone(),
88 _ => panic!("Can only get the repeated field of an array context"),
89 }
90 }
91}
92
93fn expand_struct(
94 ExprStruct { attrs, qself, path, fields, rest, .. }: ExprStruct,
95 enclosing_context: EnclosingContext,
96) -> Result<Expr> {
97 if let Some(attr) = attrs.first() {
98 return Err(Error::new_spanned(attr, "unsupported syntax"));
99 }
100
101 let merge = rest.map(|rest| {
102 quote! {
103 ::protobuf::MergeFrom::merge_from(&mut this, #rest);
104 }
105 });
106
107 let fields = expand_struct_fields(fields)?;
108 let this_type = expand_struct_type(qself.clone(), path.clone(), enclosing_context.clone());
109 let (head, tail) =
110 expand_struct_head_tail(qself.clone(), path.clone(), enclosing_context.clone())?;
111
112 Ok(parse_quote! {{
113 let mut this: #this_type = #head;
114 #merge
115 #(#fields)*
116 #tail
117 }})
118}
119
120fn expand_struct_fields(fields: Punctuated<FieldValue, Token![,]>) -> Result<Vec<Stmt>> {
121 fields
122 .into_iter()
123 .map(|field| {
124 let Member::Named(ident) = field.member else {
125 return Err(Error::new_spanned(field, "field must be an identifier, not an index"));
126 };
127 let set_method = format_ident!("set_{}", ident);
128 let enclosing_context = EnclosingContext::Struct { field: ident };
129 let expr = match field.expr {
130 Expr::Struct(struct_) => {
131 let expanded = expand_struct(struct_, enclosing_context)?;
134 return Ok(parse_quote!(#expanded));
135 }
136 Expr::Array(array) => expand_array(array, enclosing_context)?,
137 expr => expr,
138 };
139 Ok(parse_quote! {
140 this.#set_method(#expr);
141 })
142 })
143 .collect()
144}
145
146fn expand_struct_type(
147 qself: Option<QSelf>,
148 path: Path,
149 enclosing_context: EnclosingContext,
150) -> Type {
151 if should_infer_message_type(&qself, &path) {
152 return parse_quote!(_);
153 }
154
155 let type_path = TypePath { qself, path };
156 if let EnclosingContext::Struct { .. } = enclosing_context {
157 parse_quote!(::protobuf::Mut<'_, #type_path>)
159 } else {
160 parse_quote!(#type_path)
161 }
162}
163
164fn expand_struct_head_tail(
170 qself: Option<QSelf>,
171 path: Path,
172 enclosing_context: EnclosingContext,
173) -> Result<(Expr, Option<Expr>)> {
174 match enclosing_context {
175 EnclosingContext::TopLevel => {
176 if should_infer_message_type(&qself, &path) {
177 Err(Error::new_spanned(path, "message type must be specified explicitly"))
178 } else {
179 let path = ExprPath { attrs: Vec::new(), qself, path };
180 Ok((parse_quote!(#path::new()), Some(parse_quote!(this))))
181 }
182 }
183 EnclosingContext::Struct { field } => {
184 let field_mut = format_ident!("{}_mut", field);
186 Ok((parse_quote!(this.#field_mut()), None))
187 }
188 EnclosingContext::Array { repeated_field } => {
189 Ok((
192 parse_quote!(::protobuf::__internal::get_repeated_default_value(
193 ::protobuf::__internal::Private,
194 this.#repeated_field()
195 )),
196 Some(parse_quote!(this)),
197 ))
198 }
199 EnclosingContext::Map { map_field } => {
200 Ok((
203 parse_quote!(::protobuf::__internal::get_map_default_value(
204 ::protobuf::__internal::Private,
205 this.#map_field()
206 )),
207 Some(parse_quote!(this)),
208 ))
209 }
210 }
211}
212
213fn should_infer_message_type(qself: &Option<QSelf>, path: &Path) -> bool {
216 qself.is_none() && path.get_ident().is_some_and(|ident| *ident == "__")
217}
218
219fn expand_array(array: ExprArray, enclosing_context: EnclosingContext) -> Result<Expr> {
220 if let Some(attr) = array.attrs.first() {
221 return Err(Error::new_spanned(attr, "unsupported syntax"));
222 }
223
224 let enclosing_context = EnclosingContext::Array {
225 repeated_field: match enclosing_context {
226 EnclosingContext::Struct { field } => field,
227 EnclosingContext::TopLevel
228 | EnclosingContext::Array { .. }
229 | EnclosingContext::Map { .. } => {
230 return Err(Error::new_spanned(array, "arrays must be nested inside a message"))
231 }
232 },
233 };
234
235 let array = array
236 .elems
237 .into_iter()
238 .map(|elem| match elem {
239 Expr::Struct(struct_) => expand_struct(struct_, enclosing_context.clone()),
240 Expr::Array(array) => expand_array(array, enclosing_context.clone()),
241 Expr::Tuple(tuple) => expand_tuple(
242 tuple,
243 EnclosingContext::Map { map_field: enclosing_context.repeated_field() },
244 ),
245 expr => Ok(expr),
246 })
247 .collect::<Result<Vec<Expr>>>()?;
248
249 Ok(parse_quote!([#(#array),*].into_iter()))
250}
251
252fn expand_tuple(tuple: ExprTuple, enclosing_context: EnclosingContext) -> Result<Expr> {
253 if let Some(attr) = tuple.attrs.first() {
254 return Err(Error::new_spanned(attr, "unsupported syntax"));
255 }
256
257 if tuple.elems.len() != 2 {
258 return Err(Error::new_spanned(tuple, "Map tuple literals must have exactly two elements"));
259 }
260
261 let key = tuple.elems[0].clone();
262 let value = match &tuple.elems[1] {
263 Expr::Struct(struct_) => expand_struct(struct_.clone(), enclosing_context.clone()),
264 expr => Ok(expr.clone()),
265 }?;
266 Ok(parse_quote!((#key, #value)))
267}