<commit_before>// Copyright 2015 Igor Dolzhikov. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Package router 0.6.1 provides fast HTTP request router.

The router matches incoming requests by the request method and the path.
If a handle is registered for this path and method, the router delegates the
request to defined handler.
The router package is useful to prepare a RESTful API for Go services.
It has JSON output, which bind automatically for relevant type of data.
The router has timer feature to display duration of request handling in the header

Simplest example (serve static route):

	package main

	import (
		"github.com/takama/router"
	)

	func Hello(c *router.Control) {
		c.Body("Hello")
	}

	func main() {
		r := router.New()
		r.GET("/hello", Hello)

		// Listen and serve on 0.0.0.0:8888
		r.Listen(":8888")
	}

Check it:

	curl -i http://localhost:8888/hello

Serve dynamic route with parameter:

	func main() {
		r := router.New()
		r.GET("/hello/:name", func(c *router.Control) {
			c.Code(200).Body("Hello " + c.Get(":name"))
		})

		// Listen and serve on 0.0.0.0:8888
		r.Listen(":8888")
	}

Checks JSON Content-Type automatically:

	// Data is helper to construct JSON
	type Data map[string]interface{}

	func main() {
		r := router.New()
		r.GET("/settings/database/:db", func(c *router.Control) {
			data := map[string]map[string]string{
				"Database settings": {
					"database": c.Get(":db"),
					"host":     "localhost",
					"port":     "3306",
				},
			}
			c.Code(200).Body(data)
		})
		// Listen and serve on 0.0.0.0:8888
		r.Listen(":8888")
	}

Custom handler with "Access-Control-Allow" options and compact JSON:

	// Data is helper to construct JSON
	type Data map[string]interface{}

	func baseHandler(handle router.Handle) router.Handle {
		return func(c *router.Control) {
			c.CompactJSON(true)
			if origin := c.Request.Header.Get("Origin"); origin != "" {
				c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
				c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
			}
			handle(c)
		}
	}

	func Info(c *router.Control) {
		data := Data{
			"debug": true,
			"error": false,
		}
		c.Body(data)
	}

	func main() {
		r := router.New()
		r.CustomHandler = baseHandler
		r.GET("/info", Info)

		// Listen and serve on 0.0.0.0:8888
		r.Listen(":8888")
	}

Use google json style `https://google.github.io/styleguide/jsoncstyleguide.xml`

	func main() {
		r := router.New()
		r.GET("/api/v1/people/:action/:id", func(c *router.Control) {

			// Do something

			c.Method("people." + c.Get(":action"))
			c.SetParams(map[string]string{"userId": c.Get(":id")})
			c.SetError(http.StatusNotFound, "UserId not found")
			c.AddError(router.Error{Message: "Group or User not found"})
			c.Code(http.StatusNotFound).Body(nil)
		})
		// Listen and serve on 0.0.0.0:8888
		r.Listen(":8888")
	}

Go Router
*/
package router

import (
	"log"
	"net/http"
	"strings"
)

// Router represents a multiplexer for HTTP requests.
type Router struct {
	// List of handlers which accociated with known http methods (GET, POST ...)
	handlers map[string]*parser

	// NotFound is called when unknown HTTP method or a handler not found.
	// If it is not set, http.NotFound is used.
	// Please overwrite this if need your own NotFound handler.
	NotFound Handle

	// PanicHandler is called when panic happen.
	// The handler prevents your server from crashing and should be used to return
	// http status code http.StatusInternalServerError (500)
	PanicHandler Handle

	// CustomHandler is called allways if defined
	CustomHandler func(Handle) Handle

	// Logger activates logging user function for each requests
	Logger Handle
}

// Handle type is aliased to type of handler function.
type Handle func(*Control)

// Route type contains information about HTTP method and path
type Route struct {
	Method string
	Path   string
}

// New it returns a new multiplexer (Router).
func New() *Router {
	return &Router{handlers: make(map[string]*parser)}
}

// GET is a shortcut for Router Handle("GET", path, handle)
func (r *Router) GET(path string, h Handle) {
	r.Handle("GET", path, h)
}

// POST is a shortcut for Router Handle("POST", path, handle)
func (r *Router) POST(path string, h Handle) {
	r.Handle("POST", path, h)
}

// PUT is a shortcut for Router Handle("PUT", path, handle)
func (r *Router) PUT(path string, h Handle) {
	r.Handle("PUT", path, h)
}

// DELETE is a shortcut for Router Handle("DELETE", path, handle)
func (r *Router) DELETE(path string, h Handle) {
	r.Handle("DELETE", path, h)
}

// HEAD is a shortcut for Router Handle("HEAD", path, handle)
func (r *Router) HEAD(path string, h Handle) {
	r.Handle("HEAD", path, h)
}

// OPTIONS is a shortcut for Router Handle("OPTIONS", path, handle)
func (r *Router) OPTIONS(path string, h Handle) {
	r.Handle("OPTIONS", path, h)
}

// PATCH is a shortcut for router.Handle("PATCH", path, handle)
func (r *Router) PATCH(path string, handle Handle) {
	r.Handle("PATCH", path, handle)
}

// Handle registers a new request handle with the given path and method.
func (r *Router) Handle(method, path string, h Handle) {
	if r.handlers[method] == nil {
		r.handlers[method] = newParser()
	}
	r.handlers[method].register(path, h)
}

// Handler allows the usage of an http.Handler as a request handle.
func (r *Router) Handler(method, path string, handler http.Handler) {
	r.Handle(method, path,
		func(c *Control) {
			handler.ServeHTTP(c.Writer, c.Request)
		},
	)
}

// HandlerFunc allows the usage of an http.HandlerFunc as a request handle.
func (r *Router) HandlerFunc(method, path string, handler http.HandlerFunc) {
	r.Handle(method, path,
		func(c *Control) {
			handler(c.Writer, c.Request)
		},
	)
}

// Lookup returns handler and URL parameters that associated with path.
func (r *Router) Lookup(method, path string) (Handle, []Param, bool) {
	if parser := r.handlers[method]; parser != nil {
		return parser.get(path)
	}
	return nil, nil, false
}

// AllowedMethods returns list of allowed methods
func (r *Router) AllowedMethods(path string) []string {
	var allowed []string
	for method, parser := range r.handlers {
		if _, _, ok := parser.get(path); ok {
			allowed = append(allowed, method)
		}
	}

	return allowed
}

// Listen and serve on requested host and port.
func (r *Router) Listen(hostPort string) {
	if err := http.ListenAndServe(hostPort, r); err != nil {
		log.Fatal(err)
	}
}

// ServeHTTP implements http.Handler interface.
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	defer func() {
		if recovery := recover(); recovery != nil {
			if r.PanicHandler != nil {
				c := &Control{Request: req, Writer: w}
				r.PanicHandler(c)
			} else {
				log.Println("Recovered in handler:", req.Method, req.URL.Path)
			}
		}
	}()
	if r.Logger != nil {
		c := &Control{Request: req, Writer: w}
		r.Logger(c)
	}
	if _, ok := r.handlers[req.Method]; ok {
		if handle, params, ok := r.handlers[req.Method].get(req.URL.Path); ok {
			c := &Control{Request: req, Writer: w}
			if len(params) > 0 {
				c.params = append(c.params, params...)
			}
			if r.CustomHandler != nil {
				r.CustomHandler(handle)(c)
			} else {
				handle(c)
			}
			return
		}
	}
	allowed := r.AllowedMethods(req.URL.Path)

	if len(allowed) == 0 {
		if r.NotFound != nil {
			c := &Control{Request: req, Writer: w}
			r.NotFound(c)
		} else {
			http.NotFound(w, req)
		}
		return
	}

	w.Header().Add("Allow", strings.Join(allowed, ", "))
	http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
}

// Routes returns list of registered HTTP methods with path
func (r *Router) Routes() []Route {
	var rs []Route
	for method, parser := range r.handlers {
		for _, path := range parser.routes() {
			rs = append(rs, Route{Method: method, Path: path})
		}
	}

	return rs
}
<commit_msg>Bumped version number to 0.6.2<commit_after>// Copyright 2015 Igor Dolzhikov. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Package router 0.6.2 provides fast HTTP request router.

The router matches incoming requests by the request method and the path.
If a handle is registered for this path and method, the router delegates the
request to defined handler.
The router package is useful to prepare a RESTful API for Go services.
It has JSON output, which bind automatically for relevant type of data.
The router has timer feature to display duration of request handling in the header

Simplest example (serve static route):

	package main

	import (
		"github.com/takama/router"
	)

	func Hello(c *router.Control) {
		c.Body("Hello")
	}

	func main() {
		r := router.New()
		r.GET("/hello", Hello)

		// Listen and serve on 0.0.0.0:8888
		r.Listen(":8888")
	}

Check it:

	curl -i http://localhost:8888/hello

Serve dynamic route with parameter:

	func main() {
		r := router.New()
		r.GET("/hello/:name", func(c *router.Control) {
			c.Code(200).Body("Hello " + c.Get(":name"))
		})

		// Listen and serve on 0.0.0.0:8888
		r.Listen(":8888")
	}

Checks JSON Content-Type automatically:

	// Data is helper to construct JSON
	type Data map[string]interface{}

	func main() {
		r := router.New()
		r.GET("/settings/database/:db", func(c *router.Control) {
			data := map[string]map[string]string{
				"Database settings": {
					"database": c.Get(":db"),
					"host":     "localhost",
					"port":     "3306",
				},
			}
			c.Code(200).Body(data)
		})
		// Listen and serve on 0.0.0.0:8888
		r.Listen(":8888")
	}

Custom handler with "Access-Control-Allow" options and compact JSON:

	// Data is helper to construct JSON
	type Data map[string]interface{}

	func baseHandler(handle router.Handle) router.Handle {
		return func(c *router.Control) {
			c.CompactJSON(true)
			if origin := c.Request.Header.Get("Origin"); origin != "" {
				c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
				c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
			}
			handle(c)
		}
	}

	func Info(c *router.Control) {
		data := Data{
			"debug": true,
			"error": false,
		}
		c.Body(data)
	}

	func main() {
		r := router.New()
		r.CustomHandler = baseHandler
		r.GET("/info", Info)

		// Listen and serve on 0.0.0.0:8888
		r.Listen(":8888")
	}

Use google json style `https://google.github.io/styleguide/jsoncstyleguide.xml`

	func main() {
		r := router.New()
		r.GET("/api/v1/people/:action/:id", func(c *router.Control) {

			// Do something

			c.Method("people." + c.Get(":action"))
			c.SetParams(map[string]string{"userId": c.Get(":id")})
			c.SetError(http.StatusNotFound, "UserId not found")
			c.AddError(router.Error{Message: "Group or User not found"})
			c.Code(http.StatusNotFound).Body(nil)
		})
		// Listen and serve on 0.0.0.0:8888
		r.Listen(":8888")
	}

Go Router
*/
package router

import (
	"log"
	"net/http"
	"strings"
)

// Router represents a multiplexer for HTTP requests.
type Router struct {
	// List of handlers which accociated with known http methods (GET, POST ...)
	handlers map[string]*parser

	// NotFound is called when unknown HTTP method or a handler not found.
	// If it is not set, http.NotFound is used.
	// Please overwrite this if need your own NotFound handler.
	NotFound Handle

	// PanicHandler is called when panic happen.
	// The handler prevents your server from crashing and should be used to return
	// http status code http.StatusInternalServerError (500)
	PanicHandler Handle

	// CustomHandler is called allways if defined
	CustomHandler func(Handle) Handle

	// Logger activates logging user function for each requests
	Logger Handle
}

// Handle type is aliased to type of handler function.
type Handle func(*Control)

// Route type contains information about HTTP method and path
type Route struct {
	Method string
	Path   string
}

// New it returns a new multiplexer (Router).
func New() *Router {
	return &Router{handlers: make(map[string]*parser)}
}

// GET is a shortcut for Router Handle("GET", path, handle)
func (r *Router) GET(path string, h Handle) {
	r.Handle("GET", path, h)
}

// POST is a shortcut for Router Handle("POST", path, handle)
func (r *Router) POST(path string, h Handle) {
	r.Handle("POST", path, h)
}

// PUT is a shortcut for Router Handle("PUT", path, handle)
func (r *Router) PUT(path string, h Handle) {
	r.Handle("PUT", path, h)
}

// DELETE is a shortcut for Router Handle("DELETE", path, handle)
func (r *Router) DELETE(path string, h Handle) {
	r.Handle("DELETE", path, h)
}

