mirror of https://git.jolheiser.com/ffdhall.git
commit
1bcb2c430f
|
@ -0,0 +1,64 @@
|
|||
// This is taken from https://github.com/peterbourgon/ff/blob/e267c41b1d149b5151fb637d65f53b6e833448fd/internal/traverse_map.go
|
||||
package ffdhall
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// traverseMap recursively walks the given map, calling set for each value. If the
|
||||
// value is a slice, set is called for each element of the slice. The keys of
|
||||
// nested maps are joined with the given delimiter.
|
||||
func traverseMap(m map[string]any, delimiter string, set func(name, value string) error) error {
|
||||
return traverse("", m, delimiter, set)
|
||||
}
|
||||
|
||||
func traverse(key string, val any, delimiter string, set func(name, value string) error) error {
|
||||
switch v := val.(type) {
|
||||
case string:
|
||||
return set(key, v)
|
||||
case json.Number:
|
||||
return set(key, v.String())
|
||||
case uint64:
|
||||
return set(key, strconv.FormatUint(v, 10))
|
||||
case int:
|
||||
return set(key, strconv.Itoa(v))
|
||||
case int64:
|
||||
return set(key, strconv.FormatInt(v, 10))
|
||||
case float64:
|
||||
return set(key, strconv.FormatFloat(v, 'g', -1, 64))
|
||||
case bool:
|
||||
return set(key, strconv.FormatBool(v))
|
||||
case nil:
|
||||
return set(key, "")
|
||||
case []any:
|
||||
for _, v := range v {
|
||||
if err := traverse(key, v, delimiter, set); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case map[string]any:
|
||||
for k, v := range v {
|
||||
if key != "" {
|
||||
k = key + delimiter + k
|
||||
}
|
||||
if err := traverse(k, v, delimiter, set); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case map[any]any:
|
||||
for k, v := range v {
|
||||
ks := fmt.Sprint(k)
|
||||
if key != "" {
|
||||
ks = key + delimiter + ks
|
||||
}
|
||||
if err := traverse(ks, v, delimiter, set); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("couldn't convert %q (type %T) to string", val, val)
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package ffdhall
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/philandstuff/dhall-golang/v6"
|
||||
)
|
||||
|
||||
// DhallParser is a helper function that uses a default DhallParseConfig.
|
||||
func DhallParser(r io.Reader, set func(name, value string) error) error {
|
||||
return (&DhallParseConfig{}).Parse(r, set)
|
||||
}
|
||||
|
||||
// DhallParseConfig collects parameters for the Dhall config file parser.
|
||||
type DhallParseConfig struct {
|
||||
// Delimiter is used when concatenating nested node keys into a flag name.
|
||||
// The default delimiter is ".".
|
||||
Delimiter string
|
||||
}
|
||||
|
||||
// Parse a Dhall document from the provided io.Reader, using the provided set
|
||||
// function to set flag values. Flag names are derived from the node names and
|
||||
// their key/value pairs.
|
||||
func (pc *DhallParseConfig) Parse(r io.Reader, set func(name, value string) error) error {
|
||||
if pc.Delimiter == "" {
|
||||
pc.Delimiter = "."
|
||||
}
|
||||
|
||||
data, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Compatibility with other parsers
|
||||
if strings.TrimSpace(string(data)) == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var m interface{}
|
||||
if err := dhall.Unmarshal(data, &m); err != nil {
|
||||
return DhallParseError{Inner: err}
|
||||
}
|
||||
mm, ok := m.(map[string]interface{})
|
||||
if !ok {
|
||||
return errors.New("could not unmarshal to map[string]interface{}")
|
||||
}
|
||||
|
||||
if err := traverseMap(mm, pc.Delimiter, set); err != nil {
|
||||
return DhallParseError{Inner: err}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DhallParseError wraps all errors originating from the DhallParser.
|
||||
type DhallParseError struct {
|
||||
Inner error
|
||||
}
|
||||
|
||||
// Error implenents the error interface.
|
||||
func (e DhallParseError) Error() string {
|
||||
return fmt.Sprintf("could not parse Dhall config: %v", e.Inner)
|
||||
}
|
||||
|
||||
// Unwrap implements the errors.Wrapper interface, allowing errors.Is and
|
||||
// errors.As to work with DhallParseErrors.
|
||||
func (e DhallParseError) Unwrap() error {
|
||||
return e.Inner
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package ffdhall
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/peterbourgon/ff/v3"
|
||||
"github.com/peterbourgon/ff/v3/fftest"
|
||||
)
|
||||
|
||||
func TestDhallParser(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, testcase := range []struct {
|
||||
name string
|
||||
args []string
|
||||
file string
|
||||
want fftest.Vars
|
||||
}{
|
||||
{
|
||||
name: "empty input",
|
||||
args: []string{},
|
||||
file: "testdata/empty.dhall",
|
||||
want: fftest.Vars{},
|
||||
},
|
||||
{
|
||||
name: "basic KV pairs",
|
||||
args: []string{},
|
||||
file: "testdata/basic.dhall",
|
||||
want: fftest.Vars{S: "s", I: 10, B: true, D: 5 * time.Second},
|
||||
},
|
||||
{
|
||||
name: "value arrays",
|
||||
args: []string{},
|
||||
file: "testdata/value_arrays.dhall",
|
||||
want: fftest.Vars{S: "bb", I: 12, B: true, D: 5 * time.Second, X: []string{"a", "B", "👍"}},
|
||||
},
|
||||
{
|
||||
name: "bad Dhall file",
|
||||
args: []string{},
|
||||
file: "testdata/bad.dhall",
|
||||
want: fftest.Vars{WantParseErrorString: "no match found"},
|
||||
},
|
||||
} {
|
||||
t.Run(testcase.name, func(t *testing.T) {
|
||||
fs, vars := fftest.Pair()
|
||||
vars.ParseError = ff.Parse(fs, testcase.args,
|
||||
ff.WithConfigFile(testcase.file),
|
||||
ff.WithConfigFileParser(DhallParser),
|
||||
)
|
||||
fmt.Println(vars.ParseError)
|
||||
fftest.Compare(t, &testcase.want, vars)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
module go.jolheiser.com/ffdhall
|
||||
|
||||
go 1.22.3
|
||||
|
||||
require (
|
||||
github.com/peterbourgon/ff/v3 v3.4.0
|
||||
github.com/philandstuff/dhall-golang/v6 v6.0.2
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/fxamacker/cbor/v2 v2.2.1-0.20200511212021-28e39be4a84f // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
)
|
|
@ -0,0 +1,53 @@
|
|||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fxamacker/cbor/v2 v2.2.1-0.20200511212021-28e39be4a84f h1:lvGFo/tDOSQ4FKu0d2694s8XyOfAL6FLR9DCD5BIUW4=
|
||||
github.com/fxamacker/cbor/v2 v2.2.1-0.20200511212021-28e39be4a84f/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/leanovate/gopter v0.2.5-0.20190402064358-634a59d12406 h1:+OUpk+IVvmKU0jivOVFGtOzA6U5AWFs8HE4DRzWLOUE=
|
||||
github.com/leanovate/gopter v0.2.5-0.20190402064358-634a59d12406/go.mod h1:gNcbPWNEWRe4lm+bycKqxUYoH5uoVje5SkOJ3uoLer8=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/peterbourgon/ff/v3 v3.4.0 h1:QBvM/rizZM1cB0p0lGMdmR7HxZeI/ZrBWB4DqLkMUBc=
|
||||
github.com/peterbourgon/ff/v3 v3.4.0/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ=
|
||||
github.com/philandstuff/dhall-golang/v6 v6.0.2 h1:jv8fi4ZYiFe6uGrprx6dY7L3xPcgmEqWZo3s8ABCzkw=
|
||||
github.com/philandstuff/dhall-golang/v6 v6.0.2/go.mod h1:XRoxjsqZM2y7KPFhjV7CSVdWpV5CwuTzGjAY/v+1SUU=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
|
@ -0,0 +1 @@
|
|||
{
|
|
@ -0,0 +1 @@
|
|||
{ s = "s", i = 10, b = True, d = "5s" }
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
{ s = [ "a", "bb" ]
|
||||
, i = [ "10", "11", "12" ]
|
||||
, b = [ False, True ]
|
||||
, d = [ "10m", "5s" ]
|
||||
, x = [ "a", "B", "👍" ]
|
||||
}
|
Loading…
Reference in New Issue