gled-protocol 2.32.1

What gled's user interfaces and its engine send each other
Documentation
//! The asset collections on the wire. An interface gets every asset when it
//! connects, then only the assets that changed or were deleted. It saves and
//! deletes assets through the engine, which writes them to the repository.

use std::sync::Arc;

use gled_model::{
    asset::{
        Asset, AssetTrait, animation::Animation, curve::Curve, midi_controller::MidiController,
        output_device::OutputDevice, palette::Palette, project::Project, scene::Scene,
    },
    asset_id::AssetId,
    collection::{Collection, Collections},
    patching::definition::FixtureDefinition,
};
use serde::{Deserialize, Serialize};

/// What changed between two versions of the collections.
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct AssetChanges {
    /// New or changed assets.
    pub changed: Vec<AnyAsset>,
    pub deleted: Vec<AnyAssetId>,
}

impl AssetChanges {
    pub fn is_empty(&self) -> bool {
        self.changed.is_empty() && self.deleted.is_empty()
    }

    /// Applies the changes to a replica.
    pub fn apply_to(self, collections: &mut Collections) {
        for asset in self.changed {
            asset.insert_into(collections);
        }
        for id in self.deleted {
            id.remove_from(collections);
        }
    }
}

/// Every asset in `assets`, as a fresh collection.
pub fn collections_from(assets: Vec<AnyAsset>) -> Collections {
    let mut collections = Collections::default();
    for asset in assets {
        asset.insert_into(&mut collections);
    }
    collections
}

/// Adds the changes of one kind. Assets count as unchanged when they are the
/// same shared value or equal, so reloading the repository from disk sends
/// only what really differs.
fn diff_kind<T: AssetTrait>(
    old: &Collections,
    new: &Collections,
    changes: &mut AssetChanges,
    wrap: fn(Arc<Asset<T>>) -> AnyAsset,
    wrap_id: fn(AssetId<T>) -> AnyAssetId,
) {
    let empty = Collection::<T>::default();
    let old = old.get::<T>().unwrap_or(&empty);
    let new = new.get::<T>().unwrap_or(&empty);
    for asset in new.shared() {
        let unchanged = old
            .get(&asset.id)
            .is_some_and(|previous| Arc::ptr_eq(previous, asset) || previous == asset);
        if !unchanged {
            changes.changed.push(wrap(asset.clone()));
        }
    }
    for asset in old.shared() {
        if new.get(&asset.id).is_none() {
            changes.deleted.push(wrap_id(asset.id));
        }
    }
}

/// An asset kind the protocol knows, so a typed asset can be sent.
pub trait KnownAsset: AssetTrait {
    fn wrap(asset: Arc<Asset<Self>>) -> AnyAsset;
    fn wrap_id(id: AssetId<Self>) -> AnyAssetId;
}

/// Does something with an asset of whatever kind it is.
pub trait AssetVisitor {
    fn asset<T: AssetTrait>(&mut self, asset: Arc<Asset<T>>);
}

/// Does something with an asset id of whatever kind it is.
pub trait AssetIdVisitor {
    fn id<T: AssetTrait>(&mut self, id: AssetId<T>);
}

/// Declares the wire types once for every kind of asset.
macro_rules! asset_kinds {
    ($($kind:ident),* $(,)?) => {
        /// One asset of any kind.
        #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
        pub enum AnyAsset {
            $($kind(Arc<Asset<$kind>>),)*
        }

        /// The id of an asset of any kind.
        #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
        pub enum AnyAssetId {
            $($kind(AssetId<$kind>),)*
        }

        impl AnyAsset {
            pub fn id(&self) -> AnyAssetId {
                match self {
                    $(Self::$kind(asset) => AnyAssetId::$kind(asset.id),)*
                }
            }

            pub fn accept(self, visitor: &mut impl AssetVisitor) {
                match self {
                    $(Self::$kind(asset) => visitor.asset(asset),)*
                }
            }

            pub(crate) fn insert_into(self, collections: &mut Collections) {
                match self {
                    $(Self::$kind(asset) => collections.get_mut::<$kind>().insert_shared(asset),)*
                }
            }
        }

        impl AnyAssetId {
            pub fn accept(self, visitor: &mut impl AssetIdVisitor) {
                match self {
                    $(Self::$kind(id) => visitor.id(id),)*
                }
            }

            pub(crate) fn remove_from(self, collections: &mut Collections) {
                match self {
                    $(Self::$kind(id) => collections.get_mut::<$kind>().delete_asset(id),)*
                }
            }
        }

        $(
            impl KnownAsset for $kind {
                fn wrap(asset: Arc<Asset<Self>>) -> AnyAsset {
                    AnyAsset::$kind(asset)
                }

                fn wrap_id(id: AssetId<Self>) -> AnyAssetId {
                    AnyAssetId::$kind(id)
                }
            }
        )*

        /// Every asset of every kind.
        pub fn all_assets(collections: &Collections) -> Vec<AnyAsset> {
            let mut assets = Vec::new();
            $(
                if let Some(collection) = collections.get::<$kind>() {
                    assets.extend(collection.shared().cloned().map(AnyAsset::$kind));
                }
            )*
            assets
        }

        impl AssetChanges {
            /// What turns `old` into `new`.
            pub fn between(old: &Collections, new: &Collections) -> Self {
                let mut changes = Self::default();
                $(diff_kind::<$kind>(old, new, &mut changes, AnyAsset::$kind, AnyAssetId::$kind);)*
                changes
            }
        }
    };
}

asset_kinds!(
    Animation,
    Curve,
    FixtureDefinition,
    MidiController,
    OutputDevice,
    Palette,
    Project,
    Scene,
);