1pub mod compatibility;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum StabilityLevel {
11 Stable,
13 Beta,
15 Experimental,
17 Deprecated {
19 since: &'static str,
21 replacement: Option<&'static str>,
23 },
24}
25
26impl StabilityLevel {
27 pub fn is_production_ready(&self) -> bool {
29 matches!(
30 self,
31 StabilityLevel::Stable | StabilityLevel::Beta | StabilityLevel::Deprecated { .. }
32 )
33 }
34
35 pub fn label(&self) -> &'static str {
37 match self {
38 StabilityLevel::Stable => "Stable",
39 StabilityLevel::Beta => "Beta",
40 StabilityLevel::Experimental => "Experimental",
41 StabilityLevel::Deprecated { .. } => "Deprecated",
42 }
43 }
44
45 pub fn indicator(&self) -> &'static str {
47 match self {
48 StabilityLevel::Stable => "[STABLE]",
49 StabilityLevel::Beta => "[BETA]",
50 StabilityLevel::Experimental => "[EXPERIMENTAL]",
51 StabilityLevel::Deprecated { .. } => "[DEPRECATED]",
52 }
53 }
54}
55
56#[derive(Debug, Clone, PartialEq, Eq, Hash)]
58pub enum ApiCategory {
59 QueryLanguage,
60 DataModel,
61 Reasoning,
62 GraphQuery,
63 Geospatial,
64 Validation,
65 Storage,
66 Streaming,
67 Industrial,
68 ArtificialIntelligence,
69 Security,
70 RdfStar,
71 Federation,
72 Server,
73 WebAssembly,
74 TimeSeries,
75 PhysicsSimulation,
76 Quantum,
77 Tooling,
78}
79
80impl ApiCategory {
81 pub fn label(&self) -> &'static str {
83 match self {
84 ApiCategory::QueryLanguage => "Query Language",
85 ApiCategory::DataModel => "Data Model",
86 ApiCategory::Reasoning => "Reasoning",
87 ApiCategory::GraphQuery => "Graph Query",
88 ApiCategory::Geospatial => "Geospatial",
89 ApiCategory::Validation => "Validation",
90 ApiCategory::Storage => "Storage",
91 ApiCategory::Streaming => "Streaming",
92 ApiCategory::Industrial => "Industrial",
93 ApiCategory::ArtificialIntelligence => "Artificial Intelligence",
94 ApiCategory::Security => "Security",
95 ApiCategory::RdfStar => "RDF-star",
96 ApiCategory::Federation => "Federation",
97 ApiCategory::Server => "Server",
98 ApiCategory::WebAssembly => "WebAssembly",
99 ApiCategory::TimeSeries => "Time-Series",
100 ApiCategory::PhysicsSimulation => "Physics Simulation",
101 ApiCategory::Quantum => "Quantum",
102 ApiCategory::Tooling => "Tooling",
103 }
104 }
105}
106
107#[derive(Debug, Clone)]
109pub struct ApiStabilityMarker {
110 pub feature: &'static str,
112 pub category: ApiCategory,
114 pub level: StabilityLevel,
116 pub since: &'static str,
118 pub description: &'static str,
120 pub spec_url: Option<&'static str>,
122}
123
124impl ApiStabilityMarker {
125 pub fn stable(
127 feature: &'static str,
128 category: ApiCategory,
129 since: &'static str,
130 description: &'static str,
131 ) -> Self {
132 Self {
133 feature,
134 category,
135 level: StabilityLevel::Stable,
136 since,
137 description,
138 spec_url: None,
139 }
140 }
141
142 pub fn beta(
144 feature: &'static str,
145 category: ApiCategory,
146 since: &'static str,
147 description: &'static str,
148 ) -> Self {
149 Self {
150 feature,
151 category,
152 level: StabilityLevel::Beta,
153 since,
154 description,
155 spec_url: None,
156 }
157 }
158
159 pub fn experimental(
161 feature: &'static str,
162 category: ApiCategory,
163 since: &'static str,
164 description: &'static str,
165 ) -> Self {
166 Self {
167 feature,
168 category,
169 level: StabilityLevel::Experimental,
170 since,
171 description,
172 spec_url: None,
173 }
174 }
175
176 pub fn deprecated(
178 feature: &'static str,
179 category: ApiCategory,
180 since: &'static str,
181 deprecated_since: &'static str,
182 replacement: Option<&'static str>,
183 description: &'static str,
184 ) -> Self {
185 Self {
186 feature,
187 category,
188 level: StabilityLevel::Deprecated {
189 since: deprecated_since,
190 replacement,
191 },
192 since,
193 description,
194 spec_url: None,
195 }
196 }
197
198 pub fn with_spec(mut self, url: &'static str) -> Self {
200 self.spec_url = Some(url);
201 self
202 }
203
204 pub fn is_stable(&self) -> bool {
206 matches!(self.level, StabilityLevel::Stable)
207 }
208
209 pub fn is_beta(&self) -> bool {
211 matches!(self.level, StabilityLevel::Beta)
212 }
213
214 pub fn is_experimental(&self) -> bool {
216 matches!(self.level, StabilityLevel::Experimental)
217 }
218
219 pub fn is_deprecated(&self) -> bool {
221 matches!(self.level, StabilityLevel::Deprecated { .. })
222 }
223}
224
225#[derive(Debug, Clone, PartialEq, Eq)]
227pub struct RegistrySummary {
228 pub total: usize,
229 pub stable_count: usize,
230 pub beta_count: usize,
231 pub experimental_count: usize,
232 pub deprecated_count: usize,
233}
234
235impl RegistrySummary {
236 pub fn stable_percentage(&self) -> f64 {
238 if self.total == 0 {
239 return 0.0;
240 }
241 (self.stable_count as f64 / self.total as f64) * 100.0
242 }
243
244 pub fn production_ready_percentage(&self) -> f64 {
246 if self.total == 0 {
247 return 0.0;
248 }
249 ((self.stable_count + self.beta_count + self.deprecated_count) as f64 / self.total as f64)
250 * 100.0
251 }
252}
253
254pub struct StabilityRegistry {
256 markers: Vec<ApiStabilityMarker>,
257}
258
259impl StabilityRegistry {
260 pub fn new() -> Self {
262 Self {
263 markers: Vec::new(),
264 }
265 }
266
267 pub fn register(&mut self, marker: ApiStabilityMarker) {
269 self.markers.push(marker);
270 }
271
272 pub fn oxirs_v1_stability() -> Self {
274 let mut r = Self::new();
275
276 r.register(ApiStabilityMarker::stable("SPARQL 1.1 SELECT", ApiCategory::QueryLanguage, "0.1.0",
278 "Full SPARQL 1.1 SELECT query support including projections, DISTINCT, ORDER BY, LIMIT, OFFSET, GROUP BY, HAVING, and aggregate functions.")
279 .with_spec("https://www.w3.org/TR/sparql11-query/"));
280 r.register(
281 ApiStabilityMarker::stable(
282 "SPARQL 1.1 ASK",
283 ApiCategory::QueryLanguage,
284 "0.1.0",
285 "SPARQL 1.1 ASK queries returning boolean existence results.",
286 )
287 .with_spec("https://www.w3.org/TR/sparql11-query/"),
288 );
289 r.register(
290 ApiStabilityMarker::stable(
291 "SPARQL 1.1 CONSTRUCT",
292 ApiCategory::QueryLanguage,
293 "0.1.0",
294 "SPARQL 1.1 CONSTRUCT queries producing new RDF graphs from existing data.",
295 )
296 .with_spec("https://www.w3.org/TR/sparql11-query/"),
297 );
298 r.register(
299 ApiStabilityMarker::stable(
300 "SPARQL 1.1 DESCRIBE",
301 ApiCategory::QueryLanguage,
302 "0.1.0",
303 "SPARQL 1.1 DESCRIBE queries returning RDF descriptions of resources.",
304 )
305 .with_spec("https://www.w3.org/TR/sparql11-query/"),
306 );
307 r.register(ApiStabilityMarker::stable("SPARQL 1.1 Update", ApiCategory::QueryLanguage, "0.1.0",
308 "Full SPARQL 1.1 Update support: INSERT DATA, DELETE DATA, INSERT/DELETE WHERE, LOAD, CLEAR, CREATE, DROP.")
309 .with_spec("https://www.w3.org/TR/sparql11-update/"));
310 r.register(ApiStabilityMarker::stable("SPARQL 1.1 Property Paths", ApiCategory::QueryLanguage, "0.1.0",
311 "SPARQL 1.1 property path expressions including sequence, alternative, inverse, kleene star, and kleene plus."));
312 r.register(ApiStabilityMarker::stable(
313 "SPARQL 1.1 Aggregates",
314 ApiCategory::QueryLanguage,
315 "0.1.0",
316 "SPARQL 1.1 aggregate functions: COUNT, SUM, MIN, MAX, AVG, GROUP_CONCAT, SAMPLE.",
317 ));
318 r.register(
319 ApiStabilityMarker::stable(
320 "SPARQL 1.1 Federated Query (SERVICE)",
321 ApiCategory::QueryLanguage,
322 "0.1.0",
323 "SPARQL 1.1 SERVICE clause for federated queries across multiple SPARQL endpoints.",
324 )
325 .with_spec("https://www.w3.org/TR/sparql11-federated-query/"),
326 );
327 r.register(
328 ApiStabilityMarker::stable(
329 "SPARQL 1.2 RDF-star Expressions",
330 ApiCategory::RdfStar,
331 "0.1.0",
332 "SPARQL 1.2 triple expressions and annotation syntax for quoted triple patterns.",
333 )
334 .with_spec("https://www.w3.org/TR/sparql12-query/"),
335 );
336
337 r.register(ApiStabilityMarker::stable("RDF 1.2 Data Model", ApiCategory::DataModel, "0.1.0",
339 "Complete RDF 1.2 data model: IRIs, blank nodes, literals, language-tagged strings, datatyped literals, and quoted triples.")
340 .with_spec("https://www.w3.org/TR/rdf12-concepts/"));
341 r.register(
342 ApiStabilityMarker::stable(
343 "Turtle Serialization",
344 ApiCategory::DataModel,
345 "0.1.0",
346 "Streaming Turtle 1.1 parser and writer with full namespace prefix support.",
347 )
348 .with_spec("https://www.w3.org/TR/turtle/"),
349 );
350 r.register(
351 ApiStabilityMarker::stable(
352 "N-Triples Serialization",
353 ApiCategory::DataModel,
354 "0.1.0",
355 "N-Triples parser and writer for simple line-oriented RDF serialization.",
356 )
357 .with_spec("https://www.w3.org/TR/n-triples/"),
358 );
359 r.register(
360 ApiStabilityMarker::stable(
361 "N-Quads Serialization",
362 ApiCategory::DataModel,
363 "0.1.0",
364 "N-Quads parser and writer for quad-based RDF dataset serialization.",
365 )
366 .with_spec("https://www.w3.org/TR/n-quads/"),
367 );
368 r.register(
369 ApiStabilityMarker::stable(
370 "TriG Serialization",
371 ApiCategory::DataModel,
372 "0.1.0",
373 "TriG parser and writer for named graph serialization.",
374 )
375 .with_spec("https://www.w3.org/TR/trig/"),
376 );
377 r.register(
378 ApiStabilityMarker::stable(
379 "RDF/XML Serialization",
380 ApiCategory::DataModel,
381 "0.1.0",
382 "RDF/XML parser and writer for legacy compatibility.",
383 )
384 .with_spec("https://www.w3.org/TR/rdf-syntax-grammar/"),
385 );
386 r.register(
387 ApiStabilityMarker::stable(
388 "JSON-LD 1.1 Serialization",
389 ApiCategory::DataModel,
390 "0.1.0",
391 "JSON-LD 1.1 parser and writer with context expansion and compaction.",
392 )
393 .with_spec("https://www.w3.org/TR/json-ld11/"),
394 );
395 r.register(ApiStabilityMarker::stable(
396 "Named Graphs",
397 ApiCategory::DataModel,
398 "0.1.0",
399 "Full quad store support with named graph CRUD operations and graph management.",
400 ));
401
402 r.register(ApiStabilityMarker::stable("RDFS Inference", ApiCategory::Reasoning, "0.1.0",
404 "RDFS entailment regime including subClassOf, subPropertyOf, domain, range, and transitive closure.")
405 .with_spec("https://www.w3.org/TR/rdf-schema/"));
406 r.register(ApiStabilityMarker::stable("OWL 2 RL Reasoning", ApiCategory::Reasoning, "0.1.0",
407 "OWL 2 RL profile reasoning with forward-chaining rules for description logic reasoning over large datasets.")
408 .with_spec("https://www.w3.org/TR/owl2-profiles/#OWL_2_RL"));
409 r.register(ApiStabilityMarker::stable("OWL 2 EL Reasoning", ApiCategory::Reasoning, "0.1.0",
410 "OWL 2 EL profile reasoning optimized for large biomedical and enterprise ontologies.")
411 .with_spec("https://www.w3.org/TR/owl2-profiles/#OWL_2_EL"));
412 r.register(ApiStabilityMarker::beta("OWL 2 QL Reasoning", ApiCategory::Reasoning, "0.2.0",
413 "OWL 2 QL profile reasoning for query rewriting over relational sources. Conjunctive query rewriting is complete.")
414 .with_spec("https://www.w3.org/TR/owl2-profiles/#OWL_2_QL"));
415 r.register(
416 ApiStabilityMarker::stable(
417 "SWRL Rule Execution",
418 ApiCategory::Reasoning,
419 "0.2.0",
420 "Semantic Web Rule Language (SWRL) rule parsing and forward-chaining execution.",
421 )
422 .with_spec("https://www.w3.org/Submission/SWRL/"),
423 );
424
425 r.register(ApiStabilityMarker::stable("GraphQL API", ApiCategory::GraphQuery, "0.1.0",
427 "Full GraphQL query execution over RDF datasets with automatic schema generation from RDF shapes."));
428 r.register(ApiStabilityMarker::stable("GraphQL Federation", ApiCategory::GraphQuery, "0.2.0",
429 "GraphQL Federation v2 support for composing distributed subgraphs into a unified supergraph."));
430 r.register(ApiStabilityMarker::beta(
431 "GraphQL Subscriptions",
432 ApiCategory::GraphQuery,
433 "0.2.0",
434 "GraphQL subscriptions over WebSocket with change tracking and subscription optimizer.",
435 ));
436 r.register(ApiStabilityMarker::beta("GraphQL Mutations", ApiCategory::GraphQuery, "0.2.0",
437 "GraphQL mutations mapped to SPARQL UPDATE operations for write-through to the RDF store."));
438
439 r.register(ApiStabilityMarker::stable("GeoSPARQL 1.1", ApiCategory::Geospatial, "0.1.0",
441 "GeoSPARQL 1.1 spatial query functions, geometry relations, and WKT/GeoJSON serialization.")
442 .with_spec("https://www.ogc.org/standard/geosparql/"));
443 r.register(ApiStabilityMarker::beta(
444 "GeoSPARQL 3D Geometry",
445 ApiCategory::Geospatial,
446 "0.2.0",
447 "3D geometry support (PolyhedralSurface, MultiSolid) for volumetric spatial queries.",
448 ));
449 r.register(ApiStabilityMarker::beta("GeoSPARQL Coordinate Reference Systems", ApiCategory::Geospatial, "0.2.0",
450 "CRS transformation support for queries involving data in multiple spatial reference systems."));
451 r.register(ApiStabilityMarker::beta(
452 "GeoSPARQL R-Tree Index",
453 ApiCategory::Geospatial,
454 "0.2.0",
455 "R-tree spatial index for accelerating GeoSPARQL geometric relation queries.",
456 ));
457
458 r.register(ApiStabilityMarker::stable("SHACL Validation", ApiCategory::Validation, "0.1.0",
460 "Full SHACL 1.0 constraint validation including cardinality, value type, pattern, and SPARQL constraints.")
461 .with_spec("https://www.w3.org/TR/shacl/"));
462 r.register(ApiStabilityMarker::stable(
463 "SHACL-SPARQL Constraints",
464 ApiCategory::Validation,
465 "0.1.0",
466 "SHACL-SPARQL constraints allowing arbitrary SPARQL-based shape validation.",
467 ));
468 r.register(ApiStabilityMarker::stable("SAMM Aspect Model", ApiCategory::Validation, "0.1.0",
469 "SAMM (Semantic Aspect Meta Model) support for industrial asset modeling and validation."));
470 r.register(ApiStabilityMarker::beta("SHACL-AI Shape Learning", ApiCategory::Validation, "0.2.0",
471 "AI-powered automatic SHACL shape inference from example data using pattern mining and constraint learning."));
472
473 r.register(ApiStabilityMarker::stable("In-Memory RDF Store", ApiCategory::Storage, "0.1.0",
475 "High-performance in-memory RDF store with multi-index support (SPO/POS/OSP) and ACID transactions."));
476 r.register(ApiStabilityMarker::stable("TDB Persistent Storage", ApiCategory::Storage, "0.1.0",
477 "TDB-compatible disk-based RDF storage with B-tree indices, WAL journal, and crash recovery."));
478 r.register(ApiStabilityMarker::beta("Distributed Cluster Storage", ApiCategory::Storage, "0.2.0",
479 "Raft-based distributed storage cluster with automatic sharding, replication, and leader election."));
480 r.register(ApiStabilityMarker::beta("Full-Text Search Index", ApiCategory::Storage, "0.2.0",
481 "Tantivy-powered full-text search index for literal values with BM25 ranking and language filtering."));
482 r.register(ApiStabilityMarker::beta("Temporal RDF Storage", ApiCategory::Storage, "0.2.0",
483 "Versioned triple storage with time-travel queries, changelog, and bitemporal data support."));
484
485 r.register(ApiStabilityMarker::beta("RDF Streaming Processing", ApiCategory::Streaming, "0.1.0",
487 "Real-time RDF stream processing with windowed aggregations and complex event patterns."));
488 r.register(ApiStabilityMarker::beta("Kafka Integration", ApiCategory::Streaming, "0.1.0",
489 "Apache Kafka consumer/producer integration for ingesting RDF triples from event streams."));
490 r.register(ApiStabilityMarker::beta(
491 "NATS Integration",
492 ApiCategory::Streaming,
493 "0.1.0",
494 "NATS.io message broker integration for lightweight RDF event streaming.",
495 ));
496 r.register(ApiStabilityMarker::beta("Federated SPARQL Query", ApiCategory::Federation, "0.2.0",
497 "Distributed SPARQL query execution across multiple OxiRS endpoints with cost-based join reordering."));
498 r.register(ApiStabilityMarker::beta("Stream Fault Tolerance", ApiCategory::Streaming, "0.2.0",
499 "Bulkhead isolation, supervisor trees, and checkpoint recovery for streaming pipeline resilience."));
500
501 r.register(ApiStabilityMarker::beta("Modbus TCP/RTU", ApiCategory::Industrial, "0.1.0",
503 "Modbus TCP and RTU protocol client with RDF mapping for industrial sensor data ingestion."));
504 r.register(ApiStabilityMarker::beta(
505 "Modbus ASCII Protocol",
506 ApiCategory::Industrial,
507 "0.2.0",
508 "Modbus ASCII framing with LRC checksum for legacy industrial device connectivity.",
509 ));
510 r.register(ApiStabilityMarker::beta(
511 "Modbus TLS Client",
512 ApiCategory::Industrial,
513 "0.2.0",
514 "Modbus Secure (TLS over IANA port 802) for encrypted industrial communications.",
515 ));
516 r.register(ApiStabilityMarker::beta("CANbus/J1939 Integration", ApiCategory::Industrial, "0.1.0",
517 "CANbus and J1939 protocol support with automatic RDF triple generation from CAN frames."));
518 r.register(ApiStabilityMarker::beta("UDS ISO 14229 Diagnostics", ApiCategory::Industrial, "0.2.0",
519 "Unified Diagnostic Services (ISO 14229) protocol client for automotive ECU diagnostics."));
520 r.register(ApiStabilityMarker::beta(
521 "CANopen DS-301",
522 ApiCategory::Industrial,
523 "0.2.0",
524 "CANopen DS-301 protocol support with NMT, SDO, PDO, and object dictionary management.",
525 ));
526
527 r.register(ApiStabilityMarker::beta("Vector Similarity Search", ApiCategory::ArtificialIntelligence, "0.1.0",
529 "HNSW-based approximate nearest neighbour search for semantic similarity queries over RDF entity embeddings."));
530 r.register(ApiStabilityMarker::beta("Knowledge Graph Embeddings", ApiCategory::ArtificialIntelligence, "0.1.0",
531 "TransE, DistMult, ComplEx, and RotatE knowledge graph embedding models for entity and relation representation."));
532 r.register(ApiStabilityMarker::beta("GraphRAG Retrieval", ApiCategory::ArtificialIntelligence, "0.2.0",
533 "Graph-Retrieval-Augmented Generation (GraphRAG) combining SPARQL traversal with LLM reasoning for QA."));
534 r.register(ApiStabilityMarker::beta("Graph Neural Networks", ApiCategory::ArtificialIntelligence, "0.2.0",
535 "GraphSAGE and Graph Attention Network (GAT) implementations for inductive knowledge graph representation learning."));
536 r.register(ApiStabilityMarker::beta("Conversational AI (RAG Chat)", ApiCategory::ArtificialIntelligence, "0.1.0",
537 "Multi-turn conversational AI with retrieval-augmented generation over RDF knowledge graphs."));
538 r.register(ApiStabilityMarker::experimental("Physics Simulation", ApiCategory::PhysicsSimulation, "0.2.0",
539 "RDF-backed physics simulation with digital twin support (DTDL v2), conservation laws, and predictive maintenance."));
540 r.register(ApiStabilityMarker::experimental(
541 "Digital Twin Framework",
542 ApiCategory::PhysicsSimulation,
543 "0.2.0",
544 "DTDL v2 digital twin modeling with synchronisation reports and RDF integration.",
545 ));
546 r.register(ApiStabilityMarker::experimental("Predictive Maintenance", ApiCategory::PhysicsSimulation, "0.2.0",
547 "ML-based predictive maintenance using anomaly detection and remaining useful life estimation."));
548
549 r.register(ApiStabilityMarker::beta("Time-Series Database", ApiCategory::TimeSeries, "0.1.0",
551 "High-performance time-series storage with columnar compression, range queries, and downsampling."));
552 r.register(ApiStabilityMarker::beta("Anomaly Detection", ApiCategory::TimeSeries, "0.2.0",
553 "Statistical anomaly detection using Z-score, IQR, EWMA, and Isolation Forest algorithms."));
554 r.register(ApiStabilityMarker::beta(
555 "Time-Series Forecasting",
556 ApiCategory::TimeSeries,
557 "0.2.0",
558 "Holt-Winters exponential smoothing for time-series forecasting and trend analysis.",
559 ));
560 r.register(ApiStabilityMarker::beta("Prometheus Remote Write", ApiCategory::TimeSeries, "0.2.0",
561 "Prometheus remote write protocol support for ingesting metrics into OxiRS time-series storage."));
562
563 r.register(ApiStabilityMarker::beta("DID (Decentralised Identifiers)", ApiCategory::Security, "0.2.0",
565 "W3C Decentralised Identifier support (did:key, did:web, did:ion, did:pkh, did:ethr) with key management.")
566 .with_spec("https://www.w3.org/TR/did-core/"));
567 r.register(ApiStabilityMarker::beta("W3C Verifiable Credentials", ApiCategory::Security, "0.2.0",
568 "W3C Verifiable Credentials Data Model 2.0 with JSON-LD proof signatures and verification.")
569 .with_spec("https://www.w3.org/TR/vc-data-model-2.0/"));
570 r.register(ApiStabilityMarker::experimental(
571 "Zero-Knowledge Proofs",
572 ApiCategory::Security,
573 "0.2.0",
574 "ZKP-based credential presentations for privacy-preserving identity verification.",
575 ));
576 r.register(ApiStabilityMarker::experimental(
577 "DID Key Rotation",
578 ApiCategory::Security,
579 "0.2.0",
580 "Automated cryptographic key rotation with DID document update propagation.",
581 ));
582
583 r.register(ApiStabilityMarker::stable("SPARQL 1.1 HTTP Protocol", ApiCategory::Server, "0.1.0",
585 "W3C SPARQL 1.1 HTTP protocol endpoint with GET and POST query support, content negotiation, and streaming results.")
586 .with_spec("https://www.w3.org/TR/sparql11-protocol/"));
587 r.register(ApiStabilityMarker::stable("Fuseki-Compatible REST API", ApiCategory::Server, "0.1.0",
588 "Apache Jena Fuseki-compatible REST API for dataset management, upload, and administration."));
589 r.register(ApiStabilityMarker::stable("GraphQL HTTP Server", ApiCategory::Server, "0.1.0",
590 "GraphQL-over-HTTP server with multipart upload, introspection, and schema-first API design."));
591 r.register(ApiStabilityMarker::beta(
592 "Multi-tenancy",
593 ApiCategory::Server,
594 "0.2.0",
595 "Dataset-level multi-tenancy with isolated namespaces and per-tenant resource quotas.",
596 ));
597
598 r.register(ApiStabilityMarker::beta(
600 "WASM RDF Store",
601 ApiCategory::WebAssembly,
602 "0.2.0",
603 "Full OxiRS RDF store compiled to WebAssembly for in-browser and edge deployment.",
604 ));
605 r.register(ApiStabilityMarker::beta(
606 "WASM SPARQL Engine",
607 ApiCategory::WebAssembly,
608 "0.2.0",
609 "SPARQL query execution in WebAssembly with zero native dependencies.",
610 ));
611 r.register(ApiStabilityMarker::beta("WASM SPARQL UPDATE", ApiCategory::WebAssembly, "0.2.0",
612 "SPARQL UPDATE operations available in the WASM build for client-side data modification."));
613
614 r.register(ApiStabilityMarker::experimental("Quantum Graph Optimization", ApiCategory::Quantum, "0.2.0",
616 "Quantum-inspired optimization algorithms for SPARQL query planning over large knowledge graphs."));
617 r.register(ApiStabilityMarker::experimental("Quantum SPARQL Sampling", ApiCategory::Quantum, "0.2.0",
618 "Quantum amplitude estimation for approximate SPARQL aggregate queries on large datasets."));
619
620 r.register(ApiStabilityMarker::stable("OxiRS CLI Tool", ApiCategory::Tooling, "0.1.0",
622 "Command-line interface for SPARQL queries, dataset management, benchmarking, and graph analytics."));
623 r.register(ApiStabilityMarker::stable("SPARQL Query Profiler", ApiCategory::Tooling, "0.2.0",
624 "Query execution profiler with operator-level timing, memory usage, and optimization suggestions."));
625 r.register(ApiStabilityMarker::beta("Jena Parity Checker", ApiCategory::Tooling, "0.2.0",
626 "Automated verification tool comparing OxiRS feature coverage against Apache Jena baseline."));
627 r.register(ApiStabilityMarker::beta("API Stability Report", ApiCategory::Tooling, "0.2.0",
628 "Automated API stability report generation tracking stability commitments across OxiRS releases."));
629
630 r
631 }
632
633 pub fn all_apis(&self) -> &[ApiStabilityMarker] {
635 &self.markers
636 }
637
638 pub fn stable_apis(&self) -> Vec<&ApiStabilityMarker> {
640 self.markers.iter().filter(|m| m.is_stable()).collect()
641 }
642
643 pub fn beta_apis(&self) -> Vec<&ApiStabilityMarker> {
645 self.markers.iter().filter(|m| m.is_beta()).collect()
646 }
647
648 pub fn experimental_apis(&self) -> Vec<&ApiStabilityMarker> {
650 self.markers
651 .iter()
652 .filter(|m| m.is_experimental())
653 .collect()
654 }
655
656 pub fn deprecated_apis(&self) -> Vec<&ApiStabilityMarker> {
658 self.markers.iter().filter(|m| m.is_deprecated()).collect()
659 }
660
661 pub fn apis_by_category(&self, category: &ApiCategory) -> Vec<&ApiStabilityMarker> {
663 self.markers
664 .iter()
665 .filter(|m| &m.category == category)
666 .collect()
667 }
668
669 pub fn production_ready_apis(&self) -> Vec<&ApiStabilityMarker> {
671 self.markers
672 .iter()
673 .filter(|m| m.level.is_production_ready())
674 .collect()
675 }
676
677 pub fn summary(&self) -> RegistrySummary {
679 RegistrySummary {
680 total: self.markers.len(),
681 stable_count: self.stable_apis().len(),
682 beta_count: self.beta_apis().len(),
683 experimental_count: self.experimental_apis().len(),
684 deprecated_count: self.deprecated_apis().len(),
685 }
686 }
687
688 pub fn generate_report(&self) -> String {
690 let summary = self.summary();
691 let mut report = String::with_capacity(8192);
692
693 report.push_str("# OxiRS API Stability Report\n\n");
694 report.push_str("> Auto-generated from the OxiRS StabilityRegistry.\n\n");
695 report.push_str("## Summary\n\n");
696 report.push_str("| Metric | Value |\n|--------|-------|\n");
697 report.push_str(&format!("| Total APIs tracked | {} |\n", summary.total));
698 report.push_str(&format!(
699 "| Stable | {} ({:.0}%) |\n",
700 summary.stable_count,
701 summary.stable_percentage()
702 ));
703 report.push_str(&format!("| Beta | {} |\n", summary.beta_count));
704 report.push_str(&format!(
705 "| Experimental | {} |\n",
706 summary.experimental_count
707 ));
708 report.push_str(&format!("| Deprecated | {} |\n", summary.deprecated_count));
709 report.push_str(&format!(
710 "| Production-ready (Stable+Beta) | {} ({:.0}%) |\n\n",
711 summary.stable_count + summary.beta_count,
712 (summary.stable_count + summary.beta_count) as f64 / summary.total.max(1) as f64
713 * 100.0
714 ));
715
716 let categories = [
717 ApiCategory::QueryLanguage,
718 ApiCategory::DataModel,
719 ApiCategory::RdfStar,
720 ApiCategory::Reasoning,
721 ApiCategory::GraphQuery,
722 ApiCategory::Geospatial,
723 ApiCategory::Validation,
724 ApiCategory::Storage,
725 ApiCategory::Streaming,
726 ApiCategory::Federation,
727 ApiCategory::Industrial,
728 ApiCategory::ArtificialIntelligence,
729 ApiCategory::TimeSeries,
730 ApiCategory::PhysicsSimulation,
731 ApiCategory::Security,
732 ApiCategory::Server,
733 ApiCategory::WebAssembly,
734 ApiCategory::Quantum,
735 ApiCategory::Tooling,
736 ];
737
738 report.push_str("## API Details by Category\n\n");
739 for category in &categories {
740 let apis = self.apis_by_category(category);
741 if apis.is_empty() {
742 continue;
743 }
744 report.push_str(&format!("### {}\n\n", category.label()));
745 report.push_str("| Feature | Status | Since | Description |\n");
746 report.push_str("|---------|--------|-------|-------------|\n");
747 for api in &apis {
748 let desc = if api.description.len() > 80 {
749 format!("{}...", &api.description[..77])
750 } else {
751 api.description.to_string()
752 };
753 report.push_str(&format!(
754 "| {} | {} | {} | {} |\n",
755 api.feature,
756 api.level.indicator(),
757 api.since,
758 desc
759 ));
760 }
761 report.push('\n');
762 }
763
764 report.push_str("## Stability Level Definitions\n\n");
765 report.push_str("| Level | Guarantee |\n|-------|----------|\n");
766 report.push_str("| [STABLE] | Backward compatible throughout 1.x.y |\n");
767 report.push_str("| [BETA] | Stable but may change in minor releases |\n");
768 report.push_str("| [EXPERIMENTAL] | May change or be removed in any release |\n");
769 report.push_str("| [DEPRECATED] | Scheduled for removal in next major version |\n");
770
771 report
772 }
773
774 pub fn validate(&self) -> Vec<String> {
776 let mut errors = Vec::new();
777 for (i, marker) in self.markers.iter().enumerate() {
778 if marker.feature.is_empty() {
779 errors.push(format!("Marker #{i}: feature name is empty"));
780 }
781 if marker.description.is_empty() {
782 errors.push(format!(
783 "Marker #{}: '{}' has empty description",
784 i, marker.feature
785 ));
786 }
787 if marker.since.is_empty() {
788 errors.push(format!(
789 "Marker #{}: '{}' has empty 'since' version",
790 i, marker.feature
791 ));
792 }
793 }
794 errors
795 }
796}
797
798impl Default for StabilityRegistry {
799 fn default() -> Self {
800 Self::new()
801 }
802}
803
804#[cfg(test)]
805mod tests {
806 use super::*;
807 use std::collections::HashSet;
808
809 fn registry() -> StabilityRegistry {
810 StabilityRegistry::oxirs_v1_stability()
811 }
812
813 #[test]
814 fn test_registry_is_non_empty() {
815 assert!(!registry().all_apis().is_empty());
816 }
817
818 #[test]
819 fn test_registry_has_minimum_api_count() {
820 assert!(
821 registry().all_apis().len() >= 40,
822 "Registry should track at least 40 APIs"
823 );
824 }
825
826 #[test]
827 fn test_registry_validates_cleanly() {
828 let errors = registry().validate();
829 assert!(errors.is_empty(), "Validation errors: {:?}", errors);
830 }
831
832 #[test]
833 fn test_stable_apis_non_empty() {
834 assert!(!registry().stable_apis().is_empty());
835 }
836
837 #[test]
838 fn test_beta_apis_non_empty() {
839 assert!(!registry().beta_apis().is_empty());
840 }
841
842 #[test]
843 fn test_experimental_apis_non_empty() {
844 assert!(!registry().experimental_apis().is_empty());
845 }
846
847 #[test]
848 fn test_deprecated_apis_empty_by_default() {
849 assert!(registry().deprecated_apis().is_empty());
850 }
851
852 #[test]
853 fn test_stable_apis_are_all_stable() {
854 for api in registry().stable_apis() {
855 assert!(
856 matches!(api.level, StabilityLevel::Stable),
857 "{} should be Stable",
858 api.feature
859 );
860 }
861 }
862
863 #[test]
864 fn test_beta_apis_are_all_beta() {
865 for api in registry().beta_apis() {
866 assert!(
867 matches!(api.level, StabilityLevel::Beta),
868 "{} should be Beta",
869 api.feature
870 );
871 }
872 }
873
874 #[test]
875 fn test_experimental_apis_are_all_experimental() {
876 for api in registry().experimental_apis() {
877 assert!(
878 matches!(api.level, StabilityLevel::Experimental),
879 "{} should be Experimental",
880 api.feature
881 );
882 }
883 }
884
885 #[test]
886 fn test_counts_sum_to_total() {
887 let reg = registry();
888 let s = reg.summary();
889 assert_eq!(
890 s.stable_count + s.beta_count + s.experimental_count + s.deprecated_count,
891 s.total
892 );
893 }
894
895 #[test]
896 fn test_query_language_apis_exist() {
897 assert!(!registry()
898 .apis_by_category(&ApiCategory::QueryLanguage)
899 .is_empty());
900 }
901
902 #[test]
903 fn test_data_model_apis_exist() {
904 assert!(!registry()
905 .apis_by_category(&ApiCategory::DataModel)
906 .is_empty());
907 }
908
909 #[test]
910 fn test_reasoning_apis_exist() {
911 assert!(!registry()
912 .apis_by_category(&ApiCategory::Reasoning)
913 .is_empty());
914 }
915
916 #[test]
917 fn test_ai_apis_exist() {
918 assert!(!registry()
919 .apis_by_category(&ApiCategory::ArtificialIntelligence)
920 .is_empty());
921 }
922
923 #[test]
924 fn test_security_apis_exist() {
925 assert!(!registry()
926 .apis_by_category(&ApiCategory::Security)
927 .is_empty());
928 }
929
930 #[test]
931 fn test_tooling_apis_exist() {
932 assert!(!registry()
933 .apis_by_category(&ApiCategory::Tooling)
934 .is_empty());
935 }
936
937 #[test]
938 fn test_geospatial_apis_exist() {
939 assert!(!registry()
940 .apis_by_category(&ApiCategory::Geospatial)
941 .is_empty());
942 }
943
944 #[test]
945 fn test_storage_apis_exist() {
946 assert!(!registry()
947 .apis_by_category(&ApiCategory::Storage)
948 .is_empty());
949 }
950
951 #[test]
952 fn test_streaming_apis_exist() {
953 assert!(!registry()
954 .apis_by_category(&ApiCategory::Streaming)
955 .is_empty());
956 }
957
958 #[test]
959 fn test_industrial_apis_exist() {
960 assert!(!registry()
961 .apis_by_category(&ApiCategory::Industrial)
962 .is_empty());
963 }
964
965 #[test]
966 fn test_server_apis_exist() {
967 assert!(!registry().apis_by_category(&ApiCategory::Server).is_empty());
968 }
969
970 #[test]
971 fn test_wasm_apis_exist() {
972 assert!(!registry()
973 .apis_by_category(&ApiCategory::WebAssembly)
974 .is_empty());
975 }
976
977 #[test]
978 fn test_quantum_apis_exist() {
979 assert!(!registry()
980 .apis_by_category(&ApiCategory::Quantum)
981 .is_empty());
982 }
983
984 #[test]
985 fn test_time_series_apis_exist() {
986 assert!(!registry()
987 .apis_by_category(&ApiCategory::TimeSeries)
988 .is_empty());
989 }
990
991 #[test]
992 fn test_physics_apis_exist() {
993 assert!(!registry()
994 .apis_by_category(&ApiCategory::PhysicsSimulation)
995 .is_empty());
996 }
997
998 #[test]
999 fn test_rdf_star_apis_exist() {
1000 assert!(!registry()
1001 .apis_by_category(&ApiCategory::RdfStar)
1002 .is_empty());
1003 }
1004
1005 #[test]
1006 fn test_federation_apis_exist() {
1007 assert!(!registry()
1008 .apis_by_category(&ApiCategory::Federation)
1009 .is_empty());
1010 }
1011
1012 #[test]
1013 fn test_sparql_11_select_is_stable() {
1014 let reg = registry();
1015 let api = reg
1016 .all_apis()
1017 .iter()
1018 .find(|m| m.feature == "SPARQL 1.1 SELECT");
1019 assert!(api.is_some(), "SPARQL 1.1 SELECT should be registered");
1020 assert!(api.expect("API should be registered").is_stable());
1021 }
1022
1023 #[test]
1024 fn test_rdf_data_model_is_stable() {
1025 let reg = registry();
1026 let api = reg
1027 .all_apis()
1028 .iter()
1029 .find(|m| m.feature == "RDF 1.2 Data Model");
1030 assert!(api.is_some());
1031 assert!(api.expect("API should be registered").is_stable());
1032 }
1033
1034 #[test]
1035 fn test_graphql_api_is_stable() {
1036 let reg = registry();
1037 let api = reg.all_apis().iter().find(|m| m.feature == "GraphQL API");
1038 assert!(api.is_some());
1039 assert!(api.expect("API should be registered").is_stable());
1040 }
1041
1042 #[test]
1043 fn test_geosparql_is_stable() {
1044 let reg = registry();
1045 let api = reg.all_apis().iter().find(|m| m.feature == "GeoSPARQL 1.1");
1046 assert!(api.is_some());
1047 assert!(api.expect("API should be registered").is_stable());
1048 }
1049
1050 #[test]
1051 fn test_shacl_is_stable() {
1052 let reg = registry();
1053 let api = reg
1054 .all_apis()
1055 .iter()
1056 .find(|m| m.feature == "SHACL Validation");
1057 assert!(api.is_some());
1058 assert!(api.expect("API should be registered").is_stable());
1059 }
1060
1061 #[test]
1062 fn test_time_series_db_is_beta() {
1063 let reg = registry();
1064 let api = reg
1065 .all_apis()
1066 .iter()
1067 .find(|m| m.feature == "Time-Series Database");
1068 assert!(api.is_some());
1069 assert!(api.expect("API should be registered").is_beta());
1070 }
1071
1072 #[test]
1073 fn test_graphrag_is_beta() {
1074 let reg = registry();
1075 let api = reg
1076 .all_apis()
1077 .iter()
1078 .find(|m| m.feature == "GraphRAG Retrieval");
1079 assert!(api.is_some());
1080 assert!(api.expect("API should be registered").is_beta());
1081 }
1082
1083 #[test]
1084 fn test_physics_simulation_is_experimental() {
1085 let reg = registry();
1086 let api = reg
1087 .all_apis()
1088 .iter()
1089 .find(|m| m.feature == "Physics Simulation");
1090 assert!(api.is_some());
1091 assert!(api.expect("API should be registered").is_experimental());
1092 }
1093
1094 #[test]
1095 fn test_quantum_is_experimental() {
1096 let reg = registry();
1097 let api = reg
1098 .all_apis()
1099 .iter()
1100 .find(|m| m.feature == "Quantum Graph Optimization");
1101 assert!(api.is_some());
1102 assert!(api.expect("API should be registered").is_experimental());
1103 }
1104
1105 #[test]
1106 fn test_stable_percentage_in_range() {
1107 let s = registry().summary();
1108 assert!(s.stable_percentage() > 0.0 && s.stable_percentage() <= 100.0);
1109 }
1110
1111 #[test]
1112 fn test_production_ready_percentage_in_range() {
1113 let s = registry().summary();
1114 assert!(s.production_ready_percentage() > 0.0 && s.production_ready_percentage() <= 100.0);
1115 }
1116
1117 #[test]
1118 fn test_production_ready_ge_stable_percentage() {
1119 let s = registry().summary();
1120 assert!(s.production_ready_percentage() >= s.stable_percentage());
1121 }
1122
1123 #[test]
1124 fn test_generate_report_non_empty() {
1125 assert!(!registry().generate_report().is_empty());
1126 }
1127
1128 #[test]
1129 fn test_report_contains_summary_header() {
1130 assert!(registry().generate_report().contains("## Summary"));
1131 }
1132
1133 #[test]
1134 fn test_report_contains_stable_indicator() {
1135 assert!(registry().generate_report().contains("[STABLE]"));
1136 }
1137
1138 #[test]
1139 fn test_report_contains_beta_indicator() {
1140 assert!(registry().generate_report().contains("[BETA]"));
1141 }
1142
1143 #[test]
1144 fn test_report_contains_experimental_indicator() {
1145 assert!(registry().generate_report().contains("[EXPERIMENTAL]"));
1146 }
1147
1148 #[test]
1149 fn test_report_contains_api_details_header() {
1150 assert!(registry()
1151 .generate_report()
1152 .contains("## API Details by Category"));
1153 }
1154
1155 #[test]
1156 fn test_report_contains_sparql_select() {
1157 assert!(registry().generate_report().contains("SPARQL 1.1 SELECT"));
1158 }
1159
1160 #[test]
1161 fn test_stable_is_production_ready() {
1162 assert!(StabilityLevel::Stable.is_production_ready());
1163 }
1164
1165 #[test]
1166 fn test_beta_is_production_ready() {
1167 assert!(StabilityLevel::Beta.is_production_ready());
1168 }
1169
1170 #[test]
1171 fn test_experimental_is_not_production_ready() {
1172 assert!(!StabilityLevel::Experimental.is_production_ready());
1173 }
1174
1175 #[test]
1176 fn test_deprecated_is_production_ready() {
1177 let dep = StabilityLevel::Deprecated {
1178 since: "0.9.0",
1179 replacement: None,
1180 };
1181 assert!(dep.is_production_ready());
1182 }
1183
1184 #[test]
1185 fn test_stability_labels() {
1186 assert_eq!(StabilityLevel::Stable.label(), "Stable");
1187 assert_eq!(StabilityLevel::Beta.label(), "Beta");
1188 assert_eq!(StabilityLevel::Experimental.label(), "Experimental");
1189 let dep = StabilityLevel::Deprecated {
1190 since: "0.9.0",
1191 replacement: None,
1192 };
1193 assert_eq!(dep.label(), "Deprecated");
1194 }
1195
1196 #[test]
1197 fn test_stability_indicators() {
1198 assert_eq!(StabilityLevel::Stable.indicator(), "[STABLE]");
1199 assert_eq!(StabilityLevel::Beta.indicator(), "[BETA]");
1200 assert_eq!(StabilityLevel::Experimental.indicator(), "[EXPERIMENTAL]");
1201 let dep = StabilityLevel::Deprecated {
1202 since: "0.9.0",
1203 replacement: None,
1204 };
1205 assert_eq!(dep.indicator(), "[DEPRECATED]");
1206 }
1207
1208 #[test]
1209 fn test_marker_stable_builder() {
1210 let m = ApiStabilityMarker::stable("Test API", ApiCategory::Tooling, "1.0.0", "A test API");
1211 assert!(m.is_stable());
1212 assert_eq!(m.feature, "Test API");
1213 assert!(m.spec_url.is_none());
1214 }
1215
1216 #[test]
1217 fn test_marker_with_spec_url() {
1218 let m = ApiStabilityMarker::stable("Spec API", ApiCategory::QueryLanguage, "1.0.0", "desc")
1219 .with_spec("https://example.org/spec");
1220 assert_eq!(m.spec_url, Some("https://example.org/spec"));
1221 }
1222
1223 #[test]
1224 fn test_marker_beta_builder() {
1225 let m = ApiStabilityMarker::beta("Beta Feature", ApiCategory::Streaming, "0.5.0", "desc");
1226 assert!(m.is_beta());
1227 assert!(!m.is_stable());
1228 }
1229
1230 #[test]
1231 fn test_marker_experimental_builder() {
1232 let m =
1233 ApiStabilityMarker::experimental("Exp Feature", ApiCategory::Quantum, "0.9.0", "desc");
1234 assert!(m.is_experimental());
1235 assert!(!m.is_beta());
1236 }
1237
1238 #[test]
1239 fn test_marker_deprecated_builder() {
1240 let m = ApiStabilityMarker::deprecated(
1241 "Old Feature",
1242 ApiCategory::Tooling,
1243 "0.1.0",
1244 "0.9.0",
1245 Some("New Feature"),
1246 "desc",
1247 );
1248 assert!(m.is_deprecated());
1249 if let StabilityLevel::Deprecated { since, replacement } = &m.level {
1250 assert_eq!(*since, "0.9.0");
1251 assert_eq!(*replacement, Some("New Feature"));
1252 }
1253 }
1254
1255 #[test]
1256 fn test_feature_names_are_unique() {
1257 let reg = registry();
1258 let mut seen = HashSet::new();
1259 for api in reg.all_apis() {
1260 assert!(
1261 seen.insert(api.feature),
1262 "Duplicate feature name: '{}'",
1263 api.feature
1264 );
1265 }
1266 }
1267
1268 #[test]
1269 fn test_production_ready_apis_non_empty() {
1270 assert!(!registry().production_ready_apis().is_empty());
1271 }
1272
1273 #[test]
1274 fn test_production_ready_count_matches_summary() {
1275 let reg = registry();
1276 let s = reg.summary();
1277 let actual = reg.production_ready_apis().len();
1278 assert_eq!(actual, s.stable_count + s.beta_count + s.deprecated_count);
1279 }
1280
1281 #[test]
1282 fn test_default_registry_is_empty() {
1283 let reg = StabilityRegistry::default();
1284 assert_eq!(reg.all_apis().len(), 0);
1285 }
1286
1287 #[test]
1288 fn test_register_and_retrieve() {
1289 let mut reg = StabilityRegistry::new();
1290 reg.register(ApiStabilityMarker::stable(
1291 "Custom API",
1292 ApiCategory::Tooling,
1293 "1.0.0",
1294 "Custom",
1295 ));
1296 assert_eq!(reg.all_apis().len(), 1);
1297 assert_eq!(reg.stable_apis().len(), 1);
1298 }
1299
1300 #[test]
1301 fn test_all_markers_have_valid_since_version() {
1302 for api in registry().all_apis() {
1303 assert!(
1304 api.since.starts_with('0') || api.since.starts_with('1'),
1305 "'{}' has invalid since version: '{}'",
1306 api.feature,
1307 api.since
1308 );
1309 }
1310 }
1311
1312 #[test]
1313 fn test_stable_sparql_update_exists() {
1314 let reg = registry();
1315 let api = reg
1316 .all_apis()
1317 .iter()
1318 .find(|m| m.feature == "SPARQL 1.1 Update");
1319 assert!(api.is_some());
1320 assert!(api.expect("API should be registered").is_stable());
1321 }
1322
1323 #[test]
1324 fn test_owl2_rl_is_stable() {
1325 let reg = registry();
1326 let api = reg
1327 .all_apis()
1328 .iter()
1329 .find(|m| m.feature == "OWL 2 RL Reasoning");
1330 assert!(api.is_some());
1331 assert!(api.expect("API should be registered").is_stable());
1332 }
1333
1334 #[test]
1335 fn test_owl2_ql_is_beta() {
1336 let reg = registry();
1337 let api = reg
1338 .all_apis()
1339 .iter()
1340 .find(|m| m.feature == "OWL 2 QL Reasoning");
1341 assert!(api.is_some());
1342 assert!(api.expect("API should be registered").is_beta());
1343 }
1344
1345 #[test]
1346 fn test_category_label_non_empty() {
1347 let categories = [
1348 ApiCategory::QueryLanguage,
1349 ApiCategory::DataModel,
1350 ApiCategory::Reasoning,
1351 ApiCategory::GraphQuery,
1352 ApiCategory::Geospatial,
1353 ApiCategory::Validation,
1354 ApiCategory::Storage,
1355 ApiCategory::Streaming,
1356 ApiCategory::Industrial,
1357 ApiCategory::ArtificialIntelligence,
1358 ApiCategory::Security,
1359 ApiCategory::RdfStar,
1360 ApiCategory::Federation,
1361 ApiCategory::Server,
1362 ApiCategory::WebAssembly,
1363 ApiCategory::TimeSeries,
1364 ApiCategory::PhysicsSimulation,
1365 ApiCategory::Quantum,
1366 ApiCategory::Tooling,
1367 ];
1368 for cat in &categories {
1369 assert!(
1370 !cat.label().is_empty(),
1371 "Category label should not be empty"
1372 );
1373 }
1374 }
1375
1376 #[test]
1377 fn test_zkp_is_experimental() {
1378 let reg = registry();
1379 let api = reg
1380 .all_apis()
1381 .iter()
1382 .find(|m| m.feature == "Zero-Knowledge Proofs");
1383 assert!(api.is_some());
1384 assert!(api.expect("API should be registered").is_experimental());
1385 }
1386
1387 #[test]
1388 fn test_did_is_beta() {
1389 let reg = registry();
1390 let api = reg
1391 .all_apis()
1392 .iter()
1393 .find(|m| m.feature == "DID (Decentralised Identifiers)");
1394 assert!(api.is_some());
1395 assert!(api.expect("API should be registered").is_beta());
1396 }
1397
1398 #[test]
1399 fn test_turtle_serialization_has_spec_url() {
1400 let reg = registry();
1401 let api = reg
1402 .all_apis()
1403 .iter()
1404 .find(|m| m.feature == "Turtle Serialization");
1405 assert!(api.is_some());
1406 assert!(api.expect("API should be registered").spec_url.is_some());
1407 }
1408
1409 #[test]
1410 fn test_report_contains_stability_level_defs() {
1411 let report = registry().generate_report();
1412 assert!(report.contains("Stability Level Definitions"));
1413 }
1414}