Skip to main content

ed_journals/modules/galaxy/models/
volcanism.rs

1use std::str::FromStr;
2
3use lazy_static::lazy_static;
4use regex::Regex;
5use serde::Serialize;
6use thiserror::Error;
7
8use crate::from_str_deserialize_impl;
9use crate::modules::galaxy::VolcanismType;
10
11#[derive(Debug, Serialize, Clone, PartialEq)]
12pub struct Volcanism {
13    pub kind: VolcanismType,
14    pub classification: VolcanismClassification,
15}
16
17#[derive(Debug, Serialize, Clone, PartialEq)]
18pub enum VolcanismClassification {
19    Minor,
20    Normal,
21    Major,
22}
23
24#[derive(Debug, Error)]
25pub enum VolcanismError {
26    #[error("Unknown volcanism type: {0}")]
27    UnknownVolcanismType(#[source] serde_json::Error),
28
29    #[error("Failed to parse volcanism: '{0}'")]
30    FailedToParse(String),
31}
32
33lazy_static! {
34    static ref VOLCANISM_REGEX: Regex =
35        Regex::new("(^[mM]inor |^[mM]ajor |^)([a-zA-Z ]+?)( volcanism)?$").unwrap();
36}
37
38impl FromStr for Volcanism {
39    type Err = VolcanismError;
40
41    fn from_str(s: &str) -> Result<Self, Self::Err> {
42        // TODO I would prefer to move this to a impl FromStr for Option<Volcanism> but this is not
43        //  possible. Some other way needs to be found.
44        if s.is_empty() {
45            return Ok(Volcanism {
46                kind: VolcanismType::None,
47                classification: VolcanismClassification::Normal,
48            });
49        }
50
51        let Some(captures) = VOLCANISM_REGEX.captures(s) else {
52            return Err(VolcanismError::FailedToParse(s.to_string()));
53        };
54
55        let kind = captures
56            .get(2)
57            .expect("Should have been captured already")
58            .as_str()
59            .parse()
60            .map_err(VolcanismError::UnknownVolcanismType)?;
61
62        let classification = match captures
63            .get(1)
64            .expect("Should have been captured already")
65            .as_str()
66        {
67            "minor " | "Minor " => VolcanismClassification::Minor,
68            "major " | "Major " => VolcanismClassification::Major,
69            _ => VolcanismClassification::Normal,
70        };
71
72        Ok(Volcanism {
73            kind,
74            classification,
75        })
76    }
77}
78
79from_str_deserialize_impl!(Volcanism);
80
81#[cfg(test)]
82mod tests {
83    use std::str::FromStr;
84
85    use crate::modules::galaxy::{Volcanism, VolcanismClassification, VolcanismType};
86
87    #[test]
88    fn volcanism_test_cases_are_parsed_correctly() {
89        let test_cases = [
90            (
91                "minor silicate vapour geysers volcanism",
92                Volcanism {
93                    kind: VolcanismType::SilicateVapourGeysers,
94                    classification: VolcanismClassification::Minor,
95                },
96            ),
97            (
98                "major rocky magma volcanism",
99                Volcanism {
100                    kind: VolcanismType::RockyMagma,
101                    classification: VolcanismClassification::Major,
102                },
103            ),
104            (
105                "minor metallic magma volcanism",
106                Volcanism {
107                    kind: VolcanismType::MetallicMagma,
108                    classification: VolcanismClassification::Minor,
109                },
110            ),
111        ];
112
113        for (case, expected) in test_cases {
114            let result = Volcanism::from_str(case);
115
116            assert!(result.is_ok());
117            assert_eq!(result.unwrap(), expected);
118        }
119    }
120}