<commit_before>package tailer

import (
	"fmt"
	debugLogger "github.com/fstab/grok_exporter/logger"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
	"time"
)

type logrotateOption int
type logrotateMoveOption int
type loggerOption int

const ( // see 'man logrotate'
	copy         logrotateOption = iota // Don’t change the original logfile at all.
	copytruncate                        // Truncate the original log file in place instead of removing it.
	nocreate                            // Don't create a new logfile after rotation.
	create                              // Create a new empty logfile immediately after rotation.
)

const (
	mv logrotateMoveOption = iota // Move the old logfile to the backup.
	cp                            // Copy the old logfile to the backup, then remove it.
	rm                            // Delete the old logfile without keeping a backup.
)

const (
	closeFileAfterEachLine loggerOption = iota // Logger does not keep the file open.
	keepOpen                                   // Logger keeps the file open.
)

func (opt logrotateOption) String() string {
	switch {
	case opt == copy:
		return "copy"
	case opt == copytruncate:
		return "copytruncate"
	case opt == nocreate:
		return "nocreate"
	case opt == create:
		return "create"
	default:
		return "unknown"
	}
}

func (opt logrotateMoveOption) String() string {
	switch {
	case opt == mv:
		return "mv"
	case opt == cp:
		return "cp"
	case opt == rm:
		return "rm"
	default:
		return "unknown"
	}
}

func (opt loggerOption) String() string {
	switch {
	case opt == closeFileAfterEachLine:
		return "closeFileAfterEachLine"
	case opt == keepOpen:
		return "keepOpen"
	default:
		return "unknown"
	}
}

func TestCloseLogfileAfterEachLine(t *testing.T) {
	for _, mvOpt := range []logrotateMoveOption{mv, cp, rm} {
		testLogrotate(t, create, mvOpt, closeFileAfterEachLine)
		testLogrotate(t, nocreate, mvOpt, closeFileAfterEachLine)
	}
	// For logrotate options 'copy' and 'copytruncate', only the mvOpt 'cp' makes sense.
	testLogrotate(t, copy, cp, closeFileAfterEachLine)
	testLogrotate(t, copytruncate, cp, closeFileAfterEachLine)
}

func TestKeepLogfileOpen(t *testing.T) {
	// When the logger keeps the file open, only the logrotate options 'copy' and 'copytruncate' make sense.
	testLogrotate(t, copy, cp, keepOpen)
	testLogrotate(t, copytruncate, cp, keepOpen)
}

func testLogrotate(t *testing.T, logrotateOpt logrotateOption, logrotateMoveOpt logrotateMoveOption, loggerOpt loggerOption) {
	fmt.Printf("Running test with logrotate option '%v', move option '%v', and logger option '%v'.\n", logrotateOpt, logrotateMoveOpt, loggerOpt)
	tmpDir := mkTmpDirOrFail(t)
	defer cleanUp(t, tmpDir)
	logfile := mkTmpFileOrFail(t, tmpDir)
	logger := newLogger(t, logfile, loggerOpt)
	defer logger.close(t)

	logger.log(t, "test line 1")
	logger.log(t, "test line 2")

	tail := RunFileTailer(logfile, true, debugLogger.New(true))
	defer tail.Close()

	// We don't expect errors. However, start a go-routine listening on
	// the tailer's errorChannel in case something goes wrong.
	stopFailOnError := failOnError(t, tail.ErrorChan())
	defer func() {
		stopFailOnError <- true
		close(stopFailOnError)
	}()

	// The first two lines are received without any fsnotify event,
	// because they were written before the watcher was started.
	expect(tail.LineChan(), "test line 1", 1*time.Second, t)
	expect(tail.LineChan(), "test line 2", 1*time.Second, t)

	// Append a line and see if the event is processed.
	logger.log(t, "test line 3")
	expect(tail.LineChan(), "test line 3", 1*time.Second, t)

	rotate(t, logfile, logrotateOpt, logrotateMoveOpt)

	// Log two more lines and see if they are received.
	logger.log(t, "line 4")
	logger.log(t, "line 5")
	expect(tail.LineChan(), "line 4", 10*time.Second, t) // few seconds longer to get filesystem notifications for rotate()
	expect(tail.LineChan(), "line 5", 1*time.Second, t)
}

// Consume the tailer's error channel in case something goes wrong.
func failOnError(t *testing.T, errorChan chan error) chan bool {
	done := make(chan bool)
	go func() {
		for {
			select {
			case err := <-errorChan:
				// err == nil means the tailer is shutting down, we will receive
				// on done channel in that case.
				if err != nil {
					t.Fatalf("Tailer failed: %v", err.Error())
				}
			case <-done:
				return
			}
		}
	}()
	return done
}

func newLogger(t *testing.T, logfile string, opt loggerOption) logger {
	switch {
	case opt == closeFileAfterEachLine:
		return newCloseFileAfterEachLineLogger(t, logfile)
	case opt == keepOpen:
		return newKeepOpenLogger(t, logfile)
	default:
		t.Fatalf("%v: Unsupported logger option.", opt)
		return nil
	}
}

type logger interface {
	log(t *testing.T, msg string)
	close(t *testing.T)
}

type closeFileAfterEachLineLogger struct {
	path string
}

func newCloseFileAfterEachLineLogger(t *testing.T, logfile string) logger {
	return &closeFileAfterEachLineLogger{
		path: logfile,
	}
}

func (l *closeFileAfterEachLineLogger) log(t *testing.T, msg string) {
	f, err := os.OpenFile(l.path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		t.Fatalf("%v: Failed to open file for writing: %v", l.path, err.Error())
	}
	defer f.Close()
	_, err = f.WriteString(fmt.Sprintf("%v\n", msg))
	if err != nil {
		t.Fatalf("%v: Failed to write to file: %v", l.path, err.Error())
	}
	err = f.Close()
	if err != nil {
		t.Fatalf("%v: Failed to close file: %v", l.path, err.Error())
	}
}

func (l *closeFileAfterEachLineLogger) close(t *testing.T) {
	// nothing to do
}

type keepOpenLogger struct {
	file *os.File
}

func newKeepOpenLogger(t *testing.T, logfile string) logger {
	f, err := os.OpenFile(logfile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		t.Fatalf("%v: Failed to open file for writing: %v", logfile, err.Error())
	}
	return &keepOpenLogger{
		file: f,
	}
}

func (l *keepOpenLogger) log(t *testing.T, msg string) {
	_, err := l.file.WriteString(fmt.Sprintf("%v\n", msg))
	if err != nil {
		t.Fatalf("%v: Failed to write to file: %v", l.file.Name(), err.Error())
	}
	err = l.file.Sync()
	if err != nil {
		t.Fatalf("%v: Failed to flush the file: %v", l.file.Name(), err.Error())
	}
}

func (l *keepOpenLogger) close(t *testing.T) {
	err := l.file.Close()
	if err != nil {
		t.Fatalf("%v: Failed to close logfile: %v", l.file.Name(), err.Error())
	}
}

func mkTmpDirOrFail(t *testing.T) string {
	testdir, err := ioutil.TempDir("", "grok_exporter")
	if err != nil {
		t.Fatalf("Failed to create test directory: %v", err.Error())
	}
	return testdir
}

func cleanUp(t *testing.T, dir string) {
	err := os.RemoveAll(dir)
	if err != nil {
		t.Errorf("%v: Failed to remove the test directory after running the tests: %v", dir, err.Error())
	}
}

func mkTmpFileOrFail(t *testing.T, dir string) string {
	if len(ls(t, dir)) != 0 {
		t.Fatalf("%v: Directory not empty.", dir)
	}
	logfile, err := ioutil.TempFile(dir, "grok_exporter_test_log")
	if err != nil {
		t.Fatalf("Cannot create temp file: %v", err.Error())
	}
	err = logfile.Close()
	if err != nil {
		t.Fatalf("Failed to close temp file: %v", err.Error())
	}
	if len(ls(t, dir)) != 1 {
		t.Fatalf("%v: Expected exactly one file in directory.", dir)
	}
	return logfile.Name()
}

func containsFile(files []os.FileInfo, filename string) bool {
	for _, f := range files {
		if filepath.Base(f.Name()) == filepath.Base(filename) {
			return true
		}
	}
	return false
}

func ls(t *testing.T, path string) []os.FileInfo {
	result, err := ioutil.ReadDir(path)
	if err != nil {
		t.Fatalf("%v: Failed to list directory: %v", path, err.Error())
	}
	return result
}

func rotate(t *testing.T, logfile string, opt logrotateOption, mvOpt logrotateMoveOption) {
	dir := filepath.Dir(logfile)
	filename := filepath.Base(logfile)
	filesBefore := ls(t, dir)
	if !containsFile(filesBefore, filename) {
		t.Fatalf("%v does not contain %v before logrotate.", dir, filename)
	}
	switch {
	case opt == nocreate:
		moveOrFail(t, mvOpt, logfile)
	case opt == create:
		moveOrFail(t, mvOpt, logfile)
		createOrFail(t, logfile)
	case opt == copytruncate:
		if mvOpt != cp {
			t.Fatalf("Rotating with '%v' does not make sense when moving the logfile with '%v'", opt, mvOpt)
		}
		cpOrFail(t, logfile, fmt.Sprintf("%v.1", logfile))
		truncateOrFail(t, logfile)
	case opt == copy:
		if mvOpt != cp {
			t.Fatalf("Rotating with '%v' does not make sense when moving the logfile with '%v'", opt, mvOpt)
		}
		cpOrFail(t, logfile, fmt.Sprintf("%v.1", logfile))
	default:
		t.Fatalf("Unknown logrotate option.")
	}
}

func moveOrFail(t *testing.T, mvOpt logrotateMoveOption, logfile string) {
	dir := filepath.Dir(logfile)
	filename := filepath.Base(logfile)
	switch {
	case mvOpt == mv:
		mvOrFail(t, logfile, fmt.Sprintf("%v.1", logfile))
	case mvOpt == cp:
		cpOrFail(t, logfile, fmt.Sprintf("%v.1", logfile))
		rmOrFail(t, logfile)
	case mvOpt == rm:
		rmOrFail(t, logfile)
	}
	filesAfterRename := ls(t, dir)
	if containsFile(filesAfterRename, filename) {
		t.Fatalf("%v still contains file %v after mv.", dir, filename)
	}
}

func mvOrFail(t *testing.T, fromPath string, toPath string) {
	err := os.Rename(fromPath, toPath)
	if err != nil {
		t.Fatalf("%v: Failed to mv file: %v", fromPath, err.Error())
	}
}

func cpOrFail(t *testing.T, fromPath string, toPath string) {
	data, err := ioutil.ReadFile(fromPath)
	if err != nil {
		t.Fatalf("%v: Copy failed, cannot read file: %v", fromPath, err.Error())
	}
	err = ioutil.WriteFile(toPath, data, 0644)
	if err != nil {
		t.Fatalf("%v: Copy failed, cannot write file: %v", toPath, err.Error())
	}
}

func rmOrFail(t *testing.T, path string) {
	err := os.Remove(path)
	if err != nil {
		t.Fatalf("%v: Remove failed: %v", path, err.Error())
	}
}

func createOrFail(t *testing.T, logfile string) {
	dir := filepath.Dir(logfile)
	filename := filepath.Base(logfile)
	filesBeforeCreate := ls(t, dir)
	if containsFile(filesBeforeCreate, filename) {
		t.Fatalf("%v contains file %v before create.", dir, filename)
	}
	f, err := os.Create(logfile)
	if err != nil {
		t.Fatalf("Failed to re-create %v while simulating logrotate: %v", filename, err.Error())
	}
	err = f.Close()
	if err != nil {
		t.Fatalf("%v: Failed to close file: %v", filename, err.Error())
	}
	filesAfterCreate := ls(t, dir)
	if !containsFile(filesAfterCreate, filename) {
		t.Fatalf("%v does not contain %v after create.", dir, filename)
	}
}

func truncateOrFail(t *testing.T, logfile string) {
	err := os.Truncate(logfile, 0)
	if err != nil {
		t.Fatalf("%v: Error truncating the file: %v", logfile, err.Error())
	}
}

func expect(c chan string, line string, timeout time.Duration, t *testing.T) {
	timeoutChan := make(chan bool, 1)
	go func() {
		time.Sleep(timeout)
		timeoutChan <- true
		close(timeoutChan)
	}()
	select {
	case result := <-c:
		if result != line {
			t.Errorf("Expected '%v', but got '%v'.", line, result)
		} else {
			fmt.Printf("Read expected line '%v'\n", line)
		}
	case <-timeoutChan:
		t.Errorf("Timeout while waiting for line '%v'", line)
	}
}
<commit_msg>debugging fileTailer_test on osx on travis-ci.<commit_after>package tailer

import (
	"fmt"
	debugLogger "github.com/fstab/grok_exporter/logger"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
	"time"
)

type logrotateOption int
type logrotateMoveOption int
type loggerOption int

const ( // see 'man logrotate'
	copy         logrotateOption = iota // Don’t change the original logfile at all.
	copytruncate                        // Truncate the original log file in place instead of removing it.
	nocreate                            // Don't create a new logfile after rotation.
	create                              // Create a new empty logfile immediately after rotation.
)

