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}