diff --git a/core_service.go b/core_service.go index a6c124e..9737ce9 100644 --- a/core_service.go +++ b/core_service.go @@ -126,7 +126,7 @@ func (s *CoreService) handleMissingKey(mk MissingKey) { } s.missingKeysMu.Lock() defer s.missingKeysMu.Unlock() - s.missingKeys = append(s.missingKeys, mk) + s.missingKeys = append(s.missingKeys, cloneMissingKey(mk)) } // MissingKeys returns all missing keys collected in collect mode. @@ -137,7 +137,9 @@ func (s *CoreService) MissingKeys() []MissingKey { s.missingKeysMu.Lock() defer s.missingKeysMu.Unlock() result := make([]MissingKey, len(s.missingKeys)) - copy(result, s.missingKeys) + for i, mk := range s.missingKeys { + result[i] = cloneMissingKey(mk) + } return result } diff --git a/core_service_test.go b/core_service_test.go index a14c95e..23ad650 100644 --- a/core_service_test.go +++ b/core_service_test.go @@ -49,3 +49,22 @@ func TestCoreServiceNilSafe(t *testing.T) { require.ErrorIs(t, svc.AddLoader(nil), ErrServiceNotInitialised) require.ErrorIs(t, svc.LoadFS(nil, "locales"), ErrServiceNotInitialised) } + +func TestCoreServiceMissingKeysReturnsCopies(t *testing.T) { + svc, err := New() + require.NoError(t, err) + coreSvc := &CoreService{svc: svc} + + coreSvc.SetMode(ModeCollect) + _ = svc.T("missing.copy.key", map[string]any{"foo": "bar"}) + + missing := coreSvc.MissingKeys() + require.Len(t, missing, 1) + require.Equal(t, "bar", missing[0].Args["foo"]) + + missing[0].Args["foo"] = "mutated" + + again := coreSvc.MissingKeys() + require.Len(t, again, 1) + assert.Equal(t, "bar", again[0].Args["foo"]) +} diff --git a/hooks.go b/hooks.go index 7a8b0f4..32b7467 100644 --- a/hooks.go +++ b/hooks.go @@ -201,7 +201,7 @@ func dispatchMissingKey(key string, args map[string]any) { return } file, line := missingKeyCaller() - mk := MissingKey{Key: key, Args: args, CallerFile: file, CallerLine: line} + mk := cloneMissingKey(MissingKey{Key: key, Args: args, CallerFile: file, CallerLine: line}) for _, h := range state.handlers { if h != nil { h(mk) @@ -209,6 +209,19 @@ func dispatchMissingKey(key string, args map[string]any) { } } +func cloneMissingKey(mk MissingKey) MissingKey { + if len(mk.Args) == 0 { + mk.Args = nil + return mk + } + args := make(map[string]any, len(mk.Args)) + for key, value := range mk.Args { + args[key] = value + } + mk.Args = args + return mk +} + func missingKeyCaller() (string, int) { const packagePrefix = "dappco.re/go/core/i18n."