Skip to main content

struct_threads/
lib.rs

1//! A simple library providing a clean, object-oriented way to structure thread logic.
2//!
3//! `struct-threads` allows you to define your task state in a `struct`, implement the
4//! [`Runnable`] trait, and seamlessly spawn it using the [`Thread`] extension trait.
5//!
6//! # Features
7//!
8//! - **Default**: Provides [`Runnable`], [`Thread`], and [`ParallelRun`] traits for standard thread-based execution
9//! - **`tokio`**: Adds [`AsyncRunnable`], [`TokioTask`], and [`TokioParallelRun`] traits for async task execution with Tokio runtime
10//!
11//! # Basic Example
12//!
13//! ```rust
14//! use struct_threads::{Runnable, Thread};
15//!
16//! struct MyTask(i32);
17//!
18//! impl Runnable for MyTask {
19//!     type Output = i32;
20//!
21//!     fn run(self) -> Self::Output {
22//!         self.0 * 2
23//!     }
24//! }
25//!
26//! let handle = MyTask(21).start();
27//! assert_eq!(handle.join().unwrap(), 42);
28//! ```
29//!
30//! # Parallel Execution
31//!
32//! With `struct-threads`, you can also run multiple tasks in parallel using the [`ParallelRun`] extension trait.
33//!
34//! ```rust
35//! use struct_threads::{Runnable, ParallelRun};
36//!
37//! struct MyTask(i32);
38//!
39//! impl Runnable for MyTask {
40//!     type Output = i32;
41//!
42//!    fn run(self) -> Self::Output {
43//!        self.0 * 2
44//!    }
45//! }
46//!
47//! let results = (0..10)
48//!     .map(MyTask)
49//!     .collect::<Vec<_>>()
50//!     .par_run()
51//!     .unwrap();
52//!
53//!  assert_eq!(results, (0..10).map(|x| x * 2).collect::<Vec<_>>());
54//! ```
55//!
56//! # Custom Thread Configuration
57//!
58//! You can customize thread properties using the [`Thread::start_with_builder`] method:
59//!
60//! ```rust
61//! use std::thread::Builder;
62//! use struct_threads::{Runnable, Thread};
63//!
64//! struct MyTask(i32);
65//!
66//! impl Runnable for MyTask {
67//!     type Output = i32;
68//!     fn run(self) -> Self::Output { self.0 * 2 }
69//! }
70//!
71//! let builder = Builder::new()
72//!     .name("custom-thread".to_string())
73//!     .stack_size(4 * 1024 * 1024);
74//!
75//! let handle = MyTask(21).start_with_builder(builder);
76//! assert_eq!(handle.join().unwrap(), 42);
77//! ```
78//!
79//! # Async Support (requires `tokio` feature)
80//!
81//! Enable async support by adding the `tokio` feature:
82//!
83//! ```toml
84//! [dependencies]
85//! struct-threads = { version = "1.0", features = ["tokio"] }
86//! ```
87//!
88//! Then use [`AsyncRunnable`] and [`TokioTask`]:
89//!
90//! ```rust,ignore
91//! use struct_threads::{AsyncRunnable, TokioTask};
92//!
93//! struct AsyncTask(i32);
94//!
95//! impl AsyncRunnable for AsyncTask {
96//!     type Output = i32;
97//!
98//!     fn run(self) -> impl std::future::Future<Output = Self::Output> + Send {
99//!         async move {
100//!             tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
101//!             self.0 * 2
102//!         }
103//!     }
104//! }
105//!
106//! #[tokio::main]
107//! async fn main() {
108//!     let handle = AsyncTask(21).async_start();
109//!     assert_eq!(handle.await.unwrap(), 42);
110//! }
111//! ```
112
113pub mod traits;
114
115pub use traits::{AsyncRunnable, ParallelRun, Runnable, Thread};
116
117#[cfg(feature = "tokio")]
118pub use traits::{TokioParallelRun, TokioTask};
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123
124    struct TestTask(i32);
125    impl Runnable for TestTask {
126        type Output = i32;
127
128        fn run(self) -> Self::Output {
129            self.0 * 2
130        }
131    }
132
133    #[test]
134    fn it_works() {
135        let task = TestTask(10);
136        let handle = task.start();
137        assert_eq!(handle.join().unwrap(), 20);
138    }
139
140    #[test]
141    fn par_test() {
142        let tasks = (0..1_000_000).map(TestTask).collect::<Vec<_>>();
143
144        let results = tasks.par_run().unwrap();
145        assert_eq!(results, (0..1_000_000).map(|x| x * 2).collect::<Vec<_>>());
146    }
147
148    #[test]
149    fn par_test_empty() {
150        let results = Vec::<TestTask>::new().par_run().unwrap();
151        assert!(results.is_empty());
152    }
153
154    #[test]
155    fn test_builder() {
156        let task = TestTask(10);
157        let builder = std::thread::Builder::new().name("custom_thread".to_string());
158        let handle = task.start_with_builder(builder);
159        assert_eq!(handle.join().unwrap(), 20);
160
161        struct BuilderTask;
162
163        impl Runnable for BuilderTask {
164            type Output = String;
165
166            fn run(self) -> Self::Output {
167                std::thread::current().name().unwrap().to_string()
168            }
169        }
170
171        let builder = std::thread::Builder::new().name("custom_thread".to_string());
172        let handle = BuilderTask.start_with_builder(builder);
173        assert_eq!(handle.join().unwrap(), "custom_thread".to_string());
174    }
175
176    #[cfg(feature = "tokio")]
177    mod async_tests {
178        use super::*;
179
180        struct AsyncTestTask(i32);
181
182        impl AsyncRunnable for AsyncTestTask {
183            type Output = i32;
184
185            fn run(self) -> impl std::future::Future<Output = Self::Output> + Send {
186                async move {
187                    tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
188                    self.0 * 2
189                }
190            }
191        }
192
193        #[tokio::test]
194        async fn test_async_task() {
195            let task = AsyncTestTask(21);
196            let handle = task.async_start();
197            assert_eq!(handle.await.unwrap(), 42);
198        }
199
200        #[tokio::test]
201        async fn test_async_parallel_run() {
202            let tasks = (0..10).map(AsyncTestTask).collect::<Vec<_>>();
203            let results = tasks.async_par_run().await.unwrap();
204            assert_eq!(results, (0..10).map(|x| x * 2).collect::<Vec<_>>());
205        }
206
207        #[tokio::test]
208        async fn test_async_parallel_run_empty() {
209            let tasks = Vec::<AsyncTestTask>::new();
210            let results = tasks.async_par_run().await.unwrap();
211            assert!(results.is_empty());
212        }
213
214        #[tokio::test]
215        async fn test_async_parallel_run_large() {
216            let tasks = (0..1_000).map(AsyncTestTask).collect::<Vec<_>>();
217            let results = tasks.async_par_run().await.unwrap();
218            assert_eq!(results, (0..1_000).map(|x| x * 2).collect::<Vec<_>>());
219        }
220
221        struct AsyncComplexTask {
222            value: i32,
223            multiplier: i32,
224        }
225
226        impl AsyncRunnable for AsyncComplexTask {
227            type Output = i32;
228
229            fn run(self) -> impl std::future::Future<Output = Self::Output> + Send {
230                async move {
231                    tokio::time::sleep(tokio::time::Duration::from_millis(5)).await;
232                    self.value * self.multiplier
233                }
234            }
235        }
236
237        #[tokio::test]
238        async fn test_async_complex_task() {
239            let task = AsyncComplexTask {
240                value: 7,
241                multiplier: 6,
242            };
243            let handle = task.async_start();
244            assert_eq!(handle.await.unwrap(), 42);
245        }
246
247        #[tokio::test]
248        async fn test_async_multiple_awaits() {
249            let task1 = AsyncTestTask(10);
250            let task2 = AsyncTestTask(20);
251            let task3 = AsyncTestTask(30);
252
253            let handle1 = task1.async_start();
254            let handle2 = task2.async_start();
255            let handle3 = task3.async_start();
256
257            let result1 = handle1.await.unwrap();
258            let result2 = handle2.await.unwrap();
259            let result3 = handle3.await.unwrap();
260
261            assert_eq!(result1, 20);
262            assert_eq!(result2, 40);
263            assert_eq!(result3, 60);
264        }
265
266        struct AsyncStringTask(String);
267
268        impl AsyncRunnable for AsyncStringTask {
269            type Output = String;
270
271            fn run(self) -> impl std::future::Future<Output = Self::Output> + Send {
272                async move {
273                    tokio::time::sleep(tokio::time::Duration::from_millis(5)).await;
274                    format!("Hello, {}!", self.0)
275                }
276            }
277        }
278
279        #[tokio::test]
280        async fn test_async_string_output() {
281            let task = AsyncStringTask("World".to_string());
282            let handle = task.async_start();
283            assert_eq!(handle.await.unwrap(), "Hello, World!");
284        }
285
286        #[tokio::test]
287        async fn test_async_parallel_run_strings() {
288            let tasks = vec![
289                AsyncStringTask("Alice".to_string()),
290                AsyncStringTask("Bob".to_string()),
291                AsyncStringTask("Charlie".to_string()),
292            ];
293            let results = tasks.async_par_run().await.unwrap();
294            assert_eq!(
295                results,
296                vec![
297                    "Hello, Alice!".to_string(),
298                    "Hello, Bob!".to_string(),
299                    "Hello, Charlie!".to_string()
300                ]
301            );
302        }
303    }
304}