aboutsummaryrefslogblamecommitdiffstats
path: root/commands/execenv/loading.go
blob: 2263f70099af3af6cc7325c49460494068598a81 (plain) (tree)








































































































































































                                                                                                                      
package execenv

import (
	"fmt"
	"os"

	"github.com/spf13/cobra"
	"github.com/vbauerster/mpb/v8"
	"github.com/vbauerster/mpb/v8/decor"

	"github.com/MichaelMure/git-bug/cache"
	"github.com/MichaelMure/git-bug/entities/identity"
	"github.com/MichaelMure/git-bug/repository"
	"github.com/MichaelMure/git-bug/util/interrupt"
)

// LoadRepo is a pre-run function that load the repository for use in a command
func LoadRepo(env *Env) func(*cobra.Command, []string) error {
	return func(cmd *cobra.Command, args []string) error {
		cwd, err := os.Getwd()
		if err != nil {
			return fmt.Errorf("unable to get the current working directory: %q", err)
		}

		// Note: we are not loading clocks here because we assume that LoadRepo is only used
		//  when we don't manipulate entities, or as a child call of LoadBackend which will
		//  read all clocks anyway.
		env.Repo, err = repository.OpenGoGitRepo(cwd, gitBugNamespace, nil)
		if err == repository.ErrNotARepo {
			return fmt.Errorf("%s must be run from within a git Repo", RootCommandName)
		}
		if err != nil {
			return err
		}

		return nil
	}
}

// LoadRepoEnsureUser is the same as LoadRepo, but also ensure that the user has configured
// an identity. Use this pre-run function when an error after using the configured user won't
// do.
func LoadRepoEnsureUser(env *Env) func(*cobra.Command, []string) error {
	return func(cmd *cobra.Command, args []string) error {
		err := LoadRepo(env)(cmd, args)
		if err != nil {
			return err
		}

		_, err = identity.GetUserIdentity(env.Repo)
		if err != nil {
			return err
		}

		return nil
	}
}

// LoadBackend is a pre-run function that load the repository and the Backend for use in a command
// When using this function you also need to use CloseBackend as a post-run
func LoadBackend(env *Env) func(*cobra.Command, []string) error {
	return func(cmd *cobra.Command, args []string) error {
		err := LoadRepo(env)(cmd, args)
		if err != nil {
			return err
		}

		var events chan cache.BuildEvent
		env.Backend, events = cache.NewRepoCache(env.Repo)

		err = CacheBuildProgressBar(env, events)
		if err != nil {
			return err
		}

		cleaner := func(env *Env) interrupt.CleanerFunc {
			return func() error {
				if env.Backend != nil {
					err := env.Backend.Close()
					env.Backend = nil
					return err
				}
				return nil
			}
		}

		// Cleanup properly on interrupt
		interrupt.RegisterCleaner(cleaner(env))
		return nil
	}
}

// LoadBackendEnsureUser is the same as LoadBackend, but also ensure that the user has configured
// an identity. Use this pre-run function when an error after using the configured user won't
// do.
func LoadBackendEnsureUser(env *Env) func(*cobra.Command, []string) error {
	return func(cmd *cobra.Command, args []string) error {
		err := LoadBackend(env)(cmd, args)
		if err != nil {
			return err
		}

		_, err = identity.GetUserIdentity(env.Repo)
		if err != nil {
			return err
		}

		return nil
	}
}

// CloseBackend is a wrapper for a RunE function that will close the Backend properly
// if it has been opened.
// This wrapper style is necessary because a Cobra PostE function does not run if RunE return an error.
func CloseBackend(env *Env, runE func(cmd *cobra.Command, args []string) error) func(*cobra.Command, []string) error {
	return func(cmd *cobra.Command, args []string) error {
		errRun := runE(cmd, args)

		if env.Backend == nil {
			return nil
		}
		err := env.Backend.Close()
		env.Backend = nil

		// prioritize the RunE error
		if errRun != nil {
			return errRun
		}
		return err
	}
}

func CacheBuildProgressBar(env *Env, events chan cache.BuildEvent) error {
	var progress *mpb.Progress
	var bars = make(map[string]*mpb.Bar)

	for event := range events {
		if event.Err != nil {
			return event.Err
		}

		if progress == nil {
			progress = mpb.New(mpb.WithOutput(env.Err.Raw()))
		}

		switch event.Event {
		case cache.BuildEventCacheIsBuilt:
			env.Err.Println("Building cache... ")
		case cache.BuildEventStarted:
			bars[event.Typename] = progress.AddBar(-1,
				mpb.BarRemoveOnComplete(),
				mpb.PrependDecorators(
					decor.Name(event.Typename, decor.WCSyncSpace),
					decor.CountersNoUnit("%d / %d", decor.WCSyncSpace),
				),
				mpb.AppendDecorators(decor.Percentage(decor.WCSyncSpace)),
			)
		case cache.BuildEventProgress:
			bars[event.Typename].SetTotal(event.Total, false)
			bars[event.Typename].SetCurrent(event.Progress)
		}
	}

	if progress != nil {
		progress.Shutdown()
	}

	return nil
}