-
Notifications
You must be signed in to change notification settings - Fork 0
/
pathtmpl.go
115 lines (94 loc) · 3.11 KB
/
pathtmpl.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
// Copyright (C) 2017, 2018 Damon Revoe. All rights reserved.
// Use of this source code is governed by the MIT
// license, which can be found in the LICENSE file.
package main
import (
"fmt"
"path/filepath"
"strings"
)
type outputFileParams struct {
filename string
params templateParams
}
type pathnameTemplateMultiplier struct {
paramName string
paramValues []string
continuation pathnameTemplateText
}
type pathnameTemplateText struct {
text string
next *pathnameTemplateMultiplier
}
// subst updates the 'pathnameTemplateText' receiver by replacing all
// instances of 'name' surrounded by braces with 'value', which can be
// either a string or a slice of strings. In the latter case, the text
// in the receiver structure gets truncated by the substitution and the
// receiver structure gets extended by a new pathnameTemplateMultiplier
// structure. Subst returns the number of substitution values.
func (t *pathnameTemplateText) subst(name string, value interface{}) int {
if textValue, ok := value.(string); ok {
t.text = strings.Replace(t.text, "{"+name+"}",
textValue, -1)
} else if arrayValue, ok := value.([]string); !ok {
t.text = strings.Replace(t.text, "{"+name+"}",
fmt.Sprint(value), -1)
} else if pos := strings.Index(t.text, "{"+name+"}"); pos >= 0 {
t.next = &pathnameTemplateMultiplier{name, arrayValue,
pathnameTemplateText{t.text[pos+len(name)+2:], t.next}}
t.text = t.text[:pos]
return len(arrayValue)
}
return 1
}
// expandPathnameTemplate takes a pathname template and substitutes
// template parameter names with their values. Parameter values can be
// either strings or slices of strings. Each template value that is a
// slice of strings multiplies the number of output strings by the number
// of strings in the slice.
func expandPathnameTemplate(pathname string,
params templateParams) []outputFileParams {
root := pathnameTemplateText{pathname, nil}
resultSize := 1
for name, value := range params {
resultSize *= root.subst(name, value)
for n := root.next; n != nil; n = n.continuation.next {
resultSize *= n.continuation.subst(name, value)
}
}
result := make([]outputFileParams, resultSize)
if resultSize == 0 {
return result
}
for i := 0; i < resultSize; i++ {
result[i].filename = root.text
copyOfParams := templateParams{}
for name, value := range params {
copyOfParams[name] = value
}
result[i].params = copyOfParams
}
sliceSize := 1
for a := root.next; a != nil; a = a.continuation.next {
numberOfValues := len(a.paramValues)
continuationText := a.continuation.text
for i := 0; i < resultSize; {
for j := 0; j < numberOfValues; j++ {
value := a.paramValues[j]
filenameFragment := value + continuationText
for k := 0; k < sliceSize; k++ {
result[i].filename += filenameFragment
result[i].params[a.paramName] = value
i++
}
}
}
sliceSize *= numberOfValues
}
// Let the templates know their output file and directory names.
for _, outputFile := range result {
outputFile.params["filename"] = outputFile.filename
outputFile.params["dirname"] = filepath.Dir(outputFile.filename)
}
return result
}