1use 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#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
20pub struct AssetChanges {
21 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 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
42pub 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
51fn 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
79pub trait KnownAsset: AssetTrait {
81 fn wrap(asset: Arc<Asset<Self>>) -> AnyAsset;
82 fn wrap_id(id: AssetId<Self>) -> AnyAssetId;
83}
84
85pub trait AssetVisitor {
87 fn asset<T: AssetTrait>(&mut self, asset: Arc<Asset<T>>);
88}
89
90pub trait AssetIdVisitor {
92 fn id<T: AssetTrait>(&mut self, id: AssetId<T>);
93}
94
95macro_rules! asset_kinds {
97 ($($kind:ident),* $(,)?) => {
98 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
100 pub enum AnyAsset {
101 $($kind(Arc<Asset<$kind>>),)*
102 }
103
104 #[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 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 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);