1use serde_json::Value;
4use std::{future::Future, time::Duration};
5
6#[derive(Clone)]
8pub struct Error {
9 pub code: String,
10 pub message: String,
11 pub host_message: Option<Box<str>>,
14 pub details: Option<Box<Value>>,
15 pub operation_id: Option<Box<str>>,
16 pub rpc_code: Option<i64>,
17 pub exit_code: Option<Box<i64>>,
18 pub outcome_unknown: bool,
20}
21
22impl Error {
23 pub fn new(code: impl Into<String>, message: impl Into<String>) -> Self {
24 Self {
25 code: code.into(),
26 message: message.into(),
27 host_message: None,
28 details: None,
29 operation_id: None,
30 rpc_code: None,
31 exit_code: None,
32 outcome_unknown: false,
33 }
34 }
35
36 pub fn decode(cause: serde_json::Error) -> Self {
37 let mut error = Self::new(
38 "HOST_PROTOCOL_ERROR",
39 "Host result does not match its declared response type",
40 );
41 error.details = Some(Box::new(
42 serde_json::json!({"decodeError":cause.to_string()}),
43 ));
44 error
45 }
46}
47
48impl std::fmt::Display for Error {
49 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50 write!(formatter, "{}: {}", self.code, self.message)
51 }
52}
53impl std::error::Error for Error {}
54
55impl std::fmt::Debug for Error {
56 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 formatter
58 .debug_struct("Error")
59 .field("code", &self.code)
60 .field("operation_id", &self.operation_id)
61 .field("rpc_code", &self.rpc_code)
62 .field("exit_code", &self.exit_code)
63 .field("outcome_unknown", &self.outcome_unknown)
64 .finish_non_exhaustive()
65 }
66}
67
68pub type Result<T> = std::result::Result<T, Error>;
69
70#[derive(Clone, Default)]
71pub struct InvokeOptions {
72 pub timeout: Option<Duration>,
73 pub expected_revision: Option<String>,
74 pub change_mode: Option<String>,
75 pub dry_run: Option<bool>,
76 pub stdin_bytes: Option<Vec<u8>>,
77}
78
79impl std::fmt::Debug for InvokeOptions {
80 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81 formatter
82 .debug_struct("InvokeOptions")
83 .field("timeout", &self.timeout)
84 .field("has_stdin", &self.stdin_bytes.is_some())
85 .finish_non_exhaustive()
86 }
87}
88
89pub trait Invoker: Sync {
92 fn handle_decode_error(&self, error: Error) -> Error {
93 error
94 }
95
96 fn invoke(
97 &self,
98 operation_id: &str,
99 params: Value,
100 options: InvokeOptions,
101 ) -> impl Future<Output = Result<Value>> + Send;
102}