File: config.go

package info (click to toggle)
golang-github-tdewolff-argp 0.0~git20240625.87b04d5-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 152 kB
  • sloc: makefile: 2
file content (94 lines) | stat: -rw-r--r-- 2,078 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package argp

import (
	"fmt"
	"os"
	"path/filepath"
	"reflect"

	"github.com/pelletier/go-toml"
)

// Config is an option that sets all options from a configuration file.
type Config struct {
	Argp     *Argp
	Filename string
}

func (config *Config) Help() (string, string) {
	return config.Filename, "string"
}

func (config *Config) Scan(name string, s []string) (int, error) {
	n, err := scanValue(reflect.ValueOf(&config.Filename).Elem(), s)
	if err != nil {
		return n, err
	}

	f, err := os.Open(config.Filename)
	if err != nil {
		return n, err
	}
	defer f.Close()

	values := map[string]interface{}{}
	switch ext := filepath.Ext(config.Filename); ext {
	case ".toml":
		if err := toml.NewDecoder(f).Decode(&values); err != nil {
			return n, fmt.Errorf("toml: %v", err)
		}
	default:
		return n, fmt.Errorf("unknown configuration file extension: %s", ext)
	}

	if err := config.unmarshal("", values); err != nil {
		return n, err
	}
	return n, nil
}

func (config *Config) unmarshal(prefix string, values map[string]interface{}) error {
	for key, ival := range values {
		name := key
		if prefix != "" {
			name = prefix + "." + name
		}
		if val, ok := ival.(map[string]interface{}); ok {
			if err := config.unmarshal(name, val); err != nil {
				return err
			}
			continue
		}

		v := config.Argp.findName(name)
		if v == nil {
			continue
		}

		vals := []string{}
		switch val := ival.(type) {
		case string:
			if val[0] == '[' || val[0] == '{' {
				vals = splitArguments(val)
			} else {
				vals = []string{val}
			}
		case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, bool:
			vals = []string{fmt.Sprintf("%v", ival)}
		case []interface{}:
			vals = append(vals, "[")
			for _, v := range val {
				vals = append(vals, fmt.Sprintf("%v", v))
			}
			vals = append(vals, "]")
		default:
			return fmt.Errorf("%s: unknown type", name)
		}
		if n, err := scanVar(v.Value, name, vals); err != nil {
			return fmt.Errorf("%s: %v", name, err)
		} else if n != len(vals) {
			return fmt.Errorf("%s: invalid value", name)
		}
	}
	return nil
}