1package config
  2
  3import (
  4	"log"
  5	"os"
  6	"path/filepath"
  7	"strings"
  8
  9	"github.com/charmbracelet/soft-serve/git"
 10	"github.com/charmbracelet/soft-serve/proto"
 11	"github.com/charmbracelet/soft-serve/server/db/types"
 12)
 13
 14var _ proto.Provider = &Config{}
 15var _ proto.MetadataProvider = &Config{}
 16
 17// Metadata returns the repository's metadata.
 18func (c *Config) Metadata(name string) (proto.Metadata, error) {
 19	i, err := c.db.GetRepo(name)
 20	if err != nil {
 21		return nil, err
 22	}
 23	return &repo{
 24		cfg:  c,
 25		info: i,
 26	}, nil
 27}
 28
 29// Open opens a repository.
 30func (c *Config) Open(name string) (proto.Repository, error) {
 31	if name == "" {
 32		return nil, os.ErrNotExist
 33	}
 34	r, err := git.Open(filepath.Join(c.RepoPath(), name+".git"))
 35	if err != nil {
 36		log.Printf("error opening repository %q: %v", name, err)
 37		return nil, err
 38	}
 39	return &repo{
 40		cfg:  c,
 41		repo: r,
 42	}, nil
 43}
 44
 45// ListRepos lists all repositories metadata.
 46func (c *Config) ListRepos() ([]proto.Metadata, error) {
 47	md := make([]proto.Metadata, 0)
 48	ds, err := os.ReadDir(c.RepoPath())
 49	if err != nil {
 50		return nil, err
 51	}
 52	for _, d := range ds {
 53		name := strings.TrimSuffix(d.Name(), ".git")
 54		r, err := c.db.GetRepo(name)
 55		if err != nil || r == nil {
 56			md = append(md, &emptyMetadata{
 57				name: name,
 58				cfg:  c,
 59			})
 60		} else {
 61			md = append(md, &repo{
 62				cfg:  c,
 63				info: r,
 64			})
 65		}
 66	}
 67	return md, nil
 68}
 69
 70var _ proto.Metadata = emptyMetadata{}
 71
 72type emptyMetadata struct {
 73	name string
 74	cfg  *Config
 75}
 76
 77// Collabs implements proto.Metadata.
 78func (emptyMetadata) Collabs() []proto.User {
 79	return []proto.User{}
 80}
 81
 82// Description implements proto.Metadata.
 83func (emptyMetadata) Description() string {
 84	return ""
 85}
 86
 87// IsPrivate implements proto.Metadata.
 88func (emptyMetadata) IsPrivate() bool {
 89	return false
 90}
 91
 92// Name implements proto.Metadata.
 93func (e emptyMetadata) Name() string {
 94	return e.name
 95}
 96
 97// Open implements proto.Metadata.
 98func (e emptyMetadata) Open() (proto.Repository, error) {
 99	return e.cfg.Open(e.Name())
100}
101
102// ProjectName implements proto.Metadata.
103func (emptyMetadata) ProjectName() string {
104	return ""
105}
106
107var _ proto.Metadata = &repo{}
108var _ proto.Repository = &repo{}
109
110// repo represents a Git repository.
111type repo struct {
112	cfg  *Config
113	repo *git.Repository
114	info *types.Repo
115}
116
117// Open opens the underlying Repository.
118func (r *repo) Open() (proto.Repository, error) {
119	return r.cfg.Open(r.Name())
120}
121
122// Name returns the name of the repository.
123func (r *repo) Name() string {
124	if r.repo != nil {
125		strings.TrimSuffix(filepath.Base(r.repo.Path), ".git")
126	}
127	return r.info.Name
128}
129
130// ProjectName returns the repository's project name.
131func (r *repo) ProjectName() string {
132	return r.info.ProjectName
133}
134
135// Description returns the repository's description.
136func (r *repo) Description() string {
137	return r.info.Description
138}
139
140// IsPrivate returns true if the repository is private.
141func (r *repo) IsPrivate() bool {
142	return r.info.Private
143}
144
145// Collabs returns the repository's collaborators.
146func (r *repo) Collabs() []proto.User {
147	collabs := make([]proto.User, 0)
148	cs, err := r.cfg.db.ListRepoCollabs(r.Name())
149	if err != nil {
150		return collabs
151	}
152	for i, c := range cs {
153		ks, err := r.cfg.db.GetUserPublicKeys(c)
154		if err != nil {
155			log.Printf("error getting public keys for %q: %v", c.Name, err)
156			continue
157		}
158		u := &user{
159			user: c,
160			keys: ks,
161		}
162		collabs[i] = u
163	}
164	return collabs
165}
166
167// Repository returns the underlying git.Repository.
168func (r *repo) Repository() *git.Repository {
169	return r.repo
170}