<commit_before>/*
A smart client for go.

Usage:

 client, err := couchbase.Connect("http://myserver:8091/")
 handleError(err)
 pool, err := client.GetPool("default")
 handleError(err)
 bucket, err := pool.getBucket("MyAwesomeBucket")
 handleError(err)
 ...

or a shortcut for the bucket directly

 bucket, err := couchbase.GetBucket("http://myserver:8091/", "default", "default")
*/
package couchbase

import (
	"encoding/json"
	"errors"
	"fmt"
	"math/rand"
	"net/http"
	"net/url"
	"sync"
	"sync/atomic"

	"github.com/dustin/gomemcached"
	"github.com/dustin/gomemcached/client"
)

type connectionPool struct {
	host        string
	name        string
	connections []*memcached.Client
	mutex       sync.Mutex
}

func (cp *connectionPool) Close() error {
	cp.mutex.Lock()
	defer cp.mutex.Unlock()
	for _, c := range cp.connections {
		c.Close()
	}
	cp.connections = []*memcached.Client{}
	return nil
}

func (cp *connectionPool) Get() (*memcached.Client, error) {
	cp.mutex.Lock()
	defer cp.mutex.Unlock()

	if len(cp.connections) == 0 {
		conn, err := memcached.Connect("tcp", cp.host)
		if err != nil {
			return nil, err
		}
		if cp.name != "default" {
			conn.Auth(cp.name, "")
		}

		cp.connections = append(cp.connections, conn)
	}

	rv := cp.connections[0]
	cp.connections = cp.connections[1:]

	return rv, nil
}

func (cp *connectionPool) Return(c *memcached.Client) {
	cp.mutex.Lock()
	defer cp.mutex.Unlock()

	if c != nil {
		if c.IsHealthy() {
			cp.connections = append(cp.connections, c)
		} else {
			c.Close()
		}
	}
}

// Execute a function on a memcached connection to the node owning key "k"
//
// Note that this automatically handles transient errors by replaying
// your function on a "not-my-vbucket" error, so don't assume
// your command will only be executed only once.
func (b *Bucket) Do(k string, f func(mc *memcached.Client, vb uint16) error) error {
	vb := b.VBHash(k)
	for {
		masterId := b.VBucketServerMap.VBucketMap[vb][0]
		conn, err := b.connections[masterId].Get()
		defer b.connections[masterId].Return(conn)
		if err != nil {
			return err
		}

		err = f(conn, uint16(vb))
		switch err.(type) {
		default:
			return err
		case gomemcached.MCResponse:
			st := err.(gomemcached.MCResponse).Status
			atomic.AddUint64(&b.pool.client.Statuses[st], 1)
			if st == gomemcached.NOT_MY_VBUCKET {
				b.refresh()
			} else {
				return err
			}
		}
	}
	panic("Unreachable.")
}

type gathered_stats struct {
	sn   string
	vals map[string]string
}

func getStatsParallel(b *Bucket, offset int, which string, ch chan<- gathered_stats) {
	sn := b.VBucketServerMap.ServerList[offset]

	results := map[string]string{}
	conn, err := b.connections[offset].Get()
	defer b.connections[offset].Return(conn)
	if err != nil {
		ch <- gathered_stats{sn, results}
	} else {
		st, err := conn.StatsMap(which)
		if err == nil {
			ch <- gathered_stats{sn, st}
		} else {
			ch <- gathered_stats{sn, results}
		}
	}
}

// Get a set of stats from all servers.
//
// Returns a map of server ID -> map of stat key to map value.
func (b *Bucket) GetStats(which string) map[string]map[string]string {
	rv := map[string]map[string]string{}

	if b.VBucketServerMap.ServerList == nil {
		return rv
	}
	// Go grab all the things at once.
	todo := len(b.VBucketServerMap.ServerList)
	ch := make(chan gathered_stats, todo)

	for offset, _ := range b.VBucketServerMap.ServerList {
		go getStatsParallel(b, offset, which, ch)
	}

	// Gather the results
	for i := 0; i < len(b.VBucketServerMap.ServerList); i++ {
		g := <-ch
		if len(g.vals) > 0 {
			rv[g.sn] = g.vals
		}
	}

	return rv
}

func (b *Bucket) doBulkGet(vb uint16, keys []string,
	ch chan<- map[string]*gomemcached.MCResponse) {

	masterId := b.VBucketServerMap.VBucketMap[vb][0]
	conn, err := b.connections[masterId].Get()
	if err != nil {
		ch <- map[string]*gomemcached.MCResponse{}
	}
	defer b.connections[masterId].Return(conn)

	m, err := conn.GetBulk(vb, keys)
	switch err.(type) {
	default:
		ch <- m
	case *gomemcached.MCResponse:
		fmt.Printf("Got a memcached error")
		st := err.(gomemcached.MCResponse).Status
		atomic.AddUint64(&b.pool.client.Statuses[st], 1)
		if st == gomemcached.NOT_MY_VBUCKET {
			b.refresh()
		}
		ch <- map[string]*gomemcached.MCResponse{}
	}
}

func (b *Bucket) GetBulk(keys []string) map[string]*gomemcached.MCResponse {
	// Organize by vbucket
	kdm := map[uint16][]string{}
	for _, k := range keys {
		vb := uint16(b.VBHash(k))
		a, ok := kdm[vb]
		if !ok {
			a = []string{}
		}
		kdm[vb] = append(a, k)
	}

	ch := make(chan map[string]*gomemcached.MCResponse)
	defer close(ch)

	for k, v := range kdm {
		go b.doBulkGet(k, v, ch)
	}

	rv := map[string]*gomemcached.MCResponse{}
	for _ = range kdm {
		m := <-ch
		for k, v := range m {
			rv[k] = v
		}
	}

	return rv
}

// Set a value in this bucket.
// The value will be serialized into a JSON document.
func (b *Bucket) Set(k string, v interface{}) error {
	return b.Do(k, func(mc *memcached.Client, vb uint16) error {
		data, err := json.Marshal(v)
		if err != nil {
			return err
		}
		res, err := mc.Set(vb, k, 0, 0, data)
		if err != nil {
			return err
		}
		if res.Status != gomemcached.SUCCESS {
			return res
		}
		return nil
	})
}

// Get a value from this bucket.
// The value is expected to be a JSON stream and will be deserialized
// into rv.
func (b *Bucket) Gets(k string, rv interface{}, cas *uint64) error {
	return b.Do(k, func(mc *memcached.Client, vb uint16) error {
		res, err := mc.Get(vb, k)
		if err != nil {
			return err
		}
		if res.Status != gomemcached.SUCCESS {
			return res
		}
		if cas != nil {
			*cas = res.Cas
		}
		return json.Unmarshal(res.Body, rv)
	})
}

// Get a value from this bucket.
// The value is expected to be a JSON stream and will be deserialized
// into rv.
func (b *Bucket) Get(k string, rv interface{}) error {
	return b.Gets(k, rv, nil)
}

// Delete a key from this bucket.
func (b *Bucket) Delete(k string) error {
	return b.Do(k, func(mc *memcached.Client, vb uint16) error {
		res, err := mc.Del(vb, k)
		if err != nil {
			return err
		}
		if res.Status != gomemcached.SUCCESS {
			return res
		}
		return nil
	})
}

// Increment a key
func (b *Bucket) Incr(k string, amt, def uint64, exp int) (uint64, error) {
	var rv uint64
	err := b.Do(k, func(mc *memcached.Client, vb uint16) error {
		res, err := mc.Incr(vb, k, amt, def, exp)
		if err != nil {
			return err
		}
		rv = res
		return nil
	})
	return rv, err
}

type ViewRow struct {
	ID    string
	Key   interface{}
	Value interface{}
	Doc   *interface{}
}

type ViewResult struct {
	TotalRows int `json:"total_rows"`
	Rows      []ViewRow
	Errors    []struct {
		From   string
		Reason string
	}
}

// Perform a view request that can map row values to a custom type.
//
// See the source to View for an example usage.
func (b *Bucket) ViewCustom(ddoc, name string, params map[string]interface{},
	vres interface{}) error {

	// Pick a random node to service our request.
	node := b.Nodes[rand.Intn(len(b.Nodes))]
	u, err := url.Parse(node.CouchAPIBase)
	if err != nil {
		return err
	}

	values := url.Values{}
	for k, v := range params {
		values[k] = []string{fmt.Sprintf("%v", v)}
	}

	u.Path = fmt.Sprintf("/%s/_design/%s/_view/%s", b.Name, ddoc, name)
	u.RawQuery = values.Encode()

	res, err := http.Get(u.String())
	if err != nil {
		return err
	}
	defer res.Body.Close()

	if res.StatusCode != 200 {
		return errors.New(res.Status)
	}

	d := json.NewDecoder(res.Body)
	if err := d.Decode(vres); err != nil {
		return err
	}
	return nil
}

// Execute a view
func (b *Bucket) View(ddoc, name string, params map[string]interface{}) (ViewResult, error) {
	vres := ViewResult{}
	return vres, b.ViewCustom(ddoc, name, params, &vres)
}
<commit_msg>Handle more types in view args Closes #5<commit_after>/*
A smart client for go.

Usage:

 client, err := couchbase.Connect("http://myserver:8091/")
 handleError(err)
 pool, err := client.GetPool("default")
 handleError(err)
 bucket, err := pool.getBucket("MyAwesomeBucket")
 handleError(err)
 ...

or a shortcut for the bucket directly

 bucket, err := couchbase.GetBucket("http://myserver:8091/", "default", "default")
*/
package couchbase

import (
	"encoding/json"
	"errors"
	"fmt"
	"math/rand"
	"net/http"
	"net/url"
	"sync"
	"sync/atomic"

	"github.com/dustin/gomemcached"
	"github.com/dustin/gomemcached/client"
)

type connectionPool struct {
	host        string
	name        string
	connections []*memcached.Client
	mutex       sync.Mutex
}

func (cp *connectionPool) Close() error {
	cp.mutex.Lock()
	defer cp.mutex.Unlock()
	for _, c := range cp.connections {
		c.Close()
	}
	cp.connections = []*memcached.Client{}
	return nil
}

func (cp *connectionPool) Get() (*memcached.Client, error) {
	cp.mutex.Lock()
	defer cp.mutex.Unlock()

	if len(cp.connections) == 0 {
		conn, err := memcached.Connect("tcp", cp.host)
		if err != nil {
			return nil, err
		}
		if cp.name != "default" {
			conn.Auth(cp.name, "")
		}

		cp.connections = append(cp.connections, conn)
	}

	rv := cp.connections[0]
	cp.connections = cp.connections[1:]

	return rv, nil
}

func (cp *connectionPool) Return(c *memcached.Client) {
	cp.mutex.Lock()
	defer cp.mutex.Unlock()

	if c != nil {
		if c.IsHealthy() {
			cp.connections = append(cp.connections, c)
		} else {
			c.Close()
		}
	}
}

// Execute a function on a memcached connection to the node owning key "k"
//
// Note that this automatically handles transient errors by replaying
// your function on a "not-my-vbucket" error, so don't assume
// your command will only be executed only once.
func (b *Bucket) Do(k string, f func(mc *memcached.Client, vb uint16) error) error {
	vb := b.VBHash(k)
	for {
		masterId := b.VBucketServerMap.VBucketMap[vb][0]
		conn, err := b.connections[masterId].Get()
		defer b.connections[masterId].Return(conn)
		if err != nil {
			return err
		}

		err = f(conn, uint16(vb))
		switch err.(type) {
		default:
			return err
		case gomemcached.MCResponse:
			st := err.(gomemcached.MCResponse).Status
			atomic.AddUint64(&b.pool.client.Statuses[st], 1)
			if st == gomemcached.NOT_MY_VBUCKET {
				b.refresh()
			} else {
				return err
			}
		}
	}
	panic("Unreachable.")
}

type gathered_stats struct {
	sn   string
	vals map[string]string
}

func getStatsParallel(b *Bucket, offset int, which string, ch chan<- gathered_stats) {
	sn := b.VBucketServerMap.ServerList[offset]

	results := map[string]string{}
	conn, err := b.connections[offset].Get()
	defer b.connections[offset].Return(conn)
	if err != nil {
		ch <- gathered_stats{sn, results}
	} else {
		st, err := conn.StatsMap(which)
		if err == nil {
			ch <- gathered_stats{sn, st}
		} else {
			ch <- gathered_stats{sn, results}
		}
	}
}