const (
	mv logrotateMoveOption = iota // Move the old logfile to the backup.
	cp                            // Copy the old logfile to the backup, then remove it.
	rm                            // Delete the old logfile without keeping a backup.
)

const (
	closeFileAfterEachLine loggerOption = iota // Logger does not keep the file open.
	keepOpen                                   // Logger keeps the file open.
)

func (opt logrotateOption) String() string {
	switch {
	case opt == copy:
		return "copy"
	case opt == copytruncate:
		return "copytruncate"
	case opt == nocreate:
		return "nocreate"
	case opt == create:
		return "create"
	default:
		return "unknown"
	}
}

func (opt logrotateMoveOption) String() string {
	switch {
	case opt == mv:
		return "mv"
	case opt == cp:
		return "cp"
	case opt == rm:
		return "rm"
	default:
		return "unknown"
	}
}

func (opt loggerOption) String() string {
	switch {
	case opt == closeFileAfterEachLine:
		return "closeFileAfterEachLine"
	case opt == keepOpen:
		return "keepOpen"
	default:
		return "unknown"
	}
}

func TestCloseLogfileAfterEachLine(t *testing.T) {
	for _, mvOpt := range []logrotateMoveOption{mv, cp, rm} {
		testLogrotate(t, create, mvOpt, closeFileAfterEachLine)
		testLogrotate(t, nocreate, mvOpt, closeFileAfterEachLine)
	}
	// For logrotate options 'copy' and 'copytruncate', only the mvOpt 'cp' makes sense.
	testLogrotate(t, copy, cp, closeFileAfterEachLine)
	testLogrotate(t, copytruncate, cp, closeFileAfterEachLine)
}

func TestKeepLogfileOpen(t *testing.T) {
	// When the logger keeps the file open, only the logrotate options 'copy' and 'copytruncate' make sense.
	testLogrotate(t, copy, cp, keepOpen)
	testLogrotate(t, copytruncate, cp, keepOpen)
}

func testLogrotate(t *testing.T, logrotateOpt logrotateOption, logrotateMoveOpt logrotateMoveOption, loggerOpt loggerOption) {
	fmt.Printf("Running test with logrotate option '%v', move option '%v', and logger option '%v'.\n", logrotateOpt, logrotateMoveOpt, loggerOpt)
	tmpDir := mkTmpDirOrFail(t)
	defer cleanUp(t, tmpDir)
	logfile := mkTmpFileOrFail(t, tmpDir)
	logger := newLogger(t, logfile, loggerOpt)
	defer logger.close(t)

	logger.log(t, "test line 1")
	logger.log(t, "test line 2")

	tail := RunFileTailer(logfile, true, debugLogger.New(true))
	defer tail.Close()

	// We don't expect errors. However, start a go-routine listening on
	// the tailer's errorChannel in case something goes wrong.
	stopFailOnError := failOnError(t, tail.ErrorChan())
	defer func() {
		stopFailOnError <- true
		close(stopFailOnError)
	}()

	// The first two lines are received without any fsnotify event,
	// because they were written before the watcher was started.
	expect(tail.LineChan(), "test line 1", 1*time.Second, t)
	expect(tail.LineChan(), "test line 2", 1*time.Second, t)

	// Append a line and see if the event is processed.
	logger.log(t, "test line 3")
	expect(tail.LineChan(), "test line 3", 1*time.Second, t)

	rotate(t, logfile, logrotateOpt, logrotateMoveOpt)

	// Log two more lines and see if they are received.
	logger.log(t, "line 4")
	logger.log(t, "line 5")
	expect(tail.LineChan(), "line 4", 10*time.Second, t) // few seconds longer to get filesystem notifications for rotate()
	expect(tail.LineChan(), "line 5", 1*time.Second, t)
}

// Consume the tailer's error channel in case something goes wrong.
func failOnError(t *testing.T, errorChan chan error) chan bool {
	done := make(chan bool)
	go func() {
		for {
			select {
			case err := <-errorChan:
				// err == nil means the tailer is shutting down, we will receive
				// on done channel in that case.
				if err != nil {
					t.Fatalf("Tailer failed: %v", err.Error())
				}
			case <-done:
				return
			}
		}
	}()
	return done
}

func newLogger(t *testing.T, logfile string, opt loggerOption) logger {
	switch {
	case opt == closeFileAfterEachLine:
		return newCloseFileAfterEachLineLogger(t, logfile)
	case opt == keepOpen:
		return newKeepOpenLogger(t, logfile)
	default:
		t.Fatalf("%v: Unsupported logger option.", opt)
		return nil
	}
}

type logger interface {
	log(t *testing.T, msg string)
	close(t *testing.T)
}

type closeFileAfterEachLineLogger struct {
	path string
}

func newCloseFileAfterEachLineLogger(t *testing.T, logfile string) logger {
	return &closeFileAfterEachLineLogger{
		path: logfile,
	}
}

func (l *closeFileAfterEachLineLogger) log(t *testing.T, msg string) {
	f, err := os.OpenFile(l.path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		t.Fatalf("%v: Failed to open file for writing: %v", l.path, err.Error())
	}
	defer f.Close()
	_, err = f.WriteString(fmt.Sprintf("%v\n", msg))
	if err != nil {
		t.Fatalf("%v: Failed to write to file: %v", l.path, err.Error())
	}
	err = f.Close()
	if err != nil {
		t.Fatalf("%v: Failed to close file: %v", l.path, err.Error())
	}
}

func (l *closeFileAfterEachLineLogger) close(t *testing.T) {
	// nothing to do
}

type keepOpenLogger struct {
	file *os.File
}

func newKeepOpenLogger(t *testing.T, logfile string) logger {
	f, err := os.OpenFile(logfile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		t.Fatalf("%v: Failed to open file for writing: %v", logfile, err.Error())
	}
	return &keepOpenLogger{
		file: f,
	}
}

func (l *keepOpenLogger) log(t *testing.T, msg string) {
	_, err := l.file.WriteString(fmt.Sprintf("%v\n", msg))
	if err != nil {
		t.Fatalf("%v: Failed to write to file: %v", l.file.Name(), err.Error())
	}
	err = l.file.Sync()
	if err != nil {
		t.Fatalf("%v: Failed to flush the file: %v", l.file.Name(), err.Error())
	}
}

func (l *keepOpenLogger) close(t *testing.T) {
	err := l.file.Close()
	if err != nil {
		t.Fatalf("%v: Failed to close logfile: %v", l.file.Name(), err.Error())
	}
}

func mkTmpDirOrFail(t *testing.T) string {
	testdir, err := ioutil.TempDir("", "grok_exporter")
	if err != nil {
		t.Fatalf("Failed to create test directory: %v", err.Error())
	}
	return testdir
}

func cleanUp(t *testing.T, dir string) {
	err := os.RemoveAll(dir)
	if err != nil {
		t.Errorf("%v: Failed to remove the test directory after running the tests: %v", dir, err.Error())
	}
}

func mkTmpFileOrFail(t *testing.T, dir string) string {
	if len(ls(t, dir)) != 0 {
		t.Fatalf("%v: Directory not empty.", dir)
	}
	logfile, err := ioutil.TempFile(dir, "grok_exporter_test_log")
	if err != nil {
		t.Fatalf("Cannot create temp file: %v", err.Error())
	}
	err = logfile.Close()
	if err != nil {
		t.Fatalf("Failed to close temp file: %v", err.Error())
	}
	if len(ls(t, dir)) != 1 {
		t.Fatalf("%v: Expected exactly one file in directory.", dir)
	}
	return logfile.Name()
}

func containsFile(files []os.FileInfo, filename string) bool {
	for _, f := range files {
		if filepath.Base(f.Name()) == filepath.Base(filename) {
			return true
		}
	}
	return false
}

func ls(t *testing.T, path string) []os.FileInfo {
	result, err := ioutil.ReadDir(path)
	if err != nil {
		t.Fatalf("%v: Failed to list directory: %v", path, err.Error())
	}
	return result
}

func rotate(t *testing.T, logfile string, opt logrotateOption, mvOpt logrotateMoveOption) {
	dir := filepath.Dir(logfile)
	filename := filepath.Base(logfile)
	filesBefore := ls(t, dir)
	if !containsFile(filesBefore, filename) {
		t.Fatalf("%v does not contain %v before logrotate.", dir, filename)
	}
	switch {
	case opt == nocreate:
		moveOrFail(t, mvOpt, logfile)
	case opt == create:
		moveOrFail(t, mvOpt, logfile)
		createOrFail(t, logfile)
	case opt == copytruncate:
		if mvOpt != cp {
			t.Fatalf("Rotating with '%v' does not make sense when moving the logfile with '%v'", opt, mvOpt)
		}
		cpOrFail(t, logfile, fmt.Sprintf("%v.1", logfile))
		truncateOrFail(t, logfile)
	case opt == copy:
		if mvOpt != cp {
			t.Fatalf("Rotating with '%v' does not make sense when moving the logfile with '%v'", opt, mvOpt)
		}
		cpOrFail(t, logfile, fmt.Sprintf("%v.1", logfile))
	default:
		t.Fatalf("Unknown logrotate option.")
	}
}

func moveOrFail(t *testing.T, mvOpt logrotateMoveOption, logfile string) {
	dir := filepath.Dir(logfile)
	filename := filepath.Base(logfile)
	switch {
	case mvOpt == mv:
		mvOrFail(t, logfile, fmt.Sprintf("%v.1", logfile))
	case mvOpt == cp:
		cpOrFail(t, logfile, fmt.Sprintf("%v.1", logfile))
		rmOrFail(t, logfile)
	case mvOpt == rm:
		rmOrFail(t, logfile)
	}
	filesAfterRename := ls(t, dir)
	if containsFile(filesAfterRename, filename) {
		t.Fatalf("%v still contains file %v after mv.", dir, filename)
	}
}

func mvOrFail(t *testing.T, fromPath string, toPath string) {
	err := os.Rename(fromPath, toPath)
	if err != nil {
		t.Fatalf("%v: Failed to mv file: %v", fromPath, err.Error())
	}
}

func cpOrFail(t *testing.T, fromPath string, toPath string) {
	data, err := ioutil.ReadFile(fromPath)
	if err != nil {
		t.Fatalf("%v: Copy failed, cannot read file: %v", fromPath, err.Error())
	}
	err = ioutil.WriteFile(toPath, data, 0644)
	if err != nil {
		t.Fatalf("%v: Copy failed, cannot write file: %v", toPath, err.Error())
	}
}

func rmOrFail(t *testing.T, path string) {
	err := os.Remove(path)
	if err != nil {
		t.Fatalf("%v: Remove failed: %v", path, err.Error())
	}
}

func createOrFail(t *testing.T, logfile string) {
	dir := filepath.Dir(logfile)
	filename := filepath.Base(logfile)
	filesBeforeCreate := ls(t, dir)
	if containsFile(filesBeforeCreate, filename) {
		t.Fatalf("%v contains file %v before create.", dir, filename)
	}
	f, err := os.Create(logfile)
	if err != nil {
		t.Fatalf("Failed to re-create %v while simulating logrotate: %v", filename, err.Error())
	}
	err = f.Close()
	if err != nil {
		t.Fatalf("%v: Failed to close file: %v", filename, err.Error())
	}
	filesAfterCreate := ls(t, dir)
	if !containsFile(filesAfterCreate, filename) {
		t.Fatalf("%v does not contain %v after create.", dir, filename)
	}
}

func truncateOrFail(t *testing.T, logfile string) {
	err := os.Truncate(logfile, 0)
	if err != nil {
		t.Fatalf("%v: Error truncating the file: %v", logfile, err.Error())
	}
}

func expect(c chan string, line string, timeout time.Duration, t *testing.T) {
	timeoutChan := make(chan bool, 1)
	go func() {
		time.Sleep(timeout)
		timeoutChan <- true
		close(timeoutChan)
	}()
	lineFound := false
	select {
	case result := <-c:
		if result != line {
			t.Errorf("Expected '%v', but got '%v'.", line, result)
		} else {
			fmt.Printf("Read expected line '%v'\n", line)
			lineFound = true
		}
	case b := <-timeoutChan:
		if !b {
			fmt.Printf("WAT??? READ %v FROM timeoutChan!!!\n", b)
		}
		if lineFound {
			fmt.Printf("WAT??? READ FROM timeoutChan ALTHOUGH LINE FOUND!!!\n")
		}
		t.Errorf("Timeout while waiting for line '%v'", line)
	}
}
<|endoftext|>


<commit_before>/**
* Author: CZ cz.theng@gmail.com
 */

package log
<commit_msg>Cover Rate for CI<commit_after>/**
* Author: CZ cz.theng@gmail.com
 */

package log

import (
	"fmt"
	"testing"
)

func TestLog(t *testing.T) {
	l, err := NewFileLogger(".ossapilog", "ossapi")
	if err != nil {
		fmt.Errorf("Create Logger Error\n")
		return
	}
	l.SetMaxFileSize(1024 * 1024 * 100) //100MB
	l.SetLevel(LDEBUG)
	l.Error("Error")
	l.Debug("Debug:int a is %d", 10)
	l.Fatal("fatal")
	l.Info("Info")
	l.SetCallDepth(2)
	l.Warning("Warning")
}
<|endoftext|>


<commit_before>package dbo

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"

	"github.com/byuoitav/authmiddleware/bearertoken"
	"github.com/byuoitav/configuration-database-microservice/accessors"
)

