<commit_before>// Copyright 2010 The Go 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 fsnotify

import (
	"os"
	"os/exec"
	"testing"
	"time"
)

func TestFsnotifyDirOnly(t *testing.T) {
	// Create an fsnotify watcher instance and initialize it
	watcher, err := NewWatcher()
	if err != nil {
		t.Fatalf("NewWatcher() failed: %s", err)
	}

	const testDir string = "_test"

	// Create directory to watch
	if os.Mkdir(testDir, 0777) != nil {
		t.Fatalf("Failed to create test directory: %s", err)
	}
	defer os.RemoveAll(testDir)

	// Create a file before watching directory
	// This should NOT add any events to the fsnotify event queue
	const testFileAlreadyExists string = "_test/TestFsnotifyEventsExisting.testfile"
	{
		var f *os.File
		f, err = os.OpenFile(testFileAlreadyExists, os.O_WRONLY|os.O_CREATE, 0666)
		if err != nil {
			t.Fatalf("creating test file failed: %s", err)
		}
		f.Sync()
		f.Close()
	}

	// Add a watch for testDir
	err = watcher.Watch(testDir)
	if err != nil {
		t.Fatalf("Watcher.Watch() failed: %s", err)
	}

	// Receive errors on the error channel on a separate goroutine
	go func() {
		for err := range watcher.Error {
			t.Fatalf("error received: %s", err)
		}
	}()

	const testFile string = "_test/TestFsnotifyDirOnly.testfile"

	// Receive events on the event channel on a separate goroutine
	eventstream := watcher.Event
	var createReceived = 0
	var modifyReceived = 0
	var deleteReceived = 0
	done := make(chan bool)
	go func() {
		for event := range eventstream {
			// Only count relevant events
			if event.Name == testDir || event.Name == testFile || event.Name == testFileAlreadyExists {
				t.Logf("event received: %s", event)
				if event.IsDelete() {
					deleteReceived++
				}
				if event.IsModify() {
					modifyReceived++
				}
				if event.IsCreate() {
					createReceived++
				}
			} else {
				t.Logf("unexpected event received: %s", event)
			}
		}
		done <- true
	}()

	// Create a file
	// This should add at least one event to the fsnotify event queue
	var f *os.File
	f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		t.Fatalf("creating test file failed: %s", err)
	}
	f.Sync()

	time.Sleep(time.Millisecond)
	f.WriteString("data")
	f.Sync()
	f.Close()

	time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete

	os.Remove(testFile)
	os.Remove(testFileAlreadyExists)

	// We expect this event to be received almost immediately, but let's wait 500 ms to be sure
	time.Sleep(500 * time.Millisecond)
	if createReceived != 1 {
		t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", createReceived, 1)
	}
	if modifyReceived != 1 {
		t.Fatalf("incorrect number of modify events received after 500 ms (%d vs %d)", modifyReceived, 1)
	}
	if deleteReceived != 2 {
		t.Fatalf("incorrect number of delete events received after 500 ms (%d vs %d)", deleteReceived, 2)
	}

	// Try closing the fsnotify instance
	t.Log("calling Close()")
	watcher.Close()
	t.Log("waiting for the event channel to become closed...")
	select {
	case <-done:
		t.Log("event channel closed")
	case <-time.After(2 * time.Second):
		t.Fatal("event stream was not closed after 2 seconds")
	}
}

func TestFsnotifyRename(t *testing.T) {
	// Create an fsnotify watcher instance and initialize it
	watcher, err := NewWatcher()
	if err != nil {
		t.Fatalf("NewWatcher() failed: %s", err)
	}

	const testDir string = "_test"

	// Create directory to watch
	if os.Mkdir(testDir, 0777) != nil {
		t.Fatalf("Failed to create test directory: %s", err)
	}
	defer os.RemoveAll(testDir)

	// Add a watch for testDir
	err = watcher.Watch(testDir)
	if err != nil {
		t.Fatalf("Watcher.Watch() failed: %s", err)
	}

	// Receive errors on the error channel on a separate goroutine
	go func() {
		for err := range watcher.Error {
			t.Fatalf("error received: %s", err)
		}
	}()

	const testFile string = "_test/TestFsnotifyEvents.testfile"
	const testFileRenamed string = "_test/TestFsnotifyEvents.testfileRenamed"

	// Receive events on the event channel on a separate goroutine
	eventstream := watcher.Event
	var renameReceived = 0
	done := make(chan bool)
	go func() {
		for event := range eventstream {
			// Only count relevant events
			if event.Name == testDir || event.Name == testFile || event.Name == testFileRenamed {
				if event.IsRename() {
					renameReceived++
				}
				t.Logf("event received: %s", event)
			} else {
				t.Logf("unexpected event received: %s", event)
			}
		}
		done <- true
	}()

	// Create a file
	// This should add at least one event to the fsnotify event queue
	var f *os.File
	f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		t.Fatalf("creating test file failed: %s", err)
	}
	f.Sync()

	f.WriteString("data")
	f.Sync()
	f.Close()

	// Add a watch for testFile
	err = watcher.Watch(testFile)
	if err != nil {
		t.Fatalf("Watcher.Watch() failed: %s", err)
	}

	cmd := exec.Command("mv", testFile, testFileRenamed)
	err = cmd.Run()
	if err != nil {
		t.Fatalf("rename failed: %s", err)
	}

	// We expect this event to be received almost immediately, but let's wait 500 ms to be sure
	time.Sleep(500 * time.Millisecond)
	if renameReceived == 0 {
		t.Fatal("fsnotify rename events have not been received after 500 ms")
	}

	// Try closing the fsnotify instance
	t.Log("calling Close()")
	watcher.Close()
	t.Log("waiting for the event channel to become closed...")
	select {
	case <-done:
		t.Log("event channel closed")
	case <-time.After(2 * time.Second):
		t.Fatal("event stream was not closed after 2 seconds")
	}

	os.Remove(testFileRenamed)
}

func TestFsnotifyAttrib(t *testing.T) {
	// Create an fsnotify watcher instance and initialize it
	watcher, err := NewWatcher()
	if err != nil {
		t.Fatalf("NewWatcher() failed: %s", err)
	}

	const testDir string = "_test"

	// Create directory to watch
	if os.Mkdir(testDir, 0777) != nil {
		t.Fatalf("Failed to create test directory: %s", err)
	}
	defer os.RemoveAll(testDir)

	// Receive errors on the error channel on a separate goroutine
	go func() {
		for err := range watcher.Error {
			t.Fatalf("error received: %s", err)
		}
	}()

	const testFile string = "_test/TestFsnotifyAttrib.testfile"

	// Receive events on the event channel on a separate goroutine
	eventstream := watcher.Event
	var attribReceived = 0
	done := make(chan bool)
	go func() {
		for event := range eventstream {
			// Only count relevant events
			if event.Name == testDir || event.Name == testFile {
				if event.IsModify() {
					attribReceived++
				}
				t.Logf("event received: %s", event)
			} else {
				t.Logf("unexpected event received: %s", event)
			}
		}
		done <- true
	}()

	// Create a file
	// This should add at least one event to the fsnotify event queue
	var f *os.File
	f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		t.Fatalf("creating test file failed: %s", err)
	}
	f.Sync()

	f.WriteString("data")
	f.Sync()
	f.Close()

	// Add a watch for testFile
	err = watcher.Watch(testFile)
	if err != nil {
		t.Fatalf("Watcher.Watch() failed: %s", err)
	}

	cmd := exec.Command("chmod", "0700", testFile)
	err = cmd.Run()
	if err != nil {
		t.Fatalf("chmod failed: %s", err)
	}

	// We expect this event to be received almost immediately, but let's wait 500 ms to be sure
	time.Sleep(500 * time.Millisecond)
	if attribReceived == 0 {
		t.Fatal("fsnotify attribute events have not received after 500 ms")
	}

	// Try closing the fsnotify instance
	t.Log("calling Close()")
	watcher.Close()
	t.Log("waiting for the event channel to become closed...")
	select {
	case <-done:
		t.Log("event channel closed")
	case <-time.After(1e9):
		t.Fatal("event stream was not closed after 1 second")
	}

	os.Remove(testFile)
}

func TestFsnotifyClose(t *testing.T) {
	watcher, _ := NewWatcher()
	watcher.Close()

	done := false
	go func() {
		watcher.Close()
		done = true
	}()

	time.Sleep(50e6) // 50 ms
	if !done {
		t.Fatal("double Close() test failed: second Close() call didn't return")
	}

	err := watcher.Watch("_test")
	if err == nil {
		t.Fatal("expected error on Watch() after Close(), got nil")
	}
}
<commit_msg>test case that shows problems with dir watch on bsd<commit_after>// Copyright 2010 The Go 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 fsnotify

import (
	"os"
	"os/exec"
	"testing"
	"time"
)

func TestFsnotifyMultipleOperations(t *testing.T) {
	// Create an fsnotify watcher instance and initialize it
	watcher, err := NewWatcher()
	if err != nil {
		t.Fatalf("NewWatcher() failed: %s", err)
	}

	// Receive errors on the error channel on a separate goroutine
	go func() {
		for err := range watcher.Error {
			t.Fatalf("error received: %s", err)
		}
	}()

	const testDir string = "_test"
	const testDirToMoveFiles string = "_test2"

	// Create directory to watch
	if os.Mkdir(testDir, 0777) != nil {
		t.Fatalf("Failed to create test directory: %s", err)
	}
	defer os.RemoveAll(testDir)

	// Create directory to that's not watched
	if os.Mkdir(testDirToMoveFiles, 0777) != nil {
		t.Fatalf("Failed to create test directory: %s", err)
	}
	defer os.RemoveAll(testDirToMoveFiles)

	const testFile string = "_test/TestFsnotifySeq.testfile"
	const testFileRenamed string = "_test2/TestFsnotifySeqRename.testfile"

	// Add a watch for testDir
	err = watcher.Watch(testDir)
	if err != nil {
		t.Fatalf("Watcher.Watch() failed: %s", err)
	}
	// Receive events on the event channel on a separate goroutine
	eventstream := watcher.Event
	var createReceived = 0
	var modifyReceived = 0
	var deleteReceived = 0
	var renameReceived = 0
	done := make(chan bool)
	go func() {
		for event := range eventstream {
			// Only count relevant events
			if event.Name == testDir || event.Name == testFile {
				t.Logf("event received: %s", event)
				if event.IsDelete() {
					deleteReceived++
				}
				if event.IsModify() {
					modifyReceived++
				}
				if event.IsCreate() {
					createReceived++
				}
				if event.IsRename() {
					renameReceived++
				}
			} else {
				t.Logf("unexpected event received: %s", event)
			}
		}
		done <- true
	}()

	// Create a file
	// This should add at least one event to the fsnotify event queue
	var f *os.File
	f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		t.Fatalf("creating test file failed: %s", err)
	}
	f.Sync()

	time.Sleep(time.Millisecond)
	f.WriteString("data")
	f.Sync()

	time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete

	cmd := exec.Command("mv", testFile, testFileRenamed)
	err = cmd.Run()
	if err != nil {
		t.Fatalf("rename failed: %s", err)
	}

  // Modify the file outside of the watched dir
	f.WriteString("data")
	f.Sync()
  f.Close()

	time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete

  // Recreate the file that was moved
	f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		t.Fatalf("creating test file failed: %s", err)
	}
  f.Close()
	time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete

	// We expect this event to be received almost immediately, but let's wait 500 ms to be sure
	time.Sleep(500 * time.Millisecond)
	if createReceived != 2 {
		t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", createReceived, 2)
	}
	if modifyReceived != 1 {
		t.Fatalf("incorrect number of modify events received after 500 ms (%d vs %d)", modifyReceived, 1)
	}
	if deleteReceived != 0 {
		t.Fatalf("incorrect number of delete events received after 500 ms (%d vs %d)", deleteReceived, 0)
	}
  if renameReceived != 1 {
		t.Fatalf("incorrect number of rename events received after 500 ms (%d vs %d)", renameReceived, 1)
  }

	// Try closing the fsnotify instance
	t.Log("calling Close()")
	watcher.Close()
	t.Log("waiting for the event channel to become closed...")
	select {
	case <-done:
		t.Log("event channel closed")
	case <-time.After(2 * time.Second):
		t.Fatal("event stream was not closed after 2 seconds")
	}
}

