Crate guard_macros

source ·
Expand description

Usage

// returns when (expr) is evaluated to false
guard!( (expr) );

// returns when refuted (i.e. (expr) doesn't match (pat))
guard!( (pat) = (expr) );

// panics instead of returning (called "Refute Handler")
guard!( (expr) => panic!("false") );
guard!( (pat) = (expr) => panic!("refuted") );

guard! {
    // can be repeated
    (expr),
    (pat) = (expr) => panic!("refuted"),

    // can be scoped and nested
    {
        (expr),
        (pat) = (expr),
        {
            (expr),
            (pat) = (expr) => panic!("baz"),
        } => _, // inherit refute handler
    } => panic!("foo"),
}

Overview

guard_macros provides two macros:

Refute Handler

A “Refute Handler” is an expression that is executed when the condition of a clause is not met. It can be specified by appending => followed by an expression, either:

  • to a single clause
    guard! {
        (pat) = (expr) => panic!("refuted"),
        (expr) => panic!("false"),
    }
  • or to a group of clauses enclosed by { }.
    guard! {
        {
            (pat) = (expr),
            (expr),
        } => panic!("unmet")
    }

    Note: By default, blocks create a new scope, but it can be disabled by prepending a * to the opening brace.

Example

#![allow(dead_code)]

/// Ignores any tokens inside the block.
macro_rules! comment {
    ( $($_:tt)* ) => {};
}

use guard_macros::guard;

#[cfg_attr(test, test)]
fn main() {
    let event = Event::Message {
        author: "yuki".into(),
        message: "hello world".into(),
    };

    println!("{event:?}");
    handle_message(&event);
    handle_error(&event);
}

#[derive(Debug)]
enum Event {
    Message { author: String, message: String },
    Error { error: String },
}

enum Action {
    SendMessage { message: String },
}

fn handle_message(event: &Event) -> Option<Action> {
    guard!(Event::Message { author, message } = event);
    // expands to:
    comment! {
        let Event::Message { author, message } = event else {
            return ::core::default::Default::default()
        };
    }

    println!("{author}: {message}");

    guard! {
        *author == "yuki",
        message.starts_with("/ping"),
    };
    // expands to:
    comment! {
        if !(*author == "yuki") {
            return ::core::default::Default::default()
        }
        if !(message.starts_with("/ping")) {
            return ::core::default::Default::default()
        }
    }

    Some(Action::SendMessage {
        message: "pong!".into(),
    })
}

fn handle_error(event: &Event) -> Option<Action> {
    guard!(Event::Error { error } = event);
    // expands to:
    comment! {
        let Event::Error { error } = event else {
            return ::core::default::Default::default()
        };
    }

    println!("ERR: {error}");

    None
}

Specification

  • guard!

    Syntax

    GuardBody :
       GuardDecl ( , GuardDecl )* ,?

    GuardDecl :
          *? { GuardBody } RefuteHandlerInheritable
       | GuardClause RefuteHandler?

    GuardClause :
          PatternNoTopAlt = Expression
       | Expression

    RefuteHandler :
       => Expression

    RefuteHandlerInheritable :
          RefuteHandler
       | => _

  • make_guard!

    Syntax

    MakeGuardBody :
       MakeGuardDecl ( , MakeGuardDecl )* ,?

    MakeGuardDecl :
       Identifier RefuteHandler

Macros

  • A macro that replaces let-else and if clause(s).
  • A macro that defines new guard macro(s).