Skip to content

Commit

Permalink
✨ version(.., type: "semver")
Browse files Browse the repository at this point in the history
```
cnquery> version("1.2.3", type: 'semver')
version: 1.2.3

cnquery> version("1:1.2.3", type: 'semver')
version '1:1.2.3' is not a semantic version
version: 1:1.2.3

cnquery> version("1:1.2.3")
version: 1:1.2.3
```

Also adds support for parsing named arguments on builtin type conversion
functions (like `version`).

Signed-off-by: Dominik Richter <dominik.richter@gmail.com>
  • Loading branch information
arlimus committed Feb 12, 2025
1 parent d48ae6e commit 638b9e7
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 10 deletions.
45 changes: 42 additions & 3 deletions llx/builtin_global.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package llx
import (
"errors"
"strconv"
"strings"

"go.mondoo.com/cnquery/v11/types"
)
Expand Down Expand Up @@ -197,14 +198,52 @@ func typeofCallV2(e *blockExecutor, f *Function, ref uint64) (*RawData, uint64,
}

func versionCall(e *blockExecutor, f *Function, ref uint64) (*RawData, uint64, error) {
if len(f.Args) != 1 {
return nil, 0, errors.New("Called `version` with " + strconv.Itoa(len(f.Args)) + " arguments, expected one")
if len(f.Args) == 0 {
return nil, 0, errors.New("called `version` with no arguments, expected one")
}

res, dref, err := e.resolveValue(f.Args[0], ref)
arg := f.Args[0]
if arg.Type != string(types.String) {
return nil, 0, errors.New("called `version` with incorrect argument type, expected string")
}

res, dref, err := e.resolveValue(arg, ref)
if err != nil || dref != 0 || res == nil {
return res, dref, err
}
raw, ok := res.Value.(string)
if !ok {
return nil, 0, errors.New("called `version` with unsupported type (expected string)")
}

version := NewVersion(raw)

for i := 1; i < len(f.Args); i++ {
arg := f.Args[i]
if !types.Type(arg.Type).IsMap() {
return nil, 0, errors.New("called `version` with unknown argument, expected one string")
}
for k, v := range arg.Map {
switch k {
case "type":
t, ok := v.RawData().Value.(string)
if !ok {
return nil, 0, errors.New("unsupported `type` value in `version` call")
}
typ := strings.ToLower(t)
switch typ {
case "semver":
if !version.IsSemver() {
return &RawData{Error: errors.New("version '" + raw + "' is not a semantic version"), Value: raw, Type: types.Version}, 0, nil
}
case "all":
break
default:
return nil, 0, errors.New("unauppoerws `type=" + t + "` in `version` call")
}
}
}
}

return &RawData{Type: types.Version, Value: res.Value}, 0, nil
}
Expand Down
4 changes: 4 additions & 0 deletions llx/builtin_version.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ func NewVersion(s string) Version {
}
}

func (v *Version) IsSemver() bool {
return v.Version != nil && v.epoch == 0
}

func parseEpoch(v string) (*semver.Version, int) {
prefix := reEpoch.FindString(v)
if prefix == "" {
Expand Down
27 changes: 20 additions & 7 deletions mqlc/typemaps.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,35 @@ func compileTypeConversion(llxID string, typ types.Type) fieldCompiler {
return types.Nil, errNotConversion
}

arg := call.Function[0]
if arg == nil || arg.Value == nil || arg.Value.Operand == nil || arg.Value.Operand.Value == nil {
return types.Nil, errors.New("failed to get parameter for '" + id + "'")
otherMap := map[string]*llx.Primitive{}
args := []*llx.Primitive{}
for i := range call.Function {
arg := call.Function[i]
if arg == nil || arg.Value == nil || arg.Value.Operand == nil || arg.Value.Operand.Value == nil {
return types.Nil, errors.New("failed to get parameter for '" + id + "'")
}

argValue, err := c.compileExpression(arg.Value)
if err != nil {
return types.Nil, err
}
if arg.Name != "" {
otherMap[arg.Name] = argValue
} else {
args = append(args, argValue)
}
}

argValue, err := c.compileExpression(arg.Value)
if err != nil {
return types.Nil, err
if len(otherMap) != 0 {
args = append(args, llx.MapPrimitive(otherMap, types.Any))
}

c.addChunk(&llx.Chunk{
Call: llx.Chunk_FUNCTION,
Id: llxID,
Function: &llx.Function{
Type: string(typ),
Args: []*llx.Primitive{argValue},
Args: args,
},
})

Expand Down
15 changes: 15 additions & 0 deletions providers/core/resources/mql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -997,6 +997,21 @@ func TestVersion(t *testing.T) {
},
})
})

t.Run("version type set to semver", func(t *testing.T) {
x.TestSimple(t, []testutils.SimpleTest{
{
Code: "version('1.2.3', type: 'semver')",
ResultIndex: 2, Expectation: "1.2.3.",
},
})
x.TestSimpleErrors(t, []testutils.SimpleTest{
{
Code: "version('1:1.2.3', type: 'semver')",
ResultIndex: 2, Expectation: "error",
},
})
})
}

func TestResource_Default(t *testing.T) {
Expand Down

0 comments on commit 638b9e7

Please sign in to comment.