func (m *Mux) GetHandler(rname string) *Match {
	var tokens []string
	subrname := rname
	pl := len(m.path)
	if pl > 0 {
		rl := len(rname)
		if pl == rl {
			if m.path != rname {
				return nil
			}
			subrname = ""
		} else {
			if pl > rl || (rname[0:pl] != m.path) || rname[pl] != '.' {
				return nil
			}
			subrname = rname[pl+1:]
		}
	}
	if len(subrname) == 0 {
		if m.root.hs == nil {
			return nil
		}
		return &Match{
			Handler:   m.root.hs.Handler,
			Listeners: m.root.listeners,
			Group:     m.root.hs.group.toString(rname, nil),
		}
	}
	tokens = make([]string, 0, 32)
	start := 0
	for i := 0; i < len(subrname); i++ {
		if subrname[i] == btsep {
			tokens = append(tokens, subrname[start:i])
			start = i + 1
		}
	}
	tokens = append(tokens, subrname[start:])
	var nm nodeMatch
	matchNode(m.root, tokens, 0, 0, &nm)
	if nm.n == nil || nm.n.hs == nil {
		return nil
	}
	return &Match{
		Handler:   nm.n.hs.Handler,
		Listeners: nm.n.listeners,
		Params:    nm.params,
		Group:     nm.n.hs.group.toString(rname, tokens[nm.mountIdx:]),
	}
}
GetHandler parses the resource name and gets the registered handler,
event listeners, path params, and group ID.
Returns the matching handler, or nil if not found.
package res
import (
	"errors"
	"fmt"
	"strings"
)
const (
	pmark = '$'
	pwild = '*'
	fwild = '>'
	btsep = '.'
)
const invalidPattern = "res: invalid pattern"
type Mux struct {
	path   string
	root   *node
	parent *Mux
	mountp string
	s      *Service  
}
type Event struct {
	Name string
	Resource Resource
	NewValues map[string]interface{}
	OldValues map[string]interface{}
	Value interface{}
	Idx int
	Data interface{}
	Payload interface{}
}
type Match struct {
	Handler   Handler
	Listeners []func(*Event)
	Params    map[string]string
	Group     string
}
type regHandler struct {
	Handler
	group group
}
type node struct {
	hs        *regHandler  
	params    []pathParam  
	nodes     map[string]*node
	param     *node
	wild      *node  
	mounted   bool
	listeners []func(*Event)
}
type pathParam struct {
	name string  
	idx  int     
}
type nodeMatch struct {
	n        *node
	params   map[string]string
	mountIdx int
}
func NewMux(path string) *Mux {
	if !isValidPath(path) {
		panic("res: invalid path")
	}
	return &Mux{
		path: path,
		root: &node{},
	}
}
func (m *Mux) Path() string {
	return m.path
}
func (m *Mux) FullPath() string {
	if m.parent == nil {
		return m.path
	}
	return mergePattern(mergePattern(m.parent.FullPath(), m.mountp), m.path)
}
func (m *Mux) Register(s *Service) {
	if m.parent != nil {
		panic("res: already mounted")
	}
	if m.s != nil {
		panic("res: already registered to a service")
	}
	m.s = s
	m.callOnRegister()
}
func (m *Mux) registeredService() *Service {
	if m.parent != nil {
		return m.parent.registeredService()
	}
	return m.s
}
func (m *Mux) callOnRegister() {
	s := m.registeredService()
	if s == nil {
		return
	}
	fp := m.FullPath()
	traverse(m.root, make([]string, 0, 32), 0, func(n *node, path []string, mountIdx int) {
		if n.hs != nil && n.hs.OnRegister != nil {
			n.hs.OnRegister(s, Pattern(mergePattern(fp, pathSliceToString(n, path, mountIdx))), n.hs.Handler)
		}
	})
}
func (m *Mux) Handle(pattern string, hf ...Option) {
	var h Handler
	for _, f := range hf {
		f.SetOption(&h)
	}
	m.AddHandler(pattern, h)
}
func (m *Mux) AddHandler(pattern string, hs Handler) {
	h := regHandler{
		Handler: hs,
		group:   parseGroup(hs.Group, pattern),
	}
	m.add(pattern, &h)
}
func (m *Mux) AddListener(pattern string, handler func(*Event)) {
	if handler == nil {
		panic("nil event handler")
	}
	n, params := m.fetch(pattern, nil)
	setAndValidateParams(n, params)
	n.listeners = append(n.listeners, handler)
}
func (m *Mux) Mount(path string, sub *Mux) {
	if !isValidPath(path) {
		panic("res: invalid path")
	}
	if sub.parent != nil {
		panic("res: already mounted")
	}
	if sub.s != nil {
		panic("res: already registered to a service")
	}
	spath := mergePattern(path, sub.path)
	if spath == "" {
		panic("res: attempting to mount to root")
	}
	n, _ := m.fetch(spath, sub.root)
	if n != sub.root {
		panic("res: attempting to mount to existing pattern: " + mergePattern(m.path, spath))
	}
	sub.mountp = path
	sub.root.mounted = true
	sub.parent = m
	sub.callOnRegister()
}
func (m *Mux) Route(subpath string, fn func(m *Mux)) *Mux {
	sub := NewMux("")
	if fn != nil {
		fn(sub)
	}
	m.Mount(subpath, sub)
	return sub
}
func (m *Mux) ValidateListeners() (err error) {
	var errs []string
	traverse(m.root, make([]string, 0, 32), 0, func(n *node, path []string, mountIdx int) {
		if n.hs == nil && n.listeners != nil {
			errs = append(errs, "no handler registered for pattern: "+mergePattern(m.FullPath(), pathSliceToString(n, path, mountIdx)))
		}
	})
	if err != nil {
		return errors.New(strings.Join(errs, "\n"))
	}
	return nil
}
func (m *Mux) add(pattern string, hs *regHandler) {
	if !Pattern(pattern).IsValid() {
		panic(invalidPattern)
	}
	n, params := m.fetch(pattern, nil)
	if n.hs != nil {
		panic("res: registration already done for pattern " + mergePattern(m.path, pattern))
	}
	setAndValidateParams(n, params)
	n.hs = hs
	for pattern, handler := range hs.Listeners {
		m.AddListener(pattern, handler)
	}
	if hs.OnRegister != nil {
		s := m.registeredService()
		if s != nil {
			hs.OnRegister(s, Pattern(mergePattern(m.FullPath(), pattern)), hs.Handler)
		}
	}
}
func (m *Mux) fetch(pattern string, mount *node) (*node, []pathParam) {
	tokens := splitPattern(pattern)
	var params []pathParam
	l := m.root
	var n *node
	var doMount bool
	var mountIdx int
	for i, t := range tokens {
		if mount != nil && i == len(tokens)-1 {
			doMount = true
		}
		if l.mounted {
			mountIdx = i
		}
		lt := len(t)
		if lt == 0 {
			panic(invalidPattern)
		}
		if t[0] == pmark || t[0] == pwild {
			if lt == 1 {
				panic(invalidPattern)
			}
			if t[0] == pmark {
				name := t[1:]
				for _, p := range params {
					if p.name == name {
						panic("res: placeholder " + t + " found multiple times in pattern: " + mergePattern(m.path, pattern))
					}
				}
				params = append(params, pathParam{name: name, idx: i - mountIdx})
			}
			if l.param == nil {
				if doMount {
					l.param = mount
				} else {
					l.param = &node{}
				}
			}
			n = l.param
		} else if t[0] == fwild {
			if lt > 1 || i < len(tokens)-1 {
				panic(invalidPattern)
			}
			if l.wild == nil {
				if doMount {
					panic("res: attempting to mount on full wildcard pattern: " + mergePattern(m.path, pattern))
				}
				l.wild = &node{}
			}
			n = l.wild
		} else {
			if l.nodes == nil {
				l.nodes = make(map[string]*node)
				if doMount {
					n = mount
				} else {
					n = &node{}
				}
				l.nodes[t] = n
			} else {
				n = l.nodes[t]
				if n == nil {
					if doMount {
						n = mount
					} else {
						n = &node{}
					}
					l.nodes[t] = n
				}
			}
		}
		l = n
	}
	return l, params
}