polyvalue/operations.rs
1//! # Operations
2//! The operations module contains all the operations that can be performed on a value.
3//! Boolean operations can be performed on any value, by converting it to a boolean first.
4//! Bitwise operations can be performed on any value, which can be converted to an integer.
5//! Arithmetic operations can be performed on numeric values, and on strings (for some ops)
6//! Indexing operations can be performed on arrays, objects, strings, and ranges
7//! Mutable indexing operations can be performed on arrays and objects
8use crate::Value;
9
10/// Available arithmetic operations
11#[derive(Debug, Clone, Copy, PartialEq)]
12pub enum ArithmeticOperation {
13 /// Add two values, or concatenate two strings
14 Add,
15
16 /// Subtract two values, or remove a substring from a string
17 Subtract,
18
19 /// Multiply two values
20 Multiply,
21
22 /// Divide two values
23 Divide,
24
25 /// Modulo two values
26 Modulo,
27
28 /// Exponentiate two values
29 Exponentiate,
30}
31
32impl std::fmt::Display for ArithmeticOperation {
33 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34 match self {
35 ArithmeticOperation::Add => write!(f, "add"),
36 ArithmeticOperation::Subtract => write!(f, "sub"),
37 ArithmeticOperation::Multiply => write!(f, "mul"),
38 ArithmeticOperation::Divide => write!(f, "div"),
39 ArithmeticOperation::Modulo => write!(f, "mod"),
40 ArithmeticOperation::Exponentiate => write!(f, "exp"),
41 }
42 }
43}
44
45/// Trait for arithmetic operations
46pub trait ArithmeticOperationExt {
47 /// Perform an arithmetic operation on two values
48 /// If the operation is not supported on the given type,
49 /// an `Error::UnsupportedOperation` will be returned
50 ///
51 /// # Examples
52 /// ```
53 /// use polyvalue::{Value};
54 /// use polyvalue::operations::{ArithmeticOperation, ArithmeticOperationExt};
55 ///
56 /// let a = Value::from(1);
57 /// let b = Value::from(2);
58 ///
59 /// let result = a.arithmetic_op(b, ArithmeticOperation::Add).unwrap();
60 /// assert_eq!(result, Value::from(3));
61 /// ```
62 fn arithmetic_op(
63 self,
64 right: Self,
65 operation: ArithmeticOperation,
66 ) -> Result<Self, crate::Error>
67 where
68 Self: Sized;
69
70 /// Perform an arithmetic negation on a value
71 /// This is equivalent to `arithmetic_op with ArithmeticOperation::Negate`
72 /// but is provided for convenience
73 ///
74 /// # Examples
75 /// ```
76 /// use polyvalue::{Value};
77 /// use polyvalue::operations::{ArithmeticOperation, ArithmeticOperationExt};
78 ///
79 /// let a = Value::from(1);
80 /// let result = a.arithmetic_neg().unwrap();
81 /// assert_eq!(result, Value::from(-1));
82 /// ```
83 fn arithmetic_neg(self) -> Result<Self, crate::Error>
84 where
85 Self: Sized;
86}
87
88/// Available bitwise operations
89#[derive(Debug, Clone, Copy, PartialEq)]
90pub enum BitwiseOperation {
91 /// Perform a bitwise and
92 And,
93
94 /// Perform a bitwise or
95 Or,
96
97 /// Perform a bitwise xor
98 Xor,
99
100 /// Perform a left shift
101 LeftShift,
102
103 /// Perform a right shift
104 RightShift,
105}
106
107impl std::fmt::Display for BitwiseOperation {
108 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109 match self {
110 BitwiseOperation::And => write!(f, "and"),
111 BitwiseOperation::Or => write!(f, "or"),
112 BitwiseOperation::Xor => write!(f, "xor"),
113 BitwiseOperation::LeftShift => write!(f, "lshift"),
114 BitwiseOperation::RightShift => write!(f, "rshift"),
115 }
116 }
117}
118
119/// Trait for bitwise operations
120pub trait BitwiseOperationExt {
121 /// Perform a bitwise operation on two values
122 /// If the operation is not supported on the given type,
123 /// an `Error::UnsupportedOperation` will be returned
124 ///
125 /// # Examples
126 /// ```
127 /// use polyvalue::{Value};
128 /// use polyvalue::operations::{BitwiseOperation, BitwiseOperationExt};
129 ///
130 /// let a = Value::from(0x0F);
131 /// let b = Value::from(0xF0);
132 ///
133 /// let result = a.bitwise_op(b, BitwiseOperation::And).unwrap();
134 /// assert_eq!(result, Value::from(0x00));
135 /// ```
136 fn bitwise_op(self, right: Self, operation: BitwiseOperation) -> Result<Self, crate::Error>
137 where
138 Self: Sized;
139
140 /// Perform a bitwise not on a value
141 /// This is equivalent to `bitwise_op with BitwiseOperation::Not`
142 /// but is provided for convenience
143 ///
144 /// Please note that a mask is applied to the result of this operation
145 /// in order to ensure that the result is the same size as the input
146 /// and correct for the way the underlying data is stored
147 ///
148 /// # Examples
149 /// ```
150 /// use polyvalue::{Value};
151 /// use polyvalue::operations::{BitwiseOperation, BitwiseOperationExt};
152 ///
153 /// let a = Value::u8(0xFF);
154 ///
155 /// let result = a.bitwise_not().unwrap();
156 /// assert_eq!(result, Value::u8(0x00));
157 fn bitwise_not(self) -> Result<Self, crate::Error>
158 where
159 Self: Sized;
160}
161
162/// Available boolean operations
163#[derive(Debug, Clone, Copy, PartialEq)]
164pub enum BooleanOperation {
165 /// Perform a boolean and
166 And,
167
168 /// Perform a boolean or
169 Or,
170
171 /// Perform a less than comparison
172 LT,
173
174 /// Perform a greater than comparison
175 GT,
176
177 /// Perform a less than or equal to comparison
178 LTE,
179
180 /// Perform a greater than or equal to comparison
181 GTE,
182
183 /// Perform an equal to comparison
184 EQ,
185
186 /// Perform a not equal to comparison
187 NEQ,
188
189 /// Perform an equal to comparison, also checking type
190 StrictEQ,
191
192 /// Perform a not equal to comparison, also checking type
193 StrictNEQ,
194}
195impl std::fmt::Display for BooleanOperation {
196 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
197 match self {
198 BooleanOperation::And => write!(f, "and"),
199 BooleanOperation::Or => write!(f, "or"),
200 BooleanOperation::LT => write!(f, "lt"),
201 BooleanOperation::GT => write!(f, "gt"),
202 BooleanOperation::LTE => write!(f, "lte"),
203 BooleanOperation::GTE => write!(f, "gte"),
204 BooleanOperation::EQ => write!(f, "eq"),
205 BooleanOperation::NEQ => write!(f, "neq"),
206 BooleanOperation::StrictEQ => write!(f, "strict-eq"),
207 BooleanOperation::StrictNEQ => write!(f, "strict-neq"),
208 }
209 }
210}
211
212/// Trait for boolean operations
213pub trait BooleanOperationExt {
214 /// Perform a boolean operation on two values
215 /// If the operation is not supported on the given type,
216 /// an `Error::UnsupportedOperation` will be returned
217 ///
218 /// This type of operation will always return a boolean value
219 ///
220 /// # Examples
221 /// ```
222 /// use polyvalue::{Value};
223 /// use polyvalue::operations::{BooleanOperation, BooleanOperationExt};
224 ///
225 /// let a = Value::from(1);
226 /// let b = Value::from(2);
227 ///
228 /// let result = a.boolean_op(b, BooleanOperation::LT).unwrap();
229 /// assert_eq!(result, Value::from(true));
230 fn boolean_op(self, right: Self, operation: BooleanOperation) -> Result<Value, crate::Error>
231 where
232 Self: Sized;
233
234 /// Perform a boolean not on a value
235 /// This is equivalent to `boolean_op with BooleanOperation::Not`
236 /// but is provided for convenience
237 ///
238 /// # Examples
239 /// ```
240 /// use polyvalue::{Value};
241 /// use polyvalue::operations::{BooleanOperation, BooleanOperationExt};
242 ///
243 /// let a = Value::from(true);
244 /// let result = a.boolean_not().unwrap();
245 /// assert_eq!(result, Value::from(false));
246 /// ```
247 fn boolean_not(self) -> Result<Value, crate::Error>
248 where
249 Self: Sized;
250}
251
252/// A trait for indexing operations that can mutate the value
253pub trait IndexingMutationExt {
254 /// Get a value from an index
255 /// Returns a mutable reference to the value, or an `Error::Index` if the index is not found
256 ///
257 /// # Examples
258 /// ```
259 /// use polyvalue::{Value};
260 /// use polyvalue::types::{Object};
261 /// use polyvalue::operations::{IndexingOperationExt, IndexingMutationExt};
262 ///
263 /// let mut b = Object::try_from(vec![("a", 1), ("b", 2)]).unwrap();
264 /// let index = Value::from("b");
265 /// let result = b.get_index_mut(&index).unwrap();
266 /// *result = Value::from(3);
267 ///
268 /// assert_eq!(b.get_index(&index).unwrap(), Value::from(3));
269 /// ```
270 fn get_index_mut(&mut self, index: &Value) -> Result<&mut Value, crate::Error>;
271
272 /// Get values from one or more indices, mutably
273 /// Returns a vector of references to the values, or an `Error::Index` if any of the indices are not found
274 ///
275 /// Acts as a convenience wrapper around `get_index` where array values are treated as a set of indices
276 fn get_indices_mut(&mut self, index: &Value) -> Result<Vec<&mut Value>, crate::Error>;
277
278 /// Set a value at an index
279 /// Returns an `Error::Index` if the index is not found
280 ///
281 /// # Examples
282 /// ```
283 /// use polyvalue::{Value};
284 /// use polyvalue::operations::{IndexingOperationExt, IndexingMutationExt};
285 ///
286 /// let mut a = Value::from(vec![Value::from(1), Value::from(2), Value::from(3)]);
287 /// let index = Value::from(1);
288 /// a.set_index(&index, Value::from(4)).unwrap();
289 /// assert_eq!(a.get_index(&index).unwrap(), Value::from(4));
290 /// ```
291 fn set_index(&mut self, index: &Value, value: Value) -> Result<(), crate::Error>;
292
293 /// Insert a value at an index
294 /// Returns an `Error::Index` if the index is out of bounds
295 ///
296 /// # Examples
297 /// ```
298 /// use polyvalue::{Value};
299 /// use polyvalue::operations::{IndexingOperationExt, IndexingMutationExt};
300 ///
301 /// let mut a = Value::from(vec![Value::from(1), Value::from(2), Value::from(3)]);
302 /// let index = Value::from(1);
303 /// a.insert_at(&index, Value::from(4)).unwrap();
304 /// assert_eq!(a.get_index(&index).unwrap(), Value::from(4));
305 /// ```
306 fn insert_at(&mut self, index: &Value, value: Value) -> Result<(), crate::Error>;
307
308 /// Delete a value at an index
309 /// Returns an `Error::Index` if the index is not found
310 ///
311 /// # Examples
312 /// ```
313 /// use polyvalue::{Value};
314 /// use polyvalue::operations::{IndexingOperationExt, IndexingMutationExt};
315 ///
316 /// let mut a = Value::from(vec![Value::from(1), Value::from(2), Value::from(3)]);
317 /// let index = Value::from(1);
318 /// a.delete_index(&index).unwrap();
319 /// ```
320 fn delete_index(&mut self, index: &Value) -> Result<Value, crate::Error>;
321}
322
323/// Indexing operation trait
324pub trait IndexingOperationExt {
325 /// Get a value from an index
326 /// Returns a value, or an `Error::Index` if the index is not found
327 ///
328 /// # Examples
329 /// ```
330 /// use polyvalue::{Value};
331 /// use polyvalue::types::{Object};
332 /// use polyvalue::operations::{IndexingOperationExt};
333 ///
334 /// let a = Value::from(vec![Value::from(1), Value::from(2), Value::from(3)]);
335 /// let index = Value::from(1);
336 /// let result = a.get_index(&index).unwrap();
337 /// assert_eq!(result, Value::from(2));
338 ///
339 /// let b = Object::try_from(vec![("a", 1), ("b", 2)]).unwrap();
340 /// let index = Value::from("b");
341 /// let result = b.get_index(&index).unwrap();
342 /// assert_eq!(result, Value::from(2));
343 /// ```
344 fn get_index(&self, index: &Value) -> Result<Value, crate::Error>;
345
346 /// Get values from one or more indices
347 /// Returns a vector of values, or an `Error::Index` if any of the indices are not found
348 ///
349 /// Acts as a convenience wrapper around `get_index` where array values are treated as a set of indices
350 fn get_indices(&self, index: &Value) -> Result<Value, crate::Error>;
351}
352
353/// Matching operations
354/// These operations are used to compare two values and return a boolean result
355#[derive(Debug, Clone, Copy, PartialEq)]
356pub enum MatchingOperation {
357 /// Matches array or string contents
358 Contains,
359
360 /// Matches a string, array or regular expression
361 Matches,
362
363 /// Type-specific matching operations
364 Is,
365
366 /// This is a special case of `Matches` that matches the start of the target
367 StartsWith,
368
369 /// This is a special case of `Matches` that matches the end of the target
370 EndsWith,
371}
372
373impl std::fmt::Display for MatchingOperation {
374 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
375 match self {
376 MatchingOperation::Contains => write!(f, "contains"),
377 MatchingOperation::Matches => write!(f, "matches"),
378 MatchingOperation::Is => write!(f, "is"),
379 MatchingOperation::StartsWith => write!(f, "startswith"),
380 MatchingOperation::EndsWith => write!(f, "endswith"),
381 }
382 }
383}
384
385/// Trait for matching operations
386pub trait MatchingOperationExt {
387 /// Perform a matching operation on two values
388 /// If the operation is not supported on the given type,
389 /// an `Error::UnsupportedOperation` will be returned
390 ///
391 /// # Examples
392 /// ```
393 /// use polyvalue::{Value};
394 /// use polyvalue::operations::{MatchingOperation, MatchingOperationExt};
395 ///
396 /// let a = Value::from("Hello, world!");
397 /// let b = Value::from("world");
398 ///
399 /// let result = Value::matching_op(&a, &b, MatchingOperation::Contains).unwrap();
400 /// assert_eq!(result, Value::from(true));
401 /// ```
402 fn matching_op(
403 container: &Self,
404 pattern: &Value,
405 operation: MatchingOperation,
406 ) -> Result<Value, crate::Error>
407 where
408 Self: Sized;
409}
410
411#[cfg(test)]
412mod test {
413 use super::*;
414
415 #[test]
416 fn test_bitwise_display() {
417 assert_eq!(BitwiseOperation::And.to_string(), "and");
418 assert_eq!(BitwiseOperation::Or.to_string(), "or");
419 assert_eq!(BitwiseOperation::Xor.to_string(), "xor");
420 assert_eq!(BitwiseOperation::LeftShift.to_string(), "lshift");
421 assert_eq!(BitwiseOperation::RightShift.to_string(), "rshift");
422 }
423
424 #[test]
425 fn test_boolean_display() {
426 assert_eq!(BooleanOperation::And.to_string(), "and");
427 assert_eq!(BooleanOperation::Or.to_string(), "or");
428 assert_eq!(BooleanOperation::LT.to_string(), "lt");
429 assert_eq!(BooleanOperation::GT.to_string(), "gt");
430 assert_eq!(BooleanOperation::LTE.to_string(), "lte");
431 assert_eq!(BooleanOperation::GTE.to_string(), "gte");
432 assert_eq!(BooleanOperation::EQ.to_string(), "eq");
433 assert_eq!(BooleanOperation::NEQ.to_string(), "neq");
434 }
435
436 #[test]
437 fn test_arithmetic_display() {
438 assert_eq!(ArithmeticOperation::Add.to_string(), "add");
439 assert_eq!(ArithmeticOperation::Subtract.to_string(), "sub");
440 assert_eq!(ArithmeticOperation::Multiply.to_string(), "mul");
441 assert_eq!(ArithmeticOperation::Divide.to_string(), "div");
442 assert_eq!(ArithmeticOperation::Modulo.to_string(), "mod");
443 assert_eq!(ArithmeticOperation::Exponentiate.to_string(), "exp");
444 }
445
446 #[test]
447 fn test_matching_display() {
448 assert_eq!(MatchingOperation::Contains.to_string(), "contains");
449 assert_eq!(MatchingOperation::Matches.to_string(), "matches");
450 assert_eq!(MatchingOperation::Is.to_string(), "is");
451 assert_eq!(MatchingOperation::StartsWith.to_string(), "startswith");
452 assert_eq!(MatchingOperation::EndsWith.to_string(), "endswith");
453 }
454}