From 794a835e83eef06d3a43999c4e33fc01ce3b5a2e Mon Sep 17 00:00:00 2001 From: Denise Li Date: Thu, 18 Apr 2024 13:20:43 -0400 Subject: [PATCH] feat: Create Singleton func for convenient singleton construction (#1297) Fixes issue https://github.com/TBD54566975/ftl/issues/1295 --------- Co-authored-by: Alec Thomas --- go-runtime/ftl/singleton.go | 27 ++++++++++++++++++++++++++ go-runtime/ftl/singleton_test.go | 33 ++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 go-runtime/ftl/singleton.go create mode 100644 go-runtime/ftl/singleton_test.go diff --git a/go-runtime/ftl/singleton.go b/go-runtime/ftl/singleton.go new file mode 100644 index 000000000..ffffbe793 --- /dev/null +++ b/go-runtime/ftl/singleton.go @@ -0,0 +1,27 @@ +package ftl + +import ( + "context" + "sync" +) + +type SingletonHandle[T any] struct { + fn func(context.Context) (T, error) + out T + once *sync.Once +} + +func (sh *SingletonHandle[T]) Get(ctx context.Context) T { + sh.once.Do(func() { + t, err := sh.fn(ctx) + if err != nil { + panic(err) + } + sh.out = t + }) + return sh.out +} + +func Singleton[T any](fn func(context.Context) (T, error)) SingletonHandle[T] { + return SingletonHandle[T]{fn: fn, once: &sync.Once{}} +} diff --git a/go-runtime/ftl/singleton_test.go b/go-runtime/ftl/singleton_test.go new file mode 100644 index 000000000..996eb0d47 --- /dev/null +++ b/go-runtime/ftl/singleton_test.go @@ -0,0 +1,33 @@ +package ftl + +import ( + "context" + "fmt" + "testing" + + "github.com/alecthomas/assert/v2" +) + +func TestSingletonBaseCase(t *testing.T) { + incrementer := 0 + + ctx := context.Background() + once := Singleton[string](func(ctx context.Context) (string, error) { + incrementer++ + return "only once", nil + }) + + assert.Equal(t, once.Get(ctx), "only once") + assert.Equal(t, once.Get(ctx), "only once") + assert.Equal(t, incrementer, 1) +} + +func TestSingletonPanic(t *testing.T) { + ctx := context.Background() + once := Singleton[string](func(ctx context.Context) (string, error) { + return "", fmt.Errorf("test error") + }) + assert.Panics(t, func() { + once.Get(ctx) + }) +}