Skip to content

Commit 958b0ca

Browse files
authored
chore(kong): BindToProvider singletons workaround (#589)
BindToProvider in Kong will create new instances of bindings that come from a provider function every time they're requested by another binding. This means that we always re-open some of these resources multiple times: e.g. repository is opened when requested by state.Store, by spice.Service, and then by individual commands. Work around this with a sync.Once until the new [BindSingletonProvider option](alecthomas/kong#501) is released.
1 parent 2ca92f8 commit 958b0ca

File tree

1 file changed

+28
-6
lines changed

1 file changed

+28
-6
lines changed

main.go

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import (
88
"os"
99
"os/signal"
1010
"path/filepath"
11+
"reflect"
1112
"strconv"
13+
"sync"
1214

1315
"github.com/alecthomas/kong"
1416
"github.com/charmbracelet/lipgloss"
@@ -259,29 +261,29 @@ func (cmd *mainCmd) AfterApply(ctx context.Context, kctx *kong.Context, logger *
259261

260262
// TODO: bind interfaces, not values
261263

262-
err = kctx.BindToProvider(func() (*git.Repository, error) {
264+
err = kctx.BindToProvider(onceFunc(func() (*git.Repository, error) {
263265
repo, err := git.Open(ctx, ".", git.OpenOptions{
264266
Log: logger,
265267
})
266268
if err != nil {
267269
return nil, fmt.Errorf("open repository: %w", err)
268270
}
269271
return repo, nil
270-
})
272+
}))
271273
if err != nil {
272274
return fmt.Errorf("bind git repository: %w", err)
273275
}
274276

275-
err = kctx.BindToProvider(func(repo *git.Repository) (*state.Store, error) {
277+
err = kctx.BindToProvider(onceFunc(func(repo *git.Repository) (*state.Store, error) {
276278
return ensureStore(ctx, repo, logger, view)
277-
})
279+
}))
278280
if err != nil {
279281
return fmt.Errorf("bind state store: %w", err)
280282
}
281283

282-
err = kctx.BindToProvider(func(repo *git.Repository, store *state.Store) (*spice.Service, error) {
284+
err = kctx.BindToProvider(onceFunc(func(repo *git.Repository, store *state.Store) (*spice.Service, error) {
283285
return spice.NewService(ctx, repo, store, logger), nil
284-
})
286+
}))
285287
if err != nil {
286288
return fmt.Errorf("bind spice service: %w", err)
287289
}
@@ -298,3 +300,23 @@ var _buildView = func(stdin io.Reader, stderr io.Writer, interactive bool) (ui.V
298300
}
299301
return &ui.FileView{W: stderr}, nil
300302
}
303+
304+
// onceFunc generates a copy of F that records its result after the first call
305+
// and reproduces it in all following calls.
306+
//
307+
// TODO: drop usage once https://github.com/alecthomas/kong/pull/501 is released.
308+
func onceFunc[F any](f F) F {
309+
fv := reflect.ValueOf(f)
310+
ft := fv.Type()
311+
312+
var (
313+
once sync.Once
314+
outputs []reflect.Value
315+
)
316+
return reflect.MakeFunc(ft, func(inputs []reflect.Value) []reflect.Value {
317+
once.Do(func() {
318+
outputs = fv.Call(inputs)
319+
})
320+
return outputs
321+
}).Interface().(F)
322+
}

0 commit comments

Comments
 (0)