@@ -2,47 +2,10 @@ use std::fmt::Display;
22
33use candid:: Principal ;
44use clap:: Args ;
5- use ic_agent :: Agent ;
5+ use icp :: context :: { CanisterSelection , EnvironmentSelection , NetworkSelection } ;
66use 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 ) ]
4811pub ( 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 ) ]
5932pub ( 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
10359impl 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+
172105impl 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 ) ]
198140pub ( 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+
224175impl 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}
0 commit comments