Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 0 additions & 20 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,6 @@ permissions:
contents: read

jobs:
format:
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: stable

- name: Set up gofumpt
run: go install mvdan.cc/gofumpt@latest

- name: Run gofumpt
run: |
non_formatted_files="$(gofumpt -l .)"
echo "$non_formatted_files"
test -z "$non_formatted_files"

golangci-lint:
runs-on: ubuntu-latest

Expand Down
5 changes: 5 additions & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
version: "2"

formatters:
enable:
- gofumpt

linters:
enable:
- makezero
Expand Down
24 changes: 22 additions & 2 deletions args.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,9 @@ func (a *ArgumentsBase[T, C, VC]) Get() any {
}

type (
FloatArg = ArgumentBase[float64, NoConfig, floatValue]
FloatArg = ArgumentBase[float64, NoConfig, floatValue[float64]]
Float32Arg = ArgumentBase[float32, NoConfig, floatValue[float32]]
Float64Arg = ArgumentBase[float64, NoConfig, floatValue[float64]]
IntArg = ArgumentBase[int, IntegerConfig, intValue[int]]
Int8Arg = ArgumentBase[int8, IntegerConfig, intValue[int8]]
Int16Arg = ArgumentBase[int16, IntegerConfig, intValue[int16]]
Expand All @@ -241,7 +243,9 @@ type (
Uint32Arg = ArgumentBase[uint32, IntegerConfig, uintValue[uint32]]
Uint64Arg = ArgumentBase[uint64, IntegerConfig, uintValue[uint64]]

FloatArgs = ArgumentsBase[float64, NoConfig, floatValue]
FloatArgs = ArgumentsBase[float64, NoConfig, floatValue[float64]]
Float32Args = ArgumentsBase[float32, NoConfig, floatValue[float32]]
Float64Args = ArgumentsBase[float64, NoConfig, floatValue[float64]]
IntArgs = ArgumentsBase[int, IntegerConfig, intValue[int]]
Int8Args = ArgumentsBase[int8, IntegerConfig, intValue[int8]]
Int16Args = ArgumentsBase[int16, IntegerConfig, intValue[int16]]
Expand Down Expand Up @@ -293,6 +297,22 @@ func (c *Command) FloatArgs(name string) []float64 {
return arg[[]float64](name, c)
}

func (c *Command) Float32Arg(name string) float32 {
return arg[float32](name, c)
}

func (c *Command) Float32Args(name string) []float32 {
return arg[[]float32](name, c)
}

func (c *Command) Float64Arg(name string) float64 {
return arg[float64](name, c)
}

func (c *Command) Float64Args(name string) []float64 {
return arg[[]float64](name, c)
}

func (c *Command) IntArg(name string) int {
return arg[int](name, c)
}
Expand Down
50 changes: 50 additions & 0 deletions args_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,33 @@ import (
"github.com/stretchr/testify/require"
)

func TestArgsFloatTypes(t *testing.T) {
cmd := buildMinimalTestCommand()
var fval float64
cmd.Arguments = []Argument{
&FloatArg{
Name: "ia",
Destination: &fval,
},
}

err := cmd.Run(buildTestContext(t), []string{"foo", "10"})
r := require.New(t)
r.NoError(err)
r.Equal(float64(10), fval)
r.Equal(float64(10), cmd.FloatArg("ia"))
r.Equal(float64(10), cmd.Float64Arg("ia"))
r.Equal(float32(0), cmd.Float32Arg("ia"))
r.Equal(float64(0), cmd.FloatArg("iab"))
r.Equal(int8(0), cmd.Int8Arg("ia"))
r.Equal(int16(0), cmd.Int16Arg("ia"))
r.Equal(int32(0), cmd.Int32Arg("ia"))
r.Equal(int64(0), cmd.Int64Arg("ia"))
r.Empty(cmd.StringArg("ia"))

r.Error(cmd.Run(buildTestContext(t), []string{"foo", "a"}))
}

func TestArgsIntTypes(t *testing.T) {
cmd := buildMinimalTestCommand()
var ival int
Expand All @@ -34,6 +61,29 @@ func TestArgsIntTypes(t *testing.T) {
r.Error(cmd.Run(buildTestContext(t), []string{"foo", "10.0"}))
}

func TestArgsFloatSliceTypes(t *testing.T) {
cmd := buildMinimalTestCommand()
var fval []float64
cmd.Arguments = []Argument{
&FloatArgs{
Name: "ia",
Min: 1,
Max: -1,
Destination: &fval,
},
}

err := cmd.Run(buildTestContext(t), []string{"foo", "10", "20", "30"})
r := require.New(t)
r.NoError(err)
r.Equal([]float64{10, 20, 30}, fval)
r.Equal([]float64{10, 20, 30}, cmd.FloatArgs("ia"))
r.Equal([]float64{10, 20, 30}, cmd.Float64Args("ia"))
r.Nil(cmd.Float32Args("ia"))

r.Error(cmd.Run(buildTestContext(t), []string{"foo", "10", "a"}))
}

func TestArgsIntSliceTypes(t *testing.T) {
cmd := buildMinimalTestCommand()
var ival []int
Expand Down
2 changes: 2 additions & 0 deletions docs/v3/examples/flags/basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ The following basic flags are supported
- `BoolFlag`
- `DurationFlag`
- `FloatFlag`
- `Float32Flag`
- `Float64Flag`
- `StringFlag`
- `TimestampFlag`

Expand Down
55 changes: 41 additions & 14 deletions flag_float.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,71 @@ package cli

import (
"strconv"
"unsafe"
)

type FloatFlag = FlagBase[float64, NoConfig, floatValue]
type (
FloatFlag = FlagBase[float64, NoConfig, floatValue[float64]]
Float32Flag = FlagBase[float32, NoConfig, floatValue[float32]]
Float64Flag = FlagBase[float64, NoConfig, floatValue[float64]]
)

// -- float64 Value
type floatValue float64
// -- float Value
type floatValue[T float32 | float64] struct {
val *T
}

// Below functions are to satisfy the ValueCreator interface

func (f floatValue) Create(val float64, p *float64, c NoConfig) Value {
func (f floatValue[T]) Create(val T, p *T, c NoConfig) Value {
*p = val
return (*floatValue)(p)

return &floatValue[T]{val: p}
}

func (f floatValue) ToString(b float64) string {
return strconv.FormatFloat(b, 'g', -1, 64)
func (f floatValue[T]) ToString(b T) string {
return strconv.FormatFloat(float64(b), 'g', -1, int(unsafe.Sizeof(T(0))*8))
}

// Below functions are to satisfy the flag.Value interface

func (f *floatValue) Set(s string) error {
v, err := strconv.ParseFloat(s, 64)
func (f *floatValue[T]) Set(s string) error {
v, err := strconv.ParseFloat(s, int(unsafe.Sizeof(T(0))*8))
if err != nil {
return err
}
*f = floatValue(v)
return err
*f.val = T(v)
return nil
}

func (f *floatValue) Get() any { return float64(*f) }
func (f *floatValue[T]) Get() any { return *f.val }

func (f *floatValue) String() string { return strconv.FormatFloat(float64(*f), 'g', -1, 64) }
func (f *floatValue[T]) String() string {
return strconv.FormatFloat(float64(*f.val), 'g', -1, int(unsafe.Sizeof(T(0))*8))
}

// Float looks up the value of a local FloatFlag, returns
// 0 if not found
func (cmd *Command) Float(name string) float64 {
if v, ok := cmd.Value(name).(float64); ok {
return getFloat[float64](cmd, name)
}

// Float32 looks up the value of a local Float32Flag, returns
// 0 if not found
func (cmd *Command) Float32(name string) float32 {
return getFloat[float32](cmd, name)
}

// Float64 looks up the value of a local Float64Flag, returns
// 0 if not found
func (cmd *Command) Float64(name string) float64 {
return getFloat[float64](cmd, name)
}

func getFloat[T float32 | float64](cmd *Command, name string) T {
if v, ok := cmd.Value(name).(T); ok {
tracef("float available for flag name %[1]q with value=%[2]v (cmd=%[3]q)", name, v, cmd.Name)

return v
}

Expand Down
32 changes: 23 additions & 9 deletions flag_float_slice.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,34 @@
package cli

type (
FloatSlice = SliceBase[float64, NoConfig, floatValue]
FloatSliceFlag = FlagBase[[]float64, NoConfig, FloatSlice]
FloatSlice = SliceBase[float64, NoConfig, floatValue[float64]]
Float32Slice = SliceBase[float32, NoConfig, floatValue[float32]]
Float64Slice = SliceBase[float64, NoConfig, floatValue[float64]]
FloatSliceFlag = FlagBase[[]float64, NoConfig, FloatSlice]
Float32SliceFlag = FlagBase[[]float32, NoConfig, Float32Slice]
Float64SliceFlag = FlagBase[[]float64, NoConfig, Float64Slice]
)

var NewFloatSlice = NewSliceBase[float64, NoConfig, floatValue]
var (
NewFloatSlice = NewSliceBase[float64, NoConfig, floatValue[float64]]
NewFloat32Slice = NewSliceBase[float32, NoConfig, floatValue[float32]]
NewFloat64Slice = NewSliceBase[float64, NoConfig, floatValue[float64]]
)

// FloatSlice looks up the value of a local FloatSliceFlag, returns
// nil if not found
func (cmd *Command) FloatSlice(name string) []float64 {
if v, ok := cmd.Value(name).([]float64); ok {
tracef("float slice available for flag name %[1]q with value=%[2]v (cmd=%[3]q)", name, v, cmd.Name)
return v
}
return getNumberSlice[float64](cmd, name)
}

// Float32Slice looks up the value of a local Float32Slice, returns
// nil if not found
func (cmd *Command) Float32Slice(name string) []float32 {
return getNumberSlice[float32](cmd, name)
}

tracef("float slice NOT available for flag name %[1]q (cmd=%[2]q)", name, cmd.Name)
return nil
// Float64Slice looks up the value of a local Float64SliceFlag, returns
// nil if not found
func (cmd *Command) Float64Slice(name string) []float64 {
return getNumberSlice[float64](cmd, name)
}
Loading