Skip to content

Commit 70a4bf4

Browse files
authored
Add info package (#5)
1 parent 4309c40 commit 70a4bf4

File tree

24 files changed

+1240
-107
lines changed

24 files changed

+1240
-107
lines changed

.golangci.yml

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ linters:
3636
- ireturn # "accept interfaces, return structs" isn't ironclad
3737
- lll # don't want hard limits for line length
3838
- maintidx # covered by gocyclo
39+
- nilnil # allow this
3940
- nlreturn # generous whitespace violates house style
4041
- testifylint # does not want us to use assert
4142
- testpackage # internal tests are fine
@@ -65,12 +66,6 @@ issues:
6566
- gocritic
6667
path: check/location.go
6768
text: "commentFormatting"
68-
- linters:
69-
- nilnil
70-
path: check/rule.go
71-
- linters:
72-
- nilnil
73-
path: check/response_writer.go
7469
- linters:
7570
- unparam
7671
path: check/category_spec.go
@@ -80,9 +75,6 @@ issues:
8075
- linters:
8176
- unparam
8277
path: check/response.go
83-
- linters:
84-
- nilnil
85-
path: check/checktest/checktest.go
8678
- linters:
8779
- varnamelen
8880
path: check/internal/example

check/check_server.go

Lines changed: 0 additions & 42 deletions
This file was deleted.

check/client.go

Lines changed: 39 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ package check
1616

1717
import (
1818
"context"
19-
"fmt"
2019

2120
checkv1 "buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go/buf/plugin/check/v1"
21+
"buf.build/go/bufplugin/info"
2222
"buf.build/go/bufplugin/internal/gen/buf/plugin/check/v1/v1pluginrpc"
2323
"buf.build/go/bufplugin/internal/pkg/cache"
2424
"buf.build/go/bufplugin/internal/pkg/xslices"
@@ -31,7 +31,11 @@ const (
3131
)
3232

3333
// Client is a client for a custom lint or breaking change plugin.
34+
//
35+
// All calls with pluginrpc.Error with CodeUnimplemented if any procedure is not implemented.
3436
type Client interface {
37+
info.Client
38+
3539
// Check invokes a check using the plugin..
3640
Check(ctx context.Context, request Request, options ...CheckCallOption) (Response, error)
3741
// ListRules lists all available Rules from the plugin.
@@ -54,7 +58,7 @@ func NewClient(pluginrpcClient pluginrpc.Client, options ...ClientOption) Client
5458
for _, option := range options {
5559
option.applyToClient(clientOptions)
5660
}
57-
return newClient(pluginrpcClient, clientOptions.cacheRulesAndCategories)
61+
return newClient(pluginrpcClient, clientOptions.caching)
5862
}
5963

6064
// ClientOption is an option for a new Client.
@@ -64,12 +68,16 @@ type ClientOption interface {
6468
applyToClient(opts *clientOptions)
6569
}
6670

67-
// ClientWithCacheRulesAndCategories returns a new ClientOption that will result in the Rules from
68-
// ListRules and the Categories from ListCategories being cached.
71+
// ClientWithCaching returns a new ClientOption that will result caching for items
72+
// expected to be static:
6973
//
70-
// The default is to not cache Rules or Categories.
71-
func ClientWithCacheRulesAndCategories() ClientOption {
72-
return clientWithCacheRulesAndCategoriesOption{}
74+
// - The Rules from ListRules.
75+
// - The Categories from ListCategories.
76+
// - PluginInfo from GetPluginInfo.
77+
//
78+
// The default is to not cache.
79+
func ClientWithCaching() ClientOption {
80+
return clientWithCachingOption{}
7381
}
7482

7583
// NewClientForSpec return a new Client that directly uses the given Spec.
@@ -80,19 +88,15 @@ func NewClientForSpec(spec *Spec, options ...ClientForSpecOption) (Client, error
8088
for _, option := range options {
8189
option.applyToClientForSpec(clientForSpecOptions)
8290
}
83-
checkServiceHandler, err := NewCheckServiceHandler(spec)
84-
if err != nil {
85-
return nil, err
86-
}
87-
checkServiceServer, err := NewCheckServiceServer(checkServiceHandler)
91+
server, err := NewServer(spec)
8892
if err != nil {
8993
return nil, err
9094
}
9195
return newClient(
9296
pluginrpc.NewClient(
93-
pluginrpc.NewServerRunner(checkServiceServer),
97+
pluginrpc.NewServerRunner(server),
9498
),
95-
clientForSpecOptions.cacheRulesAndCategories,
99+
clientForSpecOptions.caching,
96100
), nil
97101
}
98102

@@ -113,9 +117,11 @@ type ListCategoriesCallOption func(*listCategoriesCallOptions)
113117
// *** PRIVATE ***
114118

115119
type client struct {
120+
info.Client
121+
116122
pluginrpcClient pluginrpc.Client
117123

118-
cacheRulesAndCategories bool
124+
caching bool
119125

120126
// Singleton ordering: rules -> categories -> checkServiceClient
121127
rules *cache.Singleton[[]Rule]
@@ -125,11 +131,16 @@ type client struct {
125131

126132
func newClient(
127133
pluginrpcClient pluginrpc.Client,
128-
cacheRulesAndCategories bool,
134+
caching bool,
129135
) *client {
136+
var infoClientOptions []info.ClientOption
137+
if caching {
138+
infoClientOptions = append(infoClientOptions, info.ClientWithCaching())
139+
}
130140
client := &client{
131-
pluginrpcClient: pluginrpcClient,
132-
cacheRulesAndCategories: cacheRulesAndCategories,
141+
Client: info.NewClient(pluginrpcClient, infoClientOptions...),
142+
pluginrpcClient: pluginrpcClient,
143+
caching: caching,
133144
}
134145
client.rules = cache.NewSingleton(client.listRulesUncached)
135146
client.categories = cache.NewSingleton(client.listCategoriesUncached)
@@ -174,14 +185,14 @@ func (c *client) Check(ctx context.Context, request Request, _ ...CheckCallOptio
174185
}
175186

176187
func (c *client) ListRules(ctx context.Context, _ ...ListRulesCallOption) ([]Rule, error) {
177-
if !c.cacheRulesAndCategories {
188+
if !c.caching {
178189
return c.listRulesUncached(ctx)
179190
}
180191
return c.rules.Get(ctx)
181192
}
182193

183194
func (c *client) ListCategories(ctx context.Context, _ ...ListCategoriesCallOption) ([]Category, error) {
184-
if !c.cacheRulesAndCategories {
195+
if !c.caching {
185196
return c.listCategoriesUncached(ctx)
186197
}
187198
return c.categories.Get(ctx)
@@ -285,7 +296,7 @@ func (c *client) getCheckServiceClientUncached(ctx context.Context) (v1pluginrpc
285296
v1pluginrpc.CheckServiceListCategoriesPath,
286297
} {
287298
if spec.ProcedureForPath(procedurePath) == nil {
288-
return nil, fmt.Errorf("plugin spec not implemented: RPC %q not found", procedurePath)
299+
return nil, pluginrpc.NewErrorf(pluginrpc.CodeUnimplemented, "procedure unimplemented: %q", procedurePath)
289300
}
290301
}
291302
return v1pluginrpc.NewCheckServiceClient(c.pluginrpcClient)
@@ -294,29 +305,29 @@ func (c *client) getCheckServiceClientUncached(ctx context.Context) (v1pluginrpc
294305
func (*client) isClient() {}
295306

296307
type clientOptions struct {
297-
cacheRulesAndCategories bool
308+
caching bool
298309
}
299310

300311
func newClientOptions() *clientOptions {
301312
return &clientOptions{}
302313
}
303314

304315
type clientForSpecOptions struct {
305-
cacheRulesAndCategories bool
316+
caching bool
306317
}
307318

308319
func newClientForSpecOptions() *clientForSpecOptions {
309320
return &clientForSpecOptions{}
310321
}
311322

312-
type clientWithCacheRulesAndCategoriesOption struct{}
323+
type clientWithCachingOption struct{}
313324

314-
func (clientWithCacheRulesAndCategoriesOption) applyToClient(clientOptions *clientOptions) {
315-
clientOptions.cacheRulesAndCategories = true
325+
func (clientWithCachingOption) applyToClient(clientOptions *clientOptions) {
326+
clientOptions.caching = true
316327
}
317328

318-
func (clientWithCacheRulesAndCategoriesOption) applyToClientForSpec(clientForSpecOptions *clientForSpecOptions) {
319-
clientForSpecOptions.cacheRulesAndCategories = true
329+
func (clientWithCachingOption) applyToClientForSpec(clientForSpecOptions *clientForSpecOptions) {
330+
clientForSpecOptions.caching = true
320331
}
321332

322333
type checkCallOptions struct{}

check/client_test.go

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,17 @@ import (
2020
"slices"
2121
"testing"
2222

23+
"buf.build/go/bufplugin/info"
2324
"buf.build/go/bufplugin/internal/pkg/xslices"
2425
"github.com/stretchr/testify/require"
26+
"pluginrpc.com/pluginrpc"
2527
)
2628

2729
func TestClientListRulesCategoriesSimple(t *testing.T) {
2830
t.Parallel()
2931

3032
testClientListRulesCategoriesSimple(t)
31-
testClientListRulesCategoriesSimple(t, ClientWithCacheRulesAndCategories())
33+
testClientListRulesCategoriesSimple(t, ClientWithCaching())
3234
}
3335

3436
func testClientListRulesCategoriesSimple(t *testing.T, options ...ClientForSpecOption) {
@@ -152,3 +154,56 @@ func testClientListRulesCount(t *testing.T, count int) {
152154
require.Equal(t, ruleSpecs[i].ID, rules[i].ID())
153155
}
154156
}
157+
158+
func TestPluginInfo(t *testing.T) {
159+
t.Parallel()
160+
161+
client, err := NewClientForSpec(
162+
&Spec{
163+
Rules: []*RuleSpec{
164+
{
165+
ID: "RULE1",
166+
Purpose: "Test RULE1.",
167+
Type: RuleTypeLint,
168+
Handler: nopRuleHandler,
169+
},
170+
},
171+
Info: &info.Spec{
172+
SPDXLicenseID: "apache-2.0",
173+
LicenseURL: "https://foo.com/license",
174+
},
175+
},
176+
)
177+
require.NoError(t, err)
178+
pluginInfo, err := client.GetPluginInfo(context.Background())
179+
require.NoError(t, err)
180+
license := pluginInfo.License()
181+
require.NotNil(t, license)
182+
require.NotNil(t, license.URL())
183+
// Case-sensitive.
184+
require.Equal(t, "Apache-2.0", license.SPDXLicenseID())
185+
require.Equal(t, "https://foo.com/license", license.URL().String())
186+
}
187+
188+
func TestPluginInfoUnimplemented(t *testing.T) {
189+
t.Parallel()
190+
191+
client, err := NewClientForSpec(
192+
&Spec{
193+
Rules: []*RuleSpec{
194+
{
195+
ID: "RULE1",
196+
Purpose: "Test RULE1.",
197+
Type: RuleTypeLint,
198+
Handler: nopRuleHandler,
199+
},
200+
},
201+
},
202+
)
203+
require.NoError(t, err)
204+
_, err = client.GetPluginInfo(context.Background())
205+
pluginrpcError := &pluginrpc.Error{}
206+
require.Error(t, err)
207+
require.ErrorAs(t, err, &pluginrpcError)
208+
require.Equal(t, pluginrpc.CodeUnimplemented, pluginrpcError.Code())
209+
}

check/internal/example/cmd/buf-plugin-field-lower-snake-case/main.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import (
3535

3636
"buf.build/go/bufplugin/check"
3737
"buf.build/go/bufplugin/check/checkutil"
38+
"buf.build/go/bufplugin/info"
3839
"google.golang.org/protobuf/reflect/protoreflect"
3940
)
4041

@@ -59,6 +60,12 @@ var (
5960
Rules: []*check.RuleSpec{
6061
fieldLowerSnakeCaseRuleSpec,
6162
},
63+
// Optional.
64+
Info: &info.Spec{
65+
URL: "https://github.com/bufbuild/bufplugin-go",
66+
SPDXLicenseID: "apache-2.0",
67+
LicenseURL: "https://github.com/bufbuild/bufplugin-go/blob/main/LICENSE",
68+
},
6269
}
6370
)
6471

check/internal/example/cmd/buf-plugin-field-option-safe-for-ml/main.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ import (
5656
"buf.build/go/bufplugin/check"
5757
"buf.build/go/bufplugin/check/checkutil"
5858
optionv1 "buf.build/go/bufplugin/check/internal/example/gen/acme/option/v1"
59+
"buf.build/go/bufplugin/info"
5960
"google.golang.org/protobuf/proto"
6061
"google.golang.org/protobuf/reflect/protoreflect"
6162
"google.golang.org/protobuf/types/descriptorpb"
@@ -107,6 +108,12 @@ var (
107108
Categories: []*check.CategorySpec{
108109
fieldOptionSafeForMLCategorySpec,
109110
},
111+
// Optional.
112+
Info: &info.Spec{
113+
URL: "https://github.com/bufbuild/bufplugin-go",
114+
SPDXLicenseID: "apache-2.0",
115+
LicenseURL: "https://github.com/bufbuild/bufplugin-go/blob/main/LICENSE",
116+
},
110117
}
111118
)
112119

check/internal/example/cmd/buf-plugin-syntax-specified/main.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import (
3737
"buf.build/go/bufplugin/check"
3838
"buf.build/go/bufplugin/check/checkutil"
3939
"buf.build/go/bufplugin/descriptor"
40+
"buf.build/go/bufplugin/info"
4041
)
4142

4243
// syntaxSpecifiedRuleID is the Rule ID of the syntax specified Rule.
@@ -60,6 +61,12 @@ var (
6061
Rules: []*check.RuleSpec{
6162
syntaxSpecifiedRuleSpec,
6263
},
64+
// Optional.
65+
Info: &info.Spec{
66+
URL: "https://github.com/bufbuild/bufplugin-go",
67+
SPDXLicenseID: "apache-2.0",
68+
LicenseURL: "https://github.com/bufbuild/bufplugin-go/blob/main/LICENSE",
69+
},
6370
}
6471
)
6572

0 commit comments

Comments
 (0)