use bincode::{Decode, Encode};
use context_error::BoxedError;
use directories::{BaseDirs, ProjectDirs};
use sha2::Digest;
use crate::{CVError, Curie, hash_buf_reader::HashBufReader};
pub trait CVSource {
const AUTOMATICALLY_WRITE_UNCOMPRESSED: bool = false;
type Data: CVData + 'static;
type Structure: CVStructure<Self::Data> + Encode + Decode<()>;
fn cv_name() -> &'static str;
fn files() -> &'static [CVFile];
fn static_data() -> Option<(CVVersion, Self::Structure)>;
fn default_stem() -> std::path::PathBuf {
let proj_dirs = ProjectDirs::from("org", "rusteomics", "mzcore");
let base = BaseDirs::new();
let folder = proj_dirs
.map(|p| p.data_dir().to_owned())
.or_else(|| base.map(|b| b.home_dir().to_owned()))
.expect("No suitable base directory could be found");
if !folder.exists() {
drop(std::fs::create_dir_all(&folder));
}
folder.join(Self::cv_name())
}
fn parse(
reader: impl Iterator<Item = HashBufReader<Box<dyn std::io::Read>, impl Digest>>,
) -> Result<(CVVersion, Self::Structure), Vec<BoxedError<'static, CVError>>>;
fn write_uncompressed<W: std::io::Write>(
_writer: W,
_version: &CVVersion,
_data: impl Iterator<Item = std::sync::Arc<Self::Data>>,
) -> Result<(), BoxedError<'static, CVError>> {
Ok(())
}
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct CVFile {
pub name: &'static str,
pub extension: &'static str,
pub url: Option<&'static str>,
pub compression: CVCompression,
}
#[derive(Clone, Debug, Decode, Default, Encode)]
pub struct CVVersion {
pub last_updated: Option<(u16, u8, u8, u8, u8)>,
pub version: Option<String>,
pub hash: Vec<u8>,
}
impl CVVersion {
pub fn last_updated(&self) -> Option<String> {
self.last_updated.map(|(year, month, day, hour, minute)| {
format!("{year:04}-{month:02}-{day:02} {hour:02}:{minute:02}")
})
}
pub fn hash_hex(&self) -> String {
use std::fmt::Write;
let mut res = String::with_capacity(self.hash.len() * 2);
for n in &self.hash {
write!(&mut res, "{n:02x}").unwrap();
}
res
}
}
pub trait CVData: Clone {
type Index: std::fmt::Debug + Clone + std::hash::Hash + Eq;
fn index(&self) -> Option<Self::Index>;
fn curie(&self) -> Option<Curie> {
None
}
fn name(&self) -> Option<std::borrow::Cow<'_, str>>;
fn synonyms(&self) -> impl Iterator<Item = &str>;
fn parents(&self) -> impl Iterator<Item = &Self::Index> {
let value: Option<&Self::Index> = None;
value.into_iter()
}
}
#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
pub enum CVCompression {
#[default]
None,
#[deprecated = "Is not implemented yet"]
Lzw,
}
pub trait CVStructure<Data>: Default {
fn is_empty(&self) -> bool;
fn len(&self) -> usize;
fn clear(&mut self);
type IterIndexed<'a>: Iterator<Item = (Self::Index, std::sync::Arc<Data>)>
where
Self: 'a;
fn iter_indexed(&self) -> Self::IterIndexed<'_>;
type IterData<'a>: Iterator<Item = std::sync::Arc<Data>>
where
Self: 'a;
fn iter_data(&self) -> Self::IterData<'_>;
fn add(&mut self, data: std::sync::Arc<Data>);
type Index: Clone;
fn index(&self, index: Self::Index) -> Option<std::sync::Arc<Data>>;
fn remove(&mut self, index: Self::Index);
}
impl<T> CVStructure<T> for Vec<std::sync::Arc<T>> {
fn is_empty(&self) -> bool {
self.is_empty()
}
fn len(&self) -> usize {
self.len()
}
fn clear(&mut self) {
self.clear();
}
type IterIndexed<'a>
= std::iter::Enumerate<std::iter::Cloned<std::slice::Iter<'a, std::sync::Arc<T>>>>
where
T: 'a;
fn iter_indexed(&self) -> Self::IterIndexed<'_> {
self.iter().cloned().enumerate()
}
type IterData<'a>
= std::iter::Cloned<std::slice::Iter<'a, std::sync::Arc<T>>>
where
T: 'a;
fn iter_data(&self) -> Self::IterData<'_> {
self.iter().cloned()
}
fn add(&mut self, data: std::sync::Arc<T>) {
self.push(data);
}
type Index = usize;
fn index(&self, index: Self::Index) -> Option<std::sync::Arc<T>> {
self.get(index).cloned()
}
fn remove(&mut self, index: Self::Index) {
self.remove(index);
}
}