cyfs_util/storage/
file_storage.rs

1use 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(value)
155                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}