swift_mt_message/
traits.rs

1//! Core traits for SWIFT field and message types
2
3use crate::{Result, SwiftResult};
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use std::fmt::Debug;
7
8/// Core trait for all Swift field types
9pub trait SwiftField: Serialize + for<'de> Deserialize<'de> + Clone + std::fmt::Debug {
10    /// Parse field value from string representation
11    fn parse(value: &str) -> Result<Self>
12    where
13        Self: Sized;
14
15    /// Parse field value with variant hint for enum fields
16    /// Default implementation falls back to regular parse
17    fn parse_with_variant(
18        value: &str,
19        _variant: Option<&str>,
20        _field_tag: Option<&str>,
21    ) -> Result<Self>
22    where
23        Self: Sized,
24    {
25        Self::parse(value)
26    }
27
28    /// Convert field back to SWIFT string format
29    fn to_swift_string(&self) -> String;
30
31    /// Get field format specification
32    fn format_spec() -> &'static str;
33
34    /// Get valid variant letters for enum fields
35    /// Returns None for non-enum fields, Some(vec) for enum fields
36    fn valid_variants() -> Option<Vec<&'static str>> {
37        None // Default implementation for non-enum fields
38    }
39}
40
41/// Core trait for Swift message types
42pub trait SwiftMessageBody: Debug + Clone + Send + Sync + Serialize + std::any::Any {
43    /// Get the message type identifier (e.g., "103", "202")
44    fn message_type() -> &'static str;
45
46    /// Create from field map with sequential consumption tracking
47    fn from_fields(fields: HashMap<String, Vec<(String, usize)>>) -> SwiftResult<Self>
48    where
49        Self: Sized;
50
51    /// Create from field map with configuration for error collection
52    fn from_fields_with_config(
53        fields: HashMap<String, Vec<(String, usize)>>,
54        config: &crate::errors::ParserConfig,
55    ) -> std::result::Result<crate::errors::ParseResult<Self>, crate::errors::ParseError>
56    where
57        Self: Sized,
58    {
59        // Default implementation: use fail-fast mode if config.fail_fast is true
60        if config.fail_fast {
61            match Self::from_fields(fields) {
62                Ok(msg) => Ok(crate::errors::ParseResult::Success(msg)),
63                Err(e) => Err(e),
64            }
65        } else {
66            // For non-fail-fast mode, derived types should override this method
67            // Default behavior falls back to fail-fast
68            match Self::from_fields(fields) {
69                Ok(msg) => Ok(crate::errors::ParseResult::Success(msg)),
70                Err(e) => Err(e),
71            }
72        }
73    }
74
75    /// Convert to field map
76    fn to_fields(&self) -> HashMap<String, Vec<String>>;
77
78    /// Convert to ordered field list for MT serialization
79    /// Returns fields in the correct sequence order for multi-sequence messages
80    fn to_ordered_fields(&self) -> Vec<(String, String)> {
81        // Default implementation: just flatten the HashMap in numeric order
82        let field_map = self.to_fields();
83        let mut ordered_fields = Vec::new();
84
85        // Create ascending field order by sorting field tags numerically
86        // Use stable sort and include the full tag as secondary sort key for deterministic ordering
87        let mut field_tags: Vec<(&String, u32)> = field_map
88            .keys()
89            .map(|tag| {
90                let num = tag
91                    .chars()
92                    .take_while(|c| c.is_ascii_digit())
93                    .fold(0u32, |acc, c| acc * 10 + (c as u32 - '0' as u32));
94                (tag, num)
95            })
96            .collect();
97        // Sort by numeric value first, then by full tag string for stable ordering
98        field_tags.sort_by(|(tag_a, num_a), (tag_b, num_b)| {
99            num_a.cmp(num_b).then_with(|| tag_a.cmp(tag_b))
100        });
101
102        // Output fields in ascending numerical order
103        for (field_tag, _) in field_tags {
104            if let Some(field_values) = field_map.get(field_tag) {
105                for field_value in field_values {
106                    ordered_fields.push((field_tag.clone(), field_value.clone()));
107                }
108            }
109        }
110
111        ordered_fields
112    }
113
114    /// Get required field tags for this message type
115    fn required_fields() -> Vec<&'static str>;
116
117    /// Get optional field tags for this message type
118    fn optional_fields() -> Vec<&'static str>;
119}