1use std::path::Path;
2
3use crate::data::{EngineRow, EuModelRow, ModelRow};
4use crate::maps::{FstMap, FstSet, data_dir};
5use crate::types::{BodyClass, FuelType};
6
7pub struct Catalog {
13 makes: FstSet,
14 make_models: FstMap<ModelRow>,
15 eu_brand_models: Option<FstMap<EuModelRow>>,
16 eu_engines: Option<FstMap<EngineRow>>,
17}
18
19impl Catalog {
20 pub fn new() -> crate::Result<Self> {
22 let dir = data_dir();
23 #[cfg(feature = "embedded")]
24 crate::embedded::ensure_installed(&dir).ok();
25 Self::open(&dir)
26 }
27
28 pub fn open(dir: &Path) -> crate::Result<Self> {
31 let eu_brand_models = if dir.join("eu_brand_models.fst").exists() {
32 Some(FstMap::open(dir)?)
33 } else {
34 None
35 };
36 let eu_engines = if dir.join("eu_engines.fst").exists() {
37 Some(FstMap::open(dir)?)
38 } else {
39 None
40 };
41 Ok(Catalog {
42 makes: FstSet::open(&dir.join("makes.fst"))?,
43 make_models: FstMap::open(dir)?,
44 eu_brand_models,
45 eu_engines,
46 })
47 }
48
49 pub fn all_makes(&self) -> Vec<String> {
51 self.makes.keys()
52 }
53
54 pub fn has_make(&self, make: &str) -> bool {
56 self.makes.contains(&make.to_ascii_uppercase())
57 }
58
59 pub fn make_count(&self) -> u64 {
61 self.makes.len()
62 }
63
64 pub fn models_for_make(&self, make: &str) -> Vec<String> {
69 let key = crate::decoder::normalize_make(make);
70 let mut models: Vec<String> = self
71 .make_models
72 .get(&key)
73 .map(|rows| rows.into_iter().map(|r| r.name).collect())
74 .unwrap_or_default();
75 if let Some(eu) = &self.eu_brand_models {
76 if let Some(rows) = eu.get(&key) {
77 for r in rows {
78 models.push(r.name);
79 }
80 }
81 }
82 models.sort();
83 models.dedup();
84 models
85 }
86
87 pub fn eu_models_for(&self, brand: &str) -> Vec<EuModelRow> {
91 let key = crate::decoder::normalize_make(brand);
92 self.eu_brand_models
93 .as_ref()
94 .and_then(|m| m.get(&key))
95 .unwrap_or_default()
96 }
97
98 pub fn engines_for_brand(&self, brand: &str) -> Vec<EngineRow> {
101 let key = crate::decoder::normalize_make(brand);
102 self.eu_engines
103 .as_ref()
104 .and_then(|m| m.get(&key))
105 .unwrap_or_default()
106 }
107
108 pub fn engines_for(&self, brand: &str, model: &str) -> Vec<EngineRow> {
111 let model_key = model.to_ascii_uppercase();
112 self.engines_for_brand(brand)
113 .into_iter()
114 .filter(|r| r.model == model_key)
115 .collect()
116 }
117
118 pub fn body_classes() -> &'static [BodyClass] {
120 &[
121 BodyClass::Sedan,
122 BodyClass::Coupe,
123 BodyClass::Hatchback,
124 BodyClass::Wagon,
125 BodyClass::Convertible,
126 BodyClass::Suv,
127 BodyClass::Crossover,
128 BodyClass::Pickup,
129 BodyClass::Van,
130 BodyClass::Minivan,
131 BodyClass::Bus,
132 BodyClass::Truck,
133 BodyClass::Motorcycle,
134 BodyClass::Trailer,
135 BodyClass::Incomplete,
136 BodyClass::Other,
137 ]
138 }
139
140 pub fn fuel_types() -> &'static [FuelType] {
142 &[
143 FuelType::Gasoline,
144 FuelType::Diesel,
145 FuelType::Electric,
146 FuelType::Hybrid,
147 FuelType::PluginHybrid,
148 FuelType::Ethanol,
149 FuelType::FlexFuel,
150 FuelType::Cng,
151 FuelType::Lng,
152 FuelType::Lpg,
153 FuelType::Hydrogen,
154 FuelType::FuelCell,
155 FuelType::Methanol,
156 FuelType::NaturalGas,
157 FuelType::Other,
158 ]
159 }
160}