conf/adapters.go

147 lines
3.1 KiB
Go

package conf
import (
"encoding/json"
"errors"
"fmt"
"strings"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/gohcl"
"github.com/hashicorp/hcl/v2/hclsimple"
"github.com/hashicorp/hcl/v2/hclwrite"
"github.com/pelletier/go-toml/v2"
)
type filetype int
const (
typeInvalid filetype = iota
typeJSON
typeTOML
typeHCL
)
// getType returns the type of the config file.
func getType(filename string) filetype {
switch {
case strings.HasSuffix(filename, ".json"):
return typeJSON
case strings.HasSuffix(filename, ".toml"):
return typeTOML
case strings.HasSuffix(filename, ".hcl"):
return typeHCL
default:
return typeInvalid
}
}
// unmarshal unmarshals the given data to the given struct.
func unmarshal(filepath string, data []byte, v any) error {
ft := getType(filepath)
switch ft {
case typeJSON:
return unmarshalJSON(data, v)
case typeTOML:
return unmarshalTOML(data, v)
case typeHCL:
return unmarshalHCL(filepath, data, v)
default:
return ErrUnsupportedFileType
}
}
// marshal marshals the given struct to bytes.
func marshal(ft filetype, v any) ([]byte, error) {
switch ft {
case typeJSON:
return marshalJSON(v)
case typeTOML:
return marshalTOML(v)
case typeHCL:
return marshalHCL(v)
default:
return nil, ErrUnsupportedFileType
}
}
// unmarshalJSON unmarshals the given data to the given struct.
func unmarshalJSON(data []byte, v any) error {
err := json.Unmarshal(data, v)
if err != nil {
return fmt.Errorf("cannot parse config file: %w", err)
}
return nil
}
// marshalJSON marshals the given struct to bytes.
func marshalJSON(v any) ([]byte, error) {
data, err := json.MarshalIndent(v, "", " ")
if err != nil {
return nil, fmt.Errorf("cannot generate config content: %w", err)
}
return data, nil
}
// unmarshalTOML unmarshals the given data to the given struct.
func unmarshalTOML(data []byte, v any) error {
err := toml.Unmarshal(data, v)
if err != nil {
return fmt.Errorf("cannot parse config file: %w", err)
}
return nil
}
// marshalTOML marshals the given struct to bytes.
func marshalTOML(v any) ([]byte, error) {
data, err := toml.Marshal(v)
if err != nil {
return nil, fmt.Errorf("cannot generate config content: %w", err)
}
return data, nil
}
// unmarshalHCL unmarshals the given data to the given struct.
func unmarshalHCL(filepath string, data []byte, v any) error {
err := hclsimple.Decode(filepath, data, nil, v)
var diags hcl.Diagnostics
errors.As(err, &diags)
newDiags := hclFilterDiagnostics(diags)
if len(newDiags) > 0 {
return fmt.Errorf("cannot parse config file: %w", newDiags)
}
return nil
}
// marshalHCL marshals the given struct to bytes.
func marshalHCL(v any) (b []byte, err error) { //nolint:nonamedreturns // need named return to convert a panic to error
f := hclwrite.NewEmptyFile()
gohcl.EncodeIntoBody(v, f.Body())
return f.Bytes(), nil
}
func hclFilterDiagnostics(diags hcl.Diagnostics) hcl.Diagnostics {
var newDiags hcl.Diagnostics
for _, diag := range diags {
if diag.Summary != "Unsupported argument" {
newDiags = append(newDiags, diag)
}
}
if len(newDiags) > 0 {
return newDiags
}
return nil
}