use std::fmt;
use std::path::{Path, PathBuf};
use crate::content_address::GraphHash;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum GraphKind {
Lockfile,
Ast,
Module,
EvalCacheEntry,
Derivation,
}
impl GraphKind {
#[must_use]
pub fn dir_name(self) -> &'static str {
match self {
Self::Lockfile => "lockfile",
Self::Ast => "ast",
Self::Module => "module",
Self::EvalCacheEntry => "eval-cache",
Self::Derivation => "derivation",
}
}
#[must_use]
pub fn tag(self) -> u8 {
match self {
Self::Lockfile => 1,
Self::Ast => 2,
Self::Module => 3,
Self::EvalCacheEntry => 4,
Self::Derivation => 5,
}
}
#[must_use]
pub fn from_tag(tag: u8) -> Option<Self> {
match tag {
1 => Some(Self::Lockfile),
2 => Some(Self::Ast),
3 => Some(Self::Module),
4 => Some(Self::EvalCacheEntry),
5 => Some(Self::Derivation),
_ => None,
}
}
}
impl fmt::Display for GraphKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.dir_name())
}
}
#[derive(Debug, Clone)]
pub struct StoreLayout {
root: PathBuf,
}
impl StoreLayout {
#[must_use]
pub fn at(root: impl Into<PathBuf>) -> Self {
Self { root: root.into() }
}
#[must_use]
pub fn root(&self) -> &Path {
&self.root
}
#[must_use]
pub fn index_db_path(&self) -> PathBuf {
self.root.join("index.redb")
}
#[must_use]
pub fn blobs_dir(&self, kind: GraphKind) -> PathBuf {
self.root.join("blobs").join(kind.dir_name())
}
#[must_use]
pub fn blob_path(&self, kind: GraphKind, hash: GraphHash) -> PathBuf {
let (aa, bb) = hash.shard_prefix();
self.blobs_dir(kind).join(aa).join(bb).join(format!(
"{}.rkyv",
hash.display_short(),
))
}
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn layout_paths_compose_as_documented() {
let layout = StoreLayout::at("/var/lib/sui/graph-store");
let hash = GraphHash::of(b"layout test");
let (aa, bb) = hash.shard_prefix();
let expected = format!(
"/var/lib/sui/graph-store/blobs/lockfile/{}/{}/{}.rkyv",
aa,
bb,
hash.display_short()
);
assert_eq!(
layout.blob_path(GraphKind::Lockfile, hash).to_string_lossy(),
expected
);
}
#[test]
fn every_kind_tag_round_trips() {
for kind in [
GraphKind::Lockfile,
GraphKind::Ast,
GraphKind::Module,
GraphKind::EvalCacheEntry,
GraphKind::Derivation,
] {
assert_eq!(GraphKind::from_tag(kind.tag()), Some(kind));
}
}
#[test]
fn unknown_tag_returns_none() {
assert!(GraphKind::from_tag(0).is_none());
assert!(GraphKind::from_tag(255).is_none());
}
}