use super::*;
pub(super) const SCHEMA: &str = include_str!("schema.sql");
pub(super) const CURRENT_SCHEMA_VERSION: i64 = 13;
pub(super) const LEGACY_PRERELEASE_COLUMNS: &[&str] = &["classification", "is_text"];
pub struct Database {
pub(crate) conn: Connection,
pub(in crate::db) path: PathBuf,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ValueEnum)]
pub enum SearchMode {
Auto,
Fts,
Literal,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ValueEnum)]
pub enum TimelineSort {
Asc,
Desc,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ValueEnum)]
#[serde(rename_all = "snake_case")]
pub enum RetrievalKind {
Text,
Html,
Rtf,
Url,
File,
Image,
Pdf,
Binary,
Other,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct RetrievalFilters {
pub(in crate::db) since: Option<String>,
pub(in crate::db) until: Option<String>,
pub(in crate::db) hours: Option<u32>,
pub(in crate::db) app: Option<String>,
pub(in crate::db) bundle_id: Option<String>,
pub(in crate::db) kind: Option<RetrievalKind>,
pub(in crate::db) has_text: bool,
pub(in crate::db) has_url: bool,
pub(in crate::db) has_file_url: bool,
pub(in crate::db) has_image: bool,
pub(in crate::db) has_pdf: bool,
pub(in crate::db) min_bytes: Option<usize>,
pub(in crate::db) max_bytes: Option<usize>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
pub(crate) struct CaptureSettings {
pub(in crate::db) paused: bool,
pub(in crate::db) retention_seconds: Option<u64>,
pub(in crate::db) api_key_filter_enabled: bool,
pub(in crate::db) ocr_enabled: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)]
pub(crate) struct CapturePolicy {
pub(in crate::db) settings: CaptureSettings,
pub(in crate::db) ignored_bundle_ids: Vec<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "snake_case")]
pub(crate) enum CaptureSkipReason {
ApiKeyFilter,
RestoredSnapshot,
}
#[derive(Debug, Clone)]
pub(crate) enum CaptureStoreOutcome {
Stored(crate::model::CaptureStoreResult),
Skipped(CaptureSkipReason),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct SnapshotDeletionReport {
pub(in crate::db) snapshot_id: i64,
pub(in crate::db) item_count: usize,
pub(in crate::db) representation_count: usize,
pub(in crate::db) capture_event_count: usize,
pub(in crate::db) total_bytes: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct PurgeReport {
pub(in crate::db) older_than_seconds: u64,
pub(in crate::db) dry_run: bool,
pub(in crate::db) snapshot_count: usize,
pub(in crate::db) item_count: usize,
pub(in crate::db) representation_count: usize,
pub(in crate::db) capture_event_count: usize,
pub(in crate::db) total_bytes: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct OcrCandidate {
pub(in crate::db) raw_sha256: String,
pub(in crate::db) blob_value: Vec<u8>,
pub(in crate::db) snapshot_count: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct OcrStatusReport {
pub(in crate::db) pending: usize,
pub(in crate::db) ready: usize,
pub(in crate::db) failed: usize,
pub(in crate::db) skipped: usize,
pub(in crate::db) snapshots_with_ocr_text: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct OcrRunReport {
pub(in crate::db) processed: usize,
pub(in crate::db) ready: usize,
pub(in crate::db) failed: usize,
pub(in crate::db) skipped: usize,
pub(in crate::db) remaining_pending: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct StorageFileSizes {
pub(crate) db: u64,
pub(crate) wal: u64,
pub(crate) shm: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct StorageCheckpointReport {
pub(crate) busy: i64,
pub(crate) log: i64,
pub(crate) checkpointed: i64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct StorageCompactReport {
pub(crate) db_path: String,
pub(crate) before: StorageFileSizes,
pub(crate) after: StorageFileSizes,
pub(crate) total_before_bytes: u64,
pub(crate) total_after_bytes: u64,
pub(crate) reclaimed_bytes: u64,
pub(crate) estimated_reclaimable_bytes: u64,
pub(crate) page_count: usize,
pub(crate) freelist_count: usize,
pub(crate) checkpoint: StorageCheckpointReport,
pub(crate) dry_run: bool,
pub(crate) completed: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub(crate) struct ImageOptimizationReport {
pub(crate) dry_run: bool,
pub(crate) format: &'static str,
pub(crate) scanned_rows: usize,
pub(crate) compressed_rows: usize,
pub(crate) skipped_rows: usize,
pub(crate) conflict_count: usize,
pub(crate) original_bytes: usize,
pub(crate) optimized_bytes: usize,
pub(crate) logical_saved_bytes: usize,
pub(crate) compact_run: bool,
pub(crate) compact: Option<StorageCompactReport>,
pub(crate) compact_error: Option<String>,
pub(crate) filesystem_saved_bytes: u64,
pub(crate) filesystem_growth_bytes: u64,
pub(crate) compact_recommended: bool,
}
#[derive(Debug, Clone, Serialize)]
pub struct SearchResults {
pub(in crate::db) mode_used: SearchMode,
pub(in crate::db) hits: Vec<SearchHit>,
pub(in crate::db) has_more: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct StatsReport {
pub snapshot_count: usize,
pub capture_event_count: usize,
pub unique_app_count: usize,
pub total_bytes: usize,
pub average_bytes_per_snapshot: f64,
pub average_captures_per_snapshot: f64,
pub dedupe_ratio: f64,
pub first_observed_at: Option<String>,
pub last_observed_at: Option<String>,
pub archive_span_seconds: Option<i64>,
pub most_recopied_snapshot: Option<StatsSnapshotLeaderboardEntry>,
pub kind_breakdown: Vec<StatsKindBreakdownEntry>,
pub top_apps: Vec<StatsAppEntry>,
pub busiest_hours: Vec<StatsTimeBucketEntry>,
pub busiest_weekdays: Vec<StatsTimeBucketEntry>,
pub largest_snapshots: Vec<StatsSnapshotLeaderboardEntry>,
pub most_captured_snapshots: Vec<StatsSnapshotLeaderboardEntry>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct StatsKindBreakdownEntry {
pub kind: String,
pub snapshot_count: usize,
pub total_bytes: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct StatsAppEntry {
pub app: String,
pub capture_event_count: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct StatsTimeBucketEntry {
pub bucket: String,
pub capture_event_count: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct StatsSnapshotLeaderboardEntry {
pub snapshot_id: i64,
pub capture_count: usize,
pub kind: String,
pub preview_text: String,
pub app_name: Option<String>,
pub last_observed_at: String,
pub total_bytes: usize,
}
#[derive(Debug, Clone)]
pub(crate) struct Page<T> {
pub(in crate::db) items: Vec<T>,
pub(in crate::db) has_more: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct RecentCursorState {
pub(in crate::db) last_seen_at: String,
pub(in crate::db) snapshot_id: i64,
}
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct SearchCursorState {
pub(in crate::db) mode_used: SearchMode,
pub(in crate::db) score: Option<f64>,
pub(in crate::db) last_seen_at: String,
pub(in crate::db) snapshot_id: i64,
}
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct TimelineCursorState {
pub(in crate::db) observed_at: String,
pub(in crate::db) event_id: i64,
}