konst/macros/unwrapping.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
/// `?`-like macro, which allows optionally mapping errors.
///
/// `?` currently doesn't work in `const fn`s because as of Rust 1.65.0
/// trait methods don't work in `const fn`s.
///
/// # Examples
///
/// ### Basic
///
/// ```rust
/// use konst::try_;
///
/// const OK: Result<&str, u8> = expect_no_whitespace("hello");
/// assert_eq!(OK, Ok("hello"));
///
/// const ERR: Result<&str, u8> = expect_no_whitespace("hello world");
/// assert_eq!(ERR, Err(b' '));
///
///
/// const fn expect_no_whitespace(string: &str) -> Result<&str, u8> {
/// let bytes = string.as_bytes();
/// konst::for_range!{i in 0..bytes.len() =>
/// try_!(assert_not_whitespace(bytes[i]));
/// }
/// Ok(string)
/// }
///
/// const fn assert_not_whitespace(byte: u8) -> Result<(), u8> {
/// if matches!(byte, b'\t' | b'\n' | b'\r' | b' ') {
/// Err(byte)
/// } else {
/// Ok(())
/// }
/// }
///
/// ```
///
/// ### Mapping errors
///
/// ```rust
/// use konst::try_;
///
/// const EVENS: Result<[Even; 4], u32> =
/// array_to_even([0, 2, 4, 6]);
///
/// let new = |n| Even::new(n).unwrap();
/// assert_eq!(EVENS, Ok([new(0), new(2), new(4), new(6)]));
///
///
/// const UNEVEN: Result<[Even; 4], u32> =
/// array_to_even([0, 2, 5, 6]);
///
/// assert_eq!(UNEVEN, Err(5));
///
///
/// const fn array_to_even(arr: [u32; 4]) -> Result<[Even; 4], u32> {
/// let mut ret = [Even::ZERO; 4];
///
/// konst::for_range!{i in 0..4 =>
/// ret[i] = try_!(Even::new(arr[i]), map_err = |e| e.get() );
/// }
///
/// Ok(ret)
/// }
///
/// #[derive(Debug, PartialEq)]
/// pub struct Even(u32);
///
/// impl Even {
/// const ZERO: Even = Even(0);
///
/// pub const fn new(number: u32) -> Result<Self, NotEven> {
/// if number % 2 == 0 {
/// Ok(Even(number))
/// } else {
/// Err(NotEven(number))
/// }
/// }
/// }
///
/// #[derive(Debug, PartialEq)]
/// pub struct NotEven(u32);
///
/// impl NotEven {
/// pub const fn get(&self) -> u32 {
/// self.0
/// }
/// }
///
/// use std::fmt::{self, Display};
///
/// impl Display for NotEven {
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
/// fmt::Debug::fmt(self, f)
/// }
/// }
///
/// impl std::error::Error for NotEven {}
///
/// ```
///
#[macro_export]
macro_rules! try_ {
($e:expr, map_err = |$($pati:pat_param)?| $v:expr $(,)*) => {
match $e {
$crate::__::Ok(x) => x,
$crate::__::Err{$(0: $pati,)? ..} => return $crate::__::Err($v),
}
};
($e:expr $(,)*) => {{
match $e {
$crate::__::Ok(x) => x,
$crate::__::Err(e) => return $crate::__::Err(e),
}
}};
}
/// `?`-like macro for `Option`s.
///
/// # Example
///
/// ```rust
/// use konst::try_opt;
///
/// const SOME: Option<u8> = sum_u8s(&[3, 5, 8, 13]);
/// assert_eq!(SOME, Some(29));
///
/// const NONE: Option<u8> = sum_u8s(&[3, 5, 8, 13, 240]);
/// assert_eq!(NONE, None);
///
/// const fn sum_u8s(mut nums: &[u8]) -> Option<u8> {
/// let mut sum = 0_u8;
/// while let [first, rem @ ..] = nums {
/// nums = rem;
/// # sum = try_opt!(checked_add(sum, *first));
/// # /*
/// sum = try_opt!(sum.checked_add(*first));
/// # */
/// }
/// Some(sum)
/// }
///
/// # const fn checked_add(l: u8, r: u8) -> Option<u8> {
/// # let (res, overflowed) = l.overflowing_add(r);
/// # if overflowed { None } else { Some(res) }
/// # }
/// ```
///
#[macro_export]
macro_rules! try_opt {
($opt:expr $(,)*) => {
match $opt {
$crate::__::Some(x) => x,
$crate::__::None => return $crate::__::None,
}
};
}