The primary focus of this PR is the introduction of support for Function Extensions in JSONPath query strings (closes #1). In addition, a refactored error system was introduced (related to #4), the crate was converted to a workspace, and tracing was introduced.
Function Extensions
The JSONPath spec outlines Function Extensions. These allow for the use of functions within queries, e.g.,
$[? length(@.foo) > 5 ]
There is a standard register of functions, currently comprised of length, count, match, search, and value functions. There is also a type-system that the standard itself, as well as implementors of the standard, can use to extend JSONPath via the creation of additional functions.
In this PR, support for Function Extensions has been added via the functions module, as well as the #[function] attribute macro.
serde_json_path will now support the standard registered functions defined in the JSONPath specification. Currently that is limited to the five listed above, but will grow as the standard grows.
The Type System and Parse-time Function Validation
serde_json_path validates the use of functions in query strings at parse time. There is a concept of well-typedness in the standard that defines where functions, based on their signature, can be used within queries. These rules are enforced for functions by serde_json_path.
This PR therefore introduces three new types to support those defined in the standard: ValueType, NodesType, and LogicalType. These are used internally by serde_json_path to implement the standard registry functions, but are also exposed through the public API for users to use in their own custom functions, along with the #[function] attribute macro.
The #[function] Attribute Macro
The #[function] attribute macro was introduced to enable custom function registration in serde_json_path. This macro allows users of serde_json_path to define their own functions. To give an example, say you want to have a function called first() in your queries, that takes a NodeList, and returns the first node, if it is not empty. You can now accomplish this by adding the following somewhere in your application's code:
And that's it! Now you can use a function called first in your queries, i.e., the following query will be valid:
$[? first(@.*) == 42 ]
The macro will ensure that user-defined functions are valid at compile-time, and will generate the means to:
validate the use of this function at parse-time, i.e., when calling JsonPath::parse, and
evaluate the use of this function at query-time, i.e., when calling JsonPath::query.
This maintains the current status quo; namely, that JsonPath::parse is fallible, while JsonPath::query is not.
The macro's capability to register functions at compile time is made possible via the inventory crate.
For users that do not want to use the #[function] attribute macro, they can disable the functions feature.
Other Changes
Updated Error System
The introduction of functions brought with it the need for a custom parser error type; mainly, to propagate specific errors pertaining to function misuse in query strings at parse-time. This was inevitable (see #4), but needed to be addressed, so I did so here, and not in a future PR.
Therefore, the internal parser error system was updated by this PR. The public Error type was also altered so that it can provide more concise error messages, and better support the addition of more fine grained errors in future, without introducing breaking changes.
Overhaul of Repository Structure
To support the new #[function] attribute macro, the repository was converted to a workspace, comprised of the following members:
serde_json_path - the primary crate for general consumption
serde_json_path_macros - re-exports the #[function] attribute macro, and provides serde_json_path_macros_internal with external dependencies
serde_json_path_macros_internal - the implementation of the #[function] macro
serde_json_path_core - defines core types used in both serde_json_path and serde_json_path_macros/macros_internal
This means that serde_json_path now covers four separate crates.
The trace feature flag
To better support debugging of query parsing and evaluation, the use of tracing was introduced, and gated by the trace feature flag, which is not enabled by default. All parser functions and query evaluation functions are instrumented at the TRACE level, to enable debugging of query string parsing and evaluation.
This is largely to aid internal development efforts of serde_json_path, but may also prove useful to anyone attempting to define their own functions, or submit issues for parser bugs.
The primary focus of this PR is the introduction of support for Function Extensions in JSONPath query strings (closes #1). In addition, a refactored error system was introduced (related to #4), the crate was converted to a workspace, and tracing was introduced.
Function Extensions
The JSONPath spec outlines Function Extensions. These allow for the use of functions within queries, e.g.,
There is a standard register of functions, currently comprised of
length
,count
,match
,search
, andvalue
functions. There is also a type-system that the standard itself, as well as implementors of the standard, can use to extend JSONPath via the creation of additional functions.In this PR, support for Function Extensions has been added via the
functions
module, as well as the#[function]
attribute macro.serde_json_path
will now support the standard registered functions defined in the JSONPath specification. Currently that is limited to the five listed above, but will grow as the standard grows.The Type System and Parse-time Function Validation
serde_json_path
validates the use of functions in query strings at parse time. There is a concept of well-typedness in the standard that defines where functions, based on their signature, can be used within queries. These rules are enforced for functions byserde_json_path
.This PR therefore introduces three new types to support those defined in the standard:
ValueType
,NodesType
, andLogicalType
. These are used internally byserde_json_path
to implement the standard registry functions, but are also exposed through the public API for users to use in their own custom functions, along with the#[function]
attribute macro.The
#[function]
Attribute MacroThe
#[function]
attribute macro was introduced to enable custom function registration inserde_json_path
. This macro allows users ofserde_json_path
to define their own functions. To give an example, say you want to have a function calledfirst()
in your queries, that takes aNodeList
, and returns the first node, if it is not empty. You can now accomplish this by adding the following somewhere in your application's code:And that's it! Now you can use a function called
first
in your queries, i.e., the following query will be valid:The macro will ensure that user-defined functions are valid at compile-time, and will generate the means to:
JsonPath::parse
, andJsonPath::query
.This maintains the current status quo; namely, that
JsonPath::parse
is fallible, whileJsonPath::query
is not.The macro's capability to register functions at compile time is made possible via the
inventory
crate.For users that do not want to use the
#[function]
attribute macro, they can disable thefunctions
feature.Other Changes
Updated Error System
The introduction of functions brought with it the need for a custom parser error type; mainly, to propagate specific errors pertaining to function misuse in query strings at parse-time. This was inevitable (see #4), but needed to be addressed, so I did so here, and not in a future PR.
Therefore, the internal parser error system was updated by this PR. The public
Error
type was also altered so that it can provide more concise error messages, and better support the addition of more fine grained errors in future, without introducing breaking changes.Overhaul of Repository Structure
To support the new
#[function]
attribute macro, the repository was converted to a workspace, comprised of the following members:serde_json_path
- the primary crate for general consumptionserde_json_path_macros
- re-exports the#[function]
attribute macro, and providesserde_json_path_macros_internal
with external dependenciesserde_json_path_macros_internal
- the implementation of the#[function]
macroserde_json_path_core
- defines core types used in bothserde_json_path
andserde_json_path_macros
/macros_internal
This means that
serde_json_path
now covers four separate crates.The
trace
feature flagTo better support debugging of query parsing and evaluation, the use of
tracing
was introduced, and gated by thetrace
feature flag, which is not enabled by default. All parser functions and query evaluation functions are instrumented at theTRACE
level, to enable debugging of query string parsing and evaluation.This is largely to aid internal development efforts of
serde_json_path
, but may also prove useful to anyone attempting to define their own functions, or submit issues for parser bugs.