...
1
2 package fftoml
3
4 import (
5 "fmt"
6 "io"
7 "strconv"
8
9 "github.com/pelletier/go-toml"
10 "github.com/peterbourgon/ff/v3"
11 )
12
13
14
15 func Parser(r io.Reader, set func(name, value string) error) error {
16 return New().Parse(r, set)
17 }
18
19
20
21
22
23 type ConfigFileParser struct {
24 delimiter string
25 }
26
27
28 func New(opts ...Option) (c ConfigFileParser) {
29 c.delimiter = "."
30 for _, opt := range opts {
31 opt(&c)
32 }
33 return c
34 }
35
36
37
38 func (c ConfigFileParser) Parse(r io.Reader, set func(name, value string) error) error {
39 tree, err := toml.LoadReader(r)
40 if err != nil {
41 return ParseError{Inner: err}
42 }
43
44 return parseTree(tree, "", c.delimiter, set)
45 }
46
47
48 type Option func(*ConfigFileParser)
49
50
51
52
53
54
55
56
57
58
59
60
61
62 func WithTableDelimiter(d string) Option {
63 return func(c *ConfigFileParser) {
64 c.delimiter = d
65 }
66 }
67
68 func parseTree(tree *toml.Tree, parent, delimiter string, set func(name, value string) error) error {
69 for _, key := range tree.Keys() {
70 name := key
71 if parent != "" {
72 name = parent + delimiter + key
73 }
74 switch t := tree.Get(key).(type) {
75 case *toml.Tree:
76 if err := parseTree(t, name, delimiter, set); err != nil {
77 return err
78 }
79 case interface{}:
80 values, err := valsToStrs(t)
81 if err != nil {
82 return ParseError{Inner: err}
83 }
84 for _, value := range values {
85 if err = set(name, value); err != nil {
86 return err
87 }
88 }
89 }
90 }
91 return nil
92 }
93
94 func valsToStrs(val interface{}) ([]string, error) {
95 if vals, ok := val.([]interface{}); ok {
96 ss := make([]string, len(vals))
97 for i := range vals {
98 s, err := valToStr(vals[i])
99 if err != nil {
100 return nil, err
101 }
102 ss[i] = s
103 }
104 return ss, nil
105 }
106 s, err := valToStr(val)
107 if err != nil {
108 return nil, err
109 }
110 return []string{s}, nil
111
112 }
113
114 func valToStr(val interface{}) (string, error) {
115 switch v := val.(type) {
116 case string:
117 return v, nil
118 case bool:
119 return strconv.FormatBool(v), nil
120 case uint64:
121 return strconv.FormatUint(v, 10), nil
122 case int64:
123 return strconv.FormatInt(v, 10), nil
124 case float64:
125 return strconv.FormatFloat(v, 'g', -1, 64), nil
126 default:
127 return "", ff.StringConversionError{Value: val}
128 }
129 }
130
131
132 type ParseError struct {
133 Inner error
134 }
135
136
137 func (e ParseError) Error() string {
138 return fmt.Sprintf("error parsing TOML config: %v", e.Inner)
139 }
140
141
142
143 func (e ParseError) Unwrap() error {
144 return e.Inner
145 }
146
View as plain text