<commit_before>package goose

import (
	"github.com/advancedlogic/gojs-config"
	"time"
)

// Configuration is a wrapper for various config options
type Configuration struct {
	localStoragePath        string //not used in this version
	imagesMinBytes          int    //not used in this version
	enableImageFetching     bool
	useMetaLanguage         bool
	targetLanguage          string
	imageMagickConvertPath  string //not used in this version
	imageMagickIdentifyPath string //not used in this version
	browserUserAgent        string
	debug                   bool
	extractPublishDate      bool
	additionalDataExtractor bool

	//path to the stopwords folder
	stopWordsPath string
	stopWords     StopWords
	parser        *Parser

	timeout time.Duration
}

// GetDefaultConfiguration returns safe default configuration options
func GetDefaultConfiguration(args ...string) Configuration {
	if len(args) == 0 {
		return Configuration{
			localStoragePath:        "",   //not used in this version
			imagesMinBytes:          4500, //not used in this version
			enableImageFetching:     true,
			useMetaLanguage:         true,
			targetLanguage:          "en",
			imageMagickConvertPath:  "/usr/bin/convert",  //not used in this version
			imageMagickIdentifyPath: "/usr/bin/identify", //not used in this version
			browserUserAgent:        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/534.52.7 (KHTML, like Gecko) Version/5.1.2 Safari/534.52.7",
			debug:                   false,
			extractPublishDate:      false,
			additionalDataExtractor: false,
			stopWordsPath:           "resources/stopwords",
			stopWords:               NewStopwords(), //TODO with path
			parser:                  NewParser(),
			timeout:                 time.Duration(5 * time.Second),
		}
	}

	path := args[0]
	jsconfiguration, err := jsconfig.LoadConfig(path)
	if err != nil {
		panic(err.Error())
	}
	stopWordsPath := jsconfiguration.String("stopWordsPath", "resources/stopwords")
	stopWords := NewStopwords() //TODO with path
	return Configuration{
		localStoragePath:        jsconfiguration.String("localStoragePath", ""), //not used in this version
		imagesMinBytes:          jsconfiguration.Int("imageMinBytes", 4500),     //not used in this version
		enableImageFetching:     jsconfiguration.Bool("enableImageFetching", true),
		useMetaLanguage:         jsconfiguration.Bool("useMetaLanguage", true),
		targetLanguage:          jsconfiguration.String("targetLanguage", "en"),
		imageMagickConvertPath:  jsconfiguration.String("imageMagickConvertPath", "/usr/bin/convert"),   //not used in this version
		imageMagickIdentifyPath: jsconfiguration.String("imageMagickIdentityPath", "/usr/bin/identify"), //not used in this version
		browserUserAgent:        jsconfiguration.String("browserUserAgent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/534.52.7 (KHTML, like Gecko) Version/5.1.2 Safari/534.52.7"),
		debug:                   jsconfiguration.Bool("debug", false),
		extractPublishDate:      jsconfiguration.Bool("extractPublishDate", false),      //TODO
		additionalDataExtractor: jsconfiguration.Bool("additionalDataExtractor", false), //TODO
		stopWordsPath:           stopWordsPath,
		stopWords:               stopWords,
		parser:                  NewParser(),
		timeout:                 time.Duration(jsconfiguration.Int("timeout", 5)) * time.Second,
	}
}
<commit_msg>extract user agent to const<commit_after>package goose

import (
	"github.com/advancedlogic/gojs-config"
	"time"
)

const defaultUserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_2) AppleWebKit/534.52.7 (KHTML, like Gecko) Version/5.1.2 Safari/534.52.7"

// Configuration is a wrapper for various config options
type Configuration struct {
	localStoragePath        string //not used in this version
	imagesMinBytes          int    //not used in this version
	enableImageFetching     bool
	useMetaLanguage         bool
	targetLanguage          string
	imageMagickConvertPath  string //not used in this version
	imageMagickIdentifyPath string //not used in this version
	browserUserAgent        string
	debug                   bool
	extractPublishDate      bool
	additionalDataExtractor bool

	//path to the stopwords folder
	stopWordsPath string
	stopWords     StopWords
	parser        *Parser

	timeout time.Duration
}

// GetDefaultConfiguration returns safe default configuration options
func GetDefaultConfiguration(args ...string) Configuration {
	if len(args) == 0 {
		return Configuration{
			localStoragePath:        "",   //not used in this version
			imagesMinBytes:          4500, //not used in this version
			enableImageFetching:     true,
			useMetaLanguage:         true,
			targetLanguage:          "en",
			imageMagickConvertPath:  "/usr/bin/convert",  //not used in this version
			imageMagickIdentifyPath: "/usr/bin/identify", //not used in this version
			browserUserAgent:        defaultUserAgent,
			debug:                   false,
			extractPublishDate:      false,
			additionalDataExtractor: false,
			stopWordsPath:           "resources/stopwords",
			stopWords:               NewStopwords(), //TODO with path
			parser:                  NewParser(),
			timeout:                 time.Duration(5 * time.Second),
		}
	}

	path := args[0]
	jsconfiguration, err := jsconfig.LoadConfig(path)
	if err != nil {
		panic(err.Error())
	}
	stopWordsPath := jsconfiguration.String("stopWordsPath", "resources/stopwords")
	stopWords := NewStopwords() //TODO with path
	return Configuration{
		localStoragePath:        jsconfiguration.String("localStoragePath", ""), //not used in this version
		imagesMinBytes:          jsconfiguration.Int("imageMinBytes", 4500),     //not used in this version
		enableImageFetching:     jsconfiguration.Bool("enableImageFetching", true),
		useMetaLanguage:         jsconfiguration.Bool("useMetaLanguage", true),
		targetLanguage:          jsconfiguration.String("targetLanguage", "en"),
		imageMagickConvertPath:  jsconfiguration.String("imageMagickConvertPath", "/usr/bin/convert"),   //not used in this version
		imageMagickIdentifyPath: jsconfiguration.String("imageMagickIdentityPath", "/usr/bin/identify"), //not used in this version
		browserUserAgent:        jsconfiguration.String("browserUserAgent", defaultUserAgent),
		debug:                   jsconfiguration.Bool("debug", false),
		extractPublishDate:      jsconfiguration.Bool("extractPublishDate", false),      //TODO
		additionalDataExtractor: jsconfiguration.Bool("additionalDataExtractor", false), //TODO
		stopWordsPath:           stopWordsPath,
		stopWords:               stopWords,
		parser:                  NewParser(),
		timeout:                 time.Duration(jsconfiguration.Int("timeout", 5)) * time.Second,
	}
}
<|endoftext|>


<commit_before>package slackapi

import (
	"encoding/json"
	"golang.org/x/net/websocket"
	"log"
	"os"
)

// RTMResponse defines the JSON-encoded output for RTM connection.
type RTMResponse struct {
	Response
	// Self                    null `json:"self"`
	// Team                    null `json:"team"`
	// LatestEventTS           null `json:"latest_event_ts"`
	// Channels                null `json:"channels"`
	// Groups                  null `json:"groups"`
	// IMS                     null `json:"ims"`
	// CacheTS                 null `json:"cache_ts"`
	// ReadOnlyChannels        null `json:"read_only_channels"`
	// CanManageSharedChannels null `json:"can_manage_shared_channels"`
	// Subteams                null `json:"subteams"`
	// DND                     null `json:"dnd"`
	// Users                   null `json:"users"`
	// CacheVersion            null `json:"cache_version"`
	// CacheTSVersion          null `json:"cache_ts_version"`
	// Bots                    null `json:"bots"`
	URL string `json:"url"`
}

// NewRTM connects to the real time messaging websocket.
func (s *SlackAPI) NewRTM() (*websocket.Conn, error) {
	resp := s.RTMStart()

	return websocket.Dial(resp.URL, "", "https://api.slack.com")
}

// RTMStart Starts a Real Time Messaging session.
func (s *SlackAPI) RTMStart() RTMResponse {
	var response RTMResponse
	s.GetRequest(&response, "rtm.start", "token")
	return response
}

// RTMMessage reads and returns a message from the websocket connection.
func (s *SlackAPI) RTMMessage(ws *websocket.Conn, res interface{}) error {
	return websocket.JSON.Receive(ws, &res)
}

// RTMEvents prints all the websocket events.
func (s *SlackAPI) RTMEvents() {
	ws, err := s.NewRTM()

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

	var data interface{}

	for {
		if err := s.RTMMessage(ws, &data); err != nil {
			log.Println(err)
			continue
		}

		if err := json.NewEncoder(os.Stdout).Encode(data); err != nil {
			log.Println("JSON encode;", err)
			continue
		}
	}

}
<commit_msg>Add full implementation of the real time messaging websocket<commit_after>package slackapi

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"reflect"

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

// RTM is the real time messaging.
type RTM struct {
	conn      *websocket.Conn
	connURL   string
	Events    chan Event
	rawEvents chan json.RawMessage
}

// RTMResponse defines the JSON-encoded output for RTM connection.
type RTMResponse struct {
	Response
	// Self                    null `json:"self"`
	// Team                    null `json:"team"`
	// LatestEventTS           null `json:"latest_event_ts"`
	// Channels                null `json:"channels"`
	// Groups                  null `json:"groups"`
	// IMS                     null `json:"ims"`
	// CacheTS                 null `json:"cache_ts"`
	// ReadOnlyChannels        null `json:"read_only_channels"`
	// CanManageSharedChannels null `json:"can_manage_shared_channels"`
	// Subteams                null `json:"subteams"`
	// DND                     null `json:"dnd"`
	// Users                   null `json:"users"`
	// CacheVersion            null `json:"cache_version"`
	// CacheTSVersion          null `json:"cache_ts_version"`
	// Bots                    null `json:"bots"`
	URL string `json:"url"`
}

// ACKMessage is used for messages received in reply to other messages
type ACKMessage struct {
	Response
	ReplyTo   int    `json:"reply_to"`
	Text      string `json:"text"`
	Timestamp string `json:"ts"`
}

// EventTypes represents the data type for each websocket event.
var EventTypes = map[string]interface{}{}

// NewRTM connects to the real time messaging websocket.
func (s *SlackAPI) NewRTM() (*RTM, error) {
	var response RTMResponse
	s.GetRequest(&response, "rtm.start", "token")

	ws, err := websocket.Dial(response.URL, "", "https://api.slack.com")

	if err != nil {
		return &RTM{}, err
	}

	return &RTM{
		conn:      ws,
		connURL:   response.URL,
		Events:    make(chan Event, 50),
		rawEvents: make(chan json.RawMessage),
	}, nil
}

// ManageEvents controls the websocket events.
func (rtm *RTM) ManageEvents() {
	keepalive := make(chan bool)

	go rtm.handleIncomingEvents(keepalive)

	rtm.handleEvents(keepalive)
}

func (rtm *RTM) handleEvents(keepRunning chan bool) {
	for {
		select {
		case rawEvent := <-rtm.rawEvents:
			rtm.handleRawEvent(rawEvent)
		}
	}
}

func (rtm *RTM) handleIncomingEvents(keepalive <-chan bool) {
	for {
		select {
		case <-keepalive:
			return
		default:
			rtm.receiveIncomingEvent()
		}
	}
}

// receiveIncomingEvent inserts the websocket events into a queue.
func (rtm *RTM) receiveIncomingEvent() {
	event := json.RawMessage{}

	if err := websocket.JSON.Receive(rtm.conn, &event); err != nil {
		if err == io.EOF {
			log.Fatal("Connection dropped")
			return
		}

		rtm.Events <- Event{Type: "error", Data: &ErrorEvent{err.Error()}}
		return
	}

	if len(event) == 0 {
		err := fmt.Errorf("invalid event; %s", event)
		rtm.Events <- Event{Type: "error", Data: &ErrorEvent{err.Error()}}
		return
	}

	rtm.rawEvents <- event
}

func (rtm *RTM) handleRawEvent(rawEvent json.RawMessage) {
	event := &Event{}

	if err := json.Unmarshal(rawEvent, event); err != nil {
		rtm.Events <- Event{Type: "error", Data: &ErrorEvent{err.Error()}}
		return
	}

	rtm.handleEvent(event.Type, rawEvent)
}

