diff --git a/go.mod b/go.mod index d809bac..7f196d8 100644 --- a/go.mod +++ b/go.mod @@ -1 +1,5 @@ module spellbook + +go 1.26.0 + +require github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..0be9157 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= diff --git a/internal/adapters/caster/caster.go b/internal/adapters/caster/caster.go new file mode 100644 index 0000000..03862e8 --- /dev/null +++ b/internal/adapters/caster/caster.go @@ -0,0 +1,62 @@ +package caster + +import ( + "bytes" + "context" + "errors" + "fmt" + "github.com/google/shlex" + "os/exec" + "spellbook/internal/models" + "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("failed to cast spell: %w", 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()) +} diff --git a/internal/models/models.go b/internal/models/models.go index 6eafe88..90560e3 100644 --- a/internal/models/models.go +++ b/internal/models/models.go @@ -14,12 +14,14 @@ type Spell struct { type SpellCast struct { Spell Spell Args map[string]string + Stdin io.Reader Stdout io.Writer Stderr io.Writer } type CastResult struct { ExitCode int + Err error Duration time.Duration Meta map[string]any }