loading.go

  1package execenv
  2
  3import (
  4	"fmt"
  5	"os"
  6
  7	"github.com/spf13/cobra"
  8	"github.com/vbauerster/mpb/v8"
  9	"github.com/vbauerster/mpb/v8/decor"
 10
 11	"github.com/MichaelMure/git-bug/cache"
 12	"github.com/MichaelMure/git-bug/entities/identity"
 13	"github.com/MichaelMure/git-bug/repository"
 14	"github.com/MichaelMure/git-bug/util/interrupt"
 15)
 16
 17// LoadRepo is a pre-run function that load the repository for use in a command
 18func LoadRepo(env *Env) func(*cobra.Command, []string) error {
 19	return func(cmd *cobra.Command, args []string) error {
 20		cwd, err := os.Getwd()
 21		if err != nil {
 22			return fmt.Errorf("unable to get the current working directory: %q", err)
 23		}
 24
 25		// Note: we are not loading clocks here because we assume that LoadRepo is only used
 26		//  when we don't manipulate entities, or as a child call of LoadBackend which will
 27		//  read all clocks anyway.
 28		env.Repo, err = repository.OpenGoGitRepo(cwd, gitBugNamespace, nil)
 29		if err == repository.ErrNotARepo {
 30			return fmt.Errorf("%s must be run from within a git Repo", RootCommandName)
 31		}
 32		if err != nil {
 33			return err
 34		}
 35
 36		return nil
 37	}
 38}
 39
 40// LoadRepoEnsureUser is the same as LoadRepo, but also ensure that the user has configured
 41// an identity. Use this pre-run function when an error after using the configured user won't
 42// do.
 43func LoadRepoEnsureUser(env *Env) func(*cobra.Command, []string) error {
 44	return func(cmd *cobra.Command, args []string) error {
 45		err := LoadRepo(env)(cmd, args)
 46		if err != nil {
 47			return err
 48		}
 49
 50		_, err = identity.GetUserIdentity(env.Repo)
 51		if err != nil {
 52			return err
 53		}
 54
 55		return nil
 56	}
 57}
 58
 59// LoadBackend is a pre-run function that load the repository and the Backend for use in a command
 60// When using this function you also need to use CloseBackend as a post-run
 61func LoadBackend(env *Env) func(*cobra.Command, []string) error {
 62	return func(cmd *cobra.Command, args []string) error {
 63		err := LoadRepo(env)(cmd, args)
 64		if err != nil {
 65			return err
 66		}
 67
 68		var events chan cache.BuildEvent
 69		env.Backend, events = cache.NewRepoCache(env.Repo)
 70
 71		err = CacheBuildProgressBar(env, events)
 72		if err != nil {
 73			return err
 74		}
 75
 76		cleaner := func(env *Env) interrupt.CleanerFunc {
 77			return func() error {
 78				if env.Backend != nil {
 79					err := env.Backend.Close()
 80					env.Backend = nil
 81					return err
 82				}
 83				return nil
 84			}
 85		}
 86
 87		// Cleanup properly on interrupt
 88		interrupt.RegisterCleaner(cleaner(env))
 89		return nil
 90	}
 91}
 92
 93// LoadBackendEnsureUser is the same as LoadBackend, but also ensure that the user has configured
 94// an identity. Use this pre-run function when an error after using the configured user won't
 95// do.
 96func LoadBackendEnsureUser(env *Env) func(*cobra.Command, []string) error {
 97	return func(cmd *cobra.Command, args []string) error {
 98		err := LoadBackend(env)(cmd, args)
 99		if err != nil {
100			return err
101		}
102
103		_, err = identity.GetUserIdentity(env.Repo)
104		if err != nil {
105			return err
106		}
107
108		return nil
109	}
110}
111
112// CloseBackend is a wrapper for a RunE function that will close the Backend properly
113// if it has been opened.
114// This wrapper style is necessary because a Cobra PostE function does not run if RunE return an error.
115func CloseBackend(env *Env, runE func(cmd *cobra.Command, args []string) error) func(*cobra.Command, []string) error {
116	return func(cmd *cobra.Command, args []string) error {
117		errRun := runE(cmd, args)
118
119		if env.Backend == nil {
120			return nil
121		}
122		err := env.Backend.Close()
123		env.Backend = nil
124
125		// prioritize the RunE error
126		if errRun != nil {
127			return errRun
128		}
129		return err
130	}
131}
132
133func CacheBuildProgressBar(env *Env, events chan cache.BuildEvent) error {
134	var progress *mpb.Progress
135	var bars = make(map[string]*mpb.Bar)
136
137	for event := range events {
138		if event.Err != nil {
139			return event.Err
140		}
141
142		if progress == nil {
143			progress = mpb.New(mpb.WithOutput(env.Err.Raw()))
144		}
145
146		switch event.Event {
147		case cache.BuildEventCacheIsBuilt:
148			env.Err.Println("Building cache... ")
149		case cache.BuildEventStarted:
150			bars[event.Typename] = progress.AddBar(-1,
151				mpb.BarRemoveOnComplete(),
152				mpb.PrependDecorators(
153					decor.Name(event.Typename, decor.WCSyncSpace),
154					decor.CountersNoUnit("%d / %d", decor.WCSyncSpace),
155				),
156				mpb.AppendDecorators(decor.Percentage(decor.WCSyncSpace)),
157			)
158		case cache.BuildEventProgress:
159			bars[event.Typename].SetCurrent(event.Progress)
160			bars[event.Typename].SetTotal(event.Total, event.Progress == event.Total)
161		case cache.BuildEventFinished:
162			if bar := bars[event.Typename]; !bar.Completed() {
163				bar.SetTotal(0, true)
164			}
165		}
166	}
167
168	if progress != nil {
169		progress.Wait()
170	}
171
172	return nil
173}