<commit_before>package ingo

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"os/user"
	"path"
	"strings"
	"unicode/utf8"
)

var (
	obsoleteKeys = make(map[string]string)
)

func Parse(appName string) error {
	if flag.Parsed() {
		return fmt.Errorf("flags have been parsed already.")
	}

	envname := strings.ToUpper(appName) + "RC"
	cPath := os.Getenv(envname)
	if cPath == "" {
		usr, err := user.Current()
		if err != nil {
			return fmt.Errorf("%v\nYou can set the environment variable %s to point to your config file as a workaround.", err, envname)
		}
		cPath = path.Join(usr.HomeDir, "."+strings.ToLower(appName)+"rc")
	}

	if err := loadConfig(appName, cPath); err != nil {
		return err
	}
	if err := saveConfig(appName, cPath); err != nil {
		return err
	}
	flag.Parse()
	return nil
}

func loadConfig(appName, configPath string) error {
	fin, err := os.Open(configPath)
	if _, ok := err.(*os.PathError); ok {
		fmt.Fprintf(os.Stderr, "No config file found for %s. Creating %s ...\n", appName, configPath)
		return nil
	} else if err != nil {
		return fmt.Errorf("Unable to read %s config file %v: %v", appName, configPath, err)
	}
	defer fin.Close()

	scanner := bufio.NewScanner(fin)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if strings.HasPrefix(line, "#") {
			continue
		}

		// find first assignment symbol and parse key, val
		i := strings.IndexAny(line, "=:")
		if i == -1 {
			continue
		}
		key, val := strings.TrimSpace(line[:i]), strings.TrimSpace(line[i+1:])

		if err := flag.Set(key, val); err != nil {
			obsoleteKeys[key] = val
			continue
		}
	}
	return nil
}

func saveConfig(appName, configPath string) error {
	fout, err := os.Create(configPath)
	if err != nil {
		return fmt.Errorf("Unable to open %s config file %v for writing: %v", appName, configPath, err)
	}
	defer fout.Close()

	writer := bufio.NewWriter(fout)
	defer writer.Flush()

	// header
	fmt.Fprintf(writer, "# %s configuration\n# \n", appName)
	fmt.Fprintln(writer, "# This config has https://github.com/schachmat/ingo syntax.")
	fmt.Fprintln(writer, "# Empty lines or lines starting with # will be ignored.")
	fmt.Fprintln(writer, "# All other lines must look like `KEY=VALUE` (without the quotes).")
	fmt.Fprintln(writer, "# The VALUE must not be enclosed in quotes!")

	// find flags pointing to the same variable. We will only write the longest
	// named flag to the config file, the shorthand version is ignored.
	deduped := make(map[flag.Value]flag.Flag)
	flag.VisitAll(func(f *flag.Flag) {
		if cur, ok := deduped[f.Value]; !ok || utf8.RuneCountInString(f.Name) > utf8.RuneCountInString(cur.Name) {
			deduped[f.Value] = *f
		}
	})
	flag.VisitAll(func(f *flag.Flag) {
		if cur, ok := deduped[f.Value]; ok && cur.Name == f.Name {
			_, usage := flag.UnquoteUsage(f)
			fmt.Fprintf(writer, "\n# %s (default %v)\n", strings.Replace(usage, "\n    \t", "\n# ", -1), f.DefValue)
			fmt.Fprintf(writer, "%v=%v\n", f.Name, f.Value.String())
		}
	})

	// if we have obsolete keys left from the old config, preserve them in an
	// additional section at the end of the file
	if len(obsoleteKeys) == 0 {
		return nil
	}
	fmt.Fprintln(os.Stderr, "!!!!!!!!!!")
	fmt.Fprintln(os.Stderr, "! WARNING: The application was probably updated,")
	fmt.Fprintln(os.Stderr, "! Check and update", configPath, " as necessary and")
	fmt.Fprintln(os.Stderr, "! remove the last \"deprecated\" paragraph to disable this message!")
	fmt.Fprintln(os.Stderr, "!!!!!!!!!!")
	fmt.Fprintln(writer, "\n\n# The following options are probably deprecated and not used currently!")
	for key, val := range obsoleteKeys {
		fmt.Fprintf(writer, "%v=%v\n", key, val)
	}
	return nil
}
<commit_msg>golint<commit_after>package ingo

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"os/user"
	"path"
	"strings"
	"unicode/utf8"
)

var (
	obsoleteKeys = make(map[string]string)
)

// Parse should be called instead of `flag.Parse()` after all flags have been
// added. It will read the config if existing, then write the config with
// default values from the flags for unknown flags and then parse the flags.
func Parse(appName string) error {
	if flag.Parsed() {
		return fmt.Errorf("flags have been parsed already")
	}

	envname := strings.ToUpper(appName) + "RC"
	cPath := os.Getenv(envname)
	if cPath == "" {
		usr, err := user.Current()
		if err != nil {
			return fmt.Errorf("%v\nYou can set the environment variable %s to point to your config file as a workaround", err, envname)
		}
		cPath = path.Join(usr.HomeDir, "."+strings.ToLower(appName)+"rc")
	}

	if err := loadConfig(appName, cPath); err != nil {
		return err
	}
	if err := saveConfig(appName, cPath); err != nil {
		return err
	}
	flag.Parse()
	return nil
}

func loadConfig(appName, configPath string) error {
	fin, err := os.Open(configPath)
	if _, ok := err.(*os.PathError); ok {
		fmt.Fprintf(os.Stderr, "No config file found for %s. Creating %s ...\n", appName, configPath)
		return nil
	} else if err != nil {
		return fmt.Errorf("Unable to read %s config file %v: %v", appName, configPath, err)
	}
	defer fin.Close()

	scanner := bufio.NewScanner(fin)
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if strings.HasPrefix(line, "#") {
			continue
		}

		// find first assignment symbol and parse key, val
		i := strings.IndexAny(line, "=:")
		if i == -1 {
			continue
		}
		key, val := strings.TrimSpace(line[:i]), strings.TrimSpace(line[i+1:])

		if err := flag.Set(key, val); err != nil {
			obsoleteKeys[key] = val
			continue
		}
	}
	return nil
}

