From 428552e58cf8056dfe3514c736931936cc4c7099 Mon Sep 17 00:00:00 2001 From: Virgil Date: Thu, 2 Apr 2026 06:22:29 +0000 Subject: [PATCH] feat(api): add iterator-based spec group registration Co-Authored-By: Virgil --- spec_registry.go | 19 ++++++++++++++++++- spec_registry_test.go | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/spec_registry.go b/spec_registry.go index 264ec2c..fd8b40a 100644 --- a/spec_registry.go +++ b/spec_registry.go @@ -25,10 +25,27 @@ var specRegistry struct { // // api.RegisterSpecGroups(api.NewToolBridge("/mcp")) func RegisterSpecGroups(groups ...RouteGroup) { + RegisterSpecGroupsIter(slices.Values(groups)) +} + +// RegisterSpecGroupsIter adds route groups from an iterator to the package-level +// spec registry. +// +// Nil groups are ignored. Registered groups are returned by RegisteredSpecGroups +// in the order they were added. +// +// Example: +// +// api.RegisterSpecGroupsIter(api.RegisteredSpecGroupsIter()) +func RegisterSpecGroupsIter(groups iter.Seq[RouteGroup]) { + if groups == nil { + return + } + specRegistry.mu.Lock() defer specRegistry.mu.Unlock() - for _, group := range groups { + for group := range groups { if group == nil { continue } diff --git a/spec_registry_test.go b/spec_registry_test.go index 72e0789..b3d22eb 100644 --- a/spec_registry_test.go +++ b/spec_registry_test.go @@ -3,6 +3,7 @@ package api_test import ( + "iter" "testing" "github.com/gin-gonic/gin" @@ -75,3 +76,37 @@ func TestRegisterSpecGroups_Good_IteratorReturnsSnapshot(t *testing.T) { t.Fatalf("expected iterator snapshot to preserve alpha at /alpha, got %s at %s", groups[0].Name(), groups[0].BasePath()) } } + +func TestRegisterSpecGroupsIter_Good_DeduplicatesAndRegisters(t *testing.T) { + snapshot := api.RegisteredSpecGroups() + api.ResetSpecGroups() + t.Cleanup(func() { + api.ResetSpecGroups() + api.RegisterSpecGroups(snapshot...) + }) + + first := &specRegistryStubGroup{name: "alpha", basePath: "/alpha"} + second := &specRegistryStubGroup{name: "alpha", basePath: "/alpha"} + third := &specRegistryStubGroup{name: "gamma", basePath: "/gamma"} + + groups := iter.Seq[api.RouteGroup](func(yield func(api.RouteGroup) bool) { + for _, group := range []api.RouteGroup{first, second, nil, third, first} { + if !yield(group) { + return + } + } + }) + + api.RegisterSpecGroupsIter(groups) + + registered := api.RegisteredSpecGroups() + if len(registered) != 2 { + t.Fatalf("expected 2 unique groups, got %d", len(registered)) + } + if registered[0].Name() != "alpha" || registered[0].BasePath() != "/alpha" { + t.Fatalf("expected first group to be alpha at /alpha, got %s at %s", registered[0].Name(), registered[0].BasePath()) + } + if registered[1].Name() != "gamma" || registered[1].BasePath() != "/gamma" { + t.Fatalf("expected second group to be gamma at /gamma, got %s at %s", registered[1].Name(), registered[1].BasePath()) + } +}