<commit_before>// PoolHandler is handler which may be useful at the projects where
// websocket connections are divided into a groups (pools) with access
// to common data
//
// Author: Pushkin Ivan <iv.pushk@gmail.com>
package pwshandler

import (
	"fmt"
	"net/http"

	"golang.org/x/net/websocket"
)

// Environment is a common data for ws connections in one pool(group)
type Environment interface{}

// PoolManager is common interface for a structures which merge
// websocket connections in a pools (groups) with access to common
// data
type PoolManager interface {
	// AddConn creates connection to a pool and returns environment
	// data
	AddConn(ws *websocket.Conn) (Environment, error)
	// DelConn removes passed connection from a pool if it exists
	// in pool
	DelConn(ws *websocket.Conn) error
}

// ConnManager contains methods for processing websocket connections
// with passed common group data
type ConnManager interface {
	// Handle handles connections using passed common environment data
	Handle(ws *websocket.Conn, data Environment) error
	// HandleError processes an errors
	HandleError(ws *websocket.Conn, err error)
}

// RequestVerifier verifies requests. It has to verify a request data
// such a passed hashes, certificates, remote addr, token, passed headers
// or something else
type RequestVerifier interface {
	Verify(ws *websocket.Conn) error
}

const _ERR_FORMAT = "%s: connection handling error: %s"

// PoolHandler returns WS handler which receives websocket requests and
// merges connection goroutines in a pools (groups) with common data.
// poolMgr is a storage of groups and connections. poolMgr divides handled
// connections into groups, stores common group data and passes common
// data to goroutines for processing ws connections. connMgr contains
// handler for processing of ws connection. connMgr gets common group and
// ws connection. verifier must verify connections. If was passed nil
// instead verifier connections will not verified
func PoolHandler(poolMgr PoolManager, connMgr ConnManager,
	verifier RequestVerifier) http.Handler {

	return websocket.Handler(func(ws *websocket.Conn) {
		var err error

		// Verify request
		if verifier != nil {
			if err = verifier.Verify(ws); err != nil {
				connMgr.HandleError(ws,
					fmt.Errorf(_ERR_FORMAT, ws.Request().RemoteAddr, err))
				return
			}
		}

		// Create connection to a pool and take envitonment data
		var data Environment
		if data, err = poolMgr.AddConn(ws); err != nil {
			connMgr.HandleError(ws,
				fmt.Errorf(_ERR_FORMAT, ws.Request().RemoteAddr, err))
			return
		}

		// Handle connection
		if err = connMgr.Handle(ws, data); err != nil {
			connMgr.HandleError(ws,
				fmt.Errorf(_ERR_FORMAT, ws.Request().RemoteAddr, err))
		}

		// Delete connection from a pool (group)
		if err = poolMgr.DelConn(ws); err != nil {
			connMgr.HandleError(ws,
				fmt.Errorf(_ERR_FORMAT, ws.Request().RemoteAddr, err))
		}
	})
}
<commit_msg>small fix<commit_after>// PoolHandler is handler which may be useful at the projects where
// websocket connections are divided into a groups (pools) with access
// to common data
package pwshandler

import (
	"fmt"
	"net/http"

	"golang.org/x/net/websocket"
)

// Environment is a common data for ws connections in one pool(group)
type Environment interface{}

// PoolManager is common interface for a structures which merge
// websocket connections in a pools (groups) with access to common
// data
type PoolManager interface {
	// AddConn creates connection to a pool and returns environment
	// data
	AddConn(ws *websocket.Conn) (Environment, error)
	// DelConn removes passed connection from a pool if it exists
	// in pool
	DelConn(ws *websocket.Conn) error
}

// ConnManager contains methods for processing websocket connections
// with passed common group data
type ConnManager interface {
	// Handle handles connections using passed common environment data
	Handle(ws *websocket.Conn, data Environment) error
	// HandleError processes an errors
	HandleError(ws *websocket.Conn, err error)
}

// RequestVerifier verifies requests. It has to verify a request data
// such a passed hashes, certificates, remote addr, token, passed headers
// or something else
type RequestVerifier interface {
	Verify(ws *websocket.Conn) error
}

const _ERR_FORMAT = "%s: connection handling error: %s"

// PoolHandler returns WS handler which receives websocket requests and
// merges connection goroutines in a pools (groups) with common data.
// poolMgr is a storage of groups and connections. poolMgr divides handled
// connections into groups, stores common group data and passes common
// data to goroutines for processing ws connections. connMgr contains
// handler for processing of ws connection. connMgr gets common group and
// ws connection. verifier must verify connections. If was passed nil
// instead verifier connections will not verified
func PoolHandler(poolMgr PoolManager, connMgr ConnManager,
	verifier RequestVerifier) http.Handler {

	return websocket.Handler(func(ws *websocket.Conn) {
		var err error

		// Verify request
		if verifier != nil {
			if err = verifier.Verify(ws); err != nil {
				connMgr.HandleError(ws,
					fmt.Errorf(_ERR_FORMAT, ws.Request().RemoteAddr, err))
				return
			}
		}

		// Create connection to a pool and take envitonment data
		var data Environment
		if data, err = poolMgr.AddConn(ws); err != nil {
			connMgr.HandleError(ws,
				fmt.Errorf(_ERR_FORMAT, ws.Request().RemoteAddr, err))
			return
		}

		// Handle connection
		if err = connMgr.Handle(ws, data); err != nil {
			connMgr.HandleError(ws,
				fmt.Errorf(_ERR_FORMAT, ws.Request().RemoteAddr, err))
		}

		// Delete connection from a pool (group)
		if err = poolMgr.DelConn(ws); err != nil {
			connMgr.HandleError(ws,
				fmt.Errorf(_ERR_FORMAT, ws.Request().RemoteAddr, err))
		}
	})
}
<|endoftext|>


<commit_before>package log

import (
	"fmt"
	"log"
	"time"

	"github.com/volatile/core"
	"github.com/volatile/core/coreutil"
	"github.com/whitedevops/colors"
)

// Use tells the core to use this handler.
func Use() {
	core.Use(func(c *core.Context) {
		start := time.Now()
		// Keep original request path in case of http.StripPrefix.
		path := c.Request.URL.Path

		c.Next()

		log.Printf(colors.ResetAll+"  %s   %s   %s  %s", fmtDuration(start), fmtStatus(c), fmtMethod(c), fmtPath(path))
	})
}

func fmtDuration(start time.Time) string {
	return fmt.Sprintf("%s%s%13s%s", colors.ResetAll, colors.ResetAll+colors.Dim, time.Since(start), colors.ResetAll)
}

func fmtStatus(c *core.Context) string {
	code := coreutil.ResponseStatus(c.ResponseWriter)

	color := colors.White

	switch {
	case code >= 200 && code <= 299:
		color += colors.BackgroundGreen
	case code >= 300 && code <= 399:
		color += colors.BackgroundCyan
	case code >= 400 && code <= 499:
		color += colors.BackgroundYellow
	default:
		color += colors.BackgroundRed
	}

	return fmt.Sprintf("%s%s %3d %s", colors.ResetAll, color, code, colors.ResetAll)
}

func fmtMethod(c *core.Context) string {
	var color string

	switch c.Request.Method {
	case "GET":
		color += colors.Green
	case "POST":
		color += colors.Cyan
	case "PUT", "PATCH":
		color += colors.Blue
	case "DELETE":
		color += colors.Red
	}

	return fmt.Sprintf("%s%s%s%s", colors.ResetAll, color, c.Request.Method, colors.ResetAll)
}

func fmtPath(path string) string {
	return fmt.Sprintf("%s%s%s%s", colors.ResetAll, colors.Dim, path, colors.ResetAll)
}
<commit_msg>Fix comment typo<commit_after>package log

import (
	"fmt"
	"log"
	"time"

	"github.com/volatile/core"
	"github.com/volatile/core/coreutil"
	"github.com/whitedevops/colors"
)

// Use tells the core to use this handler.
func Use() {
	core.Use(func(c *core.Context) {
		start := time.Now()
		path := c.Request.URL.Path // Keep original request path in case of http.StripPrefix.

		c.Next()

		log.Printf(colors.ResetAll+"  %s   %s   %s  %s", fmtDuration(start), fmtStatus(c), fmtMethod(c), fmtPath(path))
	})
}

func fmtDuration(start time.Time) string {
	return fmt.Sprintf("%s%s%13s%s", colors.ResetAll, colors.ResetAll+colors.Dim, time.Since(start), colors.ResetAll)
}

func fmtStatus(c *core.Context) string {
	code := coreutil.ResponseStatus(c.ResponseWriter)

	color := colors.White

	switch {
	case code >= 200 && code <= 299:
		color += colors.BackgroundGreen
	case code >= 300 && code <= 399:
		color += colors.BackgroundCyan
	case code >= 400 && code <= 499:
		color += colors.BackgroundYellow
	default:
		color += colors.BackgroundRed
	}

	return fmt.Sprintf("%s%s %3d %s", colors.ResetAll, color, code, colors.ResetAll)
}

func fmtMethod(c *core.Context) string {
	var color string

	switch c.Request.Method {
	case "GET":
		color += colors.Green
	case "POST":
		color += colors.Cyan
	case "PUT", "PATCH":
		color += colors.Blue
	case "DELETE":
		color += colors.Red
	}

	return fmt.Sprintf("%s%s%s%s", colors.ResetAll, color, c.Request.Method, colors.ResetAll)
}

func fmtPath(path string) string {
	return fmt.Sprintf("%s%s%s%s", colors.ResetAll, colors.Dim, path, colors.ResetAll)
}
<|endoftext|>


<commit_before>package rest

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
)

type HttpTransport struct {
	BaseURL    *url.URL
	HTTPClient *http.Client
	httpDo     func(c *http.Client, req *http.Request) (*http.Response, error)
}

func (h HttpTransport) Request(req Request) ([]interface{}, error) {
	var raw []interface{}

	rel, err := url.Parse(req.RefURL)
	if err != nil {
		return nil, err
	}
	if req.Params != nil {
		rel.RawQuery = req.Params.Encode()
	}
	if req.Data == nil {
		req.Data = map[string]interface{}{}
	}

	b, err := json.Marshal(req.Data)
	if err != nil {
		return nil, err
	}

	body := bytes.NewReader(b)

	u := h.BaseURL.ResolveReference(rel)
	httpReq, err := http.NewRequest(req.Method, u.String(), body)
	for k, v := range req.Headers {
		httpReq.Header.Add(k, v)
	}
	if err != nil {
		return nil, err
	}

	resp, err := h.do(httpReq, &raw)
	if err != nil {
		if resp != nil {
			return nil, fmt.Errorf("%v", err)
		} else {
			return nil, fmt.Errorf("could not parse response: %s", resp.Response.Status)
		}

	}

	return raw, nil
}

// Do executes API request created by NewRequest method or custom *http.Request.
func (h HttpTransport) do(req *http.Request, v interface{}) (*Response, error) {
	resp, err := h.httpDo(h.HTTPClient, req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	response := newResponse(resp)
	err = checkResponse(response)
	if err != nil {
		return response, err
	}

	if v != nil {
		err = json.Unmarshal(response.Body, v)
		if err != nil {
			return response, err
		}
	}

	return response, nil
}
<commit_msg>nil dereference in Request fixed<commit_after>package rest

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
)

type HttpTransport struct {
	BaseURL    *url.URL
	HTTPClient *http.Client
	httpDo     func(c *http.Client, req *http.Request) (*http.Response, error)
}

func (h HttpTransport) Request(req Request) ([]interface{}, error) {
	var raw []interface{}

	rel, err := url.Parse(req.RefURL)
	if err != nil {
		return nil, err
	}
	if req.Params != nil {
		rel.RawQuery = req.Params.Encode()
	}
	if req.Data == nil {
		req.Data = map[string]interface{}{}
	}

	b, err := json.Marshal(req.Data)
	if err != nil {
		return nil, err
	}

	body := bytes.NewReader(b)

	u := h.BaseURL.ResolveReference(rel)
	httpReq, err := http.NewRequest(req.Method, u.String(), body)
	for k, v := range req.Headers {
		httpReq.Header.Add(k, v)
	}
	if err != nil {
		return nil, err
	}

	resp, err := h.do(httpReq, &raw)
	if err != nil {
		if resp != nil {
			return nil, fmt.Errorf("could not parse response: %s", resp.Response.Status)
		} else {
			return nil, fmt.Errorf("%v", err)
		}

	}

	return raw, nil
}

