nostr_sdk/client/
options.rs

1// Copyright (c) 2022-2023 Yuki Kishimoto
2// Copyright (c) 2023-2025 Rust Nostr Developers
3// Distributed under the MIT software license
4
5//! Client Options
6
7#[cfg(not(target_arch = "wasm32"))]
8use std::net::SocketAddr;
9#[cfg(all(feature = "tor", not(target_arch = "wasm32")))]
10use std::path::Path;
11use std::time::Duration;
12
13use nostr_relay_pool::prelude::*;
14
15/// Max number of relays to use for gossip
16#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub struct GossipRelayLimits {
18    /// Max number of **read** relays per user (default: 3)
19    pub read_relays_per_user: usize,
20    /// Max number of **write** relays per user (default: 3)
21    pub write_relays_per_user: usize,
22    /// Max number of **hint** relays per user (default: 1)
23    pub hint_relays_per_user: usize,
24    /// Max number of **most used** relays per user (default: 1)
25    pub most_used_relays_per_user: usize,
26    /// Max number of NIP-17 relays per user (default: 3)
27    pub nip17_relays: usize,
28}
29
30impl Default for GossipRelayLimits {
31    fn default() -> Self {
32        Self {
33            read_relays_per_user: 3,
34            write_relays_per_user: 3,
35            hint_relays_per_user: 1,
36            most_used_relays_per_user: 1,
37            nip17_relays: 3,
38        }
39    }
40}
41
42/// Gossip options
43#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
44pub struct GossipOptions {
45    /// Max number of relays to use
46    pub limits: GossipRelayLimits,
47}
48
49impl GossipOptions {
50    /// Set limits
51    #[inline]
52    pub fn limits(mut self, limits: GossipRelayLimits) -> Self {
53        self.limits = limits;
54        self
55    }
56}
57
58/// Options
59#[derive(Debug, Clone, Default)]
60pub struct ClientOptions {
61    pub(super) autoconnect: bool,
62    #[cfg(not(target_arch = "wasm32"))]
63    pub(super) connection: Connection,
64    pub(super) relay_limits: RelayLimits,
65    pub(super) max_avg_latency: Option<Duration>,
66    pub(super) sleep_when_idle: SleepWhenIdle,
67    pub(super) verify_subscriptions: bool,
68    pub(super) ban_relay_on_mismatch: bool,
69    pub(super) gossip: GossipOptions,
70    pub(super) pool: RelayPoolOptions,
71}
72
73impl ClientOptions {
74    /// Create new default options
75    #[inline]
76    pub fn new() -> Self {
77        Self::default()
78    }
79
80    /// Automatically start connection with relays (default: false)
81    ///
82    /// When set to `true`, there isn't the need of calling the connect methods.
83    #[inline]
84    pub fn autoconnect(mut self, val: bool) -> Self {
85        self.autoconnect = val;
86        self
87    }
88
89    /// Auto authenticate to relays (default: true)
90    ///
91    /// <https://github.com/nostr-protocol/nips/blob/master/42.md>
92    #[inline]
93    pub fn automatic_authentication(mut self, enabled: bool) -> Self {
94        self.pool = self.pool.automatic_authentication(enabled);
95        self
96    }
97
98    /// Connection mode and target
99    #[inline]
100    #[cfg(not(target_arch = "wasm32"))]
101    pub fn connection(mut self, connection: Connection) -> Self {
102        self.connection = connection;
103        self
104    }
105
106    /// Set relay limits
107    #[inline]
108    pub fn relay_limits(mut self, limits: RelayLimits) -> Self {
109        self.relay_limits = limits;
110        self
111    }
112
113    /// Set max latency (default: None)
114    ///
115    /// Relays with an avg. latency greater that this value will be skipped.
116    #[inline]
117    pub fn max_avg_latency(mut self, max: Duration) -> Self {
118        self.max_avg_latency = Some(max);
119        self
120    }
121
122    /// Set sleep when idle config
123    #[inline]
124    pub fn sleep_when_idle(mut self, config: SleepWhenIdle) -> Self {
125        self.sleep_when_idle = config;
126        self
127    }
128
129    /// Verify that received events belong to a subscription and match the filter.
130    pub fn verify_subscriptions(mut self, enable: bool) -> Self {
131        self.verify_subscriptions = enable;
132        self
133    }
134
135    /// If true, ban a relay when it sends an event that doesn't match the subscription filter.
136    pub fn ban_relay_on_mismatch(mut self, ban_relay: bool) -> Self {
137        self.ban_relay_on_mismatch = ban_relay;
138        self
139    }
140
141    /// Set gossip options
142    #[inline]
143    pub fn gossip(mut self, opts: GossipOptions) -> Self {
144        self.gossip = opts;
145        self
146    }
147
148    /// Set relay pool options
149    #[inline]
150    pub fn pool(mut self, opts: RelayPoolOptions) -> Self {
151        self.pool = opts;
152        self
153    }
154}
155
156/// Put relays to sleep when idle.
157#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
158pub enum SleepWhenIdle {
159    /// Disabled
160    #[default]
161    Disabled,
162    /// Enabled for all relays
163    Enabled {
164        /// Idle timeout
165        ///
166        /// After how much time of inactivity put the relay to sleep.
167        timeout: Duration,
168    },
169}
170
171/// Connection target
172#[cfg(not(target_arch = "wasm32"))]
173#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
174pub enum ConnectionTarget {
175    /// All relays
176    #[default]
177    All,
178    /// Only `.onion` relays
179    Onion,
180}
181
182/// Connection
183#[cfg(not(target_arch = "wasm32"))]
184#[derive(Debug, Clone, PartialEq, Eq, Hash)]
185pub struct Connection {
186    /// Mode
187    pub mode: ConnectionMode,
188    /// Target
189    pub target: ConnectionTarget,
190}
191
192#[allow(clippy::derivable_impls)]
193#[cfg(not(target_arch = "wasm32"))]
194impl Default for Connection {
195    fn default() -> Self {
196        #[cfg(all(feature = "tor", not(target_os = "android"), not(target_os = "ios")))]
197        {
198            Self {
199                mode: ConnectionMode::tor(),
200                target: ConnectionTarget::Onion,
201            }
202        }
203
204        #[cfg(any(
205            not(feature = "tor"),
206            all(feature = "tor", any(target_os = "android", target_os = "ios")),
207        ))]
208        Self {
209            mode: ConnectionMode::default(),
210            target: ConnectionTarget::default(),
211        }
212    }
213}
214
215#[cfg(not(target_arch = "wasm32"))]
216impl Connection {
217    /// New default connection config
218    #[inline]
219    pub fn new() -> Self {
220        Self {
221            mode: ConnectionMode::default(),
222            target: ConnectionTarget::default(),
223        }
224    }
225
226    /// Set connection mode (default: direct)
227    #[inline]
228    pub fn mode(mut self, mode: ConnectionMode) -> Self {
229        self.mode = mode;
230        self
231    }
232
233    /// Set connection target (default: all)
234    #[inline]
235    pub fn target(mut self, target: ConnectionTarget) -> Self {
236        self.target = target;
237        self
238    }
239
240    /// Set direct connection
241    #[inline]
242    pub fn direct(mut self) -> Self {
243        self.mode = ConnectionMode::direct();
244        self
245    }
246
247    /// Set proxy
248    #[inline]
249    pub fn proxy(mut self, addr: SocketAddr) -> Self {
250        self.mode = ConnectionMode::proxy(addr);
251        self
252    }
253
254    /// Use embedded tor client
255    #[inline]
256    #[cfg(feature = "tor")]
257    pub fn embedded_tor(mut self) -> Self {
258        self.mode = ConnectionMode::tor();
259        self
260    }
261
262    /// Use embedded tor client
263    ///
264    /// Specify a path where to store data
265    #[inline]
266    #[cfg(feature = "tor")]
267    pub fn embedded_tor_with_path<P>(mut self, path: P) -> Self
268    where
269        P: AsRef<Path>,
270    {
271        self.mode = ConnectionMode::tor_with_path(path);
272        self
273    }
274}