<commit_before>package lexer

import (
	"fmt"
	"strings"
	"unicode"

	"github.com/stephens2424/php/token"
)

// longestToken is length of the longest token string
var longestToken = 0

const shortPHPBegin = "<?"
const longPHPBegin = "<?php"
const phpEnd = "?>"

const eof = -1

func init() {
	for k := range token.TokenMap {
		if len(k) > longestToken {
			longestToken = len(k)
		}
	}
}

// lexHTML consumes and emits an html t until it
// finds a php begin
func lexHTML(l *lexer) stateFn {
	for {
		if strings.HasPrefix(l.input[l.pos:], shortPHPBegin) {
			if l.pos > l.start {
				l.emit(token.HTML)
			}
			return lexPHPBegin
		}
		if l.next() == eof {
			break
		}
	}
	if l.pos > l.start {
		l.emit(token.HTML)
	}
	l.emit(token.EOF)
	return nil
}

func lexPHPBegin(l *lexer) stateFn {
	if strings.HasPrefix(l.input[l.pos:], longPHPBegin) {
		l.pos += len(longPHPBegin)
	}
	if strings.HasPrefix(l.input[l.pos:], shortPHPBegin) {
		l.pos += len(shortPHPBegin)
	}
	l.emit(token.PHPBegin)
	return lexPHP
}

func lexPHP(l *lexer) stateFn {
	l.skipSpace()

	if r := l.peek(); unicode.IsDigit(r) {
		return lexNumberLiteral
	} else if r == '.' {
		l.next() // must advance because we only peeked before
		secondR := l.peek()
		l.backup()
		if unicode.IsDigit(secondR) {
			return lexNumberLiteral
		}
	}

	if strings.HasPrefix(l.input[l.pos:], "<<<") {
		return lexDoc
	}

	if strings.HasPrefix(l.input[l.pos:], "?>") {
		return lexPHPEnd
	}

	if strings.HasPrefix(l.input[l.pos:], "#") {
		return lexLineComment
	}

	if strings.HasPrefix(l.input[l.pos:], "//") {
		return lexLineComment
	}

	if strings.HasPrefix(l.input[l.pos:], "/*") {
		return lexBlockComment
	}

	if l.peek() == eof {
		l.emit(token.EOF)
		return nil
	}

	// strings
	if l.peek() == '`' {
		return lexShellCommand
	}

	if l.peek() == '\'' {
		return lexSingleQuotedStringLiteral
	}

	if l.peek() == '"' {
		return lexDoubleQuotedStringLiteral
	}

	if t, ok := hasKeyword(l); ok {
		l.emit(t)
		return lexPHP
	}

	return lexIdentifier
}

func hasKeyword(l *lexer) (token.Token, bool) {
	var t token.Token
	// is it an operator or keyword?
	//
	// start by getting the longest possible keyword string
	// out of the remaining input. find the longest keyword
	// match in the input
	var tokenString string
	if len(l.input[l.pos:]) > longestToken {
		tokenString = l.input[l.pos : l.pos+longestToken]
	} else {
		tokenString = l.input[l.pos:]
	}

	tokenString = strings.ToLower(tokenString)

	// starting with the longest match, iterate to see if we have found
	// a keyword or operator token
	for ; tokenString != ""; tokenString = tokenString[:len(tokenString)-1] {
		if t, ok := token.TokenMap[tokenString]; ok {
			isKw := IsKeyword(t, tokenString)

			if isKw {
				switch l.getPrevious().Typ {
				case token.VariableOperator,
					token.ObjectOperator,
					token.ScopeResolutionOperator:
					// if the keyword is preceded by a variable
					// operator, object operator, or scope resolution
					// operator, we actually have an identifier.
					return t, false
				}
			}

			// we think we're at a token of some kind
			l.pos += len(tokenString)
			if isKw && l.accept(alphabet+underscore+digits) {
				// but if the keyword actually continues on
				// unexpectedly, roll back because this is
				// actually an identifier

				// to account for the extra character consumed by
				// accept
				l.backup()

				// move back the length of the false keyword now
				l.pos -= len(tokenString)
				return t, false
			}

			// we definitely have a token, emit it.
			return t, true
		}
	}
	return t, false
}

func lexIdentifier(l *lexer) stateFn {
	var weirdIdentifier bool
	l.acceptRunFn(func(r rune) bool {
		if unicode.IsLetter(r) || unicode.IsNumber(r) || r == '_' || r == '\\' {
			return true
		}

		if unicode.IsSpace(r) {
			return false
		}

		if _, ok := token.OperatorMarks[r]; ok {
			return false
		}

		weirdIdentifier = true
		return true
	})

	if weirdIdentifier {
		l.errorf("unexpected characters in identifier")
	}

	l.emit(token.Identifier)
	return lexPHP
}

func lexNumberLiteral(l *lexer) stateFn {
	if l.accept("0") {
		// binary?
		if l.accept("b") {
			l.acceptRun("01")
			l.emit(token.NumberLiteral)
			return lexPHP
		}
		// hexadecimal?
		if l.accept("xX") {
			l.acceptRun(digits + "abcdefABCDEF")
			l.emit(token.NumberLiteral)
			return lexPHP
		}
	}
	// is decimal?
	l.acceptRun(digits)
	if l.accept(".") {
		l.acceptRun(digits)
	}

	if l.accept("eE") {
		l.acceptRun(digits)
	}

	l.emit(token.NumberLiteral)
	return lexPHP
}

func lexShellCommand(l *lexer) stateFn {
	l.next()
	for {
		switch l.next() {
		case '`':
			l.emit(token.ShellCommand)
			return lexPHP
		case eof:
			l.emit(token.ShellCommand)
			return nil
		}
	}
}

func lexSingleQuotedStringLiteral(l *lexer) stateFn {
	l.next()
	for {
		switch l.next() {
		case '\\':
			l.next()
			continue
		case '\'':
			l.emit(token.StringLiteral)
			return lexPHP
		}
	}
}

func lexDoubleQuotedStringLiteral(l *lexer) stateFn {
	l.next()
	for {
		switch l.next() {
		case '\\':
			l.next()
			continue
		case '"':
			l.emit(token.StringLiteral)
			return lexPHP
		}
	}
}

const alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const digits = "0123456789"
const underscore = "_"

// lexPHPEnd lexes the end of a PHP section returning the context to HTML
func lexPHPEnd(l *lexer) stateFn {
	l.pos += len(phpEnd)
	l.emit(token.PHPEnd)
	return lexHTML
}

func lexLineComment(l *lexer) stateFn {
	lineLength := strings.IndexAny(l.input[l.pos:], "\r\n") + 1
	if lineLength == 0 {
		// this is the last line, so lex until the end
		lineLength = len(l.input[l.pos:])
	}

	// deal with varying line endings
	if l.input[l.pos+lineLength-1] == '\r' &&
		len(l.input[l.pos+lineLength:]) > 0 &&
		l.input[l.pos+lineLength] == '\n' {
		lineLength++
	}

	// don't lex php end
	if phpEndLength := strings.Index(l.input[l.pos:l.pos+lineLength], phpEnd); phpEndLength >= 0 && phpEndLength < lineLength {
		lineLength = phpEndLength
	}
	l.pos += lineLength
	l.emit(token.CommentLine)
	return lexPHP
}

func lexBlockComment(l *lexer) stateFn {
	commentLength := strings.Index(l.input[l.pos:], "*/") + 2
	if commentLength == 1 {
		// the file ends before we find */
		commentLength = len(l.input[l.pos:])
	}
	l.pos += commentLength
	l.emit(token.CommentBlock)
	return lexPHP
}

func lexDoc(l *lexer) stateFn {
	var nowDoc bool
	l.pos += len("<<<")
	l.skipSpace()
	if strings.HasPrefix(l.input[l.pos:], "'") {
		nowDoc = true
		l.pos += len("'")
	} else if l.peek() == '"' {
		l.next()
	}
	labelPos := l.pos
	l.accept(underscore + alphabet)
	l.acceptRun(underscore + alphabet + digits)

	endMarkerA := fmt.Sprintf("\r\n%s", l.input[labelPos:l.pos])
	endMarkerB := fmt.Sprintf("\n%s", l.input[labelPos:l.pos])
	endMarkerC := fmt.Sprintf("\r%s", l.input[labelPos:l.pos])
	if nowDoc {
		l.accept("'")
	} else if l.peek() == '"' {
		l.next()
	}
	l.accept("\r\n")
	l.accept("\r\n")

	for !strings.HasPrefix(l.input[l.pos:], endMarkerA) &&
		!strings.HasPrefix(l.input[l.pos:], endMarkerB) &&
		!strings.HasPrefix(l.input[l.pos:], endMarkerC) {
		l.next()
	}

	if strings.HasPrefix(l.input[l.pos:], endMarkerA) {
		l.pos += len(endMarkerA)
	} else {
		l.pos += len(endMarkerB)
	}

	l.emit(token.StringLiteral)
	return lexPHP
}
<commit_msg>clean hasKeyword function #18<commit_after>package lexer

import (
	"fmt"
	"strings"
	"unicode"

	"github.com/stephens2424/php/token"
)

// longestToken is length of the longest token string
var longestToken = 0

const shortPHPBegin = "<?"
const longPHPBegin = "<?php"
const phpEnd = "?>"

const eof = -1

func init() {
	for k := range token.TokenMap {
		if len(k) > longestToken {
			longestToken = len(k)
		}
	}
}

// lexHTML consumes and emits an html t until it
// finds a php begin
func lexHTML(l *lexer) stateFn {
	for {
		if strings.HasPrefix(l.input[l.pos:], shortPHPBegin) {
			if l.pos > l.start {
				l.emit(token.HTML)
			}
			return lexPHPBegin
		}
		if l.next() == eof {
			break
		}
	}
	if l.pos > l.start {
		l.emit(token.HTML)
	}
	l.emit(token.EOF)
	return nil
}

func lexPHPBegin(l *lexer) stateFn {
	if strings.HasPrefix(l.input[l.pos:], longPHPBegin) {
		l.pos += len(longPHPBegin)
	}
	if strings.HasPrefix(l.input[l.pos:], shortPHPBegin) {
		l.pos += len(shortPHPBegin)
	}
	l.emit(token.PHPBegin)
	return lexPHP
}

func lexPHP(l *lexer) stateFn {
	l.skipSpace()

	if r := l.peek(); unicode.IsDigit(r) {
		return lexNumberLiteral
	} else if r == '.' {
		l.next() // must advance because we only peeked before
		secondR := l.peek()
		l.backup()
		if unicode.IsDigit(secondR) {
			return lexNumberLiteral
		}
	}

	if strings.HasPrefix(l.input[l.pos:], "<<<") {
		return lexDoc
	}

	if strings.HasPrefix(l.input[l.pos:], "?>") {
		return lexPHPEnd
	}

	if strings.HasPrefix(l.input[l.pos:], "#") {
		return lexLineComment
	}

	if strings.HasPrefix(l.input[l.pos:], "//") {
		return lexLineComment
	}

	if strings.HasPrefix(l.input[l.pos:], "/*") {
		return lexBlockComment
	}

	if l.peek() == eof {
		l.emit(token.EOF)
		return nil
	}

	// strings
	if l.peek() == '`' {
		return lexShellCommand
	}

	if l.peek() == '\'' {
		return lexSingleQuotedStringLiteral
	}

	if l.peek() == '"' {
		return lexDoubleQuotedStringLiteral
	}

	if t, ok := hasKeyword(l); ok {
		l.emit(t)
		return lexPHP
	}

	return lexIdentifier
}

func isOperator(t token.Token) bool {
	_, ok := map[token.Token]struct{}{
		token.VariableOperator: struct{}{},
		token.ObjectOperator: struct{}{},
		token.ScopeResolutionOperator: struct{}{},
	}[t]

	return ok
}