func saveConfig(appName, configPath string) error {
	fout, err := os.Create(configPath)
	if err != nil {
		return fmt.Errorf("Unable to open %s config file %v for writing: %v", appName, configPath, err)
	}
	defer fout.Close()

	writer := bufio.NewWriter(fout)
	defer writer.Flush()

	// header
	fmt.Fprintf(writer, "# %s configuration\n# \n", appName)
	fmt.Fprintln(writer, "# This config has https://github.com/schachmat/ingo syntax.")
	fmt.Fprintln(writer, "# Empty lines or lines starting with # will be ignored.")
	fmt.Fprintln(writer, "# All other lines must look like `KEY=VALUE` (without the quotes).")
	fmt.Fprintln(writer, "# The VALUE must not be enclosed in quotes!")

	// find flags pointing to the same variable. We will only write the longest
	// named flag to the config file, the shorthand version is ignored.
	deduped := make(map[flag.Value]flag.Flag)
	flag.VisitAll(func(f *flag.Flag) {
		if cur, ok := deduped[f.Value]; !ok || utf8.RuneCountInString(f.Name) > utf8.RuneCountInString(cur.Name) {
			deduped[f.Value] = *f
		}
	})
	flag.VisitAll(func(f *flag.Flag) {
		if cur, ok := deduped[f.Value]; ok && cur.Name == f.Name {
			_, usage := flag.UnquoteUsage(f)
			fmt.Fprintf(writer, "\n# %s (default %v)\n", strings.Replace(usage, "\n    \t", "\n# ", -1), f.DefValue)
			fmt.Fprintf(writer, "%v=%v\n", f.Name, f.Value.String())
		}
	})

	// if we have obsolete keys left from the old config, preserve them in an
	// additional section at the end of the file
	if len(obsoleteKeys) == 0 {
		return nil
	}
	fmt.Fprintln(os.Stderr, "!!!!!!!!!!")
	fmt.Fprintln(os.Stderr, "! WARNING: The application was probably updated,")
	fmt.Fprintln(os.Stderr, "! Check and update", configPath, " as necessary and")
	fmt.Fprintln(os.Stderr, "! remove the last \"deprecated\" paragraph to disable this message!")
	fmt.Fprintln(os.Stderr, "!!!!!!!!!!")
	fmt.Fprintln(writer, "\n\n# The following options are probably deprecated and not used currently!")
	for key, val := range obsoleteKeys {
		fmt.Fprintf(writer, "%v=%v\n", key, val)
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2013 Jari Takkala. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"crypto/sha1"
	sysio "io"
	"launchpad.net/tomb"
	"log"
	"os"
	"path/filepath"
)

type IO struct {
	metaInfo MetaInfo
	files    []*os.File
	t        tomb.Tomb
}

// checkHash accepts a byte buffer and pieceIndex, computes the SHA-1 hash of
// the buffer and returns true or false if it's correct.
func (io *IO) checkHash(buf []byte, pieceIndex int) bool {
	h := sha1.New()
	h.Write(buf)
	if bytes.Equal(h.Sum(nil), []byte(io.metaInfo.Info.Pieces[pieceIndex:pieceIndex+h.Size()])) {
		return true
	}
	return false
}

// Verify reads in each file and verifies the SHA-1 checksum of each piece.
// Return the boolean list pieces that are correct.
func (io *IO) Verify() (finishedPieces []bool) {
	log.Println("IO : Verify : Started")
	defer log.Println("IO : Verify : Completed")

	buf := make([]byte, io.metaInfo.Info.PieceLength)
	var pieceIndex, n int
	var err error

	if len(io.metaInfo.Info.Files) > 0 {
		// Multiple File Mode
		var m int
		// Iterate over each file
		for i, _ := range io.metaInfo.Info.Files {
			for offset := int64(0); ; offset += int64(n) {
				// Read from file at offset, up to buf size or
				// less if last read was incomplete due to EOF
				n, err = io.files[i].ReadAt(buf[m:], offset)
				if err != nil {
					if err == sysio.EOF {
						// Reached EOF. Increment partial read counter by bytes read
						m += n
						break
					}
					log.Fatal(err)
				}
				// We have a full buf, check the hash of buf and
				// append the result to the finished pieces
				finishedPieces = append(finishedPieces, io.checkHash(buf, pieceIndex))
				// Reset partial read counter
				m = 0
				// Increment piece by the length of a SHA-1 hash (20 bytes)
				pieceIndex += 20
			}
		}
		// If the final iteration resulted in a partial read, then
		// check the hash of it and append the result
		if m > 0 {
			finishedPieces = append(finishedPieces, io.checkHash(buf[:m], pieceIndex))
		}
	} else {
		// Single File Mode
		for offset := int64(0); ; offset += int64(n) {
			// Read from file at offset, up to buf size or
			// less if last read was incomplete due to EOF
			n, err = io.files[0].ReadAt(buf, offset)
			if err != nil {
				if err == sysio.EOF {
					// Reached EOF
					break
				}
				log.Fatal(err)
			}
			// We have a full buf, check the hash of buf and
			// append the result to the finished pieces
			finishedPieces = append(finishedPieces, io.checkHash(buf, pieceIndex))
			// Increment piece by the length of a SHA-1 hash (20 bytes)
			pieceIndex += 20
		}
		// If the final iteration resulted in a partial read, then compute a hash
		if n > 0 {
			finishedPieces = append(finishedPieces, io.checkHash(buf[:n], pieceIndex))
		}
	}

	return finishedPieces
}

func checkError(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

// openOrCreateFile opens the named file or creates it if it doesn't already
// exist. If successful it returns a file handle that can be used for I/O.
func openOrCreateFile(name string) (file *os.File) {
	// Create the file if it doesn't exist
	if _, err := os.Stat(name); os.IsNotExist(err) {
		// Create the file and return a handle
		file, err = os.Create(name)
		checkError(err)
	} else {
		// Open the file and return a handle
		file, err = os.Open(name)
		checkError(err)
	}
	return
}

func (io *IO) Init() {
	if len(io.metaInfo.Info.Files) > 0 {
		// Multiple File Mode
		directory := io.metaInfo.Info.Name
		// Create the directory if it doesn't exist
		if _, err := os.Stat(directory); os.IsNotExist(err) {
			err = os.Mkdir(directory, os.ModeDir|os.ModePerm)
			checkError(err)
		}
		err := os.Chdir(directory)
		checkError(err)
		for _, file := range io.metaInfo.Info.Files {
			// Create any sub-directories if required
			if len(file.Path) > 1 {
				directory = filepath.Join(file.Path[1:]...)
				if _, err := os.Stat(directory); os.IsNotExist(err) {
					err = os.MkdirAll(directory, os.ModeDir|os.ModePerm)
					checkError(err)
				}
			}
			// Create the file if it doesn't exist
			name := filepath.Join(file.Path...)
			io.files = append(io.files, openOrCreateFile(name))
		}
	} else {
		// Single File Mode
		io.files = append(io.files, openOrCreateFile(io.metaInfo.Info.Name))
	}
}

func (io *IO) Stop() error {
	log.Println("IO : Stop : Stopping")
	io.t.Kill(nil)
	return io.t.Wait()
}

func (io *IO) Run() {
	log.Println("IO : Run : Started")
	defer io.t.Done()
	defer log.Println("IO : Run : Completed")

	io.Init()
	finishedPieces := io.Verify()

	for {
		select {
		case <-io.t.Dying():
			return
		}
	}
}
<commit_msg>Add progress indication to file verification<commit_after>// Copyright 2013 Jari Takkala. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"crypto/sha1"
	"fmt"
	sysio "io"
	"launchpad.net/tomb"
	"log"
	"os"
	"path/filepath"
)

type IO struct {
	metaInfo MetaInfo
	files    []*os.File
	t        tomb.Tomb
}

// checkHash accepts a byte buffer and pieceIndex, computes the SHA-1 hash of
// the buffer and returns true or false if it's correct.
func (io *IO) checkHash(buf []byte, pieceIndex int) bool {
	h := sha1.New()
	h.Write(buf)
	if bytes.Equal(h.Sum(nil), []byte(io.metaInfo.Info.Pieces[pieceIndex:pieceIndex+h.Size()])) {
		return true
	}
	return false
}

// Verify reads in each file and verifies the SHA-1 checksum of each piece.
// Return the boolean list pieces that are correct.
func (io *IO) Verify() (finishedPieces []bool) {
	log.Println("IO : Verify : Started")
	defer log.Println("IO : Verify : Completed")

	buf := make([]byte, io.metaInfo.Info.PieceLength)
	var pieceIndex, n int
	var err error

	fmt.Printf("Verifying downloaded files")
	if len(io.metaInfo.Info.Files) > 0 {
		// Multiple File Mode
		var m int
		// Iterate over each file
		for i, _ := range io.metaInfo.Info.Files {
			for offset := int64(0); ; offset += int64(n) {
				// Read from file at offset, up to buf size or
				// less if last read was incomplete due to EOF
				fmt.Printf(".")
				n, err = io.files[i].ReadAt(buf[m:], offset)
				if err != nil {
					if err == sysio.EOF {
						// Reached EOF. Increment partial read counter by bytes read
						m += n
						break
					}
					log.Fatal(err)
				}
				// We have a full buf, check the hash of buf and
				// append the result to the finished pieces
				finishedPieces = append(finishedPieces, io.checkHash(buf, pieceIndex))
				// Reset partial read counter
				m = 0
				// Increment piece by the length of a SHA-1 hash (20 bytes)
				pieceIndex += 20
			}
		}
		// If the final iteration resulted in a partial read, then
		// check the hash of it and append the result
		if m > 0 {
			finishedPieces = append(finishedPieces, io.checkHash(buf[:m], pieceIndex))
		}
	} else {
		// Single File Mode
		for offset := int64(0); ; offset += int64(n) {
			// Read from file at offset, up to buf size or
			// less if last read was incomplete due to EOF
			fmt.Printf(".")
			n, err = io.files[0].ReadAt(buf, offset)
			if err != nil {
				if err == sysio.EOF {
					// Reached EOF
					break
				}
				log.Fatal(err)
			}
			// We have a full buf, check the hash of buf and
			// append the result to the finished pieces
			finishedPieces = append(finishedPieces, io.checkHash(buf, pieceIndex))
			// Increment piece by the length of a SHA-1 hash (20 bytes)
			pieceIndex += 20
		}
		// If the final iteration resulted in a partial read, then compute a hash
		if n > 0 {
			finishedPieces = append(finishedPieces, io.checkHash(buf[:n], pieceIndex))
		}
	}
	fmt.Println()

	return finishedPieces
}

func checkError(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

// openOrCreateFile opens the named file or creates it if it doesn't already
// exist. If successful it returns a file handle that can be used for I/O.
func openOrCreateFile(name string) (file *os.File) {
	// Create the file if it doesn't exist
	if _, err := os.Stat(name); os.IsNotExist(err) {
		// Create the file and return a handle
		file, err = os.Create(name)
		checkError(err)
	} else {
		// Open the file and return a handle
		file, err = os.Open(name)
		checkError(err)
	}
	return
}

func (io *IO) Init() {
	if len(io.metaInfo.Info.Files) > 0 {
		// Multiple File Mode
		directory := io.metaInfo.Info.Name
		// Create the directory if it doesn't exist
		if _, err := os.Stat(directory); os.IsNotExist(err) {
			err = os.Mkdir(directory, os.ModeDir|os.ModePerm)
			checkError(err)
		}
		err := os.Chdir(directory)
		checkError(err)
		for _, file := range io.metaInfo.Info.Files {
			// Create any sub-directories if required
			if len(file.Path) > 1 {
				directory = filepath.Join(file.Path[1:]...)
				if _, err := os.Stat(directory); os.IsNotExist(err) {
					err = os.MkdirAll(directory, os.ModeDir|os.ModePerm)
					checkError(err)
				}
			}
			// Create the file if it doesn't exist
			name := filepath.Join(file.Path...)
			io.files = append(io.files, openOrCreateFile(name))
		}
	} else {
		// Single File Mode
		io.files = append(io.files, openOrCreateFile(io.metaInfo.Info.Name))
	}
}

func (io *IO) Stop() error {
	log.Println("IO : Stop : Stopping")
	io.t.Kill(nil)
	return io.t.Wait()
}

func (io *IO) Run() {
	log.Println("IO : Run : Started")
	defer io.t.Done()
	defer log.Println("IO : Run : Completed")

	io.Init()
	finishedPieces := io.Verify()
	fmt.Println(finishedPieces)

	for {
		select {
		case <-io.t.Dying():
			return
		}
	}
}
<|endoftext|>


<commit_before>package app

import (
	"fmt"
)

const (
	jsFmt = `
function Call(msg) {
    %v
}
    `
)

func MurlokJS() string {
	return fmt.Sprintf(jsFmt, driver.JavascriptBridge())
}
<commit_msg>js comment<commit_after>package app

import (
	"fmt"
)

const (
	jsFmt = `
function Call(msg) {
    %v
}
    `
)

// MurlokJS returns the javascript code allowing bidirectional communication
// between a context and it's webview.
// Should be used only in drivers implementations.
func MurlokJS() string {
	return fmt.Sprintf(jsFmt, driver.JavascriptBridge())
}
<|endoftext|>


<commit_before>// Copyright (C) 2013  gokabinet authors.
// Use of this source code is governed by a GPLv3
// license that can be found in the LICENSE file.

package kt

/*
#cgo pkg-config: kyototycoon
#include <ktlangc.h>
#include "kt.h"
*/
import "C"

import (
	"bytes"
	"fmt"
	"unsafe"
	"encoding/gob"
)

const (
	_ = iota
	DEFAULT_TIMEOUT = -1.
)

// KTError is used for errors using the gokabinet library.  It implements the
// builtin error interface.
type KTError string

func (err KTError) Error() string {
	return string(err)
}

// DB is the basic type for the gokabinet library. Holds an unexported instance
// of the database, for interactions.
type RemoteDB struct {
	db   *C.KTRDB
}

// Open opens a connection to a remote database.
//
func Open(host string, port int, timeout float32) (*RemoteDB, error) {
	d := &RemoteDB{db: C.ktdbnew()}
	cHost := C.CString(host)
	defer C.free(unsafe.Pointer(cHost))
	cPort := C.int32_t(port)
	cTimeout := C.double(timeout)

	if C.ktdbopen(d.db, cHost, cPort, cTimeout) == 0 {
		errMsg := d.LastError()
		return nil, KTError(fmt.Sprintf("Error opening %s:%d -- %s", host, port, errMsg))
	}
	return d, nil
}

// LastError returns a KTError instance representing the last occurred error in
// the database.
func (d *RemoteDB) LastError() error {
	errMsg := C.GoString(C.ktecodename(C.ktdbecode(d.db)))
	return KTError(errMsg)
}

// Close the connection to the db
func (d *RemoteDB) Close() {
	if (d != nil) {
		C.ktdbclose(d.db)
		C.ktdbdel(d.db)
	}
}

func (d *RemoteDB) Set(key, value string) error {

	cKey := C.CString(key)
	defer C.free(unsafe.Pointer(cKey))
	cValue := C.CString(value)
	defer C.free(unsafe.Pointer(cValue))
	lKey := C.size_t(len(key))
	lValue := C.size_t(len(value))
	if C.ktdbset(d.db, cKey, lKey, cValue, lValue) == 0 {
		errMsg := d.LastError()
		return KTError(fmt.Sprintf("Failed to add a record with the value %s and the key %s: %s", value, key, errMsg))
	}
	return nil
}

// Get byte[] directly
func (d *RemoteDB) GetBytes(key string) ([]byte, error) {
	var resultLen C.size_t
	cKey := C.CString(key)
	defer C.free(unsafe.Pointer(cKey))
	lKey := C.size_t(len(key))
	cValue := C.ktdbget(d.db, cKey, lKey, &resultLen)
	defer C.free(unsafe.Pointer(cValue))
	if cValue == nil {
		errMsg := d.LastError()
		return []byte{}, KTError(fmt.Sprintf("Failed to get the record with the key %s: %s", key, errMsg))
	}
	return C.GoBytes(unsafe.Pointer(cValue), C.int(resultLen)), nil
}

func (d *RemoteDB) Get(key string) (string, error) {
	var resultLen C.size_t
	cKey := C.CString(key)
	defer C.free(unsafe.Pointer(cKey))
	lKey := C.size_t(len(key))
	cValue := C.ktdbget(d.db, cKey, lKey, &resultLen)
	defer C.free(unsafe.Pointer(cValue))
	if cValue == nil {
		errMsg := d.LastError()
		return "", KTError(fmt.Sprintf("Failed to get the record with the key %s: %s", key, errMsg))
	}
	return C.GoStringN(cValue, C.int(resultLen)), nil
}

// GetGob gets a record in the database by its key, decoding from gob format.
//
// Returns nil in case of success. In case of errors, it returns a KCError
// instance explaining what happened.
func (d *RemoteDB) GetGob(key string, e interface{}) error {
	data, err := d.Get(key)
	if err != nil {
		return err
	}
	buffer := bytes.NewBufferString(data)
	decoder := gob.NewDecoder(buffer)
	if err := decoder.Decode(e); err != nil {
		return KTError(fmt.Sprintf("Failed to decode the record with the key %s: %s", key, err))
	}
	return nil
}

// SetGob adds a record to the database, stored in gob format.
//
// Returns a KCError instance in case of errors, otherwise, returns nil.
func (d *RemoteDB) SetGob(key string, e interface{}) error {
	var buffer bytes.Buffer
	encoder := gob.NewEncoder(&buffer)
	err := encoder.Encode(e)
	if err != nil {
		return KTError(fmt.Sprintf("Failed to add a record with the value %s and the key %s: %s", e, key, err))
	}
	err = d.Set(key, buffer.String())
	return err
}

// Removes the key from the DB.
func (d *RemoteDB) Remove(key string) error {
	cKey := C.CString(key)
	defer C.free(unsafe.Pointer(cKey))
	lKey := C.size_t(len(key))
	status := C.ktdbremove(d.db, cKey, lKey)
	if status == 0 {
		errMsg := d.LastError()
		return KTError(fmt.Sprintf("Failed to remove the record with the key %s: %s", key, errMsg))
	}
	return nil
}

// Clear removes all records from the database.
//
// Returns a KTError in case of failure.
func (d *RemoteDB) Clear() error {
	if C.ktdbclear(d.db) == 0 {
		msg := d.LastError()
		return KTError(fmt.Sprintf("Failed to clear the database: %s.", msg))
	}
	return nil
}

// Increment increments the value of a numeric record by a given number, and
// return the incremented value.
//
// In case of errors, returns 0 and a KCError instance with detailed error
// message.
func (d *RemoteDB) Increment(key string, number int) (int, error) {
	cKey := C.CString(key)
	defer C.free(unsafe.Pointer(cKey))
	lKey := C.size_t(len(key))
	cValue := C.int64_t(number)
	v := C.ktdbincrint(d.db, cKey, lKey, cValue, 0)
	if v == C.INT64_MIN {
		return 0, KTError("It's not possible to increment a non-numeric record")
	}
	return int(v), nil
}

// Returns the number of elements
func (d *RemoteDB) Count() (int, error) {
	var err error
	v := int(C.ktdbcount(d.db))
	if v == -1 {
		err = d.LastError()
	}
	return v, err
}

// MatchPrefix returns a list of keys that matches a prefix or an error in case
// of failure.
func (d *RemoteDB) MatchPrefix(prefix string, max int64) ([]string, error) {
	cprefix := C.CString(prefix)
	defer C.free(unsafe.Pointer(cprefix))
	strary := C.match_prefix(d.db, cprefix, C.size_t(max))
	if strary.v == nil {
		return nil, d.LastError()
	}
	defer C.free_strary(&strary)
	n := int64(strary.n)
	if n == 0 {
		return nil, nil
	}
	result := make([]string, n)
	for i := int64(0); i < n; i++ {
		result[i] = C.GoString(C.strary_item(&strary, C.int64_t(i)))
	}
	return result, nil
}

// GetBulk fetches all keys in the given map. If a key does not exist, it is deleted from the map before being returned.
// If the key does exist, the value in the map is set accordingly.
func (d *RemoteDB) GetBulk(keysAndVals map[string]string) (error) {

	keyList := make([]string, len(keysAndVals))
	cKeys := C.make_char_array(C.int(len(keysAndVals)))
	defer C.free_char_array(cKeys, C.int(len(keysAndVals)))
	next := 0;
	for s, _ := range (keysAndVals) {
        C.set_array_string(cKeys, C.CString(s), C.int(next))
		keyList[next] = s
		next++
	}

	strary := C.get_bulk_binary(d.db, cKeys, C.size_t(len(keysAndVals)))
	if strary.v == nil {
		return d.LastError()
	}
	defer C.free_strary(&strary)
	n := int64(strary.n)
	if n == 0 {
		return nil
	}

	for i := int64(0); i < n; i++ {
		if C.bool(C.strary_present(&strary, C.int64_t(i))) {
			keysAndVals[keyList[i]] = C.GoString(C.strary_item(&strary, C.int64_t(i)))
		} else {
			delete(keysAndVals, keyList[i])
		}
	}
	return nil
}

// GetBulkBytes fetches all keys in the given map. If a key does not exist, it is deleted from the map before being returned.
// If the key does exist, the value in the map is set accordingly.
func (d *RemoteDB) GetBulkBytes(keysAndVals map[string][]byte) (error) {

	keyList := make([]string, len(keysAndVals))
	cKeys := C.make_char_array(C.int(len(keysAndVals)))
	defer C.free_char_array(cKeys, C.int(len(keysAndVals)))
	next := 0;
	for s, _ := range (keysAndVals) {
        C.set_array_string(cKeys, C.CString(s), C.int(next))
		keyList[next] = s
		next++
	}

	strary := C.get_bulk_binary(d.db, cKeys, C.size_t(len(keysAndVals)))
	if strary.v == nil {
		return d.LastError()
	}
	defer C.free_strary(&strary)
	n := int64(strary.n)
	if n == 0 {
		return nil
	}

	for i := int64(0); i < n; i++ {
		if C.bool(C.strary_present(&strary, C.int64_t(i))) {
			keysAndVals[keyList[i]] = C.GoBytes(unsafe.Pointer(C.strary_item(&strary, C.int64_t(i))), C.int(C.strary_size(&strary, C.int64_t(i))))
		} else {
			delete(keysAndVals, keyList[i])
		}
	}
	return nil
}

// RemoveBulk removes all of the keys passed in at once.
func (d *RemoteDB) RemoveBulk(keys []string) (int64, error) {

	var err error
	cKeys := C.make_char_array(C.int(len(keys)))
	defer C.free_char_array(cKeys, C.int(len(keys)))
	next := 0;
	for _, s := range (keys) {
        C.set_array_string(cKeys, C.CString(s), C.int(next))
		next++
	}

	n := int64(C.ktdbremovebulkbinary(d.db, cKeys, C.size_t(len(keys))))
	if n == -1 {
		err = d.LastError()
	}
	return n, err
}

// SetBulk sets all of the keys passed in at once.
func (d *RemoteDB) SetBulk(keysAndVals map[string]string) (int64, error) {

	var err error
	cKeys := C.make_char_array(C.int(len(keysAndVals)))
	cVals := C.make_char_array(C.int(len(keysAndVals)))
	defer C.free_char_array(cKeys, C.int(len(keysAndVals)))
	defer C.free_char_array(cVals, C.int(len(keysAndVals)))
	next := 0;
	for k, v := range (keysAndVals) {
        C.set_array_string(cKeys, C.CString(k), C.int(next))
        C.set_array_string(cVals, C.CString(v), C.int(next))
		next++
	}

	n := int64(C.ktdbsetbulkbinary(d.db, cKeys, C.size_t(len(keysAndVals)), cVals, C.size_t(len(keysAndVals))))
	if n == -1 {
		err = d.LastError()
	}
	return n, err
}

// Plays the passed in lua script, returning the result as a key->value map.
func (d *RemoteDB) PlayScript(script string, params map[string]string) (map[string]string, error) {

	result := map[string]string{}
	cScript := C.CString(script)
	defer C.free(unsafe.Pointer(cScript))

	paramSize := len(params) * 2
	cParams := C.make_char_array(C.int(paramSize))
	defer C.free_char_array(cParams, C.int(paramSize))
	next := 0;
	for k, v := range (params) {
        C.set_array_string(cParams, C.CString(k), C.int(next))
		next++
        C.set_array_string(cParams, C.CString(v), C.int(next))
		next++
	}

	strary := C.play_script(d.db, cScript, cParams, C.size_t(paramSize))
	if strary.v == nil {
		return nil, d.LastError()
	}
	defer C.free_strary(&strary)
	n := int64(strary.n)
	if n == 0 {
		return result, nil
	}

	for i := int64(0); i < n; i++ {
		result[C.GoString(C.strary_item(&strary, C.int64_t(i)))] = C.GoString(C.strary_item(&strary, C.int64_t(i+1)))
		i++
	}
	return result, nil
}<commit_msg>better error formatting for insertion failure<commit_after>// Copyright (C) 2013  gokabinet authors.
// Use of this source code is governed by a GPLv3
// license that can be found in the LICENSE file.

package kt

/*
#cgo pkg-config: kyototycoon
#include <ktlangc.h>
#include "kt.h"
*/
import "C"

import (
	"bytes"
	"fmt"
	"unsafe"
	"encoding/gob"
)

const (
	_ = iota
	DEFAULT_TIMEOUT = -1.
)

// KTError is used for errors using the gokabinet library.  It implements the
// builtin error interface.
type KTError string

func (err KTError) Error() string {
	return string(err)
}

// DB is the basic type for the gokabinet library. Holds an unexported instance
// of the database, for interactions.
type RemoteDB struct {
	db   *C.KTRDB
}

// Open opens a connection to a remote database.
//
func Open(host string, port int, timeout float32) (*RemoteDB, error) {
	d := &RemoteDB{db: C.ktdbnew()}
	cHost := C.CString(host)
	defer C.free(unsafe.Pointer(cHost))
	cPort := C.int32_t(port)
	cTimeout := C.double(timeout)

	if C.ktdbopen(d.db, cHost, cPort, cTimeout) == 0 {
		errMsg := d.LastError()
		return nil, KTError(fmt.Sprintf("Error opening %s:%d -- %s", host, port, errMsg))
	}
	return d, nil
}

// LastError returns a KTError instance representing the last occurred error in
// the database.
func (d *RemoteDB) LastError() error {
	errMsg := C.GoString(C.ktecodename(C.ktdbecode(d.db)))
	return KTError(errMsg)
}

// Close the connection to the db
func (d *RemoteDB) Close() {
	if (d != nil) {
		C.ktdbclose(d.db)
		C.ktdbdel(d.db)
	}
}

func (d *RemoteDB) Set(key, value string) error {

	cKey := C.CString(key)
	defer C.free(unsafe.Pointer(cKey))
	cValue := C.CString(value)
	defer C.free(unsafe.Pointer(cValue))
	lKey := C.size_t(len(key))
	lValue := C.size_t(len(value))
	if C.ktdbset(d.db, cKey, lKey, cValue, lValue) == 0 {
		errMsg := d.LastError()
		valforprint := value
		truncatedots := ""
		if len(value) > 80 {
			valforprint = value[:80]
			truncatedots = "..."
		}
		return KTError(fmt.Sprintf("Failed to add a record with the value %q%s and the key %q: %s", valforprint, truncatedots, key, errMsg))
	}
	return nil
}

// Get byte[] directly
func (d *RemoteDB) GetBytes(key string) ([]byte, error) {
	var resultLen C.size_t
	cKey := C.CString(key)
	defer C.free(unsafe.Pointer(cKey))
	lKey := C.size_t(len(key))
	cValue := C.ktdbget(d.db, cKey, lKey, &resultLen)
	defer C.free(unsafe.Pointer(cValue))
	if cValue == nil {
		errMsg := d.LastError()
		return []byte{}, KTError(fmt.Sprintf("Failed to get the record with the key %s: %s", key, errMsg))
	}
	return C.GoBytes(unsafe.Pointer(cValue), C.int(resultLen)), nil
}

func (d *RemoteDB) Get(key string) (string, error) {
	var resultLen C.size_t
	cKey := C.CString(key)
	defer C.free(unsafe.Pointer(cKey))
	lKey := C.size_t(len(key))
	cValue := C.ktdbget(d.db, cKey, lKey, &resultLen)
	defer C.free(unsafe.Pointer(cValue))
	if cValue == nil {
		errMsg := d.LastError()
		return "", KTError(fmt.Sprintf("Failed to get the record with the key %s: %s", key, errMsg))
	}
	return C.GoStringN(cValue, C.int(resultLen)), nil
}

// GetGob gets a record in the database by its key, decoding from gob format.
//
// Returns nil in case of success. In case of errors, it returns a KCError
// instance explaining what happened.
func (d *RemoteDB) GetGob(key string, e interface{}) error {
	data, err := d.Get(key)
	if err != nil {
		return err
	}
	buffer := bytes.NewBufferString(data)
	decoder := gob.NewDecoder(buffer)
	if err := decoder.Decode(e); err != nil {
		return KTError(fmt.Sprintf("Failed to decode the record with the key %s: %s", key, err))
	}
	return nil
}

// SetGob adds a record to the database, stored in gob format.
//
// Returns a KCError instance in case of errors, otherwise, returns nil.
func (d *RemoteDB) SetGob(key string, e interface{}) error {
	var buffer bytes.Buffer
	encoder := gob.NewEncoder(&buffer)
	err := encoder.Encode(e)
	if err != nil {
		return KTError(fmt.Sprintf("Failed to add a record with the value %s and the key %s: %s", e, key, err))
	}
	err = d.Set(key, buffer.String())
	return err
}

// Removes the key from the DB.
func (d *RemoteDB) Remove(key string) error {
	cKey := C.CString(key)
	defer C.free(unsafe.Pointer(cKey))
	lKey := C.size_t(len(key))
	status := C.ktdbremove(d.db, cKey, lKey)
	if status == 0 {
		errMsg := d.LastError()
		return KTError(fmt.Sprintf("Failed to remove the record with the key %s: %s", key, errMsg))
	}
	return nil
}

// Clear removes all records from the database.
//
// Returns a KTError in case of failure.
func (d *RemoteDB) Clear() error {
	if C.ktdbclear(d.db) == 0 {
		msg := d.LastError()
		return KTError(fmt.Sprintf("Failed to clear the database: %s.", msg))
	}
	return nil
}

// Increment increments the value of a numeric record by a given number, and
// return the incremented value.
//
// In case of errors, returns 0 and a KCError instance with detailed error
// message.
func (d *RemoteDB) Increment(key string, number int) (int, error) {
	cKey := C.CString(key)
	defer C.free(unsafe.Pointer(cKey))
	lKey := C.size_t(len(key))
	cValue := C.int64_t(number)
	v := C.ktdbincrint(d.db, cKey, lKey, cValue, 0)
	if v == C.INT64_MIN {
		return 0, KTError("It's not possible to increment a non-numeric record")
	}
	return int(v), nil
}

// Returns the number of elements
func (d *RemoteDB) Count() (int, error) {
	var err error
	v := int(C.ktdbcount(d.db))
	if v == -1 {
		err = d.LastError()
	}
	return v, err
}

// MatchPrefix returns a list of keys that matches a prefix or an error in case
// of failure.
func (d *RemoteDB) MatchPrefix(prefix string, max int64) ([]string, error) {
	cprefix := C.CString(prefix)
	defer C.free(unsafe.Pointer(cprefix))
	strary := C.match_prefix(d.db, cprefix, C.size_t(max))
	if strary.v == nil {
		return nil, d.LastError()
	}
	defer C.free_strary(&strary)
	n := int64(strary.n)
	if n == 0 {
		return nil, nil
	}
	result := make([]string, n)
	for i := int64(0); i < n; i++ {
		result[i] = C.GoString(C.strary_item(&strary, C.int64_t(i)))
	}
	return result, nil
}

// GetBulk fetches all keys in the given map. If a key does not exist, it is deleted from the map before being returned.
// If the key does exist, the value in the map is set accordingly.
func (d *RemoteDB) GetBulk(keysAndVals map[string]string) (error) {

	keyList := make([]string, len(keysAndVals))
	cKeys := C.make_char_array(C.int(len(keysAndVals)))
	defer C.free_char_array(cKeys, C.int(len(keysAndVals)))
	next := 0;
	for s, _ := range (keysAndVals) {
        C.set_array_string(cKeys, C.CString(s), C.int(next))
		keyList[next] = s
		next++
	}

	strary := C.get_bulk_binary(d.db, cKeys, C.size_t(len(keysAndVals)))
	if strary.v == nil {
		return d.LastError()
	}
	defer C.free_strary(&strary)
	n := int64(strary.n)
	if n == 0 {
		return nil
	}

	for i := int64(0); i < n; i++ {
		if C.bool(C.strary_present(&strary, C.int64_t(i))) {
			keysAndVals[keyList[i]] = C.GoString(C.strary_item(&strary, C.int64_t(i)))
		} else {
			delete(keysAndVals, keyList[i])
		}
	}
	return nil
}

// GetBulkBytes fetches all keys in the given map. If a key does not exist, it is deleted from the map before being returned.
// If the key does exist, the value in the map is set accordingly.
func (d *RemoteDB) GetBulkBytes(keysAndVals map[string][]byte) (error) {

	keyList := make([]string, len(keysAndVals))
	cKeys := C.make_char_array(C.int(len(keysAndVals)))
	defer C.free_char_array(cKeys, C.int(len(keysAndVals)))
	next := 0;
	for s, _ := range (keysAndVals) {
        C.set_array_string(cKeys, C.CString(s), C.int(next))
		keyList[next] = s
		next++
	}

	strary := C.get_bulk_binary(d.db, cKeys, C.size_t(len(keysAndVals)))
	if strary.v == nil {
		return d.LastError()
	}
	defer C.free_strary(&strary)
	n := int64(strary.n)
	if n == 0 {
		return nil
	}

	for i := int64(0); i < n; i++ {
		if C.bool(C.strary_present(&strary, C.int64_t(i))) {
			keysAndVals[keyList[i]] = C.GoBytes(unsafe.Pointer(C.strary_item(&strary, C.int64_t(i))), C.int(C.strary_size(&strary, C.int64_t(i))))
		} else {
			delete(keysAndVals, keyList[i])
		}
	}
	return nil
}

// RemoveBulk removes all of the keys passed in at once.
func (d *RemoteDB) RemoveBulk(keys []string) (int64, error) {

	var err error
	cKeys := C.make_char_array(C.int(len(keys)))
	defer C.free_char_array(cKeys, C.int(len(keys)))
	next := 0;
	for _, s := range (keys) {
        C.set_array_string(cKeys, C.CString(s), C.int(next))
		next++
	}

	n := int64(C.ktdbremovebulkbinary(d.db, cKeys, C.size_t(len(keys))))
	if n == -1 {
		err = d.LastError()
	}
	return n, err
}

// SetBulk sets all of the keys passed in at once.
func (d *RemoteDB) SetBulk(keysAndVals map[string]string) (int64, error) {

	var err error
	cKeys := C.make_char_array(C.int(len(keysAndVals)))
	cVals := C.make_char_array(C.int(len(keysAndVals)))
	defer C.free_char_array(cKeys, C.int(len(keysAndVals)))
	defer C.free_char_array(cVals, C.int(len(keysAndVals)))
	next := 0;
	for k, v := range (keysAndVals) {
        C.set_array_string(cKeys, C.CString(k), C.int(next))
        C.set_array_string(cVals, C.CString(v), C.int(next))
		next++
	}

	n := int64(C.ktdbsetbulkbinary(d.db, cKeys, C.size_t(len(keysAndVals)), cVals, C.size_t(len(keysAndVals))))
	if n == -1 {
		err = d.LastError()
	}
	return n, err
}

// Plays the passed in lua script, returning the result as a key->value map.
func (d *RemoteDB) PlayScript(script string, params map[string]string) (map[string]string, error) {

	result := map[string]string{}
	cScript := C.CString(script)
	defer C.free(unsafe.Pointer(cScript))

	paramSize := len(params) * 2
	cParams := C.make_char_array(C.int(paramSize))
	defer C.free_char_array(cParams, C.int(paramSize))
	next := 0;
	for k, v := range (params) {
        C.set_array_string(cParams, C.CString(k), C.int(next))
		next++
        C.set_array_string(cParams, C.CString(v), C.int(next))
		next++
	}

	strary := C.play_script(d.db, cScript, cParams, C.size_t(paramSize))
	if strary.v == nil {
		return nil, d.LastError()
	}
	defer C.free_strary(&strary)
	n := int64(strary.n)
	if n == 0 {
		return result, nil
	}

	for i := int64(0); i < n; i++ {
		result[C.GoString(C.strary_item(&strary, C.int64_t(i)))] = C.GoString(C.strary_item(&strary, C.int64_t(i+1)))
		i++
	}
	return result, nil
}<|endoftext|>


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

package api

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"reflect"
	"testing"
)

var (
	testFormula1 = "=A-B"
	testFormula2 = "=VAL/1000"
	testGraph    = Graph{
		CID:        "/graph/01234567-89ab-cdef-0123-456789abcdef",
		AccessKeys: []GraphAccessKey{},
		Composites: []GraphComposite{
			GraphComposite{
				Axis:        "l",
				Color:       "#000000",
				DataFormula: &testFormula1,
				Hidden:      false,
				Name:        "Time After First Byte",
			},
		},
		Datapoints: []GraphDatapoint{
			GraphDatapoint{
				Axis:        "l",
				CheckID:     1234,
				Color:       "#ff0000",
				DataFormula: &testFormula2,
				Derive:      "gauge",
				Hidden:      false,
				MetricName:  "duration",
				MetricType:  "numeric",
				Name:        "Total Request Time",
			},
			GraphDatapoint{
				Axis:        "l",
				CheckID:     2345,
				Color:       "#00ff00",
				DataFormula: &testFormula2,
				Derive:      "gauge",
				Hidden:      false,
				MetricName:  "tt_firstbyte",
				MetricType:  "numeric",
				Name:        "Time Till First Byte",
			},
		},
		Description: "Time to first byte verses time to whole thing",
		LineStyle:   "interpolated",
		LogLeftY:    10,
		Notes:       "This graph shows just the main webserver",
		Style:       "line",
		Tags:        []string{"datacenter:primary"},
		Title:       "Slow Webserver",
	}
)

func testGraphServer() *httptest.Server {
	f := func(w http.ResponseWriter, r *http.Request) {
		path := r.URL.Path
		if path == "/graph/01234567-89ab-cdef-0123-456789abcdef" {
			switch r.Method {
			case "GET":
				ret, err := json.Marshal(testGraph)
				if err != nil {
					panic(err)
				}
				w.WriteHeader(200)
				w.Header().Set("Content-Type", "application/json")
				fmt.Fprintln(w, string(ret))
			case "PUT":
				defer r.Body.Close()
				b, err := ioutil.ReadAll(r.Body)
				if err != nil {
					panic(err)
				}
				w.WriteHeader(200)
				w.Header().Set("Content-Type", "application/json")
				fmt.Fprintln(w, string(b))
			case "DELETE":
				w.WriteHeader(200)
				w.Header().Set("Content-Type", "application/json")
			default:
				w.WriteHeader(404)
				fmt.Fprintln(w, fmt.Sprintf("not found: %s %s", r.Method, path))
			}
		} else if path == "/graph" {
			switch r.Method {
			case "GET":
				c := []Graph{testGraph}
				ret, err := json.Marshal(c)
				if err != nil {
					panic(err)
				}
				w.WriteHeader(200)
				w.Header().Set("Content-Type", "application/json")
				fmt.Fprintln(w, string(ret))
			case "POST":
				defer r.Body.Close()
				_, err := ioutil.ReadAll(r.Body)
				if err != nil {
					panic(err)
				}
				ret, err := json.Marshal(testGraph)
				if err != nil {
					panic(err)
				}
				w.WriteHeader(200)
				w.Header().Set("Content-Type", "application/json")
				fmt.Fprintln(w, string(ret))
			default:
				w.WriteHeader(404)
				fmt.Fprintln(w, fmt.Sprintf("not found: %s %s", r.Method, path))
			}
		} else {
			w.WriteHeader(404)
			fmt.Fprintln(w, fmt.Sprintf("not found: %s %s", r.Method, path))
		}
	}

	return httptest.NewServer(http.HandlerFunc(f))
}

func TestFetchGraph(t *testing.T) {
	server := testGraphServer()
	defer server.Close()

	ac := &Config{
		TokenKey: "abc123",
		TokenApp: "test",
		URL:      server.URL,
	}
	apih, err := NewAPI(ac)
	if err != nil {
		t.Errorf("Expected no error, got '%v'", err)
	}

	t.Log("without CID")
	{
		expectedError := errors.New("Invalid graph CID [none]")
		_, err := apih.FetchGraph(nil)
		if err == nil {
			t.Fatalf("Expected error")
		}
		if err.Error() != expectedError.Error() {
			t.Fatalf("Expected %+v got '%+v'", expectedError, err)
		}
	}

	t.Log("with valid CID")
	{
		cid := "/graph/01234567-89ab-cdef-0123-456789abcdef"
		graph, err := apih.FetchGraph(CIDType(&cid))
		if err != nil {
			t.Fatalf("Expected no error, got '%v'", err)
		}

		actualType := reflect.TypeOf(graph)
		expectedType := "*api.Graph"
		if actualType.String() != expectedType {
			t.Fatalf("Expected %s, got %s", expectedType, actualType.String())
		}

		if graph.CID != testGraph.CID {
			t.Fatalf("CIDs do not match: %+v != %+v\n", graph, testGraph)
		}
	}

	t.Log("with invalid CID")
	{
		cid := "/invalid"
		expectedError := errors.New("Invalid graph CID [/invalid]")
		_, err := apih.FetchGraph(CIDType(&cid))
		if err == nil {
			t.Fatalf("Expected error")
		}
		if err.Error() != expectedError.Error() {
			t.Fatalf("Expected %+v got '%+v'", expectedError, err)
		}
	}
}

func TestFetchGraphs(t *testing.T) {
	server := testGraphServer()
	defer server.Close()

	ac := &Config{
		TokenKey: "abc123",
		TokenApp: "test",
		URL:      server.URL,
	}
	apih, err := NewAPI(ac)
	if err != nil {
		t.Errorf("Expected no error, got '%v'", err)
	}

	graphs, err := apih.FetchGraphs()
	if err != nil {
		t.Fatalf("Expected no error, got '%v'", err)
	}

	actualType := reflect.TypeOf(graphs)
	expectedType := "*[]api.Graph"
	if actualType.String() != expectedType {
		t.Fatalf("Expected %s, got %s", expectedType, actualType.String())
	}

}

func TestCreateGraph(t *testing.T) {
	server := testGraphServer()
	defer server.Close()

	var apih *API

	ac := &Config{
		TokenKey: "abc123",
		TokenApp: "test",
		URL:      server.URL,
	}
	apih, err := NewAPI(ac)
	if err != nil {
		t.Errorf("Expected no error, got '%v'", err)
	}

	graph, err := apih.CreateGraph(&testGraph)
	if err != nil {
		t.Fatalf("Expected no error, got '%v'", err)
	}

	actualType := reflect.TypeOf(graph)
	expectedType := "*api.Graph"
	if actualType.String() != expectedType {
		t.Fatalf("Expected %s, got %s", expectedType, actualType.String())
	}
}

func TestUpdateGraph(t *testing.T) {
	server := testGraphServer()
	defer server.Close()

	var apih *API

	ac := &Config{
		TokenKey: "abc123",
		TokenApp: "test",
		URL:      server.URL,
	}
	apih, err := NewAPI(ac)
	if err != nil {
		t.Errorf("Expected no error, got '%v'", err)
	}

	t.Log("valid Graph")
	{
		graph, err := apih.UpdateGraph(&testGraph)
		if err != nil {
			t.Fatalf("Expected no error, got '%v'", err)
		}

		actualType := reflect.TypeOf(graph)
		expectedType := "*api.Graph"
		if actualType.String() != expectedType {
			t.Fatalf("Expected %s, got %s", expectedType, actualType.String())
		}
	}

	t.Log("Test with invalid CID")
	{
		expectedError := errors.New("Invalid graph CID [/invalid]")
		x := &Graph{CID: "/invalid"}
		_, err := apih.UpdateGraph(x)
		if err == nil {
			t.Fatal("Expected an error")
		}
		if err.Error() != expectedError.Error() {
			t.Fatalf("Expected %+v got '%+v'", expectedError, err)
		}
	}
}

func TestDeleteGraph(t *testing.T) {
	server := testGraphServer()
	defer server.Close()

	var apih *API

	ac := &Config{
		TokenKey: "abc123",
		TokenApp: "test",
		URL:      server.URL,
	}
	apih, err := NewAPI(ac)
	if err != nil {
		t.Errorf("Expected no error, got '%v'", err)
	}

	t.Log("valid Graph")
	{
		_, err := apih.DeleteGraph(&testGraph)
		if err != nil {
			t.Fatalf("Expected no error, got '%v'", err)
		}
	}

	t.Log("Test with invalid CID")
	{
		expectedError := errors.New("Invalid graph CID [/invalid]")
		x := &Graph{CID: "/invalid"}
		_, err := apih.UpdateGraph(x)
		if err == nil {
			t.Fatal("Expected an error")
		}
		if err.Error() != expectedError.Error() {
			t.Fatalf("Expected %+v got '%+v'", expectedError, err)
		}
	}
}
<commit_msg>add: search test coverage<commit_after>// Copyright 2016 Circonus, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package api

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"reflect"
	"testing"
)