// GetData will run a get on the url, and attempt to fill the interface provided from the returned JSON.
func GetData(url string, structToFill interface{}) error {
	log.Printf("Getting data from URL: %s...", url)
	// Make an HTTP client so we can add custom headers (currently used for adding in the Bearer token for inter-microservice communication)

	client := &http.Client{}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}

	err = setToken(req)
	if err != nil {
		return err
	}

	if req == nil {
		fmt.Printf("Alert! req is nil!")
	}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}

	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	if resp.StatusCode != http.StatusOK {
		errorString, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return err
		}
		return errors.New(string(errorString))
	}

	err = json.Unmarshal(b, structToFill)
	if err != nil {
		return err
	}
	log.Printf("Done.")
	return nil
}

//PostData hits POST endpoints
func PostData(url string, structToAdd interface{}, structToFill interface{}) error {
	log.Printf("Posting data to URL: %s...", url)

	body, err := json.Marshal(structToAdd)
	if err != nil {
		return err
	}
	client := &http.Client{}
	req, _ := http.NewRequest("POST", url, bytes.NewBuffer(body))

	req.Header.Set("Content-Type", "application/json")

	err = setToken(req)
	if err != nil {
		return err
	}

	response, err := client.Do(req)
	if err != nil {
		return err
	}
	if response.StatusCode != http.StatusOK {
		errorString, err := ioutil.ReadAll(response.Body)
		if err != nil {
			return err
		}
		return errors.New(string(errorString))
	}

	jsonArray, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return err
	}

	err = json.Unmarshal(jsonArray, structToFill)
	if err != nil {
		return err
	}

	return nil
}

func setToken(request *http.Request) error {
	fmt.Printf("Calling setToken on %v", request)

	if len(os.Getenv("LOCAL_ENVIRONMENT")) == 0 {

		log.Printf("Adding the bearer token for inter-service communication")

		token, err := bearertoken.GetToken()
		if err != nil {
			return err
		}

		request.Header.Set("Authorization", "Bearer "+token.Token)

	}

	return nil
}

// GetAllRawCommands retrieves all the commands
func GetAllRawCommands() (commands []accessors.RawCommand, err error) {
	log.Printf("Getting all commands.")
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/devices/commands"
	err = GetData(url, &commands)

	if err != nil {
		log.Printf("Error: %s", err.Error())
		return
	}

	log.Printf("Done.")
	return
}

// GetRoomByInfo simply retrieves a device's information from the databse.
func GetRoomByInfo(buildingName string, roomName string) (toReturn accessors.Room, err error) {
	log.Printf("Getting room %s in building %s...", roomName, buildingName)
	err = GetData(os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS")+"/buildings/"+buildingName+"/rooms/"+roomName, &toReturn)
	return
}

// GetDeviceByName simply retrieves a device's information from the databse.
func GetDeviceByName(buildingName string, roomName string, deviceName string) (toReturn accessors.Device, err error) {
	err = GetData(os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS")+"/buildings/"+buildingName+"/rooms/"+roomName+"/devices/"+deviceName, &toReturn)
	return
}

// GetDevicesByRoom will jut get the devices based on the room.
func GetDevicesByRoom(buildingName string, roomName string) (toReturn []accessors.Device, err error) {
	err = GetData(os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS")+"/buildings/"+buildingName+"/rooms/"+roomName+"/devices", &toReturn)
	return
}

// GetDevicesByBuildingAndRoomAndRole will get the devices with the given role from the DB
func GetDevicesByBuildingAndRoomAndRole(building string, room string, roleName string) (toReturn []accessors.Device, err error) {
	err = GetData(os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS")+"/buildings/"+building+"/rooms/"+room+"/devices/roles/"+roleName, &toReturn)
	return
}

// SetAudioInDB will set the audio levels in the database
func SetAudioInDB(building string, room string, device accessors.Device) error {
	log.Printf("Updating audio levels in DB.")

	if device.Volume != nil {
		url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/buildings/" + building + "/rooms/" + room + "/devices/" + device.Name + "/attributes/volume/" + strconv.Itoa(*device.Volume)
		request, err := http.NewRequest("PUT", url, nil)
		client := &http.Client{}
		_, err = client.Do(request)

		if err != nil {
			return err
		}
	}

	if device.Muted != nil {
		url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/buildings/" + building + "/rooms/" + room + "/devices/" + device.Name + "/attributes/muted/" + strconv.FormatBool(*device.Muted)
		request, err := http.NewRequest("PUT", url, nil)
		client := &http.Client{}
		_, err = client.Do(request)

		if err != nil {
			return err
		}
	}

	return nil
}

// GetBuildings will return all buildings
func GetBuildings() ([]accessors.Building, error) {
	log.Printf("getting all buildings...")
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/buildings"
	log.Printf(url)
	var buildings []accessors.Building
	err := GetData(url, &buildings)

	return buildings, err
}

// GetRooms returns all the rooms in a given building
func GetRoomsByBuilding(building string) ([]accessors.Room, error) {

	log.Printf("dbo.GetRoomsByBuilding called")

	log.Printf("getting all rooms from %v ...", building)
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/buildings/" + building + "/rooms"
	var rooms []accessors.Room
	err := GetData(url, &rooms)
	return rooms, err
}

// GetBuildingByShortname returns a building with a given shortname
func GetBuildingByShortname(building string) (accessors.Building, error) {
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/buildings/shortname/" + building
	var output accessors.Building
	err := GetData(url, &output)
	if err != nil {
		return output, err
	}
	return output, nil
}

// AddBuilding
func AddBuilding(buildingToAdd accessors.Building) (accessors.Building, error) {
	log.Printf("adding building %v to database", buildingToAdd.Shortname)
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/buildings/" + buildingToAdd.Shortname

	var buildingToFill accessors.Building
	err := PostData(url, buildingToAdd, &buildingToFill)
	if err != nil {
		return accessors.Building{}, err
	}

	return buildingToFill, nil
}

func AddRoom(building string, roomToAdd accessors.Room) (accessors.Room, error) {
	log.Printf("adding room %v to building %v in database", roomToAdd.Name, building)
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/buildings/" + building + "/rooms/" + roomToAdd.Name

	var roomToFill accessors.Room
	err := PostData(url, roomToAdd, &roomToFill)
	if err != nil {
		return accessors.Room{}, err
	}

	return roomToFill, nil
}

func GetDeviceTypes() ([]accessors.DeviceType, error) {

	log.Printf("getting all device types")
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/devices/types/"

	var DeviceTypes []accessors.DeviceType
	err := GetData(url, &DeviceTypes)
	if err != nil {
		return []accessors.DeviceType{}, err
	}

	return DeviceTypes, nil
}

func AddDeviceType(toAdd accessors.DeviceType) (accessors.DeviceType, error) {

	log.Printf("adding device type: %v to database")
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/devices/types/" + toAdd.Name

	var toFill accessors.DeviceType
	err := PostData(url, toAdd, &toFill)
	if err != nil {
		return accessors.DeviceType{}, err
	}

	return toFill, nil
}
func GetPowerStates() ([]accessors.PowerState, error) {

	log.Printf("getting all power states")
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/devices/powerstates/"

	var powerStates []accessors.PowerState
	err := GetData(url, &powerStates)
	if err != nil {
		return []accessors.PowerState{}, err
	}

	return powerStates, nil
}

func AddPowerState(toAdd accessors.PowerState) (accessors.PowerState, error) {

	log.Printf("adding power state: %v to database")
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/devices/powerstates/" + toAdd.Name

	var toFill accessors.PowerState
	err := PostData(url, toAdd, &toFill)
	if err != nil {
		return accessors.PowerState{}, err
	}

	return toFill, nil
}

func GetMicroservices() ([]accessors.Microservice, error) {

	log.Printf("getting all microservices")
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/devices/microservices"

	var microservices []accessors.Microservice
	err := GetData(url, &microservices)
	if err != nil {
		return []accessors.Microservice{}, err
	}

	return microservices, nil
}

func AddMicroservice(toAdd accessors.Microservice) (accessors.Microservice, error) {

	log.Printf("adding microservice: %v to database")
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/devices/microservices" + toAdd.Name

	var toFill accessors.Microservice
	err := PostData(url, toAdd, &toFill)
	if err != nil {
		return accessors.Microservice{}, err
	}

	return toFill, nil
}

func GetEndpoints() ([]accessors.Endpoint, error) {

	log.Printf("getting all endpoints")
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/devices/endpoints"

	var endpoints []accessors.Endpoint
	err := GetData(url, &endpoints)
	if err != nil {
		return []accessors.Endpoint{}, err
	}

	return endpoints, nil
}

func AddEndpoint(toAdd accessors.Endpoint) (accessors.Endpoint, error) {

	log.Printf("adding endpoint: %v to database", toAdd.Name)
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/devices/endpoints/" + toAdd.Name

	var toFill accessors.Endpoint
	err := PostData(url, toAdd, &toFill)
	if err != nil {
		return accessors.Endpoint{}, err
	}

	return toFill, nil
}

func GetPorts() ([]accessors.PortType, error) {

	log.Printf("getting all ports")
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/devices/ports"

	var ports []accessors.PortType
	err := GetData(url, &ports)
	if err != nil {
		return []accessors.PortType{}, err
	}

	return ports, nil
}

func AddPort(portToAdd accessors.PortType) (accessors.PortType, error) {

	log.Printf("adding Port: %v to database", portToAdd.Name)
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/devices/ports/" + portToAdd.Name

	var portToFill accessors.PortType
	err := PostData(url, portToAdd, &portToFill)
	if err != nil {
		return accessors.PortType{}, err
	}

	return portToFill, nil
}

func GetDeviceRoleDefinitions() ([]accessors.DeviceRoleDef, error) {

	log.Printf("getting device role definitions")
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/devices/roledefinitions"

	var definitions []accessors.DeviceRoleDef
	err := GetData(url, definitions)
	if err != nil {
		return []accessors.DeviceRoleDef{}, err
	}

	return definitions, nil
}

func AddRoleDefinition(toAdd accessors.DeviceRoleDef) (accessors.DeviceRoleDef, error) {

	log.Printf("adding role definition: %v to database", toAdd.Name)
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/devices/roledefinitions/" + toAdd.Name

	var toFill accessors.DeviceRoleDef
	err := PostData(url, toAdd, &toFill)
	if err != nil {
		return accessors.DeviceRoleDef{}, err
	}

	return toFill, nil
}
<commit_msg>fixed missing trailing /, added correct debug statements<commit_after>package dbo

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"

	"github.com/byuoitav/authmiddleware/bearertoken"
	"github.com/byuoitav/configuration-database-microservice/accessors"
)

// GetData will run a get on the url, and attempt to fill the interface provided from the returned JSON.
func GetData(url string, structToFill interface{}) error {
	log.Printf("Getting data from URL: %s...", url)
	// Make an HTTP client so we can add custom headers (currently used for adding in the Bearer token for inter-microservice communication)

	client := &http.Client{}
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return err
	}

	err = setToken(req)
	if err != nil {
		return err
	}

	if req == nil {
		fmt.Printf("Alert! req is nil!")
	}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}

	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	if resp.StatusCode != http.StatusOK {
		errorString, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return err
		}
		return errors.New(string(errorString))
	}

	err = json.Unmarshal(b, structToFill)
	if err != nil {
		return err
	}
	log.Printf("Done.")
	return nil
}

//PostData hits POST endpoints
func PostData(url string, structToAdd interface{}, structToFill interface{}) error {
	log.Printf("Posting data to URL: %s...", url)

	body, err := json.Marshal(structToAdd)
	if err != nil {
		return err
	}
	client := &http.Client{}
	req, _ := http.NewRequest("POST", url, bytes.NewBuffer(body))

	req.Header.Set("Content-Type", "application/json")

	err = setToken(req)
	if err != nil {
		return err
	}

	response, err := client.Do(req)
	if err != nil {
		return err
	}
	if response.StatusCode != http.StatusOK {
		errorString, err := ioutil.ReadAll(response.Body)
		if err != nil {
			return err
		}
		return errors.New(string(errorString))
	}

	jsonArray, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return err
	}

	err = json.Unmarshal(jsonArray, structToFill)
	if err != nil {
		return err
	}

	return nil
}

func setToken(request *http.Request) error {
	fmt.Printf("Calling setToken on %v", request)

	if len(os.Getenv("LOCAL_ENVIRONMENT")) == 0 {

		log.Printf("Adding the bearer token for inter-service communication")

		token, err := bearertoken.GetToken()
		if err != nil {
			return err
		}

		request.Header.Set("Authorization", "Bearer "+token.Token)

	}

	return nil
}

// GetAllRawCommands retrieves all the commands
func GetAllRawCommands() (commands []accessors.RawCommand, err error) {
	log.Printf("Getting all commands.")
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/devices/commands"
	err = GetData(url, &commands)

	if err != nil {
		log.Printf("Error: %s", err.Error())
		return
	}

	log.Printf("Done.")
	return
}

