1use std::collections::HashMap;
7
8use gled_model::asset::{
9 project::Project,
10 scene::{grid::GridLocation, instance::SceneInstance},
11 svg::Svg,
12};
13use serde::{Deserialize, Serialize};
14
15#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
17pub enum ProjectUpdate {
18 Unchanged,
19 Replaced(Option<Box<Project>>),
21 Patched(ProjectPatch),
22}
23
24#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
26pub struct ProjectPatch {
27 pub scenes: Vec<(GridLocation, Option<SceneInstance>)>,
29 pub svg: Option<Option<Svg>>,
31 pub rest: Option<Box<Project>>,
34}
35
36impl ProjectUpdate {
37 pub fn between(old: Option<&Project>, new: Option<&Project>) -> Self {
39 match (old, new) {
40 (None, None) => Self::Unchanged,
41 (_, None) => Self::Replaced(None),
42 (None, Some(new)) => Self::Replaced(Some(Box::new(new.clone()))),
43 (Some(old), Some(new)) if old == new => Self::Unchanged,
44 (Some(old), Some(new)) => Self::Patched(ProjectPatch::between(old, new)),
45 }
46 }
47
48 pub fn apply_to(self, project: &mut Option<Project>) {
51 match self {
52 Self::Unchanged => {}
53 Self::Replaced(new) => *project = new.map(|new| *new),
54 Self::Patched(patch) => {
55 if let Some(project) = project {
56 patch.apply_to(project);
57 }
58 }
59 }
60 }
61}
62
63impl ProjectPatch {
64 fn between(old: &Project, new: &Project) -> Self {
65 let mut scenes: Vec<_> = new
66 .scenes_instances_grid
67 .iter()
68 .filter(|(location, scene)| old.scenes_instances_grid.get(location) != Some(scene))
69 .map(|(location, scene)| (*location, Some(scene.clone())))
70 .collect();
71 scenes.extend(
72 old.scenes_instances_grid
73 .keys()
74 .filter(|location| !new.scenes_instances_grid.contains_key(location))
75 .map(|location| (*location, None)),
76 );
77 let svg = (old.svg != new.svg).then(|| new.svg.clone());
78 let new_rest = rest(new);
79 let rest = (rest(old) != new_rest).then(|| Box::new(new_rest));
80 Self { scenes, svg, rest }
81 }
82
83 fn apply_to(self, project: &mut Project) {
84 if let Some(rest) = self.rest {
85 let scenes = std::mem::take(&mut project.scenes_instances_grid);
86 let svg = project.svg.take();
87 *project = *rest;
88 project.scenes_instances_grid = scenes;
89 project.svg = svg;
90 }
91 if let Some(svg) = self.svg {
92 project.svg = svg;
93 }
94 for (location, scene) in self.scenes {
95 match scene {
96 Some(scene) => {
97 project.scenes_instances_grid.insert(location, scene);
98 }
99 None => {
100 project.scenes_instances_grid.remove(&location);
101 }
102 }
103 }
104 }
105}
106
107fn rest(project: &Project) -> Project {
111 let Project {
112 palette,
113 groups,
114 grid_width,
115 grid_height,
116 grid_highlight,
117 scenes_instances_grid: _,
118 svg: _,
119 channel_overwrites,
120 output_routings,
121 artnet_config,
122 tap_input_events,
123 blackout_input_events,
124 blackout_hold_input_events,
125 half_input_events,
126 double_input_events,
127 midi_active_mappings,
128 main_dimmer,
129 audio_input_device,
130 artnet_control_config,
131 osc_config,
132 } = project;
133 Project {
134 palette: palette.clone(),
135 groups: groups.clone(),
136 grid_width: *grid_width,
137 grid_height: *grid_height,
138 grid_highlight: *grid_highlight,
139 channel_overwrites: channel_overwrites.clone(),
140 output_routings: output_routings.clone(),
141 artnet_config: artnet_config.clone(),
142 tap_input_events: tap_input_events.clone(),
143 blackout_input_events: blackout_input_events.clone(),
144 blackout_hold_input_events: blackout_hold_input_events.clone(),
145 half_input_events: half_input_events.clone(),
146 double_input_events: double_input_events.clone(),
147 midi_active_mappings: midi_active_mappings.clone(),
148 main_dimmer: *main_dimmer,
149 audio_input_device: audio_input_device.clone(),
150 artnet_control_config: *artnet_control_config,
151 osc_config: *osc_config,
152 scenes_instances_grid: HashMap::new(),
153 svg: None,
154 }
155}