1package tui
  2
  3import (
  4	"bytes"
  5	"fmt"
  6	"text/template"
  7
  8	tea "github.com/charmbracelet/bubbletea"
  9	"github.com/charmbracelet/lipgloss"
 10	gitypes "github.com/charmbracelet/soft-serve/internal/tui/bubbles/git/types"
 11	"github.com/charmbracelet/soft-serve/internal/tui/bubbles/repo"
 12	"github.com/charmbracelet/soft-serve/internal/tui/bubbles/selection"
 13	gm "github.com/charmbracelet/wish/git"
 14)
 15
 16type errMsg struct{ err error }
 17
 18func (e errMsg) Error() string {
 19	return e.err.Error()
 20}
 21
 22func (b *Bubble) setupCmd() tea.Msg {
 23	if b.config == nil || b.config.Source == nil {
 24		return errMsg{err: fmt.Errorf("config not set")}
 25	}
 26	mes, err := b.menuEntriesFromSource()
 27	if err != nil {
 28		return errMsg{err}
 29	}
 30	if len(mes) == 0 {
 31		return errMsg{fmt.Errorf("no repos found")}
 32	}
 33	b.repoMenu = mes
 34	rs := make([]string, 0)
 35	for _, m := range mes {
 36		rs = append(rs, m.Name)
 37	}
 38	b.repoSelect = selection.NewBubble(rs, b.styles)
 39	b.boxes[0] = b.repoSelect
 40
 41	// Jump to an initial repo
 42	ir := -1
 43	if b.initialRepo != "" {
 44		for i, me := range b.repoMenu {
 45			if me.Repo == b.initialRepo {
 46				ir = i
 47			}
 48		}
 49	}
 50	if ir == -1 {
 51		b.boxes[1] = b.repoMenu[0].bubble
 52		b.activeBox = 0
 53	} else {
 54		b.boxes[1] = b.repoMenu[ir].bubble
 55		b.repoSelect.SelectedItem = ir
 56		b.activeBox = 1
 57	}
 58
 59	b.state = loadedState
 60	return nil
 61}
 62
 63func (b *Bubble) menuEntriesFromSource() ([]MenuEntry, error) {
 64	mes := make([]MenuEntry, 0)
 65	for _, cr := range b.config.Repos {
 66		acc := b.config.AuthRepo(cr.Repo, b.session.PublicKey())
 67		if acc == gm.NoAccess && cr.Repo != "config" {
 68			continue
 69		}
 70		me, err := b.newMenuEntry(cr.Name, cr.Repo)
 71		if err != nil {
 72			return nil, err
 73		}
 74		mes = append(mes, me)
 75	}
 76	for _, r := range b.config.Source.AllRepos() {
 77		var found bool
 78		for _, me := range mes {
 79			if me.Repo == r.Name {
 80				found = true
 81			}
 82		}
 83		if !found {
 84			acc := b.config.AuthRepo(r.Name, b.session.PublicKey())
 85			if acc == gm.NoAccess {
 86				continue
 87			}
 88			me, err := b.newMenuEntry(r.Name, r.Name)
 89			if err != nil {
 90				return nil, err
 91			}
 92			mes = append(mes, me)
 93		}
 94	}
 95	return mes, nil
 96}
 97
 98func (b *Bubble) newMenuEntry(name string, rn string) (MenuEntry, error) {
 99	me := MenuEntry{Name: name, Repo: rn}
100	r, err := b.config.Source.GetRepo(rn)
101	if err != nil {
102		return me, err
103	}
104	if rn == "config" {
105		md, err := templatize(r.Readme, b.config)
106		if err != nil {
107			return me, err
108		}
109		r.Readme = md
110	}
111	boxLeftWidth := b.styles.Menu.GetWidth() + b.styles.Menu.GetHorizontalFrameSize()
112	// TODO: also send this along with a tea.WindowSizeMsg
113	var heightMargin = lipgloss.Height(b.headerView()) +
114		lipgloss.Height(b.footerView()) +
115		b.styles.RepoBody.GetVerticalFrameSize() +
116		b.styles.App.GetVerticalMargins()
117	rb := repo.NewBubble(rn, b.config.Host, b.config.Port, r, b.styles, b.width, boxLeftWidth, b.height, heightMargin)
118	initCmd := rb.Init()
119	msg := initCmd()
120	switch msg := msg.(type) {
121	case gitypes.ErrMsg:
122		return me, fmt.Errorf("missing %s: %s", me.Repo, msg.Err.Error())
123	}
124	me.bubble = rb
125	return me, nil
126}
127
128func templatize(mdt string, tmpl interface{}) (string, error) {
129	t, err := template.New("readme").Parse(mdt)
130	if err != nil {
131		return "", err
132	}
133	buf := &bytes.Buffer{}
134	err = t.Execute(buf, tmpl)
135	if err != nil {
136		return "", err
137	}
138	return buf.String(), nil
139}