@@ -17,18 +17,12 @@ package bufpolicy
1717import (
1818 "bytes"
1919 "encoding/hex"
20- "encoding/json"
2120 "errors"
2221 "fmt"
23- "slices"
2422 "strconv"
2523 "strings"
2624
27- pluginoptionv1 "buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go/buf/plugin/option/v1"
28- "buf.build/go/bufplugin/option"
29- "buf.build/go/standard/xslices"
3025 "github.com/bufbuild/buf/private/bufpkg/bufcas"
31- "github.com/bufbuild/buf/private/bufpkg/bufconfig"
3226 "github.com/bufbuild/buf/private/bufpkg/bufparse"
3327 "github.com/bufbuild/buf/private/pkg/syserror"
3428)
@@ -221,7 +215,7 @@ func (*digest) isDigest() {}
221215
222216// getO1Digest returns the O1 digest for the given PolicyConfig.
223217func getO1Digest (policyConfig PolicyConfig ) (Digest , error ) {
224- policyDataJSON , err := marshalStablePolicyConfig (policyConfig )
218+ policyDataJSON , err := marshalPolicyConfigAsJSON (policyConfig )
225219 if err != nil {
226220 return nil , err
227221 }
@@ -231,194 +225,3 @@ func getO1Digest(policyConfig PolicyConfig) (Digest, error) {
231225 }
232226 return NewDigest (DigestTypeO1 , bufcasDigest )
233227}
234-
235- // marshalStablePolicyConfig marshals the given PolicyConfig to a stable JSON representation.
236- //
237- // This is used for the O1 digest and should not be used for other purposes.
238- // It is a valid JSON encoding of the type buf.registry.policy.v1beta1.PolicyConfig.
239- func marshalStablePolicyConfig (policyConfig PolicyConfig ) ([]byte , error ) {
240- lintConfig := policyConfig .LintConfig ()
241- if lintConfig == nil {
242- return nil , syserror .Newf ("policyConfig.LintConfig() must not be nil" )
243- }
244- breakingConfig := policyConfig .BreakingConfig ()
245- if breakingConfig == nil {
246- return nil , syserror .Newf ("policyConfig.BreakingConfig() must not be nil" )
247- }
248- pluginConfigs , err := xslices .MapError (policyConfig .PluginConfigs (), func (pluginConfig bufconfig.PluginConfig ) (* policyV1Beta1PolicyConfig_PluginConfig , error ) {
249- ref := pluginConfig .Ref ()
250- if ref == nil {
251- return nil , fmt .Errorf ("PluginConfig must have a non-nil Ref" )
252- }
253- optionsConfig , err := optionsToOptionConfig (pluginConfig .Options ())
254- if err != nil {
255- return nil , err
256- }
257- return & policyV1Beta1PolicyConfig_PluginConfig {
258- Name : policyV1Beta1PolicyConfig_PluginConfig_Name {
259- Owner : ref .FullName ().Owner (),
260- Plugin : ref .FullName ().Name (),
261- Ref : ref .Ref (),
262- },
263- Options : optionsConfig ,
264- Args : pluginConfig .Args (),
265- }, nil
266- })
267- if err != nil {
268- return nil , fmt .Errorf ("failed converting PluginConfigs to PolicyConfig_CheckPluginConfig: %w" , err )
269- }
270- slices .SortFunc (pluginConfigs , func (a , b * policyV1Beta1PolicyConfig_PluginConfig ) int {
271- // Sort by owner, plugin, and ref.
272- return strings .Compare (
273- fmt .Sprintf ("%s/%s:%s" , a .Name .Owner , a .Name .Plugin , a .Name .Ref ),
274- fmt .Sprintf ("%s/%s:%s" , b .Name .Owner , b .Name .Plugin , b .Name .Ref ),
275- )
276- })
277- config := policyV1Beta1PolicyConfig {
278- Lint : & policyV1Beta1PolicyConfig_LintConfig {
279- Use : lintConfig .UseIDsAndCategories (),
280- Except : lintConfig .ExceptIDsAndCategories (),
281- EnumZeroValueSuffix : lintConfig .EnumZeroValueSuffix (),
282- RpcAllowSameRequestResponse : lintConfig .RPCAllowSameRequestResponse (),
283- RpcAllowGoogleProtobufEmptyRequests : lintConfig .RPCAllowGoogleProtobufEmptyRequests (),
284- RpcAllowGoogleProtobufEmptyResponses : lintConfig .RPCAllowGoogleProtobufEmptyResponses (),
285- ServiceSuffix : lintConfig .ServiceSuffix (),
286- },
287- Breaking : & policyV1Beta1PolicyConfig_BreakingConfig {
288- Use : breakingConfig .UseIDsAndCategories (),
289- Except : breakingConfig .ExceptIDsAndCategories (),
290- IgnoreUnstablePackages : breakingConfig .IgnoreUnstablePackages (),
291- },
292- Plugins : pluginConfigs ,
293- }
294- return json .Marshal (config )
295- }
296-
297- // policyV1Beta1PolicyConfig is a stable JSON representation of the buf.registry.policy.v1beta1.PolicyConfig.
298- type policyV1Beta1PolicyConfig struct {
299- Lint * policyV1Beta1PolicyConfig_LintConfig `json:"lint,omitempty"`
300- Breaking * policyV1Beta1PolicyConfig_BreakingConfig `json:"breaking,omitempty"`
301- Plugins []* policyV1Beta1PolicyConfig_PluginConfig `json:"plugins,omitempty"`
302- }
303-
304- // policyV1Beta1PolicyConfig_LintConfig is a stable JSON representation of the buf.registry.policy.v1beta1.PolicyConfig.LintConfig.
305- type policyV1Beta1PolicyConfig_LintConfig struct {
306- Use []string `json:"use,omitempty"`
307- Except []string `json:"except,omitempty"`
308- EnumZeroValueSuffix string `json:"enumZeroValue_suffix,omitempty"`
309- RpcAllowSameRequestResponse bool `json:"rpcAllowSame_request_response,omitempty"`
310- RpcAllowGoogleProtobufEmptyRequests bool `json:"rpcAllowGoogleProtobufEmptyRequests,omitempty"`
311- RpcAllowGoogleProtobufEmptyResponses bool `json:"rpcAllowGoogleProtobufEmptyResponses,omitempty"`
312- ServiceSuffix string `json:"serviceSuffix,omitempty"`
313- }
314-
315- // policyV1Beta1PolicyConfig_BreakingConfig is a stable JSON representation of the buf.registry.policy.v1beta1.PolicyConfig.BreakingConfig.
316- type policyV1Beta1PolicyConfig_BreakingConfig struct {
317- Use []string `json:"use,omitempty"`
318- Except []string `json:"except,omitempty"`
319- IgnoreUnstablePackages bool `json:"ignoreUnstablePackages,omitempty"`
320- }
321-
322- // policyV1Beta1PolicyConfig_PluginConfig is a stable JSON representation of the buf.registry.policy.v1beta1.PolicyConfig.PluginConfig.
323- type policyV1Beta1PolicyConfig_PluginConfig struct {
324- Name policyV1Beta1PolicyConfig_PluginConfig_Name `json:"name,omitempty"`
325- Options []* optionV1Option `json:"options,omitempty"`
326- Args []string `json:"args,omitempty"`
327- }
328-
329- // policyV1Beta1PolicyConfig_PluginConfig_Name is a stable JSON representation of the buf.registry.policy.v1beta1.PolicyConfig.PluginConfig.Name.
330- type policyV1Beta1PolicyConfig_PluginConfig_Name struct {
331- Owner string `json:"owner,omitempty"`
332- Plugin string `json:"plugin,omitempty"`
333- Ref string `json:"ref,omitempty"`
334- }
335-
336- // optionV1Option is a stable JSON representation of the buf.plugin.option.v1.Option.
337- type optionV1Option struct {
338- Key string `json:"key,omitempty"`
339- Value * optionV1Value `json:"value,omitempty"`
340- }
341-
342- // optionV1Value is a stable JSON representation of the buf.plugin.option.v1.Value.
343- type optionV1Value struct {
344- BoolValue bool `json:"boolValue,omitempty"`
345- Int64Value int64 `json:"intValue,omitempty"`
346- DoubleValue float64 `json:"floatValue,omitempty"`
347- StringValue string `json:"stringValue,omitempty"`
348- BytesValue []byte `json:"bytesValue,omitempty"`
349- ListValue * optionV1ListValue `json:"listValue,omitempty"`
350- }
351-
352- // optionV1ListValue is a stable JSON representation of the buf.plugin.option.v1.ListValue.
353- type optionV1ListValue struct {
354- Values []* optionV1Value `json:"values,omitempty"`
355- }
356-
357- // optionsToOptionsV1Options converts a map of options to a slice of optionV1Option.
358- func optionsToOptionConfig (keyToValue map [string ]any ) ([]* optionV1Option , error ) {
359- options , err := option .NewOptions (keyToValue ) // This will validate the options.
360- if err != nil {
361- return nil , fmt .Errorf ("failed to convert options: %w" , err )
362- }
363- optionsProto , err := options .ToProto ()
364- if err != nil {
365- return nil , fmt .Errorf ("failed to convert options to proto: %w" , err )
366- }
367- // Sort the options by key to ensure a stable order.
368- slices .SortFunc (optionsProto , func (a , b * pluginoptionv1.Option ) int {
369- return strings .Compare (a .Key , b .Key )
370- })
371- optionsV1Options := make ([]* optionV1Option , len (optionsProto ))
372- for i , optionProto := range optionsProto {
373- optionValue , err := optionV1ValueProtoToOptionValue (optionProto .Value )
374- if err != nil {
375- return nil , fmt .Errorf ("failed to convert option value: %w" , err )
376- }
377- optionsV1Options [i ] = & optionV1Option {
378- Key : optionProto .Key ,
379- Value : optionValue ,
380- }
381- }
382- return optionsV1Options , nil
383- }
384-
385- func optionV1ValueProtoToOptionValue (optionValue * pluginoptionv1.Value ) (* optionV1Value , error ) {
386- if optionValue == nil {
387- return nil , nil
388- }
389- switch optionValue .Type .(type ) {
390- case * pluginoptionv1.Value_BoolValue :
391- return & optionV1Value {BoolValue : optionValue .GetBoolValue ()}, nil
392- case * pluginoptionv1.Value_Int64Value :
393- return & optionV1Value {Int64Value : optionValue .GetInt64Value ()}, nil
394- case * pluginoptionv1.Value_DoubleValue :
395- return & optionV1Value {DoubleValue : optionValue .GetDoubleValue ()}, nil
396- case * pluginoptionv1.Value_StringValue :
397- return & optionV1Value {StringValue : optionValue .GetStringValue ()}, nil
398- case * pluginoptionv1.Value_BytesValue :
399- return & optionV1Value {BytesValue : optionValue .GetBytesValue ()}, nil
400- case * pluginoptionv1.Value_ListValue :
401- listValue , err := optionV1ListValueProtoToOptionListValue (optionValue .GetListValue ())
402- if err != nil {
403- return nil , err
404- }
405- return & optionV1Value {ListValue : listValue }, nil
406- default :
407- return nil , fmt .Errorf ("unknown option value type: %T" , optionValue .Type )
408- }
409- }
410-
411- func optionV1ListValueProtoToOptionListValue (listValue * pluginoptionv1.ListValue ) (* optionV1ListValue , error ) {
412- if listValue == nil {
413- return nil , nil
414- }
415- values := make ([]* optionV1Value , len (listValue .Values ))
416- for i , value := range listValue .Values {
417- optionValue , err := optionV1ValueProtoToOptionValue (value )
418- if err != nil {
419- return nil , fmt .Errorf ("failed to convert option value: %w" , err )
420- }
421- values [i ] = optionValue
422- }
423- return & optionV1ListValue {Values : values }, nil
424- }
0 commit comments