Skip to content

feature: serialize JSON LabelSet as array of strings #31

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 1 commit into from
Apr 24, 2025
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
5 changes: 5 additions & 0 deletions .changeset/three-melons-stand.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"chainlink-deployments-framework": patch
---

Datastore labels are now serialized into JSON as an array of strings
3 changes: 1 addition & 2 deletions datastore/address_ref.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package datastore

import (
"errors"
"maps"

"github.com/Masterminds/semver/v3"
)
Expand Down Expand Up @@ -51,7 +50,7 @@ func (r AddressRef) Clone() AddressRef {
Version: r.Version,
Qualifier: r.Qualifier,
Address: r.Address,
Labels: maps.Clone(r.Labels),
Labels: r.Labels.Clone(),
}
}

Expand Down
98 changes: 64 additions & 34 deletions datastore/label_set.go
Original file line number Diff line number Diff line change
@@ -1,43 +1,51 @@
package datastore

import (
"sort"
"encoding/json"
"maps"
"slices"
"strings"
)

// LabelSet represents a set of labels on an address book entry.
type LabelSet map[string]struct{}
type LabelSet struct {
elements map[string]struct{}
}

// NewLabelSet initializes a new LabelSet with any number of labels.
func NewLabelSet(labels ...string) LabelSet {
set := make(LabelSet)
for _, lb := range labels {
set[lb] = struct{}{}
set := make(map[string]struct{}, len(labels))
for _, l := range labels {
set[l] = struct{}{}
}

return set
return LabelSet{
elements: set,
}
}

// Add inserts a label into the set.
func (ls LabelSet) Add(label string) {
ls[label] = struct{}{}
func (s LabelSet) Add(label string) {
s.elements[label] = struct{}{}
}

// Remove deletes a label from the set, if it exists.
func (ls LabelSet) Remove(label string) {
delete(ls, label)
func (s LabelSet) Remove(label string) {
delete(s.elements, label)
}

// Contains checks if the set contains the given label.
func (ls LabelSet) Contains(label string) bool {
_, ok := ls[label]
func (s LabelSet) Contains(label string) bool {
_, ok := s.elements[label]

return ok
}

// String returns the labels as a sorted, space-separated string.
// It implements the fmt.Stringer interface.
func (ls LabelSet) String() string {
labels := ls.List()
//
// Implements the fmt.Stringer interface.
func (s LabelSet) String() string {
labels := s.List()
if len(labels) == 0 {
return ""
}
Expand All @@ -47,38 +55,60 @@ func (ls LabelSet) String() string {
}

// List returns the labels as a sorted slice of strings.
func (ls LabelSet) List() []string {
if len(ls) == 0 {
func (s LabelSet) List() []string {
if len(s.elements) == 0 {
return []string{}
}

// Collect labels into a slice
labels := make([]string, 0, len(ls))
for label := range ls {
labels = append(labels, label)
}
labels := slices.Collect(maps.Keys(s.elements))

// Sort the labels to ensure consistent ordering
sort.Strings(labels)
slices.Sort(labels)

return labels
}

// Equal checks if two LabelSets are equal.
func (ls LabelSet) Equal(other LabelSet) bool {
if len(ls) != len(other) {
return false
}
for label := range ls {
if _, ok := other[label]; !ok {
return false
}
}
func (s LabelSet) Equal(other LabelSet) bool {
return maps.Equal(s.elements, other.elements)
}

return true
// Len returns the number of labels in the set.
func (s LabelSet) Length() int {
return len(s.elements)
}

// IsEmpty checks if the LabelSet is empty.
func (ls LabelSet) IsEmpty() bool {
return len(ls) == 0
func (s LabelSet) IsEmpty() bool {
return s.Length() == 0
}

// Clone creates a copy of the LabelSet.
func (s LabelSet) Clone() LabelSet {
return LabelSet{
elements: maps.Clone(s.elements),
}
}

// MarshalJSON marshals the LabelSet as a JSON array of strings.
//
// Implements the json.Marshaler interface.
func (s LabelSet) MarshalJSON() ([]byte, error) {
return json.Marshal(s.List())
}

// UnmarshalJSON unmarshals a JSON array of strings into the LabelSet.
//
// Implements the json.Unmarshaler interface.
func (s *LabelSet) UnmarshalJSON(data []byte) error {
var labels []string
if err := json.Unmarshal(data, &labels); err != nil {
return err
}

// Initialize the LabelSet with the unmarshaled labels
*s = NewLabelSet(labels...)

return nil
}
Loading
Loading