func TestFsnotifyDirOnly(t *testing.T) {
	// Create an fsnotify watcher instance and initialize it
	watcher, err := NewWatcher()
	if err != nil {
		t.Fatalf("NewWatcher() failed: %s", err)
	}

	const testDir string = "_test"

	// Create directory to watch
	if os.Mkdir(testDir, 0777) != nil {
		t.Fatalf("Failed to create test directory: %s", err)
	}
	defer os.RemoveAll(testDir)

	// Create a file before watching directory
	// This should NOT add any events to the fsnotify event queue
	const testFileAlreadyExists string = "_test/TestFsnotifyEventsExisting.testfile"
	{
		var f *os.File
		f, err = os.OpenFile(testFileAlreadyExists, os.O_WRONLY|os.O_CREATE, 0666)
		if err != nil {
			t.Fatalf("creating test file failed: %s", err)
		}
		f.Sync()
		f.Close()
	}

	// Add a watch for testDir
	err = watcher.Watch(testDir)
	if err != nil {
		t.Fatalf("Watcher.Watch() failed: %s", err)
	}

	// Receive errors on the error channel on a separate goroutine
	go func() {
		for err := range watcher.Error {
			t.Fatalf("error received: %s", err)
		}
	}()

	const testFile string = "_test/TestFsnotifyDirOnly.testfile"

	// Receive events on the event channel on a separate goroutine
	eventstream := watcher.Event
	var createReceived = 0
	var modifyReceived = 0
	var deleteReceived = 0
	done := make(chan bool)
	go func() {
		for event := range eventstream {
			// Only count relevant events
			if event.Name == testDir || event.Name == testFile || event.Name == testFileAlreadyExists {
				t.Logf("event received: %s", event)
				if event.IsDelete() {
					deleteReceived++
				}
				if event.IsModify() {
					modifyReceived++
				}
				if event.IsCreate() {
					createReceived++
				}
			} else {
				t.Logf("unexpected event received: %s", event)
			}
		}
		done <- true
	}()

	// Create a file
	// This should add at least one event to the fsnotify event queue
	var f *os.File
	f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		t.Fatalf("creating test file failed: %s", err)
	}
	f.Sync()

	time.Sleep(time.Millisecond)
	f.WriteString("data")
	f.Sync()
	f.Close()

	time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete

	os.Remove(testFile)
	os.Remove(testFileAlreadyExists)

	// We expect this event to be received almost immediately, but let's wait 500 ms to be sure
	time.Sleep(500 * time.Millisecond)
	if createReceived != 1 {
		t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", createReceived, 1)
	}
	if modifyReceived != 1 {
		t.Fatalf("incorrect number of modify events received after 500 ms (%d vs %d)", modifyReceived, 1)
	}
	if deleteReceived != 2 {
		t.Fatalf("incorrect number of delete events received after 500 ms (%d vs %d)", deleteReceived, 2)
	}

	// Try closing the fsnotify instance
	t.Log("calling Close()")
	watcher.Close()
	t.Log("waiting for the event channel to become closed...")
	select {
	case <-done:
		t.Log("event channel closed")
	case <-time.After(2 * time.Second):
		t.Fatal("event stream was not closed after 2 seconds")
	}
}

func TestFsnotifyRename(t *testing.T) {
	// Create an fsnotify watcher instance and initialize it
	watcher, err := NewWatcher()
	if err != nil {
		t.Fatalf("NewWatcher() failed: %s", err)
	}

	const testDir string = "_test"

	// Create directory to watch
	if os.Mkdir(testDir, 0777) != nil {
		t.Fatalf("Failed to create test directory: %s", err)
	}
	defer os.RemoveAll(testDir)

	// Add a watch for testDir
	err = watcher.Watch(testDir)
	if err != nil {
		t.Fatalf("Watcher.Watch() failed: %s", err)
	}

	// Receive errors on the error channel on a separate goroutine
	go func() {
		for err := range watcher.Error {
			t.Fatalf("error received: %s", err)
		}
	}()

	const testFile string = "_test/TestFsnotifyEvents.testfile"
	const testFileRenamed string = "_test/TestFsnotifyEvents.testfileRenamed"

	// Receive events on the event channel on a separate goroutine
	eventstream := watcher.Event
	var renameReceived = 0
	done := make(chan bool)
	go func() {
		for event := range eventstream {
			// Only count relevant events
			if event.Name == testDir || event.Name == testFile || event.Name == testFileRenamed {
				if event.IsRename() {
					renameReceived++
				}
				t.Logf("event received: %s", event)
			} else {
				t.Logf("unexpected event received: %s", event)
			}
		}
		done <- true
	}()

	// Create a file
	// This should add at least one event to the fsnotify event queue
	var f *os.File
	f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		t.Fatalf("creating test file failed: %s", err)
	}
	f.Sync()

	f.WriteString("data")
	f.Sync()
	f.Close()

	// Add a watch for testFile
	err = watcher.Watch(testFile)
	if err != nil {
		t.Fatalf("Watcher.Watch() failed: %s", err)
	}

	cmd := exec.Command("mv", testFile, testFileRenamed)
	err = cmd.Run()
	if err != nil {
		t.Fatalf("rename failed: %s", err)
	}

	// We expect this event to be received almost immediately, but let's wait 500 ms to be sure
	time.Sleep(500 * time.Millisecond)
	if renameReceived == 0 {
		t.Fatal("fsnotify rename events have not been received after 500 ms")
	}

	// Try closing the fsnotify instance
	t.Log("calling Close()")
	watcher.Close()
	t.Log("waiting for the event channel to become closed...")
	select {
	case <-done:
		t.Log("event channel closed")
	case <-time.After(2 * time.Second):
		t.Fatal("event stream was not closed after 2 seconds")
	}

	os.Remove(testFileRenamed)
}

func TestFsnotifyAttrib(t *testing.T) {
	// Create an fsnotify watcher instance and initialize it
	watcher, err := NewWatcher()
	if err != nil {
		t.Fatalf("NewWatcher() failed: %s", err)
	}

	const testDir string = "_test"

	// Create directory to watch
	if os.Mkdir(testDir, 0777) != nil {
		t.Fatalf("Failed to create test directory: %s", err)
	}
	defer os.RemoveAll(testDir)

	// Receive errors on the error channel on a separate goroutine
	go func() {
		for err := range watcher.Error {
			t.Fatalf("error received: %s", err)
		}
	}()

	const testFile string = "_test/TestFsnotifyAttrib.testfile"

	// Receive events on the event channel on a separate goroutine
	eventstream := watcher.Event
	var attribReceived = 0
	done := make(chan bool)
	go func() {
		for event := range eventstream {
			// Only count relevant events
			if event.Name == testDir || event.Name == testFile {
				if event.IsModify() {
					attribReceived++
				}
				t.Logf("event received: %s", event)
			} else {
				t.Logf("unexpected event received: %s", event)
			}
		}
		done <- true
	}()

	// Create a file
	// This should add at least one event to the fsnotify event queue
	var f *os.File
	f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		t.Fatalf("creating test file failed: %s", err)
	}
	f.Sync()

	f.WriteString("data")
	f.Sync()
	f.Close()

	// Add a watch for testFile
	err = watcher.Watch(testFile)
	if err != nil {
		t.Fatalf("Watcher.Watch() failed: %s", err)
	}

	cmd := exec.Command("chmod", "0700", testFile)
	err = cmd.Run()
	if err != nil {
		t.Fatalf("chmod failed: %s", err)
	}

	// We expect this event to be received almost immediately, but let's wait 500 ms to be sure
	time.Sleep(500 * time.Millisecond)
	if attribReceived == 0 {
		t.Fatal("fsnotify attribute events have not received after 500 ms")
	}

	// Try closing the fsnotify instance
	t.Log("calling Close()")
	watcher.Close()
	t.Log("waiting for the event channel to become closed...")
	select {
	case <-done:
		t.Log("event channel closed")
	case <-time.After(1e9):
		t.Fatal("event stream was not closed after 1 second")
	}

	os.Remove(testFile)
}

func TestFsnotifyClose(t *testing.T) {
	watcher, _ := NewWatcher()
	watcher.Close()

	done := false
	go func() {
		watcher.Close()
		done = true
	}()

	time.Sleep(50e6) // 50 ms
	if !done {
		t.Fatal("double Close() test failed: second Close() call didn't return")
	}

	err := watcher.Watch("_test")
	if err == nil {
		t.Fatal("expected error on Watch() after Close(), got nil")
	}
}
<|endoftext|>


<commit_before>// Utilities for testing the Fs
package fstest

// FIXME put name of test FS in Fs structure

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

	"github.com/ncw/rclone/fs"
)

// Seed the random number generator
func init() {
	rand.Seed(time.Now().UnixNano())

}

// Represents an item for checking
type Item struct {
	Path    string
	Md5sum  string
	ModTime time.Time
	Size    int64
	WinPath string
}

// Checks the times are equal within the precision, returns the delta and a flag
func CheckTimeEqualWithPrecision(t0, t1 time.Time, precision time.Duration) (time.Duration, bool) {
	dt := t0.Sub(t1)
	if dt >= precision || dt <= -precision {
		return dt, false
	}
	return dt, true
}

// check the mod time to the given precision
func (i *Item) CheckModTime(t *testing.T, obj fs.Object, modTime time.Time, precision time.Duration) {
	dt, ok := CheckTimeEqualWithPrecision(modTime, i.ModTime, precision)
	if !ok {
		t.Errorf("%s: Modification time difference too big |%s| > %s (%s vs %s) (precision %s)", obj.Remote(), dt, precision, modTime, i.ModTime, precision)
	}
}

func (i *Item) Check(t *testing.T, obj fs.Object, precision time.Duration) {
	if obj == nil {
		t.Fatalf("Object is nil")
	}
	// Check attributes
	Md5sum, err := obj.Md5sum()
	if err != nil {
		t.Fatalf("Failed to read md5sum for %q: %v", obj.Remote(), err)
	}
	if !fs.Md5sumsEqual(i.Md5sum, Md5sum) {
		t.Errorf("%s: Md5sum incorrect - expecting %q got %q", obj.Remote(), i.Md5sum, Md5sum)
	}
	if i.Size != obj.Size() {
		t.Errorf("%s: Size incorrect - expecting %d got %d", obj.Remote(), i.Size, obj.Size())
	}
	i.CheckModTime(t, obj, obj.ModTime(), precision)
}

// Represents all items for checking
type Items struct {
	byName    map[string]*Item
	byNameAlt map[string]*Item
	items     []Item
}

// Make an Items
func NewItems(items []Item) *Items {
	is := &Items{
		byName:    make(map[string]*Item),
		byNameAlt: make(map[string]*Item),
		items:     items,
	}
	// Fill up byName
	for i := range items {
		is.byName[items[i].Path] = &items[i]
		is.byNameAlt[items[i].WinPath] = &items[i]
	}
	return is
}

// Check off an item
func (is *Items) Find(t *testing.T, obj fs.Object, precision time.Duration) {
	i, ok := is.byName[obj.Remote()]
	if !ok {
		i, ok = is.byNameAlt[obj.Remote()]
		if !ok {
			t.Errorf("Unexpected file %q", obj.Remote())
			return
		}
	}
	delete(is.byName, i.Path)
	delete(is.byName, i.WinPath)
	i.Check(t, obj, precision)
}

// Check all done
func (is *Items) Done(t *testing.T) {
	if len(is.byName) != 0 {
		for name := range is.byName {
			log.Printf("Not found %q", name)
		}
		t.Errorf("%d objects not found", len(is.byName))
	}
}

// Checks the fs to see if it has the expected contents
func CheckListingWithPrecision(t *testing.T, f fs.Fs, items []Item, precision time.Duration) {
	is := NewItems(items)
	oldErrors := fs.Stats.GetErrors()
	for obj := range f.List() {
		if obj == nil {
			t.Errorf("Unexpected nil in List()")
			continue
		}
		is.Find(t, obj, precision)
	}
	is.Done(t)
	// Don't notice an error when listing an empty directory
	if len(items) == 0 && oldErrors == 0 && fs.Stats.GetErrors() == 1 {
		fs.Stats.ResetErrors()
	}
}

// Checks the fs to see if it has the expected contents
func CheckListing(t *testing.T, f fs.Fs, items []Item) {
	precision := f.Precision()
	CheckListingWithPrecision(t, f, items, precision)
}

// Parse a time string or explode
func Time(timeString string) time.Time {
	t, err := time.Parse(time.RFC3339Nano, timeString)
	if err != nil {
		log.Fatalf("Failed to parse time %q: %v", timeString, err)
	}
	return t
}

