Skip to main content

asimov_protocol/
resolve_handle.rs

1// This is free and unencumbered software released into the public domain.
2
3use crate::PeerId;
4use alloc::boxed::Box;
5use asimov_id::{Handle, Id};
6use core::pin::Pin;
7use futures_lite::{Stream, StreamExt, stream};
8
9/// A resolver for ASIMOV handles (e.g., "â’¶jhacker").
10pub trait ResolveHandle {
11    type Error: core::fmt::Debug + Send;
12
13    /// Resolves an ASIMOV ID and yields a random known peer ID.
14    /// Ignores any erroneous results, sampling from only successful results.
15    ///
16    /// The default implementation requires the `random` feature.
17    fn resolve_random(
18        &mut self,
19        id: impl Into<Id>,
20    ) -> impl Future<Output = Result<Option<PeerId>, Self::Error>> {
21        #[cfg(not(feature = "random"))]
22        {
23            unimplemented!("resolve_random requires the `random` feature");
24            return async { Ok(None) };
25        }
26
27        #[cfg(feature = "random")]
28        async move {
29            let mut stream = Box::pin(self.resolve_all(id));
30            let mut results = alloc::vec::Vec::new();
31            while let Some(result) = stream.next().await {
32                let Ok(result) = result else {
33                    continue; // ignore errors silently
34                };
35                results.push(result);
36            }
37            let index = fastrand::usize(..results.len());
38            Ok(results.get(index).copied())
39        }
40    }
41
42    /// Resolves an ASIMOV ID and yields only the first known peer ID.
43    /// Ignores any initial erroneous results, returning the first successful result.
44    fn resolve_first(
45        &mut self,
46        id: impl Into<Id>,
47    ) -> impl Future<Output = Result<Option<PeerId>, Self::Error>> {
48        async move {
49            let mut stream = Box::pin(self.resolve_all(id));
50            while let Some(result) = stream.next().await {
51                let Ok(result) = result else {
52                    continue; // ignore errors silently
53                };
54                return Ok(Some(result));
55            }
56            Ok(None)
57        }
58    }
59
60    /// Resolves an ASIMOV ID into a stream of all known peer IDs.
61    fn resolve_all(
62        &mut self,
63        id: impl Into<Id>,
64    ) -> impl Stream<Item = Result<PeerId, Self::Error>> + Send {
65        let output: Pin<Box<dyn Stream<Item = Result<PeerId, Self::Error>> + Send>> =
66            match id.into() {
67                Id::Handle(handle) => Box::pin(self.resolve_handle(handle)),
68                Id::PublicKey(key) => Box::pin(stream::once(Ok(key))),
69            };
70        output
71    }
72
73    /// Resolves an ASIMOV handle into a stream of all known peer IDs.
74    ///
75    /// This is the only method that trait implementors must provide.
76    fn resolve_handle(
77        &mut self,
78        _handle: impl Into<Handle>,
79    ) -> impl Stream<Item = Result<PeerId, Self::Error>> + Send {
80        Box::pin(stream::empty())
81    }
82}