cosmwasm_std/assertions.rs
1//! A module containing an assertion framework for CosmWasm contracts.
2//! The methods in here never panic but return errors instead.
3
4/// Quick check for a guard. If the condition (first argument) is false,
5/// then return the second argument `x` wrapped in `Err(x)`.
6///
7/// ```
8/// # enum ContractError {
9/// # DelegatePerm {},
10/// # }
11/// #
12/// # struct Permissions {
13/// # delegate: bool,
14/// # }
15/// #
16/// # fn body() -> Result<(), ContractError> {
17/// # let permissions = Permissions { delegate: true };
18/// use cosmwasm_std::ensure;
19/// ensure!(permissions.delegate, ContractError::DelegatePerm {});
20///
21/// // is the same as
22///
23/// if !permissions.delegate {
24/// return Err(ContractError::DelegatePerm {});
25/// }
26/// # Ok(())
27/// # }
28/// ```
29#[macro_export]
30macro_rules! ensure {
31 ($cond:expr, $e:expr) => {
32 if !($cond) {
33 return Err(core::convert::From::from($e));
34 }
35 };
36}
37
38/// Quick check for a guard. Like `assert_eq!`, but rather than panic,
39/// it returns the third argument `x` wrapped in `Err(x)`.
40///
41/// ```
42/// # use cosmwasm_std::{MessageInfo, Addr};
43/// #
44/// # enum ContractError {
45/// # Unauthorized {},
46/// # }
47/// # struct Config {
48/// # admin: Addr,
49/// # }
50/// #
51/// # fn body() -> Result<(), ContractError> {
52/// # let info = MessageInfo { sender: Addr::unchecked("foo"), funds: Vec::new() };
53/// # let cfg = Config { admin: Addr::unchecked("foo") };
54/// use cosmwasm_std::ensure_eq;
55///
56/// ensure_eq!(info.sender, cfg.admin, ContractError::Unauthorized {});
57///
58/// // is the same as
59///
60/// if info.sender != cfg.admin {
61/// return Err(ContractError::Unauthorized {});
62/// }
63/// # Ok(())
64/// # }
65/// ```
66#[macro_export]
67macro_rules! ensure_eq {
68 ($a:expr, $b:expr, $e:expr) => {
69 // Not implemented via `ensure!` because the caller would have to import both macros.
70 if !($a == $b) {
71 return Err(core::convert::From::from($e));
72 }
73 };
74}
75
76/// Quick check for a guard. Like `assert_ne!`, but rather than panic,
77/// it returns the third argument `x` wrapped in Err(x).
78///
79/// ```
80/// # enum ContractError {
81/// # NotAVoter {},
82/// # }
83/// #
84/// # fn body() -> Result<(), ContractError> {
85/// # let voting_power = 123;
86/// use cosmwasm_std::ensure_ne;
87///
88/// ensure_ne!(voting_power, 0, ContractError::NotAVoter {});
89///
90/// // is the same as
91///
92/// if voting_power != 0 {
93/// return Err(ContractError::NotAVoter {});
94/// }
95/// # Ok(())
96/// # }
97/// ```
98#[macro_export]
99macro_rules! ensure_ne {
100 ($a:expr, $b:expr, $e:expr) => {
101 // Not implemented via `ensure!` because the caller would have to import both macros.
102 if !($a != $b) {
103 return Err(core::convert::From::from($e));
104 }
105 };
106}
107
108#[cfg(test)]
109mod tests {
110 use crate::{errors::ErrorKind, StdError};
111
112 #[test]
113 fn ensure_works() {
114 fn check(a: usize, b: usize) -> Result<(), StdError> {
115 ensure!(a == b, StdError::msg("foobar"));
116 Ok(())
117 }
118
119 let err = check(5, 6).unwrap_err();
120 assert!(matches!(err.kind(), ErrorKind::Other));
121
122 check(5, 5).unwrap();
123 }
124
125 #[test]
126 fn ensure_can_infer_error_type() {
127 let check = |a, b| {
128 ensure!(a == b, StdError::msg("foobar"));
129 Ok(())
130 };
131
132 let err: StdError = check(5, 6).unwrap_err();
133 assert!(matches!(err.kind(), ErrorKind::Other));
134
135 check(5, 5).unwrap();
136 }
137
138 #[test]
139 fn ensure_can_convert_into() {
140 #[derive(Debug)]
141 struct ContractError;
142
143 impl From<StdError> for ContractError {
144 fn from(_original: StdError) -> Self {
145 ContractError
146 }
147 }
148
149 fn check(a: usize, b: usize) -> Result<(), ContractError> {
150 ensure!(a == b, StdError::msg("foobar"));
151 Ok(())
152 }
153
154 let err = check(5, 6).unwrap_err();
155 assert!(matches!(err, ContractError));
156
157 check(5, 5).unwrap();
158 }
159
160 #[test]
161 fn ensure_eq_works() {
162 let check = |a, b| {
163 ensure_eq!(a, b, StdError::msg("foobar"));
164 Ok(())
165 };
166
167 let err: StdError = check("123", "456").unwrap_err();
168 assert!(matches!(err.kind(), ErrorKind::Other));
169 check("123", "123").unwrap();
170 }
171
172 #[test]
173 fn ensure_eq_gets_precedence_right() {
174 // If this was expanded to `true || false == false` we'd get equality.
175 // It must be expanded to `(true || false) == false` and we expect inequality.
176
177 #[allow(clippy::nonminimal_bool)]
178 fn check() -> Result<(), StdError> {
179 ensure_eq!(true || false, false, StdError::msg("foobar"));
180 Ok(())
181 }
182
183 let _err = check().unwrap_err();
184 }
185
186 #[test]
187 fn ensure_ne_works() {
188 let check = |a, b| {
189 ensure_ne!(a, b, StdError::msg("foobar"));
190 Ok(())
191 };
192
193 let err: StdError = check("123", "123").unwrap_err();
194 assert!(matches!(err.kind(), ErrorKind::Other));
195 check("123", "456").unwrap();
196 }
197
198 #[test]
199 fn ensure_ne_gets_precedence_right() {
200 // If this was expanded to `true || false == false` we'd get equality.
201 // It must be expanded to `(true || false) == false` and we expect inequality.
202
203 #[allow(clippy::nonminimal_bool)]
204 fn check() -> Result<(), StdError> {
205 ensure_ne!(true || false, false, StdError::msg("foobar"));
206 Ok(())
207 }
208
209 check().unwrap();
210 }
211}