Skip to main content

gled_protocol/
assets.rs

1//! The asset collections on the wire. An interface gets every asset when it
2//! connects, then only the assets that changed or were deleted. It saves and
3//! deletes assets through the engine, which writes them to the repository.
4
5use std::sync::Arc;
6
7use gled_model::{
8    asset::{
9        Asset, AssetTrait, animation::Animation, curve::Curve, midi_controller::MidiController,
10        output_device::OutputDevice, palette::Palette, project::Project, scene::Scene,
11    },
12    asset_id::AssetId,
13    collection::{Collection, Collections},
14    patching::definition::FixtureDefinition,
15};
16use serde::{Deserialize, Serialize};
17
18/// What changed between two versions of the collections.
19#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
20pub struct AssetChanges {
21    /// New or changed assets.
22    pub changed: Vec<AnyAsset>,
23    pub deleted: Vec<AnyAssetId>,
24}
25
26impl AssetChanges {
27    pub fn is_empty(&self) -> bool {
28        self.changed.is_empty() && self.deleted.is_empty()
29    }
30
31    /// Applies the changes to a replica.
32    pub fn apply_to(self, collections: &mut Collections) {
33        for asset in self.changed {
34            asset.insert_into(collections);
35        }
36        for id in self.deleted {
37            id.remove_from(collections);
38        }
39    }
40}
41
42/// Every asset in `assets`, as a fresh collection.
43pub fn collections_from(assets: Vec<AnyAsset>) -> Collections {
44    let mut collections = Collections::default();
45    for asset in assets {
46        asset.insert_into(&mut collections);
47    }
48    collections
49}
50
51/// Adds the changes of one kind. Assets count as unchanged when they are the
52/// same shared value or equal, so reloading the repository from disk sends
53/// only what really differs.
54fn diff_kind<T: AssetTrait>(
55    old: &Collections,
56    new: &Collections,
57    changes: &mut AssetChanges,
58    wrap: fn(Arc<Asset<T>>) -> AnyAsset,
59    wrap_id: fn(AssetId<T>) -> AnyAssetId,
60) {
61    let empty = Collection::<T>::default();
62    let old = old.get::<T>().unwrap_or(&empty);
63    let new = new.get::<T>().unwrap_or(&empty);
64    for asset in new.shared() {
65        let unchanged = old
66            .get(&asset.id)
67            .is_some_and(|previous| Arc::ptr_eq(previous, asset) || previous == asset);
68        if !unchanged {
69            changes.changed.push(wrap(asset.clone()));
70        }
71    }
72    for asset in old.shared() {
73        if new.get(&asset.id).is_none() {
74            changes.deleted.push(wrap_id(asset.id));
75        }
76    }
77}
78
79/// An asset kind the protocol knows, so a typed asset can be sent.
80pub trait KnownAsset: AssetTrait {
81    fn wrap(asset: Arc<Asset<Self>>) -> AnyAsset;
82    fn wrap_id(id: AssetId<Self>) -> AnyAssetId;
83}
84
85/// Does something with an asset of whatever kind it is.
86pub trait AssetVisitor {
87    fn asset<T: AssetTrait>(&mut self, asset: Arc<Asset<T>>);
88}
89
90/// Does something with an asset id of whatever kind it is.
91pub trait AssetIdVisitor {
92    fn id<T: AssetTrait>(&mut self, id: AssetId<T>);
93}
94
95/// Declares the wire types once for every kind of asset.
96macro_rules! asset_kinds {
97    ($($kind:ident),* $(,)?) => {
98        /// One asset of any kind.
99        #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
100        pub enum AnyAsset {
101            $($kind(Arc<Asset<$kind>>),)*
102        }
103
104        /// The id of an asset of any kind.
105        #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
106        pub enum AnyAssetId {
107            $($kind(AssetId<$kind>),)*
108        }
109
110        impl AnyAsset {
111            pub fn id(&self) -> AnyAssetId {
112                match self {
113                    $(Self::$kind(asset) => AnyAssetId::$kind(asset.id),)*
114                }
115            }
116
117            pub fn accept(self, visitor: &mut impl AssetVisitor) {
118                match self {
119                    $(Self::$kind(asset) => visitor.asset(asset),)*
120                }
121            }
122
123            pub(crate) fn insert_into(self, collections: &mut Collections) {
124                match self {
125                    $(Self::$kind(asset) => collections.get_mut::<$kind>().insert_shared(asset),)*
126                }
127            }
128        }
129
130        impl AnyAssetId {
131            pub fn accept(self, visitor: &mut impl AssetIdVisitor) {
132                match self {
133                    $(Self::$kind(id) => visitor.id(id),)*
134                }
135            }
136
137            pub(crate) fn remove_from(self, collections: &mut Collections) {
138                match self {
139                    $(Self::$kind(id) => collections.get_mut::<$kind>().delete_asset(id),)*
140                }
141            }
142        }
143
144        $(
145            impl KnownAsset for $kind {
146                fn wrap(asset: Arc<Asset<Self>>) -> AnyAsset {
147                    AnyAsset::$kind(asset)
148                }
149
150                fn wrap_id(id: AssetId<Self>) -> AnyAssetId {
151                    AnyAssetId::$kind(id)
152                }
153            }
154        )*
155
156        /// Every asset of every kind.
157        pub fn all_assets(collections: &Collections) -> Vec<AnyAsset> {
158            let mut assets = Vec::new();
159            $(
160                if let Some(collection) = collections.get::<$kind>() {
161                    assets.extend(collection.shared().cloned().map(AnyAsset::$kind));
162                }
163            )*
164            assets
165        }
166
167        impl AssetChanges {
168            /// What turns `old` into `new`.
169            pub fn between(old: &Collections, new: &Collections) -> Self {
170                let mut changes = Self::default();
171                $(diff_kind::<$kind>(old, new, &mut changes, AnyAsset::$kind, AnyAssetId::$kind);)*
172                changes
173            }
174        }
175    };
176}
177
178asset_kinds!(
179    Animation,
180    Curve,
181    FixtureDefinition,
182    MidiController,
183    OutputDevice,
184    Palette,
185    Project,
186    Scene,
187);