func TestEventEqual(t *testing.T) { a := ast.NewValueMap() a.Put(ast.String("foo"), ast.Number("1")) b := ast.NewValueMap() b.Put(ast.String("foo"), ast.Number("2")) tests := []struct { a *Event b *Event equal bool }{ {&Event{}, &Event{}, true}, {&Event{Op: EvalOp}, &Event{Op: EnterOp}, false}, {&Event{QueryID: 1}, &Event{QueryID: 2}, false}, {&Event{ParentID: 1}, &Event{ParentID: 2}, false}, {&Event{Node: ast.MustParseBody("true")}, &Event{Node: ast.MustParseBody("false")}, false}, {&Event{Node: ast.MustParseBody("true")[0]}, &Event{Node: ast.MustParseBody("false")[0]}, false}, {&Event{Node: ast.MustParseRule("p :- true")}, &Event{Node: ast.MustParseRule("p :- false")}, false}, {&Event{Node: "foo"}, &Event{Node: "foo"}, false}, // test some unsupported node type } for _, tc := range tests { if tc.a.Equal(tc.b) != tc.equal { var s string if tc.equal { s = "==" } else { s = "!=" } t.Errorf("Expected %v %v %v", tc.a, s, tc.b) } } }
func loadExpectedBindings(input string) []*ast.ValueMap { var data []map[string]interface{} if err := util.UnmarshalJSON([]byte(input), &data); err != nil { panic(err) } var expected []*ast.ValueMap for _, bindings := range data { buf := ast.NewValueMap() for k, v := range bindings { switch v := v.(type) { case string: buf.Put(ast.Var(k), ast.String(v)) case json.Number: buf.Put(ast.Var(k), ast.Number(v)) default: panic("unreachable") } } expected = append(expected, buf) } return expected }
// evalToNumber implements the BuiltinFunc type to provide support for casting // values to numbers. For details on the signature, see builtins.go. This // function will only be called by evaluation enigne when the expression refers // to the "to_number" built-in. func evalToNumber(t *Topdown, expr *ast.Expr, iter Iterator) error { // Step 1. unpack the expression to get access to the operands. The Terms // contains the parsed expression: ["to_number", <value>, <output>] ops := expr.Terms.([]*ast.Term) a, b := ops[1].Value, ops[2].Value // Step 2. convert input to native Go value. This is a common step for // built-ins that are mostly pass-through to Go standard library functions. // ValueToInterface will handle references contained inside the value. If // your built-in function is specific to certain types (e.g., strings) see // the other variants of the ValueToInterface function at // https://godoc.org/github.com/open-policy-agent/opa/topdown. x, err := ValueToInterface(a, t) if err != nil { return errors.Wrapf(err, "to_number") } // Step 3. conversion logic. This logic is specific to this built-in. var n ast.Number switch x := x.(type) { case string: _, err := strconv.ParseFloat(string(x), 64) if err != nil { return errors.Wrapf(err, "to_number") } n = ast.Number(json.Number(x)) case json.Number: n = ast.Number(x) case bool: if x { n = ast.Number("1") } else { n = ast.Number("0") } default: return fmt.Errorf("to_number: source must be a string, boolean, or number: %T", a) } // Step 4. unify the result with the output value. If the output value is // ground then the unification will act as an equality test. If the output // value is non-ground (e.g., a variable), the unification will bind the // result to the variable. The unification will also invoke the iterator to // continue evaluation when necessary. // // Alternatively, if your built-in has no outputs, just call the iterator if // the expression evaluated successfully, for example: // // success := <logic> // // if success { // return iter(t) // } // // return nil undo, err := evalEqUnify(t, n, b, nil, iter) // Step 5. at this point, evaluation is backtracking so the bindings must be undone. t.Unbind(undo) // Step 6. finished, return error (which may be nil). return err }
func floatToASTNumber(f *big.Float) ast.Number { return ast.Number(floatToJSONNumber(f)) }