// Do executes API request created by NewRequest method or custom *http.Request.
func (h HttpTransport) do(req *http.Request, v interface{}) (*Response, error) {
	resp, err := h.httpDo(h.HTTPClient, req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	response := newResponse(resp)
	err = checkResponse(response)
	if err != nil {
		return response, err
	}

	if v != nil {
		err = json.Unmarshal(response.Body, v)
		if err != nil {
			return response, err
		}
	}

	return response, nil
}
<|endoftext|>


<commit_before>//----------------------------------------
//
// Copyright © ying32. All Rights Reserved.
//
// Licensed under Apache License 2.0
//
//----------------------------------------

// 实现一些Go的Image转vcl/lcl的
package bitmap

import (
	"bytes"
	"errors"
	"image"
	"image/gif"
	"image/jpeg"
	"image/png"
	"unsafe"

	"github.com/ying32/govcl/vcl/types"

	"github.com/ying32/govcl/vcl"
)

var (
	ErrPixelDataEmpty        = errors.New("The pixel data is empty")
	ErrUnsupportedDataFormat = errors.New("Unsupported pixel data format")
)

// 将Go的Image转为VCL/LCL的 TPngImage
// 返回的Png对象用完记得Free掉
func ToPngImage(img image.Image) (*vcl.TPngImage, error) {
	buff := bytes.NewBuffer([]byte{})
	if err := png.Encode(buff, img); err != nil {
		return nil, err
	}
	mem := vcl.NewMemoryStreamFromBytes(buff.Bytes())
	defer mem.Free()
	mem.SetPosition(0)
	obj := vcl.NewPngImage()
	obj.LoadFromStream(mem)
	return obj, nil
}

// 32bit bmp，丢失透明度
// 返回的Bmp对象用完记得Free掉
// LCL貌似不分丢失透明度，VCL会。。。。
func ToBitmap(img image.Image) (*vcl.TBitmap, error) {
	switch img.(type) {
	case *image.RGBA:
		data, _ := img.(*image.RGBA)
		return toBitmap(img.Bounds().Size().X, img.Bounds().Size().Y, data.Pix)

	case *image.NRGBA:
		data, _ := img.(*image.NRGBA)
		return toBitmap(img.Bounds().Size().X, img.Bounds().Size().Y, data.Pix)

	default:
		return nil, ErrUnsupportedDataFormat
	}
}

// 将Go的Image转为VCL/LCL的 TJPEGImage
// 返回的jpg对象用完记得Free掉
func ToJPEGImage(img image.Image, quality int) (*vcl.TJPEGImage, error) {
	buff := bytes.NewBuffer([]byte{})
	if err := jpeg.Encode(buff, img, &jpeg.Options{quality}); err != nil {
		return nil, err
	}
	mem := vcl.NewMemoryStreamFromBytes(buff.Bytes())
	defer mem.Free()
	mem.SetPosition(0)
	obj := vcl.NewJPEGImage()
	obj.LoadFromStream(mem)
	return obj, nil
}

// 将Go的Image转为VCL/LCL的 TGIFImage
// 返回的gif对象用完记得Free掉
func ToGIFImage(img image.Image, quality int) (*vcl.TGIFImage, error) {
	buff := bytes.NewBuffer([]byte{})
	if err := gif.Encode(buff, img, &gif.Options{NumColors: 256}); err != nil {
		return nil, err
	}
	mem := vcl.NewMemoryStreamFromBytes(buff.Bytes())
	defer mem.Free()
	mem.SetPosition(0)
	obj := vcl.NewGIFImage()
	obj.LoadFromStream(mem)
	return obj, nil
}

func toBitmap(width, height int, pix []uint8) (*vcl.TBitmap, error) {
	if len(pix) == 0 {
		return nil, ErrPixelDataEmpty
	}
	bmp := vcl.NewBitmap()
	bmp.SetPixelFormat(types.Pf32bit)
	bmp.SetSize(int32(width), int32(height))
	// 填充，左下角为起点
	for h := height - 1; h >= 0; h-- {
		ptr := bmp.ScanLine(int32(h))
		for w := 0; w < width; w++ {
			index := (h*width + w) * 4
			*(*byte)(unsafe.Pointer(ptr + uintptr(w*4))) = pix[index+pixIndex[0]]
			*(*byte)(unsafe.Pointer(ptr + uintptr(w*4+1))) = pix[index+pixIndex[1]]
			*(*byte)(unsafe.Pointer(ptr + uintptr(w*4+2))) = pix[index+pixIndex[2]]
			*(*byte)(unsafe.Pointer(ptr + uintptr(w*4+3))) = pix[index+pixIndex[3]]
		}
	}
	return bmp, nil
}
<commit_msg>Update<commit_after>//----------------------------------------
//
// Copyright © ying32. All Rights Reserved.
//
// Licensed under Apache License 2.0
//
//----------------------------------------

// 实现一些Go的Image转vcl/lcl的
package bitmap

import (
	"bytes"
	"errors"
	"image"
	"image/gif"
	"image/jpeg"
	"image/png"
	"unsafe"

	"github.com/ying32/govcl/vcl/types"

	"github.com/ying32/govcl/vcl"
)

var (
	ErrPixelDataEmpty        = errors.New("The pixel data is empty")
	ErrUnsupportedDataFormat = errors.New("Unsupported pixel data format")
)

// 将Go的Image转为VCL/LCL的 TPngImage
// 返回的Png对象用完记得Free掉
func ToPngImage(img image.Image) (*vcl.TPngImage, error) {
	buff := bytes.NewBuffer([]byte{})
	if err := png.Encode(buff, img); err != nil {
		return nil, err
	}
	mem := vcl.NewMemoryStreamFromBytes(buff.Bytes())
	defer mem.Free()
	mem.SetPosition(0)
	obj := vcl.NewPngImage()
	obj.LoadFromStream(mem)
	return obj, nil
}

// 32bit bmp，丢失透明度
// 返回的Bmp对象用完记得Free掉
// LCL貌似不会丢失透明度，VCL会。。。。
func ToBitmap(img image.Image) (*vcl.TBitmap, error) {
	switch img.(type) {
	case *image.RGBA:
		data, _ := img.(*image.RGBA)
		return toBitmap(img.Bounds().Size().X, img.Bounds().Size().Y, data.Pix)

	case *image.NRGBA:
		data, _ := img.(*image.NRGBA)
		return toBitmap(img.Bounds().Size().X, img.Bounds().Size().Y, data.Pix)

	default:
		return nil, ErrUnsupportedDataFormat
	}
}

// 将Go的Image转为VCL/LCL的 TJPEGImage
// 返回的jpg对象用完记得Free掉
func ToJPEGImage(img image.Image, quality int) (*vcl.TJPEGImage, error) {
	buff := bytes.NewBuffer([]byte{})
	if err := jpeg.Encode(buff, img, &jpeg.Options{quality}); err != nil {
		return nil, err
	}
	mem := vcl.NewMemoryStreamFromBytes(buff.Bytes())
	defer mem.Free()
	mem.SetPosition(0)
	obj := vcl.NewJPEGImage()
	obj.LoadFromStream(mem)
	return obj, nil
}

// 将Go的Image转为VCL/LCL的 TGIFImage
// 返回的gif对象用完记得Free掉
func ToGIFImage(img image.Image, quality int) (*vcl.TGIFImage, error) {
	buff := bytes.NewBuffer([]byte{})
	if err := gif.Encode(buff, img, &gif.Options{NumColors: 256}); err != nil {
		return nil, err
	}
	mem := vcl.NewMemoryStreamFromBytes(buff.Bytes())
	defer mem.Free()
	mem.SetPosition(0)
	obj := vcl.NewGIFImage()
	obj.LoadFromStream(mem)
	return obj, nil
}

func toBitmap(width, height int, pix []uint8) (*vcl.TBitmap, error) {
	if len(pix) == 0 {
		return nil, ErrPixelDataEmpty
	}
	bmp := vcl.NewBitmap()
	bmp.SetPixelFormat(types.Pf32bit)
	bmp.SetSize(int32(width), int32(height))
	// 填充，左下角为起点
	for h := height - 1; h >= 0; h-- {
		ptr := bmp.ScanLine(int32(h))
		for w := 0; w < width; w++ {
			index := (h*width + w) * 4
			*(*byte)(unsafe.Pointer(ptr + uintptr(w*4))) = pix[index+pixIndex[0]]
			*(*byte)(unsafe.Pointer(ptr + uintptr(w*4+1))) = pix[index+pixIndex[1]]
			*(*byte)(unsafe.Pointer(ptr + uintptr(w*4+2))) = pix[index+pixIndex[2]]
			*(*byte)(unsafe.Pointer(ptr + uintptr(w*4+3))) = pix[index+pixIndex[3]]
		}
	}
	return bmp, nil
}
<|endoftext|>


<commit_before>package hamaprs

// #cgo LDFLAGS: -lfap
/*
#include <fap.h>
#include <stdlib.h>

// type is a reserved keyword in Go, we need something to reach p->type
fap_packet_type_t getPacketType(fap_packet_t* p) {
	if (!p) return -1;
    if (p->type != NULL) return *p->type;
    return -1;
}
*/
import "C"
import (
	"errors"
	"reflect"
	"runtime"
	"strings"
	"time"
	"unsafe"
)

type PacketType int

const (
	LocationPacketType PacketType = iota
	ObjectPacketType
	ItemPacketType
	MicePacketType
	NMEAPacketType
	WXPacketType
	MessagePacketType
	CapabilitiesPacketType
	StatusPacketType
	TelemetryPacketType
	TelemetryMessagePacketType
	DXSpotPacketType
	ExperimentalPacketType
	InvalidPacketType
)

const InvalidCoordinate float64 = 360

// Packet describes an APRS packet
type Packet struct {
	PacketType
	Timestamp           int
	SourceCallsign      string
	DestinationCallsign string
	Path                []string
	Status              string
	Symbol              string
	Latitude            float64
	Longitude           float64
	Altitude            float64
	Speed               float64
	Course              uint8
	Weather             *WeatherReport
	RawMessage          string
	MicE                string
	Message             string
	Comment             string
}

// WeatherReport describes the weather related part of an APRS packet
type WeatherReport struct {
	Temperature       float64
	InsideTemperature float64
	Humidity          uint8
	InsideHumidity    uint8
	WindGust          float64
	WindDirection     uint8
	WindSpeed         float64
	Pressure          float64
}

// Telemetry describes the telemetry related part of an APRS packet
type Telemetry struct {
	Val1, Val2, Val3, Val4, Val5 float64
}

// Parser is an APRS Parser
type Parser struct{}

// Returns a new APRS Parser
func NewParser() *Parser {
	C.fap_init()
	p := &Parser{}
	runtime.SetFinalizer(p, func() {
		C.fap_cleanup()
	})
	return p
}

// ParsePacket parse raw packet string and return a new Packet
func (p *Parser) ParsePacket(raw string, isAX25 bool) (*Packet, error) {
	packet := &Packet{Latitude: InvalidCoordinate, Longitude: InvalidCoordinate}
	return p.FillAprsPacket(raw, isAX25, packet)
}

func (p *Parser) FillAprsPacket(raw string, isAX25 bool, packet *Packet) (*Packet, error) {
	message_cstring := C.CString(raw)
	message_length := C.uint(len(raw))
	defer C.free(unsafe.Pointer(message_cstring))

	cpacket := C.fap_parseaprs(message_cstring, message_length, C.short(boolToInt(isAX25)))

	defer C.fap_free(cpacket)

	if cpacket.error_code != nil {
		return nil, errors.New("Unable to parse APRS message")
	}

	packet.Timestamp = int(time.Now().Unix())
	packet.SourceCallsign = strings.ToUpper(C.GoString(cpacket.src_callsign))
	packet.DestinationCallsign = strings.ToUpper(C.GoString(cpacket.dst_callsign))
	packet.Latitude = parseNilableCoordinate(cpacket.latitude)
	packet.Longitude = parseNilableCoordinate(cpacket.longitude)
	packet.Speed = parseNilableFloat(cpacket.speed)
	packet.Course = parseNilableUInt(cpacket.course)
	packet.Altitude = parseNilableFloat(cpacket.altitude)
	packet.Message = C.GoString(cpacket.message)
	packet.Status = C.GoStringN(cpacket.status, C.int(cpacket.status_len))
	packet.Comment = C.GoStringN(cpacket.comment, C.int(cpacket.comment_len))
	packet.RawMessage = raw

	if C.int(cpacket.path_len) > 0 {
		var CPath **C.char = cpacket.path
		length := int(cpacket.path_len)
		hdr := reflect.SliceHeader{
			Data: uintptr(unsafe.Pointer(CPath)),
			Len:  length,
			Cap:  length,
		}
		ptrSlice := *(*[]*C.char)(unsafe.Pointer(&hdr))
		packet.Path = make([]string, int(cpacket.path_len))
		for i, v := range ptrSlice {
			packet.Path[i] = C.GoString(v)
		}
	}
	switch C.getPacketType(cpacket) {
	case C.fapLOCATION:
		packet.PacketType = LocationPacketType
	case C.fapOBJECT:
		packet.PacketType = ObjectPacketType
	case C.fapITEM:
		packet.PacketType = ItemPacketType
	case C.fapMICE:
		packet.PacketType = MicePacketType
	case C.fapNMEA:
		packet.PacketType = NMEAPacketType
	case C.fapWX:
		packet.PacketType = WXPacketType
	case C.fapMESSAGE:
		packet.PacketType = MessagePacketType
	case C.fapCAPABILITIES:
		packet.PacketType = CapabilitiesPacketType
	case C.fapSTATUS:
		packet.PacketType = StatusPacketType
	case C.fapTELEMETRY:
		packet.PacketType = TelemetryPacketType
	case C.fapTELEMETRY_MESSAGE:
		packet.PacketType = TelemetryMessagePacketType
	case C.fapDX_SPOT:
		packet.PacketType = DXSpotPacketType
	case C.fapEXPERIMENTAL:
		packet.PacketType = ExperimentalPacketType
	default:
		packet.PacketType = InvalidPacketType
	}

	if cpacket.wx_report != nil {
		w := WeatherReport{
			Temperature:       parseNilableFloat(cpacket.wx_report.temp),
			InsideTemperature: parseNilableFloat(cpacket.wx_report.temp_in),
			Humidity:          parseNilableUInt(cpacket.wx_report.humidity),
			InsideHumidity:    parseNilableUInt(cpacket.wx_report.humidity_in),
			WindGust:          parseNilableFloat(cpacket.wx_report.wind_gust),
			WindDirection:     parseNilableUInt(cpacket.wx_report.wind_dir),
			WindSpeed:         parseNilableFloat(cpacket.wx_report.wind_speed),
			Pressure:          parseNilableFloat(cpacket.wx_report.pressure),
		}
		packet.Weather = &w
	}

	// MicE alloc a buffer of 20 bytes for fap_mice_mbits_to_message C func
	cbuffer := (*C.char)(C.malloc(C.size_t(20)))
	defer C.free(unsafe.Pointer(cbuffer))

	if cpacket.messagebits != nil {
		C.fap_mice_mbits_to_message(cpacket.messagebits, cbuffer)
		packet.MicE = C.GoString(cbuffer)
	}

	return packet, nil
}

// IncludePosition return true if the packet contains a Position
func (p *Packet) IncludePosition() bool {
	if p.Latitude != InvalidCoordinate && p.Longitude != InvalidCoordinate {
		return true
	}
	return false
}

// return a short version of the callsign as KK6NXK for KK6NXK-7
func ShortCallsign(c string) string {
	s := strings.Split(c, "-")
	return s[0]
}

func boolToInt(b bool) int {
	if b {
		return 1
	}
	return 0
}

func parseNilableFloat(d *C.double) float64 {
	if d != nil {
		return float64(C.double(*d))
	}
	return 0
}

func parseNilableCoordinate(d *C.double) float64 {
	if d != nil {
		return float64(C.double(*d))
	}
	return InvalidCoordinate
}

func parseNilableUInt(d *C.uint) uint8 {
	if d != nil {
		return uint8(C.uint(*d))
	}
	return 0
}
<commit_msg>added telemetry<commit_after>package hamaprs

// #cgo LDFLAGS: -lfap
/*
#include <fap.h>
#include <stdlib.h>

// type is a reserved keyword in Go, we need something to reach p->type
fap_packet_type_t getPacketType(fap_packet_t* p) {
	if (!p) return -1;
    if (p->type != NULL) return *p->type;
    return -1;
}
*/
import "C"
import (
	"errors"
	"reflect"
	"runtime"
	"strings"
	"time"
	"unsafe"
)

type PacketType int

const (
	LocationPacketType PacketType = iota
	ObjectPacketType
	ItemPacketType
	MicePacketType
	NMEAPacketType
	WXPacketType
	MessagePacketType
	CapabilitiesPacketType
	StatusPacketType
	TelemetryPacketType
	TelemetryMessagePacketType
	DXSpotPacketType
	ExperimentalPacketType
	InvalidPacketType
)

// InvalidCoordinate is a marker for an unset position
const InvalidCoordinate float64 = 360

// Packet describes an APRS packet
type Packet struct {
	PacketType
	Timestamp           int
	SourceCallsign      string
	DestinationCallsign string
	Path                []string
	Status              string
	Symbol              string
	Latitude            float64
	Longitude           float64
	Altitude            float64
	Speed               float64
	Course              uint8
	Weather             *WeatherReport
	Telemetry           *Telemetry
	RawMessage          string
	MicE                string
	Message             string
	Comment             string
}

// WeatherReport describes the weather related part of an APRS packet
type WeatherReport struct {
	Temperature       float64
	InsideTemperature float64
	Humidity          uint8
	InsideHumidity    uint8
	WindGust          float64
	WindDirection     uint8
	WindSpeed         float64
	Pressure          float64
}

// Telemetry describes the telemetry related part of an APRS packet
type Telemetry struct {
	Val1, Val2, Val3, Val4, Val5 float64
}

// Parser is an APRS Parser
type Parser struct{}

// Returns a new APRS Parser
func NewParser() *Parser {
	C.fap_init()
	p := &Parser{}
	runtime.SetFinalizer(p, func() {
		C.fap_cleanup()
	})
	return p
}

// ParsePacket parse raw packet string and return a new Packet
func (p *Parser) ParsePacket(raw string, isAX25 bool) (*Packet, error) {
	packet := &Packet{Latitude: InvalidCoordinate, Longitude: InvalidCoordinate}
	return p.FillAprsPacket(raw, isAX25, packet)
}

func (p *Parser) FillAprsPacket(raw string, isAX25 bool, packet *Packet) (*Packet, error) {
	message_cstring := C.CString(raw)
	message_length := C.uint(len(raw))
	defer C.free(unsafe.Pointer(message_cstring))

	cpacket := C.fap_parseaprs(message_cstring, message_length, C.short(boolToInt(isAX25)))

	defer C.fap_free(cpacket)

	if cpacket.error_code != nil {
		return nil, errors.New("Unable to parse APRS message")
	}

	packet.Timestamp = int(time.Now().Unix())
	packet.SourceCallsign = strings.ToUpper(C.GoString(cpacket.src_callsign))
	packet.DestinationCallsign = strings.ToUpper(C.GoString(cpacket.dst_callsign))
	packet.Latitude = parseNilableCoordinate(cpacket.latitude)
	packet.Longitude = parseNilableCoordinate(cpacket.longitude)
	packet.Speed = parseNilableFloat(cpacket.speed)
	packet.Course = parseNilableUInt(cpacket.course)
	packet.Altitude = parseNilableFloat(cpacket.altitude)
	packet.Message = C.GoString(cpacket.message)
	packet.Status = C.GoStringN(cpacket.status, C.int(cpacket.status_len))
	packet.Comment = C.GoStringN(cpacket.comment, C.int(cpacket.comment_len))
	packet.RawMessage = raw

	if C.int(cpacket.path_len) > 0 {
		var CPath **C.char = cpacket.path
		length := int(cpacket.path_len)
		hdr := reflect.SliceHeader{
			Data: uintptr(unsafe.Pointer(CPath)),
			Len:  length,
			Cap:  length,
		}
		ptrSlice := *(*[]*C.char)(unsafe.Pointer(&hdr))
		packet.Path = make([]string, int(cpacket.path_len))
		for i, v := range ptrSlice {
			packet.Path[i] = C.GoString(v)
		}
	}
	switch C.getPacketType(cpacket) {
	case C.fapLOCATION:
		packet.PacketType = LocationPacketType
	case C.fapOBJECT:
		packet.PacketType = ObjectPacketType
	case C.fapITEM:
		packet.PacketType = ItemPacketType
	case C.fapMICE:
		packet.PacketType = MicePacketType
	case C.fapNMEA:
		packet.PacketType = NMEAPacketType
	case C.fapWX:
		packet.PacketType = WXPacketType
	case C.fapMESSAGE:
		packet.PacketType = MessagePacketType
	case C.fapCAPABILITIES:
		packet.PacketType = CapabilitiesPacketType
	case C.fapSTATUS:
		packet.PacketType = StatusPacketType
	case C.fapTELEMETRY:
		packet.PacketType = TelemetryPacketType
	case C.fapTELEMETRY_MESSAGE:
		packet.PacketType = TelemetryMessagePacketType
	case C.fapDX_SPOT:
		packet.PacketType = DXSpotPacketType
	case C.fapEXPERIMENTAL:
		packet.PacketType = ExperimentalPacketType
	default:
		packet.PacketType = InvalidPacketType
	}

	if cpacket.wx_report != nil {
		w := WeatherReport{
			Temperature:       parseNilableFloat(cpacket.wx_report.temp),
			InsideTemperature: parseNilableFloat(cpacket.wx_report.temp_in),
			Humidity:          parseNilableUInt(cpacket.wx_report.humidity),
			InsideHumidity:    parseNilableUInt(cpacket.wx_report.humidity_in),
			WindGust:          parseNilableFloat(cpacket.wx_report.wind_gust),
			WindDirection:     parseNilableUInt(cpacket.wx_report.wind_dir),
			WindSpeed:         parseNilableFloat(cpacket.wx_report.wind_speed),
			Pressure:          parseNilableFloat(cpacket.wx_report.pressure),
		}
		packet.Weather = &w
	}

	if cpacket.telemetry != nil {
		t := Telemetry{
			Val1: parseNilableFloat(cpacket.telemetry.val1),
			Val2: parseNilableFloat(cpacket.telemetry.val2),
			Val3: parseNilableFloat(cpacket.telemetry.val3),
			Val4: parseNilableFloat(cpacket.telemetry.val4),
			Val5: parseNilableFloat(cpacket.telemetry.val5),
		}
		packet.Telemetry = &t
	}

	// MicE alloc a buffer of 20 bytes for fap_mice_mbits_to_message C func
	cbuffer := (*C.char)(C.malloc(C.size_t(20)))
	defer C.free(unsafe.Pointer(cbuffer))

	if cpacket.messagebits != nil {
		C.fap_mice_mbits_to_message(cpacket.messagebits, cbuffer)
		packet.MicE = C.GoString(cbuffer)
	}

	return packet, nil
}

// IncludePosition return true if the packet contains a Position
func (p *Packet) IncludePosition() bool {
	if p.Latitude != InvalidCoordinate && p.Longitude != InvalidCoordinate {
		return true
	}
	return false
}

// return a short version of the callsign as KK6NXK for KK6NXK-7
func ShortCallsign(c string) string {
	s := strings.Split(c, "-")
	return s[0]
}

func boolToInt(b bool) int {
	if b {
		return 1
	}
	return 0
}

func parseNilableFloat(d *C.double) float64 {
	if d != nil {
		return float64(C.double(*d))
	}
	return 0
}

func parseNilableCoordinate(d *C.double) float64 {
	if d != nil {
		return float64(C.double(*d))
	}
	return InvalidCoordinate
}

func parseNilableUInt(d *C.uint) uint8 {
	if d != nil {
		return uint8(C.uint(*d))
	}
	return 0
}
<|endoftext|>


<commit_before>package gincrud

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/fatih/structs"
	"github.com/gin-gonic/gin"
	"github.com/mgutz/logxi/v1"
	"github.com/osiloke/gostore"
	"reflect"
	"runtime"
	"strconv"
	"time"
)

