Skip to content

Commit

Permalink
Support if let in condval!
Browse files Browse the repository at this point in the history
  • Loading branch information
nvzqz committed May 9, 2023
1 parent 10261e9 commit 339b729
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 4 deletions.
10 changes: 7 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@ The format is based on [Keep a Changelog] and this project adheres to

### Added

- `if let` pattern matching in [`condval!`].

## [1.1.0] - 2023-04-25

- [`condval!`](https://docs.rs/condtype/latest/condtype/macro.condval.html)
macro to construct [conditionally-typed][CondType] values.
### Added

- [`condval!`] macro to construct [conditionally-typed][CondType] values.

## 1.0.0 - 2023-04-18

Expand All @@ -30,4 +33,5 @@ The format is based on [Keep a Changelog] and this project adheres to
[Unreleased]: https://github.com/nvzqz/condtype/compare/v1.1.0...HEAD
[1.1.0]: https://github.com/nvzqz/condtype/compare/v1.0.0...v1.1.0

[CondType]: https://docs.rs/condtype/latest/condtype/type.CondType.html
[CondType]: https://docs.rs/condtype/latest/condtype/type.CondType.html
[`condval!`]: https://docs.rs/condtype/latest/condtype/macro.condval.html
44 changes: 43 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,21 @@ pub type CondType<const B: bool, T, F> = <imp::CondType<B, T, F> as imp::AssocTy
/// });
/// ```
///
/// This macro can also be used with [`CondType`] to construct [`const`] values:
/// `if let` pattern matching is also supported:
///
/// ```
/// # use condtype::*;
/// const STR: Option<&str> = // ...
/// # None;
///
/// let val = condval!(if let Some(str) = STR {
/// str.to_uppercase()
/// } else {
/// 42
/// });
/// ```
///
/// This macro can be used with [`CondType`] to construct [`const`] values:
///
/// ```
/// use condtype::{condval, CondType};
Expand Down Expand Up @@ -195,6 +209,22 @@ pub type CondType<const B: bool, T, F> = <imp::CondType<B, T, F> as imp::AssocTy
/// [`if`]: https://doc.rust-lang.org/std/keyword.if.html
#[macro_export]
macro_rules! condval {
(if let $pat:pat = $input:block $then:block else $else:block) => {
$crate::condval!(if {
#[allow(unused_variables)]
{ ::core::matches!($input, $pat) }
} {
if let $pat = $input $then else {
::core::unreachable!()
}
} else $else)
};
(if let $pat:pat = $input:block $then:block else $($else:tt)+) => {
$crate::condval!(if let $pat = $input $then else { $crate::condval!($($else)+) })
};
(if let $pat:pat = $($rest:tt)*) => {
$crate::__condval_let_parser!($pat, [] $($rest)*)
};
(if $cond:block $then:block else $else:block) => {
match <() as $crate::__private::If<$cond, _, _>>::PROOF {
$crate::__private::EitherTypeEq::Left(te) => te.coerce($then),
Expand All @@ -221,6 +251,18 @@ macro_rules! __condval_parser {
};
}

/// Helps `condval!` parse any `if let` input expression by accumulating tokens.
#[doc(hidden)]
#[macro_export]
macro_rules! __condval_let_parser {
($pat:pat, [$($input:tt)+] $then:block else $($else:tt)+) => {
$crate::condval!(if let $pat = { $($input)+ } $then else $($else)+)
};
($pat:pat, [$($input:tt)*] $next:tt $($rest:tt)*) => {
$crate::__condval_let_parser!($pat, [$($input)* $next] $($rest)*)
};
}

/// Pseudo-public implementation details for `condval!`.
#[doc(hidden)]
pub mod __private {
Expand Down
38 changes: 38 additions & 0 deletions tests/condval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,41 @@ fn condval_else_if4() {
});
assert_eq!(x, [1, 2, 3]);
}

#[test]
fn condval_let1() {
const VAL: Option<&str> = Some("a");

let x = condval!(if let Some(val) = VAL { val } else { 1 });
assert_eq!(x, "a");
}

#[test]
fn condval_let2() {
const VAL1: Option<i32> = None;
const VAL2: Option<&str> = Some("a");

let x = condval!(if let Some(val1) = VAL1 {
val1
} else if let Some(val2) = VAL2 {
val2
} else {
42.0
});
assert_eq!(x, "a");
}

#[test]
fn condval_let3() {
const VAL1: Option<i32> = None;
const VAL2: Option<&str> = None;

let x = condval!(if let Some(val1) = VAL1 {
val1
} else if let Some(val2) = VAL2 {
val2
} else {
42.0
});
assert_eq!(x, 42.0);
}

0 comments on commit 339b729

Please sign in to comment.