Skip to content

Commit de2b0d1

Browse files
authored
Merge pull request urfave#2112 from ldez/feat/float
feat: adds support for explicit `float32` and `float64`
2 parents 5357b6d + 55836e6 commit de2b0d1

15 files changed

Lines changed: 665 additions & 77 deletions

.github/workflows/lint.yml

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,6 @@ permissions:
1414
contents: read
1515

1616
jobs:
17-
format:
18-
runs-on: ubuntu-latest
19-
steps:
20-
- name: Clone repository
21-
uses: actions/checkout@v4
22-
23-
- name: Set up Go
24-
uses: actions/setup-go@v5
25-
with:
26-
go-version: stable
27-
28-
- name: Set up gofumpt
29-
run: go install mvdan.cc/gofumpt@latest
30-
31-
- name: Run gofumpt
32-
run: |
33-
non_formatted_files="$(gofumpt -l .)"
34-
echo "$non_formatted_files"
35-
test -z "$non_formatted_files"
36-
3717
golangci-lint:
3818
runs-on: ubuntu-latest
3919

.golangci.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
version: "2"
2+
3+
formatters:
4+
enable:
5+
- gofumpt
6+
27
linters:
38
enable:
49
- makezero

args.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,9 @@ func (a *ArgumentsBase[T, C, VC]) Get() any {
226226
}
227227

