maybe_fut/lib.rs
1#![crate_name = "maybe_fut"]
2#![crate_type = "lib"]
3#![cfg_attr(docsrs, feature(doc_cfg))]
4
5//! # maybe-fut
6//!
7//! **Maybe-fut** is a Rust library that provides a way to export both a **sync** and an **async** API from the same codebase.
8//! It allows you to write your code once and have it work in both synchronous and asynchronous contexts.
9//!
10//! This is achieved through a complex mechanism of **proc macros** and wrappers around `tokio` and `std` libraries.
11//!
12//! Maybe-fut provides its own type library, for `fs`, `io`, `net`, `sync` and `time` modules, which are designed to
13//! use `std` or `tokio` types as needed. Mind that for compatibility reasons, the `io` module has been re-implemented from scratch.
14//!
15//! At runtime it checks whether the thread is running in a **sync** or **async** context and calls the appropriate function.
16//! This allows you to write your code once and have it work in both synchronous and asynchronous contexts.
17//!
18//! In order to check whether the current context is synchronous or asynchronous, you can use the [`is_async_context`] function.
19//!
20//! This is a simple example of how it works:
21//!
22//! 1. Setup your logic to be exported using `maybe-fut` types:
23//!
24//! ```rust
25//! use std::path::{Path, PathBuf};
26//!
27//! use maybe_fut::fs::File;
28//!
29//! struct FsClient {
30//! path: PathBuf,
31//! }
32//!
33//! #[maybe_fut::maybe_fut(
34//! sync = SyncFsClient,
35//! tokio = TokioFsClient,
36//! tokio_feature = "tokio"
37//! )]
38//! impl FsClient {
39//! /// Creates a new `FsClient` instance.
40//! pub fn new(path: impl AsRef<Path>) -> Self {
41//! Self {
42//! path: path.as_ref().to_path_buf(),
43//! }
44//! }
45//!
46//! /// Creates a new file at the specified path.
47//! pub async fn create(&self) -> std::io::Result<()> {
48//! // Create a new file at the specified path.
49//! let file = File::create(&self.path).await?;
50//! file.sync_all().await?;
51//!
52//! Ok(())
53//! }
54//! }
55//! ```
56//!
57//! If you see there is an attribute macro there, called `maybe_fut`. This macro takes 3 arguments:
58//!
59//! - `sync`: The name of the sync struct that will be generated.
60//! - `tokio`: The name of the async struct that will be generated.
61//! - `tokio_feature`: The name of the feature that will be used to enable the async struct.
62//!
63//! 2. Users can now access the public API exported from the library:
64//!
65//! ```rust,ignore
66//! fn sync_main(path: &Path) -> Result<(), Box<dyn std::error::Error>> {
67//! println!("Running in sync mode");
68//!
69//! let client = SyncFsClient::new(path);
70//! client.create()?;
71//!
72//! Ok(())
73//! }
74//!
75//! #[cfg(feature = "tokio")]
76//! async fn tokio_main(path: &Path) -> Result<(), Box<dyn std::error::Error>> {
77//! println!("Running in async mode");
78//!
79//! let client = TokioFsClient::new(path);
80//! client.create().await?;
81//!
82//! Ok(())
83//! }
84//! ```
85//!
86//! A full example can be found in the [examples](./maybe-fut/examples/) folder and can be run using the following command:
87//!
88//! ```bash
89//! cargo run --example fs-client --features tokio-fs -- /tmp/test.txt
90//! ```
91//!
92//! And the `maybe_fut` macro can be applied to traits as well, even combining generics:
93//!
94//! ```rust,ignore
95//! use std::fmt::Display;
96//!
97//! #[derive(Debug, Clone, Copy)]
98//! struct TestStruct<T: Sized + Copy + Display> {
99//! value: T,
100//! }
101//!
102//! #[maybe_fut::maybe_fut(
103//! sync = SyncTestStruct,
104//! tokio = TokioTestStruct,
105//! tokio_feature = "tokio",
106//! )]
107//! impl<T> TestStruct<T>
108//! where
109//! T: Sized + Copy + Display,
110//! {
111//! /// Creates a new [`TestStruct`] instance.
112//! pub fn new(value: T) -> Self {
113//! Self { value }
114//! }
115//!
116//! /// Get underlying value.
117//! pub fn value(&self) -> T {
118//! self.value
119//! }
120//! }
121//!
122//! /// A trait to greet the user.
123//! pub trait Greet {
124//! /// Greets the user with a message.
125//! fn greet(&self) -> String;
126//!
127//! // Greets the user with a message asynchronously.
128//! fn greet_async(&self) -> impl Future<Output = String>;
129//! }
130//!
131//! #[maybe_fut::maybe_fut(
132//! sync = SyncTestStruct,
133//! tokio = TokioTestStruct,
134//! tokio_feature = "tokio",
135//! )]
136//! impl<T> Greet for TestStruct<T>
137//! where
138//! T: Sized + Copy + Display,
139//! {
140//! fn greet(&self) -> String {
141//! format!("Hello, I'm {}", self.value)
142//! }
143//!
144//! async fn greet_async(&self) -> String {
145//! format!("Hello, I'm {}", self.value)
146//! }
147//! }
148//!
149//! #[cfg(feature = "tokio")]
150//! {
151//! let test_struct = TokioTestStruct::new(42);
152//! test_struct.greet();
153//! test_struct.greet_async().await;
154//! }
155//! ```
156//!
157
158#![doc(html_playground_url = "https://play.rust-lang.org")]
159#![doc(
160 html_favicon_url = "https://raw.githubusercontent.com/veeso/maybe-fut/main/assets/images/logo-128.png"
161)]
162#![doc(
163 html_logo_url = "https://raw.githubusercontent.com/veeso/maybe-fut/main/assets/images/logo-500.png"
164)]
165
166#[macro_use]
167extern crate maybe_fut_io_derive;
168#[macro_use]
169extern crate maybe_fut_unwrap_derive;
170
171// private api
172mod api;
173mod context;
174mod macros;
175mod rt;
176mod unwrap;
177
178// public api (api is exported at top-level)
179// export maybe fut derive macro
180pub use maybe_fut_derive::maybe_fut;
181
182pub use self::api::*;
183pub use self::context::is_async_context;
184pub use self::rt::{SyncRuntime, block_on};
185pub use self::unwrap::Unwrap;