// GetRoomByInfo simply retrieves a device's information from the databse.
func GetRoomByInfo(buildingName string, roomName string) (toReturn accessors.Room, err error) {
	log.Printf("Getting room %s in building %s...", roomName, buildingName)
	err = GetData(os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS")+"/buildings/"+buildingName+"/rooms/"+roomName, &toReturn)
	return
}

// GetDeviceByName simply retrieves a device's information from the databse.
func GetDeviceByName(buildingName string, roomName string, deviceName string) (toReturn accessors.Device, err error) {
	err = GetData(os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS")+"/buildings/"+buildingName+"/rooms/"+roomName+"/devices/"+deviceName, &toReturn)
	return
}

// GetDevicesByRoom will jut get the devices based on the room.
func GetDevicesByRoom(buildingName string, roomName string) (toReturn []accessors.Device, err error) {
	err = GetData(os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS")+"/buildings/"+buildingName+"/rooms/"+roomName+"/devices", &toReturn)
	return
}

// GetDevicesByBuildingAndRoomAndRole will get the devices with the given role from the DB
func GetDevicesByBuildingAndRoomAndRole(building string, room string, roleName string) (toReturn []accessors.Device, err error) {
	err = GetData(os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS")+"/buildings/"+building+"/rooms/"+room+"/devices/roles/"+roleName, &toReturn)
	return
}

// SetAudioInDB will set the audio levels in the database
func SetAudioInDB(building string, room string, device accessors.Device) error {
	log.Printf("Updating audio levels in DB.")

	if device.Volume != nil {
		url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/buildings/" + building + "/rooms/" + room + "/devices/" + device.Name + "/attributes/volume/" + strconv.Itoa(*device.Volume)
		request, err := http.NewRequest("PUT", url, nil)
		client := &http.Client{}
		_, err = client.Do(request)

		if err != nil {
			return err
		}
	}

	if device.Muted != nil {
		url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/buildings/" + building + "/rooms/" + room + "/devices/" + device.Name + "/attributes/muted/" + strconv.FormatBool(*device.Muted)
		request, err := http.NewRequest("PUT", url, nil)
		client := &http.Client{}
		_, err = client.Do(request)

		if err != nil {
			return err
		}
	}

	return nil
}

// GetBuildings will return all buildings
func GetBuildings() ([]accessors.Building, error) {
	log.Printf("getting all buildings...")
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/buildings"
	log.Printf(url)
	var buildings []accessors.Building
	err := GetData(url, &buildings)

	return buildings, err
}

// GetRooms returns all the rooms in a given building
func GetRoomsByBuilding(building string) ([]accessors.Room, error) {

	log.Printf("dbo.GetRoomsByBuilding called")

	log.Printf("getting all rooms from %v ...", building)
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/buildings/" + building + "/rooms"
	var rooms []accessors.Room
	err := GetData(url, &rooms)
	return rooms, err
}

// GetBuildingByShortname returns a building with a given shortname
func GetBuildingByShortname(building string) (accessors.Building, error) {
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/buildings/shortname/" + building
	var output accessors.Building
	err := GetData(url, &output)
	if err != nil {
		return output, err
	}
	return output, nil
}

// AddBuilding
func AddBuilding(buildingToAdd accessors.Building) (accessors.Building, error) {
	log.Printf("adding building %v to database", buildingToAdd.Shortname)
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/buildings/" + buildingToAdd.Shortname

	var buildingToFill accessors.Building
	err := PostData(url, buildingToAdd, &buildingToFill)
	if err != nil {
		return accessors.Building{}, err
	}

	return buildingToFill, nil
}

func AddRoom(building string, roomToAdd accessors.Room) (accessors.Room, error) {
	log.Printf("adding room %v to building %v in database", roomToAdd.Name, building)
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/buildings/" + building + "/rooms/" + roomToAdd.Name

	var roomToFill accessors.Room
	err := PostData(url, roomToAdd, &roomToFill)
	if err != nil {
		return accessors.Room{}, err
	}

	return roomToFill, nil
}

func GetDeviceTypes() ([]accessors.DeviceType, error) {

	log.Printf("getting all device types")
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/devices/types/"

	var DeviceTypes []accessors.DeviceType
	err := GetData(url, &DeviceTypes)
	if err != nil {
		return []accessors.DeviceType{}, err
	}

	return DeviceTypes, nil
}

func AddDeviceType(toAdd accessors.DeviceType) (accessors.DeviceType, error) {

	log.Printf("adding device type: %v to database", toAdd.Name)
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/devices/types/" + toAdd.Name

	var toFill accessors.DeviceType
	err := PostData(url, toAdd, &toFill)
	if err != nil {
		return accessors.DeviceType{}, err
	}

	return toFill, nil
}
func GetPowerStates() ([]accessors.PowerState, error) {

	log.Printf("getting all power states")
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/devices/powerstates/"

	var powerStates []accessors.PowerState
	err := GetData(url, &powerStates)
	if err != nil {
		return []accessors.PowerState{}, err
	}

	return powerStates, nil
}

func AddPowerState(toAdd accessors.PowerState) (accessors.PowerState, error) {

	log.Printf("adding power state: %v to database", toAdd.Name)
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/devices/powerstates/" + toAdd.Name

	var toFill accessors.PowerState
	err := PostData(url, toAdd, &toFill)
	if err != nil {
		return accessors.PowerState{}, err
	}

	return toFill, nil
}

func GetMicroservices() ([]accessors.Microservice, error) {

	log.Printf("getting all microservices")
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/devices/microservices"

	var microservices []accessors.Microservice
	err := GetData(url, &microservices)
	if err != nil {
		return []accessors.Microservice{}, err
	}

	return microservices, nil
}

func AddMicroservice(toAdd accessors.Microservice) (accessors.Microservice, error) {

	log.Printf("adding microservice: %v to database", toAdd.Name)
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/devices/microservices/" + toAdd.Name

	var toFill accessors.Microservice
	err := PostData(url, toAdd, &toFill)
	if err != nil {
		return accessors.Microservice{}, err
	}

	return toFill, nil
}

func GetEndpoints() ([]accessors.Endpoint, error) {

	log.Printf("getting all endpoints")
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/devices/endpoints"

	var endpoints []accessors.Endpoint
	err := GetData(url, &endpoints)
	if err != nil {
		return []accessors.Endpoint{}, err
	}

	return endpoints, nil
}

func AddEndpoint(toAdd accessors.Endpoint) (accessors.Endpoint, error) {

	log.Printf("adding endpoint: %v to database", toAdd.Name)
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/devices/endpoints/" + toAdd.Name

	var toFill accessors.Endpoint
	err := PostData(url, toAdd, &toFill)
	if err != nil {
		return accessors.Endpoint{}, err
	}

	return toFill, nil
}

func GetPorts() ([]accessors.PortType, error) {

	log.Printf("getting all ports")
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/devices/ports"

	var ports []accessors.PortType
	err := GetData(url, &ports)
	if err != nil {
		return []accessors.PortType{}, err
	}

	return ports, nil
}

func AddPort(portToAdd accessors.PortType) (accessors.PortType, error) {

	log.Printf("adding Port: %v to database", portToAdd.Name)
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/devices/ports/" + portToAdd.Name

	var portToFill accessors.PortType
	err := PostData(url, portToAdd, &portToFill)
	if err != nil {
		return accessors.PortType{}, err
	}

	return portToFill, nil
}

func GetDeviceRoleDefinitions() ([]accessors.DeviceRoleDef, error) {

	log.Printf("getting device role definitions")
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/devices/roledefinitions"

	var definitions []accessors.DeviceRoleDef
	err := GetData(url, definitions)
	if err != nil {
		return []accessors.DeviceRoleDef{}, err
	}

	return definitions, nil
}

func AddRoleDefinition(toAdd accessors.DeviceRoleDef) (accessors.DeviceRoleDef, error) {

	log.Printf("adding role definition: %v to database", toAdd.Name)
	url := os.Getenv("CONFIGURATION_DATABASE_MICROSERVICE_ADDRESS") + "/devices/roledefinitions/" + toAdd.Name

	var toFill accessors.DeviceRoleDef
	err := PostData(url, toAdd, &toFill)
	if err != nil {
		return accessors.DeviceRoleDef{}, err
	}

	return toFill, nil
}
<|endoftext|>


<commit_before>package goshare

import (
  "time"

  "github.com/jmhodges/levigo"

  abkleveldb "github.com/abhishekkr/levigoNS/leveldb"
  levigoNS "github.com/abhishekkr/levigoNS"
  levigoTSDS "github.com/abhishekkr/levigoTSDS"
)

type GetValFunc func(key string) string
type PushKeyValFunc func(key string, val string) bool
type DelKeyFunc func(key string) bool

func GetVal(key string) string{
  return abkleveldb.GetVal(key, db)
}

func PushKeyVal(key string, val string) bool{
  return abkleveldb.PushKeyVal(key, val, db)
}

func DelKey(key string) bool{
  return abkleveldb.DelKey(key, db)
}

func hmap_to_csv(hmap){
  csv = ""
  for  key, value := range hmap {
    csv += key + "," + value + "\n"
  }
  return csv
}

func GetValNS(key string) string{
  return hmap_to_csv(levigoNS.ReadNS(key, db))
}

func PushKeyValNS(key string, val string) bool{
  return levigoNS.PushNS(key, val, db)
}

func DelKeyNS(key string) bool{
  levigoNS.DeleteNSRecursive(key, db)
  return true
}

func GetValTSDS(key string) string{
  return hmap_to_csv(levigoTSDS.ReadTSDS(key, db))
}

func PushKeyValTSDS(key string, val string) bool{
  return levigoTSDS.PushTSDS(key, val, time.Now(), db)
}

func PushKeyValNowTSDS(key string, val string) bool{
  return levigoTSDS.PushNowTSDS(key, val, db)
}

func DelKeyTSDS(key string) bool{
  levigoNS.DeleteTSDS(key, db)
  return true
}

func GetValTask(task_type string) GetValFunc {
  if task_type == "tsds" {
    return GetValTSDS
  } else if task_type == "ns" {
    return GetValNS
  }
  return GetVal
}

func PushKeyValTask(task_type string) PushKeyValFunc {
  if task_type == "tsds-now" {
    return PushKeyValNowTSDS
  } else if task_type == "tsds" {
    return PushKeyValTSDS
  } else if task_type == "ns" {
    return PushKeyValNS
  }
  return PushKeyVal
}

func DelKeyTask(task_type string) DelKeyFunc {
  if task_type == "tsds" {
    return DelKeyTSDS
  } else if task_type == "ns" {
    return DelKeyNS
  }
  return DelKey
}
<commit_msg>dbtasks removing extra libs and adding missing ref<commit_after>package goshare

import (
  "time"

  abkleveldb "github.com/abhishekkr/levigoNS/leveldb"
  levigoNS "github.com/abhishekkr/levigoNS"
  levigoTSDS "github.com/abhishekkr/levigoTSDS"
)

type GetValFunc func(key string) string
type PushKeyValFunc func(key string, val string) bool
type DelKeyFunc func(key string) bool

func GetVal(key string) string{
  return abkleveldb.GetVal(key, db)
}

func PushKeyVal(key string, val string) bool{
  return abkleveldb.PushKeyVal(key, val, db)
}

func DelKey(key string) bool{
  return abkleveldb.DelKey(key, db)
}

func hmap_to_csv(hmap levigoNS.HashMap){
  csv := ""
  for  key, value := range hmap {
    csv += key + "," + value + "\n"
  }
  return csv
}

func GetValNS(key string) string{
  return hmap_to_csv(levigoNS.ReadNS(key, db))
}

func PushKeyValNS(key string, val string) bool{
  return levigoNS.PushNS(key, val, db)
}

func DelKeyNS(key string) bool{
  levigoNS.DeleteNSRecursive(key, db)
  return true
}

func GetValTSDS(key string) string{
  return hmap_to_csv(levigoTSDS.ReadTSDS(key, db))
}

func PushKeyValTSDS(key string, val string) bool{
  return levigoTSDS.PushTSDS(key, val, time.Now(), db)
}

func PushKeyValNowTSDS(key string, val string) bool{
  return levigoTSDS.PushNowTSDS(key, val, db)
}

func DelKeyTSDS(key string) bool{
  levigoNS.DeleteTSDS(key, db)
  return true
}

func GetValTask(task_type string) GetValFunc {
  if task_type == "tsds" {
    return GetValTSDS
  } else if task_type == "ns" {
    return GetValNS
  }
  return GetVal
}

func PushKeyValTask(task_type string) PushKeyValFunc {
  if task_type == "tsds-now" {
    return PushKeyValNowTSDS
  } else if task_type == "tsds" {
    return PushKeyValTSDS
  } else if task_type == "ns" {
    return PushKeyValNS
  }
  return PushKeyVal
}

func DelKeyTask(task_type string) DelKeyFunc {
  if task_type == "tsds" {
    return DelKeyTSDS
  } else if task_type == "ns" {
    return DelKeyNS
  }
  return DelKey
}
<|endoftext|>


<commit_before>package pg

import (
	"encoding/hex"
	"fmt"
	"strconv"
	"time"
)

const (
	dateFormat     = "2006-01-02"
	timeFormat     = "15:04:05.999999999"
	datetimeFormat = "2006-01-02 15:04:05.999999999"
)

func decode(dst interface{}, f []byte) error {
	// NULL.
	if f == nil {
		return nil
	}

	switch v := dst.(type) {
	case *bool:
		if string(f) == "t" {
			*v = true
		}
		return nil
	case *int8:
		n, err := strconv.ParseInt(string(f), 10, 8)
		if err != nil {
			return err
		}
		*v = int8(n)
		return nil
	case *int16:
		n, err := strconv.ParseInt(string(f), 10, 16)
		if err != nil {
			return err
		}
		*v = int16(n)
		return nil
	case *int32:
		n, err := strconv.ParseInt(string(f), 10, 32)
		if err != nil {
			return err
		}
		*v = int32(n)
		return nil
	case *int64:
		n, err := strconv.ParseInt(string(f), 10, 64)
		if err != nil {
			return err
		}
		*v = int64(n)
		return nil
	case *int:
		n, err := strconv.ParseInt(string(f), 10, 64)
		if err != nil {
			return err
		}
		*v = int(n)
		return nil
	case *uint8:
		n, err := strconv.ParseInt(string(f), 10, 8)
		if err != nil {
			return err
		}
		*v = uint8(n)
		return nil
	case *uint16:
		n, err := strconv.ParseInt(string(f), 10, 16)
		if err != nil {
			return err
		}
		*v = uint16(n)
		return nil
	case *uint32:
		n, err := strconv.ParseInt(string(f), 10, 32)
		if err != nil {
			return err
		}
		*v = uint32(n)
		return nil
	case *uint64:
		n, err := strconv.ParseInt(string(f), 10, 64)
		if err != nil {
			return err
		}
		*v = uint64(n)
		return nil
	case *uint:
		n, err := strconv.ParseInt(string(f), 10, 64)
		if err != nil {
			return err
		}
		*v = uint(n)
		return nil
	case *string:
		*v = string(f)
		return nil
	case *[]byte:
		f = f[2:] // Trim off "\\x".
		d := make([]byte, hex.DecodedLen(len(f)))
		_, err := hex.Decode(d, f)
		if err != nil {
			return err
		}
		*v = d
		return nil
	case *time.Time:
		var format string
		switch l := len(f); {
		case l <= len(dateFormat):
			format = dateFormat
		case l <= len(timeFormat):
			format = timeFormat
		default:
			format = datetimeFormat
		}

		tm, err := time.Parse(format, string(f))
		if err != nil {
			return err
		}
		*v = tm.UTC()

		return nil
	case *[]string:
		p := newArrayParser(f[1 : len(f)-1])
		vv := make([]string, 0)
		for p.Valid() {
			elem := p.NextElem()
			if elem == nil {
				return fmt.Errorf("pg: unexpected NULL: %q", f)
			}
			vv = append(vv, string(elem))
		}
		*v = vv
		return p.Err()
	case *[]int:
		p := newArrayParser(f[1 : len(f)-1])
		vv := make([]int, 0)
		for p.Valid() {
			elem := p.NextElem()
			if elem == nil {
				return fmt.Errorf("pg: unexpected NULL: %q", f)
			}
			n, err := strconv.ParseInt(string(elem), 10, 64)
			if err != nil {
				return err
			}
			vv = append(vv, int(n))
		}
		*v = vv
		return p.Err()
	case *[]int64:
		p := newArrayParser(f[1 : len(f)-1])
		vv := make([]int64, 0)
		for p.Valid() {
			elem := p.NextElem()
			if elem == nil {
				return fmt.Errorf("pg: unexpected NULL: %q", f)
			}
			n, err := strconv.ParseInt(string(elem), 10, 64)
			if err != nil {
				return err
			}
			vv = append(vv, n)
		}
		*v = vv
		return p.Err()
	case *map[string]string:
		p := newHstoreParser(f)
		vv := make(map[string]string)
		for p.Valid() {
			key := p.NextKey()
			if key == nil {
				return fmt.Errorf("pg: unexpected NULL: %q", f)
			}
			value := p.NextValue()
			if value == nil {
				return fmt.Errorf("pg: unexpected NULL: %q", f)
			}
			vv[string(key)] = string(value)
		}
		*v = vv
		return nil
	}
	return fmt.Errorf("pg: unsupported destination type: %T", dst)
}
<commit_msg>Export decode.<commit_after>package pg

import (
	"encoding/hex"
	"fmt"
	"strconv"
	"time"
)

const (
	dateFormat     = "2006-01-02"
	timeFormat     = "15:04:05.999999999"
	datetimeFormat = "2006-01-02 15:04:05.999999999"
)

func Decode(dst interface{}, f []byte) error {
	// NULL.
	if f == nil {
		return nil
	}

	switch v := dst.(type) {
	case *bool:
		if string(f) == "t" {
			*v = true
		}
		return nil
	case *int8:
		n, err := strconv.ParseInt(string(f), 10, 8)
		if err != nil {
			return err
		}
		*v = int8(n)
		return nil
	case *int16:
		n, err := strconv.ParseInt(string(f), 10, 16)
		if err != nil {
			return err
		}
		*v = int16(n)
		return nil
	case *int32:
		n, err := strconv.ParseInt(string(f), 10, 32)
		if err != nil {
			return err
		}
		*v = int32(n)
		return nil
	case *int64:
		n, err := strconv.ParseInt(string(f), 10, 64)
		if err != nil {
			return err
		}
		*v = int64(n)
		return nil
	case *int:
		n, err := strconv.ParseInt(string(f), 10, 64)
		if err != nil {
			return err
		}
		*v = int(n)
		return nil
	case *uint8:
		n, err := strconv.ParseInt(string(f), 10, 8)
		if err != nil {
			return err
		}
		*v = uint8(n)
		return nil
	case *uint16:
		n, err := strconv.ParseInt(string(f), 10, 16)
		if err != nil {
			return err
		}
		*v = uint16(n)
		return nil
	case *uint32:
		n, err := strconv.ParseInt(string(f), 10, 32)
		if err != nil {
			return err
		}
		*v = uint32(n)
		return nil
	case *uint64:
		n, err := strconv.ParseInt(string(f), 10, 64)
		if err != nil {
			return err
		}
		*v = uint64(n)
		return nil
	case *uint:
		n, err := strconv.ParseInt(string(f), 10, 64)
		if err != nil {
			return err
		}
		*v = uint(n)
		return nil
	case *string:
		*v = string(f)
		return nil
	case *[]byte:
		f = f[2:] // Trim off "\\x".
		d := make([]byte, hex.DecodedLen(len(f)))
		_, err := hex.Decode(d, f)
		if err != nil {
			return err
		}
		*v = d
		return nil
	case *time.Time:
		var format string
		switch l := len(f); {
		case l <= len(dateFormat):
			format = dateFormat
		case l <= len(timeFormat):
			format = timeFormat
		default:
			format = datetimeFormat
		}

		tm, err := time.Parse(format, string(f))
		if err != nil {
			return err
		}
		*v = tm.UTC()

		return nil
	case *[]string:
		p := newArrayParser(f[1 : len(f)-1])
		vv := make([]string, 0)
		for p.Valid() {
			elem := p.NextElem()
			if elem == nil {
				return fmt.Errorf("pg: unexpected NULL: %q", f)
			}
			vv = append(vv, string(elem))
		}
		*v = vv
		return p.Err()
	case *[]int:
		p := newArrayParser(f[1 : len(f)-1])
		vv := make([]int, 0)
		for p.Valid() {
			elem := p.NextElem()
			if elem == nil {
				return fmt.Errorf("pg: unexpected NULL: %q", f)
			}
			n, err := strconv.ParseInt(string(elem), 10, 64)
			if err != nil {
				return err
			}
			vv = append(vv, int(n))
		}
		*v = vv
		return p.Err()
	case *[]int64:
		p := newArrayParser(f[1 : len(f)-1])
		vv := make([]int64, 0)
		for p.Valid() {
			elem := p.NextElem()
			if elem == nil {
				return fmt.Errorf("pg: unexpected NULL: %q", f)
			}
			n, err := strconv.ParseInt(string(elem), 10, 64)
			if err != nil {
				return err
			}
			vv = append(vv, n)
		}
		*v = vv
		return p.Err()
	case *map[string]string:
		p := newHstoreParser(f)
		vv := make(map[string]string)
		for p.Valid() {
			key := p.NextKey()
			if key == nil {
				return fmt.Errorf("pg: unexpected NULL: %q", f)
			}
			value := p.NextValue()
			if value == nil {
				return fmt.Errorf("pg: unexpected NULL: %q", f)
			}
			vv[string(key)] = string(value)
		}
		*v = vv
		return nil
	}
	return fmt.Errorf("pg: unsupported destination type: %T", dst)
}
<|endoftext|>


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

package schema

import (
	"errors"
	"fmt"
	"reflect"
)

// NewDecoder returns a new Decoder.
func NewDecoder() *Decoder {
	return &Decoder{cache: newCache()}
}

// Decoder decodes values from a map[string][]string to a struct.
type Decoder struct {
	cache *cache
}

// RegisterConverter registers a converter function for a custom type.
func (d *Decoder) RegisterConverter(value interface{}, converterFunc Converter) {
	d.cache.conv[reflect.TypeOf(value)] = converterFunc
}

// Decode decodes a map[string][]string to a struct.
//
// The first parameter must be a pointer to a struct.
//
// The second parameter is a map, typically url.Values from an HTTP request.
// Keys are "paths" in dotted notation to the struct fields and nested structs.
//
// See the package documentation for a full explanation of the mechanics.
func (d *Decoder) Decode(dst interface{}, src map[string][]string) error {
	v := reflect.ValueOf(dst)
	if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
		return errors.New("schema: interface must be a pointer to struct")
	}
	v = v.Elem()
	t := v.Type()
	errors := MultiError{}
	for path, values := range src {
		if parts, err := d.cache.parsePath(path, t); err == nil {
			if err = d.decode(v, path, parts, values); err != nil {
				errors[path] = err
			}
		} else {
			errors[path] = fmt.Errorf("schema: invalid path %q", path)
		}
	}
	if len(errors) > 0 {
		return errors
	}
	return nil
}

// decode fills a struct field using a parsed path.
func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart,
	values []string) error {
	// Get the field walking the struct fields by index.
	for _, idx := range parts[0].path {
		if v.Type().Kind() == reflect.Ptr {
			if v.IsNil() {
				v.Set(reflect.New(v.Type().Elem()))
			}
			v = v.Elem()
		}
		v = v.Field(idx)
	}

	// Don't even bother for unexported fields.
	if !v.CanSet() {
		return nil
	}

	// Dereference if needed.
	t := v.Type()
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
		if v.IsNil() {
			v.Set(reflect.New(t))
		}
		v = v.Elem()
	}

	// Slice of structs. Let's go recursive.
	if len(parts) > 1 {
		idx := parts[0].index
		if v.IsNil() || v.Len() < idx+1 {
			value := reflect.MakeSlice(t, idx+1, idx+1)
			if v.Len() < idx+1 {
				// Resize it.
				reflect.Copy(value, v)
			}
			v.Set(value)
		}
		return d.decode(v.Index(idx), path, parts[1:], values)
	}

	// Simple case.
	if t.Kind() == reflect.Slice {
		var items []reflect.Value
		elemT := t.Elem()
		isPtrElem := elemT.Kind() == reflect.Ptr
		if isPtrElem {
			elemT = elemT.Elem()
		}
		conv := d.cache.conv[elemT]
		if conv == nil {
			return fmt.Errorf("schema: converter not found for %v", elemT)
		}
		for key, value := range values {
			if value == "" {
				// We are just ignoring empty values for now.
				continue
			} else if item := conv(value); item.IsValid() {
				if isPtrElem {
					ptr := reflect.New(elemT)
					ptr.Elem().Set(item)
					item = ptr
				}
				items = append(items, item)
			} else {
				// If a single value is invalid should we give up
				// or set a zero value?
				return ConversionError{path, key}
			}
		}
		value := reflect.Append(reflect.MakeSlice(t, 0, 0), items...)
		v.Set(value)
	} else {
		if values[0] == "" {
			// We are just ignoring empty values for now.
			return nil
		} else if conv := d.cache.conv[t]; conv != nil {
			if value := conv(values[len(values)-1]); value.IsValid() {
				v.Set(value)
			} else {
				return ConversionError{path, -1}
			}
		} else {
			return fmt.Errorf("schema: converter not found for %v", t)
		}
	}
	return nil
}

// Errors ---------------------------------------------------------------------

// ConversionError stores information about a failed conversion.
type ConversionError struct {
	Key   string // key from the source map.
	Index int    // index for multi-value fields; -1 for single-value fields.
}

func (e ConversionError) Error() string {
	if e.Index < 0 {
		return fmt.Sprintf("schema: error converting value for %q", e.Key)
	}
	return fmt.Sprintf("schema: error converting value for index %d of %q",
		e.Index, e.Key)
}

// MultiError stores multiple decoding errors.
//
// Borrowed from the App Engine SDK.
type MultiError map[string]error

func (e MultiError) Error() string {
	s := ""
	for _, err := range e {
		s = err.Error()
		break
	}
	switch len(e) {
	case 0:
		return "(0 errors)"
	case 1:
		return s
	case 2:
		return s + " (and 1 other error)"
	}
	return fmt.Sprintf("%s (and %d other errors)", s, len(e)-1)
}
<commit_msg>ignore-path<commit_after>// Copyright 2012 The Gorilla Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package schema

import (
	"errors"
	"fmt"
	"reflect"
)

// NewDecoder returns a new Decoder.
func NewDecoder() *Decoder {
	return &Decoder{cache: newCache()}
}

// Decoder decodes values from a map[string][]string to a struct.
type Decoder struct {
	cache *cache
}

// RegisterConverter registers a converter function for a custom type.
func (d *Decoder) RegisterConverter(value interface{}, converterFunc Converter) {
	d.cache.conv[reflect.TypeOf(value)] = converterFunc
}

// Decode decodes a map[string][]string to a struct.
//
// The first parameter must be a pointer to a struct.
//
// The second parameter is a map, typically url.Values from an HTTP request.
// Keys are "paths" in dotted notation to the struct fields and nested structs.
//
// See the package documentation for a full explanation of the mechanics.
func (d *Decoder) Decode(dst interface{}, src map[string][]string) error {
	v := reflect.ValueOf(dst)
	if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
		return errors.New("schema: interface must be a pointer to struct")
	}
	v = v.Elem()
	t := v.Type()
	errors := MultiError{}
	for path, values := range src {
		if parts, err := d.cache.parsePath(path, t); err == nil {
			if len(parts) == 0 {
				// ignore empty parameter
				continue
			}

			if err = d.decode(v, path, parts, values); err != nil {
				errors[path] = err
			}
		} else {
			// ignore invalid path
			continue
		}
	}
	if len(errors) > 0 {
		return errors
	}
	return nil
}

