use std::num::NonZeroUsize;
use std::sync::Arc;
use std::time::Duration;
use nostr_database::{IntoNostrDatabase, NostrDatabase};
use nostr_gossip::{GossipAllowedRelays, IntoNostrGossip, NostrGossip};
use crate::authenticator::Authenticator;
use crate::client::Client;
use crate::events_tracker::MemoryEventsTracker;
use crate::monitor::Monitor;
use crate::policy::AdmitPolicy;
#[cfg(not(target_arch = "wasm32"))]
use crate::proxy::Proxy;
use crate::relay::{RelayLimits, SleepWhenIdle};
use crate::transport::websocket::{
DefaultWebsocketTransport, IntoWebSocketTransport, WebSocketTransport,
};
const DEFAULT_NOTIFICATION_CHANNEL_SIZE: NonZeroUsize = NonZeroUsize::new(4096).unwrap();
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct GossipRelayLimits {
pub read_relays_per_user: u8,
pub write_relays_per_user: u8,
pub hint_relays_per_user: u8,
pub most_used_relays_per_user: u8,
pub nip17_relays: u8,
}
impl Default for GossipRelayLimits {
fn default() -> Self {
Self {
read_relays_per_user: 3,
write_relays_per_user: 3,
hint_relays_per_user: 1,
most_used_relays_per_user: 1,
nip17_relays: 3,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct GossipBackgroundRefresh {
pub interval: Duration,
pub max_public_keys_per_round: NonZeroUsize,
}
impl Default for GossipBackgroundRefresh {
fn default() -> Self {
Self {
interval: Duration::from_secs(5 * 60),
max_public_keys_per_round: NonZeroUsize::new(512).unwrap(),
}
}
}
impl GossipBackgroundRefresh {
pub fn interval(mut self, interval: Duration) -> Self {
self.interval = interval;
self
}
pub fn max_public_keys_per_round(mut self, max: NonZeroUsize) -> Self {
self.max_public_keys_per_round = max;
self
}
}
#[derive(Debug, Clone)]
pub struct GossipConfig {
pub limits: GossipRelayLimits,
pub allowed: GossipAllowedRelays,
pub sync_initial_timeout: Duration,
pub sync_idle_timeout: Duration,
pub fetch_timeout: Duration,
pub fetch_chunks: usize,
pub background_refresh: Option<GossipBackgroundRefresh>,
}
impl Default for GossipConfig {
fn default() -> Self {
Self {
limits: GossipRelayLimits::default(),
allowed: GossipAllowedRelays::default(),
sync_initial_timeout: Duration::from_secs(10),
sync_idle_timeout: Duration::from_secs(10),
fetch_timeout: Duration::from_secs(10),
fetch_chunks: 10,
background_refresh: Some(GossipBackgroundRefresh::default()),
}
}
}
impl GossipConfig {
pub fn limits(mut self, limits: GossipRelayLimits) -> Self {
self.limits = limits;
self
}
pub fn allowed(mut self, allowed: GossipAllowedRelays) -> Self {
self.allowed = allowed;
self
}
pub fn sync_initial_timeout(mut self, timeout: Duration) -> Self {
self.sync_initial_timeout = timeout;
self
}
pub fn sync_idle_timeout(mut self, timeout: Duration) -> Self {
self.sync_idle_timeout = timeout;
self
}
pub fn fetch_timeout(mut self, timeout: Duration) -> Self {
self.fetch_timeout = timeout;
self
}
pub fn fetch_chunks(mut self, chunks: usize) -> Self {
self.fetch_chunks = chunks;
self
}
#[inline]
pub fn background_refresh(mut self, config: GossipBackgroundRefresh) -> Self {
self.background_refresh = Some(config);
self
}
#[inline]
pub fn no_background_refresh(mut self) -> Self {
self.background_refresh = None;
self
}
}
#[derive(Debug, Clone)]
pub struct ClientBuilder {
pub websocket_transport: Arc<dyn WebSocketTransport>,
pub admit_policy: Option<Arc<dyn AdmitPolicy>>,
pub authenticator: Option<Arc<dyn Authenticator>>,
pub database: Arc<dyn NostrDatabase>,
pub gossip: Option<Arc<dyn NostrGossip>>,
pub gossip_config: GossipConfig,
pub monitor: Option<Monitor>,
#[cfg(not(target_arch = "wasm32"))]
pub proxy: Option<Proxy>,
pub max_relays: Option<NonZeroUsize>,
pub notification_channel_size: NonZeroUsize,
pub connect_timeout: Duration,
pub relay_limits: RelayLimits,
pub max_avg_latency: Option<Duration>,
pub sleep_when_idle: SleepWhenIdle,
pub verify_subscriptions: bool,
pub ban_relay_on_mismatch: bool,
}
impl Default for ClientBuilder {
fn default() -> Self {
Self {
websocket_transport: Arc::new(DefaultWebsocketTransport),
admit_policy: None,
authenticator: None,
database: Arc::new(MemoryEventsTracker::default()),
gossip: None,
gossip_config: GossipConfig::default(),
monitor: None,
#[cfg(not(target_arch = "wasm32"))]
proxy: None,
max_relays: None,
connect_timeout: Duration::from_secs(15),
relay_limits: RelayLimits::default(),
max_avg_latency: None,
sleep_when_idle: SleepWhenIdle::default(),
verify_subscriptions: false,
ban_relay_on_mismatch: false,
notification_channel_size: DEFAULT_NOTIFICATION_CHANNEL_SIZE,
}
}
}
impl ClientBuilder {
#[inline]
pub fn new() -> Self {
Self::default()
}
#[inline]
pub fn websocket_transport<T>(mut self, transport: T) -> Self
where
T: IntoWebSocketTransport,
{
self.websocket_transport = transport.into_transport();
self
}
#[inline]
pub fn admit_policy<T>(mut self, policy: T) -> Self
where
T: AdmitPolicy + 'static,
{
self.admit_policy = Some(Arc::new(policy));
self
}
#[inline]
pub fn authenticator<T>(mut self, authenticator: T) -> Self
where
T: Authenticator + 'static,
{
self.authenticator = Some(Arc::new(authenticator));
self
}
#[inline]
pub fn database<D>(mut self, database: D) -> Self
where
D: IntoNostrDatabase,
{
self.database = database.into_nostr_database();
self
}
#[inline]
pub fn gossip<T>(mut self, gossip: T) -> Self
where
T: IntoNostrGossip,
{
self.gossip = Some(gossip.into_nostr_gossip());
self
}
#[inline]
pub fn gossip_config(mut self, config: GossipConfig) -> Self {
self.gossip_config = config;
self
}
#[inline]
pub fn monitor(mut self, monitor: Monitor) -> Self {
self.monitor = Some(monitor);
self
}
#[inline]
#[cfg(not(target_arch = "wasm32"))]
pub fn proxy(mut self, proxy: Proxy) -> Self {
self.proxy = Some(proxy);
self
}
#[inline]
pub fn max_relays(mut self, num: Option<NonZeroUsize>) -> Self {
self.max_relays = num;
self
}
#[inline]
pub fn connect_timeout(mut self, timeout: Duration) -> Self {
self.connect_timeout = timeout;
self
}
#[inline]
pub fn relay_limits(mut self, limits: RelayLimits) -> Self {
self.relay_limits = limits;
self
}
#[inline]
pub fn max_avg_latency(mut self, max: Duration) -> Self {
self.max_avg_latency = Some(max);
self
}
#[inline]
pub fn sleep_when_idle(mut self, config: SleepWhenIdle) -> Self {
self.sleep_when_idle = config;
self
}
pub fn verify_subscriptions(mut self, enable: bool) -> Self {
self.verify_subscriptions = enable;
self
}
pub fn ban_relay_on_mismatch(mut self, ban_relay: bool) -> Self {
self.ban_relay_on_mismatch = ban_relay;
self
}
#[inline]
pub fn notification_channel_size(mut self, size: NonZeroUsize) -> Self {
self.notification_channel_size = size;
self
}
#[inline]
pub fn build(self) -> Client {
Client::from_builder(self)
}
}