dsq_core/ops/
access_ops.rs

1use crate::error::Result;
2use crate::Value;
3
4use super::Operation;
5
6/// Identity operation - returns input unchanged
7pub struct IdentityOperation;
8
9impl Operation for IdentityOperation {
10    fn apply(&self, value: &Value) -> Result<Value> {
11        Ok(value.clone())
12    }
13
14    fn description(&self) -> String {
15        "identity".to_string()
16    }
17}
18
19/// Field access operation
20pub struct FieldAccessOperation {
21    pub fields: Vec<String>,
22}
23
24impl FieldAccessOperation {
25    #[must_use]
26    pub fn new(field: String) -> Self {
27        Self {
28            fields: vec![field],
29        }
30    }
31
32    #[must_use]
33    pub fn with_fields(fields: Vec<String>) -> Self {
34        Self { fields }
35    }
36}
37
38impl Operation for FieldAccessOperation {
39    fn apply(&self, value: &Value) -> Result<Value> {
40        let mut current = value.clone();
41        for field in &self.fields {
42            current = current.field(field)?;
43        }
44        Ok(current)
45    }
46
47    fn description(&self) -> String {
48        format!("field access: {}", self.fields.join("."))
49    }
50}
51
52/// Array indexing operation
53pub struct IndexOperation {
54    pub index_ops: Vec<Box<dyn Operation + Send + Sync>>,
55}
56
57impl IndexOperation {
58    #[must_use]
59    pub fn new(index_ops: Vec<Box<dyn Operation + Send + Sync>>) -> Self {
60        Self { index_ops }
61    }
62}
63
64impl Operation for IndexOperation {
65    fn apply(&self, value: &Value) -> Result<Value> {
66        let mut current = value.clone();
67        for op in &self.index_ops {
68            let index_value = op.apply(&current)?;
69            match index_value {
70                Value::Int(idx) => current = current.index(idx)?,
71                _ => return Err(crate::error::Error::operation("Index must be an integer")),
72            }
73        }
74        Ok(current)
75    }
76
77    fn description(&self) -> String {
78        "array index".to_string()
79    }
80}
81
82/// Array slicing operation
83pub struct SliceOperation {
84    pub start_ops: Option<Vec<Box<dyn Operation + Send + Sync>>>,
85    pub end_ops: Option<Vec<Box<dyn Operation + Send + Sync>>>,
86}
87
88impl SliceOperation {
89    #[must_use]
90    pub fn new(
91        start_ops: Option<Vec<Box<dyn Operation + Send + Sync>>>,
92        end_ops: Option<Vec<Box<dyn Operation + Send + Sync>>>,
93    ) -> Self {
94        Self { start_ops, end_ops }
95    }
96}
97
98impl Operation for SliceOperation {
99    fn apply(&self, value: &Value) -> Result<Value> {
100        let start = if let Some(ref ops) = self.start_ops {
101            let mut start_val = value.clone();
102            for op in ops {
103                start_val = op.apply(&start_val)?;
104            }
105            match start_val {
106                Value::Int(i) => Some(usize::try_from(i).map_err(|_| {
107                    crate::error::Error::operation("Slice start index out of range")
108                })?),
109                _ => {
110                    return Err(crate::error::Error::operation(
111                        "Slice start must be an integer",
112                    ));
113                }
114            }
115        } else {
116            None
117        };
118
119        let end =
120            if let Some(ref ops) = self.end_ops {
121                let mut end_val = value.clone();
122                for op in ops {
123                    end_val = op.apply(&end_val)?;
124                }
125                match end_val {
126                    Value::Int(i) => Some(usize::try_from(i).map_err(|_| {
127                        crate::error::Error::operation("Slice end index out of range")
128                    })?),
129                    _ => {
130                        return Err(crate::error::Error::operation(
131                            "Slice end must be an integer",
132                        ));
133                    }
134                }
135            } else {
136                None
137            };
138
139        match value {
140            Value::Array(arr) => {
141                let start_idx = start.unwrap_or(0);
142                let end_idx = end.unwrap_or(arr.len());
143                Ok(Value::Array(arr[start_idx..end_idx].to_vec()))
144            }
145            Value::DataFrame(df) => {
146                let start_idx = i64::try_from(start.unwrap_or(0)).map_err(|_| {
147                    crate::error::Error::operation("Start index out of range for i64")
148                })?;
149                let end_idx = end.unwrap_or(df.height());
150                let end_idx_i64 = i64::try_from(end_idx).map_err(|_| {
151                    crate::error::Error::operation("End index out of range for i64")
152                })?;
153                let length = usize::try_from(end_idx_i64 - start_idx)
154                    .map_err(|_| crate::error::Error::operation("Slice length out of range"))?;
155                Ok(Value::DataFrame(df.slice(start_idx, length)))
156            }
157            _ => Err(crate::error::Error::operation(
158                "Slice operation requires array or DataFrame",
159            )),
160        }
161    }
162
163    fn description(&self) -> String {
164        "array slice".to_string()
165    }
166}
167
168/// Array iteration operation (.[])
169pub struct IterateOperation;
170
171impl Operation for IterateOperation {
172    fn apply(&self, value: &Value) -> Result<Value> {
173        match value {
174            Value::Array(arr) => Ok(Value::Array(arr.clone())),
175            Value::Object(obj) => {
176                let values: Vec<Value> = obj.values().cloned().collect();
177                Ok(Value::Array(values))
178            }
179            Value::DataFrame(df) => {
180                // Convert DataFrame to array of objects
181                let mut rows = Vec::new();
182                for i in 0..df.height() {
183                    let mut row_obj = std::collections::HashMap::new();
184                    for col_name in df.get_column_names() {
185                        if let Ok(series) = df.column(col_name) {
186                            if let Ok(val) = series.get(i) {
187                                let value = match val {
188                                    polars::prelude::AnyValue::Int64(i) => Value::Int(i),
189                                    polars::prelude::AnyValue::Float64(f) => Value::Float(f),
190                                    polars::prelude::AnyValue::String(s) => {
191                                        Value::String(s.to_string())
192                                    }
193                                    polars::prelude::AnyValue::Boolean(b) => Value::Bool(b),
194                                    _ => Value::Null,
195                                };
196                                row_obj.insert(col_name.to_string(), value);
197                            }
198                        }
199                    }
200                    rows.push(Value::Object(row_obj));
201                }
202                Ok(Value::Array(rows))
203            }
204            _ => Ok(value.clone()),
205        }
206    }
207
208    fn description(&self) -> String {
209        "iterate array/object".to_string()
210    }
211}