var logger = log.New("gincrud")

type ErrorList struct {
	Msg   string                 `json:"msg"`
	Error map[string]interface{} `json:"error"`
}

type ErrorCtx struct {
	Bucket string
	key    string
	GinCtx *gin.Context
}

type SuccessCtx struct {
	Bucket string
	Key    string
	Result map[string]interface{}
	GinCtx *gin.Context
}

type MarshalError struct {
	Data map[string]interface{}
}

type UnknownContent struct {
	S string `json:"msg"`
}

func (e *UnknownContent) Error() string {
	return e.S
}

type JSONError interface {
	Serialize() map[string]interface{} //serialize error to json
}

type ParsedContent map[string]interface{}

//Convert request json data to data and map, you can handle validation here
type MarshalFn func(ctx *gin.Context) (map[string]interface{}, error)
type UnMarshalFn func(*gin.Context, []byte) (map[string]interface{}, error)

//Get unique key from object and request
type GetKey func(interface{}, *gin.Context) string

//Called when a crud operation is successful
type OnSuccess func(ctx SuccessCtx) (string, error)

//Called when a crud operation fails
type OnError func(ctx interface{}, err error) error

type Results struct {
	Data       []map[string]interface{} `json:"data"`
	Count      int                      `json:"count,omitempty"`
	TotalCount int                      `json:"total_count,omitempty"`
}