// HEAD is a shortcut for Router Handle("HEAD", path, handle)
func (r *Router) HEAD(path string, h Handle) {
	r.Handle("HEAD", path, h)
}

// OPTIONS is a shortcut for Router Handle("OPTIONS", path, handle)
func (r *Router) OPTIONS(path string, h Handle) {
	r.Handle("OPTIONS", path, h)
}

// PATCH is a shortcut for router.Handle("PATCH", path, handle)
func (r *Router) PATCH(path string, handle Handle) {
	r.Handle("PATCH", path, handle)
}

// Handle registers a new request handle with the given path and method.
func (r *Router) Handle(method, path string, h Handle) {
	if r.handlers[method] == nil {
		r.handlers[method] = newParser()
	}
	r.handlers[method].register(path, h)
}

// Handler allows the usage of an http.Handler as a request handle.
func (r *Router) Handler(method, path string, handler http.Handler) {
	r.Handle(method, path,
		func(c *Control) {
			handler.ServeHTTP(c.Writer, c.Request)
		},
	)
}

// HandlerFunc allows the usage of an http.HandlerFunc as a request handle.
func (r *Router) HandlerFunc(method, path string, handler http.HandlerFunc) {
	r.Handle(method, path,
		func(c *Control) {
			handler(c.Writer, c.Request)
		},
	)
}

// Lookup returns handler and URL parameters that associated with path.
func (r *Router) Lookup(method, path string) (Handle, []Param, bool) {
	if parser := r.handlers[method]; parser != nil {
		return parser.get(path)
	}
	return nil, nil, false
}

// AllowedMethods returns list of allowed methods
func (r *Router) AllowedMethods(path string) []string {
	var allowed []string
	for method, parser := range r.handlers {
		if _, _, ok := parser.get(path); ok {
			allowed = append(allowed, method)
		}
	}

	return allowed
}

// Listen and serve on requested host and port.
func (r *Router) Listen(hostPort string) {
	if err := http.ListenAndServe(hostPort, r); err != nil {
		log.Fatal(err)
	}
}

// ServeHTTP implements http.Handler interface.
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	defer func() {
		if recovery := recover(); recovery != nil {
			if r.PanicHandler != nil {
				c := &Control{Request: req, Writer: w}
				r.PanicHandler(c)
			} else {
				log.Println("Recovered in handler:", req.Method, req.URL.Path)
			}
		}
	}()
	if r.Logger != nil {
		c := &Control{Request: req, Writer: w}
		r.Logger(c)
	}
	if _, ok := r.handlers[req.Method]; ok {
		if handle, params, ok := r.handlers[req.Method].get(req.URL.Path); ok {
			c := &Control{Request: req, Writer: w}
			if len(params) > 0 {
				c.params = append(c.params, params...)
			}
			if r.CustomHandler != nil {
				r.CustomHandler(handle)(c)
			} else {
				handle(c)
			}
			return
		}
	}
	allowed := r.AllowedMethods(req.URL.Path)

	if len(allowed) == 0 {
		if r.NotFound != nil {
			c := &Control{Request: req, Writer: w}
			r.NotFound(c)
		} else {
			http.NotFound(w, req)
		}
		return
	}

	w.Header().Add("Allow", strings.Join(allowed, ", "))
	http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
}

// Routes returns list of registered HTTP methods with path
func (r *Router) Routes() []Route {
	var rs []Route
	for method, parser := range r.handlers {
		for _, path := range parser.routes() {
			rs = append(rs, Route{Method: method, Path: path})
		}
	}

	return rs
}
<|endoftext|>


<commit_before>package goxp

import (
	"fmt"
	"net/http"
	"reflect"
	"regxp"
	"strconv"
	"sync"
)

// Params is a map of name/value pairs for named routes. An instance of goxp.Params is available to be injected into any route handler.
type Params map[string]string

// Router is GoXp's de-facto interface. Supports HTTP verbs, stacked handlers, and dependency injection
// Idea is to use booster rather than injection
type Router interface {
	Routes

	// Group adds a group where related routes can be added.
	Group(string, func(Router), ...Handler)
	// Get adds a route for a HTTP GET request to the specified mathcing pattern.
	Get(string, ...Handler) Route
	// Patch adds a route for a HTTP PATCH request to the specified matching patten.
	Patch(string, ...Handler) Route
	// Post adds a route for a HTTP POST request to the specified matching pattern.
	Post(string, ...Handler) Route
	// Put adds a route for a HTTP Put request to the specified matching pattern.
	Put(string, ...Handler) Route
	// Delete adds a route for a HTTP DELETE request to the specified matching pattern.
	Delete(string, ...Handler) Route
	// Options adds a route for HTTP OPTIONS request to the specified matching pattern.
	Options(string, ...Handler) Route
	// Head adds a route for HTTP HEAD request to the specified matching pattern.
	Head(string, ...Handler) Route
	// Any adds a route for a HTTP method request to the specified matching pattern.
	Any(string, ...Handler) Route
	// AddRoute adds a route for a given HTTP method request to the specified matching pattern.
	AddRoute(string, string, ...Handler) Route

	// NotFound sets the handlers that are called when a no route matches a request. Throws a basic 404 by default.
	NotFound(...Handler)

	// Handle is the entry point for routing. This is used as a goxp.Handler
	Handle(http.ResponseWriter, *http.Request, Context)
}

type router struct {
	routes     []*route
	notFounds  []Handler
	groups     []group
	routesLock sync.RWMutex
}
<commit_msg>router.go +type group struct<commit_after>package goxp

import (
	"fmt"
	"net/http"
	"reflect"
	"regxp"
	"strconv"
	"sync"
)

// Params is a map of name/value pairs for named routes. An instance of goxp.Params is available to be injected into any route handler.
type Params map[string]string

// Router is GoXp's de-facto interface. Supports HTTP verbs, stacked handlers, and dependency injection
// Idea is to use booster rather than injection
type Router interface {
	Routes

	// Group adds a group where related routes can be added.
	Group(string, func(Router), ...Handler)
	// Get adds a route for a HTTP GET request to the specified mathcing pattern.
	Get(string, ...Handler) Route
	// Patch adds a route for a HTTP PATCH request to the specified matching patten.
	Patch(string, ...Handler) Route
	// Post adds a route for a HTTP POST request to the specified matching pattern.
	Post(string, ...Handler) Route
	// Put adds a route for a HTTP Put request to the specified matching pattern.
	Put(string, ...Handler) Route
	// Delete adds a route for a HTTP DELETE request to the specified matching pattern.
	Delete(string, ...Handler) Route
	// Options adds a route for HTTP OPTIONS request to the specified matching pattern.
	Options(string, ...Handler) Route
	// Head adds a route for HTTP HEAD request to the specified matching pattern.
	Head(string, ...Handler) Route
	// Any adds a route for a HTTP method request to the specified matching pattern.
	Any(string, ...Handler) Route
	// AddRoute adds a route for a given HTTP method request to the specified matching pattern.
	AddRoute(string, string, ...Handler) Route

	// NotFound sets the handlers that are called when a no route matches a request. Throws a basic 404 by default.
	NotFound(...Handler)

	// Handle is the entry point for routing. This is used as a goxp.Handler
	Handle(http.ResponseWriter, *http.Request, Context)
}

type router struct {
	routes     []*route
	notFounds  []Handler
	groups     []group
	routesLock sync.RWMutex
}

type group struct {
	pattern  string
	handlers []Handler
}
<|endoftext|>


<commit_before>package baa

import (
	"fmt"
	"sync"
)

const (
	// RouteMaxLength set length limit of route pattern
	routeMaxLength = 256
	// RouterParamMaxLength set length limit of route pattern param
	routerParamMaxLength = 32
)

const (
	// method key in routeMap
	GET int = iota
	POST
	PUT
	DELETE
	PATCH
	OPTIONS
	HEAD
	// RouteLength route table length
	RouteLength
)

// methods declare support HTTP method
var methods = map[string]bool{
	"GET":     true,
	"POST":    true,
	"PUT":     true,
	"DELETE":  true,
	"PATCH":   true,
	"OPTIONS": true,
	"HEAD":    true,
}

// methodKeys declare method key in routeMap
var methodKeys = map[string]int{
	"GET":     GET,
	"POST":    POST,
	"PUT":     PUT,
	"DELETE":  DELETE,
	"PATCH":   PATCH,
	"OPTIONS": OPTIONS,
	"HEAD":    HEAD,
}

// optimize ...
var _radix [routeMaxLength]byte
var _param [routerParamMaxLength]byte

// Router provlider router for baa
type Router struct {
	autoHead        bool
	mu              sync.RWMutex
	notFoundHandler HandlerFunc
	routeMap        [RouteLength]*Route
	routeNamedMap   map[string]string
	groups          []*group
}

// Route is a tree node
// route use radix tree
type Route struct {
	pattern  string
	hasParam bool
	parent   *Route
	router   *Router
	children []*Route
	handlers []HandlerFunc
}

// group route
type group struct {
	pattern  string
	handlers []HandlerFunc
}

// newRouter create a router instance
func newRouter() *Router {
	r := new(Router)
	for i := 0; i < len(r.routeMap); i++ {
		r.routeMap[i] = newRoute("/", nil, nil)
	}
	r.routeNamedMap = make(map[string]string)
	r.groups = make([]*group, 0)
	return r
}

// newRoute create a route item
func newRoute(pattern string, handles []HandlerFunc, router *Router) *Route {
	r := new(Route)
	r.pattern = pattern
	r.handlers = handles
	r.router = router
	r.children = make([]*Route, 0)
	return r
}

// newGroup create a group router
func newGroup() *group {
	g := new(group)
	g.handlers = make([]HandlerFunc, 0)
	return g
}

// Match match the uri for handler
func (r *Router) match(method, uri string, c *Context) *Route {
	return r.lookup(uri, r.routeMap[methodKeys[method]], c)
}

// urlFor use named route return format url
func (r *Router) urlFor(name string, args ...interface{}) string {
	if name == "" {
		return ""
	}
	url := r.routeNamedMap[name]
	if url == "" {
		return ""
	}
	return fmt.Sprintf(url, args...)
}

// groupAdd add a group route has same prefix and handle chain
func (r *Router) groupAdd(pattern string, f func(), handlers []HandlerFunc) {
	g := newGroup()
	g.pattern = pattern
	g.handlers = handlers
	r.groups = append(r.groups, g)

	f()

	r.groups = r.groups[:len(r.groups)-1]
}

// Handle registers a new request handle with the given pattern, method and handlers.
func (r *Router) add(method string, pattern string, handlers []HandlerFunc) *Route {
	if _, ok := methods[method]; !ok {
		panic("unsupport http method [" + method + "]")
	}
	if pattern == "" {
		panic("route pattern can not be emtpy!")
	}
	if pattern[0] != '/' {
		panic("route pattern must begin /")
	}
	if len(pattern) > routeMaxLength {
		panic(fmt.Sprintf("route pattern max length limit %d", routeMaxLength))
	}
	r.mu.Lock()
	defer r.mu.Unlock()

	// check group set
	if len(r.groups) > 0 {
		var gpattern string
		var ghandlers []HandlerFunc
		for i := range r.groups {
			gpattern += r.groups[i].pattern
			if len(r.groups[i].handlers) > 0 {
				ghandlers = append(ghandlers, r.groups[i].handlers...)
			}
		}
		pattern = gpattern + pattern
		ghandlers = append(ghandlers, handlers...)
		handlers = ghandlers
	}

	for i := 0; i < len(handlers); i++ {
		handlers[i] = wrapHandlerFunc(handlers[i])
	}

	root := r.routeMap[methodKeys[method]]
	radix := _radix[:0]
	var i, k int
	var tru *Route
	for i = 0; i < len(pattern); i++ {
		//param route
		if pattern[i] == ':' {
			// clear static route
			if len(radix) > 0 {
				root = r.insert(root, newRoute(string(radix), nil, nil))
				radix = _radix[:0]
			}
			// set param route
			param := _param[:0]
			k = 0
			for i = i + 1; i < len(pattern); i++ {
				if !isParamChar(pattern[i]) {
					i--
					break
				}
				param = append(param, pattern[i])
				k++
			}
			if k == 0 {
				panic("route pattern param is empty")
			}
			if k > routerParamMaxLength {
				panic(fmt.Sprintf("route pattern param max length limit %d", routerParamMaxLength))
			}
			// check last character
			p := ":" + string(param[:k])
			if i == len(pattern) {
				tru = newRoute(p, handlers, r)
			} else {
				tru = newRoute(p, nil, nil)
			}
			tru.hasParam = true
			root = r.insert(root, tru)
			continue
		}
		radix = append(radix, pattern[i])
	}

	// static route
	if len(radix) > 0 {
		tru = newRoute(string(radix), handlers, r)
		r.insert(root, tru)
	}

	return newRoute(pattern, handlers, r)
}

