sz_rust_capability/
facade.rs1use std::sync::{Arc, OnceLock};
5
6use crate::capability::Capability;
7use crate::error::{CapError, CapResult};
8use crate::metrics::CapMetrics;
9use crate::permission::PermissionChecker;
10use crate::registry::CapabilityRegistry;
11use crate::source::CapabilitySource;
12
13struct CapInstance {
14 registry: Arc<CapabilityRegistry>,
15}
16
17static GLOBAL: OnceLock<CapInstance> = OnceLock::new();
18
19pub struct Cap;
32
33impl Cap {
34 pub fn init_with(registry: Arc<CapabilityRegistry>) -> CapResult<()> {
41 GLOBAL
42 .set(CapInstance { registry })
43 .map_err(|_| CapError::NotInitialized)
44 }
45
46 pub fn init() -> CapResult<()> {
47 Self::init_with(Arc::new(CapabilityRegistry::new()))
48 }
49
50 pub fn is_initialized() -> bool {
51 GLOBAL.get().is_some()
52 }
53
54 fn instance() -> CapResult<&'static CapInstance> {
55 GLOBAL.get().ok_or(CapError::NotInitialized)
56 }
57
58 pub fn register(cap: Arc<dyn Capability>) -> CapResult<Option<Arc<dyn Capability>>> {
59 Ok(Self::instance()?.registry.register(cap))
60 }
61
62 pub fn unregister(name: &str) -> CapResult<Option<Arc<dyn Capability>>> {
63 Ok(Self::instance()?.registry.unregister(name))
64 }
65
66 pub fn get(name: &str) -> CapResult<Option<Arc<dyn Capability>>> {
67 Ok(Self::instance()?.registry.get(name))
68 }
69
70 pub fn find_by_tags(
71 tags: &[&str],
72 source: Option<CapabilitySource>,
73 ) -> CapResult<Vec<Arc<dyn Capability>>> {
74 Ok(Self::instance()?.registry.find_by_tags(tags, source))
75 }
76
77 pub fn search(query: &str) -> CapResult<Vec<Arc<dyn Capability>>> {
78 Ok(Self::instance()?.registry.search(query))
79 }
80
81 pub fn list_all() -> CapResult<Vec<Arc<dyn Capability>>> {
82 Ok(Self::instance()?.registry.list_all())
83 }
84
85 pub fn list_by_source(source: CapabilitySource) -> CapResult<Vec<Arc<dyn Capability>>> {
86 Ok(Self::instance()?.registry.list_by_source(source))
87 }
88
89 pub async fn call(name: &str, args: serde_json::Value) -> CapResult<serde_json::Value> {
90 Self::instance()?.registry.call(name, args).await
91 }
92
93 pub async fn call_with_tenant(
95 name: &str,
96 args: serde_json::Value,
97 tenant_id: i64,
98 ) -> CapResult<serde_json::Value> {
99 Self::instance()?
100 .registry
101 .call_with_tenant(name, args, tenant_id)
102 .await
103 }
104
105 pub fn set_permission_checker(checker: Arc<dyn PermissionChecker>) -> CapResult<()> {
107 Self::instance()?.registry.set_permission_checker(checker);
108 Ok(())
109 }
110
111 pub fn metrics() -> CapResult<CapMetrics> {
112 Ok(Self::instance()?.registry.metrics())
113 }
114
115 pub fn len() -> CapResult<usize> {
116 Ok(Self::instance()?.registry.len())
117 }
118
119 pub fn is_empty() -> CapResult<bool> {
120 Ok(Self::instance()?.registry.is_empty())
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127 use async_trait::async_trait;
128 use serde_json::json;
129
130 struct TestCapability;
131
132 #[async_trait]
133 impl Capability for TestCapability {
134 fn name(&self) -> &'static str {
135 "test_cap"
136 }
137 fn description(&self) -> &'static str {
138 "测试能力"
139 }
140 fn schema(&self) -> serde_json::Value {
141 json!({})
142 }
143 fn tags(&self) -> &[&'static str] {
144 &["test"]
145 }
146 fn source(&self) -> CapabilitySource {
147 CapabilitySource::Skill
148 }
149 async fn call(&self, args: serde_json::Value) -> CapResult<serde_json::Value> {
150 Ok(args)
151 }
152 }
153
154 struct PluginCapability;
155
156 #[async_trait]
157 impl Capability for PluginCapability {
158 fn name(&self) -> &'static str {
159 "plugin_cap"
160 }
161 fn description(&self) -> &'static str {
162 "插件能力"
163 }
164 fn schema(&self) -> serde_json::Value {
165 json!({})
166 }
167 fn tags(&self) -> &[&'static str] {
168 &["plugin", "data"]
169 }
170 fn source(&self) -> CapabilitySource {
171 CapabilitySource::Plugin
172 }
173 async fn call(&self, args: serde_json::Value) -> CapResult<serde_json::Value> {
174 Ok(args)
175 }
176 }
177
178 #[test]
179 fn test_facade_lifecycle() {
180 Cap::init().ok();
181 let cap = Arc::new(TestCapability) as Arc<dyn Capability>;
182 Cap::register(cap).unwrap();
183 assert!(Cap::get("test_cap").unwrap().is_some());
184 assert!(Cap::len().unwrap() >= 1);
185 }
186
187 #[tokio::test]
188 async fn test_call_through_facade() {
189 Cap::init().ok();
190 let cap = Arc::new(TestCapability) as Arc<dyn Capability>;
191 Cap::register(cap).ok();
192 let result = Cap::call("test_cap", json!({"hello": "world"})).await;
193 assert!(result.is_ok());
194 }
195
196 #[test]
197 fn test_is_initialized_after_init() {
198 Cap::init().ok();
199 assert!(Cap::is_initialized());
200 }
201
202 #[test]
203 fn test_unregister_returns_removed_cap() {
204 Cap::init().ok();
205 let cap = Arc::new(TestCapability) as Arc<dyn Capability>;
206 Cap::register(cap).ok();
207 let removed = Cap::unregister("test_cap").unwrap();
208 assert!(removed.is_some());
209 assert_eq!(removed.unwrap().name(), "test_cap");
210 assert!(Cap::get("test_cap").unwrap().is_none());
211 let again = Cap::unregister("test_cap").unwrap();
212 assert!(again.is_none());
213 }
214
215 #[test]
216 fn test_find_by_tags_returns_matching_names() {
217 Cap::init().ok();
218 let cap = Arc::new(PluginCapability) as Arc<dyn Capability>;
219 Cap::register(cap).ok();
220 let results = Cap::find_by_tags(&["plugin"], Some(CapabilitySource::Plugin)).unwrap();
221 let names: Vec<&str> = results.iter().map(|c| c.name()).collect();
222 assert!(names.contains(&"plugin_cap"));
223 let none = Cap::find_by_tags(&["zzz_no_such_tag_zzz"], None).unwrap();
224 assert!(none.is_empty());
225 }
226
227 #[test]
228 fn test_search_returns_matching_names() {
229 Cap::init().ok();
230 let cap = Arc::new(PluginCapability) as Arc<dyn Capability>;
231 Cap::register(cap).ok();
232 let results = Cap::search("plugin_cap").unwrap();
233 let names: Vec<&str> = results.iter().map(|c| c.name()).collect();
234 assert!(names.contains(&"plugin_cap"));
235 let empty = Cap::search("zzz_no_match_zzz").unwrap();
236 assert!(empty.is_empty());
237 }
238
239 #[test]
240 fn test_list_all_contains_registered_cap() {
241 Cap::init().ok();
242 let cap = Arc::new(PluginCapability) as Arc<dyn Capability>;
243 Cap::register(cap).ok();
244 let all = Cap::list_all().unwrap();
245 let names: Vec<&str> = all.iter().map(|c| c.name()).collect();
246 assert!(names.contains(&"plugin_cap"));
247 }
248
249 #[test]
250 fn test_list_by_source_filters_correctly() {
251 Cap::init().ok();
252 let cap = Arc::new(PluginCapability) as Arc<dyn Capability>;
253 Cap::register(cap).ok();
254 let plugins = Cap::list_by_source(CapabilitySource::Plugin).unwrap();
255 let names: Vec<&str> = plugins.iter().map(|c| c.name()).collect();
256 assert!(names.contains(&"plugin_cap"));
257 let services = Cap::list_by_source(CapabilitySource::Service).unwrap();
258 let service_names: Vec<&str> = services.iter().map(|c| c.name()).collect();
259 assert!(!service_names.contains(&"plugin_cap"));
260 }
261
262 #[test]
263 fn test_is_empty_and_len_consistent() {
264 Cap::init().ok();
265 let cap = Arc::new(PluginCapability) as Arc<dyn Capability>;
266 Cap::register(cap).ok();
267 let len = Cap::len().unwrap();
268 let is_empty = Cap::is_empty().unwrap();
269 assert!(len >= 1);
270 assert!(!is_empty);
271 assert_eq!(is_empty, len == 0);
272 }
273
274 #[test]
275 fn test_metrics_reports_registered_count() {
276 Cap::init().ok();
277 let cap = Arc::new(PluginCapability) as Arc<dyn Capability>;
278 Cap::register(cap).ok();
279 let metrics = Cap::metrics().unwrap();
280 assert!(metrics.total >= 1);
281 }
282
283 #[test]
284 fn test_set_permission_checker_succeeds() {
285 Cap::init().ok();
286 let checker = Arc::new(crate::permission::AllowAll) as Arc<dyn PermissionChecker>;
287 assert!(Cap::set_permission_checker(checker).is_ok());
288 }
289
290 #[tokio::test]
291 async fn test_call_with_tenant_returns_expected_value() {
292 Cap::init().ok();
293 let cap = Arc::new(PluginCapability) as Arc<dyn Capability>;
294 Cap::register(cap).ok();
295 let checker = Arc::new(crate::permission::AllowAll) as Arc<dyn PermissionChecker>;
296 Cap::set_permission_checker(checker).ok();
297 let result = Cap::call_with_tenant("plugin_cap", json!({"x": 1}), 42).await;
298 assert!(result.is_ok());
299 assert_eq!(result.unwrap(), json!({"x": 1}));
300 }
301
302 #[test]
303 fn test_init_with_already_initialized_fails() {
304 Cap::init().ok();
305 let registry = Arc::new(CapabilityRegistry::new());
306 let result = Cap::init_with(registry);
307 assert!(result.is_err());
308 }
309
310 #[test]
311 fn test_get_nonexistent_returns_none() {
312 Cap::init().ok();
313 assert!(Cap::get("zzz_nonexistent_zzz").unwrap().is_none());
314 }
315}