use crate::schema::types::SchemaError;
use crate::transform::chain_parser::{ChainParser, ParsedChain};
use serde_json::Value as JsonValue;
use std::collections::HashMap;
pub fn resolve_dotted_path(
path: &str,
input_values: &HashMap<String, JsonValue>,
) -> Result<JsonValue, SchemaError> {
let parts: Vec<&str> = path.split('.').collect();
if parts.is_empty() {
return Err(SchemaError::InvalidField("Empty path provided".to_string()));
}
let mut current_value = input_values
.get(parts[0])
.ok_or_else(|| SchemaError::InvalidField(format!("Field '{}' not found", parts[0])))?
.clone();
for part in parts.iter().skip(1) {
if let JsonValue::Object(obj) = current_value {
current_value = obj
.get(*part)
.ok_or_else(|| {
SchemaError::InvalidField(format!(
"Field '{}' not found in path '{}'",
part, path
))
})?
.clone();
} else if let JsonValue::Array(arr) = current_value {
if let Ok(index) = part.parse::<usize>() {
current_value = arr
.get(index)
.ok_or_else(|| {
SchemaError::InvalidField(format!(
"Index '{}' out of bounds in path '{}'",
index, path
))
})?
.clone();
} else {
return Err(SchemaError::InvalidField(format!(
"Invalid array index '{}' in path '{}'",
part, path
)));
}
} else {
return Err(SchemaError::InvalidField(format!(
"Cannot access '{}' on non-object/non-array value in path '{}'",
part, path
)));
}
}
Ok(current_value)
}
pub fn resolve_field_value_from_chain(
parsed_chain: &ParsedChain,
input_values: &HashMap<String, JsonValue>,
field_name: &str,
) -> Result<JsonValue, SchemaError> {
let mut path_parts = Vec::new();
for operation in &parsed_chain.operations {
match operation {
crate::transform::chain_parser::ChainOperation::FieldAccess(field_name) => {
path_parts.push(field_name.clone());
}
_ => {
return Err(SchemaError::InvalidField(format!(
"No simple path found in parsed chain for field '{}'",
field_name
)));
}
}
}
let simple_path = path_parts.join(".");
if simple_path.is_empty() {
return Err(SchemaError::InvalidField(format!(
"No simple path found in parsed chain for field '{}'",
field_name
)));
}
resolve_dotted_path(&simple_path, input_values)
}
pub fn parse_expressions_batch(
expressions: &[(String, String)],
) -> Result<Vec<(String, ParsedChain)>, SchemaError> {
let mut parsed_chains = Vec::new();
let mut parsing_errors = Vec::new();
for (field_name, expression) in expressions {
match ChainParser::new().parse(expression) {
Ok(parsed_chain) => {
parsed_chains.push((field_name.clone(), parsed_chain));
}
Err(err) => {
parsing_errors.push((field_name.clone(), expression.clone(), err));
}
}
}
if !parsing_errors.is_empty() {
let error_messages: Vec<String> = parsing_errors
.iter()
.map(|(field, expr, err)| format!("Field '{}' expression '{}': {}", field, expr, err))
.collect();
log::warn!(
"⚠️ {} expressions failed to parse (will use fallback): {}",
parsing_errors.len(),
error_messages.join("; ")
);
}
Ok(parsed_chains)
}