diff --git a/cmd/dasel/main.go b/cmd/dasel/main.go index 651331a..0dc8346 100644 --- a/cmd/dasel/main.go +++ b/cmd/dasel/main.go @@ -1,6 +1,7 @@ package main import ( + "io" "os" "github.com/tomwright/dasel/v3/internal/cli" @@ -11,5 +12,10 @@ import ( ) func main() { - cli.MustRun(os.Stdin, os.Stdout, os.Stderr) + var stdin io.Reader = os.Stdin + fi, err := os.Stdin.Stat() + if err != nil || (fi.Mode()&os.ModeNamedPipe == 0) { + stdin = nil + } + cli.MustRun(stdin, os.Stdout, os.Stderr) } diff --git a/execution/func.go b/execution/func.go index 5d8e749..504a8af 100644 --- a/execution/func.go +++ b/execution/func.go @@ -15,6 +15,8 @@ var ( FuncMerge, FuncReverse, FuncTypeOf, + FuncMax, + FuncMin, ) ) diff --git a/execution/func_max.go b/execution/func_max.go new file mode 100644 index 0000000..9de230c --- /dev/null +++ b/execution/func_max.go @@ -0,0 +1,32 @@ +package execution + +import ( + "github.com/tomwright/dasel/v3/model" +) + +// FuncMax is a function that returns the highest number. +var FuncMax = NewFunc( + "max", + func(data *model.Value, args model.Values) (*model.Value, error) { + res := model.NewNullValue() + for _, arg := range args { + if res.IsNull() { + res = arg + continue + } + gt, err := arg.GreaterThan(res) + if err != nil { + return nil, err + } + gtBool, err := gt.BoolValue() + if err != nil { + return nil, err + } + if gtBool { + res = arg + } + } + return res, nil + }, + ValidateArgsMin(1), +) diff --git a/execution/func_max_test.go b/execution/func_max_test.go new file mode 100644 index 0000000..d006368 --- /dev/null +++ b/execution/func_max_test.go @@ -0,0 +1,22 @@ +package execution_test + +import ( + "testing" + + "github.com/tomwright/dasel/v3/model" +) + +func TestFuncMax(t *testing.T) { + t.Run("int", testCase{ + s: `max(1, 2, 3)`, + out: model.NewIntValue(3), + }.run) + t.Run("float", testCase{ + s: `max(1f, 2.5, 3.5)`, + out: model.NewFloatValue(3.5), + }.run) + t.Run("mixed", testCase{ + s: `max(1, 2f)`, + out: model.NewFloatValue(2), + }.run) +} diff --git a/execution/func_min.go b/execution/func_min.go new file mode 100644 index 0000000..e45af42 --- /dev/null +++ b/execution/func_min.go @@ -0,0 +1,32 @@ +package execution + +import ( + "github.com/tomwright/dasel/v3/model" +) + +// FuncMin is a function that returns the smalled number. +var FuncMin = NewFunc( + "min", + func(data *model.Value, args model.Values) (*model.Value, error) { + res := model.NewNullValue() + for _, arg := range args { + if res.IsNull() { + res = arg + continue + } + lt, err := arg.LessThan(res) + if err != nil { + return nil, err + } + ltBool, err := lt.BoolValue() + if err != nil { + return nil, err + } + if ltBool { + res = arg + } + } + return res, nil + }, + ValidateArgsMin(1), +) diff --git a/execution/func_min_test.go b/execution/func_min_test.go new file mode 100644 index 0000000..74f54be --- /dev/null +++ b/execution/func_min_test.go @@ -0,0 +1,22 @@ +package execution_test + +import ( + "testing" + + "github.com/tomwright/dasel/v3/model" +) + +func TestFuncMin(t *testing.T) { + t.Run("int", testCase{ + s: `min(1, 2, 3)`, + out: model.NewIntValue(1), + }.run) + t.Run("float", testCase{ + s: `min(1f, 2.5, 3.5)`, + out: model.NewFloatValue(1), + }.run) + t.Run("mixed", testCase{ + s: `min(1, 2f)`, + out: model.NewIntValue(1), + }.run) +} diff --git a/model/value.go b/model/value.go index 82b9205..623edc9 100644 --- a/model/value.go +++ b/model/value.go @@ -128,6 +128,21 @@ func (v *Value) UnpackUntilKind(k reflect.Kind) (*Value, error) { } } +// UnpackUntilKinds unpacks the reflect value until it matches the given kind. +func (v *Value) UnpackUntilKinds(kinds ...reflect.Kind) (*Value, error) { + res := v.Value + for { + if slices.Contains(kinds, res.Kind()) { + return NewValue(res), nil + } + if res.Kind() == reflect.Interface || res.Kind() == reflect.Ptr && !res.IsNil() { + res = res.Elem() + continue + } + return nil, fmt.Errorf("could not unpack to kinds: %v", kinds) + } +} + // Type returns the type of the value. func (v *Value) Type() Type { switch { diff --git a/model/value_literal.go b/model/value_literal.go index 7b7d079..fd3a1bd 100644 --- a/model/value_literal.go +++ b/model/value_literal.go @@ -20,7 +20,12 @@ func (v *Value) IsNull() bool { } func (v *Value) isNull() bool { - return v.Value.IsNil() + // This logic can be cleaned up. + unpacked, err := v.UnpackUntilKinds(reflect.Chan, reflect.Func, reflect.Map, reflect.Pointer, reflect.UnsafePointer, reflect.Interface, reflect.Slice) + if err != nil { + return false + } + return unpacked.Value.IsNil() } // NewStringValue creates a new Value with a string value.