Skip to main content

zvariant/
from_value.rs

1#[cfg(feature = "gvariant")]
2#[allow(deprecated)]
3use crate::Maybe;
4use crate::{
5    Array, Dict, Error, NoneValue, ObjectPath, Optional, OwnedObjectPath, Signature, Str,
6    Structure, Value,
7};
8
9#[cfg(unix)]
10use crate::Fd;
11
12use std::{collections::HashMap, hash::BuildHasher};
13
14macro_rules! value_try_from {
15    ($kind:ident, $to:ty) => {
16        impl<'a> TryFrom<Value<'a>> for $to {
17            type Error = Error;
18
19            fn try_from(value: Value<'a>) -> Result<Self, Self::Error> {
20                if let Value::$kind(value) = value {
21                    Ok(value.into())
22                } else {
23                    Err(Error::IncorrectType)
24                }
25            }
26        }
27    };
28}
29
30macro_rules! value_try_from_ref {
31    ($kind:ident, $to:ty) => {
32        impl<'a> TryFrom<&'a Value<'_>> for &'a $to {
33            type Error = Error;
34
35            fn try_from(value: &'a Value<'_>) -> Result<Self, Self::Error> {
36                if let Value::$kind(value) = value {
37                    Ok(value)
38                } else {
39                    Err(Error::IncorrectType)
40                }
41            }
42        }
43    };
44}
45
46macro_rules! value_try_from_ref_clone {
47    ($kind:ident, $to:ty) => {
48        impl<'a> TryFrom<&Value<'a>> for $to {
49            type Error = Error;
50
51            fn try_from(value: &Value<'a>) -> Result<Self, Self::Error> {
52                if let Value::$kind(value) = value {
53                    Ok(value.clone().into())
54                } else {
55                    Err(Error::IncorrectType)
56                }
57            }
58        }
59    };
60}
61
62macro_rules! value_try_from_all {
63    ($from:ident, $to:ty) => {
64        value_try_from!($from, $to);
65        value_try_from_ref!($from, $to);
66        value_try_from_ref_clone!($from, $to);
67    };
68}
69
70value_try_from_all!(U8, u8);
71value_try_from_all!(Bool, bool);
72value_try_from_all!(I16, i16);
73value_try_from_all!(U16, u16);
74value_try_from_all!(I32, i32);
75value_try_from_all!(U32, u32);
76value_try_from_all!(I64, i64);
77value_try_from_all!(U64, u64);
78value_try_from_all!(F64, f64);
79
80value_try_from_all!(Str, Str<'a>);
81value_try_from_all!(Signature, Signature);
82value_try_from_all!(ObjectPath, ObjectPath<'a>);
83value_try_from!(Str, String);
84value_try_from_ref!(Str, str);
85
86macro_rules! value_try_from_ref_try_clone {
87    ($kind:ident, $to:ty) => {
88        impl<'a> TryFrom<&Value<'a>> for $to {
89            type Error = Error;
90
91            fn try_from(value: &Value<'a>) -> Result<Self, Self::Error> {
92                if let Value::$kind(value) = value {
93                    value.try_clone().map_err(Into::into)
94                } else {
95                    Err(Error::IncorrectType)
96                }
97            }
98        }
99    };
100}
101
102value_try_from!(Structure, Structure<'a>);
103value_try_from_ref!(Structure, Structure<'a>);
104value_try_from_ref_try_clone!(Structure, Structure<'a>);
105
106value_try_from!(Dict, Dict<'a, 'a>);
107value_try_from_ref!(Dict, Dict<'a, 'a>);
108value_try_from_ref_try_clone!(Dict, Dict<'a, 'a>);
109
110value_try_from!(Array, Array<'a>);
111value_try_from_ref!(Array, Array<'a>);
112value_try_from_ref_try_clone!(Array, Array<'a>);
113
114// `#[allow(deprecated)]` can't attach to a macro invocation directly, hence the wrapping
115// module.
116#[cfg(feature = "gvariant")]
117#[allow(deprecated)]
118mod maybe_value_impls {
119    use super::*;
120
121    value_try_from!(Maybe, Maybe<'a>);
122    value_try_from_ref!(Maybe, Maybe<'a>);
123    value_try_from_ref_try_clone!(Maybe, Maybe<'a>);
124}
125
126#[cfg(unix)]
127value_try_from!(Fd, Fd<'a>);
128#[cfg(unix)]
129value_try_from_ref!(Fd, Fd<'a>);
130#[cfg(unix)]
131value_try_from_ref_try_clone!(Fd, Fd<'a>);
132
133impl TryFrom<&Value<'_>> for String {
134    type Error = Error;
135
136    fn try_from(value: &Value<'_>) -> Result<Self, Self::Error> {
137        Ok(<&str>::try_from(value)?.into())
138    }
139}
140
141impl<'a, T> TryFrom<Value<'a>> for Vec<T>
142where
143    T: TryFrom<Value<'a>>,
144    T::Error: Into<crate::Error>,
145{
146    type Error = Error;
147
148    fn try_from(value: Value<'a>) -> Result<Self, Self::Error> {
149        if let Value::Array(v) = value {
150            Self::try_from(v)
151        } else {
152            Err(Error::IncorrectType)
153        }
154    }
155}
156
157impl TryFrom<Value<'_>> for OwnedObjectPath {
158    type Error = Error;
159
160    fn try_from(value: Value<'_>) -> Result<Self, Self::Error> {
161        ObjectPath::try_from(value).map(OwnedObjectPath::from)
162    }
163}
164
165// tuple conversions in `structure` module for avoiding code-duplication.
166
167#[cfg(feature = "enumflags2")]
168impl<'a, F> TryFrom<Value<'a>> for enumflags2::BitFlags<F>
169where
170    F: enumflags2::BitFlag,
171    F::Numeric: TryFrom<Value<'a>, Error = Error>,
172{
173    type Error = Error;
174
175    fn try_from(value: Value<'a>) -> Result<Self, Self::Error> {
176        Self::from_bits(F::Numeric::try_from(value)?)
177            .map_err(|_| Error::Message("Failed to convert to bitflags".into()))
178    }
179}
180
181impl<'a, K, V, H> TryFrom<Value<'a>> for HashMap<K, V, H>
182where
183    K: crate::Basic + TryFrom<Value<'a>> + std::hash::Hash + std::cmp::Eq,
184    V: TryFrom<Value<'a>>,
185    H: BuildHasher + Default,
186    K::Error: Into<crate::Error>,
187    V::Error: Into<crate::Error>,
188{
189    type Error = crate::Error;
190
191    fn try_from(value: Value<'a>) -> Result<Self, Self::Error> {
192        if let Value::Dict(v) = value {
193            Self::try_from(v)
194        } else {
195            Err(crate::Error::IncorrectType)
196        }
197    }
198}
199
200impl<'a, T> TryFrom<Value<'a>> for Optional<T>
201where
202    T: TryFrom<Value<'a>> + NoneValue + PartialEq<<T as NoneValue>::NoneType>,
203    T::Error: Into<crate::Error>,
204{
205    type Error = crate::Error;
206
207    fn try_from(value: Value<'a>) -> Result<Self, Self::Error> {
208        T::try_from(value).map_err(Into::into).map(|value| {
209            if value == T::null_value() {
210                Optional::from(None)
211            } else {
212                Optional::from(Some(value))
213            }
214        })
215    }
216}
217
218// This would be great but somehow it conflicts with some blanket generic implementations from
219// core:
220//
221// impl<'a, T> TryFrom<Value<'a>> for Option<T>
222//
223// TODO: this could be useful
224// impl<'a, 'b, T> TryFrom<&'a Value<'b>> for Vec<T>
225// impl<'a, 'b, K, V, H> TryFrom<&'a Value<'v>> for HashMap<K, V, H>
226// and more..