<commit_before>// Copyright 2013-2015 Oliver Eilhard.
// Use of this source code is governed by the MIT LICENSE that
// can be found in the MIT-LICENSE file included in the project.

package mruby

import (
	"reflect"
	"testing"
)

func TestToValue(t *testing.T) {
	ctx := NewContext()
	if ctx == nil {
		t.Fatal("expected NewContext() to be != nil")
	}

	tests := []struct {
		Input    string
		Expected interface{}
		Failed   bool
		Error    string
	}{
		{`true`, true, false, ""},
		{`false`, false, false, ""},
		{`''`, string(""), false, ""},
		{`""`, string(""), false, ""},
		{`"abc"`, string("abc"), false, ""},
		{`:abc`, string("abc"), false, ""},
		{`1`, int(1), false, ""},
		{`1.5`, float64(1.5), false, ""},
		{`nil`, nil, false, ""},
		{"['Oliver', 2, 42.3, true, nil]", []interface{}{"Oliver", 2, float64(42.3), true, nil}, false, ""},
		{"{:name => 'Oliver', :age => 21}",
			map[string]interface{}{
				"name": "Oliver",
				"age":  21,
			},
			false,
			""},
		{"{:name => 'Oliver', 'age' => 21, address: {city: 'Munich'}}",
			map[string]interface{}{
				"name": "Oliver",
				"age":  21,
				"address": map[string]interface{}{
					"city": "Munich",
				},
			},
			false,
			""},
		{"raise 'kaboom'", nil, true, "kaboom"},
	}

	for _, test := range tests {
		val, err := ctx.LoadString(test.Input)
		if err != nil {
			// Should it fail?
			if !test.Failed {
				t.Fatal(err)
			} else if test.Error != err.Error() {
				t.Errorf("expected error %q; got: %q", test.Error, err.Error())
			}
		} else {
			// Should succeed
			got, err := val.ToInterface()
			if err != nil {
				t.Fatal(err)
			}
			if reflect.TypeOf(got) != reflect.TypeOf(test.Expected) {
				t.Errorf("expected %v; got: %v", reflect.TypeOf(test.Expected), reflect.TypeOf(got))
			}
			if !reflect.DeepEqual(got, test.Expected) {
				t.Errorf("expected %v; got: %v", test.Expected, got)
			}
		}
	}
}

func TestValueStringer(t *testing.T) {
	ctx := NewContext()
	if ctx == nil {
		t.Fatal("expected NewContext() to be != nil")
	}

	val, err := ctx.LoadString("'Hello'")
	if err != nil {
		t.Fatal(err)
	}
	expected := "Value{Value:\"Hello\",ValueType{Type:MRB_TT_STRING,Class:String}}"
	if val.String() != expected {
		t.Errorf("expected %q; got: %q", expected, val.String())
	}
}

func TestNilType(t *testing.T) {
	ctx := NewContext()
	if ctx == nil {
		t.Fatal("expected NewContext() to be != nil")
	}

	val, err := ctx.LoadString("nil")
	if err != nil {
		t.Fatal(err)
	}
	if !val.IsNil() {
		t.Errorf("expected type NilClass; got: %v", val.Type())
	}

	res, err := val.ToInterface()
	if err != nil {
		t.Fatal(err)
	}
	if res != nil {
		t.Errorf("expected %v; got: %v", nil, res)
	}
}

func TestTrueType(t *testing.T) {
	ctx := NewContext()
	if ctx == nil {
		t.Fatal("expected NewContext() to be != nil")
	}

	val, err := ctx.LoadString("1 == 1")
	if err != nil {
		t.Fatal(err)
	}
	if !val.IsBool() {
		t.Errorf("expected type True; got: %v", val.Type())
	}

	flag, err := val.ToBool()
	if err != nil {
		t.Fatal(err)
	}
	if !flag {
		t.Errorf("expected %v; got: %v", true, flag)
	}
}

func TestFalseType(t *testing.T) {
	ctx := NewContext()
	if ctx == nil {
		t.Fatal("expected NewContext() to be != nil")
	}

	val, err := ctx.LoadString("1 != 1")
	if err != nil {
		t.Fatal(err)
	}
	if !val.IsBool() {
		t.Errorf("expected type False; got: %v", val.Type())
	}

	flag, err := val.ToBool()
	if err != nil {
		t.Fatal(err)
	}
	if flag {
		t.Errorf("expected %v; got: %v", false, flag)
	}
}

func TestFixnumType(t *testing.T) {
	ctx := NewContext()
	if ctx == nil {
		t.Fatal("expected NewContext() to be != nil")
	}

	val, err := ctx.LoadString("1+2")
	if err != nil {
		t.Fatal(err)
	}
	if !val.IsFixnum() {
		t.Errorf("expected type Fixnum; got: %v", val.Type())
	}
	i, err := val.ToInt()
	if err != nil {
		t.Fatal(err)
	}
	if i != 3 {
		t.Errorf("expected %d; got: %d", 3, i)
	}

	i8, err := val.ToInt8()
	if err != nil {
		t.Fatal(err)
	}
	if i8 != 3 {
		t.Errorf("expected %d; got: %d", 3, i8)
	}

	i16, err := val.ToInt()
	if err != nil {
		t.Fatal(err)
	}
	if i16 != 3 {
		t.Errorf("expected %d; got: %d", 3, i16)
	}

	i32, err := val.ToInt()
	if err != nil {
		t.Fatal(err)
	}
	if i32 != 3 {
		t.Errorf("expected %d; got: %d", 3, i32)
	}

	i64, err := val.ToInt()
	if err != nil {
		t.Fatal(err)
	}
	if i64 != 3 {
		t.Errorf("expected %d; got: %d", 3, i64)
	}
}

func TestFloatType(t *testing.T) {
	ctx := NewContext()
	if ctx == nil {
		t.Fatal("expected NewContext() to be != nil")
	}

	val, err := ctx.LoadString("1.5+2.25")
	if err != nil {
		t.Fatal(err)
	}
	if !val.IsFloat() {
		t.Errorf("expected type Float; got: %v", val.Type())
	}
	f32, err := val.ToFloat32()
	if err != nil {
		t.Fatal(err)
	}
	if f32 != 3.75 {
		t.Errorf("expected %v; got: %v", 3.75, f32)
	}

	f64, err := val.ToFloat64()
	if err != nil {
		t.Fatal(err)
	}
	if f64 != 3.75 {
		t.Errorf("expected %v; got: %v", 3.75, f64)
	}
}

func TestStringType(t *testing.T) {
	ctx := NewContext()
	if ctx == nil {
		t.Fatal("expected NewContext() to be != nil")
	}

	val, err := ctx.LoadString("'Hello'")
	if err != nil {
		t.Fatal(err)
	}
	if !val.IsString() {
		t.Errorf("expected type String; got: %v", val.Type())
	}
	s, err := val.ToString()
	if err != nil {
		t.Fatal(err)
	}
	if s != "Hello" {
		t.Errorf("expected %q; got: %q", "Hello", s)
	}
}

func TestSymbolType(t *testing.T) {
	ctx := NewContext()
	if ctx == nil {
		t.Fatal("expected NewContext() to be != nil")
	}

	val, err := ctx.LoadString(":Hello")
	if err != nil {
		t.Fatal(err)
	}
	if !val.IsSymbol() {
		t.Errorf("expected type Symbol; got: %v", val.Type())
	}
	s, err := val.ToString()
	if err != nil {
		t.Fatal(err)
	}
	if s != "Hello" {
		t.Errorf("expected %q; got: %q", "Hello", s)
	}
}

func TestArrayType(t *testing.T) {
	ctx := NewContext()
	if ctx == nil {
		t.Fatal("expected NewContext() to be != nil")
	}

	val, err := ctx.LoadString("[1,2,'Oliver']")
	if err != nil {
		t.Fatal(err)
	}
	if !val.IsArray() {
		t.Errorf("expected type Array; got: %v", val.Type())
	}

	got, err := val.ToArray()
	if err != nil {
		t.Fatal(err)
	}
	expected := []interface{}{1, 2, "Oliver"}
	if !reflect.DeepEqual(got, expected) {
		t.Errorf("expected %v; got: %v", expected, got)
	}
}

func TestMapType(t *testing.T) {
	ctx := NewContext()
	if ctx == nil {
		t.Fatal("expected NewContext() to be != nil")
	}

	val, err := ctx.LoadString("{city: 'Munich', :name => 'Oliver', age: 21}")
	if err != nil {
		t.Fatal(err)
	}
	if !val.IsHash() {
		t.Errorf("expected type Hash; got: %v", val.Type())
	}

	got, err := val.ToMap()
	if err != nil {
		t.Fatal(err)
	}
	expected := map[string]interface{}{"city": "Munich", "name": "Oliver", "age": 21}
	if !reflect.DeepEqual(got, expected) {
		t.Errorf("expected %v; got: %v", expected, got)
	}
}

func TestExceptionType(t *testing.T) {
	ctx := NewContext()
	if ctx == nil {
		t.Fatal("expected NewContext() to be != nil")
	}

	_, err := ctx.LoadString("raise 'bang bang'")
	if err == nil {
		t.Fatal("expected error")
	}
}
<commit_msg>Add test for complex array/hash value<commit_after>// Copyright 2013-2015 Oliver Eilhard.
// Use of this source code is governed by the MIT LICENSE that
// can be found in the MIT-LICENSE file included in the project.

package mruby

import (
	"reflect"
	"testing"
)

func TestToValue(t *testing.T) {
	ctx := NewContext()
	if ctx == nil {
		t.Fatal("expected NewContext() to be != nil")
	}

	tests := []struct {
		Input    string
		Expected interface{}
		Failed   bool
		Error    string
	}{
		{`true`, true, false, ""},
		{`false`, false, false, ""},
		{`''`, string(""), false, ""},
		{`""`, string(""), false, ""},
		{`"abc"`, string("abc"), false, ""},
		{`:abc`, string("abc"), false, ""},
		{`1`, int(1), false, ""},
		{`1.5`, float64(1.5), false, ""},
		{`nil`, nil, false, ""},
		{"['Oliver', 2, 42.3, true, nil]", []interface{}{"Oliver", 2, float64(42.3), true, nil}, false, ""},
		{"{:name => 'Oliver', :age => 21}",
			map[string]interface{}{
				"name": "Oliver",
				"age":  21,
			},
			false,
			""},
		{"{:name => 'Oliver', 'age' => 21, address: {city: 'Munich'}}",
			map[string]interface{}{
				"name": "Oliver",
				"age":  21,
				"address": map[string]interface{}{
					"city": "Munich",
				},
			},
			false,
			""},
		{"raise 'kaboom'", nil, true, "kaboom"},
	}

	for _, test := range tests {
		val, err := ctx.LoadString(test.Input)
		if err != nil {
			// Should it fail?
			if !test.Failed {
				t.Fatal(err)
			} else if test.Error != err.Error() {
				t.Errorf("expected error %q; got: %q", test.Error, err.Error())
			}
		} else {
			// Should succeed
			got, err := val.ToInterface()
			if err != nil {
				t.Fatal(err)
			}
			if reflect.TypeOf(got) != reflect.TypeOf(test.Expected) {
				t.Errorf("expected %v; got: %v", reflect.TypeOf(test.Expected), reflect.TypeOf(got))
			}
			if !reflect.DeepEqual(got, test.Expected) {
				t.Errorf("expected %v; got: %v", test.Expected, got)
			}
		}
	}
}

func TestValueStringer(t *testing.T) {
	ctx := NewContext()
	if ctx == nil {
		t.Fatal("expected NewContext() to be != nil")
	}

	val, err := ctx.LoadString("'Hello'")
	if err != nil {
		t.Fatal(err)
	}
	expected := "Value{Value:\"Hello\",ValueType{Type:MRB_TT_STRING,Class:String}}"
	if val.String() != expected {
		t.Errorf("expected %q; got: %q", expected, val.String())
	}
}

func TestNilType(t *testing.T) {
	ctx := NewContext()
	if ctx == nil {
		t.Fatal("expected NewContext() to be != nil")
	}

	val, err := ctx.LoadString("nil")
	if err != nil {
		t.Fatal(err)
	}
	if !val.IsNil() {
		t.Errorf("expected type NilClass; got: %v", val.Type())
	}

	res, err := val.ToInterface()
	if err != nil {
		t.Fatal(err)
	}
	if res != nil {
		t.Errorf("expected %v; got: %v", nil, res)
	}
}

func TestTrueType(t *testing.T) {
	ctx := NewContext()
	if ctx == nil {
		t.Fatal("expected NewContext() to be != nil")
	}

	val, err := ctx.LoadString("1 == 1")
	if err != nil {
		t.Fatal(err)
	}
	if !val.IsBool() {
		t.Errorf("expected type True; got: %v", val.Type())
	}

	flag, err := val.ToBool()
	if err != nil {
		t.Fatal(err)
	}
	if !flag {
		t.Errorf("expected %v; got: %v", true, flag)
	}
}

func TestFalseType(t *testing.T) {
	ctx := NewContext()
	if ctx == nil {
		t.Fatal("expected NewContext() to be != nil")
	}

	val, err := ctx.LoadString("1 != 1")
	if err != nil {
		t.Fatal(err)
	}
	if !val.IsBool() {
		t.Errorf("expected type False; got: %v", val.Type())
	}

	flag, err := val.ToBool()
	if err != nil {
		t.Fatal(err)
	}
	if flag {
		t.Errorf("expected %v; got: %v", false, flag)
	}
}

func TestFixnumType(t *testing.T) {
	ctx := NewContext()
	if ctx == nil {
		t.Fatal("expected NewContext() to be != nil")
	}

	val, err := ctx.LoadString("1+2")
	if err != nil {
		t.Fatal(err)
	}
	if !val.IsFixnum() {
		t.Errorf("expected type Fixnum; got: %v", val.Type())
	}
	i, err := val.ToInt()
	if err != nil {
		t.Fatal(err)
	}
	if i != 3 {
		t.Errorf("expected %d; got: %d", 3, i)
	}

	i8, err := val.ToInt8()
	if err != nil {
		t.Fatal(err)
	}
	if i8 != 3 {
		t.Errorf("expected %d; got: %d", 3, i8)
	}

	i16, err := val.ToInt()
	if err != nil {
		t.Fatal(err)
	}
	if i16 != 3 {
		t.Errorf("expected %d; got: %d", 3, i16)
	}

	i32, err := val.ToInt()
	if err != nil {
		t.Fatal(err)
	}
	if i32 != 3 {
		t.Errorf("expected %d; got: %d", 3, i32)
	}

	i64, err := val.ToInt()
	if err != nil {
		t.Fatal(err)
	}
	if i64 != 3 {
		t.Errorf("expected %d; got: %d", 3, i64)
	}
}