// Get a set of stats from all servers.
//
// Returns a map of server ID -> map of stat key to map value.
func (b *Bucket) GetStats(which string) map[string]map[string]string {
	rv := map[string]map[string]string{}

	if b.VBucketServerMap.ServerList == nil {
		return rv
	}
	// Go grab all the things at once.
	todo := len(b.VBucketServerMap.ServerList)
	ch := make(chan gathered_stats, todo)

	for offset, _ := range b.VBucketServerMap.ServerList {
		go getStatsParallel(b, offset, which, ch)
	}

	// Gather the results
	for i := 0; i < len(b.VBucketServerMap.ServerList); i++ {
		g := <-ch
		if len(g.vals) > 0 {
			rv[g.sn] = g.vals
		}
	}

	return rv
}

func (b *Bucket) doBulkGet(vb uint16, keys []string,
	ch chan<- map[string]*gomemcached.MCResponse) {

	masterId := b.VBucketServerMap.VBucketMap[vb][0]
	conn, err := b.connections[masterId].Get()
	if err != nil {
		ch <- map[string]*gomemcached.MCResponse{}
	}
	defer b.connections[masterId].Return(conn)

	m, err := conn.GetBulk(vb, keys)
	switch err.(type) {
	default:
		ch <- m
	case *gomemcached.MCResponse:
		fmt.Printf("Got a memcached error")
		st := err.(gomemcached.MCResponse).Status
		atomic.AddUint64(&b.pool.client.Statuses[st], 1)
		if st == gomemcached.NOT_MY_VBUCKET {
			b.refresh()
		}
		ch <- map[string]*gomemcached.MCResponse{}
	}
}

func (b *Bucket) GetBulk(keys []string) map[string]*gomemcached.MCResponse {
	// Organize by vbucket
	kdm := map[uint16][]string{}
	for _, k := range keys {
		vb := uint16(b.VBHash(k))
		a, ok := kdm[vb]
		if !ok {
			a = []string{}
		}
		kdm[vb] = append(a, k)
	}

	ch := make(chan map[string]*gomemcached.MCResponse)
	defer close(ch)

	for k, v := range kdm {
		go b.doBulkGet(k, v, ch)
	}

	rv := map[string]*gomemcached.MCResponse{}
	for _ = range kdm {
		m := <-ch
		for k, v := range m {
			rv[k] = v
		}
	}

	return rv
}

// Set a value in this bucket.
// The value will be serialized into a JSON document.
func (b *Bucket) Set(k string, v interface{}) error {
	return b.Do(k, func(mc *memcached.Client, vb uint16) error {
		data, err := json.Marshal(v)
		if err != nil {
			return err
		}
		res, err := mc.Set(vb, k, 0, 0, data)
		if err != nil {
			return err
		}
		if res.Status != gomemcached.SUCCESS {
			return res
		}
		return nil
	})
}

// Get a value from this bucket.
// The value is expected to be a JSON stream and will be deserialized
// into rv.
func (b *Bucket) Gets(k string, rv interface{}, cas *uint64) error {
	return b.Do(k, func(mc *memcached.Client, vb uint16) error {
		res, err := mc.Get(vb, k)
		if err != nil {
			return err
		}
		if res.Status != gomemcached.SUCCESS {
			return res
		}
		if cas != nil {
			*cas = res.Cas
		}
		return json.Unmarshal(res.Body, rv)
	})
}

// Get a value from this bucket.
// The value is expected to be a JSON stream and will be deserialized
// into rv.
func (b *Bucket) Get(k string, rv interface{}) error {
	return b.Gets(k, rv, nil)
}

// Delete a key from this bucket.
func (b *Bucket) Delete(k string) error {
	return b.Do(k, func(mc *memcached.Client, vb uint16) error {
		res, err := mc.Del(vb, k)
		if err != nil {
			return err
		}
		if res.Status != gomemcached.SUCCESS {
			return res
		}
		return nil
	})
}

// Increment a key
func (b *Bucket) Incr(k string, amt, def uint64, exp int) (uint64, error) {
	var rv uint64
	err := b.Do(k, func(mc *memcached.Client, vb uint16) error {
		res, err := mc.Incr(vb, k, amt, def, exp)
		if err != nil {
			return err
		}
		rv = res
		return nil
	})
	return rv, err
}

type ViewRow struct {
	ID    string
	Key   interface{}
	Value interface{}
	Doc   *interface{}
}

type ViewResult struct {
	TotalRows int `json:"total_rows"`
	Rows      []ViewRow
	Errors    []struct {
		From   string
		Reason string
	}
}

// Perform a view request that can map row values to a custom type.
//
// See the source to View for an example usage.
func (b *Bucket) ViewCustom(ddoc, name string, params map[string]interface{},
	vres interface{}) error {

	// Pick a random node to service our request.
	node := b.Nodes[rand.Intn(len(b.Nodes))]
	u, err := url.Parse(node.CouchAPIBase)
	if err != nil {
		return err
	}

	values := url.Values{}
	for k, v := range params {
		switch t := v.(type) {
		case string:
			values[k] = []string{fmt.Sprintf(`"%s"`, url.QueryEscape(t))}
		case int:
			values[k] = []string{fmt.Sprintf(`%d`, t)}
		case bool:
			values[k] = []string{fmt.Sprintf(`%v`, t)}
		default:
			b, err := json.Marshal(v)
			if err != nil {
				panic(fmt.Sprintf("unsupported value-type %T in Query, json encoder said %v", t, err))
			}
			values[k] = []string{fmt.Sprintf(`%v`, string(b))}
		}
	}

	u.Path = fmt.Sprintf("/%s/_design/%s/_view/%s", b.Name, ddoc, name)
	u.RawQuery = values.Encode()

	res, err := http.Get(u.String())
	if err != nil {
		return err
	}
	defer res.Body.Close()

	if res.StatusCode != 200 {
		return errors.New(res.Status)
	}

	d := json.NewDecoder(res.Body)
	if err := d.Decode(vres); err != nil {
		return err
	}
	return nil
}

// Execute a view
func (b *Bucket) View(ddoc, name string, params map[string]interface{}) (ViewResult, error) {
	vres := ViewResult{}
	return vres, b.ViewCustom(ddoc, name, params, &vres)
}
<|endoftext|>


<commit_before>package grab

import (
	"fmt"
	"mime"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"
)

// A Client is a file download client.
//
// Clients are safe for concurrent use by multiple goroutines.
type Client struct {
	// HTTPClient specifies the http.Client which will be used for communicating
	// with the remote server during the file transfer.
	HTTPClient *http.Client

	// UserAgent specifies the User-Agent string which will be set in the
	// headers of all requests made by this client.
	//
	// The user agent string may be overridden in the headers of each request.
	UserAgent string
}

// NewClient returns a new file download Client, using default configuration.
func NewClient() *Client {
	return &Client{
		UserAgent: "grab",
		HTTPClient: &http.Client{
			Transport: &http.Transport{
				Proxy: http.ProxyFromEnvironment,
			},
		},
	}
}

// DefaultClient is the default client and is used by all Get convenience
// functions.
var DefaultClient = NewClient()

// CancelRequest cancels an in-flight request by closing its connection.
func (c *Client) CancelRequest(req *Request) {
	if t, ok := c.HTTPClient.Transport.(*http.Transport); ok {
		t.CancelRequest(req.HTTPRequest)
	}
}

// Do sends a file transfer request and returns a file transfer response
// context, following policy (e.g. redirects, cookies, auth) as configured on
// the client's HTTPClient.
//
// An error is returned if caused by client policy (such as CheckRedirect), or
// if there was an HTTP protocol error.
//
// Do is a synchronous, blocking operation which returns only once a download
// request is completed or fails. For non-blocking operations which enable the
// monitoring of transfers in process, see DoAsync and DoBatch.
func (c *Client) Do(req *Request) (*Response, error) {
	// prepare request with HEAD request
	resp, err := c.do(req)
	if err != nil {
		return resp, err
	}

	// transfer content
	if !resp.IsComplete() {
		if err := resp.copy(); err != nil {
			return resp, err
		}
	}

	return resp, nil
}

// DoAsync sends a file transfer request and returns a channel to receive the
// file transfer response context.
//
// The Response is sent via the returned channel and the channel closed as soon
// as the HTTP/1.1 GET request has been served; before the file transfer begins.
//
// The Response may then be used to monitor the progress of the file transfer
// while it is in process.
//
// Any error which occurs during the file transfer will be set in the returned
// Response.Error field as soon as the Response.IsComplete method returns true.
func (c *Client) DoAsync(req *Request) <-chan *Response {
	r := make(chan *Response, 1)
	go func() {
		// prepare request with HEAD request
		resp, err := c.do(req)
		if err == nil && !resp.IsComplete() {
			// transfer data in new goroutine
			go resp.copy()
		}

		r <- resp
		close(r)
	}()

	return r
}

// DoBatch executes multiple requests with the given number of workers and
// immediately returns a channel to receive the Responses as they become
// available. Excess requests are queued until a worker becomes available. The
// channel is closed once all responses have been sent.
//
// If zero is given as the worker count, one worker will be created for each
// given request and all requests will start at the same time.
//
// Each response is sent through the channel once the request is initiated via
// HTTP GET or an error has occurred, but before the file transfer begins.
//
// Any error which occurs during any of the file transfers will be set in the
// associated Response.Error field as soon as the Response.IsComplete method
// returns true.
func (c *Client) DoBatch(workers int, reqs ...*Request) <-chan *Response {
	// TODO: enable cancelling of batch jobs

	// start work queue
	producer := make(chan *Request, 0)
	go func() {
		// feed queue
		for i := 0; i < len(reqs); i++ {
			producer <- reqs[i]
		}
		close(producer)
	}()

	// default one worker per request
	if workers == 0 {
		workers = len(reqs)
	}

	return c.DoChannel(workers, producer)
}

// DoChannel executes multiple requests with the given number of workers and
// immediately returns a channel to receive the Responses as they become
// available. Excess requests are queued until a worker becomes available. The
// channel is closed once the reqs channel is closed and all responses have been sent.
//
// If zero is given as the worker count, one worker will be created.
//
// Each response is sent through the channel once the request is initiated via
// HTTP GET or an error has occurred, but before the file transfer begins.
//
// Any error which occurs during any of the file transfers will be set in the
// associated Response.Error field as soon as the Response.IsComplete method
// returns true.
func (c *Client) DoChannel(workers int, reqs <-chan *Request) <-chan *Response {
	// TODO: enable cancelling of batch jobs

	responses := make(chan *Response, workers)
	workerDone := make(chan bool, workers)

	// start work queue
	go func() {
		// close channel when all workers are finished
		for i := 0; i < workers; i++ {
			<-workerDone
		}
		close(responses)
	}()

	// default one worker
	if workers == 0 {
		workers = 1
	}

	// start workers
	for i := 0; i < workers; i++ {
		go func(i int) {
			// work until reqs is dried up
			for req := range reqs {
				// set up notifier
				req.notifyOnCloseInternal = make(chan *Response, 1)

				// start request
				resp := <-c.DoAsync(req)

				// ship state to caller
				responses <- resp

				// wait for async op to finish before moving to next request
				<-req.notifyOnCloseInternal
			}

			// signal worker is done
			workerDone <- true
		}(i)
	}

	return responses
}

