Hi Marc,
Thanks for the heads up! Here's a brief outline of the problem, and solution I've come up with.
Currently, it's possible to import a "mode" in Rapture which determines the return type of standard calls which might fail, for example,
import modes.returnTry._
myJson.as[CaseClass] // returns Try[CaseClass]
import modes.returnFuture._
myJson.as[CaseClass] // returns Future[CaseClass]
import modes.returnEither._
myJson.as[CaseClass] // returns Either[JsonGetException, CaseClass]
This all works well, though rather than getting the first JsonGetException that occurs, we would like to be able to be able to collect *all* the errors that occur when extracting a `CaseClass`.
Unfortunately, this wasn't possible, because errors were dealt with as thrown exceptions, which may or may not be caught, but either way resulted in processing being aborted at the point of the first error.
I've changed this so that modes may define their own "throw" method. By default, it will continue to throw, but it enables implementations that register the exceptions as a side-effect, return `null` (because we have to return something), and attempts to carry on. When we return a value to the user, the `null`s don't matter because we've already established that there's been at least one failure.
I've called the result `Outcome`, with two subtypes: `Result` (for success) and `Problems` (containing all the issues that occurred).
Using this new mode style does require methods which use modes to be rewritten, and I've implemented as a test when implementing the Rapture CSV library, which is similar in some ways to Rapture JSON.
Note that the Gist also demonstrates how errors can be extracted from a `Problems` type based on their type (and indeed how the type of `Problems` encodes the exceptions it may contain).
So, Marc, this is the first step in getting what you need. The next step is getting this working on Rapture JSON, and then I will need to work out how to get extraction to potentially cause validation exceptions.
One way I've found to do this is to use artificial marker traits to drive type inference to use alternative extractors, like this:
trait UpperCase
implicit def extractor: Extractor[String with UpperCase, Json] =
Json.extractor[String].filter(_.forall(_.isUpper))
case class Foo(str: String, upperStr: String with UpperCase, upperStr2: String with UpperCase)
import modes.returnOutcome._
json"""{ "str": "Hello world", "upperStr": "lower!", "upperStr2": "x" }""".as[Foo]
In this particular example, validation should fail, so I'd expect to get back something like this:
val res: Outcome[Foo, DataGetException] =
Problems(
rapture.data.ValidationException:
validation failed accessing <value>.upperStr
validation failed accessing <value>.upperStr2
)
That's roughly it.
It's only a brief outline of how it will work, but I think I can get this working. :D
Cheers,
Jon