func (rtm *RTM) handleEvent(_type string, event json.RawMessage) {
	v, exists := EventTypes[_type]

	if !exists {
		err := fmt.Errorf("unsupported event %q: %s", _type, string(event))
		rtm.Events <- Event{Type: "error", Data: &ErrorEvent{err.Error()}}
		return
	}

	t := reflect.TypeOf(v)
	recvEvent := reflect.New(t).Interface()

	if err := json.Unmarshal(event, recvEvent); err != nil {
		err = fmt.Errorf("unmarshall event %q: %s", _type, string(event))
		rtm.Events <- Event{Type: "error", Data: &ErrorEvent{err.Error()}}
		return
	}
	rtm.Events <- Event{_type, recvEvent}
}
<|endoftext|>


<commit_before>// +build windows

package box

import (
	"syscall"
	"unsafe"
)

var kernel32 = syscall.NewLazyDLL("kernel32")

var hConout, _ = syscall.GetStdHandle(syscall.STD_ERROR_HANDLE)

type coord_t struct {
	X int16
	Y int16
}

type small_rect_t struct {
	Left   int16
	Top    int16
	Right  int16
	Bottom int16
}

type console_screen_buffer_info_t struct {
	Size              coord_t
	CursorPosition    coord_t
	Attributes        uint16
	Window            small_rect_t
	MaximumWindowSize coord_t
}

var getConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")

type console_handle_t syscall.Handle

func newHandle(handle syscall.Handle) console_handle_t {
	return console_handle_t(handle)
}

func (h console_handle_t) GetScreenBufferInfo() *console_screen_buffer_info_t {
	var csbi console_screen_buffer_info_t
	getConsoleScreenBufferInfo.Call(
		uintptr(h),
		uintptr(unsafe.Pointer(&csbi)))
	return &csbi
}

func GetScreenBufferInfo() *console_screen_buffer_info_t {
	return console_handle_t(hConout).GetScreenBufferInfo()
}

func (this *console_screen_buffer_info_t) ViewSize() (int, int) {
	return int(this.Window.Right-this.Window.Left) + 1,
		int(this.Window.Bottom-this.Window.Top) + 1
}

func (this *console_screen_buffer_info_t) CursorPos() (int, int) {
	return int(this.CursorPosition.X), int(this.CursorPosition.Y)
}

func GetLocate() (int, int) {
	return GetScreenBufferInfo().CursorPos()
}
<commit_msg>Get screen size from NOT STD_ERROR BUT "CONOUT$"<commit_after>// +build windows

package box

import (
	"syscall"
	"unsafe"
)

var kernel32 = syscall.NewLazyDLL("kernel32")

var hConout syscall.Handle

func init() {
	var err error
	hConout, err = syscall.Open("CONOUT$", syscall.O_RDWR, 0)
	if err != nil {
		panic(err.Error())
	}
}

type coord_t struct {
	X int16
	Y int16
}

type small_rect_t struct {
	Left   int16
	Top    int16
	Right  int16
	Bottom int16
}

type console_screen_buffer_info_t struct {
	Size              coord_t
	CursorPosition    coord_t
	Attributes        uint16
	Window            small_rect_t
	MaximumWindowSize coord_t
}

var getConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")

type console_handle_t syscall.Handle

func newHandle(handle syscall.Handle) console_handle_t {
	return console_handle_t(handle)
}

func (h console_handle_t) GetScreenBufferInfo() *console_screen_buffer_info_t {
	var csbi console_screen_buffer_info_t
	getConsoleScreenBufferInfo.Call(
		uintptr(h),
		uintptr(unsafe.Pointer(&csbi)))
	return &csbi
}

func GetScreenBufferInfo() *console_screen_buffer_info_t {
	return console_handle_t(hConout).GetScreenBufferInfo()
}

func (this *console_screen_buffer_info_t) ViewSize() (int, int) {
	return int(this.Window.Right-this.Window.Left) + 1,
		int(this.Window.Bottom-this.Window.Top) + 1
}

func (this *console_screen_buffer_info_t) CursorPos() (int, int) {
	return int(this.CursorPosition.X), int(this.CursorPosition.Y)
}

func GetLocate() (int, int) {
	return GetScreenBufferInfo().CursorPos()
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"strings"
	"syscall"
	"time"

	"golang.org/x/crypto/ssh/terminal"
)

func test() {

}

func listFiles() []string {
	files, _ := ioutil.ReadDir(path.Join(RuntimeArgs.WorkingPath))
	fileNames := []string{}
	for _, f := range files {
		fileNameSplit := strings.Split(f.Name(), "/")
		fileName := fileNameSplit[len(fileNameSplit)-1]
		if fileName == "config.json" || fileName == "temp" || strings.Contains(fileName, ".cache") {
			continue
		}
		fileNames = append(fileNames, fileName)
	}
	return fileNames
}

func getFullEntry() string {
	type CachedDoc struct {
		Files      []string
		Entries    []string
		Timestamps []int
	}

	fullEntry := ""
	cache := CachedDoc{[]string{}, []string{}, []int{}}
	allEntries := []string{}
	gts := []int{}
	allFiles := readAllFiles()
	// if cache does not exist
	if !exists(path.Join(path.Join(RuntimeArgs.WorkingPath, ConfigArgs.WorkingFile+".cache.json"))) {
		logger.Debug("No cache.")
		wholeText := ""
		for _, file := range allFiles {
			wholeText += decrypt(file) + "\n"
			cache.Files = append(cache.Files, file)
		}
		allEntries, gts = parseEntries(wholeText)
	} else {
		logger.Debug("Using cache.")
		fileContents, _ := ioutil.ReadFile(path.Join(RuntimeArgs.WorkingPath, ConfigArgs.WorkingFile+".cache.json"))
		decryptedFileContents, err := decryptString(string(fileContents), RuntimeArgs.Passphrase)
		if err != nil {
			log.Fatal(err)
		}

		// Unmarshal JSON
		err = json.Unmarshal([]byte(decryptedFileContents), &cache)
		if err != nil {
			log.Fatal(err)
		}

		// Make set of files
		hasFile := make(map[string]bool)
		for _, file := range cache.Files {
			hasFile[file] = true
		}

		// If file doesn't exist, add it
		cache.Files = []string{}
		for _, file := range allFiles {
			cache.Files = append(cache.Files, file)
			if _, ok := hasFile[file]; !ok {
				logger.Debug("New entry %s.", file)
				newEntry, _ := ioutil.ReadFile(file)
				newEntryDecoded, err := decryptString(string(newEntry), RuntimeArgs.Passphrase)
				if err != nil {
					log.Fatal(err)
				}
				text, gt := parseEntries(newEntryDecoded)
				cache.Entries = append(cache.Entries, text...)
				cache.Timestamps = append(cache.Timestamps, gt...)
			}
		}

		entries := make(map[int]string)
		for i, entry := range cache.Entries {
			entries[cache.Timestamps[i]] = entry
		}
		allEntries, gts = sortEntries(entries)

	}

	cache.Entries = []string{}
	cache.Timestamps = []int{}
	for i, entry := range allEntries {
		fullEntry += entry + "\n\n"
		cache.Entries = append(cache.Entries, entry)
		cache.Timestamps = append(cache.Timestamps, gts[i])
	}

	cacheJson, _ := json.Marshal(cache)
	encryptedCacheJson := encryptString(string(cacheJson), RuntimeArgs.Passphrase)
	err := ioutil.WriteFile(path.Join(RuntimeArgs.WorkingPath, ConfigArgs.WorkingFile+".cache.json"), []byte(encryptedCacheJson), 0644)
	if err != nil {
		log.Fatal(err)
	}
	return strings.TrimSpace(fullEntry)

}

func promptPassword() {
	// Get password for working file
	passwordAccepted := false
	for passwordAccepted == false {
		fmt.Printf("Enter password for editing '%s': ", ConfigArgs.WorkingFile)
		bytePassword, _ := terminal.ReadPassword(int(os.Stdin.Fd()))
		password := strings.TrimSpace(string(bytePassword))
		RuntimeArgs.Passphrase = password
		if exists(path.Join(RuntimeArgs.FullPath, ConfigArgs.WorkingFile+".pass")) {
			// Check old password
			fileContents, _ := ioutil.ReadFile(path.Join(RuntimeArgs.FullPath, ConfigArgs.WorkingFile+".pass"))
			err := CheckPasswordHash(string(fileContents), password)
			if err == nil {
				passwordAccepted = true
			} else {
				fmt.Println("\nPasswords do not match.")
			}
		} else {
			// Generate new passwrod
			fmt.Printf("\nEnter password again: ")
			bytePassword2, _ := terminal.ReadPassword(int(syscall.Stdin))
			password2 := strings.TrimSpace(string(bytePassword2))
			if password == password2 {
				// Write password to file
				passwordAccepted = true
				passwordHashed, _ := HashPassword(password)
				err := ioutil.WriteFile(path.Join(RuntimeArgs.FullPath, ConfigArgs.WorkingFile+".pass"), passwordHashed, 0644)
				if err != nil {
					log.Fatal("Could not write to file.")
				}
			} else {
				fmt.Println("\nPasswords do not match.")
			}
		}
	}
	fmt.Println("")
}

func run() {
	logger.Debug("Available files: %s", strings.Join(listFiles(), ", "))

	// Check if VIM exists
	_, err := exec.Command("vim", "--version").Output()
	if err != nil {
		fmt.Println(`You need to download vim. If your using Unix:

	apt-get install vim

If you're using Windows:

	wget ftp://ftp.vim.org/pub/vim/pc/vim74w32.zip
	unzip vim74w32.zip
	mv vim/vim74/vim.exe ./
`)
		os.Exit(-1)
	}

	if !RuntimeArgs.EditLocally && HasInternetAccess() {
		syncDown()
	}

	promptPassword()

	fullEntry := ""
	if RuntimeArgs.EditWhole {
		start := time.Now()
		fullEntry = getFullEntry()
		logger.Info("Full entry loaded in %s.", time.Since(start))
		if len(fullEntry) > 0 {
			fullEntry += "\n\n"
		}
	}

	t := time.Now()
	fullEntry += string(t.Format("2006-01-02 15:04:05")) + "  "
	err = ioutil.WriteFile(path.Join(RuntimeArgs.TempPath, "temp"), []byte(fullEntry), 0644)
	if err != nil {
		log.Fatal(err)
	}

	newEntry := editEntry()
	entries, _ := parseEntries(newEntry)
	for _, entry := range entries {
		writeEntry(entry, false)
	}

	if !RuntimeArgs.EditLocally && HasInternetAccess() {
		syncUp()
	}

}
<commit_msg>Tracking full entry time<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"strings"
	"syscall"
	"time"

	"golang.org/x/crypto/ssh/terminal"
)

func test() {

}

func listFiles() []string {
	files, _ := ioutil.ReadDir(path.Join(RuntimeArgs.WorkingPath))
	fileNames := []string{}
	for _, f := range files {
		fileNameSplit := strings.Split(f.Name(), "/")
		fileName := fileNameSplit[len(fileNameSplit)-1]
		if fileName == "config.json" || fileName == "temp" || strings.Contains(fileName, ".cache") {
			continue
		}
		fileNames = append(fileNames, fileName)
	}
	return fileNames
}

