diff --git a/dfx/src/commands/canister/mod.rs b/dfx/src/commands/canister/mod.rs index 1f2740f5e9..5a1a3ec0f4 100644 --- a/dfx/src/commands/canister/mod.rs +++ b/dfx/src/commands/canister/mod.rs @@ -5,14 +5,16 @@ use clap::{App, ArgMatches, SubCommand}; mod call; mod install; +mod query; fn builtins() -> Vec> where T: ClientEnv + ProjectConfigEnv, { vec![ - CliCommand::new(install::construct(), install::exec), CliCommand::new(call::construct(), call::exec), + CliCommand::new(install::construct(), install::exec), + CliCommand::new(query::construct(), query::exec), ] } diff --git a/dfx/src/commands/canister/query.rs b/dfx/src/commands/canister/query.rs new file mode 100644 index 0000000000..09fe7c70a3 --- /dev/null +++ b/dfx/src/commands/canister/query.rs @@ -0,0 +1,76 @@ +use crate::lib::api_client::{query, Blob, CanisterQueryCall, QueryResponseReply, ReadResponse}; +use crate::lib::env::ClientEnv; +use crate::lib::error::{DfxError, DfxResult}; +use clap::{App, Arg, ArgMatches, SubCommand}; +use tokio::runtime::Runtime; + +fn is_number(v: String) -> Result<(), String> { + v.parse::() + .map_err(|_| String::from("The value must be a number.")) + .map(|_| ()) +} + +pub fn construct() -> App<'static, 'static> { + SubCommand::with_name("query") + .about("Query a canister.") + .arg( + Arg::with_name("canister") + .takes_value(true) + .help("The canister ID (a number) to query.") + .required(true) + .validator(is_number), + ) + .arg( + Arg::with_name("method_name") + .help("The query method name file to use.") + .required(true), + ) + .arg( + Arg::with_name("arguments") + .help("Arguments to pass to the method.") + .takes_value(true) + .multiple(true), + ) +} + +pub fn exec(env: &T, args: &ArgMatches<'_>) -> DfxResult +where + T: ClientEnv, +{ + // Read the config. + let canister_id = args.value_of("canister").unwrap().parse::()?; + let method_name = args.value_of("method_name").unwrap(); + let arguments: Option> = args.values_of("arguments").map(|args| args.collect()); + + let client = env.get_client(); + let install = query( + client, + CanisterQueryCall { + canister_id, + method_name: method_name.to_owned(), + arg: arguments + .map(|args| Blob(Vec::from(args[0]))) + .unwrap_or_else(|| Blob(vec![])), + }, + ); + + let mut runtime = Runtime::new().expect("Unable to create a runtime"); + match runtime.block_on(install) { + Ok(ReadResponse::Pending) => { + println!("Pending"); + Ok(()) + } + Ok(ReadResponse::Replied { + reply: QueryResponseReply { arg: Blob(blob) }, + }) => { + println!("{}", String::from_utf8_lossy(&blob)); + Ok(()) + } + Ok(ReadResponse::Rejected { + reject_code, + reject_message, + }) => Err(DfxError::ClientError(reject_code, reject_message)), + Ok(ReadResponse::Unknown) => Err(DfxError::Unknown("Unknown response".to_owned())), + Err(x) => Err(DfxError::from(x)), + } +}