<commit_before>/**
 * (C) Copyright 2014, Deft Labs
 *
 * 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.
 *
 *
 * Some of the initial SES code came from: https://github.com/sourcegraph/go-ses/blob/master/ses.go
 * Copyright 2013 SourceGraph, Inc.
 * Copyright 2011-2013 Numrotron Inc.
 * Use of this source code is governed by an MIT-style license
 */

package dlshared

import (
	"fmt"
	"time"
	"errors"
	"net/url"
	"net/http"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/xml"
	"encoding/base64"
	"labix.org/v2/mgo/bson"
)

// -----------------------------------------------------------------------------
// The email ds interface

type EmailDs interface {
	SendTextEmailToOneAddress(from, to, subject, body string) (interface{}, error)
	SendHtmlEmailToOneAddress(from, to, subject, bodyHtml, bodyText string) (interface{}, error)
}

// -----------------------------------------------------------------------------
// The AWS SES email ds.

const (
	AwsSesEndpoint = "https://email.us-east-1.amazonaws.com"
)

type AwsEmailDs struct {
	Logger
	accessKeyId string
	secretAccessKey string
	httpClient *HttpRequestClient
}

// Create a new aws email ds.
func NewAwsEmailDs(awsAccessKeyId, awsSecretKey string, httpClient  *HttpRequestClient, logger Logger) *AwsEmailDs {
	return &AwsEmailDs{ accessKeyId: awsAccessKeyId, secretAccessKey: awsSecretKey, httpClient: httpClient, Logger: logger }
}

// Send a text email to one address.
func (self *AwsEmailDs) SendTextEmailToOneAddress(from, to, subject, body string) (interface{}, error) {
	data := make(url.Values)
	data.Add("Action", "SendEmail")
	data.Add("Source", from)
	data.Add("Destination.ToAddresses.member.1", to)
	data.Add("Message.Subject.Data", subject)
	data.Add("Message.Body.Text.Data", body)
	data.Add("AWSAccessKeyId", self.accessKeyId)
	return self.postEmailToSes(data)
}

// Send an html email to one address.
func (self *AwsEmailDs) SendHtmlEmailToOneAddress(from, to, subject, bodyHtml, bodyText string) (interface{}, error) {
	data := make(url.Values)
	data.Add("Action", "SendEmail")
	data.Add("Source", from)
	data.Add("Destination.ToAddresses.member.1", to)
	data.Add("Message.Subject.Data", subject)
	data.Add("Message.Body.Text.Data", bodyText)
	data.Add("Message.Body.Html.Data", bodyHtml)
	data.Add("AWSAccessKeyId", self.accessKeyId)
	return self.postEmailToSes(data)
}

type AwsEmailResponse struct {
	MessageId string `xml:"SendEmailResult>MessageId"`
	RequestId string `xml:"ResponseMetadata>RequestId"`
	HttpStatusCode int
}

// Post the email to SES. The raw response is:
// 	<SendEmailResponse xmlns="http://ses.amazonaws.com/doc/2010-12-01/">
// 		<SendEmailResult>
// 			<MessageId>00000144b1cc0395-e655ff4d-6c5f-4aed-919b-b4f0472f4491-000000</MessageId>
// 		</SendEmailResult>
// 		<ResponseMetadata>
// 			<RequestId>44c89891-a933-11e3-bec7-3f7c55b51d3e</RequestId>
// 		</ResponseMetadata>
// </SendEmailResponse>
//
// Documentation for the API is @: http://docs.aws.amazon.com/ses/2010-12-01/APIReference/Welcome.html
//
func (self *AwsEmailDs) postEmailToSes(data url.Values) (interface{}, error) {

	headers := make(map[string]string)

	date := time.Now().UTC().Format("Mon, 02 Jan 2006 15:04:05 -0700")

	headers["Date"] = date

	h := hmac.New(sha256.New, []uint8(self.secretAccessKey))
	h.Write([]uint8(date))
	headers["X-Amzn-Authorization"] = fmt.Sprintf("AWS3-HTTPS AWSAccessKeyId=%s, Algorithm=HmacSHA256, Signature=%s", self.accessKeyId, base64.StdEncoding.EncodeToString(h.Sum(nil)))

	httpStatusCode, response, err := self.httpClient.Post(AwsSesEndpoint, data, headers)
	if err != nil { return nil, err }

	awsResponse := &AwsEmailResponse{ HttpStatusCode: httpStatusCode }

	if httpStatusCode != http.StatusOK {
		return awsResponse, errors.New(fmt.Sprintf("Non-200 http error code returned from Aws SES post - status code: %d", httpStatusCode))
	}

	// Parse the response.
	err = xml.Unmarshal(response, &awsResponse)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Unable to parse Aws SES response - raw: %s - error: %v", string(response), err))
	}

	return awsResponse, nil
}

// -----------------------------------------------------------------------------
// The email service

type EmailSvc interface {
	SendHtmlEmailToOneAddress(from, to, subject, htmlTemplateFileName, textTemplateFileName string, params map[string]interface{}) error
	Start(kernel *Kernel) error
	Stop(kernel *Kernel) error
}

type EmailDoc struct {

	Id bson.ObjectId `bson:"_id"`

	HtmlTemplateName string `bson:"htmlTemplateName,omitempty"`
	TextTemplateName string `bson:"textTemplateName,omitempty"`

	SendMethod string `bson:"sendMethod"`

	EmailType string `bson:"type"`

	MessageId string `bson:"messageId,omitempty"`
	RequestId string `bson:"requestId,omitempty"`
	HttpStatusCode int `bson:"httpStatusCode,omitempty"`

	ToAddrs []string `bson:"toAddrs"`
	FromAddr string `bson:"fromAddr"`
	Sent *time.Time `bson:"sent"`

	Subject []byte `bson:"subject"`
	BodyHtml []byte `bson:"bodyHtml"`
	BodyText []byte `bson:"bodyText"`

	Error string `bson:"error,omitempty"`
}

// Create a new email service. Set cappedCollectionSizeInBytes to less than one to create a permanent collection
// (i.e., otherwise it creates a capped collection). Currently, the email service only supports sending via
// AWS SES.
func NewAwsEmailSvc(dbComponentName,
					templateComponentName,
					dbName, collectionName,
					awsAccessKeyId,
					awsSecretKey string,
					cappedCollectionSizeInBytes int) EmailSvc {
	return &AwsEmailSvc{
		Logger: Logger{},
		DataSource: DataSource{ DbName: dbName, CollectionName: collectionName },
		dbComponentName: dbComponentName,
		templateComponentName: templateComponentName,
		cappedCollectionSizeInBytes: cappedCollectionSizeInBytes,
		awsEmailDs: NewAwsEmailDs(awsAccessKeyId, awsSecretKey, NewDefaultHttpRequestClient(), Logger{}),
	}
}

type AwsEmailSvc struct {
	Logger
	DataSource
	dbComponentName string
	templateComponentName string
	cappedCollectionSizeInBytes int
	awsEmailDs *AwsEmailDs
	templateSvc *TemplateSvc
}

func (self *AwsEmailSvc) storeEmail(from, to, subject, htmlTemplateFileName, textTemplateFileName, bodyHtml, bodyText string, response interface{}, err error) {
	now := time.Now()

	compressedBodyHtml, err := CompressBytes([]byte(bodyHtml))
	if err != nil { self.Logf(Warn, "Unable to compress html body: %v", err) }

	compressedBodyTxt, err := CompressBytes([]byte(bodyText))
	if err != nil { self.Logf(Warn, "Unable to compress text body: %v", err) }

	compressedSubject, err := CompressBytes([]byte(subject))
	if err != nil { self.Logf(Warn, "Unable to compress subject: %v", err) }

	doc := &EmailDoc{	Id: bson.NewObjectId(),
						HtmlTemplateName: htmlTemplateFileName,
						TextTemplateName: textTemplateFileName,
						SendMethod: "AWS-SES",
						EmailType: "html",
						ToAddrs: []string{ to },
						FromAddr: from,
						Sent: &now,
						Subject: compressedSubject,
						BodyHtml: compressedBodyHtml,
						BodyText: compressedBodyTxt,
	}

	if err != nil { doc.Error = err.Error() }

	if response != nil {
		r := response.(*AwsEmailResponse)
		doc.MessageId = r.MessageId
		doc.RequestId = r.RequestId
		doc.HttpStatusCode = r.HttpStatusCode
	}

	if err := self.Insert(doc); err != nil { self.Logf(Warn, "Unable to store email doc: %v", err) }
}

// Send an html email.
func (self *AwsEmailSvc) SendHtmlEmailToOneAddress(from, to, subject, htmlTemplateFileName, textTemplateFileName string, params map[string]interface{}) error {

	// Render the templates.
	bodyHtml, bodyText, err := self.templateSvc.RenderHtmlAndText(htmlTemplateFileName, textTemplateFileName, params)

	// Send the email to the user.
	response, err := self.awsEmailDs.SendHtmlEmailToOneAddress(from, to, subject, bodyHtml, bodyText)
	if err != nil {
		self.storeEmail(from, to, subject, htmlTemplateFileName, textTemplateFileName, bodyHtml, bodyText, response, err)
		return err
	}

	self.storeEmail(from, to, subject, htmlTemplateFileName, textTemplateFileName, bodyHtml, bodyText, response, nil)

	return nil
}

func (self *AwsEmailSvc) Start(kernel *Kernel) error {

	self.Logger = kernel.Logger
	self.awsEmailDs.Logger = self.Logger

	self.Mongo = kernel.GetComponent(self.dbComponentName).(*Mongo)
	self.templateSvc = kernel.GetComponent(self.templateComponentName).(*TemplateSvc)

	// Create the capped collection to store the emails.
	if self.cappedCollectionSizeInBytes > 0 {
		if err := self.CreateCappedCollection(self.cappedCollectionSizeInBytes); err != nil { return err }
	}

	if err := self.EnsureIndex([]string{ "sent" }); err != nil { return err }
	if err := self.EnsureIndex([]string{ "toAddrs" }); err != nil { return err }

	if err := self.EnsureIndex([]string{ "toAddrs", "sent"  }); err != nil { return err }

	if err := self.EnsureSparseIndex([]string{ "messageId" }); err != nil { return err }
	if err := self.EnsureSparseIndex([]string{ "requestId" }); err != nil { return err }

	return nil
}

func (self *AwsEmailSvc) Stop(kernel *Kernel) error { return nil }

<commit_msg>fixed ses response error handling.<commit_after>/**
 * (C) Copyright 2014, Deft Labs
 *
 * 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.
 *
 *
 * Some of the initial SES code came from: https://github.com/sourcegraph/go-ses/blob/master/ses.go
 * Copyright 2013 SourceGraph, Inc.
 * Copyright 2011-2013 Numrotron Inc.
 * Use of this source code is governed by an MIT-style license
 */

package dlshared

import (
	"fmt"
	"time"
	"errors"
	"net/url"
	//"net/http"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/xml"
	"encoding/base64"
	"labix.org/v2/mgo/bson"
)

// -----------------------------------------------------------------------------
// The email ds interface

type EmailDs interface {
	SendTextEmailToOneAddress(from, to, subject, body string) (interface{}, error)
	SendHtmlEmailToOneAddress(from, to, subject, bodyHtml, bodyText string) (interface{}, error)
}

// -----------------------------------------------------------------------------
// The AWS SES email ds.

const (
	AwsSesEndpoint = "https://email.us-east-1.amazonaws.com"
)

type AwsEmailDs struct {
	Logger
	accessKeyId string
	secretAccessKey string
	httpClient *HttpRequestClient
}

// Create a new aws email ds.
func NewAwsEmailDs(awsAccessKeyId, awsSecretKey string, httpClient  *HttpRequestClient, logger Logger) *AwsEmailDs {
	return &AwsEmailDs{ accessKeyId: awsAccessKeyId, secretAccessKey: awsSecretKey, httpClient: httpClient, Logger: logger }
}

// Send a text email to one address.
func (self *AwsEmailDs) SendTextEmailToOneAddress(from, to, subject, body string) (interface{}, error) {
	data := make(url.Values)
	data.Add("Action", "SendEmail")
	data.Add("Source", from)
	data.Add("Destination.ToAddresses.member.1", to)
	data.Add("Message.Subject.Data", subject)
	data.Add("Message.Body.Text.Data", body)
	data.Add("AWSAccessKeyId", self.accessKeyId)
	return self.postEmailToSes(data)
}

