wslplugins_rs/wsl_version/
semver_impl.rs1use super::WSLVersion;
2use semver::{BuildMetadata, Prerelease, Version};
3use std::num::TryFromIntError;
4use thiserror::Error;
5
6#[derive(Debug, Error, Clone, Copy, PartialEq, Eq, Hash)]
12pub enum SemverConversionError {
13 #[error("semantic versions with pre-release identifiers cannot be converted into WSLVersion")]
16 PrereleaseNotSupported,
17 #[error("semantic versions with build metadata cannot be converted into WSLVersion")]
20 BuildMetadataNotSupported,
21 #[error("semantic version numeric component exceeds u32 range")]
24 ComponentOutOfRange,
25}
26
27impl From<TryFromIntError> for SemverConversionError {
28 #[inline]
29 fn from(_: TryFromIntError) -> Self {
30 Self::ComponentOutOfRange
31 }
32}
33
34impl From<WSLVersion> for Version {
35 #[inline]
36 fn from(value: WSLVersion) -> Self {
37 Self {
38 major: u64::from(value.major()),
39 minor: u64::from(value.minor()),
40 patch: u64::from(value.revision()),
41 pre: Prerelease::EMPTY,
42 build: BuildMetadata::EMPTY,
43 }
44 }
45}
46
47impl TryFrom<Version> for WSLVersion {
48 type Error = SemverConversionError;
49
50 #[inline]
51 fn try_from(value: Version) -> Result<Self, Self::Error> {
52 if !value.pre.is_empty() {
53 return Err(SemverConversionError::PrereleaseNotSupported);
54 }
55
56 if !value.build.is_empty() {
57 return Err(SemverConversionError::BuildMetadataNotSupported);
58 }
59
60 Ok(Self::new(
61 u32::try_from(value.major)?,
62 u32::try_from(value.minor)?,
63 u32::try_from(value.patch)?,
64 ))
65 }
66}
67
68#[cfg(test)]
69mod tests {
70
71 use super::*;
72 use proptest::prelude::*;
73
74 #[test]
75 fn test_try_from_semver_version_rejects_prerelease() {
76 let semver_version = Version {
77 major: 2,
78 minor: 4,
79 patch: 4,
80 #[allow(clippy::unwrap_used, reason = "test data is valid")]
81 pre: "alpha.1".parse().unwrap(),
82 build: BuildMetadata::EMPTY,
83 };
84
85 let result = WSLVersion::try_from(semver_version);
86
87 assert_eq!(result, Err(SemverConversionError::PrereleaseNotSupported));
88 }
89
90 #[test]
91 fn test_try_from_semver_version_rejects_build_metadata() {
92 let semver_version = Version {
93 major: 2,
94 minor: 4,
95 patch: 4,
96 pre: Prerelease::EMPTY,
97 #[allow(clippy::unwrap_used, reason = "test data is valid")]
98 build: "build.1".parse().unwrap(),
99 };
100
101 let result = WSLVersion::try_from(semver_version);
102
103 assert_eq!(
104 result,
105 Err(SemverConversionError::BuildMetadataNotSupported)
106 );
107 }
108
109 #[test]
110 fn test_try_from_semver_version_rejects_out_of_range_component() {
111 let semver_version = Version::new(u64::from(u32::MAX) + 1, 0, 0);
112
113 let result = WSLVersion::try_from(semver_version);
114
115 assert_eq!(result, Err(SemverConversionError::ComponentOutOfRange));
116 }
117
118 proptest! {
119 #[test]
120 fn proptest_into_semver_preserves_components(
121 major in any::<u32>(),
122 minor in any::<u32>(),
123 revision in any::<u32>(),
124 ) {
125 let version = WSLVersion::new(major, minor, revision);
126 let semver_version: Version = version.into();
127
128 prop_assert_eq!(semver_version.major, u64::from(major));
129 prop_assert_eq!(semver_version.minor, u64::from(minor));
130 prop_assert_eq!(semver_version.patch, u64::from(revision));
131 prop_assert_eq!(semver_version.pre, Prerelease::EMPTY);
132 prop_assert_eq!(semver_version.build, BuildMetadata::EMPTY);
133 }
134
135 #[test]
136 fn proptest_try_from_semver_roundtrip(
137 major in any::<u32>(),
138 minor in any::<u32>(),
139 patch in any::<u32>(),
140 ) {
141 let semver_version = Version::new(
142 u64::from(major),
143 u64::from(minor),
144 u64::from(patch),
145 );
146
147 prop_assert_eq!(
148 WSLVersion::try_from(semver_version),
149 Ok(WSLVersion::new(major, minor, patch))
150 );
151 }
152 }
153}