use std::{
error::Error,
fmt,
io::{Cursor, Read, Write},
};
use super::{
byte_string::ByteString, encoding::*, node_id::NodeId, node_ids::ObjectId,
status_codes::StatusCode, string::XmlElement,
};
#[derive(Debug)]
pub struct ExtensionObjectError;
impl fmt::Display for ExtensionObjectError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ExtensionObjectError")
}
}
impl Error for ExtensionObjectError {}
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
pub enum ExtensionObjectEncoding {
None,
ByteString(ByteString),
XmlElement(XmlElement),
}
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ExtensionObject {
pub node_id: NodeId,
pub body: ExtensionObjectEncoding,
}
impl Default for ExtensionObject {
fn default() -> Self {
Self::null()
}
}
impl BinaryEncoder<ExtensionObject> for ExtensionObject {
fn byte_len(&self) -> usize {
let mut size = self.node_id.byte_len();
size += match self.body {
ExtensionObjectEncoding::None => 1,
ExtensionObjectEncoding::ByteString(ref value) => {
1 + value.byte_len()
}
ExtensionObjectEncoding::XmlElement(ref value) => {
1 + value.byte_len()
}
};
size
}
fn encode<S: Write>(&self, stream: &mut S) -> EncodingResult<usize> {
let mut size = 0;
size += self.node_id.encode(stream)?;
match self.body {
ExtensionObjectEncoding::None => {
size += write_u8(stream, 0x0)?;
}
ExtensionObjectEncoding::ByteString(ref value) => {
size += write_u8(stream, 0x1)?;
size += value.encode(stream)?;
}
ExtensionObjectEncoding::XmlElement(ref value) => {
size += write_u8(stream, 0x2)?;
size += value.encode(stream)?;
}
}
assert_eq!(size, self.byte_len());
Ok(size)
}
fn decode<S: Read>(stream: &mut S, decoding_options: &DecodingOptions) -> EncodingResult<Self> {
let _depth_lock = decoding_options.depth_lock()?;
let node_id = NodeId::decode(stream, decoding_options)?;
let encoding_type = u8::decode(stream, decoding_options)?;
let body = match encoding_type {
0x0 => ExtensionObjectEncoding::None,
0x1 => {
ExtensionObjectEncoding::ByteString(ByteString::decode(stream, decoding_options)?)
}
0x2 => {
ExtensionObjectEncoding::XmlElement(XmlElement::decode(stream, decoding_options)?)
}
_ => {
error!("Invalid encoding type {} in stream", encoding_type);
return Err(StatusCode::BadDecodingError);
}
};
Ok(ExtensionObject { node_id, body })
}
}
impl ExtensionObject {
pub fn null() -> ExtensionObject {
ExtensionObject {
node_id: NodeId::null(),
body: ExtensionObjectEncoding::None,
}
}
pub fn is_null(&self) -> bool {
self.node_id.is_null()
}
pub fn is_empty(&self) -> bool {
self.is_null() || matches!(self.body, ExtensionObjectEncoding::None)
}
pub fn object_id(&self) -> Result<ObjectId, ExtensionObjectError> {
self.node_id
.as_object_id()
.map_err(|_| ExtensionObjectError)
}
pub fn from_encodable<N, T>(node_id: N, encodable: &T) -> ExtensionObject
where
N: Into<NodeId>,
T: BinaryEncoder<T>,
{
let mut stream = Cursor::new(vec![0u8; encodable.byte_len()]);
let _ = encodable.encode(&mut stream);
ExtensionObject {
node_id: node_id.into(),
body: ExtensionObjectEncoding::ByteString(ByteString::from(stream.into_inner())),
}
}
pub fn decode_inner<T>(&self, decoding_options: &DecodingOptions) -> EncodingResult<T>
where
T: BinaryEncoder<T>,
{
match self.body {
ExtensionObjectEncoding::ByteString(ref byte_string) => {
if let Some(ref value) = byte_string.value {
let mut stream = Cursor::new(value);
T::decode(&mut stream, decoding_options)
} else {
Err(StatusCode::BadDecodingError)
}
}
_ => {
error!("decode_inner called on an unsupported ExtensionObject type");
Err(StatusCode::BadDecodingError)
}
}
}
}