1use std::any::Any;
6use std::fmt::Debug;
7use std::mem::take;
8
9use as_any::AsAny;
10use itertools::Itertools;
11use rayon::prelude::*;
12use strum::EnumCount;
13
14use crate::block::Block;
15use crate::context::ScopeContext;
16use crate::everything::Everything;
17use crate::game::Game;
18use crate::helpers::{TigerHashMap, TigerHashSet, dup_error, exact_dup_advice, exact_dup_error};
19use crate::item::Item;
20#[cfg(any(feature = "vic3", feature = "eu5"))]
21use crate::item::ItemExt;
22use crate::lowercase::Lowercase;
23#[cfg(any(feature = "vic3", feature = "eu5"))]
24use crate::report::{ErrorKey, err};
25use crate::token::Token;
26use crate::variables::Variables;
27
28pub type FlagValidator = fn(&Token, &Everything);
29
30#[derive(Debug)]
32pub struct Db {
33 database: Vec<TigerHashMap<&'static str, DbEntry>>,
36 flags: Vec<(TigerHashSet<Token>, Option<FlagValidator>)>,
40 anonymous: Vec<DbEntry>,
42 items_lc: Vec<TigerHashMap<Lowercase<'static>, &'static str>>,
44}
45
46impl Default for Db {
47 fn default() -> Self {
48 Self {
49 database: (0..Item::COUNT).map(|_| TigerHashMap::default()).collect(),
50 flags: (0..Item::COUNT).map(|_| (TigerHashSet::default(), None)).collect(),
51 anonymous: Vec::new(),
52 items_lc: (0..Item::COUNT).map(|_| TigerHashMap::default()).collect(),
53 }
54 }
55}
56
57impl Db {
58 pub fn add(&mut self, item: Item, key: Token, block: Block, kind: Box<dyn DbKind>) {
59 self.add_inner(item, key, block, kind, false);
60 }
61
62 #[allow(dead_code)]
63 pub fn add_exact_dup_ok(
64 &mut self,
65 item: Item,
66 key: Token,
67 block: Block,
68 kind: Box<dyn DbKind>,
69 ) {
70 self.add_inner(item, key, block, kind, true);
71 }
72
73 fn add_inner(
74 &mut self,
75 item: Item,
76 key: Token,
77 block: Block,
78 kind: Box<dyn DbKind>,
79 exact_dup_ok: bool,
80 ) {
81 if Game::is_vic3() || Game::is_eu5() {
82 #[cfg(any(feature = "vic3", feature = "eu5"))]
83 if let Some((prefix, name)) = key.split_once(':') {
84 if !item.injectable() {
85 let msg = format!("cannot use prefixes with {item}");
86 err(ErrorKey::Prefixes).msg(msg).loc(key).push();
87 return;
88 }
89 match prefix.as_str() {
90 "INJECT" => {
91 if let Some(other) = self.database[item as usize].get_mut(name.as_str()) {
92 other.inject(block, kind);
93 } else {
94 let msg = "injecting into a non-existing item";
95 err(ErrorKey::Prefixes).msg(msg).loc(name).push();
96 }
97 }
98 "REPLACE" => {
99 if self.database[item as usize].contains_key(name.as_str()) {
100 self.add_inner2(item, name, block, kind);
101 } else {
102 let msg = "replacing a non-existing item";
103 err(ErrorKey::Prefixes).msg(msg).loc(name).push();
104 }
105 }
106 "TRY_INJECT" => {
107 if let Some(other) = self.database[item as usize].get_mut(name.as_str()) {
108 other.inject(block, kind);
109 }
110 }
111 "TRY_REPLACE" => {
112 if self.database[item as usize].contains_key(name.as_str()) {
113 self.add_inner2(item, name, block, kind);
114 }
115 }
116 "REPLACE_OR_CREATE" => {
117 self.add_inner2(item, name, block, kind);
118 }
119 "INJECT_OR_CREATE" => {
120 if let Some(other) = self.database[item as usize].get_mut(name.as_str()) {
121 other.inject(block, kind);
122 } else {
123 self.add_inner2(item, name, block, kind);
124 }
125 }
126 _ => {
127 let msg = format!("unknown prefix `{prefix}`");
128 err(ErrorKey::Prefixes).msg(msg).loc(prefix).push();
129 }
130 }
131 } else {
132 #[allow(clippy::collapsible_else_if)]
133 if item.injectable() {
134 if let Some(other) = self.database[item as usize].get(key.as_str()) {
135 let msg =
136 format!("must have a prefix such as `REPLACE:` to replace {item}");
137 err(ErrorKey::Prefixes)
138 .msg(msg)
139 .loc(key)
140 .loc_msg(&other.key, "original here")
141 .push();
142 } else {
143 self.add_inner2(item, key, block, kind);
144 }
145 } else {
146 self.add_inner2(item, key, block, kind);
147 }
148 }
149 } else {
150 if let Some(other) = self.database[item as usize].get(key.as_str())
151 && other.key.loc.kind >= key.loc.kind
152 {
153 if other.block.equivalent(&block) {
154 if exact_dup_ok {
155 exact_dup_advice(&key, &other.key, &item.to_string());
156 } else {
157 exact_dup_error(&key, &other.key, &item.to_string());
158 }
159 } else {
160 dup_error(&key, &other.key, &item.to_string());
161 }
162 }
163 self.add_inner2(item, key, block, kind);
164 }
165 }
166
167 fn add_inner2(&mut self, item: Item, key: Token, block: Block, kind: Box<dyn DbKind>) {
169 self.items_lc[item as usize].insert(Lowercase::new(key.as_str()), key.as_str());
170 self.database[item as usize].insert(key.as_str(), DbEntry { key, block, kind });
171 }
172
173 #[cfg(feature = "hoi4")]
174 pub fn set_flag_validator(&mut self, item: Item, f: FlagValidator) {
175 self.flags[item as usize].1 = Some(f);
176 }
177
178 pub fn add_flag(&mut self, item: Item, key: Token) {
179 self.items_lc[item as usize].insert(Lowercase::new(key.as_str()), key.as_str());
180 self.flags[item as usize].0.insert(key);
181 }
182
183 #[cfg(feature = "hoi4")]
184 pub fn add_anonymous(&mut self, ident: Token, block: Block, kind: Box<dyn DbKind>) {
185 self.anonymous.push(DbEntry { key: ident, block, kind });
186 }
187
188 pub fn add_subitems(&mut self) {
189 for itype in 0..Item::COUNT {
190 let queue = take(&mut self.database[itype]);
191 for entry in queue.values().sorted_by_key(|e| e.key.loc) {
192 entry.kind.add_subitems(&entry.key, &entry.block, self);
193 }
194 if self.database[itype].is_empty() {
195 self.database[itype] = queue;
198 } else {
199 self.database[itype].extend(queue);
200 }
201 }
202 }
203
204 pub fn scan_variables(&self, registry: &mut Variables) {
205 for map in &self.database {
206 for entry in map.values() {
207 registry.scan(&entry.block);
208 }
209 }
210 }
211
212 pub fn validate(&self, data: &Everything) {
213 self.database.par_iter().for_each(|map| {
214 map.par_iter().for_each(|(_, entry)| {
215 entry.kind.validate(&entry.key, &entry.block, data);
216 });
217 });
218 self.flags.par_iter().for_each(|(map, fv)| {
219 if let Some(fv) = fv {
220 map.par_iter().for_each(|flag| {
221 fv(flag, data);
222 });
223 }
224 });
225 self.anonymous.par_iter().for_each(|entry| {
226 entry.kind.validate(&entry.key, &entry.block, data);
227 });
228 }
229
230 pub fn exists(&self, item: Item, key: &str) -> bool {
231 self.database[item as usize].contains_key(key) || self.flags[item as usize].0.contains(key)
232 }
233
234 pub fn exists_lc(&self, item: Item, key: &Lowercase) -> bool {
235 self.items_lc[item as usize].contains_key(key)
236 }
237
238 #[allow(dead_code)]
239 pub fn get_item<T: DbKind + Any>(&self, item: Item, key: &str) -> Option<(&Token, &Block, &T)> {
240 if let Some(entry) = self.database[item as usize].get(key)
241 && let Some(kind) = (*entry.kind).as_any().downcast_ref::<T>()
242 {
243 return Some((&entry.key, &entry.block, kind));
244 }
245 None
246 }
247
248 pub fn get_key_block(&self, item: Item, key: &str) -> Option<(&Token, &Block)> {
249 self.database[item as usize].get(key).map(|entry| (&entry.key, &entry.block))
250 }
251
252 #[allow(dead_code)]
253 pub fn has_property(&self, item: Item, key: &str, property: &str, data: &Everything) -> bool {
254 if let Some(entry) = self.database[item as usize].get(key) {
255 entry.kind.has_property(&entry.key, &entry.block, property, data)
256 } else {
257 false
258 }
259 }
260
261 #[cfg(feature = "ck3")] pub fn lc_has_property(
263 &self,
264 item: Item,
265 key: &Lowercase,
266 property: &str,
267 data: &Everything,
268 ) -> bool {
269 let real_key = self.items_lc[item as usize].get(key);
270 if let Some(entry) = real_key.and_then(|key| self.database[item as usize].get(key)) {
271 entry.kind.has_property(&entry.key, &entry.block, property, data)
272 } else {
273 false
274 }
275 }
276
277 #[cfg(feature = "ck3")] pub fn set_property(&mut self, item: Item, key: &str, property: &str) {
279 if let Some(entry) = self.database[item as usize].get_mut(key) {
280 entry.kind.set_property(&entry.key, &entry.block, property);
281 }
282 }
283
284 #[allow(dead_code)]
285 pub fn validate_call(
286 &self,
287 item: Item,
288 key: &Token,
289 block: &Block,
290 data: &Everything,
291 sc: &mut ScopeContext,
292 ) {
293 if let Some(entry) = self.database[item as usize].get(key.as_str()) {
294 entry.kind.validate_call(&entry.key, &entry.block, key, block, data, sc);
295 }
296 }
297
298 #[allow(dead_code)]
299 pub fn validate_use(&self, item: Item, key: &Token, block: &Block, data: &Everything) {
300 if let Some(entry) = self.database[item as usize].get(key.as_str()) {
301 entry.kind.validate_use(&entry.key, &entry.block, data, key, block);
302 }
303 }
304
305 #[allow(dead_code)]
306 pub fn validate_property_use(
307 &self,
308 item: Item,
309 key: &Token,
310 data: &Everything,
311 property: &Token,
312 caller: &str,
313 ) {
314 if let Some(entry) = self.database[item as usize].get(key.as_str()) {
315 entry.kind.validate_property_use(&entry.key, &entry.block, property, caller, data);
316 }
317 }
318
319 #[allow(dead_code)]
320 pub fn iter_key_block(&self, itype: Item) -> impl Iterator<Item = (&Token, &Block)> {
321 self.database[itype as usize].values().map(|entry| (&entry.key, &entry.block))
322 }
323
324 pub fn iter_keys(&self, itype: Item) -> impl Iterator<Item = &Token> {
325 self.database[itype as usize]
326 .values()
327 .map(|entry| &entry.key)
328 .chain(self.flags[itype as usize].0.iter())
329 }
330}
331
332#[derive(Debug)]
333pub struct DbEntry {
334 key: Token,
335 block: Block,
336 kind: Box<dyn DbKind>,
337}
338
339impl DbEntry {
340 #[cfg(any(feature = "vic3", feature = "eu5"))]
341 fn inject(&mut self, mut block: Block, kind: Box<dyn DbKind>) {
342 self.block.append(&mut block);
343 self.kind.merge_in(kind);
344 }
345}
346
347#[allow(dead_code)]
348pub trait DbKind: Debug + AsAny + Sync + Send {
349 fn add_subitems(&self, _key: &Token, _block: &Block, _db: &mut Db) {}
353
354 fn validate(&self, key: &Token, block: &Block, data: &Everything);
355 fn has_property(
356 &self,
357 _key: &Token,
358 _block: &Block,
359 _property: &str,
360 _data: &Everything,
361 ) -> bool {
362 false
363 }
364 fn validate_call(
365 &self,
366 _key: &Token,
367 _block: &Block,
368 _from: &Token,
369 _from_block: &Block,
370 _data: &Everything,
371 _sc: &mut ScopeContext,
372 ) {
373 }
374
375 fn validate_use(
376 &self,
377 _key: &Token,
378 _block: &Block,
379 _data: &Everything,
380 _call_key: &Token,
381 _call_block: &Block,
382 ) {
383 }
384
385 fn validate_property_use(
386 &self,
387 _key: &Token,
388 _block: &Block,
389 _property: &Token,
390 _caller: &str,
391 _data: &Everything,
392 ) {
393 }
394
395 fn set_property(&mut self, _key: &Token, _block: &Block, _property: &str) {}
396
397 fn merge_in(&mut self, _other: Box<dyn DbKind>) {}
398}