<commit_before>package iradix

import (
	"bytes"

	"github.com/hashicorp/golang-lru/simplelru"
)

const (
	// defaultModifiedCache is the default size of the modified node
	// cache used per transaction. This is used to cache the updates
	// to the nodes near the root, while the leaves do not need to be
	// cached. This is important for very large transactions to prevent
	// the modified cache from growing to be enormous.
	defaultModifiedCache = 8192
)

// Tree implements an immutable radix tree. This can be treated as a
// Dictionary abstract data type. The main advantage over a standard
// hash map is prefix-based lookups and ordered iteration. The immutability
// means that it is safe to concurrently read from a Tree without any
// coordination.
type Tree struct {
	root *Node
	size int
}

// New returns an empty Tree
func New() *Tree {
	t := &Tree{
		root: &Node{
			mutateCh: make(chan struct{}),
		},
	}
	return t
}

// Len is used to return the number of elements in the tree
func (t *Tree) Len() int {
	return t.size
}

// Txn is a transaction on the tree. This transaction is applied
// atomically and returns a new tree when committed. A transaction
// is not thread safe, and should only be used by a single goroutine.
type Txn struct {
	root     *Node
	size     int
	modified *simplelru.LRU

	mutatedNode  map[*Node]struct{}
	mutatedLeaf  map[*leafNode]struct{}
	notifyMutate bool
}

// Txn starts a new transaction that can be used to mutate the tree
func (t *Tree) Txn() *Txn {
	txn := &Txn{
		root: t.root,
		size: t.size,
	}
	return txn
}

// TrackMutate can be used to toggle if mutation cause a notification
// to the affected nodes. This must be enabled before any modifications are
// made within the transaction.
func (t *Txn) TrackMutate(notify bool) error {
	if len(t.modified) > 0 && notify {
		return fmt.Errorf("transaction already in progress")
	}
	t.notifyMutate = notify
	return nil
}

// writeNode returns a node to be modified, if the current
// node as already been modified during the course of
// the transaction, it is used in-place.
func (t *Txn) writeNode(n *Node) *Node {
	// Ensure the modified set exists
	if t.modified == nil {
		lru, err := simplelru.NewLRU(defaultModifiedCache, nil)
		if err != nil {
			panic(err)
		}
		t.modified = lru
	}

	// If this node has already been modified, we can
	// continue to use it during this transaction.
	if _, ok := t.modified.Get(n); ok {
		return n
	}

	// Mark this node as being mutated
	if t.notifyMutate {
		if t.mutatedNode == nil {
			t.mutatedNode = make(map[*Node]struct{})
		}
		t.mutatedNode[n] = struct{}{}
	}

	// Copy the existing node
	nc := new(Node)
	nc.mutateCh = make(chan struct{})
	if n.prefix != nil {
		nc.prefix = make([]byte, len(n.prefix))
		copy(nc.prefix, n.prefix)
	}
	if n.leaf != nil {
		nc.leaf = n.leaf
	}
	if len(n.edges) != 0 {
		nc.edges = make([]edge, len(n.edges))
		copy(nc.edges, n.edges)
	}

	// Mark this node as modified
	t.modified.Add(nc, nil)
	return nc
}

// mutateLeaf is used to mark a leaf as being mutated
// for the purposes of notification.
func (t *Txn) mutateLeaf(leaf *leafNode) {
	if !t.notifyMutate {
		return
	}
	if t.mutatedLeaf == nil {
		t.mutatedLeaf = make(map[*leafNode]struct{})
	}
	t.mutatedLeaf[leaf] = struct{}{}
}

// insert does a recursive insertion
func (t *Txn) insert(n *Node, k, search []byte, v interface{}) (*Node, interface{}, bool) {
	// Handle key exhaution
	if len(search) == 0 {
		nc := t.writeNode(n)
		nc.leaf = &leafNode{
			mutateCh: make(chan struct{}),
			key:      k,
			val:      v,
		}
		if n.isLeaf() {
			t.mutateLeaf(n.leaf)
			return nc, n.leaf.val, true
		} else {
			return nc, nil, false
		}
	}

	// Look for the edge
	idx, child := n.getEdge(search[0])

	// No edge, create one
	if child == nil {
		e := edge{
			label: search[0],
			node: &Node{
				mutateCh: make(chan struct{}),
				leaf: &leafNode{
					mutateCh: make(chan struct{}),
					key:      k,
					val:      v,
				},
				prefix: search,
			},
		}
		nc := t.writeNode(n)
		nc.addEdge(e)
		return nc, nil, false
	}

	// Determine longest prefix of the search key on match
	commonPrefix := longestPrefix(search, child.prefix)
	if commonPrefix == len(child.prefix) {
		search = search[commonPrefix:]
		newChild, oldVal, didUpdate := t.insert(child, k, search, v)
		if newChild != nil {
			nc := t.writeNode(n)
			nc.edges[idx].node = newChild
			return nc, oldVal, didUpdate
		}
		return nil, oldVal, didUpdate
	}

	// Split the node
	nc := t.writeNode(n)
	splitNode := &Node{
		mutateCh: make(chan struct{}),
		prefix:   search[:commonPrefix],
	}
	nc.replaceEdge(edge{
		label: search[0],
		node:  splitNode,
	})

	// Restore the existing child node
	modChild := t.writeNode(child)
	splitNode.addEdge(edge{
		label: modChild.prefix[commonPrefix],
		node:  modChild,
	})
	modChild.prefix = modChild.prefix[commonPrefix:]

	// Create a new leaf node
	leaf := &leafNode{
		mutateCh: make(chan struct{}),
		key:      k,
		val:      v,
	}

	// If the new key is a subset, add to to this node
	search = search[commonPrefix:]
	if len(search) == 0 {
		splitNode.leaf = leaf
		return nc, nil, false
	}

	// Create a new edge for the node
	splitNode.addEdge(edge{
		label: search[0],
		node: &Node{
			mutateCh: make(chan struct{}),
			leaf:     leaf,
			prefix:   search,
		},
	})
	return nc, nil, false
}

// delete does a recursive deletion
func (t *Txn) delete(parent, n *Node, search []byte) (*Node, *leafNode) {
	// Check for key exhaution
	if len(search) == 0 {
		if !n.isLeaf() {
			return nil, nil
		}

		// Remove the leaf node
		nc := t.writeNode(n)
		nc.leaf = nil
		t.mutateLeaf(n.leaf)

		// Check if this node should be merged
		if n != t.root && len(nc.edges) == 1 {
			nc.mergeChild()
		}
		return nc, n.leaf
	}

	// Look for an edge
	label := search[0]
	idx, child := n.getEdge(label)
	if child == nil || !bytes.HasPrefix(search, child.prefix) {
		return nil, nil
	}

	// Consume the search prefix
	search = search[len(child.prefix):]
	newChild, leaf := t.delete(n, child, search)
	if newChild == nil {
		return nil, nil
	}

	// Copy this node
	nc := t.writeNode(n)

	// Delete the edge if the node has no edges
	if newChild.leaf == nil && len(newChild.edges) == 0 {
		nc.delEdge(label)
		if n != t.root && len(nc.edges) == 1 && !nc.isLeaf() {
			nc.mergeChild()
		}
	} else {
		nc.edges[idx].node = newChild
	}
	return nc, leaf
}

// Insert is used to add or update a given key. The return provides
// the previous value and a bool indicating if any was set.
func (t *Txn) Insert(k []byte, v interface{}) (interface{}, bool) {
	newRoot, oldVal, didUpdate := t.insert(t.root, k, k, v)
	if newRoot != nil {
		t.root = newRoot
	}
	if !didUpdate {
		t.size++
	}
	return oldVal, didUpdate
}

// Delete is used to delete a given key. Returns the old value if any,
// and a bool indicating if the key was set.
func (t *Txn) Delete(k []byte) (interface{}, bool) {
	newRoot, leaf := t.delete(nil, t.root, k)
	if newRoot != nil {
		t.root = newRoot
	}
	if leaf != nil {
		t.size--
		return leaf.val, true
	}
	return nil, false
}

// Root returns the current root of the radix tree within this
// transaction. The root is not safe across insert and delete operations,
// but can be used to read the current state during a transaction.
func (t *Txn) Root() *Node {
	return t.root
}

// Get is used to lookup a specific key, returning
// the value and if it was found
func (t *Txn) Get(k []byte) (interface{}, bool) {
	return t.root.Get(k)
}

// GetWatch is used to lookup a specific key, returning
// the watch channel, value and if it was found
func (t *Txn) GetWatch(k []byte) (<-chan struct{}, interface{}, bool) {
	return t.root.GetWatch(k)
}

// Commit is used to finalize the transaction and return a new tree
func (t *Txn) Commit() *Tree {
	nt := &Tree{t.root, t.size}
	t.modified = nil
	return nt
}

// Notify is used along with TrackMutate to trigger notifications.
// It should only be invoked after the transaction has been committed.
func (t *Txn) Notify() {
	if t.notifyMutate {
		for leaf := range t.mutatedLeaf {
			close(leaf.mutateCh)
		}
		for node := range t.mutatedNode {
			close(node.mutateCh)
		}
	}
	t.mutatedNode = nil
	t.mutatedLeaf = nil
}

// Insert is used to add or update a given key. The return provides
// the new tree, previous value and a bool indicating if any was set.
func (t *Tree) Insert(k []byte, v interface{}) (*Tree, interface{}, bool) {
	txn := t.Txn()
	old, ok := txn.Insert(k, v)
	return txn.Commit(), old, ok
}

// Delete is used to delete a given key. Returns the new tree,
// old value if any, and a bool indicating if the key was set.
func (t *Tree) Delete(k []byte) (*Tree, interface{}, bool) {
	txn := t.Txn()
	old, ok := txn.Delete(k)
	return txn.Commit(), old, ok
}

// Root returns the root node of the tree which can be used for richer
// query operations.
func (t *Tree) Root() *Node {
	return t.root
}

// Get is used to lookup a specific key, returning
// the value and if it was found
func (t *Tree) Get(k []byte) (interface{}, bool) {
	return t.root.Get(k)
}

// longestPrefix finds the length of the shared prefix
// of two strings
func longestPrefix(k1, k2 []byte) int {
	max := len(k1)
	if l := len(k2); l < max {
		max = l
	}
	var i int
	for i = 0; i < max; i++ {
		if k1[i] != k2[i] {
			break
		}
	}
	return i
}

// concat two byte slices, returning a third new copy
func concat(a, b []byte) []byte {
	c := make([]byte, len(a)+len(b))
	copy(c, a)
	copy(c[len(a):], b)
	return c
}
<commit_msg>Simplify mutation tracking<commit_after>package iradix

import (
	"bytes"

	"github.com/hashicorp/golang-lru/simplelru"
)

const (
	// defaultModifiedCache is the default size of the modified node
	// cache used per transaction. This is used to cache the updates
	// to the nodes near the root, while the leaves do not need to be
	// cached. This is important for very large transactions to prevent
	// the modified cache from growing to be enormous.
	defaultModifiedCache = 8192
)

// Tree implements an immutable radix tree. This can be treated as a
// Dictionary abstract data type. The main advantage over a standard
// hash map is prefix-based lookups and ordered iteration. The immutability
// means that it is safe to concurrently read from a Tree without any
// coordination.
type Tree struct {
	root *Node
	size int
}

// New returns an empty Tree
func New() *Tree {
	t := &Tree{
		root: &Node{
			mutateCh: make(chan struct{}),
		},
	}
	return t
}

// Len is used to return the number of elements in the tree
func (t *Tree) Len() int {
	return t.size
}

// Txn is a transaction on the tree. This transaction is applied
// atomically and returns a new tree when committed. A transaction
// is not thread safe, and should only be used by a single goroutine.
type Txn struct {
	root     *Node
	size     int
	modified *simplelru.LRU

	mutatedNode map[*Node]struct{}
	mutatedLeaf map[*leafNode]struct{}
	trackMutate bool
}

// Txn starts a new transaction that can be used to mutate the tree
func (t *Tree) Txn() *Txn {
	txn := &Txn{
		root: t.root,
		size: t.size,
	}
	return txn
}

// TrackMutate can be used to toggle if mutations are tracked.
// This is used with Notify to cause a notification to the affected
// internal nodes and leaves. Mutations made before enabling tracking
// will not be notified.
func (t *Txn) TrackMutate(track bool) {
	t.trackMutate = track
}

