Skip to content

Commit 7ad91b8

Browse files
committed
tear! now uses convert::From and add intra-doc links
1 parent ffb2d8b commit 7ad91b8

File tree

9 files changed

+134
-54
lines changed

9 files changed

+134
-54
lines changed

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88

9+
## [0.5.0] – 2021-04-11
10+
11+
### Changed
12+
- `tear!` now uses the `convert::From` trait to automatically convert types like `?`
13+
14+
### Fixed
15+
- `twist!` no longer requires traits to be in scope
16+
- `ret!` is now documented
17+
18+
919
## [0.4.0] — 2020-08-03
1020

1121
Removed `rip!` and `fear!`. Items marked as `(dev)` are no longer considered public API.
@@ -22,6 +32,7 @@ Removed `rip!` and `fear!`. Items marked as `(dev)` are no longer considered pub
2232
- `tear::prelude` no longer exports the Judge and Return symbols.
2333
- `Judge::ret_error` as it was supposed to be `(dev)`
2434

35+
2536
## [0.3.0] – 2020-05-27
2637

2738
Make `terror!` work for booleans and add the `next_if!` and `last_if!` macros.
@@ -38,6 +49,7 @@ Make `terror!` work for booleans and add the `next_if!` and `last_if!` macros.
3849
### Changed
3950
- Use `Maru` instead of `()` in `gut` and `Judge` for `Option`
4051

52+
4153
## [0.2.0] – 2020-05-26
4254

4355
Implemented typed loop control with `twist!`. Make `terror!` fully compatible with `?`.
@@ -67,6 +79,7 @@ Implemented typed loop control with `twist!`. Make `terror!` fully compatible wi
6779
- `or_else` and `map_ret` methods on `ValRet`. Use `result().or_else()` or `result().map_err()`
6880
instead
6981

82+
7083
## [0.1.1] – 2020-05-19
7184

7285
This release was to test if I could overwrite
@@ -76,6 +89,7 @@ Turns out you can, but the squatter is still in the list of owners and authors.
7689
### Added
7790
- Licenses
7891

92+
7993
## [0.1.0] – 2020-05-19
8094

8195
Initial release

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "tear"
33
description = "Typed early returns and loop control + Syntax sugar for try!-like error handling"
4-
version = "0.4.0"
4+
version = "0.5.0"
55
authors = ["Tilwa Qendov <[email protected]>"]
66
edition = "2018"
77
license = "MIT OR Apache-2.0"

notes.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
# Notes
22

3+
## TODO
4+
5+
Nothing currently
6+
7+
## Thoughts
8+
39
- `Moral` is not meant to be manipulated, nor is `ValRet`. If you need combinators,
410
use `Judge`'s side or result methods.
511
- If there's a use-case where Judge -> Return blanket trait implementation poses a problem, I should
612
replace it with a macro. Also, if auto traits get stabilized, we could let the user disable it.
7-
- I should probably use proc_macros instead of abusing macros for `__impl_twist!`, but I don't know how
13+
- I should probably use proc\_macros instead of abusing macros for `__impl_twist!`, but I don't know how
814
to write one, and docs aren't easily found.
915
- Convenience functions are named shortly and memorable. Trait functions are named boringly and at least
1016
two words.
1117

12-
## TODO
13-
1418
## Useful resources
1519
- <https://stackoverflow.com/questions/40302026/what-does-the-tt-metavariable-type-mean-in-rust-macros>
1620
- <https://medium.com/@phoomparin/a-beginners-guide-to-rust-macros-5c75594498f1>

src/lib.rs

Lines changed: 81 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ In this module, we define in order
130130
pub mod overview; // For documentation
131131
pub mod prelude;
132132
pub mod extra;
133-
pub mod trait_impl; // Move the trait implementions as they are quite noisy
133+
pub mod trait_impl; // Move the trait implementations as they are quite noisy
134134
pub mod twist_impl; // Currently only for `twist!`
135135
#[macro_use] pub mod util; // Utility macros that aren't the main focus. To reduce file size.
136136

@@ -147,13 +147,14 @@ use ValRet::*;
147147
use Moral::*;
148148
#[cfg(feature = "combinators")] use either::Either::{self, *};
149149