func hasKeyword(l *lexer) (token.Token, bool) {
	var t token.Token
	// is it an operator or keyword?
	//
	// start by getting the longest possible keyword string
	// out of the remaining input. find the longest keyword
	// match in the input
	var tokenString string
	if len(l.input[l.pos:]) > longestToken {
		tokenString = l.input[l.pos : l.pos+longestToken]
	} else {
		tokenString = l.input[l.pos:]
	}

	tokenString = strings.ToLower(tokenString)

	// starting with the longest match, iterate to see if we have found
	// a keyword or operator token
	for ; tokenString != ""; tokenString = tokenString[:len(tokenString)-1] {
		t, ok := token.TokenMap[tokenString]
		if !ok {
			continue
		}

		if !IsKeyword(t, tokenString) {
			l.pos += len(tokenString)
			return t, true
		}

		if (isOperator(l.getPrevious().Typ)) {
			// if the keyword is preceded by a variable
			// operator, object operator, or scope resolution
			// operator, we actually have an identifier.
			return t, false
		}

		// we think we're at a token of some kind
		l.pos += len(tokenString)
		if l.accept(alphabet + underscore + digits) {
			// but if the keyword actually continues on
			// unexpectedly, roll back because this is
			// actually an identifier

			// to account for the extra character consumed by
			// accept
			l.backup()

			// move back the length of the false keyword now
			l.pos -= len(tokenString)
			return t, false
		}

		// we definitely have a token, emit it.
		return t, true
	}
	return t, false
}

func lexIdentifier(l *lexer) stateFn {
	var weirdIdentifier bool
	l.acceptRunFn(func(r rune) bool {
		if unicode.IsLetter(r) || unicode.IsNumber(r) || r == '_' || r == '\\' {
			return true
		}

		if unicode.IsSpace(r) {
			return false
		}

		if _, ok := token.OperatorMarks[r]; ok {
			return false
		}

		weirdIdentifier = true
		return true
	})

	if weirdIdentifier {
		l.errorf("unexpected characters in identifier")
	}

	l.emit(token.Identifier)
	return lexPHP
}

func lexNumberLiteral(l *lexer) stateFn {
	if l.accept("0") {
		// binary?
		if l.accept("b") {
			l.acceptRun("01")
			l.emit(token.NumberLiteral)
			return lexPHP
		}
		// hexadecimal?
		if l.accept("xX") {
			l.acceptRun(digits + "abcdefABCDEF")
			l.emit(token.NumberLiteral)
			return lexPHP
		}
	}
	// is decimal?
	l.acceptRun(digits)
	if l.accept(".") {
		l.acceptRun(digits)
	}

	if l.accept("eE") {
		l.acceptRun(digits)
	}

	l.emit(token.NumberLiteral)
	return lexPHP
}

func lexShellCommand(l *lexer) stateFn {
	l.next()
	for {
		switch l.next() {
		case '`':
			l.emit(token.ShellCommand)
			return lexPHP
		case eof:
			l.emit(token.ShellCommand)
			return nil
		}
	}
}

func lexSingleQuotedStringLiteral(l *lexer) stateFn {
	l.next()
	for {
		switch l.next() {
		case '\\':
			l.next()
			continue
		case '\'':
			l.emit(token.StringLiteral)
			return lexPHP
		}
	}
}

func lexDoubleQuotedStringLiteral(l *lexer) stateFn {
	l.next()
	for {
		switch l.next() {
		case '\\':
			l.next()
			continue
		case '"':
			l.emit(token.StringLiteral)
			return lexPHP
		}
	}
}

const alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const digits = "0123456789"
const underscore = "_"

// lexPHPEnd lexes the end of a PHP section returning the context to HTML
func lexPHPEnd(l *lexer) stateFn {
	l.pos += len(phpEnd)
	l.emit(token.PHPEnd)
	return lexHTML
}

func lexLineComment(l *lexer) stateFn {
	lineLength := strings.IndexAny(l.input[l.pos:], "\r\n") + 1
	if lineLength == 0 {
		// this is the last line, so lex until the end
		lineLength = len(l.input[l.pos:])
	}

	// deal with varying line endings
	if l.input[l.pos+lineLength-1] == '\r' &&
		len(l.input[l.pos+lineLength:]) > 0 &&
		l.input[l.pos+lineLength] == '\n' {
		lineLength++
	}

	// don't lex php end
	if phpEndLength := strings.Index(l.input[l.pos:l.pos+lineLength], phpEnd); phpEndLength >= 0 && phpEndLength < lineLength {
		lineLength = phpEndLength
	}
	l.pos += lineLength
	l.emit(token.CommentLine)
	return lexPHP
}

func lexBlockComment(l *lexer) stateFn {
	commentLength := strings.Index(l.input[l.pos:], "*/") + 2
	if commentLength == 1 {
		// the file ends before we find */
		commentLength = len(l.input[l.pos:])
	}
	l.pos += commentLength
	l.emit(token.CommentBlock)
	return lexPHP
}

func lexDoc(l *lexer) stateFn {
	var nowDoc bool
	l.pos += len("<<<")
	l.skipSpace()
	if strings.HasPrefix(l.input[l.pos:], "'") {
		nowDoc = true
		l.pos += len("'")
	} else if l.peek() == '"' {
		l.next()
	}
	labelPos := l.pos
	l.accept(underscore + alphabet)
	l.acceptRun(underscore + alphabet + digits)

	endMarkerA := fmt.Sprintf("\r\n%s", l.input[labelPos:l.pos])
	endMarkerB := fmt.Sprintf("\n%s", l.input[labelPos:l.pos])
	endMarkerC := fmt.Sprintf("\r%s", l.input[labelPos:l.pos])
	if nowDoc {
		l.accept("'")
	} else if l.peek() == '"' {
		l.next()
	}
	l.accept("\r\n")
	l.accept("\r\n")

	for !strings.HasPrefix(l.input[l.pos:], endMarkerA) &&
		!strings.HasPrefix(l.input[l.pos:], endMarkerB) &&
		!strings.HasPrefix(l.input[l.pos:], endMarkerC) {
		l.next()
	}

	if strings.HasPrefix(l.input[l.pos:], endMarkerA) {
		l.pos += len(endMarkerA)
	} else {
		l.pos += len(endMarkerB)
	}

	l.emit(token.StringLiteral)
	return lexPHP
}
<|endoftext|>


<commit_before>package lib

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"strings"

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

const (
	//being a good citizen
	start_cmd, help_cmd = "/start", "/help"

	chat_cmd, remind_cmd, say_cmd, reminders_cmd, said_cmd = "/chat", "/remind", "/say", "/reminders", "/said"

	remind_cmd_hint, say_cmd_hint = "/remind me in <days> to <message>", "/say [what you want to say]"

	default_script = "default"
)

type (
	Info struct {
		App       []string `json:"appInfo"`
		Reminders []string `json:"remindersInfo"`
		Chat      []string `json:"chatInfo"`
		Said      []string `json:"saidInfo"`
	}

	QProcess struct {
		s    *session
		lang struct {
			questions `json:"QandA"`
		}
		info     *Info
		next     *Question
		lastTime Notes
	}
)

func NewQProcess(b *telebot.Bot, s *Storage) *QProcess {
	q := &QProcess{s: newSession(b, s)}
	q.loadInfo()
	return q
}

func (this *QProcess) Run(input <-chan telebot.Message) {
	for msg := range input {
		this.s.User = msg.Sender
		this.
			quickWinFirst(msg).
			determineScript(msg).
			findNextQuestion(msg).
			andChat()
	}
}

func (this *QProcess) quickWinFirst(msg telebot.Message) *QProcess {

	if isCmd(msg.Text, start_cmd, help_cmd) {
		this.s.send(this.info.App...)

		availCmds := fmt.Sprintf("%s %s %s %s %s",
			chat_cmd+" - to have a *quick chat* about what your upto \n\n",
			say_cmd_hint+" - to say *anything* thats on your mind \n\n",
			said_cmd+" - to show all the things you have said \n\n",
			remind_cmd_hint+" - to keep track of the things you need to be reminded about \n\n",
			reminders_cmd+" - to show all the reminders you have made",
		)

		this.s.send(availCmds)
	} else if hasSubmisson(msg.Text, say_cmd) {
		log.Println("save something said ", msg.Text)
		this.s.save(msg, say_cmd)
	} else if hasSubmisson(msg.Text, remind_cmd) {
		log.Println("save a reminder ", msg.Text)
		this.s.saveAsReminder(msg)
	} else if hasSubmisson(msg.Text, reminders_cmd) || isCmd(msg.Text, reminders_cmd) {
		log.Println("showing reminders ", msg.Text)
		r := this.s.getReminders(msg)
		this.s.send(this.info.Reminders...)
		for i := range r {
			this.s.send(r[i].ToString())

		}
	} else if hasSubmisson(msg.Text, said_cmd) || isCmd(msg.Text, said_cmd) {
		log.Println("showing things said ", msg.Text)
		r := this.s.getNotes(msg)
		this.s.send(this.info.Said...)
		for i := range r {
			this.s.send(r[i].ToString())
		}
	}
	return this
}

func (this *QProcess) loadInfo() {
	file, err := os.Open("./config/fantail.json")
	if err != nil {
		log.Panic("could not load App info", err.Error())
	}
	err = json.NewDecoder(file).Decode(&this.info)
	if err != nil {
		log.Panic("could not decode App info ", err.Error())
	}
}

func (this *QProcess) loadScript(scriptName string) {
	file, err := os.Open(fmt.Sprintf("./config/%s.json", scriptName))
	if err != nil {
		log.Panic("could not load QandA language file ", err.Error())
	}
	err = json.NewDecoder(file).Decode(&this.lang)
	if err != nil {
		log.Panic("could not decode QandA ", err.Error())
	}
}

func (this *QProcess) determineScript(msg telebot.Message) *QProcess {

	if hasSubmisson(msg.Text, remind_cmd, say_cmd) {
		log.Println("load default script after submisson")
		this.loadScript(default_script)
	} else if isCmd(msg.Text, chat_cmd, said_cmd, say_cmd, remind_cmd, reminders_cmd) {
		words := strings.Fields(msg.Text)
		scriptName := strings.SplitAfter(words[0], "/")[1]
		log.Println("load command script", scriptName)
		this.loadScript(scriptName)
	}
	return this
}

func (this *QProcess) findNextQuestion(msg telebot.Message) *QProcess {
	this.next = nil

	if isCmd(msg.Text, chat_cmd, said_cmd, say_cmd, remind_cmd, reminders_cmd) {
		//start at the beginning - covers submissons also
		this.next = this.lang.questions[0]
		return this
	} else {
		//find the next question
		for i := range this.lang.questions {
			for a := range this.lang.questions[i].RelatesTo.Answers {
				if this.lang.questions[i].RelatesTo.Answers[a] == msg.Text {
					//was the answer a remainder to save?
					if this.lang.questions[i].RelatesTo.Save {
						this.s.save(msg, chat_cmd, this.lang.questions[i].RelatesTo.SaveTag)
						this.lastTime = append(this.lastTime, this.s.getLastChat(this.lang.questions[i].RelatesTo.SaveTag))
					}
					this.next = this.lang.questions[i]
					return this
				}
			}
		}
	}
	log.Println("looks like we are all done!")
	return this
}

func (this *QProcess) makeKeyboard() Keyboard {
	keyboard := Keyboard{}
	for i := range this.next.PossibleAnswers {
		keyboard = append(keyboard, []string{this.next.PossibleAnswers[i]})
	}
	return keyboard
}

func (this *QProcess) andChat() {
	if this.next != nil {
		this.s.send(this.next.Context...)
		this.s.sendWithKeyboard(this.next.QuestionText, this.makeKeyboard())
		return
	}

	if len(this.lastTime) > 0 {
		this.s.send(this.info.Chat...)
		for i := range this.lastTime {
			this.s.send(this.lastTime[i].ToString())
		}
		this.lastTime = nil
	}
	return
}
<commit_msg>cleanup help cmd, logging for incoming message<commit_after>package lib

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"strings"

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

const (
	//being a good citizen
	start_cmd, help_cmd = "/start", "/help"

	chat_cmd, remind_cmd, say_cmd, reminders_cmd, said_cmd = "/chat", "/remind", "/say", "/reminders", "/said"

	remind_cmd_hint, say_cmd_hint = "/remind me in <days> to <message>", "/say [what you want to say]"

	default_script = "default"
)

