blob: 916969851de68b7042938c178869724305233777 [file] [log] [blame]
// Copyright 2018 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package lucicfg
import (
"context"
"go.starlark.net/starlark"
"go.chromium.org/luci/common/data/stringset"
"go.chromium.org/luci/common/errors"
"go.chromium.org/luci/lucicfg/graph"
)
// State is mutated throughout execution of the script and at the end contains
// the final execution result.
//
// It is available in the implementation of native functions exposed to the
// Starlark side. Starlark code operates with the state exclusively through
// these functions.
type State struct {
Inputs Inputs // all inputs, exactly as passed to Generate.
Configs map[string]string // all generated config files, populated at the end
errors errors.MultiError // all errors emitted during the generation (if any)
seenErrs stringset.Set // set of all string backtraces in 'errors', for deduping
generators generators // callbacks that generate config files based on state
graph graph.Graph // the graph with config entities defined so far
}
// clear resets the state.
func (s *State) clear() {
*s = State{Inputs: s.Inputs}
}
// err adds errors to the list of errors and returns the list as MultiError,
// deduplicating errors with identical backtraces.
func (s *State) err(err ...error) error {
if s.seenErrs == nil {
s.seenErrs = stringset.New(len(err))
}
for _, e := range err {
if bt, _ := e.(BacktracableError); bt == nil || s.seenErrs.Add(bt.Backtrace()) {
s.errors = append(s.errors, e)
}
}
return s.errors
}
var stateCtxKey = "lucicfg.State"
// withState puts *State into the context, to be accessed by native functions.
func withState(ctx context.Context, s *State) context.Context {
return context.WithValue(ctx, &stateCtxKey, s)
}
// ctxState pulls out *State from the context, as put there by withState.
//
// Panics if not there.
func ctxState(ctx context.Context) *State {
return ctx.Value(&stateCtxKey).(*State)
}
func init() {
// graph() returns a graph with config entities defines thus far.
declNative("graph", func(call nativeCall) (starlark.Value, error) {
if err := call.unpack(0); err != nil {
return nil, err
}
return &call.State.graph, nil
})
// clear_state() wipes the state of the generator, for tests.
declNative("clear_state", func(call nativeCall) (starlark.Value, error) {
if err := call.unpack(0); err != nil {
return nil, err
}
call.State.clear()
return starlark.None, nil
})
}