@@ -49,14 +49,14 @@ func EncodeListToBuffer[T any](b EncoderBuffer, vals []T) error {
4949 })
5050}
5151
52- // EncodeStructFields encodes the ` required` and ` optional` slices to `w`,
52+ // EncodeStructFields encodes the required and optional slices to `w`,
5353// concatenated as a single list, as if they were fields in a struct. The
54- // optional "fields" are treated identically to those tagged with
55- // `rlp:"optional"`.
54+ // optional "fields", which MAY be nil, are treated identically to those tagged
55+ // with `rlp:"optional"`.
5656//
5757// See the example for [Stream.DecodeStructFields].
58- func EncodeStructFields (w io.Writer , required , optional []any ) error {
59- includeOptional , err := optionalFieldInclusionFlags ( optional )
58+ func EncodeStructFields (w io.Writer , required []any , opt * OptionalFields ) error {
59+ includeOptional , err := opt . inclusionFlags ( )
6060 if err != nil {
6161 return err
6262 }
@@ -69,7 +69,7 @@ func EncodeStructFields(w io.Writer, required, optional []any) error {
6969 }
7070 }
7171
72- for i , v := range optional {
72+ for i , v := range opt . vals () {
7373 if ! includeOptional [i ] {
7474 return nil
7575 }
@@ -85,22 +85,51 @@ func EncodeStructFields(w io.Writer, required, optional []any) error {
8585 return b .Flush ()
8686}
8787
88+ // Optional returns the `vals` as [OptionalFields]; see the type's documentation
89+ // for the resulting behaviour.
90+ func Optional (vals ... any ) * OptionalFields {
91+ return & OptionalFields {vals }
92+ }
93+
94+ // OptionalFields are treated by [EncodeStructFields] and
95+ // [Stream.DecodeStructFields] as if they were tagged with `rlp:"optional"`.
96+ type OptionalFields struct {
97+ // Note that the [OptionalFields] type exists primarily to improve
98+ // readability at the call sites of [EncodeStructFields] and
99+ // [Stream.DecodeStructFields]. While an `[]any` slice would suffice, it
100+ // results in ambiguous usage of field functionality.
101+
102+ v []any
103+ }
104+
105+ // vals is a convenience wrapper, returning o.v, but allowing for a nil
106+ // receiver, in which case it returns a nil slice.
107+ func (o * OptionalFields ) vals () []any {
108+ if o == nil {
109+ return nil
110+ }
111+ return o .v
112+ }
113+
88114var errUnsupportedOptionalFieldType = errors .New ("unsupported optional field type" )
89115
90- // optionalFieldInclusionFlags returns a slice of booleans, the same length as
91- // `vals`, indicating whether or not the respective optional value MUST be
92- // written to a list. A value must be written if it or any later value is
93- // non-nil; the returned slice is therefore monotonic non-increasing from true
94- // to false.
95- func optionalFieldInclusionFlags (vals []any ) ([]bool , error ) {
96- flags := make ([]bool , len (vals ))
116+ // inclusionFlags returns a slice of booleans, the same length as `fs`,
117+ // indicating whether or not the respective field MUST be written to a list. A
118+ // field must be written if it or any later field value is non-nil; the returned
119+ // slice is therefore monotonic non-increasing from true to false.
120+ func (o * OptionalFields ) inclusionFlags () ([]bool , error ) {
121+ if o == nil {
122+ return nil , nil
123+ }
124+
125+ flags := make ([]bool , len (o .v ))
97126 var include bool
98- for i := len (vals ) - 1 ; i >= 0 ; i -- {
99- switch v := reflect .ValueOf (vals [i ]); v .Kind () {
127+ for i := len (o . v ) - 1 ; i >= 0 ; i -- {
128+ switch v := reflect .ValueOf (o . v [i ]); v .Kind () {
100129 case reflect .Slice , reflect .Pointer :
101130 include = include || ! v .IsNil ()
102131 default :
103- return nil , fmt .Errorf ("%w: %T" , errUnsupportedOptionalFieldType , vals [i ])
132+ return nil , fmt .Errorf ("%w: %T" , errUnsupportedOptionalFieldType , o . v [i ])
104133 }
105134 flags [i ] = include
106135 }
@@ -142,20 +171,21 @@ func DecodeList[T any](s *Stream) ([]*T, error) {
142171}
143172
144173// DecodeStructFields is the inverse of [EncodeStructFields]. All destination
145- // fields, be they `required` or `optional`, MUST be pointers and all `optional`
146- // fields MUST be provided in case they are present in the RLP being decoded.
174+ // fields, be they required or optional, MUST be pointers and all optional
175+ // fields MUST be provided in case they are present in the RLP being decoded. If
176+ // no optional fields exist, the argument MAY be nil.
147177//
148178// Typically, the arguments to this function mirror those passed to
149179// [EncodeStructFields] except for being pointers. See the example.
150- func (s * Stream ) DecodeStructFields (required , optional []any ) error {
180+ func (s * Stream ) DecodeStructFields (required []any , opt * OptionalFields ) error {
151181 return s .FromList (func () error {
152182 for _ , v := range required {
153183 if err := s .Decode (v ); err != nil {
154184 return err
155185 }
156186 }
157187
158- for _ , v := range optional {
188+ for _ , v := range opt . vals () {
159189 if ! s .MoreDataInList () {
160190 return nil
161191 }
0 commit comments