const (
	FORM_CONTENT = "form"
	JSON_CONTENT = "json"
	XML_CONTENT  = "xml"
)

func timeTrack(start time.Time, name string) {
	elapsed := time.Since(start)
	log.Debug(fmt.Sprintf("%s took %s", name, elapsed))
}

func GetFunctionName(i interface{}) string {
	return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
}

func filterFlags(content string) string {
	for i, a := range content {
		if a == ' ' || a == ';' {
			return content[:i]
		}
	}
	return content
}
func Decode(c *gin.Context, obj interface{}) error {
	ctype := filterFlags(c.Request.Header.Get("Content-Type"))
	switch {
	case c.Request.Method == "GET" || ctype == gin.MIMEPOSTForm:
		return &UnknownContent{"unimplemented content-type: " + ctype}
	case ctype == gin.MIMEJSON:
		decoder := json.NewDecoder(c.Request.Body)
		if err := decoder.Decode(&obj); err == nil {
			return nil
		} else {
			return err
		}
	case ctype == gin.MIMEXML || ctype == gin.MIMEXML2:
		return &UnknownContent{"unimplemented content-type: " + ctype}
	default:
		err := &UnknownContent{"unknown content-type: " + ctype}
		return err
	}
}

func requestContent(c *gin.Context) (ParsedContent, error) {
	ctype := filterFlags(c.Request.Header.Get("Content-Type"))
	switch {
	case c.Request.Method == "GET" || ctype == gin.MIMEPOSTForm:
		return nil, errors.New("Unimplemented content-type: " + ctype)
	case ctype == gin.MIMEJSON:
		var obj ParsedContent
		decoder := json.NewDecoder(c.Request.Body)
		if err := decoder.Decode(obj); err == nil {
			return obj, err
		} else {
			return nil, err
		}
	case ctype == gin.MIMEXML || ctype == gin.MIMEXML2:
		return nil, errors.New("Unimplemented content-type: " + ctype)
	default:
		err := errors.New("unknown content-type: " + ctype)
		c.Fail(400, err)
		return nil, err
	}
}
func doUnmarshal(key, bucket string, data [][]byte, c *gin.Context, unMarshalFn UnMarshalFn, onSuccess OnSuccess, onError OnError) {

	defer timeTrack(time.Now(), "Do Unmarshal "+key+" from "+bucket)
	m, err := unMarshalFn(c, data[1])
	if m == nil {
		m = make(map[string]interface{})
	}
	m["key"] = string(data[0])
	if err != nil {
		c.JSON(500, err)
	} else {
		kk := string(data[0])
		if onSuccess != nil {
			ctx := SuccessCtx{bucket, kk, m, c}
			onSuccess(ctx)
		}
		c.JSON(200, m)
	}
}
func Get(key, bucket string, store gostore.Store, c *gin.Context, record interface{},
	unMarshalFn UnMarshalFn, onSuccess OnSuccess, onError OnError) {
	data, err := store.Get([]byte(key), bucket)
	if err != nil {
		//TODO: Does not exist error for store
		if onError != nil {
			onError(ErrorCtx{bucket, key, c}, err)
		}
		c.JSON(404, gin.H{"msg": fmt.Sprintf("%s Not found", key)})
	} else {
		if unMarshalFn != nil {
			doUnmarshal(key, bucket, data, c, unMarshalFn, onSuccess, onError)
		} else {
			_ = json.Unmarshal(data[1], record)
			m := structs.Map(record)
			kk := string(data[0])
			m["key"] = kk
			if onSuccess != nil {
				ctx := SuccessCtx{bucket, kk, m, c}
				onSuccess(ctx)
			}
			c.JSON(200, m)
		}
	}
}

//TODO: Extract core logic from each crud function i.e make doGetAll, doGet, ... they return data, err
func GetAll(bucket string, store gostore.Store, c *gin.Context, onSuccess OnSuccess, onError OnError) {
	var results []map[string]interface{}
	var err error

	count := 10
	q := c.Request.URL.Query()
	if val, ok := q["_perPage"]; ok {
		count, _ = strconv.Atoi(val[0])
	}
	var data [][][]byte

	if val, ok := q["afterKey"]; ok {
		data, err = store.GetAllAfter([]byte(val[0]), count+1, 0, bucket)
	} else if val, ok := q["beforeKey"]; ok {
		data, err = store.GetAllBefore([]byte(val[0]), count+1, 0, bucket)
	} else {
		data, err = store.GetAll(count+1, 0, bucket)
	}
	if err != nil {
		if onError != nil {
			onError(ErrorCtx{Bucket: bucket, GinCtx: c}, err)
		}
		c.JSON(200, []string{})
	} else {
		for _, element := range data {
			var result map[string]interface{}
			if err := json.Unmarshal(element[1], &result); err != nil {
				onError(ErrorCtx{Bucket: bucket, GinCtx: c}, err)
				c.JSON(500, gin.H{"msg": err})
			} else {
				if result == nil {
					result = make(map[string]interface{})
				}

				result["key"] = string(element[0])
				results = append(results, result)
			}
		}
		if len(results) == 0 {
			c.JSON(200, []string{})
		} else {
			if onSuccess != nil {
			}
			stats, _ := store.Stats(bucket)
			total_count := stats["KeyN"].(int)
			c.Writer.Header().Set("X-Total-Count", fmt.Sprintf("%d", total_count))
			c.JSON(200, Results{results, count, total_count})
			// c.JSON(200, results)
		}
	}
}

func Post(bucket string, store gostore.Store, c *gin.Context,
	record interface{}, fn GetKey, marshalFn MarshalFn, onSuccess OnSuccess, onError OnError) {

	defer func() {
		if r := recover(); r != nil {
			trace := make([]byte, 1024)
			runtime.Stack(trace, true)
			fmt.Printf("Stack: %s", trace)
			//				log.Error("Stack of %d bytes: %s", count, trace)
			//				fmt.Println("Defer Panic in auth middleware:", r)
			logger.Error("POST:", "err", string(trace))
			c.JSON(500, gin.H{"message": "Unable to edit item "})
			c.Abort()
		}
	}()
	if marshalFn != nil {
		logger.Debug("Post", "bucket", bucket, "marshalfn", GetFunctionName(marshalFn))
		obj, err := marshalFn(c)
		if err != nil {
			if onError != nil {
				onError(ErrorCtx{Bucket: bucket}, err)
			}
			if e, ok := err.(JSONError); ok {
				result := ErrorList{"Malformed data", e.Serialize()}
				c.JSON(400, result)
			} else {
				c.JSON(400, gin.H{"msg": err})
			}

		} else {
			key := fn(obj, c)
			if key == "" {
				c.JSON(500, err)
			} else {
				data, err := json.Marshal(obj)
				if err != nil {
					onError(ErrorCtx{Bucket: bucket}, err)
				} else {
					store.Save([]byte(key), data, bucket)
					if onSuccess != nil {

						logger.Debug("onSuccess", "bucket", bucket, "key", key, "onSuccess", GetFunctionName(onSuccess))
						ctx := SuccessCtx{bucket, key, obj, c}
						onSuccess(ctx)
					}
					obj["key"] = key
					c.JSON(200, obj)
				}
			}
		}

	} else {
		if b := c.Bind(record); b != false {
			m := structs.Map(record)
			data, err := json.Marshal(&record)
			key := fn(m, c)
			if err != nil {
				onError(ErrorCtx{Bucket: bucket}, err)
				c.JSON(500, gin.H{"msg": "An error occured and this item could not be saved"})
			} else {
				store.Save([]byte([]byte(key)), data, bucket)
				m["key"] = key
				logger.Debug("Successfully saved object", "bucket", bucket, "key", key)

				if onSuccess != nil {
					ctx := SuccessCtx{bucket, key, m, c}
					onSuccess(ctx)
				}
				c.JSON(200, m)
			}
		} else {
			c.JSON(400, gin.H{"msg": "Seems like the data submitted is not formatted properly"})
		}
	}
}

func Put(key, bucket string, store gostore.Store, c *gin.Context, record interface{},
	marshalFn MarshalFn, onSuccess OnSuccess, onError OnError) {
	defer func() {
		if r := recover(); r != nil {
			trace := make([]byte, 1024)
			runtime.Stack(trace, true)
			fmt.Printf("Stack: %s", trace)
			//				log.Error("Stack of %d bytes: %s", count, trace)
			//				fmt.Println("Defer Panic in auth middleware:", r)
			logger.Error("Defer Panic in Gincrud PUT:", "err", string(trace))
			c.JSON(500, gin.H{"message": "Unable to edit item "})
			c.Abort()
		}
	}()
	if marshalFn != nil {
		obj, err := marshalFn(c)
		if err != nil {
			if onError != nil {
				onError(ErrorCtx{Bucket: bucket}, err)
			}
			if e, ok := err.(JSONError); ok {
				result := ErrorList{"Malformed data", e.Serialize()}
				c.JSON(400, result)
			} else {
				c.JSON(400, gin.H{"msg": err.Error()})
			}

		} else {
			data, err := json.Marshal(obj)
			if err != nil {
				onError(ErrorCtx{Bucket: bucket}, err)
			} else {
				store.Save([]byte([]byte(key)), data, bucket)
				if onSuccess != nil {
					ctx := SuccessCtx{bucket, key, obj, c}
					onSuccess(ctx)
				}
				c.JSON(200, obj)
			}
		}

	} else {
		if b := c.Bind(record); b != false {
			m := structs.Map(record)
			data, err := json.Marshal(&record)
			if err != nil {
				onError(ErrorCtx{Bucket: bucket}, err)
				c.JSON(500, gin.H{"msg": "An error occured and this item could not be saved"})
			} else {
				store.Save([]byte([]byte(key)), data, bucket)
				m["key"] = key

				if onSuccess != nil {
					ctx := SuccessCtx{bucket, key, m, c}
					onSuccess(ctx)
				}
				c.JSON(200, m)
			}
		} else {
			c.JSON(400, gin.H{"msg": "Seems like the data submitted is not formatted properly"})
		}
	}
}

func Delete(key, bucket string, store gostore.Store, c *gin.Context, onSuccess OnSuccess, onError OnError) {
	err := store.Delete([]byte(key), bucket)
	if err != nil {
		if onError != nil {
			onError(ErrorCtx{bucket, key, c}, err)
		}
		c.JSON(500, gin.H{"msg": "The item [" + key + "] was not deleted"})
	} else {
		if onSuccess != nil {
			ctx := SuccessCtx{Bucket: bucket, Key: key, GinCtx: c}
			onSuccess(ctx)
		}
		c.JSON(200, gin.H{"msg": "The item [" + key + "] was deleted"})
	}
}
<commit_msg>GetAll now accepts an UnMarshalFn<commit_after>package gincrud

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/fatih/structs"
	"github.com/gin-gonic/gin"
	"github.com/mgutz/logxi/v1"
	"github.com/osiloke/gostore"
	"reflect"
	"runtime"
	"strconv"
	"time"
)

var logger = log.New("gincrud")

type ErrorList struct {
	Msg   string                 `json:"msg"`
	Error map[string]interface{} `json:"error"`
}

type ErrorCtx struct {
	Bucket string
	key    string
	GinCtx *gin.Context
}

type SuccessCtx struct {
	Bucket string
	Key    string
	Result map[string]interface{}
	GinCtx *gin.Context
}

type MarshalError struct {
	Data map[string]interface{}
}

type UnknownContent struct {
	S string `json:"msg"`
}

func (e *UnknownContent) Error() string {
	return e.S
}

type JSONError interface {
	Serialize() map[string]interface{} //serialize error to json
}

type ParsedContent map[string]interface{}

//Convert request json data to data and map, you can handle validation here
type MarshalFn func(ctx *gin.Context) (map[string]interface{}, error)
type UnMarshalFn func(*gin.Context, [][]byte) (map[string]interface{}, error)

//Get unique key from object and request
type GetKey func(interface{}, *gin.Context) string

