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}