datalogic_rs/builder/
array_builder.rs

1use crate::arena::DataArena;
2use crate::logic::ArrayOp;
3use crate::logic::{Logic, OperatorType};
4
5/// Builder for array operations.
6///
7/// This builder provides a fluent interface for creating array operations
8/// such as map, filter, reduce, etc.
9pub struct ArrayBuilder<'a> {
10    /// The arena in which all allocations will be made.
11    arena: &'a DataArena,
12}
13
14impl<'a> ArrayBuilder<'a> {
15    /// Creates a new array builder.
16    pub fn new(arena: &'a DataArena) -> Self {
17        Self { arena }
18    }
19
20    /// Creates a map operation.
21    pub fn map_op(&self) -> MapBuilder<'a> {
22        MapBuilder::new(self.arena)
23    }
24
25    /// Creates a filter operation.
26    pub fn filter_op(&self) -> FilterBuilder<'a> {
27        FilterBuilder::new(self.arena)
28    }
29
30    /// Creates a reduce operation.
31    pub fn reduce_op(&self) -> ReduceBuilder<'a> {
32        ReduceBuilder::new(self.arena)
33    }
34
35    /// Creates a merge operation.
36    pub fn merge_op(&self) -> ArrayOperationBuilder<'a> {
37        ArrayOperationBuilder::new(self.arena, ArrayOp::Merge)
38    }
39
40    /// Creates a length operation.
41    pub fn length_op(&self, array: Logic<'a>) -> Logic<'a> {
42        Logic::operator(
43            OperatorType::Array(ArrayOp::Length),
44            vec![array],
45            self.arena,
46        )
47    }
48
49    /// Creates a slice operation.
50    pub fn slice_op(&self) -> SliceBuilder<'a> {
51        SliceBuilder::new(self.arena)
52    }
53
54    /// Creates a sort operation.
55    pub fn sort_op(&self) -> SortBuilder<'a> {
56        SortBuilder::new(self.arena)
57    }
58
59    /// Creates an in-array check operation.
60    pub fn in_op(&self, value: Logic<'a>, array: Logic<'a>) -> Logic<'a> {
61        Logic::operator(
62            OperatorType::Array(ArrayOp::In),
63            vec![value, array],
64            self.arena,
65        )
66    }
67
68    /// Creates an array literal.
69    pub fn array_literal_op(&self, elements: Vec<Logic<'a>>) -> Logic<'a> {
70        Logic::operator(OperatorType::ArrayLiteral, elements, self.arena)
71    }
72}
73
74/// Builder for map operations.
75pub struct MapBuilder<'a> {
76    /// The arena in which all allocations will be made.
77    arena: &'a DataArena,
78    /// The array to map over.
79    array: Option<Logic<'a>>,
80    /// The mapping function.
81    mapper: Option<Logic<'a>>,
82}
83
84impl<'a> MapBuilder<'a> {
85    /// Creates a new map builder.
86    pub fn new(arena: &'a DataArena) -> Self {
87        Self {
88            arena,
89            array: None,
90            mapper: None,
91        }
92    }
93
94    /// Sets the array to map over.
95    pub fn array(mut self, array: Logic<'a>) -> Self {
96        self.array = Some(array);
97        self
98    }
99
100    /// Sets the array to map over using a literal array of Logic values.
101    pub fn array_literal(self, elements: Vec<Logic<'a>>) -> Self {
102        let array = Logic::operator(OperatorType::ArrayLiteral, elements, self.arena);
103        self.array(array)
104    }
105
106    /// Sets the array to map over using a variable reference.
107    pub fn array_var(self, path: &str) -> Self {
108        let var = Logic::variable(path, None, self.arena);
109        self.array(var)
110    }
111
112    /// Sets the mapping function.
113    pub fn mapper(mut self, mapper: Logic<'a>) -> Self {
114        self.mapper = Some(mapper);
115        self
116    }
117
118    /// Sets the mapping function using a variable reference.
119    pub fn mapper_var(self, path: &str) -> Self {
120        let var = Logic::variable(path, None, self.arena);
121        self.mapper(var)
122    }
123
124    /// Builds the map operation.
125    pub fn build(self) -> Logic<'a> {
126        let array = self.array.unwrap_or_else(|| {
127            Logic::literal(crate::value::DataValue::array(self.arena, &[]), self.arena)
128        });
129
130        let mapper = self.mapper.unwrap_or_else(|| {
131            // Default mapper is identity function
132            Logic::variable("", None, self.arena)
133        });
134
135        Logic::operator(
136            OperatorType::Array(ArrayOp::Map),
137            vec![array, mapper],
138            self.arena,
139        )
140    }
141}
142
143/// Builder for filter operations.
144pub struct FilterBuilder<'a> {
145    /// The arena in which all allocations will be made.
146    arena: &'a DataArena,
147    /// The array to filter.
148    array: Option<Logic<'a>>,
149    /// The filter condition.
150    condition: Option<Logic<'a>>,
151}
152
153impl<'a> FilterBuilder<'a> {
154    /// Creates a new filter builder.
155    pub fn new(arena: &'a DataArena) -> Self {
156        Self {
157            arena,
158            array: None,
159            condition: None,
160        }
161    }
162
163    /// Sets the array to filter.
164    pub fn array(mut self, array: Logic<'a>) -> Self {
165        self.array = Some(array);
166        self
167    }
168
169    /// Sets the array to filter using a literal array of Logic values.
170    pub fn array_literal(self, elements: Vec<Logic<'a>>) -> Self {
171        let array = Logic::operator(OperatorType::ArrayLiteral, elements, self.arena);
172        self.array(array)
173    }
174
175    /// Sets the array to filter using a variable reference.
176    pub fn array_var(self, path: &str) -> Self {
177        let var = Logic::variable(path, None, self.arena);
178        self.array(var)
179    }
180
181    /// Sets the filter condition.
182    pub fn condition(mut self, condition: Logic<'a>) -> Self {
183        self.condition = Some(condition);
184        self
185    }
186
187    /// Sets the filter condition using a variable reference.
188    pub fn condition_var(self, path: &str) -> Self {
189        let var = Logic::variable(path, None, self.arena);
190        self.condition(var)
191    }
192
193    /// Builds the filter operation.
194    pub fn build(self) -> Logic<'a> {
195        let array = self.array.unwrap_or_else(|| {
196            Logic::literal(crate::value::DataValue::array(self.arena, &[]), self.arena)
197        });
198
199        let condition = self.condition.unwrap_or_else(|| {
200            // Default condition is truthy check
201            Logic::variable("", None, self.arena)
202        });
203
204        Logic::operator(
205            OperatorType::Array(ArrayOp::Filter),
206            vec![array, condition],
207            self.arena,
208        )
209    }
210}
211
212/// Builder for reduce operations.
213pub struct ReduceBuilder<'a> {
214    /// The arena in which all allocations will be made.
215    arena: &'a DataArena,
216    /// The array to reduce.
217    array: Option<Logic<'a>>,
218    /// The reducer function.
219    reducer: Option<Logic<'a>>,
220    /// The initial value.
221    initial: Option<Logic<'a>>,
222}
223
224impl<'a> ReduceBuilder<'a> {
225    /// Creates a new reduce builder.
226    pub fn new(arena: &'a DataArena) -> Self {
227        Self {
228            arena,
229            array: None,
230            reducer: None,
231            initial: None,
232        }
233    }
234
235    /// Sets the array to reduce.
236    pub fn array(mut self, array: Logic<'a>) -> Self {
237        self.array = Some(array);
238        self
239    }
240
241    /// Sets the array to reduce using a literal array of Logic values.
242    pub fn array_literal(self, elements: Vec<Logic<'a>>) -> Self {
243        let array = Logic::operator(OperatorType::ArrayLiteral, elements, self.arena);
244        self.array(array)
245    }
246
247    /// Sets the array to reduce using a variable reference.
248    pub fn array_var(self, path: &str) -> Self {
249        let var = Logic::variable(path, None, self.arena);
250        self.array(var)
251    }
252
253    /// Sets the reducer function.
254    pub fn reducer(mut self, reducer: Logic<'a>) -> Self {
255        self.reducer = Some(reducer);
256        self
257    }
258
259    /// Sets the reducer function using a variable reference.
260    pub fn reducer_var(self, path: &str) -> Self {
261        let var = Logic::variable(path, None, self.arena);
262        self.reducer(var)
263    }
264
265    /// Sets the initial value.
266    pub fn initial(mut self, initial: Logic<'a>) -> Self {
267        self.initial = Some(initial);
268        self
269    }
270
271    /// Sets the initial value using a variable reference.
272    pub fn initial_var(self, path: &str) -> Self {
273        let var = Logic::variable(path, None, self.arena);
274        self.initial(var)
275    }
276
277    /// Sets the initial value as an integer.
278    pub fn initial_int(self, value: i64) -> Self {
279        let val = Logic::literal(crate::value::DataValue::integer(value), self.arena);
280        self.initial(val)
281    }
282
283    /// Sets the initial value as a float.
284    pub fn initial_float(self, value: f64) -> Self {
285        let val = Logic::literal(crate::value::DataValue::float(value), self.arena);
286        self.initial(val)
287    }
288
289    /// Sets the initial value as a string.
290    pub fn initial_string(self, value: &str) -> Self {
291        let val = Logic::literal(
292            crate::value::DataValue::string(self.arena, value),
293            self.arena,
294        );
295        self.initial(val)
296    }
297
298    /// Sets the initial value as a boolean.
299    pub fn initial_bool(self, value: bool) -> Self {
300        let val = Logic::literal(crate::value::DataValue::bool(value), self.arena);
301        self.initial(val)
302    }
303
304    /// Builds the reduce operation.
305    pub fn build(self) -> Logic<'a> {
306        let array = self.array.unwrap_or_else(|| {
307            Logic::literal(crate::value::DataValue::array(self.arena, &[]), self.arena)
308        });
309
310        let reducer = self.reducer.unwrap_or_else(|| {
311            // Default reducer is sum
312            let var_a = Logic::variable("current", None, self.arena);
313            let var_b = Logic::variable("accumulator", None, self.arena);
314            Logic::operator(
315                OperatorType::Arithmetic(crate::logic::ArithmeticOp::Add),
316                vec![var_a, var_b],
317                self.arena,
318            )
319        });
320
321        let initial = self.initial.unwrap_or_else(|| {
322            // Default initial value is 0
323            Logic::literal(crate::value::DataValue::integer(0), self.arena)
324        });
325
326        Logic::operator(
327            OperatorType::Array(ArrayOp::Reduce),
328            vec![array, reducer, initial],
329            self.arena,
330        )
331    }
332}
333
334/// Builder for generic array operations like merge.
335pub struct ArrayOperationBuilder<'a> {
336    /// The arena in which all allocations will be made.
337    arena: &'a DataArena,
338    /// The array operation to use.
339    operation: ArrayOp,
340    /// The operands collected so far.
341    operands: Vec<Logic<'a>>,
342}
343
344impl<'a> ArrayOperationBuilder<'a> {
345    /// Creates a new array operation builder.
346    pub fn new(arena: &'a DataArena, operation: ArrayOp) -> Self {
347        Self {
348            arena,
349            operation,
350            operands: Vec::new(),
351        }
352    }
353
354    /// Adds an operand to the array operation.
355    pub fn operand(mut self, operand: Logic<'a>) -> Self {
356        self.operands.push(operand);
357        self
358    }
359
360    /// Adds a variable as an operand to the array operation.
361    pub fn var(mut self, path: &str) -> Self {
362        let var = Logic::variable(path, None, self.arena);
363        self.operands.push(var);
364        self
365    }
366
367    /// Adds a literal value as an operand to the array operation.
368    pub fn value<T: Into<crate::value::DataValue<'a>>>(mut self, value: T) -> Self {
369        let val = Logic::literal(value.into(), self.arena);
370        self.operands.push(val);
371        self
372    }
373
374    /// Adds an integer as an operand to the array operation.
375    pub fn int(mut self, value: i64) -> Self {
376        let val = Logic::literal(crate::value::DataValue::integer(value), self.arena);
377        self.operands.push(val);
378        self
379    }
380
381    /// Adds a float as an operand to the array operation.
382    pub fn float(mut self, value: f64) -> Self {
383        let val = Logic::literal(crate::value::DataValue::float(value), self.arena);
384        self.operands.push(val);
385        self
386    }
387
388    /// Adds a string as an operand to the array operation.
389    pub fn string(mut self, value: &str) -> Self {
390        let val = Logic::literal(
391            crate::value::DataValue::string(self.arena, value),
392            self.arena,
393        );
394        self.operands.push(val);
395        self
396    }
397
398    /// Adds a boolean as an operand to the array operation.
399    pub fn bool(mut self, value: bool) -> Self {
400        let val = Logic::literal(crate::value::DataValue::bool(value), self.arena);
401        self.operands.push(val);
402        self
403    }
404
405    /// Builds the array operation with the collected operands.
406    pub fn build(self) -> Logic<'a> {
407        if self.operands.is_empty() {
408            return Logic::literal(crate::value::DataValue::array(self.arena, &[]), self.arena);
409        }
410
411        Logic::operator(
412            OperatorType::Array(self.operation),
413            self.operands,
414            self.arena,
415        )
416    }
417}
418
419/// Builder for slice operations.
420pub struct SliceBuilder<'a> {
421    /// The arena in which all allocations will be made.
422    arena: &'a DataArena,
423    /// The array or string to slice.
424    collection: Option<Logic<'a>>,
425    /// The start index (default: 0).
426    start: Option<Logic<'a>>,
427    /// The end index (default: length).
428    end: Option<Logic<'a>>,
429    /// The step value (default: 1).
430    step: Option<Logic<'a>>,
431}
432
433impl<'a> SliceBuilder<'a> {
434    /// Creates a new slice builder.
435    pub fn new(arena: &'a DataArena) -> Self {
436        Self {
437            arena,
438            collection: None,
439            start: None,
440            end: None,
441            step: None,
442        }
443    }
444
445    /// Sets the array or string to slice.
446    pub fn collection(mut self, collection: Logic<'a>) -> Self {
447        self.collection = Some(collection);
448        self
449    }
450
451    /// Sets the array to slice using a variable reference.
452    pub fn collection_var(self, path: &str) -> Self {
453        let var = Logic::variable(path, None, self.arena);
454        self.collection(var)
455    }
456
457    /// Sets the start index.
458    pub fn start(mut self, start: Logic<'a>) -> Self {
459        self.start = Some(start);
460        self
461    }
462
463    /// Sets the start index as an integer.
464    pub fn start_int(self, value: i64) -> Self {
465        let val = Logic::literal(crate::value::DataValue::integer(value), self.arena);
466        self.start(val)
467    }
468
469    /// Sets the end index.
470    pub fn end(mut self, end: Logic<'a>) -> Self {
471        self.end = Some(end);
472        self
473    }
474
475    /// Sets the end index as an integer.
476    pub fn end_int(self, value: i64) -> Self {
477        let val = Logic::literal(crate::value::DataValue::integer(value), self.arena);
478        self.end(val)
479    }
480
481    /// Sets the step value.
482    pub fn step(mut self, step: Logic<'a>) -> Self {
483        self.step = Some(step);
484        self
485    }
486
487    /// Sets the step value as an integer.
488    pub fn step_int(self, value: i64) -> Self {
489        let val = Logic::literal(crate::value::DataValue::integer(value), self.arena);
490        self.step(val)
491    }
492
493    /// Builds the slice operation.
494    pub fn build(self) -> Logic<'a> {
495        let mut args = Vec::new();
496
497        // Add the collection (required)
498        args.push(self.collection.unwrap_or_else(|| {
499            Logic::literal(crate::value::DataValue::array(self.arena, &[]), self.arena)
500        }));
501
502        // Add optional parameters
503        if let Some(start) = self.start {
504            args.push(start);
505            if let Some(end) = self.end {
506                args.push(end);
507                if let Some(step) = self.step {
508                    args.push(step);
509                }
510            }
511        }
512
513        Logic::operator(OperatorType::Array(ArrayOp::Slice), args, self.arena)
514    }
515}
516
517/// Builder for sort operations.
518pub struct SortBuilder<'a> {
519    /// The arena in which all allocations will be made.
520    arena: &'a DataArena,
521    /// The array to sort.
522    array: Option<Logic<'a>>,
523    /// The direction (true=ascending, false=descending).
524    ascending: Option<Logic<'a>>,
525    /// The field extractor function.
526    extractor: Option<Logic<'a>>,
527}
528
529impl<'a> SortBuilder<'a> {
530    /// Creates a new sort builder.
531    pub fn new(arena: &'a DataArena) -> Self {
532        Self {
533            arena,
534            array: None,
535            ascending: None,
536            extractor: None,
537        }
538    }
539
540    /// Sets the array to sort.
541    pub fn array(mut self, array: Logic<'a>) -> Self {
542        self.array = Some(array);
543        self
544    }
545
546    /// Sets the array to sort using a variable reference.
547    pub fn array_var(self, path: &str) -> Self {
548        let var = Logic::variable(path, None, self.arena);
549        self.array(var)
550    }
551
552    /// Sets the sort direction (true=ascending, false=descending).
553    pub fn ascending(mut self, ascending: bool) -> Self {
554        let val = Logic::literal(crate::value::DataValue::bool(ascending), self.arena);
555        self.ascending = Some(val);
556        self
557    }
558
559    /// Sets the field extractor function.
560    pub fn extractor(mut self, extractor: Logic<'a>) -> Self {
561        self.extractor = Some(extractor);
562        self
563    }
564
565    /// Sets the field extractor using a variable reference.
566    pub fn extractor_var(self, path: &str) -> Self {
567        let var = Logic::variable(path, None, self.arena);
568        self.extractor(var)
569    }
570
571    /// Builds the sort operation.
572    pub fn build(self) -> Logic<'a> {
573        let mut args = Vec::new();
574
575        // Add the array (required)
576        args.push(self.array.unwrap_or_else(|| {
577            Logic::literal(crate::value::DataValue::array(self.arena, &[]), self.arena)
578        }));
579
580        // Add direction if specified
581        if let Some(ascending) = self.ascending {
582            args.push(ascending);
583            // Add extractor if specified and direction is also specified
584            if let Some(extractor) = self.extractor {
585                args.push(extractor);
586            }
587        } else if let Some(extractor) = self.extractor {
588            // If no direction specified but extractor is, add default ascending direction
589            let default_asc = Logic::literal(crate::value::DataValue::bool(true), self.arena);
590            args.push(default_asc);
591            args.push(extractor);
592        }
593
594        Logic::operator(OperatorType::Array(ArrayOp::Sort), args, self.arena)
595    }
596}