swift_mt_message/fields/
field34.rs

1use super::swift_utils::{parse_amount, parse_currency};
2use crate::errors::ParseError;
3use crate::traits::SwiftField;
4use serde::{Deserialize, Serialize};
5
6/// **Field 34F: Floor Limit**
7///
8/// Specifies floor limit amount and currency for automatic processing thresholds.
9///
10/// **Format:** `3!a[1!a]15d` (currency + optional D/C indicator + amount)
11/// **Constraints:** Valid ISO 4217 currency, positive amount, indicator must be D (Debit) or C (Credit)
12///
13/// **Example:**
14/// ```text
15/// :34F:USD5000,00
16/// :34F:USDD2500,00
17/// ```
18#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
19pub struct Field34F {
20    /// ISO 4217 currency code (e.g., USD, EUR, GBP)
21    pub currency: String,
22
23    /// Optional indicator: 'D' (Debit) or 'C' (Credit)
24    pub indicator: Option<char>,
25
26    /// Floor limit amount (positive)
27    pub amount: f64,
28}
29
30impl SwiftField for Field34F {
31    fn parse(input: &str) -> crate::Result<Self>
32    where
33        Self: Sized,
34    {
35        // Field34F format: 3!a[1!a]15d (currency + optional indicator + amount)
36        if input.len() < 4 {
37            // Minimum: 3 chars currency + 1 digit amount
38            return Err(ParseError::InvalidFormat {
39                message: format!(
40                    "Field 34F must be at least 4 characters, found {}",
41                    input.len()
42                ),
43            });
44        }
45
46        // Parse currency code (first 3 characters)
47        let currency = parse_currency(&input[0..3])?;
48
49        // Check for optional indicator (4th character might be D or C)
50        let (indicator, amount_start) = if input.len() > 3 {
51            let fourth_char = input.chars().nth(3).unwrap();
52            if fourth_char == 'D' || fourth_char == 'C' {
53                (Some(fourth_char), 4)
54            } else {
55                (None, 3)
56            }
57        } else {
58            (None, 3)
59        };
60
61        // Parse amount (remaining characters)
62        let amount_str = &input[amount_start..];
63        if amount_str.is_empty() {
64            return Err(ParseError::InvalidFormat {
65                message: "Field 34F amount cannot be empty".to_string(),
66            });
67        }
68
69        let amount = parse_amount(amount_str)?;
70
71        // Amount must be positive
72        if amount <= 0.0 {
73            return Err(ParseError::InvalidFormat {
74                message: "Field 34F amount must be greater than zero".to_string(),
75            });
76        }
77
78        Ok(Field34F {
79            currency,
80            indicator,
81            amount,
82        })
83    }
84
85    fn to_swift_string(&self) -> String {
86        let indicator_str = self.indicator.map_or(String::new(), |c| c.to_string());
87        format!(
88            ":34F:{}{}{}",
89            self.currency,
90            indicator_str,
91            super::swift_utils::format_swift_amount(self.amount, 2)
92        )
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99
100    #[test]
101    fn test_field34f_valid() {
102        // Without indicator
103        let field = Field34F::parse("USD5000,00").unwrap();
104        assert_eq!(field.currency, "USD");
105        assert_eq!(field.indicator, None);
106        assert_eq!(field.amount, 5000.00);
107        assert_eq!(field.to_swift_string(), ":34F:USD5000");
108
109        // With D indicator
110        let field = Field34F::parse("USDD2500,00").unwrap();
111        assert_eq!(field.currency, "USD");
112        assert_eq!(field.indicator, Some('D'));
113        assert_eq!(field.amount, 2500.00);
114
115        // With C indicator
116        let field = Field34F::parse("EURC1000,00").unwrap();
117        assert_eq!(field.currency, "EUR");
118        assert_eq!(field.indicator, Some('C'));
119        assert_eq!(field.amount, 1000.00);
120
121        // Large amount without indicator
122        let field = Field34F::parse("GBP10000,00").unwrap();
123        assert_eq!(field.currency, "GBP");
124        assert_eq!(field.indicator, None);
125        assert_eq!(field.amount, 10000.00);
126    }
127
128    #[test]
129    fn test_field34f_invalid() {
130        // Invalid currency
131        assert!(Field34F::parse("12A100").is_err());
132        assert!(Field34F::parse("US100").is_err());
133
134        // Invalid indicator
135        assert!(Field34F::parse("USDX100").is_err());
136
137        // Zero amount
138        assert!(Field34F::parse("USD0").is_err());
139        assert!(Field34F::parse("USDD0").is_err());
140
141        // Negative amount
142        assert!(Field34F::parse("USD-100").is_err());
143
144        // Missing amount
145        assert!(Field34F::parse("USD").is_err());
146        assert!(Field34F::parse("USDD").is_err());
147    }
148}