// Send an html email to one address.
func (self *AwsEmailDs) SendHtmlEmailToOneAddress(from, to, subject, bodyHtml, bodyText string) (interface{}, error) {
	data := make(url.Values)
	data.Add("Action", "SendEmail")
	data.Add("Source", from)
	data.Add("Destination.ToAddresses.member.1", to)
	data.Add("Message.Subject.Data", subject)
	data.Add("Message.Body.Text.Data", bodyText)
	data.Add("Message.Body.Html.Data", bodyHtml)
	data.Add("AWSAccessKeyId", self.accessKeyId)
	return self.postEmailToSes(data)
}

type AwsEmailResponse struct {
	MessageId string `xml:"SendEmailResult>MessageId"`
	RequestId string `xml:"ResponseMetadata>RequestId"`
	HttpStatusCode int
}

// Post the email to SES. The raw response is:
// 	<SendEmailResponse xmlns="http://ses.amazonaws.com/doc/2010-12-01/">
// 		<SendEmailResult>
// 			<MessageId>00000144b1cc0395-e655ff4d-6c5f-4aed-919b-b4f0472f4491-000000</MessageId>
// 		</SendEmailResult>
// 		<ResponseMetadata>
// 			<RequestId>44c89891-a933-11e3-bec7-3f7c55b51d3e</RequestId>
// 		</ResponseMetadata>
// </SendEmailResponse>
//
// Documentation for the API is @: http://docs.aws.amazon.com/ses/2010-12-01/APIReference/Welcome.html
//
func (self *AwsEmailDs) postEmailToSes(data url.Values) (interface{}, error) {

	headers := make(map[string]string)

	date := time.Now().UTC().Format("Mon, 02 Jan 2006 15:04:05 -0700")

	headers["Date"] = date

	h := hmac.New(sha256.New, []uint8(self.secretAccessKey))
	h.Write([]uint8(date))
	headers["X-Amzn-Authorization"] = fmt.Sprintf("AWS3-HTTPS AWSAccessKeyId=%s, Algorithm=HmacSHA256, Signature=%s", self.accessKeyId, base64.StdEncoding.EncodeToString(h.Sum(nil)))

	httpStatusCode, response, err := self.httpClient.Post(AwsSesEndpoint, data, headers)
	if err != nil { return nil, err }

	awsResponse := &AwsEmailResponse{ HttpStatusCode: httpStatusCode }

	/*
	if httpStatusCode != http.StatusOK {
		return awsResponse, errors.New(fmt.Sprintf("Non-200 http error code returned from Aws SES post - status code: %d", httpStatusCode))
	}
	*/

	// Parse the response.
	err = xml.Unmarshal(response, &awsResponse)
	if err != nil {
		return awsResponse, errors.New(fmt.Sprintf("Unable to parse Aws SES response - raw: %s - error: %v", string(response), err))
	}

	return awsResponse, nil
}

// -----------------------------------------------------------------------------
// The email service

type EmailSvc interface {
	SendHtmlEmailToOneAddress(from, to, subject, htmlTemplateFileName, textTemplateFileName string, params map[string]interface{}) error
	Start(kernel *Kernel) error
	Stop(kernel *Kernel) error
}

type EmailDoc struct {

	Id bson.ObjectId `bson:"_id"`

	HtmlTemplateName string `bson:"htmlTemplateName,omitempty"`
	TextTemplateName string `bson:"textTemplateName,omitempty"`

	SendMethod string `bson:"sendMethod"`

	EmailType string `bson:"type"`

	MessageId string `bson:"messageId,omitempty"`
	RequestId string `bson:"requestId,omitempty"`
	HttpStatusCode int `bson:"httpStatusCode,omitempty"`

	ToAddrs []string `bson:"toAddrs"`
	FromAddr string `bson:"fromAddr"`
	Sent *time.Time `bson:"sent"`

	Subject []byte `bson:"subject"`
	BodyHtml []byte `bson:"bodyHtml"`
	BodyText []byte `bson:"bodyText"`

	Error string `bson:"error,omitempty"`
}

// Create a new email service. Set cappedCollectionSizeInBytes to less than one to create a permanent collection
// (i.e., otherwise it creates a capped collection). Currently, the email service only supports sending via
// AWS SES.
func NewAwsEmailSvc(dbComponentName,
					templateComponentName,
					dbName, collectionName,
					awsAccessKeyId,
					awsSecretKey string,
					cappedCollectionSizeInBytes int) EmailSvc {
	return &AwsEmailSvc{
		Logger: Logger{},
		DataSource: DataSource{ DbName: dbName, CollectionName: collectionName },
		dbComponentName: dbComponentName,
		templateComponentName: templateComponentName,
		cappedCollectionSizeInBytes: cappedCollectionSizeInBytes,
		awsEmailDs: NewAwsEmailDs(awsAccessKeyId, awsSecretKey, NewDefaultHttpRequestClient(), Logger{}),
	}
}

type AwsEmailSvc struct {
	Logger
	DataSource
	dbComponentName string
	templateComponentName string
	cappedCollectionSizeInBytes int
	awsEmailDs *AwsEmailDs
	templateSvc *TemplateSvc
}

func (self *AwsEmailSvc) storeEmail(from, to, subject, htmlTemplateFileName, textTemplateFileName, bodyHtml, bodyText string, response interface{}, err error) {
	now := time.Now()

	compressedBodyHtml, err := CompressBytes([]byte(bodyHtml))
	if err != nil { self.Logf(Warn, "Unable to compress html body: %v", err) }

	compressedBodyTxt, err := CompressBytes([]byte(bodyText))
	if err != nil { self.Logf(Warn, "Unable to compress text body: %v", err) }

	compressedSubject, err := CompressBytes([]byte(subject))
	if err != nil { self.Logf(Warn, "Unable to compress subject: %v", err) }

	doc := &EmailDoc{	Id: bson.NewObjectId(),
						HtmlTemplateName: htmlTemplateFileName,
						TextTemplateName: textTemplateFileName,
						SendMethod: "AWS-SES",
						EmailType: "html",
						ToAddrs: []string{ to },
						FromAddr: from,
						Sent: &now,
						Subject: compressedSubject,
						BodyHtml: compressedBodyHtml,
						BodyText: compressedBodyTxt,
	}

	if err != nil { doc.Error = err.Error() }

	if response != nil {
		r := response.(*AwsEmailResponse)
		doc.MessageId = r.MessageId
		doc.RequestId = r.RequestId
		doc.HttpStatusCode = r.HttpStatusCode
	}

	if err := self.Insert(doc); err != nil { self.Logf(Warn, "Unable to store email doc: %v", err) }
}

// Send an html email.
func (self *AwsEmailSvc) SendHtmlEmailToOneAddress(from, to, subject, htmlTemplateFileName, textTemplateFileName string, params map[string]interface{}) error {

	// Render the templates.
	bodyHtml, bodyText, err := self.templateSvc.RenderHtmlAndText(htmlTemplateFileName, textTemplateFileName, params)

	// Send the email to the user.
	response, err := self.awsEmailDs.SendHtmlEmailToOneAddress(from, to, subject, bodyHtml, bodyText)
	if err != nil {
		self.storeEmail(from, to, subject, htmlTemplateFileName, textTemplateFileName, bodyHtml, bodyText, response, err)
		return err
	}

	self.storeEmail(from, to, subject, htmlTemplateFileName, textTemplateFileName, bodyHtml, bodyText, response, nil)

	return nil
}

func (self *AwsEmailSvc) Start(kernel *Kernel) error {

	self.Logger = kernel.Logger
	self.awsEmailDs.Logger = self.Logger

	self.Mongo = kernel.GetComponent(self.dbComponentName).(*Mongo)
	self.templateSvc = kernel.GetComponent(self.templateComponentName).(*TemplateSvc)

	// Create the capped collection to store the emails.
	if self.cappedCollectionSizeInBytes > 0 {
		if err := self.CreateCappedCollection(self.cappedCollectionSizeInBytes); err != nil { return err }
	}

	if err := self.EnsureIndex([]string{ "sent" }); err != nil { return err }
	if err := self.EnsureIndex([]string{ "toAddrs" }); err != nil { return err }

	if err := self.EnsureIndex([]string{ "toAddrs", "sent"  }); err != nil { return err }

	if err := self.EnsureSparseIndex([]string{ "messageId" }); err != nil { return err }
	if err := self.EnsureSparseIndex([]string{ "requestId" }); err != nil { return err }

	return nil
}

func (self *AwsEmailSvc) Stop(kernel *Kernel) error { return nil }

<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"strings"
	"time"

	"github.com/jh-bate/fantail-bot/Godeps/_workspace/src/github.com/tucnak/telebot"
	"github.com/jh-bate/fantail-bot/lib"
)

const (
	//commands
	yostart_command = "/hey"
	yobg_command    = "/bg"
	yofood_command  = "/food"
	yolow_command   = "/low"
)

type (
	fantailBot struct {
		bot     *telebot.Bot
		process lib.Process
	}
)

func newFantailBot() *fantailBot {
	botToken := os.Getenv("BOT_TOKEN")

	if botToken == "" {
		log.Fatal("$BOT_TOKEN must be set")
	}

	bot, err := telebot.NewBot(botToken)
	if err != nil {
		return nil
	}
	return &fantailBot{bot: bot}
}

func (this *fantailBot) showOptions(usr telebot.User) {
	this.bot.SendMessage(
		usr,
		fmt.Sprintf("Hey %s", usr.FirstName),
		nil)
	this.bot.SendMessage(
		usr,
		"Sorry but at this time you can only choose from the options below ...",
		nil)
	this.bot.SendMessage(
		usr,
		fmt.Sprintf("Select one and we can get this party started!"),
		&telebot.SendOptions{
			ReplyMarkup: telebot.ReplyMarkup{
				ForceReply: false,
				CustomKeyboard: [][]string{
					[]string{yobg_command},
					[]string{yolow_command},
				},
				ResizeKeyboard:  false,
				OneTimeKeyboard: false,
			},
		})
	return
}

func (this *fantailBot) setRunning(p lib.Process) {
	this.process = nil
	this.process = p
	return
}

func (this *fantailBot) isRunning() bool {
	return this.process != nil && this.process.CanRun()
}

func (this *fantailBot) startLow(usr telebot.User) {
	log.Println("init LOW setup")
	this.setRunning(lib.NewLow(&lib.Details{Bot: this.bot, User: usr}))
}

func (this *fantailBot) startQuickBg(usr telebot.User) {
	log.Println("init QBG setup")
	this.setRunning(lib.NewQuickBg(&lib.Details{Bot: this.bot, User: usr}))
}

func (this *fantailBot) startFunWithFood(usr telebot.User) {
	log.Println("init Fun w Food setup")
	this.setRunning(lib.NewFood(&lib.Details{Bot: this.bot, User: usr}))
}

func main() {

	fBot := newFantailBot()
	messages := make(chan telebot.Message)
	fBot.bot.Listen(messages, 1*time.Second)

	for msg := range messages {

		log.Println("incoming ...", msg.Text)

		if strings.Contains(msg.Text, yolow_command) {
			fBot.startLow(msg.Chat)
		} else if strings.Contains(msg.Text, yobg_command) {
			fBot.startQuickBg(msg.Chat)
		} else if strings.Contains(msg.Text, yobg_command) {
			fBot.startQuickBg(msg.Chat)
		}
		if fBot.isRunning() {
			fBot.process.Run(msg)
		}
	}

}
<commit_msg>allow food to run<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"strings"
	"time"

	"github.com/jh-bate/fantail-bot/Godeps/_workspace/src/github.com/tucnak/telebot"
	"github.com/jh-bate/fantail-bot/lib"
)

const (
	//commands
	yostart_command = "/hey"
	yobg_command    = "/bg"
	yofood_command  = "/food"
	yolow_command   = "/low"
)

type (
	fantailBot struct {
		bot     *telebot.Bot
		process lib.Process
	}
)

func newFantailBot() *fantailBot {
	botToken := os.Getenv("BOT_TOKEN")

	if botToken == "" {
		log.Fatal("$BOT_TOKEN must be set")
	}

	bot, err := telebot.NewBot(botToken)
	if err != nil {
		return nil
	}
	return &fantailBot{bot: bot}
}

func (this *fantailBot) showOptions(usr telebot.User) {
	this.bot.SendMessage(
		usr,
		fmt.Sprintf("Hey %s", usr.FirstName),
		nil)
	this.bot.SendMessage(
		usr,
		"Sorry but at this time you can only choose from the options below ...",
		nil)
	this.bot.SendMessage(
		usr,
		fmt.Sprintf("Select one and we can get this party started!"),
		&telebot.SendOptions{
			ReplyMarkup: telebot.ReplyMarkup{
				ForceReply: false,
				CustomKeyboard: [][]string{
					[]string{yobg_command},
					[]string{yolow_command},
				},
				ResizeKeyboard:  false,
				OneTimeKeyboard: false,
			},
		})
	return
}

