cosmwasm_std/results/
contract_result.rs

1use core::fmt;
2use schemars::JsonSchema;
3use serde::{Deserialize, Serialize};
4
5use crate::prelude::*;
6
7/// This is the final result type that is created and serialized in a contract for
8/// every init/execute/migrate call. The VM then deserializes this type to distinguish
9/// between successful and failed executions.
10///
11/// We use a custom type here instead of Rust's Result because we want to be able to
12/// define the serialization, which is a public interface. Every language that compiles
13/// to Wasm and runs in the ComsWasm VM needs to create the same JSON representation.
14///
15/// # Examples
16///
17/// Success:
18///
19/// ```
20/// # use cosmwasm_std::{to_json_string, ContractResult, Response};
21/// let response: Response = Response::default();
22/// let result: ContractResult<Response> = ContractResult::Ok(response);
23/// assert_eq!(to_json_string(&result).unwrap(), r#"{"ok":{"messages":[],"attributes":[],"events":[],"data":null}}"#);
24/// ```
25///
26/// Failure:
27///
28/// ```
29/// # use cosmwasm_std::{to_json_string, ContractResult, Response};
30/// let error_msg = String::from("Something went wrong");
31/// let result: ContractResult<Response> = ContractResult::Err(error_msg);
32/// assert_eq!(to_json_string(&result).unwrap(), r#"{"error":"Something went wrong"}"#);
33/// ```
34#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
35#[serde(rename_all = "snake_case")]
36pub enum ContractResult<S> {
37    Ok(S),
38    /// An error type that every custom error created by contract developers can be converted to.
39    /// This could potentially have more structure, but String is the easiest.
40    #[serde(rename = "error")]
41    Err(String),
42}
43
44// Implementations here mimic the Result API and should be implemented via a conversion to Result
45// to ensure API consistency
46impl<S> ContractResult<S> {
47    /// Converts a `ContractResult<S>` to a `Result<S, String>` as a convenient way
48    /// to access the full Result API.
49    pub fn into_result(self) -> Result<S, String> {
50        Result::<S, String>::from(self)
51    }
52
53    pub fn unwrap(self) -> S {
54        self.into_result().unwrap()
55    }
56
57    pub fn is_ok(&self) -> bool {
58        matches!(self, ContractResult::Ok(_))
59    }
60
61    pub fn is_err(&self) -> bool {
62        matches!(self, ContractResult::Err(_))
63    }
64}
65
66impl<S: fmt::Debug> ContractResult<S> {
67    pub fn unwrap_err(self) -> String {
68        self.into_result().unwrap_err()
69    }
70}
71
72impl<S, E: ToString> From<Result<S, E>> for ContractResult<S> {
73    fn from(original: Result<S, E>) -> ContractResult<S> {
74        match original {
75            Ok(value) => ContractResult::Ok(value),
76            Err(err) => ContractResult::Err(err.to_string()),
77        }
78    }
79}
80
81impl<S> From<ContractResult<S>> for Result<S, String> {
82    fn from(original: ContractResult<S>) -> Result<S, String> {
83        match original {
84            ContractResult::Ok(value) => Ok(value),
85            ContractResult::Err(err) => Err(err),
86        }
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93    use crate::{from_json, to_json_vec, Response, StdError, StdResult};
94
95    #[test]
96    fn contract_result_serialization_works() {
97        let result = ContractResult::Ok(12);
98        assert_eq!(&to_json_vec(&result).unwrap(), b"{\"ok\":12}");
99
100        let result = ContractResult::Ok("foo");
101        assert_eq!(&to_json_vec(&result).unwrap(), b"{\"ok\":\"foo\"}");
102
103        let result: ContractResult<Response> = ContractResult::Ok(Response::default());
104        assert_eq!(
105            to_json_vec(&result).unwrap(),
106            br#"{"ok":{"messages":[],"attributes":[],"events":[],"data":null}}"#
107        );
108
109        let result: ContractResult<Response> = ContractResult::Err("broken".to_string());
110        assert_eq!(&to_json_vec(&result).unwrap(), b"{\"error\":\"broken\"}");
111    }
112
113    #[test]
114    fn contract_result_deserialization_works() {
115        let result: ContractResult<u64> = from_json(br#"{"ok":12}"#).unwrap();
116        assert_eq!(result, ContractResult::Ok(12));
117
118        let result: ContractResult<String> = from_json(br#"{"ok":"foo"}"#).unwrap();
119        assert_eq!(result, ContractResult::Ok("foo".to_string()));
120
121        let result: ContractResult<Response> =
122            from_json(br#"{"ok":{"messages":[],"attributes":[],"events":[],"data":null}}"#)
123                .unwrap();
124        assert_eq!(result, ContractResult::Ok(Response::default()));
125
126        let result: ContractResult<Response> = from_json(br#"{"error":"broken"}"#).unwrap();
127        assert_eq!(result, ContractResult::Err("broken".to_string()));
128
129        // ignores whitespace
130        let result: ContractResult<u64> = from_json(b" {\n\t  \"ok\": 5898\n}  ").unwrap();
131        assert_eq!(result, ContractResult::Ok(5898));
132
133        // fails for additional attributes
134        let parse: StdResult<ContractResult<u64>> = from_json(br#"{"unrelated":321,"ok":4554}"#);
135        match parse.unwrap_err() {
136            StdError::ParseErr { .. } => {}
137            err => panic!("Unexpected error: {err:?}"),
138        }
139        let parse: StdResult<ContractResult<u64>> = from_json(br#"{"ok":4554,"unrelated":321}"#);
140        match parse.unwrap_err() {
141            StdError::ParseErr { .. } => {}
142            err => panic!("Unexpected error: {err:?}"),
143        }
144        let parse: StdResult<ContractResult<u64>> =
145            from_json(br#"{"ok":4554,"error":"What's up now?"}"#);
146        match parse.unwrap_err() {
147            StdError::ParseErr { .. } => {}
148            err => panic!("Unexpected error: {err:?}"),
149        }
150    }
151
152    #[test]
153    fn can_convert_from_core_result() {
154        let original: Result<Response, StdError> = Ok(Response::default());
155        let converted: ContractResult<Response> = original.into();
156        assert_eq!(converted, ContractResult::Ok(Response::default()));
157
158        let original: Result<Response, StdError> = Err(StdError::generic_err("broken"));
159        let converted: ContractResult<Response> = original.into();
160        assert_eq!(
161            converted,
162            ContractResult::Err("Generic error: broken".to_string())
163        );
164    }
165
166    #[test]
167    fn can_convert_to_core_result() {
168        let original = ContractResult::Ok(Response::default());
169        let converted: Result<Response, String> = original.into();
170        assert_eq!(converted, Ok(Response::default()));
171
172        let original = ContractResult::Err("went wrong".to_string());
173        let converted: Result<Response, String> = original.into();
174        assert_eq!(converted, Err("went wrong".to_string()));
175    }
176}