macro_rules! fb {
    (async, $fn_name:ident, ($($param_name:ident : $param_type:ty),*), -> $return_type:ty, $body:block) => { ... };
    (sync, $fn_name:ident, ($($param_name:ident : $param_type:ty),*), -> $return_type:ty, $body:block) => { ... };
}
Expand description

The fb macro (Flexi Block) or (Function Builder) simplifies the generation of conditional synchronous or asynchronous functions within Rust code.

By specifying a mode, function name, parameters, return type, and body, this macro can dynamically create the desired function type based on the provided mode. This approach is particularly useful in contexts where both synchronous and asynchronous versions of a function might be needed, allowing for cleaner code organization and reuse.

§Syntax

fb!(mode, function_name, (parameter1: Type1, parameter2: Type2, ...), -> ReturnType, {
    // Function body
});

§Parameters

  • mode: A compile-time string literal that determines whether the generated function is synchronous ("sync") or asynchronous ("async").
  • function_name: The identifier for the generated function.
  • parameters: A comma-separated list of function parameters in the form parameter_name: Type.
  • ReturnType: The return type of the function.
  • body: The block of code that defines the function body.

§Usage

Generating a synchronous function:

fb!("sync", greet, (name: String), -> String, {
    format!("Hello, {}", name)
});

Generating an asynchronous function:

fb!("async", fetch_data, (url: String), -> Result<String, reqwest::Error>, {
    reqwest::get(&url).await?.text().await
});

§Tricks and Advanced Usage

§Conditional Compilation

The fb macro can be combined with Rust’s conditional compilation features to selectively compile either the synchronous or asynchronous version of a function based on feature flags or target environment.

Example with feature flags:

#[cfg(feature = "async")]
fb!("async", process_data, (data: Vec<u8>), -> Result<(), MyError>, {
    // Asynchronous processing
});

#[cfg(not(feature = "async"))]
fb!("sync", process_data, (data: Vec<u8>), -> Result<(), MyError>, {
    // Synchronous processing
});

§Leveraging Macros for DRY Principles

You can define a wrapper macro around fb to reduce repetition when declaring similar functions in different modes. This is especially handy when you have a set of functions that need to be available in both synchronous and asynchronous forms.

Example:

macro_rules! define_greeting_fn {
    ($mode:tt) => {
        fb!($mode, greet, (name: String), -> String, {
            format!("Hello, {}", name)
        });
    };
}

// Now, you can easily generate both versions with minimal repetition:
define_greeting_fn!("sync");
define_greeting_fn!("async");

By leveraging the fb macro in your Rust projects, you can maintain cleaner and more maintainable codebases, especially when dealing with the complexities of synchronous and asynchronous programming patterns.