func (this *fantailBot) setRunning(p lib.Process) {
	this.process = nil
	this.process = p
	return
}

func (this *fantailBot) isRunning() bool {
	return this.process != nil && this.process.CanRun()
}

func (this *fantailBot) startLow(usr telebot.User) {
	log.Println("init LOW setup")
	this.setRunning(lib.NewLow(&lib.Details{Bot: this.bot, User: usr}))
}

func (this *fantailBot) startQuickBg(usr telebot.User) {
	log.Println("init QBG setup")
	this.setRunning(lib.NewQuickBg(&lib.Details{Bot: this.bot, User: usr}))
}

func (this *fantailBot) startFunWithFood(usr telebot.User) {
	log.Println("init Fun w Food setup")
	this.setRunning(lib.NewFood(&lib.Details{Bot: this.bot, User: usr}))
}

func main() {

	fBot := newFantailBot()
	messages := make(chan telebot.Message)
	fBot.bot.Listen(messages, 1*time.Second)

	for msg := range messages {

		log.Println("incoming ...", msg.Text)

		if strings.Contains(msg.Text, yolow_command) {
			fBot.startLow(msg.Chat)
		} else if strings.Contains(msg.Text, yobg_command) {
			fBot.startQuickBg(msg.Chat)
		} else if strings.Contains(msg.Text, yofood_command) {
			fBot.startFunWithFood(msg.Chat)
		}
		if fBot.isRunning() {
			fBot.process.Run(msg)
		}
	}

}
<|endoftext|>


<commit_before>package Debounce

import (
	"time"
)

func Example_debounce() {

	i := 1
	due := []time.Duration{
		0,
		300 * time.Millisecond,
		80 * time.Millisecond, // 80ms < 100ms => '2' is ignored
		110 * time.Millisecond,
		0,
	}
	source := MakeTimedInt(due[0],
		func(Next func(int), Error func(error), Complete func()) time.Duration {
			if i < len(due) {
				Next(i)
				i++
				return due[i-1]
			} else {
				Complete()
				return 0
			}
		})

	debounced := source.Debounce(100 * time.Millisecond)

	debounced.Println()

	// Output:
	// 1
	// 3
	// 4
}
<commit_msg>Handle error in Debounce test<commit_after>package Debounce

import (
	"fmt"
	"time"
)

func Example_debounce() {

	i := 1
	due := []time.Duration{
		0,
		300 * time.Millisecond,
		80 * time.Millisecond, // 80ms < 100ms => '2' is ignored
		110 * time.Millisecond,
		0,
	}
	source := MakeTimedInt(due[0],
		func(Next func(int), Error func(error), Complete func()) time.Duration {
			if i < len(due) {
				Next(i)
				i++
				return due[i-1]
			} else {
				Complete()
				return 0
			}
		})

	debounced := source.Debounce(100 * time.Millisecond)

	if err := debounced.Println(); err != nil {
		fmt.Println(err)
	}

	// Output:
	// 1
	// 3
	// 4
}
<|endoftext|>


<commit_before>// Package emoji terminal output.
package gomoji

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"unicode"
)

//go:generate generateEmojiCodeMap -pkg emoji

// Replace Padding character for emoji.
const (
	ReplacePadding = " "
)

// CodeMap gets the underlying map of emoji.
func CodeMap() map[string]string {
	return emojiCodeMap
}

func AllCodeMap() map[string]string {
	return allCodeMap
}

func emojize(x string) string {
	str, ok := emojiCodeMap[x]
	if ok {
		return str + ReplacePadding
	}
	return x
}

func replaseEmoji(input *bytes.Buffer) string {
	emoji := bytes.NewBufferString(":")
	for {
		i, _, err := input.ReadRune()
		if err != nil {
			// not replase
			return emoji.String()
		}

		if i == ':' && emoji.Len() == 1 {
			return emoji.String() + replaseEmoji(input)
		}

		emoji.WriteRune(i)
		switch {
		case unicode.IsSpace(i):
			return emoji.String()
		case i == ':':
			return emojize(emoji.String())
		}
	}
}

func compile(x string) string {
	if x == "" {
		return ""
	}

	input := bytes.NewBufferString(x)
	output := bytes.NewBufferString("")

	for {
		i, _, err := input.ReadRune()
		if err != nil {
			break
		}
		switch i {
		default:
			output.WriteRune(i)
		case ':':
			output.WriteString(replaseEmoji(input))
		}
	}
	return output.String()
}

func compileValues(a *[]interface{}) {
	for i, x := range *a {
		if str, ok := x.(string); ok {
			(*a)[i] = compile(str)
		}
	}
}

// Print is fmt.Print which supports emoji
func Print(a ...interface{}) (int, error) {
	compileValues(&a)
	return fmt.Print(a...)
}

// Println is fmt.Println which supports emoji
func Println(a ...interface{}) (int, error) {
	compileValues(&a)
	return fmt.Println(a...)
}

// Printf is fmt.Printf which supports emoji
func Printf(format string, a ...interface{}) (int, error) {
	format = compile(format)
	return fmt.Printf(format, a...)
}

// Fprint is fmt.Fprint which supports emoji
func Fprint(w io.Writer, a ...interface{}) (int, error) {
	compileValues(&a)
	return fmt.Fprint(w, a...)
}

// Fprintln is fmt.Fprintln which supports emoji
func Fprintln(w io.Writer, a ...interface{}) (int, error) {
	compileValues(&a)
	return fmt.Fprintln(w, a...)
}

// Fprintf is fmt.Fprintf which supports emoji
func Fprintf(w io.Writer, format string, a ...interface{}) (int, error) {
	format = compile(format)
	return fmt.Fprintf(w, format, a...)
}

// Sprint is fmt.Sprint which supports emoji
func Sprint(a ...interface{}) string {
	compileValues(&a)
	return fmt.Sprint(a...)
}

// Sprintf is fmt.Sprintf which supports emoji
func Sprintf(format string, a ...interface{}) string {
	format = compile(format)
	return fmt.Sprintf(format, a...)
}

// Errorf is fmt.Errorf which supports emoji
func Errorf(format string, a ...interface{}) error {
	return errors.New(Sprintf(format, a...))
}
<commit_msg>fix bug<commit_after>// Package emoji terminal output.
package gomoji

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"unicode"
)

//go:generate generateEmojiCodeMap -pkg emoji

// Replace Padding character for emoji.
const (
	ReplacePadding = " "
)

// CodeMap gets the underlying map of emoji.
func CodeMap() map[string]string {
	return emojiCodeMap
}

// func AllCodeMap() map[string]string {
// 	return allCodeMap
// }

func emojize(x string) string {
	str, ok := emojiCodeMap[x]
	if ok {
		return str + ReplacePadding
	}
	return x
}

func replaseEmoji(input *bytes.Buffer) string {
	emoji := bytes.NewBufferString(":")
	for {
		i, _, err := input.ReadRune()
		if err != nil {
			// not replase
			return emoji.String()
		}

		if i == ':' && emoji.Len() == 1 {
			return emoji.String() + replaseEmoji(input)
		}

		emoji.WriteRune(i)
		switch {
		case unicode.IsSpace(i):
			return emoji.String()
		case i == ':':
			return emojize(emoji.String())
		}
	}
}

func compile(x string) string {
	if x == "" {
		return ""
	}

	input := bytes.NewBufferString(x)
	output := bytes.NewBufferString("")

	for {
		i, _, err := input.ReadRune()
		if err != nil {
			break
		}
		switch i {
		default:
			output.WriteRune(i)
		case ':':
			output.WriteString(replaseEmoji(input))
		}
	}
	return output.String()
}

func compileValues(a *[]interface{}) {
	for i, x := range *a {
		if str, ok := x.(string); ok {
			(*a)[i] = compile(str)
		}
	}
}

// Print is fmt.Print which supports emoji
func Print(a ...interface{}) (int, error) {
	compileValues(&a)
	return fmt.Print(a...)
}

// Println is fmt.Println which supports emoji
func Println(a ...interface{}) (int, error) {
	compileValues(&a)
	return fmt.Println(a...)
}

// Printf is fmt.Printf which supports emoji
func Printf(format string, a ...interface{}) (int, error) {
	format = compile(format)
	return fmt.Printf(format, a...)
}

// Fprint is fmt.Fprint which supports emoji
func Fprint(w io.Writer, a ...interface{}) (int, error) {
	compileValues(&a)
	return fmt.Fprint(w, a...)
}

// Fprintln is fmt.Fprintln which supports emoji
func Fprintln(w io.Writer, a ...interface{}) (int, error) {
	compileValues(&a)
	return fmt.Fprintln(w, a...)
}

// Fprintf is fmt.Fprintf which supports emoji
func Fprintf(w io.Writer, format string, a ...interface{}) (int, error) {
	format = compile(format)
	return fmt.Fprintf(w, format, a...)
}

// Sprint is fmt.Sprint which supports emoji
func Sprint(a ...interface{}) string {
	compileValues(&a)
	return fmt.Sprint(a...)
}

// Sprintf is fmt.Sprintf which supports emoji
func Sprintf(format string, a ...interface{}) string {
	format = compile(format)
	return fmt.Sprintf(format, a...)
}

// Errorf is fmt.Errorf which supports emoji
func Errorf(format string, a ...interface{}) error {
	return errors.New(Sprintf(format, a...))
}
<|endoftext|>


<commit_before>package logrus

import (
	"bytes"
	"context"
	"fmt"
	"os"
	"reflect"
	"runtime"
	"strings"
	"sync"
	"time"
)

var (
	bufferPool *sync.Pool

	// qualified package name, cached at first use
	logrusPackage string

	// Positions in the call stack when tracing to report the calling method
	minimumCallerDepth int

	// Used for caller information initialisation
	callerInitOnce sync.Once
)

const (
	maximumCallerDepth int = 25
	knownLogrusFrames  int = 4
)

func init() {
	bufferPool = &sync.Pool{
		New: func() interface{} {
			return new(bytes.Buffer)
		},
	}

	// start at the bottom of the stack before the package-name cache is primed
	minimumCallerDepth = 1
}

// Defines the key when adding errors using WithError.
var ErrorKey = "error"

// An entry is the final or intermediate Logrus logging entry. It contains all
// the fields passed with WithField{,s}. It's finally logged when Trace, Debug,
// Info, Warn, Error, Fatal or Panic is called on it. These objects can be
// reused and passed around as much as you wish to avoid field duplication.
type Entry struct {
	Logger *Logger

	// Contains all the fields set by the user.
	Data Fields

	// Time at which the log entry was created
	Time time.Time

	// Level the log entry was logged at: Trace, Debug, Info, Warn, Error, Fatal or Panic
	// This field will be set on entry firing and the value will be equal to the one in Logger struct field.
	Level Level

	// Calling method, with package name
	Caller *runtime.Frame

	// Message passed to Trace, Debug, Info, Warn, Error, Fatal or Panic
	Message string

	// When formatter is called in entry.log(), a Buffer may be set to entry
	Buffer *bytes.Buffer

	// Contains the context set by the user. Useful for hook processing etc.
	Context context.Context

	// err may contain a field formatting error
	err string
}

func NewEntry(logger *Logger) *Entry {
	return &Entry{
		Logger: logger,
		// Default is three fields, plus one optional.  Give a little extra room.
		Data: make(Fields, 6),
	}
}

// Returns the bytes representation of this entry from the formatter.
func (entry *Entry) Bytes() ([]byte, error) {
	return entry.Logger.Formatter.Format(entry)
}

// Returns the string representation from the reader and ultimately the
// formatter.
func (entry *Entry) String() (string, error) {
	serialized, err := entry.Bytes()
	if err != nil {
		return "", err
	}
	str := string(serialized)
	return str, nil
}

// Add an error as single field (using the key defined in ErrorKey) to the Entry.
func (entry *Entry) WithError(err error) *Entry {
	return entry.WithField(ErrorKey, err)
}

// Add a context to the Entry.
func (entry *Entry) WithContext(ctx context.Context) *Entry {
	dataCopy := make(Fields, len(entry.Data))
	for k, v := range entry.Data {
		dataCopy[k] = v
	}
	return &Entry{Logger: entry.Logger, Data: dataCopy, Time: entry.Time, err: entry.err, Context: ctx}
}

// Add a single field to the Entry.
func (entry *Entry) WithField(key string, value interface{}) *Entry {
	return entry.WithFields(Fields{key: value})
}

