-
Notifications
You must be signed in to change notification settings - Fork 25
/
include.go
100 lines (90 loc) · 3.3 KB
/
include.go
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
95
96
97
98
99
100
// Copyright (c) 2023, The Emergent Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// note: FindFileOnPaths adapted from viper package https://github.com/spf13/viper
// Copyright (c) 2014 Steve Francia
package econfig
import (
"reflect"
"github.com/goki/ki/kit"
"github.com/goki/ki/toml"
)
// Includeser enables processing of Includes []string field with files to include in Config objects.
type Includeser interface {
// IncludesPtr returns a pointer to the Includes []string field containing file(s) to include
// before processing the current config file.
IncludesPtr() *[]string
}
// Includer enables processing of Include string field with file to include in Config objects.
type Includer interface {
// IncludePtr returns a pointer to the Include string field containing single file to include
// before processing the current config file.
IncludePtr() *string
}
// IncludesStack returns the stack of include files in the natural
// order in which they are encountered (nil if none).
// Files should then be read in reverse order of the slice.
// Returns an error if any of the include files cannot be found on IncludePath.
// Does not alter cfg.
func IncludesStack(cfg Includeser) ([]string, error) {
clone := reflect.New(kit.NonPtrType(reflect.TypeOf(cfg))).Interface().(Includeser)
*clone.IncludesPtr() = *cfg.IncludesPtr()
return includesStackImpl(clone, nil)
}
// includeStackImpl implements IncludeStack, operating on cloned cfg
// todo: could use a more efficient method to just extract the include field..
func includesStackImpl(clone Includeser, includes []string) ([]string, error) {
incs := *clone.IncludesPtr()
ni := len(incs)
if ni == 0 {
return includes, nil
}
for i := ni - 1; i >= 0; i-- {
includes = append(includes, incs[i]) // reverse order so later overwrite earlier
}
var errs []error
for _, inc := range incs {
*clone.IncludesPtr() = nil
err := toml.OpenFromPaths(clone, inc, IncludePaths)
if err == nil {
includes, err = includesStackImpl(clone, includes)
if err != nil {
errs = append(errs, err)
}
} else {
errs = append(errs, err)
}
}
return includes, kit.AllErrors(errs, 10)
}
// IncludeStack returns the stack of include files in the natural
// order in which they are encountered (nil if none).
// Files should then be read in reverse order of the slice.
// Returns an error if any of the include files cannot be found on IncludePath.
// Does not alter cfg.
func IncludeStack(cfg Includer) ([]string, error) {
clone := reflect.New(kit.NonPtrType(reflect.TypeOf(cfg))).Interface().(Includer)
*clone.IncludePtr() = *cfg.IncludePtr()
return includeStackImpl(clone, nil)
}
// includeStackImpl implements IncludeStack, operating on cloned cfg
// todo: could use a more efficient method to just extract the include field..
func includeStackImpl(clone Includer, includes []string) ([]string, error) {
inc := *clone.IncludePtr()
if inc == "" {
return includes, nil
}
includes = append(includes, inc)
var errs []error
*clone.IncludePtr() = ""
err := toml.OpenFromPaths(clone, inc, IncludePaths)
if err == nil {
includes, err = includeStackImpl(clone, includes)
if err != nil {
errs = append(errs, err)
}
} else {
errs = append(errs, err)
}
return includes, kit.AllErrors(errs, 10)
}