cfg @main -
refs -
log -
-
https://git.jolheiser.com/cfg.git
Convert between various configuration formats
feat: refactor for library and add test
Signed-off-by: jolheiser <john.olheiser@gmail.com>
Signature
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEgqEQpE3xoo1QwJO/uFOtpdp7v3oFAmZhDLcACgkQuFOtpdp7
v3pH+w/9FwUHUoultggw6onBZ1krWT1RrNNAcwtgIEle0SqPGThTrw9p/zDjkN5H
0iM+zqlGNlkchQtgovmDapHhsD9rGdKi2saV5/u65F+STK4MscuxEmPDik36gN35
vvpnP708kgsDDXvK3sVoDtLeD5nnxbmPkTqFeJI1EIQBzIaQQhW/yvUtv4k4/EWj
9RZj6eqga2EA1jDYPMCk8fTPiXOKKZUP3vkXo9ZWbaiUXHE6q1XYibNgbpPbSq4u
6bIdJnUmsUZhVkn1bv5O/EZg6tvdlpaFgXJ2sfHSQ+k/JHKZPduSSjflIpTPc2dn
qYaZjzute0sUa1DFWlZPzjaenaBi8A5Z24zsWH7r/3DvvXyC4Tr8L62yCtgcqESo
AIIsxmmFOtO734/lv/4otUPJ1Ua6THhEX7W2VIHa/J+AtFQxxYsGSs1ndk+wwBgj
YfmEGKMsP3UyGTZtWxJBoRbudbvLO+fuA3ThIh+h/kB1wtttkQbsqPhp1Ae7Uac7
n+hKVs6hwQg8F82y8GHdhKIS8M5jjsH4MSgw+HPldePZpbW3TcK+Yl+qlsOY/pGf
r7LXgRKt4UXSizHo7mFf840ClxIP2hV/IXolPQLfRdTDZr27VJjiuQhJwwudgHXp
DHeS1Q4Qxet/JqceEm+L3ixB7RGE2BBTdMfFII9PUK7Rfp6rvfk=
=7yT0
-----END PGP SIGNATURE-----
5 changed files, 180 additions(+), 44 deletions(-)
diff --git a/cfg.go b/cfg.go
new file mode 100644
index 0000000000000000000000000000000000000000..c3d9612364a3f0ce2be9fc02ea1967631c588bf8
--- /dev/null
+++ b/cfg.go
@@ -0,0 +1,94 @@
+package cfg
+
+import (
+ "encoding/json"
+ "fmt"
+ "strings"
+
+ "github.com/pelletier/go-toml/v2"
+ "github.com/philandstuff/dhall-golang/v6"
+ "github.com/tailscale/hujson"
+ "go.jolheiser.com/nixfig"
+ "gopkg.in/yaml.v3"
+)
+
+// Encoding is a type of configuration encoding
+type Encoding string
+
+const (
+ JSON Encoding = "json"
+ JSONC Encoding = "jsonc"
+ YAML Encoding = "yaml"
+ TOML Encoding = "toml"
+ NIX Encoding = "nix"
+ DHALL Encoding = "dhall"
+)
+
+// Marshal takes data and encodes for an [Encoding]
+func (e Encoding) Marshal(v any) ([]byte, error) {
+ return Marshal(e, v)
+}
+
+// Unmarshal decodes data for an [Encoding]
+func (e Encoding) Unmarshal(data []byte, v any) error {
+ return Unmarshal(e, data, v)
+}
+
+// ParseEncoding parses a string into an [Encoding]
+func ParseEncoding(s string) (Encoding, error) {
+ switch strings.ToLower(s) {
+ case "json":
+ return JSON, nil
+ case "jsonc":
+ return JSONC, nil
+ case "yaml":
+ return YAML, nil
+ case "toml":
+ return TOML, nil
+ case "nix":
+ return NIX, nil
+ case "dhall":
+ return DHALL, nil
+ default:
+ return "", fmt.Errorf("unknown encoding %q", s)
+ }
+}
+
+// Marshal is a top-level helper for encoding data
+func Marshal(e Encoding, v any) ([]byte, error) {
+ switch e {
+ case JSON, JSONC:
+ return json.MarshalIndent(v, "", "\t")
+ case YAML:
+ return yaml.Marshal(v)
+ case TOML:
+ return toml.Marshal(v)
+ case NIX:
+ return nixfig.Marshal(v)
+ default:
+ return nil, fmt.Errorf("unknown marshal format %q", e)
+ }
+}
+
+// Unmarshal is a top-level helper for decoding data
+func Unmarshal(e Encoding, data []byte, v any) error {
+ switch e {
+ case JSON, JSONC:
+ b, err := hujson.Standardize(data)
+ if err != nil {
+ return err
+ }
+ return json.Unmarshal(b, v)
+ case YAML:
+ return yaml.Unmarshal(data, v)
+ case TOML:
+ return toml.Unmarshal(data, v)
+ case NIX:
+ return nixfig.Unmarshal(data, v)
+ case DHALL:
+ return dhall.Unmarshal(data, v)
+ default:
+ return fmt.Errorf("unknown unmarshal format %q", e)
+
+ }
+}
diff --git a/cfg_test.go b/cfg_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..79323fa243c124d51255ec5a9e2afcbcb8909460
--- /dev/null
+++ b/cfg_test.go
@@ -0,0 +1,67 @@
+package cfg
+
+import (
+ "testing"
+
+ "github.com/matryer/is"
+ "go.jolheiser.com/nixfig"
+)
+
+type TestData struct {
+ Foo string
+ Baz bool
+ Bux int
+ Qux TestSubData
+}
+
+type TestSubData struct {
+ Honk string
+ Chonk int
+ Gonk bool
+}
+
+func TestEncoding(t *testing.T) {
+ assert := is.New(t)
+
+ // Starting with dhall since it can't be encoded
+ dhall := `
+ {
+ Foo = "bar"
+ , Baz = True
+ , Bux = 10
+ , Qux = {
+ Honk = "bonk"
+ , Chonk = 50
+ , Gonk = False
+ }
+ }
+ `
+ final := TestData{
+ Foo: "bar",
+ Baz: true,
+ Bux: 10,
+ Qux: TestSubData{
+ Honk: "bonk",
+ Chonk: 50,
+ Gonk: false,
+ },
+ }
+
+ encoders := []Encoding{JSON, JSONC, YAML, TOML}
+ // Only test nix if it's available
+ if nixfig.Nix != "" {
+ encoders = append(encoders, NIX)
+ }
+
+ var data TestData
+ err := DHALL.Unmarshal([]byte(dhall), &data)
+ assert.NoErr(err) // Should be able to unmarshal dhall
+
+ for _, e := range encoders {
+ out, err := e.Marshal(data)
+ assert.NoErr(err) // Should be able to marshal
+ err = e.Unmarshal(out, &data)
+ assert.NoErr(err) // Should be able to unmarshal
+ }
+ assert.Equal(data, final) // Final structs should be equal
+}
diff --git a/go.mod b/go.mod
index 15cf553ee69b1f25903cf8613e98f5d76d22ada4..bedd44dffecde29e3d29bc7c296876164c05b762 100644
--- a/go.mod
+++ b/go.mod
@@ -3,6 +3,7 @@
go 1.22.3
require (
+ github.com/matryer/is v1.4.1
github.com/pelletier/go-toml/v2 v2.2.2
github.com/philandstuff/dhall-golang/v6 v6.0.2
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a
@@ -13,4 +14,6 @@
require (
github.com/fxamacker/cbor/v2 v2.2.1-0.20200511212021-28e39be4a84f // indirect
github.com/x448/float16 v0.8.4 // indirect
+ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect
+ golang.org/x/text v0.3.7 // indirect
)
diff --git a/go.sum b/go.sum
index 5cc028741d7146c3d467ab494832f664be87ee82..2a702be2992b154f56f6667931a8690ef5c6c842 100644
--- a/go.sum
+++ b/go.sum
@@ -25,6 +25,8 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
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/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
+github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
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=
@@ -65,12 +67,13 @@ 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=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/main.go b/cmd/cfg/main.go
rename from main.go
rename to cmd/cfg/main.go
index 213bddb0a269c96a3a081848c9eec024afa4be59..71625957efd9690d9ff4f81139406b09a15068fa 100644
--- a/main.go
+++ b/cmd/cfg/main.go
@@ -1,7 +1,6 @@
package main
import (
- "encoding/json"
"errors"
"flag"
"fmt"
@@ -9,11 +8,7 @@ "os"
"path/filepath"
"strings"
- "github.com/pelletier/go-toml/v2"
- "github.com/philandstuff/dhall-golang/v6"
- "github.com/tailscale/hujson"
- "go.jolheiser.com/nixfig"
-package main
+ "os"
"errors"
)
@@ -23,54 +18,28 @@ var marshal func(any) ([]byte, error)
fs := flag.NewFlagSet("cfg", flag.ExitOnError)
fromFunc := func(s string) error {
- switch strings.ToLower(s) {
+ e, err := cfg.ParseEncoding(s)
- case "json", "jsonc":
- unmarshal = func(b []byte, a any) error {
-
+ "flag"
"errors"
-
"flag"
- return err
- }
- return json.Unmarshal(b, a)
- }
-import (
package main
- unmarshal = yaml.Unmarshal
- case "toml":
-import (
"encoding/json"
- case "dhall":
- unmarshal = dhall.Unmarshal
- case "nix":
+package main
-import (
"os"
- default:
- return fmt.Errorf("unknown format %q", s)
- }
+ "fmt"
return nil
}
fs.Func("from", "The format to convert from", fromFunc)
fs.Func("f", "--from", fromFunc)
toFunc := func(s string) error {
- switch strings.ToLower(s) {
- case "json", "jsonc":
- marshal = func(a any) ([]byte, error) {
- "encoding/json"
"os"
+ "flag"
- }
+ if err != nil {
-import (
+ "flag"
package main
"encoding/json"
- "path/filepath"
- case "toml":
- marshal = toml.Marshal
- case "nix":
- "errors"
package main
- default:
- return fmt.Errorf("unknown format %q", s)
- }
+ marshal = e.Marshal
return nil
}
fs.Func("to", "The format to convert to", toFunc)
@@ -105,7 +74,7 @@ return err
}
}
} else if marshal == nil {
- marshal = json.Marshal
+ marshal = cfg.JSON.Marshal
}
var data any