diff --git a/internal/server/scriptlet/marshal/marshal.go b/shared/scriptlet/marshal.go similarity index 94% rename from internal/server/scriptlet/marshal/marshal.go rename to shared/scriptlet/marshal.go index dffaf7e96..5acbce64a 100644 --- a/internal/server/scriptlet/marshal/marshal.go +++ b/shared/scriptlet/marshal.go @@ -1,4 +1,4 @@ -package marshal +package scriptlet import ( "fmt" @@ -16,25 +16,31 @@ type starlarkObject struct { typeName string } +// Type is a starlark object type. func (s *starlarkObject) Type() string { return s.typeName } +// String is a starlark object string. func (s *starlarkObject) String() string { return s.d.String() } +// Freeze freezes the starlark object. func (s *starlarkObject) Freeze() { } +// Hash returns a hash of the starlark object. func (s *starlarkObject) Hash() (uint32, error) { return 0, fmt.Errorf("Unhashable type %s", s.Type()) } +// Truth returns whether the starlark object is true. func (s *starlarkObject) Truth() starlark.Bool { return starlark.True } +// AttrNames returns the attribute names of the starlark object. func (s *starlarkObject) AttrNames() []string { keys := s.d.Keys() keyNames := make([]string, 0, len(keys)) @@ -45,6 +51,7 @@ func (s *starlarkObject) AttrNames() []string { return keyNames } +// Attr gets an attribute of the starlark object. func (s *starlarkObject) Attr(name string) (starlark.Value, error) { field, found, err := s.d.Get(starlark.String(name)) if err != nil { diff --git a/internal/server/scriptlet/marshal/marshal_test.go b/shared/scriptlet/marshal_test.go similarity index 99% rename from internal/server/scriptlet/marshal/marshal_test.go rename to shared/scriptlet/marshal_test.go index 9a46ea428..87d284298 100644 --- a/internal/server/scriptlet/marshal/marshal_test.go +++ b/shared/scriptlet/marshal_test.go @@ -1,4 +1,4 @@ -package marshal +package scriptlet import ( "fmt" diff --git a/internal/server/scriptlet/load/utils.go b/shared/scriptlet/utils.go similarity index 74% rename from internal/server/scriptlet/load/utils.go rename to shared/scriptlet/utils.go index 59e05732f..fe9378e99 100644 --- a/internal/server/scriptlet/load/utils.go +++ b/shared/scriptlet/utils.go @@ -1,4 +1,4 @@ -package load +package scriptlet import ( "errors" @@ -6,11 +6,18 @@ import ( "slices" "sort" "strings" + "sync" "go.starlark.net/starlark" "go.starlark.net/syntax" ) +// Loader holds the programs for the scriptlet. +type Loader struct { + programsMu sync.Mutex + programs map[string]*starlark.Program +} + // argMismatch represents mismatching arguments in a function. type argMismatch struct { gotten []string @@ -23,11 +30,19 @@ type scriptletFunction struct { optional bool } -// declaration is a type alias to make scriptlet declaration easier. -type declaration = map[scriptletFunction][]string +// Declaration is a type alias to make scriptlet declaration easier. +type Declaration = map[scriptletFunction][]string -// compile compiles a scriptlet. -func compile(programName string, src string, preDeclared []string) (*starlark.Program, error) { +// NewLoader creates a new Loader. +func NewLoader() *Loader { + return &Loader{ + programsMu: sync.Mutex{}, + programs: map[string]*starlark.Program{}, + } +} + +// Compile compiles a scriptlet. +func Compile(programName string, src string, preDeclared []string) (*starlark.Program, error) { isPreDeclared := func(name string) bool { return slices.Contains(preDeclared, name) } @@ -45,13 +60,13 @@ func compile(programName string, src string, preDeclared []string) (*starlark.Pr return mod, nil } -// required is a convenience wrapper declaring a required function. -func required(name string) scriptletFunction { +// Required is a convenience wrapper declaring a required function. +func Required(name string) scriptletFunction { return scriptletFunction{name: name, optional: false} } -// required is a convenience wrapper declaring an optional function. -func optional(name string) scriptletFunction { +// Optional is a convenience wrapper declaring an optional function. +func Optional(name string) scriptletFunction { return scriptletFunction{name: name, optional: true} } @@ -105,8 +120,8 @@ func validateFunction(funv starlark.Value, requiredArgs []string) (bool, bool, * return false, false, nil } -// validate validates a scriptlet by compiling it and checking the presence of required and optional functions. -func validate(compiler func(string, string) (*starlark.Program, error), programName string, src string, scriptletFunctions declaration) error { +// Validate validates a scriptlet by compiling it and checking the presence of required and optional functions. +func Validate(compiler func(string, string) (*starlark.Program, error), programName string, src string, scriptletFunctions Declaration) error { // Try to compile the program. prog, err := compiler(programName, src) if err != nil { @@ -152,11 +167,13 @@ func validate(compiler func(string, string) (*starlark.Program, error), programN // String builder to format pretty error messages. appendToError := func(text string) { var link string - if sentences == 0 { + + switch sentences { + case 0: link = "" - } else if sentences == 1 { + case 1: link = "; additionally, " - } else { + default: link = "; finally, " } @@ -194,31 +211,31 @@ func validate(compiler func(string, string) (*starlark.Program, error), programN return errors.New(errorText) } -// set compiles a scriptlet into memory. If empty src is provided the current program is deleted. -func set(compiler func(string, string) (*starlark.Program, error), programName string, src string) error { +// Set compiles a scriptlet into memory. If empty src is provided the current program is deleted. +func (l *Loader) Set(compiler func(string, string) (*starlark.Program, error), programName string, src string) error { if src == "" { - programsMu.Lock() - delete(programs, programName) - programsMu.Unlock() + l.programsMu.Lock() + delete(l.programs, programName) + l.programsMu.Unlock() } else { prog, err := compiler(programName, src) if err != nil { return err } - programsMu.Lock() - programs[programName] = prog - programsMu.Unlock() + l.programsMu.Lock() + l.programs[programName] = prog + l.programsMu.Unlock() } return nil } -// program returns a precompiled scriptlet program. -func program(name string, programName string) (*starlark.Program, *starlark.Thread, error) { - programsMu.Lock() - prog, found := programs[programName] - programsMu.Unlock() +// Program returns a precompiled scriptlet program. +func (l *Loader) Program(name string, programName string) (*starlark.Program, *starlark.Thread, error) { + l.programsMu.Lock() + prog, found := l.programs[programName] + l.programsMu.Unlock() if !found { return nil, nil, fmt.Errorf("%s scriptlet not loaded", name) }