Skip to content
35 changes: 35 additions & 0 deletions crates/icp-cli/src/commands/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,41 @@ impl CanisterCommandArgs {
}
}

// Common argument used for Token and Cycles commands
#[derive(Args, Clone, Debug)]
pub(crate) struct TokenCommandArgs {
#[command(flatten)]
pub(crate) network: NetworkOpt,

#[command(flatten)]
pub(crate) environment: EnvironmentOpt,

#[command(flatten)]
pub(crate) identity: IdentityOpt,
}

/// Selections derived from TokenCommandArgs
pub(crate) struct TokenCommandSelections {
pub(crate) environment: EnvironmentSelection,
pub(crate) network: NetworkSelection,
pub(crate) identity: IdentitySelection,
}

impl TokenCommandArgs {
/// Convert command arguments into selection enums
pub(crate) fn selections(&self) -> TokenCommandSelections {
let environment_selection: EnvironmentSelection = self.environment.clone().into();
let network_selection: NetworkSelection = self.network.clone().into();
let identity_selection: IdentitySelection = self.identity.clone().into();

TokenCommandSelections {
environment: environment_selection,
network: network_selection,
identity: identity_selection,
}
}
}

#[derive(Clone, Debug, PartialEq)]
pub(crate) enum Canister {
Name(String),
Expand Down
21 changes: 15 additions & 6 deletions crates/icp-cli/src/commands/canister/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use candid::IDLArgs;
use clap::Args;
use dialoguer::console::Term;

use icp::context::Context;
use icp::context::{Context, GetAgentError, GetCanisterIdError};

use crate::commands::args;

Expand Down Expand Up @@ -35,18 +35,27 @@ pub(crate) enum CommandError {
Call(#[from] ic_agent::AgentError),

#[error(transparent)]
GetCanisterIdAndAgent(#[from] icp::context::GetCanisterIdAndAgentError),
GetAgent(#[from] GetAgentError),

#[error(transparent)]
GetCanisterId(#[from] GetCanisterIdError),
}

pub(crate) async fn exec(ctx: &Context, args: &CallArgs) -> Result<(), CommandError> {
let selections = args.cmd_args.selections();

let (cid, agent) = ctx
.get_canister_id_and_agent(
&selections.canister,
let agent = ctx
.get_agent(
&selections.identity,
&selections.network,
&selections.environment,
)
.await?;
let cid = ctx
.get_canister_id(
&selections.canister,
&selections.network,
&selections.identity,
&selections.environment,
)
.await?;

Expand Down
5 changes: 4 additions & 1 deletion crates/icp-cli/src/commands/canister/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,10 @@ pub(crate) async fn exec(ctx: &Context, args: &CreateArgs) -> Result<(), Command
let (_, canister_info) = env.get_canister_info(&canister).map_err(|e| anyhow!(e))?;

if ctx
.get_canister_id_for_env(&canister, &selections.environment)
.get_canister_id_for_env(
&icp::context::CanisterSelection::Named(canister.clone()),
&selections.environment,
)
.await
.is_ok()
{
Expand Down
21 changes: 15 additions & 6 deletions crates/icp-cli/src/commands/canister/delete.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use clap::Args;
use ic_agent::AgentError;

use icp::context::Context;
use icp::context::{Context, GetAgentError, GetCanisterIdError};

use crate::commands::args;

Expand All @@ -17,18 +17,27 @@ pub(crate) enum CommandError {
Delete(#[from] AgentError),

#[error(transparent)]
GetCanisterIdAndAgent(#[from] icp::context::GetCanisterIdAndAgentError),
GetAgent(#[from] GetAgentError),

#[error(transparent)]
GetCanisterId(#[from] GetCanisterIdError),
}

pub(crate) async fn exec(ctx: &Context, args: &DeleteArgs) -> Result<(), CommandError> {
let selections = args.cmd_args.selections();

let (cid, agent) = ctx
.get_canister_id_and_agent(
&selections.canister,
let agent = ctx
.get_agent(
&selections.identity,
&selections.network,
&selections.environment,
)
.await?;
let cid = ctx
.get_canister_id(
&selections.canister,
&selections.network,
&selections.identity,
&selections.environment,
)
.await?;

Expand Down
23 changes: 16 additions & 7 deletions crates/icp-cli/src/commands/canister/info.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use clap::Args;
use ic_agent::AgentError;
use ic_utils::interfaces::management_canister::CanisterStatusResult;
use icp::{agent, context::GetCanisterIdAndAgentError, identity, network};
use icp::{agent, identity, network};
use itertools::Itertools;

use icp::context::Context;
use icp::context::{Context, GetAgentError, GetCanisterIdError};

use crate::commands::args;
use icp::store_id::LookupIdError;
Expand Down Expand Up @@ -36,17 +36,26 @@ pub(crate) enum CommandError {
Status(#[from] AgentError),

#[error(transparent)]
GetCanisterIdAndAgent(#[from] GetCanisterIdAndAgentError),
GetAgent(#[from] GetAgentError),

#[error(transparent)]
GetCanisterId(#[from] GetCanisterIdError),
}

pub(crate) async fn exec(ctx: &Context, args: &InfoArgs) -> Result<(), CommandError> {
let selections = args.cmd_args.selections();
let (cid, agent) = ctx
.get_canister_id_and_agent(
&selections.canister,
let agent = ctx
.get_agent(
&selections.identity,
&selections.network,
&selections.environment,
)
.await?;
let cid = ctx
.get_canister_id(
&selections.canister,
&selections.network,
&selections.identity,
&selections.environment,
)
.await?;

Expand Down
21 changes: 15 additions & 6 deletions crates/icp-cli/src/commands/canister/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use ic_utils::interfaces::ManagementCanister;
use icp::fs;
use icp::{context::CanisterSelection, prelude::*};

use icp::context::Context;
use icp::context::{Context, GetAgentError, GetCanisterIdError};

use crate::{
commands::args,
Expand Down Expand Up @@ -34,7 +34,10 @@ pub(crate) enum CommandError {
ReadWasmFile(#[from] fs::Error),

#[error(transparent)]
GetCanisterIdAndAgent(#[from] icp::context::GetCanisterIdAndAgentError),
GetAgent(#[from] GetAgentError),

#[error(transparent)]
GetCanisterId(#[from] GetCanisterIdError),

#[error(transparent)]
Unexpected(#[from] anyhow::Error),
Expand Down Expand Up @@ -62,12 +65,18 @@ pub(crate) async fn exec(ctx: &Context, args: &InstallArgs) -> Result<(), Comman
.map_err(|e| anyhow!(e))?
};

let (canister_id, agent) = ctx
.get_canister_id_and_agent(
&selections.canister,
let agent = ctx
.get_agent(
&selections.identity,
&selections.network,
&selections.environment,
)
.await?;
let canister_id = ctx
.get_canister_id(
&selections.canister,
&selections.network,
&selections.identity,
&selections.environment,
)
.await?;

Expand Down
21 changes: 15 additions & 6 deletions crates/icp-cli/src/commands/canister/settings/show.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use ic_agent::{AgentError, export::Principal};
use ic_management_canister_types::{CanisterStatusResult, LogVisibility};
use icp::{agent, identity, network};

use icp::context::{Context, GetAgentForEnvError, GetCanisterIdAndAgentError};
use icp::context::{Context, GetAgentError, GetAgentForEnvError, GetCanisterIdError};

use crate::commands::args;
use icp::store_id::LookupIdError;
Expand Down Expand Up @@ -38,17 +38,26 @@ pub(crate) enum CommandError {
GetAgentForEnv(#[from] GetAgentForEnvError),

#[error(transparent)]
GetCanisterIdAndAgent(#[from] GetCanisterIdAndAgentError),
GetAgent(#[from] GetAgentError),

#[error(transparent)]
GetCanisterId(#[from] GetCanisterIdError),
}

pub(crate) async fn exec(ctx: &Context, args: &ShowArgs) -> Result<(), CommandError> {
let selections = args.cmd_args.selections();
let (cid, agent) = ctx
.get_canister_id_and_agent(
&selections.canister,
let agent = ctx
.get_agent(
&selections.identity,
&selections.network,
&selections.environment,
)
.await?;
let cid = ctx
.get_canister_id(
&selections.canister,
&selections.network,
&selections.identity,
&selections.environment,
)
.await?;

Expand Down
21 changes: 15 additions & 6 deletions crates/icp-cli/src/commands/canister/settings/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
use clap::Args;
use ic_utils::interfaces::ManagementCanister;
use icp::context::{
CanisterSelection, Context, GetCanisterIdAndAgentError, GetEnvCanisterError,
CanisterSelection, Context, GetAgentError, GetCanisterIdError, GetEnvCanisterError,
GetEnvironmentError,
};
use snafu::Snafu;
Expand All @@ -19,7 +19,10 @@ pub(crate) struct SyncArgs {
#[derive(Debug, Snafu)]
pub(crate) enum CommandError {
#[snafu(transparent)]
GetIdAndAgent { source: GetCanisterIdAndAgentError },
GetAgent { source: GetAgentError },

#[snafu(transparent)]
GetCanisterId { source: GetCanisterIdError },

#[snafu(transparent)]
GetEnvironment { source: GetEnvironmentError },
Expand All @@ -44,12 +47,18 @@ pub(crate) async fn exec(ctx: &Context, args: &SyncArgs) -> Result<(), CommandEr
.get_canister_and_path_for_env(name, &selections.environment)
.await?;

let (cid, agent) = ctx
.get_canister_id_and_agent(
&selections.canister,
let agent = ctx
.get_agent(
&selections.identity,
&selections.network,
&selections.environment,
)
.await?;
let cid = ctx
.get_canister_id(
&selections.canister,
&selections.network,
&selections.identity,
&selections.environment,
)
.await?;

Expand Down
21 changes: 15 additions & 6 deletions crates/icp-cli/src/commands/canister/settings/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use ic_agent::{AgentError, export::Principal};
use ic_management_canister_types::{CanisterStatusResult, EnvironmentVariable, LogVisibility};
use icp::{LoadError, agent, identity, network};

use icp::context::{CanisterSelection, Context, GetCanisterIdAndAgentError};
use icp::context::{CanisterSelection, Context, GetAgentError, GetCanisterIdError};
use snafu::{ResultExt, Snafu};

use crate::commands::args;
Expand Down Expand Up @@ -131,20 +131,29 @@ pub(crate) enum CommandError {
Update { source: AgentError },

#[snafu(transparent)]
GetCanisterIdAndAgent { source: GetCanisterIdAndAgentError },
GetAgent { source: GetAgentError },

#[snafu(transparent)]
GetCanisterId { source: GetCanisterIdError },

#[snafu(display("failed to write to terminal"))]
WriteTerm { source: std::io::Error },
}

pub(crate) async fn exec(ctx: &Context, args: &UpdateArgs) -> Result<(), CommandError> {
let selections = args.cmd_args.selections();
let (cid, agent) = ctx
.get_canister_id_and_agent(
&selections.canister,
let agent = ctx
.get_agent(
&selections.identity,
&selections.network,
&selections.environment,
)
.await?;
let cid = ctx
.get_canister_id(
&selections.canister,
&selections.network,
&selections.identity,
&selections.environment,
)
.await?;

Expand Down
4 changes: 2 additions & 2 deletions crates/icp-cli/src/commands/canister/show.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ pub(crate) struct ShowArgs {
#[derive(Debug, thiserror::Error)]
pub(crate) enum CommandError {
#[error(transparent)]
GetCanisterId(#[from] icp::context::GetCanisterIdError),
GetCanisterId(#[from] icp::context::GetCanisterIdForEnvError),
}

pub(crate) async fn exec(ctx: &Context, args: &ShowArgs) -> Result<(), CommandError> {
let (canister_selection, environment_selection) = args.cmd_args.selections();

let cid = ctx
.get_canister_id(&canister_selection, &environment_selection)
.get_canister_id_for_env(&canister_selection, &environment_selection)
Copy link
Contributor

@raymondk raymondk Nov 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not going to work if the canister selection is a principal.
We could:

  • use ctx.get_canister_id and pass the default network.
  • leave it as is for now - the command doesn't do anything super special at the moment - but perhaps add a comment.

This is a weird command: unlike all the other commands which could be used inside or outside the context of project this one doesn't make much sense outside of a project.

.await?;

println!("{cid} => {}", args.cmd_args.canister);
Expand Down
Loading