1use std::collections::{HashMap, HashSet};
16
17use proc_macro2::{Literal, TokenStream};
18use quote::{format_ident, quote};
19
20use crate::{
21 emit::{
22 EmittedMessage,
23 borrowed::{
24 self, base_type, borrowed_default_expr, borrowed_populated_value, decode_borrow_call,
25 decode_owned_call, encode_call, encode_call_option_as_non_nullable, encoded_len_expr,
26 encoded_len_expr_option_as_non_nullable, field_forces_non_flex, flex_min, has_any_flex,
27 has_float64_recursive, is_nullable, is_struct_type, is_tagged, needs_lifetime,
28 nullable_split_cond, owned_struct_path_for, spec_needs_lifetime, struct_path_for,
29 tagged_field_needs_owned, tagged_is_default_cond, to_owned_field_expr, version_cond,
30 },
31 common::{self, banner, parse_tokens, statement, tag_literal},
32 owned::{EmitError, tagged_version_cond},
33 owned_quote::tagged_decode_arm,
34 },
35 ir::{FieldSpec, MessageSpec, MessageType},
36 name_conv,
37 resolve::{self, Resolution, StructKind},
38 type_map,
39};
40
41type ResMap = HashMap<String, Resolution>;
42
43fn tagged_should_encode(f: &FieldSpec) -> Result<TokenStream, EmitError> {
44 if base_type(&f.field_type) == "bool" {
45 let field = format_ident!("{}", name_conv::field_name(&f.name));
46 return Ok(match &f.default {
47 Some(serde_json::Value::Bool(true)) => {
48 quote!(!self.#field)
49 }
50 Some(serde_json::Value::String(value)) if value == "true" => {
51 quote!(!self.#field)
52 }
53 Some(serde_json::Value::Bool(false)) => {
54 quote!(self.#field)
55 }
56 Some(serde_json::Value::String(value)) if value == "false" => quote!(self.#field),
57 _ => tagged_should_encode_from_default(f)?,
58 });
59 }
60 tagged_should_encode_from_default(f)
61}
62
63fn gated_tagged_should_encode(f: &FieldSpec, flex_minimum: i16) -> Result<TokenStream, EmitError> {
67 let value = tagged_should_encode(f)?;
68 Ok(match tagged_version_cond(f, flex_minimum) {
69 Some(cond) => {
72 let cond = parse_tokens(&cond)?;
73 quote!(#cond && #value)
74 }
75 None => value,
76 })
77}
78
79fn tagged_should_encode_from_default(f: &FieldSpec) -> Result<TokenStream, EmitError> {
80 let default = tagged_is_default_cond(f);
81 if let Some(option) = default.strip_suffix(".is_none()") {
82 return parse_tokens(&format!("{option}.is_some()"));
83 }
84 if let Some((value, expected)) = default.split_once(" == ") {
85 return parse_tokens(&format!("{value} != {expected}"));
86 }
87 if let Some(positive) = default.strip_prefix('!') {
88 return parse_tokens(positive);
89 }
90 let default = parse_tokens(&default)?;
91 Ok(quote!(!(#default)))
92}
93
94fn owned_root(namespace: Option<&str>) -> String {
96 namespace.map_or_else(
97 || "crate::owned".to_owned(),
98 |ns| format!("crate::{ns}::owned"),
99 )
100}
101
102struct Ctx<'a> {
104 res_map: &'a ResMap,
105 parent_module: &'a str,
107 owned_root: &'a str,
109 fallible: &'a HashSet<String>,
112}
113
114pub(crate) fn fallible_structs(spec: &MessageSpec) -> HashSet<String> {
117 let mut structs: Vec<(String, &[FieldSpec])> =
118 vec![(spec.name.clone(), spec.fields.as_slice())];
119 let mut stack: Vec<&[FieldSpec]> = vec![spec.fields.as_slice()];
120 stack.extend(spec.common_structs.iter().map(|cs| cs.fields.as_slice()));
121 structs.extend(
122 spec.common_structs
123 .iter()
124 .map(|cs| (cs.name.clone(), cs.fields.as_slice())),
125 );
126 while let Some(fields) = stack.pop() {
127 for f in fields.iter().filter(|f| !f.fields.is_empty()) {
128 structs.push((base_type(&f.field_type).to_owned(), f.fields.as_slice()));
129 stack.push(&f.fields);
130 }
131 }
132 let mut fallible = HashSet::new();
133 let mut changed = true;
134 while changed {
135 changed = false;
136 for (name, fields) in &structs {
137 if fallible.contains(name) {
138 continue;
139 }
140 let holds_records = fields.iter().any(|f| {
141 let base = base_type(&f.field_type);
142 base == "records" || fallible.contains(base)
143 });
144 if holds_records {
145 fallible.insert(name.clone());
146 changed = true;
147 }
148 }
149 }
150 fallible
151}
152
153enum FlexSource {
154 TopLevel(i16),
155 Nested(i16),
156}
157
158impl FlexSource {
159 fn minimum(&self) -> i16 {
162 match self {
163 Self::TopLevel(fm) | Self::Nested(fm) => *fm,
164 }
165 }
166
167 fn tokens(&self) -> TokenStream {
168 match self {
169 Self::TopLevel(_) => quote!(is_flexible(version)),
170 Self::Nested(i16::MIN) => quote!(true),
171 Self::Nested(i16::MAX) => quote!(version == i16::MAX),
172 Self::Nested(fm) => {
173 let n = Literal::i16_unsuffixed(*fm);
174 quote!(version >= #n)
175 }
176 }
177 }
178}
179
180pub fn emit(
183 spec: &MessageSpec,
184 schemas_version: &str,
185 namespace: Option<&str>,
186) -> Result<EmittedMessage, EmitError> {
187 let res_map = resolve::resolve_message(spec)?;
189 let parent_module = name_conv::module_name(&spec.name);
190 let owned_root = owned_root(namespace);
191 let fallible = fallible_structs(spec);
192 let ctx = Ctx {
193 res_map: &res_map,
194 parent_module: &parent_module,
195 owned_root: &owned_root,
196 fallible: &fallible,
197 };
198
199 let mut structural = struct_block(
200 &ctx,
201 &name_conv::type_name(&spec.name),
202 &spec.fields,
203 spec_needs_lifetime(spec, &res_map),
204 &FlexSource::TopLevel(flex_min(spec)),
205 Some(version_err(spec)),
206 (true, has_any_flex(spec)),
207 )?;
208 structural.extend(nested_structs(&ctx, &spec.fields, flex_min(spec))?);
209
210 let imports = borrowed::emit_imports(spec, &res_map).to_string();
211 let constants = borrowed::emit_constants(spec)?.to_string();
212 let banner = common::banner(schemas_version);
213
214 let primary = format!("{banner}{imports}{constants}\n{structural}\n");
215 syn::parse_str::<syn::File>(&primary).map_err(|e| {
216 EmitError::Unsupported(format!(
217 "borrowed_quote produced invalid Rust for {}: {e}",
218 spec.name
219 ))
220 })?;
221
222 let commons = emit_commons(spec, &res_map, schemas_version, namespace, &fallible)?;
223
224 Ok(EmittedMessage { primary, commons })
225}
226
227fn emit_commons(
235 spec: &MessageSpec,
236 res_map: &ResMap,
237 schemas_version: &str,
238 namespace: Option<&str>,
239 fallible: &HashSet<String>,
240) -> Result<Vec<(String, String)>, EmitError> {
241 let message_snake = name_conv::module_name(&spec.name);
242 let fm = flex_min(spec);
243 let owned_root = owned_root(namespace);
244 let mut commons: Vec<(String, String)> = Vec::new();
245 for cs in &spec.common_structs {
246 let common_res_map: ResMap = res_map
252 .iter()
253 .map(|(k, v)| {
254 let new_path = if v.kind == StructKind::Common {
255 let snake = name_conv::module_name(k);
256 format!("super::{snake}::{k}")
257 } else {
258 v.rust_path.clone()
259 };
260 (
261 k.clone(),
262 Resolution {
263 kind: v.kind.clone(),
264 rust_path: new_path,
265 needs_lifetime: v.needs_lifetime,
266 },
267 )
268 })
269 .collect();
270
271 let cs_snake = name_conv::module_name(&cs.name);
272 let cs_parent_module = format!("common::{message_snake}::{cs_snake}");
275 let ctx = Ctx {
276 res_map: &common_res_map,
277 parent_module: &cs_parent_module,
278 owned_root: &owned_root,
279 fallible,
280 };
281
282 let name = name_conv::type_name(&cs.name);
283 let has_lt = needs_lifetime(&cs.fields, &common_res_map);
284 let mut struct_tokens = struct_block(
285 &ctx,
286 &name,
287 &cs.fields,
288 has_lt,
289 &FlexSource::Nested(fm),
290 None,
291 (false, fm < i16::MAX),
292 )?;
293 struct_tokens.extend(nested_structs(&ctx, &cs.fields, fm)?);
294
295 let body = format!(
296 "{}{}{}",
297 banner(schemas_version),
298 borrowed::emit_common_imports(&cs.fields, fm),
299 struct_tokens,
300 );
301
302 commons.push((format!("{message_snake}/{cs_snake}"), body));
303 }
304 Ok(commons)
305}
306
307fn version_err(spec: &MessageSpec) -> TokenStream {
308 match spec.message_type {
309 MessageType::Request | MessageType::Response => {
310 quote!(return Err(ProtocolError::UnsupportedVersion { api_key: API_KEY, version });)
311 }
312 MessageType::Header | MessageType::Data => {
313 let msg = format!("{} version out of range", spec.name);
314 quote!(return Err(ProtocolError::SchemaMismatch(#msg));)
315 }
316 }
317}
318
319fn struct_block(
324 ctx: &Ctx,
325 name: &str,
326 fields: &[FieldSpec],
327 has_lt: bool,
328 flex: &FlexSource,
329 guard: Option<TokenStream>,
330 options: (bool, bool),
331) -> Result<TokenStream, EmitError> {
332 let (top_level, has_flex) = options;
333 let ty = format_ident!("{name}");
334 let gen_a = if has_lt { quote!(<'a>) } else { quote!() };
336 let gen_de = if has_lt { quote!(<'de>) } else { quote!() };
337 let impl_a = if has_lt {
338 quote!(impl<'a>)
339 } else {
340 quote!(impl)
341 };
342 let ty_a = quote!(#ty #gen_a);
343 let ty_de = quote!(#ty #gen_de);
344 let ty_elided = if has_lt { quote!(#ty<'_>) } else { quote!(#ty) };
346
347 let eq = if has_float64_recursive(fields) {
348 quote!()
349 } else {
350 quote!(, Eq)
351 };
352
353 let (field_defs, default_assigns, owned_target, to_owned_assigns) =
354 struct_members(ctx, name, fields, top_level)?;
355 let owned_value = quote! {
356 #owned_target {
357 #to_owned_assigns
358 unknown_tagged_fields: self.unknown_tagged_fields.clone(),
359 }
360 };
361 let to_owned_fn = if ctx.fallible.contains(name) {
362 quote! {
363 pub fn to_owned(&self) -> Result<#owned_target, crate::records::RecordsError> {
368 Ok(#owned_value)
369 }
370 }
371 } else {
372 quote! {
373 #[must_use]
374 pub fn to_owned(&self) -> #owned_target {
375 #owned_value
376 }
377 }
378 };
379
380 let guard =
381 guard.map(|err| quote!(if !(MIN_VERSION..=MAX_VERSION).contains(&version) { #err }));
382 let flex_src = flex.tokens();
383 let flex_minimum = flex.minimum();
384 let encode_body = encode_body(ctx, fields, has_flex, flex_minimum)?;
385 let len_body = len_body(ctx, fields, has_flex, flex_minimum)?;
386 let decode_body = decode_body(ctx, fields, has_flex, flex_minimum)?;
387 let (codec_helpers, encode_body, decode_body) = if fields.len() >= 8 {
388 let (encode_helpers, encode_calls) =
389 split_encode_body(ctx, fields, has_flex, flex_minimum)?;
390 let (decode_helpers, decode_calls) =
391 split_decode_body(ctx, fields, has_flex, flex_minimum, has_lt)?;
392 (
393 quote!(#encode_helpers #decode_helpers),
394 encode_calls,
395 decode_calls,
396 )
397 } else {
398 (quote!(), encode_body, decode_body)
399 };
400 let encode_flex = flex_binding(&encode_body, &flex_src);
401 let len_flex = flex_binding(&len_body, &flex_src);
402 let decode_flex = flex_binding(&decode_body, &flex_src);
403 let (default_impl, default_ty) = if default_assigns.to_string().contains("'a") {
405 (impl_a.clone(), ty_a.clone())
406 } else {
407 (quote!(impl), ty_elided.clone())
408 };
409 let inherent_impl = if fields.len() >= 8 {
412 quote!(#impl_a #ty_a)
413 } else {
414 quote!(impl #ty_elided)
415 };
416 let populated_target = if has_lt {
417 quote!(impl #ty<'_>)
418 } else {
419 quote!(impl #ty)
420 };
421 let populated = populated_impl(ctx, &populated_target, fields)?;
422
423 Ok(quote! {
424 #[derive(Debug, Clone, PartialEq #eq)]
425 pub struct #ty_a {
426 #field_defs
427 pub unknown_tagged_fields: UnknownTaggedFields,
428 }
429
430 #default_impl Default for #default_ty {
431 fn default() -> Self {
432 Self {
433 #default_assigns
434 unknown_tagged_fields: UnknownTaggedFields::default(),
435 }
436 }
437 }
438
439 #inherent_impl {
440 #to_owned_fn
441
442 #codec_helpers
443 }
444
445 impl Encode for #ty_elided {
446 fn encode<B: BufMut>(&self, buf: &mut B, version: i16) -> Result<(), ProtocolError> {
447 #guard
448 #encode_flex
449 #encode_body
450 Ok(())
451 }
452 fn encoded_len(&self, version: i16) -> usize {
453 #len_flex
454 let mut n: usize = 0;
455 #len_body
456 n
457 }
458 }
459
460 impl<'de> DecodeBorrow<'de> for #ty_de {
461 fn decode_borrow(buf: &mut &'de [u8], version: i16) -> Result<Self, ProtocolError> {
462 #guard
463 #decode_flex
464 let mut out = Self::default();
465 #decode_body
466 Ok(out)
467 }
468 }
469
470 #populated
471 })
472}
473
474type Members = (TokenStream, TokenStream, TokenStream, TokenStream);
478
479fn struct_members(
480 ctx: &Ctx,
481 name: &str,
482 fields: &[FieldSpec],
483 top_level: bool,
484) -> Result<Members, EmitError> {
485 let field_defs = ordered(fields)
486 .map(|field| {
487 let name = format_ident!("{}", name_conv::field_name(&field.name));
488 let ty = parse_tokens(&field_type(ctx, field, top_level))?;
489 Ok(quote!(pub #name: #ty,))
490 })
491 .collect::<Result<Vec<_>, EmitError>>()?;
492 let default_assigns = ordered(fields)
493 .map(|field| {
494 let name = format_ident!("{}", name_conv::field_name(&field.name));
495 let expr = if is_struct_type(base_type(&field.field_type))
496 && !field.field_type.starts_with("[]")
497 && field.default.is_none()
498 && !is_nullable(field)
499 {
500 let ty = parse_tokens(&field_type(ctx, field, top_level))?;
501 quote!(<#ty>::default())
502 } else {
503 parse_tokens(&borrowed_default_expr(field, ctx.res_map))?
504 };
505 Ok(quote!(#name: #expr,))
506 })
507 .collect::<Result<Vec<_>, EmitError>>()?;
508 let owned_target = parse_tokens(&format!(
509 "{}::{}::{name}",
510 ctx.owned_root, ctx.parent_module
511 ))?;
512 let to_owned_assigns = ordered(fields)
513 .map(|field| {
514 let name = format_ident!("{}", name_conv::field_name(&field.name));
515 let expr = parse_tokens(&to_owned_expr(ctx, field, top_level))?;
516 Ok(quote!(#name: #expr,))
517 })
518 .collect::<Result<Vec<_>, EmitError>>()?;
519 Ok((
520 quote!(#(#field_defs)*),
521 quote!(#(#default_assigns)*),
522 owned_target,
523 quote!(#(#to_owned_assigns)*),
524 ))
525}
526
527fn flex_binding(body: &TokenStream, flex_source: &TokenStream) -> Option<TokenStream> {
528 body.to_string()
529 .contains("flex")
530 .then(|| quote!(let flex = #flex_source;))
531}
532
533fn ordered(fields: &[FieldSpec]) -> impl Iterator<Item = &FieldSpec> {
534 fields
535 .iter()
536 .filter(|f| !is_tagged(f))
537 .chain(fields.iter().filter(|f| is_tagged(f)))
538}
539
540fn default_is_null(f: &FieldSpec) -> bool {
541 matches!(&f.default, Some(serde_json::Value::Null))
542}
543
544fn field_type(ctx: &Ctx, f: &FieldSpec, top_level: bool) -> String {
547 if is_tagged(f) {
548 let nullable = is_nullable(f) || default_is_null(f);
549 if top_level && tagged_field_needs_owned(f, ctx.res_map) {
550 let owned_path = owned_struct_path_for(f, ctx.parent_module, ctx.res_map);
551 return type_map::owned_type(&f.field_type, nullable, owned_path.as_deref());
552 }
553 return type_map::borrowed_type(
554 &f.field_type,
555 nullable,
556 struct_path_for(f, ctx.res_map).as_deref(),
557 );
558 }
559 type_map::borrowed_type(
560 &f.field_type,
561 is_nullable(f),
562 struct_path_for(f, ctx.res_map).as_deref(),
563 )
564}
565
566fn to_owned_expr(ctx: &Ctx, f: &FieldSpec, top_level: bool) -> String {
567 let field = name_conv::field_name(&f.name);
568 let expr = format!("self.{field}");
569 let struct_path = ctx
572 .res_map
573 .get(base_type(&f.field_type))
574 .map(|r| r.rust_path.as_str());
575 let fallible = ctx.fallible.contains(base_type(&f.field_type));
576 if is_tagged(f) {
577 let nullable = is_nullable(f) || default_is_null(f);
578 if top_level && tagged_field_needs_owned(f, ctx.res_map) {
579 return format!("self.{field}.clone()");
580 }
581 return to_owned_field_expr(&f.field_type, &expr, nullable, struct_path, fallible);
582 }
583 to_owned_field_expr(&f.field_type, &expr, is_nullable(f), struct_path, fallible)
584}
585
586fn encode_body(
589 ctx: &Ctx,
590 fields: &[FieldSpec],
591 has_flex: bool,
592 flex_minimum: i16,
593) -> Result<TokenStream, EmitError> {
594 let stmts = fields
595 .iter()
596 .filter(|f| !is_tagged(f))
597 .map(encode_one)
598 .collect::<Result<Vec<_>, EmitError>>()?;
599 let trailer = if has_flex {
600 Some(encode_tagged_trailer(ctx, fields, flex_minimum)?)
601 } else {
602 None
603 };
604 Ok(quote!(#(#stmts)* #trailer))
605}
606
607fn encode_tagged_trailer(
609 ctx: &Ctx,
610 fields: &[FieldSpec],
611 flex_minimum: i16,
612) -> Result<TokenStream, EmitError> {
613 let mut_kw = if fields.iter().any(is_tagged) {
614 quote!(mut)
615 } else {
616 quote!()
617 };
618 let adds = fields
619 .iter()
620 .filter(|f| is_tagged(f))
621 .map(|f| encode_tagged(ctx, f, flex_minimum))
622 .collect::<Result<Vec<_>, EmitError>>()?;
623 Ok(quote! {
624 if flex {
625 let #mut_kw tagged = WriteTaggedFields::new();
626 #(#adds)*
627 tagged.write(buf, &self.unknown_tagged_fields)?;
628 }
629 })
630}
631
632fn split_encode_body(
633 ctx: &Ctx,
634 fields: &[FieldSpec],
635 has_flex: bool,
636 flex_minimum: i16,
637) -> Result<(TokenStream, TokenStream), EmitError> {
638 let mut helpers = Vec::new();
639 let mut calls = Vec::new();
640 for (index, field) in fields.iter().filter(|field| !is_tagged(field)).enumerate() {
641 let helper = format_ident!("encode_field_{index}");
642 let body = encode_one(field)?;
643 let flex = if body.to_string().contains("flex") {
644 quote!(flex)
645 } else {
646 quote!(_flex)
647 };
648 if body.to_string().contains('?') {
649 helpers.push(quote! {
650 fn #helper<B: BufMut>(
651 &self,
652 buf: &mut B,
653 version: i16,
654 #flex: bool,
655 ) -> Result<(), ProtocolError> {
656 #body
657 Ok(())
658 }
659 });
660 calls.push(quote!(self.#helper(buf, version, flex)?;));
661 } else {
662 helpers.push(quote! {
663 fn #helper<B: BufMut>(&self, buf: &mut B, version: i16, #flex: bool) {
664 #body
665 }
666 });
667 calls.push(quote!(self.#helper(buf, version, flex);));
668 }
669 }
670 if has_flex {
671 let helper = format_ident!("encode_tagged_fields");
672 let body = encode_tagged_trailer(ctx, fields, flex_minimum)?;
673 let version = if body.to_string().contains("version") {
674 quote!(version)
675 } else {
676 quote!(_version)
677 };
678 helpers.push(quote! {
679 fn #helper<B: BufMut>(
680 &self,
681 buf: &mut B,
682 #version: i16,
683 flex: bool,
684 ) -> Result<(), ProtocolError> {
685 #body
686 Ok(())
687 }
688 });
689 calls.push(quote!(self.#helper(buf, version, flex)?;));
690 }
691 Ok((quote!(#(#helpers)*), quote!(#(#calls)*)))
692}
693
694fn non_flex_wrap(f: &FieldSpec, body: TokenStream) -> TokenStream {
695 if field_forces_non_flex(f) {
696 quote!({ let flex = false; #body })
697 } else {
698 body
699 }
700}
701
702fn encode_one(f: &FieldSpec) -> Result<TokenStream, EmitError> {
703 let field = name_conv::field_name(&f.name);
704 let cond = parse_tokens(&version_cond(f.versions, "version"))?;
705 let expr = format!("self.{field}");
706 let inner = if let Some(ncond) = nullable_split_cond(f) {
707 let nb = parse_tokens(&encode_call(&f.field_type, &expr, true))?;
708 let nnb = parse_tokens(&encode_call_option_as_non_nullable(&f.field_type, &expr))?;
709 let nc = parse_tokens(&ncond)?;
710 non_flex_wrap(f, quote!(if #nc { #nb } else { #nnb }))
711 } else {
712 non_flex_wrap(
713 f,
714 parse_tokens(&encode_call(&f.field_type, &expr, is_nullable(f)))?,
715 )
716 };
717 let inner = statement(&inner);
718 Ok(quote!(if #cond { #inner }))
719}
720
721fn tagged_owned_codec(ctx: &Ctx, f: &FieldSpec) -> bool {
724 tagged_field_needs_owned(f, ctx.res_map)
725 && matches!(base_type(&f.field_type), "string" | "bytes")
726}
727
728fn encode_tagged(ctx: &Ctx, f: &FieldSpec, flex_minimum: i16) -> Result<TokenStream, EmitError> {
729 let field = name_conv::field_name(&f.name);
730 let tag = tag_literal(f)?;
731 let nullable = is_nullable(f) || default_is_null(f);
732 let expr = format!("self.{field}");
733 let body_str = if tagged_owned_codec(ctx, f) {
734 borrowed::owned_encode_call(&f.field_type, &expr, nullable)
735 } else {
736 encode_call(&f.field_type, &expr, nullable)
737 };
738 let body = statement(&parse_tokens(&body_str.replace("buf", "b"))?);
739 let len = parse_tokens(&tagged_len_str(ctx, f, &expr, nullable))?;
740 let should_encode = gated_tagged_should_encode(f, flex_minimum)?;
741 Ok(quote! {
742 if #should_encode {
743 let payload = encode_to_bytes(#len, |b| { #body Ok(()) })?;
744 tagged.add(#tag, payload);
745 }
746 })
747}
748
749fn tagged_len_str(ctx: &Ctx, f: &FieldSpec, expr: &str, nullable: bool) -> String {
750 if tagged_owned_codec(ctx, f) {
751 borrowed::owned_encoded_len_expr(&f.field_type, expr, nullable)
752 } else {
753 encoded_len_expr(&f.field_type, expr, nullable)
754 }
755}
756
757fn len_body(
758 ctx: &Ctx,
759 fields: &[FieldSpec],
760 has_flex: bool,
761 flex_minimum: i16,
762) -> Result<TokenStream, EmitError> {
763 let stmts = fields
764 .iter()
765 .filter(|f| !is_tagged(f))
766 .map(len_one)
767 .collect::<Result<Vec<_>, EmitError>>()?;
768 let trailer = if has_flex {
769 let mut_kw = if fields.iter().any(is_tagged) {
770 quote!(mut)
771 } else {
772 quote!()
773 };
774 let pushes = fields
775 .iter()
776 .filter(|f| is_tagged(f))
777 .map(|f| len_tagged(ctx, f, flex_minimum))
778 .collect::<Result<Vec<_>, EmitError>>()?;
779 Some(quote! {
780 if flex {
781 let #mut_kw known_pairs: Vec<(u32, usize)> = Vec::new();
782 #(#pushes)*
783 n += tagged_fields_len(&known_pairs, &self.unknown_tagged_fields);
784 }
785 })
786 } else {
787 None
788 };
789 Ok(quote!(#(#stmts)* #trailer))
790}
791
792fn len_one(f: &FieldSpec) -> Result<TokenStream, EmitError> {
793 let field = name_conv::field_name(&f.name);
794 let cond = parse_tokens(&version_cond(f.versions, "version"))?;
795 let expr = format!("self.{field}");
796 let inner = if let Some(ncond) = nullable_split_cond(f) {
797 let nb = parse_tokens(&encoded_len_expr(&f.field_type, &expr, true))?;
798 let nnb = parse_tokens(&encoded_len_expr_option_as_non_nullable(
799 &f.field_type,
800 &expr,
801 ))?;
802 let nc = parse_tokens(&ncond)?;
803 non_flex_wrap(f, quote!(if #nc { #nb } else { #nnb }))
804 } else {
805 non_flex_wrap(
806 f,
807 parse_tokens(&encoded_len_expr(&f.field_type, &expr, is_nullable(f)))?,
808 )
809 };
810 Ok(quote!(if #cond { n += #inner; }))
811}
812
813fn len_tagged(ctx: &Ctx, f: &FieldSpec, flex_minimum: i16) -> Result<TokenStream, EmitError> {
814 let field = name_conv::field_name(&f.name);
815 let tag = tag_literal(f)?;
816 let nullable = is_nullable(f) || default_is_null(f);
817 let len = parse_tokens(&tagged_len_str(ctx, f, &format!("self.{field}"), nullable))?;
818 let should_encode = gated_tagged_should_encode(f, flex_minimum)?;
819 Ok(quote! {
820 if #should_encode {
821 known_pairs.push((#tag, #len));
822 }
823 })
824}
825
826fn decode_body(
829 ctx: &Ctx,
830 fields: &[FieldSpec],
831 has_flex: bool,
832 flex_minimum: i16,
833) -> Result<TokenStream, EmitError> {
834 let stmts = fields
835 .iter()
836 .filter(|f| !is_tagged(f))
837 .map(|f| decode_one(ctx, f))
838 .collect::<Result<Vec<_>, EmitError>>()?;
839 let trailer = if has_flex {
840 Some(decode_tagged_block(ctx, fields, flex_minimum)?)
841 } else {
842 None
843 };
844 Ok(quote!(#(#stmts)* #trailer))
845}
846
847fn split_decode_body(
848 ctx: &Ctx,
849 fields: &[FieldSpec],
850 has_flex: bool,
851 flex_minimum: i16,
852 has_lt: bool,
853) -> Result<(TokenStream, TokenStream), EmitError> {
854 let mut helpers = Vec::new();
855 let mut calls = Vec::new();
856 let helper_buf = if has_lt {
857 quote!(&mut &'a [u8])
858 } else {
859 quote!(&mut &[u8])
860 };
861 for (index, field) in fields.iter().filter(|field| !is_tagged(field)).enumerate() {
862 let helper = format_ident!("decode_field_{index}");
863 let body = decode_one(ctx, field)?;
864 let flex = if body.to_string().contains("flex") {
865 quote!(flex)
866 } else {
867 quote!(_flex)
868 };
869 helpers.push(quote! {
870 fn #helper(
871 out: &mut Self,
872 buf: #helper_buf,
873 version: i16,
874 #flex: bool,
875 ) -> Result<(), ProtocolError> {
876 #body
877 Ok(())
878 }
879 });
880 calls.push(quote!(Self::#helper(&mut out, buf, version, flex)?;));
881 }
882 if has_flex {
883 let helper = format_ident!("decode_tagged_fields");
884 let body = decode_tagged_block(ctx, fields, flex_minimum)?;
885 let version = if body.to_string().contains("version") {
886 quote!(version)
887 } else {
888 quote!(_version)
889 };
890 helpers.push(quote! {
891 fn #helper(
892 out: &mut Self,
893 buf: #helper_buf,
894 #version: i16,
895 flex: bool,
896 ) -> Result<(), ProtocolError> {
897 #body
898 Ok(())
899 }
900 });
901 calls.push(quote!(Self::#helper(&mut out, buf, version, flex)?;));
902 }
903 Ok((quote!(#(#helpers)*), quote!(#(#calls)*)))
904}
905
906fn decode_one(ctx: &Ctx, f: &FieldSpec) -> Result<TokenStream, EmitError> {
907 let field = format_ident!("{}", name_conv::field_name(&f.name));
908 let cond = parse_tokens(&version_cond(f.versions, "version"))?;
909 let inner = if let Some(ncond) = nullable_split_cond(f) {
910 let nb = parse_tokens(&decode_borrow_call(&f.field_type, true, ctx.res_map))?;
911 let nnb = parse_tokens(&decode_borrow_call(&f.field_type, false, ctx.res_map))?;
912 let nc = parse_tokens(&ncond)?;
913 non_flex_wrap(f, quote!(if #nc { #nb } else { Some(#nnb) }))
914 } else {
915 non_flex_wrap(
916 f,
917 parse_tokens(&decode_borrow_call(
918 &f.field_type,
919 is_nullable(f),
920 ctx.res_map,
921 ))?,
922 )
923 };
924 Ok(quote!(if #cond { out.#field = #inner; }))
925}
926
927fn decode_tagged_block(
928 ctx: &Ctx,
929 fields: &[FieldSpec],
930 flex_minimum: i16,
931) -> Result<TokenStream, EmitError> {
932 if !fields.iter().any(is_tagged) {
933 return Ok(quote! {
934 if flex {
935 out.unknown_tagged_fields = read_tagged_fields(buf, |_tag, _payload| { Ok(false) })?;
936 }
937 });
938 }
939 let slots = fields.iter().filter(|f| is_tagged(f)).map(|f| {
940 let slot = format_ident!("tag_{}", name_conv::field_name(&f.name));
941 quote!(let mut #slot = None;)
942 });
943 let arms = fields
944 .iter()
945 .filter(|f| is_tagged(f))
946 .map(|f| {
947 let slot = format_ident!("tag_{}", name_conv::field_name(&f.name));
948 let nullable = is_nullable(f) || default_is_null(f);
949 let call = if tagged_field_needs_owned(f, ctx.res_map) {
950 decode_owned_call(&f.field_type, nullable, ctx.parent_module, ctx.res_map)
951 .replace("buf", "b")
952 } else {
953 decode_borrow_call(&f.field_type, nullable, ctx.res_map).replace("buf", "b")
954 };
955 let call = parse_tokens(&call)?;
956 let read = quote!(#slot = Some({ let b: &mut &[u8] = payload; #call }); Ok(true));
957 tagged_decode_arm(f, flex_minimum, &read)
958 })
959 .collect::<Result<Vec<_>, EmitError>>()?;
960 let writebacks = fields.iter().filter(|f| is_tagged(f)).map(|f| {
961 let field = format_ident!("{}", name_conv::field_name(&f.name));
962 let slot = format_ident!("tag_{}", name_conv::field_name(&f.name));
963 quote!(if let Some(v) = #slot { out.#field = v; })
964 });
965 Ok(quote! {
966 if flex {
967 #(#slots)*
968 out.unknown_tagged_fields = read_tagged_fields(buf, |tag, payload| {
969 match tag {
970 #(#arms)*
971 _ => Ok(false),
972 }
973 })?;
974 #(#writebacks)*
975 }
976 })
977}
978
979fn populated_impl(
982 ctx: &Ctx,
983 impl_target: &TokenStream,
984 fields: &[FieldSpec],
985) -> Result<TokenStream, EmitError> {
986 let assigns = ordered(fields)
987 .filter(|f| base_type(&f.field_type) != "records")
988 .map(|f| {
989 let field = format_ident!("{}", name_conv::field_name(&f.name));
990 let option = is_nullable(f) || (is_tagged(f) && default_is_null(f));
991 let value = parse_tokens(&borrowed_populated_value(
992 f,
993 ctx.res_map,
994 ctx.parent_module,
995 option,
996 )?)?;
997 let cond = parse_tokens(&version_cond(f.versions, "version"))?;
998 Ok(quote!(if #cond { m.#field = #value; }))
999 })
1000 .collect::<Result<Vec<_>, EmitError>>()?;
1001 if assigns.is_empty() {
1002 return Ok(quote! {
1003 #[cfg(test)]
1004 #impl_target {
1005 #[must_use]
1006 pub fn populated(_version: i16) -> Self {
1007 Self::default()
1008 }
1009 }
1010 });
1011 }
1012 let mut_kw = quote!(mut);
1013 let version = if assigns
1014 .iter()
1015 .any(|assign| assign.to_string().contains("version"))
1016 {
1017 quote!(version)
1018 } else {
1019 quote!(_version)
1020 };
1021 Ok(quote! {
1022 #[cfg(test)]
1023 #impl_target {
1024 #[must_use]
1025 pub fn populated(#version: i16) -> Self {
1026 let #mut_kw m = Self::default();
1027 #(#assigns)*
1028 m
1029 }
1030 }
1031 })
1032}
1033
1034fn nested_structs(
1035 ctx: &Ctx,
1036 fields: &[FieldSpec],
1037 flex_min_val: i16,
1038) -> Result<TokenStream, EmitError> {
1039 let mut out = TokenStream::new();
1040 for f in fields.iter().filter(|f| !f.fields.is_empty()) {
1041 let name = base_type(&f.field_type);
1042 out.extend(struct_block(
1043 ctx,
1044 name,
1045 &f.fields,
1046 needs_lifetime(&f.fields, ctx.res_map),
1047 &FlexSource::Nested(flex_min_val),
1048 None,
1049 (false, flex_min_val < i16::MAX),
1050 )?);
1051 out.extend(nested_structs(ctx, &f.fields, flex_min_val)?);
1052 }
1053 Ok(out)
1054}
1055
1056#[cfg(test)]
1057mod tests {
1058 use std::slice;
1059
1060 use assert2::assert;
1061
1062 use super::*;
1063 use crate::resolve::StructKind;
1064
1065 #[test]
1075 fn nullable_struct_field_with_no_default_routes_through_borrowed_default_expr() {
1076 let field: FieldSpec = serde_json::from_value(serde_json::json!({
1077 "name": "Owner", "type": "OwnerStruct", "versions": "1+",
1078 "nullableVersions": "1+"
1079 }))
1080 .unwrap();
1081 let mut res_map = ResMap::new();
1082 res_map.insert(
1083 "OwnerStruct".to_owned(),
1084 Resolution {
1085 kind: StructKind::Nested,
1086 rust_path: "OwnerStruct".to_owned(),
1087 needs_lifetime: false,
1088 },
1089 );
1090 let fallible = HashSet::new();
1091 let ctx = Ctx {
1092 res_map: &res_map,
1093 parent_module: "test",
1094 owned_root: "crate::owned",
1095 fallible: &fallible,
1096 };
1097
1098 let (_, default_assigns, _, _) =
1099 struct_members(&ctx, "Test", slice::from_ref(&field), true).unwrap();
1100
1101 let expected = quote!(owner: Some(OwnerStruct::default()),);
1102 assert!(default_assigns.to_string() == expected.to_string());
1103 }
1104}