use serde_derive::*;
use serde_json::{Map, Value};
use std::collections::btree_map::BTreeMap;
use std::fmt::Display;
pub type RoleMap = BTreeMap<Role, RoleDesc>;
pub type Dict = Map<String, Value>;
#[allow(dead_code)]
pub mod types {
pub const HELLO: u8 = 01;
pub const WELCOME: u8 = 02;
pub const ABORT: u8 = 03;
pub const GOODBYE: u8 = 06;
pub const CHALLENGE: u8 = 04;
pub const AUTHENTICATE: u8 = 05;
pub const ERROR: u8 = 08;
pub const PUBLISH: u8 = 16;
pub const PUBLISHED: u8 = 17;
pub const SUBSCRIBE: u8 = 32;
pub const SUBSCRIBED: u8 = 33;
pub const UNSUBSCRIBE: u8 = 34;
pub const UNSUBSCRIBED: u8 = 35;
pub const EVENT: u8 = 36;
pub const CALL: u8 = 48;
pub const CANCEL: u8 = 49;
pub const RESULT: u8 = 50;
pub const INTERRUPT: u8 = 69;
}
#[derive(Serialize, Deserialize, Default)]
#[serde(default)]
pub struct RoleDesc {
#[serde(skip_serializing_if = "Vec::is_empty")]
pub features: Vec<String>,
}
#[derive(Serialize, Deserialize, Hash, PartialOrd, PartialEq, Eq, Ord)]
#[serde(rename_all = "lowercase")]
pub enum Role {
Caller,
Callee,
Publisher,
Dealer,
}
#[derive(Debug, Clone)]
pub struct WampError {
pub code: ErrorKind,
pub message: String,
pub extra: Dict,
}
impl Display for WampError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
write!(f, "{}: {}", self.code.uri(), self.message)
}
}
impl WampError {
pub fn new(uri: &str, args: &rmpv::Value, kwargs: &rmpv::Value) -> Self {
let code = ErrorKind::from_uri(uri);
let extra: Dict = kwargs
.as_map()
.map(|v| {
v.into_iter()
.filter_map(|(k, v)| {
let key = match k {
rmpv::Value::String(key) => key.clone().into_str()?,
_ => return None,
};
let value = serde_json::to_value(v).ok()?;
Some((key, value))
})
.collect()
})
.unwrap_or_else(|| Dict::new());
let message = extra
.get("message")
.and_then(|v| v.as_str())
.or_else(|| args[0].as_str())
.unwrap_or_else(|| code.uri())
.to_string();
WampError {
code,
message,
extra,
}
}
}
macro_rules! error_kinds {
($(
$(#[$outer:meta])*
$error_code:ident : $uri:expr),+) => {
#[derive(Debug, PartialEq, Clone)]
pub enum ErrorKind {
$(
$(#[$outer])*
#[doc= "\n\n**WAMP uri:**"]
#[doc= $uri]
$error_code
),+
,
Other(String)
}
impl ErrorKind {
pub fn uri(&self) -> &str {
match self {
$(
ErrorKind::$error_code => $uri,
)+
ErrorKind::Other(code) => code
}
}
pub fn from_uri(uri : &str) -> Self {
match uri {
$(
$uri => ErrorKind::$error_code,
)+
uri => ErrorKind::Other(uri.to_string())
}
}
}
};
}
error_kinds! {
InvalidURI : "wamp.error.invalid_uri",
NoSuchProcedure : "wamp.error.no_such_procedure",
ProcedureAlreadyExists : "wamp.error.procedure_already_exists",
NoSuchRegistration : "wamp.error.no_such_registration",
NoSuchSubscription : "wamp.error.no_such_subscription",
InvalidArgument : "wamp.error.invalid_argument",
SystemShutdown : "wamp.error.system_shutdown",
CloseRealm : "wamp.error.close_realm",
GoodbyeAndOut : "wamp.error.goodbye_and_out",
NotAuthorized : "wamp.error.not_authorized",
AuthorizationFailed : "wamp.error.authorization_failed",
NoSuchRealm : "wamp.error.no_such_realm",
NoSuchRole : "wamp.error.no_such_role",
Canceled : "wamp.error.canceled",
OptionNotAllowed : "wamp.error.option_not_allowed",
NoEligibleCallee : "wamp.error.no_eligible_callee",
NetworkFailure : "wamp.error.network_failure"
}
impl serde::Serialize for ErrorKind {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.uri())
}
}
#[derive(Serialize, Deserialize, Default)]
pub struct HelloSpec<'a> {
pub roles: RoleMap,
#[serde(rename = "authmethods")]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub auth_methods: Vec<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub authid: Option<&'a str>,
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_uri() {
assert_eq!(ErrorKind::Canceled.uri(), "wamp.error.canceled");
assert_eq!(
ErrorKind::from_uri("wamp.error.network_failure"),
ErrorKind::NetworkFailure
);
eprintln!(
"json={}",
serde_json::to_string_pretty(&ErrorKind::AuthorizationFailed).unwrap()
);
}
#[test]
fn test_hello() {
let val = HelloSpec {
roles: vec![(Role::Caller, RoleDesc::default())]
.into_iter()
.collect(),
auth_methods: vec![],
authid: None,
};
eprintln!("json={}", serde_json::to_value(&val).unwrap());
}
}