surreal-sync-runtime 0.6.0

Shared runtime: apply pipeline, init, SurrealDB config, and transform loading for surreal-sync
Documentation
//! Arc copy-on-write batch adapter over [`InPlaceTransform`].

use anyhow::Result;
use std::sync::Arc;
use surreal_sync_core::InPlaceTransform;
use surreal_sync_core::{Change, Relation, Row};

/// Batch of `Arc`-shared items with copy-on-write mutation via [`Arc::make_mut`].
///
/// Prefer owned `Vec` + [`InPlaceTransform::transform_rows_inplace`] /
/// [`InPlaceTransform::transform_changes_inplace`] when sharing is not required.
/// Use [`CowBatch`] only when the same items may be shared across holders and
/// mutation must not affect other owners.
///
/// # Cost of `apply_inplace`
///
/// [`Self::apply_inplace`] always calls [`Arc::make_mut`] before invoking the
/// transform. When an item's strong count is greater than one, **`make_mut`
/// clones even if the transform is a no-op** (e.g. [`crate::pipeline::Passthrough`]).
/// Unique arcs (refcount 1) are mutated without allocation regardless of whether
/// the transform writes.
#[derive(Debug, Clone)]
pub struct CowBatch<T> {
    /// Shared items; mutated through [`Arc::make_mut`] in [`Self::apply_inplace`].
    pub items: Vec<Arc<T>>,
}

impl<T> CowBatch<T> {
    /// Create a batch from already-shared items.
    pub fn new(items: Vec<Arc<T>>) -> Self {
        Self { items }
    }

    /// Create a batch by wrapping each owned item in a fresh `Arc` (refcount 1).
    pub fn from_owned(items: Vec<T>) -> Self {
        Self {
            items: items.into_iter().map(Arc::new).collect(),
        }
    }

    /// Number of items in the batch.
    pub fn len(&self) -> usize {
        self.items.len()
    }

    /// Whether the batch is empty.
    pub fn is_empty(&self) -> bool {
        self.items.is_empty()
    }

    /// Consume the batch, returning the underlying `Arc`s.
    pub fn into_items(self) -> Vec<Arc<T>> {
        self.items
    }
}

impl CowBatch<Row> {
    /// Apply an in-place transform with Arc COW semantics.
    ///
    /// Always calls [`Arc::make_mut`] per item. Clones when the arc is shared
    /// (strong count &gt; 1), including when `t` is a no-op such as
    /// [`crate::pipeline::Passthrough`]. Unique arcs are mutated without allocation.
    pub fn apply_inplace(&mut self, t: &impl InPlaceTransform) -> Result<()> {
        for item in &mut self.items {
            t.transform_row(Arc::make_mut(item))?;
        }
        Ok(())
    }
}

impl CowBatch<Change> {
    /// Apply an in-place transform with Arc COW semantics.
    ///
    /// Always calls [`Arc::make_mut`] per item. Clones when the arc is shared
    /// (strong count &gt; 1), including when `t` is a no-op such as
    /// [`crate::pipeline::Passthrough`]. Unique arcs are mutated without allocation.
    pub fn apply_inplace(&mut self, t: &impl InPlaceTransform) -> Result<()> {
        for item in &mut self.items {
            t.transform_change(Arc::make_mut(item))?;
        }
        Ok(())
    }
}

impl CowBatch<Relation> {
    /// Apply an in-place transform with Arc COW semantics for relations.
    pub fn apply_inplace(&mut self, t: &impl InPlaceTransform) -> Result<()> {
        for item in &mut self.items {
            t.transform_relation(Arc::make_mut(item))?;
        }
        Ok(())
    }
}