// insert build the route tree
func (r *Router) insert(root *Route, node *Route) *Route {
	// same route
	if root.pattern == node.pattern {
		if node.handlers != nil {
			root.handlers = node.handlers
		}
		return root
	}

	// param route
	if root.hasParam && node.hasParam {
		if root.parent == nil {
			panic("Router.insert error route has no parent")
		}
		return root.parent.insertChild(node)
	}

	if !root.hasParam && node.hasParam {
		return root.insertChild(node)
	}

	var i int
	if root.hasParam && !node.hasParam {
		for i = range root.children {
			if root.children[i].pattern == node.pattern {
				if node.handlers != nil {
					root.children[i].handlers = node.handlers
				}
				return root.children[i]
			}
			if root.children[i].hasPrefix(node) > 0 {
				return r.insert(root.children[i], node)
			}
		}

		root.insertChild(node)
		return node
	}

	// find radix
	pos := root.hasPrefix(node)
	if pos == 0 {
		panic("Router.insert error root[" + root.pattern + "] and node[" + node.pattern + "] not have both prefix")
	}
	if pos == len(node.pattern) {
		root.parent.deleteChild(root)
		root.parent.insertChild(node)
		root.pattern = root.pattern[pos:]
		node.insertChild(root)
		return node
	}

	node.pattern = node.pattern[pos:]
	if pos == len(root.pattern) {
		for i = range root.children {
			if root.children[i].pattern == node.pattern {
				if node.handlers != nil {
					root.children[i].handlers = node.handlers
				}
				return root.children[i]
			}
			if root.children[i].hasPrefix(node) > 0 {
				return r.insert(root.children[i], node)
			}
		}

		root.insertChild(node)
		return node
	}

	// _parent root and ru has new parent
	_parent := newRoute(root.pattern[:pos], nil, r)
	root.parent.deleteChild(root)
	root.parent.insertChild(_parent)
	root.pattern = root.pattern[pos:]
	_parent.insertChild(root)
	_parent.insertChild(node)

	return node
}

func (r *Router) lookup(pattern string, root *Route, c *Context) *Route {
	var ru *Route
	var i int
	// static route
	if !root.hasParam {
		if pattern == root.pattern {
			return root
		}
		if len(pattern) >= len(root.pattern) && pattern[0:len(root.pattern)] == root.pattern {
			pattern = pattern[len(root.pattern):]
		} else {
			return nil
		}
	} else {
		if len(root.children) == 0 {
			i = len(pattern)
		} else {
			for i = 0; i < len(pattern) && isParamChar(pattern[i]); i++ {
			}
		}
		c.SetParam(root.pattern[1:], pattern[:i])
		if i == len(pattern) {
			return root
		}
		pattern = pattern[i:]
	}
	if len(root.children) == 0 {
		return nil
	}

	// first, static route
	for i = range root.children {
		if ru = r.lookup(pattern, root.children[i], c); ru != nil {
			if ru.handlers != nil {
				return ru
			}
		}
	}

	return nil
}

// print the route map
func (r *Router) print(prefix string, root *Route) {
	if root == nil {
		for m := range r.routeMap {
			fmt.Println(m)
			r.print("", r.routeMap[m])
		}
		return
	}
	fmt.Println(prefix + " -> " + root.pattern)
	for i := range root.children {
		r.print(prefix+" -> "+root.pattern, root.children[i])
	}
}

// Name set name of route
func (r *Route) Name(name string) {
	if name == "" {
		return
	}
	p := make([]byte, 0, len(r.pattern))
	for i := 0; i < len(r.pattern); i++ {
		if r.pattern[i] != ':' {
			p = append(p, r.pattern[i])
			continue
		}
		p = append(p, '%')
		p = append(p, 'v')
		for i = i + 1; i < len(r.pattern); i++ {
			if !isParamChar(r.pattern[i]) {
				i--
				break
			}
		}
	}
	r.router.routeNamedMap[name] = string(p)
}

// deleteChild find child and delete from root route
func (r *Route) deleteChild(child *Route) {
	for i := 0; i < len(r.children); i++ {
		if r.children[i].pattern != child.pattern {
			continue
		}
		if len(r.children) == 1 {
			r.children = r.children[:0]
			return
		}
		if i == 0 {
			r.children = r.children[1:]
			return
		}
		if i+1 == len(r.children) {
			r.children = r.children[:i]
			return
		}
		r.children = append(r.children[:i], r.children[i+1:]...)
		return
	}
}

// insertChild insert child into root route, and returns the child route
func (r *Route) insertChild(child *Route) *Route {
	for i := 0; i < len(r.children); i++ {
		if r.children[i].pattern == child.pattern {
			if child.handlers != nil {
				r.children[i].handlers = child.handlers
			}
			return r.children[i]
		}
	}
	child.parent = r
	r.children = append(r.children, child)
	return child
}

// hasChild check root has child, if yes return child route, or reutrn nil
func (r *Route) hasChild(child *Route) *Route {
	for i := 0; i < len(r.children); i++ {
		if r.children[i].pattern == child.pattern {
			return r.children[i]
		}
	}
	return nil
}

// hasPrefix returns the same prefix position, if none return 0
func (r *Route) hasPrefix(ru *Route) int {
	l := len(r.pattern)
	var i int
	for i = 0; i < len(ru.pattern) && i < l && ru.pattern[i] == r.pattern[i]; i++ {
	}
	return i
}

// wrapHandlerFunc wrap for context handler chain
func wrapHandlerFunc(h HandlerFunc) HandlerFunc {
	return func(c *Context) {
		h(c)
		c.Next()
	}
}

// isParamChar check the char can used for route params
// a-z->65:90, A-Z->97:122, 0-9->48->57, _->95, :->58
func isParamChar(c byte) bool {
	if (c >= 65 && c <= 90) || (c >= 97 && c <= 122) || (c >= 48 && c <= 58) || c == 95 {
		return true
	}
	return false
}
<commit_msg>optimize router<commit_after>package baa

import (
	"fmt"
	"sync"
)

const (
	// method key in routeMap
	GET int = iota
	POST
	PUT
	DELETE
	PATCH
	OPTIONS
	HEAD
	// RouteLength route table length
	RouteLength
)

// methods declare support HTTP method
var methods = map[string]bool{
	"GET":     true,
	"POST":    true,
	"PUT":     true,
	"DELETE":  true,
	"PATCH":   true,
	"OPTIONS": true,
	"HEAD":    true,
}

// methodKeys declare method key in routeMap
var methodKeys = map[string]int{
	"GET":     GET,
	"POST":    POST,
	"PUT":     PUT,
	"DELETE":  DELETE,
	"PATCH":   PATCH,
	"OPTIONS": OPTIONS,
	"HEAD":    HEAD,
}

// Router provlider router for baa
type Router struct {
	autoHead        bool
	mu              sync.RWMutex
	notFoundHandler HandlerFunc
	routeMap        [RouteLength]*Route
	routeNamedMap   map[string]string
	groups          []*group
}

// Route is a tree node
// route use radix tree
type Route struct {
	pattern  string
	hasParam bool
	parent   *Route
	router   *Router
	children []*Route
	handlers []HandlerFunc
}

// group route
type group struct {
	pattern  string
	handlers []HandlerFunc
}

// newRouter create a router instance
func newRouter() *Router {
	r := new(Router)
	for i := 0; i < len(r.routeMap); i++ {
		r.routeMap[i] = newRoute("/", nil, nil)
	}
	r.routeNamedMap = make(map[string]string)
	r.groups = make([]*group, 0)
	return r
}

// newRoute create a route item
func newRoute(pattern string, handles []HandlerFunc, router *Router) *Route {
	r := new(Route)
	r.pattern = pattern
	r.handlers = handles
	r.router = router
	r.children = make([]*Route, 0)
	return r
}

// newGroup create a group router
func newGroup() *group {
	g := new(group)
	g.handlers = make([]HandlerFunc, 0)
	return g
}

// Match match the uri for handler
func (r *Router) match(method, uri string, c *Context) *Route {
	return r.lookup(uri, r.routeMap[methodKeys[method]], c)
}

// urlFor use named route return format url
func (r *Router) urlFor(name string, args ...interface{}) string {
	if name == "" {
		return ""
	}
	url := r.routeNamedMap[name]
	if url == "" {
		return ""
	}
	return fmt.Sprintf(url, args...)
}

// groupAdd add a group route has same prefix and handle chain
func (r *Router) groupAdd(pattern string, f func(), handlers []HandlerFunc) {
	g := newGroup()
	g.pattern = pattern
	g.handlers = handlers
	r.groups = append(r.groups, g)

	f()

	r.groups = r.groups[:len(r.groups)-1]
}

// Handle registers a new request handle with the given pattern, method and handlers.
func (r *Router) add(method string, pattern string, handlers []HandlerFunc) *Route {
	if _, ok := methods[method]; !ok {
		panic("unsupport http method [" + method + "]")
	}
	if pattern == "" {
		panic("route pattern can not be emtpy!")
	}
	if pattern[0] != '/' {
		panic("route pattern must begin /")
	}
	r.mu.Lock()
	defer r.mu.Unlock()

	// check group set
	if len(r.groups) > 0 {
		var gpattern string
		var ghandlers []HandlerFunc
		for i := range r.groups {
			gpattern += r.groups[i].pattern
			if len(r.groups[i].handlers) > 0 {
				ghandlers = append(ghandlers, r.groups[i].handlers...)
			}
		}
		pattern = gpattern + pattern
		ghandlers = append(ghandlers, handlers...)
		handlers = ghandlers
	}

	for i := 0; i < len(handlers); i++ {
		handlers[i] = wrapHandlerFunc(handlers[i])
	}

	root := r.routeMap[methodKeys[method]]
	var radix []byte
	var param []byte
	var i, k int
	var tru *Route
	for i = 0; i < len(pattern); i++ {
		//param route
		if pattern[i] == ':' {
			// clear static route
			if len(radix) > 0 {
				root = r.insert(root, newRoute(string(radix), nil, nil))
				radix = radix[:0]
			}
			// set param route
			param = param[:0]
			k = 0
			for i = i + 1; i < len(pattern); i++ {
				if !isParamChar(pattern[i]) {
					i--
					break
				}
				param = append(param, pattern[i])
				k++
			}
			if k == 0 {
				panic("route pattern param is empty")
			}
			// check last character
			p := ":" + string(param[:k])
			if i == len(pattern) {
				tru = newRoute(p, handlers, r)
			} else {
				tru = newRoute(p, nil, nil)
			}
			tru.hasParam = true
			root = r.insert(root, tru)
			continue
		}
		radix = append(radix, pattern[i])
	}

	// static route
	if len(radix) > 0 {
		tru = newRoute(string(radix), handlers, r)
		r.insert(root, tru)
	}

	return newRoute(pattern, handlers, r)
}

// insert build the route tree
func (r *Router) insert(root *Route, node *Route) *Route {
	// same route
	if root.pattern == node.pattern {
		if node.handlers != nil {
			root.handlers = node.handlers
		}
		return root
	}

	// param route
	if root.hasParam && node.hasParam {
		if root.parent == nil {
			panic("Router.insert error route has no parent")
		}
		return root.parent.insertChild(node)
	}

	if !root.hasParam && node.hasParam {
		return root.insertChild(node)
	}

	var i int
	if root.hasParam && !node.hasParam {
		for i = range root.children {
			if root.children[i].pattern == node.pattern {
				if node.handlers != nil {
					root.children[i].handlers = node.handlers
				}
				return root.children[i]
			}
			if root.children[i].hasPrefix(node) > 0 {
				return r.insert(root.children[i], node)
			}
		}

		root.insertChild(node)
		return node
	}

	// find radix
	pos := root.hasPrefix(node)
	if pos == 0 {
		panic("Router.insert error root[" + root.pattern + "] and node[" + node.pattern + "] not have both prefix")
	}
	if pos == len(node.pattern) {
		root.parent.deleteChild(root)
		root.parent.insertChild(node)
		root.pattern = root.pattern[pos:]
		node.insertChild(root)
		return node
	}

	node.pattern = node.pattern[pos:]
	if pos == len(root.pattern) {
		for i = range root.children {
			if root.children[i].pattern == node.pattern {
				if node.handlers != nil {
					root.children[i].handlers = node.handlers
				}
				return root.children[i]
			}
			if root.children[i].hasPrefix(node) > 0 {
				return r.insert(root.children[i], node)
			}
		}

		root.insertChild(node)
		return node
	}

	// _parent root and ru has new parent
	_parent := newRoute(root.pattern[:pos], nil, r)
	root.parent.deleteChild(root)
	root.parent.insertChild(_parent)
	root.pattern = root.pattern[pos:]
	_parent.insertChild(root)
	_parent.insertChild(node)

	return node
}

