Skip to content

Commit

Permalink
Add 'protols decode' cli command
Browse files Browse the repository at this point in the history
  • Loading branch information
kralicky committed Oct 21, 2023
1 parent 4e9ca42 commit 3a4ba5a
Show file tree
Hide file tree
Showing 5 changed files with 278 additions and 2 deletions.
7 changes: 7 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ module github.com/kralicky/protols
go 1.21

require (
github.com/AlecAivazis/survey/v2 v2.3.7
github.com/bufbuild/protocompile v0.4.0
github.com/gogo/protobuf v1.3.2
github.com/jhump/protoreflect v1.15.1
github.com/kralicky/gpkg v0.0.0-20220311205216-0d8ea9557555
github.com/mattn/go-tty v0.0.5
github.com/samber/lo v1.38.1
github.com/spf13/cobra v1.7.0
go.uber.org/multierr v1.11.0
Expand All @@ -25,11 +27,16 @@ require (
cloud.google.com/go/dlp v1.10.1 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/mattn/go-colorable v0.1.4 // indirect
github.com/mattn/go-isatty v0.0.10 // indirect
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/plar/go-adaptive-radix-tree v1.0.5 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/net v0.16.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/telemetry v0.0.0-20231011160506-788d5629a052 // indirect
golang.org/x/term v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/vuln v1.0.1 // indirect
google.golang.org/grpc v1.57.0 // indirect
Expand Down
40 changes: 38 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
cloud.google.com/go/dlp v1.10.1 h1:tF3wsJ2QulRhRLWPzWVkeDz3FkOGVoMl6cmDUHtfYxw=
cloud.google.com/go/dlp v1.10.1/go.mod h1:IM8BWz1iJd8njcNcG0+Kyd9OPnqnRNkDV8j42VT5KOI=
github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand All @@ -12,14 +18,29 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kralicky/gpkg v0.0.0-20220311205216-0d8ea9557555 h1:w/v9aYd9gdL0pEofCiOM7MWNroB9+HWxHT29Wj2NMYc=
github.com/kralicky/gpkg v0.0.0-20220311205216-0d8ea9557555/go.mod h1:EJrGSfmocDg2CBjHDm3zy9oxNKCSGhf+MNTiN1DRbKA=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-tty v0.0.5 h1:s09uXI7yDbXzzTTfw3zonKFzwGkyYlgU3OMjqA0ddz4=
github.com/mattn/go-tty v0.0.5/go.mod h1:u5GGXBtZU6RQoKV8gY5W6UhMudbR5vXnUe7j3pxse28=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/plar/go-adaptive-radix-tree v1.0.5 h1:rHR89qy/6c24TBAHullFMrJsU9hGlKmPibdBGU6/gbM=
github.com/plar/go-adaptive-radix-tree v1.0.5/go.mod h1:15VOUO7R9MhJL8HOJdpydR0rvanrtRE6fA6XSa/tqWE=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand All @@ -36,6 +57,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
Expand All @@ -44,27 +66,41 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/exp/typeparams v0.0.0-20221212164502-fae10dda9338 h1:2O2DON6y3XMJiQRAS1UWU+54aec2uopH3x7MAiqGW6Y=
golang.org/x/exp/typeparams v0.0.0-20221212164502-fae10dda9338/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos=
golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/telemetry v0.0.0-20231011160506-788d5629a052 h1:1baVNneD/IRxmu8JQdBuki78zUqBtZxq8smZXQj0X2Y=
golang.org/x/telemetry v0.0.0-20231011160506-788d5629a052/go.mod h1:6p4ScoNeC2dhpQ1nSSMmkZ7mEj5JQUSCyc0uExBp5T4=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
Expand Down
15 changes: 15 additions & 0 deletions pkg/lsp/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/bufbuild/protocompile/reporter"
gsync "github.com/kralicky/gpkg/sync"
"github.com/kralicky/protols/pkg/format"
"golang.org/x/exp/slices"
"golang.org/x/sync/errgroup"
"golang.org/x/tools/gopls/pkg/lsp/cache"
"golang.org/x/tools/gopls/pkg/lsp/protocol"
Expand Down Expand Up @@ -1823,3 +1824,17 @@ func (c *Cache) FindAllDescriptorsByPrefix(ctx context.Context, prefix string, l
}
return combined
}

func (c *Cache) AllMessages() []protoreflect.MessageDescriptor {
c.resultsMu.RLock()
defer c.resultsMu.RUnlock()
var all []protoreflect.MessageDescriptor
for _, res := range c.results {
msgs := res.Messages()
all = slices.Grow(all, msgs.Len())
for i, l := 0, msgs.Len(); i < l; i++ {
all = append(all, msgs.Get(i))
}
}
return all
}
217 changes: 217 additions & 0 deletions pkg/protols/commands/decode.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
package commands

import (
"bytes"
"context"
"encoding/base64"
"fmt"
"io"
"os"
"strings"

"github.com/AlecAivazis/survey/v2"
"github.com/kralicky/protols/pkg/lsp"
"github.com/kralicky/protols/pkg/sources"
"github.com/mattn/go-tty"
"github.com/spf13/cobra"
"golang.org/x/tools/gopls/pkg/lsp/protocol"
"golang.org/x/tools/gopls/pkg/span"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/encoding/prototext"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/testing/protopack"
"google.golang.org/protobuf/types/dynamicpb"
)

// DecodeCmd represents the decode command
func BuildDecodeCmd() *cobra.Command {
var output string
var msgType string
cmd := &cobra.Command{
Use: "decode [--type=pkg.Message]",
Short: "Decodes a protobuf message from stdin and prints it in text format",
Long: `
If a message type is given with --type, protols will attempt to look up the message
and use it to provide type information when decoding. If the message could not be
found or if no message name is given, a textual representation of the wire format
will be printed instead.
`[1:],
RunE: func(cmd *cobra.Command, args []string) error {
in := cmd.InOrStdin()
if len(msgType) == 0 {
text, err := decodeWithNoType(cmd.Context(), in)
if err != nil {
return err
}
cmd.Println(text)
return nil
} else {
msg, err := decodeWithType(cmd.Context(), in, msgType)
if err != nil {
return err
}
switch output {
case "text":
cmd.Println(prototext.MarshalOptions{
Multiline: true,
Indent: " ",
AllowPartial: true,
EmitUnknown: true,
}.Format(msg))
case "json":
cmd.Println(protojson.MarshalOptions{
Multiline: true,
Indent: " ",
AllowPartial: true,
UseProtoNames: true,
}.Format(msg))
}
return nil
}
},
}
cmd.Flags().StringVarP(&msgType, "type", "t", "", "The message type to use when decoding")
cmd.Flags().StringVarP(&output, "output", "o", "text", "Output format (text|json)")
return cmd
}

func decodeWithNoType(ctx context.Context, in io.Reader) (string, error) {
input, err := readMaybeBase64AndTrim(in)
if err != nil {
return "", err
}
msg := protopack.Message{}
msg.UnmarshalAbductive(input, nil)
if len(msg) == 0 {
return "", fmt.Errorf("no input")
}
return strings.ReplaceAll(fmt.Sprintf("%+v\n", msg), "\t", " "), nil
}

func decodeWithType(ctx context.Context, in io.Reader, msgType string) (proto.Message, error) {
cwd, err := os.Getwd()
if err != nil {
return nil, err
}
cache := lsp.NewCache(protocol.WorkspaceFolder{
URI: string(span.URIFromPath(cwd)),
})
cache.LoadFiles(sources.SearchDirs(cwd))
allMsgs := cache.AllMessages()
var exact protoreflect.MessageDescriptor
var exactNameOnly []protoreflect.MessageDescriptor
var partialMatch []protoreflect.MessageDescriptor
for _, d := range allMsgs {
if string(d.FullName()) == msgType {
// found it
exact = d
break
} else if string(d.Name()) == msgType {
// found a name match
exactNameOnly = append(exactNameOnly, d)
} else if strings.Contains(strings.ToLower(string(d.FullName())), strings.ToLower(msgType)) {
// found a partial match
partialMatch = append(partialMatch, d)
}
}
if exact != nil {
// found an exact match, use it
return decodeWithDescriptor(ctx, in, exact)
}
if len(exactNameOnly) == 1 {
// found a single name match, use it
return decodeWithDescriptor(ctx, in, exactNameOnly[0])
} else if len(exactNameOnly) > 1 {
// found multiple name matches, prompt the user to choose one
return chooseAndDecode(ctx, in, exactNameOnly)
}
if len(partialMatch) == 1 {
// found a single partial match, use it
return decodeWithDescriptor(ctx, in, partialMatch[0])
} else if len(partialMatch) > 1 {
// found multiple partial matches, prompt the user to choose one
return chooseAndDecode(ctx, in, partialMatch)
}

return nil, fmt.Errorf("could not find a matching type for %q", msgType)
}

func chooseAndDecode(ctx context.Context, in io.Reader, choices []protoreflect.MessageDescriptor) (proto.Message, error) {
var selected string
tty, err := tty.Open()
if err != nil {
return nil, err
}
defer tty.Close()
if err := survey.AskOne(&survey.Select{
Message: "Multiple message types found, choose one:",
Options: func() []string {
var opts []string
for _, d := range choices {
opts = append(opts, string(d.FullName()))
}
return opts
}(),
Default: string(choices[0].FullName()),
}, &selected, survey.WithStdio(tty.Input(), tty.Output(), tty.Output())); err != nil {
return nil, err
}
for _, d := range choices {
if string(d.FullName()) == selected {
return decodeWithDescriptor(ctx, in, d)
}
}
return nil, fmt.Errorf("no type selected")
}

func decodeWithDescriptor(ctx context.Context, in io.Reader, desc protoreflect.MessageDescriptor) (proto.Message, error) {
input, err := readMaybeBase64AndTrim(in)
if err != nil {
return nil, err
}
// try to decode as wire format
newMsg := dynamicpb.NewMessage(desc)
if err := proto.Unmarshal(input, newMsg); err != nil {
return nil, fmt.Errorf("could not decode input (wrong type?): %w", err)
}

return newMsg, nil
}

func looksLikeBase64(input []byte) bool {
return len(bytes.Trim(input, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/-_=")) == 0
}

func readMaybeBase64AndTrim(in io.Reader) ([]byte, error) {
input, err := io.ReadAll(in)
if err != nil {
return nil, err
}
input = bytes.TrimSpace(input)
if len(input) == 0 {
return nil, fmt.Errorf("no input")
}

// figure out what kind of input we have
// 1. check if it's base64 encoded
if looksLikeBase64(input) {
encodings := []*base64.Encoding{
base64.StdEncoding,
base64.URLEncoding,
base64.RawStdEncoding,
base64.RawURLEncoding,
}
if bytes.HasSuffix(input, []byte{'='}) {
encodings = encodings[0:2]
}
for _, codec := range encodings {
decoded, err := codec.DecodeString(string(input))
if err == nil {
input = decoded
break
}
}
}
return input, nil
}
1 change: 1 addition & 0 deletions pkg/protols/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ func BuildRootCmd() *cobra.Command {
rootCmd.AddCommand(commands.BuildFmtCmd())
rootCmd.AddCommand(commands.BuildServeCmd())
rootCmd.AddCommand(commands.BuildVetCmd())
rootCmd.AddCommand(commands.BuildDecodeCmd())
//+cobra:subcommands

return rootCmd
Expand Down

0 comments on commit 3a4ba5a

Please sign in to comment.