var (
	testFormula1 = "=A-B"
	testFormula2 = "=VAL/1000"
	testGraph    = Graph{
		CID:        "/graph/01234567-89ab-cdef-0123-456789abcdef",
		AccessKeys: []GraphAccessKey{},
		Composites: []GraphComposite{
			GraphComposite{
				Axis:        "l",
				Color:       "#000000",
				DataFormula: &testFormula1,
				Hidden:      false,
				Name:        "Time After First Byte",
			},
		},
		Datapoints: []GraphDatapoint{
			GraphDatapoint{
				Axis:        "l",
				CheckID:     1234,
				Color:       "#ff0000",
				DataFormula: &testFormula2,
				Derive:      "gauge",
				Hidden:      false,
				MetricName:  "duration",
				MetricType:  "numeric",
				Name:        "Total Request Time",
			},
			GraphDatapoint{
				Axis:        "l",
				CheckID:     2345,
				Color:       "#00ff00",
				DataFormula: &testFormula2,
				Derive:      "gauge",
				Hidden:      false,
				MetricName:  "tt_firstbyte",
				MetricType:  "numeric",
				Name:        "Time Till First Byte",
			},
		},
		Description: "Time to first byte verses time to whole thing",
		LineStyle:   "interpolated",
		LogLeftY:    10,
		Notes:       "This graph shows just the main webserver",
		Style:       "line",
		Tags:        []string{"datacenter:primary"},
		Title:       "Slow Webserver",
	}
)

