@@ -24,6 +24,8 @@ fn eval_statement(stmt: &Statement, value: &Value) -> error::Result<Value> {
2424 Statement :: Cast {
2525 path, target_type, ..
2626 } => eval_cast ( value, path, target_type) ,
27+ Statement :: Flatten { path, prefix, .. } => eval_flatten ( value, path, prefix. as_deref ( ) ) ,
28+ Statement :: Nest { paths, target, .. } => eval_nest ( value, paths, target) ,
2729 }
2830}
2931
@@ -175,6 +177,88 @@ fn cast_value(value: &Value, target_type: &CastType) -> error::Result<Value> {
175177 }
176178}
177179
180+ // ---------------------------------------------------------------------------
181+ // flatten
182+ // ---------------------------------------------------------------------------
183+
184+ fn eval_flatten ( value : & Value , path : & Path , prefix : Option < & str > ) -> error:: Result < Value > {
185+ let target_val = resolve_path ( value, & path. segments ) ;
186+ match target_val {
187+ Some ( Value :: Map ( inner_map) ) => {
188+ // Determine the prefix for flattened keys
189+ let key_prefix = match prefix {
190+ Some ( p) => p. to_string ( ) ,
191+ None => {
192+ // Use the last segment of the path as the prefix
193+ last_field_name ( & path. segments ) . unwrap_or_default ( )
194+ }
195+ } ;
196+
197+ // Remove the original nested field
198+ let mut result = remove_path ( value, & path. segments ) ;
199+
200+ // Insert flattened key-value pairs into the parent map
201+ if let Value :: Map ( ref mut parent_map) = result {
202+ for ( key, val) in & inner_map {
203+ let flat_key = format ! ( "{key_prefix}_{key}" ) ;
204+ parent_map. insert ( flat_key, val. clone ( ) ) ;
205+ }
206+ }
207+
208+ Ok ( result)
209+ }
210+ Some ( _) => {
211+ // Non-object: no-op
212+ Ok ( value. clone ( ) )
213+ }
214+ None => {
215+ // Path doesn't exist: no-op
216+ Ok ( value. clone ( ) )
217+ }
218+ }
219+ }
220+
221+ // ---------------------------------------------------------------------------
222+ // nest
223+ // ---------------------------------------------------------------------------
224+
225+ fn eval_nest ( value : & Value , paths : & [ Path ] , target : & Path ) -> error:: Result < Value > {
226+ // Get the target field name to strip as prefix
227+ let target_name = last_field_name ( & target. segments ) . unwrap_or_default ( ) ;
228+
229+ let mut nested_map = IndexMap :: new ( ) ;
230+ let mut result = value. clone ( ) ;
231+
232+ for path in paths {
233+ if let Some ( val) = resolve_path ( value, & path. segments ) {
234+ // Get the field name from the path
235+ let field_name = last_field_name ( & path. segments ) . unwrap_or_default ( ) ;
236+
237+ // Strip target prefix (e.g., "a_x" with target "a" → "x")
238+ let nested_key = if !target_name. is_empty ( ) {
239+ let prefix_with_underscore = format ! ( "{target_name}_" ) ;
240+ if field_name. starts_with ( & prefix_with_underscore) {
241+ field_name[ prefix_with_underscore. len ( ) ..] . to_string ( )
242+ } else {
243+ field_name
244+ }
245+ } else {
246+ field_name
247+ } ;
248+
249+ nested_map. insert ( nested_key, val) ;
250+
251+ // Remove the original field
252+ result = remove_path ( & result, & path. segments ) ;
253+ }
254+ }
255+
256+ // Set the nested map at the target path
257+ result = set_path ( & result, & target. segments , Value :: Map ( nested_map) ) ;
258+
259+ Ok ( result)
260+ }
261+
178262// ---------------------------------------------------------------------------
179263// Expression evaluation
180264// ---------------------------------------------------------------------------
0 commit comments