Expand description
§maybe-fut
Maybe-fut is a Rust library that provides a way to export both a sync and an async API from the same codebase. It allows you to write your code once and have it work in both synchronous and asynchronous contexts.
This is achieved through a complex mechanism of proc macros and wrappers around tokio and std libraries.
Maybe-fut provides its own type library, for fs, io, net, sync and time modules, which are designed to
use std or tokio types as needed. Mind that for compatibility reasons, the io module has been re-implemented from scratch.
At runtime it checks whether the thread is running in a sync or async context and calls the appropriate function. This allows you to write your code once and have it work in both synchronous and asynchronous contexts.
In order to check whether the current context is synchronous or asynchronous, you can use the is_async_context function.
This is a simple example of how it works:
-
Setup your logic to be exported using
maybe-futtypes:use std::path::{Path, PathBuf}; use maybe_fut::fs::File; struct FsClient { path: PathBuf, } #[maybe_fut::maybe_fut( sync = SyncFsClient, tokio = TokioFsClient, tokio_feature = "tokio" )] impl FsClient { /// Creates a new `FsClient` instance. pub fn new(path: impl AsRef<Path>) -> Self { Self { path: path.as_ref().to_path_buf(), } } /// Creates a new file at the specified path. pub async fn create(&self) -> std::io::Result<()> { // Create a new file at the specified path. let file = File::create(&self.path).await?; file.sync_all().await?; Ok(()) } }If you see there is an attribute macro there, called
maybe_fut. This macro takes 3 arguments:sync: The name of the sync struct that will be generated.tokio: The name of the async struct that will be generated.tokio_feature: The name of the feature that will be used to enable the async struct.
-
Users can now access the public API exported from the library:
ⓘfn sync_main(path: &Path) -> Result<(), Box<dyn std::error::Error>> { println!("Running in sync mode"); let client = SyncFsClient::new(path); client.create()?; Ok(()) } #[cfg(feature = "tokio")] async fn tokio_main(path: &Path) -> Result<(), Box<dyn std::error::Error>> { println!("Running in async mode"); let client = TokioFsClient::new(path); client.create().await?; Ok(()) }
A full example can be found in the examples folder and can be run using the following command:
cargo run --example fs-client --features tokio-fs -- /tmp/test.txtAnd the maybe_fut macro can be applied to traits as well, even combining generics:
use std::fmt::Display;
#[derive(Debug, Clone, Copy)]
struct TestStruct<T: Sized + Copy + Display> {
value: T,
}
#[maybe_fut::maybe_fut(
sync = SyncTestStruct,
tokio = TokioTestStruct,
tokio_feature = "tokio",
)]
impl<T> TestStruct<T>
where
T: Sized + Copy + Display,
{
/// Creates a new [`TestStruct`] instance.
pub fn new(value: T) -> Self {
Self { value }
}
/// Get underlying value.
pub fn value(&self) -> T {
self.value
}
}
/// A trait to greet the user.
pub trait Greet {
/// Greets the user with a message.
fn greet(&self) -> String;
// Greets the user with a message asynchronously.
fn greet_async(&self) -> impl Future<Output = String>;
}
#[maybe_fut::maybe_fut(
sync = SyncTestStruct,
tokio = TokioTestStruct,
tokio_feature = "tokio",
)]
impl<T> Greet for TestStruct<T>
where
T: Sized + Copy + Display,
{
fn greet(&self) -> String {
format!("Hello, I'm {}", self.value)
}
async fn greet_async(&self) -> String {
format!("Hello, I'm {}", self.value)
}
}
#[cfg(feature = "tokio")]
{
let test_struct = TokioTestStruct::new(42);
test_struct.greet();
test_struct.greet_async().await;
}Modules§
- fs
- File system utilities
- io
- Traits, helpers, and type definitions for core I/O functionality.
- net
- Networking primitives for TCP/UDP communication.
- sync
- Useful synchronization primitives
- time
- Utilities for tracking time
Macros§
- maybe_
fut_ constructor - A macro to create a constructor function that can be used in both async and sync contexts.
- maybe_
fut_ constructor_ result - A macro to create a constructor function that can be used in both async and sync contexts.
- maybe_
fut_ constructor_ sync - A macro to create a constructor function that can be used in both async and sync contexts.
- maybe_
fut_ function - A macro to create a function that can be used in both async and sync contexts.
- maybe_
fut_ method - A macro to create a method that can be used in both async and sync contexts.
- maybe_
fut_ method_ mut - A macro to create a mutable method that can be used in both async and sync contexts.
- maybe_
fut_ method_ sync - A macro to create a method that can be used in both async and sync contexts.
Structs§
- Sync
Runtime - A runtime to execute sync code without async context.
Traits§
- Unwrap
- Unwrap trait for MaybeFut types.
Functions§
- block_
on - Blocks on a future in a sync context.
- is_
async_ context - Returns whether the current code is being executed in an async context.