serde enum formatting
Guide for serde enum formatting
Serde Enum Variant Formatting
Overview
This document explains how to correctly format enum variants in JSON files for Rust Serde deserialization.
Problem
Serde's default JSON serialization for enums follows specific patterns that must be matched in JSON data files. Mismatches cause parsing errors like:
ERROR Failed to parse tutorials: invalid type: unit variant, expected struct variantEnum Variant Types
1. Unit Variants
Rust Definition:
pub enum CompletionTrigger {
OpenInventory,
EquipItem,
AttackEnemy,
// ...
}Correct JSON:
{
"completion_trigger": "AttackEnemy"
}Incorrect JSON:
{
"completion_trigger": {
"AttackEnemy": null
}
}2. Struct Variants
Rust Definition:
pub enum CompletionTrigger {
UseItem { item_id: String },
TalkToNPC { npc_id: String },
ReachLocation { x: f32, z: f32, radius: f32 },
GainXP { skill: String, amount: i32 },
Custom { event_id: String },
}Correct JSON:
{
"completion_trigger": {
"UseItem": {
"item_id": "rohu"
}
}
}Incorrect JSON:
{
"completion_trigger": "UseItem"
}3. Tuple Variants
Rust Definition:
pub enum Message {
Move(f32, f32),
Write(String),
}Correct JSON:
{
"message": {
"Move": [10.5, 20.3]
}
}Common Errors and Fixes
Error 1: Unit Variant as Struct
Error Message:
invalid type: unit variant, expected struct variant at line 13 column 46Cause:
{
"completion_trigger": "Custom"
}Fix:
{
"completion_trigger": {
"Custom": {
"event_id": "click_anywhere"
}
}
}Error 2: Struct Variant as Unit
Error Message:
invalid type: struct variant, expected unit variantCause:
{
"completion_trigger": {
"AttackEnemy": null
}
}Fix:
{
"completion_trigger": "AttackEnemy"
}Tutorial System Example
Before (Incorrect)
{
"steps": [
{
"step_id": "welcome",
"completion_trigger": "Custom", // ❌ Should be struct variant
"hint": "Click anywhere"
},
{
"step_id": "find_enemy",
"completion_trigger": {
"AttackEnemy": null // ❌ Should be unit variant
},
"hint": "Look for enemies"
}
]
}After (Correct)
{
"steps": [
{
"step_id": "welcome",
"completion_trigger": {
"Custom": {
"event_id": "click_anywhere"
}
},
"hint": "Click anywhere"
},
{
"step_id": "find_enemy",
"completion_trigger": "AttackEnemy",
"hint": "Look for enemies"
}
]
}Verification Strategy
1. Check Rust Definition
Look at the enum definition to determine variant type:
pub enum CompletionTrigger {
OpenInventory, // Unit variant
EquipItem, // Unit variant
AttackEnemy, // Unit variant
UseItem { item_id: String }, // Struct variant
Custom { event_id: String }, // Struct variant
}2. Match JSON Format
- Unit variants: Use string directly
- Struct variants: Use object with variant name as key
3. Test Parsing
Run the game and check logs:
cargo run --bin legends_client 2>&1 | grep "Failed to parse"If parsing succeeds, you'll see:
INFO Loaded 3 tutorialsSerde Attributes
Changing Default Behavior
You can customize Serde's enum serialization with attributes:
#[derive(Deserialize, Serialize)]
#[serde(tag = "type")] // Internally tagged
pub enum CompletionTrigger {
OpenInventory,
UseItem { item_id: String },
}JSON with internal tagging:
{
"type": "UseItem",
"item_id": "rohu"
}Note: The current codebase uses Serde's default (externally tagged) format.
Best Practices
- Document enum types: Add comments to Rust enums indicating variant types
- Validate JSON: Use a JSON schema validator if possible
- Test incrementally: Test each enum variant individually
- Use consistent formatting: Follow the same pattern across all data files
Related Documentation
- Serde Enum Representations
- Quest System: See
loh_quest_system_refactorfor more enum examples