type (
	Info struct {
		App       []string `json:"appInfo"`
		Reminders []string `json:"remindersInfo"`
		Chat      []string `json:"chatInfo"`
		Said      []string `json:"saidInfo"`
	}

	QProcess struct {
		s    *session
		lang struct {
			questions `json:"QandA"`
		}
		info     *Info
		next     *Question
		lastTime Notes
	}
)

func NewQProcess(b *telebot.Bot, s *Storage) *QProcess {
	q := &QProcess{s: newSession(b, s)}
	q.loadInfo()
	return q
}

func (this *QProcess) Run(input <-chan telebot.Message) {
	for msg := range input {

		log.Println("incoming ", msg)

		this.s.User = msg.Sender
		this.
			quickWinFirst(msg).
			determineScript(msg).
			findNextQuestion(msg).
			andChat()
	}
}

func (this *QProcess) quickWinFirst(msg telebot.Message) *QProcess {

	if isCmd(msg.Text, start_cmd, help_cmd) {

		appInfo := fmt.Sprintf("%s %s %s %s %s %s",
			this.info.App,
			chat_cmd+" - to have a *quick chat* about what your upto \n\n",
			say_cmd_hint+" - to say *anything* thats on your mind \n\n",
			said_cmd+" - to show all the things you have said \n\n",
			remind_cmd_hint+" - to keep track of the things you need to be reminded about \n\n",
			reminders_cmd+" - to show all the reminders you have made",
		)

		this.s.send(appInfo)
	} else if hasSubmisson(msg.Text, say_cmd) {
		log.Println("save something said ", msg.Text)
		this.s.save(msg, say_cmd)
	} else if hasSubmisson(msg.Text, remind_cmd) {
		log.Println("save a reminder ", msg.Text)
		this.s.saveAsReminder(msg)
	} else if hasSubmisson(msg.Text, reminders_cmd) || isCmd(msg.Text, reminders_cmd) {
		log.Println("showing reminders ", msg.Text)
		r := this.s.getReminders(msg)
		this.s.send(this.info.Reminders...)
		for i := range r {
			this.s.send(r[i].ToString())

		}
	} else if hasSubmisson(msg.Text, said_cmd) || isCmd(msg.Text, said_cmd) {
		log.Println("showing things said ", msg.Text)
		r := this.s.getNotes(msg)
		this.s.send(this.info.Said...)
		for i := range r {
			this.s.send(r[i].ToString())
		}
	}
	return this
}

func (this *QProcess) loadInfo() {
	file, err := os.Open("./config/fantail.json")
	if err != nil {
		log.Panic("could not load App info", err.Error())
	}
	err = json.NewDecoder(file).Decode(&this.info)
	if err != nil {
		log.Panic("could not decode App info ", err.Error())
	}
}

func (this *QProcess) loadScript(scriptName string) {
	file, err := os.Open(fmt.Sprintf("./config/%s.json", scriptName))
	if err != nil {
		log.Panic("could not load QandA language file ", err.Error())
	}
	err = json.NewDecoder(file).Decode(&this.lang)
	if err != nil {
		log.Panic("could not decode QandA ", err.Error())
	}
}

func (this *QProcess) determineScript(msg telebot.Message) *QProcess {

	if hasSubmisson(msg.Text, remind_cmd, say_cmd) {
		log.Println("load default script after submisson")
		this.loadScript(default_script)
	} else if isCmd(msg.Text, chat_cmd, said_cmd, say_cmd, remind_cmd, reminders_cmd) {
		words := strings.Fields(msg.Text)
		scriptName := strings.SplitAfter(words[0], "/")[1]
		log.Println("load command script", scriptName)
		this.loadScript(scriptName)
	}
	return this
}

func (this *QProcess) findNextQuestion(msg telebot.Message) *QProcess {
	this.next = nil

	if isCmd(msg.Text, chat_cmd, said_cmd, say_cmd, remind_cmd, reminders_cmd) {
		//start at the beginning - covers submissons also
		this.next = this.lang.questions[0]
		return this
	} else {
		//find the next question
		for i := range this.lang.questions {
			for a := range this.lang.questions[i].RelatesTo.Answers {
				if this.lang.questions[i].RelatesTo.Answers[a] == msg.Text {
					//was the answer a remainder to save?
					if this.lang.questions[i].RelatesTo.Save {
						this.s.save(msg, chat_cmd, this.lang.questions[i].RelatesTo.SaveTag)
						this.lastTime = append(this.lastTime, this.s.getLastChat(this.lang.questions[i].RelatesTo.SaveTag))
					}
					this.next = this.lang.questions[i]
					return this
				}
			}
		}
	}
	log.Println("looks like we are all done!")
	return this
}

func (this *QProcess) makeKeyboard() Keyboard {
	keyboard := Keyboard{}
	for i := range this.next.PossibleAnswers {
		keyboard = append(keyboard, []string{this.next.PossibleAnswers[i]})
	}
	return keyboard
}

func (this *QProcess) andChat() {
	if this.next != nil {
		this.s.send(this.next.Context...)
		this.s.sendWithKeyboard(this.next.QuestionText, this.makeKeyboard())
		return
	}

	if len(this.lastTime) > 0 {
		this.s.send(this.info.Chat...)
		for i := range this.lastTime {
			this.s.send(this.lastTime[i].ToString())
		}
		this.lastTime = nil
	}
	return
}
<|endoftext|>


<commit_before>package lib

import (
	"fmt"
	"strings"
)

type model interface {
	incr(key string) error //increment the given key, setting it to zero if it doesn't exist
	getInt(key string) int //get a key as an int, defaulting to 0 if it doesn't exist
}

// a dabopobo command, consisting of a regex to match against and a commandHandler to run if it matches
type cmd struct {
	regex   string
	handler commandHandler
}

/*
A commandHandler is a function that handles a dabopobo command and returns a slice of bytes to respond with and possibly an error.

m is the data model for this process.
submatches is the output of FindAllStringSubmatch() when running the message text on the corresponding cmd's regex.
username is the username of the user who sent the message.
response is the response to be sent back to slack.
err is non-nil if an error is produced. response should be empty in this case.
*/
type commandHandler func(m model, submatches [][]string, username string) (response string, err error)

var mutateKarmaCmd = cmd{"(\\(.+\\)|[^ ]+?)(\\+\\++|--+|\\+-|-\\+)", mutateKarma}

//handles identifier++
func mutateKarma(m model, mutations [][]string, username string) (s string, err error) {
	karmaMutations := filterMutations(mutations, username)
	for _, mutation := range karmaMutations {
		key := mutation.key()
		err = m.incr(key)
		if err != nil {
			err = nil
			return
		}
		fmt.Println(key)
	}
	return
}

var getKarmaCmd = cmd{"^!karma +([^ ].+)", getKarma}

//handles !karma identifier
func getKarma(m model, identifier [][]string, username string) (text string, err error) {
	name := identifier[0][1] //since the regex has a beginning of string hook, there should only be one match, so we only care about index 0.
	karmaset := getKarmaSet(m, name)
	text = fmt.Sprintf("%v's karma is %v %v", name, karmaset.value(), karmaset)
	fmt.Println(text)
	return
}

var helpCmd = cmd{"^!karma(|help)$", help}

func help(m model, s [][]string, u string) (string, error) {
	fmt.Println("help message")
	return strings.Join(
		[]string{
			"!karma thing\tdisplays things karma. It can be anything, including with spaces.",
			"thing++\t(with at least 2 pluses) gives positive karma",
			"thing--\t(with at least 2 minuses) gives negative karma",
			"thing+-\t(either order) gives neutral karma",
			"(thing with spaces)++\t(with any of the above) gives karma to a thing with spaces in it.",
			"!karma or !karmahelp\tdisplays this help",
		},
		"\n",
	), nil
}

var mentionedCmd = cmd{"dabopobo", mentioned}

func mentioned(m model, s [][]string, u string) (string, error) {
	return "don't touch me", nil
}

// getKarmaSet loads the karma for a given key
func getKarmaSet(m model, name string) (k karmaSet) {
	name = strings.ToLower(name)
	k.plusplus = m.getInt(name + "++")
	k.minusminus = m.getInt(name + "--")
	k.plusminus = m.getInt(name + "+-")
	return
}
<commit_msg>!karma now supports single character identifiers<commit_after>package lib

import (
	"fmt"
	"strings"
)

type model interface {
	incr(key string) error //increment the given key, setting it to zero if it doesn't exist
	getInt(key string) int //get a key as an int, defaulting to 0 if it doesn't exist
}

// a dabopobo command, consisting of a regex to match against and a commandHandler to run if it matches
type cmd struct {
	regex   string
	handler commandHandler
}

/*
A commandHandler is a function that handles a dabopobo command and returns a slice of bytes to respond with and possibly an error.

m is the data model for this process.
submatches is the output of FindAllStringSubmatch() when running the message text on the corresponding cmd's regex.
username is the username of the user who sent the message.
response is the response to be sent back to slack.
err is non-nil if an error is produced. response should be empty in this case.
*/
type commandHandler func(m model, submatches [][]string, username string) (response string, err error)

var mutateKarmaCmd = cmd{"(\\(.+\\)|[^ ]+?)(\\+\\++|--+|\\+-|-\\+)", mutateKarma}

//handles identifier++
func mutateKarma(m model, mutations [][]string, username string) (s string, err error) {
	karmaMutations := filterMutations(mutations, username)
	for _, mutation := range karmaMutations {
		key := mutation.key()
		err = m.incr(key)
		if err != nil {
			err = nil
			return
		}
		fmt.Println(key)
	}
	return
}

var getKarmaCmd = cmd{"^!karma +([^ ].*)", getKarma}

//handles !karma identifier
func getKarma(m model, identifier [][]string, username string) (text string, err error) {
	name := identifier[0][1] //since the regex has a beginning of string hook, there should only be one match, so we only care about index 0.
	karmaset := getKarmaSet(m, name)
	text = fmt.Sprintf("%v's karma is %v %v", name, karmaset.value(), karmaset)
	fmt.Println(text)
	return
}

var helpCmd = cmd{"^!karma(|help)$", help}

func help(m model, s [][]string, u string) (string, error) {
	fmt.Println("help message")
	return strings.Join(
		[]string{
			"!karma thing\tdisplays things karma. It can be anything, including with spaces.",
			"thing++\t(with at least 2 pluses) gives positive karma",
			"thing--\t(with at least 2 minuses) gives negative karma",
			"thing+-\t(either order) gives neutral karma",
			"(thing with spaces)++\t(with any of the above) gives karma to a thing with spaces in it.",
			"!karma or !karmahelp\tdisplays this help",
		},
		"\n",
	), nil
}

var mentionedCmd = cmd{"dabopobo", mentioned}

func mentioned(m model, s [][]string, u string) (string, error) {
	return "don't touch me", nil
}

// getKarmaSet loads the karma for a given key
func getKarmaSet(m model, name string) (k karmaSet) {
	name = strings.ToLower(name)
	k.plusplus = m.getInt(name + "++")
	k.minusminus = m.getInt(name + "--")
	k.plusminus = m.getInt(name + "+-")
	return
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	dist "github.com/losinggeneration/hge-go/helpers/distortionmesh"
	"github.com/losinggeneration/hge-go/helpers/font"
	HGE "github.com/losinggeneration/hge-go/hge"
	hge "github.com/losinggeneration/hge-go/legacy"
	"math"
)

var (
	h   *hge.HGE
	tex HGE.Texture
	dis dist.DistortionMesh
	fnt *font.Font
)

const (
	rows  = 16
	cols  = 16
	cellw = 512.0 / (cols - 1)
	cellh = 512.0 / (rows - 1)

	meshx = 144.0
	meshy = 44.0
)

var trans = 0
var t = 0.0