// decode fills a struct field using a parsed path.
func (d *Decoder) decode(v reflect.Value, path string, parts []pathPart,
	values []string) error {
	// Get the field walking the struct fields by index.
	for _, idx := range parts[0].path {
		if v.Type().Kind() == reflect.Ptr {
			if v.IsNil() {
				v.Set(reflect.New(v.Type().Elem()))
			}
			v = v.Elem()
		}
		v = v.Field(idx)
	}

	// Don't even bother for unexported fields.
	if !v.CanSet() {
		return nil
	}

	// Dereference if needed.
	ov := v
	t := v.Type()
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
		if v.IsNil() {
			v.Set(reflect.New(t))
		}
		v = v.Elem()
	}

	// Slice of structs. Let's go recursive.
	if len(parts) > 1 {
		idx := parts[0].index
		if v.IsNil() || v.Len() < idx+1 {
			value := reflect.MakeSlice(t, idx+1, idx+1)
			if v.Len() < idx+1 {
				// Resize it.
				reflect.Copy(value, v)
			}
			v.Set(value)
		}
		return d.decode(v.Index(idx), path, parts[1:], values)
	}

	// Simple case.
	if t.Kind() == reflect.Slice {
		var items []reflect.Value
		elemT := t.Elem()
		isPtrElem := elemT.Kind() == reflect.Ptr
		if isPtrElem {
			elemT = elemT.Elem()
		}
		conv := d.cache.conv[elemT]
		if conv == nil {
			return fmt.Errorf("schema: converter not found for %v", elemT)
		}
		for key, value := range values {
			if value == "" {
				// We are just ignoring empty values for now.
				ov.Set(reflect.Zero(ov.Type()))
			} else if item := conv(value); item.IsValid() {
				if isPtrElem {
					ptr := reflect.New(elemT)
					ptr.Elem().Set(item)
					item = ptr
				}
				items = append(items, item)
			} else {
				// If a single value is invalid should we give up
				// or set a zero value?
				return ConversionError{path, key}
			}
		}
		value := reflect.Append(reflect.MakeSlice(t, 0, 0), items...)
		v.Set(value)
	} else {
		if values[0] == "" {
			// We are just ignoring empty values for now.
			ov.Set(reflect.Zero(ov.Type()))
			//return nil
		} else if conv := d.cache.conv[t]; conv != nil {
			if value := conv(values[len(values)-1]); value.IsValid() {
				v.Set(value)
			} else {
				return ConversionError{path, -1}
			}
		} else {
			return fmt.Errorf("schema: converter not found for %v", t)
		}
	}
	return nil
}

