1use std::collections::{BTreeMap, BTreeSet};
2
3use serde::{Deserialize, Serialize};
4
5use crate::CityName;
6
7#[derive(Serialize, Deserialize)]
11pub struct Manifest {
12 pub entries: BTreeMap<String, Entry>,
14}
15
16#[derive(Serialize, Deserialize)]
18pub struct Entry {
19 pub checksum: String,
21 pub uncompressed_size_bytes: u64,
24 pub compressed_size_bytes: u64,
26}
27
28impl Manifest {
29 #[cfg(not(target_arch = "wasm32"))]
30 pub fn load() -> Manifest {
31 crate::maybe_read_json(
32 crate::path("MANIFEST.json"),
33 &mut abstutil::Timer::throwaway(),
34 )
35 .unwrap()
36 }
37
38 #[cfg(target_arch = "wasm32")]
39 pub fn load() -> Manifest {
40 abstutil::from_json(&include_bytes!("../../data/MANIFEST.json").to_vec()).unwrap()
41 }
42
43 pub fn filter(mut self, data_packs: DataPacks) -> Manifest {
45 let mut remove = Vec::new();
46 for path in self.entries.keys() {
47 if path.starts_with("data/system/extra_fonts") {
48 continue;
50 }
51 if path.starts_with("data/input/shared") {
54 if path.ends_with("Road Safety Data - Accidents 2019.csv")
57 || path.ends_with("wu03ew_v2.csv")
58 || path.ends_with("zones_core.geojson")
59 {
60 if data_packs.input.iter().any(|x| x.starts_with("gb/")) {
61 continue;
62 }
63 } else if path.ends_with("kc_2016_lidar.tif") {
64 if data_packs.input.contains("us/seattle") {
65 continue;
66 }
67 } else if !data_packs.input.is_empty() {
68 continue;
69 }
70 }
71
72 let parts = path.split('/').collect::<Vec<_>>();
73 let mut data_pack = format!("{}/{}", parts[2], parts[3]);
74 if Manifest::is_file_part_of_huge_seattle(path) {
75 data_pack = "us/huge_seattle".to_string();
76 }
77 if parts[1] == "input" {
78 if data_packs.input.contains(&data_pack) {
79 continue;
80 }
81 } else if parts[1] == "system" {
82 if data_packs.runtime.contains(&data_pack) {
83 continue;
84 }
85 } else {
86 panic!("Wait what's {}", path);
87 }
88 remove.push(path.clone());
89 }
90 for path in remove {
91 self.entries.remove(&path).unwrap();
92 }
93 self
94 }
95
96 pub fn is_file_part_of_huge_seattle(path: &str) -> bool {
101 let path = path
102 .strip_prefix(&crate::path(""))
103 .or_else(|| path.strip_prefix("data/"))
104 .unwrap_or(path);
105 let name = if let Some(x) = path.strip_prefix("system/us/seattle/maps/") {
106 x.strip_suffix(".bin").unwrap()
107 } else if let Some(x) = path.strip_prefix("system/us/seattle/scenarios/") {
108 x.split('/').next().unwrap()
109 } else if let Some(x) = path.strip_prefix("system/us/seattle/prebaked_results/") {
110 x.split('/').next().unwrap()
111 } else {
112 return false;
113 };
114 name == "huge_seattle"
115 || name == "north_seattle"
116 || name == "south_seattle"
117 || name == "west_seattle"
118 }
119
120 pub fn path_to_city(path: &str) -> Option<CityName> {
122 let parts = path.split('/').collect::<Vec<_>>();
123 if parts[1] == "system" {
124 if parts[2] == "assets"
125 || parts[2] == "extra_fonts"
126 || parts[2] == "ltn_proposals"
127 || parts[2] == "proposals"
128 || parts[2] == "study_areas"
129 {
130 return None;
131 }
132 return Some(CityName::new(parts[2], parts[3]));
133 }
134 None
135 }
136
137 pub fn get_entry(&self, path: &str) -> Option<&Entry> {
139 let path = path.strip_prefix(&crate::path("")).unwrap_or(path);
140 self.entries.get(&format!("data/{}", path))
141 }
142}
143
144#[derive(Serialize, Deserialize)]
146pub struct DataPacks {
147 pub runtime: BTreeSet<String>,
150 pub input: BTreeSet<String>,
152}
153
154impl DataPacks {
155 #[cfg(not(target_arch = "wasm32"))]
157 pub fn load_or_create() -> DataPacks {
158 let path = crate::path_player("data.json");
159 match crate::maybe_read_json::<DataPacks>(path.clone(), &mut abstutil::Timer::throwaway()) {
160 Ok(cfg) => cfg,
161 Err(err) => {
162 warn!("player/data.json invalid, assuming defaults: {}", err);
163 let mut cfg = DataPacks {
164 runtime: BTreeSet::new(),
165 input: BTreeSet::new(),
166 };
167 cfg.runtime.insert("us/seattle".to_string());
168 crate::write_json(path, &cfg);
169 cfg
170 }
171 }
172 }
173
174 #[cfg(not(target_arch = "wasm32"))]
176 pub fn save(&self) {
177 crate::write_json(crate::path_player("data.json"), self);
178 }
179
180 pub fn all_data_packs() -> DataPacks {
182 let mut data_packs = DataPacks {
183 runtime: BTreeSet::new(),
184 input: BTreeSet::new(),
185 };
186 for path in Manifest::load().entries.keys() {
187 if path.starts_with("data/system/extra_fonts") || path.starts_with("data/input/shared")
188 {
189 continue;
190 }
191 let parts = path.split('/').collect::<Vec<_>>();
192 let mut city = format!("{}/{}", parts[2], parts[3]);
193 if Manifest::is_file_part_of_huge_seattle(path) {
194 city = "us/huge_seattle".to_string();
195 }
196 if parts[1] == "input" {
197 data_packs.input.insert(city);
198 } else if parts[1] == "system" {
199 data_packs.runtime.insert(city);
200 }
201 }
202 data_packs
203 }
204}