Skip to main content

tiger_tables/
datatype.rs

1//! Validator for the `[ ... ]` code blocks in localization and gui files.
2//! The main entry points are the [`validate_datatypes`] function and the [`Datatype`] enum.
3
4use std::fmt::{Display, Formatter};
5use std::str::FromStr;
6
7use phf::phf_map;
8use strum_macros::{Display, EnumString};
9
10use crate::game::Game;
11#[cfg(feature = "jomini")]
12use crate::item::Item;
13
14// Load the game-specific datatype definitions
15#[cfg(feature = "ck3")]
16include!("ck3/include/datatypes.rs");
17#[cfg(feature = "vic3")]
18include!("vic3/include/datatypes.rs");
19#[cfg(feature = "imperator")]
20include!("imperator/include/datatypes.rs");
21#[cfg(feature = "eu5")]
22include!("eu5/include/datatypes.rs");
23#[cfg(feature = "hoi4")]
24include!("hoi4/include/datatypes.rs");
25
26/// All the object types used in `[...]` code in localization and gui files.
27///
28/// The names exactly match the ones in the `data_types` logs from the games,
29/// which is why some of them are lowercase.
30/// Most of the variants are generated directly from those logs.
31///
32/// The enum is divided into the "generic" datatypes, which are valid for all games and which can
33/// be referenced directly in code, and the per-game lists of datatypes which are in game-specific
34/// wrappers. With a few exceptions, the per-game datatypes are only referenced in the per-game tables
35/// of datafunctions and promotes.
36///
37/// The game-specific datatypes are wrapped because otherwise they would still have name
38/// collisions. This is because the list of generic datatypes is only a small selection; there are
39/// many more datatypes that are in effect generic but separating them out would be pointless work.
40/// (Separating them out would be made harder because the lists of variants are generated from the docs).
41#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
42#[allow(non_camel_case_types)]
43pub enum Datatype {
44    // Synthetic datatypes for our typechecking
45    Unknown,
46    AnyScope,
47
48    // The generic datatypes
49    CFixedPoint,
50    CString,
51    CUTF8String,
52    CVector2f,
53    CVector2i,
54    CVector3f,
55    CVector3i,
56    CVector4f,
57    CVector4i,
58    Date,
59    Scope,
60    TopScope,
61    bool,
62    double,
63    float,
64    int16,
65    int32,
66    int64,
67    int8,
68    uint16,
69    uint32,
70    uint64,
71    uint8,
72    void,
73
74    // Wrappers for the per-game datatypes
75    #[cfg(feature = "ck3")]
76    Ck3(Ck3Datatype),
77    #[cfg(feature = "vic3")]
78    Vic3(Vic3Datatype),
79    #[cfg(feature = "imperator")]
80    Imperator(ImperatorDatatype),
81    #[cfg(feature = "eu5")]
82    Eu5(Eu5Datatype),
83    #[cfg(feature = "hoi4")]
84    Hoi4(Hoi4Datatype),
85}
86
87static STR_DATATYPE_MAP: phf::Map<&'static str, Datatype> = phf_map! {
88    "Unknown" => Datatype::Unknown,
89    "AnyScope" => Datatype::AnyScope,
90    "CFixedPoint" => Datatype::CFixedPoint,
91    "CString" => Datatype::CString,
92    "CUTF8String" => Datatype::CUTF8String,
93    "CVector2f" => Datatype::CVector2f,
94    "CVector2i" => Datatype::CVector2i,
95    "CVector3f" => Datatype::CVector3f,
96    "CVector3i" => Datatype::CVector3i,
97    "CVector4f" => Datatype::CVector4f,
98    "CVector4i" => Datatype::CVector4i,
99    "Date" => Datatype::Date,
100    "Scope" => Datatype::Scope,
101    "TopScope" => Datatype::TopScope,
102    "bool" => Datatype::bool,
103    "double" => Datatype::double,
104    "float" => Datatype::float,
105    "int16" => Datatype::int16,
106    "int32" => Datatype::int32,
107    "int64" => Datatype::int64,
108    "int8" => Datatype::int8,
109    "uint16" => Datatype::uint16,
110    "uint32" => Datatype::uint32,
111    "uint64" => Datatype::uint64,
112    "uint8" => Datatype::uint8,
113    "void" => Datatype::void,
114};
115
116impl FromStr for Datatype {
117    type Err = strum::ParseError;
118    /// Read a Datatype from a string, without requiring the string to use the game-specific wrappers.
119    fn from_str(s: &str) -> Result<Self, strum::ParseError> {
120        STR_DATATYPE_MAP.get(s).copied().ok_or(strum::ParseError::VariantNotFound).or_else(|_| {
121            match Game::game() {
122                #[cfg(feature = "ck3")]
123                Game::Ck3 => Ck3Datatype::from_str(s).map(Datatype::Ck3),
124                #[cfg(feature = "vic3")]
125                Game::Vic3 => Vic3Datatype::from_str(s).map(Datatype::Vic3),
126                #[cfg(feature = "imperator")]
127                Game::Imperator => ImperatorDatatype::from_str(s).map(Datatype::Imperator),
128                #[cfg(feature = "eu5")]
129                Game::Eu5 => Eu5Datatype::from_str(s).map(Datatype::Eu5),
130                #[cfg(feature = "hoi4")]
131                Game::Hoi4 => Hoi4Datatype::from_str(s).map(Datatype::Hoi4),
132            }
133        })
134    }
135}
136
137impl Display for Datatype {
138    /// Convert a `Datatype` to string format, while leaving out the game-specific wrappers.
139    fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
140        // Have to do the generic variants by hand, so that the per-game variants can be done with the macro.
141        match *self {
142            Datatype::Unknown => write!(f, "Unknown"),
143            Datatype::AnyScope => write!(f, "AnyScope"),
144            Datatype::CFixedPoint => write!(f, "CFixedPoint"),
145            Datatype::CString => write!(f, "CString"),
146            Datatype::CUTF8String => write!(f, "CUTF8String"),
147            Datatype::CVector2f => write!(f, "CVector2f"),
148            Datatype::CVector2i => write!(f, "CVector2i"),
149            Datatype::CVector3f => write!(f, "CVector3f"),
150            Datatype::CVector3i => write!(f, "CVector3i"),
151            Datatype::CVector4f => write!(f, "CVector4f"),
152            Datatype::CVector4i => write!(f, "CVector4i"),
153            Datatype::Date => write!(f, "Date"),
154            Datatype::Scope => write!(f, "Scope"),
155            Datatype::TopScope => write!(f, "TopScope"),
156            Datatype::bool => write!(f, "bool"),
157            Datatype::double => write!(f, "double"),
158            Datatype::float => write!(f, "float"),
159            Datatype::int16 => write!(f, "int16"),
160            Datatype::int32 => write!(f, "int32"),
161            Datatype::int64 => write!(f, "int64"),
162            Datatype::int8 => write!(f, "int8"),
163            Datatype::uint16 => write!(f, "uint16"),
164            Datatype::uint32 => write!(f, "uint32"),
165            Datatype::uint64 => write!(f, "uint64"),
166            Datatype::uint8 => write!(f, "uint8"),
167            Datatype::void => write!(f, "void"),
168            #[cfg(feature = "ck3")]
169            Datatype::Ck3(dt) => dt.fmt(f),
170            #[cfg(feature = "vic3")]
171            Datatype::Vic3(dt) => dt.fmt(f),
172            #[cfg(feature = "imperator")]
173            Datatype::Imperator(dt) => dt.fmt(f),
174            #[cfg(feature = "eu5")]
175            Datatype::Eu5(dt) => dt.fmt(f),
176            #[cfg(feature = "hoi4")]
177            Datatype::Hoi4(dt) => dt.fmt(f),
178        }
179    }
180}
181
182/// `Arg` represents what kind of argument is expected by a promote or function.
183#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
184pub enum Arg {
185    /// The argument is expected to be a code chain whose final function returns this [`Datatype`],
186    /// or a literal that is encoded to be of the expected type.
187    #[cfg(feature = "jomini")]
188    DType(Datatype),
189    /// The argument is expected to be a literal containing a key to this [`Item`] type, or a code
190    /// chain that returns a `CString` (in which case the `Item` lookup is not checked).
191    #[cfg(feature = "jomini")]
192    IType(Item),
193    /// The argument is considered to be one of the literals in this array, or a code chain that
194    /// returns a `CString`.
195    #[allow(dead_code)]
196    Choice(&'static [&'static str]),
197}
198
199/// [`Args`] is the list of arguments expected by a given promote or function.
200/// The special value `Args::Unknown` means that all arguments are accepted.
201#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
202pub enum Args {
203    Unknown,
204    Args(&'static [Arg]),
205}
206
207#[cfg(feature = "ck3")]
208pub use crate::ck3::datafunctions::*;
209#[cfg(feature = "eu5")]
210pub use crate::eu5::datafunctions::*;
211#[cfg(feature = "hoi4")]
212pub use crate::hoi4::datafunctions::*;
213#[cfg(feature = "imperator")]
214pub use crate::imperator::datafunctions::*;
215#[cfg(feature = "vic3")]
216pub use crate::vic3::datafunctions::*;