// Create a random string
func RandomString(n int) string {
	source := "abcdefghijklmnopqrstuvwxyz0123456789"
	out := make([]byte, n)
	for i := range out {
		out[i] = source[rand.Intn(len(source))]
	}
	return string(out)
}

// Creates a temporary directory name for local remotes
func LocalRemote() (path string, err error) {
	path, err = ioutil.TempDir("", "rclone")
	if err == nil {
		// Now remove the directory
		err = os.Remove(path)
	}
	path = filepath.ToSlash(path)
	return
}

// Make a random bucket or subdirectory name
//
// Returns a random remote name plus the leaf name
func RandomRemoteName(remoteName string) (string, string, error) {
	var err error
	var leafName string

	// Make a directory if remote name is null
	if remoteName == "" {
		remoteName, err = LocalRemote()
		if err != nil {
			return "", "", err
		}
	} else {
		if !strings.HasSuffix(remoteName, ":") {
			remoteName += "/"
		}
		leafName = RandomString(32)
		remoteName += leafName
	}
	return remoteName, leafName, nil
}

// Make a random bucket or subdirectory on the remote
//
// Call the finalise function returned to Purge the fs at the end (and
// the parent if necessary)
func RandomRemote(remoteName string, subdir bool) (fs.Fs, func(), error) {
	var err error
	var parentRemote fs.Fs

	remoteName, _, err = RandomRemoteName(remoteName)
	if err != nil {
		return nil, nil, err
	}

	if subdir {
		parentRemote, err = fs.NewFs(remoteName)
		if err != nil {
			return nil, nil, err
		}
		remoteName += "/" + RandomString(8)
	}

	remote, err := fs.NewFs(remoteName)
	if err != nil {
		return nil, nil, err
	}

	finalise := func() {
		_ = fs.Purge(remote) // ignore error
		if parentRemote != nil {
			err = fs.Purge(parentRemote) // ignore error
			if err != nil {
				log.Printf("Failed to purge %v: %v", parentRemote, err)
			}
		}
	}

	return remote, finalise, nil
}

func TestMkdir(t *testing.T, remote fs.Fs) {
	err := fs.Mkdir(remote)
	if err != nil {
		t.Fatalf("Mkdir failed: %v", err)
	}
	CheckListing(t, remote, []Item{})
}

func TestPurge(t *testing.T, remote fs.Fs) {
	err := fs.Purge(remote)
	if err != nil {
		t.Fatalf("Purge failed: %v", err)
	}
	CheckListing(t, remote, []Item{})
}

func TestRmdir(t *testing.T, remote fs.Fs) {
	err := fs.Rmdir(remote)
	if err != nil {
		t.Fatalf("Rmdir failed: %v", err)
	}
}
<commit_msg>Retry listing in tests to work around eventual consistency<commit_after>// Utilities for testing the Fs
package fstest

// FIXME put name of test FS in Fs structure

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

	"github.com/ncw/rclone/fs"
)

// Seed the random number generator
func init() {
	rand.Seed(time.Now().UnixNano())

}

// Represents an item for checking
type Item struct {
	Path    string
	Md5sum  string
	ModTime time.Time
	Size    int64
	WinPath string
}

// Checks the times are equal within the precision, returns the delta and a flag
func CheckTimeEqualWithPrecision(t0, t1 time.Time, precision time.Duration) (time.Duration, bool) {
	dt := t0.Sub(t1)
	if dt >= precision || dt <= -precision {
		return dt, false
	}
	return dt, true
}

// check the mod time to the given precision
func (i *Item) CheckModTime(t *testing.T, obj fs.Object, modTime time.Time, precision time.Duration) {
	dt, ok := CheckTimeEqualWithPrecision(modTime, i.ModTime, precision)
	if !ok {
		t.Errorf("%s: Modification time difference too big |%s| > %s (%s vs %s) (precision %s)", obj.Remote(), dt, precision, modTime, i.ModTime, precision)
	}
}

func (i *Item) Check(t *testing.T, obj fs.Object, precision time.Duration) {
	if obj == nil {
		t.Fatalf("Object is nil")
	}
	// Check attributes
	Md5sum, err := obj.Md5sum()
	if err != nil {
		t.Fatalf("Failed to read md5sum for %q: %v", obj.Remote(), err)
	}
	if !fs.Md5sumsEqual(i.Md5sum, Md5sum) {
		t.Errorf("%s: Md5sum incorrect - expecting %q got %q", obj.Remote(), i.Md5sum, Md5sum)
	}
	if i.Size != obj.Size() {
		t.Errorf("%s: Size incorrect - expecting %d got %d", obj.Remote(), i.Size, obj.Size())
	}
	i.CheckModTime(t, obj, obj.ModTime(), precision)
}

// Represents all items for checking
type Items struct {
	byName    map[string]*Item
	byNameAlt map[string]*Item
	items     []Item
}

// Make an Items
func NewItems(items []Item) *Items {
	is := &Items{
		byName:    make(map[string]*Item),
		byNameAlt: make(map[string]*Item),
		items:     items,
	}
	// Fill up byName
	for i := range items {
		is.byName[items[i].Path] = &items[i]
		is.byNameAlt[items[i].WinPath] = &items[i]
	}
	return is
}

// Check off an item
func (is *Items) Find(t *testing.T, obj fs.Object, precision time.Duration) {
	i, ok := is.byName[obj.Remote()]
	if !ok {
		i, ok = is.byNameAlt[obj.Remote()]
		if !ok {
			t.Errorf("Unexpected file %q", obj.Remote())
			return
		}
	}
	delete(is.byName, i.Path)
	delete(is.byName, i.WinPath)
	i.Check(t, obj, precision)
}

// Check all done
func (is *Items) Done(t *testing.T) {
	if len(is.byName) != 0 {
		for name := range is.byName {
			log.Printf("Not found %q", name)
		}
		t.Errorf("%d objects not found", len(is.byName))
	}
}

// Checks the fs to see if it has the expected contents
func CheckListingWithPrecision(t *testing.T, f fs.Fs, items []Item, precision time.Duration) {
	is := NewItems(items)
	oldErrors := fs.Stats.GetErrors()
	var objs []fs.Object
	for i := 1; i <= 5; i++ {
		objs = nil
		for obj := range f.List() {
			objs = append(objs, obj)
		}
		if len(objs) == len(items) {
			break
		}
		t.Logf("Sleeping for 1 second for list eventual consistency: %d/5", i)
		time.Sleep(1 * time.Second)
	}
	for _, obj := range objs {
		if obj == nil {
			t.Errorf("Unexpected nil in List()")
			continue
		}
		is.Find(t, obj, precision)
	}
	is.Done(t)
	// Don't notice an error when listing an empty directory
	if len(items) == 0 && oldErrors == 0 && fs.Stats.GetErrors() == 1 {
		fs.Stats.ResetErrors()
	}
}

// Checks the fs to see if it has the expected contents
func CheckListing(t *testing.T, f fs.Fs, items []Item) {
	precision := f.Precision()
	CheckListingWithPrecision(t, f, items, precision)
}

// Parse a time string or explode
func Time(timeString string) time.Time {
	t, err := time.Parse(time.RFC3339Nano, timeString)
	if err != nil {
		log.Fatalf("Failed to parse time %q: %v", timeString, err)
	}
	return t
}

// Create a random string
func RandomString(n int) string {
	source := "abcdefghijklmnopqrstuvwxyz0123456789"
	out := make([]byte, n)
	for i := range out {
		out[i] = source[rand.Intn(len(source))]
	}
	return string(out)
}

// Creates a temporary directory name for local remotes
func LocalRemote() (path string, err error) {
	path, err = ioutil.TempDir("", "rclone")
	if err == nil {
		// Now remove the directory
		err = os.Remove(path)
	}
	path = filepath.ToSlash(path)
	return
}

// Make a random bucket or subdirectory name
//
// Returns a random remote name plus the leaf name
func RandomRemoteName(remoteName string) (string, string, error) {
	var err error
	var leafName string

	// Make a directory if remote name is null
	if remoteName == "" {
		remoteName, err = LocalRemote()
		if err != nil {
			return "", "", err
		}
	} else {
		if !strings.HasSuffix(remoteName, ":") {
			remoteName += "/"
		}
		leafName = RandomString(32)
		remoteName += leafName
	}
	return remoteName, leafName, nil
}

// Make a random bucket or subdirectory on the remote
//
// Call the finalise function returned to Purge the fs at the end (and
// the parent if necessary)
func RandomRemote(remoteName string, subdir bool) (fs.Fs, func(), error) {
	var err error
	var parentRemote fs.Fs

	remoteName, _, err = RandomRemoteName(remoteName)
	if err != nil {
		return nil, nil, err
	}

	if subdir {
		parentRemote, err = fs.NewFs(remoteName)
		if err != nil {
			return nil, nil, err
		}
		remoteName += "/" + RandomString(8)
	}

	remote, err := fs.NewFs(remoteName)
	if err != nil {
		return nil, nil, err
	}

	finalise := func() {
		_ = fs.Purge(remote) // ignore error
		if parentRemote != nil {
			err = fs.Purge(parentRemote) // ignore error
			if err != nil {
				log.Printf("Failed to purge %v: %v", parentRemote, err)
			}
		}
	}

	return remote, finalise, nil
}

func TestMkdir(t *testing.T, remote fs.Fs) {
	err := fs.Mkdir(remote)
	if err != nil {
		t.Fatalf("Mkdir failed: %v", err)
	}
	CheckListing(t, remote, []Item{})
}

func TestPurge(t *testing.T, remote fs.Fs) {
	err := fs.Purge(remote)
	if err != nil {
		t.Fatalf("Purge failed: %v", err)
	}
	CheckListing(t, remote, []Item{})
}

func TestRmdir(t *testing.T, remote fs.Fs) {
	err := fs.Rmdir(remote)
	if err != nil {
		t.Fatalf("Rmdir failed: %v", err)
	}
}
<|endoftext|>


<commit_before>// Call a function dynamically.
//
// The constraint will be checked at the runtime.
//
package function

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

var (
	TOTALGO uint = 9900

	NotFuncError  = errors.New("The first argument is not the function")
	ArgsNumError  = errors.New("The number of the argument is incorrect")
	ArgsTypeError = errors.New("The type of the argument is incorrect")
)

func Valid(f interface{}, args ...interface{}) (vf reflect.Value, vargs []reflect.Value, err error) {
	vf = reflect.ValueOf(f)
	if vf.Kind() != reflect.Func {
		return reflect.ValueOf(nil), nil, NotFuncError
	}

	tf := vf.Type()
	_len := len(args)
	if tf.NumIn() != _len {
		return reflect.ValueOf(nil), nil, ArgsNumError
	}

	vargs = make([]reflect.Value, _len)
	for i := 0; i < _len; i++ {
		if tf.In(i).Kind() != reflect.TypeOf(args[i]).Kind() {
			return reflect.ValueOf(nil), nil, ArgsTypeError
		}
		vargs[i] = reflect.ValueOf(args[i])
	}
	return vf, vargs, nil
}

func Call(f interface{}, args ...interface{}) (results []interface{}, err error) {
	defer func() {
		if _err := recover(); _err != nil {
			err = errors.New(fmt.Sprintf("%v", _err))
			results = nil
		}
	}()

	vf, vargs, _err := Valid(f, args...)
	if _err != nil {
		return nil, _err
	}
	ret := vf.Call(vargs)
	_len := len(ret)
	results = make([]interface{}, _len)
	for i := 0; i < _len; i++ {
		results[i] = ret[i].Interface()
	}
	return
}
<commit_msg>Fix a bug.<commit_after>// Call a function dynamically.
//
// The constraint will be checked at the runtime.
//
package function

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

var (
	TOTALGO uint = 9900

	NotFuncError  = errors.New("The first argument is not the function")
	ArgsNumError  = errors.New("The number of the argument is incorrect")
	ArgsTypeError = errors.New("The type of the argument is incorrect")
)

func Valid(f interface{}, args ...interface{}) (vf reflect.Value, vargs []reflect.Value, err error) {
	vf = reflect.ValueOf(f)
	if vf.Kind() != reflect.Func {
		return reflect.ValueOf(nil), nil, NotFuncError
	}

	tf := vf.Type()
	_len := len(args)
	if tf.NumIn() != _len {
		return reflect.ValueOf(nil), nil, ArgsNumError
	}

	vargs = make([]reflect.Value, _len)
	for i := 0; i < _len; i++ {
		typ := tf.In(i).Kind()
		if (typ != reflect.Interface) && (typ != reflect.TypeOf(args[i]).Kind()) {
			return reflect.ValueOf(nil), nil, ArgsTypeError
		}
		vargs[i] = reflect.ValueOf(args[i])
	}
	return vf, vargs, nil
}

