@@ -477,6 +477,74 @@ macro_rules! require_child {
477477 } } ;
478478}
479479
480+ /// Like [`require_child!`], but for optional children. If the child is `None`, this is a no-op.
481+ /// If the child is `Some` but does not match `$M`, early-returns an [`ExecutionResult`] requesting
482+ /// execution of child `$idx`.
483+ ///
484+ /// Unlike `require_child!`, this is a statement macro (no value produced) and does not clone
485+ /// `$parent` — it is moved into the early-return path.
486+ ///
487+ /// ```ignore
488+ /// require_opt_child!(array, array.patches().map(|p| p.indices()), 1 => Primitive);
489+ /// ```
490+ #[ macro_export]
491+ macro_rules! require_opt_child {
492+ ( $parent: expr, $child_opt: expr, $idx: expr => $M: ty) => {
493+ if $child_opt. is_some_and( |child| !child. is:: <$M>( ) ) {
494+ return Ok ( $crate:: ExecutionResult :: execute_slot:: <$M>( $parent, $idx) ) ;
495+ }
496+ } ;
497+ }
498+
499+ /// Require that all children of a [`Patches`](crate::patches::Patches) (indices, values, and
500+ /// optionally chunk_offsets) are `Primitive`. If no patches are present, this is a no-op.
501+ ///
502+ /// Like [`require_opt_child!`], `$parent` is moved (not cloned) into the early-return path.
503+ ///
504+ /// ```ignore
505+ /// require_patches!(array, array.patches(), PATCH_INDICES_SLOT, PATCH_VALUES_SLOT, PATCH_CHUNK_OFFSETS_SLOT);
506+ /// ```
507+ #[ macro_export]
508+ macro_rules! require_patches {
509+ ( $parent: expr, $patches: expr, $indices_slot: expr, $values_slot: expr, $chunk_offsets_slot: expr) => {
510+ $crate:: require_opt_child!(
511+ $parent,
512+ $patches. map( |p| p. indices( ) ) ,
513+ $indices_slot => $crate:: arrays:: Primitive
514+ ) ;
515+ $crate:: require_opt_child!(
516+ $parent,
517+ $patches. map( |p| p. values( ) ) ,
518+ $values_slot => $crate:: arrays:: Primitive
519+ ) ;
520+ $crate:: require_opt_child!(
521+ $parent,
522+ $patches. and_then( |p| p. chunk_offsets( ) . as_ref( ) ) ,
523+ $chunk_offsets_slot => $crate:: arrays:: Primitive
524+ ) ;
525+ } ;
526+ }
527+
528+ /// Require that a [`Validity::Array`] child matches `$M`. If validity is not array-backed
529+ /// (e.g. `NonNullable` or `AllValid`), this is a no-op. If it is array-backed but does not
530+ /// match `$M`, early-returns an [`ExecutionResult`] requesting execution of the validity slot.
531+ ///
532+ /// Like [`require_opt_child!`], `$parent` is moved (not cloned) into the early-return path.
533+ ///
534+ /// ```ignore
535+ /// require_validity!(array, &array.validity, VALIDITY_SLOT => AnyCanonical);
536+ /// ```
537+ #[ macro_export]
538+ macro_rules! require_validity {
539+ ( $parent: expr, $validity: expr, $idx: expr => $M: ty) => {
540+ if let $crate:: validity:: Validity :: Array ( v) = $validity {
541+ if !v. is:: <$M>( ) {
542+ return Ok ( $crate:: ExecutionResult :: execute_slot:: <$M>( $parent, $idx) ) ;
543+ }
544+ }
545+ } ;
546+ }
547+
480548/// Extension trait for creating an execution context from a session.
481549pub trait VortexSessionExecute {
482550 /// Create a new execution context from this session.
0 commit comments