func FrameFunc() int {
	t += h.Timer_GetDelta()

	// Process keys
	switch h.Input_GetKey() {
	case HGE.K_ESCAPE:
		return 1

	case HGE.K_SPACE:
		trans++

		if trans > 2 {
			trans = 0
		}

		dis.Clear(HGE.Dword(0xFF000000))
	}

	// Calculate new displacements and coloring for one of the three effects
	switch trans {
	case 0:
		for i := 1; i < rows-1; i++ {
			for j := 1; j < cols-1; j++ {
				dis.SetDisplacement(j, i, math.Cos(t*float64(10+(i+j))/2)*5, math.Sin(t*float64(10+(i+j))/2)*5, dist.DISP_NODE)
			}
		}

	case 1:
		for i := 0; i < rows; i++ {
			for j := 1; j < cols-1; j++ {
				dis.SetDisplacement(j, i, math.Cos(t*float64(5+j)/2)*15, 0, dist.DISP_NODE)
				col := HGE.Dword((math.Cos(t*float64(5+(i+j))/2) + 1) * 35)
				dis.SetColor(j, i, 0xFF<<24|col<<16|col<<8|col)
			}
		}

	case 2:
		for i := 0.0; i < rows; i++ {
			for j := 0.0; j < cols; j++ {
				r := math.Sqrt(math.Pow(j-float64(cols)/2, 2) + math.Pow(i-float64(rows)/2, 2))
				a := r * math.Cos(t*2) * 0.1
				dx := math.Sin(a)*(i*cellh-256) + math.Cos(a)*(j*cellw-256)
				dy := math.Cos(a)*(i*cellh-256) - math.Sin(a)*(j*cellw-256)
				dis.SetDisplacement(int(j), int(i), dx, dy, dist.DISP_CENTER)
				col := HGE.Dword((math.Cos(r+t*4) + 1) * 40)
				dis.SetColor(int(j), int(i), 0xFF<<24|col<<16|(col/2)<<8)
			}
		}
	}

	return 0
}

func RenderFunc() int {
	// Render graphics
	h.Gfx_BeginScene()
	h.Gfx_Clear(0)
	dis.Render(meshx, meshy)
	fnt.Printf(5, 5, font.TEXT_LEFT, "dt:%.3f\nFPS:%d\n\nUse your\nSPACE!", h.Timer_GetDelta(), h.Timer_GetFPS())
	h.Gfx_EndScene()

	return 0
}

func main() {
	h = hge.Create(HGE.VERSION)
	defer h.Release()

	h.System_SetState(HGE.LOGFILE, "tutorial05.log")
	h.System_SetState(HGE.FRAMEFUNC, FrameFunc)
	h.System_SetState(HGE.RENDERFUNC, RenderFunc)
	h.System_SetState(HGE.TITLE, "HGE Tutorial 05 - Using distortion mesh")
	h.System_SetState(HGE.WINDOWED, true)
	h.System_SetState(HGE.SCREENWIDTH, 800)
	h.System_SetState(HGE.SCREENHEIGHT, 600)
	h.System_SetState(HGE.SCREENBPP, 32)
	h.System_SetState(HGE.USESOUND, false)

	if h.System_Initiate() {
		defer h.System_Shutdown()
		tex = h.Texture_Load("texture.jpg")
		if tex == 0 {
			fmt.Println("Error: Can't load texture.jpg")
			return
		}
		defer h.Texture_Free(tex)

		dis = dist.NewDistortionMesh(cols, rows)
		dis.SetTexture(tex)
		dis.SetTextureRect(0, 0, 512, 512)
		dis.SetBlendMode(HGE.BLEND_COLORADD | HGE.BLEND_ALPHABLEND | HGE.BLEND_ZWRITE)
		dis.Clear(HGE.Dword(0xFF000000))

		// Load a font
		fnt = font.NewFont("font1.fnt")

		if fnt == nil {
			fmt.Println("Error: Can't load font1.fnt or font1.png")
			return
		}

		// Let's rock now!
		h.System_Start()
	}
}
<commit_msg>Updated tutorial05 to use new API<commit_after>package main

import (
	"fmt"
	dist "github.com/losinggeneration/hge-go/helpers/distortionmesh"
	"github.com/losinggeneration/hge-go/helpers/font"
	HGE "github.com/losinggeneration/hge-go/hge"
	"math"
)

var (
	tex HGE.Texture
	dis dist.DistortionMesh
	fnt *font.Font
)

const (
	rows  = 16
	cols  = 16
	cellw = 512.0 / (cols - 1)
	cellh = 512.0 / (rows - 1)

	meshx = 144.0
	meshy = 44.0
)

var trans = 0
var t = 0.0

func FrameFunc() int {
	t += HGE.NewTimer().Delta()

	// Process keys
	switch HGE.GetKey() {
	case HGE.K_ESCAPE:
		return 1

	case HGE.K_SPACE:
		trans++

		if trans > 2 {
			trans = 0
		}

		dis.Clear(HGE.Dword(0xFF000000))
	}

	// Calculate new displacements and coloring for one of the three effects
	switch trans {
	case 0:
		for i := 1; i < rows-1; i++ {
			for j := 1; j < cols-1; j++ {
				dis.SetDisplacement(j, i, math.Cos(t*float64(10+(i+j))/2)*5, math.Sin(t*float64(10+(i+j))/2)*5, dist.DISP_NODE)
			}
		}

	case 1:
		for i := 0; i < rows; i++ {
			for j := 1; j < cols-1; j++ {
				dis.SetDisplacement(j, i, math.Cos(t*float64(5+j)/2)*15, 0, dist.DISP_NODE)
				col := HGE.Dword((math.Cos(t*float64(5+(i+j))/2) + 1) * 35)
				dis.SetColor(j, i, 0xFF<<24|col<<16|col<<8|col)
			}
		}

	case 2:
		for i := 0.0; i < rows; i++ {
			for j := 0.0; j < cols; j++ {
				r := math.Sqrt(math.Pow(j-float64(cols)/2, 2) + math.Pow(i-float64(rows)/2, 2))
				a := r * math.Cos(t*2) * 0.1
				dx := math.Sin(a)*(i*cellh-256) + math.Cos(a)*(j*cellw-256)
				dy := math.Cos(a)*(i*cellh-256) - math.Sin(a)*(j*cellw-256)
				dis.SetDisplacement(int(j), int(i), dx, dy, dist.DISP_CENTER)
				col := HGE.Dword((math.Cos(r+t*4) + 1) * 40)
				dis.SetColor(int(j), int(i), 0xFF<<24|col<<16|(col/2)<<8)
			}
		}
	}

	return 0
}

func RenderFunc() int {
	// Render graphics
	HGE.GfxBeginScene()
	HGE.GfxClear(0)
	dis.Render(meshx, meshy)
	fnt.Printf(5, 5, font.TEXT_LEFT, "dt:%.3f\nFPS:%d\n\nUse your\nSPACE!", HGE.NewTimer().Delta(), HGE.GetFPS())
	HGE.GfxEndScene()

	return 0
}

func main() {
	defer HGE.Free()

	HGE.SetState(HGE.LOGFILE, "tutorial05.log")
	HGE.SetState(HGE.FRAMEFUNC, FrameFunc)
	HGE.SetState(HGE.RENDERFUNC, RenderFunc)
	HGE.SetState(HGE.TITLE, "HGE Tutorial 05 - Using distortion mesh")
	HGE.SetState(HGE.WINDOWED, true)
	HGE.SetState(HGE.SCREENWIDTH, 800)
	HGE.SetState(HGE.SCREENHEIGHT, 600)
	HGE.SetState(HGE.SCREENBPP, 32)
	HGE.SetState(HGE.USESOUND, false)

	if err := HGE.Initiate(); err == nil {
		defer HGE.Shutdown()
		tex = HGE.LoadTexture("texture.jpg")
		if tex == 0 {
			fmt.Println("Error: Can't load texture.jpg")
			return
		}
		defer tex.Free()

		dis = dist.NewDistortionMesh(cols, rows)
		dis.SetTexture(tex)
		dis.SetTextureRect(0, 0, 512, 512)
		dis.SetBlendMode(HGE.BLEND_COLORADD | HGE.BLEND_ALPHABLEND | HGE.BLEND_ZWRITE)
		dis.Clear(HGE.Dword(0xFF000000))

		// Load a font
		fnt = font.NewFont("font1.fnt")

		if fnt == nil {
			fmt.Println("Error: Can't load font1.fnt or font1.png")
			return
		}

		// Let's rock now!
		HGE.Start()
	}
}
<|endoftext|>


<commit_before>package restpc

import (
	"fmt"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"

	"github.com/golang/mock/gomock"
)

func TestFromBody_GetString(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	mockReq := NewMockRequest(ctrl)
	var req Request = mockReq
	{
		mockReq.EXPECT().BodyMap().Return(nil, fmt.Errorf("unknown error"))
		value, err := FromBody.GetString(req, "name")
		assert.Nil(t, value)
		assert.EqualError(t, err, "unknown error")
	}
	{
		mockReq.EXPECT().BodyMap().Return(nil, nil)
		value, err := FromBody.GetString(req, "name")
		assert.Nil(t, value)
		assert.Nil(t, err)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"name": 123,
		}, nil)
		value, err := FromBody.GetString(req, "name")
		assert.Nil(t, value)
		assert.EqualError(t, err, "invalid 'name', must be string")
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"name": "John Smith",
		}, nil)
		value, err := FromBody.GetString(req, "name")
		assert.Equal(t, "John Smith", *value)
		assert.Nil(t, err)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"name": []byte("John Smith"),
		}, nil)
		value, err := FromBody.GetString(req, "name")
		assert.Equal(t, "John Smith", *value)
		assert.Nil(t, err)
	}
}
func TestFromBody_GetStringList(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	mockReq := NewMockRequest(ctrl)
	var req Request = mockReq
	{
		mockReq.EXPECT().BodyMap().Return(nil, fmt.Errorf("unknown error"))
		value, err := FromBody.GetStringList(req, "names")
		assert.Nil(t, value)
		assert.EqualError(t, err, "unknown error")
	}
	{
		mockReq.EXPECT().BodyMap().Return(nil, nil)
		value, err := FromBody.GetStringList(req, "names")
		assert.Nil(t, value)
		assert.Nil(t, err)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"names": 123,
		}, nil)
		value, err := FromBody.GetStringList(req, "names")
		assert.Nil(t, value)
		assert.EqualError(t, err, "invalid 'names', must be array of strings")
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"names": "John Smith",
		}, nil)
		value, err := FromBody.GetStringList(req, "names")
		assert.Nil(t, value)
		assert.EqualError(t, err, "invalid 'names', must be array of strings")
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"names": []interface{}{"John Smith", 1234},
		}, nil)
		value, err := FromBody.GetStringList(req, "names")
		assert.Nil(t, value)
		assert.EqualError(t, err, "invalid 'names', must be array of strings")
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"names": []string{"John Smith", "John Doe"},
		}, nil)
		value, err := FromBody.GetStringList(req, "names")
		assert.Equal(t, []string{"John Smith", "John Doe"}, value)
		assert.Nil(t, err)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"names": []interface{}{"John Smith", "John Doe"},
		}, nil)
		value, err := FromBody.GetStringList(req, "names")
		assert.Equal(t, []string{"John Smith", "John Doe"}, value)
		assert.Nil(t, err)
	}

}

func TestFromBody_GetInt(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	mockReq := NewMockRequest(ctrl)
	var req Request = mockReq
	{
		mockReq.EXPECT().BodyMap().Return(nil, fmt.Errorf("unknown error"))
		value, err := FromBody.GetInt(req, "count")
		assert.Nil(t, value)
		assert.EqualError(t, err, "unknown error")
	}
	{
		mockReq.EXPECT().BodyMap().Return(nil, nil)
		value, err := FromBody.GetInt(req, "count")
		assert.Nil(t, value)
		assert.Nil(t, err)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"count": "abc",
		}, nil)
		value, err := FromBody.GetInt(req, "count")
		assert.Nil(t, value)
		assert.EqualError(t, err, "invalid 'count', must be integer")
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"count": "345",
		}, nil)
		value, err := FromBody.GetInt(req, "count")
		assert.Nil(t, value)
		assert.EqualError(t, err, "invalid 'count', must be integer")
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"count": 5001,
		}, nil)
		value, err := FromBody.GetInt(req, "count")
		assert.Equal(t, 5001, *value)
		assert.Nil(t, err)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"count": int32(5003),
		}, nil)
		value, err := FromBody.GetInt(req, "count")
		assert.Equal(t, 5003, *value)
		assert.Nil(t, err)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"count": int64(6123),
		}, nil)
		value, err := FromBody.GetInt(req, "count")
		assert.Equal(t, 6123, *value)
		assert.Nil(t, err)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"count": 14.15,
		}, nil)
		value, err := FromBody.GetInt(req, "count")
		assert.Equal(t, 14, *value)
		assert.Nil(t, err)
	}
}