func Call(f interface{}, args ...interface{}) (results []interface{}, err error) {
	defer func() {
		if _err := recover(); _err != nil {
			err = errors.New(fmt.Sprintf("%v", _err))
			results = nil
		}
	}()

	vf, vargs, _err := Valid(f, args...)
	if _err != nil {
		return nil, _err
	}
	ret := vf.Call(vargs)
	_len := len(ret)
	results = make([]interface{}, _len)
	for i := 0; i < _len; i++ {
		results[i] = ret[i].Interface()
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/getlantern/systray"
	"github.com/sc7639/mysql-notifier/icon"

	toast "gopkg.in/toast.v1"
	yaml "gopkg.in/yaml.v2"
)

func readSettings(readSettings chan settings, mysqlInstances chan mysql) (bool, error) { // Read settings
	data, err := ioutil.ReadFile(settingsPath)
	if err != nil {
		log.Printf("Failed to read settings: %s\n", err.Error())
		return false, err
	}

	s := settings{}
	err = yaml.Unmarshal(data, &s)
	if err != nil {
		log.Printf("Failed to read settings: %s\n", err.Error())
		return false, err
	}

	mysqlInstances <- s.Mysql
	readSettings <- s

	return true, nil
}

func openSettings(editor string) {
	if _, err := os.Stat(settingsPath); os.IsNotExist(err) { // If settings file doesn't exist, create it and add min settings
		fp, err := os.OpenFile(settingsPath, os.O_RDWR|os.O_CREATE, 0755)
		if err != nil {
			log.Printf("Failed to open / create settings.yml: %s\n", err.Error())
			return
		}

		defer fp.Close()

		ys, err := yaml.Marshal(minSettings)
		if err != nil {
			log.Printf("Failed to create yml: %s\n", err.Error())
		}

		_, err = fp.Write(ys)
		if err != nil {
			log.Printf("Failed to write to settings.yml: %s\n", err.Error())
		}
	}

	cmd := exec.Command(editor, settingsPath)
	err := cmd.Start()
	if err != nil {
		log.Printf("Failed to open settings: %s\n", err.Error())
	} else {
		log.Println("Open Settings")
	}
}

func openMysqlCMD(details map[string]string) {
	// Parse ip address
	var host string
	if _, ok := details["host"]; !ok {
		host = "127.0.0.1"
	} else {
		host = details["host"]
	}

	// Parse port
	var port string
	if _, ok := details["port"]; !ok {
		port = "3306"
	} else {
		port = details["port"]
	}

	var cmd *exec.Cmd
	if passwd := details["password"]; passwd != "" { // If password is set then execute command with password
		log.Println("Password")
		cmd = exec.Command("cmd", "/c", "start", "mysql", "-h"+host, "-u"+details["username"], "-p"+details["password"], "-P"+port)
	} else {
		log.Println("No Password")
		cmd = exec.Command("cmd", "/c", "start", "mysql", "-h"+host, "-u"+details["username"], "-P"+port)
	}

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.Fatal(err)
	}

	stderr, err := cmd.StderrPipe()
	if err != nil {
		log.Fatal(err)
	}

	err = cmd.Start()
	if err != nil {
		log.Printf("Failed to open mysql command line: %s", err)
	} else {
		log.Println("Open mysql cmd")
	}

	out, _ := ioutil.ReadAll(stdout)
	sterr, _ := ioutil.ReadAll(stderr)

	// log.Println("cmd /c start mysql -h"+host+" -u"+details["username"], " -P"+port)

	if string(out) != "" {
		log.Printf("Out pipe: %s\n", out)
	}
	if string(sterr) != "" {
		log.Printf("Err pipe: %s\n", sterr)
	}
}

func updateItem(status chan bool, title string, item *systray.MenuItem, appID string) { // On check of database connection update menu item title
	for live := range status {
		// log.Printf("Checked %s status: %v\n", title, live)
		if live {
			item.SetTitle(title + " - Live")
		} else {
			item.SetTitle(title + " - Dead")
		}
	}
}

// For each db status check, check check if the the status has changed and if so
func notifications(statuses []chan bool, appID string, connections mysql, duration time.Duration) {
	var prevStatuses = make([]bool, len(statuses))
	var changed = make([]bool, len(statuses))
	for i := range prevStatuses { // Initialize prevStatuses as true
		prevStatuses[i] = true
	}

	var notification toast.Notification
	var timer *time.Timer
	var connectionKeys []string
	var timerStarted bool
	sendNotification := make(chan []bool)

	for key := range connections {
		connectionKeys = append(connectionKeys, key)
	}

	for i, status := range statuses {
		go func(status chan bool, i int) {
			for live := range status {
				if !timerStarted {
					timer = time.NewTimer(duration)
					timerStarted = true
					log.Printf("duration: %v\n", duration)

					go func(sendNot chan<- []bool) {
						<-timer.C
						changedArr := make([]bool, len(changed))
						copy(changedArr, changed)
						sendNot <- changedArr

						timerStarted = false
						for i := range changed {
							changed[i] = false
						}
					}(sendNotification)
				}

				if prevStatuses[i] != live {
					changed[i] = true
					prevStatuses[i] = live
				}

			}
		}(status, i)
	}

	go func(sendNot <-chan []bool, appID string) {
		for {
			changedItems := <-sendNot
			changedIndices := make(map[string][]int)

			for i, changed := range changedItems {
				if changed {
					if prevStatuses[i] {
						changedIndices["live"] = append(changedIndices["live"], i)
					} else {
						changedIndices["dead"] = append(changedIndices["dead"], i)
					}
				}
			}

			// Create message and title depending on how many of the connections are down / up
			for typ := range changedIndices {
				title := ""
				message := "Failed to ping"
				numType := len(changedIndices[typ])
				icon := path + "\\images\\icon-red.png"
				// actions := []toast.Action{}

				if typ == "live" {
					message = "Successfully pinged "
					icon = path + "\\images\\icon.png"
				}

				if numType > 1 {
					title = strconv.Itoa(numType) + " Connections are " + typ
					title = strings.Title(title)
					message += "\n"
					for i := range changedIndices[typ] {
						message += strings.Title(connectionKeys[i]) + ", "
					}
					message += " databases"
				} else {
					title = connectionKeys[changedIndices[typ][0]] + " - " + typ
					title = strings.Title(title)
					message += title + " database"
					// actions = []toast.Action{
					// 	{Type: "protocol", Label: "SSH To Server", Arguments: ""},
					// }
				}

				if runtime.GOOS == "windows" {
					notification = toast.Notification{
						AppID:    appID,
						Icon:     icon,
						Title:    title,
						Message:  message,
						Duration: "long",
						// Actions: actions,
					}
				}

				err := notification.Push()
				if err != nil {
					log.Fatalln(err)
				}

			}
		}
	}(sendNotification, appID)

}

func updateIcon(statuses []chan bool) { // On check of database connection update menu icon
	var allLive = true
	var i = 0
	for _, status := range statuses { // For each status
		go func(status chan bool) {
			for live := range status { // Wait to receive information on channel
				i++
				// log.Printf("status: %v, i: %v, len(statuses): %v", live, i, len(statuses))
				if !live {
					allLive = false
				}

				if i == len(statuses) { // After receiving on all channels in the array
					if allLive {
						systray.SetIcon(icon.Green)
						systray.SetTooltip("All Ok")
					} else {
						systray.SetIcon(icon.Red)
						systray.SetTooltip("Connection Down")

						allLive = true
					}

					i = 0
				}
			}
		}(status)
	}
}
<commit_msg>Fixed database dead notification text<commit_after>package main

import (
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/getlantern/systray"
	"github.com/sc7639/mysql-notifier/icon"

	toast "gopkg.in/toast.v1"
	yaml "gopkg.in/yaml.v2"
)

func readSettings(readSettings chan settings, mysqlInstances chan mysql) (bool, error) { // Read settings
	data, err := ioutil.ReadFile(settingsPath)
	if err != nil {
		log.Printf("Failed to read settings: %s\n", err.Error())
		return false, err
	}

	s := settings{}
	err = yaml.Unmarshal(data, &s)
	if err != nil {
		log.Printf("Failed to read settings: %s\n", err.Error())
		return false, err
	}

	mysqlInstances <- s.Mysql
	readSettings <- s

	return true, nil
}

func openSettings(editor string) {
	if _, err := os.Stat(settingsPath); os.IsNotExist(err) { // If settings file doesn't exist, create it and add min settings
		fp, err := os.OpenFile(settingsPath, os.O_RDWR|os.O_CREATE, 0755)
		if err != nil {
			log.Printf("Failed to open / create settings.yml: %s\n", err.Error())
			return
		}

		defer fp.Close()

		ys, err := yaml.Marshal(minSettings)
		if err != nil {
			log.Printf("Failed to create yml: %s\n", err.Error())
		}

		_, err = fp.Write(ys)
		if err != nil {
			log.Printf("Failed to write to settings.yml: %s\n", err.Error())
		}
	}

	cmd := exec.Command(editor, settingsPath)
	err := cmd.Start()
	if err != nil {
		log.Printf("Failed to open settings: %s\n", err.Error())
	} else {
		log.Println("Open Settings")
	}
}

func openMysqlCMD(details map[string]string) {
	// Parse ip address
	var host string
	if _, ok := details["host"]; !ok {
		host = "127.0.0.1"
	} else {
		host = details["host"]
	}

	// Parse port
	var port string
	if _, ok := details["port"]; !ok {
		port = "3306"
	} else {
		port = details["port"]
	}

	var cmd *exec.Cmd
	if passwd := details["password"]; passwd != "" { // If password is set then execute command with password
		log.Println("Password")
		cmd = exec.Command("cmd", "/c", "start", "mysql", "-h"+host, "-u"+details["username"], "-p"+details["password"], "-P"+port)
	} else {
		log.Println("No Password")
		cmd = exec.Command("cmd", "/c", "start", "mysql", "-h"+host, "-u"+details["username"], "-P"+port)
	}

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.Fatal(err)
	}

	stderr, err := cmd.StderrPipe()
	if err != nil {
		log.Fatal(err)
	}

	err = cmd.Start()
	if err != nil {
		log.Printf("Failed to open mysql command line: %s", err)
	} else {
		log.Println("Open mysql cmd")
	}

	out, _ := ioutil.ReadAll(stdout)
	sterr, _ := ioutil.ReadAll(stderr)

	// log.Println("cmd /c start mysql -h"+host+" -u"+details["username"], " -P"+port)

	if string(out) != "" {
		log.Printf("Out pipe: %s\n", out)
	}
	if string(sterr) != "" {
		log.Printf("Err pipe: %s\n", sterr)
	}
}

func updateItem(status chan bool, title string, item *systray.MenuItem, appID string) { // On check of database connection update menu item title
	for live := range status {
		// log.Printf("Checked %s status: %v\n", title, live)
		if live {
			item.SetTitle(title + " - Live")
		} else {
			item.SetTitle(title + " - Dead")
		}
	}
}

// For each db status check, check check if the the status has changed and if so
func notifications(statuses []chan bool, appID string, connections mysql, duration time.Duration) {
	var prevStatuses = make([]bool, len(statuses))
	var changed = make([]bool, len(statuses))
	for i := range prevStatuses { // Initialize prevStatuses as true
		prevStatuses[i] = true
	}

	var notification toast.Notification
	var timer *time.Timer
	var connectionKeys []string
	var timerStarted bool
	sendNotification := make(chan []bool)

	for key := range connections {
		connectionKeys = append(connectionKeys, key)
	}

	for i, status := range statuses {
		go func(status chan bool, i int) {
			for live := range status {
				if !timerStarted {
					timer = time.NewTimer(duration)
					timerStarted = true
					log.Printf("duration: %v\n", duration)

					go func(sendNot chan<- []bool) {
						<-timer.C
						changedArr := make([]bool, len(changed))
						copy(changedArr, changed)
						sendNot <- changedArr

						timerStarted = false
						for i := range changed {
							changed[i] = false
						}
					}(sendNotification)
				}

				if prevStatuses[i] != live {
					changed[i] = true
					prevStatuses[i] = live
				}

			}
		}(status, i)
	}

	go func(sendNot <-chan []bool, appID string) {
		for {
			changedItems := <-sendNot
			changedIndices := make(map[string][]int)

			for i, changed := range changedItems {
				if changed {
					if prevStatuses[i] {
						changedIndices["live"] = append(changedIndices["live"], i)
					} else {
						changedIndices["dead"] = append(changedIndices["dead"], i)
					}
				}
			}

			// Create message and title depending on how many of the connections are down / up
			for typ := range changedIndices {
				title := ""
				message := "Failed to ping "
				numType := len(changedIndices[typ])
				icon := path + "\\images\\icon-red.png"
				// actions := []toast.Action{}

				if typ == "live" {
					message = "Successfully pinged "
					icon = path + "\\images\\icon.png"
				}

				if numType > 1 {
					title = strconv.Itoa(numType) + " Connections are " + typ
					title = strings.Title(title)
					message += "\n"
					for i := range changedIndices[typ] {
						message += strings.Title(connectionKeys[i]) + ", "
					}
					message += " databases"
				} else {
					title = connectionKeys[changedIndices[typ][0]]
					message += strings.Title(title) + " database"
					title = strings.Title(title + " - " + typ)

					// actions = []toast.Action{
					// 	{Type: "protocol", Label: "SSH To Server", Arguments: ""},
					// }
				}

				if runtime.GOOS == "windows" {
					notification = toast.Notification{
						AppID:    appID,
						Icon:     icon,
						Title:    title,
						Message:  message,
						Duration: "long",
						// Actions: actions,
					}
				}

				err := notification.Push()
				if err != nil {
					log.Fatalln(err)
				}

			}
		}
	}(sendNotification, appID)

}

