2 releases

0.0.2 Aug 15, 2024
0.0.1 Aug 15, 2024

#483 in Rust patterns

MIT/Apache

32KB
402 lines

This crate offers the tri! macro, a tool for concisely writing tedious try-except statements.

Having to unwrap an option or result from a function is a common task. Although the ? operator can be useful, it forwards exceptions rather than handling them.

[dependencies]
tri_ton = "0.0.2"
use tri_ton::tri;
// Try Formats
tri!(a => b $$ c);
tri!(a => b(A) $$ c);
tri!(a => b[B] $$ c);
tri!(a => [R] $$ c);
  • $$ - A Tri Operator
  • a - The Expression to Evaluate
  • b - The Expected Output of a
  • c - Alternative Expression(s).
  • A - A Field of the Enum Variant b
  • B - A Field of the Enum Variant b
  • R - A Pattern-Rule to Compare to the Output of a

a can be any form of expression in the rust language. foo(), foo::BAR, and 5_usize are acceptable expressions.

b can be most enum variants and paths. Items such as None and crate::foo::<bar>::cin are acceptable paths.

c can be a single or multiple alternate expressions. These expressions are usually evaluated in some form when the output or value of a does not match b.

A can be a variable declaration or null pattern. Multiple comma-separated items can be specified if the enum variant has multiple fields. foo, ref mut bar, and _ are all acceptable items.

In the expressiontri!(foo => Some(bar) $$ ...), bar is returned like the output of a function. In the expression let cin = tri!(foo => Some(bar) $$ ...), the value bar will be bound to the variable cin.

B is similar to A. However, variables declared like B are automatically bound within the local scope. In the expression tri!(foo => bar[cin] $$ ...), the variable cin is automatically bound within the same scope as the tri macro.

R is for matching non-enum values to patterns. ..foo, _, and (FOO, 0..=bar) are all acceptable patterns.

Tri Expressions

tri! has five operators for handling exceptions.

  • Tri-Fall
  • Tri-Fail
  • Tri-Return
  • Tri-Until
  • Tri-While

Tri-Fall

The <> operator can be used to provide a fallback value if an expression doesn't match the given term.

use tri_ton::tri;

fn main() {
   let foo = Some(true);
   
   // If `foo` is Some(bar), `cin` is initialized with `bar`.
   // If `foo` isn't Some,  `cin` is initialized as false.
   let cin = tri!(foo => Some(bar) <> false);
}

Tri-Fail

The -> operator returns the trailing expression in an error if the expression doesn't match the given term.

use tri_ton::tri;

fn foo_bar() -> Result<bool, &'static str> {
   let foo = Some(true);
   
   // If `foo` isn't Some, Err("Error!") is automatically returned.
   tri!(foo => Some[bar] -> "Error!");
   
   // `bar` is now an accessible variable.
   Ok(bar)
}

Tri-Return

The #> operator returns the trailing expression without an error wrapper. It can also be used as a break expression.

#[no_std]
use tri_ton::tri;

fn foo_loop() -> Result<(), &'static str> {
   let foo = Some(true);
   
   // If `foo` isn't Some, Err("Custom Error!") gets returned.
   tri!(foo => Some[bar] #> Err("Custom Error!"));
   
   'a: loop {
       // If `bar` isn't false, the loop will be broken.
       tri!(bar => [false] #> break 'a);
   }
}

Tri-Until

The %> operator repeatedly evaluates the leading expression until it matches the given term. For every time the expression does not match the given term, the tailing expression is evaluated.

use tri_ton::tri;

fn main() {
     let mut foo: u8 = 0;
    
    // Until `foo` equals 10, the loop will increment `foo`.
     tri!(foo => [10] %> foo += 1);
}

Tri-While

The >> operator is similar to a do-while loop. The tailing expression is evaluated with an initial set of values. The leading expression is then evaluated in a loop, and for every time that it matches the given term, the trailing expression is evaluated with those values.

use tri_ton::tri;

fn main() { 
    let foo = |a: u8| -> Option<u8> {
        if a >= 10 { None }
        else { Some(a) }
    };
    
    // This is performed until foo(bar) returns None.
    tri!(foo(bar) => Some[mut bar = 0] >> bar += 1);
    assert_eq!(bar, 10);
    
    // "bar += 1" is performed before "foo(bar)" is checked.
    tri!(foo(bar) => Some[mut bar = bar] >> bar += 1);
    assert_eq!(bar, 11);
}

No runtime deps