1pub 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
71#[cfg(feature = "serialization")]
72use serde::{Deserialize, Serialize};
73
74pub use compatibility::{CompatibilityChecker, CompatibilityLevel, CompatibilityReport};
76pub use deprecation::{DeprecationManager, DeprecationPolicy, DeprecationStatus};
77pub use migration::{MigrationManager, MigrationPlan, MigrationStep};
78pub use negotiation::{ClientCapabilities, NegotiationResult, VersionNegotiator};
79pub use semantic::{Version, VersionBuilder, VersionConstraint, VersionRange};
80
81#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
83#[derive(Debug, Clone, PartialEq, Eq)]
84pub struct ApiVersion {
85 pub version: Version,
87 pub release_date: chrono::DateTime<chrono::Utc>,
89 pub stability: StabilityLevel,
91 pub support_status: SupportStatus,
93 pub end_of_life: Option<chrono::DateTime<chrono::Utc>>,
95 pub features: BTreeSet<String>,
97 pub breakingchanges: Vec<String>,
99 pub new_features: Vec<String>,
101 pub bug_fixes: Vec<String>,
103 pub deprecated_features: Vec<String>,
105 pub min_clientversion: Option<Version>,
107 pub max_clientversion: Option<Version>,
109}
110
111#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
113#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
114pub enum StabilityLevel {
115 Experimental,
117 Alpha,
119 Beta,
121 Stable,
123 Mature,
125 Legacy,
127}
128
129impl StabilityLevel {
130 #[must_use]
132 pub const fn as_str(&self) -> &'static str {
133 match self {
134 Self::Experimental => "experimental",
135 Self::Alpha => "alpha",
136 Self::Beta => "beta",
137 Self::Stable => "stable",
138 Self::Mature => "mature",
139 Self::Legacy => "legacy",
140 }
141 }
142}
143
144#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
146#[derive(Debug, Clone, Copy, PartialEq, Eq)]
147pub enum SupportStatus {
148 Active,
150 Maintenance,
152 Deprecated,
154 EndOfLife,
156 SecurityOnly,
158}
159
160impl SupportStatus {
161 #[must_use]
163 pub const fn as_str(&self) -> &'static str {
164 match self {
165 Self::Active => "active",
166 Self::Maintenance => "maintenance",
167 Self::Deprecated => "deprecated",
168 Self::EndOfLife => "end_of_life",
169 Self::SecurityOnly => "security_only",
170 }
171 }
172}
173
174pub struct VersionManager {
176 versions: HashMap<Version, ApiVersion>,
178 currentversion: Option<Version>,
180 compatibility_checker: CompatibilityChecker,
182 negotiator: VersionNegotiator,
184 migration_manager: MigrationManager,
186 deprecation_manager: DeprecationManager,
188}
189
190impl VersionManager {
191 #[must_use]
193 pub fn new() -> Self {
194 Self {
195 versions: HashMap::new(),
196 currentversion: None,
197 compatibility_checker: CompatibilityChecker::new(),
198 negotiator: VersionNegotiator::new(),
199 migration_manager: MigrationManager::new(),
200 deprecation_manager: DeprecationManager::new(),
201 }
202 }
203
204 pub fn registerversion(&mut self, apiversion: ApiVersion) -> Result<(), CoreError> {
210 let version = apiversion.version.clone();
211
212 if self.versions.contains_key(&version) {
214 return Err(CoreError::ComputationError(
215 crate::error::ErrorContext::new(format!("Version {version} is already registered")),
216 ));
217 }
218
219 self.compatibility_checker.register_version(&apiversion)?;
221
222 self.migration_manager.register_version(&apiversion)?;
224
225 self.deprecation_manager.register_version(&apiversion)?;
227
228 self.versions.insert(version, apiversion);
229 Ok(())
230 }
231
232 pub fn set_currentversion(&mut self, version: Version) -> Result<(), CoreError> {
238 if !self.versions.contains_key(&version) {
239 return Err(CoreError::ComputationError(
240 crate::error::ErrorContext::new(format!("Version {version} is not registered")),
241 ));
242 }
243
244 self.currentversion = Some(version);
245 Ok(())
246 }
247
248 #[must_use]
250 pub fn currentversion(&self) -> Option<&Version> {
251 self.currentversion.as_ref()
252 }
253
254 #[must_use]
256 pub fn getversions(&self) -> Vec<&ApiVersion> {
257 let mut versions: Vec<_> = self.versions.values().collect();
258 versions.sort_by(|a, b| a.version.cmp(&b.version));
259 versions
260 }
261
262 #[must_use]
264 pub fn get_supportedversions(&self) -> Vec<&ApiVersion> {
265 self.versions
266 .values()
267 .filter(|v| {
268 matches!(
269 v.support_status,
270 SupportStatus::Active | SupportStatus::Maintenance
271 )
272 })
273 .collect()
274 }
275
276 #[must_use]
278 pub fn getversion(&self, version: &Version) -> Option<&ApiVersion> {
279 self.versions.get(version)
280 }
281
282 pub fn check_compatibility(
288 &self,
289 fromversion: &Version,
290 toversion: &Version,
291 ) -> Result<CompatibilityLevel, CoreError> {
292 self.compatibility_checker
293 .check_compatibility(fromversion, toversion)
294 }
295
296 pub fn get_compatibility_report(
302 &self,
303 fromversion: &Version,
304 toversion: &Version,
305 ) -> Result<CompatibilityReport, CoreError> {
306 self.compatibility_checker
307 .get_compatibility_report(fromversion, toversion)
308 }
309
310 pub fn negotiateversion(
316 &self,
317 client_capabilities: &ClientCapabilities,
318 ) -> Result<NegotiationResult, CoreError> {
319 let supportedversions: Vec<_> = self
320 .get_supportedversions()
321 .into_iter()
322 .map(|v| &v.version)
323 .collect();
324
325 self.negotiator
326 .negotiate(client_capabilities, &supportedversions)
327 }
328
329 pub fn get_migration_plan(
335 &self,
336 fromversion: &Version,
337 toversion: &Version,
338 ) -> Result<MigrationPlan, CoreError> {
339 self.migration_manager
340 .create_migration_plan(fromversion, toversion)
341 }
342
343 #[must_use]
345 pub fn isversion_deprecated(&self, version: &Version) -> bool {
346 if let Some(apiversion) = self.versions.get(version) {
347 matches!(
348 apiversion.support_status,
349 SupportStatus::Deprecated | SupportStatus::EndOfLife
350 )
351 } else {
352 false
353 }
354 }
355
356 #[must_use]
358 pub fn get_deprecation_status(&self, version: &Version) -> Option<DeprecationStatus> {
359 self.deprecation_manager.get_deprecation_status(version)
360 }
361
362 pub fn update_deprecation_status(
368 &mut self,
369 version: &Version,
370 status: DeprecationStatus,
371 ) -> Result<(), CoreError> {
372 self.deprecation_manager.update_status(version, status)
373 }
374
375 #[must_use]
377 pub fn get_latest_in_major(&self, major: u64) -> Option<&ApiVersion> {
378 self.versions
379 .values()
380 .filter(|v| v.version.major() == major)
381 .max_by(|a, b| a.version.cmp(&b.version))
382 }
383
384 #[must_use]
386 pub fn get_latest_stable(&self) -> Option<&ApiVersion> {
387 self.versions
388 .values()
389 .filter(|v| {
390 v.stability == StabilityLevel::Stable || v.stability == StabilityLevel::Mature
391 })
392 .filter(|v| v.support_status == SupportStatus::Active)
393 .max_by(|a, b| a.version.cmp(&b.version))
394 }
395
396 #[must_use]
398 pub fn has_upgrade_path(&self, fromversion: &Version, toversion: &Version) -> bool {
399 self.migration_manager
400 .has_migration_path(fromversion, toversion)
401 }
402
403 pub fn validate_constraint(
409 &self,
410 constraint: &VersionConstraint,
411 ) -> Result<Vec<&Version>, CoreError> {
412 let matchingversions: Vec<_> = self
413 .versions
414 .keys()
415 .filter(|v| constraint.matches(v))
416 .collect();
417
418 Ok(matchingversions)
419 }
420
421 #[must_use]
423 pub fn getversion_statistics(&self) -> VersionStatistics {
424 let mut stats = VersionStatistics::default();
425
426 for apiversion in self.versions.values() {
427 stats.totalversions += 1;
428
429 match apiversion.stability {
430 StabilityLevel::Experimental => stats.experimentalversions += 1,
431 StabilityLevel::Alpha => stats.alphaversions += 1,
432 StabilityLevel::Beta => stats.betaversions += 1,
433 StabilityLevel::Stable => stats.stableversions += 1,
434 StabilityLevel::Mature => stats.matureversions += 1,
435 StabilityLevel::Legacy => stats.legacyversions += 1,
436 }
437
438 match apiversion.support_status {
439 SupportStatus::Active => stats.activeversions += 1,
440 SupportStatus::Maintenance => stats.maintenanceversions += 1,
441 SupportStatus::Deprecated => stats.deprecatedversions += 1,
442 SupportStatus::EndOfLife => stats.end_of_lifeversions += 1,
443 SupportStatus::SecurityOnly => stats.security_onlyversions += 1,
444 }
445 }
446
447 stats
448 }
449
450 pub fn perform_maintenance(&mut self) -> Result<MaintenanceReport, CoreError> {
456 let mut report = MaintenanceReport::default();
457 let now = chrono::Utc::now();
458
459 for (version, apiversion) in &mut self.versions {
461 if let Some(eol_date) = apiversion.end_of_life {
462 if now > eol_date && apiversion.support_status != SupportStatus::EndOfLife {
463 apiversion.support_status = SupportStatus::EndOfLife;
464 report.versions_marked_eol.push(version.clone());
465 }
466 }
467 }
468
469 let deprecation_updates = self.deprecation_manager.perform_maintenance()?;
471 report.deprecation_updates = deprecation_updates.len();
472
473 let migration_cleanup = self.migration_manager.cleanup_old_plans()?;
475 report.migration_plans_cleaned = migration_cleanup;
476
477 Ok(report)
478 }
479}
480
481impl Default for VersionManager {
482 fn default() -> Self {
483 Self::new()
484 }
485}
486
487#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
489#[derive(Debug, Clone, Default)]
490pub struct VersionStatistics {
491 pub totalversions: usize,
493 pub experimentalversions: usize,
495 pub alphaversions: usize,
497 pub betaversions: usize,
499 pub stableversions: usize,
501 pub matureversions: usize,
503 pub legacyversions: usize,
505 pub activeversions: usize,
507 pub maintenanceversions: usize,
509 pub deprecatedversions: usize,
511 pub end_of_lifeversions: usize,
513 pub security_onlyversions: usize,
515}
516
517#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
519#[derive(Debug, Clone, Default)]
520pub struct MaintenanceReport {
521 pub versions_marked_eol: Vec<Version>,
523 pub deprecation_updates: usize,
525 pub migration_plans_cleaned: usize,
527}
528
529pub struct ApiVersionBuilder {
531 version: Option<Version>,
532 release_date: chrono::DateTime<chrono::Utc>,
533 stability: StabilityLevel,
534 support_status: SupportStatus,
535 end_of_life: Option<chrono::DateTime<chrono::Utc>>,
536 features: BTreeSet<String>,
537 breakingchanges: Vec<String>,
538 new_features: Vec<String>,
539 bug_fixes: Vec<String>,
540 deprecated_features: Vec<String>,
541 min_clientversion: Option<Version>,
542 max_clientversion: Option<Version>,
543}
544
545impl ApiVersionBuilder {
546 #[must_use]
548 pub fn new(version: Version) -> Self {
549 Self {
550 version: Some(version),
551 release_date: chrono::Utc::now(),
552 stability: StabilityLevel::Stable,
553 support_status: SupportStatus::Active,
554 end_of_life: None,
555 features: BTreeSet::new(),
556 breakingchanges: Vec::new(),
557 new_features: Vec::new(),
558 bug_fixes: Vec::new(),
559 deprecated_features: Vec::new(),
560 min_clientversion: None,
561 max_clientversion: None,
562 }
563 }
564
565 #[must_use]
567 pub fn release_date(mut self, date: chrono::DateTime<chrono::Utc>) -> Self {
568 self.release_date = date;
569 self
570 }
571
572 #[must_use]
574 pub fn stability(mut self, stability: StabilityLevel) -> Self {
575 self.stability = stability;
576 self
577 }
578
579 #[must_use]
581 pub fn support_status(mut self, status: SupportStatus) -> Self {
582 self.support_status = status;
583 self
584 }
585
586 #[must_use]
588 pub fn end_of_life(mut self, date: chrono::DateTime<chrono::Utc>) -> Self {
589 self.end_of_life = Some(date);
590 self
591 }
592
593 #[must_use]
595 pub fn feature(mut self, feature: &str) -> Self {
596 self.features.insert(feature.to_string());
597 self
598 }
599
600 #[must_use]
602 pub fn breaking_change(mut self, change: &str) -> Self {
603 self.breakingchanges.push(change.to_string());
604 self
605 }
606
607 #[must_use]
609 pub fn new_feature(mut self, feature: &str) -> Self {
610 self.new_features.push(feature.to_string());
611 self
612 }
613
614 #[must_use]
616 pub fn bug_fix(mut self, fix: &str) -> Self {
617 self.bug_fixes.push(fix.to_string());
618 self
619 }
620
621 #[must_use]
623 pub fn deprecated_feature(mut self, feature: &str) -> Self {
624 self.deprecated_features.push(feature.to_string());
625 self
626 }
627
628 #[must_use]
630 pub fn min_clientversion(mut self, version: Version) -> Self {
631 self.min_clientversion = Some(version);
632 self
633 }
634
635 #[must_use]
637 pub fn max_clientversion(mut self, version: Version) -> Self {
638 self.max_clientversion = Some(version);
639 self
640 }
641
642 pub fn build(self) -> Result<ApiVersion, CoreError> {
648 let version = self.version.ok_or_else(|| {
649 CoreError::ComputationError(crate::error::ErrorContext::new(
650 "Version is required".to_string(),
651 ))
652 })?;
653
654 Ok(ApiVersion {
655 version,
656 release_date: self.release_date,
657 stability: self.stability,
658 support_status: self.support_status,
659 end_of_life: self.end_of_life,
660 features: self.features,
661 breakingchanges: self.breakingchanges,
662 new_features: self.new_features,
663 bug_fixes: self.bug_fixes,
664 deprecated_features: self.deprecated_features,
665 min_clientversion: self.min_clientversion,
666 max_clientversion: self.max_clientversion,
667 })
668 }
669}
670
671#[cfg(test)]
672mod tests {
673 use super::*;
674
675 #[test]
676 fn testversion_manager_creation() {
677 let manager = VersionManager::new();
678 assert!(manager.currentversion().is_none());
679 assert_eq!(manager.getversions().len(), 0);
680 }
681
682 #[test]
683 fn test_apiversion_builder() {
684 let version = Version::parse("1.0.0").unwrap();
685 let apiversion = ApiVersionBuilder::new(version)
686 .stability(StabilityLevel::Stable)
687 .feature("feature1")
688 .new_feature("New awesome feature")
689 .build()
690 .unwrap();
691
692 assert_eq!(apiversion.version.to_string(), "1.0.0");
693 assert_eq!(apiversion.stability, StabilityLevel::Stable);
694 assert!(apiversion.features.contains("feature1"));
695 assert_eq!(apiversion.new_features.len(), 1);
696 }
697
698 #[test]
699 fn testversion_registration() {
700 let mut manager = VersionManager::new();
701 let version = Version::parse("1.0.0").unwrap();
702 let apiversion = ApiVersionBuilder::new(version.clone()).build().unwrap();
703
704 manager.registerversion(apiversion).unwrap();
705 assert_eq!(manager.getversions().len(), 1);
706 assert!(manager.getversion(&version).is_some());
707 }
708
709 #[test]
710 fn test_currentversion_setting() {
711 let mut manager = VersionManager::new();
712 let version = Version::parse("1.0.0").unwrap();
713 let apiversion = ApiVersionBuilder::new(version.clone()).build().unwrap();
714
715 manager.registerversion(apiversion).unwrap();
716 manager.set_currentversion(version.clone()).unwrap();
717 assert_eq!(manager.currentversion(), Some(&version));
718 }
719
720 #[test]
721 fn test_stability_levels() {
722 assert_eq!(StabilityLevel::Experimental.as_str(), "experimental");
723 assert_eq!(StabilityLevel::Stable.as_str(), "stable");
724 assert_eq!(StabilityLevel::Mature.as_str(), "mature");
725
726 assert!(StabilityLevel::Experimental < StabilityLevel::Alpha);
727 assert!(StabilityLevel::Stable > StabilityLevel::Beta);
728 }
729
730 #[test]
731 fn test_support_status() {
732 assert_eq!(SupportStatus::Active.as_str(), "active");
733 assert_eq!(SupportStatus::Deprecated.as_str(), "deprecated");
734 assert_eq!(SupportStatus::EndOfLife.as_str(), "end_of_life");
735 }
736
737 #[test]
738 fn testversion_statistics() {
739 let mut manager = VersionManager::new();
740
741 let v1 = ApiVersionBuilder::new(Version::parse("1.0.0").unwrap())
743 .stability(StabilityLevel::Stable)
744 .build()
745 .unwrap();
746 let v2 = ApiVersionBuilder::new(Version::parse("2.0.0").unwrap())
747 .stability(StabilityLevel::Beta)
748 .support_status(SupportStatus::Maintenance)
749 .build()
750 .unwrap();
751
752 manager.registerversion(v1).unwrap();
753 manager.registerversion(v2).unwrap();
754
755 let stats = manager.getversion_statistics();
756 assert_eq!(stats.totalversions, 2);
757 assert_eq!(stats.stableversions, 1);
758 assert_eq!(stats.betaversions, 1);
759 assert_eq!(stats.activeversions, 1);
760 assert_eq!(stats.maintenanceversions, 1);
761 }
762}