func TestFloatType(t *testing.T) {
	ctx := NewContext()
	if ctx == nil {
		t.Fatal("expected NewContext() to be != nil")
	}

	val, err := ctx.LoadString("1.5+2.25")
	if err != nil {
		t.Fatal(err)
	}
	if !val.IsFloat() {
		t.Errorf("expected type Float; got: %v", val.Type())
	}
	f32, err := val.ToFloat32()
	if err != nil {
		t.Fatal(err)
	}
	if f32 != 3.75 {
		t.Errorf("expected %v; got: %v", 3.75, f32)
	}

	f64, err := val.ToFloat64()
	if err != nil {
		t.Fatal(err)
	}
	if f64 != 3.75 {
		t.Errorf("expected %v; got: %v", 3.75, f64)
	}
}

func TestStringType(t *testing.T) {
	ctx := NewContext()
	if ctx == nil {
		t.Fatal("expected NewContext() to be != nil")
	}

	val, err := ctx.LoadString("'Hello'")
	if err != nil {
		t.Fatal(err)
	}
	if !val.IsString() {
		t.Errorf("expected type String; got: %v", val.Type())
	}
	s, err := val.ToString()
	if err != nil {
		t.Fatal(err)
	}
	if s != "Hello" {
		t.Errorf("expected %q; got: %q", "Hello", s)
	}
}

func TestSymbolType(t *testing.T) {
	ctx := NewContext()
	if ctx == nil {
		t.Fatal("expected NewContext() to be != nil")
	}

	val, err := ctx.LoadString(":Hello")
	if err != nil {
		t.Fatal(err)
	}
	if !val.IsSymbol() {
		t.Errorf("expected type Symbol; got: %v", val.Type())
	}
	s, err := val.ToString()
	if err != nil {
		t.Fatal(err)
	}
	if s != "Hello" {
		t.Errorf("expected %q; got: %q", "Hello", s)
	}
}

func TestArrayType(t *testing.T) {
	ctx := NewContext()
	if ctx == nil {
		t.Fatal("expected NewContext() to be != nil")
	}

	val, err := ctx.LoadString("[1,2,'Oliver']")
	if err != nil {
		t.Fatal(err)
	}
	if !val.IsArray() {
		t.Errorf("expected type Array; got: %v", val.Type())
	}

	got, err := val.ToArray()
	if err != nil {
		t.Fatal(err)
	}
	expected := []interface{}{1, 2, "Oliver"}
	if !reflect.DeepEqual(got, expected) {
		t.Errorf("expected %v; got: %v", expected, got)
	}
}

func TestMapType(t *testing.T) {
	ctx := NewContext()
	if ctx == nil {
		t.Fatal("expected NewContext() to be != nil")
	}

	val, err := ctx.LoadString("{city: 'Munich', :name => 'Oliver', age: 21}")
	if err != nil {
		t.Fatal(err)
	}
	if !val.IsHash() {
		t.Errorf("expected type Hash; got: %v", val.Type())
	}

	got, err := val.ToMap()
	if err != nil {
		t.Fatal(err)
	}
	expected := map[string]interface{}{"city": "Munich", "name": "Oliver", "age": 21}
	if !reflect.DeepEqual(got, expected) {
		t.Errorf("expected %v; got: %v", expected, got)
	}
}

func TestExceptionType(t *testing.T) {
	ctx := NewContext()
	if ctx == nil {
		t.Fatal("expected NewContext() to be != nil")
	}

	_, err := ctx.LoadString("raise 'bang bang'")
	if err == nil {
		t.Fatal("expected error")
	}
}

func TestArrayOfHashes(t *testing.T) {
	ctx := NewContext()
	if ctx == nil {
		t.Fatal("expected NewContext() to be != nil")
	}

	in := []map[string]interface{}{
		{
			"a": 1,
			"b": 2,
		},
		{
			"a": 17,
			"b": 4,
		},
	}

	script := `
ARGV[0].each do |hsh|
	hsh[:c] = hsh["a"] + hsh["b"]
end
`

	val, err := ctx.LoadString(script, in)
	if err != nil {
		t.Fatal(err)
	}
	if !val.IsArray() {
		t.Errorf("expected type Array; got: %v", val.Type())
	}

	got, err := val.ToArray()
	if err != nil {
		t.Fatal(err)
	}
	if len(got) != 2 {
		t.Fatalf("expected %d entries; got: %d", 2, len(got))
	}

	ent, ok := got[0].(map[string]interface{})
	if !ok {
		t.Fatal("expected entry to be a map")
	}
	a, found := ent["a"]
	if !found {
		t.Errorf("expected entry %q", "a")
	}
	if a != 1 {
		t.Errorf("expected entry %q = %d; got: %d", "a", 1, a)
	}
	b, found := ent["b"]
	if !found {
		t.Errorf("expected entry %q", "b")
	}
	if b != 2 {
		t.Errorf("expected entry %q = %d; got: %d", "b", 2, b)
	}
	c, found := ent["c"]
	if !found {
		t.Errorf("expected entry %q", "c")
	}
	if c != 3 {
		t.Errorf("expected entry %q = %d; got: %d", "c", 3, c)
	}

	ent, ok = got[1].(map[string]interface{})
	if !ok {
		t.Fatal("expected entry to be a map")
	}
	a, found = ent["a"]
	if !found {
		t.Errorf("expected entry %q", "a")
	}
	if a != 17 {
		t.Errorf("expected entry %q = %d; got: %d", "a", 17, a)
	}
	b, found = ent["b"]
	if !found {
		t.Errorf("expected entry %q", "b")
	}
	if b != 4 {
		t.Errorf("expected entry %q = %d; got: %d", "b", 4, b)
	}
	c, found = ent["c"]
	if !found {
		t.Errorf("expected entry %q", "c")
	}
	if c != 21 {
		t.Errorf("expected entry %q = %d; got: %d", "c", 21, c)
	}
}
<|endoftext|>


<commit_before>// goncurses - ncurses library for Go.
// Copyright 2011 Rob Thornton. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package goncurses is a new curses (ncurses) library for the Go programming
// language. It implements all the ncurses extension libraries: form, menu and
// panel.
//
// Minimal operation would consist of initializing the display:
//
// 	src, err := goncurses.Init()
// 	if err != nil {
// 		log.Fatal("init:", err)
// 	}
// 	defer goncurses.End()
//
// It is important to always call End() before your program exits. If you
// fail to do so, the terminal will not perform properly and will either
// need to be reset or restarted completely.
//
// The examples directory contains demontrations of many of the capabilities
// goncurses can provide.
package goncurses

// #cgo pkg-config: ncurses
// #include <ncurses.h>
// #include "goncurses.h"
import "C"

import (
	"errors"
	"fmt"
	"unsafe"
)

// BaudRate returns the speed of the terminal in bits per second
func BaudRate() int {
	return int(C.baudrate())
}

// Beep requests the terminal make an audible bell or, if not available,
// flashes the screen. Note that screen flashing doesn't work on all
// terminals
func Beep() {
	C.beep()
}

// Turn on/off buffering; raw user signals are passed to the program for
// handling. Overrides raw mode
func CBreak(on bool) {
	if on {
		C.cbreak()
		return
	}
	C.nocbreak()
}

// Test whether colour values can be changed
func CanChangeColor() bool {
	return bool(C.bool(C.can_change_color()))
}

// Get RGB values for specified colour
func ColorContent(col int) (int, int, int) {
	var r, g, b C.short
	C.color_content(C.short(col), (*C.short)(&r), (*C.short)(&g),
		(*C.short)(&b))
	return int(r), int(g), int(b)
}

// Return the value of a color pair which can be passed to functions which
// accept attributes like AddChar or AttrOn/Off.
func ColorPair(pair int) int {
	return int(C.COLOR_PAIR(C.int(pair)))
}

// CursesVersion returns the version of the ncurses library currently linked to
func CursesVersion() string {
	return C.GoString(C.curses_version())
}

// Set the cursor visibility. Options are: 0 (invisible/hidden), 1 (normal)
// and 2 (extra-visible)
func Cursor(vis byte) error {
	if C.curs_set(C.int(vis)) == C.ERR {
		return errors.New("Failed to enable ")
	}
	return nil
}

// Echo turns on/off the printing of typed characters
func Echo(on bool) {
	if on {
		C.echo()
		return
	}
	C.noecho()
}

// Must be called prior to exiting the program in order to make sure the
// terminal returns to normal operation
func End() {
	C.endwin()
}

// Flash requests the terminal flashes the screen or, if not available,
// make an audible bell. Note that screen flashing doesn't work on all
// terminals
func Flash() {
	C.flash()
}

// FlushInput flushes all input
func FlushInput() error {
	if C.flushinp() == C.ERR {
		return errors.New("Flush input failed")
	}
	return nil
}

// Returns an array of integers representing the following, in order:
// x, y and z coordinates, id of the device, and a bit masked state of
// the devices buttons
func GetMouse() ([]int, error) {
	if bool(C.ncurses_has_mouse()) != true {
		return nil, errors.New("Mouse support not enabled")
	}
	var event C.MEVENT
	if C.getmouse(&event) != C.OK {
		return nil, errors.New("Failed to get mouse event")
	}
	return []int{int(event.x), int(event.y), int(event.z), int(event.id),
		int(event.bstate)}, nil
}

// Behaves like cbreak() but also adds a timeout for input. If timeout is
// exceeded after a call to Getch() has been made then GetChar will return
// with an error.
func HalfDelay(delay int) error {
	var cerr C.int
	if delay > 0 {
		cerr = C.halfdelay(C.int(delay))
	}
	if cerr == C.ERR {
		return errors.New("Unable to set delay mode")
	}
	return nil
}

// HasColors returns true if terminal can display colors
func HasColors() bool {
	return bool(C.has_colors())
}

// HasInsertCharacter return true if the terminal has insert and delete
// character capabilities
func HasInsertCharacter() bool {
	return bool(C.has_ic())
}

// HasInsertLine returns true if the terminal has insert and delete line
// capabilities. See ncurses documentation for more details
func HasInsertLine() bool {
	return bool(C.has_il())
}

// HasKey returns true if terminal recognized the given character
func HasKey(ch Key) bool {
	if C.has_key(C.int(ch)) == 1 {
		return true
	}
	return false
}

// InitColor is used to set 'color' to the specified RGB values. Values may
// be between 0 and 1000.
func InitColor(col int, r, g, b int) error {
	if C.init_color(C.short(col), C.short(r), C.short(g),
		C.short(b)) == C.ERR {
		return errors.New("Failed to set new color definition")
	}
	return nil
}

// InitPair sets a colour pair designated by 'pair' to fg and bg colors
func InitPair(pair byte, fg, bg int) error {
	if pair == 0 || C.int(pair) > (C.COLOR_PAIRS-1) {
		return errors.New("Invalid color pair selected")
	}
	if C.init_pair(C.short(pair), C.short(fg), C.short(bg)) == C.ERR {
		return errors.New("Failed to init color pair")
	}
	return nil
}

// Initialize the ncurses library. You must run this function prior to any
// other goncurses function in order for the library to work
func Init() (stdscr Window, err error) {
	stdscr = Window{C.initscr()}
	if unsafe.Pointer(stdscr.win) == nil {
		err = errors.New("An error occurred initializing ncurses")
	}
	return
}

// IsEnd returns true if End() has been called, otherwise false
func IsEnd() bool {
	return bool(C.isendwin())
}

// IsTermResized returns true if ResizeTerm would modify any current Windows
// if called with the given parameters
func IsTermResized(nlines, ncols int) bool {
	return bool(C.is_term_resized(C.int(nlines), C.int(ncols)))
}

// Returns a string representing the value of input returned by Getch
func KeyString(k Key) string {
	key, ok := keyList[k]
	if !ok {
		key = fmt.Sprintf("%c", int(k))
	}
	return key
}

func Mouse() bool {
	return bool(C.ncurses_has_mouse())
}

func MouseInterval() {
}

// MouseMask accepts a single int of OR'd mouse events. If a mouse event
// is triggered, GetChar() will return KEY_MOUSE. To retrieve the actual
// event use GetMouse() to pop it off the queue. Pass a pointer as the
// second argument to store the prior events being monitored or nil.
func MouseMask(mask int, old *int) (m int) {
	if bool(C.ncurses_has_mouse()) {
		m = int(C.mousemask((C.mmask_t)(mask),
			(*C.mmask_t)(unsafe.Pointer(old))))
	}
	return
}

// NapMilliseconds is used to sleep for ms milliseconds
func NapMilliseconds(ms int) {
	C.napms(C.int(ms))
}

// NewWindow creates a window of size h(eight) and w(idth) at y, x
func NewWindow(h, w, y, x int) (window Window, err error) {
	window = Window{C.newwin(C.int(h), C.int(w), C.int(y), C.int(x))}
	if window.win == nil {
		err = errors.New("Failed to create a new window")
	}
	return
}

// NL turns newline translation on/off.
func NL(on bool) {
	if on {
		C.nl()
		return
	}
	C.nonl()
}

// Raw turns on input buffering; user signals are disabled and the key strokes
// are passed directly to input. Set to false if you wish to turn this mode
// off
func Raw(on bool) {
	if on {
		C.raw()
		return
	}
	C.noraw()
}

// ResizeTerm will attempt to resize the terminal. This only has an effect if
// the terminal is in an XWindows (GUI) environment.
func ResizeTerm(nlines, ncols int) error {
	if C.resizeterm(C.int(nlines), C.int(ncols)) == C.ERR {
		return errors.New("Failed to resize terminal")
	}
	return nil
}

// Enables colors to be displayed. Will return an error if terminal is not
// capable of displaying colors
func StartColor() error {
	if C.has_colors() == C.bool(false) {
		return errors.New("Terminal does not support colors")
	}
	if C.start_color() == C.ERR {
		return errors.New("Failed to enable color mode")
	}
	return nil
}