// Errors ---------------------------------------------------------------------

// ConversionError stores information about a failed conversion.
type ConversionError struct {
	Key   string // key from the source map.
	Index int    // index for multi-value fields; -1 for single-value fields.
}

func (e ConversionError) Error() string {
	if e.Index < 0 {
		return fmt.Sprintf("schema: error converting value for %q", e.Key)
	}
	return fmt.Sprintf("schema: error converting value for index %d of %q",
		e.Index, e.Key)
}

// MultiError stores multiple decoding errors.
//
// Borrowed from the App Engine SDK.
type MultiError map[string]error

func (e MultiError) Error() string {
	s := ""
	for _, err := range e {
		s = err.Error()
		break
	}
	switch len(e) {
	case 0:
		return "(0 errors)"
	case 1:
		return s
	case 2:
		return s + " (and 1 other error)"
	}
	return fmt.Sprintf("%s (and %d other errors)", s, len(e)-1)
}
<|endoftext|>


<commit_before>package id3

import (
	"bytes"
	"encoding/binary"
	"io"
	"io/ioutil"
)

type Peeker interface {
	Peek(n int) ([]byte, error)
}

// Check reports whether r looks like it starts with an ID3 tag.
func Check(r Peeker) (bool, error) {
	b, err := r.Peek(3)
	if err != nil {
		return false, err
	}
	return bytes.Equal(b, Magic[:]), nil
}

type Decoder struct {
	r io.Reader
	h TagHeader
}

func NewDecoder(r io.Reader) *Decoder {
	return &Decoder{r: r}
}

// ParseHeader parses only the ID3 header.
func (d *Decoder) ParseHeader() (TagHeader, error) {
	header, err := d.readHeader()
	if err != nil {
		return TagHeader{}, err
	}

	d.h = header
	d.r = io.LimitReader(d.r, int64(header.size))

	return header, nil
}

func (d *Decoder) remaining() int64 {
	return d.r.(*io.LimitedReader).N
}

// Parse parses a tag.
//
// Parse will always return a valid tag. In the case of an error, the
// tag will be empty.
//
// If Parse successfully parsed a tag, the reader will be positioned
// immediately after the tag, which usually is directly before audio
// data. If there wasn't a valid tag, however, the position of the
// reader is undefined. If you're not sure if your reader starts with
// a tag at all, consider using Check first.
//
// Parse cannot be called if either ParseHeader or ParseFrame have
// been called for the current tag.
func (d *Decoder) Parse() (*Tag, error) {
	tag := NewTag()
	header, err := d.ParseHeader()
	if err != nil {
		return tag, err
	}
	tag.Header = header

	// FIXME consider moving this to ParseHeader
	if header.Flags.ExtendedHeader() {
		panic("not implemented: cannot parse extended header")
	}

	if header.Flags.Unsynchronisation() {
		panic("not implemented: cannot parse unsynchronised tag")
	}

	for {
		frame, err := d.ParseFrame()
		if err != nil {
			if err == io.EOF {
				break
			}

			return tag, err
		}
		tag.Frames[frame.ID()] = append(tag.Frames[frame.ID()], frame)
	}

	if header.Version < 0x0400 {
		tag.upgrade()
	}

	return tag, nil
}

func readBinary(r io.Reader, args ...interface{}) (err error) {
	for _, arg := range args {
		err = binary.Read(r, binary.BigEndian, arg)
		if err != nil {
			break
		}
	}

	return
}

// readHeader reads an ID3v2 header. It expects the reader to be
// seeked to the beginning of the header.
func (d *Decoder) readHeader() (header TagHeader, err error) {
	var bytes struct {
		Magic   [3]byte
		Version [2]byte
		Flags   byte
		Size    [4]byte
	}

	err = binary.Read(d.r, binary.BigEndian, &bytes)
	if err != nil {
		return header, err
	}
	if bytes.Magic != Magic {
		return TagHeader{}, notATagHeader{bytes.Magic}
	}
	version := Version(int16(bytes.Version[0])<<8 | int16(bytes.Version[1]))
	if bytes.Version[0] > 4 || bytes.Version[0] < 3 {
		return TagHeader{}, UnsupportedVersion{version}
	}

	header.Version = version
	header.Flags = HeaderFlags(bytes.Flags)
	header.size = desynchsafeInt(bytes.Size)

	return header, nil
}

// ParseFrame reads the next ID3 frame. When it reaches padding, it
// will read and discard all of it and return io.EOF. This should set
// the reader immediately before the audio data.
//
// ParseHeader must be called before calling ParseFrame.
func (d *Decoder) ParseFrame() (Frame, error) {
	if d.remaining() == 0 {
		return nil, io.EOF
	}

	var (
		headerBytes struct {
			ID    [4]byte
			Size  [4]byte
			Flags [2]byte
		}
		header FrameHeader
	)

	err := binary.Read(d.r, binary.BigEndian, &headerBytes)
	if err != nil {
		return nil, err
	}

	// We're in the padding, discard remaining bytes and return io.EOF
	if headerBytes.ID == [4]byte{0, 0, 0, 0} {
		_, err := io.Copy(ioutil.Discard, d.r)
		if err != nil {
			return nil, err
		}
		return nil, io.EOF
	}

	for _, byte := range headerBytes.ID {
		// Allow 0-9
		if byte >= 48 && byte <= 57 {
			continue
		}

		// Allow A-Z
		if byte >= 65 && byte <= 90 {
			continue
		}

		return nil, NotAFrameHeader{headerBytes}
	}

	header.id = FrameType(headerBytes.ID[:])
	header.flags = FrameFlags(int16(headerBytes.Flags[0])<<8 | int16(headerBytes.Flags[1]))
	frameSize := desynchsafeInt(headerBytes.Size)

	if header.flags.Compressed() {
		panic("not implemented: cannot read compressed frame")
		// TODO: Read decompressed size (4 bytes)
	}

	if header.flags.Encrypted() {
		panic("not implemented: cannot read encrypted frame")
		// TODO: Read encryption method (1 byte)
	}

	if header.flags.Grouped() {
		panic("not implemented: cannot read grouped frame")
		// TODO: Read group identifier (1 byte)
	}

	if header.id[0] == 'T' && header.id != "TXXX" {
		var encoding Encoding
		frame := TextInformationFrame{FrameHeader: header}
		information := make([]byte, frameSize-1)
		err := readBinary(d.r, &encoding, &information)
		if err != nil {
			return nil, err
		}

		frame.Text = string(encoding.toUTF8(information))

		return frame, nil
	}

	if header.id[0] == 'W' && header.id != "WXXX" {
		frame := URLLinkFrame{FrameHeader: header}
		url := make([]byte, frameSize)
		_, err = d.r.Read(url)
		if err != nil {
			return nil, err
		}
		frame.URL = string(iso88591.toUTF8(url))

		return frame, nil
	}

	switch header.id {
	case "TXXX":
		return d.readTXXXFrame(header, frameSize)
	case "WXXX":
		return d.readWXXXFrame(header, frameSize)
	case "UFID":
		return d.readUFIDFrame(header, frameSize)
	case "COMM":
		return d.readCOMMFrame(header, frameSize)
	case "PRIV":
		return d.readPRIVFrame(header, frameSize)
	case "APIC":
		return d.readAPICFrame(header, frameSize)
	case "MCDI":
		return d.readMCDIFrame(header, frameSize)
	case "USLT":
		return d.readUFIDFrame(header, frameSize)
	default:
		data := make([]byte, frameSize)
		n, err := d.r.Read(data)

		return UnsupportedFrame{
			FrameHeader: header,
			Data:        data[:n],
		}, err
	}
}

func (d *Decoder) readTXXXFrame(header FrameHeader, frameSize int) (Frame, error) {
	var encoding Encoding
	frame := UserTextInformationFrame{FrameHeader: header}
	rest := make([]byte, frameSize-1)

	err := readBinary(d.r, &encoding, &rest)
	if err != nil {
		return nil, err
	}
	parts := splitNullN(rest, encoding, 2)

	frame.Description = string(encoding.toUTF8(parts[0]))
	frame.Text = string(encoding.toUTF8(parts[1]))

	return frame, nil
}

func (d *Decoder) readWXXXFrame(header FrameHeader, frameSize int) (Frame, error) {
	var encoding Encoding
	frame := UserDefinedURLLinkFrame{FrameHeader: header}
	rest := make([]byte, frameSize-1)

	err := readBinary(d.r, &encoding, &rest)
	if err != nil {
		return nil, err
	}

	parts := splitNullN(rest, encoding, 2)
	frame.Description = string(encoding.toUTF8(parts[0]))
	frame.URL = string(iso88591ToUTF8(parts[1]))

	return frame, nil
}

func (d *Decoder) readUFIDFrame(header FrameHeader, frameSize int) (Frame, error) {
	frame := UniqueFileIdentifierFrame{FrameHeader: header}
	rest := make([]byte, frameSize)

	err := binary.Read(d.r, binary.BigEndian, rest)
	if err != nil {
		return nil, err
	}

	parts := bytes.SplitN(rest, []byte{0}, 2)
	frame.Owner = string(iso88591.toUTF8(parts[0]))
	frame.Identifier = parts[1]

	return frame, nil
}

