-
Notifications
You must be signed in to change notification settings - Fork 0
/
filesystem.go
97 lines (81 loc) · 2.31 KB
/
filesystem.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
package vmt
import (
keyvalues "github.com/galaco/KeyValues"
"io"
"strings"
)
const (
materialRootDir = "materials/"
vmtExtension = ".vmt"
)
type virtualFilesystem interface {
GetFile(string) (io.Reader, error)
}
// FromFilesystem loads a material from filesystem
// Its important to note this is the ONLY way to automatically
// resolve `import` properties present in a vmt
func FromFilesystem(filePath string, fs virtualFilesystem, definition Material) (Material, error) {
// ensure proper file path
validatedPath := sanitizeFilePath(filePath)
kvs, err := readVmtFromFilesystem(validatedPath, fs)
if err != nil {
return nil, err
}
return FromKeyValues(kvs, definition)
}
func sanitizeFilePath(filePath string) string {
if !strings.HasPrefix(filePath, materialRootDir) {
filePath = materialRootDir + filePath
}
if !strings.HasSuffix(filePath, vmtExtension) {
filePath += vmtExtension
}
return filePath
}
func readVmtFromFilesystem(path string, fs virtualFilesystem) (*keyvalues.KeyValue, error) {
root, err := readKeyValuesFromFilesystem(path, fs)
if err != nil {
return nil, err
}
include, err := root.Find("include")
if include != nil && err == nil {
includePath, _ := include.AsString()
root, err = mergeIncludedVmtRecursive(root, includePath, fs)
if err != nil {
return nil, err
}
}
return root, nil
}
func mergeIncludedVmtRecursive(base *keyvalues.KeyValue, includePath string, fs virtualFilesystem) (*keyvalues.KeyValue, error) {
parent, err := readKeyValuesFromFilesystem(includePath, fs)
if err != nil {
return base, err
}
result, err := base.Patch(parent)
if err != nil {
return base, err
}
include, err := result.Find("include")
if err == nil {
newIncludePath, _ := include.AsString()
if newIncludePath == includePath {
err = result.RemoveChild("include")
return &result, err
}
return mergeIncludedVmtRecursive(&result, newIncludePath, fs)
}
return &result, nil
}
// ReadKeyValues loads a keyvalues file.
// Its just a simple wrapper that combines the KeyValues library and
// the filesystem module.
func readKeyValuesFromFilesystem(filePath string, fs virtualFilesystem) (*keyvalues.KeyValue, error) {
stream, err := fs.GetFile(filePath)
if err != nil {
return nil, err
}
reader := keyvalues.NewReader(stream)
kvs, err := reader.Read()
return &kvs, err
}