/
storage.rs
134 lines (112 loc) · 3.3 KB
/
storage.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
use std::fs::{DirBuilder, File};
use std::io::{BufReader, BufWriter, Read, Write};
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use typed_builder::TypedBuilder;
use crate::Error;
#[derive(Debug, Error)]
pub enum StorageError {
#[error("Path can't be empty")]
EmptyPathError,
}
#[derive(Debug, Error)]
pub enum ReadDataError {
#[error("Could not load data")]
LoadError,
}
/// Implemented by anything that wants to read specific data from a storage.
pub trait ReadData<D> {
fn data(&self) -> Result<&D, Error>;
}
#[derive(Serialize, Deserialize)]
pub(crate) struct StorageInfo {
pub backend: String,
pub args: StorageArgs,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum StorageArgs {
FSStorage { path: String },
}
impl From<&StorageArgs> for FSStorage {
fn from(other: &StorageArgs) -> FSStorage {
match other {
StorageArgs::FSStorage { path } => {
let mut fullpath = PathBuf::new();
fullpath.push(".");
fullpath.push(path);
FSStorage {
fullpath,
subdir: path.clone(),
}
}
}
}
}
/// An abstraction for any place where we can store data.
pub trait Storage {
/// Save bytes into path
fn save(&self, path: &str, content: &[u8]) -> Result<String, Error>;
/// Load bytes from path
fn load(&self, path: &str) -> Result<Vec<u8>, Error>;
/// Args for initializing a new Storage
fn args(&self) -> StorageArgs;
}
/// Store files locally into a directory
#[derive(TypedBuilder, Debug, Clone, Default)]
pub struct FSStorage {
/// absolute path for the directory where data is saved.
fullpath: PathBuf,
subdir: String,
}
impl FSStorage {
pub fn new(location: &str, subdir: &str) -> FSStorage {
let mut fullpath = PathBuf::new();
fullpath.push(location);
fullpath.push(subdir);
FSStorage {
fullpath,
subdir: subdir.into(),
}
}
pub fn set_base(&mut self, location: &str) {
let mut fullpath = PathBuf::new();
fullpath.push(location);
fullpath.push(&self.subdir);
self.fullpath = fullpath;
}
}
impl Storage for FSStorage {
fn save(&self, path: &str, content: &[u8]) -> Result<String, Error> {
if path.is_empty() {
return Err(StorageError::EmptyPathError.into());
}
let fpath = self.fullpath.join(path);
DirBuilder::new()
.recursive(true)
.create(fpath.parent().unwrap())?;
let file = File::create(&fpath)?;
let mut buf_writer = BufWriter::new(file);
buf_writer.write_all(content)?;
Ok(path.into())
}
fn load(&self, path: &str) -> Result<Vec<u8>, Error> {
let path = self.fullpath.join(path);
let file = File::open(path)?;
let mut buf_reader = BufReader::new(file);
let mut contents = Vec::new();
buf_reader.read_to_end(&mut contents)?;
Ok(contents)
}
fn args(&self) -> StorageArgs {
StorageArgs::FSStorage {
path: self.subdir.clone(),
}
}
}
pub trait ToWriter {
fn to_writer<W>(&self, writer: &mut W) -> Result<(), Error>
where
W: Write;
}