Skip to main content

fraiseql_server/files/
traits.rs

1//! Testing seams for file operations
2
3use std::{collections::HashMap, time::Duration};
4
5use async_trait::async_trait;
6use bytes::Bytes;
7
8use crate::files::{
9    config::{FileConfig, ProcessingConfig},
10    error::{FileError, ProcessingError, ScanError, StorageError},
11    processing::ProcessedImages,
12};
13
14/// Storage backend abstraction for testing
15#[async_trait]
16pub trait StorageBackend: Send + Sync {
17    fn name(&self) -> &'static str;
18
19    async fn upload(
20        &self,
21        key: &str,
22        data: Bytes,
23        content_type: &str,
24        metadata: Option<&StorageMetadata>,
25    ) -> Result<StorageResult, StorageError>;
26
27    async fn download(&self, key: &str) -> Result<Bytes, StorageError>;
28    async fn delete(&self, key: &str) -> Result<(), StorageError>;
29    async fn exists(&self, key: &str) -> Result<bool, StorageError>;
30    async fn metadata(&self, key: &str) -> Result<StorageMetadata, StorageError>;
31    async fn signed_url(&self, key: &str, expiry: Duration) -> Result<String, StorageError>;
32    fn public_url(&self, key: &str) -> String;
33}
34
35#[derive(Debug, Clone)]
36pub struct StorageMetadata {
37    pub content_type:   String,
38    pub content_length: u64,
39    pub etag:           Option<String>,
40    pub last_modified:  Option<chrono::DateTime<chrono::Utc>>,
41    pub custom:         HashMap<String, String>,
42}
43
44#[derive(Debug)]
45pub struct StorageResult {
46    pub key:  String,
47    pub url:  String,
48    pub etag: Option<String>,
49    pub size: u64,
50}
51
52/// File validator abstraction for testing
53pub trait FileValidator: Send + Sync {
54    fn validate(
55        &self,
56        data: &Bytes,
57        declared_type: &str,
58        filename: &str,
59        config: &FileConfig,
60    ) -> Result<ValidatedFile, FileError>;
61}
62
63/// Image processor abstraction for testing
64#[async_trait]
65pub trait ImageProcessor: Send + Sync {
66    async fn process(
67        &self,
68        data: &Bytes,
69        config: &ProcessingConfig,
70    ) -> Result<ProcessedImages, ProcessingError>;
71}
72
73/// Malware scanner abstraction (optional external service)
74#[async_trait]
75pub trait MalwareScanner: Send + Sync {
76    async fn scan(&self, data: &Bytes) -> Result<ScanResult, ScanError>;
77}
78
79#[derive(Debug)]
80pub struct ScanResult {
81    pub clean:           bool,
82    pub threat_name:     Option<String>,
83    pub scanner_version: String,
84}
85
86#[derive(Debug)]
87pub struct ValidatedFile {
88    pub content_type:       String,
89    pub sanitized_filename: String,
90    pub size:               usize,
91    pub detected_type:      Option<String>,
92}