zlink_core/varlink_service/
api.rs1use alloc::borrow::Cow;
2use serde::{Deserialize, Serialize};
3
4#[cfg(feature = "introspection")]
5use crate::introspect;
6
7use crate::ReplyError;
8
9#[cfg(feature = "idl")]
10use super::InterfaceDescription;
11use super::{Info, OwnedInfo};
12
13#[derive(Debug, Serialize, Deserialize)]
15#[serde(tag = "method", content = "parameters")]
16pub enum Method<'a> {
17 #[serde(rename = "org.varlink.service.GetInfo")]
19 GetInfo,
20 #[serde(rename = "org.varlink.service.GetInterfaceDescription")]
22 GetInterfaceDescription {
23 interface: &'a str,
25 },
26}
27
28#[derive(Debug, Serialize)]
32#[cfg_attr(feature = "idl-parse", derive(Deserialize))]
33#[serde(untagged)]
34pub enum Reply<'a> {
35 #[serde(borrow)]
37 Info(Info<'a>),
38 #[cfg(feature = "idl")]
41 InterfaceDescription(InterfaceDescription<'static>),
42}
43
44#[derive(Debug, Serialize)]
50#[cfg_attr(any(not(feature = "idl"), feature = "idl-parse"), derive(Deserialize))]
51#[serde(untagged)]
52pub enum OwnedReply {
53 Info(OwnedInfo),
55 #[cfg(feature = "idl")]
57 InterfaceDescription(InterfaceDescription<'static>),
58}
59
60#[cfg(feature = "idl")]
61impl<'a> From<Reply<'a>> for OwnedReply {
62 fn from(reply: Reply<'a>) -> Self {
63 match reply {
64 Reply::Info(info) => OwnedReply::Info(info.into()),
65 Reply::InterfaceDescription(desc) => OwnedReply::InterfaceDescription(desc),
66 }
67 }
68}
69
70#[cfg(not(feature = "idl"))]
71impl<'a> From<Reply<'a>> for OwnedReply {
72 fn from(reply: Reply<'a>) -> Self {
73 match reply {
74 Reply::Info(info) => OwnedReply::Info(info.into()),
75 }
76 }
77}
78
79#[derive(Debug, Clone, PartialEq, ReplyError)]
81#[cfg_attr(feature = "introspection", derive(introspect::ReplyError))]
82#[zlink(interface = "org.varlink.service")]
83#[cfg_attr(feature = "introspection", zlink(crate = "crate"))]
84pub enum Error<'a> {
85 InterfaceNotFound {
87 #[zlink(borrow)]
89 interface: Cow<'a, str>,
90 },
91 MethodNotFound {
93 #[zlink(borrow)]
95 method: Cow<'a, str>,
96 },
97 MethodNotImplemented {
99 #[zlink(borrow)]
101 method: Cow<'a, str>,
102 },
103 InvalidParameter {
105 #[zlink(borrow)]
107 parameter: Cow<'a, str>,
108 },
109 PermissionDenied,
111 ExpectedMore,
113}
114
115impl Error<'_> {
116 pub fn into_owned(self) -> Error<'static> {
120 match self {
121 Error::InterfaceNotFound { interface } => Error::InterfaceNotFound {
122 interface: Cow::Owned(interface.into_owned()),
123 },
124 Error::MethodNotFound { method } => Error::MethodNotFound {
125 method: Cow::Owned(method.into_owned()),
126 },
127 Error::MethodNotImplemented { method } => Error::MethodNotImplemented {
128 method: Cow::Owned(method.into_owned()),
129 },
130 Error::InvalidParameter { parameter } => Error::InvalidParameter {
131 parameter: Cow::Owned(parameter.into_owned()),
132 },
133 Error::PermissionDenied => Error::PermissionDenied,
134 Error::ExpectedMore => Error::ExpectedMore,
135 }
136 }
137}
138
139impl core::error::Error for Error<'_> {
140 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
141 None
142 }
143}
144
145impl core::fmt::Display for Error<'_> {
146 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
147 match self {
148 Error::InterfaceNotFound { interface } => {
149 write!(f, "Interface not found: {interface}")
150 }
151 Error::MethodNotFound { method } => {
152 write!(f, "Method not found: {method}")
153 }
154 Error::InvalidParameter { parameter } => {
155 write!(f, "Invalid parameter: {parameter}")
156 }
157 Error::PermissionDenied => {
158 write!(f, "Permission denied")
159 }
160 Error::ExpectedMore => {
161 write!(f, "Expected more")
162 }
163 Error::MethodNotImplemented { method } => {
164 write!(f, "Method not implemented: {method}")
165 }
166 }
167 }
168}
169
170#[derive(Debug, Clone, PartialEq)]
176pub struct OwnedError(Error<'static>);
177
178impl OwnedError {
179 pub fn inner(&self) -> &Error<'static> {
181 &self.0
182 }
183
184 pub fn into_inner(self) -> Error<'static> {
186 self.0
187 }
188}
189
190impl core::ops::Deref for OwnedError {
191 type Target = Error<'static>;
192
193 fn deref(&self) -> &Self::Target {
194 &self.0
195 }
196}
197
198impl core::ops::DerefMut for OwnedError {
199 fn deref_mut(&mut self) -> &mut Self::Target {
200 &mut self.0
201 }
202}
203
204impl core::error::Error for OwnedError {
205 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
206 self.0.source()
207 }
208}
209
210impl core::fmt::Display for OwnedError {
211 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
212 self.0.fmt(f)
213 }
214}
215
216impl<'a> From<Error<'a>> for OwnedError {
217 fn from(err: Error<'a>) -> Self {
218 Self(err.into_owned())
219 }
220}
221
222impl Serialize for OwnedError {
223 fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
224 where
225 S: serde::Serializer,
226 {
227 self.0.serialize(serializer)
228 }
229}
230
231impl<'de> Deserialize<'de> for OwnedError {
232 fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
233 where
234 D: serde::Deserializer<'de>,
235 {
236 use alloc::string::String;
237
238 #[derive(Deserialize)]
240 #[serde(tag = "error", content = "parameters")]
241 enum ErrorHelper {
242 #[serde(rename = "org.varlink.service.InterfaceNotFound")]
243 InterfaceNotFound { interface: String },
244 #[serde(rename = "org.varlink.service.MethodNotFound")]
245 MethodNotFound { method: String },
246 #[serde(rename = "org.varlink.service.MethodNotImplemented")]
247 MethodNotImplemented { method: String },
248 #[serde(rename = "org.varlink.service.InvalidParameter")]
249 InvalidParameter { parameter: String },
250 #[serde(rename = "org.varlink.service.PermissionDenied")]
251 PermissionDenied,
252 #[serde(rename = "org.varlink.service.ExpectedMore")]
253 ExpectedMore,
254 }
255
256 let helper = ErrorHelper::deserialize(deserializer)?;
257 let error = match helper {
258 ErrorHelper::InterfaceNotFound { interface } => Error::InterfaceNotFound {
259 interface: Cow::Owned(interface),
260 },
261 ErrorHelper::MethodNotFound { method } => Error::MethodNotFound {
262 method: Cow::Owned(method),
263 },
264 ErrorHelper::MethodNotImplemented { method } => Error::MethodNotImplemented {
265 method: Cow::Owned(method),
266 },
267 ErrorHelper::InvalidParameter { parameter } => Error::InvalidParameter {
268 parameter: Cow::Owned(parameter),
269 },
270 ErrorHelper::PermissionDenied => Error::PermissionDenied,
271 ErrorHelper::ExpectedMore => Error::ExpectedMore,
272 };
273 Ok(Self(error))
274 }
275}
276
277pub type Result<'a, T> = core::result::Result<T, Error<'a>>;
279
280#[cfg(test)]
281mod tests {
282 use super::*;
283
284 #[test]
285 fn error_serialization() {
286 let err = Error::InterfaceNotFound {
287 interface: Cow::Borrowed("com.example.missing"),
288 };
289
290 let json = serialize_error(&err);
291 assert!(json.contains("org.varlink.service.InterfaceNotFound"));
292 assert!(json.contains("com.example.missing"));
293
294 let err = Error::PermissionDenied;
295
296 let json = serialize_error(&err);
297 assert!(json.contains("org.varlink.service.PermissionDenied"));
298 }
299
300 #[test]
301 fn error_deserialization() {
302 let json = r#"{"error":"org.varlink.service.InterfaceNotFound","parameters":{"interface":"com.example.missing"}}"#;
304 let err: Error<'_> = deserialize_error(json);
305 assert_eq!(
306 err,
307 Error::InterfaceNotFound {
308 interface: Cow::Borrowed("com.example.missing")
309 }
310 );
311
312 let json = r#"{"error":"org.varlink.service.PermissionDenied"}"#;
314 let err: Error<'_> = deserialize_error(json);
315 assert_eq!(err, Error::PermissionDenied);
316
317 let json = r#"{"error":"org.varlink.service.MethodNotFound","parameters":{"method":"NonExistentMethod"}}"#;
319 let err: Error<'_> = deserialize_error(json);
320 assert_eq!(
321 err,
322 Error::MethodNotFound {
323 method: Cow::Borrowed("NonExistentMethod")
324 }
325 );
326
327 let json = r#"{"error":"org.varlink.service.InvalidParameter","parameters":{"parameter":"invalid_param"}}"#;
329 let err: Error<'_> = deserialize_error(json);
330 assert_eq!(
331 err,
332 Error::InvalidParameter {
333 parameter: Cow::Borrowed("invalid_param")
334 }
335 );
336
337 let json = r#"{"error":"org.varlink.service.MethodNotImplemented","parameters":{"method":"UnimplementedMethod"}}"#;
339 let err: Error<'_> = deserialize_error(json);
340 assert_eq!(
341 err,
342 Error::MethodNotImplemented {
343 method: Cow::Borrowed("UnimplementedMethod")
344 }
345 );
346
347 let json = r#"{"error":"org.varlink.service.ExpectedMore"}"#;
349 let err: Error<'_> = deserialize_error(json);
350 assert_eq!(err, Error::ExpectedMore);
351 }
352
353 #[test]
354 fn error_round_trip_serialization() {
355 let original = Error::InterfaceNotFound {
357 interface: Cow::Borrowed("com.example.missing"),
358 };
359
360 test_round_trip_serialize(&original);
361
362 let original = Error::PermissionDenied;
364
365 test_round_trip_serialize(&original);
366 }
367
368 #[test]
369 fn into_owned() {
370 let borrowed = Error::InterfaceNotFound {
371 interface: Cow::Borrowed("test.interface"),
372 };
373 let owned = borrowed.into_owned();
374 assert_eq!(
375 owned,
376 Error::InterfaceNotFound {
377 interface: Cow::Owned("test.interface".into())
378 }
379 );
380 }
381
382 fn serialize_error(err: &Error<'_>) -> String {
384 serde_json::to_string(err).unwrap()
385 }
386
387 fn deserialize_error(json: &str) -> Error<'_> {
389 serde_json::from_str(json).unwrap()
390 }
391
392 fn test_round_trip_serialize(original: &Error<'_>) {
394 let json = serde_json::to_string(original).unwrap();
395 let deserialized: Error<'_> = serde_json::from_str(&json).unwrap();
396 assert_eq!(*original, deserialized);
397 }
398}