func TestFromBody_GetFloat(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	mockReq := NewMockRequest(ctrl)
	var req Request = mockReq
	{
		mockReq.EXPECT().BodyMap().Return(nil, fmt.Errorf("unknown error"))
		value, err := FromBody.GetFloat(req, "weight")
		assert.Nil(t, value)
		assert.EqualError(t, err, "unknown error")
	}
	{
		mockReq.EXPECT().BodyMap().Return(nil, nil)
		value, err := FromBody.GetFloat(req, "weight")
		assert.Nil(t, value)
		assert.Nil(t, err)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"weight": "abc",
		}, nil)
		value, err := FromBody.GetFloat(req, "weight")
		assert.Nil(t, value)
		assert.EqualError(t, err, "invalid 'weight', must be float")
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"weight": "345",
		}, nil)
		value, err := FromBody.GetFloat(req, "weight")
		assert.Nil(t, value)
		assert.EqualError(t, err, "invalid 'weight', must be float")
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"weight": 1231,
		}, nil)
		value, err := FromBody.GetFloat(req, "weight")
		assert.Equal(t, 1231.0, *value)
		assert.Nil(t, err)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"weight": int32(2345),
		}, nil)
		value, err := FromBody.GetFloat(req, "weight")
		assert.Equal(t, 2345.0, *value)
		assert.Nil(t, err)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"weight": int64(7123),
		}, nil)
		value, err := FromBody.GetFloat(req, "weight")
		assert.Equal(t, 7123.0, *value)
		assert.Nil(t, err)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"weight": 104.15,
		}, nil)
		value, err := FromBody.GetFloat(req, "weight")
		assert.Equal(t, 104.15, *value)
		assert.Nil(t, err)
	}
	{
		weight := float32(104.15)
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"weight": weight,
		}, nil)
		value, err := FromBody.GetFloat(req, "weight")
		assert.Equal(t, float64(weight), *value)
		assert.Nil(t, err)
	}
}

func TestFromBody_GetBool(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	mockReq := NewMockRequest(ctrl)
	var req Request = mockReq
	{
		mockReq.EXPECT().BodyMap().Return(nil, fmt.Errorf("unknown error"))
		value, err := FromBody.GetBool(req, "agree")
		assert.Nil(t, value)
		assert.EqualError(t, err, "unknown error")
	}
	{
		mockReq.EXPECT().BodyMap().Return(nil, nil)
		value, err := FromBody.GetBool(req, "agree")
		assert.Nil(t, value)
		assert.Nil(t, err)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"agree": "abcd",
		}, nil)
		value, err := FromBody.GetBool(req, "agree")
		assert.Nil(t, value)
		assert.EqualError(t, err, "invalid 'agree', must be true or false")
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"agree": "3465",
		}, nil)
		value, err := FromBody.GetBool(req, "agree")
		assert.Nil(t, value)
		assert.EqualError(t, err, "invalid 'agree', must be true or false")
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"agree": 1231,
		}, nil)
		value, err := FromBody.GetBool(req, "agree")
		assert.Nil(t, value)
		assert.EqualError(t, err, "invalid 'agree', must be true or false")
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"agree": true,
		}, nil)
		value, err := FromBody.GetBool(req, "agree")
		assert.Equal(t, true, *value)
		assert.Nil(t, err)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"agree": false,
		}, nil)
		value, err := FromBody.GetBool(req, "agree")
		assert.Equal(t, false, *value)
		assert.Nil(t, err)
	}
}

func TestFromBody_GetTime(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	mockReq := NewMockRequest(ctrl)
	var req Request = mockReq
	{
		mockReq.EXPECT().BodyMap().Return(nil, fmt.Errorf("unknown error"))
		value, err := FromBody.GetTime(req, "since")
		assert.Nil(t, value)
		assert.EqualError(t, err, "unknown error")
	}
	{
		mockReq.EXPECT().BodyMap().Return(nil, nil)
		value, err := FromBody.GetTime(req, "since")
		assert.Nil(t, value)
		assert.Nil(t, err)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"since": "abcd",
		}, nil)
		value, err := FromBody.GetTime(req, "since")
		assert.Nil(t, value)
		assert.EqualError(t, err, "invalid 'since', must be RFC3339 time string")
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"since": 3465,
		}, nil)
		value, err := FromBody.GetTime(req, "since")
		assert.Nil(t, value)
		assert.EqualError(t, err, "invalid 'since', must be RFC3339 time string")
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"since": "2017-12-20T17:30:00Z",
		}, nil)
		value, err := FromBody.GetTime(req, "since")
		assert.Nil(t, err)
		assert.Equal(t, time.Date(2017, time.Month(12), 20, 17, 30, 0, 0, time.UTC), *value)
	}
}
<commit_msg>from_body_test.go: check error before value<commit_after>package restpc

import (
	"fmt"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"

	"github.com/golang/mock/gomock"
)

func TestFromBody_GetString(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	mockReq := NewMockRequest(ctrl)
	var req Request = mockReq
	{
		mockReq.EXPECT().BodyMap().Return(nil, fmt.Errorf("unknown error"))
		value, err := FromBody.GetString(req, "name")
		assert.EqualError(t, err, "unknown error")
		assert.Nil(t, value)
	}
	{
		mockReq.EXPECT().BodyMap().Return(nil, nil)
		value, err := FromBody.GetString(req, "name")
		assert.Nil(t, err)
		assert.Nil(t, value)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"name": 123,
		}, nil)
		value, err := FromBody.GetString(req, "name")
		assert.EqualError(t, err, "invalid 'name', must be string")
		assert.Nil(t, value)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"name": "John Smith",
		}, nil)
		value, err := FromBody.GetString(req, "name")
		assert.Nil(t, err)
		assert.Equal(t, "John Smith", *value)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"name": []byte("John Smith"),
		}, nil)
		value, err := FromBody.GetString(req, "name")
		assert.Nil(t, err)
		assert.Equal(t, "John Smith", *value)
	}
}
func TestFromBody_GetStringList(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	mockReq := NewMockRequest(ctrl)
	var req Request = mockReq
	{
		mockReq.EXPECT().BodyMap().Return(nil, fmt.Errorf("unknown error"))
		value, err := FromBody.GetStringList(req, "names")
		assert.EqualError(t, err, "unknown error")
		assert.Nil(t, value)
	}
	{
		mockReq.EXPECT().BodyMap().Return(nil, nil)
		value, err := FromBody.GetStringList(req, "names")
		assert.Nil(t, err)
		assert.Nil(t, value)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"names": 123,
		}, nil)
		value, err := FromBody.GetStringList(req, "names")
		assert.EqualError(t, err, "invalid 'names', must be array of strings")
		assert.Nil(t, value)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"names": "John Smith",
		}, nil)
		value, err := FromBody.GetStringList(req, "names")
		assert.EqualError(t, err, "invalid 'names', must be array of strings")
		assert.Nil(t, value)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"names": []interface{}{"John Smith", 1234},
		}, nil)
		value, err := FromBody.GetStringList(req, "names")
		assert.EqualError(t, err, "invalid 'names', must be array of strings")
		assert.Nil(t, value)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"names": []string{"John Smith", "John Doe"},
		}, nil)
		value, err := FromBody.GetStringList(req, "names")
		assert.Nil(t, err)
		assert.Equal(t, []string{"John Smith", "John Doe"}, value)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"names": []interface{}{"John Smith", "John Doe"},
		}, nil)
		value, err := FromBody.GetStringList(req, "names")
		assert.Nil(t, err)
		assert.Equal(t, []string{"John Smith", "John Doe"}, value)
	}

}

func TestFromBody_GetInt(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	mockReq := NewMockRequest(ctrl)
	var req Request = mockReq
	{
		mockReq.EXPECT().BodyMap().Return(nil, fmt.Errorf("unknown error"))
		value, err := FromBody.GetInt(req, "count")
		assert.EqualError(t, err, "unknown error")
		assert.Nil(t, value)
	}
	{
		mockReq.EXPECT().BodyMap().Return(nil, nil)
		value, err := FromBody.GetInt(req, "count")
		assert.Nil(t, err)
		assert.Nil(t, value)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"count": "abc",
		}, nil)
		value, err := FromBody.GetInt(req, "count")
		assert.EqualError(t, err, "invalid 'count', must be integer")
		assert.Nil(t, value)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"count": "345",
		}, nil)
		value, err := FromBody.GetInt(req, "count")
		assert.EqualError(t, err, "invalid 'count', must be integer")
		assert.Nil(t, value)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"count": 5001,
		}, nil)
		value, err := FromBody.GetInt(req, "count")
		assert.Nil(t, err)
		assert.Equal(t, 5001, *value)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"count": int32(5003),
		}, nil)
		value, err := FromBody.GetInt(req, "count")
		assert.Nil(t, err)
		assert.Equal(t, 5003, *value)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"count": int64(6123),
		}, nil)
		value, err := FromBody.GetInt(req, "count")
		assert.Nil(t, err)
		assert.Equal(t, 6123, *value)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"count": 14.15,
		}, nil)
		value, err := FromBody.GetInt(req, "count")
		assert.Nil(t, err)
		assert.Equal(t, 14, *value)
	}
}

func TestFromBody_GetFloat(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	mockReq := NewMockRequest(ctrl)
	var req Request = mockReq
	{
		mockReq.EXPECT().BodyMap().Return(nil, fmt.Errorf("unknown error"))
		value, err := FromBody.GetFloat(req, "weight")
		assert.Nil(t, value)
		assert.EqualError(t, err, "unknown error")
	}
	{
		mockReq.EXPECT().BodyMap().Return(nil, nil)
		value, err := FromBody.GetFloat(req, "weight")
		assert.Nil(t, value)
		assert.Nil(t, err)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"weight": "abc",
		}, nil)
		value, err := FromBody.GetFloat(req, "weight")
		assert.Nil(t, value)
		assert.EqualError(t, err, "invalid 'weight', must be float")
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"weight": "345",
		}, nil)
		value, err := FromBody.GetFloat(req, "weight")
		assert.Nil(t, value)
		assert.EqualError(t, err, "invalid 'weight', must be float")
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"weight": 1231,
		}, nil)
		value, err := FromBody.GetFloat(req, "weight")
		assert.Equal(t, 1231.0, *value)
		assert.Nil(t, err)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"weight": int32(2345),
		}, nil)
		value, err := FromBody.GetFloat(req, "weight")
		assert.Equal(t, 2345.0, *value)
		assert.Nil(t, err)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"weight": int64(7123),
		}, nil)
		value, err := FromBody.GetFloat(req, "weight")
		assert.Equal(t, 7123.0, *value)
		assert.Nil(t, err)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"weight": 104.15,
		}, nil)
		value, err := FromBody.GetFloat(req, "weight")
		assert.Equal(t, 104.15, *value)
		assert.Nil(t, err)
	}
	{
		weight := float32(104.15)
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"weight": weight,
		}, nil)
		value, err := FromBody.GetFloat(req, "weight")
		assert.Equal(t, float64(weight), *value)
		assert.Nil(t, err)
	}
}

