1use ic_platform::pocket_ic::PocketIc;
2use ic_platform::principal::ExternalPrincipal as Principal;
3
4use super::{PocketIcInstance, PocketIcOperationError};
5
6#[non_exhaustive]
8#[derive(Debug, Eq, PartialEq)]
9pub enum CanisterInstallError {
10 CreateCanister {
12 label: Option<String>,
14 source: PocketIcOperationError,
16 },
17 AddCycles {
19 canister_id: Principal,
21 label: Option<String>,
23 source: PocketIcOperationError,
25 },
26 InstallCode {
28 canister_id: Principal,
30 label: Option<String>,
32 source: PocketIcOperationError,
34 },
35}
36
37pub struct StandaloneCanisterInstallError {
39 instance: Box<PocketIcInstance<PocketIc>>,
40 install_error: CanisterInstallError,
41}
42
43impl std::fmt::Display for CanisterInstallError {
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45 let (operation, canister_id, label, source) = match self {
46 Self::CreateCanister { label, source } => ("create_canister", None, label, source),
47 Self::AddCycles {
48 canister_id,
49 label,
50 source,
51 } => ("add_cycles", Some(canister_id), label, source),
52 Self::InstallCode {
53 canister_id,
54 label,
55 source,
56 } => ("install_canister", Some(canister_id), label, source),
57 };
58 write!(f, "{operation} failed")?;
59 if let Some(canister_id) = canister_id {
60 write!(f, " for canister {canister_id}")?;
61 }
62 if let Some(label) = label {
63 write!(f, " ({label})")?;
64 }
65 write!(f, ": {source}")
66 }
67}
68
69impl std::error::Error for CanisterInstallError {
70 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
71 let (Self::CreateCanister { source, .. }
72 | Self::AddCycles { source, .. }
73 | Self::InstallCode { source, .. }) = self;
74 Some(source)
75 }
76}
77
78impl StandaloneCanisterInstallError {
79 pub(super) fn new(
80 instance: PocketIcInstance<PocketIc>,
81 install_error: CanisterInstallError,
82 ) -> Self {
83 Self {
84 instance: Box::new(instance),
85 install_error,
86 }
87 }
88
89 #[must_use]
91 pub fn pocket_ic(&self) -> &PocketIc {
92 &self.instance.instance
93 }
94
95 #[must_use]
97 pub const fn install_error(&self) -> &CanisterInstallError {
98 &self.install_error
99 }
100
101 pub fn into_parts(self) -> (PocketIcInstance<PocketIc>, CanisterInstallError) {
103 (*self.instance, self.install_error)
104 }
105}
106
107impl std::fmt::Debug for StandaloneCanisterInstallError {
108 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109 f.debug_struct("StandaloneCanisterInstallError")
110 .field("install_error", &self.install_error)
111 .finish_non_exhaustive()
112 }
113}
114
115impl std::fmt::Display for StandaloneCanisterInstallError {
116 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117 self.install_error.fmt(f)
118 }
119}
120
121impl std::error::Error for StandaloneCanisterInstallError {
122 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
123 Some(&self.install_error)
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use ic_platform::principal::ExternalPrincipal as Principal;
130
131 use super::CanisterInstallError;
132
133 #[test]
134 fn labeled_install_error_display_includes_label() {
135 let err = CanisterInstallError::InstallCode {
136 canister_id: Principal::anonymous(),
137 label: Some("authority".into()),
138 source: super::PocketIcOperationError::new("trap"),
139 };
140 assert!(err.to_string().contains("(authority): trap"));
141 }
142}