Skip to main content

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}