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 }