//Called when a crud operation is successful
type OnSuccess func(ctx SuccessCtx) (string, error)

//Called when a crud operation fails
type OnError func(ctx interface{}, err error) error

type Results struct {
	Data       []map[string]interface{} `json:"data"`
	Count      int                      `json:"count,omitempty"`
	TotalCount int                      `json:"total_count,omitempty"`
}

const (
	FORM_CONTENT = "form"
	JSON_CONTENT = "json"
	XML_CONTENT  = "xml"
)

func timeTrack(start time.Time, name string) {
	elapsed := time.Since(start)
	log.Debug(fmt.Sprintf("%s took %s", name, elapsed))
}

func GetFunctionName(i interface{}) string {
	return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
}

func filterFlags(content string) string {
	for i, a := range content {
		if a == ' ' || a == ';' {
			return content[:i]
		}
	}
	return content
}
func Decode(c *gin.Context, obj interface{}) error {
	ctype := filterFlags(c.Request.Header.Get("Content-Type"))
	switch {
	case c.Request.Method == "GET" || ctype == gin.MIMEPOSTForm:
		return &UnknownContent{"unimplemented content-type: " + ctype}
	case ctype == gin.MIMEJSON:
		decoder := json.NewDecoder(c.Request.Body)
		if err := decoder.Decode(&obj); err == nil {
			return nil
		} else {
			return err
		}
	case ctype == gin.MIMEXML || ctype == gin.MIMEXML2:
		return &UnknownContent{"unimplemented content-type: " + ctype}
	default:
		err := &UnknownContent{"unknown content-type: " + ctype}
		return err
	}
}

func requestContent(c *gin.Context) (ParsedContent, error) {
	ctype := filterFlags(c.Request.Header.Get("Content-Type"))
	switch {
	case c.Request.Method == "GET" || ctype == gin.MIMEPOSTForm:
		return nil, errors.New("Unimplemented content-type: " + ctype)
	case ctype == gin.MIMEJSON:
		var obj ParsedContent
		decoder := json.NewDecoder(c.Request.Body)
		if err := decoder.Decode(obj); err == nil {
			return obj, err
		} else {
			return nil, err
		}
	case ctype == gin.MIMEXML || ctype == gin.MIMEXML2:
		return nil, errors.New("Unimplemented content-type: " + ctype)
	default:
		err := errors.New("unknown content-type: " + ctype)
		c.Fail(400, err)
		return nil, err
	}
}
func doSingleUnmarshal(bucket string, item [][]byte, c *gin.Context, unMarshalFn UnMarshalFn) (data map[string]interface{}, err error) {
	key := string(item[0])
	defer timeTrack(time.Now(), "Do Single Unmarshal "+key+" from "+bucket)
	data, err = unMarshalFn(c, item)
	if err != nil {
		return nil, err
	}
	data["key"] = string(key)
	return
}
func doUnmarshal(key, bucket string, data [][]byte, c *gin.Context, unMarshalFn UnMarshalFn, onSuccess OnSuccess, onError OnError) {

	defer timeTrack(time.Now(), "Do Unmarshal "+key+" from "+bucket)
	m, err := unMarshalFn(c, data)
	if m == nil {
		m = make(map[string]interface{})
	}
	m["key"] = string(data[0])
	if err != nil {
		c.JSON(500, err)
	} else {
		kk := string(data[0])
		if onSuccess != nil {
			ctx := SuccessCtx{bucket, kk, m, c}
			onSuccess(ctx)
		}
		c.JSON(200, m)
	}
}
func Get(key, bucket string, store gostore.Store, c *gin.Context, record interface{},
	unMarshalFn UnMarshalFn, onSuccess OnSuccess, onError OnError) {
	data, err := store.Get([]byte(key), bucket)
	if err != nil {
		//TODO: Does not exist error for store
		if onError != nil {
			onError(ErrorCtx{bucket, key, c}, err)
		}
		c.JSON(404, gin.H{"msg": fmt.Sprintf("%s Not found", key)})
	} else {
		if unMarshalFn != nil {
			doUnmarshal(key, bucket, data, c, unMarshalFn, onSuccess, onError)
		} else {
			_ = json.Unmarshal(data[1], record)
			m := structs.Map(record)
			kk := string(data[0])
			m["key"] = kk
			if onSuccess != nil {
				ctx := SuccessCtx{bucket, kk, m, c}
				onSuccess(ctx)
			}
			c.JSON(200, m)
		}
	}
}

//TODO: Extract core logic from each crud function i.e make doGetAll, doGet, ... they return data, err
func GetAll(bucket string, store gostore.Store, c *gin.Context, unMarshalFn UnMarshalFn, onSuccess OnSuccess, onError OnError) {
	var results []map[string]interface{}
	var err error

	count := 10
	q := c.Request.URL.Query()
	if val, ok := q["_perPage"]; ok {
		count, _ = strconv.Atoi(val[0])
	}
	var data [][][]byte

	if val, ok := q["afterKey"]; ok {
		data, err = store.GetAllAfter([]byte(val[0]), count+1, 0, bucket)
	} else if val, ok := q["beforeKey"]; ok {
		data, err = store.GetAllBefore([]byte(val[0]), count+1, 0, bucket)
	} else {
		data, err = store.GetAll(count+1, 0, bucket)
	}
	if err != nil {
		if onError != nil {
			onError(ErrorCtx{Bucket: bucket, GinCtx: c}, err)
		}
		c.JSON(200, []string{})
	} else {
		if unMarshalFn != nil {
			for _, element := range data {
				data, err := doSingleUnmarshal(bucket, element, c, unMarshalFn)
				if err == nil {
					results = append(results, data)
				}
			}
		} else {
			for _, element := range data {
				var result map[string]interface{}
				if err := json.Unmarshal(element[1], &result); err != nil {
					onError(ErrorCtx{Bucket: bucket, GinCtx: c}, err)
					c.JSON(500, gin.H{"msg": err})
				} else {
					if result == nil {
						result = make(map[string]interface{})
					}

					result["key"] = string(element[0])
					results = append(results, result)
				}
			}
		}
		if len(results) == 0 {
			c.JSON(200, []string{})
		} else {
			if onSuccess != nil {
			}
			stats, _ := store.Stats(bucket)
			total_count := stats["KeyN"].(int)
			c.Writer.Header().Set("X-Total-Count", fmt.Sprintf("%d", total_count))
			c.JSON(200, Results{results, count, total_count})
			// c.JSON(200, results)
		}
	}
}

func Post(bucket string, store gostore.Store, c *gin.Context,
	record interface{}, fn GetKey, marshalFn MarshalFn, onSuccess OnSuccess, onError OnError) {

	defer func() {
		if r := recover(); r != nil {
			trace := make([]byte, 1024)
			runtime.Stack(trace, true)
			fmt.Printf("Stack: %s", trace)
			//				log.Error("Stack of %d bytes: %s", count, trace)
			//				fmt.Println("Defer Panic in auth middleware:", r)
			logger.Error("POST:", "err", string(trace))
			c.JSON(500, gin.H{"message": "Unable to edit item "})
			c.Abort()
		}
	}()
	if marshalFn != nil {
		logger.Debug("Post", "bucket", bucket, "marshalfn", GetFunctionName(marshalFn))
		obj, err := marshalFn(c)
		if err != nil {
			if onError != nil {
				onError(ErrorCtx{Bucket: bucket}, err)
			}
			if e, ok := err.(JSONError); ok {
				result := ErrorList{"Malformed data", e.Serialize()}
				c.JSON(400, result)
			} else {
				c.JSON(400, gin.H{"msg": err})
			}

		} else {
			key := fn(obj, c)
			if key == "" {
				c.JSON(500, err)
			} else {
				data, err := json.Marshal(obj)
				if err != nil {
					onError(ErrorCtx{Bucket: bucket}, err)
				} else {
					store.Save([]byte(key), data, bucket)
					if onSuccess != nil {

						logger.Debug("onSuccess", "bucket", bucket, "key", key, "onSuccess", GetFunctionName(onSuccess))
						ctx := SuccessCtx{bucket, key, obj, c}
						onSuccess(ctx)
					}
					obj["key"] = key
					c.JSON(200, obj)
				}
			}
		}

	} else {
		if b := c.Bind(record); b != false {
			m := structs.Map(record)
			data, err := json.Marshal(&record)
			key := fn(m, c)
			if err != nil {
				onError(ErrorCtx{Bucket: bucket}, err)
				c.JSON(500, gin.H{"msg": "An error occured and this item could not be saved"})
			} else {
				store.Save([]byte([]byte(key)), data, bucket)
				m["key"] = key
				logger.Debug("Successfully saved object", "bucket", bucket, "key", key)

				if onSuccess != nil {
					ctx := SuccessCtx{bucket, key, m, c}
					onSuccess(ctx)
				}
				c.JSON(200, m)
			}
		} else {
			c.JSON(400, gin.H{"msg": "Seems like the data submitted is not formatted properly"})
		}
	}
}

func Put(key, bucket string, store gostore.Store, c *gin.Context, record interface{},
	marshalFn MarshalFn, onSuccess OnSuccess, onError OnError) {
	defer func() {
		if r := recover(); r != nil {
			trace := make([]byte, 1024)
			runtime.Stack(trace, true)
			fmt.Printf("Stack: %s", trace)
			//				log.Error("Stack of %d bytes: %s", count, trace)
			//				fmt.Println("Defer Panic in auth middleware:", r)
			logger.Error("Defer Panic in Gincrud PUT:", "err", string(trace))
			c.JSON(500, gin.H{"message": "Unable to edit item "})
			c.Abort()
		}
	}()
	if marshalFn != nil {
		obj, err := marshalFn(c)
		if err != nil {
			if onError != nil {
				onError(ErrorCtx{Bucket: bucket}, err)
			}
			if e, ok := err.(JSONError); ok {
				result := ErrorList{"Malformed data", e.Serialize()}
				c.JSON(400, result)
			} else {
				c.JSON(400, gin.H{"msg": err.Error()})
			}

		} else {
			data, err := json.Marshal(obj)
			if err != nil {
				onError(ErrorCtx{Bucket: bucket}, err)
			} else {
				store.Save([]byte([]byte(key)), data, bucket)
				if onSuccess != nil {
					ctx := SuccessCtx{bucket, key, obj, c}
					onSuccess(ctx)
				}
				c.JSON(200, obj)
			}
		}

	} else {
		if b := c.Bind(record); b != false {
			m := structs.Map(record)
			data, err := json.Marshal(&record)
			if err != nil {
				onError(ErrorCtx{Bucket: bucket}, err)
				c.JSON(500, gin.H{"msg": "An error occured and this item could not be saved"})
			} else {
				store.Save([]byte([]byte(key)), data, bucket)
				m["key"] = key

				if onSuccess != nil {
					ctx := SuccessCtx{bucket, key, m, c}
					onSuccess(ctx)
				}
				c.JSON(200, m)
			}
		} else {
			c.JSON(400, gin.H{"msg": "Seems like the data submitted is not formatted properly"})
		}
	}
}

func Delete(key, bucket string, store gostore.Store, c *gin.Context, onSuccess OnSuccess, onError OnError) {
	err := store.Delete([]byte(key), bucket)
	if err != nil {
		if onError != nil {
			onError(ErrorCtx{bucket, key, c}, err)
		}
		c.JSON(500, gin.H{"msg": "The item [" + key + "] was not deleted"})
	} else {
		if onSuccess != nil {
			ctx := SuccessCtx{Bucket: bucket, Key: key, GinCtx: c}
			onSuccess(ctx)
		}
		c.JSON(200, gin.H{"msg": "The item [" + key + "] was deleted"})
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/md5"
	"encoding/json"
	"fmt"
	"github.com/golang/groupcache"
	"github.com/gorilla/mux"
	"io"
	"math/rand"
	"net/http"
	"os"
	"time"
	"vip/fetch"
)

type UploadResponse struct {
	Url string `json:"url"`
}

type verifyAuth func(http.ResponseWriter, *http.Request)

func (h verifyAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Enable cross-origin requests
	if domain := os.Getenv("ALLOWED_ORIGIN"); domain != "" {
		if origin := r.Header.Get("Origin"); origin == domain {
			w.Header().Set("Access-Control-Allow-Origin", origin)
			w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
			w.Header().Set("Access-Control-Allow-Headers",
				"Accept, Content-Type, Content-Length, Accept-Encoding, X-Vip-Token, Authorization")
		}
	} else {
		auth := r.Header.Get("X-Vip-Token")
		if auth != authToken {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}
	}

	if r.Method == "OPTIONS" {
		return
	}

	h(w, r)
}

func fileKey(bucket string) string {
	seed := rand.New(rand.NewSource(time.Now().UnixNano()))
	key := fmt.Sprintf("%d-%s-%d", seed.Int63(), bucket, time.Now().UnixNano())

	hash := md5.New()
	io.WriteString(hash, key)
	return fmt.Sprintf("%x", hash.Sum(nil))
}

func handleImageRequest(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}

	w.Header().Set("Cache-Control", "public, max-age=31536000")

	// Client is checking for a cached URI, assume it is valid
	// and return a 304
	if r.Header.Get("If-Modified-Since") != "" {
		w.WriteHeader(http.StatusNotModified)
		return
	}

	gc := fetch.RequestContext(r)

	var data []byte
	err := cache.Get(gc, gc.CacheKey(), groupcache.AllocatingByteSliceSink(&data))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

	w.Header().Set("Content-Type", http.DetectContentType(data))
	http.ServeContent(w, r, gc.ImageId, time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), bytes.NewReader(data))
}