// Add a map of fields to the Entry.
func (entry *Entry) WithFields(fields Fields) *Entry {
	entry.Logger.mu.Lock()
	defer entry.Logger.mu.Unlock()
	data := make(Fields, len(entry.Data)+len(fields))
	for k, v := range entry.Data {
		data[k] = v
	}
	fieldErr := entry.err
	for k, v := range fields {
		isErrField := false
		if t := reflect.TypeOf(v); t != nil {
			switch t.Kind() {
			case reflect.Func:
				isErrField = true
			case reflect.Ptr:
				isErrField = t.Elem().Kind() == reflect.Func
			}
		}
		if isErrField {
			tmp := fmt.Sprintf("can not add field %q", k)
			if fieldErr != "" {
				fieldErr = entry.err + ", " + tmp
			} else {
				fieldErr = tmp
			}
		} else {
			data[k] = v
		}
	}
	return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr, Context: entry.Context}
}

// Overrides the time of the Entry.
func (entry *Entry) WithTime(t time.Time) *Entry {
	dataCopy := make(Fields, len(entry.Data))
	for k, v := range entry.Data {
		dataCopy[k] = v
	}
	return &Entry{Logger: entry.Logger, Data: dataCopy, Time: t, err: entry.err, Context: entry.Context}
}

// getPackageName reduces a fully qualified function name to the package name
// There really ought to be to be a better way...
func getPackageName(f string) string {
	for {
		lastPeriod := strings.LastIndex(f, ".")
		lastSlash := strings.LastIndex(f, "/")
		if lastPeriod > lastSlash {
			f = f[:lastPeriod]
		} else {
			break
		}
	}

	return f
}

// getCaller retrieves the name of the first non-logrus calling function
func getCaller() *runtime.Frame {

	// cache this package's fully-qualified name
	callerInitOnce.Do(func() {
		pcs := make([]uintptr, 2)
		_ = runtime.Callers(0, pcs)
		logrusPackage = getPackageName(funcName(pcs))

		// now that we have the cache, we can skip a minimum count of known-logrus functions
		// XXX this is dubious, the number of frames may vary
		minimumCallerDepth = knownLogrusFrames
	})

	// Restrict the lookback frames to avoid runaway lookups
	pcs := make([]uintptr, maximumCallerDepth)
	depth := runtime.Callers(minimumCallerDepth, pcs)
	frames := runtime.CallersFrames(pcs[:depth])

	for f, again := frames.Next(); again; f, again = frames.Next() {
		pkg := getPackageName(f.Function)

		// If the caller isn't part of this package, we're done
		if pkg != logrusPackage {
			return &f //nolint:scopelint
		}
	}

	// if we got here, we failed to find the caller's context
	return nil
}

func (entry Entry) HasCaller() (has bool) {
	return entry.Logger != nil &&
		entry.Logger.ReportCaller &&
		entry.Caller != nil
}

// This function is not declared with a pointer value because otherwise
// race conditions will occur when using multiple goroutines
func (entry Entry) log(level Level, msg string) {

	var buffer *bytes.Buffer

	// Default to now, but allow users to override if they want.
	//
	// We don't have to worry about polluting future calls to Entry#log()
	// with this assignment because this function is declared with a
	// non-pointer receiver.
	if entry.Time.IsZero() {
		entry.Time = time.Now()
	}

	entry.Level = level
	entry.Message = msg
	entry.Logger.mu.Lock()
	if entry.Logger.ReportCaller {
		entry.Caller = getCaller()
	}
	entry.Logger.mu.Unlock()

	entry.fireHooks()

	buffer = bufferPool.Get().(*bytes.Buffer)
	buffer.Reset()
	defer bufferPool.Put(buffer)
	entry.Buffer = buffer

	entry.write()

	entry.Buffer = nil

	// To avoid Entry#log() returning a value that only would make sense for
	// panic() to use in Entry#Panic(), we avoid the allocation by checking
	// directly here.
	if level <= PanicLevel {
		panic(&entry)
	}
}

func (entry *Entry) fireHooks() {
	entry.Logger.mu.Lock()
	defer entry.Logger.mu.Unlock()
	err := entry.Logger.Hooks.Fire(entry.Level, entry)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
	}
}

func (entry *Entry) write() {
	entry.Logger.mu.Lock()
	defer entry.Logger.mu.Unlock()
	serialized, err := entry.Logger.Formatter.Format(entry)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
		return
	}
	if _, err = entry.Logger.Out.Write(serialized); err != nil {
		fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
	}
}

func (entry *Entry) Log(level Level, args ...interface{}) {
	if entry.Logger.IsLevelEnabled(level) {
		entry.log(level, fmt.Sprint(args...))
	}
}

func (entry *Entry) Trace(args ...interface{}) {
	entry.Log(TraceLevel, args...)
}

func (entry *Entry) Debug(args ...interface{}) {
	entry.Log(DebugLevel, args...)
}

func (entry *Entry) Print(args ...interface{}) {
	entry.Info(args...)
}

func (entry *Entry) Info(args ...interface{}) {
	entry.Log(InfoLevel, args...)
}

func (entry *Entry) Warn(args ...interface{}) {
	entry.Log(WarnLevel, args...)
}

func (entry *Entry) Warning(args ...interface{}) {
	entry.Warn(args...)
}

func (entry *Entry) Error(args ...interface{}) {
	entry.Log(ErrorLevel, args...)
}

func (entry *Entry) Fatal(args ...interface{}) {
	entry.Log(FatalLevel, args...)
	entry.Logger.Exit(1)
}

func (entry *Entry) Panic(args ...interface{}) {
	entry.Log(PanicLevel, args...)
	panic(fmt.Sprint(args...))
}

// Entry Printf family functions

func (entry *Entry) Logf(level Level, format string, args ...interface{}) {
	if entry.Logger.IsLevelEnabled(level) {
		entry.Log(level, fmt.Sprintf(format, args...))
	}
}

func (entry *Entry) Tracef(format string, args ...interface{}) {
	entry.Logf(TraceLevel, format, args...)
}

func (entry *Entry) Debugf(format string, args ...interface{}) {
	entry.Logf(DebugLevel, format, args...)
}

func (entry *Entry) Infof(format string, args ...interface{}) {
	entry.Logf(InfoLevel, format, args...)
}

func (entry *Entry) Printf(format string, args ...interface{}) {
	entry.Infof(format, args...)
}

func (entry *Entry) Warnf(format string, args ...interface{}) {
	entry.Logf(WarnLevel, format, args...)
}

func (entry *Entry) Warningf(format string, args ...interface{}) {
	entry.Warnf(format, args...)
}

func (entry *Entry) Errorf(format string, args ...interface{}) {
	entry.Logf(ErrorLevel, format, args...)
}

func (entry *Entry) Fatalf(format string, args ...interface{}) {
	entry.Logf(FatalLevel, format, args...)
	entry.Logger.Exit(1)
}

func (entry *Entry) Panicf(format string, args ...interface{}) {
	entry.Logf(PanicLevel, format, args...)
}

// Entry Println family functions

func (entry *Entry) Logln(level Level, args ...interface{}) {
	if entry.Logger.IsLevelEnabled(level) {
		entry.Log(level, entry.sprintlnn(args...))
	}
}

func (entry *Entry) Traceln(args ...interface{}) {
	entry.Logln(TraceLevel, args...)
}

func (entry *Entry) Debugln(args ...interface{}) {
	entry.Logln(DebugLevel, args...)
}

func (entry *Entry) Infoln(args ...interface{}) {
	entry.Logln(InfoLevel, args...)
}

func (entry *Entry) Println(args ...interface{}) {
	entry.Infoln(args...)
}

func (entry *Entry) Warnln(args ...interface{}) {
	entry.Logln(WarnLevel, args...)
}

func (entry *Entry) Warningln(args ...interface{}) {
	entry.Warnln(args...)
}

func (entry *Entry) Errorln(args ...interface{}) {
	entry.Logln(ErrorLevel, args...)
}

func (entry *Entry) Fatalln(args ...interface{}) {
	entry.Logln(FatalLevel, args...)
	entry.Logger.Exit(1)
}

func (entry *Entry) Panicln(args ...interface{}) {
	entry.Logln(PanicLevel, args...)
}

// Sprintlnn => Sprint no newline. This is to get the behavior of how
// fmt.Sprintln where spaces are always added between operands, regardless of
// their type. Instead of vendoring the Sprintln implementation to spare a
// string allocation, we do the simplest thing.
func (entry *Entry) sprintlnn(args ...interface{}) string {
	msg := fmt.Sprintln(args...)
	return msg[:len(msg)-1]
}
<commit_msg>remove errant whitespace<commit_after>package logrus

import (
	"bytes"
	"context"
	"fmt"
	"os"
	"reflect"
	"runtime"
	"strings"
	"sync"
	"time"
)

var (
	bufferPool *sync.Pool

	// qualified package name, cached at first use
	logrusPackage string

	// Positions in the call stack when tracing to report the calling method
	minimumCallerDepth int

	// Used for caller information initialisation
	callerInitOnce sync.Once
)

const (
	maximumCallerDepth int = 25
	knownLogrusFrames  int = 4
)

func init() {
	bufferPool = &sync.Pool{
		New: func() interface{} {
			return new(bytes.Buffer)
		},
	}

	// start at the bottom of the stack before the package-name cache is primed
	minimumCallerDepth = 1
}

// Defines the key when adding errors using WithError.
var ErrorKey = "error"

// An entry is the final or intermediate Logrus logging entry. It contains all
// the fields passed with WithField{,s}. It's finally logged when Trace, Debug,
// Info, Warn, Error, Fatal or Panic is called on it. These objects can be
// reused and passed around as much as you wish to avoid field duplication.
type Entry struct {
	Logger *Logger

	// Contains all the fields set by the user.
	Data Fields

	// Time at which the log entry was created
	Time time.Time

	// Level the log entry was logged at: Trace, Debug, Info, Warn, Error, Fatal or Panic
	// This field will be set on entry firing and the value will be equal to the one in Logger struct field.
	Level Level

	// Calling method, with package name
	Caller *runtime.Frame

	// Message passed to Trace, Debug, Info, Warn, Error, Fatal or Panic
	Message string

	// When formatter is called in entry.log(), a Buffer may be set to entry
	Buffer *bytes.Buffer

	// Contains the context set by the user. Useful for hook processing etc.
	Context context.Context

	// err may contain a field formatting error
	err string
}

func NewEntry(logger *Logger) *Entry {
	return &Entry{
		Logger: logger,
		// Default is three fields, plus one optional.  Give a little extra room.
		Data: make(Fields, 6),
	}
}

// Returns the bytes representation of this entry from the formatter.
func (entry *Entry) Bytes() ([]byte, error) {
	return entry.Logger.Formatter.Format(entry)
}

// Returns the string representation from the reader and ultimately the
// formatter.
func (entry *Entry) String() (string, error) {
	serialized, err := entry.Bytes()
	if err != nil {
		return "", err
	}
	str := string(serialized)
	return str, nil
}

// Add an error as single field (using the key defined in ErrorKey) to the Entry.
func (entry *Entry) WithError(err error) *Entry {
	return entry.WithField(ErrorKey, err)
}

// Add a context to the Entry.
func (entry *Entry) WithContext(ctx context.Context) *Entry {
	dataCopy := make(Fields, len(entry.Data))
	for k, v := range entry.Data {
		dataCopy[k] = v
	}
	return &Entry{Logger: entry.Logger, Data: dataCopy, Time: entry.Time, err: entry.err, Context: ctx}
}

// Add a single field to the Entry.
func (entry *Entry) WithField(key string, value interface{}) *Entry {
	return entry.WithFields(Fields{key: value})
}

// Add a map of fields to the Entry.
func (entry *Entry) WithFields(fields Fields) *Entry {
	entry.Logger.mu.Lock()
	defer entry.Logger.mu.Unlock()
	data := make(Fields, len(entry.Data)+len(fields))
	for k, v := range entry.Data {
		data[k] = v
	}
	fieldErr := entry.err
	for k, v := range fields {
		isErrField := false
		if t := reflect.TypeOf(v); t != nil {
			switch t.Kind() {
			case reflect.Func:
				isErrField = true
			case reflect.Ptr:
				isErrField = t.Elem().Kind() == reflect.Func
			}
		}
		if isErrField {
			tmp := fmt.Sprintf("can not add field %q", k)
			if fieldErr != "" {
				fieldErr = entry.err + ", " + tmp
			} else {
				fieldErr = tmp
			}
		} else {
			data[k] = v
		}
	}
	return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr, Context: entry.Context}
}

// Overrides the time of the Entry.
func (entry *Entry) WithTime(t time.Time) *Entry {
	dataCopy := make(Fields, len(entry.Data))
	for k, v := range entry.Data {
		dataCopy[k] = v
	}
	return &Entry{Logger: entry.Logger, Data: dataCopy, Time: t, err: entry.err, Context: entry.Context}
}

