#![expect(unused_crate_dependencies, reason = "Didn't remove them yet")] use std::sync::Arc; use crate::aclient::record::sqlite_store::SqliteStore; use crate::aclient::settings::Settings; use crate::api::server::control::ControlService; use crate::{ aclient::database::ClientSqlite as HistoryDatabase, api::generated::history::history_server::HistoryServer, }; use eyre::Result; pub mod aclient; pub(crate) mod api; pub(crate) mod components; pub(crate) mod daemon; pub(crate) mod events; pub(crate) mod server; // Re-export core daemon types for convenience pub(crate) use daemon::Daemon; pub use events::DaemonEvent; // Re-export components pub(crate) use components::{HistoryComponent, SyncComponent}; /// Boot the daemon using the new component-based architecture. /// /// This creates a daemon with the standard components (history, search, sync), /// starts the gRPC server with their services, and runs the event loop. pub async fn boot( settings: Settings, store: SqliteStore, history_db: HistoryDatabase, ) -> Result<()> { // Create the components let history_component = Arc::new(HistoryComponent::new()); let sync_component = Arc::new(Box::new(&SyncComponent::new())); // Get the gRPC services before moving components into the daemon // (The services share state with the components via Arc) let history_service = HistoryServer::from_arc(Arc::clone(&history_component)); // Build the daemon let mut daemon = Daemon::builder(settings.clone()) .store(store) .history_db(history_db) .component(history_component) .component(sync_component) .build()?; let handle = daemon.handle(); let control_service = ControlService::new(handle.clone()); // Start all components first (so gRPC services can work) daemon.start_components().await?; // Spawn signal handler to emit ShutdownRequested on Ctrl+C/SIGTERM let signal_handle = handle.clone(); tokio::spawn(async move { shutdown_signal().await; tracing::info!("received shutdown signal"); signal_handle.shutdown(); }); server::run_grpc_server( &settings, history_service, control_service.into_server(), handle, )?; daemon.run_event_loop().await?; // After the event loop exited, we shut-down the components. daemon.stop_components().await; tracing::info!("daemon shut down complete"); Ok(()) } /// Wait for a shutdown signal (Ctrl+C or SIGTERM). #[cfg(unix)] async fn shutdown_signal() { let mut term = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()) .expect("failed to register sigterm handler"); let mut int = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::interrupt()) .expect("failed to register sigint handler"); tokio::select! { _ = term.recv() => {}, _ = int.recv() => {}, } }