// UnGetChar places the character back into the input queue
func UnGetChar(ch Character) {
	C.ungetch(C.int(ch))
}

// Update the screen, refreshing all windows
func Update() error {
	if C.doupdate() == C.ERR {
		return errors.New("Failed to update")
	}
	return nil
}

// UseEnvironment specifies whether the LINES and COLUMNS environmental
// variables should be used or not
func UseEnvironment(use bool) {
	C.use_env(C.bool(use))
}
<commit_msg>Fix MouseMask to workproperly<commit_after>// goncurses - ncurses library for Go.
// Copyright 2011 Rob Thornton. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package goncurses is a new curses (ncurses) library for the Go programming
// language. It implements all the ncurses extension libraries: form, menu and
// panel.
//
// Minimal operation would consist of initializing the display:
//
// 	src, err := goncurses.Init()
// 	if err != nil {
// 		log.Fatal("init:", err)
// 	}
// 	defer goncurses.End()
//
// It is important to always call End() before your program exits. If you
// fail to do so, the terminal will not perform properly and will either
// need to be reset or restarted completely.
//
// The examples directory contains demontrations of many of the capabilities
// goncurses can provide.
package goncurses

// #cgo pkg-config: ncurses
// #include <ncurses.h>
// #include "goncurses.h"
import "C"

import (
	"errors"
	"fmt"
	"unsafe"
)

// BaudRate returns the speed of the terminal in bits per second
func BaudRate() int {
	return int(C.baudrate())
}

// Beep requests the terminal make an audible bell or, if not available,
// flashes the screen. Note that screen flashing doesn't work on all
// terminals
func Beep() {
	C.beep()
}

// Turn on/off buffering; raw user signals are passed to the program for
// handling. Overrides raw mode
func CBreak(on bool) {
	if on {
		C.cbreak()
		return
	}
	C.nocbreak()
}

// Test whether colour values can be changed
func CanChangeColor() bool {
	return bool(C.bool(C.can_change_color()))
}

// Get RGB values for specified colour
func ColorContent(col int) (int, int, int) {
	var r, g, b C.short
	C.color_content(C.short(col), (*C.short)(&r), (*C.short)(&g),
		(*C.short)(&b))
	return int(r), int(g), int(b)
}

// Return the value of a color pair which can be passed to functions which
// accept attributes like AddChar or AttrOn/Off.
func ColorPair(pair int) int {
	return int(C.COLOR_PAIR(C.int(pair)))
}

// CursesVersion returns the version of the ncurses library currently linked to
func CursesVersion() string {
	return C.GoString(C.curses_version())
}

// Set the cursor visibility. Options are: 0 (invisible/hidden), 1 (normal)
// and 2 (extra-visible)
func Cursor(vis byte) error {
	if C.curs_set(C.int(vis)) == C.ERR {
		return errors.New("Failed to enable ")
	}
	return nil
}

// Echo turns on/off the printing of typed characters
func Echo(on bool) {
	if on {
		C.echo()
		return
	}
	C.noecho()
}

// Must be called prior to exiting the program in order to make sure the
// terminal returns to normal operation
func End() {
	C.endwin()
}

// Flash requests the terminal flashes the screen or, if not available,
// make an audible bell. Note that screen flashing doesn't work on all
// terminals
func Flash() {
	C.flash()
}

// FlushInput flushes all input
func FlushInput() error {
	if C.flushinp() == C.ERR {
		return errors.New("Flush input failed")
	}
	return nil
}

// Returns an array of integers representing the following, in order:
// x, y and z coordinates, id of the device, and a bit masked state of
// the devices buttons
func GetMouse() ([]int, error) {
	if bool(C.ncurses_has_mouse()) != true {
		return nil, errors.New("Mouse support not enabled")
	}
	var event C.MEVENT
	if C.getmouse(&event) != C.OK {
		return nil, errors.New("Failed to get mouse event")
	}
	return []int{int(event.x), int(event.y), int(event.z), int(event.id),
		int(event.bstate)}, nil
}

// Behaves like cbreak() but also adds a timeout for input. If timeout is
// exceeded after a call to Getch() has been made then GetChar will return
// with an error.
func HalfDelay(delay int) error {
	var cerr C.int
	if delay > 0 {
		cerr = C.halfdelay(C.int(delay))
	}
	if cerr == C.ERR {
		return errors.New("Unable to set delay mode")
	}
	return nil
}

// HasColors returns true if terminal can display colors
func HasColors() bool {
	return bool(C.has_colors())
}

// HasInsertCharacter return true if the terminal has insert and delete
// character capabilities
func HasInsertCharacter() bool {
	return bool(C.has_ic())
}

// HasInsertLine returns true if the terminal has insert and delete line
// capabilities. See ncurses documentation for more details
func HasInsertLine() bool {
	return bool(C.has_il())
}

// HasKey returns true if terminal recognized the given character
func HasKey(ch Key) bool {
	if C.has_key(C.int(ch)) == 1 {
		return true
	}
	return false
}

// InitColor is used to set 'color' to the specified RGB values. Values may
// be between 0 and 1000.
func InitColor(col int, r, g, b int) error {
	if C.init_color(C.short(col), C.short(r), C.short(g),
		C.short(b)) == C.ERR {
		return errors.New("Failed to set new color definition")
	}
	return nil
}

// InitPair sets a colour pair designated by 'pair' to fg and bg colors
func InitPair(pair byte, fg, bg int) error {
	if pair == 0 || C.int(pair) > (C.COLOR_PAIRS-1) {
		return errors.New("Invalid color pair selected")
	}
	if C.init_pair(C.short(pair), C.short(fg), C.short(bg)) == C.ERR {
		return errors.New("Failed to init color pair")
	}
	return nil
}

// Initialize the ncurses library. You must run this function prior to any
// other goncurses function in order for the library to work
func Init() (stdscr Window, err error) {
	stdscr = Window{C.initscr()}
	if unsafe.Pointer(stdscr.win) == nil {
		err = errors.New("An error occurred initializing ncurses")
	}
	return
}

// IsEnd returns true if End() has been called, otherwise false
func IsEnd() bool {
	return bool(C.isendwin())
}

// IsTermResized returns true if ResizeTerm would modify any current Windows
// if called with the given parameters
func IsTermResized(nlines, ncols int) bool {
	return bool(C.is_term_resized(C.int(nlines), C.int(ncols)))
}

// Returns a string representing the value of input returned by Getch
func KeyString(k Key) string {
	key, ok := keyList[k]
	if !ok {
		key = fmt.Sprintf("%c", int(k))
	}
	return key
}

func Mouse() bool {
	return bool(C.ncurses_has_mouse())
}

func MouseInterval() {
}

// MouseMask accepts a single int of OR'd mouse events. If a mouse event
// is triggered, GetChar() will return KEY_MOUSE. To retrieve the actual
// event use GetMouse() to pop it off the queue. Pass a pointer as the
// second argument to store the prior events being monitored or nil.
func MouseMask(mask MouseButton, old *MouseButton) int {
	return int(C.mousemask((C.mmask_t)(mask),
		(*C.mmask_t)(unsafe.Pointer(old))))
}

// NapMilliseconds is used to sleep for ms milliseconds
func NapMilliseconds(ms int) {
	C.napms(C.int(ms))
}

// NewWindow creates a window of size h(eight) and w(idth) at y, x
func NewWindow(h, w, y, x int) (window Window, err error) {
	window = Window{C.newwin(C.int(h), C.int(w), C.int(y), C.int(x))}
	if window.win == nil {
		err = errors.New("Failed to create a new window")
	}
	return
}

// NL turns newline translation on/off.
func NL(on bool) {
	if on {
		C.nl()
		return
	}
	C.nonl()
}

// Raw turns on input buffering; user signals are disabled and the key strokes
// are passed directly to input. Set to false if you wish to turn this mode
// off
func Raw(on bool) {
	if on {
		C.raw()
		return
	}
	C.noraw()
}

// ResizeTerm will attempt to resize the terminal. This only has an effect if
// the terminal is in an XWindows (GUI) environment.
func ResizeTerm(nlines, ncols int) error {
	if C.resizeterm(C.int(nlines), C.int(ncols)) == C.ERR {
		return errors.New("Failed to resize terminal")
	}
	return nil
}

// Enables colors to be displayed. Will return an error if terminal is not
// capable of displaying colors
func StartColor() error {
	if C.has_colors() == C.bool(false) {
		return errors.New("Terminal does not support colors")
	}
	if C.start_color() == C.ERR {
		return errors.New("Failed to enable color mode")
	}
	return nil
}

// UnGetChar places the character back into the input queue
func UnGetChar(ch Character) {
	C.ungetch(C.int(ch))
}

// Update the screen, refreshing all windows
func Update() error {
	if C.doupdate() == C.ERR {
		return errors.New("Failed to update")
	}
	return nil
}

// UseEnvironment specifies whether the LINES and COLUMNS environmental
// variables should be used or not
func UseEnvironment(use bool) {
	C.use_env(C.bool(use))
}
<|endoftext|>


<commit_before>package vcs

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

type HgRepositoryCmd struct {
	Dir string
}

func (r *HgRepositoryCmd) ResolveRevision(spec string) (CommitID, error) {
	cmd := exec.Command("hg", "identify", "--debug", "-i", "--rev="+spec)
	cmd.Dir = r.Dir
	out, err := cmd.CombinedOutput()
	if err != nil {
		return "", fmt.Errorf("exec `hg identify` failed: %s. Output was:\n\n%s", err, out)
	}
	return CommitID(bytes.TrimSpace(out)), nil
}

func (r *HgRepositoryCmd) ResolveTag(name string) (CommitID, error) {
	return r.ResolveRevision(name)
}

func (r *HgRepositoryCmd) ResolveBranch(name string) (CommitID, error) {
	return r.ResolveRevision(name)
}

func (r *HgRepositoryCmd) GetCommit(id CommitID) (*Commit, error) {
	commits, err := r.commitLog(string(id))
	if err != nil {
		return nil, err
	}

	if len(commits) != 1 {
		return nil, fmt.Errorf("hg log: expected 1 commit, got %d", len(commits))
	}

	return commits[0], nil
}

func (r *HgRepositoryCmd) CommitLog(to CommitID) ([]*Commit, error) {
	return r.commitLog(string(to) + ":0")
}

var hgNullParentNodeID = []byte("0000000000000000000000000000000000000000")

func (r *HgRepositoryCmd) commitLog(revSpec string) ([]*Commit, error) {
	cmd := exec.Command("hg", "log", `--template={node}\x00{author|person}\x00{author|email}\x00{date|rfc3339date}\x00{desc}\x00{p1node}\x00{p2node}\x00`, "--rev="+revSpec)
	cmd.Dir = r.Dir
	out, err := cmd.CombinedOutput()
	if err != nil {
		return nil, fmt.Errorf("exec `hg log` failed: %s. Output was:\n\n%s", err, out)
	}

	const partsPerCommit = 7 // number of \x00-separated fields per commit
	allParts := bytes.Split(out, []byte{'\x00'})
	numCommits := len(allParts) / partsPerCommit
	commits := make([]*Commit, numCommits)
	for i := 0; i < numCommits; i++ {
		parts := allParts[partsPerCommit*i : partsPerCommit*(i+1)]

		authorTime, err := time.Parse(time.RFC3339, string(parts[3]))
		if err != nil {
			return nil, err
		}

		var parents []CommitID
		if p1 := parts[5]; len(p1) > 0 && !bytes.Equal(p1, hgNullParentNodeID) {
			parents = append(parents, CommitID(p1))
		}
		if p2 := parts[6]; len(p2) > 0 && !bytes.Equal(p2, hgNullParentNodeID) {
			parents = append(parents, CommitID(p2))
		}

		commits[i] = &Commit{
			ID:      CommitID(parts[0]),
			Author:  Signature{string(parts[1]), string(parts[2]), authorTime},
			Message: string(parts[4]),
			Parents: parents,
		}
	}
	return commits, nil
}

func (r *HgRepositoryCmd) FileSystem(at CommitID) (FileSystem, error) {
	return &hgFSCmd{
		dir: r.Dir,
		at:  at,
	}, nil
}

type hgFSCmd struct {
	dir string
	at  CommitID
}

func (fs *hgFSCmd) Open(name string) (ReadSeekCloser, error) {
	cmd := exec.Command("hg", "cat", "--rev="+string(fs.at), "--", name)
	cmd.Dir = fs.dir
	out, err := cmd.CombinedOutput()
	if err != nil {
		if bytes.Contains(out, []byte("no such file in rev")) {
			return nil, os.ErrNotExist
		}
		return nil, fmt.Errorf("exec `hg cat` failed: %s. Output was:\n\n%s", err, out)
	}
	return nopCloser{bytes.NewReader(out)}, nil
}

func (fs *hgFSCmd) Lstat(path string) (os.FileInfo, error) {
	return fs.Stat(path)
}

func (fs *hgFSCmd) Stat(path string) (os.FileInfo, error) {
	// TODO(sqs): follow symlinks (as Stat is required to do)

	// this just determines if the file exists.
	cmd := exec.Command("hg", "locate", "--rev="+string(fs.at), "--", path)
	cmd.Dir = fs.dir
	err := cmd.Run()
	if err != nil {
		// hg doesn't track dirs, so use a workaround to see if path is a dir.
		if _, err := fs.ReadDir(path); err == nil {
			return &fileInfo{name: filepath.Base(path), mode: os.ModeDir}, nil
		}
		return nil, os.ErrNotExist
	}

	// read file to determine file size
	f, err := fs.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	data, err := ioutil.ReadAll(f)

	return &fileInfo{name: filepath.Base(path), size: int64(len(data))}, nil
}