// do creates a Response context for the given request using a HTTP HEAD
// request to the remote server.
func (c *Client) do(req *Request) (*Response, error) {
	// create a response
	resp := &Response{
		Request:    req,
		Start:      time.Now(),
		bufferSize: req.BufferSize,
	}

	// default to current working directory
	if req.Filename == "" {
		req.Filename = "."
	} else if req.SkipExisting {
		// skip existing files
		fi, err := os.Stat(req.Filename)
		if !os.IsNotExist(err) {
			if err != nil {
				return resp, resp.close(err)
			}

			if !fi.IsDir() {
				resp.Filename = req.Filename
				resp.DidResume = true
				resp.Size = uint64(fi.Size())
				resp.bytesResumed = uint64(fi.Size())
				return resp, resp.close(nil)
			}
		}
	}

	// default write flags
	wflags := os.O_CREATE | os.O_WRONLY

	// flag to resume previous download
	doResume := false

	// set user agent string
	if c.UserAgent != "" && req.HTTPRequest.Header.Get("User-Agent") == "" {
		req.HTTPRequest.Header.Set("User-Agent", c.UserAgent)
	}

	// request HEAD first
	methods := []string{"HEAD", req.HTTPRequest.Method}
	for _, method := range methods {
		// set request method
		req.HTTPRequest.Method = method

		// get response headers
		if hresp, err := c.HTTPClient.Do(req.HTTPRequest); err != nil {
			return resp, resp.close(err)

		} else if method != "HEAD" || (hresp.StatusCode >= 200 && hresp.StatusCode < 300) {
			// ignore non 2XX results for HEAD requests.
			// dont make assumptions about non 2XX statuses for GET requests -
			// let the caller make such decisions.

			// allow caller to see response if error occurs before final transfer
			resp.HTTPResponse = hresp

			// set response size
			if hresp.ContentLength > 0 {
				// If this is a GET request which resumes a previous transfer,
				// ContentLength will likely only be the size of the requested
				// byte range, not the full file size.
				resp.Size = resp.BytesTransferred() + uint64(hresp.ContentLength)
			}

			// check content length matches expected length
			if req.Size > 0 && hresp.ContentLength > 0 && req.Size != resp.Size {
				return resp, resp.close(newGrabError(errBadLength, "Bad content length in %s response: %d, expected %d", method, resp.Size, req.Size))
			}

			// compute destination filename
			if err := computeFilename(req, resp); err != nil {
				return resp, resp.close(err)
			}

			// TODO: skip FileInfo check if completed in HEAD

			// get fileinfo for destination
			if fi, err := os.Stat(resp.Filename); err == nil {
				// check if file transfer already complete
				if resp.Size > 0 && uint64(fi.Size()) == resp.Size {
					// update response
					resp.DidResume = true
					resp.bytesResumed = uint64(fi.Size())
					resp.bytesTransferred = uint64(fi.Size())

					// validate checksum
					if err := resp.checksum(); err != nil {
						return resp, resp.close(err)
					}

					return resp, resp.close(nil)
				}

				// check if existing file is larger than expected
				if uint64(fi.Size()) > resp.Size {
					return resp, resp.close(newGrabError(errBadLength, "Existing file (%d bytes) is larger than remote (%d bytes)", fi.Size(), resp.Size))
				}

				// check if resume is supported
				if method == "HEAD" && hresp.Header.Get("Accept-Ranges") == "bytes" {
					// resume previous download
					doResume = true

					// allow writer to append to existing file
					wflags = os.O_APPEND | os.O_WRONLY

					// update progress
					resp.bytesTransferred = uint64(fi.Size())

					// set byte range in next request
					if fi.Size() > 0 {
						resp.bytesResumed = uint64(fi.Size())
						req.HTTPRequest.Header.Set("Range", fmt.Sprintf("bytes=%d-", fi.Size()))
					}
				}
			} else if !os.IsNotExist(err) {
				// error in os.Stat
				return resp, resp.close(err)
			}
		}
	}

	// create destination directory
	if req.CreateMissing {
		dir := filepath.Dir(req.Filename)
		if dir != "" {
			if err := os.MkdirAll(dir, 0755); err != nil {
				return resp, resp.close(err)
			}
		}
	}

	// open destination for writing
	f, err := os.OpenFile(resp.Filename, wflags, 0644)
	if err != nil {
		return resp, resp.close(err)
	}
	resp.writer = f

	// seek to the start of the file
	if _, err := f.Seek(0, 0); err != nil {
		return resp, resp.close(err)
	}

	// seek to end if resuming previous download
	if doResume {
		if _, err = f.Seek(0, os.SEEK_END); err != nil {
			return resp, resp.close(err)
		}

		resp.DidResume = true
	}

	// response context is ready to start transfer with resp.copy()
	return resp, nil
}

// computeFilename determines the destination filename for a request and sets
// resp.Filename. If no name is determined, an error is returned which can be
// identified with IsNoFilename.
func computeFilename(req *Request, resp *Response) error {
	// return if destination path already set
	if resp.Filename != "" {
		return nil
	}

	// see if requested destination is a directory
	dir := ""
	if fi, err := os.Stat(req.Filename); err == nil {
		// file exists - is it a directory?
		if fi.IsDir() {
			// destination is a directory - compute a file name later
			dir = req.Filename
		} else {
			// destination is an existing file
			resp.Filename = req.Filename
		}
	} else if os.IsNotExist(err) {
		// file doesn't exist
		resp.Filename = req.Filename
	} else {
		// an error occurred
		return err
	}

	// get filename from Content-Disposition header
	if resp.Filename == "" {
		if cd := resp.HTTPResponse.Header.Get("Content-Disposition"); cd != "" {
			if _, params, err := mime.ParseMediaType(cd); err == nil {
				if filename, ok := params["filename"]; ok {
					resp.Filename = path.Join(dir, filename)
				}
			}
		}
	}

	// get filename from url if still needed
	if resp.Filename == "" {
		if req.HTTPRequest.URL.Path != "" && !strings.HasSuffix(req.HTTPRequest.URL.Path, "/") {
			// update filepath with filename from URL
			resp.Filename = filepath.Join(dir, path.Base(req.HTTPRequest.URL.Path))
		}
	}

	// too bad if no filename found yet
	if resp.Filename == "" {
		return newGrabError(errNoFilename, "No filename could be determined")
	}

	return nil
}
<commit_msg>use waitGroup instead of done channel<commit_after>package grab

