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(
35    Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema, cw_schema::Schemaifier,
36)]
37#[serde(rename_all = "snake_case")]
38pub enum ContractResult<S> {
39    Ok(S),
40    /// An error type that every custom error created by contract developers can be converted to.
41    /// This could potentially have more structure, but String is the easiest.
42    #[serde(rename = "error")]
43    Err(String),
44}
45
46// Implementations here mimic the Result API and should be implemented via a conversion to Result
47// to ensure API consistency
48impl<S> ContractResult<S> {
49    /// Converts a `ContractResult<S>` to a `Result<S, String>` as a convenient way
50    /// to access the full Result API.
51    pub fn into_result(self) -> Result<S, String> {
52        Result::<S, String>::from(self)
53    }
54
55    pub fn unwrap(self) -> S {
56        self.into_result().unwrap()
57    }
58
59    pub fn is_ok(&self) -> bool {
60        matches!(self, ContractResult::Ok(_))
61    }
62
63    pub fn is_err(&self) -> bool {
64        matches!(self, ContractResult::Err(_))
65    }
66}
67
68impl<S: fmt::Debug> ContractResult<S> {
69    pub fn unwrap_err(self) -> String {
70        self.into_result().unwrap_err()
71    }
72}
73
74impl<S, E: ToString> From<Result<S, E>> for ContractResult<S> {
75    fn from(original: Result<S, E>) -> ContractResult<S> {
76        match original {
77            Ok(value) => ContractResult::Ok(value),
78            Err(err) => ContractResult::Err(err.to_string()),
79        }
80    }
81}
82
83impl<S> From<ContractResult<S>> for Result<S, String> {
84    fn from(original: ContractResult<S>) -> Result<S, String> {
85        match original {
86            ContractResult::Ok(value) => Ok(value),
87            ContractResult::Err(err) => Err(err),
88        }
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95    use crate::{errors::ErrorKind, from_json, to_json_vec, Response, StdError, StdResult};
96
97    #[test]
98    fn contract_result_serialization_works() {
99        let result = ContractResult::Ok(12);
100        assert_eq!(&to_json_vec(&result).unwrap(), b"{\"ok\":12}");
101
102        let result = ContractResult::Ok("foo");
103        assert_eq!(&to_json_vec(&result).unwrap(), b"{\"ok\":\"foo\"}");
104
105        let result: ContractResult<Response> = ContractResult::Ok(Response::default());
106        assert_eq!(
107            to_json_vec(&result).unwrap(),
108            br#"{"ok":{"messages":[],"attributes":[],"events":[],"data":null}}"#
109        );
110
111        let result: ContractResult<Response> = ContractResult::Err("broken".to_string());
112        assert_eq!(&to_json_vec(&result).unwrap(), b"{\"error\":\"broken\"}");
113    }
114
115    #[test]
116    fn contract_result_deserialization_works() {
117        let result: ContractResult<u64> = from_json(br#"{"ok":12}"#).unwrap();
118        assert_eq!(result, ContractResult::Ok(12));
119
120        let result: ContractResult<String> = from_json(br#"{"ok":"foo"}"#).unwrap();
121        assert_eq!(result, ContractResult::Ok("foo".to_string()));
122
123        let result: ContractResult<Response> =
124            from_json(br#"{"ok":{"messages":[],"attributes":[],"events":[],"data":null}}"#)
125                .unwrap();
126        assert_eq!(result, ContractResult::Ok(Response::default()));
127
128        let result: ContractResult<Response> = from_json(br#"{"error":"broken"}"#).unwrap();
129        assert_eq!(result, ContractResult::Err("broken".to_string()));
130
131        // ignores whitespace
132        let result: ContractResult<u64> = from_json(b" {\n\t  \"ok\": 5898\n}  ").unwrap();
133        assert_eq!(result, ContractResult::Ok(5898));
134
135        // fails for additional attributes
136        let parse: StdResult<ContractResult<u64>> = from_json(br#"{"unrelated":321,"ok":4554}"#);
137        match parse.unwrap_err().kind() {
138            ErrorKind::Serialization => {}
139            err => panic!("Unexpected error: {err:?}"),
140        }
141        let parse: StdResult<ContractResult<u64>> = from_json(br#"{"ok":4554,"unrelated":321}"#);
142        match parse.unwrap_err().kind() {
143            ErrorKind::Serialization => {}
144            err => panic!("Unexpected error: {err:?}"),
145        }
146        let parse: StdResult<ContractResult<u64>> =
147            from_json(br#"{"ok":4554,"error":"What's up now?"}"#);
148        match parse.unwrap_err().kind() {
149            ErrorKind::Serialization => {}
150            err => panic!("Unexpected error: {err:?}"),
151        }
152    }
153
154    #[test]
155    fn can_convert_from_core_result() {
156        let original: Result<Response, StdError> = Ok(Response::default());
157        let converted: ContractResult<Response> = original.into();
158        assert_eq!(converted, ContractResult::Ok(Response::default()));
159
160        let original: Result<Response, StdError> = Err(StdError::msg("broken"));
161        let converted: ContractResult<Response> = original.into();
162        assert_eq!(
163            converted,
164            ContractResult::Err("kind: Other, error: broken".to_string())
165        );
166    }
167
168    #[test]
169    fn can_convert_to_core_result() {
170        let original = ContractResult::Ok(Response::default());
171        let converted: Result<Response, String> = original.into();
172        assert_eq!(converted, Ok(Response::default()));
173
174        let original = ContractResult::Err("went wrong".to_string());
175        let converted: Result<Response, String> = original.into();
176        assert_eq!(converted, Err("went wrong".to_string()));
177    }
178}