1use crate::{HawDBError, Result};
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
18pub enum RuntimeCapability {
19 AccessControl,
20 FullTextSearch,
21 VectorSearch,
22 GraphAnalytics,
23 BackgroundMaintenance,
24}
25
26impl RuntimeCapability {
27 pub const fn as_str(self) -> &'static str {
28 match self {
29 Self::AccessControl => "access_control",
30 Self::FullTextSearch => "full_text_search",
31 Self::VectorSearch => "vector_search",
32 Self::GraphAnalytics => "graph_analytics",
33 Self::BackgroundMaintenance => "background_maintenance",
34 }
35 }
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub struct RuntimeCapabilities {
40 pub access_control: bool,
41 pub full_text_search: bool,
42 pub vector_search: bool,
43 pub graph_analytics: bool,
44 pub background_maintenance: bool,
45}
46
47impl RuntimeCapabilities {
48 pub const fn shared_host() -> Self {
49 Self {
50 access_control: false,
51 full_text_search: true,
52 vector_search: true,
53 graph_analytics: true,
54 background_maintenance: true,
55 }
56 }
57
58 pub const fn mobile_embedded() -> Self {
59 Self {
60 access_control: false,
61 full_text_search: true,
62 vector_search: true,
63 graph_analytics: false,
64 background_maintenance: false,
65 }
66 }
67
68 pub const fn is_enabled(self, capability: RuntimeCapability) -> bool {
69 match capability {
70 RuntimeCapability::AccessControl => self.access_control,
71 RuntimeCapability::FullTextSearch => self.full_text_search,
72 RuntimeCapability::VectorSearch => self.vector_search,
73 RuntimeCapability::GraphAnalytics => self.graph_analytics,
74 RuntimeCapability::BackgroundMaintenance => self.background_maintenance,
75 }
76 }
77
78 pub fn require(self, capability: RuntimeCapability) -> Result<()> {
79 if self.is_enabled(capability) {
80 return Ok(());
81 }
82 Err(HawDBError::CapabilityUnavailable { capability })
83 }
84
85 pub const fn with(mut self, capability: RuntimeCapability, enabled: bool) -> Self {
86 match capability {
87 RuntimeCapability::AccessControl => self.access_control = enabled,
88 RuntimeCapability::FullTextSearch => self.full_text_search = enabled,
89 RuntimeCapability::VectorSearch => self.vector_search = enabled,
90 RuntimeCapability::GraphAnalytics => self.graph_analytics = enabled,
91 RuntimeCapability::BackgroundMaintenance => self.background_maintenance = enabled,
92 }
93 self
94 }
95
96 pub const fn intersection(self, available: Self) -> Self {
97 Self {
98 access_control: self.access_control && available.access_control,
99 full_text_search: self.full_text_search && available.full_text_search,
100 vector_search: self.vector_search && available.vector_search,
101 graph_analytics: self.graph_analytics && available.graph_analytics,
102 background_maintenance: self.background_maintenance && available.background_maintenance,
103 }
104 }
105}
106
107impl Default for RuntimeCapabilities {
108 fn default() -> Self {
109 Self::shared_host()
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn mobile_disables_only_optional_heavy_runtime_capabilities() {
119 let capabilities = RuntimeCapabilities::mobile_embedded();
120
121 assert!(!capabilities.is_enabled(RuntimeCapability::AccessControl));
122 assert!(capabilities.is_enabled(RuntimeCapability::FullTextSearch));
123 assert!(capabilities.is_enabled(RuntimeCapability::VectorSearch));
124 assert!(!capabilities.is_enabled(RuntimeCapability::GraphAnalytics));
125 assert!(!capabilities.is_enabled(RuntimeCapability::BackgroundMaintenance));
126 }
127
128 #[test]
129 fn disabled_capability_returns_typed_error() {
130 let error = RuntimeCapabilities::mobile_embedded()
131 .require(RuntimeCapability::GraphAnalytics)
132 .unwrap_err();
133
134 assert_eq!(
135 error,
136 HawDBError::CapabilityUnavailable {
137 capability: RuntimeCapability::GraphAnalytics
138 }
139 );
140 assert_eq!(error.to_string(), "capability unavailable: graph_analytics");
141 }
142
143 #[test]
144 fn requested_capabilities_are_bounded_by_compiled_availability() {
145 let available = RuntimeCapabilities::shared_host()
146 .with(RuntimeCapability::AccessControl, true)
147 .with(RuntimeCapability::GraphAnalytics, false)
148 .with(RuntimeCapability::BackgroundMaintenance, false);
149 let effective = RuntimeCapabilities::shared_host().intersection(available);
150
151 assert!(!effective.access_control);
152 assert!(effective.full_text_search);
153 assert!(effective.vector_search);
154 assert!(!effective.graph_analytics);
155 assert!(!effective.background_maintenance);
156 }
157}