quickfix_msg_gen/
lib.rs

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