1use serde::{Deserialize, Serialize};
2use std::fmt;
3use std::str::FromStr;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
7#[cfg_attr(
8 feature = "archive",
9 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
10)]
11#[non_exhaustive]
12pub enum Biome {
13 Lush,
14 Toxic,
15 Scorched,
16 Radioactive,
17 Frozen,
18 Barren,
19 Dead,
20 Weird,
21 Red,
22 Green,
23 Blue,
24 Swamp,
25 Lava,
26 Waterworld,
27 GasGiant,
28}
29
30impl fmt::Display for Biome {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 let s = match self {
33 Self::Lush => "Lush",
34 Self::Toxic => "Toxic",
35 Self::Scorched => "Scorched",
36 Self::Radioactive => "Radioactive",
37 Self::Frozen => "Frozen",
38 Self::Barren => "Barren",
39 Self::Dead => "Dead",
40 Self::Weird => "Weird",
41 Self::Red => "Red",
42 Self::Green => "Green",
43 Self::Blue => "Blue",
44 Self::Swamp => "Swamp",
45 Self::Lava => "Lava",
46 Self::Waterworld => "Waterworld",
47 Self::GasGiant => "GasGiant",
48 };
49 write!(f, "{s}")
50 }
51}
52
53#[derive(Debug, Clone, PartialEq, Eq)]
55pub struct BiomeParseError(pub String);
56
57impl fmt::Display for BiomeParseError {
58 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 write!(f, "unknown biome: {}", self.0)
60 }
61}
62
63impl std::error::Error for BiomeParseError {}
64
65impl FromStr for Biome {
66 type Err = BiomeParseError;
67
68 fn from_str(s: &str) -> Result<Self, Self::Err> {
69 match s.to_lowercase().as_str() {
70 "lush" => Ok(Self::Lush),
71 "toxic" => Ok(Self::Toxic),
72 "scorched" => Ok(Self::Scorched),
73 "radioactive" => Ok(Self::Radioactive),
74 "frozen" => Ok(Self::Frozen),
75 "barren" => Ok(Self::Barren),
76 "dead" => Ok(Self::Dead),
77 "weird" => Ok(Self::Weird),
78 "red" => Ok(Self::Red),
79 "green" => Ok(Self::Green),
80 "blue" => Ok(Self::Blue),
81 "swamp" => Ok(Self::Swamp),
82 "lava" => Ok(Self::Lava),
83 "waterworld" => Ok(Self::Waterworld),
84 "gasgiant" | "gas_giant" => Ok(Self::GasGiant),
85 _ => Err(BiomeParseError(s.to_string())),
86 }
87 }
88}
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
92#[cfg_attr(
93 feature = "archive",
94 derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
95)]
96#[non_exhaustive]
97pub enum BiomeSubType {
98 LushRoomTemp,
99 LushHumid,
100 LushInactive,
101 ToxicTentacles,
102 ToxicFungus,
103 ToxicEggs,
104 ScorchedSinged,
105 ScorchedCharred,
106 ScorchedBlasted,
107 RadioactiveFungal,
108 RadioactiveContaminated,
109 RadioactiveIrradiated,
110 FrozenIce,
111 FrozenSnow,
112 FrozenGlacial,
113 BarrenDusty,
114 BarrenRocky,
115 BarrenMountainous,
116 DeadEmpty,
117 DeadCorroded,
118 DeadVoid,
119 WeirdHexagonal,
120 WeirdCabled,
121 WeirdBubbling,
122 WeirdFractured,
123 WeirdShattered,
124 WeirdContorted,
125 WeirdWireCell,
126 SwampMurky,
127 LavaVolcanic,
128 WaterworldOcean,
129}
130
131impl fmt::Display for BiomeSubType {
132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133 write!(f, "{self:?}")
134 }
135}
136
137impl FromStr for BiomeSubType {
138 type Err = BiomeParseError;
139
140 fn from_str(s: &str) -> Result<Self, Self::Err> {
141 match s.to_lowercase().as_str() {
142 "lushroomtemp" => Ok(Self::LushRoomTemp),
143 "lushhumid" => Ok(Self::LushHumid),
144 "lushinactive" => Ok(Self::LushInactive),
145 "toxictentacles" => Ok(Self::ToxicTentacles),
146 "toxicfungus" => Ok(Self::ToxicFungus),
147 "toxiceggs" => Ok(Self::ToxicEggs),
148 "scorchedsinged" => Ok(Self::ScorchedSinged),
149 "scorchedcharred" => Ok(Self::ScorchedCharred),
150 "scorchedblasted" => Ok(Self::ScorchedBlasted),
151 "radioactivefungal" => Ok(Self::RadioactiveFungal),
152 "radioactivecontaminated" => Ok(Self::RadioactiveContaminated),
153 "radioactiveirradiated" => Ok(Self::RadioactiveIrradiated),
154 "frozenice" => Ok(Self::FrozenIce),
155 "frozensnow" => Ok(Self::FrozenSnow),
156 "frozenglacial" => Ok(Self::FrozenGlacial),
157 "barrendusty" => Ok(Self::BarrenDusty),
158 "barrenrocky" => Ok(Self::BarrenRocky),
159 "barrenmountainous" => Ok(Self::BarrenMountainous),
160 "deadempty" => Ok(Self::DeadEmpty),
161 "deadcorroded" => Ok(Self::DeadCorroded),
162 "deadvoid" => Ok(Self::DeadVoid),
163 "weirdhexagonal" => Ok(Self::WeirdHexagonal),
164 "weirdcabled" => Ok(Self::WeirdCabled),
165 "weirdbubbling" => Ok(Self::WeirdBubbling),
166 "weirdfractured" => Ok(Self::WeirdFractured),
167 "weirdshattered" => Ok(Self::WeirdShattered),
168 "weirdcontorted" => Ok(Self::WeirdContorted),
169 "weirdwirecell" => Ok(Self::WeirdWireCell),
170 "swampmurky" => Ok(Self::SwampMurky),
171 "lavavolcanic" => Ok(Self::LavaVolcanic),
172 "waterworldocean" => Ok(Self::WaterworldOcean),
173 _ => Err(BiomeParseError(s.to_string())),
174 }
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181
182 const BIOME_SUB_VARIANTS: [BiomeSubType; 31] = [
183 BiomeSubType::LushRoomTemp,
184 BiomeSubType::LushHumid,
185 BiomeSubType::LushInactive,
186 BiomeSubType::ToxicTentacles,
187 BiomeSubType::ToxicFungus,
188 BiomeSubType::ToxicEggs,
189 BiomeSubType::ScorchedSinged,
190 BiomeSubType::ScorchedCharred,
191 BiomeSubType::ScorchedBlasted,
192 BiomeSubType::RadioactiveFungal,
193 BiomeSubType::RadioactiveContaminated,
194 BiomeSubType::RadioactiveIrradiated,
195 BiomeSubType::FrozenIce,
196 BiomeSubType::FrozenSnow,
197 BiomeSubType::FrozenGlacial,
198 BiomeSubType::BarrenDusty,
199 BiomeSubType::BarrenRocky,
200 BiomeSubType::BarrenMountainous,
201 BiomeSubType::DeadEmpty,
202 BiomeSubType::DeadCorroded,
203 BiomeSubType::DeadVoid,
204 BiomeSubType::WeirdHexagonal,
205 BiomeSubType::WeirdCabled,
206 BiomeSubType::WeirdBubbling,
207 BiomeSubType::WeirdFractured,
208 BiomeSubType::WeirdShattered,
209 BiomeSubType::WeirdContorted,
210 BiomeSubType::WeirdWireCell,
211 BiomeSubType::SwampMurky,
212 BiomeSubType::LavaVolcanic,
213 BiomeSubType::WaterworldOcean,
214 ];
215
216 #[test]
217 fn display_fromstr_roundtrip() {
218 for biome in [
219 Biome::Lush,
220 Biome::Toxic,
221 Biome::Scorched,
222 Biome::Radioactive,
223 Biome::Frozen,
224 Biome::Barren,
225 Biome::Dead,
226 Biome::Weird,
227 Biome::Red,
228 Biome::Green,
229 Biome::Blue,
230 Biome::Swamp,
231 Biome::Lava,
232 Biome::Waterworld,
233 Biome::GasGiant,
234 ] {
235 let s = biome.to_string();
236 let parsed: Biome = s.parse().unwrap();
237 assert_eq!(biome, parsed);
238 }
239 }
240
241 #[test]
242 fn case_insensitive_parse() {
243 assert_eq!("lush".parse::<Biome>().unwrap(), Biome::Lush);
244 assert_eq!("LUSH".parse::<Biome>().unwrap(), Biome::Lush);
245 assert_eq!("Lush".parse::<Biome>().unwrap(), Biome::Lush);
246 }
247
248 #[test]
249 fn gas_giant_alternate_name() {
250 assert_eq!("gas_giant".parse::<Biome>().unwrap(), Biome::GasGiant);
251 }
252
253 #[test]
254 fn unknown_biome_error() {
255 assert!("NotABiome".parse::<Biome>().is_err());
256 }
257
258 #[test]
259 fn subtype_display_fromstr_roundtrip() {
260 for v in BIOME_SUB_VARIANTS {
261 let s = v.to_string();
262 let parsed: BiomeSubType = s.parse().unwrap();
263 assert_eq!(v, parsed);
264 }
265 }
266}