chore: cli refactor

This commit is contained in:
abelith committed 2026-08-21 16:30:49 +05:00
1 parent 181642172a
commit 99264ca438
8 files changed
+27 -50

No files matched your search

-59
View File
@@ -1,59 +0,0 @@
package cli
import (
"fmt"
"github.com/abelith/spellbook/internal/errors"
"github.com/abelith/spellbook/internal/models"
"github.com/spf13/cobra"
"strings"
)
var reservedCommands = map[string]struct{}{
"add": {},
"rm": {},
"help": {},
"completion": {},
"search": {},
"export": {},
"import": {},
}
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()
if len(args) < 1 {
return errors.ErrInsufficientArgs
}
if isReservedCommand(args[0]) {
return fmt.Errorf("%w: %s", errors.ErrReservedCmd, args[0])
}
spell.Spell = strings.Join(args, " ")
if spell.Name == "" {
return fmt.Errorf("%w: --name is missing", errors.ErrInsufficientArgs)
}
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
}
func isReservedCommand(cmd string) bool {
_, reserved := reservedCommands[strings.ToLower(cmd)]
return reserved
}
-41
View File
@@ -1,41 +0,0 @@
package cli
import (
"context"
"github.com/abelith/spellbook/internal/models"
"github.com/spf13/cobra"
"io"
)
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)
ExportSpells(ctx context.Context, exp string, w io.Writer) error
ImportSpells(ctx context.Context, exp string, r io.Reader) error
}
type CLI struct {
root *cobra.Command
}
func NewCLI(ver string, app Application) *CLI {
root := NewRootCmd(ver, app)
root.AddCommand(
NewAddCommand(app),
NewRmCommand(app),
NewSearchCmd(app),
NewExportCmd(app),
NewImportCmd(app),
)
return &CLI{
root: root,
}
}
func (c *CLI) Run(_ context.Context) error {
return c.root.Execute()
}
-52
View File
@@ -1,52 +0,0 @@
package cli
import (
"bufio"
"errors"
_ "github.com/abelith/spellbook/internal/adapters/json"
"github.com/spf13/cobra"
"io"
"os"
)
func NewExportCmd(app Application) *cobra.Command {
var format string
var out string
cmd := &cobra.Command{
Use: "export",
Short: "Export spellbook",
Long: `WIP`,
RunE: func(cmd *cobra.Command, args []string) (err error) {
ctx := cmd.Context()
var w io.Writer = os.Stdout
if out != "" {
f, err := os.OpenFile(out, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer func() {
if innerErr := f.Close(); innerErr != nil {
err = errors.Join(err, innerErr)
}
}()
w = f
}
buf := bufio.NewWriter(w)
defer func() {
if innerErr := buf.Flush(); innerErr != nil {
err = errors.Join(err, innerErr)
}
}()
return app.ExportSpells(ctx, format, buf)
},
}
cmd.Flags().StringVarP(&format, "format", "f", "jsonl", "Output format")
cmd.Flags().StringVarP(&out, "out", "o", "", "Output")
return cmd
}
-46
View File
@@ -1,46 +0,0 @@
package cli
import (
"bufio"
"errors"
_ "github.com/abelith/spellbook/internal/adapters/json"
"github.com/spf13/cobra"
"io"
"os"
)
func NewImportCmd(app Application) *cobra.Command {
var format string
var in string
cmd := &cobra.Command{
Use: "import",
Short: "Import spellbook",
Long: `WIP`,
RunE: func(cmd *cobra.Command, args []string) (err error) {
ctx := cmd.Context()
var r io.Reader = os.Stdin
if in != "" {
f, err := os.Open(in)
if err != nil {
return err
}
defer func() {
if innerErr := f.Close(); innerErr != nil {
err = errors.Join(err, innerErr)
}
}()
r = f
}
buf := bufio.NewReader(r)
return app.ImportSpells(ctx, format, buf)
},
}
cmd.Flags().StringVarP(&format, "format", "f", "jsonl", "Input format")
cmd.Flags().StringVarP(&in, "input", "i", "", "Input")
return cmd
}
-26
View File
@@ -1,26 +0,0 @@
package cli
import (
"fmt"
"github.com/abelith/spellbook/internal/errors"
"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()
if len(args) < 1 {
return fmt.Errorf("%w: %s", errors.ErrInsufficientArgs, "spell_name")
}
return app.DeleteSpell(ctx, args[0])
},
}
return cmd
}
-97
View File
@@ -1,97 +0,0 @@
package cli
import (
"context"
"github.com/abelith/spellbook/internal/models"
"github.com/olekukonko/tablewriter"
"github.com/spf13/cobra"
"os"
"strings"
)
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 nil
}
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(ver string, 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,
Version: ver,
}
return cmd
}
-47
View File
@@ -1,47 +0,0 @@
package cli
import (
"fmt"
"github.com/abelith/spellbook/internal/errors"
"github.com/abelith/spellbook/internal/models"
"github.com/spf13/cobra"
)
func NewSearchCmd(app Application) *cobra.Command {
query := models.SearchQuery{}
cmd := &cobra.Command{
Use: "search [query]",
Short: "Search for spells",
Long: "WIP",
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
if query.IsEmpty() {
return errors.ErrEmptyQuery
}
if len(args) > 0 {
query.Text = args[0]
}
spells, err := app.GetSpells(ctx, query)
if err != nil {
return err
}
if len(spells) < 1 {
return fmt.Errorf("%w: spells", errors.ErrNotFound)
}
return printTable(cmd, spells)
},
}
cmd.Flags().StringVarP(&query.Name, "name", "n", "", "Spell name")
cmd.Flags().StringVarP(&query.Command, "cmd", "c", "", "Spell command template")
cmd.Flags().StringVarP(&query.Description, "desc", "d", "", "Spell description")
cmd.Flags().StringSliceVarP(&query.Tags, "tags", "t", nil, "Spell tags")
return cmd
}