Skip to main content

lean_ctx/core/knowledge/
store.rs

1//! Storage contract for portable local knowledge objects.
2
3use super::query::KnowledgeQuery;
4use lean_ctx_protocol::KnowledgeObjectV1;
5use std::error::Error;
6use std::fmt;
7
8/// Errors returned by a Knowledge Hub store.
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum KnowledgeStoreError {
11    /// The content-addressed object has no usable identifier.
12    EmptyObjectId,
13    /// A supersession requested an object that is not present.
14    NotFound(String),
15    /// The object failed protocol validation.
16    InvalidObject(String),
17}
18
19impl fmt::Display for KnowledgeStoreError {
20    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
21        match self {
22            Self::EmptyObjectId => formatter.write_str("knowledge object id must not be empty"),
23            Self::NotFound(object_id) => {
24                write!(formatter, "knowledge object not found: {object_id}")
25            }
26            Self::InvalidObject(message) => {
27                write!(formatter, "invalid knowledge object: {message}")
28            }
29        }
30    }
31}
32
33impl Error for KnowledgeStoreError {}
34
35/// Minimal CRUD and supersession surface shared by local and enterprise stores.
36pub trait KnowledgeStore {
37    /// Fetch a content-addressed object by its stable object id.
38    fn get(&self, object_id: &str) -> Option<KnowledgeObjectV1>;
39
40    /// Insert or replace an object.
41    fn put(&mut self, object: KnowledgeObjectV1) -> Result<(), KnowledgeStoreError>;
42
43    /// Return objects matching every supplied filter.
44    fn query(&self, query: &KnowledgeQuery) -> Vec<KnowledgeObjectV1>;
45
46    /// Store a replacement and link the old object to it through
47    /// `ValidityWindow::superseded_by`.
48    fn supersede(
49        &mut self,
50        old_object_id: &str,
51        replacement: KnowledgeObjectV1,
52    ) -> Result<(), KnowledgeStoreError>;
53
54    /// Delete an object and report whether it existed.
55    fn delete(&mut self, object_id: &str) -> bool;
56}
57
58/// Return the stable identifier used by the local store.
59pub fn object_id(object: &KnowledgeObjectV1) -> &str {
60    object.object_id()
61}
62
63/// Validate the protocol-level invariants required before storing an object.
64pub fn validate_object(object: &KnowledgeObjectV1) -> Result<(), KnowledgeStoreError> {
65    if object_id(object).trim().is_empty() {
66        return Err(KnowledgeStoreError::EmptyObjectId);
67    }
68    object
69        .validate()
70        .map_err(|error| KnowledgeStoreError::InvalidObject(error.to_string()))
71}