Skip to main content

ottl/
helpers.rs

1//! Helper utilities for OTTL integrators.
2//!
3//! This module provides functions that implement common behavior expected when
4//! integrating OTTL (e.g. implementing [`crate::PathAccessor`]). Index resolution
5//! is the integrator's responsibility; this helper is provided for convenience
6//! in tests and reference implementations.
7
8use crate::{IndexExpr, Value};
9
10/// Applies a sequence of index expressions to a value and returns the result.
11///
12/// Supports indexing into [`Value::List`] and [`Value::Map`] with [`IndexExpr::Int`]
13/// and [`IndexExpr::String`] respectively, and into [`Value::String`] with
14/// [`IndexExpr::Int`] (character index). Any other combination returns an error.
15///
16/// Typically called inside a [`crate::PathAccessor`] implementation to apply per-field
17/// keys: `apply_indexes(base_value, &field.keys)`.
18///
19/// # Errors
20///
21/// Returns an error if an index is out of bounds, a map key is missing, or
22/// the value type does not support the given index type.
23///
24/// # Example
25///
26/// ```ignore
27/// use ottl::helpers::apply_indexes;
28/// use ottl::{Value, IndexExpr};
29///
30/// let list = Value::List(vec![Value::Int(1), Value::Int(2)]);
31/// let keys = [IndexExpr::Int(0)];
32/// let v = apply_indexes(list, &keys)?;
33/// assert!(matches!(v, Value::Int(1)));
34/// ```
35pub fn apply_indexes(value: Value, indexes: &[IndexExpr]) -> crate::Result<Value> {
36    let mut current = value;
37    for index in indexes {
38        current = match (&current, index) {
39            (Value::List(list), IndexExpr::Int(i)) => list
40                .get(*i)
41                .cloned()
42                .ok_or_else(|| -> crate::BoxError { format!("Index {} out of bounds", i).into() })?,
43            (Value::Map(map), IndexExpr::String(key)) => map
44                .get(key)
45                .cloned()
46                .ok_or_else(|| -> crate::BoxError { format!("Key '{}' not found", key).into() })?,
47            (Value::String(s), IndexExpr::Int(i)) => s
48                .chars()
49                .nth(*i)
50                .map(|c| Value::string(c.to_string()))
51                .ok_or_else(|| -> crate::BoxError { format!("Index {} out of bounds", i).into() })?,
52            _ => return Err(format!("Cannot index {:?} with {:?}", current, index).into()),
53        };
54    }
55    Ok(current)
56}