<commit_before>// Copyright 2013 by Dobrosław Żybort. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package slug

import (
	"github.com/fiam/gounidecode/unidecode"
	"regexp"
	"strings"
)

var (
	// Custom substitution map
	CustomSub map[string]string
	// Custom rune substitution map
	CustomRuneSub map[rune]string

	// Maximum slug length. It's smart so it will cat slug after full word.
	// By default slugs aren't shortened.
	// If MaxLength is smaller than length of the first word, then returned
	// slug will contain substring from the first word truncated after
	// MaxLength.
	MaxLength int
)

//=============================================================================

// Make returns slug generated from provided string. Will use "en" as language
// substitution.
func Make(s string) (slug string) {
	return MakeLang(s, "en")
}

// MakeLang returns slug generated from provided string and will use provided
// language for chars substitution.
func MakeLang(s string, lang string) (slug string) {
	slug = strings.TrimSpace(s)

	// Select substitution language
	switch lang {
	case "de":
		slug = SubstituteRune(slug, deSub)
	case "en":
		slug = SubstituteRune(slug, enSub)
	case "pl":
		slug = SubstituteRune(slug, plSub)
	default: // fallback to "en" if lang not found
		slug = SubstituteRune(slug, enSub)
	}

	slug = SubstituteRune(slug, defaultSub)

	// Custom substitutions
	slug = SubstituteRune(slug, CustomRuneSub)
	slug = Substitute(slug, CustomSub)

	slug = unidecode.Unidecode(slug)

	slug = strings.ToLower(slug)

	slug = regexp.MustCompile("[^a-z0-9-_]").ReplaceAllString(slug, "-")
	slug = regexp.MustCompile("-+").ReplaceAllString(slug, "-")
	slug = strings.Trim(slug, "-")

	if MaxLength > 0 {
		slug = smartTruncate(slug)
	}

	return slug
}

// Substitute returns string with superseded all substrings from
// provided substitution map.
func Substitute(s string, sub map[string]string) (buf string) {
	buf = s
	for key, val := range sub {
		buf = strings.Replace(s, key, val, -1)
	}
	return
}

// SubstituteRune substitutes string chars with provided rune
// substitution map.
func SubstituteRune(s string, sub map[rune]string) (buf string) {
	for _, c := range s {
		if d, ok := sub[c]; ok {
			buf += d
		} else {
			buf += string(c)
		}
	}
	return
}

func smartTruncate(text string) string {
	if len(text) < MaxLength {
		return text
	}

	var truncated string
	words := strings.SplitAfter(text, "-")
	// If MaxLength is smaller than length of the first word return word
	// truncated after MaxLength.
	if len(words[0]) > MaxLength {
		return words[0][:MaxLength]
	}
	for _, word := range words {
		if len(truncated)+len(word)-1 <= MaxLength {
			truncated = truncated + word
		} else {
			break
		}
	}
	return strings.Trim(truncated, "-")
}
<commit_msg>Update: docs<commit_after>// Copyright 2013 by Dobrosław Żybort. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package slug

import (
	"github.com/fiam/gounidecode/unidecode"
	"regexp"
	"strings"
)

var (
	// Custom substitution map
	CustomSub map[string]string
	// Custom rune substitution map
	CustomRuneSub map[rune]string

	// Maximum slug length. It's smart so it will cat slug after full word.
	// By default slugs aren't shortened.
	// If MaxLength is smaller than length of the first word, then returned
	// slug will contain only substring from the first word truncated
	// after MaxLength.
	MaxLength int
)

//=============================================================================

// Make returns slug generated from provided string. Will use "en" as language
// substitution.
func Make(s string) (slug string) {
	return MakeLang(s, "en")
}

// MakeLang returns slug generated from provided string and will use provided
// language for chars substitution.
func MakeLang(s string, lang string) (slug string) {
	slug = strings.TrimSpace(s)

	// Select substitution language
	switch lang {
	case "de":
		slug = SubstituteRune(slug, deSub)
	case "en":
		slug = SubstituteRune(slug, enSub)
	case "pl":
		slug = SubstituteRune(slug, plSub)
	default: // fallback to "en" if lang not found
		slug = SubstituteRune(slug, enSub)
	}

	slug = SubstituteRune(slug, defaultSub)

	// Custom substitutions
	slug = SubstituteRune(slug, CustomRuneSub)
	slug = Substitute(slug, CustomSub)

	slug = unidecode.Unidecode(slug)

	slug = strings.ToLower(slug)

	slug = regexp.MustCompile("[^a-z0-9-_]").ReplaceAllString(slug, "-")
	slug = regexp.MustCompile("-+").ReplaceAllString(slug, "-")
	slug = strings.Trim(slug, "-")

	if MaxLength > 0 {
		slug = smartTruncate(slug)
	}

	return slug
}

// Substitute returns string with superseded all substrings from
// provided substitution map.
func Substitute(s string, sub map[string]string) (buf string) {
	buf = s
	for key, val := range sub {
		buf = strings.Replace(s, key, val, -1)
	}
	return
}

// SubstituteRune substitutes string chars with provided rune
// substitution map.
func SubstituteRune(s string, sub map[rune]string) (buf string) {
	for _, c := range s {
		if d, ok := sub[c]; ok {
			buf += d
		} else {
			buf += string(c)
		}
	}
	return
}

func smartTruncate(text string) string {
	if len(text) < MaxLength {
		return text
	}

	var truncated string
	words := strings.SplitAfter(text, "-")
	// If MaxLength is smaller than length of the first word return word
	// truncated after MaxLength.
	if len(words[0]) > MaxLength {
		return words[0][:MaxLength]
	}
	for _, word := range words {
		if len(truncated)+len(word)-1 <= MaxLength {
			truncated = truncated + word
		} else {
			break
		}
	}
	return strings.Trim(truncated, "-")
}
<|endoftext|>


<commit_before>package goczmq

/*
#include "czmq.h"
#include <stdlib.h>
#include <string.h>

int Sock_connect(zsock_t *self, const char *format) {return zsock_connect(self, format, NULL);}
int Sock_disconnect(zsock_t *self, const char *format) {return zsock_disconnect(self, format, NULL);}
int Sock_bind(zsock_t *self, const char *format) {return zsock_bind(self, format, NULL);}
int Sock_unbind(zsock_t *self, const char *format) {return zsock_unbind(self, format, NULL);}
int Sock_sendframe(zsock_t *sock, const void *data, size_t size, int flags) {
	zframe_t *frame = zframe_new (data, size);
	int rc = zframe_send (&frame, sock, flags);
	return rc;
}
*/
import "C"

import (
	"os"
	"runtime"
	"strings"
	"unsafe"
)

// Sock wraps the CZMQ zsock class.
type Sock struct {
	zsockT    *C.struct__zsock_t
	file      string
	line      int
	zType     int
	clientIDs []string
}

func init() {
	if err := os.Setenv("ZSYS_SIGHANDLER", "false"); err != nil {
		panic(err)
	}
}

// GetLastClientID returns the id of the last client you received
// a message from if the underlying socket is a Router socket
// DEPRECATED: See goczmq.ReadWriter
func (s *Sock) GetLastClientID() []byte {
	id := []byte(s.clientIDs[0])
	s.clientIDs = s.clientIDs[1:]
	return id
}

// SetLastClientID lets you manually set the id of the client
// you last received a message from if the underlying socket
// is a Router socket
// DEPRECATED: See goczmq.ReadWriter
func (s *Sock) SetLastClientID(id []byte) {
	s.clientIDs = append(s.clientIDs, string(id))
}

// NewSock creates a new socket.  The caller source and
// line number are passed so CZMQ can report socket leaks
// intelligently.
func NewSock(t int) *Sock {
	var s *Sock
	_, file, line, ok := runtime.Caller(1)

	if ok {
		s = &Sock{
			file:      file,
			line:      line,
			zType:     t,
			clientIDs: make([]string, 0),
		}
	} else {
		s = &Sock{
			file:      "",
			line:      0,
			zType:     t,
			clientIDs: make([]string, 0),
		}
	}

	cFile := C.CString(s.file)
	defer C.free(unsafe.Pointer(cFile))

	s.zsockT = C.zsock_new_checked(C.int(s.zType), cFile, C.size_t(s.line))
	return s
}

// Connect connects a socket to an endpoint
// returns an error if the connect failed.
func (s *Sock) Connect(endpoint string) error {
	cEndpoint := C.CString(endpoint)
	defer C.free(unsafe.Pointer(cEndpoint))

	rc := C.Sock_connect(s.zsockT, cEndpoint)
	if rc != C.int(0) {
		return ErrConnect
	}
	return nil
}

// Disconnect disconnects a socket from an endpoint.  If returns
// an error if the endpoint was not found
func (s *Sock) Disconnect(endpoint string) error {
	cEndpoint := C.CString(endpoint)
	defer C.free(unsafe.Pointer(cEndpoint))

	rc := C.Sock_disconnect(s.zsockT, C.CString(endpoint))
	if int(rc) == -1 {
		return ErrDisconnect
	}
	return nil
}

// Bind binds a socket to an endpoint.  On success returns
// the port number used for tcp transports, or 0 for other
// transports.  On failure returns a -1 for port, and an error.
func (s *Sock) Bind(endpoint string) (int, error) {
	cEndpoint := C.CString(endpoint)
	defer C.free(unsafe.Pointer(cEndpoint))

	port := C.Sock_bind(s.zsockT, cEndpoint)
	if port == C.int(-1) {
		return -1, ErrBind
	}
	return int(port), nil
}

// Unbind unbinds a socket from an endpoint.  If returns
// an error if the endpoint was not found
func (s *Sock) Unbind(endpoint string) error {
	rc := C.Sock_unbind(s.zsockT, C.CString(endpoint))
	if int(rc) == -1 {
		return ErrUnbind
	}
	return nil
}

// Attach attaches a socket to zero or more endpoints. If endpoints is not null,
// parses as list of ZeroMQ endpoints, separated by commas, and prefixed by
// '@' (to bind the socket) or '>' (to attach the socket). If the endpoint
// does not start with '@' or '>', the serverish argument determines whether
// it is used to bind (serverish = true) or connect (serverish = false)
func (s *Sock) Attach(endpoints string, serverish bool) error {
	cEndpoints := C.CString(endpoints)
	defer C.free(unsafe.Pointer(cEndpoints))

	rc := C.zsock_attach(s.zsockT, cEndpoints, C._Bool(serverish))
	if rc == -1 {
		return ErrSockAttach
	}
	return nil
}

// NewPub creates a Pub socket and calls Attach.
// The socket will Bind by default.
func NewPub(endpoints string) (*Sock, error) {
	s := NewSock(Pub)
	return s, s.Attach(endpoints, true)
}

// NewSub creates a Sub socket and calls Attach.
// 'subscribe' is a comma delimited list of topics to subscribe to.
// The socket will Connect by default.
func NewSub(endpoints string, subscribe string) (*Sock, error) {
	s := NewSock(Sub)
	subscriptions := strings.Split(subscribe, ",")

	for _, topic := range subscriptions {
		s.SetSubscribe(topic)
	}

	return s, s.Attach(endpoints, false)
}

// NewRep creates a Rep socket and calls Attach.
// The socket will Bind by default.
func NewRep(endpoints string) (*Sock, error) {
	s := NewSock(Rep)
	return s, s.Attach(endpoints, true)
}

// NewReq creates a Req socket and calls Attach.
// The socket will Connect by default.
func NewReq(endpoints string) (*Sock, error) {
	s := NewSock(Req)
	return s, s.Attach(endpoints, false)
}

// NewPull creates a Pull socket and calls Attach.
// The socket will Bind by default.
func NewPull(endpoints string) (*Sock, error) {
	s := NewSock(Pull)
	return s, s.Attach(endpoints, true)
}

// NewPush creates a Push socket and calls Attach.
// The socket will Connect by default.
func NewPush(endpoints string) (*Sock, error) {
	s := NewSock(Push)
	return s, s.Attach(endpoints, false)
}

// NewRouter creates a Router socket and calls Attach.
// The socket will Bind by default.
func NewRouter(endpoints string) (*Sock, error) {
	s := NewSock(Router)
	return s, s.Attach(endpoints, true)
}

// NewDealer creates a Dealer socket and calls Attach.
// The socket will Connect by default.
func NewDealer(endpoints string) (*Sock, error) {
	s := NewSock(Dealer)
	return s, s.Attach(endpoints, false)
}

// NewXPub creates an XPub socket and calls Attach.
// The socket will Bind by default.
func NewXPub(endpoints string) (*Sock, error) {
	s := NewSock(XPub)
	return s, s.Attach(endpoints, true)
}

// NewXSub creates an XSub socket and calls Attach.
// The socket will Connect by default.
func NewXSub(endpoints string) (*Sock, error) {
	s := NewSock(XSub)
	return s, s.Attach(endpoints, false)
}

// NewPair creates a Pair socket and calls Attach.
// The socket will Connect by default.
func NewPair(endpoints string) (*Sock, error) {
	s := NewSock(Pair)
	return s, s.Attach(endpoints, false)
}

// NewStream creates a Stream socket and calls Attach.
// The socket will Connect by default.
func NewStream(endpoints string) (*Sock, error) {
	s := NewSock(Stream)
	return s, s.Attach(endpoints, false)
}

// Pollin returns true if there is a Pollin
// event on the socket
func (s *Sock) Pollin() bool {
	return s.Events() == Pollin
}

