<commit_before>// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.

package gopacket

import (
	"fmt"
)

// Layer represents a single decoded packet layer (using either the
// OSI or TCP/IP definition of a layer).  When decoding, a packet's data is
// broken up into a number of layers.  The caller may call LayerType() to
// figure out which type of layer they've received from the packet.  Optionally,
// they may then use a type assertion to get the actual layer type for deep
// inspection of the data.
type Layer interface {
	// LayerType is the gopacket type for this layer.
	LayerType() LayerType
	// LayerContents returns the set of bytes that make up this layer.
	LayerContents() []byte
	// LayerPayload returns the set of bytes contained within this layer, not
	// including the layer itself.
	LayerPayload() []byte
}

// Payload is a Layer containing the payload of a packet.  The definition of
// what constitutes the payload of a packet depends on previous layers; for
// TCP and UDP, we stop decoding above layer 4 and return the remaining
// bytes as a Payload.  Payload is an ApplicationLayer.
type Payload []byte

// LayerType returns LayerTypePayload
func (p *Payload) LayerType() LayerType     { return LayerTypePayload }
func (p *Payload) LayerContents() []byte    { return []byte(*p) }
func (p *Payload) LayerPayload() []byte     { return nil }
func (p *Payload) Payload() []byte          { return []byte(*p) }
func (p *Payload) String() string           { return fmt.Sprintf("%d byte(s)", len(*p)) }
func (p *Payload) CanDecode() LayerClass    { return LayerTypePayload }
func (p *Payload) NextLayerType() LayerType { return LayerTypeZero }
func (p *Payload) DecodeFromBytes(data []byte, df DecodeFeedback) error {
	*p = Payload(data)
	return nil
}

// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (p *Payload) SerializeTo(b SerializeBuffer, opts SerializeOptions) error {
	bytes, err := b.PrependBytes(len(*p))
	if err != nil {
		return err
	}
	copy(bytes, *p)
	return nil
}

// decodePayload decodes data by returning it all in a Payload layer.
func decodePayload(data []byte, p PacketBuilder) error {
	payload := &Payload{}
	if err := payload.DecodeFromBytes(data, p); err != nil {
		return nil
	}
	p.AddLayer(payload)
	p.SetApplicationLayer(payload)
	return nil
}

// These layers correspond to Internet Protocol Suite (TCP/IP) layers, and their
// corresponding OSI layers, as best as possible.

// LinkLayer is the packet layer corresponding to TCP/IP layer 1 (OSI layer 2)
type LinkLayer interface {
	Layer
	LinkFlow() Flow
}

// NetworkLayer is the packet layer corresponding to TCP/IP layer 2 (OSI
// layer 3)
type NetworkLayer interface {
	Layer
	NetworkFlow() Flow
}

// TransportLayer is the packet layer corresponding to the TCP/IP layer 3 (OSI
// layer 4)
type TransportLayer interface {
	Layer
	TransportFlow() Flow
}

// ApplicationLayer is the packet layer corresponding to the TCP/IP layer 4 (OSI
// layer 7), also known as the packet payload.
type ApplicationLayer interface {
	Layer
	Payload() []byte
}

// ErrorLayer is a packet layer created when decoding of the packet has failed.
// Its payload is all the bytes that we were unable to decode, and the returned
// error details why the decoding failed.
type ErrorLayer interface {
	Layer
	Error() error
}
<commit_msg>Switch methods for Payload to not take pointer receivers unless necessary.<commit_after>// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.

package gopacket

import (
	"fmt"
)

// Layer represents a single decoded packet layer (using either the
// OSI or TCP/IP definition of a layer).  When decoding, a packet's data is
// broken up into a number of layers.  The caller may call LayerType() to
// figure out which type of layer they've received from the packet.  Optionally,
// they may then use a type assertion to get the actual layer type for deep
// inspection of the data.
type Layer interface {
	// LayerType is the gopacket type for this layer.
	LayerType() LayerType
	// LayerContents returns the set of bytes that make up this layer.
	LayerContents() []byte
	// LayerPayload returns the set of bytes contained within this layer, not
	// including the layer itself.
	LayerPayload() []byte
}

// Payload is a Layer containing the payload of a packet.  The definition of
// what constitutes the payload of a packet depends on previous layers; for
// TCP and UDP, we stop decoding above layer 4 and return the remaining
// bytes as a Payload.  Payload is an ApplicationLayer.
type Payload []byte

// LayerType returns LayerTypePayload
func (p Payload) LayerType() LayerType     { return LayerTypePayload }
func (p Payload) LayerContents() []byte    { return []byte(p) }
func (p Payload) LayerPayload() []byte     { return nil }
func (p Payload) Payload() []byte          { return []byte(p) }
func (p Payload) String() string           { return fmt.Sprintf("%d byte(s)", len(p)) }
func (p Payload) CanDecode() LayerClass    { return LayerTypePayload }
func (p Payload) NextLayerType() LayerType { return LayerTypeZero }
func (p *Payload) DecodeFromBytes(data []byte, df DecodeFeedback) error {
	*p = Payload(data)
	return nil
}

// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (p Payload) SerializeTo(b SerializeBuffer, opts SerializeOptions) error {
	bytes, err := b.PrependBytes(len(p))
	if err != nil {
		return err
	}
	copy(bytes, p)
	return nil
}

// decodePayload decodes data by returning it all in a Payload layer.
func decodePayload(data []byte, p PacketBuilder) error {
	payload := &Payload{}
	if err := payload.DecodeFromBytes(data, p); err != nil {
		return nil
	}
	p.AddLayer(payload)
	p.SetApplicationLayer(payload)
	return nil
}

// These layers correspond to Internet Protocol Suite (TCP/IP) layers, and their
// corresponding OSI layers, as best as possible.

// LinkLayer is the packet layer corresponding to TCP/IP layer 1 (OSI layer 2)
type LinkLayer interface {
	Layer
	LinkFlow() Flow
}

// NetworkLayer is the packet layer corresponding to TCP/IP layer 2 (OSI
// layer 3)
type NetworkLayer interface {
	Layer
	NetworkFlow() Flow
}

// TransportLayer is the packet layer corresponding to the TCP/IP layer 3 (OSI
// layer 4)
type TransportLayer interface {
	Layer
	TransportFlow() Flow
}

// ApplicationLayer is the packet layer corresponding to the TCP/IP layer 4 (OSI
// layer 7), also known as the packet payload.
type ApplicationLayer interface {
	Layer
	Payload() []byte
}

// ErrorLayer is a packet layer created when decoding of the packet has failed.
// Its payload is all the bytes that we were unable to decode, and the returned
// error details why the decoding failed.
type ErrorLayer interface {
	Layer
	Error() error
}
<|endoftext|>


<commit_before>package golbStore

import (
	"net/http"
	"time"

	"github.com/martini-contrib/binding"
	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
)

// struct for dbquery results
// mgo requires the string literal tags
type Entry struct {
	ObjId   bson.ObjectId `bson:"_id,omitempty" form:"-"`
	Id      string        `bson:"-" form:"id"`
	Author  string        `bson:"author,omitempty" form:"-"`
	Written time.Time     `bson:"written,omitempty" form:"-"`
	Title   string        `bson:"title" form:"title" binding:"required"`
	Text    string        `bson:"text" form:"text" binding:"required"`
}

func (e Entry) Validate(errors binding.Errors, req *http.Request) binding.Errors {

	if len(e.Title) == 0 {
		errors = append(errors, binding.Error{
			FieldNames:     []string{"title"},
			Classification: "IncompleteError",
			Message:        "Title can't be empty",
		})
	}

	if len(e.Text) == 0 {
		errors = append(errors, binding.Error{
			FieldNames:     []string{"text"},
			Classification: "IncompleteError",
			Message:        "Text can't be empty",
		})
	}

	return errors
}

// defaults
const (
	blogCollName = "blogEntries"
	blogDbName   = "blog"
)

type MgoBlog struct {
	dbName, collName string
	s                *mgo.Session
}

func (m MgoBlog) getCollection() (*mgo.Collection, *mgo.Session) {
	s := m.s.Clone()

	return s.DB(m.dbName).C(m.collName), s
}

type Options struct {
	DbName         string
	CollectionName string
}

func NewMgoBlog(session *mgo.Session, o *Options) *MgoBlog {
	b := MgoBlog{s: session}

	if o == nil {
		b.collName = blogCollName
		b.dbName = blogDbName
	} else {
		if o.CollectionName != "" {
			b.collName = o.CollectionName
		} else {
			b.collName = blogCollName
		}

		if o.DbName != "" {
			b.dbName = o.DbName
		} else {
			b.dbName = blogDbName
		}
	}

	return &b
}
<commit_msg>removed martini/binding and Validate()<commit_after>package golbStore

import (
	"time"

	"labix.org/v2/mgo"
	"labix.org/v2/mgo/bson"
)

// struct for dbquery results
// mgo requires the string literal tags
type Entry struct {
	ObjId   bson.ObjectId `bson:"_id,omitempty"`
	Author  string        `bson:"author,omitempty"`
	Written time.Time     `bson:"written,omitempty"`
	Title   string        `bson:"title"`
	Text    string        `bson:"text"`
	Teaser  string        `bson:"teaser"`
}

// defaults
const (
	blogCollName = "blogEntries"
	blogDbName   = "blog"
)

type MgoBlog struct {
	dbName, collName string
	s                *mgo.Session
}

func (m MgoBlog) getCollection() (*mgo.Collection, *mgo.Session) {
	s := m.s.Clone()

	return s.DB(m.dbName).C(m.collName), s
}

type Options struct {
	DbName         string
	CollectionName string
}

func NewMgoBlog(session *mgo.Session, o *Options) *MgoBlog {
	b := MgoBlog{s: session}

	if o == nil {
		b.collName = blogCollName
		b.dbName = blogDbName
	} else {
		if o.CollectionName != "" {
			b.collName = o.CollectionName
		} else {
			b.collName = blogCollName
		}

		if o.DbName != "" {
			b.dbName = o.DbName
		} else {
			b.dbName = blogDbName
		}
	}

	return &b
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
)

const (
	buildListURL = "https://circleci.com/api/v1/project/%s/tree/%s?limit=1&filter=successful&circle-token=%s"
	artifactsURL = "https://circleci.com/api/v1/project/%s/%d/artifacts?circle-token=%s"
)

type build struct {
	BuildNum int `json:"build_num"`
}

type artifact struct {
	URL string `json:"url"`
}

var (
	project         string
	branch          string
	buildNum        int
	circleToken     string
	outputPath      string
	verbose, dryRun bool
)

func main() {
	if err := Main(); err != nil {
		log.Fatalf("Error: %s", err)
	}
}

// Main allows deferred functions to fire before exiting.
func Main() error {
	log.SetFlags(0)
	log.SetOutput(os.Stderr)

	flag.StringVar(&project, "repo", "", "github <username>/<repo>")
	flag.StringVar(&branch, "branch", "master", "search builds for branch")
	flag.IntVar(&buildNum, "build", 0, "get artifact for build #<n>, ignoring branch")
	flag.StringVar(&circleToken, "token", "", "CircleCI auth token")
	flag.StringVar(&outputPath, "o", "", "(optional) output file path")
	flag.BoolVar(&verbose, "v", false, "verbose output")
	flag.BoolVar(&dryRun, "n", false, "skip artifact download")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [flags] <artifact>\n\n", filepath.Base(os.Args[0]))
		flag.PrintDefaults()
	}

	flag.Parse()

	if project == "" {
		project = gitProject()
	}

	artifactName := flag.Arg(0)
	if circleToken == "" {
		circleToken = os.Getenv("CIRCLE_TOKEN")
	}

	switch {
	case project == "":
		flag.Usage()
		return fmt.Errorf("no <username>/<project> provided")
	case artifactName == "":
		flag.Usage()
		return fmt.Errorf("no <artifact> provided")
	case circleToken == "":
		flag.Usage()
		return fmt.Errorf("no auth token set: use $CIRCLE_TOKEN or flag -token")
	case buildNum > 0:
		// Don't look for a green build.
		log.Printf("Build: %d", buildNum)
	default:
		u := fmt.Sprintf(buildListURL, project, branch, circleToken)
		if verbose {
			log.Println("Build list:", u)
		}
		req, err := http.NewRequest("GET", u, nil)
		if err != nil {
			return err
		}
		req.Header.Set("Accept", "application/json")
		res, err := http.DefaultClient.Do(req)
		if err != nil {
			return err
		}
		defer res.Body.Close()
		var builds []build
		if err := json.NewDecoder(res.Body).Decode(&builds); err != nil {
			return err
		}
		if len(builds) == 0 {
			return fmt.Errorf("no builds found for branch: %s", branch)
		}
		buildNum = builds[0].BuildNum
		log.Printf("Build: %d branch: %s", buildNum, branch)
	}

	// Get artifact from buildNum
	u := fmt.Sprintf(artifactsURL, project, buildNum, circleToken)
	if verbose {
		log.Println("Artifact list:", u)
	}
	req, err := http.NewRequest("GET", u, nil)
	if err != nil {
		return err
	}
	req.Header.Set("Accept", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	var artifacts []artifact
	if err := json.NewDecoder(res.Body).Decode(&artifacts); err != nil {
		return err
	}
	if outputPath == "" {
		outputPath = filepath.Base(artifactName)
	}
	n, err := downloadArtifact(artifacts, artifactName, outputPath)
	if err != nil {
		return err
	}
	log.Printf("Wrote %s (%d bytes) to %s", artifactName, n, outputPath)
	return nil
}

