1use std::collections::HashMap;
9
10use proc_macro2::TokenStream;
11use quote::{format_ident, quote};
12
13use crate::{
14 emit::{EmitError, common::format_int_literal},
15 ir::{FieldSpec, FlexibleVersions, MessageSpec, MessageType, VersionRange},
16 name_conv,
17 resolve::{Resolution, StructKind},
18};
19
20pub(crate) fn flex_min(spec: &MessageSpec) -> i16 {
23 match spec.flexible_versions {
24 FlexibleVersions::Range(r) => r.min,
25 FlexibleVersions::None => i16::MAX,
26 }
27}
28
29pub(crate) fn base_type(t: &str) -> &str {
30 t.strip_prefix("[]").unwrap_or(t)
31}
32
33pub(crate) fn is_struct_type(t: &str) -> bool {
34 t.chars().next().is_some_and(char::is_uppercase)
35}
36
37pub(crate) fn needs_lifetime(fields: &[FieldSpec], res_map: &HashMap<String, Resolution>) -> bool {
45 fields.iter().any(|f| {
46 let base = base_type(&f.field_type);
47 matches!(base, "string" | "bytes" | "records") || (is_struct_type(base) && !f.fields.is_empty() && needs_lifetime(&f.fields, res_map))
50 || (is_struct_type(base) && f.fields.is_empty()
52 && res_map.get(base).is_some_and(|r| r.needs_lifetime))
53 })
54}
55
56pub(crate) fn is_tagged(f: &FieldSpec) -> bool {
57 f.tag.is_some()
58}
59
60pub(crate) fn spec_needs_lifetime(
65 spec: &MessageSpec,
66 res_map: &HashMap<String, Resolution>,
67) -> bool {
68 let non_tagged: Vec<&FieldSpec> = spec.fields.iter().filter(|f| !is_tagged(f)).collect();
69 non_tagged.iter().any(|f| {
70 let base = base_type(&f.field_type);
71 matches!(base, "string" | "bytes" | "records")
72 || (is_struct_type(base) && !f.fields.is_empty() && needs_lifetime(&f.fields, res_map))
73 || (is_struct_type(base) && f.fields.is_empty()
75 && res_map.get(base).is_some_and(|r| r.needs_lifetime))
76 })
77}
78
79pub(crate) fn tagged_field_needs_owned(
83 f: &FieldSpec,
84 res_map: &HashMap<String, Resolution>,
85) -> bool {
86 if !is_tagged(f) {
87 return false;
88 }
89 let base = base_type(&f.field_type);
90 if !is_struct_type(base) && !matches!(base, "string" | "bytes" | "records") {
92 return false;
93 }
94 if is_struct_type(base) {
96 if f.fields.is_empty() {
97 return res_map.get(base).is_some_and(|r| r.needs_lifetime);
99 }
100 return needs_lifetime(&f.fields, res_map);
101 }
102 true
104}
105
106pub(crate) fn is_nullable(f: &FieldSpec) -> bool {
107 f.nullable_versions.is_some()
108}
109
110pub(crate) fn nullable_split_cond(f: &FieldSpec) -> Option<String> {
119 let r = f.nullable_versions?;
120 let need_lower = r.min > f.versions.min;
121 let need_upper = r.max < f.versions.max;
122 if !need_lower && !need_upper {
123 return None;
124 }
125 let mut parts = Vec::new();
126 if need_lower {
127 parts.push(if r.min == i16::MAX {
128 "version == i16::MAX".to_owned()
129 } else {
130 format!("version >= {}", r.min)
131 });
132 }
133 if need_upper {
134 parts.push(if r.max == i16::MIN {
135 "version == i16::MIN".to_owned()
136 } else {
137 format!("version <= {}", r.max)
138 });
139 }
140 Some(parts.join(" && "))
141}
142
143pub(crate) fn has_any_flex(spec: &MessageSpec) -> bool {
144 matches!(spec.flexible_versions, FlexibleVersions::Range(_))
145}
146
147fn has_tagged_fields_recursive(fields: &[FieldSpec]) -> bool {
148 fields
149 .iter()
150 .any(|f| f.tag.is_some() || has_tagged_fields_recursive(&f.fields))
151}
152
153fn has_any_tagged_in_spec(spec: &MessageSpec) -> bool {
154 has_tagged_fields_recursive(&spec.fields)
155}
156
157fn has_tagged_fields_needing_owned(
160 spec: &MessageSpec,
161 res_map: &HashMap<String, Resolution>,
162) -> bool {
163 spec.fields
164 .iter()
165 .any(|f| is_tagged(f) && tagged_field_needs_owned(f, res_map))
166}
167
168fn used_field_types_recursive(fields: &[FieldSpec]) -> Vec<String> {
169 let mut types: Vec<String> = Vec::new();
170 for f in fields {
171 let base = base_type(&f.field_type).to_owned();
172 if !types.contains(&base) {
173 types.push(base);
174 }
175 if !f.fields.is_empty() {
176 for t in used_field_types_recursive(&f.fields) {
177 if !types.contains(&t) {
178 types.push(t);
179 }
180 }
181 }
182 }
183 types
184}
185
186fn uses_fixed_type(types: &[String], t: &str) -> bool {
187 types.iter().any(|s| s == t)
188}
189
190pub(crate) fn has_float64_recursive(fields: &[FieldSpec]) -> bool {
194 fields.iter().any(|f| {
195 let base = base_type(&f.field_type);
196 base == "float64" || has_float64_recursive(&f.fields)
197 })
198}
199
200fn uses_string(types: &[String]) -> bool {
201 types.iter().any(|t| t == "string")
202}
203
204fn uses_bytes(types: &[String]) -> bool {
205 types.iter().any(|t| t.as_str() == "bytes")
206}
207
208fn uses_nullable_string_recursive(fields: &[FieldSpec]) -> bool {
209 fields.iter().any(|f| {
210 let base = base_type(&f.field_type);
211 let here = base == "string" && (f.nullable_versions.is_some() || f.tag.is_some());
212 here || uses_nullable_string_recursive(&f.fields)
213 })
214}
215
216fn uses_nullable_bytes_recursive(fields: &[FieldSpec]) -> bool {
217 fields.iter().any(|f| {
218 let base = base_type(&f.field_type);
219 let here = base == "bytes" && f.nullable_versions.is_some();
220 here || uses_nullable_bytes_recursive(&f.fields)
221 })
222}
223
224fn uses_non_nullable_bytes_recursive(fields: &[FieldSpec]) -> bool {
225 fields.iter().any(|f| {
226 let base = base_type(&f.field_type);
227 let here = base == "bytes" && needs_non_nullable_codec(f);
228 here || uses_non_nullable_bytes_recursive(&f.fields)
229 })
230}
231
232fn needs_non_nullable_codec(f: &FieldSpec) -> bool {
237 f.nullable_versions.is_none() || nullable_split_cond(f).is_some()
238}
239
240fn uses_nullable_records_recursive(fields: &[FieldSpec]) -> bool {
241 fields.iter().any(|f| {
242 let base = base_type(&f.field_type);
243 let here = base == "records" && f.nullable_versions.is_some();
244 here || uses_nullable_records_recursive(&f.fields)
245 })
246}
247
248fn uses_non_nullable_records_recursive(fields: &[FieldSpec]) -> bool {
249 fields.iter().any(|f| {
250 let base = base_type(&f.field_type);
251 let here = base == "records" && needs_non_nullable_codec(f);
252 here || uses_non_nullable_records_recursive(&f.fields)
253 })
254}
255
256fn uses_nullable_struct_recursive(fields: &[FieldSpec]) -> bool {
259 fields.iter().any(|f| {
260 let t = &f.field_type;
261 let base = base_type(t);
262 let here = is_struct_type(base) && !t.starts_with("[]") && f.nullable_versions.is_some();
263 here || uses_nullable_struct_recursive(&f.fields)
264 })
265}
266
267pub(crate) fn struct_path_for(
272 f: &FieldSpec,
273 res_map: &HashMap<String, Resolution>,
274) -> Option<String> {
275 let base = base_type(&f.field_type);
276 if is_struct_type(base) {
277 res_map.get(base).map(|r| {
278 let needs_lt = if f.fields.is_empty() {
281 r.needs_lifetime
282 } else {
283 needs_lifetime(&f.fields, res_map)
284 };
285 if needs_lt {
286 format!("{}<'a>", r.rust_path)
287 } else {
288 r.rust_path.clone()
289 }
290 })
291 } else {
292 None
293 }
294}
295
296pub(crate) fn owned_struct_path_for(
300 f: &FieldSpec,
301 parent_module: &str,
302 res_map: &HashMap<String, Resolution>,
303) -> Option<String> {
304 let base = base_type(&f.field_type);
305 if is_struct_type(base) {
306 res_map
307 .get(base)
308 .map(|_| resolved_to_owned_path(base, parent_module, res_map))
309 } else {
310 None
311 }
312}
313
314fn resolved_to_owned_path(
328 type_name: &str,
329 parent_module: &str,
330 res_map: &HashMap<String, Resolution>,
331) -> String {
332 match res_map.get(type_name) {
333 Some(r) if r.kind == StructKind::Common => {
334 if let Some(without_super) = r.rust_path.strip_prefix("super::common::") {
336 return format!("crate::owned::common::{without_super}");
339 }
340 if let Some(sibling) = r.rust_path.strip_prefix("super::") {
341 let msg_seg = parent_module
345 .strip_prefix("common::")
346 .and_then(|rest| rest.split("::").next())
347 .unwrap_or(parent_module);
348 return format!("crate::owned::common::{msg_seg}::{sibling}");
349 }
350 format!("crate::owned::{parent_module}::{type_name}")
352 }
353 _ => {
354 format!("crate::owned::{parent_module}::{type_name}")
356 }
357 }
358}
359
360pub(crate) fn version_cond(r: VersionRange, version_var: &str) -> String {
361 if r.min == i16::MIN && r.max == i16::MAX {
362 "true".to_owned()
363 } else if r.min == r.max {
364 format!("{version_var} == {}", r.min)
365 } else if r.min == i16::MIN {
366 format!("{version_var} <= {}", r.max)
367 } else if r.max == i16::MAX {
368 format!("{version_var} >= {}", r.min)
369 } else {
370 format!("({}..={}).contains(&{version_var})", r.min, r.max)
371 }
372}
373
374fn fixed_import(types: &[String], force_get_i8: bool) -> TokenStream {
381 let mut gets: Vec<&str> = Vec::new();
382 let mut puts: Vec<&str> = Vec::new();
383 if uses_fixed_type(types, "int8") {
384 gets.push("get_i8");
385 puts.push("put_i8");
386 } else if force_get_i8 {
387 gets.push("get_i8");
389 }
390 if uses_fixed_type(types, "int16") {
391 gets.push("get_i16");
392 puts.push("put_i16");
393 }
394 if uses_fixed_type(types, "uint16") {
395 gets.push("get_u16");
396 puts.push("put_u16");
397 }
398 if uses_fixed_type(types, "int32") {
399 gets.push("get_i32");
400 puts.push("put_i32");
401 }
402 if uses_fixed_type(types, "int64") {
403 gets.push("get_i64");
404 puts.push("put_i64");
405 }
406 if uses_fixed_type(types, "bool") {
407 gets.push("get_bool");
408 puts.push("put_bool");
409 }
410 if uses_fixed_type(types, "float64") {
411 gets.push("get_f64");
412 puts.push("put_f64");
413 }
414 if gets.is_empty() {
415 return quote!();
416 }
417 let mut combined: Vec<&str> = gets.into_iter().chain(puts).collect();
418 combined.sort_unstable();
419 let items: Vec<_> = combined.iter().map(|n| format_ident!("{n}")).collect();
420 quote!(use crate::primitives::fixed::{ #(#items),* };)
421}
422
423fn string_import(nullable: bool) -> TokenStream {
426 if nullable {
427 quote! {
428 use crate::primitives::string_bytes::{
429 compact_nullable_string_len, compact_string_len, nullable_string_len,
430 put_compact_nullable_string, put_compact_string, put_nullable_string, put_string,
431 string_len,
432 };
433 use crate::primitives::string_bytes_borrowed::{
434 get_compact_nullable_string_borrowed, get_compact_string_borrowed,
435 get_nullable_string_borrowed, get_string_borrowed,
436 };
437 }
438 } else {
439 quote! {
440 use crate::primitives::string_bytes::{
441 compact_string_len, put_compact_string, put_string, string_len,
442 };
443 use crate::primitives::string_bytes_borrowed::{
444 get_compact_string_borrowed, get_string_borrowed,
445 };
446 }
447 }
448}
449
450fn bytes_import(use_non_nullable: bool, use_nullable: bool) -> TokenStream {
452 let mut put_items: Vec<&str> = Vec::new();
453 let mut get_borrowed_items: Vec<&str> = Vec::new();
454 if use_non_nullable {
455 put_items.extend(["put_bytes", "put_compact_bytes"]);
456 get_borrowed_items.extend(["get_bytes_borrowed", "get_compact_bytes_borrowed"]);
457 }
458 if use_nullable {
459 put_items.extend(["put_compact_nullable_bytes", "put_nullable_bytes"]);
460 get_borrowed_items.extend([
461 "get_compact_nullable_bytes_borrowed",
462 "get_nullable_bytes_borrowed",
463 ]);
464 }
465 put_items.sort_unstable();
466 get_borrowed_items.sort_unstable();
467 let puts: Vec<_> = put_items.iter().map(|n| format_ident!("{n}")).collect();
468 let gets: Vec<_> = get_borrowed_items
469 .iter()
470 .map(|n| format_ident!("{n}"))
471 .collect();
472 quote! {
473 use crate::primitives::string_bytes::{ #(#puts),* };
474 use crate::primitives::string_bytes_borrowed::{ #(#gets),* };
475 }
476}
477
478fn tagged_import(flex: bool, tagged: bool) -> TokenStream {
481 if flex && tagged {
482 quote!(
483 use crate::tagged_fields::{
484 encode_to_bytes, read_tagged_fields, tagged_fields_len, WriteTaggedFields,
485 };
486 )
487 } else if flex {
488 quote!(
489 use crate::tagged_fields::{read_tagged_fields, tagged_fields_len, WriteTaggedFields};
490 )
491 } else {
492 quote!()
493 }
494}
495
496pub(crate) fn emit_imports(
497 spec: &MessageSpec,
498 res_map: &HashMap<String, Resolution>,
499) -> TokenStream {
500 let types = used_field_types_recursive(&spec.fields);
501 let tagged = has_any_tagged_in_spec(spec);
502 let flex = has_any_flex(spec);
503 let use_string = uses_string(&types);
504 let use_bytes = uses_bytes(&types);
505 let use_nullable_string = uses_nullable_string_recursive(&spec.fields);
506 let use_nullable_bytes = uses_nullable_bytes_recursive(&spec.fields);
507 let use_non_nullable_bytes = uses_non_nullable_bytes_recursive(&spec.fields);
508
509 let use_nullable_records = uses_nullable_records_recursive(&spec.fields);
510 let use_non_nullable_records = uses_non_nullable_records_recursive(&spec.fields);
511 let use_records = use_nullable_records || use_non_nullable_records;
512 let use_nullable_struct = uses_nullable_struct_recursive(&spec.fields);
513
514 let mut out = if use_bytes {
517 quote!(
518 use bytes::{Bytes, BufMut};
519 )
520 } else {
521 quote!(
522 use bytes::BufMut;
523 )
524 };
525
526 out.extend(fixed_import(&types, use_nullable_struct));
528
529 if use_string {
530 out.extend(string_import(use_nullable_string));
531 }
532
533 if use_bytes {
534 out.extend(bytes_import(use_non_nullable_bytes, use_nullable_bytes));
535 }
536
537 if use_records {
540 let mut put_items: Vec<&str> = Vec::new();
541 let mut get_borrowed_items: Vec<&str> = Vec::new();
542 if use_non_nullable_records {
543 put_items.extend(["put_bytes", "put_compact_bytes"]);
544 get_borrowed_items.extend(["get_bytes_borrowed", "get_compact_bytes_borrowed"]);
545 }
546 if use_nullable_records {
547 put_items.extend([
548 "put_bytes",
549 "put_compact_bytes",
550 "put_compact_nullable_bytes",
551 "put_nullable_bytes",
552 ]);
553 get_borrowed_items.extend([
554 "get_compact_nullable_bytes_borrowed",
555 "get_nullable_bytes_borrowed",
556 ]);
557 }
558 put_items.sort_unstable();
559 put_items.dedup();
560 get_borrowed_items.sort_unstable();
561 get_borrowed_items.dedup();
562 let puts: Vec<_> = put_items.iter().map(|n| format_ident!("{n}")).collect();
563 out.extend(quote!(use crate::primitives::string_bytes::{ #(#puts),* };));
564 if !get_borrowed_items.is_empty() {
565 let gets: Vec<_> = get_borrowed_items
566 .iter()
567 .map(|n| format_ident!("{n}"))
568 .collect();
569 out.extend(quote!(use crate::primitives::string_bytes_borrowed::{ #(#gets),* };));
570 }
571 }
572
573 out.extend(tagged_import(flex, tagged));
574
575 if has_tagged_fields_needing_owned(spec, res_map) {
577 out.extend(quote!(
578 use crate::{Decode, DecodeBorrow, Encode, ProtocolError, UnknownTaggedFields};
579 ));
580 } else {
581 out.extend(quote!(
582 use crate::{DecodeBorrow, Encode, ProtocolError, UnknownTaggedFields};
583 ));
584 }
585 out
586}
587
588pub(crate) fn emit_common_imports(fields: &[FieldSpec], flex_min_val: i16) -> TokenStream {
596 let types = used_field_types_recursive(fields);
597 let has_flex = flex_min_val < i16::MAX;
598 let tagged = fields.iter().any(|f| f.tag.is_some());
599 let use_string = uses_string(&types);
600 let use_bytes = uses_bytes(&types);
601 let use_nullable_struct = uses_nullable_struct_recursive(fields);
602
603 let mut out = if use_bytes {
604 quote!(
605 use bytes::{Bytes, BufMut};
606 )
607 } else {
608 quote!(
609 use bytes::BufMut;
610 )
611 };
612
613 out.extend(fixed_import(&types, use_nullable_struct));
614
615 if use_string {
616 out.extend(string_import(uses_nullable_string_recursive(fields)));
617 }
618
619 if use_bytes {
620 out.extend(bytes_import(
621 uses_non_nullable_bytes_recursive(fields),
622 uses_nullable_bytes_recursive(fields),
623 ));
624 }
625
626 out.extend(tagged_import(has_flex, tagged));
627
628 out.extend(quote!(
629 use crate::{DecodeBorrow, Encode, ProtocolError, UnknownTaggedFields};
630 ));
631 out
632}
633
634pub(crate) fn emit_constants(spec: &MessageSpec) -> Result<TokenStream, EmitError> {
637 let min_version = proc_macro2::Literal::i16_unsuffixed(spec.valid_versions.min);
638 let max_version = proc_macro2::Literal::i16_unsuffixed(spec.valid_versions.max);
639 let flex_minimum = flex_min(spec);
640 let flex = proc_macro2::Literal::i16_unsuffixed(flex_minimum);
641 let flex_check = match flex_minimum {
642 i16::MIN => quote!(true),
643 i16::MAX => quote!(version == FLEXIBLE_MIN),
644 _ => quote!(version >= FLEXIBLE_MIN),
645 };
646 let api_key_const = match spec.message_type {
648 MessageType::Request | MessageType::Response => {
649 let api_key = spec
650 .api_key
651 .map(proc_macro2::Literal::i16_unsuffixed)
652 .ok_or_else(|| EmitError::MissingApiKey(spec.name.clone()))?;
653 quote!(pub const API_KEY: i16 = #api_key;)
654 }
655 MessageType::Header | MessageType::Data => quote!(),
657 };
658 let latest_stable_const = match spec.message_type {
661 MessageType::Request => {
662 let latest_stable = proc_macro2::Literal::i16_unsuffixed(spec.latest_stable_version());
663 quote!(pub const LATEST_STABLE_VERSION: i16 = #latest_stable;)
664 }
665 MessageType::Response | MessageType::Header | MessageType::Data => quote!(),
666 };
667 Ok(quote! {
668 #api_key_const
669 pub const MIN_VERSION: i16 = #min_version;
670 pub const MAX_VERSION: i16 = #max_version;
671 #latest_stable_const
672 pub const FLEXIBLE_MIN: i16 = #flex;
673
674 #[inline]
675 #[must_use]
676 pub fn is_flexible(version: i16) -> bool { #flex_check }
677 })
678}
679
680pub(crate) fn borrowed_default_expr(
688 f: &FieldSpec,
689 res_map: &HashMap<String, Resolution>,
690) -> String {
691 let base = base_type(&f.field_type);
692 let is_array = f.field_type.starts_with("[]");
693 let nullable = is_nullable(f) || matches!(&f.default, Some(serde_json::Value::Null));
694 let default_is_null = matches!(&f.default, Some(serde_json::Value::Null))
696 || matches!(&f.default, Some(serde_json::Value::String(s)) if s == "null");
697
698 if base == "records" && nullable {
709 return "None".into();
710 }
711
712 if nullable && default_is_null {
713 return "None".into();
714 }
715
716 let inner = if is_array {
717 "Vec::new()".into()
718 } else if f.default.is_none()
719 && let Some(resolution) = res_map.get(base)
720 {
721 format!("{}::default()", resolution.rust_path)
722 } else {
723 f.default
724 .as_ref()
725 .map_or_else(|| borrowed_zero(base), |v| scalar_borrowed_default(base, v))
726 };
727
728 if nullable {
729 format!("Some({inner})")
730 } else {
731 inner
732 }
733}
734
735fn scalar_borrowed_default(base_type: &str, val: &serde_json::Value) -> String {
736 match (base_type, val) {
738 (_, serde_json::Value::String(s)) if s == "null" => "None".into(),
739 ("string", serde_json::Value::String(s)) if s.is_empty() => "\"\"".into(),
740 ("string", serde_json::Value::String(s)) => format!("\"{s}\""),
741 ("bool", serde_json::Value::Bool(b)) => b.to_string(),
742 ("bool", serde_json::Value::String(s)) => match s.as_str() {
743 "true" => "true".into(),
744 _ => "false".into(),
745 },
746 ("int8", serde_json::Value::String(s)) => format!("{}i8", s.trim()),
747 ("int16", serde_json::Value::String(s)) => format!("{}i16", s.trim()),
748 ("int32", serde_json::Value::String(s)) => format_int_literal(s.trim(), "i32"),
750 ("int64", serde_json::Value::String(s)) => format_int_literal(s.trim(), "i64"),
751 ("int8", serde_json::Value::Number(n)) => format!("{n}i8"),
752 ("int16", serde_json::Value::Number(n)) => format!("{n}i16"),
753 ("int32", serde_json::Value::Number(n)) => format_int_literal(&n.to_string(), "i32"),
754 ("int64", serde_json::Value::Number(n)) => format_int_literal(&n.to_string(), "i64"),
755 _ => borrowed_zero(base_type),
756 }
757}
758
759fn borrowed_zero(base: &str) -> String {
760 match base {
761 "string" => "\"\"".into(),
762 "bytes" => "&[]".into(),
763 "bool" => "false".into(),
764 "int8" => "0i8".into(),
765 "int16" => "0i16".into(),
766 "int32" => "0i32".into(),
767 "int64" => "0i64".into(),
768 "uint16" => "0u16".into(),
769 "uint32" => "0u32".into(),
770 "float64" => "0.0f64".into(),
771 "uuid" => "crate::primitives::uuid::Uuid::default()".into(),
772 "records" => "crate::records::RecordsPayloadBorrowed::default()".into(),
773 _ => "Default::default()".into(),
774 }
775}
776
777pub(crate) fn to_owned_field_expr(
783 schema_type: &str,
784 expr: &str,
785 nullable: bool,
786 struct_path: Option<&str>,
787 fallible: bool,
788) -> String {
789 let struct_to_owned = struct_path.map_or_else(
791 || "|it| it.to_owned()".to_owned(),
792 |path| format!("{path}::to_owned"),
793 );
794 let records_to_owned = "crate::records::RecordsPayloadBorrowed::to_owned";
795 if let Some(elem) = schema_type.strip_prefix("[]") {
796 let elem_base = base_type(elem);
797 if is_struct_type(elem_base) {
798 let collect = if fallible {
799 ".collect::<Result<_, _>>()"
800 } else {
801 ".collect()"
802 };
803 if nullable {
804 let transpose = if fallible { ".transpose()?" } else { "" };
805 return format!(
806 "({expr}).as_ref().map(|v| v.iter().map({struct_to_owned}){collect}){transpose}"
807 );
808 }
809 let question = if fallible { "?" } else { "" };
810 return format!("({expr}).iter().map({struct_to_owned}){collect}{question}");
811 }
812 return match elem {
814 "string" if nullable => {
815 format!("({expr}).as_ref().map(|v| v.iter().map(|&s| s.to_owned()).collect())")
816 }
817 "string" => format!("({expr}).iter().map(|&s| s.to_owned()).collect()"),
818 "bytes" if nullable => format!(
819 "({expr}).as_ref().map(|v| v.iter().map(|b| Bytes::copy_from_slice(b)).collect())"
820 ),
821 "bytes" => format!("({expr}).iter().map(|b| Bytes::copy_from_slice(b)).collect()"),
822 "records" if nullable => format!(
823 "({expr}).as_ref().map(|v| v.iter().map({records_to_owned}).collect::<Result<_, _>>()).transpose()?"
824 ),
825 "records" => {
826 format!("({expr}).iter().map({records_to_owned}).collect::<Result<_, _>>()?")
827 }
828 _ => format!("({expr}).clone()"),
830 };
831 }
832
833 if is_struct_type(schema_type) {
834 return match (nullable, fallible) {
835 (true, true) => format!("({expr}).as_ref().map({struct_to_owned}).transpose()?"),
836 (true, false) => format!("({expr}).as_ref().map({struct_to_owned})"),
837 (false, true) => format!("({expr}).to_owned()?"),
838 (false, false) => format!("({expr}).to_owned()"),
839 };
840 }
841
842 match (schema_type, nullable) {
843 ("string", false) => format!("({expr}).to_owned()"),
844 ("string", true) => format!("({expr}).map(str::to_owned)"),
845 ("bytes", false) => format!("Bytes::copy_from_slice({expr})"),
846 ("bytes", true) => format!("({expr}).map(Bytes::copy_from_slice)"),
847 ("records", false) => format!("({expr}).to_owned()?"),
848 ("records", true) => format!("({expr}).as_ref().map({records_to_owned}).transpose()?"),
849 _ => {
850 format!("({expr})")
852 }
853 }
854}
855
856pub(crate) fn field_forces_non_flex(f: &FieldSpec) -> bool {
860 matches!(f.flexible_versions, Some(FlexibleVersions::None))
861}
862
863pub(crate) fn encode_call_option_as_non_nullable(schema_type: &str, expr: &str) -> String {
867 if let Some(elem) = schema_type.strip_prefix("[]") {
868 let elem_base = base_type(elem);
869 if is_struct_type(elem_base) {
870 return format!(
871 "{{ let v = ({expr}).as_deref().unwrap_or(&[]); \
872 crate::primitives::array::put_array_len(buf, v.len(), flex)?; \
873 for it in v {{ it.encode(buf, version)?; }} }}"
874 );
875 }
876 return format!(
877 "{{ let v = ({expr}).as_deref().unwrap_or(&[]); \
878 crate::primitives::array::put_array_len(buf, v.len(), flex)?; \
879 for it in v {{ {inner}; }} }}",
880 inner = encode_call(elem, "it", false),
881 );
882 }
883 match schema_type {
884 "string" => format!(
885 "if flex {{ put_compact_string(buf, ({expr}).unwrap_or(\"\"))?; }} \
886 else {{ put_string(buf, ({expr}).unwrap_or(\"\"))?; }}"
887 ),
888 "uuid" => format!("crate::primitives::uuid::put_uuid(buf, ({expr}).unwrap_or_default())"),
889 "records" => format!(
892 "match &{expr} {{ \
893 None => {{ let __rb_buf = bytes::BytesMut::new(); if flex {{ put_compact_bytes(buf, &__rb_buf)?; }} else {{ put_bytes(buf, &__rb_buf)?; }} }}, \
894 Some(__rb) => {{ let mut __rb_buf = bytes::BytesMut::new(); <crate::records::RecordsPayloadBorrowed as crate::Encode>::encode(__rb, &mut __rb_buf, version)?; if flex {{ put_compact_bytes(buf, &__rb_buf)?; }} else {{ put_bytes(buf, &__rb_buf)?; }} }} \
895 }}"
896 ),
897 _ => encode_call(schema_type, &format!("({expr}).unwrap_or_default()"), false),
898 }
899}
900
901pub(crate) fn encoded_len_expr_option_as_non_nullable(schema_type: &str, expr: &str) -> String {
904 if let Some(elem) = schema_type.strip_prefix("[]") {
905 let elem_base = base_type(elem);
906 if is_struct_type(elem_base) {
907 return format!(
908 "{{ let v = ({expr}).as_deref().unwrap_or(&[]); \
909 let prefix = crate::primitives::array::array_len_prefix_len(v.len(), flex); \
910 let body: usize = v.iter().map(|it| it.encoded_len(version)).sum(); \
911 prefix + body }}"
912 );
913 }
914 return format!(
915 "{{ let v = ({expr}).as_deref().unwrap_or(&[]); \
916 let prefix = crate::primitives::array::array_len_prefix_len(v.len(), flex); \
917 let body: usize = v.iter().map(|it| {inner}).sum(); \
918 prefix + body }}",
919 inner = encoded_len_expr(elem, array_element(elem), false),
920 );
921 }
922 match schema_type {
923 "string" => format!(
924 "if flex {{ compact_string_len(({expr}).unwrap_or(\"\")) }} \
925 else {{ string_len(({expr}).unwrap_or(\"\")) }}"
926 ),
927 "uuid" => "16".into(),
928 "records" => format!(
929 "({expr}).as_ref().map_or_else( \
930 || if flex {{ crate::primitives::string_bytes::compact_bytes_len_from_size(0) }} else {{ 4 }}, \
931 |__rb| {{ let __rb_len = <crate::records::RecordsPayloadBorrowed as crate::Encode>::encoded_len(__rb, version); if flex {{ crate::primitives::string_bytes::compact_bytes_len_from_size(__rb_len) }} else {{ 4 + __rb_len }} }})"
932 ),
933 _ => encoded_len_expr(schema_type, &format!("({expr}).unwrap_or_default()"), false),
934 }
935}
936
937pub(crate) fn tagged_is_default_cond(f: &FieldSpec) -> String {
941 let field = name_conv::field_name(&f.name);
942 let base = base_type(&f.field_type);
943 let is_array = f.field_type.starts_with("[]");
944 let nullable = is_nullable(f) || matches!(&f.default, Some(serde_json::Value::Null));
945 let default_is_null = matches!(&f.default, Some(serde_json::Value::Null))
946 || matches!(&f.default, Some(serde_json::Value::String(s)) if s == "null");
947
948 if base == "records" && nullable {
951 return format!("self.{field}.is_none()");
952 }
953 if nullable && default_is_null {
954 return format!("self.{field}.is_none()");
956 }
957 if nullable && f.default.is_none() {
958 let inner = if is_array {
964 "Vec::new()".to_owned()
965 } else {
966 borrowed_zero(base)
967 };
968 return format!("self.{field} == Some({inner})");
969 }
970 if let Some(v) = &f.default {
971 let cmp_val = scalar_borrowed_default(base, v);
972 if cmp_val == "None" {
973 return format!("self.{field}.is_none()");
974 }
975 if nullable {
976 return format!("self.{field} == Some({cmp_val})");
977 }
978 if f.field_type.starts_with("[]") {
979 return format!("self.{field}.is_empty()");
980 }
981 return format!("self.{field} == {cmp_val}");
982 }
983 format!("crate::codegen_helpers::is_default(&self.{field})")
984}
985
986pub(crate) fn owned_encoded_len_expr(schema_type: &str, expr: &str, nullable: bool) -> String {
990 match (schema_type, nullable) {
991 ("string", false) => {
992 format!("if flex {{ compact_string_len(&{expr}) }} else {{ string_len(&{expr}) }}")
993 }
994 ("string", true) => format!(
995 "if flex {{ compact_nullable_string_len({expr}.as_deref()) }} else {{ nullable_string_len({expr}.as_deref()) }}"
996 ),
997 ("bytes", false) => format!(
998 "if flex {{ crate::primitives::string_bytes::compact_bytes_len(&{expr}) }} \
999 else {{ crate::primitives::string_bytes::bytes_len(&{expr}) }}"
1000 ),
1001 ("bytes", true) => format!(
1002 "if flex {{ crate::primitives::string_bytes::compact_nullable_bytes_len({expr}.as_deref()) }} \
1003 else {{ crate::primitives::string_bytes::nullable_bytes_len({expr}.as_deref()) }}"
1004 ),
1005 _ => encoded_len_expr(schema_type, expr, nullable),
1006 }
1007}
1008
1009pub(crate) fn owned_encode_call(schema_type: &str, expr: &str, nullable: bool) -> String {
1014 match (schema_type, nullable) {
1015 ("string", false) => format!(
1016 "if flex {{ put_compact_string(buf, &{expr})?; }} else {{ put_string(buf, &{expr})?; }}"
1017 ),
1018 ("string", true) => format!(
1019 "if flex {{ put_compact_nullable_string(buf, {expr}.as_deref())?; }} else {{ put_nullable_string(buf, {expr}.as_deref())?; }}"
1020 ),
1021 ("bytes", false) => format!(
1022 "if flex {{ put_compact_bytes(buf, &{expr})?; }} else {{ put_bytes(buf, &{expr})?; }}"
1023 ),
1024 ("bytes", true) => format!(
1025 "if flex {{ put_compact_nullable_bytes(buf, {expr}.as_deref())?; }} else {{ put_nullable_bytes(buf, {expr}.as_deref())?; }}"
1026 ),
1027 _ => encode_call(schema_type, expr, nullable),
1029 }
1030}
1031
1032pub(crate) fn borrowed_populated_value(
1034 f: &FieldSpec,
1035 res_map: &HashMap<String, Resolution>,
1036 parent_module: &str,
1037 option: bool,
1038) -> Result<String, EmitError> {
1039 let base = base_type(&f.field_type);
1040 let is_array = f.field_type.starts_with("[]");
1041 let owned = is_tagged(f) && tagged_field_needs_owned(f, res_map);
1042 let elem = borrowed_populated_scalar(base, f, res_map, parent_module, owned)?;
1043 let inner = if is_array {
1044 format!("vec![{elem}]")
1045 } else {
1046 elem
1047 };
1048 Ok(if option {
1049 format!("Some({inner})")
1050 } else {
1051 inner
1052 })
1053}
1054
1055fn borrowed_populated_scalar(
1056 base: &str,
1057 f: &FieldSpec,
1058 res_map: &HashMap<String, Resolution>,
1059 parent_module: &str,
1060 owned: bool,
1061) -> Result<String, EmitError> {
1062 Ok(match base {
1063 "bool" => "true".to_owned(),
1064 "int8" => "1i8".to_owned(),
1065 "int16" => "1i16".to_owned(),
1066 "int32" => "1i32".to_owned(),
1067 "int64" => "1i64".to_owned(),
1068 "uint16" => "1u16".to_owned(),
1069 "uint32" => "1u32".to_owned(),
1070 "float64" => "1.0f64".to_owned(),
1071 "uuid" => "crate::primitives::uuid::Uuid([1u8; 16])".to_owned(),
1072 "string" if owned => "\"x\".to_owned()".to_owned(),
1073 "string" => "\"x\"".to_owned(),
1074 "bytes" if owned => "::bytes::Bytes::from_static(b\"x\")".to_owned(),
1075 "bytes" => "&b\"x\"[..]".to_owned(),
1076 _ if owned => {
1077 let path = owned_struct_path_for(f, parent_module, res_map)
1078 .ok_or_else(|| EmitError::Unresolved(f.name.clone()))?;
1079 format!("{path}::populated(version)")
1080 }
1081 _ => {
1082 let path = res_map
1083 .get(base)
1084 .map(|r| r.rust_path.clone())
1085 .ok_or_else(|| EmitError::Unresolved(f.name.clone()))?;
1086 format!("{path}::populated(version)")
1087 }
1088 })
1089}
1090
1091fn array_element(elem: &str) -> &'static str {
1097 if matches!(elem, "string" | "bytes") {
1098 "it"
1099 } else {
1100 "*it"
1101 }
1102}
1103
1104fn array_element_encode(elem: &str) -> String {
1106 let call = encode_call(elem, array_element(elem), false);
1107 if call.starts_with("if ") {
1108 call
1109 } else {
1110 format!("{call};")
1111 }
1112}
1113
1114pub(crate) fn encode_call(schema_type: &str, expr: &str, nullable: bool) -> String {
1115 if let Some(elem) = schema_type.strip_prefix("[]") {
1116 let elem_base = base_type(elem);
1117 if is_struct_type(elem_base) {
1118 if nullable {
1119 return format!(
1120 "{{ let len = ({expr}).as_ref().map(Vec::len); \
1121 crate::primitives::array::put_nullable_array_len(buf, len, flex)?; \
1122 if let Some(v) = &{expr} {{ for it in v {{ it.encode(buf, version)?; }} }} }}",
1123 );
1124 }
1125 return format!(
1126 "{{ crate::primitives::array::put_array_len(buf, ({expr}).len(), flex)?; \
1127 for it in &{expr} {{ it.encode(buf, version)?; }} }}",
1128 );
1129 }
1130 if nullable {
1131 return format!(
1132 "{{ let len = ({expr}).as_ref().map(Vec::len); \
1133 crate::primitives::array::put_nullable_array_len(buf, len, flex)?; \
1134 if let Some(v) = &{expr} {{ for it in v {{ {inner} }} }} }}",
1135 inner = array_element_encode(elem),
1136 );
1137 }
1138 return format!(
1139 "{{ crate::primitives::array::put_array_len(buf, ({expr}).len(), flex)?; \
1140 for it in &{expr} {{ {inner} }} }}",
1141 inner = array_element_encode(elem),
1142 );
1143 }
1144
1145 if is_struct_type(schema_type) {
1146 if nullable {
1148 return format!(
1149 "match &{expr} {{ \
1150 None => {{ buf.put_i8(-1); }}, \
1151 Some(v) => {{ buf.put_i8(1); v.encode(buf, version)?; }} \
1152 }}"
1153 );
1154 }
1155 return format!("{expr}.encode(buf, version)?");
1156 }
1157
1158 match (schema_type, nullable) {
1160 ("int8", _) => format!("put_i8(buf, {expr})"),
1161 ("int16", _) => format!("put_i16(buf, {expr})"),
1162 ("uint16", _) => format!("put_u16(buf, {expr})"),
1163 ("int32", _) => format!("put_i32(buf, {expr})"),
1164 ("int64", _) => format!("put_i64(buf, {expr})"),
1165 ("bool", _) => format!("put_bool(buf, {expr})"),
1166 ("float64", _) => format!("put_f64(buf, {expr})"),
1167 ("uuid", _) => format!("crate::primitives::uuid::put_uuid(buf, {expr})"),
1168 ("string", false) => format!(
1169 "if flex {{ put_compact_string(buf, {expr})?; }} else {{ put_string(buf, {expr})?; }}"
1170 ),
1171 ("string", true) => format!(
1172 "if flex {{ put_compact_nullable_string(buf, {expr})?; }} else {{ put_nullable_string(buf, {expr})?; }}"
1173 ),
1174 ("bytes", false) => format!(
1175 "if flex {{ put_compact_bytes(buf, {expr})?; }} else {{ put_bytes(buf, {expr})?; }}"
1176 ),
1177 ("bytes", true) => format!(
1178 "if flex {{ put_compact_nullable_bytes(buf, {expr})?; }} else {{ put_nullable_bytes(buf, {expr})?; }}"
1179 ),
1180 ("records", false) => format!(
1181 "{{ \
1182 let mut __rb_buf = bytes::BytesMut::new(); \
1183 <crate::records::RecordsPayloadBorrowed as crate::Encode>::encode(&{expr}, &mut __rb_buf, version)?; \
1184 if flex {{ put_compact_bytes(buf, &__rb_buf)?; }} else {{ put_bytes(buf, &__rb_buf)?; }} \
1185 }}"
1186 ),
1187 ("records", true) => format!(
1188 "match &{expr} {{ \
1189 None => if flex {{ put_compact_nullable_bytes(buf, None)?; }} else {{ put_nullable_bytes(buf, None)?; }}, \
1190 Some(__rb) => {{ \
1191 let mut __rb_buf = bytes::BytesMut::new(); \
1192 <crate::records::RecordsPayloadBorrowed as crate::Encode>::encode(__rb, &mut __rb_buf, version)?; \
1193 if flex {{ put_compact_bytes(buf, &__rb_buf)?; }} else {{ put_bytes(buf, &__rb_buf)?; }} \
1194 }} \
1195 }}"
1196 ),
1197 (t, _) => format!("compile_error!(\"unhandled type in encode_call (borrowed): {t}\")"),
1198 }
1199}
1200
1201pub(crate) fn encoded_len_expr(schema_type: &str, expr: &str, nullable: bool) -> String {
1202 if let Some(elem) = schema_type.strip_prefix("[]") {
1203 let elem_base = base_type(elem);
1204 if is_struct_type(elem_base) {
1205 if nullable {
1206 return format!(
1207 "{{ let opt: Option<&Vec<_>> = ({expr}).as_ref(); \
1208 let prefix = crate::primitives::array::nullable_array_len_prefix_len(opt.map(std::vec::Vec::len), flex); \
1209 let body: usize = opt.map_or(0, |v| v.iter().map(|it| it.encoded_len(version)).sum()); \
1210 prefix + body }}",
1211 );
1212 }
1213 return format!(
1214 "{{ let prefix = crate::primitives::array::array_len_prefix_len(({expr}).len(), flex); \
1215 let body: usize = ({expr}).iter().map(|it| it.encoded_len(version)).sum(); \
1216 prefix + body }}",
1217 );
1218 }
1219 {
1220 let closure_arg = if encoded_len_expr(elem, "*it", false).contains("*it") {
1223 "it"
1224 } else {
1225 "_"
1226 };
1227 let inner = encoded_len_expr(elem, array_element(elem), false);
1228 if nullable {
1229 return format!(
1230 "{{ let opt: Option<&Vec<_>> = ({expr}).as_ref(); \
1231 let prefix = crate::primitives::array::nullable_array_len_prefix_len(opt.map(std::vec::Vec::len), flex); \
1232 let body: usize = opt.map_or(0, |v| v.iter().map(|{closure_arg}| {inner}).sum()); \
1233 prefix + body }}",
1234 );
1235 }
1236 return format!(
1237 "{{ let prefix = crate::primitives::array::array_len_prefix_len(({expr}).len(), flex); \
1238 let body: usize = ({expr}).iter().map(|{closure_arg}| {inner}).sum(); \
1239 prefix + body }}",
1240 );
1241 }
1242 }
1243
1244 if is_struct_type(schema_type) {
1245 if nullable {
1246 return format!("1 + {expr}.as_ref().map_or(0, |v| v.encoded_len(version))");
1248 }
1249 return format!("{expr}.encoded_len(version)");
1250 }
1251
1252 match (schema_type, nullable) {
1254 ("int8" | "bool", _) => "1".into(),
1255 ("int16" | "uint16", _) => "2".into(),
1256 ("int32", _) => "4".into(),
1257 ("int64" | "float64", _) => "8".into(),
1258 ("uuid", _) => "16".into(),
1259 ("string", false) => {
1260 format!("if flex {{ compact_string_len({expr}) }} else {{ string_len({expr}) }}")
1261 }
1262 ("string", true) => format!(
1263 "if flex {{ compact_nullable_string_len({expr}) }} else {{ nullable_string_len({expr}) }}"
1264 ),
1265 ("bytes", false) => format!(
1266 "if flex {{ crate::primitives::string_bytes::compact_bytes_len({expr}) }} \
1267 else {{ crate::primitives::string_bytes::bytes_len({expr}) }}"
1268 ),
1269 ("bytes", true) => format!(
1270 "if flex {{ crate::primitives::string_bytes::compact_nullable_bytes_len({expr}) }} \
1271 else {{ crate::primitives::string_bytes::nullable_bytes_len({expr}) }}"
1272 ),
1273 ("records", false) => format!(
1274 "{{ let __rb_len = <crate::records::RecordsPayloadBorrowed as crate::Encode>::encoded_len(&({expr}), version); \
1275 if flex {{ crate::primitives::string_bytes::compact_bytes_len_from_size(__rb_len) }} \
1276 else {{ 4 + __rb_len }} }}"
1277 ),
1278 ("records", true) => format!(
1279 "({expr}).as_ref().map_or_else( \
1280 || if flex {{ crate::primitives::varint::uvarint_len(0) }} else {{ 4 }}, \
1281 |__rb| {{ let __rb_len = <crate::records::RecordsPayloadBorrowed as crate::Encode>::encoded_len(__rb, version); \
1282 if flex {{ crate::primitives::string_bytes::compact_bytes_len_from_size(__rb_len) }} \
1283 else {{ 4 + __rb_len }} }})"
1284 ),
1285 (t, _) => format!("compile_error!(\"unhandled type in encoded_len_expr (borrowed): {t}\")"),
1286 }
1287}
1288
1289pub(crate) fn decode_borrow_call(
1290 schema_type: &str,
1291 nullable: bool,
1292 res_map: &HashMap<String, Resolution>,
1293) -> String {
1294 if let Some(elem) = schema_type.strip_prefix("[]") {
1295 let elem_base = base_type(elem);
1296 if is_struct_type(elem_base) {
1297 let type_path = res_map
1298 .get(elem_base)
1299 .map_or(elem_base, |r| r.rust_path.as_str());
1300 if nullable {
1301 return format!(
1302 "{{ let opt = crate::primitives::array::get_nullable_array_len(buf, flex)?; \
1303 match opt {{ None => None, Some(n) => {{ let mut v = Vec::with_capacity(n); \
1304 for _ in 0..n {{ v.push({type_path}::decode_borrow(buf, version)?); }} Some(v) }} }} }}",
1305 );
1306 }
1307 return format!(
1308 "{{ let n = crate::primitives::array::get_array_len(buf, flex)?; \
1309 let mut v = Vec::with_capacity(n); \
1310 for _ in 0..n {{ v.push({type_path}::decode_borrow(buf, version)?); }} v }}",
1311 );
1312 }
1313 if nullable {
1314 return format!(
1315 "{{ let opt = crate::primitives::array::get_nullable_array_len(buf, flex)?; \
1316 match opt {{ None => None, Some(n) => {{ let mut v = Vec::with_capacity(n); \
1317 for _ in 0..n {{ v.push({inner}); }} Some(v) }} }} }}",
1318 inner = decode_borrow_call(elem, false, res_map),
1319 );
1320 }
1321 return format!(
1322 "{{ let n = crate::primitives::array::get_array_len(buf, flex)?; \
1323 let mut v = Vec::with_capacity(n); for _ in 0..n {{ v.push({inner}); }} v }}",
1324 inner = decode_borrow_call(elem, false, res_map),
1325 );
1326 }
1327
1328 if is_struct_type(schema_type) {
1329 let type_path = res_map
1330 .get(schema_type)
1331 .map_or(schema_type, |r| r.rust_path.as_str());
1332 if nullable {
1333 return format!(
1335 "if get_i8(buf)? < 0 {{ None }} else {{ Some({type_path}::decode_borrow(buf, version)?) }}"
1336 );
1337 }
1338 return format!("{type_path}::decode_borrow(buf, version)?");
1339 }
1340
1341 match (schema_type, nullable) {
1342 ("int8", _) => "get_i8(buf)?".into(),
1343 ("int16", _) => "get_i16(buf)?".into(),
1344 ("uint16", _) => "get_u16(buf)?".into(),
1345 ("int32", _) => "get_i32(buf)?".into(),
1346 ("int64", _) => "get_i64(buf)?".into(),
1347 ("bool", _) => "get_bool(buf)?".into(),
1348 ("float64", _) => "get_f64(buf)?".into(),
1349 ("uuid", _) => "crate::primitives::uuid::get_uuid(buf)?".into(),
1350 ("string", false) => {
1351 "if flex { get_compact_string_borrowed(buf)? } else { get_string_borrowed(buf)? }".into()
1352 }
1353 ("string", true) => {
1354 "if flex { get_compact_nullable_string_borrowed(buf)? } else { get_nullable_string_borrowed(buf)? }".into()
1355 }
1356 ("bytes", false) => {
1357 "if flex { get_compact_bytes_borrowed(buf)? } else { get_bytes_borrowed(buf)? }".into()
1358 }
1359 ("bytes", true) => {
1360 "if flex { get_compact_nullable_bytes_borrowed(buf)? } else { get_nullable_bytes_borrowed(buf)? }".into()
1361 }
1362 ("records", false) => "{ \
1363 let __rb_slice = if flex { get_compact_bytes_borrowed(buf)? } else { get_bytes_borrowed(buf)? }; \
1364 let mut __rb_cur = __rb_slice; \
1365 <crate::records::RecordsPayloadBorrowed as crate::DecodeBorrow>::decode_borrow(&mut __rb_cur, version)? \
1366 }".into(),
1367 ("records", true) => "{ \
1368 let __rb_opt = if flex { get_compact_nullable_bytes_borrowed(buf)? } else { get_nullable_bytes_borrowed(buf)? }; \
1369 match __rb_opt { \
1370 None => None, \
1371 Some(__rb_slice) => { \
1372 let mut __rb_cur = __rb_slice; \
1373 Some(<crate::records::RecordsPayloadBorrowed as crate::DecodeBorrow>::decode_borrow(&mut __rb_cur, version)?) \
1374 } \
1375 } \
1376 }".into(),
1377 (t, _) => format!("compile_error!(\"unhandled type in decode_borrow_call: {t}\")"),
1378 }
1379}
1380
1381pub(crate) fn decode_owned_call(
1385 schema_type: &str,
1386 nullable: bool,
1387 parent_module: &str,
1388 res_map: &HashMap<String, Resolution>,
1389) -> String {
1390 if let Some(elem) = schema_type.strip_prefix("[]") {
1391 let elem_base = base_type(elem);
1392 if is_struct_type(elem_base) {
1393 let owned_path = resolved_to_owned_path(elem_base, parent_module, res_map);
1397 if nullable {
1398 return format!(
1399 "{{ let opt = crate::primitives::array::get_nullable_array_len(buf, flex)?; \
1400 match opt {{ None => None, Some(n) => {{ let mut v = Vec::with_capacity(n); \
1401 for _ in 0..n {{ v.push({owned_path}::decode(buf, version)?); }} Some(v) }} }} }}"
1402 );
1403 }
1404 return format!(
1405 "{{ let n = crate::primitives::array::get_array_len(buf, flex)?; \
1406 let mut v = Vec::with_capacity(n); \
1407 for _ in 0..n {{ v.push({owned_path}::decode(buf, version)?); }} v }}"
1408 );
1409 }
1410 if nullable {
1412 return format!(
1413 "{{ let opt = crate::primitives::array::get_nullable_array_len(buf, flex)?; \
1414 match opt {{ None => None, Some(n) => {{ let mut v = Vec::with_capacity(n); \
1415 for _ in 0..n {{ v.push({inner}); }} Some(v) }} }} }}",
1416 inner = decode_owned_call(elem, false, parent_module, res_map),
1417 );
1418 }
1419 return format!(
1420 "{{ let n = crate::primitives::array::get_array_len(buf, flex)?; \
1421 let mut v = Vec::with_capacity(n); for _ in 0..n {{ v.push({inner}); }} v }}",
1422 inner = decode_owned_call(elem, false, parent_module, res_map),
1423 );
1424 }
1425
1426 if is_struct_type(schema_type) {
1427 let owned_path = resolved_to_owned_path(schema_type, parent_module, res_map);
1428 if nullable {
1429 return format!("Some({owned_path}::decode(buf, version)?)");
1430 }
1431 return format!("{owned_path}::decode(buf, version)?");
1432 }
1433
1434 match (schema_type, nullable) {
1436 ("int8", _) => "get_i8(buf)?".into(),
1437 ("int16", _) => "get_i16(buf)?".into(),
1438 ("uint16", _) => "get_u16(buf)?".into(),
1439 ("int32", _) => "get_i32(buf)?".into(),
1440 ("int64", _) => "get_i64(buf)?".into(),
1441 ("bool", _) => "get_bool(buf)?".into(),
1442 ("float64", _) => "get_f64(buf)?".into(),
1443 ("uuid", _) => "crate::primitives::uuid::get_uuid(buf)?".into(),
1444 ("string", false) => {
1445 "if flex { crate::primitives::string_bytes::get_compact_string_owned(buf)? } \
1446 else { crate::primitives::string_bytes::get_string_owned(buf)? }"
1447 .into()
1448 }
1449 ("string", true) => {
1450 "if flex { crate::primitives::string_bytes::get_compact_nullable_string_owned(buf)? } \
1451 else { crate::primitives::string_bytes::get_nullable_string_owned(buf)? }"
1452 .into()
1453 }
1454 (t, _) => format!("compile_error!(\"unhandled type in decode_owned_call: {t}\")"),
1455 }
1456}
1457
1458#[cfg(test)]
1459mod tests {
1460 use assert2::assert;
1461
1462 use super::*;
1463
1464 fn tagged_nullable_no_default_array_field() -> FieldSpec {
1468 serde_json::from_value(serde_json::json!({
1469 "name": "Owners", "type": "[]string", "versions": "1+",
1470 "nullableVersions": "1+", "taggedVersions": "1+", "tag": 5
1471 }))
1472 .unwrap()
1473 }
1474
1475 #[test]
1479 fn tagged_nullable_no_default_field_defaults_to_some_empty_not_none() {
1480 let field = tagged_nullable_no_default_array_field();
1481 let res_map: HashMap<String, Resolution> = HashMap::new();
1482
1483 assert!(borrowed_default_expr(&field, &res_map) == "Some(Vec::new())");
1484 assert!(tagged_is_default_cond(&field) == "self.owners == Some(Vec::new())");
1485 }
1486
1487 fn tagged_nullable_no_default_scalar_field() -> FieldSpec {
1493 serde_json::from_value(serde_json::json!({
1494 "name": "Owner", "type": "string", "versions": "1+",
1495 "nullableVersions": "1+", "taggedVersions": "1+", "tag": 6
1496 }))
1497 .unwrap()
1498 }
1499
1500 #[test]
1501 fn tagged_nullable_no_default_scalar_field_defaults_to_some_empty_not_none() {
1502 let field = tagged_nullable_no_default_scalar_field();
1503 let res_map: HashMap<String, Resolution> = HashMap::new();
1504
1505 assert!(borrowed_default_expr(&field, &res_map) == "Some(\"\")");
1506 assert!(tagged_is_default_cond(&field) == "self.owner == Some(\"\")");
1507 }
1508
1509 #[test]
1514 fn nullable_struct_field_with_no_default_wraps_resolved_default_in_some() {
1515 let field: FieldSpec = serde_json::from_value(serde_json::json!({
1516 "name": "Owner", "type": "OwnerStruct", "versions": "1+",
1517 "nullableVersions": "1+"
1518 }))
1519 .unwrap();
1520 let mut res_map: HashMap<String, Resolution> = HashMap::new();
1521 res_map.insert(
1522 "OwnerStruct".to_owned(),
1523 Resolution {
1524 kind: StructKind::Nested,
1525 rust_path: "OwnerStruct".to_owned(),
1526 needs_lifetime: false,
1527 },
1528 );
1529
1530 assert!(borrowed_default_expr(&field, &res_map) == "Some(OwnerStruct::default())");
1531 }
1532
1533 #[test]
1538 fn nullable_records_field_always_defaults_to_none() {
1539 let field: FieldSpec = serde_json::from_value(serde_json::json!({
1540 "name": "Records", "type": "records", "versions": "0+",
1541 "nullableVersions": "0+"
1542 }))
1543 .unwrap();
1544 let res_map: HashMap<String, Resolution> = HashMap::new();
1545
1546 assert!(borrowed_default_expr(&field, &res_map) == "None");
1547 assert!(tagged_is_default_cond(&field) == "self.records.is_none()");
1548 }
1549
1550 #[test]
1555 fn non_nullable_records_field_keeps_empty_default() {
1556 let field: FieldSpec = serde_json::from_value(serde_json::json!({
1557 "name": "UnalignedRecords", "type": "records", "versions": "0+"
1558 }))
1559 .unwrap();
1560 let res_map: HashMap<String, Resolution> = HashMap::new();
1561
1562 assert!(
1563 borrowed_default_expr(&field, &res_map)
1564 == "crate::records::RecordsPayloadBorrowed::default()"
1565 );
1566 }
1567}