use std::{fmt, i32};
use serde::{
de, de::DeserializeOwned, de::Error, Deserialize, Deserializer, Serialize, Serializer,
};
use serde_json::json;
use crate::types::*;
pub(crate) enum VariantJsonId {
Empty = 0,
Boolean,
SByte,
Byte,
Int16,
UInt16,
Int32,
UInt32,
Int64,
UInt64,
Float,
Double,
String,
DateTime,
Guid,
ByteString,
XmlElement,
NodeId,
ExpandedNodeId,
StatusCode,
QualifiedName,
LocalizedText,
ExtensionObject,
DataValue,
Variant,
DiagnosticInfo,
}
#[derive(Serialize, Deserialize)]
struct JsonVariant {
#[serde(rename = "Type")]
variant_type: u32,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "Body")]
body: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "Dimensions")]
dimensions: Option<Vec<u32>>,
}
const VALUE_INFINITY: &str = "Infinity";
const VALUE_NEG_INFINITY: &str = "-Infinity";
const VALUE_NAN: &str = "NaN";
macro_rules! float_to_json {
( $v: expr, $t: ty) => {
if $v == <$t>::INFINITY {
json!(VALUE_INFINITY)
} else if $v == <$t>::NEG_INFINITY {
json!(VALUE_NEG_INFINITY)
} else if $v.is_nan() {
json!(VALUE_NAN)
} else {
json!($v)
}
};
}
macro_rules! serializable_to_json {
( $v: expr ) => {
serde_json::value::to_value($v).unwrap()
};
}
fn json_as_value<T, E>(v: Option<serde_json::Value>, typename: &str) -> Result<T, E>
where
T: DeserializeOwned,
E: Error,
{
if let Some(v) = v {
let v = serde_json::from_value::<T>(v)
.map_err(|_| Error::custom(format!("Invalid value, cannot parse {}", typename)))?;
Ok(v)
} else {
Err(Error::custom(format!(
"Invalid value, cannot parse {}",
typename
)))
}
}
impl Serialize for Variant {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let (body, dimensions) = if *self == Variant::Empty {
(None, None)
} else {
let body = match self {
Variant::Boolean(v) => json!(*v),
Variant::SByte(v) => json!(*v),
Variant::Byte(v) => json!(*v),
Variant::Int16(v) => json!(*v),
Variant::UInt16(v) => json!(*v),
Variant::Int32(v) => json!(*v),
Variant::UInt32(v) => json!(*v),
Variant::Int64(v) => json!(v.to_string()),
Variant::UInt64(v) => json!(v.to_string()),
Variant::Float(v) => float_to_json!(*v, f32),
Variant::Double(v) => float_to_json!(*v, f64),
Variant::String(v) => serializable_to_json!(v),
Variant::XmlElement(v) => serializable_to_json!(v),
Variant::DateTime(v) => serializable_to_json!(v),
Variant::Guid(v) => serializable_to_json!(v),
Variant::ByteString(v) => serializable_to_json!(v),
Variant::NodeId(v) => serializable_to_json!(v),
Variant::ExpandedNodeId(v) => serializable_to_json!(v),
Variant::StatusCode(v) => serializable_to_json!(v),
Variant::QualifiedName(v) => serializable_to_json!(v),
Variant::LocalizedText(v) => serializable_to_json!(v),
Variant::ExtensionObject(v) => serializable_to_json!(v),
Variant::DataValue(v) => serializable_to_json!(v),
Variant::DiagnosticInfo(v) => serializable_to_json!(v),
Variant::Variant(v) => serializable_to_json!(v),
_ => panic!("Unsupported variant type"),
};
(Some(body), None)
};
let json_variant = JsonVariant {
variant_type: self.json_id() as u32,
body,
dimensions,
};
json_variant.serialize(serializer)
}
}
struct VariantVisitor;
impl VariantVisitor {
fn numeric_i64<E>(
v: Option<serde_json::Value>,
name: &str,
min: i64,
max: i64,
) -> Result<i64, E>
where
E: de::Error,
{
if let Some(v) = v {
let v = v
.as_i64()
.ok_or_else(|| Error::custom(format!("Wrong type, expecting {} value", name)))?;
if v < min || v > max {
Err(Error::custom(format!(
"Value {} is out of range for {}",
v, name
)))
} else {
Ok(v)
}
} else {
Ok(0)
}
}
fn numeric_u64<E>(
v: Option<serde_json::Value>,
name: &str,
min: u64,
max: u64,
) -> Result<u64, E>
where
E: de::Error,
{
if let Some(v) = v {
let v = v
.as_u64()
.ok_or_else(|| Error::custom(format!("Wrong type, expecting {} value", name)))?;
if v < min || v > max {
Err(Error::custom(format!(
"Value {} is out of range for {}",
v, name
)))
} else {
Ok(v)
}
} else {
Ok(0)
}
}
fn numeric_f64<E>(
v: Option<serde_json::Value>,
name: &str,
min: f64,
max: f64,
) -> Result<f64, E>
where
E: de::Error,
{
if let Some(v) = v {
if let Some(v) = v.as_str() {
match v {
VALUE_INFINITY => Ok(f64::INFINITY),
VALUE_NEG_INFINITY => Ok(f64::NEG_INFINITY),
VALUE_NAN => Ok(f64::NAN),
_ => Err(Error::custom(format!(
"Wrong type, expecting {} value",
name
))),
}
} else {
let v = v.as_f64().ok_or_else(|| {
Error::custom(format!("Wrong type, expecting {} value", name))
})?;
if v < min || v > max {
Err(Error::custom(format!(
"Value {} is out of range for {}",
v, name
)))
} else {
Ok(v)
}
}
} else {
Ok(0.0)
}
}
}
impl<'de> serde::de::Visitor<'de> for VariantVisitor {
type Value = Variant;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a variant value or null")
}
fn visit_none<E>(self) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Variant::Empty)
}
fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
let v = JsonVariant::deserialize(deserializer)?;
let t = v.variant_type;
let body = v.body;
let dimensions = v.dimensions;
if dimensions.is_some() {
return Err(Error::custom("Dimensions not supported yet"));
}
match t {
t if t == VariantJsonId::Empty as u32 => {
if body.is_some() {
Err(Error::custom("Unexpected Body"))
} else {
Ok(Variant::Empty)
}
}
t if t == VariantJsonId::Boolean as u32 => {
let v = body.ok_or_else(|| Error::custom("Missing Boolean value"))?;
Ok(Variant::Boolean(
v.as_bool()
.ok_or_else(|| Error::custom("Value is not Boolean"))?,
))
}
t if t == VariantJsonId::SByte as u32 => {
Ok(Variant::SByte(
Self::numeric_i64(body, "SByte", i8::MIN as i64, i8::MAX as i64)? as i8,
))
}
t if t == VariantJsonId::Byte as u32 => {
Ok(Variant::Byte(
Self::numeric_u64(body, "Byte", u8::MIN as u64, u8::MAX as u64)? as u8,
))
}
t if t == VariantJsonId::Int16 as u32 => Ok(Variant::Int16(Self::numeric_i64(
body,
"Int16",
i16::MIN as i64,
i16::MAX as i64,
)? as i16)),
t if t == VariantJsonId::UInt16 as u32 => Ok(Variant::UInt16(Self::numeric_u64(
body,
"UInt16",
u16::MIN as u64,
u16::MAX as u64,
)? as u16)),
t if t == VariantJsonId::Int32 as u32 => Ok(Variant::Int32(Self::numeric_i64(
body,
"Int32",
i32::MIN as i64,
i32::MAX as i64,
)? as i32)),
t if t == VariantJsonId::UInt32 as u32 => Ok(Variant::UInt32(Self::numeric_u64(
body,
"UInt32",
u32::MIN as u64,
u32::MAX as u64,
)? as u32)),
t if t == VariantJsonId::Int64 as u32 => {
let v = if let Some(v) = body {
const ERR: &str = "Int64 encoded as a string";
let v = v.as_str().ok_or_else(|| {
Error::custom(format!("Wrong type, expecting {} value", ERR))
})?;
v.parse::<i64>().map_err(|_| {
Error::custom(format!("Parse error, expecting {} value", ERR))
})?
} else {
0i64
};
Ok(Variant::Int64(v))
}
t if t == VariantJsonId::UInt64 as u32 => {
let v = if let Some(v) = body {
const ERR: &str = "UInt64 encoded as a string";
let v = v.as_str().ok_or_else(|| {
Error::custom(format!("Wrong type, expecting {} value", ERR))
})?;
v.parse::<u64>().map_err(|_| {
Error::custom(format!("Parse error, expecting {} value", ERR))
})?
} else {
0u64
};
Ok(Variant::UInt64(v))
}
t if t == VariantJsonId::Float as u32 => Ok(Variant::Float(Self::numeric_f64(
body,
"Float",
f32::MIN as f64,
f32::MAX as f64,
)? as f32)),
t if t == VariantJsonId::Double as u32 => Ok(Variant::Double(Self::numeric_f64(
body,
"Double",
f64::MIN,
f64::MAX,
)?)),
t if t == VariantJsonId::String as u32 => {
if let Some(ref v) = body {
if v.is_null() {
Ok(Variant::String(UAString::null()))
} else {
json_as_value(body, "UAString").map(|v| Variant::String(v))
}
} else {
Ok(Variant::String(UAString::null()))
}
}
t if t == VariantJsonId::DateTime as u32 => {
json_as_value(body, "DateTime").map(|v| Variant::DateTime(Box::new(v)))
}
t if t == VariantJsonId::Guid as u32 => {
json_as_value(body, "Guid").map(|v| Variant::Guid(Box::new(v)))
}
t if t == VariantJsonId::ByteString as u32 => {
if let Some(ref v) = body {
if v.is_null() {
Ok(Variant::ByteString(ByteString::null()))
} else {
json_as_value(body, "ByteString").map(|v| Variant::ByteString(v))
}
} else {
Ok(Variant::ByteString(ByteString::null()))
}
}
t if t == VariantJsonId::XmlElement as u32 => {
json_as_value(body, "XmlElement").map(|v| Variant::XmlElement(v))
}
t if t == VariantJsonId::NodeId as u32 => {
json_as_value(body, "NodeId").map(|v| Variant::NodeId(Box::new(v)))
}
t if t == VariantJsonId::ExpandedNodeId as u32 => {
json_as_value(body, "ExpandedNodeId").map(|v| Variant::ExpandedNodeId(Box::new(v)))
}
t if t == VariantJsonId::StatusCode as u32 => {
if let Some(v) = body {
let v = serde_json::from_value::<u32>(v)
.map_err(|_| Error::custom("Invalid value, cannot parse StatusCode"))?;
Ok(Variant::StatusCode(StatusCode::from_bits_truncate(v)))
} else {
Err(Error::custom("Invalid value, cannot parse StatusCode"))
}
}
t if t == VariantJsonId::QualifiedName as u32 => {
json_as_value(body, "QualifiedName").map(|v| Variant::QualifiedName(Box::new(v)))
}
t if t == VariantJsonId::LocalizedText as u32 => {
json_as_value(body, "LocalizedText").map(|v| Variant::LocalizedText(Box::new(v)))
}
t if t == VariantJsonId::ExtensionObject as u32 => {
json_as_value(body, "ExtensionObject")
.map(|v| Variant::ExtensionObject(Box::new(v)))
}
t if t == VariantJsonId::DataValue as u32 => {
json_as_value(body, "DataValue").map(|v| Variant::DataValue(Box::new(v)))
}
t if t == VariantJsonId::Variant as u32 => {
json_as_value(body, "Variant").map(|v| Variant::Variant(Box::new(v)))
}
t if t == VariantJsonId::DiagnosticInfo as u32 => {
json_as_value(body, "DiagnosticInfo").map(|v| Variant::DiagnosticInfo(Box::new(v)))
}
t => Err(Error::custom(format!("Unhandled type {}", t))),
}
}
}
impl<'de> Deserialize<'de> for Variant {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_option(VariantVisitor)
}
}
impl Variant {
fn json_id(&self) -> VariantJsonId {
match self {
Variant::Empty => VariantJsonId::Empty,
Variant::Boolean(_) => VariantJsonId::Boolean,
Variant::SByte(_) => VariantJsonId::SByte,
Variant::Byte(_) => VariantJsonId::Byte,
Variant::Int16(_) => VariantJsonId::Int16,
Variant::UInt16(_) => VariantJsonId::UInt16,
Variant::Int32(_) => VariantJsonId::Int32,
Variant::UInt32(_) => VariantJsonId::UInt32,
Variant::Int64(_) => VariantJsonId::Int64,
Variant::UInt64(_) => VariantJsonId::UInt64,
Variant::Float(_) => VariantJsonId::Float,
Variant::Double(_) => VariantJsonId::Double,
Variant::String(_) => VariantJsonId::String,
Variant::DateTime(_) => VariantJsonId::DateTime,
Variant::Guid(_) => VariantJsonId::Guid,
Variant::ByteString(_) => VariantJsonId::ByteString,
Variant::XmlElement(_) => VariantJsonId::XmlElement,
Variant::NodeId(_) => VariantJsonId::NodeId,
Variant::ExpandedNodeId(_) => VariantJsonId::ExpandedNodeId,
Variant::StatusCode(_) => VariantJsonId::StatusCode,
Variant::QualifiedName(_) => VariantJsonId::QualifiedName,
Variant::LocalizedText(_) => VariantJsonId::LocalizedText,
Variant::ExtensionObject(_) => VariantJsonId::ExtensionObject,
Variant::Variant(_) => VariantJsonId::Variant,
Variant::DataValue(_) => VariantJsonId::DataValue,
Variant::DiagnosticInfo(_) => VariantJsonId::DiagnosticInfo,
_ => {
panic!("Cannot return type")
}
}
}
}