func (d *Decoder) readCOMMFrame(header FrameHeader, frameSize int) (Frame, error) {
	frame := CommentFrame{FrameHeader: header}
	var (
		encoding Encoding
		language [3]byte
		rest     []byte
	)
	rest = make([]byte, frameSize-4)

	err := readBinary(d.r, &encoding, &language, &rest)
	if err != nil {
		return nil, err
	}

	parts := splitNullN(rest, encoding, 2)

	frame.Language = string(language[:])

	frame.Description = string(encoding.toUTF8(parts[0]))
	frame.Text = string(encoding.toUTF8(parts[1]))

	return frame, nil
}

func (d *Decoder) readPRIVFrame(header FrameHeader, frameSize int) (Frame, error) {
	frame := PrivateFrame{FrameHeader: header}
	data := make([]byte, frameSize)
	_, err := d.r.Read(data)
	if err != nil {
		return frame, err
	}

	parts := bytes.SplitN(data, nul, 2)
	frame.Owner = parts[0]
	frame.Data = parts[1]

	return frame, nil
}

func (d *Decoder) readAPICFrame(header FrameHeader, frameSize int) (Frame, error) {
	frame := PictureFrame{FrameHeader: header}
	var (
		encoding Encoding
		rest     []byte
	)
	rest = make([]byte, frameSize-1)
	err := readBinary(d.r, &encoding, &rest)
	if err != nil {
		return frame, err
	}

	parts1 := bytes.SplitN(rest, nul, 2)
	parts2 := splitNullN(parts1[1][1:], encoding, 2)

	frame.MIMEType = string(iso88591.toUTF8(parts1[0]))
	frame.PictureType = PictureType(parts1[1][0])
	frame.Description = string(encoding.toUTF8(parts2[0]))
	frame.Data = parts2[1]

	return frame, nil
}

func (d *Decoder) readMCDIFrame(header FrameHeader, frameSize int) (Frame, error) {
	frame := MusicCDIdentifierFrame{FrameHeader: header}
	frame.TOC = make([]byte, frameSize)
	_, err := d.r.Read(frame.TOC)
	return frame, err
}

func (d *Decoder) readUSLTFrame(header FrameHeader, frameSize int) (Frame, error) {
	frame := UnsynchronisedLyricsFrame{FrameHeader: header}
	var (
		encoding Encoding
		language [3]byte
		rest     []byte
	)
	rest = make([]byte, frameSize-4)

	err := readBinary(d.r, &encoding, &language, rest)
	if err != nil {
		return frame, err
	}

	parts := splitNullN(rest, encoding, 2)
	frame.Language = string(language[:])

	frame.Description = string(encoding.toUTF8(parts[0]))
	frame.Lyrics = string(encoding.toUTF8(parts[1]))

	return frame, nil
}
<commit_msg>convert frame type switch to map<commit_after>package id3

import (
	"bytes"
	"encoding/binary"
	"io"
	"io/ioutil"
)

type fnFrameReader func(r io.Reader, header FrameHeader, frameSize int) (Frame, error)

var frameReaders = map[FrameType]fnFrameReader{
	"TXXX": readTXXXFrame,
	"WXXX": readWXXXFrame,
	"UFID": readUFIDFrame,
	"COMM": readCOMMFrame,
	"PRIV": readPRIVFrame,
	"APIC": readAPICFrame,
	"MCDI": readMCDIFrame,
	"USLT": readUFIDFrame,
}

type Peeker interface {
	Peek(n int) ([]byte, error)
}

// Check reports whether r looks like it starts with an ID3 tag.
func Check(r Peeker) (bool, error) {
	b, err := r.Peek(3)
	if err != nil {
		return false, err
	}
	return bytes.Equal(b, Magic[:]), nil
}

type Decoder struct {
	r io.Reader
	h TagHeader
}

func NewDecoder(r io.Reader) *Decoder {
	return &Decoder{r: r}
}

// ParseHeader parses only the ID3 header.
func (d *Decoder) ParseHeader() (TagHeader, error) {
	header, err := d.readHeader()
	if err != nil {
		return TagHeader{}, err
	}

	d.h = header
	d.r = io.LimitReader(d.r, int64(header.size))

	return header, nil
}

func (d *Decoder) remaining() int64 {
	return d.r.(*io.LimitedReader).N
}

// Parse parses a tag.
//
// Parse will always return a valid tag. In the case of an error, the
// tag will be empty.
//
// If Parse successfully parsed a tag, the reader will be positioned
// immediately after the tag, which usually is directly before audio
// data. If there wasn't a valid tag, however, the position of the
// reader is undefined. If you're not sure if your reader starts with
// a tag at all, consider using Check first.
//
// Parse cannot be called if either ParseHeader or ParseFrame have
// been called for the current tag.
func (d *Decoder) Parse() (*Tag, error) {
	tag := NewTag()
	header, err := d.ParseHeader()
	if err != nil {
		return tag, err
	}
	tag.Header = header

	// FIXME consider moving this to ParseHeader
	if header.Flags.ExtendedHeader() {
		panic("not implemented: cannot parse extended header")
	}

	if header.Flags.Unsynchronisation() {
		panic("not implemented: cannot parse unsynchronised tag")
	}

	for {
		frame, err := d.ParseFrame()
		if err != nil {
			if err == io.EOF {
				break
			}

			return tag, err
		}
		tag.Frames[frame.ID()] = append(tag.Frames[frame.ID()], frame)
	}

	if header.Version < 0x0400 {
		tag.upgrade()
	}

	return tag, nil
}

func readBinary(r io.Reader, args ...interface{}) (err error) {
	for _, arg := range args {
		err = binary.Read(r, binary.BigEndian, arg)
		if err != nil {
			break
		}
	}

	return
}

// readHeader reads an ID3v2 header. It expects the reader to be
// seeked to the beginning of the header.
func (d *Decoder) readHeader() (header TagHeader, err error) {
	var bytes struct {
		Magic   [3]byte
		Version [2]byte
		Flags   byte
		Size    [4]byte
	}

	err = binary.Read(d.r, binary.BigEndian, &bytes)
	if err != nil {
		return header, err
	}
	if bytes.Magic != Magic {
		return TagHeader{}, notATagHeader{bytes.Magic}
	}
	version := Version(int16(bytes.Version[0])<<8 | int16(bytes.Version[1]))
	if bytes.Version[0] > 4 || bytes.Version[0] < 3 {
		return TagHeader{}, UnsupportedVersion{version}
	}

	header.Version = version
	header.Flags = HeaderFlags(bytes.Flags)
	header.size = desynchsafeInt(bytes.Size)

	return header, nil
}

// ParseFrame reads the next ID3 frame. When it reaches padding, it
// will read and discard all of it and return io.EOF. This should set
// the reader immediately before the audio data.
//
// ParseHeader must be called before calling ParseFrame.
func (d *Decoder) ParseFrame() (Frame, error) {
	if d.remaining() == 0 {
		return nil, io.EOF
	}

	var (
		headerBytes struct {
			ID    [4]byte
			Size  [4]byte
			Flags [2]byte
		}
		header FrameHeader
	)

	err := binary.Read(d.r, binary.BigEndian, &headerBytes)
	if err != nil {
		return nil, err
	}

	// We're in the padding, discard remaining bytes and return io.EOF
	if headerBytes.ID == [4]byte{0, 0, 0, 0} {
		_, err := io.Copy(ioutil.Discard, d.r)
		if err != nil {
			return nil, err
		}
		return nil, io.EOF
	}

	for _, byte := range headerBytes.ID {
		// Allow 0-9
		if byte >= 48 && byte <= 57 {
			continue
		}

		// Allow A-Z
		if byte >= 65 && byte <= 90 {
			continue
		}

		return nil, NotAFrameHeader{headerBytes}
	}

	header.id = FrameType(headerBytes.ID[:])
	header.flags = FrameFlags(int16(headerBytes.Flags[0])<<8 | int16(headerBytes.Flags[1]))
	frameSize := desynchsafeInt(headerBytes.Size)

	if header.flags.Compressed() {
		panic("not implemented: cannot read compressed frame")
		// TODO: Read decompressed size (4 bytes)
	}

	if header.flags.Encrypted() {
		panic("not implemented: cannot read encrypted frame")
		// TODO: Read encryption method (1 byte)
	}

	if header.flags.Grouped() {
		panic("not implemented: cannot read grouped frame")
		// TODO: Read group identifier (1 byte)
	}

	if header.id[0] == 'T' && header.id != "TXXX" {
		var encoding Encoding
		frame := TextInformationFrame{FrameHeader: header}
		information := make([]byte, frameSize-1)
		err := readBinary(d.r, &encoding, &information)
		if err != nil {
			return nil, err
		}

		frame.Text = string(encoding.toUTF8(information))

		return frame, nil
	}

	if header.id[0] == 'W' && header.id != "WXXX" {
		frame := URLLinkFrame{FrameHeader: header}
		url := make([]byte, frameSize)
		_, err = d.r.Read(url)
		if err != nil {
			return nil, err
		}
		frame.URL = string(iso88591.toUTF8(url))

		return frame, nil
	}

	fn, ok := frameReaders[header.id]
	if !ok {
		data := make([]byte, frameSize)
		n, err := d.r.Read(data)

		return UnsupportedFrame{
			FrameHeader: header,
			Data:        data[:n],
		}, err
	}
	return fn(d.r, header, frameSize)
}

func readTXXXFrame(r io.Reader, header FrameHeader, frameSize int) (Frame, error) {
	var encoding Encoding
	frame := UserTextInformationFrame{FrameHeader: header}
	rest := make([]byte, frameSize-1)

	err := readBinary(r, &encoding, &rest)
	if err != nil {
		return nil, err
	}
	parts := splitNullN(rest, encoding, 2)

	frame.Description = string(encoding.toUTF8(parts[0]))
	frame.Text = string(encoding.toUTF8(parts[1]))

	return frame, nil
}

func readWXXXFrame(r io.Reader, header FrameHeader, frameSize int) (Frame, error) {
	var encoding Encoding
	frame := UserDefinedURLLinkFrame{FrameHeader: header}
	rest := make([]byte, frameSize-1)

	err := readBinary(r, &encoding, &rest)
	if err != nil {
		return nil, err
	}

	parts := splitNullN(rest, encoding, 2)
	frame.Description = string(encoding.toUTF8(parts[0]))
	frame.URL = string(iso88591ToUTF8(parts[1]))

	return frame, nil
}

func readUFIDFrame(r io.Reader, header FrameHeader, frameSize int) (Frame, error) {
	frame := UniqueFileIdentifierFrame{FrameHeader: header}
	rest := make([]byte, frameSize)

	err := binary.Read(r, binary.BigEndian, rest)
	if err != nil {
		return nil, err
	}

	parts := bytes.SplitN(rest, []byte{0}, 2)
	frame.Owner = string(iso88591.toUTF8(parts[0]))
	frame.Identifier = parts[1]

	return frame, nil
}

func readCOMMFrame(r io.Reader, header FrameHeader, frameSize int) (Frame, error) {
	frame := CommentFrame{FrameHeader: header}
	var (
		encoding Encoding
		language [3]byte
		rest     []byte
	)
	rest = make([]byte, frameSize-4)

	err := readBinary(r, &encoding, &language, &rest)
	if err != nil {
		return nil, err
	}

	parts := splitNullN(rest, encoding, 2)

	frame.Language = string(language[:])

	frame.Description = string(encoding.toUTF8(parts[0]))
	frame.Text = string(encoding.toUTF8(parts[1]))

	return frame, nil
}

func readPRIVFrame(r io.Reader, header FrameHeader, frameSize int) (Frame, error) {
	frame := PrivateFrame{FrameHeader: header}
	data := make([]byte, frameSize)
	_, err := r.Read(data)
	if err != nil {
		return frame, err
	}

	parts := bytes.SplitN(data, nul, 2)
	frame.Owner = parts[0]
	frame.Data = parts[1]

	return frame, nil
}

func readAPICFrame(r io.Reader, header FrameHeader, frameSize int) (Frame, error) {
	frame := PictureFrame{FrameHeader: header}
	var (
		encoding Encoding
		rest     []byte
	)
	rest = make([]byte, frameSize-1)
	err := readBinary(r, &encoding, &rest)
	if err != nil {
		return frame, err
	}

	parts1 := bytes.SplitN(rest, nul, 2)
	parts2 := splitNullN(parts1[1][1:], encoding, 2)

	frame.MIMEType = string(iso88591.toUTF8(parts1[0]))
	frame.PictureType = PictureType(parts1[1][0])
	frame.Description = string(encoding.toUTF8(parts2[0]))
	frame.Data = parts2[1]

	return frame, nil
}

func readMCDIFrame(r io.Reader, header FrameHeader, frameSize int) (Frame, error) {
	frame := MusicCDIdentifierFrame{FrameHeader: header}
	frame.TOC = make([]byte, frameSize)
	_, err := r.Read(frame.TOC)
	return frame, err
}

func readUSLTFrame(r io.Reader, header FrameHeader, frameSize int) (Frame, error) {
	frame := UnsynchronisedLyricsFrame{FrameHeader: header}
	var (
		encoding Encoding
		language [3]byte
		rest     []byte
	)
	rest = make([]byte, frameSize-4)

	err := readBinary(r, &encoding, &language, rest)
	if err != nil {
		return frame, err
	}

	parts := splitNullN(rest, encoding, 2)
	frame.Language = string(language[:])

	frame.Description = string(encoding.toUTF8(parts[0]))
	frame.Lyrics = string(encoding.toUTF8(parts[1]))

	return frame, nil
}
<|endoftext|>


