/
loader.go
140 lines (118 loc) · 3.5 KB
/
loader.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
// package loader loads a source file into a sysl Module
package loader
import (
"os"
"path/filepath"
"github.com/anz-bank/sysl/pkg/pbutil"
"github.com/anz-bank/sysl/pkg/syslutil"
"github.com/anz-bank/sysl/pkg/parse"
"github.com/anz-bank/sysl/pkg/sysl"
"github.com/sirupsen/logrus"
"github.com/spf13/afero"
)
type ProjectConfiguration struct {
Module, Root string
RootIsFound bool
Fs afero.Fs
}
func LoadSyslModule(root, filename string, fs afero.Fs, logger *logrus.Logger) (*sysl.Module, string, error) {
logger.Debugf("Attempting to load module:%s (root:%s)", filename, root)
projectConfig := NewProjectConfiguration()
if err := projectConfig.ConfigureProject(root, filename, fs, logger); err != nil {
return nil, "", err
}
modelParser := parse.NewParser()
if !projectConfig.RootIsFound {
modelParser.RestrictToLocalImport()
}
return parse.LoadAndGetDefaultApp(projectConfig.Module, projectConfig.Fs, modelParser)
}
// LoadSyslPb decodes a Sysl module from a protobuf message.
func LoadSyslModuleFromPb(pbPath string, fs afero.Fs) (*sysl.Module, error) {
return pbutil.FromPB(pbPath, fs)
}
func NewProjectConfiguration() *ProjectConfiguration {
return &ProjectConfiguration{
Root: "",
Module: "",
RootIsFound: false,
Fs: nil,
}
}
func (pc *ProjectConfiguration) ConfigureProject(root, module string, fs afero.Fs, logger *logrus.Logger) error {
rootIsDefined := root != ""
modulePath := module
if rootIsDefined {
modulePath = filepath.Join(root, module)
}
syslRootPath, err := FindRootFromSyslModule(modulePath, fs)
if err != nil {
return err
}
rootMarkerExists := syslRootPath != ""
switch {
case rootIsDefined:
pc.RootIsFound = true
pc.Root = root
pc.Module = module
case rootMarkerExists:
pc.Root = syslRootPath
// module has to be relative to the root
absModulePath, err := filepath.Abs(module)
if err != nil {
return err
}
pc.Module, err = filepath.Rel(pc.Root, absModulePath)
if err != nil {
return err
}
pc.RootIsFound = true
default:
// uses the module directory as the root, changing the module to be relative to the root
pc.Root = filepath.Dir(module)
pc.Module = filepath.Base(module)
pc.RootIsFound = false
}
logrus.Debugf("root is set to: %s\n", pc.Root)
pc.Fs = syslutil.NewChrootFs(fs, pc.Root)
return nil
}
func FindRootFromSyslModule(modulePath string, fs afero.Fs) (string, error) {
currentPath, err := filepath.Abs(modulePath)
if err != nil {
return "", err
}
systemRoot, err := filepath.Abs(string(os.PathSeparator))
if err != nil {
return "", err
}
// Keep walking up the directories to find nearest root marker
for {
currentPath = filepath.Dir(currentPath)
exists, err := afero.Exists(fs, filepath.Join(currentPath, parse.SyslRootMarker))
reachedRoot := currentPath == systemRoot || (err != nil && os.IsPermission(err))
switch {
case exists:
return currentPath, nil
case reachedRoot:
return "", nil
case err != nil:
return "", err
}
}
}
// EnsureSyslPb generates a binary protobuf message alongside a Sysl file if one does not already
// exist.
// TODO(ladeo): Remove once parsed modules can be easily passed into arr.ai.
func EnsureSyslPb(fs afero.Fs, root, syslPath string) error {
target := syslPath + ".pb"
_, err := os.Stat(target)
if err != nil && os.IsNotExist(err) {
module, _, err := LoadSyslModule(root, syslPath, fs, logrus.StandardLogger())
if err != nil {
return err
}
return pbutil.GeneratePBBinaryMessageFile(module, target, fs)
}
return nil
}