func testGraphServer() *httptest.Server {
	f := func(w http.ResponseWriter, r *http.Request) {
		path := r.URL.Path
		if path == "/graph/01234567-89ab-cdef-0123-456789abcdef" {
			switch r.Method {
			case "GET":
				ret, err := json.Marshal(testGraph)
				if err != nil {
					panic(err)
				}
				w.WriteHeader(200)
				w.Header().Set("Content-Type", "application/json")
				fmt.Fprintln(w, string(ret))
			case "PUT":
				defer r.Body.Close()
				b, err := ioutil.ReadAll(r.Body)
				if err != nil {
					panic(err)
				}
				w.WriteHeader(200)
				w.Header().Set("Content-Type", "application/json")
				fmt.Fprintln(w, string(b))
			case "DELETE":
				w.WriteHeader(200)
				w.Header().Set("Content-Type", "application/json")
			default:
				w.WriteHeader(404)
				fmt.Fprintln(w, fmt.Sprintf("not found: %s %s", r.Method, path))
			}
		} else if path == "/graph" {
			switch r.Method {
			case "GET":
				reqURL := r.URL.String()
				var c []Graph
				if reqURL == "/graph?search=CPU+Utilization" {
					c = []Graph{testGraph}
				} else if reqURL == "/graph?f__tags_has=os%3Arhel7" {
					c = []Graph{testGraph}
				} else if reqURL == "/graph?f__tags_has=os%3Arhel7&search=CPU+Utilization" {
					c = []Graph{testGraph}
				} else if reqURL == "/graph" {
					c = []Graph{testGraph}
				} else {
					c = []Graph{}
				}
				if len(c) > 0 {
					ret, err := json.Marshal(c)
					if err != nil {
						panic(err)
					}
					w.WriteHeader(200)
					w.Header().Set("Content-Type", "application/json")
					fmt.Fprintln(w, string(ret))
				} else {
					w.WriteHeader(404)
					fmt.Fprintln(w, fmt.Sprintf("not found: %s %s", r.Method, reqURL))
				}
			case "POST":
				defer r.Body.Close()
				_, err := ioutil.ReadAll(r.Body)
				if err != nil {
					panic(err)
				}
				ret, err := json.Marshal(testGraph)
				if err != nil {
					panic(err)
				}
				w.WriteHeader(200)
				w.Header().Set("Content-Type", "application/json")
				fmt.Fprintln(w, string(ret))
			default:
				w.WriteHeader(404)
				fmt.Fprintln(w, fmt.Sprintf("not found: %s %s", r.Method, path))
			}
		} else {
			w.WriteHeader(404)
			fmt.Fprintln(w, fmt.Sprintf("not found: %s %s", r.Method, path))
		}
	}

	return httptest.NewServer(http.HandlerFunc(f))
}

