wslplugins_rs/api/errors/require_update_error/
requirement_definition.rs1use crate::{WSLVersion, WSLVersionCapability};
2use std::collections::HashSet;
3use std::fmt::{self, Display};
4use std::hash::{Hash, Hasher};
5use strum::IntoEnumIterator as _;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
13pub enum RequirementDefinition {
14 Version(WSLVersion),
16 Capabilities(HashSet<WSLVersionCapability>),
18}
19
20impl RequirementDefinition {
21 #[must_use]
23 #[inline]
24 pub fn version(&self) -> WSLVersion {
25 match self {
26 Self::Version(version) => *version,
27 Self::Capabilities(capabilities) => capabilities
28 .iter()
29 .copied()
30 .map(WSLVersionCapability::required_version)
31 .max()
32 .unwrap_or(WSLVersion::new(0, 0, 0)),
33 }
34 }
35}
36
37impl From<WSLVersion> for RequirementDefinition {
38 #[inline]
39 fn from(value: WSLVersion) -> Self {
40 Self::Version(value)
41 }
42}
43
44impl From<WSLVersionCapability> for RequirementDefinition {
45 #[inline]
46 fn from(value: WSLVersionCapability) -> Self {
47 Self::Capabilities(HashSet::from([value]))
48 }
49}
50
51impl From<HashSet<WSLVersionCapability>> for RequirementDefinition {
52 #[inline]
53 fn from(value: HashSet<WSLVersionCapability>) -> Self {
54 Self::Capabilities(value)
55 }
56}
57
58impl<const N: usize> From<[WSLVersionCapability; N]> for RequirementDefinition {
59 #[inline]
60 fn from(value: [WSLVersionCapability; N]) -> Self {
61 Self::Capabilities(HashSet::from(value))
62 }
63}
64
65impl FromIterator<WSLVersionCapability> for RequirementDefinition {
66 #[inline]
67 fn from_iter<T: IntoIterator<Item = WSLVersionCapability>>(iter: T) -> Self {
68 Self::Capabilities(iter.into_iter().collect())
69 }
70}
71
72impl Hash for RequirementDefinition {
73 #[inline]
74 fn hash<H: Hasher>(&self, state: &mut H) {
75 match self {
76 Self::Version(version) => {
77 0_u8.hash(state);
78 version.hash(state);
79 }
80 Self::Capabilities(capabilities) => {
81 1_u8.hash(state);
82 for capability in WSLVersionCapability::iter()
83 .filter(|capability| capabilities.contains(capability))
84 {
85 capability.hash(state);
86 }
87 }
88 }
89 }
90}
91
92impl Display for RequirementDefinition {
93 #[inline]
94 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95 match self {
96 Self::Version(version) => write!(f, "version {version}"),
97 Self::Capabilities(capabilities) => {
98 write!(f, "capabilities ")?;
99 for (index, capability) in WSLVersionCapability::iter()
100 .filter(|capability| capabilities.contains(capability))
101 .enumerate()
102 {
103 if index > 0 {
104 write!(f, ", ")?;
105 }
106 write!(f, "{capability}")?;
107 }
108 Ok(())
109 }
110 }
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117 use proptest::prelude::*;
118 use proptest::sample::select;
119
120 fn arb_wsl_version() -> impl Strategy<Value = WSLVersion> {
121 (any::<u32>(), any::<u32>(), any::<u32>())
122 .prop_map(|(major, minor, revision)| WSLVersion::new(major, minor, revision))
123 }
124
125 fn arb_capability() -> impl Strategy<Value = WSLVersionCapability> {
126 select(WSLVersionCapability::iter().collect::<Vec<_>>())
127 }
128
129 #[test]
130 fn version_requirement_returns_explicit_version() {
131 let version = WSLVersion::new(2, 1, 2);
132 let requirement = RequirementDefinition::from(version);
133
134 assert_eq!(requirement.version(), version);
135 }
136
137 #[test]
138 fn capability_requirement_returns_highest_required_version() {
139 let requirement = RequirementDefinition::from([
140 WSLVersionCapability::DistributionInitPid,
141 WSLVersionCapability::DistributionVersion,
142 WSLVersionCapability::DistributionVersion,
143 ]);
144
145 assert_eq!(requirement.version(), WSLVersion::new(2, 4, 4));
146 assert_eq!(
147 requirement,
148 RequirementDefinition::Capabilities(HashSet::from([
149 WSLVersionCapability::DistributionInitPid,
150 WSLVersionCapability::DistributionVersion,
151 ]))
152 );
153 }
154
155 proptest! {
156 #[test]
157 fn version_requirement_always_returns_explicit_version(version in arb_wsl_version()) {
158 let requirement = RequirementDefinition::from(version);
159
160 prop_assert_eq!(requirement.version(), version);
161 }
162
163 #[test]
164 fn capability_requirement_returns_maximum_required_version(
165 capabilities in proptest::collection::hash_set(arb_capability(), 0..=6),
166 ) {
167 let expected = capabilities
168 .iter()
169 .copied()
170 .map(WSLVersionCapability::required_version)
171 .max()
172 .unwrap_or(WSLVersion::new(0, 0, 0));
173 let requirement = RequirementDefinition::from(capabilities);
174
175 prop_assert_eq!(requirement.version(), expected);
176 }
177 }
178}