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;