Skip to content

Commit

Permalink
Add AttributeCollection.Get() and HasFlags(), etc methods.
Browse files Browse the repository at this point in the history
  • Loading branch information
jmalloc committed Nov 7, 2023
1 parent efefb65 commit 20f5854
Show file tree
Hide file tree
Showing 2 changed files with 207 additions and 3 deletions.
71 changes: 69 additions & 2 deletions dnssd/attributes.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@ func (a Attributes) Flags() map[string]struct{} {
return flags
}

// Without returns a clone of the attributes wouth the given keys, regardless of
// whether they are key/value pairs or flags.
// Without returns a clone of the attributes without the given keys, regardless
// of whether they are key/value pairs or flags.
func (a Attributes) Without(keys ...string) Attributes {
return a.mutate(func(m map[string][]byte) {
for _, k := range keys {
Expand Down Expand Up @@ -319,6 +319,73 @@ func normalizeAttributeKey(k string) (string, error) {
// contains the attributes conveyed in a separate TXT record.
type AttributeCollection []Attributes

// Get returns the last value that is associated with the key k.
//
// ok is true there is a key/value pair with this key.
func (c AttributeCollection) Get(k string) (v []byte, ok bool) {
k = mustNormalizeAttributeKey(k)

for i := len(c) - 1; i >= 0; i-- {
a := c[i]
v = a.m[k]
if v != nil {
return v, true
}
}

return nil, false
}

// Pairs returns the key/value pair (i.e. non-flag) attributes.
func (c AttributeCollection) Pairs() map[string][]byte {
attrs := map[string][]byte{}

for _, a := range c {
for k, v := range a.m {
if v != nil {
attrs[k] = v
}
}
}

return attrs
}

// HasFlags returns true if all of the given flags are present in the
// attributes.
func (c AttributeCollection) HasFlags(keys ...string) bool {
key:
for _, k := range keys {
k = mustNormalizeAttributeKey(k)

for _, a := range c {
v, ok := a.m[k]
if ok && v == nil {
continue key
}
}

return false
}

return true
}

// Flags returns the flag (i.e. non-pair) attributes that are set.
func (c AttributeCollection) Flags() map[string]struct{} {
flags := map[string]struct{}{}

for _, a := range c {
for k, v := range a.m {
if v == nil {
flags[k] = struct{}{}
}
}
}

return flags
}

// Equal returns true if c and x contain the same sets of attributes, in any
// order.
func (c AttributeCollection) Equal(x AttributeCollection) bool {
Expand Down
139 changes: 138 additions & 1 deletion dnssd/attributes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ var _ = Describe("type Attributes", func() {
Expect(ok).To(BeFalse())
})

It("returns false the key is a flag", func() {
It("returns false if the key is a flag", func() {
attrs := NewAttributes().
WithFlag("<key>")

Expand Down Expand Up @@ -402,3 +402,140 @@ var _ = Describe("type Attributes", func() {
})
})
})

var _ = Describe("type AttributeCollection", func() {
Describe("func Get()", func() {
It("returns the associated value", func() {
col := AttributeCollection{
NewAttributes().
WithPair("<key>", []byte("<value>")),
}

v, ok := col.Get("<key>")
Expect(v).To(Equal([]byte("<value>")))
Expect(ok).To(BeTrue())
})

It("returns false if there is no such key", func() {
col := AttributeCollection{}

_, ok := col.Get("<key>")
Expect(ok).To(BeFalse())
})

It("returns false if the key is a flag", func() {
col := AttributeCollection{
NewAttributes().
WithFlag("<key>"),
}

_, ok := col.Get("<key>")
Expect(ok).To(BeFalse())
})

It("is case insensitive", func() {
col := AttributeCollection{
NewAttributes().
WithPair("<KEY>", []byte("<value>")),
}

v, ok := col.Get("<key>")
Expect(v).To(Equal([]byte("<value>")))
Expect(ok).To(BeTrue())
})

It("returns the value from the last / right-most attribute set", func() {
col := AttributeCollection{
NewAttributes().
WithPair("<key>", []byte("<value-1>")),
NewAttributes().
WithPair("<key>", []byte("<value-2>")),
}

v, ok := col.Get("<key>")
Expect(v).To(Equal([]byte("<value-2>")))
Expect(ok).To(BeTrue())
})
})

Describe("func Pairs()", func() {
It("returns the binary attributes", func() {
col := AttributeCollection{
NewAttributes().
WithPair("<key-1>", []byte("<value-1>")),
NewAttributes().
WithPair("<key-2>", nil),
NewAttributes().
WithFlag("<key-3>"),
NewAttributes().
WithPair("<key-1>", []byte("<value-2>")),
}

Expect(col.Pairs()).To(Equal(
map[string][]byte{
"<key-1>": []byte("<value-2>"),
"<key-2>": {},
},
))
})
})

Describe("func HasFlags()", func() {
DescribeTable(
"it returns the expected result",
func(expect bool, keys ...string) {
col := AttributeCollection{
NewAttributes().
WithFlag("<key-1>"),
NewAttributes().
WithFlag("<key-2>"),
}

Expect(col.HasFlags(keys...)).To(Equal(expect))
},
Entry("no flags", true),
Entry("equivalent set", true, "<key-1>", "<key-2>"),
Entry("superset", true, "<key-1>"),
Entry("subset", false, "<key-1>", "<key-2>", "<key-3>"),
Entry("disjoint set", false, "<key-4>", "<key-5>"),
)

It("returns false if the key has a binary value associated with it", func() {
col := AttributeCollection{
NewAttributes().
WithPair("<key>", []byte("<value>")),
}

Expect(col.HasFlags("<key>")).To(BeFalse())
})

It("is case insensitive", func() {
col := AttributeCollection{
NewAttributes().
WithFlag("<KEY>"),
}

Expect(col.HasFlags("<key>")).To(BeTrue())
})
})

Describe("func Flags()", func() {
It("returns the flags that are set", func() {
col := AttributeCollection{
NewAttributes().
WithFlag("<key-1>"),
NewAttributes().
WithFlag("<key-2>"),
NewAttributes().
WithPair("<key-3>", []byte("<value>")),
}

Expect(col.Flags()).To(Equal(
map[string]struct{}{
"<key-1>": {},
"<key-2>": {},
},
))
})
})
})

0 comments on commit 20f5854

Please sign in to comment.