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 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
28/// Take a FIX XML spec file as `src` parameter and generated code to `dst` parameter.
29pub 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    // Generate the code.
39    println!("Generating code ...");
40    let mut output = String::with_capacity(5 << 20); // 5Mo initial buffer
41    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    // Spawn a rustfmt daemon.
50    let mut rustfmt = process::Command::new("rustfmt")
51        .stdin(Stdio::piped())
52        .stdout(Stdio::piped())
53        .spawn()?;
54
55    // Send the code to rustfmt.
56    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); // Avoid infinite waiting !
61
62    // Check output and write result.
63    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    // Write code to disk.
74    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    // Generate enum possible values.
161    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    // Generate enum possible values.
181    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    // Generate method helpers.
200    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    // Generate `FromStr`.
224    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    // Generate `IntoFixValue`.
236    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    // Generate main struct content.
387    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    // Generate default constructor
455    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    // Generate getter / setters and sub-components.
483    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    // Generate main struct.
526    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    // Generate getter / setters and sub-components.
558    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    // Check if message has some sub components
582    if components
583        .iter()
584        .any(|x| matches!(x, SubComponent::Group(_)))
585    {
586        // Create dedicate module for the component.
587        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(_) => {} // There is no sub-components to generate for a basic field
597                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    // Eval trait and make some string alias.
649    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    // Generate code.
657    if field_required {
658        // Generate a getter that `panic()` if field is not set.
659        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        // Generate an optional getter.
671        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    // Eval trait and make some string alias.
689    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    // Generate code.
697    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 is optional, we can generate a remover.
708    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    // Add some type alias.
723    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    // Generate code.
727    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    // Add some type alias.
758    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    // Generate code.
762    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    // Generate enum with all possible messages.
775    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    // Generate decode helpers.
791    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}