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
use serde::de::DeserializeOwned;
use std::{fs::File, io::prelude::*};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum FileError {
#[error("The specified backup file could not be created")]
FileCreate,
#[error("The specified backup file could not be opened")]
FileOpen,
#[error("Could not read from backup file")]
FileRead,
#[error("Could not write to backup file")]
FileWrite,
#[error("Problem serializing struct into JSON")]
Serialize,
#[error("Problem deserializing JSON into a new struct")]
Deserialize,
}
pub trait FileBackup<T> {
fn from_file(filename: &str) -> Result<T, FileError>;
fn to_file(&self, filename: &str) -> Result<(), FileError>;
}
impl<T> FileBackup<T> for T
where T: serde::Serialize + DeserializeOwned
{
fn from_file(filename: &str) -> Result<T, FileError> {
let mut file_handle = match File::open(&filename) {
Ok(file) => file,
Err(_e) => return Err(FileError::FileOpen),
};
let mut file_content = String::new();
match file_handle.read_to_string(&mut file_content) {
Ok(_) => match serde_json::from_str(&file_content) {
Ok(km) => Ok(km),
Err(_) => Err(FileError::Deserialize),
},
Err(_) => Err(FileError::FileRead),
}
}
fn to_file(&self, filename: &str) -> Result<(), FileError> {
match File::create(filename) {
Ok(mut file_handle) => match serde_json::to_string(&self) {
Ok(json_data) => match file_handle.write_all(json_data.as_bytes()) {
Ok(_) => Ok(()),
Err(_) => Err(FileError::FileWrite),
},
Err(_) => Err(FileError::Serialize),
},
Err(_) => Err(FileError::FileCreate),
}
}
}
#[cfg(test)]
mod test {
use crate::file_backup::*;
use serde_derive::{Deserialize, Serialize};
use std::fs::remove_file;
#[test]
fn test_struct_to_file_and_from_file() {
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct R {
pub var1: String,
pub var2: Vec<u8>,
pub var3: usize,
}
let desired_struct = R {
var1: "Test".to_string(),
var2: vec![0, 1, 2],
var3: 3,
};
let backup_filename = "test_backup.json".to_string();
match desired_struct.to_file(&backup_filename) {
Ok(_v) => {
let backup_result: Result<R, FileError> = R::from_file(&backup_filename);
match backup_result {
Ok(backup_struct) => {
remove_file(backup_filename).unwrap();
assert_eq!(desired_struct, backup_struct);
},
Err(_e) => assert!(false),
};
},
Err(_e) => assert!(false),
};
}
}