use std::panic::{RefUnwindSafe, UnwindSafe};
use std::path::PathBuf;
use crate::session_id::SessionId;
macro_rules! unwind_safe {
($($t:ty),+ $(,)?) => {
$(
impl UnwindSafe for $t {}
impl RefUnwindSafe for $t {}
)+
};
}
#[ohno::error]
#[display("Attaching requires a console")]
pub(crate) struct NoConsoleError;
#[ohno::error]
#[display("dure run requires a command after --")]
pub(crate) struct EmptyCommandError;
#[ohno::error]
#[display("No live sessions")]
pub(crate) struct NoLiveSessionsError;
#[ohno::error]
#[display("Session {id} is not a live session")]
pub(crate) struct SessionNotFoundError {
id: u32,
}
impl SessionNotFoundError {
pub(crate) fn for_id(id: SessionId) -> Self {
Self::new(id.get())
}
}
#[ohno::error]
#[display("Timed out connecting to session {id}")]
pub(crate) struct ResumeTimeoutError {
id: u32,
}
impl ResumeTimeoutError {
pub(crate) fn for_id(id: SessionId) -> Self {
Self::new(id.get())
}
}
#[ohno::error]
#[display("Failed to attach to session {id}; it may still be running")]
pub(crate) struct AttachFailedError {
id: u32,
}
impl AttachFailedError {
pub(crate) fn for_id(id: SessionId) -> Self {
Self::new(id.get())
}
}
#[ohno::error]
#[display("Failed to terminate session {id}")]
pub(crate) struct KillFailedError {
id: u32,
}
impl KillFailedError {
pub(crate) fn for_id(id: SessionId) -> Self {
Self::new(id.get())
}
}
#[ohno::error]
#[display(
"Cannot start a durable session: this process belongs to a Windows job object that \
forbids breakaway, so the supervisor would be killed together with the launcher. \
Launch dure.exe directly instead of through a wrapper such as `cargo run`."
)]
pub(crate) struct BreakawayDeniedError;
#[ohno::error]
#[display("Failed to start the session")]
pub(crate) struct StartupFailedError;
#[ohno::error]
#[display("Failed to determine the current directory")]
pub(crate) struct CurrentDirectoryError;
#[ohno::error]
#[display("Could not canonicalize '{}'", path.display())]
pub(crate) struct CanonicalizeError {
path: PathBuf,
}
#[ohno::error]
#[display("Session store error")]
pub(crate) struct StoreError;
#[ohno::error]
#[display("Cannot prompt for a session id without a terminal stdin; use --id")]
pub(crate) struct PromptFailedError;
#[ohno::error]
#[display("Session taken by another client")]
pub(crate) struct DisplacedError;
#[ohno::error]
#[display("Failed to inspect supervisor process {pid}")]
pub(crate) struct InspectProcessError {
pid: u32,
}
#[ohno::error]
#[display("Platform operation failed")]
pub(crate) struct PalFailedError;
#[ohno::error]
#[display("Console relay failed")]
pub(crate) struct RelayFailedError;
#[ohno::error]
#[display("Invalid session id")]
pub(crate) struct InvalidSessionIdError;
unwind_safe!(
NoConsoleError,
EmptyCommandError,
NoLiveSessionsError,
SessionNotFoundError,
ResumeTimeoutError,
AttachFailedError,
KillFailedError,
BreakawayDeniedError,
StartupFailedError,
CurrentDirectoryError,
CanonicalizeError,
StoreError,
PromptFailedError,
DisplacedError,
InspectProcessError,
PalFailedError,
RelayFailedError,
InvalidSessionIdError,
);
impl InspectProcessError {
pub(crate) fn for_pid(pid: u32) -> Self {
Self::new(pid)
}
}
pub(crate) fn parse_prompted_id(line: &str) -> Result<SessionId, InvalidSessionIdError> {
let line = line.trim();
let id: u32 = line
.parse()
.map_err(|_error| InvalidSessionIdError::new())?;
SessionId::from_u32(id).ok_or_else(InvalidSessionIdError::new)
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use std::fmt::Debug;
use static_assertions::assert_impl_all;
use super::*;
assert_impl_all!(NoConsoleError: Send, Sync, Debug, UnwindSafe, RefUnwindSafe);
#[test]
fn parse_prompted_id_rejects_zero_and_garbage() {
parse_prompted_id("0").unwrap_err();
parse_prompted_id("nope").unwrap_err();
assert_eq!(parse_prompted_id(" 3\n").unwrap().get(), 3);
}
}