Skip to main content

canic_host/icp/
error.rs

1use std::{error::Error, fmt};
2
3use super::model::{ICP_CLI_SUPPORTED_VERSION_RANGE, REQUIRED_ICP_CLI_VERSION};
4
5///
6/// IcpCommandError
7///
8
9#[derive(Debug)]
10pub enum IcpCommandError {
11    Io(std::io::Error),
12    MissingCli {
13        executable: String,
14    },
15    IncompatibleCliVersion {
16        executable: String,
17        found: String,
18    },
19    Failed {
20        command: String,
21        stderr: String,
22    },
23    Json {
24        command: String,
25        output: String,
26        source: serde_json::Error,
27    },
28    SnapshotIdUnavailable {
29        output: String,
30    },
31}
32
33impl fmt::Display for IcpCommandError {
34    // Render ICP CLI command failures with the command line and captured diagnostics.
35    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
36        match self {
37            Self::Io(err) => write!(formatter, "{err}"),
38            Self::MissingCli { executable } => {
39                write!(
40                    formatter,
41                    "icp-cli executable not found: {executable}\nrequired: icp-cli {ICP_CLI_SUPPORTED_VERSION_RANGE}\nnext: install icp-cli {REQUIRED_ICP_CLI_VERSION} from https://github.com/dfinity/icp-cli/releases/tag/v{REQUIRED_ICP_CLI_VERSION}, or pass top-level --icp <path>"
42                )
43            }
44            Self::IncompatibleCliVersion { executable, found } => {
45                write!(
46                    formatter,
47                    "unsupported icp-cli version for {executable}\nfound: {found}\nrequired: icp-cli {ICP_CLI_SUPPORTED_VERSION_RANGE}\nnext: install icp-cli {REQUIRED_ICP_CLI_VERSION} from https://github.com/dfinity/icp-cli/releases/tag/v{REQUIRED_ICP_CLI_VERSION}, or pass top-level --icp <path>"
48                )
49            }
50            Self::Failed { command, stderr } => {
51                write!(formatter, "icp command failed: {command}\n{stderr}")
52            }
53            Self::Json {
54                command,
55                output,
56                source,
57            } => {
58                write!(
59                    formatter,
60                    "could not parse icp json output for {command}: {source}\n{output}"
61                )
62            }
63            Self::SnapshotIdUnavailable { output } => {
64                write!(
65                    formatter,
66                    "could not parse snapshot id from icp output: {output}"
67                )
68            }
69        }
70    }
71}
72
73impl Error for IcpCommandError {
74    // Preserve the underlying I/O error as the source when command execution fails locally.
75    fn source(&self) -> Option<&(dyn Error + 'static)> {
76        match self {
77            Self::Io(err) => Some(err),
78            Self::Json { source, .. } => Some(source),
79            Self::Failed { .. }
80            | Self::IncompatibleCliVersion { .. }
81            | Self::MissingCli { .. }
82            | Self::SnapshotIdUnavailable { .. } => None,
83        }
84    }
85}
86
87impl From<std::io::Error> for IcpCommandError {
88    // Convert process-spawn failures into the shared ICP CLI command error type.
89    fn from(err: std::io::Error) -> Self {
90        Self::Io(err)
91    }
92}