Dirk
Dirk

Reputation: 233

Go CLI Framework with context aware flags

I'm trying to create command lines like the following

myapp --config=config1.yaml --start=step1 --start=step2 --config=config2.yamp --start=step1

There are multipe --start flags, each of it corresponds with the previous --config flag. Is there any cli framework that supports flags like this? Can this be done with the standard go flags package

Upvotes: 2

Views: 38

Answers (1)

kozmo
kozmo

Reputation: 4490

Can this be done with the standard go flags package?

There is no default behavior, but you can implement it yourself. For example, you can create custom implementation of flag.Value interface to Parse flags (use FlagSet) with "position" in args and than define which --start flags belong to --config flag.

var (
    // flag position
    fPosition = 1
)

type FlagWithPosition struct {
    Position int
    Val      string
}

type FlagsWithPosition []FlagWithPosition

func (s *FlagsWithPosition) String() string {
    return fmt.Sprintf("%v", *s)
}

func (s *FlagsWithPosition) Set(value string) error {
    value = strings.TrimSpace(value)
    if value == "" {
        return nil
    }
    *s = append(*s, FlagWithPosition{
        Position: fPosition, //position in args
        Val:      value,
    })
    fPosition++ // increment position in args
    return nil
}

func main() {
    var (
        configs FlagsWithPosition
        steps   FlagsWithPosition
    )

    // Use FlagSet
    fs := flag.NewFlagSet("ExampleValue", flag.ExitOnError)
    fs.Var(&configs, "config", "configurations")
    fs.Var(&steps, "start", "steps")

    // prepare args
    args := strings.Split("--config=config1.yaml --start=step1 --start=step2 --config=config2.yamp --start=step1", " ")
    // parse flags using FlagSet
    _ = fs.Parse(args)

    fmt.Println(configs)
    fmt.Println(steps)
    fmt.Println(getConfigsWithSteps(configs, steps)) // map[config1.yaml:[step1 step2] config2.yamp:[step1]]
}

// getConfigsWithSteps - EXAMPLE 
func getConfigsWithSteps(configs, steps FlagsWithPosition) map[string][]string {
    configsWithSteps := make(map[string][]string, len(configs))
    for i, config := range configs {
        var (
            nextPosition = -1
        )
        if len(configs) > i+1 {
            nextPosition = configs[i+1].Position
        }
        for j, step := range steps {
            if nextPosition > -1 && step.Position >= nextPosition {
                steps = steps[j:]
                break
            }
            configsWithSteps[config.Val] = append(configsWithSteps[config.Val], step.Val)
        }
    }

    return configsWithSteps
}

PLAYGROUND

Upvotes: 0

Related Questions