1use std::{collections::HashMap, sync::Arc};
7
8use gled_model::{
9 asset::{
10 project::Project,
11 scene::{grid::GridLocation, instance::SceneInstance},
12 },
13 patching::Patch,
14};
15use serde::{Deserialize, Serialize};
16
17#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
19pub enum ProjectUpdate {
20 Unchanged,
21 Replaced(Option<Box<Project>>),
23 Changed(ProjectDiff),
24}
25
26#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
28pub struct ProjectDiff {
29 pub scenes: Vec<(GridLocation, Option<SceneInstance>)>,
31 pub patch: Option<Box<Patch>>,
33 pub rest: Option<Box<Project>>,
36}
37
38impl ProjectUpdate {
39 pub fn between(old: Option<&Project>, new: Option<&Project>) -> Self {
41 match (old, new) {
42 (None, None) => Self::Unchanged,
43 (_, None) => Self::Replaced(None),
44 (None, Some(new)) => Self::Replaced(Some(Box::new(new.clone()))),
45 (Some(old), Some(new)) if std::ptr::eq(old, new) => Self::Unchanged,
48 (Some(old), Some(new)) => {
51 let diff = ProjectDiff::between(old, new);
52 if diff.is_empty() {
53 Self::Unchanged
54 } else {
55 Self::Changed(diff)
56 }
57 }
58 }
59 }
60
61 pub fn apply_to(self, project: &mut Option<Arc<Project>>) {
64 match self {
65 Self::Unchanged => {}
66 Self::Replaced(new) => *project = new.map(|new| Arc::new(*new)),
67 Self::Changed(diff) => {
68 if let Some(project) = project {
69 diff.apply_to(Arc::make_mut(project));
70 }
71 }
72 }
73 }
74}
75
76impl ProjectDiff {
77 fn is_empty(&self) -> bool {
78 self.scenes.is_empty() && self.patch.is_none() && self.rest.is_none()
79 }
80
81 fn between(old: &Project, new: &Project) -> Self {
82 let mut scenes: Vec<_> = new
83 .scenes_instances_grid
84 .iter()
85 .filter(|(location, scene)| old.scenes_instances_grid.get(location) != Some(scene))
86 .map(|(location, scene)| (*location, Some(scene.clone())))
87 .collect();
88 scenes.extend(
89 old.scenes_instances_grid
90 .keys()
91 .filter(|location| !new.scenes_instances_grid.contains_key(location))
92 .map(|location| (*location, None)),
93 );
94 let patch = (old.patch != new.patch).then(|| Box::new(new.patch.clone()));
95 let new_rest = rest(new);
96 let rest = (rest(old) != new_rest).then(|| Box::new(new_rest));
97 Self {
98 scenes,
99 patch,
100 rest,
101 }
102 }
103
104 fn apply_to(self, project: &mut Project) {
105 if let Some(rest) = self.rest {
106 let scenes = std::mem::take(&mut project.scenes_instances_grid);
107 let patch = std::mem::take(&mut project.patch);
108 *project = *rest;
109 project.scenes_instances_grid = scenes;
110 project.patch = patch;
111 }
112 if let Some(patch) = self.patch {
113 project.patch = *patch;
114 }
115 for (location, scene) in self.scenes {
116 match scene {
117 Some(scene) => {
118 project.scenes_instances_grid.insert(location, scene);
119 }
120 None => {
121 project.scenes_instances_grid.remove(&location);
122 }
123 }
124 }
125 }
126}
127
128fn rest(project: &Project) -> Project {
132 let Project {
133 palette,
134 patch: _,
135 grid_width,
136 grid_height,
137 grid_highlight,
138 scenes_instances_grid: _,
139 channel_overwrites,
140 output_routings,
141 artnet_config,
142 tap_input_events,
143 blackout_input_events,
144 blackout_hold_input_events,
145 half_input_events,
146 double_input_events,
147 midi_active_mappings,
148 main_dimmer,
149 audio_input_device,
150 artnet_control_config,
151 osc_config,
152 } = project;
153 Project {
154 palette: palette.clone(),
155 patch: Patch::default(),
156 grid_width: *grid_width,
157 grid_height: *grid_height,
158 grid_highlight: *grid_highlight,
159 channel_overwrites: channel_overwrites.clone(),
160 output_routings: output_routings.clone(),
161 artnet_config: artnet_config.clone(),
162 tap_input_events: tap_input_events.clone(),
163 blackout_input_events: blackout_input_events.clone(),
164 blackout_hold_input_events: blackout_hold_input_events.clone(),
165 half_input_events: half_input_events.clone(),
166 double_input_events: double_input_events.clone(),
167 midi_active_mappings: midi_active_mappings.clone(),
168 main_dimmer: *main_dimmer,
169 audio_input_device: audio_input_device.clone(),
170 artnet_control_config: *artnet_control_config,
171 osc_config: *osc_config,
172 scenes_instances_grid: HashMap::new(),
173 }
174}