bevy_map_schema/
lib.rs

1//! Schema validation for bevy_map_editor
2//!
3//! This crate provides schema definitions and validation for entity types
4//! used in bevy_map_editor. It allows defining data types with properties
5//! that can be validated at load time.
6//!
7//! # Example
8//!
9//! ```rust,ignore
10//! use bevy_map_schema::{Schema, load_schema, validate_instance};
11//! use bevy_map_core::EntityInstance;
12//!
13//! // Load schema from JSON
14//! let schema = load_schema("schema.json")?;
15//!
16//! // Validate an entity instance against the schema
17//! let entity = EntityInstance::new("NPC".to_string(), [100.0, 200.0]);
18//! schema.validate_entity(&entity)?;
19//! ```
20
21mod types;
22mod validate;
23
24pub use types::*;
25pub use validate::*;
26
27use std::path::Path;
28use thiserror::Error;
29
30/// Errors that can occur when loading or validating schemas
31#[derive(Debug, Error)]
32pub enum SchemaError {
33    #[error("IO error: {0}")]
34    IoError(String),
35    #[error("Parse error: {0}")]
36    ParseError(String),
37    #[error("Validation error: {0}")]
38    ValidationError(String),
39}
40
41/// Load a schema from a JSON file
42pub fn load_schema(path: &Path) -> Result<Schema, SchemaError> {
43    let content = std::fs::read_to_string(path).map_err(|e| SchemaError::IoError(e.to_string()))?;
44
45    parse_schema(&content)
46}
47
48/// Parse a schema from a JSON string
49pub fn parse_schema(json: &str) -> Result<Schema, SchemaError> {
50    let schema: Schema =
51        serde_json::from_str(json).map_err(|e| SchemaError::ParseError(e.to_string()))?;
52
53    validate_schema(&schema)?;
54
55    Ok(schema)
56}
57
58/// Save a schema to a JSON file
59pub fn save_schema(schema: &Schema, path: &Path) -> Result<(), SchemaError> {
60    let content =
61        serde_json::to_string_pretty(schema).map_err(|e| SchemaError::ParseError(e.to_string()))?;
62
63    std::fs::write(path, content).map_err(|e| SchemaError::IoError(e.to_string()))?;
64
65    Ok(())
66}
67
68/// Load a schema from bytes
69pub fn load_schema_from_bytes(bytes: &[u8]) -> Result<Schema, SchemaError> {
70    let schema: Schema =
71        serde_json::from_slice(bytes).map_err(|e| SchemaError::ParseError(e.to_string()))?;
72
73    validate_schema(&schema)?;
74
75    Ok(schema)
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    #[test]
83    fn test_parse_minimal_schema() {
84        let json = r#"{
85            "version": 1,
86            "project": { "name": "Test" },
87            "enums": {},
88            "data_types": {},
89            "embedded_types": {}
90        }"#;
91
92        let schema = parse_schema(json).unwrap();
93        assert_eq!(schema.version, 1);
94        assert_eq!(schema.project.name, "Test");
95    }
96
97    #[test]
98    fn test_parse_schema_with_types() {
99        let json = r##"{
100            "version": 1,
101            "project": { "name": "Test" },
102            "enums": {
103                "ItemType": ["Weapon", "Armor", "Consumable"]
104            },
105            "data_types": {
106                "Item": {
107                    "color": "#4CAF50",
108                    "properties": [
109                        { "name": "name", "type": "string", "required": true },
110                        { "name": "itemType", "type": "enum", "enumType": "ItemType" }
111                    ]
112                }
113            },
114            "embedded_types": {}
115        }"##;
116
117        let schema = parse_schema(json).unwrap();
118        assert!(schema.enums.contains_key("ItemType"));
119        assert!(schema.data_types.contains_key("Item"));
120
121        let item_type = schema.get_type("Item").unwrap();
122        assert_eq!(item_type.properties.len(), 2);
123    }
124
125    #[test]
126    fn test_invalid_enum_reference() {
127        let json = r#"{
128            "version": 1,
129            "project": { "name": "Test" },
130            "enums": {},
131            "data_types": {
132                "Item": {
133                    "properties": [
134                        { "name": "itemType", "type": "enum", "enumType": "NonExistent" }
135                    ]
136                }
137            },
138            "embedded_types": {}
139        }"#;
140
141        let result = parse_schema(json);
142        assert!(result.is_err());
143        if let Err(SchemaError::ValidationError(msg)) = result {
144            assert!(msg.contains("NonExistent"));
145        }
146    }
147}