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}