func getFullEntry() string {
	defer timeTrack(time.Now(), "Got full entry")
	type CachedDoc struct {
		Files      []string
		Entries    []string
		Timestamps []int
	}

	fullEntry := ""
	cache := CachedDoc{[]string{}, []string{}, []int{}}
	allEntries := []string{}
	gts := []int{}
	allFiles := readAllFiles()
	// if cache does not exist
	if !exists(path.Join(path.Join(RuntimeArgs.WorkingPath, ConfigArgs.WorkingFile+".cache.json"))) {
		logger.Debug("No cache.")
		wholeText := ""
		for _, file := range allFiles {
			wholeText += decrypt(file) + "\n"
			cache.Files = append(cache.Files, file)
		}
		allEntries, gts = parseEntries(wholeText)
	} else {
		logger.Debug("Using cache.")
		fileContents, _ := ioutil.ReadFile(path.Join(RuntimeArgs.WorkingPath, ConfigArgs.WorkingFile+".cache.json"))
		decryptedFileContents, err := decryptString(string(fileContents), RuntimeArgs.Passphrase)
		if err != nil {
			log.Fatal(err)
		}

		// Unmarshal JSON
		err = json.Unmarshal([]byte(decryptedFileContents), &cache)
		if err != nil {
			log.Fatal(err)
		}

		// Make set of files
		hasFile := make(map[string]bool)
		for _, file := range cache.Files {
			hasFile[file] = true
		}

		// If file doesn't exist, add it
		cache.Files = []string{}
		for _, file := range allFiles {
			cache.Files = append(cache.Files, file)
			if _, ok := hasFile[file]; !ok {
				logger.Debug("New entry %s.", file)
				newEntry, _ := ioutil.ReadFile(file)
				newEntryDecoded, err := decryptString(string(newEntry), RuntimeArgs.Passphrase)
				if err != nil {
					log.Fatal(err)
				}
				text, gt := parseEntries(newEntryDecoded)
				cache.Entries = append(cache.Entries, text...)
				cache.Timestamps = append(cache.Timestamps, gt...)
			}
		}

		entries := make(map[int]string)
		for i, entry := range cache.Entries {
			entries[cache.Timestamps[i]] = entry
		}
		allEntries, gts = sortEntries(entries)

	}

	cache.Entries = []string{}
	cache.Timestamps = []int{}
	for i, entry := range allEntries {
		fullEntry += entry + "\n\n"
		cache.Entries = append(cache.Entries, entry)
		cache.Timestamps = append(cache.Timestamps, gts[i])
	}

	cacheJson, _ := json.Marshal(cache)
	encryptedCacheJson := encryptString(string(cacheJson), RuntimeArgs.Passphrase)
	err := ioutil.WriteFile(path.Join(RuntimeArgs.WorkingPath, ConfigArgs.WorkingFile+".cache.json"), []byte(encryptedCacheJson), 0644)
	if err != nil {
		log.Fatal(err)
	}
	return strings.TrimSpace(fullEntry)

}

func promptPassword() {
	// Get password for working file
	passwordAccepted := false
	for passwordAccepted == false {
		fmt.Printf("Enter password for editing '%s': ", ConfigArgs.WorkingFile)
		bytePassword, _ := terminal.ReadPassword(int(os.Stdin.Fd()))
		password := strings.TrimSpace(string(bytePassword))
		RuntimeArgs.Passphrase = password
		if exists(path.Join(RuntimeArgs.FullPath, ConfigArgs.WorkingFile+".pass")) {
			// Check old password
			fileContents, _ := ioutil.ReadFile(path.Join(RuntimeArgs.FullPath, ConfigArgs.WorkingFile+".pass"))
			err := CheckPasswordHash(string(fileContents), password)
			if err == nil {
				passwordAccepted = true
			} else {
				fmt.Println("\nPasswords do not match.")
			}
		} else {
			// Generate new passwrod
			fmt.Printf("\nEnter password again: ")
			bytePassword2, _ := terminal.ReadPassword(int(syscall.Stdin))
			password2 := strings.TrimSpace(string(bytePassword2))
			if password == password2 {
				// Write password to file
				passwordAccepted = true
				passwordHashed, _ := HashPassword(password)
				err := ioutil.WriteFile(path.Join(RuntimeArgs.FullPath, ConfigArgs.WorkingFile+".pass"), passwordHashed, 0644)
				if err != nil {
					log.Fatal("Could not write to file.")
				}
			} else {
				fmt.Println("\nPasswords do not match.")
			}
		}
	}
	fmt.Println("")
}

func run() {
	logger.Debug("Available files: %s", strings.Join(listFiles(), ", "))

	// Check if VIM exists
	_, err := exec.Command("vim", "--version").Output()
	if err != nil {
		fmt.Println(`You need to download vim. If your using Unix:

	apt-get install vim

If you're using Windows:

	wget ftp://ftp.vim.org/pub/vim/pc/vim74w32.zip
	unzip vim74w32.zip
	mv vim/vim74/vim.exe ./
`)
		os.Exit(-1)
	}

	if !RuntimeArgs.EditLocally && HasInternetAccess() {
		syncDown()
	}

	promptPassword()

	fullEntry := ""
	if RuntimeArgs.EditWhole {
		fullEntry = getFullEntry()
		if len(fullEntry) > 0 {
			fullEntry += "\n\n"
		}
	}

	t := time.Now()
	fullEntry += string(t.Format("2006-01-02 15:04:05")) + "  "
	err = ioutil.WriteFile(path.Join(RuntimeArgs.TempPath, "temp"), []byte(fullEntry), 0644)
	if err != nil {
		log.Fatal(err)
	}

	newEntry := editEntry()
	entries, _ := parseEntries(newEntry)
	for _, entry := range entries {
		writeEntry(entry, false)
	}

	if !RuntimeArgs.EditLocally && HasInternetAccess() {
		syncUp()
	}

}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"math/rand"
	"os"
	"path/filepath"
	"strings"
	"time"
	"unicode/utf8"

	"github.com/mitchellh/go-homedir"
)

// Run fillin
func Run(configDir string, args []string, in *bufio.Reader, out *bufio.Writer) (string, error) {
	dir, err := homedir.Expand(normalizeFileSeparator(configDir))
	if err := os.MkdirAll(dir, 0755); err != nil {
		return "", err
	}

	path := filepath.Join(dir, "fillin.json")
	rfile, err := os.OpenFile(path, os.O_RDONLY|os.O_CREATE, 0644)
	if err != nil {
		return "", err
	}
	w := new(bytes.Buffer)
	filled, err := Fillin(args, rfile, w, in, out)
	if err != nil {
		return "", err
	}
	cmd := escapeJoin(filled)
	rfile.Close() // not be defered due to rename

	tmp := filepath.Join(dir, fmt.Sprintf("fillin.%d-%d.json", os.Getpid(), rand.Int()))
	defer os.Remove(tmp)
	wfile, err := os.OpenFile(tmp, os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		return "", err
	}
	if n, err := wfile.Write(w.Bytes()); n != w.Len() || err != nil {
		wfile.Close()
		return "", err
	}
	wfile.Close() // not be defered due to rename

	if err := os.Rename(tmp, path); err != nil {
		return "", err
	}

	if cmd != "" {
		histfile := filepath.Join(dir, ".fillin.histfile")
		hfile, err := os.OpenFile(histfile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
		defer hfile.Close()
		if err != nil {
			return "", err
		}
		hfile.WriteString(fmt.Sprintf(": %d:0;%s\n", time.Now().Unix(), cmd))
	}

	return cmd, nil
}

func normalizeFileSeparator(path string) string {
	p := filepath.Join(strings.Split(path, "/")...)
	if path[0] == '/' {
		p = "/" + p
	}
	return p
}

func escapeJoin(args []string) string {
	if len(args) == 1 {
		return args[0]
	}
	for i, arg := range args {
		args[i] = escape(arg)
	}
	return strings.Join(args, " ")
}

func escape(arg string) string {
	switch arg {
	case "|", "||", "&&", ">", ">>", "<":
		return arg
	}
quote:
	for _, quote := range []bool{false, true} {
		s := arg
		isHead, afterFd, afterRedirect := true, false, false
		var buf bytes.Buffer
		if quote {
			buf.WriteByte('\'')
		}
		for len(s) > 0 {
			c, l := utf8.DecodeRuneInString(s)
			s = s[l:]
			if (isHead || afterFd) && strings.ContainsRune("<>", c) && !quote {
				buf.WriteRune(c)
				isHead, afterFd, afterRedirect = false, false, true
				continue
			} else if afterRedirect && strings.ContainsRune("<>", c) && !quote {
				buf.WriteRune(c)
			} else if isHead && strings.ContainsRune("12", c) && !quote {
				buf.WriteRune(c)
				isHead, afterFd = false, true
				continue
			} else if afterRedirect && strings.ContainsRune("&", c) && !quote {
				buf.WriteRune(c)
			} else if strings.ContainsRune("\\'\"`${[|&;<>()*?!", c) && !quote {
				buf.WriteByte('\\')
				buf.WriteRune(c)
			} else if c == rune(' ') && !quote {
				continue quote
			} else if c == rune('\t') {
				if quote {
					buf.WriteByte('\\')
					buf.WriteByte('t')
				} else {
					continue quote
				}
			} else {
				buf.WriteRune(c)
			}
			isHead, afterFd, afterRedirect = false, false, false
		}
		if quote {
			buf.WriteByte('\'')
		}
		return buf.String()
	}
	return ""
}
<commit_msg>change the config file permission to 600<commit_after>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"math/rand"
	"os"
	"path/filepath"
	"strings"
	"time"
	"unicode/utf8"

	"github.com/mitchellh/go-homedir"
)

// Run fillin
func Run(configDir string, args []string, in *bufio.Reader, out *bufio.Writer) (string, error) {
	dir, err := homedir.Expand(normalizeFileSeparator(configDir))
	if err := os.MkdirAll(dir, 0755); err != nil {
		return "", err
	}

	path := filepath.Join(dir, "fillin.json")
	rfile, err := os.OpenFile(path, os.O_RDONLY|os.O_CREATE, 0600)
	if err != nil {
		return "", err
	}
	w := new(bytes.Buffer)
	filled, err := Fillin(args, rfile, w, in, out)
	if err != nil {
		return "", err
	}
	cmd := escapeJoin(filled)
	rfile.Close() // not be defered due to rename

	tmp := filepath.Join(dir, fmt.Sprintf("fillin.%d-%d.json", os.Getpid(), rand.Int()))
	defer os.Remove(tmp)
	wfile, err := os.OpenFile(tmp, os.O_WRONLY|os.O_CREATE, 0600)
	if err != nil {
		return "", err
	}
	if n, err := wfile.Write(w.Bytes()); n != w.Len() || err != nil {
		wfile.Close()
		return "", err
	}
	wfile.Close() // not be defered due to rename

	if err := os.Rename(tmp, path); err != nil {
		return "", err
	}

	if cmd != "" {
		histfile := filepath.Join(dir, ".fillin.histfile")
		hfile, err := os.OpenFile(histfile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600)
		defer hfile.Close()
		if err != nil {
			return "", err
		}
		hfile.WriteString(fmt.Sprintf(": %d:0;%s\n", time.Now().Unix(), cmd))
	}

	return cmd, nil
}

func normalizeFileSeparator(path string) string {
	p := filepath.Join(strings.Split(path, "/")...)
	if path[0] == '/' {
		p = "/" + p
	}
	return p
}

func escapeJoin(args []string) string {
	if len(args) == 1 {
		return args[0]
	}
	for i, arg := range args {
		args[i] = escape(arg)
	}
	return strings.Join(args, " ")
}

func escape(arg string) string {
	switch arg {
	case "|", "||", "&&", ">", ">>", "<":
		return arg
	}
quote:
	for _, quote := range []bool{false, true} {
		s := arg
		isHead, afterFd, afterRedirect := true, false, false
		var buf bytes.Buffer
		if quote {
			buf.WriteByte('\'')
		}
		for len(s) > 0 {
			c, l := utf8.DecodeRuneInString(s)
			s = s[l:]
			if (isHead || afterFd) && strings.ContainsRune("<>", c) && !quote {
				buf.WriteRune(c)
				isHead, afterFd, afterRedirect = false, false, true
				continue
			} else if afterRedirect && strings.ContainsRune("<>", c) && !quote {
				buf.WriteRune(c)
			} else if isHead && strings.ContainsRune("12", c) && !quote {
				buf.WriteRune(c)
				isHead, afterFd = false, true
				continue
			} else if afterRedirect && strings.ContainsRune("&", c) && !quote {
				buf.WriteRune(c)
			} else if strings.ContainsRune("\\'\"`${[|&;<>()*?!", c) && !quote {
				buf.WriteByte('\\')
				buf.WriteRune(c)
			} else if c == rune(' ') && !quote {
				continue quote
			} else if c == rune('\t') {
				if quote {
					buf.WriteByte('\\')
					buf.WriteByte('t')
				} else {
					continue quote
				}
			} else {
				buf.WriteRune(c)
			}
			isHead, afterFd, afterRedirect = false, false, false
		}
		if quote {
			buf.WriteByte('\'')
		}
		return buf.String()
	}
	return ""
}
<|endoftext|>


<commit_before>// Copyright 2014 Hajime Hoshi
//
// 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.