// writeNode returns a node to be modified, if the current
// node as already been modified during the course of
// the transaction, it is used in-place.
func (t *Txn) writeNode(n *Node) *Node {
	// Ensure the modified set exists
	if t.modified == nil {
		lru, err := simplelru.NewLRU(defaultModifiedCache, nil)
		if err != nil {
			panic(err)
		}
		t.modified = lru
	}

	// If this node has already been modified, we can
	// continue to use it during this transaction.
	if _, ok := t.modified.Get(n); ok {
		return n
	}

	// Mark this node as being mutated
	if t.trackMutate {
		if t.mutatedNode == nil {
			t.mutatedNode = make(map[*Node]struct{})
		}
		t.mutatedNode[n] = struct{}{}
	}

	// Copy the existing node
	nc := new(Node)
	nc.mutateCh = make(chan struct{})
	if n.prefix != nil {
		nc.prefix = make([]byte, len(n.prefix))
		copy(nc.prefix, n.prefix)
	}
	if n.leaf != nil {
		nc.leaf = n.leaf
	}
	if len(n.edges) != 0 {
		nc.edges = make([]edge, len(n.edges))
		copy(nc.edges, n.edges)
	}

	// Mark this node as modified
	t.modified.Add(nc, nil)
	return nc
}

// mutateLeaf is used to mark a leaf as being mutated
// for the purposes of notification.
func (t *Txn) mutateLeaf(leaf *leafNode) {
	if !t.trackMutate {
		return
	}
	if t.mutatedLeaf == nil {
		t.mutatedLeaf = make(map[*leafNode]struct{})
	}
	t.mutatedLeaf[leaf] = struct{}{}
}

// insert does a recursive insertion
func (t *Txn) insert(n *Node, k, search []byte, v interface{}) (*Node, interface{}, bool) {
	// Handle key exhaution
	if len(search) == 0 {
		nc := t.writeNode(n)
		nc.leaf = &leafNode{
			mutateCh: make(chan struct{}),
			key:      k,
			val:      v,
		}
		if n.isLeaf() {
			t.mutateLeaf(n.leaf)
			return nc, n.leaf.val, true
		} else {
			return nc, nil, false
		}
	}

	// Look for the edge
	idx, child := n.getEdge(search[0])

	// No edge, create one
	if child == nil {
		e := edge{
			label: search[0],
			node: &Node{
				mutateCh: make(chan struct{}),
				leaf: &leafNode{
					mutateCh: make(chan struct{}),
					key:      k,
					val:      v,
				},
				prefix: search,
			},
		}
		nc := t.writeNode(n)
		nc.addEdge(e)
		return nc, nil, false
	}

	// Determine longest prefix of the search key on match
	commonPrefix := longestPrefix(search, child.prefix)
	if commonPrefix == len(child.prefix) {
		search = search[commonPrefix:]
		newChild, oldVal, didUpdate := t.insert(child, k, search, v)
		if newChild != nil {
			nc := t.writeNode(n)
			nc.edges[idx].node = newChild
			return nc, oldVal, didUpdate
		}
		return nil, oldVal, didUpdate
	}

	// Split the node
	nc := t.writeNode(n)
	splitNode := &Node{
		mutateCh: make(chan struct{}),
		prefix:   search[:commonPrefix],
	}
	nc.replaceEdge(edge{
		label: search[0],
		node:  splitNode,
	})

	// Restore the existing child node
	modChild := t.writeNode(child)
	splitNode.addEdge(edge{
		label: modChild.prefix[commonPrefix],
		node:  modChild,
	})
	modChild.prefix = modChild.prefix[commonPrefix:]

	// Create a new leaf node
	leaf := &leafNode{
		mutateCh: make(chan struct{}),
		key:      k,
		val:      v,
	}

	// If the new key is a subset, add to to this node
	search = search[commonPrefix:]
	if len(search) == 0 {
		splitNode.leaf = leaf
		return nc, nil, false
	}

	// Create a new edge for the node
	splitNode.addEdge(edge{
		label: search[0],
		node: &Node{
			mutateCh: make(chan struct{}),
			leaf:     leaf,
			prefix:   search,
		},
	})
	return nc, nil, false
}

// delete does a recursive deletion
func (t *Txn) delete(parent, n *Node, search []byte) (*Node, *leafNode) {
	// Check for key exhaution
	if len(search) == 0 {
		if !n.isLeaf() {
			return nil, nil
		}

		// Remove the leaf node
		nc := t.writeNode(n)
		nc.leaf = nil
		t.mutateLeaf(n.leaf)

		// Check if this node should be merged
		if n != t.root && len(nc.edges) == 1 {
			nc.mergeChild()
		}
		return nc, n.leaf
	}

	// Look for an edge
	label := search[0]
	idx, child := n.getEdge(label)
	if child == nil || !bytes.HasPrefix(search, child.prefix) {
		return nil, nil
	}

	// Consume the search prefix
	search = search[len(child.prefix):]
	newChild, leaf := t.delete(n, child, search)
	if newChild == nil {
		return nil, nil
	}

	// Copy this node
	nc := t.writeNode(n)

	// Delete the edge if the node has no edges
	if newChild.leaf == nil && len(newChild.edges) == 0 {
		nc.delEdge(label)
		if n != t.root && len(nc.edges) == 1 && !nc.isLeaf() {
			nc.mergeChild()
		}
	} else {
		nc.edges[idx].node = newChild
	}
	return nc, leaf
}

// Insert is used to add or update a given key. The return provides
// the previous value and a bool indicating if any was set.
func (t *Txn) Insert(k []byte, v interface{}) (interface{}, bool) {
	newRoot, oldVal, didUpdate := t.insert(t.root, k, k, v)
	if newRoot != nil {
		t.root = newRoot
	}
	if !didUpdate {
		t.size++
	}
	return oldVal, didUpdate
}

// Delete is used to delete a given key. Returns the old value if any,
// and a bool indicating if the key was set.
func (t *Txn) Delete(k []byte) (interface{}, bool) {
	newRoot, leaf := t.delete(nil, t.root, k)
	if newRoot != nil {
		t.root = newRoot
	}
	if leaf != nil {
		t.size--
		return leaf.val, true
	}
	return nil, false
}

// Root returns the current root of the radix tree within this
// transaction. The root is not safe across insert and delete operations,
// but can be used to read the current state during a transaction.
func (t *Txn) Root() *Node {
	return t.root
}

// Get is used to lookup a specific key, returning
// the value and if it was found
func (t *Txn) Get(k []byte) (interface{}, bool) {
	return t.root.Get(k)
}

// GetWatch is used to lookup a specific key, returning
// the watch channel, value and if it was found
func (t *Txn) GetWatch(k []byte) (<-chan struct{}, interface{}, bool) {
	return t.root.GetWatch(k)
}

// Commit is used to finalize the transaction and return a new tree
func (t *Txn) Commit() *Tree {
	nt := &Tree{t.root, t.size}
	t.modified = nil
	return nt
}

// Notify is used along with TrackMutate to trigger notifications.
// It should only be invoked after the transaction has been committed.
func (t *Txn) Notify() {
	for leaf := range t.mutatedLeaf {
		close(leaf.mutateCh)
	}
	for node := range t.mutatedNode {
		close(node.mutateCh)
	}
	t.mutatedNode = nil
	t.mutatedLeaf = nil
}

// Insert is used to add or update a given key. The return provides
// the new tree, previous value and a bool indicating if any was set.
func (t *Tree) Insert(k []byte, v interface{}) (*Tree, interface{}, bool) {
	txn := t.Txn()
	old, ok := txn.Insert(k, v)
	return txn.Commit(), old, ok
}

// Delete is used to delete a given key. Returns the new tree,
// old value if any, and a bool indicating if the key was set.
func (t *Tree) Delete(k []byte) (*Tree, interface{}, bool) {
	txn := t.Txn()
	old, ok := txn.Delete(k)
	return txn.Commit(), old, ok
}

// Root returns the root node of the tree which can be used for richer
// query operations.
func (t *Tree) Root() *Node {
	return t.root
}

// Get is used to lookup a specific key, returning
// the value and if it was found
func (t *Tree) Get(k []byte) (interface{}, bool) {
	return t.root.Get(k)
}

// longestPrefix finds the length of the shared prefix
// of two strings
func longestPrefix(k1, k2 []byte) int {
	max := len(k1)
	if l := len(k2); l < max {
		max = l
	}
	var i int
	for i = 0; i < max; i++ {
		if k1[i] != k2[i] {
			break
		}
	}
	return i
}

// concat two byte slices, returning a third new copy
func concat(a, b []byte) []byte {
	c := make([]byte, len(a)+len(b))
	copy(c, a)
	copy(c[len(a):], b)
	return c
}
<|endoftext|>


<commit_before>package toolbox_test

import (
	"fmt"
	"github.com/stretchr/testify/assert"
	"github.com/viant/toolbox"
	"log"
	"net/http"
	"testing"
	"time"
)

type ReverseService struct{}

func (this ReverseService) Reverse(values []int) []int {
	var result = make([]int, 0)
	for i := len(values) - 1; i >= 0; i-- {
		result = append(result, values[i])
	}

	return result
}

func (this ReverseService) Reverse2(values []int) []int {
	var result = make([]int, 0)
	for i := len(values) - 1; i >= 0; i-- {
		result = append(result, values[i])
	}
	return result
}

var ReverseInvoker = func(serviceRouting *toolbox.ServiceRouting, request *http.Request, response http.ResponseWriter, uriParameters map[string]interface{}) error {
	var function = serviceRouting.Handler.(func(values []int) []int)
	idsParam := uriParameters["ids"]
	ids := idsParam.([]string)
	values := make([]int, 0)
	for _, item := range ids {
		values = append(values, toolbox.AsInt(item))
	}
	var result = function(values)
	err := toolbox.WriteServiceRoutingResponse(response, request, serviceRouting, result)
	if err != nil {
		return err
	}
	return nil
}

func StartServer(port string, t *testing.T) {
	service := ReverseService{}
	router := toolbox.NewServiceRouter(
		toolbox.ServiceRouting{
			HTTPMethod: "GET",
			URI:        "/v1/reverse/{ids}",
			Handler:    service.Reverse,
			Parameters: []string{"ids"},
		},
		toolbox.ServiceRouting{
			HTTPMethod: "POST",
			URI:        "/v1/reverse/",
			Handler:    service.Reverse,
			Parameters: []string{"ids"},
		},
		toolbox.ServiceRouting{
			HTTPMethod: "DELETE",
			URI:        "/v1/delete/{ids}",
			Handler:    service.Reverse,
			Parameters: []string{"ids"},
		},
		toolbox.ServiceRouting{
			HTTPMethod:     "GET",
			URI:            "/v1/reverse2/{ids}",
			Handler:        service.Reverse,
			Parameters:     []string{"ids"},
			HandlerInvoker: ReverseInvoker,
		},
		toolbox.ServiceRouting{
			HTTPMethod: "GET",
			URI:        "/v1/tasks",
			Parameters: []string{"status"},
			Handler: func(status string) map[string]interface{} {
				var result = map[string]interface{}{
					"STATUS": status,
					"ABc":    101,
				}
				return result
			},
		},
		toolbox.ServiceRouting{
			HTTPMethod: "GET",
			URI:        "/v1/tasks/{ids}",
			Parameters: []string{"ids"},
			Handler: func(ids ...string) map[string]interface{} {
				var result = map[string]interface{}{
					"STATUS": ids,
					"ABc":    102,
				}
				return result
			},
		},
	)

	http.HandleFunc("/v1/", func(writer http.ResponseWriter, reader *http.Request) {
		err := router.Route(writer, reader)
		assert.Nil(t, err)
	})

	fmt.Printf("Started test server on port %v\n", port)
	log.Fatal(http.ListenAndServe(":"+port, nil))
}

func TestServiceRouter(t *testing.T) {
	go func() {
		StartServer("8082", t)
	}()

	time.Sleep(2 * time.Second)

	{

		var result = map[string]interface{}{}
		err := toolbox.RouteToService("get", "http://127.0.0.1:8082/v1/tasks/1,2,3", nil, &result)
		if err != nil {
			t.Errorf("failed to send get request  %v", err)
		}

		assert.EqualValues(t, []interface{}{"1", "2", "3"}, result["STATUS"])

	}

	//
	{

		var result = map[string]interface{}{}
		err := toolbox.RouteToService("get", "http://127.0.0.1:8082/v1/tasks?status=OK", nil, &result)
		if err != nil {
			t.Errorf("failed to send get request  %v", err)
		}

		assert.EqualValues(t, "OK", result["STATUS"])

	}

	var result = make([]int, 0)

	{

		err := toolbox.RouteToService("get", "http://127.0.0.1:8082/v1/reverse/1,7,3", nil, &result)
		if err != nil {
			t.Errorf("failed to send get request  %v", err)
		}
		assert.EqualValues(t, []int{3, 7, 1}, result)

	}

	{

		err := toolbox.RouteToService("post", "http://127.0.0.1:8082/v1/reverse/", []int{1, 7, 3}, &result)
		if err != nil {
			t.Errorf("failed to send get request  %v", err)
		}
		assert.EqualValues(t, []int{3, 7, 1}, result)
	}
	{

		err := toolbox.RouteToService("delete", "http://127.0.0.1:8082/v1/delete/", []int{1, 7, 3}, &result)
		if err != nil {
			t.Errorf("failed to send delete request  %v", err)
		}
		assert.EqualValues(t, []int{3, 7, 1}, result)
	}
	{

		err := toolbox.RouteToService("delete", "http://127.0.0.1:8082/v1/delete/1,7,3", nil, &result)
		if err != nil {
			t.Errorf("failed to send delete request  %v", err)
		}
		assert.EqualValues(t, []int{3, 7, 1}, result)
	}

	{ //Test custom handler invocation without reflection

		err := toolbox.RouteToService("get", "http://127.0.0.1:8082/v1/reverse2/1,7,3", nil, &result)
		if err != nil {
			t.Errorf("failed to send delete request  %v", err)
		}
		assert.EqualValues(t, []int{3, 7, 1}, result)
	}

}
<commit_msg>optimized ToInt<commit_after>package toolbox_test

