opcua_types/variant/
from.rs

1use uuid::Uuid;
2
3use crate::{
4    ByteString, DataValue, DateTime, DateTimeUtc, DiagnosticInfo, DynEncodable, Error,
5    ExpandedNodeId, ExtensionObject, Guid, LocalizedText, NodeId, QualifiedName, StatusCode,
6    UAString, VariantScalarTypeId,
7};
8
9use super::{Variant, XmlElement};
10
11/// Trait for types that can be cast from a variant.
12///
13/// Unlike `IntoVariant`, this does not imply `TryFrom<Variant>`, due to
14/// orphan rules.
15pub trait TryFromVariant: Sized {
16    /// Try to cast the given variant to this type.
17    fn try_from_variant(v: Variant) -> Result<Self, Error>;
18}
19
20macro_rules! impl_from_variant_primitive {
21    ($tp:ty, $vt:ident) => {
22        impl TryFromVariant for $tp {
23            fn try_from_variant(v: Variant) -> Result<Self, Error> {
24                let cast = v.cast(VariantScalarTypeId::$vt);
25                if let Variant::$vt(v) = cast {
26                    Ok(v)
27                } else {
28                    Err(Error::new(
29                        StatusCode::BadTypeMismatch,
30                        concat!("Unable to convert variant to ", stringify!($vt)),
31                    ))
32                }
33            }
34        }
35    };
36}
37
38macro_rules! impl_from_variant_primitive_unbox {
39    ($tp:ty, $vt:ident) => {
40        impl TryFromVariant for $tp {
41            fn try_from_variant(v: Variant) -> Result<Self, Error> {
42                let cast = v.cast(VariantScalarTypeId::$vt);
43                if let Variant::$vt(v) = cast {
44                    Ok(*v)
45                } else {
46                    Err(Error::new(
47                        StatusCode::BadTypeMismatch,
48                        concat!("Unable to convert variant to ", stringify!($vt)),
49                    ))
50                }
51            }
52        }
53    };
54}
55
56impl_from_variant_primitive!(bool, Boolean);
57impl_from_variant_primitive!(i8, SByte);
58impl_from_variant_primitive!(u8, Byte);
59impl_from_variant_primitive!(i16, Int16);
60impl_from_variant_primitive!(u16, UInt16);
61impl_from_variant_primitive!(i32, Int32);
62impl_from_variant_primitive!(u32, UInt32);
63impl_from_variant_primitive!(i64, Int64);
64impl_from_variant_primitive!(u64, UInt64);
65impl_from_variant_primitive!(f32, Float);
66impl_from_variant_primitive!(f64, Double);
67impl_from_variant_primitive!(UAString, String);
68impl_from_variant_primitive!(XmlElement, XmlElement);
69impl_from_variant_primitive_unbox!(DateTime, DateTime);
70impl_from_variant_primitive_unbox!(Guid, Guid);
71impl_from_variant_primitive!(StatusCode, StatusCode);
72impl_from_variant_primitive!(ByteString, ByteString);
73impl_from_variant_primitive_unbox!(QualifiedName, QualifiedName);
74impl_from_variant_primitive_unbox!(LocalizedText, LocalizedText);
75impl_from_variant_primitive_unbox!(NodeId, NodeId);
76impl_from_variant_primitive_unbox!(ExpandedNodeId, ExpandedNodeId);
77impl_from_variant_primitive!(ExtensionObject, ExtensionObject);
78impl_from_variant_primitive_unbox!(DataValue, DataValue);
79impl_from_variant_primitive_unbox!(DiagnosticInfo, DiagnosticInfo);
80
81impl TryFromVariant for String {
82    fn try_from_variant(v: Variant) -> Result<Self, Error> {
83        Ok(UAString::try_from_variant(v)?.into())
84    }
85}
86
87impl TryFromVariant for Uuid {
88    fn try_from_variant(v: Variant) -> Result<Self, Error> {
89        Ok(Guid::try_from_variant(v)?.into())
90    }
91}
92
93impl TryFromVariant for DateTimeUtc {
94    fn try_from_variant(v: Variant) -> Result<Self, Error> {
95        Ok(DateTime::try_from_variant(v)?.as_chrono())
96    }
97}
98
99impl<T> TryFromVariant for T
100where
101    T: DynEncodable,
102{
103    fn try_from_variant(v: Variant) -> Result<Self, Error> {
104        let Variant::ExtensionObject(o) = v else {
105            return Err(Error::new(
106                StatusCode::BadTypeMismatch,
107                "Variant is not extension object",
108            ));
109        };
110        o.into_inner_as().map(|v| *v).ok_or_else(|| {
111            Error::new(
112                StatusCode::BadTypeMismatch,
113                "Variant is extension object, but not requested type",
114            )
115        })
116    }
117}
118
119impl<T> TryFromVariant for Option<T>
120where
121    T: TryFromVariant,
122{
123    fn try_from_variant(v: Variant) -> Result<Self, Error> {
124        if v.is_empty() {
125            return Ok(None);
126        }
127        Ok(Some(T::try_from_variant(v)?))
128    }
129}
130
131impl<T> TryFromVariant for Vec<T>
132where
133    T: TryFromVariant,
134{
135    fn try_from_variant(v: Variant) -> Result<Self, Error> {
136        match v {
137            Variant::Empty => Err(Error::new(
138                StatusCode::BadTypeMismatch,
139                "Attempted to cast empty variant to array",
140            )),
141            Variant::Array(a) => a
142                .values
143                .into_iter()
144                .map(|v| T::try_from_variant(v))
145                .collect::<Result<Vec<_>, _>>(),
146            r => Ok(vec![T::try_from_variant(r)?]),
147        }
148    }
149}
150
151impl TryFromVariant for Variant {
152    fn try_from_variant(v: Variant) -> Result<Self, Error> {
153        Ok(v)
154    }
155}
156
157impl<const N: usize, T> TryFromVariant for [T; N]
158where
159    T: TryFromVariant,
160{
161    fn try_from_variant(v: Variant) -> Result<Self, Error> {
162        let vals = match v {
163            Variant::Empty => {
164                return Err(Error::new(
165                    StatusCode::BadTypeMismatch,
166                    "Attempted to cast empty variant to array",
167                ))
168            }
169            Variant::Array(a) => {
170                if N != a.values.len() {
171                    return Err(Error::new(
172                        StatusCode::BadTypeMismatch,
173                        "Array size mismatch",
174                    ));
175                }
176                a.values
177                    .into_iter()
178                    .map(|v| T::try_from_variant(v))
179                    .collect::<Result<Vec<_>, _>>()?
180            }
181            r => {
182                if N != 1 {
183                    return Err(Error::new(
184                        StatusCode::BadTypeMismatch,
185                        "Array size mismatch",
186                    ));
187                }
188                vec![T::try_from_variant(r)?]
189            }
190        };
191
192        vals.try_into()
193            .map_err(|_| Error::new(StatusCode::BadTypeMismatch, "Array size mismatch"))
194    }
195}