package ebiten

import (
	"sync/atomic"

	"github.com/hajimehoshi/ebiten/v2/internal/clock"
	"github.com/hajimehoshi/ebiten/v2/internal/driver"
)

// Game defines necessary functions for a game.
type Game interface {
	// Update updates a game by one tick. The given argument represents a screen image.
	//
	// Update updates only the game logic and Draw draws the screen.
	//
	// In the first frame, it is ensured that Update is called at least once before Draw. You can use Update
	// to initialize the game state.
	//
	// After the first frame, Update might not be called or might be called once
	// or more for one frame. The frequency is determined by the current TPS (tick-per-second).
	Update() error

	// Draw draws the game screen by one frame.
	//
	// The give argument represents a screen image. The updated content is adopted as the game screen.
	Draw(screen *Image)

	// Layout accepts a native outside size in device-independent pixels and returns the game's logical screen
	// size.
	//
	// On desktops, the outside is a window or a monitor (fullscreen mode). On browsers, the outside is a body
	// element. On mobiles, the outside is the view's size.
	//
	// Even though the outside size and the screen size differ, the rendering scale is automatically adjusted to
	// fit with the outside.
	//
	// Layout is called almost every frame.
	//
	// It is ensured that Layout is invoked before Update is called in the first frame.
	//
	// If Layout returns non-positive numbers, the caller can panic.
	//
	// You can return a fixed screen size if you don't care, or you can also return a calculated screen size
	// adjusted with the given outside size.
	Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int)
}

// DefaultTPS represents a default ticks per second, that represents how many times game updating happens in a second.
const DefaultTPS = 60

// CurrentFPS returns the current number of FPS (frames per second), that represents
// how many swapping buffer happens per second.
//
// On some environments, CurrentFPS doesn't return a reliable value since vsync doesn't work well there.
// If you want to measure the application's speed, Use CurrentTPS.
//
// CurrentFPS is concurrent-safe.
func CurrentFPS() float64 {
	return clock.CurrentFPS()
}

var (
	isScreenClearedEveryFrame = int32(1)
	currentMaxTPS             = int32(DefaultTPS)
)

// SetScreenClearedEveryFrame enables or disables the clearing of the screen at the beginning of each frame.
// The default value is false and the screen is cleared each frame by default.
//
// SetScreenClearedEveryFrame is concurrent-safe.
func SetScreenClearedEveryFrame(cleared bool) {
	v := int32(0)
	if cleared {
		v = 1
	}
	atomic.StoreInt32(&isScreenClearedEveryFrame, v)
	theUIContext.setScreenClearedEveryFrame(cleared)
}

// IsScreenClearedEveryFrame returns true if the frame isn't cleared at the beginning.
//
// IsScreenClearedEveryFrame is concurrent-safe.
func IsScreenClearedEveryFrame() bool {
	return atomic.LoadInt32(&isScreenClearedEveryFrame) != 0
}

type imageDumperGame struct {
	game Game
	d    *imageDumper
	err  error
}

func (i *imageDumperGame) Update() error {
	if i.err != nil {
		return i.err
	}
	if i.d == nil {
		i.d = &imageDumper{g: i.game}
	}
	return i.d.update()
}

func (i *imageDumperGame) Draw(screen *Image) {
	if i.err != nil {
		return
	}

	i.game.Draw(screen)
	i.err = i.d.dump(screen)
}

func (i *imageDumperGame) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
	return i.game.Layout(outsideWidth, outsideHeight)
}

// RunGame starts the main loop and runs the game.
// game's Update function is called every tick to update the game logic.
// game's Draw function is, if it exists, called every frame to draw the screen.
// game's Layout function is called when necessary, and you can specify the logical screen size by the function.
//
// game must implement Game interface.
// Game's Draw function is optional, but it is recommended to implement Draw to seperate updating the logic and
// rendering.
//
// RunGame is a more flexibile form of Run due to game's Layout function.
// You can make a resizable window if you use RunGame, while you cannot if you use Run.
// RunGame is more sophisticated way than Run and hides the notion of 'scale'.
//
// While Run specifies the window size, RunGame does not.
// You need to call SetWindowSize before RunGame if you want.
// Otherwise, a default window size is adopted.
//
// Some functions (ScreenScale, SetScreenScale, SetScreenSize) are not available with RunGame.
//
// On browsers, it is strongly recommended to use iframe if you embed an Ebiten application in your website.
//
// RunGame must be called on the main thread.
// Note that Ebiten bounds the main goroutine to the main OS thread by runtime.LockOSThread.
//
// Ebiten tries to call game's Update function 60 times a second by default. In other words,
// TPS (ticks per second) is 60 by default.
// This is not related to framerate (display's refresh rate).
//
// RunGame returns error when 1) OpenGL error happens, 2) audio error happens or
// 3) f returns error. In the case of 3), RunGame returns the same error.
//
// The size unit is device-independent pixel.
//
// Don't call RunGame twice or more in one process.
func RunGame(game Game) error {
	fixWindowPosition(WindowSize())
	theUIContext.set(&imageDumperGame{
		game: game,
	})
	if err := uiDriver().Run(theUIContext); err != nil {
		if err == driver.RegularTermination {
			return nil
		}
		return err
	}
	return nil
}

// RunGameWithoutMainLoop runs the game, but don't call the loop on the main (UI) thread.
// Different from Run, RunGameWithoutMainLoop returns immediately.
//
// Ebiten users should NOT call RunGameWithoutMainLoop.
// Instead, functions in github.com/hajimehoshi/ebiten/v2/mobile package calls this.
//
// TODO: Remove this. In order to remove this, the uiContext should be in another package.
func RunGameWithoutMainLoop(game Game) {
	fixWindowPosition(WindowSize())
	theUIContext.set(&imageDumperGame{
		game: game,
	})
	uiDriver().RunWithoutMainLoop(theUIContext)
}

// ScreenSizeInFullscreen returns the size in device-independent pixels when the game is fullscreen.
// The adopted monitor is the 'current' monitor which the window belongs to.
// The returned value can be given to Run or SetSize function if the perfectly fit fullscreen is needed.
//
// On browsers, ScreenSizeInFullscreen returns the 'window' (global object) size, not 'screen' size since an Ebiten
// game should not know the outside of the window object. For more details, see SetFullscreen API comment.
//
// On mobiles, ScreenSizeInFullscreen returns (0, 0) so far.
//
// ScreenSizeInFullscreen's use cases are limited. If you are making a fullscreen application, you can use RunGame and
// the Game interface's Layout function instead. If you are making a not-fullscreen application but the application's
// behavior depends on the monitor size, ScreenSizeInFullscreen is useful.
//
// ScreenSizeInFullscreen must be called on the main thread before ebiten.Run, and is concurrent-safe after
// ebiten.Run.
func ScreenSizeInFullscreen() (int, int) {
	return uiDriver().ScreenSizeInFullscreen()
}

// CursorMode returns the current cursor mode.
//
// On browsers, only CursorModeVisible and CursorModeHidden are supported.
//
// CursorMode returns CursorModeHidden on mobiles.
//
// CursorMode is concurrent-safe.
func CursorMode() CursorModeType {
	return CursorModeType(uiDriver().CursorMode())
}

// SetCursorMode sets the render and capture mode of the mouse cursor.
// CursorModeVisible sets the cursor to always be visible.
// CursorModeHidden hides the system cursor when over the window.
// CursorModeCaptured hides the system cursor and locks it to the window.
//
// On browsers, only CursorModeVisible and CursorModeHidden are supported.
//
// SetCursorMode does nothing on mobiles.
//
// SetCursorMode is concurrent-safe.
func SetCursorMode(mode CursorModeType) {
	uiDriver().SetCursorMode(driver.CursorMode(mode))
}

// IsFullscreen reports whether the current mode is fullscreen or not.
//
// IsFullscreen always returns false on browsers or mobiles.
//
// IsFullscreen is concurrent-safe.
func IsFullscreen() bool {
	return uiDriver().IsFullscreen()
}

// SetFullscreen changes the current mode to fullscreen or not on desktops.
//
// On fullscreen mode, the game screen is automatically enlarged
// to fit with the monitor. The current scale value is ignored.
//
// On desktops, Ebiten uses 'windowed' fullscreen mode, which doesn't change
// your monitor's resolution.
//
// SetFullscreen does nothing on browsers or mobiles.
//
// SetFullscreen is concurrent-safe.
func SetFullscreen(fullscreen bool) {
	uiDriver().SetFullscreen(fullscreen)
}

// IsFocused returns a boolean value indicating whether
// the game is in focus or in the foreground.
//
// IsFocused will only return true if IsRunnableOnUnfocused is false.
//
// IsFocused is concurrent-safe.
func IsFocused() bool {
	return uiDriver().IsFocused()
}

// IsRunnableOnUnfocused returns a boolean value indicating whether
// the game runs even in background.
//
// IsRunnableOnUnfocused is concurrent-safe.
func IsRunnableOnUnfocused() bool {
	return uiDriver().IsRunnableOnUnfocused()
}

// SetRunnableOnUnfocused sets the state if the game runs even in background.
//
// If the given value is true, the game runs even in background e.g. when losing focus.
// The initial state is true.
//
// Known issue: On browsers, even if the state is on, the game doesn't run in background tabs.
// This is because browsers throttles background tabs not to often update.
//
// SetRunnableOnUnfocused does nothing on mobiles so far.
//
// SetRunnableOnUnfocused is concurrent-safe.
func SetRunnableOnUnfocused(runnableOnUnfocused bool) {
	uiDriver().SetRunnableOnUnfocused(runnableOnUnfocused)
}

// DeviceScaleFactor returns a device scale factor value of the current monitor which the window belongs to.
//
// DeviceScaleFactor returns a meaningful value on high-DPI display environment,
// otherwise DeviceScaleFactor returns 1.
//
// DeviceScaleFactor might panic on init function on some devices like Android.
// Then, it is not recommended to call DeviceScaleFactor from init functions.
//
// DeviceScaleFactor must be called on the main thread before the main loop, and is concurrent-safe after the main
// loop.
func DeviceScaleFactor() float64 {
	return uiDriver().DeviceScaleFactor()
}

// IsVsyncEnabled returns a boolean value indicating whether
// the game uses the display's vsync.
//
// IsVsyncEnabled is concurrent-safe.
func IsVsyncEnabled() bool {
	return uiDriver().IsVsyncEnabled()
}

// SetVsyncEnabled sets a boolean value indicating whether
// the game uses the display's vsync.
//
// If the given value is true, the game tries to sync the display's refresh rate.
// If false, the game ignores the display's refresh rate.
// The initial value is true.
// By disabling vsync, the game works more efficiently but consumes more CPU.
//
// Note that the state doesn't affect TPS (ticks per second, i.e. how many the run function is
// updated per second).
//
// SetVsyncEnabled does nothing on mobiles so far.
//
// SetVsyncEnabled is concurrent-safe.
func SetVsyncEnabled(enabled bool) {
	uiDriver().SetVsyncEnabled(enabled)
}

// MaxTPS returns the current maximum TPS.
//
// MaxTPS is concurrent-safe.
func MaxTPS() int {
	return int(atomic.LoadInt32(&currentMaxTPS))
}

// CurrentTPS returns the current TPS (ticks per second),
// that represents how many update function is called in a second.
//
// CurrentTPS is concurrent-safe.
func CurrentTPS() float64 {
	return clock.CurrentTPS()
}

// UncappedTPS is a special TPS value that means the game doesn't have limitation on TPS.
const UncappedTPS = clock.UncappedTPS

// SetMaxTPS sets the maximum TPS (ticks per second),
// that represents how many updating function is called per second.
// The initial value is 60.
//
// If tps is UncappedTPS, TPS is uncapped and the game is updated per frame.
// If tps is negative but not UncappedTPS, SetMaxTPS panics.
//
// SetMaxTPS is concurrent-safe.
func SetMaxTPS(tps int) {
	if tps < 0 && tps != UncappedTPS {
		panic("ebiten: tps must be >= 0 or UncappedTPS")
	}
	atomic.StoreInt32(&currentMaxTPS, int32(tps))
}

// IsScreenTransparent reports whether the window is transparent.
//
// IsScreenTransparent is concurrent-safe.
func IsScreenTransparent() bool {
	return uiDriver().IsScreenTransparent()
}