228228
type (
229-
FloatArg = ArgumentBase[float64, NoConfig, floatValue]
229+
FloatArg = ArgumentBase[float64, NoConfig, floatValue[float64]]
230+
Float32Arg = ArgumentBase[float32, NoConfig, floatValue[float32]]
231+
Float64Arg = ArgumentBase[float64, NoConfig, floatValue[float64]]
230232
IntArg = ArgumentBase[int, IntegerConfig, intValue[int]]
231233
Int8Arg = ArgumentBase[int8, IntegerConfig, intValue[int8]]
232234
Int16Arg = ArgumentBase[int16, IntegerConfig, intValue[int16]]
@@ -241,7 +243,9 @@ type (
241243
Uint32Arg = ArgumentBase[uint32, IntegerConfig, uintValue[uint32]]
242244
Uint64Arg = ArgumentBase[uint64, IntegerConfig, uintValue[uint64]]
243245

244-
FloatArgs = ArgumentsBase[float64, NoConfig, floatValue]
246+
FloatArgs = ArgumentsBase[float64, NoConfig, floatValue[float64]]
247+
Float32Args = ArgumentsBase[float32, NoConfig, floatValue[float32]]
248+
Float64Args = ArgumentsBase[float64, NoConfig, floatValue[float64]]
245249
IntArgs = ArgumentsBase[int, IntegerConfig, intValue[int]]
246250
Int8Args = ArgumentsBase[int8, IntegerConfig, intValue[int8]]
247251
Int16Args = ArgumentsBase[int16, IntegerConfig, intValue[int16]]
@@ -293,6 +297,22 @@ func (c *Command) FloatArgs(name string) []float64 {
293297
return arg[[]float64](name, c)
294298
}
295299

300+
func (c *Command) Float32Arg(name string) float32 {
301+
return arg[float32](name, c)
302+
}
303+
304+
func (c *Command) Float32Args(name string) []float32 {
305+
return arg[[]float32](name, c)
306+
}
307+
308+
func (c *Command) Float64Arg(name string) float64 {
309+
return arg[float64](name, c)
310+
}
311+
312+
func (c *Command) Float64Args(name string) []float64 {
313+
return arg[[]float64](name, c)
314+
}
315+
296316
func (c *Command) IntArg(name string) int {
297317
return arg[int](name, c)
298318
}

args_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,33 @@ import (
88
"github.com/stretchr/testify/require"
99
)
1010

11+
func TestArgsFloatTypes(t *testing.T) {
12+
cmd := buildMinimalTestCommand()
13+
var fval float64
14+
cmd.Arguments = []Argument{
15+
&FloatArg{
16+
Name: "ia",
17+
Destination: &fval,
18+
},
19+
}
20+
21+
err := cmd.Run(buildTestContext(t), []string{"foo", "10"})
22+
r := require.New(t)
23+
r.NoError(err)
24+
r.Equal(float64(10), fval)
25+
r.Equal(float64(10), cmd.FloatArg("ia"))
26+
r.Equal(float64(10), cmd.Float64Arg("ia"))
27+
r.Equal(float32(0), cmd.Float32Arg("ia"))
28+
r.Equal(float64(0), cmd.FloatArg("iab"))
29+
r.Equal(int8(0), cmd.Int8Arg("ia"))
30+
r.Equal(int16(0), cmd.Int16Arg("ia"))
31+
r.Equal(int32(0), cmd.Int32Arg("ia"))
32+
r.Equal(int64(0), cmd.Int64Arg("ia"))
33+
r.Empty(cmd.StringArg("ia"))
34+
35+
r.Error(cmd.Run(buildTestContext(t), []string{"foo", "a"}))
36+
}
37+
1138
func TestArgsIntTypes(t *testing.T) {
1239
cmd := buildMinimalTestCommand()
1340
var ival int
@@ -34,6 +61,29 @@ func TestArgsIntTypes(t *testing.T) {
3461
r.Error(cmd.Run(buildTestContext(t), []string{"foo", "10.0"}))
3562
}
3663

64+
func TestArgsFloatSliceTypes(t *testing.T) {
65+
cmd := buildMinimalTestCommand()
66+
var fval []float64
67+
cmd.Arguments = []Argument{
68+
&FloatArgs{
69+
Name: "ia",
70+
Min: 1,
71+
Max: -1,
72+
Destination: &fval,
73+
},
74+
}
75+
76+
err := cmd.Run(buildTestContext(t), []string{"foo", "10", "20", "30"})
77+
r := require.New(t)
78+
r.NoError(err)
79+
r.Equal([]float64{10, 20, 30}, fval)
80+
r.Equal([]float64{10, 20, 30}, cmd.FloatArgs("ia"))
81+
r.Equal([]float64{10, 20, 30}, cmd.Float64Args("ia"))
82+
r.Nil(cmd.Float32Args("ia"))
83+
84+
r.Error(cmd.Run(buildTestContext(t), []string{"foo", "10", "a"}))
85+
}
86+
3787
func TestArgsIntSliceTypes(t *testing.T) {
3888
cmd := buildMinimalTestCommand()
3989
var ival []int

docs/v3/examples/flags/basics.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ The following basic flags are supported
147147
- `BoolFlag`
148148
- `DurationFlag`
149149
- `FloatFlag`
150+
- `Float32Flag`
151+
- `Float64Flag`
150152
- `StringFlag`
151153
- `TimestampFlag`
152154

flag_float.go

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,44 +2,71 @@ package cli
22

33
import (
44
"strconv"
5+
"unsafe"
56
)
67

7-
type FloatFlag = FlagBase[float64, NoConfig, floatValue]
8+
type (
9+
FloatFlag = FlagBase[float64, NoConfig, floatValue[float64]]
10+
Float32Flag = FlagBase[float32, NoConfig, floatValue[float32]]
11+
Float64Flag = FlagBase[float64, NoConfig, floatValue[float64]]
12+
)
813

9-
// -- float64 Value
10-
type floatValue float64
14+
// -- float Value
15+
type floatValue[T float32 | float64] struct {
16+
val *T
17+
}
1118

1219
// Below functions are to satisfy the ValueCreator interface
1320

14-
func (f floatValue) Create(val float64, p *float64, c NoConfig) Value {
21+
func (f floatValue[T]) Create(val T, p *T, c NoConfig) Value {
1522
*p = val
16-
return (*floatValue)(p)
23+
24+
return &floatValue[T]{val: p}
1725
}
1826

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

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

25-
func (f *floatValue) Set(s string) error {
26-
v, err := strconv.ParseFloat(s, 64)
33+
func (f *floatValue[T]) Set(s string) error {
34+
v, err := strconv.ParseFloat(s, int(unsafe.Sizeof(T(0))*8))
2735
if err != nil {
2836
return err
2937
}
30-
*f = floatValue(v)
31-
return err
38+
*f.val = T(v)
39+
return nil
3240
}
3341

34-
func (f *floatValue) Get() any { return float64(*f) }
42+
func (f *floatValue[T]) Get() any { return *f.val }
3543

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

3848
// Float looks up the value of a local FloatFlag, returns
3949
// 0 if not found
4050
func (cmd *Command) Float(name string) float64 {
41-
if v, ok := cmd.Value(name).(float64); ok {
51+
return getFloat[float64](cmd, name)
52+
}
53+
54+
// Float32 looks up the value of a local Float32Flag, returns
55+
// 0 if not found
56+
func (cmd *Command) Float32(name string) float32 {
57+
return getFloat[float32](cmd, name)
58+
}
59+
60+
// Float64 looks up the value of a local Float64Flag, returns
61+
// 0 if not found
62+
func (cmd *Command) Float64(name string) float64 {
63+
return getFloat[float64](cmd, name)
64+
}
65+
66+
func getFloat[T float32 | float64](cmd *Command, name string) T {
67+
if v, ok := cmd.Value(name).(T); ok {
4268
tracef("float available for flag name %[1]q with value=%[2]v (cmd=%[3]q)", name, v, cmd.Name)
69+
4370
return v
4471
}
4572

flag_float_slice.go

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,34 @@
11
package cli
22

33
type (
4-
FloatSlice = SliceBase[float64, NoConfig, floatValue]
5-
FloatSliceFlag = FlagBase[[]float64, NoConfig, FloatSlice]
4+
FloatSlice = SliceBase[float64, NoConfig, floatValue[float64]]
5+
Float32Slice = SliceBase[float32, NoConfig, floatValue[float32]]
6+
Float64Slice = SliceBase[float64, NoConfig, floatValue[float64]]
7+
FloatSliceFlag = FlagBase[[]float64, NoConfig, FloatSlice]
8+
Float32SliceFlag = FlagBase[[]float32, NoConfig, Float32Slice]
9+
Float64SliceFlag = FlagBase[[]float64, NoConfig, Float64Slice]
610
)
711

8-
var NewFloatSlice = NewSliceBase[float64, NoConfig, floatValue]
12+
var (
13+
NewFloatSlice = NewSliceBase[float64, NoConfig, floatValue[float64]]
14+
NewFloat32Slice = NewSliceBase[float32, NoConfig, floatValue[float32]]
15+
NewFloat64Slice = NewSliceBase[float64, NoConfig, floatValue[float64]]
16+
)
917

1018
// FloatSlice looks up the value of a local FloatSliceFlag, returns
1119
// nil if not found
1220
func (cmd *Command) FloatSlice(name string) []float64 {
13-
if v, ok := cmd.Value(name).([]float64); ok {
14-
tracef("float slice available for flag name %[1]q with value=%[2]v (cmd=%[3]q)", name, v, cmd.Name)
15-
return v
16-
}
21+
return getNumberSlice[float64](cmd, name)
22+
}
23+
24+
// Float32Slice looks up the value of a local Float32Slice, returns
25+
// nil if not found
26+
func (cmd *Command) Float32Slice(name string) []float32 {
27+
return getNumberSlice[float32](cmd, name)
28+
}
1729

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

0 commit comments

Comments
 (0)