import (
	"fmt"
	"mime"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

// A Client is a file download client.
//
// Clients are safe for concurrent use by multiple goroutines.
type Client struct {
	// HTTPClient specifies the http.Client which will be used for communicating
	// with the remote server during the file transfer.
	HTTPClient *http.Client

	// UserAgent specifies the User-Agent string which will be set in the
	// headers of all requests made by this client.
	//
	// The user agent string may be overridden in the headers of each request.
	UserAgent string
}

// NewClient returns a new file download Client, using default configuration.
func NewClient() *Client {
	return &Client{
		UserAgent: "grab",
		HTTPClient: &http.Client{
			Transport: &http.Transport{
				Proxy: http.ProxyFromEnvironment,
			},
		},
	}
}

// DefaultClient is the default client and is used by all Get convenience
// functions.
var DefaultClient = NewClient()

// CancelRequest cancels an in-flight request by closing its connection.
func (c *Client) CancelRequest(req *Request) {
	if t, ok := c.HTTPClient.Transport.(*http.Transport); ok {
		t.CancelRequest(req.HTTPRequest)
	}
}

// Do sends a file transfer request and returns a file transfer response
// context, following policy (e.g. redirects, cookies, auth) as configured on
// the client's HTTPClient.
//
// An error is returned if caused by client policy (such as CheckRedirect), or
// if there was an HTTP protocol error.
//
// Do is a synchronous, blocking operation which returns only once a download
// request is completed or fails. For non-blocking operations which enable the
// monitoring of transfers in process, see DoAsync and DoBatch.
func (c *Client) Do(req *Request) (*Response, error) {
	// prepare request with HEAD request
	resp, err := c.do(req)
	if err != nil {
		return resp, err
	}

	// transfer content
	if !resp.IsComplete() {
		if err := resp.copy(); err != nil {
			return resp, err
		}
	}

	return resp, nil
}

// DoAsync sends a file transfer request and returns a channel to receive the
// file transfer response context.
//
// The Response is sent via the returned channel and the channel closed as soon
// as the HTTP/1.1 GET request has been served; before the file transfer begins.
//
// The Response may then be used to monitor the progress of the file transfer
// while it is in process.
//
// Any error which occurs during the file transfer will be set in the returned
// Response.Error field as soon as the Response.IsComplete method returns true.
func (c *Client) DoAsync(req *Request) <-chan *Response {
	r := make(chan *Response, 1)
	go func() {
		// prepare request with HEAD request
		resp, err := c.do(req)
		if err == nil && !resp.IsComplete() {
			// transfer data in new goroutine
			go resp.copy()
		}

		r <- resp
		close(r)
	}()

	return r
}

// DoBatch executes multiple requests with the given number of workers and
// immediately returns a channel to receive the Responses as they become
// available. Excess requests are queued until a worker becomes available. The
// channel is closed once all responses have been sent.
//
// If zero is given as the worker count, one worker will be created for each
// given request and all requests will start at the same time.
//
// Each response is sent through the channel once the request is initiated via
// HTTP GET or an error has occurred, but before the file transfer begins.
//
// Any error which occurs during any of the file transfers will be set in the
// associated Response.Error field as soon as the Response.IsComplete method
// returns true.
func (c *Client) DoBatch(workers int, reqs ...*Request) <-chan *Response {
	// TODO: enable cancelling of batch jobs

	// start work queue
	producer := make(chan *Request, 0)
	go func() {
		// feed queue
		for i := 0; i < len(reqs); i++ {
			producer <- reqs[i]
		}
		close(producer)
	}()

	// default one worker per request
	if workers == 0 {
		workers = len(reqs)
	}

	return c.DoChannel(workers, producer)
}

// DoChannel executes multiple requests with the given number of workers and
// immediately returns a channel to receive the Responses as they become
// available. Excess requests are queued until a worker becomes available. The
// channel is closed once the reqs channel is closed and all responses have been sent.
//
// If zero is given as the worker count, one worker will be created.
//
// Each response is sent through the channel once the request is initiated via
// HTTP GET or an error has occurred, but before the file transfer begins.
//
// Any error which occurs during any of the file transfers will be set in the
// associated Response.Error field as soon as the Response.IsComplete method
// returns true.
func (c *Client) DoChannel(workers int, reqs <-chan *Request) <-chan *Response {
	// TODO: enable cancelling of batch jobs

	responses := make(chan *Response, workers)
	wg := sync.WaitGroup{}

	// default one worker
	if workers == 0 {
		workers = 1
	}

	// start workers
	for i := 0; i < workers; i++ {
		wg.Add(1)
		go func(i int) {
			// work until reqs is dried up
			for req := range reqs {
				// set up notifier
				req.notifyOnCloseInternal = make(chan *Response, 1)

				// start request
				resp := <-c.DoAsync(req)

				// ship state to caller
				responses <- resp

				// wait for async op to finish before moving to next request
				<-req.notifyOnCloseInternal
			}

			// signal worker is done
			wg.Done()
		}(i)
	}

	go func() {
		// close channel when all workers are finished
		wg.Wait()
		close(responses)
	}()

	return responses
}

// do creates a Response context for the given request using a HTTP HEAD
// request to the remote server.
func (c *Client) do(req *Request) (*Response, error) {
	// create a response
	resp := &Response{
		Request:    req,
		Start:      time.Now(),
		bufferSize: req.BufferSize,
	}

	// default to current working directory
	if req.Filename == "" {
		req.Filename = "."
	} else if req.SkipExisting {
		// skip existing files
		fi, err := os.Stat(req.Filename)
		if !os.IsNotExist(err) {
			if err != nil {
				return resp, resp.close(err)
			}

			if !fi.IsDir() {
				resp.Filename = req.Filename
				resp.DidResume = true
				resp.Size = uint64(fi.Size())
				resp.bytesResumed = uint64(fi.Size())
				return resp, resp.close(nil)
			}
		}
	}

	// default write flags
	wflags := os.O_CREATE | os.O_WRONLY

	// flag to resume previous download
	doResume := false

	// set user agent string
	if c.UserAgent != "" && req.HTTPRequest.Header.Get("User-Agent") == "" {
		req.HTTPRequest.Header.Set("User-Agent", c.UserAgent)
	}

	// request HEAD first
	methods := []string{"HEAD", req.HTTPRequest.Method}
	for _, method := range methods {
		// set request method
		req.HTTPRequest.Method = method

		// get response headers
		if hresp, err := c.HTTPClient.Do(req.HTTPRequest); err != nil {
			return resp, resp.close(err)

		} else if method != "HEAD" || (hresp.StatusCode >= 200 && hresp.StatusCode < 300) {
			// ignore non 2XX results for HEAD requests.
			// dont make assumptions about non 2XX statuses for GET requests -
			// let the caller make such decisions.

			// allow caller to see response if error occurs before final transfer
			resp.HTTPResponse = hresp

			// set response size
			if hresp.ContentLength > 0 {
				// If this is a GET request which resumes a previous transfer,
				// ContentLength will likely only be the size of the requested
				// byte range, not the full file size.
				resp.Size = resp.BytesTransferred() + uint64(hresp.ContentLength)
			}

			// check content length matches expected length
			if req.Size > 0 && hresp.ContentLength > 0 && req.Size != resp.Size {
				return resp, resp.close(newGrabError(errBadLength, "Bad content length in %s response: %d, expected %d", method, resp.Size, req.Size))
			}

			// compute destination filename
			if err := computeFilename(req, resp); err != nil {
				return resp, resp.close(err)
			}

			// TODO: skip FileInfo check if completed in HEAD

			// get fileinfo for destination
			if fi, err := os.Stat(resp.Filename); err == nil {
				// check if file transfer already complete
				if resp.Size > 0 && uint64(fi.Size()) == resp.Size {
					// update response
					resp.DidResume = true
					resp.bytesResumed = uint64(fi.Size())
					resp.bytesTransferred = uint64(fi.Size())

					// validate checksum
					if err := resp.checksum(); err != nil {
						return resp, resp.close(err)
					}

					return resp, resp.close(nil)
				}

				// check if existing file is larger than expected
				if uint64(fi.Size()) > resp.Size {
					return resp, resp.close(newGrabError(errBadLength, "Existing file (%d bytes) is larger than remote (%d bytes)", fi.Size(), resp.Size))
				}

				// check if resume is supported
				if method == "HEAD" && hresp.Header.Get("Accept-Ranges") == "bytes" {
					// resume previous download
					doResume = true

					// allow writer to append to existing file
					wflags = os.O_APPEND | os.O_WRONLY

					// update progress
					resp.bytesTransferred = uint64(fi.Size())

					// set byte range in next request
					if fi.Size() > 0 {
						resp.bytesResumed = uint64(fi.Size())
						req.HTTPRequest.Header.Set("Range", fmt.Sprintf("bytes=%d-", fi.Size()))
					}
				}
			} else if !os.IsNotExist(err) {
				// error in os.Stat
				return resp, resp.close(err)
			}
		}
	}

	// create destination directory
	if req.CreateMissing {
		dir := filepath.Dir(req.Filename)
		if dir != "" {
			if err := os.MkdirAll(dir, 0755); err != nil {
				return resp, resp.close(err)
			}
		}
	}

	// open destination for writing
	f, err := os.OpenFile(resp.Filename, wflags, 0644)
	if err != nil {
		return resp, resp.close(err)
	}
	resp.writer = f

	// seek to the start of the file
	if _, err := f.Seek(0, 0); err != nil {
		return resp, resp.close(err)
	}

	// seek to end if resuming previous download
	if doResume {
		if _, err = f.Seek(0, os.SEEK_END); err != nil {
			return resp, resp.close(err)
		}

		resp.DidResume = true
	}

	// response context is ready to start transfer with resp.copy()
	return resp, nil
}

// computeFilename determines the destination filename for a request and sets
// resp.Filename. If no name is determined, an error is returned which can be
// identified with IsNoFilename.
func computeFilename(req *Request, resp *Response) error {
	// return if destination path already set
	if resp.Filename != "" {
		return nil
	}

	// see if requested destination is a directory
	dir := ""
	if fi, err := os.Stat(req.Filename); err == nil {
		// file exists - is it a directory?
		if fi.IsDir() {
			// destination is a directory - compute a file name later
			dir = req.Filename
		} else {
			// destination is an existing file
			resp.Filename = req.Filename
		}
	} else if os.IsNotExist(err) {
		// file doesn't exist
		resp.Filename = req.Filename
	} else {
		// an error occurred
		return err
	}

	// get filename from Content-Disposition header
	if resp.Filename == "" {
		if cd := resp.HTTPResponse.Header.Get("Content-Disposition"); cd != "" {
			if _, params, err := mime.ParseMediaType(cd); err == nil {
				if filename, ok := params["filename"]; ok {
					resp.Filename = path.Join(dir, filename)
				}
			}
		}
	}

	// get filename from url if still needed
	if resp.Filename == "" {
		if req.HTTPRequest.URL.Path != "" && !strings.HasSuffix(req.HTTPRequest.URL.Path, "/") {
			// update filepath with filename from URL
			resp.Filename = filepath.Join(dir, path.Base(req.HTTPRequest.URL.Path))
		}
	}

	// too bad if no filename found yet
	if resp.Filename == "" {
		return newGrabError(errNoFilename, "No filename could be determined")
	}

	return nil
}
<|endoftext|>


<commit_before>package couchbase

import (
	"encoding/json"
	"errors"
	"fmt"
	"math/rand"
	"net/http"
	"net/url"
	"sync/atomic"
)

func (b *Bucket) do(k string, f func(mc *memcachedClient, vb uint16) error) error {
	vb := b.VBHash(k)
	for {
		masterId := b.VBucketServerMap.VBucketMap[vb][0]
		if b.connections[masterId] == nil {
			b.connections[masterId] = connect("tcp", b.VBucketServerMap.ServerList[masterId])
		}
		err := f(b.connections[masterId], uint16(vb))
		switch err.(type) {
		default:
			return err
		case mcResponse:
			st := err.(mcResponse).Status
			atomic.AddUint64(&b.pool.client.Statuses[st], 1)
			if st == mcNOT_MY_VBUCKET {
				b.refresh()
			} else {
				return err
			}
		}
	}
	panic("Unreachable.")
}

// Set a value in this bucket.
// The value will be serialized into a JSON document.
func (b *Bucket) Set(k string, v interface{}) error {
	return b.do(k, func(mc *memcachedClient, vb uint16) error {
		data, err := json.Marshal(v)
		if err != nil {
			return err
		}
		res := mc.Set(vb, k, 0, 0, data)
		if res.Status != mcSUCCESS {
			return res
		}
		return nil
	})
}

// Get a value from this bucket.
// The value is expected to be a JSON stream and will be deserialized
// into rv.
func (b *Bucket) Get(k string, rv interface{}) error {
	return b.do(k, func(mc *memcachedClient, vb uint16) error {
		res := mc.Get(vb, k)
		if res.Status != mcSUCCESS {
			return res
		}
		return json.Unmarshal(res.Body, rv)
	})
}

// Delete a key from this bucket.
func (b *Bucket) Delete(k string) error {
	return b.do(k, func(mc *memcachedClient, vb uint16) error {
		res := mc.Del(vb, k)
		if res.Status != mcSUCCESS {
			return res
		}
		return nil
	})
}

type ViewRow struct {
	ID    string
	Key   interface{}
	Value interface{}
	Doc   *interface{}
}

type ViewResult struct {
	TotalRows int `json:"total_rows"`
	Rows      []ViewRow
	Errors    []struct {
		From   string
		Reason string
	}
}

// Execute a view
func (b *Bucket) View(ddoc, name string, params map[string]interface{}) (vres ViewResult, err error) {
	// Pick a random node to service our request.
	node := b.Nodes[rand.Intn(len(b.Nodes))]
	u, err := url.Parse(node.CouchAPIBase)
	if err != nil {
		return ViewResult{}, err
	}

	values := url.Values{}
	for k, v := range params {
		values[k] = []string{fmt.Sprintf("%v", v)}
	}

	u.Path = fmt.Sprintf("/%s/_design/%s/_view/%s", b.Name, ddoc, name)
	u.RawQuery = values.Encode()

	res, err := http.Get(u.String())
	if err != nil {
		return ViewResult{}, err
	}
	defer res.Body.Close()

	if res.StatusCode != 200 {
		return ViewResult{}, errors.New(res.Status)
	}

	d := json.NewDecoder(res.Body)
	if err = d.Decode(&vres); err != nil {
		return ViewResult{}, err
	}
	return vres, nil

}
<commit_msg>Added a bit of package docs.<commit_after>/*
A smart client for go.

Usage:

 client, err := couchbase.Connect("http://myserver:8091/")
 handleError(err)
 pool, err := client.GetPool("default")
 handleError(err)
 bucket, err := pool.getBucket("MyAwesomeBucket")
 handleError(err)
 ...
*/
package couchbase

import (
	"encoding/json"
	"errors"
	"fmt"
	"math/rand"
	"net/http"
	"net/url"
	"sync/atomic"
)

func (b *Bucket) do(k string, f func(mc *memcachedClient, vb uint16) error) error {
	vb := b.VBHash(k)
	for {
		masterId := b.VBucketServerMap.VBucketMap[vb][0]
		if b.connections[masterId] == nil {
			b.connections[masterId] = connect("tcp", b.VBucketServerMap.ServerList[masterId])
		}
		err := f(b.connections[masterId], uint16(vb))
		switch err.(type) {
		default:
			return err
		case mcResponse:
			st := err.(mcResponse).Status
			atomic.AddUint64(&b.pool.client.Statuses[st], 1)
			if st == mcNOT_MY_VBUCKET {
				b.refresh()
			} else {
				return err
			}
		}
	}
	panic("Unreachable.")
}

// Set a value in this bucket.
// The value will be serialized into a JSON document.
func (b *Bucket) Set(k string, v interface{}) error {
	return b.do(k, func(mc *memcachedClient, vb uint16) error {
		data, err := json.Marshal(v)
		if err != nil {
			return err
		}
		res := mc.Set(vb, k, 0, 0, data)
		if res.Status != mcSUCCESS {
			return res
		}
		return nil
	})
}

// Get a value from this bucket.
// The value is expected to be a JSON stream and will be deserialized
// into rv.
func (b *Bucket) Get(k string, rv interface{}) error {
	return b.do(k, func(mc *memcachedClient, vb uint16) error {
		res := mc.Get(vb, k)
		if res.Status != mcSUCCESS {
			return res
		}
		return json.Unmarshal(res.Body, rv)
	})
}

// Delete a key from this bucket.
func (b *Bucket) Delete(k string) error {
	return b.do(k, func(mc *memcachedClient, vb uint16) error {
		res := mc.Del(vb, k)
		if res.Status != mcSUCCESS {
			return res
		}
		return nil
	})
}

type ViewRow struct {
	ID    string
	Key   interface{}
	Value interface{}
	Doc   *interface{}
}

type ViewResult struct {
	TotalRows int `json:"total_rows"`
	Rows      []ViewRow
	Errors    []struct {
		From   string
		Reason string
	}
}

// Execute a view
func (b *Bucket) View(ddoc, name string, params map[string]interface{}) (vres ViewResult, err error) {
	// Pick a random node to service our request.
	node := b.Nodes[rand.Intn(len(b.Nodes))]
	u, err := url.Parse(node.CouchAPIBase)
	if err != nil {
		return ViewResult{}, err
	}

	values := url.Values{}
	for k, v := range params {
		values[k] = []string{fmt.Sprintf("%v", v)}
	}

	u.Path = fmt.Sprintf("/%s/_design/%s/_view/%s", b.Name, ddoc, name)
	u.RawQuery = values.Encode()

	res, err := http.Get(u.String())
	if err != nil {
		return ViewResult{}, err
	}
	defer res.Body.Close()

	if res.StatusCode != 200 {
		return ViewResult{}, errors.New(res.Status)
	}

	d := json.NewDecoder(res.Body)
	if err = d.Decode(&vres); err != nil {
		return ViewResult{}, err
	}
	return vres, nil

}
<|endoftext|>


<commit_before>// Copyright 2015 Datajin Technologies, Inc. All rights reserved.
// Use of this source code is governed by an Artistic-2
// license that can be found in the LICENSE file.

// Package mktmpio provides easy access to the database servier provisioning
// services at https://mktmp.io/
package mktmpio

import (
	"bytes"
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"

	"github.com/docker/docker/pkg/stdcopy"
	"golang.org/x/net/websocket"
)

// go-1.2 does not automatically load SHA2-384, which is what parts of
// mktmp.io's cert chain use.
// See also: http://bridge.grumpy-troll.org/2014/05/golang-tls-comodo/
import _ "crypto/sha512" // side-effect only

// Client provides authenticated API access for creating, listing, and destorying
// database servers.
type Client struct {
	token     string
	url       string
	UserAgent string
	logger    *log.Logger
}

var devNull = log.New(ioutil.Discard, "", 0)

// NewClient creates a mktmpio Client using credentials loaded from the user
// config stored in ~/.mktmpio.yml
func NewClient(cfg *Config) (*Client, error) {
	client := &Client{
		token:     cfg.Token,
		url:       cfg.URL,
		UserAgent: "go-mktmpio",
	}
	if client.url == "" {
		client.url = MktmpioURL
	}
	return client, nil
}

// SetLogger sets the logger to be used for verbose logging of errors
func (c *Client) SetLogger(logger *log.Logger) {
	c.logger = logger
}

func (c Client) log() *log.Logger {
	if c.logger == nil {
		return devNull
	}
	return c.logger
}

// NewRequest creates an http.Request based on the Client's configuration. The
// created request object is suitable for passing to http.Client.Do()
func (c Client) newRequest(method, path string) (*http.Request, error) {
	req, err := http.NewRequest(method, c.url+path, nil)
	if req != nil {
		req.Header.Set("Accept", "application/json")
		req.Header.Set("User-Agent", c.UserAgent)
		req.Header.Set("X-Auth-Token", c.token)
	}
	return req, err
}

// Create creates a server of the type specified by `service`.
func (c Client) rawRequest(method, path string) ([]byte, error) {
	req, _ := c.newRequest(method, path)
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		c.log().Printf("req: %+v", req)
		return nil, err
	}
	defer resp.Body.Close()
	return ioutil.ReadAll(resp.Body)
}