func updateIcon(statuses []chan bool) { // On check of database connection update menu icon
	var allLive = true
	var i = 0
	for _, status := range statuses { // For each status
		go func(status chan bool) {
			for live := range status { // Wait to receive information on channel
				i++
				// log.Printf("status: %v, i: %v, len(statuses): %v", live, i, len(statuses))
				if !live {
					allLive = false
				}

				if i == len(statuses) { // After receiving on all channels in the array
					if allLive {
						systray.SetIcon(icon.Green)
						systray.SetTooltip("All Ok")
					} else {
						systray.SetIcon(icon.Red)
						systray.SetTooltip("Connection Down")

						allLive = true
					}

					i = 0
				}
			}
		}(status)
	}
}
<|endoftext|>


<commit_before>/*

cli is a simple package to allow a game to render to the screen and be
interacted with. It's intended primarily as a tool to diagnose and play around
with a game while its moves and logic are being defined.

*/
package cli

import (
	"github.com/jkomoros/boardgame"
	"github.com/nsf/termbox-go"
)

//Controller is the primary type of the package.
type Controller struct {
	game *boardgame.Game
}

func NewController(game *boardgame.Game) *Controller {
	return &Controller{
		game: game,
	}
}

//Once the controller is set up, call MainLoop. It will block until it is time
//to exit.
func (c *Controller) MainLoop() {

	termbox.Init()

	defer termbox.Close()

	c.draw()

	for {
		evt := termbox.PollEvent()

		switch evt.Type {
		case termbox.EventKey:
			switch evt.Key {
			case termbox.KeyCtrlC:
				return
			}
		}

		c.draw()

	}

}

//draw draws the entire app to the screen
func (c *Controller) draw() {

	clearScreen()

	//TODO: do real drawing
	x := 0
	y := 0
	for _, ch := range "Hello World! You can kill with Ctrl-C" {
		termbox.SetCell(x, y, ch, termbox.ColorWhite, termbox.ColorBlack)
		x++
	}

	termbox.Flush()
}

func clearScreen() {
	width, height := termbox.Size()
	for x := 0; x < width; x++ {
		for y := 0; y < height; y++ {
			termbox.SetCell(x, y, ' ', termbox.ColorDefault, termbox.ColorDefault)
		}
	}
}
<commit_msg>Instead of printing a random message, print the current json state<commit_after>/*

cli is a simple package to allow a game to render to the screen and be
interacted with. It's intended primarily as a tool to diagnose and play around
with a game while its moves and logic are being defined.

*/
package cli

import (
	"github.com/jkomoros/boardgame"
	"github.com/nsf/termbox-go"
	"strings"
)

//Controller is the primary type of the package.
type Controller struct {
	game *boardgame.Game
}

func NewController(game *boardgame.Game) *Controller {
	return &Controller{
		game: game,
	}
}

//Once the controller is set up, call MainLoop. It will block until it is time
//to exit.
func (c *Controller) MainLoop() {

	termbox.Init()

	defer termbox.Close()

	c.draw()

	for {
		evt := termbox.PollEvent()

		switch evt.Type {
		case termbox.EventKey:
			switch evt.Key {
			case termbox.KeyCtrlC:
				return
			}
		}

		c.draw()

	}

}

//draw draws the entire app to the screen
func (c *Controller) draw() {

	clearScreen()

	c.drawJSON()

	termbox.Flush()
}

func clearScreen() {
	width, height := termbox.Size()
	for x := 0; x < width; x++ {
		for y := 0; y < height; y++ {
			termbox.SetCell(x, y, ' ', termbox.ColorDefault, termbox.ColorDefault)
		}
	}
}

//Draws the JSON output of the current state to the screen
func (c *Controller) drawJSON() {
	x := 0
	y := 0

	json := string(boardgame.Serialize(c.game.State.JSON()))

	for _, line := range strings.Split(json, "\n") {

		x = 0

		for _, ch := range line {
			termbox.SetCell(x, y, ch, termbox.ColorWhite, termbox.ColorBlack)
			x++
		}

		y++
	}

}
<|endoftext|>


<commit_before>package api

import (
	"bytes"
	"context"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"io"
	"net"
	"net/http"
	"net/http/httptrace"
	"net/url"
	"time"

	jwt "github.com/dgrijalva/jwt-go"
	"github.com/pkg/errors"
	uuid "github.com/satori/go.uuid"
	"github.com/sirupsen/logrus"

	"github.com/netlify/gotrue/conf"
	"github.com/netlify/gotrue/models"
	"github.com/netlify/gotrue/storage"
)

type HookEvent string

const (
	headerHookSignature = "x-webhook-signature"
	defaultHookRetries  = 3
	gotrueIssuer        = "gotrue"
	ValidateEvent       = "validate"
	SignupEvent         = "signup"
	LoginEvent          = "login"
)

var defaultTimeout time.Duration = time.Second * 5

type webhookClaims struct {
	jwt.StandardClaims
	SHA256 string `json:"sha256"`
}

type Webhook struct {
	*conf.WebhookConfig

	instanceID uuid.UUID
	jwtSecret  string
	claims     jwt.Claims
	payload    []byte
	headers    map[string]string
}

type WebhookResponse struct {
	AppMetaData  map[string]interface{} `json:"app_metadata,omitempty"`
	UserMetaData map[string]interface{} `json:"user_metadata,omitempty"`
}

func (w *Webhook) trigger() (io.ReadCloser, error) {
	timeout := defaultTimeout
	if w.TimeoutSec > 0 {
		timeout = time.Duration(w.TimeoutSec) * time.Second
	}

	if w.Retries == 0 {
		w.Retries = defaultHookRetries
	}

	client := http.Client{
		Timeout: timeout,
	}

	hooklog := logrus.WithFields(logrus.Fields{
		"component":   "webhook",
		"url":         w.URL,
		"signed":      w.jwtSecret != "",
		"instance_id": w.instanceID,
	})

	for i := 0; i < w.Retries; i++ {
		hooklog = hooklog.WithField("attempt", i+1)
		hooklog.Info("Starting to perform signup hook request")

		req, err := http.NewRequest(http.MethodPost, w.URL, bytes.NewBuffer(w.payload))
		if err != nil {
			return nil, internalServerError("Failed to make request object").WithInternalError(err)
		}
		req.Header.Set("Content-Type", "application/json")
		watcher, req := watchForConnection(req)

		if w.jwtSecret != "" {
			header, jwtErr := w.generateSignature()
			if jwtErr != nil {
				return nil, jwtErr
			}
			req.Header.Set(headerHookSignature, header)
		}

		start := time.Now()
		rsp, err := client.Do(req)
		if err != nil {
			if terr, ok := err.(net.Error); ok && terr.Timeout() {
				// timed out - try again?
				if i == w.Retries-1 {
					closeBody(rsp)
					return nil, httpError(http.StatusGatewayTimeout, "Failed to perform webhook in time frame (%d seconds)", timeout.Seconds())
				}
				hooklog.Info("Request timed out")
				continue
			} else if watcher.gotConn {
				closeBody(rsp)
				return nil, internalServerError("Failed to trigger webhook to %s", w.URL).WithInternalError(err)
			} else {
				closeBody(rsp)
				return nil, httpError(http.StatusBadGateway, "Failed to connect to %s", w.URL)
			}
		}
		dur := time.Since(start)
		rspLog := hooklog.WithFields(logrus.Fields{
			"status_code": rsp.StatusCode,
			"dur":         dur.Nanoseconds(),
		})
		switch rsp.StatusCode {
		case http.StatusOK, http.StatusNoContent, http.StatusAccepted:
			rspLog.Infof("Finished processing webhook in %s", dur)
			var body io.ReadCloser
			if rsp.ContentLength > 0 {
				body = rsp.Body
			}
			return body, nil
		default:
			rspLog.Infof("Bad response for webhook %d in %s", rsp.StatusCode, dur)
		}
	}

	hooklog.Infof("Failed to process webhook for %s after %d attempts", w.URL, w.Retries)
	return nil, unprocessableEntityError("Failed to handle signup webhook")
}

func (w *Webhook) generateSignature() (string, error) {
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, w.claims)
	tokenString, err := token.SignedString([]byte(w.jwtSecret))
	if err != nil {
		return "", internalServerError("Failed build signing string").WithInternalError(err)
	}
	return tokenString, nil
}

func closeBody(rsp *http.Response) {
	if rsp != nil && rsp.Body != nil {
		rsp.Body.Close()
	}
}

func triggerHook(ctx context.Context, conn *storage.Connection, event HookEvent, user *models.User, instanceID uuid.UUID, config *conf.Configuration) error {
	var hookURL *url.URL
	if config.Webhook.URL != "" {
		var err error
		hookURL, err = url.Parse(config.Webhook.URL)
		if err != nil {
			return errors.Wrapf(err, "Failed to parse Webhook URL")
		}
	}

	if hookURL == nil {
		fun := getFunctionHooks(ctx)
		if fun == nil {
			return nil
		}

		if eventHookURL, ok := fun[string(event)]; ok {
			var err error
			hookURL, err = url.Parse(eventHookURL)
			if err != nil {
				return errors.Wrapf(err, "Failed to parse Event Function Hook URL")
			}
		} else {
			// abort hook call if there are no functions for this event
			return nil
		}
	}

	if !hookURL.IsAbs() {
		siteURL, err := url.Parse(config.SiteURL)
		if err != nil {
			return errors.Wrapf(err, "Failed to parse Site URL")
		}
		hookURL.Scheme = siteURL.Scheme
		hookURL.Host = siteURL.Host
		hookURL.User = siteURL.User
	}

	payload := struct {
		Event      HookEvent    `json:"event"`
		InstanceID uuid.UUID    `json:"instance_id,omitempty"`
		User       *models.User `json:"user"`
	}{
		Event:      event,
		InstanceID: instanceID,
		User:       user,
	}
	data, err := json.Marshal(&payload)
	if err != nil {
		return internalServerError("Failed to serialize the data for signup webhook").WithInternalError(err)
	}

	sha, err := checksum(data)
	if err != nil {
		return internalServerError("Failed to checksum the data for signup webhook").WithInternalError(err)
	}

	claims := webhookClaims{
		StandardClaims: jwt.StandardClaims{
			IssuedAt: time.Now().Unix(),
			Subject:  instanceID.String(),
			Issuer:   gotrueIssuer,
		},
		SHA256: sha,
	}

	w := Webhook{
		WebhookConfig: &config.Webhook,
		jwtSecret:     config.Webhook.Secret,
		instanceID:    instanceID,
		claims:        claims,
		payload:       data,
	}

	w.URL = hookURL.String()

	body, err := w.trigger()
	defer func() {
		if body != nil {
			body.Close()
		}
	}()
	if err == nil && body != nil {
		webhookRsp := &WebhookResponse{}
		decoder := json.NewDecoder(body)
		if err = decoder.Decode(webhookRsp); err != nil {
			return internalServerError("Webhook returned malformed JSON: %v", err).WithInternalError(err)
		}
		return conn.Transaction(func(tx *storage.Connection) error {
			if webhookRsp.UserMetaData != nil {
				user.UserMetaData = nil
				if terr := user.UpdateUserMetaData(tx, webhookRsp.UserMetaData); terr != nil {
					return terr
				}
			}
			if webhookRsp.AppMetaData != nil {
				user.AppMetaData = nil
				if terr := user.UpdateAppMetaData(tx, webhookRsp.AppMetaData); terr != nil {
					return terr
				}
			}
			return nil
		})
	}
	return err
}

