fieldx/async/
fxproxy.rs

1use crate::traits::FXBuilderWrapper;
2use crate::traits::FXStruct;
3use async_trait::async_trait;
4use std::any;
5use std::cell::RefCell;
6use std::fmt;
7use std::fmt::Debug;
8use std::fmt::Formatter;
9use std::future::Future;
10use std::pin::Pin;
11use std::sync::atomic::AtomicBool;
12use std::sync::atomic::Ordering;
13
14use super::RwLock;
15use super::RwLockReadGuard;
16use super::RwLockWriteGuard;
17
18pub type FXProxyReadGuard<'a, T> = crate::lock_guards::FXProxyReadGuard<RwLockReadGuard<'a, Option<T>>, T>;
19pub type FXProxyWriteGuard<'a, T> = crate::lock_guards::FXProxyWriteGuard<RwLockWriteGuard<'a, Option<T>>, T>;
20
21type FXCallback<S, T> = Box<dyn Fn(&S) -> Pin<Box<dyn Future<Output = T> + Send + '_>> + Send + Sync>;
22
23#[cfg(feature = "async-tokio")]
24type ReadOrInitGuard<'a, T> = tokio::sync::RwLockWriteGuard<'a, T>;
25#[cfg(all(feature = "async-lock", not(docsrs)))]
26type ReadOrInitGuard<'a, T> = async_lock::RwLockUpgradableReadGuard<'a, T>;
27
28#[doc(hidden)]
29#[async_trait]
30pub trait FXBuilderWrapperAsync: FXBuilderWrapper {
31    async fn invoke(&self, owner: &Self::Owner) -> Result<Self::Value, Self::Error>;
32}
33
34#[doc(hidden)]
35pub struct FXBuilderInfallible<S, T> {
36    builder: FXCallback<S, T>,
37}
38
39impl<S, T> FXBuilderInfallible<S, T> {
40    pub fn new(builder: FXCallback<S, T>) -> Self {
41        Self { builder }
42    }
43}
44
45impl<S: FXStruct, T> FXBuilderWrapper for FXBuilderInfallible<S, T> {
46    type Error = ();
47    type Owner = S;
48    type Value = T;
49}
50
51#[async_trait]
52impl<S, T> FXBuilderWrapperAsync for FXBuilderInfallible<S, T>
53where
54    S: Sync + FXStruct,
55{
56    #[inline(always)]
57    async fn invoke(&self, owner: &Self::Owner) -> Result<Self::Value, Self::Error> {
58        Ok((self.builder)(owner).await)
59    }
60}
61
62#[doc(hidden)]
63pub struct FXBuilderFallible<S, T, E> {
64    builder: FXCallback<S, Result<T, E>>,
65}
66
67impl<S, T, E> FXBuilderFallible<S, T, E> {
68    pub fn new(builder: FXCallback<S, Result<T, E>>) -> Self {
69        Self { builder }
70    }
71}
72
73impl<S, T, E: Debug> FXBuilderWrapper for FXBuilderFallible<S, T, E>
74where
75    S: FXStruct,
76{
77    type Error = E;
78    type Owner = S;
79    type Value = T;
80}
81
82#[async_trait]
83impl<S, T, E: Debug> FXBuilderWrapperAsync for FXBuilderFallible<S, T, E>
84where
85    S: FXStruct + Sync,
86{
87    #[inline(always)]
88    async fn invoke(&self, owner: &Self::Owner) -> Result<Self::Value, Self::Error> {
89        (self.builder)(owner).await
90    }
91}
92
93/// Container type for lazy fields
94pub struct FXProxy<B>
95where
96    B: FXBuilderWrapperAsync,
97{
98    value:   RwLock<Option<B::Value>>,
99    is_set:  AtomicBool,
100    builder: RwLock<Option<B>>,
101}
102
103/// Write-lock returned by [`FXProxy::write`] method
104///
105/// This type, in cooperation with the [`FXProxy`] type, takes care of safely updating lazy field status when data
106/// is being stored.
107pub struct FXWriter<'a, B>
108where
109    B: FXBuilderWrapperAsync,
110{
111    lock:    RefCell<RwLockWriteGuard<'a, Option<B::Value>>>,
112    fxproxy: &'a FXProxy<B>,
113}
114
115impl<B, V> Debug for FXProxy<B>
116where
117    B: FXBuilderWrapperAsync<Value = V>,
118    V: Debug,
119{
120    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
121        #[cfg(feature = "async-tokio")]
122        let vlock = self.value.blocking_read();
123        #[cfg(feature = "async-lock")]
124        let vlock = self.value.read_blocking();
125
126        formatter
127            .debug_struct(any::type_name::<Self>())
128            .field("value", &*vlock)
129            .finish()
130    }
131}
132
133impl<B, E> FXProxy<B>
134where
135    B: FXBuilderWrapperAsync<Error = E>,
136    E: Debug,
137{
138    #[doc(hidden)]
139    pub fn new_default(builder: B, value: Option<B::Value>) -> Self {
140        Self {
141            is_set:  AtomicBool::new(value.is_some()),
142            value:   RwLock::new(value),
143            builder: RwLock::new(Some(builder)),
144        }
145    }
146
147    /// Consumes the container, returns the wrapped value or None if the container is empty
148    pub fn into_inner(self) -> Option<B::Value> {
149        self.value.into_inner()
150    }
151
152    #[inline]
153    fn is_set_raw(&self) -> &AtomicBool {
154        &self.is_set
155    }
156
157    /// Returns `true` if the container has a value.
158    #[inline]
159    pub fn is_set(&self) -> bool {
160        self.is_set_raw().load(Ordering::SeqCst)
161    }
162
163    /// Initialize the field without obtaining the lock by calling code. _Note_ though that internally the lock is still
164    /// required.
165    pub async fn lazy_init(&self, owner: &B::Owner) {
166        let _ = self.read_or_init(owner).await;
167    }
168
169    async fn read_or_init<'a>(&'a self, owner: &B::Owner) -> Result<ReadOrInitGuard<'a, Option<B::Value>>, B::Error> {
170        #[cfg(feature = "async-tokio")]
171        let mut guard = self.value.write().await;
172        #[cfg(feature = "async-lock")]
173        let guard = self.value.upgradable_read().await;
174
175        if (*guard).is_none() {
176            #[cfg(feature = "async-lock")]
177            let mut guard = ReadOrInitGuard::upgrade(guard).await;
178            // No value has been set yet
179            match *self.builder.read().await {
180                Some(ref builder_cb) => {
181                    *guard = Some((*builder_cb).invoke(owner).await?);
182                    self.is_set_raw().store(true, Ordering::SeqCst);
183                }
184                None => panic!("Builder is not set"),
185            }
186            #[cfg(feature = "async-lock")]
187            return Ok(RwLockWriteGuard::downgrade_to_upgradable(guard));
188        }
189        Ok(guard)
190    }
191
192    /// Lazy-initialize the field if necessary and return lock read guard for the inner value.
193    ///
194    /// Panics if fallible field builder returns an error.
195    pub async fn read<'a>(&'a self, owner: &B::Owner) -> FXProxyReadGuard<'a, B::Value> {
196        FXProxyReadGuard::new(ReadOrInitGuard::downgrade(self.read_or_init(owner).await.unwrap()))
197    }
198
199    /// Lazy-initialize the field if necessary and return lock write guard for the inner value.
200    ///
201    /// Panics if fallible field builder returns an error.
202    pub async fn read_mut<'a>(&'a self, owner: &B::Owner) -> FXProxyWriteGuard<'a, B::Value> {
203        #[cfg(feature = "async-tokio")]
204        return FXProxyWriteGuard::new(self.read_or_init(owner).await.unwrap());
205        #[cfg(feature = "async-lock")]
206        return FXProxyWriteGuard::new(ReadOrInitGuard::upgrade(self.read_or_init(owner).await.unwrap()).await);
207    }
208
209    /// Lazy-initialize the field if necessary and return lock read guard for the inner value.
210    ///
211    /// Return the same error, as fallible field builder if it errors out.
212    pub async fn try_read<'a>(&'a self, owner: &B::Owner) -> Result<FXProxyReadGuard<'a, B::Value>, B::Error> {
213        #[cfg(feature = "async-tokio")]
214        return Ok(FXProxyReadGuard::new(self.read_or_init(owner).await?.downgrade()));
215        #[cfg(feature = "async-lock")]
216        return Ok(FXProxyReadGuard::new(ReadOrInitGuard::downgrade(
217            self.read_or_init(owner).await?,
218        )));
219    }
220
221    /// Lazy-initialize the field if necessary and return lock write guard for the inner value.
222    ///
223    /// Return the same error, as fallible field builder if it errors out.
224    pub async fn try_read_mut<'a>(&'a self, owner: &B::Owner) -> Result<FXProxyWriteGuard<'a, B::Value>, B::Error> {
225        #[cfg(feature = "async-tokio")]
226        return Ok(FXProxyWriteGuard::new(self.read_or_init(owner).await?));
227        #[cfg(feature = "async-lock")]
228        return Ok(FXProxyWriteGuard::new(
229            ReadOrInitGuard::upgrade(self.read_or_init(owner).await?).await,
230        ));
231    }
232
233    /// Provides write-lock to directly store the value. Never calls the lazy builder.
234    pub async fn write<'a>(&'a self) -> FXWriter<'a, B> {
235        FXWriter::<'a, B>::new(self.value.write().await, self)
236    }
237
238    fn clear_with_lock(&self, wguard: &mut RwLockWriteGuard<Option<B::Value>>) -> Option<B::Value> {
239        self.is_set_raw().store(false, Ordering::SeqCst);
240        wguard.take()
241    }
242
243    /// Resets the container into unitialized state
244    pub async fn clear(&self) -> Option<B::Value> {
245        let mut wguard = self.value.write().await;
246        self.clear_with_lock(&mut wguard)
247    }
248}
249
250#[allow(private_bounds)]
251impl<'a, B> FXWriter<'a, B>
252where
253    B: FXBuilderWrapperAsync,
254{
255    #[doc(hidden)]
256    pub fn new(lock: RwLockWriteGuard<'a, Option<B::Value>>, fxproxy: &'a FXProxy<B>) -> Self {
257        let lock = RefCell::new(lock);
258        Self { lock, fxproxy }
259    }
260
261    /// Store a new value into the container and returns the previous value or `None`.
262    pub fn store(&mut self, value: B::Value) -> Option<B::Value> {
263        self.fxproxy.is_set_raw().store(true, Ordering::Release);
264        self.lock.borrow_mut().replace(value)
265    }
266
267    /// Resets the container into unitialized state
268    pub fn clear(&self) -> Option<B::Value> {
269        self.fxproxy.clear_with_lock(&mut *self.lock.borrow_mut())
270    }
271}
272
273impl<B> Clone for FXProxy<B>
274where
275    B: FXBuilderWrapperAsync + Clone,
276    B::Value: Clone,
277{
278    fn clone(&self) -> Self {
279        #[cfg(feature = "async-tokio")]
280        let vguard = self.value.blocking_read();
281        #[cfg(feature = "async-tokio")]
282        let bguard = self.builder.blocking_read();
283
284        #[cfg(feature = "async-lock")]
285        let vguard = self.value.read_blocking();
286        #[cfg(feature = "async-lock")]
287        let bguard = self.builder.read_blocking();
288
289        Self {
290            value:   RwLock::new((*vguard).as_ref().cloned()),
291            is_set:  AtomicBool::new(self.is_set()),
292            builder: RwLock::new((*bguard).clone()),
293        }
294    }
295}