func TestFetchGraph(t *testing.T) {
	server := testGraphServer()
	defer server.Close()

	ac := &Config{
		TokenKey: "abc123",
		TokenApp: "test",
		URL:      server.URL,
	}
	apih, err := NewAPI(ac)
	if err != nil {
		t.Errorf("Expected no error, got '%v'", err)
	}

	t.Log("without CID")
	{
		expectedError := errors.New("Invalid graph CID [none]")
		_, err := apih.FetchGraph(nil)
		if err == nil {
			t.Fatalf("Expected error")
		}
		if err.Error() != expectedError.Error() {
			t.Fatalf("Expected %+v got '%+v'", expectedError, err)
		}
	}

	t.Log("with valid CID")
	{
		cid := "/graph/01234567-89ab-cdef-0123-456789abcdef"
		graph, err := apih.FetchGraph(CIDType(&cid))
		if err != nil {
			t.Fatalf("Expected no error, got '%v'", err)
		}

		actualType := reflect.TypeOf(graph)
		expectedType := "*api.Graph"
		if actualType.String() != expectedType {
			t.Fatalf("Expected %s, got %s", expectedType, actualType.String())
		}

		if graph.CID != testGraph.CID {
			t.Fatalf("CIDs do not match: %+v != %+v\n", graph, testGraph)
		}
	}

	t.Log("with invalid CID")
	{
		cid := "/invalid"
		expectedError := errors.New("Invalid graph CID [/invalid]")
		_, err := apih.FetchGraph(CIDType(&cid))
		if err == nil {
			t.Fatalf("Expected error")
		}
		if err.Error() != expectedError.Error() {
			t.Fatalf("Expected %+v got '%+v'", expectedError, err)
		}
	}
}

func TestFetchGraphs(t *testing.T) {
	server := testGraphServer()
	defer server.Close()

	ac := &Config{
		TokenKey: "abc123",
		TokenApp: "test",
		URL:      server.URL,
	}
	apih, err := NewAPI(ac)
	if err != nil {
		t.Errorf("Expected no error, got '%v'", err)
	}

	graphs, err := apih.FetchGraphs()
	if err != nil {
		t.Fatalf("Expected no error, got '%v'", err)
	}

	actualType := reflect.TypeOf(graphs)
	expectedType := "*[]api.Graph"
	if actualType.String() != expectedType {
		t.Fatalf("Expected %s, got %s", expectedType, actualType.String())
	}

}

func TestCreateGraph(t *testing.T) {
	server := testGraphServer()
	defer server.Close()

	var apih *API

	ac := &Config{
		TokenKey: "abc123",
		TokenApp: "test",
		URL:      server.URL,
	}
	apih, err := NewAPI(ac)
	if err != nil {
		t.Errorf("Expected no error, got '%v'", err)
	}

	graph, err := apih.CreateGraph(&testGraph)
	if err != nil {
		t.Fatalf("Expected no error, got '%v'", err)
	}

	actualType := reflect.TypeOf(graph)
	expectedType := "*api.Graph"
	if actualType.String() != expectedType {
		t.Fatalf("Expected %s, got %s", expectedType, actualType.String())
	}
}

func TestUpdateGraph(t *testing.T) {
	server := testGraphServer()
	defer server.Close()

	var apih *API

	ac := &Config{
		TokenKey: "abc123",
		TokenApp: "test",
		URL:      server.URL,
	}
	apih, err := NewAPI(ac)
	if err != nil {
		t.Errorf("Expected no error, got '%v'", err)
	}

	t.Log("valid Graph")
	{
		graph, err := apih.UpdateGraph(&testGraph)
		if err != nil {
			t.Fatalf("Expected no error, got '%v'", err)
		}

		actualType := reflect.TypeOf(graph)
		expectedType := "*api.Graph"
		if actualType.String() != expectedType {
			t.Fatalf("Expected %s, got %s", expectedType, actualType.String())
		}
	}

	t.Log("Test with invalid CID")
	{
		expectedError := errors.New("Invalid graph CID [/invalid]")
		x := &Graph{CID: "/invalid"}
		_, err := apih.UpdateGraph(x)
		if err == nil {
			t.Fatal("Expected an error")
		}
		if err.Error() != expectedError.Error() {
			t.Fatalf("Expected %+v got '%+v'", expectedError, err)
		}
	}
}

func TestDeleteGraph(t *testing.T) {
	server := testGraphServer()
	defer server.Close()

	var apih *API

	ac := &Config{
		TokenKey: "abc123",
		TokenApp: "test",
		URL:      server.URL,
	}
	apih, err := NewAPI(ac)
	if err != nil {
		t.Errorf("Expected no error, got '%v'", err)
	}

	t.Log("valid Graph")
	{
		_, err := apih.DeleteGraph(&testGraph)
		if err != nil {
			t.Fatalf("Expected no error, got '%v'", err)
		}
	}

	t.Log("Test with invalid CID")
	{
		expectedError := errors.New("Invalid graph CID [/invalid]")
		x := &Graph{CID: "/invalid"}
		_, err := apih.UpdateGraph(x)
		if err == nil {
			t.Fatal("Expected an error")
		}
		if err.Error() != expectedError.Error() {
			t.Fatalf("Expected %+v got '%+v'", expectedError, err)
		}
	}
}

func TestSearchGraphs(t *testing.T) {
	server := testGraphServer()
	defer server.Close()

	var apih *API

	ac := &Config{
		TokenKey: "abc123",
		TokenApp: "test",
		URL:      server.URL,
	}
	apih, err := NewAPI(ac)
	if err != nil {
		t.Errorf("Expected no error, got '%v'", err)
	}

	search := SearchQueryType("CPU Utilization")
	filter := SearchFilterType(map[string][]string{"f__tags_has": []string{"os:rhel7"}})

	t.Log("no search, no filter")
	{
		graphs, err := apih.SearchGraphs(nil, nil)
		if err != nil {
			t.Fatalf("Expected no error, got '%v'", err)
		}

		actualType := reflect.TypeOf(graphs)
		expectedType := "*[]api.Graph"
		if actualType.String() != expectedType {
			t.Fatalf("Expected %s, got %s", expectedType, actualType.String())
		}
	}

	t.Log("search, no filter")
	{
		graphs, err := apih.SearchGraphs(&search, nil)
		if err != nil {
			t.Fatalf("Expected no error, got '%v'", err)
		}

		actualType := reflect.TypeOf(graphs)
		expectedType := "*[]api.Graph"
		if actualType.String() != expectedType {
			t.Fatalf("Expected %s, got %s", expectedType, actualType.String())
		}
	}

	t.Log("no search, filter")
	{
		graphs, err := apih.SearchGraphs(nil, &filter)
		if err != nil {
			t.Fatalf("Expected no error, got '%v'", err)
		}

		actualType := reflect.TypeOf(graphs)
		expectedType := "*[]api.Graph"
		if actualType.String() != expectedType {
			t.Fatalf("Expected %s, got %s", expectedType, actualType.String())
		}
	}

	t.Log("search, filter")
	{
		graphs, err := apih.SearchGraphs(&search, &filter)
		if err != nil {
			t.Fatalf("Expected no error, got '%v'", err)
		}

		actualType := reflect.TypeOf(graphs)
		expectedType := "*[]api.Graph"
		if actualType.String() != expectedType {
			t.Fatalf("Expected %s, got %s", expectedType, actualType.String())
		}
	}
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"
	"net/http"
	"strings"

	"github.com/FederationOfFathers/dashboard/bot"
	"github.com/FederationOfFathers/dashboard/db"
	"github.com/gorilla/mux"
	"github.com/jinzhu/gorm"
	"go.uber.org/zap"
)