func watchForConnection(req *http.Request) (*connectionWatcher, *http.Request) {
	w := new(connectionWatcher)
	t := &httptrace.ClientTrace{
		GotConn: w.GotConn,
	}

	req = req.WithContext(httptrace.WithClientTrace(req.Context(), t))
	return w, req
}

func checksum(data []byte) (string, error) {
	sha := sha256.New()
	_, err := sha.Write(data)
	if err != nil {
		return "", err
	}

	return hex.EncodeToString(sha.Sum(nil)), nil
}

type connectionWatcher struct {
	gotConn bool
}

func (c *connectionWatcher) GotConn(_ httptrace.GotConnInfo) {
	c.gotConn = true
}
<commit_msg>Fix hook signature for function hooks<commit_after>package api

import (
	"bytes"
	"context"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"io"
	"net"
	"net/http"
	"net/http/httptrace"
	"net/url"
	"time"

	jwt "github.com/dgrijalva/jwt-go"
	"github.com/pkg/errors"
	uuid "github.com/satori/go.uuid"
	"github.com/sirupsen/logrus"

	"github.com/netlify/gotrue/conf"
	"github.com/netlify/gotrue/models"
	"github.com/netlify/gotrue/storage"
)

type HookEvent string

const (
	headerHookSignature = "x-webhook-signature"
	defaultHookRetries  = 3
	gotrueIssuer        = "gotrue"
	ValidateEvent       = "validate"
	SignupEvent         = "signup"
	LoginEvent          = "login"
)

var defaultTimeout time.Duration = time.Second * 5

type webhookClaims struct {
	jwt.StandardClaims
	SHA256 string `json:"sha256"`
}

type Webhook struct {
	*conf.WebhookConfig

	instanceID uuid.UUID
	jwtSecret  string
	claims     jwt.Claims
	payload    []byte
	headers    map[string]string
}

type WebhookResponse struct {
	AppMetaData  map[string]interface{} `json:"app_metadata,omitempty"`
	UserMetaData map[string]interface{} `json:"user_metadata,omitempty"`
}

func (w *Webhook) trigger() (io.ReadCloser, error) {
	timeout := defaultTimeout
	if w.TimeoutSec > 0 {
		timeout = time.Duration(w.TimeoutSec) * time.Second
	}

	if w.Retries == 0 {
		w.Retries = defaultHookRetries
	}

	client := http.Client{
		Timeout: timeout,
	}

	hooklog := logrus.WithFields(logrus.Fields{
		"component":   "webhook",
		"url":         w.URL,
		"signed":      w.jwtSecret != "",
		"instance_id": w.instanceID,
	})

	for i := 0; i < w.Retries; i++ {
		hooklog = hooklog.WithField("attempt", i+1)
		hooklog.Info("Starting to perform signup hook request")

		req, err := http.NewRequest(http.MethodPost, w.URL, bytes.NewBuffer(w.payload))
		if err != nil {
			return nil, internalServerError("Failed to make request object").WithInternalError(err)
		}
		req.Header.Set("Content-Type", "application/json")
		watcher, req := watchForConnection(req)

		if w.jwtSecret != "" {
			header, jwtErr := w.generateSignature()
			if jwtErr != nil {
				return nil, jwtErr
			}
			req.Header.Set(headerHookSignature, header)
		}

		start := time.Now()
		rsp, err := client.Do(req)
		if err != nil {
			if terr, ok := err.(net.Error); ok && terr.Timeout() {
				// timed out - try again?
				if i == w.Retries-1 {
					closeBody(rsp)
					return nil, httpError(http.StatusGatewayTimeout, "Failed to perform webhook in time frame (%d seconds)", timeout.Seconds())
				}
				hooklog.Info("Request timed out")
				continue
			} else if watcher.gotConn {
				closeBody(rsp)
				return nil, internalServerError("Failed to trigger webhook to %s", w.URL).WithInternalError(err)
			} else {
				closeBody(rsp)
				return nil, httpError(http.StatusBadGateway, "Failed to connect to %s", w.URL)
			}
		}
		dur := time.Since(start)
		rspLog := hooklog.WithFields(logrus.Fields{
			"status_code": rsp.StatusCode,
			"dur":         dur.Nanoseconds(),
		})
		switch rsp.StatusCode {
		case http.StatusOK, http.StatusNoContent, http.StatusAccepted:
			rspLog.Infof("Finished processing webhook in %s", dur)
			var body io.ReadCloser
			if rsp.ContentLength > 0 {
				body = rsp.Body
			}
			return body, nil
		default:
			rspLog.Infof("Bad response for webhook %d in %s", rsp.StatusCode, dur)
		}
	}

	hooklog.Infof("Failed to process webhook for %s after %d attempts", w.URL, w.Retries)
	return nil, unprocessableEntityError("Failed to handle signup webhook")
}

func (w *Webhook) generateSignature() (string, error) {
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, w.claims)
	tokenString, err := token.SignedString([]byte(w.jwtSecret))
	if err != nil {
		return "", internalServerError("Failed build signing string").WithInternalError(err)
	}
	return tokenString, nil
}

func closeBody(rsp *http.Response) {
	if rsp != nil && rsp.Body != nil {
		rsp.Body.Close()
	}
}

func triggerHook(ctx context.Context, conn *storage.Connection, event HookEvent, user *models.User, instanceID uuid.UUID, config *conf.Configuration) error {
	var hookURL *url.URL
	secret := config.Webhook.Secret
	if config.Webhook.URL != "" {
		var err error
		hookURL, err = url.Parse(config.Webhook.URL)
		if err != nil {
			return errors.Wrapf(err, "Failed to parse Webhook URL")
		}
	}

	if hookURL == nil {
		fun := getFunctionHooks(ctx)
		if fun == nil {
			return nil
		}

		if eventHookURL, ok := fun[string(event)]; ok {
			var err error
			hookURL, err = url.Parse(eventHookURL)
			if err != nil {
				return errors.Wrapf(err, "Failed to parse Event Function Hook URL")
			}
			secret = config.JWT.Secret
		} else {
			// abort hook call if there are no functions for this event
			return nil
		}
	}

	if !hookURL.IsAbs() {
		siteURL, err := url.Parse(config.SiteURL)
		if err != nil {
			return errors.Wrapf(err, "Failed to parse Site URL")
		}
		hookURL.Scheme = siteURL.Scheme
		hookURL.Host = siteURL.Host
		hookURL.User = siteURL.User
	}

	payload := struct {
		Event      HookEvent    `json:"event"`
		InstanceID uuid.UUID    `json:"instance_id,omitempty"`
		User       *models.User `json:"user"`
	}{
		Event:      event,
		InstanceID: instanceID,
		User:       user,
	}
	data, err := json.Marshal(&payload)
	if err != nil {
		return internalServerError("Failed to serialize the data for signup webhook").WithInternalError(err)
	}

	sha, err := checksum(data)
	if err != nil {
		return internalServerError("Failed to checksum the data for signup webhook").WithInternalError(err)
	}

	claims := webhookClaims{
		StandardClaims: jwt.StandardClaims{
			IssuedAt: time.Now().Unix(),
			Subject:  instanceID.String(),
			Issuer:   gotrueIssuer,
		},
		SHA256: sha,
	}

	w := Webhook{
		WebhookConfig: &config.Webhook,
		jwtSecret:     secret,
		instanceID:    instanceID,
		claims:        claims,
		payload:       data,
	}

	w.URL = hookURL.String()

	body, err := w.trigger()
	defer func() {
		if body != nil {
			body.Close()
		}
	}()
	if err == nil && body != nil {
		webhookRsp := &WebhookResponse{}
		decoder := json.NewDecoder(body)
		if err = decoder.Decode(webhookRsp); err != nil {
			return internalServerError("Webhook returned malformed JSON: %v", err).WithInternalError(err)
		}
		return conn.Transaction(func(tx *storage.Connection) error {
			if webhookRsp.UserMetaData != nil {
				user.UserMetaData = nil
				if terr := user.UpdateUserMetaData(tx, webhookRsp.UserMetaData); terr != nil {
					return terr
				}
			}
			if webhookRsp.AppMetaData != nil {
				user.AppMetaData = nil
				if terr := user.UpdateAppMetaData(tx, webhookRsp.AppMetaData); terr != nil {
					return terr
				}
			}
			return nil
		})
	}
	return err
}

func watchForConnection(req *http.Request) (*connectionWatcher, *http.Request) {
	w := new(connectionWatcher)
	t := &httptrace.ClientTrace{
		GotConn: w.GotConn,
	}

	req = req.WithContext(httptrace.WithClientTrace(req.Context(), t))
	return w, req
}

func checksum(data []byte) (string, error) {
	sha := sha256.New()
	_, err := sha.Write(data)
	if err != nil {
		return "", err
	}

	return hex.EncodeToString(sha.Sum(nil)), nil
}

type connectionWatcher struct {
	gotConn bool
}

func (c *connectionWatcher) GotConn(_ httptrace.GotConnInfo) {
	c.gotConn = true
}
<|endoftext|>


<commit_before>// Copyright 2015 tsuru 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 api

import (
	"html/template"
	"net/http"
	"path"

	"github.com/tsuru/config"
)

func index(w http.ResponseWriter, r *http.Request) error {
	host, _ := config.GetString("host")
	userCreate, _ := config.GetBool("auth:user-registration")
	scheme, _ := config.GetString("auth:scheme")
	repoManager, _ := config.GetString("repo-manager")
	data := map[string]interface{}{
		"tsuruTarget": host,
		"userCreate":  userCreate,
		"nativeLogin": scheme == "" || scheme == "native",
		"keysEnabled": repoManager == "" || repoManager == "gandalf",
	}
	template, err := getTemplate()
	if err != nil {
		return err
	}
	return template.Execute(w, data)
}

func getTemplate() (*template.Template, error) {
	templateFile, _ := config.GetString("index-page-template")
	if templateFile == "" {
		return indexTemplate, nil
	}
	tmpl, err := template.New("index").Funcs(funcMap).ParseFiles(templateFile)
	if err != nil {
		return nil, err
	}
	return tmpl.Lookup(path.Base(templateFile)), nil
}
<commit_msg>api/index: add comments to describe index<commit_after>// Copyright 2015 tsuru 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 api

import (
	"html/template"
	"net/http"
	"path"

	"github.com/tsuru/config"
)

// title: index
// path: /
// method: GET
// responses:
//   200: OK
func index(w http.ResponseWriter, r *http.Request) error {
	host, _ := config.GetString("host")
	userCreate, _ := config.GetBool("auth:user-registration")
	scheme, _ := config.GetString("auth:scheme")
	repoManager, _ := config.GetString("repo-manager")
	data := map[string]interface{}{
		"tsuruTarget": host,
		"userCreate":  userCreate,
		"nativeLogin": scheme == "" || scheme == "native",
		"keysEnabled": repoManager == "" || repoManager == "gandalf",
	}
	template, err := getTemplate()
	if err != nil {
		return err
	}
	return template.Execute(w, data)
}

func getTemplate() (*template.Template, error) {
	templateFile, _ := config.GetString("index-page-template")
	if templateFile == "" {
		return indexTemplate, nil
	}
	tmpl, err := template.New("index").Funcs(funcMap).ParseFiles(templateFile)
	if err != nil {
		return nil, err
	}
	return tmpl.Lookup(path.Base(templateFile)), nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"gopkg.in/h2non/gentleman.v1"
	"gopkg.in/h2non/gentleman.v1/plugins/multipart"
)

func main() {
	// Create a new client
	cli := gentleman.New()

        // Define the generic base URL
  	cli.URL("http://httpbin.org/post")

	// Create a new request
        req := cli.Request()

	// Create a text based form fields
	fields := map[string]string{"foo": "bar", "bar": "baz"}

	// Register the multipart plugin at request middleware level
	req.Use(multipart.Fields(fields))

	// Perform the request
	res, err := req.Send()
	if err != nil {
		fmt.Printf("Request error: %s\n", err)
		return
	}
	if !res.Ok {
		fmt.Printf("Invalid server response: %d\n", res.StatusCode)
		return
	}

	fmt.Printf("Status: %d\n", res.StatusCode)
	fmt.Printf("Body: %s", res.String())
}
<commit_msg>fix(example): add indentation<commit_after>package main

