Skip to main content

ruvix_types/
graph.rs

1//! Graph store types for kernel-resident graph operations.
2//!
3//! The kernel maintains graph stores as first-class objects. All mutations
4//! are proof-gated via the `graph_apply_proved` syscall.
5
6use crate::handle::Handle;
7
8/// Handle to a kernel-resident graph store.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10#[repr(transparent)]
11pub struct GraphHandle(pub Handle);
12
13impl GraphHandle {
14    /// Creates a new graph handle.
15    #[inline]
16    #[must_use]
17    pub const fn new(id: u32, generation: u32) -> Self {
18        Self(Handle::new(id, generation))
19    }
20
21    /// Creates a null (invalid) graph handle.
22    #[inline]
23    #[must_use]
24    pub const fn null() -> Self {
25        Self(Handle::null())
26    }
27
28    /// Checks if this handle is null.
29    #[inline]
30    #[must_use]
31    pub const fn is_null(&self) -> bool {
32        self.0.is_null()
33    }
34
35    /// Returns the raw handle.
36    #[inline]
37    #[must_use]
38    pub const fn raw(&self) -> Handle {
39        self.0
40    }
41}
42
43impl Default for GraphHandle {
44    fn default() -> Self {
45        Self::null()
46    }
47}
48
49/// Kind of graph mutation operation.
50#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
51#[repr(u8)]
52pub enum GraphMutationKind {
53    /// Add a new node to the graph.
54    AddNode = 0,
55
56    /// Remove a node and all its edges.
57    RemoveNode = 1,
58
59    /// Add a new edge between two nodes.
60    AddEdge = 2,
61
62    /// Remove an edge between two nodes.
63    RemoveEdge = 3,
64
65    /// Update the weight of an existing edge.
66    UpdateEdgeWeight = 4,
67
68    /// Update node metadata.
69    UpdateNodeMeta = 5,
70}
71
72impl GraphMutationKind {
73    /// Returns the mutation kind as a string.
74    #[inline]
75    #[must_use]
76    pub const fn as_str(&self) -> &'static str {
77        match self {
78            Self::AddNode => "AddNode",
79            Self::RemoveNode => "RemoveNode",
80            Self::AddEdge => "AddEdge",
81            Self::RemoveEdge => "RemoveEdge",
82            Self::UpdateEdgeWeight => "UpdateEdgeWeight",
83            Self::UpdateNodeMeta => "UpdateNodeMeta",
84        }
85    }
86
87    /// Converts from a raw u8 value.
88    #[inline]
89    #[must_use]
90    pub const fn from_u8(value: u8) -> Option<Self> {
91        match value {
92            0 => Some(Self::AddNode),
93            1 => Some(Self::RemoveNode),
94            2 => Some(Self::AddEdge),
95            3 => Some(Self::RemoveEdge),
96            4 => Some(Self::UpdateEdgeWeight),
97            5 => Some(Self::UpdateNodeMeta),
98            _ => None,
99        }
100    }
101}
102
103/// A graph mutation request.
104///
105/// Graph mutations are passed to `graph_apply_proved` and require
106/// a valid proof token to execute.
107#[derive(Debug, Clone, Copy, PartialEq)]
108#[repr(C)]
109pub struct GraphMutation {
110    /// The kind of mutation.
111    pub kind: GraphMutationKind,
112
113    /// Source node ID (for edge operations) or target node ID.
114    pub node_a: u64,
115
116    /// Destination node ID (for edge operations).
117    pub node_b: u64,
118
119    /// Edge weight (for AddEdge and UpdateEdgeWeight).
120    /// Represented as fixed-point: weight * 10000.
121    pub weight_fp: i32,
122
123    /// Partition ID hint (for coherence routing).
124    pub partition_hint: u32,
125}
126
127impl GraphMutation {
128    /// Creates an AddNode mutation.
129    #[inline]
130    #[must_use]
131    pub const fn add_node(node_id: u64) -> Self {
132        Self {
133            kind: GraphMutationKind::AddNode,
134            node_a: node_id,
135            node_b: 0,
136            weight_fp: 0,
137            partition_hint: 0,
138        }
139    }
140
141    /// Creates a RemoveNode mutation.
142    #[inline]
143    #[must_use]
144    pub const fn remove_node(node_id: u64) -> Self {
145        Self {
146            kind: GraphMutationKind::RemoveNode,
147            node_a: node_id,
148            node_b: 0,
149            weight_fp: 0,
150            partition_hint: 0,
151        }
152    }
153
154    /// Creates an AddEdge mutation.
155    #[inline]
156    #[must_use]
157    pub const fn add_edge(from: u64, to: u64, weight: f32) -> Self {
158        Self {
159            kind: GraphMutationKind::AddEdge,
160            node_a: from,
161            node_b: to,
162            weight_fp: (weight * 10000.0) as i32,
163            partition_hint: 0,
164        }
165    }
166
167    /// Creates a RemoveEdge mutation.
168    #[inline]
169    #[must_use]
170    pub const fn remove_edge(from: u64, to: u64) -> Self {
171        Self {
172            kind: GraphMutationKind::RemoveEdge,
173            node_a: from,
174            node_b: to,
175            weight_fp: 0,
176            partition_hint: 0,
177        }
178    }
179
180    /// Creates an UpdateEdgeWeight mutation.
181    #[inline]
182    #[must_use]
183    pub const fn update_edge_weight(from: u64, to: u64, weight: f32) -> Self {
184        Self {
185            kind: GraphMutationKind::UpdateEdgeWeight,
186            node_a: from,
187            node_b: to,
188            weight_fp: (weight * 10000.0) as i32,
189            partition_hint: 0,
190        }
191    }
192
193    /// Returns the weight as a float.
194    #[inline]
195    #[must_use]
196    pub fn weight(&self) -> f32 {
197        self.weight_fp as f32 / 10000.0
198    }
199
200    /// Sets a partition hint for coherence-aware routing.
201    #[inline]
202    #[must_use]
203    pub const fn with_partition_hint(mut self, partition_id: u32) -> Self {
204        self.partition_hint = partition_id;
205        self
206    }
207}
208
209impl Default for GraphMutation {
210    fn default() -> Self {
211        Self {
212            kind: GraphMutationKind::AddNode,
213            node_a: 0,
214            node_b: 0,
215            weight_fp: 0,
216            partition_hint: 0,
217        }
218    }
219}
220
221#[cfg(test)]
222mod tests {
223    use super::*;
224
225    #[test]
226    fn test_graph_handle() {
227        let h = GraphHandle::new(42, 7);
228        assert!(!h.is_null());
229        assert_eq!(h.raw().id, 42);
230    }
231
232    #[test]
233    fn test_graph_mutation_add_edge() {
234        let mutation = GraphMutation::add_edge(1, 2, 0.75);
235        assert_eq!(mutation.kind, GraphMutationKind::AddEdge);
236        assert_eq!(mutation.node_a, 1);
237        assert_eq!(mutation.node_b, 2);
238        assert!((mutation.weight() - 0.75).abs() < 0.001);
239    }
240
241    #[test]
242    fn test_graph_mutation_with_partition() {
243        let mutation = GraphMutation::add_node(100).with_partition_hint(5);
244        assert_eq!(mutation.partition_hint, 5);
245    }
246}