// Create creates a server of the type specified by `service`.
func (c Client) jsonRequest(method, path string, instance interface{}) error {
	body, err := c.rawRequest(method, path)
	if err != nil || instance == nil {
		return err
	}
	err = json.Unmarshal(body, instance)
	if err != nil {
		c.log().Printf("res: %s", body)
		if bytes.HasPrefix(body, []byte(`{"error":`)) {
			err = errors.New(string(body[10 : len(body)-2]))
		} else {
			err = errors.New(err.Error() + string(body))
		}
	}
	return err
}

// Create creates a server of the type specified by `service`.
func (c Client) Create(service string) (*Instance, error) {
	instance := &Instance{client: c}
	reqURL := "/new/" + service
	if err := c.jsonRequest("POST", reqURL, instance); err != nil {
		return nil, err
	}
	if len(instance.Error) > 0 {
		return nil, errors.New(instance.Error)
	}
	return instance, nil
}

// List generates retrieves a list of curently running instances
func (c Client) List() ([]Instance, error) {
	reqURL := "/i"
	instances := []Instance{}
	err := c.jsonRequest("GET", reqURL, &instances)
	if err != nil {
		return nil, err
	}
	return instances, nil
}

// Destroy shuts down and deletes the server identified by `id`.
func (c Client) Destroy(id string) error {
	path := "/i/" + id
	return c.jsonRequest("DELETE", path, nil)
}

// AttachStdio creates a remote shell for the instance identified by `id` and
// returns an io.WriteCloser for that shell's stdin and an io.Reader for each of
// stdout and stderr on that shell. This is for non-interactive shells, like one
// would use for piping a script into a shell or for piping the output from.
func (c Client) AttachStdio(id string) (io.WriteCloser, io.Reader, io.Reader, error) {
	conn, err := c.attachWS(id, true)
	if err != nil {
		return nil, nil, nil, err
	}
	inReader, inWriter := io.Pipe()
	outReader, outWriter := io.Pipe()
	errReader, errWriter := io.Pipe()
	go func() {
		// stdcopy is Docker's demuxer for their stdout/stderr multiplexed stream
		stdcopy.StdCopy(outWriter, errWriter, conn)
		errWriter.Close()
		outWriter.Close()
	}()
	go func() {
		io.Copy(conn, inReader)
		// A cheap hack sentinel value to indicate EOF to the server without closing
		// the actual connection. This would be so much easier with plain TCP :-(
		conn.Write([]byte{255, 255, 255, 255})
	}()
	return inWriter, outReader, errReader, err
}

// Attach creates a remote shell for the instance identified by `id` and then
// returns a Reader and a Writer for communicating with it via a psuedo-TTY. The
// bytes read from the channel will include TTY control sequences. This type of
// connection is most appropriate for connecting directly to a local TTY.
func (c Client) Attach(id string) (io.ReadWriteCloser, error) {
	return c.attachWS(id, false)
}

func (c Client) attachWS(id string, stdio bool) (*websocket.Conn, error) {
	wsURL, err := url.Parse(c.url)
	if err != nil {
		c.log().Printf("error parsing url: %s: %s", c.url, err)
		return nil, err
	}
	if wsURL.Scheme == "https" {
		wsURL.Scheme = "wss"
		wsURL.Host = "mktmp.io:8443"
	} else {
		wsURL.Scheme = "ws"
	}
	wsURL.Path = "/ws"
	params := url.Values{}
	params.Set("id", id)
	if stdio {
		params.Set("stdio", "true")
	} else {
		params.Set("stdio", "false")
	}
	wsURL.RawQuery = params.Encode()
	cfg, err := websocket.NewConfig(wsURL.String(), "http://localhost/")
	if err != nil {
		c.log().Printf("error initializing websocket: %s: %s", wsURL, err)
		return nil, err
	}
	cfg.Header.Set("Accept", "application/json")
	cfg.Header.Set("User-Agent", "go-mktmpio")
	cfg.Header.Set("X-Auth-Token", c.token)
	conn, err := websocket.DialConfig(cfg)
	if err != nil {
		c.log().Printf("error dialing websocket: %+v: %s", cfg, err)
	} else {
		conn.PayloadType = websocket.BinaryFrame
	}
	return conn, err
}
<commit_msg>fix typos in comments<commit_after>// Copyright 2015 Datajin Technologies, Inc. All rights reserved.
// Use of this source code is governed by an Artistic-2
// license that can be found in the LICENSE file.

// Package mktmpio provides easy access to the database servier provisioning
// services at https://mktmp.io/
package mktmpio

import (
	"bytes"
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"

	"github.com/docker/docker/pkg/stdcopy"
	"golang.org/x/net/websocket"
)

// go-1.2 does not automatically load SHA2-384, which is what parts of
// mktmp.io's cert chain use.
// See also: http://bridge.grumpy-troll.org/2014/05/golang-tls-comodo/
import _ "crypto/sha512" // side-effect only

// Client provides authenticated API access for creating, listing, and destroying
// database servers.
type Client struct {
	token     string
	url       string
	UserAgent string
	logger    *log.Logger
}

var devNull = log.New(ioutil.Discard, "", 0)

// NewClient creates a mktmpio Client using credentials loaded from the user
// config stored in ~/.mktmpio.yml
func NewClient(cfg *Config) (*Client, error) {
	client := &Client{
		token:     cfg.Token,
		url:       cfg.URL,
		UserAgent: "go-mktmpio",
	}
	if client.url == "" {
		client.url = MktmpioURL
	}
	return client, nil
}

// SetLogger sets the logger to be used for verbose logging of errors
func (c *Client) SetLogger(logger *log.Logger) {
	c.logger = logger
}

func (c Client) log() *log.Logger {
	if c.logger == nil {
		return devNull
	}
	return c.logger
}

// NewRequest creates an http.Request based on the Client's configuration. The
// created request object is suitable for passing to http.Client.Do()
func (c Client) newRequest(method, path string) (*http.Request, error) {
	req, err := http.NewRequest(method, c.url+path, nil)
	if req != nil {
		req.Header.Set("Accept", "application/json")
		req.Header.Set("User-Agent", c.UserAgent)
		req.Header.Set("X-Auth-Token", c.token)
	}
	return req, err
}

// Create creates a server of the type specified by `service`.
func (c Client) rawRequest(method, path string) ([]byte, error) {
	req, _ := c.newRequest(method, path)
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		c.log().Printf("req: %+v", req)
		return nil, err
	}
	defer resp.Body.Close()
	return ioutil.ReadAll(resp.Body)
}

// Create creates a server of the type specified by `service`.
func (c Client) jsonRequest(method, path string, instance interface{}) error {
	body, err := c.rawRequest(method, path)
	if err != nil || instance == nil {
		return err
	}
	err = json.Unmarshal(body, instance)
	if err != nil {
		c.log().Printf("res: %s", body)
		if bytes.HasPrefix(body, []byte(`{"error":`)) {
			err = errors.New(string(body[10 : len(body)-2]))
		} else {
			err = errors.New(err.Error() + string(body))
		}
	}
	return err
}

// Create creates a server of the type specified by `service`.
func (c Client) Create(service string) (*Instance, error) {
	instance := &Instance{client: c}
	reqURL := "/new/" + service
	if err := c.jsonRequest("POST", reqURL, instance); err != nil {
		return nil, err
	}
	if len(instance.Error) > 0 {
		return nil, errors.New(instance.Error)
	}
	return instance, nil
}

// List generates retrieves a list of curently running instances
func (c Client) List() ([]Instance, error) {
	reqURL := "/i"
	instances := []Instance{}
	err := c.jsonRequest("GET", reqURL, &instances)
	if err != nil {
		return nil, err
	}
	return instances, nil
}

// Destroy shuts down and deletes the server identified by `id`.
func (c Client) Destroy(id string) error {
	path := "/i/" + id
	return c.jsonRequest("DELETE", path, nil)
}

// AttachStdio creates a remote shell for the instance identified by `id` and
// returns an io.WriteCloser for that shell's stdin and an io.Reader for each of
// stdout and stderr on that shell. This is for non-interactive shells, like one
// would use for piping a script into a shell or for piping the output from.
func (c Client) AttachStdio(id string) (io.WriteCloser, io.Reader, io.Reader, error) {
	conn, err := c.attachWS(id, true)
	if err != nil {
		return nil, nil, nil, err
	}
	inReader, inWriter := io.Pipe()
	outReader, outWriter := io.Pipe()
	errReader, errWriter := io.Pipe()
	go func() {
		// stdcopy is Docker's demuxer for their stdout/stderr multiplexed stream
		stdcopy.StdCopy(outWriter, errWriter, conn)
		errWriter.Close()
		outWriter.Close()
	}()
	go func() {
		io.Copy(conn, inReader)
		// A cheap hack sentinel value to indicate EOF to the server without closing
		// the actual connection. This would be so much easier with plain TCP :-(
		conn.Write([]byte{255, 255, 255, 255})
	}()
	return inWriter, outReader, errReader, err
}

// Attach creates a remote shell for the instance identified by `id` and then
// returns a Reader and a Writer for communicating with it via a pseudo-TTY. The
// bytes read from the channel will include TTY control sequences. This type of
// connection is most appropriate for connecting directly to a local TTY.
func (c Client) Attach(id string) (io.ReadWriteCloser, error) {
	return c.attachWS(id, false)
}

func (c Client) attachWS(id string, stdio bool) (*websocket.Conn, error) {
	wsURL, err := url.Parse(c.url)
	if err != nil {
		c.log().Printf("error parsing url: %s: %s", c.url, err)
		return nil, err
	}
	if wsURL.Scheme == "https" {
		wsURL.Scheme = "wss"
		wsURL.Host = "mktmp.io:8443"
	} else {
		wsURL.Scheme = "ws"
	}
	wsURL.Path = "/ws"
	params := url.Values{}
	params.Set("id", id)
	if stdio {
		params.Set("stdio", "true")
	} else {
		params.Set("stdio", "false")
	}
	wsURL.RawQuery = params.Encode()
	cfg, err := websocket.NewConfig(wsURL.String(), "http://localhost/")
	if err != nil {
		c.log().Printf("error initializing websocket: %s: %s", wsURL, err)
		return nil, err
	}
	cfg.Header.Set("Accept", "application/json")
	cfg.Header.Set("User-Agent", "go-mktmpio")
	cfg.Header.Set("X-Auth-Token", c.token)
	conn, err := websocket.DialConfig(cfg)
	if err != nil {
		c.log().Printf("error dialing websocket: %+v: %s", cfg, err)
	} else {
		conn.PayloadType = websocket.BinaryFrame
	}
	return conn, err
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/csv"
	"fmt"
	"reflect"
	"strings"

	"github.com/jmoiron/sqlx"
)

//Client is our SQL client
type Client struct {
	db      *sqlx.DB
	history []string
}

//Row will hold rows of our SQL table
type Row []interface{}

//Result will hold our SQL query resultset
type Result struct {
	Columns []string `json:"columns"`
	Rows    []Row    `json:"rows"`
}

//NewClient will create a new client
func NewClient() (*Client, error) {
	db, err := sqlx.Open("mysql", getConnectionString())

	if err != nil {
		return nil, err
	}

	return &Client{db: db}, nil
}

//NewClientFromURL will create a new mysql client using the URL provided in parameters
func NewClientFromURL(url string) (*Client, error) {
	fmt.Println(url)
	db, err := sqlx.Open("mysql", url)

	if err != nil {
		return nil, err
	}

	return &Client{db: db}, nil
}

//Test if we have a working connection with the database
func (client *Client) Test() error {
	return client.db.Ping()
}

func (client *Client) recordQuery(query string) {
	client.history = append(client.history, query)
}

//Info of our connected database
func (client *Client) Info() (*Result, error) {
	return client.Query(MySQLInfo)
}

//Databases will list all the databases in the system
func (client *Client) Databases() ([]string, error) {
	res, err := client.Query(MySQLDatabases)

	if err != nil {
		return nil, err
	}

	var tables []string

	for _, row := range res.Rows {
		tables = append(tables, row[0].(string))
	}

	return tables, nil
}

//DatabaseTables will give you list of tables belonging to the database
func (client *Client) DatabaseTables(database string) ([]string, error) {
	res, err := client.Query(fmt.Sprintf(MySQLDatabaseTables, database))

	if err != nil {
		return nil, err
	}

	var tables []string

	for _, row := range res.Rows {
		tables = append(tables, row[0].(string))
	}

	return tables, nil
}

//DatabaseProcedures returns a list of all the stored procedures in the database
func (client *Client) DatabaseProcedures(database string) ([]string, error) {
	res, err := client.Query(fmt.Sprintf(MySQLDatabaseProcedures, database))

	if err != nil {
		return nil, err
	}

	var tables []string

	for _, row := range res.Rows {
		tables = append(tables, row[0].(string))
	}

	return tables, nil
}

