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            if let Some(start) = block3.find("{103:") {
736                if let Some(end) = block3[start..].find('}') {
737                    user_header.service_identifier =
738                        Some(block3[start + 5..start + end].to_string());
739                }
740            }
741        }
742
743        if block3.contains("{113:") {
744            if let Some(start) = block3.find("{113:") {
745                if let Some(end) = block3[start..].find('}') {
746                    user_header.banking_priority = Some(block3[start + 5..start + end].to_string());
747                }
748            }
749        }
750
751        if block3.contains("{108:") {
752            if let Some(start) = block3.find("{108:") {
753                if let Some(end) = block3[start..].find('}') {
754                    user_header.message_user_reference =
755                        Some(block3[start + 5..start + end].to_string());
756                }
757            }
758        }
759
760        if block3.contains("{119:") {
761            if let Some(start) = block3.find("{119:") {
762                if let Some(end) = block3[start..].find('}') {
763                    user_header.validation_flag = Some(block3[start + 5..start + end].to_string());
764                }
765            }
766        }
767
768        if block3.contains("{423:") {
769            if let Some(start) = block3.find("{423:") {
770                if let Some(end) = block3[start..].find('}') {
771                    let value = &block3[start + 5..start + end];
772                    user_header.balance_checkpoint = Self::parse_balance_checkpoint(value);
773                }
774            }
775        }
776
777        if block3.contains("{106:") {
778            if let Some(start) = block3.find("{106:") {
779                if let Some(end) = block3[start..].find('}') {
780                    let value = &block3[start + 5..start + end];
781                    user_header.message_input_reference =
782                        Self::parse_message_input_reference(value);
783                }
784            }
785        }
786
787        if block3.contains("{424:") {
788            if let Some(start) = block3.find("{424:") {
789                if let Some(end) = block3[start..].find('}') {
790                    user_header.related_reference =
791                        Some(block3[start + 5..start + end].to_string());
792                }
793            }
794        }
795
796        if block3.contains("{111:") {
797            if let Some(start) = block3.find("{111:") {
798                if let Some(end) = block3[start..].find('}') {
799                    user_header.service_type_identifier =
800                        Some(block3[start + 5..start + end].to_string());
801                }
802            }
803        }
804
805        if block3.contains("{121:") {
806            if let Some(start) = block3.find("{121:") {
807                if let Some(end) = block3[start..].find('}') {
808                    user_header.unique_end_to_end_reference =
809                        Some(block3[start + 5..start + end].to_string());
810                }
811            }
812        }
813
814        if block3.contains("{115:") {
815            if let Some(start) = block3.find("{115:") {
816                if let Some(end) = block3[start..].find('}') {
817                    user_header.addressee_information =
818                        Some(block3[start + 5..start + end].to_string());
819                }
820            }
821        }
822
823        if block3.contains("{165:") {
824            if let Some(start) = block3.find("{165:") {
825                if let Some(end) = block3[start..].find('}') {
826                    let value = &block3[start + 5..start + end];
827                    user_header.payment_release_information =
828                        Self::parse_payment_release_info(value);
829                }
830            }
831        }
832
833        if block3.contains("{433:") {
834            if let Some(start) = block3.find("{433:") {
835                if let Some(end) = block3[start..].find('}') {
836                    let value = &block3[start + 5..start + end];
837                    user_header.sanctions_screening_info =
838                        Self::parse_sanctions_screening_info(value);
839                }
840            }
841        }
842
843        if block3.contains("{434:") {
844            if let Some(start) = block3.find("{434:") {
845                if let Some(end) = block3[start..].find('}') {
846                    let value = &block3[start + 5..start + end];
847                    user_header.payment_controls_info = Self::parse_payment_controls_info(value);
848                }
849            }
850        }
851
852        Ok(user_header)
853    }
854
855    /// Parse balance checkpoint from tag value
856    fn parse_balance_checkpoint(value: &str) -> Option<BalanceCheckpoint> {
857        if value.len() >= 12 {
858            Some(BalanceCheckpoint {
859                date: value[0..6].to_string(),
860                time: value[6..12].to_string(),
861                hundredths_of_second: if value.len() > 12 {
862                    Some(value[12..].to_string())
863                } else {
864                    None
865                },
866            })
867        } else {
868            None
869        }
870    }
871
872    /// Parse message input reference from tag value
873    fn parse_message_input_reference(value: &str) -> Option<MessageInputReference> {
874        if value.len() >= 28 {
875            Some(MessageInputReference {
876                date: value[0..6].to_string(),
877                lt_identifier: value[6..18].to_string(),
878                branch_code: value[18..21].to_string(),
879                session_number: value[21..25].to_string(),
880                sequence_number: value[25..].to_string(),
881            })
882        } else {
883            None
884        }
885    }
886
887    /// Parse payment release info from tag value
888    fn parse_payment_release_info(value: &str) -> Option<PaymentReleaseInfo> {
889        if value.len() >= 3 {
890            let code = value[0..3].to_string();
891            let additional_info = if value.len() > 4 && value.chars().nth(3) == Some('/') {
892                Some(value[4..].to_string())
893            } else {
894                None
895            };
896            Some(PaymentReleaseInfo {
897                code,
898                additional_info,
899            })
900        } else {
901            None
902        }
903    }
904
905    /// Parse sanctions screening info from tag value
906    fn parse_sanctions_screening_info(value: &str) -> Option<SanctionsScreeningInfo> {
907        if value.len() >= 3 {
908            let code_word = value[0..3].to_string();
909            let additional_info = if value.len() > 4 && value.chars().nth(3) == Some('/') {
910                Some(value[4..].to_string())
911            } else {
912                None
913            };
914            Some(SanctionsScreeningInfo {
915                code_word,
916                additional_info,
917            })
918        } else {
919            None
920        }
921    }
922
923    /// Parse payment controls info from tag value
924    fn parse_payment_controls_info(value: &str) -> Option<PaymentControlsInfo> {
925        if value.len() >= 3 {
926            let code_word = value[0..3].to_string();
927            let additional_info = if value.len() > 4 && value.chars().nth(3) == Some('/') {
928                Some(value[4..].to_string())
929            } else {
930                None
931            };
932            Some(PaymentControlsInfo {
933                code_word,
934                additional_info,
935            })
936        } else {
937            None
938        }
939    }
940}
941
942impl std::fmt::Display for UserHeader {
943    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
944        let mut result = String::new();
945
946        if let Some(ref service_id) = self.service_identifier {
947            result.push_str(&format!("{{103:{service_id}}}"));
948        }
949
950        if let Some(ref banking_priority) = self.banking_priority {
951            result.push_str(&format!("{{113:{banking_priority}}}"));
952        }
953
954        if let Some(ref message_user_ref) = self.message_user_reference {
955            result.push_str(&format!("{{108:{message_user_ref}}}"));
956        }
957
958        if let Some(ref validation_flag) = self.validation_flag {
959            result.push_str(&format!("{{119:{validation_flag}}}"));
960        }
961
962        if let Some(ref unique_end_to_end_ref) = self.unique_end_to_end_reference {
963            result.push_str(&format!("{{121:{unique_end_to_end_ref}}}"));
964        }
965
966        if let Some(ref service_type_id) = self.service_type_identifier {
967            result.push_str(&format!("{{111:{service_type_id}}}"));
968        }
969
970        if let Some(ref payment_controls) = self.payment_controls_info {
971            let mut value = payment_controls.code_word.clone();
972            if let Some(ref additional) = payment_controls.additional_info {
973                value.push('/');
974                value.push_str(additional);
975            }
976            result.push_str(&format!("{{434:{value}}}"));
977        }
978
979        if let Some(ref payment_release) = self.payment_release_information {
980            let mut value = payment_release.code.clone();
981            if let Some(ref additional) = payment_release.additional_info {
982                value.push('/');
983                value.push_str(additional);
984            }
985            result.push_str(&format!("{{165:{value}}}"));
986        }
987
988        if let Some(ref sanctions) = self.sanctions_screening_info {
989            let mut value = sanctions.code_word.clone();
990            if let Some(ref additional) = sanctions.additional_info {
991                value.push('/');
992                value.push_str(additional);
993            }
994            result.push_str(&format!("{{433:{value}}}"));
995        }
996
997        write!(f, "{result}")
998    }
999}
1000
1001/// **Trailer (Block 5): Message Security and Control Information**
1002///
1003/// ## Purpose
1004/// The Trailer block provides essential security, authentication, and control
1005/// information for SWIFT messages. It ensures message integrity through checksums,
1006/// enables duplicate detection, supports message authentication, and provides
1007/// system-level control information critical for secure and reliable message delivery.
1008///
1009/// ## Format Specification
1010/// - **Block Format**: `{5:{tag:value}{tag:value}...}`
1011/// - **Tag Types**: Three-letter tags with optional values
1012/// - **Security Tags**: MAC and CHK for authentication
1013/// - **Control Tags**: Various operational controls
1014///
1015/// ## Business Context Applications
1016/// - **Message Integrity**: Checksum validation for data integrity
1017/// - **Security Authentication**: MAC for message authentication
1018/// - **Duplicate Detection**: Prevention of duplicate processing
1019/// - **Operational Control**: Test messages and system controls
1020///
1021/// ## Security and Authentication Tags
1022/// ### CHK: Checksum (Mandatory)
1023/// - **Format**: 12 hexadecimal characters
1024/// - **Calculation**: Algorithm-based on message content
1025/// - **Purpose**: Detect transmission errors
1026/// - **Validation**: Automatic by SWIFT network
1027/// - **Failure Action**: Message rejection
1028///
1029/// ### MAC: Message Authentication Code
1030/// - **Format**: Variable length hexadecimal
1031/// - **Algorithm**: Agreed between parties
1032/// - **Purpose**: Authenticate message origin
1033/// - **Usage**: High-value or sensitive messages
1034/// - **Bilateral**: Requires key exchange
1035///
1036/// ## Duplicate Control Tags
1037/// ### PDM: Possible Duplicate Message
1038/// - **Format**: Optional time + MOR
1039/// - **Purpose**: Warn of possible duplicate
1040/// - **Action**: Receiver should check for duplicates
1041/// - **Components**: Time (HHMM) + Message Output Reference
1042///
1043/// ### PDE: Possible Duplicate Emission
1044/// - **Format**: Optional time + MIR  
1045/// - **Purpose**: Sender suspects duplicate sent
1046/// - **Usage**: Network recovery scenarios
1047/// - **Components**: Time (HHMM) + Message Input Reference
1048///
1049/// ## Operational Control Tags
1050/// ### TNG: Test and Training
1051/// - **Format**: Empty tag (presence only)
1052/// - **Purpose**: Identifies test messages
1053/// - **Processing**: Should not affect production
1054/// - **Usage**: Testing and training environments
1055/// - **Warning**: Must not be processed as live
1056///
1057/// ### DLM: Delayed Message
1058/// - **Format**: Empty tag (presence only)
1059/// - **Purpose**: Indicates delayed transmission
1060/// - **Cause**: Network issues or recovery
1061/// - **Action**: Check value dates and cut-offs
1062///
1063/// ## Reference and Tracking Tags
1064/// ### MRF: Message Reference
1065/// - **Format**: Date + Time + MIR
1066/// - **Purpose**: Reference related messages
1067/// - **Usage**: Corrections and cancellations
1068/// - **Components**: YYMMDD + HHMM + full MIR
1069///
1070/// ### SYS: System Originated Message
1071/// - **Format**: Optional time + MIR
1072/// - **Purpose**: System-generated messages
1073/// - **Examples**: Automatic responses
1074/// - **Processing**: May have special handling
1075///
1076/// ## Message Reference Structures
1077/// ### Message Input Reference (MIR)
1078/// - **Date**: YYMMDD format
1079/// - **LT Identifier**: 12-character sending LT
1080/// - **Session**: 4-digit session number
1081/// - **Sequence**: 6-digit sequence number
1082/// - **Usage**: Unique message identification
1083///
1084/// ### Message Output Reference (MOR)
1085/// - **Format**: Same structure as MIR
1086/// - **Perspective**: Receiver's reference
1087/// - **Purpose**: Delivery confirmation
1088/// - **Tracking**: End-to-end message tracking
1089///
1090/// ## Network Validation Requirements
1091/// - **CHK Mandatory**: All messages must have checksum
1092/// - **Tag Order**: Specific ordering requirements
1093/// - **Format Compliance**: Exact format specifications
1094/// - **Value Validation**: Tag-specific validations
1095///
1096/// ## Security Considerations
1097/// ### Checksum Protection
1098/// - **Coverage**: Entire message content
1099/// - **Algorithm**: SWIFT-specified calculation
1100/// - **Tampering**: Detects any modification
1101/// - **Reliability**: Very low false positive rate
1102///
1103/// ### MAC Authentication
1104/// - **Bilateral Agreement**: Key management required
1105/// - **Algorithm Choice**: Per agreement
1106/// - **Non-repudiation**: Proves message origin
1107/// - **Legal Standing**: Admissible evidence
1108///
1109/// ## Duplicate Detection Mechanisms
1110/// ### System Design
1111/// - **Detection Window**: Configurable timeframe
1112/// - **Reference Tracking**: MIR/MOR correlation
1113/// - **Recovery Support**: Post-incident reconciliation
1114/// - **Audit Trail**: Complete duplicate history
1115///
1116/// ### Processing Rules
1117/// - **PDM Messages**: Manual review recommended
1118/// - **Duplicate Window**: Typically 24-48 hours
1119/// - **Action Required**: Verify before processing
1120/// - **Documentation**: Record resolution actions
1121///
1122/// ## Operational Guidelines
1123/// ### Test Message Handling
1124/// - **TNG Identification**: Clear test marking
1125/// - **Environment Separation**: Test vs production
1126/// - **Processing Prevention**: Automatic filtering
1127/// - **Audit Exclusion**: Separate test reporting
1128///
1129/// ### Delayed Message Processing
1130/// - **DLM Recognition**: Special handling required
1131/// - **Value Date Check**: Verify still valid
1132/// - **Cut-off Impact**: May miss deadlines
1133/// - **Notification**: Alert relevant parties
1134///
1135/// ## Error Prevention Guidelines
1136/// - **CHK Calculation**: Automatic by system
1137/// - **Tag Formatting**: Follow exact specifications
1138/// - **Reference Accuracy**: Verify MIR/MOR format
1139/// - **Test Separation**: Clear test identification
1140///
1141/// ## Integration with Other Blocks
1142/// - **Block 1-4**: Content for checksum calculation
1143/// - **Block 1**: Session/sequence for references
1144/// - **Block 2**: Message type affects trailer options
1145/// - **Block 3**: Some services require specific tags
1146///
1147/// ## Compliance Framework
1148/// - **Security Standards**: Cryptographic requirements
1149/// - **Audit Requirements**: Trailer preservation
1150/// - **Legal Admissibility**: Authentication standards
1151/// - **Regulatory Compliance**: Security controls
1152///
1153/// ## Recovery and Reconciliation
1154/// ### Message Recovery
1155/// - **Reference Tracking**: Via MIR/MOR
1156/// - **Duplicate Resolution**: PDM/PDE handling
1157/// - **Audit Support**: Complete tag history
1158/// - **Dispute Resolution**: Authentication proof
1159///
1160/// ### System Recovery
1161/// - **Checkpoint References**: Recovery points
1162/// - **Sequence Verification**: Gap detection
1163/// - **Duplicate Prevention**: During recovery
1164/// - **Integrity Validation**: CHK verification
1165///
1166/// ## Best Practices
1167/// - **Security First**: Always validate CHK
1168/// - **MAC Usage**: For high-value messages
1169/// - **Duplicate Vigilance**: Check PDM warnings
1170/// - **Test Clarity**: Clearly mark test messages
1171///
1172/// ## See Also
1173/// - SWIFT FIN Security Guide: Authentication Standards
1174/// - Checksum Algorithms: Technical Specifications
1175/// - Duplicate Detection: Best Practices Guide
1176/// - MAC Implementation: Bilateral Agreement Templates
1177#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
1178pub struct Trailer {
1179    /// CHK - Checksum (12!h) - Mandatory
1180    #[serde(skip_serializing_if = "Option::is_none")]
1181    pub checksum: Option<String>,
1182
1183    /// TNG - Test & Training Message - Optional (empty tag)
1184    #[serde(skip_serializing_if = "Option::is_none")]
1185    pub test_and_training: Option<bool>,
1186
1187    /// PDE - Possible Duplicate Emission - Optional
1188    #[serde(skip_serializing_if = "Option::is_none")]
1189    pub possible_duplicate_emission: Option<PossibleDuplicateEmission>,
1190
1191    /// DLM - Delayed Message - Optional (empty tag)
1192    #[serde(skip_serializing_if = "Option::is_none")]
1193    pub delayed_message: Option<bool>,
1194
1195    /// MRF - Message Reference - Optional
1196    #[serde(skip_serializing_if = "Option::is_none")]
1197    pub message_reference: Option<MessageReference>,
1198
1199    /// PDM - Possible Duplicate Message - Optional
1200    #[serde(skip_serializing_if = "Option::is_none")]
1201    pub possible_duplicate_message: Option<PossibleDuplicateMessage>,
1202
1203    /// SYS - System Originated Message - Optional
1204    #[serde(skip_serializing_if = "Option::is_none")]
1205    pub system_originated_message: Option<SystemOriginatedMessage>,
1206
1207    /// MAC - Message Authentication Code - Optional
1208    #[serde(skip_serializing_if = "Option::is_none")]
1209    pub mac: Option<String>,
1210}
1211
1212/// Possible Duplicate Emission structure for PDE tag
1213#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1214pub struct PossibleDuplicateEmission {
1215    #[serde(skip_serializing_if = "Option::is_none")]
1216    pub time: Option<String>, // HHMM (optional)
1217
1218    #[serde(skip_serializing_if = "Option::is_none")]
1219    pub message_input_reference: Option<MessageInputReference>, // MIR (optional)
1220}
1221
1222/// Message Reference structure for MRF tag
1223#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1224pub struct MessageReference {
1225    pub date: String,                                   // YYMMDD
1226    pub full_time: String,                              // HHMM
1227    pub message_input_reference: MessageInputReference, // MIR
1228}
1229
1230/// Possible Duplicate Message structure for PDM tag
1231#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1232pub struct PossibleDuplicateMessage {
1233    #[serde(skip_serializing_if = "Option::is_none")]
1234    pub time: Option<String>, // HHMM (optional)
1235
1236    #[serde(skip_serializing_if = "Option::is_none")]
1237    pub message_output_reference: Option<MessageOutputReference>, // MOR (optional)
1238}
1239
1240/// Message Output Reference structure (similar to MIR but for output)
1241#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1242pub struct MessageOutputReference {
1243    pub date: String,            // YYMMDD
1244    pub lt_identifier: String,   // 12 characters
1245    pub branch_code: String,     // 3!c
1246    pub session_number: String,  // 4!n
1247    pub sequence_number: String, // 6!n
1248}
1249
1250/// System Originated Message structure for SYS tag
1251#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1252pub struct SystemOriginatedMessage {
1253    #[serde(skip_serializing_if = "Option::is_none")]
1254    pub time: Option<String>, // HHMM (optional)
1255
1256    #[serde(skip_serializing_if = "Option::is_none")]
1257    pub message_input_reference: Option<MessageInputReference>, // MIR (optional)
1258}
1259
1260impl Trailer {
1261    /// Parse trailer from block 5 string using structured parsing
1262    pub fn parse(block5: &str) -> Result<Self> {
1263        let mut trailer = Trailer::default();
1264
1265        // Extract common tags if present
1266        if block5.contains("{CHK:") {
1267            if let Some(start) = block5.find("{CHK:") {
1268                if let Some(end) = block5[start..].find('}') {
1269                    trailer.checksum = Some(block5[start + 5..start + end].to_string());
1270                }
1271            }
1272        }
1273
1274        if block5.contains("{TNG}") {
1275            trailer.test_and_training = Some(true);
1276        }
1277
1278        if block5.contains("{DLM}") {
1279            trailer.delayed_message = Some(true);
1280        }
1281
1282        if block5.contains("{MAC:") {
1283            if let Some(start) = block5.find("{MAC:") {
1284                if let Some(end) = block5[start..].find('}') {
1285                    trailer.mac = Some(block5[start + 5..start + end].to_string());
1286                }
1287            }
1288        }
1289
1290        // More complex parsing for structured tags can be added here
1291        // For now, implementing basic tag extraction
1292
1293        Ok(trailer)
1294    }
1295}
1296
1297impl std::fmt::Display for Trailer {
1298    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1299        let mut result = String::new();
1300
1301        if let Some(ref checksum) = self.checksum {
1302            result.push_str(&format!("{{CHK:{checksum}}}"));
1303        }
1304
1305        if let Some(true) = self.test_and_training {
1306            result.push_str("{TNG}");
1307        }
1308
1309        if let Some(true) = self.delayed_message {
1310            result.push_str("{DLM}");
1311        }
1312
1313        if let Some(ref possible_duplicate_emission) = self.possible_duplicate_emission {
1314            result.push_str(&format!(
1315                "{{PDE:{}}}",
1316                possible_duplicate_emission.time.as_deref().unwrap_or("")
1317            ));
1318        }
1319
1320        if let Some(ref message_reference) = self.message_reference {
1321            result.push_str(&format!("{{MRF:{}}}", message_reference.date));
1322        }
1323
1324        if let Some(ref mac) = self.mac {
1325            result.push_str(&format!("{{MAC:{mac}}}"));
1326        }
1327
1328        write!(f, "{result}")
1329    }
1330}