GroupDecoder
The following types have GroupDecoder
instances available out of the box:
java.util.Date
There 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)
Either
For 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))
Option
For 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)
MatchDecoder
Any 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))
Either
For 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)))
Option
For 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.