1use gled_model::collection::Collections;
5
6use crate::{
7 assets::collections_from, message::ServerMessage, project_update::ProjectUpdate,
8 state::EngineState,
9};
10
11#[derive(Debug, Clone, Default)]
12pub struct StateReplica {
13 pub state: EngineState,
14 pub applied_sequence: u64,
16 pub complete: bool,
18 pub assets: Collections,
20 pub(crate) assets_complete: bool,
22 pub project_changes: u64,
26}
27
28impl StateReplica {
29 pub fn apply(&mut self, message: &ServerMessage) -> bool {
31 match message {
32 ServerMessage::State {
33 state,
34 applied_sequence,
35 } => {
36 self.state = EngineState::clone(state);
37 self.applied_sequence = *applied_sequence;
38 self.complete = true;
39 self.project_changes += 1;
40 true
41 }
42 ServerMessage::Update {
43 state,
44 project,
45 applied_sequence,
46 } if self.complete => {
47 if !matches!(project, ProjectUpdate::Unchanged) {
48 self.project_changes += 1;
49 }
50 let mut replica_project = self.state.project.take();
51 project.clone().apply_to(&mut replica_project);
52 self.state = EngineState {
53 project: replica_project,
54 ..EngineState::clone(state)
55 };
56 self.applied_sequence = *applied_sequence;
57 true
58 }
59 ServerMessage::Assets(assets) => {
60 self.assets = collections_from(assets.clone());
61 self.assets_complete = true;
62 true
63 }
64 ServerMessage::AssetsChanged(changes) if self.assets_complete => {
65 changes.clone().apply_to(&mut self.assets);
66 true
67 }
68 ServerMessage::Timing { beat, render } if self.complete => {
69 self.state.beat = *beat;
70 self.state.render = *render;
71 true
72 }
73 _ => false,
74 }
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use std::sync::Arc;
81
82 use gled_model::asset::project::Project;
83
84 use super::*;
85
86 fn update(project: ProjectUpdate) -> ServerMessage {
87 ServerMessage::Update {
88 state: Arc::default(),
89 project,
90 applied_sequence: 0,
91 }
92 }
93
94 #[test]
95 fn only_messages_that_may_change_the_project_are_counted() {
96 let mut replica = StateReplica::default();
97 replica.apply(&ServerMessage::State {
98 state: Arc::default(),
99 applied_sequence: 0,
100 });
101 assert_eq!(replica.project_changes, 1, "a full state");
102
103 replica.apply(&update(ProjectUpdate::Unchanged));
104 assert_eq!(replica.project_changes, 1, "an update without one");
105
106 let project = Project::default();
107 replica.apply(&update(ProjectUpdate::between(None, Some(&project))));
108 assert_eq!(replica.project_changes, 2, "another project");
109 assert_eq!(replica.state.project.as_deref(), Some(&project));
110 }
111}