use gled_model::collection::Collections;
use crate::{
assets::collections_from, message::ServerMessage, project_update::ProjectUpdate,
state::EngineState,
};
#[derive(Debug, Clone, Default)]
pub struct StateReplica {
pub state: EngineState,
pub applied_sequence: u64,
pub complete: bool,
pub assets: Collections,
pub(crate) assets_complete: bool,
pub project_changes: u64,
pub assets_changes: u64,
}
impl StateReplica {
pub fn apply(&mut self, message: &ServerMessage) -> bool {
match message {
ServerMessage::State {
state,
applied_sequence,
} => {
self.state = EngineState::clone(state);
self.applied_sequence = *applied_sequence;
self.complete = true;
self.project_changes += 1;
true
}
ServerMessage::Update {
state,
project,
applied_sequence,
} if self.complete => {
if !matches!(project, ProjectUpdate::Unchanged) {
self.project_changes += 1;
}
let mut replica_project = self.state.project.take();
project.clone().apply_to(&mut replica_project);
self.state = EngineState {
project: replica_project,
..EngineState::clone(state)
};
self.applied_sequence = *applied_sequence;
true
}
ServerMessage::Assets(assets) => {
self.assets = collections_from(assets.clone());
self.assets_complete = true;
self.assets_changes += 1;
true
}
ServerMessage::AssetsChanged(changes) if self.assets_complete => {
changes.clone().apply_to(&mut self.assets);
self.assets_changes += 1;
true
}
ServerMessage::Timing { beat, render } if self.complete => {
self.state.beat = *beat;
self.state.render = *render;
true
}
_ => false,
}
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use gled_model::asset::project::Project;
use super::*;
fn update(project: ProjectUpdate) -> ServerMessage {
ServerMessage::Update {
state: Arc::default(),
project,
applied_sequence: 0,
}
}
#[test]
fn only_messages_that_may_change_the_project_are_counted() {
let mut replica = StateReplica::default();
replica.apply(&ServerMessage::State {
state: Arc::default(),
applied_sequence: 0,
});
assert_eq!(replica.project_changes, 1, "a full state");
replica.apply(&update(ProjectUpdate::Unchanged));
assert_eq!(replica.project_changes, 1, "an update without one");
let project = Project::default();
replica.apply(&update(ProjectUpdate::between(None, Some(&project))));
assert_eq!(replica.project_changes, 2, "another project");
assert_eq!(replica.state.project.as_deref(), Some(&project));
}
#[test]
fn asset_messages_are_counted() {
let mut replica = StateReplica::default();
replica.apply(&ServerMessage::AssetsChanged(Default::default()));
assert_eq!(replica.assets_changes, 0, "changes before the assets");
replica.apply(&ServerMessage::Assets(Vec::new()));
replica.apply(&ServerMessage::AssetsChanged(Default::default()));
assert_eq!(replica.assets_changes, 2);
}
}