func (r *Router) lookup(pattern string, root *Route, c *Context) *Route {
	var ru *Route
	var i int
	// static route
	if !root.hasParam {
		if pattern == root.pattern {
			return root
		}
		if len(pattern) >= len(root.pattern) && pattern[0:len(root.pattern)] == root.pattern {
			pattern = pattern[len(root.pattern):]
		} else {
			return nil
		}
	} else {
		if len(root.children) == 0 {
			i = len(pattern)
		} else {
			for i = 0; i < len(pattern) && isParamChar(pattern[i]); i++ {
			}
		}
		c.SetParam(root.pattern[1:], pattern[:i])
		if i == len(pattern) {
			return root
		}
		pattern = pattern[i:]
	}
	if len(root.children) == 0 {
		return nil
	}

	// first, static route
	for i = range root.children {
		if ru = r.lookup(pattern, root.children[i], c); ru != nil {
			if ru.handlers != nil {
				return ru
			}
		}
	}

	return nil
}

// print the route map
func (r *Router) print(prefix string, root *Route) {
	if root == nil {
		for m := range r.routeMap {
			fmt.Println(m)
			r.print("", r.routeMap[m])
		}
		return
	}
	fmt.Println(prefix + " -> " + root.pattern)
	for i := range root.children {
		r.print(prefix+" -> "+root.pattern, root.children[i])
	}
}

// Name set name of route
func (r *Route) Name(name string) {
	if name == "" {
		return
	}
	p := make([]byte, 0, len(r.pattern))
	for i := 0; i < len(r.pattern); i++ {
		if r.pattern[i] != ':' {
			p = append(p, r.pattern[i])
			continue
		}
		p = append(p, '%')
		p = append(p, 'v')
		for i = i + 1; i < len(r.pattern); i++ {
			if !isParamChar(r.pattern[i]) {
				i--
				break
			}
		}
	}
	r.router.routeNamedMap[name] = string(p)
}

// deleteChild find child and delete from root route
func (r *Route) deleteChild(child *Route) {
	for i := 0; i < len(r.children); i++ {
		if r.children[i].pattern != child.pattern {
			continue
		}
		if len(r.children) == 1 {
			r.children = r.children[:0]
			return
		}
		if i == 0 {
			r.children = r.children[1:]
			return
		}
		if i+1 == len(r.children) {
			r.children = r.children[:i]
			return
		}
		r.children = append(r.children[:i], r.children[i+1:]...)
		return
	}
}

// insertChild insert child into root route, and returns the child route
func (r *Route) insertChild(child *Route) *Route {
	for i := 0; i < len(r.children); i++ {
		if r.children[i].pattern == child.pattern {
			if child.handlers != nil {
				r.children[i].handlers = child.handlers
			}
			return r.children[i]
		}
	}
	child.parent = r
	r.children = append(r.children, child)
	return child
}

// hasChild check root has child, if yes return child route, or reutrn nil
func (r *Route) hasChild(child *Route) *Route {
	for i := 0; i < len(r.children); i++ {
		if r.children[i].pattern == child.pattern {
			return r.children[i]
		}
	}
	return nil
}

// hasPrefix returns the same prefix position, if none return 0
func (r *Route) hasPrefix(ru *Route) int {
	l := len(r.pattern)
	var i int
	for i = 0; i < len(ru.pattern) && i < l && ru.pattern[i] == r.pattern[i]; i++ {
	}
	return i
}

// wrapHandlerFunc wrap for context handler chain
func wrapHandlerFunc(h HandlerFunc) HandlerFunc {
	return func(c *Context) {
		h(c)
		c.Next()
	}
}

