1use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
13#[serde(rename_all = "snake_case")]
14pub enum DataClass {
15 PersistentCatalog,
19 RebuildableIndex,
22 EphemeralCache,
25 LocalDependency,
27 OperationalLog,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
33#[serde(rename_all = "snake_case")]
34pub enum StorageCategory {
35 PersistentCatalog,
36 KeywordIndex,
37 VectorIndex,
38 SnippetCache,
39 SearchCache,
40 TemporaryExtraction,
41 ModelFiles,
42 Logs,
43}
44
45impl StorageCategory {
46 pub const ALL: [StorageCategory; 8] = [
48 StorageCategory::PersistentCatalog,
49 StorageCategory::KeywordIndex,
50 StorageCategory::VectorIndex,
51 StorageCategory::SnippetCache,
52 StorageCategory::SearchCache,
53 StorageCategory::TemporaryExtraction,
54 StorageCategory::ModelFiles,
55 StorageCategory::Logs,
56 ];
57
58 pub fn as_str(&self) -> &'static str {
60 match self {
61 StorageCategory::PersistentCatalog => "persistent_catalog",
62 StorageCategory::KeywordIndex => "keyword_index",
63 StorageCategory::VectorIndex => "vector_index",
64 StorageCategory::SnippetCache => "snippet_cache",
65 StorageCategory::SearchCache => "search_cache",
66 StorageCategory::TemporaryExtraction => "temporary_extraction",
67 StorageCategory::ModelFiles => "model_files",
68 StorageCategory::Logs => "logs",
69 }
70 }
71
72 pub fn data_class(&self) -> DataClass {
74 match self {
75 StorageCategory::PersistentCatalog => DataClass::PersistentCatalog,
76 StorageCategory::KeywordIndex | StorageCategory::VectorIndex => {
77 DataClass::RebuildableIndex
78 }
79 StorageCategory::SnippetCache
80 | StorageCategory::SearchCache
81 | StorageCategory::TemporaryExtraction => DataClass::EphemeralCache,
82 StorageCategory::ModelFiles => DataClass::LocalDependency,
83 StorageCategory::Logs => DataClass::OperationalLog,
84 }
85 }
86}
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq)]
94pub enum StorageMeasurement {
95 Measured { bytes: u64, items: u64 },
96 Unknown,
97}
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
101#[serde(rename_all = "snake_case")]
102pub enum CleanupAction {
103 ClearExpiredSearchCache,
105 ClearSnippetCache,
107 ClearTemporaryExtraction,
109 RemoveReplacedStaleIndexes,
111 DeleteKeywordIndex,
113 DeleteVectorIndex,
115 RemoveTemporarySourceIndexes,
117 ResetCatalog,
119}
120
121impl CleanupAction {
122 pub fn affected_classes(&self) -> &'static [DataClass] {
124 match self {
125 CleanupAction::ClearExpiredSearchCache
126 | CleanupAction::ClearSnippetCache
127 | CleanupAction::ClearTemporaryExtraction => &[DataClass::EphemeralCache],
128 CleanupAction::RemoveReplacedStaleIndexes
129 | CleanupAction::DeleteKeywordIndex
130 | CleanupAction::DeleteVectorIndex
131 | CleanupAction::RemoveTemporarySourceIndexes => &[DataClass::RebuildableIndex],
132 CleanupAction::ResetCatalog => &[
133 DataClass::PersistentCatalog,
134 DataClass::RebuildableIndex,
135 DataClass::EphemeralCache,
136 DataClass::OperationalLog,
137 ],
138 }
139 }
140
141 pub fn requires_rebuild(&self) -> bool {
143 !matches!(
144 self,
145 CleanupAction::ClearExpiredSearchCache
146 | CleanupAction::ClearSnippetCache
147 | CleanupAction::ClearTemporaryExtraction
148 )
149 }
150
151 pub fn requires_confirmation(&self) -> bool {
153 self.requires_rebuild()
154 }
155
156 pub fn touches_persistent_catalog(&self) -> bool {
158 self.affected_classes()
159 .contains(&DataClass::PersistentCatalog)
160 }
161}
162
163#[derive(Debug, Clone, Serialize, Deserialize)]
165pub struct CleanupPlan {
166 pub action: CleanupAction,
167 pub affected_classes: Vec<DataClass>,
168 pub estimated_recovered_bytes: u64,
169 pub requires_rebuild: bool,
170 pub requires_confirmation: bool,
171}
172
173impl CleanupPlan {
174 pub fn for_action(action: CleanupAction, estimated_recovered_bytes: u64) -> Self {
176 Self {
177 action,
178 affected_classes: action.affected_classes().to_vec(),
179 estimated_recovered_bytes,
180 requires_rebuild: action.requires_rebuild(),
181 requires_confirmation: action.requires_confirmation(),
182 }
183 }
184
185 pub fn assert_safe_for_ordinary_cleanup(&self) -> Result<(), crate::error::OrbokError> {
189 if self
190 .affected_classes
191 .contains(&DataClass::PersistentCatalog)
192 {
193 return Err(crate::error::OrbokError::CleanupWouldTouchPersistentData);
194 }
195 Ok(())
196 }
197}