scirs2_core/versioning/
mod.rs

1//! # API Versioning Infrastructure
2//!
3//! Production-grade API versioning system for `SciRS2` Core providing semantic
4//! versioning, backward compatibility guarantees, and version negotiation
5//! capabilities for enterprise deployments and long-term API stability.
6//!
7//! ## Features
8//!
9//! - Semantic versioning (`SemVer`) compliance with custom extensions
10//! - Backward compatibility enforcement and validation
11//! - API version negotiation and client-server compatibility
12//! - Breaking change detection and migration assistance
13//! - Version deprecation management with transition periods
14//! - API evolution tracking and documentation generation
15//! - Enterprise-grade stability guarantees
16//! - Integration with CI/CD pipelines for automated compatibility testing
17//!
18//! ## Modules
19//!
20//! - `semantic`: Semantic versioning implementation with `SciRS2` extensions
21//! - `compatibility`: Backward compatibility checking and enforcement
22//! - `negotiation`: Version negotiation between clients and servers
23//! - `migration`: Migration assistance for API upgrades
24//! - `deprecation`: Deprecation management and transition planning
25//!
26//! ## Example
27//!
28//! ```rust
29//! use scirs2_core::versioning::{Version, VersionManager, CompatibilityLevel, ApiVersionBuilder, ClientCapabilities};
30//!
31//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
32//! // Create version manager
33//! let mut version_manager = VersionManager::new();
34//!
35//! // Register API versions using the builder pattern
36//! let v1_0_0 = ApiVersionBuilder::new(Version::parse("1.0.0")?)
37//!     .new_feature("Initial API release")
38//!     .build()?;
39//! let v1_1_0 = ApiVersionBuilder::new(Version::parse("1.1.0")?)
40//!     .new_feature("Added new computation methods")
41//!     .build()?;
42//! let v2_0_0 = ApiVersionBuilder::new(Version::parse("2.0.0")?)
43//!     .breaking_change("Changed function signatures")
44//!     .build()?;
45//!
46//! version_manager.registerversion(v1_0_0.clone())?;
47//! version_manager.registerversion(v1_1_0.clone())?;
48//! version_manager.registerversion(v2_0_0.clone())?;
49//!
50//! // Check compatibility
51//! let compat = version_manager.check_compatibility(&v1_0_0.version, &v1_1_0.version)?;
52//! assert_eq!(compat, CompatibilityLevel::BackwardCompatible);
53//!
54//! // Negotiate version with client capabilities
55//! let client_caps = ClientCapabilities::new("test_client".to_string(), Version::parse("1.0.5")?);
56//! let negotiated = version_manager.negotiateversion(&client_caps)?;
57//! assert!(negotiated.negotiated_version.major() >= 1);
58//! # Ok(())
59//! # }
60//! ```
61
62pub mod compatibility;
63pub mod deprecation;
64pub mod migration;
65pub mod negotiation;
66pub mod semantic;
67
68use crate::error::CoreError;
69use std::collections::{BTreeSet, HashMap};
70
71use serde::{Deserialize, Serialize};
72
73// Re-export main types
74pub use compatibility::{CompatibilityChecker, CompatibilityLevel, CompatibilityReport};
75pub use deprecation::{DeprecationManager, DeprecationPolicy, DeprecationStatus};
76pub use migration::{MigrationManager, MigrationPlan, MigrationStep};
77pub use negotiation::{ClientCapabilities, NegotiationResult, VersionNegotiator};
78pub use semantic::{Version, VersionBuilder, VersionConstraint, VersionRange};
79
80/// API version information with metadata
81#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
82pub struct ApiVersion {
83    /// The semantic version
84    pub version: Version,
85    /// Release date
86    pub release_date: chrono::DateTime<chrono::Utc>,
87    /// Stability level
88    pub stability: StabilityLevel,
89    /// Support status
90    pub support_status: SupportStatus,
91    /// End of life date (if applicable)
92    pub end_of_life: Option<chrono::DateTime<chrono::Utc>>,
93    /// Feature flags supported in this version
94    pub features: BTreeSet<String>,
95    /// Breaking changes from previous version
96    pub breakingchanges: Vec<String>,
97    /// New features in this version
98    pub new_features: Vec<String>,
99    /// Bug fixes in this version
100    pub bug_fixes: Vec<String>,
101    /// Deprecated features in this version
102    pub deprecated_features: Vec<String>,
103    /// Minimum client version required
104    pub min_clientversion: Option<Version>,
105    /// Maximum client version supported
106    pub max_clientversion: Option<Version>,
107}
108
109/// API stability levels
110#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
111pub enum StabilityLevel {
112    /// Experimental - subject to breaking changes
113    Experimental,
114    /// Alpha - feature complete but may have breaking changes
115    Alpha,
116    /// Beta - stable API but may have minor breaking changes
117    Beta,
118    /// Stable - backward compatible changes only
119    Stable,
120    /// Mature - minimal changes, long-term support
121    Mature,
122    /// Legacy - deprecated but still supported
123    Legacy,
124}
125
126impl StabilityLevel {
127    /// Get the string representation
128    #[must_use]
129    pub const fn as_str(&self) -> &'static str {
130        match self {
131            Self::Experimental => "experimental",
132            Self::Alpha => "alpha",
133            Self::Beta => "beta",
134            Self::Stable => "stable",
135            Self::Mature => "mature",
136            Self::Legacy => "legacy",
137        }
138    }
139}
140
141/// Support status for API versions
142#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
143pub enum SupportStatus {
144    /// Active development and support
145    Active,
146    /// Maintenance mode - bug fixes only
147    Maintenance,
148    /// Deprecated - migration encouraged
149    Deprecated,
150    /// End of life - no longer supported
151    EndOfLife,
152    /// Security updates only
153    SecurityOnly,
154}
155
156impl SupportStatus {
157    /// Get the string representation
158    #[must_use]
159    pub const fn as_str(&self) -> &'static str {
160        match self {
161            Self::Active => "active",
162            Self::Maintenance => "maintenance",
163            Self::Deprecated => "deprecated",
164            Self::EndOfLife => "end_of_life",
165            Self::SecurityOnly => "security_only",
166        }
167    }
168}
169
170/// Version manager for coordinating all versioning operations
171pub struct VersionManager {
172    /// Registered API versions
173    versions: HashMap<Version, ApiVersion>,
174    /// Current active version
175    currentversion: Option<Version>,
176    /// Compatibility checker
177    compatibility_checker: CompatibilityChecker,
178    /// Version negotiator
179    negotiator: VersionNegotiator,
180    /// Migration manager
181    migration_manager: MigrationManager,
182    /// Deprecation manager
183    deprecation_manager: DeprecationManager,
184}
185
186impl VersionManager {
187    /// Create a new version manager
188    #[must_use]
189    pub fn new() -> Self {
190        Self {
191            versions: HashMap::new(),
192            currentversion: None,
193            compatibility_checker: CompatibilityChecker::new(),
194            negotiator: VersionNegotiator::new(),
195            migration_manager: MigrationManager::new(),
196            deprecation_manager: DeprecationManager::new(),
197        }
198    }
199
200    /// Register an API version
201    ///
202    /// # Errors
203    ///
204    /// Returns an error if the version is already registered.
205    pub fn registerversion(&mut self, apiversion: ApiVersion) -> Result<(), CoreError> {
206        let version = apiversion.version.clone();
207
208        // Validate version is not already registered
209        if self.versions.contains_key(&version) {
210            return Err(CoreError::ComputationError(
211                crate::error::ErrorContext::new(format!("Version {version} is already registered")),
212            ));
213        }
214
215        // Register with compatibility checker
216        self.compatibility_checker.register_version(&apiversion)?;
217
218        // Register with migration manager
219        self.migration_manager.register_version(&apiversion)?;
220
221        // Register with deprecation manager
222        self.deprecation_manager.register_version(&apiversion)?;
223
224        self.versions.insert(version, apiversion);
225        Ok(())
226    }
227
228    /// Set the current active version
229    ///
230    /// # Errors
231    ///
232    /// Returns an error if the version is not registered.
233    pub fn set_currentversion(&mut self, version: Version) -> Result<(), CoreError> {
234        if !self.versions.contains_key(&version) {
235            return Err(CoreError::ComputationError(
236                crate::error::ErrorContext::new(format!("Version {version} is not registered")),
237            ));
238        }
239
240        self.currentversion = Some(version);
241        Ok(())
242    }
243
244    /// Get the current active version
245    #[must_use]
246    pub fn currentversion(&self) -> Option<&Version> {
247        self.currentversion.as_ref()
248    }
249
250    /// Get all registered versions
251    #[must_use]
252    pub fn getversions(&self) -> Vec<&ApiVersion> {
253        let mut versions: Vec<_> = self.versions.values().collect();
254        versions.sort_by(|a, b| a.version.cmp(&b.version));
255        versions
256    }
257
258    /// Get supported versions (active and maintenance)
259    #[must_use]
260    pub fn get_supportedversions(&self) -> Vec<&ApiVersion> {
261        self.versions
262            .values()
263            .filter(|v| {
264                matches!(
265                    v.support_status,
266                    SupportStatus::Active | SupportStatus::Maintenance
267                )
268            })
269            .collect()
270    }
271
272    /// Get version by version number
273    #[must_use]
274    pub fn getversion(&self, version: &Version) -> Option<&ApiVersion> {
275        self.versions.get(version)
276    }
277
278    /// Check compatibility between two versions
279    ///
280    /// # Errors
281    ///
282    /// Returns an error if compatibility checking fails.
283    pub fn check_compatibility(
284        &self,
285        fromversion: &Version,
286        toversion: &Version,
287    ) -> Result<CompatibilityLevel, CoreError> {
288        self.compatibility_checker
289            .check_compatibility(fromversion, toversion)
290    }
291
292    /// Get detailed compatibility report
293    ///
294    /// # Errors
295    ///
296    /// Returns an error if compatibility report generation fails.
297    pub fn get_compatibility_report(
298        &self,
299        fromversion: &Version,
300        toversion: &Version,
301    ) -> Result<CompatibilityReport, CoreError> {
302        self.compatibility_checker
303            .get_compatibility_report(fromversion, toversion)
304    }
305
306    /// Negotiate version with client
307    ///
308    /// # Errors
309    ///
310    /// Returns an error if version negotiation fails.
311    pub fn negotiateversion(
312        &self,
313        client_capabilities: &ClientCapabilities,
314    ) -> Result<NegotiationResult, CoreError> {
315        let supportedversions: Vec<_> = self
316            .get_supportedversions()
317            .into_iter()
318            .map(|v| &v.version)
319            .collect();
320
321        self.negotiator
322            .negotiate(client_capabilities, &supportedversions)
323    }
324
325    /// Get migration plan between versions
326    ///
327    /// # Errors
328    ///
329    /// Returns an error if migration plan generation fails.
330    pub fn get_migration_plan(
331        &self,
332        fromversion: &Version,
333        toversion: &Version,
334    ) -> Result<MigrationPlan, CoreError> {
335        self.migration_manager
336            .create_migration_plan(fromversion, toversion)
337    }
338
339    /// Check if a version is deprecated
340    #[must_use]
341    pub fn isversion_deprecated(&self, version: &Version) -> bool {
342        if let Some(apiversion) = self.versions.get(version) {
343            matches!(
344                apiversion.support_status,
345                SupportStatus::Deprecated | SupportStatus::EndOfLife
346            )
347        } else {
348            false
349        }
350    }
351
352    /// Get deprecation information for a version
353    #[must_use]
354    pub fn get_deprecation_status(&self, version: &Version) -> Option<DeprecationStatus> {
355        self.deprecation_manager.get_deprecation_status(version)
356    }
357
358    /// Update deprecation status
359    ///
360    /// # Errors
361    ///
362    /// Returns an error if the deprecation status update fails.
363    pub fn update_deprecation_status(
364        &mut self,
365        version: &Version,
366        status: DeprecationStatus,
367    ) -> Result<(), CoreError> {
368        self.deprecation_manager.update_status(version, status)
369    }
370
371    /// Get the latest version in a major version line
372    #[must_use]
373    pub fn get_latest_in_major(&self, major: u64) -> Option<&ApiVersion> {
374        self.versions
375            .values()
376            .filter(|v| v.version.major() == major)
377            .max_by(|a, b| a.version.cmp(&b.version))
378    }
379
380    /// Get the latest stable version
381    #[must_use]
382    pub fn get_latest_stable(&self) -> Option<&ApiVersion> {
383        self.versions
384            .values()
385            .filter(|v| {
386                v.stability == StabilityLevel::Stable || v.stability == StabilityLevel::Mature
387            })
388            .filter(|v| v.support_status == SupportStatus::Active)
389            .max_by(|a, b| a.version.cmp(&b.version))
390    }
391
392    /// Check if an upgrade path exists
393    #[must_use]
394    pub fn has_upgrade_path(&self, fromversion: &Version, toversion: &Version) -> bool {
395        self.migration_manager
396            .has_migration_path(fromversion, toversion)
397    }
398
399    /// Validate version constraints
400    ///
401    /// # Errors
402    ///
403    /// Returns an error if validation fails.
404    pub fn validate_constraint(
405        &self,
406        constraint: &VersionConstraint,
407    ) -> Result<Vec<&Version>, CoreError> {
408        let matchingversions: Vec<_> = self
409            .versions
410            .keys()
411            .filter(|v| constraint.matches(v))
412            .collect();
413
414        Ok(matchingversions)
415    }
416
417    /// Get version statistics
418    #[must_use]
419    pub fn getversion_statistics(&self) -> VersionStatistics {
420        let mut stats = VersionStatistics::default();
421
422        for apiversion in self.versions.values() {
423            stats.totalversions += 1;
424
425            match apiversion.stability {
426                StabilityLevel::Experimental => stats.experimentalversions += 1,
427                StabilityLevel::Alpha => stats.alphaversions += 1,
428                StabilityLevel::Beta => stats.betaversions += 1,
429                StabilityLevel::Stable => stats.stableversions += 1,
430                StabilityLevel::Mature => stats.matureversions += 1,
431                StabilityLevel::Legacy => stats.legacyversions += 1,
432            }
433
434            match apiversion.support_status {
435                SupportStatus::Active => stats.activeversions += 1,
436                SupportStatus::Maintenance => stats.maintenanceversions += 1,
437                SupportStatus::Deprecated => stats.deprecatedversions += 1,
438                SupportStatus::EndOfLife => stats.end_of_lifeversions += 1,
439                SupportStatus::SecurityOnly => stats.security_onlyversions += 1,
440            }
441        }
442
443        stats
444    }
445
446    /// Perform version maintenance tasks
447    ///
448    /// # Errors
449    ///
450    /// Returns an error if maintenance tasks fail.
451    pub fn perform_maintenance(&mut self) -> Result<MaintenanceReport, CoreError> {
452        let mut report = MaintenanceReport::default();
453        let now = chrono::Utc::now();
454
455        // Check for expired versions
456        for (version, apiversion) in &mut self.versions {
457            if let Some(eol_date) = apiversion.end_of_life {
458                if now > eol_date && apiversion.support_status != SupportStatus::EndOfLife {
459                    apiversion.support_status = SupportStatus::EndOfLife;
460                    report.versions_marked_eol.push(version.clone());
461                }
462            }
463        }
464
465        // Update deprecation statuses
466        let deprecation_updates = self.deprecation_manager.perform_maintenance()?;
467        report.deprecation_updates = deprecation_updates.len();
468
469        // Clean up old migration plans
470        let migration_cleanup = self.migration_manager.cleanup_old_plans()?;
471        report.migration_plans_cleaned = migration_cleanup;
472
473        Ok(report)
474    }
475}
476
477impl Default for VersionManager {
478    fn default() -> Self {
479        Self::new()
480    }
481}
482
483/// Version statistics for monitoring and reporting
484#[derive(Debug, Clone, Default, Serialize, Deserialize)]
485pub struct VersionStatistics {
486    /// Total number of registered versions
487    pub totalversions: usize,
488    /// Experimental versions
489    pub experimentalversions: usize,
490    /// Alpha versions
491    pub alphaversions: usize,
492    /// Beta versions
493    pub betaversions: usize,
494    /// Stable versions
495    pub stableversions: usize,
496    /// Mature versions
497    pub matureversions: usize,
498    /// Legacy versions
499    pub legacyversions: usize,
500    /// Active versions
501    pub activeversions: usize,
502    /// Maintenance versions
503    pub maintenanceversions: usize,
504    /// Deprecated versions
505    pub deprecatedversions: usize,
506    /// End of life versions
507    pub end_of_lifeversions: usize,
508    /// Security only versions
509    pub security_onlyversions: usize,
510}
511
512/// Maintenance report for version management operations
513#[derive(Debug, Clone, Default, Serialize, Deserialize)]
514pub struct MaintenanceReport {
515    /// Versions marked as end of life
516    pub versions_marked_eol: Vec<Version>,
517    /// Number of deprecation status updates
518    pub deprecation_updates: usize,
519    /// Number of migration plans cleaned up
520    pub migration_plans_cleaned: usize,
521}
522
523/// Builder for creating API versions
524pub struct ApiVersionBuilder {
525    version: Option<Version>,
526    release_date: chrono::DateTime<chrono::Utc>,
527    stability: StabilityLevel,
528    support_status: SupportStatus,
529    end_of_life: Option<chrono::DateTime<chrono::Utc>>,
530    features: BTreeSet<String>,
531    breakingchanges: Vec<String>,
532    new_features: Vec<String>,
533    bug_fixes: Vec<String>,
534    deprecated_features: Vec<String>,
535    min_clientversion: Option<Version>,
536    max_clientversion: Option<Version>,
537}
538
539impl ApiVersionBuilder {
540    /// Create a new API version builder
541    #[must_use]
542    pub fn new(version: Version) -> Self {
543        Self {
544            version: Some(version),
545            release_date: chrono::Utc::now(),
546            stability: StabilityLevel::Stable,
547            support_status: SupportStatus::Active,
548            end_of_life: None,
549            features: BTreeSet::new(),
550            breakingchanges: Vec::new(),
551            new_features: Vec::new(),
552            bug_fixes: Vec::new(),
553            deprecated_features: Vec::new(),
554            min_clientversion: None,
555            max_clientversion: None,
556        }
557    }
558
559    /// Set release date
560    #[must_use]
561    pub fn release_date(mut self, date: chrono::DateTime<chrono::Utc>) -> Self {
562        self.release_date = date;
563        self
564    }
565
566    /// Set stability level
567    #[must_use]
568    pub fn stability(mut self, stability: StabilityLevel) -> Self {
569        self.stability = stability;
570        self
571    }
572
573    /// Set support status
574    #[must_use]
575    pub fn support_status(mut self, status: SupportStatus) -> Self {
576        self.support_status = status;
577        self
578    }
579
580    /// Set end of life date
581    #[must_use]
582    pub fn end_of_life(mut self, date: chrono::DateTime<chrono::Utc>) -> Self {
583        self.end_of_life = Some(date);
584        self
585    }
586
587    /// Add a feature
588    #[must_use]
589    pub fn feature(mut self, feature: &str) -> Self {
590        self.features.insert(feature.to_string());
591        self
592    }
593
594    /// Add a breaking change
595    #[must_use]
596    pub fn breaking_change(mut self, change: &str) -> Self {
597        self.breakingchanges.push(change.to_string());
598        self
599    }
600
601    /// Add a new feature
602    #[must_use]
603    pub fn new_feature(mut self, feature: &str) -> Self {
604        self.new_features.push(feature.to_string());
605        self
606    }
607
608    /// Add a bug fix
609    #[must_use]
610    pub fn bug_fix(mut self, fix: &str) -> Self {
611        self.bug_fixes.push(fix.to_string());
612        self
613    }
614
615    /// Add a deprecated feature
616    #[must_use]
617    pub fn deprecated_feature(mut self, feature: &str) -> Self {
618        self.deprecated_features.push(feature.to_string());
619        self
620    }
621
622    /// Set minimum client version
623    #[must_use]
624    pub fn min_clientversion(mut self, version: Version) -> Self {
625        self.min_clientversion = Some(version);
626        self
627    }
628
629    /// Set maximum client version
630    #[must_use]
631    pub fn max_clientversion(mut self, version: Version) -> Self {
632        self.max_clientversion = Some(version);
633        self
634    }
635
636    /// Build the API version
637    ///
638    /// # Errors
639    ///
640    /// Returns an error if the version is not set.
641    pub fn build(self) -> Result<ApiVersion, CoreError> {
642        let version = self.version.ok_or_else(|| {
643            CoreError::ComputationError(crate::error::ErrorContext::new(
644                "Version is required".to_string(),
645            ))
646        })?;
647
648        Ok(ApiVersion {
649            version,
650            release_date: self.release_date,
651            stability: self.stability,
652            support_status: self.support_status,
653            end_of_life: self.end_of_life,
654            features: self.features,
655            breakingchanges: self.breakingchanges,
656            new_features: self.new_features,
657            bug_fixes: self.bug_fixes,
658            deprecated_features: self.deprecated_features,
659            min_clientversion: self.min_clientversion,
660            max_clientversion: self.max_clientversion,
661        })
662    }
663}
664
665#[cfg(test)]
666mod tests {
667    use super::*;
668
669    #[test]
670    fn testversion_manager_creation() {
671        let manager = VersionManager::new();
672        assert!(manager.currentversion().is_none());
673        assert_eq!(manager.getversions().len(), 0);
674    }
675
676    #[test]
677    fn test_apiversion_builder() {
678        let version = Version::parse("1.0.0").unwrap();
679        let apiversion = ApiVersionBuilder::new(version)
680            .stability(StabilityLevel::Stable)
681            .feature("feature1")
682            .new_feature("New awesome feature")
683            .build()
684            .unwrap();
685
686        assert_eq!(apiversion.version.to_string(), "1.0.0");
687        assert_eq!(apiversion.stability, StabilityLevel::Stable);
688        assert!(apiversion.features.contains("feature1"));
689        assert_eq!(apiversion.new_features.len(), 1);
690    }
691
692    #[test]
693    fn testversion_registration() {
694        let mut manager = VersionManager::new();
695        let version = Version::parse("1.0.0").unwrap();
696        let apiversion = ApiVersionBuilder::new(version.clone()).build().unwrap();
697
698        manager.registerversion(apiversion).unwrap();
699        assert_eq!(manager.getversions().len(), 1);
700        assert!(manager.getversion(&version).is_some());
701    }
702
703    #[test]
704    fn test_currentversion_setting() {
705        let mut manager = VersionManager::new();
706        let version = Version::parse("1.0.0").unwrap();
707        let apiversion = ApiVersionBuilder::new(version.clone()).build().unwrap();
708
709        manager.registerversion(apiversion).unwrap();
710        manager.set_currentversion(version.clone()).unwrap();
711        assert_eq!(manager.currentversion(), Some(&version));
712    }
713
714    #[test]
715    fn test_stability_levels() {
716        assert_eq!(StabilityLevel::Experimental.as_str(), "experimental");
717        assert_eq!(StabilityLevel::Stable.as_str(), "stable");
718        assert_eq!(StabilityLevel::Mature.as_str(), "mature");
719
720        assert!(StabilityLevel::Experimental < StabilityLevel::Alpha);
721        assert!(StabilityLevel::Stable > StabilityLevel::Beta);
722    }
723
724    #[test]
725    fn test_support_status() {
726        assert_eq!(SupportStatus::Active.as_str(), "active");
727        assert_eq!(SupportStatus::Deprecated.as_str(), "deprecated");
728        assert_eq!(SupportStatus::EndOfLife.as_str(), "end_of_life");
729    }
730
731    #[test]
732    fn testversion_statistics() {
733        let mut manager = VersionManager::new();
734
735        // Add some versions
736        let v1 = ApiVersionBuilder::new(Version::parse("1.0.0").unwrap())
737            .stability(StabilityLevel::Stable)
738            .build()
739            .unwrap();
740        let v2 = ApiVersionBuilder::new(Version::parse("2.0.0").unwrap())
741            .stability(StabilityLevel::Beta)
742            .support_status(SupportStatus::Maintenance)
743            .build()
744            .unwrap();
745
746        manager.registerversion(v1).unwrap();
747        manager.registerversion(v2).unwrap();
748
749        let stats = manager.getversion_statistics();
750        assert_eq!(stats.totalversions, 2);
751        assert_eq!(stats.stableversions, 1);
752        assert_eq!(stats.betaversions, 1);
753        assert_eq!(stats.activeversions, 1);
754        assert_eq!(stats.maintenanceversions, 1);
755    }
756}