83 lines
2.0 KiB
Go
83 lines
2.0 KiB
Go
package textual
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"google.golang.org/protobuf/reflect/protoreflect"
|
|
)
|
|
|
|
var (
|
|
hashPrefix = "SHA-256="
|
|
maxByteLen = 35 // Maximum byte length to be displayed as is. Longer than this, we hash.
|
|
)
|
|
|
|
// NewBytesValueRenderer returns a ValueRenderer for Protobuf bytes, which are
|
|
// encoded as capital-letter hexadecimal, without the '0x' prefix.
|
|
func NewBytesValueRenderer() ValueRenderer {
|
|
return bytesValueRenderer{}
|
|
}
|
|
|
|
type bytesValueRenderer struct{}
|
|
|
|
var _ ValueRenderer = bytesValueRenderer{}
|
|
|
|
func (vr bytesValueRenderer) Format(ctx context.Context, v protoreflect.Value) ([]Screen, error) {
|
|
bz := v.Bytes()
|
|
|
|
if len(bz) <= maxByteLen {
|
|
text := toHex(bz)
|
|
return []Screen{{Content: text}}, nil
|
|
}
|
|
|
|
// For long bytes, we display the hash.
|
|
hasher := sha256.New()
|
|
_, err := hasher.Write(bz)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
h := hasher.Sum(nil)
|
|
|
|
text := fmt.Sprintf("%s%s", hashPrefix, toHex(h))
|
|
return []Screen{{Content: text}}, nil
|
|
}
|
|
|
|
func (vr bytesValueRenderer) Parse(_ context.Context, screens []Screen) (protoreflect.Value, error) {
|
|
if len(screens) != 1 {
|
|
return nilValue, fmt.Errorf("expected single screen: %v", screens)
|
|
}
|
|
formatted := screens[0].Content
|
|
|
|
// If the formatted string starts with `SHA-256=`, then we can't actually
|
|
// invert to get the original bytes. In this case, we return empty bytes.
|
|
if strings.HasPrefix(formatted, hashPrefix) {
|
|
return protoreflect.ValueOfBytes([]byte{}), nil
|
|
}
|
|
|
|
// Remove all spaces between every 4th char, then we can decode hex.
|
|
data, err := hex.DecodeString(strings.ReplaceAll(formatted, " ", ""))
|
|
if err != nil {
|
|
return nilValue, err
|
|
}
|
|
|
|
return protoreflect.ValueOfBytes(data), nil
|
|
}
|
|
|
|
// toHex converts bytes to hex, and inserts a space every 4th character.
|
|
func toHex(bz []byte) string {
|
|
text := strings.ToUpper(hex.EncodeToString(bz))
|
|
|
|
var buffer bytes.Buffer
|
|
for i, r := range text {
|
|
buffer.WriteRune(r)
|
|
if i < len(text)-1 && i%4 == 3 {
|
|
buffer.WriteRune(' ')
|
|
}
|
|
}
|
|
return buffer.String()
|
|
}
|