use super::AggregateLoad;
use super::aggregates::{FileAggregateSet, load_aggregate_rows, load_aggregate_rows_for_keys};
use super::records::{StoredFileRecord, load_file_records, load_file_records_for_keys};
use chrono_tz::Tz;
use eyre::Result;
use rusqlite::Connection;
use std::collections::{HashMap, HashSet};
const FULL_SNAPSHOT_KEY_THRESHOLD: usize = 4_096;
pub(super) struct ScanIndexSnapshot {
records: HashMap<String, StoredFileRecord>,
aggregates: HashMap<(String, i64), FileAggregateSet>,
invalid_aggregates: HashSet<(String, i64)>,
}
impl ScanIndexSnapshot {
pub(super) fn load_selected(
connection: &Connection,
timezone: Tz,
session_keys: &[&str],
) -> Result<Self> {
if session_keys.len() > FULL_SNAPSHOT_KEY_THRESHOLD {
return Ok(Self::from_parts(
load_file_records(connection)?,
load_aggregate_rows(connection, timezone)?,
));
}
Ok(Self::from_parts(
load_file_records_for_keys(connection, session_keys)?,
load_aggregate_rows_for_keys(connection, timezone, session_keys)?,
))
}
fn from_parts(
records: HashMap<String, StoredFileRecord>,
aggregate_rows: super::aggregates::LoadedAggregateRows,
) -> Self {
Self {
records,
aggregates: aggregate_rows.rows,
invalid_aggregates: aggregate_rows.invalid,
}
}
pub(super) fn take_record(&mut self, session_key: &str) -> Option<StoredFileRecord> {
self.records.remove(session_key)
}
pub(super) fn take_aggregates(&mut self, record: &StoredFileRecord) -> AggregateLoad {
let key = (record.session_key.clone(), record.generation);
if self.invalid_aggregates.remove(&key) {
return AggregateLoad::MissingOrInvalid;
}
let aggregates = self.aggregates.remove(&key).unwrap_or_default();
if !aggregates.matches_expected_totals(&record.total, &record.fallback_total) {
return AggregateLoad::MissingOrInvalid;
}
AggregateLoad::Valid(aggregates)
}
}