Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/benshi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ benshi-core = { version = "0.1.0", path = "../core" }
benshi-daemon = { path = "../daemon" }
benshi-detect = { version = "0.1.0", path = "../detect" }
benshi-store = { version = "0.1.0", path = "../store" }
benshi-sync = { path = "../sync" }
benshi-tray = { path = "../tray" }
clap = { version = "4.6.7", features = ["derive"] }
tokio = { version = "1.53.1", features = ["macros", "rt-multi-thread", "signal"] }
Expand Down
2 changes: 2 additions & 0 deletions crates/benshi/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ async fn serve(voice: &Arc<Voice>) -> u8 {
use std::time::SystemTime;

use benshi_core::clock::SystemClock;
use benshi_daemon::accounts::OnAniList;
use benshi_daemon::config;
use benshi_daemon::paths::{DATABASE, Role, directory};
use benshi_daemon::recognition::Recogniser;
Expand Down Expand Up @@ -368,6 +369,7 @@ async fn serve(voice: &Arc<Voice>) -> u8 {
store,
SystemTime::now,
POLL_INTERVAL,
Arc::new(OnAniList::new(benshi_sync::anilist::transport::ANILIST)),
// Said as it happens: the socket goes on answering once detection
// has stopped, and the process does not end to say so. The
// supervisor deals with no task that ended until this returns, so it
Expand Down
6 changes: 6 additions & 0 deletions crates/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,9 @@ benshi-store = { path = "../store" }
socket2 = "0.6.5"
tempfile = "3.27.0"
tokio = { version = "1.53.1", features = ["test-util"] }

[target.'cfg(unix)'.dependencies]
rustix = { version = "1.1.4", features = ["termios"] }

[target.'cfg(unix)'.dev-dependencies]
rustix = { version = "1.1.4", features = ["termios", "pty"] }
44 changes: 33 additions & 11 deletions crates/cli/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
//!
//! A connection waits for its first answer until [`DEADLINE`] from the moment
//! it began to connect, and no longer. What follows the first answer of a
//! `watch` is a stream, which waits for as long as the user does.
//! `watch` is a stream, which waits for as long as the user does, and what
//! follows that of a `login` or a `logout` waits for as long as the daemon
//! takes, which may be as long as a person takes to unlock the secret store.

use std::io;
use std::path::{Path, PathBuf};
Expand Down Expand Up @@ -144,16 +146,36 @@ impl Daemon {
.await
.map_err(|_elapsed| did_not_answer(&self.socket))?;

match answered {
Ok(Some(answer)) => Ok(answer),
Ok(None) => bail!(CLOSED_WITHOUT_ANSWERING),
Err(failure) => match failure.downcast_ref::<io::Error>() {
Some(reset) if reset.kind() == io::ErrorKind::ConnectionReset => {
bail!("{CLOSED_WITHOUT_ANSWERING}: {reset}")
}
_ => Err(failure),
},
}
unless_closed(answered)
}

/// The answer that follows the first, failing when the daemon closed
/// instead.
///
/// Waits for as long as the daemon takes, for a request whose second
/// answer may wait on a person. The daemon gives each step of such a
/// request a deadline of its own.
///
/// # Errors
///
/// As [`Daemon::expect_answer`], and never that the daemon did not
/// answer in time.
pub async fn expect_outcome(&mut self) -> Result<Response> {
unless_closed(self.answer().await)
}
}

/// An answer, or that the daemon closed the connection without one.
fn unless_closed(answered: Result<Option<Response>>) -> Result<Response> {
match answered {
Ok(Some(answer)) => Ok(answer),
Ok(None) => bail!(CLOSED_WITHOUT_ANSWERING),
Err(failure) => match failure.downcast_ref::<io::Error>() {
Some(reset) if reset.kind() == io::ErrorKind::ConnectionReset => {
bail!("{CLOSED_WITHOUT_ANSWERING}: {reset}")
}
_ => Err(failure),
},
}
}

Expand Down
Loading
Loading