Skip to content

Commit

Permalink
Merge pull request #5 from go-dew/simplify
Browse files Browse the repository at this point in the history
Simplify implementation
  • Loading branch information
yohamta authored May 23, 2024
2 parents 1049949 + b9c0965 commit a08a93a
Show file tree
Hide file tree
Showing 7 changed files with 40 additions and 332 deletions.
9 changes: 9 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,12 @@ test:

bench:
go test -bench=.

m:
rm -rf build/*.go
mkdir -p build
cgmerge
mv _merged.go build/ritsu.go
go fmt build/ritsu.go
sed -i '' -e 's/package main/package ritsu/' build/ritsu.go

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Dew is a command bus library for Go, designed to enhance developer experience an

## Features

- **Lightweight**: Clocks around 600 LOC with minimalistic design.
- **Lightweight**: Clocks around 450 LOC with minimalistic design.
- **Pragmatic and Ergonomic**: Focused on developer experience and productivity.
- **Production Ready**: 100% test coverage.
- **Zero Dependencies**: No external dependencies.
Expand Down
55 changes: 22 additions & 33 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ type CommandHandler[T Command] interface {
// NewAction creates an object that can be dispatched.
// It panics if the handler is not found.
func NewAction[T Action](bus Bus, cmd *T) CommandHandler[T] {
h, mx := resolveHandler[T](ACTION, bus)
h, mx := resolveHandler[T](bus)
return command[T]{
mux: mx,
cmd: cmd,
Expand All @@ -46,7 +46,7 @@ func NewAction[T Action](bus Bus, cmd *T) CommandHandler[T] {
// NewQuery creates an object that can be dispatched.
// It panics if the handler is not found.
func NewQuery[T QueryAction](bus Bus, cmd *T) CommandHandler[T] {
h, mx := resolveHandler[T](QUERY, bus)
h, mx := resolveHandler[T](bus)
return command[T]{
mux: mx,
cmd: cmd,
Expand All @@ -73,28 +73,9 @@ func (c command[T]) Mux() *Mux {
return c.mux
}

// resolveHandler locates a handler for a given operation type and command type within the provided Bus instance.
// It constructs a key from the command's reflect.Type, then searches the Mux's tree structure for a corresponding node.
//
// Parameters:
// - typ: The reflect.Type of the command for which a handler is being sought.
// - op: The operation type (ACTION or QUERY) under which the handler should be classified.
// - bus: The Bus instance where handlers are registered and organized.
//
// Returns:
// - *node: A pointer to the node struct representing the handler if found.
// - error: An error if no handler could be found for the provided type and operation.
//
// Example:
//
// handlerNode, err := resolveHandler(reflect.TypeOf(myCommand), ACTION, myBus)
// if err != nil {
// log.Fatalf("Handler resolution failed: %v", err)
// }
func convertInterface[T any](i any) T {
var v T
vp := unsafe.Pointer(&v)
reflect.NewAt(reflect.TypeOf(v), vp).Elem().Set(reflect.ValueOf(i))
reflect.NewAt(reflect.TypeOf(v), unsafe.Pointer(&v)).Elem().Set(reflect.ValueOf(i))
return v
}

Expand All @@ -104,10 +85,12 @@ type entry struct {
m *Mux
}

// storeCache stores the handler in the cache.
func storeCache[T Command](cache *syncMap, t reflect.Type, mx *Mux, handlerFunc HandlerFunc[T]) {
cache.Store(t, entry{t: t, m: mx, p: unsafe.Pointer(&handlerFunc)})
}

// loadHandlerCache loads the handler from the cache.
func loadHandlerCache[T Command](typ reflect.Type, mx *Mux) (HandlerFunc[T], *Mux, bool) {
if v, ok := mx.cache.Load(typ); ok {
e := v.(entry)
Expand All @@ -117,23 +100,29 @@ func loadHandlerCache[T Command](typ reflect.Type, mx *Mux) (HandlerFunc[T], *Mu
}

// resolveHandler returns the handler and mux for the given command.
func resolveHandler[T Command](op OpType, bus Bus) (HandlerFunc[T], *Mux) {
func resolveHandler[T Command](bus Bus) (HandlerFunc[T], *Mux) {
typ := typeFor[T]()
mx := bus.(*Mux)

handler, mxx, ok := loadHandlerCache[T](typ, mx)
h, mxx, ok := loadHandlerCache[T](typ, mx)
if ok {
return handler, mxx
return h, mxx
}

key := keyForType(typ)
n := mx.tree.findRoute(op, key)
if n != nil {
h := n.handler.handler
hh := convertInterface[HandlerFunc[T]](h.handler)
storeCache[T](mx.cache, typ, h.mux, hh)
return hh, h.mux
entry, ok := mx.entries.Load(typ)
if ok {
hh := entry.(*handler)
hhh := convertInterface[HandlerFunc[T]](hh.handler)
storeCache[T](mx.cache, typ, hh.mux, hhh)
return hhh, hh.mux
}

panic(fmt.Sprintf("handler not found for %s", key))
panic(fmt.Sprintf("handler not found for %s/%s", typ.PkgPath(), typ.String()))
}

type handler struct {
// handler is the function to call.
handler any
// mux is the mux that the handler belongs to.
mux *Mux
}
2 changes: 1 addition & 1 deletion docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Features
* - Feature
- Description
* - 🚀 Lightweight
- Clocks around 600 LOC with minimalistic design.
- Clocks around 450 LOC with minimalistic design.
* - 🌹 Pragmatic and Ergonomic
- Focused on developer experience and productivity.
* - ⚓️ Production Ready
Expand Down
19 changes: 7 additions & 12 deletions mux.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ type Mux struct {
parent *Mux
inline bool
lock sync.RWMutex
entries *sync.Map
handler [ALL]Middleware
tree *node
middlewares [mAll][]middleware
mHandlers [mAll]func(ctx Context, fn mHandlerFunc) error
cache *syncMap
Expand Down Expand Up @@ -64,7 +64,7 @@ type mHandlerFunc func(ctx Context) error

// newMux returns a newly initialized Mux object that implements the dispatcher interface.
func newMux() *Mux {
mux := &Mux{tree: &node{}, pool: &sync.Pool{}}
mux := &Mux{entries: &sync.Map{}, pool: &sync.Pool{}}
mux.pool.New = func() interface{} {
return NewContext()
}
Expand Down Expand Up @@ -228,7 +228,7 @@ func (mx *Mux) child() Bus {
parent: mx,
inline: true,
middlewares: mws,
tree: mx.tree,
entries: mx.entries,
cache: mx.cache,
}
}
Expand Down Expand Up @@ -289,9 +289,9 @@ func (mx *Mux) Register(handler any) {
if isHandlerMethod(mtdTyp) {
cmdTyp := mtdTyp.Type.In(2).Elem()
if cmdTyp.Implements(reflect.TypeOf((*Action)(nil)).Elem()) {
mx.addHandler(ACTION, cmdTyp, reflect.ValueOf(handler).Method(i).Interface())
mx.addHandler(cmdTyp, reflect.ValueOf(handler).Method(i).Interface())
} else if cmdTyp.Implements(reflect.TypeOf((*QueryAction)(nil)).Elem()) {
mx.addHandler(QUERY, cmdTyp, reflect.ValueOf(handler).Method(i).Interface())
mx.addHandler(cmdTyp, reflect.ValueOf(handler).Method(i).Interface())
}
}
}
Expand All @@ -310,8 +310,8 @@ func (mx *Mux) setupHandler() {
}
}

func (mx *Mux) addHandler(op OpType, t reflect.Type, h any) {
mx.tree.insert(op, keyForType(t), &handler{handler: h, mux: mx})
func (mx *Mux) addHandler(t reflect.Type, h any) {
mx.entries.Store(t, &handler{handler: h, mux: mx})
}

// isHandlerMethod checks if the method is a Executor method.
Expand Down Expand Up @@ -383,11 +383,6 @@ func filterMiddleware(op OpType, middlewares []middleware) []middleware {
return mws
}

// keyForType returns the key for the given type.
func keyForType(typ reflect.Type) string {
return fmt.Sprintf("%s:%s", typ.PkgPath(), typ.String())
}

// typeFor returns the reflect.Type for the given type.
func typeFor[T any]() reflect.Type {
var t T
Expand Down
Loading

0 comments on commit a08a93a

Please sign in to comment.