//DatabaseFunctions returns a list of all the functions in the database
func (client *Client) DatabaseFunctions(database string) ([]string, error) {
	res, err := client.Query(fmt.Sprintf(MySQLDatabaseFunctions, database))

	if err != nil {
		return nil, err
	}

	var tables []string

	for _, row := range res.Rows {
		tables = append(tables, row[0].(string))
	}

	return tables, nil
}

//Tables will return list of tables in test database
func (client *Client) Tables() ([]string, error) {
	res, err := client.Query(MySQLTables)

	if err != nil {
		return nil, err
	}

	var tables []string

	for _, row := range res.Rows {
		tables = append(tables, row[0].(string))
	}

	return tables, nil
}

//Table will return coulmns of a table
func (client *Client) Table(table string) (*Result, error) {
	return client.Query(fmt.Sprintf(MySQLTable, table))
}

//TableInfo will return info like data used, row count etc.
func (client *Client) TableInfo(table string) (*Result, error) {
	return client.Query(fmt.Sprintf(MySQLTableInfo, table))
}

//TableIndexes returns all the indexes of the table
func (client *Client) TableIndexes(table string) (*Result, error) {
	res, err := client.Query(fmt.Sprintf(MySQLTableIndexs, table))

	if err != nil {
		return nil, err
	}

	return res, err
}

//ProcedureParameters returns all the paramaters of a stored procedure
func (client *Client) ProcedureParameters(procedure string, database string) (*Result, error) {
	res, err := client.Query(fmt.Sprintf(MySQLProcedureParameters, procedure, database))

	if err != nil {
		return nil, err
	}

	return res, err
}

//DatabaseCollationCharSet returns all the collation and character sets in db
func (client *Client) DatabaseCollationCharSet() (*Result, error) {
	res, err := client.Query(MySQLAllCollationCharSet)

	if err != nil {
		return nil, err
	}

	return res, err
}

//AlterDatabase let's you set character set & collation of the database
func (client *Client) AlterDatabase(database string, charset string, collation string) (*Result, error) {
	res, err := client.Query(fmt.Sprintf(MySQLDatabaseAlter, database, charset, collation))

	if err != nil {
		return nil, err
	}

	return res, err
}

//DropDatabase will drop the database from the system
func (client *Client) DropDatabase(database string) (*Result, error) {
	res, err := client.Query(fmt.Sprintf(MySQLDatabaseDrop, database))

	if err != nil {
		return nil, err
	}

	return res, err
}

//DropTable will drop the table from selected database
func (client *Client) DropTable(database string, table string) (*Result, error) {
	res, err := client.Query(fmt.Sprintf(MySQLTableDrop, database, table))

	if err != nil {
		return nil, err
	}

	return res, err
}

//TruncateTable will truncate the table
func (client *Client) TruncateTable(database string, table string) (*Result, error) {
	res, err := client.Query(fmt.Sprintf(MySQLTableTruncate, database, table))

	if err != nil {
		return nil, err
	}

	return res, err
}

//ProcedureDefinition will give you the create statement of procedure/function
func (client *Client) ProcedureDefinition(procType string, database string, name string) (*Result, error) {
	res, err := client.Query(fmt.Sprintf(MySQLProcedureDefinition, procType, database, name))

	if err != nil {
		return nil, err
	}

	return res, err

}

func (client *Client) ProcedureDrop(procType string, database string, name string) (bool, error) {
	_, err := client.Execute(fmt.Sprintf(MySQLProcedureDrop, procType, database, name))

	if err != nil {
		return false, err
	}

	return true, err
}

func (client *Client) ProcedureCreate(procType string, database string, name string, definition string) (bool, error) {
	trans, err := client.db.Begin()

	if err != nil {
		return false, err
	}

	//set this as default database
	_, err = trans.Exec(fmt.Sprintf("use %s;", database))

	//Drop existing procedure
	_, err = trans.Exec(fmt.Sprintf(MySQLProcedureDrop, procType, database, name))

	if err != nil {
		return false, err
	}

	//Create new definition
	yoIndex := 10
	if procType == "FUNCTION" {
		yoIndex = 9
	}
	mehIndex := strings.Index(definition, procType+" `")
	newDef := splice(definition, mehIndex+yoIndex, 0, "`"+database+"`.")

	_, err = trans.Exec(newDef)

	if err != nil {
		return false, err
	}

	return true, nil
}

//Query will execute the sql query passed as parameter, and return the resultset
func (client *Client) Query(query string) (*Result, error) {
	rows, err := client.db.Queryx(query)

	client.recordQuery(query)

	if err != nil {
		return nil, err
	}

	defer rows.Close()

	cols, err := rows.Columns()

	if err != nil {
		return nil, err
	}

	result := Result{
		Columns: cols,
	}

	for rows.Next() {
		obj, err := rows.SliceScan()

		for i, item := range obj {
			if item == nil {
				obj[i] = nil
			} else {
				t := reflect.TypeOf(item).Kind().String()

				if t == "slice" {
					obj[i] = string(item.([]byte))
				}
			}
		}

		if err == nil {
			result.Rows = append(result.Rows, obj)
		}
	}

	return &result, nil
}

func (client *Client) Execute(query string) (int64, error) {
	res, err := client.db.Exec(query)

	if err != nil {
		return -1, err
	}

	rowsAffected, err := res.RowsAffected()
	if err != nil {
		return -1, err
	}

	return rowsAffected, nil
}

//Format the resultset
func (res *Result) Format() []map[string]interface{} {
	var items []map[string]interface{}

	for _, row := range res.Rows {
		item := make(map[string]interface{})

		for i, c := range res.Columns {
			item[c] = row[i]
		}

		items = append(items, item)
	}

	return items
}

//CSV will format the sql resultset as CSV
func (res *Result) CSV() []byte {
	buff := &bytes.Buffer{}
	writer := csv.NewWriter(buff)

	writer.Write(res.Columns)

	for _, row := range res.Rows {
		record := make([]string, len(res.Columns))

		for i, item := range row {
			if item != nil {
				record[i] = fmt.Sprintf("%v", item)
			} else {
				record[i] = ""
			}
		}

		err := writer.Write(record)

		if err != nil {
			fmt.Println(err)
			break
		}
	}

	writer.Flush()
	return buff.Bytes()
}
<commit_msg>Remove string processing to add database name with proc name. It seems redundant since we are already making the database default<commit_after>package main

import (
	"bytes"
	"encoding/csv"
	"fmt"
	"reflect"

	"github.com/jmoiron/sqlx"
)

//Client is our SQL client
type Client struct {
	db      *sqlx.DB
	history []string
}

//Row will hold rows of our SQL table
type Row []interface{}

//Result will hold our SQL query resultset
type Result struct {
	Columns []string `json:"columns"`
	Rows    []Row    `json:"rows"`
}

//NewClient will create a new client
func NewClient() (*Client, error) {
	db, err := sqlx.Open("mysql", getConnectionString())

	if err != nil {
		return nil, err
	}

	return &Client{db: db}, nil
}

//NewClientFromURL will create a new mysql client using the URL provided in parameters
func NewClientFromURL(url string) (*Client, error) {
	fmt.Println(url)
	db, err := sqlx.Open("mysql", url)

	if err != nil {
		return nil, err
	}

	return &Client{db: db}, nil
}

//Test if we have a working connection with the database
func (client *Client) Test() error {
	return client.db.Ping()
}

func (client *Client) recordQuery(query string) {
	client.history = append(client.history, query)
}

//Info of our connected database
func (client *Client) Info() (*Result, error) {
	return client.Query(MySQLInfo)
}

//Databases will list all the databases in the system
func (client *Client) Databases() ([]string, error) {
	res, err := client.Query(MySQLDatabases)

	if err != nil {
		return nil, err
	}

	var tables []string

	for _, row := range res.Rows {
		tables = append(tables, row[0].(string))
	}

	return tables, nil
}

//DatabaseTables will give you list of tables belonging to the database
func (client *Client) DatabaseTables(database string) ([]string, error) {
	res, err := client.Query(fmt.Sprintf(MySQLDatabaseTables, database))

	if err != nil {
		return nil, err
	}

	var tables []string

	for _, row := range res.Rows {
		tables = append(tables, row[0].(string))
	}

	return tables, nil
}

//DatabaseProcedures returns a list of all the stored procedures in the database
func (client *Client) DatabaseProcedures(database string) ([]string, error) {
	res, err := client.Query(fmt.Sprintf(MySQLDatabaseProcedures, database))

	if err != nil {
		return nil, err
	}

	var tables []string

	for _, row := range res.Rows {
		tables = append(tables, row[0].(string))
	}

	return tables, nil
}

//DatabaseFunctions returns a list of all the functions in the database
func (client *Client) DatabaseFunctions(database string) ([]string, error) {
	res, err := client.Query(fmt.Sprintf(MySQLDatabaseFunctions, database))

	if err != nil {
		return nil, err
	}

	var tables []string

	for _, row := range res.Rows {
		tables = append(tables, row[0].(string))
	}

	return tables, nil
}

//Tables will return list of tables in test database
func (client *Client) Tables() ([]string, error) {
	res, err := client.Query(MySQLTables)

	if err != nil {
		return nil, err
	}

	var tables []string

	for _, row := range res.Rows {
		tables = append(tables, row[0].(string))
	}

	return tables, nil
}

//Table will return coulmns of a table
func (client *Client) Table(table string) (*Result, error) {
	return client.Query(fmt.Sprintf(MySQLTable, table))
}

//TableInfo will return info like data used, row count etc.
func (client *Client) TableInfo(table string) (*Result, error) {
	return client.Query(fmt.Sprintf(MySQLTableInfo, table))
}

//TableIndexes returns all the indexes of the table
func (client *Client) TableIndexes(table string) (*Result, error) {
	res, err := client.Query(fmt.Sprintf(MySQLTableIndexs, table))

	if err != nil {
		return nil, err
	}

	return res, err
}

//ProcedureParameters returns all the paramaters of a stored procedure
func (client *Client) ProcedureParameters(procedure string, database string) (*Result, error) {
	res, err := client.Query(fmt.Sprintf(MySQLProcedureParameters, procedure, database))

	if err != nil {
		return nil, err
	}

	return res, err
}

//DatabaseCollationCharSet returns all the collation and character sets in db
func (client *Client) DatabaseCollationCharSet() (*Result, error) {
	res, err := client.Query(MySQLAllCollationCharSet)

	if err != nil {
		return nil, err
	}

	return res, err
}

//AlterDatabase let's you set character set & collation of the database
func (client *Client) AlterDatabase(database string, charset string, collation string) (*Result, error) {
	res, err := client.Query(fmt.Sprintf(MySQLDatabaseAlter, database, charset, collation))

	if err != nil {
		return nil, err
	}

	return res, err
}

//DropDatabase will drop the database from the system
func (client *Client) DropDatabase(database string) (*Result, error) {
	res, err := client.Query(fmt.Sprintf(MySQLDatabaseDrop, database))

	if err != nil {
		return nil, err
	}

	return res, err
}

//DropTable will drop the table from selected database
func (client *Client) DropTable(database string, table string) (*Result, error) {
	res, err := client.Query(fmt.Sprintf(MySQLTableDrop, database, table))

	if err != nil {
		return nil, err
	}

	return res, err
}

//TruncateTable will truncate the table
func (client *Client) TruncateTable(database string, table string) (*Result, error) {
	res, err := client.Query(fmt.Sprintf(MySQLTableTruncate, database, table))

	if err != nil {
		return nil, err
	}

	return res, err
}

//ProcedureDefinition will give you the create statement of procedure/function
func (client *Client) ProcedureDefinition(procType string, database string, name string) (*Result, error) {
	res, err := client.Query(fmt.Sprintf(MySQLProcedureDefinition, procType, database, name))

	if err != nil {
		return nil, err
	}

	return res, err

}

func (client *Client) ProcedureDrop(procType string, database string, name string) (bool, error) {
	_, err := client.Execute(fmt.Sprintf(MySQLProcedureDrop, procType, database, name))

	if err != nil {
		return false, err
	}

	return true, err
}

