pub struct Rows<S> {
path: std::path::PathBuf,
p1: std::marker::PhantomData<S>,
}
impl<S> Rows<S>
where
S: serde::Serialize + serde::de::DeserializeOwned,
{
pub fn new(path: &str) -> Result<Self, Box<dyn std::error::Error>> {
let mut url = std::path::PathBuf::new();
url.push(path);
if url.extension().map_or(true, |ext| ext != "csv") {
return Err("Bad extension!")?;
}
std::fs::OpenOptions::new()
.write(true)
.create(true)
.open(&url)?;
Ok(Self {
path: url,
p1: std::marker::PhantomData,
})
}
pub fn insert(&self, data: &S) -> Result<(), Box<dyn std::error::Error>> {
let file = std::fs::OpenOptions::new().append(true).open(&self.path)?;
let meta = file.metadata()?;
let mut write = csv::WriterBuilder::new()
.has_headers(meta.len() == 0)
.from_writer(file);
write.serialize(data)?;
write.flush()?;
Ok(())
}
pub fn overwrite(&self, data: Vec<S>) -> Result<(), Box<dyn std::error::Error>> {
if data.is_empty() {
return Err("Data is empty!")?;
}
let file = std::fs::File::create(&self.path)?;
let mut write = csv::Writer::from_writer(file);
for item in data {
write.serialize(item)?;
}
write.flush()?;
Ok(())
}
pub fn drop(&self) -> Result<(), Box<dyn std::error::Error>> {
std::fs::File::create(&self.path)?;
Ok(())
}
pub fn read_all(&self) -> Result<Vec<S>, Box<dyn std::error::Error>> {
let mut read = csv::Reader::from_path(&self.path)?;
let rdr = read.deserialize::<S>().collect::<Vec<_>>();
let res: Result<Vec<_>, _> = rdr.into_iter().collect();
Ok(res?)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Default, serde::Serialize, serde::Deserialize)]
struct Scaf {
ok: bool,
amount: i32,
}
fn get_path() -> String {
format!(
"/tmp/{}.csv",
std::time::SystemTime::now()
.duration_since(std::time::SystemTime::UNIX_EPOCH)
.unwrap()
.as_nanos()
)
}
#[test]
fn test_create() {
let db = Rows::<Scaf>::new(&get_path()).unwrap();
assert_eq!(db.read_all().unwrap().len(), 0);
}
#[test]
fn test_fail() {
assert!(Rows::<Scaf>::new("").is_err());
assert!(Rows::<Scaf>::new("data.txt").is_err());
}
#[test]
fn test_insert() {
let db = Rows::<Scaf>::new(&get_path()).unwrap();
db.insert(&Scaf::default()).unwrap();
assert_eq!(db.read_all().unwrap().len(), 1);
db.insert(&Scaf::default()).unwrap();
assert_eq!(db.read_all().unwrap().len(), 2);
}
#[test]
fn test_bulk() {
let db = Rows::<Scaf>::new(&get_path()).unwrap();
assert!(db.overwrite(vec![]).is_err());
assert_eq!(db.read_all().unwrap().len(), 0);
db.overwrite(vec![Scaf::default()]).unwrap();
assert_eq!(db.read_all().unwrap().len(), 1);
db.drop().unwrap();
assert_eq!(db.read_all().unwrap().len(), 0);
}
}