func (fs *hgFSCmd) ReadDir(path string) ([]os.FileInfo, error) {
	path = filepath.Clean(path)
	// This combination of --include and --exclude opts gets all the files in
	// the dir specified by path, plus all files one level deeper (but no
	// deeper). This lets us list the files *and* subdirs in the dir without
	// needlessly listing recursively.
	cmd := exec.Command("hg", "locate", "--rev="+string(fs.at), "--include="+path, "--exclude="+filepath.Clean(path)+"/*/**/*")
	cmd.Dir = fs.dir
	out, err := cmd.CombinedOutput()
	if err != nil {
		return nil, fmt.Errorf("exec `hg cat` failed: %s. Output was:\n\n%s", err, out)
	}

	subdirs := make(map[string]struct{})
	prefix := []byte(path + "/")
	files := bytes.Split(out, []byte{'\n'})
	var fis []os.FileInfo
	for _, nameb := range files {
		nameb = bytes.TrimPrefix(nameb, prefix)
		if len(nameb) == 0 {
			continue
		}
		if bytes.Contains(nameb, []byte{'/'}) {
			subdir := strings.SplitN(string(nameb), "/", 2)[0]
			if _, seen := subdirs[subdir]; !seen {
				fis = append(fis, &fileInfo{name: subdir, mode: os.ModeDir})
				subdirs[subdir] = struct{}{}
			}
			continue
		}
		fis = append(fis, &fileInfo{name: filepath.Base(string(nameb))})
	}

	return fis, nil
}

func (fs *hgFSCmd) String() string {
	return fmt.Sprintf("hg repository %s commit %s (cmd)", fs.dir, fs.at)
}
<commit_msg>Fix --exclude in hg_cmd; ignoring directories that it shouldn't.<commit_after>package vcs

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

type HgRepositoryCmd struct {
	Dir string
}

func (r *HgRepositoryCmd) ResolveRevision(spec string) (CommitID, error) {
	cmd := exec.Command("hg", "identify", "--debug", "-i", "--rev="+spec)
	cmd.Dir = r.Dir
	out, err := cmd.CombinedOutput()
	if err != nil {
		return "", fmt.Errorf("exec `hg identify` failed: %s. Output was:\n\n%s", err, out)
	}
	return CommitID(bytes.TrimSpace(out)), nil
}

func (r *HgRepositoryCmd) ResolveTag(name string) (CommitID, error) {
	return r.ResolveRevision(name)
}

func (r *HgRepositoryCmd) ResolveBranch(name string) (CommitID, error) {
	return r.ResolveRevision(name)
}

func (r *HgRepositoryCmd) GetCommit(id CommitID) (*Commit, error) {
	commits, err := r.commitLog(string(id))
	if err != nil {
		return nil, err
	}

	if len(commits) != 1 {
		return nil, fmt.Errorf("hg log: expected 1 commit, got %d", len(commits))
	}

	return commits[0], nil
}

func (r *HgRepositoryCmd) CommitLog(to CommitID) ([]*Commit, error) {
	return r.commitLog(string(to) + ":0")
}

var hgNullParentNodeID = []byte("0000000000000000000000000000000000000000")

func (r *HgRepositoryCmd) commitLog(revSpec string) ([]*Commit, error) {
	cmd := exec.Command("hg", "log", `--template={node}\x00{author|person}\x00{author|email}\x00{date|rfc3339date}\x00{desc}\x00{p1node}\x00{p2node}\x00`, "--rev="+revSpec)
	cmd.Dir = r.Dir
	out, err := cmd.CombinedOutput()
	if err != nil {
		return nil, fmt.Errorf("exec `hg log` failed: %s. Output was:\n\n%s", err, out)
	}

	const partsPerCommit = 7 // number of \x00-separated fields per commit
	allParts := bytes.Split(out, []byte{'\x00'})
	numCommits := len(allParts) / partsPerCommit
	commits := make([]*Commit, numCommits)
	for i := 0; i < numCommits; i++ {
		parts := allParts[partsPerCommit*i : partsPerCommit*(i+1)]

		authorTime, err := time.Parse(time.RFC3339, string(parts[3]))
		if err != nil {
			return nil, err
		}

		var parents []CommitID
		if p1 := parts[5]; len(p1) > 0 && !bytes.Equal(p1, hgNullParentNodeID) {
			parents = append(parents, CommitID(p1))
		}
		if p2 := parts[6]; len(p2) > 0 && !bytes.Equal(p2, hgNullParentNodeID) {
			parents = append(parents, CommitID(p2))
		}

		commits[i] = &Commit{
			ID:      CommitID(parts[0]),
			Author:  Signature{string(parts[1]), string(parts[2]), authorTime},
			Message: string(parts[4]),
			Parents: parents,
		}
	}
	return commits, nil
}

func (r *HgRepositoryCmd) FileSystem(at CommitID) (FileSystem, error) {
	return &hgFSCmd{
		dir: r.Dir,
		at:  at,
	}, nil
}

type hgFSCmd struct {
	dir string
	at  CommitID
}

func (fs *hgFSCmd) Open(name string) (ReadSeekCloser, error) {
	cmd := exec.Command("hg", "cat", "--rev="+string(fs.at), "--", name)
	cmd.Dir = fs.dir
	out, err := cmd.CombinedOutput()
	if err != nil {
		if bytes.Contains(out, []byte("no such file in rev")) {
			return nil, os.ErrNotExist
		}
		return nil, fmt.Errorf("exec `hg cat` failed: %s. Output was:\n\n%s", err, out)
	}
	return nopCloser{bytes.NewReader(out)}, nil
}

func (fs *hgFSCmd) Lstat(path string) (os.FileInfo, error) {
	return fs.Stat(path)
}

func (fs *hgFSCmd) Stat(path string) (os.FileInfo, error) {
	// TODO(sqs): follow symlinks (as Stat is required to do)

	// this just determines if the file exists.
	cmd := exec.Command("hg", "locate", "--rev="+string(fs.at), "--", path)
	cmd.Dir = fs.dir
	err := cmd.Run()
	if err != nil {
		// hg doesn't track dirs, so use a workaround to see if path is a dir.
		if _, err := fs.ReadDir(path); err == nil {
			return &fileInfo{name: filepath.Base(path), mode: os.ModeDir}, nil
		}
		return nil, os.ErrNotExist
	}

	// read file to determine file size
	f, err := fs.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	data, err := ioutil.ReadAll(f)

	return &fileInfo{name: filepath.Base(path), size: int64(len(data))}, nil
}

func (fs *hgFSCmd) ReadDir(path string) ([]os.FileInfo, error) {
	path = filepath.Clean(path)
	// This combination of --include and --exclude opts gets all the files in
	// the dir specified by path, plus all files one level deeper (but no
	// deeper). This lets us list the files *and* subdirs in the dir without
	// needlessly listing recursively.
	cmd := exec.Command("hg", "locate", "--rev="+string(fs.at), "--include="+path, "--exclude="+filepath.Clean(path)+"/*/*/*")
	cmd.Dir = fs.dir
	out, err := cmd.CombinedOutput()
	if err != nil {
		return nil, fmt.Errorf("exec `hg cat` failed: %s. Output was:\n\n%s", err, out)
	}

	subdirs := make(map[string]struct{})
	prefix := []byte(path + "/")
	files := bytes.Split(out, []byte{'\n'})
	var fis []os.FileInfo
	for _, nameb := range files {
		nameb = bytes.TrimPrefix(nameb, prefix)
		if len(nameb) == 0 {
			continue
		}
		if bytes.Contains(nameb, []byte{'/'}) {
			subdir := strings.SplitN(string(nameb), "/", 2)[0]
			if _, seen := subdirs[subdir]; !seen {
				fis = append(fis, &fileInfo{name: subdir, mode: os.ModeDir})
				subdirs[subdir] = struct{}{}
			}
			continue
		}
		fis = append(fis, &fileInfo{name: filepath.Base(string(nameb))})
	}

	return fis, nil
}

func (fs *hgFSCmd) String() string {
	return fmt.Sprintf("hg repository %s commit %s (cmd)", fs.dir, fs.at)
}
<|endoftext|>


<commit_before>package etcd

import (
	"context"
	"testing"
	"time"

	"encoding/json"

	"github.com/coreos/etcd/client"
	"github.com/stretchr/testify/assert"
)

func TestClientStruct(t *testing.T) {
	cfg := &Config{
		Clusters: []string{"http://localhost:2379"},
	}
	// local etcd server without tls and basic auth
	cl, err := NewClient(cfg)
	assert.Nil(t, err)

	t.Run("watchNext", func(t *testing.T) {
		assert := assert.New(t)
		key := "/confl/test1/watchnext"
		values := []string{
			"test1",
			"test2",
			"test3",
		}
		valueCh := make(chan string)
		go func() {
			for {
				resp, err := cl.watchNext(key)
				assert.Nil(err)
				value := <-valueCh
				assert.Equal(value, resp.Node.Value)
			}
		}()
		time.Sleep(time.Second)
		for _, value := range values {
			_, err := cl.client.Set(context.Background(), key, value, &client.SetOptions{TTL: 10 * time.Second})
			assert.Nil(err)
			valueCh <- value
		}
	})

	t.Run("Key", func(t *testing.T) {
		assert := assert.New(t)
		key := "/confl/test1/key"
		values := []string{
			"test1",
			"test2",
			"test3",
		}
		for _, value := range values {
			_, err := cl.client.Set(context.Background(), key, value, &client.SetOptions{TTL: 10 * time.Second})
			assert.Nil(err)
			v, err := cl.Key(key)
			assert.Nil(err)
			assert.Equal(value, v)
		}
	})

	t.Run("WatchKey", func(t *testing.T) {
		assert := assert.New(t)
		type config struct {
			Name string `json:"name"`
			Age  int    `json:"age"`
		}
		key := "/confl/test1/watchkey"
		values := []config{
			config{Name: "confl1", Age: 1},
			config{Name: "confl2", Age: 2},
			config{Name: "confl3", Age: 3},
		}

		changeCh := make(chan struct{})
		valueCh := make(chan string)
		doneCh := make(chan struct{})
		go cl.WatchKey(key, changeCh)
		go func() {
			for range changeCh {
				v := <-valueCh
				value, err := cl.Key(key)
				assert.Nil(err)
				assert.Equal(v, value)
				doneCh <- struct{}{}
			}
		}()
		for _, value := range values {
			data, err := json.Marshal(value)
			assert.Nil(err)
			v := string(data)
			_, err = cl.client.Set(context.Background(), key, v, &client.SetOptions{TTL: 10 * time.Second})
			valueCh <- v
			assert.Nil(err)
			<-doneCh
		}
	})
}
<commit_msg>fix test case<commit_after>package etcd

import (
	"context"
	"testing"
	"time"

	"encoding/json"

	"github.com/coreos/etcd/client"
	"github.com/stretchr/testify/assert"
)

func TestClientStruct(t *testing.T) {
	cfg := &Config{
		Clusters: []string{"http://localhost:2379"},
	}
	// local etcd server without tls and basic auth
	cl, err := NewClient(cfg)
	assert.Nil(t, err)

	t.Run("Key", func(t *testing.T) {
		assert := assert.New(t)
		key := "/confl/test1/key"
		values := []string{
			"test1",
			"test2",
			"test3",
		}
		for _, value := range values {
			_, err := cl.client.Set(context.Background(), key, value, &client.SetOptions{})
			assert.Nil(err)
			v, err := cl.Key(key)
			assert.Nil(err)
			assert.Equal(value, v)
		}
	})

	t.Run("watchNext", func(t *testing.T) {
		assert := assert.New(t)
		key := "/confl/test1/watchnext"
		values := []string{
			"test1",
			"test2",
			"test3",
		}
		valueCh := make(chan string)
		go func() {
			for {
				resp, err := cl.watchNext(key)
				value := <-valueCh
				assert.Nil(err)
				assert.Equal(value, resp.Node.Value)
			}
		}()
		time.Sleep(time.Second)
		for _, value := range values {
			_, err := cl.client.Set(context.Background(), key, value, &client.SetOptions{})
			assert.Nil(err)
			valueCh <- value
		}
	})

	t.Run("WatchKey", func(t *testing.T) {
		assert := assert.New(t)
		type config struct {
			Name string `json:"name"`
			Age  int    `json:"age"`
		}
		key := "/confl/test1/watchkey"
		values := []config{
			config{Name: "confl1", Age: 1},
			config{Name: "confl2", Age: 2},
			config{Name: "confl3", Age: 3},
		}

		changeCh := make(chan struct{})
		valueCh := make(chan string)
		doneCh := make(chan struct{})
		go cl.WatchKey(key, changeCh)
		go func() {
			for range changeCh {
				v := <-valueCh
				value, err := cl.Key(key)
				assert.Nil(err)
				assert.Equal(v, value)
				doneCh <- struct{}{}
			}
		}()
		for _, value := range values {
			data, err := json.Marshal(value)
			assert.Nil(err)
			v := string(data)
			_, err = cl.client.Set(context.Background(), key, v, &client.SetOptions{})
			valueCh <- v
			assert.Nil(err)
			<-doneCh
		}
	})
}
<|endoftext|>


<commit_before>package cli

import (
	"flag"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
	"sync"

	"github.com/opentable/sous/config"
	"github.com/opentable/sous/graph"
	"github.com/opentable/sous/lib"
	"github.com/opentable/sous/util/cmdr"
)

// SousQueryGDM is the description of the `sous query gdm` command
type SousQueryGDM struct {
	StateManager *graph.ClientStateManager
	flags        struct {
		filters string
		format  string
	}
	SousGraph *graph.SousGraph
}

func init() { QuerySubcommands["gdm"] = &SousQueryGDM{} }

const sousQueryGDMHelp = `The intended state of deployment for every project and every cluster known to Sous.

The results of 'sous query gdm' and 'sous query ads' will not be identical if
a problem is preventing sous from modifying the current state of Singularity.
`

// Help prints the help
func (*SousQueryGDM) Help() string { return sousQueryGDMHelp }

// RegisterOn adds options set by flags to the injection graph.
func (*SousQueryGDM) RegisterOn(psy Addable) {
	psy.Add(graph.DryrunNeither)
	psy.Add(&config.DeployFilterFlags{})
}

// AddFlags adds the flags for 'sous query gdm'.
func (sb *SousQueryGDM) AddFlags(fs *flag.FlagSet) {
	fs.StringVar(&sb.flags.filters, "filters", "", "filter the output, space-separated list, e.g. 'hasimage=true zeroinstances=false hasowners=true'")
	fs.StringVar(&sb.flags.format, "format", "table", "output format, one of (table, json)")
}

