tryvial/
lib.rs

1//! A small crate for Ok-wrapping and try blocks.
2//! This is compatible with [`Result`](::core::result::Result), [`Option`](::core::option::Option),
3//! and any type implementing the unstable [`std::ops::Try`](https://doc.rust-lang.org/std/ops/trait.Try.html) trait.
4//!
5//! *This crate does not require nightly Rust.*
6//!
7//! # Overview
8//!
9//! The macro `try_fn` is used to perform Ok-wrapping on the return value of a function.
10//!
11//! Before:
12//! ```
13//! fn main() -> Result<(), Box<dyn std::error::Error>> {
14//!     println!("Enter your name: ");
15//!     let mut name = String::new();
16//!     std::io::stdin().read_line(&mut name)?;
17//!     println!("Hello, {name}!");
18//!     Ok(()) // this is ugly
19//! }
20//! ```
21//!
22//! After:
23//! ```
24//! # use tryvial::try_fn;
25//! #[try_fn]
26//! fn main() -> Result<(), Box<dyn std::error::Error>> {
27//!     println!("Enter your name: ");
28//!     let mut name = String::new();
29//!     std::io::stdin().read_line(&mut name)?;
30//!     println!("Hello, {name}!");
31//! }
32//! ```
33//!
34//! ---
35//!
36//! The macro [`try_block`](crate::try_block) is an implementation of "try blocks" from nightly rust.
37//!
38//! ```
39//! # use tryvial::try_block;
40//! # type T = (); type E = ();
41//! # fn do_one((): T) -> Result<T, E> { Ok(()) }
42//! # fn do_two((): T) -> Result<T, E> { Ok(()) }
43//! # let x = ();
44//! let result: Result<T, E> = try_block! {
45//!    let a = do_one(x)?;
46//!    let b = do_two(a)?;
47//!    b
48//! };
49//! ```
50//!
51//! ---
52//!
53//! The macro [`wrap_ok`](crate::wrap_ok) simply wraps an expression with the "ok" variant for a given `Try` type.
54//!
55//! ```
56//! # use tryvial::wrap_ok;
57//! assert_eq!(Some(42), wrap_ok!(42));
58//! ```
59
60#![no_std]
61
62#[cfg(feature = "proc-macro")]
63pub use tryvial_proc::{try_fn, tryvial};
64
65/// Performs "Ok-wrapping" on the result of an expression.
66/// This is compatible with [`Result`], [`Option`], [`ControlFlow`], and any type that
67/// implements the unstable [`std::ops::Try`] trait.
68///
69/// The destination type must be specified with a type ascription somewhere.
70#[macro_export]
71macro_rules! wrap_ok {
72    ($e:expr) => {
73        ::core::iter::empty().try_fold($e, |_, __x: ::core::convert::Infallible| match __x {})
74    };
75}
76
77/// Macro for the receiving end of a `?` operation.
78///
79/// ```
80/// # use tryvial::try_block;
81/// // Note: this fails without explicitly specifying the error type.
82/// let y: Result<_, std::num::ParseIntError> = try_block! {
83///     "1".parse::<i32>()? + "2".parse::<i32>()?
84/// };
85/// assert_eq!(y, Ok(3));
86/// ```
87#[macro_export]
88macro_rules! try_block {
89    { $($token:tt)* } => {
90        (|| $crate::wrap_ok!({
91            $($token)*
92        }))()
93    }
94}
95
96#[cfg(test)]
97extern crate alloc;
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102    use core::ops::ControlFlow;
103
104    /// This is a doc comment.
105    #[try_fn]
106    /// And another one.
107    pub fn with_doc_comments() -> ControlFlow<usize> {
108        ControlFlow::Break(11)?;
109    }
110
111    #[test]
112    fn test_with_doc() {
113        assert!(matches!(with_doc_comments(), ControlFlow::Break(11)));
114    }
115
116    #[try_fn]
117    unsafe fn generic_fn<T, U: Clone>(x: T, y: &U) -> ControlFlow<U>
118    where
119        T: PartialEq<U>,
120    {
121        if x == *y {
122            ControlFlow::Break(y.clone())?;
123        }
124    }
125
126    #[test]
127    fn test_generic_fn() {
128        use alloc::borrow::ToOwned;
129        match unsafe { generic_fn("Hello, world", &"Hello, world".to_owned()) } {
130            ControlFlow::Break(s) => assert_eq!(s, "Hello, world"),
131            ControlFlow::Continue(()) => unreachable!(),
132        }
133    }
134
135    struct MyStruct(u32);
136
137    impl core::convert::TryFrom<&str> for MyStruct {
138        type Error = core::num::ParseIntError;
139        #[try_fn]
140        fn try_from(value: &str) -> Result<Self, Self::Error> {
141            Self(value.parse()?)
142        }
143    }
144
145    #[test]
146    fn test_parse() {
147        assert!(matches!("34".try_into(), Ok(MyStruct(34))));
148    }
149}