Skip to content

Commit

Permalink
fix: adds support for hierarchy
Browse files Browse the repository at this point in the history
  • Loading branch information
joicemjoseph committed Nov 4, 2024
1 parent b285c94 commit efa8f7b
Showing 1 changed file with 89 additions and 16 deletions.
105 changes: 89 additions & 16 deletions providers/cliflag/cliflag.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@
package cliflag

import (
"errors"
"strings"

"github.com/knadh/koanf/maps"
"github.com/urfave/cli/v2"
)

// CliFlag implements a cli.Flag command line provider.
type CliFlag struct {
flagset *cli.Context
delim string
ctx *cli.Context
delim string
}

// Provider returns a commandline flags provider that returns
Expand All @@ -22,31 +22,104 @@ type CliFlag struct {
// to `{parent: {child: {key: 1}}}`.
func Provider(f *cli.Context, delim string) *CliFlag {
return &CliFlag{
flagset: f,
delim: delim,
ctx: f,
delim: delim,
}
}

// Read reads the flag variables and returns a nested conf map.
func (p *CliFlag) Read() (map[string]interface{}, error) {
mp := make(map[string]interface{})
out := make(map[string]interface{})

for _, v := range p.flagset.FlagNames() {
mp[v] = p.flagset.Value(v)
// Get command lineage (from root to current command)
lineage := p.ctx.Lineage()
if len(lineage) > 0 {
// Build command path and process flags for each level
var cmdPath []string
for i := len(lineage) - 1; i >= 0; i-- {
cmd := lineage[i]
if cmd.Command == nil {
continue
}
cmdPath = append(cmdPath, cmd.Command.Name)
prefix := strings.Join(cmdPath, p.delim)
p.processFlags(cmd.Command.Flags, prefix, out)
}

// Process current command's flags
if p.ctx.Command != nil {
cmdPath = append(cmdPath, p.ctx.Command.Name)
prefix := strings.Join(cmdPath, p.delim)
p.processFlags(p.ctx.Command.Flags, prefix, out)
}
}

if p.delim == "" {
return mp, nil
return out, nil
}

return maps.Unflatten(mp, p.delim), nil
return maps.Unflatten(out, p.delim), nil
}

func (p *CliFlag) processFlags(flags []cli.Flag, prefix string, out map[string]interface{}) {
for _, flag := range flags {
name := flag.Names()[0]
if p.ctx.IsSet(name) {
value := p.getFlagValue(name)
if value != nil {
// Build the full path for the flag
fullPath := name
if prefix != "global" {
fullPath = prefix + p.delim + name
}

p.setNestedValue(fullPath, value, out)
}
}
}
}

// ReadBytes is not supported by the cliflag provider.
func (p *CliFlag) ReadBytes() ([]byte, error) {
return nil, errors.New("cliflag provider does not support this method")
// setNestedValue sets a value in the nested configuration structure
func (p *CliFlag) setNestedValue(path string, value interface{}, out map[string]interface{}) {
parts := strings.Split(path, p.delim)
current := out

// Navigate/create the nested structure
for i := 0; i < len(parts)-1; i++ {
if _, exists := current[parts[i]]; !exists {
current[parts[i]] = make(map[string]interface{})
}
current = current[parts[i]].(map[string]interface{})
}

// Set the final value
current[parts[len(parts)-1]] = value
}

// Watch is not supported by cliFlag.
func (p *CliFlag) Watch(cb func(event interface{}, err error)) error {
return errors.New("posflag provider does not support this method")
// getFlagValue extracts the typed value from the flag
func (p *CliFlag) getFlagValue(name string) interface{} {
switch {
case p.ctx.IsSet(name):
switch {
case p.ctx.String(name) != "":
return p.ctx.String(name)
case p.ctx.StringSlice(name) != nil:
return p.ctx.StringSlice(name)
case p.ctx.Int(name) != 0:
return p.ctx.Int(name)
case p.ctx.Int64(name) != 0:
return p.ctx.Int64(name)
case p.ctx.IntSlice(name) != nil:
return p.ctx.IntSlice(name)
case p.ctx.Float64(name) != 0:
return p.ctx.Float64(name)
case p.ctx.Bool(name):
return p.ctx.Bool(name)
case p.ctx.Duration(name).String() != "0s":
return p.ctx.Duration(name)
default:
return p.ctx.Generic(name)
}
}
return nil
}

0 comments on commit efa8f7b

Please sign in to comment.