// isParamChar check the char can used for route params
// a-z->65:90, A-Z->97:122, 0-9->48->57, _->95, :->58
func isParamChar(c byte) bool {
	if (c >= 65 && c <= 90) || (c >= 97 && c <= 122) || (c >= 48 && c <= 58) || c == 95 {
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package echo

type (
	// Router is the registry of all registered routes for an `Echo` instance for
	// request matching and URL path parameter parsing.
	Router struct {
		tree   *node
		routes map[string]*Route
		echo   *Echo
	}
	node struct {
		kind          kind
		label         byte
		prefix        string
		parent        *node
		children      children
		ppath         string
		pnames        []string
		methodHandler *methodHandler
	}
	kind          uint8
	children      []*node
	methodHandler struct {
		connect  HandlerFunc
		delete   HandlerFunc
		get      HandlerFunc
		head     HandlerFunc
		options  HandlerFunc
		patch    HandlerFunc
		post     HandlerFunc
		propfind HandlerFunc
		put      HandlerFunc
		trace    HandlerFunc
	}
)

const (
	skind kind = iota
	pkind
	akind
)

// NewRouter returns a new Router instance.
func NewRouter(e *Echo) *Router {
	return &Router{
		tree: &node{
			methodHandler: new(methodHandler),
		},
		routes: map[string]*Route{},
		echo:   e,
	}
}

// Add registers a new route for method and path with matching handler.
func (r *Router) Add(method, path string, h HandlerFunc) {
	// Validate path
	if path == "" {
		panic("echo: path cannot be empty")
	}
	if path[0] != '/' {
		path = "/" + path
	}
	pnames := []string{} // Param names
	ppath := path        // Pristine path

	for i, l := 0, len(path); i < l; i++ {
		if path[i] == ':' {
			j := i + 1

			r.insert(method, path[:i], nil, skind, "", nil)
			for ; i < l && path[i] != '/'; i++ {
			}

			pnames = append(pnames, path[j:i])
			path = path[:j] + path[i:]
			i, l = j, len(path)

			if i == l {
				r.insert(method, path[:i], h, pkind, ppath, pnames)
				return
			}
			r.insert(method, path[:i], nil, pkind, ppath, pnames)
		} else if path[i] == '*' {
			r.insert(method, path[:i], nil, skind, "", nil)
			pnames = append(pnames, "*")
			r.insert(method, path[:i+1], h, akind, ppath, pnames)
			return
		}
	}

	r.insert(method, path, h, skind, ppath, pnames)
}

func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string, pnames []string) {
	// Adjust max param
	l := len(pnames)
	if *r.echo.maxParam < l {
		*r.echo.maxParam = l
	}

	cn := r.tree // Current node as root
	if cn == nil {
		panic("echo: invalid method")
	}
	search := path

	for {
		sl := len(search)
		pl := len(cn.prefix)
		l := 0

		// LCP
		max := pl
		if sl < max {
			max = sl
		}
		for ; l < max && search[l] == cn.prefix[l]; l++ {
		}

		if l == 0 {
			// At root node
			cn.label = search[0]
			cn.prefix = search
			if h != nil {
				cn.kind = t
				cn.addHandler(method, h)
				cn.ppath = ppath
				cn.pnames = pnames
			}
		} else if l < pl {
			// Split node
			n := newNode(cn.kind, cn.prefix[l:], cn, cn.children, cn.methodHandler, cn.ppath, cn.pnames)

			// Reset parent node
			cn.kind = skind
			cn.label = cn.prefix[0]
			cn.prefix = cn.prefix[:l]
			cn.children = nil
			cn.methodHandler = new(methodHandler)
			cn.ppath = ""
			cn.pnames = nil

			cn.addChild(n)

			if l == sl {
				// At parent node
				cn.kind = t
				cn.addHandler(method, h)
				cn.ppath = ppath
				cn.pnames = pnames
			} else {
				// Create child node
				n = newNode(t, search[l:], cn, nil, new(methodHandler), ppath, pnames)
				n.addHandler(method, h)
				cn.addChild(n)
			}
		} else if l < sl {
			search = search[l:]
			c := cn.findChildWithLabel(search[0])
			if c != nil {
				// Go deeper
				cn = c
				continue
			}
			// Create child node
			n := newNode(t, search, cn, nil, new(methodHandler), ppath, pnames)
			n.addHandler(method, h)
			cn.addChild(n)
		} else {
			// Node already exists
			if h != nil {
				cn.addHandler(method, h)
				cn.ppath = ppath
				if len(cn.pnames) == 0 { // Issue #729
					cn.pnames = pnames
				}
			}
		}
		return
	}
}

func newNode(t kind, pre string, p *node, c children, mh *methodHandler, ppath string, pnames []string) *node {
	return &node{
		kind:          t,
		label:         pre[0],
		prefix:        pre,
		parent:        p,
		children:      c,
		ppath:         ppath,
		pnames:        pnames,
		methodHandler: mh,
	}
}

func (n *node) addChild(c *node) {
	n.children = append(n.children, c)
}

func (n *node) findChild(l byte, t kind) *node {
	for _, c := range n.children {
		if c.label == l && c.kind == t {
			return c
		}
	}
	return nil
}

func (n *node) findChildWithLabel(l byte) *node {
	for _, c := range n.children {
		if c.label == l {
			return c
		}
	}
	return nil
}

func (n *node) findChildByKind(t kind) *node {
	for _, c := range n.children {
		if c.kind == t {
			return c
		}
	}
	return nil
}

func (n *node) addHandler(method string, h HandlerFunc) {
	switch method {
	case CONNECT:
		n.methodHandler.connect = h
	case DELETE:
		n.methodHandler.delete = h
	case GET:
		n.methodHandler.get = h
	case HEAD:
		n.methodHandler.head = h
	case OPTIONS:
		n.methodHandler.options = h
	case PATCH:
		n.methodHandler.patch = h
	case POST:
		n.methodHandler.post = h
	case PROPFIND:
		n.methodHandler.propfind = h
	case PUT:
		n.methodHandler.put = h
	case TRACE:
		n.methodHandler.trace = h
	}
}

func (n *node) findHandler(method string) HandlerFunc {
	switch method {
	case CONNECT:
		return n.methodHandler.connect
	case DELETE:
		return n.methodHandler.delete
	case GET:
		return n.methodHandler.get
	case HEAD:
		return n.methodHandler.head
	case OPTIONS:
		return n.methodHandler.options
	case PATCH:
		return n.methodHandler.patch
	case POST:
		return n.methodHandler.post
	case PROPFIND:
		return n.methodHandler.propfind
	case PUT:
		return n.methodHandler.put
	case TRACE:
		return n.methodHandler.trace
	default:
		return nil
	}
}

func (n *node) checkMethodNotAllowed() HandlerFunc {
	for _, m := range methods {
		if h := n.findHandler(m); h != nil {
			return MethodNotAllowedHandler
		}
	}
	return NotFoundHandler
}

// Find lookup a handler registered for method and path. It also parses URL for path
// parameters and load them into context.
//
// For performance:
//
// - Get context from `Echo#AcquireContext()`
// - Reset it `Context#Reset()`
// - Return it `Echo#ReleaseContext()`.
func (r *Router) Find(method, path string, c Context) {
	ctx := c.(*context)
	ctx.path = path
	cn := r.tree // Current node as root

	var (
		search  = path
		child   *node         // Child node
		n       int           // Param counter
		nk      kind          // Next kind
		nn      *node         // Next node
		ns      string        // Next search
		pvalues = ctx.pvalues // Use the internal slice so the interface can keep the illusion of a dynamic slice
	)

	// Search order static > param > any
	for {
		if search == "" {
			goto End
		}

		pl := 0 // Prefix length
		l := 0  // LCP length

		if cn.label != ':' {
			sl := len(search)
			pl = len(cn.prefix)

			// LCP
			max := pl
			if sl < max {
				max = sl
			}
			for ; l < max && search[l] == cn.prefix[l]; l++ {
			}
		}

		if l == pl {
			// Continue search
			search = search[l:]
		} else {
			cn = nn
			search = ns
			if nk == pkind {
				goto Param
			} else if nk == akind {
				goto Any
			}
			// Not found
			return
		}

		if search == "" {
			goto End
		}

		// Static node
		if child = cn.findChild(search[0], skind); child != nil {
			// Save next
			if cn.prefix[len(cn.prefix)-1] == '/' { // Issue #623
				nk = pkind
				nn = cn
				ns = search
			}
			cn = child
			continue
		}

		// Param node
	Param:
		if child = cn.findChildByKind(pkind); child != nil {
			// Issue #378
			if len(pvalues) == n {
				continue
			}

			// Save next
			if cn.prefix[len(cn.prefix)-1] == '/' { // Issue #623
				nk = akind
				nn = cn
				ns = search
			}

			cn = child
			i, l := 0, len(search)
			for ; i < l && search[i] != '/' && search[i] != '.'; i++ {
			}
			pvalues[n] = search[:i]
			n++
			search = search[i:]
			continue
		}

		// Any node
	Any:
		if cn = cn.findChildByKind(akind); cn == nil {
			if nn != nil {
				cn = nn
				nn = cn.parent // Next (Issue #954)
				search = ns
				if nk == pkind {
					goto Param
				} else if nk == akind {
					goto Any
				}
			}
			// Not found
			return
		}
		pvalues[len(cn.pnames)-1] = search
		goto End
	}

End:
	ctx.handler = cn.findHandler(method)
	ctx.path = cn.ppath
	ctx.pnames = cn.pnames

	// NOTE: Slow zone...
	if ctx.handler == nil {
		ctx.handler = cn.checkMethodNotAllowed()

		// Dig further for any, might have an empty value for *, e.g.
		// serving a directory. Issue #207.
		if cn = cn.findChildByKind(akind); cn == nil {
			return
		}
		if h := cn.findHandler(method); h != nil {
			ctx.handler = h
		} else {
			ctx.handler = cn.checkMethodNotAllowed()
		}
		ctx.path = cn.ppath
		ctx.pnames = cn.pnames
		pvalues[len(cn.pnames)-1] = ""
	}

	return
}
<commit_msg>Fix parameter routes with an extension (/foo/:id.json)<commit_after>package echo

type (
	// Router is the registry of all registered routes for an `Echo` instance for
	// request matching and URL path parameter parsing.
	Router struct {
		tree   *node
		routes map[string]*Route
		echo   *Echo
	}
	node struct {
		kind          kind
		label         byte
		prefix        string
		parent        *node
		children      children
		ppath         string
		pnames        []string
		methodHandler *methodHandler
	}
	kind          uint8
	children      []*node
	methodHandler struct {
		connect  HandlerFunc
		delete   HandlerFunc
		get      HandlerFunc
		head     HandlerFunc
		options  HandlerFunc
		patch    HandlerFunc
		post     HandlerFunc
		propfind HandlerFunc
		put      HandlerFunc
		trace    HandlerFunc
	}
)

const (
	skind kind = iota
	pkind
	akind
)

// NewRouter returns a new Router instance.
func NewRouter(e *Echo) *Router {
	return &Router{
		tree: &node{
			methodHandler: new(methodHandler),
		},
		routes: map[string]*Route{},
		echo:   e,
	}
}

// Add registers a new route for method and path with matching handler.
func (r *Router) Add(method, path string, h HandlerFunc) {
	// Validate path
	if path == "" {
		panic("echo: path cannot be empty")
	}
	if path[0] != '/' {
		path = "/" + path
	}
	pnames := []string{} // Param names
	ppath := path        // Pristine path

	for i, l := 0, len(path); i < l; i++ {
		if path[i] == ':' {
			j := i + 1

			r.insert(method, path[:i], nil, skind, "", nil)
			for ; i < l && path[i] != '/' && path[i] != '.'; i++ {
			}

			pnames = append(pnames, path[j:i])
			path = path[:j] + path[i:]
			i, l = j, len(path)

			if i == l {
				r.insert(method, path[:i], h, pkind, ppath, pnames)
				return
			}
			r.insert(method, path[:i], nil, pkind, ppath, pnames)
		} else if path[i] == '*' {
			r.insert(method, path[:i], nil, skind, "", nil)
			pnames = append(pnames, "*")
			r.insert(method, path[:i+1], h, akind, ppath, pnames)
			return
		}
	}

	r.insert(method, path, h, skind, ppath, pnames)
}

func (r *Router) insert(method, path string, h HandlerFunc, t kind, ppath string, pnames []string) {
	// Adjust max param
	l := len(pnames)
	if *r.echo.maxParam < l {
		*r.echo.maxParam = l
	}

	cn := r.tree // Current node as root
	if cn == nil {
		panic("echo: invalid method")
	}
	search := path

	for {
		sl := len(search)
		pl := len(cn.prefix)
		l := 0

		// LCP
		max := pl
		if sl < max {
			max = sl
		}
		for ; l < max && search[l] == cn.prefix[l]; l++ {
		}

		if l == 0 {
			// At root node
			cn.label = search[0]
			cn.prefix = search
			if h != nil {
				cn.kind = t
				cn.addHandler(method, h)
				cn.ppath = ppath
				cn.pnames = pnames
			}
		} else if l < pl {
			// Split node
			n := newNode(cn.kind, cn.prefix[l:], cn, cn.children, cn.methodHandler, cn.ppath, cn.pnames)

			// Reset parent node
			cn.kind = skind
			cn.label = cn.prefix[0]
			cn.prefix = cn.prefix[:l]
			cn.children = nil
			cn.methodHandler = new(methodHandler)
			cn.ppath = ""
			cn.pnames = nil

			cn.addChild(n)

			if l == sl {
				// At parent node
				cn.kind = t
				cn.addHandler(method, h)
				cn.ppath = ppath
				cn.pnames = pnames
			} else {
				// Create child node
				n = newNode(t, search[l:], cn, nil, new(methodHandler), ppath, pnames)
				n.addHandler(method, h)
				cn.addChild(n)
			}
		} else if l < sl {
			search = search[l:]
			c := cn.findChildWithLabel(search[0])
			if c != nil {
				// Go deeper
				cn = c
				continue
			}
			// Create child node
			n := newNode(t, search, cn, nil, new(methodHandler), ppath, pnames)
			n.addHandler(method, h)
			cn.addChild(n)
		} else {
			// Node already exists
			if h != nil {
				cn.addHandler(method, h)
				cn.ppath = ppath
				if len(cn.pnames) == 0 { // Issue #729
					cn.pnames = pnames
				}
			}
		}
		return
	}
}

func newNode(t kind, pre string, p *node, c children, mh *methodHandler, ppath string, pnames []string) *node {
	return &node{
		kind:          t,
		label:         pre[0],
		prefix:        pre,
		parent:        p,
		children:      c,
		ppath:         ppath,
		pnames:        pnames,
		methodHandler: mh,
	}
}

func (n *node) addChild(c *node) {
	n.children = append(n.children, c)
}

func (n *node) findChild(l byte, t kind) *node {
	for _, c := range n.children {
		if c.label == l && c.kind == t {
			return c
		}
	}
	return nil
}

func (n *node) findChildWithLabel(l byte) *node {
	for _, c := range n.children {
		if c.label == l {
			return c
		}
	}
	return nil
}

func (n *node) findChildByKind(t kind) *node {
	for _, c := range n.children {
		if c.kind == t {
			return c
		}
	}
	return nil
}

func (n *node) addHandler(method string, h HandlerFunc) {
	switch method {
	case CONNECT:
		n.methodHandler.connect = h
	case DELETE:
		n.methodHandler.delete = h
	case GET:
		n.methodHandler.get = h
	case HEAD:
		n.methodHandler.head = h
	case OPTIONS:
		n.methodHandler.options = h
	case PATCH:
		n.methodHandler.patch = h
	case POST:
		n.methodHandler.post = h
	case PROPFIND:
		n.methodHandler.propfind = h
	case PUT:
		n.methodHandler.put = h
	case TRACE:
		n.methodHandler.trace = h
	}
}

func (n *node) findHandler(method string) HandlerFunc {
	switch method {
	case CONNECT:
		return n.methodHandler.connect
	case DELETE:
		return n.methodHandler.delete
	case GET:
		return n.methodHandler.get
	case HEAD:
		return n.methodHandler.head
	case OPTIONS:
		return n.methodHandler.options
	case PATCH:
		return n.methodHandler.patch
	case POST:
		return n.methodHandler.post
	case PROPFIND:
		return n.methodHandler.propfind
	case PUT:
		return n.methodHandler.put
	case TRACE:
		return n.methodHandler.trace
	default:
		return nil
	}
}

func (n *node) checkMethodNotAllowed() HandlerFunc {
	for _, m := range methods {
		if h := n.findHandler(m); h != nil {
			return MethodNotAllowedHandler
		}
	}
	return NotFoundHandler
}

// Find lookup a handler registered for method and path. It also parses URL for path
// parameters and load them into context.
//
// For performance:
//
// - Get context from `Echo#AcquireContext()`
// - Reset it `Context#Reset()`
// - Return it `Echo#ReleaseContext()`.
func (r *Router) Find(method, path string, c Context) {
	ctx := c.(*context)
	ctx.path = path
	cn := r.tree // Current node as root

	var (
		search  = path
		child   *node         // Child node
		n       int           // Param counter
		nk      kind          // Next kind
		nn      *node         // Next node
		ns      string        // Next search
		pvalues = ctx.pvalues // Use the internal slice so the interface can keep the illusion of a dynamic slice
	)

	// Search order static > param > any
	for {
		if search == "" {
			goto End
		}

		pl := 0 // Prefix length
		l := 0  // LCP length

		if cn.label != ':' {
			sl := len(search)
			pl = len(cn.prefix)

			// LCP
			max := pl
			if sl < max {
				max = sl
			}
			for ; l < max && search[l] == cn.prefix[l]; l++ {
			}
		}

		if l == pl {
			// Continue search
			search = search[l:]
		} else {
			cn = nn
			search = ns
			if nk == pkind {
				goto Param
			} else if nk == akind {
				goto Any
			}
			// Not found
			return
		}

		if search == "" {
			goto End
		}

		// Static node
		if child = cn.findChild(search[0], skind); child != nil {
			// Save next
			if cn.prefix[len(cn.prefix)-1] == '/' { // Issue #623
				nk = pkind
				nn = cn
				ns = search
			}
			cn = child
			continue
		}

		// Param node
	Param:
		if child = cn.findChildByKind(pkind); child != nil {
			// Issue #378
			if len(pvalues) == n {
				continue
			}

			// Save next
			if cn.prefix[len(cn.prefix)-1] == '/' { // Issue #623
				nk = akind
				nn = cn
				ns = search
			}

			cn = child
			i, l := 0, len(search)
			for ; i < l && search[i] != '/' && search[i] != '.'; i++ {
			}
			pvalues[n] = search[:i]
			n++
			search = search[i:]
			continue
		}

		// Any node
	Any:
		if cn = cn.findChildByKind(akind); cn == nil {
			if nn != nil {
				cn = nn
				nn = cn.parent // Next (Issue #954)
				search = ns
				if nk == pkind {
					goto Param
				} else if nk == akind {
					goto Any
				}
			}
			// Not found
			return
		}
		pvalues[len(cn.pnames)-1] = search
		goto End
	}

End:
	ctx.handler = cn.findHandler(method)
	ctx.path = cn.ppath
	ctx.pnames = cn.pnames

	// NOTE: Slow zone...
	if ctx.handler == nil {
		ctx.handler = cn.checkMethodNotAllowed()

		// Dig further for any, might have an empty value for *, e.g.
		// serving a directory. Issue #207.
		if cn = cn.findChildByKind(akind); cn == nil {
			return
		}
		if h := cn.findHandler(method); h != nil {
			ctx.handler = h
		} else {
			ctx.handler = cn.checkMethodNotAllowed()
		}
		ctx.path = cn.ppath
		ctx.pnames = cn.pnames
		pvalues[len(cn.pnames)-1] = ""
	}

	return
}
<|endoftext|>


<commit_before>package files_test

import (
	"database/sql"
	"testing"

	u "github.com/araddon/gou"
	"github.com/bmizerany/assert"
	"github.com/lytics/cloudstorage"

	"github.com/araddon/qlbridge/datasource"
	"github.com/araddon/qlbridge/datasource/files"
	"github.com/araddon/qlbridge/exec"
	"github.com/araddon/qlbridge/expr/builtins"
	"github.com/araddon/qlbridge/schema"
)

var localconfig = &cloudstorage.CloudStoreContext{
	LogggingContext: "unittest",
	TokenSource:     cloudstorage.LocalFileSource,
	LocalFS:         "tables/",
	TmpDir:          "/tmp/localcache",
}

func init() {
	if testing.Verbose() {
		u.SetupLogging("debug")
		u.SetColorOutput()
	}

	builtins.LoadAllBuiltins()

	datasource.Register("testcsvs", newSource())
	exec.RegisterSqlDriver()
	exec.DisableRecover()
}

type testSource struct {
	*files.FileSource
}

func newSource() schema.Source {
	return &testSource{files.NewFileSource()}
}

// Setup the filesource with schema info
func (m *testSource) Setup(ss *schema.SchemaSource) error {

	settings := u.JsonHelper(map[string]interface{}{
		"path":     "appearances",
		"filetype": "csv",
	})
	ss.Conf = &schema.ConfigSource{
		Name:       "testcsvs",
		SourceType: "testcsvs",
		Settings:   settings,
	}
	return m.FileSource.Setup(ss)
}

type player struct {
	PlayerId string
	YearId   string
	TeamId   string
}

// go test -bench="BenchFile" --run="BenchFile"
//
// go test -bench="BenchFile" --run="BenchFile" -cpuprofile cpu.out
// go tool pprof files.test cpu.out
func TestFileSelectSimple(t *testing.T) {

	sqlText := `SELECT playerid, yearid, teamid
	FROM appearances
	WHERE playerid = "barnero01" AND yearid = "1871";
	`
	db, err := sql.Open("qlbridge", "testcsvs")
	assert.Equalf(t, nil, err, "no error: %v", err)
	assert.NotEqual(t, nil, db, "has conn: ", db)

	defer func() {
		if err := db.Close(); err != nil {
			t.Fatalf("Should not error on close: %v", err)
		}
	}()

	rows, err := db.Query(sqlText)
	assert.Tf(t, err == nil, "no error: %v", err)
	defer rows.Close()
	assert.Tf(t, rows != nil, "has results: %v", rows)
	cols, err := rows.Columns()
	assert.Tf(t, err == nil, "no error: %v", err)
	assert.Tf(t, len(cols) == 3, "3 cols: %v", cols)
	players := make([]player, 0)
	for rows.Next() {
		var p player
		err = rows.Scan(&p.PlayerId, &p.YearId, &p.TeamId)
		assert.Tf(t, err == nil, "no error: %v", err)
		u.Debugf("player=%+v", p)
		players = append(players, p)
	}
	assert.Tf(t, rows.Err() == nil, "no error: %v", err)
	assert.Tf(t, len(players) == 1, "has 1 players row: %+v", players)

	p1 := players[0]
	assert.T(t, p1.PlayerId == "barnero01")
	assert.T(t, p1.YearId == "1871")
	assert.T(t, p1.TeamId == "BS1")
}

// go test -bench="BenchFileSqlWhere" --run="BenchFileSqlWhere"
//
// go test -bench="BenchFileSqlWhere" --run="BenchFileSqlWhere" -cpuprofile cpu.out
// go tool pprof files.test cpu.out
func BenchmarkBenchFileSqlWhere(b *testing.B) {

	sqlText := `SELECT playerid, yearid, teamid
	FROM appearances
	WHERE playerid = "barnero01" AND yearid = "1871";
	`
	db, _ := sql.Open("qlbridge", "testcsvs")

	b.StartTimer()

	for i := 0; i < b.N; i++ {
		rows, _ := db.Query(sqlText)
		players := make([]player, 0)
		for rows.Next() {
			var p player
			rows.Scan(&p.PlayerId, &p.YearId, &p.TeamId)
			players = append(players, p)
		}
		rows.Close()
		if len(players) != 1 {
			b.Fail()
		}
	}
}

/*

bench_april_2016

BenchmarkBenchFileIter-4   	       3	 356800349 ns/op
BenchmarkBenchFileIter-4   	       3	 335035981 ns/op
BenchmarkBenchFileIter-4   	       3	 337403907 ns/op

bench_master Jan 17 2017
BenchmarkBenchFileSqlWhere-4   	       3	 441293018 ns/op
BenchmarkBenchFileSqlWhere-4   	       3	 478329135 ns/op
BenchmarkBenchFileSqlWhere-4   	       3	 444717045 ns/op

1.31 times as long.  Bad but not 10x.

*/

// go test -bench="BenchFileIter" --run="BenchFileIter"
//
// go test -bench="BenchFileIter" --run="BenchFileIter" -cpuprofile cpu.out
// go tool pprof files.test cpu.out
func BenchmarkBenchFileIter(b *testing.B) {

	fs, _ := datasource.DataSourcesRegistry().Schema("testcsvs")
	b.StartTimer()

	for i := 0; i < b.N; i++ {

		conn, _ := fs.Open("appearances")
		scanner := conn.(schema.Iterator)

		for {
			msg := scanner.Next()
			if msg == nil {
				break
			}
		}
	}
}

/*

bench_april_2016  code from april 2nd 2016
BenchmarkBenchFileIter	3	 362930983 ns/op

bench_master  Jan 17 2017
BenchmarkBenchFileIter	3	 357590068 ns/op

*/
<commit_msg>update benchmark docs<commit_after>package files_test

import (
	"database/sql"
	"testing"

	u "github.com/araddon/gou"
	"github.com/bmizerany/assert"
	"github.com/lytics/cloudstorage"

	"github.com/araddon/qlbridge/datasource"
	"github.com/araddon/qlbridge/datasource/files"
	"github.com/araddon/qlbridge/exec"
	"github.com/araddon/qlbridge/expr/builtins"
	"github.com/araddon/qlbridge/schema"
)

var localconfig = &cloudstorage.CloudStoreContext{
	LogggingContext: "unittest",
	TokenSource:     cloudstorage.LocalFileSource,
	LocalFS:         "tables/",
	TmpDir:          "/tmp/localcache",
}

func init() {
	if testing.Verbose() {
		u.SetupLogging("debug")
		u.SetColorOutput()
	}

	builtins.LoadAllBuiltins()

	datasource.Register("testcsvs", newSource())
	exec.RegisterSqlDriver()
	exec.DisableRecover()
}

type testSource struct {
	*files.FileSource
}

func newSource() schema.Source {
	return &testSource{files.NewFileSource()}
}

// Setup the filesource with schema info
func (m *testSource) Setup(ss *schema.SchemaSource) error {

	settings := u.JsonHelper(map[string]interface{}{
		"path":     "appearances",
		"filetype": "csv",
	})
	ss.Conf = &schema.ConfigSource{
		Name:       "testcsvs",
		SourceType: "testcsvs",
		Settings:   settings,
	}
	return m.FileSource.Setup(ss)
}

type player struct {
	PlayerId string
	YearId   string
	TeamId   string
}

// go test -bench="BenchFile" --run="BenchFile"
//
// go test -bench="BenchFile" --run="BenchFile" -cpuprofile cpu.out
// go tool pprof files.test cpu.out
func TestFileSelectSimple(t *testing.T) {

	sqlText := `SELECT playerid, yearid, teamid
	FROM appearances
	WHERE playerid = "barnero01" AND yearid = "1871";
	`
	db, err := sql.Open("qlbridge", "testcsvs")
	assert.Equalf(t, nil, err, "no error: %v", err)
	assert.NotEqual(t, nil, db, "has conn: ", db)

	defer func() {
		if err := db.Close(); err != nil {
			t.Fatalf("Should not error on close: %v", err)
		}
	}()

	rows, err := db.Query(sqlText)
	assert.Tf(t, err == nil, "no error: %v", err)
	defer rows.Close()
	assert.Tf(t, rows != nil, "has results: %v", rows)
	cols, err := rows.Columns()
	assert.Tf(t, err == nil, "no error: %v", err)
	assert.Tf(t, len(cols) == 3, "3 cols: %v", cols)
	players := make([]player, 0)
	for rows.Next() {
		var p player
		err = rows.Scan(&p.PlayerId, &p.YearId, &p.TeamId)
		assert.Tf(t, err == nil, "no error: %v", err)
		u.Debugf("player=%+v", p)
		players = append(players, p)
	}
	assert.Tf(t, rows.Err() == nil, "no error: %v", err)
	assert.Tf(t, len(players) == 1, "has 1 players row: %+v", players)

	p1 := players[0]
	assert.T(t, p1.PlayerId == "barnero01")
	assert.T(t, p1.YearId == "1871")
	assert.T(t, p1.TeamId == "BS1")
}

// go test -bench="BenchFileSqlWhere" --run="BenchFileSqlWhere"
//
// go test -bench="BenchFileSqlWhere" --run="BenchFileSqlWhere" -cpuprofile cpu.out
// go tool pprof files.test cpu.out
func BenchmarkBenchFileSqlWhere(b *testing.B) {

	sqlText := `SELECT playerid, yearid, teamid
	FROM appearances
	WHERE playerid = "barnero01" AND yearid = "1871";
	`
	db, _ := sql.Open("qlbridge", "testcsvs")

	b.StartTimer()

	for i := 0; i < b.N; i++ {
		rows, _ := db.Query(sqlText)
		players := make([]player, 0)
		for rows.Next() {
			var p player
			rows.Scan(&p.PlayerId, &p.YearId, &p.TeamId)
			players = append(players, p)
		}
		rows.Close()
		if len(players) != 1 {
			b.Fail()
		}
	}
}

/*

bench_april_2016

BenchmarkBenchSqlWhere-4   	       3	 441965974 ns/op
BenchmarkBenchSqlWhere-4   	       3	 461789398 ns/op
BenchmarkBenchSqlWhere-4   	       3	 454068259 ns/op

bench_master Jan 17 2017
BenchmarkBenchFileSqlWhere-4   	       3	 441293018 ns/op
BenchmarkBenchFileSqlWhere-4   	       3	 478329135 ns/op
BenchmarkBenchFileSqlWhere-4   	       3	 444717045 ns/op

faster, look-elsewhere!!

*/

// go test -bench="BenchFileIter" --run="BenchFileIter"
//
// go test -bench="BenchFileIter" --run="BenchFileIter" -cpuprofile cpu.out
// go tool pprof files.test cpu.out
func BenchmarkBenchFileIter(b *testing.B) {

	fs, _ := datasource.DataSourcesRegistry().Schema("testcsvs")
	b.StartTimer()

	for i := 0; i < b.N; i++ {

		conn, _ := fs.Open("appearances")
		scanner := conn.(schema.Iterator)

		for {
			msg := scanner.Next()
			if msg == nil {
				break
			}
		}
	}
}

/*

bench_april_2016  code from april 2nd 2016
BenchmarkBenchFileIter-4   	       3	 356800349 ns/op
BenchmarkBenchFileIter-4   	       3	 335035981 ns/op
BenchmarkBenchFileIter-4   	       3	 337403907 ns/op

bench_master  Jan 17 2017
BenchmarkBenchFileIter-4   	       3	 335869429 ns/op
BenchmarkBenchFileIter-4   	       3	 336145414 ns/op
BenchmarkBenchFileIter-4   	       3	 347501696 ns/op

No difference
*/
<|endoftext|>


<commit_before><commit_msg>Add vsphere resource pool to cloud-config.<commit_after><|endoftext|>


<commit_before>
// +build linux darwin

package main

import "github.com/dontpanic92/wxGo/wx"

func (win *MainStatusWindowImpl) additionalBindings() {
	wx.Bind(win.toolbar_icon, wx.EVT_NOTIFICATION_MESSAGE_CLICK, win._on_toolbar_balloon_click, wx.ID_ANY)
	wx.Bind(win.toolbar_icon, wx.EVT_NOTIFICATION_MESSAGE_DISMISSED, win._on_toolbar_balloon_timeout, wx.ID_ANY)
}

func (win *MainStatusWindowImpl) osNotification(notification *NotificationQueueEntry) {
	nm := wx.NewNotificationMessage()
	nm.SetParent(win)
	nm.SetIcon(_get_asset_icon())
	nm.SetTitle(notification.title)
	nm.SetMessage(notification.msg)
	nm.Show(1)
}
<commit_msg>comment out the bindings for the missing notification message events on unix for now<commit_after>
// +build linux darwin

package main

import "github.com/dontpanic92/wxGo/wx"

func (win *MainStatusWindowImpl) additionalBindings() {
	// FIXME the event constants for these appear to be missing
	//wx.Bind(win.toolbar_icon, wx.EVT_NOTIFICATION_MESSAGE_CLICK, win._on_toolbar_balloon_click, wx.ID_ANY)
	//wx.Bind(win.toolbar_icon, wx.EVT_NOTIFICATION_MESSAGE_DISMISSED, win._on_toolbar_balloon_timeout, wx.ID_ANY)
}

func (win *MainStatusWindowImpl) osNotification(notification *NotificationQueueEntry) {
	nm := wx.NewNotificationMessage()
	nm.SetParent(win)
	nm.SetIcon(_get_asset_icon())
	nm.SetTitle(notification.title)
	nm.SetMessage(notification.msg)
	nm.Show(1)
}
<|endoftext|>


<commit_before>package logrus

import (
	"bytes"
	"fmt"
	"sort"
	"strings"
	"time"
)

const (
	nocolor = 0
	red     = 31
	green   = 32
	yellow  = 33
	blue    = 34
)

func init() {
	baseTimestamp = time.Now()
}

func miniTS() int {
	return int(time.Since(baseTimestamp) / time.Second)
}

type TextFormatter struct {
	// Set to true to bypass checking for a TTY before outputting colors.
	ForceColors bool
}

func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
	b := &bytes.Buffer{}

	prefixFieldClashes(entry)

	if f.ForceColors || IsTerminal() {
		levelText := strings.ToUpper(entry.Data["level"].(string))[0:4]

		levelColor := blue

		if entry.Data["level"] == "warning" {
			levelColor = yellow
		} else if entry.Data["level"] == "error" ||
			entry.Data["level"] == "fatal" ||
			entry.Data["level"] == "panic" {
			levelColor = red
		}

		fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Data["msg"])

		keys := make([]string, 0)
		for k, _ := range entry.Data {
			if k != "level" && k != "time" && k != "msg" {
				keys = append(keys, k)
			}
		}
		sort.Strings(keys)
		for _, k := range keys {
			v := entry.Data[k]
			fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%v", levelColor, k, v)
		}
	} else {
		f.AppendKeyValue(b, "time", entry.Data["time"].(string))
		f.AppendKeyValue(b, "level", entry.Data["level"].(string))
		f.AppendKeyValue(b, "msg", entry.Data["msg"].(string))

		for key, value := range entry.Data {
			if key != "time" && key != "level" && key != "msg" {
				f.AppendKeyValue(b, key, value)
			}
		}
	}

	b.WriteByte('\n')
	return b.Bytes(), nil
}