// SetScreenTransparent sets the state if the window is transparent.
//
// SetScreenTransparent panics if SetScreenTransparent is called after the main loop.
//
// SetScreenTransparent does nothing on mobiles.
//
// SetScreenTransparent is concurrent-safe.
func SetScreenTransparent(transparent bool) {
	uiDriver().SetScreenTransparent(transparent)
}

// SetInitFocused sets whether the application is focused on show.
// The default value is true, i.e., the application is focused.
// Note that the application does not proceed if this is not focused by default.
// This behavior can be changed by SetRunnableInBackground.
//
// SetInitFocused does nothing on mobile.
//
// SetInitFocused panics if this is called after the main loop.
//
// SetInitFocused is cuncurrent-safe.
func SetInitFocused(focused bool) {
	uiDriver().SetInitFocused(focused)
}
<commit_msg>ebiten: Bug fix: Wrong documentation about SetScreenClearedEveryFrame<commit_after>// Copyright 2014 Hajime Hoshi
//
// 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.

package ebiten

import (
	"sync/atomic"

	"github.com/hajimehoshi/ebiten/v2/internal/clock"
	"github.com/hajimehoshi/ebiten/v2/internal/driver"
)

// Game defines necessary functions for a game.
type Game interface {
	// Update updates a game by one tick. The given argument represents a screen image.
	//
	// Update updates only the game logic and Draw draws the screen.
	//
	// In the first frame, it is ensured that Update is called at least once before Draw. You can use Update
	// to initialize the game state.
	//
	// After the first frame, Update might not be called or might be called once
	// or more for one frame. The frequency is determined by the current TPS (tick-per-second).
	Update() error

	// Draw draws the game screen by one frame.
	//
	// The give argument represents a screen image. The updated content is adopted as the game screen.
	Draw(screen *Image)

	// Layout accepts a native outside size in device-independent pixels and returns the game's logical screen
	// size.
	//
	// On desktops, the outside is a window or a monitor (fullscreen mode). On browsers, the outside is a body
	// element. On mobiles, the outside is the view's size.
	//
	// Even though the outside size and the screen size differ, the rendering scale is automatically adjusted to
	// fit with the outside.
	//
	// Layout is called almost every frame.
	//
	// It is ensured that Layout is invoked before Update is called in the first frame.
	//
	// If Layout returns non-positive numbers, the caller can panic.
	//
	// You can return a fixed screen size if you don't care, or you can also return a calculated screen size
	// adjusted with the given outside size.
	Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int)
}

// DefaultTPS represents a default ticks per second, that represents how many times game updating happens in a second.
const DefaultTPS = 60

// CurrentFPS returns the current number of FPS (frames per second), that represents
// how many swapping buffer happens per second.
//
// On some environments, CurrentFPS doesn't return a reliable value since vsync doesn't work well there.
// If you want to measure the application's speed, Use CurrentTPS.
//
// CurrentFPS is concurrent-safe.
func CurrentFPS() float64 {
	return clock.CurrentFPS()
}

var (
	isScreenClearedEveryFrame = int32(1)
	currentMaxTPS             = int32(DefaultTPS)
)

// SetScreenClearedEveryFrame enables or disables the clearing of the screen at the beginning of each frame.
// The default value is true and the screen is cleared each frame by default.
//
// SetScreenClearedEveryFrame is concurrent-safe.
func SetScreenClearedEveryFrame(cleared bool) {
	v := int32(0)
	if cleared {
		v = 1
	}
	atomic.StoreInt32(&isScreenClearedEveryFrame, v)
	theUIContext.setScreenClearedEveryFrame(cleared)
}

// IsScreenClearedEveryFrame returns true if the frame isn't cleared at the beginning.
//
// IsScreenClearedEveryFrame is concurrent-safe.
func IsScreenClearedEveryFrame() bool {
	return atomic.LoadInt32(&isScreenClearedEveryFrame) != 0
}

type imageDumperGame struct {
	game Game
	d    *imageDumper
	err  error
}

func (i *imageDumperGame) Update() error {
	if i.err != nil {
		return i.err
	}
	if i.d == nil {
		i.d = &imageDumper{g: i.game}
	}
	return i.d.update()
}

func (i *imageDumperGame) Draw(screen *Image) {
	if i.err != nil {
		return
	}

	i.game.Draw(screen)
	i.err = i.d.dump(screen)
}

func (i *imageDumperGame) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
	return i.game.Layout(outsideWidth, outsideHeight)
}

// RunGame starts the main loop and runs the game.
// game's Update function is called every tick to update the game logic.
// game's Draw function is, if it exists, called every frame to draw the screen.
// game's Layout function is called when necessary, and you can specify the logical screen size by the function.
//
// game must implement Game interface.
// Game's Draw function is optional, but it is recommended to implement Draw to seperate updating the logic and
// rendering.
//
// RunGame is a more flexibile form of Run due to game's Layout function.
// You can make a resizable window if you use RunGame, while you cannot if you use Run.
// RunGame is more sophisticated way than Run and hides the notion of 'scale'.
//
// While Run specifies the window size, RunGame does not.
// You need to call SetWindowSize before RunGame if you want.
// Otherwise, a default window size is adopted.
//
// Some functions (ScreenScale, SetScreenScale, SetScreenSize) are not available with RunGame.
//
// On browsers, it is strongly recommended to use iframe if you embed an Ebiten application in your website.
//
// RunGame must be called on the main thread.
// Note that Ebiten bounds the main goroutine to the main OS thread by runtime.LockOSThread.
//
// Ebiten tries to call game's Update function 60 times a second by default. In other words,
// TPS (ticks per second) is 60 by default.
// This is not related to framerate (display's refresh rate).
//
// RunGame returns error when 1) OpenGL error happens, 2) audio error happens or
// 3) f returns error. In the case of 3), RunGame returns the same error.
//
// The size unit is device-independent pixel.
//
// Don't call RunGame twice or more in one process.
func RunGame(game Game) error {
	fixWindowPosition(WindowSize())
	theUIContext.set(&imageDumperGame{
		game: game,
	})
	if err := uiDriver().Run(theUIContext); err != nil {
		if err == driver.RegularTermination {
			return nil
		}
		return err
	}
	return nil
}

// RunGameWithoutMainLoop runs the game, but don't call the loop on the main (UI) thread.
// Different from Run, RunGameWithoutMainLoop returns immediately.
//
// Ebiten users should NOT call RunGameWithoutMainLoop.
// Instead, functions in github.com/hajimehoshi/ebiten/v2/mobile package calls this.
//
// TODO: Remove this. In order to remove this, the uiContext should be in another package.
func RunGameWithoutMainLoop(game Game) {
	fixWindowPosition(WindowSize())
	theUIContext.set(&imageDumperGame{
		game: game,
	})
	uiDriver().RunWithoutMainLoop(theUIContext)
}

// ScreenSizeInFullscreen returns the size in device-independent pixels when the game is fullscreen.
// The adopted monitor is the 'current' monitor which the window belongs to.
// The returned value can be given to Run or SetSize function if the perfectly fit fullscreen is needed.
//
// On browsers, ScreenSizeInFullscreen returns the 'window' (global object) size, not 'screen' size since an Ebiten
// game should not know the outside of the window object. For more details, see SetFullscreen API comment.
//
// On mobiles, ScreenSizeInFullscreen returns (0, 0) so far.
//
// ScreenSizeInFullscreen's use cases are limited. If you are making a fullscreen application, you can use RunGame and
// the Game interface's Layout function instead. If you are making a not-fullscreen application but the application's
// behavior depends on the monitor size, ScreenSizeInFullscreen is useful.
//
// ScreenSizeInFullscreen must be called on the main thread before ebiten.Run, and is concurrent-safe after
// ebiten.Run.
func ScreenSizeInFullscreen() (int, int) {
	return uiDriver().ScreenSizeInFullscreen()
}

// CursorMode returns the current cursor mode.
//
// On browsers, only CursorModeVisible and CursorModeHidden are supported.
//
// CursorMode returns CursorModeHidden on mobiles.
//
// CursorMode is concurrent-safe.
func CursorMode() CursorModeType {
	return CursorModeType(uiDriver().CursorMode())
}

// SetCursorMode sets the render and capture mode of the mouse cursor.
// CursorModeVisible sets the cursor to always be visible.
// CursorModeHidden hides the system cursor when over the window.
// CursorModeCaptured hides the system cursor and locks it to the window.
//
// On browsers, only CursorModeVisible and CursorModeHidden are supported.
//
// SetCursorMode does nothing on mobiles.
//
// SetCursorMode is concurrent-safe.
func SetCursorMode(mode CursorModeType) {
	uiDriver().SetCursorMode(driver.CursorMode(mode))
}

// IsFullscreen reports whether the current mode is fullscreen or not.
//
// IsFullscreen always returns false on browsers or mobiles.
//
// IsFullscreen is concurrent-safe.
func IsFullscreen() bool {
	return uiDriver().IsFullscreen()
}

// SetFullscreen changes the current mode to fullscreen or not on desktops.
//
// On fullscreen mode, the game screen is automatically enlarged
// to fit with the monitor. The current scale value is ignored.
//
// On desktops, Ebiten uses 'windowed' fullscreen mode, which doesn't change
// your monitor's resolution.
//
// SetFullscreen does nothing on browsers or mobiles.
//
// SetFullscreen is concurrent-safe.
func SetFullscreen(fullscreen bool) {
	uiDriver().SetFullscreen(fullscreen)
}

// IsFocused returns a boolean value indicating whether
// the game is in focus or in the foreground.
//
// IsFocused will only return true if IsRunnableOnUnfocused is false.
//
// IsFocused is concurrent-safe.
func IsFocused() bool {
	return uiDriver().IsFocused()
}

// IsRunnableOnUnfocused returns a boolean value indicating whether
// the game runs even in background.
//
// IsRunnableOnUnfocused is concurrent-safe.
func IsRunnableOnUnfocused() bool {
	return uiDriver().IsRunnableOnUnfocused()
}

// SetRunnableOnUnfocused sets the state if the game runs even in background.
//
// If the given value is true, the game runs even in background e.g. when losing focus.
// The initial state is true.
//
// Known issue: On browsers, even if the state is on, the game doesn't run in background tabs.
// This is because browsers throttles background tabs not to often update.
//
// SetRunnableOnUnfocused does nothing on mobiles so far.
//
// SetRunnableOnUnfocused is concurrent-safe.
func SetRunnableOnUnfocused(runnableOnUnfocused bool) {
	uiDriver().SetRunnableOnUnfocused(runnableOnUnfocused)
}

// DeviceScaleFactor returns a device scale factor value of the current monitor which the window belongs to.
//
// DeviceScaleFactor returns a meaningful value on high-DPI display environment,
// otherwise DeviceScaleFactor returns 1.
//
// DeviceScaleFactor might panic on init function on some devices like Android.
// Then, it is not recommended to call DeviceScaleFactor from init functions.
//
// DeviceScaleFactor must be called on the main thread before the main loop, and is concurrent-safe after the main
// loop.
func DeviceScaleFactor() float64 {
	return uiDriver().DeviceScaleFactor()
}

// IsVsyncEnabled returns a boolean value indicating whether
// the game uses the display's vsync.
//
// IsVsyncEnabled is concurrent-safe.
func IsVsyncEnabled() bool {
	return uiDriver().IsVsyncEnabled()
}

// SetVsyncEnabled sets a boolean value indicating whether
// the game uses the display's vsync.
//
// If the given value is true, the game tries to sync the display's refresh rate.
// If false, the game ignores the display's refresh rate.
// The initial value is true.
// By disabling vsync, the game works more efficiently but consumes more CPU.
//
// Note that the state doesn't affect TPS (ticks per second, i.e. how many the run function is
// updated per second).
//
// SetVsyncEnabled does nothing on mobiles so far.
//
// SetVsyncEnabled is concurrent-safe.
func SetVsyncEnabled(enabled bool) {
	uiDriver().SetVsyncEnabled(enabled)
}

// MaxTPS returns the current maximum TPS.
//
// MaxTPS is concurrent-safe.
func MaxTPS() int {
	return int(atomic.LoadInt32(&currentMaxTPS))
}