// Pollout returns true if there is a Pollout
// event on the socket
func (s *Sock) Pollout() bool {
	return s.Events() == Pollout
}

// SendFrame sends a byte array via the socket.  For the flags
// value, use 0 for a single message, or SNDFlagMore if it is
// a multi-part message
func (s *Sock) SendFrame(data []byte, flags int) error {
	var rc C.int
	if len(data) == 0 {
		rc = C.Sock_sendframe(unsafe.Pointer(s.zsockT), unsafe.Pointer(C.CString("")), C.size_t(len(data)), C.int(flags))
	} else {
		rc = C.Sock_sendframe(unsafe.Pointer(s.zsockT), unsafe.Pointer(&data[0]), C.size_t(len(data)), C.int(flags))
	}
	if rc == C.int(-1) {
		return ErrSendFrame
	}
	return nil
}

// RecvFrame reads a frame from the socket and returns it
// as a byte array, along with a more flag and and error
// (if there is an error)
func (s *Sock) RecvFrame() ([]byte, int, error) {
	if s.zsockT == nil {
		return nil, -1, ErrRecvFrameAfterDestroy
	}

	frame := C.zframe_recv(unsafe.Pointer(s.zsockT))
	if frame == nil {
		return []byte{0}, 0, ErrRecvFrame
	}
	dataSize := C.zframe_size(frame)
	dataPtr := C.zframe_data(frame)
	b := C.GoBytes(unsafe.Pointer(dataPtr), C.int(dataSize))
	more := C.zframe_more(frame)
	C.zframe_destroy(&frame)
	return b, int(more), nil
}

// RecvFrameNoWait receives a frame from the socket
// and returns it as a byte array if one is waiting.
// Returns an empty frame, a 0 more flag and an error
// if one is not immediately available
func (s *Sock) RecvFrameNoWait() ([]byte, int, error) {
	if !s.Pollin() {
		return []byte{0}, 0, ErrRecvFrame
	}

	return s.RecvFrame()
}

// SendMessage accepts an array of byte arrays and
// sends it as a multi-part message.
func (s *Sock) SendMessage(parts [][]byte) error {
	var f int
	numParts := len(parts)
	for i, val := range parts {
		if i == numParts-1 {
			f = 0
		} else {
			f = FlagMore
		}

		err := s.SendFrame(val, f)
		if err != nil {
			return err
		}
	}
	return nil
}

// RecvMessage receives a full message from the socket
// and returns it as an array of byte arrays.
func (s *Sock) RecvMessage() ([][]byte, error) {
	var msg [][]byte

	for {
		frame, flag, err := s.RecvFrame()
		if err != nil {
			return msg, err
		}
		msg = append(msg, frame)
		if flag != FlagMore {
			break
		}
	}
	return msg, nil
}

// Read provides an io.Reader interface to a zeromq socket
// DEPRECATED: see goczmq.ReadWriter
func (s *Sock) Read(p []byte) (int, error) {
	var totalRead int
	var totalFrame int

	frame, flag, err := s.RecvFrame()
	if err != nil {
		return totalRead, err
	}

	if s.GetType() == Router {
		s.clientIDs = append(s.clientIDs, string(frame))
	} else {
		totalRead += copy(p[:], frame[:])
		totalFrame += len(frame)
	}

	for flag == FlagMore {
		frame, flag, err = s.RecvFrame()
		if err != nil {
			return totalRead, err
		}
		totalRead += copy(p[totalRead:], frame[:])
		totalFrame += len(frame)
	}

	if totalFrame > len(p) {
		err = ErrSliceFull
	} else {
		err = nil
	}

	return totalRead, err
}

// Write provides an io.Writer interface to a zeromq socket
// DEPRECATED: See goczmq.ReadWriter
func (s *Sock) Write(p []byte) (int, error) {
	var total int
	if s.GetType() == Router {
		err := s.SendFrame(s.GetLastClientID(), FlagMore)
		if err != nil {
			return total, err
		}
	}
	err := s.SendFrame(p, 0)
	if err != nil {
		return total, err
	}

	return len(p), nil
}

// RecvMessageNoWait receives a full message from the socket
// and returns it as an array of byte arrays if one is waiting.
// Returns an empty message and an error if one is not immediately
// available
func (s *Sock) RecvMessageNoWait() ([][]byte, error) {
	var msg [][]byte
	if !s.Pollin() {
		return msg, ErrRecvMessage
	}

	for {
		frame, flag, err := s.RecvFrame()
		if err != nil {
			return msg, err
		}
		msg = append(msg, frame)
		if flag != FlagMore {
			break
		}
	}
	return msg, nil
}

// GetType returns the socket's type
func (s *Sock) GetType() int {
	return s.zType
}

// Destroy destroys the underlying zsockT.
func (s *Sock) Destroy() {
	cFile := C.CString(s.file)
	defer C.free(unsafe.Pointer(cFile))

	C.zsock_destroy_checked(&s.zsockT, cFile, C.size_t(s.line))
}
<commit_msg>Fix string leaks<commit_after>package goczmq

/*
#include "czmq.h"
#include <stdlib.h>
#include <string.h>

int Sock_connect(zsock_t *self, const char *format) {return zsock_connect(self, format, NULL);}
int Sock_disconnect(zsock_t *self, const char *format) {return zsock_disconnect(self, format, NULL);}
int Sock_bind(zsock_t *self, const char *format) {return zsock_bind(self, format, NULL);}
int Sock_unbind(zsock_t *self, const char *format) {return zsock_unbind(self, format, NULL);}
int Sock_sendframe(zsock_t *sock, const void *data, size_t size, int flags) {
	zframe_t *frame = zframe_new (data, size);
	int rc = zframe_send (&frame, sock, flags);
	return rc;
}
*/
import "C"

import (
	"os"
	"runtime"
	"strings"
	"unsafe"
)

// Sock wraps the CZMQ zsock class.
type Sock struct {
	zsockT    *C.struct__zsock_t
	file      string
	line      int
	zType     int
	clientIDs []string
}

func init() {
	if err := os.Setenv("ZSYS_SIGHANDLER", "false"); err != nil {
		panic(err)
	}
}

// GetLastClientID returns the id of the last client you received
// a message from if the underlying socket is a Router socket
// DEPRECATED: See goczmq.ReadWriter
func (s *Sock) GetLastClientID() []byte {
	id := []byte(s.clientIDs[0])
	s.clientIDs = s.clientIDs[1:]
	return id
}

// SetLastClientID lets you manually set the id of the client
// you last received a message from if the underlying socket
// is a Router socket
// DEPRECATED: See goczmq.ReadWriter
func (s *Sock) SetLastClientID(id []byte) {
	s.clientIDs = append(s.clientIDs, string(id))
}

// NewSock creates a new socket.  The caller source and
// line number are passed so CZMQ can report socket leaks
// intelligently.
func NewSock(t int) *Sock {
	var s *Sock
	_, file, line, ok := runtime.Caller(1)

	if ok {
		s = &Sock{
			file:      file,
			line:      line,
			zType:     t,
			clientIDs: make([]string, 0),
		}
	} else {
		s = &Sock{
			file:      "",
			line:      0,
			zType:     t,
			clientIDs: make([]string, 0),
		}
	}

	cFile := C.CString(s.file)
	defer C.free(unsafe.Pointer(cFile))

	s.zsockT = C.zsock_new_checked(C.int(s.zType), cFile, C.size_t(s.line))
	return s
}

// Connect connects a socket to an endpoint
// returns an error if the connect failed.
func (s *Sock) Connect(endpoint string) error {
	cEndpoint := C.CString(endpoint)
	defer C.free(unsafe.Pointer(cEndpoint))

	rc := C.Sock_connect(s.zsockT, cEndpoint)
	if rc != C.int(0) {
		return ErrConnect
	}
	return nil
}

// Disconnect disconnects a socket from an endpoint.  If returns
// an error if the endpoint was not found
func (s *Sock) Disconnect(endpoint string) error {
	cEndpoint := C.CString(endpoint)
	defer C.free(unsafe.Pointer(cEndpoint))

	rc := C.Sock_disconnect(s.zsockT, cEndpoint)
	if int(rc) == -1 {
		return ErrDisconnect
	}
	return nil
}

// Bind binds a socket to an endpoint.  On success returns
// the port number used for tcp transports, or 0 for other
// transports.  On failure returns a -1 for port, and an error.
func (s *Sock) Bind(endpoint string) (int, error) {
	cEndpoint := C.CString(endpoint)
	defer C.free(unsafe.Pointer(cEndpoint))

	port := C.Sock_bind(s.zsockT, cEndpoint)
	if port == C.int(-1) {
		return -1, ErrBind
	}
	return int(port), nil
}

// Unbind unbinds a socket from an endpoint.  If returns
// an error if the endpoint was not found
func (s *Sock) Unbind(endpoint string) error {
	cEndpoint := C.CString(endpoint)
	defer C.free(unsafe.Pointer(cEndpoint))

	rc := C.Sock_unbind(s.zsockT, cEndpoint)
	if int(rc) == -1 {
		return ErrUnbind
	}
	return nil
}

// Attach attaches a socket to zero or more endpoints. If endpoints is not null,
// parses as list of ZeroMQ endpoints, separated by commas, and prefixed by
// '@' (to bind the socket) or '>' (to attach the socket). If the endpoint
// does not start with '@' or '>', the serverish argument determines whether
// it is used to bind (serverish = true) or connect (serverish = false)
func (s *Sock) Attach(endpoints string, serverish bool) error {
	cEndpoints := C.CString(endpoints)
	defer C.free(unsafe.Pointer(cEndpoints))

	rc := C.zsock_attach(s.zsockT, cEndpoints, C._Bool(serverish))
	if rc == -1 {
		return ErrSockAttach
	}
	return nil
}

// NewPub creates a Pub socket and calls Attach.
// The socket will Bind by default.
func NewPub(endpoints string) (*Sock, error) {
	s := NewSock(Pub)
	return s, s.Attach(endpoints, true)
}

// NewSub creates a Sub socket and calls Attach.
// 'subscribe' is a comma delimited list of topics to subscribe to.
// The socket will Connect by default.
func NewSub(endpoints string, subscribe string) (*Sock, error) {
	s := NewSock(Sub)
	subscriptions := strings.Split(subscribe, ",")

	for _, topic := range subscriptions {
		s.SetSubscribe(topic)
	}

	return s, s.Attach(endpoints, false)
}

// NewRep creates a Rep socket and calls Attach.
// The socket will Bind by default.
func NewRep(endpoints string) (*Sock, error) {
	s := NewSock(Rep)
	return s, s.Attach(endpoints, true)
}

// NewReq creates a Req socket and calls Attach.
// The socket will Connect by default.
func NewReq(endpoints string) (*Sock, error) {
	s := NewSock(Req)
	return s, s.Attach(endpoints, false)
}

// NewPull creates a Pull socket and calls Attach.
// The socket will Bind by default.
func NewPull(endpoints string) (*Sock, error) {
	s := NewSock(Pull)
	return s, s.Attach(endpoints, true)
}

// NewPush creates a Push socket and calls Attach.
// The socket will Connect by default.
func NewPush(endpoints string) (*Sock, error) {
	s := NewSock(Push)
	return s, s.Attach(endpoints, false)
}

// NewRouter creates a Router socket and calls Attach.
// The socket will Bind by default.
func NewRouter(endpoints string) (*Sock, error) {
	s := NewSock(Router)
	return s, s.Attach(endpoints, true)
}

// NewDealer creates a Dealer socket and calls Attach.
// The socket will Connect by default.
func NewDealer(endpoints string) (*Sock, error) {
	s := NewSock(Dealer)
	return s, s.Attach(endpoints, false)
}

// NewXPub creates an XPub socket and calls Attach.
// The socket will Bind by default.
func NewXPub(endpoints string) (*Sock, error) {
	s := NewSock(XPub)
	return s, s.Attach(endpoints, true)
}

// NewXSub creates an XSub socket and calls Attach.
// The socket will Connect by default.
func NewXSub(endpoints string) (*Sock, error) {
	s := NewSock(XSub)
	return s, s.Attach(endpoints, false)
}

// NewPair creates a Pair socket and calls Attach.
// The socket will Connect by default.
func NewPair(endpoints string) (*Sock, error) {
	s := NewSock(Pair)
	return s, s.Attach(endpoints, false)
}

// NewStream creates a Stream socket and calls Attach.
// The socket will Connect by default.
func NewStream(endpoints string) (*Sock, error) {
	s := NewSock(Stream)
	return s, s.Attach(endpoints, false)
}

// Pollin returns true if there is a Pollin
// event on the socket
func (s *Sock) Pollin() bool {
	return s.Events() == Pollin
}

// Pollout returns true if there is a Pollout
// event on the socket
func (s *Sock) Pollout() bool {
	return s.Events() == Pollout
}

// SendFrame sends a byte array via the socket.  For the flags
// value, use 0 for a single message, or SNDFlagMore if it is
// a multi-part message
func (s *Sock) SendFrame(data []byte, flags int) error {
	var rc C.int
	if len(data) == 0 {
		rc = C.Sock_sendframe(unsafe.Pointer(s.zsockT), unsafe.Pointer(C.CString("")), C.size_t(len(data)), C.int(flags))
	} else {
		rc = C.Sock_sendframe(unsafe.Pointer(s.zsockT), unsafe.Pointer(&data[0]), C.size_t(len(data)), C.int(flags))
	}
	if rc == C.int(-1) {
		return ErrSendFrame
	}
	return nil
}

