27
"github.com/mitchellh/mapstructure"
28
"plemya-x.ru/alr/internal/overrides"
29
"plemya-x.ru/alr/pkg/distro"
30
"golang.org/x/exp/slices"
31
"mvdan.cc/sh/v3/expand"
32
"mvdan.cc/sh/v3/interp"
33
"mvdan.cc/sh/v3/syntax"
36
var ErrNotPointerToStruct = errors.New("val must be a pointer to a struct")
38
type VarNotFoundError struct {
42
func (nfe VarNotFoundError) Error() string {
43
return "required variable '" + nfe.name + "' could not be found"
46
type InvalidTypeError struct {
52
func (ite InvalidTypeError) Error() string {
53
return "variable '" + ite.name + "' is of type " + ite.vartype + ", but " + ite.exptype + " is expected"
58
info *distro.OSRelease
67
func New(info *distro.OSRelease, runner *interp.Runner) *Decoder {
68
return &Decoder{info, runner, true, len(info.Like) > 0}
73
func (d *Decoder) DecodeVar(name string, val any) error {
74
variable := d.getVar(name)
76
return VarNotFoundError{name}
79
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
80
WeaklyTypedInput: true,
81
DecodeHook: mapstructure.DecodeHookFuncValue(func(from, to reflect.Value) (interface{}, error) {
82
if strings.Contains(to.Type().String(), "db.JSON") {
83
valType := to.FieldByName("Val").Type()
84
if !from.Type().AssignableTo(valType) {
85
return nil, InvalidTypeError{name, from.Type().String(), valType.String()}
88
to.FieldByName("Val").Set(from)
91
return from.Interface(), nil
100
switch variable.Kind {
102
return dec.Decode(variable.List)
103
case expand.Associative:
104
return dec.Decode(variable.Map)
106
return dec.Decode(variable.Str)
112
func (d *Decoder) DecodeVars(val any) error {
113
valKind := reflect.TypeOf(val).Kind()
114
if valKind != reflect.Pointer {
115
return ErrNotPointerToStruct
117
elemKind := reflect.TypeOf(val).Elem().Kind()
118
if elemKind != reflect.Struct {
119
return ErrNotPointerToStruct
123
rVal := reflect.ValueOf(val).Elem()
125
for i := 0; i < rVal.NumField(); i++ {
126
field := rVal.Field(i)
127
fieldType := rVal.Type().Field(i)
129
if !fieldType.IsExported() {
133
name := fieldType.Name
134
tag := fieldType.Tag.Get("sh")
137
if strings.Contains(tag, ",") {
138
splitTag := strings.Split(tag, ",")
141
if len(splitTag) > 1 {
142
if slices.Contains(splitTag, "required") {
151
newVal := reflect.New(field.Type())
152
err := d.DecodeVar(name, newVal.Interface())
153
if _, ok := err.(VarNotFoundError); ok && !required {
155
} else if err != nil {
159
field.Set(newVal.Elem())
165
type ScriptFunc func(ctx context.Context, opts ...interp.RunnerOption) error
169
func (d *Decoder) GetFunc(name string) (ScriptFunc, bool) {
170
fn := d.getFunc(name)
175
return func(ctx context.Context, opts ...interp.RunnerOption) error {
176
sub := d.Runner.Subshell()
177
for _, opt := range opts {
180
return sub.Run(ctx, fn)
184
func (d *Decoder) getFunc(name string) *syntax.Stmt {
185
names, err := overrides.Resolve(d.info, overrides.DefaultOpts.WithName(name))
190
for _, fnName := range names {
191
fn, ok := d.Runner.Funcs[fnName]
201
func (d *Decoder) getVar(name string) *expand.Variable {
202
names, err := overrides.Resolve(d.info, overrides.DefaultOpts.WithName(name))
207
for _, varName := range names {
208
val, ok := d.Runner.Vars[varName]
211
_, resolved := val.Resolve(expand.FuncEnviron(func(s string) string {
212
if val, ok := d.Runner.Vars[s]; ok {