Skip to content

Commit 52218e8

Browse files
authored
Add support for encoding Duration in HOCON format (#2080)
1 parent 520eeef commit 52218e8

File tree

4 files changed

+245
-126
lines changed

4 files changed

+245
-126
lines changed

formats/hocon/src/main/kotlin/kotlinx/serialization/hocon/Hocon.kt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,17 @@ import kotlinx.serialization.modules.*
2222
* [Config] object represents "Human-Optimized Config Object Notation" —
2323
* [HOCON][https://github.com/lightbend/config#using-hocon-the-json-superset].
2424
*
25-
* [Duration] objects are decoded using "HOCON duration format" -
25+
* [Duration] objects are encoded/decoded using "HOCON duration format" -
2626
* [Duration format][https://github.com/lightbend/config/blob/main/HOCON.md#duration-format]
27-
* [Duration] objects encoding does not currently support duration HOCON format and uses standard Duration serializer which produces ISO-8601-2 string.
27+
* [Duration] objects encoded using time unit short names: d, h, m, s, ms, us, ns.
28+
* Encoding use the largest time unit.
29+
* Example:
30+
* 120.seconds -> 2 m
31+
* 121.seconds -> 121 s
32+
* 120.minutes -> 2 h
33+
* 122.minutes -> 122 m
34+
* 24.hours -> 1 d
35+
* All restrictions on the maximum and minimum duration are specified in [Duration].
2836
*
2937
* @param [useConfigNamingConvention] switches naming resolution to config naming convention (hyphen separated).
3038
* @param serializersModule A [SerializersModule] which should contain registered serializers

formats/hocon/src/main/kotlin/kotlinx/serialization/hocon/HoconEncoder.kt

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
package kotlinx.serialization.hocon
66

77
import com.typesafe.config.*
8+
import kotlin.time.*
89
import kotlinx.serialization.*
10+
import kotlinx.serialization.builtins.serializer
911
import kotlinx.serialization.descriptors.*
1012
import kotlinx.serialization.encoding.*
1113
import kotlinx.serialization.internal.*
@@ -42,17 +44,18 @@ internal abstract class AbstractHoconEncoder(
4244
override fun shouldEncodeElementDefault(descriptor: SerialDescriptor, index: Int): Boolean = hocon.encodeDefaults
4345

4446
override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) {
45-
if (serializer !is AbstractPolymorphicSerializer<*> || hocon.useArrayPolymorphism) {
46-
serializer.serialize(this, value)
47-
return
47+
when {
48+
serializer.descriptor == Duration.serializer().descriptor -> encodeDuration(value as Duration)
49+
serializer !is AbstractPolymorphicSerializer<*> || hocon.useArrayPolymorphism -> serializer.serialize(this, value)
50+
else -> {
51+
@Suppress("UNCHECKED_CAST")
52+
val casted = serializer as AbstractPolymorphicSerializer<Any>
53+
val actualSerializer = casted.findPolymorphicSerializer(this, value as Any)
54+
writeDiscriminator = true
55+
56+
actualSerializer.serialize(this, value)
57+
}
4858
}
49-
50-
@Suppress("UNCHECKED_CAST")
51-
val casted = serializer as AbstractPolymorphicSerializer<Any>
52-
val actualSerializer = casted.findPolymorphicSerializer(this, value as Any)
53-
writeDiscriminator = true
54-
55-
actualSerializer.serialize(this, value)
5659
}
5760

5861
override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {
@@ -79,6 +82,32 @@ internal abstract class AbstractHoconEncoder(
7982
}
8083

8184
private fun configValueOf(value: Any?) = ConfigValueFactory.fromAnyRef(value)
85+
86+
private fun encodeDuration(value: Duration) {
87+
val result = value.toComponents { seconds, nanoseconds ->
88+
when {
89+
nanoseconds == 0 -> {
90+
if (seconds % 60 == 0L) { // minutes
91+
if (seconds % 3600 == 0L) { // hours
92+
if (seconds % 86400 == 0L) { // days
93+
"${seconds / 86400} d"
94+
} else {
95+
"${seconds / 3600} h"
96+
}
97+
} else {
98+
"${seconds / 60} m"
99+
}
100+
} else {
101+
"$seconds s"
102+
}
103+
}
104+
nanoseconds % 1_000_000 == 0 -> "${seconds * 1_000 + nanoseconds / 1_000_000} ms"
105+
nanoseconds % 1_000 == 0 -> "${seconds * 1_000_000 + nanoseconds / 1_000} us"
106+
else -> "${value.inWholeNanoseconds} ns"
107+
}
108+
}
109+
encodeString(result)
110+
}
82111
}
83112

84113
@ExperimentalSerializationApi

formats/hocon/src/test/kotlin/kotlinx/serialization/hocon/HoconDurationDeserializerTest.kt

Lines changed: 0 additions & 114 deletions
This file was deleted.

0 commit comments

Comments
 (0)