func (sb *SousQueryGDM) dump(ds sous.Deployments) cmdr.Result {
	var err error
	switch sb.flags.format {
	default:
		err = fmt.Errorf("output format %q not valid, pick one of: table, json", sb.flags.format)
		fallthrough
	case "table":
		sous.DumpDeployments(os.Stdout, ds)
	case "json":
		sous.JSONDeployments(os.Stdout, ds)
	}
	if err != nil {
		return cmdr.EnsureErrorResult(err)
	}
	return cmdr.Success()
}

type deployFilter func(sous.Deployments, bool) sous.Deployments
type boundFilter func(sous.Deployments) sous.Deployments

func simpleFilter(p func(*sous.Deployment) bool) deployFilter {
	return func(ds sous.Deployments, which bool) sous.Deployments {
		return ds.Filter(func(d *sous.Deployment) bool {
			return p(d) == which
		})
	}
}

func (sb *SousQueryGDM) availableFilters() map[string]deployFilter {
	return map[string]deployFilter{
		"hasimage": sb.hasImageFilter,
		"zeroinstances": simpleFilter(func(d *sous.Deployment) bool {
			return d.NumInstances == 0
		}),
		"hasowners": simpleFilter(func(d *sous.Deployment) bool {
			return len(d.Owners) != 0
		}),
	}
}

func (sb *SousQueryGDM) availableFilterNames() []string {
	var names []string
	for k := range sb.availableFilters() {
		names = append(names, k)
	}
	return names
}

func (sb *SousQueryGDM) hasImageFilter(deployments sous.Deployments, which bool) sous.Deployments {
	filtered := sous.NewDeployments()
	wg := sync.WaitGroup{}

	ds := deployments.Snapshot()

	wg.Add(len(ds))
	errs := make(chan error, len(ds))
	getArtifactMutex := sync.Mutex{}

	for _, d := range ds {
		d := d
		go func() {
			defer wg.Done()
			opts := graph.ArtifactOpts{
				SourceID: config.NewSourceIDFlags(d.SourceID),
			}
			getArtifactMutex.Lock()
			getArtifact, err := sb.SousGraph.GetGetArtifact(opts)
			getArtifactMutex.Unlock()
			if err != nil {
				errs <- err
				return
			}
			exists, err := getArtifact.ArtifactExists()
			if err != nil {
				errs <- err
				return
			}
			if exists {
				filtered.Add(d)
			}
		}()
	}
	wg.Wait()
	close(errs)

	for err := range errs {
		log.Println(err)
	}
	return filtered
}

func (sb *SousQueryGDM) badFilterNameError(attempted string) error {
	return cmdr.UsageErrorf("filter %q not recognised; pick one of: %s",
		attempted, strings.Join(sb.availableFilterNames(), ", "))
}

func (sb *SousQueryGDM) getFilter(name string) (deployFilter, error) {
	f, ok := sb.availableFilters()[name]
	if !ok {
		return nil, sb.badFilterNameError(name)
	}
	return f, nil
}

func (sb *SousQueryGDM) parseFilters() ([]boundFilter, error) {
	var filters []boundFilter
	if sb.flags.filters == "" {
		return nil, nil
	}
	parts := strings.Fields(sb.flags.filters)
	for _, p := range parts {
		kv := strings.Split(p, "=")
		if len(kv) != 2 {
			return nil, cmdr.UsageErrorf("filter %q not valid; format is <name>=(true|false)")
		}
		k, v := kv[0], kv[1]
		f, err := sb.getFilter(k)
		if err != nil {
			return nil, err
		}
		tf, err := strconv.ParseBool(v)
		if err != nil {
			return nil, cmdr.UsageErrorf("filter %q accepts true or false, not %q", k, v)
		}
		filters = append(filters, func(ds sous.Deployments) sous.Deployments {
			return f(ds, tf)
		})
	}
	return filters, nil
}

func (sb *SousQueryGDM) filter(ds sous.Deployments) (sous.Deployments, error) {
	filters, err := sb.parseFilters()
	if err != nil {
		return sous.NewDeployments(), err
	}
	for _, f := range filters {
		ds = f(ds)
	}
	return ds, nil
}

// Execute defines the behavior of `sous query gdm`.
func (sb *SousQueryGDM) Execute(args []string) cmdr.Result {

	state, err := sb.StateManager.ReadState()
	if err != nil {
		return EnsureErrorResult(err)
	}
	deployments, err := state.Deployments()

	totalCount := deployments.Len()

	if err != nil {
		return EnsureErrorResult(err)
	}

	filtered, err := sb.filter(deployments)
	if err != nil {
		return cmdr.EnsureErrorResult(err)
	}

	filteredCount := filtered.Len()
	log.Printf("%d results (of %q total deployments)", filteredCount, totalCount)
	return sb.dump(filtered)

}
<commit_msg>cli: fix hasimage filter<commit_after>package cli

import (
	"flag"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
	"sync"

	"github.com/opentable/sous/config"
	"github.com/opentable/sous/graph"
	"github.com/opentable/sous/lib"
	"github.com/opentable/sous/util/cmdr"
)

// SousQueryGDM is the description of the `sous query gdm` command
type SousQueryGDM struct {
	StateManager *graph.ClientStateManager
	flags        struct {
		filters string
		format  string
	}
	SousGraph *graph.SousGraph
}

func init() { QuerySubcommands["gdm"] = &SousQueryGDM{} }

const sousQueryGDMHelp = `The intended state of deployment for every project and every cluster known to Sous.

The results of 'sous query gdm' and 'sous query ads' will not be identical if
a problem is preventing sous from modifying the current state of Singularity.
`

// Help prints the help
func (*SousQueryGDM) Help() string { return sousQueryGDMHelp }

// RegisterOn adds options set by flags to the injection graph.
func (*SousQueryGDM) RegisterOn(psy Addable) {
	psy.Add(graph.DryrunNeither)
	psy.Add(&config.DeployFilterFlags{})
}

// AddFlags adds the flags for 'sous query gdm'.
func (sb *SousQueryGDM) AddFlags(fs *flag.FlagSet) {
	fs.StringVar(&sb.flags.filters, "filters", "", "filter the output, space-separated list, e.g. 'hasimage=true zeroinstances=false hasowners=true'")
	fs.StringVar(&sb.flags.format, "format", "table", "output format, one of (table, json)")
}

func (sb *SousQueryGDM) dump(ds sous.Deployments) cmdr.Result {
	var err error
	switch sb.flags.format {
	default:
		err = fmt.Errorf("output format %q not valid, pick one of: table, json", sb.flags.format)
		fallthrough
	case "table":
		sous.DumpDeployments(os.Stdout, ds)
	case "json":
		sous.JSONDeployments(os.Stdout, ds)
	}
	if err != nil {
		return cmdr.EnsureErrorResult(err)
	}
	return cmdr.Success()
}

type deployFilter func(sous.Deployments, bool) sous.Deployments
type boundFilter func(sous.Deployments) sous.Deployments

func simpleFilter(p func(*sous.Deployment) bool) deployFilter {
	return func(ds sous.Deployments, which bool) sous.Deployments {
		return ds.Filter(func(d *sous.Deployment) bool {
			return p(d) == which
		})
	}
}

func (sb *SousQueryGDM) availableFilters() map[string]deployFilter {
	return map[string]deployFilter{
		"hasimage": sb.hasImageFilter,
		"zeroinstances": simpleFilter(func(d *sous.Deployment) bool {
			return d.NumInstances == 0
		}),
		"hasowners": simpleFilter(func(d *sous.Deployment) bool {
			return len(d.Owners) != 0
		}),
	}
}

func (sb *SousQueryGDM) availableFilterNames() []string {
	var names []string
	for k := range sb.availableFilters() {
		names = append(names, k)
	}
	return names
}

func (sb *SousQueryGDM) hasImageFilter(deployments sous.Deployments, which bool) sous.Deployments {
	filtered := sous.NewDeployments()
	wg := sync.WaitGroup{}

	ds := deployments.Snapshot()

	wg.Add(len(ds))
	errs := make(chan error, len(ds))
	getArtifactMutex := sync.Mutex{}

	for _, d := range ds {
		d := d
		go func() {
			defer wg.Done()
			opts := graph.ArtifactOpts{
				SourceID: config.NewSourceIDFlags(d.SourceID),
			}
			getArtifactMutex.Lock()
			getArtifact, err := sb.SousGraph.GetGetArtifact(opts)
			getArtifactMutex.Unlock()
			if err != nil {
				errs <- err
				return
			}
			exists, err := getArtifact.ArtifactExists()
			if err != nil {
				errs <- err
				return
			}
			if exists == which {
				filtered.Add(d)
			}
		}()
	}
	wg.Wait()
	close(errs)

	for err := range errs {
		log.Println(err)
	}
	return filtered
}

func (sb *SousQueryGDM) badFilterNameError(attempted string) error {
	return cmdr.UsageErrorf("filter %q not recognised; pick one of: %s",
		attempted, strings.Join(sb.availableFilterNames(), ", "))
}

func (sb *SousQueryGDM) getFilter(name string) (deployFilter, error) {
	f, ok := sb.availableFilters()[name]
	if !ok {
		return nil, sb.badFilterNameError(name)
	}
	return f, nil
}

func (sb *SousQueryGDM) parseFilters() ([]boundFilter, error) {
	var filters []boundFilter
	if sb.flags.filters == "" {
		return nil, nil
	}
	parts := strings.Fields(sb.flags.filters)
	for _, p := range parts {
		kv := strings.Split(p, "=")
		if len(kv) != 2 {
			return nil, cmdr.UsageErrorf("filter %q not valid; format is <name>=(true|false)")
		}
		k, v := kv[0], kv[1]
		f, err := sb.getFilter(k)
		if err != nil {
			return nil, err
		}
		tf, err := strconv.ParseBool(v)
		if err != nil {
			return nil, cmdr.UsageErrorf("filter %q accepts true or false, not %q", k, v)
		}
		filters = append(filters, func(ds sous.Deployments) sous.Deployments {
			return f(ds, tf)
		})
	}
	return filters, nil
}

func (sb *SousQueryGDM) filter(ds sous.Deployments) (sous.Deployments, error) {
	filters, err := sb.parseFilters()
	if err != nil {
		return sous.NewDeployments(), err
	}
	for _, f := range filters {
		ds = f(ds)
	}
	return ds, nil
}

// Execute defines the behavior of `sous query gdm`.
func (sb *SousQueryGDM) Execute(args []string) cmdr.Result {

	state, err := sb.StateManager.ReadState()
	if err != nil {
		return EnsureErrorResult(err)
	}
	deployments, err := state.Deployments()

	totalCount := deployments.Len()

	if err != nil {
		return EnsureErrorResult(err)
	}

	filtered, err := sb.filter(deployments)
	if err != nil {
		return cmdr.EnsureErrorResult(err)
	}

	filteredCount := filtered.Len()
	log.Printf("%d results (of %q total deployments)", filteredCount, totalCount)
	return sb.dump(filtered)

}
<|endoftext|>


<commit_before>package panos

import (
	"encoding/xml"
	"errors"
	"fmt"
	"strings"
)

// CreateL3Interface adds a new layer-3 interface to the device. You must specify the subnet mask in
// CIDR notation when specifying the IP address, i.e.: 1.1.1.1/32.
func (p *PaloAlto) CreateL3Interface(ifname, ipaddress string, comment ...string) error {
	var xmlBody string
	var reqError requestError

	if p.DeviceType == "panorama" {
		return errors.New("you cannot create interfaces on a Panorama device")
	}

	xpath := fmt.Sprintf("/config/devices/entry[@name='localhost.localdomain']/network/interface/ethernet/entry[@name='%s']", ifname)
	xmlBody = fmt.Sprintf("<layer3><ip><entry name=\"%s\"/></ip></layer3>", ipaddress)

	if len(comment) > 0 {
		xmlBody += fmt.Sprintf("<comment>%s</comment>", comment[0])
	}

	_, resp, errs := r.Post(p.URI).Query(fmt.Sprintf("type=config&action=set&xpath=%s&element=%s&key=%s", xpath, xmlBody, p.Key)).End()
	if errs != nil {
		return errs[0]
	}

	if err := xml.Unmarshal([]byte(resp), &reqError); err != nil {
		return err
	}

	if reqError.Status != "success" {
		return fmt.Errorf("error code %s: %s", reqError.Code, errorCodes[reqError.Code])
	}

	return nil
}

// CreateZone will add a new zone to the device. zonetype must be one of: tap, vwire, layer2, layer3.
func (p *PaloAlto) CreateZone(name, zonetype string) error {
	var xmlBody string
	var reqError requestError

	if p.DeviceType == "panorama" {
		return errors.New("you cannot create zones on a Panorama device")
	}

	xpath := fmt.Sprintf("/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys1']/zone/entry[@name='%s']", name)
	switch zonetype {
	case "tap":
		xmlBody = "<network><tap/></network>"
	case "vwire":
		xmlBody = "<network><virtual-wire/></network>"
	case "layer2":
		xmlBody = "<network><layer2/></network>"
	case "layer3":
		xmlBody = "<network><layer3/></network>"
	}

	_, resp, errs := r.Post(p.URI).Query(fmt.Sprintf("type=config&action=set&xpath=%s&element=%s&key=%s", xpath, xmlBody, p.Key)).End()
	if errs != nil {
		return errs[0]
	}

	if err := xml.Unmarshal([]byte(resp), &reqError); err != nil {
		return err
	}

	if reqError.Status != "success" {
		return fmt.Errorf("error code %s: %s", reqError.Code, errorCodes[reqError.Code])
	}

	return nil
}