func downloadArtifact(artifacts []artifact, name, outputPath string) (int64, error) {
	for _, a := range artifacts {
		if verbose {
			log.Println("Artifact URL:", a.URL)
		}
		if !strings.HasSuffix(a.URL, name) {
			continue
		}
		u, err := url.Parse(a.URL)
		if err != nil {
			return 0, err
		}
		q := u.Query()
		q.Add("circle-token", circleToken)
		u.RawQuery = q.Encode()
		if verbose {
			log.Println("Artifact found:", name)
		}
		log.Printf("Downloading %s...", name)
		if dryRun {
			log.Println("Dry run: skipped download")
			os.Exit(0)
		}
		res, err := http.Get(u.String())
		if err != nil {
			return 0, err
		}
		defer res.Body.Close()
		if res.StatusCode != 200 {
			return 0, fmt.Errorf("http: remote server responded %s (check http://status.circleci.com)", res.Status)
		}
		f, err := os.Create(outputPath)
		if err != nil {
			return 0, err
		}
		return io.Copy(f, res.Body)
	}
	return 0, fmt.Errorf("unable to find artifact: %s", name)
}

var ghURL = regexp.MustCompile(`github\.com/([^\s]+)`)

func gitProject() string {
	out, err := exec.Command("git", "remote", "-v").Output()
	if err != nil {
		return ""
	}
	s := bufio.NewScanner(bytes.NewBuffer(out))
	for s.Scan() {
		if bytes.Contains(s.Bytes(), []byte("origin")) {
			remote := ghURL.FindStringSubmatch(s.Text())
			if len(remote) > 1 {
				return strings.Replace(remote[1], ".git", "", 1)
			}
		}
	}
	return ""
}
<commit_msg>Include rev in output, update flag usage for Go 1.5<commit_after>package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
)

const (
	buildListURL = "https://circleci.com/api/v1/project/%s/tree/%s?limit=1&filter=successful&circle-token=%s"
	artifactsURL = "https://circleci.com/api/v1/project/%s/%d/artifacts?circle-token=%s"
)

type build struct {
	BuildNum int    `json:"build_num"`
	Revision string `json:"vcs_revision"`
}

type artifact struct {
	URL string `json:"url"`
}

var (
	project         string
	branch          string
	buildNum        int
	circleToken     string
	outputPath      string
	verbose, dryRun bool
)

func main() {
	if err := Main(); err != nil {
		log.Fatalf("Error: %s", err)
	}
}

// Main allows deferred functions to fire before exiting.
func Main() error {
	log.SetFlags(0)
	log.SetOutput(os.Stderr)

	flag.StringVar(&project, "repo", "", "github `username/repo`")
	flag.StringVar(&branch, "branch", "master", "search builds for branch `name`")
	flag.IntVar(&buildNum, "build", 0, "get artifact for build number, ignoring branch")
	flag.StringVar(&circleToken, "token", "", "CircleCI auth token")
	flag.StringVar(&outputPath, "o", "", "output file `path`")
	flag.BoolVar(&verbose, "v", false, "verbose output")
	flag.BoolVar(&dryRun, "n", false, "skip artifact download")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [flags] <artifact>\n\n", filepath.Base(os.Args[0]))
		flag.PrintDefaults()
	}

	flag.Parse()

	if project == "" {
		project = gitProject()
	}

	artifactName := flag.Arg(0)
	if circleToken == "" {
		circleToken = os.Getenv("CIRCLE_TOKEN")
	}

	switch {
	case project == "":
		flag.Usage()
		return fmt.Errorf("no <username>/<project> provided")
	case artifactName == "":
		flag.Usage()
		return fmt.Errorf("no <artifact> provided")
	case circleToken == "":
		flag.Usage()
		return fmt.Errorf("no auth token set: use $CIRCLE_TOKEN or flag -token")
	case buildNum > 0:
		// Don't look for a green build.
		log.Printf("Build: %d", buildNum)
	default:
		u := fmt.Sprintf(buildListURL, project, branch, circleToken)
		if verbose {
			log.Println("Build list:", u)
		}
		req, err := http.NewRequest("GET", u, nil)
		if err != nil {
			return err
		}
		req.Header.Set("Accept", "application/json")
		res, err := http.DefaultClient.Do(req)
		if err != nil {
			return err
		}
		defer res.Body.Close()
		var builds []build
		if err := json.NewDecoder(res.Body).Decode(&builds); err != nil {
			return err
		}
		if len(builds) == 0 {
			return fmt.Errorf("no builds found for branch: %s", branch)
		}
		build := builds[0]
		buildNum = build.BuildNum
		log.Printf("build: %d branch: %s rev: %s", buildNum, branch, build.Revision[:8])
	}

	// Get artifact from buildNum
	u := fmt.Sprintf(artifactsURL, project, buildNum, circleToken)
	if verbose {
		log.Println("Artifact list:", u)
	}
	req, err := http.NewRequest("GET", u, nil)
	if err != nil {
		return err
	}
	req.Header.Set("Accept", "application/json")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	var artifacts []artifact
	if err := json.NewDecoder(res.Body).Decode(&artifacts); err != nil {
		return err
	}
	if outputPath == "" {
		outputPath = filepath.Base(artifactName)
	}
	n, err := downloadArtifact(artifacts, artifactName, outputPath)
	if err != nil {
		return err
	}
	log.Printf("Wrote %s (%d bytes) to %s", artifactName, n, outputPath)
	return nil
}

func downloadArtifact(artifacts []artifact, name, outputPath string) (int64, error) {
	for _, a := range artifacts {
		if verbose {
			log.Println("Artifact URL:", a.URL)
		}
		if !strings.HasSuffix(a.URL, name) {
			continue
		}
		u, err := url.Parse(a.URL)
		if err != nil {
			return 0, err
		}
		q := u.Query()
		q.Add("circle-token", circleToken)
		u.RawQuery = q.Encode()
		if verbose {
			log.Println("Artifact found:", name)
		}
		if dryRun {
			log.Println("Dry run: skipped download")
			os.Exit(0)
		}
		log.Printf("Downloading %s...", name)
		res, err := http.Get(u.String())
		if err != nil {
			return 0, err
		}
		defer res.Body.Close()
		if res.StatusCode != 200 {
			return 0, fmt.Errorf("http: remote server responded %s (check http://status.circleci.com)", res.Status)
		}
		f, err := os.Create(outputPath)
		if err != nil {
			return 0, err
		}
		return io.Copy(f, res.Body)
	}
	return 0, fmt.Errorf("unable to find artifact: %s", name)
}

var ghURL = regexp.MustCompile(`github\.com/([^\s]+)`)

func gitProject() string {
	out, err := exec.Command("git", "remote", "-v").Output()
	if err != nil {
		return ""
	}
	s := bufio.NewScanner(bytes.NewBuffer(out))
	for s.Scan() {
		if bytes.Contains(s.Bytes(), []byte("origin")) {
			remote := ghURL.FindStringSubmatch(s.Text())
			if len(remote) > 1 {
				return strings.Replace(remote[1], ".git", "", 1)
			}
		}
	}
	return ""
}
<|endoftext|>


<commit_before>// Copyright 2017 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

// Clog is a channel-based logging package for Go.
package clog

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"
)

const (
	_VERSION = "0.4.0"
)

// Version returns current version of the package.
func Version() string {
	return _VERSION
}

type (
	MODE  string
	LEVEL int
)

const (
	TRACE LEVEL = iota
	INFO
	WARN
	ERROR
	FATAL
)

var formats = map[LEVEL]string{
	TRACE: "[TRACE] ",
	INFO:  "[ INFO] ",
	WARN:  "[ WARN] ",
	ERROR: "[ERROR] ",
	FATAL: "[FATAL] ",
}

// isValidLevel returns true if given level is in the valid range.
func isValidLevel(level LEVEL) bool {
	return level >= TRACE && level <= FATAL
}

// Message represents a log message to be processed.
type Message struct {
	Level LEVEL
	Body  string
}

func Write(level LEVEL, skip int, format string, v ...interface{}) {
	msg := &Message{
		Level: level,
	}

	// Only error and fatal information needs locate position for debugging.
	// But if skip is 0 means caller doesn't care so we can skip.
	if msg.Level >= ERROR && skip > 0 {
		pc, file, line, ok := runtime.Caller(skip)
		if ok {
			// Get caller function name.
			fn := runtime.FuncForPC(pc)
			var fnName string
			if fn == nil {
				fnName = "?()"
			} else {
				fnName = strings.TrimLeft(filepath.Ext(fn.Name()), ".") + "()"
			}

			if len(file) > 20 {
				file = "..." + file[len(file)-20:]
			}
			msg.Body = formats[level] + fmt.Sprintf("[%s:%d %s] ", file, line, fnName) + fmt.Sprintf(format, v...)
		}
	}
	if len(msg.Body) == 0 {
		msg.Body = formats[level] + fmt.Sprintf(format, v...)
	}

	for i := range receivers {
		if receivers[i].Level() > level {
			continue
		}

		receivers[i].msgChan <- msg
	}
}

func Trace(format string, v ...interface{}) {
	Write(TRACE, 0, format, v...)
}

func Info(format string, v ...interface{}) {
	Write(INFO, 0, format, v...)
}

func Warn(format string, v ...interface{}) {
	Write(WARN, 0, format, v...)
}

func Error(skip int, format string, v ...interface{}) {
	Write(ERROR, skip, format, v...)
}

func Fatal(skip int, format string, v ...interface{}) {
	Write(FATAL, skip, format, v...)
	Shutdown()
	os.Exit(1)
}

func Shutdown() {
	for i := range receivers {
		receivers[i].Destroy()
	}

	// Shutdown the error handling goroutine.
	quitChan <- struct{}{}
	for {
		if len(errorChan) == 0 {
			break
		}

		fmt.Printf("clog: unable to write message: %v\n", <-errorChan)
	}
}
<commit_msg>Bump version<commit_after>// Copyright 2017 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

// Clog is a channel-based logging package for Go.
package clog

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"
)

const (
	_VERSION = "1.0.0"
)

// Version returns current version of the package.
func Version() string {
	return _VERSION
}

type (
	MODE  string
	LEVEL int
)

const (
	TRACE LEVEL = iota
	INFO
	WARN
	ERROR
	FATAL
)

var formats = map[LEVEL]string{
	TRACE: "[TRACE] ",
	INFO:  "[ INFO] ",
	WARN:  "[ WARN] ",
	ERROR: "[ERROR] ",
	FATAL: "[FATAL] ",
}

// isValidLevel returns true if given level is in the valid range.
func isValidLevel(level LEVEL) bool {
	return level >= TRACE && level <= FATAL
}

// Message represents a log message to be processed.
type Message struct {
	Level LEVEL
	Body  string
}

