1use std::{
4 fs,
5 io::{self, Write},
6 path::Path,
7 process::{self, Stdio},
8};
9
10use crate::converter::convert_spec;
11use crate::model::*;
12
13use bytes::Bytes;
14use convert_case::{Case, Casing};
15use itertools::Itertools;
16
17mod converter;
18mod model;
19
20use quickfix_spec_parser::{FieldSpec, FieldType};
21
22trait FieldAccessorGenerator {
23 fn getter_prefix_text(&self) -> &'static str;
24 fn setter_prefix_text(&self) -> &'static str;
25 fn caller_suffix_text(&self) -> &'static str;
26}
27
28pub fn generate<S: AsRef<Path>, D: AsRef<Path>>(
30 src: S,
31 dst: D,
32 begin_string: &str,
33) -> io::Result<()> {
34 let spec_data = fs::read(src)?;
35 let spec = quickfix_spec_parser::parse_spec(&spec_data).expect("Cannot parse FIX spec");
36 let spec = convert_spec(spec);
37
38 println!("Generating code ...");
40 let mut output = String::with_capacity(5 << 20); generate_root(&mut output, begin_string);
42 generate_field_ids(&mut output, &spec.field_specs);
43 generate_field_types(&mut output, &spec.field_specs);
44 generate_headers(&mut output, &spec.headers);
45 generate_trailers(&mut output, &spec.trailers);
46 generate_messages(&mut output, &spec.messages);
47 generate_message_cracker(&mut output, &spec.messages);
48
49 let mut rustfmt = process::Command::new("rustfmt")
51 .stdin(Stdio::piped())
52 .stdout(Stdio::piped())
53 .spawn()?;
54
55 println!("Formatting code ...");
57 let mut rustfmt_in = rustfmt.stdin.take().expect("Fail to take rustfmt stdin");
58 rustfmt_in.write_all(output.as_bytes())?;
59 rustfmt_in.flush()?;
60 drop(rustfmt_in); let rustfmt_out = rustfmt.wait_with_output()?;
64 if !rustfmt_out.status.success() {
65 println!("rustfmt stdout =======================");
66 println!("{:?}", Bytes::from(rustfmt_out.stdout));
67 println!("rustfmt stderr =======================");
68 println!("{:?}", Bytes::from(rustfmt_out.stderr));
69
70 panic!("Fail to run rustfmt");
71 }
72
73 println!("Writing code to disk ...");
75 fs::write(dst, rustfmt_out.stdout)?;
76 Ok(())
77}
78
79fn generate_root(output: &mut String, begin_string: &str) {
80 output.push_str(&format!(
81 r#" #[allow(unused_imports)]
82 use quickfix::*;
83
84 pub const FIX_BEGIN_STRING: &str = "{begin_string}";
85
86 #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
87 pub struct FixParseError;
88
89 impl std::fmt::Display for FixParseError {{
90 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {{
91 writeln!(f, "FIX parse error")
92 }}
93 }}
94
95 impl std::error::Error for FixParseError {{}}
96
97 pub struct GroupIterator<'a, T, I> {{
98 parent: &'a T,
99 clone_group_func: fn(&'a T, usize) -> Option<I>,
100 current_index: usize,
101 }}
102
103 impl<'a, T, I> Iterator for GroupIterator<'a, T, I> {{
104 type Item = I;
105
106 fn next(&mut self) -> Option<Self::Item> {{
107 self.current_index += 1;
108 (self.clone_group_func)(self.parent, self.current_index)
109 }}
110 }}
111
112 "#
113 ))
114}
115
116fn generate_field_ids(output: &mut String, field_specs: &[FieldSpec]) {
117 output.push_str("pub mod field_id {\n");
118
119 for field_spec in field_specs {
120 output.push_str(&format!(
121 "pub const {}: i32 = {};\n",
122 field_spec.name.to_case(Case::Constant),
123 field_spec.number
124 ));
125 }
126
127 output.push_str("} // field_id\n\n");
128}
129
130fn generate_field_types(output: &mut String, field_specs: &[FieldSpec]) {
131 output.push_str("pub mod field_types {\n");
132
133 for field_spec in field_specs {
134 if !field_spec.values.is_empty() {
135 match &field_spec.r#type {
136 FieldType::Int | FieldType::Long => {
137 generate_field_type_int_values(output, field_spec);
138 }
139 _ => {
140 generate_field_type_char_values(output, field_spec);
141 }
142 }
143 } else {
144 generate_field_type_alias(output, field_spec);
145 }
146 }
147
148 output.push_str("} // field_types\n\n");
149}
150
151fn generate_field_type_int_values(output: &mut String, field_spec: &FieldSpec) {
152 assert!(!field_spec.values.is_empty());
153 assert!(matches!(
154 field_spec.r#type,
155 FieldType::Int | FieldType::Long
156 ));
157
158 let enum_name = field_spec.name.as_str();
159
160 output.push_str("#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\n");
162 output.push_str(&format!("pub enum {enum_name} {{\n"));
163 for value in &field_spec.values {
164 output.push_str(&format!(
165 "{} = {},\n",
166 value.description.to_case(Case::UpperCamel),
167 value.value
168 ));
169 }
170 output.push_str("}\n\n");
171
172 generate_field_type_values(output, field_spec);
173}
174
175fn generate_field_type_char_values(output: &mut String, field_spec: &FieldSpec) {
176 assert!(!field_spec.values.is_empty());
177
178 let enum_name = field_spec.name.as_str();
179
180 output.push_str("#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]\n");
182 output.push_str(&format!("pub enum {enum_name} {{\n"));
183 for value in &field_spec.values {
184 output.push_str(&format!(
185 "{},\n",
186 value.description.to_case(Case::UpperCamel)
187 ));
188 }
189 output.push_str("}\n\n");
190
191 generate_field_type_values(output, field_spec);
192}
193
194fn generate_field_type_values(output: &mut String, field_spec: &FieldSpec) {
195 assert!(!field_spec.values.is_empty());
196
197 let type_name = field_spec.name.as_str();
198
199 output.push_str(&format!(
201 r#" impl {type_name} {{
202 #[inline(always)]
203 pub const fn from_const_bytes(s: &[u8]) -> Result<Self, crate::FixParseError> {{
204 match s {{
205 "#
206 ));
207 for value in &field_spec.values {
208 output.push_str(&format!(
209 " b\"{}\" => Ok(Self::{}),\n",
210 value.value,
211 value.description.to_case(Case::UpperCamel),
212 ));
213 }
214 output.push_str(
215 r#" _ => Err(crate::FixParseError),
216 }
217 }
218 }
219
220 "#,
221 );
222
223 output.push_str(&format!(
225 r#" impl std::str::FromStr for {type_name} {{
226 type Err = crate::FixParseError;
227 fn from_str(s: &str) -> Result<Self, Self::Err> {{
228 Self::from_const_bytes(s.as_bytes())
229 }}
230 }}
231
232 "#
233 ));
234
235 output.push_str(&format!(
237 r#" impl quickfix::IntoFixValue for {type_name} {{
238 fn into_fix_value(self) -> Result<std::ffi::CString, std::ffi::NulError> {{
239 std::ffi::CString::new(match self {{
240 "#
241 ));
242 for value in &field_spec.values {
243 output.push_str(&format!(
244 " Self::{} => \"{}\",\n",
245 value.description.to_case(Case::UpperCamel),
246 value.value,
247 ));
248 }
249 output.push_str(
250 r#" })
251 }
252 }
253
254 "#,
255 );
256}
257
258fn generate_field_type_alias(output: &mut String, field_spec: &FieldSpec) {
259 assert!(field_spec.values.is_empty());
260
261 let type_name = field_spec.name.as_str();
262 let rust_type = match &field_spec.r#type {
263 FieldType::Int => "i64",
264 FieldType::Long => "i128",
265 FieldType::Length => "u32",
266 FieldType::SequenceNumber => "u32",
267 FieldType::NumberInGroup => "i32",
268 FieldType::Boolean => "bool",
269
270 FieldType::Float
271 | FieldType::Price
272 | FieldType::Amount
273 | FieldType::Quantity
274 | FieldType::PriceOffset
275 | FieldType::Percentage => "f64",
276
277 FieldType::Char
278 | FieldType::Data
279 | FieldType::Time
280 | FieldType::Date
281 | FieldType::MonthYear
282 | FieldType::DayOfMonth
283 | FieldType::String
284 | FieldType::Currency
285 | FieldType::MultipleValueString
286 | FieldType::Exchange
287 | FieldType::LocalMarketDate
288 | FieldType::UtcTimeStamp
289 | FieldType::UtcDate
290 | FieldType::UtcTimeOnly
291 | FieldType::Country
292 | FieldType::UtcDateOnly
293 | FieldType::MultipleCharValue
294 | FieldType::MultipleStringValue
295 | FieldType::TzTimeOnly
296 | FieldType::TzTimestamp
297 | FieldType::XmlData
298 | FieldType::Language
299 | FieldType::TagNumber
300 | FieldType::XidRef
301 | FieldType::Xid
302 | FieldType::LocalMarketTime => "String",
303 x => unimplemented!("Unsupported FieldType: {x:?}"),
304 };
305
306 output.push_str(&format!("pub type {type_name} = {rust_type};\n\n"));
307}
308
309fn generate_headers(output: &mut String, components: &[SubComponent]) {
310 struct Accessor;
311
312 impl FieldAccessorGenerator for Accessor {
313 fn getter_prefix_text(&self) -> &'static str {
314 "inner.with_header(|x| x."
315 }
316 fn setter_prefix_text(&self) -> &'static str {
317 "inner.with_header_mut(|x| x."
318 }
319 fn caller_suffix_text(&self) -> &'static str {
320 ")"
321 }
322 }
323
324 generate_message_wrapper(output, "Header", components, &Accessor);
325}
326
327fn generate_trailers(output: &mut String, components: &[SubComponent]) {
328 struct Accessor;
329
330 impl FieldAccessorGenerator for Accessor {
331 fn getter_prefix_text(&self) -> &'static str {
332 "inner.with_trailer(|x| x."
333 }
334 fn setter_prefix_text(&self) -> &'static str {
335 "inner.with_trailer_mut(|x| x."
336 }
337 fn caller_suffix_text(&self) -> &'static str {
338 ")"
339 }
340 }
341
342 generate_message_wrapper(output, "Trailer", components, &Accessor);
343}
344
345fn generate_message_wrapper(
346 output: &mut String,
347 struct_name: &str,
348 components: &[SubComponent],
349 accessor: &impl FieldAccessorGenerator,
350) {
351 output.push_str(&format!(
352 r#" #[derive(Debug)]
353 pub struct {struct_name}<'a> {{ inner: &'a quickfix::Message }}
354
355 "#
356 ));
357
358 generate_sub_components(output, &struct_name.to_case(Case::Snake), components);
359
360 output.push_str(&format!("impl {struct_name}<'_> {{\n"));
361 generate_components_getters(output, struct_name, components, accessor);
362 output.push_str("}\n\n");
363
364 output.push_str(&format!(
365 r#" #[derive(Debug)]
366 pub struct {struct_name}Mut<'a> {{ inner: &'a mut quickfix::Message }}
367
368 "#
369 ));
370
371 output.push_str(&format!("impl {struct_name}Mut<'_> {{\n"));
372 generate_components_setters(output, struct_name, components, accessor);
373 output.push_str("}\n\n");
374}
375
376fn generate_messages(output: &mut String, messages: &[MessageSpec]) {
377 for message in messages {
378 generate_message(output, message);
379 }
380}
381
382fn generate_message(output: &mut String, message: &MessageSpec) {
383 let struct_name = message.name.as_str();
384 let msg_type = message.msg_type.as_str();
385
386 output.push_str(&format!(
388 r#" #[derive(Debug, Clone)]
389 pub struct {struct_name} {{
390 inner: quickfix::Message,
391 }}
392
393 impl {struct_name} {{
394 pub const MSG_TYPE_BYTES: &'static str = "{msg_type}";
395 pub const MSG_TYPE: crate::field_types::MsgType =
396 match crate::field_types::MsgType::from_const_bytes(Self::MSG_TYPE_BYTES.as_bytes()) {{
397 Ok(value) => value,
398 Err(_) => panic!("Invalid message type for {struct_name}"),
399 }};
400
401 #[inline(always)]
402 pub fn header(&mut self) -> Header {{
403 Header {{ inner: &self.inner }}
404 }}
405
406 #[inline(always)]
407 pub fn header_mut(&mut self) -> HeaderMut {{
408 HeaderMut {{ inner: &mut self.inner }}
409 }}
410
411 #[inline(always)]
412 pub fn trailer(&mut self) -> Trailer {{
413 Trailer {{ inner: &self.inner }}
414 }}
415
416 #[inline(always)]
417 pub fn trailer_mut(&mut self) -> TrailerMut {{
418 TrailerMut {{ inner: &mut self.inner }}
419 }}
420
421 /// Convert inner message as FIX text.
422 ///
423 /// This method is only here for debug / tests purposes. Do not use this
424 /// in real production code.
425 #[inline(never)]
426 pub fn to_fix_string(&self) -> String {{
427 self.inner
428 .to_fix_string()
429 .expect("Fail to format {struct_name} message as FIX string")
430 }}
431 }}
432
433 impl From<{struct_name}> for quickfix::Message {{
434 fn from(input: {struct_name}) -> Self {{
435 input.inner
436 }}
437 }}
438
439 impl From<quickfix::Message> for {struct_name} {{
440 fn from(input: quickfix::Message) -> Self {{
441 assert_eq!(
442 input
443 .with_header(|h| h.get_field(field_id::MSG_TYPE))
444 .and_then(|x| crate::field_types::MsgType::from_const_bytes(x.as_bytes()).ok()),
445 Some(Self::MSG_TYPE),
446 );
447 Self {{ inner: input }}
448 }}
449 }}
450
451 "#
452 ));
453
454 let required_params = format_required_params(&message.components);
456 let new_setters = format_new_setters(&message.components);
457
458 output.push_str(&format!(
459 r#" impl {struct_name} {{
460 #[allow(clippy::too_many_arguments)]
461 pub fn try_new({required_params}) -> Result<Self, quickfix::QuickFixError> {{
462 let mut inner = quickfix::Message::new();
463
464 // Set headers (most of them will be set by quickfix library).
465 inner.with_header_mut(|h| {{
466 h.set_field(crate::field_id::BEGIN_STRING, crate::FIX_BEGIN_STRING)
467 }})?;
468 inner.with_header_mut(|h| {{
469 h.set_field(crate::field_id::MSG_TYPE, Self::MSG_TYPE)
470 }})?;
471
472 // Set required attributes.
473 {new_setters}
474
475 Ok(Self {{ inner }})
476 }}
477 }}
478
479 "#
480 ));
481
482 struct Accessor;
484
485 impl FieldAccessorGenerator for Accessor {
486 fn getter_prefix_text(&self) -> &'static str {
487 "inner."
488 }
489 fn setter_prefix_text(&self) -> &'static str {
490 "inner."
491 }
492 fn caller_suffix_text(&self) -> &'static str {
493 ""
494 }
495 }
496
497 generate_sub_components(
498 output,
499 &message.name.to_case(Case::Snake),
500 &message.components,
501 );
502
503 output.push_str(&format!("impl {struct_name} {{\n\n"));
504 generate_components_getters(output, struct_name, &message.components, &Accessor);
505 generate_components_setters(output, struct_name, &message.components, &Accessor);
506 output.push_str("}\n\n");
507}
508
509fn generate_group(output: &mut String, group: &MessageGroup) {
510 let struct_name = group.name.as_str();
511 let group_id = format_field_id(&group.name);
512 let group_delim = format_field_id(
513 group
514 .components
515 .first()
516 .expect("Group cannot be empty")
517 .name(),
518 );
519 let group_value_ids = group
520 .components
521 .iter()
522 .map(|x| format_field_id(x.name()))
523 .join(",");
524
525 let required_params = format_required_params(&group.components);
527 let new_setters = format_new_setters(&group.components);
528
529 output.push_str(&format!(
530 r#" #[derive(Debug, Clone)]
531 pub struct {struct_name} {{
532 pub(crate) inner: quickfix::Group,
533 }}
534
535 impl {struct_name} {{
536 pub const FIELD_ID: i32 = {group_id};
537 pub const DELIMITER: i32 = {group_delim};
538
539 #[allow(clippy::too_many_arguments)]
540 pub fn try_new({required_params}) -> Result<Self, quickfix::QuickFixError> {{
541 #[allow(unused_mut)]
542 let mut inner = quickfix::Group::try_with_orders(
543 Self::FIELD_ID,
544 Self::DELIMITER,
545 &[{group_value_ids}],
546 ).expect("Fail to build group {struct_name}");
547
548 {new_setters}
549
550 Ok(Self {{ inner }})
551 }}
552 }}
553
554 "#
555 ));
556
557 struct Accessor;
559
560 impl FieldAccessorGenerator for Accessor {
561 fn getter_prefix_text(&self) -> &'static str {
562 "inner."
563 }
564 fn setter_prefix_text(&self) -> &'static str {
565 "inner."
566 }
567 fn caller_suffix_text(&self) -> &'static str {
568 ""
569 }
570 }
571
572 generate_sub_components(output, &group.name.to_case(Case::Snake), &group.components);
573
574 output.push_str(&format!("impl {struct_name} {{\n\n"));
575 generate_components_getters(output, struct_name, &group.components, &Accessor);
576 generate_components_setters(output, struct_name, &group.components, &Accessor);
577 output.push_str("}\n\n");
578}
579
580fn generate_sub_components(output: &mut String, module_name: &str, components: &[SubComponent]) {
581 if components
583 .iter()
584 .any(|x| matches!(x, SubComponent::Group(_)))
585 {
586 output.push_str(&format!(
588 r#" pub mod {module_name} {{
589 use super::*;
590
591 "#
592 ));
593
594 for value in components {
595 match value {
596 SubComponent::Field(_) => {} SubComponent::Group(x) => {
598 generate_group(output, x);
599 }
600 }
601 }
602 output.push_str("}\n\n");
603 }
604}
605
606fn generate_components_getters(
607 output: &mut String,
608 struct_name: &str,
609 components: &[SubComponent],
610 accessor: &impl FieldAccessorGenerator,
611) {
612 for component in components {
613 match component {
614 SubComponent::Field(x) => {
615 generate_field_getter(output, &x.name, x.required, accessor);
616 }
617 SubComponent::Group(x) => {
618 generate_group_reader(output, struct_name, x);
619 }
620 }
621 }
622}
623
624fn generate_components_setters(
625 output: &mut String,
626 struct_name: &str,
627 components: &[SubComponent],
628 accessor: &impl FieldAccessorGenerator,
629) {
630 for component in components {
631 match component {
632 SubComponent::Field(x) => {
633 generate_field_setters(output, x, accessor);
634 }
635 SubComponent::Group(x) => {
636 generate_fn_add_group(output, struct_name, x);
637 }
638 }
639 }
640}
641
642fn generate_field_getter(
643 output: &mut String,
644 field_name: &str,
645 field_required: bool,
646 accessor: &impl FieldAccessorGenerator,
647) {
648 let call_get_prefix = accessor.getter_prefix_text();
650 let call_suffix = accessor.caller_suffix_text();
651
652 let fun_name = format!("get_{}", field_name.to_case(Case::Snake));
653 let field_type = format!("crate::field_types::{field_name}");
654 let field_id = format_field_id(field_name);
655
656 if field_required {
658 output.push_str(&format!(
660 r#" #[inline(always)]
661 pub fn {fun_name}(&self) -> {field_type} {{
662 self.{call_get_prefix}get_field({field_id}){call_suffix}
663 .and_then(|x| x.parse().ok())
664 .expect("{field_id} is required but it is missing")
665 }}
666
667 "#
668 ));
669 } else {
670 output.push_str(&format!(
672 r#" #[inline(always)]
673 pub fn {fun_name}(&self) -> Option<{field_type}> {{
674 self.{call_get_prefix}get_field({field_id}){call_suffix}
675 .and_then(|x| x.parse().ok())
676 }}
677
678 "#
679 ));
680 }
681}
682
683fn generate_field_setters(
684 output: &mut String,
685 field: &MessageField,
686 accessor: &impl FieldAccessorGenerator,
687) {
688 let call_set_prefix = accessor.setter_prefix_text();
690 let call_suffix = accessor.caller_suffix_text();
691
692 let field_name = field.name.to_case(Case::Snake);
693 let field_type = format!("crate::field_types::{}", field.name);
694 let field_id = format_field_id(&field.name);
695
696 output.push_str(&format!(
698 r#" #[inline(always)]
699 pub fn set_{field_name}(&mut self, value: {field_type}) -> Result<&Self, quickfix::QuickFixError> {{
700 self.{call_set_prefix}set_field({field_id}, value){call_suffix}?;
701 Ok(self)
702 }}
703
704 "#
705 ));
706
707 if !field.required {
709 output.push_str(&format!(
710 r#" #[inline(always)]
711 pub fn remove_{field_name}(&mut self) -> Result<&Self, quickfix::QuickFixError> {{
712 self.{call_set_prefix}remove_field({field_id}){call_suffix}?;
713 Ok(self)
714 }}
715
716 "#
717 ));
718 }
719}
720
721fn generate_group_reader(output: &mut String, struct_name: &str, group: &MessageGroup) {
722 let group_name = group.name.to_case(Case::Snake);
724 let group_type = format!("self::{}::{}", struct_name.to_case(Case::Snake), group.name);
725
726 output.push_str(&format!(
728 r#" #[inline(always)]
729 pub fn {group_name}_len(&self) -> usize {{
730 self.inner
731 .get_field({group_type}::FIELD_ID)
732 .and_then(|x| x.parse().ok())
733 .unwrap_or_default()
734 }}
735
736 #[inline(always)]
737 pub fn clone_group_{group_name}(&self, index: usize) -> Option<{group_type}> {{
738 self.inner
739 .clone_group(index as i32, {group_type}::FIELD_ID)
740 .map(|raw_group| {group_type} {{ inner: raw_group }})
741 }}
742
743 #[inline(always)]
744 pub fn iter_{group_name}(&self) -> GroupIterator<'_, Self, {group_type}> {{
745 GroupIterator {{
746 parent: self,
747 clone_group_func: |parent, idx| parent.clone_group_{group_name}(idx),
748 current_index: 0,
749 }}
750 }}
751
752 "#
753 ));
754}
755
756fn generate_fn_add_group(output: &mut String, struct_name: &str, group: &MessageGroup) {
757 let group_name = group.name.to_case(Case::Snake);
759 let group_type = format!("self::{}::{}", struct_name.to_case(Case::Snake), group.name);
760
761 output.push_str(&format!(
763 r#" #[inline(always)]
764 pub fn add_{group_name}(&mut self, value: {group_type}) -> Result<&Self, quickfix::QuickFixError> {{
765 self.inner.add_group(&value.inner)?;
766 Ok(self)
767 }}
768
769 "#
770 ));
771}
772
773fn generate_message_cracker(output: &mut String, messages: &[MessageSpec]) {
774 output.push_str(
776 r#" #[derive(Debug, Clone)]
777 pub enum Messages {
778 "#,
779 );
780 for message in messages {
781 let struct_name = &message.name;
782
783 output.push_str(&format!(" {struct_name}({struct_name}),\n"));
784 }
785 output.push_str(
786 r#" }
787 "#,
788 );
789
790 output.push_str(
792 r#" impl Messages {
793 /// Try decoding input message or return the message if it does not match any known message type.
794 pub fn decode(input: quickfix::Message) -> Result<Self, quickfix::Message> {
795 match input
796 .with_header(|h| h.get_field(crate::field_id::MSG_TYPE))
797 .as_deref()
798 {
799 "#,
800 );
801 for message in messages {
802 let struct_name = &message.name;
803 let message_type = &message.msg_type;
804
805 output.push_str(&format!(
806 " Some(\"{message_type}\") => Ok(Self::{struct_name}(input.into())),\n"
807 ));
808 }
809 output.push_str(
810 r#" _ => Err(input),
811 }
812 }
813 }
814 "#,
815 );
816}
817
818fn format_field_id(input: &str) -> String {
819 format!("crate::field_id::{}", input.to_case(Case::Constant))
820}
821
822fn format_required_params(components: &[SubComponent]) -> String {
823 components
824 .iter()
825 .filter(|x| x.is_required())
826 .map(|x| {
827 let name = x.name();
828 let param_name = name.to_case(Case::Snake);
829 format!("{param_name}: crate::field_types::{name}")
830 })
831 .join(", ")
832}
833
834fn format_new_setters(components: &[SubComponent]) -> String {
835 components
836 .iter()
837 .filter(|x| x.is_required())
838 .map(|x| {
839 let name = x.name();
840 let field_id = format_field_id(name);
841 let param_name = name.to_case(Case::Snake);
842 format!("inner.set_field({field_id}, {param_name})?;")
843 })
844 .join("\n")
845}