feat: caster implementation switched to actual shell interpreter
This commit is contained in:
1 parent
c5465fe6f6
commit
a86959c52a
4 files changed
+95
-32
No files matched your search
@@ -7,8 +7,9 @@ import (
|
||||
"fmt"
|
||||
errors2 "github.com/abelith/spellbook/internal/errors"
|
||||
"github.com/abelith/spellbook/internal/models"
|
||||
"github.com/google/shlex"
|
||||
"os/exec"
|
||||
"io"
|
||||
"mvdan.cc/sh/v3/interp"
|
||||
"mvdan.cc/sh/v3/syntax"
|
||||
"text/template"
|
||||
"time"
|
||||
)
|
||||
@@ -16,18 +17,8 @@ import (
|
||||
type SpellCaster struct{}
|
||||
|
||||
func (sc *SpellCaster) Cast(ctx context.Context, cast models.SpellCast) (models.CastResult, error) {
|
||||
args, err := renderSpell(cast)
|
||||
if err != nil {
|
||||
return models.CastResult{}, err
|
||||
}
|
||||
|
||||
cmd := exec.CommandContext(ctx, args[0], args[1:]...)
|
||||
cmd.Stdin = cast.Stdin
|
||||
cmd.Stdout = cast.Stdout
|
||||
cmd.Stderr = cast.Stderr
|
||||
|
||||
start := time.Now()
|
||||
err = cmd.Run()
|
||||
err := runSpell(ctx, &cast)
|
||||
duration := time.Since(start)
|
||||
|
||||
res := models.CastResult{
|
||||
@@ -35,29 +26,47 @@ func (sc *SpellCaster) Cast(ctx context.Context, cast models.SpellCast) (models.
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
var exitErr *exec.ExitError
|
||||
var exitErr *interp.ExitStatus
|
||||
if errors.As(err, &exitErr) {
|
||||
res.ExitCode = exitErr.ExitCode()
|
||||
res.ExitCode = int(*exitErr)
|
||||
res.Err = err
|
||||
return res, nil
|
||||
}
|
||||
return res, fmt.Errorf("%w: %w", errors2.ErrFailedToCast, err)
|
||||
}
|
||||
|
||||
res.ExitCode = 0
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func renderSpell(cast models.SpellCast) ([]string, error) {
|
||||
func renderSpell(cast *models.SpellCast) (io.Reader, error) {
|
||||
tmpl, err := template.New(cast.Spell.Name).Option("missingkey=error").Parse(cast.Spell.Spell)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("%w: %w", errors2.ErrTemplateFailed, err)
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
if err := tmpl.Execute(buf, cast.Args); err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("%w: %w", errors2.ErrTemplateFailed, err)
|
||||
}
|
||||
|
||||
return shlex.Split(buf.String())
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func runSpell(ctx context.Context, cast *models.SpellCast) error {
|
||||
r, err := renderSpell(cast)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
node, err := syntax.NewParser().Parse(r, cast.Spell.Name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse spell: %w", err)
|
||||
}
|
||||
|
||||
runner, err := interp.New(interp.StdIO(cast.Stdin, cast.Stdout, cast.Stderr))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to run spell: %w", err)
|
||||
}
|
||||
|
||||
return runner.Run(ctx, node)
|
||||
}
|
||||
Reference in new issue
Block a user