bare_types/sys/kernel_version.rs
1//! Kernel version type for system information.
2//!
3//! This module provides a type-safe abstraction for kernel versions,
4//! ensuring valid version number parsing and comparison.
5//!
6//! Kernel versions typically follow semantic versioning (major.minor.patch)
7//! with an optional release/build string.
8//!
9//! # Examples
10//!
11//! ```rust
12//! use bare_types::sys::KernelVersion;
13//!
14//! // Parse from string
15//! let version: KernelVersion = "6.8.0-40-generic".parse()?;
16//!
17//! // Access components
18//! assert_eq!(version.major(), 6);
19//! assert_eq!(version.minor(), 8);
20//! assert_eq!(version.patch(), 0);
21//!
22//! // Get release string
23//! assert_eq!(version.release(), Some("40-generic"));
24//!
25//! // Compare versions
26//! assert!(version >= KernelVersion::new(6, 0, 0));
27//! # Ok::<(), bare_types::sys::KernelVersionError>(())
28//! ```
29use core::fmt;
30use core::str::FromStr;
31
32#[cfg(feature = "serde")]
33use serde::{Deserialize, Serialize};
34
35/// Error type for kernel version parsing.
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
38#[non_exhaustive]
39pub enum KernelVersionError {
40 /// Empty version string
41 ///
42 /// The provided string is empty. Kernel version strings must contain at least
43 /// major and a minor version number (e.g., "6.8").
44 Empty,
45 /// Invalid major version number
46 ///
47 /// The major version component could not be parsed as a valid u16 number.
48 /// Kernel version numbers must be non-negative integers.
49 InvalidMajor,
50 /// Invalid minor version number
51 ///
52 /// The minor version component could not be parsed as a valid u16 number.
53 /// Kernel version numbers must be non-negative integers.
54 InvalidMinor,
55 /// Invalid patch version number
56 ///
57 /// The patch version component could not be parsed as a valid u16 number.
58 /// Kernel version numbers must be non-negative integers.
59 InvalidPatch,
60 /// Too many numeric components (max 3)
61 ///
62 /// Kernel version strings can have at most 3 numeric components:
63 /// major.minor.patch. More than 3 components are not supported.
64 TooManyComponents,
65 /// Not enough numeric components (need at least 2)
66 ///
67 /// Kernel version strings must contain at least major and minor components.
68 /// Only a patch number (e.g., "6") is not sufficient.
69 NotEnoughComponents,
70 /// Negative version number
71 ///
72 /// Kernel version numbers cannot be negative. This error may occur if parsing
73 /// negative integers in version components.
74 NegativeVersion,
75}
76
77impl fmt::Display for KernelVersionError {
78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 match self {
80 Self::Empty => write!(f, "kernel version string is empty"),
81 Self::InvalidMajor => write!(f, "invalid major kernel version number"),
82 Self::InvalidMinor => write!(f, "invalid minor kernel version number"),
83 Self::InvalidPatch => write!(f, "invalid patch kernel version number"),
84 Self::TooManyComponents => {
85 write!(f, "kernel version has too many numeric components (max 3)")
86 }
87 Self::NotEnoughComponents => {
88 write!(f, "kernel version needs at least 2 numeric components")
89 }
90 Self::NegativeVersion => write!(f, "kernel version numbers cannot be negative"),
91 }
92 }
93}
94
95#[cfg(feature = "std")]
96impl std::error::Error for KernelVersionError {}
97
98/// Kernel version.
99///
100/// This type provides type-safe kernel version numbers with three numeric
101/// components (major, minor, patch) and an optional release string.
102///
103/// # Format
104///
105/// Kernel version format: `major.minor.patch[-release]`
106///
107/// - **Major**: Major version number (e.g., 6 for Linux 6.x)
108/// - **Minor**: Minor version number (e.g., 8 for Linux 6.8)
109/// - **Patch**: Patch level (e.g., 0 for Linux 6.8.0)
110/// - **Release**: Optional release string (e.g., "40-generic" for Ubuntu)
111///
112/// # Invariants
113///
114/// - Major, minor, and patch versions are always present (u16)
115/// - Release string is optional and must not contain leading '-'
116/// - All version numbers are non-negative
117///
118/// # Examples
119///
120/// ```rust
121/// use bare_types::sys::KernelVersion;
122///
123/// // Create from components
124/// let version = KernelVersion::new(6, 8, 0);
125///
126/// // With release string
127/// let version = KernelVersion::with_release(6, 8, 0, "40-generic");
128///
129/// // Parse from string
130/// let version: KernelVersion = "6.8.0-40-generic".parse()?;
131/// assert_eq!(version.major(), 6);
132/// assert_eq!(version.release(), Some("40-generic"));
133/// # Ok::<(), bare_types::sys::KernelVersionError>(())
134/// ```
135#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
136#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
137pub struct KernelVersion {
138 /// The major version number
139 major: u16,
140 /// The minor version number
141 minor: u16,
142 /// The patch version number
143 patch: u16,
144 /// The optional release/build string
145 release: Option<heapless::String<64>>,
146}
147
148impl KernelVersion {
149 /// Creates a new kernel version from components.
150 ///
151 /// # Arguments
152 ///
153 /// * `major` - The major version number
154 /// * `minor` - The minor version number
155 /// * `patch` - The patch version number
156 ///
157 /// To include a release string, use [`KernelVersion::with_release`].
158 ///
159 /// # Examples
160 ///
161 /// ```rust
162 /// use bare_types::sys::KernelVersion;
163 ///
164 /// // Simple version
165 /// let version = KernelVersion::new(6, 8, 0);
166 /// assert_eq!(version.major(), 6);
167 /// assert_eq!(version.minor(), 8);
168 /// assert_eq!(version.patch(), 0);
169 /// assert_eq!(version.release(), None);
170 ///
171 /// // With release string
172 /// let version = KernelVersion::with_release(6, 8, 0, "40-generic");
173 /// assert_eq!(version.release(), Some("40-generic"));
174 /// ```
175 #[must_use]
176 pub const fn new(major: u16, minor: u16, patch: u16) -> Self {
177 Self {
178 major,
179 minor,
180 patch,
181 release: None,
182 }
183 }
184
185 /// Creates a new kernel version with a release string.
186 ///
187 /// This is a convenience method for creating a kernel version with
188 /// a release/build string. For versions without a release string,
189 /// use [`KernelVersion::new`].
190 ///
191 /// # Examples
192 ///
193 /// ```rust
194 /// use bare_types::sys::KernelVersion;
195 ///
196 /// let version = KernelVersion::with_release(6, 8, 0, "40-generic");
197 /// assert_eq!(version.release(), Some("40-generic"));
198 /// ```
199 #[must_use]
200 pub fn with_release(major: u16, minor: u16, patch: u16, release: &str) -> Self {
201 let mut rel_str: heapless::String<64> = heapless::String::new();
202 // Ignore error, truncation is acceptable for release strings
203 let _ = rel_str.push_str(release);
204
205 Self {
206 major,
207 minor,
208 patch,
209 release: Some(rel_str),
210 }
211 }
212
213 /// Returns the major version number.
214 ///
215 /// # Examples
216 ///
217 /// ```rust
218 /// use bare_types::sys::KernelVersion;
219 ///
220 /// let version = KernelVersion::new(6, 8, 0);
221 /// assert_eq!(version.major(), 6);
222 /// ```
223 #[must_use]
224 #[inline]
225 pub const fn major(&self) -> u16 {
226 self.major
227 }
228
229 /// Returns the minor version number.
230 ///
231 /// # Examples
232 ///
233 /// ```rust
234 /// use bare_types::sys::KernelVersion;
235 ///
236 /// let version = KernelVersion::new(6, 8, 0);
237 /// assert_eq!(version.minor(), 8);
238 /// ```
239 #[must_use]
240 #[inline]
241 pub const fn minor(&self) -> u16 {
242 self.minor
243 }
244
245 /// Returns the patch version number.
246 ///
247 /// # Examples
248 ///
249 /// ```rust
250 /// use bare_types::sys::KernelVersion;
251 ///
252 /// let version = KernelVersion::new(6, 8, 0);
253 /// assert_eq!(version.patch(), 0);
254 /// ```
255 #[must_use]
256 #[inline]
257 pub const fn patch(&self) -> u16 {
258 self.patch
259 }
260
261 /// Returns the optional release string.
262 ///
263 /// # Examples
264 ///
265 /// ```rust
266 /// use bare_types::sys::KernelVersion;
267 ///
268 /// let version = KernelVersion::with_release(6, 8, 0, "40-generic");
269 /// assert_eq!(version.release(), Some("40-generic"));
270 ///
271 /// let version = KernelVersion::new(6, 8, 0);
272 /// assert_eq!(version.release(), None);
273 /// ```
274 #[must_use]
275 #[inline]
276 pub fn release(&self) -> Option<&str> {
277 self.release.as_deref()
278 }
279
280 /// Returns `true` if this is a development/pre-release kernel.
281 ///
282 /// A kernel is considered a development kernel if the minor version
283 /// is odd (Linux convention: odd minor = development, even = stable).
284 ///
285 /// # Examples
286 ///
287 /// ```rust
288 /// use bare_types::sys::KernelVersion;
289 ///
290 /// // Development kernels have odd minor versions
291 /// assert!(KernelVersion::new(6, 7, 0).is_development());
292 ///
293 /// // Stable kernels have even minor versions
294 /// assert!(!KernelVersion::new(6, 8, 0).is_development());
295 /// ```
296 #[must_use]
297 pub const fn is_development(&self) -> bool {
298 // Linux convention: odd minor = development, even = stable
299 self.minor % 2 == 1
300 }
301
302 /// Returns `true` if this is a stable kernel.
303 ///
304 /// A kernel is considered stable if the minor version is even
305 /// (Linux convention).
306 ///
307 /// # Examples
308 ///
309 /// ```rust
310 /// use bare_types::sys::KernelVersion;
311 ///
312 /// assert!(KernelVersion::new(6, 8, 0).is_stable());
313 /// assert!(!KernelVersion::new(6, 7, 0).is_stable());
314 /// ```
315 #[must_use]
316 pub const fn is_stable(&self) -> bool {
317 !self.is_development()
318 }
319
320 /// Returns `true` if this is a long-term support (LTS) kernel.
321 ///
322 /// This checks if the release string contains "lts" (case-insensitive).
323 /// Note: This is a heuristic and may not be accurate for all distros.
324 ///
325 /// # Examples
326 ///
327 /// ```rust
328 /// use bare_types::sys::KernelVersion;
329 ///
330 /// let lts = KernelVersion::with_release(6, 1, 0, "lts");
331 /// assert!(lts.is_lts());
332 ///
333 /// let regular = KernelVersion::new(6, 8, 0);
334 /// assert!(!regular.is_lts());
335 /// ```
336 #[must_use]
337 #[inline]
338 pub fn is_lts(&self) -> bool {
339 self.release
340 .as_ref()
341 .is_some_and(|r| r.to_lowercase().contains("lts"))
342 }
343
344 /// Returns a tuple of (major, minor, patch) for comparison.
345 ///
346 /// # Examples
347 ///
348 /// ```rust
349 /// use bare_types::sys::KernelVersion;
350 ///
351 /// let version = KernelVersion::new(6, 8, 0);
352 /// assert_eq!(version.as_tuple(), (6, 8, 0));
353 /// ```
354 #[must_use]
355 pub const fn as_tuple(&self) -> (u16, u16, u16) {
356 (self.major, self.minor, self.patch)
357 }
358
359 /// Returns a new version with the patch level incremented.
360 ///
361 /// # Examples
362 ///
363 /// ```rust
364 /// use bare_types::sys::KernelVersion;
365 ///
366 /// let version = KernelVersion::new(6, 8, 0);
367 /// let bumped = version.bump_patch();
368 /// assert_eq!(bumped.patch(), 1);
369 /// ```
370 #[must_use]
371 pub const fn bump_patch(&self) -> Self {
372 Self::new(self.major, self.minor, self.patch.saturating_add(1))
373 }
374
375 /// Returns a new version with the minor version incremented and patch reset.
376 ///
377 /// # Examples
378 ///
379 /// ```rust
380 /// use bare_types::sys::KernelVersion;
381 ///
382 /// let version = KernelVersion::new(6, 8, 5);
383 /// let bumped = version.bump_minor();
384 /// assert_eq!(bumped.minor(), 9);
385 /// assert_eq!(bumped.patch(), 0);
386 /// ```
387 #[must_use]
388 pub const fn bump_minor(&self) -> Self {
389 Self::new(self.major, self.minor.saturating_add(1), 0)
390 }
391
392 /// Returns a new version with the major version incremented and others reset.
393 ///
394 /// # Examples
395 ///
396 /// ```rust
397 /// use bare_types::sys::KernelVersion;
398 ///
399 /// let version = KernelVersion::new(6, 8, 5);
400 /// let bumped = version.bump_major();
401 /// assert_eq!(bumped.major(), 7);
402 /// assert_eq!(bumped.minor(), 0);
403 /// assert_eq!(bumped.patch(), 0);
404 /// ```
405 #[must_use]
406 pub const fn bump_major(&self) -> Self {
407 Self::new(self.major.saturating_add(1), 0, 0)
408 }
409
410 /// Returns a version without the release string.
411 ///
412 /// # Examples
413 ///
414 /// ```rust
415 /// use bare_types::sys::KernelVersion;
416 ///
417 /// let version = KernelVersion::with_release(6, 8, 0, "40-generic");
418 /// let without = version.without_release();
419 /// assert_eq!(without.release(), None);
420 /// assert_eq!(without.major(), 6);
421 /// ```
422 #[must_use]
423 pub const fn without_release(&self) -> Self {
424 Self::new(self.major, self.minor, self.patch)
425 }
426}
427
428impl FromStr for KernelVersion {
429 type Err = KernelVersionError;
430
431 fn from_str(s: &str) -> Result<Self, Self::Err> {
432 if s.is_empty() {
433 return Err(KernelVersionError::Empty);
434 }
435
436 // Split on '-' to separate version from release
437 let (version_part, release_part) = s
438 .find('-')
439 .map_or((s, None), |idx| (&s[..idx], Some(&s[idx + 1..])));
440
441 let parts: Vec<&str> = version_part.split('.').collect();
442
443 if parts.len() > 3 {
444 return Err(KernelVersionError::TooManyComponents);
445 }
446
447 if parts.len() < 2 {
448 return Err(KernelVersionError::NotEnoughComponents);
449 }
450
451 let major = parts[0]
452 .parse::<u16>()
453 .map_err(|_| KernelVersionError::InvalidMajor)?;
454
455 let minor = parts[1]
456 .parse::<u16>()
457 .map_err(|_| KernelVersionError::InvalidMinor)?;
458
459 let patch = if parts.len() > 2 {
460 parts[2]
461 .parse::<u16>()
462 .map_err(|_| KernelVersionError::InvalidPatch)?
463 } else {
464 0
465 };
466
467 let release = release_part.map(|r| {
468 let mut rel_str: heapless::String<64> = heapless::String::new();
469 // Truncate if necessary, ignoring errors
470 let _ = rel_str.push_str(r);
471 rel_str
472 });
473
474 Ok(Self {
475 major,
476 minor,
477 patch,
478 release,
479 })
480 }
481}
482
483impl TryFrom<&str> for KernelVersion {
484 type Error = KernelVersionError;
485
486 fn try_from(s: &str) -> Result<Self, Self::Error> {
487 s.parse()
488 }
489}
490
491impl fmt::Display for KernelVersion {
492 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
493 write!(f, "{}.{}.{}", self.major, self.minor, self.patch)?;
494 if let Some(release) = &self.release {
495 write!(f, "-{release}")?;
496 }
497 Ok(())
498 }
499}
500
501#[cfg(feature = "arbitrary")]
502impl<'a> arbitrary::Arbitrary<'a> for KernelVersion {
503 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
504 let major = u16::arbitrary(u)?;
505 let minor = u16::arbitrary(u)?;
506 let patch = u16::arbitrary(u)?;
507 let has_release = bool::arbitrary(u)?;
508
509 let release = if has_release {
510 // Generate a limited release string to avoid excessive allocations
511 let len = u.int_in_range(0..=32usize)?;
512 let mut s: heapless::String<64> = heapless::String::new();
513 for _ in 0..len {
514 let ch: char = char::arbitrary(u)?;
515 if ch.is_ascii_alphanumeric() || ch == '-' || ch == '.' || ch == '_' {
516 let _ = s.push(ch);
517 }
518 }
519 Some(s)
520 } else {
521 None
522 };
523
524 Ok(Self {
525 major,
526 minor,
527 patch,
528 release,
529 })
530 }
531}
532
533#[cfg(test)]
534mod tests {
535 use super::*;
536
537 #[test]
538 fn test_new() {
539 let version = KernelVersion::new(6, 8, 0);
540 assert_eq!(version.major(), 6);
541 assert_eq!(version.minor(), 8);
542 assert_eq!(version.patch(), 0);
543 assert_eq!(version.release(), None);
544 }
545
546 #[test]
547 fn test_with_release() {
548 let version = KernelVersion::with_release(6, 8, 0, "40-generic");
549 assert_eq!(version.major(), 6);
550 assert_eq!(version.minor(), 8);
551 assert_eq!(version.patch(), 0);
552 assert_eq!(version.release(), Some("40-generic"));
553 }
554
555 #[test]
556 fn test_is_development() {
557 // Odd minor = development
558 assert!(KernelVersion::new(6, 7, 0).is_development());
559 assert!(KernelVersion::new(6, 9, 0).is_development());
560
561 // Even minor = stable
562 assert!(!KernelVersion::new(6, 8, 0).is_development());
563 assert!(!KernelVersion::new(6, 10, 0).is_development());
564 }
565
566 #[test]
567 fn test_is_stable() {
568 assert!(KernelVersion::new(6, 8, 0).is_stable());
569 assert!(!KernelVersion::new(6, 7, 0).is_stable());
570 }
571
572 #[test]
573 fn test_is_lts() {
574 let lts = KernelVersion::with_release(6, 1, 0, "lts");
575 assert!(lts.is_lts());
576
577 let lts_upper = KernelVersion::with_release(6, 1, 0, "LTS");
578 assert!(lts_upper.is_lts());
579
580 let regular = KernelVersion::new(6, 8, 0);
581 assert!(!regular.is_lts());
582
583 let generic = KernelVersion::with_release(6, 8, 0, "40-generic");
584 assert!(!generic.is_lts());
585 }
586
587 #[test]
588 fn test_as_tuple() {
589 let version = KernelVersion::new(6, 8, 5);
590 assert_eq!(version.as_tuple(), (6, 8, 5));
591 }
592
593 #[test]
594 fn test_bump_patch() {
595 let version = KernelVersion::new(6, 8, 0);
596 let bumped = version.bump_patch();
597 assert_eq!(bumped.patch(), 1);
598 assert_eq!(bumped.major(), 6);
599 assert_eq!(bumped.minor(), 8);
600 }
601
602 #[test]
603 fn test_bump_minor() {
604 let version = KernelVersion::new(6, 8, 5);
605 let bumped = version.bump_minor();
606 assert_eq!(bumped.major(), 6);
607 assert_eq!(bumped.minor(), 9);
608 assert_eq!(bumped.patch(), 0);
609 }
610
611 #[test]
612 fn test_bump_major() {
613 let version = KernelVersion::new(6, 8, 5);
614 let bumped = version.bump_major();
615 assert_eq!(bumped.major(), 7);
616 assert_eq!(bumped.minor(), 0);
617 assert_eq!(bumped.patch(), 0);
618 }
619
620 #[test]
621 fn test_without_release() {
622 let version = KernelVersion::with_release(6, 8, 0, "40-generic");
623 let without = version.without_release();
624 assert_eq!(without.release(), None);
625 assert_eq!(without.major(), 6);
626 assert_eq!(without.minor(), 8);
627 assert_eq!(without.patch(), 0);
628 }
629
630 #[test]
631 fn test_from_str_simple() {
632 let version: KernelVersion = "6.8.0".parse().unwrap();
633 assert_eq!(version.major(), 6);
634 assert_eq!(version.minor(), 8);
635 assert_eq!(version.patch(), 0);
636 assert_eq!(version.release(), None);
637 }
638
639 #[test]
640 fn test_from_str_with_release() {
641 let version: KernelVersion = "6.8.0-40-generic".parse().unwrap();
642 assert_eq!(version.major(), 6);
643 assert_eq!(version.minor(), 8);
644 assert_eq!(version.patch(), 0);
645 assert_eq!(version.release(), Some("40-generic"));
646 }
647
648 #[test]
649 fn test_from_str_two_components() {
650 let version: KernelVersion = "6.8".parse().unwrap();
651 assert_eq!(version.major(), 6);
652 assert_eq!(version.minor(), 8);
653 assert_eq!(version.patch(), 0);
654 }
655
656 #[test]
657 fn test_from_str_long_release() {
658 let version: KernelVersion = "5.15.0-1052-aws".parse().unwrap();
659 assert_eq!(version.major(), 5);
660 assert_eq!(version.minor(), 15);
661 assert_eq!(version.patch(), 0);
662 assert_eq!(version.release(), Some("1052-aws"));
663 }
664
665 #[test]
666 fn test_from_str_errors() {
667 // Empty
668 assert!(matches!(
669 "".parse::<KernelVersion>(),
670 Err(KernelVersionError::Empty)
671 ));
672
673 // Too many numeric components
674 assert!(matches!(
675 "1.2.3.4".parse::<KernelVersion>(),
676 Err(KernelVersionError::TooManyComponents)
677 ));
678
679 // Not enough components
680 assert!(matches!(
681 "6".parse::<KernelVersion>(),
682 Err(KernelVersionError::NotEnoughComponents)
683 ));
684
685 // Invalid numbers
686 assert!("abc.def".parse::<KernelVersion>().is_err());
687 assert!("6.abc".parse::<KernelVersion>().is_err());
688 assert!("6.8.abc".parse::<KernelVersion>().is_err());
689 }
690
691 #[test]
692 fn test_display() {
693 let version = KernelVersion::new(6, 8, 0);
694 assert_eq!(format!("{}", version), "6.8.0");
695
696 let version = KernelVersion::with_release(6, 8, 0, "40-generic");
697 assert_eq!(format!("{}", version), "6.8.0-40-generic");
698 }
699
700 #[test]
701 fn test_equality() {
702 let v1 = KernelVersion::new(6, 8, 0);
703 let v2 = KernelVersion::new(6, 8, 0);
704 let v3 = KernelVersion::with_release(6, 8, 0, "40-generic");
705
706 assert_eq!(v1, v2);
707 assert_ne!(v1, v3);
708 }
709
710 #[test]
711 fn test_ordering() {
712 let v1 = KernelVersion::new(6, 8, 0);
713 let v2 = KernelVersion::new(6, 8, 1);
714 let v3 = KernelVersion::new(6, 9, 0);
715 let v4 = KernelVersion::new(7, 0, 0);
716
717 assert!(v1 < v2);
718 assert!(v2 < v3);
719 assert!(v3 < v4);
720
721 // Release string affects ordering because KernelVersion derives PartialOrd
722 // Two versions with different release strings are not equal
723 let with_release = KernelVersion::with_release(6, 8, 0, "40-generic");
724 let without_release = KernelVersion::new(6, 8, 0);
725 // Version numbers are the same, but release strings differ
726 assert_ne!(with_release, without_release);
727 }
728
729 #[test]
730 fn test_clone() {
731 let version = KernelVersion::with_release(6, 8, 0, "40-generic");
732 let version2 = version.clone();
733 assert_eq!(version, version2);
734 }
735
736 #[test]
737 fn test_common_kernel_versions() {
738 // Linux kernel versions
739 let linux_6_8: KernelVersion = "6.8.0".parse().unwrap();
740 assert_eq!(linux_6_8.major(), 6);
741 assert!(linux_6_8.is_stable());
742
743 let linux_5_15: KernelVersion = "5.15.0".parse().unwrap();
744 assert_eq!(linux_5_15.major(), 5);
745
746 // Ubuntu kernel
747 let ubuntu: KernelVersion = "6.8.0-40-generic".parse().unwrap();
748 assert_eq!(ubuntu.major(), 6);
749 assert_eq!(ubuntu.release(), Some("40-generic"));
750
751 // AWS kernel
752 let aws: KernelVersion = "5.15.0-1052-aws".parse().unwrap();
753 assert_eq!(aws.major(), 5);
754 assert_eq!(aws.release(), Some("1052-aws"));
755 }
756}