tiger_lib/ck3/data/
interaction_cats.rs

1use std::path::PathBuf;
2
3use crate::block::Block;
4use crate::everything::Everything;
5use crate::fileset::{FileEntry, FileHandler};
6use crate::helpers::{TigerHashMap, dup_error};
7use crate::item::Item;
8use crate::parse::ParserMemory;
9use crate::pdxfile::PdxFile;
10use crate::report::{ErrorKey, err};
11use crate::token::Token;
12use crate::validate::validate_color;
13use crate::validator::Validator;
14use crate::variables::Variables;
15
16#[derive(Clone, Debug, Default)]
17pub struct CharacterInteractionCategories {
18    categories: TigerHashMap<&'static str, Category>,
19}
20
21impl CharacterInteractionCategories {
22    pub fn load_item(&mut self, key: Token, block: Block) {
23        if let Some(other) = self.categories.get(key.as_str()) {
24            if other.key.loc.kind == key.loc.kind {
25                dup_error(&key, &other.key, "interaction category");
26            }
27        }
28        self.categories.insert(key.as_str(), Category::new(key, block));
29    }
30
31    pub fn scan_variables(&self, registry: &mut Variables) {
32        for item in self.categories.values() {
33            registry.scan(&item.block);
34        }
35    }
36
37    pub fn exists(&self, key: &str) -> bool {
38        self.categories.contains_key(key)
39    }
40
41    pub fn iter_keys(&self) -> impl Iterator<Item = &Token> {
42        self.categories.values().map(|item| &item.key)
43    }
44
45    pub fn validate(&self, data: &Everything) {
46        for item in self.categories.values() {
47            item.validate(data);
48        }
49    }
50}
51
52impl FileHandler<Block> for CharacterInteractionCategories {
53    fn subpath(&self) -> PathBuf {
54        PathBuf::from("common/character_interaction_categories")
55    }
56
57    fn load_file(&self, entry: &FileEntry, parser: &ParserMemory) -> Option<Block> {
58        if !entry.filename().to_string_lossy().ends_with(".txt") {
59            return None;
60        }
61
62        PdxFile::read(entry, parser)
63    }
64
65    fn handle_file(&mut self, _entry: &FileEntry, mut block: Block) {
66        for (key, block) in block.drain_definitions_warn() {
67            self.load_item(key, block);
68        }
69    }
70
71    fn finalize(&mut self) {
72        let mut taken = vec![None; self.categories.len()];
73        for item in self.categories.values() {
74            if let Some(index) = item.index {
75                if index >= 0 {
76                    let index = usize::try_from(index).expect("internal error");
77                    if index < taken.len() {
78                        if let Some(other) = taken[index] {
79                            let msg = format!("index duplicates the index of {other}");
80                            err(ErrorKey::DuplicateItem).msg(msg).loc(&item.key).push();
81                        } else {
82                            taken[index] = Some(&item.key);
83                        }
84                        continue;
85                    }
86                }
87                let msg = "index needs to be from 0 to the number of categories";
88                err(ErrorKey::Range).msg(msg).loc(&item.key).push();
89            }
90            // if no index, the item will warn about that in validate
91        }
92    }
93}
94
95#[derive(Clone, Debug)]
96pub struct Category {
97    key: Token,
98    block: Block,
99    index: Option<i64>,
100}
101
102impl Category {
103    pub fn new(key: Token, block: Block) -> Self {
104        let index = block.get_field_integer("index");
105        Category { key, block, index }
106    }
107
108    pub fn validate(&self, data: &Everything) {
109        let mut vd = Validator::new(&self.block, data);
110        vd.req_field("index");
111        vd.req_field("desc");
112        vd.field_integer("index");
113        vd.field_item("desc", Item::Localization);
114        vd.field_bool("default");
115        vd.field_bool("favorite_interactions");
116        vd.field_validated_block("color", validate_color);
117    }
118}