func (f *TextFormatter) AppendKeyValue(b *bytes.Buffer, key, value interface{}) {
	if _, ok := value.(string); ok {
		fmt.Fprintf(b, "%v=%q ", key, value)
	} else {
		fmt.Fprintf(b, "%v=%v ", key, value)
	}
}
<commit_msg>can completely disable colors on text formatter<commit_after>package logrus

import (
	"bytes"
	"fmt"
	"sort"
	"strings"
	"time"
)

const (
	nocolor = 0
	red     = 31
	green   = 32
	yellow  = 33
	blue    = 34
)

func init() {
	baseTimestamp = time.Now()
}

func miniTS() int {
	return int(time.Since(baseTimestamp) / time.Second)
}

type TextFormatter struct {
	// Set to true to bypass checking for a TTY before outputting colors.
	ForceColors   bool
	DisableColors bool
}

func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
	b := &bytes.Buffer{}

	prefixFieldClashes(entry)

	if (f.ForceColors || IsTerminal()) && !DisableColors {
		levelText := strings.ToUpper(entry.Data["level"].(string))[0:4]

		levelColor := blue

		if entry.Data["level"] == "warning" {
			levelColor = yellow
		} else if entry.Data["level"] == "error" ||
			entry.Data["level"] == "fatal" ||
			entry.Data["level"] == "panic" {
			levelColor = red
		}

		fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Data["msg"])

		keys := make([]string, 0)
		for k, _ := range entry.Data {
			if k != "level" && k != "time" && k != "msg" {
				keys = append(keys, k)
			}
		}
		sort.Strings(keys)
		for _, k := range keys {
			v := entry.Data[k]
			fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%v", levelColor, k, v)
		}
	} else {
		f.AppendKeyValue(b, "time", entry.Data["time"].(string))
		f.AppendKeyValue(b, "level", entry.Data["level"].(string))
		f.AppendKeyValue(b, "msg", entry.Data["msg"].(string))

		for key, value := range entry.Data {
			if key != "time" && key != "level" && key != "msg" {
				f.AppendKeyValue(b, key, value)
			}
		}
	}

	b.WriteByte('\n')
	return b.Bytes(), nil
}

