swift_mt_message/headers/
mod.rs

1//! # SWIFT Message Headers and Trailers
2//!
3//! ## Purpose
4//! Comprehensive header and trailer structures for SWIFT MT messages, implementing the complete
5//! SWIFT FIN block structure including Basic Header (Block 1), Application Header (Block 2),
6//! User Header (Block 3), and Trailer (Block 5).
7//!
8//! ## Block Structure
9//! - **Block 1**: Basic Header - Sender identification and routing information
10//! - **Block 2**: Application Header - Message type and delivery information
11//! - **Block 3**: User Header - Optional user-defined fields and references
12//! - **Block 5**: Trailer - Optional authentication and delivery confirmation
13//!
14//! ## Features
15//! - **Complete SWIFT Compliance**: Follows SWIFT User Handbook specifications
16//! - **Type-Safe Parsing**: Strongly-typed header structures with validation
17//! - **Authentication Support**: MAC and authentication key handling
18//! - **Sample Generation**: Realistic header generation for testing
19//! - **Network Validation**: BIC validation and routing verification
20
21use crate::errors::{ParseError, Result};
22use serde::{Deserialize, Serialize};
23
24/// **Basic Header (Block 1): SWIFT Message Identification and Routing**
25///
26/// ## Purpose
27/// The Basic Header constitutes the first mandatory block of every SWIFT message, providing
28/// essential identification, routing, and sequencing information. This header enables the
29/// SWIFT network to authenticate the sender, route the message appropriately, and maintain
30/// message sequence integrity across the global financial messaging infrastructure.
31///
32/// ## Format Specification
33/// - **Block Format**: `{1:F01SSSSSSSSSCCC0000NNNNNN}`
34/// - **Total Length**: Exactly 25 characters
35/// - **Structure**: Application ID + Service ID + LT Address + Session + Sequence
36/// - **Character Set**: Alphanumeric, uppercase only
37///
38/// ## Business Context Applications
39/// - **Message Authentication**: Sender identification and verification
40/// - **Network Routing**: Logical terminal addressing for message delivery
41/// - **Session Management**: Message sequencing within communication sessions
42/// - **Audit Trail**: Complete message tracking and reconciliation
43///
44/// ## Component Breakdown
45/// ### Application Identifier (1 character)
46/// - **F**: FIN application (Financial messages)
47/// - **A**: GPA application (General Purpose Application)
48/// - **L**: GPA application (for certain message types)
49/// - **Usage**: Determines message processing rules and validation
50///
51/// ### Service Identifier (2 characters)
52/// - **01**: FIN service (standard financial messages)
53/// - **03**: FIN Copy service (third-party copy)
54/// - **05**: GPA service
55/// - **21**: ACK/NAK service
56///
57/// ### Logical Terminal Address (12 characters)
58/// - **BIC Code**: First 8 characters (institution identifier)
59/// - **Terminal Code**: Next 1 character (logical terminal)
60/// - **Branch Code**: Last 3 characters (XXX for head office)
61/// - **Format**: Must be valid SWIFT-connected BIC
62///
63/// ## Network Validation Requirements
64/// - **BIC Validation**: Must be active SWIFT participant
65/// - **Service Compatibility**: Service ID must match message type
66/// - **Session Validity**: Session number must be within valid range
67/// - **Sequence Continuity**: Sequence numbers must be sequential
68///
69/// ## Session and Sequence Management
70/// ### Session Number (4 digits)
71/// - **Range**: 0000-9999
72/// - **Purpose**: Groups related messages within a session
73/// - **Reset**: Can be reset based on bilateral agreement
74/// - **Tracking**: Used for message reconciliation
75///
76/// ### Sequence Number (6 digits)
77/// - **Range**: 000000-999999
78/// - **Increment**: Sequential within session
79/// - **Uniqueness**: Combined with session ensures message uniqueness
80/// - **Recovery**: Critical for message recovery procedures
81///
82/// ## Regional Considerations
83/// - **Time Zones**: LT address determines processing time zone
84/// - **Operating Hours**: Service availability based on regional center
85/// - **Holiday Schedules**: Regional holiday impacts on processing
86/// - **Regulatory Compliance**: Regional reporting requirements
87///
88/// ## Error Prevention Guidelines
89/// - **BIC Accuracy**: Verify sender BIC is authorized for service
90/// - **Sequence Management**: Maintain strict sequence number control
91/// - **Session Coordination**: Coordinate session numbers with correspondent
92/// - **Format Compliance**: Ensure exact 25-character length
93///
94/// ## Security and Authentication
95/// - **Sender Authentication**: BIC must match authenticated connection
96/// - **Message Integrity**: Header contributes to message MAC calculation
97/// - **Non-repudiation**: Sender identification cannot be disputed
98/// - **Audit Trail**: Complete tracking from sender to receiver
99///
100/// ## Integration with Other Blocks
101/// - **Block 2**: Message type and routing continuation
102/// - **Block 3**: Optional user header for enhanced services
103/// - **Block 4**: Message text validated based on Block 1 service
104/// - **Block 5**: Trailer with checksums and authentication
105///
106/// ## Compliance Framework
107/// - **SWIFT Standards**: Full compliance with FIN interface standards
108/// - **Service Level Agreements**: Performance guarantees based on service
109/// - **Regulatory Reporting**: Sender identification for compliance
110/// - **Audit Requirements**: Complete message trail maintenance
111///
112/// ## Best Practices
113/// - **BIC Management**: Keep BIC directory updated
114/// - **Sequence Control**: Implement robust sequence management
115/// - **Session Planning**: Plan session number allocation
116/// - **Error Recovery**: Implement sequence gap detection
117///
118/// ## See Also
119/// - SWIFT FIN Interface Standards: Block 1 Specifications
120/// - BIC Directory: Valid SWIFT Participant Codes
121/// - Session Management Guide: Best Practices
122/// - Message Sequencing: Control and Recovery Procedures
123#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
124pub struct BasicHeader {
125    /// Application identifier
126    ///
127    /// Format: 1!a - Single alphabetic character
128    /// Values: F (FIN), A (GPA), L (GPA legacy)
129    /// Determines message processing rules and validation requirements
130    pub application_id: String,
131
132    /// Service identifier
133    ///
134    /// Format: 2!n - Two numeric characters
135    /// Common values: 01 (FIN), 03 (FIN Copy), 05 (GPA), 21 (ACK/NAK)
136    /// Specifies the SWIFT service type for message handling
137    pub service_id: String,
138
139    /// Logical Terminal (LT) address
140    ///
141    /// Format: 12!c - 12 alphanumeric characters
142    /// Structure: 8-char BIC + 1-char terminal + 3-char branch
143    /// Uniquely identifies the sending terminal in SWIFT network
144    pub logical_terminal: String,
145
146    /// Sender BIC extracted from logical terminal
147    ///
148    /// Format: 8!c - First 8 characters of logical terminal
149    /// The sending institution's Bank Identifier Code
150    pub sender_bic: String,
151
152    /// Session number
153    ///
154    /// Format: 4!n - Four numeric digits (0000-9999)
155    /// Groups related messages within a communication session
156    /// Used for message reconciliation and recovery procedures
157    pub session_number: String,
158
159    /// Sequence number
160    ///
161    /// Format: 6!n - Six numeric digits (000000-999999)
162    /// Sequential message counter within session
163    /// Critical for message ordering and duplicate detection
164    pub sequence_number: String,
165}
166
167impl BasicHeader {
168    /// Parse basic header from block 1 string
169    pub fn parse(block1: &str) -> Result<Self> {
170        if block1.len() < 21 {
171            return Err(ParseError::InvalidBlockStructure {
172                block: "1".to_string(),
173                message: format!(
174                    "Block 1 too short: expected at least 21 characters, got {}",
175                    block1.len()
176                ),
177            });
178        }
179
180        let application_id = block1[0..1].to_string();
181        let service_id = block1[1..3].to_string();
182        let logical_terminal = block1[3..15].to_string();
183        let session_number = block1[15..19].to_string();
184        let sequence_number = block1[19..].to_string();
185        let sender_bic = logical_terminal[0..8].to_string();
186
187        Ok(BasicHeader {
188            application_id,
189            service_id,
190            logical_terminal,
191            sender_bic,
192            session_number,
193            sequence_number,
194        })
195    }
196}
197
198impl std::fmt::Display for BasicHeader {
199    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
200        write!(
201            f,
202            "{}{}{}{}{}",
203            self.application_id,
204            self.service_id,
205            self.logical_terminal,
206            self.session_number,
207            self.sequence_number
208        )
209    }
210}
211
212/// **Application Header (Block 2): Message Type and Routing Information**
213///
214/// ## Purpose
215/// The Application Header provides essential message type identification and routing
216/// information, enabling the SWIFT network to properly categorize, prioritize, and
217/// deliver messages. This header determines processing rules, delivery monitoring,
218/// and time-critical handling requirements for financial messages.
219///
220/// ## Format Specification
221/// - **Input Format**: `{2:I103DDDDDDDDDDDDP[M][OOO]}`
222/// - **Output Format**: `{2:O103HHMMDDDDDDDDDDDDYYYYMMDDHHMMNNNNNN}`
223/// - **Direction Dependent**: Structure varies for input (I) vs output (O)
224/// - **Variable Length**: 18-21 characters for input, fixed for output
225///
226/// ## Business Context Applications
227/// - **Message Classification**: Determines processing rules by message type
228/// - **Priority Handling**: Urgent vs normal message processing
229/// - **Delivery Assurance**: Monitoring and non-delivery notifications
230/// - **Time Management**: Obsolescence period for time-sensitive messages
231///
232/// ## Direction Indicator
233/// ### Input Messages (I)
234/// - **Sender Perspective**: Messages being sent to SWIFT
235/// - **Validation**: Full message validation applied
236/// - **Routing**: To destination specified in header
237/// - **Storage**: Stored in sender's message archive
238///
239/// ### Output Messages (O)
240/// - **Receiver Perspective**: Messages delivered from SWIFT
241/// - **Delivery**: Includes delivery timestamp and MIR
242/// - **Status**: Confirmed successful delivery
243/// - **Archive**: Stored in receiver's message archive
244///
245/// ## Message Type Classification
246/// ### Category 1: Customer Payments (MT 1nn)
247/// - **MT 103**: Single Customer Credit Transfer
248/// - **MT 110**: Advice of Cheque
249/// - **Priority**: Often urgent for same-day value
250///
251/// ### Category 2: Bank Transfers (MT 2nn)
252/// - **MT 202**: General Financial Institution Transfer
253/// - **MT 202COV**: Cover Payment
254/// - **Priority**: High priority for bank liquidity
255///
256/// ### Category 9: Balance and Status (MT 9nn)
257/// - **MT 940**: Customer Statement
258/// - **MT 950**: Statement Message
259/// - **Priority**: Normal, end-of-day processing
260///
261/// ## Priority Management
262/// ### Urgent Priority (U)
263/// - **Processing**: Immediate, ahead of normal messages
264/// - **Use Cases**: Time-critical payments, cut-off deadlines
265/// - **Delivery**: Fastest available route
266/// - **Cost**: Premium pricing may apply
267///
268/// ### Normal Priority (N)
269/// - **Processing**: Standard queue processing
270/// - **Use Cases**: Regular payments and messages
271/// - **Delivery**: Standard delivery timeframes
272/// - **Cost**: Standard message pricing
273///
274/// ### System Priority (S)
275/// - **Processing**: System-generated messages
276/// - **Use Cases**: ACKs, NAKs, system notifications
277/// - **Delivery**: Highest priority delivery
278/// - **Access**: Reserved for SWIFT system use
279///
280/// ## Delivery Monitoring Options
281/// ### Non-Delivery Warning (1)
282/// - **Timeout**: Warning if not delivered within set time
283/// - **Action**: Sender notified of delivery delay
284/// - **Use Case**: Important but not critical messages
285///
286/// ### Delivery Notification (3)
287/// - **Confirmation**: Positive delivery confirmation required
288/// - **Notification**: MT 011 sent upon successful delivery
289/// - **Use Case**: Critical messages requiring confirmation
290///
291/// ### No Monitoring (blank)
292/// - **Standard**: Default delivery without monitoring
293/// - **Notification**: No delivery status updates
294/// - **Use Case**: Routine, non-critical messages
295///
296/// ## Obsolescence Period
297/// - **Format**: 3 numeric digits (003-999)
298/// - **Unit**: 5-minute intervals
299/// - **Maximum**: 999 = 83 hours
300/// - **Purpose**: Message validity timeout
301/// - **Action**: Automatic cancellation if not delivered
302///
303/// ## Network Validation Requirements
304/// - **BIC Validation**: Destination must be valid SWIFT participant
305/// - **Message Type**: Must be valid for sender's subscription
306/// - **Priority Rules**: Certain messages restricted to normal priority
307/// - **Monitoring Compatibility**: Not all messages support monitoring
308///
309/// ## Regional Considerations
310/// - **Cut-off Times**: Regional deadlines for urgent messages
311/// - **Processing Windows**: Regional operating hours impact
312/// - **Holiday Handling**: Regional holidays affect delivery
313/// - **Regulatory Priority**: Some regions mandate priority levels
314///
315/// ## Error Prevention Guidelines
316/// - **BIC Verification**: Confirm destination BIC is reachable
317/// - **Type Validation**: Ensure message type is authorized
318/// - **Priority Selection**: Use appropriate priority level
319/// - **Monitoring Choice**: Select monitoring based on criticality
320///
321/// ## Integration with Other Blocks
322/// - **Block 1**: Sender identification coordination
323/// - **Block 3**: Service options based on message type
324/// - **Block 4**: Content validation per message type
325/// - **Block 5**: Delivery confirmations and status
326///
327/// ## Compliance Framework
328/// - **Message Standards**: Type-specific validation rules
329/// - **Priority Policies**: Fair use of urgent priority
330/// - **Delivery SLAs**: Service level compliance
331/// - **Audit Trail**: Complete routing documentation
332///
333/// ## Processing Impact
334/// - **Queue Position**: Priority determines processing order
335/// - **Validation Depth**: Message type determines checks
336/// - **Routing Path**: Optimal path based on priority
337/// - **Cost Calculation**: Priority affects message pricing
338///
339/// ## Best Practices
340/// - **Priority Discipline**: Reserve urgent for true urgency
341/// - **Monitoring Selection**: Match monitoring to risk level
342/// - **Type Accuracy**: Ensure correct message type selection
343/// - **Destination Validation**: Verify BIC before sending
344///
345/// ## See Also
346/// - SWIFT FIN User Handbook: Block 2 Specifications
347/// - Message Type Catalog: Complete MT Message List
348/// - Priority Guidelines: Best Practices for Priority Selection
349/// - Delivery Monitoring: Service Options and Usage
350#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
351pub struct ApplicationHeader {
352    /// Message direction indicator
353    ///
354    /// Format: 1!a - Single alphabetic character
355    /// Values: I (Input to SWIFT), O (Output from SWIFT)
356    /// Determines message format and processing perspective
357    pub direction: String,
358
359    /// Message type
360    ///
361    /// Format: 3!n - Three numeric digits
362    /// Examples: 103 (Customer Transfer), 202 (Bank Transfer), 940 (Statement)
363    /// Determines validation rules and processing requirements
364    pub message_type: String,
365
366    /// Destination address
367    ///
368    /// Format: 12!c - 12 alphanumeric characters  
369    /// Structure: 8-char BIC + 1-char terminal + 3-char branch
370    /// Identifies the receiving terminal in SWIFT network
371    pub destination_address: String,
372
373    /// Receiver BIC extracted from destination address
374    ///
375    /// Format: 8!c - First 8 characters of destination
376    /// The receiving institution's Bank Identifier Code
377    pub receiver_bic: String,
378
379    /// Message priority
380    ///
381    /// Format: 1!a - Single alphabetic character
382    /// Values: U (Urgent), N (Normal), S (System)
383    /// Determines processing priority and delivery speed
384    pub priority: String,
385
386    /// Delivery monitoring option
387    ///
388    /// Format: 1!n - Single numeric digit (optional)
389    /// Values: 1 (Non-delivery warning), 3 (Delivery notification)
390    /// Specifies delivery confirmation requirements
391    #[serde(skip_serializing_if = "Option::is_none")]
392    pub delivery_monitoring: Option<String>,
393
394    /// Obsolescence period
395    ///
396    /// Format: 3!n - Three numeric digits (optional)
397    /// Range: 003-999 (units of 5 minutes)
398    /// Message validity timeout for automatic cancellation
399    #[serde(skip_serializing_if = "Option::is_none")]
400    pub obsolescence_period: Option<String>,
401}
402
403impl ApplicationHeader {
404    /// Parse application header from block 2 string
405    pub fn parse(block2: &str) -> Result<Self> {
406        if block2.len() < 17 {
407            return Err(ParseError::InvalidBlockStructure {
408                block: "2".to_string(),
409                message: format!(
410                    "Block 2 too short: expected at least 18 characters, got {}",
411                    block2.len()
412                ),
413            });
414        }
415
416        let direction = block2[0..1].to_string();
417        let message_type = block2[1..4].to_string();
418        let destination_address = block2[4..16].to_string();
419        let priority = block2[16..17].to_string();
420
421        let delivery_monitoring = if block2.len() >= 18 {
422            Some(block2[17..18].to_string())
423        } else {
424            None
425        };
426
427        let obsolescence_period = if block2.len() >= 21 {
428            Some(block2[18..21].to_string())
429        } else {
430            None
431        };
432
433        let receiver_bic = destination_address[0..8].to_string();
434
435        Ok(ApplicationHeader {
436            direction,
437            message_type,
438            destination_address,
439            receiver_bic,
440            priority,
441            delivery_monitoring,
442            obsolescence_period,
443        })
444    }
445}
446
447impl std::fmt::Display for ApplicationHeader {
448    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
449        let mut result = format!(
450            "{}{}{}{}",
451            self.direction, self.message_type, self.destination_address, self.priority
452        );
453
454        if let Some(ref delivery_monitoring) = self.delivery_monitoring {
455            result.push_str(delivery_monitoring);
456        }
457
458        if let Some(ref obsolescence_period) = self.obsolescence_period {
459            result.push_str(obsolescence_period);
460        }
461
462        write!(f, "{result}")
463    }
464}
465
466/// **User Header (Block 3): Extended Service Options and Controls**
467///
468/// ## Purpose
469/// The User Header provides optional extended functionality for SWIFT messages,
470/// enabling advanced services, enhanced straight-through processing (STP),
471/// compliance controls, and end-to-end transaction tracking. This header has
472/// become increasingly important for regulatory compliance, particularly with
473/// SWIFT gpi tracking and enhanced payment transparency requirements.
474///
475/// ## Format Specification
476/// - **Block Format**: `{3:{tag:value}{tag:value}...}`
477/// - **Tag Format**: Numeric tags with structured values
478/// - **Nesting**: Tags enclosed in curly braces
479/// - **Optional Block**: Entire block 3 may be omitted
480///
481/// ## Business Context Applications
482/// - **SWIFT gpi Tracking**: End-to-end payment tracking via UETR
483/// - **STP Enhancement**: Validation flags for automated processing
484/// - **Regulatory Compliance**: Sanctions screening and payment controls
485/// - **Service Enhancement**: Additional service options and features
486///
487/// ## Critical Tags for Modern Payments
488/// ### Tag 121: Unique End-to-End Transaction Reference (UETR)
489/// - **Format**: 36 characters UUID (8-4-4-4-12 format)
490/// - **Purpose**: Global payment tracking across entire payment chain
491/// - **Mandatory**: Required for SWIFT gpi participant banks
492/// - **Persistence**: Must remain unchanged through payment lifecycle
493///
494/// ### Tag 119: Validation Flag
495/// - **STP**: Straight-Through Processing capability
496/// - **REMIT**: Remittance information present
497/// - **COV**: Cover payment indicator
498/// - **RFDD**: Request for Direct Debit
499///
500/// ## Service Identifiers
501/// ### Tag 103: Service Identifier
502/// - **Purpose**: Identifies specific SWIFT services
503/// - **Values**: Service-specific codes (e.g., "FIN")
504/// - **Usage**: Mainly for FIN Copy service
505/// - **Processing**: Affects message routing and copying
506///
507/// ### Tag 111: Service Type Identifier  
508/// - **Format**: 3 numeric digits
509/// - **Purpose**: Sub-categorizes service types
510/// - **Common**: "001" for standard processing
511/// - **Impact**: May affect fee calculation
512///
513/// ## Message Control and Reference
514/// ### Tag 108: Message User Reference (MUR)
515/// - **Format**: Up to 16 characters
516/// - **Purpose**: Sender's unique reference
517/// - **Usage**: Transaction tracking and reconciliation
518/// - **Uniqueness**: Should be unique per sender
519///
520/// ### Tag 113: Banking Priority
521/// - **Format**: 4 characters
522/// - **Values**: NORM, HIGH, URGP
523/// - **Purpose**: Internal bank priority handling
524/// - **Note**: Different from network priority
525///
526/// ## Compliance and Screening Tags
527/// ### Tag 433: Sanctions Screening Information
528/// - **AOK**: All OK - Passed screening
529/// - **FPO**: False Positive Override
530/// - **NOK**: Not OK - Requires review
531/// - **Additional**: Optional 20 character details
532///
533/// ### Tag 434: Payment Controls Information
534/// - **Format**: 3-letter code + optional details
535/// - **Purpose**: Payment control status
536/// - **Usage**: Compliance and regulatory controls
537/// - **Processing**: May trigger manual review
538///
539/// ## FIN Copy Service Tags
540/// ### Tag 115: Addressee Information
541/// - **Format**: Up to 32 characters
542/// - **Purpose**: Third-party copy recipient
543/// - **Service**: FIN Copy service only
544/// - **Delivery**: Additional message copy sent
545///
546/// ### Tag 165: Payment Release Information
547/// - **Format**: 3-char code + optional 34 chars
548/// - **Service**: FINInform service
549/// - **Purpose**: Payment release notifications
550/// - **Usage**: Corporate payment factories
551///
552/// ## Message Recovery Tags (MIRS)
553/// ### Tag 106: Message Input Reference (MIR)
554/// - **Format**: 28 characters structured
555/// - **Components**: Date + LT + Session + Sequence
556/// - **Purpose**: Original message reference
557/// - **Usage**: Message recovery and reconciliation
558///
559/// ### Tag 423: Balance Checkpoint
560/// - **Format**: YYMMDDHHMMSS\[ss\]
561/// - **Purpose**: Balance snapshot timing
562/// - **Service**: MIRS recovery service
563/// - **Precision**: Optional hundredths of second
564///
565/// ### Tag 424: Related Reference
566/// - **Format**: Up to 16 characters
567/// - **Purpose**: Links related messages
568/// - **Usage**: Message chains and corrections
569/// - **Service**: MIRS functionality
570///
571/// ## Network Validation Requirements
572/// - **Tag Compatibility**: Some tags require specific services
573/// - **Value Validation**: Each tag has specific format rules
574/// - **Service Subscription**: Tags available per service agreement
575/// - **Mandatory Combinations**: Some tags require others
576///
577/// ## Regional and Regulatory Impact
578/// - **SWIFT gpi**: Tag 121 mandatory for participants
579/// - **EU Regulations**: Enhanced screening requirements
580/// - **US Compliance**: Specific control requirements
581/// - **Local Rules**: Additional regional tag usage
582///
583/// ## STP Processing Impact
584/// ### Validation Flag Effects
585/// - **STP Flag**: Enables full automation
586/// - **Format Restrictions**: Stricter field validation
587/// - **Character Sets**: Limited to STP-safe characters
588/// - **Processing Speed**: Faster automated handling
589///
590/// ## Error Prevention Guidelines
591/// - **UETR Format**: Ensure valid UUID format
592/// - **Service Compatibility**: Verify tag availability
593/// - **Value Formats**: Follow exact specifications
594/// - **Mandatory Rules**: Include required combinations
595///
596/// ## Integration with Other Blocks
597/// - **Block 1**: Service must match subscription
598/// - **Block 2**: Message type affects available tags
599/// - **Block 4**: Validation flags affect field rules
600/// - **Block 5**: Some tags reflected in trailer
601///
602/// ## Compliance Framework
603/// - **Regulatory Mandates**: Screening and control requirements
604/// - **Audit Trail**: Enhanced tracking via UETR
605/// - **Service Agreements**: Tag usage per agreement
606/// - **Privacy Rules**: Data handling requirements
607///
608/// ## Best Practices
609/// - **UETR Generation**: Use proper UUID libraries
610/// - **Reference Uniqueness**: Ensure MUR uniqueness
611/// - **Screening Accuracy**: Accurate screening codes
612/// - **Service Alignment**: Use appropriate service tags
613///
614/// ## Future Evolution
615/// - **ISO 20022 Alignment**: Mapping considerations
616/// - **Enhanced Tracking**: Additional tracking features
617/// - **Compliance Evolution**: New regulatory tags
618/// - **Service Innovation**: New service identifiers
619///
620/// ## See Also
621/// - SWIFT FIN User Handbook: Block 3 Tag Specifications
622/// - SWIFT gpi Standards: UETR Implementation Guide
623/// - STP Guidelines: Validation Flag Requirements
624/// - Compliance Framework: Screening Tag Usage
625#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
626pub struct UserHeader {
627    /// Tag 103 - Service Identifier (3!a) - Mandatory for FINcopy Service
628    #[serde(skip_serializing_if = "Option::is_none")]
629    pub service_identifier: Option<String>,
630
631    /// Tag 113 - Banking Priority (4!x) - Optional
632    #[serde(skip_serializing_if = "Option::is_none")]
633    pub banking_priority: Option<String>,
634
635    /// Tag 108 - Message User Reference (16!x) - Optional
636    #[serde(skip_serializing_if = "Option::is_none")]
637    pub message_user_reference: Option<String>,
638
639    /// Tag 119 - Validation Flag (8c) - Optional (STP, REMIT, RFDD, COV)
640    #[serde(skip_serializing_if = "Option::is_none")]
641    pub validation_flag: Option<String>,
642
643    /// Tag 423 - Balance checkpoint date and time (YYMMDDHHMMSS\[ss\]) - Optional (MIRS only)
644    #[serde(skip_serializing_if = "Option::is_none")]
645    pub balance_checkpoint: Option<BalanceCheckpoint>,
646
647    /// Tag 106 - Message Input Reference MIR (28c) - Optional (MIRS only)
648    #[serde(skip_serializing_if = "Option::is_none")]
649    pub message_input_reference: Option<MessageInputReference>,
650
651    /// Tag 424 - Related reference (16x) - Optional (MIRS only)
652    #[serde(skip_serializing_if = "Option::is_none")]
653    pub related_reference: Option<String>,
654
655    /// Tag 111 - Service type identifier (3!n) - Optional
656    #[serde(skip_serializing_if = "Option::is_none")]
657    pub service_type_identifier: Option<String>,
658
659    /// Tag 121 - Unique end-to-end transaction reference (UUID format) - Mandatory for GPI
660    #[serde(skip_serializing_if = "Option::is_none")]
661    pub unique_end_to_end_reference: Option<String>,
662
663    /// Tag 115 - Addressee Information (32x) - Optional (FINCopy only)
664    #[serde(skip_serializing_if = "Option::is_none")]
665    pub addressee_information: Option<String>,
666
667    /// Tag 165 - Payment release information receiver (3!c/34x) - Optional (FINInform only)
668    #[serde(skip_serializing_if = "Option::is_none")]
669    pub payment_release_information: Option<PaymentReleaseInfo>,
670
671    /// Tag 433 - Sanctions screening information (3!a/\[20x\]) - Optional
672    #[serde(skip_serializing_if = "Option::is_none")]
673    pub sanctions_screening_info: Option<SanctionsScreeningInfo>,
674
675    /// Tag 434 - Payment controls information (3!a/\[20x\]) - Optional
676    #[serde(skip_serializing_if = "Option::is_none")]
677    pub payment_controls_info: Option<PaymentControlsInfo>,
678}
679
680/// Balance checkpoint structure for Tag 423
681#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
682pub struct BalanceCheckpoint {
683    pub date: String, // YYMMDD
684    pub time: String, // HHMMSS
685
686    #[serde(skip_serializing_if = "Option::is_none")]
687    pub hundredths_of_second: Option<String>, // ss (optional)
688}
689
690/// Message Input Reference structure for Tag 106
691#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
692pub struct MessageInputReference {
693    pub date: String,            // YYMMDD
694    pub lt_identifier: String,   // 12 characters
695    pub branch_code: String,     // 3!c
696    pub session_number: String,  // 4!n
697    pub sequence_number: String, // 6!n
698}
699
700/// Payment release information structure for Tag 165
701#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
702pub struct PaymentReleaseInfo {
703    pub code: String, // 3!c
704
705    #[serde(skip_serializing_if = "Option::is_none")]
706    pub additional_info: Option<String>, // 34x (optional)
707}
708
709/// Sanctions screening information structure for Tag 433
710#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
711pub struct SanctionsScreeningInfo {
712    pub code_word: String, // 3!a (AOK, FPO, NOK)
713
714    #[serde(skip_serializing_if = "Option::is_none")]
715    pub additional_info: Option<String>, // 20x (optional)
716}
717
718/// Payment controls information structure for Tag 434
719#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
720pub struct PaymentControlsInfo {
721    pub code_word: String, // 3!a
722
723    #[serde(skip_serializing_if = "Option::is_none")]
724    pub additional_info: Option<String>, // 20x (optional)
725}
726
727impl UserHeader {
728    /// Parse user header from block 3 string using structured parsing
729    pub fn parse(block3: &str) -> Result<Self> {
730        let mut user_header = UserHeader::default();
731
732        // Parse nested tags in format {tag:value}
733        // Simple parsing for now - more sophisticated regex parsing can be added later
734        if block3.contains("{103:")
735            && let Some(start) = block3.find("{103:")
736            && let Some(end) = block3[start..].find('}')
737        {
738            user_header.service_identifier = Some(block3[start + 5..start + end].to_string());
739        }
740
741        if block3.contains("{113:")
742            && let Some(start) = block3.find("{113:")
743            && let Some(end) = block3[start..].find('}')
744        {
745            user_header.banking_priority = Some(block3[start + 5..start + end].to_string());
746        }
747
748        if block3.contains("{108:")
749            && let Some(start) = block3.find("{108:")
750            && let Some(end) = block3[start..].find('}')
751        {
752            user_header.message_user_reference = Some(block3[start + 5..start + end].to_string());
753        }
754
755        if block3.contains("{119:")
756            && let Some(start) = block3.find("{119:")
757            && let Some(end) = block3[start..].find('}')
758        {
759            user_header.validation_flag = Some(block3[start + 5..start + end].to_string());
760        }
761
762        if block3.contains("{423:")
763            && let Some(start) = block3.find("{423:")
764            && let Some(end) = block3[start..].find('}')
765        {
766            let value = &block3[start + 5..start + end];
767            user_header.balance_checkpoint = Self::parse_balance_checkpoint(value);
768        }
769
770        if block3.contains("{106:")
771            && let Some(start) = block3.find("{106:")
772            && let Some(end) = block3[start..].find('}')
773        {
774            let value = &block3[start + 5..start + end];
775            user_header.message_input_reference = Self::parse_message_input_reference(value);
776        }
777
778        if block3.contains("{424:")
779            && let Some(start) = block3.find("{424:")
780            && let Some(end) = block3[start..].find('}')
781        {
782            user_header.related_reference = Some(block3[start + 5..start + end].to_string());
783        }
784
785        if block3.contains("{111:")
786            && let Some(start) = block3.find("{111:")
787            && let Some(end) = block3[start..].find('}')
788        {
789            user_header.service_type_identifier = Some(block3[start + 5..start + end].to_string());
790        }
791
792        if block3.contains("{121:")
793            && let Some(start) = block3.find("{121:")
794            && let Some(end) = block3[start..].find('}')
795        {
796            user_header.unique_end_to_end_reference =
797                Some(block3[start + 5..start + end].to_string());
798        }
799
800        if block3.contains("{115:")
801            && let Some(start) = block3.find("{115:")
802            && let Some(end) = block3[start..].find('}')
803        {
804            user_header.addressee_information = Some(block3[start + 5..start + end].to_string());
805        }
806
807        if block3.contains("{165:")
808            && let Some(start) = block3.find("{165:")
809            && let Some(end) = block3[start..].find('}')
810        {
811            let value = &block3[start + 5..start + end];
812            user_header.payment_release_information = Self::parse_payment_release_info(value);
813        }
814
815        if block3.contains("{433:")
816            && let Some(start) = block3.find("{433:")
817            && let Some(end) = block3[start..].find('}')
818        {
819            let value = &block3[start + 5..start + end];
820            user_header.sanctions_screening_info = Self::parse_sanctions_screening_info(value);
821        }
822
823        if block3.contains("{434:")
824            && let Some(start) = block3.find("{434:")
825            && let Some(end) = block3[start..].find('}')
826        {
827            let value = &block3[start + 5..start + end];
828            user_header.payment_controls_info = Self::parse_payment_controls_info(value);
829        }
830
831        Ok(user_header)
832    }
833
834    /// Parse balance checkpoint from tag value
835    fn parse_balance_checkpoint(value: &str) -> Option<BalanceCheckpoint> {
836        if value.len() >= 12 {
837            Some(BalanceCheckpoint {
838                date: value[0..6].to_string(),
839                time: value[6..12].to_string(),
840                hundredths_of_second: if value.len() > 12 {
841                    Some(value[12..].to_string())
842                } else {
843                    None
844                },
845            })
846        } else {
847            None
848        }
849    }
850
851    /// Parse message input reference from tag value
852    fn parse_message_input_reference(value: &str) -> Option<MessageInputReference> {
853        if value.len() >= 28 {
854            Some(MessageInputReference {
855                date: value[0..6].to_string(),
856                lt_identifier: value[6..18].to_string(),
857                branch_code: value[18..21].to_string(),
858                session_number: value[21..25].to_string(),
859                sequence_number: value[25..].to_string(),
860            })
861        } else {
862            None
863        }
864    }
865
866    /// Parse payment release info from tag value
867    fn parse_payment_release_info(value: &str) -> Option<PaymentReleaseInfo> {
868        if value.len() >= 3 {
869            let code = value[0..3].to_string();
870            let additional_info = if value.len() > 4 && value.chars().nth(3) == Some('/') {
871                Some(value[4..].to_string())
872            } else {
873                None
874            };
875            Some(PaymentReleaseInfo {
876                code,
877                additional_info,
878            })
879        } else {
880            None
881        }
882    }
883
884    /// Parse sanctions screening info from tag value
885    fn parse_sanctions_screening_info(value: &str) -> Option<SanctionsScreeningInfo> {
886        if value.len() >= 3 {
887            let code_word = value[0..3].to_string();
888            let additional_info = if value.len() > 4 && value.chars().nth(3) == Some('/') {
889                Some(value[4..].to_string())
890            } else {
891                None
892            };
893            Some(SanctionsScreeningInfo {
894                code_word,
895                additional_info,
896            })
897        } else {
898            None
899        }
900    }
901
902    /// Parse payment controls info from tag value
903    fn parse_payment_controls_info(value: &str) -> Option<PaymentControlsInfo> {
904        if value.len() >= 3 {
905            let code_word = value[0..3].to_string();
906            let additional_info = if value.len() > 4 && value.chars().nth(3) == Some('/') {
907                Some(value[4..].to_string())
908            } else {
909                None
910            };
911            Some(PaymentControlsInfo {
912                code_word,
913                additional_info,
914            })
915        } else {
916            None
917        }
918    }
919}
920
921impl std::fmt::Display for UserHeader {
922    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
923        let mut result = String::new();
924
925        if let Some(ref service_id) = self.service_identifier {
926            result.push_str(&format!("{{103:{service_id}}}"));
927        }
928
929        if let Some(ref banking_priority) = self.banking_priority {
930            result.push_str(&format!("{{113:{banking_priority}}}"));
931        }
932
933        if let Some(ref message_user_ref) = self.message_user_reference {
934            result.push_str(&format!("{{108:{message_user_ref}}}"));
935        }
936
937        if let Some(ref validation_flag) = self.validation_flag {
938            result.push_str(&format!("{{119:{validation_flag}}}"));
939        }
940
941        if let Some(ref unique_end_to_end_ref) = self.unique_end_to_end_reference {
942            result.push_str(&format!("{{121:{unique_end_to_end_ref}}}"));
943        }
944
945        if let Some(ref service_type_id) = self.service_type_identifier {
946            result.push_str(&format!("{{111:{service_type_id}}}"));
947        }
948
949        if let Some(ref payment_controls) = self.payment_controls_info {
950            let mut value = payment_controls.code_word.clone();
951            if let Some(ref additional) = payment_controls.additional_info {
952                value.push('/');
953                value.push_str(additional);
954            }
955            result.push_str(&format!("{{434:{value}}}"));
956        }
957
958        if let Some(ref payment_release) = self.payment_release_information {
959            let mut value = payment_release.code.clone();
960            if let Some(ref additional) = payment_release.additional_info {
961                value.push('/');
962                value.push_str(additional);
963            }
964            result.push_str(&format!("{{165:{value}}}"));
965        }
966
967        if let Some(ref sanctions) = self.sanctions_screening_info {
968            let mut value = sanctions.code_word.clone();
969            if let Some(ref additional) = sanctions.additional_info {
970                value.push('/');
971                value.push_str(additional);
972            }
973            result.push_str(&format!("{{433:{value}}}"));
974        }
975
976        write!(f, "{result}")
977    }
978}
979
980/// **Trailer (Block 5): Message Security and Control Information**
981///
982/// ## Purpose
983/// The Trailer block provides essential security, authentication, and control
984/// information for SWIFT messages. It ensures message integrity through checksums,
985/// enables duplicate detection, supports message authentication, and provides
986/// system-level control information critical for secure and reliable message delivery.
987///
988/// ## Format Specification
989/// - **Block Format**: `{5:{tag:value}{tag:value}...}`
990/// - **Tag Types**: Three-letter tags with optional values
991/// - **Security Tags**: MAC and CHK for authentication
992/// - **Control Tags**: Various operational controls
993///
994/// ## Business Context Applications
995/// - **Message Integrity**: Checksum validation for data integrity
996/// - **Security Authentication**: MAC for message authentication
997/// - **Duplicate Detection**: Prevention of duplicate processing
998/// - **Operational Control**: Test messages and system controls
999///
1000/// ## Security and Authentication Tags
1001/// ### CHK: Checksum (Mandatory)
1002/// - **Format**: 12 hexadecimal characters
1003/// - **Calculation**: Algorithm-based on message content
1004/// - **Purpose**: Detect transmission errors
1005/// - **Validation**: Automatic by SWIFT network
1006/// - **Failure Action**: Message rejection
1007///
1008/// ### MAC: Message Authentication Code
1009/// - **Format**: Variable length hexadecimal
1010/// - **Algorithm**: Agreed between parties
1011/// - **Purpose**: Authenticate message origin
1012/// - **Usage**: High-value or sensitive messages
1013/// - **Bilateral**: Requires key exchange
1014///
1015/// ## Duplicate Control Tags
1016/// ### PDM: Possible Duplicate Message
1017/// - **Format**: Optional time + MOR
1018/// - **Purpose**: Warn of possible duplicate
1019/// - **Action**: Receiver should check for duplicates
1020/// - **Components**: Time (HHMM) + Message Output Reference
1021///
1022/// ### PDE: Possible Duplicate Emission
1023/// - **Format**: Optional time + MIR  
1024/// - **Purpose**: Sender suspects duplicate sent
1025/// - **Usage**: Network recovery scenarios
1026/// - **Components**: Time (HHMM) + Message Input Reference
1027///
1028/// ## Operational Control Tags
1029/// ### TNG: Test and Training
1030/// - **Format**: Empty tag (presence only)
1031/// - **Purpose**: Identifies test messages
1032/// - **Processing**: Should not affect production
1033/// - **Usage**: Testing and training environments
1034/// - **Warning**: Must not be processed as live
1035///
1036/// ### DLM: Delayed Message
1037/// - **Format**: Empty tag (presence only)
1038/// - **Purpose**: Indicates delayed transmission
1039/// - **Cause**: Network issues or recovery
1040/// - **Action**: Check value dates and cut-offs
1041///
1042/// ## Reference and Tracking Tags
1043/// ### MRF: Message Reference
1044/// - **Format**: Date + Time + MIR
1045/// - **Purpose**: Reference related messages
1046/// - **Usage**: Corrections and cancellations
1047/// - **Components**: YYMMDD + HHMM + full MIR
1048///
1049/// ### SYS: System Originated Message
1050/// - **Format**: Optional time + MIR
1051/// - **Purpose**: System-generated messages
1052/// - **Examples**: Automatic responses
1053/// - **Processing**: May have special handling
1054///
1055/// ## Message Reference Structures
1056/// ### Message Input Reference (MIR)
1057/// - **Date**: YYMMDD format
1058/// - **LT Identifier**: 12-character sending LT
1059/// - **Session**: 4-digit session number
1060/// - **Sequence**: 6-digit sequence number
1061/// - **Usage**: Unique message identification
1062///
1063/// ### Message Output Reference (MOR)
1064/// - **Format**: Same structure as MIR
1065/// - **Perspective**: Receiver's reference
1066/// - **Purpose**: Delivery confirmation
1067/// - **Tracking**: End-to-end message tracking
1068///
1069/// ## Network Validation Requirements
1070/// - **CHK Mandatory**: All messages must have checksum
1071/// - **Tag Order**: Specific ordering requirements
1072/// - **Format Compliance**: Exact format specifications
1073/// - **Value Validation**: Tag-specific validations
1074///
1075/// ## Security Considerations
1076/// ### Checksum Protection
1077/// - **Coverage**: Entire message content
1078/// - **Algorithm**: SWIFT-specified calculation
1079/// - **Tampering**: Detects any modification
1080/// - **Reliability**: Very low false positive rate
1081///
1082/// ### MAC Authentication
1083/// - **Bilateral Agreement**: Key management required
1084/// - **Algorithm Choice**: Per agreement
1085/// - **Non-repudiation**: Proves message origin
1086/// - **Legal Standing**: Admissible evidence
1087///
1088/// ## Duplicate Detection Mechanisms
1089/// ### System Design
1090/// - **Detection Window**: Configurable timeframe
1091/// - **Reference Tracking**: MIR/MOR correlation
1092/// - **Recovery Support**: Post-incident reconciliation
1093/// - **Audit Trail**: Complete duplicate history
1094///
1095/// ### Processing Rules
1096/// - **PDM Messages**: Manual review recommended
1097/// - **Duplicate Window**: Typically 24-48 hours
1098/// - **Action Required**: Verify before processing
1099/// - **Documentation**: Record resolution actions
1100///
1101/// ## Operational Guidelines
1102/// ### Test Message Handling
1103/// - **TNG Identification**: Clear test marking
1104/// - **Environment Separation**: Test vs production
1105/// - **Processing Prevention**: Automatic filtering
1106/// - **Audit Exclusion**: Separate test reporting
1107///
1108/// ### Delayed Message Processing
1109/// - **DLM Recognition**: Special handling required
1110/// - **Value Date Check**: Verify still valid
1111/// - **Cut-off Impact**: May miss deadlines
1112/// - **Notification**: Alert relevant parties
1113///
1114/// ## Error Prevention Guidelines
1115/// - **CHK Calculation**: Automatic by system
1116/// - **Tag Formatting**: Follow exact specifications
1117/// - **Reference Accuracy**: Verify MIR/MOR format
1118/// - **Test Separation**: Clear test identification
1119///
1120/// ## Integration with Other Blocks
1121/// - **Block 1-4**: Content for checksum calculation
1122/// - **Block 1**: Session/sequence for references
1123/// - **Block 2**: Message type affects trailer options
1124/// - **Block 3**: Some services require specific tags
1125///
1126/// ## Compliance Framework
1127/// - **Security Standards**: Cryptographic requirements
1128/// - **Audit Requirements**: Trailer preservation
1129/// - **Legal Admissibility**: Authentication standards
1130/// - **Regulatory Compliance**: Security controls
1131///
1132/// ## Recovery and Reconciliation
1133/// ### Message Recovery
1134/// - **Reference Tracking**: Via MIR/MOR
1135/// - **Duplicate Resolution**: PDM/PDE handling
1136/// - **Audit Support**: Complete tag history
1137/// - **Dispute Resolution**: Authentication proof
1138///
1139/// ### System Recovery
1140/// - **Checkpoint References**: Recovery points
1141/// - **Sequence Verification**: Gap detection
1142/// - **Duplicate Prevention**: During recovery
1143/// - **Integrity Validation**: CHK verification
1144///
1145/// ## Best Practices
1146/// - **Security First**: Always validate CHK
1147/// - **MAC Usage**: For high-value messages
1148/// - **Duplicate Vigilance**: Check PDM warnings
1149/// - **Test Clarity**: Clearly mark test messages
1150///
1151/// ## See Also
1152/// - SWIFT FIN Security Guide: Authentication Standards
1153/// - Checksum Algorithms: Technical Specifications
1154/// - Duplicate Detection: Best Practices Guide
1155/// - MAC Implementation: Bilateral Agreement Templates
1156#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
1157pub struct Trailer {
1158    /// CHK - Checksum (12!h) - Mandatory
1159    #[serde(skip_serializing_if = "Option::is_none")]
1160    pub checksum: Option<String>,
1161
1162    /// TNG - Test & Training Message - Optional (empty tag)
1163    #[serde(skip_serializing_if = "Option::is_none")]
1164    pub test_and_training: Option<bool>,
1165
1166    /// PDE - Possible Duplicate Emission - Optional
1167    #[serde(skip_serializing_if = "Option::is_none")]
1168    pub possible_duplicate_emission: Option<PossibleDuplicateEmission>,
1169
1170    /// DLM - Delayed Message - Optional (empty tag)
1171    #[serde(skip_serializing_if = "Option::is_none")]
1172    pub delayed_message: Option<bool>,
1173
1174    /// MRF - Message Reference - Optional
1175    #[serde(skip_serializing_if = "Option::is_none")]
1176    pub message_reference: Option<MessageReference>,
1177
1178    /// PDM - Possible Duplicate Message - Optional
1179    #[serde(skip_serializing_if = "Option::is_none")]
1180    pub possible_duplicate_message: Option<PossibleDuplicateMessage>,
1181
1182    /// SYS - System Originated Message - Optional
1183    #[serde(skip_serializing_if = "Option::is_none")]
1184    pub system_originated_message: Option<SystemOriginatedMessage>,
1185
1186    /// MAC - Message Authentication Code - Optional
1187    #[serde(skip_serializing_if = "Option::is_none")]
1188    pub mac: Option<String>,
1189}
1190
1191/// Possible Duplicate Emission structure for PDE tag
1192#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1193pub struct PossibleDuplicateEmission {
1194    #[serde(skip_serializing_if = "Option::is_none")]
1195    pub time: Option<String>, // HHMM (optional)
1196
1197    #[serde(skip_serializing_if = "Option::is_none")]
1198    pub message_input_reference: Option<MessageInputReference>, // MIR (optional)
1199}
1200
1201/// Message Reference structure for MRF tag
1202#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1203pub struct MessageReference {
1204    pub date: String,                                   // YYMMDD
1205    pub full_time: String,                              // HHMM
1206    pub message_input_reference: MessageInputReference, // MIR
1207}
1208
1209/// Possible Duplicate Message structure for PDM tag
1210#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1211pub struct PossibleDuplicateMessage {
1212    #[serde(skip_serializing_if = "Option::is_none")]
1213    pub time: Option<String>, // HHMM (optional)
1214
1215    #[serde(skip_serializing_if = "Option::is_none")]
1216    pub message_output_reference: Option<MessageOutputReference>, // MOR (optional)
1217}
1218
1219/// Message Output Reference structure (similar to MIR but for output)
1220#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1221pub struct MessageOutputReference {
1222    pub date: String,            // YYMMDD
1223    pub lt_identifier: String,   // 12 characters
1224    pub branch_code: String,     // 3!c
1225    pub session_number: String,  // 4!n
1226    pub sequence_number: String, // 6!n
1227}
1228
1229/// System Originated Message structure for SYS tag
1230#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1231pub struct SystemOriginatedMessage {
1232    #[serde(skip_serializing_if = "Option::is_none")]
1233    pub time: Option<String>, // HHMM (optional)
1234
1235    #[serde(skip_serializing_if = "Option::is_none")]
1236    pub message_input_reference: Option<MessageInputReference>, // MIR (optional)
1237}
1238
1239impl Trailer {
1240    /// Parse trailer from block 5 string using structured parsing
1241    pub fn parse(block5: &str) -> Result<Self> {
1242        let mut trailer = Trailer::default();
1243
1244        // Extract common tags if present
1245        if block5.contains("{CHK:")
1246            && let Some(start) = block5.find("{CHK:")
1247            && let Some(end) = block5[start..].find('}')
1248        {
1249            trailer.checksum = Some(block5[start + 5..start + end].to_string());
1250        }
1251
1252        if block5.contains("{TNG}") {
1253            trailer.test_and_training = Some(true);
1254        }
1255
1256        if block5.contains("{DLM}") {
1257            trailer.delayed_message = Some(true);
1258        }
1259
1260        if block5.contains("{MAC:")
1261            && let Some(start) = block5.find("{MAC:")
1262            && let Some(end) = block5[start..].find('}')
1263        {
1264            trailer.mac = Some(block5[start + 5..start + end].to_string());
1265        }
1266
1267        // More complex parsing for structured tags can be added here
1268        // For now, implementing basic tag extraction
1269
1270        Ok(trailer)
1271    }
1272}
1273
1274impl std::fmt::Display for Trailer {
1275    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1276        let mut result = String::new();
1277
1278        if let Some(ref checksum) = self.checksum {
1279            result.push_str(&format!("{{CHK:{checksum}}}"));
1280        }
1281
1282        if let Some(true) = self.test_and_training {
1283            result.push_str("{TNG}");
1284        }
1285
1286        if let Some(true) = self.delayed_message {
1287            result.push_str("{DLM}");
1288        }
1289
1290        if let Some(ref possible_duplicate_emission) = self.possible_duplicate_emission {
1291            result.push_str(&format!(
1292                "{{PDE:{}}}",
1293                possible_duplicate_emission.time.as_deref().unwrap_or("")
1294            ));
1295        }
1296
1297        if let Some(ref message_reference) = self.message_reference {
1298            result.push_str(&format!("{{MRF:{}}}", message_reference.date));
1299        }
1300
1301        if let Some(ref mac) = self.mac {
1302            result.push_str(&format!("{{MAC:{mac}}}"));
1303        }
1304
1305        write!(f, "{result}")
1306    }
1307}