@@ -14,6 +14,20 @@ import kotlin.contracts.ExperimentalContracts
1414import kotlin.contracts.InvocationKind
1515import kotlin.contracts.contract
1616
17+ /* *
18+ * Marker type for custom claim values.
19+ *
20+ * Claims can be represented as objects, arrays, primitives, or null.
21+ *
22+ * @property objectOrNull Value as [ClaimObject] when object-typed.
23+ * @property arrayOrNull Value as [ClaimArray] when array-typed.
24+ * @property primitiveOrNull Value as [ClaimPrimitive] when primitive-typed.
25+ * @property stringOrNull Primitive string value if present.
26+ * @property booleanOrNull Primitive boolean value if present.
27+ * @property intOrNull Primitive int value if present.
28+ * @property longOrNull Primitive long value if present.
29+ * @property doubleOrNull Primitive double value if present.
30+ */
1731sealed interface ClaimElement {
1832 val objectOrNull: ClaimObject ? get() = this as ? ClaimObject
1933 val arrayOrNull: ClaimArray ? get() = this as ? ClaimArray
@@ -26,6 +40,15 @@ sealed interface ClaimElement {
2640 val doubleOrNull: Double? get() = null
2741}
2842
43+ /* *
44+ * Attempt to read this claim as a supported Kotlin type.
45+ *
46+ * Supported types are: [String], [Boolean], [Int], [Long], [Double], [ClaimObject],
47+ * and [ClaimArray]. Calling with an unsupported type will return `null`.
48+ *
49+ * @receiver Claim element to cast.
50+ * @return Cast value or `null` if unsupported/unmatched.
51+ */
2952inline fun <reified T > ClaimElement.asType (): T ? = when (T ::class ) {
3053 String ::class -> primitiveOrNull?.stringOrNull as ? T
3154 Boolean ::class -> primitiveOrNull?.booleanOrNull as ? T
@@ -37,10 +60,12 @@ inline fun <reified T> ClaimElement.asType(): T? = when (T::class) {
3760 else -> null
3861}
3962
63+ /* * Null claim value. */
4064object ClaimNull : ClaimElement
4165
4266@JvmInline
4367@Suppress(" JavaDefaultMethodsNotOverriddenByDelegation" )
68+ /* * Array claim value. */
4469value class ClaimArray internal constructor(private val content : List <ClaimElement >) :
4570 ClaimElement ,
4671 List <ClaimElement > by content {
@@ -49,6 +74,7 @@ value class ClaimArray internal constructor(private val content: List<ClaimEleme
4974}
5075
5176@JvmInline
77+ /* * Object claim value. */
5278value class ClaimObject internal constructor(private val content : Map <String , ClaimElement >) :
5379 ClaimElement ,
5480 Map <String , ClaimElement > by content {
@@ -60,6 +86,7 @@ value class ClaimObject internal constructor(private val content: Map<String, Cl
6086}
6187
6288@JvmInline
89+ /* * Primitive claim value. */
6390value class ClaimPrimitive internal constructor(internal val primitive : JsonPrimitive ) : ClaimElement {
6491 override val stringOrNull: String? get() = primitive.contentOrNull?.takeIf { primitive.isString }
6592 override val booleanOrNull: Boolean? get() = primitive.booleanOrNull
@@ -70,19 +97,61 @@ value class ClaimPrimitive internal constructor(internal val primitive: JsonPrim
7097}
7198
7299@PasetoDslMarker
100+ /* * DSL Builder for [ClaimObject]. */
73101class ClaimObjectBuilder @PublishedApi internal constructor() {
74102 private val content: MutableMap <String , ClaimElement > = linkedMapOf()
75103
104+ /* *
105+ * Put a nested claim value.
106+ *
107+ * @param key Claim key.
108+ * @param value Claim value.
109+ * @return Previous value for [key], if present.
110+ */
76111 fun put (key : String , value : ClaimElement ): ClaimElement ? = content.put(key, value)
112+ /* *
113+ * Put a boolean claim value.
114+ *
115+ * @param key Claim key.
116+ * @param value Claim value.
117+ * @return Previous value for [key], if present.
118+ */
77119 fun put (key : String , value : Boolean ): ClaimElement ? = content.put(key, primitiveValue(value))
120+ /* *
121+ * Put a numeric claim value.
122+ *
123+ * @param key Claim key.
124+ * @param value Claim value.
125+ * @return Previous value for [key], if present.
126+ */
78127 fun put (key : String , value : Number ): ClaimElement ? = content.put(key, primitiveValue(value))
128+ /* *
129+ * Put a string claim value.
130+ *
131+ * @param key Claim key.
132+ * @param value Claim value.
133+ * @return Previous value for [key], if present.
134+ */
79135 fun put (key : String , value : String ): ClaimElement ? = content.put(key, primitiveValue(value))
136+ /* *
137+ * Put a `null` claim value.
138+ *
139+ * @param key Claim key.
140+ * @param value Claim value.
141+ * @return Previous value for [key], if present.
142+ */
80143 fun put (key : String , value : Nothing? ): ClaimElement ? = content.put(key, primitiveValue(value))
81144
82145 @PublishedApi
83146 internal fun build (): ClaimObject = ClaimObject (content)
84147}
85148
149+ /* *
150+ * Build a [ClaimObject] using the claim DSL.
151+ *
152+ * @param init Claim-object builder block.
153+ * @return Built [ClaimObject].
154+ */
86155@OptIn(ExperimentalContracts ::class )
87156inline fun claimObject (init : ClaimObjectBuilder .() -> Unit ): ClaimObject {
88157 contract { callsInPlace(init , InvocationKind .EXACTLY_ONCE ) }
@@ -93,19 +162,56 @@ inline fun claimObject(init: ClaimObjectBuilder.() -> Unit): ClaimObject {
93162}
94163
95164@PasetoDslMarker
165+ /* * DSL builder for [ClaimArray]. */
96166class ClaimArrayBuilder @PublishedApi internal constructor() {
97167 private val content: MutableList <ClaimElement > = mutableListOf ()
98168
169+ /* *
170+ * Add a nested claim value.
171+ *
172+ * @param value Claim value to append.
173+ * @return `true` when appended.
174+ */
99175 fun add (value : ClaimElement ): Boolean = content.add(value)
176+ /* *
177+ * Add a boolean value.
178+ *
179+ * @param value Claim value to append.
180+ * @return `true` when appended.
181+ */
100182 fun add (value : Boolean ): Boolean = content.add(primitiveValue(value))
183+ /* *
184+ * Add a numeric value.
185+ *
186+ * @param value Claim value to append.
187+ * @return `true` when appended.
188+ */
101189 fun add (value : Number ): Boolean = content.add(primitiveValue(value))
190+ /* *
191+ * Add a string value.
192+ *
193+ * @param value Claim value to append.
194+ * @return `true` when appended.
195+ */
102196 fun add (value : String ): Boolean = content.add(primitiveValue(value))
197+ /* *
198+ * Add a `null` value.
199+ *
200+ * @param value Claim value to append.
201+ * @return `true` when appended.
202+ */
103203 fun add (value : Nothing? ): Boolean = content.add(primitiveValue(value))
104204
105205 @PublishedApi
106206 internal fun build (): ClaimArray = ClaimArray (content)
107207}
108208
209+ /* *
210+ * Build a [ClaimArray] using the claim DSL.
211+ *
212+ * @param init Array builder block.
213+ * @return Built [ClaimArray].
214+ */
109215@OptIn(ExperimentalContracts ::class )
110216inline fun claimArray (init : ClaimArrayBuilder .() -> Unit ): ClaimArray {
111217 contract { callsInPlace(init , InvocationKind .EXACTLY_ONCE ) }
@@ -129,7 +235,31 @@ internal fun ClaimElement.toJson(): JsonElement = when (this) {
129235 is ClaimObject -> JsonObject (this .mapValues { it.value.toJson() })
130236}
131237
238+ /* *
239+ * Convert a nullable boolean into a [ClaimPrimitive].
240+ *
241+ * @param value Boolean value.
242+ * @return Primitive claim.
243+ */
132244fun primitiveValue (value : Boolean? ): ClaimPrimitive = ClaimPrimitive (JsonPrimitive (value))
245+ /* *
246+ * Convert a nullable number into a [ClaimPrimitive].
247+ *
248+ * @param value Number value.
249+ * @return Primitive claim.
250+ */
133251fun primitiveValue (value : Number ? ): ClaimPrimitive = ClaimPrimitive (JsonPrimitive (value))
252+ /* *
253+ * Convert a nullable string into a [ClaimPrimitive].
254+ *
255+ * @param value String value.
256+ * @return Primitive claim.
257+ */
134258fun primitiveValue (value : String? ): ClaimPrimitive = ClaimPrimitive (JsonPrimitive (value))
259+ /* *
260+ * Convert nullable `Nothing` into a JSON null [ClaimPrimitive].
261+ *
262+ * @param value Null literal.
263+ * @return Primitive claim.
264+ */
135265fun primitiveValue (value : Nothing? ): ClaimPrimitive = ClaimPrimitive (JsonPrimitive (value))
0 commit comments