use std::collections::HashMap;
use std::fmt;
use std::future::Future;
use std::hash::Hash;
use std::num::NonZeroU64;
use std::pin::Pin;
use std::str::FromStr;
use log::*;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use crate::error::*;
pub(crate) const DEFAULT_AGENT_STR: &str =
concat!(env!("CARGO_PKG_NAME"), "_rs-", env!("CARGO_PKG_VERSION"));
pub type WampUri = String;
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub struct WampId(NonZeroU64);
impl fmt::Display for WampId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl From<WampId> for NonZeroU64 {
fn from(id: WampId) -> Self {
id.0
}
}
impl WampId {
pub(crate) fn generate() -> Self {
let random_id = rand::random::<u64>() & ((1 << 53) - 1);
Self(unsafe { NonZeroU64::new_unchecked(random_id + 1) })
}
}
pub type WampInteger = usize;
pub type WampString = String;
pub type WampBool = bool;
pub type WampDict = HashMap<String, Arg>;
pub type WampList = Vec<Arg>;
pub type WampPayloadValue = serde_json::Value;
pub type WampArgs = Vec<WampPayloadValue>;
pub type WampKwArgs = serde_json::Map<String, WampPayloadValue>;
#[derive(Serialize, Deserialize, Debug)]
#[serde(untagged)]
pub enum Arg {
Uri(WampUri),
Id(WampId),
Integer(WampInteger),
String(WampString),
Bool(WampBool),
Dict(WampDict),
List(WampList),
None,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub enum ClientRole {
Caller,
Callee,
Publisher,
Subscriber,
}
impl ClientRole {
pub fn to_str(&self) -> &'static str {
match self {
ClientRole::Caller => "caller",
ClientRole::Callee => "callee",
ClientRole::Publisher => "publisher",
ClientRole::Subscriber => "subscriber",
}
}
}
pub enum ServerRole {
Router,
Broker,
}
impl ServerRole {
pub fn to_str(&self) -> &'static str {
match self {
ServerRole::Router => "router",
ServerRole::Broker => "broker",
}
}
}
#[derive(Debug, Clone, strum::AsRefStr, strum::EnumString)]
pub enum AuthenticationMethod {
#[strum(serialize = "anonymous")]
Anonymous,
#[strum(serialize = "wampcra")]
WampCra,
#[strum(serialize = "ticket")]
Ticket,
}
impl Serialize for AuthenticationMethod {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.as_ref())
}
}
impl<'de> Deserialize<'de> for AuthenticationMethod {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Self::from_str(&s).map_err(|err| serde::de::Error::custom(err.to_string()))
}
}
pub struct AuthenticationChallengeResponse {
pub signature: WampString,
pub extra: WampDict,
}
impl AuthenticationChallengeResponse {
pub fn with_signature(signature: WampString) -> Self {
Self {
signature,
extra: WampDict::default(),
}
}
}
pub fn try_from_any_value<'a, T: DeserializeOwned>(
value: WampPayloadValue,
) -> Result<T, WampError> {
serde_json::from_value(value).map_err(|e| {
WampError::SerializationError(crate::serializer::SerializerError::Deserialization(
e.to_string(),
))
})
}
pub fn try_from_args<'a, T: DeserializeOwned>(value: WampArgs) -> Result<T, WampError> {
try_from_any_value(value.into())
}
pub fn try_from_kwargs<'a, T: DeserializeOwned>(value: WampKwArgs) -> Result<T, WampError> {
try_from_any_value(value.into())
}
pub fn try_into_any_value<T: Serialize>(value: T) -> Result<WampPayloadValue, WampError> {
serde_json::to_value(value).map_err(|e| {
WampError::SerializationError(crate::serializer::SerializerError::Serialization(
e.to_string(),
))
})
}
pub fn try_into_args<T: Serialize>(value: T) -> Result<WampArgs, WampError> {
match serde_json::to_value(value).unwrap() {
serde_json::value::Value::Array(array) => Ok(array),
value => Err(WampError::SerializationError(
crate::serializer::SerializerError::Serialization(format!(
"failed to serialize {:?} into positional arguments",
value
)),
)),
}
}
pub fn try_into_kwargs<T: Serialize>(value: T) -> Result<WampKwArgs, WampError> {
match serde_json::to_value(value).unwrap() {
serde_json::value::Value::Object(object) => Ok(object),
value => Err(WampError::SerializationError(
crate::serializer::SerializerError::Serialization(format!(
"failed to serialize {:?} into keyword arguments",
value
)),
)),
}
}
pub fn is_valid_strict_uri<T: AsRef<str>>(in_uri: T) -> bool {
let uri: &str = in_uri.as_ref();
let mut num_chars_token: usize = 0;
if uri.starts_with("wamp.") {
warn!("URI '{}' cannot start with 'wamp'", uri);
return false;
}
for (i, c) in uri.chars().enumerate() {
if c == '.' {
if num_chars_token == 0 {
warn!(
"URI '{}' contains a zero length token ending @ index {}",
uri, i
);
return false;
}
num_chars_token = 0;
} else {
num_chars_token += 1;
}
if c == '_' {
continue;
}
if !c.is_lowercase() {
warn!(
"URI '{}' contains a non lower case character @ index {}",
uri, i
);
return false;
}
if !c.is_alphanumeric() {
warn!("URI '{}' contains an invalid character @ index {}", uri, i);
return false;
}
}
true
}
pub type GenericFuture<'a> = Pin<Box<dyn Future<Output = Result<(), WampError>> + Send + 'a>>;
pub type RpcFuture<'a> = std::pin::Pin<
Box<
dyn std::future::Future<Output = Result<(Option<WampArgs>, Option<WampKwArgs>), WampError>>
+ Send
+ 'a,
>,
>;
pub type RpcFunc<'a> =
Box<dyn Fn(Option<WampArgs>, Option<WampKwArgs>) -> RpcFuture<'a> + Send + Sync + 'a>;
pub type AuthenticationChallengeHandler<'a> = Box<
dyn Fn(
AuthenticationMethod,
WampDict,
) -> std::pin::Pin<
Box<
dyn std::future::Future<Output = Result<AuthenticationChallengeResponse, WampError>>
+ Send
+ 'a,
>,
> + Send
+ Sync
+ 'a,
>;