Skip to main content

ed_journals/modules/exploration/models/
codex_entry.rs

1use crate::exploration::models::codex_anomaly_entry::CodexAnomalyEntry;
2use crate::exploration::models::codex_geological_entry::CodexGeologicalEntry;
3use crate::exploration::models::codex_guardian_entry::CodexGuardianEntry;
4use crate::exploration::models::codex_organic_structure_entry::CodexOrganicStructureEntry;
5use crate::exploration::models::codex_planet_entry::CodexPlanetEntry;
6use crate::exploration::models::codex_thargoid_entry::CodexThargoidEntry;
7use crate::from_str_deserialize_impl;
8use crate::modules::exobiology::{Genus, Species, Variant};
9use crate::modules::exploration::CodexStarClassEntry;
10use serde::Serialize;
11use std::fmt::{Display, Formatter};
12use std::str::FromStr;
13use thiserror::Error;
14
15/// Model for any kind of codex entry.
16#[derive(Debug, Serialize, Clone, PartialEq, Eq, Hash)]
17#[cfg_attr(not(feature = "allow-unknown"), non_exhaustive)]
18pub enum CodexEntry {
19    Planet(CodexPlanetEntry),
20    Geological(CodexGeologicalEntry),
21    Anomalous(CodexAnomalyEntry),
22    Thargoid(CodexThargoidEntry),
23    Guardian(CodexGuardianEntry),
24    Genus(Genus),
25    Species(Species),
26    Variant(Variant),
27    OrganicStructure(CodexOrganicStructureEntry),
28    StarClass(CodexStarClassEntry),
29
30    /// Unknown codex entry.
31    #[cfg(feature = "allow-unknown")]
32    #[cfg_attr(docsrs, doc(cfg(feature = "allow-unknown")))]
33    Unknown(String),
34}
35
36impl CodexEntry {
37    /// Whether the current variant is unknown.
38    #[cfg(feature = "allow-unknown")]
39    #[cfg_attr(docsrs, doc(cfg(feature = "allow-unknown")))]
40    pub fn is_unknown(&self) -> bool {
41        matches!(self, CodexEntry::Unknown(_))
42    }
43}
44
45#[derive(Debug, Error)]
46pub enum CodexEntryError {
47    #[error("Unknown codex entry: '{0}'")]
48    UnknownEntry(String),
49}
50
51impl FromStr for CodexEntry {
52    type Err = CodexEntryError;
53
54    #[cfg(not(feature = "allow-unknown"))]
55    fn from_str(s: &str) -> Result<Self, Self::Err> {
56        if let Ok(entry) = CodexPlanetEntry::from_str(s) {
57            return Ok(CodexEntry::Planet(entry));
58        }
59
60        if let Ok(entry) = CodexGeologicalEntry::from_str(s) {
61            return Ok(CodexEntry::Geological(entry));
62        }
63
64        if let Ok(entry) = CodexAnomalyEntry::from_str(s) {
65            return Ok(CodexEntry::Anomalous(entry));
66        }
67
68        if let Ok(entry) = CodexThargoidEntry::from_str(s) {
69            return Ok(CodexEntry::Thargoid(entry));
70        }
71
72        if let Ok(entry) = CodexGuardianEntry::from_str(s) {
73            return Ok(CodexEntry::Guardian(entry));
74        }
75
76        if let Ok(entry) = Genus::from_str(s) {
77            return Ok(CodexEntry::Genus(entry));
78        }
79
80        if let Ok(entry) = Species::from_str(s) {
81            return Ok(CodexEntry::Species(entry));
82        }
83
84        if let Ok(entry) = Variant::from_str(s) {
85            return Ok(CodexEntry::Variant(entry));
86        }
87
88        if let Ok(entry) = CodexOrganicStructureEntry::from_str(s) {
89            return Ok(CodexEntry::OrganicStructure(entry));
90        }
91
92        if let Ok(entry) = CodexStarClassEntry::from_str(s) {
93            return Ok(CodexEntry::StarClass(entry));
94        }
95
96        Err(CodexEntryError::UnknownEntry(s.to_string()))
97    }
98
99    #[cfg(feature = "allow-unknown")]
100    fn from_str(s: &str) -> Result<Self, Self::Err> {
101        if let Ok(entry) = CodexPlanetEntry::from_str(s) {
102            if !entry.is_unknown() {
103                return Ok(CodexEntry::Planet(entry));
104            }
105        }
106
107        if let Ok(entry) = CodexGeologicalEntry::from_str(s) {
108            if !entry.is_unknown() {
109                return Ok(CodexEntry::Geological(entry));
110            }
111        }
112
113        if let Ok(entry) = CodexAnomalyEntry::from_str(s) {
114            if !entry.is_unknown() {
115                return Ok(CodexEntry::Anomalous(entry));
116            }
117        }
118
119        if let Ok(entry) = CodexThargoidEntry::from_str(s) {
120            if !entry.is_unknown() {
121                return Ok(CodexEntry::Thargoid(entry));
122            }
123        }
124
125        if let Ok(entry) = CodexGuardianEntry::from_str(s) {
126            if !entry.is_unknown() {
127                return Ok(CodexEntry::Guardian(entry));
128            }
129        }
130
131        if let Ok(entry) = Genus::from_str(s) {
132            if !entry.is_unknown() {
133                return Ok(CodexEntry::Genus(entry));
134            }
135        }
136
137        if let Ok(entry) = Species::from_str(s) {
138            if !entry.is_unknown() {
139                return Ok(CodexEntry::Species(entry));
140            }
141        }
142
143        if let Ok(entry) = Variant::from_str(s) {
144            if !entry.is_unknown() {
145                return Ok(CodexEntry::Variant(entry));
146            }
147        }
148
149        if let Ok(entry) = CodexOrganicStructureEntry::from_str(s) {
150            if !entry.is_unknown() {
151                return Ok(CodexEntry::OrganicStructure(entry));
152            }
153        }
154
155        if let Ok(entry) = CodexStarClassEntry::from_str(s) {
156            if !entry.is_unknown() {
157                return Ok(CodexEntry::StarClass(entry));
158            }
159        }
160
161        Ok(CodexEntry::Unknown(s.to_string()))
162    }
163}
164
165from_str_deserialize_impl!(CodexEntry);
166
167impl Display for CodexEntry {
168    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
169        match self {
170            // CodexEntry::NeutronStars => write!(f, "Neutron Star"),
171            // CodexEntry::BlackHoles => write!(f, "Black Hole"),
172            CodexEntry::Geological(geological) => write!(f, "{geological}"),
173            CodexEntry::Anomalous(anomalous) => write!(f, "{anomalous}"),
174            CodexEntry::Thargoid(targoid) => write!(f, "{targoid}"),
175            CodexEntry::Planet(planet_class) => write!(f, "{planet_class}"),
176            CodexEntry::Genus(genus) => write!(f, "{genus}"),
177            CodexEntry::Species(species) => write!(f, "{species}"),
178            CodexEntry::Variant(variant) => write!(f, "{variant}"),
179            CodexEntry::OrganicStructure(organic_structure) => write!(f, "{organic_structure}"),
180            CodexEntry::StarClass(star_class) => write!(f, "{star_class}"),
181            CodexEntry::Guardian(guardian) => write!(f, "{guardian}"),
182
183            #[cfg(feature = "allow-unknown")]
184            CodexEntry::Unknown(unknown) => write!(f, "Unknown: '{unknown}'"),
185        }
186    }
187}
188
189#[cfg(test)]
190mod tests {
191    use crate::exploration::CodexEntry;
192    use serde_json::Value;
193
194    #[test]
195    fn codex_entries_are_parsed_correctly() {
196        let content = include_str!("zz_codex_entries");
197        let lines = content.lines();
198
199        for line in lines {
200            if line.starts_with('#') {
201                continue;
202            }
203
204            let result = serde_json::from_value::<CodexEntry>(Value::String(line.to_string()));
205
206            if result.is_err() {
207                dbg!(&line, &result);
208            }
209
210            assert!(result.is_ok());
211        }
212    }
213
214    #[cfg(not(feature = "allow-unknown"))]
215    #[test]
216    fn unknown_value_returns_error() {
217        let result = serde_json::from_value::<CodexEntry>(Value::String("yeah no".to_string()));
218        assert!(result.is_err());
219    }
220
221    #[cfg(feature = "allow-unknown")]
222    #[test]
223    fn unknown_value_returns_unknown_variant() {
224        let result = serde_json::from_value::<CodexEntry>(Value::String("yeah no".to_string()));
225        assert_eq!(result.unwrap(), CodexEntry::Unknown("yeah no".to_string()));
226    }
227}