Open sylr opened 2 months ago
Hi!
I just opened an issue #11 that describes a possible future of mus-go. I wonder what you think about it? Also there is a post on reddit.
In general it was done to write code like this:
func MarshalSliceProtobuf[T any](sl []T, m mus.Marshaller[T], bs []byte) (n int) {
for i := 0; i < len(sl); i++ {
n += m.Marshal(sl[i], bs[n:]) // instead of m.MarshalMUS(...)
}
return
}
I think Marshal/Unmarshal are too generic terms to be employed by any library in a language like Go that heavily relies on interfaces. encoding/json
uses MarshalJSON, gopkg.in/yaml.v3
uses MarshalYAML ... etc.
MarshalMUS/UnmarshalMUS made very much sense.
Sorry for the delay in response.
We can still write:
func MarshalFooMUS(foo Foo, bs []byte) (n int) {
...
}
And use it as, for example:
ord.MarshalSlice[Foo](sl, nil, mus.MarshallerFn[Foo](MarshalFooMUS), bs)
I don't understand the change to generic names for the mus.Unmarshaller/mus.Marshaller/mus.Sizes functions.
I mean, mus-go is already a more general thing and can be used to implement different formats, not only MUS.
I don't use functions, all my MUS usage is structured around methods that implements the former interfaces. Having to use Marshal
/Unmarshal
for only for MUS is not ideal, but, having to have to reserve Size
for MUS is not something I want.
// MessageMUS is a MUS marshaller for Message.
//
//nolint:all
type MessageMUS struct{}
var messageMUS = MessageMUS{}
var _ mus.Marshaller[Message] = (*MessageMUS)(nil)
var _ mus.Unmarshaller[Message] = (*MessageMUS)(nil)
var _ mus.Sizer[Message] = (*MessageMUS)(nil)
// MarshalMUS marshals struct mus encoded bytes.
func (MessageMUS) MarshalMUS(m Message, bs []byte) (n int) {
...
return n
}
// UnmarshalMUS unmarshals mus encoded bytes.
func (MessageMUS) UnmarshalMUS(bs []byte) (m2 Message, n int, err error) {
...
return m2, n, nil
}
// SizeMUS computes the length of MUS encoded bytes for the struct.
func (MessageMUS) SizeMUS(m Message) (n int) {
...
return n
}
Then I have my generic Mus Marshaller function for NATS.
// Marshaller is the interface that wraps the MusMarshaller and MusSizer methods.
type Marshaller[T any] interface {
mus.Marshaller[T]
mus.Sizer[T]
}
// MarshalMUS encodes a message to a NATS message.
func MarshalMUS[T Marshaller[T]](inputMsg, outputMsg *nats.Msg, v *T) error {
bs := make([]byte, (*v).SizeMUS(*v))
(*v).MarshalMUS(*v, bs)
...
return nil
}
Sorry for the inconvenience. As a temporary solution, I can suggest to define the Marshaller
interface as:
type Marshaller[T any] interface {
MarshalMUS(t T, bs []byte) (n int)
SizeMUS(t T) (size int)
}
, think this should work.
As a temporary solution, I can suggest to define the
Marshaller
interface as:
Actually, this is not a very good idea. It is better to use something like namespaces:
type Foo struct {
Int int
}
// First of all, let's define a general Serializer struct. It is simply an
// union of the Marshal, Unmarshal, and Size functions.
type Serializer[T any] struct {
Marshal mus.MarshallerFn[T]
Unmarshal mus.UnmarshallerFn[T]
Size mus.SizerFn[T]
}
// And a Foo serializer.
var FooSerializerMUS = Serializer[Foo]{
Marshal: func(foo Foo, bs []byte) (n int) {
return varint.MarshalInt(foo.Int, bs)
},
Unmarshal: func(bs []byte) (foo Foo, n int, err error) {
foo.Int, n, err = varint.UnmarshalInt(bs)
return
},
Size: func(foo Foo) (size int) {
return varint.SizeInt(foo.Int)
},
}
// Then a SerializableMUS interface, for structures that support serialization
// in the MUS format.
type SerializableMUS[T any] interface {
MUS() Serializer[T]
}
// And finally, add MUS support to Foo.
func (f Foo) MUS() Serializer[Foo] {
return FooSerializerMUS
}
// Now generic MarshalMUS function may look like this
func MarshalMUS[T SerializableMUS[T]](t T) (bs []byte){
var (
s = t.MUS()
bs = make([]byte, s.Size(t))
)
s.Marshal(t, bs)
return
}
// And could be used as - MarshalMUS(foo).
@sylr
Hi 👋
I don't understand the change to generic names for the mus.Unmarshaller/mus.Marshaller/mus.Sizes functions.
Could you please explain the motivation?
Regards.