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
|
package config
import (
"fmt"
"go.nc0.fr/svgu/pkg/config/lib/bzr"
"go.nc0.fr/svgu/pkg/config/lib/fossil"
"go.nc0.fr/svgu/pkg/config/lib/git"
"go.nc0.fr/svgu/pkg/config/lib/hg"
"go.nc0.fr/svgu/pkg/config/lib/svn"
"go.nc0.fr/svgu/pkg/types"
"go.starlark.net/starlark"
"strings"
)
// https://stackoverflow.com/questions/1976007/what-characters-are-forbidden-in-windows-and-linux-directory-names
const invalidName string = "..\\/<>:\"|?* \t\n\r\b\findex"
var registered types.Index
// ExecConfig configures the Starlark environment and executes the given
// configuration file "fl".
// The function returns a list of registered modules, or an error if something
// went wrong.
func ExecConfig(fl string) (*types.Index, error) {
th := &starlark.Thread{
Name: "exec " + fl,
Load: load,
}
// TODO(nc0): add built-ins
env := starlark.StringDict{
"index": starlark.NewBuiltin("index", InternIndex),
"module": starlark.NewBuiltin("module", InternModule),
}
registered = types.Index{}
if _, err := starlark.ExecFile(th, fl, nil, env); err != nil {
return &types.Index{}, err
}
return ®istered, nil
}
// load loads a module from the given path.
func load(t *starlark.Thread, module string) (starlark.StringDict, error) {
switch module {
case "git.star": // git
return git.LoadGitModule(t)
case "hg.star": // mercurial
return hg.LoadHgModule(t)
case "svn.star": // subversion
return svn.LoadSvnModule(t)
case "fossil.star": // fossil
return fossil.LoadFossilModule(t)
case "bzr.star": // bazaar
return bzr.LoadBzrModule(t)
default:
return nil, fmt.Errorf("unknown module %q", module)
}
}
// Injected built-ins.
// InternIndex represents the built-in function "index".
// index(domain) initializes a new index with the given domain.
func InternIndex(_ *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple,
kwargs []starlark.Tuple) (starlark.Value, error) {
var domain string
if err := starlark.UnpackArgs("index", args, kwargs,
"domain", &domain); err != nil {
return nil, err
}
registered.SetDomain(domain)
return starlark.None, nil
}
// InternModule represents the built-in function "module".
// module(name, vcs, repo, dir, file) registers a new module into the
// index.
func InternModule(_ *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple,
kwargs []starlark.Tuple) (starlark.Value, error) {
var name, vcs, repo, dir, file string
if err := starlark.UnpackArgs("module", args, kwargs, "name",
&name, "vcs", &vcs, "repo", &repo, "dir", &dir, "file", &file); err != nil {
return nil, err
}
if registered.Domain == "" {
return nil, fmt.Errorf("index not initialized")
}
if name == "" {
return nil, fmt.Errorf("module name cannot be empty")
}
if vcs == "" {
return nil, fmt.Errorf("module %q vcs cannot be empty", name)
}
if repo == "" {
return nil, fmt.Errorf("module %q repo cannot be empty", name)
}
// Check for name conditions.
if strings.Contains(invalidName, name) {
return nil, fmt.Errorf("module %q name is invalid", name)
}
if registered.CheckModule(name) {
return nil, fmt.Errorf("module %q already exists", name)
}
var v types.Vcs
switch vcs {
case "git":
v = types.VcsGit
case "hg":
v = types.VcsMercurial
case "svn":
v = types.VcsSubversion
case "fossil":
v = types.VcsFossil
case "bzr":
v = types.VcsBazaar
default:
return nil, fmt.Errorf("unknown vcs %q", vcs)
}
registered.AddModule(name, types.Module{
Path: name,
Vcs: v,
Repo: repo,
Dir: dir,
File: file,
})
return starlark.None, nil
}
|