func init() {
	Router.Path("/api/v1/meta/member/{memberID}/{key}").Methods("DELETE").Handler(authenticated(
		func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set("Content-Type", "application/json")
			id := getMemberID(r)
			authMember, err := DB.MemberByAny(id)
			if err != nil {
				Logger.Error("invalid member", zap.String("id", id), zap.Error(err))
				w.WriteHeader(http.StatusForbidden)
				return
			}
			admin, _ := bot.IsUserIDAdmin(authMember.Discord)
			member, err := DB.MemberByAny(mux.Vars(r)["memberID"])
			if err != nil {
				http.NotFound(w, r)
				return
			}
			if strings.ToLower(member.Slack) != strings.ToLower(id) && !admin {
				http.NotFound(w, r)
				return
			}
			DB.Delete(db.MemberMeta{}, "member_ID = ? AND meta_key = ?", member.ID, mux.Vars(r)["key"])
		},
	))

	Router.Path("/api/v1/meta/member/{memberID}").Methods("GET").Handler(authenticated(
		func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set("Content-Type", "application/json")
			member, err := DB.MemberByAny(mux.Vars(r)["memberID"])
			if err == gorm.ErrRecordNotFound || member == nil {
				http.NotFound(w, r)
				return
			}
			if err != nil {
				Logger.Error("querying user", zap.Error(err))
				w.WriteHeader(http.StatusInternalServerError)
				return
			}
			var out = map[string]string{}
			rows, err := DB.Raw("SELECT meta_key,meta_value FROM membermeta WHERE member_id = ?", member.ID).Rows()
			defer rows.Close()
			if err != nil {
				Logger.Error("querying", zap.Error(err))
				w.WriteHeader(http.StatusInternalServerError)
				return
			}
			for rows.Next() {
				var k string
				var v string
				if err := rows.Scan(&k, &v); err != nil {
					Logger.Error("scanning", zap.Error(err))
					continue
				}
				out[k] = v
			}
			json.NewEncoder(w).Encode(out)
		},
	))

	Router.Path("/api/v1/meta/member/{memberID}").Methods("PUT", "POST").Handler(
		authenticated(
			func(w http.ResponseWriter, r *http.Request) {
				w.Header().Set("Content-Type", "application/json")
				defer r.Body.Close()
				member, err := DB.MemberByAny(mux.Vars(r)["memberID"])
				if err != nil {
					http.NotFound(w, r)
					return
				}
				if !strings.Contains(strings.ToLower(r.Header.Get("Content-Type")), "json") {
					http.NotFound(w, r)
					return
				}
				var form = map[string]string{}

				err = json.NewDecoder(r.Body).Decode(&form)
				if err != nil {
					Logger.Error("Error decoding JSON", zap.String("uri", r.URL.RawPath), zap.Error(err))
				}

				id := getMemberID(r)
				m, err := DB.MemberByAny(id)
				if err != nil {
					w.WriteHeader(http.StatusForbidden)
					return
				}
				admin, _ := bot.IsUserIDAdmin(m.Discord)
				if id != member.Discord && !admin {
					http.NotFound(w, r)
					return
				}

				for k, v := range form {
					err := DB.Exec(strings.Join([]string{
						"INSERT INTO membermeta (`member_id`,`meta_key`,`meta_value`)",
						"VALUES(?,?,?)",
						"ON DUPLICATE KEY UPDATE",
						"`meta_value` = ?",
					}, " "),
						member.ID,
						k,
						v,
						v,
					).Error
					if err != nil {
						w.WriteHeader(http.StatusInternalServerError)
						return
					}
				}
			},
		),
	)
}
<commit_msg>fixing member meta id check<commit_after>package api

import (
	"encoding/json"
	"net/http"
	"strings"

	"github.com/FederationOfFathers/dashboard/bot"
	"github.com/FederationOfFathers/dashboard/db"
	"github.com/gorilla/mux"
	"github.com/jinzhu/gorm"
	"go.uber.org/zap"
)

func init() {
	Router.Path("/api/v1/meta/member/{memberID}/{key}").Methods("DELETE").Handler(authenticated(
		func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set("Content-Type", "application/json")
			id := getMemberID(r)
			authMember, err := DB.MemberByAny(id)
			if err != nil {
				Logger.Error("invalid member", zap.String("id", id), zap.Error(err))
				w.WriteHeader(http.StatusForbidden)
				return
			}
			admin, _ := bot.IsUserIDAdmin(authMember.Discord)
			member, err := DB.MemberByAny(mux.Vars(r)["memberID"])
			if err != nil {
				http.NotFound(w, r)
				return
			}
			if strings.ToLower(member.Slack) != strings.ToLower(id) && !admin {
				http.NotFound(w, r)
				return
			}
			DB.Delete(db.MemberMeta{}, "member_ID = ? AND meta_key = ?", member.ID, mux.Vars(r)["key"])
		},
	))

	Router.Path("/api/v1/meta/member/{memberID}").Methods("GET").Handler(authenticated(
		func(w http.ResponseWriter, r *http.Request) {
			w.Header().Set("Content-Type", "application/json")
			member, err := DB.MemberByAny(mux.Vars(r)["memberID"])
			if err == gorm.ErrRecordNotFound || member == nil {
				http.NotFound(w, r)
				return
			}
			if err != nil {
				Logger.Error("querying user", zap.Error(err))
				w.WriteHeader(http.StatusInternalServerError)
				return
			}
			var out = map[string]string{}
			rows, err := DB.Raw("SELECT meta_key,meta_value FROM membermeta WHERE member_id = ?", member.ID).Rows()
			defer rows.Close()
			if err != nil {
				Logger.Error("querying", zap.Error(err))
				w.WriteHeader(http.StatusInternalServerError)
				return
			}
			for rows.Next() {
				var k string
				var v string
				if err := rows.Scan(&k, &v); err != nil {
					Logger.Error("scanning", zap.Error(err))
					continue
				}
				out[k] = v
			}
			json.NewEncoder(w).Encode(out)
		},
	))

	Router.Path("/api/v1/meta/member/{memberID}").Methods("PUT", "POST").Handler(
		authenticated(
			func(w http.ResponseWriter, r *http.Request) {
				w.Header().Set("Content-Type", "application/json")
				defer r.Body.Close()
				member, err := DB.MemberByAny(mux.Vars(r)["memberID"])
				if err != nil {
					http.NotFound(w, r)
					return
				}
				if !strings.Contains(strings.ToLower(r.Header.Get("Content-Type")), "json") {
					http.NotFound(w, r)
					return
				}
				var form = map[string]string{}

				err = json.NewDecoder(r.Body).Decode(&form)
				if err != nil {
					Logger.Error("Error decoding JSON", zap.String("uri", r.URL.RawPath), zap.Error(err))
				}

				id := getMemberID(r)
				m, err := DB.MemberByAny(id)
				if err != nil {
					w.WriteHeader(http.StatusForbidden)
					return
				}
				admin, _ := bot.IsUserIDAdmin(m.Discord)
				if m.ID != member.ID && !admin {
					http.NotFound(w, r)
					return
				}

				for k, v := range form {
					err := DB.Exec(strings.Join([]string{
						"INSERT INTO membermeta (`member_id`,`meta_key`,`meta_value`)",
						"VALUES(?,?,?)",
						"ON DUPLICATE KEY UPDATE",
						"`meta_value` = ?",
					}, " "),
						member.ID,
						k,
						v,
						v,
					).Error
					if err != nil {
						w.WriteHeader(http.StatusInternalServerError)
						return
					}
				}
			},
		),
	)
}
<|endoftext|>


<commit_before>// vim: tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab tw=72
//http://stackoverflow.com/questions/8757389/reading-file-line-by-line-in-go
package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"github.com/atotto/clipboard"
	"os"
	"strconv"
	"strings"
)

type existsFunc func(string) bool

func osStatExists(file string) bool {
	_, err := os.Stat(file)
	return err == nil
}

var ignoreList = [...]string{"/", ".", "./", "..", "../"}

//var rootListing, _ = ioutil.ReadDir("/")
//var pwdListing, _ = ioutil.ReadDir(".")

func ignored(file string) bool {
	for _, val := range ignoreList {
		if file == val {
			return true
		}
	}
	return false
}

func longestFileEndIndex(line []rune, exists existsFunc) int {
	// Possible optimisations:
	// 1. this should start at the end - the longest substring first
	// 2. it could be a good strategy to list files and try to
	//    find a common prefix - if not just stop right there and then
	//    from / if it starts with / and if not from `pwd`
	//    do file listing from / and `pwd` only once
	//    need to consider relative dirs though which is annoying

	maxIndex := 0
	for i, _ := range line {
		slice := line[0 : i+1]
		file := string(slice)
		if !ignored(file) {
			if exists(file) {
				// TODO if this is not a dir, stop here
				maxIndex = i
			}
		}
	}
	return maxIndex
}

func longestFileInLine(line string, exists existsFunc) (firstCharIndex int, lastCharIndex int) {
	for searchStartIndex, _ := range line {
		searchSpace := []rune(line[searchStartIndex:len(line)])
		lastCharIndexInSlice := longestFileEndIndex(searchSpace, exists)
		lastCharIndexInLine := lastCharIndexInSlice + searchStartIndex
		if lastCharIndexInSlice > 0 && lastCharIndexInLine > lastCharIndex {
			lastCharIndex = lastCharIndexInLine
			firstCharIndex = searchStartIndex
		}
	}

	return firstCharIndex, lastCharIndex
}

func askUser() (requestedNumbers []string, err error) {
	fmt.Println()
	fmt.Print("to clipboard: ")
	ttyFile, err := os.Open("/dev/tty")
	if err != nil {
		return nil, err
	}
	defer ttyFile.Close()
	ttyReader := bufio.NewReader(ttyFile)
	s, err := ttyReader.ReadString('\n')
	if err != nil {
		return nil, err
	}
	return strings.Fields(s), nil
}

type PrintFunction func(string, string, int, int)

type Processor interface {
	processFile(file string) error
	processEnd() error
}

type NumbersGiven struct {
	clip      *bytes.Buffer
	fileCount *int
	numbers   []string
}

type AskForNumbers struct {
	clip  *bytes.Buffer
	files []string
}

func printShortFormat(fileCount *int) PrintFunction {
	return func(file string, line string, firstCharIndex int, lastCharIndex int) {
		fmt.Println(strconv.Itoa(*fileCount), file)
	}
}

func printLongFormat(fileCount *int) PrintFunction {
	return func(file string, line string, firstCharIndex int, lastCharIndex int) {
		fmt.Print("{")
		fmt.Print(strconv.Itoa(*fileCount))
		fmt.Print("} ")
		fmt.Print(line[:firstCharIndex])
		fmt.Print("{")
		fmt.Print(file)
		fmt.Print("}")
		fmt.Print(line[lastCharIndex+1:])
	}
}

func (ng *NumbersGiven) processEnd() error {
	writeToClipboard(ng.clip)
	return nil
}

func (ng *NumbersGiven) processFile(file string) error {
	// collect any file position arguments to copy to the
	// clipboard later
	for _, v := range ng.numbers {
		n, err := strconv.Atoi(v)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s is not a number\n", v)
			return err
		}
		if n == *ng.fileCount {
			ng.clip.WriteString(file)
			ng.clip.WriteString(" ")
		}
	}
	return nil
}

func (afn *AskForNumbers) processEnd() error {
	requestedNumbers, err := askUser()
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to read input: %s\n", err)
		return err
	}

	for _, n := range requestedNumbers {
		i, err := strconv.Atoi(n)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s is not a number\n", n)
			return err
		}
		afn.clip.WriteString(afn.files[i-1])
		afn.clip.WriteString(" ")
	}

	writeToClipboard(afn.clip)
	return nil
}

func (afn *AskForNumbers) processFile(file string) error {
	afn.files = append(afn.files, file)
	return nil
}

func writeToClipboard(buffer *bytes.Buffer) {
	clipboardOutput := buffer.String()
	if clipboardOutput != "" {
		clipboard.WriteAll(clipboardOutput)
		fmt.Printf("wrote \"%s\" to clipboard\n", clipboardOutput)
	}
}

func main() {
	var fileCount int
	var clip bytes.Buffer

	short := flag.Bool("s", false, "short format, only display file names")
	flag.Parse()

	extraArgs := flag.Args()

	var processor Processor
	if len(extraArgs) == 0 {
		processor = &AskForNumbers{clip: &clip}
	} else {
		processor = &NumbersGiven{
			clip:      &clip,
			fileCount: &fileCount,
			numbers:   extraArgs,
		}
	}
	var printer PrintFunction
	if *short {
		printer = printShortFormat(&fileCount)
	} else {
		printer = printLongFormat(&fileCount)
	}

	reader := bufio.NewReader(os.Stdin)

	for {
		line, err := reader.ReadString('\n')
		if err != nil {
			break
		}

		firstCharIndex, lastCharIndex := longestFileInLine(line, osStatExists)

		if lastCharIndex > 0 {
			fileCount++
			file := line[firstCharIndex : lastCharIndex+1]

			printer(file, line, firstCharIndex, lastCharIndex)
			err := processor.processFile(file)
			if err != nil {
				os.Exit(1)
			}
		} else if (!*short) {
			fmt.Print(line)
		}
	}

	err := processor.processEnd()
	if err != nil {
		os.Exit(1)
	}
}
<commit_msg>input validation<commit_after>// vim: tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab tw=72
//http://stackoverflow.com/questions/8757389/reading-file-line-by-line-in-go
package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"github.com/atotto/clipboard"
	"os"
	"strconv"
	"strings"
	"errors"
)

type existsFunc func(string) bool

func osStatExists(file string) bool {
	_, err := os.Stat(file)
	return err == nil
}

var ignoreList = [...]string{"/", ".", "./", "..", "../"}

//var rootListing, _ = ioutil.ReadDir("/")
//var pwdListing, _ = ioutil.ReadDir(".")

