kern/
data.rs

1//! Database
2
3use crate::{Fail, Result};
4use std::collections::HashMap;
5use std::fs::{File, OpenOptions, remove_file, rename};
6use std::io::prelude::*;
7
8/// Raw data storage file
9#[derive(Debug)]
10pub struct StorageFile {
11    file: File,
12    raw: String,
13    cache: HashMap<String, String>,
14}
15
16impl StorageFile {
17    /// Open file or create new
18    pub fn new(file_name: impl AsRef<str>) -> Result<Self> {
19        // open file and parse
20        let mut file = open_file(file_name)?;
21        let raw = read_file(&mut file)?;
22        let raw = String::from_utf8(raw)?;
23        let cache = parse(&raw);
24
25        // return
26        Ok(Self { file, raw, cache })
27    }
28
29    /// Get raw
30    pub fn raw(&self) -> &str {
31        &self.raw
32    }
33
34    /// Write directly to file and parse
35    pub fn raw_write(&mut self, raw: String) -> Result<()> {
36        // parse and write to file
37        self.raw = raw;
38        self.cache = parse(&self.raw);
39        write_file(&mut self.file, &self.raw)
40    }
41
42    /// Get map from cache
43    pub fn cache(&self) -> &HashMap<String, String> {
44        &self.cache
45    }
46
47    /// Get map from cache mutably
48    pub fn cache_mut(&mut self) -> &mut HashMap<String, String> {
49        &mut self.cache
50    }
51
52    /// Serialize map to string and write to file
53    pub fn write(&mut self) -> Result<()> {
54        // serialize and write
55        self.raw = serialize(self.cache());
56        write_file(&mut self.file, &self.raw)
57    }
58}
59
60/// Open file or create new
61pub fn open_file(file_name: impl AsRef<str>) -> Result<File> {
62    // open and return file
63    OpenOptions::new()
64        .read(true)
65        .write(true)
66        .create(true)
67        .truncate(false)
68        .open(file_name.as_ref())
69        .or_else(Fail::from)
70}
71
72/// Delete file if exists
73pub fn delete_file(file_name: impl AsRef<str>) -> Result<()> {
74    // delete file
75    remove_file(file_name.as_ref()).or_else(Fail::from)
76}
77
78/// Move file
79pub fn move_file(file_name: impl AsRef<str>, new_file_name: impl AsRef<str>) -> Result<()> {
80    // delete file
81    rename(file_name.as_ref(), new_file_name.as_ref()).or_else(Fail::from)
82}
83
84/// Read data from file
85pub fn read_file(file: &mut File) -> Result<Vec<u8>> {
86    // start from beginning
87    file.rewind()?;
88
89    // create buffer
90    let mut buf = Vec::with_capacity(match file.metadata() {
91        Ok(metadata) => metadata.len() as usize,
92        Err(_) => 8192,
93    });
94
95    // read and return
96    file.read_to_end(&mut buf)?;
97    Ok(buf)
98}
99
100/// Write data to file
101pub fn write_file(file: &mut File, data: impl AsRef<[u8]>) -> Result<()> {
102    // truncate file
103    file.set_len(0)?;
104
105    // start from first byte
106    file.rewind()?;
107
108    // write data
109    file.write_all(data.as_ref())?;
110    file.flush().or_else(Fail::from)
111}
112
113/// Parse storage file buf to map
114pub fn parse(buf: &str) -> HashMap<String, String> {
115    // initialize map and split lines
116    let mut conf = HashMap::new();
117    buf.split('\n')
118        // seperate and trim
119        .map(|l| l.splitn(2, '=').map(|c| c.trim()).collect())
120        // iterate through seperated lines
121        .for_each(|kv: Vec<&str>| {
122            // check if contains key and value
123            if kv.len() == 2 {
124                conf.insert(kv[0].to_lowercase(), kv[1].to_string());
125            }
126        });
127
128    // return
129    conf
130}
131
132/// Serialize map to string
133pub fn serialize(data: &HashMap<String, String>) -> String {
134    // create buffer
135    let mut buf = String::with_capacity(data.len() * 10);
136
137    // add entries
138    for (k, v) in data {
139        buf.push_str(k);
140        buf.push('=');
141        buf.push_str(v);
142        buf.push('\n');
143    }
144
145    // return
146    buf
147}