Skip to main content

khive_storage/
note.rs

1//! Note storage capability — temporal-referential record CRUD.
2
3use async_trait::async_trait;
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6use uuid::Uuid;
7
8use crate::types::{BatchWriteSummary, DeleteMode, Page, PageRequest, StorageResult};
9
10pub use khive_types::NoteKind;
11
12/// A storage-level note record. Flat, SQL-friendly representation.
13#[derive(Clone, Debug, Serialize, Deserialize)]
14pub struct Note {
15    pub id: Uuid,
16    pub namespace: String,
17    pub kind: NoteKind,
18    pub name: Option<String>,
19    pub content: String,
20    pub salience: f64,
21    pub decay_factor: f64,
22    pub expires_at: Option<i64>,
23    pub properties: Option<Value>,
24    pub created_at: i64,
25    pub updated_at: i64,
26    pub deleted_at: Option<i64>,
27}
28
29impl Note {
30    pub fn new(namespace: impl Into<String>, kind: NoteKind, content: impl Into<String>) -> Self {
31        let now = chrono::Utc::now().timestamp_micros();
32        Self {
33            id: Uuid::new_v4(),
34            namespace: namespace.into(),
35            kind,
36            name: None,
37            content: content.into(),
38            salience: 0.5,
39            decay_factor: 0.0,
40            expires_at: None,
41            properties: None,
42            created_at: now,
43            updated_at: now,
44            deleted_at: None,
45        }
46    }
47
48    pub fn with_name(mut self, n: impl Into<String>) -> Self {
49        self.name = Some(n.into());
50        self
51    }
52
53    pub fn with_salience(mut self, s: f64) -> Self {
54        self.salience = s.clamp(0.0, 1.0);
55        self
56    }
57
58    pub fn with_decay(mut self, d: f64) -> Self {
59        self.decay_factor = d.max(0.0);
60        self
61    }
62
63    pub fn with_properties(mut self, p: Value) -> Self {
64        self.properties = Some(p);
65        self
66    }
67}
68
69#[async_trait]
70pub trait NoteStore: Send + Sync + 'static {
71    async fn upsert_note(&self, note: Note) -> StorageResult<()>;
72    async fn upsert_notes(&self, notes: Vec<Note>) -> StorageResult<BatchWriteSummary>;
73    async fn get_note(&self, id: Uuid) -> StorageResult<Option<Note>>;
74    async fn delete_note(&self, id: Uuid, mode: DeleteMode) -> StorageResult<bool>;
75    async fn query_notes(
76        &self,
77        namespace: &str,
78        kind: Option<NoteKind>,
79        page: PageRequest,
80    ) -> StorageResult<Page<Note>>;
81    async fn count_notes(&self, namespace: &str, kind: Option<NoteKind>) -> StorageResult<u64>;
82
83    async fn upsert_note_if_below_quota(&self, note: Note, max_notes: u64) -> StorageResult<bool> {
84        let count = self.count_notes(&note.namespace, None).await?;
85        if count >= max_notes {
86            return Ok(false);
87        }
88        self.upsert_note(note).await?;
89        Ok(true)
90    }
91}