fips_core/endpoint/
builder.rs1use super::*;
2
3#[derive(Debug, Clone)]
5pub struct FipsEndpointBuilder {
6 config: Config,
7 identity_nsec: Option<String>,
8 discovery_scope: Option<String>,
9 local_rendezvous: bool,
10 local_instance_roles: Vec<crate::discovery::local::LocalInstanceCapability>,
11 disable_system_networking: bool,
12 packet_channel_capacity: usize,
13 #[cfg(feature = "host-ble-transport")]
14 host_ble: Option<HostBleAttachment>,
15 #[cfg(feature = "host-ble-transport")]
16 host_ble_config: Option<crate::config::BleConfig>,
17}
18
19#[cfg(feature = "host-ble-transport")]
20#[derive(Clone)]
21struct HostBleAttachment(Arc<std::sync::Mutex<Option<crate::transport::ble::host::HostBleIo>>>);
22
23#[cfg(feature = "host-ble-transport")]
24impl std::fmt::Debug for HostBleAttachment {
25 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 formatter.write_str("HostBleAttachment(..)")
27 }
28}
29
30const DEFAULT_ENDPOINT_PACKET_CHANNEL_CAPACITY: usize = 4096;
31
32impl Default for FipsEndpointBuilder {
33 fn default() -> Self {
34 Self {
35 config: Config::new(),
36 identity_nsec: None,
37 discovery_scope: None,
38 local_rendezvous: false,
39 local_instance_roles: Vec::new(),
40 disable_system_networking: true,
41 packet_channel_capacity: DEFAULT_ENDPOINT_PACKET_CHANNEL_CAPACITY,
42 #[cfg(feature = "host-ble-transport")]
43 host_ble: None,
44 #[cfg(feature = "host-ble-transport")]
45 host_ble_config: None,
46 }
47 }
48}
49
50impl FipsEndpointBuilder {
51 pub fn config(mut self, config: Config) -> Self {
53 self.config = config;
54 self
55 }
56
57 pub fn identity_nsec(mut self, nsec: impl Into<String>) -> Self {
59 self.identity_nsec = Some(nsec.into());
60 self
61 }
62
63 pub fn discovery_scope(mut self, scope: impl Into<String>) -> Self {
73 self.discovery_scope = Some(scope.into());
74 self
75 }
76
77 pub fn local_rendezvous(mut self) -> Self {
79 self.local_rendezvous = true;
80 self
81 }
82
83 pub fn local_role(mut self, name: impl Into<String>, priority: i16) -> Self {
86 let name = name.into().trim().to_string();
87 if crate::discovery::local_udp::local_capability_name_is_valid(&name)
88 && self.local_instance_roles.len()
89 < crate::discovery::local_udp::LOCAL_CAPABILITY_MAX_COUNT
90 {
91 self.local_instance_roles.push(
92 crate::discovery::local::LocalInstanceCapability::role(name)
93 .with_priority(priority),
94 );
95 }
96 self
97 }
98
99 pub fn without_system_tun(mut self) -> Self {
101 self.disable_system_networking = true;
102 self
103 }
104
105 pub fn packet_channel_capacity(mut self, capacity: usize) -> Self {
107 self.packet_channel_capacity = capacity.max(1);
108 self
109 }
110
111 #[cfg(feature = "host-ble-transport")]
116 pub fn host_ble(
117 mut self,
118 io: crate::transport::ble::host::HostBleIo,
119 config: crate::config::BleConfig,
120 ) -> Self {
121 self.host_ble = Some(HostBleAttachment(Arc::new(std::sync::Mutex::new(Some(io)))));
122 self.host_ble_config = Some(config);
123 self
124 }
125
126 pub(super) fn prepared_config(&self) -> Config {
127 let mut config = self.config.clone();
128 if let Some(nsec) = &self.identity_nsec {
129 config.node.identity = IdentityConfig {
130 nsec: Some(nsec.clone()),
131 persistent: false,
132 };
133 }
134 if self.disable_system_networking {
135 config.tun.enabled = false;
136 config.dns.enabled = false;
137 config.node.system_files_enabled = false;
138 }
139 #[cfg(feature = "host-ble-transport")]
140 if let Some(ble_config) = &self.host_ble_config {
141 config.transports.ble = crate::config::TransportInstances::Single(ble_config.clone());
145 }
146 if self.local_rendezvous {
147 config.node.discovery.local.enabled = true;
148 }
149 if let Some(scope) = self.discovery_scope.as_deref() {
150 if config
151 .node
152 .discovery
153 .lan
154 .scope
155 .as_deref()
156 .is_none_or(|scope| scope.trim().is_empty())
157 {
158 config.node.discovery.lan.scope = Some(scope.to_string());
159 }
160 apply_default_scoped_discovery(&mut config, scope);
161 }
162 config
163 }
164
165 pub async fn bind(self) -> Result<FipsEndpoint, FipsEndpointError> {
167 self.bind_inner(None).await
168 }
169
170 pub async fn bind_with_direct_receiver(
172 self,
173 ) -> Result<(FipsEndpoint, FipsEndpointDirectReceiver), FipsEndpointError> {
174 let (sink, receiver) = FipsEndpointDirectReceiver::channel();
175 let endpoint = self.bind_with_direct_sink(sink).await?;
176 Ok((endpoint, receiver))
177 }
178
179 pub async fn bind_with_direct_sink<S>(self, sink: S) -> Result<FipsEndpoint, FipsEndpointError>
185 where
186 S: FipsEndpointDirectSink,
187 {
188 self.bind_inner(Some(EndpointDirectSink::new(sink))).await
189 }
190
191 async fn bind_inner(
192 self,
193 direct_sink: Option<EndpointDirectSink>,
194 ) -> Result<FipsEndpoint, FipsEndpointError> {
195 let config = self.prepared_config();
196
197 let mut node = Node::new(config)?;
198 node.set_local_instance_roles(self.local_instance_roles);
199 #[cfg(feature = "host-ble-transport")]
200 if let Some(attachment) = &self.host_ble {
201 let io = attachment
202 .0
203 .lock()
204 .unwrap_or_else(|error| error.into_inner())
205 .take()
206 .ok_or(FipsEndpointError::HostBleAdapterConsumed)?;
207 node.set_host_ble_io(io);
208 }
209 let identity = PeerIdentity::from_pubkey_full(node.identity().pubkey_full());
210 let npub = identity.npub();
211 let node_addr = *identity.node_addr();
212 let address = *identity.address();
213 let packet_io = node.attach_external_packet_io(self.packet_channel_capacity)?;
214 let endpoint_data_io = match direct_sink {
215 Some(sink) => {
216 node.attach_endpoint_data_io_with_direct_sink(self.packet_channel_capacity, sink)?
217 }
218 None => node.attach_endpoint_data_io(self.packet_channel_capacity)?,
219 };
220 node.start().await?;
221 let local_capability_directory = node.local_capability_directory();
222
223 let (shutdown_tx, shutdown_rx) = oneshot::channel();
224 let task = spawn_node_task(node, shutdown_rx);
225 let endpoint_control_tx = endpoint_data_io.control_tx;
226 let endpoint_data_batches = endpoint_data_io.data_batch_tx;
227 let inbound_service_tx = endpoint_data_io.service_event_tx;
228
229 Ok(FipsEndpoint {
230 identity,
231 npub,
232 node_addr,
233 address,
234 discovery_scope: self.discovery_scope,
235 local_capability_directory,
236 outbound_packets: packet_io.outbound_tx,
237 delivered_packets: Arc::new(Mutex::new(packet_io.inbound_rx)),
238 endpoint_control_tx,
239 endpoint_data_batches,
240 inbound_endpoint_tx: endpoint_data_io.event_tx,
241 inbound_endpoint_rx: Arc::new(Mutex::new(EndpointReceiveState::new(
242 endpoint_data_io.event_rx,
243 ))),
244 inbound_service_tx,
245 inbound_service_rx: Arc::new(Mutex::new(ServiceReceiveState::new(
246 endpoint_data_io.service_event_rx,
247 ))),
248 registered_services: Arc::new(StdMutex::new(HashMap::new())),
249 service_channel_capacity: self.packet_channel_capacity,
250 shutdown_tx: std::sync::Mutex::new(Some(shutdown_tx)),
251 task: std::sync::Mutex::new(Some(task)),
252 })
253 }
254}