func (f *TextFormatter) AppendKeyValue(b *bytes.Buffer, key, value interface{}) {
	if _, ok := value.(string); ok {
		fmt.Fprintf(b, "%v=%q ", key, value)
	} else {
		fmt.Fprintf(b, "%v=%v ", key, value)
	}
}
<|endoftext|>


<commit_before>package gitlab

import (
	"fmt"
	"net/http"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestAuditEventsService_ListInstanceAuditEvents(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/audit_events", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id": 1,
				"author_id": 1,
				"entity_id": 6,
				"entity_type": "Project",
				"details": {
				  "custom_message": "Project archived",
				  "author_name": "Venkatesh Thalluri",
				  "target_id": "flightjs/flight",
				  "target_type": "Project",
				  "target_details": "flightjs/flight",
				  "ip_address": "127.0.0.1",
				  "entity_path": "flightjs/flight"
				}
			  }
			]
		`)
	})

	want := []*AuditEvent{{
		ID:         1,
		AuthorID:   1,
		EntityID:   6,
		EntityType: "Project",
		Details: AuditEventDetails{
			With:          "",
			Add:           "",
			As:            "",
			Change:        "",
			From:          "",
			To:            "",
			Remove:        "",
			CustomMessage: "Project archived",
			AuthorName:    "Venkatesh Thalluri",
			TargetID:      "flightjs/flight",
			TargetType:    "Project",
			TargetDetails: "flightjs/flight",
			IPAddress:     "127.0.0.1",
			EntityPath:    "flightjs/flight",
			FailedLogin:   "",
		},
		CreatedAt: nil,
	}}

	aes, resp, err := client.AuditEvents.ListInstanceAuditEvents(nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, aes)

	aes, resp, err = client.AuditEvents.ListInstanceAuditEvents(nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, aes)
}

func TestAuditEventsService_ListInstanceAuditEvents_StatusNotFound(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/audit_events", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		w.WriteHeader(http.StatusNotFound)
	})

	aes, resp, err := client.AuditEvents.ListInstanceAuditEvents(nil)
	require.Error(t, err)
	require.Nil(t, aes)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAuditEventsService_GetInstanceAuditEvent(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/audit_events/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		  {
			"id": 1,
			"author_id": 1,
			"entity_id": 6,
			"entity_type": "Project",
			"details": {
			  "custom_message": "Project archived",
			  "author_name": "Venkatesh Thalluri",
			  "target_id": "flightjs/flight",
			  "target_type": "Project",
			  "target_details": "flightjs/flight",
			  "ip_address": "127.0.0.1",
			  "entity_path": "flightjs/flight"
			}
		  }
		`)
	})

	want := &AuditEvent{
		ID:         1,
		AuthorID:   1,
		EntityID:   6,
		EntityType: "Project",
		Details: AuditEventDetails{
			With:          "",
			Add:           "",
			As:            "",
			Change:        "",
			From:          "",
			To:            "",
			Remove:        "",
			CustomMessage: "Project archived",
			AuthorName:    "Venkatesh Thalluri",
			TargetID:      "flightjs/flight",
			TargetType:    "Project",
			TargetDetails: "flightjs/flight",
			IPAddress:     "127.0.0.1",
			EntityPath:    "flightjs/flight",
			FailedLogin:   "",
		},
		CreatedAt: nil,
	}

	ae, resp, err := client.AuditEvents.GetInstanceAuditEvent(1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ae)

	ae, resp, err = client.AuditEvents.GetInstanceAuditEvent(1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AuditEvents.GetInstanceAuditEvent(3, nil)
	require.Error(t, err)
	require.Nil(t, ae)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAuditEventsService_ListGroupAuditEvents(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/groups/6/audit_events", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id": 1,
				"author_id": 1,
				"entity_id": 6,
				"entity_type": "Group",
				"details": {
				  "custom_message": "Group archived",
				  "author_name": "Venkatesh Thalluri",
				  "target_id": "flightjs/flight",
				  "target_type": "Group",
				  "target_details": "flightjs/flight",
				  "ip_address": "127.0.0.1",
				  "entity_path": "flightjs/flight"
				}
			  }
			]
		`)
	})

	want := []*AuditEvent{{
		ID:         1,
		AuthorID:   1,
		EntityID:   6,
		EntityType: "Group",
		Details: AuditEventDetails{
			With:          "",
			Add:           "",
			As:            "",
			Change:        "",
			From:          "",
			To:            "",
			Remove:        "",
			CustomMessage: "Group archived",
			AuthorName:    "Venkatesh Thalluri",
			TargetID:      "flightjs/flight",
			TargetType:    "Group",
			TargetDetails: "flightjs/flight",
			IPAddress:     "127.0.0.1",
			EntityPath:    "flightjs/flight",
			FailedLogin:   "",
		},
		CreatedAt: nil,
	}}

	aes, resp, err := client.AuditEvents.ListGroupAuditEvents(6, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, aes)

	aes, resp, err = client.AuditEvents.ListGroupAuditEvents(6.01, nil)
	require.EqualError(t, err, "invalid ID type 6.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, aes)

	aes, resp, err = client.AuditEvents.ListGroupAuditEvents(6, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, aes)

	aes, resp, err = client.AuditEvents.ListGroupAuditEvents(3, nil)
	require.Error(t, err)
	require.Nil(t, aes)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAuditEventsService_GetGroupAuditEvent(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/groups/6/audit_events/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		  {
			"id": 1,
			"author_id": 1,
			"entity_id": 6,
			"entity_type": "Group",
			"details": {
			  "custom_message": "Group archived",
			  "author_name": "Venkatesh Thalluri",
			  "target_id": "flightjs/flight",
			  "target_type": "Group",
			  "target_details": "flightjs/flight",
			  "ip_address": "127.0.0.1",
			  "entity_path": "flightjs/flight"
			}
		  }
		`)
	})

	want := &AuditEvent{
		ID:         1,
		AuthorID:   1,
		EntityID:   6,
		EntityType: "Group",
		Details: AuditEventDetails{
			With:          "",
			Add:           "",
			As:            "",
			Change:        "",
			From:          "",
			To:            "",
			Remove:        "",
			CustomMessage: "Group archived",
			AuthorName:    "Venkatesh Thalluri",
			TargetID:      "flightjs/flight",
			TargetType:    "Group",
			TargetDetails: "flightjs/flight",
			IPAddress:     "127.0.0.1",
			EntityPath:    "flightjs/flight",
			FailedLogin:   "",
		},
		CreatedAt: nil,
	}

	ae, resp, err := client.AuditEvents.GetGroupAuditEvent(6, 1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ae)

	ae, resp, err = client.AuditEvents.GetGroupAuditEvent(6.01, 1, nil)
	require.EqualError(t, err, "invalid ID type 6.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AuditEvents.GetGroupAuditEvent(6, 1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AuditEvents.GetGroupAuditEvent(3, 1, nil)
	require.Error(t, err)
	require.Nil(t, ae)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAuditEventsService_ListProjectAuditEvents(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/projects/6/audit_events", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id": 1,
				"author_id": 1,
				"entity_id": 6,
				"entity_type": "Project",
				"details": {
				  "custom_message": "Project archived",
				  "author_name": "Venkatesh Thalluri",
				  "target_id": "flightjs/flight",
				  "target_type": "Project",
				  "target_details": "flightjs/flight",
				  "ip_address": "127.0.0.1",
				  "entity_path": "flightjs/flight"
				}
			  }
			]
		`)
	})

	want := []*AuditEvent{{
		ID:         1,
		AuthorID:   1,
		EntityID:   6,
		EntityType: "Project",
		Details: AuditEventDetails{
			With:          "",
			Add:           "",
			As:            "",
			Change:        "",
			From:          "",
			To:            "",
			Remove:        "",
			CustomMessage: "Project archived",
			AuthorName:    "Venkatesh Thalluri",
			TargetID:      "flightjs/flight",
			TargetType:    "Project",
			TargetDetails: "flightjs/flight",
			IPAddress:     "127.0.0.1",
			EntityPath:    "flightjs/flight",
			FailedLogin:   "",
		},
		CreatedAt: nil,
	}}

	aes, resp, err := client.AuditEvents.ListProjectAuditEvents(6, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, aes)

	aes, resp, err = client.AuditEvents.ListProjectAuditEvents(6.01, nil)
	require.EqualError(t, err, "invalid ID type 6.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, aes)

	aes, resp, err = client.AuditEvents.ListProjectAuditEvents(6, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, aes)

	aes, resp, err = client.AuditEvents.ListProjectAuditEvents(3, nil)
	require.Error(t, err)
	require.Nil(t, aes)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAuditEventsService_GetProjectAuditEvent(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/projects/6/audit_events/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		  {
			"id": 1,
			"author_id": 1,
			"entity_id": 6,
			"entity_type": "Project",
			"details": {
			  "custom_message": "Project archived",
			  "author_name": "Venkatesh Thalluri",
			  "target_id": "flightjs/flight",
			  "target_type": "Project",
			  "target_details": "flightjs/flight",
			  "ip_address": "127.0.0.1",
			  "entity_path": "flightjs/flight"
			}
		  }
		`)
	})

	want := &AuditEvent{
		ID:         1,
		AuthorID:   1,
		EntityID:   6,
		EntityType: "Project",
		Details: AuditEventDetails{
			With:          "",
			Add:           "",
			As:            "",
			Change:        "",
			From:          "",
			To:            "",
			Remove:        "",
			CustomMessage: "Project archived",
			AuthorName:    "Venkatesh Thalluri",
			TargetID:      "flightjs/flight",
			TargetType:    "Project",
			TargetDetails: "flightjs/flight",
			IPAddress:     "127.0.0.1",
			EntityPath:    "flightjs/flight",
			FailedLogin:   "",
		},
		CreatedAt: nil,
	}

	ae, resp, err := client.AuditEvents.GetProjectAuditEvent(6, 1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ae)

	ae, resp, err = client.AuditEvents.GetProjectAuditEvent(6.01, 1, nil)
	require.EqualError(t, err, "invalid ID type 6.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AuditEvents.GetProjectAuditEvent(6, 1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AuditEvents.GetProjectAuditEvent(3, 1, nil)
	require.Error(t, err)
	require.Nil(t, ae)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}