import (
	"fmt"
	"gopkg.in/h2non/gentleman.v1"
	"gopkg.in/h2non/gentleman.v1/plugins/multipart"
)

func main() {
	// Create a new client
	cli := gentleman.New()

	// Define the generic base URL
	cli.URL("http://httpbin.org/post")

	// Create a new request
	req := cli.Request()

	// Create a text based form fields
	fields := map[string]string{"foo": "bar", "bar": "baz"}

	// Register the multipart plugin at request middleware level
	req.Use(multipart.Fields(fields))

	// Perform the request
	res, err := req.Send()
	if err != nil {
		fmt.Printf("Request error: %s\n", err)
		return
	}
	if !res.Ok {
		fmt.Printf("Invalid server response: %d\n", res.StatusCode)
		return
	}

	fmt.Printf("Status: %d\n", res.StatusCode)
	fmt.Printf("Body: %s", res.String())
}
<|endoftext|>


<commit_before>/*

In a number of cases you have a property that can only have a handful of
possible values. You want to verify that the value is always one of those
legal values, and make sure that you can compare it to a known constant so you
can make sure you don't have a typo at compile time instead of run time. It's
also nice to have them have an order in many cases, and to be serialized with
the string value so it's easier to read.

Enums are useful for this case. An EnumSet contains multiple enums, and you
can create an EnumValue which can be used as a property on a PropertyReader
object.

The idiomatic way to create an enum is the following.

In components.go:
	const (
		ColorRed = iota
		ColorBlue
		ColorGreen
	)

	const (
		CardSpade = iota
		CardHeart
		CardDiamond
		CardClub
	)

	var Enums = enum.NewSet()

	var ColorEnum = Enums.MustAdd("Color", map[int]string{
		ColorRed: "Red",
		ColorBlue: "Blue",
		ColorGreen: "Green",
	})

	var CardEnum = Enums.MustAdd("Card", map[int]string{
		CardSpade: "Spade",
		CardHeart: "Heart",
		CardDiamond: "Diamond",
		CardClub: "Club",
	})

And then in your main.go:

	func (g *GameDelegate) EmptyGameState() boardgame.SubState {
		return &gameState{
			MyIntProp: 0,
			MyColorEnumProp: ColorEnum.NewMutableVal(),
		}
	}

	//...

	func NewManager() *boardgame.GameManager {
		//...

		//NewComponentChest will call Finish() on our Enums
		chest := boardgame.NewComponentChest(Enums)

		//...
	}

This is a fair bit of boilerplate to inlude in your components.go. You can use
the autoreader package to generate the repetitive boilerplate for you.

Instead of the above code for components.go, you'd instead only include the
following:

	//+autoreader
	const (
		ColorRed = iota
		ColorBlue
		ColorGreen
	)

	//+autoreader
	const (
		CardSpade = iota
		CardHeart
		CardDiamond
		CardClub
	)

Then, the rest of the example code shown above in components.go would be
automatically generated. The longest common prefix for each name in the
constant block would be used as the name of the enum. autoreader has more
options for controlling the precise way the enums are created; see
autoreader's package doc for more information.

*/
package enum

import (
	"encoding/json"
	"errors"
	"math"
	"math/rand"
	"strconv"
)

//IllegalValue is the senitnel value that will be returned for illegal values.
const IllegalValue = math.MaxInt64

//EnumSet is a set of enums where each Enum's values are unique. Normally you
//will create one in your package, add enums to it during initalization, and
//then use it for all managers you create.
type Set struct {
	finished bool
	enums    map[string]Enum
}

//Enum is a named set of values within a set. Get a new one with
//enumSet.Add(). It's an interface to better support anonymous-embedding
//scenarios.
type Enum interface {
	//DefaultValue returns the default value for this enum (the lowest valid value
	//in it).
	DefaultValue() int
	//RandomValue returns a random value that is Valid() for this enum.
	RandomValue() int
	//Valid returns whether the given value is a valid member of this enum.
	Valid(val int) bool
	//String returns the string value associated with the given value.
	String(val int) string
	//Name returns the name of this enum; if set is the set this enum is part of,
	//set.Enum(enum.Name()) == enum will be true.
	Name() string
	//ValueFromString returns the enum value that corresponds to the given string,
	//or IllegalValue if no value has that string.
	ValueFromString(in string) int
	//NewVal returns an enum.Val that is permanently set to the provided
	//val. If that value is not valid for this enum, it will error.
	NewVal(val int) (Val, error)
	NewMutableVal() MutableVal
	//NewMutableVal returns a new EnumValue associated with this enum, set to the
	//Enum's DefaultValue to start.
	//NewDefaultVal is a convenience shortcut for creating a new const that is
	//set to the default value, which is moderately common enough that it makes
	//sense to do it without the possibility of errors.
	NewDefaultVal() Val

	//MustNewVal is like NewVal, but if it would have errored it panics
	//instead. It's convenient for initial set up where the whole app should fail
	//to startup if it can't be configured anyway, and dealing with errors would
	//be a lot of boilerplate.
	MustNewVal(val int) Val
	MustNewMutableVal(val int) MutableVal
}

//enum is the underlying type we use to implement Enum.
type enum struct {
	name         string
	values       map[int]string
	defaultValue int
}

//variable is the underlying type we'll return for both Value and Constant.
type variable struct {
	enum Enum
	val  int
}

//Val is an instantiation of an Enum that cannot be changed. You retrieve it
//from enum.NewVal(val).
type Val interface {
	Enum() Enum
	Value() int
	String() string
	Copy() Val
	MutableCopy() MutableVal
	Equals(other Val) bool
}

//MutableVal is an instantiation of a value that must be set to a value in the
//given enum. You retrieve one from enum.NewMutableVal().
type MutableVal interface {
	Val
	//SetValue changes the value. Returns true if successful. Will fail if the
	//value is locked or the val you want to set is not a valid number for the
	//enum this value is associated with.
	SetValue(val int) error
	//SetStringValue sets the value to the value associated with that string.
	SetStringValue(str string) error
}

//NewSet returns a new Set. Generally you'll call this once in a
//package and create the set during initalization.
func NewSet() *Set {
	return &Set{
		false,
		make(map[string]Enum),
	}
}

//MustCombineSets wraps CombineEnumSets, but instead of erroring will
//panic. Useful for package-level declarations outside of init().
func MustCombineSets(sets ...*Set) *Set {
	result, err := CombineSets(sets...)
	if err != nil {
		panic("Couldn't combine sets: " + err.Error())
	}
	return result
}

//CombineSets returns a new EnumSet that contains all of the EnumSets
//combined into one. The individual enums will literally be the same as the
//enums from the provided sets, so enum equality will work.
func CombineSets(sets ...*Set) (*Set, error) {
	result := NewSet()
	for i, set := range sets {
		for _, enumName := range set.EnumNames() {
			enum := set.Enum(enumName)
			if err := result.addEnum(enumName, enum); err != nil {
				return nil, errors.New("Couldn't add the " + strconv.Itoa(i) + " enumset because " + enumName + " had error: " + err.Error())
			}
		}
	}
	return result, nil
}

//Finish finalizes an EnumSet so that no more enums may be added. After this
//is called it is safe to use this in a multi-threaded environment. Repeated
//calls do nothing. ComponenChest automatically calls Finish() on the set you
//pass it.
func (e *Set) Finish() {
	e.finished = true
}

//EnumNames returns a list of all names in the Enum.
func (e *Set) EnumNames() []string {
	var result []string
	for key, _ := range e.enums {
		result = append(result, key)
	}
	return result
}

//Returns the Enum with the given name. In general you keep a reference to the
//enum yourself, but this is useful for programatically enumerating the enums.
func (e *Set) Enum(name string) Enum {
	return e.enums[name]
}

//MustAdd is like Add, but instead of an error it will panic if the enum
//cannot be added. This is useful for defining your enums at the package level
//outside of an init().
func (e *Set) MustAdd(enumName string, values map[int]string) Enum {
	result, err := e.Add(enumName, values)

	if err != nil {
		panic("Couldn't add to enumset: " + err.Error())
	}

	return result
}

/*
Add ads an enum with the given name and values to the enum manager. Will error
if that name has already been added or if the config you provide has more than
one string with the same value.
*/
func (e *Set) Add(enumName string, values map[int]string) (Enum, error) {

	if len(values) == 0 {
		return nil, errors.New("No values provided")
	}

	enum := &enum{
		enumName,
		make(map[int]string),
		math.MaxInt64,
	}

	seenValues := make(map[string]bool)

	for v, s := range values {

		numString := strconv.Itoa(v)

		if seenValues[numString] {
			return nil, errors.New("The string value of " + numString + " was already in the enum")
		}

		if seenValues[s] {
			return nil, errors.New("String " + s + " was not unique within enum " + enumName)
		}

		//We put in both values into seenValues here because it's legal for a
		//const to have its string-value be the stringification of its own
		//int.
		seenValues[numString] = true
		seenValues[s] = true

		enum.values[v] = s

		if v < enum.defaultValue {
			enum.defaultValue = v
		}

	}
	if err := e.addEnum(enumName, enum); err != nil {
		return nil, err
	}
	return enum, nil
}

func (e *Set) addEnum(enumName string, enum Enum) error {

	if e.finished {
		return errors.New("The set has been finished so no more enums can be added")
	}

	if _, ok := e.enums[enumName]; ok {
		return errors.New("That enum name has already been provided")
	}

	e.enums[enumName] = enum

	return nil
}

func (e *enum) DefaultValue() int {
	return e.defaultValue
}

func (e *enum) RandomValue() int {
	keys := make([]int, len(e.values))

	i := 0
	for key, _ := range e.values {
		keys[i] = key
		i++
	}
	return keys[rand.Intn(len(keys))]
}

func (e *enum) Valid(val int) bool {
	_, ok := e.values[val]
	return ok
}

func (e *enum) String(val int) string {
	return e.values[val]
}

func (e *enum) Name() string {
	return e.name
}

func (e *enum) ValueFromString(in string) int {
	for v, str := range e.values {
		if str == in {
			return v
		}
	}
	//Hmmm... see if they gave us the string equivalent of the int?
	num, err := strconv.Atoi(in)
	if err == nil {
		if _, ok := e.values[num]; ok {
			return num
		}
	}
	return IllegalValue
}

//Copy returns a copy of the Value, that is equivalent, but will not be
//locked.
func (e *variable) Copy() Val {
	return &variable{
		e.enum,
		e.val,
	}
}

func (e *variable) MutableCopy() MutableVal {
	return &variable{
		e.enum,
		e.val,
	}
}

func (e *enum) NewMutableVal() MutableVal {
	return &variable{
		e,
		e.DefaultValue(),
	}
}

func (e *enum) MustNewMutableVal(val int) MutableVal {
	enu := e.NewMutableVal()
	if err := enu.SetValue(val); err != nil {
		panic("Couldn't create mutable val: " + err.Error())
	}
	return enu
}

func (e *enum) MustNewVal(val int) Val {
	result, err := e.NewVal(val)
	if err != nil {
		panic("Couldn't create constant: " + err.Error())
	}
	return result
}

func (e *enum) NewDefaultVal() Val {
	c, err := e.NewVal(e.DefaultValue())
	if err != nil {
		panic("Unexpected error in NewDefaultConst: " + err.Error())
	}
	return c
}

func (e *enum) NewVal(val int) (Val, error) {
	variable := e.NewMutableVal()
	if err := variable.SetValue(val); err != nil {
		return nil, err
	}
	return variable, nil
}

//The enum marshals as the string value of the enum so it's more readable.
func (e *variable) MarshalJSON() ([]byte, error) {
	return json.Marshal(e.String())
}

//UnmarshalJSON expects the blob to be the string value. Will error if that
//doesn't correspond to a valid value for this enum.
func (e *variable) UnmarshalJSON(blob []byte) error {
	var str string
	if err := json.Unmarshal(blob, &str); err != nil {
		return err
	}
	val := e.enum.ValueFromString(str)
	if val == IllegalValue {
		return errors.New("That string value had no enum in the value")
	}
	return e.SetValue(val)
}

func (e *variable) Enum() Enum {
	return e.enum
}

func (e *variable) Value() int {
	return e.val
}

func (e *variable) String() string {
	return e.enum.String(e.val)
}

func (e *variable) SetValue(val int) error {
	if !e.enum.Valid(val) {
		return errors.New("That value is not valid for this enum")
	}
	e.val = val
	return nil
}

