diff --git a/relayer-cli/src/commands/query/connection.rs b/relayer-cli/src/commands/query/connection.rs index 3e892e5512..b0425d73a9 100644 --- a/relayer-cli/src/commands/query/connection.rs +++ b/relayer-cli/src/commands/query/connection.rs @@ -7,13 +7,13 @@ use ibc::ics03_connection::connection::ConnectionEnd; use ibc::ics24_host::error::ValidationError; use ibc::ics24_host::identifier::ChainId; use ibc::ics24_host::identifier::ConnectionId; - +use ibc_proto::ibc::core::channel::v1::QueryConnectionChannelsRequest; use relayer::chain::{Chain, CosmosSDKChain}; use relayer::config::{ChainConfig, Config}; +use crate::conclude::{on_exit, CommandOutput, CommandStatus}; use crate::error::{Error, Kind}; use crate::prelude::*; -use ibc_proto::ibc::core::channel::v1::QueryConnectionChannelsRequest; #[derive(Clone, Command, Debug, Options)] pub struct QueryConnectionEndCmd { @@ -109,6 +109,9 @@ pub struct QueryConnectionChannelsCmd { #[options(free, help = "identifier of the connection to query")] connection_id: Option, + + #[options(help = "enable output in JSON format")] + json: bool, } #[derive(Debug)] @@ -150,12 +153,16 @@ impl Runnable for QueryConnectionChannelsCmd { let (chain_config, opts) = match self.validate_options(&config) { Err(err) => { - status_err!("invalid options: {}", err); - return; + return on_exit( + self.json, + CommandOutput::new(CommandStatus::Error).with_msg(err), + ); } Ok(result) => result, }; - status_info!("Options", "{:?}", opts); + if !self.json { + status_info!("Options", "{:?}", opts); + } let rt = Arc::new(Mutex::new(TokioRuntime::new().unwrap())); let chain = CosmosSDKChain::bootstrap(chain_config, rt).unwrap(); @@ -170,17 +177,25 @@ impl Runnable for QueryConnectionChannelsCmd { .map_err(|e| Kind::Query.context(e).into()); match res { - Ok(cs) => status_info!("Result for connection channels query: ", "{:?}", cs), - Err(e) => status_info!("Error encountered on channel end query:", "{}", e), + Ok(cs) => on_exit( + self.json, + CommandOutput::new(CommandStatus::Success).with_msg(format!("{:?}", cs)), + ), + Err(e) => on_exit( + self.json, + CommandOutput::new(CommandStatus::Error) + .with_msg(format!("error encountered: {:?}", e)), + ), } } } #[cfg(test)] mod tests { - use crate::commands::query::connection::{QueryConnectionChannelsCmd, QueryConnectionEndCmd}; use relayer::config::parse; + use crate::commands::query::connection::{QueryConnectionChannelsCmd, QueryConnectionEndCmd}; + #[test] fn parse_connection_query_end_parameters() { let default_params = QueryConnectionEndCmd { @@ -280,6 +295,7 @@ mod tests { let default_params = QueryConnectionChannelsCmd { chain_id: Some("ibc-0".to_string().parse().unwrap()), connection_id: Some("ibconeconnection".to_string().parse().unwrap()), + json: false, }; struct Test { diff --git a/relayer-cli/src/conclude.rs b/relayer-cli/src/conclude.rs new file mode 100644 index 0000000000..979b9aa882 --- /dev/null +++ b/relayer-cli/src/conclude.rs @@ -0,0 +1,75 @@ +#![allow(dead_code)] +//! Handles the return message (command output) and return code from a CLI command. +//! Most of the code here is temporary/experimental. +//! https://github.com/informalsystems/ibc-rs/issues/500 will introduce a more permanent solution. + +use std::collections::HashMap; + +use serde::Serialize; + +pub fn on_exit(json: bool, out: CommandOutput) { + // Handle the output message + if json { + println!("{}", serde_json::to_string(&out).unwrap()); + } else { + println!("status={:?}\nmsg={:?}", out.status, out.msg); + } + + // The return code + if out.status == CommandStatus::Error { + std::process::exit(1); + } else { + std::process::exit(0); + } +} + +/// A CLI output with support for JSON serialization. +#[derive(Serialize, Debug)] +pub struct CommandOutput { + /// The return status + pub status: CommandStatus, + + /// An arbitrary message accompanying the output + pub msg: Option, + + /// Additional arbitrary key/values that may be present in the output + /// TODO: at the moment this is not used! + #[serde(flatten)] + pub extra: HashMap, +} + +impl CommandOutput { + pub fn new(status: CommandStatus) -> Self { + CommandOutput { + status, + msg: None, + extra: Default::default(), + } + } + + pub fn with_msg(self, msg: String) -> Self { + CommandOutput { + msg: Some(msg), + ..self + } + } + + pub fn with_extra(self, k: String, v: String) -> Self { + let mut extra = self.extra.clone(); + extra.insert(k, v); + + CommandOutput { extra, ..self } + } +} + +#[derive(Serialize, Debug, PartialEq, Eq)] +pub enum CommandStatus { + #[serde(rename(serialize = "success"))] + Success, + + #[serde(rename(serialize = "info"))] + Info, + + #[serde(rename(serialize = "error"))] + Error, +} diff --git a/relayer-cli/src/lib.rs b/relayer-cli/src/lib.rs index 6a91c6fee7..0e13022e2b 100644 --- a/relayer-cli/src/lib.rs +++ b/relayer-cli/src/lib.rs @@ -18,6 +18,7 @@ pub mod application; pub mod commands; +mod conclude; pub mod config; pub mod error; pub mod prelude;