// AddInterfaceToZone adds an interface or interfaces to the given zone. zonetype must be one of: tap, vwire, layer2, layer3.
// Separate multiple interfaces using a comma, i.e.: "ethernet1/2, ethernet1/3"
func (p *PaloAlto) AddInterfaceToZone(name, zonetype, ifname string) error {
	var xmlBody string
	var reqError requestError
	ints := strings.Split(ifname, ",")

	if p.DeviceType == "panorama" {
		return errors.New("you cannot add interfaces to zones on a Panorama device")
	}

	xpath := fmt.Sprintf("/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys1']/zone/entry[@name='%s']", name)
	switch zonetype {
	case "tap":
		xmlBody = "<network><tap>"
		for _, i := range ints {
			xmlBody += fmt.Sprintf("<member>%s</member>", strings.TrimSpace(i))
		}
		xmlBody += "</tap></network>"
	case "vwire":
		xmlBody = "<network><virtual-wire>"
		for _, i := range ints {
			xmlBody += fmt.Sprintf("<member>%s</member>", strings.TrimSpace(i))
		}
		xmlBody += "</virtual-wire></network>"
	case "layer2":
		xmlBody = "<network><layer2>"
		for _, i := range ints {
			xmlBody += fmt.Sprintf("<member>%s</member>", strings.TrimSpace(i))
		}
		xmlBody += "</layer2></network>"
	case "layer3":
		xmlBody = "<network><layer3>"
		for _, i := range ints {
			xmlBody += fmt.Sprintf("<member>%s</member>", strings.TrimSpace(i))
		}
		xmlBody += "</layer3></network>"
	}
	_, resp, errs := r.Post(p.URI).Query(fmt.Sprintf("type=config&action=set&xpath=%s&element=%s&key=%s", xpath, xmlBody, p.Key)).End()
	if errs != nil {
		return errs[0]
	}

	if err := xml.Unmarshal([]byte(resp), &reqError); err != nil {
		return err
	}

	if reqError.Status != "success" {
		return fmt.Errorf("error code %s: %s", reqError.Code, errorCodes[reqError.Code])
	}

	return nil
}

// CreateVirtualRouter will add a new virtual-router to the device.
func (p *PaloAlto) CreateVirtualRouter(name string) error {
	var xmlBody string
	var reqError requestError

	if p.DeviceType == "panorama" {
		return errors.New("you cannot create virtual-routers on a Panorama device")
	}

	xpath := fmt.Sprintf("/config/devices/entry[@name='localhost.localdomain']/network/virtual-router/entry[@name='%s']", name)
	xmlBody = "<protocol><bgp><routing-options><graceful-restart><enable>yes</enable></graceful-restart><as-format>2-byte</as-format></routing-options><enable>no</enable></bgp></protocol>"

	_, resp, errs := r.Post(p.URI).Query(fmt.Sprintf("type=config&action=set&xpath=%s&element=%s&key=%s", xpath, xmlBody, p.Key)).End()
	if errs != nil {
		return errs[0]
	}

	if err := xml.Unmarshal([]byte(resp), &reqError); err != nil {
		return err
	}

	if reqError.Status != "success" {
		return fmt.Errorf("error code %s: %s", reqError.Code, errorCodes[reqError.Code])
	}

	return nil
}

// AddInterfaceToVirtualRouter will add an interface or interfaces to the given virtual-router. Separate multiple
// interfaces using a comma, i.e.: "ethernet1/2, ethernet1/3"
func (p *PaloAlto) AddInterfaceToVirtualRouter(name, ifname string) error {
	var xmlBody string
	var reqError requestError
	ints := strings.Split(ifname, ",")

	if p.DeviceType == "panorama" {
		return errors.New("you cannot add interfaces to virtual-routers on a Panorama device")
	}

	xpath := fmt.Sprintf("/config/devices/entry[@name='localhost.localdomain']/network/virtual-router/entry[@name='%s']", name)
	xmlBody = "<interface>"
	for _, i := range ints {
		xmlBody += fmt.Sprintf("<member>%s</member>", strings.TrimSpace(i))
	}
	xmlBody += "</interface>"

	_, resp, errs := r.Post(p.URI).Query(fmt.Sprintf("type=config&action=set&xpath=%s&element=%s&key=%s", xpath, xmlBody, p.Key)).End()
	if errs != nil {
		return errs[0]
	}

	if err := xml.Unmarshal([]byte(resp), &reqError); err != nil {
		return err
	}

	if reqError.Status != "success" {
		return fmt.Errorf("error code %s: %s", reqError.Code, errorCodes[reqError.Code])
	}

	return nil
}
<commit_msg>Added functions for delete/remove<commit_after>package panos

import (
	"encoding/xml"
	"errors"
	"fmt"
	"strings"
)

// CreateL3Interface adds a new layer-3 interface to the device. You must specify the subnet mask in
// CIDR notation when specifying the IP address, i.e.: 1.1.1.1/32.
func (p *PaloAlto) CreateL3Interface(ifname, ipaddress string, comment ...string) error {
	var xmlBody string
	var reqError requestError

	if p.DeviceType == "panorama" {
		return errors.New("you cannot create interfaces on a Panorama device")
	}

	xpath := fmt.Sprintf("/config/devices/entry[@name='localhost.localdomain']/network/interface/ethernet/entry[@name='%s']", ifname)
	xmlBody = fmt.Sprintf("<layer3><ip><entry name=\"%s\"/></ip></layer3>", ipaddress)

	if len(comment) > 0 {
		xmlBody += fmt.Sprintf("<comment>%s</comment>", comment[0])
	}

	_, resp, errs := r.Post(p.URI).Query(fmt.Sprintf("type=config&action=set&xpath=%s&element=%s&key=%s", xpath, xmlBody, p.Key)).End()
	if errs != nil {
		return errs[0]
	}

	if err := xml.Unmarshal([]byte(resp), &reqError); err != nil {
		return err
	}

	if reqError.Status != "success" {
		return fmt.Errorf("error code %s: %s", reqError.Code, errorCodes[reqError.Code])
	}

	return nil
}

// DeleteL3Interface removes a layer-3 interface from the device.
func (p *PaloAlto) DeleteL3Interface(ifname string) error {
	var reqError requestError

	if p.DeviceType == "panorama" {
		return errors.New("you cannot delete interfaces on a Panorama device")
	}

	xpath := fmt.Sprintf("/config/devices/entry[@name='localhost.localdomain']/network/interface/ethernet/entry[@name='%s']", ifname)

	_, resp, errs := r.Get(p.URI).Query(fmt.Sprintf("type=config&action=delete&xpath=%s&key=%s", xpath, p.Key)).End()
	if errs != nil {
		return errs[0]
	}

	if err := xml.Unmarshal([]byte(resp), &reqError); err != nil {
		return err
	}

	if reqError.Status != "success" {
		return fmt.Errorf("error code %s: %s", reqError.Code, errorCodes[reqError.Code])
	}

	return nil
}

// CreateZone will add a new zone to the device. zonetype must be one of: tap, vwire, layer2, layer3.
func (p *PaloAlto) CreateZone(name, zonetype string) error {
	var xmlBody string
	var reqError requestError

	if p.DeviceType == "panorama" {
		return errors.New("you cannot create zones on a Panorama device")
	}

	xpath := fmt.Sprintf("/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys1']/zone/entry[@name='%s']", name)
	switch zonetype {
	case "tap":
		xmlBody = "<network><tap/></network>"
	case "vwire":
		xmlBody = "<network><virtual-wire/></network>"
	case "layer2":
		xmlBody = "<network><layer2/></network>"
	case "layer3":
		xmlBody = "<network><layer3/></network>"
	}

	_, resp, errs := r.Post(p.URI).Query(fmt.Sprintf("type=config&action=set&xpath=%s&element=%s&key=%s", xpath, xmlBody, p.Key)).End()
	if errs != nil {
		return errs[0]
	}

	if err := xml.Unmarshal([]byte(resp), &reqError); err != nil {
		return err
	}

	if reqError.Status != "success" {
		return fmt.Errorf("error code %s: %s", reqError.Code, errorCodes[reqError.Code])
	}

	return nil
}

// DeleteZone will remove a zone from the device.
func (p *PaloAlto) DeleteZone(name string) error {
	var reqError requestError

	if p.DeviceType == "panorama" {
		return errors.New("you cannot delete zones on a Panorama device")
	}

	xpath := fmt.Sprintf("/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys1']/zone/entry[@name='%s']", name)

	_, resp, errs := r.Get(p.URI).Query(fmt.Sprintf("type=config&action=delete&xpath=%s&key=%s", xpath, p.Key)).End()
	if errs != nil {
		return errs[0]
	}

	if err := xml.Unmarshal([]byte(resp), &reqError); err != nil {
		return err
	}

	if reqError.Status != "success" {
		return fmt.Errorf("error code %s: %s", reqError.Code, errorCodes[reqError.Code])
	}

	return nil
}

// AddInterfaceToZone adds an interface or interfaces to the given zone. zonetype must be one of: tap, vwire, layer2, layer3.
// Separate multiple interfaces using a comma, i.e.: "ethernet1/2, ethernet1/3"
func (p *PaloAlto) AddInterfaceToZone(name, zonetype, ifname string) error {
	var xmlBody string
	var reqError requestError
	ints := strings.Split(ifname, ",")

	if p.DeviceType == "panorama" {
		return errors.New("you cannot add interfaces to zones on a Panorama device")
	}

	xpath := fmt.Sprintf("/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys1']/zone/entry[@name='%s']", name)

	switch zonetype {
	case "tap":
		xmlBody = "<network><tap>"
		for _, i := range ints {
			xmlBody += fmt.Sprintf("<member>%s</member>", strings.TrimSpace(i))
		}
		xmlBody += "</tap></network>"
	case "vwire":
		xmlBody = "<network><virtual-wire>"
		for _, i := range ints {
			xmlBody += fmt.Sprintf("<member>%s</member>", strings.TrimSpace(i))
		}
		xmlBody += "</virtual-wire></network>"
	case "layer2":
		xmlBody = "<network><layer2>"
		for _, i := range ints {
			xmlBody += fmt.Sprintf("<member>%s</member>", strings.TrimSpace(i))
		}
		xmlBody += "</layer2></network>"
	case "layer3":
		xmlBody = "<network><layer3>"
		for _, i := range ints {
			xmlBody += fmt.Sprintf("<member>%s</member>", strings.TrimSpace(i))
		}
		xmlBody += "</layer3></network>"
	}

	_, resp, errs := r.Post(p.URI).Query(fmt.Sprintf("type=config&action=set&xpath=%s&element=%s&key=%s", xpath, xmlBody, p.Key)).End()
	if errs != nil {
		return errs[0]
	}

	if err := xml.Unmarshal([]byte(resp), &reqError); err != nil {
		return err
	}

	if reqError.Status != "success" {
		return fmt.Errorf("error code %s: %s", reqError.Code, errorCodes[reqError.Code])
	}

	return nil
}

// RemoveInterfaceFromZone removes an interface from the specified zone.
func (p *PaloAlto) RemoveInterfaceFromZone(name, zonetype, ifname string) error {
	var reqError requestError

	if p.DeviceType == "panorama" {
		return errors.New("you cannot remove interfaces from zones on a Panorama device")
	}

	xpath := fmt.Sprintf("/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys1']/zone/entry[@name='%s']", name)

	switch zonetype {
	case "tap":
		xpath += fmt.Sprintf("/network/tap/member[text()='%s']", ifname)
	case "vwire":
		xpath += fmt.Sprintf("/network/virtual-wire/member[text()='%s']", ifname)
	case "layer2":
		xpath += fmt.Sprintf("/network/layer2/member[text()='%s']", ifname)
	case "layer3":
		xpath += fmt.Sprintf("/network/layer3/member[text()='%s']", ifname)
	}

	_, resp, errs := r.Get(p.URI).Query(fmt.Sprintf("type=config&action=delete&xpath=%s&key=%s", xpath, p.Key)).End()
	if errs != nil {
		return errs[0]
	}

	if err := xml.Unmarshal([]byte(resp), &reqError); err != nil {
		return err
	}

	if reqError.Status != "success" {
		return fmt.Errorf("error code %s: %s", reqError.Code, errorCodes[reqError.Code])
	}

	return nil
}

// CreateVirtualRouter will add a new virtual-router to the device.
func (p *PaloAlto) CreateVirtualRouter(name string) error {
	var xmlBody string
	var reqError requestError

	if p.DeviceType == "panorama" {
		return errors.New("you cannot create virtual-routers on a Panorama device")
	}

	xpath := fmt.Sprintf("/config/devices/entry[@name='localhost.localdomain']/network/virtual-router/entry[@name='%s']", name)
	xmlBody = "<protocol><bgp><routing-options><graceful-restart><enable>yes</enable></graceful-restart><as-format>2-byte</as-format></routing-options><enable>no</enable></bgp></protocol>"

	_, resp, errs := r.Post(p.URI).Query(fmt.Sprintf("type=config&action=set&xpath=%s&element=%s&key=%s", xpath, xmlBody, p.Key)).End()
	if errs != nil {
		return errs[0]
	}

	if err := xml.Unmarshal([]byte(resp), &reqError); err != nil {
		return err
	}

	if reqError.Status != "success" {
		return fmt.Errorf("error code %s: %s", reqError.Code, errorCodes[reqError.Code])
	}

	return nil
}

// DeleteVirtualRouter removes a virtual-router from the device.
func (p *PaloAlto) DeleteVirtualRouter(vr string) error {
	var reqError requestError

	if p.DeviceType == "panorama" {
		return errors.New("you cannot delete a virtual-router on a Panorama device")
	}

	xpath := fmt.Sprintf("/config/devices/entry[@name='localhost.localdomain']/network/virtual-router/entry[@name='%s']", vr)

	_, resp, errs := r.Get(p.URI).Query(fmt.Sprintf("type=config&action=delete&xpath=%s&key=%s", xpath, p.Key)).End()
	if errs != nil {
		return errs[0]
	}

	if err := xml.Unmarshal([]byte(resp), &reqError); err != nil {
		return err
	}

	if reqError.Status != "success" {
		return fmt.Errorf("error code %s: %s", reqError.Code, errorCodes[reqError.Code])
	}

	return nil
}

// AddInterfaceToVirtualRouter will add an interface or interfaces to the given virtual-router. Separate multiple
// interfaces using a comma, i.e.: "ethernet1/2, ethernet1/3"
func (p *PaloAlto) AddInterfaceToVirtualRouter(name, ifname string) error {
	var xmlBody string
	var reqError requestError
	ints := strings.Split(ifname, ",")

	if p.DeviceType == "panorama" {
		return errors.New("you cannot add interfaces to virtual-routers on a Panorama device")
	}

	xpath := fmt.Sprintf("/config/devices/entry[@name='localhost.localdomain']/network/virtual-router/entry[@name='%s']", name)
	xmlBody = "<interface>"
	for _, i := range ints {
		xmlBody += fmt.Sprintf("<member>%s</member>", strings.TrimSpace(i))
	}
	xmlBody += "</interface>"

	_, resp, errs := r.Post(p.URI).Query(fmt.Sprintf("type=config&action=set&xpath=%s&element=%s&key=%s", xpath, xmlBody, p.Key)).End()
	if errs != nil {
		return errs[0]
	}

	if err := xml.Unmarshal([]byte(resp), &reqError); err != nil {
		return err
	}

	if reqError.Status != "success" {
		return fmt.Errorf("error code %s: %s", reqError.Code, errorCodes[reqError.Code])
	}

	return nil
}

