Skip to main content

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}