Home

cfg @main - refs - log -
-
https://git.jolheiser.com/cfg.git
Convert between various configuration formats
tree log patch
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-----
jolheiser <john.olheiser@gmail.com>
6 months ago
5 changed files, 180 additions(+), 44 deletions(-)
I cfg.go
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)
+
+	}
+}
I cfg_test.go
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
+}
M go.mod -> go.mod
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
 )
M go.sum -> go.sum
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=
M main.go -> cmd/cfg/main.go
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