factorio-prototypes-json 0.1.0

Rust types that parse Factorio's Prototype JSON Format.
Documentation
//! Properties for another complex type.

use serde::{Deserialize, Serialize};

use super::basic_members::BasicMembers;
use super::types::{Type, TypeLiteral};

/// The default value of a [`Property`].
///
/// Either a textual [`PropertyDefault::Description`] or a [`PropertyDefault::Literal`] value.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
#[serde(untagged)]
pub enum PropertyDefault {
    /// A textual description of the default value.
    Description(String),

    /// A literal value of the default value.
    Literal(TypeLiteral),
}

/// A property for a complex type.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct Property {
    /// The basic fields of the type.
    #[serde(flatten)]
    pub basic: BasicMembers,

    /// The list of game expansions needed to use this property.
    ///
    /// If not present, no restrictions apply.
    ///
    /// Possible values: `"space_age"`
    pub visibility: Option<Vec<String>>,

    /// An alternative name for the property. Either this or [`BasicMember::name`] can be used to refer to the property.
    pub alt_name: Option<String>,

    /// Whether the property overrides a property of the same name in one of its parents.
    pub r#override: bool,

    /// The type of the property.
    pub r#type: Type,

    /// Whether the property is optional and can be omitted. If so, it falls back to [`Property::default`].
    pub optional: bool,

    /// The default value of the property.
    ///
    /// Either a textual description or a literal value.
    pub default: Option<PropertyDefault>,
}

#[cfg(test)]
mod tests {
    use crate::{image::Image, types::Literal};

    use super::*;

    #[test]
    fn default_field_type_description() {
        let json = r#""foo""#;
        let expected = PropertyDefault::Description("foo".into());
        let actual = serde_json::from_str::<PropertyDefault>(json).unwrap();
        assert_eq!(expected, actual);
    }

    #[test]
    fn default_field_type_literal() {
        let json = r#"
{
    "complex_type": "literal",
    "value": "foo"
}"#;

        let expected = PropertyDefault::Literal(TypeLiteral {
            value: Literal::String("foo".into()),
            description: None,
        });

        let actual = serde_json::from_str::<PropertyDefault>(json).unwrap();
        assert_eq!(expected, actual);
    }

    #[test]
    fn default_field_type_non_literal() {
        let json = r#"
{
    "complex_type": "struct"
}"#;

        let actual = serde_json::from_str::<PropertyDefault>(json);
        assert!(actual.is_err());
    }

    #[test]
    fn all_fields() {
        let json = r#"
{
    "name": "f1",
    "order": 2,
    "description": "f3",
    "lists": [
        "f4"
    ],
    "examples": [
        "f5"
    ],
    "images": [
        { "filename": "f6", "caption": "f7" }
    ],
    "visibility": [
        "f8"
    ],
    "alt_name": "f9",
    "override": true,
    "type": "f10",
    "optional": true,
    "default": {
        "complex_type": "literal",
        "value": 11
    }
}"#;

        let expected = Property {
            basic: BasicMembers {
                name: "f1".into(),
                order: 2.into(),
                description: "f3".into(),
                lists: Some(vec!["f4".into()]),
                examples: Some(vec!["f5".into()]),
                images: Some(vec![Image {
                    filename: "f6".into(),
                    caption: Some("f7".into()),
                }]),
            },
            visibility: Some(vec!["f8".into()]),
            alt_name: Some("f9".into()),
            r#override: true,
            r#type: Type::Simple("f10".into()),
            optional: true,
            default: Some(PropertyDefault::Literal(TypeLiteral {
                value: Literal::Number(11.into()),
                description: None,
            })),
        };

        let actual = serde_json::from_str::<Property>(json).unwrap();
        assert_eq!(expected, actual);
    }

    #[test]
    fn without_optionals() {
        let json = r#"
{
    "name": "f1",
    "order": 2,
    "description": "f3",
    "override": true,
    "type": "f4",
    "optional": true
}"#;

        let expected = Property {
            basic: BasicMembers {
                name: "f1".into(),
                order: 2.into(),
                description: "f3".into(),
                lists: None,
                examples: None,
                images: None,
            },
            visibility: None,
            alt_name: None,
            r#override: true,
            r#type: Type::Simple("f4".into()),
            optional: true,
            default: None,
        };

        let actual = serde_json::from_str::<Property>(json).unwrap();
        assert_eq!(expected, actual);
    }
}