-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
tlv: add new types and functions #8692
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -104,15 +104,15 @@ type OptionalRecordT[T TlvType, V any] struct { | |
// TlvType returns the type of the record. This is the value used to identify | ||
// this type on the wire. This value is bound to the specified TlvType type | ||
// param. | ||
func (t *OptionalRecordT[T, V]) TlvType() Type { | ||
func (o *OptionalRecordT[T, V]) TlvType() Type { | ||
zeroRecord := ZeroRecordT[T, V]() | ||
return zeroRecord.TlvType() | ||
} | ||
|
||
// WhenSomeV executes the given function if the optional record is present. | ||
// This operates on the inner most type, V, which is the value of the record. | ||
func (t *OptionalRecordT[T, V]) WhenSomeV(f func(V)) { | ||
t.Option.WhenSome(func(r RecordT[T, V]) { | ||
func (o *OptionalRecordT[T, V]) WhenSomeV(f func(V)) { | ||
o.Option.WhenSome(func(r RecordT[T, V]) { | ||
f(r.Val) | ||
}) | ||
} | ||
|
@@ -126,7 +126,7 @@ func (o *OptionalRecordT[T, V]) UnwrapOrFailV(t *testing.T) V { | |
return inner.Val | ||
} | ||
|
||
// UnwrapOrErr is used to extract a value from an option, if the option is | ||
// UnwrapOrErrV is used to extract a value from an option, if the option is | ||
// empty, then the specified error is returned directly. This gives the | ||
// underlying value of the record, instead of the record itself. | ||
func (o *OptionalRecordT[T, V]) UnwrapOrErrV(err error) (V, error) { | ||
|
@@ -141,10 +141,19 @@ func (o *OptionalRecordT[T, V]) UnwrapOrErrV(err error) (V, error) { | |
} | ||
|
||
// Zero returns a zero value of the record type. | ||
func (t *OptionalRecordT[T, V]) Zero() RecordT[T, V] { | ||
func (o *OptionalRecordT[T, V]) Zero() RecordT[T, V] { | ||
return ZeroRecordT[T, V]() | ||
} | ||
|
||
// ValOpt returns an Option of the underlying value. This can be used to chain | ||
// other option related methods to avoid needing to first go through the outer | ||
// record. | ||
func (o *OptionalRecordT[T, V]) ValOpt() fn.Option[V] { | ||
return fn.MapOption(func(record RecordT[T, V]) V { | ||
return record.Val | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pondering... This makes me wonder if we should extract out -- we have these
type OptionalRecord t v = Option (Record t v)
valOpt :: Option (Record t v) -> Option v
mapOption :: (a -> b) -> Option a -> Option b
-- if we had this
val :: Record t v -> v
-- then we could do this
valOpt :: OptionalRecord t v -> Option v
valOpt = mapOption val Porting this go to is tricky since there's no (to my knowledge) easy way for us to refer to a method without yet supplying its receiver argument. So defining something like this wouldn't fix the ergonomics: func (r RecordT[T, V]) Val() V { ... }
func (o *OptionalRecordT[T, V]) ValOpt() fn.Option[V] {
return fn.MapOption(RecordT.Val)(*o) // no sir
}
// but we could do this
func Val[T, V any](r Record[T, V]) V { ... }
// and then do this
func (o *OptionalRecordT[T, V] ValOpt() fn.Option[V] {
return fn.MapOption(Val)(*o) // can do
} I say all this because if we have to create new names for things every time we want to map a function over some functor, then If you have ideas and/or opinions here, lmk. This code is fine, it just points to the fact that there's room for improvement on our functional patterns. |
||
})(o.Option) | ||
} | ||
|
||
// SomeRecordT creates a new OptionalRecordT type from a given RecordT type. | ||
func SomeRecordT[T TlvType, V any](record RecordT[T, V]) OptionalRecordT[T, V] { | ||
return OptionalRecordT[T, V]{ | ||
|
@@ -159,3 +168,30 @@ func ZeroRecordT[T TlvType, V any]() RecordT[T, V] { | |
Val: v, | ||
} | ||
} | ||
|
||
// BigSizeT is a high-order type that represents a TLV record that encodes an | ||
// integer as a BigSize value in the stream. | ||
type BigSizeT[T constraints.Integer] struct { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🫡 |
||
// We'll store the base value in the struct as a uin64, but then expose | ||
// a public method to cast to the specified type. | ||
v uint64 | ||
} | ||
|
||
// NewBigSizeT creates a new BigSizeT type from a given integer type. | ||
func NewBigSizeT[T constraints.Integer](val T) BigSizeT[T] { | ||
return BigSizeT[T]{ | ||
v: uint64(val), | ||
} | ||
} | ||
|
||
// Int returns the underlying integer value of the BigSize record. | ||
func (b BigSizeT[T]) Int() T { | ||
return T(b.v) | ||
} | ||
|
||
// Record returns the underlying record interface for the record type. | ||
func (b *BigSizeT[T]) Record() Record { | ||
// We use a zero value for the type here as this should be used with | ||
// the higher order RecordT type. | ||
return MakeBigSizeRecord(0, &b.v) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there are different semantics here that are being assumed, I'd recommend not making it a simple alias and actually make a newtype (dropping the =)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case using the type alias is useful, as then I can put it in a
tlv.RecordT
as a primitive type. Otherwise, you'd need the record method wrapper.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should just make RecordT a primitive type, no? I just want to stress that type aliases are not useful for anything other than giving the reader a hint on how something is used, it assures no correct api usage the way that a newtype wrapper does.