Currently, the only way to handle when a validation fails is by
`assert()`, which either throws an exception or doesn't. That means that
every time a user wants to handle the results, they must use try-catch
blocks, which may add some overhead.
This commit introduces the `ResultQuery` class that wraps a `Result`
object, providing an alternative to exception-based validation. This
allows users to handle results directly without try-catch blocks.
I’m taking a risky move here using the old method `validate()`, but I
can’t think of a better name for this method.
Both `ArrayFormatter` and `StringFormatter` accept an instance of the
`Translator`. Thinking about it a bit better, I realised that a
formatter might not always need a `Translator`, but it will surely need
a `Renderer`.
Besides, the `InterpolationRenderer` needs to take translation into
account, so it seems more natural to me that this is the one that will
get an instance of the `Translator`, as other implementations of the
`Renderer` might not even deal with translations.
I've noticed that the `StandardFormatter` was quite bloated, which made
it difficult to maintain. Understanding what each method was doing was
quite complicated. Besides, the name "Standard" doesn't mean anything,
because it doesn't say what the implementation does.
I split the `Formatter` into two different interfaces: `StringFormatter`
and `ArrayFormatter`, and I moved some code around:
* `StandardFormatter::main()` -> `FirstResultStringFormatter`
* `StandardFormatter::full()` -> `NestedListStringFormatter`
* `StandardFormatter::array()` -> `NestedArrayFormatter`
That opens up new ways of handling error messages, potentially
introducing features like `JsonStringFormatter` or `FlatArrayFormatter`
in the future.
While working on this, I removed a significant amount of unnecessary
code, which also improved my overall understanding of those formatters.
I'm not very happy with all the methods in `ValidatorDefaults`, but I
will refactor that later.