feat
- command handlers
This commit is contained in:
1 parent
3673d318a1
commit
213562956c
6 files changed
+249
-1
No files matched your search
@@ -2,4 +2,21 @@ module spellbook
|
|||||||
|
|
||||||
go 1.26.0
|
go 1.26.0
|
||||||
|
|
||||||
require github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
require (
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
|
github.com/clipperhouse/displaywidth v0.10.0 // indirect
|
||||||
|
github.com/clipperhouse/uax29/v2 v2.6.0 // indirect
|
||||||
|
github.com/fatih/color v1.18.0 // indirect
|
||||||
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/mattn/go-runewidth v0.0.19 // indirect
|
||||||
|
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
|
||||||
|
github.com/olekukonko/errors v1.2.0 // indirect
|
||||||
|
github.com/olekukonko/ll v0.1.6 // indirect
|
||||||
|
github.com/olekukonko/tablewriter v1.1.4 // indirect
|
||||||
|
github.com/spf13/cobra v1.10.2 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.9 // indirect
|
||||||
|
golang.org/x/sys v0.30.0 // indirect
|
||||||
|
)
|
||||||
@@ -1,2 +1,37 @@
|
|||||||
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/clipperhouse/displaywidth v0.10.0 h1:GhBG8WuerxjFQQYeuZAeVTuyxuX+UraiZGD4HJQ3Y8g=
|
||||||
|
github.com/clipperhouse/displaywidth v0.10.0/go.mod h1:XqJajYsaiEwkxOj4bowCTMcT1SgvHo9flfF3jQasdbs=
|
||||||
|
github.com/clipperhouse/uax29/v2 v2.6.0 h1:z0cDbUV+aPASdFb2/ndFnS9ts/WNXgTNNGFoKXuhpos=
|
||||||
|
github.com/clipperhouse/uax29/v2 v2.6.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||||
|
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||||
|
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
|
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||||
|
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
|
||||||
|
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||||
|
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc=
|
||||||
|
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0=
|
||||||
|
github.com/olekukonko/errors v1.2.0 h1:10Zcn4GeV59t/EGqJc8fUjtFT/FuUh5bTMzZ1XwmCRo=
|
||||||
|
github.com/olekukonko/errors v1.2.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
|
||||||
|
github.com/olekukonko/ll v0.1.6 h1:lGVTHO+Qc4Qm+fce/2h2m5y9LvqaW+DCN7xW9hsU3uA=
|
||||||
|
github.com/olekukonko/ll v0.1.6/go.mod h1:NVUmjBb/aCtUpjKk75BhWrOlARz3dqsM+OtszpY4o88=
|
||||||
|
github.com/olekukonko/tablewriter v1.1.4 h1:ORUMI3dXbMnRlRggJX3+q7OzQFDdvgbN9nVWj1drm6I=
|
||||||
|
github.com/olekukonko/tablewriter v1.1.4/go.mod h1:+kedxuyTtgoZLwif3P1Em4hARJs+mVnzKxmsCL/C5RY=
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||||
|
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||||
|
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
|
||||||
|
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||||
|
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"spellbook/internal/models"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewAddCommand(app Application) *cobra.Command {
|
||||||
|
spell := models.Spell{}
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "add [flags] -- [spell]",
|
||||||
|
Short: "Add new spell",
|
||||||
|
Long: `WIP`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
ctx := cmd.Context()
|
||||||
|
|
||||||
|
spell.Spell = strings.Join(args, " ")
|
||||||
|
return app.AddSpell(ctx, spell)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Flags().StringVarP(&spell.Name, "name", "n", "", "Spell name")
|
||||||
|
cmd.Flags().StringSliceVarP(&spell.Tags, "tags", "t", nil, "Spell tags")
|
||||||
|
cmd.Flags().StringVarP(&spell.ShortDesc, "desc", "d", "", "Short spell description")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import "github.com/spf13/cobra"
|
||||||
|
|
||||||
|
func NewRmCommand(app Application) *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "rm [spell_name]",
|
||||||
|
Short: "Delete spell",
|
||||||
|
Long: "WIP",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
ctx := cmd.Context()
|
||||||
|
|
||||||
|
return app.DeleteSpell(ctx, args[0])
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/olekukonko/tablewriter"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"os"
|
||||||
|
"spellbook/internal/models"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Application interface {
|
||||||
|
AddSpell(ctx context.Context, spell models.Spell) error
|
||||||
|
GetSpell(ctx context.Context, spellName string) (models.Spell, error)
|
||||||
|
GetSpells(ctx context.Context, query models.SearchQuery) ([]models.Spell, error)
|
||||||
|
DeleteSpell(ctx context.Context, spellName string) error
|
||||||
|
CastSpell(ctx context.Context, spellCast models.SpellCast) (models.CastResult, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func rootCmd(app Application) func(*cobra.Command, []string) error {
|
||||||
|
return func(cmd *cobra.Command, args []string) error {
|
||||||
|
ctx := cmd.Context()
|
||||||
|
|
||||||
|
if len(args) < 1 {
|
||||||
|
return listCommands(ctx, cmd, app)
|
||||||
|
}
|
||||||
|
|
||||||
|
return runCommand(ctx, app, args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runCommand(ctx context.Context, app Application, args []string) error {
|
||||||
|
spell, err := app.GetSpell(ctx, args[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
m := make(map[string]string, len(args[1:]))
|
||||||
|
for _, pair := range args[1:] {
|
||||||
|
keyVal := strings.Split(pair, "=")
|
||||||
|
m[keyVal[0]] = keyVal[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = app.CastSpell(ctx, models.SpellCast{
|
||||||
|
Spell: spell,
|
||||||
|
Args: m,
|
||||||
|
Stdin: os.Stdin,
|
||||||
|
Stdout: os.Stdout,
|
||||||
|
Stderr: os.Stderr,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func listCommands(ctx context.Context, cmd *cobra.Command, app Application) error {
|
||||||
|
spells, err := app.GetSpells(ctx, models.SearchQuery{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(spells) == 0 {
|
||||||
|
cmd.Println("Spell book is empty. Try `spellbook add` to add your first spell")
|
||||||
|
}
|
||||||
|
|
||||||
|
return printTable(cmd, spells)
|
||||||
|
}
|
||||||
|
|
||||||
|
func printTable(cmd *cobra.Command, spells []models.Spell) error {
|
||||||
|
table := tablewriter.NewTable(cmd.OutOrStdout())
|
||||||
|
|
||||||
|
rows := make([][]string, 0, len(spells))
|
||||||
|
for _, spell := range spells {
|
||||||
|
row := []string{
|
||||||
|
spell.Name, spell.Spell, spell.ShortDesc, strings.Join(spell.Tags, ", "),
|
||||||
|
}
|
||||||
|
rows = append(rows, row)
|
||||||
|
}
|
||||||
|
|
||||||
|
table.Header("Name", "Spell", "Short description", "Tags")
|
||||||
|
_ = table.Bulk(rows)
|
||||||
|
if err := table.Render(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRootCmd(app Application) *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "spellbook",
|
||||||
|
Short: "CLI tool to store, manage and execute command templates",
|
||||||
|
Long: `Spellbook is a lightweight binary command manager written in Go.
|
||||||
|
It allows you to save complex shell commands as parametrized templates (spells)
|
||||||
|
and execute them instantly with custom arguments from anywhere in your terminal`,
|
||||||
|
RunE: rootCmd(app),
|
||||||
|
Args: cobra.ArbitraryArgs,
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package mock
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"spellbook/internal/adapters/caster"
|
||||||
|
"spellbook/internal/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
var testSpell = models.Spell{
|
||||||
|
Id: 0,
|
||||||
|
Name: "test",
|
||||||
|
Spell: "curl {{.target}}",
|
||||||
|
ShortDesc: "test spell",
|
||||||
|
Tags: []string{"tag1", "tag2", "tag3"},
|
||||||
|
}
|
||||||
|
|
||||||
|
type Application struct{}
|
||||||
|
|
||||||
|
func (a *Application) AddSpell(_ context.Context, spell models.Spell) error {
|
||||||
|
fmt.Println("TEST - AddSpell", spell)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Application) GetSpell(_ context.Context, spellName string) (models.Spell, error) {
|
||||||
|
fmt.Println("TEST - GetSpell", spellName)
|
||||||
|
return testSpell, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Application) GetSpells(_ context.Context, query models.SearchQuery) ([]models.Spell, error) {
|
||||||
|
fmt.Println("TEST - GetSpells", query)
|
||||||
|
|
||||||
|
return []models.Spell{
|
||||||
|
testSpell,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Application) DeleteSpell(_ context.Context, spellName string) error {
|
||||||
|
fmt.Println("TEST - DeleteSpell", spellName)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Application) CastSpell(ctx context.Context, spellCast models.SpellCast) (models.CastResult, error) {
|
||||||
|
fmt.Println("TEST - CastSpell")
|
||||||
|
return (&caster.SpellCaster{}).Cast(ctx, spellCast)
|
||||||
|
}
|
||||||
Reference in new issue
Block a user