use crate::error::CoreError;
use crate::namespace::control::{
read_head_and_metadata_root_if_present, read_wal_floor_object, ControlObjectLoadError,
LoadedHeadObject,
};
use loonfs_api::wire::control::HeadState;
use loonfs_api::{manifest_object_id_manifest_id, ChangeSeq, ManifestId, ManifestObjectId};
use loonfs_api::{NamespaceId, ROOT_INODE_ID};
use loonfs_objectstore::ObjectStore;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MetadataBasis {
Genesis,
Manifest(BasisManifest),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BasisManifest {
pub owner_namespace_id: NamespaceId,
pub manifest_id: ManifestId,
pub manifest_object_id: ManifestObjectId,
pub manifest_payload_checksum: String,
}
impl MetadataBasis {
pub fn manifest(&self) -> Option<&BasisManifest> {
match self {
MetadataBasis::Genesis => None,
MetadataBasis::Manifest(manifest) => Some(manifest),
}
}
pub fn manifest_id(&self) -> ManifestId {
match self {
MetadataBasis::Genesis => ManifestId(0),
MetadataBasis::Manifest(manifest) => manifest.manifest_id,
}
}
pub fn is_owned_by(&self, namespace_id: &NamespaceId) -> bool {
self.manifest()
.is_some_and(|manifest| manifest.owner_namespace_id == *namespace_id)
}
}
pub(crate) struct LoadedNamespaceBasis {
pub(crate) head: LoadedHeadObject,
pub(crate) basis: MetadataBasis,
}
pub(crate) async fn read_head_and_metadata_basis<S: ObjectStore + ?Sized>(
store: &S,
namespace_id: &NamespaceId,
) -> Result<LoadedNamespaceBasis, ControlObjectLoadError> {
let (head, root) = read_head_and_metadata_root_if_present(store, namespace_id).await?;
let basis = match root {
Some(root) => MetadataBasis::Manifest(BasisManifest {
owner_namespace_id: namespace_id.clone(),
manifest_id: root.envelope.state.manifest_id,
manifest_object_id: root.envelope.state.manifest_object_id,
manifest_payload_checksum: root.envelope.state.manifest_payload_checksum,
}),
None => metadata_basis_without_root(&head.envelope.state)?,
};
Ok(LoadedNamespaceBasis { head, basis })
}
pub(crate) fn metadata_basis_without_root(
head: &HeadState,
) -> Result<MetadataBasis, ControlObjectLoadError> {
let Some(fork_basis) = &head.fork_basis else {
return Ok(MetadataBasis::Genesis);
};
let manifest_id = manifest_object_id_manifest_id(fork_basis.source_manifest_object_id.as_str())
.ok_or_else(|| ControlObjectLoadError::Codec {
object_key: loonfs_objectstore::keys::wal_head(head.namespace_id.as_str()),
message: format!(
"fork basis manifest object id `{}` does not encode a manifest id",
fork_basis.source_manifest_object_id
),
})?;
Ok(MetadataBasis::Manifest(BasisManifest {
owner_namespace_id: fork_basis.source_namespace_id.clone(),
manifest_id,
manifest_object_id: fork_basis.source_manifest_object_id.clone(),
manifest_payload_checksum: fork_basis.source_manifest_checksum.clone(),
}))
}
pub(crate) fn genesis_next_inode_id() -> loonfs_api::InodeId {
loonfs_api::InodeId(ROOT_INODE_ID.0 + 1)
}
pub(crate) fn namespace_birth_seq(head: &HeadState) -> ChangeSeq {
head.fork_basis
.as_ref()
.map_or(ChangeSeq(0), |fork_basis| fork_basis.fork_seq)
}
pub(crate) async fn resolve_retention_floor_seq<S: ObjectStore + ?Sized>(
store: &S,
head: &HeadState,
) -> Result<ChangeSeq, ControlObjectLoadError> {
match read_wal_floor_object(store, &head.namespace_id).await {
Ok(loaded) => Ok(loaded.envelope.state.floor_seq),
Err(ControlObjectLoadError::MissingObject { .. }) => Ok(namespace_birth_seq(head)),
Err(error) => Err(error),
}
}
pub(crate) fn advanced_floor_without_root(
namespace_id: &NamespaceId,
floor_seq: ChangeSeq,
) -> CoreError {
CoreError::NamespaceCorrupt(format!(
"namespace `{namespace_id}` has no materialized metadata root but its retention floor \
stands at `{floor_seq}`; the root object is missing"
))
}