package hopp import "fmt" import "encoding" import "git.tebibyte.media/sashakoshka/hopp/tape" // Message is any object that is both a binary marshaler and unmarshaler. type Message interface { Method() uint16 encoding.BinaryMarshaler encoding.BinaryUnmarshaler } var _ Message = new(MessageData) // MessageData can hold the structure of any message. It can be used to alter // a protocol at runtime, transmit data with arbitrary keys, etc. Bear in mind // that is less performant than generating code because it has to make extra // memory allocations. type MessageData struct { Methd uint16 Pairs map[uint16] []byte } // Method returns the message's method field. func (this *MessageData) Method() uint16 { return this.Methd } // MarshalBinary implements the [encoding.BinaryMarshaler] interface. func (this *MessageData) MarshalBinary() ([]byte, error) { buffer, err := tape.EncodePairs(this.Pairs) if err != nil { return nil, fmt.Errorf("marshaling MessageData: %w", err) } return buffer, nil } // UnmarshalBinary implements the [encoding.BinaryUnmarshaler] interface. func (this *MessageData) UnmarshalBinary(buffer []byte) error { this.Pairs = make(map[uint16] []byte) pairs, err := tape.DecodePairs(buffer) if err != nil { return fmt.Errorf("unmarshaling MessageData: %w", err) } for key, value := range pairs { this.Pairs[key] = value } return nil } // Protocol maps methods to functions that create messages. The messages must be // passed by reference, and the functions must return a new object every time. type Protocol map[uint16] func() Message // Add adds messages to the protocol. Messages with conflicting methods will // be replaced. func (this Protocol) Add(messages ...func() Message) { for _, message := range messages { this[message().Method()] = message } }