Home

Awesome

Go Reference Build Status

Starlark to Go struct converter

The starstruct package implements conversion of Go structs to Starlark StringDict and from Starlark StringDict to a Go struct.

It uses the starlark struct tag key to indicate the name of the corresponding Starlark StringDict key for a field and optional conversion options, as is common in Go marshalers (such as JSON and XML).

Since Starlark is a useful language for configuration, and configuration can often be represented by a well-defined Go struct, the idea for this package is to make it easy to provide the default configuration (as a Go struct) to a Starlark program, and read back the final configuration after the Starlark program's execution, so that the Go code can use the Go configuration struct from there on.

The code documentation is the canonical source for documentation.

Installation

Note that starstruct requires Go 1.20+.

$ go get github.com/mna/starstruct

Example

From example_test.go:

func Example() {
	const script = `
def set_server(srv):
	srv["ports"].append(2020)

def set_admin(adm):
	adm["name"] = "root"
	adm["is_admin"] = True
	roles = adm["roles"]
	adm["roles"] = roles.union(["editor", "admin"])

set_server(server)
set_admin(user)
`

	type Server struct {
		Addr  string `starlark:"addr"`
		Ports []int  `starlark:"ports"`
	}
	type User struct {
		Name    string   `starlark:"name"`
		IsAdmin bool     `starlark:"is_admin"`
		Ignored int      `starlark:"-"`
		Roles   []string `starlark:"roles,asset"`
	}
	type S struct {
		Server Server `starlark:"server"`
		User   User   `starlark:"user"`
	}

	// initialize with default values for the starlark script
	s := S{
		Server: Server{Addr: "localhost", Ports: []int{80, 443}},
		User:   User{Name: "Martin", Roles: []string{"viewer"}, Ignored: 42},
	}
	initialVars := make(starlark.StringDict)
	if err := starstruct.ToStarlark(s, initialVars); err != nil {
		log.Fatal(err)
	}

	// execute the starlark script (it doesn't create any new variables, but if
	// it did, we would capture them in outputVars and merge all global vars
	// together before calling FromStarlark).
	var th starlark.Thread
	outputVars, err := starlark.ExecFile(&th, "example", script, initialVars)
	if err != nil {
		log.Fatal(err)
	}
	allVars := mergeStringDicts(nil, initialVars, outputVars)

	if err := starstruct.FromStarlark(allVars, &s); err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%v", s)

	// Output:
	// {{localhost [80 443 2020]} {root true 42 [viewer editor admin]}}
}

License

The BSD 3-Clause license.