1use std::{
6 fmt::Debug,
7 fs::{create_dir_all, File},
8 io::{Seek, Write},
9 path::{Path, PathBuf},
10};
11
12use log::info;
13use serde::Serialize;
14use uuid::Uuid;
15
16use crate::{Dataset, Host, Session};
17
18#[cfg(feature = "json")]
19pub mod json;
20#[cfg(feature = "neo4j")]
21pub mod neo4j;
22
23pub trait Database: Debug {
28 type Error: std::error::Error;
29
30 fn add_session(
32 &mut self,
33 session: Session,
34 ) -> Result<Box<dyn DatabaseSession<Error = Self::Error>>, Self::Error>;
35
36 fn remove_session(&mut self, session: Session) -> Result<(), Self::Error>;
38
39 #[allow(clippy::type_complexity)]
41 fn get_sessions(
42 &self,
43 ) -> Result<Vec<Box<dyn DatabaseSession<Error = Self::Error>>>, Self::Error>;
44}
45
46impl<E> Database for Box<dyn Database<Error = E>>
47where
48 E: std::error::Error,
49{
50 type Error = E;
51
52 fn add_session(
53 &mut self,
54 session: Session,
55 ) -> Result<Box<dyn DatabaseSession<Error = Self::Error>>, Self::Error> {
56 (**self).add_session(session)
57 }
58
59 fn remove_session(&mut self, session: Session) -> Result<(), Self::Error> {
60 (**self).remove_session(session)
61 }
62
63 fn get_sessions(
64 &self,
65 ) -> Result<Vec<Box<dyn DatabaseSession<Error = Self::Error>>>, Self::Error> {
66 (**self).get_sessions()
67 }
68}
69
70pub trait DatabaseSession: Debug {
75 type Error: std::error::Error;
76
77 fn session(&self) -> &Session;
79
80 fn add_dataset(&mut self, dataset: &Dataset) -> Result<(), Self::Error>;
82
83 fn get_datasets(&self) -> Result<Vec<Dataset>, Self::Error>;
85
86 fn remove_dataset(&mut self, id: &Uuid) -> Result<(), Self::Error>;
88}
89
90#[cfg(feature = "sha")]
115pub struct DatasetWriter {
116 id: Uuid,
117 file: File,
118 path: PathBuf,
119}
120
121#[cfg(feature = "sha")]
122impl DatasetWriter {
123 pub fn new<P: AsRef<Path>>(folder: P) -> Result<Self, std::io::Error> {
128 let mut path: PathBuf = folder.as_ref().into();
129 let id = Uuid::new_v4();
130 create_dir_all(&path)?;
131 path.push(format!("{id}"));
132 let file = File::create(&path)?;
133 Ok(Self { id, file, path })
134 }
135
136 pub fn finalize<S: Serialize + Debug>(
144 self,
145 metadata: S,
146 ) -> Result<Dataset, crate::error::Error> {
147 Dataset::from_file(self.path, metadata, Some(self.id))
148 }
149
150 pub fn finalize_with_host<S: Serialize + Debug>(
158 self,
159 metadata: S,
160 host: Option<Host>,
161 ) -> Result<Dataset, crate::error::Error> {
162 Dataset::from_file_with_host(self.path, metadata, Some(self.id), host)
163 }
164}
165
166#[cfg(feature = "sha")]
167impl Write for DatasetWriter {
168 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
169 self.file.write(buf)
170 }
171
172 fn flush(&mut self) -> std::io::Result<()> {
173 self.file.flush()
174 }
175}
176
177#[cfg(feature = "sha")]
178impl Seek for DatasetWriter {
179 fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
180 self.file.seek(pos)
181 }
182}
183
184pub fn transfer<A: Database + Debug, B: Database + Debug>(
189 a: &A,
190 b: &mut B,
191) -> Result<(), Box<dyn std::error::Error>>
192where
193 A::Error: 'static,
194 B::Error: 'static,
195{
196 info!("Transfering from {a:?} to {b:?}");
197 for session in a.get_sessions()? {
198 info!("Transfering session {:?}", session.session());
199 let mut session_b = b.add_session(session.session().clone())?;
200 for dataset in session.get_datasets()? {
201 info!("Transfering dataset {dataset:?}");
202 session_b.add_dataset(&dataset)?;
203 }
204 }
205 Ok(())
206}