refactor(backend): optimize diff logic and integrate DiffResult

This commit is contained in:
spinline
2026-02-03 20:52:09 +03:00
parent f13a712ebd
commit 9d1aa8f7d2
2 changed files with 29 additions and 45 deletions

View File

@@ -1,35 +1,31 @@
use shared::{AppEvent, Torrent, TorrentUpdate}; use shared::{AppEvent, Torrent, TorrentUpdate};
pub fn diff_torrents(old: &[Torrent], new: &[Torrent]) -> Vec<AppEvent> { #[derive(Debug)]
// 1. Structural Change Check pub enum DiffResult {
// If length differs or any hash at specific index differs (simplistic view), send FullList. NoChange,
// Ideally we should track "Added/Removed", but for simplicity and robustness as per prompt "FullList for big changes", FullUpdate,
// we fallback to FullList on structural changes. Partial(Vec<AppEvent>),
}
pub fn diff_torrents(old: &[Torrent], new: &[Torrent]) -> DiffResult {
// 1. Structural Check (Length or Order changed)
if old.len() != new.len() { if old.len() != new.len() {
// Timestamp is needed for FullList? The definition is FullList(Vec, u64). return DiffResult::FullUpdate;
// We'll let the caller handle the timestamp or pass it in?
// AppEvent in shared::lib.rs is FullList(Vec<Torrent>, u64).
// We'll return just the list decision here, or constructs events.
// Let's assume caller adds the u64 (disk space/timestamp).
// Actually, let's keep it simple: Return Option<Vec<AppEvent>>.
// But simply returning "NeedFullList" signal is easier if we can't accept u64 here.
// Let's change signature to return an enum or boolean flag if FullList needed.
return vec![]; // Special signal: Empty vec means "No diffs" or "Caller handles FullList"?
// This function is tricky if we don't have the u64.
} }
// Check for hash mismatch (order changed)
for (i, t) in new.iter().enumerate() { for (i, t) in new.iter().enumerate() {
if old[i].hash != t.hash { if old[i].hash != t.hash {
return vec![]; // Signal Full List needed return DiffResult::FullUpdate;
} }
} }
// 2. Field Updates
let mut events = Vec::new(); let mut events = Vec::new();
for (i, new_t) in new.iter().enumerate() { for (i, new_t) in new.iter().enumerate() {
let old_t = &old[i]; let old_t = &old[i];
// Initialize with all None
let mut update = TorrentUpdate { let mut update = TorrentUpdate {
hash: new_t.hash.clone(), hash: new_t.hash.clone(),
name: None, name: None,
@@ -45,6 +41,7 @@ pub fn diff_torrents(old: &[Torrent], new: &[Torrent]) -> Vec<AppEvent> {
let mut has_changes = false; let mut has_changes = false;
// Compare fields
if old_t.name != new_t.name { if old_t.name != new_t.name {
update.name = Some(new_t.name.clone()); update.name = Some(new_t.name.clone());
has_changes = true; has_changes = true;
@@ -61,7 +58,6 @@ pub fn diff_torrents(old: &[Torrent], new: &[Torrent]) -> Vec<AppEvent> {
update.up_rate = Some(new_t.up_rate); update.up_rate = Some(new_t.up_rate);
has_changes = true; has_changes = true;
} }
// Floating point comparison with epsilon
if (old_t.percent_complete - new_t.percent_complete).abs() > 0.01 { if (old_t.percent_complete - new_t.percent_complete).abs() > 0.01 {
update.percent_complete = Some(new_t.percent_complete); update.percent_complete = Some(new_t.percent_complete);
has_changes = true; has_changes = true;
@@ -88,9 +84,10 @@ pub fn diff_torrents(old: &[Torrent], new: &[Torrent]) -> Vec<AppEvent> {
} }
} }
if !events.is_empty() { if events.is_empty() {
tracing::debug!("Generated {} updates", events.len()); DiffResult::NoChange
} else {
tracing::debug!("Generated {} partial updates", events.len());
DiffResult::Partial(events)
} }
events
} }

View File

@@ -91,30 +91,17 @@ async fn main() {
.unwrap() .unwrap()
.as_secs(); .as_secs();
let mut structural_change = false; match diff::diff_torrents(&previous_torrents, &new_torrents) {
if previous_torrents.len() != new_torrents.len() { diff::DiffResult::FullUpdate => {
structural_change = true; let _ =
} else { event_bus_tx.send(AppEvent::FullList(new_torrents.clone(), now));
// Check for order/hash change
for (i, t) in new_torrents.iter().enumerate() {
if previous_torrents[i].hash != t.hash {
structural_change = true;
break;
} }
} diff::DiffResult::Partial(updates) => {
}
if structural_change {
// Structural change -> Send FullList
let _ = event_bus_tx.send(AppEvent::FullList(new_torrents.clone(), now));
} else {
// Same structure -> Calculate partial updates
let updates = diff::diff_torrents(&previous_torrents, &new_torrents);
if !updates.is_empty() {
for update in updates { for update in updates {
let _ = event_bus_tx.send(update); let _ = event_bus_tx.send(update);
} }
} }
diff::DiffResult::NoChange => {}
} }
previous_torrents = new_torrents; previous_torrents = new_torrents;