mkideal / cli

CLI - A package for building command line app with go
MIT License
730 stars 43 forks source link

Default arguments from embedded struct not realised #68

Closed SamuelMarks closed 1 year ago

SamuelMarks commented 1 year ago
package main

import (
    "errors"
    "fmt"
    "os"

    "github.com/mkideal/cli"
)

// ConfigCmd taken from github.com/fergusstrange/embedded-postgres@v1.20.0/config.go
type ConfigCmd struct {
    version             string `cli:"version" dft:"latest"`
    port                uint32 `cli:"port" dft:"5432"`
    database            string `cli:"database" dft:"database"`
    username            string `cli:"username" dft:"username"`
    password            string `cli:"password" dft:"password"`
    runtimePath         string `cli:"runtime-path"`
    dataPath            string `cli:"data-path"`
    binariesPath        string `cli:"binaries-path"`
    locale              string `cli:"locale" dft:"en_US.UTF-8"`
    binaryRepositoryURL string `cli:"binary-repository-url" dft:"https://repo1.maven.org/maven2"`
}

type InstallStruct struct {
    cli.Helper
    ConfigCmd
}

var installCommand = &cli.Command{
    Name: "install",
    Desc: "install specified version of PostgreSQL",
    Argv: func() interface{} { return new(InstallStruct) },
    Fn: func(ctx *cli.Context) error {
        argv := ctx.Argv().(*InstallStruct)
        ctx.String("argv.database = %s\n", argv.database)
        return nil
    },
}

const successResponsePrefix = "start ok"

func main() {
    if err := cli.Root(root,
        cli.Tree(installCommand),
    ).Run(os.Args[1:]); err != nil {
        _, err := fmt.Fprintln(os.Stderr, err)
        if err != nil {
            panic(err)
        }
        os.Exit(1)
    }
}

type argT struct {
    cli.Helper
    Wait  uint `cli:"wait" usage:"seconds for waiting" dft:"10"`
    Error bool `cli:"e" usage:"create an error"`
}

var root = &cli.Command{
    Argv: func() interface{} { return new(argT) },
    Fn: func(ctx *cli.Context) error {
        argv := ctx.Argv().(*argT)
        if argv.Error {
            err := fmt.Errorf("occurs error")
            cli.DaemonResponse(err.Error())
            return err
        }
        cli.DaemonResponse(successResponsePrefix)
        <-time.After(time.Duration(argv.Wait) * time.Second)
        return nil
    },
}

Which gives me this error:

ERR! field version can not set

My use-case is I want all my subcommands to take all the config struct options + some new ones.

suntong commented 1 year ago

I'll defer to @mkideal to answer your question, but meanwhile

My use-case is I want all my subcommands to take all the config struct options + some new ones.

Take a look at https://github.com/suntong/lang/tree/master/lang/Go/src/sys/cli/027-global-redo#2016-06-01

for another way to implement subcommands with global options and options of its own.

mkideal commented 1 year ago

The fields version, port... should all be exported, e.g. Version, Port.

SamuelMarks commented 1 year ago

Thanks mkideal that worked. Been a while since I tried Go!


@suntong Good idea. I've been trying to implement it, tried setting the Global: true arg but it doesn't seem to call the subcommand also:

$ go build
$ ./postgres-version-manager-go --version latest install
Reached root Fn:func
var installCommand = &cli.Command{
    Name: "install",
    Desc: "install specified version of PostgreSQL",
    Argv: func() interface{} { return new(InstallStruct) },
    Fn: func(ctx *cli.Context) error {
        ctx.String("Reached installCommand Fn:func")
…

var root = &cli.Command{
    Argv:   func() interface{} { return new(rootCli) },
    Global: true,
    Fn: func(ctx *cli.Context) error {
        ctx.String("Reached root Fn:func")
…

func main() {
    if err := cli.Root(root,
        cli.Tree(installCommand),
        cli.Tree(lsCommand),
    ).Run(os.Args[1:]); err != nil {
        _, err := fmt.Fprintln(os.Stderr, err)
        if err != nil {
            panic(err)
        }
        os.Exit(1)
    }
}

Full code: https://github.com/offscale/postgres-version-manager-go/blob/5d9ef30/main.go

What am I doing wrong?

PS: Also does this library support default env vars (PORT=5432 if not set by $PORT or --port) and positional arguments? - I was thinking to make --version a global arg but also a positional arg, with the positional arg taking priority.

Thanks

suntong commented 1 year ago

Again, I'll defer to @mkideal to answer your question, but meanwhile for

Also does this library support default env vars (PORT=5432 if not set by $PORT or --port) and positional arguments

Take a look at https://github.com/go-easygen/wireframe/wiki/Command-line-flag-handling-code-auto-generation#cli-parameter-priorities

and see if it could be of any help.