use std::panic::{RefUnwindSafe, UnwindSafe};
use std::path::{Path, PathBuf};
use crate::SessionId;
use crate::protocol::StartupStep;
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 to execute")]
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 start the session while {step}")]
pub(crate) struct StartupStepFailedError {
step: &'static str,
}
impl StartupStepFailedError {
pub(crate) fn at(step: StartupStep) -> Self {
Self::new(step.describe())
}
}
#[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("'{}' is not a path dure can use; it is not valid Unicode", path.display())]
pub(crate) struct UnsupportedPathError {
path: PathBuf,
}
impl UnsupportedPathError {
pub(crate) fn for_path(path: &Path) -> Self {
Self::new(path.to_path_buf())
}
}
#[ohno::error]
#[display("Session store error")]
pub(crate) struct StoreError;
#[ohno::error]
#[display("Cannot prompt for a session id without a terminal stdin; run `dure resume <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("Lost the session before the app reported an exit status")]
pub(crate) struct SupervisorLostError;
#[ohno::error]
#[display("Failed to restore the console; run `cmd /c cls` or open a new terminal")]
pub(crate) struct ConsoleRestoreError;
#[ohno::error]
#[display(
"Session {id} was started by a different version of dure and cannot be resumed by this one; \
use `dure kill {id}` to end it"
)]
pub(crate) struct ProtocolMismatchError {
id: u32,
}
impl ProtocolMismatchError {
pub(crate) fn for_id(id: SessionId) -> Self {
Self::new(id.get())
}
}
#[ohno::error]
#[display("Failed to write command output")]
pub(crate) struct OutputFailedError;
#[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,
SupervisorLostError,
ConsoleRestoreError,
OutputFailedError,
ProtocolMismatchError,
StartupStepFailedError,
UnsupportedPathError,
InvalidSessionIdError,
);
impl InspectProcessError {
pub(crate) fn for_pid(pid: u32) -> Self {
Self::new(pid)
}
}
#[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);
}