mod fallback;
pub use fallback::{ByteStringBody, FallbackTypeLoader, JsonBody, XmlBody};
use std::{borrow::Cow, io::Read, sync::Arc};
use chrono::TimeDelta;
use hashbrown::HashMap;
use crate::{
BinaryDecodable, DecodingOptions, DynEncodable, EncodingResult, Error, GeneratedTypeLoader,
NamespaceMap, NodeId, UninitializedIndex,
};
type BinaryLoadFun = fn(&mut dyn Read, &Context<'_>) -> EncodingResult<Box<dyn DynEncodable>>;
#[cfg(feature = "xml")]
type XmlLoadFun = fn(
&mut crate::xml::XmlStreamReader<&mut dyn std::io::Read>,
&Context<'_>,
) -> EncodingResult<Box<dyn DynEncodable>>;
#[cfg(feature = "json")]
type JsonLoadFun = fn(
&mut crate::json::JsonStreamReader<&mut dyn std::io::Read>,
&Context<'_>,
) -> EncodingResult<Box<dyn DynEncodable>>;
#[derive(Default)]
pub struct TypeLoaderInstance {
binary_types: HashMap<u32, BinaryLoadFun>,
#[cfg(feature = "xml")]
xml_types: HashMap<u32, XmlLoadFun>,
#[cfg(feature = "json")]
json_types: HashMap<u32, JsonLoadFun>,
}
pub fn binary_decode_to_enc<T: DynEncodable + BinaryDecodable>(
stream: &mut dyn Read,
ctx: &Context<'_>,
) -> EncodingResult<Box<dyn DynEncodable>> {
Ok(Box::new(T::decode(stream, ctx)?))
}
#[cfg(feature = "json")]
pub fn json_decode_to_enc<T: DynEncodable + crate::json::JsonDecodable>(
stream: &mut crate::json::JsonStreamReader<&mut dyn std::io::Read>,
ctx: &Context<'_>,
) -> EncodingResult<Box<dyn DynEncodable>> {
Ok(Box::new(T::decode(stream, ctx)?))
}
#[cfg(feature = "xml")]
pub fn xml_decode_to_enc<T: DynEncodable + crate::xml::XmlDecodable>(
stream: &mut crate::xml::XmlStreamReader<&mut dyn std::io::Read>,
ctx: &Context<'_>,
) -> EncodingResult<Box<dyn DynEncodable>> {
Ok(Box::new(T::decode(stream, ctx)?))
}
impl TypeLoaderInstance {
pub fn new() -> Self {
Self::default()
}
pub fn add_binary_type(&mut self, data_type: u32, encoding_type: u32, fun: BinaryLoadFun) {
self.binary_types.insert(data_type, fun);
self.binary_types.insert(encoding_type, fun);
}
#[cfg(feature = "xml")]
pub fn add_xml_type(&mut self, data_type: u32, encoding_type: u32, fun: XmlLoadFun) {
self.xml_types.insert(data_type, fun);
self.xml_types.insert(encoding_type, fun);
}
#[cfg(feature = "json")]
pub fn add_json_type(&mut self, data_type: u32, encoding_type: u32, fun: JsonLoadFun) {
self.json_types.insert(data_type, fun);
self.json_types.insert(encoding_type, fun);
}
pub fn decode_binary(
&self,
ty: u32,
stream: &mut dyn Read,
context: &Context<'_>,
) -> Option<EncodingResult<Box<dyn DynEncodable>>> {
let fun = self.binary_types.get(&ty)?;
Some(fun(stream, context))
}
#[cfg(feature = "xml")]
pub fn decode_xml(
&self,
ty: u32,
stream: &mut crate::xml::XmlStreamReader<&mut dyn std::io::Read>,
context: &Context<'_>,
) -> Option<EncodingResult<Box<dyn DynEncodable>>> {
let fun = self.xml_types.get(&ty)?;
Some(fun(stream, context))
}
#[cfg(feature = "json")]
pub fn decode_json(
&self,
ty: u32,
stream: &mut crate::json::JsonStreamReader<&mut dyn std::io::Read>,
context: &Context<'_>,
) -> Option<EncodingResult<Box<dyn DynEncodable>>> {
let fun = self.json_types.get(&ty)?;
Some(fun(stream, context))
}
}
pub trait StaticTypeLoader {
fn instance() -> &'static TypeLoaderInstance;
fn namespace() -> &'static str;
}
impl<T> TypeLoader for T
where
T: StaticTypeLoader + Send + Sync + 'static,
{
#[cfg(feature = "xml")]
fn load_from_xml(
&self,
node_id: &crate::NodeId,
stream: &mut crate::xml::XmlStreamReader<&mut dyn std::io::Read>,
ctx: &Context<'_>,
_name: &str,
) -> Option<crate::EncodingResult<Box<dyn crate::DynEncodable>>> {
let idx = ctx.namespaces().get_index(Self::namespace())?;
if idx != node_id.namespace {
return None;
}
let Some(num_id) = node_id.as_u32() else {
return Some(Err(Error::decoding(
"Unsupported encoding ID. Only numeric encoding IDs are currently supported",
)));
};
Self::instance().decode_xml(num_id, stream, ctx)
}
#[cfg(feature = "json")]
fn load_from_json(
&self,
node_id: &crate::NodeId,
stream: &mut crate::json::JsonStreamReader<&mut dyn std::io::Read>,
ctx: &Context<'_>,
) -> Option<crate::EncodingResult<Box<dyn crate::DynEncodable>>> {
let idx = ctx.namespaces().get_index(Self::namespace())?;
if idx != node_id.namespace {
return None;
}
let Some(num_id) = node_id.as_u32() else {
return Some(Err(Error::decoding(
"Unsupported encoding ID. Only numeric encoding IDs are currently supported",
)));
};
Self::instance().decode_json(num_id, stream, ctx)
}
fn load_from_binary(
&self,
node_id: &NodeId,
stream: &mut dyn Read,
ctx: &Context<'_>,
_length: Option<usize>,
) -> Option<crate::EncodingResult<Box<dyn crate::DynEncodable>>> {
let idx = ctx.namespaces().get_index(Self::namespace())?;
if idx != node_id.namespace {
return None;
}
let Some(num_id) = node_id.as_u32() else {
return Some(Err(Error::decoding(
"Unsupported encoding ID. Only numeric encoding IDs are currently supported",
)));
};
Self::instance().decode_binary(num_id, stream, ctx)
}
fn priority(&self) -> TypeLoaderPriority {
TypeLoaderPriority::Generated
}
}
pub struct ContextOwned {
namespaces: NamespaceMap,
loaders: TypeLoaderCollection,
options: DecodingOptions,
}
impl std::fmt::Debug for ContextOwned {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ContextOwned")
.field("namespaces", &self.namespaces)
.field("options", &self.options)
.finish()
}
}
impl ContextOwned {
pub fn new(
namespaces: NamespaceMap,
loaders: TypeLoaderCollection,
options: DecodingOptions,
) -> Self {
Self {
namespaces,
loaders,
options,
}
}
pub fn new_default(namespaces: NamespaceMap, options: DecodingOptions) -> Self {
Self::new(namespaces, TypeLoaderCollection::new(), options)
}
pub fn context(&self) -> Context<'_> {
Context {
namespaces: &self.namespaces,
loaders: &self.loaders,
options: self.options.clone(),
aliases: None,
index_map: None,
}
}
pub fn namespaces(&self) -> &NamespaceMap {
&self.namespaces
}
pub fn namespaces_mut(&mut self) -> &mut NamespaceMap {
&mut self.namespaces
}
pub fn options(&self) -> &DecodingOptions {
&self.options
}
pub fn options_mut(&mut self) -> &mut DecodingOptions {
&mut self.options
}
pub fn loaders_mut(&mut self) -> &mut TypeLoaderCollection {
&mut self.loaders
}
}
impl Default for ContextOwned {
fn default() -> Self {
Self::new_default(Default::default(), Default::default())
}
}
#[derive(Clone)]
pub struct TypeLoaderCollection {
loaders: Vec<Arc<dyn TypeLoader>>,
}
impl Default for TypeLoaderCollection {
fn default() -> Self {
Self::new()
}
}
impl TypeLoaderCollection {
pub fn new() -> Self {
Self {
loaders: vec![Arc::new(GeneratedTypeLoader), Arc::new(FallbackTypeLoader)],
}
}
pub fn new_empty() -> Self {
Self {
loaders: Vec::new(),
}
}
pub fn add_type_loader(&mut self, loader: impl TypeLoader + 'static) {
self.add(Arc::new(loader));
}
pub fn add(&mut self, loader: Arc<dyn TypeLoader>) {
let priority = loader.priority();
for i in 0..self.loaders.len() {
if self.loaders[i].priority() > priority {
self.loaders.insert(i, loader);
return;
}
}
self.loaders.push(loader);
}
pub fn iter(&self) -> <&Self as IntoIterator>::IntoIter {
self.into_iter()
}
}
impl<'a> IntoIterator for &'a TypeLoaderCollection {
type Item = &'a Arc<dyn TypeLoader>;
type IntoIter = <&'a [Arc<dyn TypeLoader>] as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.loaders.iter()
}
}
#[derive(Clone)]
pub struct Context<'a> {
namespaces: &'a NamespaceMap,
loaders: &'a TypeLoaderCollection,
options: DecodingOptions,
aliases: Option<&'a HashMap<String, String>>,
index_map: Option<&'a HashMap<u16, u16>>,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum TypeLoaderPriority {
Core,
Generated,
Dynamic(u32),
Fallback,
}
impl TypeLoaderPriority {
pub fn priority(&self) -> u32 {
match self {
Self::Core => 0,
Self::Generated => 1,
Self::Dynamic(v) => *v,
Self::Fallback => u32::MAX,
}
}
}
impl PartialOrd for TypeLoaderPriority {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for TypeLoaderPriority {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.priority().cmp(&other.priority())
}
}
pub trait TypeLoader: Send + Sync {
#[cfg(feature = "xml")]
fn load_from_xml(
&self,
node_id: &crate::NodeId,
stream: &mut crate::xml::XmlStreamReader<&mut dyn std::io::Read>,
ctx: &Context<'_>,
name: &str,
) -> Option<crate::EncodingResult<Box<dyn crate::DynEncodable>>>;
#[cfg(feature = "json")]
fn load_from_json(
&self,
node_id: &crate::NodeId,
stream: &mut crate::json::JsonStreamReader<&mut dyn std::io::Read>,
ctx: &Context<'_>,
) -> Option<crate::EncodingResult<Box<dyn crate::DynEncodable>>>;
fn load_from_binary(
&self,
node_id: &NodeId,
stream: &mut dyn Read,
ctx: &Context<'_>,
length: Option<usize>,
) -> Option<crate::EncodingResult<Box<dyn crate::DynEncodable>>>;
fn priority(&self) -> TypeLoaderPriority {
TypeLoaderPriority::Generated
}
}
impl<'a> Context<'a> {
pub fn new(
namespaces: &'a NamespaceMap,
loaders: &'a TypeLoaderCollection,
options: DecodingOptions,
) -> Self {
Self {
namespaces,
loaders,
options,
aliases: None,
index_map: None,
}
}
#[cfg(feature = "json")]
pub fn load_from_json(
&self,
node_id: &NodeId,
stream: &mut crate::json::JsonStreamReader<&mut dyn Read>,
) -> crate::EncodingResult<crate::ExtensionObject> {
for loader in self.loaders {
if let Some(r) = loader.load_from_json(node_id, stream, self) {
return Ok(crate::ExtensionObject { body: Some(r?) });
}
}
Err(Error::decoding(format!(
"No type loader defined for {node_id}"
)))
}
pub fn load_from_binary(
&self,
node_id: &NodeId,
stream: &mut dyn Read,
length: Option<usize>,
) -> crate::EncodingResult<crate::ExtensionObject> {
for loader in self.loaders {
if let Some(r) = loader.load_from_binary(node_id, stream, self, length) {
return Ok(crate::ExtensionObject { body: Some(r?) });
}
}
Err(Error::decoding(format!(
"No type loader defined for {node_id}"
)))
}
#[cfg(feature = "xml")]
pub fn load_from_xml(
&self,
node_id: &NodeId,
stream: &mut crate::xml::XmlStreamReader<&mut dyn std::io::Read>,
name: &str,
) -> crate::EncodingResult<crate::ExtensionObject> {
for loader in self.loaders {
if let Some(r) = loader.load_from_xml(node_id, stream, self, name) {
return Ok(crate::ExtensionObject { body: Some(r?) });
}
}
Err(Error::decoding(format!(
"No type loader defined for {node_id}"
)))
}
pub fn options(&self) -> &DecodingOptions {
&self.options
}
pub fn namespaces(&self) -> &'a NamespaceMap {
self.namespaces
}
pub fn set_index_map(&mut self, index_map: &'a HashMap<u16, u16>) {
self.index_map = Some(index_map);
}
pub fn set_aliases(&mut self, aliases: &'a HashMap<String, String>) {
self.aliases = Some(aliases);
}
pub fn resolve_namespace_index(
&self,
index_in_node_set: u16,
) -> Result<u16, UninitializedIndex> {
if index_in_node_set == 0 {
return Ok(0);
}
let Some(index_map) = self.index_map else {
return Ok(index_in_node_set);
};
let Some(idx) = index_map.get(&index_in_node_set) else {
return Err(UninitializedIndex(index_in_node_set));
};
Ok(*idx)
}
pub fn resolve_namespace_index_inverse(
&self,
index_in_server: u16,
) -> Result<u16, UninitializedIndex> {
if index_in_server == 0 {
return Ok(0);
}
let Some(index_map) = self.index_map else {
return Ok(index_in_server);
};
let Some((idx, _)) = index_map.iter().find(|(_, &v)| v == index_in_server) else {
return Err(UninitializedIndex(index_in_server));
};
Ok(*idx)
}
pub fn resolve_alias<'b>(&self, node_id_str: &'b str) -> &'b str
where
'a: 'b,
{
if let Some(aliases) = self.aliases {
if let Some(alias) = aliases.get(node_id_str) {
return alias.as_str();
}
}
node_id_str
}
pub fn resolve_alias_inverse<'b>(&self, node_id_str: &'b str) -> &'b str
where
'a: 'b,
{
if let Some(aliases) = self.aliases {
for (k, v) in aliases.iter() {
if v == node_id_str {
return k.as_str();
}
}
}
node_id_str
}
pub fn with_zero_offset(&self) -> Cow<'_, Self> {
if self.options.client_offset.is_zero() {
Cow::Borrowed(self)
} else {
Cow::Owned(Self {
namespaces: self.namespaces,
loaders: self.loaders,
options: DecodingOptions {
client_offset: TimeDelta::zero(),
..self.options.clone()
},
aliases: self.aliases,
index_map: self.index_map,
})
}
}
}