dimas_time/
timer_builder.rs

1// Copyright © 2023 Stephan Kunz
2
3//! Module `timer` provides a set of `Timer` variants which can be created using the `TimerBuilder`.
4//! When fired, a `Timer` calls his assigned `TimerCallback`.
5
6#[doc(hidden)]
7extern crate alloc;
8
9#[cfg(feature = "std")]
10extern crate std;
11
12// region:		--- modules
13use crate::error::Error;
14use dimas_core::builder_states::{
15	Callback, Interval, NoCallback, NoInterval, NoSelector, NoStorage, Selector, Storage,
16};
17
18use super::{ArcTimerCallback, Timer};
19
20use alloc::{
21	format,
22	string::{String, ToString},
23	sync::Arc,
24};
25use core::time::Duration;
26use dimas_core::{Result, enums::OperationState, traits::Context};
27use std::{
28	collections::HashMap,
29	sync::{Mutex, RwLock},
30};
31// endregion:	--- modules
32
33// region:		--- TimerBuilder
34/// A builder for a timer
35#[derive(Clone)]
36pub struct TimerBuilder<P, K, I, C, S>
37where
38	P: Send + Sync + 'static,
39{
40	context: Context<P>,
41	activation_state: OperationState,
42	selector: K,
43	interval: I,
44	callback: C,
45	storage: S,
46	delay: Option<Duration>,
47}
48
49impl<P> TimerBuilder<P, NoSelector, NoInterval, NoCallback, NoStorage>
50where
51	P: Send + Sync + 'static,
52{
53	/// Construct a `TimerBuilder` in initial state
54	#[must_use]
55	pub const fn new(context: Context<P>) -> Self {
56		Self {
57			context,
58			activation_state: OperationState::Active,
59			selector: NoSelector,
60			interval: NoInterval,
61			callback: NoCallback,
62			storage: NoStorage,
63			delay: None,
64		}
65	}
66}
67
68impl<P, K, I, C, S> TimerBuilder<P, K, I, C, S>
69where
70	P: Send + Sync + 'static,
71{
72	/// Set the activation state.
73	#[must_use]
74	pub const fn activation_state(mut self, state: OperationState) -> Self {
75		self.activation_state = state;
76		self
77	}
78
79	/// Set the consolidation mode
80	#[must_use]
81	pub const fn delay(mut self, delay: Duration) -> Self {
82		self.delay.replace(delay);
83		self
84	}
85}
86
87impl<P, I, C, S> TimerBuilder<P, NoSelector, I, C, S>
88where
89	P: Send + Sync + 'static,
90{
91	/// Set the key expression for the timer
92	#[must_use]
93	pub fn selector(self, selector: &str) -> TimerBuilder<P, Selector, I, C, S> {
94		let Self {
95			context,
96			activation_state,
97			interval,
98			callback,
99			storage,
100			delay,
101			..
102		} = self;
103		TimerBuilder {
104			context,
105			activation_state,
106			selector: Selector {
107				selector: selector.into(),
108			},
109			interval,
110			callback,
111			storage,
112			delay,
113		}
114	}
115
116	/// Set only the name of the timer.
117	/// Will be prefixed with agents prefix.
118	#[must_use]
119	pub fn name(self, topic: &str) -> TimerBuilder<P, Selector, I, C, S> {
120		let selector = self
121			.context
122			.prefix()
123			.map_or_else(|| topic.to_string(), |prefix| format!("{prefix}/{topic}"));
124		let Self {
125			context,
126			activation_state,
127			interval,
128			callback,
129			storage,
130			delay,
131			..
132		} = self;
133		TimerBuilder {
134			context,
135			activation_state,
136			selector: Selector { selector },
137			interval,
138			callback,
139			storage,
140			delay,
141		}
142	}
143}
144
145impl<P, K, C, S> TimerBuilder<P, K, NoInterval, C, S>
146where
147	P: Send + Sync + 'static,
148{
149	/// set timers interval
150	#[must_use]
151	pub fn interval(self, interval: Duration) -> TimerBuilder<P, K, Interval, C, S> {
152		let Self {
153			context,
154			activation_state,
155			selector: name,
156			callback,
157			storage,
158			delay,
159			..
160		} = self;
161		TimerBuilder {
162			context,
163			activation_state,
164			selector: name,
165			interval: Interval { interval },
166			callback,
167			storage,
168			delay,
169		}
170	}
171}
172
173impl<P, K, I, S> TimerBuilder<P, K, I, NoCallback, S>
174where
175	P: Send + Sync + 'static,
176{
177	/// Set interval callback for timer
178	#[must_use]
179	pub fn callback<F>(self, callback: F) -> TimerBuilder<P, K, I, Callback<ArcTimerCallback<P>>, S>
180	where
181		F: FnMut(Context<P>) -> Result<()> + Send + Sync + 'static,
182	{
183		let Self {
184			context,
185			activation_state,
186			selector: name,
187			interval,
188			storage,
189			delay,
190			..
191		} = self;
192		let callback: ArcTimerCallback<P> = Arc::new(Mutex::new(callback));
193		TimerBuilder {
194			context,
195			activation_state,
196			selector: name,
197			interval,
198			callback: Callback { callback },
199			storage,
200			delay,
201		}
202	}
203}
204
205impl<P, K, I, C> TimerBuilder<P, K, I, C, NoStorage>
206where
207	P: Send + Sync + 'static,
208{
209	/// Provide agents storage for the timer
210	#[must_use]
211	pub fn storage(
212		self,
213		storage: Arc<RwLock<HashMap<String, Timer<P>>>>,
214	) -> TimerBuilder<P, K, I, C, Storage<Timer<P>>> {
215		let Self {
216			context,
217			activation_state,
218			selector: name,
219			interval,
220			callback,
221			delay,
222			..
223		} = self;
224		TimerBuilder {
225			context,
226			activation_state,
227			selector: name,
228			interval,
229			callback,
230			storage: Storage { storage },
231			delay,
232		}
233	}
234}
235
236impl<P, S> TimerBuilder<P, Selector, Interval, Callback<ArcTimerCallback<P>>, S>
237where
238	P: Send + Sync + 'static,
239{
240	/// Build the [Timer]
241	/// # Errors
242	///
243	pub fn build(self) -> Result<Timer<P>> {
244		let Self {
245			context,
246			activation_state,
247			selector: name,
248			interval,
249			callback,
250			delay,
251			..
252		} = self;
253
254		Ok(Timer::new(
255			name.selector,
256			context,
257			activation_state,
258			callback.callback,
259			interval.interval,
260			delay,
261		))
262	}
263}
264
265impl<P> TimerBuilder<P, Selector, Interval, Callback<ArcTimerCallback<P>>, Storage<Timer<P>>>
266where
267	P: Send + Sync + 'static,
268{
269	/// Build and add the timer to the agents context
270	/// # Errors
271	///
272	pub fn add(self) -> Result<Option<Timer<P>>> {
273		let name = self.selector.selector.clone();
274		let collection = self.storage.storage.clone();
275		let t = self.build()?;
276
277		let r = collection
278			.write()
279			.map_err(|_| Error::MutexPoison(String::from("TimerBuilder")))?
280			.insert(name, t);
281		Ok(r)
282	}
283}
284// endregion:	--- TimerBuilder
285
286#[cfg(test)]
287mod tests {
288	use super::*;
289
290	#[derive(Debug)]
291	struct Props {}
292
293	// check, that the auto traits are available
294	const fn is_normal<T: Sized + Send + Sync>() {}
295
296	#[test]
297	const fn normal_types() {
298		is_normal::<TimerBuilder<Props, NoSelector, NoInterval, NoCallback, NoStorage>>();
299	}
300}