Skip to main content

uls_core/
records.rs

1//! ULS record type definitions.
2//!
3//! This module contains struct definitions for all 89 ULS record types.
4//! Each record type corresponds to a table in the FCC ULS database.
5
6pub mod aircraft;
7pub mod amateur;
8pub mod antenna;
9pub mod attachment;
10pub mod buildout;
11pub mod coast_ground;
12pub mod common;
13pub mod control_point;
14pub mod emission;
15pub mod entity;
16pub mod frequency;
17pub mod header;
18pub mod history;
19pub mod lease;
20pub mod location;
21pub mod market;
22pub mod path;
23pub mod ship;
24pub mod special_conditions;
25pub mod transfer;
26
27pub use aircraft::*;
28pub use amateur::*;
29pub use antenna::*;
30pub use attachment::*;
31pub use buildout::*;
32pub use coast_ground::*;
33pub use common::*;
34pub use control_point::*;
35pub use emission::*;
36pub use entity::*;
37pub use frequency::*;
38pub use header::*;
39pub use history::*;
40pub use lease::*;
41pub use location::*;
42pub use market::*;
43pub use path::*;
44pub use ship::*;
45pub use special_conditions::*;
46pub use transfer::*;
47
48use crate::codes::RecordType;
49
50/// A parsed ULS record from a DAT file.
51#[derive(Debug, Clone)]
52pub enum UlsRecord {
53    /// HD - License/Application Header
54    Header(HeaderRecord),
55    /// EN - Entity (licensee/applicant)
56    Entity(EntityRecord),
57    /// AM - Amateur operator data
58    Amateur(AmateurRecord),
59    /// AD - Application details
60    ApplicationDetail(ApplicationDetailRecord),
61    /// HS - History/status log
62    History(HistoryRecord),
63    /// CO - FCC comments
64    Comment(CommentRecord),
65    /// LO - Location data
66    Location(LocationRecord),
67    /// FR - Frequency data
68    Frequency(FrequencyRecord),
69    /// AN - Antenna data
70    Antenna(AntennaRecord),
71    /// EM - Emission data
72    Emission(EmissionRecord),
73    /// SC - License level special conditions
74    SpecialCondition(SpecialConditionRecord),
75    /// SF - License level free-form conditions
76    FreeformCondition(FreeformConditionRecord),
77    /// VC - Vanity call sign request
78    VanityCallSign(VanityCallSignRecord),
79    /// AC - Aircraft data
80    Aircraft(AircraftRecord),
81    /// SH - Ship data
82    Ship(ShipRecord),
83    /// Raw record for types not yet fully implemented
84    Raw {
85        record_type: RecordType,
86        fields: Vec<String>,
87    },
88}
89
90impl UlsRecord {
91    /// Returns the record type code.
92    pub fn record_type(&self) -> RecordType {
93        match self {
94            Self::Header(_) => RecordType::HD,
95            Self::Entity(_) => RecordType::EN,
96            Self::Amateur(_) => RecordType::AM,
97            Self::ApplicationDetail(_) => RecordType::AD,
98            Self::History(_) => RecordType::HS,
99            Self::Comment(_) => RecordType::CO,
100            Self::Location(_) => RecordType::LO,
101            Self::Frequency(_) => RecordType::FR,
102            Self::Antenna(_) => RecordType::AN,
103            Self::Emission(_) => RecordType::EM,
104            Self::SpecialCondition(_) => RecordType::SC,
105            Self::FreeformCondition(_) => RecordType::SF,
106            Self::VanityCallSign(_) => RecordType::VC,
107            Self::Aircraft(_) => RecordType::AC,
108            Self::Ship(_) => RecordType::SH,
109            Self::Raw { record_type, .. } => *record_type,
110        }
111    }
112
113    /// Returns the unique system identifier if present.
114    pub fn unique_system_identifier(&self) -> Option<i64> {
115        match self {
116            Self::Header(r) => Some(r.unique_system_identifier),
117            Self::Entity(r) => Some(r.unique_system_identifier),
118            Self::Amateur(r) => Some(r.unique_system_identifier),
119            Self::ApplicationDetail(r) => Some(r.unique_system_identifier),
120            Self::History(r) => Some(r.unique_system_identifier),
121            Self::Comment(r) => Some(r.unique_system_identifier),
122            Self::Location(r) => Some(r.unique_system_identifier),
123            Self::Frequency(r) => Some(r.unique_system_identifier),
124            Self::Antenna(r) => Some(r.unique_system_identifier),
125            Self::Emission(r) => Some(r.unique_system_identifier),
126            Self::SpecialCondition(r) => Some(r.unique_system_identifier),
127            Self::FreeformCondition(r) => r.unique_system_identifier,
128            Self::VanityCallSign(r) => Some(r.unique_system_identifier),
129            Self::Aircraft(r) => Some(r.unique_system_identifier),
130            Self::Ship(r) => r.unique_system_identifier,
131            Self::Raw { .. } => None,
132        }
133    }
134
135    /// Returns the call sign if present.
136    pub fn call_sign(&self) -> Option<&str> {
137        match self {
138            Self::Header(r) => r.call_sign.as_deref(),
139            Self::Entity(r) => r.call_sign.as_deref(),
140            Self::Amateur(r) => r.callsign.as_deref(),
141            Self::ApplicationDetail(_) => None,
142            Self::History(r) => r.callsign.as_deref(),
143            Self::Comment(r) => r.callsign.as_deref(),
144            Self::Location(r) => r.call_sign.as_deref(),
145            Self::Frequency(r) => r.call_sign.as_deref(),
146            Self::Antenna(r) => r.call_sign.as_deref(),
147            Self::Emission(r) => r.call_sign.as_deref(),
148            Self::SpecialCondition(r) => r.callsign.as_deref(),
149            Self::FreeformCondition(r) => r.callsign.as_deref(),
150            Self::VanityCallSign(_) => None,
151            Self::Aircraft(r) => r.call_sign.as_deref(),
152            Self::Ship(r) => r.callsign.as_deref(),
153            Self::Raw { .. } => None,
154        }
155    }
156}
157
158#[cfg(test)]
159mod tests {
160    use super::*;
161
162    // Helper to create minimal records for testing dispatch methods
163    fn header_record() -> UlsRecord {
164        UlsRecord::Header(HeaderRecord::from_fields(&[
165            "HD", "100001", "", "", "W1TEST", "A", "HA",
166        ]))
167    }
168
169    fn entity_record() -> UlsRecord {
170        UlsRecord::Entity(EntityRecord::from_fields(&[
171            "EN", "100002", "", "", "W2TEST", "L",
172        ]))
173    }
174
175    fn amateur_record() -> UlsRecord {
176        UlsRecord::Amateur(AmateurRecord::from_fields(&[
177            "AM", "100003", "", "", "W3TEST", "E", "D", "6",
178        ]))
179    }
180
181    fn history_record() -> UlsRecord {
182        UlsRecord::History(HistoryRecord::from_fields(&[
183            "HS",
184            "100004",
185            "",
186            "W4TEST",
187            "01/01/2020",
188            "LIISS",
189        ]))
190    }
191
192    fn comment_record() -> UlsRecord {
193        UlsRecord::Comment(CommentRecord::from_fields(&[
194            "CO",
195            "100005",
196            "",
197            "W5TEST",
198            "01/01/2020",
199            "Test comment",
200        ]))
201    }
202
203    fn location_record() -> UlsRecord {
204        UlsRecord::Location(LocationRecord::from_fields(&[
205            "LO", "100006", "", "", "W6TEST",
206        ]))
207    }
208
209    fn frequency_record() -> UlsRecord {
210        UlsRecord::Frequency(FrequencyRecord::from_fields(&[
211            "FR", "100007", "", "", "W7TEST",
212        ]))
213    }
214
215    fn special_condition_record() -> UlsRecord {
216        UlsRecord::SpecialCondition(SpecialConditionRecord::from_fields(&[
217            "SC", "100008", "", "", "W8TEST", "P", "999",
218        ]))
219    }
220
221    fn raw_record() -> UlsRecord {
222        UlsRecord::Raw {
223            record_type: RecordType::BC,
224            fields: vec!["BC".to_string(), "12345".to_string()],
225        }
226    }
227
228    #[test]
229    fn test_record_type_dispatch() {
230        assert_eq!(header_record().record_type(), RecordType::HD);
231        assert_eq!(entity_record().record_type(), RecordType::EN);
232        assert_eq!(amateur_record().record_type(), RecordType::AM);
233        assert_eq!(history_record().record_type(), RecordType::HS);
234        assert_eq!(comment_record().record_type(), RecordType::CO);
235        assert_eq!(location_record().record_type(), RecordType::LO);
236        assert_eq!(frequency_record().record_type(), RecordType::FR);
237        assert_eq!(special_condition_record().record_type(), RecordType::SC);
238        assert_eq!(raw_record().record_type(), RecordType::BC);
239    }
240
241    #[test]
242    fn test_unique_system_identifier_dispatch() {
243        assert_eq!(header_record().unique_system_identifier(), Some(100001));
244        assert_eq!(entity_record().unique_system_identifier(), Some(100002));
245        assert_eq!(amateur_record().unique_system_identifier(), Some(100003));
246        assert_eq!(history_record().unique_system_identifier(), Some(100004));
247        assert_eq!(comment_record().unique_system_identifier(), Some(100005));
248        assert_eq!(location_record().unique_system_identifier(), Some(100006));
249        assert_eq!(frequency_record().unique_system_identifier(), Some(100007));
250        assert_eq!(
251            special_condition_record().unique_system_identifier(),
252            Some(100008)
253        );
254        assert_eq!(raw_record().unique_system_identifier(), None);
255    }
256
257    #[test]
258    fn test_call_sign_dispatch() {
259        assert_eq!(header_record().call_sign(), Some("W1TEST"));
260        assert_eq!(entity_record().call_sign(), Some("W2TEST"));
261        assert_eq!(amateur_record().call_sign(), Some("W3TEST"));
262        assert_eq!(history_record().call_sign(), Some("W4TEST"));
263        assert_eq!(comment_record().call_sign(), Some("W5TEST"));
264        assert_eq!(location_record().call_sign(), Some("W6TEST"));
265        assert_eq!(frequency_record().call_sign(), Some("W7TEST"));
266        assert_eq!(special_condition_record().call_sign(), Some("W8TEST"));
267        assert_eq!(raw_record().call_sign(), None);
268    }
269}