Clean up and consolidate error types #159
Labels
acceptance: go ahead
Reviewed, implementation can start
area: library
Improvements to the library API quality
type: reliability
Tests, code quality
Milestone
Is your feature request related to a problem? Please describe.
Lately I've posted on r/rust to get folks' opinion on whether error kind enums should be
non_exhaustive
or not.A lot of water was shed about best error practices under that post.
https://www.reddit.com/r/rust/comments/13p5ipy/should_error_enums_be_non_exhaustive/
No matter anyone's take on the issue, I think our current errors are miserable, and we should try to consolidate them.
Currently there are four different "top-level" error types in the crate.
ParserError
CompilerError
InputError
EngineError
(of note,EngineError::InputError
encapsulates anInputError
)There's also
ArrayIndexError
, which is a bit special and created specifically for theNonNegativeArrayIndex
type.This is endemic of a larger issue that we have, namely that the library public API surface is way too wide. We allow extending basically every core part of rsonpath, with the exception of the classifier. This is something that needs to be tackled for v1.0.0, since we sure as hell don't want to force ourselves to support it as-is until the next major version.
We should expect users of the library to be similar to users of other query-language libraries. I imagine the 99% use case to be that you have some query you want to run or have it supplied as a string, and some input you read from a file or from memory. Then you
From that perspective, I would care only about two kinds of errors - error during step 1. or step 2. It makes sense to have both, since they represent two completely different scenarios with different ways of handling.
From a programmer's perspective when using the lib dealing with those errors, there are three cases that require potentially different codepaths that arise from the above deliberations:
In none of those cases does the code writer care whether the error with the document is
DepthBelowZero
orDepthAboveLimit
. There's nothing actionable they can do with either choice, why would they give a damn.Describe the solution you'd like
My current vision is as follows:
QueryError
an opaque struct with a niceDisplay
implementation. The user of the lib doesn't care if the error was an "internal nom error" or something else, they just want to tell the user what the error was and how they can fix it.CompilerError
intoEngineError
and renameEngineError
toRsonpathError
. This is going to be the main error type for most of the public API. Make it an enum with two kinds:InputError
,EngineError
.The first will indicate an error with the input, the same as currently. The second one will be an opaque struct with a
kind()
function, returning one of two kinds:NotSupported
andDocumentError
, telling the user that either the document is incorrect or that whatever they're trying to do is not supported with current version ofrsonpath
. The kinds are non-exhaustive. Nothing else will be publicly exposed. TheInputError
type will only be raised publicly viaRsonpathError
, and as something you should return for a customInput
implementation.ArrayIndexError
can be removed. It currently has two cases, one of which is obsolete. Error-handling here can be done the same ways as the stdlib handles similar types, likeNonZeroUsize
, so by returning anOption<Self>
withNone
if the bound was exceeded.This achieves a couple of things.
QueryError
don't give any actionable decisions for the code that calls the parser, the only thing you can do with it is display it.InputError
are also important, as anIoError
can potentially be handled and retried, whileAllocationSizeExceeded
is presumably fatal. In the future we might want to have input types that have configurable heap size limits, and exceeding that could also be handled in different ways from anIoError
.The
EngineError
can give you more info on what exactly happened under the presumption that the consumer might want to know, but we're giving no stability guarantees on the enum. Also the realm of possible things will be limited to "the JSON is borked" and "we don't support this", nothing more specific. If we ever need more kinds we can put them there, for example "we exceeded the memory limit of the stack" or something like that. But crucially, we assume 99% of the time the users won't care, and if they do then they should be aware that it's basically implementation details that might be unstable.The key property that we want to keep when going forward is that we should expose different error variants only if a programmer using the lib should do different things for different variants.
The text was updated successfully, but these errors were encountered: