use std::{
path::{Path, PathBuf},
process::Command,
};
use super::{
error::IcpCommandError,
model::{IcpCli, LOCAL_NETWORK, LocalReplicaTarget},
version::compatible_version_output,
};
impl IcpCli {
#[must_use]
pub fn new(
executable: impl Into<String>,
environment: Option<String>,
network: Option<String>,
) -> Self {
Self {
executable: executable.into(),
environment,
network,
cwd: None,
local_replica: None,
}
}
#[must_use]
pub fn with_cwd(mut self, cwd: impl Into<PathBuf>) -> Self {
self.cwd = Some(cwd.into());
self
}
#[must_use]
pub fn with_local_replica(mut self, target: Option<LocalReplicaTarget>) -> Self {
self.local_replica = target;
self
}
#[must_use]
pub fn environment(&self) -> Option<&str> {
self.environment.as_deref()
}
#[must_use]
pub fn network(&self) -> Option<&str> {
self.network.as_deref()
}
#[must_use]
pub fn command(&self) -> Command {
let mut command = Command::new(&self.executable);
if let Some(cwd) = &self.cwd {
command.current_dir(cwd);
add_project_root_override_arg(&mut command, cwd);
}
command
}
#[must_use]
pub fn command_in(&self, cwd: &Path) -> Command {
let mut command = Command::new(&self.executable);
command.current_dir(cwd);
add_project_root_override_arg(&mut command, cwd);
command
}
#[must_use]
pub fn canister_command(&self) -> Command {
let mut command = self.command();
command.arg("canister");
command
}
pub(super) fn add_target_args(&self, command: &mut Command) {
add_target_args(
command,
self.environment(),
self.network(),
self.local_replica.as_ref(),
);
}
pub(super) fn add_local_network_target(&self, command: &mut Command) {
if let Some(environment) = self.environment() {
command.args(["-e", environment]);
} else if let Some(network) = self.network() {
command.arg(network);
} else {
command.arg(LOCAL_NETWORK);
}
}
}
#[must_use]
pub fn default_command() -> Command {
IcpCli::new("icp", None, None).command()
}
#[must_use]
pub fn default_command_in(cwd: &Path) -> Command {
IcpCli::new("icp", None, None).command_in(cwd)
}
pub fn add_target_args(
command: &mut Command,
environment: Option<&str>,
network: Option<&str>,
local_replica: Option<&LocalReplicaTarget>,
) {
if let Some(environment) = environment {
if environment == LOCAL_NETWORK
&& let Some(local_replica) = local_replica
{
command.env_remove("ICP_ENVIRONMENT");
command
.arg("-n")
.arg(&local_replica.url)
.arg("-k")
.arg(&local_replica.root_key);
return;
}
command.args(["-e", environment]);
} else if let Some(network) = network {
command.args(["-n", network]);
}
}
pub fn add_output_arg(command: &mut Command, output: &str) {
if output == "json" {
command.arg("--json");
} else {
command.args(["--output", output]);
}
}
pub fn add_candid_arg(command: &mut Command, candid_path: Option<&Path>) {
if let Some(candid_path) = candid_path {
command.arg("--candid").arg(candid_path);
}
}
#[must_use]
pub fn local_canister_candid_path(icp_root: &Path, environment: &str, role: &str) -> PathBuf {
icp_root
.join(".icp")
.join(environment)
.join("canisters")
.join(role)
.join(format!("{role}.did"))
}
#[must_use]
pub fn existing_local_canister_candid_path(
icp_root: &Path,
environment: &str,
role: &str,
) -> Option<PathBuf> {
let path = local_canister_candid_path(icp_root, environment, role);
path.is_file().then_some(path)
}
pub fn add_debug_arg(command: &mut Command, debug: bool) {
if debug {
command.arg("--debug");
}
}
pub fn ensure_command_compatible(command: &Command) -> Result<(), IcpCommandError> {
let executable = command.get_program().to_string_lossy();
compatible_version_output(executable.as_ref(), command.get_current_dir()).map(|_| ())
}
fn add_project_root_override_arg(command: &mut Command, cwd: &Path) {
command.arg("--project-root-override").arg(cwd);
}
#[must_use]
pub fn command_display(command: &Command) -> String {
let mut parts = vec![command.get_program().to_string_lossy().to_string()];
parts.extend(
command
.get_args()
.map(|arg| arg.to_string_lossy().to_string()),
);
parts.join(" ")
}