Skip to main content

buoyant_kernel/transforms/
mod.rs

1//! Shared transform infrastructure.
2use std::borrow::{Cow, ToOwned};
3
4mod carrier;
5mod expression;
6mod schema;
7
8pub use carrier::Carrier;
9pub(crate) use carrier::{carrier_into_inner_opt, carrier_try_none};
10
11pub use self::expression::{ExpressionDepthChecker, ExpressionTransform};
12pub use self::schema::{SchemaDepthChecker, SchemaTransform};
13
14/// Defines a transform's `Output` and `Residual` associated types.
15///
16/// Example: fallible schema visitor
17/// ```rust,no_run
18/// # use buoyant_kernel as delta_kernel;
19/// # use delta_kernel::transform_output_type;
20/// # use delta_kernel::schema::StructField;
21/// # use delta_kernel::transforms::SchemaTransform;
22/// # use delta_kernel::DeltaResult;
23/// struct Validate;
24///
25/// impl<'a> SchemaTransform<'a> for Validate {
26///     transform_output_type!(|'a, T| DeltaResult<()>);
27///
28///     fn transform_struct_field(&mut self, _field: &'a StructField) -> DeltaResult<()> {
29///         todo!()
30///     }
31/// }
32/// ```
33///
34/// Example: infallible filtering expression transform
35/// ```rust,no_run
36/// # use std::borrow::Cow;
37/// # use buoyant_kernel as delta_kernel;
38/// # use delta_kernel::transform_output_type;
39/// # use delta_kernel::expressions::ColumnName;
40/// # use delta_kernel::transforms::ExpressionTransform;
41/// struct KeepSomeColumns;
42///
43/// impl<'a> ExpressionTransform<'a> for KeepSomeColumns {
44///     transform_output_type!(|'a, T| Option<Cow<'a, T>>);
45///
46///     fn transform_expr_column(&mut self, _name: &'a ColumnName) -> Option<Cow<'a, ColumnName>> {
47///         todo!()
48///     }
49/// }
50/// ```
51#[macro_export]
52macro_rules! transform_output_type {
53    (|$lt:lifetime, $T:ident| $out:ty) => {
54        type Output<$T: ::std::borrow::ToOwned + ?Sized + $lt> = $out;
55        type Residual = <Self::Output<()> as $crate::transforms::Carrier<$lt, ()>>::Residual;
56    };
57}
58#[doc(inline)]
59pub use transform_output_type;
60
61/// Rebuilds a parent from transformed children only when needed.
62///
63/// This helper consumes transformed child carriers. When a filtering carrier is used and all
64/// children were filtered out, filter out the parent as well. If all children were unchanged, it
65/// returns a carrier for the original parent. Otherwise returns a carrier for a new owned parent,
66/// produced by the provided `map_owned`.
67pub(crate) fn map_owned_children_or_else<'a, Parent, Child, ChildCarrier, ParentCarrier, R>(
68    parent: &'a Parent,
69    children: impl ExactSizeIterator<Item = ChildCarrier>,
70    map_owned: impl FnOnce(Vec<<Child as ToOwned>::Owned>) -> <Parent as ToOwned>::Owned,
71) -> ParentCarrier
72where
73    Parent: ToOwned + ?Sized,
74    Child: ToOwned + ?Sized + 'a,
75    ChildCarrier: Carrier<'a, Child, Residual = R>,
76    ParentCarrier: Carrier<'a, Parent, Residual = R>,
77{
78    let num_children = children.len();
79    let mut num_borrowed = 0;
80
81    // NOTE: A transform in visitor mode uses the ZST () as Carrier (= vec allocates no memory),
82    let mut new_children = Vec::with_capacity(num_children);
83    for child in children {
84        if let Some(transformed) = carrier_into_inner_opt!(child) {
85            if let Cow::Borrowed(_) = transformed {
86                num_borrowed += 1;
87            }
88            new_children.push(transformed);
89        }
90    }
91
92    // If all children were filtered out, try to return the "None" carrier. If not supported, then
93    // the parent must have already been empty and it's safe to continue (return borrowed parent).
94    if new_children.is_empty() {
95        carrier_try_none!();
96    }
97
98    if num_borrowed < num_children {
99        let owned = new_children.into_iter().map(Cow::into_owned).collect();
100        Carrier::from_inner(Cow::Owned(map_owned(owned)))
101    } else {
102        Carrier::from_inner(Cow::Borrowed(parent))
103    }
104}
105
106/// Rebuilds a two-child parent from transformed children only when needed.
107///
108/// If either child is filtered out (`None`), filter out the parent by returning `None`. If both
109/// children survive as borrowed values, this returns a borrowed parent. Otherwise, it uses the
110/// provided `map_owned` function to rebuild and return an owned parent.
111pub(crate) fn map_owned_pair_or_else<'a, Parent, Child, ChildCarrier, ParentCarrier, R>(
112    parent: &'a Parent,
113    left: ChildCarrier,
114    right: ChildCarrier,
115    map_owned: impl FnOnce((Child::Owned, Child::Owned)) -> Parent,
116) -> ParentCarrier
117where
118    Parent: Clone,
119    Child: ToOwned + ?Sized + 'a,
120    ChildCarrier: Carrier<'a, Child, Residual = R>,
121    ParentCarrier: Carrier<'a, Parent, Residual = R>,
122{
123    let left = carrier_into_inner_opt!(left);
124    let right = carrier_into_inner_opt!(right);
125    let (Some(left), Some(right)) = (left, right) else {
126        // SAFETY: Only a filtering carrier could produce None => try_none must succeed.
127        carrier_try_none!();
128        unreachable!();
129    };
130    Carrier::from_inner(match (left, right) {
131        (Cow::Borrowed(_), Cow::Borrowed(_)) => Cow::Borrowed(parent),
132        (left, right) => Cow::Owned(map_owned((left.into_owned(), right.into_owned()))),
133    })
134}
135
136/// Rebuilds a single-child parent from a transformed child only when needed.
137///
138/// If the child is filtered out (`None`), filter out the parent by returning `None`. If the child
139/// survives as a borrowed value, this returns a borrowed parent. Otherwise, it uses the provided
140/// `map_owned` function to rebuild and return an owned parent.
141pub(crate) fn map_owned_or_else<'a, Parent, Child, ChildCarrier, ParentCarrier, R>(
142    parent: &'a Parent,
143    child: ChildCarrier,
144    map_owned: impl FnOnce(Child::Owned) -> Parent,
145) -> ParentCarrier
146where
147    Parent: Clone,
148    Child: ToOwned + ?Sized + 'a,
149    ChildCarrier: Carrier<'a, Child, Residual = R>,
150    ParentCarrier: Carrier<'a, Parent, Residual = R>,
151{
152    let child = carrier_into_inner_opt!(child);
153    let Some(child) = child else {
154        // SAFETY: Only a filtering carrier could produce None => try_none must succeed.
155        carrier_try_none!();
156        unreachable!();
157    };
158    Carrier::from_inner(match child {
159        Cow::Owned(v) => Cow::Owned(map_owned(v)),
160        Cow::Borrowed(_) => Cow::Borrowed(parent),
161    })
162}