chub-core 0.1.7

Chub: high-performance Rust implementation of Context Hub with team sharing
Documentation
use std::fs;
use std::path::PathBuf;

use serde::{Deserialize, Serialize};

use crate::config::chub_dir;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Annotation {
    pub id: String,
    pub note: String,
    #[serde(rename = "updatedAt")]
    pub updated_at: String,
}

fn annotations_dir() -> PathBuf {
    chub_dir().join("annotations")
}

fn annotation_path(entry_id: &str) -> PathBuf {
    let safe = entry_id.replace('/', "--");
    annotations_dir().join(format!("{}.json", safe))
}

pub fn read_annotation(entry_id: &str) -> Option<Annotation> {
    let path = annotation_path(entry_id);
    fs::read_to_string(&path)
        .ok()
        .and_then(|s| serde_json::from_str(&s).ok())
}

pub fn write_annotation(entry_id: &str, note: &str) -> Annotation {
    let dir = annotations_dir();
    let _ = fs::create_dir_all(&dir);

    let now = crate::build::builder::days_to_date(
        std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap_or_default()
            .as_secs()
            / 86400,
    );
    let secs = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .unwrap_or_default()
        .as_secs();
    let tod = secs % 86400;

    let updated_at = format!(
        "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.000Z",
        now.0,
        now.1,
        now.2,
        tod / 3600,
        (tod % 3600) / 60,
        tod % 60
    );

    let data = Annotation {
        id: entry_id.to_string(),
        note: note.to_string(),
        updated_at,
    };

    let _ = fs::write(
        annotation_path(entry_id),
        serde_json::to_string_pretty(&data).unwrap_or_default(),
    );

    data
}

pub fn clear_annotation(entry_id: &str) -> bool {
    fs::remove_file(annotation_path(entry_id)).is_ok()
}

pub fn list_annotations() -> Vec<Annotation> {
    let dir = annotations_dir();
    let files = match fs::read_dir(&dir) {
        Ok(entries) => entries,
        Err(_) => return vec![],
    };

    files
        .filter_map(|e| e.ok())
        .filter(|e| {
            e.path()
                .extension()
                .map(|ext| ext == "json")
                .unwrap_or(false)
        })
        .filter_map(|e| {
            fs::read_to_string(e.path())
                .ok()
                .and_then(|s| serde_json::from_str::<Annotation>(&s).ok())
        })
        .collect()
}