import (
	"encoding/json"
	"fmt"
	"github.com/stretchr/testify/assert"
	"github.com/viant/toolbox"
	"log"
	"net/http"
	"strings"
	"testing"
	"time"
)

type ReverseService struct{}

func (this ReverseService) Reverse(values []int) []int {
	var result = make([]int, 0)
	for i := len(values) - 1; i >= 0; i-- {
		result = append(result, values[i])
	}

	return result
}

func (this ReverseService) Reverse2(values []int) []int {
	var result = make([]int, 0)
	for i := len(values) - 1; i >= 0; i-- {
		result = append(result, values[i])
	}
	return result
}

var ReverseInvoker = func(serviceRouting *toolbox.ServiceRouting, request *http.Request, response http.ResponseWriter, uriParameters map[string]interface{}) error {
	var function = serviceRouting.Handler.(func(values []int) []int)
	idsParam := uriParameters["ids"]
	ids := idsParam.([]string)
	values := make([]int, 0)
	for _, item := range ids {
		values = append(values, toolbox.AsInt(item))
	}
	var result = function(values)
	err := toolbox.WriteServiceRoutingResponse(response, request, serviceRouting, result)
	if err != nil {
		return err
	}
	return nil
}

func StartServer(port string, t *testing.T) {
	service := ReverseService{}
	router := toolbox.NewServiceRouter(
		toolbox.ServiceRouting{
			HTTPMethod: "GET",
			URI:        "/v1/reverse/{ids}",
			Handler:    service.Reverse,
			Parameters: []string{"ids"},
		},
		toolbox.ServiceRouting{
			HTTPMethod: "POST",
			URI:        "/v1/reverse/",
			Handler:    service.Reverse,
			Parameters: []string{"ids"},
		},
		toolbox.ServiceRouting{
			HTTPMethod: "DELETE",
			URI:        "/v1/delete/{ids}",
			Handler:    service.Reverse,
			Parameters: []string{"ids"},
		},
		toolbox.ServiceRouting{
			HTTPMethod:     "GET",
			URI:            "/v1/reverse2/{ids}",
			Handler:        service.Reverse,
			Parameters:     []string{"ids"},
			HandlerInvoker: ReverseInvoker,
		},
		toolbox.ServiceRouting{
			HTTPMethod: "GET",
			URI:        "/v1/tasks",
			Parameters: []string{"status"},
			Handler: func(status string) map[string]interface{} {
				var result = map[string]interface{}{
					"STATUS": status,
					"ABc":    101,
				}
				return result
			},
		},
		toolbox.ServiceRouting{
			HTTPMethod: "GET",
			URI:        "/v1/tasks/{ids}",
			Parameters: []string{"ids"},
			Handler: func(ids ...string) map[string]interface{} {
				var result = map[string]interface{}{
					"STATUS": ids,
					"ABc":    102,
				}
				return result
			},
		},
		toolbox.ServiceRouting{
			HTTPMethod: "GET",
			URI:        "/v1/explain/{cluster}/{yyyy}/{mm}/{dd}/{hh}/{name}/{sid}/{aid}",
			Parameters: []string{"cluster", "sid"},
			Handler: func(amap map[string]interface{}) map[string]interface{} {
				return amap
			},
			HandlerInvoker: func(serviceRouting *toolbox.ServiceRouting, request *http.Request, response http.ResponseWriter, parameters map[string]interface{}) error {
				var result = make(map[string]interface{})
				for k, v := range parameters {
					if strings.HasPrefix(k, "@") {
						continue
					}
					result[k] = v
				}
				data, _ := json.Marshal(result)
				response.Write(data)
				return nil
			},
		},
	)

	http.HandleFunc("/v1/", func(writer http.ResponseWriter, reader *http.Request) {
		err := router.Route(writer, reader)
		assert.Nil(t, err)
	})

	fmt.Printf("Started test server on port %v\n", port)
	log.Fatal(http.ListenAndServe(":"+port, nil))
}

func TestServiceRouter(t *testing.T) {
	go func() {
		StartServer("8082", t)
	}()

	time.Sleep(2 * time.Second)

	{ //Test explain
		var result = map[string]interface{}{}
		err := toolbox.RouteToService("get", "http://127.0.0.1:8082/v1/explain/west/2020/07/26/22/22-02.0.i-08b66c734e4d10a60/59af369e-cf8b-11ea-8858-134d48b29f18/1258652", nil, &result)
		if err != nil {
			t.Errorf("failed to send explain request  %v", err)
		}
		assert.EqualValues(t, map[string]interface{}{
			"cluster": "west",
			"sid": "59af369e-cf8b-11ea-8858-134d48b29f18",
		}, result)
	}

	{

		var result = map[string]interface{}{}
		err := toolbox.RouteToService("get", "http://127.0.0.1:8082/v1/tasks/1,2,3", nil, &result)
		if err != nil {
			t.Errorf("failed to send get request  %v", err)
		}

		assert.EqualValues(t, []interface{}{"1", "2", "3"}, result["STATUS"])

	}

	//
	{

		var result = map[string]interface{}{}
		err := toolbox.RouteToService("get", "http://127.0.0.1:8082/v1/tasks?status=OK", nil, &result)
		if err != nil {
			t.Errorf("failed to send get request  %v", err)
		}

		assert.EqualValues(t, "OK", result["STATUS"])

	}

	var result = make([]int, 0)

	{

		err := toolbox.RouteToService("get", "http://127.0.0.1:8082/v1/reverse/1,7,3", nil, &result)
		if err != nil {
			t.Errorf("failed to send get request  %v", err)
		}
		assert.EqualValues(t, []int{3, 7, 1}, result)

	}

	{

		err := toolbox.RouteToService("post", "http://127.0.0.1:8082/v1/reverse/", []int{1, 7, 3}, &result)
		if err != nil {
			t.Errorf("failed to send get request  %v", err)
		}
		assert.EqualValues(t, []int{3, 7, 1}, result)
	}
	{

		err := toolbox.RouteToService("delete", "http://127.0.0.1:8082/v1/delete/", []int{1, 7, 3}, &result)
		if err != nil {
			t.Errorf("failed to send delete request  %v", err)
		}
		assert.EqualValues(t, []int{3, 7, 1}, result)
	}
	{

		err := toolbox.RouteToService("delete", "http://127.0.0.1:8082/v1/delete/1,7,3", nil, &result)
		if err != nil {
			t.Errorf("failed to send delete request  %v", err)
		}
		assert.EqualValues(t, []int{3, 7, 1}, result)
	}

	{ //Test custom handler invocation without reflection

		err := toolbox.RouteToService("get", "http://127.0.0.1:8082/v1/reverse2/1,7,3", nil, &result)
		if err != nil {
			t.Errorf("failed to send delete request  %v", err)
		}
		assert.EqualValues(t, []int{3, 7, 1}, result)
	}

	{ //Test custom handler invocation without reflection

		err := toolbox.RouteToService("get", "http://127.0.0.1:8082/v1/reverse2/1,7,3", nil, &result)
		if err != nil {
			t.Errorf("failed to send delete request  %v", err)
		}
		assert.EqualValues(t, []int{3, 7, 1}, result)
	}

}
<|endoftext|>


<commit_before>package lib

import (
    "fmt"
    "os"
    "time"

    . "gopkg.in/check.v1"
)

func (s *OssutilCommandSuite) rawList(args []string, shortFormat, directory bool) (bool, error) {
    command := "ls"
    str := ""
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &configFile,
        "shortFormat": &shortFormat,
        "directory": &directory,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    return showElapse, err
}

// test list buckets
func (s *OssutilCommandSuite) TestListLoadConfig(c *C) {
    command := "ls"
    var args []string
    str := ""
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &configFile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)

    args = []string{"oss://"}
    options = OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &configFile,
    }
    showElapse, err = cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)
}

func (s *OssutilCommandSuite) TestListNotExistConfigFile(c *C) {
    command := "ls"
    var args []string
    str := ""
    cfile := "notexistfile"
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)
}

func (s *OssutilCommandSuite) TestListErrConfigFile(c *C) {
    cfile := "ossutil_test.config_boto"
    s.createFile(cfile, content, c)

    command := "ls"
    var args []string
    str := ""
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    _ = os.Remove(cfile)
}

func (s *OssutilCommandSuite) TestListConfigFile(c *C) {
    cfile := "ossutil_test.config_boto"
    data := fmt.Sprintf("[Credentials]\nendpoint=%s\naccessKeyID=%s\naccessKeySecret=%s\nretryTimes=%s", endpoint, accessKeyID, accessKeySecret, "errretry")
    s.createFile(cfile, data, c)

    command := "ls"
    var args []string
    str := ""
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)

    _ = os.Remove(cfile)
}

func (s *OssutilCommandSuite) TestListWithBucketEndpoint(c *C) {
    bucket := bucketNameExist

    cfile := "ossutil_test.config_boto"
    data := fmt.Sprintf("[Credentials]\nendpoint=%s\naccessKeyID=%s\naccessKeySecret=%s\n[Bucket-Endpoint]\n%s=%s", "abc", accessKeyID, accessKeySecret, bucket, endpoint)
    s.createFile(cfile, data, c)

    command := "ls"
    args := []string{CloudURLToString(bucket, "")}
    str := ""
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)

    _ = os.Remove(cfile)
}

func (s *OssutilCommandSuite) TestListWithBucketCname(c *C) {
    bucket := bucketNamePrefix + "ls1"
    s.putBucket(bucket, c)

    cfile := "ossutil_test.config_boto"
    data := fmt.Sprintf("[Credentials]\nendpoint=%s\naccessKeyID=%s\naccessKeySecret=%s\n[Bucket-Endpoint]\n%s=%s\n[Bucket-Cname]\n%s=%s", "abc", accessKeyID, accessKeySecret, bucket, "abc", bucket, bucket + "." + endpoint)
    s.createFile(cfile, data, c)

    command := "ls"
    args := []string{CloudURLToString(bucket, "")}
    str := ""
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)

    _ = os.Remove(cfile)
    s.removeBucket(bucket, true, c)
    time.Sleep(sleepTime)
}

func (s *OssutilCommandSuite) TestListBuckets(c *C) {
    // "ls"
    bucket := bucketNamePrefix + "ls2"
    s.removeBucket(bucket, true, c)
    time.Sleep(3*sleepTime)

    buckets := s.listBuckets(false, c)
    c.Assert(FindPos(bucket, buckets) == -1, Equals, true)
    bucketNum := len(buckets)

    // "ls -s"
    buckets = s.listBuckets(true, c)
    c.Assert(FindPos(bucket, buckets) == -1, Equals, true)

    // put bucket
    s.putBucket(bucket, c)
    time.Sleep(2*sleepTime)

    // get result
    buckets = s.listBuckets(false, c)
    c.Assert(FindPos(bucket, buckets) != -1, Equals, true)
    c.Assert(len(buckets) >= bucketNum, Equals, true)

    // remove bucket
    s.removeBucket(bucket, true, c)
    time.Sleep(sleepTime)

    // get result
    buckets = s.listBuckets(false, c)
    c.Assert(FindPos(bucket, buckets) == -1, Equals, true)
}

// list objects with not exist bucket
func (s *OssutilCommandSuite) TestListObjectsBucketNotExist(c *C) {
    bucket := bucketNameNotExist
    command := "ls"
    args := []string{CloudURLToString(bucket, "")}
    str := ""
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &configFile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)
}

