use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use crate::*;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(try_from = "LocutorSerial", into = "LocutorSerial")]
pub struct Locutor {
pub entity: Entity,
pub outlet: Entity,
pub auxiliary: Entity,
pub ancillary: Entity,
pub context: Context,
pub category: Category,
pub class: Class,
pub method: Method,
pub attribute: Attribute,
pub instance: i32,
pub offset: i32,
pub parameter: i64,
pub resultant: i64,
pub count: i64,
pub index: i64,
pub event: Event,
pub mode: Mode,
pub state: State,
pub condition: Condition,
pub precedence: u16,
pub time: OffsetDateTime,
pub timeout: i64,
pub aspect: Aspect,
pub template: Template,
pub scheme: Scheme,
pub name: String255,
pub label: String,
pub key: String255,
pub value: Value,
pub format: Format,
pub authority: Token,
pub authorization: Token,
}
impl Default for Locutor {
fn default() -> Self {
Locutor {
entity: Entity::NULL,
outlet: Entity::NULL,
auxiliary: Entity::NULL,
ancillary: Entity::NULL,
context: Context::Null,
category: Category::Null,
class: Class::Null,
method: Method::Null,
attribute: Attribute::Null,
instance: 0,
offset: 0,
parameter: 0,
resultant: 0,
count: 0,
index: 0,
event: Event::Null,
mode: Mode::Null,
state: State::Null,
condition: Condition::Null,
precedence: 0,
time: OffsetDateTime::UNIX_EPOCH,
timeout: 0,
aspect: Aspect::Null,
template: Template::Null,
scheme: Scheme::Null,
name: String255::NULL,
label: String::new(),
key: String255::NULL,
value: Value::NULL,
format: Format::Null,
authority: Token::NULL,
authorization: Token::NULL,
}
}
}
impl Locutor {
#[rustfmt::skip]
pub fn merge(&self, other: &Self) -> Self {
Locutor {
entity: if self.entity.is_null() { other.entity } else { self.entity },
outlet: if self.outlet.is_null() { other.outlet } else { self.outlet },
auxiliary: if self.auxiliary.is_null() { other.auxiliary } else { self.auxiliary },
ancillary: if self.ancillary.is_null() { other.ancillary } else { self.ancillary },
context: if self.context.is_null() { other.context } else { self.context },
category: if self.category.is_null() { other.category } else { self.category },
class: if self.class.is_null() { other.class } else { self.class },
method: if self.method.is_null() { other.method } else { self.method },
attribute: if self.attribute.is_null() { other.attribute } else { self.attribute },
instance: if self.instance == 0 { other.instance } else { self.instance },
offset: if self.offset == 0 { other.offset } else { self.offset },
parameter: if self.parameter == 0 { other.parameter } else { self.parameter },
resultant: if self.resultant == 0 { other.resultant } else { self.resultant },
count: if self.count == 0 { other.count } else { self.count },
index: if self.index == 0 { other.index } else { self.index },
event: if self.event.is_null() { other.event } else { self.event },
mode: if self.mode.is_null() { other.mode } else { self.mode },
state: if self.state.is_null() { other.state } else { self.state },
condition: if self.condition.is_null() { other.condition } else { self.condition },
precedence: if self.precedence == 0 { other.precedence } else { self.precedence },
time: if self.time == OffsetDateTime::UNIX_EPOCH { other.time } else { self.time },
timeout: if self.timeout == 0 { other.timeout } else { self.timeout },
aspect: if self.aspect.is_null() { other.aspect } else { self.aspect },
template: if self.template.is_null() { other.template } else { self.template },
scheme: if self.scheme.is_null() { other.scheme } else { self.scheme },
name: if self.name.is_null() { other.name.clone() } else { self.name.clone() },
label: if self.label.is_empty() { other.label.clone() } else { self.label.clone() },
key: if self.key.is_null() { other.key.clone() } else { self.key.clone() },
value: if self.value.is_null() { other.value.clone() } else { self.value.clone() },
format: if self.format.is_null() { other.format } else { self.format },
authority: if self.authority.is_null() { other.authority } else { self.authority },
authorization: if self.authorization.is_null() { other.authorization } else { self.authorization },
}
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(default, rename_all = "UPPERCASE")]
struct LocutorSerial {
#[serde(skip_serializing_if = "Entity::is_null")]
entity: Entity,
#[serde(skip_serializing_if = "Entity::is_null")]
outlet: Entity,
#[serde(skip_serializing_if = "Entity::is_null")]
auxiliary: Entity,
#[serde(skip_serializing_if = "Entity::is_null")]
ancillary: Entity,
#[serde(skip_serializing_if = "Context::is_null")]
context: Context,
#[serde(skip_serializing_if = "Category::is_null")]
category: Category,
#[serde(skip_serializing_if = "Class::is_null")]
class: Class,
#[serde(skip_serializing_if = "Method::is_null")]
method: Method,
#[serde(skip_serializing_if = "Attribute::is_null")]
attribute: Attribute,
#[serde(skip_serializing_if = "i32_is_zero")]
instance: i32,
#[serde(skip_serializing_if = "i32_is_zero")]
offset: i32,
#[serde(skip_serializing_if = "i64_is_zero")]
parameter: i64,
#[serde(skip_serializing_if = "i64_is_zero")]
resultant: i64,
#[serde(skip_serializing_if = "i64_is_zero")]
count: i64,
#[serde(skip_serializing_if = "i64_is_zero")]
index: i64,
#[serde(skip_serializing_if = "Event::is_null")]
event: Event,
#[serde(skip_serializing_if = "Mode::is_null")]
mode: Mode,
#[serde(skip_serializing_if = "State::is_null")]
state: State,
#[serde(skip_serializing_if = "Condition::is_null")]
condition: Condition,
#[serde(skip_serializing_if = "u16_is_zero", with = "locutor_precedence_serde")]
precedence: u16,
#[serde(skip_serializing_if = "time_serde::is_zero", with = "time_serde")]
time: OffsetDateTime,
#[serde(skip_serializing_if = "i64_is_zero")]
timeout: i64,
#[serde(skip_serializing_if = "Aspect::is_null")]
aspect: Aspect,
#[serde(skip_serializing_if = "Template::is_null")]
template: Template,
#[serde(skip_serializing_if = "Scheme::is_null")]
scheme: Scheme,
#[serde(skip_serializing_if = "String::is_empty")]
name: String255,
#[serde(skip_serializing_if = "String::is_empty")]
label: String,
#[serde(skip_serializing_if = "String::is_empty")]
key: String255,
#[serde(skip_serializing_if = "String::is_empty")]
value: String,
#[serde(skip_serializing_if = "Tag::is_null")]
value_tag: Tag,
#[serde(skip_serializing_if = "Format::is_null")]
format: Format,
#[serde(skip_serializing_if = "Token::is_null")]
authority: Token,
#[serde(skip_serializing_if = "Token::is_null")]
authorization: Token,
}
fn i32_is_zero(n: &i32) -> bool {
*n == 0
}
fn i64_is_zero(n: &i64) -> bool {
*n == 0
}
fn u16_is_zero(n: &u16) -> bool {
*n == 0
}
impl From<Locutor> for LocutorSerial {
fn from(locutor: Locutor) -> Self {
LocutorSerial {
entity: locutor.entity,
outlet: locutor.outlet,
auxiliary: locutor.auxiliary,
ancillary: locutor.ancillary,
context: locutor.context,
category: locutor.category,
class: locutor.class,
method: locutor.method,
attribute: locutor.attribute,
instance: locutor.instance,
offset: locutor.offset,
parameter: locutor.parameter,
resultant: locutor.resultant,
count: locutor.count,
index: locutor.index,
event: locutor.event,
mode: locutor.mode,
state: locutor.state,
condition: locutor.condition,
precedence: locutor.precedence,
time: locutor.time,
timeout: locutor.timeout,
aspect: locutor.aspect,
template: locutor.template,
scheme: locutor.scheme,
name: locutor.name,
label: locutor.label,
key: locutor.key,
value: locutor.value.get_bytes(),
value_tag: locutor.value.get_tag(),
format: locutor.format,
authority: locutor.authority,
authorization: locutor.authorization,
}
}
}
impl TryFrom<LocutorSerial> for Locutor {
type Error = ValueCreationError;
fn try_from(locutor: LocutorSerial) -> Result<Self, Self::Error> {
Ok(Locutor {
entity: locutor.entity,
outlet: locutor.outlet,
auxiliary: locutor.auxiliary,
ancillary: locutor.ancillary,
context: locutor.context,
category: locutor.category,
class: locutor.class,
method: locutor.method,
attribute: locutor.attribute,
instance: locutor.instance,
offset: locutor.offset,
parameter: locutor.parameter,
resultant: locutor.resultant,
count: locutor.count,
index: locutor.index,
event: locutor.event,
mode: locutor.mode,
state: locutor.state,
condition: locutor.condition,
precedence: locutor.precedence,
time: locutor.time,
timeout: locutor.timeout,
aspect: locutor.aspect,
template: locutor.template,
scheme: locutor.scheme,
name: locutor.name,
label: locutor.label,
key: locutor.key,
value: Value::new(locutor.value_tag, locutor.value)?,
format: locutor.format,
authority: locutor.authority,
authorization: locutor.authorization,
})
}
}
impl Default for LocutorSerial {
fn default() -> Self {
Locutor::default().into()
}
}
mod locutor_precedence_serde {
use serde::de;
use serde::ser;
pub fn serialize<S>(precedence: &u16, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
serializer.serialize_str(&precedence.to_string())
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<u16, D::Error>
where
D: de::Deserializer<'de>,
{
match serde::Deserialize::deserialize(deserializer)? {
serde_json::Value::Number(n) => n
.as_u64()
.ok_or_else(|| de::Error::custom("Invalid number"))
.map(|n| n as u16),
serde_json::Value::String(s) => s
.trim()
.parse()
.map_err(|_| de::Error::custom("Invalid string"))
.map(|n: u16| n),
_ => Err(de::Error::custom("Invalid value")),
}
}
}
#[cfg(test)]
mod test {
use std::str::FromStr;
use super::*;
use ascii::AsciiString;
use serde_json::json;
#[test]
fn example_json() {
let json = json! (
{
"ENTITY": "<0|0|24>",
"OUTLET": "<0|0|11>",
"AUXILIARY": "<0|0|1>",
"ANCILLARY": "<0|0|2>",
"CONTEXT": "AVESTERRA_CONTEXT",
"CATEGORY": "AVESTERRA_CATEGORY",
"CLASS": "AVESTERRA_CLASS",
"METHOD": "AVESTERRA_METHOD",
"ATTRIBUTE": "AVESTERRA_ATTRIBUTE",
"INSTANCE": 1,
"NAME": "Example Name",
"VALUE": "Example String",
"VALUE_TAG": "STRING_TAG",
"INDEX": 1,
"COUNT": 123,
"PRECEDENCE": " 8",
"PARAMETER": -1,
"MODE": "AVESTERRA_MODE",
"EVENT": "AVESTERRA_EVENT",
"TIMEOUT": 60,
"ASPECT": "AVESTERRA_ASPECT",
"AUTHORITY": "********-****-****-****-************"
}
);
assert_eq!(
serde_json::from_value::<Locutor>(json).unwrap(),
Locutor {
entity: Entity::new(0, 0, 24),
outlet: Entity::new(0, 0, 11),
auxiliary: Entity::new(0, 0, 1),
ancillary: Entity::new(0, 0, 2),
context: Context::Avesterra,
category: Category::Avesterra,
class: Class::Avesterra,
method: Method::Avesterra,
attribute: Attribute::Avesterra,
instance: 1,
offset: 0,
parameter: -1,
resultant: 0,
count: 123,
index: 1,
event: Event::Avesterra,
mode: Mode::Avesterra,
state: State::Null,
condition: Condition::Null,
precedence: 8,
time: OffsetDateTime::from_unix_timestamp(0).unwrap(),
timeout: 60,
aspect: Aspect::Avesterra,
template: Template::Null,
scheme: Scheme::Null,
name: String255::unchecked("Example Name"),
label: String::new(),
key: String255::NULL,
value: Value::String(AsciiString::from_str("Example String").unwrap()),
format: Format::Null,
authority: Token::from_str("********-****-****-****-************").unwrap(),
authorization: Token::NULL,
}
)
}
#[test]
fn empty() {
let json = json!({});
assert_eq!(
serde_json::from_value::<Locutor>(json).unwrap(),
Locutor {
..Default::default()
}
)
}
#[test]
fn precedence_as_string() {
let json = json! (
{
"PRECEDENCE": " 983 ",
}
);
assert_eq!(
serde_json::from_value::<Locutor>(json).unwrap(),
Locutor {
precedence: 983,
..Default::default()
}
)
}
#[test]
fn precedence_as_number() {
let json = json! (
{
"PRECEDENCE": 123,
}
);
assert_eq!(
serde_json::from_value::<Locutor>(json).unwrap(),
Locutor {
precedence: 123,
..Default::default()
}
)
}
#[test]
fn value_null() {
let json = json!({
"VALUE": "2398",
"VALUE_TAG": "INTEGER_TAG",
});
assert_eq!(
serde_json::from_value::<Locutor>(json).unwrap(),
Locutor {
value: Value::Integer(2398),
..Default::default()
}
)
}
#[test]
fn serialize_empty() {
let v: serde_json::Value = serde_json::to_value(Locutor::default()).unwrap();
assert_eq!(v, json!({}))
}
#[test]
fn serialize_one_field() {
let loc = Locutor {
entity: Entity::new(0, 0, 24),
..Default::default()
};
let v: serde_json::Value = serde_json::to_value(loc).unwrap();
assert_eq!(v, json!({"ENTITY": "<0|0|24>"}))
}
#[test]
fn negative_values() {
let json = json!({
"TIMEOUT": -1,
"INDEX": -2,
"COUNT": -3,
"RESULTANT": -4,
"PARAMETER": -5,
"OFFSET": -6,
"INSTANCE": -7,
});
let loc = serde_json::from_value::<Locutor>(json).unwrap();
assert_eq!(
loc,
Locutor {
timeout: -1,
index: -2,
count: -3,
resultant: -4,
parameter: -5,
offset: -6,
instance: -7,
..Default::default()
}
);
assert_eq!(
serde_json::to_value(loc).unwrap(),
json!({
"TIMEOUT": -1,
"INDEX": -2,
"COUNT": -3,
"RESULTANT": -4,
"PARAMETER": -5,
"OFFSET": -6,
"INSTANCE": -7,
})
);
}
}