Skip to main content

p2panda_net/discovery/
api.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3use std::sync::Arc;
4
5use ractor::{ActorRef, call};
6use thiserror::Error;
7use tokio::sync::{RwLock, broadcast};
8
9use crate::address_book::AddressBook;
10use crate::discovery::actors::{DiscoveryManagerArgs, ToDiscoveryManager};
11use crate::discovery::events::DiscoveryEvent;
12use crate::discovery::{Builder, DiscoveryMetrics};
13use crate::iroh_endpoint::Endpoint;
14
15/// Confidential topic discovery and random-walk strategy to resolve transport infos.
16///
17/// ## Design
18///
19/// Read more about the underlying design in [`p2panda-discovery`].
20///
21/// [`p2panda-discovery`]: https://docs.rs/p2panda-discovery/latest/p2panda_discovery/
22#[derive(Clone, Debug)]
23pub struct Discovery {
24    #[allow(unused, reason = "used by supervisor behind feature flag")]
25    pub(super) args: DiscoveryManagerArgs,
26    pub(super) inner: Arc<RwLock<Inner>>,
27}
28
29#[derive(Debug)]
30pub(super) struct Inner {
31    pub(super) actor_ref: Option<ActorRef<ToDiscoveryManager>>,
32}
33
34impl Discovery {
35    pub(crate) fn new(
36        actor_ref: Option<ActorRef<ToDiscoveryManager>>,
37        args: DiscoveryManagerArgs,
38    ) -> Self {
39        Self {
40            args,
41            inner: Arc::new(RwLock::new(Inner { actor_ref })),
42        }
43    }
44
45    pub fn builder(address_book: AddressBook, endpoint: Endpoint) -> Builder {
46        Builder::new(address_book, endpoint)
47    }
48
49    /// Subscribe to system events.
50    pub async fn events(&self) -> Result<broadcast::Receiver<DiscoveryEvent>, DiscoveryError> {
51        let inner = self.inner.read().await;
52        let result = call!(
53            inner.actor_ref.as_ref().expect("actor spawned in builder"),
54            ToDiscoveryManager::Events
55        )
56        .map_err(Box::new)?;
57        Ok(result)
58    }
59
60    /// Returns current metrics.
61    pub async fn metrics(&self) -> Result<DiscoveryMetrics, DiscoveryError> {
62        let inner = self.inner.read().await;
63        let result = call!(
64            inner.actor_ref.as_ref().expect("actor spawned in builder"),
65            ToDiscoveryManager::Metrics
66        )
67        .map_err(Box::new)?;
68        Ok(result)
69    }
70}
71
72impl Drop for Inner {
73    fn drop(&mut self) {
74        if let Some(actor_ref) = self.actor_ref.take() {
75            actor_ref.stop(None);
76        }
77    }
78}
79
80#[derive(Debug, Error)]
81pub enum DiscoveryError {
82    /// Spawning the internal actor failed.
83    #[error(transparent)]
84    ActorSpawn(#[from] ractor::SpawnErr),
85
86    /// Spawning the internal actor as a child actor of a supervisor failed.
87    #[cfg(feature = "supervisor")]
88    #[error(transparent)]
89    ActorLinkedSpawn(#[from] crate::supervisor::SupervisorError),
90
91    /// Messaging with internal actor via RPC failed.
92    #[error(transparent)]
93    ActorRpc(#[from] Box<ractor::RactorErr<ToDiscoveryManager>>),
94}