Skip to main content

dynomite/seeds/
mod.rs

1//! Pluggable seeds providers.
2//!
3//! A seeds provider hands the gossip task an up-to-date list of
4//! peers in the canonical
5//! `host:port:rack:dc:tokens|host:port:rack:dc:tokens` format.
6//! Three implementations ship with the engine:
7//!
8//! * [`simple::SimpleSeedsProvider`] - returns the seeds parsed
9//!   from the YAML config.
10//! * [`dns::DnsSeedsProvider`] - resolves a configured DNS
11//!   hostname to a list of IPs (mirroring the reference
12//!   `dns_get_seeds`, with the resolver factored behind a trait so
13//!   the unit test can substitute a deterministic implementation).
14//! * [`florida::FloridaSeedsProvider`] - HTTP GET to a Florida
15//!   service, parses the body. Hand-rolled HTTP/1.0 client over
16//!   `tokio::net::TcpStream` to stay within the locked dependency
17//!   set.
18//!
19//! The trait shape is the seam Stage 13 will expose through the
20//! embedding API; this stage locks the surface so the embed
21//! wrapper only needs to forward.
22//!
23//! # Examples
24//!
25//! ```
26//! use dynomite::seeds::{SeedsProvider, simple::SimpleSeedsProvider};
27//! use dynomite::conf::ConfDynSeed;
28//! let seeds = vec![ConfDynSeed::parse("h1:8101:rA:dc1:1").unwrap()];
29//! let p = SimpleSeedsProvider::new(seeds);
30//! let got = p.get_seeds().unwrap();
31//! assert_eq!(got.len(), 1);
32//! ```
33
34pub mod dns;
35pub mod florida;
36pub mod simple;
37
38use std::io;
39
40use thiserror::Error;
41
42use crate::conf::ConfDynSeed;
43
44/// Error type for seeds providers.
45#[derive(Debug, Error)]
46pub enum SeedsError {
47    /// The provider has no fresh data: the gossip task should
48    /// retry on the next interval. Mirrors the reference engine's
49    /// `DN_NOOPS` return.
50    #[error("no fresh seeds")]
51    NoFreshSeeds,
52    /// I/O error.
53    #[error("io error: {0}")]
54    Io(#[from] io::Error),
55    /// Parse error.
56    #[error("parse error: {0}")]
57    Parse(String),
58    /// Endpoint returned an HTTP error.
59    #[error("http error: {0}")]
60    Http(String),
61}
62
63/// Pluggable seeds provider.
64///
65/// Implementations may block; the gossip task calls them from a
66/// dedicated tokio task with timeouts wrapped at the call site.
67/// `SeedsProvider` is an async trait emulated via an associated
68/// future type so it can be implemented for both blocking
69/// (`SimpleSeedsProvider`) and async (`FloridaSeedsProvider`)
70/// shapes.
71pub trait SeedsProvider: Send + Sync {
72    /// Return the current list of seeds, or an error explaining
73    /// why no fresh data is available.
74    ///
75    /// Blocking implementations do their work synchronously and
76    /// return immediately; async implementations should run on a
77    /// blocking task spawned by the caller. Stage 12 binary
78    /// wiring picks the right runtime path; the trait stays sync
79    /// to keep the surface small for embedders.
80    fn get_seeds(&self) -> Result<Vec<ConfDynSeed>, SeedsError>;
81}
82
83/// Marker trait used by Stage 13 to register custom seeds
84/// providers through the embedding API. Implementing
85/// [`SeedsProvider`] is sufficient.
86impl<T> SeedsProvider for std::sync::Arc<T>
87where
88    T: SeedsProvider + ?Sized,
89{
90    fn get_seeds(&self) -> Result<Vec<ConfDynSeed>, SeedsError> {
91        (**self).get_seeds()
92    }
93}