Skip to content

Commit 4c45d94

Browse files
lwshangraymondk
andauthored
refactor: Context getters (#164)
* remove mode * remove all the warnings and unused code * poc: argument handling * fill out the remaining branches * move errors out * use helpers to reduce boiler plate * formatting * shuffle things around even more * fix the default environment * unused import * conver canister status * convert canister delete * formatting * get_identity * get_environment * get_canister_id_for_env * get_agent_* * fix lint * fmt * define error types for getters * transparent * Trigger CI --------- Co-authored-by: Raymond Khalife <raymond.khalife@dfinity.org>
1 parent 808506a commit 4c45d94

File tree

38 files changed

+2351
-2320
lines changed

38 files changed

+2351
-2320
lines changed
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
use std::fmt::Display;
2+
3+
use candid::Principal;
4+
use clap::Args;
5+
use ic_agent::Agent;
6+
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+
GetAgentForEnv {
23+
source: crate::commands::GetAgentForEnvError,
24+
},
25+
26+
#[snafu(transparent)]
27+
GetCanisterIdForEnv {
28+
source: crate::commands::GetCanisterIdForEnvError,
29+
},
30+
31+
#[snafu(transparent)]
32+
GetAgentForNetwork {
33+
source: crate::commands::GetAgentForNetworkError,
34+
},
35+
36+
#[snafu(transparent)]
37+
GetAgentForUrl {
38+
source: crate::commands::GetAgentForUrlError,
39+
},
40+
}
41+
42+
#[derive(Args, Debug)]
43+
pub(crate) struct CanisterCommandArgs {
44+
/// Name of canister to target
45+
pub(crate) canister: Canister,
46+
47+
#[arg(long)]
48+
pub(crate) network: Option<Network>,
49+
50+
#[arg(long)]
51+
pub(crate) environment: Option<Environment>,
52+
53+
#[command(flatten)]
54+
pub(crate) identity: IdentityOpt,
55+
}
56+
57+
impl CanisterCommandArgs {
58+
pub async fn get_cid_and_agent(
59+
&self,
60+
ctx: &Context,
61+
) -> Result<(Principal, Agent), ArgValidationError> {
62+
let arg_canister = self.canister.clone();
63+
let arg_environment = self.environment.clone().unwrap_or_default();
64+
let env_name = arg_environment.name();
65+
let arg_network = self.network.clone();
66+
let identity_selection: IdentitySelection = self.identity.clone().into();
67+
68+
let (cid, agent) = match (arg_canister, &arg_environment, arg_network) {
69+
(_, Environment::Name(_), Some(_)) => {
70+
// Both an environment and a network are specified this is an error
71+
return Err(ArgValidationError::EnvironmentAndNetworkSpecified);
72+
}
73+
(Canister::Name(_), Environment::Default(_), Some(_)) => {
74+
// This is not allowed, we should not use name with an environment not a network
75+
return Err(ArgValidationError::AmbiguousCanisterName);
76+
}
77+
(Canister::Name(cname), _, None) => {
78+
// A canister name was specified so we must be in a project
79+
80+
let agent = ctx.get_agent_for_env(&identity_selection, env_name).await?;
81+
let cid = ctx.get_canister_id_for_env(&cname, env_name).await?;
82+
83+
(cid, agent)
84+
}
85+
(Canister::Principal(principal), _, None) => {
86+
// Call by canister_id to the environment specified
87+
88+
let agent = ctx.get_agent_for_env(&identity_selection, env_name).await?;
89+
90+
(principal, agent)
91+
}
92+
(Canister::Principal(principal), Environment::Default(_), Some(network)) => {
93+
// Should handle known networks by name
94+
95+
let agent = match network {
96+
Network::Name(net_name) => {
97+
ctx.get_agent_for_network(&identity_selection, &net_name)
98+
.await?
99+
}
100+
Network::Url(url) => ctx.get_agent_for_url(&identity_selection, &url).await?,
101+
};
102+
(principal, agent)
103+
}
104+
};
105+
106+
Ok((cid, agent))
107+
}
108+
}
109+
110+
#[derive(Clone, Debug, PartialEq)]
111+
pub(crate) enum Canister {
112+
Name(String),
113+
Principal(Principal),
114+
}
115+
116+
impl From<&str> for Canister {
117+
fn from(v: &str) -> Self {
118+
if let Ok(p) = Principal::from_text(v) {
119+
return Self::Principal(p);
120+
}
121+
122+
Self::Name(v.to_string())
123+
}
124+
}
125+
126+
#[derive(Clone, Debug, PartialEq)]
127+
pub(crate) enum Network {
128+
Name(String),
129+
Url(String),
130+
}
131+
132+
impl From<&str> for Network {
133+
fn from(v: &str) -> Self {
134+
if v.starts_with("http://") || v.starts_with("https://") {
135+
return Self::Url(v.to_string());
136+
}
137+
138+
Self::Name(v.to_string())
139+
}
140+
}
141+
142+
#[derive(Clone, Debug, PartialEq)]
143+
pub(crate) enum Environment {
144+
Name(String),
145+
Default(String),
146+
}
147+
148+
impl Environment {
149+
pub(crate) fn name(&self) -> &str {
150+
match self {
151+
Environment::Name(name) => name,
152+
Environment::Default(name) => name,
153+
}
154+
}
155+
}
156+
157+
impl Default for Environment {
158+
fn default() -> Self {
159+
Self::Default("local".to_string())
160+
}
161+
}
162+
163+
impl From<&str> for Environment {
164+
fn from(v: &str) -> Self {
165+
Self::Name(v.to_string())
166+
}
167+
}
168+
169+
impl Display for Environment {
170+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
171+
write!(f, "{}", self.name())
172+
}
173+
}
174+
175+
#[cfg(test)]
176+
mod tests {
177+
use candid::Principal;
178+
179+
use crate::commands::args::{Canister, Network};
180+
181+
#[test]
182+
fn canister_by_name() {
183+
assert_eq!(
184+
Canister::from("my-canister"),
185+
Canister::Name("my-canister".to_string()),
186+
);
187+
}
188+
189+
#[test]
190+
fn canister_by_principal() {
191+
let cid = "ntyui-iatoh-pfi3f-27wnk-vgdqt-mq3cl-ld7jh-743kl-sde6i-tbm7g-tqe";
192+
193+
assert_eq!(
194+
Canister::from(cid),
195+
Canister::Principal(Principal::from_text(cid).expect("failed to parse principal")),
196+
);
197+
}
198+
199+
#[test]
200+
fn network_by_name() {
201+
assert_eq!(
202+
Network::from("my-network"),
203+
Network::Name("my-network".to_string()),
204+
);
205+
}
206+
207+
#[test]
208+
fn network_by_url_http() {
209+
let url = "http://www.example.com";
210+
211+
assert_eq!(
212+
Network::from(url),
213+
Network::Url("http://www.example.com".to_string()),
214+
);
215+
}
216+
}

0 commit comments

Comments
 (0)