func ignored(file string) bool {
	for _, val := range ignoreList {
		if file == val {
			return true
		}
	}
	return false
}

func longestFileEndIndex(line []rune, exists existsFunc) int {
	// Possible optimisations:
	// 1. this should start at the end - the longest substring first
	// 2. it could be a good strategy to list files and try to
	//    find a common prefix - if not just stop right there and then
	//    from / if it starts with / and if not from `pwd`
	//    do file listing from / and `pwd` only once
	//    need to consider relative dirs though which is annoying

	maxIndex := 0
	for i, _ := range line {
		slice := line[0 : i+1]
		file := string(slice)
		if !ignored(file) {
			if exists(file) {
				// TODO if this is not a dir, stop here
				maxIndex = i
			}
		}
	}
	return maxIndex
}

func longestFileInLine(line string, exists existsFunc) (firstCharIndex int, lastCharIndex int) {
	for searchStartIndex, _ := range line {
		searchSpace := []rune(line[searchStartIndex:len(line)])
		lastCharIndexInSlice := longestFileEndIndex(searchSpace, exists)
		lastCharIndexInLine := lastCharIndexInSlice + searchStartIndex
		if lastCharIndexInSlice > 0 && lastCharIndexInLine > lastCharIndex {
			lastCharIndex = lastCharIndexInLine
			firstCharIndex = searchStartIndex
		}
	}

	return firstCharIndex, lastCharIndex
}

func askUser() (requestedNumbers []string, err error) {
	fmt.Println()
	fmt.Print("to clipboard: ")
	ttyFile, err := os.Open("/dev/tty")
	if err != nil {
		return nil, err
	}
	defer ttyFile.Close()
	ttyReader := bufio.NewReader(ttyFile)
	s, err := ttyReader.ReadString('\n')
	if err != nil {
		return nil, err
	}
	return strings.Fields(s), nil
}

type PrintFunction func(string, string, int, int)

type Processor interface {
	processFile(file string) error
	processEnd() error
}

type NumbersGiven struct {
	clip      *bytes.Buffer
	fileCount *int
	numbers   []string
}

type AskForNumbers struct {
	clip  *bytes.Buffer
	files []string
}

func printShortFormat(fileCount *int) PrintFunction {
	return func(file string, line string, firstCharIndex int, lastCharIndex int) {
		fmt.Println(strconv.Itoa(*fileCount), file)
	}
}

func printLongFormat(fileCount *int) PrintFunction {
	return func(file string, line string, firstCharIndex int, lastCharIndex int) {
		fmt.Print("{")
		fmt.Print(strconv.Itoa(*fileCount))
		fmt.Print("} ")
		fmt.Print(line[:firstCharIndex])
		fmt.Print("{")
		fmt.Print(file)
		fmt.Print("}")
		fmt.Print(line[lastCharIndex+1:])
	}
}

func (ng *NumbersGiven) processEnd() error {
	writeToClipboard(ng.clip)
	return nil
}

func (ng *NumbersGiven) processFile(file string) error {
	// collect any file position arguments to copy to the
	// clipboard later
	for _, v := range ng.numbers {
		n, err := strconv.Atoi(v)
		if err != nil {
			fmt.Fprintf(os.Stderr, "nw: %s is not a number\n", v)
			return err
		}
		if n == *ng.fileCount {
			ng.clip.WriteString(file)
			ng.clip.WriteString(" ")
		}
	}
	return nil
}

func (afn *AskForNumbers) processEnd() error {
	if len(afn.files) == 0 {
		fmt.Println("nw: no files names found, NUMBERWANG!")
		return nil
	}
	requestedNumbers, err := askUser()
	if err != nil {
		fmt.Fprintf(os.Stderr, "nw: failed to read input: %s\n", err)
		return err
	}

	for _, n := range requestedNumbers {
		i, err := strconv.Atoi(n)
		if err != nil {
			fmt.Fprintf(os.Stderr, "nw: %s is not a number\n", n)
			return err
		}
		if i <= 0 || i > len(afn.files) {
			fmt.Fprintf(os.Stderr, "nw: %s is not a valid choice\n", n)
			return errors.New("invalid choice")
		}
		afn.clip.WriteString(afn.files[i-1])
		afn.clip.WriteString(" ")
	}

	writeToClipboard(afn.clip)
	return nil
}

func (afn *AskForNumbers) processFile(file string) error {
	afn.files = append(afn.files, file)
	return nil
}

func writeToClipboard(buffer *bytes.Buffer) {
	clipboardOutput := buffer.String()
	if clipboardOutput != "" {
		clipboard.WriteAll(clipboardOutput)
		fmt.Printf("nw: wrote \"%s\" to clipboard\n", clipboardOutput)
	}
}

