dsq_core/ops/
access_ops.rs1use crate::error::Result;
2use crate::Value;
3
4use super::Operation;
5
6pub 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
19pub 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
52pub 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(¤t)?;
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
82pub 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
168pub 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 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}