Skip to main content

tokio_dbus_codegen/
types.rs

1//! Mapping D-Bus types onto the owned Rust types generated code speaks in.
2
3use genco::prelude::*;
4use tokio_dbus_core::signature::{self, Signature};
5
6use crate::error::ErrorKind;
7use crate::{Error, Result};
8
9/// The single type named by a signature.
10fn single(signature: &Signature) -> Result<signature::Type<'_>> {
11    let mut iter = signature.iter();
12
13    let Some(ty) = iter.next() else {
14        return Err(Error::new(ErrorKind::EmptyType));
15    };
16
17    if iter.next().is_some() {
18        return Err(Error::new(ErrorKind::CompoundType(signature.to_owned())));
19    }
20
21    Ok(ty)
22}
23
24/// The owned Rust type a value of this signature is decoded into.
25///
26/// # Examples
27///
28/// ```
29/// use tokio_dbus_codegen::owned_type;
30///
31/// assert_eq!(owned_type("s")?.to_string()?, "String");
32/// assert_eq!(owned_type("as")?.to_string()?, "Vec<String>");
33/// assert_eq!(owned_type("a{sv}")?.to_string()?, "HashMap<String, Value>");
34/// assert_eq!(owned_type("(iiay)")?.to_string()?, "(i32, i32, Vec<u8>)");
35/// # Ok::<_, tokio_dbus_codegen::Error>(())
36/// ```
37pub fn owned_type(signature: &str) -> Result<rust::Tokens> {
38    let signature = Signature::new(signature)?;
39    owned(single(signature)?)
40}
41
42/// The Rust type a client takes for an argument of this signature, which
43/// borrows where borrowing is free.
44///
45/// # Examples
46///
47/// ```
48/// use tokio_dbus_codegen::parameter_type;
49///
50/// assert_eq!(parameter_type("s")?.to_string()?, "&str");
51/// assert_eq!(parameter_type("u")?.to_string()?, "u32");
52/// assert_eq!(parameter_type("as")?.to_string()?, "&[String]");
53/// assert_eq!(parameter_type("a{sv}")?.to_string()?, "&HashMap<String, Value>");
54/// # Ok::<_, tokio_dbus_codegen::Error>(())
55/// ```
56pub fn parameter_type(signature: &str) -> Result<rust::Tokens> {
57    let signature = Signature::new(signature)?;
58    parameter(single(signature)?)
59}
60
61fn owned(ty: signature::Type<'_>) -> Result<rust::Tokens> {
62    Ok(match ty {
63        signature::Type::Signature(signature) => basic(signature)?,
64        signature::Type::Array(element) => {
65            // NB: A dict entry is only legal as the element type of an array,
66            // which is why a map is recognised here rather than on its own.
67            if let Ok(signature::Type::Dict(key, value)) = single(element) {
68                let key = owned(single(key)?)?;
69                let value = owned(single(value)?)?;
70                quote!(HashMap<$key, $value>)
71            } else {
72                let element = owned(single(element)?)?;
73                quote!(Vec<$element>)
74            }
75        }
76        signature::Type::Struct(fields) => {
77            let mut out = rust::Tokens::new();
78
79            for (index, field) in fields.iter().enumerate() {
80                if index > 0 {
81                    out.append(quote!(,));
82                    out.space();
83                }
84
85                out.append(owned(field)?);
86            }
87
88            // NB: A one field struct needs a trailing comma to stay a tuple.
89            if fields.iter().count() == 1 {
90                out.append(quote!(,));
91            }
92
93            quote!(($out))
94        }
95        signature::Type::Dict(..) => {
96            return Err(Error::new(ErrorKind::LooseDictEntry));
97        }
98    })
99}
100
101fn parameter(ty: signature::Type<'_>) -> Result<rust::Tokens> {
102    Ok(match ty {
103        signature::Type::Signature(signature) => match signature.as_bytes() {
104            b"s" => quote!(&str),
105            b"o" => quote!(&ObjectPath),
106            b"g" => quote!(&Signature),
107            b"v" => quote!(&Value),
108            // NB: Everything else is a scalar, which is cheaper to pass by
109            // value than by reference.
110            _ => basic(signature)?,
111        },
112        signature::Type::Array(element) => {
113            if single(element).is_ok_and(|t| matches!(t, signature::Type::Dict(..))) {
114                let map = owned(ty)?;
115                quote!(&$map)
116            } else {
117                let element = owned(single(element)?)?;
118                quote!(&[$element])
119            }
120        }
121        signature::Type::Struct(..) => {
122            let fields = owned(ty)?;
123            quote!(&$fields)
124        }
125        signature::Type::Dict(..) => {
126            return Err(Error::new(ErrorKind::LooseDictEntry));
127        }
128    })
129}
130
131fn basic(signature: &Signature) -> Result<rust::Tokens> {
132    Ok(match signature.as_bytes() {
133        b"y" => quote!(u8),
134        b"b" => quote!(bool),
135        b"n" => quote!(i16),
136        b"q" => quote!(u16),
137        b"i" => quote!(i32),
138        b"u" => quote!(u32),
139        b"x" => quote!(i64),
140        b"t" => quote!(u64),
141        b"d" => quote!(f64),
142        b"s" => quote!(String),
143        b"o" => quote!(ObjectPathBuf),
144        b"g" => quote!(SignatureBuf),
145        b"v" => quote!(Value),
146        // NB: Passing a file descriptor requires sending it out of band, which
147        // this implementation does not support.
148        b"h" => return Err(Error::new(ErrorKind::UnixFd)),
149        _ => {
150            return Err(Error::new(ErrorKind::UnknownType(signature.to_owned())));
151        }
152    })
153}