cyfs_util/storage/
file_storage.rs1use cyfs_base::BuckyError;
2use crate::get_cyfs_root_path;
3
4use async_std::fs;
5use async_trait::async_trait;
6use std::collections::BTreeMap;
7use std::path::PathBuf;
8
9pub struct FileStorage {
10 loaded: bool,
11 path: PathBuf,
12 dirty: bool,
13 storage: BTreeMap<String, String>,
14}
15
16impl FileStorage {
17 pub fn new() -> FileStorage {
18 FileStorage {
19 loaded: false,
20 path: PathBuf::from(""),
21 dirty: false,
22 storage: BTreeMap::new(),
23 }
24 }
25
26 pub async fn init(&mut self, service_name: &str) -> Result<(), BuckyError> {
27 assert!(!self.loaded);
28 self.loaded = true;
29
30 let dir = get_cyfs_root_path().join("profile").join(service_name);
31 if !dir.is_dir() {
32 if let Err(e) = std::fs::create_dir_all(&dir) {
33 let msg = format!("create profile dir error! dir={}, err={}", dir.display(), e);
34 error!("{}", msg);
35
36 return Err(BuckyError::from(msg));
37 }
38 }
39
40 let file = dir.join("profile.json");
41 self.path = file;
42 if !self.path.exists() {
43 info!("file storage file not exists! file={}", self.path.display());
44 return Ok(());
45 }
46
47 return self.load().await;
48 }
49
50 async fn load(&mut self) -> Result<(), BuckyError> {
51 assert!(self.storage.is_empty());
52
53 let contents = match fs::read_to_string(&self.path).await {
54 Ok(v) => v,
55 Err(e) => {
56 let msg = format!(
57 "load storage file as string error! file={}, err={}",
58 self.path.display(),
59 e
60 );
61 error!("{}", msg);
62
63 return Err(BuckyError::from(msg));
64 }
65 };
66
67 self.storage = match serde_json::from_str(&contents) {
68 Ok(v) => v,
69 Err(e) => {
70 let msg = format!(
71 "unserialize storage file from string error! file={}, err={}, content={}",
72 self.path.display(),
73 e,
74 contents
75 );
76 error!("{}", msg);
77
78 return Err(BuckyError::from(msg));
79 }
80 };
81
82 self.dirty = false;
83 info!("load storage success! file={}", self.path.display());
84
85 Ok(())
86 }
87
88 pub async fn get_item_str(&self, key: &str) -> Option<&str> {
89 self.storage
90 .get_key_value(key)
91 .map(|(_, value)| value.as_str())
92 }
93
94 async fn flush(&mut self) -> Result<(), BuckyError> {
95 if !self.dirty {
96 return Ok(());
97 }
98
99 let str_value = match serde_json::to_string(&self.storage) {
100 Ok(v) => v,
101 Err(e) => {
102 let msg = format!("storage to string error! err={}", e);
103 error!("{}", msg);
104
105 return Err(BuckyError::from(msg));
106 }
107 };
108
109 if let Err(e) = fs::write(&self.path, str_value.as_bytes()).await {
110 let msg = format!("write file error! file={}, err={}", self.path.display(), e);
111 error!("{}", msg);
112
113 return Err(BuckyError::from(msg));
114 }
115
116 assert!(self.dirty);
117 self.dirty = false;
118
119 Ok(())
120 }
121}
122
123#[async_trait]
124impl super::AsyncStorage for FileStorage {
125 async fn set_item(&mut self, key: &str, value: String) -> Result<(), BuckyError> {
126 match self.storage.insert(key.to_owned(), value) {
127 Some(v) => {
128 info!("key replace: {}, old: {}", key, v);
129 }
130 None => {
131 info!("key insert: {}", key);
132 }
133 };
134
135 self.dirty = true;
136
137 self.flush().await
138 }
139
140 async fn get_item(&self, key: &str) -> Option<String> {
141 self.storage
142 .get_key_value(key)
143 .map(|(_, value)| value.clone())
144 }
145
146 async fn remove_item(&mut self, key: &str) -> Option<()> {
147 match self.storage.remove(key) {
148 Some(value) => {
149 self.dirty = true;
150 let _r = self.flush().await;
151
152 info!("key removed: key={}, value={}", key, value);
153
154 Some(())
156 }
157 None => None,
158 }
159 }
160
161 async fn clear(&mut self) {
162 if !self.storage.is_empty() {
163 self.storage.clear();
164
165 self.dirty = true;
166 let _r = self.flush().await;
167
168 info!("storage cleared: file={}", self.path.display());
169 }
170 }
171
172 async fn clear_with_prefix(&mut self, prefix: &str) {
173 let keys: Vec<String> = self
174 .storage
175 .keys()
176 .filter_map(|key| {
177 if key.starts_with(prefix) {
178 Some(key.to_owned())
179 } else {
180 None
181 }
182 })
183 .collect();
184
185 for key in keys {
186 match self.storage.remove(&key) {
187 Some(value) => {
188 self.dirty = true;
189
190 info!("key removed: key={}, value={}", key, value);
191 }
192 None => {
193 error!("key not found: key={}", key);
194 }
195 }
196 }
197
198 if self.dirty {
199 let _r = self.flush().await;
200 }
201 }
202}