Skip to content

Commit 1a6f887

Browse files
authored
chore: move context into the icp crate (#180)
1 parent b7b7fd7 commit 1a6f887

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1199
-1044
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/icp-cli/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ tracing.workspace = true
5858
assert_cmd = "2"
5959
camino-tempfile = "1"
6060
indoc.workspace = true
61-
icp = { workspace = true, features = ["test-features"] }
61+
icp = { workspace = true }
6262
nix = { version = "0.30.1", features = ["process", "signal"] }
6363
pocket-ic.workspace = true
6464
predicates = "3"

crates/icp-cli/src/commands/args.rs

Lines changed: 63 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -2,47 +2,10 @@ use std::fmt::Display;
22

33
use candid::Principal;
44
use clap::Args;
5-
use ic_agent::Agent;
5+
use icp::context::{CanisterSelection, EnvironmentSelection, NetworkSelection};
66
use icp::identity::IdentitySelection;
7-
use snafu::Snafu;
8-
9-
use crate::{commands::Context, options::IdentityOpt};
10-
11-
#[derive(Debug, Snafu)]
12-
pub(crate) enum ArgValidationError {
13-
#[snafu(display("You can't specify both an environment and a network"))]
14-
EnvironmentAndNetworkSpecified,
15-
16-
#[snafu(display(
17-
"Specifying a network is not supported if you are targeting a canister by name, specify an environment instead"
18-
))]
19-
AmbiguousCanisterName,
20-
21-
#[snafu(transparent)]
22-
EnvironmentError {
23-
source: crate::commands::GetEnvironmentError,
24-
},
25-
26-
#[snafu(transparent)]
27-
GetAgentForEnv {
28-
source: crate::commands::GetAgentForEnvError,
29-
},
30-
31-
#[snafu(transparent)]
32-
GetCanisterIdForEnv {
33-
source: crate::commands::GetCanisterIdForEnvError,
34-
},
35-
36-
#[snafu(transparent)]
37-
GetAgentForNetwork {
38-
source: crate::commands::GetAgentForNetworkError,
39-
},
40-
41-
#[snafu(transparent)]
42-
GetAgentForUrl {
43-
source: crate::commands::GetAgentForUrlError,
44-
},
45-
}
7+
8+
use crate::options::IdentityOpt;
469