// RemoveInterfaceFromVirtualRouter removes a given interface from the specified virtual-router.
func (p *PaloAlto) RemoveInterfaceFromVirtualRouter(vr, ifname string) error {
	var reqError requestError

	if p.DeviceType == "panorama" {
		return errors.New("you cannot remove interfaces from a virtual-router on a Panorama device")
	}

	xpath := fmt.Sprintf("/config/devices/entry[@name='localhost.localdomain']/network/virtual-router/entry[@name='%s']/interface/member[text()='%s']", vr, ifname)

	_, resp, errs := r.Get(p.URI).Query(fmt.Sprintf("type=config&action=delete&xpath=%s&key=%s", xpath, p.Key)).End()
	if errs != nil {
		return errs[0]
	}

	if err := xml.Unmarshal([]byte(resp), &reqError); err != nil {
		return err
	}

	if reqError.Status != "success" {
		return fmt.Errorf("error code %s: %s", reqError.Code, errorCodes[reqError.Code])
	}

	return nil
}

// CreateStaticRoute adds a new static route to a given virtual-router. For the destination, you must
// include the mask, i.e. "192.168.0.0/24" or "0.0.0.0/0." You can optionally specify a metric
// for the route, and if you do not, the metric will be 10.
func (p *PaloAlto) CreateStaticRoute(vr, name, destination, nexthop string, metric ...int) error {
	var xmlBody string
	var reqError requestError

	if p.DeviceType == "panorama" {
		return errors.New("you cannot create static routes on a Panorama device")
	}

	xpath := fmt.Sprintf("/config/devices/entry[@name='localhost.localdomain']/network/virtual-router/entry[@name='%s']", vr)
	xmlBody = fmt.Sprintf("<routing-table><ip><static-route><entry name=\"%s\">", name)

	if strings.Contains(nexthop, "ethernet") {
		xmlBody += fmt.Sprintf("<interface>%s</interface><destination>%s</destination>", nexthop, destination)
	} else {
		xmlBody += fmt.Sprintf("<nexthop><ip-address>%s</ip-address></nexthop><destination>%s</destination>", nexthop, destination)
	}

	if len(metric) > 0 {
		xmlBody += fmt.Sprintf("<metric>%d</metric>", metric[0])
	} else {
		xmlBody += "<metric>10</metric>"
	}

	xmlBody += "</entry></static-route></ip></routing-table>"

	_, resp, errs := r.Post(p.URI).Query(fmt.Sprintf("type=config&action=set&xpath=%s&element=%s&key=%s", xpath, xmlBody, p.Key)).End()
	if errs != nil {
		return errs[0]
	}

	if err := xml.Unmarshal([]byte(resp), &reqError); err != nil {
		return err
	}

	if reqError.Status != "success" {
		return fmt.Errorf("error code %s: %s", reqError.Code, errorCodes[reqError.Code])
	}

	return nil
}

// DeleteStaticRoute will remove a static route from the device.
func (p *PaloAlto) DeleteStaticRoute(vr, name string) error {
	var reqError requestError

	if p.DeviceType == "panorama" {
		return errors.New("you cannot delete static routes on a Panorama device")
	}

	xpath := fmt.Sprintf("/config/devices/entry[@name='localhost.localdomain']/network/virtual-router/entry[@name='%s']/routing-table/ip/static-route/entry[@name='%s']", vr, name)

	_, resp, errs := r.Get(p.URI).Query(fmt.Sprintf("type=config&action=delete&xpath=%s&key=%s", xpath, p.Key)).End()
	if errs != nil {
		return errs[0]
	}

	if err := xml.Unmarshal([]byte(resp), &reqError); err != nil {
		return err
	}

	if reqError.Status != "success" {
		return fmt.Errorf("error code %s: %s", reqError.Code, errorCodes[reqError.Code])
	}

	return nil
}
<|endoftext|>


<commit_before>package client

import (
	"encoding/json"
	"github.com/kr/pretty"
	"github.com/spf13/pflag"
	"github.com/zrepl/yaml-config"
	"github.com/zrepl/zrepl/cli"
	"os"
)

var configcheckArgs struct {
	format string
}

var ConfigcheckCmd = &cli.Subcommand{
	Use: "configcheck",
	Short: "check if config can be parsed without errors",
	SetupFlags: func(f *pflag.FlagSet) {
		f.StringVar(&configcheckArgs.format, "format", "", "dump parsed config object [pretty|yaml|json]")
	},
	Run: func(subcommand *cli.Subcommand, args []string) error {
		switch configcheckArgs.format {
		case "pretty":
			_, err := pretty.Println(subcommand.Config())
			return err
		case "json":
			return json.NewEncoder(os.Stdout).Encode(subcommand.Config())
		case "yaml":
			return yaml.NewEncoder(os.Stdout).Encode(subcommand.Config())
		default: // no output
		}
		return nil
	},
}

<commit_msg>client/configcheck: build jobs for checking config and allow selecting what to print<commit_after>package client

import (
	"encoding/json"
	"fmt"
	"github.com/kr/pretty"
	"github.com/pkg/errors"
	"github.com/spf13/pflag"
	"github.com/zrepl/yaml-config"
	"github.com/zrepl/zrepl/cli"
	"github.com/zrepl/zrepl/config"
	"github.com/zrepl/zrepl/daemon/job"
	"os"
)

var configcheckArgs struct {
	format string
	what string
}

var ConfigcheckCmd = &cli.Subcommand{
	Use: "configcheck",
	Short: "check if config can be parsed without errors",
	SetupFlags: func(f *pflag.FlagSet) {
		f.StringVar(&configcheckArgs.format, "format", "", "dump parsed config object [pretty|yaml|json]")
		f.StringVar(&configcheckArgs.what, "what", "all", "what to print [all|config|jobs]")
	},
	Run: func(subcommand *cli.Subcommand, args []string) error {
		formatMap := map[string]func(interface{}) {
			"": func(i interface{}) {},
			"pretty": func(i interface{}) { pretty.Println(i) },
			"json": func(i interface{}) {
				json.NewEncoder(os.Stdout).Encode(subcommand.Config())
			},
			"yaml": func(i interface{}) {
				yaml.NewEncoder(os.Stdout).Encode(subcommand.Config())
			},
		}

		formatter, ok := formatMap[configcheckArgs.format]
		if !ok {
			return fmt.Errorf("unsupported --format %q", configcheckArgs.format)
		}

		var hadErr bool
		// further: try to build jobs
		confJobs, err := job.JobsFromConfig(subcommand.Config())
		if err != nil {
			err := errors.Wrap(err, "cannot build jobs from config")
			if configcheckArgs.what == "jobs" {
				return err
			} else {
				fmt.Fprintf(os.Stderr, "%s\n", err)
				confJobs = nil
				hadErr = true
			}
		}

		whatMap := map[string]func() {
			"all": func() {
				o := struct {
					config *config.Config
					jobs []job.Job
				}{
					subcommand.Config(),
					confJobs,
				}
				formatter(o)
			},
			"config": func() {
				formatter(subcommand.Config())
			},
			"jobs": func() {
				formatter(confJobs)
			},
		}

		wf, ok := whatMap[configcheckArgs.what]
		if !ok {
			return fmt.Errorf("unsupported --format %q", configcheckArgs.what)
		}
		wf()

		if hadErr {
			return fmt.Errorf("config parsing failed")
		} else {
			return nil
		}
	},
}

<|endoftext|>


<commit_before>/*
	Faye Server

*/
package fayeserver

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/serverhorror/uuid"
	"sync"
)

const CHANNEL_HANDSHAKE = "/meta/handshake"
const CHANNEL_CONNECT = "/meta/connect"
const CHANNEL_DISCONNECT = "/meta/disconnect"
const CHANNEL_SUBSCRIBE = "/meta/subscribe"
const CHANNEL_UNSUBSCRIBE = "/meta/unsubscribe"

type FayeServer struct {
	Connections   []Connection
	Subscriptions map[string][]Client
	SubMutex      sync.RWMutex
	Clients       map[string]Client
	ClientMutex   sync.RWMutex
	idCount       int
}

/*
Instantiate a new faye server
*/
func NewFayeServer() *FayeServer {
	return &FayeServer{Connections: []Connection{},
		Subscriptions: make(map[string][]Client),
		Clients:       make(map[string]Client)}
}

// general message handling
/*

*/
func (f *FayeServer) publishToChannel(channel, data string) {
	subs, ok := f.Subscriptions[channel]
	fmt.Println("Subs: ", f.Subscriptions, "count: ", len(f.Subscriptions[channel]))
	if ok {
		f.multiplexWrite(subs, data)
	}
}

/*

*/
func (f *FayeServer) multiplexWrite(subs []Client, data string) {
	var group sync.WaitGroup
	for i := range subs {
		fmt.Println("subs[i]: ", subs[i])
		group.Add(1)
		go func(client chan<- []byte, data string) {
			if client != nil {
				fmt.Println("WRITE FOR CLIENT")
				client <- []byte(data)
			} else {
				fmt.Println("NO CHANNEL DON'T TRY TO WRITE")
			}
			group.Done()
		}(subs[i].WriteChannel, data)
	}
	group.Wait()
}

func (f *FayeServer) findClientForChannel(c chan []byte) *Client {
	f.ClientMutex.Lock()
	defer f.ClientMutex.Unlock()

	for _, client := range f.Clients {
		if client.WriteChannel == c {
			fmt.Println("Matched Client: ", client.ClientId)
			return &client
		}
	}
	return nil
}

func (f *FayeServer) DisconnectChannel(c chan []byte) {
	client := f.findClientForChannel(c)
	if client != nil {
		fmt.Println("Disconnect Client: ", client.ClientId)
		f.removeClientFromServer(client.ClientId)
	}
}

// ========

type FayeMessage struct {
	Channel                  string      `json:"channel"`
	ClientId                 string      `json:"clientId,omitempty"`
	Subscription             string      `json:"subscription,omitempty"`
	Data                     interface{} `json:"data,omitempty"`
	Id                       string      `json:"id,omitempty"`
	SupportedConnectionTypes []string    `json:"supportedConnectionTypes,omitempty"`
}

// Message handling

func (f *FayeServer) HandleMessage(message []byte, c chan []byte) ([]byte, error) {
	// parse message JSON
	fm := FayeMessage{}
	err := json.Unmarshal(message, &fm)

	if err != nil {
		fmt.Println("Error parsing message json, try array parse:", err)

		ar := []FayeMessage{}
		jerr := json.Unmarshal(message, &ar)
		if jerr != nil {
			fmt.Println("Error parsing message json as array:", err)
		} else {
			fm = ar[0]
			fmt.Println("Parsed as: ", fm)
		}
	}

	switch fm.Channel {
	case CHANNEL_HANDSHAKE:
		fmt.Println("handshake")
		return f.handshake()
	case CHANNEL_CONNECT:
		fmt.Println("connect")
		return f.connect(fm.ClientId)
	case CHANNEL_DISCONNECT:
		fmt.Println("disconnect")
		return f.disconnect(fm.ClientId)
	case CHANNEL_SUBSCRIBE:
		fmt.Println("subscribe")
		return f.subscribe(fm.ClientId, fm.Subscription, c)
	case CHANNEL_UNSUBSCRIBE:
		fmt.Println("subscribe")
		return f.unsubscribe(fm.ClientId, fm.Subscription)
	default:
		fmt.Println("publish")
		fmt.Println("data is: ", fm.Data)
		return f.publish(fm.Channel, fm.Id, fm.Data)
	}
}

/*
FayeResponse
*/

type FayeResponse struct {
	Channel                  string                 `json:"channel,omitempty"`
	Successful               bool                   `json:"successful,omitempty"`
	Version                  string                 `json:"version,omitempty"`
	SupportedConnectionTypes []string               `json:"supportedConnectionTypes,omitempty"`
	ConnectionType           string                 `json:"connectionType,omitempty"`
	ClientId                 string                 `json:"clientId,omitempty"`
	Advice                   map[string]interface{} `json:"advice,omitempty"`
	Subscription             string                 `json:"subscription,omitempty"`
	Error                    string                 `json:"error,omitempty"`
	Id                       string                 `json:"id,omitempty"`
	Data                     interface{}            `json:"data,omitempty"`
	Ext                      interface{}            `json:"ext,omitempty"`
}

/*

Handshake:

Example response:
{
    "channel": "/meta/handshake",
    "successful": true,
    "version": "1.0",
    "supportedConnectionTypes": [
        "long-polling",
        "cross-origin-long-polling",
        "callback-polling",
        "websocket",
        "eventsource",
        "in-process"
    ],
    "clientId": "1fg1b9s10zm29e0ahpk490mzkqk3",
    "advice": {
        "reconnect": "retry",
        "interval": 0,
        "timeout": 45000
    }
}

Bayeux Handshake response

*/

func (f *FayeServer) handshake() ([]byte, error) {
	fmt.Println("handshake!")

	// build response
	resp := FayeResponse{
		Id:                       "1",
		Channel:                  "/meta/handshake",
		Successful:               true,
		Version:                  "1.0",
		SupportedConnectionTypes: []string{"websocket", "callback-polling", "long-polling", "cross-origin-long-polling", "eventsource", "in-process"},
		ClientId:                 generateClientId(),
		Advice:                   map[string]interface{}{"reconnect": "retry", "interval": 0, "timeout": 45000},
	}

	// wrap it in an array & convert to json
	return json.Marshal([]FayeResponse{resp})
}

/*

Connect:

Example response
[
  {
     "channel": "/meta/connect",
     "successful": true,
     "error": "",
     "clientId": "Un1q31d3nt1f13r",
     "timestamp": "12:00:00 1970",
     "advice": { "reconnect": "retry" }
   }
]
*/

