scrobble_scrubber/persistence/
file_storage.rs1use async_trait::async_trait;
2use pickledb::{PickleDb, PickleDbDumpPolicy, SerializationMethod};
3use std::path::Path;
4
5use super::{
6 PendingEditsState, PendingRewriteRulesState, RewriteRulesState, SettingsState, StateStorage,
7 TimestampState,
8};
9use crate::rewrite::load_comprehensive_default_rules;
10
11pub struct FileStorage {
13 db: PickleDb,
14}
15
16#[derive(Debug, thiserror::Error)]
17#[allow(clippy::enum_variant_names)]
18pub enum FileStorageError {
19 #[error("Database error: {0}")]
20 DatabaseError(String),
21 #[error("Serialization error: {0}")]
22 SerializationError(String),
23 #[error("IO error: {0}")]
24 IoError(#[from] std::io::Error),
25}
26
27impl FileStorage {
28 pub fn new<P: AsRef<Path>>(path: P) -> Result<Self, FileStorageError> {
29 let path_ref = path.as_ref();
30
31 let is_new_database = !path_ref.exists();
33
34 if let Some(parent) = path_ref.parent() {
36 std::fs::create_dir_all(parent)?;
37 }
38
39 let db = match PickleDb::load(
40 path_ref,
41 PickleDbDumpPolicy::AutoDump,
42 SerializationMethod::Json,
43 ) {
44 Ok(db) => db,
45 Err(_) => PickleDb::new(
46 path_ref,
47 PickleDbDumpPolicy::AutoDump,
48 SerializationMethod::Json,
49 ),
50 };
51
52 let mut storage = Self { db };
53
54 if is_new_database {
56 if let Err(e) = storage.initialize_default_rules() {
57 log::warn!("Failed to initialize default rules for new database: {e}");
58 } else {
59 log::info!("Initialized new database with comprehensive default rewrite rules");
60 }
61 }
62
63 Ok(storage)
64 }
65
66 fn initialize_default_rules(&mut self) -> Result<(), FileStorageError> {
68 let default_rules = load_comprehensive_default_rules();
69 let rules_state = RewriteRulesState {
70 rewrite_rules: default_rules,
71 };
72
73 self.db
74 .set("rewrite_rules_state", &rules_state)
75 .map_err(|e| FileStorageError::SerializationError(e.to_string()))?;
76
77 self.db
79 .dump()
80 .map_err(|e| FileStorageError::DatabaseError(e.to_string()))?;
81
82 Ok(())
83 }
84}
85
86#[async_trait]
87impl StateStorage for FileStorage {
88 type Error = FileStorageError;
89
90 async fn save_timestamp_state(&mut self, state: &TimestampState) -> Result<(), Self::Error> {
91 self.db
92 .set("timestamp_state", state)
93 .map_err(|e| FileStorageError::SerializationError(e.to_string()))?;
94 Ok(())
95 }
96
97 async fn load_timestamp_state(&self) -> Result<TimestampState, Self::Error> {
98 Ok(self.db.get("timestamp_state").unwrap_or_default())
99 }
100
101 async fn save_rewrite_rules_state(
102 &mut self,
103 state: &RewriteRulesState,
104 ) -> Result<(), Self::Error> {
105 self.db
106 .set("rewrite_rules_state", state)
107 .map_err(|e| FileStorageError::SerializationError(e.to_string()))?;
108
109 self.db
111 .dump()
112 .map_err(|e| FileStorageError::DatabaseError(e.to_string()))?;
113
114 Ok(())
115 }
116
117 async fn load_rewrite_rules_state(&self) -> Result<RewriteRulesState, Self::Error> {
118 Ok(self.db.get("rewrite_rules_state").unwrap_or_default())
119 }
120
121 async fn save_pending_edits_state(
122 &mut self,
123 state: &PendingEditsState,
124 ) -> Result<(), Self::Error> {
125 self.db
126 .set("pending_edits_state", state)
127 .map_err(|e| FileStorageError::SerializationError(e.to_string()))?;
128
129 self.db
131 .dump()
132 .map_err(|e| FileStorageError::DatabaseError(e.to_string()))?;
133
134 Ok(())
135 }
136
137 async fn load_pending_edits_state(&self) -> Result<PendingEditsState, Self::Error> {
138 Ok(self.db.get("pending_edits_state").unwrap_or_default())
139 }
140
141 async fn save_pending_rewrite_rules_state(
142 &mut self,
143 state: &PendingRewriteRulesState,
144 ) -> Result<(), Self::Error> {
145 self.db
146 .set("pending_rewrite_rules_state", state)
147 .map_err(|e| FileStorageError::SerializationError(e.to_string()))?;
148
149 self.db
151 .dump()
152 .map_err(|e| FileStorageError::DatabaseError(e.to_string()))?;
153
154 Ok(())
155 }
156
157 async fn load_pending_rewrite_rules_state(
158 &self,
159 ) -> Result<PendingRewriteRulesState, Self::Error> {
160 Ok(self
161 .db
162 .get("pending_rewrite_rules_state")
163 .unwrap_or_default())
164 }
165
166 async fn save_settings_state(&mut self, state: &SettingsState) -> Result<(), Self::Error> {
167 self.db
168 .set("settings_state", state)
169 .map_err(|e| FileStorageError::SerializationError(e.to_string()))?;
170 Ok(())
171 }
172
173 async fn load_settings_state(&self) -> Result<SettingsState, Self::Error> {
174 Ok(self.db.get("settings_state").unwrap_or_default())
175 }
176}
177
178unsafe impl Send for FileStorage {}
181unsafe impl Sync for FileStorage {}