Skip to main content

nodedb_client/
capabilities.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! `Capabilities` newtype for typed capability bit-test accessors.
4//!
5//! Wraps the raw `u64` bitfield advertised by the server in `HelloAckFrame`
6//! and exposes named predicates so SDK consumers can ask
7//! "does this server support GraphRAG fusion?" without knowing which bit to mask.
8
9use nodedb_types::protocol::{
10    CAP_COLUMNAR, CAP_CRDT, CAP_FTS, CAP_GRAPHRAG, CAP_SPATIAL, CAP_STREAMING, CAP_TIMESERIES,
11};
12
13/// Typed wrapper around the raw capability bitfield from `HelloAckFrame`.
14///
15/// Obtained via `NodeDb::capabilities()`. Use the predicate methods to
16/// test for specific server features before calling the corresponding
17/// operations.
18///
19/// # Example
20/// ```no_run
21/// # use nodedb_client::native::capabilities::Capabilities;
22/// let caps = Capabilities::from_raw(0x07); // streaming + graphrag + fts
23/// assert!(caps.supports_streaming());
24/// assert!(caps.supports_graphrag());
25/// assert!(!caps.supports_crdt());
26/// ```
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
28pub struct Capabilities(pub u64);
29
30impl Capabilities {
31    /// Create from a raw capability bitfield.
32    pub fn from_raw(bits: u64) -> Self {
33        Self(bits)
34    }
35
36    /// Returns the underlying raw bitfield.
37    pub fn raw(self) -> u64 {
38        self.0
39    }
40
41    /// Whether the server supports response streaming
42    /// (partial-response chunking via `ResponseStatus::Partial`).
43    pub fn supports_streaming(self) -> bool {
44        self.0 & CAP_STREAMING != 0
45    }
46
47    /// Whether the server supports the `GraphRagFusion` opcode.
48    pub fn supports_graphrag(self) -> bool {
49        self.0 & CAP_GRAPHRAG != 0
50    }
51
52    /// Whether the server supports full-text search opcodes
53    /// (`TextSearch`, `HybridSearch`).
54    pub fn supports_fts(self) -> bool {
55        self.0 & CAP_FTS != 0
56    }
57
58    /// Whether the server supports CRDT operations (`CrdtRead`, `CrdtApply`).
59    pub fn supports_crdt(self) -> bool {
60        self.0 & CAP_CRDT != 0
61    }
62
63    /// Whether the server supports spatial scan (`SpatialScan`).
64    pub fn supports_spatial(self) -> bool {
65        self.0 & CAP_SPATIAL != 0
66    }
67
68    /// Whether the server supports timeseries operations
69    /// (`TimeseriesScan`, `TimeseriesIngest`).
70    pub fn supports_timeseries(self) -> bool {
71        self.0 & CAP_TIMESERIES != 0
72    }
73
74    /// Whether the server supports columnar operations
75    /// (`ColumnarScan`, `ColumnarInsert`).
76    pub fn supports_columnar(self) -> bool {
77        self.0 & CAP_COLUMNAR != 0
78    }
79
80    /// Test an arbitrary capability bit.
81    pub fn has(self, bit: u64) -> bool {
82        self.0 & bit != 0
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89    use nodedb_types::protocol::{CAP_FTS, CAP_STREAMING};
90
91    #[test]
92    fn all_bits_set() {
93        let all = CAP_STREAMING
94            | CAP_GRAPHRAG
95            | CAP_FTS
96            | CAP_CRDT
97            | CAP_SPATIAL
98            | CAP_TIMESERIES
99            | CAP_COLUMNAR;
100        let caps = Capabilities::from_raw(all);
101        assert!(caps.supports_streaming());
102        assert!(caps.supports_graphrag());
103        assert!(caps.supports_fts());
104        assert!(caps.supports_crdt());
105        assert!(caps.supports_spatial());
106        assert!(caps.supports_timeseries());
107        assert!(caps.supports_columnar());
108    }
109
110    #[test]
111    fn no_bits_set() {
112        let caps = Capabilities::from_raw(0);
113        assert!(!caps.supports_streaming());
114        assert!(!caps.supports_graphrag());
115        assert!(!caps.supports_fts());
116        assert!(!caps.supports_crdt());
117        assert!(!caps.supports_spatial());
118        assert!(!caps.supports_timeseries());
119        assert!(!caps.supports_columnar());
120    }
121
122    #[test]
123    fn selective_bits() {
124        let caps = Capabilities::from_raw(CAP_STREAMING | CAP_CRDT);
125        assert!(caps.supports_streaming());
126        assert!(caps.supports_crdt());
127        assert!(!caps.supports_graphrag());
128        assert!(!caps.supports_fts());
129    }
130
131    #[test]
132    fn has_arbitrary_bit() {
133        let caps = Capabilities::from_raw(1 << 10);
134        assert!(caps.has(1 << 10));
135        assert!(!caps.has(1 << 11));
136    }
137
138    #[test]
139    fn raw_roundtrip() {
140        let bits = CAP_GRAPHRAG | CAP_FTS;
141        let caps = Capabilities::from_raw(bits);
142        assert_eq!(caps.raw(), bits);
143    }
144
145    #[test]
146    fn default_is_zero() {
147        let caps = Capabilities::default();
148        assert_eq!(caps.raw(), 0);
149        assert!(!caps.supports_streaming());
150    }
151}