use super::PluginCategory;
use derive_builder::Builder;
use indexmap::IndexSet;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Builder, Deserialize, Serialize)]
#[builder(build_fn(validate = "Self::validate"))]
#[serde(rename_all = "camelCase")]
pub struct Event {
#[builder(setter(into))]
pub(crate) id: String,
#[builder(setter(into))]
pub(crate) name: String,
#[builder(setter(into))]
pub(crate) format: String,
#[builder(setter(skip), default)]
#[serde(rename = "type")]
_type: EventType,
#[serde(flatten)]
pub(crate) value: EventValueType,
#[builder(setter(into), default)]
pub(crate) value_state_id: String,
#[builder(setter(into, strip_option), default)]
#[serde(skip_serializing_if = "Option::is_none")]
sub_category_id: Option<String>,
#[serde(rename = "localstates")]
#[serde(skip_serializing_if = "Vec::is_empty")]
#[builder(setter(each(name = "local_state")), default)]
pub(crate) local_states: Vec<LocalState>,
}
impl Event {
pub fn builder() -> EventBuilder {
EventBuilder::default()
}
}
impl EventBuilder {
fn validate(&self) -> Result<(), String> {
let fmt = self.format.as_ref().expect("format is required");
if fmt.contains("$compare") && !fmt.contains("$val") {
return Err(format!(
"format for event {} has $compare but no $val",
self.id.as_ref().expect("id is required")
));
}
Ok(())
}
}
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
#[non_exhaustive]
#[serde(rename_all = "lowercase")]
enum EventType {
#[default]
Communicate,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[non_exhaustive]
#[serde(rename_all = "lowercase")]
#[serde(tag = "valueType")]
pub enum EventValueType {
Choice(EventChoiceValue),
Text(EventTextConfiguration),
}
#[derive(Debug, Clone, Builder, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct EventChoiceValue {
#[builder(setter(each(name = "choice", into)))]
#[serde(rename = "valueChoices")]
pub(crate) choices: IndexSet<String>,
}
impl EventChoiceValue {
pub fn builder() -> EventChoiceValueBuilder {
EventChoiceValueBuilder::default()
}
}
#[derive(Debug, Clone, Builder, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct EventTextConfiguration {
#[builder(setter(strip_option), default)]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "compareOptions")]
pub(crate) compare_with: Option<CompareMethod>,
}
impl EventTextConfiguration {
pub fn builder() -> EventTextConfigurationBuilder {
EventTextConfigurationBuilder::default()
}
}
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
pub enum CompareMethod {
#[serde(rename = "none")]
#[default]
StringEq,
#[serde(rename = "choice")]
IsOrIsNot,
#[serde(rename = "string")]
ExtendedString,
#[serde(rename = "number")]
Number,
}
#[derive(Debug, Clone, Builder, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LocalState {
#[builder(setter(into))]
pub(crate) id: String,
#[builder(setter(into))]
pub(crate) name: String,
#[builder(setter(strip_option), default)]
#[serde(skip_serializing_if = "Option::is_none")]
parent_category: Option<PluginCategory>,
}
impl LocalState {
pub fn builder() -> LocalStateBuilder {
LocalStateBuilder::default()
}
}
#[test]
fn serialize_example_event() {
assert_eq!(
serde_json::to_value(
Event::builder()
.id("event002")
.name("On breakfast eating")
.format("When we eat $val as breakfast")
.value(EventValueType::Choice(
EventChoiceValue::builder()
.choice("Apple")
.choice("Pears")
.choice("Grapes")
.choice("Bananas")
.build()
.unwrap()
))
.value_state_id("fruit")
.build()
.unwrap()
)
.unwrap(),
serde_json::json! {{
"id":"event002",
"name":"On breakfast eating",
"format":"When we eat $val as breakfast",
"type":"communicate",
"valueType":"choice",
"valueChoices": [
"Apple",
"Pears",
"Grapes",
"Bananas",
],
"valueStateId":"fruit"
}}
);
}