// getPackageName reduces a fully qualified function name to the package name
// There really ought to be to be a better way...
func getPackageName(f string) string {
	for {
		lastPeriod := strings.LastIndex(f, ".")
		lastSlash := strings.LastIndex(f, "/")
		if lastPeriod > lastSlash {
			f = f[:lastPeriod]
		} else {
			break
		}
	}

	return f
}

// getCaller retrieves the name of the first non-logrus calling function
func getCaller() *runtime.Frame {

	// cache this package's fully-qualified name
	callerInitOnce.Do(func() {
		pcs := make([]uintptr, 2)
		_ = runtime.Callers(0, pcs)
		logrusPackage = getPackageName(funcName(pcs))

		// now that we have the cache, we can skip a minimum count of known-logrus functions
		// XXX this is dubious, the number of frames may vary
		minimumCallerDepth = knownLogrusFrames
	})

	// Restrict the lookback frames to avoid runaway lookups
	pcs := make([]uintptr, maximumCallerDepth)
	depth := runtime.Callers(minimumCallerDepth, pcs)
	frames := runtime.CallersFrames(pcs[:depth])

	for f, again := frames.Next(); again; f, again = frames.Next() {
		pkg := getPackageName(f.Function)

		// If the caller isn't part of this package, we're done
		if pkg != logrusPackage {
			return &f //nolint:scopelint
		}
	}

	// if we got here, we failed to find the caller's context
	return nil
}

func (entry Entry) HasCaller() (has bool) {
	return entry.Logger != nil &&
		entry.Logger.ReportCaller &&
		entry.Caller != nil
}

// This function is not declared with a pointer value because otherwise
// race conditions will occur when using multiple goroutines
func (entry Entry) log(level Level, msg string) {
	var buffer *bytes.Buffer

	// Default to now, but allow users to override if they want.
	//
	// We don't have to worry about polluting future calls to Entry#log()
	// with this assignment because this function is declared with a
	// non-pointer receiver.
	if entry.Time.IsZero() {
		entry.Time = time.Now()
	}

	entry.Level = level
	entry.Message = msg
	entry.Logger.mu.Lock()
	if entry.Logger.ReportCaller {
		entry.Caller = getCaller()
	}
	entry.Logger.mu.Unlock()

	entry.fireHooks()

	buffer = bufferPool.Get().(*bytes.Buffer)
	buffer.Reset()
	defer bufferPool.Put(buffer)
	entry.Buffer = buffer

	entry.write()

	entry.Buffer = nil

	// To avoid Entry#log() returning a value that only would make sense for
	// panic() to use in Entry#Panic(), we avoid the allocation by checking
	// directly here.
	if level <= PanicLevel {
		panic(&entry)
	}
}

func (entry *Entry) fireHooks() {
	entry.Logger.mu.Lock()
	defer entry.Logger.mu.Unlock()
	err := entry.Logger.Hooks.Fire(entry.Level, entry)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
	}
}

func (entry *Entry) write() {
	entry.Logger.mu.Lock()
	defer entry.Logger.mu.Unlock()
	serialized, err := entry.Logger.Formatter.Format(entry)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
		return
	}
	if _, err = entry.Logger.Out.Write(serialized); err != nil {
		fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
	}
}

func (entry *Entry) Log(level Level, args ...interface{}) {
	if entry.Logger.IsLevelEnabled(level) {
		entry.log(level, fmt.Sprint(args...))
	}
}

func (entry *Entry) Trace(args ...interface{}) {
	entry.Log(TraceLevel, args...)
}

func (entry *Entry) Debug(args ...interface{}) {
	entry.Log(DebugLevel, args...)
}

func (entry *Entry) Print(args ...interface{}) {
	entry.Info(args...)
}

func (entry *Entry) Info(args ...interface{}) {
	entry.Log(InfoLevel, args...)
}

func (entry *Entry) Warn(args ...interface{}) {
	entry.Log(WarnLevel, args...)
}

func (entry *Entry) Warning(args ...interface{}) {
	entry.Warn(args...)
}

func (entry *Entry) Error(args ...interface{}) {
	entry.Log(ErrorLevel, args...)
}

func (entry *Entry) Fatal(args ...interface{}) {
	entry.Log(FatalLevel, args...)
	entry.Logger.Exit(1)
}

func (entry *Entry) Panic(args ...interface{}) {
	entry.Log(PanicLevel, args...)
	panic(fmt.Sprint(args...))
}

// Entry Printf family functions

func (entry *Entry) Logf(level Level, format string, args ...interface{}) {
	if entry.Logger.IsLevelEnabled(level) {
		entry.Log(level, fmt.Sprintf(format, args...))
	}
}

func (entry *Entry) Tracef(format string, args ...interface{}) {
	entry.Logf(TraceLevel, format, args...)
}

func (entry *Entry) Debugf(format string, args ...interface{}) {
	entry.Logf(DebugLevel, format, args...)
}

func (entry *Entry) Infof(format string, args ...interface{}) {
	entry.Logf(InfoLevel, format, args...)
}

func (entry *Entry) Printf(format string, args ...interface{}) {
	entry.Infof(format, args...)
}

func (entry *Entry) Warnf(format string, args ...interface{}) {
	entry.Logf(WarnLevel, format, args...)
}

func (entry *Entry) Warningf(format string, args ...interface{}) {
	entry.Warnf(format, args...)
}

func (entry *Entry) Errorf(format string, args ...interface{}) {
	entry.Logf(ErrorLevel, format, args...)
}

func (entry *Entry) Fatalf(format string, args ...interface{}) {
	entry.Logf(FatalLevel, format, args...)
	entry.Logger.Exit(1)
}

func (entry *Entry) Panicf(format string, args ...interface{}) {
	entry.Logf(PanicLevel, format, args...)
}

// Entry Println family functions

func (entry *Entry) Logln(level Level, args ...interface{}) {
	if entry.Logger.IsLevelEnabled(level) {
		entry.Log(level, entry.sprintlnn(args...))
	}
}

func (entry *Entry) Traceln(args ...interface{}) {
	entry.Logln(TraceLevel, args...)
}

func (entry *Entry) Debugln(args ...interface{}) {
	entry.Logln(DebugLevel, args...)
}

func (entry *Entry) Infoln(args ...interface{}) {
	entry.Logln(InfoLevel, args...)
}

func (entry *Entry) Println(args ...interface{}) {
	entry.Infoln(args...)
}

func (entry *Entry) Warnln(args ...interface{}) {
	entry.Logln(WarnLevel, args...)
}

func (entry *Entry) Warningln(args ...interface{}) {
	entry.Warnln(args...)
}

func (entry *Entry) Errorln(args ...interface{}) {
	entry.Logln(ErrorLevel, args...)
}

func (entry *Entry) Fatalln(args ...interface{}) {
	entry.Logln(FatalLevel, args...)
	entry.Logger.Exit(1)
}

func (entry *Entry) Panicln(args ...interface{}) {
	entry.Logln(PanicLevel, args...)
}

// Sprintlnn => Sprint no newline. This is to get the behavior of how
// fmt.Sprintln where spaces are always added between operands, regardless of
// their type. Instead of vendoring the Sprintln implementation to spare a
// string allocation, we do the simplest thing.
func (entry *Entry) sprintlnn(args ...interface{}) string {
	msg := fmt.Sprintln(args...)
	return msg[:len(msg)-1]
}
<|endoftext|>


<commit_before>package jsonapi

import (
	"fmt"
	"net/http"
	"sync"
)

var singleErrorDocumentPool = sync.Pool{
	New: func() interface{} {
		return &Document{
			Errors: make([]*Error, 1),
		}
	},
}

var multiErrorDocumentPool = sync.Pool{
	New: func() interface{} {
		return &Document{}
	},
}

// Error objects provide additional information about problems encountered while
// performing an operation.
//
// See: http://jsonapi.org/format/#errors.
type Error struct {
	// A unique identifier for this particular occurrence of the problem.
	ID string `json:"id,omitempty"`

	// Continuing links to other resources.
	Links *ErrorLinks `json:"links,omitempty"`

	// The HTTP status code applicable to this problem.
	Status int `json:"status,string,omitempty"`

	// An application-specific error code.
	Code string `json:"code,omitempty"`

	// A short, human-readable summary of the problem.
	Title string `json:"title,omitempty"`

	// A human-readable explanation specific to this occurrence of the problem.
	Detail string `json:"detail,omitempty"`

	// A parameter or pointer reference to the source of the error.
	Source *ErrorSource `json:"source,omitempty"`

	// Non-standard meta-information about the error.
	Meta Map `json:"meta,omitempty"`
}

// ErrorLinks contains continuing links to other resources.
//
// See: http://jsonapi.org/format/#errors.
type ErrorLinks struct {
	// A link that leads to further details about this particular occurrence of
	// the problem.
	About string `json:"about"`
}

// ErrorSource contains a parameter or pointer reference to the source of the
// error.
//
// See: http://jsonapi.org/format/#errors.
type ErrorSource struct {
	// A string indicating which URI query parameter caused the error.
	Parameter string `json:"parameter,omitempty"`

	// A JSON Pointer to the associated entity in the request document.
	Pointer string `json:"pointer,omitempty"`
}

// Error returns a string representation of the error for logging purposes.
func (e *Error) Error() string {
	return fmt.Sprintf("%s: %s", e.Title, e.Detail)
}

// WriteError will write the passed error to the response writer.
//
// Note: If the supplied error is not an Error a new InternalServerError is used
// instead. Does the passed Error have an invalid or zero status code it will be
// corrected to the Internal Server Error status code.
func WriteError(w http.ResponseWriter, err error) error {
	anError, ok := err.(*Error)
	if !ok {
		anError = InternalServerError("")
	}

	// set status
	if str := http.StatusText(anError.Status); str == "" {
		anError.Status = http.StatusInternalServerError
	}

	// get document from pool
	doc := singleErrorDocumentPool.Get().(*Document)

	// put document back when finished
	defer func() {
		doc.Errors[0] = nil
		singleErrorDocumentPool.Put(doc)
	}()

	// set error
	doc.Errors[0] = anError

	return WriteResponse(w, anError.Status, doc)
}

// WriteErrorList will write the passed errors to the the response writer.
// The function will calculate a common status code for all the errors.
//
// Does a passed Error have an invalid or zero status code it will be corrected
// to the Internal Server Error status code.
func WriteErrorList(w http.ResponseWriter, errors ...*Error) error {
	// write internal server error if no errors are passed
	if len(errors) == 0 {
		return WriteError(w, nil)
	}

	// prepare common status
	commonStatus := 0

	for i, err := range errors {
		// check for zero and invalid status
		if str := http.StatusText(err.Status); str == "" {
			err.Status = http.StatusInternalServerError
		}

		// take the first status directly
		if i == 0 {
			commonStatus = err.Status
			continue
		}

		// check if the same or already 500
		if commonStatus == err.Status || commonStatus == 500 {
			continue
		}

		// settle on 500 if already in 500er range
		if err.Status >= 500 {
			commonStatus = 500
			continue
		}

		// settle on 400 if in 400er range
		commonStatus = 400
	}

	// get document from pool
	doc := multiErrorDocumentPool.Get().(*Document)

	// put document back when finished
	defer func() {
		doc.Errors = nil
		multiErrorDocumentPool.Put(doc)
	}()

	// set errors
	doc.Errors = errors

	return WriteResponse(w, commonStatus, doc)
}

// ErrorFromStatus will return an error that has been derived from the passed
// status code.
//
// Note: If the passed status code is not a valid HTTP status code, an Internal
// Server Error status code will be used instead.
func ErrorFromStatus(status int, detail string) *Error {
	// get text
	str := http.StatusText(status)

	// check text
	if str == "" {
		status = http.StatusInternalServerError
		str = http.StatusText(http.StatusInternalServerError)
	}

	return &Error{
		Status: status,
		Title:  str,
		Detail: detail,
	}
}

// NotFound returns a new not found error.
func NotFound(detail string) *Error {
	return ErrorFromStatus(http.StatusNotFound, detail)
}

// BadRequest returns a new bad request error.
func BadRequest(detail string) *Error {
	return ErrorFromStatus(http.StatusBadRequest, detail)
}

// BadRequestParam returns a new bad request error with a parameter source.
func BadRequestParam(detail, param string) *Error {
	err := ErrorFromStatus(http.StatusBadRequest, detail)
	err.Source = &ErrorSource{
		Parameter: param,
	}

	return err
}

// BadRequestPointer returns a new bad request error with a pointer source.
func BadRequestPointer(detail, pointer string) *Error {
	err := ErrorFromStatus(http.StatusBadRequest, detail)
	err.Source = &ErrorSource{
		Parameter: pointer,
	}

	return err
}