func TestFromBody_GetBool(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	mockReq := NewMockRequest(ctrl)
	var req Request = mockReq
	{
		mockReq.EXPECT().BodyMap().Return(nil, fmt.Errorf("unknown error"))
		value, err := FromBody.GetBool(req, "agree")
		assert.Nil(t, value)
		assert.EqualError(t, err, "unknown error")
	}
	{
		mockReq.EXPECT().BodyMap().Return(nil, nil)
		value, err := FromBody.GetBool(req, "agree")
		assert.Nil(t, value)
		assert.Nil(t, err)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"agree": "abcd",
		}, nil)
		value, err := FromBody.GetBool(req, "agree")
		assert.Nil(t, value)
		assert.EqualError(t, err, "invalid 'agree', must be true or false")
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"agree": "3465",
		}, nil)
		value, err := FromBody.GetBool(req, "agree")
		assert.Nil(t, value)
		assert.EqualError(t, err, "invalid 'agree', must be true or false")
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"agree": 1231,
		}, nil)
		value, err := FromBody.GetBool(req, "agree")
		assert.Nil(t, value)
		assert.EqualError(t, err, "invalid 'agree', must be true or false")
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"agree": true,
		}, nil)
		value, err := FromBody.GetBool(req, "agree")
		assert.Equal(t, true, *value)
		assert.Nil(t, err)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"agree": false,
		}, nil)
		value, err := FromBody.GetBool(req, "agree")
		assert.Equal(t, false, *value)
		assert.Nil(t, err)
	}
}

func TestFromBody_GetTime(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()
	mockReq := NewMockRequest(ctrl)
	var req Request = mockReq
	{
		mockReq.EXPECT().BodyMap().Return(nil, fmt.Errorf("unknown error"))
		value, err := FromBody.GetTime(req, "since")
		assert.Nil(t, value)
		assert.EqualError(t, err, "unknown error")
	}
	{
		mockReq.EXPECT().BodyMap().Return(nil, nil)
		value, err := FromBody.GetTime(req, "since")
		assert.Nil(t, value)
		assert.Nil(t, err)
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"since": "abcd",
		}, nil)
		value, err := FromBody.GetTime(req, "since")
		assert.Nil(t, value)
		assert.EqualError(t, err, "invalid 'since', must be RFC3339 time string")
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"since": 3465,
		}, nil)
		value, err := FromBody.GetTime(req, "since")
		assert.Nil(t, value)
		assert.EqualError(t, err, "invalid 'since', must be RFC3339 time string")
	}
	{
		mockReq.EXPECT().BodyMap().Return(map[string]interface{}{
			"since": "2017-12-20T17:30:00Z",
		}, nil)
		value, err := FromBody.GetTime(req, "since")
		assert.Nil(t, err)
		assert.Equal(t, time.Date(2017, time.Month(12), 20, 17, 30, 0, 0, time.UTC), *value)
	}
}
<|endoftext|>


<commit_before>package gedis

import (
	"fmt"
	"path"
	"runtime"
	"testing"
)

func e(t *testing.T, skip int, format string, args ...interface{}) {
	_, file, ln, _ := runtime.Caller(skip + 1)
	t.Errorf("\r\t%s:%d: %s", path.Base(file), ln, fmt.Sprintf(format, args...))
}

func assertStringEq(t *testing.T, skip int, expected string, actual interface{}) {
	if value, ok := actual.(string); ok {
		if expected != value {
			e(t, skip, "assertStringEq()\nExpected %q\rReturned %q", expected, value)
			t.FailNow()
		}
	} else {
		e(t, skip, "assertStringEq(): Cannot convert to string: %#v\n", actual)
		t.FailNow()
	}
}

func assertIntegerEq(t *testing.T, skip int, expected int, actual interface{}) {
	if value, ok := actual.(int); ok {
		if expected != value {
			e(t, skip, "assertIntegerEq()\nExpected %#v\nReturned %#v", expected, value)
			t.FailNow()
		}
	} else {
		e(t, skip, "assertIntegerEq(): Cannot convert to int: %#v", actual)
		t.FailNow()
	}
}

func assertNotError(t *testing.T, skip int, err error) {
	if err != nil {
		e(t, skip, "assertNotError(): Returned unexpected error: %v", err)
	}
}
<commit_msg>FIx assertNotError test helper<commit_after>package gedis

import (
	"fmt"
	"path"
	"runtime"
	"testing"
)

func e(t *testing.T, skip int, format string, args ...interface{}) {
	_, file, ln, _ := runtime.Caller(skip + 1)
	t.Errorf("\r\t%s:%d: %s", path.Base(file), ln, fmt.Sprintf(format, args...))
}

func assertStringEq(t *testing.T, skip int, expected string, actual interface{}) {
	if value, ok := actual.(string); ok {
		if expected != value {
			e(t, skip, "assertStringEq()\nExpected %q\rReturned %q", expected, value)
			t.FailNow()
		}
	} else {
		e(t, skip, "assertStringEq(): Cannot convert to string: %#v\n", actual)
		t.FailNow()
	}
}

func assertIntegerEq(t *testing.T, skip int, expected int, actual interface{}) {
	if value, ok := actual.(int); ok {
		if expected != value {
			e(t, skip, "assertIntegerEq()\nExpected %#v\nReturned %#v", expected, value)
			t.FailNow()
		}
	} else {
		e(t, skip, "assertIntegerEq(): Cannot convert to int: %#v", actual)
		t.FailNow()
	}
}

func assertNotError(t *testing.T, skip int, err error) {
	if err != nil {
		e(t, skip, "assertNotError(): unexpected error: %v", err)
		t.FailNow()
	}
}
<|endoftext|>


<commit_before><commit_msg>Added TimeDeltaLogger for dev/debug use.<commit_after><|endoftext|>


<commit_before>package fs

// This test requires an unlocked android MTP device plugged in.

