Skip to main content

hawdb_core/
capability.rs

1// Copyright 2026 Nowledge
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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}