func Write(level LEVEL, skip int, format string, v ...interface{}) {
	msg := &Message{
		Level: level,
	}

	// Only error and fatal information needs locate position for debugging.
	// But if skip is 0 means caller doesn't care so we can skip.
	if msg.Level >= ERROR && skip > 0 {
		pc, file, line, ok := runtime.Caller(skip)
		if ok {
			// Get caller function name.
			fn := runtime.FuncForPC(pc)
			var fnName string
			if fn == nil {
				fnName = "?()"
			} else {
				fnName = strings.TrimLeft(filepath.Ext(fn.Name()), ".") + "()"
			}

			if len(file) > 20 {
				file = "..." + file[len(file)-20:]
			}
			msg.Body = formats[level] + fmt.Sprintf("[%s:%d %s] ", file, line, fnName) + fmt.Sprintf(format, v...)
		}
	}
	if len(msg.Body) == 0 {
		msg.Body = formats[level] + fmt.Sprintf(format, v...)
	}

	for i := range receivers {
		if receivers[i].Level() > level {
			continue
		}

		receivers[i].msgChan <- msg
	}
}

func Trace(format string, v ...interface{}) {
	Write(TRACE, 0, format, v...)
}

func Info(format string, v ...interface{}) {
	Write(INFO, 0, format, v...)
}

func Warn(format string, v ...interface{}) {
	Write(WARN, 0, format, v...)
}

func Error(skip int, format string, v ...interface{}) {
	Write(ERROR, skip, format, v...)
}

func Fatal(skip int, format string, v ...interface{}) {
	Write(FATAL, skip, format, v...)
	Shutdown()
	os.Exit(1)
}

