Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve error descriptiveness in the parsing API #360

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions core/common/src/LocalDate.kt
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ public expect class LocalDate : Comparable<LocalDate> {
* (for example, [dayOfMonth] is 31 for February), consider using [DateTimeComponents.Format] instead.
*
* There is a collection of predefined formats in [LocalDate.Formats].
*
* @throws IllegalArgumentException if parsing using this format is ambiguous.
*/
@Suppress("FunctionName")
public fun Format(block: DateTimeFormatBuilder.WithDate.() -> Unit): DateTimeFormat<LocalDate>
Expand Down
2 changes: 2 additions & 0 deletions core/common/src/LocalDateTime.kt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ public expect class LocalDateTime : Comparable<LocalDateTime> {
* (for example, [dayOfMonth] is 31 for February), consider using [DateTimeComponents.Format] instead.
*
* There is a collection of predefined formats in [LocalDateTime.Formats].
*
* @throws IllegalArgumentException if parsing using this format is ambiguous.
*/
@Suppress("FunctionName")
public fun Format(builder: DateTimeFormatBuilder.WithDateTime.() -> Unit): DateTimeFormat<LocalDateTime>
Expand Down
2 changes: 2 additions & 0 deletions core/common/src/LocalTime.kt
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ public expect class LocalTime : Comparable<LocalTime> {
* (for example, [second] is 60), consider using [DateTimeComponents.Format] instead.
*
* There is a collection of predefined formats in [LocalTime.Formats].
*
* @throws IllegalArgumentException if parsing using this format is ambiguous.
*/
@Suppress("FunctionName")
public fun Format(builder: DateTimeFormatBuilder.WithTime.() -> Unit): DateTimeFormat<LocalTime>
Expand Down
2 changes: 2 additions & 0 deletions core/common/src/UtcOffset.kt
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ public expect class UtcOffset {
* [DateTimeFormatBuilder.WithUtcOffset.offset] in a format builder for a larger data structure.
*
* There is a collection of predefined formats in [UtcOffset.Formats].
*
* @throws IllegalArgumentException if parsing using this format is ambiguous.
*/
@Suppress("FunctionName")
public fun Format(block: DateTimeFormatBuilder.WithUtcOffset.() -> Unit): DateTimeFormat<UtcOffset>
Expand Down
2 changes: 2 additions & 0 deletions core/common/src/format/DateTimeComponents.kt
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ public class DateTimeComponents internal constructor(internal val contents: Date
* Creates a [DateTimeFormat] for [DateTimeComponents] values using [DateTimeFormatBuilder.WithDateTimeComponents].
*
* There is a collection of predefined formats in [DateTimeComponents.Formats].
*
* @throws IllegalArgumentException if parsing using this format is ambiguous.
*/
@Suppress("FunctionName")
public fun Format(block: DateTimeFormatBuilder.WithDateTimeComponents.() -> Unit): DateTimeFormat<DateTimeComponents> {
Expand Down
5 changes: 4 additions & 1 deletion core/common/src/format/DateTimeFormat.kt
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,10 @@ internal sealed class AbstractDateTimeFormat<T, U : Copyable<U>> : DateTimeForma
try {
return valueFromIntermediate(matched)
} catch (e: IllegalArgumentException) {
throw DateTimeFormatException(e.message!!)
throw DateTimeFormatException(when (val message = e.message) {
null -> "The value parsed from '$input' is invalid"
else -> "$message (when parsing '$input')"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it's worth adding a test or an overload of assertParsingFails that ensures that the input is present in the message

}, e)
}
}

Expand Down
4 changes: 2 additions & 2 deletions core/common/src/internal/format/FieldFormatDirective.kt
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ internal abstract class NamedUnsignedIntFieldFormatDirective<in Target>(
override fun parser(): ParserStructure<Target> =
ParserStructure(
listOf(
StringSetParserOperation(values, AssignableString(), "One of $values for $name")
StringSetParserOperation(values, AssignableString(), "one of $values for $name")
), emptyList()
)
}
Expand Down Expand Up @@ -142,7 +142,7 @@ internal abstract class NamedEnumIntFieldFormatDirective<in Target, Type>(
override fun parser(): ParserStructure<Target> =
ParserStructure(
listOf(
StringSetParserOperation(mapping.values, AssignableString(), "One of ${mapping.values} for $name")
StringSetParserOperation(mapping.values, AssignableString(), "one of ${mapping.values} for $name")
), emptyList()
)
}
Expand Down
4 changes: 2 additions & 2 deletions core/common/src/internal/format/FormatStructure.kt
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,8 @@ internal open class ConcatenatedFormatStructure<in T>(

internal class CachedFormatStructure<in T>(formats: List<NonConcatenatedFormatStructure<T>>) :
ConcatenatedFormatStructure<T>(formats) {
private val cachedFormatter: FormatterStructure<T> by lazy { super.formatter() }
private val cachedParser: ParserStructure<T> by lazy { super.parser() }
private val cachedFormatter: FormatterStructure<T> = super.formatter()
private val cachedParser: ParserStructure<T> = super.parser()

override fun formatter(): FormatterStructure<T> = cachedFormatter

Expand Down
7 changes: 6 additions & 1 deletion core/common/src/internal/format/parser/ParserOperation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,12 @@ internal class NumberSpanParserOperation<Output>(

init {
require(consumers.all { (it.length ?: Int.MAX_VALUE) > 0 })
qwwdfsad marked this conversation as resolved.
Show resolved Hide resolved
require(consumers.count { it.length == null } <= 1)
require(consumers.count { it.length == null } <= 1) {
val fieldNames = consumers.filter { it.length == null }.map { it.whatThisExpects }
"At most one variable-length numeric field in a row is allowed, but got several: $fieldNames. " +
qwwdfsad marked this conversation as resolved.
Show resolved Hide resolved
"Parsing is undefined: for example, with variable-length month number " +
"and variable-length day of month, '111' can be parsed as Jan 11th or Nov 1st."
}
}

private val whatThisExpects: String
Expand Down
10 changes: 10 additions & 0 deletions core/common/test/format/DateTimeFormatTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,14 @@ class DateTimeFormatTest {
DateTimeComponents.Format { chars(format) }.parse(format)
}
}

@Test
fun testCreatingAmbiguousFormat() {
assertFailsWith< IllegalArgumentException> {
DateTimeComponents.Format {
monthNumber(Padding.NONE)
dayOfMonth(Padding.NONE)
}
}
}
}