Skip to content

Commit 3c64ac1

Browse files
committed
grpc: Add documentation and more tests
1 parent e955b24 commit 3c64ac1

File tree

2 files changed

+385
-0
lines changed

2 files changed

+385
-0
lines changed

grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/GrpcMetadata.kt

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,211 @@
44

55
package kotlinx.rpc.grpc
66

7+
/**
8+
* Provides access to read and write metadata values to be exchanged during a gRPC call.
9+
*
10+
* Metadata is an ordered map with case-insensitive keys that are ASCII strings. Each key can be
11+
* associated with multiple values. Values can be either strings (for standard keys) or binary data
12+
* (for keys ending with "-bin" suffix).
13+
*
14+
* ## Key Requirements
15+
*
16+
* Keys must contain only the following ASCII characters:
17+
* - Digits: `0-9`
18+
* - Lowercase letters: `a-z` (uppercase letters are normalized to lowercase)
19+
* - Special characters: `-`, `_`, `.`
20+
*
21+
* Keys must not contain spaces or other special characters. Invalid keys will cause an
22+
* [IllegalArgumentException] to be thrown. Binary keys must additionally end with the `-bin` suffix.
23+
*
24+
* ## Value Requirements
25+
*
26+
* ASCII string values must contain only:
27+
* - ASCII visible characters (0x21-0x7E)
28+
* - Space (0x20), but not at the beginning or end of the string
29+
*
30+
* Non-ASCII characters in values are replaced with `?` during encoding.
31+
*
32+
* ## Thread Safety
33+
*
34+
* This class is not thread-safe. Modifications made by one thread may not be visible to another
35+
* thread concurrently reading the metadata.
36+
*
37+
* ## Example usage
38+
* ```kotlin
39+
* val metadata = GrpcMetadata().apply {
40+
* append("custom-header", "value1")
41+
* append("custom-header", "value2")
42+
* appendBinary("custom-header-bin", byteArrayOf(1, 2, 3))
43+
*}
44+
*
45+
* val firstValue = metadata["custom-header"] // returns "value2" (last added)
46+
* val allValues = metadata.getAll("custom-header") // returns ["value1", "value2"]
47+
* ```
48+
*/
749
@Suppress("RedundantConstructorKeyword")
850
public expect class GrpcMetadata constructor()
951

52+
/**
53+
* Returns the last metadata entry added with the given [key], or `null` if there are no entries.
54+
*
55+
* @param key the name of the metadata entry to retrieve (case-insensitive). Must not end with `-bin`.
56+
* @return the last value associated with the key, or `null` if no values exist
57+
* @throws IllegalArgumentException if the key ends with `-bin`
58+
*/
1059
public expect operator fun GrpcMetadata.get(key: String): String?
60+
61+
/**
62+
* Returns the last binary metadata entry added with the given [key], or `null` if there are no entries.
63+
*
64+
* Binary keys must end with the "-bin" suffix according to gRPC specification.
65+
*
66+
* @param key the name of the binary metadata entry to retrieve (case-insensitive). Must end with `-bin`.
67+
* @return the last binary value associated with the key, or `null` if no values exist
68+
* @throws IllegalArgumentException if the key does not end with `-bin`
69+
*/
1170
public expect fun GrpcMetadata.getBinary(key: String): ByteArray?
71+
72+
/**
73+
* Returns all metadata entries with the given [key], in the order they were added.
74+
*
75+
* @param key the name of the metadata entries to retrieve (case-insensitive). Must not end with `-bin`.
76+
* @return a list of all values associated with the key, or an empty list if no values exist
77+
* @throws IllegalArgumentException if the key ends with `-bin`
78+
*/
1279
public expect fun GrpcMetadata.getAll(key: String): List<String>
80+
81+
/**
82+
* Returns all binary metadata entries with the given [key], in the order they were added.
83+
*
84+
* Binary keys must end with the "-bin" suffix according to gRPC specification.
85+
*
86+
* @param key the name of the binary metadata entries to retrieve (case-insensitive). Must end with `-bin`.
87+
* @return a list of all binary values associated with the key, or an empty list if no values exist
88+
* @throws IllegalArgumentException if the key does not end with `-bin`
89+
*/
1390
public expect fun GrpcMetadata.getAllBinary(key: String): List<ByteArray>
1491

92+
/**
93+
* Returns an immutable set of all keys present in this metadata.
94+
*
95+
* The returned set is a snapshot of the keys at the time of the call and will not reflect
96+
* subsequent modifications to the metadata.
97+
*
98+
* @return an immutable set of all keys
99+
*/
15100
public expect fun GrpcMetadata.keys(): Set<String>
101+
102+
/**
103+
* Returns `true` if this metadata contains one or more values for the specified [key].
104+
*
105+
* @param key the key whose presence is to be tested (case-insensitive)
106+
* @return `true` if this metadata contains the key, `false` otherwise
107+
*/
16108
public expect operator fun GrpcMetadata.contains(key: String): Boolean
17109

