1// Copyright 2018 The Bazel Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package starlarkstruct_test
6
7import (
8	"fmt"
9	"path/filepath"
10	"testing"
11
12	"go.starlark.net/resolve"
13	"go.starlark.net/starlark"
14	"go.starlark.net/starlarkstruct"
15	"go.starlark.net/starlarktest"
16)
17
18func init() {
19	// The tests make extensive use of these not-yet-standard features.
20	resolve.AllowLambda = true
21	resolve.AllowNestedDef = true
22	resolve.AllowFloat = true
23	resolve.AllowSet = true
24}
25
26func Test(t *testing.T) {
27	testdata := starlarktest.DataFile("starlarkstruct", ".")
28	thread := &starlark.Thread{Load: load}
29	starlarktest.SetReporter(thread, t)
30	filename := filepath.Join(testdata, "testdata/struct.star")
31	predeclared := starlark.StringDict{
32		"struct": starlark.NewBuiltin("struct", starlarkstruct.Make),
33		"gensym": starlark.NewBuiltin("gensym", gensym),
34	}
35	if _, err := starlark.ExecFile(thread, filename, nil, predeclared); err != nil {
36		if err, ok := err.(*starlark.EvalError); ok {
37			t.Fatal(err.Backtrace())
38		}
39		t.Fatal(err)
40	}
41}
42
43// load implements the 'load' operation as used in the evaluator tests.
44func load(thread *starlark.Thread, module string) (starlark.StringDict, error) {
45	if module == "assert.star" {
46		return starlarktest.LoadAssertModule()
47	}
48	return nil, fmt.Errorf("load not implemented")
49}
50
51// gensym is a built-in function that generates a unique symbol.
52func gensym(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
53	var name string
54	if err := starlark.UnpackArgs("gensym", args, kwargs, "name", &name); err != nil {
55		return nil, err
56	}
57	return &symbol{name: name}, nil
58}
59
60// A symbol is a distinct value that acts as a constructor of "branded"
61// struct instances, like a class symbol in Python or a "provider" in Bazel.
62type symbol struct{ name string }
63
64var _ starlark.Callable = (*symbol)(nil)
65
66func (sym *symbol) Name() string          { return sym.name }
67func (sym *symbol) String() string        { return sym.name }
68func (sym *symbol) Type() string          { return "symbol" }
69func (sym *symbol) Freeze()               {} // immutable
70func (sym *symbol) Truth() starlark.Bool  { return starlark.True }
71func (sym *symbol) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable: %s", sym.Type()) }
72
73func (sym *symbol) CallInternal(thread *starlark.Thread, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
74	if len(args) > 0 {
75		return nil, fmt.Errorf("%s: unexpected positional arguments", sym)
76	}
77	return starlarkstruct.FromKeywords(sym, kwargs), nil
78}
79