// CurrentTPS returns the current TPS (ticks per second),
// that represents how many update function is called in a second.
//
// CurrentTPS is concurrent-safe.
func CurrentTPS() float64 {
	return clock.CurrentTPS()
}

// UncappedTPS is a special TPS value that means the game doesn't have limitation on TPS.
const UncappedTPS = clock.UncappedTPS

// SetMaxTPS sets the maximum TPS (ticks per second),
// that represents how many updating function is called per second.
// The initial value is 60.
//
// If tps is UncappedTPS, TPS is uncapped and the game is updated per frame.
// If tps is negative but not UncappedTPS, SetMaxTPS panics.
//
// SetMaxTPS is concurrent-safe.
func SetMaxTPS(tps int) {
	if tps < 0 && tps != UncappedTPS {
		panic("ebiten: tps must be >= 0 or UncappedTPS")
	}
	atomic.StoreInt32(&currentMaxTPS, int32(tps))
}

// IsScreenTransparent reports whether the window is transparent.
//
// IsScreenTransparent is concurrent-safe.
func IsScreenTransparent() bool {
	return uiDriver().IsScreenTransparent()
}

// SetScreenTransparent sets the state if the window is transparent.
//
// SetScreenTransparent panics if SetScreenTransparent is called after the main loop.
//
// SetScreenTransparent does nothing on mobiles.
//
// SetScreenTransparent is concurrent-safe.
func SetScreenTransparent(transparent bool) {
	uiDriver().SetScreenTransparent(transparent)
}

// SetInitFocused sets whether the application is focused on show.
// The default value is true, i.e., the application is focused.
// Note that the application does not proceed if this is not focused by default.
// This behavior can be changed by SetRunnableInBackground.
//
// SetInitFocused does nothing on mobile.
//
// SetInitFocused panics if this is called after the main loop.
//
// SetInitFocused is cuncurrent-safe.
func SetInitFocused(focused bool) {
	uiDriver().SetInitFocused(focused)
}
<|endoftext|>


<commit_before>// Copyright (C) 2015, 2016, 2017 Nicolas Lamirault <nicolas.lamirault@gmail.com>

// 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.

package storage

import (
	"fmt"

	"github.com/boltdb/bolt"
	"github.com/golang/glog"

	"github.com/nlamirault/abraracourcix/config"
	"github.com/nlamirault/abraracourcix/storage"
)

const (
	label      = "boltdb"
	bucketName = "abraracourcix"
)

type boltDB struct {
	db     *bolt.DB
	bucket string
	path   string
}

func init() {
	storage.RegisterStorage(label, newBoltdbStorage)
}

func newBoltdbStorage(conf *config.Configuration) (storage.Storage, error) {
	glog.V(1).Infof("Create storage using BoltDB : %s", conf.Storage)
	db, err := bolt.Open(conf.Storage.BoltDB.File, 0600, nil)
	if err != nil {
		return nil, err
	}
	return &boltDB{
		db:     db,
		bucket: conf.Storage.BoltDB.Bucket,
		path:   conf.Storage.BoltDB.File,
	}, nil
}

func (boltDB *boltDB) Name() string {
	return label
}

func (boltDB *boltDB) Init() error {
	return boltDB.db.Update(func(tx *bolt.Tx) error {
		_, err := tx.CreateBucketIfNotExists([]byte(boltDB.bucket))
		if err != nil {
			return fmt.Errorf("Can't create BoltDB bucket: %s", err)
		}
		return nil
	})
}

func (boltDB *boltDB) List() ([][]byte, error) {
	return nil, storage.ErrNotImplemented
}

func (boltDB *boltDB) Get(key []byte) ([]byte, error) {
	glog.V(1).Infof("Search entry with key : %v", string(key))
	var value []byte
	err := boltDB.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(boltDB.bucket))
		err := b.ForEach(func(k, v []byte) error {
			glog.V(3).Infof("Entry : %s %s", string(k), string(v))
			if string(k) == string(key) {
				glog.V(2).Infof("Find : %s", string(v))
				value = v
			}
			return nil
		})
		return err
	})
	if err != nil {
		return nil, err
	}
	return value, nil
}

func (boltDB *boltDB) Put(key []byte, value []byte) error {
	glog.V(1).Infof("Put : %v %v", string(key), string(value))
	err := boltDB.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(boltDB.bucket))
		err := b.Put(key, value)
		return err
	})
	if err != nil {
		return err
	}
	return nil
}

func (boltDB *boltDB) Delete(key []byte) error {
	glog.V(1).Infof("Delete : %v", string(key))
	return storage.ErrNotImplemented
}

func (boltDB *boltDB) Close() error {
	glog.V(1).Infof("Close")
	return nil
}

func (boltDB *boltDB) Print() error {
	glog.V(1).Infof("Storage backend: %s", label)
	return boltDB.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(boltDB.bucket))
		err := b.ForEach(func(key, value []byte) error {
			fmt.Printf("%s %s", string(key), string(value))
			return nil
		})
		return err
	})
}
<commit_msg>Update BoltDB backend<commit_after>// Copyright (C) 2015, 2016, 2017 Nicolas Lamirault <nicolas.lamirault@gmail.com>

// 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.

package storage

import (
	"fmt"

	"github.com/boltdb/bolt"
	"github.com/golang/glog"

	"github.com/nlamirault/abraracourcix/config"
	"github.com/nlamirault/abraracourcix/storage"
)

const (
	label      = "boltdb"
	bucketName = "abraracourcix"
)

type boltDB struct {
	db     *bolt.DB
	bucket string
	path   string
}

func init() {
	storage.RegisterStorage(label, newBoltdbStorage)
}

func newBoltdbStorage(conf *config.Configuration) (storage.Storage, error) {
	glog.V(1).Infof("Create storage using BoltDB : %s", conf.Storage)
	db, err := bolt.Open(conf.Storage.BoltDB.File, 0600, nil)
	if err != nil {
		return nil, err
	}
	return &boltDB{
		db:     db,
		bucket: conf.Storage.BoltDB.Bucket,
		path:   conf.Storage.BoltDB.File,
	}, nil
}

func (boltDB *boltDB) Name() string {
	return label
}

func (boltDB *boltDB) Init() error {
	return boltDB.db.Update(func(tx *bolt.Tx) error {
		_, err := tx.CreateBucketIfNotExists([]byte(boltDB.bucket))
		if err != nil {
			return fmt.Errorf("Can't create BoltDB bucket: %s", err)
		}
		return nil
	})
}

func (boltDB *boltDB) List() ([][]byte, error) {
	glog.V(1).Infof("List all URLs")
	urls := [][]byte{}
	err := boltDB.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(boltDB.bucket))
		b.ForEach(func(k, v []byte) error {
			glog.V(3).Infof("Entry : %s %s", string(k), string(v))
			urls = append(urls, k)
			return nil
		})
		return nil
	})
	if err != nil {
		return nil, err
	}
	glog.V(1).Infof("URLs: %s", urls)
	return urls, nil
}

func (boltDB *boltDB) Get(key []byte) ([]byte, error) {
	glog.V(1).Infof("Search entry with key : %v", string(key))
	var value []byte
	err := boltDB.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(boltDB.bucket))
		err := b.ForEach(func(k, v []byte) error {
			glog.V(3).Infof("Entry : %s %s", string(k), string(v))
			if string(k) == string(key) {
				glog.V(2).Infof("Find : %s", string(v))
				value = v
			}
			return nil
		})
		return err
	})
	if err != nil {
		return nil, err
	}
	return value, nil
}

func (boltDB *boltDB) Put(key []byte, value []byte) error {
	glog.V(1).Infof("Put : %v %v", string(key), string(value))
	err := boltDB.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(boltDB.bucket))
		err := b.Put(key, value)
		return err
	})
	if err != nil {
		return err
	}
	return nil
}

func (boltDB *boltDB) Delete(key []byte) error {
	glog.V(1).Infof("Delete : %v", string(key))
	return storage.ErrNotImplemented
}

func (boltDB *boltDB) Close() error {
	glog.V(1).Infof("Close")
	return nil
}

func (boltDB *boltDB) Print() error {
	glog.V(1).Infof("Storage backend: %s", label)
	return boltDB.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(boltDB.bucket))
		err := b.ForEach(func(key, value []byte) error {
			fmt.Printf("%s %s", string(key), string(value))
			return nil
		})
		return err
	})
}
<|endoftext|>


<commit_before>package consul

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/hashicorp/consul/consul/server_details"
	"github.com/hashicorp/consul/consul/server_manager"
	"github.com/hashicorp/consul/consul/structs"
	"github.com/hashicorp/serf/coordinate"
	"github.com/hashicorp/serf/serf"
)

const (
	// clientRPCConnMaxIdle controls how long we keep an idle connection
	// open to a server.  127s was chosen as the first prime above 120s
	// (arbitrarily chose to use a prime) with the intent of reusing
	// connections who are used by once-a-minute cron(8) jobs *and* who
	// use a 60s jitter window (e.g. in vixie cron job execution can
	// drift by up to 59s per job, or 119s for a once-a-minute cron job).
	clientRPCConnMaxIdle = 127 * time.Second

	// clientMaxStreams controls how many idle streams we keep
	// open to a server
	clientMaxStreams = 32

	// serfEventBacklog is the maximum number of unprocessed Serf Events
	// that will be held in queue before new serf events block.  A
	// blocking serf event queue is a bad thing.
	serfEventBacklog = 256

	// serfEventBacklogWarning is the threshold at which point log
	// warnings will be emitted indicating a problem when processing serf
	// events.
	serfEventBacklogWarning = 200
)

// Interface is used to provide either a Client or Server,
// both of which can be used to perform certain common
// Consul methods
type Interface interface {
	RPC(method string, args interface{}, reply interface{}) error
	LANMembers() []serf.Member
	LocalMember() serf.Member
}

// Client is Consul client which uses RPC to communicate with the
// services for service discovery, health checking, and DC forwarding.
type Client struct {
	config *Config

	// Connection pool to consul servers
	connPool *ConnPool

	// serverMgr is responsible for the selection and maintenance of
	// Consul servers this agent uses for RPC requests
	serverMgr *server_manager.ServerManager

	// eventCh is used to receive events from the
	// serf cluster in the datacenter
	eventCh chan serf.Event

	// Logger uses the provided LogOutput
	logger *log.Logger

	// serf is the Serf cluster maintained inside the DC
	// which contains all the DC nodes
	serf *serf.Serf

	shutdown     bool
	shutdownCh   chan struct{}
	shutdownLock sync.Mutex
}

// NewClient is used to construct a new Consul client from the
// configuration, potentially returning an error
func NewClient(config *Config) (*Client, error) {
	// Check the protocol version
	if err := config.CheckVersion(); err != nil {
		return nil, err
	}

	// Check for a data directory!
	if config.DataDir == "" {
		return nil, fmt.Errorf("Config must provide a DataDir")
	}

	// Sanity check the ACLs
	if err := config.CheckACL(); err != nil {
		return nil, err
	}

	// Ensure we have a log output
	if config.LogOutput == nil {
		config.LogOutput = os.Stderr
	}

	// Create the tls Wrapper
	tlsWrap, err := config.tlsConfig().OutgoingTLSWrapper()
	if err != nil {
		return nil, err
	}

	// Create a logger
	logger := log.New(config.LogOutput, "", log.LstdFlags)

	// Create server
	c := &Client{
		config:     config,
		connPool:   NewPool(config.LogOutput, clientRPCConnMaxIdle, clientMaxStreams, tlsWrap),
		eventCh:    make(chan serf.Event, serfEventBacklog),
		logger:     logger,
		shutdownCh: make(chan struct{}),
	}

	c.serverMgr = server_manager.New(c.logger, c.shutdownCh, c.serf)

	// Start consulServers maintenance
	go c.serverMgr.Start()

	// Start the Serf listeners to prevent a deadlock
	go c.lanEventHandler()

	// Initialize the lan Serf
	c.serf, err = c.setupSerf(config.SerfLANConfig,
		c.eventCh, serfLANSnapshot)
	if err != nil {
		c.Shutdown()
		return nil, fmt.Errorf("Failed to start lan serf: %v", err)
	}
	return c, nil
}