import (
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"os"
	"path/filepath"
	"testing"
	"time"

	"github.com/hanwen/go-fuse/fuse"
	"github.com/hanwen/go-mtpfs/mtp"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

func TestDevice(t *testing.T) {
	dev, err := mtp.SelectDevice("")
	if err != nil {
		t.Fatalf("detect failed: %v", err)
	}
	defer dev.Close()

	if err = dev.OpenSession(); err != nil {
		t.Fatalf("OpenSession failed: %v", err)
	}

	sids, err := SelectStorages(dev, "")
	if err != nil {
		t.Fatalf("selectStorages failed: %v", err)
	}

	tempdir, err := ioutil.TempDir("", "mtpfs")
	if err != nil {
		t.Fatal(err)
	}
	opts := DeviceFsOptions{}
	fs, err := NewDeviceFs(dev, sids, opts)
	conn := fuse.NewFileSystemConnector(fs, fuse.NewFileSystemOptions())
	rawFs := fuse.NewLockingRawFileSystem(conn)
	mount := fuse.NewMountState(rawFs)
	if err := mount.Mount(tempdir, nil); err != nil {
		t.Fatalf("mount failed: %v", err)
	}

	mount.Debug = true
	dev.DebugPrint = true

	defer mount.Unmount()
	go mount.Loop()

	var root string
	for i := 0; i < 10; i++ {
		fis, err := ioutil.ReadDir(tempdir)
		if err != nil || len(fis) == 0 {
			time.Sleep(1)
			continue
		}

		root = filepath.Join(tempdir, fis[0].Name())
		break
		if i == 9 {
			t.Fatal("mount unsuccessful")
		}
	}

	_, err = os.Lstat(root + "/Music")
	if err != nil {
		t.Fatal("Music not found", err)
	}

	name := filepath.Join(root, fmt.Sprintf("mtpfs-test-%x", rand.Int31()))
	golden := "abcpxq134"
	if err := ioutil.WriteFile(name, []byte("abcpxq134"), 0644); err != nil {
		t.Fatal(err)
	}
	got, err := ioutil.ReadFile(name)
	if err != nil {
		t.Fatal("ReadFile failed", err)
	}

	if string(got) != golden {
		t.Fatalf("got %q, want %q", got, golden)
	}

	f, err := os.OpenFile(name, os.O_RDWR|os.O_APPEND, 0644)
	if err != nil {
		t.Fatal("OpenFile failed:", err)
	}

	log.Println("writing...")
	golden += "hello"
	f.Write([]byte("hello"))
	log.Println("done...")
	f.Close()

	got, err = ioutil.ReadFile(name)
	if err != nil {
		t.Fatal("ReadFile failed", err)
	}

	if string(got) != golden {
		t.Fatalf("got %q, want %q", got, golden)
	}

	newName := filepath.Join(root, fmt.Sprintf("mtpfs-test-%x", rand.Int31()))
	err = os.Rename(name, newName)
	if err != nil {
		t.Fatal("Rename failed", err)
	}

	if fi, err := os.Lstat(name); err == nil {
		t.Fatal("should have disappeared after rename", fi)
	}

	if _, err := os.Lstat(newName); err != nil {
		t.Fatal("should be able to stat after rename", err)
	}

	err = os.Remove(newName)
	if err != nil {
		t.Fatal("Remove failed", err)
	}
	if fi, err := os.Lstat(newName); err == nil {
		t.Fatal("should have disappeared after Remove", fi)
	}
}
<commit_msg>Polish for fs/device_test.<commit_after>package fs

// This test requires an unlocked android MTP device plugged in.

import (
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"os"
	"path/filepath"
	"testing"
	"time"

	"github.com/hanwen/go-fuse/fuse"
	"github.com/hanwen/go-mtpfs/mtp"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

func TestDevice(t *testing.T) {
	dev, err := mtp.SelectDevice("")
	if err != nil {
		t.Fatalf("detect failed: %v", err)
	}
	defer dev.Close()

	if err = dev.OpenSession(); err != nil {
		t.Fatalf("OpenSession failed: %v", err)
	}

	sids, err := SelectStorages(dev, "")
	if err != nil {
		t.Fatalf("selectStorages failed: %v", err)
	}
	if len(sids) == 0 {
		t.Fatal("no storages found. Unlock device?")
	}
	tempdir, err := ioutil.TempDir("", "mtpfs")
	if err != nil {
		t.Fatal(err)
	}
	opts := DeviceFsOptions{}
	fs, err := NewDeviceFs(dev, sids, opts)
	if err != nil {
		t.Fatal("NewDeviceFs failed:", err)
	}
	conn := fuse.NewFileSystemConnector(fs, fuse.NewFileSystemOptions())
	rawFs := fuse.NewLockingRawFileSystem(conn)
	mount := fuse.NewMountState(rawFs)
	if err := mount.Mount(tempdir, nil); err != nil {
		t.Fatalf("mount failed: %v", err)
	}

	mount.Debug = true
	dev.DebugPrint = true

	defer mount.Unmount()
	go mount.Loop()

	var root string
	for i := 0; i < 10; i++ {
		fis, err := ioutil.ReadDir(tempdir)
		if err == nil && len(fis) > 0 {
			root = filepath.Join(tempdir, fis[0].Name())
			t.Log("found entry", fis[0].Name())
			break;
		}
		time.Sleep(1)
	}

	_, err = os.Lstat(root + "/Music")
	if err != nil {
		t.Fatal("Music not found", err)
	}

	name := filepath.Join(root, fmt.Sprintf("mtpfs-test-%x", rand.Int31()))
	golden := "abcpxq134"
	if err := ioutil.WriteFile(name, []byte("abcpxq134"), 0644); err != nil {
		t.Fatal(err)
	}
	got, err := ioutil.ReadFile(name)
	if err != nil {
		t.Fatal("ReadFile failed", err)
	}

	if string(got) != golden {
		t.Fatalf("got %q, want %q", got, golden)
	}

	f, err := os.OpenFile(name, os.O_RDWR|os.O_APPEND, 0644)
	if err != nil {
		t.Fatal("OpenFile failed:", err)
	}

	log.Println("writing...")
	golden += "hello"
	f.Write([]byte("hello"))
	log.Println("done...")
	f.Close()

	got, err = ioutil.ReadFile(name)
	if err != nil {
		t.Fatal("ReadFile failed", err)
	}

	if string(got) != golden {
		t.Fatalf("got %q, want %q", got, golden)
	}

	newName := filepath.Join(root, fmt.Sprintf("mtpfs-test-%x", rand.Int31()))
	err = os.Rename(name, newName)
	if err != nil {
		t.Fatal("Rename failed", err)
	}

	if fi, err := os.Lstat(name); err == nil {
		t.Fatal("should have disappeared after rename", fi)
	}

	if _, err := os.Lstat(newName); err != nil {
		t.Fatal("should be able to stat after rename", err)
	}

	err = os.Remove(newName)
	if err != nil {
		t.Fatal("Remove failed", err)
	}
	if fi, err := os.Lstat(newName); err == nil {
		t.Fatal("should have disappeared after Remove", fi)
	}
}
<|endoftext|>


<commit_before>// Package fshttp contains the common http parts of the config, Transport and Client
package fshttp

import (
	"bytes"
	"context"
	"crypto/tls"
	"crypto/x509"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/http/cookiejar"
	"net/http/httputil"
	"reflect"
	"sync"
	"time"

	"github.com/rclone/rclone/fs"
	"golang.org/x/net/publicsuffix"
	"golang.org/x/time/rate"
)

const (
	separatorReq  = ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
	separatorResp = "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
)

var (
	transport    http.RoundTripper
	noTransport  = new(sync.Once)
	tpsBucket    *rate.Limiter // for limiting number of http transactions per second
	cookieJar, _ = cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
)

// StartHTTPTokenBucket starts the token bucket if necessary
func StartHTTPTokenBucket() {
	if fs.Config.TPSLimit > 0 {
		tpsBurst := fs.Config.TPSLimitBurst
		if tpsBurst < 1 {
			tpsBurst = 1
		}
		tpsBucket = rate.NewLimiter(rate.Limit(fs.Config.TPSLimit), tpsBurst)
		fs.Infof(nil, "Starting HTTP transaction limiter: max %g transactions/s with burst %d", fs.Config.TPSLimit, tpsBurst)
	}
}

// A net.Conn that sets a deadline for every Read or Write operation
type timeoutConn struct {
	net.Conn
	timeout time.Duration
}

// create a timeoutConn using the timeout
func newTimeoutConn(conn net.Conn, timeout time.Duration) (c *timeoutConn, err error) {
	c = &timeoutConn{
		Conn:    conn,
		timeout: timeout,
	}
	err = c.nudgeDeadline()
	return
}

// Nudge the deadline for an idle timeout on by c.timeout if non-zero
func (c *timeoutConn) nudgeDeadline() (err error) {
	if c.timeout == 0 {
		return nil
	}
	when := time.Now().Add(c.timeout)
	return c.Conn.SetDeadline(when)
}

// readOrWrite bytes doing idle timeouts
func (c *timeoutConn) readOrWrite(f func([]byte) (int, error), b []byte) (n int, err error) {
	n, err = f(b)
	// Don't nudge if no bytes or an error
	if n == 0 || err != nil {
		return
	}
	// Nudge the deadline on successful Read or Write
	err = c.nudgeDeadline()
	return
}

// Read bytes doing idle timeouts
func (c *timeoutConn) Read(b []byte) (n int, err error) {
	return c.readOrWrite(c.Conn.Read, b)
}

// Write bytes doing idle timeouts
func (c *timeoutConn) Write(b []byte) (n int, err error) {
	return c.readOrWrite(c.Conn.Write, b)
}

// setDefaults for a from b
//
// Copy the public members from b to a.  We can't just use a struct
// copy as Transport contains a private mutex.
func setDefaults(a, b interface{}) {
	pt := reflect.TypeOf(a)
	t := pt.Elem()
	va := reflect.ValueOf(a).Elem()
	vb := reflect.ValueOf(b).Elem()
	for i := 0; i < t.NumField(); i++ {
		aField := va.Field(i)
		// Set a from b if it is public
		if aField.CanSet() {
			bField := vb.Field(i)
			aField.Set(bField)
		}
	}
}

// dial with context and timeouts
func dialContextTimeout(ctx context.Context, network, address string, ci *fs.ConfigInfo) (net.Conn, error) {
	dialer := NewDialer(ci)
	c, err := dialer.DialContext(ctx, network, address)
	if err != nil {
		return c, err
	}
	return newTimeoutConn(c, ci.Timeout)
}

// ResetTransport resets the existing transport, allowing it to take new settings.
// Should only be used for testing.
func ResetTransport() {
	noTransport = new(sync.Once)
}

// NewTransport returns an http.RoundTripper with the correct timeouts
func NewTransport(ci *fs.ConfigInfo) http.RoundTripper {
	(*noTransport).Do(func() {
		// Start with a sensible set of defaults then override.
		// This also means we get new stuff when it gets added to go
		t := new(http.Transport)
		setDefaults(t, http.DefaultTransport.(*http.Transport))
		t.Proxy = http.ProxyFromEnvironment
		t.MaxIdleConnsPerHost = 2 * (ci.Checkers + ci.Transfers + 1)
		t.MaxIdleConns = 2 * t.MaxIdleConnsPerHost
		t.TLSHandshakeTimeout = ci.ConnectTimeout
		t.ResponseHeaderTimeout = ci.Timeout

		// TLS Config
		t.TLSClientConfig = &tls.Config{
			InsecureSkipVerify: ci.InsecureSkipVerify,
		}

		// Load client certs
		if ci.ClientCert != "" || ci.ClientKey != "" {
			if ci.ClientCert == "" || ci.ClientKey == "" {
				log.Fatalf("Both --client-cert and --client-key must be set")
			}
			cert, err := tls.LoadX509KeyPair(ci.ClientCert, ci.ClientKey)
			if err != nil {
				log.Fatalf("Failed to load --client-cert/--client-key pair: %v", err)
			}
			t.TLSClientConfig.Certificates = []tls.Certificate{cert}
			t.TLSClientConfig.BuildNameToCertificate()
		}

		// Load CA cert
		if ci.CaCert != "" {
			caCert, err := ioutil.ReadFile(ci.CaCert)
			if err != nil {
				log.Fatalf("Failed to read --ca-cert: %v", err)
			}
			caCertPool := x509.NewCertPool()
			ok := caCertPool.AppendCertsFromPEM(caCert)
			if !ok {
				log.Fatalf("Failed to add certificates from --ca-cert")
			}
			t.TLSClientConfig.RootCAs = caCertPool
		}

		t.DisableCompression = ci.NoGzip
		t.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
			return dialContextTimeout(ctx, network, addr, ci)
		}
		t.IdleConnTimeout = 60 * time.Second
		t.ExpectContinueTimeout = ci.ConnectTimeout
		// Wrap that http.Transport in our own transport
		transport = newTransport(ci, t)
	})
	return transport
}

// NewClient returns an http.Client with the correct timeouts
func NewClient(ci *fs.ConfigInfo) *http.Client {
	transport := &http.Client{
		Transport: NewTransport(ci),
	}
	if ci.Cookie {
		transport.Jar = cookieJar
	}
	return transport
}

// Transport is a our http Transport which wraps an http.Transport
// * Sets the User Agent
// * Does logging
type Transport struct {
	*http.Transport
	dump          fs.DumpFlags
	filterRequest func(req *http.Request)
	userAgent     string
}

// newTransport wraps the http.Transport passed in and logs all
// roundtrips including the body if logBody is set.
func newTransport(ci *fs.ConfigInfo, transport *http.Transport) *Transport {
	return &Transport{
		Transport: transport,
		dump:      ci.Dump,
		userAgent: ci.UserAgent,
	}
}

// SetRequestFilter sets a filter to be used on each request
func (t *Transport) SetRequestFilter(f func(req *http.Request)) {
	t.filterRequest = f
}

// A mutex to protect this map
var checkedHostMu sync.RWMutex

// A map of servers we have checked for time
var checkedHost = make(map[string]struct{}, 1)

// Check the server time is the same as ours, once for each server
func checkServerTime(req *http.Request, resp *http.Response) {
	host := req.URL.Host
	if req.Host != "" {
		host = req.Host
	}
	checkedHostMu.RLock()
	_, ok := checkedHost[host]
	checkedHostMu.RUnlock()
	if ok {
		return
	}
	dateString := resp.Header.Get("Date")
	if dateString == "" {
		return
	}
	date, err := http.ParseTime(dateString)
	if err != nil {
		fs.Debugf(nil, "Couldn't parse Date: from server %s: %q: %v", host, dateString, err)
		return
	}
	dt := time.Since(date)
	const window = 5 * 60 * time.Second
	if dt > window || dt < -window {
		fs.Logf(nil, "Time may be set wrong - time from %q is %v different from this computer", host, dt)
	}
	checkedHostMu.Lock()
	checkedHost[host] = struct{}{}
	checkedHostMu.Unlock()
}

// cleanAuth gets rid of one authBuf header within the first 4k
func cleanAuth(buf, authBuf []byte) []byte {
	// Find how much buffer to check
	n := 4096
	if len(buf) < n {
		n = len(buf)
	}
	// See if there is an Authorization: header
	i := bytes.Index(buf[:n], authBuf)
	if i < 0 {
		return buf
	}
	i += len(authBuf)
	// Overwrite the next 4 chars with 'X'
	for j := 0; i < len(buf) && j < 4; j++ {
		if buf[i] == '\n' {
			break
		}
		buf[i] = 'X'
		i++
	}
	// Snip out to the next '\n'
	j := bytes.IndexByte(buf[i:], '\n')
	if j < 0 {
		return buf[:i]
	}
	n = copy(buf[i:], buf[i+j:])
	return buf[:i+n]
}

var authBufs = [][]byte{
	[]byte("Authorization: "),
	[]byte("X-Auth-Token: "),
}

// cleanAuths gets rid of all the possible Auth headers
func cleanAuths(buf []byte) []byte {
	for _, authBuf := range authBufs {
		buf = cleanAuth(buf, authBuf)
	}
	return buf
}

// RoundTrip implements the RoundTripper interface.
func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
	// Get transactions per second token first if limiting
	if tpsBucket != nil {
		tbErr := tpsBucket.Wait(req.Context())
		if tbErr != nil {
			fs.Errorf(nil, "HTTP token bucket error: %v", err)
		}
	}
	// Force user agent
	req.Header.Set("User-Agent", t.userAgent)
	// Filter the request if required
	if t.filterRequest != nil {
		t.filterRequest(req)
	}
	// Logf request
	if t.dump&(fs.DumpHeaders|fs.DumpBodies|fs.DumpAuth|fs.DumpRequests|fs.DumpResponses) != 0 {
		buf, _ := httputil.DumpRequestOut(req, t.dump&(fs.DumpBodies|fs.DumpRequests) != 0)
		if t.dump&fs.DumpAuth == 0 {
			buf = cleanAuths(buf)
		}
		fs.Debugf(nil, "%s", separatorReq)
		fs.Debugf(nil, "%s (req %p)", "HTTP REQUEST", req)
		fs.Debugf(nil, "%s", string(buf))
		fs.Debugf(nil, "%s", separatorReq)
	}
	// Do round trip
	resp, err = t.Transport.RoundTrip(req)
	// Logf response
	if t.dump&(fs.DumpHeaders|fs.DumpBodies|fs.DumpAuth|fs.DumpRequests|fs.DumpResponses) != 0 {
		fs.Debugf(nil, "%s", separatorResp)
		fs.Debugf(nil, "%s (req %p)", "HTTP RESPONSE", req)
		if err != nil {
			fs.Debugf(nil, "Error: %v", err)
		} else {
			buf, _ := httputil.DumpResponse(resp, t.dump&(fs.DumpBodies|fs.DumpResponses) != 0)
			fs.Debugf(nil, "%s", string(buf))
		}
		fs.Debugf(nil, "%s", separatorResp)
	}
	if err == nil {
		checkServerTime(req, resp)
	}
	return resp, err
}

// NewDialer creates a net.Dialer structure with Timeout, Keepalive
// and LocalAddr set from rclone flags.
func NewDialer(ci *fs.ConfigInfo) *net.Dialer {
	dialer := &net.Dialer{
		Timeout:   ci.ConnectTimeout,
		KeepAlive: 30 * time.Second,
	}
	if ci.BindAddr != nil {
		dialer.LocalAddr = &net.TCPAddr{IP: ci.BindAddr}
	}
	return dialer
}
<commit_msg>fshttp: allow Transport to be customized #3631<commit_after>// Package fshttp contains the common http parts of the config, Transport and Client
package fshttp