func (client *Client) ProcedureCreate(procType string, database string, name string, definition string) (bool, error) {
	trans, err := client.db.Begin()

	if err != nil {
		return false, err
	}

	//set this as default database
	_, err = trans.Exec(fmt.Sprintf("use %s;", database))

	//Drop existing procedure
	_, err = trans.Exec(fmt.Sprintf(MySQLProcedureDrop, procType, database, name))

	if err != nil {
		return false, err
	}

	//Create new definition
	//yoIndex := 10
	//if procType == "FUNCTION" {
	//	yoIndex = 9
	//}
	//mehIndex := strings.Index(definition, procType+" `")
	//newDef := splice(definition, mehIndex+yoIndex, 0, "`"+database+"`.")

	_, err = trans.Exec(definition)

	if err != nil {
		return false, err
	}

	return true, nil
}

//Query will execute the sql query passed as parameter, and return the resultset
func (client *Client) Query(query string) (*Result, error) {
	rows, err := client.db.Queryx(query)

	client.recordQuery(query)

	if err != nil {
		return nil, err
	}

	defer rows.Close()

	cols, err := rows.Columns()

	if err != nil {
		return nil, err
	}

	result := Result{
		Columns: cols,
	}

	for rows.Next() {
		obj, err := rows.SliceScan()

		for i, item := range obj {
			if item == nil {
				obj[i] = nil
			} else {
				t := reflect.TypeOf(item).Kind().String()

				if t == "slice" {
					obj[i] = string(item.([]byte))
				}
			}
		}

		if err == nil {
			result.Rows = append(result.Rows, obj)
		}
	}

	return &result, nil
}

func (client *Client) Execute(query string) (int64, error) {
	res, err := client.db.Exec(query)

	if err != nil {
		return -1, err
	}

	rowsAffected, err := res.RowsAffected()
	if err != nil {
		return -1, err
	}

	return rowsAffected, nil
}

//Format the resultset
func (res *Result) Format() []map[string]interface{} {
	var items []map[string]interface{}

	for _, row := range res.Rows {
		item := make(map[string]interface{})

		for i, c := range res.Columns {
			item[c] = row[i]
		}

		items = append(items, item)
	}

	return items
}

//CSV will format the sql resultset as CSV
func (res *Result) CSV() []byte {
	buff := &bytes.Buffer{}
	writer := csv.NewWriter(buff)

	writer.Write(res.Columns)

	for _, row := range res.Rows {
		record := make([]string, len(res.Columns))

		for i, item := range row {
			if item != nil {
				record[i] = fmt.Sprintf("%v", item)
			} else {
				record[i] = ""
			}
		}

		err := writer.Write(record)

		if err != nil {
			fmt.Println(err)
			break
		}
	}

	writer.Flush()
	return buff.Bytes()
}
<|endoftext|>


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

package brotli

import "math"

// TODO(dsnet): Almost all of this logic is identical to compress/flate.
// Centralize common logic to compress/internal/prefix.

// The algorithm used to decode variable length codes is based on the lookup
// method in zlib. If the code is less-than-or-equal to prefixMaxChunkBits,
// then the symbol can be decoded using a single lookup into the chunks table.
// Otherwise, the links table will be used for a second level lookup.
//
// The chunks slice is keyed by the contents of the bit buffer ANDed with
// the chunkMask to avoid a out-of-bounds lookup. The value of chunks is a tuple
// that is decoded as follow:
//
//	var length = chunks[bitBuffer&chunkMask] & prefixCountMask
//	var symbol = chunks[bitBuffer&chunkMask] >> prefixCountBits
//
// If the decoded length is larger than chunkBits, then an overflow link table
// must be used for further decoding. In this case, the symbol is actually the
// index into the links tables. The second-level links table returned is
// processed in the same way as the chunks table.
//
//	if length > chunkBits {
//		var index = symbol // Previous symbol is index into links tables
//		length = links[index][bitBuffer>>chunkBits & linkMask] & prefixCountMask
//		symbol = links[index][bitBuffer>>chunkBits & linkMask] >> prefixCountBits
//	}
//
// See the following:
//	http://www.gzip.org/algorithm.txt

const (
	// These values add up to the width of a uint16 integer.
	prefixCountBits  = 4  // Number of bits to store the bit-width of the code
	prefixSymbolBits = 12 // Number of bits to store the symbol value

	prefixCountMask    = (1 << prefixCountBits) - 1
	prefixMaxChunkBits = 9 // This can be tuned for better performance
)

type prefixDecoder struct {
	chunks    []uint16   // First-level lookup map
	links     [][]uint16 // Second-level lookup map
	chunkMask uint16     // Mask the width of the chunks table
	linkMask  uint16     // Mask the width of the link table
	numSyms   uint16     // Number of symbols
	chunkBits uint8      // Bit-width of the chunks table
	minBits   uint8      // The minimum number of bits to safely make progress
}

// Init initializes prefixDecoder according to the codes provided.
// The symbols provided must be unique and in ascending order.
//
// If assignCodes is true, then generate a canonical prefix tree using the
// prefixCode.len field and assign the generated value to prefixCode.val.
//
// If assignCodes is false, then initialize using the information inside the
// codes themselves. The input codes must form a valid prefix tree.
func (pd *prefixDecoder) Init(codes []prefixCode, assignCodes bool) {
	// Handle special case trees.
	if len(codes) <= 1 {
		switch {
		case len(codes) == 0: // Empty tree (should error if used later)
			*pd = prefixDecoder{chunks: pd.chunks[:0], links: pd.links[:0], numSyms: 0}
		case len(codes) == 1: // Single code tree (bit-width of zero)
			*pd = prefixDecoder{
				chunks:  append(pd.chunks[:0], codes[0].sym<<prefixCountBits|0),
				links:   pd.links[:0],
				numSyms: 1,
			}
		}
		return
	}

	// Compute basic statistics on the symbols.
	var bitCnts [maxPrefixBits + 1]uint
	var minBits, maxBits uint8 = math.MaxUint8, 0
	symLast := -1
	for _, c := range codes {
		if int(c.sym) <= symLast {
			panic(ErrCorrupt) // Non-unique or non-monotonically increasing
		}
		if minBits > c.len {
			minBits = c.len
		}
		if maxBits < c.len {
			maxBits = c.len
		}
		bitCnts[c.len]++     // Histogram of bit counts
		symLast = int(c.sym) // Keep track of last symbol
	}
	if maxBits >= 1<<prefixCountBits || minBits == 0 {
		panic(ErrCorrupt) // Bit-width is too long or too short
	}
	if symLast >= 1<<prefixSymbolBits {
		panic(ErrCorrupt) // Alphabet cardinality too large
	}

	// Compute the next code for a symbol of a given bit length.
	var nextCodes [maxPrefixBits + 1]uint
	var code uint
	for i := minBits; i <= maxBits; i++ {
		code <<= 1
		nextCodes[i] = code
		code += bitCnts[i]
	}
	if code != 1<<maxBits {
		panic(ErrCorrupt) // Tree is under or over subscribed
	}

	// Allocate chunks table if necessary.
	pd.numSyms = uint16(len(codes))
	pd.minBits = minBits
	pd.chunkBits = maxBits
	if pd.chunkBits > prefixMaxChunkBits {
		pd.chunkBits = prefixMaxChunkBits
	}
	numChunks := 1 << pd.chunkBits
	pd.chunks = extendUint16s(pd.chunks, numChunks)
	pd.chunkMask = uint16(numChunks - 1)

	// Allocate links tables if necessary.
	pd.links = pd.links[:0]
	pd.linkMask = 0
	if pd.chunkBits < maxBits {
		numLinks := 1 << (maxBits - pd.chunkBits)
		pd.linkMask = uint16(numLinks - 1)

		if assignCodes {
			baseCode := nextCodes[pd.chunkBits+1] >> 1
			pd.links = extendSliceUints16s(pd.links, numChunks-int(baseCode))
			for linkIdx := range pd.links {
				code := reverseBits(uint16(baseCode)+uint16(linkIdx), uint(pd.chunkBits))
				pd.links[linkIdx] = extendUint16s(pd.links[linkIdx], numLinks)
				pd.chunks[code] = uint16(linkIdx<<prefixCountBits) | uint16(pd.chunkBits+1)
			}
		} else {
			for i := range pd.chunks {
				pd.chunks[i] = 0 // Logic below relies zero value as uninitialized
			}
			for _, c := range codes {
				if c.len <= pd.chunkBits {
					continue // Ignore symbols that don't require links
				}
				code := c.val & pd.chunkMask
				if pd.chunks[code] > 0 {
					continue // Link table already initialized
				}
				linkIdx := len(pd.links)
				pd.links = extendSliceUints16s(pd.links, len(pd.links)+1)
				pd.links[linkIdx] = extendUint16s(pd.links[linkIdx], numLinks)
				pd.chunks[code] = uint16(linkIdx<<prefixCountBits) | uint16(pd.chunkBits+1)
			}
		}
	}

	// Fill out chunks and links tables with values.
	for i, c := range codes {
		chunk := c.sym<<prefixCountBits | uint16(c.len)
		if assignCodes {
			codes[i].val = reverseBits(uint16(nextCodes[c.len]), uint(c.len))
			nextCodes[c.len]++
			c = codes[i]
		}

		if c.len <= pd.chunkBits {
			skip := 1 << uint(c.len)
			for j := int(c.val); j < len(pd.chunks); j += skip {
				pd.chunks[j] = chunk
			}
		} else {
			linkIdx := pd.chunks[c.val&pd.chunkMask] >> prefixCountBits
			links := pd.links[linkIdx]
			skip := 1 << uint(c.len-pd.chunkBits)
			for j := int(c.val >> pd.chunkBits); j < len(links); j += skip {
				links[j] = chunk
			}
		}
	}

	if debug && !checkPrefixes(codes) {
		panic(ErrCorrupt) // The codes do not form a valid prefix tree.
	}
}

// checkPrefixes reports whether any codes have overlapping prefixes.
// This check is expensive and runs in O(n^2) time!
func checkPrefixes(codes []prefixCode) bool {
	for i, c1 := range codes {
		for j, c2 := range codes {
			mask := uint16(1)<<c1.len - 1
			if i != j && c1.len <= c2.len && c1.val&mask == c2.val&mask {
				return false
			}
		}
	}
	return true
}

func extendUint16s(s []uint16, n int) []uint16 {
	if cap(s) >= n {
		return s[:n]
	}
	return append(s[:cap(s)], make([]uint16, n-cap(s))...)
}

func extendSliceUints16s(s [][]uint16, n int) [][]uint16 {
	if cap(s) >= n {
		return s[:n]
	}
	return append(s[:cap(s)], make([][]uint16, n-cap(s))...)
}
<commit_msg>brotli: remove dependency on math package<commit_after>// Copyright 2015, Joe Tsai. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.

package brotli

// TODO(dsnet): Almost all of this logic is identical to compress/flate.
// Centralize common logic to compress/internal/prefix.

// The algorithm used to decode variable length codes is based on the lookup
// method in zlib. If the code is less-than-or-equal to prefixMaxChunkBits,
// then the symbol can be decoded using a single lookup into the chunks table.
// Otherwise, the links table will be used for a second level lookup.
//
// The chunks slice is keyed by the contents of the bit buffer ANDed with
// the chunkMask to avoid a out-of-bounds lookup. The value of chunks is a tuple
// that is decoded as follow:
//
//	var length = chunks[bitBuffer&chunkMask] & prefixCountMask
//	var symbol = chunks[bitBuffer&chunkMask] >> prefixCountBits
//
// If the decoded length is larger than chunkBits, then an overflow link table
// must be used for further decoding. In this case, the symbol is actually the
// index into the links tables. The second-level links table returned is
// processed in the same way as the chunks table.
//
//	if length > chunkBits {
//		var index = symbol // Previous symbol is index into links tables
//		length = links[index][bitBuffer>>chunkBits & linkMask] & prefixCountMask
//		symbol = links[index][bitBuffer>>chunkBits & linkMask] >> prefixCountBits
//	}
//
// See the following:
//	http://www.gzip.org/algorithm.txt

const (
	// These values add up to the width of a uint16 integer.
	prefixCountBits  = 4  // Number of bits to store the bit-width of the code
	prefixSymbolBits = 12 // Number of bits to store the symbol value

	prefixCountMask    = (1 << prefixCountBits) - 1
	prefixMaxChunkBits = 9 // This can be tuned for better performance
)

type prefixDecoder struct {
	chunks    []uint16   // First-level lookup map
	links     [][]uint16 // Second-level lookup map
	chunkMask uint16     // Mask the width of the chunks table
	linkMask  uint16     // Mask the width of the link table
	numSyms   uint16     // Number of symbols
	chunkBits uint8      // Bit-width of the chunks table
	minBits   uint8      // The minimum number of bits to safely make progress
}