func handleUpload(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	vars := mux.Vars(r)
	bucket := vars["bucket_id"]

	// Set a hard 5mb limit on files
	if r.ContentLength > 5<<20 {
		w.WriteHeader(http.StatusRequestEntityTooLarge)
		return
	}

	key := fileKey(bucket)
	err := storage.PutReader(bucket, key, r.Body,
		r.ContentLength, r.Header.Get("Content-Type"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer r.Body.Close()

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	uri := r.URL

	if r.URL.Host == "" {
		uri.Host = os.Getenv("URI_HOSTNAME")
		if *secure {
			uri.Scheme = "https"
		} else {
			uri.Scheme = "http"
		}
	}

	uri.Path = fmt.Sprintf("%s/%s", bucket, key)

	w.Header().Set("Content-Type", "application/json")

	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(UploadResponse{
		Url: uri.String(),
	})
}

func handlePing(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, "pong")
}
<commit_msg>Add error message for 413 response<commit_after>package main

import (
	"bytes"
	"crypto/md5"
	"encoding/json"
	"fmt"
	"github.com/golang/groupcache"
	"github.com/gorilla/mux"
	"io"
	"math/rand"
	"net/http"
	"os"
	"time"
	"vip/fetch"
)

type UploadResponse struct {
	Url string `json:"url"`
}

type verifyAuth func(http.ResponseWriter, *http.Request)

func (h verifyAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Enable cross-origin requests
	if domain := os.Getenv("ALLOWED_ORIGIN"); domain != "" {
		if origin := r.Header.Get("Origin"); origin == domain {
			w.Header().Set("Access-Control-Allow-Origin", origin)
			w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
			w.Header().Set("Access-Control-Allow-Headers",
				"Accept, Content-Type, Content-Length, Accept-Encoding, X-Vip-Token, Authorization")
		}
	} else {
		auth := r.Header.Get("X-Vip-Token")
		if auth != authToken {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}
	}

	if r.Method == "OPTIONS" {
		return
	}

	h(w, r)
}

func fileKey(bucket string) string {
	seed := rand.New(rand.NewSource(time.Now().UnixNano()))
	key := fmt.Sprintf("%d-%s-%d", seed.Int63(), bucket, time.Now().UnixNano())

	hash := md5.New()
	io.WriteString(hash, key)
	return fmt.Sprintf("%x", hash.Sum(nil))
}

func handleImageRequest(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		w.WriteHeader(http.StatusMethodNotAllowed)
	}

	w.Header().Set("Cache-Control", "public, max-age=31536000")

	// Client is checking for a cached URI, assume it is valid
	// and return a 304
	if r.Header.Get("If-Modified-Since") != "" {
		w.WriteHeader(http.StatusNotModified)
		return
	}

	gc := fetch.RequestContext(r)

	var data []byte
	err := cache.Get(gc, gc.CacheKey(), groupcache.AllocatingByteSliceSink(&data))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

	w.Header().Set("Content-Type", http.DetectContentType(data))
	http.ServeContent(w, r, gc.ImageId, time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), bytes.NewReader(data))
}

func handleUpload(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	vars := mux.Vars(r)
	bucket := vars["bucket_id"]

	// Set a hard 5mb limit on files
	limit := 5
	if r.ContentLength > limit<<20 {
		errMsg := fmt.Printf("The file size limit is %dMB.\n", limit)
		http.Error(w, errMsg, http.StatusRequestEntityTooLarge)
		return
	}

	key := fileKey(bucket)
	err := storage.PutReader(bucket, key, r.Body,
		r.ContentLength, r.Header.Get("Content-Type"))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	defer r.Body.Close()

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	uri := r.URL

	if r.URL.Host == "" {
		uri.Host = os.Getenv("URI_HOSTNAME")
		if *secure {
			uri.Scheme = "https"
		} else {
			uri.Scheme = "http"
		}
	}

	uri.Path = fmt.Sprintf("%s/%s", bucket, key)

	w.Header().Set("Content-Type", "application/json")

	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(UploadResponse{
		Url: uri.String(),
	})
}

func handlePing(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	fmt.Fprintf(w, "pong")
}
<|endoftext|>


<commit_before>/*
Package log provides a handler that logs each request/response (time, duration, status, method, path).

The log formatting can either be couloured or not.

Make sure to include this handler above any other handler to get accurate performance logs.
*/
package log

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

const (
	cReset    = "\033[0m"
	cDim      = "\033[2m"
	cRed      = "\033[31m"
	cGreen    = "\033[32m"
	cBlue     = "\033[34m"
	cCyan     = "\033[36m"
	cWhite    = "\033[97m"
	cBgRed    = "\033[41m"
	cBgGreen  = "\033[42m"
	cBgYellow = "\033[43m"
	cBgCyan   = "\033[46m"
)

// A handler provides a request/response logging handler.
type handler struct {
	options *Options
	next    http.Handler
}

// Options provides the handler options.
type Options struct {
	Color bool // Colors triggers a coloured formatting compatible with Unix-based terminals.
}

// Handle returns a Handler wrapping another http.Handler.
func Handle(h http.Handler, o *Options) http.Handler {
	return &handler{o, h}
}

// HandleFunc returns a Handler wrapping an http.HandlerFunc.
func HandleFunc(f http.HandlerFunc, o *Options) http.Handler {
	return Handle(f, o)
}

func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	start := time.Now()
	lw := &logWriter{
		ResponseWriter: w,
	}
	// Keep originals in case the response will be altered.
	method := r.Method
	path := r.URL.Path

	defer func() {
		if lw.status == 0 {
			lw.status = http.StatusOK
		}

		if h.options == nil || !h.options.Color {
			log.Printf("%s %s ▶ %d @ %s", method, path, lw.status, time.Since(start))
			return
		}

		var cBgStatus string
		switch {
		case lw.status >= 200 && lw.status <= 299:
			cBgStatus += cBgGreen
		case lw.status >= 300 && lw.status <= 399:
			cBgStatus += cBgCyan
		case lw.status >= 400 && lw.status <= 499:
			cBgStatus += cBgYellow
		default:
			cBgStatus += cBgRed
		}

		var cMethod string
		switch method {
		case "GET":
			cMethod += cGreen
		case "POST":
			cMethod += cCyan
		case "PUT", "PATCH":
			cMethod += cBlue
		case "DELETE":
			cMethod += cRed
		}

		log.Printf("%s  %s%13s%s   %s%s %3d %s   %s%s%s  %s%s%s", cReset, cDim, time.Since(start), cReset, cWhite, cBgStatus, lw.status, cReset, cMethod, method, cReset, cDim, path, cReset)
	}()

	h.next.ServeHTTP(lw, r)
}

// logWriter catches the status code from WriteHeader.
type logWriter struct {
	http.ResponseWriter
	status int
}

func (lw *logWriter) WriteHeader(status int) {
	if lw.status == 0 {
		lw.status = status
	}
	lw.ResponseWriter.WriteHeader(status)
}
<commit_msg>Implement CloseNotify, Flush, Hijack and Push<commit_after>/*
Package log provides a handler that logs each request/response (time, duration, status, method, path).

The log formatting can either be couloured or not.

Make sure to include this handler above any other handler to get accurate performance logs.
*/
package log

import (
	"bufio"
	"log"
	"net"
	"net/http"
	"time"
)

const (
	cReset    = "\033[0m"
	cDim      = "\033[2m"
	cRed      = "\033[31m"
	cGreen    = "\033[32m"
	cBlue     = "\033[34m"
	cCyan     = "\033[36m"
	cWhite    = "\033[97m"
	cBgRed    = "\033[41m"
	cBgGreen  = "\033[42m"
	cBgYellow = "\033[43m"
	cBgCyan   = "\033[46m"
)

// A handler provides a request/response logging handler.
type handler struct {
	options *Options
	next    http.Handler
}

// Options provides the handler options.
type Options struct {
	Color bool // Colors triggers a coloured formatting compatible with Unix-based terminals.
}

// Handle returns a Handler wrapping another http.Handler.
func Handle(h http.Handler, o *Options) http.Handler {
	return &handler{o, h}
}

// HandleFunc returns a Handler wrapping an http.HandlerFunc.
func HandleFunc(f http.HandlerFunc, o *Options) http.Handler {
	return Handle(f, o)
}

func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	start := time.Now()
	lw := &logWriter{
		ResponseWriter: w,
	}
	// Keep originals in case the response will be altered.
	method := r.Method
	path := r.URL.Path

	defer func() {
		if lw.status == 0 {
			lw.status = http.StatusOK
		}

		if h.options == nil || !h.options.Color {
			log.Printf("%s %s ▶ %d @ %s", method, path, lw.status, time.Since(start))
			return
		}

		var cBgStatus string
		switch {
		case lw.status >= 200 && lw.status <= 299:
			cBgStatus += cBgGreen
		case lw.status >= 300 && lw.status <= 399:
			cBgStatus += cBgCyan
		case lw.status >= 400 && lw.status <= 499:
			cBgStatus += cBgYellow
		default:
			cBgStatus += cBgRed
		}

		var cMethod string
		switch method {
		case "GET":
			cMethod += cGreen
		case "POST":
			cMethod += cCyan
		case "PUT", "PATCH":
			cMethod += cBlue
		case "DELETE":
			cMethod += cRed
		}

		log.Printf("%s  %s%13s%s   %s%s %3d %s   %s%s%s  %s%s%s", cReset, cDim, time.Since(start), cReset, cWhite, cBgStatus, lw.status, cReset, cMethod, method, cReset, cDim, path, cReset)
	}()

	h.next.ServeHTTP(lw, r)
}

// logWriter catches the status code from WriteHeader.
type logWriter struct {
	http.ResponseWriter
	status int
}

func (lw *logWriter) WriteHeader(status int) {
	if lw.status == 0 {
		lw.status = status
	}
	lw.ResponseWriter.WriteHeader(status)
}

// CloseNotify implements the http.CloseNotifier interface.
// No channel is returned if CloseNotify is not implemented by an upstream response writer.
func (lw *logWriter) CloseNotify() <-chan bool {
	n, ok := lw.ResponseWriter.(http.CloseNotifier)
	if !ok {
		return nil
	}
	return n.CloseNotify()
}

// Flush implements the http.Flusher interface.
// Nothing is done if Flush is not implemented by an upstream response writer.
func (lw *logWriter) Flush() {
	f, ok := lw.ResponseWriter.(http.Flusher)
	if ok {
		f.Flush()
	}
}

// Hijack implements the http.Hijacker interface.
// Error http.ErrNotSupported is returned if Hijack is not implemented by an upstream response writer.
func (lw *logWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	h, ok := lw.ResponseWriter.(http.Hijacker)
	if !ok {
		return nil, nil, http.ErrNotSupported
	}
	return h.Hijack()
}

// Push implements the http.Pusher interface.
// http.ErrNotSupported is returned if Push is not implemented by an upstream response writer or not supported by the client.
func (lw *logWriter) Push(target string, opts *http.PushOptions) error {
	p, ok := lw.ResponseWriter.(http.Pusher)
	if !ok {
		return http.ErrNotSupported
	}
	return p.Push(target, opts)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"go/model"
	"golang.org/x/net/websocket"
	"log"
	"net/http"
	"strconv"
	"strings"
)

type reqHandler struct {
	*Context
	Fn func(*Context, http.ResponseWriter, *http.Request) (int, error)
}

