Skip to main content

oxirs_core/stability/
mod.rs

1//! # API Stability Markers for OxiRS
2//!
3//! This module provides stability documentation and tracking for OxiRS public APIs,
4//! following Rust API guidelines and SemVer compatibility promises.
5
6pub mod compatibility;
7
8/// API stability levels for OxiRS public interfaces.
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum StabilityLevel {
11    /// Guaranteed backward compatible through the 1.x.y series.
12    Stable,
13    /// Likely stable but may change in minor releases.
14    Beta,
15    /// May change or be removed in any release.
16    Experimental,
17    /// Scheduled for removal in the next major version.
18    Deprecated {
19        /// The version in which this API was deprecated.
20        since: &'static str,
21        /// The recommended replacement API, if one exists.
22        replacement: Option<&'static str>,
23    },
24}
25
26impl StabilityLevel {
27    /// Returns true if the API is considered production-ready (Stable or Beta).
28    pub fn is_production_ready(&self) -> bool {
29        matches!(
30            self,
31            StabilityLevel::Stable | StabilityLevel::Beta | StabilityLevel::Deprecated { .. }
32        )
33    }
34
35    /// Returns a short descriptive label for the stability level.
36    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    /// Returns a bracket indicator for report formatting.
46    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/// The broad functional category of an OxiRS API.
57#[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    /// Returns a human-readable label for the category.
82    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/// A single API stability marker recording the stability commitment for one feature.
108#[derive(Debug, Clone)]
109pub struct ApiStabilityMarker {
110    /// The feature name.
111    pub feature: &'static str,
112    /// The functional category this feature belongs to.
113    pub category: ApiCategory,
114    /// The stability guarantee for this feature.
115    pub level: StabilityLevel,
116    /// The version in which this API was introduced.
117    pub since: &'static str,
118    /// Human-readable description of the feature and its stability rationale.
119    pub description: &'static str,
120    /// Optional link to specification or documentation.
121    pub spec_url: Option<&'static str>,
122}
123
124impl ApiStabilityMarker {
125    /// Creates a Stable API marker.
126    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    /// Creates a Beta API marker.
143    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    /// Creates an Experimental API marker.
160    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    /// Creates a Deprecated API marker.
177    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    /// Attaches a specification URL (builder pattern).
199    pub fn with_spec(mut self, url: &'static str) -> Self {
200        self.spec_url = Some(url);
201        self
202    }
203
204    /// Returns true if this marker is at the Stable level.
205    pub fn is_stable(&self) -> bool {
206        matches!(self.level, StabilityLevel::Stable)
207    }
208
209    /// Returns true if this marker is at the Beta level.
210    pub fn is_beta(&self) -> bool {
211        matches!(self.level, StabilityLevel::Beta)
212    }
213
214    /// Returns true if this marker is at the Experimental level.
215    pub fn is_experimental(&self) -> bool {
216        matches!(self.level, StabilityLevel::Experimental)
217    }
218
219    /// Returns true if this marker is Deprecated.
220    pub fn is_deprecated(&self) -> bool {
221        matches!(self.level, StabilityLevel::Deprecated { .. })
222    }
223}
224
225/// Summary statistics for a stability registry.
226#[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    /// Percentage of APIs that are Stable (0-100).
237    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    /// Percentage of APIs that are production-ready (Stable + Beta + Deprecated).
245    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
254/// Registry of all OxiRS public API stability commitments.
255pub struct StabilityRegistry {
256    markers: Vec<ApiStabilityMarker>,
257}
258
259impl StabilityRegistry {
260    /// Creates an empty registry.
261    pub fn new() -> Self {
262        Self {
263            markers: Vec::new(),
264        }
265    }
266
267    /// Registers a new API stability marker.
268    pub fn register(&mut self, marker: ApiStabilityMarker) {
269        self.markers.push(marker);
270    }
271
272    /// Returns the complete OxiRS v1 stability registry.
273    pub fn oxirs_v1_stability() -> Self {
274        let mut r = Self::new();
275
276        // SPARQL Query Language
277        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        // RDF Data Model
338        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        // Reasoning
403        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        // GraphQL
426        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        // GeoSPARQL
440        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        // Validation
459        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        // Storage
474        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        // Streaming / Federation
486        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        // Industrial
502        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        // AI
528        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        // Time-Series
550        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        // Security
564        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        // Server
584        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        // WebAssembly
599        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        // Quantum
615        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        // Tooling
621        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    /// Returns all registered API stability markers.
634    pub fn all_apis(&self) -> &[ApiStabilityMarker] {
635        &self.markers
636    }
637
638    /// Returns all APIs at the Stable level.
639    pub fn stable_apis(&self) -> Vec<&ApiStabilityMarker> {
640        self.markers.iter().filter(|m| m.is_stable()).collect()
641    }
642
643    /// Returns all APIs at the Beta level.
644    pub fn beta_apis(&self) -> Vec<&ApiStabilityMarker> {
645        self.markers.iter().filter(|m| m.is_beta()).collect()
646    }
647
648    /// Returns all APIs at the Experimental level.
649    pub fn experimental_apis(&self) -> Vec<&ApiStabilityMarker> {
650        self.markers
651            .iter()
652            .filter(|m| m.is_experimental())
653            .collect()
654    }
655
656    /// Returns all Deprecated APIs.
657    pub fn deprecated_apis(&self) -> Vec<&ApiStabilityMarker> {
658        self.markers.iter().filter(|m| m.is_deprecated()).collect()
659    }
660
661    /// Returns all APIs in a given category.
662    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    /// Returns all APIs that are production-ready (Stable, Beta, or Deprecated).
670    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    /// Computes summary statistics for this registry.
678    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    /// Generates a comprehensive Markdown stability report.
689    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    /// Validates all markers for non-empty required fields.
775    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}