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}