use std::{
fs,
path::{Path, PathBuf},
};
use walkdir;
use crate::{mo::Message, storage, Error};
const SBD_EXTENSION: &str = "sbd";
#[derive(Clone, Debug)]
pub struct Storage {
root: PathBuf,
}
#[derive(Debug)]
pub struct StorageIterator {
iter: walkdir::IntoIter,
}
impl Storage {
pub fn open<P: AsRef<Path>>(root: P) -> Result<Storage, Error> {
let metadata = fs::metadata(root.as_ref())?;
if !metadata.is_dir() {
Err(Error::NotADirectory(root.as_ref().to_path_buf()))
} else {
Ok(Storage {
root: root.as_ref().to_path_buf(),
})
}
}
pub fn iter(&self) -> StorageIterator {
StorageIterator::new(&self.root)
}
}
impl storage::Storage for Storage {
fn store(&mut self, message: Message) -> Result<(), Error> {
let mut path_buf = self.root.clone();
path_buf.push(message.imei());
path_buf.push(message.time_of_session().format("%Y").to_string());
path_buf.push(message.time_of_session().format("%m").to_string());
fs::create_dir_all(&path_buf)?;
path_buf.push(
message
.time_of_session()
.format(&format!("%y%m%d_%H%M%S.{}", SBD_EXTENSION))
.to_string(),
);
let mut file = fs::File::create(&path_buf)?;
message.write_to(&mut file)?;
Ok(())
}
fn messages(&self) -> Result<Vec<Message>, Error> {
self.iter().collect()
}
fn messages_from_imei(&self, imei: &str) -> Result<Vec<Message>, Error> {
let mut path = self.root.clone();
path.push(imei);
StorageIterator::new(&path).collect()
}
}
impl StorageIterator {
fn new(root: &Path) -> StorageIterator {
StorageIterator {
iter: walkdir::WalkDir::new(root).into_iter(),
}
}
}
impl Iterator for StorageIterator {
type Item = Result<Message, Error>;
fn next(&mut self) -> Option<Self::Item> {
self.iter
.by_ref()
.find(|r| {
r.as_ref()
.map(|d| d.path().extension().is_some_and(|e| e == SBD_EXTENSION))
.unwrap_or(false)
})
.map(|r| {
r.map_err(Error::from)
.and_then(|d| Message::from_path(d.path()))
})
}
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use tempdir::TempDir;
use super::*;
use crate::{mo::Message, storage::Storage as StorageTrait};
#[test]
fn open() {
Storage::open(TempDir::new("").unwrap().path()).unwrap();
}
#[test]
fn no_directory() {
assert!(Storage::open("not/a/real/directory").is_err());
}
#[test]
fn file_is_error() {
assert!(Storage::open("data/0-mo.sbd").is_err());
}
#[test]
fn store() {
let tempdir = TempDir::new("").unwrap();
let mut storage = Storage::open(tempdir.path()).unwrap();
let message = Message::from_path("data/0-mo.sbd").unwrap();
storage.store(message).unwrap();
let mut message_path = PathBuf::from(tempdir.path());
message_path.push("300234063904190");
message_path.push("2015");
message_path.push("07");
message_path.push("150709_181508.sbd");
Message::from_path(message_path).unwrap();
}
#[test]
fn iter() {
let tempdir = TempDir::new("").unwrap();
let mut storage = Storage::open(tempdir.path()).unwrap();
assert_eq!(0, storage.iter().count());
let message = Message::from_path("data/0-mo.sbd").unwrap();
storage.store(message).unwrap();
assert_eq!(1, storage.iter().count());
}
#[test]
fn messages_from_imei() {
let tempdir = TempDir::new("").unwrap();
let mut storage = Storage::open(tempdir.path()).unwrap();
let message = Message::from_path("data/0-mo.sbd").unwrap();
storage.store(message.clone()).unwrap();
let messages = storage.messages_from_imei("300234063904190").unwrap();
assert_eq!(vec![message], messages);
let messages = storage.messages_from_imei("300234063904191").unwrap();
assert!(messages.is_empty());
}
}