use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::Binary;
use super::{CosmosMsg, Empty, Event};
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ReplyOn {
Always,
Error,
Success,
Never,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct SubMsg<T = Empty> {
pub id: u64,
pub msg: CosmosMsg<T>,
pub gas_limit: Option<u64>,
pub reply_on: ReplyOn,
}
pub const UNUSED_MSG_ID: u64 = 0;
impl<T> SubMsg<T> {
pub fn new(msg: impl Into<CosmosMsg<T>>) -> Self {
SubMsg {
id: UNUSED_MSG_ID,
msg: msg.into(),
reply_on: ReplyOn::Never,
gas_limit: None,
}
}
pub fn reply_on_success(msg: impl Into<CosmosMsg<T>>, id: u64) -> Self {
Self::reply_on(msg.into(), id, ReplyOn::Success)
}
pub fn reply_on_error(msg: impl Into<CosmosMsg<T>>, id: u64) -> Self {
Self::reply_on(msg.into(), id, ReplyOn::Error)
}
pub fn reply_always(msg: impl Into<CosmosMsg<T>>, id: u64) -> Self {
Self::reply_on(msg.into(), id, ReplyOn::Always)
}
pub fn with_gas_limit(mut self, limit: u64) -> Self {
self.gas_limit = Some(limit);
self
}
fn reply_on(msg: CosmosMsg<T>, id: u64, reply_on: ReplyOn) -> Self {
SubMsg {
id,
msg,
reply_on,
gas_limit: None,
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct Reply {
pub id: u64,
pub result: SubMsgResult,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum SubMsgResult {
Ok(SubMsgResponse),
#[serde(rename = "error")]
Err(String),
}
impl SubMsgResult {
pub fn into_result(self) -> Result<SubMsgResponse, String> {
Result::<SubMsgResponse, String>::from(self)
}
pub fn unwrap(self) -> SubMsgResponse {
self.into_result().unwrap()
}
pub fn unwrap_err(self) -> String {
self.into_result().unwrap_err()
}
pub fn is_ok(&self) -> bool {
matches!(self, SubMsgResult::Ok(_))
}
pub fn is_err(&self) -> bool {
matches!(self, SubMsgResult::Err(_))
}
}
impl<E: ToString> From<Result<SubMsgResponse, E>> for SubMsgResult {
fn from(original: Result<SubMsgResponse, E>) -> SubMsgResult {
match original {
Ok(value) => SubMsgResult::Ok(value),
Err(err) => SubMsgResult::Err(err.to_string()),
}
}
}
impl From<SubMsgResult> for Result<SubMsgResponse, String> {
fn from(original: SubMsgResult) -> Result<SubMsgResponse, String> {
match original {
SubMsgResult::Ok(value) => Ok(value),
SubMsgResult::Err(err) => Err(err),
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct SubMsgResponse {
pub events: Vec<Event>,
pub data: Option<Binary>,
}
#[deprecated(note = "Renamed to SubMsgResponse")]
pub type SubMsgExecutionResponse = SubMsgResponse;
#[cfg(test)]
mod tests {
use super::*;
use crate::{from_slice, to_vec, StdError, StdResult};
#[test]
fn sub_msg_result_serialization_works() {
let result = SubMsgResult::Ok(SubMsgResponse {
data: None,
events: vec![],
});
assert_eq!(
&to_vec(&result).unwrap(),
br#"{"ok":{"events":[],"data":null}}"#
);
let result = SubMsgResult::Ok(SubMsgResponse {
data: Some(Binary::from_base64("MTIzCg==").unwrap()),
events: vec![Event::new("wasm").add_attribute("fo", "ba")],
});
assert_eq!(
&to_vec(&result).unwrap(),
br#"{"ok":{"events":[{"type":"wasm","attributes":[{"key":"fo","value":"ba"}]}],"data":"MTIzCg=="}}"#
);
let result: SubMsgResult = SubMsgResult::Err("broken".to_string());
assert_eq!(&to_vec(&result).unwrap(), b"{\"error\":\"broken\"}");
}
#[test]
fn sub_msg_result_deserialization_works() {
let result: SubMsgResult = from_slice(br#"{"ok":{"events":[],"data":null}}"#).unwrap();
assert_eq!(
result,
SubMsgResult::Ok(SubMsgResponse {
events: vec![],
data: None,
})
);
let result: SubMsgResult = from_slice(
br#"{"ok":{"events":[{"type":"wasm","attributes":[{"key":"fo","value":"ba"}]}],"data":"MTIzCg=="}}"#).unwrap();
assert_eq!(
result,
SubMsgResult::Ok(SubMsgResponse {
data: Some(Binary::from_base64("MTIzCg==").unwrap()),
events: vec![Event::new("wasm").add_attribute("fo", "ba")],
})
);
let result: SubMsgResult = from_slice(br#"{"error":"broken"}"#).unwrap();
assert_eq!(result, SubMsgResult::Err("broken".to_string()));
let parse: StdResult<SubMsgResult> = from_slice(br#"{"unrelated":321,"error":"broken"}"#);
match parse.unwrap_err() {
StdError::ParseErr { .. } => {}
err => panic!("Unexpected error: {:?}", err),
}
let parse: StdResult<SubMsgResult> = from_slice(br#"{"error":"broken","unrelated":321}"#);
match parse.unwrap_err() {
StdError::ParseErr { .. } => {}
err => panic!("Unexpected error: {:?}", err),
}
}
#[test]
fn sub_msg_result_unwrap_works() {
let response = SubMsgResponse {
data: Some(Binary::from_base64("MTIzCg==").unwrap()),
events: vec![Event::new("wasm").add_attribute("fo", "ba")],
};
let success = SubMsgResult::Ok(response.clone());
assert_eq!(success.unwrap(), response);
}
#[test]
#[should_panic]
fn sub_msg_result_unwrap_panicks_for_err() {
let failure = SubMsgResult::Err("broken".to_string());
let _ = failure.unwrap();
}
#[test]
fn sub_msg_result_unwrap_err_works() {
let failure = SubMsgResult::Err("broken".to_string());
assert_eq!(failure.unwrap_err(), "broken");
}
#[test]
#[should_panic]
fn sub_msg_result_unwrap_err_panics_for_ok() {
let response = SubMsgResponse {
data: Some(Binary::from_base64("MTIzCg==").unwrap()),
events: vec![Event::new("wasm").add_attribute("fo", "ba")],
};
let success = SubMsgResult::Ok(response);
let _ = success.unwrap_err();
}
#[test]
fn sub_msg_result_is_ok_works() {
let success = SubMsgResult::Ok(SubMsgResponse {
data: Some(Binary::from_base64("MTIzCg==").unwrap()),
events: vec![Event::new("wasm").add_attribute("fo", "ba")],
});
let failure = SubMsgResult::Err("broken".to_string());
assert!(success.is_ok());
assert!(!failure.is_ok());
}
#[test]
fn sub_msg_result_is_err_works() {
let success = SubMsgResult::Ok(SubMsgResponse {
data: Some(Binary::from_base64("MTIzCg==").unwrap()),
events: vec![Event::new("wasm").add_attribute("fo", "ba")],
});
let failure = SubMsgResult::Err("broken".to_string());
assert!(failure.is_err());
assert!(!success.is_err());
}
#[test]
fn sub_msg_result_can_convert_from_core_result() {
let original: Result<SubMsgResponse, StdError> = Ok(SubMsgResponse {
data: Some(Binary::from_base64("MTIzCg==").unwrap()),
events: vec![],
});
let converted: SubMsgResult = original.into();
assert_eq!(
converted,
SubMsgResult::Ok(SubMsgResponse {
data: Some(Binary::from_base64("MTIzCg==").unwrap()),
events: vec![],
})
);
let original: Result<SubMsgResponse, StdError> = Err(StdError::generic_err("broken"));
let converted: SubMsgResult = original.into();
assert_eq!(
converted,
SubMsgResult::Err("Generic error: broken".to_string())
);
}
#[test]
fn sub_msg_result_can_convert_to_core_result() {
let original = SubMsgResult::Ok(SubMsgResponse {
data: Some(Binary::from_base64("MTIzCg==").unwrap()),
events: vec![],
});
let converted: Result<SubMsgResponse, String> = original.into();
assert_eq!(
converted,
Ok(SubMsgResponse {
data: Some(Binary::from_base64("MTIzCg==").unwrap()),
events: vec![],
})
);
let original = SubMsgResult::Err("went wrong".to_string());
let converted: Result<SubMsgResponse, String> = original.into();
assert_eq!(converted, Err("went wrong".to_string()));
}
}