func Shutdown() {
	for i := range receivers {
		receivers[i].Destroy()
	}

	// Shutdown the error handling goroutine.
	quitChan <- struct{}{}
	for {
		if len(errorChan) == 0 {
			break
		}

		fmt.Printf("clog: unable to write message: %v\n", <-errorChan)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"crypto/tls"
	"fmt"
	"net"
	"net/textproto"
	"strings"
	"sync"
	"time"
)

type connType uint32

const (
	connWhisperConn = iota
	connReadConn
	connSendConn
	connDelete
)

type connection struct {
	sync.Mutex
	conn     net.Conn
	active   bool
	anon     bool
	joins    []string
	msgCount int
	lastUse  time.Time
	alive    bool
	conntype connType
	bot      *bot
}

func newConnection(t connType) *connection {
	return &connection{
		joins:    make([]string, 0),
		conntype: t,
		lastUse:  time.Now(),
	}
}

func (conn *connection) login(pass string, nick string) {
	conn.anon = pass == ""
	if !conn.anon {
		conn.send("PASS " + pass)
		conn.send("NICK " + nick)
		return
	}
	conn.send("NICK justinfan123")
}

func (conn *connection) close() {
	if conn.conn != nil {
		conn.conn.Close()
	}
	for _, channel := range conn.joins {
		conn.part(channel)
	}
	conn.alive = false
}

func (conn *connection) part(channel string) {
	channel = strings.ToLower(channel)
	for i, ch := range conn.joins {
		if ch == channel {
			conn.joins = append(conn.joins[:i], conn.joins[i+1:]...)
		}
	}
}

func (conn *connection) restore() {
	if conn.conntype == connReadConn {
		var i int
		var channels []string
		conn.bot.Lock()
		for index, co := range conn.bot.readconns {
			if conn == co {
				i = index
				channels = co.joins
				break
			}
		}
		Log.Error("readconn died, lost joins:", channels)
		conn.bot.readconns = append(conn.bot.readconns[:i], conn.bot.readconns[i+1:]...)
		for _, channel := range channels {
			conns := conn.bot.channels[channel]
			for i, co := range conns {
				if conn == co {
					conn.bot.channels[channel] = append(conns[:i], conns[i+1:]...)
					conn.part(channel)
				}
			}
			conn.bot.join <- channel
		}
		conn.bot.Unlock()

	} else if conn.conntype == connSendConn {
		Log.Error("sendconn died")
		var i int
		conn.bot.Lock()
		for index, co := range conn.bot.sendconns {
			if conn == co {
				i = index
				break
			}
		}
		conn.bot.sendconns = append(conn.bot.sendconns[:i], conn.bot.sendconns[i+1:]...)
		conn.bot.Unlock()
	} else if conn.conntype == connWhisperConn {
		Log.Error("whisperconn died, reconnecting")
		conn.close()
		conn.bot.whisperconn = newConnection(connWhisperConn)
	}
	conn.conntype = connDelete
}

func (conn *connection) connect(client *Client, pass string, nick string) {
	c, err := tls.Dial("tcp", *addr, nil)
	conn.conn = c
	conn.bot = client.bot
	if err != nil {
		Log.Error("unable to connect to irc server", err)
		time.Sleep(2 * time.Second)
		conn.restore()
		return
	}

	conn.login(pass, nick)
	conn.send("CAP REQ :twitch.tv/tags")
	conn.send("CAP REQ :twitch.tv/commands")

	defer func() {
		if r := recover(); r != nil {
			Log.Error(r)
		}
		conn.restore()
	}()
	reader := bufio.NewReader(conn.conn)
	tp := textproto.NewReader(reader)
	for {
		line, err := tp.ReadLine()
		if err != nil {
			Log.Error("read:", err)
			conn.restore()
			return
		}
		if conn.conntype == connDelete {
			conn.restore()
		}
		if strings.HasPrefix(line, "PING") {
			conn.send(strings.Replace(line, "PING", "PONG", 1))
		} else if strings.HasPrefix(line, "PONG") {
			Log.Debug("PONG")
		} else {
			if isWhisper(line) && conn.conntype != connWhisperConn {
				// throw away message
			} else {
				client.toClient <- line
			}
		}
		conn.active = true
		stats.totalMsgsReceived++
	}
}

func isWhisper(line string) bool {
	if !strings.Contains(line, ".tmi.twitch.tv WHISPER ") {
		return false
	}
	spl := strings.SplitN(line, " :", 3)
	if strings.Contains(spl[1], ".tmi.twitch.tv WHISPER ") {
		return true
	}
	return false
}

func (conn *connection) send(msg string) error {
	if conn.conn == nil {
		return fmt.Errorf("conn is nil")
	}
	_, err := fmt.Fprint(conn.conn, msg+"\r\n")
	if err != nil {
		Log.Error(msg)
		Log.Error(err)
		Log.Error(conn)
		return err
	}
	stats.totalMsgsSent++
	return nil
}

func (conn *connection) reduceMsgCount() {
	conn.Lock()
	conn.msgCount--
	conn.Unlock()
}

func (conn *connection) countMsg() {
	conn.Lock()
	conn.msgCount++
	conn.Unlock()
	time.AfterFunc(30*time.Second, conn.reduceMsgCount)
}
<commit_msg>lazy fix for nil pointer issue<commit_after>package main

import (
	"bufio"
	"crypto/tls"
	"fmt"
	"net"
	"net/textproto"
	"strings"
	"sync"
	"time"
)

type connType uint32

const (
	connWhisperConn = iota
	connReadConn
	connSendConn
	connDelete
)

type connection struct {
	sync.Mutex
	conn     net.Conn
	active   bool
	anon     bool
	joins    []string
	msgCount int
	lastUse  time.Time
	alive    bool
	conntype connType
	bot      *bot
}

func newConnection(t connType) *connection {
	return &connection{
		joins:    make([]string, 0),
		conntype: t,
		lastUse:  time.Now(),
	}
}

func (conn *connection) login(pass string, nick string) {
	conn.anon = pass == ""
	if !conn.anon {
		conn.send("PASS " + pass)
		conn.send("NICK " + nick)
		return
	}
	conn.send("NICK justinfan123")
}

func (conn *connection) close() {
	if conn.conn != nil {
		conn.conn.Close()
	}
	for _, channel := range conn.joins {
		conn.part(channel)
	}
	conn.alive = false
}

func (conn *connection) part(channel string) {
	channel = strings.ToLower(channel)
	for i, ch := range conn.joins {
		if ch == channel {
			conn.joins = append(conn.joins[:i], conn.joins[i+1:]...)
		}
	}
}

func (conn *connection) restore() {
	defer func() {
		if r := recover(); r != nil {
			Log.Error(r)
		}
	}()
	if conn.conntype == connReadConn {
		var i int
		var channels []string
		conn.bot.Lock()
		for index, co := range conn.bot.readconns {
			if conn == co {
				i = index
				channels = co.joins
				break
			}
		}
		Log.Error("readconn died, lost joins:", channels)
		conn.bot.readconns = append(conn.bot.readconns[:i], conn.bot.readconns[i+1:]...)
		for _, channel := range channels {
			conns := conn.bot.channels[channel]
			for i, co := range conns {
				if conn == co {
					conn.bot.channels[channel] = append(conns[:i], conns[i+1:]...)
					conn.part(channel)
				}
			}
			conn.bot.join <- channel
		}
		conn.bot.Unlock()

	} else if conn.conntype == connSendConn {
		Log.Error("sendconn died")
		var i int
		conn.bot.Lock()
		for index, co := range conn.bot.sendconns {
			if conn == co {
				i = index
				break
			}
		}
		conn.bot.sendconns = append(conn.bot.sendconns[:i], conn.bot.sendconns[i+1:]...)
		conn.bot.Unlock()
	} else if conn.conntype == connWhisperConn {
		Log.Error("whisperconn died, reconnecting")
		conn.close()
		conn.bot.whisperconn = newConnection(connWhisperConn)
	}
	conn.conntype = connDelete
}

func (conn *connection) connect(client *Client, pass string, nick string) {
	c, err := tls.Dial("tcp", *addr, nil)
	conn.conn = c
	conn.bot = client.bot
	if err != nil {
		Log.Error("unable to connect to irc server", err)
		time.Sleep(2 * time.Second)
		conn.restore()
		return
	}

	conn.login(pass, nick)
	conn.send("CAP REQ :twitch.tv/tags")
	conn.send("CAP REQ :twitch.tv/commands")

	defer func() {
		if r := recover(); r != nil {
			Log.Error(r)
		}
		conn.restore()
	}()
	reader := bufio.NewReader(conn.conn)
	tp := textproto.NewReader(reader)
	for {
		line, err := tp.ReadLine()
		if err != nil {
			Log.Error("read:", err)
			conn.restore()
			return
		}
		if conn.conntype == connDelete {
			conn.restore()
		}
		if strings.HasPrefix(line, "PING") {
			conn.send(strings.Replace(line, "PING", "PONG", 1))
		} else if strings.HasPrefix(line, "PONG") {
			Log.Debug("PONG")
		} else {
			if isWhisper(line) && conn.conntype != connWhisperConn {
				// throw away message
			} else {
				client.toClient <- line
			}
		}
		conn.active = true
		stats.totalMsgsReceived++
	}
}

func isWhisper(line string) bool {
	if !strings.Contains(line, ".tmi.twitch.tv WHISPER ") {
		return false
	}
	spl := strings.SplitN(line, " :", 3)
	if strings.Contains(spl[1], ".tmi.twitch.tv WHISPER ") {
		return true
	}
	return false
}

func (conn *connection) send(msg string) error {
	if conn.conn == nil {
		return fmt.Errorf("conn is nil")
	}
	_, err := fmt.Fprint(conn.conn, msg+"\r\n")
	if err != nil {
		Log.Error(msg)
		Log.Error(err)
		Log.Error(conn)
		return err
	}
	stats.totalMsgsSent++
	return nil
}

func (conn *connection) reduceMsgCount() {
	conn.Lock()
	conn.msgCount--
	conn.Unlock()
}

func (conn *connection) countMsg() {
	conn.Lock()
	conn.msgCount++
	conn.Unlock()
	time.AfterFunc(30*time.Second, conn.reduceMsgCount)
}
<|endoftext|>


<commit_before>// Package udp provides a connection-oriented listener over a UDP PacketConn
package udp

import (
	"context"
	"errors"
	"io"
	"net"
	"sync"
	"sync/atomic"
	"time"

	"github.com/pion/dtls/v2/internal/net/deadline"
)

const receiveMTU = 8192

var errClosedListener = errors.New("udp: listener closed")

// Listener augments a connection-oriented Listener over a UDP PacketConn
type Listener struct {
	pConn *net.UDPConn

	accepting atomic.Value // bool
	acceptCh  chan *Conn
	doneCh    chan struct{}
	doneOnce  sync.Once

	connLock sync.Mutex
	conns    map[string]*Conn
	connWG   sync.WaitGroup

	readWG   sync.WaitGroup
	errClose atomic.Value // error
}

// Accept waits for and returns the next connection to the listener.
func (l *Listener) Accept() (*Conn, error) {
	select {
	case c := <-l.acceptCh:
		l.connWG.Add(1)
		return c, nil

	case <-l.doneCh:
		return nil, errClosedListener
	}
}

// Close closes the listener.
// Any blocked Accept operations will be unblocked and return errors.
func (l *Listener) Close() error {
	var err error
	l.doneOnce.Do(func() {
		l.connWG.Done()
		l.accepting.Store(false)
		close(l.doneCh)

		l.connLock.Lock()
		nConns := len(l.conns)
		l.connLock.Unlock()

		if nConns == 0 {
			// Wait if this is the final connection
			l.readWG.Wait()
			if errClose, ok := l.errClose.Load().(error); ok {
				err = errClose
			}
		} else {
			err = nil
		}
	})

	return err
}

// Addr returns the listener's network address.
func (l *Listener) Addr() net.Addr {
	return l.pConn.LocalAddr()
}

// Listen creates a new listener
func Listen(network string, laddr *net.UDPAddr) (*Listener, error) {
	conn, err := net.ListenUDP(network, laddr)
	if err != nil {
		return nil, err
	}

	l := &Listener{
		pConn:    conn,
		acceptCh: make(chan *Conn),
		conns:    make(map[string]*Conn),
		doneCh:   make(chan struct{}),
	}
	l.accepting.Store(true)
	l.connWG.Add(1)
	l.readWG.Add(2) // wait readLoop and Close execution routine

	go l.readLoop()
	go func() {
		l.connWG.Wait()
		if err := l.pConn.Close(); err != nil {
			l.errClose.Store(err)
		}
		l.readWG.Done()
	}()

	return l, nil
}

// readLoop has to tasks:
// 1. Dispatching incoming packets to the correct Conn.
//    It can therefore not be ended until all Conns are closed.
// 2. Creating a new Conn when receiving from a new remote.
func (l *Listener) readLoop() {
	defer l.readWG.Done()
	buf := make([]byte, receiveMTU)

	for {
		n, raddr, err := l.pConn.ReadFrom(buf)
		if err != nil {
			return
		}
		conn, err := l.getConn(raddr)
		if err != nil {
			continue
		}
		cBuf := <-conn.readCh
		n = copy(cBuf, buf[:n])
		conn.sizeCh <- n
	}
}

func (l *Listener) getConn(raddr net.Addr) (*Conn, error) {
	l.connLock.Lock()
	defer l.connLock.Unlock()
	conn, ok := l.conns[raddr.String()]
	if !ok {
		if !l.accepting.Load().(bool) {
			return nil, errClosedListener
		}
		conn = l.newConn(raddr)
		l.conns[raddr.String()] = conn
		l.acceptCh <- conn
	}
	return conn, nil
}

// Conn augments a connection-oriented connection over a UDP PacketConn
type Conn struct {
	listener *Listener

	rAddr net.Addr

	readCh chan []byte
	sizeCh chan int

	doneCh   chan struct{}
	doneOnce sync.Once

	readDeadline  *deadline.Deadline
	writeDeadline *deadline.Deadline
}

func (l *Listener) newConn(rAddr net.Addr) *Conn {
	return &Conn{
		listener:      l,
		rAddr:         rAddr,
		readCh:        make(chan []byte),
		sizeCh:        make(chan int),
		doneCh:        make(chan struct{}),
		readDeadline:  deadline.New(),
		writeDeadline: deadline.New(),
	}
}

// Read
func (c *Conn) Read(p []byte) (int, error) {
	select {
	case c.readCh <- p:
		n := <-c.sizeCh
		return n, nil
	case <-c.doneCh:
		return 0, io.EOF
	case <-c.readDeadline.Done():
		return 0, context.DeadlineExceeded
	}
}

// Write writes len(p) bytes from p to the DTLS connection
func (c *Conn) Write(p []byte) (n int, err error) {
	select {
	case <-c.writeDeadline.Done():
		return 0, context.DeadlineExceeded
	default:
	}
	return c.listener.pConn.WriteTo(p, c.rAddr)
}

// Close closes the conn and releases any Read calls
func (c *Conn) Close() error {
	var err error
	c.doneOnce.Do(func() {
		c.listener.connWG.Done()
		close(c.doneCh)
		c.listener.connLock.Lock()
		delete(c.listener.conns, c.rAddr.String())
		nConns := len(c.listener.conns)
		c.listener.connLock.Unlock()

		if nConns == 0 && !c.listener.accepting.Load().(bool) {
			// Wait if this is the final connection
			c.listener.readWG.Wait()
			if errClose, ok := c.listener.errClose.Load().(error); ok {
				err = errClose
			}
		} else {
			err = nil
		}
	})

	return err
}

// LocalAddr implements net.Conn.LocalAddr
func (c *Conn) LocalAddr() net.Addr {
	return c.listener.pConn.LocalAddr()
}

// RemoteAddr implements net.Conn.RemoteAddr
func (c *Conn) RemoteAddr() net.Addr {
	return c.rAddr
}

// SetDeadline implements net.Conn.SetDeadline
func (c *Conn) SetDeadline(t time.Time) error {
	c.readDeadline.Set(t)
	c.writeDeadline.Set(t)
	// Deadline of underlying connection should not be changed
	// since the connection can be shared.
	return nil
}

// SetReadDeadline implements net.Conn.SetDeadline
func (c *Conn) SetReadDeadline(t time.Time) error {
	c.readDeadline.Set(t)
	return nil
}

// SetWriteDeadline implements net.Conn.SetDeadline
func (c *Conn) SetWriteDeadline(t time.Time) error {
	c.writeDeadline.Set(t)
	return nil
}
<commit_msg>Fix udp.Conn deadlock on Close()<commit_after>// Package udp provides a connection-oriented listener over a UDP PacketConn
package udp

import (
	"context"
	"errors"
	"io"
	"net"
	"sync"
	"sync/atomic"
	"time"

	"github.com/pion/dtls/v2/internal/net/deadline"
)

const receiveMTU = 8192

var errClosedListener = errors.New("udp: listener closed")

// Listener augments a connection-oriented Listener over a UDP PacketConn
type Listener struct {
	pConn *net.UDPConn

	accepting atomic.Value // bool
	acceptCh  chan *Conn
	doneCh    chan struct{}
	doneOnce  sync.Once

	connLock sync.Mutex
	conns    map[string]*Conn
	connWG   sync.WaitGroup

	readWG   sync.WaitGroup
	errClose atomic.Value // error
}

// Accept waits for and returns the next connection to the listener.
func (l *Listener) Accept() (*Conn, error) {
	select {
	case c := <-l.acceptCh:
		l.connWG.Add(1)
		return c, nil

	case <-l.doneCh:
		return nil, errClosedListener
	}
}

// Close closes the listener.
// Any blocked Accept operations will be unblocked and return errors.
func (l *Listener) Close() error {
	var err error
	l.doneOnce.Do(func() {
		l.connWG.Done()
		l.accepting.Store(false)
		close(l.doneCh)

		l.connLock.Lock()
		nConns := len(l.conns)
		l.connLock.Unlock()

		if nConns == 0 {
			// Wait if this is the final connection
			l.readWG.Wait()
			if errClose, ok := l.errClose.Load().(error); ok {
				err = errClose
			}
		} else {
			err = nil
		}
	})

	return err
}

// Addr returns the listener's network address.
func (l *Listener) Addr() net.Addr {
	return l.pConn.LocalAddr()
}

// Listen creates a new listener
func Listen(network string, laddr *net.UDPAddr) (*Listener, error) {
	conn, err := net.ListenUDP(network, laddr)
	if err != nil {
		return nil, err
	}

	l := &Listener{
		pConn:    conn,
		acceptCh: make(chan *Conn),
		conns:    make(map[string]*Conn),
		doneCh:   make(chan struct{}),
	}
	l.accepting.Store(true)
	l.connWG.Add(1)
	l.readWG.Add(2) // wait readLoop and Close execution routine

	go l.readLoop()
	go func() {
		l.connWG.Wait()
		if err := l.pConn.Close(); err != nil {
			l.errClose.Store(err)
		}
		l.readWG.Done()
	}()

	return l, nil
}

// readLoop has to tasks:
// 1. Dispatching incoming packets to the correct Conn.
//    It can therefore not be ended until all Conns are closed.
// 2. Creating a new Conn when receiving from a new remote.
func (l *Listener) readLoop() {
	defer l.readWG.Done()
	buf := make([]byte, receiveMTU)

	for {
		n, raddr, err := l.pConn.ReadFrom(buf)
		if err != nil {
			return
		}
		conn, err := l.getConn(raddr)
		if err != nil {
			continue
		}
		select {
		case <-l.doneCh:
			return
		case cBuf := <-conn.readCh:
			n = copy(cBuf, buf[:n])
			conn.sizeCh <- n
		}
	}
}

func (l *Listener) getConn(raddr net.Addr) (*Conn, error) {
	l.connLock.Lock()
	defer l.connLock.Unlock()
	conn, ok := l.conns[raddr.String()]
	if !ok {
		if !l.accepting.Load().(bool) {
			return nil, errClosedListener
		}
		conn = l.newConn(raddr)
		l.conns[raddr.String()] = conn
		select {
		case <-l.doneCh:
			return nil, errClosedListener
		case l.acceptCh <- conn:
		}
	}
	return conn, nil
}

// Conn augments a connection-oriented connection over a UDP PacketConn
type Conn struct {
	listener *Listener

	rAddr net.Addr

	readCh chan []byte
	sizeCh chan int

	doneCh   chan struct{}
	doneOnce sync.Once

	readDeadline  *deadline.Deadline
	writeDeadline *deadline.Deadline
}

func (l *Listener) newConn(rAddr net.Addr) *Conn {
	return &Conn{
		listener:      l,
		rAddr:         rAddr,
		readCh:        make(chan []byte),
		sizeCh:        make(chan int),
		doneCh:        make(chan struct{}),
		readDeadline:  deadline.New(),
		writeDeadline: deadline.New(),
	}
}

// Read
func (c *Conn) Read(p []byte) (int, error) {
	select {
	case c.readCh <- p:
		n := <-c.sizeCh
		return n, nil
	case <-c.doneCh:
		return 0, io.EOF
	case <-c.readDeadline.Done():
		return 0, context.DeadlineExceeded
	}
}

// Write writes len(p) bytes from p to the DTLS connection
func (c *Conn) Write(p []byte) (n int, err error) {
	select {
	case <-c.writeDeadline.Done():
		return 0, context.DeadlineExceeded
	default:
	}
	return c.listener.pConn.WriteTo(p, c.rAddr)
}

// Close closes the conn and releases any Read calls
func (c *Conn) Close() error {
	var err error
	c.doneOnce.Do(func() {
		c.listener.connWG.Done()
		close(c.doneCh)
		c.listener.connLock.Lock()
		delete(c.listener.conns, c.rAddr.String())
		nConns := len(c.listener.conns)
		c.listener.connLock.Unlock()

		if nConns == 0 && !c.listener.accepting.Load().(bool) {
			// Wait if this is the final connection
			c.listener.readWG.Wait()
			if errClose, ok := c.listener.errClose.Load().(error); ok {
				err = errClose
			}
		} else {
			err = nil
		}
	})

	return err
}

// LocalAddr implements net.Conn.LocalAddr
func (c *Conn) LocalAddr() net.Addr {
	return c.listener.pConn.LocalAddr()
}

// RemoteAddr implements net.Conn.RemoteAddr
func (c *Conn) RemoteAddr() net.Addr {
	return c.rAddr
}

// SetDeadline implements net.Conn.SetDeadline
func (c *Conn) SetDeadline(t time.Time) error {
	c.readDeadline.Set(t)
	c.writeDeadline.Set(t)
	// Deadline of underlying connection should not be changed
	// since the connection can be shared.
	return nil
}

// SetReadDeadline implements net.Conn.SetDeadline
func (c *Conn) SetReadDeadline(t time.Time) error {
	c.readDeadline.Set(t)
	return nil
}

// SetWriteDeadline implements net.Conn.SetDeadline
func (c *Conn) SetWriteDeadline(t time.Time) error {
	c.writeDeadline.Set(t)
	return nil
}
<|endoftext|>


<commit_before>package copy

import (
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
)

const (
	// tmpPermissionForDirectory makes the destination directory writable,
	// so that stuff can be copied recursively even if any original directory is NOT writable.
	// See https://github.com/otiai10/copy/pull/9 for more information.
	tmpPermissionForDirectory = os.FileMode(0755)
)

// Copy copies src to dest, doesn't matter if src is a directory or a file.
func Copy(src, dest string, opt ...Options) error {
	info, err := os.Lstat(src)
	if err != nil {
		return err
	}
	options := assure(opt...)
	return copy(src, dest, info, false, options)
}

// copy dispatches copy-funcs according to the mode.
// Because this "copy" could be called recursively,
// "info" MUST be given here, NOT nil.
func copy(src, dest string, info os.FileInfo, skip bool, opt Options) error {

	if skip {
		return nil
	}

	if info.Mode()&os.ModeSymlink != 0 {
		return onsymlink(src, dest, info, opt)
	}

	if info.IsDir() {
		return dcopy(src, dest, info, opt)
	}
	return fcopy(src, dest, info, opt)
}

// fcopy is for just a file,
// with considering existence of parent directory
// and file permission.
func fcopy(src, dest string, info os.FileInfo, opt Options) (err error) {

	if err = os.MkdirAll(filepath.Dir(dest), os.ModePerm); err != nil {
		return
	}

	f, err := os.Create(dest)
	if err != nil {
		return
	}
	defer fclose(f, &err)

	if err = os.Chmod(f.Name(), info.Mode()|opt.AddPermission); err != nil {
		return
	}

	s, err := os.Open(src)
	if err != nil {
		return
	}
	defer fclose(s, &err)

	if _, err = io.Copy(f, s); err != nil {
		return
	}

	if opt.Sync {
		err = f.Sync()
	}

	return
}

// dcopy is for a directory,
// with scanning contents inside the directory
// and pass everything to "copy" recursively.
func dcopy(srcdir, destdir string, info os.FileInfo, opt Options) (err error) {

	originalMode := info.Mode()

	// Make dest dir with 0755 so that everything writable.
	if err = os.MkdirAll(destdir, tmpPermissionForDirectory); err != nil {
		return
	}
	// Recover dir mode with original one.
	defer chmod(destdir, originalMode|opt.AddPermission, &err)

	contents, err := ioutil.ReadDir(srcdir)
	if err != nil {
		return
	}

	var skip bool
	for _, content := range contents {
		cs, cd := filepath.Join(srcdir, content.Name()), filepath.Join(destdir, content.Name())

		skip, err = opt.Skip(cs)
		if err != nil {
			return
		}

		if err = copy(cs, cd, content, skip, opt); err != nil {
			// If any error, exit immediately
			return
		}
	}

	return
}

func onsymlink(src, dest string, info os.FileInfo, opt Options) error {

	switch opt.OnSymlink(src) {
	case Shallow:
		return lcopy(src, dest)
	case Deep:
		orig, err := os.Readlink(src)
		if err != nil {
			return err
		}
		info, err = os.Lstat(orig)
		if err != nil {
			return err
		}
		skip, err := opt.Skip(orig)
		if err != nil {
			return err
		}
		return copy(orig, dest, info, skip, opt)
	case Skip:
		fallthrough
	default:
		return nil // do nothing
	}
}

// lcopy is for a symlink,
// with just creating a new symlink by replicating src symlink.
func lcopy(src, dest string) error {
	src, err := os.Readlink(src)
	if err != nil {
		return err
	}
	return os.Symlink(src, dest)
}

// fclose ANYHOW closes file,
// with asiging error raised during Close,
// BUT respecting the error already reported.
func fclose(f *os.File, reported *error) {
	if err := f.Close(); *reported == nil {
		*reported = err
	}
}

// chmod ANYHOW changes file mode,
// with asiging error raised during Chmod,
// BUT respecting the error already reported.
func chmod(dir string, mode os.FileMode, reported *error) {
	if err := os.Chmod(dir, mode); *reported == nil {
		*reported = err
	}
}

// assure Options struct, should be called only once.
// All optional values MUST NOT BE nil/zero after assured.
func assure(opts ...Options) Options {
	if len(opts) == 0 {
		return getDefaultOptions()
	}
	defopt := getDefaultOptions()
	if opts[0].OnSymlink == nil {
		opts[0].OnSymlink = defopt.OnSymlink
	}
	if opts[0].Skip == nil {
		opts[0].Skip = defopt.Skip
	}
	return opts[0]
}
<commit_msg>Add switchboard for refactoring<commit_after>package copy

import (
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
)

const (
	// tmpPermissionForDirectory makes the destination directory writable,
	// so that stuff can be copied recursively even if any original directory is NOT writable.
	// See https://github.com/otiai10/copy/pull/9 for more information.
	tmpPermissionForDirectory = os.FileMode(0755)
)

// Copy copies src to dest, doesn't matter if src is a directory or a file.
func Copy(src, dest string, opt ...Options) error {
	info, err := os.Lstat(src)
	if err != nil {
		return err
	}
	return switchboard(src, dest, info, assure(opt...))
}

// switchboard switches proper copy functions regarding file type, etc...
// If there would be anything else here, add a case to this switchboard.
func switchboard(src, dest string, info os.FileInfo, opt Options) error {
	switch {
	case info.Mode()&os.ModeSymlink != 0:
		return onsymlink(src, dest, info, opt)
	case info.IsDir():
		return dcopy(src, dest, info, opt)
	default:
		return fcopy(src, dest, info, opt)
	}
}

// copy decide if this src should be copied or not.
// Because this "copy" could be called recursively,
// "info" MUST be given here, NOT nil.
func copy(src, dest string, info os.FileInfo, opt Options) error {
	skip, err := opt.Skip(src)
	if err != nil {
		return err
	}
	if skip {
		return nil
	}
	return switchboard(src, dest, info, opt)
}

// fcopy is for just a file,
// with considering existence of parent directory
// and file permission.
func fcopy(src, dest string, info os.FileInfo, opt Options) (err error) {

	if err = os.MkdirAll(filepath.Dir(dest), os.ModePerm); err != nil {
		return
	}

	f, err := os.Create(dest)
	if err != nil {
		return
	}
	defer fclose(f, &err)

	if err = os.Chmod(f.Name(), info.Mode()|opt.AddPermission); err != nil {
		return
	}

	s, err := os.Open(src)
	if err != nil {
		return
	}
	defer fclose(s, &err)

	if _, err = io.Copy(f, s); err != nil {
		return
	}

	if opt.Sync {
		err = f.Sync()
	}

	return
}

// dcopy is for a directory,
// with scanning contents inside the directory
// and pass everything to "copy" recursively.
func dcopy(srcdir, destdir string, info os.FileInfo, opt Options) (err error) {

	originalMode := info.Mode()

	// Make dest dir with 0755 so that everything writable.
	if err = os.MkdirAll(destdir, tmpPermissionForDirectory); err != nil {
		return
	}
	// Recover dir mode with original one.
	defer chmod(destdir, originalMode|opt.AddPermission, &err)

	contents, err := ioutil.ReadDir(srcdir)
	if err != nil {
		return
	}

	for _, content := range contents {
		cs, cd := filepath.Join(srcdir, content.Name()), filepath.Join(destdir, content.Name())

		if err = copy(cs, cd, content, opt); err != nil {
			// If any error, exit immediately
			return
		}
	}

	return
}

func onsymlink(src, dest string, info os.FileInfo, opt Options) error {

	switch opt.OnSymlink(src) {
	case Shallow:
		return lcopy(src, dest)
	case Deep:
		orig, err := os.Readlink(src)
		if err != nil {
			return err
		}
		info, err = os.Lstat(orig)
		if err != nil {
			return err
		}
		return copy(orig, dest, info, opt)
	case Skip:
		fallthrough
	default:
		return nil // do nothing
	}
}

// lcopy is for a symlink,
// with just creating a new symlink by replicating src symlink.
func lcopy(src, dest string) error {
	src, err := os.Readlink(src)
	if err != nil {
		return err
	}
	return os.Symlink(src, dest)
}

// fclose ANYHOW closes file,
// with asiging error raised during Close,
// BUT respecting the error already reported.
func fclose(f *os.File, reported *error) {
	if err := f.Close(); *reported == nil {
		*reported = err
	}
}

// chmod ANYHOW changes file mode,
// with asiging error raised during Chmod,
// BUT respecting the error already reported.
func chmod(dir string, mode os.FileMode, reported *error) {
	if err := os.Chmod(dir, mode); *reported == nil {
		*reported = err
	}
}

// assure Options struct, should be called only once.
// All optional values MUST NOT BE nil/zero after assured.
func assure(opts ...Options) Options {
	if len(opts) == 0 {
		return getDefaultOptions()
	}
	defopt := getDefaultOptions()
	if opts[0].OnSymlink == nil {
		opts[0].OnSymlink = defopt.OnSymlink
	}
	if opts[0].Skip == nil {
		opts[0].Skip = defopt.Skip
	}
	return opts[0]
}
<|endoftext|>


<commit_before>package mailjet

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"reflect"
	"runtime"
	"strconv"
	"strings"
)

