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