orthodoxy 0.2.3

A collection of my utility libraries
Documentation
use std::{
    fs::OpenOptions,
    io::{Read, Write},
};

use camino::Utf8Path;
use facet::Facet;
use facet_json::JsonSerializeError;

#[derive(thiserror::Error, Debug)]
pub enum Errors {
    #[error(transparent)]
    StdIo(#[from] std::io::Error),

    #[error("{0}")]
    Custom(String),
}

impl From<String> for Errors {
    fn from(value: String) -> Self {
        Self::Custom(value)
    }
}

impl From<&str> for Errors {
    fn from(value: &str) -> Self {
        Self::from(value.to_string())
    }
}

impl From<facet_json::DeserializeError> for Errors {
    fn from(value: facet_json::DeserializeError) -> Self {
        todo!()
    }
}

impl From<facet_format::SerializeError<JsonSerializeError>> for Errors {
    fn from(value: facet_format::SerializeError<JsonSerializeError>) -> Self {
        todo!()
    }
}

pub type Res<T> = Result<T, Errors>;

#[derive(Facet, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct Annotation<T, A> {
    inner: T,
    key: &'static str,
    value: A,
}

impl<T, A> Annotation<T, A> {
    pub fn store_xattr(&self, path: &Utf8Path) -> Res<()> {
        todo!()
    }
    pub fn load_xattr(path: &Utf8Path) -> Res<Self> {
        todo!()
    }
}

impl<T, A> Annotation<T, A> {
    pub fn store_sidecar_file(&self, path: &Utf8Path, kind: SidecarKind) -> Res<()> {
        todo!()
    }
    pub fn load_sidecar_file(path: &Utf8Path, kind: SidecarKind) -> Res<Self> {
        todo!()
    }
}

#[derive(facet::Facet, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default)]
#[repr(C)]
pub enum SidecarKind {
    #[default]
    Json,
    JsonLines,
}

impl SidecarKind {
    pub fn guess_kind(path: &Utf8Path) -> Option<Self> {
        if let Some(ext) = path.extension() {
            match ext {
                "json" => Some(Self::Json),
                "ndjson" => Some(Self::JsonLines),
                _ => None,
            }
        } else {
            None
        }
    }
    pub fn load<T, A>(&self, path: &Utf8Path) -> Res<Annotation<T, A>> {
        let file = OpenOptions::new()
            .create_new(true)
            .create(true)
            .truncate(false)
            .read(true)
            .open(path)?;

        todo!()
    }
    pub fn store<T, A>(&self, annot: Annotation<T, A>, path: &Utf8Path) -> Res<()> {
        let data: facet_value::Value = if path.canonicalize_utf8()?.exists() {
            let mut f = OpenOptions::new().read(true).open(path)?;
            let mut buf = vec![];
            f.read_to_end(&mut buf)?;
            facet_json::from_slice(&buf)?
        } else {
            facet_json::from_str(&format!("{{\"{}\": null}}", annot.key))?
        };

        let mut file = OpenOptions::new()
            .create_new(true)
            .create(true)
            .truncate(true)
            .write(true)
            .open(path)?;

        let s = facet_json::to_string_pretty(&data)?;
        writeln!(&mut file, "{s}")?;
        Ok(())
    }
}