canic_host/icp/
command.rs1use std::{
2 path::{Path, PathBuf},
3 process::Command,
4};
5
6use super::{
7 error::IcpCommandError,
8 model::{IcpCli, LOCAL_NETWORK, LocalReplicaTarget},
9 version::compatible_version_output,
10};
11
12impl IcpCli {
13 #[must_use]
15 pub fn new(
16 executable: impl Into<String>,
17 environment: Option<String>,
18 network: Option<String>,
19 ) -> Self {
20 Self {
21 executable: executable.into(),
22 environment,
23 network,
24 cwd: None,
25 local_replica: None,
26 }
27 }
28
29 #[must_use]
31 pub fn with_cwd(mut self, cwd: impl Into<PathBuf>) -> Self {
32 self.cwd = Some(cwd.into());
33 self
34 }
35
36 #[must_use]
38 pub fn with_local_replica(mut self, target: Option<LocalReplicaTarget>) -> Self {
39 self.local_replica = target;
40 self
41 }
42
43 #[must_use]
45 pub fn environment(&self) -> Option<&str> {
46 self.environment.as_deref()
47 }
48
49 #[must_use]
51 pub fn network(&self) -> Option<&str> {
52 self.network.as_deref()
53 }
54
55 #[must_use]
57 pub fn command(&self) -> Command {
58 let mut command = Command::new(&self.executable);
59 if let Some(cwd) = &self.cwd {
60 command.current_dir(cwd);
61 add_project_root_override_arg(&mut command, cwd);
62 }
63 command
64 }
65
66 #[must_use]
68 pub fn command_in(&self, cwd: &Path) -> Command {
69 let mut command = Command::new(&self.executable);
70 command.current_dir(cwd);
71 add_project_root_override_arg(&mut command, cwd);
72 command
73 }
74
75 #[must_use]
77 pub fn canister_command(&self) -> Command {
78 let mut command = self.command();
79 command.arg("canister");
80 command
81 }
82
83 pub(super) fn add_target_args(&self, command: &mut Command) {
84 add_target_args(
85 command,
86 self.environment(),
87 self.network(),
88 self.local_replica.as_ref(),
89 );
90 }
91
92 pub(super) fn add_local_network_target(&self, command: &mut Command) {
93 if let Some(environment) = self.environment() {
94 command.args(["-e", environment]);
95 } else if let Some(network) = self.network() {
96 command.arg(network);
97 } else {
98 command.arg(LOCAL_NETWORK);
99 }
100 }
101}
102
103#[must_use]
105pub fn default_command() -> Command {
106 IcpCli::new("icp", None, None).command()
107}
108
109#[must_use]
111pub fn default_command_in(cwd: &Path) -> Command {
112 IcpCli::new("icp", None, None).command_in(cwd)
113}
114
115pub fn add_target_args(
117 command: &mut Command,
118 environment: Option<&str>,
119 network: Option<&str>,
120 local_replica: Option<&LocalReplicaTarget>,
121) {
122 if let Some(environment) = environment {
123 if environment == LOCAL_NETWORK
124 && let Some(local_replica) = local_replica
125 {
126 command.env_remove("ICP_ENVIRONMENT");
127 command
128 .arg("-n")
129 .arg(&local_replica.url)
130 .arg("-k")
131 .arg(&local_replica.root_key);
132 return;
133 }
134 command.args(["-e", environment]);
135 } else if let Some(network) = network {
136 command.args(["-n", network]);
137 }
138}
139
140pub fn add_output_arg(command: &mut Command, output: &str) {
142 if output == "json" {
143 command.arg("--json");
144 } else {
145 command.args(["--output", output]);
146 }
147}
148
149pub fn add_candid_arg(command: &mut Command, candid_path: Option<&Path>) {
151 if let Some(candid_path) = candid_path {
152 command.arg("--candid").arg(candid_path);
153 }
154}
155
156#[must_use]
158pub fn local_canister_candid_path(icp_root: &Path, environment: &str, role: &str) -> PathBuf {
159 icp_root
160 .join(".icp")
161 .join(environment)
162 .join("canisters")
163 .join(role)
164 .join(format!("{role}.did"))
165}
166
167#[must_use]
169pub fn existing_local_canister_candid_path(
170 icp_root: &Path,
171 environment: &str,
172 role: &str,
173) -> Option<PathBuf> {
174 let path = local_canister_candid_path(icp_root, environment, role);
175 path.is_file().then_some(path)
176}
177
178pub fn add_debug_arg(command: &mut Command, debug: bool) {
180 if debug {
181 command.arg("--debug");
182 }
183}
184
185pub fn ensure_command_compatible(command: &Command) -> Result<(), IcpCommandError> {
187 let executable = command.get_program().to_string_lossy();
188 compatible_version_output(executable.as_ref(), command.get_current_dir()).map(|_| ())
189}
190
191fn add_project_root_override_arg(command: &mut Command, cwd: &Path) {
192 command.arg("--project-root-override").arg(cwd);
193}
194
195#[must_use]
197pub fn command_display(command: &Command) -> String {
198 let mut parts = vec![command.get_program().to_string_lossy().to_string()];
199 parts.extend(
200 command
201 .get_args()
202 .map(|arg| arg.to_string_lossy().to_string()),
203 );
204 parts.join(" ")
205}