// InternalServerError returns na new internal server error.
func InternalServerError(detail string) *Error {
	return ErrorFromStatus(http.StatusInternalServerError, detail)
}
<commit_msg>fixed bug<commit_after>package jsonapi

import (
	"fmt"
	"net/http"
	"sync"
)

var singleErrorDocumentPool = sync.Pool{
	New: func() interface{} {
		return &Document{
			Errors: make([]*Error, 1),
		}
	},
}

var multiErrorDocumentPool = sync.Pool{
	New: func() interface{} {
		return &Document{}
	},
}

// Error objects provide additional information about problems encountered while
// performing an operation.
//
// See: http://jsonapi.org/format/#errors.
type Error struct {
	// A unique identifier for this particular occurrence of the problem.
	ID string `json:"id,omitempty"`

	// Continuing links to other resources.
	Links *ErrorLinks `json:"links,omitempty"`

	// The HTTP status code applicable to this problem.
	Status int `json:"status,string,omitempty"`

	// An application-specific error code.
	Code string `json:"code,omitempty"`

	// A short, human-readable summary of the problem.
	Title string `json:"title,omitempty"`

	// A human-readable explanation specific to this occurrence of the problem.
	Detail string `json:"detail,omitempty"`

	// A parameter or pointer reference to the source of the error.
	Source *ErrorSource `json:"source,omitempty"`

	// Non-standard meta-information about the error.
	Meta Map `json:"meta,omitempty"`
}

// ErrorLinks contains continuing links to other resources.
//
// See: http://jsonapi.org/format/#errors.
type ErrorLinks struct {
	// A link that leads to further details about this particular occurrence of
	// the problem.
	About string `json:"about"`
}

// ErrorSource contains a parameter or pointer reference to the source of the
// error.
//
// See: http://jsonapi.org/format/#errors.
type ErrorSource struct {
	// A string indicating which URI query parameter caused the error.
	Parameter string `json:"parameter,omitempty"`

	// A JSON Pointer to the associated entity in the request document.
	Pointer string `json:"pointer,omitempty"`
}

// Error returns a string representation of the error for logging purposes.
func (e *Error) Error() string {
	return fmt.Sprintf("%s: %s", e.Title, e.Detail)
}

// WriteError will write the passed error to the response writer.
//
// Note: If the supplied error is not an Error a new InternalServerError is used
// instead. Does the passed Error have an invalid or zero status code it will be
// corrected to the Internal Server Error status code.
func WriteError(w http.ResponseWriter, err error) error {
	anError, ok := err.(*Error)
	if !ok {
		anError = InternalServerError("")
	}

	// set status
	if str := http.StatusText(anError.Status); str == "" {
		anError.Status = http.StatusInternalServerError
	}

	// get document from pool
	doc := singleErrorDocumentPool.Get().(*Document)

	// put document back when finished
	defer func() {
		doc.Errors[0] = nil
		singleErrorDocumentPool.Put(doc)
	}()

	// set error
	doc.Errors[0] = anError

	return WriteResponse(w, anError.Status, doc)
}

// WriteErrorList will write the passed errors to the the response writer.
// The function will calculate a common status code for all the errors.
//
// Does a passed Error have an invalid or zero status code it will be corrected
// to the Internal Server Error status code.
func WriteErrorList(w http.ResponseWriter, errors ...*Error) error {
	// write internal server error if no errors are passed
	if len(errors) == 0 {
		return WriteError(w, nil)
	}

	// prepare common status
	commonStatus := 0

	for i, err := range errors {
		// check for zero and invalid status
		if str := http.StatusText(err.Status); str == "" {
			err.Status = http.StatusInternalServerError
		}

		// take the first status directly
		if i == 0 {
			commonStatus = err.Status
			continue
		}

		// check if the same or already 500
		if commonStatus == err.Status || commonStatus == 500 {
			continue
		}

		// settle on 500 if already in 500er range
		if err.Status >= 500 {
			commonStatus = 500
			continue
		}

		// settle on 400 if in 400er range
		commonStatus = 400
	}

	// get document from pool
	doc := multiErrorDocumentPool.Get().(*Document)

	// put document back when finished
	defer func() {
		doc.Errors = nil
		multiErrorDocumentPool.Put(doc)
	}()

	// set errors
	doc.Errors = errors

	return WriteResponse(w, commonStatus, doc)
}

// ErrorFromStatus will return an error that has been derived from the passed
// status code.
//
// Note: If the passed status code is not a valid HTTP status code, an Internal
// Server Error status code will be used instead.
func ErrorFromStatus(status int, detail string) *Error {
	// get text
	str := http.StatusText(status)

	// check text
	if str == "" {
		status = http.StatusInternalServerError
		str = http.StatusText(http.StatusInternalServerError)
	}

	return &Error{
		Status: status,
		Title:  str,
		Detail: detail,
	}
}

// NotFound returns a new not found error.
func NotFound(detail string) *Error {
	return ErrorFromStatus(http.StatusNotFound, detail)
}

// BadRequest returns a new bad request error.
func BadRequest(detail string) *Error {
	return ErrorFromStatus(http.StatusBadRequest, detail)
}

// BadRequestParam returns a new bad request error with a parameter source.
func BadRequestParam(detail, param string) *Error {
	err := ErrorFromStatus(http.StatusBadRequest, detail)
	err.Source = &ErrorSource{
		Parameter: param,
	}

	return err
}

// BadRequestPointer returns a new bad request error with a pointer source.
func BadRequestPointer(detail, pointer string) *Error {
	err := ErrorFromStatus(http.StatusBadRequest, detail)
	err.Source = &ErrorSource{
		Pointer: pointer,
	}

	return err
}

// InternalServerError returns na new internal server error.
func InternalServerError(detail string) *Error {
	return ErrorFromStatus(http.StatusInternalServerError, detail)
}
<|endoftext|>


<commit_before>// Package etree provides XML services through an Element Tree
// abstraction.
package etree

import (
	"bufio"
	"encoding/xml"
	"errors"
	"io"
)

const (
	NoIndent = -1
)

var (
	ErrInvalidFormat = errors.New("etree: invalid XML format")
)

// A Token is an empty interface that represents an Element,
// Comment, CharData, Directive or ProcInst.
type Token interface {
	writeTo(w *bufio.Writer)
}

// A Document is the root level object in an etree.  It represents the
// XML document as a whole.  It embeds an Element type but only uses the
// Element type's Child tokens.
type Document struct {
	Element
}

// An Element represents an XML element, its attributes, and its child tokens.
type Element struct {
	Tag   string    // The element tag
	Attr  []Attr    // The element's list of key-value attribute pairs
	Child []Token   // The element's child tokens (elements, comments, etc.)
	text  *CharData // The char data token containing the element's text
}

// An Attr represents a key-value attribute of an XML element.
type Attr struct {
	Key   string
	Value string
}

// A Comment represents an XML comment.
type Comment struct {
	Data string
}

// CharData represents character data within XML.
type CharData struct {
	Data       string
	whitespace bool
}

// A ProcInst represents an XML processing instruction.
type ProcInst struct {
	Target string
	Inst   string
}

// NewDocument creates an empty XML document and returns it.
func NewDocument() *Document {
	d := new(Document)
	d.Child = make([]Token, 0)
	return d
}

// ReadFrom reads XML from the reader r and adds the result as
// a new child of the receiving document.
func (d *Document) ReadFrom(r io.Reader) error {
	return d.Element.ReadFrom(r)
}

// WriteTo serializes an XML document into the writer w.
func (d *Document) WriteTo(w io.Writer) error {
	b := bufio.NewWriter(w)
	for _, c := range d.Child {
		c.writeTo(b)
	}
	return b.Flush()
}

// Indent modifies the document's element tree by inserting
// CharData entities containing carriage returns and indentation.
// The amount of indenting per depth level is equal to spaces.
// Use etree.NoIndent for spaces if you want no indentation at all.
func (d *Document) Indent(spaces int) {
	d.stripIndent()
	n := len(d.Child)
	if n == 0 {
		return
	}

	newChild := make([]Token, n*2-1)
	for i, c := range d.Child {
		j := i * 2
		newChild[j] = c
		if j+1 < len(newChild) {
			newChild[j+1] = newIndentCharData(0, spaces)
		}
		if e, ok := c.(*Element); ok {
			e.indent(1, spaces)
		}
	}
	d.Child = newChild
}

// NewElement creates an XML element with the specified name.
// In most cases, you should use NewDocument and create elements
// with the CreateElement function.
func NewElement(tag string) *Element {
	return &Element{
		Tag:   tag,
		Attr:  make([]Attr, 0),
		Child: make([]Token, 0),
	}
}

// Text returns the characters immediately following the element's
// opening tag.
func (e *Element) Text() string {
	if e.text == nil {
		return ""
	} else {
		return e.text.Data
	}
}

// SetText replaces an element's subsidiary CharData text with a new
// string.
func (e *Element) SetText(text string) {
	if e.text == nil {
		e.Child = append(e.Child, nil)
		copy(e.Child[1:], e.Child[0:])
		c := newCharData(text, false)
		e.Child[0] = c
		e.text = c
	} else {
		e.text.Data = text
	}
}

// CreateElement creates a child element of the receiving element and
// gives it the specified name.
func (e *Element) CreateElement(name string) *Element {
	c := NewElement(name)
	e.addChild(c)
	return c
}

// An element stack is a simple stack of elements used by ReadFrom.
type elementStack []*Element

func (s *elementStack) push(e *Element) {
	*s = append(*s, e)
}

func (s *elementStack) pop() {
	(*s)[len(*s)-1] = nil
	*s = (*s)[:len(*s)-1]
}

func (s *elementStack) peek() *Element {
	return (*s)[len(*s)-1]
}

// ReadFrom reads XML from the reader r and stores the result as
// a new child of the receiving element.
func (e *Element) ReadFrom(r io.Reader) error {
	stack := elementStack{e}
	var textPtr **CharData
	dec := xml.NewDecoder(r)
	for {
		t, err := dec.RawToken()
		switch {
		case err == io.EOF:
			return nil
		case err != nil:
			return err
		case len(stack) == 0:
			return ErrInvalidFormat
		}

		top := stack.peek()

		switch t := t.(type) {
		case xml.StartElement:
			e := top.CreateElement(t.Name.Local)
			for _, a := range t.Attr {
				e.CreateAttr(a.Name.Local, a.Value)
			}
			stack.push(e)
			textPtr = &e.text
		case xml.EndElement:
			stack.pop()
			textPtr = nil
		case xml.CharData:
			data := string(t)
			cd := top.createCharData(data, isWhitespace(data))
			if textPtr != nil {
				*textPtr = cd
			}
		case xml.Comment:
			top.CreateComment(string(t))
		case xml.ProcInst:
			top.CreateProcInst(t.Target, string(t.Inst))
		}
	}
}

// WriteTo serializes the element and its children as XML into
// the writer w.
func (e *Element) WriteTo(w io.Writer) error {
	b := bufio.NewWriter(w)
	e.writeTo(b)
	return b.Flush()
}

// ChildElements returns all elements that are children of the
// receiving element.
func (e *Element) ChildElements() []*Element {
	elements := make([]*Element, 0)
	for _, c := range e.Child {
		if e, ok := c.(*Element); ok {
			elements = append(elements, e)
		}
	}
	return elements
}

// Indent modifies the element's element tree by inserting
// CharData entities containing carriage returns and indentation.
// The amount of indenting per depth level is equal to spaces.
// Use etree.NoIndent for spaces if you want no indentation at all.
func (e *Element) Indent(spaces int) {
	e.indent(1, spaces)
}

// indent recursively inserts proper indentation between an
// XML element's child tokens.
func (e *Element) indent(depth, spaces int) {
	e.stripIndent()
	n := len(e.Child)
	if n == 0 {
		return
	}

	oldChild := e.Child
	e.Child = make([]Token, 0, n*2+1)
	isCharData := false
	for _, c := range oldChild {
		_, isCharData = c.(*CharData)
		if !isCharData && spaces >= 0 {
			e.addChild(newIndentCharData(depth, spaces))
		}
		e.addChild(c)
		if ce, ok := c.(*Element); ok {
			ce.indent(depth+1, spaces)
		}
	}
	if !isCharData && spaces >= 0 {
		e.addChild(newIndentCharData(depth-1, spaces))
	}
}

// stripIndent removes any previously inserted indentation.
func (e *Element) stripIndent() {
	// Count the number of non-indent child tokens
	n := len(e.Child)
	for _, c := range e.Child {
		if cd, ok := c.(*CharData); ok && cd.whitespace {
			n--
		}
	}
	if n == len(e.Child) {
		return
	}

	// Strip out indent CharData
	newChild := make([]Token, n)
	j := 0
	for _, c := range e.Child {
		if cd, ok := c.(*CharData); ok && cd.whitespace {
			continue
		}
		newChild[j] = c
		j++
	}
	e.Child = newChild
}

