Skip to content

Commit cc3c638

Browse files
committed
Implement custom result Re with stack trace
1 parent d83c40f commit cc3c638

File tree

3 files changed

+107
-4
lines changed

3 files changed

+107
-4
lines changed

Cargo.toml

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "gstuff"
3-
version = "0.7.6"
3+
version = "0.7.7"
44
edition = "2018"
55
authors = ["ArtemGr <artemciy@gmail.com>"]
66

@@ -20,9 +20,11 @@ base62 = ["num-bigint", "num-traits", "smallvec"]
2020
base62j = ["smallvec"]
2121
base91 = ["smallvec"]
2222
rdtsc = []
23+
re = ["fomat-macros"]
2324

2425
[dependencies]
2526
crossterm = {version = "0.21", optional = true}
27+
fomat-macros = {version = "0.3", optional = true}
2628
lazy_static = "1"
2729
libc = "0.2"
2830
num-bigint = {version = "0.4", optional = true}

gstuff.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
#![cfg_attr(feature = "nightly", feature(asm))]
44

5-
// https://github.com/rust-lang/rust/issues/57563
6-
#![cfg_attr(feature = "nightly", feature(const_panic))]
7-
85
#![cfg_attr(feature = "nightly", feature(test))]
96

7+
#![cfg_attr(feature = "re", feature(try_trait_v2))]
8+
#![cfg_attr(feature = "re", feature(never_type))]
9+
1010
#[allow(unused_imports)] #[macro_use] extern crate lazy_static;
1111
extern crate libc;
1212
#[cfg(feature = "crossterm")] extern crate crossterm;
@@ -108,6 +108,8 @@ pub fn filename<'a> (path: &'a str) -> &'a str {
108108

109109
#[cfg(feature = "winapi")] pub mod win;
110110

111+
#[cfg(feature = "re")] pub mod re;
112+
111113
// --- status line -------
112114

113115
#[cfg(all(feature = "crossterm", not(target_arch = "wasm32")))]

re.rs

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// https://doc.rust-lang.org/nightly/std/ops/trait.Try.html
2+
// https://github.com/rust-lang/rfcs/blob/master/text/3058-try-trait-v2.md
3+
// https://github.com/rust-lang/rust/issues/84277#issuecomment-905821708
4+
// https://blog.rust-lang.org/2020/08/27/Rust-1.46.0.html#track_caller
5+
6+
use fomat_macros::fomat;
7+
use std::fmt;
8+
use std::ops::{ControlFlow, FromResidual, Try};
9+
use std::panic::Location;
10+
use super::filename;
11+
12+
#[macro_export]
13+
macro_rules! fail {($($args: tt)+) => (return $crate::re::Re::fail (fomat! ($($args)+)))}
14+
15+
#[derive(Debug)]
16+
#[must_use = "this `Re` may be an `Err` variant, which should be handled"]
17+
pub enum Re<T> {Ok (T), Err (String)}
18+
19+
impl<T> Try for Re<T> {
20+
type Output = T;
21+
type Residual = Re<!>;
22+
23+
#[inline]
24+
fn from_output (c: T) -> Self {Re::Ok (c)}
25+
26+
#[inline]
27+
fn branch (self) -> ControlFlow<Self::Residual, T> {
28+
match self {
29+
Re::Ok (c) => ControlFlow::Continue (c),
30+
Re::Err (e) => ControlFlow::Break (Re::Err (e))}}}
31+
32+
impl<T> FromResidual<Re<!>> for Re<T> {
33+
#[track_caller]
34+
fn from_residual (x: Re<!>) -> Self {
35+
let err = match x {Re::Ok (_) => unreachable!(), Re::Err (e) => e};
36+
let loc = Location::caller();
37+
let err = fomat! ((filename (loc.file())) ':' (loc.line()) "] " (err));
38+
Re::Err (err)}}
39+
40+
impl<T, O, E> FromResidual<Result<O, E>> for Re<T> where E: fmt::Display {
41+
#[track_caller]
42+
fn from_residual (x: Result<O, E>) -> Self {
43+
let err = match x {Result::Ok (_) => unreachable!(), Result::Err (e) => e};
44+
let loc = Location::caller();
45+
let err = fomat! ((filename (loc.file())) ':' (loc.line()) "] " (err));
46+
Re::Err (err)}}
47+
48+
impl<T> Re<T> {
49+
#[track_caller]
50+
pub fn fail<E: fmt::Display> (emsg: E) -> Re<T> {
51+
let loc = Location::caller();
52+
let err = fomat! ((filename (loc.file())) ':' (loc.line()) "] " (emsg));
53+
Re::Err (err)}
54+
55+
#[inline]
56+
pub fn err (self) -> Option<String> {
57+
match self {Re::Ok (_) => None, Re::Err (e) => Some (e)}}
58+
59+
#[inline]
60+
#[track_caller]
61+
pub fn expect (self, msg: &str) -> T {
62+
match self {Re::Ok (k) => k, Re::Err (err) => panic! ("{}: {:?}", msg, err)}}
63+
64+
#[inline]
65+
#[track_caller]
66+
pub fn unwrap (self) -> T {
67+
match self {Re::Ok (k) => k, Re::Err (err) => panic! ("called `Re::unwrap()` on an `Err` value: {:?}", err)}}
68+
69+
#[inline]
70+
pub fn unwrap_or (self, default: T) -> T {
71+
match self {Re::Ok (k) => k, Re::Err (_) => default}}
72+
73+
#[inline]
74+
pub fn map<U, F: FnOnce (T) -> U> (self, op: F) -> Re<U> {
75+
match self {Re::Ok (k) => Re::Ok (op (k)), Re::Err (e) => Re::Err (e)}}}
76+
77+
#[cfg(all(test, feature = "nightly"))] mod test {
78+
extern crate test;
79+
80+
use super::*;
81+
82+
// cargo bench --features nightly,re
83+
84+
fn foobar (succ: bool) -> Re<bool> {
85+
if test::black_box (succ) {Re::Ok (true)} else {Re::fail ("!succ")}}
86+
87+
fn bang (succ: bool) -> Re<()> {
88+
let b = test::black_box (foobar (succ)?);
89+
assert_eq! (true, b);
90+
Re::Ok(())}
91+
92+
#[bench] fn err (bm: &mut test::Bencher) {
93+
bm.iter (|| {
94+
let e = bang (false) .err().unwrap();
95+
assert! (e.starts_with ("re:"));
96+
assert! (e.ends_with ("] !succ"))})}
97+
98+
#[bench] fn ok (bm: &mut test::Bencher) {
99+
bm.iter (|| {bang (true) .expect ("!user")})}}

0 commit comments

Comments
 (0)