func (e *variable) SetStringValue(str string) error {
	val := e.Enum().ValueFromString(str)
	return e.SetValue(val)
}

//Equals returns true if the two Consts are equivalent.
func (e *variable) Equals(other Val) bool {
	if other == nil {
		return false
	}
	if e.Enum() != other.Enum() {
		return false
	}
	if e.Value() != other.Value() {
		return false
	}
	return true
}
<commit_msg>Fix up the example in the enums package doc.<commit_after>/*

In a number of cases you have a property that can only have a handful of
possible values. You want to verify that the value is always one of those
legal values, and make sure that you can compare it to a known constant so you
can make sure you don't have a typo at compile time instead of run time. It's
also nice to have them have an order in many cases, and to be serialized with
the string value so it's easier to read.

Enums are useful for this case. An EnumSet contains multiple enums, and you
can create an EnumValue which can be used as a property on a PropertyReader
object.

The idiomatic way to create an enum is the following.

In components.go:
	const (
		ColorRed = iota
		ColorBlue
		ColorGreen
	)

	const (
		CardSpade = iota
		CardHeart
		CardDiamond
		CardClub
	)

	var Enums = enum.NewSet()

	var ColorEnum = Enums.MustAdd("Color", map[int]string{
		ColorRed: "Red",
		ColorBlue: "Blue",
		ColorGreen: "Green",
	})

	var CardEnum = Enums.MustAdd("Card", map[int]string{
		CardSpade: "Spade",
		CardHeart: "Heart",
		CardDiamond: "Diamond",
		CardClub: "Club",
	})

And then in your main.go:

	func (g *GameDelegate) EmptyGameState() boardgame.ConfigurableSubState {

		//You could also just return a zero-valued struct if you used struct
		//tags for the enum. See the Constructors section of boardgame package
		//doc for more.
		return &gameState{
			MyIntProp: 0,
			MyColorEnumProp: ColorEnum.NewMutableVal(),
		}
	}

	//...

	func NewManager() *boardgame.GameManager {
		//...

		//NewComponentChest will call Finish() on our Enums
		chest := boardgame.NewComponentChest(Enums)

		//...
	}

This is a fair bit of boilerplate to inlude in your components.go. You can use
the autoreader package to generate the repetitive boilerplate for you.

Instead of the above code for components.go, you'd instead only include the
following:

	//+autoreader
	const (
		ColorRed = iota
		ColorBlue
		ColorGreen
	)

	//+autoreader
	const (
		CardSpade = iota
		CardHeart
		CardDiamond
		CardClub
	)

Then, the rest of the example code shown above in components.go would be
automatically generated. The longest common prefix for each name in the
constant block would be used as the name of the enum. autoreader has more
options for controlling the precise way the enums are created; see
autoreader's package doc for more information.

*/
package enum

import (
	"encoding/json"
	"errors"
	"math"
	"math/rand"
	"strconv"
)

//IllegalValue is the senitnel value that will be returned for illegal values.
const IllegalValue = math.MaxInt64

//EnumSet is a set of enums where each Enum's values are unique. Normally you
//will create one in your package, add enums to it during initalization, and
//then use it for all managers you create.
type Set struct {
	finished bool
	enums    map[string]Enum
}

//Enum is a named set of values within a set. Get a new one with
//enumSet.Add(). It's an interface to better support anonymous-embedding
//scenarios.
type Enum interface {
	//DefaultValue returns the default value for this enum (the lowest valid value
	//in it).
	DefaultValue() int
	//RandomValue returns a random value that is Valid() for this enum.
	RandomValue() int
	//Valid returns whether the given value is a valid member of this enum.
	Valid(val int) bool
	//String returns the string value associated with the given value.
	String(val int) string
	//Name returns the name of this enum; if set is the set this enum is part of,
	//set.Enum(enum.Name()) == enum will be true.
	Name() string
	//ValueFromString returns the enum value that corresponds to the given string,
	//or IllegalValue if no value has that string.
	ValueFromString(in string) int
	//NewVal returns an enum.Val that is permanently set to the provided
	//val. If that value is not valid for this enum, it will error.
	NewVal(val int) (Val, error)
	NewMutableVal() MutableVal
	//NewMutableVal returns a new EnumValue associated with this enum, set to the
	//Enum's DefaultValue to start.
	//NewDefaultVal is a convenience shortcut for creating a new const that is
	//set to the default value, which is moderately common enough that it makes
	//sense to do it without the possibility of errors.
	NewDefaultVal() Val

	//MustNewVal is like NewVal, but if it would have errored it panics
	//instead. It's convenient for initial set up where the whole app should fail
	//to startup if it can't be configured anyway, and dealing with errors would
	//be a lot of boilerplate.
	MustNewVal(val int) Val
	MustNewMutableVal(val int) MutableVal
}

//enum is the underlying type we use to implement Enum.
type enum struct {
	name         string
	values       map[int]string
	defaultValue int
}

//variable is the underlying type we'll return for both Value and Constant.
type variable struct {
	enum Enum
	val  int
}

//Val is an instantiation of an Enum that cannot be changed. You retrieve it
//from enum.NewVal(val).
type Val interface {
	Enum() Enum
	Value() int
	String() string
	Copy() Val
	MutableCopy() MutableVal
	Equals(other Val) bool
}

//MutableVal is an instantiation of a value that must be set to a value in the
//given enum. You retrieve one from enum.NewMutableVal().
type MutableVal interface {
	Val
	//SetValue changes the value. Returns true if successful. Will fail if the
	//value is locked or the val you want to set is not a valid number for the
	//enum this value is associated with.
	SetValue(val int) error
	//SetStringValue sets the value to the value associated with that string.
	SetStringValue(str string) error
}

//NewSet returns a new Set. Generally you'll call this once in a
//package and create the set during initalization.
func NewSet() *Set {
	return &Set{
		false,
		make(map[string]Enum),
	}
}

//MustCombineSets wraps CombineEnumSets, but instead of erroring will
//panic. Useful for package-level declarations outside of init().
func MustCombineSets(sets ...*Set) *Set {
	result, err := CombineSets(sets...)
	if err != nil {
		panic("Couldn't combine sets: " + err.Error())
	}
	return result
}

//CombineSets returns a new EnumSet that contains all of the EnumSets
//combined into one. The individual enums will literally be the same as the
//enums from the provided sets, so enum equality will work.
func CombineSets(sets ...*Set) (*Set, error) {
	result := NewSet()
	for i, set := range sets {
		for _, enumName := range set.EnumNames() {
			enum := set.Enum(enumName)
			if err := result.addEnum(enumName, enum); err != nil {
				return nil, errors.New("Couldn't add the " + strconv.Itoa(i) + " enumset because " + enumName + " had error: " + err.Error())
			}
		}
	}
	return result, nil
}

//Finish finalizes an EnumSet so that no more enums may be added. After this
//is called it is safe to use this in a multi-threaded environment. Repeated
//calls do nothing. ComponenChest automatically calls Finish() on the set you
//pass it.
func (e *Set) Finish() {
	e.finished = true
}

//EnumNames returns a list of all names in the Enum.
func (e *Set) EnumNames() []string {
	var result []string
	for key, _ := range e.enums {
		result = append(result, key)
	}
	return result
}

//Returns the Enum with the given name. In general you keep a reference to the
//enum yourself, but this is useful for programatically enumerating the enums.
func (e *Set) Enum(name string) Enum {
	return e.enums[name]
}

//MustAdd is like Add, but instead of an error it will panic if the enum
//cannot be added. This is useful for defining your enums at the package level
//outside of an init().
func (e *Set) MustAdd(enumName string, values map[int]string) Enum {
	result, err := e.Add(enumName, values)

	if err != nil {
		panic("Couldn't add to enumset: " + err.Error())
	}

	return result
}

/*
Add ads an enum with the given name and values to the enum manager. Will error
if that name has already been added or if the config you provide has more than
one string with the same value.
*/
func (e *Set) Add(enumName string, values map[int]string) (Enum, error) {

	if len(values) == 0 {
		return nil, errors.New("No values provided")
	}

	enum := &enum{
		enumName,
		make(map[int]string),
		math.MaxInt64,
	}

	seenValues := make(map[string]bool)

	for v, s := range values {

		numString := strconv.Itoa(v)

		if seenValues[numString] {
			return nil, errors.New("The string value of " + numString + " was already in the enum")
		}

		if seenValues[s] {
			return nil, errors.New("String " + s + " was not unique within enum " + enumName)
		}

		//We put in both values into seenValues here because it's legal for a
		//const to have its string-value be the stringification of its own
		//int.
		seenValues[numString] = true
		seenValues[s] = true

		enum.values[v] = s

		if v < enum.defaultValue {
			enum.defaultValue = v
		}

	}
	if err := e.addEnum(enumName, enum); err != nil {
		return nil, err
	}
	return enum, nil
}

func (e *Set) addEnum(enumName string, enum Enum) error {

	if e.finished {
		return errors.New("The set has been finished so no more enums can be added")
	}

	if _, ok := e.enums[enumName]; ok {
		return errors.New("That enum name has already been provided")
	}

	e.enums[enumName] = enum

	return nil
}

func (e *enum) DefaultValue() int {
	return e.defaultValue
}

func (e *enum) RandomValue() int {
	keys := make([]int, len(e.values))

	i := 0
	for key, _ := range e.values {
		keys[i] = key
		i++
	}
	return keys[rand.Intn(len(keys))]
}

func (e *enum) Valid(val int) bool {
	_, ok := e.values[val]
	return ok
}

func (e *enum) String(val int) string {
	return e.values[val]
}

func (e *enum) Name() string {
	return e.name
}

func (e *enum) ValueFromString(in string) int {
	for v, str := range e.values {
		if str == in {
			return v
		}
	}
	//Hmmm... see if they gave us the string equivalent of the int?
	num, err := strconv.Atoi(in)
	if err == nil {
		if _, ok := e.values[num]; ok {
			return num
		}
	}
	return IllegalValue
}

//Copy returns a copy of the Value, that is equivalent, but will not be
//locked.
func (e *variable) Copy() Val {
	return &variable{
		e.enum,
		e.val,
	}
}

func (e *variable) MutableCopy() MutableVal {
	return &variable{
		e.enum,
		e.val,
	}
}

func (e *enum) NewMutableVal() MutableVal {
	return &variable{
		e,
		e.DefaultValue(),
	}
}

func (e *enum) MustNewMutableVal(val int) MutableVal {
	enu := e.NewMutableVal()
	if err := enu.SetValue(val); err != nil {
		panic("Couldn't create mutable val: " + err.Error())
	}
	return enu
}

func (e *enum) MustNewVal(val int) Val {
	result, err := e.NewVal(val)
	if err != nil {
		panic("Couldn't create constant: " + err.Error())
	}
	return result
}

func (e *enum) NewDefaultVal() Val {
	c, err := e.NewVal(e.DefaultValue())
	if err != nil {
		panic("Unexpected error in NewDefaultConst: " + err.Error())
	}
	return c
}

func (e *enum) NewVal(val int) (Val, error) {
	variable := e.NewMutableVal()
	if err := variable.SetValue(val); err != nil {
		return nil, err
	}
	return variable, nil
}

//The enum marshals as the string value of the enum so it's more readable.
func (e *variable) MarshalJSON() ([]byte, error) {
	return json.Marshal(e.String())
}

//UnmarshalJSON expects the blob to be the string value. Will error if that
//doesn't correspond to a valid value for this enum.
func (e *variable) UnmarshalJSON(blob []byte) error {
	var str string
	if err := json.Unmarshal(blob, &str); err != nil {
		return err
	}
	val := e.enum.ValueFromString(str)
	if val == IllegalValue {
		return errors.New("That string value had no enum in the value")
	}
	return e.SetValue(val)
}

func (e *variable) Enum() Enum {
	return e.enum
}

func (e *variable) Value() int {
	return e.val
}

func (e *variable) String() string {
	return e.enum.String(e.val)
}

func (e *variable) SetValue(val int) error {
	if !e.enum.Valid(val) {
		return errors.New("That value is not valid for this enum")
	}
	e.val = val
	return nil
}

func (e *variable) SetStringValue(str string) error {
	val := e.Enum().ValueFromString(str)
	return e.SetValue(val)
}

//Equals returns true if the two Consts are equivalent.
func (e *variable) Equals(other Val) bool {
	if other == nil {
		return false
	}
	if e.Enum() != other.Enum() {
		return false
	}
	if e.Value() != other.Value() {
		return false
	}
	return true
}
<|endoftext|>