// RecvFrame reads a frame from the socket and returns it
// as a byte array, along with a more flag and and error
// (if there is an error)
func (s *Sock) RecvFrame() ([]byte, int, error) {
	if s.zsockT == nil {
		return nil, -1, ErrRecvFrameAfterDestroy
	}

	frame := C.zframe_recv(unsafe.Pointer(s.zsockT))
	if frame == nil {
		return []byte{0}, 0, ErrRecvFrame
	}
	dataSize := C.zframe_size(frame)
	dataPtr := C.zframe_data(frame)
	b := C.GoBytes(unsafe.Pointer(dataPtr), C.int(dataSize))
	more := C.zframe_more(frame)
	C.zframe_destroy(&frame)
	return b, int(more), nil
}

// RecvFrameNoWait receives a frame from the socket
// and returns it as a byte array if one is waiting.
// Returns an empty frame, a 0 more flag and an error
// if one is not immediately available
func (s *Sock) RecvFrameNoWait() ([]byte, int, error) {
	if !s.Pollin() {
		return []byte{0}, 0, ErrRecvFrame
	}

	return s.RecvFrame()
}

// SendMessage accepts an array of byte arrays and
// sends it as a multi-part message.
func (s *Sock) SendMessage(parts [][]byte) error {
	var f int
	numParts := len(parts)
	for i, val := range parts {
		if i == numParts-1 {
			f = 0
		} else {
			f = FlagMore
		}

		err := s.SendFrame(val, f)
		if err != nil {
			return err
		}
	}
	return nil
}

// RecvMessage receives a full message from the socket
// and returns it as an array of byte arrays.
func (s *Sock) RecvMessage() ([][]byte, error) {
	var msg [][]byte

	for {
		frame, flag, err := s.RecvFrame()
		if err != nil {
			return msg, err
		}
		msg = append(msg, frame)
		if flag != FlagMore {
			break
		}
	}
	return msg, nil
}

// Read provides an io.Reader interface to a zeromq socket
// DEPRECATED: see goczmq.ReadWriter
func (s *Sock) Read(p []byte) (int, error) {
	var totalRead int
	var totalFrame int

	frame, flag, err := s.RecvFrame()
	if err != nil {
		return totalRead, err
	}

	if s.GetType() == Router {
		s.clientIDs = append(s.clientIDs, string(frame))
	} else {
		totalRead += copy(p[:], frame[:])
		totalFrame += len(frame)
	}

	for flag == FlagMore {
		frame, flag, err = s.RecvFrame()
		if err != nil {
			return totalRead, err
		}
		totalRead += copy(p[totalRead:], frame[:])
		totalFrame += len(frame)
	}

	if totalFrame > len(p) {
		err = ErrSliceFull
	} else {
		err = nil
	}

	return totalRead, err
}

// Write provides an io.Writer interface to a zeromq socket
// DEPRECATED: See goczmq.ReadWriter
func (s *Sock) Write(p []byte) (int, error) {
	var total int
	if s.GetType() == Router {
		err := s.SendFrame(s.GetLastClientID(), FlagMore)
		if err != nil {
			return total, err
		}
	}
	err := s.SendFrame(p, 0)
	if err != nil {
		return total, err
	}

	return len(p), nil
}

// RecvMessageNoWait receives a full message from the socket
// and returns it as an array of byte arrays if one is waiting.
// Returns an empty message and an error if one is not immediately
// available
func (s *Sock) RecvMessageNoWait() ([][]byte, error) {
	var msg [][]byte
	if !s.Pollin() {
		return msg, ErrRecvMessage
	}

	for {
		frame, flag, err := s.RecvFrame()
		if err != nil {
			return msg, err
		}
		msg = append(msg, frame)
		if flag != FlagMore {
			break
		}
	}
	return msg, nil
}

// GetType returns the socket's type
func (s *Sock) GetType() int {
	return s.zType
}

// Destroy destroys the underlying zsockT.
func (s *Sock) Destroy() {
	cFile := C.CString(s.file)
	defer C.free(unsafe.Pointer(cFile))

	C.zsock_destroy_checked(&s.zsockT, cFile, C.size_t(s.line))
}
<|endoftext|>


<commit_before>package hdfs

import (
	"code.google.com/p/goprotobuf/proto"
	hdfs "github.com/colinmarc/hdfs/protocol/hadoop_hdfs"
	"github.com/colinmarc/hdfs/rpc"
	"os"
	"path"
	"time"
)

// FileInfo implements os.FileInfo, and provides information about a file or
// directory in HDFS.
type FileInfo struct {
	name   string
	status *hdfs.HdfsFileStatusProto
}

// Stat returns an os.FileInfo describing the named file.
func (c *Client) Stat(name string) (fi os.FileInfo, err error) {
	return c.getFileInfo(name)
}

func newFileInfo(status *hdfs.HdfsFileStatusProto, name string) *FileInfo {
	fi := &FileInfo{status: status}

	var fullName string
	if string(status.GetPath()) != "" {
		fullName = string(status.GetPath())
	} else {
		fullName = name
	}

	fi.name = path.Base(fullName)
	return fi
}

func (fi *FileInfo) Name() string {
	return fi.name
}

func (fi *FileInfo) Size() int64 {
	return int64(fi.status.GetLength())
}

func (fi *FileInfo) Mode() os.FileMode {
	return os.FileMode(fi.status.GetPermission().GetPerm())
}

func (fi *FileInfo) ModTime() time.Time {
	return time.Unix(int64(fi.status.GetModificationTime())/1000, 0)
}

func (fi *FileInfo) IsDir() bool {
	return fi.status.GetFileType() == hdfs.HdfsFileStatusProto_IS_DIR
}

// Sys returns the raw *hadoop_hdfs.HdfsFileStatusProto message from the
// namenode.
func (fi *FileInfo) Sys() interface{} {
	return fi.status
}

// Owner returns the name of the user that owns the file or directory.
func (fi *FileInfo) Owner() string {
	return fi.status.GetOwner()
}

// OwnerGroup returns the name of the group that owns the file or directory.
func (fi *FileInfo) OwnerGroup() string {
	return fi.status.GetGroup()
}

func (c *Client) getFileInfo(name string) (fi os.FileInfo, err error) {
	req := &hdfs.GetFileInfoRequestProto{Src: proto.String(name)}
	resp := &hdfs.GetFileInfoResponseProto{}

	err = c.namenode.Execute("getFileInfo", req, resp)
	if err != nil {
		if nnErr, ok := err.(*rpc.NamenodeError); ok {
			err = interpretException(nnErr.Exception, err)
		}

		return nil, err
	}

	if resp.GetFs() == nil {
		return nil, os.ErrNotExist
	}

	return newFileInfo(resp.GetFs(), name), nil
}
<commit_msg>-1 to named return parameters<commit_after>package hdfs

import (
	"code.google.com/p/goprotobuf/proto"
	hdfs "github.com/colinmarc/hdfs/protocol/hadoop_hdfs"
	"github.com/colinmarc/hdfs/rpc"
	"os"
	"path"
	"time"
)

// FileInfo implements os.FileInfo, and provides information about a file or
// directory in HDFS.
type FileInfo struct {
	name   string
	status *hdfs.HdfsFileStatusProto
}

// Stat returns an os.FileInfo describing the named file.
func (c *Client) Stat(name string) (os.FileInfo, error) {
	return c.getFileInfo(name)
}

func newFileInfo(status *hdfs.HdfsFileStatusProto, name string) *FileInfo {
	fi := &FileInfo{status: status}

	var fullName string
	if string(status.GetPath()) != "" {
		fullName = string(status.GetPath())
	} else {
		fullName = name
	}

	fi.name = path.Base(fullName)
	return fi
}

func (fi *FileInfo) Name() string {
	return fi.name
}

func (fi *FileInfo) Size() int64 {
	return int64(fi.status.GetLength())
}

func (fi *FileInfo) Mode() os.FileMode {
	return os.FileMode(fi.status.GetPermission().GetPerm())
}

func (fi *FileInfo) ModTime() time.Time {
	return time.Unix(int64(fi.status.GetModificationTime())/1000, 0)
}

func (fi *FileInfo) IsDir() bool {
	return fi.status.GetFileType() == hdfs.HdfsFileStatusProto_IS_DIR
}

// Sys returns the raw *hadoop_hdfs.HdfsFileStatusProto message from the
// namenode.
func (fi *FileInfo) Sys() interface{} {
	return fi.status
}

// Owner returns the name of the user that owns the file or directory.
func (fi *FileInfo) Owner() string {
	return fi.status.GetOwner()
}

// OwnerGroup returns the name of the group that owns the file or directory.
func (fi *FileInfo) OwnerGroup() string {
	return fi.status.GetGroup()
}

func (c *Client) getFileInfo(name string) (fi os.FileInfo, err error) {
	req := &hdfs.GetFileInfoRequestProto{Src: proto.String(name)}
	resp := &hdfs.GetFileInfoResponseProto{}

	err = c.namenode.Execute("getFileInfo", req, resp)
	if err != nil {
		if nnErr, ok := err.(*rpc.NamenodeError); ok {
			err = interpretException(nnErr.Exception, err)
		}

		return nil, err
	}

	if resp.GetFs() == nil {
		return nil, os.ErrNotExist
	}

	return newFileInfo(resp.GetFs(), name), nil
}
<|endoftext|>


<commit_before>// Package stun implements Session Traversal Utilities for NAT (STUN) RFC 5389.
//
// The stun package is intended to use by package that implements extension
// to STUN (e.g. TURN) or client/server applications.
//
// Most methods are designed to be zero allocations. If it is not enough,
// low-level methods are available. On other hand, there are helpers that
// reduce code repeat.
//
// See examples for Message for basic usage, or https://github.com/ernado/turn
// package for example of stun extension implementation.
package stun

import "encoding/binary"

// bin is shorthand to binary.BigEndian.
var bin = binary.BigEndian

// IANA assigned ports for "stun" protocol/
const (
	DefaultPort    = 3478
	DefaultTLSPort = 5349
)

type transactionIDSetter bool

func (transactionIDSetter) AddTo(m *Message) error {
	return m.NewTransactionID()
}

// TransactionID is Setter for m.TransactionID.
var TransactionID Setter = transactionIDSetter(true)
<commit_msg>docs: fix typo in comment<commit_after>// Package stun implements Session Traversal Utilities for NAT (STUN) RFC 5389.
//
// The stun package is intended to use by package that implements extension
// to STUN (e.g. TURN) or client/server applications.
//
// Most methods are designed to be zero allocations. If it is not enough,
// low-level methods are available. On other hand, there are helpers that
// reduce code repeat.
//
// See examples for Message for basic usage, or https://github.com/ernado/turn
// package for example of stun extension implementation.
package stun

import "encoding/binary"

// bin is shorthand to binary.BigEndian.
var bin = binary.BigEndian

// IANA assigned ports for "stun" protocol.
const (
	DefaultPort    = 3478
	DefaultTLSPort = 5349
)

type transactionIDSetter bool

func (transactionIDSetter) AddTo(m *Message) error {
	return m.NewTransactionID()
}

// TransactionID is Setter for m.TransactionID.
var TransactionID Setter = transactionIDSetter(true)
<|endoftext|>


<commit_before>// Copyright (c) 2013 ActiveState Software Inc. All rights reserved.

package tail

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"time"

	"github.com/masahide/tail/ratelimiter"
	"github.com/masahide/tail/util"
	"github.com/masahide/tail/watch"
	"gopkg.in/tomb.v1"
)

var (
	ErrStop = fmt.Errorf("tail should now stop")
)

const (
	NewLineNotify int = iota
	NewFileNotify
	TickerNotify
)

type Line struct {
	Time       time.Time
	Text       []byte
	Filename   string
	Offset     int64
	OpenTime   time.Time
	Err        error // Error from tail
	NotifyType int
}

// NewLine returns a Line with present time.
func NewLine(text []byte) *Line {
	return &Line{Text: text, Time: time.Now(), Err: nil}
}

// SeekInfo represents arguments to `os.Seek`
type SeekInfo struct {
	Offset int64
	Whence int // os.SEEK_*
}

// Config is used to specify how a file must be tailed.
type Config struct {
	// File-specifc
	Location    *SeekInfo     // Seek to this location before tailing
	ReOpen      bool          // Reopen recreated files (tail -F)
	ReOpenDelay time.Duration // Reopen Delay
	MustExist   bool          // Fail early if the file does not exist
	Poll        bool          // Poll for file changes instead of using inotify
	RateLimiter *ratelimiter.LeakyBucket

	// Generic IO
	Follow         bool          // Continue looking for new lines (tail -f)
	MaxLineSize    int           // If non-zero, split longer lines into multiple lines
	NotifyInterval time.Duration // Notice interval of the elapsed time

	// Logger, when nil, is set to tail.DefaultLogger
	// To disable logging: set field to tail.DiscardingLogger
	Logger *log.Logger
}

type Tail struct {
	Filename string
	Lines    chan *Line
	Config

	file     *os.File
	reader   *bufio.Reader
	tracker  *watch.InotifyTracker
	ticker   *time.Ticker
	openTime time.Time

	watcher      watch.FileWatcher
	changes      *watch.FileChanges
	reOpenNotify <-chan time.Time

	tomb.Tomb // provides: Done, Kill, Dying
}

