rusty_runner_api/api.rs
1//! This module defines the API query, request body and response body
2//! schema for this crate and its server by means of serde serializable
3//! and deserializable rust structs.
4
5use serde::{Deserialize, Serialize};
6use std::time::Duration;
7
8pub const VERSION: &str = env!("CARGO_PKG_VERSION");
9
10/// The json-response schema for `GET /api/info`.
11///
12/// # Serialized Example
13/// ```
14/// # let ser = r#"
15/// {
16/// "os_type": "Unix",
17/// "computer_name": "GLaDOS",
18/// "api_version": "2.0.0"
19/// }
20/// # "#;
21/// # let deser: rusty_runner_api::api::InfoResponse
22/// # = serde_json::from_str(ser).expect("failed parsing");
23/// # assert_eq!(deser.computer_name, "GLaDOS");
24/// # assert_eq!(deser.api_version, rusty_runner_api::api::VERSION);
25/// ```
26#[derive(Debug, Serialize, Deserialize)]
27pub struct InfoResponse {
28 /// The operating system type running.
29 pub os_type: OsType,
30 /// Any descriptive name of the runner.
31 pub computer_name: String,
32 /// The version of the api supported. Defined by [`VERSION`].
33 pub api_version: String,
34}
35
36/// The OS type as given by `#[cfg(windows)]` and `#[cfg(unix)]`.
37#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
38pub enum OsType {
39 Windows,
40 Unix,
41}
42
43/// The json-body schema for `POST /api/run`.
44///
45/// # Serialized Example
46/// ```
47/// # let ser = r#"
48/// {
49/// "command": "echo",
50/// "arguments": [
51/// "Hello",
52/// "World"
53/// ],
54/// "return_stderr": true,
55/// "return_stdout": false
56///}
57/// # "#;
58/// # let deser: rusty_runner_api::api::RunRequest
59/// # = serde_json::from_str(ser).expect("failed parsing");
60/// # assert_eq!(deser.command, "echo");
61/// ```
62#[derive(Debug, Serialize, Deserialize)]
63pub struct RunRequest {
64 /// The command as available on the path or a path to an executable.
65 pub command: String,
66 /// The arguments as passed to `tokio::process::Command::args`
67 ///
68 /// # Warning
69 /// [Raw args](https://doc.rust-lang.org/stable/std/os/windows/process/trait.CommandExt.html#tymethod.raw_arg)
70 /// are not supported.
71 /// Avoid `cmd.exe /C`!
72 pub arguments: Vec<String>,
73 /// `true` if the api should capture and return `stdout`. Defaults to `false`.
74 #[serde(default)]
75 pub return_stdout: bool,
76 /// `true` if the api should capture and return `stderr`. Defaults to `false`.
77 #[serde(default)]
78 pub return_stderr: bool,
79}
80
81/// The query schema for `POST /api/runscript`.
82///
83/// # Serialized Example
84/// ```
85/// # let ser = r#"
86/// interpreter=bash&return_stderr=true
87/// # "#;
88/// # let deser: rusty_runner_api::api::RunScriptQuery
89/// # = serde_urlencoded::from_str(ser.trim()).expect("failed parsing");
90/// # assert!(matches!(deser.interpreter, rusty_runner_api::api::ScriptInterpreter::Bash));
91/// ```
92#[derive(Debug, Serialize, Deserialize)]
93pub struct RunScriptQuery {
94 /// The script in the request body will be run by the given `interpreter`.
95 pub interpreter: ScriptInterpreter,
96 // Note, `serde` does not support proper flattening here, so this cannot be moved to a struct `OutputOptions`,
97 // <https://github.com/nox/serde_urlencoded/issues/33>.
98 /// `true` if the api should capture and return `stdout`. Defaults to `false`.
99 #[serde(default)]
100 pub return_stdout: bool,
101 /// `true` if the api should capture and return `stderr`. Defaults to `false`.
102 #[serde(default)]
103 pub return_stderr: bool,
104}
105
106/// The interpreter that the script will be called with.
107///
108/// Not all interpreters may be supported by any runner.
109#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
110#[serde(rename_all = "lowercase")]
111pub enum ScriptInterpreter {
112 Bash,
113 Cmd,
114 Powershell,
115}
116
117impl ScriptInterpreter {
118 /// Returns the default file extension.
119 #[must_use]
120 pub fn as_extension(&self) -> &'static str {
121 match self {
122 ScriptInterpreter::Bash => "sh",
123 ScriptInterpreter::Cmd => "bat",
124 ScriptInterpreter::Powershell => "ps1",
125 }
126 }
127}
128
129/// The json response format for `/api/run` and `/api/runscript`.
130///
131/// # Serialized Examples
132/// A completed command:
133/// ```
134/// # let ser = r#"
135/// {
136/// "id": 73001,
137/// "status": "Completed",
138/// "exit_code": 1,
139/// "time_taken": {
140/// "secs": 21,
141/// "nanos": 800000
142/// }
143/// }
144/// # "#;
145/// # let deser: rusty_runner_api::api::RunResponse
146/// # = serde_json::from_str(ser).expect("failed parsing");
147/// # assert!(matches!(deser.status, rusty_runner_api::api::RunStatus::Completed { .. }));
148/// ```
149/// A command that could not be executed:
150/// ```
151/// # let ser = r#"
152/// {
153/// "id": 1234567890,
154/// "status": "Failure",
155/// "reason": "Not supported"
156/// }
157/// # "#;
158/// # let deser: rusty_runner_api::api::RunResponse
159/// # = serde_json::from_str(ser).expect("failed parsing");
160/// # assert!(matches!(deser.status, rusty_runner_api::api::RunStatus::Failure { .. }));
161/// ```
162#[derive(Debug, Serialize, Deserialize)]
163pub struct RunResponse {
164 pub id: u64,
165 #[serde(flatten)]
166 pub status: RunStatus,
167}
168
169/// The outcome of a command.
170///
171/// If the command could be started, then this is a [`Completed`](RunStatus::Completed)
172/// even if the command itself exited non-successfully.
173/// Otherwise this is [`Failure`](RunStatus::Failure).
174#[derive(Debug, Serialize, Deserialize)]
175#[serde(tag = "status")]
176pub enum RunStatus {
177 /// Completely ran the command. The command may have succeeded of failed.
178 Completed {
179 /// Exit code of the command or -1001 if terminated by a signal.
180 /// This may get only return the least byte.
181 exit_code: i32,
182 /// The wall time it took to run.
183 time_taken: Duration,
184 /// If `return_stdout` is set, this returns the raw `stdout` bytes.
185 #[serde(skip_serializing_if = "Option::is_none")]
186 stdout: Option<Vec<u8>>,
187 /// If `return_stderr` is set, this returns the raw `stderr` bytes.
188 #[serde(skip_serializing_if = "Option::is_none")]
189 stderr: Option<Vec<u8>>,
190 },
191 /// Failed to run the command due to internal reasons.
192 /// Does not indicate a command that ran with a non-success exit code, but
193 /// rather that the command couldn't even be started.
194 Failure { reason: String },
195}