Skip to main content

dynomite/embed/
mod.rs

1//! Public embedding API for the Dynomite engine.
2//!
3//! `dynomite::embed` is the surface an embedding program uses to
4//! construct, run, observe, and shut down a Dynomite engine
5//! in-process. The same surface drives the [`dynomited`] binary;
6//! the binary is a thin wrapper around this module rather than a
7//! parallel code path.
8//!
9//! # Two-tier API
10//!
11//! 1. [`Server::builder`] returns a [`ServerBuilder`] - a typed,
12//!    fluent, validated-at-`build` builder that mirrors every
13//!    YAML-visible [`crate::conf::ConfPool`] field plus typed
14//!    setters for hook traits with no YAML form.
15//! 2. [`Server::start`] returns immediately; the tokio runtime
16//!    owns the background work. The caller drives the running
17//!    server through a [`ServerHandle`].
18//!
19//! # Hooks
20//!
21//! Five public traits expose every cross-cutting concern an
22//! embedder may want to override:
23//!
24//! * [`hooks::Datastore`] - backing store (Redis / Memcache /
25//!   custom).
26//! * [`hooks::SeedsProvider`] - service discovery integration.
27//! * [`hooks::CryptoProvider`] - HSM / KMS integration.
28//! * [`hooks::MetricsSink`] - exporter integration.
29//! * [`crate::io::reactor::Transport`] - already exposed by
30//!   `io::reactor`; re-exported here as
31//!   [`Transport`](crate::embed::Transport) for discoverability.
32//!
33//! Every trait has at least one default implementation shipped
34//! in-crate, and the crate-root re-exports below mean a typical
35//! embedder writes
36//! `use dynomite::embed::{Server, ServerBuilder, ServerHandle};`
37//! without reaching into the submodules.
38//!
39//! # Examples
40//!
41//! ```no_run
42//! use dynomite::embed::{Server, ServerBuilder};
43//! use dynomite::conf::DataStore;
44//! # tokio::runtime::Builder::new_multi_thread().enable_all().build().unwrap().block_on(async {
45//! let server: Server = ServerBuilder::new("dyn_o_mite")
46//!     .listen("127.0.0.1:0".parse().unwrap())
47//!     .dyn_listen("127.0.0.1:0".parse().unwrap())
48//!     .data_store(DataStore::Redis)
49//!     .servers(vec![dynomite::conf::ConfServer::parse("127.0.0.1:6379:1").unwrap()])
50//!     .tokens_str("0")
51//!     .build()
52//!     .unwrap();
53//! let handle = server.start().await.unwrap();
54//! assert_eq!(handle.peers().len(), 1);
55//! handle.shutdown().await.unwrap();
56//! # });
57//! ```
58//!
59//! [`dynomited`]: ../../dynomited/index.html
60
61pub mod builder;
62pub mod error;
63pub mod events;
64pub mod extension;
65pub mod hooks;
66pub mod server;
67pub mod snapshots;
68
69pub use crate::io::reactor::{ConnRole, TcpTransport, Transport};
70
71pub use self::builder::ServerBuilder;
72pub use self::error::EmbedError;
73pub use self::events::{
74    CloseReason, ConnId, ConnRoleTag, EventStream, PeerDownReason, PeerId, ServerEvent,
75};
76pub use self::extension::{CommandExtension, HsetOutcome};
77pub use self::hooks::{
78    BoxFuture, CryptoProvider, CryptoProviderError, Datastore, DatastoreError, DnsSeedsProvider,
79    FloridaSeedsProvider, LoggingMetricsSink, MemcacheDatastore, MemoryDatastore, MetricsError,
80    MetricsSink, Protocol, RedisDatastore, RustCryptoProvider, SeedsProvider, SimpleSeedsProvider,
81};
82pub use self::server::{Server, ServerHandle};
83pub use self::snapshots::{DatacenterSnapshot, PeerSnapshot, RackSnapshot, RingSnapshot};
84
85impl Server {
86    /// Convenience entry point.
87    ///
88    /// # Examples
89    ///
90    /// ```
91    /// use dynomite::embed::Server;
92    /// let _b = Server::builder("dyn_o_mite");
93    /// ```
94    #[must_use]
95    pub fn builder(pool_name: impl Into<String>) -> ServerBuilder {
96        ServerBuilder::new(pool_name)
97    }
98
99    /// One-shot "validate, build, and start" helper.
100    ///
101    /// Equivalent to calling `builder.build()?.start().await`. The
102    /// chained-call form remains available for embedders that
103    /// want to inspect the [`Server`] before spawning tasks; this
104    /// helper exists because most embedders only ever build then
105    /// immediately start.
106    ///
107    /// # Errors
108    ///
109    /// Returns the same [`EmbedError`] variants that
110    /// [`ServerBuilder::build`] and [`Server::start`] surface.
111    ///
112    /// # Examples
113    ///
114    /// ```no_run
115    /// use dynomite::embed::{Server, ServerBuilder};
116    /// use dynomite::conf::DataStore;
117    /// # tokio::runtime::Builder::new_multi_thread().enable_all().build().unwrap().block_on(async {
118    /// let builder = ServerBuilder::new("dyn_o_mite")
119    ///     .listen("127.0.0.1:0".parse().unwrap())
120    ///     .dyn_listen("127.0.0.1:0".parse().unwrap())
121    ///     .data_store(DataStore::Redis)
122    ///     .servers(vec![dynomite::conf::ConfServer::parse("127.0.0.1:6379:1").unwrap()])
123    ///     .tokens_str("0");
124    /// let handle = Server::start_with(builder).await.unwrap();
125    /// handle.shutdown().await.unwrap();
126    /// # });
127    /// ```
128    pub async fn start_with(builder: ServerBuilder) -> Result<ServerHandle, EmbedError> {
129        builder.build()?.start().await
130    }
131}