Skip to content

Commit

Permalink
topdown: Add units.parse builtin.
Browse files Browse the repository at this point in the history
This function works on all base decimal and binary SI units of the set:

    m, K/Ki, M/Mi, G/Gi, T/Ti, P/Pi, and E/Ei

Note: Unlike `units.parse_bytes`, this function is case sensitive.

Fixes open-policy-agent#1802.

Signed-off-by: Philip Conrad <philipaconrad@gmail.com>
  • Loading branch information
philipaconrad committed May 11, 2022
1 parent e0b9c12 commit b5d5c86
Show file tree
Hide file tree
Showing 8 changed files with 771 additions and 8 deletions.
13 changes: 13 additions & 0 deletions ast/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ var DefaultBuiltins = [...]*Builtin{
GlobQuoteMeta,

// Units
UnitsParse,
UnitsParseBytes,

// UUIDs
Expand Down Expand Up @@ -1123,6 +1124,18 @@ var NumbersRange = &Builtin{
* Units
*/

// UnitsParse converts strings like 10G, 5K, 4M, 1500m and the like into a
// number. This number can be a non-integer, such as 1.5, 0.22, etc.
var UnitsParse = &Builtin{
Name: "units.parse",
Decl: types.NewFunction(
types.Args(
types.S,
),
types.N,
),
}

// UnitsParseBytes converts strings like 10GB, 5K, 4mb, and the like into an
// integer number of bytes.
var UnitsParseBytes = &Builtin{
Expand Down
14 changes: 14 additions & 0 deletions capabilities.json
Original file line number Diff line number Diff line change
Expand Up @@ -3627,6 +3627,20 @@
"type": "function"
}
},
{
"name": "units.parse",
"decl": {
"args": [
{
"type": "string"
}
],
"result": {
"type": "number"
},
"type": "function"
}
},
{
"name": "units.parse_bytes",
"decl": {
Expand Down
1 change: 1 addition & 0 deletions docs/content/policy-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,7 @@ The following table shows examples of how ``glob.match`` works:

| Built-in | Description | Wasm Support |
| ------- |-------------|---------------|
| <span class="opa-keep-it-together">``output := units.parse(x)``</span> | ``output`` is ``x`` converted to a number with support for standard metric decimal and binary SI units (e.g., K, Ki, M, Mi, G, Gi etc.) m, K, M, G, T, P, and E are treated as decimal units and Ki, Mi, Gi, Ti, Pi, and Ei are treated as binary units. Note that 'm' and 'M' are case-sensitive, to allow distinguishing between "milli" and "mega" units respectively. Other units are case-insensitive. | ``SDK-dependent`` |
| <span class="opa-keep-it-together">``output := units.parse_bytes(x)``</span> | ``output`` is ``x`` converted to a number with support for standard byte units (e.g., KB, KiB, etc.) KB, MB, GB, and TB are treated as decimal units and KiB, MiB, GiB, and TiB are treated as binary units. The bytes symbol (b/B) in the unit is optional and omitting it wil give the same result (e.g. Mi and MiB) | ``SDK-dependent`` |

### Types
Expand Down
111 changes: 111 additions & 0 deletions test/cases/testdata/units/test-parse-units-comparisons.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
cases:
- data:
modules:
- |
package test
p {
units.parse("8k") > units.parse("7k")
}
note: parse/comparison
query: data.test.p = x
want_result:
- x: true
- data:
modules:
- |
package test
p {
units.parse("8g") > units.parse("8m")
}
note: parse/comparison
query: data.test.p = x
want_result:
- x: true
- data:
modules:
- |
package test
p {
units.parse("1234k") < units.parse("1g")
}
note: parse/comparison
query: data.test.p = x
want_result:
- x: true
- data:
modules:
- |
package test
p {
units.parse("1024") == units.parse("1Ki")
}
note: parse/comparison
query: data.test.p = x
want_result:
- x: true
- data:
modules:
- |
package test
p {
units.parse("2Mi") == units.parse("2097152")
}
note: parse/comparison
query: data.test.p = x
want_result:
- x: true
- data:
modules:
- |
package test
p {
units.parse("3Mi") > units.parse("3M")
}
note: parse/comparison
query: data.test.p = x
want_result:
- x: true
- data:
modules:
- |
package test
p {
units.parse("2Mi") == units.parse("2Mi")
}
note: parse/comparison
query: data.test.p = x
want_result:
- x: true
- data:
modules:
- |
package test
p {
units.parse("4Mi") > units.parse("4M")
}
note: parse/comparison
query: data.test.p = x
want_result:
- x: true
- data:
modules:
- |
package test
p {
units.parse("4.1Mi") > units.parse("4Mi")
}
note: parse/comparison
query: data.test.p = x
want_result:
- x: true
- data:
modules:
- |
package test
p {
units.parse("128Gi") == units.parse("137438953472")
}
note: parse/comparison
query: data.test.p = x
want_result:
- x: true
78 changes: 78 additions & 0 deletions test/cases/testdata/units/test-parse-units-errors.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
cases:
- data:
modules:
- |
package test
p {
units.parse("")
}
note: parse/failure
query: data.test.p = x
want_error: "units.parse error: no amount provided"
strict_error: true
- data:
modules:
- |
package test
p {
units.parse("G")
}
note: parse/failure
query: data.test.p = x
want_error: "units.parse error: no amount provided"
strict_error: true
- data:
modules:
- |
package test
p {
units.parse("foo")
}
note: parse/failure
query: data.test.p = x
want_error: "units.parse error: no amount provided"
strict_error: true
- data:
modules:
- |
package test
p {
units.parse("0.0.0")
}
note: parse/failure
query: data.test.p = x
want_error: "units.parse error: could not parse amount to a number"
strict_error: true
- data:
modules:
- |
package test
p {
units.parse(".5.2")
}
note: parse/failure
query: data.test.p = x
want_error: "units.parse error: could not parse amount to a number"
strict_error: true
- data:
modules:
- |
package test
p {
units.parse("100 k")
}
note: parse/failure
query: data.test.p = x
want_error: "units.parse error: spaces not allowed in resource strings"
strict_error: true
- data:
modules:
- |
package test
p {
units.parse(" 327Mi ")
}
note: parse/failure
query: data.test.p = x
want_error: "units.parse error: spaces not allowed in resource strings"
strict_error: true
Loading

0 comments on commit b5d5c86

Please sign in to comment.