use std::fs;
use std::path::{Path, PathBuf};
use serde::{Deserialize, Serialize};
use super::error::VacuumError;
pub const MANIFEST_FILE: &str = "metadata-manifest.json";
pub const MANIFEST_FORMAT_VERSION: u32 = 1;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ManifestEntryState {
Reserved,
Publishing,
Published,
Updating { nonce: u64 },
Decommissioning,
}
impl ManifestEntryState {
pub const fn is_settled(&self) -> bool {
matches!(self, Self::Published)
}
pub fn display_name(&self) -> String {
match self {
Self::Reserved => "Reserved".to_owned(),
Self::Publishing => "Publishing".to_owned(),
Self::Published => "Published".to_owned(),
Self::Updating { nonce } => format!("Updating {{ nonce: {nonce} }}"),
Self::Decommissioning => "Decommissioning".to_owned(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ManifestSourceKind {
RefsDir,
SnapshotRegistry,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ManifestEntry {
pub kind: ManifestSourceKind,
pub path: PathBuf,
pub state: ManifestEntryState,
pub reservation_nonce: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub shards: Option<Vec<usize>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MetadataManifest {
pub format_version: u32,
pub generation: u64,
pub entries: Vec<ManifestEntry>,
}
impl MetadataManifest {
pub fn resolved_path(data_dir: &Path, entry: &ManifestEntry) -> PathBuf {
if entry.path.is_absolute() {
entry.path.clone()
} else {
data_dir.join(&entry.path)
}
}
}
pub fn read_manifest(data_dir: &Path) -> Result<Option<MetadataManifest>, VacuumError> {
let path = data_dir.join(MANIFEST_FILE);
let bytes = match fs::read(&path) {
Ok(bytes) => bytes,
Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(None),
Err(error) => return Err(VacuumError::Io { path, error }),
};
let manifest: MetadataManifest =
serde_json::from_slice(&bytes).map_err(|error| VacuumError::MetadataOpen {
path: path.clone(),
reason: error.to_string(),
})?;
if manifest.format_version != MANIFEST_FORMAT_VERSION {
return Err(VacuumError::MetadataOpen {
path,
reason: format!(
"manifest format_version {} is not supported (this binary reads {})",
manifest.format_version, MANIFEST_FORMAT_VERSION
),
});
}
Ok(Some(manifest))
}