ic_testkit/pocket_ic/
error.rs1use reqwest::StatusCode;
4
5#[derive(Clone, Debug, PartialEq, Eq)]
7pub struct Operation {
8 pub state_label: String,
9 pub op_id: String,
10}
11
12#[derive(Clone, Debug)]
14pub struct RequestContext {
15 pub url: String,
16 pub operation: Option<Operation>,
17 pub ingress: Option<crate::pocket_ic::common::rest::RawMessageId>,
19 pub may_have_executed: bool,
21 pub status: Option<StatusCode>,
22}
23
24#[derive(Debug, thiserror::Error)]
26pub enum ClientError {
27 #[error(
28 "PocketIC requires an explicit server URL; provision/start it through ic_testkit::pic or supply a running server"
29 )]
30 MissingServerUrl,
31 #[error("PocketIC transport failed: {source}")]
32 Transport {
33 context: Box<RequestContext>,
34 #[source]
35 source: reqwest::Error,
36 },
37 #[error("PocketIC response body failed: {source}")]
38 Body {
39 context: Box<RequestContext>,
40 #[source]
41 source: reqwest::Error,
42 },
43 #[error("PocketIC response JSON failed: {source}")]
44 Json {
45 context: Box<RequestContext>,
46 #[source]
47 source: serde_json::Error,
48 },
49 #[error("PocketIC server returned {status}: {message}")]
50 Server {
51 context: Box<RequestContext>,
52 status: StatusCode,
53 message: String,
54 },
55 #[error("PocketIC request deadline expired")]
56 Timeout { context: Box<RequestContext> },
57 #[error("PocketIC response exceeds {limit} bytes")]
58 ResponseTooLarge {
59 context: Box<RequestContext>,
60 limit: usize,
61 },
62 #[error("canister rejected the call: {0:?}")]
63 Canister(crate::pocket_ic::RejectResponse),
64 #[error("Candid failed: {0}")]
65 Candid(#[from] candid::Error),
66 #[error("CBOR failed: {0}")]
67 Cbor(#[from] serde_cbor::Error),
68 #[error("I/O failed: {0}")]
69 Io(#[from] std::io::Error),
70 #[error("invalid PocketIC response: {0}")]
71 Protocol(String),
72}
73
74impl ClientError {
75 pub(crate) fn with_ingress(
76 mut self,
77 ingress: crate::pocket_ic::common::rest::RawMessageId,
78 ) -> Self {
79 match &mut self {
80 Self::Transport { context, .. }
81 | Self::Body { context, .. }
82 | Self::Json { context, .. }
83 | Self::Server { context, .. }
84 | Self::ResponseTooLarge { context, .. }
85 | Self::Timeout { context } => context.ingress = Some(ingress),
86 _ => {}
87 }
88 self
89 }
90
91 pub fn request_context(&self) -> Option<&RequestContext> {
92 match self {
93 Self::Transport { context, .. }
94 | Self::Body { context, .. }
95 | Self::Json { context, .. }
96 | Self::Server { context, .. }
97 | Self::ResponseTooLarge { context, .. }
98 | Self::Timeout { context } => Some(context),
99 _ => None,
100 }
101 }
102}
103
104impl From<crate::pocket_ic::RejectResponse> for ClientError {
105 fn from(value: crate::pocket_ic::RejectResponse) -> Self {
106 Self::Canister(value)
107 }
108}