// list objects
func (s *OssutilCommandSuite) TestListObjects(c *C) {
    bucket := bucketNameList

    // "ls oss://bucket"
    objects := s.listObjects(bucket, "", false, false, c)
    c.Assert(len(objects), Equals, 0)

    // put objects
    num := 10
    objectNames := []string{}
    for i := 0; i < num; i++ {
        object := fmt.Sprintf("lstest:#%d", i)
        s.putObject(bucket, object, uploadFileName, c)
        objectNames = append(objectNames, object)
    }

    object := "another_object"
    s.putObject(bucket, object, uploadFileName, c)
    objectNames = append(objectNames, object)

    // "ls oss://bucket -s"
    objects = s.listObjects(bucket, "", true, false, c)
    c.Assert(len(objects), Equals, len(objectNames))

    // "ls oss://bucket/prefix -s"
    objects = s.listObjects(bucket, "lstest:", true, false, c)
    c.Assert(len(objects), Equals, len(objectNames) - 1)

    //put directories
    num = 5
    objectNames = []string{}
    for i := 0; i < num; i++ {
        object := fmt.Sprintf("lstest:#%d/", i)
        s.putObject(bucket, object, uploadFileName, c)

        object = fmt.Sprintf("lstest:#%d/%d/", i, i)
        s.putObject(bucket, object, uploadFileName, c)
        objectNames = append(objectNames, object)
    }

    // "ls oss://bucket/prefix"
    objects = s.listObjects(bucket, "lstest:#", false, false, c)
    c.Assert(len(objects), Equals, 20)

    // "ls oss://bucket/prefix -d"
    objects = s.listObjects(bucket, "lstest:#", false, true, c)
    c.Assert(len(objects), Equals, 15)

    objects = s.listObjects(bucket, "lstest:#1/", false, true, c)
    c.Assert(len(objects), Equals, 2)
}

func (s *OssutilCommandSuite) TestErrList(c *C) {
    showElapse, err := s.rawList([]string{"../"}, true, false)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    bucket := bucketNameNotExist
    showElapse, err = s.rawList([]string{CloudURLToString(bucket, "")}, false, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    // list buckets with -d
    showElapse, err = s.rawList([]string{"oss://"}, false, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)
}

func (s *OssutilCommandSuite) TestListIDKey(c *C) {
    bucket := bucketNamePrefix + "lsidkey"

    cfile := "ossutil_test.config_boto"
    data := fmt.Sprintf("[Credentials]\nendpoint=%s\naccessKeyID=%s\naccessKeySecret=%s\n[Bucket-Endpoint]\n%s=%s[Bucket-Cname]\n%s=%s", "abc", "def", "ghi", bucket, "abc", bucket, "abc")
    s.createFile(cfile, data, c)

    command := "ls"
    str := ""
    args := []string{"oss://"}
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, NotNil)

    options = OptionMapType{
        "endpoint": &endpoint,
        "accessKeyID": &accessKeyID,
        "accessKeySecret": &accessKeySecret,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err = cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)

    _ = os.Remove(cfile)
}

func (s *OssutilCommandSuite) TestListBucketIDKey(c *C) {
    bucket := bucketNameExist

    cfile := "ossutil_test.config_boto"
    data := fmt.Sprintf("[Credentials]\nendpoint=%s\naccessKeyID=%s\naccessKeySecret=%s\n[Bucket-Endpoint]\n%s=%s[Bucket-Cname]\n%s=%s", "abc", "def", "ghi", bucket, "abc", bucket, "abc")
    s.createFile(cfile, data, c)

    command := "ls"
    str := ""
    args := []string{CloudURLToString(bucket, "")}
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, NotNil)

    options = OptionMapType{
        "endpoint": &endpoint,
        "accessKeyID": &accessKeyID,
        "accessKeySecret": &accessKeySecret,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err = cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)

    _ = os.Remove(cfile)
}
<commit_msg>modify case<commit_after>package lib

import (
    "fmt"
    "os"
    "time"

    . "gopkg.in/check.v1"
)

func (s *OssutilCommandSuite) rawList(args []string, shortFormat, directory bool) (bool, error) {
    command := "ls"
    str := ""
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &configFile,
        "shortFormat": &shortFormat,
        "directory": &directory,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    return showElapse, err
}

// test list buckets
func (s *OssutilCommandSuite) TestListLoadConfig(c *C) {
    command := "ls"
    var args []string
    str := ""
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &configFile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)

    args = []string{"oss://"}
    options = OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &configFile,
    }
    showElapse, err = cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)
}

func (s *OssutilCommandSuite) TestListNotExistConfigFile(c *C) {
    command := "ls"
    var args []string
    str := ""
    cfile := "notexistfile"
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)
}

func (s *OssutilCommandSuite) TestListErrConfigFile(c *C) {
    cfile := "ossutil_test.config_boto"
    s.createFile(cfile, content, c)

    command := "ls"
    var args []string
    str := ""
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    _ = os.Remove(cfile)
}

func (s *OssutilCommandSuite) TestListConfigFile(c *C) {
    cfile := "ossutil_test.config_boto"
    data := fmt.Sprintf("[Credentials]\nendpoint=%s\naccessKeyID=%s\naccessKeySecret=%s\nretryTimes=%s", endpoint, accessKeyID, accessKeySecret, "errretry")
    s.createFile(cfile, data, c)

    command := "ls"
    var args []string
    str := ""
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)

    _ = os.Remove(cfile)
}

func (s *OssutilCommandSuite) TestListWithBucketEndpoint(c *C) {
    bucket := bucketNameExist

    cfile := "ossutil_test.config_boto"
    data := fmt.Sprintf("[Credentials]\nendpoint=%s\naccessKeyID=%s\naccessKeySecret=%s\n[Bucket-Endpoint]\n%s=%s", "abc", accessKeyID, accessKeySecret, bucket, endpoint)
    s.createFile(cfile, data, c)

    command := "ls"
    args := []string{CloudURLToString(bucket, "")}
    str := ""
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)

    _ = os.Remove(cfile)
}

func (s *OssutilCommandSuite) TestListWithBucketCname(c *C) {
    bucket := bucketNamePrefix + "ls1"
    s.putBucket(bucket, c)

    cfile := "ossutil_test.config_boto"
    data := fmt.Sprintf("[Credentials]\nendpoint=%s\naccessKeyID=%s\naccessKeySecret=%s\n[Bucket-Endpoint]\n%s=%s\n[Bucket-Cname]\n%s=%s", "abc", accessKeyID, accessKeySecret, bucket, "abc", bucket, bucket + "." + endpoint)
    s.createFile(cfile, data, c)

    command := "ls"
    args := []string{CloudURLToString(bucket, "")}
    str := ""
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)

    _ = os.Remove(cfile)
    s.removeBucket(bucket, true, c)
    time.Sleep(sleepTime)
}

func (s *OssutilCommandSuite) TestListBuckets(c *C) {
    // "ls"
    bucket := bucketNamePrefix + "ls2"
    s.removeBucket(bucket, true, c)
    time.Sleep(3*sleepTime)

    buckets := s.listBuckets(false, c)
    c.Assert(FindPos(bucket, buckets) == -1, Equals, true)
    bucketNum := len(buckets)

    // "ls -s"
    buckets = s.listBuckets(true, c)
    c.Assert(FindPos(bucket, buckets) == -1, Equals, true)

    // put bucket
    s.putBucket(bucket, c)
    time.Sleep(2*sleepTime)

    // get result
    buckets = s.listBuckets(false, c)
    c.Assert(FindPos(bucket, buckets) != -1, Equals, true)
    c.Assert(len(buckets) >= bucketNum, Equals, true)

    // remove bucket
    s.removeBucket(bucket, true, c)
    time.Sleep(sleepTime)

    // get result
    buckets = s.listBuckets(false, c)
    c.Assert(FindPos(bucket, buckets) == -1, Equals, true)
}

// list objects with not exist bucket
func (s *OssutilCommandSuite) TestListObjectsBucketNotExist(c *C) {
    bucket := bucketNameNotExist
    command := "ls"
    args := []string{CloudURLToString(bucket, "")}
    str := ""
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &configFile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)
}

// list objects
func (s *OssutilCommandSuite) TestListObjects(c *C) {
    bucket := bucketNameList
    s.removeObjects(bucket, "", true, true, c)
    time.Sleep(3*sleepTime)

    // "ls oss://bucket"
    objects := s.listObjects(bucket, "", false, false, c)
    c.Assert(len(objects), Equals, 0)

    // put objects
    num := 10
    objectNames := []string{}
    for i := 0; i < num; i++ {
        object := fmt.Sprintf("lstest:#%d", i)
        s.putObject(bucket, object, uploadFileName, c)
        objectNames = append(objectNames, object)
    }

    object := "another_object"
    s.putObject(bucket, object, uploadFileName, c)
    objectNames = append(objectNames, object)

    // "ls oss://bucket -s"
    objects = s.listObjects(bucket, "", true, false, c)
    c.Assert(len(objects), Equals, len(objectNames))

    // "ls oss://bucket/prefix -s"
    objects = s.listObjects(bucket, "lstest:", true, false, c)
    c.Assert(len(objects), Equals, len(objectNames) - 1)

    //put directories
    num = 5
    objectNames = []string{}
    for i := 0; i < num; i++ {
        object := fmt.Sprintf("lstest:#%d/", i)
        s.putObject(bucket, object, uploadFileName, c)

        object = fmt.Sprintf("lstest:#%d/%d/", i, i)
        s.putObject(bucket, object, uploadFileName, c)
        objectNames = append(objectNames, object)
    }

    // "ls oss://bucket/prefix"
    objects = s.listObjects(bucket, "lstest:#", false, false, c)
    c.Assert(len(objects), Equals, 20)

    // "ls oss://bucket/prefix -d"
    objects = s.listObjects(bucket, "lstest:#", false, true, c)
    c.Assert(len(objects), Equals, 15)

    objects = s.listObjects(bucket, "lstest:#1/", false, true, c)
    c.Assert(len(objects), Equals, 2)
}

func (s *OssutilCommandSuite) TestErrList(c *C) {
    showElapse, err := s.rawList([]string{"../"}, true, false)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    bucket := bucketNameNotExist
    showElapse, err = s.rawList([]string{CloudURLToString(bucket, "")}, false, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)

    // list buckets with -d
    showElapse, err = s.rawList([]string{"oss://"}, false, true)
    c.Assert(err, NotNil)
    c.Assert(showElapse, Equals, false)
}

func (s *OssutilCommandSuite) TestListIDKey(c *C) {
    bucket := bucketNamePrefix + "lsidkey"

    cfile := "ossutil_test.config_boto"
    data := fmt.Sprintf("[Credentials]\nendpoint=%s\naccessKeyID=%s\naccessKeySecret=%s\n[Bucket-Endpoint]\n%s=%s[Bucket-Cname]\n%s=%s", "abc", "def", "ghi", bucket, "abc", bucket, "abc")
    s.createFile(cfile, data, c)

    command := "ls"
    str := ""
    args := []string{"oss://"}
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, NotNil)

    options = OptionMapType{
        "endpoint": &endpoint,
        "accessKeyID": &accessKeyID,
        "accessKeySecret": &accessKeySecret,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err = cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)

    _ = os.Remove(cfile)
}

