Skip to main content

ucp_graph/store/
mod.rs

1mod document;
2#[cfg(not(target_arch = "wasm32"))]
3mod sqlite;
4
5use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7use thiserror::Error;
8use ucm_core::{BlockId, PortableDocument};
9
10use crate::types::GraphEdgeSummary;
11
12pub use document::InMemoryGraphStore;
13#[cfg(not(target_arch = "wasm32"))]
14pub use sqlite::SqliteGraphStore;
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct GraphNodeRecord {
18    pub block_id: BlockId,
19    #[serde(default, skip_serializing_if = "Option::is_none")]
20    pub label: Option<String>,
21    pub content_type: String,
22    #[serde(default, skip_serializing_if = "Option::is_none")]
23    pub semantic_role: Option<String>,
24    #[serde(default)]
25    pub tags: Vec<String>,
26    #[serde(default, skip_serializing_if = "Option::is_none")]
27    pub parent: Option<BlockId>,
28    pub children: usize,
29    pub outgoing_edges: usize,
30    pub incoming_edges: usize,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct GraphStoreStats {
35    pub backend: String,
36    pub document_id: String,
37    pub root_block_id: BlockId,
38    pub node_count: usize,
39    pub explicit_edge_count: usize,
40    pub structural_edge_count: usize,
41    pub captured_at: DateTime<Utc>,
42    #[serde(default, skip_serializing_if = "Option::is_none")]
43    pub graph_key: Option<String>,
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct GraphStoreObservability {
48    pub stats: GraphStoreStats,
49    #[serde(default)]
50    pub indexed_fields: Vec<String>,
51}
52
53#[derive(Debug, Error)]
54pub enum GraphStoreError {
55    #[error(transparent)]
56    Ucm(#[from] ucm_core::Error),
57    #[error(transparent)]
58    Regex(#[from] regex::Error),
59    #[error(transparent)]
60    Serde(#[from] serde_json::Error),
61    #[error(transparent)]
62    Io(#[from] std::io::Error),
63    #[error(transparent)]
64    #[cfg(not(target_arch = "wasm32"))]
65    Sqlite(#[from] rusqlite::Error),
66    #[error("graph not found: {0}")]
67    GraphNotFound(String),
68}
69
70pub trait GraphStore {
71    fn stats(&self) -> GraphStoreStats;
72    fn observability(&self) -> GraphStoreObservability;
73    fn root_id(&self) -> BlockId;
74    fn node_ids(&self) -> Vec<BlockId>;
75    fn node(&self, block_id: BlockId) -> Option<GraphNodeRecord>;
76    fn children(&self, block_id: BlockId) -> Vec<BlockId>;
77    fn parent(&self, block_id: BlockId) -> Option<BlockId>;
78    fn outgoing_edges(&self, block_id: BlockId) -> Vec<GraphEdgeSummary>;
79    fn incoming_edges(&self, block_id: BlockId) -> Vec<GraphEdgeSummary>;
80    fn resolve_selector(&self, selector: &str) -> Option<BlockId>;
81    fn to_portable_document(&self) -> Result<PortableDocument, GraphStoreError>;
82}