44 "errors"
55 "fmt"
66 "github.com/go-playground/itertools"
7+ syncext "github.com/go-playground/pkg/v5/sync"
78 resultext "github.com/go-playground/pkg/v5/values/result"
89 "io"
910 "reflect"
@@ -17,6 +18,86 @@ import (
1718 "github.com/tidwall/gjson"
1819)
1920
21+ var (
22+ // Coercions is a `map` of all coercions guarded by a Mutex for use allowing registration,
23+ // removal or even replacing of existing coercions.
24+ Coercions = syncext .NewRWMutex2 (map [string ]func (constEligible bool , expression Expression ) (stillConstEligible bool , e Expression , err error ){
25+ "_datetime_" : func (constEligible bool , expression Expression ) (stillConstEligible bool , e Expression , err error ) {
26+ expression = coerceDateTime {value : expression }
27+ if constEligible {
28+ value , err := expression .Calculate ([]byte {})
29+ if err != nil {
30+ return false , nil , err
31+ }
32+ return constEligible , coercedConstant {value : value }, nil
33+ } else {
34+ return false , expression , nil
35+ }
36+ },
37+ "_lowercase_" : func (constEligible bool , expression Expression ) (stillConstEligible bool , e Expression , err error ) {
38+ expression = coerceLowercase {value : expression }
39+ if constEligible {
40+ value , err := expression .Calculate ([]byte {})
41+ if err != nil {
42+ return false , nil , err
43+ }
44+ return constEligible , coercedConstant {value : value }, nil
45+ } else {
46+ return false , expression , nil
47+ }
48+ },
49+ "_string_" : func (constEligible bool , expression Expression ) (stillConstEligible bool , e Expression , err error ) {
50+ expression = coerceString {value : expression }
51+ if constEligible {
52+ value , err := expression .Calculate ([]byte {})
53+ if err != nil {
54+ return false , nil , err
55+ }
56+ return constEligible , coercedConstant {value : value }, nil
57+ } else {
58+ return false , expression , nil
59+ }
60+ },
61+ "_number_" : func (constEligible bool , expression Expression ) (stillConstEligible bool , e Expression , err error ) {
62+ expression = coerceNumber {value : expression }
63+ if constEligible {
64+ value , err := expression .Calculate ([]byte {})
65+ if err != nil {
66+ return false , nil , err
67+ }
68+ return constEligible , coercedConstant {value : value }, nil
69+ } else {
70+ return false , expression , nil
71+ }
72+ },
73+ "_uppercase_" : func (constEligible bool , expression Expression ) (stillConstEligible bool , e Expression , err error ) {
74+ expression = coerceUppercase {value : expression }
75+ if constEligible {
76+ value , err := expression .Calculate ([]byte {})
77+ if err != nil {
78+ return false , nil , err
79+ }
80+ return constEligible , coercedConstant {value : value }, nil
81+ } else {
82+ return false , expression , nil
83+ }
84+ },
85+ "_title_" : func (constEligible bool , expression Expression ) (stillConstEligible bool , e Expression , err error ) {
86+ expression = coerceTitle {value : expression }
87+ if constEligible {
88+ value , err := expression .Calculate ([]byte {})
89+ if err != nil {
90+ return false , nil , err
91+ }
92+ return constEligible , coercedConstant {value : value }, nil
93+ } else {
94+ return false , expression , nil
95+ }
96+ },
97+ })
98+ )
99+
100+ // Expression Represents a stateless parsed expression that can be applied to JSON data.
20101type Expression interface {
21102
22103 // Calculate will execute the parsed expression and apply it against the supplied data.
@@ -196,29 +277,19 @@ func (p *parser) parseValue(token Token) (Expression, error) {
196277 return nil , fmt .Errorf ("COERCE missing data type identifier, found instead: %s" , identifier )
197278 }
198279
199- switch identifier {
200- case "_datetime_" :
201- expression = coerceDateTime {value : expression }
202- if constEligible {
203- value , err := expression .Calculate ([]byte {})
204- if err != nil {
205- return nil , err
206- }
207- expression = coercedConstant {value : value }
280+ guard := Coercions .RLock ()
281+ fn , found := guard .T [identifier ]
282+ guard .RUnlock ()
283+
284+ if found {
285+ constEligible , expression , err = fn (constEligible , expression )
286+ if err != nil {
287+ return nil , err
208288 }
209- case "_lowercase_" :
210- expression = coerceLowercase {value : expression }
211- case "_string_" :
212- expression = coerceString {value : expression }
213- case "_number_" :
214- expression = coerceNumber {value : expression }
215- case "_uppercase_" :
216- expression = coerceUppercase {value : expression }
217- case "_title_" :
218- expression = coerceTitle {value : expression }
219- default :
289+ } else {
220290 return nil , fmt .Errorf ("invalid COERCE data type '%s'" , identifier )
221291 }
292+
222293 nextPeeked := p .tokenizer .Peek ()
223294 if nextPeeked .IsSome () && nextPeeked .Unwrap ().IsOk () && nextPeeked .Unwrap ().Unwrap ().kind == Comma {
224295 _ = p .tokenizer .Next () // consume peeked comma
0 commit comments