var (
	// DefaultLogger is used when Config.Logger == nil
	DefaultLogger = log.New(os.Stderr, "", log.LstdFlags)
	// DiscardingLogger can be used to disable logging output
	DiscardingLogger = log.New(ioutil.Discard, "", 0)
)

// TailFile begins tailing the file. Output stream is made available
// via the `Tail.Lines` channel. To handle errors during tailing,
// invoke the `Wait` or `Err` method after finishing reading from the
// `Lines` channel.
func TailFile(filename string, config Config) (*Tail, error) {
	if config.ReOpen && !config.Follow {
		util.Fatal("cannot set ReOpen without Follow.")
	}

	t := &Tail{
		Filename: filename,
		Lines:    make(chan *Line),
		Config:   config,
	}

	// when Logger was not specified in config, use default logger
	if t.Logger == nil {
		t.Logger = log.New(os.Stderr, "", log.LstdFlags)
	}

	if t.Poll {
		t.watcher = watch.NewPollingFileWatcher(filename)
	} else {
		t.tracker = watch.NewInotifyTracker()
		w, err := t.tracker.NewWatcher()
		if err != nil {
			return nil, err
		}
		t.watcher = watch.NewInotifyFileWatcher(filename, w)
	}

	if t.MustExist {
		var err error
		t.file, err = OpenFile(t.Filename)
		if err != nil {
			return nil, err
		}
	}

	go t.tailFileSync()

	return t, nil
}

// Return the file's current position, like stdio's ftell().
// But this value is not very accurate.
// it may readed one line in the chan(tail.Lines),
// so it may lost one line.
func (tail *Tail) Tell() (offset int64, err error) {
	if tail.file == nil {
		return
	}
	offset, err = tail.file.Seek(0, os.SEEK_CUR)
	if err == nil {
		offset -= int64(tail.reader.Buffered())
	}
	return
}

// Stop stops the tailing activity.
func (tail *Tail) Stop() error {
	tail.Kill(nil)
	return tail.Wait()
}

func (tail *Tail) close() {
	close(tail.Lines)
	if tail.file != nil {
		tail.file.Close()
	}
}

func (tail *Tail) reopen() error {
	if tail.file != nil {
		tail.file.Close()
	}
	for {
		var err error
		tail.file, err = OpenFile(tail.Filename)
		if err != nil {
			if os.IsNotExist(err) {
				tail.Logger.Printf("Waiting for %s to appear...", tail.Filename)
				if err := tail.watcher.BlockUntilExists(&tail.Tomb); err != nil {
					if err == tomb.ErrDying {
						return err
					}
					return fmt.Errorf("Failed to detect creation of %s: %s", tail.Filename, err)
				}
				continue
			}
			return fmt.Errorf("Unable to open file %s: %s", tail.Filename, err)
		}
		break
	}
	return nil
}

func (tail *Tail) readLine() ([]byte, error) {
	line, err := tail.reader.ReadBytes(byte('\n'))
	if err != nil {
		// Note ReadString "returns the data read before the error" in
		// case of an error, including EOF, so we return it as is. The
		// caller is expected to process it if err is EOF.
		return line, err
	}

	//line = bytes.TrimRight(line, "\n")

	return line, err
}

func (tail *Tail) tailFileSync() {
	defer tail.Done()
	defer tail.close()

	tail.reOpenNotify = make(chan time.Time)
	tail.ticker = &time.Ticker{}
	if tail.NotifyInterval != 0 {
		tail.ticker = time.NewTicker(tail.NotifyInterval)
	}
	defer tail.ticker.Stop()

	if !tail.MustExist {
		// deferred first open.
		err := tail.reopen()
		if err != nil {
			if err != tomb.ErrDying {
				tail.Kill(err)
			}
			return
		}
	}

	// Seek to requested location on first open of the file.
	offset := int64(0)
	if tail.Location != nil {
		offset = tail.Location.Offset
		_, err := tail.file.Seek(offset, tail.Location.Whence)
		tail.Logger.Printf("Seeked %s - %+v\n", tail.Filename, tail.Location)
		if err != nil {
			tail.Killf("Seek error on %s: %s", tail.Filename, err)
			return
		}
	}

	tail.openReader()
	tail.Lines <- &Line{NotifyType: NewFileNotify, Filename: tail.Filename, Offset: offset, Time: time.Now(), OpenTime: tail.openTime}

	// Read line by line.
	for {
		// grab the position in case we need to back up in the event of a half-line
		offset, err := tail.Tell()
		if err != nil {
			tail.Kill(err)
			return
		}

		line, err := tail.readLine()

		// Process `line` even if err is EOF.
		if err == nil {
			cooloff := !tail.sendLine(line)
			if cooloff {
				// Wait a second before seeking till the end of
				// file when rate limit is reached.
				msg := fmt.Sprintf(
					"Too much log activity; waiting a second " +
						"before resuming tailing")
				tail.Lines <- &Line{Text: []byte(msg), Time: time.Now(), Filename: tail.Filename, OpenTime: tail.openTime, Offset: offset, Err: fmt.Errorf(msg)}
				select {
				case <-time.After(time.Second):
				case <-tail.Dying():
					return
				}
				err = tail.seekEnd()
				if err != nil {
					tail.Kill(err)
					return
				}
			}
		} else if err == io.EOF {
			if !tail.Follow {
				if len(line) != 0 {
					tail.sendLine(line)
				}
				return
			}

			if tail.Follow && len(line) != 0 {
				// this has the potential to never return the last line if
				// it's not followed by a newline; seems a fair trade here
				err := tail.seekTo(SeekInfo{Offset: offset, Whence: 0})
				if err != nil {
					tail.Kill(err)
					return
				}
			}

			// When EOF is reached, wait for more data to become
			// available. Wait strategy is based on the `tail.watcher`
			// implementation (inotify or polling).
			err := tail.waitForChanges()
			if err != nil {
				if err != ErrStop {
					tail.Kill(err)
				}
				return
			}
		} else {
			// non-EOF error
			tail.Killf("Error reading %s: %s", tail.Filename, err)
			return
		}

		select {
		case <-tail.Dying():
			return
		default:
		}
	}
}

// waitForChanges waits until the file has been appended, deleted,
// moved or truncated. When moved or deleted - the file will be
// reopened if ReOpen is true. Truncated files are always reopened.
func (tail *Tail) waitForChanges() error {
	if tail.changes == nil {
		st, err := tail.file.Stat()
		if err != nil {
			return err
		}
		tail.changes = tail.watcher.ChangeEvents(&tail.Tomb, st)
	}

	for {

		select {
		case <-tail.ticker.C:
			offset, err := tail.Tell()
			if err != nil {
				return err
			}
			tail.Lines <- &Line{NotifyType: TickerNotify, Time: time.Now(), Filename: tail.Filename, OpenTime: tail.openTime, Offset: offset}
			continue
		case <-tail.changes.Modified:
			return nil
		case <-tail.changes.Deleted:
			if tail.ReOpen {
				tail.Logger.Printf("moved/deleted file %s ... Reopen delay %s", tail.Filename, tail.ReOpenDelay)
				tail.reOpenNotify = time.After(tail.ReOpenDelay)
				continue
			} else {
				tail.changes = nil
				tail.Logger.Printf("Stopping tail as file no longer exists: %s", tail.Filename)
				return ErrStop
			}
		case <-tail.changes.Truncated:
			// Always reopen truncated files (Follow is true)
			tail.Logger.Printf("Re-opening truncated file %s ...", tail.Filename)
			if err := tail.reopen(); err != nil {
				return err
			}
			tail.Logger.Printf("Successfully reopened truncated %s", tail.Filename)
			tail.openReader()
			return nil
		case <-tail.reOpenNotify:
			tail.changes = nil
			// XXX: we must not log from a library.
			tail.Logger.Printf("Re-opening moved/deleted file %s ...", tail.Filename)
			if err := tail.reopen(); err != nil {
				return err
			}
			tail.Logger.Printf("Successfully reopened %s", tail.Filename)
			tail.openReader()
			return nil
		case <-tail.Dying():
			return ErrStop
		}
		panic("unreachable")
	}
}

func (tail *Tail) openReader() {
	if tail.MaxLineSize > 0 {
		// add 2 to account for newline characters
		tail.reader = bufio.NewReaderSize(tail.file, tail.MaxLineSize+2)
	} else {
		tail.reader = bufio.NewReader(tail.file)
	}
	fi, err := os.Stat(tail.Filename)
	if err != nil {
		tail.openTime = time.Now()
		return
	}
	tail.openTime = fi.ModTime()
}

func (tail *Tail) seekEnd() error {
	return tail.seekTo(SeekInfo{Offset: 0, Whence: 2})
}

func (tail *Tail) seekTo(pos SeekInfo) error {
	_, err := tail.file.Seek(pos.Offset, pos.Whence)
	if err != nil {
		return fmt.Errorf("Seek error on %s: %s", tail.Filename, err)
	}
	// Reset the read buffer whenever the file is re-seek'ed
	tail.reader.Reset(tail.file)
	return nil
}

// sendLine sends the line(s) to Lines channel, splitting longer lines
// if necessary. Return false if rate limit is reached.
func (tail *Tail) sendLine(line []byte) bool {
	now := time.Now()
	offset, err := tail.Tell()
	if err != nil {
		tail.Kill(err)
		return true
	}
	tail.Lines <- &Line{NotifyType: NewLineNotify, Text: line, Time: now, Filename: tail.Filename, OpenTime: tail.openTime, Offset: offset}

	if tail.Config.RateLimiter != nil {
		ok := tail.Config.RateLimiter.Pour(uint16(1))
		if !ok {
			tail.Logger.Printf("Leaky bucket full (%v); entering 1s cooloff period.\n",
				tail.Filename)
			return false
		}
	}

	return true
}

// Cleanup removes inotify watches added by the tail package. This function is
// meant to be invoked from a process's exit handler. Linux kernel may not
// automatically remove inotify watches after the process exits.
func (tail *Tail) Cleanup() {
	if tail.tracker != nil {
		tail.tracker.CloseAll()
	}
}
<commit_msg>add debug log<commit_after>// Copyright (c) 2013 ActiveState Software Inc. All rights reserved.

package tail

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"time"

	"github.com/masahide/tail/ratelimiter"
	"github.com/masahide/tail/util"
	"github.com/masahide/tail/watch"
	"gopkg.in/tomb.v1"
)

var (
	ErrStop = fmt.Errorf("tail should now stop")
)

const (
	NewLineNotify int = iota
	NewFileNotify
	TickerNotify
)

type Line struct {
	Time       time.Time
	Text       []byte
	Filename   string
	Offset     int64
	OpenTime   time.Time
	Err        error // Error from tail
	NotifyType int
}

// NewLine returns a Line with present time.
func NewLine(text []byte) *Line {
	return &Line{Text: text, Time: time.Now(), Err: nil}
}

// SeekInfo represents arguments to `os.Seek`
type SeekInfo struct {
	Offset int64
	Whence int // os.SEEK_*
}

// Config is used to specify how a file must be tailed.
type Config struct {
	// File-specifc
	Location    *SeekInfo     // Seek to this location before tailing
	ReOpen      bool          // Reopen recreated files (tail -F)
	ReOpenDelay time.Duration // Reopen Delay
	MustExist   bool          // Fail early if the file does not exist
	Poll        bool          // Poll for file changes instead of using inotify
	RateLimiter *ratelimiter.LeakyBucket

	// Generic IO
	Follow         bool          // Continue looking for new lines (tail -f)
	MaxLineSize    int           // If non-zero, split longer lines into multiple lines
	NotifyInterval time.Duration // Notice interval of the elapsed time

	// Logger, when nil, is set to tail.DefaultLogger
	// To disable logging: set field to tail.DiscardingLogger
	Logger *log.Logger
}

type Tail struct {
	Filename string
	Lines    chan *Line
	Config

	file     *os.File
	reader   *bufio.Reader
	tracker  *watch.InotifyTracker
	ticker   *time.Ticker
	openTime time.Time

	watcher      watch.FileWatcher
	changes      *watch.FileChanges
	reOpenNotify <-chan time.Time

	tomb.Tomb // provides: Done, Kill, Dying
}

var (
	// DefaultLogger is used when Config.Logger == nil
	DefaultLogger = log.New(os.Stderr, "", log.LstdFlags)
	// DiscardingLogger can be used to disable logging output
	DiscardingLogger = log.New(ioutil.Discard, "", 0)
)

// TailFile begins tailing the file. Output stream is made available
// via the `Tail.Lines` channel. To handle errors during tailing,
// invoke the `Wait` or `Err` method after finishing reading from the
// `Lines` channel.
func TailFile(filename string, config Config) (*Tail, error) {
	if config.ReOpen && !config.Follow {
		util.Fatal("cannot set ReOpen without Follow.")
	}

	t := &Tail{
		Filename: filename,
		Lines:    make(chan *Line),
		Config:   config,
	}

	// when Logger was not specified in config, use default logger
	if t.Logger == nil {
		t.Logger = log.New(os.Stderr, "", log.LstdFlags)
	}

	if t.Poll {
		t.watcher = watch.NewPollingFileWatcher(filename)
	} else {
		t.tracker = watch.NewInotifyTracker()
		w, err := t.tracker.NewWatcher()
		if err != nil {
			return nil, err
		}
		t.Logger.Printf("start InotifyFileWatcher: %s", filename)
		t.watcher = watch.NewInotifyFileWatcher(filename, w)
	}

	if t.MustExist {
		var err error
		t.file, err = OpenFile(t.Filename)
		if err != nil {
			return nil, err
		}
	}

	go t.tailFileSync()

	return t, nil
}