// writeTo serializes the element to the writer w.
func (e *Element) writeTo(w *bufio.Writer) {
	w.WriteByte('<')
	w.WriteString(e.Tag)
	for _, a := range e.Attr {
		w.WriteByte(' ')
		a.writeTo(w)
	}
	if len(e.Child) > 0 {
		w.WriteString(">")
		for _, c := range e.Child {
			c.writeTo(w)
		}
		w.Write([]byte{'<', '/'})
		w.WriteString(e.Tag)
		w.WriteByte('>')
	} else {
		w.Write([]byte{'/', '>'})
	}
}

// addChild adds a child token to the receiving element.
func (e *Element) addChild(t Token) {
	e.Child = append(e.Child, t)
}

// CreateAttr creates an attribute and adds it to the receiving element.
func (e *Element) CreateAttr(key, value string) Attr {
	a := Attr{key, value}
	e.Attr = append(e.Attr, a)
	return a
}

// writeTo serializes the attribute to the writer.
func (a *Attr) writeTo(w *bufio.Writer) {
	w.WriteString(a.Key)
	w.WriteString(`="`)
	w.WriteString(a.Value)
	w.WriteByte('"')
}

// newCharData creates an XML character data entity.
func newCharData(data string, whitespace bool) *CharData {
	return &CharData{Data: data, whitespace: whitespace}
}

// CreateCharData creates an XML character data entity and adds it
// as a child of the receiving element.
func (e *Element) CreateCharData(data string) *CharData {
	return e.createCharData(data, false)
}

// CreateCharData creates an XML character data entity and adds it
// as a child of the receiving element.
func (e *Element) createCharData(data string, whitespace bool) *CharData {
	c := newCharData(data, whitespace)
	e.addChild(c)
	if !whitespace {
		e.text = c
	}
	return c
}

// writeTo serializes the character data entity to the writer.
func (c *CharData) writeTo(w *bufio.Writer) {
	w.WriteString(escape(c.Data))
}

// NewComment creates an XML comment.
func newComment(comment string) *Comment {
	return &Comment{Data: comment}
}

// CreateComment creates an XML comment and adds it as a child of the
// receiving element.
func (e *Element) CreateComment(comment string) *Comment {
	c := newComment(comment)
	e.addChild(c)
	return c
}

// writeTo serialies the comment to the writer.
func (c *Comment) writeTo(w *bufio.Writer) {
	w.WriteString("<!--")
	w.WriteString(c.Data)
	w.WriteString("-->")
}

// newProcInst creates a new processing instruction.
func newProcInst(target, inst string) *ProcInst {
	return &ProcInst{Target: target, Inst: inst}
}

// CreateProcInst creates a processing instruction and adds it as a
// child of the receiving element
func (e *Element) CreateProcInst(target, inst string) *ProcInst {
	p := newProcInst(target, inst)
	e.addChild(p)
	return p
}

// writeTo serializes the processing instruction to the writer.
func (p *ProcInst) writeTo(w *bufio.Writer) {
	w.WriteString("<?")
	w.WriteString(p.Target)
	w.WriteByte(' ')
	w.WriteString(p.Inst)
	w.WriteString("?>")
}

// newIndentCharData returns the indentation CharData token for the given
// depth level with the given number of spaces per level.
func newIndentCharData(depth, spaces int) *CharData {
	return newCharData(crSpaces(depth*spaces), true)
}
<commit_msg>Simplify processing of element child text.<commit_after>// Package etree provides XML services through an Element Tree
// abstraction.
package etree

import (
	"bufio"
	"encoding/xml"
	"errors"
	"io"
)

const (
	NoIndent = -1
)

var (
	ErrInvalidFormat = errors.New("etree: invalid XML format")
)

// A Token is an empty interface that represents an Element,
// Comment, CharData, Directive or ProcInst.
type Token interface {
	writeTo(w *bufio.Writer)
}

// A Document is the root level object in an etree.  It represents the
// XML document as a whole.  It embeds an Element type but only uses the
// Element type's Child tokens.
type Document struct {
	Element
}

// An Element represents an XML element, its attributes, and its child tokens.
type Element struct {
	Tag   string  // The element tag
	Attr  []Attr  // The element's list of key-value attribute pairs
	Child []Token // The element's child tokens (elements, comments, etc.)
}

// An Attr represents a key-value attribute of an XML element.
type Attr struct {
	Key   string
	Value string
}

// A Comment represents an XML comment.
type Comment struct {
	Data string
}

// CharData represents character data within XML.
type CharData struct {
	Data       string
	whitespace bool
}

// A ProcInst represents an XML processing instruction.
type ProcInst struct {
	Target string
	Inst   string
}

// NewDocument creates an empty XML document and returns it.
func NewDocument() *Document {
	d := new(Document)
	d.Child = make([]Token, 0)
	return d
}

// ReadFrom reads XML from the reader r and adds the result as
// a new child of the receiving document.
func (d *Document) ReadFrom(r io.Reader) error {
	return d.Element.ReadFrom(r)
}

// WriteTo serializes an XML document into the writer w.
func (d *Document) WriteTo(w io.Writer) error {
	b := bufio.NewWriter(w)
	for _, c := range d.Child {
		c.writeTo(b)
	}
	return b.Flush()
}

// Indent modifies the document's element tree by inserting
// CharData entities containing carriage returns and indentation.
// The amount of indenting per depth level is equal to spaces.
// Use etree.NoIndent for spaces if you want no indentation at all.
func (d *Document) Indent(spaces int) {
	d.stripIndent()
	n := len(d.Child)
	if n == 0 {
		return
	}

	newChild := make([]Token, n*2-1)
	for i, c := range d.Child {
		j := i * 2
		newChild[j] = c
		if j+1 < len(newChild) {
			newChild[j+1] = newIndentCharData(0, spaces)
		}
		if e, ok := c.(*Element); ok {
			e.indent(1, spaces)
		}
	}
	d.Child = newChild
}

// NewElement creates an XML element with the specified name.
// In most cases, you should use NewDocument and create elements
// with the CreateElement function.
func NewElement(tag string) *Element {
	return &Element{
		Tag:   tag,
		Attr:  make([]Attr, 0),
		Child: make([]Token, 0),
	}
}

// Text returns the characters immediately following the element's
// opening tag.
func (e *Element) Text() string {
	if len(e.Child) == 0 {
		return ""
	}
	if cd, ok := e.Child[0].(*CharData); ok {
		return cd.Data
	} else {
		return ""
	}
}

// SetText replaces an element's subsidiary CharData text with a new
// string.
func (e *Element) SetText(text string) {
	if len(e.Child) > 0 {
		if cd, ok := e.Child[0].(*CharData); ok {
			cd.Data = text
			return
		}
	}
	e.Child = append(e.Child, nil)
	copy(e.Child[1:], e.Child[0:])
	c := newCharData(text, false)
	e.Child[0] = c
}

// CreateElement creates a child element of the receiving element and
// gives it the specified name.
func (e *Element) CreateElement(name string) *Element {
	c := NewElement(name)
	e.addChild(c)
	return c
}

// An element stack is a simple stack of elements used by ReadFrom.
type elementStack []*Element

func (s *elementStack) push(e *Element) {
	*s = append(*s, e)
}

func (s *elementStack) pop() {
	(*s)[len(*s)-1] = nil
	*s = (*s)[:len(*s)-1]
}

func (s *elementStack) peek() *Element {
	return (*s)[len(*s)-1]
}

// ReadFrom reads XML from the reader r and stores the result as
// a new child of the receiving element.
func (e *Element) ReadFrom(r io.Reader) error {
	stack := elementStack{e}
	dec := xml.NewDecoder(r)
	for {
		t, err := dec.RawToken()
		switch {
		case err == io.EOF:
			return nil
		case err != nil:
			return err
		case len(stack) == 0:
			return ErrInvalidFormat
		}

		top := stack.peek()

		switch t := t.(type) {
		case xml.StartElement:
			e := top.CreateElement(t.Name.Local)
			for _, a := range t.Attr {
				e.CreateAttr(a.Name.Local, a.Value)
			}
			stack.push(e)
		case xml.EndElement:
			stack.pop()
		case xml.CharData:
			data := string(t)
			top.createCharData(data, isWhitespace(data))
		case xml.Comment:
			top.CreateComment(string(t))
		case xml.ProcInst:
			top.CreateProcInst(t.Target, string(t.Inst))
		}
	}
}

// WriteTo serializes the element and its children as XML into
// the writer w.
func (e *Element) WriteTo(w io.Writer) error {
	b := bufio.NewWriter(w)
	e.writeTo(b)
	return b.Flush()
}

// ChildElements returns all elements that are children of the
// receiving element.
func (e *Element) ChildElements() []*Element {
	elements := make([]*Element, 0)
	for _, c := range e.Child {
		if e, ok := c.(*Element); ok {
			elements = append(elements, e)
		}
	}
	return elements
}

// Indent modifies the element's element tree by inserting
// CharData entities containing carriage returns and indentation.
// The amount of indenting per depth level is equal to spaces.
// Use etree.NoIndent for spaces if you want no indentation at all.
func (e *Element) Indent(spaces int) {
	e.indent(1, spaces)
}

// indent recursively inserts proper indentation between an
// XML element's child tokens.
func (e *Element) indent(depth, spaces int) {
	e.stripIndent()
	n := len(e.Child)
	if n == 0 {
		return
	}

	oldChild := e.Child
	e.Child = make([]Token, 0, n*2+1)
	isCharData := false
	for _, c := range oldChild {
		_, isCharData = c.(*CharData)
		if !isCharData && spaces >= 0 {
			e.addChild(newIndentCharData(depth, spaces))
		}
		e.addChild(c)
		if ce, ok := c.(*Element); ok {
			ce.indent(depth+1, spaces)
		}
	}
	if !isCharData && spaces >= 0 {
		e.addChild(newIndentCharData(depth-1, spaces))
	}
}

// stripIndent removes any previously inserted indentation.
func (e *Element) stripIndent() {
	// Count the number of non-indent child tokens
	n := len(e.Child)
	for _, c := range e.Child {
		if cd, ok := c.(*CharData); ok && cd.whitespace {
			n--
		}
	}
	if n == len(e.Child) {
		return
	}

	// Strip out indent CharData
	newChild := make([]Token, n)
	j := 0
	for _, c := range e.Child {
		if cd, ok := c.(*CharData); ok && cd.whitespace {
			continue
		}
		newChild[j] = c
		j++
	}
	e.Child = newChild
}

// writeTo serializes the element to the writer w.
func (e *Element) writeTo(w *bufio.Writer) {
	w.WriteByte('<')
	w.WriteString(e.Tag)
	for _, a := range e.Attr {
		w.WriteByte(' ')
		a.writeTo(w)
	}
	if len(e.Child) > 0 {
		w.WriteString(">")
		for _, c := range e.Child {
			c.writeTo(w)
		}
		w.Write([]byte{'<', '/'})
		w.WriteString(e.Tag)
		w.WriteByte('>')
	} else {
		w.Write([]byte{'/', '>'})
	}
}

// addChild adds a child token to the receiving element.
func (e *Element) addChild(t Token) {
	e.Child = append(e.Child, t)
}

// CreateAttr creates an attribute and adds it to the receiving element.
func (e *Element) CreateAttr(key, value string) Attr {
	a := Attr{key, value}
	e.Attr = append(e.Attr, a)
	return a
}

// writeTo serializes the attribute to the writer.
func (a *Attr) writeTo(w *bufio.Writer) {
	w.WriteString(a.Key)
	w.WriteString(`="`)
	w.WriteString(a.Value)
	w.WriteByte('"')
}

// newCharData creates an XML character data entity.
func newCharData(data string, whitespace bool) *CharData {
	return &CharData{Data: data, whitespace: whitespace}
}

// CreateCharData creates an XML character data entity and adds it
// as a child of the receiving element.
func (e *Element) CreateCharData(data string) *CharData {
	return e.createCharData(data, false)
}

// CreateCharData creates an XML character data entity and adds it
// as a child of the receiving element.
func (e *Element) createCharData(data string, whitespace bool) *CharData {
	c := newCharData(data, whitespace)
	e.addChild(c)
	return c
}

// writeTo serializes the character data entity to the writer.
func (c *CharData) writeTo(w *bufio.Writer) {
	w.WriteString(escape(c.Data))
}

// NewComment creates an XML comment.
func newComment(comment string) *Comment {
	return &Comment{Data: comment}
}

