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