generated from maragudk/template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0925e03
commit 5e29579
Showing
5 changed files
with
133 additions
and
130 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,94 +1,95 @@ | ||
package clir | ||
|
||
// CommandRouter is a router for commands which itself satisfies [Command]. | ||
type CommandRouter struct { | ||
commands map[string]Command | ||
// Router for [Runner]-s which itself satisfies [Runner]. | ||
type Router struct { | ||
middlewares []Middleware | ||
patterns []string | ||
subRouters []*CommandRouter | ||
routers []*Router | ||
runners map[string]Runner | ||
} | ||
|
||
func NewCommandRouter() *CommandRouter { | ||
return &CommandRouter{ | ||
commands: map[string]Command{}, | ||
func NewRouter() *Router { | ||
return &Router{ | ||
runners: map[string]Runner{}, | ||
} | ||
} | ||
|
||
// Run satisfies [Command]. | ||
func (c *CommandRouter) Run(ctx Context) error { | ||
// Run satisfies [Runner]. | ||
func (r *Router) Run(ctx Context) error { | ||
if len(ctx.Args) == 0 { | ||
cmd, ok := c.commands[""] | ||
runner, ok := r.runners[""] | ||
if !ok { | ||
return ErrorNotFound | ||
return ErrorRouteNotFound | ||
} | ||
|
||
// Apply middlewares in reverse order, so that middlewares are applied in the order they were added. | ||
for i := len(c.middlewares) - 1; i >= 0; i-- { | ||
cmd = c.middlewares[i](cmd) | ||
for i := len(r.middlewares) - 1; i >= 0; i-- { | ||
runner = r.middlewares[i](runner) | ||
} | ||
|
||
return cmd.Run(ctx) | ||
return runner.Run(ctx) | ||
} | ||
|
||
for _, pattern := range c.patterns { | ||
for _, pattern := range r.patterns { | ||
if ctx.Args[0] == pattern { | ||
cmd := c.commands[pattern] | ||
runner := r.runners[pattern] | ||
ctx.Args = ctx.Args[1:] | ||
|
||
for i := len(c.middlewares) - 1; i >= 0; i-- { | ||
cmd = c.middlewares[i](cmd) | ||
for i := len(r.middlewares) - 1; i >= 0; i-- { | ||
runner = r.middlewares[i](runner) | ||
} | ||
|
||
return cmd.Run(ctx) | ||
return runner.Run(ctx) | ||
} | ||
} | ||
|
||
for _, r := range c.subRouters { | ||
if err := r.Run(ctx); err == nil { | ||
for _, router := range r.routers { | ||
if err := router.Run(ctx); err == nil { | ||
return err | ||
} | ||
} | ||
|
||
return ErrorNotFound | ||
return ErrorRouteNotFound | ||
} | ||
|
||
// Sub adds a subcommand to the router with the given pattern. | ||
func (c *CommandRouter) Sub(pattern string, cmd Command) { | ||
c.patterns = append(c.patterns, pattern) | ||
c.commands[pattern] = cmd | ||
// Route a [Runner] with the given pattern. | ||
func (r *Router) Route(pattern string, runner Runner) { | ||
r.patterns = append(r.patterns, pattern) | ||
r.runners[pattern] = runner | ||
} | ||
|
||
// SubFunc is a convenience method for adding a subcommand with a [CommandFunc]. | ||
// It's the same as calling [CommandRouter.Sub] with a [CommandFunc], but you don't have to wrap the function. | ||
func (c *CommandRouter) SubFunc(pattern string, cmd CommandFunc) { | ||
c.Sub(pattern, cmd) | ||
// RouteFunc is like [Router.Route], but with a [RunnerFunc]. | ||
func (r *Router) RouteFunc(pattern string, runner RunnerFunc) { | ||
r.Route(pattern, runner) | ||
} | ||
|
||
// SubGroup is a convenience method for adding a subcommand with a new router. | ||
func (c *CommandRouter) SubGroup(pattern string, cb func(r *CommandRouter)) { | ||
r := NewCommandRouter() | ||
cb(r) | ||
c.Sub(pattern, r) | ||
// Branch into a new [Router] with the given pattern. | ||
func (r *Router) Branch(pattern string, cb func(r *Router)) { | ||
newR := NewRouter() | ||
cb(newR) | ||
r.Route(pattern, newR) | ||
} | ||
|
||
// Group commands with a new router. | ||
// The middleware from the parent router is copied to the new router. | ||
func (c *CommandRouter) Group(cb func(r *CommandRouter)) { | ||
r := NewCommandRouter() | ||
r.middlewares = append(r.middlewares, c.middlewares...) | ||
cb(r) | ||
c.subRouters = append(c.subRouters, r) | ||
// Scope into a new [Router]. | ||
// The middleware from the parent router is copied to the new router, | ||
// but new middleware is only added to the new router, not the parent router. | ||
func (r *Router) Scope(cb func(r *Router)) { | ||
newR := NewRouter() | ||
newR.middlewares = append(newR.middlewares, r.middlewares...) | ||
cb(newR) | ||
r.routers = append(r.routers, newR) | ||
} | ||
|
||
type Middleware = func(next Command) Command | ||
// Middleware for [Router.Use]. | ||
type Middleware = func(next Runner) Runner | ||
|
||
// Use a middleware for all commands. If called on the root router, it will apply to all commands. | ||
// If called in a Group, it will apply to all commands in that group. | ||
func (c *CommandRouter) Use(middlewares ...Middleware) { | ||
if len(c.commands) > 0 { | ||
panic("cannot add middlewares after adding commands") | ||
// Use [Middleware] on the current branch of the [Router]. | ||
// If called in a [Scope], it will apply to all routes in that scope. | ||
func (r *Router) Use(middlewares ...Middleware) { | ||
if len(r.runners) > 0 { | ||
panic("cannot use middlewares after adding routes") | ||
} | ||
c.middlewares = append(c.middlewares, middlewares...) | ||
r.middlewares = append(r.middlewares, middlewares...) | ||
} | ||
|
||
var _ Command = (*CommandRouter)(nil) | ||
var _ Runner = (*Router)(nil) |
Oops, something went wrong.