diff --git a/README.md b/README.md index ca5e5ac..0c5da57 100644 --- a/README.md +++ b/README.md @@ -340,6 +340,7 @@ GLOBAL OPTIONS: --buildtag value, -b value [ --buildtag value, -b value ] Adds build tags to a generated enum file. --output-suffix .go Changes the default filename suffix of _enum to something else. .go will be appended to the end of the string no matter what, so that `_test.go` cases can be accommodated --no-iota Disables the use of iota in generated enums. (default: false) + --bitfield Generates the values as bit fields. (default: false) --help, -h show help --version, -v print the version ``` diff --git a/example/enum_bitfield.go b/example/enum_bitfield.go new file mode 100644 index 0000000..7e634e6 --- /dev/null +++ b/example/enum_bitfield.go @@ -0,0 +1,43 @@ +//go:generate ../bin/go-enum --names -b example --bitfield --values + +package example + +/* +ENUM( + +E2P0 +E2P1 +E2P2 +E2P3 +E2P4 +E2P5 +E2P6 +E2P7 +E2P8 +E2P9 +E2P10 +E2P11 +E2P12 +E2P13 +E2P14 +E2P15 +E2P16 +E2P17 +E2P18 +E2P19 +E2P20 +E2P21 +E2P22 +E2P23 +E2P24 +E2P25 +E2P26 +E2P27 +E2P28 +E2P29 +E2P30 +E2P31 + +) +*/ +type Enum32bitfield uint32 diff --git a/example/enum_bitfield_enum.go b/example/enum_bitfield_enum.go new file mode 100644 index 0000000..3ae3d6e --- /dev/null +++ b/example/enum_bitfield_enum.go @@ -0,0 +1,259 @@ +// Code generated by go-enum DO NOT EDIT. +// Version: example +// Revision: example +// Build Date: example +// Built By: example + +//go:build example +// +build example + +package example + +import ( + "fmt" + "strings" +) + +const ( + // Enum32bitfieldE2P0 is a Enum32bitfield of type E2P0. + Enum32bitfieldE2P0 Enum32bitfield = 1 << iota + // Enum32bitfieldE2P1 is a Enum32bitfield of type E2P1. + Enum32bitfieldE2P1 + // Enum32bitfieldE2P2 is a Enum32bitfield of type E2P2. + Enum32bitfieldE2P2 + // Enum32bitfieldE2P3 is a Enum32bitfield of type E2P3. + Enum32bitfieldE2P3 + // Enum32bitfieldE2P4 is a Enum32bitfield of type E2P4. + Enum32bitfieldE2P4 + // Enum32bitfieldE2P5 is a Enum32bitfield of type E2P5. + Enum32bitfieldE2P5 + // Enum32bitfieldE2P6 is a Enum32bitfield of type E2P6. + Enum32bitfieldE2P6 + // Enum32bitfieldE2P7 is a Enum32bitfield of type E2P7. + Enum32bitfieldE2P7 + // Enum32bitfieldE2P8 is a Enum32bitfield of type E2P8. + Enum32bitfieldE2P8 + // Enum32bitfieldE2P9 is a Enum32bitfield of type E2P9. + Enum32bitfieldE2P9 + // Enum32bitfieldE2P10 is a Enum32bitfield of type E2P10. + Enum32bitfieldE2P10 + // Enum32bitfieldE2P11 is a Enum32bitfield of type E2P11. + Enum32bitfieldE2P11 + // Enum32bitfieldE2P12 is a Enum32bitfield of type E2P12. + Enum32bitfieldE2P12 + // Enum32bitfieldE2P13 is a Enum32bitfield of type E2P13. + Enum32bitfieldE2P13 + // Enum32bitfieldE2P14 is a Enum32bitfield of type E2P14. + Enum32bitfieldE2P14 + // Enum32bitfieldE2P15 is a Enum32bitfield of type E2P15. + Enum32bitfieldE2P15 + // Enum32bitfieldE2P16 is a Enum32bitfield of type E2P16. + Enum32bitfieldE2P16 + // Enum32bitfieldE2P17 is a Enum32bitfield of type E2P17. + Enum32bitfieldE2P17 + // Enum32bitfieldE2P18 is a Enum32bitfield of type E2P18. + Enum32bitfieldE2P18 + // Enum32bitfieldE2P19 is a Enum32bitfield of type E2P19. + Enum32bitfieldE2P19 + // Enum32bitfieldE2P20 is a Enum32bitfield of type E2P20. + Enum32bitfieldE2P20 + // Enum32bitfieldE2P21 is a Enum32bitfield of type E2P21. + Enum32bitfieldE2P21 + // Enum32bitfieldE2P22 is a Enum32bitfield of type E2P22. + Enum32bitfieldE2P22 + // Enum32bitfieldE2P23 is a Enum32bitfield of type E2P23. + Enum32bitfieldE2P23 + // Enum32bitfieldE2P24 is a Enum32bitfield of type E2P24. + Enum32bitfieldE2P24 + // Enum32bitfieldE2P25 is a Enum32bitfield of type E2P25. + Enum32bitfieldE2P25 + // Enum32bitfieldE2P26 is a Enum32bitfield of type E2P26. + Enum32bitfieldE2P26 + // Enum32bitfieldE2P27 is a Enum32bitfield of type E2P27. + Enum32bitfieldE2P27 + // Enum32bitfieldE2P28 is a Enum32bitfield of type E2P28. + Enum32bitfieldE2P28 + // Enum32bitfieldE2P29 is a Enum32bitfield of type E2P29. + Enum32bitfieldE2P29 + // Enum32bitfieldE2P30 is a Enum32bitfield of type E2P30. + Enum32bitfieldE2P30 + // Enum32bitfieldE2P31 is a Enum32bitfield of type E2P31. + Enum32bitfieldE2P31 +) + +var ErrInvalidEnum32bitfield = fmt.Errorf("not a valid Enum32bitfield, try [%s]", strings.Join(_Enum32bitfieldNames, ", ")) + +const _Enum32bitfieldName = "E2P0E2P1E2P2E2P3E2P4E2P5E2P6E2P7E2P8E2P9E2P10E2P11E2P12E2P13E2P14E2P15E2P16E2P17E2P18E2P19E2P20E2P21E2P22E2P23E2P24E2P25E2P26E2P27E2P28E2P29E2P30E2P31" + +var _Enum32bitfieldNames = []string{ + _Enum32bitfieldName[0:4], + _Enum32bitfieldName[4:8], + _Enum32bitfieldName[8:12], + _Enum32bitfieldName[12:16], + _Enum32bitfieldName[16:20], + _Enum32bitfieldName[20:24], + _Enum32bitfieldName[24:28], + _Enum32bitfieldName[28:32], + _Enum32bitfieldName[32:36], + _Enum32bitfieldName[36:40], + _Enum32bitfieldName[40:45], + _Enum32bitfieldName[45:50], + _Enum32bitfieldName[50:55], + _Enum32bitfieldName[55:60], + _Enum32bitfieldName[60:65], + _Enum32bitfieldName[65:70], + _Enum32bitfieldName[70:75], + _Enum32bitfieldName[75:80], + _Enum32bitfieldName[80:85], + _Enum32bitfieldName[85:90], + _Enum32bitfieldName[90:95], + _Enum32bitfieldName[95:100], + _Enum32bitfieldName[100:105], + _Enum32bitfieldName[105:110], + _Enum32bitfieldName[110:115], + _Enum32bitfieldName[115:120], + _Enum32bitfieldName[120:125], + _Enum32bitfieldName[125:130], + _Enum32bitfieldName[130:135], + _Enum32bitfieldName[135:140], + _Enum32bitfieldName[140:145], + _Enum32bitfieldName[145:150], +} + +// Enum32bitfieldNames returns a list of possible string values of Enum32bitfield. +func Enum32bitfieldNames() []string { + tmp := make([]string, len(_Enum32bitfieldNames)) + copy(tmp, _Enum32bitfieldNames) + return tmp +} + +// Enum32bitfieldValues returns a list of the values for Enum32bitfield +func Enum32bitfieldValues() []Enum32bitfield { + return []Enum32bitfield{ + Enum32bitfieldE2P0, + Enum32bitfieldE2P1, + Enum32bitfieldE2P2, + Enum32bitfieldE2P3, + Enum32bitfieldE2P4, + Enum32bitfieldE2P5, + Enum32bitfieldE2P6, + Enum32bitfieldE2P7, + Enum32bitfieldE2P8, + Enum32bitfieldE2P9, + Enum32bitfieldE2P10, + Enum32bitfieldE2P11, + Enum32bitfieldE2P12, + Enum32bitfieldE2P13, + Enum32bitfieldE2P14, + Enum32bitfieldE2P15, + Enum32bitfieldE2P16, + Enum32bitfieldE2P17, + Enum32bitfieldE2P18, + Enum32bitfieldE2P19, + Enum32bitfieldE2P20, + Enum32bitfieldE2P21, + Enum32bitfieldE2P22, + Enum32bitfieldE2P23, + Enum32bitfieldE2P24, + Enum32bitfieldE2P25, + Enum32bitfieldE2P26, + Enum32bitfieldE2P27, + Enum32bitfieldE2P28, + Enum32bitfieldE2P29, + Enum32bitfieldE2P30, + Enum32bitfieldE2P31, + } +} + +var _Enum32bitfieldMap = map[Enum32bitfield]string{ + Enum32bitfieldE2P0: _Enum32bitfieldName[0:4], + Enum32bitfieldE2P1: _Enum32bitfieldName[4:8], + Enum32bitfieldE2P2: _Enum32bitfieldName[8:12], + Enum32bitfieldE2P3: _Enum32bitfieldName[12:16], + Enum32bitfieldE2P4: _Enum32bitfieldName[16:20], + Enum32bitfieldE2P5: _Enum32bitfieldName[20:24], + Enum32bitfieldE2P6: _Enum32bitfieldName[24:28], + Enum32bitfieldE2P7: _Enum32bitfieldName[28:32], + Enum32bitfieldE2P8: _Enum32bitfieldName[32:36], + Enum32bitfieldE2P9: _Enum32bitfieldName[36:40], + Enum32bitfieldE2P10: _Enum32bitfieldName[40:45], + Enum32bitfieldE2P11: _Enum32bitfieldName[45:50], + Enum32bitfieldE2P12: _Enum32bitfieldName[50:55], + Enum32bitfieldE2P13: _Enum32bitfieldName[55:60], + Enum32bitfieldE2P14: _Enum32bitfieldName[60:65], + Enum32bitfieldE2P15: _Enum32bitfieldName[65:70], + Enum32bitfieldE2P16: _Enum32bitfieldName[70:75], + Enum32bitfieldE2P17: _Enum32bitfieldName[75:80], + Enum32bitfieldE2P18: _Enum32bitfieldName[80:85], + Enum32bitfieldE2P19: _Enum32bitfieldName[85:90], + Enum32bitfieldE2P20: _Enum32bitfieldName[90:95], + Enum32bitfieldE2P21: _Enum32bitfieldName[95:100], + Enum32bitfieldE2P22: _Enum32bitfieldName[100:105], + Enum32bitfieldE2P23: _Enum32bitfieldName[105:110], + Enum32bitfieldE2P24: _Enum32bitfieldName[110:115], + Enum32bitfieldE2P25: _Enum32bitfieldName[115:120], + Enum32bitfieldE2P26: _Enum32bitfieldName[120:125], + Enum32bitfieldE2P27: _Enum32bitfieldName[125:130], + Enum32bitfieldE2P28: _Enum32bitfieldName[130:135], + Enum32bitfieldE2P29: _Enum32bitfieldName[135:140], + Enum32bitfieldE2P30: _Enum32bitfieldName[140:145], + Enum32bitfieldE2P31: _Enum32bitfieldName[145:150], +} + +// String implements the Stringer interface. +func (x Enum32bitfield) String() string { + if str, ok := _Enum32bitfieldMap[x]; ok { + return str + } + return fmt.Sprintf("Enum32bitfield(%d)", x) +} + +// IsValid provides a quick way to determine if the typed value is +// part of the allowed enumerated values +func (x Enum32bitfield) IsValid() bool { + _, ok := _Enum32bitfieldMap[x] + return ok +} + +var _Enum32bitfieldValue = map[string]Enum32bitfield{ + _Enum32bitfieldName[0:4]: Enum32bitfieldE2P0, + _Enum32bitfieldName[4:8]: Enum32bitfieldE2P1, + _Enum32bitfieldName[8:12]: Enum32bitfieldE2P2, + _Enum32bitfieldName[12:16]: Enum32bitfieldE2P3, + _Enum32bitfieldName[16:20]: Enum32bitfieldE2P4, + _Enum32bitfieldName[20:24]: Enum32bitfieldE2P5, + _Enum32bitfieldName[24:28]: Enum32bitfieldE2P6, + _Enum32bitfieldName[28:32]: Enum32bitfieldE2P7, + _Enum32bitfieldName[32:36]: Enum32bitfieldE2P8, + _Enum32bitfieldName[36:40]: Enum32bitfieldE2P9, + _Enum32bitfieldName[40:45]: Enum32bitfieldE2P10, + _Enum32bitfieldName[45:50]: Enum32bitfieldE2P11, + _Enum32bitfieldName[50:55]: Enum32bitfieldE2P12, + _Enum32bitfieldName[55:60]: Enum32bitfieldE2P13, + _Enum32bitfieldName[60:65]: Enum32bitfieldE2P14, + _Enum32bitfieldName[65:70]: Enum32bitfieldE2P15, + _Enum32bitfieldName[70:75]: Enum32bitfieldE2P16, + _Enum32bitfieldName[75:80]: Enum32bitfieldE2P17, + _Enum32bitfieldName[80:85]: Enum32bitfieldE2P18, + _Enum32bitfieldName[85:90]: Enum32bitfieldE2P19, + _Enum32bitfieldName[90:95]: Enum32bitfieldE2P20, + _Enum32bitfieldName[95:100]: Enum32bitfieldE2P21, + _Enum32bitfieldName[100:105]: Enum32bitfieldE2P22, + _Enum32bitfieldName[105:110]: Enum32bitfieldE2P23, + _Enum32bitfieldName[110:115]: Enum32bitfieldE2P24, + _Enum32bitfieldName[115:120]: Enum32bitfieldE2P25, + _Enum32bitfieldName[120:125]: Enum32bitfieldE2P26, + _Enum32bitfieldName[125:130]: Enum32bitfieldE2P27, + _Enum32bitfieldName[130:135]: Enum32bitfieldE2P28, + _Enum32bitfieldName[135:140]: Enum32bitfieldE2P29, + _Enum32bitfieldName[140:145]: Enum32bitfieldE2P30, + _Enum32bitfieldName[145:150]: Enum32bitfieldE2P31, +} + +// ParseEnum32bitfield attempts to convert a string to a Enum32bitfield. +func ParseEnum32bitfield(name string) (Enum32bitfield, error) { + if x, ok := _Enum32bitfieldValue[name]; ok { + return x, nil + } + return Enum32bitfield(0), fmt.Errorf("%s is %w", name, ErrInvalidEnum32bitfield) +} diff --git a/example/enum_bitfield_test.go b/example/enum_bitfield_test.go new file mode 100644 index 0000000..2eaf781 --- /dev/null +++ b/example/enum_bitfield_test.go @@ -0,0 +1,49 @@ +//go:build example +// +build example + +package example + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestEnum32bitfield(t *testing.T) { + t.Parallel() + + allFields := Enum32bitfieldValues() + + // Generate all pairs (i, j) where i < j to avoid duplicates + testCases := make([]struct { + name string + field1 Enum32bitfield + field2 Enum32bitfield + }, 0, len(allFields)*(len(allFields)-1)/2) + + for i := 0; i < len(allFields); i++ { + for j := i + 1; j < len(allFields); j++ { + testCases = append(testCases, struct { + name string + field1 Enum32bitfield + field2 Enum32bitfield + }{ + name: allFields[i].String() + "_" + allFields[j].String(), + field1: allFields[i], + field2: allFields[j], + }) + } + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + // Collapse the two views + collapsed := tc.field1 | tc.field2 + + assert.Equal(t, tc.field1, collapsed^tc.field2) + assert.Equal(t, tc.field2, collapsed^tc.field1) + }) + } +} diff --git a/generator/enum.tmpl b/generator/enum.tmpl index 8d214a5..946a55d 100644 --- a/generator/enum.tmpl +++ b/generator/enum.tmpl @@ -23,6 +23,7 @@ const ( {{- $enumType := .enum.Type -}} {{- $noComments := .nocomments -}} {{- $noIota := .noIota -}} +{{- $bitField := .bitField -}} {{- $vars := dict "lastoffset" "0" -}} {{ range $rIndex, $value := .enum.Values }} {{- $lastOffset := pluck "lastoffset" $vars | first }}{{ $offset := offset $rIndex $enumType $value }} @@ -32,7 +33,7 @@ const ( // {{$value.Comment}} {{- end}} {{if $noIota }}{{$value.PrefixedName}} {{$enumName}} = {{directVal $enumType $value}}{{else -}} - {{$value.PrefixedName}} {{ if eq $rIndex 0 }}{{$enumName}} = iota{{ if ne "0" $offset }} + {{ $offset }}{{end}}{{else if ne $lastOffset $offset }}{{$enumName}} = iota + {{ $offset }}{{end}}{{$_ := set $vars "lastoffset" $offset}} + {{$value.PrefixedName}} {{ if eq $rIndex 0 }}{{$enumName}} = {{if $bitField }}1<<{{end}}iota{{ if ne "0" $offset }} + {{ $offset }}{{end}}{{else if ne $lastOffset $offset }}{{$enumName}} = iota + {{ $offset }}{{end}}{{$_ := set $vars "lastoffset" $offset}} {{- end}} {{- end}} ) diff --git a/generator/generator.go b/generator/generator.go index cac315a..215609b 100644 --- a/generator/generator.go +++ b/generator/generator.go @@ -25,6 +25,9 @@ const ( parseCommentPrefix = `//` ) +var ErrBitfieldOnString = errors.New("bitfield option is not allowed on string types") +var ErrBitfieldManuallValue = errors.New("manually setting values is not allowed with the bitfield option") + // Generator is responsible for generating validation files for the given in a go source file. type Generator struct { Version string @@ -189,6 +192,11 @@ func (g *Generator) Generate(f *ast.File) ([]byte, error) { // Parse the enum doc statement enum, pErr := g.parseEnum(ts) if pErr != nil { + if errors.Is(pErr, ErrBitfieldOnString) || + errors.Is(pErr, ErrBitfieldManuallValue) { + return nil, pErr + } + continue } @@ -227,6 +235,7 @@ func (g *Generator) Generate(f *ast.File) ([]byte, error) { "forcelower": g.ForceLower, "forceupper": g.ForceUpper, "noparse": g.NoParse, + "bitField": g.BitField, // Computed values for cleaner templates "generateParse": generateParse, "parseIsPublic": parseIsPublic, @@ -295,6 +304,10 @@ func (g *Generator) parseEnum(ts *ast.TypeSpec) (*Enum, error) { enum.Prefix = g.Prefix + enum.Prefix } + if g.BitField && enum.Type == "string" { + return nil, ErrBitfieldOnString + } + commentPreEnumDecl, _, _ := strings.Cut(ts.Doc.Text(), `ENUM(`) enum.Comment = strings.TrimSpace(commentPreEnumDecl) @@ -332,6 +345,9 @@ func (g *Generator) parseEnum(ts *ast.TypeSpec) (*Enum, error) { valueStr := value if strings.Contains(value, `=`) { + if g.BitField { + return nil, ErrBitfieldManuallValue + } // Get the value specified and set the data to that value. equalIndex := strings.Index(value, `=`) dataVal := strings.TrimSpace(value[equalIndex+1:]) diff --git a/generator/generator_test.go b/generator/generator_test.go index 23f2264..a2d2767 100644 --- a/generator/generator_test.go +++ b/generator/generator_test.go @@ -469,6 +469,50 @@ func TestQuotedStrings(t *testing.T) { } } +func TestBitFieldOfStringFailure(t *testing.T) { + input := `package test + // This is a pre-enum comment that needs (to be handled properly) + // ENUM( + // abc + // ghi + //). This is an extra string comment (With parentheses of it's own) + // And (another line) with Parentheses + type Animal string + ` + g := NewGenerator(WithBitfield()) + f, err := parser.ParseFile(g.fileSet, "TestRequiredErrors", input, parser.ParseComments) + assert.Nil(t, err, "Error parsing input") + + output, err := g.Generate(f) + assert.ErrorIs(t, err, ErrBitfieldOnString) + assert.Empty(t, output) + if false { // Debugging statement + fmt.Println(output) + } +} + +func TestBitFieldManuallyValueFailure(t *testing.T) { + input := `package test + // This is a pre-enum comment that needs (to be handled properly) + // ENUM( + // abc = 1 + // ghi + //). This is an extra string comment (With parentheses of it's own) + // And (another line) with Parentheses + type Animal int + ` + g := NewGenerator(WithBitfield()) + f, err := parser.ParseFile(g.fileSet, "TestRequiredErrors", input, parser.ParseComments) + assert.Nil(t, err, "Error parsing input") + + output, err := g.Generate(f) + assert.ErrorIs(t, err, ErrBitfieldManuallValue) + assert.Empty(t, output) + if false { // Debugging statement + fmt.Println(output) + } +} + func TestStringWithSingleDoubleQuoteValue(t *testing.T) { input := `package test // ENUM(DoubleQuote='"') diff --git a/generator/options.go b/generator/options.go index df08594..f506262 100644 --- a/generator/options.go +++ b/generator/options.go @@ -23,6 +23,7 @@ type GeneratorConfig struct { ForceUpper bool `json:"force_upper"` NoComments bool `json:"no_comments"` NoParse bool `json:"no_parse"` + BitField bool `json:"bit_field"` BuildTags []string `json:"build_tags"` ReplacementNames map[string]string `json:"replacement_names"` TemplateFileNames []string `json:"template_file_names"` @@ -212,3 +213,10 @@ func WithNoParse() Option { g.NoParse = true } } + +// WithBitfield is used to create bitfield enums. +func WithBitfield() Option { + return func(g *GeneratorConfig) { + g.BitField = true + } +} diff --git a/main.go b/main.go index a149bc4..dd6e8b8 100644 --- a/main.go +++ b/main.go @@ -9,9 +9,10 @@ import ( "strings" "sync" - "github.com/abice/go-enum/generator" "github.com/labstack/gommon/color" "github.com/urfave/cli/v2" + + "github.com/abice/go-enum/generator" ) var ( @@ -48,6 +49,7 @@ type rootT struct { ForceUpper bool NoComments bool NoParse bool + BitField bool OutputSuffix string } @@ -229,6 +231,11 @@ func main() { Usage: "Disables the use of iota in generated enums.", Destination: &argv.NoIota, }, + &cli.BoolFlag{ + Name: "bitfield", + Usage: "Generates the values as bit fields.", + Destination: &argv.BitField, + }, }, Action: func(ctx *cli.Context) error { // Validate incompatible flag combinations @@ -281,6 +288,7 @@ func main() { ForceUpper: argv.ForceUpper, NoComments: argv.NoComments, NoParse: argv.NoParse, + BitField: argv.BitField, BuildTags: argv.BuildTags.Value(), ReplacementNames: aliases, TemplateFileNames: templateFileNames, diff --git a/main_test.go b/main_test.go index 6ad90d4..6a480e6 100644 --- a/main_test.go +++ b/main_test.go @@ -8,10 +8,11 @@ import ( "testing" "time" - "github.com/abice/go-enum/generator" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/urfave/cli/v2" + + "github.com/abice/go-enum/generator" ) func TestGlobFilenames(t *testing.T) { @@ -261,6 +262,7 @@ func TestCliFlagsConfiguration(t *testing.T) { "--forcelower", "--forceupper", "--nocomments", + "--bitfield", }, setup: func() (string, func()) { tmpDir, err := os.MkdirTemp("", "go-enum-test") @@ -311,6 +313,7 @@ const ( ForceLower: true, ForceUpper: true, NoComments: true, + BitField: true, }, }, { @@ -441,6 +444,9 @@ const ( if tt.expected.NoComments { assert.True(t, argv.NoComments) } + if tt.expected.BitField { + assert.True(t, argv.BitField) + } if tt.expected.Prefix != "" { assert.Equal(t, tt.expected.Prefix, argv.Prefix) } @@ -592,6 +598,11 @@ func createCliApp(argv *rootT) *cli.App { Usage: "Changes the default filename suffix of _enum to something else. `.go` will be appended to the end of the string no matter what, so that `_test.go` cases can be accommodated ", Destination: &argv.OutputSuffix, }, + &cli.BoolFlag{ + Name: "bitfield", + Usage: "Generates the values as bit fields.", + Destination: &argv.BitField, + }, }, Action: func(ctx *cli.Context) error { // For testing, we mimic the real app's validation without doing actual work