feat: media API (video/bbox/thumbnail), UUID unification, dot matrix text, portal fixes, API dictionary V1.3
This commit is contained in:
@@ -1,9 +1,7 @@
|
||||
pub mod file_manager;
|
||||
pub mod output_dir;
|
||||
pub mod snapshot_manager;
|
||||
pub mod uuid;
|
||||
|
||||
pub use file_manager::FileManager;
|
||||
pub use output_dir::OutputDir;
|
||||
pub use snapshot_manager::SnapshotManager;
|
||||
pub use uuid::compute_uuid;
|
||||
|
||||
@@ -1,268 +0,0 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::core::config;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum SnapshotTier {
|
||||
Hot,
|
||||
Warm,
|
||||
Cold,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SnapshotTier {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
SnapshotTier::Hot => write!(f, "hot"),
|
||||
SnapshotTier::Warm => write!(f, "warm"),
|
||||
SnapshotTier::Cold => write!(f, "cold"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct SnapshotStatus {
|
||||
pub file_uuid: String,
|
||||
pub tier: SnapshotTier,
|
||||
pub hits: u64,
|
||||
pub types: Vec<String>,
|
||||
pub generated_at: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SnapshotManager {
|
||||
base_dir: PathBuf,
|
||||
}
|
||||
|
||||
impl SnapshotManager {
|
||||
pub fn new(user_dir: &str) -> Self {
|
||||
let snapshot_dir_name = config::snapshot::SNAPSHOT_DIR_NAME.as_str();
|
||||
let base_dir = Path::new(user_dir).join(snapshot_dir_name);
|
||||
Self { base_dir }
|
||||
}
|
||||
|
||||
pub fn base_dir(&self) -> &Path {
|
||||
&self.base_dir
|
||||
}
|
||||
|
||||
pub fn file_snapshot_dir(&self, file_uuid: &str) -> PathBuf {
|
||||
self.base_dir.join(file_uuid)
|
||||
}
|
||||
|
||||
pub fn file_type_dir(&self, file_uuid: &str, snapshot_type: &str) -> PathBuf {
|
||||
self.base_dir.join(file_uuid).join(snapshot_type)
|
||||
}
|
||||
|
||||
pub fn identity_snapshot_dir(&self, identity_uuid: &str) -> PathBuf {
|
||||
self.base_dir.join("identities").join(identity_uuid)
|
||||
}
|
||||
|
||||
pub fn identity_face_dir(&self, identity_uuid: &str) -> PathBuf {
|
||||
self.base_dir
|
||||
.join("identities")
|
||||
.join(identity_uuid)
|
||||
.join("faces")
|
||||
}
|
||||
|
||||
pub fn ensure_file_dirs(&self, file_uuid: &str) -> std::io::Result<()> {
|
||||
let dir = self.file_snapshot_dir(file_uuid);
|
||||
std::fs::create_dir_all(&dir)?;
|
||||
for snap_type in ["faces", "logos", "products", "ocr"] {
|
||||
std::fs::create_dir_all(dir.join(snap_type))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn ensure_identity_dirs(&self, identity_uuid: &str) -> std::io::Result<()> {
|
||||
let dir = self.identity_snapshot_dir(identity_uuid);
|
||||
std::fs::create_dir_all(&dir)?;
|
||||
std::fs::create_dir_all(dir.join("faces"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compute_tier(hits: u64) -> SnapshotTier {
|
||||
let threshold = *config::snapshot::HOT_THRESHOLD;
|
||||
if hits >= threshold {
|
||||
SnapshotTier::Hot
|
||||
} else if hits > 0 {
|
||||
SnapshotTier::Warm
|
||||
} else {
|
||||
SnapshotTier::Cold
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tier_ttl(&self, tier: SnapshotTier) -> u64 {
|
||||
match tier {
|
||||
SnapshotTier::Hot => *config::snapshot::HOT_TTL_SECS,
|
||||
SnapshotTier::Warm => *config::snapshot::WARM_TTL_SECS,
|
||||
SnapshotTier::Cold => 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn snapshot_exists(&self, file_uuid: &str, snapshot_type: &str) -> bool {
|
||||
self.file_type_dir(file_uuid, snapshot_type).exists()
|
||||
}
|
||||
|
||||
pub fn list_snapshot_types(&self, file_uuid: &str) -> Vec<String> {
|
||||
let dir = self.file_snapshot_dir(file_uuid);
|
||||
if !dir.exists() {
|
||||
return Vec::new();
|
||||
}
|
||||
std::fs::read_dir(&dir)
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.filter_map(|e| e.ok())
|
||||
.filter(|e| e.path().is_dir())
|
||||
.filter_map(|e| e.file_name().to_str().map(String::from))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn remove_file_snapshots(&self, file_uuid: &str) -> std::io::Result<()> {
|
||||
let dir = self.file_snapshot_dir(file_uuid);
|
||||
if dir.exists() {
|
||||
std::fs::remove_dir_all(&dir)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn remove_identity_snapshots(&self, identity_uuid: &str) -> std::io::Result<()> {
|
||||
let dir = self.identity_snapshot_dir(identity_uuid);
|
||||
if dir.exists() {
|
||||
std::fs::remove_dir_all(&dir)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn create_test_manager() -> (SnapshotManager, tempfile::TempDir) {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let manager = SnapshotManager::new(temp_dir.path().to_str().unwrap());
|
||||
(manager, temp_dir)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compute_tier_hot() {
|
||||
assert_eq!(SnapshotManager::compute_tier(5), SnapshotTier::Hot);
|
||||
assert_eq!(SnapshotManager::compute_tier(10), SnapshotTier::Hot);
|
||||
assert_eq!(SnapshotManager::compute_tier(100), SnapshotTier::Hot);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compute_tier_warm() {
|
||||
assert_eq!(SnapshotManager::compute_tier(1), SnapshotTier::Warm);
|
||||
assert_eq!(SnapshotManager::compute_tier(4), SnapshotTier::Warm);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compute_tier_cold() {
|
||||
assert_eq!(SnapshotManager::compute_tier(0), SnapshotTier::Cold);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tier_display() {
|
||||
assert_eq!(SnapshotTier::Hot.to_string(), "hot");
|
||||
assert_eq!(SnapshotTier::Warm.to_string(), "warm");
|
||||
assert_eq!(SnapshotTier::Cold.to_string(), "cold");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ensure_file_dirs_creates_structure() {
|
||||
let (manager, _temp) = create_test_manager();
|
||||
let file_uuid = "test_file_123";
|
||||
|
||||
manager.ensure_file_dirs(file_uuid).unwrap();
|
||||
|
||||
assert!(manager.file_snapshot_dir(file_uuid).exists());
|
||||
assert!(manager.file_type_dir(file_uuid, "faces").exists());
|
||||
assert!(manager.file_type_dir(file_uuid, "logos").exists());
|
||||
assert!(manager.file_type_dir(file_uuid, "products").exists());
|
||||
assert!(manager.file_type_dir(file_uuid, "ocr").exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ensure_identity_dirs_creates_structure() {
|
||||
let (manager, _temp) = create_test_manager();
|
||||
let identity_uuid = "test_identity_456";
|
||||
|
||||
manager.ensure_identity_dirs(identity_uuid).unwrap();
|
||||
|
||||
assert!(manager.identity_snapshot_dir(identity_uuid).exists());
|
||||
assert!(manager.identity_face_dir(identity_uuid).exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list_snapshot_types_empty() {
|
||||
let (manager, _temp) = create_test_manager();
|
||||
let types = manager.list_snapshot_types("nonexistent");
|
||||
assert!(types.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list_snapshot_types_after_creation() {
|
||||
let (manager, _temp) = create_test_manager();
|
||||
let file_uuid = "test_file_789";
|
||||
|
||||
manager.ensure_file_dirs(file_uuid).unwrap();
|
||||
let types = manager.list_snapshot_types(file_uuid);
|
||||
|
||||
assert_eq!(types.len(), 4);
|
||||
assert!(types.contains(&"faces".to_string()));
|
||||
assert!(types.contains(&"logos".to_string()));
|
||||
assert!(types.contains(&"products".to_string()));
|
||||
assert!(types.contains(&"ocr".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove_file_snapshots() {
|
||||
let (manager, _temp) = create_test_manager();
|
||||
let file_uuid = "test_file_remove";
|
||||
|
||||
manager.ensure_file_dirs(file_uuid).unwrap();
|
||||
assert!(manager.file_snapshot_dir(file_uuid).exists());
|
||||
|
||||
manager.remove_file_snapshots(file_uuid).unwrap();
|
||||
assert!(!manager.file_snapshot_dir(file_uuid).exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove_identity_snapshots() {
|
||||
let (manager, _temp) = create_test_manager();
|
||||
let identity_uuid = "test_identity_remove";
|
||||
|
||||
manager.ensure_identity_dirs(identity_uuid).unwrap();
|
||||
assert!(manager.identity_snapshot_dir(identity_uuid).exists());
|
||||
|
||||
manager.remove_identity_snapshots(identity_uuid).unwrap();
|
||||
assert!(!manager.identity_snapshot_dir(identity_uuid).exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_snapshot_exists() {
|
||||
let (manager, _temp) = create_test_manager();
|
||||
let file_uuid = "test_exists";
|
||||
|
||||
assert!(!manager.snapshot_exists(file_uuid, "faces"));
|
||||
|
||||
manager.ensure_file_dirs(file_uuid).unwrap();
|
||||
assert!(manager.snapshot_exists(file_uuid, "faces"));
|
||||
assert!(!manager.snapshot_exists(file_uuid, "nonexistent"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tier_ttl() {
|
||||
let (manager, _temp) = create_test_manager();
|
||||
|
||||
let hot_ttl = manager.tier_ttl(SnapshotTier::Hot);
|
||||
assert_eq!(hot_ttl, *config::snapshot::HOT_TTL_SECS);
|
||||
|
||||
let warm_ttl = manager.tier_ttl(SnapshotTier::Warm);
|
||||
assert_eq!(warm_ttl, *config::snapshot::WARM_TTL_SECS);
|
||||
|
||||
let cold_ttl = manager.tier_ttl(SnapshotTier::Cold);
|
||||
assert_eq!(cold_ttl, 0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user