Skip to main content

uls_core/
codes.rs

1//! Code definitions and enumerations for ULS data.
2//!
3//! This module contains all the standardized codes used in ULS records,
4//! including radio service codes, application purposes, statuses, etc.
5
6use serde::{Deserialize, Serialize};
7use std::fmt;
8use std::str::FromStr;
9
10use crate::error::{Error, Result};
11
12/// Radio service codes as defined by the FCC.
13/// These identify the type of wireless service a license is for.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
15#[serde(rename_all = "UPPERCASE")]
16pub enum RadioService {
17    /// AA - Aviation Auxiliary Group
18    AA,
19    /// AB - Aural Microwave Booster
20    AB,
21    /// AC - Aircraft
22    AC,
23    /// AD - AWS-4 (2000-2020 MHz and 2180-2200 MHz)
24    AD,
25    /// AF - Aeronautical and Fixed
26    AF,
27    /// AH - AWS-H Block (1915-1920 MHz and 1995-2000 MHz)
28    AH,
29    /// AI - Aural Intercity Relay
30    AI,
31    /// AL - ALL (used for assignments/transfers across services)
32    AL,
33    /// AN - Antenna Structure Registration
34    AN,
35    /// AR - Aviation Radionavigation
36    AR,
37    /// AS - Aural Studio Transmitter Link
38    AS,
39    /// AT - AWS-3 (1695-1710, 1755-1780, 2155-2180 MHz)
40    AT,
41    /// AW - AWS (1710-1755 MHz and 2110-2155 MHz)
42    AW,
43    /// BA - 1390-1392 MHz Band, Market Area
44    BA,
45    /// BB - 1392-1395 and 1432-1435 MHz Bands, Market Area
46    BB,
47    /// BC - 1670-1675 MHz Band, Market Area
48    BC,
49    /// BR - Broadband Radio Service
50    BR,
51    /// BS - 900 MHz Broadband Service
52    BS,
53    /// CA - Commercial Air-ground Radiotelephone
54    CA,
55    /// CB - BETRS
56    CB,
57    /// CD - Paging and Radiotelephone
58    CD,
59    /// CE - Digital Electronic Message Service - Common Carrier
60    CE,
61    /// CF - Common Carrier Fixed Point to Point Microwave
62    CF,
63    /// CG - General Aviation Air-ground Radiotelephone
64    CG,
65    /// CJ - Commercial Aviation Air-Ground Radiotelephone (800 MHz)
66    CJ,
67    /// CL - Cellular
68    CL,
69    /// CM - Commercial Operator
70    CM,
71    /// CN - PCS Narrowband
72    CN,
73    /// CO - Offshore Radiotelephone
74    CO,
75    /// CP - Part 22 VHF/UHF Paging (excluding 931MHz)
76    CP,
77    /// CR - Rural Radiotelephone
78    CR,
79    /// CT - Local Television Transmission
80    CT,
81    /// CW - PCS Broadband
82    CW,
83    /// CX - Cellular, Auctioned
84    CX,
85    /// CY - 1910-1915/1990-1995 MHz Bands, Market Area
86    CY,
87    /// CZ - Part 22 931 MHz Paging
88    CZ,
89    /// DV - Multichannel Video Distribution AND Data Service
90    DV,
91    /// ED - Educational Broadband Service
92    ED,
93    /// GB - Business, 806-821/851-866 MHz, Conventional
94    GB,
95    /// GC - 929-931 MHz Band, Auctioned
96    GC,
97    /// GE - PubSafty/SpecEmer/PubSaftyNtlPlan, 806-817/851-862MHz, Conv
98    GE,
99    /// GF - Public Safety Ntl Plan, 821-824/866-869 MHz, Conv
100    GF,
101    /// GI - Other Indust/Land Transp, 896-901/935-940 MHz, Conv
102    GI,
103    /// GJ - Business/Industrial/Land Trans, 809-824/854-869 MHz, Conv
104    GJ,
105    /// GL - 900 MHz Conventional SMR (SMR, Site-Specific)
106    GL,
107    /// GM - 800 MHz Conventional SMR (SMR, Site-specific)
108    GM,
109    /// GO - Other Indust/Land Transp, 806-821/851-866 MHz, Conv
110    GO,
111    /// GP - Public Safety/Spec Emerg, 806-821/851-866 MHz, Conv
112    GP,
113    /// GR - SMR, 896-901/935-940 MHz, Conventional
114    GR,
115    /// GS - Private Carrier Paging, 929-930 MHz
116    GS,
117    /// GU - Business, 896-901/935-940 MHz, Conventional
118    GU,
119    /// GW - General Wireless Communications Service
120    GW,
121    /// GX - SMR, 806-821/851-866 MHz, Conventional
122    GX,
123    /// HA - Amateur
124    HA,
125    /// HV - Vanity (Amateur)
126    HV,
127    /// IG - Industrial/Business Pool, Conventional
128    IG,
129    /// IK - Industrial/Business Pool - Commercial, Conventional
130    IK,
131    /// IQ - Intelligent Transportation Service (Public Safety)
132    IQ,
133    /// LD - Local Multipoint Distribution Service
134    LD,
135    /// LN - 902-928 MHz Location Narrowband (Non-multilateration)
136    LN,
137    /// LP - Broadcast Auxiliary Low Power
138    LP,
139    /// LS - Location and Monitoring Service, Multilateration (LMS)
140    LS,
141    /// LV - Low Power Wireless Assist Video Devices
142    LV,
143    /// LW - 902-928 MHz Location Wideband (Grandfathered AVM)
144    LW,
145    /// MA - Marine Auxiliary Group
146    MA,
147    /// MC - Coastal Group
148    MC,
149    /// MD - Multipoint Distribution Service (MDS and MMDS)
150    MD,
151    /// MG - Microwave Industrial/Business Pool
152    MG,
153    /// MK - Alaska Group
154    MK,
155    /// MM - Millimeter Wave 70/80/90 GHz Service
156    MM,
157    /// MR - Marine Radiolocation Land
158    MR,
159    /// MS - Multiple Address Service, Auctioned
160    MS,
161    /// MW - Microwave Public Safety Pool
162    MW,
163    /// NC - Nationwide Commercial 5 Channel, 220 MHz
164    NC,
165    /// NN - 3650-3700 MHz
166    NN,
167    /// OW - FCC Ownership Disclosure Information
168    OW,
169    /// PA - Public Safety 4940-4990 MHz Band
170    PA,
171    /// PB - 4940-4990 MHz Public Safety, Base/Mobile
172    PB,
173    /// PC - Public Coast Stations, Auctioned
174    PC,
175    /// PE - Digital Electronic Message Service - Private
176    PE,
177    /// PF - 4940-4990 MHz Public Safety, Pt-to-Pt, Pt-to-Multi-Pt
178    PF,
179    /// PK - 3.45 GHz Service
180    PK,
181    /// PL - 3.5 GHz Band Priority Access License
182    PL,
183    /// PM - 3.7 GHz Service
184    PM,
185    /// PW - Public Safety Pool, Conventional
186    PW,
187    /// QA - 220-222 MHz Band, Auctioned
188    QA,
189    /// QD - Non-Nationwide Data, 220 MHz
190    QD,
191    /// QM - Non-Nationwide Public Safety/Mutual Aid, 220 MHz
192    QM,
193    /// QO - Non-Nationwide Other, 220 MHz
194    QO,
195    /// QQ - Intelligent Transportation Service (Non-Public Safety)
196    QQ,
197    /// QT - Non-Nationwide 5 Channel Trunked, 220 MHz
198    QT,
199    /// RP - Broadcast Auxiliary Remote Pickup
200    RP,
201    /// RR - Restricted Operator
202    RR,
203    /// RS - Land Mobile Radiolocation
204    RS,
205    /// SA - Ship Recreational or Voluntarily Equipped
206    SA,
207    /// SB - Ship Compulsory Equipped
208    SB,
209    /// SE - Ship Exemption
210    SE,
211    /// SG - Conventional Public Safety 700 MHz
212    SG,
213    /// SL - Public Safety 700 MHz Band-State License
214    SL,
215    /// SP - 700 MHz Public Safety Broadband Nationwide License
216    SP,
217    /// SY - Trunked Public Safety 700 MHz
218    SY,
219    /// TB - TV Microwave Booster
220    TB,
221    /// TC - MSS Ancillary Terrestrial Component (ATC) Leasing
222    TC,
223    /// TI - TV Intercity Relay
224    TI,
225    /// TN - 39 GHz, Auctioned
226    TN,
227    /// TP - TV Pickup
228    TP,
229    /// TS - TV Studio Transmitter Link
230    TS,
231    /// TT - TV Translator Relay
232    TT,
233    /// TZ - 24 GHz Service
234    TZ,
235    /// UM - Unlicensed Wireless Microphone Registration
236    UM,
237    /// UU - Upper Microwave Flexible Use Service
238    UU,
239    /// VX - Instructional Television Fixed Service
240    VX,
241    /// WA - Microwave Aviation
242    WA,
243    /// WM - Microwave Marine
244    WM,
245    /// WP - 700 MHz Upper Band (Block D)
246    WP,
247    /// WR - Microwave Radiolocation
248    WR,
249    /// WS - Wireless Communications Service
250    WS,
251    /// WT - 600 MHz Band
252    WT,
253    /// WU - 700 MHz Upper Band (Block C)
254    WU,
255    /// WX - 700 MHz Guard Band
256    WX,
257    /// WY - 700 MHz Lower Band (Blocks A, B & E)
258    WY,
259    /// WZ - 700 MHz Lower Band (Blocks C, D)
260    WZ,
261    /// YB - Business, 806-821/851-866 MHz, Trunked
262    YB,
263    /// YC - SMR, 806-821/851-866 MHz, Auctioned
264    YC,
265    /// YD - SMR, 896-901/935-940 MHz, Auctioned
266    YD,
267    /// YE - PubSafty/SpecEmer/PubSaftyNtlPlan, 806-817/851-862MHz, Trunked
268    YE,
269    /// YF - Public Safety Ntl Plan, 821-824/866-869 MHz, Trunked
270    YF,
271    /// YG - Industrial/Business Pool, Trunked
272    YG,
273    /// YH - SMR, 806-821/851-866 MHz, Auctioned (Rebanded YC license)
274    YH,
275    /// YI - Other Indust/Land Transp, 896-901/935-940 MHz, Trunked
276    YI,
277    /// YJ - Business/Industrial/Land Trans, 809-824/854-869 MHz, Trunked
278    YJ,
279    /// YK - Industrial/Business Pool - Commercial, Trunked
280    YK,
281    /// YL - 900 MHz Trunked SMR (SMR, Site-Specific)
282    YL,
283    /// YM - 800 MHz Trunked SMR (SMR, Site-specific)
284    YM,
285    /// YO - Other Indust/Land Transp, 806-821/851-866 MHz, Trunked
286    YO,
287    /// YP - Public Safety/Spec Emerg, 806-821/851-866 MHz, Trunked
288    YP,
289    /// YS - SMR, 896-901/935-940 MHz, Trunked
290    YS,
291    /// YU - Business, 896-901/935-940 MHz, Trunked
292    YU,
293    /// YW - Public Safety Pool, Trunked
294    YW,
295    /// YX - SMR, 806-821/851-866 MHz, Trunked
296    YX,
297    /// ZA - General Mobile Radio (GMRS)
298    ZA,
299    /// ZV - 218-219 MHz Service
300    ZV,
301}
302
303impl RadioService {
304    /// Returns the two-character code for this radio service.
305    pub fn as_str(&self) -> &'static str {
306        match self {
307            Self::AA => "AA",
308            Self::AB => "AB",
309            Self::AC => "AC",
310            Self::AD => "AD",
311            Self::AF => "AF",
312            Self::AH => "AH",
313            Self::AI => "AI",
314            Self::AL => "AL",
315            Self::AN => "AN",
316            Self::AR => "AR",
317            Self::AS => "AS",
318            Self::AT => "AT",
319            Self::AW => "AW",
320            Self::BA => "BA",
321            Self::BB => "BB",
322            Self::BC => "BC",
323            Self::BR => "BR",
324            Self::BS => "BS",
325            Self::CA => "CA",
326            Self::CB => "CB",
327            Self::CD => "CD",
328            Self::CE => "CE",
329            Self::CF => "CF",
330            Self::CG => "CG",
331            Self::CJ => "CJ",
332            Self::CL => "CL",
333            Self::CM => "CM",
334            Self::CN => "CN",
335            Self::CO => "CO",
336            Self::CP => "CP",
337            Self::CR => "CR",
338            Self::CT => "CT",
339            Self::CW => "CW",
340            Self::CX => "CX",
341            Self::CY => "CY",
342            Self::CZ => "CZ",
343            Self::DV => "DV",
344            Self::ED => "ED",
345            Self::GB => "GB",
346            Self::GC => "GC",
347            Self::GE => "GE",
348            Self::GF => "GF",
349            Self::GI => "GI",
350            Self::GJ => "GJ",
351            Self::GL => "GL",
352            Self::GM => "GM",
353            Self::GO => "GO",
354            Self::GP => "GP",
355            Self::GR => "GR",
356            Self::GS => "GS",
357            Self::GU => "GU",
358            Self::GW => "GW",
359            Self::GX => "GX",
360            Self::HA => "HA",
361            Self::HV => "HV",
362            Self::IG => "IG",
363            Self::IK => "IK",
364            Self::IQ => "IQ",
365            Self::LD => "LD",
366            Self::LN => "LN",
367            Self::LP => "LP",
368            Self::LS => "LS",
369            Self::LV => "LV",
370            Self::LW => "LW",
371            Self::MA => "MA",
372            Self::MC => "MC",
373            Self::MD => "MD",
374            Self::MG => "MG",
375            Self::MK => "MK",
376            Self::MM => "MM",
377            Self::MR => "MR",
378            Self::MS => "MS",
379            Self::MW => "MW",
380            Self::NC => "NC",
381            Self::NN => "NN",
382            Self::OW => "OW",
383            Self::PA => "PA",
384            Self::PB => "PB",
385            Self::PC => "PC",
386            Self::PE => "PE",
387            Self::PF => "PF",
388            Self::PK => "PK",
389            Self::PL => "PL",
390            Self::PM => "PM",
391            Self::PW => "PW",
392            Self::QA => "QA",
393            Self::QD => "QD",
394            Self::QM => "QM",
395            Self::QO => "QO",
396            Self::QQ => "QQ",
397            Self::QT => "QT",
398            Self::RP => "RP",
399            Self::RR => "RR",
400            Self::RS => "RS",
401            Self::SA => "SA",
402            Self::SB => "SB",
403            Self::SE => "SE",
404            Self::SG => "SG",
405            Self::SL => "SL",
406            Self::SP => "SP",
407            Self::SY => "SY",
408            Self::TB => "TB",
409            Self::TC => "TC",
410            Self::TI => "TI",
411            Self::TN => "TN",
412            Self::TP => "TP",
413            Self::TS => "TS",
414            Self::TT => "TT",
415            Self::TZ => "TZ",
416            Self::UM => "UM",
417            Self::UU => "UU",
418            Self::VX => "VX",
419            Self::WA => "WA",
420            Self::WM => "WM",
421            Self::WP => "WP",
422            Self::WR => "WR",
423            Self::WS => "WS",
424            Self::WT => "WT",
425            Self::WU => "WU",
426            Self::WX => "WX",
427            Self::WY => "WY",
428            Self::WZ => "WZ",
429            Self::YB => "YB",
430            Self::YC => "YC",
431            Self::YD => "YD",
432            Self::YE => "YE",
433            Self::YF => "YF",
434            Self::YG => "YG",
435            Self::YH => "YH",
436            Self::YI => "YI",
437            Self::YJ => "YJ",
438            Self::YK => "YK",
439            Self::YL => "YL",
440            Self::YM => "YM",
441            Self::YO => "YO",
442            Self::YP => "YP",
443            Self::YS => "YS",
444            Self::YU => "YU",
445            Self::YW => "YW",
446            Self::YX => "YX",
447            Self::ZA => "ZA",
448            Self::ZV => "ZV",
449        }
450    }
451
452    /// Returns a human-readable description of this radio service.
453    pub fn description(&self) -> &'static str {
454        match self {
455            Self::AA => "Aviation Auxiliary Group",
456            Self::AB => "Aural Microwave Booster",
457            Self::AC => "Aircraft",
458            Self::AD => "AWS-4 (2000-2020 MHz and 2180-2200 MHz)",
459            Self::AF => "Aeronautical and Fixed",
460            Self::AH => "AWS-H Block (1915-1920 MHz and 1995-2000 MHz)",
461            Self::AI => "Aural Intercity Relay",
462            Self::AL => "ALL",
463            Self::AN => "Antenna Structure Registration",
464            Self::AR => "Aviation Radionavigation",
465            Self::AS => "Aural Studio Transmitter Link",
466            Self::AT => "AWS-3 (1695-1710, 1755-1780, 2155-2180 MHz)",
467            Self::AW => "AWS (1710-1755 MHz and 2110-2155 MHz)",
468            Self::BA => "1390-1392 MHz Band, Market Area",
469            Self::BB => "1392-1395 and 1432-1435 MHz Bands, Market Area",
470            Self::BC => "1670-1675 MHz Band, Market Area",
471            Self::BR => "Broadband Radio Service",
472            Self::BS => "900 MHz Broadband Service",
473            Self::CA => "Commercial Air-ground Radiotelephone",
474            Self::CB => "BETRS",
475            Self::CD => "Paging and Radiotelephone",
476            Self::CE => "Digital Electronic Message Service - Common Carrier",
477            Self::CF => "Common Carrier Fixed Point to Point Microwave",
478            Self::CG => "General Aviation Air-ground Radiotelephone",
479            Self::CJ => "Commercial Aviation Air-Ground Radiotelephone (800 MHz)",
480            Self::CL => "Cellular",
481            Self::CM => "Commercial Operator",
482            Self::CN => "PCS Narrowband",
483            Self::CO => "Offshore Radiotelephone",
484            Self::CP => "Part 22 VHF/UHF Paging (excluding 931MHz)",
485            Self::CR => "Rural Radiotelephone",
486            Self::CT => "Local Television Transmission",
487            Self::CW => "PCS Broadband",
488            Self::CX => "Cellular, Auctioned",
489            Self::CY => "1910-1915/1990-1995 MHz Bands, Market Area",
490            Self::CZ => "Part 22 931 MHz Paging",
491            Self::DV => "Multichannel Video Distribution AND Data Service",
492            Self::ED => "Educational Broadband Service",
493            Self::GB => "Business, 806-821/851-866 MHz, Conventional",
494            Self::GC => "929-931 MHz Band, Auctioned",
495            Self::GE => "Public Safety/Special Emergency, 806-817/851-862MHz, Conventional",
496            Self::GF => "Public Safety Ntl Plan, 821-824/866-869 MHz, Conventional",
497            Self::GI => "Other Industrial/Land Transport, 896-901/935-940 MHz, Conventional",
498            Self::GJ => "Business/Industrial/Land Trans, 809-824/854-869 MHz, Conventional",
499            Self::GL => "900 MHz Conventional SMR (Site-Specific)",
500            Self::GM => "800 MHz Conventional SMR (Site-specific)",
501            Self::GO => "Other Industrial/Land Transport, 806-821/851-866 MHz, Conventional",
502            Self::GP => "Public Safety/Special Emergency, 806-821/851-866 MHz, Conventional",
503            Self::GR => "SMR, 896-901/935-940 MHz, Conventional",
504            Self::GS => "Private Carrier Paging, 929-930 MHz",
505            Self::GU => "Business, 896-901/935-940 MHz, Conventional",
506            Self::GW => "General Wireless Communications Service",
507            Self::GX => "SMR, 806-821/851-866 MHz, Conventional",
508            Self::HA => "Amateur",
509            Self::HV => "Vanity (Amateur)",
510            Self::IG => "Industrial/Business Pool, Conventional",
511            Self::IK => "Industrial/Business Pool - Commercial, Conventional",
512            Self::IQ => "Intelligent Transportation Service (Public Safety)",
513            Self::LD => "Local Multipoint Distribution Service",
514            Self::LN => "902-928 MHz Location Narrowband (Non-multilateration)",
515            Self::LP => "Broadcast Auxiliary Low Power",
516            Self::LS => "Location and Monitoring Service, Multilateration (LMS)",
517            Self::LV => "Low Power Wireless Assist Video Devices",
518            Self::LW => "902-928 MHz Location Wideband (Grandfathered AVM)",
519            Self::MA => "Marine Auxiliary Group",
520            Self::MC => "Coastal Group",
521            Self::MD => "Multipoint Distribution Service (MDS and MMDS)",
522            Self::MG => "Microwave Industrial/Business Pool",
523            Self::MK => "Alaska Group",
524            Self::MM => "Millimeter Wave 70/80/90 GHz Service",
525            Self::MR => "Marine Radiolocation Land",
526            Self::MS => "Multiple Address Service, Auctioned",
527            Self::MW => "Microwave Public Safety Pool",
528            Self::NC => "Nationwide Commercial 5 Channel, 220 MHz",
529            Self::NN => "3650-3700 MHz",
530            Self::OW => "FCC Ownership Disclosure Information",
531            Self::PA => "Public Safety 4940-4990 MHz Band",
532            Self::PB => "4940-4990 MHz Public Safety, Base/Mobile",
533            Self::PC => "Public Coast Stations, Auctioned",
534            Self::PE => "Digital Electronic Message Service - Private",
535            Self::PF => "4940-4990 MHz Public Safety, Point-to-Point",
536            Self::PK => "3.45 GHz Service",
537            Self::PL => "3.5 GHz Band Priority Access License",
538            Self::PM => "3.7 GHz Service",
539            Self::PW => "Public Safety Pool, Conventional",
540            Self::QA => "220-222 MHz Band, Auctioned",
541            Self::QD => "Non-Nationwide Data, 220 MHz",
542            Self::QM => "Non-Nationwide Public Safety/Mutual Aid, 220 MHz",
543            Self::QO => "Non-Nationwide Other, 220 MHz",
544            Self::QQ => "Intelligent Transportation Service (Non-Public Safety)",
545            Self::QT => "Non-Nationwide 5 Channel Trunked, 220 MHz",
546            Self::RP => "Broadcast Auxiliary Remote Pickup",
547            Self::RR => "Restricted Operator",
548            Self::RS => "Land Mobile Radiolocation",
549            Self::SA => "Ship Recreational or Voluntarily Equipped",
550            Self::SB => "Ship Compulsory Equipped",
551            Self::SE => "Ship Exemption",
552            Self::SG => "Conventional Public Safety 700 MHz",
553            Self::SL => "Public Safety 700 MHz Band-State License",
554            Self::SP => "700 MHz Public Safety Broadband Nationwide License",
555            Self::SY => "Trunked Public Safety 700 MHz",
556            Self::TB => "TV Microwave Booster",
557            Self::TC => "MSS Ancillary Terrestrial Component (ATC) Leasing",
558            Self::TI => "TV Intercity Relay",
559            Self::TN => "39 GHz, Auctioned",
560            Self::TP => "TV Pickup",
561            Self::TS => "TV Studio Transmitter Link",
562            Self::TT => "TV Translator Relay",
563            Self::TZ => "24 GHz Service",
564            Self::UM => "Unlicensed Wireless Microphone Registration",
565            Self::UU => "Upper Microwave Flexible Use Service",
566            Self::VX => "Instructional Television Fixed Service",
567            Self::WA => "Microwave Aviation",
568            Self::WM => "Microwave Marine",
569            Self::WP => "700 MHz Upper Band (Block D)",
570            Self::WR => "Microwave Radiolocation",
571            Self::WS => "Wireless Communications Service",
572            Self::WT => "600 MHz Band",
573            Self::WU => "700 MHz Upper Band (Block C)",
574            Self::WX => "700 MHz Guard Band",
575            Self::WY => "700 MHz Lower Band (Blocks A, B & E)",
576            Self::WZ => "700 MHz Lower Band (Blocks C, D)",
577            Self::YB => "Business, 806-821/851-866 MHz, Trunked",
578            Self::YC => "SMR, 806-821/851-866 MHz, Auctioned",
579            Self::YD => "SMR, 896-901/935-940 MHz, Auctioned",
580            Self::YE => "Public Safety/Special Emergency, 806-817/851-862MHz, Trunked",
581            Self::YF => "Public Safety Ntl Plan, 821-824/866-869 MHz, Trunked",
582            Self::YG => "Industrial/Business Pool, Trunked",
583            Self::YH => "SMR, 806-821/851-866 MHz, Auctioned (Rebanded YC)",
584            Self::YI => "Other Industrial/Land Transport, 896-901/935-940 MHz, Trunked",
585            Self::YJ => "Business/Industrial/Land Trans, 809-824/854-869 MHz, Trunked",
586            Self::YK => "Industrial/Business Pool - Commercial, Trunked",
587            Self::YL => "900 MHz Trunked SMR (Site-Specific)",
588            Self::YM => "800 MHz Trunked SMR (Site-specific)",
589            Self::YO => "Other Industrial/Land Transport, 806-821/851-866 MHz, Trunked",
590            Self::YP => "Public Safety/Special Emergency, 806-821/851-866 MHz, Trunked",
591            Self::YS => "SMR, 896-901/935-940 MHz, Trunked",
592            Self::YU => "Business, 896-901/935-940 MHz, Trunked",
593            Self::YW => "Public Safety Pool, Trunked",
594            Self::YX => "SMR, 806-821/851-866 MHz, Trunked",
595            Self::ZA => "General Mobile Radio (GMRS)",
596            Self::ZV => "218-219 MHz Service",
597        }
598    }
599
600    /// Returns true if this is an amateur radio service.
601    pub fn is_amateur(&self) -> bool {
602        matches!(self, Self::HA | Self::HV)
603    }
604
605    /// Returns true if this is a ship/maritime service.
606    pub fn is_maritime(&self) -> bool {
607        matches!(
608            self,
609            Self::SA | Self::SB | Self::SE | Self::MA | Self::MC | Self::MK
610        )
611    }
612
613    /// Returns true if this is an aircraft service.
614    pub fn is_aircraft(&self) -> bool {
615        matches!(
616            self,
617            Self::AC | Self::AA | Self::AF | Self::AR | Self::CA | Self::CG | Self::CJ
618        )
619    }
620
621    /// Convert to a u8 for compact database storage.
622    /// The encoding is stable and matches the alphabetical order of variants.
623    pub fn to_u8(&self) -> u8 {
624        match self {
625            Self::AA => 0,
626            Self::AB => 1,
627            Self::AC => 2,
628            Self::AD => 3,
629            Self::AF => 4,
630            Self::AH => 5,
631            Self::AI => 6,
632            Self::AL => 7,
633            Self::AN => 8,
634            Self::AR => 9,
635            Self::AS => 10,
636            Self::AT => 11,
637            Self::AW => 12,
638            Self::BA => 13,
639            Self::BB => 14,
640            Self::BC => 15,
641            Self::BR => 16,
642            Self::BS => 17,
643            Self::CA => 18,
644            Self::CB => 19,
645            Self::CD => 20,
646            Self::CE => 21,
647            Self::CF => 22,
648            Self::CG => 23,
649            Self::CJ => 24,
650            Self::CL => 25,
651            Self::CM => 26,
652            Self::CN => 27,
653            Self::CO => 28,
654            Self::CP => 29,
655            Self::CR => 30,
656            Self::CT => 31,
657            Self::CW => 32,
658            Self::CX => 33,
659            Self::CY => 34,
660            Self::CZ => 35,
661            Self::DV => 36,
662            Self::ED => 37,
663            Self::GB => 38,
664            Self::GC => 39,
665            Self::GE => 40,
666            Self::GF => 41,
667            Self::GI => 42,
668            Self::GJ => 43,
669            Self::GL => 44,
670            Self::GM => 45,
671            Self::GO => 46,
672            Self::GP => 47,
673            Self::GR => 48,
674            Self::GS => 49,
675            Self::GU => 50,
676            Self::GW => 51,
677            Self::GX => 52,
678            Self::HA => 53,
679            Self::HV => 54,
680            Self::IG => 55,
681            Self::IK => 56,
682            Self::IQ => 57,
683            Self::LD => 58,
684            Self::LN => 59,
685            Self::LP => 60,
686            Self::LS => 61,
687            Self::LV => 62,
688            Self::LW => 63,
689            Self::MA => 64,
690            Self::MC => 65,
691            Self::MD => 66,
692            Self::MG => 67,
693            Self::MK => 68,
694            Self::MM => 69,
695            Self::MR => 70,
696            Self::MS => 71,
697            Self::MW => 72,
698            Self::NC => 73,
699            Self::NN => 74,
700            Self::OW => 75,
701            Self::PA => 76,
702            Self::PB => 77,
703            Self::PC => 78,
704            Self::PE => 79,
705            Self::PF => 80,
706            Self::PK => 81,
707            Self::PL => 82,
708            Self::PM => 83,
709            Self::PW => 84,
710            Self::QA => 85,
711            Self::QD => 86,
712            Self::QM => 87,
713            Self::QO => 88,
714            Self::QQ => 89,
715            Self::QT => 90,
716            Self::RP => 91,
717            Self::RR => 92,
718            Self::RS => 93,
719            Self::SA => 94,
720            Self::SB => 95,
721            Self::SE => 96,
722            Self::SG => 97,
723            Self::SL => 98,
724            Self::SP => 99,
725            Self::SY => 100,
726            Self::TB => 101,
727            Self::TC => 102,
728            Self::TI => 103,
729            Self::TN => 104,
730            Self::TP => 105,
731            Self::TS => 106,
732            Self::TT => 107,
733            Self::TZ => 108,
734            Self::UM => 109,
735            Self::UU => 110,
736            Self::VX => 111,
737            Self::WA => 112,
738            Self::WM => 113,
739            Self::WP => 114,
740            Self::WR => 115,
741            Self::WS => 116,
742            Self::WT => 117,
743            Self::WU => 118,
744            Self::WX => 119,
745            Self::WY => 120,
746            Self::WZ => 121,
747            Self::YB => 122,
748            Self::YC => 123,
749            Self::YD => 124,
750            Self::YE => 125,
751            Self::YF => 126,
752            Self::YG => 127,
753            Self::YH => 128,
754            Self::YI => 129,
755            Self::YJ => 130,
756            Self::YK => 131,
757            Self::YL => 132,
758            Self::YM => 133,
759            Self::YO => 134,
760            Self::YP => 135,
761            Self::YS => 136,
762            Self::YU => 137,
763            Self::YW => 138,
764            Self::YX => 139,
765            Self::ZA => 140,
766            Self::ZV => 141,
767        }
768    }
769
770    /// Convert from a u8 database value back to RadioService.
771    /// Returns None if the value is invalid.
772    pub fn from_u8(value: u8) -> Option<Self> {
773        match value {
774            0 => Some(Self::AA),
775            1 => Some(Self::AB),
776            2 => Some(Self::AC),
777            3 => Some(Self::AD),
778            4 => Some(Self::AF),
779            5 => Some(Self::AH),
780            6 => Some(Self::AI),
781            7 => Some(Self::AL),
782            8 => Some(Self::AN),
783            9 => Some(Self::AR),
784            10 => Some(Self::AS),
785            11 => Some(Self::AT),
786            12 => Some(Self::AW),
787            13 => Some(Self::BA),
788            14 => Some(Self::BB),
789            15 => Some(Self::BC),
790            16 => Some(Self::BR),
791            17 => Some(Self::BS),
792            18 => Some(Self::CA),
793            19 => Some(Self::CB),
794            20 => Some(Self::CD),
795            21 => Some(Self::CE),
796            22 => Some(Self::CF),
797            23 => Some(Self::CG),
798            24 => Some(Self::CJ),
799            25 => Some(Self::CL),
800            26 => Some(Self::CM),
801            27 => Some(Self::CN),
802            28 => Some(Self::CO),
803            29 => Some(Self::CP),
804            30 => Some(Self::CR),
805            31 => Some(Self::CT),
806            32 => Some(Self::CW),
807            33 => Some(Self::CX),
808            34 => Some(Self::CY),
809            35 => Some(Self::CZ),
810            36 => Some(Self::DV),
811            37 => Some(Self::ED),
812            38 => Some(Self::GB),
813            39 => Some(Self::GC),
814            40 => Some(Self::GE),
815            41 => Some(Self::GF),
816            42 => Some(Self::GI),
817            43 => Some(Self::GJ),
818            44 => Some(Self::GL),
819            45 => Some(Self::GM),
820            46 => Some(Self::GO),
821            47 => Some(Self::GP),
822            48 => Some(Self::GR),
823            49 => Some(Self::GS),
824            50 => Some(Self::GU),
825            51 => Some(Self::GW),
826            52 => Some(Self::GX),
827            53 => Some(Self::HA),
828            54 => Some(Self::HV),
829            55 => Some(Self::IG),
830            56 => Some(Self::IK),
831            57 => Some(Self::IQ),
832            58 => Some(Self::LD),
833            59 => Some(Self::LN),
834            60 => Some(Self::LP),
835            61 => Some(Self::LS),
836            62 => Some(Self::LV),
837            63 => Some(Self::LW),
838            64 => Some(Self::MA),
839            65 => Some(Self::MC),
840            66 => Some(Self::MD),
841            67 => Some(Self::MG),
842            68 => Some(Self::MK),
843            69 => Some(Self::MM),
844            70 => Some(Self::MR),
845            71 => Some(Self::MS),
846            72 => Some(Self::MW),
847            73 => Some(Self::NC),
848            74 => Some(Self::NN),
849            75 => Some(Self::OW),
850            76 => Some(Self::PA),
851            77 => Some(Self::PB),
852            78 => Some(Self::PC),
853            79 => Some(Self::PE),
854            80 => Some(Self::PF),
855            81 => Some(Self::PK),
856            82 => Some(Self::PL),
857            83 => Some(Self::PM),
858            84 => Some(Self::PW),
859            85 => Some(Self::QA),
860            86 => Some(Self::QD),
861            87 => Some(Self::QM),
862            88 => Some(Self::QO),
863            89 => Some(Self::QQ),
864            90 => Some(Self::QT),
865            91 => Some(Self::RP),
866            92 => Some(Self::RR),
867            93 => Some(Self::RS),
868            94 => Some(Self::SA),
869            95 => Some(Self::SB),
870            96 => Some(Self::SE),
871            97 => Some(Self::SG),
872            98 => Some(Self::SL),
873            99 => Some(Self::SP),
874            100 => Some(Self::SY),
875            101 => Some(Self::TB),
876            102 => Some(Self::TC),
877            103 => Some(Self::TI),
878            104 => Some(Self::TN),
879            105 => Some(Self::TP),
880            106 => Some(Self::TS),
881            107 => Some(Self::TT),
882            108 => Some(Self::TZ),
883            109 => Some(Self::UM),
884            110 => Some(Self::UU),
885            111 => Some(Self::VX),
886            112 => Some(Self::WA),
887            113 => Some(Self::WM),
888            114 => Some(Self::WP),
889            115 => Some(Self::WR),
890            116 => Some(Self::WS),
891            117 => Some(Self::WT),
892            118 => Some(Self::WU),
893            119 => Some(Self::WX),
894            120 => Some(Self::WY),
895            121 => Some(Self::WZ),
896            122 => Some(Self::YB),
897            123 => Some(Self::YC),
898            124 => Some(Self::YD),
899            125 => Some(Self::YE),
900            126 => Some(Self::YF),
901            127 => Some(Self::YG),
902            128 => Some(Self::YH),
903            129 => Some(Self::YI),
904            130 => Some(Self::YJ),
905            131 => Some(Self::YK),
906            132 => Some(Self::YL),
907            133 => Some(Self::YM),
908            134 => Some(Self::YO),
909            135 => Some(Self::YP),
910            136 => Some(Self::YS),
911            137 => Some(Self::YU),
912            138 => Some(Self::YW),
913            139 => Some(Self::YX),
914            140 => Some(Self::ZA),
915            141 => Some(Self::ZV),
916            _ => None,
917        }
918    }
919}
920
921impl fmt::Display for RadioService {
922    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
923        write!(f, "{}", self.as_str())
924    }
925}
926
927impl FromStr for RadioService {
928    type Err = Error;
929
930    fn from_str(s: &str) -> Result<Self> {
931        match s.trim().to_uppercase().as_str() {
932            "AA" => Ok(Self::AA),
933            "AB" => Ok(Self::AB),
934            "AC" => Ok(Self::AC),
935            "AD" => Ok(Self::AD),
936            "AF" => Ok(Self::AF),
937            "AH" => Ok(Self::AH),
938            "AI" => Ok(Self::AI),
939            "AL" => Ok(Self::AL),
940            "AN" => Ok(Self::AN),
941            "AR" => Ok(Self::AR),
942            "AS" => Ok(Self::AS),
943            "AT" => Ok(Self::AT),
944            "AW" => Ok(Self::AW),
945            "BA" => Ok(Self::BA),
946            "BB" => Ok(Self::BB),
947            "BC" => Ok(Self::BC),
948            "BR" => Ok(Self::BR),
949            "BS" => Ok(Self::BS),
950            "CA" => Ok(Self::CA),
951            "CB" => Ok(Self::CB),
952            "CD" => Ok(Self::CD),
953            "CE" => Ok(Self::CE),
954            "CF" => Ok(Self::CF),
955            "CG" => Ok(Self::CG),
956            "CJ" => Ok(Self::CJ),
957            "CL" => Ok(Self::CL),
958            "CM" => Ok(Self::CM),
959            "CN" => Ok(Self::CN),
960            "CO" => Ok(Self::CO),
961            "CP" => Ok(Self::CP),
962            "CR" => Ok(Self::CR),
963            "CT" => Ok(Self::CT),
964            "CW" => Ok(Self::CW),
965            "CX" => Ok(Self::CX),
966            "CY" => Ok(Self::CY),
967            "CZ" => Ok(Self::CZ),
968            "DV" => Ok(Self::DV),
969            "ED" => Ok(Self::ED),
970            "GB" => Ok(Self::GB),
971            "GC" => Ok(Self::GC),
972            "GE" => Ok(Self::GE),
973            "GF" => Ok(Self::GF),
974            "GI" => Ok(Self::GI),
975            "GJ" => Ok(Self::GJ),
976            "GL" => Ok(Self::GL),
977            "GM" => Ok(Self::GM),
978            "GO" => Ok(Self::GO),
979            "GP" => Ok(Self::GP),
980            "GR" => Ok(Self::GR),
981            "GS" => Ok(Self::GS),
982            "GU" => Ok(Self::GU),
983            "GW" => Ok(Self::GW),
984            "GX" => Ok(Self::GX),
985            "HA" => Ok(Self::HA),
986            "HV" => Ok(Self::HV),
987            "IG" => Ok(Self::IG),
988            "IK" => Ok(Self::IK),
989            "IQ" => Ok(Self::IQ),
990            "LD" => Ok(Self::LD),
991            "LN" => Ok(Self::LN),
992            "LP" => Ok(Self::LP),
993            "LS" => Ok(Self::LS),
994            "LV" => Ok(Self::LV),
995            "LW" => Ok(Self::LW),
996            "MA" => Ok(Self::MA),
997            "MC" => Ok(Self::MC),
998            "MD" => Ok(Self::MD),
999            "MG" => Ok(Self::MG),
1000            "MK" => Ok(Self::MK),
1001            "MM" => Ok(Self::MM),
1002            "MR" => Ok(Self::MR),
1003            "MS" => Ok(Self::MS),
1004            "MW" => Ok(Self::MW),
1005            "NC" => Ok(Self::NC),
1006            "NN" => Ok(Self::NN),
1007            "OW" => Ok(Self::OW),
1008            "PA" => Ok(Self::PA),
1009            "PB" => Ok(Self::PB),
1010            "PC" => Ok(Self::PC),
1011            "PE" => Ok(Self::PE),
1012            "PF" => Ok(Self::PF),
1013            "PK" => Ok(Self::PK),
1014            "PL" => Ok(Self::PL),
1015            "PM" => Ok(Self::PM),
1016            "PW" => Ok(Self::PW),
1017            "QA" => Ok(Self::QA),
1018            "QD" => Ok(Self::QD),
1019            "QM" => Ok(Self::QM),
1020            "QO" => Ok(Self::QO),
1021            "QQ" => Ok(Self::QQ),
1022            "QT" => Ok(Self::QT),
1023            "RP" => Ok(Self::RP),
1024            "RR" => Ok(Self::RR),
1025            "RS" => Ok(Self::RS),
1026            "SA" => Ok(Self::SA),
1027            "SB" => Ok(Self::SB),
1028            "SE" => Ok(Self::SE),
1029            "SG" => Ok(Self::SG),
1030            "SL" => Ok(Self::SL),
1031            "SP" => Ok(Self::SP),
1032            "SY" => Ok(Self::SY),
1033            "TB" => Ok(Self::TB),
1034            "TC" => Ok(Self::TC),
1035            "TI" => Ok(Self::TI),
1036            "TN" => Ok(Self::TN),
1037            "TP" => Ok(Self::TP),
1038            "TS" => Ok(Self::TS),
1039            "TT" => Ok(Self::TT),
1040            "TZ" => Ok(Self::TZ),
1041            "UM" => Ok(Self::UM),
1042            "UU" => Ok(Self::UU),
1043            "VX" => Ok(Self::VX),
1044            "WA" => Ok(Self::WA),
1045            "WM" => Ok(Self::WM),
1046            "WP" => Ok(Self::WP),
1047            "WR" => Ok(Self::WR),
1048            "WS" => Ok(Self::WS),
1049            "WT" => Ok(Self::WT),
1050            "WU" => Ok(Self::WU),
1051            "WX" => Ok(Self::WX),
1052            "WY" => Ok(Self::WY),
1053            "WZ" => Ok(Self::WZ),
1054            "YB" => Ok(Self::YB),
1055            "YC" => Ok(Self::YC),
1056            "YD" => Ok(Self::YD),
1057            "YE" => Ok(Self::YE),
1058            "YF" => Ok(Self::YF),
1059            "YG" => Ok(Self::YG),
1060            "YH" => Ok(Self::YH),
1061            "YI" => Ok(Self::YI),
1062            "YJ" => Ok(Self::YJ),
1063            "YK" => Ok(Self::YK),
1064            "YL" => Ok(Self::YL),
1065            "YM" => Ok(Self::YM),
1066            "YO" => Ok(Self::YO),
1067            "YP" => Ok(Self::YP),
1068            "YS" => Ok(Self::YS),
1069            "YU" => Ok(Self::YU),
1070            "YW" => Ok(Self::YW),
1071            "YX" => Ok(Self::YX),
1072            "ZA" => Ok(Self::ZA),
1073            "ZV" => Ok(Self::ZV),
1074            _ => Err(Error::InvalidEnumValue {
1075                enum_type: "RadioService",
1076                value: s.to_string(),
1077            }),
1078        }
1079    }
1080}
1081
1082/// Application purpose codes.
1083#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
1084pub enum ApplicationPurpose {
1085    /// AA - Assignment of Authorization
1086    AssignmentOfAuthorization,
1087    /// AM - Amendment
1088    Amendment,
1089    /// AR - DE Annual Report
1090    DEAnnualReport,
1091    /// AU - Administrative Update
1092    AdministrativeUpdate,
1093    /// CA - Cancellation of License
1094    CancellationOfLicense,
1095    /// CB - C Block Election
1096    CBlockElection,
1097    /// DC - Data Correction
1098    DataCorrection,
1099    /// DU - Duplicate License
1100    DuplicateLicense,
1101    /// EX - Request for Extension of Time
1102    ExtensionOfTime,
1103    /// HA - HAC Report
1104    HACReport,
1105    /// LC - Cancel a Lease
1106    CancelLease,
1107    /// LE - Extend Term of a Lease
1108    ExtendLease,
1109    /// LM - Modification of a Lease
1110    ModifyLease,
1111    /// LN - New Lease
1112    NewLease,
1113    /// LT - Transfer of Control of a Lessee
1114    TransferLessee,
1115    /// LU - Administrative Update of a Lease
1116    AdminUpdateLease,
1117    /// MD - Modification
1118    Modification,
1119    /// NE - New
1120    New,
1121    /// NT - Required Notification
1122    RequiredNotification,
1123    /// RE - DE Reportable Event
1124    DEReportableEvent,
1125    /// RL - Register Link/Location
1126    RegisterLinkLocation,
1127    /// RM - Renewal/Modification
1128    RenewalModification,
1129    /// RO - Renewal Only
1130    RenewalOnly,
1131    /// TC - Transfer of Control
1132    TransferOfControl,
1133    /// WD - Withdrawal of Application
1134    Withdrawal,
1135}
1136
1137impl ApplicationPurpose {
1138    /// Returns the two-character code for this application purpose.
1139    pub fn as_str(&self) -> &'static str {
1140        match self {
1141            Self::AssignmentOfAuthorization => "AA",
1142            Self::Amendment => "AM",
1143            Self::DEAnnualReport => "AR",
1144            Self::AdministrativeUpdate => "AU",
1145            Self::CancellationOfLicense => "CA",
1146            Self::CBlockElection => "CB",
1147            Self::DataCorrection => "DC",
1148            Self::DuplicateLicense => "DU",
1149            Self::ExtensionOfTime => "EX",
1150            Self::HACReport => "HA",
1151            Self::CancelLease => "LC",
1152            Self::ExtendLease => "LE",
1153            Self::ModifyLease => "LM",
1154            Self::NewLease => "LN",
1155            Self::TransferLessee => "LT",
1156            Self::AdminUpdateLease => "LU",
1157            Self::Modification => "MD",
1158            Self::New => "NE",
1159            Self::RequiredNotification => "NT",
1160            Self::DEReportableEvent => "RE",
1161            Self::RegisterLinkLocation => "RL",
1162            Self::RenewalModification => "RM",
1163            Self::RenewalOnly => "RO",
1164            Self::TransferOfControl => "TC",
1165            Self::Withdrawal => "WD",
1166        }
1167    }
1168}
1169
1170impl FromStr for ApplicationPurpose {
1171    type Err = Error;
1172
1173    fn from_str(s: &str) -> Result<Self> {
1174        match s.trim().to_uppercase().as_str() {
1175            "AA" => Ok(Self::AssignmentOfAuthorization),
1176            "AM" => Ok(Self::Amendment),
1177            "AR" => Ok(Self::DEAnnualReport),
1178            "AU" => Ok(Self::AdministrativeUpdate),
1179            "CA" => Ok(Self::CancellationOfLicense),
1180            "CB" => Ok(Self::CBlockElection),
1181            "DC" => Ok(Self::DataCorrection),
1182            "DU" => Ok(Self::DuplicateLicense),
1183            "EX" => Ok(Self::ExtensionOfTime),
1184            "HA" => Ok(Self::HACReport),
1185            "LC" => Ok(Self::CancelLease),
1186            "LE" => Ok(Self::ExtendLease),
1187            "LM" => Ok(Self::ModifyLease),
1188            "LN" => Ok(Self::NewLease),
1189            "LT" => Ok(Self::TransferLessee),
1190            "LU" => Ok(Self::AdminUpdateLease),
1191            "MD" => Ok(Self::Modification),
1192            "NE" => Ok(Self::New),
1193            "NT" => Ok(Self::RequiredNotification),
1194            "RE" => Ok(Self::DEReportableEvent),
1195            "RL" => Ok(Self::RegisterLinkLocation),
1196            "RM" => Ok(Self::RenewalModification),
1197            "RO" => Ok(Self::RenewalOnly),
1198            "TC" => Ok(Self::TransferOfControl),
1199            "WD" => Ok(Self::Withdrawal),
1200            _ => Err(Error::InvalidEnumValue {
1201                enum_type: "ApplicationPurpose",
1202                value: s.to_string(),
1203            }),
1204        }
1205    }
1206}
1207
1208impl fmt::Display for ApplicationPurpose {
1209    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1210        write!(f, "{}", self.as_str())
1211    }
1212}
1213
1214/// Application status codes.
1215#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
1216pub enum ApplicationStatus {
1217    /// 1 - Submitted
1218    Submitted,
1219    /// 2 - Pending
1220    Pending,
1221    /// A - Granted (alternative)
1222    AGranted,
1223    /// C - Consented To
1224    ConsentedTo,
1225    /// D - Dismissed
1226    Dismissed,
1227    /// E - Eliminate
1228    Eliminate,
1229    /// G - Granted
1230    Granted,
1231    /// H - History Only
1232    HistoryOnly,
1233    /// I - Inactive
1234    Inactive,
1235    /// J - HAC Submitted
1236    HACSubmitted,
1237    /// K - Killed
1238    Killed,
1239    /// M - Consummated
1240    Consummated,
1241    /// N - Granted in Part
1242    GrantedInPart,
1243    /// P - Pending Pack Filing
1244    PendingPackFiling,
1245    /// Q - Accepted
1246    Accepted,
1247    /// R - Returned
1248    Returned,
1249    /// S - Saved
1250    Saved,
1251    /// T - Terminated
1252    Terminated,
1253    /// U - Unprocessable
1254    Unprocessable,
1255    /// W - Withdrawn
1256    Withdrawn,
1257    /// X - Not Applicable
1258    NotApplicable,
1259    /// Y - Application has problems
1260    HasProblems,
1261}
1262
1263impl ApplicationStatus {
1264    /// Returns the single-character code for this status.
1265    pub fn as_str(&self) -> &'static str {
1266        match self {
1267            Self::Submitted => "1",
1268            Self::Pending => "2",
1269            Self::AGranted => "A",
1270            Self::ConsentedTo => "C",
1271            Self::Dismissed => "D",
1272            Self::Eliminate => "E",
1273            Self::Granted => "G",
1274            Self::HistoryOnly => "H",
1275            Self::Inactive => "I",
1276            Self::HACSubmitted => "J",
1277            Self::Killed => "K",
1278            Self::Consummated => "M",
1279            Self::GrantedInPart => "N",
1280            Self::PendingPackFiling => "P",
1281            Self::Accepted => "Q",
1282            Self::Returned => "R",
1283            Self::Saved => "S",
1284            Self::Terminated => "T",
1285            Self::Unprocessable => "U",
1286            Self::Withdrawn => "W",
1287            Self::NotApplicable => "X",
1288            Self::HasProblems => "Y",
1289        }
1290    }
1291}
1292
1293impl FromStr for ApplicationStatus {
1294    type Err = Error;
1295
1296    fn from_str(s: &str) -> Result<Self> {
1297        match s.trim().to_uppercase().as_str() {
1298            "1" => Ok(Self::Submitted),
1299            "2" => Ok(Self::Pending),
1300            "A" => Ok(Self::AGranted),
1301            "C" => Ok(Self::ConsentedTo),
1302            "D" => Ok(Self::Dismissed),
1303            "E" => Ok(Self::Eliminate),
1304            "G" => Ok(Self::Granted),
1305            "H" => Ok(Self::HistoryOnly),
1306            "I" => Ok(Self::Inactive),
1307            "J" => Ok(Self::HACSubmitted),
1308            "K" => Ok(Self::Killed),
1309            "M" => Ok(Self::Consummated),
1310            "N" => Ok(Self::GrantedInPart),
1311            "P" => Ok(Self::PendingPackFiling),
1312            "Q" => Ok(Self::Accepted),
1313            "R" => Ok(Self::Returned),
1314            "S" => Ok(Self::Saved),
1315            "T" => Ok(Self::Terminated),
1316            "U" => Ok(Self::Unprocessable),
1317            "W" => Ok(Self::Withdrawn),
1318            "X" => Ok(Self::NotApplicable),
1319            "Y" => Ok(Self::HasProblems),
1320            _ => Err(Error::InvalidEnumValue {
1321                enum_type: "ApplicationStatus",
1322                value: s.to_string(),
1323            }),
1324        }
1325    }
1326}
1327
1328impl fmt::Display for ApplicationStatus {
1329    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1330        write!(f, "{}", self.as_str())
1331    }
1332}
1333
1334/// License status codes.
1335#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
1336pub enum LicenseStatus {
1337    /// A - Active
1338    Active,
1339    /// C - Cancelled
1340    Cancelled,
1341    /// E - Expired
1342    Expired,
1343    /// L - Pending Legal Status
1344    PendingLegalStatus,
1345    /// P - Parent Station Cancelled
1346    ParentStationCancelled,
1347    /// T - Terminated
1348    Terminated,
1349    /// X - Term Pending
1350    TermPending,
1351}
1352
1353impl LicenseStatus {
1354    /// Returns the single-character code for this status.
1355    pub fn as_str(&self) -> &'static str {
1356        match self {
1357            Self::Active => "A",
1358            Self::Cancelled => "C",
1359            Self::Expired => "E",
1360            Self::PendingLegalStatus => "L",
1361            Self::ParentStationCancelled => "P",
1362            Self::Terminated => "T",
1363            Self::TermPending => "X",
1364        }
1365    }
1366
1367    /// Returns true if this license is considered active/valid.
1368    pub fn is_active(&self) -> bool {
1369        matches!(self, Self::Active)
1370    }
1371
1372    /// Convert to a u8 for compact database storage.
1373    pub fn to_u8(&self) -> u8 {
1374        match self {
1375            Self::Active => 0,
1376            Self::Cancelled => 1,
1377            Self::Expired => 2,
1378            Self::PendingLegalStatus => 3,
1379            Self::ParentStationCancelled => 4,
1380            Self::Terminated => 5,
1381            Self::TermPending => 6,
1382        }
1383    }
1384
1385    /// Convert from a u8 database value back to LicenseStatus.
1386    pub fn from_u8(value: u8) -> Option<Self> {
1387        match value {
1388            0 => Some(Self::Active),
1389            1 => Some(Self::Cancelled),
1390            2 => Some(Self::Expired),
1391            3 => Some(Self::PendingLegalStatus),
1392            4 => Some(Self::ParentStationCancelled),
1393            5 => Some(Self::Terminated),
1394            6 => Some(Self::TermPending),
1395            _ => None,
1396        }
1397    }
1398}
1399
1400impl FromStr for LicenseStatus {
1401    type Err = Error;
1402
1403    fn from_str(s: &str) -> Result<Self> {
1404        match s.trim().to_uppercase().as_str() {
1405            "A" => Ok(Self::Active),
1406            "C" => Ok(Self::Cancelled),
1407            "E" => Ok(Self::Expired),
1408            "L" => Ok(Self::PendingLegalStatus),
1409            "P" => Ok(Self::ParentStationCancelled),
1410            "T" => Ok(Self::Terminated),
1411            "X" => Ok(Self::TermPending),
1412            _ => Err(Error::InvalidEnumValue {
1413                enum_type: "LicenseStatus",
1414                value: s.to_string(),
1415            }),
1416        }
1417    }
1418}
1419
1420impl fmt::Display for LicenseStatus {
1421    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1422        write!(f, "{}", self.as_str())
1423    }
1424}
1425
1426/// Amateur operator class codes.
1427#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
1428pub enum OperatorClass {
1429    /// A - Advanced
1430    Advanced,
1431    /// E - Amateur Extra
1432    Extra,
1433    /// G - General
1434    General,
1435    /// N - Novice
1436    Novice,
1437    /// P - Technician Plus
1438    TechnicianPlus,
1439    /// T - Technician
1440    Technician,
1441}
1442
1443impl OperatorClass {
1444    /// Returns the single-character code for this operator class.
1445    pub fn as_str(&self) -> &'static str {
1446        match self {
1447            Self::Advanced => "A",
1448            Self::Extra => "E",
1449            Self::General => "G",
1450            Self::Novice => "N",
1451            Self::TechnicianPlus => "P",
1452            Self::Technician => "T",
1453        }
1454    }
1455
1456    /// Returns a human-readable description of this operator class.
1457    pub fn description(&self) -> &'static str {
1458        match self {
1459            Self::Advanced => "Advanced",
1460            Self::Extra => "Amateur Extra",
1461            Self::General => "General",
1462            Self::Novice => "Novice",
1463            Self::TechnicianPlus => "Technician Plus",
1464            Self::Technician => "Technician",
1465        }
1466    }
1467
1468    /// Convert to a u8 for compact database storage.
1469    pub fn to_u8(&self) -> u8 {
1470        match self {
1471            Self::Advanced => 0,
1472            Self::Extra => 1,
1473            Self::General => 2,
1474            Self::Novice => 3,
1475            Self::TechnicianPlus => 4,
1476            Self::Technician => 5,
1477        }
1478    }
1479
1480    /// Convert from a u8 database value back to OperatorClass.
1481    pub fn from_u8(value: u8) -> Option<Self> {
1482        match value {
1483            0 => Some(Self::Advanced),
1484            1 => Some(Self::Extra),
1485            2 => Some(Self::General),
1486            3 => Some(Self::Novice),
1487            4 => Some(Self::TechnicianPlus),
1488            5 => Some(Self::Technician),
1489            _ => None,
1490        }
1491    }
1492}
1493
1494impl FromStr for OperatorClass {
1495    type Err = Error;
1496
1497    fn from_str(s: &str) -> Result<Self> {
1498        match s.trim().to_uppercase().as_str() {
1499            "A" => Ok(Self::Advanced),
1500            "E" => Ok(Self::Extra),
1501            "G" => Ok(Self::General),
1502            "N" => Ok(Self::Novice),
1503            "P" => Ok(Self::TechnicianPlus),
1504            "T" => Ok(Self::Technician),
1505            _ => Err(Error::InvalidEnumValue {
1506                enum_type: "OperatorClass",
1507                value: s.to_string(),
1508            }),
1509        }
1510    }
1511}
1512
1513impl fmt::Display for OperatorClass {
1514    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1515        write!(f, "{}", self.as_str())
1516    }
1517}
1518
1519/// Entity type codes for the EN (Entity) record.
1520#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
1521pub enum EntityType {
1522    /// CE - Transferee Contact
1523    TransfereeContact,
1524    /// CL - Licensee Contact
1525    LicenseeContact,
1526    /// CR - Assignor Contact
1527    AssignorContact,
1528    /// CS - Lessee Contact
1529    LesseeContact,
1530    /// E - Transferee
1531    Transferee,
1532    /// L - Licensee
1533    Licensee,
1534    /// O - Owner
1535    Owner,
1536    /// R - Assignor or Transferor
1537    Assignor,
1538    /// S - Lessee
1539    Lessee,
1540}
1541
1542impl EntityType {
1543    /// Returns the code for this entity type.
1544    pub fn as_str(&self) -> &'static str {
1545        match self {
1546            Self::TransfereeContact => "CE",
1547            Self::LicenseeContact => "CL",
1548            Self::AssignorContact => "CR",
1549            Self::LesseeContact => "CS",
1550            Self::Transferee => "E",
1551            Self::Licensee => "L",
1552            Self::Owner => "O",
1553            Self::Assignor => "R",
1554            Self::Lessee => "S",
1555        }
1556    }
1557
1558    /// Convert to a u8 for compact database storage.
1559    pub fn to_u8(&self) -> u8 {
1560        match self {
1561            Self::TransfereeContact => 0,
1562            Self::LicenseeContact => 1,
1563            Self::AssignorContact => 2,
1564            Self::LesseeContact => 3,
1565            Self::Transferee => 4,
1566            Self::Licensee => 5,
1567            Self::Owner => 6,
1568            Self::Assignor => 7,
1569            Self::Lessee => 8,
1570        }
1571    }
1572
1573    /// Convert from a u8 database value back to EntityType.
1574    pub fn from_u8(value: u8) -> Option<Self> {
1575        match value {
1576            0 => Some(Self::TransfereeContact),
1577            1 => Some(Self::LicenseeContact),
1578            2 => Some(Self::AssignorContact),
1579            3 => Some(Self::LesseeContact),
1580            4 => Some(Self::Transferee),
1581            5 => Some(Self::Licensee),
1582            6 => Some(Self::Owner),
1583            7 => Some(Self::Assignor),
1584            8 => Some(Self::Lessee),
1585            _ => None,
1586        }
1587    }
1588}
1589
1590impl FromStr for EntityType {
1591    type Err = Error;
1592
1593    fn from_str(s: &str) -> Result<Self> {
1594        match s.trim().to_uppercase().as_str() {
1595            "CE" => Ok(Self::TransfereeContact),
1596            "CL" => Ok(Self::LicenseeContact),
1597            "CR" => Ok(Self::AssignorContact),
1598            "CS" => Ok(Self::LesseeContact),
1599            "E" => Ok(Self::Transferee),
1600            "L" => Ok(Self::Licensee),
1601            "O" => Ok(Self::Owner),
1602            "R" => Ok(Self::Assignor),
1603            "S" => Ok(Self::Lessee),
1604            _ => Err(Error::InvalidEnumValue {
1605                enum_type: "EntityType",
1606                value: s.to_string(),
1607            }),
1608        }
1609    }
1610}
1611
1612impl fmt::Display for EntityType {
1613    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1614        write!(f, "{}", self.as_str())
1615    }
1616}
1617
1618/// Record type codes identifying the type of record in a DAT file.
1619#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
1620pub enum RecordType {
1621    A2,
1622    A3,
1623    AC,
1624    AD,
1625    AG,
1626    AH,
1627    AM,
1628    AN,
1629    AP,
1630    AS,
1631    AT,
1632    BC,
1633    BD,
1634    BE,
1635    BF,
1636    BL,
1637    BO,
1638    BT,
1639    CD,
1640    CF,
1641    CG,
1642    CO,
1643    CP,
1644    CS,
1645    EC,
1646    EM,
1647    EN,
1648    F2,
1649    F3,
1650    F4,
1651    F5,
1652    F6,
1653    FA,
1654    FC,
1655    FF,
1656    FR,
1657    FS,
1658    FT,
1659    HD,
1660    HS,
1661    IA,
1662    IF,
1663    IR,
1664    L2,
1665    L3,
1666    L4,
1667    L5,
1668    L6,
1669    LA,
1670    LC,
1671    LD,
1672    LF,
1673    LH,
1674    LL,
1675    LM,
1676    LO,
1677    LS,
1678    MC,
1679    ME,
1680    MF,
1681    MH,
1682    MI,
1683    MK,
1684    MP,
1685    MW,
1686    O2,
1687    OP,
1688    P2,
1689    PA,
1690    PC,
1691    RA,
1692    RC,
1693    RE,
1694    RI,
1695    RZ,
1696    SC,
1697    SE,
1698    SF,
1699    SG,
1700    SH,
1701    SI,
1702    SR,
1703    ST,
1704    SV,
1705    TA,
1706    TL,
1707    TP,
1708    UA,
1709    VC,
1710}
1711
1712impl RecordType {
1713    /// Returns the two-character code for this record type.
1714    pub fn as_str(&self) -> &'static str {
1715        match self {
1716            Self::A2 => "A2",
1717            Self::A3 => "A3",
1718            Self::AC => "AC",
1719            Self::AD => "AD",
1720            Self::AG => "AG",
1721            Self::AH => "AH",
1722            Self::AM => "AM",
1723            Self::AN => "AN",
1724            Self::AP => "AP",
1725            Self::AS => "AS",
1726            Self::AT => "AT",
1727            Self::BC => "BC",
1728            Self::BD => "BD",
1729            Self::BE => "BE",
1730            Self::BF => "BF",
1731            Self::BL => "BL",
1732            Self::BO => "BO",
1733            Self::BT => "BT",
1734            Self::CD => "CD",
1735            Self::CF => "CF",
1736            Self::CG => "CG",
1737            Self::CO => "CO",
1738            Self::CP => "CP",
1739            Self::CS => "CS",
1740            Self::EC => "EC",
1741            Self::EM => "EM",
1742            Self::EN => "EN",
1743            Self::F2 => "F2",
1744            Self::F3 => "F3",
1745            Self::F4 => "F4",
1746            Self::F5 => "F5",
1747            Self::F6 => "F6",
1748            Self::FA => "FA",
1749            Self::FC => "FC",
1750            Self::FF => "FF",
1751            Self::FR => "FR",
1752            Self::FS => "FS",
1753            Self::FT => "FT",
1754            Self::HD => "HD",
1755            Self::HS => "HS",
1756            Self::IA => "IA",
1757            Self::IF => "IF",
1758            Self::IR => "IR",
1759            Self::L2 => "L2",
1760            Self::L3 => "L3",
1761            Self::L4 => "L4",
1762            Self::L5 => "L5",
1763            Self::L6 => "L6",
1764            Self::LA => "LA",
1765            Self::LC => "LC",
1766            Self::LD => "LD",
1767            Self::LF => "LF",
1768            Self::LH => "LH",
1769            Self::LL => "LL",
1770            Self::LM => "LM",
1771            Self::LO => "LO",
1772            Self::LS => "LS",
1773            Self::MC => "MC",
1774            Self::ME => "ME",
1775            Self::MF => "MF",
1776            Self::MH => "MH",
1777            Self::MI => "MI",
1778            Self::MK => "MK",
1779            Self::MP => "MP",
1780            Self::MW => "MW",
1781            Self::O2 => "O2",
1782            Self::OP => "OP",
1783            Self::P2 => "P2",
1784            Self::PA => "PA",
1785            Self::PC => "PC",
1786            Self::RA => "RA",
1787            Self::RC => "RC",
1788            Self::RE => "RE",
1789            Self::RI => "RI",
1790            Self::RZ => "RZ",
1791            Self::SC => "SC",
1792            Self::SE => "SE",
1793            Self::SF => "SF",
1794            Self::SG => "SG",
1795            Self::SH => "SH",
1796            Self::SI => "SI",
1797            Self::SR => "SR",
1798            Self::ST => "ST",
1799            Self::SV => "SV",
1800            Self::TA => "TA",
1801            Self::TL => "TL",
1802            Self::TP => "TP",
1803            Self::UA => "UA",
1804            Self::VC => "VC",
1805        }
1806    }
1807
1808    /// Returns the corresponding DAT filename for this record type.
1809    pub fn dat_filename(&self) -> String {
1810        format!("{}.dat", self.as_str())
1811    }
1812}
1813
1814impl FromStr for RecordType {
1815    type Err = Error;
1816
1817    fn from_str(s: &str) -> Result<Self> {
1818        match s.trim().to_uppercase().as_str() {
1819            "A2" => Ok(Self::A2),
1820            "A3" => Ok(Self::A3),
1821            "AC" => Ok(Self::AC),
1822            "AD" => Ok(Self::AD),
1823            "AG" => Ok(Self::AG),
1824            "AH" => Ok(Self::AH),
1825            "AM" => Ok(Self::AM),
1826            "AN" => Ok(Self::AN),
1827            "AP" => Ok(Self::AP),
1828            "AS" => Ok(Self::AS),
1829            "AT" => Ok(Self::AT),
1830            "BC" => Ok(Self::BC),
1831            "BD" => Ok(Self::BD),
1832            "BE" => Ok(Self::BE),
1833            "BF" => Ok(Self::BF),
1834            "BL" => Ok(Self::BL),
1835            "BO" => Ok(Self::BO),
1836            "BT" => Ok(Self::BT),
1837            "CD" => Ok(Self::CD),
1838            "CF" => Ok(Self::CF),
1839            "CG" => Ok(Self::CG),
1840            "CO" => Ok(Self::CO),
1841            "CP" => Ok(Self::CP),
1842            "CS" => Ok(Self::CS),
1843            "EC" => Ok(Self::EC),
1844            "EM" => Ok(Self::EM),
1845            "EN" => Ok(Self::EN),
1846            "F2" => Ok(Self::F2),
1847            "F3" => Ok(Self::F3),
1848            "F4" => Ok(Self::F4),
1849            "F5" => Ok(Self::F5),
1850            "F6" => Ok(Self::F6),
1851            "FA" => Ok(Self::FA),
1852            "FC" => Ok(Self::FC),
1853            "FF" => Ok(Self::FF),
1854            "FR" => Ok(Self::FR),
1855            "FS" => Ok(Self::FS),
1856            "FT" => Ok(Self::FT),
1857            "HD" => Ok(Self::HD),
1858            "HS" => Ok(Self::HS),
1859            "IA" => Ok(Self::IA),
1860            "IF" => Ok(Self::IF),
1861            "IR" => Ok(Self::IR),
1862            "L2" => Ok(Self::L2),
1863            "L3" => Ok(Self::L3),
1864            "L4" => Ok(Self::L4),
1865            "L5" => Ok(Self::L5),
1866            "L6" => Ok(Self::L6),
1867            "LA" => Ok(Self::LA),
1868            "LC" => Ok(Self::LC),
1869            "LD" => Ok(Self::LD),
1870            "LF" => Ok(Self::LF),
1871            "LH" => Ok(Self::LH),
1872            "LL" => Ok(Self::LL),
1873            "LM" => Ok(Self::LM),
1874            "LO" => Ok(Self::LO),
1875            "LS" => Ok(Self::LS),
1876            "MC" => Ok(Self::MC),
1877            "ME" => Ok(Self::ME),
1878            "MF" => Ok(Self::MF),
1879            "MH" => Ok(Self::MH),
1880            "MI" => Ok(Self::MI),
1881            "MK" => Ok(Self::MK),
1882            "MP" => Ok(Self::MP),
1883            "MW" => Ok(Self::MW),
1884            "O2" => Ok(Self::O2),
1885            "OP" => Ok(Self::OP),
1886            "P2" => Ok(Self::P2),
1887            "PA" => Ok(Self::PA),
1888            "PC" => Ok(Self::PC),
1889            "RA" => Ok(Self::RA),
1890            "RC" => Ok(Self::RC),
1891            "RE" => Ok(Self::RE),
1892            "RI" => Ok(Self::RI),
1893            "RZ" => Ok(Self::RZ),
1894            "SC" => Ok(Self::SC),
1895            "SE" => Ok(Self::SE),
1896            "SF" => Ok(Self::SF),
1897            "SG" => Ok(Self::SG),
1898            "SH" => Ok(Self::SH),
1899            "SI" => Ok(Self::SI),
1900            "SR" => Ok(Self::SR),
1901            "ST" => Ok(Self::ST),
1902            "SV" => Ok(Self::SV),
1903            "TA" => Ok(Self::TA),
1904            "TL" => Ok(Self::TL),
1905            "TP" => Ok(Self::TP),
1906            "UA" => Ok(Self::UA),
1907            "VC" => Ok(Self::VC),
1908            _ => Err(Error::InvalidRecordType(s.to_string())),
1909        }
1910    }
1911}
1912
1913impl fmt::Display for RecordType {
1914    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1915        write!(f, "{}", self.as_str())
1916    }
1917}
1918
1919#[cfg(test)]
1920mod tests {
1921    use super::*;
1922
1923    #[test]
1924    fn test_radio_service_roundtrip() {
1925        // Test ALL RadioService variants for complete coverage
1926        let codes = [
1927            "AA", "AB", "AC", "AD", "AF", "AH", "AI", "AL", "AN", "AR", "AS", "AT", "AW", "BA",
1928            "BB", "BC", "BR", "BS", "CA", "CB", "CD", "CE", "CF", "CG", "CJ", "CL", "CM", "CN",
1929            "CO", "CP", "CR", "CT", "CW", "CX", "CY", "CZ", "DV", "ED", "GB", "GC", "GE", "GF",
1930            "GI", "GJ", "GL", "GM", "GO", "GP", "GR", "GS", "GU", "GW", "GX", "HA", "HV", "IG",
1931            "IK", "IQ", "LD", "LN", "LP", "LS", "LV", "LW", "MA", "MC", "MD", "MG", "MK", "MM",
1932            "MR", "MS", "MW", "NC", "NN", "OW", "PA", "PB", "PC", "PE", "PF", "PK", "PL", "PM",
1933            "PW", "QA", "QD", "QM", "QO", "QQ", "QT", "RP", "RR", "RS", "SA", "SB", "SE", "SG",
1934            "SL", "SP", "SY", "TB", "TC", "TI", "TN", "TP", "TS", "TT", "TZ", "UM", "UU", "VX",
1935            "WA", "WM", "WP", "WR", "WS", "WT", "WU", "WX", "WY", "WZ", "YB", "YC", "YD", "YE",
1936            "YF", "YG", "YH", "YI", "YJ", "YK", "YL", "YM", "YO", "YP", "YS", "YU", "YW", "YX",
1937            "ZA", "ZV",
1938        ];
1939        for code in codes {
1940            let service: RadioService = code
1941                .parse()
1942                .unwrap_or_else(|_| panic!("Failed to parse RadioService {}", code));
1943            assert_eq!(service.as_str(), code);
1944            assert_eq!(service.to_string(), code);
1945        }
1946    }
1947
1948    #[test]
1949    fn test_radio_service_amateur() {
1950        assert!(RadioService::HA.is_amateur());
1951        assert!(RadioService::HV.is_amateur());
1952        assert!(!RadioService::AC.is_amateur());
1953    }
1954
1955    #[test]
1956    fn test_radio_service_maritime() {
1957        assert!(RadioService::SA.is_maritime());
1958        assert!(RadioService::SB.is_maritime());
1959        assert!(RadioService::SE.is_maritime());
1960        assert!(RadioService::MA.is_maritime());
1961        assert!(RadioService::MC.is_maritime());
1962        assert!(!RadioService::HA.is_maritime());
1963        assert!(!RadioService::AC.is_maritime());
1964    }
1965
1966    #[test]
1967    fn test_radio_service_aircraft() {
1968        assert!(RadioService::AC.is_aircraft());
1969        assert!(RadioService::AF.is_aircraft());
1970        assert!(!RadioService::HA.is_aircraft());
1971        assert!(!RadioService::SA.is_aircraft());
1972    }
1973
1974    #[test]
1975    fn test_radio_service_description() {
1976        // Test that ALL RadioService variants have non-empty descriptions
1977        let all_services = [
1978            RadioService::AA,
1979            RadioService::AB,
1980            RadioService::AC,
1981            RadioService::AD,
1982            RadioService::AF,
1983            RadioService::AH,
1984            RadioService::AI,
1985            RadioService::AL,
1986            RadioService::AN,
1987            RadioService::AR,
1988            RadioService::AS,
1989            RadioService::AT,
1990            RadioService::AW,
1991            RadioService::BA,
1992            RadioService::BB,
1993            RadioService::BC,
1994            RadioService::BR,
1995            RadioService::BS,
1996            RadioService::CA,
1997            RadioService::CB,
1998            RadioService::CD,
1999            RadioService::CE,
2000            RadioService::CF,
2001            RadioService::CG,
2002            RadioService::CJ,
2003            RadioService::CL,
2004            RadioService::CM,
2005            RadioService::CN,
2006            RadioService::CO,
2007            RadioService::CP,
2008            RadioService::CR,
2009            RadioService::CT,
2010            RadioService::CW,
2011            RadioService::CX,
2012            RadioService::CY,
2013            RadioService::CZ,
2014            RadioService::DV,
2015            RadioService::ED,
2016            RadioService::GB,
2017            RadioService::GC,
2018            RadioService::GE,
2019            RadioService::GF,
2020            RadioService::GI,
2021            RadioService::GJ,
2022            RadioService::GL,
2023            RadioService::GM,
2024            RadioService::GO,
2025            RadioService::GP,
2026            RadioService::GR,
2027            RadioService::GS,
2028            RadioService::GU,
2029            RadioService::GW,
2030            RadioService::GX,
2031            RadioService::HA,
2032            RadioService::HV,
2033            RadioService::IG,
2034            RadioService::IK,
2035            RadioService::IQ,
2036            RadioService::LD,
2037            RadioService::LN,
2038            RadioService::LP,
2039            RadioService::LS,
2040            RadioService::LV,
2041            RadioService::LW,
2042            RadioService::MA,
2043            RadioService::MC,
2044            RadioService::MD,
2045            RadioService::MG,
2046            RadioService::MK,
2047            RadioService::MM,
2048            RadioService::MR,
2049            RadioService::MS,
2050            RadioService::MW,
2051            RadioService::NC,
2052            RadioService::NN,
2053            RadioService::OW,
2054            RadioService::PA,
2055            RadioService::PB,
2056            RadioService::PC,
2057            RadioService::PE,
2058            RadioService::PF,
2059            RadioService::PK,
2060            RadioService::PL,
2061            RadioService::PM,
2062            RadioService::PW,
2063            RadioService::QA,
2064            RadioService::QD,
2065            RadioService::QM,
2066            RadioService::QO,
2067            RadioService::QQ,
2068            RadioService::QT,
2069            RadioService::RP,
2070            RadioService::RR,
2071            RadioService::RS,
2072            RadioService::SA,
2073            RadioService::SB,
2074            RadioService::SE,
2075            RadioService::SG,
2076            RadioService::SL,
2077            RadioService::SP,
2078            RadioService::SY,
2079            RadioService::TB,
2080            RadioService::TC,
2081            RadioService::TI,
2082            RadioService::TN,
2083            RadioService::TP,
2084            RadioService::TS,
2085            RadioService::TT,
2086            RadioService::TZ,
2087            RadioService::UM,
2088            RadioService::UU,
2089            RadioService::VX,
2090            RadioService::WA,
2091            RadioService::WM,
2092            RadioService::WP,
2093            RadioService::WR,
2094            RadioService::WS,
2095            RadioService::WT,
2096            RadioService::WU,
2097            RadioService::WX,
2098            RadioService::WY,
2099            RadioService::WZ,
2100            RadioService::YB,
2101            RadioService::YC,
2102            RadioService::YD,
2103            RadioService::YE,
2104            RadioService::YF,
2105            RadioService::YG,
2106            RadioService::YH,
2107            RadioService::YI,
2108            RadioService::YJ,
2109            RadioService::YK,
2110            RadioService::YL,
2111            RadioService::YM,
2112            RadioService::YO,
2113            RadioService::YP,
2114            RadioService::YS,
2115            RadioService::YU,
2116            RadioService::YW,
2117            RadioService::YX,
2118            RadioService::ZA,
2119            RadioService::ZV,
2120        ];
2121        for service in all_services {
2122            let desc = service.description();
2123            assert!(
2124                !desc.is_empty(),
2125                "RadioService::{:?} has empty description",
2126                service
2127            );
2128        }
2129
2130        // Spot check some specific descriptions
2131        assert_eq!(RadioService::HA.description(), "Amateur");
2132        assert_eq!(RadioService::HV.description(), "Vanity (Amateur)");
2133        assert_eq!(
2134            RadioService::ZA.description(),
2135            "General Mobile Radio (GMRS)"
2136        );
2137        assert_eq!(RadioService::AC.description(), "Aircraft");
2138    }
2139
2140    #[test]
2141    fn test_radio_service_invalid() {
2142        let result: Result<RadioService> = "XX".parse();
2143        assert!(result.is_err());
2144    }
2145
2146    #[test]
2147    fn test_radio_service_case_insensitive() {
2148        let upper: RadioService = "HA".parse().unwrap();
2149        let lower: RadioService = "ha".parse().unwrap();
2150        let mixed: RadioService = "Ha".parse().unwrap();
2151        assert_eq!(upper, lower);
2152        assert_eq!(lower, mixed);
2153    }
2154
2155    #[test]
2156    fn test_operator_class_roundtrip() {
2157        for code in ["A", "E", "G", "N", "P", "T"] {
2158            let class: OperatorClass = code.parse().unwrap();
2159            assert_eq!(class.as_str(), code);
2160            assert_eq!(class.to_string(), code);
2161        }
2162    }
2163
2164    #[test]
2165    fn test_operator_class_description() {
2166        assert_eq!(OperatorClass::Extra.description(), "Amateur Extra");
2167        assert_eq!(OperatorClass::Advanced.description(), "Advanced");
2168        assert_eq!(OperatorClass::General.description(), "General");
2169        assert_eq!(OperatorClass::Technician.description(), "Technician");
2170        assert_eq!(
2171            OperatorClass::TechnicianPlus.description(),
2172            "Technician Plus"
2173        );
2174        assert_eq!(OperatorClass::Novice.description(), "Novice");
2175    }
2176
2177    #[test]
2178    fn test_operator_class_invalid() {
2179        let result: Result<OperatorClass> = "X".parse();
2180        assert!(result.is_err());
2181    }
2182
2183    #[test]
2184    fn test_operator_class_case_insensitive() {
2185        let upper: OperatorClass = "E".parse().unwrap();
2186        let lower: OperatorClass = "e".parse().unwrap();
2187        assert_eq!(upper, lower);
2188    }
2189
2190    #[test]
2191    fn test_license_status_roundtrip() {
2192        for code in ["A", "C", "E", "L", "P", "T", "X"] {
2193            let status: LicenseStatus = code.parse().unwrap();
2194            assert_eq!(status.as_str(), code);
2195            assert_eq!(status.to_string(), code);
2196        }
2197    }
2198
2199    #[test]
2200    fn test_license_status_is_active() {
2201        assert!(LicenseStatus::Active.is_active());
2202        assert!(!LicenseStatus::Expired.is_active());
2203        assert!(!LicenseStatus::Cancelled.is_active());
2204        assert!(!LicenseStatus::Terminated.is_active());
2205        assert!(!LicenseStatus::PendingLegalStatus.is_active());
2206        assert!(!LicenseStatus::ParentStationCancelled.is_active());
2207        assert!(!LicenseStatus::TermPending.is_active());
2208    }
2209
2210    #[test]
2211    fn test_license_status_invalid() {
2212        let result: Result<LicenseStatus> = "Z".parse();
2213        assert!(result.is_err());
2214    }
2215
2216    #[test]
2217    fn test_license_status_case_insensitive() {
2218        let upper: LicenseStatus = "A".parse().unwrap();
2219        let lower: LicenseStatus = "a".parse().unwrap();
2220        assert_eq!(upper, lower);
2221    }
2222
2223    #[test]
2224    fn test_application_purpose_roundtrip() {
2225        let codes = [
2226            "AA", "AM", "AR", "AU", "CA", "CB", "DC", "DU", "EX", "HA", "LC", "LE", "LM", "LN",
2227            "LT", "LU", "MD", "NE", "NT", "RE", "RL", "RM", "RO", "TC", "WD",
2228        ];
2229        for code in codes {
2230            let purpose: ApplicationPurpose = code
2231                .parse()
2232                .unwrap_or_else(|_| panic!("Failed to parse ApplicationPurpose {}", code));
2233            assert_eq!(purpose.as_str(), code);
2234            assert_eq!(purpose.to_string(), code);
2235        }
2236    }
2237
2238    #[test]
2239    fn test_application_purpose_specific_values() {
2240        assert_eq!(ApplicationPurpose::AssignmentOfAuthorization.as_str(), "AA");
2241        assert_eq!(ApplicationPurpose::New.as_str(), "NE");
2242        assert_eq!(ApplicationPurpose::RenewalOnly.as_str(), "RO");
2243        assert_eq!(ApplicationPurpose::Modification.as_str(), "MD");
2244        assert_eq!(ApplicationPurpose::Withdrawal.as_str(), "WD");
2245    }
2246
2247    #[test]
2248    fn test_application_purpose_invalid() {
2249        let result: Result<ApplicationPurpose> = "XX".parse();
2250        assert!(result.is_err());
2251    }
2252
2253    #[test]
2254    fn test_application_purpose_case_insensitive() {
2255        let upper: ApplicationPurpose = "NE".parse().unwrap();
2256        let lower: ApplicationPurpose = "ne".parse().unwrap();
2257        let mixed: ApplicationPurpose = "Ne".parse().unwrap();
2258        assert_eq!(upper, lower);
2259        assert_eq!(lower, mixed);
2260    }
2261
2262    #[test]
2263    fn test_application_status_roundtrip() {
2264        let codes = [
2265            "1", "2", "A", "C", "D", "E", "G", "H", "I", "J", "K", "M", "N", "P", "Q", "R", "S",
2266            "T", "U", "W", "X", "Y",
2267        ];
2268        for code in codes {
2269            let status: ApplicationStatus = code
2270                .parse()
2271                .unwrap_or_else(|_| panic!("Failed to parse ApplicationStatus {}", code));
2272            assert_eq!(status.as_str(), code);
2273            assert_eq!(status.to_string(), code);
2274        }
2275    }
2276
2277    #[test]
2278    fn test_application_status_specific_values() {
2279        assert_eq!(ApplicationStatus::Submitted.as_str(), "1");
2280        assert_eq!(ApplicationStatus::Pending.as_str(), "2");
2281        assert_eq!(ApplicationStatus::Granted.as_str(), "G");
2282        assert_eq!(ApplicationStatus::Withdrawn.as_str(), "W");
2283        assert_eq!(ApplicationStatus::Terminated.as_str(), "T");
2284        assert_eq!(ApplicationStatus::HasProblems.as_str(), "Y");
2285    }
2286
2287    #[test]
2288    fn test_application_status_invalid() {
2289        let result: Result<ApplicationStatus> = "Z".parse();
2290        assert!(result.is_err());
2291    }
2292
2293    #[test]
2294    fn test_application_status_case_insensitive() {
2295        let upper: ApplicationStatus = "G".parse().unwrap();
2296        let lower: ApplicationStatus = "g".parse().unwrap();
2297        assert_eq!(upper, lower);
2298    }
2299
2300    #[test]
2301    fn test_entity_type_roundtrip() {
2302        let codes = ["CE", "CL", "CR", "CS", "E", "L", "O", "R", "S"];
2303        for code in codes {
2304            let entity: EntityType = code
2305                .parse()
2306                .unwrap_or_else(|_| panic!("Failed to parse EntityType {}", code));
2307            assert_eq!(entity.as_str(), code);
2308            assert_eq!(entity.to_string(), code);
2309        }
2310    }
2311
2312    #[test]
2313    fn test_entity_type_specific_values() {
2314        assert_eq!(EntityType::Licensee.as_str(), "L");
2315        assert_eq!(EntityType::LicenseeContact.as_str(), "CL");
2316        assert_eq!(EntityType::Transferee.as_str(), "E");
2317        assert_eq!(EntityType::TransfereeContact.as_str(), "CE");
2318        assert_eq!(EntityType::Owner.as_str(), "O");
2319        assert_eq!(EntityType::Assignor.as_str(), "R");
2320        assert_eq!(EntityType::Lessee.as_str(), "S");
2321    }
2322
2323    #[test]
2324    fn test_entity_type_invalid() {
2325        let result: Result<EntityType> = "XX".parse();
2326        assert!(result.is_err());
2327    }
2328
2329    #[test]
2330    fn test_entity_type_case_insensitive() {
2331        let upper: EntityType = "L".parse().unwrap();
2332        let lower: EntityType = "l".parse().unwrap();
2333        assert_eq!(upper, lower);
2334    }
2335
2336    #[test]
2337    fn test_record_type_dat_filename() {
2338        assert_eq!(RecordType::HD.dat_filename(), "HD.dat");
2339        assert_eq!(RecordType::AM.dat_filename(), "AM.dat");
2340        assert_eq!(RecordType::EN.dat_filename(), "EN.dat");
2341    }
2342
2343    #[test]
2344    fn test_all_record_types_parse() {
2345        let codes = [
2346            "A2", "A3", "AC", "AD", "AG", "AH", "AM", "AN", "AP", "AS", "AT", "BC", "BD", "BE",
2347            "BF", "BL", "BO", "BT", "CD", "CF", "CG", "CO", "CP", "CS", "EC", "EM", "EN", "F2",
2348            "F3", "F4", "F5", "F6", "FA", "FC", "FF", "FR", "FS", "FT", "HD", "HS", "IA", "IF",
2349            "IR", "L2", "L3", "L4", "L5", "L6", "LA", "LC", "LD", "LF", "LH", "LL", "LM", "LO",
2350            "LS", "MC", "ME", "MF", "MH", "MI", "MK", "MP", "MW", "O2", "OP", "P2", "PA", "PC",
2351            "RA", "RC", "RE", "RI", "RZ", "SC", "SE", "SF", "SG", "SH", "SI", "SR", "ST", "SV",
2352            "TA", "TL", "TP", "UA", "VC",
2353        ];
2354
2355        for code in codes {
2356            let rt: RecordType = code
2357                .parse()
2358                .unwrap_or_else(|_| panic!("Failed to parse {}", code));
2359            assert_eq!(rt.as_str(), code);
2360            assert_eq!(rt.to_string(), code);
2361        }
2362    }
2363
2364    #[test]
2365    fn test_record_type_invalid() {
2366        let result: Result<RecordType> = "XX".parse();
2367        assert!(result.is_err());
2368    }
2369
2370    #[test]
2371    fn test_record_type_case_insensitive() {
2372        let upper: RecordType = "HD".parse().unwrap();
2373        let lower: RecordType = "hd".parse().unwrap();
2374        let mixed: RecordType = "Hd".parse().unwrap();
2375        assert_eq!(upper, lower);
2376        assert_eq!(lower, mixed);
2377    }
2378}