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},
};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct AssetChanges {
pub changed: Vec<AnyAsset>,
pub deleted: Vec<AnyAssetId>,
}
impl AssetChanges {
pub fn is_empty(&self) -> bool {
self.changed.is_empty() && self.deleted.is_empty()
}
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);
}
}
}
pub fn collections_from(assets: Vec<AnyAsset>) -> Collections {
let mut collections = Collections::default();
for asset in assets {
asset.insert_into(&mut collections);
}
collections
}
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));
}
}
}
pub trait KnownAsset: AssetTrait {
fn wrap(asset: Arc<Asset<Self>>) -> AnyAsset;
fn wrap_id(id: AssetId<Self>) -> AnyAssetId;
}
pub trait AssetVisitor {
fn asset<T: AssetTrait>(&mut self, asset: Arc<Asset<T>>);
}
pub trait AssetIdVisitor {
fn id<T: AssetTrait>(&mut self, id: AssetId<T>);
}
macro_rules! asset_kinds {
($($kind:ident),* $(,)?) => {
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum AnyAsset {
$($kind(Arc<Asset<$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)
}
}
)*
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 {
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,
MidiController,
OutputDevice,
Palette,
Project,
Scene,
);