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 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
use std::fmt::Debug;
use std::fs::{File, OpenOptions};
use std::io::{self, Seek, SeekFrom, Write};
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use bincode::deserialize_from;
use serde::de::DeserializeOwned;
use serde::Serialize;
use crate::types::BinaryKv;
/// The Mini Client. Used for simple data storage and retrieval.
///
/// # Example
/// ```rust
/// use std::collections::HashMap;
///
/// use quick_kv::prelude::*;
///
/// let mut client = QuickClientMini::new(None).unwrap();
///
/// let mut map = HashMap::new();
///
/// for i in 0..9 {
/// map.insert(i.to_string(), i);
/// }
///
/// client
/// .set("test-hash", TypedValue::<i32>::Hash(map.clone()))
/// .unwrap();
///
/// let map_results = client
/// .get::<TypedValue<i32>>("test-hash")
/// .unwrap()
/// .unwrap()
/// .into_hash();
///
/// for (key, value) in map_results.iter() {
/// println!("{}: {}", key, value)
/// }
///
/// assert_eq!(map, map_results);
/// ```
#[derive(Debug)]
pub struct QuickClientMini
{
pub file: Arc<Mutex<File>>,
}
impl QuickClientMini
{
pub fn new(path: Option<PathBuf>) -> io::Result<Self>
{
let path = match path {
Some(path) => path,
None => PathBuf::from("db.qkv"),
};
let file = match OpenOptions::new().read(true).write(true).create(true).open(path) {
Ok(file) => file,
Err(e) => {
return Err(io::Error::new(io::ErrorKind::Other, format!("Error opening file: {:?}", e)));
}
};
Ok(Self {
file: Arc::new(Mutex::new(file)),
})
}
pub fn get<T>(&mut self, key: &str) -> io::Result<Option<T>>
where
T: Serialize + DeserializeOwned + Clone + Debug,
{
let mut file = match self.file.lock() {
Ok(file) => file,
Err(e) => {
return Err(io::Error::new(io::ErrorKind::Other, format!("Error locking file: {:?}", e)));
}
};
let mut reader = io::BufReader::new(&mut *file);
// Seek to the beginning of the file
reader.seek(SeekFrom::Start(0))?;
// Read and deserialize entries until the end of the file is reached
loop {
match deserialize_from::<_, BinaryKv<T>>(&mut reader) {
Ok(BinaryKv { key: entry_key, value }) if key == entry_key => {
return Ok(Some(value));
}
Err(e) => {
if let bincode::ErrorKind::Io(io_err) = e.as_ref() {
if io_err.kind() == io::ErrorKind::UnexpectedEof {
// Reached the end of the serialized data
break;
}
}
}
_ => {}
}
}
// Key not found
Ok(None)
}
pub fn set<T>(&mut self, key: &str, value: T) -> io::Result<()>
where
T: Serialize + DeserializeOwned + Clone + Debug,
{
if self.get::<T>(key)?.is_none() {
// Key doesn't exist, add a new key-value pair
let mut file = match self.file.lock() {
Ok(file) => file,
Err(e) => {
return Err(io::Error::new(io::ErrorKind::Other, format!("Error locking file: {:?}", e)));
}
};
let mut writer = io::BufWriter::new(&mut *file);
let data = BinaryKv::new(key.to_string(), value.clone());
let serialized = match bincode::serialize(&data) {
Ok(data) => data,
Err(e) => panic!("Error serializing data: {:?}", e),
};
// Write the serialized data to the file
writer.write_all(&serialized)?;
// Flush the writer to ensure data is written to the file
writer.get_ref().sync_all()?;
} else {
// Key already exists, update the value
self.update(key, value)?;
}
Ok(())
}
pub fn delete<T>(&mut self, key: &str) -> io::Result<()>
where
T: Serialize + DeserializeOwned + Clone + Debug,
{
let mut file = match self.file.lock() {
Ok(file) => file,
Err(e) => {
return Err(io::Error::new(io::ErrorKind::Other, format!("Error locking file: {:?}", e)));
}
};
let mut reader = io::BufReader::new(&mut *file);
// Create a temporary buffer to store the updated data
let mut updated_buffer = Vec::new();
// Read and process entries
loop {
match deserialize_from::<_, BinaryKv<T>>(&mut reader) {
Ok(BinaryKv { key: entry_key, .. }) if key != entry_key => {
// Keep entries that don't match the key
updated_buffer.extend_from_slice(reader.buffer());
}
Ok(_) => {
// Skip entries that match the key
}
Err(e) => {
if let bincode::ErrorKind::Io(io_err) = e.as_ref() {
if io_err.kind() == io::ErrorKind::UnexpectedEof {
// Reached the end of the serialized data
break;
}
}
}
}
}
// Close the file and open it in write mode for writing
drop(reader); // Release the reader
let mut writer = io::BufWriter::new(&mut *file);
// Truncate the file and write the updated data back
writer.get_mut().set_len(0)?;
writer.seek(SeekFrom::Start(0))?;
writer.write_all(&updated_buffer)?;
// Flush the writer to ensure data is written to the file
writer.flush()?;
Ok(())
}
pub fn update<T>(&mut self, key: &str, value: T) -> io::Result<()>
where
T: Serialize + DeserializeOwned + Clone + Debug,
{
// Lock the file and use a buffered reader
let mut file = match self.file.lock() {
Ok(file) => file,
Err(e) => {
return Err(io::Error::new(io::ErrorKind::Other, format!("Error locking file: {:?}", e)));
}
};
let mut reader = io::BufReader::new(&mut *file);
// Seek to the beginning of the file
reader.seek(SeekFrom::Start(0))?;
let mut updated_entries = Vec::new();
let mut updated = false;
// Read and process entries
loop {
match deserialize_from::<_, BinaryKv<T>>(&mut reader) {
Ok(entry) => {
if key == entry.key {
// Update the value associated with the key
let mut updated_entry = entry.clone();
updated_entry.value = value.clone();
updated_entries.push(updated_entry);
updated = true;
} else {
updated_entries.push(entry);
}
}
Err(e) => {
if let bincode::ErrorKind::Io(io_err) = e.as_ref() {
if io_err.kind() == io::ErrorKind::UnexpectedEof {
// Reached the end of the serialized data
break;
}
}
}
}
}
if !updated {
// Key not found
return Err(io::Error::new(io::ErrorKind::Other, format!("Key not found: {}", key)));
}
// Close the file and open it in write mode
drop(reader); // Release the reader
// Reopen the file in write mode for writing
let mut writer = io::BufWriter::new(&mut *file);
// Truncate the file and write the updated data back
writer.get_mut().set_len(0)?;
writer.seek(SeekFrom::Start(0))?;
for entry in updated_entries.iter() {
let serialized = match bincode::serialize(entry) {
Ok(data) => data,
Err(e) => panic!("Error serializing data: {:?}", e),
};
writer.write_all(&serialized)?;
}
writer.get_ref().sync_all()?;
Ok(())
}
}