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