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