Skip to content

Commit

Permalink
Add a merge method to Metadata (#2084)
Browse files Browse the repository at this point in the history
## Motivation
We want to be able to flatten `RPCError`s, and to do so we need to be
able to merge the `Metadata` contained in each.

## Modifications
This PR adds a helper function to merge one `Metadata` instance into
another.

## Result
Unblocks #2083 and also provides
a potentially useful API for users.

**- Note:** Because of the way `Metadata` has been implemented, we can
have multiple _identical_ key-value pairs. This isn't ideal, as it's
particularly feasible that we'll end up with multiple repeated identical
pairs when merging two `Metadata`s. I think we should reconsider the
backing data structure (using a set for example) or add a check before
inserting to avoid this.
  • Loading branch information
gjcairo authored Oct 7, 2024
1 parent 74d0848 commit d5e0d70
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 0 deletions.
14 changes: 14 additions & 0 deletions Sources/GRPCCore/Metadata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,20 @@ public struct Metadata: Sendable, Hashable {
self.elements.append(.init(key: key, value: value))
}

/// Add the contents of a `Sequence` of key-value pairs to this `Metadata` instance.
///
/// - Parameter other: the `Sequence` whose key-value pairs should be added into this `Metadata` instance.
public mutating func add(contentsOf other: some Sequence<Element>) {
self.elements.append(contentsOf: other.map(KeyValuePair.init))
}

/// Add the contents of another `Metadata` to this instance.
///
/// - Parameter other: the `Metadata` whose key-value pairs should be added into this one.
public mutating func add(contentsOf other: Metadata) {
self.elements.append(contentsOf: other.elements)
}

/// Removes all values associated with the given key.
///
/// - Parameter key: The key for which all values should be removed.
Expand Down
62 changes: 62 additions & 0 deletions Tests/GRPCCoreTests/MetadataTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -252,4 +252,66 @@ struct MetadataTests {
#expect(self.metadata == ["key1": "value1", "key3": "value1"])
}
}

@Suite("Merge")
struct Merge {
var metadata: Metadata = [
"key1": "value1-1",
"key2": "value2",
"key3": "value3",
]
var otherMetadata: Metadata = [
"key4": "value4",
"key5": "value5",
]

@Test("Where key is already present with a different value")
mutating func mergeWhereKeyIsAlreadyPresentWithDifferentValue() async throws {
self.otherMetadata.addString("value1-2", forKey: "key1")
self.metadata.add(contentsOf: self.otherMetadata)

#expect(
self.metadata == [
"key1": "value1-1",
"key2": "value2",
"key3": "value3",
"key4": "value4",
"key5": "value5",
"key1": "value1-2",
]
)
}

@Test("Where key is already present with same value")
mutating func mergeWhereKeyIsAlreadyPresentWithSameValue() async throws {
self.otherMetadata.addString("value1-1", forKey: "key1")
self.metadata.add(contentsOf: self.otherMetadata)

#expect(
self.metadata == [
"key1": "value1-1",
"key2": "value2",
"key3": "value3",
"key4": "value4",
"key5": "value5",
"key1": "value1-1",
]
)
}

@Test("Where key is not already present")
mutating func mergeWhereKeyIsNotAlreadyPresent() async throws {
self.metadata.add(contentsOf: self.otherMetadata)

#expect(
self.metadata == [
"key1": "value1-1",
"key2": "value2",
"key3": "value3",
"key4": "value4",
"key5": "value5",
]
)
}
}
}

0 comments on commit d5e0d70

Please sign in to comment.