dsq_shared/ops/
special_ops.rs

1//! Advanced/specialized operations
2//!
3//! This module contains complex operations that don't fit other categories.
4
5use crate::value::Value;
6use crate::Result;
7use std::any::Any;
8
9use super::traits::{AssignmentOperator, Context, Operation};
10
11/// Type alias for built-in function implementations
12type BuiltinFunc =
13    std::sync::Arc<dyn Fn(&[crate::value::Value]) -> Result<crate::value::Value> + Send + Sync>;
14
15/// Function call operation
16pub struct FunctionCallOperation {
17    /// Function name
18    pub function_name: String,
19    /// Argument operations
20    pub arg_ops: Vec<Box<dyn Operation + Send + Sync>>,
21    /// Built-in function implementation
22    pub builtin_func: Option<BuiltinFunc>,
23}
24
25impl FunctionCallOperation {
26    /// Create a new function call operation
27    pub fn new(
28        function_name: String,
29        arg_ops: Vec<Box<dyn Operation + Send + Sync>>,
30        builtin_func: Option<BuiltinFunc>,
31    ) -> Self {
32        Self {
33            function_name,
34            arg_ops,
35            builtin_func,
36        }
37    }
38}
39
40impl Operation for FunctionCallOperation {
41    fn apply(&self, value: &Value) -> Result<Value> {
42        let mut context = None;
43        self.apply_with_context(value, &mut context)
44    }
45
46    fn apply_with_context(
47        &self,
48        value: &Value,
49        context: &mut Option<&mut dyn Context>,
50    ) -> Result<Value> {
51        if let Some(func) = &self.builtin_func {
52            // Evaluate arguments
53            let mut args = Vec::new();
54            for arg_op in &self.arg_ops {
55                let arg_value = arg_op.apply_with_context(value, context)?;
56                args.push(arg_value);
57            }
58
59            // Special handling for select: pass the current input as the first argument
60            if self.function_name == "select" && args.len() == 1 {
61                args.insert(0, value.clone());
62            }
63
64            // Call the built-in function
65            func(&args)
66        } else {
67            Err(crate::error::operation_error(format!(
68                "Unknown function '{}'",
69                self.function_name
70            )))
71        }
72    }
73
74    fn description(&self) -> String {
75        format!("call function {}", self.function_name)
76    }
77
78    fn as_any(&self) -> &dyn Any {
79        self
80    }
81}
82
83/// Delete operation
84///
85/// Removes fields or elements from objects/arrays (currently a placeholder implementation).
86pub struct DelOperation {
87    /// Operations that produce the paths to delete
88    pub path_ops: Vec<Box<dyn Operation + Send + Sync>>,
89}
90
91impl DelOperation {
92    /// Creates a new delete operation with the given path operations
93    pub fn new(path_ops: Vec<Box<dyn Operation + Send + Sync>>) -> Self {
94        Self { path_ops }
95    }
96}
97
98impl Operation for DelOperation {
99    fn apply(&self, value: &Value) -> Result<Value> {
100        let mut context = None;
101        self.apply_with_context(value, &mut context)
102    }
103
104    fn apply_with_context(
105        &self,
106        value: &Value,
107        _context: &mut Option<&mut dyn Context>,
108    ) -> Result<Value> {
109        // For now, just return the value unchanged
110        // Del operations are complex and would need to modify the input structure
111        Ok(value.clone())
112    }
113
114    fn description(&self) -> String {
115        "delete".to_string()
116    }
117
118    fn as_any(&self) -> &dyn Any {
119        self
120    }
121}
122
123/// Assignment operation
124///
125/// Assigns a value to a target location (field, variable, etc.) using the specified operator.
126pub struct AssignmentOperation {
127    /// Operations that produce the target location
128    pub target_ops: Vec<Box<dyn Operation + Send + Sync>>,
129    /// The assignment operator to use
130    pub operator: AssignmentOperator,
131    /// Operations that produce the value to assign
132    pub value_ops: Vec<Box<dyn Operation + Send + Sync>>,
133}
134
135impl AssignmentOperation {
136    /// Creates a new assignment operation with the given target, operator, and value operations
137    pub fn new(
138        target_ops: Vec<Box<dyn Operation + Send + Sync>>,
139        operator: AssignmentOperator,
140        value_ops: Vec<Box<dyn Operation + Send + Sync>>,
141    ) -> Self {
142        Self {
143            target_ops,
144            operator,
145            value_ops,
146        }
147    }
148}
149
150impl Operation for AssignmentOperation {
151    fn apply(&self, value: &Value) -> Result<Value> {
152        let mut context = None;
153        self.apply_with_context(value, &mut context)
154    }
155
156    fn apply_with_context(
157        &self,
158        value: &Value,
159        context: &mut Option<&mut dyn Context>,
160    ) -> Result<Value> {
161        // Handle field assignment on objects
162        if let Value::Object(obj) = value {
163            // Check if this is a field assignment like .field |= value or .a.b.c |= value
164            // target_ops should contain [IdentityOperation, FieldAccessOperation(fields)]
165            if self.target_ops.len() == 2 {
166                // Check if second operation is field access
167                if let Some(field_op) = self.target_ops[1]
168                    .as_any()
169                    .downcast_ref::<super::basic_ops::FieldAccessOperation>()
170                {
171                    if !field_op.fields.is_empty() {
172                        // Evaluate the value_ops to get the new value
173                        let new_value = if self.value_ops.len() == 1 {
174                            self.value_ops[0].apply_with_context(value, context)?
175                        } else {
176                            Value::Null
177                        };
178
179                        // Helper function to update nested field
180                        fn update_nested(
181                            obj: &std::collections::HashMap<String, Value>,
182                            fields: &[String],
183                            new_value: Value,
184                            operator: &AssignmentOperator,
185                        ) -> Value {
186                            if fields.is_empty() {
187                                return Value::Object(obj.clone());
188                            }
189
190                            let field_name = &fields[0];
191                            let mut new_obj = obj.clone();
192
193                            if fields.len() == 1 {
194                                // Last field - apply the assignment
195                                let current_value =
196                                    obj.get(field_name).cloned().unwrap_or(Value::Null);
197                                let final_value = match operator {
198                                    AssignmentOperator::AddAssign => {
199                                        match (&current_value, &new_value) {
200                                            (Value::Int(a), Value::Int(b)) => Value::Int(a + b),
201                                            (Value::Float(a), Value::Float(b)) => {
202                                                Value::Float(a + b)
203                                            }
204                                            (Value::String(a), Value::String(b)) => {
205                                                Value::String(format!("{}{}", a, b))
206                                            }
207                                            _ => new_value,
208                                        }
209                                    }
210                                    AssignmentOperator::UpdateAssign => new_value,
211                                };
212                                new_obj.insert(field_name.clone(), final_value);
213                            } else {
214                                // Not the last field - recurse into nested object
215                                if let Some(Value::Object(nested)) = obj.get(field_name) {
216                                    let updated =
217                                        update_nested(nested, &fields[1..], new_value, operator);
218                                    new_obj.insert(field_name.clone(), updated);
219                                } else {
220                                    // Path doesn't exist or not an object, create nested structure
221                                    let empty = std::collections::HashMap::new();
222                                    let updated =
223                                        update_nested(&empty, &fields[1..], new_value, operator);
224                                    new_obj.insert(field_name.clone(), updated);
225                                }
226                            }
227                            Value::Object(new_obj)
228                        }
229
230                        return Ok(update_nested(
231                            obj,
232                            &field_op.fields,
233                            new_value,
234                            &self.operator,
235                        ));
236                    }
237                }
238            }
239        }
240
241        // For non-object values or complex paths, return the evaluated value
242        // This handles cases where assignment doesn't make sense
243        if self.value_ops.len() == 1 {
244            self.value_ops[0].apply_with_context(value, context)
245        } else {
246            Ok(value.clone())
247        }
248    }
249
250    fn description(&self) -> String {
251        "assignment".to_string()
252    }
253
254    fn as_any(&self) -> &dyn Any {
255        self
256    }
257}
258
259/// Join from file operation
260///
261/// Performs a join operation by loading data from a file and joining on specified keys.
262pub struct JoinFromFileOperation {
263    /// Path to the file to join with
264    pub file_path: String,
265    /// Key field name in the left (current) data
266    pub left_key: String,
267    /// Key field name in the right (file) data
268    pub right_key: String,
269}
270
271impl JoinFromFileOperation {
272    /// Creates a new join from file operation with the given file path and key fields
273    pub fn new(file_path: String, left_key: String, right_key: String) -> Self {
274        Self {
275            file_path,
276            left_key,
277            right_key,
278        }
279    }
280}
281
282impl Operation for JoinFromFileOperation {
283    fn apply(&self, value: &Value) -> Result<Value> {
284        let mut context = None;
285        self.apply_with_context(value, &mut context)
286    }
287
288    fn apply_with_context(
289        &self,
290        value: &Value,
291        _context: &mut Option<&mut dyn Context>,
292    ) -> Result<Value> {
293        // This should be implemented for actual join functionality
294        // For now, return the value unchanged
295        Ok(value.clone())
296    }
297
298    fn description(&self) -> String {
299        "join from file".to_string()
300    }
301
302    fn as_any(&self) -> &dyn Any {
303        self
304    }
305}