1use std::collections::HashMap;
20
21use proc_macro2::{Ident, Literal, TokenStream};
22use quote::{format_ident, quote};
23
24use crate::{
25 emit::{
26 EmittedMessage,
27 common::{banner, parse_tokens, statement, tag_literal},
28 default_json,
29 owned::{
30 self, EmitError, base_type, decode_call, decode_call_with_buf, emit_common_imports,
31 encode_call, encode_call_option_as_non_nullable, encode_call_with_buf,
32 encoded_len_expr, field_forces_non_flex, flex_min, has_any_flex, has_float64_recursive,
33 is_nullable, is_tagged, needs_manual_default, nullable_split_cond, owned_default_expr,
34 owned_populated_value, struct_path_for, tagged_is_default_cond, tagged_version_cond,
35 version_cond, wrap_non_nullable_for_option,
36 },
37 protocol_request, tagged_fixture,
38 },
39 ir::{FieldSpec, MessageSpec, MessageType},
40 name_conv,
41 resolve::{self, Resolution, StructKind},
42 type_map,
43};
44
45type ResMap = HashMap<String, Resolution>;
46
47fn tagged_should_encode(f: &FieldSpec) -> Result<TokenStream, EmitError> {
48 if base_type(&f.field_type) == "bool" {
49 let field = format_ident!("{}", name_conv::field_name(&f.name));
50 return Ok(match &f.default {
51 Some(serde_json::Value::Bool(true)) => {
52 quote!(!self.#field)
53 }
54 Some(serde_json::Value::String(value)) if value == "true" => {
55 quote!(!self.#field)
56 }
57 Some(serde_json::Value::Bool(false)) => {
58 quote!(self.#field)
59 }
60 Some(serde_json::Value::String(value)) if value == "false" => quote!(self.#field),
61 _ => tagged_should_encode_from_default(f)?,
62 });
63 }
64 tagged_should_encode_from_default(f)
65}
66
67fn gated_tagged_should_encode(f: &FieldSpec, flex_minimum: i16) -> Result<TokenStream, EmitError> {
71 let value = tagged_should_encode(f)?;
72 Ok(match tagged_version_cond(f, flex_minimum) {
73 Some(cond) => {
76 let cond = parse_tokens(&cond)?;
77 quote!(#cond && #value)
78 }
79 None => value,
80 })
81}
82
83pub(crate) fn tagged_decode_arm(
93 f: &FieldSpec,
94 flex_minimum: i16,
95 read: &TokenStream,
96) -> Result<TokenStream, EmitError> {
97 let tag = tag_literal(f)?;
98 let Some(cond) = tagged_version_cond(f, flex_minimum) else {
99 return Ok(quote!(#tag => { #read }));
100 };
101 let cond = parse_tokens(&cond)?;
102 Ok(quote! {
103 #tag if #cond => { #read }
104 #tag => Err(ProtocolError::TagNotValidForVersion { tag: #tag, version }),
105 })
106}
107
108fn tagged_should_encode_from_default(f: &FieldSpec) -> Result<TokenStream, EmitError> {
109 let default = tagged_is_default_cond(f);
110 if let Some(option) = default.strip_suffix(".is_none()") {
111 return parse_tokens(&format!("{option}.is_some()"));
112 }
113 if let Some((value, expected)) = default.split_once(" == ") {
114 return parse_tokens(&format!("{value} != {expected}"));
115 }
116 if let Some(positive) = default.strip_prefix('!') {
117 return parse_tokens(positive);
118 }
119 let default = parse_tokens(&default)?;
120 Ok(quote!(!(#default)))
121}
122
123enum FlexSource {
127 TopLevel(i16),
128 Nested(i16),
129}
130
131impl FlexSource {
132 fn minimum(&self) -> i16 {
135 match self {
136 Self::TopLevel(fm) | Self::Nested(fm) => *fm,
137 }
138 }
139
140 fn tokens(&self) -> TokenStream {
141 match self {
142 Self::TopLevel(_) => quote!(is_flexible(version)),
143 Self::Nested(i16::MIN) => quote!(true),
144 Self::Nested(i16::MAX) => quote!(version == i16::MAX),
145 Self::Nested(fm) => {
146 let n = Literal::i16_unsuffixed(*fm);
147 quote!(version >= #n)
148 }
149 }
150 }
151}
152
153pub fn emit(spec: &MessageSpec, schemas_version: &str) -> Result<EmittedMessage, EmitError> {
156 let res_map = resolve::resolve_message(spec)?;
158 let lenient = matches!(spec.message_type, MessageType::Response);
159
160 let version_err = version_err(spec);
162 let mut structural = struct_block(
163 &name_conv::type_name(&spec.name),
164 &spec.fields,
165 &res_map,
166 &FlexSource::TopLevel(flex_min(spec)),
167 Some(version_err),
168 has_any_flex(spec),
169 lenient,
170 )?;
171 structural.extend(nested_structs(
172 &spec.fields,
173 flex_min(spec),
174 &res_map,
175 lenient,
176 )?);
177
178 let imports = owned::emit_imports(spec).to_string();
180 let constants = owned::emit_constants(spec)?.to_string();
181 let banner = format!(
182 "// AUTO-GENERATED by krabka-protocol-codegen against {schemas_version}. Do not edit.\n\n"
183 );
184 let dj = default_json::emit_default_json(spec)?;
185 let tf = tagged_fixture::emit_tagged_fixture(spec, &res_map)?;
186 let pr = protocol_request::emit_protocol_request(spec)?.unwrap_or_default();
187
188 let primary = format!("{banner}{imports}{constants}\n{structural}\n{dj}{tf}{pr}");
189
190 syn::parse_str::<syn::File>(&primary).map_err(|e| {
193 EmitError::Unsupported(format!(
194 "owned_quote produced invalid Rust for {}: {e}",
195 spec.name
196 ))
197 })?;
198
199 let commons = emit_commons(spec, &res_map, schemas_version, lenient)?;
200
201 Ok(EmittedMessage { primary, commons })
202}
203
204fn emit_commons(
212 spec: &MessageSpec,
213 res_map: &ResMap,
214 schemas_version: &str,
215 lenient: bool,
216) -> Result<Vec<(String, String)>, EmitError> {
217 let message_snake = name_conv::module_name(&spec.name);
218 let fm = flex_min(spec);
219 let mut commons: Vec<(String, String)> = Vec::new();
220 for cs in &spec.common_structs {
221 let common_res_map: ResMap = res_map
226 .iter()
227 .map(|(k, v)| {
228 let new_path = if v.kind == StructKind::Common {
229 let snake = name_conv::module_name(k);
230 format!("super::{snake}::{k}")
231 } else {
232 v.rust_path.clone()
233 };
234 (
235 k.clone(),
236 Resolution {
237 kind: v.kind.clone(),
238 rust_path: new_path,
239 needs_lifetime: v.needs_lifetime,
240 },
241 )
242 })
243 .collect();
244
245 let name = name_conv::type_name(&cs.name);
246 let struct_tokens = struct_block(
247 &name,
248 &cs.fields,
249 &common_res_map,
250 &FlexSource::Nested(fm),
251 None,
252 fm < i16::MAX,
253 lenient,
254 )?;
255 let nested = nested_structs(&cs.fields, fm, &common_res_map, lenient)?;
256
257 let body = format!(
258 "{}{}{}{}",
259 banner(schemas_version),
260 emit_common_imports(&cs.fields, fm),
261 struct_tokens,
262 nested,
263 );
264
265 let cs_snake = name_conv::module_name(&cs.name);
266 commons.push((format!("{message_snake}/{cs_snake}"), body));
267 }
268 Ok(commons)
269}
270
271fn version_err(spec: &MessageSpec) -> TokenStream {
272 match spec.message_type {
273 MessageType::Request | MessageType::Response => {
274 quote!(return Err(ProtocolError::UnsupportedVersion { api_key: API_KEY, version });)
275 }
276 MessageType::Header | MessageType::Data => {
277 let msg = format!("{} version out of range", spec.name);
278 quote!(return Err(ProtocolError::SchemaMismatch(#msg));)
279 }
280 }
281}
282
283fn struct_block(
288 name: &str,
289 fields: &[FieldSpec],
290 res_map: &ResMap,
291 flex: &FlexSource,
292 guard: Option<TokenStream>,
293 has_flex: bool,
294 lenient: bool,
295) -> Result<TokenStream, EmitError> {
296 let ty = format_ident!("{name}");
297 let eq = if has_float64_recursive(fields) {
298 quote!()
299 } else {
300 quote!(, Eq)
301 };
302 let manual_default = needs_manual_default(fields);
303 let default_derive = if manual_default {
304 quote!()
305 } else {
306 quote!(, Default)
307 };
308
309 let field_defs = ordered(fields)
310 .map(|f| {
311 let name = format_ident!("{}", name_conv::field_name(&f.name));
312 let nullable = is_nullable(f) || (is_tagged(f) && default_is_null(f));
313 let ty = parse_tokens(&type_map::owned_type(
314 &f.field_type,
315 nullable,
316 struct_path_for(f, res_map).as_deref(),
317 ))?;
318 Ok(quote!(pub #name: #ty,))
319 })
320 .collect::<Result<Vec<_>, EmitError>>()?;
321
322 let default_impl = if manual_default {
323 let assigns = ordered(fields)
324 .map(|f| {
325 let name = format_ident!("{}", name_conv::field_name(&f.name));
326 let expr = parse_tokens(&owned_default_expr(f, res_map))?;
327 Ok(quote!(#name: #expr,))
328 })
329 .collect::<Result<Vec<_>, EmitError>>()?;
330 Some(quote! {
331 impl Default for #ty {
332 fn default() -> Self {
333 Self {
334 #(#assigns)*
335 unknown_tagged_fields: UnknownTaggedFields::default(),
336 }
337 }
338 }
339 })
340 } else {
341 None
342 };
343
344 let flex_src = flex.tokens();
345 let guard = guard.map(|err| {
346 quote! {
347 if !(MIN_VERSION..=MAX_VERSION).contains(&version) { #err }
348 }
349 });
350
351 let flex_minimum = flex.minimum();
352 let encode_body = encode_body(fields, has_flex, flex_minimum)?;
353 let len_body = len_body(fields, has_flex, flex_minimum)?;
354 let decode_body = decode_body(fields, res_map, has_flex, flex_minimum, lenient)?;
355 let (codec_helpers, encode_body, decode_body) = if fields.len() >= 8 {
356 let (encode_helpers, encode_calls) = split_encode_body(fields, has_flex, flex_minimum)?;
357 let (decode_helpers, decode_calls) =
358 split_decode_body(fields, res_map, has_flex, flex_minimum, lenient)?;
359 (
360 quote!(impl #ty { #encode_helpers #decode_helpers }),
361 encode_calls,
362 decode_calls,
363 )
364 } else {
365 (quote!(), encode_body, decode_body)
366 };
367 let encode_flex = flex_binding(&encode_body, &flex_src);
368 let len_flex = flex_binding(&len_body, &flex_src);
369 let decode_flex = flex_binding(&decode_body, &flex_src);
370 let populated = populated_impl(&ty, fields, res_map)?;
371
372 Ok(quote! {
373 #[derive(Debug, Clone, PartialEq #eq #default_derive)]
374 pub struct #ty {
375 #(#field_defs)*
376 pub unknown_tagged_fields: UnknownTaggedFields,
377 }
378
379 #default_impl
380
381 #codec_helpers
382
383 impl Encode for #ty {
384 fn encode<B: BufMut>(&self, buf: &mut B, version: i16) -> Result<(), ProtocolError> {
385 #guard
386 #encode_flex
387 #encode_body
388 Ok(())
389 }
390 fn encoded_len(&self, version: i16) -> usize {
391 #len_flex
392 let mut n: usize = 0;
393 #len_body
394 n
395 }
396 }
397
398 impl Decode<'_> for #ty {
399 fn decode<B: Buf>(buf: &mut B, version: i16) -> Result<Self, ProtocolError> {
400 #guard
401 #decode_flex
402 let mut out = Self::default();
403 #decode_body
404 Ok(out)
405 }
406 }
407
408 #populated
409 })
410}
411
412fn flex_binding(body: &TokenStream, flex_source: &TokenStream) -> Option<TokenStream> {
413 body.to_string()
414 .contains("flex")
415 .then(|| quote!(let flex = #flex_source;))
416}
417
418fn ordered(fields: &[FieldSpec]) -> impl Iterator<Item = &FieldSpec> {
421 fields
422 .iter()
423 .filter(|f| !is_tagged(f))
424 .chain(fields.iter().filter(|f| is_tagged(f)))
425}
426
427fn default_is_null(f: &FieldSpec) -> bool {
428 matches!(&f.default, Some(serde_json::Value::Null))
429}
430
431fn encode_body(
434 fields: &[FieldSpec],
435 has_flex: bool,
436 flex_minimum: i16,
437) -> Result<TokenStream, EmitError> {
438 let stmts = fields
439 .iter()
440 .filter(|f| !is_tagged(f))
441 .map(encode_one)
442 .collect::<Result<Vec<_>, EmitError>>()?;
443 let trailer = if has_flex {
444 Some(encode_tagged_trailer(fields, flex_minimum)?)
445 } else {
446 None
447 };
448 Ok(quote!(#(#stmts)* #trailer))
449}
450
451fn encode_tagged_trailer(
453 fields: &[FieldSpec],
454 flex_minimum: i16,
455) -> Result<TokenStream, EmitError> {
456 let has_tagged = fields.iter().any(is_tagged);
457 let mut_kw = if has_tagged { quote!(mut) } else { quote!() };
458 let adds = fields
459 .iter()
460 .filter(|f| is_tagged(f))
461 .map(|f| encode_tagged(f, flex_minimum))
462 .collect::<Result<Vec<_>, EmitError>>()?;
463 Ok(quote! {
464 if flex {
465 let #mut_kw tagged = WriteTaggedFields::new();
466 #(#adds)*
467 tagged.write(buf, &self.unknown_tagged_fields)?;
468 }
469 })
470}
471
472fn split_encode_body(
473 fields: &[FieldSpec],
474 has_flex: bool,
475 flex_minimum: i16,
476) -> Result<(TokenStream, TokenStream), EmitError> {
477 let mut helpers = Vec::new();
478 let mut calls = Vec::new();
479 for (index, field) in fields.iter().filter(|field| !is_tagged(field)).enumerate() {
480 let helper = format_ident!("encode_field_{index}");
481 let body = encode_one(field)?;
482 let flex = if body.to_string().contains("flex") {
483 quote!(flex)
484 } else {
485 quote!(_flex)
486 };
487 if body.to_string().contains('?') {
488 helpers.push(quote! {
489 fn #helper<B: BufMut>(
490 &self,
491 buf: &mut B,
492 version: i16,
493 #flex: bool,
494 ) -> Result<(), ProtocolError> {
495 #body
496 Ok(())
497 }
498 });
499 calls.push(quote!(self.#helper(buf, version, flex)?;));
500 } else {
501 helpers.push(quote! {
502 fn #helper<B: BufMut>(&self, buf: &mut B, version: i16, #flex: bool) {
503 #body
504 }
505 });
506 calls.push(quote!(self.#helper(buf, version, flex);));
507 }
508 }
509 if has_flex {
510 let helper = format_ident!("encode_tagged_fields");
511 let body = encode_tagged_trailer(fields, flex_minimum)?;
512 let version = if body.to_string().contains("version") {
513 quote!(version)
514 } else {
515 quote!(_version)
516 };
517 helpers.push(quote! {
518 fn #helper<B: BufMut>(
519 &self,
520 buf: &mut B,
521 #version: i16,
522 flex: bool,
523 ) -> Result<(), ProtocolError> {
524 #body
525 Ok(())
526 }
527 });
528 calls.push(quote!(self.#helper(buf, version, flex)?;));
529 }
530 Ok((quote!(#(#helpers)*), quote!(#(#calls)*)))
531}
532
533fn encode_one(f: &FieldSpec) -> Result<TokenStream, EmitError> {
534 let field = name_conv::field_name(&f.name);
535 let cond = parse_tokens(&version_cond(f.versions, "version"))?;
536 let expr = format!("self.{field}");
537 let inner = if let Some(ncond) = nullable_split_cond(f) {
538 let nb = parse_tokens(&encode_call(&f.field_type, &expr, true))?;
539 let nnb = parse_tokens(&encode_call_option_as_non_nullable(&f.field_type, &expr))?;
540 let nc = parse_tokens(&ncond)?;
541 non_flex_wrap(f, quote!(if #nc { #nb } else { #nnb }))
542 } else {
543 let b = parse_tokens(&encode_call(&f.field_type, &expr, is_nullable(f)))?;
544 non_flex_wrap(f, b)
545 };
546 let inner = statement(&inner);
547 Ok(quote!(if #cond { #inner }))
548}
549
550fn non_flex_wrap(f: &FieldSpec, body: TokenStream) -> TokenStream {
553 if field_forces_non_flex(f) {
554 quote!({ let flex = false; #body })
555 } else {
556 body
557 }
558}
559
560fn encode_tagged(f: &FieldSpec, flex_minimum: i16) -> Result<TokenStream, EmitError> {
561 let field = name_conv::field_name(&f.name);
562 let tag = tag_literal(f)?;
563 let nullable = is_nullable(f) || default_is_null(f);
564 let expr = format!("self.{field}");
565 let body = statement(&parse_tokens(&encode_call_with_buf(
566 &f.field_type,
567 &expr,
568 nullable,
569 "b",
570 ))?);
571 let len = parse_tokens(&encoded_len_expr(&f.field_type, &expr, nullable))?;
572 let should_encode = gated_tagged_should_encode(f, flex_minimum)?;
573 Ok(quote! {
574 if #should_encode {
575 let payload = encode_to_bytes(#len, |b| { #body Ok(()) })?;
576 tagged.add(#tag, payload);
577 }
578 })
579}
580
581fn len_body(
584 fields: &[FieldSpec],
585 has_flex: bool,
586 flex_minimum: i16,
587) -> Result<TokenStream, EmitError> {
588 let stmts = fields
589 .iter()
590 .filter(|f| !is_tagged(f))
591 .map(len_one)
592 .collect::<Result<Vec<_>, EmitError>>()?;
593 let trailer = if has_flex {
594 let has_tagged = fields.iter().any(is_tagged);
595 let mut_kw = if has_tagged { quote!(mut) } else { quote!() };
596 let pushes = fields
597 .iter()
598 .filter(|f| is_tagged(f))
599 .map(|f| len_tagged(f, flex_minimum))
600 .collect::<Result<Vec<_>, EmitError>>()?;
601 Some(quote! {
602 if flex {
603 let #mut_kw known_pairs: Vec<(u32, usize)> = Vec::new();
604 #(#pushes)*
605 n += tagged_fields_len(&known_pairs, &self.unknown_tagged_fields);
606 }
607 })
608 } else {
609 None
610 };
611 Ok(quote!(#(#stmts)* #trailer))
612}
613
614fn len_one(f: &FieldSpec) -> Result<TokenStream, EmitError> {
615 let field = name_conv::field_name(&f.name);
616 let cond = parse_tokens(&version_cond(f.versions, "version"))?;
617 let expr = format!("self.{field}");
618 let inner = if let Some(ncond) = nullable_split_cond(f) {
619 let nb = parse_tokens(&encoded_len_expr(&f.field_type, &expr, true))?;
620 let nnb = parse_tokens(&owned::encoded_len_expr_option_as_non_nullable(
621 &f.field_type,
622 &expr,
623 ))?;
624 let nc = parse_tokens(&ncond)?;
625 non_flex_wrap(f, quote!(if #nc { #nb } else { #nnb }))
626 } else {
627 let b = parse_tokens(&encoded_len_expr(&f.field_type, &expr, is_nullable(f)))?;
628 non_flex_wrap(f, b)
629 };
630 Ok(quote!(if #cond { n += #inner; }))
631}
632
633fn len_tagged(f: &FieldSpec, flex_minimum: i16) -> Result<TokenStream, EmitError> {
634 let field = name_conv::field_name(&f.name);
635 let tag = tag_literal(f)?;
636 let nullable = is_nullable(f) || default_is_null(f);
637 let len = parse_tokens(&encoded_len_expr(
638 &f.field_type,
639 &format!("self.{field}"),
640 nullable,
641 ))?;
642 let should_encode = gated_tagged_should_encode(f, flex_minimum)?;
643 Ok(quote! {
644 if #should_encode {
645 known_pairs.push((#tag, #len));
646 }
647 })
648}
649
650fn decode_body(
653 fields: &[FieldSpec],
654 res_map: &ResMap,
655 has_flex: bool,
656 flex_minimum: i16,
657 lenient: bool,
658) -> Result<TokenStream, EmitError> {
659 let stmts = fields
660 .iter()
661 .filter(|f| !is_tagged(f))
662 .map(|f| decode_one(f, res_map, lenient))
663 .collect::<Result<Vec<_>, EmitError>>()?;
664 let trailer = if has_flex {
665 Some(decode_tagged_block(fields, res_map, flex_minimum, lenient)?)
666 } else {
667 None
668 };
669 Ok(quote!(#(#stmts)* #trailer))
670}
671
672fn split_decode_body(
673 fields: &[FieldSpec],
674 res_map: &ResMap,
675 has_flex: bool,
676 flex_minimum: i16,
677 lenient: bool,
678) -> Result<(TokenStream, TokenStream), EmitError> {
679 let mut helpers = Vec::new();
680 let mut calls = Vec::new();
681 for (index, field) in fields.iter().filter(|field| !is_tagged(field)).enumerate() {
682 let helper = format_ident!("decode_field_{index}");
683 let body = decode_one(field, res_map, lenient)?;
684 let flex = if body.to_string().contains("flex") {
685 quote!(flex)
686 } else {
687 quote!(_flex)
688 };
689 helpers.push(quote! {
690 fn #helper<B: Buf>(
691 out: &mut Self,
692 buf: &mut B,
693 version: i16,
694 #flex: bool,
695 ) -> Result<(), ProtocolError> {
696 #body
697 Ok(())
698 }
699 });
700 calls.push(quote!(Self::#helper(&mut out, buf, version, flex)?;));
701 }
702 if has_flex {
703 let helper = format_ident!("decode_tagged_fields");
704 let body = decode_tagged_block(fields, res_map, flex_minimum, lenient)?;
705 let version = if body.to_string().contains("version") {
706 quote!(version)
707 } else {
708 quote!(_version)
709 };
710 helpers.push(quote! {
711 fn #helper<B: Buf>(
712 out: &mut Self,
713 buf: &mut B,
714 #version: i16,
715 flex: bool,
716 ) -> Result<(), ProtocolError> {
717 #body
718 Ok(())
719 }
720 });
721 calls.push(quote!(Self::#helper(&mut out, buf, version, flex)?;));
722 }
723 Ok((quote!(#(#helpers)*), quote!(#(#calls)*)))
724}
725
726fn decode_one(f: &FieldSpec, res_map: &ResMap, lenient: bool) -> Result<TokenStream, EmitError> {
727 let field = format_ident!("{}", name_conv::field_name(&f.name));
728 let cond = parse_tokens(&version_cond(f.versions, "version"))?;
729 let inner = if let Some(ncond) = nullable_split_cond(f) {
730 let nb = parse_tokens(&decode_call(&f.field_type, true, res_map, lenient))?;
731 let nnb_call = decode_call(&f.field_type, false, res_map, lenient);
732 let nnb = parse_tokens(&wrap_non_nullable_for_option(
733 &f.field_type,
734 &nnb_call,
735 res_map,
736 ))?;
737 let nc = parse_tokens(&ncond)?;
738 non_flex_wrap(f, quote!(if #nc { #nb } else { #nnb }))
739 } else {
740 let b = parse_tokens(&decode_call(
741 &f.field_type,
742 is_nullable(f),
743 res_map,
744 lenient,
745 ))?;
746 non_flex_wrap(f, b)
747 };
748 Ok(quote!(if #cond { out.#field = #inner; }))
749}
750
751fn decode_tagged_block(
752 fields: &[FieldSpec],
753 res_map: &ResMap,
754 flex_minimum: i16,
755 lenient: bool,
756) -> Result<TokenStream, EmitError> {
757 let has_tagged = fields.iter().any(is_tagged);
758 if !has_tagged {
759 return Ok(quote! {
760 if flex {
761 out.unknown_tagged_fields = read_tagged_fields(buf, |_tag, _payload| { Ok(false) })?;
762 }
763 });
764 }
765 let slots = fields.iter().filter(|f| is_tagged(f)).map(|f| {
766 let slot = format_ident!("tag_{}", name_conv::field_name(&f.name));
767 quote!(let mut #slot = None;)
768 });
769 let arms = fields
770 .iter()
771 .filter(|f| is_tagged(f))
772 .map(|f| {
773 let slot = format_ident!("tag_{}", name_conv::field_name(&f.name));
774 let nullable = is_nullable(f) || default_is_null(f);
775 let call = parse_tokens(&decode_call_with_buf(
776 &f.field_type,
777 nullable,
778 res_map,
779 "b",
780 lenient,
781 ))?;
782 let read = quote!(#slot = Some({ let b: &mut &[u8] = payload; #call }); Ok(true));
783 tagged_decode_arm(f, flex_minimum, &read)
784 })
785 .collect::<Result<Vec<_>, EmitError>>()?;
786 let writebacks = fields.iter().filter(|f| is_tagged(f)).map(|f| {
787 let field = format_ident!("{}", name_conv::field_name(&f.name));
788 let slot = format_ident!("tag_{}", name_conv::field_name(&f.name));
789 quote!(if let Some(v) = #slot { out.#field = v; })
790 });
791 Ok(quote! {
792 if flex {
793 #(#slots)*
794 out.unknown_tagged_fields = read_tagged_fields(buf, |tag, payload| {
795 match tag {
796 #(#arms)*
797 _ => Ok(false),
798 }
799 })?;
800 #(#writebacks)*
801 }
802 })
803}
804
805fn populated_impl(
808 ty: &Ident,
809 fields: &[FieldSpec],
810 res_map: &ResMap,
811) -> Result<TokenStream, EmitError> {
812 let assigns = ordered(fields)
813 .filter(|f| base_type(&f.field_type) != "records")
814 .map(|f| {
815 let field = format_ident!("{}", name_conv::field_name(&f.name));
816 let option = is_nullable(f) || (is_tagged(f) && default_is_null(f));
817 let value = parse_tokens(&owned_populated_value(f, res_map, option)?)?;
818 let cond = parse_tokens(&version_cond(f.versions, "version"))?;
819 Ok(quote!(if #cond { m.#field = #value; }))
820 })
821 .collect::<Result<Vec<_>, EmitError>>()?;
822 if assigns.is_empty() {
823 return Ok(quote! {
824 #[cfg(test)]
825 impl #ty {
826 #[must_use]
827 pub fn populated(_version: i16) -> Self {
828 Self::default()
829 }
830 }
831 });
832 }
833 let mut_kw = quote!(mut);
834 let version = if assigns
835 .iter()
836 .any(|assign| assign.to_string().contains("version"))
837 {
838 quote!(version)
839 } else {
840 quote!(_version)
841 };
842 Ok(quote! {
843 #[cfg(test)]
844 impl #ty {
845 #[must_use]
846 pub fn populated(#version: i16) -> Self {
847 let #mut_kw m = Self::default();
848 #(#assigns)*
849 m
850 }
851 }
852 })
853}
854
855fn nested_structs(
859 fields: &[FieldSpec],
860 flex_min_val: i16,
861 res_map: &ResMap,
862 lenient: bool,
863) -> Result<TokenStream, EmitError> {
864 let mut out = TokenStream::new();
865 for f in fields.iter().filter(|f| !f.fields.is_empty()) {
866 let name = base_type(&f.field_type);
867 out.extend(struct_block(
868 name,
869 &f.fields,
870 res_map,
871 &FlexSource::Nested(flex_min_val),
872 None,
873 flex_min_val < i16::MAX,
874 lenient,
875 )?);
876 out.extend(nested_structs(&f.fields, flex_min_val, res_map, lenient)?);
877 }
878 Ok(out)
879}