use crate::entity::EntityState;
use std::collections::HashMap;
#[derive(Debug)]
pub struct ChangeTracker {
entries: Vec<TrackerEntry>,
auto_detect_changes: bool,
next_id: u64,
}
pub struct EntityEntry {
pub entry_id: u64,
pub type_id: std::any::TypeId,
pub type_name: String,
pub state: EntityState,
pub modified_properties: Vec<String>,
}
struct TrackerEntry {
id: u64,
type_id: std::any::TypeId,
type_name: String,
state: EntityState,
snapshot: HashMap<String, PropertySnapshot>,
}
#[derive(Debug, Clone)]
struct PropertySnapshot {
serialized: String,
}
impl std::fmt::Debug for TrackerEntry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TrackerEntry")
.field("id", &self.id)
.field("type_name", &self.type_name)
.field("state", &self.state)
.finish()
}
}
impl ChangeTracker {
pub fn new() -> Self {
Self {
entries: Vec::new(),
auto_detect_changes: true,
next_id: 0,
}
}
pub fn track_entity_with_snapshot(
&mut self,
type_id: std::any::TypeId,
type_name: &str,
state: EntityState,
snapshot: HashMap<String, String>,
) -> u64 {
let id = self.next_id;
self.next_id += 1;
self.entries.push(TrackerEntry {
id,
type_id,
type_name: type_name.to_string(),
state,
snapshot: snapshot
.into_iter()
.map(|(k, v)| (k, PropertySnapshot { serialized: v }))
.collect(),
});
id
}
pub fn track_entity(
&mut self,
type_id: std::any::TypeId,
type_name: &str,
state: EntityState,
) -> u64 {
self.track_entity_with_snapshot(type_id, type_name, state, HashMap::new())
}
pub fn detect_changes_with_properties(
&mut self,
current_properties: &[(u64, HashMap<String, String>)],
) {
let current_map: HashMap<u64, &HashMap<String, String>> = current_properties
.iter()
.map(|(id, props)| (*id, props))
.collect();
for entry in &mut self.entries {
if entry.state != EntityState::Unchanged {
continue;
}
if let Some(current) = current_map.get(&entry.id) {
for (prop_name, snapshot) in &entry.snapshot {
let changed = match current.get(prop_name) {
Some(current_val) => current_val != &snapshot.serialized,
None => true, };
if changed {
entry.state = EntityState::Modified;
break; }
}
}
}
}
pub fn update_snapshot(&mut self, entry_id: u64, properties: HashMap<String, String>) {
if let Some(entry) = self.entries.iter_mut().find(|e| e.id == entry_id) {
entry.snapshot = properties
.into_iter()
.map(|(k, v)| (k, PropertySnapshot { serialized: v }))
.collect();
}
}
pub fn detect_changes(&mut self) {
}
pub fn has_changes(&self) -> bool {
self.entries.iter().any(|e| {
matches!(
e.state,
EntityState::Added | EntityState::Modified | EntityState::Deleted
)
})
}
pub fn clear(&mut self) {
self.entries.clear();
}
pub fn entries(&self) -> Vec<EntityEntry> {
self.entries
.iter()
.map(|e| EntityEntry {
entry_id: e.id,
type_id: e.type_id,
type_name: e.type_name.clone(),
state: e.state,
modified_properties: Vec::new(),
})
.collect()
}
pub fn count_by_state(&self, state: EntityState) -> usize {
self.entries.iter().filter(|e| e.state == state).count()
}
pub fn entries_by_state(&self, state: EntityState) -> Vec<EntityEntry> {
self.entries()
.into_iter()
.filter(|e| e.state == state)
.collect()
}
pub fn accept_all_changes(&mut self) {
self.entries.retain(|e| e.state != EntityState::Deleted);
for entry in &mut self.entries {
if entry.state == EntityState::Added || entry.state == EntityState::Modified {
entry.state = EntityState::Unchanged;
}
}
}
pub fn reject_all_changes(&mut self) {
self.entries.retain(|e| e.state != EntityState::Added);
for entry in &mut self.entries {
if entry.state == EntityState::Modified || entry.state == EntityState::Deleted {
entry.state = EntityState::Unchanged;
}
}
}
pub fn detach(&mut self, entry_id: u64) {
self.entries.retain(|e| e.id != entry_id);
}
pub fn is_auto_detect_changes_enabled(&self) -> bool {
self.auto_detect_changes
}
pub fn set_auto_detect_changes(&mut self, enabled: bool) {
self.auto_detect_changes = enabled;
}
}
impl Default for ChangeTracker {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct TrackedEntity<T> {
pub entity: T,
pub entry_id: u64,
pub state: EntityState,
}
impl<T> TrackedEntity<T> {
pub fn new(entity: T, entry_id: u64, state: EntityState) -> Self {
Self {
entity,
entry_id,
state,
}
}
}