1use std::{collections::HashMap, fs, path::Path};
2
3use itertools::Itertools;
4use semver::Version;
5use serde_derive::Deserialize;
6use tracing::debug;
7
8use crate::{FactorioExporterError, Result};
9
10pub fn load_api(api_file_path: &Path) -> Result<Api> {
36 debug!("Loading API definition file from {}", &api_file_path.display());
37
38 if !api_file_path.is_file() {
39 return Err(FactorioExporterError::FileNotFoundError { file: api_file_path.into() });
40 }
41
42 let s = fs::read_to_string(api_file_path)?;
43 let api: Api = serde_json::from_str(&s)?;
44
45 debug!("parsed API, got {} classes and {} concepts", &api.classes.len(), &api.concepts.len());
46 Ok(api)
47}
48
49#[derive(Debug, Deserialize)]
56#[serde(from = "RawApi")]
57pub struct Api {
58 pub application_version: Version,
59 pub classes: HashMap<String, Class>,
60 pub concepts: HashMap<String, Concept>,
61}
62
63#[derive(Debug, Deserialize)]
64struct RawApi {
65 application: String,
66 application_version: Version,
67 api_version: i32,
68 stage: String,
69 classes: Vec<Class>,
70 concepts: Vec<Concept>,
71}
72
73impl From<RawApi> for Api {
74 fn from(raw: RawApi) -> Self {
75 assert!(raw.application == "factorio");
76 assert!(raw.api_version == 3);
77 assert!(raw.stage == "runtime");
78
79 Api {
80 application_version: raw.application_version,
81 classes: raw.classes.into_iter().map(|class| (class.name.clone(), class)).collect(),
82 concepts: raw
83 .concepts
84 .into_iter()
85 .map(|concept| (concept.name.clone(), concept))
86 .collect(),
87 }
88 }
89}
90
91#[derive(Debug, Deserialize)]
92#[serde(from = "RawClass")]
93pub struct Class {
94 pub name: String,
95 pub attributes: HashMap<String, Attribute>,
96 pub description: String,
97 pub notes: Option<Vec<String>>,
98 pub examples: Option<Vec<String>>,
99 pub order: u64,
100 pub base_classes: Option<Vec<String>>,
101}
102
103pub trait HasAttributes {
104 fn attributes(&self) -> Vec<&Attribute>;
105}
106
107impl HasAttributes for Class {
108 fn attributes(&self) -> Vec<&Attribute> {
109 self.attributes
110 .values()
111 .filter(|a| a.read != Some(false) && a.subclasses.is_none())
113 .sorted_by_key(|attr| &attr.order)
114 .collect_vec()
115 }
116}
117
118#[derive(Debug, Deserialize)]
119struct RawClass {
120 name: String,
121 attributes: Vec<Attribute>,
122 description: String,
123 notes: Option<Vec<String>>,
124 examples: Option<Vec<String>>,
125 order: u64,
126 base_classes: Option<Vec<String>>,
127}
128
129impl From<RawClass> for Class {
130 fn from(raw: RawClass) -> Self {
131 Class {
132 name: raw.name,
133 attributes: raw.attributes.into_iter().map(|attr| (attr.name.clone(), attr)).collect(),
134 description: raw.description,
135 notes: raw.notes,
136 examples: raw.examples,
137 order: raw.order,
138 base_classes: raw.base_classes,
139 }
140 }
141}
142
143#[derive(Debug, Deserialize)]
144pub struct Attribute {
145 pub name: String,
146 pub r#type: Type,
147 pub optional: bool,
148 pub order: u64,
149 pub description: String,
150 pub read: Option<bool>,
151 pub subclasses: Option<Vec<String>>,
152}
153
154#[derive(Debug, Deserialize)]
155pub struct VariantParameterGroup {
156 pub name: String,
157 pub order: u64,
158 pub parameters: Vec<Attribute>,
159}
160
161#[derive(Debug, Deserialize)]
162#[serde(untagged)]
163pub enum LiteralValue {
164 String(String),
165 Boolean(bool),
166}
167
168#[derive(Debug, Deserialize)]
169pub struct Concept {
170 pub name: String,
171 pub r#type: Type,
172 pub description: String,
173 pub notes: Option<Vec<String>>,
174 pub examples: Option<Vec<String>>,
175 pub order: u64,
176}
177
178impl HasAttributes for Concept {
179 fn attributes(&self) -> Vec<&Attribute> {
180 if let Type::Table { parameters, variant_parameter_groups } = &self.r#type {
181 variant_parameter_groups
182 .iter()
183 .flatten()
184 .flat_map(|group| &group.parameters)
185 .chain(parameters)
186 .sorted_by_key(|&a| &a.name)
187 .dedup_by(|a, b| a.name == b.name)
188 .sorted_by_key(|&a| &a.order)
189 .collect_vec()
190 } else {
191 Vec::new()
192 }
193 }
194}
195
196pub fn is_number(r#type: &Type) -> bool {
197 use self::Type::*;
198 match r#type {
199 Int8 | Int | UInt8 | UInt16 | UInt | UInt64 | Double | Float | Number => true,
200 Union { options } => options.iter().all(is_number),
201 _ => false,
202 }
203}
204
205#[derive(Debug, Deserialize)]
206#[serde(from = "RawType")]
207pub enum Type {
208 Int8,
209 Int,
210 UInt8,
211 UInt16,
212 UInt,
213 UInt64,
214 Double,
215 Float,
216 Number,
217 String,
218 Boolean,
219
220 NamedType {
221 name: String,
222 },
223
224 Array {
225 value: Box<Type>,
226 },
227
228 Dictionary {
229 key: Box<Type>,
230 value: Box<Type>,
231 },
232
233 Literal {
234 value: LiteralValue,
235 description: Option<String>,
236 },
237
238 LuaCustomTable {
239 key: Box<Type>,
240 value: Box<Type>,
241 },
242
243 Struct {
244 attributes: Vec<Attribute>,
245 },
246
247 Table {
248 parameters: Vec<Attribute>,
249 variant_parameter_groups: Option<Vec<VariantParameterGroup>>,
250 },
251
252 Tuple {
253 parameters: Vec<Attribute>,
254 },
255
256 Type {
257 value: Box<Type>,
258 },
259
260 Union {
261 options: Vec<Type>,
262 },
263}
264
265impl<'a> From<RawType<'a>> for Type {
266 fn from(raw: RawType<'a>) -> Self {
267 use ComplexType::*;
268 use RawType::*;
269 match raw {
270 String("int8") => Self::Int8,
271 String("int") => Self::Int,
272 String("uint8") => Self::UInt8,
273 String("uint16") => Self::UInt16,
274 String("uint") => Self::UInt,
275 String("uint64") => Self::UInt64,
276 String("double") => Self::Double,
277 String("float") => Self::Float,
278 String("number") => Self::Number,
279 String("string" | "LocalisedString") => Self::String,
280 String("boolean") => Self::Boolean,
281
282 String(name) => Self::NamedType { name: name.into() },
283
284 Complex(Array { value }) => Self::Array { value },
285 Complex(Dictionary { key, value }) => Self::Dictionary { key, value },
286 Complex(Literal { value, description }) => Self::Literal { value, description },
287 Complex(LuaCustomTable { key, value }) => Self::LuaCustomTable { key, value },
288 Complex(Struct { attributes }) => Self::Struct { attributes },
289 Complex(Table { parameters, variant_parameter_groups }) => {
290 Self::Table { parameters, variant_parameter_groups }
291 }
292 Complex(Tuple { parameters }) => Self::Tuple { parameters },
293 Complex(Type { value }) => Self::Type { value },
294 Complex(Union { options }) => Self::Union { options },
295 }
296 }
297}
298
299#[derive(Debug, Deserialize)]
300#[serde(untagged)]
301enum RawType<'a> {
302 String(&'a str),
303 Complex(ComplexType),
304}
305
306#[derive(Debug, Deserialize)]
307#[serde(tag = "complex_type")]
308#[serde(rename_all = "lowercase")]
309enum ComplexType {
310 Array {
311 value: Box<Type>,
312 },
313
314 Dictionary {
315 key: Box<Type>,
316 value: Box<Type>,
317 },
318
319 Literal {
320 value: LiteralValue,
321 description: Option<String>,
322 },
323
324 #[serde(rename = "LuaCustomTable")]
325 LuaCustomTable {
326 key: Box<Type>,
327 value: Box<Type>,
328 },
329
330 Struct {
331 attributes: Vec<Attribute>,
332 },
333
334 Table {
335 parameters: Vec<Attribute>,
336 variant_parameter_groups: Option<Vec<VariantParameterGroup>>,
337 },
338
339 Tuple {
340 parameters: Vec<Attribute>,
341 },
342
343 Type {
344 value: Box<Type>,
345 },
346
347 Union {
348 options: Vec<Type>,
349 },
350}