110+
/**
111+
* Appends a metadata entry with the given [key] and [value].
112+
*
113+
* If the key already has values, the new value is added to the end of the list.
114+
* Duplicate values for the same key are permitted.
115+
*
116+
* @param key the name of the metadata entry (case-insensitive). Must contain only digits (0-9),
117+
* lowercase letters (a-z), and special characters (`-`, `_`, `.`). Must not end with `-bin`.
118+
* @param value the ASCII string value to add. Non-ASCII characters will be replaced with `?`.
119+
* @throws IllegalArgumentException if the key contains invalid characters or ends with `-bin`
120+
*/
18121
public expect fun GrpcMetadata.append(key: String, value: String)
122+
123+
/**
124+
* Appends a binary metadata entry with the given [key] and [value].
125+
*
126+
* Binary keys must end with the "-bin" suffix according to gRPC specification.
127+
* If the key already has values, the new value is added to the end of the list.
128+
* Duplicate values for the same key are permitted.
129+
*
130+
* @param key the name of the binary metadata entry (case-insensitive). Must contain only digits (0-9),
131+
* lowercase letters (a-z), and special characters (`-`, `_`, `.`). Must end with `-bin`.
132+
* @param value the binary value to add
133+
* @throws IllegalArgumentException if the key contains invalid characters or does not end with `-bin`
134+
*/
19135
public expect fun GrpcMetadata.appendBinary(key: String, value: ByteArray)
20136

137+
/**
138+
* Removes the first occurrence of the specified [value] for the given [key].
139+
*
140+
* @param key the name of the metadata entry (case-insensitive). Must not end with `-bin`.
141+
* @param value the value to remove
142+
* @return `true` if the value was found and removed, `false` if the value was not present
143+
* @throws IllegalArgumentException if the key ends with `-bin`
144+
*/
21145
public expect fun GrpcMetadata.remove(key: String, value: String): Boolean
146+
147+
/**
148+
* Removes the first occurrence of the specified binary [value] for the given [key].
149+
*
150+
* Binary keys must end with the "-bin" suffix according to gRPC specification.
151+
*
152+
* @param key the name of the binary metadata entry (case-insensitive). Must end with `-bin`.
153+
* @param value the binary value to remove
154+
* @return `true` if the value was found and removed, `false` if the value was not present
155+
* @throws IllegalArgumentException if the key does not end with `-bin`
156+
*/
22157
public expect fun GrpcMetadata.removeBinary(key: String, value: ByteArray): Boolean
158+
159+
/**
160+
* Removes all values for the given [key] and returns them.
161+
*
162+
* @param key the name of the metadata entries to remove (case-insensitive). Must not end with `-bin`.
163+
* @return a list of all values that were removed, or an empty list if there were no values
164+
* @throws IllegalArgumentException if the key ends with `-bin`
165+
*/
23166
public expect fun GrpcMetadata.removeAll(key: String): List<String>
167+
168+
/**
169+
* Removes all binary values for the given [key] and returns them.
170+
*
171+
* Binary keys must end with the "-bin" suffix according to gRPC specification.
172+
*
173+
* @param key the name of the binary metadata entries to remove (case-insensitive). Must end with `-bin`.
174+
* @return a list of all binary values that were removed, or an empty list if there were no values
175+
* @throws IllegalArgumentException if the key does not end with `-bin`
176+
*/
24177
public expect fun GrpcMetadata.removeAllBinary(key: String): List<ByteArray>
25178

179+
/**
180+
* Merges all entries from [other] metadata into this metadata.
181+
*
182+
* This is a purely additive operation. All entries from [other] are appended to this metadata,
183+
* preserving the order and allowing duplicate values for the same key.
184+
*
185+
* @param other the metadata to merge into this metadata
186+
*/
26187
public expect fun GrpcMetadata.merge(other: GrpcMetadata)
188+
189+
/**
190+
* Merges all entries from [other] metadata into this metadata using the `+=` operator.
191+
*
192+
* This is an alias for [merge] that allows idiomatic Kotlin usage.
193+
*
194+
* @param other the metadata to merge into this metadata
195+
* @see merge
196+
*/
27197
public operator fun GrpcMetadata.plusAssign(other: GrpcMetadata): Unit = merge(other)
28198

199+
/**
200+
* Creates a copy of this metadata containing all entries.
201+
*
202+
* @return a new [GrpcMetadata] instance with the same entries as this metadata
203+
*/
29204
public fun GrpcMetadata.copy(): GrpcMetadata = GrpcMetadata().also { it.merge(this) }
205+
206+
/**
207+
* Creates a new metadata instance containing all entries from this metadata and [other].
208+
*
209+
* This operation does not modify either the current or [other] metadata.
210+
*
211+
* @param other the metadata to merge with this metadata
212+
* @return a new [GrpcMetadata] instance containing entries from both metadata objects
213+
*/
30214
public operator fun GrpcMetadata.plus(other: GrpcMetadata): GrpcMetadata = copy().apply { merge(other) }

0 commit comments

Comments
 (0)