opening_hours/localization/country/
mod.rs1mod generated;
2
3use std::collections::HashMap;
4use std::sync::{Arc, LazyLock};
5
6use compact_calendar::CompactCalendar;
7use flate2::bufread::DeflateDecoder;
8
9use crate::ContextHolidays;
10
11pub use generated::*;
13
14#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
20pub struct UnknownCountryCode(pub String);
21
22impl std::fmt::Display for UnknownCountryCode {
23 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24 write!(f, "Unknown ISO code `{}`", self.0)
25 }
26}
27
28impl std::error::Error for UnknownCountryCode {}
29
30impl Country {
35 #[cfg(feature = "auto-country")]
45 pub fn try_from_coords(coords: crate::localization::Coordinates) -> Option<Self> {
46 use country_boundaries::CountryBoundaries;
47 use std::io::Read;
48
49 static BOUNDARIES: LazyLock<CountryBoundaries> = LazyLock::new(|| {
50 let mut buffer = Vec::new();
51
52 DeflateDecoder::new(include_bytes!(env!("COUNTRY_BOUNDS_FILE")).as_slice())
53 .read_to_end(&mut buffer)
54 .expect("unable to parse country bounds data");
55
56 CountryBoundaries::from_reader(buffer.as_slice())
57 .expect("failed to load country boundaries database")
58 });
59
60 for cc in BOUNDARIES.ids(country_boundaries::LatLon::new(coords.lat(), coords.lon()).ok()?)
61 {
62 if let Ok(res) = cc.parse() {
63 return Some(res);
64 }
65 }
66
67 None
68 }
69
70 pub fn holidays(self) -> ContextHolidays {
81 static DB_PUBLIC_GLOBAL: LazyLock<HashMap<Country, Arc<CompactCalendar>>> =
82 LazyLock::new(|| {
83 decode_holidays_db(
84 env!("HOLIDAYS_PUBLIC_GLOBAL_REGIONS"),
85 include_bytes!(env!("HOLIDAYS_PUBLIC_GLOBAL_FILE")),
86 )
87 });
88
89 static DB_SCHOOL_GLOBAL: LazyLock<HashMap<Country, Arc<CompactCalendar>>> =
90 LazyLock::new(|| {
91 decode_holidays_db(
92 env!("HOLIDAYS_SCHOOL_GLOBAL_REGIONS"),
93 include_bytes!(env!("HOLIDAYS_SCHOOL_GLOBAL_FILE")),
94 )
95 });
96
97 ContextHolidays::new(
98 DB_PUBLIC_GLOBAL.get(&self).cloned().unwrap_or_default(),
99 DB_SCHOOL_GLOBAL.get(&self).cloned().unwrap_or_default(),
100 )
101 }
102
103 pub fn holidays_regional(self) -> ContextHolidays {
115 static DB_PUBLIC_REGIONAL: LazyLock<HashMap<Country, Arc<CompactCalendar>>> =
116 LazyLock::new(|| {
117 decode_holidays_db(
118 env!("HOLIDAYS_PUBLIC_REGIONAL_REGIONS"),
119 include_bytes!(env!("HOLIDAYS_PUBLIC_REGIONAL_FILE")),
120 )
121 });
122
123 static DB_SCHOOL_REGIONAL: LazyLock<HashMap<Country, Arc<CompactCalendar>>> =
124 LazyLock::new(|| {
125 decode_holidays_db(
126 env!("HOLIDAYS_SCHOOL_REGIONAL_REGIONS"),
127 include_bytes!(env!("HOLIDAYS_SCHOOL_REGIONAL_FILE")),
128 )
129 });
130
131 ContextHolidays::new(
132 DB_PUBLIC_REGIONAL.get(&self).cloned().unwrap_or_default(),
133 DB_SCHOOL_REGIONAL.get(&self).cloned().unwrap_or_default(),
134 )
135 }
136}
137
138fn decode_holidays_db(
140 countries: &'static str,
141 encoded_data: &'static [u8],
142) -> HashMap<Country, Arc<CompactCalendar>> {
143 let mut reader = DeflateDecoder::new(encoded_data);
144
145 countries
146 .split(',')
147 .filter_map(|region| {
148 let calendar =
149 CompactCalendar::deserialize(&mut reader).expect("unable to parse holiday data");
150
151 let Ok(country) = region.parse() else {
152 #[cfg(feature = "log")]
153 log::warn!("Unknown initialized country code {region}");
154 return None;
155 };
156
157 Some((country, Arc::new(calendar)))
158 })
159 .collect()
160}