Refactor / extract the registry

Work in progress
This commit is contained in:
Mickael Remond 2019-06-03 12:40:42 +02:00 committed by Mickaël Rémond
parent b05efea81d
commit 836e723273
4 changed files with 100 additions and 51 deletions

16
iq.go
View file

@ -210,7 +210,7 @@ func (iq *IQ) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
if level <= 1 {
var elt interface{}
payloadType := tt.Name.Space + " " + tt.Name.Local
if payloadType := typeRegistry[payloadType]; payloadType != nil {
if payloadType := iqTypeRegistry[payloadType]; payloadType != nil {
val := reflect.New(payloadType)
elt = val.Interface()
} else {
@ -332,15 +332,9 @@ type DiscoItem struct {
Node string `xml:"node,attr,omitempty"`
}
// ============================================================================
// TODO: Make it configurable at to be able to easily add new XMPP extensions
// in separate modules
var typeRegistry = make(map[string]reflect.Type)
func init() {
typeRegistry["http://jabber.org/protocol/disco#info query"] = reflect.TypeOf(DiscoInfo{})
typeRegistry["http://jabber.org/protocol/disco#items query"] = reflect.TypeOf(DiscoItems{})
typeRegistry["urn:ietf:params:xml:ns:xmpp-bind bind"] = reflect.TypeOf(BindBind{})
typeRegistry["urn:xmpp:iot:control set"] = reflect.TypeOf(iot.ControlSet{})
iqTypeRegistry["http://jabber.org/protocol/disco#info query"] = reflect.TypeOf(DiscoInfo{})
iqTypeRegistry["http://jabber.org/protocol/disco#items query"] = reflect.TypeOf(DiscoItems{})
iqTypeRegistry["urn:ietf:params:xml:ns:xmpp-bind bind"] = reflect.TypeOf(BindBind{})
iqTypeRegistry["urn:xmpp:iot:control set"] = reflect.TypeOf(iot.ControlSet{})
}

View file

@ -3,7 +3,6 @@ package xmpp // import "gosrc.io/xmpp"
import (
"encoding/xml"
"fmt"
"reflect"
)
// ============================================================================
@ -87,21 +86,16 @@ func (msg *Message) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
switch tt := t.(type) {
case xml.StartElement:
var elt interface{}
elementType := tt.Name.Space
if extensionType := msgTypeRegistry[elementType]; extensionType != nil {
val := reflect.New(extensionType)
elt = val.Interface()
if msgExt, ok := elt.(MsgExtension); ok {
err = d.DecodeElement(elt, &tt)
if err != nil {
return err
}
msg.Extensions = append(msg.Extensions, msgExt)
if msgExt := typeRegistry.getmsgType(elementType); msgExt != nil {
// Decode message extension
err = d.DecodeElement(msgExt, &tt)
if err != nil {
return err
}
msg.Extensions = append(msg.Extensions, msgExt)
} else {
// Decode default message elements
// Decode standard message sub-elements
var err error
switch tt.Name.Local {
case "body":
@ -125,30 +119,3 @@ func (msg *Message) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
}
}
}
// ============================================================================
// Message extensions
// Provide ability to add support to XMPP extension tags on messages
type MsgExtension interface {
IsMsgExtension()
}
// XEP-0184
type Receipt struct {
// xmlns: urn:xmpp:receipts
XMLName xml.Name
Id string
}
func (*Receipt) IsMsgExtension() {}
// ============================================================================
// TODO: Make it configurable at to be able to easily add new XMPP extensions
// in separate modules
var msgTypeRegistry = make(map[string]reflect.Type)
func init() {
msgTypeRegistry["urn:xmpp:receipts"] = reflect.TypeOf(Receipt{})
}

23
msg_receipts.go Normal file
View file

@ -0,0 +1,23 @@
package xmpp
import "encoding/xml"
/*
Support for:
- XEP-0184 - Message Delivery Receipts: https://xmpp.org/extensions/xep-0184.html
*/
const (
NSReceipts = "urn:xmpp:receipts"
)
// XEP-0184 message receipt markers
type Receipt struct {
MsgExtension
XMLName xml.Name
Id string
}
func init() {
typeRegistry.RegisterMsgExt(NSReceipts, Receipt{})
}

65
registry.go Normal file
View file

@ -0,0 +1,65 @@
package xmpp
import (
"reflect"
"sync"
)
type MsgExtension interface{}
// The Registry for msg and IQ types is a global variable.
// TODO: Move to the client init process to remove the dependency on a global variable.
// That should make it possible to be able to share the decoder.
// TODO: Ensure that a client can add its own custom namespace to the registry (or overload existing ones).
var typeRegistry = newRegistry()
type namespace = string
type registry struct {
// Key is namespace of message extension
msgTypes map[namespace]reflect.Type
msgTypesLock *sync.RWMutex
iqTypes map[namespace]reflect.Type
}
func newRegistry() registry {
return registry{
msgTypes: make(map[namespace]reflect.Type),
msgTypesLock: &sync.RWMutex{},
iqTypes: make(map[namespace]reflect.Type),
}
}
// Mutexes are not needed when adding a Message or IQ extension in init function.
// However, forcing the use of the mutex protect the data structure against unexpected use
// of the registry by developers using the library.
func (r registry) RegisterMsgExt(namespace string, extension MsgExtension) {
r.msgTypesLock.Lock()
defer r.msgTypesLock.Unlock()
r.msgTypes[namespace] = reflect.TypeOf(extension)
}
func (r registry) getMsgExtType(namespace string) reflect.Type {
r.msgTypesLock.RLock()
defer r.msgTypesLock.RUnlock()
return r.msgTypes[namespace]
}
func (r registry) getmsgType(namespace string) MsgExtension {
if extensionType := r.getMsgExtType(namespace); extensionType != nil {
val := reflect.New(extensionType)
elt := val.Interface()
if msgExt, ok := elt.(MsgExtension); ok {
return msgExt
}
}
return nil
}
// Registry to support message extensions
//var msgTypeRegistry = make(map[string]reflect.Type)
// Registry to instantiate the right IQ payload element
// Key is namespace and key of the payload
var iqTypeRegistry = make(map[string]reflect.Type)