func (s *OssutilCommandSuite) TestListBucketIDKey(c *C) {
    bucket := bucketNameExist

    cfile := "ossutil_test.config_boto"
    data := fmt.Sprintf("[Credentials]\nendpoint=%s\naccessKeyID=%s\naccessKeySecret=%s\n[Bucket-Endpoint]\n%s=%s[Bucket-Cname]\n%s=%s", "abc", "def", "ghi", bucket, "abc", bucket, "abc")
    s.createFile(cfile, data, c)

    command := "ls"
    str := ""
    args := []string{CloudURLToString(bucket, "")}
    options := OptionMapType{
        "endpoint": &str,
        "accessKeyID": &str,
        "accessKeySecret": &str,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err := cm.RunCommand(command, args, options)
    c.Assert(err, NotNil)

    options = OptionMapType{
        "endpoint": &endpoint,
        "accessKeyID": &accessKeyID,
        "accessKeySecret": &accessKeySecret,
        "stsToken": &str,
        "configFile": &cfile,
    }
    showElapse, err = cm.RunCommand(command, args, options)
    c.Assert(err, IsNil)
    c.Assert(showElapse, Equals, true)

    _ = os.Remove(cfile)
}
<|endoftext|>


<commit_before>package router

import "github.com/docker/docker/api/server/httputils"

// localRoute defines an individual API route to connect
// with the docker daemon. It implements Route.
type localRoute struct {
	method  string
	path    string
	handler httputils.APIFunc
}

// Handler returns the APIFunc to let the server wrap it in middlewares.
func (l localRoute) Handler() httputils.APIFunc {
	return l.handler
}

// Method returns the http method that the route responds to.
func (l localRoute) Method() string {
	return l.method
}

// Path returns the subpath where the route responds to.
func (l localRoute) Path() string {
	return l.path
}

// NewRoute initializes a new local route for the router.
func NewRoute(method, path string, handler httputils.APIFunc) Route {
	return localRoute{method, path, handler}
}

// NewGetRoute initializes a new route with the http method GET.
func NewGetRoute(path string, handler httputils.APIFunc) Route {
	return NewRoute("GET", path, handler)
}

// NewPostRoute initializes a new route with the http method POST.
func NewPostRoute(path string, handler httputils.APIFunc) Route {
	return NewRoute("POST", path, handler)
}

// NewPutRoute initializes a new route with the http method PUT.
func NewPutRoute(path string, handler httputils.APIFunc) Route {
	return NewRoute("PUT", path, handler)
}

// NewDeleteRoute initializes a new route with the http method DELETE.
func NewDeleteRoute(path string, handler httputils.APIFunc) Route {
	return NewRoute("DELETE", path, handler)
}

// NewOptionsRoute initializes a new route with the http method OPTIONS.
func NewOptionsRoute(path string, handler httputils.APIFunc) Route {
	return NewRoute("OPTIONS", path, handler)
}

// NewHeadRoute initializes a new route with the http method HEAD.
func NewHeadRoute(path string, handler httputils.APIFunc) Route {
	return NewRoute("HEAD", path, handler)
}
<commit_msg>api/server/router: add Cancellable function<commit_after>package router

import (
	"net/http"

	"github.com/docker/docker/api/server/httputils"
	"golang.org/x/net/context"
)

// localRoute defines an individual API route to connect
// with the docker daemon. It implements Route.
type localRoute struct {
	method  string
	path    string
	handler httputils.APIFunc
}

// Handler returns the APIFunc to let the server wrap it in middlewares.
func (l localRoute) Handler() httputils.APIFunc {
	return l.handler
}

// Method returns the http method that the route responds to.
func (l localRoute) Method() string {
	return l.method
}

// Path returns the subpath where the route responds to.
func (l localRoute) Path() string {
	return l.path
}

// NewRoute initializes a new local route for the router.
func NewRoute(method, path string, handler httputils.APIFunc) Route {
	return localRoute{method, path, handler}
}

// NewGetRoute initializes a new route with the http method GET.
func NewGetRoute(path string, handler httputils.APIFunc) Route {
	return NewRoute("GET", path, handler)
}

// NewPostRoute initializes a new route with the http method POST.
func NewPostRoute(path string, handler httputils.APIFunc) Route {
	return NewRoute("POST", path, handler)
}

// NewPutRoute initializes a new route with the http method PUT.
func NewPutRoute(path string, handler httputils.APIFunc) Route {
	return NewRoute("PUT", path, handler)
}

// NewDeleteRoute initializes a new route with the http method DELETE.
func NewDeleteRoute(path string, handler httputils.APIFunc) Route {
	return NewRoute("DELETE", path, handler)
}

// NewOptionsRoute initializes a new route with the http method OPTIONS.
func NewOptionsRoute(path string, handler httputils.APIFunc) Route {
	return NewRoute("OPTIONS", path, handler)
}

// NewHeadRoute initializes a new route with the http method HEAD.
func NewHeadRoute(path string, handler httputils.APIFunc) Route {
	return NewRoute("HEAD", path, handler)
}

func cancellableHandler(h httputils.APIFunc) httputils.APIFunc {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
		if notifier, ok := w.(http.CloseNotifier); ok {
			notify := notifier.CloseNotify()
			notifyCtx, cancel := context.WithCancel(ctx)
			finished := make(chan struct{})
			defer close(finished)
			ctx = notifyCtx
			go func() {
				select {
				case <-notify:
					cancel()
				case <-finished:
				}
			}()
		}
		return h(ctx, w, r, vars)
	}
}

// Cancellable makes new route which embeds http.CloseNotifier feature to
// context.Context of handler.
func Cancellable(r Route) Route {
	return localRoute{
		method:  r.Method(),
		path:    r.Path(),
		handler: cancellableHandler(r.Handler()),
	}
}
<|endoftext|>


<commit_before>package types

