aboutsummaryrefslogtreecommitdiffstats
path: root/crates/daemon/src/client.rs
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2026-07-20 14:18:36 +0200
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2026-07-20 14:18:36 +0200
commit63a0ec3901a863fb07d18f4b814a98a813644382 (patch)
treec94b7b501601b5df05cf654e9f9c9b5bc66a239e /crates/daemon/src/client.rs
parentchore: Commit (diff)
downloadatuin-63a0ec3901a863fb07d18f4b814a98a813644382.zip
chore: Commit
Diffstat (limited to '')
-rw-r--r--crates/daemon/src/api/client/mod.rs (renamed from crates/daemon/src/client.rs)140
1 files changed, 32 insertions, 108 deletions
diff --git a/crates/daemon/src/client.rs b/crates/daemon/src/api/client/mod.rs
index 5f8ea0f2..d6cbbe85 100644
--- a/crates/daemon/src/client.rs
+++ b/crates/daemon/src/api/client/mod.rs
@@ -8,30 +8,33 @@ use hyper_util::rt::TokioIo;
#[cfg(unix)]
use tokio::net::UnixStream;
-use crate::aclient::{history::History, settings::Settings};
-use crate::generated;
use crate::{
- events::DaemonEvent,
- generated::{
- control::{
- ForceSyncEvent, HistoryDeletedEvent, HistoryPrunedEvent, HistoryRebuiltEvent,
- SendEventRequest, SettingsReloadedEvent, ShutdownEvent,
- control_client::ControlClient as ControlServiceClient,
- },
- history::{
- EndHistoryReply, EndHistoryRequest, ShutdownRequest, StartHistoryReply,
- StartHistoryRequest, StatusReply, StatusRequest, TailHistoryReply, TailHistoryRequest,
- history_client::HistoryClient as HistoryServiceClient,
- },
- semantic::{
- CommandCapture, RecordCommandsReply,
- semantic_client::SemanticClient as SemanticServiceClient,
+ aclient::{history::History, settings::Settings},
+ api::{
+ DAEMON_PROTOCOL_VERSION, DAEMON_VERSION,
+ generated::{
+ control::{
+ StatusReply, StatusRequest, control_client::ControlClient as ControlServiceClient,
+ },
+ history::{
+ EndHistoryReply, EndHistoryRequest, StartHistoryReply, StartHistoryRequest,
+ TailHistoryReply, TailHistoryRequest,
+ history_client::HistoryClient as HistoryServiceClient,
+ },
},
},
};
-pub struct HistoryClient {
- client: HistoryServiceClient<Channel>,
+fn daemon_matches_expected(version: &str, protocol: u32) -> bool {
+ version == DAEMON_VERSION && protocol == DAEMON_PROTOCOL_VERSION
+}
+
+fn daemon_mismatch_message(version: &str, protocol: u32) -> String {
+ if protocol == DAEMON_PROTOCOL_VERSION {
+ format!("daemon is out of date: expected {DAEMON_VERSION}, got {version}")
+ } else {
+ format!("daemon protocol mismatch: expected {DAEMON_PROTOCOL_VERSION}, got {protocol}")
+ }
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -61,6 +64,14 @@ pub fn classify_error(error: &eyre::Report) -> DaemonClientErrorKind {
DaemonClientErrorKind::Other
}
+// ============================================================================
+// History Client
+// ============================================================================
+
+pub struct HistoryClient {
+ client: HistoryServiceClient<Channel>,
+}
+
// Wrap the grpc client
impl HistoryClient {
#[cfg(unix)]
@@ -114,10 +125,6 @@ impl HistoryClient {
Ok(self.client.end_history(req).await?.into_inner())
}
- pub async fn status(&mut self) -> Result<StatusReply> {
- Ok(self.client.status(StatusRequest {}).await?.into_inner())
- }
-
pub(crate) async fn tail_history(&mut self) -> Result<tonic::Streaming<TailHistoryReply>> {
Ok(self
.client
@@ -125,54 +132,6 @@ impl HistoryClient {
.await?
.into_inner())
}
-
- pub async fn shutdown(&mut self) -> Result<bool> {
- let resp = self.client.shutdown(ShutdownRequest {}).await?.into_inner();
- Ok(resp.accepted)
- }
-}
-
-pub(crate) struct SemanticClient {
- client: SemanticServiceClient<Channel>,
-}
-
-impl SemanticClient {
- #[cfg(unix)]
- pub(crate) async fn new(path: String) -> Result<Self> {
- let log_path = path.clone();
- let channel = Endpoint::try_from("http://atuin_local_daemon:0")?
- .connect_with_connector(service_fn(move |_: Uri| {
- let path = path.clone();
-
- async move {
- Ok::<_, std::io::Error>(TokioIo::new(UnixStream::connect(path.clone()).await?))
- }
- }))
- .await
- .wrap_err_with(|| {
- format!(
- "failed to connect to local atuin daemon at {}. Is it running?",
- &log_path
- )
- })?;
-
- let client = SemanticServiceClient::new(channel);
-
- Ok(Self { client })
- }
-
- #[cfg(unix)]
- pub(crate) async fn from_settings(settings: &Settings) -> Result<Self> {
- Self::new(settings.daemon.socket_path.clone()).await
- }
-
- pub(crate) async fn record_commands(
- &mut self,
- captures: Vec<CommandCapture>,
- ) -> Result<RecordCommandsReply> {
- let stream = tokio_stream::iter(captures);
- Ok(self.client.record_commands(stream).await?.into_inner())
- }
}
// ============================================================================
@@ -180,15 +139,12 @@ impl SemanticClient {
// ============================================================================
/// Client for the Control gRPC service.
-///
-/// Used to inject events into a running daemon from external processes.
pub struct ControlClient {
client: ControlServiceClient<Channel>,
}
impl ControlClient {
/// Connect to the daemon's control service.
- #[cfg(unix)]
pub(crate) async fn new(path: String) -> Result<Self> {
let log_path = path.clone();
let channel = Endpoint::try_from("http://atuin_local_daemon:0")?
@@ -218,39 +174,7 @@ impl ControlClient {
Self::new(settings.daemon.socket_path.clone()).await
}
- /// Send an event to the daemon.
- pub async fn send_event(&mut self, event: DaemonEvent) -> Result<()> {
- let proto_event = daemon_event_to_proto(event);
- let request = SendEventRequest {
- event: Some(proto_event),
- };
- self.client.send_event(request).await?;
- Ok(())
- }
-}
-
-/// Convert a daemon event to its proto representation.
-fn daemon_event_to_proto(event: DaemonEvent) -> generated::control::send_event_request::Event {
- use generated::control::send_event_request::Event;
-
- match event {
- DaemonEvent::HistoryPruned => Event::HistoryPruned(HistoryPrunedEvent {}),
- DaemonEvent::HistoryRebuilt => Event::HistoryRebuilt(HistoryRebuiltEvent {}),
- DaemonEvent::HistoryDeleted { ids } => Event::HistoryDeleted(HistoryDeletedEvent {
- ids: ids.into_iter().map(|id| id.0).collect(),
- }),
- DaemonEvent::ForceSync => Event::ForceSync(ForceSyncEvent {}),
- DaemonEvent::SettingsReloaded => Event::SettingsReloaded(SettingsReloadedEvent {}),
- DaemonEvent::ShutdownRequested => Event::Shutdown(ShutdownEvent {}),
- // These events are internal and not sent via the control service
- DaemonEvent::HistoryStarted(_)
- | DaemonEvent::HistoryEnded(_)
- | DaemonEvent::RecordsAdded(_)
- | DaemonEvent::SyncCompleted { .. }
- | DaemonEvent::SyncFailed { .. } => {
- // Use shutdown as a fallback, though this shouldn't happen
- tracing::warn!("attempted to send internal event via control service");
- Event::Shutdown(ShutdownEvent {})
- }
+ pub async fn status(&mut self) -> Result<StatusReply> {
+ Ok(self.client.status(StatusRequest {}).await?.into_inner())
}
}