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 pub fn is_lts(&self) -> bool {
338 self.release
339 .as_ref()
340 .is_some_and(|r| r.to_lowercase().contains("lts"))
341 }
342
343 /// Returns a tuple of (major, minor, patch) for comparison.
344 ///
345 /// # Examples
346 ///
347 /// ```rust
348 /// use bare_types::sys::KernelVersion;
349 ///
350 /// let version = KernelVersion::new(6, 8, 0);
351 /// assert_eq!(version.as_tuple(), (6, 8, 0));
352 /// ```
353 #[must_use]
354 pub const fn as_tuple(&self) -> (u16, u16, u16) {
355 (self.major, self.minor, self.patch)
356 }
357
358 /// Returns a new version with the patch level incremented.
359 ///
360 /// # Examples
361 ///
362 /// ```rust
363 /// use bare_types::sys::KernelVersion;
364 ///
365 /// let version = KernelVersion::new(6, 8, 0);
366 /// let bumped = version.bump_patch();
367 /// assert_eq!(bumped.patch(), 1);
368 /// ```
369 #[must_use]
370 pub const fn bump_patch(&self) -> Self {
371 Self::new(self.major, self.minor, self.patch.saturating_add(1))
372 }
373
374 /// Returns a new version with the minor version incremented and patch reset.
375 ///
376 /// # Examples
377 ///
378 /// ```rust
379 /// use bare_types::sys::KernelVersion;
380 ///
381 /// let version = KernelVersion::new(6, 8, 5);
382 /// let bumped = version.bump_minor();
383 /// assert_eq!(bumped.minor(), 9);
384 /// assert_eq!(bumped.patch(), 0);
385 /// ```
386 #[must_use]
387 pub const fn bump_minor(&self) -> Self {
388 Self::new(self.major, self.minor.saturating_add(1), 0)
389 }
390
391 /// Returns a new version with the major version incremented and others reset.
392 ///
393 /// # Examples
394 ///
395 /// ```rust
396 /// use bare_types::sys::KernelVersion;
397 ///
398 /// let version = KernelVersion::new(6, 8, 5);
399 /// let bumped = version.bump_major();
400 /// assert_eq!(bumped.major(), 7);
401 /// assert_eq!(bumped.minor(), 0);
402 /// assert_eq!(bumped.patch(), 0);
403 /// ```
404 #[must_use]
405 pub const fn bump_major(&self) -> Self {
406 Self::new(self.major.saturating_add(1), 0, 0)
407 }
408
409 /// Returns a version without the release string.
410 ///
411 /// # Examples
412 ///
413 /// ```rust
414 /// use bare_types::sys::KernelVersion;
415 ///
416 /// let version = KernelVersion::with_release(6, 8, 0, "40-generic");
417 /// let without = version.without_release();
418 /// assert_eq!(without.release(), None);
419 /// assert_eq!(without.major(), 6);
420 /// ```
421 #[must_use]
422 pub const fn without_release(&self) -> Self {
423 Self::new(self.major, self.minor, self.patch)
424 }
425}
426
427impl FromStr for KernelVersion {
428 type Err = KernelVersionError;
429
430 fn from_str(s: &str) -> Result<Self, Self::Err> {
431 if s.is_empty() {
432 return Err(KernelVersionError::Empty);
433 }
434
435 // Split on '-' to separate version from release
436 let (version_part, release_part) = s
437 .find('-')
438 .map_or((s, None), |idx| (&s[..idx], Some(&s[idx + 1..])));
439
440 let parts: Vec<&str> = version_part.split('.').collect();
441
442 if parts.len() > 3 {
443 return Err(KernelVersionError::TooManyComponents);
444 }
445
446 if parts.len() < 2 {
447 return Err(KernelVersionError::NotEnoughComponents);
448 }
449
450 let major = parts[0]
451 .parse::<u16>()
452 .map_err(|_| KernelVersionError::InvalidMajor)?;
453
454 let minor = parts[1]
455 .parse::<u16>()
456 .map_err(|_| KernelVersionError::InvalidMinor)?;
457
458 let patch = if parts.len() > 2 {
459 parts[2]
460 .parse::<u16>()
461 .map_err(|_| KernelVersionError::InvalidPatch)?
462 } else {
463 0
464 };
465
466 let release = release_part.map(|r| {
467 let mut rel_str: heapless::String<64> = heapless::String::new();
468 // Truncate if necessary, ignoring errors
469 let _ = rel_str.push_str(r);
470 rel_str
471 });
472
473 Ok(Self {
474 major,
475 minor,
476 patch,
477 release,
478 })
479 }
480}
481
482impl TryFrom<&str> for KernelVersion {
483 type Error = KernelVersionError;
484
485 fn try_from(s: &str) -> Result<Self, Self::Error> {
486 s.parse()
487 }
488}
489
490impl fmt::Display for KernelVersion {
491 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
492 write!(f, "{}.{}.{}", self.major, self.minor, self.patch)?;
493 if let Some(release) = &self.release {
494 write!(f, "-{release}")?;
495 }
496 Ok(())
497 }
498}
499
500#[cfg(feature = "arbitrary")]
501impl<'a> arbitrary::Arbitrary<'a> for KernelVersion {
502 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
503 let major = u16::arbitrary(u)?;
504 let minor = u16::arbitrary(u)?;
505 let patch = u16::arbitrary(u)?;
506 let has_release = bool::arbitrary(u)?;
507
508 let release = if has_release {
509 // Generate a limited release string to avoid excessive allocations
510 let len = u.int_in_range(0..=32usize)?;
511 let mut s: heapless::String<64> = heapless::String::new();
512 for _ in 0..len {
513 let ch: char = char::arbitrary(u)?;
514 if ch.is_ascii_alphanumeric() || ch == '-' || ch == '.' || ch == '_' {
515 let _ = s.push(ch);
516 }
517 }
518 Some(s)
519 } else {
520 None
521 };
522
523 Ok(Self {
524 major,
525 minor,
526 patch,
527 release,
528 })
529 }
530}
531
532#[cfg(test)]
533mod tests {
534 use super::*;
535
536 #[test]
537 fn test_new() {
538 let version = KernelVersion::new(6, 8, 0);
539 assert_eq!(version.major(), 6);
540 assert_eq!(version.minor(), 8);
541 assert_eq!(version.patch(), 0);
542 assert_eq!(version.release(), None);
543 }
544
545 #[test]
546 fn test_with_release() {
547 let version = KernelVersion::with_release(6, 8, 0, "40-generic");
548 assert_eq!(version.major(), 6);
549 assert_eq!(version.minor(), 8);
550 assert_eq!(version.patch(), 0);
551 assert_eq!(version.release(), Some("40-generic"));
552 }
553
554 #[test]
555 fn test_is_development() {
556 // Odd minor = development
557 assert!(KernelVersion::new(6, 7, 0).is_development());
558 assert!(KernelVersion::new(6, 9, 0).is_development());
559
560 // Even minor = stable
561 assert!(!KernelVersion::new(6, 8, 0).is_development());
562 assert!(!KernelVersion::new(6, 10, 0).is_development());
563 }
564
565 #[test]
566 fn test_is_stable() {
567 assert!(KernelVersion::new(6, 8, 0).is_stable());
568 assert!(!KernelVersion::new(6, 7, 0).is_stable());
569 }
570
571 #[test]
572 fn test_is_lts() {
573 let lts = KernelVersion::with_release(6, 1, 0, "lts");
574 assert!(lts.is_lts());
575
576 let lts_upper = KernelVersion::with_release(6, 1, 0, "LTS");
577 assert!(lts_upper.is_lts());
578
579 let regular = KernelVersion::new(6, 8, 0);
580 assert!(!regular.is_lts());
581
582 let generic = KernelVersion::with_release(6, 8, 0, "40-generic");
583 assert!(!generic.is_lts());
584 }
585
586 #[test]
587 fn test_as_tuple() {
588 let version = KernelVersion::new(6, 8, 5);
589 assert_eq!(version.as_tuple(), (6, 8, 5));
590 }
591
592 #[test]
593 fn test_bump_patch() {
594 let version = KernelVersion::new(6, 8, 0);
595 let bumped = version.bump_patch();
596 assert_eq!(bumped.patch(), 1);
597 assert_eq!(bumped.major(), 6);
598 assert_eq!(bumped.minor(), 8);
599 }
600
601 #[test]
602 fn test_bump_minor() {
603 let version = KernelVersion::new(6, 8, 5);
604 let bumped = version.bump_minor();
605 assert_eq!(bumped.major(), 6);
606 assert_eq!(bumped.minor(), 9);
607 assert_eq!(bumped.patch(), 0);
608 }
609
610 #[test]
611 fn test_bump_major() {
612 let version = KernelVersion::new(6, 8, 5);
613 let bumped = version.bump_major();
614 assert_eq!(bumped.major(), 7);
615 assert_eq!(bumped.minor(), 0);
616 assert_eq!(bumped.patch(), 0);
617 }
618
619 #[test]
620 fn test_without_release() {
621 let version = KernelVersion::with_release(6, 8, 0, "40-generic");
622 let without = version.without_release();
623 assert_eq!(without.release(), None);
624 assert_eq!(without.major(), 6);
625 assert_eq!(without.minor(), 8);
626 assert_eq!(without.patch(), 0);
627 }
628
629 #[test]
630 fn test_from_str_simple() {
631 let version: KernelVersion = "6.8.0".parse().unwrap();
632 assert_eq!(version.major(), 6);
633 assert_eq!(version.minor(), 8);
634 assert_eq!(version.patch(), 0);
635 assert_eq!(version.release(), None);
636 }
637
638 #[test]
639 fn test_from_str_with_release() {
640 let version: KernelVersion = "6.8.0-40-generic".parse().unwrap();
641 assert_eq!(version.major(), 6);
642 assert_eq!(version.minor(), 8);
643 assert_eq!(version.patch(), 0);
644 assert_eq!(version.release(), Some("40-generic"));
645 }
646
647 #[test]
648 fn test_from_str_two_components() {
649 let version: KernelVersion = "6.8".parse().unwrap();
650 assert_eq!(version.major(), 6);
651 assert_eq!(version.minor(), 8);
652 assert_eq!(version.patch(), 0);
653 }
654
655 #[test]
656 fn test_from_str_long_release() {
657 let version: KernelVersion = "5.15.0-1052-aws".parse().unwrap();
658 assert_eq!(version.major(), 5);
659 assert_eq!(version.minor(), 15);
660 assert_eq!(version.patch(), 0);
661 assert_eq!(version.release(), Some("1052-aws"));
662 }
663
664 #[test]
665 fn test_from_str_errors() {
666 // Empty
667 assert!(matches!(
668 "".parse::<KernelVersion>(),
669 Err(KernelVersionError::Empty)
670 ));
671
672 // Too many numeric components
673 assert!(matches!(
674 "1.2.3.4".parse::<KernelVersion>(),
675 Err(KernelVersionError::TooManyComponents)
676 ));
677
678 // Not enough components
679 assert!(matches!(
680 "6".parse::<KernelVersion>(),
681 Err(KernelVersionError::NotEnoughComponents)
682 ));
683
684 // Invalid numbers
685 assert!("abc.def".parse::<KernelVersion>().is_err());
686 assert!("6.abc".parse::<KernelVersion>().is_err());
687 assert!("6.8.abc".parse::<KernelVersion>().is_err());
688 }
689
690 #[test]
691 fn test_display() {
692 let version = KernelVersion::new(6, 8, 0);
693 assert_eq!(format!("{}", version), "6.8.0");
694
695 let version = KernelVersion::with_release(6, 8, 0, "40-generic");
696 assert_eq!(format!("{}", version), "6.8.0-40-generic");
697 }
698
699 #[test]
700 fn test_equality() {
701 let v1 = KernelVersion::new(6, 8, 0);
702 let v2 = KernelVersion::new(6, 8, 0);
703 let v3 = KernelVersion::with_release(6, 8, 0, "40-generic");
704
705 assert_eq!(v1, v2);
706 assert_ne!(v1, v3);
707 }
708
709 #[test]
710 fn test_ordering() {
711 let v1 = KernelVersion::new(6, 8, 0);
712 let v2 = KernelVersion::new(6, 8, 1);
713 let v3 = KernelVersion::new(6, 9, 0);
714 let v4 = KernelVersion::new(7, 0, 0);
715
716 assert!(v1 < v2);
717 assert!(v2 < v3);
718 assert!(v3 < v4);
719
720 // Release string affects ordering because KernelVersion derives PartialOrd
721 // Two versions with different release strings are not equal
722 let with_release = KernelVersion::with_release(6, 8, 0, "40-generic");
723 let without_release = KernelVersion::new(6, 8, 0);
724 // Version numbers are the same, but release strings differ
725 assert_ne!(with_release, without_release);
726 }
727
728 #[test]
729 fn test_clone() {
730 let version = KernelVersion::with_release(6, 8, 0, "40-generic");
731 let version2 = version.clone();
732 assert_eq!(version, version2);
733 }
734
735 #[test]
736 fn test_common_kernel_versions() {
737 // Linux kernel versions
738 let linux_6_8: KernelVersion = "6.8.0".parse().unwrap();
739 assert_eq!(linux_6_8.major(), 6);
740 assert!(linux_6_8.is_stable());
741
742 let linux_5_15: KernelVersion = "5.15.0".parse().unwrap();
743 assert_eq!(linux_5_15.major(), 5);
744
745 // Ubuntu kernel
746 let ubuntu: KernelVersion = "6.8.0-40-generic".parse().unwrap();
747 assert_eq!(ubuntu.major(), 6);
748 assert_eq!(ubuntu.release(), Some("40-generic"));
749
750 // AWS kernel
751 let aws: KernelVersion = "5.15.0-1052-aws".parse().unwrap();
752 assert_eq!(aws.major(), 5);
753 assert_eq!(aws.release(), Some("1052-aws"));
754 }
755}