type StoragePlan struct {
	ID                  string `json:"id" header:"ID"`
	Name                string `json:"name" header:"NAME"`
	MinSize             int    `json:"min_size,omitempty" header:"MIN_SIZE"`
	MaxSize             int    `json:"max_size,omitempty" header:"MAX_SIZE"`
	CloudProviderID     string `json:"cloud_provider_id,omitempty" header:"CLOUD_PROVIDER_ID"`
	CloudProviderName   string `json:"cloud_provider_name" header:"CLOUD_PROVIDER_NAME"`
	LocationID          string `json:"location_id,omitempty" header:"LOCATION_ID"`
	LocationName        string `json:"location_name" header:"LOCATION_NAME"`
	RealmID             string `json:"realm_id,omitempty" header:"REALM_ID"`
	RealmProviderName   string `json:"realm_provider_name,omitempty" header:"REALM_PROVIDER_NAME"`
	FlavourProviderName string `json:"flavour_provider_name,omitempty" header:"FLAVOUR_PROVIDER_NAME"`
	Deprecated          bool   `json:"deprecated,omitempty" header:"DEPRECATED" show:"nolist,noshow"`
	ResourceType        string `json:"resource_type" header:"RESOURCE_TYPE" show:"nolist"`
}
<commit_msg>Remove unnecessary json tag (issue #106)<commit_after>package types

type StoragePlan struct {
	ID                  string `json:"id" header:"ID"`
	Name                string `json:"name" header:"NAME"`
	MinSize             int    `json:"min_size,omitempty" header:"MIN_SIZE"`
	MaxSize             int    `json:"max_size,omitempty" header:"MAX_SIZE"`
	CloudProviderID     string `json:"cloud_provider_id,omitempty" header:"CLOUD_PROVIDER_ID"`
	CloudProviderName   string `header:"CLOUD_PROVIDER_NAME"`
	LocationID          string `json:"location_id,omitempty" header:"LOCATION_ID"`
	LocationName        string `header:"LOCATION_NAME"`
	RealmID             string `json:"realm_id,omitempty" header:"REALM_ID"`
	RealmProviderName   string `json:"realm_provider_name,omitempty" header:"REALM_PROVIDER_NAME"`
	FlavourProviderName string `json:"flavour_provider_name,omitempty" header:"FLAVOUR_PROVIDER_NAME"`
	Deprecated          bool   `json:"deprecated,omitempty" header:"DEPRECATED" show:"nolist,noshow"`
	ResourceType        string `json:"resource_type" header:"RESOURCE_TYPE" show:"nolist"`
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"
	"net/url"
	"os"

	"github.com/kurrik/twittergo"
)

func findUser() (uint64, error) {
	query := url.Values{}
	query.Set("screen_name", "freshfruitcn")

	req, err = http.NewRequest("GET", fmt.Sprintf("/1.1/users/lookup.json?%v", query.Encode()), nil)
	if err != nil {
		fmt.Printf("Could not parse sync user request: %v\n", err)
		return 0, err
	}

	resp, err = client.SendRequest(req)
	if err != nil {
		fmt.Printf("Could not send sync user request: %v\n", err)
		return 0, err
	}

	var users []twittergo.User
	err = resp.Parse(&users)
	if err != nil {
		fmt.Printf("Problem parsing response: %v\n", err)
		os.Exit(1)
	}
	if len(users) != 1 {
		fmt.Println("Can't find the user @freshfruitcn")
		os.Exit(1)
	}
	user := users[0]
	fmt.Printf("ID: %v\n", user.Id())
	fmt.Printf("Name: %v\n", user.Name())
	fmt.Printf("ScreenName: %v\n", user.ScreenName())
	return user.Id(), nil
}

func retweet(id uint64) error {
	req, err = http.NewRequest("POST", fmt.Sprintf("/1.1/statuses/retweet/%d.json", id), nil)
	if err != nil {
		fmt.Printf("Could not parse sync user request: %v\n", err)
		return err
	}

	resp, err = client.SendRequest(req)
	if err != nil {
		fmt.Printf("Could not send sync user request: %v\n", err)
		return err
	}

	b, err := ReadBody(resp)
	if err != nil {
		fmt.Printf("Retweeting failed: %v\n", err, string(b))
		return err
	}

	return nil
}

func syncUser() error {
	userID, err := findUser()
	if err != nil {
		os.Exit(1)
	}
	query := url.Values{}
	query.Set("screen_name", "freshfruitcn")
	query.Set("user_id", fmt.Sprint(userID))
	query.Set("count", "10")

	req, err = http.NewRequest("GET", fmt.Sprintf("/1.1/statuses/user_timeline.json?%v", query.Encode()), nil)
	if err != nil {
		fmt.Printf("Could not parse get user timeline request: %v\n", err)
		return err
	}

	resp, err = client.SendRequest(req)
	if err != nil {
		fmt.Printf("Could not send get user timeline request: %v\n", err)
		return err
	}

	var tweets []twittergo.Tweet
	err = resp.Parse(&tweets)
	if err != nil {
		fmt.Printf("Problem parsing response: %v\n", err)
		os.Exit(1)
	}

	for _, tweet := range tweets {
		rt, ok := tweet["retweeted"]
		if !ok {
			fmt.Println("can't find retweet field")
			continue
		}
		if rt.(bool) == true {
			fmt.Println("already retweeted")
			continue
		}
		// check user
		user, ok := tweet["user"]
		if !ok {
			fmt.Println("can't find user field")
			continue
		}
		u, ok := user.(map[string]interface{})
		if !ok {
			fmt.Println("converting user struct failed", user)
			continue
		}
		id, ok := u["id"]
		if !ok {
			fmt.Println("can't find user id field")
			continue
		}
		uid, ok := id.(float64)
		if !ok {
			fmt.Println("converting user id type failed", id)
			continue
		}
		if uint64(uid) != userID {
			fmt.Println("not posted by user", userID)
			continue
		}
		// check has media
		ee := tweet.ExtendedEntities()
		if len(ee) == 0 {
			fmt.Println("no extended entities, skip")
			continue
		}

		// do retweet
		if err = retweet(tweet.Id()); err == nil {
			fmt.Println(tweet.Id(), "is retweeted")
		}
	}

	return nil
}
<commit_msg>(*)get full text<commit_after>package main

import (
	"fmt"
	"net/http"
	"net/url"
	"os"

	"github.com/kurrik/twittergo"
)

func findUser() (uint64, error) {
	query := url.Values{}
	query.Set("screen_name", "freshfruitcn")

	req, err = http.NewRequest("GET", fmt.Sprintf("/1.1/users/lookup.json?%v", query.Encode()), nil)
	if err != nil {
		fmt.Printf("Could not parse sync user request: %v\n", err)
		return 0, err
	}

	resp, err = client.SendRequest(req)
	if err != nil {
		fmt.Printf("Could not send sync user request: %v\n", err)
		return 0, err
	}

	var users []twittergo.User
	err = resp.Parse(&users)
	if err != nil {
		fmt.Printf("Problem parsing response: %v\n", err)
		os.Exit(1)
	}
	if len(users) != 1 {
		fmt.Println("Can't find the user @freshfruitcn")
		os.Exit(1)
	}
	user := users[0]
	fmt.Printf("ID: %v\n", user.Id())
	fmt.Printf("Name: %v\n", user.Name())
	fmt.Printf("ScreenName: %v\n", user.ScreenName())
	return user.Id(), nil
}

func retweet(id uint64) error {
	req, err = http.NewRequest("POST", fmt.Sprintf("/1.1/statuses/retweet/%d.json", id), nil)
	if err != nil {
		fmt.Printf("Could not parse sync user request: %v\n", err)
		return err
	}

	resp, err = client.SendRequest(req)
	if err != nil {
		fmt.Printf("Could not send sync user request: %v\n", err)
		return err
	}

	b, err := ReadBody(resp)
	if err != nil {
		fmt.Printf("Retweeting failed: %v\n", err, string(b))
		return err
	}

	return nil
}

func syncUser() error {
	userID, err := findUser()
	if err != nil {
		os.Exit(1)
	}
	query := url.Values{}
	query.Set("screen_name", "freshfruitcn")
	query.Set("user_id", fmt.Sprint(userID))
	query.Set("count", "10")
	query.Set("tweet_mode", "extended")

	req, err = http.NewRequest("GET", fmt.Sprintf("/1.1/statuses/user_timeline.json?%v", query.Encode()), nil)
	if err != nil {
		fmt.Printf("Could not parse get user timeline request: %v\n", err)
		return err
	}

	resp, err = client.SendRequest(req)
	if err != nil {
		fmt.Printf("Could not send get user timeline request: %v\n", err)
		return err
	}

	var tweets []twittergo.Tweet
	err = resp.Parse(&tweets)
	if err != nil {
		fmt.Printf("Problem parsing response: %v\n", err)
		os.Exit(1)
	}

	for _, tweet := range tweets {
		rt, ok := tweet["retweeted"]
		if !ok {
			fmt.Println("can't find retweet field")
			continue
		}
		if rt.(bool) == true {
			fmt.Println("already retweeted")
			continue
		}
		// check user
		user, ok := tweet["user"]
		if !ok {
			fmt.Println("can't find user field")
			continue
		}
		u, ok := user.(map[string]interface{})
		if !ok {
			fmt.Println("converting user struct failed", user)
			continue
		}
		id, ok := u["id"]
		if !ok {
			fmt.Println("can't find user id field")
			continue
		}
		uid, ok := id.(float64)
		if !ok {
			fmt.Println("converting user id type failed", id)
			continue
		}
		if uint64(uid) != userID {
			fmt.Println("not posted by user", userID)
			continue
		}

		// check tag
		tags := tweet.Entities().Hashtags()
		for _, tag := range tags {
			text, ok := tag["text"]
			if ok {
				t, ok := text.(string)
				if ok && t == "FF" {
					// do retweet
					if err = retweet(tweet.Id()); err == nil {
						fmt.Println(tweet.Id(), "is retweeted")
					}
					break
				}
			}
		}

		// check has media
		ee := tweet.ExtendedEntities()
		if len(ee) == 0 {
			fmt.Println("no extended entities, skip")
			continue
		}

		// do retweet
		if err = retweet(tweet.Id()); err == nil {
			fmt.Println(tweet.Id(), "is retweeted")
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package buildkite

import (
	"bufio"
	"errors"
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/oleiade/reflections"
	"os"
	"reflect"
	"strconv"
	"strings"
)

func LoadConfiguration(obj interface{}, c *cli.Context) error {
	configFileMap := map[string]string{}

	var pathToConfigFile string

	// If a config file was passed, load it into a map
	if c.String("config") != "" {
		pathToConfigFile = os.ExpandEnv(c.String("config"))
	} else {
		// If no config was passed, look at the default locations to
		// try and find one that exists
		pathToConfigFile = findDefaultConfiguration()
	}

	// If we need to load from a config file
	if pathToConfigFile != "" {
		loadedConfigFileMap, err := readFile(pathToConfigFile)
		if err != nil {
			return errors.New(fmt.Sprintf("Failed to load config file: %s", err))
		}

		configFileMap = loadedConfigFileMap

		// Store the loaded file in the configuration object
		_ = reflections.SetField(obj, "File", pathToConfigFile)
	}

	// Get all the fields from the configuration interface
	var fields []string
	fields, _ = reflections.Fields(obj)

	for _, name := range fields {
		// Check if the value needs to be loaded from the cli.Context
		cliName, err := reflections.GetFieldTag(obj, name, "cli")
		if err != nil || cliName == "" {
			continue
		}

		// Get the kind of field we need to set
		fieldKind, err := reflections.GetFieldKind(obj, name)
		if err != nil {
			return errors.New(fmt.Sprintf("Failed to get the type of struct field \"%s\"", name))
		}

		var value interface{}

		// Start by defaulting the value to what ever was provided by
		// the CLI, or the ENV
		if fieldKind == reflect.String {
			value = c.String(cliName)
		} else if fieldKind == reflect.Slice {
			value = c.StringSlice(cliName)
		} else if fieldKind == reflect.Bool {
			value = c.Bool(cliName)
		} else {
			return errors.New(fmt.Sprintf("Unable to handle type: %s", fieldKind))
		}

		// Now, override with what ever is in the conf file (if one has
		// been provided)
		if configFileValue, ok := configFileMap[cliName]; ok {
			// Convert the config file value to it's correct type
			if fieldKind == reflect.String {
				value = configFileValue
			} else if fieldKind == reflect.Slice {
				value = strings.Split(configFileValue, ",")
			} else if fieldKind == reflect.Bool {
				value, _ = strconv.ParseBool(configFileValue)
			} else {
				return errors.New(fmt.Sprintf("Unable to convert string to type %s", fieldKind))
			}
		}

		// Set the value if we've found data for it
		if value != nil {
			err = reflections.SetField(obj, name, value)
			if err != nil {
				return errors.New(fmt.Sprintf("Failed to set a value to struct field \"%s\"", name))
			}
		}
	}

	return nil
}

func findDefaultConfiguration() string {
	var paths []string

	if MachineIsWindows() {
		paths = []string{
			"$USERPROFILE\\AppData\\Local\\BuildkiteAgent\\buildkite-agent.cfg",
		}
	} else {
		paths = []string{
			"$HOME/.buildkite-agent/buildkite-agent.cfg",
			"/usr/local/etc/buildkite-agent/buildkite-agent.cfg",
			"/etc/buildkite-agent/buildkite-agent.cfg",
		}
	}

	// Return the first configration file that exists
	for _, path := range paths {
		expandedPath := os.ExpandEnv(path)

		if _, err := os.Stat(expandedPath); err == nil {
			return expandedPath
		}
	}

	return ""
}

// This file parsing code was copied from:
// https://github.com/joho/godotenv/blob/master/godotenv.go
//
// The project is released under an MIT License, which can be seen here:
// https://github.com/joho/godotenv/blob/master/LICENCE

func readFile(filename string) (envMap map[string]string, err error) {
	file, err := os.Open(filename)
	if err != nil {
		return
	}
	defer file.Close()

	envMap = make(map[string]string)

	var lines []string
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}

	for _, fullLine := range lines {
		if !isIgnoredLine(fullLine) {
			key, value, err := parseLine(fullLine)

			if err == nil {
				envMap[key] = value
			}
		}
	}
	return
}

func parseLine(line string) (key string, value string, err error) {
	if len(line) == 0 {
		err = errors.New("zero length string")
		return
	}

	// ditch the comments (but keep quoted hashes)
	if strings.Contains(line, "#") {
		segmentsBetweenHashes := strings.Split(line, "#")
		quotesAreOpen := false
		segmentsToKeep := make([]string, 0)
		for _, segment := range segmentsBetweenHashes {
			if strings.Count(segment, "\"") == 1 || strings.Count(segment, "'") == 1 {
				if quotesAreOpen {
					quotesAreOpen = false
					segmentsToKeep = append(segmentsToKeep, segment)
				} else {
					quotesAreOpen = true
				}
			}

			if len(segmentsToKeep) == 0 || quotesAreOpen {
				segmentsToKeep = append(segmentsToKeep, segment)
			}
		}

		line = strings.Join(segmentsToKeep, "#")
	}

	// now split key from value
	splitString := strings.SplitN(line, "=", 2)

	if len(splitString) != 2 {
		// try yaml mode!
		splitString = strings.SplitN(line, ":", 2)
	}

	if len(splitString) != 2 {
		err = errors.New("Can't separate key from value")
		return
	}

	// Parse the key
	key = splitString[0]
	if strings.HasPrefix(key, "export") {
		key = strings.TrimPrefix(key, "export")
	}
	key = strings.Trim(key, " ")

	// Parse the value
	value = splitString[1]
	// trim
	value = strings.Trim(value, " ")

	// check if we've got quoted values
	if strings.Count(value, "\"") == 2 || strings.Count(value, "'") == 2 {
		// pull the quotes off the edges
		value = strings.Trim(value, "\"'")

		// expand quotes
		value = strings.Replace(value, "\\\"", "\"", -1)
		// expand newlines
		value = strings.Replace(value, "\\n", "\n", -1)
	}

	return
}

func isIgnoredLine(line string) bool {
	trimmedLine := strings.Trim(line, " \n\t")
	return len(trimmedLine) == 0 || strings.HasPrefix(trimmedLine, "#")
}
<commit_msg>Use the config file as a default, and ENV and cli override.<commit_after>package buildkite

import (
	"bufio"
	"errors"
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/oleiade/reflections"
	"os"
	"reflect"
	"strconv"
	"strings"
)

func LoadConfiguration(obj interface{}, c *cli.Context) error {
	configFileMap := map[string]string{}

	var pathToConfigFile string

	// If a config file was passed, load it into a map
	if c.String("config") != "" {
		pathToConfigFile = os.ExpandEnv(c.String("config"))
	} else {
		// If no config was passed, look at the default locations to
		// try and find one that exists
		pathToConfigFile = findDefaultConfiguration()
	}

	// If we need to load from a config file
	if pathToConfigFile != "" {
		loadedConfigFileMap, err := readFile(pathToConfigFile)
		if err != nil {
			return errors.New(fmt.Sprintf("Failed to load config file: %s", err))
		}

		configFileMap = loadedConfigFileMap

		// Store the loaded file in the configuration object
		_ = reflections.SetField(obj, "File", pathToConfigFile)
	}

	// Get all the fields from the configuration interface
	var fields []string
	fields, _ = reflections.Fields(obj)

	for _, name := range fields {
		// Check if the value needs to be loaded from the cli.Context
		cliName, err := reflections.GetFieldTag(obj, name, "cli")
		if err != nil || cliName == "" {
			continue
		}

		// Get the kind of field we need to set
		fieldKind, err := reflections.GetFieldKind(obj, name)
		if err != nil {
			return errors.New(fmt.Sprintf("Failed to get the type of struct field \"%s\"", name))
		}

		var value interface{}

		// Start by defaulting the value to what ever was provided by
		// the configuration file
		if configFileValue, ok := configFileMap[cliName]; ok {
			// Convert the config file value to it's correct type
			if fieldKind == reflect.String {
				value = configFileValue
			} else if fieldKind == reflect.Slice {
				value = strings.Split(configFileValue, ",")
			} else if fieldKind == reflect.Bool {
				value, _ = strconv.ParseBool(configFileValue)
			} else {
				return errors.New(fmt.Sprintf("Unable to convert string to type %s", fieldKind))
			}
		}

		// Now, override with what ever is in the cli or the ENV
		if c.IsSet(cliName) {
			if fieldKind == reflect.String {
				value = c.String(cliName)
			} else if fieldKind == reflect.Slice {
				value = c.StringSlice(cliName)
			} else if fieldKind == reflect.Bool {
				value = c.Bool(cliName)
			} else {
				return errors.New(fmt.Sprintf("Unable to handle type: %s", fieldKind))
			}
		}

		// Set the value if we've found data for it
		if value != nil {
			err = reflections.SetField(obj, name, value)
			if err != nil {
				return errors.New(fmt.Sprintf("Failed to set a value to struct field \"%s\"", name))
			}
		}
	}

	return nil
}

func findDefaultConfiguration() string {
	var paths []string

	if MachineIsWindows() {
		paths = []string{
			"$USERPROFILE\\AppData\\Local\\BuildkiteAgent\\buildkite-agent.cfg",
		}
	} else {
		paths = []string{
			"$HOME/.buildkite-agent/buildkite-agent.cfg",
			"/usr/local/etc/buildkite-agent/buildkite-agent.cfg",
			"/etc/buildkite-agent/buildkite-agent.cfg",
		}
	}

	// Return the first configration file that exists
	for _, path := range paths {
		expandedPath := os.ExpandEnv(path)

		if _, err := os.Stat(expandedPath); err == nil {
			return expandedPath
		}
	}

	return ""
}

// This file parsing code was copied from:
// https://github.com/joho/godotenv/blob/master/godotenv.go
//
// The project is released under an MIT License, which can be seen here:
// https://github.com/joho/godotenv/blob/master/LICENCE

func readFile(filename string) (envMap map[string]string, err error) {
	file, err := os.Open(filename)
	if err != nil {
		return
	}
	defer file.Close()

	envMap = make(map[string]string)

	var lines []string
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}

	for _, fullLine := range lines {
		if !isIgnoredLine(fullLine) {
			key, value, err := parseLine(fullLine)

			if err == nil {
				envMap[key] = value
			}
		}
	}
	return
}

func parseLine(line string) (key string, value string, err error) {
	if len(line) == 0 {
		err = errors.New("zero length string")
		return
	}

	// ditch the comments (but keep quoted hashes)
	if strings.Contains(line, "#") {
		segmentsBetweenHashes := strings.Split(line, "#")
		quotesAreOpen := false
		segmentsToKeep := make([]string, 0)
		for _, segment := range segmentsBetweenHashes {
			if strings.Count(segment, "\"") == 1 || strings.Count(segment, "'") == 1 {
				if quotesAreOpen {
					quotesAreOpen = false
					segmentsToKeep = append(segmentsToKeep, segment)
				} else {
					quotesAreOpen = true
				}
			}

			if len(segmentsToKeep) == 0 || quotesAreOpen {
				segmentsToKeep = append(segmentsToKeep, segment)
			}
		}

		line = strings.Join(segmentsToKeep, "#")
	}

	// now split key from value
	splitString := strings.SplitN(line, "=", 2)

	if len(splitString) != 2 {
		// try yaml mode!
		splitString = strings.SplitN(line, ":", 2)
	}

	if len(splitString) != 2 {
		err = errors.New("Can't separate key from value")
		return
	}

	// Parse the key
	key = splitString[0]
	if strings.HasPrefix(key, "export") {
		key = strings.TrimPrefix(key, "export")
	}
	key = strings.Trim(key, " ")

	// Parse the value
	value = splitString[1]
	// trim
	value = strings.Trim(value, " ")

	// check if we've got quoted values
	if strings.Count(value, "\"") == 2 || strings.Count(value, "'") == 2 {
		// pull the quotes off the edges
		value = strings.Trim(value, "\"'")

		// expand quotes
		value = strings.Replace(value, "\\\"", "\"", -1)
		// expand newlines
		value = strings.Replace(value, "\\n", "\n", -1)
	}

	return
}

func isIgnoredLine(line string) bool {
	trimmedLine := strings.Trim(line, " \n\t")
	return len(trimmedLine) == 0 || strings.HasPrefix(trimmedLine, "#")
}
<|endoftext|>


<commit_before>package version

// Version contains the LXD version number
var Version = "4.13"
<commit_msg>Release LXD 4.14<commit_after>package version

// Version contains the LXD version number
var Version = "4.14"
<|endoftext|>


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

package frames

import (
	"bytes"
	"fmt"
	"io"

	"github.com/SlyMarbo/spdy/common"
)

type RST_STREAM struct {
	StreamID common.StreamID
	Status   common.StatusCode
}

func (frame *RST_STREAM) Compress(comp common.Compressor) error {
	return nil
}

func (frame *RST_STREAM) Decompress(decomp common.Decompressor) error {
	return nil
}

func (frame *RST_STREAM) Error() string {
	if err := frame.Status.String(); err != "" {
		return err
	}

	return fmt.Sprintf("[unknown status code %d]", frame.Status)
}

func (frame *RST_STREAM) Name() string {
	return "RST_STREAM"
}

func (frame *RST_STREAM) ReadFrom(reader io.Reader) (int64, error) {
	data, err := common.ReadExactly(reader, 16)
	if err != nil {
		return 0, err
	}

	err = controlFrameCommonProcessing(data[:5], _RST_STREAM, 0)
	if err != nil {
		return 16, err
	}

	// Get and check length.
	length := int(common.BytesToUint24(data[5:8]))
	if length != 8 {
		return 16, common.IncorrectDataLength(length, 8)
	} else if length > common.MAX_FRAME_SIZE-8 {
		return 16, common.FrameTooLarge
	}

	frame.StreamID = common.StreamID(common.BytesToUint32(data[8:12]))
	frame.Status = common.StatusCode(common.BytesToUint32(data[12:16]))

	if !frame.StreamID.Valid() {
		return 16, common.StreamIdTooLarge
	}

	return 16, nil
}

func (frame *RST_STREAM) String() string {
	buf := new(bytes.Buffer)

	buf.WriteString("RST_STREAM {\n\t")
	buf.WriteString(fmt.Sprintf("Version:              2\n\t"))
	buf.WriteString(fmt.Sprintf("Stream ID:            %d\n\t", frame.StreamID))
	buf.WriteString(fmt.Sprintf("Status code:          %s\n}\n", frame.Status))

	return buf.String()
}

func (frame *RST_STREAM) WriteTo(writer io.Writer) (int64, error) {
	if !frame.StreamID.Valid() {
		return 0, common.StreamIdTooLarge
	}

	out := make([]byte, 16)

	out[0] = 128                  // Control bit and Version
	out[1] = 2                    // Version
	out[2] = 0                    // Type
	out[3] = 2                    // Type
	out[4] = 0                    // Flags
	out[5] = 0                    // Length
	out[6] = 0                    // Length
	out[7] = 8                    // Length
	out[8] = frame.StreamID.B1()  // Stream ID
	out[9] = frame.StreamID.B2()  // Stream ID
	out[10] = frame.StreamID.B3() // Stream ID
	out[11] = frame.StreamID.B4() // Stream ID
	out[12] = frame.Status.B1()   // Status
	out[13] = frame.Status.B2()   // Status
	out[14] = frame.Status.B3()   // Status
	out[15] = frame.Status.B4()   // Status

	err := common.WriteExactly(writer, out)
	if err != nil {
		return 0, err
	}

	return 16, nil
}
<commit_msg>Fix rst_stream.go<commit_after>// Copyright 2014 Jamie Hall. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package frames

import (
	"bytes"
	"fmt"
	"io"

	"github.com/SlyMarbo/spdy/common"
)

type RST_STREAM struct {
	StreamID common.StreamID
	Status   common.StatusCode
}

func (frame *RST_STREAM) Compress(comp common.Compressor) error {
	return nil
}

func (frame *RST_STREAM) Decompress(decomp common.Decompressor) error {
	return nil
}

func (frame *RST_STREAM) Error() string {
	if err := frame.Status.String(); err != "" {
		return err
	}

	return fmt.Sprintf("[unknown status code %d]", frame.Status)
}

func (frame *RST_STREAM) Name() string {
	return "RST_STREAM"
}

func (frame *RST_STREAM) ReadFrom(reader io.Reader) (int64, error) {
	data, err := common.ReadExactly(reader, 16)
	if err != nil {
		return 0, err
	}

	err = controlFrameCommonProcessing(data[:5], _RST_STREAM, 0)
	if err != nil {
		return 16, err
	}

	// Get and check length.
	length := int(common.BytesToUint24(data[5:8]))
	if length != 8 {
		return 16, common.IncorrectDataLength(length, 8)
	} else if length > common.MAX_FRAME_SIZE-8 {
		return 16, common.FrameTooLarge
	}

	frame.StreamID = common.StreamID(common.BytesToUint32(data[8:12]))
	frame.Status = common.StatusCode(common.BytesToUint32(data[12:16]))

	if !frame.StreamID.Valid() {
		return 16, common.StreamIdTooLarge
	}

	return 16, nil
}

func (frame *RST_STREAM) String() string {
	buf := new(bytes.Buffer)

	buf.WriteString("RST_STREAM {\n\t")
	buf.WriteString(fmt.Sprintf("Version:              2\n\t"))
	buf.WriteString(fmt.Sprintf("Stream ID:            %d\n\t", frame.StreamID))
	buf.WriteString(fmt.Sprintf("Status code:          %s\n}\n", frame.Status))

	return buf.String()
}

func (frame *RST_STREAM) WriteTo(writer io.Writer) (int64, error) {
	if !frame.StreamID.Valid() {
		return 0, common.StreamIdTooLarge
	}

	out := make([]byte, 16)

	out[0] = 128                  // Control bit and Version
	out[1] = 2                    // Version
	out[2] = 0                    // Type
	out[3] = 3                    // Type
	out[4] = 0                    // Flags
	out[5] = 0                    // Length
	out[6] = 0                    // Length
	out[7] = 8                    // Length
	out[8] = frame.StreamID.B1()  // Stream ID
	out[9] = frame.StreamID.B2()  // Stream ID
	out[10] = frame.StreamID.B3() // Stream ID
	out[11] = frame.StreamID.B4() // Stream ID
	out[12] = frame.Status.B1()   // Status
	out[13] = frame.Status.B2()   // Status
	out[14] = frame.Status.B3()   // Status
	out[15] = frame.Status.B4()   // Status

	err := common.WriteExactly(writer, out)
	if err != nil {
		return 0, err
	}

	return 16, nil
}
<|endoftext|>


<commit_before>package browser

import (
	"net/url"
	"strings"

	"github.com/PuerkitoBio/goquery"
	"github.com/gomaps/surf/errors"
)

// Submittable represents an element that may be submitted, such as a form.
type Submittable interface {
	Method() string
	Action() string
	Input(name, value string) error
	Click(button string) error
	Submit() error
	Dom() *goquery.Selection
}

// Form is the default form element.
type Form struct {
	bow       Browsable
	selection *goquery.Selection
	method    string
	action    string
	fields    url.Values
	buttons   url.Values
}

// NewForm creates and returns a *Form type.
func NewForm(bow Browsable, s *goquery.Selection) *Form {
	fields, buttons := serializeForm(s)
	method, action := formAttributes(bow, s)

	//fmt.Println(fields)

	return &Form{
		bow:       bow,
		selection: s,
		method:    method,
		action:    action,
		fields:    fields,
		buttons:   buttons,
	}
}

// Method returns the form method, eg "GET" or "POST".
func (f *Form) Method() string {
	return f.method
}

// Action returns the form action URL.
// The URL will always be absolute.
func (f *Form) Action() string {
	return f.action
}

// Input sets the value of a form field.
func (f *Form) Input(name, value string) error {
	if _, ok := f.fields[name]; ok {
		f.fields.Set(name, value)
		return nil
	}
	return errors.NewElementNotFound(
		"No input found with name '%s'.", name)
}

// Submit submits the form.
// Clicks the first button in the form, or submits the form without using
// any button when the form does not contain any buttons.
func (f *Form) Submit() error {
	if len(f.buttons) > 0 {
		for name := range f.buttons {
			return f.Click(name)
		}
	}
	return f.send("", "")
}

// Click submits the form by clicking the button with the given name.
func (f *Form) Click(button string) error {
	if _, ok := f.buttons[button]; !ok {
		return errors.NewInvalidFormValue(
			"Form does not contain a button with the name '%s'.", button)
	}
	return f.send(button, f.buttons[button][0])
}

// Dom returns the inner *goquery.Selection.
func (f *Form) Dom() *goquery.Selection {
	return f.selection
}

// send submits the form.
func (f *Form) send(buttonName, buttonValue string) error {
	method, ok := f.selection.Attr("method")
	if !ok {
		method = "GET"
	}
	action, ok := f.selection.Attr("action")
	if !ok {
		action = f.bow.Url().String()
	}
	aurl, err := url.Parse(action)
	if err != nil {
		return err
	}
	aurl = f.bow.ResolveUrl(aurl)

	values := make(url.Values, len(f.fields)+1)
	for name, vals := range f.fields {
		values[name] = vals
	}
	if buttonName != "" {
		values.Set(buttonName, buttonValue)
	}

	//fmt.Println(values)
	if strings.ToUpper(method) == "GET" {
		return f.bow.OpenForm(aurl.String(), values)
	} else {
		enctype, _ := f.selection.Attr("enctype")
		if enctype == "multipart/form-data" {
			return f.bow.PostMultipart(aurl.String(), values)
		}
		return f.bow.PostForm(aurl.String(), values)
	}
}

// Serialize converts the form fields into a url.Values type.
// Returns two url.Value types. The first is the form field values, and the
// second is the form button values.
func serializeForm(sel *goquery.Selection) (url.Values, url.Values) {
	input := sel.Find("input,button,select")
	if input.Length() == 0 {
		return url.Values{}, url.Values{}
	}

	fields := make(url.Values)
	buttons := make(url.Values)
	input.Each(func(_ int, s *goquery.Selection) {
		name, ok := s.Attr("name")
		if ok {
			typ, ok := s.Attr("type")
			if ok {
				if typ == "submit" {
					val, ok := s.Attr("value")
					if ok {
						buttons.Add(name, val)
					} else {
						buttons.Add(name, "")
					}
				} else {
					val, ok := s.Attr("value")
					if !ok {
						val = ""
					}
					fields.Add(name, val)
				}
			} else {
				//fmt.Println(name)
				// Handle select (dropdown) inputs
				s.Find("option").Each(func(i int, s *goquery.Selection) {
					val, ok := s.Attr("selected")
					if ok {
						if val == "selected" {
							val, ok := s.Attr("value")
							if ok {
								//fmt.Println(val)
								fields.Add(name, val)
							}
						}
					}
				})
			}
		}
	})

	return fields, buttons
}

func formAttributes(bow Browsable, s *goquery.Selection) (string, string) {
	method, ok := s.Attr("method")
	if !ok {
		method = "GET"
	}
	action, ok := s.Attr("action")
	if !ok {
		action = bow.Url().String()
	}
	aurl, err := url.Parse(action)
	if err != nil {
		return "", ""
	}
	aurl = bow.ResolveUrl(aurl)

	return strings.ToUpper(method), aurl.String()
}
<commit_msg>include non-selectable forms<commit_after>package browser

import (
	"net/url"
	"strings"

	"github.com/PuerkitoBio/goquery"
	"github.com/gomaps/surf/errors"
)

// Submittable represents an element that may be submitted, such as a form.
type Submittable interface {
	Method() string
	Action() string
	Input(name, value string) error
	Click(button string) error
	Submit() error
	Dom() *goquery.Selection
}

// Form is the default form element.
type Form struct {
	bow       Browsable
	selection *goquery.Selection
	method    string
	action    string
	fields    url.Values
	buttons   url.Values
}

// NewForm creates and returns a *Form type.
func NewForm(bow Browsable, s *goquery.Selection) *Form {
	fields, buttons := serializeForm(s)
	method, action := formAttributes(bow, s)

	//fmt.Println(fields)

	return &Form{
		bow:       bow,
		selection: s,
		method:    method,
		action:    action,
		fields:    fields,
		buttons:   buttons,
	}
}

// Method returns the form method, eg "GET" or "POST".
func (f *Form) Method() string {
	return f.method
}

// Action returns the form action URL.
// The URL will always be absolute.
func (f *Form) Action() string {
	return f.action
}

// Input sets the value of a form field.
func (f *Form) Input(name, value string) error {
	if _, ok := f.fields[name]; ok {
		f.fields.Set(name, value)
		return nil
	}
	return errors.NewElementNotFound(
		"No input found with name '%s'.", name)
}

// Submit submits the form.
// Clicks the first button in the form, or submits the form without using
// any button when the form does not contain any buttons.
func (f *Form) Submit() error {
	if len(f.buttons) > 0 {
		for name := range f.buttons {
			return f.Click(name)
		}
	}
	return f.send("", "")
}

// Click submits the form by clicking the button with the given name.
func (f *Form) Click(button string) error {
	if _, ok := f.buttons[button]; !ok {
		return errors.NewInvalidFormValue(
			"Form does not contain a button with the name '%s'.", button)
	}
	return f.send(button, f.buttons[button][0])
}

// Dom returns the inner *goquery.Selection.
func (f *Form) Dom() *goquery.Selection {
	return f.selection
}

// send submits the form.
func (f *Form) send(buttonName, buttonValue string) error {
	method, ok := f.selection.Attr("method")
	if !ok {
		method = "GET"
	}
	action, ok := f.selection.Attr("action")
	if !ok {
		action = f.bow.Url().String()
	}
	aurl, err := url.Parse(action)
	if err != nil {
		return err
	}
	aurl = f.bow.ResolveUrl(aurl)

	values := make(url.Values, len(f.fields)+1)
	for name, vals := range f.fields {
		values[name] = vals
	}
	if buttonName != "" {
		values.Set(buttonName, buttonValue)
	}

	//fmt.Println(values)
	if strings.ToUpper(method) == "GET" {
		return f.bow.OpenForm(aurl.String(), values)
	} else {
		enctype, _ := f.selection.Attr("enctype")
		if enctype == "multipart/form-data" {
			return f.bow.PostMultipart(aurl.String(), values)
		}
		return f.bow.PostForm(aurl.String(), values)
	}
}

// Serialize converts the form fields into a url.Values type.
// Returns two url.Value types. The first is the form field values, and the
// second is the form button values.
func serializeForm(sel *goquery.Selection) (url.Values, url.Values) {
	input := sel.Find("input,button,select")
	if input.Length() == 0 {
		return url.Values{}, url.Values{}
	}

	fields := make(url.Values)
	buttons := make(url.Values)
	input.Each(func(_ int, s *goquery.Selection) {
		name, ok := s.Attr("name")
		if ok {
			typ, ok := s.Attr("type")
			if ok {
				if typ == "submit" {
					val, ok := s.Attr("value")
					if ok {
						buttons.Add(name, val)
					} else {
						buttons.Add(name, "")
					}
				} else {
					val, ok := s.Attr("value")
					if !ok {
						val = ""
					}
					fields.Add(name, val)
				}
			} else {
				//fmt.Println(name)
				// Handle select (dropdown) inputs
				s.Find("option").Each(func(i int, s *goquery.Selection) {
					fields.Add(name, "")
				})
			}
		}
	})

	return fields, buttons
}

func formAttributes(bow Browsable, s *goquery.Selection) (string, string) {
	method, ok := s.Attr("method")
	if !ok {
		method = "GET"
	}
	action, ok := s.Attr("action")
	if !ok {
		action = bow.Url().String()
	}
	aurl, err := url.Parse(action)
	if err != nil {
		return "", ""
	}
	aurl = bow.ResolveUrl(aurl)

	return strings.ToUpper(method), aurl.String()
}
<|endoftext|>


<commit_before>package rotatefile

import (
	"io"
	"os"
	"path/filepath"

	"github.com/philchia/gol/adapter"
)

// ByteSize represent file size in byte
type ByteSize float64

const (
	_ ByteSize = 1 << (iota * 10)
	// KB = 1 kb bytes
	KB
	// MB = 1 mb bytes
	MB
	// GB = 1 gb bytes
	GB
	// TB = 1 tb bytes
	TB
	// PB = 1 pb bytes
	PB
	// EB = 1 eb bytes
	EB
	// ZB = 1 zb bytes
	ZB
	// YB = 1 yb bytes
	YB
)

var _ adapter.Adapter = (*rotatefileAdapter)(nil)

type rotatefileAdapter struct {
	maxFileBackups     int
	maxByteSizePerFile ByteSize
	size               ByteSize
	fileName           string
	io.WriteCloser
}

// NewAdapter create a new rotate file adapter
func NewAdapter(name string, maxFileBackups int, maxBytesPerFile ByteSize) adapter.Adapter {
	path, err := filepath.Abs(name)
	if err != nil {
		return nil
	}
	file, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return nil
	}
	info, err := os.Stat(path)
	if err != nil {
		return nil
	}

	adapter := &rotatefileAdapter{
		maxFileBackups:     maxFileBackups,
		maxByteSizePerFile: maxBytesPerFile,
		fileName:           name,
		WriteCloser:        file,
		size:               ByteSize(info.Size()),
	}
	return adapter
}

// Write implement Writer
func (r *rotatefileAdapter) Write(b []byte) (n int, err error) {
	n, err = r.WriteCloser.Write(b)
	r.size += ByteSize(n)
	return
}
<commit_msg>use int64 as ByteSize<commit_after>package rotatefile

import (
	"io"
	"os"
	"path/filepath"

	"github.com/philchia/gol/adapter"
)

// ByteSize represent file size in byte
type ByteSize int64

const (
	_ ByteSize = 1 << (iota * 10)
	// KB = 1 kb bytes
	KB
	// MB = 1 mb bytes
	MB
	// GB = 1 gb bytes
	GB
	// TB = 1 tb bytes
	TB
	// PB = 1 pb bytes
	PB
	// EB = 1 eb bytes
	EB
)

var _ adapter.Adapter = (*rotatefileAdapter)(nil)

type rotatefileAdapter struct {
	maxFileBackups     int
	maxByteSizePerFile ByteSize
	size               ByteSize
	fileName           string
	io.WriteCloser
}

// NewAdapter create a new rotate file adapter
func NewAdapter(name string, maxFileBackups int, maxBytesPerFile ByteSize) adapter.Adapter {
	path, err := filepath.Abs(name)
	if err != nil {
		return nil
	}
	file, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return nil
	}
	info, err := os.Stat(path)
	if err != nil {
		return nil
	}

	adapter := &rotatefileAdapter{
		maxFileBackups:     maxFileBackups,
		maxByteSizePerFile: maxBytesPerFile,
		fileName:           name,
		WriteCloser:        file,
		size:               ByteSize(info.Size()),
	}
	return adapter
}