func (f *FayeServer) connect(clientId string) ([]byte, error) {
	// TODO: setup client connection state

	resp := FayeResponse{
		Channel:    "/meta/connect",
		Successful: true,
		Error:      "",
		ClientId:   clientId,
		Advice:     map[string]interface{}{"reconnect": "retry"},
	}

	// wrap it in an array & convert to json
	return json.Marshal([]FayeResponse{resp})
}

/*
Disconnect

Example response
[
  {
     "channel": "/meta/disconnect",
     "clientId": "Un1q31d3nt1f13r"
     "successful": true
  }
]
*/

func (f *FayeServer) disconnect(clientId string) ([]byte, error) {
	// tear down client connection state
	f.removeClientFromServer(clientId)

	resp := FayeResponse{
		Channel:    "/meta/disconnect",
		Successful: true,
		ClientId:   clientId,
	}

	// wrap it in an array & convert to json
	return json.Marshal([]FayeResponse{resp})
}

/*
Subscribe

Example response
[
  {
     "channel": "/meta/subscribe",
     "clientId": "Un1q31d3nt1f13r",
     "subscription": "/foo/**",
     "successful": true,
     "error": ""
   }
]
*/

func (f *FayeServer) subscribe(clientId, subscription string, c chan []byte) ([]byte, error) {

	// subscribe the client to the given channel
	if len(subscription) == 0 {
		return []byte{}, errors.New("Subscription channel not present")
	}

	f.addClientToSubscription(clientId, subscription, c)

	// if successful send success response
	resp := FayeResponse{
		Channel:      "/meta/subscribe",
		ClientId:     clientId,
		Subscription: subscription,
		Successful:   true,
		Error:        "",
	}

	// TODO: handle failure case

	// wrap it in an array and convert to json
	return json.Marshal([]FayeResponse{resp})
}

/*
Unsubscribe

Example response
[
  {
     "channel": "/meta/unsubscribe",
     "clientId": "Un1q31d3nt1f13r",
     "subscription": "/foo/**",
     "successful": true,
     "error": ""
   }
]
*/

func (f *FayeServer) unsubscribe(clientId, subscription string) ([]byte, error) {
	// TODO: unsubscribe the client from the given channel
	if len(subscription) == 0 {
		return []byte{}, errors.New("Subscription channel not present")
	}

	// remove the client as a subscriber on the channel
	if f.removeClientFromSubscription(clientId, subscription) {
		fmt.Println("Successful unsubscribe")
	} else {
		fmt.Println("Failed to unsubscribe")
	}

	// if successful send success response
	resp := FayeResponse{
		Channel:      "/meta/unsubscribe",
		ClientId:     clientId,
		Subscription: subscription,
		Successful:   true,
		Error:        "",
	}

	// TODO: handle failure case

	// wrap it in an array and convert to json
	return json.Marshal([]FayeResponse{resp})
}

/*
Publish

Example response
[
  {
     "channel": "/some/channel",
     "successful": true,
     "id": "some unique message id"
  }
]

*/
func (f *FayeServer) publish(channel, id string, data interface{}) ([]byte, error) {

	//convert data back to json string
	message := FayeResponse{
		Channel: channel,
		Id:      id,
		Data:    data,
	}

	dataStr, err := json.Marshal([]FayeResponse{message})
	if err != nil {
		fmt.Println("Error parsing message!")
		return []byte{}, errors.New("Invalid Message Data")
	}
	fmt.Println("publish to: ", channel)
	fmt.Println("data: ", string(dataStr))

	f.publishToChannel(channel, string(dataStr))

	resp := FayeResponse{
		Channel:    channel,
		Successful: true,
		Id:         id,
	}

	return json.Marshal([]FayeResponse{resp})
}

// Helper functions:

/*
	Generate a clientId for use in the communication with the client
*/
func generateClientId() string {
	return uuid.UUID4()
}

func (f *FayeServer) nextMessageId() string {
	f.idCount++
	return string(f.idCount)
}
<commit_msg>Updating uuid lib<commit_after>/*
	Faye Server

*/
package fayeserver

import (
	"encoding/json"
	"errors"
	"fmt"
	"sync"

	"code.google.com/p/go-uuid/uuid"
)

const CHANNEL_HANDSHAKE = "/meta/handshake"
const CHANNEL_CONNECT = "/meta/connect"
const CHANNEL_DISCONNECT = "/meta/disconnect"
const CHANNEL_SUBSCRIBE = "/meta/subscribe"
const CHANNEL_UNSUBSCRIBE = "/meta/unsubscribe"

type FayeServer struct {
	Connections   []Connection
	Subscriptions map[string][]Client
	SubMutex      sync.RWMutex
	Clients       map[string]Client
	ClientMutex   sync.RWMutex
	idCount       int
}

/*
Instantiate a new faye server
*/
func NewFayeServer() *FayeServer {
	return &FayeServer{Connections: []Connection{},
		Subscriptions: make(map[string][]Client),
		Clients:       make(map[string]Client)}
}

// general message handling
/*

*/
func (f *FayeServer) publishToChannel(channel, data string) {
	subs, ok := f.Subscriptions[channel]
	fmt.Println("Subs: ", f.Subscriptions, "count: ", len(f.Subscriptions[channel]))
	if ok {
		f.multiplexWrite(subs, data)
	}
}

/*

*/
func (f *FayeServer) multiplexWrite(subs []Client, data string) {
	var group sync.WaitGroup
	for i := range subs {
		fmt.Println("subs[i]: ", subs[i])
		group.Add(1)
		go func(client chan<- []byte, data string) {
			if client != nil {
				fmt.Println("WRITE FOR CLIENT")
				client <- []byte(data)
			} else {
				fmt.Println("NO CHANNEL DON'T TRY TO WRITE")
			}
			group.Done()
		}(subs[i].WriteChannel, data)
	}
	group.Wait()
}

func (f *FayeServer) findClientForChannel(c chan []byte) *Client {
	f.ClientMutex.Lock()
	defer f.ClientMutex.Unlock()

	for _, client := range f.Clients {
		if client.WriteChannel == c {
			fmt.Println("Matched Client: ", client.ClientId)
			return &client
		}
	}
	return nil
}

func (f *FayeServer) DisconnectChannel(c chan []byte) {
	client := f.findClientForChannel(c)
	if client != nil {
		fmt.Println("Disconnect Client: ", client.ClientId)
		f.removeClientFromServer(client.ClientId)
	}
}

// ========

type FayeMessage struct {
	Channel                  string      `json:"channel"`
	ClientId                 string      `json:"clientId,omitempty"`
	Subscription             string      `json:"subscription,omitempty"`
	Data                     interface{} `json:"data,omitempty"`
	Id                       string      `json:"id,omitempty"`
	SupportedConnectionTypes []string    `json:"supportedConnectionTypes,omitempty"`
}

// Message handling

func (f *FayeServer) HandleMessage(message []byte, c chan []byte) ([]byte, error) {
	// parse message JSON
	fm := FayeMessage{}
	err := json.Unmarshal(message, &fm)

	if err != nil {
		fmt.Println("Error parsing message json, try array parse:", err)

		ar := []FayeMessage{}
		jerr := json.Unmarshal(message, &ar)
		if jerr != nil {
			fmt.Println("Error parsing message json as array:", err)
		} else {
			fm = ar[0]
			fmt.Println("Parsed as: ", fm)
		}
	}

	switch fm.Channel {
	case CHANNEL_HANDSHAKE:
		fmt.Println("handshake")
		return f.handshake()
	case CHANNEL_CONNECT:
		fmt.Println("connect")
		return f.connect(fm.ClientId)
	case CHANNEL_DISCONNECT:
		fmt.Println("disconnect")
		return f.disconnect(fm.ClientId)
	case CHANNEL_SUBSCRIBE:
		fmt.Println("subscribe")
		return f.subscribe(fm.ClientId, fm.Subscription, c)
	case CHANNEL_UNSUBSCRIBE:
		fmt.Println("subscribe")
		return f.unsubscribe(fm.ClientId, fm.Subscription)
	default:
		fmt.Println("publish")
		fmt.Println("data is: ", fm.Data)
		return f.publish(fm.Channel, fm.Id, fm.Data)
	}
}

/*
FayeResponse
*/

type FayeResponse struct {
	Channel                  string                 `json:"channel,omitempty"`
	Successful               bool                   `json:"successful,omitempty"`
	Version                  string                 `json:"version,omitempty"`
	SupportedConnectionTypes []string               `json:"supportedConnectionTypes,omitempty"`
	ConnectionType           string                 `json:"connectionType,omitempty"`
	ClientId                 string                 `json:"clientId,omitempty"`
	Advice                   map[string]interface{} `json:"advice,omitempty"`
	Subscription             string                 `json:"subscription,omitempty"`
	Error                    string                 `json:"error,omitempty"`
	Id                       string                 `json:"id,omitempty"`
	Data                     interface{}            `json:"data,omitempty"`
	Ext                      interface{}            `json:"ext,omitempty"`
}

/*

Handshake:

Example response:
{
    "channel": "/meta/handshake",
    "successful": true,
    "version": "1.0",
    "supportedConnectionTypes": [
        "long-polling",
        "cross-origin-long-polling",
        "callback-polling",
        "websocket",
        "eventsource",
        "in-process"
    ],
    "clientId": "1fg1b9s10zm29e0ahpk490mzkqk3",
    "advice": {
        "reconnect": "retry",
        "interval": 0,
        "timeout": 45000
    }
}

Bayeux Handshake response

*/

func (f *FayeServer) handshake() ([]byte, error) {
	fmt.Println("handshake!")

	// build response
	resp := FayeResponse{
		Id:                       "1",
		Channel:                  "/meta/handshake",
		Successful:               true,
		Version:                  "1.0",
		SupportedConnectionTypes: []string{"websocket", "callback-polling", "long-polling", "cross-origin-long-polling", "eventsource", "in-process"},
		ClientId:                 generateClientId(),
		Advice:                   map[string]interface{}{"reconnect": "retry", "interval": 0, "timeout": 45000},
	}

	// wrap it in an array & convert to json
	return json.Marshal([]FayeResponse{resp})
}

/*

Connect:

Example response
[
  {
     "channel": "/meta/connect",
     "successful": true,
     "error": "",
     "clientId": "Un1q31d3nt1f13r",
     "timestamp": "12:00:00 1970",
     "advice": { "reconnect": "retry" }
   }
]
*/

func (f *FayeServer) connect(clientId string) ([]byte, error) {
	// TODO: setup client connection state

	resp := FayeResponse{
		Channel:    "/meta/connect",
		Successful: true,
		Error:      "",
		ClientId:   clientId,
		Advice:     map[string]interface{}{"reconnect": "retry"},
	}

	// wrap it in an array & convert to json
	return json.Marshal([]FayeResponse{resp})
}

/*
Disconnect

Example response
[
  {
     "channel": "/meta/disconnect",
     "clientId": "Un1q31d3nt1f13r"
     "successful": true
  }
]
*/

func (f *FayeServer) disconnect(clientId string) ([]byte, error) {
	// tear down client connection state
	f.removeClientFromServer(clientId)

	resp := FayeResponse{
		Channel:    "/meta/disconnect",
		Successful: true,
		ClientId:   clientId,
	}

	// wrap it in an array & convert to json
	return json.Marshal([]FayeResponse{resp})
}

/*
Subscribe

Example response
[
  {
     "channel": "/meta/subscribe",
     "clientId": "Un1q31d3nt1f13r",
     "subscription": "/foo/**",
     "successful": true,
     "error": ""
   }
]
*/

func (f *FayeServer) subscribe(clientId, subscription string, c chan []byte) ([]byte, error) {

	// subscribe the client to the given channel
	if len(subscription) == 0 {
		return []byte{}, errors.New("Subscription channel not present")
	}

	f.addClientToSubscription(clientId, subscription, c)

	// if successful send success response
	resp := FayeResponse{
		Channel:      "/meta/subscribe",
		ClientId:     clientId,
		Subscription: subscription,
		Successful:   true,
		Error:        "",
	}

	// TODO: handle failure case

	// wrap it in an array and convert to json
	return json.Marshal([]FayeResponse{resp})
}

/*
Unsubscribe

Example response
[
  {
     "channel": "/meta/unsubscribe",
     "clientId": "Un1q31d3nt1f13r",
     "subscription": "/foo/**",
     "successful": true,
     "error": ""
   }
]
*/

func (f *FayeServer) unsubscribe(clientId, subscription string) ([]byte, error) {
	// TODO: unsubscribe the client from the given channel
	if len(subscription) == 0 {
		return []byte{}, errors.New("Subscription channel not present")
	}

	// remove the client as a subscriber on the channel
	if f.removeClientFromSubscription(clientId, subscription) {
		fmt.Println("Successful unsubscribe")
	} else {
		fmt.Println("Failed to unsubscribe")
	}

	// if successful send success response
	resp := FayeResponse{
		Channel:      "/meta/unsubscribe",
		ClientId:     clientId,
		Subscription: subscription,
		Successful:   true,
		Error:        "",
	}

	// TODO: handle failure case

	// wrap it in an array and convert to json
	return json.Marshal([]FayeResponse{resp})
}

/*
Publish

Example response
[
  {
     "channel": "/some/channel",
     "successful": true,
     "id": "some unique message id"
  }
]

*/
func (f *FayeServer) publish(channel, id string, data interface{}) ([]byte, error) {

	//convert data back to json string
	message := FayeResponse{
		Channel: channel,
		Id:      id,
		Data:    data,
	}

	dataStr, err := json.Marshal([]FayeResponse{message})
	if err != nil {
		fmt.Println("Error parsing message!")
		return []byte{}, errors.New("Invalid Message Data")
	}
	fmt.Println("publish to: ", channel)
	fmt.Println("data: ", string(dataStr))

	f.publishToChannel(channel, string(dataStr))

	resp := FayeResponse{
		Channel:    channel,
		Successful: true,
		Id:         id,
	}

	return json.Marshal([]FayeResponse{resp})
}

// Helper functions:

/*
	Generate a clientId for use in the communication with the client
*/
func generateClientId() string {
	return uuid.New()
}

func (f *FayeServer) nextMessageId() string {
	f.idCount++
	return string(f.idCount)
}
<|endoftext|>