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 }