// CreateComment creates an XML comment and adds it as a child of the
// receiving element.
func (e *Element) CreateComment(comment string) *Comment {
	c := newComment(comment)
	e.addChild(c)
	return c
}

// writeTo serialies the comment to the writer.
func (c *Comment) writeTo(w *bufio.Writer) {
	w.WriteString("<!--")
	w.WriteString(c.Data)
	w.WriteString("-->")
}

// newProcInst creates a new processing instruction.
func newProcInst(target, inst string) *ProcInst {
	return &ProcInst{Target: target, Inst: inst}
}

// CreateProcInst creates a processing instruction and adds it as a
// child of the receiving element
func (e *Element) CreateProcInst(target, inst string) *ProcInst {
	p := newProcInst(target, inst)
	e.addChild(p)
	return p
}

// writeTo serializes the processing instruction to the writer.
func (p *ProcInst) writeTo(w *bufio.Writer) {
	w.WriteString("<?")
	w.WriteString(p.Target)
	w.WriteByte(' ')
	w.WriteString(p.Inst)
	w.WriteString("?>")
}

// newIndentCharData returns the indentation CharData token for the given
// depth level with the given number of spaces per level.
func newIndentCharData(depth, spaces int) *CharData {
	return newCharData(crSpaces(depth*spaces), true)
}
<|endoftext|>


<commit_before>// Package fatherhood offers a fast JSON decoder with a terrible API.
package fatherhood

import (
	"errors"
	"fmt"
	"github.com/benbjohnson/megajson/scanner"
	"io"
)

// Some docs shamelessly stolen from encoding/json

// Decoder reads and decodes JSON objects from an input stream.
type Decoder struct {
	scan scanner.Scanner
}

// NewDecoder returns a new decoder that reads from r.
//
// Alike encoding/json, the decoder introduces its own buffering and may read
// data from r beyond the JSON values requested.
//
// Unlike encoding/json, there's no Buffered method to retrieve the data
// buffered. Sorry.
func NewDecoder(r io.Reader) *Decoder {
	return &Decoder{scan: scanner.NewScanner(r)}
}

func (d *Decoder) ReadString(target *string) error              { return d.scan.ReadString(target) }
func (d *Decoder) ReadInt(target *int) error                    { return d.scan.ReadInt(target) }
func (d *Decoder) ReadInt64(target *int64) error                { return d.scan.ReadInt64(target) }
func (d *Decoder) ReadUint(target *uint) error                  { return d.scan.ReadUint(target) }
func (d *Decoder) ReadUint64(target *uint64) error              { return d.scan.ReadUint64(target) }
func (d *Decoder) ReadFloat32(target *float32) error            { return d.scan.ReadFloat32(target) }
func (d *Decoder) ReadFloat64(target *float64) error            { return d.scan.ReadFloat64(target) }
func (d *Decoder) ReadBool(target *bool) error                  { return d.scan.ReadBool(target) }
func (d *Decoder) ReadMap(target *map[string]interface{}) error { return d.scan.ReadMap(target) }

// Discard will read the next token out of the reader. You should discard any
// member in an object that you do not use, to avoid leaving the decoder in an
// unexpected state in future calls.
func (d *Decoder) Discard() error {
	_, _, err := d.scan.Scan()
	return err
}

// Disclaimer: The code that follows is almost vertbatim copy/paste from
// codegenerated megajson decoder.

// EachMember iterates over the members of an object. Invoke the proper Read
// function to get the value back.
func (d *Decoder) EachMember(dst interface{}, visitFunc func(*Decoder, interface{}, string) error) error {

	if tok, tokval, err := d.scan.Scan(); err != nil {
		return err
	} else if tok == scanner.TNULL {
		return nil
	} else if tok != scanner.TLBRACE {
		return fmt.Errorf("unexpected %s at %d: %s; expected '{'", scanner.TokenName(tok), d.scan.Pos(), string(tokval))
	}

	index := 0
	for {
		// Read in key.
		var key string
		tok, tokval, err := d.scan.Scan()
		if err != nil {
			return err
		} else if tok == scanner.TRBRACE {
			return nil
		} else if tok == scanner.TCOMMA {
			if index == 0 {
				return fmt.Errorf("unexpected comma at %d", d.scan.Pos())
			}
			if tok, tokval, err = d.scan.Scan(); err != nil {
				return err
			}
		}

		if tok != scanner.TSTRING {
			return fmt.Errorf("unexpected %s at %d: %s; expected '{' or string", scanner.TokenName(tok), d.scan.Pos(), string(tokval))
		}

		key = string(tokval)

		// Read in the colon.
		if tok, tokval, err := d.scan.Scan(); err != nil {
			return err
		} else if tok != scanner.TCOLON {
			return fmt.Errorf("unexpected %s at %d (member %s): %s; expected colon", scanner.TokenName(tok), d.scan.Pos(), key, string(tokval))
		}

		if err := visitFunc(d, dst, key); err != nil {
			return fmt.Errorf("visiting member %s, type %s, %v", key, scanner.TokenName(tok), err)
		}
		index++
	}
}

// EachValue iterates over and invokes visitFunc at each value of an array. Invoke
// the proper Read function to get the value. The type of the value to read is
// specified by the JSONType given as argument.
func (d *Decoder) EachValue(dst interface{}, visitFunc func(*Decoder, interface{}, JSONType) error) error {
	if tok, _, err := d.scan.Scan(); err != nil {
		return err
	} else if tok != scanner.TLBRACKET {
		return errors.New("expected '['")
	}

	// Loop over items.
	index := 0
	for {
		tok, tokval, err := d.scan.Scan()
		if err != nil {
			return err
		} else if tok == scanner.TRBRACKET {
			return nil
		} else if tok == scanner.TCOMMA {
			if index == 0 {
				return fmt.Errorf("unexpected comma in array at %d", d.scan.Pos())
			}
			if tok, tokval, err = d.scan.Scan(); err != nil {
				return err
			}
		}
		d.scan.Unscan(tok, tokval)

		if err := visitFunc(d, dst, toJSONType(tok)); err != nil {
			return fmt.Errorf("visiting index %d, type %s, %v", index, scanner.TokenName(tok), err)
		}
		index++
	}
}

// JSONType that can be received by EachValue funcs.
type JSONType uint8

// Types recognized by the decoder.
const (
	String = iota
	Number
	Bool
	Null
	Object
	Array
)

func toJSONType(token int) JSONType {
	switch token {
	case scanner.TSTRING:
		return String
	case scanner.TNUMBER:
		return Number
	case scanner.TTRUE:
		return Bool
	case scanner.TFALSE:
		return Bool
	case scanner.TNULL:
		return Null
	case scanner.TLBRACE:
		return Object
	case scanner.TLBRACKET:
		return Array
	}
	panic(fmt.Sprintf("unexpected token type, %s (%d). This is likely a bug, please open an issue with the stacktrace at '%s'",
		scanner.TokenName(token), token, "https://github.com/aybabtme/fatherhood/issues/new"))
}
<commit_msg>Refer to the source of where I took code.<commit_after>// Package fatherhood offers a fast JSON decoder with a terrible API.
package fatherhood

import (
	"errors"
	"fmt"
	"github.com/benbjohnson/megajson/scanner"
	"io"
)

// Some docs shamelessly stolen from encoding/json

// Decoder reads and decodes JSON objects from an input stream.
type Decoder struct {
	scan scanner.Scanner
}

// NewDecoder returns a new decoder that reads from r.
//
// Alike encoding/json, the decoder introduces its own buffering and may read
// data from r beyond the JSON values requested.
//
// Unlike encoding/json, there's no Buffered method to retrieve the data
// buffered. Sorry.
func NewDecoder(r io.Reader) *Decoder {
	return &Decoder{scan: scanner.NewScanner(r)}
}

func (d *Decoder) ReadString(target *string) error              { return d.scan.ReadString(target) }
func (d *Decoder) ReadInt(target *int) error                    { return d.scan.ReadInt(target) }
func (d *Decoder) ReadInt64(target *int64) error                { return d.scan.ReadInt64(target) }
func (d *Decoder) ReadUint(target *uint) error                  { return d.scan.ReadUint(target) }
func (d *Decoder) ReadUint64(target *uint64) error              { return d.scan.ReadUint64(target) }
func (d *Decoder) ReadFloat32(target *float32) error            { return d.scan.ReadFloat32(target) }
func (d *Decoder) ReadFloat64(target *float64) error            { return d.scan.ReadFloat64(target) }
func (d *Decoder) ReadBool(target *bool) error                  { return d.scan.ReadBool(target) }
func (d *Decoder) ReadMap(target *map[string]interface{}) error { return d.scan.ReadMap(target) }

// Discard will read the next token out of the reader. You should discard any
// member in an object that you do not use, to avoid leaving the decoder in an
// unexpected state in future calls.
func (d *Decoder) Discard() error {
	_, _, err := d.scan.Scan()
	return err
}

// Disclaimer: The code that follows is almost verbatim copy/pasted from a
// codegenerated megajson decoder: https://github.com/benbjohnson/megajson/blob/master/.bench/code_decoder.go#L22:L132

// EachMember iterates over the members of an object. Invoke the proper Read
// function to get the value back.
func (d *Decoder) EachMember(dst interface{}, visitFunc func(*Decoder, interface{}, string) error) error {

	if tok, tokval, err := d.scan.Scan(); err != nil {
		return err
	} else if tok == scanner.TNULL {
		return nil
	} else if tok != scanner.TLBRACE {
		return fmt.Errorf("unexpected %s at %d: %s; expected '{'", scanner.TokenName(tok), d.scan.Pos(), string(tokval))
	}

	index := 0
	for {
		// Read in key.
		var key string
		tok, tokval, err := d.scan.Scan()
		if err != nil {
			return err
		} else if tok == scanner.TRBRACE {
			return nil
		} else if tok == scanner.TCOMMA {
			if index == 0 {
				return fmt.Errorf("unexpected comma at %d", d.scan.Pos())
			}
			if tok, tokval, err = d.scan.Scan(); err != nil {
				return err
			}
		}

		if tok != scanner.TSTRING {
			return fmt.Errorf("unexpected %s at %d: %s; expected '{' or string", scanner.TokenName(tok), d.scan.Pos(), string(tokval))
		}

		key = string(tokval)

		// Read in the colon.
		if tok, tokval, err := d.scan.Scan(); err != nil {
			return err
		} else if tok != scanner.TCOLON {
			return fmt.Errorf("unexpected %s at %d (member %s): %s; expected colon", scanner.TokenName(tok), d.scan.Pos(), key, string(tokval))
		}

		if err := visitFunc(d, dst, key); err != nil {
			return fmt.Errorf("visiting member %s, type %s, %v", key, scanner.TokenName(tok), err)
		}
		index++
	}
}

// EachValue iterates over and invokes visitFunc at each value of an array. Invoke
// the proper Read function to get the value. The type of the value to read is
// specified by the JSONType given as argument.
func (d *Decoder) EachValue(dst interface{}, visitFunc func(*Decoder, interface{}, JSONType) error) error {
	if tok, _, err := d.scan.Scan(); err != nil {
		return err
	} else if tok != scanner.TLBRACKET {
		return errors.New("expected '['")
	}

	// Loop over items.
	index := 0
	for {
		tok, tokval, err := d.scan.Scan()
		if err != nil {
			return err
		} else if tok == scanner.TRBRACKET {
			return nil
		} else if tok == scanner.TCOMMA {
			if index == 0 {
				return fmt.Errorf("unexpected comma in array at %d", d.scan.Pos())
			}
			if tok, tokval, err = d.scan.Scan(); err != nil {
				return err
			}
		}
		d.scan.Unscan(tok, tokval)

		if err := visitFunc(d, dst, toJSONType(tok)); err != nil {
			return fmt.Errorf("visiting index %d, type %s, %v", index, scanner.TokenName(tok), err)
		}
		index++
	}
}

// JSONType that can be received by EachValue funcs.
type JSONType uint8

// Types recognized by the decoder.
const (
	String = iota
	Number
	Bool
	Null
	Object
	Array
)

func toJSONType(token int) JSONType {
	switch token {
	case scanner.TSTRING:
		return String
	case scanner.TNUMBER:
		return Number
	case scanner.TTRUE:
		return Bool
	case scanner.TFALSE:
		return Bool
	case scanner.TNULL:
		return Null
	case scanner.TLBRACE:
		return Object
	case scanner.TLBRACKET:
		return Array
	}
	panic(fmt.Sprintf("unexpected token type, %s (%d). This is likely a bug, please open an issue with the stacktrace at '%s'",
		scanner.TokenName(token), token, "https://github.com/aybabtme/fatherhood/issues/new"))
}
<|endoftext|>