// Write implement Writer
func (r *rotatefileAdapter) Write(b []byte) (n int, err error) {
	n, err = r.WriteCloser.Write(b)
	r.size += ByteSize(n)
	return
}
<|endoftext|>


<commit_before>// +build !goczmq

package boomer

import (
	"fmt"
	"log"
	"testing"

	"github.com/zeromq/gomq"
	"github.com/zeromq/gomq/zmtp"
)

type testServer struct {
	bindHost       string
	pushPort       int
	pullPort       int
	fromClient     chan *message
	toClient       chan *message
	pushSocket     *gomq.PushSocket
	pullSocket     *gomq.PullSocket
	shutdownSignal chan bool
}

func newTestServer(bindHost string, pushPort, pullPort int) (server *testServer) {
	return &testServer{
		bindHost:       bindHost,
		pushPort:       pushPort,
		pullPort:       pullPort,
		fromClient:     make(chan *message, 100),
		toClient:       make(chan *message, 100),
		shutdownSignal: make(chan bool, 1),
	}
}

func (s *testServer) send() {
	for {
		select {
		case <-s.shutdownSignal:
			s.pushSocket.Close()
			return
		case msg := <-s.toClient:
			s.sendMessage(msg)
		}
	}
}

func (s *testServer) sendMessage(msg *message) {
	err := s.pushSocket.Send(msg.serialize())
	if err != nil {
		log.Printf("Error sending to client: %v\n", err)
	}
}

