use derive_builder::Builder;
use log::info;
use spin::{Mutex, RwLock};
use uuid::Uuid;
#[cfg(all(
any(feature = "subscribe", feature = "presence"),
feature = "std",
feature = "tokio"
))]
use crate::providers::futures_tokio::RuntimeTokio;
#[cfg(all(any(feature = "subscribe", feature = "presence"), feature = "std"))]
use crate::subscribe::{EventDispatcher, SubscriptionCursor, SubscriptionManager};
#[cfg(all(feature = "presence", feature = "std"))]
use crate::presence::PresenceManager;
#[cfg(not(feature = "serde"))]
use crate::core::Deserializer;
#[cfg(feature = "serde")]
use crate::providers::deserialization_serde::DeserializerSerde;
#[cfg(feature = "reqwest")]
use crate::transport::TransportReqwest;
#[cfg(feature = "std")]
use crate::{
core::{retry_policy::Endpoint, runtime::RuntimeSupport, RequestRetryConfiguration},
lib::alloc::vec,
};
use crate::{
core::{CryptoProvider, PubNubEntity, PubNubError},
lib::{
alloc::{
borrow::ToOwned,
format,
string::{String, ToString},
sync::Arc,
vec::Vec,
},
collections::HashMap,
core::{
cmp::max,
ops::{Deref, DerefMut},
},
},
transport::middleware::{PubNubMiddleware, SignatureKeySet},
Channel, ChannelGroup, ChannelMetadata, UserMetadata,
};
pub type PubNubGenericClient<T, D> = PubNubClientInstance<PubNubMiddleware<T>, D>;
#[cfg(all(feature = "reqwest", feature = "serde"))]
pub type PubNubClient = PubNubGenericClient<TransportReqwest, DeserializerSerde>;
#[derive(Debug)]
pub struct PubNubClientInstance<T, D> {
pub(crate) inner: Arc<PubNubClientRef<T, D>>,
#[cfg(all(feature = "subscribe", feature = "std"))]
pub(crate) cursor: Arc<RwLock<Option<SubscriptionCursor>>>,
#[cfg(all(feature = "subscribe", feature = "std"))]
pub(crate) event_dispatcher: Arc<EventDispatcher>,
}
impl<T, D> Deref for PubNubClientInstance<T, D> {
type Target = PubNubClientRef<T, D>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T, D> DerefMut for PubNubClientInstance<T, D> {
fn deref_mut(&mut self) -> &mut Self::Target {
Arc::get_mut(&mut self.inner)
.expect("Multiple mutable references to PubNubClientInstance are not allowed")
}
}
impl<T, D> Clone for PubNubClientInstance<T, D> {
fn clone(&self) -> Self {
Self {
inner: Arc::clone(&self.inner),
#[cfg(all(feature = "subscribe", feature = "std"))]
cursor: Default::default(),
#[cfg(all(feature = "subscribe", feature = "std"))]
event_dispatcher: Arc::clone(&self.event_dispatcher),
}
}
}
#[derive(Builder, Debug)]
#[builder(
pattern = "owned",
name = "PubNubClientConfigBuilder",
build_fn(private, name = "build_internal"),
setter(prefix = "with"),
no_std
)]
pub struct PubNubClientRef<T, D> {
pub(crate) transport: T,
pub(crate) deserializer: Arc<D>,
#[builder(
setter(custom, strip_option),
field(vis = "pub(crate)"),
default = "None"
)]
pub(crate) cryptor: Option<Arc<dyn CryptoProvider + Send + Sync>>,
#[builder(
setter(into),
field(type = "String", build = "Arc::new(Some(Uuid::new_v4().to_string()))")
)]
pub(crate) instance_id: Arc<Option<String>>,
#[builder(default = "Mutex::new(1)")]
pub(crate) next_seqn: Mutex<u16>,
pub(crate) config: PubNubConfig,
#[builder(
setter(custom),
field(vis = "pub(crate)"),
default = "Arc::new(spin::RwLock::new(String::new()))"
)]
pub(crate) auth_token: Arc<RwLock<String>>,
#[cfg(feature = "subscribe")]
#[builder(
setter(custom, strip_option),
field(vis = "pub(crate)"),
default = "Arc::new(spin::RwLock::new(String::new()))"
)]
pub(crate) filter_expression: Arc<RwLock<String>>,
#[cfg(feature = "presence")]
#[builder(
setter(custom),
field(vis = "pub(crate)"),
default = "Arc::new(spin::RwLock::new(HashMap::new()))"
)]
pub(crate) state: Arc<RwLock<HashMap<String, Vec<u8>>>>,
#[cfg(feature = "std")]
#[builder(setter(custom), field(vis = "pub(crate)"))]
pub(crate) runtime: RuntimeSupport,
#[cfg(all(feature = "subscribe", feature = "std"))]
#[builder(setter(skip), field(vis = "pub(crate)"))]
pub(crate) subscription: Arc<RwLock<Option<SubscriptionManager<T, D>>>>,
#[cfg(all(feature = "presence", feature = "std"))]
#[builder(setter(skip), field(vis = "pub(crate)"))]
pub(crate) presence: Arc<RwLock<Option<PresenceManager>>>,
#[builder(setter(skip), field(vis = "pub(crate)"))]
pub(crate) entities: RwLock<HashMap<String, PubNubEntity<T, D>>>,
}
impl<T, D> PubNubClientInstance<T, D> {
pub fn channel<S>(&self, name: S) -> Channel<T, D>
where
S: Into<String>,
{
let mut entities_slot = self.entities.write();
let name = name.into();
let entity = entities_slot
.entry(format!("{}_ch", &name))
.or_insert(Channel::new(self, name).into());
match entity {
PubNubEntity::Channel(channel) => channel.clone(),
_ => panic!("Unexpected entry type for Channel"),
}
}
pub fn channels<S>(&self, names: &[S]) -> Vec<Channel<T, D>>
where
S: Into<String> + Clone,
{
let mut channels = Vec::with_capacity(names.len());
let mut entities_slot = self.entities.write();
for name in names.iter() {
let name = name.to_owned().into();
let entity = entities_slot
.entry(format!("{}_ch", name))
.or_insert(Channel::new(self, name).into());
match entity {
PubNubEntity::Channel(channel) => channels.push(channel.clone()),
_ => panic!("Unexpected entry type for Channel"),
}
}
channels
}
pub fn channel_group<S>(&self, name: S) -> ChannelGroup<T, D>
where
S: Into<String>,
{
let mut entities_slot = self.entities.write();
let name = name.into();
let entity = entities_slot
.entry(format!("{}_chg", &name))
.or_insert(ChannelGroup::new(self, name).into());
match entity {
PubNubEntity::ChannelGroup(channel_group) => channel_group.clone(),
_ => panic!("Unexpected entry type for ChannelGroup"),
}
}
pub fn channel_groups<S>(&self, names: &[S]) -> Vec<ChannelGroup<T, D>>
where
S: Into<String> + Clone,
{
let mut channel_groups = Vec::with_capacity(names.len());
let mut entities_slot = self.entities.write();
for name in names.iter() {
let name = name.clone().into();
let entity = entities_slot
.entry(format!("{}_chg", name))
.or_insert(ChannelGroup::new(self, name).into());
match entity {
PubNubEntity::ChannelGroup(channel_group) => {
channel_groups.push(channel_group.clone())
}
_ => panic!("Unexpected entry type for ChannelGroup"),
}
}
channel_groups
}
pub fn channel_metadata<S>(&self, id: S) -> ChannelMetadata<T, D>
where
S: Into<String>,
{
let mut entities_slot = self.entities.write();
let id = id.into();
let entity = entities_slot
.entry(format!("{}_chm", &id))
.or_insert(ChannelMetadata::new(self, id).into());
match entity {
PubNubEntity::ChannelMetadata(channel_metadata) => channel_metadata.clone(),
_ => panic!("Unexpected entry type for ChannelMetadata"),
}
}
pub fn channels_metadata<S>(&self, ids: &[S]) -> Vec<ChannelMetadata<T, D>>
where
S: Into<String> + Clone,
{
let mut channels_metadata = Vec::with_capacity(ids.len());
let mut entities_slot = self.entities.write();
for id in ids.iter() {
let id = id.clone().into();
let entity = entities_slot
.entry(format!("{}_chm", id))
.or_insert(ChannelMetadata::new(self, id).into());
match entity {
PubNubEntity::ChannelMetadata(channel_metadata) => {
channels_metadata.push(channel_metadata.clone())
}
_ => panic!("Unexpected entry type for ChannelMetadata"),
}
}
channels_metadata
}
pub fn user_metadata<S>(&self, id: S) -> UserMetadata<T, D>
where
S: Into<String>,
{
let mut entities_slot = self.entities.write();
let id = id.into();
let entity = entities_slot
.entry(format!("{}_uidm", &id))
.or_insert(UserMetadata::new(self, id).into());
match entity {
PubNubEntity::UserMetadata(user_metadata) => user_metadata.clone(),
_ => panic!("Unexpected entry type for UserMetadata"),
}
}
pub fn users_metadata<S>(&self, ids: &[S]) -> Vec<UserMetadata<T, D>>
where
S: Into<String> + Clone,
{
let mut users_metadata = Vec::with_capacity(ids.len());
let mut entities_slot = self.entities.write();
for id in ids.iter() {
let id = id.clone().into();
let entity = entities_slot
.entry(format!("{}_uidm", id))
.or_insert(UserMetadata::new(self, id).into());
match entity {
PubNubEntity::UserMetadata(user_metadata) => {
users_metadata.push(user_metadata.clone())
}
_ => panic!("Unexpected entry type for UserMetadata"),
}
}
users_metadata
}
pub fn set_token<S>(&self, access_token: S)
where
S: Into<String>,
{
let mut token = self.auth_token.write();
*token = access_token.into();
}
pub fn get_token(&self) -> Option<String> {
let token = self.auth_token.read().deref().clone();
(!token.is_empty()).then_some(token)
}
}
impl<T, D> PubNubClientInstance<T, D>
where
T: crate::core::Transport + Send + Sync + 'static,
D: crate::core::Deserializer + Send + Sync + 'static,
{
#[cfg(all(any(feature = "subscribe", feature = "presence"), feature = "std"))]
pub fn terminate(&self) {
#[cfg(feature = "subscribe")]
{
let manager = self.subscription_manager(false);
let mut manager_slot = manager.write();
if let Some(manager) = manager_slot.as_ref() {
manager.terminate();
}
*manager_slot = None;
}
#[cfg(feature = "presence")]
{
let manager = self.presence_manager(false);
let mut manager_slot = manager.write();
if let Some(manager) = manager_slot.as_ref() {
manager.terminate();
}
*manager_slot = None;
}
}
}
impl<T, D> PubNubClientConfigBuilder<T, D> {
pub fn with_auth_key<S>(mut self, auth_key: S) -> Self
where
S: Into<String>,
{
if let Some(configuration) = self.config.as_mut() {
configuration.auth_key = Some(Arc::new(auth_key.into()));
}
self
}
#[cfg(any(feature = "subscribe", feature = "presence"))]
pub fn with_heartbeat_value(mut self, value: u64) -> Self {
if let Some(configuration) = self.config.as_mut() {
let value = max(20, value);
configuration.presence.heartbeat_value = value;
#[cfg(feature = "std")]
{
configuration.presence.heartbeat_interval = Some(value / 2 - 1);
}
}
self
}
#[cfg(all(any(feature = "subscribe", feature = "presence"), feature = "std"))]
pub fn with_heartbeat_interval(mut self, interval: u64) -> Self {
if let Some(configuration) = self.config.as_mut() {
configuration.presence.heartbeat_interval = Some(interval);
}
self
}
#[cfg(any(feature = "subscribe", feature = "presence"))]
pub fn with_suppress_leave_events(mut self, suppress_leave_events: bool) -> Self {
if let Some(configuration) = self.config.as_mut() {
configuration.presence.suppress_leave_events = suppress_leave_events;
}
self
}
#[cfg(feature = "std")]
pub fn with_retry_configuration(
mut self,
retry_configuration: RequestRetryConfiguration,
) -> Self {
if let Some(configuration) = self.config.as_mut() {
configuration.transport.retry_configuration = retry_configuration;
}
self
}
pub fn with_cryptor<C>(mut self, cryptor: C) -> Self
where
C: CryptoProvider + Send + Sync + 'static,
{
self.cryptor = Some(Some(Arc::new(cryptor)));
self
}
#[cfg(feature = "subscribe")]
pub fn with_filter_expression<S>(self, expression: S) -> Self
where
S: Into<String>,
{
if let Some(filter_expression) = &self.filter_expression {
let mut filter_expression = filter_expression.write();
*filter_expression = expression.into();
}
self
}
pub fn build(self) -> Result<PubNubClientInstance<PubNubMiddleware<T>, D>, PubNubError> {
self.build_internal()
.map_err(|err| PubNubError::ClientInitialization {
details: err.to_string(),
})
.and_then(|pre_build| {
let token = Arc::new(RwLock::new(String::new()));
#[cfg(all(feature = "subscribe", feature = "std"))]
let subscription = Arc::new(RwLock::new(None));
#[cfg(all(feature = "presence", feature = "std"))]
let presence: Arc<RwLock<Option<PresenceManager>>> = Arc::new(RwLock::new(None));
info!(
"Client Configuration: \n publish_key: {:?}\n subscribe_key: {}\n user_id: {}\n instance_id: {:?}",
pre_build.config.publish_key,
pre_build.config.subscribe_key,
pre_build.config.user_id,
pre_build.instance_id
);
Ok(PubNubClientRef {
transport: PubNubMiddleware {
signature_keys: pre_build.config.clone().signature_key_set()?,
auth_key: pre_build.config.auth_key.clone(),
instance_id: pre_build.instance_id.clone(),
user_id: pre_build.config.user_id.clone(),
transport: pre_build.transport,
auth_token: token.clone(),
},
deserializer: pre_build.deserializer,
instance_id: pre_build.instance_id,
next_seqn: pre_build.next_seqn,
auth_token: token,
config: pre_build.config,
cryptor: pre_build.cryptor.clone(),
#[cfg(feature = "subscribe")]
filter_expression: pre_build.filter_expression,
#[cfg(feature = "presence")]
state: Arc::new(RwLock::new(HashMap::new())),
#[cfg(feature = "std")]
runtime: pre_build.runtime,
#[cfg(all(feature = "subscribe", feature = "std"))]
subscription: subscription.clone(),
#[cfg(all(feature = "presence", feature = "std"))]
presence: presence.clone(),
entities: RwLock::new(HashMap::new()),
})
})
.map(|client| {
PubNubClientInstance {
inner: Arc::new(client),
#[cfg(all(feature = "subscribe", feature = "std"))]
cursor: Default::default(),
#[cfg(all(feature = "subscribe", feature = "std"))]
event_dispatcher: Default::default(),
}
})
}
}
#[cfg(feature = "std")]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TransportConfiguration {
pub subscribe_request_timeout: u64,
pub request_timeout: u64,
pub(crate) retry_configuration: RequestRetryConfiguration,
}
#[cfg(feature = "std")]
impl Default for TransportConfiguration {
fn default() -> Self {
Self {
subscribe_request_timeout: 310,
request_timeout: 10,
retry_configuration: RequestRetryConfiguration::Exponential {
min_delay: 2,
max_delay: 150,
max_retry: 6,
excluded_endpoints: Some(vec![
Endpoint::MessageSend,
Endpoint::Presence,
Endpoint::Files,
Endpoint::MessageStorage,
Endpoint::ChannelGroups,
Endpoint::DevicePushNotifications,
Endpoint::AppContext,
Endpoint::MessageReactions,
]),
},
}
}
}
#[cfg(any(feature = "subscribe", feature = "presence"))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct PresenceConfiguration {
pub heartbeat_value: u64,
#[cfg(feature = "std")]
pub heartbeat_interval: Option<u64>,
pub suppress_leave_events: bool,
}
#[cfg(any(feature = "subscribe", feature = "presence"))]
impl Default for PresenceConfiguration {
fn default() -> Self {
Self {
heartbeat_value: 300,
suppress_leave_events: false,
#[cfg(feature = "std")]
heartbeat_interval: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PubNubConfig {
pub(crate) subscribe_key: String,
pub(crate) publish_key: Option<String>,
pub(crate) secret_key: Option<String>,
pub(crate) user_id: Arc<String>,
pub(crate) auth_key: Option<Arc<String>>,
#[cfg(feature = "std")]
pub transport: TransportConfiguration,
#[cfg(any(feature = "subscribe", feature = "presence"))]
pub presence: PresenceConfiguration,
}
impl PubNubConfig {
fn signature_key_set(self) -> Result<Option<SignatureKeySet>, PubNubError> {
if let Some(secret_key) = self.secret_key {
#[cfg(not(feature = "std"))]
log::warn!("Signature calculation is not supported in `no_std`` environment!");
let publish_key = self.publish_key.ok_or(PubNubError::ClientInitialization {
details: "You must also provide the publish key if you use the secret key."
.to_string(),
})?;
Ok(Some(SignatureKeySet {
secret_key,
publish_key,
subscribe_key: self.subscribe_key,
}))
} else {
Ok(None)
}
}
}
#[derive(Debug, Clone)]
pub struct PubNubClientBuilder;
impl PubNubClientBuilder {
#[cfg(all(any(feature = "subscribe", feature = "presence"), feature = "std"))]
pub fn with_transport<T>(transport: T) -> PubNubClientRuntimeBuilder<T>
where
T: crate::core::Transport,
{
PubNubClientRuntimeBuilder { transport }
}
#[cfg(any(
all(not(feature = "subscribe"), not(feature = "presence")),
not(feature = "std")
))]
pub fn with_transport<T>(transport: T) -> PubNubClientDeserializerBuilder<T> {
PubNubClientDeserializerBuilder { transport }
}
#[cfg(all(
any(feature = "subscribe", feature = "presence"),
feature = "std",
feature = "blocking"
))]
pub fn with_blocking_transport<T>(transport: T) -> PubNubClientRuntimeBuilder<T>
where
T: crate::core::blocking::Transport + Send + Sync,
{
PubNubClientRuntimeBuilder { transport }
}
#[cfg(feature = "blocking")]
#[cfg(any(
all(not(feature = "subscribe"), not(feature = "presence")),
not(feature = "std")
))]
pub fn with_blocking_transport<T>(transport: T) -> PubNubClientDeserializerBuilder<T>
where
T: crate::core::blocking::Transport + Send + Sync,
{
PubNubClientDeserializerBuilder { transport }
}
}
#[derive(Debug, Clone)]
pub struct PubNubClientKeySetBuilder<T, D> {
pub(crate) transport: T,
pub(crate) deserializer: D,
#[cfg(all(any(feature = "subscribe", feature = "presence"), feature = "std"))]
pub(crate) runtime: RuntimeSupport,
}
impl<T, D> PubNubClientKeySetBuilder<T, D> {
pub fn with_keyset<S>(self, keyset: Keyset<S>) -> PubNubClientUserIdBuilder<T, S, D>
where
S: Into<String>,
{
PubNubClientUserIdBuilder {
transport: self.transport,
deserializer: self.deserializer,
keyset,
#[cfg(all(any(feature = "subscribe", feature = "presence"), feature = "std"))]
runtime: self.runtime,
}
}
}
#[cfg(all(any(feature = "subscribe", feature = "presence"), feature = "std"))]
pub struct PubNubClientRuntimeBuilder<T> {
pub(crate) transport: T,
}
#[cfg(all(any(feature = "subscribe", feature = "presence"), feature = "std"))]
impl<T> PubNubClientRuntimeBuilder<T> {
#[cfg(all(not(feature = "serde"), not(feature = "tokio")))]
pub fn with_runtime<R>(self, runtime: R) -> PubNubClientDeserializerBuilder<T>
where
R: Runtime + Send + Sync + 'static,
{
PubNubClientDeserializerBuilder {
transport: self.transport,
runtime: RuntimeSupport::new(Arc::new(runtime)),
}
}
#[cfg(all(feature = "serde", not(feature = "tokio")))]
pub fn with_runtime<R>(self, runtime: R) -> PubNubClientKeySetBuilder<T, DeserializerSerde>
where
R: Runtime + Send + Sync + 'static,
{
PubNubClientKeySetBuilder {
transport: self.transport,
deserializer: DeserializerSerde,
runtime: RuntimeSupport::new(Arc::new(runtime)),
}
}
#[cfg(all(feature = "serde", feature = "tokio"))]
pub fn with_keyset<S>(
self,
keyset: Keyset<S>,
) -> PubNubClientUserIdBuilder<T, S, DeserializerSerde>
where
S: Into<String>,
{
PubNubClientUserIdBuilder {
transport: self.transport,
deserializer: DeserializerSerde,
keyset,
runtime: RuntimeSupport::new(Arc::new(RuntimeTokio)),
}
}
}
pub struct PubNubClientDeserializerBuilder<T> {
pub(crate) transport: T,
#[cfg(all(any(feature = "subscribe", feature = "presence"), feature = "std"))]
pub(crate) runtime: RuntimeSupport,
}
impl<T> PubNubClientDeserializerBuilder<T> {
#[cfg(not(feature = "serde"))]
pub fn with_deserializer<D>(self, deserializer: D) -> PubNubClientKeySetBuilder<T, D>
where
D: Deserializer,
{
PubNubClientKeySetBuilder {
transport: self.transport,
deserializer,
#[cfg(all(any(feature = "subscribe", feature = "presence"), feature = "std"))]
runtime: self.runtime,
}
}
#[cfg(feature = "serde")]
pub fn with_keyset<S>(
self,
keyset: Keyset<S>,
) -> PubNubClientUserIdBuilder<T, S, DeserializerSerde>
where
S: Into<String>,
{
PubNubClientUserIdBuilder {
transport: self.transport,
deserializer: DeserializerSerde,
keyset,
#[cfg(all(any(feature = "subscribe", feature = "presence"), feature = "std"))]
runtime: self.runtime,
}
}
}
#[derive(Debug, Clone)]
pub struct PubNubClientUserIdBuilder<T, S, D>
where
S: Into<String>,
{
transport: T,
deserializer: D,
keyset: Keyset<S>,
#[cfg(all(any(feature = "subscribe", feature = "presence"), feature = "std"))]
runtime: RuntimeSupport,
}
impl<T, S, D> PubNubClientUserIdBuilder<T, S, D>
where
S: Into<String>,
{
pub fn with_user_id<U>(self, user_id: U) -> PubNubClientConfigBuilder<T, D>
where
U: Into<String>,
{
let publish_key = self.keyset.publish_key.map(|k| k.into());
let secret_key = self.keyset.secret_key.map(|k| k.into());
PubNubClientConfigBuilder {
transport: Some(self.transport),
config: Some(PubNubConfig {
publish_key,
subscribe_key: self.keyset.subscribe_key.into(),
secret_key,
user_id: Arc::new(user_id.into()),
auth_key: None,
#[cfg(feature = "std")]
transport: Default::default(),
#[cfg(any(feature = "subscribe", feature = "presence"))]
presence: Default::default(),
}),
#[cfg(all(any(feature = "subscribe", feature = "presence"), feature = "std"))]
runtime: Some(self.runtime),
deserializer: Some(Arc::new(self.deserializer)),
..Default::default()
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Keyset<S>
where
S: Into<String>,
{
pub subscribe_key: S,
pub publish_key: Option<S>,
pub secret_key: Option<S>,
}
#[cfg(test)]
mod should {
use super::*;
use crate::core::{TransportRequest, TransportResponse};
use std::any::type_name;
#[test]
fn include_pubnub_middleware() {
#[derive(Default)]
struct MockTransport;
#[async_trait::async_trait]
impl crate::core::Transport for MockTransport {
async fn send(
&self,
_request: TransportRequest,
) -> Result<TransportResponse, PubNubError> {
Ok(TransportResponse::default())
}
}
fn type_of<T>(_: &T) -> &'static str {
type_name::<T>()
}
let client = PubNubClientBuilder::with_transport(MockTransport)
.with_keyset(Keyset {
subscribe_key: "",
publish_key: Some(""),
secret_key: None,
})
.with_user_id("my-user_id")
.build()
.unwrap();
assert_eq!(
type_of(&client.transport),
type_name::<PubNubMiddleware<MockTransport>>()
);
}
#[test]
fn publish_key_is_required_if_secret_is_set() {
let config = PubNubConfig {
publish_key: None,
subscribe_key: "sub_key".into(),
secret_key: Some("sec_key".into()),
user_id: Arc::new("".into()),
auth_key: None,
#[cfg(feature = "std")]
transport: Default::default(),
#[cfg(any(feature = "subscribe", feature = "presence"))]
presence: Default::default(),
};
assert!(config.signature_key_set().is_err());
}
}