Skip to content

Commit 4f6953b

Browse files
authored
feat: Add TryFromIterator trait and implement it for Labels and Annotations (#715)
* Add `TryFromIterator` trait, add impl for `Labels` and `Annotations` * Update changelog * Add new PR link to changelog * Add examples in doc comments for labels and annotations * Add doc comment for TryFromIterator * Add TryFromIterator tests
1 parent 55ea1ea commit 4f6953b

File tree

5 files changed

+188
-60
lines changed

5 files changed

+188
-60
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,18 @@ All notable changes to this project will be documented in this file.
88

99
- Add `TryFrom<[(K, V); N]>` implementation for `Annotations` and `Labels` ([#711]).
1010
- Add `parse_insert` associated function for `Annotations` and `Labels` ([#711]).
11+
- Add generic types for `TryFrom<BTreeMap<K, V>>` impl ([#714]).
12+
- Add `TryFromIterator` trait, which tries to construct `Self` from an iterator. It is a falliable version of
13+
`FromIterator` ([#715]).
14+
- Add `TryFromIterator` impl for `Labels` and `Annotations` ([#715]).
1115

1216
### Changed
1317

1418
- Adjust `try_insert` for `Annotations` and `Labels` slightly ([#711]).
1519

1620
[#711]: https://github.com/stackabletech/operator-rs/pull/711
21+
[#714]: https://github.com/stackabletech/operator-rs/pull/714
22+
[#715]: https://github.com/stackabletech/operator-rs/pull/715
1723

1824
## [0.60.1] - 2024-01-04
1925

src/iter.rs

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,21 @@ where
4545
}
4646
}
4747

48+
/// This is a fallible version of the std [`FromIterator`] trait.
49+
///
50+
/// The standard [`FromIterator`] trait specifies it must never fail. This trait
51+
/// makes it easier to work with iterators, which can fail during the creation
52+
/// `Self`. It will immediately return an error if processing failed and will
53+
/// not continue to process items.
54+
pub trait TryFromIterator<T>: Sized {
55+
type Error: std::error::Error;
56+
57+
fn try_from_iter<I: IntoIterator<Item = T>>(iter: I) -> Result<Self, Self::Error>;
58+
}
59+
4860
#[cfg(test)]
4961
mod tests {
50-
use crate::iter::try_flatten;
62+
use super::*;
5163

5264
#[test]
5365
fn try_flatten_marble_test() {
@@ -62,4 +74,50 @@ mod tests {
6274
vec![Ok(1), Err(2), Ok(3), Err(4), Err(5), Ok(6)],
6375
);
6476
}
77+
78+
#[test]
79+
fn try_from_iter_success() {
80+
let iter = [1, 2, 3, 4];
81+
82+
#[derive(Debug, PartialEq)]
83+
struct Sum(usize);
84+
85+
impl TryFromIterator<usize> for Sum {
86+
type Error = std::convert::Infallible;
87+
88+
fn try_from_iter<I: IntoIterator<Item = usize>>(iter: I) -> Result<Self, Self::Error> {
89+
let sum = iter.into_iter().sum();
90+
Ok(Sum(sum))
91+
}
92+
}
93+
94+
assert_eq!(Sum(10), Sum::try_from_iter(iter).unwrap());
95+
}
96+
97+
#[test]
98+
fn try_from_iter_error() {
99+
let iter = ["1", "2", "3", "-4"];
100+
101+
#[derive(Debug, PartialEq)]
102+
struct Sum(usize);
103+
104+
impl<T> TryFromIterator<T> for Sum
105+
where
106+
T: AsRef<str>,
107+
{
108+
type Error = std::num::ParseIntError;
109+
110+
fn try_from_iter<I: IntoIterator<Item = T>>(iter: I) -> Result<Self, Self::Error> {
111+
let mut sum = 0;
112+
113+
for item in iter {
114+
sum += item.as_ref().parse::<usize>()?;
115+
}
116+
117+
Ok(Self(sum))
118+
}
119+
}
120+
121+
assert!(Sum::try_from_iter(iter).is_err());
122+
}
65123
}

src/kvp/annotation/mod.rs

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use delegate::delegate;
1919

2020
use crate::{
2121
builder::SecretOperatorVolumeScope,
22+
iter::TryFromIterator,
2223
kvp::{Key, KeyValuePair, KeyValuePairError, KeyValuePairs, KeyValuePairsError},
2324
};
2425

@@ -138,6 +139,31 @@ impl Annotation {
138139
///
139140
/// It provides selected associated functions to manipulate the set of
140141
/// annotations, like inserting or extending.
142+
///
143+
/// ## Examples
144+
///
145+
/// ### Converting a BTreeMap into a list of labels
146+
///
147+
/// ```
148+
/// # use std::collections::BTreeMap;
149+
/// # use stackable_operator::kvp::Annotations;
150+
/// let map = BTreeMap::from([
151+
/// ("stackable.tech/managed-by", "stackablectl"),
152+
/// ("stackable.tech/vendor", "Stäckable"),
153+
/// ]);
154+
///
155+
/// let labels = Annotations::try_from(map).unwrap();
156+
/// ```
157+
///
158+
/// ### Creating a list of labels from an array
159+
///
160+
/// ```
161+
/// # use stackable_operator::kvp::Annotations;
162+
/// let labels = Annotations::try_from([
163+
/// ("stackable.tech/managed-by", "stackablectl"),
164+
/// ("stackable.tech/vendor", "Stäckable"),
165+
/// ]).unwrap();
166+
/// ```
141167
#[derive(Clone, Debug, Default)]
142168
pub struct Annotations(KeyValuePairs<AnnotationValue>);
143169

@@ -148,9 +174,8 @@ where
148174
{
149175
type Error = AnnotationError;
150176

151-
fn try_from(value: BTreeMap<K, V>) -> Result<Self, Self::Error> {
152-
let kvps = KeyValuePairs::try_from(value)?;
153-
Ok(Self(kvps))
177+
fn try_from(map: BTreeMap<K, V>) -> Result<Self, Self::Error> {
178+
Self::try_from_iter(map)
154179
}
155180
}
156181

@@ -161,9 +186,8 @@ where
161186
{
162187
type Error = AnnotationError;
163188

164-
fn try_from(value: &BTreeMap<K, V>) -> Result<Self, Self::Error> {
165-
let kvps = KeyValuePairs::try_from(value)?;
166-
Ok(Self(kvps))
189+
fn try_from(map: &BTreeMap<K, V>) -> Result<Self, Self::Error> {
190+
Self::try_from_iter(map)
167191
}
168192
}
169193

@@ -174,9 +198,8 @@ where
174198
{
175199
type Error = AnnotationError;
176200

177-
fn try_from(value: [(K, V); N]) -> Result<Self, Self::Error> {
178-
let kvps = KeyValuePairs::try_from(value)?;
179-
Ok(Self(kvps))
201+
fn try_from(array: [(K, V); N]) -> Result<Self, Self::Error> {
202+
Self::try_from_iter(array)
180203
}
181204
}
182205

@@ -187,6 +210,19 @@ impl FromIterator<KeyValuePair<AnnotationValue>> for Annotations {
187210
}
188211
}
189212

213+
impl<K, V> TryFromIterator<(K, V)> for Annotations
214+
where
215+
K: AsRef<str>,
216+
V: AsRef<str>,
217+
{
218+
type Error = AnnotationError;
219+
220+
fn try_from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Result<Self, Self::Error> {
221+
let kvps = KeyValuePairs::try_from_iter(iter)?;
222+
Ok(Self(kvps))
223+
}
224+
}
225+
190226
impl From<Annotations> for BTreeMap<String, String> {
191227
fn from(value: Annotations) -> Self {
192228
value.0.into()

src/kvp/label/mod.rs

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use delegate::delegate;
1818
use kube::{Resource, ResourceExt};
1919

2020
use crate::{
21+
iter::TryFromIterator,
2122
kvp::{
2223
consts::{
2324
K8S_APP_COMPONENT_KEY, K8S_APP_INSTANCE_KEY, K8S_APP_MANAGED_BY_KEY, K8S_APP_NAME_KEY,
@@ -139,6 +140,31 @@ impl Label {
139140
///
140141
/// It provides selected associated functions to manipulate the set of labels,
141142
/// like inserting or extending.
143+
///
144+
/// ## Examples
145+
///
146+
/// ### Converting a BTreeMap into a list of labels
147+
///
148+
/// ```
149+
/// # use std::collections::BTreeMap;
150+
/// # use stackable_operator::kvp::Labels;
151+
/// let map = BTreeMap::from([
152+
/// ("stackable.tech/managed-by", "stackablectl"),
153+
/// ("stackable.tech/vendor", "Stackable"),
154+
/// ]);
155+
///
156+
/// let labels = Labels::try_from(map).unwrap();
157+
/// ```
158+
///
159+
/// ### Creating a list of labels from an array
160+
///
161+
/// ```
162+
/// # use stackable_operator::kvp::Labels;
163+
/// let labels = Labels::try_from([
164+
/// ("stackable.tech/managed-by", "stackablectl"),
165+
/// ("stackable.tech/vendor", "Stackable"),
166+
/// ]).unwrap();
167+
/// ```
142168
#[derive(Clone, Debug, Default)]
143169
pub struct Labels(KeyValuePairs<LabelValue>);
144170

@@ -150,8 +176,7 @@ where
150176
type Error = LabelError;
151177

152178
fn try_from(map: BTreeMap<K, V>) -> Result<Self, Self::Error> {
153-
let kvps = KeyValuePairs::try_from(map)?;
154-
Ok(Self(kvps))
179+
Self::try_from_iter(map)
155180
}
156181
}
157182

@@ -163,8 +188,7 @@ where
163188
type Error = LabelError;
164189

165190
fn try_from(map: &BTreeMap<K, V>) -> Result<Self, Self::Error> {
166-
let kvps = KeyValuePairs::try_from(map)?;
167-
Ok(Self(kvps))
191+
Self::try_from_iter(map)
168192
}
169193
}
170194

@@ -175,9 +199,8 @@ where
175199
{
176200
type Error = LabelError;
177201

178-
fn try_from(value: [(K, V); N]) -> Result<Self, Self::Error> {
179-
let kvps = KeyValuePairs::try_from(value)?;
180-
Ok(Self(kvps))
202+
fn try_from(array: [(K, V); N]) -> Result<Self, Self::Error> {
203+
Self::try_from_iter(array)
181204
}
182205
}
183206

@@ -188,6 +211,19 @@ impl FromIterator<KeyValuePair<LabelValue>> for Labels {
188211
}
189212
}
190213

214+
impl<K, V> TryFromIterator<(K, V)> for Labels
215+
where
216+
K: AsRef<str>,
217+
V: AsRef<str>,
218+
{
219+
type Error = LabelError;
220+
221+
fn try_from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Result<Self, Self::Error> {
222+
let kvps = KeyValuePairs::try_from_iter(iter)?;
223+
Ok(Self(kvps))
224+
}
225+
}
226+
191227
impl From<Labels> for BTreeMap<String, String> {
192228
fn from(value: Labels) -> Self {
193229
value.0.into()

0 commit comments

Comments
 (0)