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}