4710
#[derive(Args, Debug)]
4811
pub(crate) struct CanisterEnvironmentArgs {
@@ -55,15 +18,25 @@ pub(crate) struct CanisterEnvironmentArgs {
5518
pub(crate) environment: Option<Environment>,
5619
}
5720

21+
impl CanisterEnvironmentArgs {
22+
/// Convert arguments into selection enums for canister and environment
23+
pub(crate) fn selections(&self) -> (CanisterSelection, EnvironmentSelection) {
24+
let canister_selection: CanisterSelection = self.canister.clone().into();
25+
let environment_selection: EnvironmentSelection =
26+
self.environment.clone().unwrap_or_default().into();
27+
(canister_selection, environment_selection)
28+
}
29+
}
30+
5831
#[derive(Args, Debug)]
5932
pub(crate) struct CanisterCommandArgs {
6033
// Note: Could have flattened CanisterEnvironmentArg to avoid adding child field
6134
/// Name or principal of canister to target
6235
/// When using a name an environment must be specified
6336
pub(crate) canister: Canister,
6437

65-
/// Name of the network to target
66-
#[arg(long)]
38+
/// Name of the network to target, conflicts with environment argument
39+
#[arg(long, conflicts_with = "environment")]
6740
pub(crate) network: Option<Network>,
6841

6942
/// Name of the target environment
@@ -75,81 +48,32 @@ pub(crate) struct CanisterCommandArgs {
7548
pub(crate) identity: IdentityOpt,
7649
}
7750

78-
impl CanisterEnvironmentArgs {
79-
pub async fn get_cid_for_environment(
80-
&self,
81-
ctx: &Context,
82-
) -> Result<Principal, ArgValidationError> {
83-
let arg_canister = self.canister.clone();
84-
let arg_environment = self.environment.clone().unwrap_or_default();
85-
let environment_name = arg_environment.name();
86-
87-
let principal = match arg_canister {
88-
Canister::Name(canister_name) => {
89-
ctx.get_canister_id_for_env(&canister_name, environment_name)
90-
.await?
91-
}
92-
Canister::Principal(principal) => {
93-
// Make sure a valid environment was requested
94-
let _ = ctx.get_environment(environment_name).await?;
95-
principal
96-
}
97-
};
98-
99-
Ok(principal)
100-
}
51+
/// Selections derived from CanisterCommandArgs
52+
pub(crate) struct CommandSelections {
53+
pub(crate) canister: CanisterSelection,
54+
pub(crate) environment: EnvironmentSelection,
55+
pub(crate) network: NetworkSelection,
56+
pub(crate) identity: IdentitySelection,
10157
}
10258

10359
impl CanisterCommandArgs {
104-
pub async fn get_cid_and_agent(
105-
&self,
106-
ctx: &Context,
107-
) -> Result<(Principal, Agent), ArgValidationError> {
108-
let arg_canister = self.canister.clone();
109-
let arg_environment = self.environment.clone().unwrap_or_default();
110-
let env_name = arg_environment.name();
111-
let arg_network = self.network.clone();
112-
let identity_selection: IdentitySelection = self.identity.clone().into();
113-
114-
let (cid, agent) = match (arg_canister, &arg_environment, arg_network) {
115-
(_, Environment::Name(_), Some(_)) => {
116-
// Both an environment and a network are specified this is an error
117-
return Err(ArgValidationError::EnvironmentAndNetworkSpecified);
118-
}
119-
(Canister::Name(_), Environment::Default(_), Some(_)) => {
120-
// This is not allowed, we should not use name with an environment not a network
121-
return Err(ArgValidationError::AmbiguousCanisterName);
122-
}
123-
(Canister::Name(cname), _, None) => {
124-
// A canister name was specified so we must be in a project
125-
126-
let agent = ctx.get_agent_for_env(&identity_selection, env_name).await?;
127-
let cid = ctx.get_canister_id_for_env(&cname, env_name).await?;
128-
129-
(cid, agent)
130-
}
131-
(Canister::Principal(principal), _, None) => {
132-
// Call by canister_id to the environment specified
133-
134-
let agent = ctx.get_agent_for_env(&identity_selection, env_name).await?;
135-
136-
(principal, agent)
137-
}
138-
(Canister::Principal(principal), Environment::Default(_), Some(network)) => {
139-
// Should handle known networks by name
140-
141-
let agent = match network {
142-
Network::Name(net_name) => {
143-
ctx.get_agent_for_network(&identity_selection, &net_name)
144-
.await?
145-
}
146-
Network::Url(url) => ctx.get_agent_for_url(&identity_selection, &url).await?,
147-
};
148-
(principal, agent)
149-
}
60+
/// Convert command arguments into selection enums
61+
pub(crate) fn selections(&self) -> CommandSelections {
62+
let canister_selection: CanisterSelection = self.canister.clone().into();
63+
let environment_selection: EnvironmentSelection =
64+
self.environment.clone().unwrap_or_default().into();
65+
let network_selection: NetworkSelection = match self.network.clone() {
66+
Some(network) => network.into_selection(),
67+
None => NetworkSelection::FromEnvironment,
15068
};
69+
let identity_selection: IdentitySelection = self.identity.clone().into();
15170

152-
Ok((cid, agent))
71+
CommandSelections {
72+
canister: canister_selection,
73+
environment: environment_selection,
74+
network: network_selection,
75+
identity: identity_selection,
76+
}
15377
}
15478
}
15579

@@ -169,6 +93,15 @@ impl From<&str> for Canister {
16993
}
17094
}
17195

96+
impl From<Canister> for CanisterSelection {
97+
fn from(v: Canister) -> Self {
98+
match v {
99+
Canister::Name(name) => CanisterSelection::Named(name),
100+
Canister::Principal(principal) => CanisterSelection::Principal(principal),
101+
}
102+
}
103+
}
104+
172105
impl Display for Canister {
173106
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
174107
match self {
@@ -194,6 +127,15 @@ impl From<&str> for Network {
194127
}
195128
}
196129

130+
impl Network {
131+
pub(crate) fn into_selection(self) -> NetworkSelection {
132+
match self {
133+
Network::Name(name) => NetworkSelection::Named(name),
134+
Network::Url(url) => NetworkSelection::Url(url),
135+
}
136+
}
137+
}
138+
197139
#[derive(Clone, Debug, PartialEq)]
198140
pub(crate) enum Environment {
199141
Name(String),
@@ -221,6 +163,15 @@ impl From<&str> for Environment {
221163
}
222164
}
223165

166+
impl From<Environment> for EnvironmentSelection {
167+
fn from(v: Environment) -> Self {
168+
match v {
169+
Environment::Name(name) => EnvironmentSelection::Named(name),
170+
Environment::Default(_) => EnvironmentSelection::Default,
171+
}
172+
}
173+
}
174+
224175
impl Display for Environment {
225176
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226177
write!(f, "{}", self.name())
@@ -232,10 +183,6 @@ mod tests {
232183
use candid::Principal;
233184

234185
use super::*;
235-
use icp::MockProjectLoader;
236-
use std::sync::Arc;
237-
238-
use crate::{commands::args::Environment, store_id::MockInMemoryIdStore};
239186

240187
#[test]
241188
fn canister_by_name() {
@@ -272,49 +219,4 @@ mod tests {
272219
Network::Url("http://www.example.com".to_string()),
273220
);
274221
}
275-
276-
#[tokio::test]
277-
async fn test_get_cid_for_environment() {
278-
use crate::store_id::{Access as IdAccess, Key};
279-
use candid::Principal;
280-
281-
let ids_store = Arc::new(MockInMemoryIdStore::new());
282-
283-
// Register a canister ID for the dev environment
284-
let canister_id = Principal::from_text("rrkah-fqaaa-aaaaa-aaaaq-cai").unwrap();
285-
ids_store
286-
.register(
287-
&Key {
288-
network: "local".to_string(),
289-
environment: "dev".to_string(),
290-
canister: "backend".to_string(),
291-
},
292-
&canister_id,
293-
)
294-
.unwrap();
295-
296-
let ctx = Context {
297-
project: Arc::new(MockProjectLoader::complex()),
298-
ids: ids_store,
299-
..Context::mocked()
300-
};
301-
302-
let args = CanisterEnvironmentArgs {
303-
canister: Canister::Name("backend".to_string()),
304-
environment: Some(Environment::Name("dev".to_string())),
305-
};
306-
307-
assert!(matches!(args.get_cid_for_environment(&ctx).await, Ok(id) if id == canister_id));
308-
309-
let args = CanisterEnvironmentArgs {
310-
canister: Canister::Name("INVALID".to_string()),
311-
environment: Some(Environment::Name("dev".to_string())),
312-
};
313-
314-
let res = args.get_cid_for_environment(&ctx).await;
315-
assert!(
316-
res.is_err(),
317-
"An invalid canister name should result in an error"
318-
);
319-
}
320222
}

crates/icp-cli/src/commands/build/mod.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,11 @@ use clap::Args;
66
use futures::{StreamExt, stream::FuturesOrdered};
77
use icp::{
88
canister::build::{BuildError, Params},
9+
context::Context,
910
fs::read,
1011
};
1112

12-
use crate::{
13-
commands::Context,
14-
progress::{ProgressManager, ProgressManagerSettings},
15-
};
13+
use crate::progress::{ProgressManager, ProgressManagerSettings};
1614

1715
#[derive(Args, Debug)]
1816
pub(crate) struct BuildArgs {

crates/icp-cli/src/commands/canister/binding_env_vars.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@ use ic_utils::interfaces::management_canister::builders::EnvironmentVariable;
1111
use icp::{agent, identity, network};
1212
use tracing::debug;
1313

14+
use icp::context::Context;
15+
1416
use crate::{
15-
commands::Context,
1617
options::{EnvironmentOpt, IdentityOpt},
1718
progress::{ProgressManager, ProgressManagerSettings},
18-
store_artifact::LookupError as LookupArtifactError,
19-
store_id::{Key, LookupError as LookupIdError},
2019
};
20+
use icp::store_artifact::LookupError as LookupArtifactError;
21+
use icp::store_id::{Key, LookupError as LookupIdError};
2122

2223
#[derive(Clone, Debug, Args)]
2324
pub(crate) struct BindingArgs {

crates/icp-cli/src/commands/canister/call.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@ use candid::IDLArgs;
44
use clap::Args;
55
use dialoguer::console::Term;
66

7-
use crate::commands::{
8-
Context,
9-
args::{self, ArgValidationError},
10-
};
7+
use icp::context::Context;
8+
9+
use crate::commands::args;
1110

1211
#[derive(Args, Debug)]
1312
pub(crate) struct CallArgs {
@@ -36,11 +35,20 @@ pub(crate) enum CommandError {
3635
Call(#[from] ic_agent::AgentError),
3736

3837
#[error(transparent)]
39-
Shared(#[from] ArgValidationError),
38+
GetCanisterIdAndAgent(#[from] icp::context::GetCanisterIdAndAgentError),
4039
}
4140

4241
pub(crate) async fn exec(ctx: &Context, args: &CallArgs) -> Result<(), CommandError> {
43-
let (cid, agent) = args.cmd_args.get_cid_and_agent(ctx).await?;
42+
let selections = args.cmd_args.selections();
43+
44+
let (cid, agent) = ctx
45+
.get_canister_id_and_agent(
46+
&selections.canister,
47+
&selections.environment,
48+
&selections.network,
49+
&selections.identity,
50+
)
51+
.await?;
4452

4553
// Parse candid arguments
4654
let cargs = candid_parser::parse_idl_args(&args.args)?;

crates/icp-cli/src/commands/canister/create.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@ use icp_canister_interfaces::{
1616
};
1717
use rand::seq::IndexedRandom;
1818

19+
use icp::context::Context;
20+
1921
use crate::{
20-
commands::Context,
2122
options::{EnvironmentOpt, IdentityOpt},
2223
progress::{ProgressManager, ProgressManagerSettings},
23-
store_id::{Key, LookupError, RegisterError},
2424
};
25+
use icp::store_id::{Key, LookupError, RegisterError};
2526

2627
pub(crate) const DEFAULT_CANISTER_CYCLES: u128 = 2 * TRILLION;
2728

0 commit comments

Comments
 (0)