#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Symbol {
pub table: char,
pub code: char,
}
impl Symbol {
pub fn new(table: char, code: char) -> Self {
Self { table, code }
}
pub fn is_primary_table(self) -> bool {
self.table == '/'
}
pub fn is_alternate_table(self) -> bool {
self.table == '\\'
}
pub fn overlay(self) -> Option<char> {
if self.table.is_ascii_alphanumeric() && self.table != '/' {
Some(self.table)
} else {
None
}
}
pub fn description(self) -> Option<&'static str> {
let idx = self.code as usize;
if !(33..=126).contains(&idx) {
return None;
}
let i = idx - 33; if self.is_primary_table() {
PRIMARY[i]
} else {
ALTERNATE[i]
}
}
}
#[rustfmt::skip]
const PRIMARY: [Option<&'static str>; 94] = [
Some("Police, Sheriff"), None, Some("Digipeater"), Some("Phone"), Some("DX Cluster"), Some("HF Gateway"), Some("Small Aircraft"), Some("Mobile Satellite Station"), Some("Wheelchair, Handicapped"), Some("Snowflake"), Some("Red Cross"), Some("Boy Scouts"), Some("House"), Some("X"), Some("Dot"), Some("Circle 0"), Some("Circle 1"), Some("Circle 2"), Some("Circle 3"), Some("Circle 4"), Some("Circle 5"), Some("Circle 6"), Some("Circle 7"), Some("Circle 8"), Some("Circle 9"), Some("Fire"), Some("Campground, Tent"), Some("Motorcycle"), Some("Railroad Engine"), Some("Car"), Some("File Server"), Some("Hurricane, Tropical Storm"), None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, Some("Jogger"), Some("Triangle"), Some("PBBS"), Some("Large Aircraft"), Some("Weather Station"), Some("Satellite Dish"), Some("Ambulance"), Some("Bike"), Some("Incident Command Post"), Some("Fire Dept"), Some("Horse, Equestrian"), Some("Fire Truck"), Some("Glider"), Some("Hospital"), Some("IOTA"), Some("Jeep"), Some("Truck"), Some("Laptop"), Some("Mic-E Repeater"), Some("Node"), Some("EOC"), Some("Rover, Dog"), Some("Grid Square"), Some("Antenna"), Some("Power Boat"), Some("Truck Stop"), Some("18-Wheeler"), Some("Van"), Some("Water Station"), Some("APRS"), Some("Yagi Antenna"), Some("Shelter"), None, None, None, None, ];
#[rustfmt::skip]
const ALTERNATE: [Option<&'static str>; 94] = [
Some("Emergency"), None, Some("Digipeater (numbered)"), Some("ATM, Bank"), Some("Accident Scene"), Some("Haze"), Some("Flash"), Some("Cloud"), Some("Sunny, Partly Cloudy"), Some("Snow"), Some("Church"), Some("Girl Scouts"), Some("House, Shack"), Some("X"), Some("Circle"), Some("Circle 0 (overlay)"), Some("Circle 1 (overlay)"), Some("Circle 2 (overlay)"), Some("Circle 3 (overlay)"), Some("Circle 4 (overlay)"), Some("Circle 5 (overlay)"), Some("Circle 6 (overlay)"), Some("Circle 7 (overlay)"), Some("Circle 8 (overlay)"), Some("Circle 9 (overlay)"), Some("Hail"), Some("Park, Picnic"), Some("NWS Advisory"), Some("Railroad Station"), Some("Info Kiosk"), Some("Work Zone"), Some("Tornado"), None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, Some("Wall Cloud"), Some("Misc Aircraft"), Some("Rocket Launch"), Some("Jet Aircraft"), Some("Funnel Cloud"), Some("Rain Shower"), Some("ARES"), Some("Blowing Snow"), Some("Coast Guard"), Some("Drizzle"), Some("Smoke"), Some("Freezing Rain"), Some("Snow Shower"), Some("Haze"), Some("Rain Shower"), Some("Lightning"), Some("Kenwood Radio"), Some("Lighthouse"), Some("MARS"), Some("Navigation Buoy"), Some("Rocket"), Some("Parking"), Some("Earthquake"), Some("Restaurant"), Some("Satellite"), Some("Thunderstorm"), Some("Sunny"), Some("VORTAC, Nav Aid"), Some("NWS Site"), Some("Pharmacy"), Some("Radiosonde"), Some("Shelter"), Some("Fog"), None, None, None, ];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn primary_car() {
let s = Symbol::new('/', '>');
assert_eq!(s.description(), Some("Car"));
}
#[test]
fn primary_house() {
let s = Symbol::new('/', '-');
assert_eq!(s.description(), Some("House"));
}
#[test]
fn primary_weather_station() {
let s = Symbol::new('/', '_');
assert_eq!(s.description(), Some("Weather Station"));
}
#[test]
fn alternate_tornado() {
let s = Symbol::new('\\', '@');
assert_eq!(s.description(), Some("Tornado"));
}
#[test]
fn overlay_uses_alternate_table() {
let s = Symbol::new('3', '>');
assert_eq!(s.description(), Some("Info Kiosk"));
}
#[test]
fn reserved_overlay_code_returns_none() {
let s = Symbol::new('/', 'A');
assert_eq!(s.description(), None);
}
#[test]
fn out_of_range_code_returns_none() {
let s = Symbol::new('/', '\x01');
assert_eq!(s.description(), None);
}
#[test]
fn all_primary_entries_no_panic() {
for code in '!'..='~' {
let _ = Symbol::new('/', code).description();
}
}
#[test]
fn all_alternate_entries_no_panic() {
for code in '!'..='~' {
let _ = Symbol::new('\\', code).description();
}
}
}