use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::Path;
use serde_json::Value;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct MortaredData {
pub metadata: Metadata,
#[serde(default)]
pub variables: Vec<Variable>,
#[serde(default)]
pub constants: Vec<Constant>,
#[serde(default)]
pub enums: Vec<Enum>,
pub nodes: Vec<Node>,
pub functions: Vec<Function>,
#[serde(default)]
pub events: Vec<EventDef>,
#[serde(default)]
pub timelines: Vec<TimelineDef>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Metadata {
pub version: String,
pub generated_at: DateTime<Utc>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Node {
pub name: String,
#[serde(default)]
pub content: Vec<Value>, #[serde(default, skip_serializing_if = "Option::is_none")]
pub branches: Option<Vec<BranchDef>>,
#[serde(default)]
pub variables: Vec<Variable>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(tag = "type")]
#[serde(rename_all = "snake_case")]
pub enum ContentItem {
Text {
value: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
interpolated_parts: Option<Vec<StringPart>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
condition: Option<IfCondition>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pre_statements: Vec<Statement>,
#[serde(default, skip_serializing_if = "Option::is_none")]
events: Option<Vec<Event>>,
},
Line {
value: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
interpolated_parts: Option<Vec<StringPart>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
condition: Option<IfCondition>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pre_statements: Vec<Statement>,
#[serde(default, skip_serializing_if = "Option::is_none")]
events: Option<Vec<Event>>,
},
RunEvent {
name: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
args: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
index_override: Option<IndexOverride>,
#[serde(default)]
ignore_duration: bool,
},
RunTimeline {
name: String,
},
Choice {
options: Vec<Choice>,
},
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct IndexOverride {
#[serde(rename = "type")]
pub override_type: String,
pub value: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BranchDef {
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub enum_type: Option<String>,
pub cases: Vec<BranchCase>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct BranchCase {
pub condition: String,
pub text: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub events: Option<Vec<Event>>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Statement {
#[serde(rename = "type")]
pub stmt_type: String, #[serde(default, skip_serializing_if = "Option::is_none")]
pub var_name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub value: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct IfCondition {
#[serde(rename = "type")]
pub cond_type: String, #[serde(default, skip_serializing_if = "Option::is_none")]
pub operator: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub left: Option<Box<IfCondition>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub right: Option<Box<IfCondition>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub operand: Option<Box<IfCondition>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub value: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct StringPart {
#[serde(rename = "type")]
pub part_type: String, pub content: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub function_name: Option<String>,
#[serde(default)]
pub args: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub enum_type: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub branches: Option<Vec<BranchCase>>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Event {
pub index: f64,
#[serde(skip_serializing_if = "Option::is_none")]
pub index_variable: Option<String>,
pub actions: Vec<Action>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Action {
#[serde(rename = "type")]
pub action_type: String,
#[serde(default)]
pub args: Vec<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Choice {
pub text: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub condition: Option<Condition>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub action: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub choice: Option<Vec<Choice>>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Condition {
#[serde(rename = "type")]
pub condition_type: String,
#[serde(default)]
pub args: Vec<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Function {
pub name: String,
#[serde(default)]
pub params: Vec<Param>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[serde(rename = "return")]
pub return_type: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Param {
pub name: String,
#[serde(rename = "type")]
pub param_type: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Variable {
pub name: String,
#[serde(rename = "type")]
pub var_type: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub value: Option<serde_json::Value>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Constant {
pub name: String,
#[serde(rename = "type")]
pub const_type: String,
pub value: serde_json::Value,
pub public: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Enum {
pub name: String,
pub variants: Vec<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct EventDef {
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub index: Option<f64>,
pub action: Action,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub duration: Option<f64>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TimelineDef {
pub name: String,
pub statements: Vec<TimelineStmt>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TimelineStmt {
#[serde(rename = "type")]
pub stmt_type: String, #[serde(default, skip_serializing_if = "Option::is_none")]
pub event_name: Option<String>,
#[serde(default)]
pub args: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub duration: Option<f64>,
#[serde(default)]
pub ignore_duration: bool,
}
pub struct Deserializer;
impl Deserializer {
pub fn from_json(json_str: &str) -> Result<MortaredData, String> {
serde_json::from_str(json_str).map_err(|e| format!("Deserialization error: {}", e))
}
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<MortaredData, String> {
let path = path.as_ref();
let content = fs::read_to_string(path)
.map_err(|e| format!("Failed to read file {}: {}", path.display(), e))?;
Self::from_json(&content)
}
pub fn from_bytes(bytes: &[u8]) -> Result<MortaredData, String> {
serde_json::from_slice(bytes).map_err(|e| format!("Deserialization error: {}", e))
}
}
impl MortaredData {
pub fn get_node(&self, name: &str) -> Option<&Node> {
self.nodes.iter().find(|n| n.name == name)
}
pub fn get_function(&self, name: &str) -> Option<&Function> {
self.functions.iter().find(|f| f.name == name)
}
pub fn get_variable(&self, name: &str) -> Option<&Variable> {
self.variables.iter().find(|v| v.name == name)
}
pub fn get_constant(&self, name: &str) -> Option<&Constant> {
self.constants.iter().find(|c| c.name == name)
}
pub fn get_enum(&self, name: &str) -> Option<&Enum> {
self.enums.iter().find(|e| e.name == name)
}
pub fn get_event(&self, name: &str) -> Option<&EventDef> {
self.events.iter().find(|e| e.name == name)
}
pub fn get_timeline(&self, name: &str) -> Option<&TimelineDef> {
self.timelines.iter().find(|t| t.name == name)
}
pub fn node_names(&self) -> Vec<&str> {
self.nodes.iter().map(|n| n.name.as_str()).collect()
}
}