1#![warn(clippy::all)]
6#![deny(missing_docs)]
7#![forbid(unused_must_use)]
8#![feature(type_alias_enum_variants)]
10
11use serde::{Deserialize, Serialize};
12
13mod ability;
14mod attribute;
15mod cost;
16mod ids;
17mod race;
18mod requirement;
19mod unittype;
20mod upgrade;
21mod weapon;
22
23pub use serde_json::Error as DeserializeError;
24
25pub use self::ability::Ability;
26pub use self::attribute::Attribute;
27pub use self::cost::Cost;
28pub use self::ids::*;
29pub use self::race::Race;
30pub use self::requirement::Requirement;
31pub use self::unittype::UnitType;
32pub use self::upgrade::Upgrade;
33pub use self::weapon::*;
34
35use self::ability::{AbilityResearch, AbilityTarget};
36
37#[derive(Debug, Clone, PartialEq, Eq)]
39pub enum QueryError {
40 NotFound,
42}
43
44#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
46pub struct TechData {
47 #[serde(rename = "Ability")]
49 pub abilities: Vec<Ability>,
50 #[serde(rename = "Unit")]
52 pub unittypes: Vec<UnitType>,
53 #[serde(rename = "Upgrade")]
55 pub upgrades: Vec<Upgrade>,
56}
57impl TechData {
58 pub fn current() -> Self {
60 serde_json::from_str::<TechData>(include_str!("../data/data.json")).expect("Invalid bundled data")
61 }
62
63 pub fn from_json(source: &str) -> Result<Self, DeserializeError> {
65 serde_json::from_str::<TechData>(source)
66 }
67
68 pub fn ability(&self, ability_id: AbilityId) -> Option<Ability> {
70 for ability in &self.abilities {
71 if ability.id == ability_id {
72 return Some(ability.clone());
73 }
74 }
75 None
76 }
77
78 pub fn upgrade(&self, upgrade_id: UpgradeId) -> Option<Upgrade> {
80 for upgrade in &self.upgrades {
81 if upgrade.id == upgrade_id {
82 return Some(upgrade.clone());
83 }
84 }
85 None
86 }
87
88 pub fn unittype(&self, unittype_id: UnitTypeId) -> Option<UnitType> {
90 for unittype in &self.unittypes {
91 if unittype.id == unittype_id {
92 return Some(unittype.clone());
93 }
94 }
95 None
96 }
97
98 pub fn upgrade_ability(&self, upgrade_id: UpgradeId) -> Result<Ability, QueryError> {
100 for ability in &self.abilities {
101 if let AbilityTarget::Research(AbilityResearch { upgrade }) = ability.target {
102 if upgrade == upgrade_id {
103 return Ok(ability.clone());
104 }
105 }
106 }
107 Err(QueryError::NotFound)
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::{QueryError, TechData, UpgradeId};
114
115 use std::fs::File;
116 use std::io::prelude::*;
117
118 #[test]
119 fn load_data() {
120 let mut f = File::open("data/data.json").expect("File not found");
121
122 let mut contents = String::new();
123 f.read_to_string(&mut contents).expect("Could not read");
124
125 TechData::from_json(&contents).expect("Deserialization failed");
126 }
127
128 #[test]
129 fn upgrade_ability() {
130 let td = TechData::current();
131
132 assert_eq!(
133 td.upgrade_ability(UpgradeId::new(64)).unwrap().name,
134 "RESEARCH_BURROW"
135 );
136
137 assert_eq!(
138 td.upgrade_ability(UpgradeId::new(86)).unwrap().name,
139 "RESEARCH_CHARGE"
140 );
141
142 assert_eq!(
143 td.upgrade_ability(UpgradeId::new(123_456)),
144 Err(QueryError::NotFound)
145 );
146 }
147}