Skip to content

Commit dab9303

Browse files
committed
Define the public API representation of a format
1 parent 8cf6dd4 commit dab9303

File tree

1 file changed

+113
-0
lines changed

1 file changed

+113
-0
lines changed
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
* Copyright 2019-2023 JetBrains s.r.o. and contributors.
3+
* Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file.
4+
*/
5+
6+
package kotlinx.datetime.format
7+
8+
import kotlinx.datetime.*
9+
import kotlinx.datetime.internal.format.*
10+
import kotlinx.datetime.internal.format.parser.*
11+
12+
/**
13+
* A format for parsing and formatting date-time-related values.
14+
*/
15+
public sealed interface DateTimeFormat<T> {
16+
/**
17+
* Formats the given [value] into a string, using this format.
18+
*/
19+
public fun format(value: T): String
20+
21+
/**
22+
* Formats the given [value] into the given [appendable] using this format.
23+
*/
24+
public fun <A : Appendable> formatTo(appendable: A, value: T): A
25+
26+
/**
27+
* Parses the given [input] string as [T] using this format.
28+
*
29+
* @throws IllegalArgumentException if the input string is not in the expected format or the value is invalid.
30+
*/
31+
public fun parse(input: CharSequence): T
32+
33+
/**
34+
* Parses the given [input] string as [T] using this format.
35+
*
36+
* @return the parsed value, or `null` if the input string is not in the expected format or the value is invalid.
37+
*/
38+
public fun parseOrNull(input: CharSequence): T?
39+
}
40+
41+
/**
42+
* The style of padding to use when formatting a value.
43+
*/
44+
public enum class Padding {
45+
/**
46+
* No padding.
47+
*/
48+
NONE,
49+
50+
/**
51+
* Pad with zeros.
52+
*/
53+
ZERO,
54+
55+
/**
56+
* Pad with spaces.
57+
*/
58+
SPACE
59+
}
60+
61+
internal fun Padding.toKotlinCode(): String = when (this) {
62+
Padding.NONE -> "Padding.NONE"
63+
Padding.ZERO -> "Padding.ZERO"
64+
Padding.SPACE -> "Padding.SPACE"
65+
}
66+
67+
internal inline fun Padding.minDigits(width: Int) = if (this == Padding.ZERO) width else 1
68+
internal inline fun Padding.spaces(width: Int) = if (this == Padding.SPACE) width else null
69+
70+
/** [T] is the user-visible type, whereas [U] is its mutable representation for parsing and formatting. */
71+
internal sealed class AbstractDateTimeFormat<T, U : Copyable<U>> : DateTimeFormat<T> {
72+
73+
abstract val actualFormat: CachedFormatStructure<U>
74+
75+
abstract fun intermediateFromValue(value: T): U
76+
77+
abstract fun valueFromIntermediate(intermediate: U): T
78+
79+
abstract val emptyIntermediate: U // should be part of the `Copyable` interface once the language allows this
80+
81+
open fun valueFromIntermediateOrNull(intermediate: U): T? = try {
82+
valueFromIntermediate(intermediate)
83+
} catch (e: IllegalArgumentException) {
84+
null
85+
}
86+
87+
override fun format(value: T): String = StringBuilder().also {
88+
actualFormat.formatter().format(intermediateFromValue(value), it)
89+
}.toString()
90+
91+
override fun <A : Appendable> formatTo(appendable: A, value: T): A = appendable.apply {
92+
actualFormat.formatter().format(intermediateFromValue(value), this)
93+
}
94+
95+
override fun parse(input: CharSequence): T {
96+
val matched = try {
97+
// without the fully qualified name, the compilation fails for some reason
98+
Parser(actualFormat.parser()).match(input, emptyIntermediate)
99+
} catch (e: ParseException) {
100+
throw DateTimeFormatException("Failed to parse value from '$input'", e)
101+
}
102+
try {
103+
return valueFromIntermediate(matched)
104+
} catch (e: IllegalArgumentException) {
105+
throw DateTimeFormatException(e.message!!)
106+
}
107+
}
108+
109+
override fun parseOrNull(input: CharSequence): T? =
110+
// without the fully qualified name, the compilation fails for some reason
111+
Parser(actualFormat.parser()).matchOrNull(input, emptyIntermediate)?.let { valueFromIntermediateOrNull(it) }
112+
113+
}

0 commit comments

Comments
 (0)