// ServeHTTP is called on a reqHandler by net/http; Satisfies http.Handler
func (h reqHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	status, err := h.Fn(h.Context, w, r)
	if err != nil {
		switch status {
		case http.StatusNotFound:
			http.NotFound(w, r)
		case http.StatusBadRequest:
			http.Error(w, err.Error(), status)
		default:
			status = http.StatusInternalServerError
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		}
	}
	log.Printf("%s %s %s %d", strings.Split(r.RemoteAddr, ":")[0], r.Method, r.URL.Path, status)
}

// Renders the home and about templates
func rootHandler(c *Context, w http.ResponseWriter, r *http.Request) (int, error) {
	switch r.URL.Path {
	case "/":
		return http.StatusOK, renderTemplate(c, w, "home", nil)
	case "/about":
		return http.StatusOK, renderTemplate(c, w, "about", nil)
	default:
		return http.StatusNotFound, errors.New("handler: page not found")
	}
}

// Renders the game template
func gameHandler(c *Context, w http.ResponseWriter, r *http.Request) (int, error) {
	if r.Method == "POST" {
		size, _ := strconv.Atoi(r.FormValue("size"))
		var black, white string
		if r.FormValue("color") == "black" {
			black = r.FormValue("player_1")
			white = r.FormValue("player_2")
		} else {
			black = r.FormValue("player_2")
			white = r.FormValue("player_1")
		}
		game, err := model.New(black, white, size)
		if err != nil {
			return http.StatusBadRequest, err
		}
		http.Redirect(w, r, "/game/"+game.Id, 303)
		return http.StatusSeeOther, nil
	} else {
		id := r.URL.Path[6:]
		game, err := model.Load(id)
		if err != nil {
			return http.StatusNotFound, err
		}
		if r.Method == "PATCH" {
			x, _ := strconv.Atoi(r.FormValue("x"))
			y, _ := strconv.Atoi(r.FormValue("y"))
			err = game.Move(x, y)
			if err != nil {
				return http.StatusBadRequest, err
			}
			return http.StatusOK, nil
		} else {
			return http.StatusOK, renderTemplate(c, w, "game", game)
		}
	}
}

// Sends game updates to a WebSocket connection
func liveHandler(ws *websocket.Conn) {
	r := ws.Request()
	log.Printf("%s %s %s websocket", strings.Split(r.RemoteAddr, ":")[0], r.Method, r.URL.Path)

	id := r.URL.Path[11:]

	model.Subscribe(id, func(g *model.Game) {
		log.Printf("Sending WebSocket message for game %s", g.Id)
		err := json.NewEncoder(ws).Encode(g)
		if err != nil {
			log.Fatalf(err.Error())
		}
	})
}
<commit_msg>Use full import path for internal packages<commit_after>package main

import (
	"encoding/json"
	"errors"
	"github.com/waits/go/model"
	"golang.org/x/net/websocket"
	"log"
	"net/http"
	"strconv"
	"strings"
)

type reqHandler struct {
	*Context
	Fn func(*Context, http.ResponseWriter, *http.Request) (int, error)
}

// ServeHTTP is called on a reqHandler by net/http; Satisfies http.Handler
func (h reqHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	status, err := h.Fn(h.Context, w, r)
	if err != nil {
		switch status {
		case http.StatusNotFound:
			http.NotFound(w, r)
		case http.StatusBadRequest:
			http.Error(w, err.Error(), status)
		default:
			status = http.StatusInternalServerError
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		}
	}
	log.Printf("%s %s %s %d", strings.Split(r.RemoteAddr, ":")[0], r.Method, r.URL.Path, status)
}

// Renders the home and about templates
func rootHandler(c *Context, w http.ResponseWriter, r *http.Request) (int, error) {
	switch r.URL.Path {
	case "/":
		return http.StatusOK, renderTemplate(c, w, "home", nil)
	case "/about":
		return http.StatusOK, renderTemplate(c, w, "about", nil)
	default:
		return http.StatusNotFound, errors.New("handler: page not found")
	}
}

// Renders the game template
func gameHandler(c *Context, w http.ResponseWriter, r *http.Request) (int, error) {
	if r.Method == "POST" {
		size, _ := strconv.Atoi(r.FormValue("size"))
		var black, white string
		if r.FormValue("color") == "black" {
			black = r.FormValue("player_1")
			white = r.FormValue("player_2")
		} else {
			black = r.FormValue("player_2")
			white = r.FormValue("player_1")
		}
		game, err := model.New(black, white, size)
		if err != nil {
			return http.StatusBadRequest, err
		}
		http.Redirect(w, r, "/game/"+game.Id, 303)
		return http.StatusSeeOther, nil
	} else {
		id := r.URL.Path[6:]
		game, err := model.Load(id)
		if err != nil {
			return http.StatusNotFound, err
		}
		if r.Method == "PATCH" {
			x, _ := strconv.Atoi(r.FormValue("x"))
			y, _ := strconv.Atoi(r.FormValue("y"))
			err = game.Move(x, y)
			if err != nil {
				return http.StatusBadRequest, err
			}
			return http.StatusOK, nil
		} else {
			return http.StatusOK, renderTemplate(c, w, "game", game)
		}
	}
}

// Sends game updates to a WebSocket connection
func liveHandler(ws *websocket.Conn) {
	r := ws.Request()
	log.Printf("%s %s %s websocket", strings.Split(r.RemoteAddr, ":")[0], r.Method, r.URL.Path)

	id := r.URL.Path[11:]

	model.Subscribe(id, func(g *model.Game) {
		log.Printf("Sending WebSocket message for game %s", g.Id)
		err := json.NewEncoder(ws).Encode(g)
		if err != nil {
			log.Fatalf(err.Error())
		}
	})
}
<|endoftext|>


<commit_before>// Package nosurf implements an HTTP handler that
// mitigates Cross-Site Request Forgery Attacks.
package nosurf

import (
	"net/http"
	"regexp"
)

const (
	// the name of CSRF cookie
	CookieName = "csrf_token"
	// the name of the form field
	FormFieldName = "csrf_token"
	// the name of CSRF header
	HeaderName = "X-CSRF-Token"
	// the HTTP status code for the default failure handler
	FailureCode = 400

	// Max-Age for the default base cookie. 365 days.
	DefaultMaxAge = 365 * 24 * 60 * 60
)

var safeMethods = []string{"GET", "HEAD", "OPTIONS", "TRACE"}

type CSRFHandler struct {
	// Handlers that CSRFHandler wraps.
	successHandler http.Handler
	failureHandler http.Handler

	// The base cookie that CSRF cookies will be built upon.
	// This should be a better solution of customizing the options
	// than a bunch of methods SetCookieExpiration(), etc.
	baseCookie http.Cookie

	// Slices of URLs that are exempt from CSRF checks.
	// They can be specified by...
	// ...an exact URL
	exemptPaths []string
	// ...a glob (as used by path.Match())
	exemptGlobs []string
	// ...a regexp.
	exemptRegexps []*regexp.Regexp

	// All of those will be matched against Request.URL.Path,
	// So they should take the leading slash into account
}

func defaultFailureHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(FailureCode)
}

// Constructs a new CSRFHandler that calls
// the specified handler if the CSRF check succeeds.
func New(handler http.Handler) *CSRFHandler {
	baseCookie := http.Cookie{}
	baseCookie.MaxAge = DefaultMaxAge

	csrf := &CSRFHandler{successHandler: handler,
		failureHandler: http.HandlerFunc(defaultFailureHandler),
		exemptPaths:    make([]string, 0),
		exemptGlobs:    make([]string, 0),
		exemptRegexps:  make([]*regexp.Regexp, 0),
		baseCookie:     baseCookie,
	}

	return csrf
}

func (h *CSRFHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Prefer the header over form value
	sent_token := r.Header.Get(HeaderName)
	if sent_token == "" {
		sent_token = r.PostFormValue(FormFieldName)
	}

	token_cookie, err := r.Cookie(CookieName)
	real_token := ""
	if err == http.ErrNoCookie {
		real_token = h.RegenerateToken(w, r)
	} else {
		real_token = token_cookie.Value
	}
	// If the length of the real token isn't what it should be,
	// it has either been tampered with,
	// or we're migrating onto a new algorithm for generating tokens.
	// In any case of those, we should regenerate it.
	if len(real_token) != tokenLength {
		real_token = h.RegenerateToken(w, r)
	}

	// clear the context after the request is served
	defer ctxClear(r)
	ctxSetToken(r, real_token)

	if sContains(safeMethods, r.Method) {
		// short-circuit with a success for safe methods
		h.handleSuccess(w, r)
		return
	}
}

func (h *CSRFHandler) handleSuccess(w http.ResponseWriter, r *http.Request) {
	h.successHandler.ServeHTTP(w, r)
}

// Generates a new token, sets it on the given request and returns it
func (h *CSRFHandler) RegenerateToken(w http.ResponseWriter, r *http.Request) string {
	token := generateToken()

	cookie := h.baseCookie
	cookie.Name = CookieName
	cookie.Value = token

	http.SetCookie(w, &cookie)

	ctxSetToken(r, token)

	return token
}

// Sets the handler to call in case the CSRF check
// fails. By default it's defaultFailureHandler.
func (h *CSRFHandler) SetFailureHandler(handler http.Handler) {
	h.failureHandler = handler
}

// Sets the base cookie to use when building a CSRF token cookie
// This way you can specify the Domain, Path, HttpOnly, Secure, etc.
func (h *CSRFHandler) SetBaseCookie(cookie http.Cookie) {
	h.baseCookie = cookie
}
<commit_msg>handleFailure()<commit_after>// Package nosurf implements an HTTP handler that
// mitigates Cross-Site Request Forgery Attacks.
package nosurf

import (
	"net/http"
	"regexp"
)

const (
	// the name of CSRF cookie
	CookieName = "csrf_token"
	// the name of the form field
	FormFieldName = "csrf_token"
	// the name of CSRF header
	HeaderName = "X-CSRF-Token"
	// the HTTP status code for the default failure handler
	FailureCode = 400

	// Max-Age for the default base cookie. 365 days.
	DefaultMaxAge = 365 * 24 * 60 * 60
)

var safeMethods = []string{"GET", "HEAD", "OPTIONS", "TRACE"}

type CSRFHandler struct {
	// Handlers that CSRFHandler wraps.
	successHandler http.Handler
	failureHandler http.Handler

	// The base cookie that CSRF cookies will be built upon.
	// This should be a better solution of customizing the options
	// than a bunch of methods SetCookieExpiration(), etc.
	baseCookie http.Cookie

	// Slices of URLs that are exempt from CSRF checks.
	// They can be specified by...
	// ...an exact URL
	exemptPaths []string
	// ...a glob (as used by path.Match())
	exemptGlobs []string
	// ...a regexp.
	exemptRegexps []*regexp.Regexp

	// All of those will be matched against Request.URL.Path,
	// So they should take the leading slash into account
}

func defaultFailureHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(FailureCode)
}

// Constructs a new CSRFHandler that calls
// the specified handler if the CSRF check succeeds.
func New(handler http.Handler) *CSRFHandler {
	baseCookie := http.Cookie{}
	baseCookie.MaxAge = DefaultMaxAge

	csrf := &CSRFHandler{successHandler: handler,
		failureHandler: http.HandlerFunc(defaultFailureHandler),
		exemptPaths:    make([]string, 0),
		exemptGlobs:    make([]string, 0),
		exemptRegexps:  make([]*regexp.Regexp, 0),
		baseCookie:     baseCookie,
	}

	return csrf
}

func (h *CSRFHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Prefer the header over form value
	sent_token := r.Header.Get(HeaderName)
	if sent_token == "" {
		sent_token = r.PostFormValue(FormFieldName)
	}

	token_cookie, err := r.Cookie(CookieName)
	real_token := ""
	if err == http.ErrNoCookie {
		real_token = h.RegenerateToken(w, r)
	} else {
		real_token = token_cookie.Value
	}
	// If the length of the real token isn't what it should be,
	// it has either been tampered with,
	// or we're migrating onto a new algorithm for generating tokens.
	// In any case of those, we should regenerate it.
	if len(real_token) != tokenLength {
		real_token = h.RegenerateToken(w, r)
	}

	// clear the context after the request is served
	defer ctxClear(r)
	ctxSetToken(r, real_token)

	if sContains(safeMethods, r.Method) {
		// short-circuit with a success for safe methods
		h.handleSuccess(w, r)
		return
	}
}

// handleSuccess simply calls the successHandler
// everything else, like setting a token in the context
// is taken care of by h.ServeHTTP()
func (h *CSRFHandler) handleSuccess(w http.ResponseWriter, r *http.Request) {
	h.successHandler.ServeHTTP(w, r)
}

