github.com/emcfarlane/larking@v0.0.0-20220605172417-1704b45ee6c3/starlib/starlarkruntimevar/runtimevar.go (about)

     1  // Copyright 2021 Edward McFarlane. 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  
     5  // Package runtimevar adds configuration variables at runtime.
     6  package starlarkruntimevar
     7  
     8  import (
     9  	"fmt"
    10  	"sort"
    11  
    12  	"github.com/emcfarlane/larking/starlib/starext"
    13  	"github.com/emcfarlane/larking/starlib/starlarkthread"
    14  	starlarktime "go.starlark.net/lib/time"
    15  	"go.starlark.net/starlark"
    16  	"go.starlark.net/starlarkstruct"
    17  	"gocloud.dev/runtimevar"
    18  )
    19  
    20  func NewModule() *starlarkstruct.Module {
    21  	return &starlarkstruct.Module{
    22  		Name: "runtimevar",
    23  		Members: starlark.StringDict{
    24  			"open": starext.MakeBuiltin("runtimevar.open", Open),
    25  		},
    26  	}
    27  }
    28  
    29  func Open(thread *starlark.Thread, fnname string, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
    30  	var name string
    31  	if err := starlark.UnpackPositionalArgs(fnname, args, kwargs, 1, &name); err != nil {
    32  		return nil, err
    33  	}
    34  
    35  	ctx := starlarkthread.GetContext(thread)
    36  
    37  	variable, err := runtimevar.OpenVariable(ctx, name)
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  
    42  	v := &Variable{
    43  		name:     name,
    44  		variable: variable,
    45  	}
    46  	if err := starlarkthread.AddResource(thread, v); err != nil {
    47  		variable.Close() //nolint
    48  		return nil, err
    49  	}
    50  	return v, nil
    51  }
    52  
    53  type Variable struct {
    54  	name     string
    55  	variable *runtimevar.Variable
    56  }
    57  
    58  func (v *Variable) String() string { return fmt.Sprintf("<variable %q>", v.name) }
    59  func (v *Variable) Type() string   { return "runtimevar.variable" }
    60  func (v *Variable) Freeze()        {} // immutable?
    61  
    62  func (v *Variable) Truth() starlark.Bool {
    63  	// TODO: checkhealth?
    64  	//if err := v.variable.CheckHealth(); err != nil {
    65  	//	return starlark.Bool(false)
    66  	//}
    67  	return starlark.Bool(true)
    68  }
    69  func (v *Variable) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable type: %s", v.Type()) }
    70  func (v *Variable) Close() error {
    71  	return v.variable.Close()
    72  }
    73  
    74  type variableAttr func(v *Variable) starlark.Value
    75  
    76  var variableAttrs = map[string]variableAttr{
    77  	"latest": func(v *Variable) starlark.Value { return starext.MakeMethod(v, "latest", v.latest) },
    78  	"close":  func(v *Variable) starlark.Value { return starext.MakeMethod(v, "close", v.close) }, // TODO: expose me?
    79  }
    80  
    81  func (v *Variable) Attr(name string) (starlark.Value, error) {
    82  	if a := variableAttrs[name]; a != nil {
    83  		return a(v), nil
    84  	}
    85  	return nil, nil
    86  }
    87  func (v *Variable) AttrNames() []string {
    88  	names := make([]string, 0, len(variableAttrs))
    89  	for name := range variableAttrs {
    90  		names = append(names, name)
    91  	}
    92  	sort.Strings(names)
    93  	return names
    94  }
    95  
    96  func (v *Variable) latest(thread *starlark.Thread, fnname string, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
    97  	if err := starlark.UnpackPositionalArgs(fnname, args, kwargs, 0); err != nil {
    98  		return nil, err
    99  	}
   100  
   101  	ctx := starlarkthread.GetContext(thread)
   102  	snapshot, err := v.variable.Latest(ctx)
   103  	if err != nil {
   104  		return nil, err
   105  	}
   106  	return Snapshot(snapshot), nil
   107  }
   108  
   109  func (v *Variable) close(_ *starlark.Thread, fnname string, _ starlark.Tuple, _ []starlark.Tuple) (starlark.Value, error) {
   110  	if err := v.variable.Close(); err != nil {
   111  		return nil, err
   112  	}
   113  	return starlark.None, nil
   114  }
   115  
   116  type Snapshot runtimevar.Snapshot
   117  
   118  func (v Snapshot) String() string        { return fmt.Sprintf("<snapshot %v>", v.Value) }
   119  func (v Snapshot) Type() string          { return "runtimevar.snapshot" }
   120  func (v Snapshot) Freeze()               {} // immutable?
   121  func (v Snapshot) Truth() starlark.Bool  { return v.Value != nil }
   122  func (v Snapshot) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable type: %s", v.Type()) }
   123  func (v Snapshot) AttrNames() []string   { return []string{"value", "update_time"} }
   124  func (v Snapshot) Attr(name string) (starlark.Value, error) {
   125  	switch name {
   126  	case "value":
   127  		switch x := v.Value.(type) {
   128  		case string:
   129  			return starlark.String(x), nil
   130  		case []byte:
   131  			return starlark.Bytes(string(x)), nil
   132  		case map[string]interface{}:
   133  			// TODO: json map
   134  			return nil, fmt.Errorf("TODO: file issue for jsonmap support")
   135  		default:
   136  			return nil, fmt.Errorf("unhandled runtimevar type: %T", x)
   137  		}
   138  
   139  	case "update_time":
   140  		return starlarktime.Time(v.UpdateTime), nil
   141  	default:
   142  		return nil, nil
   143  	}
   144  
   145  }