1use std::fmt::{Display, Formatter};
4use std::sync::OnceLock;
5
6use anyhow::{anyhow, Result};
7use bitflags::bitflags;
8
9use crate::helpers::display_choices;
10
11static GAME: OnceLock<Game> = OnceLock::new();
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18pub enum Game {
19    #[cfg(feature = "ck3")]
20    Ck3,
21    #[cfg(feature = "vic3")]
22    Vic3,
23    #[cfg(feature = "imperator")]
24    Imperator,
25}
26
27impl Game {
28    pub fn set(game: Game) -> Result<()> {
31        GAME.set(game).map_err(|_| anyhow!("tried to set game type twice"))?;
32        Ok(())
33    }
34
35    #[allow(clippy::self_named_constructors)] #[allow(unreachable_code)]
41    pub fn game() -> Game {
42        #[cfg(all(feature = "ck3", not(feature = "vic3"), not(feature = "imperator")))]
43        return Game::Ck3;
44        #[cfg(all(feature = "vic3", not(feature = "ck3"), not(feature = "imperator")))]
45        return Game::Vic3;
46        #[cfg(all(feature = "imperator", not(feature = "ck3"), not(feature = "vic3")))]
47        return Game::Imperator;
48        *GAME.get().expect("internal error: don't know which game we are validating")
49    }
50
51    pub(crate) fn is_ck3() -> bool {
53        #[cfg(not(feature = "ck3"))]
54        return false;
55        #[cfg(all(feature = "ck3", not(feature = "vic3"), not(feature = "imperator")))]
56        return true;
57        #[cfg(all(feature = "ck3", any(feature = "vic3", feature = "imperator")))]
58        return GAME.get() == Some(&Game::Ck3);
59    }
60
61    pub(crate) fn is_vic3() -> bool {
63        #[cfg(not(feature = "vic3"))]
64        return false;
65        #[cfg(all(feature = "vic3", not(feature = "ck3"), not(feature = "imperator")))]
66        return true;
67        #[cfg(all(feature = "vic3", any(feature = "ck3", feature = "imperator")))]
68        return GAME.get() == Some(&Game::Vic3);
69    }
70
71    pub(crate) fn is_imperator() -> bool {
73        #[cfg(not(feature = "imperator"))]
74        return false;
75        #[cfg(all(feature = "imperator", not(feature = "ck3"), not(feature = "vic3")))]
76        return true;
77        #[cfg(all(feature = "imperator", any(feature = "ck3", feature = "vic3")))]
78        return GAME.get() == Some(&Game::Imperator);
79    }
80}
81
82bitflags! {
83    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
88    pub struct GameFlags: u8 {
89        const Ck3 = 0x01;
90        const Vic3 = 0x02;
91        const Imperator = 0x04;
92    }
93}
94
95impl GameFlags {
96    pub fn game() -> Self {
99        match Game::game() {
101            #[cfg(feature = "ck3")]
102            Game::Ck3 => GameFlags::Ck3,
103            #[cfg(feature = "vic3")]
104            Game::Vic3 => GameFlags::Vic3,
105            #[cfg(feature = "imperator")]
106            Game::Imperator => GameFlags::Imperator,
107        }
108    }
109}
110
111impl Display for GameFlags {
112    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
113        let mut vec = Vec::new();
114        if self.contains(Self::Ck3) {
115            vec.push("Crusader Kings 3");
116        }
117        if self.contains(Self::Vic3) {
118            vec.push("Victoria 3");
119        }
120        if self.contains(Self::Imperator) {
121            vec.push("Imperator: Rome");
122        }
123        display_choices(f, &vec, "and")
124    }
125}
126
127impl From<Game> for GameFlags {
128    fn from(game: Game) -> Self {
130        match game {
131            #[cfg(feature = "ck3")]
132            Game::Ck3 => GameFlags::Ck3,
133            #[cfg(feature = "vic3")]
134            Game::Vic3 => GameFlags::Vic3,
135            #[cfg(feature = "imperator")]
136            Game::Imperator => GameFlags::Imperator,
137        }
138    }
139}