#![allow(dead_code)]
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
pub const QT_SCJSON_SUBSET_VERSION: u32 = 1;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Scxml {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub state: Vec<State>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub datamodel: Vec<Datamodel>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub script: Vec<Script>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub initial: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub version: Option<Value>,
#[serde(
default = "default_datamodel_attribute",
rename = "datamodel_attribute"
)]
pub datamodel_attribute: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub other_element: Vec<Value>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub other_attributes: Map<String, Value>,
}
fn default_datamodel_attribute() -> String {
"null".to_string()
}
impl Default for Scxml {
fn default() -> Self {
Self {
state: Vec::new(),
datamodel: Vec::new(),
script: Vec::new(),
initial: Vec::new(),
name: None,
version: None,
datamodel_attribute: default_datamodel_attribute(),
other_element: Vec::new(),
other_attributes: Map::new(),
}
}
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct State {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub onentry: Vec<Onentry>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub onexit: Vec<Onexit>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub transition: Vec<Transition>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub state: Vec<State>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub datamodel: Vec<Datamodel>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub initial: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub other_element: Vec<Value>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub other_attributes: Map<String, Value>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct Transition {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub event: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cond: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub target: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty", rename = "raise")]
pub raise_value: Vec<Raise>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub assign: Vec<Assign>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub script: Vec<Script>,
#[serde(default, skip_serializing_if = "Option::is_none", rename = "type")]
pub type_value: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub other_element: Vec<Value>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub other_attributes: Map<String, Value>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct Datamodel {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub data: Vec<Data>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub other_element: Vec<Value>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub other_attributes: Map<String, Value>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct Data {
#[serde(default)]
pub id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub src: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub expr: Option<String>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub other_attributes: Map<String, Value>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub content: Vec<Value>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct Onentry {
#[serde(default, skip_serializing_if = "Vec::is_empty", rename = "raise")]
pub raise_value: Vec<Raise>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub assign: Vec<Assign>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub script: Vec<Script>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub other_element: Vec<Value>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub other_attributes: Map<String, Value>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct Onexit {
#[serde(default, skip_serializing_if = "Vec::is_empty", rename = "raise")]
pub raise_value: Vec<Raise>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub assign: Vec<Assign>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub script: Vec<Script>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub other_element: Vec<Value>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub other_attributes: Map<String, Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Assign {
#[serde(default)]
pub location: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub expr: Option<String>,
#[serde(default = "default_assign_type", rename = "type")]
pub type_value: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub attr: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub content: Vec<Value>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub other_attributes: Map<String, Value>,
}
fn default_assign_type() -> String {
"replacechildren".to_string()
}
impl Default for Assign {
fn default() -> Self {
Self {
location: String::new(),
expr: None,
type_value: default_assign_type(),
attr: None,
content: Vec::new(),
other_attributes: Map::new(),
}
}
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct Raise {
#[serde(default)]
pub event: String,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub other_attributes: Map<String, Value>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct Script {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub src: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub content: Vec<Value>,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub other_attributes: Map<String, Value>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn scxml_roundtrip_minimal() {
let doc = Scxml {
state: vec![State {
id: Some("idle".to_string()),
transition: vec![Transition {
event: Some("go".to_string()),
target: vec!["running".to_string()],
..Default::default()
}],
..Default::default()
}],
initial: vec!["idle".to_string()],
..Default::default()
};
let s = serde_json::to_string(&doc).expect("serialize");
let back: Scxml = serde_json::from_str(&s).expect("deserialize");
assert_eq!(doc, back);
}
#[test]
fn raise_and_type_renames_render_as_canonical_scxml_keys() {
let onentry = Onentry {
raise_value: vec![Raise {
event: "tick".to_string(),
..Default::default()
}],
assign: vec![Assign {
location: "count".to_string(),
expr: Some("count + 1".to_string()),
type_value: "replacechildren".to_string(),
..Default::default()
}],
..Default::default()
};
let s = serde_json::to_string(&onentry).expect("serialize");
assert!(s.contains(r#""raise""#), "expected `raise` key, got: {s}");
assert!(
!s.contains(r#""raise_value""#),
"expected NO `raise_value` key, got: {s}"
);
assert!(
s.contains(r#""type":"replacechildren""#),
"expected `\"type\":\"replacechildren\"`, got: {s}"
);
}
#[test]
fn deferred_elements_pass_through_unchanged() {
let raw = r#"{
"state": [{"id": "s1"}],
"other_element": [{"parallel": [{"id": "p1"}]}]
}"#;
let doc: Scxml = serde_json::from_str(raw).expect("parse");
assert_eq!(doc.state.len(), 1);
assert_eq!(doc.other_element.len(), 1);
let s = serde_json::to_string(&doc).expect("re-serialize");
assert!(s.contains(r#""parallel""#));
}
#[test]
fn datamodel_attribute_default_and_override() {
let default_doc = Scxml::default();
let s = serde_json::to_string(&default_doc).expect("serialize");
assert!(s.contains(r#""datamodel_attribute":"null""#));
let custom = Scxml {
datamodel_attribute: "ecmascript".to_string(),
..Default::default()
};
let s = serde_json::to_string(&custom).expect("serialize");
assert!(s.contains(r#""datamodel_attribute":"ecmascript""#));
}
}