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}