ed_journals/modules/galaxy/models/
volcanism.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use std::str::FromStr;

use lazy_static::lazy_static;
use regex::Regex;
use serde::Serialize;
use thiserror::Error;

use crate::from_str_deserialize_impl;
use crate::modules::galaxy::VolcanismType;

#[derive(Debug, Serialize, Clone, PartialEq)]
pub struct Volcanism {
    pub kind: VolcanismType,
    pub classification: VolcanismClassification,
}

#[derive(Debug, Serialize, Clone, PartialEq)]
pub enum VolcanismClassification {
    Minor,
    Normal,
    Major,
}

#[derive(Debug, Error)]
pub enum VolcanismError {
    #[error("Unknown volcanism type: {0}")]
    UnknownVolcanismType(#[source] serde_json::Error),

    #[error("Failed to parse volcanism: '{0}'")]
    FailedToParse(String),
}

lazy_static! {
    static ref VOLCANISM_REGEX: Regex =
        Regex::new("(^minor |^major |^)([a-zA-Z ]+) volcanism$").unwrap();
}

impl FromStr for Volcanism {
    type Err = VolcanismError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        // TODO I would prefer to move this to a impl FromStr for Option<Volcanism> but this is not
        //  possible. Some other way needs to be found.
        if s.is_empty() {
            return Ok(Volcanism {
                kind: VolcanismType::None,
                classification: VolcanismClassification::Normal,
            });
        }

        let Some(captures) = VOLCANISM_REGEX.captures(s) else {
            return Err(VolcanismError::FailedToParse(s.to_string()));
        };

        let kind = captures
            .get(2)
            .expect("Should have been captured already")
            .as_str()
            .parse()
            .map_err(VolcanismError::UnknownVolcanismType)?;

        let classification = match captures
            .get(1)
            .expect("Should have been captured already")
            .as_str()
        {
            "minor " => VolcanismClassification::Minor,
            "major " => VolcanismClassification::Major,
            _ => VolcanismClassification::Normal,
        };

        Ok(Volcanism {
            kind,
            classification,
        })
    }
}

from_str_deserialize_impl!(Volcanism);

#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use crate::modules::galaxy::{Volcanism, VolcanismClassification, VolcanismType};

    #[test]
    fn volcanism_test_cases_are_parsed_correctly() {
        let test_cases = [
            (
                "minor silicate vapour geysers volcanism",
                Volcanism {
                    kind: VolcanismType::SilicateVapourGeysers,
                    classification: VolcanismClassification::Minor,
                },
            ),
            (
                "major rocky magma volcanism",
                Volcanism {
                    kind: VolcanismType::RockyMagma,
                    classification: VolcanismClassification::Major,
                },
            ),
            (
                "minor metallic magma volcanism",
                Volcanism {
                    kind: VolcanismType::MetallicMagma,
                    classification: VolcanismClassification::Minor,
                },
            ),
        ];

        for (case, expected) in test_cases {
            let result = Volcanism::from_str(case);

            assert!(result.is_ok());
            assert_eq!(result.unwrap(), expected);
        }
    }
}