A => .gitignore +2 -0
@@ 1,2 @@
+/target
+Cargo.lock
A => .rustfmt.toml +1 -0
@@ 1,1 @@
+version = "Two"
A => Cargo.toml +9 -0
@@ 1,9 @@
+[package]
+name = "and_then_some"
+description = "Provides an extension trait for `bool` with methods that return `Option<T>`"
+authors = ["Jonas Platte <jplatte@posteo.de>"]
+version = "1.0.0"
+license = "MIT"
+readme = "README.md"
+repository = "https://git.sr.ht/~jplatte/and_then_some"
+edition = "2018"
A => LICENSE +19 -0
@@ 1,19 @@
+Copyright (c) 2020 Jonas Platte
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
A => README.md +23 -0
@@ 1,23 @@
+# and_then_some
+
+Provides an extension trait for `bool` with methods that return `Option<T>`.
+
+This crate provides four methods; they all are defined in `BoolExt`, all take `self` and another
+argument, and all return `Option<T>`. The only difference is in the type of the second argument:
+
+* `and`: `Option<T>`
+* `and_some`: `T`
+* `and_then`: `impl FnOnce() -> Option<T>`
+* `and_then_some`: `impl FnOnce() -> T`
+
+I do not consider this crate to be useful myself, I just created it to draw some attention to
+[the tracking issue for RFC 2757 (methods for converting from `bool` to `Option<T>`)][issue].
+
+This crate achieves maximum consistency: `Some`-wrapping is given a `_some` suffix in the method
+names while methods that take a function over a plain value have a `_then` suffix; `and_then` is
+consistent with `Option::and_then` in the type of its second argument. This naming scheme results in
+the amusing name `and_then_some` for one of the methods, which made me choose it as the crate name
+too. I hope `.and_then_some` can be stabilized under a different name though, because to me it seems
+like the most common out of the four operations.
+
+[issue]: https://github.com/rust-lang/rust/issues/64260
A => src/lib.rs +96 -0
@@ 1,96 @@
+//! Provides an extension trait for `bool` with methods that return `Option<T>`.
+
+#![no_std]
+#![forbid(unsafe_code)]
+#![warn(missing_docs)]
+
+/// Extends the primitive `bool` type with four methods to create a `Option`s.
+pub trait BoolExt {
+ /// `if self { value } else { None }`
+ fn and<T>(self, value: Option<T>) -> Option<T>;
+
+ /// `if self { Some(value) } else { None }`
+ fn and_some<T>(self, value: T) -> Option<T>;
+
+ /// `if self { f() } else { None }`
+ fn and_then<T>(self, f: impl FnOnce() -> Option<T>) -> Option<T>;
+
+ /// `if self { Some(f()) } else { None }`
+ fn and_then_some<T>(self, f: impl FnOnce() -> T) -> Option<T>;
+}
+
+impl BoolExt for bool {
+ fn and<T>(self, value: Option<T>) -> Option<T> {
+ if self { value } else { None }
+ }
+
+ fn and_some<T>(self, value: T) -> Option<T> {
+ if self { Some(value) } else { None }
+ }
+
+ fn and_then<T>(self, f: impl FnOnce() -> Option<T>) -> Option<T> {
+ if self { f() } else { None }
+ }
+
+ fn and_then_some<T>(self, f: impl FnOnce() -> T) -> Option<T> {
+ if self { Some(f()) } else { None }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::BoolExt;
+
+ #[test]
+ fn and() {
+ assert_eq!(true.and(Some("hello")), Some("hello"));
+ assert_eq!(true.and(Option::<&str>::None), None);
+ assert_eq!(false.and(Some("world")), None);
+ assert_eq!(false.and(Option::<&str>::None), None);
+ }
+
+ #[test]
+ fn and_some() {
+ assert_eq!(true.and_some(()), Some(()));
+ assert_eq!(false.and_some(()), None);
+ }
+
+ #[test]
+ fn and_then() {
+ assert_eq!(true.and_then(|| Some(1 + 1)), Some(2));
+ assert_eq!(true.and_then(|| Option::<u32>::None), None);
+ assert_eq!(false.and_then(|| Some(1 + 1)), None);
+ assert_eq!(false.and_then(|| Option::<u32>::None), None);
+ }
+
+ #[test]
+ fn and_then_some() {
+ assert_eq!(true.and_then_some(|| true), Some(true));
+ assert_eq!(true.and_then_some(|| false), Some(false));
+ assert_eq!(false.and_then_some(|| true), None);
+ assert_eq!(false.and_then_some(|| false), None);
+ }
+
+ #[test]
+ fn side_effects() {
+ let mut ct = 0;
+
+ let _ = true.and_then(|| {
+ ct += 1;
+ Some(())
+ });
+ assert_eq!(ct, 1);
+
+ let _ = false.and_then(|| {
+ ct += 1;
+ Some(())
+ });
+ assert_eq!(ct, 1);
+
+ let _ = true.and_then_some(|| ct += 1);
+ assert_eq!(ct, 2);
+
+ let _ = false.and_then_some(|| ct += 1);
+ assert_eq!(ct, 2);
+ }
+}