1use {
25 crate::{UnstableFeatureVersions, supported_feature_range},
26 krabka_metadata::metadata_version::{
27 KRAFT_VERSION_FEATURE, METADATA_VERSION_FEATURE, METADATA_VERSION_MIN,
28 },
29 krabka_protocol::owned::api_versions_response::{FinalizedFeatureKey, SupportedFeatureKey},
30};
31
32const ZERO_MINIMUM_API_VERSION: i16 = 4;
35
36#[must_use]
39pub fn supported_feature_key(
40 name: &str,
41 min_version: i16,
42 max_version: i16,
43 api_version: i16,
44) -> Option<SupportedFeatureKey> {
45 let unregistrable = max_version == 0;
46 let alter_level_zero = api_version < ZERO_MINIMUM_API_VERSION && min_version == 0;
47 (!unregistrable && !alter_level_zero).then(|| SupportedFeatureKey {
48 name: name.to_owned(),
49 min_version,
50 max_version,
51 ..Default::default()
52 })
53}
54
55#[must_use]
59pub fn supported_feature_keys(
60 api_version: i16,
61 unstable: UnstableFeatureVersions,
62) -> Vec<SupportedFeatureKey> {
63 krabka_metadata::feature_registry()
64 .iter()
65 .filter_map(|feature| {
66 let (min_version, max_version) = supported_feature_range(*feature, unstable);
67 supported_feature_key(feature.name(), min_version, max_version, api_version)
68 })
69 .collect()
70}
71
72#[must_use]
76pub fn finalized_feature_keys(image: &krabka_metadata::MetadataImage) -> Vec<FinalizedFeatureKey> {
77 let mut levels = image.finalized_features().clone();
78 levels
82 .entry(METADATA_VERSION_FEATURE.to_owned())
83 .or_insert(METADATA_VERSION_MIN);
84 levels.insert(
85 KRAFT_VERSION_FEATURE.to_owned(),
86 i16::try_from(image.kraft_version()).unwrap_or(i16::MAX),
87 );
88 levels
89 .into_iter()
90 .filter(|(_, level)| *level != 0)
91 .map(|(name, level)| FinalizedFeatureKey {
92 name,
93 max_version_level: level,
95 min_version_level: level,
96 ..Default::default()
97 })
98 .collect()
99}
100
101#[cfg(test)]
102mod tests {
103 use {
104 super::*,
105 assert2::check,
106 krabka_metadata::metadata_version::{METADATA_VERSION_MAX, METADATA_VERSION_MIN},
107 };
108
109 krabka_macros::supported_feature_fixture!(supported);
110 krabka_macros::supported_features_fixture!(modern_supported, supported);
111
112 krabka_macros::finalized_feature_fixture!(finalized);
113
114 use crate::test_support::feature_image as image;
115
116 #[yare::parameterized(
122 stable_v0 = {
123 0,
124 crate::UnstableFeatureVersions::Disabled,
125 vec![supported("metadata.version", METADATA_VERSION_MIN, 30)],
126 },
127 stable_v3 = {
128 3,
129 crate::UnstableFeatureVersions::Disabled,
130 vec![supported("metadata.version", METADATA_VERSION_MIN, 30)],
131 },
132 stable_v4 = {
133 4,
134 crate::UnstableFeatureVersions::Disabled,
135 modern_supported(supported("metadata.version", METADATA_VERSION_MIN, 30), 1),
136 },
137 stable_v5 = {
138 5,
139 crate::UnstableFeatureVersions::Disabled,
140 modern_supported(supported("metadata.version", METADATA_VERSION_MIN, 30), 1),
141 },
142 unstable_v0 = {
143 0,
144 crate::UnstableFeatureVersions::Enabled,
145 vec![supported("metadata.version", METADATA_VERSION_MIN, METADATA_VERSION_MAX)],
146 },
147 unstable_v3 = {
148 3,
149 crate::UnstableFeatureVersions::Enabled,
150 vec![supported("metadata.version", METADATA_VERSION_MIN, METADATA_VERSION_MAX)],
151 },
152 unstable_v4 = {
153 4,
154 crate::UnstableFeatureVersions::Enabled,
155 modern_supported(supported("metadata.version", METADATA_VERSION_MIN, METADATA_VERSION_MAX), 2),
156 },
157 unstable_v5 = {
158 5,
159 crate::UnstableFeatureVersions::Enabled,
160 modern_supported(supported("metadata.version", METADATA_VERSION_MIN, METADATA_VERSION_MAX), 2),
161 },
162 )]
163 fn supported_features_follow_kafkas_minimums_and_alter_level_zero(
164 api_version: i16,
165 unstable: UnstableFeatureVersions,
166 expected: Vec<SupportedFeatureKey>,
167 ) {
168 check!(
170 supported_feature_keys(api_version, unstable) == expected,
171 "v{api_version} {unstable:?}"
172 );
173 }
174
175 #[yare::parameterized(
178 v0 = { 0 },
179 v3 = { 3 },
180 v4 = { 4 },
181 v5 = { 5 },
182 )]
183 fn a_feature_with_maximum_zero_is_never_advertised(api_version: i16) {
184 check!(supported_feature_key("x.version", 0, 0, api_version) == None);
185 check!(supported_feature_key("x.version", 1, 2, 3) == Some(supported("x.version", 1, 2)));
186 }
187
188 #[yare::parameterized(
190 empty_levels = { 0, &[][..], vec![finalized("metadata.version", METADATA_VERSION_MIN)] },
191 group_level = {
192 0,
193 &[("metadata.version", 25), ("group.version", 1)][..],
194 vec![finalized("group.version", 1), finalized("metadata.version", 25)],
195 },
196 kraft_level = {
197 1,
198 &[("metadata.version", 25)][..],
199 vec![finalized("kraft.version", 1), finalized("metadata.version", 25)],
200 },
201 zero_group_ignored = {
202 0,
203 &[("metadata.version", 25), ("group.version", 1), ("group.version", 0)][..],
204 vec![finalized("metadata.version", 25)],
205 },
206 krabka_version = {
207 1,
208 &[("metadata.version", 30), ("krabka.version", 1)][..],
209 vec![
210 finalized("krabka.version", 1),
211 finalized("kraft.version", 1),
212 finalized("metadata.version", 30),
213 ],
214 },
215 zero_krabka_ignored = {
216 0,
217 &[("metadata.version", 30), ("krabka.version", 0)][..],
218 vec![finalized("metadata.version", 30)],
219 },
220 )]
221 fn finalized_features_omit_level_zero(
222 kraft_version: u16,
223 levels: &[(&str, i16)],
224 expected: Vec<FinalizedFeatureKey>,
225 ) {
226 check!(
227 finalized_feature_keys(&image(kraft_version, levels)) == expected,
228 "kraft.version {kraft_version}, {levels:?}"
229 );
230 }
231}