<commit_before>// Copyright 2014 Rana Ian. All rights reserved.
// Use of this source code is governed by The MIT License
// found in the accompanying LICENSE file.

package ora

/*
#include <oci.h>
#include "version.h"
*/
import "C"
import (
	"bytes"
	"math"
	"strings"
	"time"
	"unsafe"
)

type defTime struct {
	rset        *Rset
	ocidef      *C.OCIDefine
	ociDateTime *C.OCIDateTime
	null        C.sb2
	isNullable  bool
}

func (def *defTime) define(position int, isNullable bool, rset *Rset) error {
	def.rset = rset
	def.isNullable = isNullable
	r := C.OCIDEFINEBYPOS(
		def.rset.ocistmt,                              //OCIStmt     *stmtp,
		&def.ocidef,                                   //OCIDefine   **defnpp,
		def.rset.stmt.ses.srv.env.ocierr,              //OCIError    *errhp,
		C.ub4(position),                               //ub4         position,
		unsafe.Pointer(&def.ociDateTime),              //void        *valuep,
		C.LENGTH_TYPE(unsafe.Sizeof(def.ociDateTime)), //sb8         value_sz,
		C.SQLT_TIMESTAMP_TZ,                           //defineTypeCode,                               //ub2         dty,
		unsafe.Pointer(&def.null),                     //void        *indp,
		nil,           //ub2         *rlenp,
		nil,           //ub2         *rcodep,
		C.OCI_DEFAULT) //ub4         mode );
	if r == C.OCI_ERROR {
		return def.rset.stmt.ses.srv.env.ociError()
	}
	return nil
}

func (def *defTime) value() (value interface{}, err error) {
	if def.isNullable {
		oraTimeValue := Time{IsNull: def.null < 0}
		if !oraTimeValue.IsNull {
			oraTimeValue.Value, err = getTime(def.rset.stmt.ses.srv.env, def.ociDateTime)
		}
		value = oraTimeValue
		return value, err
	}
	if def.null < 0 {
		return time.Time{}, nil // zero time
	}
	return getTime(def.rset.stmt.ses.srv.env, def.ociDateTime)
}

func (def *defTime) alloc() error {
	r := C.OCIDescriptorAlloc(
		unsafe.Pointer(def.rset.stmt.ses.srv.env.ocienv),    //CONST dvoid   *parenth,
		(*unsafe.Pointer)(unsafe.Pointer(&def.ociDateTime)), //dvoid         **descpp,
		C.OCI_DTYPE_TIMESTAMP_TZ,                            //ub4           type,
		0,   //size_t        xtramem_sz,
		nil) //dvoid         **usrmempp);
	if r == C.OCI_ERROR {
		return def.rset.stmt.ses.srv.env.ociError()
	} else if r == C.OCI_INVALID_HANDLE {
		return errNew("unable to allocate oci timestamp handle during define")
	}
	return nil

}

func (def *defTime) free() {
	defer func() {
		recover()
	}()
	C.OCIDescriptorFree(
		unsafe.Pointer(def.ociDateTime), //void     *descp,
		C.OCI_DTYPE_TIMESTAMP_TZ)        //ub4      type );
}

func (def *defTime) close() (err error) {
	defer func() {
		if value := recover(); value != nil {
			err = errR(value)
		}
	}()

	rset := def.rset
	def.rset = nil
	def.ocidef = nil
	rset.putDef(defIdxTime, def)
	return nil
}

func getTime(env *Env, ociDateTime *C.OCIDateTime) (result time.Time, err error) {
	var year C.sb2
	var month C.ub1
	var day C.ub1
	var hour C.ub1
	var minute C.ub1
	var second C.ub1
	var fsec C.ub4
	var location *time.Location
	r := C.OCIDateTimeGetDate(
		unsafe.Pointer(env.ocienv), //void               *hndl,
		env.ocierr,                 //OCIError           *err,
		ociDateTime,                //const OCIDateTime  *datetime,
		&year,                      //sb2                *year,
		&month,                     //ub1                *month,
		&day)                       //ub1                *day );
	if r == C.OCI_ERROR {
		return result, env.ociError()
	}
	r = C.OCIDateTimeGetTime(
		unsafe.Pointer(env.ocienv), //void               *hndl,
		env.ocierr,                 //OCIError           *err,
		ociDateTime,                //OCIDateTime  *datetime,
		&hour,                      //ub1           *hour,
		&minute,                    //ub1           *min,
		&second,                    //ub1           *sec,
		&fsec)                      //ub4           *fsec );
	if r == C.OCI_ERROR {
		return result, env.ociError()
	}
	var buf [32]byte
	var buflen C.ub4 = 32
	r = C.OCIDateTimeGetTimeZoneName(
		unsafe.Pointer(env.ocienv), //void               *hndl,
		env.ocierr,                 //OCIError           *err,
		ociDateTime,                //const OCIDateTime  *datetime,
		(*C.ub1)(&buf[0]),          //ub1                *buf,
		&buflen)                    //ub4                *buflen, );
	if r != C.OCI_ERROR {
		var buffer bytes.Buffer
		for n := 0; n < int(buflen); n++ {
			buffer.WriteByte(buf[n])
		}
		locName := buffer.String()
		location = _drv.locations[locName]
		if location == nil {
			// timestamp_ltz returns numeric offset
			// time.Time's lookup for numeric offset is unknown;
			// therefore, create a fixed location for the offset
			var offsetHour C.sb1
			var offsetMinute C.sb1
			if strings.ContainsAny(locName, "-0123456789") {
				r = C.OCIDateTimeGetTimeZoneOffset(
					unsafe.Pointer(env.ocienv), //void               *hndl,
					env.ocierr,                 //OCIError           *err,
					ociDateTime,                //const OCIDateTime  *datetime,
					&offsetHour,                //sb1                *hour,
					&offsetMinute)              //sb1                *min, );
				if r == C.OCI_ERROR {
					return result, env.ociError()
				}
				seconds := math.Abs(float64(offsetHour)) * 60 * 60
				seconds += math.Abs(float64(offsetMinute)) * 60
				if offsetHour < 0 {
					seconds *= -1
				}
				location = time.FixedZone(locName, int(seconds))
			} else {
				location, err = time.LoadLocation(locName)
				if err != nil {
					return result, err
				}
			}
			// stored location for future reference
			// important that FixedZone is called as few times as possible
			// to reduce significant memory allocation
			_drv.locations[locName] = location
		}
	} else {
		// Date Oracle type doesn't have timezone info
		// no timezone information available from server
		location = time.Local
	}
	result = time.Date(int(year), time.Month(int(month)), int(day), int(hour), int(minute), int(second), int(fsec), location)
	return result, nil
}
<commit_msg>Let defTime.value return nil for nil DATE<commit_after>// Copyright 2014 Rana Ian. All rights reserved.
// Use of this source code is governed by The MIT License
// found in the accompanying LICENSE file.

package ora

/*
#include <oci.h>
#include "version.h"
*/
import "C"
import (
	"bytes"
	"math"
	"strings"
	"time"
	"unsafe"
)

type defTime struct {
	rset        *Rset
	ocidef      *C.OCIDefine
	ociDateTime *C.OCIDateTime
	null        C.sb2
	isNullable  bool
}

func (def *defTime) define(position int, isNullable bool, rset *Rset) error {
	def.rset = rset
	def.isNullable = isNullable
	r := C.OCIDEFINEBYPOS(
		def.rset.ocistmt,                              //OCIStmt     *stmtp,
		&def.ocidef,                                   //OCIDefine   **defnpp,
		def.rset.stmt.ses.srv.env.ocierr,              //OCIError    *errhp,
		C.ub4(position),                               //ub4         position,
		unsafe.Pointer(&def.ociDateTime),              //void        *valuep,
		C.LENGTH_TYPE(unsafe.Sizeof(def.ociDateTime)), //sb8         value_sz,
		C.SQLT_TIMESTAMP_TZ,                           //defineTypeCode,                               //ub2         dty,
		unsafe.Pointer(&def.null),                     //void        *indp,
		nil,           //ub2         *rlenp,
		nil,           //ub2         *rcodep,
		C.OCI_DEFAULT) //ub4         mode );
	if r == C.OCI_ERROR {
		return def.rset.stmt.ses.srv.env.ociError()
	}
	return nil
}

func (def *defTime) value() (value interface{}, err error) {
	if def.isNullable {
		oraTimeValue := Time{IsNull: def.null < 0}
		if !oraTimeValue.IsNull {
			oraTimeValue.Value, err = getTime(def.rset.stmt.ses.srv.env, def.ociDateTime)
		}
		return oraTimeValue, err
	}
	if def.null < 0 {
		return nil, nil
	}
	return getTime(def.rset.stmt.ses.srv.env, def.ociDateTime)
}

func (def *defTime) alloc() error {
	r := C.OCIDescriptorAlloc(
		unsafe.Pointer(def.rset.stmt.ses.srv.env.ocienv),    //CONST dvoid   *parenth,
		(*unsafe.Pointer)(unsafe.Pointer(&def.ociDateTime)), //dvoid         **descpp,
		C.OCI_DTYPE_TIMESTAMP_TZ,                            //ub4           type,
		0,   //size_t        xtramem_sz,
		nil) //dvoid         **usrmempp);
	if r == C.OCI_ERROR {
		return def.rset.stmt.ses.srv.env.ociError()
	} else if r == C.OCI_INVALID_HANDLE {
		return errNew("unable to allocate oci timestamp handle during define")
	}
	return nil

}

func (def *defTime) free() {
	defer func() {
		recover()
	}()
	C.OCIDescriptorFree(
		unsafe.Pointer(def.ociDateTime), //void     *descp,
		C.OCI_DTYPE_TIMESTAMP_TZ)        //ub4      type );
}

func (def *defTime) close() (err error) {
	defer func() {
		if value := recover(); value != nil {
			err = errR(value)
		}
	}()

	rset := def.rset
	def.rset = nil
	def.ocidef = nil
	rset.putDef(defIdxTime, def)
	return nil
}

func getTime(env *Env, ociDateTime *C.OCIDateTime) (result time.Time, err error) {
	var year C.sb2
	var month C.ub1
	var day C.ub1
	var hour C.ub1
	var minute C.ub1
	var second C.ub1
	var fsec C.ub4
	var location *time.Location
	r := C.OCIDateTimeGetDate(
		unsafe.Pointer(env.ocienv), //void               *hndl,
		env.ocierr,                 //OCIError           *err,
		ociDateTime,                //const OCIDateTime  *datetime,
		&year,                      //sb2                *year,
		&month,                     //ub1                *month,
		&day)                       //ub1                *day );
	if r == C.OCI_ERROR {
		return result, env.ociError()
	}
	r = C.OCIDateTimeGetTime(
		unsafe.Pointer(env.ocienv), //void               *hndl,
		env.ocierr,                 //OCIError           *err,
		ociDateTime,                //OCIDateTime  *datetime,
		&hour,                      //ub1           *hour,
		&minute,                    //ub1           *min,
		&second,                    //ub1           *sec,
		&fsec)                      //ub4           *fsec );
	if r == C.OCI_ERROR {
		return result, env.ociError()
	}
	var buf [32]byte
	var buflen C.ub4 = 32
	r = C.OCIDateTimeGetTimeZoneName(
		unsafe.Pointer(env.ocienv), //void               *hndl,
		env.ocierr,                 //OCIError           *err,
		ociDateTime,                //const OCIDateTime  *datetime,
		(*C.ub1)(&buf[0]),          //ub1                *buf,
		&buflen)                    //ub4                *buflen, );
	if r != C.OCI_ERROR {
		var buffer bytes.Buffer
		for n := 0; n < int(buflen); n++ {
			buffer.WriteByte(buf[n])
		}
		locName := buffer.String()
		location = _drv.locations[locName]
		if location == nil {
			// timestamp_ltz returns numeric offset
			// time.Time's lookup for numeric offset is unknown;
			// therefore, create a fixed location for the offset
			var offsetHour C.sb1
			var offsetMinute C.sb1
			if strings.ContainsAny(locName, "-0123456789") {
				r = C.OCIDateTimeGetTimeZoneOffset(
					unsafe.Pointer(env.ocienv), //void               *hndl,
					env.ocierr,                 //OCIError           *err,
					ociDateTime,                //const OCIDateTime  *datetime,
					&offsetHour,                //sb1                *hour,
					&offsetMinute)              //sb1                *min, );
				if r == C.OCI_ERROR {
					return result, env.ociError()
				}
				seconds := math.Abs(float64(offsetHour)) * 60 * 60
				seconds += math.Abs(float64(offsetMinute)) * 60
				if offsetHour < 0 {
					seconds *= -1
				}
				location = time.FixedZone(locName, int(seconds))
			} else {
				location, err = time.LoadLocation(locName)
				if err != nil {
					return result, err
				}
			}
			// stored location for future reference
			// important that FixedZone is called as few times as possible
			// to reduce significant memory allocation
			_drv.locations[locName] = location
		}
	} else {
		// Date Oracle type doesn't have timezone info
		// no timezone information available from server
		location = time.Local
	}
	result = time.Date(int(year), time.Month(int(month)), int(day), int(hour), int(minute), int(second), int(fsec), location)
	return result, nil
}
<|endoftext|>