goo/config.go

137 lines
2.8 KiB
Go

package main
import (
"errors"
"fmt"
"os"
"strings"
"reflect"
"github.com/hjson/hjson-go/v4"
)
// Errors
var ErrUndefined = errors.New("value not defined")
var ErrFileNotFound = errors.New("file not found")
var ErrParse = errors.New("file could not be parsed")
var ErrType = errors.New("invalid type")
var ErrKey = errors.New("key not found")
// File and parsing errors
type ParseError struct{
File string
Err error
}
func (e *ParseError) Error() string {
return fmt.Sprintf("Error parsing configuration file %s: %s",
e.File, e.Err.Error())
}
func (e *ParseError) Unwrap() error { return e.Err }
func fileNotFoundError(file string) *ParseError {
return &ParseError{file, ErrFileNotFound}
}
func parsingError(file string) *ParseError {
return &ParseError{file, ErrParse}
}
// Config struct errors
type ConfigError struct {
Key string
Value interface{}
Err error
}
func (e *ConfigError) Error() string {
return fmt.Sprintf("Error in configuration for %s: %s",
e.Key, e.Err.Error())
}
func (e *ConfigError) Unwrap() error { return e.Err }
func undefinedError(key string) *ConfigError {
return &ConfigError{key, nil, ErrUndefined}
}
func ParseConfig(file string) (Config, error) {
var err error
var c Config
fp, err := os.ReadFile(file)
if err != nil {
return Config{}, fileNotFoundError(file)
}
err = hjson.Unmarshal(fp, &c)
if err != nil {
fmt.Println(err)
return Config{}, parsingError(file)
}
return c, nil
}
func ParseServiceConfig(file string) (ServiceConfig, error) {
var err error
var c ServiceConfig
fp, err := os.ReadFile(file)
if err != nil {
return ServiceConfig{}, fileNotFoundError(file)
}
err = hjson.Unmarshal(fp, &c)
if err != nil {
fmt.Println(err)
return ServiceConfig{}, parsingError(file)
}
return c, nil
}
// Get the first host port from a service
func (s ServiceConfig) GetHostPort() (string, error) {
if s.Ports == nil {
return "", undefinedError("ports")
}
ports := strings.Split(s.Ports[0], ":")
if len(ports) == 0 {
return "", undefinedError("ports")
}
return ports[0], nil
}
// Walk a tree of nested structures or maps until the node is found.
// Node must be of type string or array.
func GetValue(i interface{}, path string) (interface{}, error) {
for _, p := range strings.Split(path, ".") {
switch reflect.ValueOf(i).Kind() {
case reflect.Struct:
v := reflect.ValueOf(i).FieldByName(p)
if v.IsValid() == false { return nil, ErrKey }
i = v.Interface()
case reflect.Map:
v := reflect.ValueOf(i)
if v.IsValid() == false { return nil, ErrKey }
i = v.MapIndex(reflect.ValueOf(p)).Interface()
case reflect.Array, reflect.Slice:
return i, nil
case reflect.String:
return i, nil
default:
fmt.Printf("%s is not a valid type\n",
reflect.TypeOf(i).Kind())
return nil, ErrType
}
}
return i, nil
}