mirror of https://git.jolheiser.com/ugit.git
169 lines
3.1 KiB
Go
169 lines
3.1 KiB
Go
|
package tui
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"os"
|
||
|
"sort"
|
||
|
"strings"
|
||
|
|
||
|
tea "github.com/charmbracelet/bubbletea"
|
||
|
"github.com/charmbracelet/huh"
|
||
|
"github.com/charmbracelet/ssh"
|
||
|
"go.jolheiser.com/ugit/internal/git"
|
||
|
)
|
||
|
|
||
|
type tui struct {
|
||
|
s ssh.Session
|
||
|
repoDir string
|
||
|
note *string
|
||
|
repo *git.Repo
|
||
|
nextCmd func() error
|
||
|
}
|
||
|
|
||
|
func New(s ssh.Session, repoDir string) error {
|
||
|
t := tui{
|
||
|
s: s,
|
||
|
repoDir: repoDir,
|
||
|
}
|
||
|
t.nextCmd = t.repoList
|
||
|
return t.run()
|
||
|
}
|
||
|
|
||
|
func (t *tui) run() error {
|
||
|
for {
|
||
|
if t.nextCmd == nil {
|
||
|
break
|
||
|
}
|
||
|
if err := t.nextCmd(); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (t *tui) repoList() error {
|
||
|
repoPaths, err := os.ReadDir(t.repoDir)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
repos := make([]*git.Repo, 0, len(repoPaths))
|
||
|
for _, repoName := range repoPaths {
|
||
|
if !strings.HasSuffix(repoName.Name(), ".git") {
|
||
|
continue
|
||
|
}
|
||
|
repo, err := git.NewRepo(t.repoDir, repoName.Name())
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
repos = append(repos, repo)
|
||
|
}
|
||
|
sort.Slice(repos, func(i, j int) bool {
|
||
|
return repos[i].Name() < repos[j].Name()
|
||
|
})
|
||
|
|
||
|
opts := make([]huh.Option[*git.Repo], 0, len(repos))
|
||
|
for _, repo := range repos {
|
||
|
opts = append(opts, huh.NewOption(repo.Name(), repo))
|
||
|
}
|
||
|
|
||
|
if err := t.form(huh.NewSelect[*git.Repo]().
|
||
|
Title("Repo Selection").
|
||
|
Options(opts...).
|
||
|
Value(&t.repo),
|
||
|
).Run(); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
t.nextCmd = t.repoMenu
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (t *tui) repoMenu() error {
|
||
|
var sel string
|
||
|
if err := t.form(huh.NewSelect[string]().
|
||
|
Title(t.repo.Name()).
|
||
|
Options(
|
||
|
huh.NewOption("edit", "edit"),
|
||
|
huh.NewOption("delete", "delete"),
|
||
|
huh.NewOption("back", "back"),
|
||
|
).
|
||
|
Value(&sel),
|
||
|
).Run(); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
switch sel {
|
||
|
case "edit":
|
||
|
t.nextCmd = t.repoEdit
|
||
|
case "delete":
|
||
|
t.nextCmd = t.repoDelete
|
||
|
case "back":
|
||
|
t.nextCmd = t.repoList
|
||
|
default:
|
||
|
t.nextCmd = nil
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (t *tui) repoEdit() error {
|
||
|
form := git.RepoMeta{
|
||
|
Description: t.repo.Meta.Description,
|
||
|
Private: t.repo.Meta.Private,
|
||
|
}
|
||
|
if err := t.form(
|
||
|
huh.NewText().
|
||
|
Title("Description").
|
||
|
Value(&form.Description),
|
||
|
huh.NewConfirm().
|
||
|
Title("Visibility").
|
||
|
Affirmative("Private").
|
||
|
Negative("Public").
|
||
|
Value(&form.Private),
|
||
|
).Run(); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
t.repo.Meta = form
|
||
|
if err := t.repo.SaveMeta(); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
t.note = note(fmt.Sprintf("%q edited", t.repo.Name()))
|
||
|
t.nextCmd = t.repoList
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (t *tui) repoDelete() error {
|
||
|
var del bool
|
||
|
if err := t.form(huh.NewConfirm().
|
||
|
Title(fmt.Sprintf("Are you sure you want to delete %q?", t.repo.Name())).
|
||
|
Value(&del),
|
||
|
).Run(); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if del {
|
||
|
if err := os.RemoveAll(t.repo.Path()); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
t.note = note(fmt.Sprintf("%q deleted", t.repo.Name()))
|
||
|
}
|
||
|
t.nextCmd = t.repoList
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (t *tui) form(fields ...huh.Field) *huh.Form {
|
||
|
if t.note != nil {
|
||
|
fields = append([]huh.Field{huh.NewNote().Title(*t.note)}, fields...)
|
||
|
t.note = nil
|
||
|
}
|
||
|
return huh.NewForm(
|
||
|
huh.NewGroup(fields...),
|
||
|
).WithProgramOptions(tea.WithAltScreen()).WithInput(t.s).WithOutput(t.s).WithTheme(huh.ThemeCatppuccin())
|
||
|
}
|
||
|
|
||
|
func note(msg string) *string {
|
||
|
return &msg
|
||
|
}
|