Skip to content

feat: Add key/value pair QoL improvements #711

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file.

## [Unreleased]

### Added

- Add `TryFrom<[(K, V); N]>` implementation for `Annotations` and `Labels` ([#711]).
- Add `parse_insert` associated function for `Annotations` and `Labels` ([#711]).

### Changed

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

[#711]: https://github.com/stackabletech/operator-rs/pull/711

## [0.60.1] - 2024-01-04

### Fixed
Expand Down
68 changes: 54 additions & 14 deletions src/kvp/annotation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ mod value;

pub use value::*;

/// A type alias for errors returned when construction of an annotation fails.
pub type AnnotationsError = KeyValuePairsError<Infallible>;
pub type AnnotationsError = KeyValuePairsError;

/// A type alias for errors returned when construction or manipulation of a set
/// of annotations fails.
Expand All @@ -45,13 +44,14 @@ pub type AnnotationError = KeyValuePairError<Infallible>;
#[derive(Debug)]
pub struct Annotation(KeyValuePair<AnnotationValue>);

impl<T> TryFrom<(T, T)> for Annotation
impl<K, V> TryFrom<(K, V)> for Annotation
where
T: AsRef<str>,
K: AsRef<str>,
V: AsRef<str>,
{
type Error = AnnotationError;

fn try_from(value: (T, T)) -> Result<Self, Self::Error> {
fn try_from(value: (K, V)) -> Result<Self, Self::Error> {
let kvp = KeyValuePair::try_from(value)?;
Ok(Self(kvp))
}
Expand Down Expand Up @@ -150,6 +150,19 @@ impl TryFrom<BTreeMap<String, String>> for Annotations {
}
}

impl<const N: usize, K, V> TryFrom<[(K, V); N]> for Annotations
where
K: AsRef<str>,
V: AsRef<str>,
{
type Error = AnnotationError;

fn try_from(value: [(K, V); N]) -> Result<Self, Self::Error> {
let kvps = KeyValuePairs::try_from(value)?;
Ok(Self(kvps))
}
}

impl FromIterator<KeyValuePair<AnnotationValue>> for Annotations {
fn from_iter<T: IntoIterator<Item = KeyValuePair<AnnotationValue>>>(iter: T) -> Self {
let kvps = KeyValuePairs::from_iter(iter);
Expand All @@ -174,17 +187,19 @@ impl Annotations {
Self(KeyValuePairs::new_with(pairs))
}

/// Tries to insert a new [`Annotation`]. It ensures there are no duplicate
/// entries. Trying to insert duplicated data returns an error. If no such
/// check is required, use the `insert` function instead.
pub fn try_insert(&mut self, annotation: Annotation) -> Result<&mut Self, AnnotationsError> {
self.0.try_insert(annotation.0)?;
Ok(self)
/// Tries to insert a new annotation by first parsing `annotation` as an
/// [`Annotation`] and then inserting it into the list. This function will
/// overwrite any existing annotation already present.
pub fn parse_insert(
&mut self,
annotation: impl TryInto<Annotation, Error = AnnotationError>,
) -> Result<(), AnnotationError> {
self.0.insert(annotation.try_into()?.0);
Ok(())
}

/// Inserts a new [`Annotation`]. This function will overide any existing
/// annotation already present. If this behaviour is not desired, use the
/// `try_insert` function instead.
/// Inserts a new [`Annotation`]. This function will overwrite any existing
/// annotation already present.
pub fn insert(&mut self, annotation: Annotation) -> &mut Self {
self.0.insert(annotation.0);
self
Expand All @@ -196,6 +211,11 @@ impl Annotations {
// the need to write boilerplate code.
delegate! {
to self.0 {
/// Tries to insert a new [`Annotation`]. It ensures there are no duplicate
/// entries. Trying to insert duplicated data returns an error. If no such
/// check is required, use [`Annotations::insert`] instead.
pub fn try_insert(&mut self, #[newtype] annotation: Annotation) -> Result<(), AnnotationsError>;

/// Extends `self` with `other`.
pub fn extend(&mut self, #[newtype] other: Self);

Expand All @@ -217,3 +237,23 @@ impl Annotations {
}
}
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn parse_insert() {
let mut annotations = Annotations::new();

annotations
.parse_insert(("stackable.tech/managed-by", "stackablectl"))
.unwrap();

annotations
.parse_insert(("stackable.tech/vendor", "Stäckable"))
.unwrap();

assert_eq!(annotations.len(), 2);
}
}
68 changes: 54 additions & 14 deletions src/kvp/label/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ mod value;
pub use selector::*;
pub use value::*;

/// A type alias for errors returned when construction of a label fails.
pub type LabelsError = KeyValuePairsError<LabelValueError>;
pub type LabelsError = KeyValuePairsError;

/// A type alias for errors returned when construction or manipulation of a set
/// of labels fails.
Expand All @@ -57,13 +56,14 @@ pub type LabelError = KeyValuePairError<LabelValueError>;
#[derive(Clone, Debug)]
pub struct Label(KeyValuePair<LabelValue>);

impl<T> TryFrom<(T, T)> for Label
impl<K, V> TryFrom<(K, V)> for Label
where
T: AsRef<str>,
K: AsRef<str>,
V: AsRef<str>,
{
type Error = LabelError;

fn try_from(value: (T, T)) -> Result<Self, Self::Error> {
fn try_from(value: (K, V)) -> Result<Self, Self::Error> {
let kvp = KeyValuePair::try_from(value)?;
Ok(Self(kvp))
}
Expand Down Expand Up @@ -151,6 +151,19 @@ impl TryFrom<BTreeMap<String, String>> for Labels {
}
}

impl<const N: usize, K, V> TryFrom<[(K, V); N]> for Labels
where
K: AsRef<str>,
V: AsRef<str>,
{
type Error = LabelError;

fn try_from(value: [(K, V); N]) -> Result<Self, Self::Error> {
let kvps = KeyValuePairs::try_from(value)?;
Ok(Self(kvps))
}
}

impl FromIterator<KeyValuePair<LabelValue>> for Labels {
fn from_iter<T: IntoIterator<Item = KeyValuePair<LabelValue>>>(iter: T) -> Self {
let kvps = KeyValuePairs::from_iter(iter);
Expand All @@ -175,17 +188,19 @@ impl Labels {
Self(KeyValuePairs::new_with(pairs))
}

/// Tries to insert a new [`Label`]. It ensures there are no duplicate
/// entries. Trying to insert duplicated data returns an error. If no such
/// check is required, use the `insert` function instead.
pub fn try_insert(&mut self, label: Label) -> Result<&mut Self, LabelsError> {
self.0.try_insert(label.0)?;
Ok(self)
/// Tries to insert a new label by first parsing `label` as a [`Label`]
/// and then inserting it into the list. This function will overwrite any
/// existing label already present.
pub fn parse_insert(
&mut self,
label: impl TryInto<Label, Error = LabelError>,
) -> Result<(), LabelError> {
self.0.insert(label.try_into()?.0);
Ok(())
}

/// Inserts a new [`Label`]. This function will overide any existing label
/// already present. If this behaviour is not desired, use the `try_insert`
/// function instead.
/// Inserts a new [`Label`]. This function will overwrite any existing label
/// already present.
pub fn insert(&mut self, label: Label) -> &mut Self {
self.0.insert(label.0);
self
Expand Down Expand Up @@ -285,6 +300,11 @@ impl Labels {
// need to write boilerplate code.
delegate! {
to self.0 {
/// Tries to insert a new [`Label`]. It ensures there are no duplicate
/// entries. Trying to insert duplicated data returns an error. If no such
/// check is required, use [`Labels::insert`] instead.
pub fn try_insert(&mut self, #[newtype] label: Label) -> Result<(), LabelsError>;

/// Extends `self` with `other`.
pub fn extend(&mut self, #[newtype] other: Self);

Expand All @@ -304,3 +324,23 @@ impl Labels {
}
}
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn parse_insert() {
let mut labels = Labels::new();

labels
.parse_insert(("stackable.tech/managed-by", "stackablectl"))
.unwrap();

labels
.parse_insert(("stackable.tech/vendor", "Stackable"))
.unwrap();

assert_eq!(labels.len(), 2);
}
}
Loading