You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

136 lines
2.8 KiB

1 year ago
package scpt
import (
"errors"
"fmt"
"github.com/alecthomas/participle"
"github.com/gen2brain/dlgs"
"io"
"os"
"os/exec"
"reflect"
"strings"
)
var Vars = map[string]interface{}{}
type FuncMap map[string]func(map[string]interface{}) (interface{}, error)
var Funcs = FuncMap{
"displaydialog": displayDialog,
"doshellscript": doShellScript,
}
func AddFuncs(fnMap FuncMap) {
for name, fn := range fnMap {
Funcs[name] = fn
}
}
func AddVars(varMap map[string]interface{}) {
for name, val := range varMap {
Vars[name] = val
}
}
func Parse(r io.Reader) (*AST, error) {
parser, err := participle.Build(&AST{})
if err != nil {
return nil, err
}
ast := &AST{}
err = parser.Parse("", r, ast)
if err != nil {
return nil, err
}
return ast, nil
}
func Execute(ast AST) {
for _, cmd := range ast.Commands {
if cmd.Vars != nil {
for _, Var := range cmd.Vars {
val := ParseValue(Var.Value)
if strings.Contains(reflect.TypeOf(val).String(), ".FuncCall") {
Call := val.(*FuncCall)
Vars[Var.Key], _ = CallFunction(Call)
} else {
Vars[Var.Key] = val
}
}
} else if cmd.Calls != nil {
for _, Call := range cmd.Calls {
_, _ = CallFunction(Call)
}
}
}
}
func ParseValue(val *Value) interface{} {
if val.String != nil {
return strings.Trim(*val.String, `"`)
} else if val.Bool != nil {
return *val.Bool
} else if val.Float != nil {
return *val.Float
} else if val.Integer != nil {
return *val.Integer
} else if val.SubCmd != nil {
return val.SubCmd
} else if val.VarVal != nil {
return Vars[*val.VarVal]
}
return nil
}
func UnwrapArgs(args []*Arg) map[string]interface{} {
argMap := map[string]interface{}{}
for _, arg := range args {
argMap[arg.Key] = ParseValue(arg.Value)
}
return argMap
}
func CallFunction(call *FuncCall) (interface{}, error) {
argMap := UnwrapArgs(call.Args)
return Funcs[call.Name](argMap)
}
func displayDialog(args map[string]interface{}) (interface{}, error) {
title, ok := args["title"]
if !ok {
return nil, errors.New("title not provided")
}
text, ok := args["text"]
if !ok {
return nil, errors.New("text not provided")
}
switch args["type"] {
case "yesno":
return dlgs.Question(fmt.Sprint(title), fmt.Sprint(text), true)
case "info":
return dlgs.Info(fmt.Sprint(title), fmt.Sprint(text))
case "error":
return dlgs.Error(fmt.Sprint(title), fmt.Sprint(text))
case "entry":
defaultText, ok := args["default"]
if !ok {
defaultText = ""
}
input, _, err := dlgs.Entry(fmt.Sprint(title), fmt.Sprint(text), fmt.Sprint(defaultText))
return input, err
}
return nil, nil
}
func doShellScript(args map[string]interface{}) (interface{}, error) {
script, ok := args["content"].(string)
if ok {
cmd := exec.Command("sh", "-c", script)
cmd.Stdout = os.Stdout
_ = cmd.Run()
return "", nil
} else {
return nil, errors.New("script not provided")
}
}