datalogic_rs/builder/
string_builder.rs

1use crate::arena::DataArena;
2use crate::logic::StringOp;
3use crate::logic::{Logic, OperatorType};
4
5/// Builder for string operations.
6///
7/// This builder provides a fluent interface for creating string operations
8/// such as concatenation, substring, etc.
9pub struct StringBuilder<'a> {
10    /// The arena in which all allocations will be made.
11    arena: &'a DataArena,
12}
13
14impl<'a> StringBuilder<'a> {
15    /// Creates a new string builder.
16    pub fn new(arena: &'a DataArena) -> Self {
17        Self { arena }
18    }
19
20    /// Creates a concatenation operation.
21    pub fn concat_op(&self) -> StringOperationBuilder<'a> {
22        StringOperationBuilder::new(self.arena, StringOp::Cat)
23    }
24
25    /// Creates a substring operation.
26    pub fn substr_op(&self) -> SubstringBuilder<'a> {
27        SubstringBuilder::new(self.arena)
28    }
29
30    /// Creates a "starts with" operation.
31    pub fn starts_with_op(&self) -> StringOperationBuilder<'a> {
32        StringOperationBuilder::new(self.arena, StringOp::StartsWith)
33    }
34
35    /// Creates an "ends with" operation.
36    pub fn ends_with_op(&self) -> StringOperationBuilder<'a> {
37        StringOperationBuilder::new(self.arena, StringOp::EndsWith)
38    }
39
40    /// Creates an "upper case" operation.
41    pub fn upper_op(&self) -> StringOperationBuilder<'a> {
42        StringOperationBuilder::new(self.arena, StringOp::Upper)
43    }
44
45    /// Creates a "lower case" operation.
46    pub fn lower_op(&self) -> StringOperationBuilder<'a> {
47        StringOperationBuilder::new(self.arena, StringOp::Lower)
48    }
49
50    /// Creates a "trim" operation.
51    pub fn trim_op(&self) -> StringOperationBuilder<'a> {
52        StringOperationBuilder::new(self.arena, StringOp::Trim)
53    }
54}
55
56/// Builder for string operations with multiple operands.
57pub struct StringOperationBuilder<'a> {
58    /// The arena in which all allocations will be made.
59    arena: &'a DataArena,
60    /// The string operation to use.
61    operation: StringOp,
62    /// The operands collected so far.
63    operands: Vec<Logic<'a>>,
64}
65
66impl<'a> StringOperationBuilder<'a> {
67    /// Creates a new string operation builder.
68    pub fn new(arena: &'a DataArena, operation: StringOp) -> Self {
69        Self {
70            arena,
71            operation,
72            operands: Vec::new(),
73        }
74    }
75
76    /// Adds an operand to the string operation.
77    pub fn operand(mut self, operand: Logic<'a>) -> Self {
78        self.operands.push(operand);
79        self
80    }
81
82    /// Adds a variable as an operand to the string operation.
83    pub fn var(mut self, path: &str) -> Self {
84        let var = Logic::variable(path, None, self.arena);
85        self.operands.push(var);
86        self
87    }
88
89    /// Adds a literal string value as an operand to the string operation.
90    pub fn string(mut self, value: &str) -> Self {
91        let val = Logic::literal(
92            crate::value::DataValue::string(self.arena, value),
93            self.arena,
94        );
95        self.operands.push(val);
96        self
97    }
98
99    /// Adds an integer as an operand to the string operation.
100    pub fn int(mut self, value: i64) -> Self {
101        let val = Logic::literal(crate::value::DataValue::integer(value), self.arena);
102        self.operands.push(val);
103        self
104    }
105
106    /// Adds a float as an operand to the string operation.
107    pub fn float(mut self, value: f64) -> Self {
108        let val = Logic::literal(crate::value::DataValue::float(value), self.arena);
109        self.operands.push(val);
110        self
111    }
112
113    /// Adds a boolean as an operand to the string operation.
114    pub fn bool(mut self, value: bool) -> Self {
115        let val = Logic::literal(crate::value::DataValue::bool(value), self.arena);
116        self.operands.push(val);
117        self
118    }
119
120    /// Builds the string operation with the collected operands.
121    pub fn build(self) -> Logic<'a> {
122        if self.operands.is_empty() {
123            // Default for string operations is an empty string
124            return Logic::literal(crate::value::DataValue::string(self.arena, ""), self.arena);
125        }
126
127        Logic::operator(
128            OperatorType::String(self.operation),
129            self.operands,
130            self.arena,
131        )
132    }
133}
134
135/// Builder for substring operations.
136pub struct SubstringBuilder<'a> {
137    /// The arena in which all allocations will be made.
138    arena: &'a DataArena,
139    /// The string to extract from.
140    string: Option<Logic<'a>>,
141    /// The start index.
142    start: Option<Logic<'a>>,
143    /// The length.
144    length: Option<Logic<'a>>,
145}
146
147impl<'a> SubstringBuilder<'a> {
148    /// Creates a new substring builder.
149    pub fn new(arena: &'a DataArena) -> Self {
150        Self {
151            arena,
152            string: None,
153            start: None,
154            length: None,
155        }
156    }
157
158    /// Sets the string to extract from.
159    pub fn string(mut self, string: Logic<'a>) -> Self {
160        self.string = Some(string);
161        self
162    }
163
164    /// Sets the string to extract from using a variable reference.
165    pub fn var(self, path: &str) -> Self {
166        let var = Logic::variable(path, None, self.arena);
167        self.string(var)
168    }
169
170    /// Sets the string to extract from using a literal string.
171    pub fn literal(self, value: &str) -> Self {
172        let val = Logic::literal(
173            crate::value::DataValue::string(self.arena, value),
174            self.arena,
175        );
176        self.string(val)
177    }
178
179    /// Sets the start index.
180    pub fn start(mut self, start: Logic<'a>) -> Self {
181        self.start = Some(start);
182        self
183    }
184
185    /// Sets the start index using an integer literal.
186    pub fn start_at(self, index: i64) -> Self {
187        let val = Logic::literal(crate::value::DataValue::integer(index), self.arena);
188        self.start(val)
189    }
190
191    /// Sets the length.
192    pub fn length(mut self, length: Logic<'a>) -> Self {
193        self.length = Some(length);
194        self
195    }
196
197    /// Sets the length using an integer literal.
198    pub fn take(self, length: i64) -> Self {
199        let val = Logic::literal(crate::value::DataValue::integer(length), self.arena);
200        self.length(val)
201    }
202
203    /// Builds the substring operation.
204    ///
205    /// If string is not set, it will use an empty string.
206    /// If start is not set, it will use 0.
207    /// If length is not set, it will extract to the end of the string.
208    pub fn build(self) -> Logic<'a> {
209        let string = self.string.unwrap_or_else(|| {
210            Logic::literal(crate::value::DataValue::string(self.arena, ""), self.arena)
211        });
212
213        let start = self
214            .start
215            .unwrap_or_else(|| Logic::literal(crate::value::DataValue::integer(0), self.arena));
216
217        let mut operands = vec![string, start];
218
219        if let Some(length) = self.length {
220            operands.push(length);
221        }
222
223        Logic::operator(OperatorType::String(StringOp::Substr), operands, self.arena)
224    }
225}