chore: refactor plugin structure (#18335)
This commit is contained in:
parent
9127e1a913
commit
07fa1b64f8
127 changed files with 861 additions and 938 deletions
162
plugin/script.go
Normal file
162
plugin/script.go
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/evcc-io/evcc/plugin/pipeline"
|
||||
"github.com/evcc-io/evcc/util"
|
||||
"github.com/evcc-io/evcc/util/request"
|
||||
"github.com/kballard/go-shellquote"
|
||||
)
|
||||
|
||||
// Script implements shell script-based providers and setters
|
||||
type Script struct {
|
||||
*getter
|
||||
log *util.Logger
|
||||
script string
|
||||
timeout time.Duration
|
||||
cache time.Duration
|
||||
updated time.Time
|
||||
val string
|
||||
err error
|
||||
pipeline *pipeline.Pipeline
|
||||
}
|
||||
|
||||
func init() {
|
||||
registry.Add("script", NewScriptPluginFromConfig)
|
||||
}
|
||||
|
||||
// NewScriptPluginFromConfig creates a script plugin.
|
||||
func NewScriptPluginFromConfig(other map[string]interface{}) (Plugin, error) {
|
||||
cc := struct {
|
||||
Cmd string
|
||||
pipeline.Settings `mapstructure:",squash"`
|
||||
Scale float64
|
||||
Timeout time.Duration
|
||||
Cache time.Duration
|
||||
}{
|
||||
Timeout: request.Timeout,
|
||||
Scale: 1,
|
||||
}
|
||||
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p, err := NewScripPlugin(cc.Cmd, cc.Timeout, cc.Scale, cc.Cache)
|
||||
p.getter = defaultGetters(p, cc.Scale)
|
||||
|
||||
if err == nil {
|
||||
var pipe *pipeline.Pipeline
|
||||
pipe, err = pipeline.New(log, cc.Settings)
|
||||
p.pipeline = pipe
|
||||
}
|
||||
|
||||
return p, err
|
||||
}
|
||||
|
||||
// NewScriptProvider creates a script plugin.
|
||||
// Script execution is aborted after given timeout.
|
||||
func NewScripPlugin(script string, timeout time.Duration, scale float64, cache time.Duration) (*Script, error) {
|
||||
if strings.TrimSpace(script) == "" {
|
||||
return nil, errors.New("script is required")
|
||||
}
|
||||
|
||||
s := &Script{
|
||||
log: util.NewLogger("script"),
|
||||
script: script,
|
||||
timeout: timeout,
|
||||
cache: cache,
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (p *Script) exec(script string) (string, error) {
|
||||
args, err := shellquote.Split(script)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), p.timeout)
|
||||
defer cancel()
|
||||
|
||||
cmd := exec.CommandContext(ctx, args[0], args[1:]...)
|
||||
b, err := cmd.Output()
|
||||
|
||||
s := strings.TrimSpace(string(b))
|
||||
|
||||
if err != nil {
|
||||
// use STDOUT if available
|
||||
var ee *exec.ExitError
|
||||
if errors.As(err, &ee) {
|
||||
s = strings.TrimSpace(string(ee.Stderr))
|
||||
}
|
||||
|
||||
p.log.ERROR.Printf("%s: %s", strings.Join(args, " "), s)
|
||||
return "", err
|
||||
}
|
||||
|
||||
p.log.DEBUG.Printf("%s: %s", strings.Join(args, " "), s)
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
var _ Getters = (*Script)(nil)
|
||||
|
||||
// StringGetter returns string from exec result. Only STDOUT is considered.
|
||||
func (p *Script) StringGetter() (func() (string, error), error) {
|
||||
return func() (string, error) {
|
||||
if time.Since(p.updated) > p.cache {
|
||||
p.val, p.err = p.exec(p.script)
|
||||
p.updated = time.Now()
|
||||
|
||||
if p.err == nil && p.pipeline != nil {
|
||||
var b []byte
|
||||
b, p.err = p.pipeline.Process([]byte(p.val))
|
||||
p.val = string(b)
|
||||
}
|
||||
}
|
||||
|
||||
return p.val, p.err
|
||||
}, nil
|
||||
}
|
||||
|
||||
func scriptSetter[T any](p *Script, param string) (func(T) error, error) {
|
||||
return func(val T) error {
|
||||
cmd, err := util.ReplaceFormatted(p.script, map[string]interface{}{
|
||||
param: val,
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
_, err = p.exec(cmd)
|
||||
}
|
||||
|
||||
return err
|
||||
}, nil
|
||||
}
|
||||
|
||||
var _ IntSetter = (*Script)(nil)
|
||||
|
||||
// IntSetter invokes script with parameter replaced by int value
|
||||
func (p *Script) IntSetter(param string) (func(int64) error, error) {
|
||||
return scriptSetter[int64](p, param)
|
||||
}
|
||||
|
||||
var _ BoolSetter = (*Script)(nil)
|
||||
|
||||
// BoolSetter invokes script with parameter replaced by bool value
|
||||
func (p *Script) BoolSetter(param string) (func(bool) error, error) {
|
||||
return scriptSetter[bool](p, param)
|
||||
}
|
||||
|
||||
var _ StringSetter = (*Script)(nil)
|
||||
|
||||
// StringSetter returns a function that invokes a script with parameter by a string value
|
||||
func (p *Script) StringSetter(param string) (func(string) error, error) {
|
||||
return scriptSetter[string](p, param)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue