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).ok();
185 assert!(Cap::get("test_cap").unwrap().is_some());
186 assert!(Cap::len().unwrap() >= 1);
187 }
188
189 #[tokio::test]
190 async fn test_call_through_facade() {
191 Cap::init().ok();
192 let cap = Arc::new(TestCapability) as Arc<dyn Capability>;
193 Cap::register(cap).ok();
194 let result = Cap::call("test_cap", json!({"hello": "world"})).await;
195 assert!(result.is_ok());
196 }
197
198 #[test]
199 fn test_is_initialized_after_init() {
200 Cap::init().ok();
201 assert!(Cap::is_initialized());
202 }
203
204 #[test]
205 fn test_unregister_returns_removed_cap() {
206 Cap::init().ok();
207 let cap = Arc::new(TestCapability) as Arc<dyn Capability>;
208 Cap::register(cap).ok();
209 let removed = Cap::unregister("test_cap").unwrap();
210 assert!(removed.is_some());
211 assert_eq!(removed.unwrap().name(), "test_cap");
212 assert!(Cap::get("test_cap").unwrap().is_none());
213 let again = Cap::unregister("test_cap").unwrap();
214 assert!(again.is_none());
215 }
216
217 #[test]
218 fn test_find_by_tags_returns_matching_names() {
219 Cap::init().ok();
220 let cap = Arc::new(PluginCapability) as Arc<dyn Capability>;
221 Cap::register(cap).ok();
222 let results = Cap::find_by_tags(&["plugin"], Some(CapabilitySource::Plugin)).unwrap();
223 let names: Vec<&str> = results.iter().map(|c| c.name()).collect();
224 assert!(names.contains(&"plugin_cap"));
225 let none = Cap::find_by_tags(&["zzz_no_such_tag_zzz"], None).unwrap();
226 assert!(none.is_empty());
227 }
228
229 #[test]
230 fn test_search_returns_matching_names() {
231 Cap::init().ok();
232 let cap = Arc::new(PluginCapability) as Arc<dyn Capability>;
233 Cap::register(cap).ok();
234 let results = Cap::search("plugin_cap").unwrap();
235 let names: Vec<&str> = results.iter().map(|c| c.name()).collect();
236 assert!(names.contains(&"plugin_cap"));
237 let empty = Cap::search("zzz_no_match_zzz").unwrap();
238 assert!(empty.is_empty());
239 }
240
241 #[test]
242 fn test_list_all_contains_registered_cap() {
243 Cap::init().ok();
244 let cap = Arc::new(PluginCapability) as Arc<dyn Capability>;
245 Cap::register(cap).ok();
246 let all = Cap::list_all().unwrap();
247 let names: Vec<&str> = all.iter().map(|c| c.name()).collect();
248 assert!(names.contains(&"plugin_cap"));
249 }
250
251 #[test]
252 fn test_list_by_source_filters_correctly() {
253 Cap::init().ok();
254 let cap = Arc::new(PluginCapability) as Arc<dyn Capability>;
255 Cap::register(cap).ok();
256 let plugins = Cap::list_by_source(CapabilitySource::Plugin).unwrap();
257 let names: Vec<&str> = plugins.iter().map(|c| c.name()).collect();
258 assert!(names.contains(&"plugin_cap"));
259 let services = Cap::list_by_source(CapabilitySource::Service).unwrap();
260 let service_names: Vec<&str> = services.iter().map(|c| c.name()).collect();
261 assert!(!service_names.contains(&"plugin_cap"));
262 }
263
264 #[test]
265 fn test_is_empty_and_len_consistent() {
266 Cap::init().ok();
267 let cap = Arc::new(PluginCapability) as Arc<dyn Capability>;
268 Cap::register(cap).ok();
269 let len = Cap::len().unwrap();
270 let is_empty = Cap::is_empty().unwrap();
271 assert!(len >= 1);
272 assert!(!is_empty);
273 assert_eq!(is_empty, len == 0);
274 }
275
276 #[test]
277 fn test_metrics_reports_registered_count() {
278 Cap::init().ok();
279 let cap = Arc::new(PluginCapability) as Arc<dyn Capability>;
280 Cap::register(cap).ok();
281 let metrics = Cap::metrics().unwrap();
282 assert!(metrics.total >= 1);
283 }
284
285 #[test]
286 fn test_set_permission_checker_succeeds() {
287 Cap::init().ok();
288 let checker = Arc::new(crate::permission::AllowAll) as Arc<dyn PermissionChecker>;
289 assert!(Cap::set_permission_checker(checker).is_ok());
290 }
291
292 #[tokio::test]
293 async fn test_call_with_tenant_returns_expected_value() {
294 Cap::init().ok();
295 let cap = Arc::new(PluginCapability) as Arc<dyn Capability>;
296 Cap::register(cap).ok();
297 let checker = Arc::new(crate::permission::AllowAll) as Arc<dyn PermissionChecker>;
298 Cap::set_permission_checker(checker).ok();
299 let result = Cap::call_with_tenant("plugin_cap", json!({"x": 1}), 42).await;
300 assert!(result.is_ok());
301 assert_eq!(result.unwrap(), json!({"x": 1}));
302 }
303
304 #[test]
305 fn test_init_with_already_initialized_fails() {
306 Cap::init().ok();
307 let registry = Arc::new(CapabilityRegistry::new());
308 let result = Cap::init_with(registry);
309 assert!(result.is_err());
310 }
311
312 #[test]
313 fn test_get_nonexistent_returns_none() {
314 Cap::init().ok();
315 assert!(Cap::get("zzz_nonexistent_zzz").unwrap().is_none());
316 }
317}