use crate::schema::Notification;
use std::path::PathBuf;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum InboxError {
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
#[error("Notification not found: {0}")]
NotFound(String),
}
pub type Result<T> = std::result::Result<T, InboxError>;
pub fn inbox_dir() -> PathBuf {
dirs::data_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join("thor")
.join("inbox")
}
pub async fn ensure_inbox_dir() -> Result<PathBuf> {
let dir = inbox_dir();
tokio::fs::create_dir_all(&dir).await?;
Ok(dir)
}
pub async fn save_notification(notification: &Notification) -> Result<PathBuf> {
let dir = ensure_inbox_dir().await?;
let filename = format!(
"{}-{}-{}.json",
notification.timestamp.format("%Y%m%d%H%M%S"),
notification.branch.replace('/', "-"),
notification.id
);
let path = dir.join(filename);
let json = serde_json::to_string_pretty(notification)?;
tokio::fs::write(&path, json).await?;
Ok(path)
}
pub async fn list_notifications() -> Result<Vec<Notification>> {
let dir = inbox_dir();
if !dir.exists() {
return Ok(Vec::new());
}
let mut notifications = Vec::new();
let mut entries = tokio::fs::read_dir(dir).await?;
while let Some(entry) = entries.next_entry().await? {
let path = entry.path();
if path.extension().map(|e| e == "json").unwrap_or(false) {
let content = tokio::fs::read_to_string(&path).await?;
if let Ok(notification) = serde_json::from_str::<Notification>(&content) {
notifications.push(notification);
}
}
}
notifications.sort_by(|a, b| b.timestamp.cmp(&a.timestamp));
Ok(notifications)
}
pub async fn get_notification(id: &str) -> Result<Notification> {
let notifications = list_notifications().await?;
notifications
.into_iter()
.find(|n| n.id.to_string() == id || n.id.to_string().starts_with(id))
.ok_or_else(|| InboxError::NotFound(id.to_string()))
}
pub async fn delete_notification(id: &str) -> Result<()> {
let dir = inbox_dir();
let mut entries = tokio::fs::read_dir(&dir).await?;
while let Some(entry) = entries.next_entry().await? {
let path = entry.path();
if path.extension().map(|e| e == "json").unwrap_or(false) {
if let Ok(content) = tokio::fs::read_to_string(&path).await {
if let Ok(n) = serde_json::from_str::<Notification>(&content) {
if n.id.to_string() == id || n.id.to_string().starts_with(id) {
tokio::fs::remove_file(path).await?;
return Ok(());
}
}
}
}
}
Err(InboxError::NotFound(id.to_string()))
}