use alloc::borrow::Cow;
use serde::{Deserialize, Serialize};
#[cfg(feature = "introspection")]
use crate::introspect;
use crate::ReplyError;
#[cfg(feature = "idl")]
use super::InterfaceDescription;
use super::{Info, OwnedInfo};
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "method", content = "parameters")]
pub enum Method<'a> {
#[serde(rename = "org.varlink.service.GetInfo")]
GetInfo,
#[serde(rename = "org.varlink.service.GetInterfaceDescription")]
GetInterfaceDescription {
interface: &'a str,
},
}
#[derive(Debug, Serialize)]
#[cfg_attr(feature = "idl-parse", derive(Deserialize))]
#[serde(untagged)]
pub enum Reply<'a> {
#[serde(borrow)]
Info(Info<'a>),
#[cfg(feature = "idl")]
InterfaceDescription(InterfaceDescription<'static>),
}
#[derive(Debug, Serialize)]
#[cfg_attr(any(not(feature = "idl"), feature = "idl-parse"), derive(Deserialize))]
#[serde(untagged)]
pub enum OwnedReply {
Info(OwnedInfo),
#[cfg(feature = "idl")]
InterfaceDescription(InterfaceDescription<'static>),
}
#[cfg(feature = "idl")]
impl<'a> From<Reply<'a>> for OwnedReply {
fn from(reply: Reply<'a>) -> Self {
match reply {
Reply::Info(info) => OwnedReply::Info(info.into()),
Reply::InterfaceDescription(desc) => OwnedReply::InterfaceDescription(desc),
}
}
}
#[cfg(not(feature = "idl"))]
impl<'a> From<Reply<'a>> for OwnedReply {
fn from(reply: Reply<'a>) -> Self {
match reply {
Reply::Info(info) => OwnedReply::Info(info.into()),
}
}
}
#[derive(Debug, Clone, PartialEq, ReplyError)]
#[cfg_attr(feature = "introspection", derive(introspect::ReplyError))]
#[zlink(interface = "org.varlink.service")]
#[cfg_attr(feature = "introspection", zlink(crate = "crate"))]
pub enum Error<'a> {
InterfaceNotFound {
#[zlink(borrow)]
interface: Cow<'a, str>,
},
MethodNotFound {
#[zlink(borrow)]
method: Cow<'a, str>,
},
MethodNotImplemented {
#[zlink(borrow)]
method: Cow<'a, str>,
},
InvalidParameter {
#[zlink(borrow)]
parameter: Cow<'a, str>,
},
PermissionDenied,
ExpectedMore,
}
impl Error<'_> {
pub fn into_owned(self) -> Error<'static> {
match self {
Error::InterfaceNotFound { interface } => Error::InterfaceNotFound {
interface: Cow::Owned(interface.into_owned()),
},
Error::MethodNotFound { method } => Error::MethodNotFound {
method: Cow::Owned(method.into_owned()),
},
Error::MethodNotImplemented { method } => Error::MethodNotImplemented {
method: Cow::Owned(method.into_owned()),
},
Error::InvalidParameter { parameter } => Error::InvalidParameter {
parameter: Cow::Owned(parameter.into_owned()),
},
Error::PermissionDenied => Error::PermissionDenied,
Error::ExpectedMore => Error::ExpectedMore,
}
}
}
impl core::error::Error for Error<'_> {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
None
}
}
impl core::fmt::Display for Error<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Error::InterfaceNotFound { interface } => {
write!(f, "Interface not found: {interface}")
}
Error::MethodNotFound { method } => {
write!(f, "Method not found: {method}")
}
Error::InvalidParameter { parameter } => {
write!(f, "Invalid parameter: {parameter}")
}
Error::PermissionDenied => {
write!(f, "Permission denied")
}
Error::ExpectedMore => {
write!(f, "Expected more")
}
Error::MethodNotImplemented { method } => {
write!(f, "Method not implemented: {method}")
}
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct OwnedError(Error<'static>);
impl OwnedError {
pub fn inner(&self) -> &Error<'static> {
&self.0
}
pub fn into_inner(self) -> Error<'static> {
self.0
}
}
impl core::ops::Deref for OwnedError {
type Target = Error<'static>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl core::ops::DerefMut for OwnedError {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl core::error::Error for OwnedError {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
self.0.source()
}
}
impl core::fmt::Display for OwnedError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.0.fmt(f)
}
}
impl<'a> From<Error<'a>> for OwnedError {
fn from(err: Error<'a>) -> Self {
Self(err.into_owned())
}
}
impl Serialize for OwnedError {
fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.0.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for OwnedError {
fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use alloc::string::String;
#[derive(Deserialize)]
#[serde(tag = "error", content = "parameters")]
enum ErrorHelper {
#[serde(rename = "org.varlink.service.InterfaceNotFound")]
InterfaceNotFound { interface: String },
#[serde(rename = "org.varlink.service.MethodNotFound")]
MethodNotFound { method: String },
#[serde(rename = "org.varlink.service.MethodNotImplemented")]
MethodNotImplemented { method: String },
#[serde(rename = "org.varlink.service.InvalidParameter")]
InvalidParameter { parameter: String },
#[serde(rename = "org.varlink.service.PermissionDenied")]
PermissionDenied,
#[serde(rename = "org.varlink.service.ExpectedMore")]
ExpectedMore,
}
let helper = ErrorHelper::deserialize(deserializer)?;
let error = match helper {
ErrorHelper::InterfaceNotFound { interface } => Error::InterfaceNotFound {
interface: Cow::Owned(interface),
},
ErrorHelper::MethodNotFound { method } => Error::MethodNotFound {
method: Cow::Owned(method),
},
ErrorHelper::MethodNotImplemented { method } => Error::MethodNotImplemented {
method: Cow::Owned(method),
},
ErrorHelper::InvalidParameter { parameter } => Error::InvalidParameter {
parameter: Cow::Owned(parameter),
},
ErrorHelper::PermissionDenied => Error::PermissionDenied,
ErrorHelper::ExpectedMore => Error::ExpectedMore,
};
Ok(Self(error))
}
}
pub type Result<'a, T> = core::result::Result<T, Error<'a>>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn error_serialization() {
let err = Error::InterfaceNotFound {
interface: Cow::Borrowed("com.example.missing"),
};
let json = serialize_error(&err);
assert!(json.contains("org.varlink.service.InterfaceNotFound"));
assert!(json.contains("com.example.missing"));
let err = Error::PermissionDenied;
let json = serialize_error(&err);
assert!(json.contains("org.varlink.service.PermissionDenied"));
}
#[test]
fn error_deserialization() {
let json = r#"{"error":"org.varlink.service.InterfaceNotFound","parameters":{"interface":"com.example.missing"}}"#;
let err: Error<'_> = deserialize_error(json);
assert_eq!(
err,
Error::InterfaceNotFound {
interface: Cow::Borrowed("com.example.missing")
}
);
let json = r#"{"error":"org.varlink.service.PermissionDenied"}"#;
let err: Error<'_> = deserialize_error(json);
assert_eq!(err, Error::PermissionDenied);
let json = r#"{"error":"org.varlink.service.MethodNotFound","parameters":{"method":"NonExistentMethod"}}"#;
let err: Error<'_> = deserialize_error(json);
assert_eq!(
err,
Error::MethodNotFound {
method: Cow::Borrowed("NonExistentMethod")
}
);
let json = r#"{"error":"org.varlink.service.InvalidParameter","parameters":{"parameter":"invalid_param"}}"#;
let err: Error<'_> = deserialize_error(json);
assert_eq!(
err,
Error::InvalidParameter {
parameter: Cow::Borrowed("invalid_param")
}
);
let json = r#"{"error":"org.varlink.service.MethodNotImplemented","parameters":{"method":"UnimplementedMethod"}}"#;
let err: Error<'_> = deserialize_error(json);
assert_eq!(
err,
Error::MethodNotImplemented {
method: Cow::Borrowed("UnimplementedMethod")
}
);
let json = r#"{"error":"org.varlink.service.ExpectedMore"}"#;
let err: Error<'_> = deserialize_error(json);
assert_eq!(err, Error::ExpectedMore);
}
#[test]
fn error_round_trip_serialization() {
let original = Error::InterfaceNotFound {
interface: Cow::Borrowed("com.example.missing"),
};
test_round_trip_serialize(&original);
let original = Error::PermissionDenied;
test_round_trip_serialize(&original);
}
#[test]
fn into_owned() {
let borrowed = Error::InterfaceNotFound {
interface: Cow::Borrowed("test.interface"),
};
let owned = borrowed.into_owned();
assert_eq!(
owned,
Error::InterfaceNotFound {
interface: Cow::Owned("test.interface".into())
}
);
}
fn serialize_error(err: &Error<'_>) -> String {
serde_json::to_string(err).unwrap()
}
fn deserialize_error(json: &str) -> Error<'_> {
serde_json::from_str(json).unwrap()
}
fn test_round_trip_serialize(original: &Error<'_>) {
let json = serde_json::to_string(original).unwrap();
let deserialized: Error<'_> = serde_json::from_str(&json).unwrap();
assert_eq!(*original, deserialized);
}
}