Skip to main content

zlink_core/varlink_service/
interface_description.rs

1use core::fmt::Debug;
2
3use crate::idl::Interface;
4#[cfg(feature = "introspection")]
5use crate::introspect::Type;
6#[cfg(feature = "idl-parse")]
7use alloc::string::String;
8#[cfg(feature = "idl-parse")]
9use serde::Deserialize;
10use serde::Serialize;
11
12/// The interface description.
13///
14/// Use [`InterfaceDescription::parse`] to get the [`Interface`].
15///
16/// Under the hood, the interface description is either a parsed [`Interface`] or a raw string.
17#[derive(Debug, Serialize, Clone)]
18#[cfg_attr(feature = "introspection", derive(Type))]
19#[cfg_attr(feature = "introspection", zlink(crate = "crate"))]
20pub struct InterfaceDescription<'a> {
21    description: Description<'a>,
22}
23
24impl<'a> InterfaceDescription<'a> {
25    /// Parse the interface description as an [`Interface`].
26    ///
27    /// If the description is already parsed, it is returned as is. If the description is a raw
28    /// string, it is parsed as an [`Interface`] and returned.
29    pub fn parse(&self) -> crate::Result<Interface<'_>> {
30        match &self.description {
31            Description::Parsed(interface) => Ok(interface.clone()),
32            #[cfg(feature = "idl-parse")]
33            Description::Raw(description) => description.as_str().try_into(),
34        }
35    }
36
37    /// The raw interface description, if `self` is based on a raw description.
38    pub fn as_raw(&self) -> Option<&str> {
39        match &self.description {
40            Description::Parsed(_) => None,
41            #[cfg(feature = "idl-parse")]
42            Description::Raw(description) => Some(description.as_str()),
43        }
44    }
45}
46
47impl<'a> From<&Interface<'a>> for InterfaceDescription<'a> {
48    fn from(interface: &Interface<'a>) -> Self {
49        Self {
50            description: Description::Parsed(interface.clone()),
51        }
52    }
53}
54
55#[cfg(feature = "idl-parse")]
56impl<'de> Deserialize<'de> for InterfaceDescription<'static> {
57    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
58    where
59        D: serde::Deserializer<'de>,
60    {
61        use core::fmt;
62
63        use serde::de::{Error, MapAccess, Visitor};
64
65        struct IDVisitor;
66
67        impl<'de> Visitor<'de> for IDVisitor {
68            type Value = InterfaceDescription<'static>;
69
70            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
71                formatter.write_str("a valid interface description")
72            }
73
74            fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
75            where
76                A: MapAccess<'de>,
77            {
78                let (field_name, description): (&str, String) = map
79                    .next_entry()?
80                    .ok_or_else(|| A::Error::invalid_length(0, &self))?;
81                if field_name != "description" {
82                    return Err(A::Error::unknown_field(field_name, &["description"]));
83                }
84
85                Ok(InterfaceDescription {
86                    description: Description::Raw(description),
87                })
88            }
89        }
90
91        deserializer.deserialize_map(IDVisitor)
92    }
93}
94
95#[derive(Debug, Clone)]
96enum Description<'a> {
97    Parsed(Interface<'a>),
98    #[cfg(feature = "idl-parse")]
99    Raw(String),
100}
101
102#[cfg(feature = "introspection")]
103impl Type for Description<'_> {
104    const TYPE: &'static crate::idl::Type<'static> = &crate::idl::Type::String;
105}
106
107impl Serialize for Description<'_> {
108    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
109    where
110        S: serde::Serializer,
111    {
112        match self {
113            Description::Parsed(interface) => serializer.collect_str(interface),
114            #[cfg(feature = "idl-parse")]
115            Description::Raw(description) => serializer.serialize_str(description),
116        }
117    }
118}
119
120#[cfg(feature = "idl-parse")]
121impl<'de> Deserialize<'de> for Description<'static> {
122    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
123    where
124        D: serde::Deserializer<'de>,
125    {
126        let description = String::deserialize(deserializer)?;
127        Ok(Description::Raw(description))
128    }
129}