Skip to content

Commit cbb292d

Browse files
committed
Start moving function
1 parent f8a93bc commit cbb292d

File tree

7 files changed

+156
-136
lines changed

7 files changed

+156
-136
lines changed

parser/parse_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,14 +197,14 @@ func TestParseFilter(t *testing.T) {
197197
a := assert.New(t)
198198
r := require.New(t)
199199
reg := registry.New()
200-
_ = reg.Register(registry.NewFunction(
200+
_ = reg.Register(
201201
"__true",
202202
spec.FuncLogical,
203203
func([]spec.FuncExprArg) error { return nil },
204204
func([]spec.JSONPathValue) spec.JSONPathValue {
205205
return spec.LogicalTrue
206206
},
207-
))
207+
)
208208
trueFunc := reg.Get("__true")
209209

210210
for _, tc := range []struct {

path_example_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,12 +237,12 @@ func ExampleParser() {
237237
func ExampleWithRegistry() {
238238
// Register the first function.
239239
reg := registry.New()
240-
err := reg.Register(registry.NewFunction(
240+
err := reg.Register(
241241
"first", // name
242242
spec.FuncValue, // returns a single value
243243
validateFirstArgs, // parse-time validation defined below
244244
firstFunc, // function defined below
245-
))
245+
)
246246
if err != nil {
247247
log.Fatalf("Error %v", err)
248248
}

registry/registry.go

Lines changed: 26 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import (
1515
// [RFC 9535]: https://www.rfc-editor.org/rfc/rfc9535.html
1616
type Registry struct {
1717
mu sync.RWMutex
18-
funcs map[string]spec.PathFunction
18+
funcs map[string]*spec.Func
1919
}
2020

2121
// New returns a new [Registry] loaded with the [RFC 9535]-mandated functions:
@@ -35,104 +35,52 @@ type Registry struct {
3535
func New() *Registry {
3636
return &Registry{
3737
mu: sync.RWMutex{},
38-
funcs: map[string]spec.PathFunction{
39-
"length": NewFunction("length", spec.FuncValue, checkLengthArgs, lengthFunc),
40-
"count": NewFunction("count", spec.FuncValue, checkCountArgs, countFunc),
41-
"value": NewFunction("value", spec.FuncValue, checkValueArgs, valueFunc),
42-
"match": NewFunction("match", spec.FuncLogical, checkMatchArgs, matchFunc),
43-
"search": NewFunction("search", spec.FuncLogical, checkSearchArgs, searchFunc),
38+
funcs: map[string]*spec.Func{
39+
"length": spec.NewFunction("length", spec.FuncValue, checkLengthArgs, lengthFunc),
40+
"count": spec.NewFunction("count", spec.FuncValue, checkCountArgs, countFunc),
41+
"value": spec.NewFunction("value", spec.FuncValue, checkValueArgs, valueFunc),
42+
"match": spec.NewFunction("match", spec.FuncLogical, checkMatchArgs, matchFunc),
43+
"search": spec.NewFunction("search", spec.FuncLogical, checkSearchArgs, searchFunc),
4444
},
4545
}
4646
}
4747

48-
// Validator functions validate that the args expressions to a function can be
49-
// processed by the function.
50-
type Validator func(args []spec.FuncExprArg) error
51-
52-
// Evaluator functions execute a function against the values returned by args
53-
// and returns a result.
54-
type Evaluator func(args []spec.JSONPathValue) spec.JSONPathValue
55-
5648
// ErrRegister errors are returned by [Register].
5749
var ErrRegister = errors.New("register")
5850

59-
// Register registers a function extension by its name. Returns an
60-
// [ErrRegister] error if r already contains the name of fn.
61-
func (r *Registry) Register(fn spec.PathFunction) error {
51+
// Register registers a function extension by its name. Returns [ErrRegister]
52+
// if validator or nil or evaluator is nil or if r already contains name.
53+
func (r *Registry) Register(
54+
name string,
55+
resultType spec.FuncType,
56+
validator spec.Validator,
57+
evaluator spec.Evaluator,
58+
) error {
59+
if validator == nil {
60+
return fmt.Errorf("%w: validator is nil", ErrRegister)
61+
}
62+
if evaluator == nil {
63+
return fmt.Errorf("%w: evaluator is nil", ErrRegister)
64+
}
65+
6266
r.mu.Lock()
6367
defer r.mu.Unlock()
64-
if _, dup := r.funcs[fn.Name()]; dup {
68+
if _, dup := r.funcs[name]; dup {
6569
return fmt.Errorf(
6670
"%w: Register called twice for function %v",
67-
ErrRegister, fn.Name(),
71+
ErrRegister, name,
6872
)
6973
}
7074

71-
r.funcs[fn.Name()] = fn
75+
r.funcs[name] = spec.NewFunction(name, resultType, validator, evaluator)
7276
return nil
7377
}
7478

7579
// Get returns a reference to the registered function named name. Returns nil
7680
// if no function with that name has been registered.
77-
//
78-
//nolint:ireturn
79-
func (r *Registry) Get(name string) spec.PathFunction {
81+
func (r *Registry) Get(name string) *spec.Func {
8082
r.mu.RLock()
8183
defer r.mu.RUnlock()
8284
function := r.funcs[name]
8385
return function
8486
}
85-
86-
// Function defines a JSONPath function. Use [Register] to register a new
87-
// function. Implements [spec.PathFunction].
88-
type Function struct {
89-
// name is the name of the function. Must be unique among all functions in
90-
// a registry.
91-
name string
92-
93-
// resultType defines the type of the function return value.
94-
resultType spec.FuncType
95-
96-
// validator executes at parse time to validate that all the args to
97-
// the function are compatible with the function.
98-
validator Validator
99-
100-
// evaluator executes the function and returns the result of type
101-
// resultType.
102-
evaluator Evaluator
103-
}
104-
105-
// NewFunction creates a new JSONPath function extension. The parameters are:
106-
//
107-
// - name: the name of the function as used in JSONPath queries.
108-
// - resultType: The data type of the function return value.
109-
// - validator: A validation function that will be called by at parse time
110-
// to validate that all the function args are compatible with the function.
111-
// - evaluator: The implementation of the function itself that executes the
112-
// against args and returns the result defined by resultType.
113-
func NewFunction(
114-
name string,
115-
resultType spec.FuncType,
116-
validator func(args []spec.FuncExprArg) error,
117-
evaluator func(args []spec.JSONPathValue) spec.JSONPathValue,
118-
) *Function {
119-
return &Function{name, resultType, validator, evaluator}
120-
}
121-
122-
// Name returns the name of the function.
123-
func (f *Function) Name() string { return f.name }
124-
125-
// ResultType returns the data type of the function return value.
126-
func (f *Function) ResultType() spec.FuncType { return f.resultType }
127-
128-
// Evaluate executes the function against args and returns the result of type
129-
// [ResultType].
130-
func (f *Function) Evaluate(args []spec.JSONPathValue) spec.JSONPathValue {
131-
return f.evaluator(args)
132-
}
133-
134-
// Validate executes at parse time to validate that all the args to the
135-
// function are compatible with the function.
136-
func (f *Function) Validate(args []spec.FuncExprArg) error {
137-
return f.validator((args))
138-
}

registry/registry_example_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ import (
1414
// [github.com/theory/jsonpath.WithRegistry] for a more complete example.
1515
func Example() {
1616
reg := registry.New()
17-
err := reg.Register(registry.NewFunction(
17+
err := reg.Register(
1818
"first", // function name
1919
spec.FuncValue, // returns a single value
2020
validateFirstArgs, // parse-time validation defined below
2121
firstFunc, // function defined below
22-
))
22+
)
2323
if err != nil {
2424
log.Fatalf("Error %v", err)
2525
}
@@ -30,12 +30,12 @@ func Example() {
3030
// validateFirstArgs validates that a single argument is passed to the first()
3131
// function, and that it can be converted to [spec.NodesType], so that first()
3232
// can return the first node. It's called by the parser.
33-
func validateFirstArgs(fea []spec.FuncExprArg) error {
34-
if len(fea) != 1 {
35-
return fmt.Errorf("expected 1 argument but found %v", len(fea))
33+
func validateFirstArgs(args []spec.FuncExprArg) error {
34+
if len(args) != 1 {
35+
return fmt.Errorf("expected 1 argument but found %v", len(args))
3636
}
3737

38-
if !fea[0].ResultType().ConvertsToNodes() {
38+
if !args[0].ResultType().ConvertsToNodes() {
3939
return errors.New("cannot convert argument to Nodes")
4040
}
4141

registry/registry_test.go

Lines changed: 3 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package registry
22

33
import (
4-
"errors"
54
"testing"
65

76
"github.com/stretchr/testify/assert"
@@ -80,8 +79,8 @@ func TestRegisterErr(t *testing.T) {
8079
for _, tc := range []struct {
8180
name string
8281
fnName string
83-
valid Validator
84-
eval Evaluator
82+
valid spec.Validator
83+
eval spec.Evaluator
8584
err string
8685
}{
8786
{
@@ -94,53 +93,9 @@ func TestRegisterErr(t *testing.T) {
9493
} {
9594
t.Run(tc.name, func(t *testing.T) {
9695
t.Parallel()
97-
err := reg.Register(
98-
NewFunction(tc.fnName, spec.FuncValue, tc.valid, tc.eval),
99-
)
96+
err := reg.Register(tc.fnName, spec.FuncValue, tc.valid, tc.eval)
10097
r.ErrorIs(err, ErrRegister, tc.name)
10198
r.EqualError(err, tc.err, tc.name)
10299
})
103100
}
104101
}
105-
106-
func TestFunction(t *testing.T) {
107-
t.Parallel()
108-
a := assert.New(t)
109-
110-
for _, tc := range []struct {
111-
name string
112-
fn *Function
113-
args []spec.JSONPathValue
114-
err error
115-
exp spec.JSONPathValue
116-
}{
117-
{
118-
name: "valid_err_value",
119-
fn: NewFunction(
120-
"xyz", spec.FuncValue,
121-
func([]spec.FuncExprArg) error { return errors.New("oops") },
122-
func([]spec.JSONPathValue) spec.JSONPathValue { return spec.Value(42) },
123-
),
124-
args: []spec.JSONPathValue{},
125-
exp: spec.Value(42),
126-
err: errors.New("oops"),
127-
},
128-
{
129-
name: "no_valid_err_nodes",
130-
fn: NewFunction(
131-
"abc", spec.FuncNodes,
132-
func([]spec.FuncExprArg) error { return nil },
133-
func([]spec.JSONPathValue) spec.JSONPathValue { return spec.Nodes("hi") },
134-
),
135-
args: []spec.JSONPathValue{},
136-
exp: spec.Nodes("hi"),
137-
},
138-
} {
139-
t.Run(tc.name, func(t *testing.T) {
140-
t.Parallel()
141-
a.Equal(tc.fn.name, tc.fn.Name())
142-
a.Equal(tc.err, tc.fn.Validate(nil))
143-
a.Equal(tc.exp, tc.fn.Evaluate(tc.args))
144-
})
145-
}
146-
}

spec/function.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,18 @@ import (
88
"strings"
99
)
1010

11+
// ResultType blah blah.
12+
type ResultType uint8
13+
14+
const (
15+
// ResultValue blah blah.
16+
ResultValue ResultType = iota + 1 // Value
17+
// ResultLogical blah blah.
18+
ResultLogical // Logical
19+
// ResultNodes blah blah.
20+
ResultNodes // Nodes
21+
)
22+
1123
// FuncType describes the types of function parameters and results for the
1224
// purpose of validating function parameters.
1325
//
@@ -476,6 +488,68 @@ func (fq *NodesQueryExpr) writeTo(buf *strings.Builder) {
476488
buf.WriteString(fq.String())
477489
}
478490

491+
// Validator functions validate that the args expressions to a function can be
492+
// processed by the function.
493+
type Validator func(args []FuncExprArg) error
494+
495+
// Evaluator functions execute a function against the values returned by args
496+
// and returns a result.
497+
type Evaluator func(args []JSONPathValue) JSONPathValue
498+
499+
// Func defines a JSONPath function. Use [Register] to register a new
500+
// function. Implements [PathFunction].
501+
type Func struct {
502+
// name is the name of the function. Must be unique among all functions in
503+
// a registry.
504+
name string
505+
506+
// resultType defines the type of the function return value.
507+
resultType FuncType
508+
509+
// validator executes at parse time to validate that all the args to
510+
// the function are compatible with the function.
511+
validator Validator
512+
513+
// evaluator executes the function and returns the result of type
514+
// resultType.
515+
evaluator Evaluator
516+
}
517+
518+
// NewFunction creates a new JSONPath function extension. The parameters are:
519+
//
520+
// - name: the name of the function as used in JSONPath queries.
521+
// - resultType: The data type of the function return value.
522+
// - validator: A validation function that will be called by at parse time
523+
// to validate that all the function args are compatible with the function.
524+
// - evaluator: The implementation of the function itself that executes the
525+
// against args and returns the result defined by resultType.
526+
func NewFunction(
527+
name string,
528+
resultType FuncType,
529+
validator Validator,
530+
evaluator Evaluator,
531+
) *Func {
532+
return &Func{name, resultType, validator, evaluator}
533+
}
534+
535+
// Name returns the name of the function.
536+
func (f *Func) Name() string { return f.name }
537+
538+
// ResultType returns the data type of the function return value.
539+
func (f *Func) ResultType() FuncType { return f.resultType }
540+
541+
// Evaluate executes the function against args and returns the result of type
542+
// [ResultType].
543+
func (f *Func) Evaluate(args []JSONPathValue) JSONPathValue {
544+
return f.evaluator(args)
545+
}
546+
547+
// Validate executes at parse time to validate that all the args to the
548+
// function are compatible with the function.
549+
func (f *Func) Validate(args []FuncExprArg) error {
550+
return f.validator(args)
551+
}
552+
479553
// PathFunction represents a JSONPath function. Implemented by
480554
// [github.com/theory/jsonpath/registry.Function].
481555
type PathFunction interface {

0 commit comments

Comments
 (0)