GroupDecoderThe following types have GroupDecoder instances available out of the box:
java.util.DateThere also is a default GroupDecoder instance available for Date, but this one is slightly more complicated.
There are so many different ways of writing dates that there is no reasonable default behaviour - one might argue that
defaulting to ISO 8601 might make sense, but there doesn’t appear to be a sane way of implementing that in Java’s crusty
date / time API.
Instead of providing a default implementation that is likely going to be incorrect for most people, kantan.regex
provides easy tools for creating decoders from an instance of DateFormat.
We could for example declare a decoder for something ISO 8601-like:
import kantan.regex.implicits._
import kantan.regex.GroupDecoder
import java.text.SimpleDateFormat
import java.util.{Locale, Date}
implicit val dateDecoder: GroupDecoder[Date] = GroupDecoder.dateDecoder(new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH))
And we’re now capable of decoding matches as dates:
"2000-01-00T00:00:00.000".evalRegex[Date](rx"\d\d\d\d-\d\d-\d\d").foreach(println _)
// Right(Fri Dec 31 00:00:00 CET 1999)
EitherFor any two types A and B that each have a GroupDecoder, there exists a
GroupDecoder[Either[A, B]].
This is useful for dodgy string data where the type of a value is not well defined - it might sometimes be an int, sometimes a boolean, for example:
"[123] [true]".evalRegex[Either[Int, Boolean]](rx"\[(\d+|true|false)\]", 1).foreach(println _)
// Right(Left(123))
// Right(Right(true))
OptionFor any type A that has a GroupDecoder, there exists a GroupDecoder[Option[A]].
This is particularly useful for optional groups. For example:
"[123], []".evalRegex[Option[Int]](rx"\[(\d+)?\]", 1).foreach(println _)
// Right(Some(123))
// Right(None)
MatchDecoderAny type A such that A has a GroupDecoder also has a MatchDecoder.
Note that this can sometimes be a bit tricky, as it involves selecting the group that’s the most likely to be the
correct one. Kantan.regex will consider that, unless explicitly instructed otherwise, turning a GroupDecoder into
a MatchDecoder is done by looking at the entire match (that is, group 0).
That is in fact what was happening in most examples above: evalRegex expects a MatchDecoder, not a
GroupDecoder.
Tuples composed of types that each have a GroupDecoder automatically have a MatchDecoder. This is done by
assuming that the value of group 1 corresponds to the first field in the tuple, group 2 to the second, …
"[1, true] and then [3, false]".evalRegex[(Int, Boolean)](rx"\[(\d+), ([a-z]+)\]").foreach(println _)
// Right((1,true))
// Right((3,false))
EitherFor any two types A and B that each have a MatchDecoder, there exists a
MatchDecoder[Either[A, B]].
This works essentially the same way as GroupDecoder for Either:
"[123, true] [456, foo]".evalRegex[Either[(Int, Boolean), (Int, String)]](rx"\[(\d+), ([a-z]+)\]").foreach(println _)
// Right(Left((123,true)))
// Right(Right((456,foo)))
OptionFor any type A that has a MatchDecoder, there exists a MatchDecoder[Option[A]].
Now, I know for a fact this works - I have tests for it. I just can’t really think of an actual use case for it - the notion of an optional match is… odd.