// DebugLevel defines the verbosity of the debug.
var DebugLevel int

// These are the different level of debug.
const (
	LevelNone      = iota // No debug.
	LevelDebug            // Debug without body.
	LevelDebugFull        // Debug with body.
)

// User-Agent is formated as "UserAgentBase/UserAgentVersion;runtime.Version()".
const (
	UserAgentBase    = "mailjet-api-v3-go"
	UserAgentVersion = "2.0.1"
)

const (
	apiBase  = "https://api.mailjet.com/v3"
	apiPath  = "REST"
	dataPath = "DATA"
)

// createRequest is the main core function.
func createRequest(method string, url string,
	payload interface{}, onlyFields []string,
	options ...RequestOptions) (req *http.Request, err error) {

	body, err := convertPayload(payload, onlyFields)
	if err != nil {
		return req, fmt.Errorf("creating request: %s\n", err)
	}
	req, err = http.NewRequest(method, url, bytes.NewBuffer(body))
	if err != nil {
		return req, fmt.Errorf("creating request: %s\n", err)
	}
	for _, option := range options {
		option(req)
	}
	userAgent(req)
	req.Header.Add("Accept", "application/json")
	return req, err
}

// converPayload returns payload casted in []byte.
// If the payload is a structure, it's encoded to JSON.
func convertPayload(payload interface{}, onlyFields []string) (body []byte, err error) {
	if payload != nil {
		switch t := payload.(type) {
		case string:
			body = []byte(t)
		case []byte:
			body = t
		default:
			v := reflect.Indirect(reflect.ValueOf(payload))
			if v.Kind() == reflect.Ptr {
				return convertPayload(v.Interface(), onlyFields)
			} else if v.Kind() == reflect.Struct {
				body, err = json.Marshal(buildMap(v, onlyFields))
				if err != nil {
					return body, err
				}
			}
		}
		if DebugLevel == LevelDebugFull {
			log.Println("Body:", string(body))
		}
	}
	return body, err
}

// buildMap returns a map with fields specified in onlyFields (all fields if nil)
// and without the read_only fields.
func buildMap(v reflect.Value, onlyFields []string) map[string]interface{} {
	res := make(map[string]interface{})
	if onlyFields != nil {
		for _, onlyField := range onlyFields {
			fieldType, exist := v.Type().FieldByName(onlyField)
			if exist {
				addFieldToMap(true, fieldType, v.FieldByName(onlyField), res)
			}
		}
	} else {
		for i := 0; i < v.NumField(); i++ {
			addFieldToMap(false, v.Type().Field(i), v.Field(i), res)
		}
	}
	return res
}

func addFieldToMap(onlyField bool, fieldType reflect.StructField,
	fieldValue reflect.Value, res map[string]interface{}) {
	if fieldType.Tag.Get("mailjet") != "read_only" {
		name, second := parseTag(fieldType.Tag.Get("json"))
		if name == "" {
			name = fieldType.Name
		}
		if !onlyField && second == "omitempty" &&
			isEmptyValue(fieldValue) {
			return
		}
		res[name] = fieldValue.Interface()
	}
}

func parseTag(tag string) (string, string) {
	if idx := strings.Index(tag, ","); idx != -1 {
		return tag[:idx], tag[idx+1:]
	}
	return tag, ""
}

