data/
breed.rs

1//! Data structures and logic about the DMW2 breed formulae
2
3use std::str;
4
5use quick_xml::events::BytesStart;
6use serde::Serialize;
7
8use error::Error;
9use crate::xml;
10
11/// A parent in a breed formula.
12#[derive(Clone, Serialize, PartialEq)]
13#[cfg_attr(test, derive(Debug))]
14pub enum Parent
15{
16    /// Any monster in this family could be the parent. Value
17    /// indicates the name of the family.
18    Family(String),
19    /// The parent is this monster, whose name is the value.
20    Monster(String),
21}
22
23/// A parent in a breed formula, with some extra requirements.
24#[derive(Clone, Serialize)]
25#[cfg_attr(test, derive(Debug, PartialEq))]
26pub struct ParentRequirement
27{
28    /// The parent in the breed formula
29    pub parent: Parent,
30    /// The minimal +level of this parent. Usually this is just 0. But
31    /// for some formulae (e.g. Slime + Slime = KingSlime) this is
32    /// non-zero.
33    pub min_plus: u8,
34}
35
36impl ParentRequirement
37{
38    fn fromXMLTag(e: &BytesStart) -> Result<Self, Error>
39    {
40        let min_plus = if let Some(attr) = e.try_get_attribute("min_plus")
41            .map_err(|_| xmlerr!("Failed to get attribute 'min_plus'."))?
42        {
43            str::from_utf8(attr.value.as_ref()).map_err(
44                |_| xmlerr!("Failed to decode min +level in XML"))?.parse()
45                .map_err(|_| xmlerr!("Invalid min +level in XML"))?
46        }
47        else
48        {
49            0
50        };
51
52        if let Some(attr) = e.try_get_attribute("monster").map_err(
53            |_| xmlerr!("Failed to get attribute 'monster'."))?
54        {
55            let monster = String::from_utf8(attr.value.into_owned()).map_err(
56                |_| xmlerr!("Failed to decode monster name in XML"))?;
57            Ok(Self {
58                parent: Parent::Monster(monster),
59                min_plus
60            })
61        }
62        else if let Some(attr) = e.try_get_attribute("family").map_err(
63            |_| xmlerr!("Failed to get attribute 'family'."))?
64        {
65            let family = String::from_utf8(attr.value.into_owned()).map_err(
66                |_| xmlerr!("Failed to decode family name in XML"))?;
67            Ok(Self {
68                parent: Parent::Family(family),
69                min_plus
70            })
71        }
72        else
73        {
74            Err(xmlerr!("Invalid parent definition in XML"))
75        }
76    }
77}
78
79/// A breed formula
80#[derive(Clone, Serialize)]
81#[cfg_attr(test, derive(Debug, PartialEq))]
82pub struct Formula
83{
84    /// The base monster in the formula.
85    pub base: Vec<ParentRequirement>,
86    /// The mate monster in the formula.
87    pub mate: Vec<ParentRequirement>,
88    /// The name of the offspring monster.
89    pub offspring: String,
90}
91
92impl Formula
93{
94    pub fn fromXML(x: &[u8]) -> Result<Self, Error>
95    {
96        let mut offspring = String::new();
97        let mut base = Vec::new();
98        let mut mate = Vec::new();
99        let mut parser = xml::Parser::new();
100
101        parser.addBeginHandler("breed", |_, e: &BytesStart| {
102            if let Some(attr) = e.try_get_attribute("target").map_err(
103                |_| xmlerr!("Failed to get attribute 'target'."))?
104            {
105                offspring = String::from_utf8(attr.value.into_owned()).map_err(
106                    |_| xmlerr!("Failed to decode breed target name in XML"))?;
107                Ok(())
108            }
109            else
110            {
111                Err(xmlerr!("Found breed formula without target"))
112            }
113        });
114
115        parser.addBeginHandler("breed-requirement", |path, e: &BytesStart| {
116            if let Some(tag) = path.get(path.len() - 2)
117            {
118                match tag.as_str()
119                {
120                    "base" => base.push(ParentRequirement::fromXMLTag(e)?),
121                    "mate" => mate.push(ParentRequirement::fromXMLTag(e)?),
122                    _ => return Err(xmlerr!("Invalid tag '{}'", tag)),
123                }
124            }
125            else
126            {
127                return Err(xmlerr!("Invalid breed requirement"));
128            }
129            Ok(())
130        });
131        parser.parse(x)?;
132        drop(parser);
133        let result = Self { base, mate, offspring };
134        Ok(result)
135    }
136}
137
138// ========== Tests =================================================>
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143
144    use anyhow::Result;
145
146    #[test]
147    fn deserializeFormula() -> Result<()>
148    {
149        let xml = r#"<breed target="KingSlime">
150      <base>
151        <breed-requirement monster="SpotKing"/>
152      </base>
153      <mate>
154        <breed-requirement monster="DeadNoble"/>
155        <breed-requirement monster="Divinegon"/>
156        <breed-requirement monster="Gigantes"/>
157      </mate>
158    </breed>"#;
159        let p = Formula::fromXML(xml.as_bytes())?;
160        assert_eq!(
161            p,
162            Formula {
163                base: vec![ParentRequirement{ parent: Parent::Monster("SpotKing".to_owned()), min_plus: 0 }],
164                mate: vec![ParentRequirement{ parent: Parent::Monster("DeadNoble".to_owned()), min_plus: 0 },
165                           ParentRequirement{ parent: Parent::Monster("Divinegon".to_owned()), min_plus: 0 },
166                           ParentRequirement{ parent: Parent::Monster("Gigantes".to_owned()), min_plus: 0},],
167                offspring: "KingSlime".to_owned(),
168            });
169
170        let xml = r#"<breed target="Octogon">
171      <base>
172        <breed-requirement monster="Octoreach"/>
173      </base>
174      <mate>
175        <breed-requirement monster="Octoreach" min_plus="4"/>
176      </mate>
177    </breed>"#;
178        let p = Formula::fromXML(xml.as_bytes())?;
179        assert_eq!(
180            p,
181            Formula {
182                base: vec![ParentRequirement{ parent: Parent::Monster("Octoreach".to_owned()), min_plus: 0 }],
183                mate: vec![ParentRequirement{ parent: Parent::Monster("Octoreach".to_owned()), min_plus: 4 }],
184                offspring: "Octogon".to_owned(),
185            });
186
187        let xml = r#"<breed target="Moray">
188      <base>
189        <breed-requirement family="water"/>
190      </base>
191      <mate>
192        <breed-requirement family="dragon"/>
193      </mate>
194    </breed>"#;
195        let p = Formula::fromXML(xml.as_bytes())?;
196        assert_eq!(
197            p,
198            Formula {
199                base: vec![ParentRequirement{ parent: Parent::Family("water".to_owned()), min_plus: 0 }],
200                mate: vec![ParentRequirement{ parent: Parent::Family("dragon".to_owned()), min_plus: 0 }],
201                offspring: "Moray".to_owned(),
202            });
203
204        Ok(())
205    }
206}