github.com/yaling888/clash@v1.53.0/component/script/expr_matcher.go (about) 1 package script 2 3 import ( 4 "fmt" 5 6 "github.com/expr-lang/expr" 7 "github.com/expr-lang/expr/vm" 8 "github.com/expr-lang/expr/vm/runtime" 9 10 C "github.com/yaling888/clash/constant" 11 ) 12 13 var inStringPatch = &stringInString{} 14 15 var _ C.Matcher = (*ExprMatcher)(nil) 16 17 type ExprMatcher struct { 18 name string 19 hasNow bool 20 program *vm.Program 21 } 22 23 func (e *ExprMatcher) Name() string { 24 return e.name 25 } 26 27 func (*ExprMatcher) Eval(*C.Metadata) (string, error) { 28 panic("unimplemented") 29 } 30 31 func (e *ExprMatcher) Match(mtd *C.Metadata) (bool, error) { 32 env := parseEnv(mtd, e.hasNow) 33 34 result, err := expr.Run(e.program, env) 35 if err != nil { 36 return false, err 37 } 38 39 if v, ok := result.(bool); ok { 40 return v, nil 41 } 42 43 return false, fmt.Errorf("invalid return type, got %T, want bool", result) 44 } 45 46 func NewExprMatcher(name, code string) (*ExprMatcher, error) { 47 options := []expr.Option{ 48 expr.Env(shortcutEnvironment{}), 49 expr.Patch(inStringPatch), 50 expr.DisableBuiltin("now"), 51 expr.AsBool(), 52 } 53 54 program, err := expr.Compile(code, options...) 55 if err != nil { 56 return nil, fmt.Errorf("compile expr code error: %w", err) 57 } 58 59 var hasNow bool 60 for _, m := range program.Constants { 61 if f, ok := m.(*runtime.Field); ok { 62 if l := len(f.Path); l != 0 && f.Path[0] == "now" { 63 hasNow = true 64 break 65 } 66 } 67 } 68 69 return &ExprMatcher{ 70 name: name, 71 hasNow: hasNow, 72 program: program, 73 }, nil 74 }