1use crate::id::ModelId;
10use serde::{Deserialize, Serialize};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
14#[serde(rename_all = "snake_case")]
15pub enum DataClass {
16 PersistentCatalog,
20 RebuildableIndex,
23 EphemeralCache,
26 LocalDependency,
28 OperationalLog,
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
34#[serde(rename_all = "snake_case")]
35pub enum StorageCategory {
36 PersistentCatalog,
37 KeywordIndex,
38 VectorIndex,
39 SnippetCache,
40 SearchCache,
41 TemporaryExtraction,
42 ModelFiles,
43 Logs,
44}
45
46impl StorageCategory {
47 pub const ALL: [StorageCategory; 8] = [
49 StorageCategory::PersistentCatalog,
50 StorageCategory::KeywordIndex,
51 StorageCategory::VectorIndex,
52 StorageCategory::SnippetCache,
53 StorageCategory::SearchCache,
54 StorageCategory::TemporaryExtraction,
55 StorageCategory::ModelFiles,
56 StorageCategory::Logs,
57 ];
58
59 pub fn as_str(&self) -> &'static str {
61 match self {
62 StorageCategory::PersistentCatalog => "persistent_catalog",
63 StorageCategory::KeywordIndex => "keyword_index",
64 StorageCategory::VectorIndex => "vector_index",
65 StorageCategory::SnippetCache => "snippet_cache",
66 StorageCategory::SearchCache => "search_cache",
67 StorageCategory::TemporaryExtraction => "temporary_extraction",
68 StorageCategory::ModelFiles => "model_files",
69 StorageCategory::Logs => "logs",
70 }
71 }
72
73 pub fn data_class(&self) -> DataClass {
75 match self {
76 StorageCategory::PersistentCatalog => DataClass::PersistentCatalog,
77 StorageCategory::KeywordIndex | StorageCategory::VectorIndex => {
78 DataClass::RebuildableIndex
79 }
80 StorageCategory::SnippetCache
81 | StorageCategory::SearchCache
82 | StorageCategory::TemporaryExtraction => DataClass::EphemeralCache,
83 StorageCategory::ModelFiles => DataClass::LocalDependency,
84 StorageCategory::Logs => DataClass::OperationalLog,
85 }
86 }
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
95pub enum StorageMeasurement {
96 Measured { bytes: u64, items: u64 },
97 Unknown,
98}
99
100#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
102#[serde(rename_all = "snake_case")]
103pub enum CleanupAction {
104 ClearExpiredSearchCache,
106 ClearSnippetCache,
108 ClearTemporaryExtraction,
110 RemoveReplacedStaleIndexes,
112 DeleteKeywordIndex,
114 DeleteVectorIndex,
116 RemoveTemporarySourceIndexes,
118 ResetCatalog,
120}
121
122impl CleanupAction {
123 pub fn affected_classes(&self) -> &'static [DataClass] {
125 match self {
126 CleanupAction::ClearExpiredSearchCache
127 | CleanupAction::ClearSnippetCache
128 | CleanupAction::ClearTemporaryExtraction => &[DataClass::EphemeralCache],
129 CleanupAction::RemoveReplacedStaleIndexes
130 | CleanupAction::DeleteKeywordIndex
131 | CleanupAction::DeleteVectorIndex
132 | CleanupAction::RemoveTemporarySourceIndexes => &[DataClass::RebuildableIndex],
133 CleanupAction::ResetCatalog => &[
134 DataClass::PersistentCatalog,
135 DataClass::RebuildableIndex,
136 DataClass::EphemeralCache,
137 DataClass::OperationalLog,
138 ],
139 }
140 }
141
142 pub fn requires_rebuild(&self) -> bool {
144 !matches!(
145 self,
146 CleanupAction::ClearExpiredSearchCache
147 | CleanupAction::ClearSnippetCache
148 | CleanupAction::ClearTemporaryExtraction
149 )
150 }
151
152 pub fn requires_confirmation(&self) -> bool {
154 self.requires_rebuild()
155 }
156
157 pub fn touches_persistent_catalog(&self) -> bool {
159 self.affected_classes()
160 .contains(&DataClass::PersistentCatalog)
161 }
162}
163
164#[derive(Debug, Clone, Serialize, Deserialize)]
166pub struct CleanupPlan {
167 pub action: CleanupAction,
168 pub affected_classes: Vec<DataClass>,
169 pub estimated_recovered_bytes: u64,
170 pub requires_rebuild: bool,
171 pub requires_confirmation: bool,
172 pub model_id: Option<ModelId>,
179}
180
181impl CleanupPlan {
182 pub fn for_action(action: CleanupAction, estimated_recovered_bytes: u64) -> Self {
184 Self {
185 action,
186 affected_classes: action.affected_classes().to_vec(),
187 estimated_recovered_bytes,
188 requires_rebuild: action.requires_rebuild(),
189 requires_confirmation: action.requires_confirmation(),
190 model_id: None,
191 }
192 }
193
194 pub fn with_model(mut self, model_id: ModelId) -> Self {
197 self.model_id = Some(model_id);
198 self
199 }
200
201 pub fn assert_safe_for_ordinary_cleanup(&self) -> Result<(), crate::error::OrbokError> {
205 if self
206 .affected_classes
207 .contains(&DataClass::PersistentCatalog)
208 {
209 return Err(crate::error::OrbokError::CleanupWouldTouchPersistentData);
210 }
211 Ok(())
212 }
213}