<commit_msg>removed explicitly defining default values as per review comment<commit_after>package gitlab

import (
	"fmt"
	"net/http"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestAuditEventsService_ListInstanceAuditEvents(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/audit_events", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id": 1,
				"author_id": 1,
				"entity_id": 6,
				"entity_type": "Project",
				"details": {
				  "custom_message": "Project archived",
				  "author_name": "Venkatesh Thalluri",
				  "target_id": "flightjs/flight",
				  "target_type": "Project",
				  "target_details": "flightjs/flight",
				  "ip_address": "127.0.0.1",
				  "entity_path": "flightjs/flight"
				}
			  }
			]
		`)
	})

	want := []*AuditEvent{{
		ID:         1,
		AuthorID:   1,
		EntityID:   6,
		EntityType: "Project",
		Details: AuditEventDetails{
			CustomMessage: "Project archived",
			AuthorName:    "Venkatesh Thalluri",
			TargetID:      "flightjs/flight",
			TargetType:    "Project",
			TargetDetails: "flightjs/flight",
			IPAddress:     "127.0.0.1",
			EntityPath:    "flightjs/flight",
		},
	}}

	aes, resp, err := client.AuditEvents.ListInstanceAuditEvents(nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, aes)

	aes, resp, err = client.AuditEvents.ListInstanceAuditEvents(nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, aes)
}

func TestAuditEventsService_ListInstanceAuditEvents_StatusNotFound(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/audit_events", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		w.WriteHeader(http.StatusNotFound)
	})

	aes, resp, err := client.AuditEvents.ListInstanceAuditEvents(nil)
	require.Error(t, err)
	require.Nil(t, aes)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAuditEventsService_GetInstanceAuditEvent(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/audit_events/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		  {
			"id": 1,
			"author_id": 1,
			"entity_id": 6,
			"entity_type": "Project",
			"details": {
			  "custom_message": "Project archived",
			  "author_name": "Venkatesh Thalluri",
			  "target_id": "flightjs/flight",
			  "target_type": "Project",
			  "target_details": "flightjs/flight",
			  "ip_address": "127.0.0.1",
			  "entity_path": "flightjs/flight"
			}
		  }
		`)
	})

	want := &AuditEvent{
		ID:         1,
		AuthorID:   1,
		EntityID:   6,
		EntityType: "Project",
		Details: AuditEventDetails{
			CustomMessage: "Project archived",
			AuthorName:    "Venkatesh Thalluri",
			TargetID:      "flightjs/flight",
			TargetType:    "Project",
			TargetDetails: "flightjs/flight",
			IPAddress:     "127.0.0.1",
			EntityPath:    "flightjs/flight",
		},
	}

	ae, resp, err := client.AuditEvents.GetInstanceAuditEvent(1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ae)

	ae, resp, err = client.AuditEvents.GetInstanceAuditEvent(1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AuditEvents.GetInstanceAuditEvent(3, nil)
	require.Error(t, err)
	require.Nil(t, ae)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAuditEventsService_ListGroupAuditEvents(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/groups/6/audit_events", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id": 1,
				"author_id": 1,
				"entity_id": 6,
				"entity_type": "Group",
				"details": {
				  "custom_message": "Group archived",
				  "author_name": "Venkatesh Thalluri",
				  "target_id": "flightjs/flight",
				  "target_type": "Group",
				  "target_details": "flightjs/flight",
				  "ip_address": "127.0.0.1",
				  "entity_path": "flightjs/flight"
				}
			  }
			]
		`)
	})

	want := []*AuditEvent{{
		ID:         1,
		AuthorID:   1,
		EntityID:   6,
		EntityType: "Group",
		Details: AuditEventDetails{
			CustomMessage: "Group archived",
			AuthorName:    "Venkatesh Thalluri",
			TargetID:      "flightjs/flight",
			TargetType:    "Group",
			TargetDetails: "flightjs/flight",
			IPAddress:     "127.0.0.1",
			EntityPath:    "flightjs/flight",
		},
	}}

	aes, resp, err := client.AuditEvents.ListGroupAuditEvents(6, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, aes)

	aes, resp, err = client.AuditEvents.ListGroupAuditEvents(6.01, nil)
	require.EqualError(t, err, "invalid ID type 6.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, aes)

	aes, resp, err = client.AuditEvents.ListGroupAuditEvents(6, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, aes)

	aes, resp, err = client.AuditEvents.ListGroupAuditEvents(3, nil)
	require.Error(t, err)
	require.Nil(t, aes)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAuditEventsService_GetGroupAuditEvent(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/groups/6/audit_events/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		  {
			"id": 1,
			"author_id": 1,
			"entity_id": 6,
			"entity_type": "Group",
			"details": {
			  "custom_message": "Group archived",
			  "author_name": "Venkatesh Thalluri",
			  "target_id": "flightjs/flight",
			  "target_type": "Group",
			  "target_details": "flightjs/flight",
			  "ip_address": "127.0.0.1",
			  "entity_path": "flightjs/flight"
			}
		  }
		`)
	})

	want := &AuditEvent{
		ID:         1,
		AuthorID:   1,
		EntityID:   6,
		EntityType: "Group",
		Details: AuditEventDetails{
			CustomMessage: "Group archived",
			AuthorName:    "Venkatesh Thalluri",
			TargetID:      "flightjs/flight",
			TargetType:    "Group",
			TargetDetails: "flightjs/flight",
			IPAddress:     "127.0.0.1",
			EntityPath:    "flightjs/flight",
		},
	}

	ae, resp, err := client.AuditEvents.GetGroupAuditEvent(6, 1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ae)

	ae, resp, err = client.AuditEvents.GetGroupAuditEvent(6.01, 1, nil)
	require.EqualError(t, err, "invalid ID type 6.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AuditEvents.GetGroupAuditEvent(6, 1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AuditEvents.GetGroupAuditEvent(3, 1, nil)
	require.Error(t, err)
	require.Nil(t, ae)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAuditEventsService_ListProjectAuditEvents(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/projects/6/audit_events", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			[
			  {
				"id": 1,
				"author_id": 1,
				"entity_id": 6,
				"entity_type": "Project",
				"details": {
				  "custom_message": "Project archived",
				  "author_name": "Venkatesh Thalluri",
				  "target_id": "flightjs/flight",
				  "target_type": "Project",
				  "target_details": "flightjs/flight",
				  "ip_address": "127.0.0.1",
				  "entity_path": "flightjs/flight"
				}
			  }
			]
		`)
	})

	want := []*AuditEvent{{
		ID:         1,
		AuthorID:   1,
		EntityID:   6,
		EntityType: "Project",
		Details: AuditEventDetails{
			CustomMessage: "Project archived",
			AuthorName:    "Venkatesh Thalluri",
			TargetID:      "flightjs/flight",
			TargetType:    "Project",
			TargetDetails: "flightjs/flight",
			IPAddress:     "127.0.0.1",
			EntityPath:    "flightjs/flight",
		},
	}}

	aes, resp, err := client.AuditEvents.ListProjectAuditEvents(6, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, aes)

	aes, resp, err = client.AuditEvents.ListProjectAuditEvents(6.01, nil)
	require.EqualError(t, err, "invalid ID type 6.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, aes)

	aes, resp, err = client.AuditEvents.ListProjectAuditEvents(6, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, aes)

	aes, resp, err = client.AuditEvents.ListProjectAuditEvents(3, nil)
	require.Error(t, err)
	require.Nil(t, aes)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}

func TestAuditEventsService_GetProjectAuditEvent(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/projects/6/audit_events/1", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
		  {
			"id": 1,
			"author_id": 1,
			"entity_id": 6,
			"entity_type": "Project",
			"details": {
			  "custom_message": "Project archived",
			  "author_name": "Venkatesh Thalluri",
			  "target_id": "flightjs/flight",
			  "target_type": "Project",
			  "target_details": "flightjs/flight",
			  "ip_address": "127.0.0.1",
			  "entity_path": "flightjs/flight"
			}
		  }
		`)
	})

	want := &AuditEvent{
		ID:         1,
		AuthorID:   1,
		EntityID:   6,
		EntityType: "Project",
		Details: AuditEventDetails{
			CustomMessage: "Project archived",
			AuthorName:    "Venkatesh Thalluri",
			TargetID:      "flightjs/flight",
			TargetType:    "Project",
			TargetDetails: "flightjs/flight",
			IPAddress:     "127.0.0.1",
			EntityPath:    "flightjs/flight",
		},
	}

	ae, resp, err := client.AuditEvents.GetProjectAuditEvent(6, 1, nil)
	require.NoError(t, err)
	require.NotNil(t, resp)
	require.Equal(t, want, ae)

	ae, resp, err = client.AuditEvents.GetProjectAuditEvent(6.01, 1, nil)
	require.EqualError(t, err, "invalid ID type 6.01, the ID must be an int or a string")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AuditEvents.GetProjectAuditEvent(6, 1, nil, errorOption)
	require.EqualError(t, err, "RequestOptionFunc returns an error")
	require.Nil(t, resp)
	require.Nil(t, ae)

	ae, resp, err = client.AuditEvents.GetProjectAuditEvent(3, 1, nil)
	require.Error(t, err)
	require.Nil(t, ae)
	require.Equal(t, http.StatusNotFound, resp.StatusCode)
}
<|endoftext|>


<commit_before>package vagrant

import (
	"github.com/pulcy/quark/providers"
)

// Apply defaults for the given options
func (vp *vagrantProvider) InstanceDefaults(options providers.CreateInstanceOptions) providers.CreateInstanceOptions {
	options.InstanceConfig = instanceConfigDefaults(options.InstanceConfig)
	return options
}

// Apply defaults for the given options
func (vp *vagrantProvider) ClusterDefaults(options providers.CreateClusterOptions) providers.CreateClusterOptions {
	options.InstanceConfig = instanceConfigDefaults(options.InstanceConfig)
	return options
}

func instanceConfigDefaults(ic providers.InstanceConfig) providers.InstanceConfig {
	if ic.RegionID == "" {
		ic.RegionID = "n/a"
	}
	if ic.ImageID == "" {
		ic.ImageID = "n/a"
	}
	if ic.TypeID == "" {
		ic.TypeID = "n/a"
	}
	return ic
}
<commit_msg>Changed vagrant region default<commit_after>package vagrant

import (
	"github.com/pulcy/quark/providers"
)

// Apply defaults for the given options
func (vp *vagrantProvider) InstanceDefaults(options providers.CreateInstanceOptions) providers.CreateInstanceOptions {
	options.InstanceConfig = instanceConfigDefaults(options.InstanceConfig)
	return options
}

// Apply defaults for the given options
func (vp *vagrantProvider) ClusterDefaults(options providers.CreateClusterOptions) providers.CreateClusterOptions {
	options.InstanceConfig = instanceConfigDefaults(options.InstanceConfig)
	return options
}

func instanceConfigDefaults(ic providers.InstanceConfig) providers.InstanceConfig {
	if ic.RegionID == "" {
		ic.RegionID = "local"
	}
	if ic.ImageID == "" {
		ic.ImageID = "n/a"
	}
	if ic.TypeID == "" {
		ic.TypeID = "n/a"
	}
	return ic
}
<|endoftext|>