wails-release/internal/runner/runner.go

102 lines
2.1 KiB
Go
Raw Normal View History

package runner
import (
"bytes"
"context"
"fmt"
"os/exec"
)
// Spec describes a single command to run.
type Spec struct {
Name string
Args []string
Dir string
Env []string
Stdin []byte
}
// Runner runs external commands. Production code uses Real; tests use Fake.
type Runner interface {
Run(ctx context.Context, s Spec) ([]byte, error)
}
// Real executes via os/exec and returns combined stdout+stderr.
type Real struct{}
func (Real) Run(ctx context.Context, s Spec) ([]byte, error) {
cmd := exec.CommandContext(ctx, s.Name, s.Args...)
if s.Dir != "" {
cmd.Dir = s.Dir
}
if len(s.Env) > 0 {
cmd.Env = s.Env
}
if len(s.Stdin) > 0 {
cmd.Stdin = bytes.NewReader(s.Stdin)
}
out, err := cmd.CombinedOutput()
if err != nil {
return out, fmt.Errorf("%s %v: %w (output: %s)", s.Name, s.Args, err, string(out))
}
return out, nil
}
// Call is a recorded invocation made against a Fake.
type Call struct {
Name string
Args []string
Dir string
}
type fakeResult struct {
out []byte
err error
}
// Fake records calls and returns configured results.
type Fake struct {
Calls []Call
results map[string]fakeResult
}
// On configures a result for the given name + args. nil args matches any.
func (f *Fake) On(name string, args []string) *FakeBuilder {
return &FakeBuilder{f: f, name: name, args: args}
}
type FakeBuilder struct {
f *Fake
name string
args []string
}
func (b *FakeBuilder) Return(out []byte, err error) {
if b.f.results == nil {
b.f.results = map[string]fakeResult{}
}
b.f.results[fakeKey(b.name, b.args)] = fakeResult{out: out, err: err}
}
func fakeKey(name string, args []string) string {
if args == nil {
return name + "\x00*"
}
k := name
for _, a := range args {
k += "\x00" + a
}
return k
}
func (f *Fake) Run(ctx context.Context, s Spec) ([]byte, error) {
f.Calls = append(f.Calls, Call{Name: s.Name, Args: append([]string(nil), s.Args...), Dir: s.Dir})
if r, ok := f.results[fakeKey(s.Name, s.Args)]; ok {
return r.out, r.err
}
if r, ok := f.results[fakeKey(s.Name, nil)]; ok {
return r.out, r.err
}
return nil, fmt.Errorf("fake runner: no result configured for %s %v", s.Name, s.Args)
}