use serde::{Deserialize, Serialize};
use crate::error::{ItemKind, MindError, Result};
use crate::paths::Paths;
mod kind_serde {
use super::ItemKind;
use serde::{Deserialize, Deserializer, Serializer, de::Error};
pub fn serialize<S: Serializer>(kind: &ItemKind, s: S) -> Result<S::Ok, S::Error> {
s.serialize_str(kind.as_str())
}
pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<ItemKind, D::Error> {
let raw = String::deserialize(d)?;
ItemKind::parse(&raw).ok_or_else(|| D::Error::custom(format!("unknown item kind '{raw}'")))
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InstalledItem {
#[serde(with = "kind_serde")]
pub kind: ItemKind,
pub name: String,
pub bare_name: String,
pub source: String,
pub commit: String,
pub hash: String,
pub store: String,
pub links: Vec<String>,
#[serde(default)]
pub description: Option<String>,
}
impl InstalledItem {
pub fn key(&self) -> String {
format!("{}:{}", self.kind.as_str(), self.name)
}
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct Manifest {
#[serde(default)]
pub items: std::collections::BTreeMap<String, InstalledItem>,
}
impl Manifest {
pub fn load(paths: &Paths) -> Result<Self> {
let file = paths.manifest_file();
match std::fs::read(&file) {
Ok(bytes) => {
serde_json::from_slice(&bytes).map_err(|e| MindError::json("manifest.json", e))
}
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(Manifest::default()),
Err(e) => Err(MindError::io(&file, e)),
}
}
pub fn save(&self, paths: &Paths) -> Result<()> {
paths.ensure_layout()?;
let file = paths.manifest_file();
let json =
serde_json::to_vec_pretty(self).map_err(|e| MindError::json("manifest.json", e))?;
Paths::atomic_write(&file, &json)
}
pub fn insert(&mut self, item: InstalledItem) {
self.items.insert(item.key(), item);
}
}