reovim_kernel/api/version.rs
1//! Version management and compatibility checking.
2//!
3//! Provides API version constants and compatibility utilities for ensuring
4//! module-kernel version compatibility.
5
6use std::fmt;
7
8// ============================================================================
9// Version Type
10// ============================================================================
11
12/// Semantic version representation.
13///
14/// Provides type-safe version handling with named fields instead of tuples.
15///
16/// # FFI Safety
17///
18/// This type is `#[repr(C)]` for predictable memory layout across dynamic
19/// library boundaries. This ensures the struct can be safely passed to/from
20/// dynamically loaded modules.
21///
22/// # Ordering
23///
24/// Versions are ordered lexicographically by (major, minor, patch):
25/// - `1.0.0 < 1.0.1 < 1.1.0 < 2.0.0`
26///
27/// This allows version comparison and sorting:
28///
29/// ```
30/// use reovim_kernel::api::v1::Version;
31///
32/// assert!(Version::new(1, 0, 0) < Version::new(1, 0, 1));
33/// assert!(Version::new(1, 2, 0) < Version::new(2, 0, 0));
34///
35/// let mut versions = vec![
36/// Version::new(2, 0, 0),
37/// Version::new(1, 0, 1),
38/// Version::new(1, 0, 0),
39/// ];
40/// versions.sort();
41/// assert_eq!(versions[0], Version::new(1, 0, 0));
42/// ```
43#[repr(C)]
44#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
45pub struct Version {
46 /// Major version - breaking changes increment this.
47 pub major: u32,
48 /// Minor version - backwards-compatible additions increment this.
49 pub minor: u32,
50 /// Patch version - backwards-compatible fixes increment this.
51 pub patch: u32,
52}
53
54impl Version {
55 /// Create a new version.
56 #[inline]
57 #[must_use]
58 pub const fn new(major: u32, minor: u32, patch: u32) -> Self {
59 Self {
60 major,
61 minor,
62 patch,
63 }
64 }
65}
66
67impl fmt::Display for Version {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
70 }
71}
72
73// ============================================================================
74// Version Constants
75// ============================================================================
76
77/// Current API version.
78///
79/// This is a pre-release API (0.x.y). Breaking changes may occur between minor versions.
80/// Phase 4.6 bumped from 0.1.0 to 0.2.0 for ABI-breaking `ModuleProbe` extensions.
81pub const API_VERSION: Version = Version::new(0, 2, 0);
82
83/// Current API version as a string.
84pub const API_VERSION_STR: &str = "0.2.0";
85
86// ============================================================================
87// Error Types
88// ============================================================================
89
90/// Error returned when version compatibility check fails.
91#[derive(Debug, Clone, PartialEq, Eq)]
92pub struct VersionError {
93 /// The kind of version error.
94 pub kind: VersionErrorKind,
95 /// The version that was required.
96 pub required: Version,
97 /// The version that was provided/available.
98 pub provided: Version,
99}
100
101/// Kinds of version compatibility errors.
102#[derive(Debug, Clone, Copy, PartialEq, Eq)]
103pub enum VersionErrorKind {
104 /// Major version mismatch (breaking change).
105 MajorMismatch,
106 /// Required minor version is newer than provided.
107 MinorTooNew,
108}
109
110impl fmt::Display for VersionError {
111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112 match self.kind {
113 VersionErrorKind::MajorMismatch => {
114 write!(
115 f,
116 "API major version mismatch: required {}.x.x, provided {}.x.x",
117 self.required.major, self.provided.major
118 )
119 }
120 VersionErrorKind::MinorTooNew => {
121 write!(
122 f,
123 "API minor version too new: required {}.{}.x, provided {}.{}.x",
124 self.required.major,
125 self.required.minor,
126 self.provided.major,
127 self.provided.minor
128 )
129 }
130 }
131 }
132}
133
134impl std::error::Error for VersionError {}
135
136// ============================================================================
137// Compatibility Functions
138// ============================================================================
139
140/// Check if the current API version is compatible with the required version.
141///
142/// Returns `Ok(())` if compatible, `Err(VersionError)` otherwise.
143///
144/// # Compatibility Rules (semver)
145///
146/// - Major version must match exactly
147/// - Module can require an older minor version than kernel provides
148/// - Patch version is ignored for compatibility
149///
150/// # Errors
151///
152/// Returns `Err(VersionError)` if:
153/// - Major version mismatch (`VersionErrorKind::MajorMismatch`)
154/// - Required minor version is newer than provided (`VersionErrorKind::MinorTooNew`)
155///
156/// # Example
157///
158/// ```
159/// use reovim_kernel::api::v1::{check_api_version, Version};
160///
161/// // Check if kernel provides compatible API
162/// let result = check_api_version(Version::new(0, 2, 0));
163/// assert!(result.is_ok());
164/// ```
165pub const fn check_api_version(required: Version) -> Result<(), VersionError> {
166 if is_compatible(required, API_VERSION) {
167 Ok(())
168 } else if required.major != API_VERSION.major {
169 Err(VersionError {
170 kind: VersionErrorKind::MajorMismatch,
171 required,
172 provided: API_VERSION,
173 })
174 } else {
175 Err(VersionError {
176 kind: VersionErrorKind::MinorTooNew,
177 required,
178 provided: API_VERSION,
179 })
180 }
181}
182
183/// Check if `required` version is compatible with `provided` version.
184///
185/// # Compatibility Rules
186///
187/// - Major version must match exactly
188/// - Required minor must be <= provided minor (backwards compatible)
189/// - Patch version is ignored
190///
191/// # Example
192///
193/// ```
194/// use reovim_kernel::api::v1::{is_compatible, Version};
195///
196/// // Same version is compatible
197/// assert!(is_compatible(Version::new(1, 0, 0), Version::new(1, 0, 0)));
198///
199/// // Older required minor is compatible
200/// assert!(is_compatible(Version::new(1, 0, 0), Version::new(1, 1, 0)));
201///
202/// // Newer required minor is NOT compatible
203/// assert!(!is_compatible(Version::new(1, 2, 0), Version::new(1, 1, 0)));
204///
205/// // Different major is NOT compatible
206/// assert!(!is_compatible(Version::new(2, 0, 0), Version::new(1, 0, 0)));
207/// ```
208#[must_use]
209pub const fn is_compatible(required: Version, provided: Version) -> bool {
210 // Major must match exactly
211 if required.major != provided.major {
212 return false;
213 }
214
215 // Required minor must be <= provided minor
216 required.minor <= provided.minor
217}