all repos

viye @ 896d55427eac536be9d99d6c941215e4bf165ac1

my shot at reimplementing xiki

viye/internal/plumbing/shell/shell.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
add viye ls, 1 month ago
1
package shell
2
3
import (
4
	"context"
5
	"fmt"
6
	"os"
7
	"os/exec"
8
	"path/filepath"
9
	"strconv"
10
	"strings"
11
	"syscall"
12
	"time"
13
14
	"github.com/olexsmir/viye/internal/viye"
15
)
16
17
type Tool struct{}
18
19
func (Tool) Name() string { return "shell($, $$, ls, kill, mkdir)" }
20
func (Tool) Match(c *viye.Context) bool {
21
	return isCmd(c) || isBGCmd(c) || isLs(c) || isKill(c) || isMkdir(c)
22
}
23
24
func (Tool) Execute(c *viye.Context) (string, error) {
25
	switch {
26
	case isCmd(c):
27
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
28
		defer cancel()
29
30
		cmd := exec.CommandContext(ctx, "sh", "-c", strings.Join(c.Args, " "))
31
		cmd.Dir = c.Dir
32
		out, err := cmd.CombinedOutput()
33
		if err != nil {
34
			if ctx.Err() == context.DeadlineExceeded {
35
				return "", fmt.Errorf("timeout: command exceeded 5s")
36
			}
37
		}
38
		return viye.FormatOutput(string(out)), nil
39
40
	case isBGCmd(c):
41
		cmd := exec.Command("sh", "-c", strings.Join(c.Args, " "))
42
		cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
43
		cmd.Dir = c.Dir
44
		if err := cmd.Start(); err != nil {
45
			return "", err
46
		}
47
		return fmt.Sprintf("| pid: %d", cmd.Process.Pid), nil
48
49
	case isLs(c):
50
		entries, err := os.ReadDir(c.Dir)
51
		if err != nil {
52
			return "", err
53
		}
54
		var res []string
55
		for _, e := range entries {
56
			name := e.Name()
57
			if e.IsDir() {
58
				name += "/"
59
			}
60
			res = append(res, name)
61
		}
62
		return viye.FormatOutputList(res), nil
63
64
	case isKill(c):
65
		pid, err := strconv.Atoi(c.Args[0])
66
		if err != nil {
67
			return "", fmt.Errorf("kill: invalid pid %q", c.Args[0])
68
		}
69
		p, err := os.FindProcess(pid)
70
		if err != nil {
71
			return "", fmt.Errorf("kill: %w", err)
72
		}
73
		if err := p.Kill(); err != nil {
74
			return "", fmt.Errorf("kill: %w", err)
75
		}
76
		return "| done\n", nil
77
78
	case isMkdir(c):
79
		path := filepath.Join(append([]string{c.Dir}, c.Args...)...)
80
		if err := os.MkdirAll(path, 0o755); err != nil {
81
			return "", fmt.Errorf("mkdir: %w", err)
82
		}
83
		return "| created " + path + "\n", nil
84
85
	default:
86
		panic("shouldn't have paniced")
87
	}
88
}
89
90
func isCmd(c *viye.Context) bool   { return c.Cmd == "$" }
91
func isBGCmd(c *viye.Context) bool { return c.Cmd == "$$" }
92
func isLs(c *viye.Context) bool    { return c.Cmd == "ls" }
93
func isKill(c *viye.Context) bool  { return c.Cmd == "kill" && len(c.Args) == 1 }
94
func isMkdir(c *viye.Context) bool { return c.Cmd == "mkdir" && len(c.Args) >= 1 }