tang_rs/
lib.rs

1//! # An asynchronous connection pool.
2//! some code come from
3//! [bb8](https://docs.rs/bb8/0.3.1/bb8/)
4//! [L3-37](https://github.com/OneSignal/L3-37/)
5//!
6//! ## feature
7//!
8//! * `default` - multi thread pool where `Send` bound is needed for all futures.
9//! * `no-send` - single thread pool where `!Send` futures are accepted.
10//!
11//! # Known Limitation:
12//! can't be used in nested runtimes.
13//!
14//! # Example:
15//! ```
16//! // This example shows how to implement the pool on async_std runtime.
17//! // Most of the xxx-tang crates are implemented with tokio runtime so they can be seen as examples on that matter.
18//!
19//! use std::fmt::{Debug, Formatter, Result as FmtResult};
20//! use std::future::Future;
21//! use std::sync::atomic::{AtomicUsize, Ordering};
22//! use std::time::{Duration, Instant};
23//!
24//! use async_std::task;
25//! use smol::Timer;
26//! use tang_rs::{Builder, Manager, ManagerFuture, ManagerTimeout};
27//!
28//! // our test pool would just generate usize from 0 as connections.
29//! struct TestPoolManager(AtomicUsize);
30//!
31//! impl TestPoolManager {
32//!     fn new() -> Self {
33//!         TestPoolManager(AtomicUsize::new(0))
34//!     }
35//! }
36//!
37//! // dummy error type
38//! struct TestPoolError;
39//!
40//! impl Debug for TestPoolError {
41//!     fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
42//!         f.debug_struct("TestPoolError")
43//!             .field("source", &"Unknown")
44//!             .finish()
45//!     }
46//! }
47//!
48//! // convert instant as timeout error to our pool error.
49//! impl From<Instant> for TestPoolError {
50//!     fn from(_: Instant) -> Self {
51//!         TestPoolError
52//!     }
53//! }
54//!
55//! impl Manager for TestPoolManager {
56//!     type Connection = usize;
57//!     type Error = TestPoolError;
58//!     type Timeout = Timer;
59//!     type TimeoutError = Instant;
60//!
61//!     fn connect(&self) -> ManagerFuture<'_, Result<Self::Connection, Self::Error>> {
62//!         // how we generate new connections and put them into pool.
63//!         Box::pin(async move { Ok(self.0.fetch_add(1, Ordering::SeqCst)) })
64//!     }
65//!
66//!     fn is_valid<'a>(
67//!         &self,
68//!         _conn: &'a mut Self::Connection,
69//!     ) -> ManagerFuture<'a, Result<(), Self::Error>> {
70//!         Box::pin(async {
71//!             // when the connection is pulled from the pool we can check if it's valid.
72//!             Ok(())
73//!         })
74//!     }
75//!
76//!     fn is_closed(&self, _conn: &mut Self::Connection) -> bool {
77//!         // return true if you check the connection and want it to be dropped from the pool because it's closed.
78//!         false
79//!     }
80//!
81//!     fn spawn<Fut>(&self, fut: Fut)
82//!         where
83//!             Fut: Future<Output = ()> + Send + 'static,
84//!     {
85//!         // some pool inner functions would want to spawn on your executor.
86//!         // you can use the handler to further manage them if you want.
87//!         // normally we just spawn the task and forget about it.
88//!         let _handler = task::spawn(fut);
89//!     }
90//!
91//!     // Boilerplate implement for runtime specific timeout future.
92//!     fn timeout<Fut: Future>(&self,fut: Fut, dur: Duration) -> ManagerTimeout<Fut, Self::Timeout> {
93//!         ManagerTimeout::new(fut, Timer::after(dur))
94//!     }
95//! }
96//!
97//! #[async_std::main]
98//! async fn main() {
99//!     let mgr = TestPoolManager::new();
100//!
101//!     let builder = Builder::new()
102//!         .always_check(false)
103//!         .idle_timeout(None)
104//!         .max_lifetime(None)
105//!         .min_idle(24)
106//!         .max_size(24)
107//!         .build(mgr);
108//!
109//!     let pool = builder.await.expect("fail to build pool");
110//!
111//!     // spawn 24 futures and pull connections from pool at the same time.
112//!     let (tx, rx) = async_std::sync::channel(100);
113//!     for _i in 0..24 {
114//!         let pool = pool.clone();
115//!         let tx = tx.clone();
116//!         task::spawn(async move {
117//!             let mut pool_ref = pool.get().await.expect("fail to get PoolRef");
118//!             let conn_ref = &*pool_ref;
119//!             println!("we have the reference of a connection : {:?}", conn_ref);
120//!
121//!             // we can also get a mut reference from pool_ref
122//!             let conn_ref = &mut *pool_ref;
123//!
124//!             let _ = tx.send(*conn_ref);
125//!         });
126//!     }
127//!     drop(tx);
128//!
129//!     while let Ok(_connection) = rx.recv().await {
130//!         // We just wait until all connections are pulled out once
131//!     }
132//! }
133//!```
134
135pub use builder::Builder;
136pub use manager::{GarbageCollect, Manager, ManagerFuture, ManagerInterval, ScheduleReaping};
137pub use pool::{Pool, PoolRef, PoolRefOwned, SharedManagedPool};
138pub use util::timeout::ManagerTimeout;
139
140mod builder;
141mod manager;
142mod pool;
143mod pool_inner;
144mod util;
145
146#[cfg(all(feature = "default", feature = "no-send"))]
147compile_error!("only one of 'default' or 'no-send' features can be enabled");