func isEmptyValue(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
		return v.Len() == 0
	case reflect.Bool:
		return !v.Bool()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return v.Int() == 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return v.Uint() == 0
	case reflect.Float32, reflect.Float64:
		return v.Float() == 0
	case reflect.Interface, reflect.Ptr:
		return v.IsNil()
	}
	return false
}

// userAgent add the User-Agent value to the request header.
func userAgent(req *http.Request) {
	ua := fmt.Sprintf("%s/%s;%s",
		UserAgentBase,
		UserAgentVersion,
		runtime.Version(),
	)
	req.Header.Add("User-Agent", ua)
}

func buildURL(info *Request) string {
	tokens := []string{apiBase, apiPath, info.Resource}
	if info.ID != 0 {
		id := strconv.FormatInt(info.ID, 10)
		tokens = append(tokens, id)
	} else if info.AltID != "" {
		tokens = append(tokens, string(info.AltID))
	}
	if info.Action != "" {
		tokens = append(tokens, info.Action)
	}
	if info.ActionID != 0 {
		actionID := strconv.FormatInt(info.ActionID, 10)
		tokens = append(tokens, actionID)
	}
	return strings.Join(tokens, "/")
}

func buildDataURL(info *DataRequest) string {
	tokens := []string{apiBase, dataPath, info.SourceType}
	if info.SourceTypeID != 0 {
		id := strconv.FormatInt(info.SourceTypeID, 10)
		tokens = append(tokens, id)
	}
	if info.DataType != "" {
		tokens = append(tokens, info.DataType)
		if info.MimeType != "" {
			tokens = append(tokens, info.MimeType)
		}
	}
	if info.DataTypeID != 0 {
		DataTypeID := strconv.FormatInt(info.DataTypeID, 10)
		tokens = append(tokens, DataTypeID)
	} else if info.LastID == true {
		tokens = append(tokens, "LAST")
	}
	return strings.Join(tokens, "/")
}

// readJsonResult decodes the API response, returns Count and Total values
// and stores the Data in the value pointed to by data.
func readJSONResult(r io.Reader, data interface{}) (int, int, error) {
	var res RequestResult
	res.Data = &data

	jsonBlob, err := ioutil.ReadAll(r) // ReadAll and store in jsonBlob (mandatory if we want to unmarshal two times)
	if err != nil {
		return 0, 0, fmt.Errorf("Error reading API response: %s", err)
	}
	if DebugLevel == LevelDebugFull {
		log.Println("Body: ", string(jsonBlob)) // DEBUG
	}

	err = json.Unmarshal(jsonBlob, &res) // First try with the RequestResult struct
	if err != nil {
		return 0, 0, fmt.Errorf("Error decoding API response: %s", err)
	} else if res.Total == 0  { // No result
		err = json.Unmarshal(jsonBlob, &data) // Trying directly with struct specified in parameter
		if err != nil {
			return 0, 0, fmt.Errorf("Error decoding API response: %s", err)
		}
		return 0, 0, nil // Count and Total are undetermined
	}
	return res.Count, res.Total, nil
}

// NbAttempt defines the number of attempt
// for a request as long as StatusCode == 500.
var NbAttempt = 5

// doRequest is called to execute the request. Authentification is set
// with the public key and the secret key specified in MailjetClient.
func (c *httpClient) doRequest(req *http.Request) (resp *http.Response, err error) {
	debugRequest(req) //DEBUG
	req.SetBasicAuth(c.apiKeyPublic, c.apiKeyPrivate)
	for attempt := 0; attempt < NbAttempt; attempt++ {
		if resp != nil {
			resp.Body.Close()
		}
		resp, err = c.client.Do(req)
		if err != nil || (resp != nil && resp.StatusCode != 500) {
			break
		}
	}
	defer debugResponse(resp) //DEBUG
	if err != nil {
		if resp != nil {
			resp.Body.Close()
		}
		return nil, fmt.Errorf("Error getting %s: %s", req.URL, err)
	}
	err = checkResponseError(resp)
	return resp, err
}

// checkResponseError returns response error if the statuscode is < 200 or >= 400.
func checkResponseError(resp *http.Response) error {
	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
		var mailjetErr RequestError
		err := json.NewDecoder(resp.Body).Decode(&mailjetErr)
		resp.Body.Close()
		if err != nil {
			return fmt.Errorf("Unexpected server response code: %d: %s", resp.StatusCode, err)
		}
		return fmt.Errorf("Unexpected server response code: %d: %s (%s)",
			resp.StatusCode, mailjetErr.ErrorMessage, mailjetErr.ErrorInfo)
	}
	return nil
}

// debugRequest is a custom dump of the request.
// Method used, final URl called, and Header content are logged.
func debugRequest(req *http.Request) {
	if DebugLevel > LevelNone && req != nil {
		log.Printf("Method used is: %s\n", req.Method)
		log.Printf("Final URL is: %s\n", req.URL)
		log.Printf("Header is: %s\n", req.Header)
	}
}

// debugResponse is a custom dump of the response.
// Status and Header content are logged.
func debugResponse(resp *http.Response) {
	if DebugLevel > LevelNone && resp != nil {
		log.Printf("Status is: %s\n", resp.Status)
		log.Printf("Header is: %s\n", resp.Header)
	}
}<commit_msg>remove unnecesary resp.Body.Close()<commit_after>package mailjet

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"reflect"
	"runtime"
	"strconv"
	"strings"
)

// DebugLevel defines the verbosity of the debug.
var DebugLevel int

// These are the different level of debug.
const (
	LevelNone      = iota // No debug.
	LevelDebug            // Debug without body.
	LevelDebugFull        // Debug with body.
)

// User-Agent is formated as "UserAgentBase/UserAgentVersion;runtime.Version()".
const (
	UserAgentBase    = "mailjet-api-v3-go"
	UserAgentVersion = "2.0.1"
)

const (
	apiBase  = "https://api.mailjet.com/v3"
	apiPath  = "REST"
	dataPath = "DATA"
)

// createRequest is the main core function.
func createRequest(method string, url string,
	payload interface{}, onlyFields []string,
	options ...RequestOptions) (req *http.Request, err error) {

	body, err := convertPayload(payload, onlyFields)
	if err != nil {
		return req, fmt.Errorf("creating request: %s\n", err)
	}
	req, err = http.NewRequest(method, url, bytes.NewBuffer(body))
	if err != nil {
		return req, fmt.Errorf("creating request: %s\n", err)
	}
	for _, option := range options {
		option(req)
	}
	userAgent(req)
	req.Header.Add("Accept", "application/json")
	return req, err
}

// converPayload returns payload casted in []byte.
// If the payload is a structure, it's encoded to JSON.
func convertPayload(payload interface{}, onlyFields []string) (body []byte, err error) {
	if payload != nil {
		switch t := payload.(type) {
		case string:
			body = []byte(t)
		case []byte:
			body = t
		default:
			v := reflect.Indirect(reflect.ValueOf(payload))
			if v.Kind() == reflect.Ptr {
				return convertPayload(v.Interface(), onlyFields)
			} else if v.Kind() == reflect.Struct {
				body, err = json.Marshal(buildMap(v, onlyFields))
				if err != nil {
					return body, err
				}
			}
		}
		if DebugLevel == LevelDebugFull {
			log.Println("Body:", string(body))
		}
	}
	return body, err
}

// buildMap returns a map with fields specified in onlyFields (all fields if nil)
// and without the read_only fields.
func buildMap(v reflect.Value, onlyFields []string) map[string]interface{} {
	res := make(map[string]interface{})
	if onlyFields != nil {
		for _, onlyField := range onlyFields {
			fieldType, exist := v.Type().FieldByName(onlyField)
			if exist {
				addFieldToMap(true, fieldType, v.FieldByName(onlyField), res)
			}
		}
	} else {
		for i := 0; i < v.NumField(); i++ {
			addFieldToMap(false, v.Type().Field(i), v.Field(i), res)
		}
	}
	return res
}

func addFieldToMap(onlyField bool, fieldType reflect.StructField,
	fieldValue reflect.Value, res map[string]interface{}) {
	if fieldType.Tag.Get("mailjet") != "read_only" {
		name, second := parseTag(fieldType.Tag.Get("json"))
		if name == "" {
			name = fieldType.Name
		}
		if !onlyField && second == "omitempty" &&
			isEmptyValue(fieldValue) {
			return
		}
		res[name] = fieldValue.Interface()
	}
}

func parseTag(tag string) (string, string) {
	if idx := strings.Index(tag, ","); idx != -1 {
		return tag[:idx], tag[idx+1:]
	}
	return tag, ""
}

func isEmptyValue(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
		return v.Len() == 0
	case reflect.Bool:
		return !v.Bool()
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return v.Int() == 0
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return v.Uint() == 0
	case reflect.Float32, reflect.Float64:
		return v.Float() == 0
	case reflect.Interface, reflect.Ptr:
		return v.IsNil()
	}
	return false
}

// userAgent add the User-Agent value to the request header.
func userAgent(req *http.Request) {
	ua := fmt.Sprintf("%s/%s;%s",
		UserAgentBase,
		UserAgentVersion,
		runtime.Version(),
	)
	req.Header.Add("User-Agent", ua)
}

func buildURL(info *Request) string {
	tokens := []string{apiBase, apiPath, info.Resource}
	if info.ID != 0 {
		id := strconv.FormatInt(info.ID, 10)
		tokens = append(tokens, id)
	} else if info.AltID != "" {
		tokens = append(tokens, string(info.AltID))
	}
	if info.Action != "" {
		tokens = append(tokens, info.Action)
	}
	if info.ActionID != 0 {
		actionID := strconv.FormatInt(info.ActionID, 10)
		tokens = append(tokens, actionID)
	}
	return strings.Join(tokens, "/")
}

func buildDataURL(info *DataRequest) string {
	tokens := []string{apiBase, dataPath, info.SourceType}
	if info.SourceTypeID != 0 {
		id := strconv.FormatInt(info.SourceTypeID, 10)
		tokens = append(tokens, id)
	}
	if info.DataType != "" {
		tokens = append(tokens, info.DataType)
		if info.MimeType != "" {
			tokens = append(tokens, info.MimeType)
		}
	}
	if info.DataTypeID != 0 {
		DataTypeID := strconv.FormatInt(info.DataTypeID, 10)
		tokens = append(tokens, DataTypeID)
	} else if info.LastID == true {
		tokens = append(tokens, "LAST")
	}
	return strings.Join(tokens, "/")
}

// readJsonResult decodes the API response, returns Count and Total values
// and stores the Data in the value pointed to by data.
func readJSONResult(r io.Reader, data interface{}) (int, int, error) {
	var res RequestResult
	res.Data = &data

	jsonBlob, err := ioutil.ReadAll(r) // ReadAll and store in jsonBlob (mandatory if we want to unmarshal two times)
	if err != nil {
		return 0, 0, fmt.Errorf("Error reading API response: %s", err)
	}
	if DebugLevel == LevelDebugFull {
		log.Println("Body: ", string(jsonBlob)) // DEBUG
	}

	err = json.Unmarshal(jsonBlob, &res) // First try with the RequestResult struct
	if err != nil {
		return 0, 0, fmt.Errorf("Error decoding API response: %s", err)
	} else if res.Total == 0  { // No result
		err = json.Unmarshal(jsonBlob, &data) // Trying directly with struct specified in parameter
		if err != nil {
			return 0, 0, fmt.Errorf("Error decoding API response: %s", err)
		}
		return 0, 0, nil // Count and Total are undetermined
	}
	return res.Count, res.Total, nil
}

// NbAttempt defines the number of attempt
// for a request as long as StatusCode == 500.
var NbAttempt = 5

// doRequest is called to execute the request. Authentification is set
// with the public key and the secret key specified in MailjetClient.
func (c *httpClient) doRequest(req *http.Request) (resp *http.Response, err error) {
	debugRequest(req) //DEBUG
	req.SetBasicAuth(c.apiKeyPublic, c.apiKeyPrivate)
	for attempt := 0; attempt < NbAttempt; attempt++ {
		if resp != nil {
			resp.Body.Close()
		}
		resp, err = c.client.Do(req)
		if err != nil || (resp != nil && resp.StatusCode != 500) {
			break
		}
	}
	defer debugResponse(resp) //DEBUG
	if err != nil {
		return resp, fmt.Errorf("Error getting %s: %s", req.URL, err)
	}
	err = checkResponseError(resp)
	return resp, err
}

