1package commands
  2
  3import (
  4	"fmt"
  5	"io"
  6	"os"
  7
  8	"github.com/spf13/cobra"
  9
 10	"github.com/MichaelMure/git-bug/bug"
 11	"github.com/MichaelMure/git-bug/cache"
 12	"github.com/MichaelMure/git-bug/identity"
 13	"github.com/MichaelMure/git-bug/repository"
 14	"github.com/MichaelMure/git-bug/util/interrupt"
 15)
 16
 17// Env is the environment of a command
 18type Env struct {
 19	repo    repository.ClockedRepo
 20	backend *cache.RepoCache
 21	out     out
 22	err     out
 23}
 24
 25func newEnv() *Env {
 26	return &Env{
 27		repo: nil,
 28		out:  out{Writer: os.Stdout},
 29		err:  out{Writer: os.Stderr},
 30	}
 31}
 32
 33type out struct {
 34	io.Writer
 35}
 36
 37func (o out) Printf(format string, a ...interface{}) {
 38	_, _ = fmt.Fprintf(o, format, a...)
 39}
 40
 41func (o out) Print(a ...interface{}) {
 42	_, _ = fmt.Fprint(o, a...)
 43}
 44
 45func (o out) Println(a ...interface{}) {
 46	_, _ = fmt.Fprintln(o, a...)
 47}
 48
 49// loadRepo is a pre-run function that load the repository for use in a command
 50func loadRepo(env *Env) func(*cobra.Command, []string) error {
 51	return func(cmd *cobra.Command, args []string) error {
 52		cwd, err := os.Getwd()
 53		if err != nil {
 54			return fmt.Errorf("unable to get the current working directory: %q", err)
 55		}
 56
 57		env.repo, err = repository.NewGoGitRepo(cwd, []repository.ClockLoader{bug.ClockLoader})
 58		if err == repository.ErrNotARepo {
 59			return fmt.Errorf("%s must be run from within a git repo", rootCommandName)
 60		}
 61
 62		if err != nil {
 63			return err
 64		}
 65
 66		return nil
 67	}
 68}
 69
 70// loadRepoEnsureUser is the same as loadRepo, but also ensure that the user has configured
 71// an identity. Use this pre-run function when an error after using the configured user won't
 72// do.
 73func loadRepoEnsureUser(env *Env) func(*cobra.Command, []string) error {
 74	return func(cmd *cobra.Command, args []string) error {
 75		err := loadRepo(env)(cmd, args)
 76		if err != nil {
 77			return err
 78		}
 79
 80		_, err = identity.GetUserIdentity(env.repo)
 81		if err != nil {
 82			return err
 83		}
 84
 85		return nil
 86	}
 87}
 88
 89// loadBackend is a pre-run function that load the repository and the backend for use in a command
 90// When using this function you also need to use closeBackend as a post-run
 91func loadBackend(env *Env) func(*cobra.Command, []string) error {
 92	return func(cmd *cobra.Command, args []string) error {
 93		err := loadRepo(env)(cmd, args)
 94		if err != nil {
 95			return err
 96		}
 97
 98		env.backend, err = cache.NewRepoCache(env.repo)
 99		if err != nil {
100			return err
101		}
102
103		cleaner := func(env *Env) interrupt.CleanerFunc {
104			return func() error {
105				if env.backend != nil {
106					err := env.backend.Close()
107					env.backend = nil
108					return err
109				}
110				return nil
111			}
112		}
113
114		// Cleanup properly on interrupt
115		interrupt.RegisterCleaner(cleaner(env))
116		return nil
117	}
118}
119
120// loadBackendEnsureUser is the same as loadBackend, but also ensure that the user has configured
121// an identity. Use this pre-run function when an error after using the configured user won't
122// do.
123func loadBackendEnsureUser(env *Env) func(*cobra.Command, []string) error {
124	return func(cmd *cobra.Command, args []string) error {
125		err := loadBackend(env)(cmd, args)
126		if err != nil {
127			return err
128		}
129
130		_, err = identity.GetUserIdentity(env.repo)
131		if err != nil {
132			return err
133		}
134
135		return nil
136	}
137}
138
139// closeBackend is a post-run function that will close the backend properly
140// if it has been opened.
141func closeBackend(env *Env) func(*cobra.Command, []string) error {
142	return func(cmd *cobra.Command, args []string) error {
143		if env.backend == nil {
144			return nil
145		}
146		err := env.backend.Close()
147		env.backend = nil
148		return err
149	}
150}