tiger_lib/data/
defines.rs1use std::path::PathBuf;
2
3use crate::block::{BV, Block};
4use crate::everything::Everything;
5use crate::fileset::{FileEntry, FileHandler};
6use crate::game::Game;
7use crate::helpers::{TigerHashMap, dup_error};
8#[cfg(feature = "ck3")]
9use crate::item::Item;
10use crate::parse::ParserMemory;
11use crate::pdxfile::PdxFile;
12#[cfg(feature = "ck3")]
13use crate::report::Severity;
14use crate::report::{ErrorKey, err};
15use crate::token::Token;
16
17#[derive(Clone, Debug, Default)]
18pub struct Defines {
19 defines: TigerHashMap<String, Define>,
20}
21
22impl Defines {
23 pub fn load_item(&mut self, group: Token, name: Token, bv: &BV) {
24 let key = format!("{}|{}", &group, &name);
25 if let Some(other) = self.defines.get(&key) {
26 if other.name.loc.kind >= name.loc.kind && !bv.equivalent(&other.bv) {
27 dup_error(&name, &other.name, "define");
28 }
29 }
30 self.defines.insert(key, Define::new(group, name, bv.clone()));
31 }
32
33 pub fn exists(&self, key: &str) -> bool {
34 self.defines.contains_key(key)
35 }
36
37 pub fn iter_keys(&self) -> impl Iterator<Item = &Token> {
39 self.defines.values().map(|item| &item.name)
40 }
41
42 pub fn validate(&self, data: &Everything) {
43 for item in self.defines.values() {
44 item.validate(data);
45 }
46 }
47
48 #[cfg(feature = "jomini")]
49 pub fn get_bv(&self, key: &str) -> Option<&BV> {
50 self.defines.get(key).map(|d| &d.bv)
51 }
52}
53
54impl FileHandler<Block> for Defines {
55 fn subpath(&self) -> PathBuf {
56 PathBuf::from("common/defines")
57 }
58
59 fn load_file(&self, entry: &FileEntry, parser: &ParserMemory) -> Option<Block> {
60 if !entry.filename().to_string_lossy().ends_with(".txt") {
61 return None;
62 }
63
64 PdxFile::read(entry, parser)
65 }
66
67 fn handle_file(&mut self, _entry: &FileEntry, mut block: Block) {
68 for (group, block) in block.drain_definitions_warn() {
70 for (name, bv) in block.iter_assignments_and_definitions_warn() {
71 self.load_item(group.clone(), name.clone(), bv);
72 }
73 }
74 }
75}
76
77#[derive(Clone, Debug)]
78pub struct Define {
79 #[allow(dead_code)] group: Token,
81 name: Token,
82 bv: BV,
83}
84
85impl Define {
86 pub fn new(group: Token, name: Token, bv: BV) -> Self {
87 Self { group, name, bv }
88 }
89
90 #[allow(clippy::unused_self)]
91 #[allow(unused_variables)] pub fn validate(&self, data: &Everything) {
93 let defines_map = match Game::game() {
94 #[cfg(feature = "ck3")]
95 Game::Ck3 => &crate::ck3::tables::defines::DEFINES_MAP,
96 #[cfg(feature = "vic3")]
97 Game::Vic3 => &crate::vic3::tables::defines::DEFINES_MAP,
98 #[cfg(feature = "imperator")]
99 Game::Imperator => &crate::imperator::tables::defines::DEFINES_MAP,
100 #[cfg(feature = "eu5")]
101 Game::Eu5 => &crate::eu5::tables::defines::DEFINES_MAP,
102 #[cfg(feature = "hoi4")]
103 Game::Hoi4 => &crate::hoi4::tables::defines::DEFINES_MAP,
104 };
105
106 let key = format!("{}|{}", &self.group, &self.name);
108 if let Some(dt) = defines_map.get(&*key) {
109 dt.validate(&self.bv, data);
110 } else {
111 let msg = format!("unknown define {key}");
112 err(ErrorKey::UnknownField).msg(msg).loc(&self.name).push();
113 }
114
115 #[cfg(feature = "ck3")]
116 if self.group.is("NGameIcons") && self.name.is("PIETY_GROUPS") {
117 if let Some(icon_path) =
118 data.get_defined_string_warn(&self.name, "NGameIcons|PIETY_LEVEL_PATH")
119 {
120 if let Some(groups) = self.bv.expect_block() {
121 for icon_group in groups.iter_values_warn() {
122 for nr in &["00", "01", "02", "03", "04", "05"] {
123 let pathname = format!("{icon_path}/icon_piety_{icon_group}_{nr}.dds");
124 data.verify_exists_implied_max_sev(
125 Item::File,
126 &pathname,
127 icon_group,
128 Severity::Warning,
129 );
130 }
131 }
132 }
133 }
134 }
135 }
136}