rust_lodash/chain/
mod.rs

1/*!
2Chain module for Lodash-RS.
3
4This module provides the fluent method chaining system that allows
5composing multiple operations in a readable and efficient way.
6*/
7
8pub mod builder;
9pub mod executor;
10
11use crate::collection::Collection;
12// Note: These imports are kept for future use in error handling
13// use crate::utils::{LodashError, Result};
14
15/// Create a chain wrapper that enables method chaining.
16/// 
17/// # Examples
18/// 
19/// ```
20/// use rust_lodash::chain::chain;
21/// 
22/// let result = chain(&[1, 2, 3, 4, 5])
23///     .filter(|x| x % 2 == 0)
24///     .map(|x| x * 3)
25///     .collect();
26/// assert_eq!(result, vec![6, 12]);
27/// ```
28pub fn chain<T>(data: &[T]) -> Chain<T>
29where
30    T: Clone,
31{
32    Chain::new(data)
33}
34
35/// Create an async chain wrapper that enables async method chaining.
36/// 
37/// # Examples
38/// 
39/// ```
40/// use rust_lodash::chain::chain_async;
41/// 
42/// # async fn example() {
43/// let result = chain_async(&[1, 2, 3, 4, 5])
44///     .filter_async(|x| async move { x % 2 == 0 })
45///     .map_async(|x| async move { x * 3 })
46///     .await;
47/// assert_eq!(result, vec![6, 12]);
48/// # }
49/// ```
50#[cfg(feature = "async")]
51pub fn chain_async<T>(data: &[T]) -> AsyncChain<T>
52where
53    T: Clone,
54{
55    AsyncChain::new(data)
56}
57
58/// Chain wrapper for synchronous operations.
59/// 
60/// This struct provides a fluent interface for chaining collection operations.
61/// Operations are lazily evaluated and only executed when `collect()` or `value()` is called.
62pub struct Chain<T> {
63    /// The underlying data
64    data: Vec<T>,
65    /// Operations to be applied
66    operations: Vec<Operation<T>>,
67}
68
69/// Async chain wrapper for asynchronous operations.
70#[cfg(feature = "async")]
71pub struct AsyncChain<T> {
72    /// The underlying data
73    data: Vec<T>,
74    /// Async operations to be applied
75    operations: Vec<AsyncOperation<T>>,
76}
77
78/// Represents a synchronous operation in the chain.
79pub enum Operation<T> {
80    /// Map operation
81    Map(Box<dyn Fn(&T) -> T + Send + Sync>),
82    /// Filter operation
83    Filter(Box<dyn Fn(&T) -> bool + Send + Sync>),
84    /// Take operation
85    Take(usize),
86    /// Skip operation
87    Skip(usize),
88    /// Reverse operation
89    Reverse,
90}
91
92/// Represents an asynchronous operation in the chain.
93#[cfg(feature = "async")]
94pub enum AsyncOperation<T> {
95    /// Async map operation
96    MapAsync(Box<dyn Fn(&T) -> std::pin::Pin<Box<dyn std::future::Future<Output = T> + Send + Sync>> + Send + Sync>),
97    /// Async filter operation
98    FilterAsync(Box<dyn Fn(&T) -> std::pin::Pin<Box<dyn std::future::Future<Output = bool> + Send + Sync>> + Send + Sync>),
99    /// Take operation
100    Take(usize),
101    /// Skip operation
102    Skip(usize),
103    /// Reverse operation
104    Reverse,
105}
106
107impl<T> Chain<T>
108where
109    T: Clone,
110{
111    /// Create a new chain with the given data.
112    pub fn new(data: &[T]) -> Self {
113        Self {
114            data: data.to_vec(),
115            operations: Vec::new(),
116        }
117    }
118
119    /// Apply a map operation to each element.
120    /// 
121    /// # Examples
122    /// 
123    /// ```
124    /// use rust_lodash::chain::chain;
125    /// 
126    /// let result = chain(&[1, 2, 3])
127    ///     .map(|x| x * 2)
128    ///     .collect();
129    /// assert_eq!(result, vec![2, 4, 6]);
130    /// ```
131    #[must_use]
132    pub fn map<F>(mut self, mapper: F) -> Self
133    where
134        F: Fn(&T) -> T + Send + Sync + 'static,
135    {
136        self.operations.push(Operation::Map(Box::new(mapper)));
137        self
138    }
139
140    /// Apply a filter operation to each element.
141    /// 
142    /// # Examples
143    /// 
144    /// ```
145    /// use rust_lodash::chain::chain;
146    /// 
147    /// let result = chain(&[1, 2, 3, 4, 5])
148    ///     .filter(|x| x % 2 == 0)
149    ///     .collect();
150    /// assert_eq!(result, vec![2, 4]);
151    /// ```
152    #[must_use]
153    pub fn filter<F>(mut self, predicate: F) -> Self
154    where
155        F: Fn(&T) -> bool + Send + Sync + 'static,
156    {
157        self.operations.push(Operation::Filter(Box::new(predicate)));
158        self
159    }
160
161    /// Take the first n elements.
162    /// 
163    /// # Examples
164    /// 
165    /// ```
166    /// use rust_lodash::chain::chain;
167    /// 
168    /// let result = chain(&[1, 2, 3, 4, 5])
169    ///     .take(3)
170    ///     .collect();
171    /// assert_eq!(result, vec![1, 2, 3]);
172    /// ```
173    #[must_use]
174    pub fn take(mut self, n: usize) -> Self {
175        self.operations.push(Operation::Take(n));
176        self
177    }
178
179    /// Skip the first n elements.
180    /// 
181    /// # Examples
182    /// 
183    /// ```
184    /// use rust_lodash::chain::chain;
185    /// 
186    /// let result = chain(&[1, 2, 3, 4, 5])
187    ///     .skip(2)
188    ///     .collect();
189    /// assert_eq!(result, vec![3, 4, 5]);
190    /// ```
191    #[must_use]
192    pub fn skip(mut self, n: usize) -> Self {
193        self.operations.push(Operation::Skip(n));
194        self
195    }
196
197    /// Reverse the order of elements.
198    /// 
199    /// # Examples
200    /// 
201    /// ```
202    /// use rust_lodash::chain::chain;
203    /// 
204    /// let result = chain(&[1, 2, 3])
205    ///     .reverse()
206    ///     .collect();
207    /// assert_eq!(result, vec![3, 2, 1]);
208    /// ```
209    #[must_use]
210    pub fn reverse(mut self) -> Self {
211        self.operations.push(Operation::Reverse);
212        self
213    }
214
215    /// Collect the results into a vector.
216    /// 
217    /// # Examples
218    /// 
219    /// ```
220    /// use rust_lodash::chain::chain;
221    /// 
222    /// let result = chain(&[1, 2, 3])
223    ///     .map(|x| x * 2)
224    ///     .collect();
225    /// assert_eq!(result, vec![2, 4, 6]);
226    /// ```
227    #[must_use]
228    pub fn collect(self) -> Vec<T> {
229        self.value()
230    }
231
232    /// Get the final value after applying all operations.
233    /// 
234    /// # Examples
235    /// 
236    /// ```
237    /// use rust_lodash::chain::chain;
238    /// 
239    /// let result = chain(&[1, 2, 3])
240    ///     .map(|x| x * 2)
241    ///     .value();
242    /// assert_eq!(result, vec![2, 4, 6]);
243    /// ```
244    #[must_use]
245    pub fn value(self) -> Vec<T> {
246        let mut result = self.data;
247        
248        for operation in self.operations {
249            match operation {
250                Operation::Map(mapper) => {
251                    result = result.into_iter().map(|x| mapper(&x)).collect();
252                }
253                Operation::Filter(predicate) => {
254                    result.retain(|x| predicate(x));
255                }
256                Operation::Take(n) => {
257                    result = result.into_iter().take(n).collect();
258                }
259                Operation::Skip(n) => {
260                    result = result.into_iter().skip(n).collect();
261                }
262                Operation::Reverse => {
263                    result.reverse();
264                }
265            }
266        }
267        
268        result
269    }
270
271    /// Convert to a Collection.
272    /// 
273    /// # Examples
274    /// 
275    /// ```
276    /// use rust_lodash::chain::chain;
277    /// 
278    /// let collection = chain(&[1, 2, 3])
279    ///     .map(|x| x * 2)
280    ///     .into_collection();
281    /// assert_eq!(collection.data(), &vec![2, 4, 6]);
282    /// ```
283    #[must_use]
284    pub fn into_collection(self) -> Collection<T> {
285        Collection::new(self.value())
286    }
287}
288
289#[cfg(feature = "async")]
290impl<T> AsyncChain<T>
291where
292    T: Clone,
293{
294    /// Create a new async chain with the given data.
295    pub fn new(data: &[T]) -> Self {
296        Self {
297            data: data.to_vec(),
298            operations: Vec::new(),
299        }
300    }
301
302    /// Apply an async map operation to each element.
303    /// 
304    /// # Examples
305    /// 
306    /// ```
307    /// use rust_lodash::chain::chain_async;
308    /// 
309    /// # async fn example() {
310    /// let result = chain_async(&[1, 2, 3])
311    ///     .map_async(|x| async move { x * 2 })
312    ///     .await;
313    /// assert_eq!(result, vec![2, 4, 6]);
314    /// # }
315    /// ```
316    pub fn map_async<F, Fut>(mut self, mapper: F) -> Self
317    where
318        F: Fn(&T) -> Fut + Send + Sync + 'static,
319        Fut: std::future::Future<Output = T> + Send + Sync + 'static,
320    {
321        self.operations.push(AsyncOperation::MapAsync(Box::new(move |x| {
322            Box::pin(mapper(x))
323        })));
324        self
325    }
326
327    /// Apply an async filter operation to each element.
328    /// 
329    /// # Examples
330    /// 
331    /// ```
332    /// use rust_lodash::chain::chain_async;
333    /// 
334    /// # async fn example() {
335    /// let result = chain_async(&[1, 2, 3, 4, 5])
336    ///     .filter_async(|x| async move { x % 2 == 0 })
337    ///     .await;
338    /// assert_eq!(result, vec![2, 4]);
339    /// # }
340    /// ```
341    pub fn filter_async<F, Fut>(mut self, predicate: F) -> Self
342    where
343        F: Fn(&T) -> Fut + Send + Sync + 'static,
344        Fut: std::future::Future<Output = bool> + Send + Sync + 'static,
345    {
346        self.operations.push(AsyncOperation::FilterAsync(Box::new(move |x| {
347            Box::pin(predicate(x))
348        })));
349        self
350    }
351
352    /// Take the first n elements.
353    pub fn take(mut self, n: usize) -> Self {
354        self.operations.push(AsyncOperation::Take(n));
355        self
356    }
357
358    /// Skip the first n elements.
359    pub fn skip(mut self, n: usize) -> Self {
360        self.operations.push(AsyncOperation::Skip(n));
361        self
362    }
363
364    /// Reverse the order of elements.
365    pub fn reverse(mut self) -> Self {
366        self.operations.push(AsyncOperation::Reverse);
367        self
368    }
369
370    /// Get the final value after applying all async operations.
371    /// 
372    /// # Examples
373    /// 
374    /// ```
375    /// use rust_lodash::chain::chain_async;
376    /// 
377    /// # async fn example() {
378    /// let result = chain_async(&[1, 2, 3])
379    ///     .map_async(|x| async move { x * 2 })
380    ///     .await;
381    /// assert_eq!(result, vec![2, 4, 6]);
382    /// # }
383    /// ```
384    pub async fn r#await(self) -> Vec<T> {
385        let mut result = self.data;
386        
387        for operation in self.operations {
388            match operation {
389                AsyncOperation::MapAsync(mapper) => {
390                    let mut new_result = Vec::new();
391                    for item in result {
392                        let mapped = mapper(&item).await;
393                        new_result.push(mapped);
394                    }
395                    result = new_result;
396                }
397                AsyncOperation::FilterAsync(predicate) => {
398                    let mut new_result = Vec::new();
399                    for item in result {
400                        if predicate(&item).await {
401                            new_result.push(item);
402                        }
403                    }
404                    result = new_result;
405                }
406                AsyncOperation::Take(n) => {
407                    result = result.into_iter().take(n).collect();
408                }
409                AsyncOperation::Skip(n) => {
410                    result = result.into_iter().skip(n).collect();
411                }
412                AsyncOperation::Reverse => {
413                    result.reverse();
414                }
415            }
416        }
417        
418        result
419    }
420
421    /// Convert to a Collection.
422    pub async fn into_collection(self) -> Collection<T> {
423        Collection::new(self.r#await().await)
424    }
425}
426
427#[cfg(test)]
428mod tests {
429    use super::*;
430
431    #[test]
432    fn test_chain_map() {
433        let result = chain(&[1, 2, 3])
434            .map(|x| x * 2)
435            .collect();
436        assert_eq!(result, vec![2, 4, 6]);
437    }
438
439    #[test]
440    fn test_chain_filter() {
441        let result = chain(&[1, 2, 3, 4, 5])
442            .filter(|x| x % 2 == 0)
443            .collect();
444        assert_eq!(result, vec![2, 4]);
445    }
446
447    #[test]
448    fn test_chain_take() {
449        let result = chain(&[1, 2, 3, 4, 5])
450            .take(3)
451            .collect();
452        assert_eq!(result, vec![1, 2, 3]);
453    }
454
455    #[test]
456    fn test_chain_skip() {
457        let result = chain(&[1, 2, 3, 4, 5])
458            .skip(2)
459            .collect();
460        assert_eq!(result, vec![3, 4, 5]);
461    }
462
463    #[test]
464    fn test_chain_reverse() {
465        let result = chain(&[1, 2, 3])
466            .reverse()
467            .collect();
468        assert_eq!(result, vec![3, 2, 1]);
469    }
470
471    #[test]
472    fn test_chain_complex() {
473        let result = chain(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
474            .filter(|x| x % 2 == 0)
475            .map(|x| x * 3)
476            .take(3)
477            .collect();
478        assert_eq!(result, vec![6, 12, 18]);
479    }
480
481    #[test]
482    fn test_chain_value() {
483        let result = chain(&[1, 2, 3])
484            .map(|x| x * 2)
485            .value();
486        assert_eq!(result, vec![2, 4, 6]);
487    }
488
489    #[test]
490    fn test_chain_into_collection() {
491        let collection = chain(&[1, 2, 3])
492            .map(|x| x * 2)
493            .into_collection();
494        assert_eq!(collection.data(), &vec![2, 4, 6]);
495    }
496
497    #[test]
498    fn test_chain_empty() {
499        let empty: Vec<i32> = vec![];
500        let result = chain(&empty)
501            .map(|x| x * 2)
502            .collect();
503        assert!(result.is_empty());
504    }
505
506    #[cfg(feature = "async")]
507    #[tokio::test]
508    async fn test_chain_async_map() {
509        let result = chain_async(&[1, 2, 3])
510            .map_async(|x| async move { x * 2 })
511            .await;
512        assert_eq!(result, vec![2, 4, 6]);
513    }
514
515    #[cfg(feature = "async")]
516    #[tokio::test]
517    async fn test_chain_async_filter() {
518        let result = chain_async(&[1, 2, 3, 4, 5])
519            .filter_async(|x| async move { x % 2 == 0 })
520            .await;
521        assert_eq!(result, vec![2, 4]);
522    }
523
524    #[cfg(feature = "async")]
525    #[tokio::test]
526    async fn test_chain_async_complex() {
527        let result = chain_async(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
528            .filter_async(|x| async move { x % 2 == 0 })
529            .map_async(|x| async move { x * 3 })
530            .take(3)
531            .await;
532        assert_eq!(result, vec![6, 12, 18]);
533    }
534
535    #[cfg(feature = "async")]
536    #[tokio::test]
537    async fn test_chain_async_into_collection() {
538        let collection = chain_async(&[1, 2, 3])
539            .map_async(|x| async move { x * 2 })
540            .into_collection()
541            .await;
542        assert_eq!(collection.data(), &vec![2, 4, 6]);
543    }
544}