import (
	"bytes"
	"context"
	"crypto/tls"
	"crypto/x509"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/http/cookiejar"
	"net/http/httputil"
	"reflect"
	"sync"
	"time"

	"github.com/rclone/rclone/fs"
	"golang.org/x/net/publicsuffix"
	"golang.org/x/time/rate"
)

const (
	separatorReq  = ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
	separatorResp = "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
)

var (
	transport    http.RoundTripper
	noTransport  = new(sync.Once)
	tpsBucket    *rate.Limiter // for limiting number of http transactions per second
	cookieJar, _ = cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
)

// StartHTTPTokenBucket starts the token bucket if necessary
func StartHTTPTokenBucket() {
	if fs.Config.TPSLimit > 0 {
		tpsBurst := fs.Config.TPSLimitBurst
		if tpsBurst < 1 {
			tpsBurst = 1
		}
		tpsBucket = rate.NewLimiter(rate.Limit(fs.Config.TPSLimit), tpsBurst)
		fs.Infof(nil, "Starting HTTP transaction limiter: max %g transactions/s with burst %d", fs.Config.TPSLimit, tpsBurst)
	}
}

// A net.Conn that sets a deadline for every Read or Write operation
type timeoutConn struct {
	net.Conn
	timeout time.Duration
}

// create a timeoutConn using the timeout
func newTimeoutConn(conn net.Conn, timeout time.Duration) (c *timeoutConn, err error) {
	c = &timeoutConn{
		Conn:    conn,
		timeout: timeout,
	}
	err = c.nudgeDeadline()
	return
}

// Nudge the deadline for an idle timeout on by c.timeout if non-zero
func (c *timeoutConn) nudgeDeadline() (err error) {
	if c.timeout == 0 {
		return nil
	}
	when := time.Now().Add(c.timeout)
	return c.Conn.SetDeadline(when)
}

// readOrWrite bytes doing idle timeouts
func (c *timeoutConn) readOrWrite(f func([]byte) (int, error), b []byte) (n int, err error) {
	n, err = f(b)
	// Don't nudge if no bytes or an error
	if n == 0 || err != nil {
		return
	}
	// Nudge the deadline on successful Read or Write
	err = c.nudgeDeadline()
	return
}

// Read bytes doing idle timeouts
func (c *timeoutConn) Read(b []byte) (n int, err error) {
	return c.readOrWrite(c.Conn.Read, b)
}

// Write bytes doing idle timeouts
func (c *timeoutConn) Write(b []byte) (n int, err error) {
	return c.readOrWrite(c.Conn.Write, b)
}

// setDefaults for a from b
//
// Copy the public members from b to a.  We can't just use a struct
// copy as Transport contains a private mutex.
func setDefaults(a, b interface{}) {
	pt := reflect.TypeOf(a)
	t := pt.Elem()
	va := reflect.ValueOf(a).Elem()
	vb := reflect.ValueOf(b).Elem()
	for i := 0; i < t.NumField(); i++ {
		aField := va.Field(i)
		// Set a from b if it is public
		if aField.CanSet() {
			bField := vb.Field(i)
			aField.Set(bField)
		}
	}
}

// dial with context and timeouts
func dialContextTimeout(ctx context.Context, network, address string, ci *fs.ConfigInfo) (net.Conn, error) {
	dialer := NewDialer(ci)
	c, err := dialer.DialContext(ctx, network, address)
	if err != nil {
		return c, err
	}
	return newTimeoutConn(c, ci.Timeout)
}

// ResetTransport resets the existing transport, allowing it to take new settings.
// Should only be used for testing.
func ResetTransport() {
	noTransport = new(sync.Once)
}

// NewTransportCustom returns an http.RoundTripper with the correct timeouts.
// The customize function is called if set to give the caller an opportunity to
// customize any defaults in the Transport.
func NewTransportCustom(ci *fs.ConfigInfo, customize func(*http.Transport)) http.RoundTripper {
	// Start with a sensible set of defaults then override.
	// This also means we get new stuff when it gets added to go
	t := new(http.Transport)
	setDefaults(t, http.DefaultTransport.(*http.Transport))
	t.Proxy = http.ProxyFromEnvironment
	t.MaxIdleConnsPerHost = 2 * (ci.Checkers + ci.Transfers + 1)
	t.MaxIdleConns = 2 * t.MaxIdleConnsPerHost
	t.TLSHandshakeTimeout = ci.ConnectTimeout
	t.ResponseHeaderTimeout = ci.Timeout

	// TLS Config
	t.TLSClientConfig = &tls.Config{
		InsecureSkipVerify: ci.InsecureSkipVerify,
	}

	// Load client certs
	if ci.ClientCert != "" || ci.ClientKey != "" {
		if ci.ClientCert == "" || ci.ClientKey == "" {
			log.Fatalf("Both --client-cert and --client-key must be set")
		}
		cert, err := tls.LoadX509KeyPair(ci.ClientCert, ci.ClientKey)
		if err != nil {
			log.Fatalf("Failed to load --client-cert/--client-key pair: %v", err)
		}
		t.TLSClientConfig.Certificates = []tls.Certificate{cert}
		t.TLSClientConfig.BuildNameToCertificate()
	}

	// Load CA cert
	if ci.CaCert != "" {
		caCert, err := ioutil.ReadFile(ci.CaCert)
		if err != nil {
			log.Fatalf("Failed to read --ca-cert: %v", err)
		}
		caCertPool := x509.NewCertPool()
		ok := caCertPool.AppendCertsFromPEM(caCert)
		if !ok {
			log.Fatalf("Failed to add certificates from --ca-cert")
		}
		t.TLSClientConfig.RootCAs = caCertPool
	}

	t.DisableCompression = ci.NoGzip
	t.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
		return dialContextTimeout(ctx, network, addr, ci)
	}
	t.IdleConnTimeout = 60 * time.Second
	t.ExpectContinueTimeout = ci.ConnectTimeout

	// customize the transport if required
	if customize != nil {
		customize(t)
	}

	// Wrap that http.Transport in our own transport
	return newTransport(ci, t)
}

// NewTransport returns an http.RoundTripper with the correct timeouts
func NewTransport(ci *fs.ConfigInfo) http.RoundTripper {
	(*noTransport).Do(func() {
		transport = NewTransportCustom(ci, nil)
	})
	return transport
}

// NewClient returns an http.Client with the correct timeouts
func NewClient(ci *fs.ConfigInfo) *http.Client {
	client := &http.Client{
		Transport: NewTransport(ci),
	}
	if ci.Cookie {
		client.Jar = cookieJar
	}
	return client
}

// Transport is a our http Transport which wraps an http.Transport
// * Sets the User Agent
// * Does logging
type Transport struct {
	*http.Transport
	dump          fs.DumpFlags
	filterRequest func(req *http.Request)
	userAgent     string
}

// newTransport wraps the http.Transport passed in and logs all
// roundtrips including the body if logBody is set.
func newTransport(ci *fs.ConfigInfo, transport *http.Transport) *Transport {
	return &Transport{
		Transport: transport,
		dump:      ci.Dump,
		userAgent: ci.UserAgent,
	}
}

// SetRequestFilter sets a filter to be used on each request
func (t *Transport) SetRequestFilter(f func(req *http.Request)) {
	t.filterRequest = f
}

// A mutex to protect this map
var checkedHostMu sync.RWMutex

// A map of servers we have checked for time
var checkedHost = make(map[string]struct{}, 1)

// Check the server time is the same as ours, once for each server
func checkServerTime(req *http.Request, resp *http.Response) {
	host := req.URL.Host
	if req.Host != "" {
		host = req.Host
	}
	checkedHostMu.RLock()
	_, ok := checkedHost[host]
	checkedHostMu.RUnlock()
	if ok {
		return
	}
	dateString := resp.Header.Get("Date")
	if dateString == "" {
		return
	}
	date, err := http.ParseTime(dateString)
	if err != nil {
		fs.Debugf(nil, "Couldn't parse Date: from server %s: %q: %v", host, dateString, err)
		return
	}
	dt := time.Since(date)
	const window = 5 * 60 * time.Second
	if dt > window || dt < -window {
		fs.Logf(nil, "Time may be set wrong - time from %q is %v different from this computer", host, dt)
	}
	checkedHostMu.Lock()
	checkedHost[host] = struct{}{}
	checkedHostMu.Unlock()
}

// cleanAuth gets rid of one authBuf header within the first 4k
func cleanAuth(buf, authBuf []byte) []byte {
	// Find how much buffer to check
	n := 4096
	if len(buf) < n {
		n = len(buf)
	}
	// See if there is an Authorization: header
	i := bytes.Index(buf[:n], authBuf)
	if i < 0 {
		return buf
	}
	i += len(authBuf)
	// Overwrite the next 4 chars with 'X'
	for j := 0; i < len(buf) && j < 4; j++ {
		if buf[i] == '\n' {
			break
		}
		buf[i] = 'X'
		i++
	}
	// Snip out to the next '\n'
	j := bytes.IndexByte(buf[i:], '\n')
	if j < 0 {
		return buf[:i]
	}
	n = copy(buf[i:], buf[i+j:])
	return buf[:i+n]
}

var authBufs = [][]byte{
	[]byte("Authorization: "),
	[]byte("X-Auth-Token: "),
}

// cleanAuths gets rid of all the possible Auth headers
func cleanAuths(buf []byte) []byte {
	for _, authBuf := range authBufs {
		buf = cleanAuth(buf, authBuf)
	}
	return buf
}

// RoundTrip implements the RoundTripper interface.
func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
	// Get transactions per second token first if limiting
	if tpsBucket != nil {
		tbErr := tpsBucket.Wait(req.Context())
		if tbErr != nil {
			fs.Errorf(nil, "HTTP token bucket error: %v", err)
		}
	}
	// Force user agent
	req.Header.Set("User-Agent", t.userAgent)
	// Filter the request if required
	if t.filterRequest != nil {
		t.filterRequest(req)
	}
	// Logf request
	if t.dump&(fs.DumpHeaders|fs.DumpBodies|fs.DumpAuth|fs.DumpRequests|fs.DumpResponses) != 0 {
		buf, _ := httputil.DumpRequestOut(req, t.dump&(fs.DumpBodies|fs.DumpRequests) != 0)
		if t.dump&fs.DumpAuth == 0 {
			buf = cleanAuths(buf)
		}
		fs.Debugf(nil, "%s", separatorReq)
		fs.Debugf(nil, "%s (req %p)", "HTTP REQUEST", req)
		fs.Debugf(nil, "%s", string(buf))
		fs.Debugf(nil, "%s", separatorReq)
	}
	// Do round trip
	resp, err = t.Transport.RoundTrip(req)
	// Logf response
	if t.dump&(fs.DumpHeaders|fs.DumpBodies|fs.DumpAuth|fs.DumpRequests|fs.DumpResponses) != 0 {
		fs.Debugf(nil, "%s", separatorResp)
		fs.Debugf(nil, "%s (req %p)", "HTTP RESPONSE", req)
		if err != nil {
			fs.Debugf(nil, "Error: %v", err)
		} else {
			buf, _ := httputil.DumpResponse(resp, t.dump&(fs.DumpBodies|fs.DumpResponses) != 0)
			fs.Debugf(nil, "%s", string(buf))
		}
		fs.Debugf(nil, "%s", separatorResp)
	}
	if err == nil {
		checkServerTime(req, resp)
	}
	return resp, err
}

// NewDialer creates a net.Dialer structure with Timeout, Keepalive
// and LocalAddr set from rclone flags.
func NewDialer(ci *fs.ConfigInfo) *net.Dialer {
	dialer := &net.Dialer{
		Timeout:   ci.ConnectTimeout,
		KeepAlive: 30 * time.Second,
	}
	if ci.BindAddr != nil {
		dialer.LocalAddr = &net.TCPAddr{IP: ci.BindAddr}
	}
	return dialer
}
<|endoftext|>