1use super::evaluator::ExpressionEvaluator;
5use super::expression::{CaptureRef, Expression, PropertyKey, RemapExpression};
6use super::ids::{ExportId, ImportId};
7use super::tables::{ExportTable, ExportValueRef, ImportTable, ImportValue, Value};
8use std::collections::HashMap;
9use std::sync::Arc;
10
11#[derive(Debug, Clone)]
13pub struct RemapContext {
14 captured_values: HashMap<usize, Value>,
16 context_imports: HashMap<ImportId, Value>,
18 context_exports: HashMap<ExportId, Value>,
19}
20
21impl Default for RemapContext {
22 fn default() -> Self {
23 Self::new()
24 }
25}
26
27impl RemapContext {
28 pub fn new() -> Self {
29 Self {
30 captured_values: HashMap::new(),
31 context_imports: HashMap::new(),
32 context_exports: HashMap::new(),
33 }
34 }
35
36 pub fn add_capture(&mut self, index: usize, value: Value) {
38 self.captured_values.insert(index, value);
39 }
40
41 pub fn get_capture(&self, index: usize) -> Option<&Value> {
43 self.captured_values.get(&index)
44 }
45
46 pub fn add_context_import(&mut self, id: ImportId, value: Value) {
48 self.context_imports.insert(id, value);
49 }
50
51 pub fn add_context_export(&mut self, id: ExportId, value: Value) {
52 self.context_exports.insert(id, value);
53 }
54}
55
56pub struct RemapEngine {
58 imports: Arc<ImportTable>,
59 exports: Arc<ExportTable>,
60}
61
62impl RemapEngine {
63 pub fn new(imports: Arc<ImportTable>, exports: Arc<ExportTable>) -> Self {
65 Self { imports, exports }
66 }
67
68 pub async fn execute_remap(
70 &self,
71 remap: &RemapExpression,
72 evaluator: &ExpressionEvaluator,
73 ) -> Result<Value, RemapError> {
74 tracing::debug!(
75 import_id = %remap.import_id,
76 captures_count = remap.captures.len(),
77 instructions_count = remap.instructions.len(),
78 "Starting remap execution"
79 );
80
81 let base_value = self.resolve_base_import(remap).await?;
83 tracing::debug!("Base import resolved: {:?}", base_value);
84
85 let mut context = RemapContext::new();
87 self.capture_values(remap, &mut context).await?;
88 tracing::debug!("Captured {} values", context.captured_values.len());
89
90 let result = self
92 .execute_instructions(&remap.instructions, &context, evaluator)
93 .await?;
94 tracing::debug!("Remap execution completed: {:?}", result);
95
96 Ok(result)
97 }
98
99 async fn resolve_base_import(&self, remap: &RemapExpression) -> Result<Value, RemapError> {
101 let import_value = self
103 .imports
104 .get(remap.import_id)
105 .ok_or(RemapError::UnknownImport(remap.import_id))?;
106
107 let base_value = match import_value {
108 ImportValue::Value(value) => value,
109 ImportValue::Stub(_) => {
110 return Err(RemapError::UnsupportedImportType(
111 "Stub remapping not yet implemented".to_string(),
112 ));
113 }
114 ImportValue::Promise(_) => {
115 return Err(RemapError::UnsupportedImportType(
116 "Promise remapping not yet implemented".to_string(),
117 ));
118 }
119 };
120
121 if let Some(path) = &remap.property_path {
123 self.resolve_property_path(&base_value, path)
124 } else {
125 Ok(base_value)
126 }
127 }
128
129 fn resolve_property_path(
131 &self,
132 value: &Value,
133 path: &[PropertyKey],
134 ) -> Result<Value, RemapError> {
135 let mut current = value;
136 #[allow(unused_assignments)]
137 let mut owned_value: Option<Value> = None;
138
139 for key in path {
140 match key {
141 PropertyKey::String(prop) => match current {
142 Value::Object(obj) => {
143 if let Some(val) = obj.get(prop) {
144 owned_value = Some((**val).clone());
145 current = owned_value.as_ref().expect("Just set owned_value to Some");
146 } else {
147 return Err(RemapError::PropertyNotFound(prop.clone()));
148 }
149 }
150 _ => {
151 return Err(RemapError::InvalidPropertyAccess(format!(
152 "Cannot access property '{}' on non-object",
153 prop
154 )))
155 }
156 },
157 PropertyKey::Number(index) => match current {
158 Value::Array(arr) => {
159 if *index < arr.len() {
160 owned_value = Some(arr[*index].clone());
161 current = owned_value.as_ref().expect("Just set owned_value to Some");
162 } else {
163 return Err(RemapError::IndexOutOfBounds(*index));
164 }
165 }
166 _ => {
167 return Err(RemapError::InvalidPropertyAccess(format!(
168 "Cannot index with {} on non-array",
169 index
170 )))
171 }
172 },
173 }
174 }
175
176 Ok(current.clone())
177 }
178
179 async fn capture_values(
181 &self,
182 remap: &RemapExpression,
183 context: &mut RemapContext,
184 ) -> Result<(), RemapError> {
185 for (index, capture_ref) in remap.captures.iter().enumerate() {
186 let captured_value = match capture_ref {
187 CaptureRef::Import(import_id) => {
188 let import_value = self
189 .imports
190 .get(*import_id)
191 .ok_or(RemapError::UnknownImport(*import_id))?;
192
193 match import_value {
194 ImportValue::Value(value) => value,
195 ImportValue::Stub(_) => {
196 return Err(RemapError::UnsupportedCaptureType(
197 "Cannot capture stub".to_string(),
198 ));
199 }
200 ImportValue::Promise(_) => {
201 return Err(RemapError::UnsupportedCaptureType(
202 "Cannot capture unresolved promise".to_string(),
203 ));
204 }
205 }
206 }
207 CaptureRef::Export(export_id) => {
208 let export_value = self
209 .exports
210 .get(*export_id)
211 .ok_or(RemapError::UnknownExport(*export_id))?;
212
213 match export_value {
214 ExportValueRef::Resolved(value) => value,
215 ExportValueRef::Rejected(error) => {
216 return Err(RemapError::CapturedRejectedPromise(error.clone()));
217 }
218 ExportValueRef::Stub(_) => {
219 return Err(RemapError::UnsupportedCaptureType(
220 "Cannot capture stub".to_string(),
221 ));
222 }
223 ExportValueRef::Promise(_) => {
224 return Err(RemapError::UnsupportedCaptureType(
225 "Cannot capture unresolved promise".to_string(),
226 ));
227 }
228 }
229 }
230 };
231
232 context.add_capture(index, captured_value);
233 }
234
235 Ok(())
236 }
237
238 async fn execute_instructions(
240 &self,
241 instructions: &[Expression],
242 context: &RemapContext,
243 evaluator: &ExpressionEvaluator,
244 ) -> Result<Value, RemapError> {
245 if instructions.is_empty() {
246 return Err(RemapError::EmptyInstructions);
247 }
248
249 let mut result = Value::Null;
250
251 for (i, instruction) in instructions.iter().enumerate() {
252 tracing::debug!("Executing instruction {}: {:?}", i, instruction);
253
254 let resolved_instruction = Self::resolve_instruction_captures(instruction, context)?;
256
257 result = evaluator
259 .evaluate(resolved_instruction)
260 .await
261 .map_err(|e| RemapError::InstructionExecutionError(i, e.to_string()))?;
262
263 tracing::debug!("Instruction {} result: {:?}", i, result);
264 }
265
266 Ok(result)
267 }
268
269 fn resolve_instruction_captures(
271 instruction: &Expression,
272 context: &RemapContext,
273 ) -> Result<Expression, RemapError> {
274 match instruction {
275 Expression::String(s) if s.starts_with('$') => {
277 if let Ok(index) = s[1..].parse::<usize>() {
278 if let Some(captured_value) = context.get_capture(index) {
279 Ok(Self::value_to_expression(captured_value))
280 } else {
281 Err(RemapError::InvalidCaptureReference(index))
282 }
283 } else {
284 Ok(instruction.clone())
285 }
286 }
287
288 Expression::Array(elements) => {
290 let resolved_elements: Result<Vec<Expression>, RemapError> = elements
291 .iter()
292 .map(|elem| Self::resolve_instruction_captures(elem, context))
293 .collect();
294 Ok(Expression::Array(resolved_elements?))
295 }
296
297 Expression::Object(obj) => {
299 let mut resolved_obj = std::collections::HashMap::new();
300 for (key, value) in obj {
301 let resolved_value = Self::resolve_instruction_captures(value, context)?;
302 resolved_obj.insert(key.clone(), Box::new(resolved_value));
303 }
304 Ok(Expression::Object(resolved_obj))
305 }
306
307 _ => Ok(instruction.clone()),
309 }
310 }
311
312 fn value_to_expression(value: &Value) -> Expression {
314 match value {
315 Value::Null => Expression::Null,
316 Value::Bool(b) => Expression::Bool(*b),
317 Value::Number(n) => Expression::Number(n.clone()),
318 Value::String(s) => Expression::String(s.clone()),
319 Value::Array(arr) => {
320 let elements = arr.iter().map(Self::value_to_expression).collect();
321 Expression::Array(elements)
322 }
323 Value::Object(obj) => {
324 let mut map = std::collections::HashMap::new();
325 for (key, val) in obj {
326 map.insert(key.clone(), Box::new(Self::value_to_expression(val)));
327 }
328 Expression::Object(map)
329 }
330 Value::Date(timestamp) => Expression::Date(*timestamp),
331 Value::Error {
332 error_type,
333 message,
334 stack,
335 } => Expression::Error(super::expression::ErrorExpression {
336 error_type: error_type.clone(),
337 message: message.clone(),
338 stack: stack.clone(),
339 }),
340 Value::Stub(_) | Value::Promise(_) => {
342 Expression::String("[Complex value - not serializable]".to_string())
343 }
344 }
345 }
346}
347
348#[derive(Debug, thiserror::Error)]
350pub enum RemapError {
351 #[error("Unknown import: {0}")]
352 UnknownImport(ImportId),
353
354 #[error("Unknown export: {0}")]
355 UnknownExport(ExportId),
356
357 #[error("Property not found: {0}")]
358 PropertyNotFound(String),
359
360 #[error("Index out of bounds: {0}")]
361 IndexOutOfBounds(usize),
362
363 #[error("Invalid property access: {0}")]
364 InvalidPropertyAccess(String),
365
366 #[error("Unsupported import type: {0}")]
367 UnsupportedImportType(String),
368
369 #[error("Unsupported capture type: {0}")]
370 UnsupportedCaptureType(String),
371
372 #[error("Captured rejected promise: {0:?}")]
373 CapturedRejectedPromise(Value),
374
375 #[error("Empty instruction sequence")]
376 EmptyInstructions,
377
378 #[error("Invalid capture reference: ${0}")]
379 InvalidCaptureReference(usize),
380
381 #[error("Instruction {0} execution error: {1}")]
382 InstructionExecutionError(usize, String),
383}
384
385#[cfg(test)]
386mod tests {
387 use super::*;
388 use crate::protocol::{IdAllocator, ImportValue};
389 use serde_json::Number;
390 use std::sync::Arc;
391
392 #[tokio::test]
393 async fn test_basic_remap_execution() {
394 let allocator = Arc::new(IdAllocator::new());
395 let imports = Arc::new(ImportTable::new(allocator.clone()));
396 let exports = Arc::new(ExportTable::new(allocator));
397
398 let import_id = ImportId(1);
400 imports
401 .insert(
402 import_id,
403 ImportValue::Value(Value::Number(Number::from(42))),
404 )
405 .unwrap();
406
407 let remap = RemapExpression {
409 import_id,
410 property_path: None,
411 captures: vec![CaptureRef::Import(import_id)],
412 instructions: vec![Expression::String("$0".to_string())],
413 };
414
415 let engine = RemapEngine::new(imports.clone(), exports.clone());
416 let evaluator = ExpressionEvaluator::new(imports, exports);
417
418 let result = engine.execute_remap(&remap, &evaluator).await.unwrap();
419
420 match result {
421 Value::Number(n) => assert_eq!(n.as_i64(), Some(42)),
422 _ => panic!("Expected number result"),
423 }
424 }
425
426 #[tokio::test]
427 async fn test_property_path_resolution() {
428 let allocator = Arc::new(IdAllocator::new());
429 let imports = Arc::new(ImportTable::new(allocator.clone()));
430 let exports = Arc::new(ExportTable::new(allocator));
431
432 let mut obj = std::collections::HashMap::new();
434 obj.insert(
435 "user".to_string(),
436 Box::new(Value::Object({
437 let mut user_obj = std::collections::HashMap::new();
438 user_obj.insert(
439 "name".to_string(),
440 Box::new(Value::String("Alice".to_string())),
441 );
442 user_obj.insert("age".to_string(), Box::new(Value::Number(Number::from(30))));
443 user_obj
444 })),
445 );
446
447 let import_id = ImportId(1);
448 imports
449 .insert(import_id, ImportValue::Value(Value::Object(obj)))
450 .unwrap();
451
452 let remap = RemapExpression {
454 import_id,
455 property_path: Some(vec![
456 PropertyKey::String("user".to_string()),
457 PropertyKey::String("name".to_string()),
458 ]),
459 captures: vec![],
460 instructions: vec![Expression::String("Alice".to_string())], };
462
463 let engine = RemapEngine::new(imports.clone(), exports.clone());
464 let evaluator = ExpressionEvaluator::new(imports, exports);
465
466 let result = engine.execute_remap(&remap, &evaluator).await.unwrap();
467
468 match result {
469 Value::String(s) => assert_eq!(s, "Alice"),
470 _ => panic!("Expected string result, got: {:?}", result),
471 }
472 }
473
474 #[tokio::test]
475 async fn test_capture_resolution() {
476 let allocator = Arc::new(IdAllocator::new());
477 let imports = Arc::new(ImportTable::new(allocator.clone()));
478 let exports = Arc::new(ExportTable::new(allocator));
479
480 let import1 = ImportId(1);
482 let import2 = ImportId(2);
483
484 imports
485 .insert(import1, ImportValue::Value(Value::Number(Number::from(10))))
486 .unwrap();
487 imports
488 .insert(import2, ImportValue::Value(Value::Number(Number::from(20))))
489 .unwrap();
490
491 let remap = RemapExpression {
493 import_id: import1,
494 property_path: None,
495 captures: vec![CaptureRef::Import(import1), CaptureRef::Import(import2)],
496 instructions: vec![
497 Expression::Array(vec![
499 Expression::String("$0".to_string()),
500 Expression::String("$1".to_string()),
501 ]),
502 ],
503 };
504
505 let engine = RemapEngine::new(imports.clone(), exports.clone());
506 let evaluator = ExpressionEvaluator::new(imports, exports);
507
508 let result = engine.execute_remap(&remap, &evaluator).await.unwrap();
509
510 match result {
511 Value::Array(arr) => {
512 assert_eq!(arr.len(), 2);
513 }
515 _ => panic!("Expected array result"),
516 }
517 }
518}