// Return the file's current position, like stdio's ftell().
// But this value is not very accurate.
// it may readed one line in the chan(tail.Lines),
// so it may lost one line.
func (tail *Tail) Tell() (offset int64, err error) {
	if tail.file == nil {
		return
	}
	offset, err = tail.file.Seek(0, os.SEEK_CUR)
	if err == nil {
		offset -= int64(tail.reader.Buffered())
	}
	return
}

// Stop stops the tailing activity.
func (tail *Tail) Stop() error {
	tail.Kill(nil)
	return tail.Wait()
}

func (tail *Tail) close() {
	close(tail.Lines)
	if tail.file != nil {
		tail.file.Close()
	}
}

func (tail *Tail) reopen() error {
	if tail.file != nil {
		tail.file.Close()
	}
	for {
		var err error
		tail.file, err = OpenFile(tail.Filename)
		if err != nil {
			if os.IsNotExist(err) {
				tail.Logger.Printf("Waiting for %s to appear...", tail.Filename)
				if err := tail.watcher.BlockUntilExists(&tail.Tomb); err != nil {
					if err == tomb.ErrDying {
						return err
					}
					return fmt.Errorf("Failed to detect creation of %s: %s", tail.Filename, err)
				}
				continue
			}
			return fmt.Errorf("Unable to open file %s: %s", tail.Filename, err)
		}
		break
	}
	return nil
}

func (tail *Tail) readLine() ([]byte, error) {
	line, err := tail.reader.ReadBytes(byte('\n'))
	if err != nil {
		// Note ReadString "returns the data read before the error" in
		// case of an error, including EOF, so we return it as is. The
		// caller is expected to process it if err is EOF.
		return line, err
	}

	//line = bytes.TrimRight(line, "\n")

	return line, err
}

func (tail *Tail) tailFileSync() {
	defer tail.Done()
	defer tail.close()

	tail.reOpenNotify = make(chan time.Time)
	tail.ticker = &time.Ticker{}
	if tail.NotifyInterval != 0 {
		tail.ticker = time.NewTicker(tail.NotifyInterval)
	}
	defer tail.ticker.Stop()

	if !tail.MustExist {
		// deferred first open.
		err := tail.reopen()
		if err != nil {
			if err != tomb.ErrDying {
				tail.Kill(err)
			}
			return
		}
	}

	// Seek to requested location on first open of the file.
	offset := int64(0)
	if tail.Location != nil {
		offset = tail.Location.Offset
		_, err := tail.file.Seek(offset, tail.Location.Whence)
		tail.Logger.Printf("Seeked %s - %+v\n", tail.Filename, tail.Location)
		if err != nil {
			tail.Killf("Seek error on %s: %s", tail.Filename, err)
			return
		}
	}

	tail.openReader()
	tail.Lines <- &Line{NotifyType: NewFileNotify, Filename: tail.Filename, Offset: offset, Time: time.Now(), OpenTime: tail.openTime}

	// Read line by line.
	for {
		// grab the position in case we need to back up in the event of a half-line
		offset, err := tail.Tell()
		if err != nil {
			tail.Kill(err)
			return
		}

		line, err := tail.readLine()

		// Process `line` even if err is EOF.
		if err == nil {
			cooloff := !tail.sendLine(line)
			if cooloff {
				// Wait a second before seeking till the end of
				// file when rate limit is reached.
				msg := fmt.Sprintf(
					"Too much log activity; waiting a second " +
						"before resuming tailing")
				tail.Lines <- &Line{Text: []byte(msg), Time: time.Now(), Filename: tail.Filename, OpenTime: tail.openTime, Offset: offset, Err: fmt.Errorf(msg)}
				select {
				case <-time.After(time.Second):
				case <-tail.Dying():
					return
				}
				err = tail.seekEnd()
				if err != nil {
					tail.Kill(err)
					return
				}
			}
		} else if err == io.EOF {
			if !tail.Follow {
				if len(line) != 0 {
					tail.sendLine(line)
				}
				return
			}

			if tail.Follow && len(line) != 0 {
				// this has the potential to never return the last line if
				// it's not followed by a newline; seems a fair trade here
				err := tail.seekTo(SeekInfo{Offset: offset, Whence: 0})
				if err != nil {
					tail.Kill(err)
					return
				}
			}

			// When EOF is reached, wait for more data to become
			// available. Wait strategy is based on the `tail.watcher`
			// implementation (inotify or polling).
			err := tail.waitForChanges()
			if err != nil {
				if err != ErrStop {
					tail.Kill(err)
				}
				return
			}
		} else {
			// non-EOF error
			tail.Killf("Error reading %s: %s", tail.Filename, err)
			return
		}

		select {
		case <-tail.Dying():
			return
		default:
		}
	}
}

// waitForChanges waits until the file has been appended, deleted,
// moved or truncated. When moved or deleted - the file will be
// reopened if ReOpen is true. Truncated files are always reopened.
func (tail *Tail) waitForChanges() error {
	if tail.changes == nil {
		st, err := tail.file.Stat()
		if err != nil {
			return err
		}
		tail.changes = tail.watcher.ChangeEvents(&tail.Tomb, st)
	}

	for {

		select {
		case <-tail.ticker.C:
			offset, err := tail.Tell()
			if err != nil {
				return err
			}
			tail.Lines <- &Line{NotifyType: TickerNotify, Time: time.Now(), Filename: tail.Filename, OpenTime: tail.openTime, Offset: offset}
			continue
		case <-tail.changes.Modified:
			return nil
		case <-tail.changes.Deleted:
			if tail.ReOpen {
				tail.Logger.Printf("moved/deleted file %s ... Reopen delay %s", tail.Filename, tail.ReOpenDelay)
				tail.reOpenNotify = time.After(tail.ReOpenDelay)
				continue
			} else {
				tail.changes = nil
				tail.Logger.Printf("Stopping tail as file no longer exists: %s", tail.Filename)
				return ErrStop
			}
		case <-tail.changes.Truncated:
			// Always reopen truncated files (Follow is true)
			tail.Logger.Printf("Re-opening truncated file %s ...", tail.Filename)
			if err := tail.reopen(); err != nil {
				return err
			}
			tail.Logger.Printf("Successfully reopened truncated %s", tail.Filename)
			tail.openReader()
			return nil
		case <-tail.reOpenNotify:
			tail.changes = nil
			// XXX: we must not log from a library.
			tail.Logger.Printf("Re-opening moved/deleted file %s ...", tail.Filename)
			if err := tail.reopen(); err != nil {
				return err
			}
			tail.Logger.Printf("Successfully reopened %s", tail.Filename)
			tail.openReader()
			return nil
		case <-tail.Dying():
			return ErrStop
		}
		panic("unreachable")
	}
}

func (tail *Tail) openReader() {
	if tail.MaxLineSize > 0 {
		// add 2 to account for newline characters
		tail.reader = bufio.NewReaderSize(tail.file, tail.MaxLineSize+2)
	} else {
		tail.reader = bufio.NewReader(tail.file)
	}
	fi, err := os.Stat(tail.Filename)
	if err != nil {
		tail.openTime = time.Now()
		return
	}
	tail.openTime = fi.ModTime()
}

func (tail *Tail) seekEnd() error {
	return tail.seekTo(SeekInfo{Offset: 0, Whence: 2})
}

func (tail *Tail) seekTo(pos SeekInfo) error {
	_, err := tail.file.Seek(pos.Offset, pos.Whence)
	if err != nil {
		return fmt.Errorf("Seek error on %s: %s", tail.Filename, err)
	}
	// Reset the read buffer whenever the file is re-seek'ed
	tail.reader.Reset(tail.file)
	return nil
}

// sendLine sends the line(s) to Lines channel, splitting longer lines
// if necessary. Return false if rate limit is reached.
func (tail *Tail) sendLine(line []byte) bool {
	now := time.Now()
	offset, err := tail.Tell()
	if err != nil {
		tail.Kill(err)
		return true
	}
	tail.Lines <- &Line{NotifyType: NewLineNotify, Text: line, Time: now, Filename: tail.Filename, OpenTime: tail.openTime, Offset: offset}

	if tail.Config.RateLimiter != nil {
		ok := tail.Config.RateLimiter.Pour(uint16(1))
		if !ok {
			tail.Logger.Printf("Leaky bucket full (%v); entering 1s cooloff period.\n",
				tail.Filename)
			return false
		}
	}

	return true
}

// Cleanup removes inotify watches added by the tail package. This function is
// meant to be invoked from a process's exit handler. Linux kernel may not
// automatically remove inotify watches after the process exits.
func (tail *Tail) Cleanup() {
	if tail.tracker != nil {
		tail.tracker.CloseAll()
	}
}
<|endoftext|>


<commit_before>package atc

import (
	"fmt"
	"strings"
)

type TaskConfig struct {
	// The platform the task must run on (e.g. linux, windows).
	Platform string `json:"platform,omitempty" yaml:"platform,omitempty"`

	// Optional string specifying an image to use for the build. Depending on the
	// platform, this may or may not be required (e.g. Windows/OS X vs. Linux).
	Image string `json:"image,omitempty"   yaml:"image,omitempty"`

	ImageResource *TaskImageConfig `json:"image_resource,omitempty"	yaml:"image_resource,omitempty"`

	// Parameters to pass to the task via environment variables.
	Params map[string]string `json:"params,omitempty"  yaml:"params,omitempty"`

	// Script to execute.
	Run TaskRunConfig `json:"run,omitempty"     yaml:"run,omitempty"`

	// The set of (logical, name-only) inputs required by the task.
	Inputs []TaskInputConfig `json:"inputs,omitempty"  yaml:"inputs,omitempty"`

	// The set of (logical, name-only) outputs provided by the task.
	Outputs []TaskOutputConfig `json:"outputs,omitempty"  yaml:"outputs,omitempty"`
}

type TaskImageConfig struct {
	Type   string `yaml:"type" json:"type" mapstructure:"type"`
	Source Source `yaml:"source" json:"source" mapstructure:"source"`
}

func (a TaskConfig) Merge(b TaskConfig) TaskConfig {
	if b.Platform != "" {
		a.Platform = b.Platform
	}

	if b.Image != "" {
		a.Image = b.Image
	}

	if len(a.Params) > 0 {
		newParams := map[string]string{}

		for k, v := range a.Params {
			newParams[k] = v
		}

		for k, v := range b.Params {
			newParams[k] = v
		}

		a.Params = newParams
	} else {
		a.Params = b.Params
	}

	if len(b.Inputs) != 0 {
		a.Inputs = b.Inputs
	}

	if b.Run.Path != "" {
		a.Run = b.Run
	}

	return a
}

func (config TaskConfig) Validate() error {
	messages := []string{"invalid task configuration:"}

	var invalid bool
	if config.Platform == "" {
		messages = append(messages, "  missing 'platform'")
		invalid = true
	}

	if config.Run.Path == "" {
		messages = append(messages, "  missing path to executable to run")
		invalid = true
	}

	if invalid {
		return fmt.Errorf(strings.Join(messages, "\n"))
	}

	return nil
}

type TaskRunConfig struct {
	Path string   `json:"path" yaml:"path"`
	Args []string `json:"args,omitempty" yaml:"args"`
}

type TaskInputConfig struct {
	Name string `json:"name" yaml:"name"`
	Path string `json:"path,omitempty" yaml:"path"`
}

type TaskOutputConfig struct {
	Name string `json:"name" yaml:"name"`
	Path string `json:"path,omitempty" yaml:"path"`
}

type MetadataField struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}
<commit_msg>fix unmarshalling. no, really.<commit_after>package atc

import (
	"fmt"
	"strings"
)

type TaskConfig struct {
	// The platform the task must run on (e.g. linux, windows).
	Platform string `json:"platform,omitempty" yaml:"platform,omitempty"`

	// Optional string specifying an image to use for the build. Depending on the
	// platform, this may or may not be required (e.g. Windows/OS X vs. Linux).
	Image string `json:"image,omitempty"   yaml:"image,omitempty"`

	ImageResource *TaskImageConfig `json:"image_resource,omitempty" yaml:"image_resource,omitempty"`

	// Parameters to pass to the task via environment variables.
	Params map[string]string `json:"params,omitempty"  yaml:"params,omitempty"`

	// Script to execute.
	Run TaskRunConfig `json:"run,omitempty"     yaml:"run,omitempty"`

	// The set of (logical, name-only) inputs required by the task.
	Inputs []TaskInputConfig `json:"inputs,omitempty"  yaml:"inputs,omitempty"`

	// The set of (logical, name-only) outputs provided by the task.
	Outputs []TaskOutputConfig `json:"outputs,omitempty"  yaml:"outputs,omitempty"`
}

type TaskImageConfig struct {
	Type   string `yaml:"type" json:"type" mapstructure:"type"`
	Source Source `yaml:"source" json:"source" mapstructure:"source"`
}

func (a TaskConfig) Merge(b TaskConfig) TaskConfig {
	if b.Platform != "" {
		a.Platform = b.Platform
	}

	if b.Image != "" {
		a.Image = b.Image
	}

	if len(a.Params) > 0 {
		newParams := map[string]string{}

		for k, v := range a.Params {
			newParams[k] = v
		}

		for k, v := range b.Params {
			newParams[k] = v
		}

		a.Params = newParams
	} else {
		a.Params = b.Params
	}

	if len(b.Inputs) != 0 {
		a.Inputs = b.Inputs
	}

	if b.Run.Path != "" {
		a.Run = b.Run
	}

	return a
}