// setupSerf is used to setup and initialize a Serf
func (c *Client) setupSerf(conf *serf.Config, ch chan serf.Event, path string) (*serf.Serf, error) {
	conf.Init()
	conf.NodeName = c.config.NodeName
	conf.Tags["role"] = "node"
	conf.Tags["dc"] = c.config.Datacenter
	conf.Tags["vsn"] = fmt.Sprintf("%d", c.config.ProtocolVersion)
	conf.Tags["vsn_min"] = fmt.Sprintf("%d", ProtocolVersionMin)
	conf.Tags["vsn_max"] = fmt.Sprintf("%d", ProtocolVersionMax)
	conf.Tags["build"] = c.config.Build
	conf.MemberlistConfig.LogOutput = c.config.LogOutput
	conf.LogOutput = c.config.LogOutput
	conf.EventCh = ch
	conf.SnapshotPath = filepath.Join(c.config.DataDir, path)
	conf.ProtocolVersion = protocolVersionMap[c.config.ProtocolVersion]
	conf.RejoinAfterLeave = c.config.RejoinAfterLeave
	conf.Merge = &lanMergeDelegate{dc: c.config.Datacenter}
	conf.DisableCoordinates = c.config.DisableCoordinates
	if err := ensurePath(conf.SnapshotPath, false); err != nil {
		return nil, err
	}
	return serf.Create(conf)
}

// Shutdown is used to shutdown the client
func (c *Client) Shutdown() error {
	c.logger.Printf("[INFO] consul: shutting down client")
	c.shutdownLock.Lock()
	defer c.shutdownLock.Unlock()

	if c.shutdown {
		return nil
	}

	c.shutdown = true
	close(c.shutdownCh)

	if c.serf != nil {
		c.serf.Shutdown()
	}

	// Close the connection pool
	c.connPool.Shutdown()
	return nil
}

// Leave is used to prepare for a graceful shutdown
func (c *Client) Leave() error {
	c.logger.Printf("[INFO] consul: client starting leave")

	// Leave the LAN pool
	if c.serf != nil {
		if err := c.serf.Leave(); err != nil {
			c.logger.Printf("[ERR] consul: Failed to leave LAN Serf cluster: %v", err)
		}
	}
	return nil
}

// JoinLAN is used to have Consul client join the inner-DC pool
// The target address should be another node inside the DC
// listening on the Serf LAN address
func (c *Client) JoinLAN(addrs []string) (int, error) {
	return c.serf.Join(addrs, true)
}

// LocalMember is used to return the local node
func (c *Client) LocalMember() serf.Member {
	return c.serf.LocalMember()
}

// LANMembers is used to return the members of the LAN cluster
func (c *Client) LANMembers() []serf.Member {
	return c.serf.Members()
}

// RemoveFailedNode is used to remove a failed node from the cluster
func (c *Client) RemoveFailedNode(node string) error {
	return c.serf.RemoveFailedNode(node)
}

// KeyManagerLAN returns the LAN Serf keyring manager
func (c *Client) KeyManagerLAN() *serf.KeyManager {
	return c.serf.KeyManager()
}

// Encrypted determines if gossip is encrypted
func (c *Client) Encrypted() bool {
	return c.serf.EncryptionEnabled()
}

// lanEventHandler is used to handle events from the lan Serf cluster
func (c *Client) lanEventHandler() {
	var numQueuedEvents int
	for {
		numQueuedEvents = len(c.eventCh)
		if numQueuedEvents > serfEventBacklogWarning {
			c.logger.Printf("[WARN] consul: number of queued serf events above warning threshold: %d/%d", numQueuedEvents, serfEventBacklogWarning)
		}

		select {
		case e := <-c.eventCh:
			switch e.EventType() {
			case serf.EventMemberJoin:
				c.nodeJoin(e.(serf.MemberEvent))
			case serf.EventMemberLeave, serf.EventMemberFailed:
				c.nodeFail(e.(serf.MemberEvent))
			case serf.EventUser:
				c.localEvent(e.(serf.UserEvent))
			case serf.EventMemberUpdate: // Ignore
			case serf.EventMemberReap: // Ignore
			case serf.EventQuery: // Ignore
			default:
				c.logger.Printf("[WARN] consul: unhandled LAN Serf Event: %#v", e)
			}
		case <-c.shutdownCh:
			return
		}
	}
}

// nodeJoin is used to handle join events on the serf cluster
func (c *Client) nodeJoin(me serf.MemberEvent) {
	for _, m := range me.Members {
		ok, parts := server_details.IsConsulServer(m)
		if !ok {
			continue
		}
		if parts.Datacenter != c.config.Datacenter {
			c.logger.Printf("[WARN] consul: server %s for datacenter %s has joined wrong cluster",
				m.Name, parts.Datacenter)
			continue
		}
		c.logger.Printf("[INFO] consul: adding server %s", parts)
		c.serverMgr.AddServer(parts)

		// Trigger the callback
		if c.config.ServerUp != nil {
			c.config.ServerUp()
		}
	}
}

// nodeFail is used to handle fail events on the serf cluster
func (c *Client) nodeFail(me serf.MemberEvent) {
	for _, m := range me.Members {
		ok, parts := server_details.IsConsulServer(m)
		if !ok {
			continue
		}
		c.logger.Printf("[INFO] consul: removing server %s", parts)
		c.serverMgr.RemoveServer(parts)
	}
}

// localEvent is called when we receive an event on the local Serf
func (c *Client) localEvent(event serf.UserEvent) {
	// Handle only consul events
	if !strings.HasPrefix(event.Name, "consul:") {
		return
	}

	switch name := event.Name; {
	case name == newLeaderEvent:
		c.logger.Printf("[INFO] consul: New leader elected: %s", event.Payload)

		// Trigger the callback
		if c.config.ServerUp != nil {
			c.config.ServerUp()
		}
	case isUserEvent(name):
		event.Name = rawUserEventName(name)
		c.logger.Printf("[DEBUG] consul: user event: %s", event.Name)

		// Trigger the callback
		if c.config.UserEventHandler != nil {
			c.config.UserEventHandler(event)
		}
	default:
		c.logger.Printf("[WARN] consul: Unhandled local event: %v", event)
	}
}

// RPC is used to forward an RPC call to a consul server, or fail if no servers
func (c *Client) RPC(method string, args interface{}, reply interface{}) error {
	server := c.serverMgr.FindServer()
	if server == nil {
		c.logger.Printf("[ERR] consul: No healthy servers found in the server config")
		return structs.ErrNoServers
	}

	// Forward to remote Consul
	if err := c.connPool.RPC(c.config.Datacenter, server.Addr, server.Version, method, args, reply); err != nil {
		c.serverMgr.NotifyFailedServer(server)
		c.logger.Printf("[ERR] consul: RPC failed to server %s: %v", server.Addr, err)
		return err
	}

	return nil
}

// Stats is used to return statistics for debugging and insight
// for various sub-systems
func (c *Client) Stats() map[string]map[string]string {
	numServers := c.serverMgr.NumServers()

	toString := func(v uint64) string {
		return strconv.FormatUint(v, 10)
	}
	stats := map[string]map[string]string{
		"consul": map[string]string{
			"server":        "false",
			"known_servers": toString(uint64(numServers)),
		},
		"serf_lan": c.serf.Stats(),
		"runtime":  runtimeStats(),
	}
	return stats
}

// GetCoordinate returns the network coordinate of the current node, as
// maintained by Serf.
func (c *Client) GetCoordinate() (*coordinate.Coordinate, error) {
	return c.serf.GetCoordinate()
}
<commit_msg>Fix stale comment<commit_after>package consul

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/hashicorp/consul/consul/server_details"
	"github.com/hashicorp/consul/consul/server_manager"
	"github.com/hashicorp/consul/consul/structs"
	"github.com/hashicorp/serf/coordinate"
	"github.com/hashicorp/serf/serf"
)

const (
	// clientRPCConnMaxIdle controls how long we keep an idle connection
	// open to a server.  127s was chosen as the first prime above 120s
	// (arbitrarily chose to use a prime) with the intent of reusing
	// connections who are used by once-a-minute cron(8) jobs *and* who
	// use a 60s jitter window (e.g. in vixie cron job execution can
	// drift by up to 59s per job, or 119s for a once-a-minute cron job).
	clientRPCConnMaxIdle = 127 * time.Second

	// clientMaxStreams controls how many idle streams we keep
	// open to a server
	clientMaxStreams = 32

	// serfEventBacklog is the maximum number of unprocessed Serf Events
	// that will be held in queue before new serf events block.  A
	// blocking serf event queue is a bad thing.
	serfEventBacklog = 256

	// serfEventBacklogWarning is the threshold at which point log
	// warnings will be emitted indicating a problem when processing serf
	// events.
	serfEventBacklogWarning = 200
)

// Interface is used to provide either a Client or Server,
// both of which can be used to perform certain common
// Consul methods
type Interface interface {
	RPC(method string, args interface{}, reply interface{}) error
	LANMembers() []serf.Member
	LocalMember() serf.Member
}

// Client is Consul client which uses RPC to communicate with the
// services for service discovery, health checking, and DC forwarding.
type Client struct {
	config *Config

	// Connection pool to consul servers
	connPool *ConnPool

	// serverMgr is responsible for the selection and maintenance of
	// Consul servers this agent uses for RPC requests
	serverMgr *server_manager.ServerManager

	// eventCh is used to receive events from the
	// serf cluster in the datacenter
	eventCh chan serf.Event

	// Logger uses the provided LogOutput
	logger *log.Logger

	// serf is the Serf cluster maintained inside the DC
	// which contains all the DC nodes
	serf *serf.Serf

	shutdown     bool
	shutdownCh   chan struct{}
	shutdownLock sync.Mutex
}

// NewClient is used to construct a new Consul client from the
// configuration, potentially returning an error
func NewClient(config *Config) (*Client, error) {
	// Check the protocol version
	if err := config.CheckVersion(); err != nil {
		return nil, err
	}

	// Check for a data directory!
	if config.DataDir == "" {
		return nil, fmt.Errorf("Config must provide a DataDir")
	}

	// Sanity check the ACLs
	if err := config.CheckACL(); err != nil {
		return nil, err
	}

	// Ensure we have a log output
	if config.LogOutput == nil {
		config.LogOutput = os.Stderr
	}

	// Create the tls Wrapper
	tlsWrap, err := config.tlsConfig().OutgoingTLSWrapper()
	if err != nil {
		return nil, err
	}

	// Create a logger
	logger := log.New(config.LogOutput, "", log.LstdFlags)

	// Create server
	c := &Client{
		config:     config,
		connPool:   NewPool(config.LogOutput, clientRPCConnMaxIdle, clientMaxStreams, tlsWrap),
		eventCh:    make(chan serf.Event, serfEventBacklog),
		logger:     logger,
		shutdownCh: make(chan struct{}),
	}

	c.serverMgr = server_manager.New(c.logger, c.shutdownCh, c.serf)

	// Start maintenance task for serverMgr
	go c.serverMgr.Start()

	// Start the Serf listeners to prevent a deadlock
	go c.lanEventHandler()

	// Initialize the lan Serf
	c.serf, err = c.setupSerf(config.SerfLANConfig,
		c.eventCh, serfLANSnapshot)
	if err != nil {
		c.Shutdown()
		return nil, fmt.Errorf("Failed to start lan serf: %v", err)
	}
	return c, nil
}

// setupSerf is used to setup and initialize a Serf
func (c *Client) setupSerf(conf *serf.Config, ch chan serf.Event, path string) (*serf.Serf, error) {
	conf.Init()
	conf.NodeName = c.config.NodeName
	conf.Tags["role"] = "node"
	conf.Tags["dc"] = c.config.Datacenter
	conf.Tags["vsn"] = fmt.Sprintf("%d", c.config.ProtocolVersion)
	conf.Tags["vsn_min"] = fmt.Sprintf("%d", ProtocolVersionMin)
	conf.Tags["vsn_max"] = fmt.Sprintf("%d", ProtocolVersionMax)
	conf.Tags["build"] = c.config.Build
	conf.MemberlistConfig.LogOutput = c.config.LogOutput
	conf.LogOutput = c.config.LogOutput
	conf.EventCh = ch
	conf.SnapshotPath = filepath.Join(c.config.DataDir, path)
	conf.ProtocolVersion = protocolVersionMap[c.config.ProtocolVersion]
	conf.RejoinAfterLeave = c.config.RejoinAfterLeave
	conf.Merge = &lanMergeDelegate{dc: c.config.Datacenter}
	conf.DisableCoordinates = c.config.DisableCoordinates
	if err := ensurePath(conf.SnapshotPath, false); err != nil {
		return nil, err
	}
	return serf.Create(conf)
}