// checkResponseError returns response error if the statuscode is < 200 or >= 400.
func checkResponseError(resp *http.Response) error {
	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
		var mailjetErr RequestError
		err := json.NewDecoder(resp.Body).Decode(&mailjetErr)

		if err != nil {
			return fmt.Errorf("Unexpected server response code: %d: %s", resp.StatusCode, err)
		}
		return fmt.Errorf("Unexpected server response code: %d: %s (%s)",
			resp.StatusCode, mailjetErr.ErrorMessage, mailjetErr.ErrorInfo)
	}
	return nil
}

// debugRequest is a custom dump of the request.
// Method used, final URl called, and Header content are logged.
func debugRequest(req *http.Request) {
	if DebugLevel > LevelNone && req != nil {
		log.Printf("Method used is: %s\n", req.Method)
		log.Printf("Final URL is: %s\n", req.URL)
		log.Printf("Header is: %s\n", req.Header)
	}
}

// debugResponse is a custom dump of the response.
// Status and Header content are logged.
func debugResponse(resp *http.Response) {
	if DebugLevel > LevelNone && resp != nil {
		log.Printf("Status is: %s\n", resp.Status)
		log.Printf("Header is: %s\n", resp.Header)
	}
}<|endoftext|>


<commit_before>package core

import (
	"encoding/json"
	"fmt"
	"github.com/thethingsnetwork/core/lorawan"
)

var ErrInvalidPacket error = fmt.Errorf("Invalid Packet")

type Packet struct {
	Metadata Metadata
	Payload  lorawan.PHYPayload
}

func (p Packet) DevAddr() *lorawan.DevAddr {
	addr, err := p.Payload.DevAddr()
	if err != nil {
		return nil
	}
	return &addr
}

func (p Packet) String() string {
	str := "Packet {"
	if p.Metadata != nil {
		str += fmt.Sprintf("\n\t%s}", p.Metadata.String())
	}
	str += fmt.Sprintf("\n\tPayload%+v\n}", p.Payload)
	return str
}

type Recipient struct {
	Address interface{}
	Id      interface{}
}

type Metadata interface {
	json.Marshaler
	json.Unmarshaler
	String() string
}

type AckNacker interface {
	Ack(p Packet) error
	Nack(p Packet) error
}

type Component interface {
	Handle(p Packet, an AckNacker) error
	NextUp() (*Packet, error)
	NextDown() (*Packet, error)
}

type Adapter interface {
	Send(p Packet, r ...Recipient) error
	Next() (*Packet, *AckNacker, error)
}

type BrkHdlAdapter interface {
	Adapter
	NextRegistration() (lorawan.DevAddr, interface{}, error)
}

type Router Component
type Broker interface {
	Component
	Register(handler Recipient, devAddr lorawan.DevAddr, nwskey lorawan.AES128Key) error
}
type Handler Component
type NetworkController Component
<commit_msg>[broker] Adjust core signature<commit_after>package core

import (
	"encoding/json"
	"fmt"
	"github.com/thethingsnetwork/core/lorawan"
)

var ErrInvalidPacket error = fmt.Errorf("Invalid Packet")

type Packet struct {
	Metadata Metadata
	Payload  lorawan.PHYPayload
}

func (p Packet) DevAddr() *lorawan.DevAddr {
	addr, err := p.Payload.DevAddr()
	if err != nil {
		return nil
	}
	return &addr
}

func (p Packet) String() string {
	str := "Packet {"
	if p.Metadata != nil {
		str += fmt.Sprintf("\n\t%s}", p.Metadata.String())
	}
	str += fmt.Sprintf("\n\tPayload%+v\n}", p.Payload)
	return str
}

type Recipient struct {
	Address interface{}
	Id      interface{}
}

type Metadata interface {
	json.Marshaler
	json.Unmarshaler
	String() string
}

type AckNacker interface {
	Ack(p Packet) error
	Nack(p Packet) error
}

type Component interface {
	Handle(p Packet, an AckNacker) error
	NextUp() (Packet, error)
	NextDown() (Packet, error)
}

type Adapter interface {
	Send(p Packet, r ...Recipient) error
	Next() (Packet, AckNacker, error)
}

type Registration struct {
	DevAddr lorawan.DevAddr
	Handler Recipient
	NwsKey  lorawan.AES128Key
}

type BrkHdlAdapter interface {
	Adapter
	NextRegistration() (Registration, AckNacker, error)
}

type Router Component
type Broker interface {
	Component
	Register(reg Registration) error
}
type Handler Component
type NetworkController Component
<|endoftext|>


<commit_before>package data

import (
	"net/http"
	"net/url"
	"strconv"
	"strings"
)

// Data holds data obtained from the request body and url query parameters.
// Because Data is built from multiple sources, sometimes there will be more
// than one value for a given key. You can use Get, Set, Add, and Del to access
// the first element for a given key or access the map directly to access additional
// elements for a given key. You can also use helper methods to convert the first
// value for a given key to a different type (e.g. bool or int).
type Data url.Values

// Parse parses the request body and url query parameters into
// Data. The content in the body of the request has a higher priority,
// will be added to Data first, and will be the result of any operation
// which gets the first element for a given key (e.g. Get, GetInt, or GetBool).
func Parse(req *http.Request) (Data, error) {
	values := url.Values{}
	contentType := req.Header.Get("Content-Type")
	if strings.Contains(contentType, "multipart/form-data") {
		if err := req.ParseMultipartForm(2048); err != nil {
			return nil, err
		}
		for key, val := range req.MultipartForm.Value {
			values.Add(key, value)
		}
	} else if strings.Contains(contentType, "form-urlencoded") {
		if err := req.ParseForm(); err != nil {
			return nil, err
		}
		for key, val := range req.PostForm {
			values.Add(key, value)
		}
	}
	for key, val := range req.URL.Query() {
		values.Add(key, value)
	}
	return Data(values), nil
}

// Add adds the value to key. It appends to any existing values associated with key.
func (d Data) Add(key string, value string) {
	url.Values(d).Add(key, value)
}

// Del deletes the values associated with key.
func (d Data) Del(key string) {
	url.Values(d).Del(key)
}

// Encode encodes the values into “URL encoded” form ("bar=baz&foo=quux") sorted by key.
func (d Data) Encode() string {
	return url.Values(d).Encode()
}

// Get gets the first value associated with the given key. If there are no values
// associated with the key, Get returns the empty string. To access multiple values,
// use the map directly.
func (d Data) Get(key string) string {
	return url.Values(d).Get(key)
}

// Set sets the key to value. It replaces any existing values.
func (d Data) Set(key string, value string) {
	url.Values(d).Set(key, value)
}

// KeyExists returns true iff data[key] exists. If the value of data[key] is an empty
// string, it is still considered to be in existence.
func (d Data) KeyExists(key string) bool {
	_, found := d[key]
	return found
}

// GetInt returns the first element in data[key] converted to an int.
func (d Data) GetInt(key string) int {
	if !d.KeyExists(key) || len(d[key]) == 0 {
		return 0
	}
	str := d[key][0]
	if result, err := strconv.Atoi(str); err != nil {
		panic(err)
	} else {
		return result
	}
}

// GetBool returns the first element in data[key] converted to a bool.
func (d Data) GetBool(key string) bool {
	if !d.KeyExists(key) || len(d[key]) == 0 {
		return false
	}
	str := d[key][0]
	if result, err := strconv.ParseBool(str); err != nil {
		panic(err)
	} else {
		return result
	}
}

// GetStringsSplit returns the first element in data[key] split into a slice delimited by delim.
func (d Data) GetStringsSplit(key string, delim string) []string {
	if !d.KeyExists(key) || len(d[key]) == 0 {
		return nil
	}
	return strings.Split(d[key][0], delim)
}

// Validator returns a Validator which can be used to easily validate data.
func (d Data) Validator() *Validator {
	return &Validator{
		data: d,
	}
}
<commit_msg>Fix typo in data.go<commit_after>package data

import (
	"net/http"
	"net/url"
	"strconv"
	"strings"
)

// Data holds data obtained from the request body and url query parameters.
// Because Data is built from multiple sources, sometimes there will be more
// than one value for a given key. You can use Get, Set, Add, and Del to access
// the first element for a given key or access the map directly to access additional
// elements for a given key. You can also use helper methods to convert the first
// value for a given key to a different type (e.g. bool or int).
type Data url.Values

// Parse parses the request body and url query parameters into
// Data. The content in the body of the request has a higher priority,
// will be added to Data first, and will be the result of any operation
// which gets the first element for a given key (e.g. Get, GetInt, or GetBool).
func Parse(req *http.Request) (Data, error) {
	values := url.Values{}
	contentType := req.Header.Get("Content-Type")
	if strings.Contains(contentType, "multipart/form-data") {
		if err := req.ParseMultipartForm(2048); err != nil {
			return nil, err
		}
		for key, vals := range req.MultipartForm.Value {
			for _, val := range vals {
				values.Add(key, val)
			}
		}
	} else if strings.Contains(contentType, "form-urlencoded") {
		if err := req.ParseForm(); err != nil {
			return nil, err
		}
		for key, vals := range req.PostForm {
			for _, val := range vals {
				values.Add(key, val)
			}
		}
	}
	for key, vals := range req.URL.Query() {
		for _, val := range vals {
			values.Add(key, val)
		}
	}
	return Data(values), nil
}

// Add adds the value to key. It appends to any existing values associated with key.
func (d Data) Add(key string, value string) {
	url.Values(d).Add(key, value)
}

// Del deletes the values associated with key.
func (d Data) Del(key string) {
	url.Values(d).Del(key)
}

// Encode encodes the values into “URL encoded” form ("bar=baz&foo=quux") sorted by key.
func (d Data) Encode() string {
	return url.Values(d).Encode()
}

// Get gets the first value associated with the given key. If there are no values
// associated with the key, Get returns the empty string. To access multiple values,
// use the map directly.
func (d Data) Get(key string) string {
	return url.Values(d).Get(key)
}

// Set sets the key to value. It replaces any existing values.
func (d Data) Set(key string, value string) {
	url.Values(d).Set(key, value)
}

// KeyExists returns true iff data[key] exists. If the value of data[key] is an empty
// string, it is still considered to be in existence.
func (d Data) KeyExists(key string) bool {
	_, found := d[key]
	return found
}

// GetInt returns the first element in data[key] converted to an int.
func (d Data) GetInt(key string) int {
	if !d.KeyExists(key) || len(d[key]) == 0 {
		return 0
	}
	str := d[key][0]
	if result, err := strconv.Atoi(str); err != nil {
		panic(err)
	} else {
		return result
	}
}

// GetBool returns the first element in data[key] converted to a bool.
func (d Data) GetBool(key string) bool {
	if !d.KeyExists(key) || len(d[key]) == 0 {
		return false
	}
	str := d[key][0]
	if result, err := strconv.ParseBool(str); err != nil {
		panic(err)
	} else {
		return result
	}
}

// GetStringsSplit returns the first element in data[key] split into a slice delimited by delim.
func (d Data) GetStringsSplit(key string, delim string) []string {
	if !d.KeyExists(key) || len(d[key]) == 0 {
		return nil
	}
	return strings.Split(d[key][0], delim)
}