func (config TaskConfig) Validate() error {
	messages := []string{"invalid task configuration:"}

	var invalid bool
	if config.Platform == "" {
		messages = append(messages, "  missing 'platform'")
		invalid = true
	}

	if config.Run.Path == "" {
		messages = append(messages, "  missing path to executable to run")
		invalid = true
	}

	if invalid {
		return fmt.Errorf(strings.Join(messages, "\n"))
	}

	return nil
}

type TaskRunConfig struct {
	Path string   `json:"path" yaml:"path"`
	Args []string `json:"args,omitempty" yaml:"args"`
}

type TaskInputConfig struct {
	Name string `json:"name" yaml:"name"`
	Path string `json:"path,omitempty" yaml:"path"`
}

type TaskOutputConfig struct {
	Name string `json:"name" yaml:"name"`
	Path string `json:"path,omitempty" yaml:"path"`
}

type MetadataField struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"strings"

	"github.com/fatih/color"
	"github.com/gammons/todolist/todolist"
	"github.com/skratchdot/open-golang/open"
)

const (
	VERSION = "0.6"
)

func main() {
	if len(os.Args) <= 1 {
		usage()
		os.Exit(0)
	}
	input := strings.Join(os.Args[1:], " ")
	routeInput(os.Args[1], input)
}

func usage() {
	blue := color.New(color.FgBlue)
	cyan := color.New(color.FgCyan)
	yellow := color.New(color.FgYellow)

	blueBold := blue.Add(color.Bold)

	fmt.Printf("todo v%s, a simple, command line based, GTD-style todo manager\n", VERSION)

	blueBold.Println("\nAdding todos")
	fmt.Println("  the 'a' command adds todos.")
	fmt.Println("  You can also optionally specify a due date.")
	fmt.Println("  Specify a due date by putting 'due <date>' at the end, where <date> is in (tod|today|tom|tomorrow|mon|tue|wed|thu|fri|sat|sun)")
	fmt.Println("\n  Examples for adding a todo:")
	yellow.Println("\ttodo a Meeting with @bob about +importantPrject due today")
	yellow.Println("\ttodo a +work +verify did @john fix the build\\?")

	blueBold.Println("\nListing todos")
	fmt.Println("  When listing todos, you can filter and group the output.\n")

	fmt.Println("  todo l due (tod|today|tom|tomorrow|overdue|this week|next week|last week|mon|tue|wed|thu|fri|sat|sun|none)")
	fmt.Println("  todo l overdue")

	cyan.Println("  Filtering by date:\n")
	yellow.Println("\ttodo l due tod")
	fmt.Println("\tlists all todos due today\n")
	yellow.Println("\ttodo l due tom")
	fmt.Println("\tlists all todos due tomorrow\n")
	yellow.Println("\ttodo l due mon")
	fmt.Println("\tlists all todos due monday\n")
	yellow.Println("\ttodo l overdue")
	fmt.Println("\tlists all todos where the due date is in the past\n")
	yellow.Println("\ttodo agenda")
	fmt.Println("\tlists all todos where the due date is today or in the past\n")

	cyan.Println("  Grouping:")
	fmt.Println("  You can group todos by context or project.")
	yellow.Println("\ttodo l by c")
	fmt.Println("\tlists all todos grouped by context\n")
	yellow.Println("\ttodo l by p")
	fmt.Println("\tlists all todos grouped by project\n")

	cyan.Println("  Grouping and filtering:")
	fmt.Println("  Of course, you can combine grouping and filtering to get a nice formatted list.\n")
	yellow.Println("\ttodo l due today by c")
	fmt.Println("\tlists all todos due today grouped by context\n")
	yellow.Println("\ttodo l +project due this week by c")
	fmt.Println("\tlists all todos due today for +project, grouped by context\n")
	yellow.Println("\ttodo l @frank due tom by p")
	fmt.Println("\tlists all todos due tomorrow concerining @frank for +project, grouped by project\n")

	blueBold.Println("\nCompleting and uncompleting ")
	fmt.Println("Complete and Uncomplete a todo by its Id:\n")
	yellow.Println("\ttodo c 33")
	fmt.Println("\tCompletes a todo with id 33\n")
	yellow.Println("\ttodo uc 33")
	fmt.Println("\tUncompletes a todo with id 33\n")

	blueBold.Println("\nPrioritizing")
	fmt.Println("Todos have a priority flag, which will make them bold when listed.\n")
	yellow.Println("\ttodo p 33")
	fmt.Println("\tPrioritizes a todo with id 33\n")
	yellow.Println("\ttodo up 33")
	fmt.Println("\tUn-prioritizes a todo with id 33\n")
	yellow.Println("\ttodo l p")
	fmt.Println("\tlist all priority todos\n")

	blueBold.Println("\nArchiving")
	fmt.Println("You can archive todos once they are done, or if you might come back to them.")
	fmt.Println("By default, todo will only show unarchived todos.\n")
	yellow.Println("\ttodo ar 33")
	fmt.Println("\tArchives a todo with id 33\n")
	yellow.Println("\ttodo ac")
	fmt.Println("\tArchives all completed todos\n")
	yellow.Println("\ttodo l archived")
	fmt.Println("\tlist all archived todos\n")

	blueBold.Println("\nEditing due dates")
	yellow.Println("\ttodo e 33 due mon")
	fmt.Println("\tEdits the todo with 33 and sets the due date to this coming Monday\n")
	yellow.Println("\ttodo e 33 due none")
	fmt.Println("\tEdits the todo with 33 and removes the due date\n")

	blueBold.Println("\nExpanding existing todos")
	yellow.Println("\ttodo ex 39 +final: read physics due mon, do literature report due fri")
	fmt.Println("\tRemoves the todo with id 39, and adds following two todos\n")

	blueBold.Println("\nDeleting")
	yellow.Println("\ttodo d 33")
	fmt.Println("\tDeletes a todo with id 33\n")
	fmt.Println("Todolist was lovingly crafted by Grant Ammons (https://twitter.com/gammons).")
	fmt.Println("For full documentation, please visit http://todolist.site")

	blueBold.Println("\nGarbage Collection")
	yellow.Println("\ttodo gc")
	fmt.Println("\tDeletes all archived todos.\n")
}

func routeInput(command string, input string) {
	app := todolist.NewApp()
	switch command {
	case "l", "list", "agenda":
		app.ListTodos(input)
	case "a", "add":
		app.AddTodo(input)
	case "d", "delete":
		app.DeleteTodo(input)
	case "c", "complete":
		app.CompleteTodo(input)
	case "uc", "uncomplete":
		app.UncompleteTodo(input)
	case "ar", "archive":
		app.ArchiveTodo(input)
	case "uar", "unarchive":
		app.UnarchiveTodo(input)
	case "ac":
		app.ArchiveCompleted()
	case "e", "edit":
		app.EditTodo(input)
	case "ex", "expand":
		app.ExpandTodo(input)
	case "gc":
		app.GarbageCollect()
	case "p", "prioritize":
		app.PrioritizeTodo(input)
	case "up", "unprioritize":
		app.UnprioritizeTodo(input)
	case "init":
		app.InitializeRepo()
	case "web":
		if err := app.Load(); err != nil {
			os.Exit(1)
		} else {
			web := todolist.NewWebapp()
			fmt.Println("Now serving todolist web.\nHead to http://localhost:7890 to see your todo list!")
			open.Start("http://localhost:7890")
			web.Run()
		}
	}
}
<commit_msg>Updated the command line docs<commit_after>package main

import (
	"fmt"
	"os"
	"strings"

	"github.com/fatih/color"
	"github.com/gammons/todolist/todolist"
	"github.com/skratchdot/open-golang/open"
)

const (
	VERSION = "0.6"
)

func main() {
	if len(os.Args) <= 1 {
		usage()
		os.Exit(0)
	}
	input := strings.Join(os.Args[1:], " ")
	routeInput(os.Args[1], input)
}

func usage() {
	blue := color.New(color.FgBlue)
	cyan := color.New(color.FgCyan)
	yellow := color.New(color.FgYellow)

	blueBold := blue.Add(color.Bold)

	fmt.Printf("todo v%s, a simple, command line based, GTD-style todo manager\n", VERSION)

	blueBold.Println("\nAdding todos")
	fmt.Println("  the 'a' command adds todos.")
	fmt.Println("  You can also optionally specify a due date.")
	fmt.Println("  Specify a due date by putting 'due <date>' at the end, where <date> is in (tod|today|tom|tomorrow|mon|tue|wed|thu|fri|sat|sun)")
	fmt.Println("\n  Examples for adding a todo:")
	yellow.Println("\ttodo a Meeting with @bob about +importantPrject due today")
	yellow.Println("\ttodo a +work +verify did @john fix the build\\?")

	blueBold.Println("\nListing todos")
	fmt.Println("  When listing todos, you can filter and group the output.\n")

	fmt.Println("  todo l due (tod|today|tom|tomorrow|overdue|this week|next week|last week|mon|tue|wed|thu|fri|sat|sun|none)")
	fmt.Println("  todo l overdue")

	cyan.Println("  Filtering by date:\n")
	yellow.Println("\ttodo l due tod")
	fmt.Println("\tlists all todos due today\n")
	yellow.Println("\ttodo l due tom")
	fmt.Println("\tlists all todos due tomorrow\n")
	yellow.Println("\ttodo l due mon")
	fmt.Println("\tlists all todos due monday\n")
	yellow.Println("\ttodo l overdue")
	fmt.Println("\tlists all todos where the due date is in the past\n")
	yellow.Println("\ttodo agenda")
	fmt.Println("\tlists all todos where the due date is today or in the past\n")

	fmt.Println("  todo l completed (tod|today|this week)")
	cyan.Println("  Filtering by date:\n")

	yellow.Println("\ttodo l completed (tod|today)")
	fmt.Println("\tlists all todos that were completed today\n")
	yellow.Println("\ttodo l completed this week")
	fmt.Println("\tlists all todos that were completed this week\n")

	cyan.Println("  Grouping:")
	fmt.Println("  You can group todos by context or project.")
	yellow.Println("\ttodo l by c")
	fmt.Println("\tlists all todos grouped by context\n")
	yellow.Println("\ttodo l by p")
	fmt.Println("\tlists all todos grouped by project\n")

	cyan.Println("  Grouping and filtering:")
	fmt.Println("  Of course, you can combine grouping and filtering to get a nice formatted list.\n")
	yellow.Println("\ttodo l due today by c")
	fmt.Println("\tlists all todos due today grouped by context\n")
	yellow.Println("\ttodo l +project due this week by c")
	fmt.Println("\tlists all todos due today for +project, grouped by context\n")
	yellow.Println("\ttodo l @frank due tom by p")
	fmt.Println("\tlists all todos due tomorrow concerining @frank for +project, grouped by project\n")

	blueBold.Println("\nCompleting and uncompleting ")
	fmt.Println("Complete and Uncomplete a todo by its Id:\n")
	yellow.Println("\ttodo c 33")
	fmt.Println("\tCompletes a todo with id 33\n")
	yellow.Println("\ttodo uc 33")
	fmt.Println("\tUncompletes a todo with id 33\n")

	blueBold.Println("\nPrioritizing")
	fmt.Println("Todos have a priority flag, which will make them bold when listed.\n")
	yellow.Println("\ttodo p 33")
	fmt.Println("\tPrioritizes a todo with id 33\n")
	yellow.Println("\ttodo up 33")
	fmt.Println("\tUn-prioritizes a todo with id 33\n")
	yellow.Println("\ttodo l p")
	fmt.Println("\tlist all priority todos\n")

	blueBold.Println("\nArchiving")
	fmt.Println("You can archive todos once they are done, or if you might come back to them.")
	fmt.Println("By default, todo will only show unarchived todos.\n")
	yellow.Println("\ttodo ar 33")
	fmt.Println("\tArchives a todo with id 33\n")
	yellow.Println("\ttodo ac")
	fmt.Println("\tArchives all completed todos\n")
	yellow.Println("\ttodo l archived")
	fmt.Println("\tlist all archived todos\n")

	blueBold.Println("\nEditing due dates")
	yellow.Println("\ttodo e 33 due mon")
	fmt.Println("\tEdits the todo with 33 and sets the due date to this coming Monday\n")
	yellow.Println("\ttodo e 33 due none")
	fmt.Println("\tEdits the todo with 33 and removes the due date\n")

	blueBold.Println("\nExpanding existing todos")
	yellow.Println("\ttodo ex 39 +final: read physics due mon, do literature report due fri")
	fmt.Println("\tRemoves the todo with id 39, and adds following two todos\n")

	blueBold.Println("\nDeleting")
	yellow.Println("\ttodo d 33")
	fmt.Println("\tDeletes a todo with id 33\n")
	fmt.Println("Todolist was lovingly crafted by Grant Ammons (https://twitter.com/gammons).")
	fmt.Println("For full documentation, please visit http://todolist.site")

	blueBold.Println("\nGarbage Collection")
	yellow.Println("\ttodo gc")
	fmt.Println("\tDeletes all archived todos.\n")
}