func main() {
	var fileCount int
	var clip bytes.Buffer

	short := flag.Bool("s", false, "short format, only display file names")
	flag.Parse()

	extraArgs := flag.Args()

	var processor Processor
	if len(extraArgs) == 0 {
		processor = &AskForNumbers{clip: &clip}
	} else {
		processor = &NumbersGiven{
			clip:      &clip,
			fileCount: &fileCount,
			numbers:   extraArgs,
		}
	}
	var printer PrintFunction
	if *short {
		printer = printShortFormat(&fileCount)
	} else {
		printer = printLongFormat(&fileCount)
	}

	reader := bufio.NewReader(os.Stdin)

	for {
		line, err := reader.ReadString('\n')
		if err != nil {
			break
		}

		firstCharIndex, lastCharIndex := longestFileInLine(line, osStatExists)

		if lastCharIndex > 0 {
			fileCount++
			file := line[firstCharIndex : lastCharIndex+1]

			printer(file, line, firstCharIndex, lastCharIndex)
			err := processor.processFile(file)
			if err != nil {
				os.Exit(1)
			}
		} else if (!*short) {
			fmt.Print(line)
		}
	}

	err := processor.processEnd()
	if err != nil {
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package z

import (
	"archive/tar"
	"archive/zip"
	"bufio"
	"bytes"
	"compress/gzip"
	"crypto/md5"
	"crypto/sha1"
	"fmt"
	"hash"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
)

// 获取本地MAC地址，只限Linux系统
func GetMac() string {
	var mac string
	var stdout, stderr bytes.Buffer
	cmd := exec.Command("/sbin/ifconfig", "-a")
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	cmd.Run()
	sOut := stdout.String()
	sErr := stderr.String()
	if len(sErr) == 0 {
		rx, _ := regexp.Compile("[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}")
		macStr := rx.FindString(strings.ToUpper(sOut))
		str := strings.ToUpper(macStr)
		mac = strings.Replace(str, ":", "", -1)
	} else {
		log.Panic(sErr)
	}
	return Trim(mac)
}

func GetIntMac(v string) (string, error) {
	var mac string
	var stdout, stderr bytes.Buffer
	cmd := exec.Command("/sbin/ifconfig", v)
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	cmd.Run()
	sOut := stdout.String()
	sErr := stderr.String()
	if len(sErr) == 0 {
		rx, _ := regexp.Compile("[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}")
		macStr := rx.FindString(strings.ToUpper(sOut))
		str := strings.ToUpper(macStr)
		mac = strings.Replace(str, ":", "", -1)
	} else {
		return mac, fmt.Errorf("%s", Trim(sErr))
	}
	return Trim(mac), nil
}

// 计算一个文件的 MD5 指纹, 文件路径为磁盘绝对路径
func MD5(ph string) string {
	return Finger(md5.New(), ph)
}

// 将磁盘某个文件按照某种算法计算成加密指纹
func Finger(h hash.Hash, ph string) string {
	// 打开文件
	f, err := os.Open(ph)
	if err != nil {
		return ""
	}
	defer f.Close()
	// 读取
	io.Copy(h, bufio.NewReader(f))
	// 返回计算结果
	return fmt.Sprintf("%x", h.Sum(nil))
}

// 对字符串进行SHA1哈希
func StrSHA1(data string) string {
	t := sha1.New()
	io.WriteString(t, data)
	return fmt.Sprintf("%x", t.Sum(nil))
}

// 通过唯一时间的字符串，返回唯一的SHA1哈希
func RandomSHA1() string {
	return StrSHA1(UnixNano())
}

// 生成一个 UUID 字符串（小写，去掉减号），需要系统支持 "uuidgen" 命令
// 返回的字符串格式如 "1694108edc6348b08364e604dee1bf35"
func UU() string {
	return strings.Replace(UU16(), "-", "", -1)
}

// 生成一个 UUID 字符串（小写），需要系统支持 "uuidgen" 命令
// 返回的字符串格式如 "1694108e-dc63-48b0-8364-e604dee1bf35"
func UU16() string {
	bs, err := exec.Command("uuidgen").Output()
	if nil != err {
		log.Fatal("fail to found command 'uuidgen' in $PATH")
	}
	return strings.ToLower(TrimBytes(bs))
}

// 解压Tar文件
func Untar(file, path string) error {
	// 打开文件
	f, err := os.Open(file)
	if err != nil {
		return err
	}
	defer f.Close()
	// 读取GZIP
	gr, err := gzip.NewReader(f)
	if err != nil {
		return err
	}
	defer gr.Close()
	// 读取TAR
	tr := tar.NewReader(gr)
	for {
		hdr, err := tr.Next()
		if err == io.EOF {
			break
		} else if err != nil {
			return err
		}
		if hdr.FileInfo().IsDir() {
			os.MkdirAll(path+string(os.PathSeparator)+hdr.Name, hdr.FileInfo().Mode())
		} else {
			fw, err := os.OpenFile(path+string(os.PathSeparator)+hdr.Name, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, hdr.FileInfo().Mode())
			if err != nil {
				return err
			}
			defer fw.Close()
			_, err = io.Copy(fw, tr)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

// 运行命令脚本，只限Linux系统
func LinuxCmd(sh string) error {
	var stderr bytes.Buffer
	var stdout bytes.Buffer
	cmd := exec.Command("/bin/sh", sh)
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	err := cmd.Run()
	if err != nil {
		return fmt.Errorf("[%s] [%s]", sh, err)
	}
	sOut := stdout.String()
	if len(sOut) != 0 {
		log.Println(sOut)
	}
	sErr := stderr.String()
	if len(sErr) != 0 {
		return fmt.Errorf(sh, sErr)
	}
	return nil
}

// 运行系统命令，只限Linux系统
func LinuxBash(sh string) error {
	var stderr bytes.Buffer
	var stdout bytes.Buffer
	cmd := exec.Command(sh)
	cmd.Stderr = &stderr
	cmd.Stdout = &stdout
	err := cmd.Run()
	if err != nil {
		return fmt.Errorf("[%s] [%s]", sh, err)
	}
	sOut := stdout.String()
	if len(sOut) != 0 {
		log.Println(sOut)
	}
	sErr := stderr.String()
	if len(sErr) != 0 {
		return fmt.Errorf(sErr)
	}
	return nil
}

// 创建压缩文件
func CreateZip(path, ph string) error {
	// 创建写入缓冲区
	buf := new(bytes.Buffer)
	// 创建压缩缓冲区
	w := zip.NewWriter(buf)
	// 文件列表
	files := make([]string, 0)
	// 读取文件列表
	err := filepath.Walk(path, func(aph string, f os.FileInfo, err error) error {
		// 文件不存在
		if f == nil {
			return nil
		}
		// 跳过文件夹
		if f.IsDir() {
			return nil
		}
		files = append(files, Range(aph, len(path), len(aph)))
		return nil
	})
	// 判断是否出错
	if err != nil {
		return err
	}
	// 将文件读取
	for _, file := range files {
		f, err := w.Create(file)
		if err != nil {
			return err
		}
		r, err := os.Open(path + "/" + file)
		if err != nil {
			return err
		}
		data, err := ioutil.ReadAll(r)
		if err != nil {
			return err
		}
		_, err = f.Write(data)
		if err != nil {
			return err
		}
		r.Close()
	}
	// 关闭缓冲区
	err = w.Close()
	if err != nil {
		return err
	}
	// 写入
	FileWF(ph, func(f *os.File) {
		f.Write(buf.Bytes())
	})
	// 返回
	return nil
}

// 字符串
func Range(str string, start, end int) string {
	var data string
	for i, s := range str {
		if i >= start && i < end {
			data += string(s)
		}
	}
	return data
}
<commit_msg>update GetIntMac<commit_after>package z

import (
	"archive/tar"
	"archive/zip"
	"bufio"
	"bytes"
	"compress/gzip"
	"crypto/md5"
	"crypto/sha1"
	"fmt"
	"hash"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
)

// 获取本地MAC地址，只限Linux系统
func GetMac() string {
	var mac string
	var stdout, stderr bytes.Buffer
	cmd := exec.Command("/sbin/ifconfig", "-a")
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	cmd.Run()
	sOut := stdout.String()
	sErr := stderr.String()
	if len(sErr) == 0 {
		rx, _ := regexp.Compile("[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}")
		macStr := rx.FindString(strings.ToUpper(sOut))
		str := strings.ToUpper(macStr)
		mac = strings.Replace(str, ":", "", -1)
	} else {
		log.Panic(sErr)
	}
	return Trim(mac)
}

func GetIntMac(v string) string {
	var mac string
	var stdout, stderr bytes.Buffer
	cmd := exec.Command("/sbin/ifconfig", v)
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	cmd.Run()
	sOut := stdout.String()
	sErr := stderr.String()
	if len(sErr) == 0 {
		rx, _ := regexp.Compile("[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}")
		macStr := rx.FindString(strings.ToUpper(sOut))
		str := strings.ToUpper(macStr)
		mac = strings.Replace(str, ":", "", -1)
	} else {
		log.Panic(sErr)
	}
	return Trim(mac), nil
}

// 计算一个文件的 MD5 指纹, 文件路径为磁盘绝对路径
func MD5(ph string) string {
	return Finger(md5.New(), ph)
}

// 将磁盘某个文件按照某种算法计算成加密指纹
func Finger(h hash.Hash, ph string) string {
	// 打开文件
	f, err := os.Open(ph)
	if err != nil {
		return ""
	}
	defer f.Close()
	// 读取
	io.Copy(h, bufio.NewReader(f))
	// 返回计算结果
	return fmt.Sprintf("%x", h.Sum(nil))
}

// 对字符串进行SHA1哈希
func StrSHA1(data string) string {
	t := sha1.New()
	io.WriteString(t, data)
	return fmt.Sprintf("%x", t.Sum(nil))
}

// 通过唯一时间的字符串，返回唯一的SHA1哈希
func RandomSHA1() string {
	return StrSHA1(UnixNano())
}

// 生成一个 UUID 字符串（小写，去掉减号），需要系统支持 "uuidgen" 命令
// 返回的字符串格式如 "1694108edc6348b08364e604dee1bf35"
func UU() string {
	return strings.Replace(UU16(), "-", "", -1)
}

// 生成一个 UUID 字符串（小写），需要系统支持 "uuidgen" 命令
// 返回的字符串格式如 "1694108e-dc63-48b0-8364-e604dee1bf35"
func UU16() string {
	bs, err := exec.Command("uuidgen").Output()
	if nil != err {
		log.Fatal("fail to found command 'uuidgen' in $PATH")
	}
	return strings.ToLower(TrimBytes(bs))
}

// 解压Tar文件
func Untar(file, path string) error {
	// 打开文件
	f, err := os.Open(file)
	if err != nil {
		return err
	}
	defer f.Close()
	// 读取GZIP
	gr, err := gzip.NewReader(f)
	if err != nil {
		return err
	}
	defer gr.Close()
	// 读取TAR
	tr := tar.NewReader(gr)
	for {
		hdr, err := tr.Next()
		if err == io.EOF {
			break
		} else if err != nil {
			return err
		}
		if hdr.FileInfo().IsDir() {
			os.MkdirAll(path+string(os.PathSeparator)+hdr.Name, hdr.FileInfo().Mode())
		} else {
			fw, err := os.OpenFile(path+string(os.PathSeparator)+hdr.Name, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, hdr.FileInfo().Mode())
			if err != nil {
				return err
			}
			defer fw.Close()
			_, err = io.Copy(fw, tr)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

// 运行命令脚本，只限Linux系统
func LinuxCmd(sh string) error {
	var stderr bytes.Buffer
	var stdout bytes.Buffer
	cmd := exec.Command("/bin/sh", sh)
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	err := cmd.Run()
	if err != nil {
		return fmt.Errorf("[%s] [%s]", sh, err)
	}
	sOut := stdout.String()
	if len(sOut) != 0 {
		log.Println(sOut)
	}
	sErr := stderr.String()
	if len(sErr) != 0 {
		return fmt.Errorf(sh, sErr)
	}
	return nil
}

// 运行系统命令，只限Linux系统
func LinuxBash(sh string) error {
	var stderr bytes.Buffer
	var stdout bytes.Buffer
	cmd := exec.Command(sh)
	cmd.Stderr = &stderr
	cmd.Stdout = &stdout
	err := cmd.Run()
	if err != nil {
		return fmt.Errorf("[%s] [%s]", sh, err)
	}
	sOut := stdout.String()
	if len(sOut) != 0 {
		log.Println(sOut)
	}
	sErr := stderr.String()
	if len(sErr) != 0 {
		return fmt.Errorf(sErr)
	}
	return nil
}

// 创建压缩文件
func CreateZip(path, ph string) error {
	// 创建写入缓冲区
	buf := new(bytes.Buffer)
	// 创建压缩缓冲区
	w := zip.NewWriter(buf)
	// 文件列表
	files := make([]string, 0)
	// 读取文件列表
	err := filepath.Walk(path, func(aph string, f os.FileInfo, err error) error {
		// 文件不存在
		if f == nil {
			return nil
		}
		// 跳过文件夹
		if f.IsDir() {
			return nil
		}
		files = append(files, Range(aph, len(path), len(aph)))
		return nil
	})
	// 判断是否出错
	if err != nil {
		return err
	}
	// 将文件读取
	for _, file := range files {
		f, err := w.Create(file)
		if err != nil {
			return err
		}
		r, err := os.Open(path + "/" + file)
		if err != nil {
			return err
		}
		data, err := ioutil.ReadAll(r)
		if err != nil {
			return err
		}
		_, err = f.Write(data)
		if err != nil {
			return err
		}
		r.Close()
	}
	// 关闭缓冲区
	err = w.Close()
	if err != nil {
		return err
	}
	// 写入
	FileWF(ph, func(f *os.File) {
		f.Write(buf.Bytes())
	})
	// 返回
	return nil
}

// 字符串
func Range(str string, start, end int) string {
	var data string
	for i, s := range str {
		if i >= start && i < end {
			data += string(s)
		}
	}
	return data
}
<|endoftext|>


<commit_before>/**
 * go-mapbox Maps Module Tests
 * Wraps the mapbox Maps API for server side use
 * See https://www.mapbox.com/api-documentation/#maps for API information
 *
 * https://github.com/ryankurte/go-mapbox
 * Copyright 2017 Ryan Kurte
 */

package maps

import (
	"os"
	"testing"
)

import (
	"fmt"
	"github.com/ryankurte/go-mapbox/lib/base"
)

func TestMaps(t *testing.T) {

	token := os.Getenv("MAPBOX_TOKEN")
	if token == "" {
		t.Error("Mapbox API token not found")
		t.FailNow()
	}

	b := base.NewBase(token)
	//b.SetDebug(true)

	maps := NewMaps(b)

	t.Run("Can fetch map tiles as png", func(t *testing.T) {

		img, _, err := maps.GetTile(MapIDStreets, 1, 0, 1, MapFormatPng, true)

		if err != nil {
			t.Error(err)
			t.FailNow()
		}

		err = SaveImagePNG(img, "/tmp/go-mapbox-test.png")
		if err != nil {
			t.Error(err)
			t.FailNow()
		}
	})

	t.Run("Can fetch map tiles as jpeg", func(t *testing.T) {

		img, _, err := maps.GetTile(MapIDSatellite, 1, 0, 1, MapFormatJpg90, true)

		if err != nil {
			t.Error(err)
			t.FailNow()
		}

		err = SaveImageJPG(img, "/tmp/go-mapbox-test.jpg")
		if err != nil {
			t.Error(err)
			t.FailNow()
		}
	})

	t.Run("Can fetch terrain RGB tiles", func(t *testing.T) {

		img, _, err := maps.GetTile(MapIDTerrainRGB, 1, 0, 1, MapFormatPngRaw, true)
		if err != nil {
			t.Error(err)
			t.FailNow()
		}

		err = SaveImagePNG(img, "/tmp/go-mapbox-terrain.png")
		if err != nil {
			t.Error(err)
			t.FailNow()
		}
	})

	t.Run("Can fetch map tiles by location", func(t *testing.T) {

		locA := base.Location{-45.942805, 166.568500}
		locB := base.Location{-34.2186101, 183.4015517}

		images, configs, err := maps.GetEnclosingTiles(MapIDSatellite, locA, locB, 6, MapFormatJpg90, true)

		if err != nil {
			t.Error(err)
			t.FailNow()
		}

		for y := range images {
			for x := range images[y] {
				SaveImageJPG(images[y][x], fmt.Sprintf("/tmp/go-mapbox-stitch-%d-%d.jpg", x, y))
			}
		}

		img := StitchTiles(images, configs[0][0])

		err = SaveImageJPG(img, "/tmp/go-mapbox-stitch.jpg")
		if err != nil {
			t.Error(err)
			t.FailNow()
		}

	})

	t.Run("Can fetch map tiles by location (with cache)", func(t *testing.T) {

		locA := base.Location{-45.942805, 166.568500}
		locB := base.Location{-34.2186101, 183.4015517}

		cache, err := NewFileCache("/tmp/cache")
		if err != nil {
			t.Error(err)
			t.FailNow()
		}

		maps.SetCache(cache)

		images, configs, err := maps.GetEnclosingTiles(MapIDSatellite, locA, locB, 6, MapFormatJpg90, true)
		if err != nil {
			t.Error(err)
			t.FailNow()
		}
		img := StitchTiles(images, configs[0][0])

		err = SaveImageJPG(img, "/tmp/go-mapbox-stitch2.jpg")
		if err != nil {
			t.Error(err)
			t.FailNow()
		}

	})

}
<commit_msg>Updated tmp cache name<commit_after>/**
 * go-mapbox Maps Module Tests
 * Wraps the mapbox Maps API for server side use
 * See https://www.mapbox.com/api-documentation/#maps for API information
 *
 * https://github.com/ryankurte/go-mapbox
 * Copyright 2017 Ryan Kurte
 */

package maps

import (
	"os"
	"testing"
)

import (
	"fmt"
	"github.com/ryankurte/go-mapbox/lib/base"
)

func TestMaps(t *testing.T) {

	token := os.Getenv("MAPBOX_TOKEN")
	if token == "" {
		t.Error("Mapbox API token not found")
		t.FailNow()
	}

	b := base.NewBase(token)
	//b.SetDebug(true)

	maps := NewMaps(b)

	t.Run("Can fetch map tiles as png", func(t *testing.T) {

		img, _, err := maps.GetTile(MapIDStreets, 1, 0, 1, MapFormatPng, true)

		if err != nil {
			t.Error(err)
			t.FailNow()
		}

		err = SaveImagePNG(img, "/tmp/go-mapbox-test.png")
		if err != nil {
			t.Error(err)
			t.FailNow()
		}
	})

	t.Run("Can fetch map tiles as jpeg", func(t *testing.T) {

		img, _, err := maps.GetTile(MapIDSatellite, 1, 0, 1, MapFormatJpg90, true)

		if err != nil {
			t.Error(err)
			t.FailNow()
		}

		err = SaveImageJPG(img, "/tmp/go-mapbox-test.jpg")
		if err != nil {
			t.Error(err)
			t.FailNow()
		}
	})

	t.Run("Can fetch terrain RGB tiles", func(t *testing.T) {

		img, _, err := maps.GetTile(MapIDTerrainRGB, 1, 0, 1, MapFormatPngRaw, true)
		if err != nil {
			t.Error(err)
			t.FailNow()
		}

		err = SaveImagePNG(img, "/tmp/go-mapbox-terrain.png")
		if err != nil {
			t.Error(err)
			t.FailNow()
		}
	})

	t.Run("Can fetch map tiles by location", func(t *testing.T) {

		locA := base.Location{-45.942805, 166.568500}
		locB := base.Location{-34.2186101, 183.4015517}

		images, configs, err := maps.GetEnclosingTiles(MapIDSatellite, locA, locB, 6, MapFormatJpg90, true)

		if err != nil {
			t.Error(err)
			t.FailNow()
		}

		for y := range images {
			for x := range images[y] {
				SaveImageJPG(images[y][x], fmt.Sprintf("/tmp/go-mapbox-stitch-%d-%d.jpg", x, y))
			}
		}

		img := StitchTiles(images, configs[0][0])

		err = SaveImageJPG(img, "/tmp/go-mapbox-stitch.jpg")
		if err != nil {
			t.Error(err)
			t.FailNow()
		}

	})

	t.Run("Can fetch map tiles by location (with cache)", func(t *testing.T) {

		locA := base.Location{-45.942805, 166.568500}
		locB := base.Location{-34.2186101, 183.4015517}

		cache, err := NewFileCache("/tmp/go-mapbox-cache")
		if err != nil {
			t.Error(err)
			t.FailNow()
		}

		maps.SetCache(cache)

		images, configs, err := maps.GetEnclosingTiles(MapIDSatellite, locA, locB, 6, MapFormatJpg90, true)
		if err != nil {
			t.Error(err)
			t.FailNow()
		}
		img := StitchTiles(images, configs[0][0])

		err = SaveImageJPG(img, "/tmp/go-mapbox-stitch2.jpg")
		if err != nil {
			t.Error(err)
			t.FailNow()
		}

	})

}
<|endoftext|>