-
Notifications
You must be signed in to change notification settings - Fork 3
/
TemplateStore.go
150 lines (127 loc) · 4.6 KB
/
TemplateStore.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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package main
import (
"fmt"
"sort"
)
// TemplateStore stores multiple ChronicleTemplates and provides means
// to retrieve them by name.
type TemplateStore struct {
templates map[string]*ChronicleTemplate // Store as ptrs so that it is easier to modify them do things like aliasing
}
// GetTemplateStore returns a template store that is already filled with all templates
// contained in the main template directory. If some error showed up during reading and
// parsing files, resolving dependencies etc, then nil is returned together with an error.
func GetTemplateStore() (ts *TemplateStore, err error) {
return getTemplateStoreForDir(GetTemplatesDir())
}
// getTemplateStoreForDir takes a directory and returns a template store
// for all entries in that directory, including its subdirectories
func getTemplateStoreForDir(dirName string) (ts *TemplateStore, err error) {
yFiles, err := GetTemplateFilesFromDir(dirName)
if err != nil {
return nil, err
}
ts = new(TemplateStore)
ts.templates = make(map[string]*ChronicleTemplate)
// put basic yaml files as ChronicleTemplates into the store
for yFilename, yFile := range yFiles {
ct, err := NewChronicleTemplate(yFilename, yFile)
if err != nil {
return nil, err
}
if otherEntry, exists := ts.templates[ct.ID()]; exists {
return nil, fmt.Errorf("Found multiple templates with ID '%v':\n- %v\n- %v", ct.ID(), otherEntry.Filename(), ct.Filename())
}
ts.templates[ct.ID()] = ct
}
// resolve inheritance between templates
resolvedIDs := make(map[string]bool, 0) // stores IDs of all entries that are already resolved
for _, entry := range ts.templates {
err := resolveInheritance(ts, entry, &resolvedIDs)
if err != nil {
return nil, err
}
}
// resolve presets and content
for _, templateID := range ts.GetTemplateIDs(false) {
// TODO test resolving above
template, _ := ts.GetTemplate(templateID)
if err = template.ResolvePresets(); err != nil {
return nil, err
}
if err = template.ResolveContent(); err != nil {
return nil, err
}
}
return ts, nil
}
// resolveInheritance is responsible for resolving template inheritance by copying entries
// from the content and the presets section to other templates.
func resolveInheritance(ts *TemplateStore, ct *ChronicleTemplate, resolvedIDs *map[string]bool, resolveChain ...string) (err error) {
// check if we have already seen that entry
if _, exists := (*resolvedIDs)[ct.ID()]; exists {
return nil
}
// check if we have a cyclic dependency
for idx, inheritedID := range resolveChain {
if inheritedID == ct.ID() {
resolveChain = append(resolveChain, inheritedID) // add entry before printing to have complete cycle in output
return fmt.Errorf("Error resolving dependencies of template '%v'. Inheritance chain is %v", ct.ID(), resolveChain[idx:])
}
}
// entries without inheritance information can simply be added to the list of resolved IDs
if ct.Inherit() == "" {
(*resolvedIDs)[ct.ID()] = true
return nil
}
inheritedID := ct.Inherit()
inheritedCe, err := ts.GetTemplate(inheritedID)
if err != nil {
return err
}
// add current id to inheritance list for recursive call
resolveChain = append(resolveChain, ct.ID())
err = resolveInheritance(ts, inheritedCe, resolvedIDs, resolveChain...)
if err != nil {
return err
}
// now resolve chronicle inheritance
err = ct.InheritFrom(inheritedCe)
if err != nil {
return err
}
// add to list of resolved entries
(*resolvedIDs)[ct.ID()] = true
return nil
}
// GetTemplateIDs returns a sorted list of keys contained in this TemplateStore
func (ts *TemplateStore) GetTemplateIDs(includeAliases bool) (keyList []string) {
keyList = make([]string, 0, len(ts.templates))
for key, entry := range ts.templates {
if includeAliases || key == entry.ID() {
keyList = append(keyList, key)
}
}
sort.Strings(keyList)
return keyList
}
// GetTemplate returns the template with the specified name from the TemplateStore, or
// an error if no template with that name exists
func (ts *TemplateStore) GetTemplate(templateID string) (ct *ChronicleTemplate, err error) {
ct, exists := ts.templates[templateID]
if !exists {
return nil, fmt.Errorf("Could not find template with ID '%v'", templateID)
}
return ct, nil
}
// GetTemplate returns the template with the specified name, or
// an error if no template with that name exists. This is merely a
// convenience wrapper to avoid the need to create a TemplateStore
// object just for receiving a single template.
func GetTemplate(templateID string) (ct *ChronicleTemplate, err error) {
ts, err := GetTemplateStore()
if err != nil {
return nil, err
}
return ts.GetTemplate(templateID)
}