rust_queries_core/
lock_ext.rs

1//! Extended lock support for parking_lot and tokio.
2//!
3//! This module provides wrappers and extension traits for third-party lock types,
4//! enabling them to work with the lock-aware query system.
5//!
6//! ## Features
7//!
8//! - **parking_lot Support**: High-performance RwLock and Mutex wrappers
9//! - **tokio Support**: Async RwLock support for async applications
10//! - **Extension Traits**: Direct `.lock_query()` and `.lock_join()` support
11//!
12//! ## Example (parking_lot)
13//!
14//! ```ignore
15//! use rust_queries_core::lock_ext::{ParkingLotRwLockWrapper, ParkingLotQueryExt};
16//! use std::collections::HashMap;
17//! use parking_lot::RwLock;
18//!
19//! let mut products: HashMap<String, ParkingLotRwLockWrapper<Product>> = HashMap::new();
20//! products.insert("p1".to_string(), ParkingLotRwLockWrapper::new(Product {
21//!     id: 1,
22//!     price: 999.99,
23//! }));
24//!
25//! // Direct method call!
26//! let expensive = products
27//!     .lock_query()
28//!     .where_(Product::price(), |&p| p > 500.0)
29//!     .all();
30//! ```
31//!
32//! ## Example (tokio)
33//!
34//! ```ignore
35//! use rust_queries_core::lock_ext::{TokioRwLockWrapper, TokioLockQueryExt};
36//! use std::collections::HashMap;
37//!
38//! async fn query_products(products: &HashMap<String, TokioRwLockWrapper<Product>>) {
39//!     let expensive = products
40//!         .lock_query()  // Direct method call!
41//!         .where_(Product::price(), |&p| p > 500.0)
42//!         .all();
43//! }
44//! ```
45
46#[cfg(any(feature = "parking_lot", feature = "tokio"))]
47use crate::locks::LockValue;
48
49#[cfg(any(feature = "parking_lot", feature = "tokio"))]
50use crate::lock_query::LockQuery;
51
52#[cfg(any(feature = "parking_lot", feature = "tokio"))]
53use crate::lock_lazy::LockLazyQuery;
54
55#[cfg(any(feature = "parking_lot", feature = "tokio"))]
56use crate::lock_join::LockJoinQuery;
57
58#[cfg(any(feature = "parking_lot", feature = "tokio"))]
59use std::collections::HashMap;
60
61#[cfg(any(feature = "parking_lot", feature = "tokio"))]
62use std::sync::Arc;
63
64// ============================================================================
65// parking_lot Support
66// ============================================================================
67
68/// Wrapper around Arc<parking_lot::RwLock<T>>.
69///
70/// This newtype is needed because of Rust's orphan rules - we can't implement
71/// foreign traits (LockValue) on foreign types.
72///
73/// # Example
74///
75/// ```ignore
76/// use rust_queries_core::lock_ext::ParkingLotRwLockWrapper;
77/// use parking_lot::RwLock;
78///
79/// let wrapper = ParkingLotRwLockWrapper::new(Product {
80///     id: 1,
81///     name: "Laptop".to_string(),
82///     price: 999.99,
83/// });
84/// ```
85#[cfg(feature = "parking_lot")]
86#[derive(Clone, Debug)]
87pub struct ParkingLotRwLockWrapper<T>(Arc<parking_lot::RwLock<T>>);
88
89#[cfg(feature = "parking_lot")]
90impl<T> ParkingLotRwLockWrapper<T> {
91    /// Create a new ParkingLotRwLockWrapper.
92    pub fn new(value: T) -> Self {
93        Self(Arc::new(parking_lot::RwLock::new(value)))
94    }
95
96    /// Get a reference to the inner Arc<RwLock<T>>.
97    pub fn inner(&self) -> &Arc<parking_lot::RwLock<T>> {
98        &self.0
99    }
100}
101
102#[cfg(feature = "parking_lot")]
103impl<T> LockValue<T> for ParkingLotRwLockWrapper<T> {
104    fn with_value<F, R>(&self, f: F) -> Option<R>
105    where
106        F: FnOnce(&T) -> R,
107    {
108        // parking_lot RwLock is synchronous and doesn't panic on poisoning
109        let guard = self.0.read();
110        Some(f(&*guard))
111    }
112}
113
114/// Wrapper around Arc<parking_lot::Mutex<T>>.
115#[cfg(feature = "parking_lot")]
116#[derive(Clone, Debug)]
117pub struct ParkingLotMutexWrapper<T>(Arc<parking_lot::Mutex<T>>);
118
119#[cfg(feature = "parking_lot")]
120impl<T> ParkingLotMutexWrapper<T> {
121    /// Create a new ParkingLotMutexWrapper.
122    pub fn new(value: T) -> Self {
123        Self(Arc::new(parking_lot::Mutex::new(value)))
124    }
125
126    /// Get a reference to the inner Arc<Mutex<T>>.
127    pub fn inner(&self) -> &Arc<parking_lot::Mutex<T>> {
128        &self.0
129    }
130}
131
132#[cfg(feature = "parking_lot")]
133impl<T> LockValue<T> for ParkingLotMutexWrapper<T> {
134    fn with_value<F, R>(&self, f: F) -> Option<R>
135    where
136        F: FnOnce(&T) -> R,
137    {
138        // parking_lot Mutex is synchronous and doesn't panic on poisoning
139        let guard = self.0.lock();
140        Some(f(&*guard))
141    }
142}
143
144// Extension traits for parking_lot
145
146/// Extension trait to enable direct .lock_query() and .lock_lazy_query() calls
147/// on HashMap with parking_lot RwLock.
148#[cfg(feature = "parking_lot")]
149pub trait ParkingLotQueryExt<V> {
150    /// Create a LockQuery for SQL-like operations.
151    fn lock_query(&self) -> LockQuery<'_, V, ParkingLotRwLockWrapper<V>>;
152    
153    /// Create a lazy lock query.
154    fn lock_lazy_query(&self) -> LockLazyQuery<'_, V, ParkingLotRwLockWrapper<V>, impl Iterator<Item = &ParkingLotRwLockWrapper<V>>>;
155}
156
157#[cfg(feature = "parking_lot")]
158impl<K, V: 'static> ParkingLotQueryExt<V> for HashMap<K, ParkingLotRwLockWrapper<V>>
159where
160    K: std::hash::Hash + Eq,
161{
162    fn lock_query(&self) -> LockQuery<'_, V, ParkingLotRwLockWrapper<V>> {
163        let locks: Vec<_> = self.values().collect();
164        LockQuery::from_locks(locks)
165    }
166    
167    fn lock_lazy_query(&self) -> LockLazyQuery<'_, V, ParkingLotRwLockWrapper<V>, impl Iterator<Item = &ParkingLotRwLockWrapper<V>>> {
168        LockLazyQuery::new(self.values())
169    }
170}
171
172/// Extension trait for Mutex queries.
173#[cfg(feature = "parking_lot")]
174pub trait ParkingLotMutexQueryExt<V> {
175    /// Create a LockQuery for SQL-like operations.
176    fn lock_query(&self) -> LockQuery<'_, V, ParkingLotMutexWrapper<V>>;
177    
178    /// Create a lazy lock query.
179    fn lock_lazy_query(&self) -> LockLazyQuery<'_, V, ParkingLotMutexWrapper<V>, impl Iterator<Item = &ParkingLotMutexWrapper<V>>>;
180}
181
182#[cfg(feature = "parking_lot")]
183impl<K, V: 'static> ParkingLotMutexQueryExt<V> for HashMap<K, ParkingLotMutexWrapper<V>>
184where
185    K: std::hash::Hash + Eq,
186{
187    fn lock_query(&self) -> LockQuery<'_, V, ParkingLotMutexWrapper<V>> {
188        let locks: Vec<_> = self.values().collect();
189        LockQuery::from_locks(locks)
190    }
191    
192    fn lock_lazy_query(&self) -> LockLazyQuery<'_, V, ParkingLotMutexWrapper<V>, impl Iterator<Item = &ParkingLotMutexWrapper<V>>> {
193        LockLazyQuery::new(self.values())
194    }
195}
196
197// Extension trait for JOIN operations
198
199/// Extension trait for JOIN operations with parking_lot RwLock.
200#[cfg(feature = "parking_lot")]
201pub trait ParkingLotJoinExt<V> {
202    /// Create a join query with another collection.
203    fn lock_join<'a, R>(&'a self, right: &'a HashMap<impl std::hash::Hash + Eq, ParkingLotRwLockWrapper<R>>) 
204        -> LockJoinQuery<'a, V, R, ParkingLotRwLockWrapper<V>, ParkingLotRwLockWrapper<R>>
205    where
206        R: 'static;
207}
208
209#[cfg(feature = "parking_lot")]
210impl<K, V: 'static> ParkingLotJoinExt<V> for HashMap<K, ParkingLotRwLockWrapper<V>>
211where
212    K: std::hash::Hash + Eq,
213{
214    fn lock_join<'a, R>(&'a self, right: &'a HashMap<impl std::hash::Hash + Eq, ParkingLotRwLockWrapper<R>>) 
215        -> LockJoinQuery<'a, V, R, ParkingLotRwLockWrapper<V>, ParkingLotRwLockWrapper<R>>
216    where
217        R: 'static,
218    {
219        let left_locks: Vec<_> = self.values().collect();
220        let right_locks: Vec<_> = right.values().collect();
221        LockJoinQuery::new(left_locks, right_locks)
222    }
223}
224
225/// Extension trait for JOIN operations with parking_lot Mutex.
226#[cfg(feature = "parking_lot")]
227pub trait ParkingLotMutexJoinExt<V> {
228    /// Create a join query with another collection.
229    fn lock_join<'a, R>(&'a self, right: &'a HashMap<impl std::hash::Hash + Eq, ParkingLotMutexWrapper<R>>) 
230        -> LockJoinQuery<'a, V, R, ParkingLotMutexWrapper<V>, ParkingLotMutexWrapper<R>>
231    where
232        R: 'static;
233}
234
235#[cfg(feature = "parking_lot")]
236impl<K, V: 'static> ParkingLotMutexJoinExt<V> for HashMap<K, ParkingLotMutexWrapper<V>>
237where
238    K: std::hash::Hash + Eq,
239{
240    fn lock_join<'a, R>(&'a self, right: &'a HashMap<impl std::hash::Hash + Eq, ParkingLotMutexWrapper<R>>) 
241        -> LockJoinQuery<'a, V, R, ParkingLotMutexWrapper<V>, ParkingLotMutexWrapper<R>>
242    where
243        R: 'static,
244    {
245        let left_locks: Vec<_> = self.values().collect();
246        let right_locks: Vec<_> = right.values().collect();
247        LockJoinQuery::new(left_locks, right_locks)
248    }
249}
250
251// ============================================================================
252// tokio Support
253// ============================================================================
254
255/// Wrapper around Arc<tokio::sync::RwLock<T>>.
256///
257/// This newtype enables tokio RwLock to work with the lock-aware query system.
258///
259/// # Example
260///
261/// ```ignore
262/// use rust_queries_core::lock_ext::TokioRwLockWrapper;
263///
264/// let wrapper = TokioRwLockWrapper::new(Product {
265///     id: 1,
266///     name: "Laptop".to_string(),
267///     price: 999.99,
268/// });
269/// ```
270#[cfg(feature = "tokio")]
271#[derive(Clone, Debug)]
272pub struct TokioRwLockWrapper<T>(Arc<tokio::sync::RwLock<T>>);
273
274#[cfg(feature = "tokio")]
275impl<T> TokioRwLockWrapper<T> {
276    /// Create a new TokioRwLockWrapper.
277    pub fn new(value: T) -> Self {
278        Self(Arc::new(tokio::sync::RwLock::new(value)))
279    }
280
281    /// Get a reference to the inner Arc<RwLock<T>>.
282    pub fn inner(&self) -> &Arc<tokio::sync::RwLock<T>> {
283        &self.0
284    }
285}
286
287#[cfg(feature = "tokio")]
288impl<T> LockValue<T> for TokioRwLockWrapper<T> {
289    fn with_value<F, R>(&self, f: F) -> Option<R>
290    where
291        F: FnOnce(&T) -> R,
292    {
293        // Note: This blocks! For fully async code, consider using async queries.
294        // We use blocking here because LockValue trait is synchronous.
295        let guard = self.0.blocking_read();
296        Some(f(&*guard))
297    }
298}
299
300/// Wrapper around Arc<tokio::sync::Mutex<T>>.
301#[cfg(feature = "tokio")]
302#[derive(Clone, Debug)]
303pub struct TokioMutexWrapper<T>(Arc<tokio::sync::Mutex<T>>);
304
305#[cfg(feature = "tokio")]
306impl<T> TokioMutexWrapper<T> {
307    /// Create a new TokioMutexWrapper.
308    pub fn new(value: T) -> Self {
309        Self(Arc::new(tokio::sync::Mutex::new(value)))
310    }
311
312    /// Get a reference to the inner Arc<Mutex<T>>.
313    pub fn inner(&self) -> &Arc<tokio::sync::Mutex<T>> {
314        &self.0
315    }
316}
317
318#[cfg(feature = "tokio")]
319impl<T> LockValue<T> for TokioMutexWrapper<T> {
320    fn with_value<F, R>(&self, f: F) -> Option<R>
321    where
322        F: FnOnce(&T) -> R,
323    {
324        // Note: This blocks! For fully async code, consider using async queries.
325        let guard = self.0.blocking_lock();
326        Some(f(&*guard))
327    }
328}
329
330// Extension traits for tokio
331
332/// Extension trait to enable direct .lock_query() and .lock_lazy_query() calls
333/// on HashMap with tokio RwLock.
334#[cfg(feature = "tokio")]
335pub trait TokioLockQueryExt<V> {
336    /// Create a LockQuery for SQL-like operations.
337    fn lock_query(&self) -> LockQuery<'_, V, TokioRwLockWrapper<V>>;
338    
339    /// Create a lazy lock query.
340    fn lock_lazy_query(&self) -> LockLazyQuery<'_, V, TokioRwLockWrapper<V>, impl Iterator<Item = &TokioRwLockWrapper<V>>>;
341}
342
343#[cfg(feature = "tokio")]
344impl<K, V: 'static> TokioLockQueryExt<V> for HashMap<K, TokioRwLockWrapper<V>>
345where
346    K: std::hash::Hash + Eq,
347{
348    fn lock_query(&self) -> LockQuery<'_, V, TokioRwLockWrapper<V>> {
349        let locks: Vec<_> = self.values().collect();
350        LockQuery::from_locks(locks)
351    }
352    
353    fn lock_lazy_query(&self) -> LockLazyQuery<'_, V, TokioRwLockWrapper<V>, impl Iterator<Item = &TokioRwLockWrapper<V>>> {
354        LockLazyQuery::new(self.values())
355    }
356}
357
358/// Extension trait for Mutex queries.
359#[cfg(feature = "tokio")]
360pub trait TokioMutexQueryExt<V> {
361    /// Create a LockQuery for SQL-like operations.
362    fn lock_query(&self) -> LockQuery<'_, V, TokioMutexWrapper<V>>;
363    
364    /// Create a lazy lock query.
365    fn lock_lazy_query(&self) -> LockLazyQuery<'_, V, TokioMutexWrapper<V>, impl Iterator<Item = &TokioMutexWrapper<V>>>;
366}
367
368#[cfg(feature = "tokio")]
369impl<K, V: 'static> TokioMutexQueryExt<V> for HashMap<K, TokioMutexWrapper<V>>
370where
371    K: std::hash::Hash + Eq,
372{
373    fn lock_query(&self) -> LockQuery<'_, V, TokioMutexWrapper<V>> {
374        let locks: Vec<_> = self.values().collect();
375        LockQuery::from_locks(locks)
376    }
377    
378    fn lock_lazy_query(&self) -> LockLazyQuery<'_, V, TokioMutexWrapper<V>, impl Iterator<Item = &TokioMutexWrapper<V>>> {
379        LockLazyQuery::new(self.values())
380    }
381}
382
383// Extension trait for JOIN operations
384
385/// Extension trait for JOIN operations with tokio RwLock.
386#[cfg(feature = "tokio")]
387pub trait TokioLockJoinExt<V> {
388    /// Create a join query with another collection.
389    fn lock_join<'a, R>(&'a self, right: &'a HashMap<impl std::hash::Hash + Eq, TokioRwLockWrapper<R>>) 
390        -> LockJoinQuery<'a, V, R, TokioRwLockWrapper<V>, TokioRwLockWrapper<R>>
391    where
392        R: 'static;
393}
394
395#[cfg(feature = "tokio")]
396impl<K, V: 'static> TokioLockJoinExt<V> for HashMap<K, TokioRwLockWrapper<V>>
397where
398    K: std::hash::Hash + Eq,
399{
400    fn lock_join<'a, R>(&'a self, right: &'a HashMap<impl std::hash::Hash + Eq, TokioRwLockWrapper<R>>) 
401        -> LockJoinQuery<'a, V, R, TokioRwLockWrapper<V>, TokioRwLockWrapper<R>>
402    where
403        R: 'static,
404    {
405        let left_locks: Vec<_> = self.values().collect();
406        let right_locks: Vec<_> = right.values().collect();
407        LockJoinQuery::new(left_locks, right_locks)
408    }
409}
410
411/// Extension trait for JOIN operations with tokio Mutex.
412#[cfg(feature = "tokio")]
413pub trait TokioMutexJoinExt<V> {
414    /// Create a join query with another collection.
415    fn lock_join<'a, R>(&'a self, right: &'a HashMap<impl std::hash::Hash + Eq, TokioMutexWrapper<R>>) 
416        -> LockJoinQuery<'a, V, R, TokioMutexWrapper<V>, TokioMutexWrapper<R>>
417    where
418        R: 'static;
419}
420
421#[cfg(feature = "tokio")]
422impl<K, V: 'static> TokioMutexJoinExt<V> for HashMap<K, TokioMutexWrapper<V>>
423where
424    K: std::hash::Hash + Eq,
425{
426    fn lock_join<'a, R>(&'a self, right: &'a HashMap<impl std::hash::Hash + Eq, TokioMutexWrapper<R>>) 
427        -> LockJoinQuery<'a, V, R, TokioMutexWrapper<V>, TokioMutexWrapper<R>>
428    where
429        R: 'static,
430    {
431        let left_locks: Vec<_> = self.values().collect();
432        let right_locks: Vec<_> = right.values().collect();
433        LockJoinQuery::new(left_locks, right_locks)
434    }
435}
436
437#[cfg(test)]
438mod tests {
439    use super::*;
440    
441    #[cfg(feature = "parking_lot")]
442    #[test]
443    fn test_parking_lot_wrapper() {
444        let wrapper = ParkingLotRwLockWrapper::new(42);
445        let result = wrapper.with_value(|v| *v * 2);
446        assert_eq!(result, Some(84));
447    }
448    
449    #[cfg(feature = "tokio")]
450    #[test]
451    fn test_tokio_wrapper() {
452        let wrapper = TokioRwLockWrapper::new(42);
453        let result = wrapper.with_value(|v| *v * 2);
454        assert_eq!(result, Some(84));
455    }
456}
457