package caster import ( "bytes" "context" "errors" "fmt" errors2 "github.com/abelith/spellbook/internal/errors" "github.com/abelith/spellbook/internal/models" "github.com/google/shlex" "os/exec" "text/template" "time" ) 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() duration := time.Since(start) res := models.CastResult{ Duration: duration, } if err != nil { var exitErr *exec.ExitError if errors.As(err, &exitErr) { res.ExitCode = exitErr.ExitCode() 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) { tmpl, err := template.New(cast.Spell.Name).Option("missingkey=error").Parse(cast.Spell.Spell) if err != nil { return nil, err } buf := bytes.NewBuffer(nil) if err := tmpl.Execute(buf, cast.Args); err != nil { return nil, err } return shlex.Split(buf.String()) }