zlink_core/varlink_service/
api.rs

1use mayheap::string::String;
2#[cfg(feature = "std")]
3use serde::Deserialize;
4use serde::Serialize;
5
6#[cfg(feature = "introspection")]
7use crate::introspect;
8
9use crate::ReplyError;
10
11use super::Info;
12#[cfg(feature = "idl")]
13use super::InterfaceDescription;
14
15/// `org.varlink.service` interface methods.
16#[derive(Debug, Serialize)]
17#[cfg_attr(feature = "std", derive(Deserialize))]
18#[serde(tag = "method", content = "parameters")]
19pub enum Method<'a> {
20    /// Get information about the Varlink service.
21    #[serde(rename = "org.varlink.service.GetInfo")]
22    GetInfo,
23    /// Get the description of the specified interface.
24    #[serde(rename = "org.varlink.service.GetInterfaceDescription")]
25    GetInterfaceDescription {
26        /// The interface to get the description for.
27        interface: &'a str,
28    },
29}
30
31/// `org.varlink.service` interface replies.
32///
33/// This enum represents all possible replies from the varlink service interface methods.
34#[derive(Debug, Serialize)]
35#[cfg_attr(feature = "idl-parse", derive(Deserialize))]
36#[serde(untagged)]
37pub enum Reply<'a> {
38    /// Reply for `GetInfo` method.
39    #[serde(borrow)]
40    Info(Info<'a>),
41    /// Reply for `GetInterfaceDescription` method.
42    /// Note: InterfaceDescription only supports 'static lifetime for deserialization.
43    #[cfg(feature = "idl")]
44    InterfaceDescription(InterfaceDescription<'static>),
45}
46
47/// Errors that can be returned by the `org.varlink.service` interface.
48#[derive(Debug, Clone, PartialEq, ReplyError)]
49#[cfg_attr(feature = "introspection", derive(introspect::ReplyError))]
50#[zlink(interface = "org.varlink.service")]
51#[cfg_attr(feature = "introspection", zlink(crate = "crate"))]
52pub enum Error {
53    /// The requested interface was not found.
54    InterfaceNotFound {
55        /// The interface that was not found.
56        interface: String<MAX_INTERFACE_NAME_LENGTH>,
57    },
58    /// The requested method was not found.
59    MethodNotFound {
60        /// The method that was not found.
61        method: String<MAX_METHOD_NAME_LENGTH>,
62    },
63    /// The interface defines the requested method, but the service does not implement it.
64    MethodNotImplemented {
65        /// The method that is not implemented.
66        method: String<MAX_METHOD_NAME_LENGTH>,
67    },
68    /// One of the passed parameters is invalid.
69    InvalidParameter {
70        /// The parameter that is invalid.
71        parameter: String<MAX_PARAMETER_NAME_LENGTH>,
72    },
73    /// Client is denied access.
74    PermissionDenied,
75    /// Method is expected to be called with 'more' set to true, but wasn't.
76    ExpectedMore,
77}
78
79impl core::error::Error for Error {
80    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
81        None
82    }
83}
84
85impl core::fmt::Display for Error {
86    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
87        match self {
88            Error::InterfaceNotFound { interface } => {
89                write!(f, "Interface not found: {interface}")
90            }
91            Error::MethodNotFound { method } => {
92                write!(f, "Method not found: {method}")
93            }
94            Error::InvalidParameter { parameter } => {
95                write!(f, "Invalid parameter: {parameter}")
96            }
97            Error::PermissionDenied => {
98                write!(f, "Permission denied")
99            }
100            Error::ExpectedMore => {
101                write!(f, "Expected more")
102            }
103            Error::MethodNotImplemented { method } => {
104                write!(f, "Method not implemented: {method}")
105            }
106        }
107    }
108}
109
110/// Result type for Varlink service methods.
111pub type Result<T> = core::result::Result<T, Error>;
112
113const MAX_INTERFACE_NAME_LENGTH: usize = 64;
114const MAX_METHOD_NAME_LENGTH: usize = 64;
115const MAX_PARAMETER_NAME_LENGTH: usize = 24;
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120    use core::str::FromStr;
121
122    #[test]
123    fn error_serialization() {
124        let err = Error::InterfaceNotFound {
125            interface: String::from_str("com.example.missing").unwrap(),
126        };
127
128        let json = serialize_error(&err);
129        assert!(json.contains("org.varlink.service.InterfaceNotFound"));
130        assert!(json.contains("com.example.missing"));
131
132        let err = Error::PermissionDenied;
133
134        let json = serialize_error(&err);
135        assert!(json.contains("org.varlink.service.PermissionDenied"));
136    }
137
138    #[test]
139    fn error_deserialization() {
140        // Test error with parameter
141        let json = r#"{"error":"org.varlink.service.InterfaceNotFound","parameters":{"interface":"com.example.missing"}}"#;
142        let err = deserialize_error(json);
143        assert_eq!(
144            err,
145            Error::InterfaceNotFound {
146                interface: String::from_str("com.example.missing").unwrap()
147            }
148        );
149
150        // Test error without parameters
151        let json = r#"{"error":"org.varlink.service.PermissionDenied"}"#;
152        let err = deserialize_error(json);
153        assert_eq!(err, Error::PermissionDenied);
154
155        // Test MethodNotFound error
156        let json = r#"{"error":"org.varlink.service.MethodNotFound","parameters":{"method":"NonExistentMethod"}}"#;
157        let err = deserialize_error(json);
158        assert_eq!(
159            err,
160            Error::MethodNotFound {
161                method: String::from_str("NonExistentMethod").unwrap()
162            }
163        );
164
165        // Test InvalidParameter error
166        let json = r#"{"error":"org.varlink.service.InvalidParameter","parameters":{"parameter":"invalid_param"}}"#;
167        let err = deserialize_error(json);
168        assert_eq!(
169            err,
170            Error::InvalidParameter {
171                parameter: String::from_str("invalid_param").unwrap()
172            }
173        );
174
175        // Test MethodNotImplemented error
176        let json = r#"{"error":"org.varlink.service.MethodNotImplemented","parameters":{"method":"UnimplementedMethod"}}"#;
177        let err = deserialize_error(json);
178        assert_eq!(
179            err,
180            Error::MethodNotImplemented {
181                method: String::from_str("UnimplementedMethod").unwrap()
182            }
183        );
184
185        // Test ExpectedMore error
186        let json = r#"{"error":"org.varlink.service.ExpectedMore"}"#;
187        let err = deserialize_error(json);
188        assert_eq!(err, Error::ExpectedMore);
189    }
190
191    #[test]
192    fn error_round_trip_serialization() {
193        // Test with error that has parameters
194        let original = Error::InterfaceNotFound {
195            interface: String::from_str("com.example.missing").unwrap(),
196        };
197
198        test_round_trip_serialize(&original);
199
200        // Test with error that has no parameters
201        let original = Error::PermissionDenied;
202
203        test_round_trip_serialize(&original);
204    }
205
206    // Helper function to serialize Error to JSON string, abstracting std vs nostd differences
207    fn serialize_error(err: &Error) -> mayheap::string::String<256> {
208        #[cfg(feature = "std")]
209        {
210            mayheap::string::String::from_str(&serde_json::to_string(err).unwrap()).unwrap()
211        }
212        #[cfg(not(feature = "std"))]
213        {
214            use mayheap::string::String;
215            let mut buffer = [0u8; 256];
216            let len = serde_json_core::to_slice(err, &mut buffer).unwrap();
217            let vec = mayheap::Vec::<_, 256>::from_slice(&buffer[..len]).unwrap();
218            String::<256>::from_utf8(vec).unwrap()
219        }
220    }
221
222    // Helper function to deserialize JSON string to Error, abstracting std vs nostd differences
223    fn deserialize_error(json: &str) -> Error {
224        #[cfg(feature = "std")]
225        {
226            serde_json::from_str(json).unwrap()
227        }
228        #[cfg(not(feature = "std"))]
229        {
230            let (err, _): (Error, usize) = serde_json_core::from_str(json).unwrap();
231            err
232        }
233    }
234
235    // Helper function for round-trip serialization test, abstracting std vs nostd differences
236    fn test_round_trip_serialize(original: &Error) {
237        #[cfg(feature = "std")]
238        {
239            let json = serde_json::to_string(original).unwrap();
240            let deserialized: Error = serde_json::from_str(&json).unwrap();
241            assert_eq!(*original, deserialized);
242        }
243        #[cfg(not(feature = "std"))]
244        {
245            let mut buffer = [0u8; 256];
246            let len = serde_json_core::to_slice(original, &mut buffer).unwrap();
247            let json_bytes = &buffer[..len];
248            let (deserialized, _): (Error, usize) =
249                serde_json_core::from_slice(json_bytes).unwrap();
250            assert_eq!(*original, deserialized);
251        }
252    }
253}