summaryrefslogtreecommitdiff
path: root/pkg/config/starlark.go
blob: a954e92bf8a371b6b21da3c10221897c477a5791 (plain)
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 &registered, 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
}