Skip to main content

agent_diva_files/
config.rs

1//! Configuration for file management system
2
3use serde::{Deserialize, Serialize};
4use std::path::PathBuf;
5
6/// Default maximum file size (500MB for UI)
7pub const DEFAULT_MAX_FILE_SIZE: u64 = 500 * 1024 * 1024;
8
9/// Default maximum total storage size (10GB)
10pub const DEFAULT_MAX_TOTAL_SIZE: u64 = 10 * 1024 * 1024 * 1024;
11
12/// Default cleanup interval in seconds (1 hour)
13pub const DEFAULT_CLEANUP_INTERVAL_SECS: u64 = 3600;
14
15/// Configuration for the file management system
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct FileConfig {
18    /// Base path for file storage
19    #[serde(default = "default_storage_path")]
20    pub storage_path: PathBuf,
21
22    /// Maximum size for a single file
23    #[serde(default = "default_max_file_size")]
24    pub max_file_size: u64,
25
26    /// Maximum total storage size
27    #[serde(default = "default_max_total_size")]
28    pub max_total_size: u64,
29
30    /// Whether to enable deduplication
31    #[serde(default = "default_true")]
32    pub deduplication: bool,
33
34    /// Cleanup strategy
35    #[serde(default)]
36    pub cleanup: CleanupConfig,
37
38    /// Channel-specific configuration
39    #[serde(default)]
40    pub channels: ChannelConfigs,
41}
42
43impl Default for FileConfig {
44    fn default() -> Self {
45        Self {
46            storage_path: default_storage_path(),
47            max_file_size: DEFAULT_MAX_FILE_SIZE,
48            max_total_size: DEFAULT_MAX_TOTAL_SIZE,
49            deduplication: true,
50            cleanup: CleanupConfig::default(),
51            channels: ChannelConfigs::default(),
52        }
53    }
54}
55
56impl FileConfig {
57    /// Create configuration with custom storage path
58    pub fn with_path(path: impl Into<PathBuf>) -> Self {
59        Self {
60            storage_path: path.into(),
61            ..Default::default()
62        }
63    }
64
65    /// Get the data directory path
66    pub fn data_dir(&self) -> PathBuf {
67        self.storage_path.join("data")
68    }
69
70    /// Get the index file path
71    pub fn index_path(&self) -> PathBuf {
72        self.storage_path.join("index.jsonl")
73    }
74
75    /// Get the config file path
76    pub fn config_path(&self) -> PathBuf {
77        self.storage_path.join("config.json")
78    }
79}
80
81/// Cleanup configuration
82#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct CleanupConfig {
84    /// Cleanup strategy
85    #[serde(default)]
86    pub strategy: CleanupStrategy,
87
88    /// Maximum age in days before a file can be cleaned up
89    #[serde(default = "default_max_age_days")]
90    pub max_age_days: u32,
91
92    /// Minimum reference count for cleanup eligibility
93    #[serde(default)]
94    pub min_ref_count: i32,
95
96    /// Cleanup interval in seconds
97    #[serde(default = "default_cleanup_interval")]
98    pub interval_secs: u64,
99}
100
101impl Default for CleanupConfig {
102    fn default() -> Self {
103        Self {
104            strategy: CleanupStrategy::Lazy,
105            max_age_days: 7,
106            min_ref_count: 0,
107            interval_secs: DEFAULT_CLEANUP_INTERVAL_SECS,
108        }
109    }
110}
111
112/// Cleanup strategy
113#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
114#[serde(rename_all = "snake_case")]
115pub enum CleanupStrategy {
116    /// Clean up immediately when ref_count reaches 0
117    Immediate,
118    /// Mark for deletion but delay actual cleanup
119    #[default]
120    Lazy,
121    /// Only clean up on scheduled runs
122    Scheduled,
123}
124
125/// Channel-specific configurations
126#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct ChannelConfigs {
128    #[serde(default)]
129    pub telegram: ChannelConfig,
130    #[serde(default)]
131    pub discord: ChannelConfig,
132    #[serde(default)]
133    pub slack: ChannelConfig,
134    #[serde(default)]
135    pub ui: UiChannelConfig,
136}
137
138impl Default for ChannelConfigs {
139    fn default() -> Self {
140        Self {
141            telegram: ChannelConfig {
142                enabled: true,
143                max_file_size: 10 * 1024 * 1024, // 10MB
144                auto_download: true,
145                preview_max_size: 100 * 1024, // 100KB
146            },
147            discord: ChannelConfig {
148                enabled: true,
149                max_file_size: 20 * 1024 * 1024, // 20MB
150                auto_download: true,
151                preview_max_size: 100 * 1024,
152            },
153            slack: ChannelConfig {
154                enabled: true,
155                max_file_size: 50 * 1024 * 1024, // 50MB
156                auto_download: true,
157                preview_max_size: 100 * 1024,
158            },
159            ui: UiChannelConfig {
160                enabled: true,
161                max_file_size: 500 * 1024 * 1024, // 500MB
162                chunk_size: 10 * 1024 * 1024,     // 10MB chunks
163                stream_upload: true,
164                auto_download: true,
165                preview_max_size: 1024 * 1024, // 1MB
166            },
167        }
168    }
169}
170
171/// Generic channel configuration
172#[derive(Debug, Clone, Serialize, Deserialize, Default)]
173pub struct ChannelConfig {
174    pub enabled: bool,
175    pub max_file_size: u64,
176    pub auto_download: bool,
177    pub preview_max_size: u64,
178}
179
180/// UI-specific channel configuration
181#[derive(Debug, Clone, Serialize, Deserialize, Default)]
182pub struct UiChannelConfig {
183    pub enabled: bool,
184    pub max_file_size: u64,
185    pub chunk_size: u64,
186    pub stream_upload: bool,
187    pub auto_download: bool,
188    pub preview_max_size: u64,
189}
190
191// Default value functions
192fn default_storage_path() -> PathBuf {
193    dirs::home_dir()
194        .map(|h| h.join(".agent-diva").join("files"))
195        .unwrap_or_else(|| PathBuf::from(".agent-diva/files"))
196}
197
198fn default_max_file_size() -> u64 {
199    DEFAULT_MAX_FILE_SIZE
200}
201
202fn default_max_total_size() -> u64 {
203    DEFAULT_MAX_TOTAL_SIZE
204}
205
206fn default_max_age_days() -> u32 {
207    7
208}
209
210fn default_cleanup_interval() -> u64 {
211    DEFAULT_CLEANUP_INTERVAL_SECS
212}
213
214fn default_true() -> bool {
215    true
216}
217
218/// Get the default data directory path for agent-diva files
219///
220/// Uses the system's local data directory (e.g., %LOCALAPPDATA% on Windows,
221/// ~/Library/Application Support on macOS, ~/.local/share on Linux)
222/// and appends "agent-diva/files".
223///
224/// # Returns
225/// * `Some(PathBuf)` - The default data directory path
226/// * `None` - If the local data directory cannot be determined
227pub fn default_data_dir() -> Option<PathBuf> {
228    dirs::data_local_dir().map(|base| base.join("agent-diva").join("files"))
229}
230
231/// Get the default data directory path or fall back to a path in the home directory
232///
233/// This is a convenience function that never fails - if the system data directory
234/// cannot be determined, it falls back to ~/.agent-diva/files
235pub fn default_data_dir_or_fallback() -> PathBuf {
236    default_data_dir().unwrap_or_else(|| {
237        dirs::home_dir()
238            .map(|h| h.join(".agent-diva").join("files"))
239            .unwrap_or_else(|| PathBuf::from(".agent-diva/files"))
240    })
241}