Skip to content

Commit ee42d6f

Browse files
authored
pkg/config/configdoc: move DefaultsOnly (#1526)
1 parent 7713941 commit ee42d6f

2 files changed

Lines changed: 109 additions & 88 deletions

File tree

pkg/config/configdoc/defaults.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package configdoc
2+
3+
import (
4+
"bufio"
5+
"encoding"
6+
"fmt"
7+
"io"
8+
"reflect"
9+
"strings"
10+
)
11+
12+
// DefaultsOnly reads only the default values from a docs TOML file and decodes in to cfg.
13+
// Fields without defaults will set to zero values.
14+
// Arrays of tables are ignored.
15+
func DefaultsOnly(r io.Reader, cfg any, decode func(io.Reader, any) error) error {
16+
pr, pw := io.Pipe()
17+
defer pr.Close()
18+
go writeDefaults(r, pw)
19+
if err := decode(pr, cfg); err != nil {
20+
return fmt.Errorf("failed to decode default core configuration: %w", err)
21+
}
22+
// replace niled examples with zero values.
23+
nilToZero(reflect.ValueOf(cfg))
24+
return nil
25+
}
26+
27+
// writeDefaults writes default lines from defaultsTOML to w.
28+
func writeDefaults(r io.Reader, w *io.PipeWriter) {
29+
defer w.Close()
30+
s := bufio.NewScanner(r)
31+
var skipUntil func(line string) bool
32+
for s.Scan() {
33+
t := s.Text()
34+
if skipUntil != nil {
35+
if skipUntil(t) {
36+
skipUntil = nil
37+
}
38+
continue
39+
}
40+
// Skip comments and examples (which become zero values)
41+
if strings.HasPrefix(t, "#") || strings.HasSuffix(t, "# Example") {
42+
continue
43+
}
44+
// Skip arrays of tables
45+
if strings.HasPrefix(t, "[[") {
46+
// skip fields until next empty line
47+
skipUntil = func(line string) bool { return strings.TrimSpace(line) == "" }
48+
continue
49+
}
50+
if _, err := io.WriteString(w, t); err != nil {
51+
w.CloseWithError(err)
52+
}
53+
if _, err := w.Write([]byte{'\n'}); err != nil {
54+
w.CloseWithError(err)
55+
}
56+
}
57+
if err := s.Err(); err != nil {
58+
w.CloseWithError(fmt.Errorf("failed to scan core defaults: %w", err))
59+
}
60+
}
61+
62+
var (
63+
textUnmarshaler encoding.TextUnmarshaler
64+
textUnmarshalerType = reflect.TypeOf(&textUnmarshaler).Elem()
65+
)
66+
67+
func nilToZero(val reflect.Value) {
68+
if val.Kind() == reflect.Ptr {
69+
if val.IsNil() {
70+
t := val.Type().Elem()
71+
val.Set(reflect.New(t))
72+
}
73+
if val.Type().Implements(textUnmarshalerType) {
74+
return // don't descend inside - leave whole zero value
75+
}
76+
val = val.Elem()
77+
}
78+
switch val.Kind() {
79+
case reflect.Struct:
80+
if val.Type().Implements(textUnmarshalerType) {
81+
return // skip values unmarshaled from strings
82+
}
83+
for i := 0; i < val.NumField(); i++ {
84+
f := val.Field(i)
85+
nilToZero(f)
86+
}
87+
return
88+
case reflect.Map:
89+
if !val.IsNil() {
90+
for _, k := range val.MapKeys() {
91+
nilToZero(val.MapIndex(k))
92+
}
93+
}
94+
return
95+
case reflect.Slice:
96+
if !val.IsNil() {
97+
for i := 0; i < val.Len(); i++ {
98+
nilToZero(val.Index(i))
99+
}
100+
}
101+
return
102+
default:
103+
return
104+
}
105+
}

pkg/config/configtest/defaults.go

Lines changed: 4 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,99 +1,15 @@
11
package configtest
22

33
import (
4-
"bufio"
5-
"fmt"
64
"io"
7-
"reflect"
8-
"strings"
5+
6+
"github.com/smartcontractkit/chainlink-common/pkg/config/configdoc"
97
)
108

119
// DocDefaultsOnly reads only the default values from a docs TOML file and decodes in to cfg.
1210
// Fields without defaults will set to zero values.
1311
// Arrays of tables are ignored.
12+
// Deprecated: use configdoc.DefaultsOnly
1413
func DocDefaultsOnly(r io.Reader, cfg any, decode func(io.Reader, any) error) error {
15-
pr, pw := io.Pipe()
16-
defer pr.Close()
17-
go writeDefaults(r, pw)
18-
if err := decode(pr, cfg); err != nil {
19-
return fmt.Errorf("failed to decode default core configuration: %w", err)
20-
}
21-
// replace niled examples with zero values.
22-
nilToZero(reflect.ValueOf(cfg))
23-
return nil
24-
}
25-
26-
// writeDefaults writes default lines from defaultsTOML to w.
27-
func writeDefaults(r io.Reader, w *io.PipeWriter) {
28-
defer w.Close()
29-
s := bufio.NewScanner(r)
30-
var skipUntil func(line string) bool
31-
for s.Scan() {
32-
t := s.Text()
33-
if skipUntil != nil {
34-
if skipUntil(t) {
35-
skipUntil = nil
36-
}
37-
continue
38-
}
39-
// Skip comments and examples (which become zero values)
40-
if strings.HasPrefix(t, "#") || strings.HasSuffix(t, "# Example") {
41-
continue
42-
}
43-
// Skip arrays of tables
44-
if strings.HasPrefix(t, "[[") {
45-
// skip fields until next empty line
46-
skipUntil = func(line string) bool { return strings.TrimSpace(line) == "" }
47-
continue
48-
}
49-
if _, err := io.WriteString(w, t); err != nil {
50-
w.CloseWithError(err)
51-
}
52-
if _, err := w.Write([]byte{'\n'}); err != nil {
53-
w.CloseWithError(err)
54-
}
55-
}
56-
if err := s.Err(); err != nil {
57-
w.CloseWithError(fmt.Errorf("failed to scan core defaults: %w", err))
58-
}
59-
}
60-
61-
func nilToZero(val reflect.Value) {
62-
if val.Kind() == reflect.Ptr {
63-
if val.IsNil() {
64-
t := val.Type().Elem()
65-
val.Set(reflect.New(t))
66-
}
67-
if val.Type().Implements(textUnmarshalerType) {
68-
return // don't descend inside - leave whole zero value
69-
}
70-
val = val.Elem()
71-
}
72-
switch val.Kind() {
73-
case reflect.Struct:
74-
if val.Type().Implements(textUnmarshalerType) {
75-
return // skip values unmarshaled from strings
76-
}
77-
for i := 0; i < val.NumField(); i++ {
78-
f := val.Field(i)
79-
nilToZero(f)
80-
}
81-
return
82-
case reflect.Map:
83-
if !val.IsNil() {
84-
for _, k := range val.MapKeys() {
85-
nilToZero(val.MapIndex(k))
86-
}
87-
}
88-
return
89-
case reflect.Slice:
90-
if !val.IsNil() {
91-
for i := 0; i < val.Len(); i++ {
92-
nilToZero(val.Index(i))
93-
}
94-
}
95-
return
96-
default:
97-
return
98-
}
14+
return configdoc.DefaultsOnly(r, cfg, decode)
9915
}

0 commit comments

Comments
 (0)