// Init initializes prefixDecoder according to the codes provided.
// The symbols provided must be unique and in ascending order.
//
// If assignCodes is true, then generate a canonical prefix tree using the
// prefixCode.len field and assign the generated value to prefixCode.val.
//
// If assignCodes is false, then initialize using the information inside the
// codes themselves. The input codes must form a valid prefix tree.
func (pd *prefixDecoder) Init(codes []prefixCode, assignCodes bool) {
	// Handle special case trees.
	if len(codes) <= 1 {
		switch {
		case len(codes) == 0: // Empty tree (should error if used later)
			*pd = prefixDecoder{chunks: pd.chunks[:0], links: pd.links[:0], numSyms: 0}
		case len(codes) == 1: // Single code tree (bit-width of zero)
			*pd = prefixDecoder{
				chunks:  append(pd.chunks[:0], codes[0].sym<<prefixCountBits|0),
				links:   pd.links[:0],
				numSyms: 1,
			}
		}
		return
	}

	// Compute basic statistics on the symbols.
	var bitCnts [maxPrefixBits + 1]uint
	c0 := codes[0]
	bitCnts[c0.len]++
	minBits, maxBits, symLast := c0.len, c0.len, int(c0.sym)
	for _, c := range codes[1:] {
		if int(c.sym) <= symLast {
			panic(ErrCorrupt) // Non-unique or non-monotonically increasing
		}
		if minBits > c.len {
			minBits = c.len
		}
		if maxBits < c.len {
			maxBits = c.len
		}
		bitCnts[c.len]++     // Histogram of bit counts
		symLast = int(c.sym) // Keep track of last symbol
	}
	if maxBits >= 1<<prefixCountBits || minBits == 0 {
		panic(ErrCorrupt) // Bit-width is too long or too short
	}
	if symLast >= 1<<prefixSymbolBits {
		panic(ErrCorrupt) // Alphabet cardinality too large
	}

	// Compute the next code for a symbol of a given bit length.
	var nextCodes [maxPrefixBits + 1]uint
	var code uint
	for i := minBits; i <= maxBits; i++ {
		code <<= 1
		nextCodes[i] = code
		code += bitCnts[i]
	}
	if code != 1<<maxBits {
		panic(ErrCorrupt) // Tree is under or over subscribed
	}

	// Allocate chunks table if necessary.
	pd.numSyms = uint16(len(codes))
	pd.minBits = minBits
	pd.chunkBits = maxBits
	if pd.chunkBits > prefixMaxChunkBits {
		pd.chunkBits = prefixMaxChunkBits
	}
	numChunks := 1 << pd.chunkBits
	pd.chunks = extendUint16s(pd.chunks, numChunks)
	pd.chunkMask = uint16(numChunks - 1)

	// Allocate links tables if necessary.
	pd.links = pd.links[:0]
	pd.linkMask = 0
	if pd.chunkBits < maxBits {
		numLinks := 1 << (maxBits - pd.chunkBits)
		pd.linkMask = uint16(numLinks - 1)

		if assignCodes {
			baseCode := nextCodes[pd.chunkBits+1] >> 1
			pd.links = extendSliceUints16s(pd.links, numChunks-int(baseCode))
			for linkIdx := range pd.links {
				code := reverseBits(uint16(baseCode)+uint16(linkIdx), uint(pd.chunkBits))
				pd.links[linkIdx] = extendUint16s(pd.links[linkIdx], numLinks)
				pd.chunks[code] = uint16(linkIdx<<prefixCountBits) | uint16(pd.chunkBits+1)
			}
		} else {
			for i := range pd.chunks {
				pd.chunks[i] = 0 // Logic below relies zero value as uninitialized
			}
			for _, c := range codes {
				if c.len <= pd.chunkBits {
					continue // Ignore symbols that don't require links
				}
				code := c.val & pd.chunkMask
				if pd.chunks[code] > 0 {
					continue // Link table already initialized
				}
				linkIdx := len(pd.links)
				pd.links = extendSliceUints16s(pd.links, len(pd.links)+1)
				pd.links[linkIdx] = extendUint16s(pd.links[linkIdx], numLinks)
				pd.chunks[code] = uint16(linkIdx<<prefixCountBits) | uint16(pd.chunkBits+1)
			}
		}
	}

	// Fill out chunks and links tables with values.
	for i, c := range codes {
		chunk := c.sym<<prefixCountBits | uint16(c.len)
		if assignCodes {
			codes[i].val = reverseBits(uint16(nextCodes[c.len]), uint(c.len))
			nextCodes[c.len]++
			c = codes[i]
		}

		if c.len <= pd.chunkBits {
			skip := 1 << uint(c.len)
			for j := int(c.val); j < len(pd.chunks); j += skip {
				pd.chunks[j] = chunk
			}
		} else {
			linkIdx := pd.chunks[c.val&pd.chunkMask] >> prefixCountBits
			links := pd.links[linkIdx]
			skip := 1 << uint(c.len-pd.chunkBits)
			for j := int(c.val >> pd.chunkBits); j < len(links); j += skip {
				links[j] = chunk
			}
		}
	}

	if debug && !checkPrefixes(codes) {
		panic(ErrCorrupt) // The codes do not form a valid prefix tree.
	}
}

// checkPrefixes reports whether any codes have overlapping prefixes.
// This check is expensive and runs in O(n^2) time!
func checkPrefixes(codes []prefixCode) bool {
	for i, c1 := range codes {
		for j, c2 := range codes {
			mask := uint16(1)<<c1.len - 1
			if i != j && c1.len <= c2.len && c1.val&mask == c2.val&mask {
				return false
			}
		}
	}
	return true
}

func extendUint16s(s []uint16, n int) []uint16 {
	if cap(s) >= n {
		return s[:n]
	}
	return append(s[:cap(s)], make([]uint16, n-cap(s))...)
}

func extendSliceUints16s(s [][]uint16, n int) [][]uint16 {
	if cap(s) >= n {
		return s[:n]
	}
	return append(s[:cap(s)], make([][]uint16, n-cap(s))...)
}
<|endoftext|>


<commit_before>package cmd

import (
	"os"
	"path/filepath"

	"github.com/gsamokovarov/jump/cli"
	"github.com/gsamokovarov/jump/config"
	"github.com/gsamokovarov/jump/scoring"
)

func cdCmd(args cli.Args, conf *config.Config) {
	term := args.CommandName()
	entries, err := conf.ReadEntries()

	if err != nil {
		cli.Exitf(1, "%s\n", err)
	}

	// If an auto-completion triggered a full path, just go there.
	if filepath.IsAbs(term) {
		cli.Outf("%s\n", term)
		return
	}

	index, search := 0, conf.ReadSearch()

	// If we happen to match the last term, e.g. j is called with no
	// arguments then jump to the previous search.
	if len(term) == 0 {
		term, index = search.Term, search.Index+1
	}

	fuzzyEntries := scoring.NewFuzzyEntries(entries, term)
	for {
		if entry, empty := fuzzyEntries.Select(index); !empty {
			// Remove the entries that no longer exists.
			if _, err := os.Stat(entry.Path); os.IsNotExist(err) {
				entries.Remove(entry.Path)
				conf.WriteEntries(entries)

				index += 1
				continue
			}

			// Jump to the next entry, if the jump is going to land on the
			// current directory.
			if cwd, err := os.Getwd(); err == nil && entry.Path == cwd {
				index += 1
				continue
			}

			cli.Outf("%s\n", entry.Path)
			conf.WriteSearch(term, index)
		}

		break
	}
}

func init() {
	cli.RegisterCommand("cd", "Fuzzy match a directory to jump to.", cdCmd)
}
<commit_msg>Touch on a make lint report<commit_after>package cmd

import (
	"os"
	"path/filepath"

	"github.com/gsamokovarov/jump/cli"
	"github.com/gsamokovarov/jump/config"
	"github.com/gsamokovarov/jump/scoring"
)

func cdCmd(args cli.Args, conf *config.Config) {
	term := args.CommandName()
	entries, err := conf.ReadEntries()

	if err != nil {
		cli.Exitf(1, "%s\n", err)
	}

	// If an auto-completion triggered a full path, just go there.
	if filepath.IsAbs(term) {
		cli.Outf("%s\n", term)
		return
	}

	index, search := 0, conf.ReadSearch()

	// If we happen to match the last term, e.g. j is called with no
	// arguments then jump to the previous search.
	if len(term) == 0 {
		term, index = search.Term, search.Index+1
	}

	fuzzyEntries := scoring.NewFuzzyEntries(entries, term)
	for {
		if entry, empty := fuzzyEntries.Select(index); !empty {
			// Remove the entries that no longer exists.
			if _, err := os.Stat(entry.Path); os.IsNotExist(err) {
				entries.Remove(entry.Path)
				conf.WriteEntries(entries)

				index++
				continue
			}

			// Jump to the next entry, if the jump is going to land on the
			// current directory.
			if cwd, err := os.Getwd(); err == nil && entry.Path == cwd {
				index++
				continue
			}

			cli.Outf("%s\n", entry.Path)
			conf.WriteSearch(term, index)
		}

		break
	}
}

func init() {
	cli.RegisterCommand("cd", "Fuzzy match a directory to jump to.", cdCmd)
}
<|endoftext|>


<commit_before>package git

/*
#include <git2.h>

extern int _go_git_treewalk(git_tree *tree, git_treewalk_mode mode, void *ptr);
*/
import "C"

import (
	"runtime"
)

// Commit
type Commit struct {
	gitObject
	cast_ptr *C.git_commit
}

func (c Commit) Message() string {
	return C.GoString(C.git_commit_message(c.cast_ptr))
}

func (c Commit) Tree() (*Tree, error) {
	var ptr *C.git_tree

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	err := C.git_commit_tree(&ptr, c.cast_ptr)
	if err < 0 {
		return nil, MakeGitError(err)
	}

	return allocObject((*C.git_object)(ptr), c.repo).(*Tree), nil
}

func (c Commit) TreeId() *Oid {
	return newOidFromC(C.git_commit_tree_id(c.cast_ptr))
}

func (c Commit) Author() *Signature {
	cast_ptr := C.git_commit_author(c.cast_ptr)
	return newSignatureFromC(cast_ptr)
}

func (c Commit) Committer() *Signature {
	cast_ptr := C.git_commit_committer(c.cast_ptr)
	return newSignatureFromC(cast_ptr)
}

func (c *Commit) Parent(n uint) *Commit {
	var cobj *C.git_commit
	ret := C.git_commit_parent(&cobj, c.cast_ptr, C.uint(n))
	if ret != 0 {
		return nil
	}

	return allocObject((*C.git_object)(cobj), c.repo).(*Commit)
}

func (c *Commit) ParentId(n uint) *Oid {
	return newOidFromC(C.git_commit_parent_id(c.cast_ptr, C.uint(n)))
}

func (c *Commit) ParentCount() uint {
	return uint(C.git_commit_parentcount(c.cast_ptr))
}
<commit_msg>Add commit summary getter<commit_after>package git

/*
#include <git2.h>

extern int _go_git_treewalk(git_tree *tree, git_treewalk_mode mode, void *ptr);
*/
import "C"

import (
	"runtime"
)

// Commit
type Commit struct {
	gitObject
	cast_ptr *C.git_commit
}

func (c Commit) Message() string {
	return C.GoString(C.git_commit_message(c.cast_ptr))
}

func (c Commit) Summary() string {
	return C.GoString(C.git_commit_summary(c.cast_ptr))
}

func (c Commit) Tree() (*Tree, error) {
	var ptr *C.git_tree

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	err := C.git_commit_tree(&ptr, c.cast_ptr)
	if err < 0 {
		return nil, MakeGitError(err)
	}

	return allocObject((*C.git_object)(ptr), c.repo).(*Tree), nil
}

func (c Commit) TreeId() *Oid {
	return newOidFromC(C.git_commit_tree_id(c.cast_ptr))
}

func (c Commit) Author() *Signature {
	cast_ptr := C.git_commit_author(c.cast_ptr)
	return newSignatureFromC(cast_ptr)
}

func (c Commit) Committer() *Signature {
	cast_ptr := C.git_commit_committer(c.cast_ptr)
	return newSignatureFromC(cast_ptr)
}

func (c *Commit) Parent(n uint) *Commit {
	var cobj *C.git_commit
	ret := C.git_commit_parent(&cobj, c.cast_ptr, C.uint(n))
	if ret != 0 {
		return nil
	}

	return allocObject((*C.git_object)(cobj), c.repo).(*Commit)
}

func (c *Commit) ParentId(n uint) *Oid {
	return newOidFromC(C.git_commit_parent_id(c.cast_ptr, C.uint(n)))
}

func (c *Commit) ParentCount() uint {
	return uint(C.git_commit_parentcount(c.cast_ptr))
}
<|endoftext|>