pkg/math: added MultipleOf
Also:
- add support for Decimal type (internal)
- add correspondng support in openapi package
Change-Id: Idc4b829423bba63380a8edfc1109c345f4248355
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/2640
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/builtin.go b/cue/builtin.go
index dccb441..08fdcfc 100644
--- a/cue/builtin.go
+++ b/cue/builtin.go
@@ -28,6 +28,7 @@
"cuelang.org/go/cue/errors"
"cuelang.org/go/cue/parser"
"cuelang.org/go/internal"
+ "github.com/cockroachdb/apd/v2"
)
// A builtin is a builtin function or constant.
@@ -393,6 +394,15 @@
return res
}
+func (c *callCtxt) decimal(i int) *apd.Decimal {
+ x := newValueRoot(c.ctx, c.args[i])
+ if _, err := x.MantExp(nil); err != nil {
+ c.invalidArgType(c.args[i], i, "Decimal", err)
+ return nil
+ }
+ return &c.args[i].(*numLit).v
+}
+
func (c *callCtxt) float64(i int) float64 {
x := newValueRoot(c.ctx, c.args[i])
res, err := x.Float64()
diff --git a/cue/builtin_test.go b/cue/builtin_test.go
index d93bd05..b710f73 100644
--- a/cue/builtin_test.go
+++ b/cue/builtin_test.go
@@ -90,6 +90,21 @@
test("math", `math.Asin(2.0e400)`),
`_|_(cannot use 2.0e+400 (type float) as float64 in argument 0 to math.Asin: value was rounded up)`,
}, {
+ test("math", `math.MultipleOf(4, 2)`), `true`,
+ }, {
+ test("math", `math.MultipleOf(5, 2)`), `false`,
+ }, {
+ test("math", `math.MultipleOf(5, 0)`),
+ `_|_(error in call to math.MultipleOf: division by zero)`,
+ }, {
+ test("math", `math.MultipleOf(100, 1.00001)`), `false`,
+ }, {
+ test("math", `math.MultipleOf(1, 1)`), `true`,
+ }, {
+ test("math", `math.MultipleOf(5, 2.5)`), `true`,
+ }, {
+ test("math", `math.MultipleOf(100e100, 10)`), `true`,
+ }, {
test("encoding/csv", `csv.Decode("1,2,3\n4,5,6")`),
`[["1","2","3"],["4","5","6"]]`,
}, {
diff --git a/cue/builtins.go b/cue/builtins.go
index 1fd88a3..1a73490 100644
--- a/cue/builtins.go
+++ b/cue/builtins.go
@@ -28,6 +28,7 @@
"cuelang.org/go/cue/literal"
"cuelang.org/go/cue/parser"
"cuelang.org/go/internal/third_party/yaml"
+ "github.com/cockroachdb/apd/v2"
goyaml "github.com/ghodss/yaml"
)
@@ -37,6 +38,8 @@
var _ io.Reader
+var mulContext = apd.BaseContext.WithPrecision(1)
+
var split = path.Split
var pathClean = path.Clean
@@ -564,6 +567,18 @@
}()
},
}, {
+ Name: "MultipleOf",
+ Params: []kind{numKind, numKind},
+ Result: boolKind,
+ Func: func(c *callCtxt) {
+ x, y := c.decimal(0), c.decimal(1)
+ c.ret, c.err = func() (interface{}, error) {
+ var d apd.Decimal
+ cond, err := mulContext.Quo(&d, x, y)
+ return !cond.Inexact(), err
+ }()
+ },
+ }, {
Name: "Abs",
Params: []kind{numKind},
Result: numKind,
@@ -1506,6 +1521,7 @@
Func: func(c *callCtxt) {
s, max := c.string(0), c.int(1)
c.ret = func() interface{} {
+
return len([]rune(s)) <= max
}()
},
diff --git a/cue/gen.go b/cue/gen.go
index 6e91f03..3fd9e80 100644
--- a/cue/gen.go
+++ b/cue/gen.go
@@ -388,6 +388,8 @@
return "bigFloat"
case "big.Rat":
return "bigRat"
+ case "internal.Decimal":
+ return "decimal"
case "cue.Value":
return "value"
case "cue.List":
@@ -419,7 +421,7 @@
"uint", "byte", "uint8", "uint16", "uint32", "uint64",
"bigInt":
cueKind += "intKind"
- case "float64", "bigRat", "bigFloat":
+ case "float64", "bigRat", "bigFloat", "decimal":
cueKind += "numKind"
case "list":
cueKind += "listKind"
diff --git a/encoding/openapi/build.go b/encoding/openapi/build.go
index 8387c72..d2315fa 100644
--- a/encoding/openapi/build.go
+++ b/encoding/openapi/build.go
@@ -435,7 +435,7 @@
// object supports the following
// - maxProperties: maximum allowed fields in this struct.
-// - minProperties: minimum required fields in this struct.a
+// - minProperties: minimum required fields in this struct.
// - patternProperties: [regexp]: schema
// TODO: we can support this once .kv(key, value) allow
// foo [=~"pattern"]: type
@@ -599,6 +599,18 @@
b.kv("maxItems", i),
})
+ case cue.CallOp:
+ name := fmt.Sprint(a[0])
+ switch name {
+ case "math.MultipleOf":
+ if len(a) != 2 {
+ b.failf(v, "builtin %v may only be used with single argument", name)
+ }
+ b.setFilter("Schema", "multipleOf", b.int(a[1]))
+ default:
+ b.failf(v, "builtin %v not supported in OpenAPI", name)
+ }
+
case cue.NoOp:
// TODO: extract format from specific type.
diff --git a/encoding/openapi/openapi_test.go b/encoding/openapi/openapi_test.go
index b085f75..430b5d4 100644
--- a/encoding/openapi/openapi_test.go
+++ b/encoding/openapi/openapi_test.go
@@ -55,6 +55,10 @@
"strings.json",
defaultConfig,
}, {
+ "nums.cue",
+ "nums.json",
+ defaultConfig,
+ }, {
"oneof.cue",
"oneof.json",
defaultConfig,
diff --git a/encoding/openapi/testdata/nums.cue b/encoding/openapi/testdata/nums.cue
new file mode 100644
index 0000000..a20b36b
--- /dev/null
+++ b/encoding/openapi/testdata/nums.cue
@@ -0,0 +1,3 @@
+import "math"
+
+mul: math.MultipleOf(5)
diff --git a/encoding/openapi/testdata/nums.json b/encoding/openapi/testdata/nums.json
new file mode 100644
index 0000000..819d5e8
--- /dev/null
+++ b/encoding/openapi/testdata/nums.json
@@ -0,0 +1,12 @@
+{
+ "openapi": "3.0.0",
+ "info": {},
+ "components": {
+ "schemas": {
+ "mul": {
+ "type": "number",
+ "multipleOf": 5
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/internal/internal.go b/internal/internal.go
index 6133bac..261b9d6 100644
--- a/internal/internal.go
+++ b/internal/internal.go
@@ -16,6 +16,13 @@
// TODO: refactor packages as to make this package unnecessary.
+import "github.com/cockroachdb/apd/v2"
+
+// A Decimal is an arbitrary-precision binary-coded decimal number.
+//
+// Right now Decimal is aliased to apd.Decimal. This may change in the future.
+type Decimal = apd.Decimal
+
// DebugStr prints a syntax node.
var DebugStr func(x interface{}) string
diff --git a/pkg/math/manual.go b/pkg/math/manual.go
index 493f8c5..361a6c1 100644
--- a/pkg/math/manual.go
+++ b/pkg/math/manual.go
@@ -14,7 +14,12 @@
package math
-import "math"
+import (
+ "math"
+
+ "cuelang.org/go/internal"
+ "github.com/cockroachdb/apd/v2"
+)
// TODO: use apd
@@ -67,3 +72,12 @@
func RoundToEven(x float64) float64 {
return math.RoundToEven(x)
}
+
+var mulContext = apd.BaseContext.WithPrecision(1)
+
+// MultipleOf reports whether x is a multiple of y.
+func MultipleOf(x, y *internal.Decimal) (bool, error) {
+ var d apd.Decimal
+ cond, err := mulContext.Quo(&d, x, y)
+ return !cond.Inexact(), err
+}