Skip to main content

uls_core/records/
header.rs

1//! HD (Header) record type - License/Application Header.
2//!
3//! The HD record contains the main form data that carries to the license.
4
5use chrono::NaiveDate;
6use serde::{Deserialize, Serialize};
7
8use super::common::*;
9
10/// HD record - License/Application Header.
11/// This is the primary record for a license or application.
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct HeaderRecord {
14    // Required fields
15    pub unique_system_identifier: i64,
16
17    // Optional fields
18    pub uls_file_number: Option<String>,
19    pub ebf_number: Option<String>,
20    pub call_sign: Option<String>,
21    pub license_status: Option<char>,
22    pub radio_service_code: Option<String>,
23    pub grant_date: Option<NaiveDate>,
24    pub expired_date: Option<NaiveDate>,
25    pub cancellation_date: Option<NaiveDate>,
26    pub eligibility_rule_num: Option<String>,
27    pub applicant_type_code_reserved: Option<char>,
28    pub alien: Option<char>,
29    pub alien_government: Option<char>,
30    pub alien_corporation: Option<char>,
31    pub alien_officer: Option<char>,
32    pub alien_control: Option<char>,
33    pub revoked: Option<char>,
34    pub convicted: Option<char>,
35    pub adjudged: Option<char>,
36    pub involved_reserved: Option<char>,
37    pub common_carrier: Option<char>,
38    pub non_common_carrier: Option<char>,
39    pub private_comm: Option<char>,
40    pub fixed: Option<char>,
41    pub mobile: Option<char>,
42    pub radiolocation: Option<char>,
43    pub satellite: Option<char>,
44    pub developmental_or_sta: Option<char>,
45    pub interconnected_service: Option<char>,
46    pub certifier_first_name: Option<String>,
47    pub certifier_mi: Option<char>,
48    pub certifier_last_name: Option<String>,
49    pub certifier_suffix: Option<String>,
50    pub certifier_title: Option<String>,
51    pub sex: Option<char>,
52    pub african_american: Option<char>,
53    pub native_american: Option<char>,
54    pub hawaiian: Option<char>,
55    pub asian: Option<char>,
56    pub white: Option<char>,
57    pub ethnicity: Option<char>,
58    pub effective_date: Option<NaiveDate>,
59    pub last_action_date: Option<NaiveDate>,
60    pub auction_id: Option<i32>,
61    pub reg_stat_broad_serv: Option<char>,
62    pub band_manager: Option<char>,
63    pub type_serv_broad_serv: Option<char>,
64    pub alien_ruling: Option<char>,
65    pub licensee_name_change: Option<char>,
66    pub whitespace_ind: Option<char>,
67    pub additional_cert_choice: Option<char>,
68    pub additional_cert_answer: Option<char>,
69    pub discontinuation_ind: Option<char>,
70    pub regulatory_compliance_ind: Option<char>,
71    pub eligibility_cert_900: Option<char>,
72    pub transition_plan_cert_900: Option<char>,
73    pub return_spectrum_cert_900: Option<char>,
74    pub payment_cert_900: Option<char>,
75}
76
77impl HeaderRecord {
78    /// Parse a header record from pipe-delimited fields.
79    pub fn from_fields(fields: &[&str]) -> Self {
80        Self {
81            unique_system_identifier: parse_i64_or_default(fields.get(1).unwrap_or(&"")),
82            uls_file_number: parse_opt_string(fields.get(2).unwrap_or(&"")),
83            ebf_number: parse_opt_string(fields.get(3).unwrap_or(&"")),
84            call_sign: parse_opt_string(fields.get(4).unwrap_or(&"")),
85            license_status: parse_opt_char(fields.get(5).unwrap_or(&"")),
86            radio_service_code: parse_opt_string(fields.get(6).unwrap_or(&"")),
87            grant_date: parse_uls_date(fields.get(7).unwrap_or(&"")),
88            expired_date: parse_uls_date(fields.get(8).unwrap_or(&"")),
89            cancellation_date: parse_uls_date(fields.get(9).unwrap_or(&"")),
90            eligibility_rule_num: parse_opt_string(fields.get(10).unwrap_or(&"")),
91            applicant_type_code_reserved: parse_opt_char(fields.get(11).unwrap_or(&"")),
92            alien: parse_opt_char(fields.get(12).unwrap_or(&"")),
93            alien_government: parse_opt_char(fields.get(13).unwrap_or(&"")),
94            alien_corporation: parse_opt_char(fields.get(14).unwrap_or(&"")),
95            alien_officer: parse_opt_char(fields.get(15).unwrap_or(&"")),
96            alien_control: parse_opt_char(fields.get(16).unwrap_or(&"")),
97            revoked: parse_opt_char(fields.get(17).unwrap_or(&"")),
98            convicted: parse_opt_char(fields.get(18).unwrap_or(&"")),
99            adjudged: parse_opt_char(fields.get(19).unwrap_or(&"")),
100            involved_reserved: parse_opt_char(fields.get(20).unwrap_or(&"")),
101            common_carrier: parse_opt_char(fields.get(21).unwrap_or(&"")),
102            non_common_carrier: parse_opt_char(fields.get(22).unwrap_or(&"")),
103            private_comm: parse_opt_char(fields.get(23).unwrap_or(&"")),
104            fixed: parse_opt_char(fields.get(24).unwrap_or(&"")),
105            mobile: parse_opt_char(fields.get(25).unwrap_or(&"")),
106            radiolocation: parse_opt_char(fields.get(26).unwrap_or(&"")),
107            satellite: parse_opt_char(fields.get(27).unwrap_or(&"")),
108            developmental_or_sta: parse_opt_char(fields.get(28).unwrap_or(&"")),
109            interconnected_service: parse_opt_char(fields.get(29).unwrap_or(&"")),
110            certifier_first_name: parse_opt_string(fields.get(30).unwrap_or(&"")),
111            certifier_mi: parse_opt_char(fields.get(31).unwrap_or(&"")),
112            certifier_last_name: parse_opt_string(fields.get(32).unwrap_or(&"")),
113            certifier_suffix: parse_opt_string(fields.get(33).unwrap_or(&"")),
114            certifier_title: parse_opt_string(fields.get(34).unwrap_or(&"")),
115            sex: parse_opt_char(fields.get(35).unwrap_or(&"")),
116            african_american: parse_opt_char(fields.get(36).unwrap_or(&"")),
117            native_american: parse_opt_char(fields.get(37).unwrap_or(&"")),
118            hawaiian: parse_opt_char(fields.get(38).unwrap_or(&"")),
119            asian: parse_opt_char(fields.get(39).unwrap_or(&"")),
120            white: parse_opt_char(fields.get(40).unwrap_or(&"")),
121            ethnicity: parse_opt_char(fields.get(41).unwrap_or(&"")),
122            effective_date: parse_uls_date(fields.get(42).unwrap_or(&"")),
123            last_action_date: parse_uls_date(fields.get(43).unwrap_or(&"")),
124            auction_id: parse_opt_i32(fields.get(44).unwrap_or(&"")),
125            reg_stat_broad_serv: parse_opt_char(fields.get(45).unwrap_or(&"")),
126            band_manager: parse_opt_char(fields.get(46).unwrap_or(&"")),
127            type_serv_broad_serv: parse_opt_char(fields.get(47).unwrap_or(&"")),
128            alien_ruling: parse_opt_char(fields.get(48).unwrap_or(&"")),
129            licensee_name_change: parse_opt_char(fields.get(49).unwrap_or(&"")),
130            whitespace_ind: parse_opt_char(fields.get(50).unwrap_or(&"")),
131            additional_cert_choice: parse_opt_char(fields.get(51).unwrap_or(&"")),
132            additional_cert_answer: parse_opt_char(fields.get(52).unwrap_or(&"")),
133            discontinuation_ind: parse_opt_char(fields.get(53).unwrap_or(&"")),
134            regulatory_compliance_ind: parse_opt_char(fields.get(54).unwrap_or(&"")),
135            eligibility_cert_900: parse_opt_char(fields.get(55).unwrap_or(&"")),
136            transition_plan_cert_900: parse_opt_char(fields.get(56).unwrap_or(&"")),
137            return_spectrum_cert_900: parse_opt_char(fields.get(57).unwrap_or(&"")),
138            payment_cert_900: parse_opt_char(fields.get(58).unwrap_or(&"")),
139        }
140    }
141
142    /// Returns true if this is an active license.
143    pub fn is_active(&self) -> bool {
144        self.license_status == Some('A')
145    }
146
147    /// Returns true if this license has expired.
148    pub fn is_expired(&self) -> bool {
149        self.license_status == Some('E')
150    }
151}
152
153#[cfg(test)]
154mod tests {
155    use super::*;
156
157    #[test]
158    fn test_header_from_fields() {
159        let fields: Vec<&str> = vec![
160            "HD",
161            "123456789",
162            "0000123456",
163            "",
164            "W1AW",
165            "A",
166            "HA",
167            "01/01/2020",
168            "01/01/2030",
169            "",
170            "",
171            "",
172            "",
173            "",
174            "",
175            "",
176            "",
177            "",
178            "",
179            "",
180            "",
181            "",
182            "",
183            "",
184            "",
185            "",
186            "",
187            "",
188            "",
189            "",
190            "",
191            "",
192            "",
193            "",
194            "",
195            "",
196            "",
197            "",
198            "",
199            "",
200            "",
201            "01/01/2020",
202            "06/15/2024",
203            "",
204            "",
205            "",
206            "",
207            "",
208            "",
209            "",
210            "",
211            "",
212            "",
213            "",
214            "",
215            "",
216        ];
217        let record = HeaderRecord::from_fields(&fields);
218        assert_eq!(record.unique_system_identifier, 123456789);
219        assert_eq!(record.call_sign, Some("W1AW".to_string()));
220        assert_eq!(record.license_status, Some('A'));
221        assert_eq!(record.radio_service_code, Some("HA".to_string()));
222        assert!(record.is_active());
223    }
224
225    #[test]
226    fn test_header_empty_fields() {
227        let fields: Vec<&str> = vec!["HD", "", "", "", "", "", ""];
228        let record = HeaderRecord::from_fields(&fields);
229        assert_eq!(record.unique_system_identifier, 0);
230        assert_eq!(record.call_sign, None);
231        assert!(!record.is_active());
232    }
233}