Skip to content

Commit 6a794b9

Browse files
authored
feat: add iter methods for Labels and Annotations (#720)
* use Vec instead of BTreeMap for KeyValuePairs * rustfmt hangovers * upgrade rust to 1.75.0 * add iterator methods for KeyValuePairs * add iterator methods for Labels and Annotations * impl PartialEq<AsRef<str>> for KeyPrefix * impl PartialEq<AsRef<str>> for KeyName * delegate iter/into_iter for Labels and Annotations * update changelog * ci: update rust toolchain version * impl IntoIterator for KeyValuePairs, Labels and Attributes (instead of custom into_iter() method) * fix unrelated clippy warnings https://rust-lang.github.io/rust-clippy/master/index.html#/get_first * update changelog * Revert "use Vec instead of BTreeMap for KeyValuePairs" This reverts commit 3b1ea5a. * remove BTreeSet -> Vec from changelog * docs: a note about why BTreeSet is used * fix IntoIterator for BTreeSet * use a real label prefix in tests * add test for KeyName equality with AsRef<str>
1 parent 3e002b4 commit 6a794b9

File tree

10 files changed

+106
-9
lines changed

10 files changed

+106
-9
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ env:
1717
CARGO_TERM_COLOR: always
1818
CARGO_INCREMENTAL: '0'
1919
CARGO_PROFILE_DEV_DEBUG: '0'
20-
RUST_TOOLCHAIN_VERSION: "1.71.0"
20+
RUST_TOOLCHAIN_VERSION: "1.75.0"
2121
RUSTFLAGS: "-D warnings"
2222
RUSTDOCFLAGS: "-D warnings"
2323
RUST_LOG: "info"

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,18 @@ All notable changes to this project will be documented in this file.
77
### Added
88

99
- Added `Option::as_ref_or_else` to `utils` ([#717]).
10+
- Add `iter()` methods to `KeyValuePairs<T>`, and delegate iter() for `Labels`, and `Annotations` ([#720]).
11+
- Implement `IntoIterator` for `KeyValuePairs<T>`, `Labels` and `Annotations` ([#720]).
1012
- Added `ListenerOperatorVolumeSourceBuilder::build_pvc` ([#719]).
1113

1214
### Changed
1315

1416
- Split `utils` into submodules ([#717]).
17+
- Bump rust to 1.75.0 ([#720]).
1518
- Renamed `ListenerOperatorVolumeSourceBuilder::build` to `::build_ephemeral` ([#719]).
1619

1720
[#717]: https://github.com/stackabletech/operator-rs/pull/717
21+
[#720]: https://github.com/stackabletech/operator-rs/pull/720
1822
[#719]: https://github.com/stackabletech/operator-rs/pull/719
1923

2024
## [0.61.0] - 2024-01-15

rust-toolchain.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
[toolchain]
2-
channel = "1.71.0"
2+
channel = "1.75.0"

src/builder/meta.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ mod tests {
343343
assert_eq!(meta.name, Some("foo".to_string()));
344344
assert_eq!(meta.owner_references.as_ref().unwrap().len(), 1);
345345
assert!(
346-
matches!(meta.owner_references.unwrap().get(0), Some(OwnerReference { uid, ..}) if uid == "uid")
346+
matches!(meta.owner_references.unwrap().first(), Some(OwnerReference { uid, ..}) if uid == "uid")
347347
);
348348
assert_eq!(meta.annotations.as_ref().unwrap().len(), 1);
349349
assert_eq!(

src/builder/pod/container.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ mod tests {
392392

393393
assert_eq!(container.name, "testcontainer");
394394
assert!(
395-
matches!(container.env.as_ref().unwrap().get(0), Some(EnvVar {name, value: Some(value), ..}) if name == "foo" && value == "bar")
395+
matches!(container.env.as_ref().unwrap().first(), Some(EnvVar {name, value: Some(value), ..}) if name == "foo" && value == "bar")
396396
);
397397
assert!(
398398
matches!(container.env.as_ref().unwrap().get(1), Some(EnvVar {name, value_from: Some(EnvVarSource {config_map_key_ref: Some(ConfigMapKeySelector {name: Some(config_map_name), key: config_map_key, ..}), ..}), ..}) if name == "envFromConfigMap" && config_map_name == "my-configmap" && config_map_key == "my-key")
@@ -402,7 +402,7 @@ mod tests {
402402
);
403403
assert_eq!(container.volume_mounts.as_ref().unwrap().len(), 1);
404404
assert!(
405-
matches!(container.volume_mounts.as_ref().unwrap().get(0), Some(VolumeMount {mount_path, name, ..}) if mount_path == "/mount" && name == "configmap")
405+
matches!(container.volume_mounts.as_ref().unwrap().first(), Some(VolumeMount {mount_path, name, ..}) if mount_path == "/mount" && name == "configmap")
406406
);
407407
assert_eq!(container.ports.as_ref().unwrap().len(), 2);
408408
assert_eq!(

src/builder/pod/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -691,13 +691,13 @@ mod tests {
691691
pod_spec
692692
.init_containers
693693
.as_ref()
694-
.and_then(|containers| containers.get(0).as_ref().map(|c| c.name.clone())),
694+
.and_then(|containers| containers.first().as_ref().map(|c| c.name.clone())),
695695
Some("init-containername".to_string())
696696
);
697697

698698
assert_eq!(
699699
pod_spec.volumes.as_ref().and_then(|volumes| volumes
700-
.get(0)
700+
.first()
701701
.as_ref()
702702
.and_then(|volume| volume.config_map.as_ref()?.name.clone())),
703703
Some("configmap".to_string())

src/kvp/annotation/mod.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,10 +287,25 @@ impl Annotations {
287287
/// provided `key`. Failure to parse/validate the [`Key`] will
288288
/// return `false`.
289289
pub fn contains_key(&self, key: impl TryInto<Key>) -> bool;
290+
291+
/// Returns an [`Iterator`] over [`Annotations`] yielding a reference to every [`Annotation`] contained within.
292+
pub fn iter(&self) -> impl Iterator<Item = &KeyValuePair<AnnotationValue>>;
293+
290294
}
291295
}
292296
}
293297

298+
impl IntoIterator for Annotations {
299+
type Item = KeyValuePair<AnnotationValue>;
300+
type IntoIter = std::collections::btree_set::IntoIter<Self::Item>;
301+
302+
/// Returns a consuming [`Iterator`] over [`Annotations`] moving every [`Annotation`] out.
303+
/// The [`Annotations`] cannot be used again after calling this.
304+
fn into_iter(self) -> Self::IntoIter {
305+
self.0.into_iter()
306+
}
307+
}
308+
294309
#[cfg(test)]
295310
mod test {
296311
use super::*;

src/kvp/key.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,15 @@ impl Display for KeyPrefix {
242242
}
243243
}
244244

245+
impl<T> PartialEq<T> for KeyPrefix
246+
where
247+
T: AsRef<str>,
248+
{
249+
fn eq(&self, other: &T) -> bool {
250+
self.as_str() == other.as_ref()
251+
}
252+
}
253+
245254
/// The error type for key name parsing/validation operations.
246255
#[derive(Debug, PartialEq, Snafu)]
247256
pub enum KeyNameError {
@@ -315,6 +324,15 @@ impl Display for KeyName {
315324
}
316325
}
317326

327+
impl<T> PartialEq<T> for KeyName
328+
where
329+
T: AsRef<str>,
330+
{
331+
fn eq(&self, other: &T) -> bool {
332+
self.as_str() == other.as_ref()
333+
}
334+
}
335+
318336
#[cfg(test)]
319337
mod test {
320338
use super::*;
@@ -338,6 +356,22 @@ mod test {
338356
assert_eq!(key.to_string(), "vendor");
339357
}
340358

359+
#[test]
360+
fn prefix_equality() {
361+
const EXAMPLE_PREFIX_STR: &str = "stackable.tech";
362+
363+
let example_prefix = KeyPrefix::from_str(EXAMPLE_PREFIX_STR).expect("valid test prefix");
364+
assert!(example_prefix == EXAMPLE_PREFIX_STR);
365+
}
366+
367+
#[test]
368+
fn name_equality() {
369+
const EXAMPLE_NAME_STR: &str = "managed-by";
370+
371+
let example_name = KeyName::from_str(EXAMPLE_NAME_STR).expect("valid test name");
372+
assert!(example_name == EXAMPLE_NAME_STR);
373+
}
374+
341375
#[rstest]
342376
#[case("foo/bar/baz", KeyError::NestedPrefix)]
343377
#[case("", KeyError::EmptyInput)]

src/kvp/label/mod.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,10 +374,25 @@ impl Labels {
374374
/// Returns if the set of labels contains a label with the provided `key`.
375375
/// Failure to parse/validate the [`Key`] will return `false`.
376376
pub fn contains_key(&self, key: impl TryInto<Key>) -> bool;
377+
378+
/// Returns an [`Iterator`] over [`Labels`] yielding a reference to every [`Label`] contained within.
379+
pub fn iter(&self) -> impl Iterator<Item = &KeyValuePair<LabelValue>>;
380+
377381
}
378382
}
379383
}
380384

385+
impl IntoIterator for Labels {
386+
type Item = KeyValuePair<LabelValue>;
387+
type IntoIter = std::collections::btree_set::IntoIter<Self::Item>;
388+
389+
/// Returns a consuming [`Iterator`] over [`Labels`] moving every [`Label`] out.
390+
/// The [`Labels`] cannot be used again after calling this.
391+
fn into_iter(self) -> Self::IntoIter {
392+
self.0.into_iter()
393+
}
394+
}
395+
381396
#[cfg(test)]
382397
mod test {
383398
use super::*;

src/kvp/mod.rs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,12 @@ pub enum KeyValuePairsError {
161161
/// - `From<KeyValuePairs<T>> for BTreeMap<String, String>`
162162
///
163163
/// See [`Labels`] and [`Annotations`] on how these traits can be used.
164+
///
165+
/// # Note
166+
///
167+
/// A [`BTreeSet`] is used as the inner collection to preserve order of items
168+
/// which ultimately prevent unncessary reconciliations due to changes
169+
/// in item order.
164170
#[derive(Clone, Debug, Default)]
165171
pub struct KeyValuePairs<T: Value>(BTreeSet<KeyValuePair<T>>);
166172

@@ -295,13 +301,17 @@ where
295301

296302
/// Returns if the list contains a specific [`KeyValuePair`].
297303
pub fn contains(&self, kvp: impl TryInto<KeyValuePair<T>>) -> bool {
298-
let Ok(kvp) = kvp.try_into() else {return false};
304+
let Ok(kvp) = kvp.try_into() else {
305+
return false;
306+
};
299307
self.0.contains(&kvp)
300308
}
301309

302310
/// Returns if the list contains a key/value pair with a specific [`Key`].
303311
pub fn contains_key(&self, key: impl TryInto<Key>) -> bool {
304-
let Ok(key) = key.try_into() else {return false};
312+
let Ok(key) = key.try_into() else {
313+
return false;
314+
};
305315

306316
for kvp in &self.0 {
307317
if kvp.key == key {
@@ -311,6 +321,25 @@ where
311321

312322
false
313323
}
324+
325+
/// Returns an [`Iterator`] over [`KeyValuePairs`] yielding a reference to every [`KeyValuePair`] contained within.
326+
pub fn iter(&self) -> impl Iterator<Item = &KeyValuePair<T>> {
327+
self.0.iter()
328+
}
329+
}
330+
331+
impl<T> IntoIterator for KeyValuePairs<T>
332+
where
333+
T: Value,
334+
{
335+
type Item = KeyValuePair<T>;
336+
type IntoIter = std::collections::btree_set::IntoIter<Self::Item>;
337+
338+
/// Returns a consuming [`Iterator`] over [`KeyValuePairs`] moving every [`KeyValuePair`] out.
339+
/// The [`KeyValuePairs`] cannot be used again after calling this.
340+
fn into_iter(self) -> Self::IntoIter {
341+
self.0.into_iter()
342+
}
314343
}
315344

316345
/// A recommended set of labels to set on objects created by Stackable

0 commit comments

Comments
 (0)