use crate::{Authorization, Client, Event, ToServerAddrs};
use futures::Future;
use std::fmt::Formatter;
use std::{fmt, path::PathBuf, pin::Pin, sync::Arc, time::Duration};
use tokio::io;
use tokio_rustls::rustls;
pub struct ConnectOptions {
pub(crate) name: Option<String>,
pub(crate) no_echo: bool,
pub(crate) retry_on_failed_connect: bool,
pub(crate) max_reconnects: Option<usize>,
pub(crate) reconnect_buffer_size: usize,
pub(crate) connection_timeout: Duration,
pub(crate) auth: Authorization,
pub(crate) tls_required: bool,
pub(crate) certificates: Vec<PathBuf>,
pub(crate) client_cert: Option<PathBuf>,
pub(crate) client_key: Option<PathBuf>,
pub(crate) tls_client_config: Option<rustls::ClientConfig>,
pub(crate) flush_interval: Duration,
pub(crate) ping_interval: Duration,
pub(crate) subscription_capacity: usize,
pub(crate) sender_capacity: usize,
pub(crate) event_callback: CallbackArg1<Event, ()>,
pub(crate) inbox_prefix: String,
pub(crate) request_timeout: Option<Duration>,
}
impl fmt::Debug for ConnectOptions {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_map()
.entry(&"name", &self.name)
.entry(&"no_echo", &self.no_echo)
.entry(&"retry_on_failed_connect", &self.retry_on_failed_connect)
.entry(&"reconnect_buffer_size", &self.reconnect_buffer_size)
.entry(&"max_reconnects", &self.max_reconnects)
.entry(&"connection_timeout", &self.connection_timeout)
.entry(&"tls_required", &self.tls_required)
.entry(&"certificates", &self.certificates)
.entry(&"client_cert", &self.client_cert)
.entry(&"client_key", &self.client_key)
.entry(&"tls_client_config", &"XXXXXXXX")
.entry(&"flush_interval", &self.flush_interval)
.entry(&"ping_interval", &self.ping_interval)
.entry(&"sender_capacity", &self.sender_capacity)
.entry(&"inbox_prefix", &self.inbox_prefix)
.finish()
}
}
impl Default for ConnectOptions {
fn default() -> ConnectOptions {
ConnectOptions {
name: None,
no_echo: false,
retry_on_failed_connect: false,
reconnect_buffer_size: 8 * 1024 * 1024,
max_reconnects: Some(60),
connection_timeout: Duration::from_secs(5),
auth: Authorization::None,
tls_required: false,
certificates: Vec::new(),
client_cert: None,
client_key: None,
tls_client_config: None,
flush_interval: Duration::from_millis(100),
ping_interval: Duration::from_secs(60),
sender_capacity: 128,
subscription_capacity: 1024,
event_callback: CallbackArg1::<Event, ()>(Box::new(move |error| {
Box::pin(async move {
println!("error : {}", error);
})
})),
inbox_prefix: "_INBOX".to_string(),
request_timeout: Some(Duration::from_secs(10)),
}
}
}
impl ConnectOptions {
pub fn new() -> ConnectOptions {
ConnectOptions::default()
}
pub async fn connect<A: ToServerAddrs>(self, addrs: A) -> io::Result<Client> {
crate::connect_with_options(addrs, self).await
}
pub fn with_token(token: String) -> Self {
ConnectOptions {
auth: Authorization::Token(token),
..Default::default()
}
}
pub fn with_user_and_password(user: String, pass: String) -> Self {
ConnectOptions {
auth: Authorization::UserAndPassword(user, pass),
..Default::default()
}
}
pub fn with_nkey(seed: String) -> Self {
ConnectOptions {
auth: Authorization::NKey(seed),
..Default::default()
}
}
pub fn with_jwt<F, Fut>(jwt: String, sign_cb: F) -> Self
where
F: Fn(Vec<u8>) -> Fut + Send + Sync + 'static,
Fut: Future<Output = std::result::Result<Vec<u8>, AuthError>> + 'static + Send + Sync,
{
let sign_cb = Arc::new(sign_cb);
ConnectOptions {
auth: Authorization::Jwt(
jwt,
CallbackArg1(Box::new(move |nonce: String| {
let sign_cb = sign_cb.clone();
Box::pin(async move {
let sig = sign_cb(nonce.as_bytes().to_vec())
.await
.map_err(AuthError::new)?;
Ok(base64_url::encode(&sig))
})
})),
),
..Default::default()
}
}
pub async fn with_credentials_file(path: PathBuf) -> io::Result<Self> {
let cred_file_contents = crate::auth_utils::load_creds(path).await?;
Self::with_credentials(&cred_file_contents)
}
pub fn with_credentials(creds: &str) -> io::Result<Self> {
let (jwt, key_pair) = crate::auth_utils::parse_jwt_and_key_from_creds(creds)?;
let key_pair = std::sync::Arc::new(key_pair);
Ok(Self::with_jwt(jwt, move |nonce| {
let key_pair = key_pair.clone();
async move { key_pair.sign(&nonce).map_err(AuthError::new) }
}))
}
pub fn add_root_certificates(mut self, path: PathBuf) -> ConnectOptions {
self.certificates = vec![path];
self
}
pub fn add_client_certificate(mut self, cert: PathBuf, key: PathBuf) -> ConnectOptions {
self.client_cert = Some(cert);
self.client_key = Some(key);
self
}
pub fn require_tls(mut self, is_required: bool) -> ConnectOptions {
self.tls_required = is_required;
self
}
pub fn flush_interval(mut self, flush_interval: Duration) -> ConnectOptions {
self.flush_interval = flush_interval;
self
}
pub fn ping_interval(mut self, ping_interval: Duration) -> ConnectOptions {
self.ping_interval = ping_interval;
self
}
pub fn no_echo(mut self) -> ConnectOptions {
self.no_echo = true;
self
}
pub fn subscription_capacity(mut self, capacity: usize) -> ConnectOptions {
self.subscription_capacity = capacity;
self
}
pub fn connection_timeout(mut self, timeout: Duration) -> ConnectOptions {
self.connection_timeout = timeout;
self
}
pub fn request_timeout(mut self, timeout: Option<Duration>) -> ConnectOptions {
self.request_timeout = timeout;
self
}
pub fn event_callback<F, Fut>(mut self, cb: F) -> ConnectOptions
where
F: Fn(Event) -> Fut + Send + Sync + 'static,
Fut: Future<Output = ()> + 'static + Send + Sync,
{
self.event_callback = CallbackArg1::<Event, ()>(Box::new(move |event| Box::pin(cb(event))));
self
}
pub fn client_capacity(mut self, capacity: usize) -> ConnectOptions {
self.sender_capacity = capacity;
self
}
pub fn custom_inbox_prefix<T: ToString>(mut self, prefix: T) -> ConnectOptions {
self.inbox_prefix = prefix.to_string();
self
}
pub fn name<T: ToString>(mut self, name: T) -> ConnectOptions {
self.name = Some(name.to_string());
self
}
}
type AsyncCallbackArg1<A, T> =
Box<dyn Fn(A) -> Pin<Box<dyn Future<Output = T> + Send + Sync + 'static>> + Send + Sync>;
pub(crate) struct CallbackArg1<A, T>(AsyncCallbackArg1<A, T>);
impl<A, T> CallbackArg1<A, T> {
pub(crate) async fn call(&self, arg: A) -> T {
(self.0.as_ref())(arg).await
}
}
impl<A, T> fmt::Debug for CallbackArg1<A, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.write_str("callback")
}
}
#[derive(Clone)]
pub struct AuthError(String);
impl AuthError {
pub fn new(s: impl ToString) -> Self {
Self(s.to_string())
}
}
impl std::fmt::Display for AuthError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(&format!("AuthError({})", &self.0))
}
}
impl std::fmt::Debug for AuthError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(&format!("AuthError({})", &self.0))
}
}
impl std::error::Error for AuthError {}