// Validator returns a Validator which can be used to easily validate data.
func (d Data) Validator() *Validator {
	return &Validator{
		data: d,
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 The Tcell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package views

import (
	"strings"
	"sync"

	"github.com/gdamore/tcell"
)

// TextArea is a pannable 2 dimensional text widget. It wraps both
// the view and the model for text in a single, convenient widget.
// Text is provided as an array of strings, each of which represents
// a single line to display.  All text in the TextArea has the same
// style.  An optional soft cursor is available.
type TextArea struct {
	model *linesModel
	once  sync.Once
	CellView
}

type linesModel struct {
	lines  []string
	width  int
	height int
	x      int
	y      int
	hide   bool
	cursor bool
	style  tcell.Style
}

func (m *linesModel) GetCell(x, y int) (rune, tcell.Style, []rune, int) {
	var ch rune
	if x < 0 || y < 0 || y >= len(m.lines) || x >= len(m.lines[y]) {
		return ch, m.style, nil, 1
	}
	// XXX: extend this to support combining and full width chars
	return rune(m.lines[y][x]), m.style, nil, 1
}

func (m *linesModel) GetBounds() (int, int) {
	return m.width, m.height
}

func (m *linesModel) limitCursor() {
	if m.x > m.width-1 {
		m.x = m.width - 1
	}
	if m.y > m.height-1 {
		m.y = m.height - 1
	}
	if m.x < 0 {
		m.x = 0
	}
	if m.y < 0 {
		m.y = 0
	}
}

func (m *linesModel) SetCursor(x, y int) {
	m.x = x
	m.y = y
	m.limitCursor()
}

func (m *linesModel) MoveCursor(x, y int) {
	m.x += x
	m.y += y
	m.limitCursor()
}

func (m *linesModel) GetCursor() (int, int, bool, bool) {
	return m.x, m.y, m.cursor, !m.hide
}

// SetLines sets the content text to display.
func (ta *TextArea) SetLines(lines []string) {
	ta.Init()
	m := ta.model
	m.width = 0
	m.height = len(lines)
	m.lines = append([]string{}, lines...)
	for _, l := range lines {
		if len(l) > m.width {
			m.width = len(l)
		}
	}
	ta.CellView.SetModel(m)
}

func (ta *TextArea) SetStyle(style tcell.Style) {
	ta.model.style = style
	ta.CellView.SetStyle(style)
}

// EnableCursor enables a soft cursor in the TextArea.
func (ta *TextArea) EnableCursor(on bool) {
	ta.Init()
	ta.model.cursor = on
}

// HideCursor hides or shows the cursor in the TextArea.
// If on is true, the cursor is hidden.  Note that a cursor is only
// shown if it is enabled.
func (ta *TextArea) HideCursor(on bool) {
	ta.Init()
	ta.model.hide = on
}

// SetContent is used to set the textual content, passed as a
// single string.  Lines within the string are delimited by newlines.
func (ta *TextArea) SetContent(text string) {
	ta.Init()
	lines := strings.Split(strings.Trim(text, "\n"), "\n")
	ta.SetLines(lines)
}

// Init initializes the TextArea.
func (ta *TextArea) Init() {
	ta.once.Do(func() {
		lm := &linesModel{lines: []string{}, width: 0}
		ta.model = lm
		ta.CellView.Init()
		ta.CellView.SetModel(lm)
	})
}

// NewTextArea creates a blank TextArea.
func NewTextArea() *TextArea {
	ta := &TextArea{}
	ta.Init()
	return ta
}
<commit_msg>TextArea: use [][]rune instead of []string in linesModel<commit_after>// Copyright 2016 The Tcell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package views

import (
	"strings"
	"sync"

	"github.com/gdamore/tcell"
)

// TextArea is a pannable 2 dimensional text widget. It wraps both
// the view and the model for text in a single, convenient widget.
// Text is provided as an array of strings, each of which represents
// a single line to display.  All text in the TextArea has the same
// style.  An optional soft cursor is available.
type TextArea struct {
	model *linesModel
	once  sync.Once
	CellView
}

type linesModel struct {
	runes  [][]rune
	width  int
	height int
	x      int
	y      int
	hide   bool
	cursor bool
	style  tcell.Style
}

func (m *linesModel) GetCell(x, y int) (rune, tcell.Style, []rune, int) {
	if x < 0 || y < 0 || y >= m.height || x >= len(m.runes[y]) {
		return 0, m.style, nil, 1
	}
	// XXX: extend this to support combining and full width chars
	return m.runes[y][x], m.style, nil, 1
}

func (m *linesModel) GetBounds() (int, int) {
	return m.width, m.height
}

func (m *linesModel) limitCursor() {
	if m.x > m.width-1 {
		m.x = m.width - 1
	}
	if m.y > m.height-1 {
		m.y = m.height - 1
	}
	if m.x < 0 {
		m.x = 0
	}
	if m.y < 0 {
		m.y = 0
	}
}

func (m *linesModel) SetCursor(x, y int) {
	m.x = x
	m.y = y
	m.limitCursor()
}

func (m *linesModel) MoveCursor(x, y int) {
	m.x += x
	m.y += y
	m.limitCursor()
}

func (m *linesModel) GetCursor() (int, int, bool, bool) {
	return m.x, m.y, m.cursor, !m.hide
}

// SetLines sets the content text to display.
func (ta *TextArea) SetLines(lines []string) {
	ta.Init()
	m := ta.model

	// extend slice before using m.runes[row] to avoid panic
	slice := make([][]rune, len(lines))
	m.runes = append(m.runes, slice...)

	for row, line := range lines {
		for _, ch := range line {
			m.runes[row] = append(m.runes[row], ch)
		}
		if len(m.runes[row]) > m.width {
			m.width = len(m.runes[row])
		}
	}

	m.height = len(m.runes)

	ta.CellView.SetModel(m)
}

func (ta *TextArea) SetStyle(style tcell.Style) {
	ta.model.style = style
	ta.CellView.SetStyle(style)
}

// EnableCursor enables a soft cursor in the TextArea.
func (ta *TextArea) EnableCursor(on bool) {
	ta.Init()
	ta.model.cursor = on
}

// HideCursor hides or shows the cursor in the TextArea.
// If on is true, the cursor is hidden.  Note that a cursor is only
// shown if it is enabled.
func (ta *TextArea) HideCursor(on bool) {
	ta.Init()
	ta.model.hide = on
}

// SetContent is used to set the textual content, passed as a
// single string.  Lines within the string are delimited by newlines.
func (ta *TextArea) SetContent(text string) {
	ta.Init()
	lines := strings.Split(strings.Trim(text, "\n"), "\n")
	ta.SetLines(lines)
}

// Init initializes the TextArea.
func (ta *TextArea) Init() {
	ta.once.Do(func() {
		lm := &linesModel{runes: [][]rune{}, width: 0}
		ta.model = lm
		ta.CellView.Init()
		ta.CellView.SetModel(lm)
	})
}

// NewTextArea creates a blank TextArea.
func NewTextArea() *TextArea {
	ta := &TextArea{}
	ta.Init()
	return ta
}
<|endoftext|>


<commit_before>// Copyright (c) 2017, A. Stoewer <adrian.stoewer@rz.ifi.lmu.de>
// All rights reserved.

// Package qparam provides convenient functions to read query parameters from request
// URLs. On errors the package functions create errors which provide detailed information about
// missing parameters and other failure causes.
package qparam

import (
	"errors"
	"fmt"
	"net/url"
	"reflect"
	"strings"

	"github.com/stoewer/go-qparam/internal"
)

var (
	defaultTag    = "param"
	defaultMapper = strings.ToLower
	errNoPtr      = errors.New("Target must be a pointer")
	errNoStruct   = errors.New("Target must be a struct")
	errMultiple   = errors.New("Parameter has multiple values but target is not a slice")
)

// ErrorMapper is an error which also contains a map of additional (named) errors
// which altogether caused the actual failure.
type ErrorMapper interface {
	error
	ErrorMap() map[string]error
}

// Option is a functional option which can be applied to a reader.
type Option func(*ParameterReader)

// Mapper is a functional option which allows to specify a custom name mapper to
// the reader.
func Mapper(mapper func(string) string) Option {
	return func(r *ParameterReader) {
		r.mapper = mapper
	}
}

// Tag is a functional option which allows to specify a custom struct tag for the
// reader.
func Tag(tag string) Option {
	return func(r *ParameterReader) {
		r.tag = tag
	}
}

// ParameterReader defines methods which can read query parameters and assign them to matching
// fields of target structs.
type ParameterReader struct {
	tag    string
	mapper func(string) string
}

// New creates a new reader which can be configured with predefined functional options.
func New(options ...Option) *ParameterReader {
	r := &ParameterReader{tag: defaultTag, mapper: defaultMapper}

	for _, opt := range options {
		opt(r)
	}

	return r
}

// ReadParams takes the provided query parameter and assigns them to the matching fields of the
// target structs.
func (r *ParameterReader) ReadParams(params url.Values, targets ...interface{}) error {
	for _, target := range targets {
		targetType := reflect.TypeOf(target)
		if targetType.Kind() != reflect.Ptr {
			return errNoPtr
		}

		targetType = targetType.Elem()
		if targetType.Kind() != reflect.Struct {
			return errNoStruct
		}

		targetVal := reflect.ValueOf(target).Elem()
		for i := 0; i < targetType.NumField(); i++ {
			field := targetType.Field(i)

			paramName, ok := field.Tag.Lookup(r.tag)
			if !ok {
				paramName = r.mapper(field.Name)
			}

			paramVal, ok := params[paramName]
			if !ok {
				continue
			}
			if len(paramVal) == 0 {
				continue
			}

			fieldVal := targetVal.Field(i)
			switch fieldVal.Kind() {
			case reflect.Slice:
				return fmt.Errorf("Field type Slice is not supported yet")
			default:
				if len(paramVal) > 1 {
					return errMultiple
				}
				internal.ParseInto(paramVal[0], fieldVal)
			}
		}
	}
	return nil
}
<commit_msg>Use registered parsers<commit_after>// Copyright (c) 2017, A. Stoewer <adrian.stoewer@rz.ifi.lmu.de>
// All rights reserved.

// Package qparam provides convenient functions to read query parameters from request
// URLs. On errors the package functions create errors which provide detailed information about
// missing parameters and other failure causes.
package qparam

import (
	"errors"
	"net/url"
	"reflect"
	"strings"

	"github.com/stoewer/go-qparam/internal"
)

var (
	defaultTag    = "param"
	defaultMapper = strings.ToLower
	errNoPtr      = errors.New("Target must be a pointer")
	errNoStruct   = errors.New("Target must be a struct")
)

// ErrorMapper is an error which also contains a map of additional (named) errors
// which altogether caused the actual failure.
type ErrorMapper interface {
	error
	ErrorMap() map[string]error
}

// Option is a functional option which can be applied to a reader.
type Option func(*ParameterReader)

// Mapper is a functional option which allows to specify a custom name mapper to
// the reader.
func Mapper(mapper func(string) string) Option {
	return func(r *ParameterReader) {
		r.mapper = mapper
	}
}

// Tag is a functional option which allows to specify a custom struct tag for the
// reader.
func Tag(tag string) Option {
	return func(r *ParameterReader) {
		r.tag = tag
	}
}

// ParameterReader defines methods which can read query parameters and assign them to matching
// fields of target structs.
type ParameterReader struct {
	tag    string
	mapper func(string) string
}

// New creates a new reader which can be configured with predefined functional options.
func New(options ...Option) *ParameterReader {
	r := &ParameterReader{tag: defaultTag, mapper: defaultMapper}

	for _, opt := range options {
		opt(r)
	}

	return r
}

// ReadParams takes the provided query parameter and assigns them to the matching fields of the
// target structs.
func (r *ParameterReader) ReadParams(params url.Values, targets ...interface{}) error {
	for _, target := range targets {
		err := r.readParams(params, target)
		if err != nil {
			return err
		}
	}
	return nil
}

func (r *ParameterReader) readParams(params url.Values, target interface{}) error {
	targetVal := reflect.ValueOf(target)
	if targetVal.Kind() != reflect.Ptr {
		return errNoPtr
	}

	targetVal = targetVal.Elem()
	if targetVal.Kind() != reflect.Struct {
		return errNoStruct
	}

	targetType := reflect.TypeOf(target).Elem()
	for i := 0; i < targetType.NumField(); i++ {
		field := targetType.Field(i)

		paramName := r.paramName(field)
		paramVal, ok := params[paramName]
		if !ok || len(paramVal) == 0 {
			continue
		}

		fieldVal := targetVal.Field(i)
		r.parseSingle(paramVal[0], fieldVal)
	}

	return nil
}

func (r *ParameterReader) paramName(field reflect.StructField) string {
	name, ok := field.Tag.Lookup(r.tag)
	if !ok {
		name = r.mapper(field.Name)
	}
	return name
}

func (r *ParameterReader) parseSingle(str string, target reflect.Value) error {
	for _, parser := range internal.RegisteredCheckedParsers {
		if parser.Check(target) {
			err := parser.Parse(target, str)
			if err != nil {
				return err
			}
			return nil
		}
	}

	parser, ok := internal.RegisteredParsers[target.Kind()]
	if !ok {
		return errors.New("Not supported")
	}

	value, err := parser.Parse(str)
	if err != nil {
		return err
	}

	target.Set(value)
	return nil
}
<|endoftext|>