use crate::zipstore;
use crate::ZONEINFO_ZIP;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ZoneMeta<'a> {
pub lat: f64,
pub lon: f64,
pub commentary: &'a str,
codes: &'a str,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Country<'a> {
pub code: &'a str,
pub name: &'a str,
}
impl ZoneMeta<'static> {
pub fn countries(&self) -> impl Iterator<Item = Country<'static>> {
self.codes.split(',').map(|code| Country {
code,
name: iso_name(code),
})
}
}
pub fn meta(name: &str) -> Option<ZoneMeta<'static>> {
let data = zipstore::find(ZONEINFO_ZIP, "zone1970.tab").ok()?;
let text = core::str::from_utf8(data).ok()?;
for line in text.split('\n') {
if line.is_empty() || line.starts_with('#') {
continue;
}
let mut fields = line.split('\t');
let codes = fields.next()?;
let coord = fields.next()?;
let zname = match fields.next() {
Some(z) => z,
None => continue,
};
if zname != name {
continue;
}
let commentary = fields.next().unwrap_or("");
let (lat, lon) = parse_iso6709(coord);
return Some(ZoneMeta {
lat,
lon,
commentary,
codes,
});
}
None
}
fn iso_name(code: &str) -> &'static str {
let data = match zipstore::find(ZONEINFO_ZIP, "iso3166.tab") {
Ok(d) => d,
Err(_) => return "",
};
let text = core::str::from_utf8(data).unwrap_or("");
for line in text.split('\n') {
if line.is_empty() || line.starts_with('#') {
continue;
}
let mut parts = line.splitn(2, '\t');
let c = parts.next().unwrap_or("");
if c == code {
return parts.next().unwrap_or("");
}
}
""
}
pub fn parse_iso6709(s: &str) -> (f64, f64) {
let b = s.as_bytes();
let mut lon_start = None;
for (i, &c) in b.iter().enumerate().skip(1) {
if c == b'+' || c == b'-' {
lon_start = Some(i);
break;
}
}
let Some(lon_start) = lon_start else {
return (0.0, 0.0);
};
let lat = parse_dms(&s[..lon_start], 2);
let lon = parse_dms(&s[lon_start..], 3);
(lat, lon)
}
fn parse_dms(s: &str, deg_digits: usize) -> f64 {
let b = s.as_bytes();
if b.len() < 1 + deg_digits + 2 {
return 0.0;
}
let neg = b[0] == b'-';
let mut i = 1;
let deg = atoi(&b[i..i + deg_digits]);
i += deg_digits;
let min = atoi(&b[i..i + 2]);
i += 2;
let sec = if b.len() >= i + 2 {
atoi(&b[i..i + 2])
} else {
0
};
let total_seconds = deg * 3600 + min * 60 + sec;
let val_e4 = (total_seconds * 10000 + 1800) / 3600;
let v = val_e4 as f64 / 10000.0;
if neg {
-v
} else {
v
}
}
fn atoi(b: &[u8]) -> i64 {
let mut n = 0i64;
for &c in b {
if c.is_ascii_digit() {
n = n * 10 + (c - b'0') as i64;
}
}
n
}