#synchronous #async #wrap #automatic #sync #function #wrapper

macro ut

UT automatically wraps async functions in a synchronous wrapper based on feature flags

Show the crate…

1 unstable release

0.3.0 Mar 16, 2021

#60 in #synchronous

MIT license

13KB
67 lines

Ut

Wraps asynchronous functions in order to make them synchronous based on if a "sync" feature is enabled. This is useful when writting http clients as you can write async methods and wrap them for use in synchronous code bases automatically.

Usage

[dependencies]
ut = "0.2.0"

Then in the crate that you want to have synchronous functions created for you you create a sync feature. When this feature is enabled ut will create synchronous functions on what you have wrapped.

you can either:

  • Replace your asynchronous function with a synchronous one
  • Clone your asynchronous function with a synchronous one ending in blocking
  • Clone all methods in an impl with synchronous ones

Replacing async functions

You can replace your asynchronous function with a synchronous one by doing something like the following:

#[ut::wrap]
async fn foo(input: &str) -> String {
  format!("I am {} now", input)
}

fn main() {
  let out = foo("sync");
  assert_eq!(out, "I am sync now".to_owned())
}

Cloning async functions

You can clone your asynchronous function with a synchronous one ending in blocking by doing something like the following:

#[ut::clone]
async fn foo(input: &str) -> String {
 format!("I am {} now", input)
}

let out = foo_blocking("sync");
assert_eq!(out, "I am sync now".to_owned())

Cloning async methods in implementations

You can clone all methods in an impl with synchronous ones by using ut::clone_impl. This is useful when you want to support both async and sync functions in a struct implementation.

// The original struct
#[derive(Default)]
pub struct Example {
  pub fooers: Fooers,
}

// You also need to create the struct to place the cloned impls in
// This is done so you can choose what structs/impls to clone/wrap
// The cloned structs/impls should end in Blocking
#[derive(Default)]
pub struct ExampleBlocking {
  pub fooers: FooersBlocking,
}

// The original struct with async functions
#[derive(Default)]
pub struct Fooers;

// The blocking struct that we are cloning impls into
// You have to create this so you can add custom derives
#[derive(Default)]
pub struct FooersBlocking;

// The async impls that you want to wrap
// All methods within this impl must be async
#[ut::clone_impl]
impl Fooers {
  pub async fn foo(&self, input: &str) -> String {
    format!("I am {} now", input)
  }

  pub async fn bar(&self, input: &str) -> String {
    format!("I am also {} now", input)
  }
}
let example = ExampleBlocking::default();
let out = example.fooers.foo("sync");
assert_eq!(out, "I am sync now".to_owned());
let out = example.fooers.bar("sync");
assert_eq!(out, "I am also sync now".to_owned())

Currently the wrapping is very naive and simply wraps the function in tokio::main. This is likely more expensive then it needs to be and I hope to make it more efficient later.

Dependencies

~5.5MB
~95K SLoC