From f55c50b0d775b991614a53614cc14babc06aaf02 Mon Sep 17 00:00:00 2001 From: plastikfan Date: Sat, 9 Sep 2023 10:45:03 +0100 Subject: [PATCH] feat(generators): add skeleton go generator (#189) --- .gitignore | 1 + .vscode/settings.json | 3 + Taskfile.yml | 77 +++++++- generate.go | 3 + generators/gola/gen/main.go | 109 +++++++++++ generators/gola/gola_suite_test.go | 13 ++ generators/gola/gomega-matchers_test.go | 72 ++++++++ generators/gola/internal/utils/fs.go | 14 ++ generators/gola/logical-type.go | 43 +++++ generators/gola/source-code-data.go | 172 ++++++++++++++++++ generators/gola/source-code-data_test.go | 49 +++++ generators/gola/source-code-generator.go | 11 ++ .../top/option-validator-auto.templ.top.txt | 14 ++ .../option-validator-auto_test.templ.top.txt | 73 ++++++++ .../top/param-set-auto.templ.top.txt | 12 ++ .../top/param-set-auto_test.templ.top.txt | 82 +++++++++ ...aram-set-binder-helpers-auto.templ.top.txt | 18 ++ ...set-binder-helpers-auto_test.templ.top.txt | 64 +++++++ 18 files changed, 828 insertions(+), 2 deletions(-) create mode 100644 generate.go create mode 100644 generators/gola/gen/main.go create mode 100644 generators/gola/gola_suite_test.go create mode 100644 generators/gola/gomega-matchers_test.go create mode 100644 generators/gola/internal/utils/fs.go create mode 100644 generators/gola/logical-type.go create mode 100644 generators/gola/source-code-data.go create mode 100644 generators/gola/source-code-data_test.go create mode 100644 generators/gola/source-code-generator.go create mode 100644 generators/gola/templates/top/option-validator-auto.templ.top.txt create mode 100644 generators/gola/templates/top/option-validator-auto_test.templ.top.txt create mode 100644 generators/gola/templates/top/param-set-auto.templ.top.txt create mode 100644 generators/gola/templates/top/param-set-auto_test.templ.top.txt create mode 100644 generators/gola/templates/top/param-set-binder-helpers-auto.templ.top.txt create mode 100644 generators/gola/templates/top/param-set-binder-helpers-auto_test.templ.top.txt diff --git a/.gitignore b/.gitignore index 3ad122d..5274014 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ # vendor/ dist/ +generators/gola/out/ # go-task intermediate files like checksums # diff --git a/.vscode/settings.json b/.vscode/settings.json index 8859331..8ca4d1a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -17,7 +17,9 @@ "gocritic", "gocyclo", "gofmt", + "gogen", "goimports", + "gola", "gomnd", "gosec", "gosimple", @@ -36,6 +38,7 @@ "psname", "rebinder", "refl", + "repotoken", "Selectf", "staticcheck", "structcheck", diff --git a/Taskfile.yml b/Taskfile.yml index 71f4f54..e030179 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -1,7 +1,14 @@ -version: '3' +version: "3" silent: true -dotenv: ['.env'] +dotenv: [".env"] + +vars: + FORMAT: json + GEN_BINARY_NAME: cobrass-gen + GEN_DIR: ./generators/gola/gen + GEN_TEST_OUTPUT_DIR: ./generators/gola/out/assistant + DIST_DIR: ./dist tasks: b: @@ -57,3 +64,69 @@ tasks: cover: cmds: - goveralls -repotoken {{.COVERALLS_TOKEN}} + + # === code generator ========================================= + # + # NB: go generate can't evaluate variables, but we need to + # distinguish between a test run and a real run. For this reason + # we only invoke go generate for a real run and for a test run + # we invoke the generator directly without go generate, passing + # in the test flag. + + ov-gen-t: + cmds: + - mkdir -p {{.GEN_TEST_OUTPUT_DIR}} + - cobrass-gen -test -cwd ./ + + ov-gen: + cmds: + - go generate ./... + + # === build/deploy code generator =========================== + + b-gen-linux: + cmds: + - task: build-generic + vars: { TARGET_OS: linux, TARGET_ARCH: amd64 } + + build-generic: + vars: + APPLICATION_ENTRY: ./generators/gola/gen + cmds: + - echo "cross compiling generator from {{OS}} to {{.TARGET_OS}}" + - GOOS={{.TARGET_OS}} GOARCH={{.TARGET_ARCH}} go build -o {{.DIST_DIR}}/{{.TARGET_OS}}/{{.GEN_BINARY_NAME}} -v {{.APPLICATION_ENTRY}} + + sources: + - ./generators/gola/*.go + + generates: + - "{{.DIST_DIR}}/{{.TARGET_OS}}/{{.GEN_BINARY_NAME}}" + + d: + cmds: + - task: deploy + + # currently, this is hardcoded for linux + # + deploy: + vars: + TARGET_OS: linux + DEPLOY_BINARY: "{{.DIST_DIR}}/{{.TARGET_OS}}/{{.GEN_BINARY_NAME}}" + + cmds: + - echo "deploying to location (.env) DEPLOY_TO ==> '$DEPLOY_TO'" + - /bin/cp -f {{.DEPLOY_BINARY}} $DEPLOY_TO + + generates: + - $DEPLOY_TO/{{.DEPLOY_BINARY}} + - $DEPLOY_TO/{{.ACTIVE_US}} + + preconditions: + - test $DEPLOY_TO + - test -f {{.DEPLOY_BINARY}} + + tbd: + cmds: + - task: t + - task: b-gen-linux + - task: d diff --git a/generate.go b/generate.go new file mode 100644 index 0000000..605a14f --- /dev/null +++ b/generate.go @@ -0,0 +1,3 @@ +package cobrass + +//go:generate cobrass-gen -cwd ./ diff --git a/generators/gola/gen/main.go b/generators/gola/gen/main.go new file mode 100644 index 0000000..fb9a8f4 --- /dev/null +++ b/generators/gola/gen/main.go @@ -0,0 +1,109 @@ +package main + +import ( + "flag" + "fmt" + "os" + "path/filepath" + + "github.com/samber/lo" + "github.com/snivilised/cobrass/generators/gola" + "github.com/snivilised/cobrass/generators/gola/internal/utils" +) + +const ( + appName = "cobrass-gen" + outputPathNotFoundExitCode = 2 +) + +var ( + testFlag = flag.Bool("test", false, "generate code in test location?") + cwdFlag = flag.String("cwd", "", "current working directory") + testPath = filepath.Join("generators", "gola", "out", "assistant") + sourcePath = filepath.Join("src", "assistant") + outputPathNotFound = "Output path '%v', not found" +) + +func Usage() { + fmt.Fprintf(os.Stderr, "Use of %v:\n", appName) + fmt.Fprintf(os.Stderr, "run the command from the root of the repo ...\n") + fmt.Fprintf(os.Stderr, "\t%v [Flags]\n", appName) + fmt.Fprintf(os.Stderr, "Flags:\n") + flag.PrintDefaults() +} + +func fail(reason string, callback ...func()) { + if len(callback) > 0 { + callback[0]() + } + + fmt.Fprintf(os.Stderr, "๐Ÿ”ฅ Failed: '%v'\n", reason) + flag.Usage() + os.Exit(outputPathNotFoundExitCode) +} + +// ??? +// https://askgolang.com/how-to-get-current-directory-in-golang/ + +func main() { + flag.Usage = Usage + flag.Parse() + + outputPath := lo.Ternary(*testFlag, testPath, sourcePath) + + if *cwdFlag == "" { + fail("๐Ÿ”ฅ current working directory not specified") + } + + absolutePath, _ := filepath.Abs(*cwdFlag) + absolutePath = filepath.Join(absolutePath, outputPath) + + if !utils.FileExists(absolutePath) { + callback := func() { + fmt.Printf("๐Ÿ’ฅ ---> CWD: '%v' \n", *cwdFlag) + fmt.Printf("๐Ÿ’ฅ ---> OUTPUT: '%v' \n", outputPath) + fmt.Printf("๐Ÿ’ฅ ---> RESOLVED: '%v' \n", absolutePath) + } + fail(fmt.Sprintf(outputPathNotFound, absolutePath), callback) + + return + } + + sourceCode := gola.NewSourceCodeContainer() + mode := lo.Ternary(*testFlag, "๐Ÿงช Test", "๐ŸŽ Source") + + fmt.Printf("โ˜‘๏ธ ---> CWD: '%v' \n", *cwdFlag) + fmt.Printf("โ˜‘๏ธ ---> OUTPUT: '%v' \n", outputPath) + fmt.Printf("โ˜‘๏ธ ---> RESOLVED: '%v' \n", absolutePath) + fmt.Printf("---> ๐Ÿฒ cobrass generator (%v, to: %v)\n", mode, absolutePath) + + if !*testFlag { + if sourceCode.AnyMissing(absolutePath) { + sourceCode.Verify(absolutePath, func(entry *gola.SourceCodeData) { + exists := entry.Exists(absolutePath) + indicator := lo.Ternary(exists, "โœ”๏ธ", "โŒ") + status := lo.Ternary(exists, "exists", "missing") + path := entry.FullPath(absolutePath) + message := fmt.Sprintf("%v source file: '%v' %v", indicator, path, status) + + fmt.Printf("%v\n", message) + }) + } else { + fmt.Printf("โœ… ---> ALL-PRESENT-AT: '%v' \n", absolutePath) + } + } + + sourceCode.Generator().Run() + + logicalEnum := gola.LogicalType{ + TypeName: "Enum", + GoType: "string", + DisplayType: "enum", + UnderlyingTypeName: "String", + FlagName: "Format", + Short: "f", + Def: "xml", + } + + fmt.Printf("---> ๐Ÿฒ cobrass generator (enum: %+v)\n", logicalEnum) +} diff --git a/generators/gola/gola_suite_test.go b/generators/gola/gola_suite_test.go new file mode 100644 index 0000000..ebd15c7 --- /dev/null +++ b/generators/gola/gola_suite_test.go @@ -0,0 +1,13 @@ +package gola_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestGola(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Gola Suite") +} diff --git a/generators/gola/gomega-matchers_test.go b/generators/gola/gomega-matchers_test.go new file mode 100644 index 0000000..389216d --- /dev/null +++ b/generators/gola/gomega-matchers_test.go @@ -0,0 +1,72 @@ +package gola_test + +import ( + "fmt" + "strings" + + "github.com/samber/lo" + "github.com/snivilised/cobrass/generators/gola" + + . "github.com/onsi/gomega/types" +) + +type ( + AreAllSourceCodeFilesPresentMatcher struct { + directory string + } +) + +func ContainAllSourceCodeFilesAt(directory string) GomegaMatcher { + return &AreAllSourceCodeFilesPresentMatcher{ + directory: directory, + } +} + +func (m *AreAllSourceCodeFilesPresentMatcher) Match(actual interface{}) (bool, error) { + sourceCode, ok := actual.(*gola.SourceCodeContainer) + if !ok { + return false, fmt.Errorf("matcher expected a SourceCodeContainer value (actual: '%v')", actual) + } + + return !sourceCode.AnyMissing(m.directory), nil +} + +func (m *AreAllSourceCodeFilesPresentMatcher) report( + negated bool, + sourceCode *gola.SourceCodeContainer, +) string { + builder := strings.Builder{} + not := lo.Ternary(negated, "NOT ", " ") + builder.WriteString( + fmt.Sprintf("๐Ÿ”ฅ Expected all source code files %vto be present\n", not), + ) + + sourceCode.ReportAll(func(entry *gola.SourceCodeData) { + exists := entry.Exists(m.directory) + indicator := lo.Ternary(exists, "โœ”๏ธ", "โŒ") + status := lo.Ternary(exists, "exists", "missing") + path := entry.FullPath(m.directory) + message := fmt.Sprintf("%v source file: '%v' %v\n", indicator, path, status) + builder.WriteString(message) + }) + + return builder.String() +} + +func (m *AreAllSourceCodeFilesPresentMatcher) FailureMessage(actual interface{}) string { + sourceCode, ok := actual.(*gola.SourceCodeContainer) + if !ok { + return fmt.Sprintf("matcher expected a SourceCodeContainer value (actual: '%v')", actual) + } + + return m.report(false, sourceCode) +} + +func (m *AreAllSourceCodeFilesPresentMatcher) NegatedFailureMessage(actual interface{}) string { + sourceCode, ok := actual.(*gola.SourceCodeContainer) + if !ok { + return fmt.Sprintf("matcher expected a SourceCodeContainer value (actual: '%v')", actual) + } + + return m.report(true, sourceCode) +} diff --git a/generators/gola/internal/utils/fs.go b/generators/gola/internal/utils/fs.go new file mode 100644 index 0000000..28c91bd --- /dev/null +++ b/generators/gola/internal/utils/fs.go @@ -0,0 +1,14 @@ +package utils + +import "os" + +// FileExists provides a simple way to determine whether the item identified by a +// path actually exists as a file +func FileExists(path string) bool { + result := false + if info, err := os.Lstat(path); err == nil { + result = !info.IsDir() + } + + return result +} diff --git a/generators/gola/logical-type.go b/generators/gola/logical-type.go new file mode 100644 index 0000000..e9ce928 --- /dev/null +++ b/generators/gola/logical-type.go @@ -0,0 +1,43 @@ +package gola + +type PsCaseEntry struct { // this needs a better name, not Ps + AssertFn string +} + +type TestCaseEntry struct { +} + +type BhTest struct { // binder helper +} + +type BhTestCollection map[string]*BhTest + +type LogicalType struct { + TypeName string + GoType string + DisplayType string + UnderlyingTypeName string + FlagName string + Short string + Def any + Assign string + Setup string + BindTo string + Assert string + QuoteExpect string + Equate string + Validatable bool + ForeignValidatorFn bool + GenerateSlice bool + SliceFlagName string + SliceShort string + DefSliceVal string + ExpectSlice string + SliceValue string + OptionValue string + TcEntry PsCaseEntry + BindDoc string + BindValidatedDoc string + Containable bool + BhTests BhTestCollection +} diff --git a/generators/gola/source-code-data.go b/generators/gola/source-code-data.go new file mode 100644 index 0000000..0171591 --- /dev/null +++ b/generators/gola/source-code-data.go @@ -0,0 +1,172 @@ +package gola + +import ( + _ "embed" + "path/filepath" + "sort" + "strings" + "text/template" + + "github.com/samber/lo" + "github.com/snivilised/cobrass/generators/gola/internal/utils" +) + +// https://pkg.go.dev/text/template +// https://developer.hashicorp.com/nomad/tutorials/templates/go-template-syntax + +type CodeFileName string + +var ( + //go:embed templates/top/option-validator-auto.templ.top.txt + optionValidatorAutoTop string + + //go:embed templates/top/option-validator-auto_test.templ.top.txt + optionValidatorAutoTestTop string + + //go:embed templates/top/param-set-auto.templ.top.txt + paramSetAutoTop string + + //go:embed templates/top/param-set-auto_test.templ.top.txt + paramSetAutoTestTop string + + //go:embed templates/top/param-set-binder-helpers-auto.templ.top.txt + paramSetBinderHelpersAutoTop string + + //go:embed templates/top/param-set-binder-helpers-auto_test.templ.top.txt + paramSetBinderHelpersAutoTestTop string +) + +type SourceCodeData struct { + name CodeFileName + top string + templ *template.Template +} + +func (d *SourceCodeData) FileName() string { + return string(d.name) + ".go" +} + +func (d *SourceCodeData) IsTest() bool { + return strings.HasSuffix(string(d.name), "_test") +} + +func (d *SourceCodeData) Exists(absolutePath string) bool { + return utils.FileExists(d.FullPath(absolutePath)) +} + +func (d *SourceCodeData) FullPath(absolutePath string) string { + filename := d.FileName() + return filepath.Join(absolutePath, filename) +} + +type sourceCodeDataCollection map[CodeFileName]*SourceCodeData + +type SourceCodeContainer struct { + collection sourceCodeDataCollection +} + +func (d *SourceCodeContainer) init() { + d.collection = sourceCodeDataCollection{ + "option-validator-auto": &SourceCodeData{ + name: "option-validator-auto", + top: optionValidatorAutoTop, + }, + "option-validator-auto_test": &SourceCodeData{ + name: "option-validator-auto_test", + top: optionValidatorAutoTestTop, + }, + "param-set-auto": &SourceCodeData{ + name: "param-set-auto", + top: paramSetAutoTop, + }, + "param-set-auto_test": &SourceCodeData{ + name: "param-set-auto_test", + top: paramSetAutoTestTop, + }, + "param-set-binder-helpers-auto": &SourceCodeData{ + name: "param-set-binder-helpers-auto", + top: paramSetBinderHelpersAutoTop, + }, + "param-set-binder-helpers-auto_test": &SourceCodeData{ + name: "param-set-binder-helpers-auto_test", + top: paramSetBinderHelpersAutoTestTop, + }, + } + + for _, data := range d.collection { + if templ, err := template.New(string(data.name)).Parse(data.top); err == nil { + data.templ = templ + } + } +} + +func (d *SourceCodeContainer) sourceNames() []string { + keys := lo.Keys(d.collection) + sorted := lo.Map(keys, func(item CodeFileName, index int) string { + return string(item) + }) + sort.Strings(sorted) + + return sorted +} + +func (d *SourceCodeContainer) AnyMissing(absolutePath string) bool { + names := d.sourceNames() + + for _, name := range names { + sourceCodeName := CodeFileName(name) + data := (d.collection)[sourceCodeName] + exists := data.Exists(absolutePath) + + if !exists { + return true + } + } + + return false +} + +func (d *SourceCodeContainer) ReportAll(fn ...func(entry *SourceCodeData)) { + names := d.sourceNames() + + for _, name := range names { + sourceCodeName := CodeFileName(name) + data := (d.collection)[sourceCodeName] + + if len(fn) > 0 { + fn[0](data) + } + } +} + +func (d *SourceCodeContainer) Verify(absolutePath string, fn ...func(entry *SourceCodeData)) bool { + result := d.AnyMissing(absolutePath) + + if !result { + return result + } + + names := d.sourceNames() + + for _, name := range names { + sourceCodeName := CodeFileName(name) + data := (d.collection)[sourceCodeName] + + if len(fn) > 0 { + fn[0](data) + } + } + + return result +} + +func (d *SourceCodeContainer) Generator() *SourceCodeGenerator { + return &SourceCodeGenerator{} +} + +func NewSourceCodeContainer() *SourceCodeContainer { + data := &SourceCodeContainer{} + data.init() + + return data +} diff --git a/generators/gola/source-code-data_test.go b/generators/gola/source-code-data_test.go new file mode 100644 index 0000000..a09a942 --- /dev/null +++ b/generators/gola/source-code-data_test.go @@ -0,0 +1,49 @@ +package gola_test + +import ( + "path/filepath" + "runtime" + "strings" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/snivilised/cobrass/generators/gola" +) + +// . "github.com/onsi/gomega/types" + +func Path(parent, relative string) string { + segments := strings.Split(relative, "/") + return filepath.Join(append([]string{parent}, segments...)...) +} + +func Repo(relative string) string { + _, filename, _, _ := runtime.Caller(0) //nolint:dogsled // use of 3 _ is out of our control + return Path(filepath.Dir(filename), relative) +} + +var _ = Describe("SourceCodeData", Ordered, func() { + + var ( + repo, testPath, sourcePath string + ) + + BeforeAll(func() { + repo = Repo("../..") + testPath = filepath.Join("generators", "gola", "out", "assistant") + sourcePath = filepath.Join("src", "assistant") + _ = testPath + }) + + Context("AnyMissing", func() { + When("source mode", func() { + It("should: find all source code files are present", func() { + codeData := gola.NewSourceCodeContainer() + outputPath := filepath.Join(repo, sourcePath) + + Expect(codeData).To(ContainAllSourceCodeFilesAt(outputPath)) + }) + }) + }) +}) diff --git a/generators/gola/source-code-generator.go b/generators/gola/source-code-generator.go new file mode 100644 index 0000000..70a31cd --- /dev/null +++ b/generators/gola/source-code-generator.go @@ -0,0 +1,11 @@ +package gola + +import "bytes" + +type SourceCodeGenerator struct { + buffer bytes.Buffer +} + +func (g *SourceCodeGenerator) Run() { + +} diff --git a/generators/gola/templates/top/option-validator-auto.templ.top.txt b/generators/gola/templates/top/option-validator-auto.templ.top.txt new file mode 100644 index 0000000..025dc2c --- /dev/null +++ b/generators/gola/templates/top/option-validator-auto.templ.top.txt @@ -0,0 +1,14 @@ +package assistant + +import ( + "net" + "time" + + "github.com/spf13/pflag" +) + +// ----> auto generated(Build-Validators/gen-ov) + +{{ PLACEHOLDER }} + +// <---- end of auto generated diff --git a/generators/gola/templates/top/option-validator-auto_test.templ.top.txt b/generators/gola/templates/top/option-validator-auto_test.templ.top.txt new file mode 100644 index 0000000..be71a5d --- /dev/null +++ b/generators/gola/templates/top/option-validator-auto_test.templ.top.txt @@ -0,0 +1,73 @@ +package assistant_test + +import ( + "fmt" + "net" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/snivilised/cobrass/src/assistant" +) + +type OvEntry struct { + Message string + Validator func() assistant.OptionValidator + Setup func() +} + +var _ = Describe("OptionValidator", func() { + + var rootCommand *cobra.Command + var widgetCommand *cobra.Command + var paramSet *assistant.ParamSet[WidgetParameterSet] + var outputFormatEnumInfo *assistant.EnumInfo[OutputFormatEnum] + + BeforeEach(func() { + outputFormatEnumInfo = assistant.NewEnumInfo(AcceptableOutputFormats) + + rootCommand = &cobra.Command{ + Use: "poke", + Short: "A brief description of your application", + Long: "A long description of the root poke command", + } + + widgetCommand = &cobra.Command{ + Version: "1.0.1", + Use: "widget", + Short: "Create widget", + Long: "Index file system at root: '/'", + Args: cobra.ExactArgs(1), + + RunE: func(command *cobra.Command, args []string) error { + GinkgoWriter.Printf("===> ๐Ÿ“ EXECUTE (Directory: '%v')\n", args[0]) + + paramSet.Native.Directory = args[0] + return nil + }, + } + rootCommand.AddCommand(widgetCommand) + + paramSet = assistant.NewParamSet[WidgetParameterSet](widgetCommand) + }) + + DescribeTable("ParamSet with validation", + func(entry OvEntry) { + validator := entry.Validator() + entry.Setup() + _ = validator.Validate() + }, + func(entry OvEntry) string { + return fmt.Sprintf("๐Ÿงช --> ๐Ÿ’ given: flag type is '%v'", entry.Message) + }, + + // ----> auto generated(Build-TestEntry/gen-ov-t) + + {{ PLACEHOLDER }} + + // <---- end of auto generated + ) +}) diff --git a/generators/gola/templates/top/param-set-auto.templ.top.txt b/generators/gola/templates/top/param-set-auto.templ.top.txt new file mode 100644 index 0000000..cd5f585 --- /dev/null +++ b/generators/gola/templates/top/param-set-auto.templ.top.txt @@ -0,0 +1,12 @@ +package assistant + +import ( + "net" + "time" +) + +// ----> auto generated(Build-ParamSet/gen-ps) + +{{ PLACEHOLDER }} + +// <---- end of auto generated diff --git a/generators/gola/templates/top/param-set-auto_test.templ.top.txt b/generators/gola/templates/top/param-set-auto_test.templ.top.txt new file mode 100644 index 0000000..380eaea --- /dev/null +++ b/generators/gola/templates/top/param-set-auto_test.templ.top.txt @@ -0,0 +1,82 @@ +package assistant_test + +import ( + "fmt" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/spf13/cobra" + + "github.com/snivilised/cobrass/src/assistant" + "github.com/snivilised/cobrass/src/internal/helpers" +) + +// the auto version of param-set_test.go + +var _ = Describe("ParamSet (auto)", func() { + + When("Binding a flag (auto)", func() { + var rootCommand *cobra.Command + var widgetCommand *cobra.Command + var paramSet *assistant.ParamSet[WidgetParameterSet] + var outputFormatEnumInfo *assistant.EnumInfo[OutputFormatEnum] + var outputFormatEnum assistant.EnumValue[OutputFormatEnum] + + BeforeEach(func() { + rootCommand = &cobra.Command{ + Use: "poke", + Short: "A brief description of your application", + Long: "A long description of the root poke command", + } + + widgetCommand = &cobra.Command{ + Version: "1.0.1", + Use: "widget", + Short: "Create widget", + Long: "Index file system at root: '/'", + Args: cobra.ExactArgs(1), + + PreRun: func(command *cobra.Command, args []string) { + GinkgoWriter.Printf("**** ๐Ÿ‰ PRE-RUN\n") + }, + RunE: func(command *cobra.Command, args []string) error { + GinkgoWriter.Printf("===> ๐Ÿ“ EXECUTE (Directory: '%v')\n", args[0]) + + paramSet.Native.Directory = args[0] + return nil + }, + PostRun: func(command *cobra.Command, args []string) { + GinkgoWriter.Printf("**** ๐Ÿฅฅ POST-RUN\n") + }, + } + rootCommand.AddCommand(widgetCommand) + + paramSet = assistant.NewParamSet[WidgetParameterSet](widgetCommand) + + outputFormatEnumInfo = assistant.NewEnumInfo(AcceptableOutputFormats) + outputFormatEnum = outputFormatEnumInfo.NewValue() + }) + + DescribeTable("binder", + func(entry TcEntry) { + entry.Binder() + + _, _ = helpers.ExecuteCommand( + rootCommand, "widget", "/usr/fuse/home/music", entry.CommandLine, + ) + entry.Assert() + }, + + func(entry TcEntry) string { + return fmt.Sprintf("๐Ÿงช --> ๐Ÿ’ given: flag is '%v'", entry.Message) + }, + + // ----> auto generated(Build-PsTestEntry/gen-ps-t) + + {{ PLACEHOLDER }} + + // <---- auto generated(Build-PsTestEntry/gen-ps-t) + ) + }) +}) diff --git a/generators/gola/templates/top/param-set-binder-helpers-auto.templ.top.txt b/generators/gola/templates/top/param-set-binder-helpers-auto.templ.top.txt new file mode 100644 index 0000000..f478cb4 --- /dev/null +++ b/generators/gola/templates/top/param-set-binder-helpers-auto.templ.top.txt @@ -0,0 +1,18 @@ +//nolint:gocritic // regexp.MustCompile: solution unknown +package assistant + +import ( + "regexp" + "time" + + "github.com/samber/lo" + "github.com/snivilised/cobrass/src/assistant/i18n" + + "github.com/spf13/pflag" +) + +// ----> auto generated(Build-Predefined/gen-help) + +{{ PLACEHOLDER }} + +// <---- end of auto generated diff --git a/generators/gola/templates/top/param-set-binder-helpers-auto_test.templ.top.txt b/generators/gola/templates/top/param-set-binder-helpers-auto_test.templ.top.txt new file mode 100644 index 0000000..01400ef --- /dev/null +++ b/generators/gola/templates/top/param-set-binder-helpers-auto_test.templ.top.txt @@ -0,0 +1,64 @@ +package assistant_test + +import ( + "fmt" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/spf13/cobra" + + "github.com/snivilised/cobrass/src/assistant" +) + +type validatorDecorator struct { + Decorated assistant.OptionValidator +} + +func (v *validatorDecorator) Validate() error { + flag := v.Decorated.GetFlag() + flag.Changed = true + + return v.Decorated.Validate() +} + +var _ = Describe("ParamSetBinderHelpers", func() { + var rootCommand *cobra.Command + var widgetCommand *cobra.Command + var paramSet *assistant.ParamSet[WidgetParameterSet] + var outputFormatEnumInfo *assistant.EnumInfo[OutputFormatEnum] + var outputFormatEnum assistant.EnumValue[OutputFormatEnum] + + Context("Comparables", func() { + BeforeEach(func() { + outputFormatEnumInfo = assistant.NewEnumInfo(AcceptableOutputFormats) + outputFormatEnum = outputFormatEnumInfo.NewValue() + + rootCommand = &cobra.Command{ + Use: "flick", + Short: "A brief description of your application", + Long: "A long description of the root flick command", + } + + widgetCommand = &cobra.Command{ + Version: "1.0.1", + Use: "widget", + Short: "Create widget", + Long: "Index file system at root: '/'", + Args: cobra.ExactArgs(1), + RunE: func(command *cobra.Command, args []string) error { + paramSet.Native.Directory = args[0] + return nil + }, + } + rootCommand.AddCommand(widgetCommand) + paramSet = assistant.NewParamSet[WidgetParameterSet](widgetCommand) + }) + + // ----> auto generated(Build-BinderHelperTests/gen-help-t) + + {{ PLACEHOLDER }} + + // <---- auto generated + }) +})