func routeInput(command string, input string) {
	app := todolist.NewApp()
	switch command {
	case "l", "list", "agenda":
		app.ListTodos(input)
	case "a", "add":
		app.AddTodo(input)
	case "d", "delete":
		app.DeleteTodo(input)
	case "c", "complete":
		app.CompleteTodo(input)
	case "uc", "uncomplete":
		app.UncompleteTodo(input)
	case "ar", "archive":
		app.ArchiveTodo(input)
	case "uar", "unarchive":
		app.UnarchiveTodo(input)
	case "ac":
		app.ArchiveCompleted()
	case "e", "edit":
		app.EditTodo(input)
	case "ex", "expand":
		app.ExpandTodo(input)
	case "gc":
		app.GarbageCollect()
	case "p", "prioritize":
		app.PrioritizeTodo(input)
	case "up", "unprioritize":
		app.UnprioritizeTodo(input)
	case "init":
		app.InitializeRepo()
	case "web":
		if err := app.Load(); err != nil {
			os.Exit(1)
		} else {
			web := todolist.NewWebapp()
			fmt.Println("Now serving todolist web.\nHead to http://localhost:7890 to see your todo list!")
			open.Start("http://localhost:7890")
			web.Run()
		}
	}
}
<|endoftext|>


<commit_before>package parse

// Copyright © 2017 Dmitry Moskowski
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

// Tree represents a single Rule match with corresponding
// information about the input, position and matched Rule.
// It will be recursive in case of nested Rule match.
type Tree struct {
	Rule
	Start  int
	End    int
	Childs []*Tree
	Data   []byte
}

// ID returns current node identifier.
func (t *Tree) ID() string {
	if t.Rule != nil {
		return t.Rule.ID()
	}
	return ""
}

// GetChilds returns a slice of the Treer with children nodes.
func (t *Tree) GetChilds() Treers {
	treer := make(Treers, len(t.Childs))
	for k, v := range t.Childs {
		treer[k] = v
	}
	return treer
}
<commit_msg>Do not embed Rule type<commit_after>package parse

// Copyright © 2017 Dmitry Moskowski
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

// Tree represents a single Rule match with corresponding
// information about the input, position and matched Rule.
// It will be recursive in case of nested Rule match.
type Tree struct {
	Rule   Rule
	Start  int
	End    int
	Childs []*Tree
	Data   []byte
}

// ID returns current node identifier.
func (t *Tree) ID() string {
	if t.Rule != nil {
		return t.Rule.ID()
	}
	return ""
}

// GetChilds returns a slice of the Treer with children nodes.
func (t *Tree) GetChilds() Treers {
	treer := make(Treers, len(t.Childs))
	for k, v := range t.Childs {
		treer[k] = v
	}
	return treer
}
<|endoftext|>


<commit_before>package chi

// Radix tree implementation below is a based on the original work by
// Armon Dadgar in https://github.com/armon/go-radix/blob/master/radix.go
// (MIT licensed). It's been heavily modified for use as a HTTP routing tree.

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

type methodTyp int

const (
	mCONNECT methodTyp = 1 << iota
	mDELETE
	mGET
	mHEAD
	mOPTIONS
	mPATCH
	mPOST
	mPUT
	mTRACE
	mSTUB

	mALL methodTyp = mCONNECT | mDELETE | mGET | mHEAD | mOPTIONS |
		mPATCH | mPOST | mPUT | mTRACE
)

var methodMap = map[string]methodTyp{
	"CONNECT": mCONNECT,
	"DELETE":  mDELETE,
	"GET":     mGET,
	"HEAD":    mHEAD,
	"OPTIONS": mOPTIONS,
	"PATCH":   mPATCH,
	"POST":    mPOST,
	"PUT":     mPUT,
	"TRACE":   mTRACE,
}

type nodeTyp uint8

const (
	ntStatic   nodeTyp = iota // /home
	ntRegexp                  // /:id([0-9]+) or #id^[0-9]+$
	ntParam                   // /:user
	ntCatchAll                // /api/v1/*
)

type node struct {
	// node type
	typ nodeTyp

	// first byte of the prefix
	label byte

	// prefix is the common prefix we ignore
	prefix string

	// pattern is the computed path of prefixes
	pattern string

	// HTTP handler on the leaf node
	handlers methodHandlers

	// chi subroutes on the leaf node
	subroutes Routes

	// Child nodes should be stored in-order for iteration,
	// in groups of the node type.
	children [ntCatchAll + 1]nodes
}

func (n *node) FindRoute(rctx *Context, path string) methodHandlers {
	// Reset the context routing pattern
	rctx.RoutePattern = ""

	// Find the routing handlers for the path
	rn := n.findRoute(rctx, path)
	if rn == nil {
		return nil
	}

	// Record the routing pattern in the request lifecycle
	if rn.pattern != "" {
		rctx.RoutePattern = rn.pattern
		rctx.RoutePatterns = append(rctx.RoutePatterns, rctx.RoutePattern)
	}

	return rn.handlers
}

func (n *node) InsertRoute(method methodTyp, pattern string, handler http.Handler) *node {
	var parent *node
	search := pattern

	for {
		// Handle key exhaustion
		if len(search) == 0 {
			// Insert or update the node's leaf handler
			n.setHandler(method, handler)
			n.pattern = pattern
			return n
		}

		// Look for the edge
		parent = n
		n = n.getEdge(search[0])

		// No edge, create one
		if n == nil {
			cn := &node{label: search[0], prefix: search, pattern: pattern}
			cn.setHandler(method, handler)
			parent.addChild(pattern, cn)
			return cn
		}

		if n.typ > ntStatic {
			// We found a wildcard node, meaning search path starts with
			// a wild prefix. Trim off the wildcard search path and continue.
			p := strings.Index(search, "/")
			if p < 0 {
				p = len(search)
			}
			search = search[p:]
			continue
		}

		// Static nodes fall below here.
		// Determine longest prefix of the search key on match.
		commonPrefix := n.longestPrefix(search, n.prefix)
		if commonPrefix == len(n.prefix) {
			// the common prefix is as long as the current node's prefix we're attempting to insert.
			// keep the search going.
			search = search[commonPrefix:]
			continue
		}

		// Split the node
		child := &node{
			typ:    ntStatic,
			prefix: search[:commonPrefix],
		}
		parent.replaceChild(search[0], child)

		// Restore the existing node
		n.label = n.prefix[commonPrefix]
		n.prefix = n.prefix[commonPrefix:]
		child.addChild(pattern, n)

		// If the new key is a subset, add to to this node
		search = search[commonPrefix:]
		if len(search) == 0 {
			child.setHandler(method, handler)
			child.pattern = pattern
			return child
		}

		// Create a new edge for the node
		subchild := &node{
			typ:     ntStatic,
			label:   search[0],
			prefix:  search,
			pattern: pattern,
		}
		subchild.setHandler(method, handler)
		child.addChild(pattern, subchild)
		return subchild
	}
}

func (n *node) findPattern(pattern string) *node {
	nn := n
	for _, nds := range nn.children {
		if len(nds) == 0 {
			continue
		}

		n = nn.getEdge(pattern[0])
		if n == nil {
			continue
		}

		idx := n.longestPrefix(pattern, n.prefix)
		xpattern := pattern[idx:]
		if xpattern[0] == '/' && idx < len(n.prefix) {
			continue
		}

		return n.findPattern(xpattern)
	}
	return nil
}

func (n *node) isLeaf() bool {
	return n.handlers != nil
}

func (n *node) addChild(pattern string, child *node) {
	search := child.prefix

	// Find any wildcard segments
	p := strings.IndexAny(search, ":*")

	// Determine new node type
	ntyp := child.typ
	if p >= 0 {
		switch search[p] {
		case ':':
			ntyp = ntParam
		case '*':
			ntyp = ntCatchAll
		}
	}

	if p == 0 {
		// Path starts with a wildcard

		handlers := child.handlers
		child.typ = ntyp

		if ntyp == ntCatchAll {
			p = -1
		} else {
			p = strings.IndexByte(search, '/')
		}
		if p < 0 {
			p = len(search)
		}
		child.prefix = search[:p]

		if p != len(search) {
			// add edge for the remaining part, split the end.
			child.handlers = nil

			search = search[p:]

			child.addChild(pattern, &node{
				typ:      ntStatic,
				label:    search[0], // this will always start with /
				prefix:   search,
				pattern:  pattern,
				handlers: handlers,
			})
		}

	} else if p > 0 {
		// Path has some wildcard

		// starts with a static segment
		handlers := child.handlers
		child.typ = ntStatic
		child.prefix = search[:p]
		child.handlers = nil

		// add the wild edge node
		search = search[p:]

		child.addChild(pattern, &node{
			typ:      ntyp,
			label:    search[0],
			prefix:   search,
			pattern:  pattern,
			handlers: handlers,
		})

	} else {
		// Path is all static
		child.typ = ntyp

	}

	n.children[child.typ] = append(n.children[child.typ], child)
	n.children[child.typ].Sort()
}

func (n *node) replaceChild(label byte, child *node) {
	for i := 0; i < len(n.children[child.typ]); i++ {
		if n.children[child.typ][i].label == label {
			n.children[child.typ][i] = child
			n.children[child.typ][i].label = label
			return
		}
	}

	panic("chi: replacing missing child")
}

func (n *node) getEdge(label byte) *node {
	for _, nds := range n.children {
		num := len(nds)
		for i := 0; i < num; i++ {
			if nds[i].label == label {
				return nds[i]
			}
		}
	}
	return nil
}

func (n *node) findEdge(ntyp nodeTyp, label byte) *node {
	nds := n.children[ntyp]
	num := len(nds)
	idx := 0

	switch ntyp {
	case ntStatic:
		i, j := 0, num-1
		for i <= j {
			idx = i + (j-i)/2
			if label > nds[idx].label {
				i = idx + 1
			} else if label < nds[idx].label {
				j = idx - 1
			} else {
				i = num // breaks cond
			}
		}
		if nds[idx].label != label {
			return nil
		}
		return nds[idx]

	default: // wild nodes
		// TODO: right now we match them all.. but regexp should
		// run through regexp matcher
		return nds[idx]
	}
}

// Recursive edge traversal by checking all nodeTyp groups along the way.
// It's like searching through a multi-dimensional radix trie.
func (n *node) findRoute(rctx *Context, path string) *node {
	nn := n
	search := path

	for t, nds := range nn.children {
		ntyp := nodeTyp(t)
		if len(nds) == 0 {
			continue
		}

		// search subset of edges of the index for a matching node
		var label byte
		if search != "" {
			label = search[0]
		}

		xn := nn.findEdge(ntyp, label) // next node
		if xn == nil {
			continue
		}

		// Prepare next search path by trimming prefix from requested path
		xsearch := search
		if xn.typ > ntStatic {
			p := -1
			if xn.typ < ntCatchAll {
				p = strings.IndexByte(xsearch, '/')
			}
			if p < 0 {
				p = len(xsearch)
			}

			if xn.typ == ntCatchAll {
				rctx.URLParams.Add("*", xsearch)
			} else {
				rctx.URLParams.Add(xn.prefix[1:], xsearch[:p])
			}

			xsearch = xsearch[p:]
		} else if strings.HasPrefix(xsearch, xn.prefix) {
			xsearch = xsearch[len(xn.prefix):]
		} else {
			continue // no match
		}

		// did we find it yet?
		if len(xsearch) == 0 {
			if xn.isLeaf() {
				return xn
			}
		}

		// recursively find the next node..
		fin := xn.findRoute(rctx, xsearch)
		if fin != nil {
			// found a node, return it
			return fin
		}

		// Did not found final handler, let's remove the param here if it was set
		if xn.typ > ntStatic {
			if xn.typ == ntCatchAll {
				rctx.URLParams.Del("*")
			} else {
				rctx.URLParams.Del(xn.prefix[1:])
			}
		}
	}

	return nil
}

// longestPrefix finds the length of the shared prefix
// of two strings
func (n *node) longestPrefix(k1, k2 string) int {
	max := len(k1)
	if l := len(k2); l < max {
		max = l
	}
	var i int
	for i = 0; i < max; i++ {
		if k1[i] != k2[i] {
			break
		}
	}
	return i
}

func (n *node) setHandler(method methodTyp, handler http.Handler) {
	if n.handlers == nil {
		n.handlers = make(methodHandlers, 0)
	}
	if method&mSTUB == mSTUB {
		n.handlers[mSTUB] = handler
	} else {
		n.handlers[mSTUB] = nil
	}
	if method&mALL == mALL {
		n.handlers[mALL] = handler
		for _, m := range methodMap {
			n.handlers[m] = handler
		}
	} else {
		n.handlers[method] = handler
	}
}

func (n *node) isEmpty() bool {
	for _, nds := range n.children {
		if len(nds) > 0 {
			return false
		}
	}
	return true
}

func (n *node) routes() []Route {
	rts := []Route{}

	n.walkRoutes(n.prefix, n, func(pattern string, handlers methodHandlers, subroutes Routes) bool {
		if handlers[mSTUB] != nil && subroutes == nil {
			return false
		}

		if subroutes != nil && len(pattern) > 2 {
			pattern = pattern[:len(pattern)-2]
		}

		var hs = make(map[string]http.Handler, 0)
		if handlers[mALL] != nil {
			hs["*"] = handlers[mALL]
		}
		for mt, h := range handlers {
			if h == nil {
				continue
			}
			m := methodTypString(mt)
			if m == "" {
				continue
			}
			hs[m] = h
		}

		rt := Route{pattern, hs, subroutes}
		rts = append(rts, rt)
		return false
	})

	return rts
}

