Skip to main content

dvm_run/
error.rs

1use std::path::PathBuf;
2
3use dvm_api::{CommandNumber, RunId, RunState, SandboxId};
4
5pub type Result<T, E = Error> = std::result::Result<T, E>;
6
7/// What the SDK returns when a call fails. A command's exit code is never
8/// one of these.
9#[derive(Debug, thiserror::Error)]
10#[non_exhaustive]
11pub enum Error {
12    /// 400: the request isn't one dvm.run takes, such as an image that
13    /// isn't one.
14    #[error("400: {reason}")]
15    InvalidRequest { reason: String },
16    /// 401: the API key was revoked, or isn't one.
17    #[error("401: {reason}")]
18    Authentication { reason: String },
19    /// 404: no such sandbox, command, execution or replay, or not the key's
20    /// user's.
21    #[error("404: {reason}")]
22    NotFound { reason: String },
23    /// 409: what was asked can't be done in the state it's in.
24    #[error("409: {reason}")]
25    Conflict { reason: String },
26    /// Another refusal: its status, and dvm.run's reason.
27    #[error("{status}: {reason}")]
28    Refused { status: u16, reason: String },
29    /// The sandbox ended (stopped, or its command exited) before the call
30    /// could act on it.
31    #[error("sandbox {sandbox} is {}", state.kind())]
32    SandboxEnded { sandbox: SandboxId, state: RunState },
33    /// dvm or the platform ended the sandbox, and why: a syscall dvm can't
34    /// serve, a limit, its runner going away.
35    #[error("sandbox {sandbox} failed: {reason}")]
36    SandboxFailed { sandbox: SandboxId, reason: String },
37    /// The sandbox didn't take the command, and why: a script in an image
38    /// with no shell, a command too long.
39    #[error("command {number} was refused: {reason}")]
40    CommandRefused {
41        number: CommandNumber,
42        reason: String,
43    },
44    /// A replay ended without a verdict: dvm saw it part from its execution
45    /// and failed it, with where, or it was stopped.
46    #[error("replay {replay} {}{}", state.kind(), failure_of(state))]
47    ReplayEnded { replay: RunId, state: RunState },
48    /// No key was given, in DVM_API_KEY, or saved by `dvm-run login`.
49    #[error("no API key for {api_url}: run `dvm-run login`, or set DVM_API_KEY")]
50    NoApiKey { api_url: String },
51    /// The saved keys couldn't be read or written.
52    #[error("{}: {problem}", path.display())]
53    Credentials { path: PathBuf, problem: String },
54    /// dvm.run couldn't be reached, or the connection to it dropped.
55    #[error("{context}")]
56    Network {
57        context: String,
58        #[source]
59        source: Option<reqwest::Error>,
60    },
61    /// dvm.run answered what the SDK can't read.
62    #[error("{0}")]
63    Unexpected(String),
64}
65
66impl Error {
67    /// The refusal a response's status and reason are.
68    pub(crate) fn refusal(status: u16, reason: String) -> Self {
69        match status {
70            400 => Self::InvalidRequest { reason },
71            401 => Self::Authentication { reason },
72            404 => Self::NotFound { reason },
73            409 => Self::Conflict { reason },
74            status => Self::Refused { status, reason },
75        }
76    }
77
78    /// The error a call to a sandbox that ended in `state` is.
79    pub(crate) fn ended(sandbox: SandboxId, state: RunState) -> Self {
80        match state {
81            RunState::Failed { reason } => Self::SandboxFailed { sandbox, reason },
82            state => Self::SandboxEnded { sandbox, state },
83        }
84    }
85
86    pub(crate) fn network(context: impl Into<String>, source: reqwest::Error) -> Self {
87        Self::Network {
88            context: context.into(),
89            source: Some(source),
90        }
91    }
92}
93
94fn failure_of(state: &RunState) -> String {
95    match state {
96        RunState::Failed { reason } => format!(": {reason}"),
97        _ => String::new(),
98    }
99}