aboutsummaryrefslogtreecommitdiffstats
path: root/crates/daemon/src/daemon.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--crates/daemon/src/daemon.rs (renamed from crates/turtle/src/atuin_daemon/daemon.rs)190
1 files changed, 22 insertions, 168 deletions
diff --git a/crates/turtle/src/atuin_daemon/daemon.rs b/crates/daemon/src/daemon.rs
index 80aaeef8..70e65c1e 100644
--- a/crates/turtle/src/atuin_daemon/daemon.rs
+++ b/crates/daemon/src/daemon.rs
@@ -4,20 +4,19 @@
//!
//! - [`DaemonState`]: Shared state owned by the daemon
//! - [`DaemonHandle`]: A lightweight, cloneable handle for accessing daemon state
-//! - [`Component`]: A trait for implementing daemon components
//! - [`Daemon`]: The main daemon orchestrator
//! - [`DaemonBuilder`]: Builder for constructing and configuring the daemon
use std::sync::Arc;
-use crate::atuin_client::{
+use crate::aclient::{
database::ClientSqlite as HistoryDatabase, encryption, record::sqlite_store::SqliteStore,
settings::Settings,
};
use eyre::{Context, Result};
use tokio::sync::{RwLock, broadcast};
-use crate::atuin_daemon::events::DaemonEvent;
+use crate::events::DaemonEvent;
// ============================================================================
// DaemonState
@@ -25,7 +24,7 @@ use crate::atuin_daemon::events::DaemonEvent;
/// Shared state owned by the daemon.
///
-/// This contains all the resources that components and services need access to.
+/// This contains all the resources that services need access to.
/// The state is wrapped in an `Arc` and accessed via [`DaemonHandle`].
pub(crate) struct DaemonState {
// Event bus
@@ -48,7 +47,7 @@ pub(crate) struct DaemonState {
/// A lightweight handle to the daemon's shared state.
///
-/// This is the primary way for components, gRPC services, and spawned tasks to
+/// This is the primary way for gRPC services, and spawned tasks to
/// interact with the daemon. It provides access to:
///
/// - Event emission and subscription
@@ -88,12 +87,24 @@ impl DaemonHandle {
tracing::warn!("failed to emit event (no receivers?): {e}");
}
}
+ pub(crate) async fn wait_for(&self, matches: fn(&DaemonEvent) -> bool) -> Result<DaemonEvent> {
+ let mut rx = self.subscribe();
+ loop {
+ match rx.recv().await {
+ Ok(e) if matches(&e) => {
+ return Ok(e);
+ }
+ Err(err) => {
+ return Err(err).context("while waiting for events");
+ }
+ Ok(_) => (),
+ }
+ }
+ }
/// Subscribe to the event bus.
///
/// Returns a receiver that will receive all events emitted after this call.
- /// Useful for components that need to listen for events outside of the
- /// normal `handle_event` callback flow.
pub(crate) fn subscribe(&self) -> broadcast::Receiver<DaemonEvent> {
self.state.event_tx.subscribe()
}
@@ -113,16 +124,6 @@ impl DaemonHandle {
self.state.settings.read().await
}
- /// Apply already-loaded settings and emit a [`SettingsReloaded`] event.
- ///
- /// Use this when settings have already been loaded (e.g., from a file watcher)
- /// to avoid parsing the config file twice.
- pub(crate) async fn apply_settings(&self, settings: Settings) {
- *self.state.settings.write().await = settings;
- self.emit(DaemonEvent::SettingsReloaded);
- tracing::info!("settings applied");
- }
-
/// Get the encryption key.
pub(crate) fn encryption_key(&self) -> &[u8; 32] {
&self.state.encryption_key
@@ -148,92 +149,12 @@ impl std::fmt::Debug for DaemonHandle {
}
// ============================================================================
-// Component Trait
-// ============================================================================
-
-/// A daemon component that handles a specific domain.
-///
-/// Components are the building blocks of the daemon. Each component:
-///
-/// - Has a unique name for logging and debugging
-/// - Can optionally expose gRPC services
-/// - Receives a [`DaemonHandle`] on startup for accessing daemon resources
-/// - Handles events from the event bus
-/// - Performs cleanup on shutdown
-///
-/// # Lifecycle
-///
-/// 1. **Construction**: Component is created (usually via `new()`)
-/// 2. **Start**: `start()` is called with a [`DaemonHandle`]
-/// 3. **Running**: `handle_event()` is called for each event on the bus
-/// 4. **Shutdown**: `stop()` is called for cleanup
-///
-/// # Example
-///
-/// ```ignore
-/// pub(crate) struct MyComponent {
-/// handle: Option<DaemonHandle>,
-/// }
-///
-/// #[async_trait]
-/// impl Component for MyComponent {
-/// fn name(&self) -> &'static str { "my-component" }
-///
-/// async fn start(&mut self, handle: DaemonHandle) -> Result<()> {
-/// self.handle = Some(handle);
-/// Ok(())
-/// }
-///
-/// async fn handle_event(&mut self, event: &DaemonEvent) -> Result<()> {
-/// match event {
-/// DaemonEvent::SomeEvent => {
-/// // Handle the event
-/// if let Some(handle) = &self.handle {
-/// handle.emit(DaemonEvent::ResponseEvent);
-/// }
-/// }
-/// _ => {}
-/// }
-/// Ok(())
-/// }
-///
-/// async fn stop(&mut self) -> Result<()> {
-/// Ok(())
-/// }
-/// }
-/// ```
-#[tonic::async_trait]
-pub(crate) trait Component: Send + Sync {
- /// Human-readable name for logging and debugging.
- fn name(&self) -> &'static str;
-
- /// Called once at startup.
- ///
- /// Store the handle if you need to emit events or access daemon resources
- /// later. The handle is cheaply cloneable, so feel free to clone it for
- /// spawned tasks.
- async fn start(&mut self, handle: DaemonHandle) -> Result<()>;
-
- /// Handle an incoming event.
- ///
- /// Called for every event on the bus. To emit new events in response,
- /// use the handle stored during `start()`. Events emitted here will be
- /// processed in subsequent event loop iterations.
- async fn handle_event(&mut self, event: &DaemonEvent) -> Result<()>;
-
- /// Called on graceful shutdown.
- ///
- /// Use this to clean up resources, abort spawned tasks, etc.
- async fn stop(&mut self) -> Result<()>;
-}
-
-// ============================================================================
// Daemon
// ============================================================================
/// The main daemon orchestrator.
///
-/// The daemon manages components, runs the event loop, and coordinates startup
+/// The daemon runs the event loop, and coordinates startup
/// and shutdown. It is constructed via [`DaemonBuilder`].
///
/// # Event Loop
@@ -241,14 +162,11 @@ pub(crate) trait Component: Send + Sync {
/// The daemon runs a simple event loop:
///
/// 1. Wait for an event on the bus
-/// 2. Dispatch the event to all components (in registration order)
-/// 3. Components may emit new events in response
/// 4. Repeat until `ShutdownRequested` is received
///
/// Events emitted during handling are queued and processed in subsequent
/// iterations, ensuring the loop eventually drains.
pub(crate) struct Daemon {
- components: Vec<Box<dyn Component>>,
handle: DaemonHandle,
}
@@ -265,26 +183,8 @@ impl Daemon {
self.handle.clone()
}
- /// Start all components.
- ///
- /// This must be called before `run_event_loop()`. It initializes all
- /// registered components with the daemon handle.
- pub(crate) async fn start_components(&mut self) -> Result<()> {
- for component in &mut self.components {
- tracing::info!(component = component.name(), "starting component");
- component
- .start(self.handle.clone())
- .await
- .with_context(|| format!("failed to start component: {}", component.name()))?;
- }
- Ok(())
- }
-
/// Run the daemon event loop.
- ///
- /// This processes events until a [`ShutdownRequested`] event is received.
- /// Components must be started first via `start_components()`.
- pub(crate) async fn run_event_loop(&mut self) -> Result<()> {
+ pub(crate) async fn wait_for_shutdown(&mut self) -> Result<()> {
let mut event_rx = self.handle.subscribe();
loop {
match event_rx.recv().await {
@@ -293,8 +193,7 @@ impl Daemon {
break;
}
Ok(event) => {
- tracing::debug!(?event, "processing event");
- self.dispatch_event(&event).await;
+ tracing::debug!(?event, "event received");
}
Err(broadcast::error::RecvError::Lagged(n)) => {
tracing::warn!(
@@ -310,35 +209,6 @@ impl Daemon {
}
Ok(())
}
-
- /// Stop all components.
- ///
- /// This performs graceful shutdown of all components.
- pub(crate) async fn stop_components(&mut self) {
- for component in &mut self.components {
- tracing::info!(component = component.name(), "stopping component");
- if let Err(e) = component.stop().await {
- tracing::error!(
- component = component.name(),
- error = ?e,
- "error stopping component"
- );
- }
- }
- tracing::info!("all components stopped");
- }
-
- async fn dispatch_event(&mut self, event: &DaemonEvent) {
- for component in &mut self.components {
- if let Err(e) = component.handle_event(event).await {
- tracing::error!(
- component = component.name(),
- error = ?e,
- "error handling event"
- );
- }
- }
- }
}
// ============================================================================
@@ -353,9 +223,6 @@ impl Daemon {
/// let daemon = Daemon::builder(settings)
/// .store(store)
/// .history_db(history_db)
-/// .component(HistoryComponent::new())
-/// .component(SearchComponent::new())
-/// .component(SyncComponent::new())
/// .build()
/// .await?;
///
@@ -365,7 +232,6 @@ pub(crate) struct DaemonBuilder {
settings: Settings,
store: Option<SqliteStore>,
history_db: Option<HistoryDatabase>,
- components: Vec<Box<dyn Component>>,
}
impl DaemonBuilder {
@@ -375,7 +241,6 @@ impl DaemonBuilder {
settings,
store: None,
history_db: None,
- components: Vec::new(),
}
}
@@ -391,14 +256,6 @@ impl DaemonBuilder {
self
}
- /// Register a component.
- ///
- /// Components are started in registration order and stopped in reverse order.
- pub(crate) fn component(mut self, component: impl Component + 'static) -> Self {
- self.components.push(Box::new(component));
- self
- }
-
/// Build the daemon.
///
/// This loads the encryption key and creates the daemon state.
@@ -428,9 +285,6 @@ impl DaemonBuilder {
// Create the handle (just a reference to the state)
let handle = DaemonHandle { state };
- Ok(Daemon {
- components: self.components,
- handle,
- })
+ Ok(Daemon { handle })
}
}