Skip to main content

loonfs_core/namespace/
catalog.rs

1//! The namespace catalog: the namespace's immutable identity — its content
2//! store and name policy — read from the head that carries them.
3
4use crate::namespace::control::{read_head_object, ControlObjectLoadError};
5use loonfs_api::wire::control::HeadState;
6use loonfs_api::{ContentStoreId, NamespaceId};
7use loonfs_objectstore::ObjectStore;
8use thiserror::Error;
9
10#[derive(Debug, Clone, PartialEq, Eq, Error)]
11pub enum NamespaceCatalogLoadError {
12    #[error("failed to load namespace head: {0}")]
13    LoadHead(#[from] ControlObjectLoadError),
14}
15
16/// The namespace's spec-immutable identity: the content store it publishes
17/// file bytes into and the name policy its keys are computed under.
18///
19/// Both live in the head and are carried forward verbatim by every head the
20/// namespace ever publishes, so an entry built from any head of a namespace
21/// is valid for the namespace's whole life.
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct VerifiedNamespaceCatalogEntry {
24    namespace_id: NamespaceId,
25    content_store_id: ContentStoreId,
26}
27
28impl VerifiedNamespaceCatalogEntry {
29    /// Reads the namespace's identity off a loaded head. No I/O: the head is
30    /// the only durable home of these fields.
31    pub fn from_head(head: &HeadState) -> Self {
32        Self {
33            namespace_id: head.namespace_id.clone(),
34            content_store_id: head.content_store_id.clone(),
35        }
36    }
37
38    /// Returns the namespace whose immutable identity this entry carries.
39    pub fn namespace_id(&self) -> &NamespaceId {
40        &self.namespace_id
41    }
42
43    pub fn content_store_id(&self) -> &ContentStoreId {
44        &self.content_store_id
45    }
46}
47
48/// Loads the namespace's immutable identity for a caller holding no head.
49///
50/// Callers that already loaded or pinned a head build the entry from it
51/// with [`VerifiedNamespaceCatalogEntry::from_head`] instead of paying this
52/// read.
53pub async fn load_namespace_catalog_entry<S: ObjectStore + ?Sized>(
54    store: &S,
55    expected_namespace_id: &NamespaceId,
56) -> Result<VerifiedNamespaceCatalogEntry, NamespaceCatalogLoadError> {
57    let head = read_head_object(store, expected_namespace_id).await?;
58    Ok(VerifiedNamespaceCatalogEntry::from_head(
59        &head.envelope.state,
60    ))
61}
62
63pub(crate) async fn load_namespace_content_store_id<S: ObjectStore + ?Sized>(
64    store: &S,
65    expected_namespace_id: &NamespaceId,
66) -> Result<ContentStoreId, NamespaceCatalogLoadError> {
67    Ok(load_namespace_catalog_entry(store, expected_namespace_id)
68        .await?
69        .content_store_id)
70}