Skip to content

Commit bed3ac0

Browse files
authored
feat(collections): add optional key and value naming methods (#20538)
1 parent eb7653c commit bed3ac0

File tree

14 files changed

+311
-20
lines changed

14 files changed

+311
-20
lines changed

collections/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
3939
* [#19861](https://github.com/cosmos/cosmos-sdk/pull/19861) Add `NewJSONValueCodec` value codec as an alternative for `codec.CollValue` from the SDK for non protobuf types.
4040
* [#21090](https://github.com/cosmos/cosmos-sdk/pull/21090) Introduces `Quad`, a composite key with four keys.
4141
* [#20704](https://github.com/cosmos/cosmos-sdk/pull/20704) Add `ModuleCodec` method to `Schema` and `HasSchemaCodec` interface in order to support `cosmossdk.io/schema` compatible indexing.
42+
* [#20538](https://github.com/cosmos/cosmos-sdk/pull/20538) Add `Nameable` variations to `KeyCodec` and `ValueCodec` to allow for better indexing of `collections` types.
4243

4344
## [v0.4.0](https://github.com/cosmos/cosmos-sdk/releases/tag/collections%2Fv0.4.0)
4445

collections/codec/bool.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import (
66
"strconv"
77
)
88

9-
func NewBoolKey[T ~bool]() KeyCodec[T] { return boolKey[T]{} }
9+
func NewBoolKey[T ~bool]() NameableKeyCodec[T] { return boolKey[T]{} }
1010

1111
type boolKey[T ~bool] struct{}
1212

@@ -64,3 +64,7 @@ func (b boolKey[T]) DecodeNonTerminal(buffer []byte) (int, T, error) {
6464
func (b boolKey[T]) SizeNonTerminal(key T) int {
6565
return b.Size(key)
6666
}
67+
68+
func (b boolKey[T]) WithName(name string) KeyCodec[T] {
69+
return NamedKeyCodec[T]{KeyCodec: b, Name: name}
70+
}

collections/codec/bytes.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
// using the BytesKey KeyCodec.
1111
const MaxBytesKeyNonTerminalSize = math.MaxUint8
1212

13-
func NewBytesKey[T ~[]byte]() KeyCodec[T] { return bytesKey[T]{} }
13+
func NewBytesKey[T ~[]byte]() NameableKeyCodec[T] { return bytesKey[T]{} }
1414

1515
type bytesKey[T ~[]byte] struct{}
1616

@@ -77,3 +77,7 @@ func (bytesKey[T]) DecodeNonTerminal(buffer []byte) (int, T, error) {
7777
func (bytesKey[T]) SizeNonTerminal(key T) int {
7878
return len(key) + 1
7979
}
80+
81+
func (b bytesKey[T]) WithName(name string) KeyCodec[T] {
82+
return NamedKeyCodec[T]{KeyCodec: b, Name: name}
83+
}

collections/codec/codec.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,9 @@ type UntypedValueCodec struct {
125125
}
126126

127127
// KeyToValueCodec converts a KeyCodec into a ValueCodec.
128-
func KeyToValueCodec[K any](keyCodec KeyCodec[K]) ValueCodec[K] { return keyToValueCodec[K]{keyCodec} }
128+
func KeyToValueCodec[K any](keyCodec KeyCodec[K]) NameableValueCodec[K] {
129+
return keyToValueCodec[K]{kc: keyCodec}
130+
}
129131

130132
// keyToValueCodec is a ValueCodec that wraps a KeyCodec to make it behave like a ValueCodec.
131133
type keyToValueCodec[K any] struct {
@@ -167,3 +169,7 @@ func (k keyToValueCodec[K]) Stringify(value K) string {
167169
func (k keyToValueCodec[K]) ValueType() string {
168170
return k.kc.KeyType()
169171
}
172+
173+
func (k keyToValueCodec[K]) WithName(name string) ValueCodec[K] {
174+
return NamedValueCodec[K]{ValueCodec: k, Name: name}
175+
}

collections/codec/codec_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
)
88

99
func TestUntypedValueCodec(t *testing.T) {
10-
vc := NewUntypedValueCodec(KeyToValueCodec(NewStringKeyCodec[string]()))
10+
vc := NewUntypedValueCodec(ValueCodec[string](KeyToValueCodec(KeyCodec[string](NewStringKeyCodec[string]()))))
1111

1212
t.Run("encode/decode", func(t *testing.T) {
1313
_, err := vc.Encode(0)

collections/codec/int.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"strconv"
88
)
99

10-
func NewInt64Key[T ~int64]() KeyCodec[T] { return int64Key[T]{} }
10+
func NewInt64Key[T ~int64]() NameableKeyCodec[T] { return int64Key[T]{} }
1111

1212
type int64Key[T ~int64] struct{}
1313

@@ -64,7 +64,11 @@ func (i int64Key[T]) SizeNonTerminal(_ T) int {
6464
return 8
6565
}
6666

67-
func NewInt32Key[T ~int32]() KeyCodec[T] {
67+
func (i int64Key[T]) WithName(name string) KeyCodec[T] {
68+
return NamedKeyCodec[T]{KeyCodec: i, Name: name}
69+
}
70+
71+
func NewInt32Key[T ~int32]() NameableKeyCodec[T] {
6872
return int32Key[T]{}
6973
}
7074

@@ -121,3 +125,7 @@ func (i int32Key[T]) DecodeNonTerminal(buffer []byte) (int, T, error) {
121125
func (i int32Key[T]) SizeNonTerminal(_ T) int {
122126
return 4
123127
}
128+
129+
func (i int32Key[T]) WithName(name string) KeyCodec[T] {
130+
return NamedKeyCodec[T]{KeyCodec: i, Name: name}
131+
}

collections/codec/naming.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package codec
2+
3+
import "fmt"
4+
5+
// NameableKeyCodec is a KeyCodec that can be named.
6+
type NameableKeyCodec[T any] interface {
7+
KeyCodec[T]
8+
9+
// WithName returns the KeyCodec with the provided name.
10+
WithName(name string) KeyCodec[T]
11+
}
12+
13+
// NameableValueCodec is a ValueCodec that can be named.
14+
type NameableValueCodec[T any] interface {
15+
ValueCodec[T]
16+
17+
// WithName returns the ValueCodec with the provided name.
18+
WithName(name string) ValueCodec[T]
19+
}
20+
21+
// NamedKeyCodec wraps a KeyCodec with a name.
22+
// The underlying key codec MUST have exactly one field in its schema.
23+
type NamedKeyCodec[T any] struct {
24+
KeyCodec[T]
25+
26+
// Name is the name of the KeyCodec in the schema.
27+
Name string
28+
}
29+
30+
// SchemaCodec returns the schema codec for the named key codec.
31+
func (n NamedKeyCodec[T]) SchemaCodec() (SchemaCodec[T], error) {
32+
cdc, err := KeySchemaCodec[T](n.KeyCodec)
33+
if err != nil {
34+
return SchemaCodec[T]{}, err
35+
}
36+
return withName(cdc, n.Name)
37+
}
38+
39+
// NamedValueCodec wraps a ValueCodec with a name.
40+
// The underlying value codec MUST have exactly one field in its schema.
41+
type NamedValueCodec[T any] struct {
42+
ValueCodec[T]
43+
44+
// Name is the name of the ValueCodec in the schema.
45+
Name string
46+
}
47+
48+
// SchemaCodec returns the schema codec for the named value codec.
49+
func (n NamedValueCodec[T]) SchemaCodec() (SchemaCodec[T], error) {
50+
cdc, err := ValueSchemaCodec[T](n.ValueCodec)
51+
if err != nil {
52+
return SchemaCodec[T]{}, err
53+
}
54+
return withName(cdc, n.Name)
55+
}
56+
57+
func withName[T any](cdc SchemaCodec[T], name string) (SchemaCodec[T], error) {
58+
if len(cdc.Fields) != 1 {
59+
return SchemaCodec[T]{}, fmt.Errorf("expected exactly one field to be named, got %d", len(cdc.Fields))
60+
}
61+
cdc.Fields[0].Name = name
62+
return cdc, nil
63+
}

collections/codec/string.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import (
66
"fmt"
77
)
88

9-
func NewStringKeyCodec[T ~string]() KeyCodec[T] { return stringKey[T]{} }
9+
func NewStringKeyCodec[T ~string]() NameableKeyCodec[T] { return stringKey[T]{} }
1010

1111
const (
1212
// StringDelimiter defines the delimiter of a string key when used in non-terminal encodings.
@@ -66,3 +66,7 @@ func (stringKey[T]) Stringify(key T) string {
6666
func (stringKey[T]) KeyType() string {
6767
return "string"
6868
}
69+
70+
func (s stringKey[T]) WithName(name string) KeyCodec[T] {
71+
return NamedKeyCodec[T]{KeyCodec: s, Name: name}
72+
}

collections/codec/uint.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"strconv"
88
)
99

10-
func NewUint64Key[T ~uint64]() KeyCodec[T] { return uint64Key[T]{} }
10+
func NewUint64Key[T ~uint64]() NameableKeyCodec[T] { return uint64Key[T]{} }
1111

1212
type uint64Key[T ~uint64] struct{}
1313

@@ -55,7 +55,11 @@ func (uint64Key[T]) KeyType() string {
5555
return "uint64"
5656
}
5757

58-
func NewUint32Key[T ~uint32]() KeyCodec[T] { return uint32Key[T]{} }
58+
func (u uint64Key[T]) WithName(name string) KeyCodec[T] {
59+
return NamedKeyCodec[T]{KeyCodec: u, Name: name}
60+
}
61+
62+
func NewUint32Key[T ~uint32]() NameableKeyCodec[T] { return uint32Key[T]{} }
5963

6064
type uint32Key[T ~uint32] struct{}
6165

@@ -95,7 +99,11 @@ func (u uint32Key[T]) DecodeNonTerminal(buffer []byte) (int, T, error) { return
9599

96100
func (uint32Key[T]) SizeNonTerminal(_ T) int { return 4 }
97101

98-
func NewUint16Key[T ~uint16]() KeyCodec[T] { return uint16Key[T]{} }
102+
func (u uint32Key[T]) WithName(name string) KeyCodec[T] {
103+
return NamedKeyCodec[T]{KeyCodec: u, Name: name}
104+
}
105+
106+
func NewUint16Key[T ~uint16]() NameableKeyCodec[T] { return uint16Key[T]{} }
99107

100108
type uint16Key[T ~uint16] struct{}
101109

@@ -135,6 +143,10 @@ func (u uint16Key[T]) DecodeNonTerminal(buffer []byte) (int, T, error) { return
135143

136144
func (u uint16Key[T]) SizeNonTerminal(key T) int { return u.Size(key) }
137145

146+
func (u uint16Key[T]) WithName(name string) KeyCodec[T] {
147+
return NamedKeyCodec[T]{KeyCodec: u, Name: name}
148+
}
149+
138150
func uintEncodeJSON(value uint64) ([]byte, error) {
139151
str := `"` + strconv.FormatUint(value, 10) + `"`
140152
return []byte(str), nil

collections/collections.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ import (
66
"io"
77
"math"
88

9-
"cosmossdk.io/collections/codec"
109
"cosmossdk.io/schema"
10+
11+
"cosmossdk.io/collections/codec"
1112
)
1213

1314
var (

0 commit comments

Comments
 (0)