factorio-prototypes-json 0.1.0

Rust types that parse Factorio's Prototype JSON Format.
Documentation
//! Rust types that parse Factorio's [machine-readable JSON format](https://lua-api.factorio.com/latest/auxiliary/json-docs-prototype.html).

#![deny(missing_docs)]

use std::{fs::File, io::BufReader, path::Path};

use serde::{Deserialize, Serialize};
use serde_json::Number;

mod basic_members;
mod concept;
mod custom_properties;
mod define;
mod image;
mod property;
mod prototype;
mod types;

pub use basic_members::BasicMembers;
pub use concept::Concept;
pub use custom_properties::CustomProperties;
pub use define::{Define, DefineValue};
pub use image::Image;
pub use property::{Property, PropertyDefault};
pub use prototype::Prototype;
pub use types::{Literal, Type, TypeLiteral};

/// Errors that can occur while loading the JSON data with [`load_prototypes_json`].
#[derive(Debug)]
pub enum LoadingError {
    /// The file could not be opened.
    FileOpen(std::io::Error),
    /// The JSON data could not be parsed.
    JsonParse(serde_json::Error),
}

impl From<std::io::Error> for LoadingError {
    fn from(err: std::io::Error) -> Self {
        LoadingError::FileOpen(err)
    }
}

impl From<serde_json::Error> for LoadingError {
    fn from(err: serde_json::Error) -> Self {
        LoadingError::JsonParse(err)
    }
}

impl core::fmt::Display for LoadingError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            LoadingError::FileOpen(err) => write!(f, "Failed to open file: {}", err),
            LoadingError::JsonParse(err) => write!(f, "Failed to parse JSON: {}", err),
        }
    }
}

impl core::error::Error for LoadingError {
    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
        match self {
            LoadingError::FileOpen(err) => Some(err),
            LoadingError::JsonParse(err) => Some(err),
        }
    }
}

/// The JSON, deserializable from the [machine-readable JSON format](https://lua-api.factorio.com/latest/auxiliary/json-docs-prototype.html) file.
///
/// See the Factorio documentation for more details.
#[derive(Serialize, Deserialize, Debug)]
pub struct PrototypeApi {
    /// The application this documentation is for. Will always be `"factorio"`.
    pub application: String,

    /// The version of the game that this documentation is for. An example would be `"1.1.90"`.
    pub application_version: String,

    /// The version of the machine-readable format itself. It is incremented every time the format changes.
    pub api_version: Number,

    /// Indicates the state this documentation is for. Will always be `"prototype"` (as opposed to `"runtime"`; see the [data lifecycle](https://lua-api.factorio.com/latest/auxiliary/data-lifecycle.html) for more detail).
    pub stage: String,

    /// The list of prototypes that can be created. Equivalent to the [prototypes](https://lua-api.factorio.com/latest/prototypes.html) page.
    pub prototypes: Vec<prototype::Prototype>,

    /// The list of types (concepts) that the prototypes use. Equivalent to the [types](https://lua-api.factorio.com/latest/types.html) page.
    pub types: Vec<concept::Concept>,

    /// The list of defines that the game uses. Equivalent to the [defines](https://lua-api.factorio.com/latest/defines.html) page.
    pub defines: Vec<define::Define>,
}

impl PrototypeApi {
    /// Reads the JSON data from the `prototype-api.json` file.
    pub fn try_from_file<P: AsRef<Path>>(path: P) -> Result<Self, LoadingError> {
        // Counterintuitively, reading is faster if you read the entire file into a String then parse it.
        let json_file = File::open(path)?;
        let reader = BufReader::new(json_file);
        let json = serde_json::from_reader(reader)?;
        Ok(json)
    }
}