use std::{
fmt::Debug,
fs::{create_dir_all, File},
io::{Seek, Write},
path::{Path, PathBuf},
};
use log::info;
use serde::Serialize;
use uuid::Uuid;
use crate::{Dataset, Host, Session};
#[cfg(feature = "json")]
pub mod json;
#[cfg(feature = "neo4j")]
pub mod neo4j;
pub trait Database: Debug {
type Error: std::error::Error;
fn add_session(
&mut self,
session: Session,
) -> Result<Box<dyn DatabaseSession<Error = Self::Error>>, Self::Error>;
fn remove_session(&mut self, session: Session) -> Result<(), Self::Error>;
#[allow(clippy::type_complexity)]
fn get_sessions(
&self,
) -> Result<Vec<Box<dyn DatabaseSession<Error = Self::Error>>>, Self::Error>;
}
impl<E> Database for Box<dyn Database<Error = E>>
where
E: std::error::Error,
{
type Error = E;
fn add_session(
&mut self,
session: Session,
) -> Result<Box<dyn DatabaseSession<Error = Self::Error>>, Self::Error> {
(**self).add_session(session)
}
fn remove_session(&mut self, session: Session) -> Result<(), Self::Error> {
(**self).remove_session(session)
}
fn get_sessions(
&self,
) -> Result<Vec<Box<dyn DatabaseSession<Error = Self::Error>>>, Self::Error> {
(**self).get_sessions()
}
}
pub trait DatabaseSession: Debug {
type Error: std::error::Error;
fn session(&self) -> &Session;
fn add_dataset(&mut self, dataset: &Dataset) -> Result<(), Self::Error>;
fn get_datasets(&self) -> Result<Vec<Dataset>, Self::Error>;
fn remove_dataset(&mut self, id: &Uuid) -> Result<(), Self::Error>;
}
#[cfg(feature = "sha")]
pub struct DatasetWriter {
id: Uuid,
file: File,
path: PathBuf,
}
#[cfg(feature = "sha")]
impl DatasetWriter {
pub fn new<P: AsRef<Path>>(folder: P) -> Result<Self, std::io::Error> {
let mut path: PathBuf = folder.as_ref().into();
let id = Uuid::new_v4();
create_dir_all(&path)?;
path.push(format!("{id}"));
let file = File::create(&path)?;
Ok(Self { id, file, path })
}
pub fn finalize<S: Serialize + Debug>(
self,
metadata: S,
) -> Result<Dataset, crate::error::Error> {
Dataset::from_file(self.path, metadata, Some(self.id))
}
pub fn finalize_with_host<S: Serialize + Debug>(
self,
metadata: S,
host: Option<Host>,
) -> Result<Dataset, crate::error::Error> {
Dataset::from_file_with_host(self.path, metadata, Some(self.id), host)
}
}
#[cfg(feature = "sha")]
impl Write for DatasetWriter {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.file.write(buf)
}
fn flush(&mut self) -> std::io::Result<()> {
self.file.flush()
}
}
#[cfg(feature = "sha")]
impl Seek for DatasetWriter {
fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
self.file.seek(pos)
}
}
pub fn transfer<A: Database + Debug, B: Database + Debug>(
a: &A,
b: &mut B,
) -> Result<(), Box<dyn std::error::Error>>
where
A::Error: 'static,
B::Error: 'static,
{
info!("Transfering from {a:?} to {b:?}");
for session in a.get_sessions()? {
info!("Transfering session {:?}", session.session());
let mut session_b = b.add_session(session.session().clone())?;
for dataset in session.get_datasets()? {
info!("Transfering dataset {dataset:?}");
session_b.add_dataset(&dataset)?;
}
}
Ok(())
}