//! Computed Fields Module
//!
//! Generated by backbone-schema.
//!
//! Provides virtual/computed fields for entities based on their data and relationships.
//! These fields are not stored in the database but calculated on-demand.
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
#[allow(unused_imports)]
use chrono::{DateTime, Utc, NaiveDate};
use crate::domain::entity::*;
/// Trait for entities that have computed fields
pub trait HasComputedFields {
/// Get all computed field values
fn computed_fields(&self) -> ComputedFieldValues;
/// Get a specific computed field value
fn computed_field(&self, name: &str) -> Option<serde_json::Value>;
}
/// Collection of computed field values
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ComputedFieldValues {
pub fields: HashMap<String, serde_json::Value>,
}
impl ComputedFieldValues {
pub fn new() -> Self {
Self { fields: HashMap::new() }
}
pub fn set(&mut self, name: impl Into<String>, value: serde_json::Value) {
self.fields.insert(name.into(), value);
}
pub fn get(&self, name: &str) -> Option<&serde_json::Value> {
self.fields.get(name)
}
pub fn get_bool(&self, name: &str) -> Option<bool> {
self.fields.get(name).and_then(|v| v.as_bool())
}
pub fn get_str(&self, name: &str) -> Option<&str> {
self.fields.get(name).and_then(|v| v.as_str())
}
pub fn get_i64(&self, name: &str) -> Option<i64> {
self.fields.get(name).and_then(|v| v.as_i64())
}
}
/// Computed fields for Bucket entity
impl HasComputedFields for Bucket {
fn computed_fields(&self) -> ComputedFieldValues {
let mut fields = ComputedFieldValues::new();
// is_empty
fields.set("is_empty", serde_json::json!(self.file_count == 0));
// usage_bytes
fields.set("usage_bytes", serde_json::json!(self.total_size_bytes));
// average_file_size
fields.set("average_file_size", serde_json::json!(self.file_count > 0));
// is_accessible
fields.set("is_accessible", serde_json::json!(self.status == BucketStatus::Active || self.status == BucketStatus::Readonly));
// accepts_uploads
fields.set("accepts_uploads", serde_json::json!(self.status == BucketStatus::Active));
fields
}
fn computed_field(&self, name: &str) -> Option<serde_json::Value> {
self.computed_fields().fields.get(name).cloned()
}
}
/// Computed fields for FileShare entity
impl HasComputedFields for FileShare {
fn computed_fields(&self) -> ComputedFieldValues {
let mut fields = ComputedFieldValues::new();
// is_valid
// WARNING: Expression references computed fields not yet defined
// Implement via AsyncComputedFields trait or repository query.
fields.set("is_valid", serde_json::json!(null));
// share_url
fields.set("share_url", serde_json::json!(format!("/share/{}", self.token)));
// days_until_expiry
fields.set("days_until_expiry", serde_json::json!(self.expires_at.is_some()));
// downloads_remaining
fields.set("downloads_remaining", serde_json::json!(self.max_downloads.is_some()));
// is_password_protected
fields.set("is_password_protected", serde_json::json!(self.share_type == ShareType::Password && self.password_hash.is_some()));
// is_expired
fields.set("is_expired", serde_json::json!(self.expires_at.is_some() && self.expires_at <= Some(chrono::Utc::now())));
// has_downloads_remaining
fields.set("has_downloads_remaining", serde_json::json!(self.max_downloads.is_none() || self.download_count < self.max_downloads.unwrap()));
fields
}
fn computed_field(&self, name: &str) -> Option<serde_json::Value> {
self.computed_fields().fields.get(name).cloned()
}
}
/// Computed fields for StoredFile entity
impl HasComputedFields for StoredFile {
fn computed_fields(&self) -> ComputedFieldValues {
let mut fields = ComputedFieldValues::new();
// compression_ratio
fields.set("compression_ratio", serde_json::json!(self.original_size.is_some()));
// is_image
fields.set("is_image", serde_json::json!(self.mime_type.starts_with("image/")));
// is_video
fields.set("is_video", serde_json::json!(self.mime_type.starts_with("video/")));
// is_document
fields.set("is_document", serde_json::json!(self.mime_type.starts_with("application/pdf") || self.mime_type.contains("document")));
// full_path
// WARNING: Expression references fields not on this entity: ["bucket"]
// These may be on a related entity or junction table.
// Implement via AsyncComputedFields trait or repository query.
fields.set("full_path", serde_json::json!(null));
// age_days
// WARNING: metadata.created_at not available on JsonValue
// Implement via AsyncComputedFields trait or repository query.
fields.set("age_days", serde_json::json!(null));
// is_accessible
fields.set("is_accessible", serde_json::json!(self.status == FileStatus::Active));
// is_safe
fields.set("is_safe", serde_json::json!(self.is_scanned && self.threat_level.is_none() || self.threat_level == Some(ThreatLevel::Safe)));
fields
}
fn computed_field(&self, name: &str) -> Option<serde_json::Value> {
self.computed_fields().fields.get(name).cloned()
}
}
#[cfg(test)]
mod tests {
#[allow(unused_imports)]
use super::*;
#[test]
fn test_bucket_computed_fields() {
// TODO: Add test implementation for Bucket
}
#[test]
fn test_file_share_computed_fields() {
// TODO: Add test implementation for FileShare
}
#[test]
fn test_stored_file_computed_fields() {
// TODO: Add test implementation for StoredFile
}
}