func (s *testServer) recv() {
	for {
		select {
		case <-s.shutdownSignal:
			s.pullSocket.Close()
			return
		default:
			msg, err := s.pullSocket.Recv()
			if err != nil {
				log.Printf("Error reading: %v\n", err)
			} else {
				msgFromClient := newMessageFromBytes(msg)
				s.fromClient <- msgFromClient
			}
		}
	}
}

func (s *testServer) close() {
	close(s.shutdownSignal)
}

func (s *testServer) start() {
	pushAddr := fmt.Sprintf("tcp://%s:%d", s.bindHost, s.pushPort)
	pullAddr := fmt.Sprintf("tcp://%s:%d", s.bindHost, s.pullPort)

	pushSocket := gomq.NewPush(zmtp.NewSecurityNull())
	pullSocket := gomq.NewPull(zmtp.NewSecurityNull())

	go pushSocket.Bind(pushAddr)
	go pullSocket.Bind(pullAddr)
	s.pushSocket = pushSocket
	s.pullSocket = pullSocket

	go s.recv()
	go s.send()
}

func TestPingPong(t *testing.T) {
	masterHost := "127.0.0.1"
	masterPort := 5557
	toMaster = make(chan *message, 10)

	server := newTestServer(masterHost, masterPort+1, masterPort)
	defer server.close()
	server.start()

	// start client
	client := newZmqClient(masterHost, masterPort)
	defer client.close()

	toMaster <- newMessage("ping", nil, "testing ping pong")
	msg := <-server.fromClient
	if msg.Type != "ping" || msg.NodeID != "testing ping pong" {
		t.Error("server doesn't recv ping message")
	}

	server.toClient <- newMessage("pong", nil, "testing ping pong")
	msg = <-fromMaster
	if msg.Type != "pong" || msg.NodeID != "testing ping pong" {
		t.Error("client doesn't recv pong message")
	}
}
<commit_msg>remove close to avoid panics from inside gomq<commit_after>// +build !goczmq

package boomer

import (
	"fmt"
	"log"
	"testing"

	"github.com/zeromq/gomq"
	"github.com/zeromq/gomq/zmtp"
)

type testServer struct {
	bindHost       string
	pushPort       int
	pullPort       int
	fromClient     chan *message
	toClient       chan *message
	pushSocket     *gomq.PushSocket
	pullSocket     *gomq.PullSocket
	shutdownSignal chan bool
}

func newTestServer(bindHost string, pushPort, pullPort int) (server *testServer) {
	return &testServer{
		bindHost:       bindHost,
		pushPort:       pushPort,
		pullPort:       pullPort,
		fromClient:     make(chan *message, 100),
		toClient:       make(chan *message, 100),
		shutdownSignal: make(chan bool, 1),
	}
}

func (s *testServer) send() {
	for {
		select {
		case <-s.shutdownSignal:
			s.pushSocket.Close()
			return
		case msg := <-s.toClient:
			s.sendMessage(msg)
		}
	}
}

func (s *testServer) sendMessage(msg *message) {
	err := s.pushSocket.Send(msg.serialize())
	if err != nil {
		log.Printf("Error sending to client: %v\n", err)
	}
}

func (s *testServer) recv() {
	for {
		select {
		case <-s.shutdownSignal:
			s.pullSocket.Close()
			return
		default:
			msg, err := s.pullSocket.Recv()
			if err != nil {
				log.Printf("Error reading: %v\n", err)
			} else {
				msgFromClient := newMessageFromBytes(msg)
				s.fromClient <- msgFromClient
			}
		}
	}
}

func (s *testServer) close() {
	close(s.shutdownSignal)
}

func (s *testServer) start() {
	pushAddr := fmt.Sprintf("tcp://%s:%d", s.bindHost, s.pushPort)
	pullAddr := fmt.Sprintf("tcp://%s:%d", s.bindHost, s.pullPort)

	pushSocket := gomq.NewPush(zmtp.NewSecurityNull())
	pullSocket := gomq.NewPull(zmtp.NewSecurityNull())

	go pushSocket.Bind(pushAddr)
	go pullSocket.Bind(pullAddr)
	s.pushSocket = pushSocket
	s.pullSocket = pullSocket

	go s.recv()
	go s.send()
}

func TestPingPong(t *testing.T) {
	masterHost := "127.0.0.1"
	masterPort := 5557
	toMaster = make(chan *message, 10)

	server := newTestServer(masterHost, masterPort+1, masterPort)
	// defer server.close()
	server.start()

	// start client
	newZmqClient(masterHost, masterPort)
	// defer client.close()

	toMaster <- newMessage("ping", nil, "testing ping pong")
	msg := <-server.fromClient
	if msg.Type != "ping" || msg.NodeID != "testing ping pong" {
		t.Error("server doesn't recv ping message")
	}

	server.toClient <- newMessage("pong", nil, "testing ping pong")
	msg = <-fromMaster
	if msg.Type != "pong" || msg.NodeID != "testing ping pong" {
		t.Error("client doesn't recv pong message")
	}
}
<|endoftext|>