1use crate::error::Result;
7use crate::types::{Document, Value};
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
13pub enum ComputedExpression {
14 Concat(Vec<String>),
16 Sum(Vec<String>),
18 Product(Vec<String>),
20 Average(Vec<String>),
22 Custom(String),
24}
25
26impl ComputedExpression {
27 pub fn evaluate(&self, doc: &Document) -> Option<Value> {
29 match self {
30 ComputedExpression::Concat(fields) => {
31 let mut result = String::new();
32 for field in fields {
33 if let Some(value) = doc.data.get(field)
34 && let Some(s) = value.as_str() {
35 if !result.is_empty() {
36 result.push(' ');
37 }
38 result.push_str(s);
39 }
40 }
41 Some(Value::String(result))
42 }
43
44 ComputedExpression::Sum(fields) => {
45 let mut sum = 0i64;
46 for field in fields {
47 if let Some(value) = doc.data.get(field)
48 && let Some(i) = value.as_i64() {
49 sum += i;
50 }
51 }
52 Some(Value::Int(sum))
53 }
54
55 ComputedExpression::Product(fields) => {
56 let mut product = 1i64;
57 for field in fields {
58 if let Some(value) = doc.data.get(field)
59 && let Some(i) = value.as_i64() {
60 product *= i;
61 }
62 }
63 Some(Value::Int(product))
64 }
65
66 ComputedExpression::Average(fields) => {
67 let mut sum = 0.0;
68 let mut count = 0;
69 for field in fields {
70 if let Some(value) = doc.data.get(field) {
71 if let Some(f) = value.as_f64() {
72 sum += f;
73 count += 1;
74 } else if let Some(i) = value.as_i64() {
75 sum += i as f64;
76 count += 1;
77 }
78 }
79 }
80 if count > 0 {
81 Some(Value::Float(sum / count as f64))
82 } else {
83 None
84 }
85 }
86
87 ComputedExpression::Custom(_expr) => {
88 None
91 }
92 }
93 }
94}
95
96pub struct ComputedFields {
98 fields: HashMap<String, HashMap<String, ComputedExpression>>,
100}
101
102impl ComputedFields {
103 pub fn new() -> Self {
104 Self {
105 fields: HashMap::new(),
106 }
107 }
108
109 pub fn register(
111 &mut self,
112 collection: impl Into<String>,
113 field: impl Into<String>,
114 expression: ComputedExpression,
115 ) {
116 let collection = collection.into();
117 self.fields
118 .entry(collection)
119 .or_default()
120 .insert(field.into(), expression);
121 }
122
123 pub fn apply(&self, collection: &str, doc: &mut Document) -> Result<()> {
125 if let Some(computed) = self.fields.get(collection) {
126 for (field_name, expression) in computed {
127 if let Some(value) = expression.evaluate(doc) {
128 doc.data.insert(field_name.clone(), value);
129 }
130 }
131 }
132 Ok(())
133 }
134
135 pub fn get_fields(&self, collection: &str) -> Option<&HashMap<String, ComputedExpression>> {
137 self.fields.get(collection)
138 }
139}
140
141impl Default for ComputedFields {
142 fn default() -> Self {
143 Self::new()
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150
151 #[test]
152 fn test_concat_expression() {
153 let expr =
154 ComputedExpression::Concat(vec!["first_name".to_string(), "last_name".to_string()]);
155
156 let mut doc = Document::new();
157 doc.data
158 .insert("first_name".to_string(), Value::String("John".to_string()));
159 doc.data
160 .insert("last_name".to_string(), Value::String("Doe".to_string()));
161
162 let result = expr.evaluate(&doc);
163 assert_eq!(result, Some(Value::String("John Doe".to_string())));
164 }
165
166 #[test]
167 fn test_sum_expression() {
168 let expr = ComputedExpression::Sum(vec!["a".to_string(), "b".to_string(), "c".to_string()]);
169
170 let mut doc = Document::new();
171 doc.data.insert("a".to_string(), Value::Int(10));
172 doc.data.insert("b".to_string(), Value::Int(20));
173 doc.data.insert("c".to_string(), Value::Int(30));
174
175 let result = expr.evaluate(&doc);
176 assert_eq!(result, Some(Value::Int(60)));
177 }
178
179 #[test]
180 fn test_average_expression() {
181 let expr = ComputedExpression::Average(vec!["score1".to_string(), "score2".to_string()]);
182
183 let mut doc = Document::new();
184 doc.data.insert("score1".to_string(), Value::Float(85.5));
185 doc.data.insert("score2".to_string(), Value::Float(92.5));
186
187 let result = expr.evaluate(&doc);
188 assert_eq!(result, Some(Value::Float(89.0)));
189 }
190
191 #[test]
192 fn test_computed_fields_registry() {
193 let mut registry = ComputedFields::new();
194
195 registry.register(
196 "users",
197 "full_name",
198 ComputedExpression::Concat(vec!["first_name".to_string(), "last_name".to_string()]),
199 );
200
201 let mut doc = Document::new();
202 doc.data
203 .insert("first_name".to_string(), Value::String("Jane".to_string()));
204 doc.data
205 .insert("last_name".to_string(), Value::String("Smith".to_string()));
206
207 registry.apply("users", &mut doc).unwrap();
208
209 assert_eq!(
210 doc.data.get("full_name"),
211 Some(&Value::String("Jane Smith".to_string()))
212 );
213 }
214}