func (n *node) walkRoutes(pattern string, nd *node, fn walkFn) bool {
	pattern = nd.pattern

	// Visit the leaf values if any
	if (nd.handlers != nil || nd.subroutes != nil) && fn(pattern, nd.handlers, nd.subroutes) {
		return true
	}

	// Recurse on the children
	for _, nds := range nd.children {
		for _, nd := range nds {
			if n.walkRoutes(pattern, nd, fn) {
				return true
			}
		}
	}
	return false
}

func methodTypString(method methodTyp) string {
	for s, t := range methodMap {
		if method == t {
			return s
		}
	}
	return ""
}

type walkFn func(pattern string, handlers methodHandlers, subroutes Routes) bool

// methodHandlers is a mapping of http method constants to handlers
// for a given route.
type methodHandlers map[methodTyp]http.Handler

type nodes []*node

// Sort the list of nodes by label
func (ns nodes) Len() int           { return len(ns) }
func (ns nodes) Less(i, j int) bool { return ns[i].label < ns[j].label }
func (ns nodes) Swap(i, j int)      { ns[i], ns[j] = ns[j], ns[i] }
func (ns nodes) Sort()              { sort.Sort(ns) }

type Route struct {
	Pattern   string
	Handlers  map[string]http.Handler
	SubRoutes Routes
}
<commit_msg>Improve tree findPattern()<commit_after>package chi

// Radix tree implementation below is a based on the original work by
// Armon Dadgar in https://github.com/armon/go-radix/blob/master/radix.go
// (MIT licensed). It's been heavily modified for use as a HTTP routing tree.

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

type methodTyp int

const (
	mCONNECT methodTyp = 1 << iota
	mDELETE
	mGET
	mHEAD
	mOPTIONS
	mPATCH
	mPOST
	mPUT
	mTRACE
	mSTUB

	mALL methodTyp = mCONNECT | mDELETE | mGET | mHEAD | mOPTIONS |
		mPATCH | mPOST | mPUT | mTRACE
)

var methodMap = map[string]methodTyp{
	"CONNECT": mCONNECT,
	"DELETE":  mDELETE,
	"GET":     mGET,
	"HEAD":    mHEAD,
	"OPTIONS": mOPTIONS,
	"PATCH":   mPATCH,
	"POST":    mPOST,
	"PUT":     mPUT,
	"TRACE":   mTRACE,
}

type nodeTyp uint8

const (
	ntStatic   nodeTyp = iota // /home
	ntRegexp                  // /:id([0-9]+) or #id^[0-9]+$
	ntParam                   // /:user
	ntCatchAll                // /api/v1/*
)

type node struct {
	// node type
	typ nodeTyp

	// first byte of the prefix
	label byte

	// prefix is the common prefix we ignore
	prefix string

	// pattern is the computed path of prefixes
	pattern string

	// HTTP handler on the leaf node
	handlers methodHandlers

	// chi subroutes on the leaf node
	subroutes Routes

	// Child nodes should be stored in-order for iteration,
	// in groups of the node type.
	children [ntCatchAll + 1]nodes
}

func (n *node) FindRoute(rctx *Context, path string) methodHandlers {
	// Reset the context routing pattern
	rctx.RoutePattern = ""

	// Find the routing handlers for the path
	rn := n.findRoute(rctx, path)
	if rn == nil {
		return nil
	}

	// Record the routing pattern in the request lifecycle
	if rn.pattern != "" {
		rctx.RoutePattern = rn.pattern
		rctx.RoutePatterns = append(rctx.RoutePatterns, rctx.RoutePattern)
	}

	return rn.handlers
}

func (n *node) InsertRoute(method methodTyp, pattern string, handler http.Handler) *node {
	var parent *node
	search := pattern

	for {
		// Handle key exhaustion
		if len(search) == 0 {
			// Insert or update the node's leaf handler
			n.setHandler(method, handler)
			n.pattern = pattern
			return n
		}

		// Look for the edge
		parent = n
		n = n.getEdge(search[0])

		// No edge, create one
		if n == nil {
			cn := &node{label: search[0], prefix: search, pattern: pattern}
			cn.setHandler(method, handler)
			parent.addChild(pattern, cn)
			return cn
		}

		if n.typ > ntStatic {
			// We found a wildcard node, meaning search path starts with
			// a wild prefix. Trim off the wildcard search path and continue.
			p := strings.Index(search, "/")
			if p < 0 {
				p = len(search)
			}
			search = search[p:]
			continue
		}

		// Static nodes fall below here.
		// Determine longest prefix of the search key on match.
		commonPrefix := n.longestPrefix(search, n.prefix)
		if commonPrefix == len(n.prefix) {
			// the common prefix is as long as the current node's prefix we're attempting to insert.
			// keep the search going.
			search = search[commonPrefix:]
			continue
		}

		// Split the node
		child := &node{
			typ:    ntStatic,
			prefix: search[:commonPrefix],
		}
		parent.replaceChild(search[0], child)

		// Restore the existing node
		n.label = n.prefix[commonPrefix]
		n.prefix = n.prefix[commonPrefix:]
		child.addChild(pattern, n)

		// If the new key is a subset, add to to this node
		search = search[commonPrefix:]
		if len(search) == 0 {
			child.setHandler(method, handler)
			child.pattern = pattern
			return child
		}

		// Create a new edge for the node
		subchild := &node{
			typ:     ntStatic,
			label:   search[0],
			prefix:  search,
			pattern: pattern,
		}
		subchild.setHandler(method, handler)
		child.addChild(pattern, subchild)
		return subchild
	}
}

func (n *node) findPattern(pattern string) *node {
	nn := n
	for _, nds := range nn.children {
		if len(nds) == 0 {
			continue
		}

		n = nn.getEdge(pattern[0])
		if n == nil {
			continue
		}

		idx := n.longestPrefix(pattern, n.prefix)
		xpattern := pattern[idx:]

		if len(xpattern) == 0 {
			return n
		} else if xpattern[0] == '/' && idx < len(n.prefix) {
			continue
		}

		return n.findPattern(xpattern)
	}
	return nil
}

func (n *node) isLeaf() bool {
	return n.handlers != nil
}

func (n *node) addChild(pattern string, child *node) {
	search := child.prefix

	// Find any wildcard segments
	p := strings.IndexAny(search, ":*")

	// Determine new node type
	ntyp := child.typ
	if p >= 0 {
		switch search[p] {
		case ':':
			ntyp = ntParam
		case '*':
			ntyp = ntCatchAll
		}
	}

	if p == 0 {
		// Path starts with a wildcard

		handlers := child.handlers
		child.typ = ntyp

		if ntyp == ntCatchAll {
			p = -1
		} else {
			p = strings.IndexByte(search, '/')
		}
		if p < 0 {
			p = len(search)
		}
		child.prefix = search[:p]

		if p != len(search) {
			// add edge for the remaining part, split the end.
			child.handlers = nil

			search = search[p:]

			child.addChild(pattern, &node{
				typ:      ntStatic,
				label:    search[0], // this will always start with /
				prefix:   search,
				pattern:  pattern,
				handlers: handlers,
			})
		}

	} else if p > 0 {
		// Path has some wildcard

		// starts with a static segment
		handlers := child.handlers
		child.typ = ntStatic
		child.prefix = search[:p]
		child.handlers = nil

		// add the wild edge node
		search = search[p:]

		child.addChild(pattern, &node{
			typ:      ntyp,
			label:    search[0],
			prefix:   search,
			pattern:  pattern,
			handlers: handlers,
		})

	} else {
		// Path is all static
		child.typ = ntyp

	}

	n.children[child.typ] = append(n.children[child.typ], child)
	n.children[child.typ].Sort()
}

func (n *node) replaceChild(label byte, child *node) {
	for i := 0; i < len(n.children[child.typ]); i++ {
		if n.children[child.typ][i].label == label {
			n.children[child.typ][i] = child
			n.children[child.typ][i].label = label
			return
		}
	}

	panic("chi: replacing missing child")
}

func (n *node) getEdge(label byte) *node {
	for _, nds := range n.children {
		num := len(nds)
		for i := 0; i < num; i++ {
			if nds[i].label == label {
				return nds[i]
			}
		}
	}
	return nil
}

func (n *node) findEdge(ntyp nodeTyp, label byte) *node {
	nds := n.children[ntyp]
	num := len(nds)
	idx := 0

	switch ntyp {
	case ntStatic:
		i, j := 0, num-1
		for i <= j {
			idx = i + (j-i)/2
			if label > nds[idx].label {
				i = idx + 1
			} else if label < nds[idx].label {
				j = idx - 1
			} else {
				i = num // breaks cond
			}
		}
		if nds[idx].label != label {
			return nil
		}
		return nds[idx]

	default: // wild nodes
		// TODO: right now we match them all.. but regexp should
		// run through regexp matcher
		return nds[idx]
	}
}

// Recursive edge traversal by checking all nodeTyp groups along the way.
// It's like searching through a multi-dimensional radix trie.
func (n *node) findRoute(rctx *Context, path string) *node {
	nn := n
	search := path

	for t, nds := range nn.children {
		ntyp := nodeTyp(t)
		if len(nds) == 0 {
			continue
		}

		// search subset of edges of the index for a matching node
		var label byte
		if search != "" {
			label = search[0]
		}

		xn := nn.findEdge(ntyp, label) // next node
		if xn == nil {
			continue
		}

		// Prepare next search path by trimming prefix from requested path
		xsearch := search
		if xn.typ > ntStatic {
			p := -1
			if xn.typ < ntCatchAll {
				p = strings.IndexByte(xsearch, '/')
			}
			if p < 0 {
				p = len(xsearch)
			}

			if xn.typ == ntCatchAll {
				rctx.URLParams.Add("*", xsearch)
			} else {
				rctx.URLParams.Add(xn.prefix[1:], xsearch[:p])
			}

			xsearch = xsearch[p:]
		} else if strings.HasPrefix(xsearch, xn.prefix) {
			xsearch = xsearch[len(xn.prefix):]
		} else {
			continue // no match
		}

		// did we find it yet?
		if len(xsearch) == 0 {
			if xn.isLeaf() {
				return xn
			}
		}

		// recursively find the next node..
		fin := xn.findRoute(rctx, xsearch)
		if fin != nil {
			// found a node, return it
			return fin
		}

		// Did not found final handler, let's remove the param here if it was set
		if xn.typ > ntStatic {
			if xn.typ == ntCatchAll {
				rctx.URLParams.Del("*")
			} else {
				rctx.URLParams.Del(xn.prefix[1:])
			}
		}
	}

	return nil
}

// longestPrefix finds the length of the shared prefix
// of two strings
func (n *node) longestPrefix(k1, k2 string) int {
	max := len(k1)
	if l := len(k2); l < max {
		max = l
	}
	var i int
	for i = 0; i < max; i++ {
		if k1[i] != k2[i] {
			break
		}
	}
	return i
}

func (n *node) setHandler(method methodTyp, handler http.Handler) {
	if n.handlers == nil {
		n.handlers = make(methodHandlers, 0)
	}
	if method&mSTUB == mSTUB {
		n.handlers[mSTUB] = handler
	} else {
		n.handlers[mSTUB] = nil
	}
	if method&mALL == mALL {
		n.handlers[mALL] = handler
		for _, m := range methodMap {
			n.handlers[m] = handler
		}
	} else {
		n.handlers[method] = handler
	}
}

func (n *node) isEmpty() bool {
	for _, nds := range n.children {
		if len(nds) > 0 {
			return false
		}
	}
	return true
}

func (n *node) routes() []Route {
	rts := []Route{}

	n.walkRoutes(n.prefix, n, func(pattern string, handlers methodHandlers, subroutes Routes) bool {
		if handlers[mSTUB] != nil && subroutes == nil {
			return false
		}

		if subroutes != nil && len(pattern) > 2 {
			pattern = pattern[:len(pattern)-2]
		}

		var hs = make(map[string]http.Handler, 0)
		if handlers[mALL] != nil {
			hs["*"] = handlers[mALL]
		}
		for mt, h := range handlers {
			if h == nil {
				continue
			}
			m := methodTypString(mt)
			if m == "" {
				continue
			}
			hs[m] = h
		}

		rt := Route{pattern, hs, subroutes}
		rts = append(rts, rt)
		return false
	})

	return rts
}

func (n *node) walkRoutes(pattern string, nd *node, fn walkFn) bool {
	pattern = nd.pattern

	// Visit the leaf values if any
	if (nd.handlers != nil || nd.subroutes != nil) && fn(pattern, nd.handlers, nd.subroutes) {
		return true
	}

	// Recurse on the children
	for _, nds := range nd.children {
		for _, nd := range nds {
			if n.walkRoutes(pattern, nd, fn) {
				return true
			}
		}
	}
	return false
}

func methodTypString(method methodTyp) string {
	for s, t := range methodMap {
		if method == t {
			return s
		}
	}
	return ""
}

type walkFn func(pattern string, handlers methodHandlers, subroutes Routes) bool

// methodHandlers is a mapping of http method constants to handlers
// for a given route.
type methodHandlers map[methodTyp]http.Handler

type nodes []*node

// Sort the list of nodes by label
func (ns nodes) Len() int           { return len(ns) }
func (ns nodes) Less(i, j int) bool { return ns[i].label < ns[j].label }
func (ns nodes) Swap(i, j int)      { ns[i], ns[j] = ns[j], ns[i] }
func (ns nodes) Sort()              { sort.Sort(ns) }

type Route struct {
	Pattern   string
	Handlers  map[string]http.Handler
	SubRoutes Routes
}
<|endoftext|>