// Shutdown is used to shutdown the client
func (c *Client) Shutdown() error {
	c.logger.Printf("[INFO] consul: shutting down client")
	c.shutdownLock.Lock()
	defer c.shutdownLock.Unlock()

	if c.shutdown {
		return nil
	}

	c.shutdown = true
	close(c.shutdownCh)

	if c.serf != nil {
		c.serf.Shutdown()
	}

	// Close the connection pool
	c.connPool.Shutdown()
	return nil
}

// Leave is used to prepare for a graceful shutdown
func (c *Client) Leave() error {
	c.logger.Printf("[INFO] consul: client starting leave")

	// Leave the LAN pool
	if c.serf != nil {
		if err := c.serf.Leave(); err != nil {
			c.logger.Printf("[ERR] consul: Failed to leave LAN Serf cluster: %v", err)
		}
	}
	return nil
}

// JoinLAN is used to have Consul client join the inner-DC pool
// The target address should be another node inside the DC
// listening on the Serf LAN address
func (c *Client) JoinLAN(addrs []string) (int, error) {
	return c.serf.Join(addrs, true)
}

// LocalMember is used to return the local node
func (c *Client) LocalMember() serf.Member {
	return c.serf.LocalMember()
}

// LANMembers is used to return the members of the LAN cluster
func (c *Client) LANMembers() []serf.Member {
	return c.serf.Members()
}

// RemoveFailedNode is used to remove a failed node from the cluster
func (c *Client) RemoveFailedNode(node string) error {
	return c.serf.RemoveFailedNode(node)
}

// KeyManagerLAN returns the LAN Serf keyring manager
func (c *Client) KeyManagerLAN() *serf.KeyManager {
	return c.serf.KeyManager()
}

// Encrypted determines if gossip is encrypted
func (c *Client) Encrypted() bool {
	return c.serf.EncryptionEnabled()
}

// lanEventHandler is used to handle events from the lan Serf cluster
func (c *Client) lanEventHandler() {
	var numQueuedEvents int
	for {
		numQueuedEvents = len(c.eventCh)
		if numQueuedEvents > serfEventBacklogWarning {
			c.logger.Printf("[WARN] consul: number of queued serf events above warning threshold: %d/%d", numQueuedEvents, serfEventBacklogWarning)
		}

		select {
		case e := <-c.eventCh:
			switch e.EventType() {
			case serf.EventMemberJoin:
				c.nodeJoin(e.(serf.MemberEvent))
			case serf.EventMemberLeave, serf.EventMemberFailed:
				c.nodeFail(e.(serf.MemberEvent))
			case serf.EventUser:
				c.localEvent(e.(serf.UserEvent))
			case serf.EventMemberUpdate: // Ignore
			case serf.EventMemberReap: // Ignore
			case serf.EventQuery: // Ignore
			default:
				c.logger.Printf("[WARN] consul: unhandled LAN Serf Event: %#v", e)
			}
		case <-c.shutdownCh:
			return
		}
	}
}

// nodeJoin is used to handle join events on the serf cluster
func (c *Client) nodeJoin(me serf.MemberEvent) {
	for _, m := range me.Members {
		ok, parts := server_details.IsConsulServer(m)
		if !ok {
			continue
		}
		if parts.Datacenter != c.config.Datacenter {
			c.logger.Printf("[WARN] consul: server %s for datacenter %s has joined wrong cluster",
				m.Name, parts.Datacenter)
			continue
		}
		c.logger.Printf("[INFO] consul: adding server %s", parts)
		c.serverMgr.AddServer(parts)

		// Trigger the callback
		if c.config.ServerUp != nil {
			c.config.ServerUp()
		}
	}
}

// nodeFail is used to handle fail events on the serf cluster
func (c *Client) nodeFail(me serf.MemberEvent) {
	for _, m := range me.Members {
		ok, parts := server_details.IsConsulServer(m)
		if !ok {
			continue
		}
		c.logger.Printf("[INFO] consul: removing server %s", parts)
		c.serverMgr.RemoveServer(parts)
	}
}

// localEvent is called when we receive an event on the local Serf
func (c *Client) localEvent(event serf.UserEvent) {
	// Handle only consul events
	if !strings.HasPrefix(event.Name, "consul:") {
		return
	}

	switch name := event.Name; {
	case name == newLeaderEvent:
		c.logger.Printf("[INFO] consul: New leader elected: %s", event.Payload)

		// Trigger the callback
		if c.config.ServerUp != nil {
			c.config.ServerUp()
		}
	case isUserEvent(name):
		event.Name = rawUserEventName(name)
		c.logger.Printf("[DEBUG] consul: user event: %s", event.Name)

		// Trigger the callback
		if c.config.UserEventHandler != nil {
			c.config.UserEventHandler(event)
		}
	default:
		c.logger.Printf("[WARN] consul: Unhandled local event: %v", event)
	}
}

// RPC is used to forward an RPC call to a consul server, or fail if no servers
func (c *Client) RPC(method string, args interface{}, reply interface{}) error {
	server := c.serverMgr.FindServer()
	if server == nil {
		c.logger.Printf("[ERR] consul: No healthy servers found in the server config")
		return structs.ErrNoServers
	}

	// Forward to remote Consul
	if err := c.connPool.RPC(c.config.Datacenter, server.Addr, server.Version, method, args, reply); err != nil {
		c.serverMgr.NotifyFailedServer(server)
		c.logger.Printf("[ERR] consul: RPC failed to server %s: %v", server.Addr, err)
		return err
	}

	return nil
}

// Stats is used to return statistics for debugging and insight
// for various sub-systems
func (c *Client) Stats() map[string]map[string]string {
	numServers := c.serverMgr.NumServers()

	toString := func(v uint64) string {
		return strconv.FormatUint(v, 10)
	}
	stats := map[string]map[string]string{
		"consul": map[string]string{
			"server":        "false",
			"known_servers": toString(uint64(numServers)),
		},
		"serf_lan": c.serf.Stats(),
		"runtime":  runtimeStats(),
	}
	return stats
}

// GetCoordinate returns the network coordinate of the current node, as
// maintained by Serf.
func (c *Client) GetCoordinate() (*coordinate.Coordinate, error) {
	return c.serf.GetCoordinate()
}
<|endoftext|>


<commit_before>package xero

import (
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestContactIterator_url(t *testing.T) {
	type testcase struct {
		tname       string
		page        int
		expectedURL string
	}
	tt := []testcase{
		testcase{
			tname:       "page 1",
			page:        1,
			expectedURL: "https://api.xero.com/api.xro/2.0/Contacts?page=1",
		},
		testcase{
			tname:       "page 2",
			page:        2,
			expectedURL: "https://api.xero.com/api.xro/2.0/Contacts?page=2",
		},
		testcase{
			tname:       "page 3",
			page:        3,
			expectedURL: "https://api.xero.com/api.xro/2.0/Contacts?page=3",
		},
	}
	for _, tc := range tt {
		t.Run(tc.tname, func(t *testing.T) {
			i := ContactIterator{tc.page, &Client{}, &url.URL{
				Scheme: "https",
				Host:   "api.xero.com",
				Path:   "/api.xro/2.0/Contacts",
			}}
			assert.Equal(t, tc.expectedURL, i.url())
		})
	}
}

func TestContactIterator_Next(t *testing.T) {
	type testcase struct {
		tname            string
		ts               func(t *testing.T) (*httptest.Server, *url.URL)
		expectedContacts []Contact
		expectedErr      error
	}
	tt := []testcase{
		testcase{
			tname: "returns contacts",
			ts: func(t *testing.T) (*httptest.Server, *url.URL) {
				ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
					w.WriteHeader(http.StatusOK)
					w.Write([]byte(`<Response>
						<Contacts>
							<Contact>
								<Name>Foo</Name>
							</Contact>
						</Contacts>
					</Response>`))
				}))
				u, err := url.Parse(ts.URL)
				assert.NoError(t, err)
				return ts, u
			},
			expectedContacts: []Contact{{Name: "Foo"}},
		},
	}
	for _, tc := range tt {
		t.Run(tc.tname, func(t *testing.T) {
			ts, tsUrl := tc.ts(t)
			defer ts.Close()
			c := &Client{
				authorizer: new(testAuthorizer),
				scheme:     tsUrl.Scheme,
				host:       tsUrl.Host,
				root:       tsUrl.Path,
			}
			i := ContactIterator{1, c, c.url("/")}
			_, contacts, err := i.Next()
			assert.Equal(t, tc.expectedErr, err)
			assert.Equal(t, tc.expectedContacts, contacts)
		})
	}
}
<commit_msg>test no contacts returns io.EOF<commit_after>package xero

import (
	"encoding/xml"
	"io"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestContactIterator_url(t *testing.T) {
	type testcase struct {
		tname       string
		page        int
		expectedURL string
	}
	tt := []testcase{
		testcase{
			tname:       "page 1",
			page:        1,
			expectedURL: "https://api.xero.com/api.xro/2.0/Contacts?page=1",
		},
		testcase{
			tname:       "page 2",
			page:        2,
			expectedURL: "https://api.xero.com/api.xro/2.0/Contacts?page=2",
		},
		testcase{
			tname:       "page 3",
			page:        3,
			expectedURL: "https://api.xero.com/api.xro/2.0/Contacts?page=3",
		},
	}
	for _, tc := range tt {
		t.Run(tc.tname, func(t *testing.T) {
			i := ContactIterator{tc.page, &Client{}, &url.URL{
				Scheme: "https",
				Host:   "api.xero.com",
				Path:   "/api.xro/2.0/Contacts",
			}}
			assert.Equal(t, tc.expectedURL, i.url())
		})
	}
}

func TestContactIterator_Next(t *testing.T) {
	type testcase struct {
		tname            string
		ts               func(t *testing.T) (*httptest.Server, *url.URL)
		expectedContacts []Contact
		expectedErr      error
	}
	tt := []testcase{
		testcase{
			tname: "bad xml",
			ts: func(t *testing.T) (*httptest.Server, *url.URL) {
				ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
					w.WriteHeader(http.StatusOK)
					w.Write([]byte(`</uwot>`))
				}))
				u, err := url.Parse(ts.URL)
				assert.NoError(t, err)
				return ts, u
			},
			expectedErr: &xml.SyntaxError{
				Msg:  "unexpected end element </uwot>",
				Line: 1,
			},
		},
		testcase{
			tname: "no contacts EOF",
			ts: func(t *testing.T) (*httptest.Server, *url.URL) {
				ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
					w.WriteHeader(http.StatusOK)
					w.Write([]byte(`<Response>
						<Contacts></Contacts>
					</Response>`))
				}))
				u, err := url.Parse(ts.URL)
				assert.NoError(t, err)
				return ts, u
			},
			expectedErr: io.EOF,
		},
		testcase{
			tname: "returns contacts",
			ts: func(t *testing.T) (*httptest.Server, *url.URL) {
				ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
					w.WriteHeader(http.StatusOK)
					w.Write([]byte(`<Response>
						<Contacts>
							<Contact>
								<Name>Foo</Name>
							</Contact>
						</Contacts>
					</Response>`))
				}))
				u, err := url.Parse(ts.URL)
				assert.NoError(t, err)
				return ts, u
			},
			expectedContacts: []Contact{{Name: "Foo"}},
		},
	}
	for _, tc := range tt {
		t.Run(tc.tname, func(t *testing.T) {
			ts, tsUrl := tc.ts(t)
			defer ts.Close()
			c := &Client{
				authorizer: new(testAuthorizer),
				scheme:     tsUrl.Scheme,
				host:       tsUrl.Host,
				root:       tsUrl.Path,
			}
			i := ContactIterator{1, c, c.url("/")}
			_, contacts, err := i.Next()
			assert.Equal(t, tc.expectedErr, err)
			assert.Equal(t, tc.expectedContacts, contacts)
		})
	}
}
<|endoftext|>