150-
/** Represents a usable value or an early return. Use with `tear!`
150+
/** Represents a usable value or an early return. Use with [`tear!`]
151151
152152
# Description
153153
154154
The idea is to type an early return. The early return either evaluates to something (Val) or
155155
returns early (Ret).
156156
*/
157+
#[must_use = "Suggestion: use tear! to handle it"]
157158
#[derive(PartialEq, Debug, Clone)]
158159
pub enum ValRet<V, R> {
159160
/// The usable value
@@ -164,7 +165,7 @@ pub enum ValRet<V, R> {
164165

165166
/**
166167
**NB**: Other combinators such as `and`, `and_then`, `or`, `map_val`
167-
aren't implemented because I didn't need them and not because they aren't useful.
168+
aren't implemented because I didn't need them, not because they aren't useful.
168169
169170
Examples will all use the following two variables
170171
```
@@ -182,7 +183,7 @@ impl<V, R> ValRet<V, R> {
182183
pub fn ret (self) -> Option<R> { maybe_match! { self, Ret(r) => r } }
183184
}
184185

185-
/// Convert into ValRet
186+
/// Convert into [`ValRet`]
186187
pub trait Return where Self :Sized {
187188
/// The Val in ValRet
188189
type Value;
@@ -193,7 +194,7 @@ pub trait Return where Self :Sized {
193194
fn into_valret (self) -> ValRet<Self::Value, Self::Returned>;
194195
}
195196

196-
/// A notion of good and bad for the `terror!` macro
197+
/// A notion of good and bad for the [`terror!`] macro
197198
#[derive(PartialEq, Debug, Clone)]
198199
pub enum Moral<Y, N> {
199200
/// The good
@@ -248,11 +249,11 @@ impl<Y, N> Moral<Y, N> {
248249

249250
/* Special conversions */
250251

251-
/** (dev) Convert to a `Looping` by mapping Good to Resume, and Bad through a function
252+
/** (dev) Convert to a [`Looping`] by mapping Good to Resume, and Bad through a function
252253
253254
The function `f` takes the bad value and maps it to a `Looping` value.
254255
255-
Used in the `twist!` macro with the mapping (`=>`) syntax. See `twist!` documentation.
256+
Used in the `twist!` macro with the mapping (`=>`) syntax. See [`twist!`] documentation.
256257
*/
257258
pub fn resume_or_else<B> (self, f :impl FnOnce(N) -> Looping<Y, B>) -> Looping<Y, B> {
258259
match self {
@@ -262,9 +263,9 @@ impl<Y, N> Moral<Y, N> {
262263
}
263264
}
264265

265-
/** Convert from and to Moral. Used for the macro map syntax.
266+
/** Convert from and to [`Moral`]. Used for the macro map syntax.
266267
267-
This mirrors the `std::ops::Try` trait.
268+
This mirrors the [`ops::Try`](`core::ops::Try`) trait.
268269
269270
It is used for the `=>` mapping syntax of macros, to differentiate the value we want to keep from
270271
the value we want to map through the function.
@@ -304,9 +305,9 @@ pub trait Judge :Sized {
304305
}
305306
}
306307

307-
/** Turns a `ValRet` into a value or an early return
308+
/** Turns a [`ValRet`] into a value or an early return
308309
309-
It also coerces its argument to a ValRet (Return trait).
310+
It also coerces its argument to a `ValRet` ([`Return`] trait).
310311
311312
# Description
312313
@@ -326,6 +327,10 @@ let x = tear! { $e => $f }
326327
Same as the previous form, but the return value `r` is first mapped through $f before returning.
327328
In short, we return `$f(r)`.
328329
330+
Additionally, both forms make use of the [`convert::From`](`core::convert::From`) trait to automatically convert
331+
the value when returning it. This behaviour is the same as the try operator `?`.
332+
You may need to be more specific with type annotations so that the compiler can infer the right types.
333+
329334
# Examples
330335
331336
tear! with Val and Ret.
@@ -335,7 +340,7 @@ tear! with Val and Ret.
335340
# use tear::prelude::*;
336341
#
337342
// "Ian" is assigned to name
338-
let name = tear! { Val("Ian") };
343+
let name = tear! { Val::<_, ()>("Ian") };
339344
# assert_eq![ name, "Ian" ];
340345
341346
# fn func () -> i32 {
@@ -379,6 +384,23 @@ fn string_id(s: OsString) -> String {
379384
# assert_eq![ string_id(OsString::from("ROOT")), "4" ];
380385
```
381386
387+
Automatic conversion with `convert::From`
388+
389+
```rust
390+
# use tear::prelude::*;
391+
#[derive(Debug, PartialEq, Eq)]
392+
struct MyInt(u8);
393+
impl std::convert::From<u8> for MyInt {
394+
fn from(x :u8) -> Self { Self(x) }
395+
}
396+
397+
fn five_as_myint() -> MyInt {
398+
tear! { Ret(5) }
399+
}
400+
401+
assert_eq![ five_as_myint(), MyInt(5) ];
402+
```
403+
382404
# Naming
383405
384406
The name "tear" comes from the image of tearing apart the the usable value from the early return.
@@ -390,7 +412,7 @@ macro_rules! tear {
390412
( $e:expr ) => {
391413
match $crate::Return::into_valret($e) {
392414
$crate::ValRet::Val(v) => v,
393-
$crate::ValRet::Ret(r) => return r,
415+
$crate::ValRet::Ret(r) => return $crate::From::from(r),
394416
}
395417
};
396418
// With a mapping function eg. `tear! { $e => |v| v }` or `tear! { $e => func }`
@@ -435,7 +457,7 @@ Early return a value: recursively computing the length of a slice.
435457
# #[macro_use] extern crate tear;
436458
fn len (v: &[i32]) -> usize {
437459
// Base case
438-
tear_if! { v.is_empty(), 0 }
460+
tear_if! { v.is_empty(), 0 as usize }
439461
440462
// Recursion
441463
1 + len(&v[1..])
@@ -495,10 +517,10 @@ macro_rules! tear_if {
495517
};
496518
}
497519

498-
/** `try!`-like error-handling macro
520+
/** [`try!`]-like error-handling macro
499521
500522
`terror!` is like `tear!`, but stronger and more righteous.
501-
It automatically converts the Bad value to the return type Bad value (Judge trait).
523+
It automatically converts the Bad value to the return type Bad value ([`Judge`] trait).
502524
503525
# Description
504526
@@ -516,6 +538,9 @@ let x = terror! { $e => $f };
516538
Same as the previous form, but the bad `value` is first mapped through $f before returning.
517539
In short, we return `from_bad($f(value))`.
518540
541+
Both forms make use of the [`convert::From`](`core::convert::From`) trait to convert the bad value,
542+
making it fully compatible with `try!` and the `?` operator.
543+
519544
# Explanation using examples
520545
521546
The description is especially terse on purpose: it is really hard to explain what `terror!` does without using examples.
@@ -641,6 +666,46 @@ To do so, we extract the `ParseIntError`, and wrap it into our custom error with
641666
That is the role of the function following the `=>` arrow: it converts the error type of
642667
the left statement, into the function return error type.
643668
669+
### Automatic conversion just like `?`
670+
671+
Since `terror!` mimics `?`, it also supports autoconversion using the `convert::From` trait.
672+
673+
```rust
674+
# use tear::prelude::*;
675+
# use std::io;
676+
# macro_rules! assert_match {
677+
# ( $e:expr, $($p:pat)|+ ) => {
678+
# match $e {
679+
# $($p)|+ => (),
680+
# ref e => panic!("assertion failed: `{:?}` does not match `{}`", e, stringify!($($p)|+)),
681+
# }
682+
# }
683+
# }
684+
# #[derive(Debug)]
685+
enum CustomError {
686+
IOError(io::Error),
687+
OtherError,
688+
}
689+
690+
impl std::convert::From<io::Error> for CustomError {
691+
fn from(x :io::Error) -> Self {
692+
Self::IOError(x)
693+
}
694+
}
695+
696+
# fn fail_with_io_error() -> io::Result<()> {
697+
# Err(io::Error::new(io::ErrorKind::Other, "oh no!"))
698+
# }
699+
#
700+
fn auto_convert() -> Result<bool, CustomError> {
701+
terror! { fail_with_io_error() };
702+
Ok(false)
703+
}
704+
705+
assert_match![ auto_convert(), Err(CustomError::IOError(_)) ];
706+
```
707+
708+
644709
# `terror!` vs. `?` when moving into closures
645710
646711
The only difference between `terror!` and `?` is that since `terror!` is a macro,

src/overview.rs

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ All symbols are accessible directly from the crate root as we reexport them all.
1515
1616
# Early returns
1717
18-
We represent an early return with `ValRet` and process it with `tear!`. The macro accepts any
19-
type that knows how to convert to a `ValRet` using the `Return` trait.
18+
We represent an early return with [`ValRet`] and process it with [`tear!`]. The macro accepts any
19+
type that knows how to convert to a `ValRet` using the [`Return`] trait.
2020
21-
We use `tear!` in `tear_if!` to implement early returns as a syntax.
21+
We use `tear!` in [`tear_if!`] to implement early returns as a syntax.
2222
2323
# Mapping syntax
2424
@@ -36,10 +36,10 @@ This is why arguments must implement the `Judge` trait that knows how to convert
3636
3737
# Error handling
3838
39-
`terror!` is the error-handling macro. It depends on `Judge` to decide if the value is usable
39+
`terror!` is the error-handling macro. It depends on [`Judge`] to decide if the value is usable
4040
or not.
4141
42-
A short way of discarding the error value in a function returning `Option<T>`, is to use the `gut`
42+
A short way of discarding the error value in a function returning `Option<T>`, is to use the [`gut`]
4343
function:
4444
4545
```
@@ -52,7 +52,7 @@ fn f () -> Option<i32> {
5252
```
5353
5454
If you need to do some things before returning `None`, use a block, and return `tear::Maru` at the
55-
end. `Maru` is the placeholder type used to represent the bad value of `Option<T>`, or the good
55+
end. [`Maru`] is the placeholder type used to represent the bad value of `Option<T>`, or the good
5656
and bad values of `bool`.
5757
5858
# Loop control
@@ -70,8 +70,8 @@ assert_eq![ x, 3 ];
7070
```
7171
7272
In the complex case where you want to breakval from multiple loops with a different type, you can
73-
use `Box<dyn Any>` to hide those type. We provide the `anybox!` macro to take the concrete type,
74-
and wrap it into a `Box<dyn Any>` object. See `twist!` documentation for more information.
73+
use `Box<dyn Any>` to hide those type. We provide the [`anybox!`] macro to take the concrete type,
74+
and wrap it into a `Box<dyn Any>` object. See [`twist!`] documentation for more information.
7575
7676
```
7777
use tear::prelude::*;
@@ -88,7 +88,7 @@ assert_eq![ x, 3 ];
8888
```
8989
9090
For simple cases where you only break from one loop (ie. when you don't use `-labels`), you can
91-
use the `last!`, `next!`, and `resume!` as shortcuts for the right-hand side of `twist!`:
91+
use the [`last!`], [`next!`], and [`resume!`] as shortcuts for the right-hand side of `twist!`:
9292
9393
```
9494
use tear::extra::*;
@@ -98,14 +98,9 @@ loop {
9898
}
9999
```
100100
101-
There's also `next_if!` and `last_if!` macros that continue or break the loop based on a condition
101+
There's also [`next_if!`] and [`last_if!`] macros that continue or break the loop based on a condition
102102
or a pattern match.
103103
104-
# Legacy
105-
106-
`rip!` is an alias for `terror!`, and `fear!` is an alias for `tear!`. This is because mapping
107-
syntax and normal syntax used to separate.
108-
109104
# Add functionality to your own types
110105
111106
If you want to enable the mapping syntax for your type.
@@ -126,3 +121,4 @@ If using the "experimental" crate feature, then you only need to implement the `
126121
`Judge` and `Return` trait will be automatically implemented.
127122
128123
*/
124+
use super::*;

src/trait_impl.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::*;
1515

1616
/** A placeholder type with a single value ◯
1717
18-
It mirrors the `NoneError` type. For example, it is used in conjunction with `Moral` to
18+
It mirrors the [`NoneError`](`core::option::NoneError`) type. For example, it is used in conjunction with [`Moral`] to
1919
represent the bad types for `bool` or `Option<T>`.
2020
2121
# Examples
@@ -39,7 +39,7 @@ fn f() -> () {
3939
4040
# See also
4141
42-
- the `gut` function, that takes over the right-hand side
42+
- the [`gut`] function, that takes over the right-hand side
4343
*/
4444
#[derive(Copy, Debug, Clone)]
4545
pub struct Maru;

0 commit comments

Comments
 (0)