// Same applies here: h.ServeHTTP() sets the failure reason, the token,
// and only then calls handleFailure()
func (h *CSRFHandler) handleFailure(w http.ResponseWriter, r *http.Request) {
	h.failureHandler.ServeHTTP(w, r)
}

// Generates a new token, sets it on the given request and returns it
func (h *CSRFHandler) RegenerateToken(w http.ResponseWriter, r *http.Request) string {
	token := generateToken()

	cookie := h.baseCookie
	cookie.Name = CookieName
	cookie.Value = token

	http.SetCookie(w, &cookie)

	ctxSetToken(r, token)

	return token
}

// Sets the handler to call in case the CSRF check
// fails. By default it's defaultFailureHandler.
func (h *CSRFHandler) SetFailureHandler(handler http.Handler) {
	h.failureHandler = handler
}

// Sets the base cookie to use when building a CSRF token cookie
// This way you can specify the Domain, Path, HttpOnly, Secure, etc.
func (h *CSRFHandler) SetBaseCookie(cookie http.Cookie) {
	h.baseCookie = cookie
}
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

func main() {
	goPath := os.Getenv("GOPATH")
	hugoPath := filepath.Join(goPath, "src/github.com/spf13/hugo")

	if found, err := exists(hugoPath); !found || err != nil {
		log.Fatalf("Aborting. Can't find Hugo source on %s.", hugoPath)
	}

	// NOTE: I assume that 'go get -u' was run before of this and that
	// every package and dependency is up to date.

	// Get new tags from remote
	run("git", []string{"fetch", "--tags"}, hugoPath)

	// Get the revision for the latest tag
	commit := run("git", []string{"rev-list", "--tags", "--max-count=1"}, hugoPath)

	// Get the latest tag
	tag := run("git", []string{"describe", "--tags", commit}, hugoPath)

	// Checkout the latest tag
	run("git", []string{"checkout", tag}, hugoPath)

	// Build hugo binary
	pluginPath := filepath.Join(goPath, "src/github.com/hacdias/caddy-hugo")
	run("go", []string{"build", "-o", "assets/hugo", "github.com/spf13/hugo"}, pluginPath)

	updateVersion(pluginPath, tag)
}

func run(command string, args []string, path string) string {
	cmd := exec.Command(command, args...)
	cmd.Dir = path
	out, err := cmd.Output()

	if err != nil {
		log.Fatal(err)
	}

	return strings.TrimSpace(string(out))
}

// exists returns whether the given file or directory exists or not
func exists(path string) (bool, error) {
	_, err := os.Stat(path)

	if err == nil {
		return true, nil
	}

	if os.IsNotExist(err) {
		return false, nil
	}

	return true, err
}

func updateVersion(path string, version string) {
	path = filepath.Join(path, "installer.go")

	input, err := ioutil.ReadFile(path)
	if err != nil {
		log.Fatalln(err)
	}

	lines := strings.Split(string(input), "\n")

	for i, line := range lines {
		if strings.Contains(line, "const version") {
			lines[i] = "const version = \"" + version + "\""
		}
	}

	output := strings.Join(lines, "\n")
	err = ioutil.WriteFile(path, []byte(output), 0644)
	if err != nil {
		log.Fatalln(err)
	}
}
<commit_msg>travis update<commit_after>package main

import (
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

func main() {
	if len(os.Getenv("TRAVIS")) > 0 || len(os.Getenv("CI")) > 0 {
		return
	}

	goPath := os.Getenv("GOPATH")
	hugoPath := filepath.Join(goPath, "src/github.com/spf13/hugo")

	if found, err := exists(hugoPath); !found || err != nil {
		log.Fatalf("Aborting. Can't find Hugo source on %s.", hugoPath)
	}

	// NOTE: I assume that 'go get -u' was run before of this and that
	// every package and dependency is up to date.

	// Get new tags from remote
	run("git", []string{"fetch", "--tags"}, hugoPath)

	// Get the revision for the latest tag
	commit := run("git", []string{"rev-list", "--tags", "--max-count=1"}, hugoPath)

	// Get the latest tag
	tag := run("git", []string{"describe", "--tags", commit}, hugoPath)

	// Checkout the latest tag
	run("git", []string{"checkout", tag}, hugoPath)

	// Build hugo binary
	pluginPath := filepath.Join(goPath, "src/github.com/hacdias/caddy-hugo")
	run("go", []string{"build", "-o", "assets/hugo", "github.com/spf13/hugo"}, pluginPath)

	updateVersion(pluginPath, tag)
}

func run(command string, args []string, path string) string {
	cmd := exec.Command(command, args...)
	cmd.Dir = path
	out, err := cmd.Output()

	if err != nil {
		log.Fatal(err)
	}

	return strings.TrimSpace(string(out))
}

// exists returns whether the given file or directory exists or not
func exists(path string) (bool, error) {
	_, err := os.Stat(path)

	if err == nil {
		return true, nil
	}

	if os.IsNotExist(err) {
		return false, nil
	}

	return true, err
}

func updateVersion(path string, version string) {
	path = filepath.Join(path, "installer.go")

	input, err := ioutil.ReadFile(path)
	if err != nil {
		log.Fatalln(err)
	}

	lines := strings.Split(string(input), "\n")

	for i, line := range lines {
		if strings.Contains(line, "const version") {
			lines[i] = "const version = \"" + version + "\""
		}
	}

	output := strings.Join(lines, "\n")
	err = ioutil.WriteFile(path, []byte(output), 0644)
	if err != nil {
		log.Fatalln(err)
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2017, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package pattern

import (
	"fmt"
	"regexp/syntax"
	"testing"
)

var translateTests = []struct {
	pattern string
	greedy  bool
	want    string
	wantErr bool
}{
	{``, false, ``, false},
	{`foo`, false, `foo`, false},
	{`.`, false, `\.`, false},
	{`foo*`, false, `foo.*?`, false},
	{`foo*`, true, `foo.*`, false},
	{`\*`, false, `\*`, false},
	{`\`, false, "", true},
	{`?`, false, `.`, false},
	{`\a`, false, `a`, false},
	{`(`, false, `\(`, false},
	{`a|b`, false, `a\|b`, false},
	{`x{3}`, false, `x\{3\}`, false},
	{`[a]`, false, `[a]`, false},
	{`[abc]`, false, `[abc]`, false},
	{`[^bc]`, false, `[^bc]`, false},
	{`[!bc]`, false, `[^bc]`, false},
	{`[[]`, false, `[[]`, false},
	{`[]]`, false, `[]]`, false},
	{`[^]]`, false, `[^]]`, false},
	{`[`, false, "", true},
	{`[]`, false, "", true},
	{`[^]`, false, "", true},
	{`[ab`, false, "", true},
	{`[a-]`, false, `[a-]`, false},
	{`[z-a]`, false, "", true},
	{`[a-a]`, false, "[a-a]", false},
	{`[aa]`, false, `[aa]`, false},
	{`[0-4A-Z]`, false, `[0-4A-Z]`, false},
	{`[-a]`, false, "[-a]", false},
	{`[^-a]`, false, "[^-a]", false},
	{`[a-]`, false, "[a-]", false},
	{`[[:digit:]]`, false, `[[:digit:]]`, false},
	{`[[:`, false, "", true},
	{`[[:digit`, false, "", true},
	{`[[:wrong:]]`, false, "", true},
	{`[[=x=]]`, false, "", true},
	{`[[.x.]]`, false, "", true},
}

func TestRegexp(t *testing.T) {
	t.Parallel()
	for i, tc := range translateTests {
		t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) {
			got, gotErr := Regexp(tc.pattern, tc.greedy)
			if tc.wantErr && gotErr == nil {
				t.Fatalf("(%q, %v) did not error",
					tc.pattern, tc.greedy)
			}
			if !tc.wantErr && gotErr != nil {
				t.Fatalf("(%q, %v) errored with %q",
					tc.pattern, tc.greedy, gotErr)
			}
			if got != tc.want {
				t.Fatalf("(%q, %v) got %q, wanted %q",
					tc.pattern, tc.greedy, got, tc.want)
			}
			_, rxErr := syntax.Parse(got, syntax.Perl)
			if gotErr == nil && rxErr != nil {
				t.Fatalf("regexp/syntax.Parse(%q) failed with %q",
					got, rxErr)
			}
		})
	}
}

var quoteTests = []struct {
	pattern string
	want    string
}{
	{``, ``},
	{`foo`, `foo`},
	{`.`, `.`},
	{`*`, `\*`},
	{`foo?`, `foo\?`},
	{`\[`, `\\\[`},
}

func TestQuoteMeta(t *testing.T) {
	t.Parallel()
	for _, tc := range quoteTests {
		got := QuoteMeta(tc.pattern)
		if got != tc.want {
			t.Errorf("(%q) got %q, wanted %q",
				tc.pattern, got, tc.want)
		}
	}
}
<commit_msg>pattern: cover HasMeta in its tests<commit_after>// Copyright (c) 2017, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package pattern

import (
	"fmt"
	"regexp/syntax"
	"testing"
)

var translateTests = []struct {
	pattern string
	greedy  bool
	want    string
	wantErr bool
}{
	{``, false, ``, false},
	{`foo`, false, `foo`, false},
	{`.`, false, `\.`, false},
	{`foo*`, false, `foo.*?`, false},
	{`foo*`, true, `foo.*`, false},
	{`\*`, false, `\*`, false},
	{`\`, false, "", true},
	{`?`, false, `.`, false},
	{`\a`, false, `a`, false},
	{`(`, false, `\(`, false},
	{`a|b`, false, `a\|b`, false},
	{`x{3}`, false, `x\{3\}`, false},
	{`[a]`, false, `[a]`, false},
	{`[abc]`, false, `[abc]`, false},
	{`[^bc]`, false, `[^bc]`, false},
	{`[!bc]`, false, `[^bc]`, false},
	{`[[]`, false, `[[]`, false},
	{`[]]`, false, `[]]`, false},
	{`[^]]`, false, `[^]]`, false},
	{`[`, false, "", true},
	{`[]`, false, "", true},
	{`[^]`, false, "", true},
	{`[ab`, false, "", true},
	{`[a-]`, false, `[a-]`, false},
	{`[z-a]`, false, "", true},
	{`[a-a]`, false, "[a-a]", false},
	{`[aa]`, false, `[aa]`, false},
	{`[0-4A-Z]`, false, `[0-4A-Z]`, false},
	{`[-a]`, false, "[-a]", false},
	{`[^-a]`, false, "[^-a]", false},
	{`[a-]`, false, "[a-]", false},
	{`[[:digit:]]`, false, `[[:digit:]]`, false},
	{`[[:`, false, "", true},
	{`[[:digit`, false, "", true},
	{`[[:wrong:]]`, false, "", true},
	{`[[=x=]]`, false, "", true},
	{`[[.x.]]`, false, "", true},
}

func TestRegexp(t *testing.T) {
	t.Parallel()
	for i, tc := range translateTests {
		t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) {
			got, gotErr := Regexp(tc.pattern, tc.greedy)
			if tc.wantErr && gotErr == nil {
				t.Fatalf("(%q, %v) did not error", tc.pattern, tc.greedy)
			}
			if !tc.wantErr && gotErr != nil {
				t.Fatalf("(%q, %v) errored with %q", tc.pattern, tc.greedy, gotErr)
			}
			if got != tc.want {
				t.Fatalf("(%q, %v) got %q, wanted %q", tc.pattern, tc.greedy, got, tc.want)
			}
			_, rxErr := syntax.Parse(got, syntax.Perl)
			if gotErr == nil && rxErr != nil {
				t.Fatalf("regexp/syntax.Parse(%q) failed with %q", got, rxErr)
			}
		})
	}
}

var metaTests = []struct {
	pat       string
	wantHas   bool
	wantQuote string
}{
	{``, false, ``},
	{`foo`, false, `foo`},
	{`.`, false, `.`},
	{`*`, true, `\*`},
	{`foo?`, true, `foo\?`},
	{`\[`, false, `\\\[`},
}

func TestMeta(t *testing.T) {
	t.Parallel()
	for _, tc := range metaTests {
		if got := HasMeta(tc.pat); got != tc.wantHas {
			t.Errorf("HasMeta(%q) got %t, wanted %t",
				tc.pat, got, tc.wantHas)
		}
		if got := QuoteMeta(tc.pat); got != tc.wantQuote {
			t.Errorf("QuoteMeta(%q) got %q, wanted %q",
				tc.pat, got, tc.wantQuote)
		}
	}
}
<|endoftext|>