<commit_before>package sftp

import (
	"encoding"
	"io"
	"sync"

	"github.com/pkg/errors"
)

// conn implements a bidirectional channel on which client and server
// connections are multiplexed.
type conn struct {
	io.Reader
	io.WriteCloser
	// this is the same allocator used in packet manager
	alloc      *allocator
	sync.Mutex // used to serialise writes to sendPacket
	// sendPacketTest is needed to replicate packet issues in testing
	sendPacketTest func(w io.Writer, m encoding.BinaryMarshaler) error
}

// the orderID is used in server mode if the allocator is enabled.
// For the client mode just pass 0
func (c *conn) recvPacket(orderID uint32) (uint8, []byte, error) {
	return recvPacket(c, c.alloc, orderID)
}

func (c *conn) sendPacket(m encoding.BinaryMarshaler) error {
	c.Lock()
	defer c.Unlock()
	if c.sendPacketTest != nil {
		return c.sendPacketTest(c, m)
	}
	return sendPacket(c, m)
}

func (c *conn) Close() error {
	c.Lock()
	defer c.Unlock()
	return c.WriteCloser.Close()
}

type clientConn struct {
	conn
	wg sync.WaitGroup

	sync.Mutex                          // protects inflight
	inflight   map[uint32]chan<- result // outstanding requests

	closed chan struct{}
	err    error
}

// Wait blocks until the conn has shut down, and return the error
// causing the shutdown. It can be called concurrently from multiple
// goroutines.
func (c *clientConn) Wait() error {
	<-c.closed
	return c.err
}

// Close closes the SFTP session.
func (c *clientConn) Close() error {
	defer c.wg.Wait()
	return c.conn.Close()
}

func (c *clientConn) loop() {
	defer c.wg.Done()
	err := c.recv()
	if err != nil {
		c.broadcastErr(err)
	}
}

// recv continuously reads from the server and forwards responses to the
// appropriate channel.
func (c *clientConn) recv() error {
	defer c.conn.Close()

	for {
		typ, data, err := c.recvPacket(0)
		if err != nil {
			return err
		}
		sid, _ := unmarshalUint32(data)

		ch, ok := c.getChannel(sid)
		if !ok {
			// This is an unexpected occurrence. Send the error
			// back to all listeners so that they terminate
			// gracefully.
			return errors.Errorf("sid: %v not fond", sid)
		}

		ch <- result{typ: typ, data: data}
	}
}

func (c *clientConn) putChannel(ch chan<- result, sid uint32) bool {
	c.Lock()
	defer c.Unlock()

	select {
	case <-c.closed:
		// already closed with broadcastErr, return error on chan.
		ch <- result{err: ErrSSHFxConnectionLost}
		return false
	default:
	}

	c.inflight[sid] = ch
	return true
}

func (c *clientConn) getChannel(sid uint32) (chan<- result, bool) {
	c.Lock()
	defer c.Unlock()

	ch, ok := c.inflight[sid]
	delete(c.inflight, sid)

	return ch, ok
}

// result captures the result of receiving the a packet from the server
type result struct {
	typ  byte
	data []byte
	err  error
}

type idmarshaler interface {
	id() uint32
	encoding.BinaryMarshaler
}

func (c *clientConn) sendPacket(ch chan result, p idmarshaler) (byte, []byte, error) {
	if cap(ch) < 1 {
		ch = make(chan result, 1)
	}

	c.dispatchRequest(ch, p)
	s := <-ch
	return s.typ, s.data, s.err
}

// dispatchRequest should ideally only be called by race-detection tests outside of this file,
// where you have to ensure two packets are in flight sequentially after each other.
func (c *clientConn) dispatchRequest(ch chan<- result, p idmarshaler) {
	sid := p.id()

	if !c.putChannel(ch, sid) {
		// already closed.
		return
	}

	if err := c.conn.sendPacket(p); err != nil {
		if ch, ok := c.getChannel(sid); ok {
			ch <- result{err: err}
		}
	}
}

// broadcastErr sends an error to all goroutines waiting for a response.
func (c *clientConn) broadcastErr(err error) {
	c.Lock()
	defer c.Unlock()

	bcastRes := result{err: ErrSSHFxConnectionLost}
	for sid, ch := range c.inflight {
		ch <- bcastRes

		// Replace the chan in inflight,
		// we have hijacked this chan,
		// and this guarantees always-only-once sending.
		c.inflight[sid] = make(chan<- result, 1)
	}

	c.err = err
	close(c.closed)
}

type serverConn struct {
	conn
}

func (s *serverConn) sendError(p ider, err error) error {
	return s.sendPacket(statusFromError(p, err))
}
<commit_msg>fix typo, error message is now two-way greppable<commit_after>package sftp

import (
	"encoding"
	"io"
	"sync"

	"github.com/pkg/errors"
)

// conn implements a bidirectional channel on which client and server
// connections are multiplexed.
type conn struct {
	io.Reader
	io.WriteCloser
	// this is the same allocator used in packet manager
	alloc      *allocator
	sync.Mutex // used to serialise writes to sendPacket
	// sendPacketTest is needed to replicate packet issues in testing
	sendPacketTest func(w io.Writer, m encoding.BinaryMarshaler) error
}

// the orderID is used in server mode if the allocator is enabled.
// For the client mode just pass 0
func (c *conn) recvPacket(orderID uint32) (uint8, []byte, error) {
	return recvPacket(c, c.alloc, orderID)
}

func (c *conn) sendPacket(m encoding.BinaryMarshaler) error {
	c.Lock()
	defer c.Unlock()
	if c.sendPacketTest != nil {
		return c.sendPacketTest(c, m)
	}
	return sendPacket(c, m)
}

func (c *conn) Close() error {
	c.Lock()
	defer c.Unlock()
	return c.WriteCloser.Close()
}

type clientConn struct {
	conn
	wg sync.WaitGroup

	sync.Mutex                          // protects inflight
	inflight   map[uint32]chan<- result // outstanding requests

	closed chan struct{}
	err    error
}

// Wait blocks until the conn has shut down, and return the error
// causing the shutdown. It can be called concurrently from multiple
// goroutines.
func (c *clientConn) Wait() error {
	<-c.closed
	return c.err
}

// Close closes the SFTP session.
func (c *clientConn) Close() error {
	defer c.wg.Wait()
	return c.conn.Close()
}

func (c *clientConn) loop() {
	defer c.wg.Done()
	err := c.recv()
	if err != nil {
		c.broadcastErr(err)
	}
}

// recv continuously reads from the server and forwards responses to the
// appropriate channel.
func (c *clientConn) recv() error {
	defer c.conn.Close()

	for {
		typ, data, err := c.recvPacket(0)
		if err != nil {
			return err
		}
		sid, _ := unmarshalUint32(data)

		ch, ok := c.getChannel(sid)
		if !ok {
			// This is an unexpected occurrence. Send the error
			// back to all listeners so that they terminate
			// gracefully.
			return errors.Errorf("sid not found: %v", sid)
		}

		ch <- result{typ: typ, data: data}
	}
}

func (c *clientConn) putChannel(ch chan<- result, sid uint32) bool {
	c.Lock()
	defer c.Unlock()

	select {
	case <-c.closed:
		// already closed with broadcastErr, return error on chan.
		ch <- result{err: ErrSSHFxConnectionLost}
		return false
	default:
	}

	c.inflight[sid] = ch
	return true
}

func (c *clientConn) getChannel(sid uint32) (chan<- result, bool) {
	c.Lock()
	defer c.Unlock()

	ch, ok := c.inflight[sid]
	delete(c.inflight, sid)

	return ch, ok
}

// result captures the result of receiving the a packet from the server
type result struct {
	typ  byte
	data []byte
	err  error
}

type idmarshaler interface {
	id() uint32
	encoding.BinaryMarshaler
}

func (c *clientConn) sendPacket(ch chan result, p idmarshaler) (byte, []byte, error) {
	if cap(ch) < 1 {
		ch = make(chan result, 1)
	}

	c.dispatchRequest(ch, p)
	s := <-ch
	return s.typ, s.data, s.err
}

// dispatchRequest should ideally only be called by race-detection tests outside of this file,
// where you have to ensure two packets are in flight sequentially after each other.
func (c *clientConn) dispatchRequest(ch chan<- result, p idmarshaler) {
	sid := p.id()

	if !c.putChannel(ch, sid) {
		// already closed.
		return
	}

	if err := c.conn.sendPacket(p); err != nil {
		if ch, ok := c.getChannel(sid); ok {
			ch <- result{err: err}
		}
	}
}

// broadcastErr sends an error to all goroutines waiting for a response.
func (c *clientConn) broadcastErr(err error) {
	c.Lock()
	defer c.Unlock()

	bcastRes := result{err: ErrSSHFxConnectionLost}
	for sid, ch := range c.inflight {
		ch <- bcastRes

		// Replace the chan in inflight,
		// we have hijacked this chan,
		// and this guarantees always-only-once sending.
		c.inflight[sid] = make(chan<- result, 1)
	}

	c.err = err
	close(c.closed)
}

type serverConn struct {
	conn
}

func (s *serverConn) sendError(p ider, err error) error {
	return s.sendPacket(statusFromError(p, err))
}
<|endoftext|>


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

package ldap

import (
	"crypto/tls"
	"errors"
	"fmt"
	"log"
	"net"
	"sync"

	"gopkg.in/asn1-ber.v1"
)

const (
	MessageQuit     = 0
	MessageRequest  = 1
	MessageResponse = 2
	MessageFinish   = 3
)

type messagePacket struct {
	Op        int
	MessageID int64
	Packet    *ber.Packet
	Channel   chan *ber.Packet
}

type sendMessageFlags uint

const (
	startTLS sendMessageFlags = 1 << iota
)

// Conn represents an LDAP Connection
type Conn struct {
	conn                net.Conn
	isTLS               bool
	isClosing           bool
	isStartingTLS       bool
	Debug               debugging
	chanConfirm         chan bool
	chanResults         map[int64]chan *ber.Packet
	chanMessage         chan *messagePacket
	chanMessageID       chan int64
	wgSender            sync.WaitGroup
	wgClose             sync.WaitGroup
	once                sync.Once
	outstandingRequests uint
	messageMutex        sync.Mutex
}

// Dial connects to the given address on the given network using net.Dial
// and then returns a new Conn for the connection.
func Dial(network, addr string) (*Conn, error) {
	c, err := net.Dial(network, addr)
	if err != nil {
		return nil, NewError(ErrorNetwork, err)
	}
	conn := NewConn(c, false)
	conn.Start()
	return conn, nil
}

// DialTLS connects to the given address on the given network using tls.Dial
// and then returns a new Conn for the connection.
func DialTLS(network, addr string, config *tls.Config) (*Conn, error) {
	c, err := tls.Dial(network, addr, config)
	if err != nil {
		return nil, NewError(ErrorNetwork, err)
	}
	conn := NewConn(c, true)
	conn.Start()
	return conn, nil
}

// NewConn returns a new Conn using conn for network I/O.
func NewConn(conn net.Conn, isTLS bool) *Conn {
	return &Conn{
		conn:          conn,
		chanConfirm:   make(chan bool),
		chanMessageID: make(chan int64),
		chanMessage:   make(chan *messagePacket, 10),
		chanResults:   map[int64]chan *ber.Packet{},
		isTLS:         isTLS,
	}
}

func (l *Conn) Start() {
	go l.reader()
	go l.processMessages()
	l.wgClose.Add(1)
}

// Close closes the connection.
func (l *Conn) Close() {
	l.once.Do(func() {
		l.isClosing = true
		l.wgSender.Wait()

		l.Debug.Printf("Sending quit message and waiting for confirmation")
		l.chanMessage <- &messagePacket{Op: MessageQuit}
		<-l.chanConfirm
		close(l.chanMessage)

		l.Debug.Printf("Closing network connection")
		if err := l.conn.Close(); err != nil {
			log.Print(err)
		}

		l.wgClose.Done()
	})
	l.wgClose.Wait()
}

// Returns the next available messageID
func (l *Conn) nextMessageID() int64 {
	if l.chanMessageID != nil {
		if messageID, ok := <-l.chanMessageID; ok {
			return messageID
		}
	}
	return 0
}

// StartTLS sends the command to start a TLS session and then creates a new TLS Client
func (l *Conn) StartTLS(config *tls.Config) error {
	messageID := l.nextMessageID()

	if l.isTLS {
		return NewError(ErrorNetwork, errors.New("ldap: already encrypted"))
	}

	packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
	packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
	request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Start TLS")
	request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, "1.3.6.1.4.1.1466.20037", "TLS Extended Command"))
	packet.AppendChild(request)
	l.Debug.PrintPacket(packet)

	channel, err := l.sendMessageWithFlags(packet, startTLS)
	if err != nil {
		return err
	}
	if channel == nil {
		return NewError(ErrorNetwork, errors.New("ldap: could not send message"))
	}

	l.Debug.Printf("%d: waiting for response", messageID)
	packet = <-channel
	l.Debug.Printf("%d: got response %p", messageID, packet)
	l.finishMessage(messageID)

	if l.Debug {
		if err := addLDAPDescriptions(packet); err != nil {
			l.Close()
			return err
		}
		ber.PrintPacket(packet)
	}

	if packet.Children[1].Children[0].Value.(int64) == 0 {
		conn := tls.Client(l.conn, config)

		if err := conn.Handshake(); err != nil {
			l.Close()
			return NewError(ErrorNetwork, fmt.Errorf("TLS handshake failed (%v)", err))
		}

		l.isTLS = true
		l.conn = conn
	}
	go l.reader()

	return nil
}

func (l *Conn) sendMessage(packet *ber.Packet) (chan *ber.Packet, error) {
	return l.sendMessageWithFlags(packet, 0)
}

func (l *Conn) sendMessageWithFlags(packet *ber.Packet, flags sendMessageFlags) (chan *ber.Packet, error) {
	if l.isClosing {
		return nil, NewError(ErrorNetwork, errors.New("ldap: connection closed"))
	}
	l.messageMutex.Lock()
	l.Debug.Printf("flags&startTLS = %d", flags&startTLS)
	if l.isStartingTLS {
		l.messageMutex.Unlock()
		return nil, NewError(ErrorNetwork, errors.New("ldap: connection is in startls phase."))
	}
	if flags&startTLS != 0 {
		if l.outstandingRequests != 0 {
			l.messageMutex.Unlock()
			return nil, NewError(ErrorNetwork, errors.New("ldap: cannot StartTLS with outstanding requests"))
		} else {
			l.isStartingTLS = true
		}
	}
	l.outstandingRequests++

	l.messageMutex.Unlock()

	out := make(chan *ber.Packet)
	message := &messagePacket{
		Op:        MessageRequest,
		MessageID: packet.Children[0].Value.(int64),
		Packet:    packet,
		Channel:   out,
	}
	l.sendProcessMessage(message)
	return out, nil
}

func (l *Conn) finishMessage(messageID int64) {
	if l.isClosing {
		return
	}

	l.messageMutex.Lock()
	l.outstandingRequests--
	if l.isStartingTLS {
		l.isStartingTLS = false
	}
	l.messageMutex.Unlock()

	message := &messagePacket{
		Op:        MessageFinish,
		MessageID: messageID,
	}
	l.sendProcessMessage(message)
}

func (l *Conn) sendProcessMessage(message *messagePacket) bool {
	if l.isClosing {
		return false
	}
	l.wgSender.Add(1)
	l.chanMessage <- message
	l.wgSender.Done()
	return true
}

func (l *Conn) processMessages() {
	defer func() {
		if err := recover(); err != nil {
			log.Printf("ldap: recovered panic in processMessages: %v", err)
		}
		for messageID, channel := range l.chanResults {
			l.Debug.Printf("Closing channel for MessageID %d", messageID)
			close(channel)
			delete(l.chanResults, messageID)
		}
		close(l.chanMessageID)
		l.chanConfirm <- true
		close(l.chanConfirm)
	}()

	var messageID int64 = 1
	for {
		select {
		case l.chanMessageID <- messageID:
			messageID++
		case messagePacket, ok := <-l.chanMessage:
			if !ok {
				l.Debug.Printf("Shutting down - message channel is closed")
				return
			}
			switch messagePacket.Op {
			case MessageQuit:
				l.Debug.Printf("Shutting down - quit message received")
				return
			case MessageRequest:
				// Add to message list and write to network
				l.Debug.Printf("Sending message %d", messagePacket.MessageID)
				l.chanResults[messagePacket.MessageID] = messagePacket.Channel
				// go routine
				buf := messagePacket.Packet.Bytes()

				_, err := l.conn.Write(buf)
				if err != nil {
					l.Debug.Printf("Error Sending Message: %s", err.Error())
					break
				}
			case MessageResponse:
				l.Debug.Printf("Receiving message %d", messagePacket.MessageID)
				if chanResult, ok := l.chanResults[messagePacket.MessageID]; ok {
					chanResult <- messagePacket.Packet
				} else {
					log.Printf("Received unexpected message %d", messagePacket.MessageID)
					ber.PrintPacket(messagePacket.Packet)
				}
			case MessageFinish:
				// Remove from message list
				l.Debug.Printf("Finished message %d", messagePacket.MessageID)
				close(l.chanResults[messagePacket.MessageID])
				delete(l.chanResults, messagePacket.MessageID)
			}
		}
	}
}

func (l *Conn) reader() {
	cleanstop := false
	defer func() {
		if err := recover(); err != nil {
			log.Printf("ldap: recovered panic in reader: %v", err)
		}
		if !cleanstop {
			l.Close()
		}
	}()

	for {
		if cleanstop {
			l.Debug.Printf("reader clean stopping (without closing the connection)")
			return
		}
		packet, err := ber.ReadPacket(l.conn)
		if err != nil {
			// A read error is expected here if we are closing the connection...
			if !l.isClosing {
				l.Debug.Printf("reader error: %s", err.Error())
			}
			return
		}
		addLDAPDescriptions(packet)
		if len(packet.Children) == 0 {
			l.Debug.Printf("Received bad ldap packet")
			continue
		}
		l.messageMutex.Lock()
		if l.isStartingTLS {
			cleanstop = true
		}
		l.messageMutex.Unlock()
		message := &messagePacket{
			Op:        MessageResponse,
			MessageID: packet.Children[0].Value.(int64),
			Packet:    packet,
		}
		if !l.sendProcessMessage(message) {
			return
		}

	}
}
<commit_msg>Allow timeouts to be set for the ldap dial and handle the handshake with tls.client that supports timeouts rather than DialTLS.  Currently using flags to set the timeouts to not cause a breaking change in the function params.<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ldap

import (
	"crypto/tls"
	"errors"
	"fmt"
	"log"
	"net"
	"sync"
	"time"
	"gopkg.in/asn1-ber.v1"
	"flag"
)

const (
	MessageQuit     = 0
	MessageRequest  = 1
	MessageResponse = 2
	MessageFinish   = 3
)

type messagePacket struct {
	Op        int
	MessageID int64
	Packet    *ber.Packet
	Channel   chan *ber.Packet
}

type sendMessageFlags uint

const (
	startTLS sendMessageFlags = 1 << iota
)

// Conn represents an LDAP Connection
type Conn struct {
	conn                net.Conn
	isTLS               bool
	isClosing           bool
	isStartingTLS       bool
	Debug               debugging
	chanConfirm         chan bool
	chanResults         map[int64]chan *ber.Packet
	chanMessage         chan *messagePacket
	chanMessageID       chan int64
	wgSender            sync.WaitGroup
	wgClose             sync.WaitGroup
	once                sync.Once
	outstandingRequests uint
	messageMutex        sync.Mutex
}

var ldapTimeout time.Duration

func init(){
	flag.DurationVar(&ldapTimeout, "ldapConnectionTimeout", 60 * time.Second, "Default connection timeout for ldap")
	flag.Parse()
}

// Dial connects to the given address on the given network using net.Dial
// and then returns a new Conn for the connection.
func Dial(network, addr string) (*Conn, error) {
	c, err := net.DialTimeout(network, addr, ldapTimeout)
	if err != nil {
		return nil, NewError(ErrorNetwork, err)
	}
	conn := NewConn(c, false)
	conn.Start()
	return conn, nil
}

// DialTLS connects to the given address on the given network using tls.Dial
// and then returns a new Conn for the connection.
func DialTLS(network, addr string, config *tls.Config) (*Conn, error) {
	dc, err := net.DialTimeout(network, addr, ldapTimeout)
	if err != nil{
		return nil, err
	}
	c := tls.Client(dc, config)
	err = c.Handshake()
	if err != nil{
		return nil, err
	}
	conn := NewConn(c, true)
	conn.Start()
	return conn, nil
}

// NewConn returns a new Conn using conn for network I/O.
func NewConn(conn net.Conn, isTLS bool) *Conn {
	return &Conn{
		conn:          conn,
		chanConfirm:   make(chan bool),
		chanMessageID: make(chan int64),
		chanMessage:   make(chan *messagePacket, 10),
		chanResults:   map[int64]chan *ber.Packet{},
		isTLS:         isTLS,
	}
}

func (l *Conn) Start() {
	go l.reader()
	go l.processMessages()
	l.wgClose.Add(1)
}

// Close closes the connection.
func (l *Conn) Close() {
	l.once.Do(func() {
		l.isClosing = true
		l.wgSender.Wait()

		l.Debug.Printf("Sending quit message and waiting for confirmation")
		l.chanMessage <- &messagePacket{Op: MessageQuit}
		<-l.chanConfirm
		close(l.chanMessage)

		l.Debug.Printf("Closing network connection")
		if err := l.conn.Close(); err != nil {
			log.Print(err)
		}

		l.wgClose.Done()
	})
	l.wgClose.Wait()
}

// Returns the next available messageID
func (l *Conn) nextMessageID() int64 {
	if l.chanMessageID != nil {
		if messageID, ok := <-l.chanMessageID; ok {
			return messageID
		}
	}
	return 0
}

// StartTLS sends the command to start a TLS session and then creates a new TLS Client
func (l *Conn) StartTLS(config *tls.Config) error {
	messageID := l.nextMessageID()

	if l.isTLS {
		return NewError(ErrorNetwork, errors.New("ldap: already encrypted"))
	}

	packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
	packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
	request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Start TLS")
	request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, "1.3.6.1.4.1.1466.20037", "TLS Extended Command"))
	packet.AppendChild(request)
	l.Debug.PrintPacket(packet)

	channel, err := l.sendMessageWithFlags(packet, startTLS)
	if err != nil {
		return err
	}
	if channel == nil {
		return NewError(ErrorNetwork, errors.New("ldap: could not send message"))
	}

	l.Debug.Printf("%d: waiting for response", messageID)
	packet = <-channel
	l.Debug.Printf("%d: got response %p", messageID, packet)
	l.finishMessage(messageID)

	if l.Debug {
		if err := addLDAPDescriptions(packet); err != nil {
			l.Close()
			return err
		}
		ber.PrintPacket(packet)
	}

	if packet.Children[1].Children[0].Value.(int64) == 0 {
		conn := tls.Client(l.conn, config)

		if err := conn.Handshake(); err != nil {
			l.Close()
			return NewError(ErrorNetwork, fmt.Errorf("TLS handshake failed (%v)", err))
		}

		l.isTLS = true
		l.conn = conn
	}
	go l.reader()

	return nil
}

func (l *Conn) sendMessage(packet *ber.Packet) (chan *ber.Packet, error) {
	return l.sendMessageWithFlags(packet, 0)
}

func (l *Conn) sendMessageWithFlags(packet *ber.Packet, flags sendMessageFlags) (chan *ber.Packet, error) {
	if l.isClosing {
		return nil, NewError(ErrorNetwork, errors.New("ldap: connection closed"))
	}
	l.messageMutex.Lock()
	l.Debug.Printf("flags&startTLS = %d", flags&startTLS)
	if l.isStartingTLS {
		l.messageMutex.Unlock()
		return nil, NewError(ErrorNetwork, errors.New("ldap: connection is in startls phase."))
	}
	if flags&startTLS != 0 {
		if l.outstandingRequests != 0 {
			l.messageMutex.Unlock()
			return nil, NewError(ErrorNetwork, errors.New("ldap: cannot StartTLS with outstanding requests"))
		} else {
			l.isStartingTLS = true
		}
	}
	l.outstandingRequests++

	l.messageMutex.Unlock()

	out := make(chan *ber.Packet)
	message := &messagePacket{
		Op:        MessageRequest,
		MessageID: packet.Children[0].Value.(int64),
		Packet:    packet,
		Channel:   out,
	}
	l.sendProcessMessage(message)
	return out, nil
}

func (l *Conn) finishMessage(messageID int64) {
	if l.isClosing {
		return
	}

	l.messageMutex.Lock()
	l.outstandingRequests--
	if l.isStartingTLS {
		l.isStartingTLS = false
	}
	l.messageMutex.Unlock()

	message := &messagePacket{
		Op:        MessageFinish,
		MessageID: messageID,
	}
	l.sendProcessMessage(message)
}

func (l *Conn) sendProcessMessage(message *messagePacket) bool {
	if l.isClosing {
		return false
	}
	l.wgSender.Add(1)
	l.chanMessage <- message
	l.wgSender.Done()
	return true
}

func (l *Conn) processMessages() {
	defer func() {
		if err := recover(); err != nil {
			log.Printf("ldap: recovered panic in processMessages: %v", err)
		}
		for messageID, channel := range l.chanResults {
			l.Debug.Printf("Closing channel for MessageID %d", messageID)
			close(channel)
			delete(l.chanResults, messageID)
		}
		close(l.chanMessageID)
		l.chanConfirm <- true
		close(l.chanConfirm)
	}()

	var messageID int64 = 1
	for {
		select {
		case l.chanMessageID <- messageID:
			messageID++
		case messagePacket, ok := <-l.chanMessage:
			if !ok {
				l.Debug.Printf("Shutting down - message channel is closed")
				return
			}
			switch messagePacket.Op {
			case MessageQuit:
				l.Debug.Printf("Shutting down - quit message received")
				return
			case MessageRequest:
				// Add to message list and write to network
				l.Debug.Printf("Sending message %d", messagePacket.MessageID)
				l.chanResults[messagePacket.MessageID] = messagePacket.Channel
				// go routine
				buf := messagePacket.Packet.Bytes()

				_, err := l.conn.Write(buf)
				if err != nil {
					l.Debug.Printf("Error Sending Message: %s", err.Error())
					break
				}
			case MessageResponse:
				l.Debug.Printf("Receiving message %d", messagePacket.MessageID)
				if chanResult, ok := l.chanResults[messagePacket.MessageID]; ok {
					chanResult <- messagePacket.Packet
				} else {
					log.Printf("Received unexpected message %d", messagePacket.MessageID)
					ber.PrintPacket(messagePacket.Packet)
				}
			case MessageFinish:
				// Remove from message list
				l.Debug.Printf("Finished message %d", messagePacket.MessageID)
				close(l.chanResults[messagePacket.MessageID])
				delete(l.chanResults, messagePacket.MessageID)
			}
		}
	}
}

func (l *Conn) reader() {
	cleanstop := false
	defer func() {
		if err := recover(); err != nil {
			log.Printf("ldap: recovered panic in reader: %v", err)
		}
		if !cleanstop {
			l.Close()
		}
	}()

	for {
		if cleanstop {
			l.Debug.Printf("reader clean stopping (without closing the connection)")
			return
		}
		packet, err := ber.ReadPacket(l.conn)
		if err != nil {
			// A read error is expected here if we are closing the connection...
			if !l.isClosing {
				l.Debug.Printf("reader error: %s", err.Error())
			}
			return
		}
		addLDAPDescriptions(packet)
		if len(packet.Children) == 0 {
			l.Debug.Printf("Received bad ldap packet")
			continue
		}
		l.messageMutex.Lock()
		if l.isStartingTLS {
			cleanstop = true
		}
		l.messageMutex.Unlock()
		message := &messagePacket{
			Op:        MessageResponse,
			MessageID: packet.Children[0].Value.(int64),
			Packet:    packet,
		}
		if !l.sendProcessMessage(message) {
			return
		}

	}
}
<|endoftext|>


<commit_before>package async

import (
	"reflect"
)

// Type: M a
//
// Represents the async monad
type Async struct {
	ret  []reflect.Value
	done bool
	wait chan bool
}

// Type: (unit -> a) -> M a
//
// Deferred executes a function asynchronously and returns an Async
// struct representing the return values.
func Deferred(f interface{}) *Async {
	return withNewAsync(func() []reflect.Value {
		ffun := reflect.ValueOf(f)
		return ffun.Call(nil)
	})
}

// Type: a -> M a
//
// Return takes a set of inputs and returns an already determined Async
// struct wrapping those inputs
func Return(vs ...interface{}) *Async {
	return withNewAsync(func() []reflect.Value {
		ret := make([]reflect.Value, len(vs))
		for i := range vs {
			ret[i] = reflect.ValueOf(vs[i])
		}
		return ret
	})
}

// Type: M a -> (a -> b) -> M b
//
// Fmapping a function to self guarantees that function will execute on the
// return values of self as soon self is determined. It returns an Async
// struct representing the return values of the called function.
//
// If Fmap was called after self is already deteremined, the function will
// begin to execute right away.
//
// Note that while this represents a functor, the input types are reversed.
// This is done to make it a method rather than a function as Go does not
// allow creation of new operators.
func (self *Async) Fmap(f interface{}) *Async {
	return withNewAsync(func() []reflect.Value {
		self.Wait()

		ffun := reflect.ValueOf(f)
		ftyp := reflect.TypeOf(f)
		return ffun.Call(self.ret[0:ftyp.NumIn()])
	})
}

// Type: M a -> (a -> M b) -> M b
//
// Bind behaves the same way as Fmap except it operates on functions
// that return Async structs.
func (self *Async) Bind(f interface{}) *Async {
	return withNewAsync(func() []reflect.Value {
		self.Wait()

		ffun := reflect.ValueOf(f)
		ftyp := reflect.TypeOf(f)
		next := ffun.Call(self.ret[0:ftyp.NumIn()])[0].Interface().(*Async)
		next.Wait()

		return next.ret
	})
}

// Type: M (M a) -> M a
//
// Join is used to unify multiple layers of Async structs.
func (self *Async) Join(f interface{}) *Async {
	return withNewAsync(func() []reflect.Value {
		self.Wait()

		selfRet := self.ret[0].Interface().(*Async)
		selfRet.Wait()

		return selfRet.ret
	})
}

// Type: M a -> bool
//
// IsDone returns whether or not self has becomed determined yet.
func (self *Async) IsDone() bool {
	return self.done
}

// Type: M a -> unit
//
// Wait is a blocking function that waits until self has become determined.
func (self *Async) Wait() {
	for _ = range self.wait {
	}
}

// Type: (a -> b) -> (M a -> M b)
//
// Lift is another representation of Fmap with the types reversed.
//
// Lift can take any unary function and return a function that operates on
// Async structs.
func Lift(f interface{}) func(*Async) *Async {
	return func(self *Async) *Async {
		return self.Fmap(f)
	}
}

// Type: ((a, b) -> c) -> M a -> M b -> M c
//
// Lift2 behaves the same as lift except with binary functions.
//
// The parameters to the function are curried as well.
func Lift2(f interface{}) func(*Async) func(*Async) *Async {
	return func(self *Async) func(*Async) *Async {
		return func(other *Async) *Async {
			return self.And(other).Fmap(f)
		}
	}
}
<commit_msg>added end and waittoend<commit_after>package async

import (
	"reflect"
)

// Type: M a
//
// Represents the async monad
type Async struct {
	ret  []reflect.Value
	done bool
	wait chan bool
}

// Type: (unit -> a) -> M a
//
// Deferred executes a function asynchronously and returns an Async
// struct representing the return values.
func Deferred(f interface{}) *Async {
	return withNewAsync(func() []reflect.Value {
		ffun := reflect.ValueOf(f)
		return ffun.Call(nil)
	})
}

// Type: a -> M a
//
// Return takes a set of inputs and returns an already determined Async
// struct wrapping those inputs
func Return(vs ...interface{}) *Async {
	return withNewAsync(func() []reflect.Value {
		ret := make([]reflect.Value, len(vs))
		for i := range vs {
			ret[i] = reflect.ValueOf(vs[i])
		}
		return ret
	})
}

// Type: M a -> (a -> b) -> M b
//
// Fmapping a function to self guarantees that function will execute on the
// return values of self as soon self is determined. It returns an Async
// struct representing the return values of the called function.
//
// If Fmap was called after self is already deteremined, the function will
// begin to execute right away.
//
// Note that while this represents a functor, the input types are reversed.
// This is done to make it a method rather than a function as Go does not
// allow creation of new operators.
func (self *Async) Fmap(f interface{}) *Async {
	return withNewAsync(func() []reflect.Value {
		self.Wait()

		ffun := reflect.ValueOf(f)
		ftyp := reflect.TypeOf(f)
		return ffun.Call(self.ret[0:ftyp.NumIn()])
	})
}

// Type: M a -> (a -> M b) -> M b
//
// Bind behaves the same way as Fmap except it operates on functions
// that return Async structs.
func (self *Async) Bind(f interface{}) *Async {
	return withNewAsync(func() []reflect.Value {
		self.Wait()

		ffun := reflect.ValueOf(f)
		ftyp := reflect.TypeOf(f)
		next := ffun.Call(self.ret[0:ftyp.NumIn()])[0].Interface().(*Async)
		next.Wait()

		return next.ret
	})
}

// Type: M (M a) -> M a
//
// Join is used to unify multiple layers of Async structs.
func (self *Async) Join(f interface{}) *Async {
	return withNewAsync(func() []reflect.Value {
		self.Wait()

		selfRet := self.ret[0].Interface().(*Async)
		selfRet.Wait()

		return selfRet.ret
	})
}

// Type: M a -> (a -> b) -> unit
//
// End is the nonblocking version of WaitToEnd
func (self *Async) End(f interface{}) {
	go self.WaitToEnd(f)
}

// Type: M a -> (a -> b) -> unit
//
// WaitToEnd is a blocking function that executes a function on the return
// of self. This ends the chain of Asyncs so there is no overheard of a channel
// being instantiated.
func (self *Async) WaitToEnd(f interface{}) {
	self.Wait()

	ffun := reflect.ValueOf(f)
	ftyp := reflect.TypeOf(f)
	ffun.Call(self.ret[0:ftyp.NumIn()])
}

// Type: M a -> bool
//
// IsDone returns whether or not self has becomed determined yet.
func (self *Async) IsDone() bool {
	return self.done
}

// Type: M a -> unit
//
// Wait is a blocking function that waits until self has become determined.
func (self *Async) Wait() {
	for _ = range self.wait {
	}
}

// Type: (a -> b) -> (M a -> M b)
//
// Lift is another representation of Fmap with the types reversed.
//
// Lift can take any unary function and return a function that operates on
// Async structs.
func Lift(f interface{}) func(*Async) *Async {
	return func(self *Async) *Async {
		return self.Fmap(f)
	}
}

// Type: ((a, b) -> c) -> M a -> M b -> M c
//
// Lift2 behaves the same as lift except with binary functions.
//
// The parameters to the function are curried as well.
func Lift2(f interface{}) func(*Async) func(*Async) *Async {
	return func(self *Async) func(*Async) *Async {
		return func(other *Async) *Async {
			return self.And(other).Fmap(f)
		}
	}
}
<|endoftext|>


<commit_before>/*
This code implements the flow chart that can be found here.
http://www.html5rocks.com/static/images/cors_server_flowchart.png

A Default Config for example is below:

	cors.Config{
		Origins:        "*",
		Methods:        "GET, PUT, POST, DELETE",
		RequestHeaders: "Origin, Authorization, Content-Type",
		ExposedHeaders: "",
		MaxAge: 1 * time.Minute,
		Credentials: true,
		ValidateHeaders: false,
	}
*/
package cors

import (
	"fmt"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
)

const (
	AllowOriginKey      string = "Access-Control-Allow-Origin"
	AllowCredentialsKey        = "Access-Control-Allow-Credentials"
	AllowHeadersKey            = "Access-Control-Allow-Headers"
	AllowMethodsKey            = "Access-Control-Allow-Methods"
	MaxAgeKey                  = "Access-Control-Max-Age"

	OriginKey         = "Origin"
	RequestMethodKey  = "Access-Control-Request-Method"
	RequestHeadersKey = "Access-Control-Request-Headers"
	ExposeHeadersKey  = "Access-Control-Expose-Headers"
)

/*
Config defines the configuration options available to control how the CORS middleware should function.
*/
type Config struct {
	// Enabling this causes us to compare Request-Method and Request-Headers to confirm they contain a subset of the Allowed Methods and Allowed Headers
	// The spec however allows for the server to always match, and simply return the allowed methods and headers. Either is supported in this middleware.
	ValidateHeaders bool

	// Comma delimited list of origin domains. Wildcard "*" is also allowed, and matches all origins.
	// If the origin does not match an item in the list, then the request is denied.
	Origins string
	origins []string

	// This are the headers that the resource supports, and will accept in the request.
	// Default is "Authorization".
	RequestHeaders string
	requestHeaders []string

	// These are headers that should be accessable by the CORS client, they are in addition to those defined by the spec as "simple response headers"
	//	 Cache-Control
	//	 Content-Language
	//	 Content-Type
	//	 Expires
	//	 Last-Modified
	//	 Pragma
	ExposedHeaders string

	// Comma delimited list of acceptable HTTP methods.
	Methods string
	methods []string

	// The amount of time in seconds that the client should cache the Preflight request
	MaxAge time.Duration
	maxAge string

	// If true, then cookies and Authorization headers are allowed along with the request.  This
	// is passed to the browser, but is not enforced.
	Credentials bool
	credentials string
}

// One time, do the conversion from our the public facing Configuration,
// to all the formats we use internally strings for headers.. slices for looping
func (config *Config) prepare() {
	config.origins = strings.Split(config.Origins, ", ")
	config.methods = strings.Split(config.Methods, ", ")
	config.requestHeaders = strings.Split(config.RequestHeaders, ", ")
	config.maxAge = fmt.Sprintf("%.f", config.MaxAge.Seconds())
	config.credentials = fmt.Sprintf("%t", config.Credentials)
}

/*
Middleware generates a middleware handler function that works inside of a Gin request
to set the correct CORS headers.  It accepts a cors.Options struct for configuration.
*/
func Middleware(config Config) gin.HandlerFunc {
	forceOriginMatch := false

	if config.Origins == "" {
		panic("You must set at least a single valid origin. If you don't want CORS, to apply, simply remove the middleware.")
	}

	if config.Origins == "*" {
		forceOriginMatch = true
	}

	config.prepare()

	// Create the Middleware function
	return func(context *gin.Context) {
		// Read the Origin header from the HTTP request
		currentOrigin := context.Request.Header.Get(OriginKey)

		// CORS headers are added whenever the browser request includes an "Origin" header
		// However, if no Origin is supplied, they should never be added.
		if currentOrigin == "" {
			return
		}

		originMatch := false
		if !forceOriginMatch {
			for _, value := range config.origins {
				if value == currentOrigin {
					originMatch = true
					break
				}
			}
		}

		if forceOriginMatch || originMatch {
			valid := false
			preflight := false

			if context.Request.Method == "OPTIONS" {
				requestMethod := context.Request.Header.Get(RequestMethodKey)
				if requestMethod != "" {
					preflight = true
					valid = handlePreflight(context, config, requestMethod)
				}
			}

			if !preflight {
				valid = handleRequest(context, config)
			}

			if valid {
				if config.Credentials {
					context.Writer.Header().Set(AllowCredentialsKey, config.credentials)
					context.Writer.Header().Set(AllowOriginKey, currentOrigin)
				} else {
					context.Writer.Header().Set(AllowOriginKey, "*")
				}

				//If this is a preflight request, we are finished, quit.
				//Otherwise this is a normal request and operations should proceed at normal
				if preflight {
					context.AbortWithStatus(200)
				}
				return
			}
		}

		//If it reaches here, it was not a valid request
		context.Abort()
	}
}

func handlePreflight(context *gin.Context, config Config, requestMethod string) bool {
	if ok := validateRequestMethod(requestMethod, config); ok == false {
		return false
	}

	if ok := validateRequestHeaders(context.Request.Header.Get(RequestHeadersKey), config); ok == true {
		context.Writer.Header().Set(AllowMethodsKey, config.Methods)
		context.Writer.Header().Set(AllowHeadersKey, config.RequestHeaders)

		if config.maxAge != "0" {
			context.Writer.Header().Set(MaxAgeKey, config.maxAge)
		}

		return true
	}

	return false
}

func handleRequest(context *gin.Context, config Config) bool {
	if config.ExposedHeaders != "" {
		context.Writer.Header().Set(ExposeHeadersKey, config.ExposedHeaders)
	}

	return true
}

func validateRequestMethod(requestMethod string, config Config) bool {
	if !config.ValidateHeaders {
		return true
	}

	if requestMethod != "" {
		for _, value := range config.methods {
			if value == requestMethod {
				return true
			}
		}
	}

	return false
}

func validateRequestHeaders(requestHeaders string, config Config) bool {
	if !config.ValidateHeaders {
		return true
	}

	headers := strings.Split(requestHeaders, ", ")

	for _, header := range headers {
		match := false

		for _, value := range config.requestHeaders {
			if strings.ToLower(value) == strings.ToLower(header) {
				match = true
				break
			}
		}

		if !match {
			return false
		}
	}

	return true
}
<commit_msg>Alternative method for PR #2. There are less calls to strings.ToLower<commit_after>/*
This code implements the flow chart that can be found here.
http://www.html5rocks.com/static/images/cors_server_flowchart.png

A Default Config for example is below:

	cors.Config{
		Origins:        "*",
		Methods:        "GET, PUT, POST, DELETE",
		RequestHeaders: "Origin, Authorization, Content-Type",
		ExposedHeaders: "",
		MaxAge: 1 * time.Minute,
		Credentials: true,
		ValidateHeaders: false,
	}
*/
package cors

import (
	"fmt"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
)

const (
	AllowOriginKey      string = "Access-Control-Allow-Origin"
	AllowCredentialsKey        = "Access-Control-Allow-Credentials"
	AllowHeadersKey            = "Access-Control-Allow-Headers"
	AllowMethodsKey            = "Access-Control-Allow-Methods"
	MaxAgeKey                  = "Access-Control-Max-Age"

	OriginKey         = "Origin"
	RequestMethodKey  = "Access-Control-Request-Method"
	RequestHeadersKey = "Access-Control-Request-Headers"
	ExposeHeadersKey  = "Access-Control-Expose-Headers"
)

const (
	optionsMethod = "OPTIONS"
)

/*
Config defines the configuration options available to control how the CORS middleware should function.
*/
type Config struct {
	// Enabling this causes us to compare Request-Method and Request-Headers to confirm they contain a subset of the Allowed Methods and Allowed Headers
	// The spec however allows for the server to always match, and simply return the allowed methods and headers. Either is supported in this middleware.
	ValidateHeaders bool

	// Comma delimited list of origin domains. Wildcard "*" is also allowed, and matches all origins.
	// If the origin does not match an item in the list, then the request is denied.
	Origins string
	origins []string

	// This are the headers that the resource supports, and will accept in the request.
	// Default is "Authorization".
	RequestHeaders string
	requestHeaders []string

	// These are headers that should be accessable by the CORS client, they are in addition to those defined by the spec as "simple response headers"
	//	 Cache-Control
	//	 Content-Language
	//	 Content-Type
	//	 Expires
	//	 Last-Modified
	//	 Pragma
	ExposedHeaders string

	// Comma delimited list of acceptable HTTP methods.
	Methods string
	methods []string

	// The amount of time in seconds that the client should cache the Preflight request
	MaxAge time.Duration
	maxAge string

	// If true, then cookies and Authorization headers are allowed along with the request.  This
	// is passed to the browser, but is not enforced.
	Credentials bool
	credentials string
}

// One time, do the conversion from our the public facing Configuration,
// to all the formats we use internally strings for headers.. slices for looping
func (config *Config) prepare() {
	config.origins = strings.Split(config.Origins, ", ")
	config.methods = strings.Split(config.Methods, ", ")
	config.requestHeaders = strings.Split(config.RequestHeaders, ", ")
	config.maxAge = fmt.Sprintf("%.f", config.MaxAge.Seconds())

	// Generates a boolean of value "true".
	config.credentials = fmt.Sprintf("%t", config.Credentials)

	// Convert to lower-case once as request headers are supposed to be a case-insensitive match
	for idx, header := range config.requestHeaders {
		config.requestHeaders[idx] = strings.ToLower(header)
	}
}

/*
Middleware generates a middleware handler function that works inside of a Gin request
to set the correct CORS headers.  It accepts a cors.Options struct for configuration.
*/
func Middleware(config Config) gin.HandlerFunc {
	forceOriginMatch := false

	if config.Origins == "" {
		panic("You must set at least a single valid origin. If you don't want CORS, to apply, simply remove the middleware.")
	}

	if config.Origins == "*" {
		forceOriginMatch = true
	}

	config.prepare()

	// Create the Middleware function
	return func(context *gin.Context) {
		// Read the Origin header from the HTTP request
		currentOrigin := context.Request.Header.Get(OriginKey)

		// CORS headers are added whenever the browser request includes an "Origin" header
		// However, if no Origin is supplied, they should never be added.
		if currentOrigin == "" {
			return
		}

		originMatch := false
		if !forceOriginMatch {
			originMatch = matchOrigin(currentOrigin, config)
		}

		if forceOriginMatch || originMatch {
			valid := false
			preflight := false

			if context.Request.Method == optionsMethod {
				requestMethod := context.Request.Header.Get(RequestMethodKey)
				if requestMethod != "" {
					preflight = true
					valid = handlePreflight(context, config, requestMethod)
				}
			}

			if !preflight {
				valid = handleRequest(context, config)
			}

			if valid {

				// Allowed origins cannot be the string "*" cannot be used for a resource that supports credentials.
				if config.Credentials {
					context.Writer.Header().Set(AllowCredentialsKey, config.credentials)
					context.Writer.Header().Set(AllowOriginKey, currentOrigin)
				} else {
					context.Writer.Header().Set(AllowOriginKey, "*")
				}

				//If this is a preflight request, we are finished, quit.
				//Otherwise this is a normal request and operations should proceed at normal
				if preflight {
					context.AbortWithStatus(200)
				}
				return
			}
		}

		//If it reaches here, it was not a valid request
		context.Abort()
	}
}

func handlePreflight(context *gin.Context, config Config, requestMethod string) bool {
	if ok := validateRequestMethod(requestMethod, config); ok == false {
		return false
	}

	if ok := validateRequestHeaders(context.Request.Header.Get(RequestHeadersKey), config); ok == true {
		context.Writer.Header().Set(AllowMethodsKey, config.Methods)
		context.Writer.Header().Set(AllowHeadersKey, config.RequestHeaders)

		if config.maxAge != "0" {
			context.Writer.Header().Set(MaxAgeKey, config.maxAge)
		}

		return true
	}

	return false
}

func handleRequest(context *gin.Context, config Config) bool {
	if config.ExposedHeaders != "" {
		context.Writer.Header().Set(ExposeHeadersKey, config.ExposedHeaders)
	}

	return true
}

// Case-sensitive match of origin header
func matchOrigin(origin string, config Config) bool {
	for _, value := range config.origins {
		if value == origin {
			return true
		}
	}
	return false
}

// Case-sensitive match of request method
func validateRequestMethod(requestMethod string, config Config) bool {
	if !config.ValidateHeaders {
		return true
	}

	if requestMethod != "" {
		for _, value := range config.methods {
			if value == requestMethod {
				return true
			}
		}
	}

	return false
}

// Case-insensitive match of request headers
func validateRequestHeaders(requestHeaders string, config Config) bool {
	if !config.ValidateHeaders {
		return true
	}

	headers := strings.Split(requestHeaders, ", ")

	for _, header := range headers {
		match := false
		header = strings.ToLower(header)

		for _, value := range config.requestHeaders {
			if value == header {
				match = true
				break
			}
		}

		if !match {
			return false
		}
	}

	return true
}
<|endoftext|>


<commit_before>package cqlb

import (
	"fmt"
	"reflect"
	"strings"

	"github.com/gocql/gocql"
	"github.com/jinzhu/inflection"
)

const (
	insertQueryTemplate = "insert into %s (%s) values(%s);"
	whereQueryTemplate  = "select %s from %s where %s %s %s;"
)

type fieldTag struct {
	Name      string
	OmitEmpty bool
}

type Session struct {
	s         *gocql.Session
	query     interface{}
	args      []interface{}
	sel       []string
	limit     int
	value     reflect.Value
	indirect  reflect.Value
	tableName string
}

func SetSession(s *gocql.Session) *Session {
	return &Session{s: s}
}

func (s *Session) Find(value interface{}) error {
	var fields map[string]interface{}
	var fieldsToScan []interface{}
	v := reflect.ValueOf(value)
	indirect := reflect.Indirect(v)
	s.setModel(v)
	query := s.query
	vq := reflect.ValueOf(query)
	kindQuery := vq.Kind()
	switch kindQuery {
	case reflect.Map:
		fields = whereFieldsFromMap(query)
	}
	values := fields["values"].([]interface{})
	iter := s.s.Query(s.whereQuery(fields), values).Iter()
	names := f["names"].([]string)
	for i := 0; i < len(names); i++ {
		name := names[i]
		field := v.FieldByName(name)
		fieldsToScan = append(fieldsToScan, field.Interface())
	}
	return nil
}

func (s *Session) Insert(v interface{}) error {
	f := fields(v)
	stmt := insertQuery(f)
	return s.s.Query(stmt, f["values"].([]interface{})...).Exec()
}

func (s *Session) Where(query interface{}, args ...interface{}) *Session {
	ns := s.clone()
	ns.query = query
	ns.args = args
	return ns
}

func (s *Session) Limit(limit int) *Session {
	c := s.clone()
	c.limit = limit
	return c
}

func (s *Session) Model(value interface{}) *Session {
	v := reflect.ValueOf(value)
	ns := s.clone()
	ns.setModel(v)
	return ns
}

func (s *Session) Select(sel ...string) *Session {
	c := s.clone()
	c.sel = sel
	return c
}

func (s *Session) limitString() string {
	if limit := s.limit; limit > 0 {
		return fmt.Sprintf("LIMIT %v", limit)
	}
	return ""
}

func (s *Session) selectString() string {
	if sel := s.sel; len(sel) > 0 {
		return strings.Join(sel, ",")
	}
	return "*"
}

func (s *Session) setModel(v reflect.Value) {
	indirect := reflect.Indirect(v)
	t := indirect.Type()
	s.value = v
	s.indirect = indirect
	s.tableName = inflection.Plural(strings.ToLower(t.Name()))
}

func (s *Session) clone() *Session {
	ns := *s
	return &ns
}

func (s *Session) whereQuery(f map[string]interface{}) string {
	sel := s.selectString()
	limit := s.limitString()
	query := fmt.Sprintf(whereQueryTemplate, sel, s.tableName, f["conditions"], limit, "")
	return query
}

func insertQuery(f map[string]interface{}) string {
	query := fmt.Sprintf(insertQueryTemplate, f["table_name"], f["names"], f["slots"])
	return query
}

func compile(v interface{}, cols []gocql.ColumnInfo) error {

	return nil
}

func tag(f reflect.StructField) *fieldTag {
	ft := &fieldTag{}
	tag := f.Tag.Get("cql")
	opts := strings.Split(tag, ",")
	ft.Name = opts[0]
	if len(opts) > 1 && opts[1] == "omitempty" {
		ft.OmitEmpty = true
	}
	return ft
}

func fields(v interface{}) map[string]interface{} {
	var names string
	var slots string
	var values []interface{}
	result := make(map[string]interface{})
	value := reflect.ValueOf(v)
	indirect := reflect.Indirect(value)
	t := indirect.Type()
	result["table_name"] = inflection.Plural(strings.ToLower(t.Name()))
	for i := 0; i < t.NumField(); i++ {
		var inf interface{}
		f := t.Field(i)
		fv := indirect.Field(i)
		tag := tag(f)
		fvIndirect := reflect.Indirect(fv)
		inf = fvIndirect.Interface()
		isZero := isZero(inf)
		if isZero == true && tag.OmitEmpty == true {
			continue
		}
		if i != 0 {
			names += ","
			slots += ","
		}
		if tag.Name != "" {
			names += tag.Name
		} else {
			names += strings.ToLower(f.Name)
		}
		slots += "?"
		values = append(values, inf)
	}
	result["names"] = names
	result["values"] = values
	result["slots"] = slots
	return result
}

func whereFieldsFromMap(value interface{}) map[string]interface{} {
	var conditions string
	var values []interface{}
	var names []string
	result := make(map[string]interface{})
	v := reflect.ValueOf(value)
	keys := v.MapKeys()
	for i := 0; i < len(keys); i++ {
		key := keys[i]
		keyString := key.String()
		value := v.MapIndex(key)
		if i != 0 {
			conditions += " AND "
		}
		conditions += fmt.Sprintf("%s = ?", keyString)
		names = append(names, keyString)
		values = append(values, value)
	}
	result["conditions"] = conditions
	result["values"] = values
	result["names"] = names
	return result
}

func isZero(x interface{}) bool {
	return reflect.DeepEqual(x, reflect.Zero(reflect.TypeOf(x)).Interface())
}

func contentOfSlice(v reflect.Value) []interface{} {
	slice := make([]interface{}, v.Len())
	for i := 0; i < v.Len(); i++ {
		f := reflect.Indirect(v.Index(i))
		slice[i] = f.Interface()
	}
	return slice
}

func getType(v interface{}) {

}
<commit_msg>I got no time. Stopped Find development<commit_after>package cqlb

import (
	"fmt"
	"reflect"
	"strings"

	"github.com/gocql/gocql"
	"github.com/jinzhu/inflection"
)

const (
	insertQueryTemplate = "insert into %s (%s) values(%s);"
	whereQueryTemplate  = "select %s from %s where %s %s %s;"
)

type fieldTag struct {
	Name      string
	OmitEmpty bool
}

type Session struct {
	s         *gocql.Session
	query     interface{}
	args      []interface{}
	sel       []string
	limit     int
	value     reflect.Value
	indirect  reflect.Value
	tableName string
}

func SetSession(s *gocql.Session) *Session {
	return &Session{s: s}
}

func (s *Session) Find(slice interface{}) error {
	// var fields map[string]interface{}
	// var fieldsToScan []interface{}
	// value := reflect.ValueOf(slice)
	// k := value.Kind()
	// if k != reflect.Slice {
	// 	return errors.New("value should be a slice.")
	// }
	// v := value.Index(0)
	// indirect := reflect.Indirect(v)
	// s.setModel(v)
	// query := s.query
	// vq := reflect.ValueOf(query)
	// kindQuery := vq.Kind()
	// switch kindQuery {
	// case reflect.Map:
	// 	fields = whereFieldsFromMap(query)
	// }
	// iter := s.s.Query(s.whereQuery(fields), values).Iter()
	// cols := iter.Columns()
	// values := make([]interface{}, len(cols))
	// names := f["names"].([]string)
	// for i, col := range cols {
	// 	values[i] = f["strategies"].(map[string]interface{})[col.Name]
	// }
	return nil
}

func (s *Session) Insert(v interface{}) error {
	f := fields(v)
	stmt := insertQuery(f)
	return s.s.Query(stmt, f["values"].([]interface{})...).Exec()
}

func (s *Session) Where(query interface{}, args ...interface{}) *Session {
	ns := s.clone()
	ns.query = query
	ns.args = args
	return ns
}

func (s *Session) Limit(limit int) *Session {
	c := s.clone()
	c.limit = limit
	return c
}

func (s *Session) Model(value interface{}) *Session {
	v := reflect.ValueOf(value)
	ns := s.clone()
	ns.setModel(v)
	return ns
}

func (s *Session) Select(sel ...string) *Session {
	c := s.clone()
	c.sel = sel
	return c
}

func (s *Session) limitString() string {
	if limit := s.limit; limit > 0 {
		return fmt.Sprintf("LIMIT %v", limit)
	}
	return ""
}

func (s *Session) selectString() string {
	if sel := s.sel; len(sel) > 0 {
		return strings.Join(sel, ",")
	}
	return "*"
}

func (s *Session) setModel(v reflect.Value) {
	indirect := reflect.Indirect(v)
	t := indirect.Type()
	s.value = v
	s.indirect = indirect
	s.tableName = inflection.Plural(strings.ToLower(t.Name()))
}

func (s *Session) clone() *Session {
	ns := *s
	return &ns
}

func (s *Session) whereQuery(f map[string]interface{}) string {
	sel := s.selectString()
	limit := s.limitString()
	query := fmt.Sprintf(whereQueryTemplate, sel, s.tableName, f["conditions"], limit, "")
	return query
}

func insertQuery(f map[string]interface{}) string {
	query := fmt.Sprintf(insertQueryTemplate, f["table_name"], f["names"], f["slots"])
	return query
}

func compile(v interface{}, cols []gocql.ColumnInfo) error {

	return nil
}

func tag(f reflect.StructField) *fieldTag {
	ft := &fieldTag{}
	tag := f.Tag.Get("cql")
	opts := strings.Split(tag, ",")
	ft.Name = opts[0]
	if len(opts) > 1 && opts[1] == "omitempty" {
		ft.OmitEmpty = true
	}
	return ft
}

func fields(v interface{}) map[string]interface{} {
	var names string
	var slots string
	var values []interface{}
	var strategies map[string]interface{}
	result := make(map[string]interface{})
	value := reflect.ValueOf(v)
	indirect := reflect.Indirect(value)
	t := indirect.Type()
	result["table_name"] = inflection.Plural(strings.ToLower(t.Name()))
	for i := 0; i < t.NumField(); i++ {
		var inf interface{}
		var tagName string
		f := t.Field(i)
		fv := indirect.Field(i)
		tag := tag(f)
		fvIndirect := reflect.Indirect(fv)
		inf = fvIndirect.Interface()
		isZero := isZero(inf)
		if isZero == true && tag.OmitEmpty == true {
			continue
		}
		if i != 0 {
			names += ","
			slots += ","
		}
		if tag.Name != "" {
			tagName = tag.Name
		} else {
			tagName = strings.ToLower(f.Name)
		}
		names += tagName
		slots += "?"
		strategies[tagName] = inf
		values = append(values, inf)
	}
	result["names"] = names
	result["values"] = values
	result["slots"] = slots
	return result
}

func whereFieldsFromMap(value interface{}) map[string]interface{} {
	var conditions string
	var values []interface{}
	var names []string
	result := make(map[string]interface{})
	v := reflect.ValueOf(value)
	keys := v.MapKeys()
	for i := 0; i < len(keys); i++ {
		key := keys[i]
		keyString := key.String()
		value := v.MapIndex(key)
		if i != 0 {
			conditions += " AND "
		}
		conditions += fmt.Sprintf("%s = ?", keyString)
		names = append(names, keyString)
		values = append(values, value)
	}
	result["conditions"] = conditions
	result["values"] = values
	result["names"] = names
	return result
}

func isZero(x interface{}) bool {
	return reflect.DeepEqual(x, reflect.Zero(reflect.TypeOf(x)).Interface())
}

func contentOfSlice(v reflect.Value) []interface{} {
	slice := make([]interface{}, v.Len())
	for i := 0; i < v.Len(); i++ {
		f := reflect.Indirect(v.Index(i))
		slice[i] = f.Interface()
	}
	return slice
}

func getType(v interface{}) {

}
<|endoftext|>


<commit_before>// This library implements a cron spec parser and runner.  See the README for
// more details.
package cron

import (
	"sort"
	"time"
)

// Cron keeps track of any number of entries, invoking the associated func as
// specified by the schedule. It may be started, stopped, and the entries may
// be inspected while running.
type Cron struct {
	entries  []*Entry
	stop     chan struct{}
	add      chan *Entry
	snapshot chan []*Entry
	running  bool
	count    int
}

// Job is an interface for submitted cron jobs.
type Job interface {
	Run()
}

// The Schedule describes a job's duty cycle.
type Schedule interface {
	// Return the next activation time, later than the given time.
	// Next is invoked initially, and then each time the job is run.
	Next(time.Time) time.Time
}

// Entry consists of a schedule and the func to execute on that schedule.
type Entry struct {
	// The schedule on which this job should be run.
	Schedule Schedule

	// The next time the job will run. This is the zero time if Cron has not been
	// started or this entry's schedule is unsatisfiable
	Next time.Time

	// The last time this job was run. This is the zero time if the job has never
	// been run.
	Prev time.Time

	// The Job to run.
	Job Job

	// The identifier to reference the job instance.
	id int

	// 0: normal, 1: paused
	status int
}

// byTime is a wrapper for sorting the entry array by time
// (with zero time at the end).
type byTime []*Entry

func (s byTime) Len() int { return len(s) }
func (s byTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s byTime) Less(i, j int) bool {
	// Two zero times should return false.
	// Otherwise, zero is "greater" than any other time.
	// (To sort it at the end of the list.)
	if s[i].Next.IsZero() {
		return false
	}
	if s[j].Next.IsZero() {
		return true
	}
	return s[i].Next.Before(s[j].Next)
}

// New returns a new Cron job runner.
func New() *Cron {
	return &Cron{
		entries:  nil,
		add:      make(chan *Entry),
		stop:     make(chan struct{}),
		snapshot: make(chan []*Entry),
		running:  false,
		count:    0,
	}
}

// A wrapper that turns a func() into a cron.Job
type FuncJob func()

func (f FuncJob) Run() { f() }

// AddFunc adds a func to the Cron to be run on the given schedule.
func (c *Cron) AddFunc(spec string, cmd func()) (int, error) {
	return c.AddJob(spec, FuncJob(cmd))
}

// RemoveFunc removes a func from the Cron referenced by the id.
func (c *Cron) RemoveFunc(id int) {
	w := 0 // write index
	for _, x := range c.entries {
		if id == x.id {
			continue
		}
		c.entries[w] = x
		w++
	}
	c.entries = c.entries[:w]
}

func (c *Cron) PauseFunc(id int) {
	for _, x := range c.entries {
		if id == x.id {
			x.status = 1
			break
		}
	}
}

func (c *Cron) ResumeFunc(id int) {
	for _, x := range c.entries {
		if id == x.id {
			x.status = 0
			break
		}
	}
}

// AddFunc adds a Job to the Cron to be run on the given schedule.
func (c *Cron) AddJob(spec string, cmd Job) (int, error) {
	schedule, err := Parse(spec)
	if err != nil {
		return -1, err
	}
	c.count++
	c.Schedule(schedule, cmd, c.count)
	return c.count, nil
}

// Schedule adds a Job to the Cron to be run on the given schedule.
func (c *Cron) Schedule(schedule Schedule, cmd Job, id int) {
	entry := &Entry{
		Schedule: schedule,
		Job:      cmd,
		id:       id,
		status:    0,
	}
	if !c.running {
		c.entries = append(c.entries, entry)
		return
	}

	c.add <- entry
}

// Entries returns a snapshot of the cron entries.
func (c *Cron) Entries() []*Entry {
	if c.running {
		c.snapshot <- nil
		x := <-c.snapshot
		return x
	}
	return c.entrySnapshot()
}

// Start the cron scheduler in its own go-routine.
func (c *Cron) Start() {
	c.running = true
	go c.run()
}

// Run the scheduler.. this is private just due to the need to synchronize
// access to the 'running' state variable.
func (c *Cron) run() {
	// Figure out the next activation times for each entry.
	now := time.Now().Local()
	for _, entry := range c.entries {
		entry.Next = entry.Schedule.Next(now)
	}

	for {
		// Determine the next entry to run.
		sort.Sort(byTime(c.entries))

		var effective time.Time
		if len(c.entries) == 0 || c.entries[0].Next.IsZero() {
			// If there are no entries yet, just sleep - it still handles new entries
			// and stop requests.
			effective = now.AddDate(10, 0, 0)
		} else {
			effective = c.entries[0].Next
		}

		select {
		case now = <-time.After(effective.Sub(now)): {
			// Run every entry whose next time was this effective time.
			for _, e := range c.entries {
				if e.Next != effective {
					break
				}
				if e.status == 0 {
					go e.Job.Run()
				}
				e.Prev = e.Next
				e.Next = e.Schedule.Next(effective)
			}
			continue
		}

		case newEntry := <-c.add:
			c.entries = append(c.entries, newEntry)
			newEntry.Next = newEntry.Schedule.Next(now)

		case <-c.snapshot:
		c.snapshot <- c.entrySnapshot()

		case <-c.stop:
			return
		}

		// 'now' should be updated after newEntry and snapshot cases.
		now = time.Now().Local()
	}
}

// Stop the cron scheduler.
func (c *Cron) Stop() {
	c.stop <- struct{}{}
	c.running = false
}

// entrySnapshot returns a copy of the current cron entry list.
func (c *Cron) entrySnapshot() []*Entry {
	entries := []*Entry{}
	for _, e := range c.entries {
		entries = append(entries, &Entry{
				Schedule: e.Schedule,
				Next:     e.Next,
				Prev:     e.Prev,
				Job:      e.Job,
			})
	}
	return entries
}
<commit_msg>go fmt applied.<commit_after>// This library implements a cron spec parser and runner.  See the README for
// more details.
package cron

import (
	"sort"
	"time"
)

// Cron keeps track of any number of entries, invoking the associated func as
// specified by the schedule. It may be started, stopped, and the entries may
// be inspected while running.
type Cron struct {
	entries  []*Entry
	stop     chan struct{}
	add      chan *Entry
	snapshot chan []*Entry
	running  bool
	count    int
}

// Job is an interface for submitted cron jobs.
type Job interface {
	Run()
}

// The Schedule describes a job's duty cycle.
type Schedule interface {
	// Return the next activation time, later than the given time.
	// Next is invoked initially, and then each time the job is run.
	Next(time.Time) time.Time
}

// Entry consists of a schedule and the func to execute on that schedule.
type Entry struct {
	// The schedule on which this job should be run.
	Schedule Schedule

	// The next time the job will run. This is the zero time if Cron has not been
	// started or this entry's schedule is unsatisfiable
	Next time.Time

	// The last time this job was run. This is the zero time if the job has never
	// been run.
	Prev time.Time

	// The Job to run.
	Job Job

	// The identifier to reference the job instance.
	id int

	// 0: normal, 1: paused
	status int
}

// byTime is a wrapper for sorting the entry array by time
// (with zero time at the end).
type byTime []*Entry

func (s byTime) Len() int      { return len(s) }
func (s byTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s byTime) Less(i, j int) bool {
	// Two zero times should return false.
	// Otherwise, zero is "greater" than any other time.
	// (To sort it at the end of the list.)
	if s[i].Next.IsZero() {
		return false
	}
	if s[j].Next.IsZero() {
		return true
	}
	return s[i].Next.Before(s[j].Next)
}

// New returns a new Cron job runner.
func New() *Cron {
	return &Cron{
		entries:  nil,
		add:      make(chan *Entry),
		stop:     make(chan struct{}),
		snapshot: make(chan []*Entry),
		running:  false,
		count:    0,
	}
}

// A wrapper that turns a func() into a cron.Job
type FuncJob func()

func (f FuncJob) Run() { f() }

// AddFunc adds a func to the Cron to be run on the given schedule.
func (c *Cron) AddFunc(spec string, cmd func()) (int, error) {
	return c.AddJob(spec, FuncJob(cmd))
}

// RemoveFunc removes a func from the Cron referenced by the id.
func (c *Cron) RemoveFunc(id int) {
	w := 0 // write index
	for _, x := range c.entries {
		if id == x.id {
			continue
		}
		c.entries[w] = x
		w++
	}
	c.entries = c.entries[:w]
}

func (c *Cron) PauseFunc(id int) {
	for _, x := range c.entries {
		if id == x.id {
			x.status = 1
			break
		}
	}
}

func (c *Cron) ResumeFunc(id int) {
	for _, x := range c.entries {
		if id == x.id {
			x.status = 0
			break
		}
	}
}

// AddFunc adds a Job to the Cron to be run on the given schedule.
func (c *Cron) AddJob(spec string, cmd Job) (int, error) {
	schedule, err := Parse(spec)
	if err != nil {
		return -1, err
	}
	c.count++
	c.Schedule(schedule, cmd, c.count)
	return c.count, nil
}

// Schedule adds a Job to the Cron to be run on the given schedule.
func (c *Cron) Schedule(schedule Schedule, cmd Job, id int) {
	entry := &Entry{
		Schedule: schedule,
		Job:      cmd,
		id:       id,
		status:   0,
	}
	if !c.running {
		c.entries = append(c.entries, entry)
		return
	}

	c.add <- entry
}

// Entries returns a snapshot of the cron entries.
func (c *Cron) Entries() []*Entry {
	if c.running {
		c.snapshot <- nil
		x := <-c.snapshot
		return x
	}
	return c.entrySnapshot()
}

// Start the cron scheduler in its own go-routine.
func (c *Cron) Start() {
	c.running = true
	go c.run()
}

// Run the scheduler.. this is private just due to the need to synchronize
// access to the 'running' state variable.
func (c *Cron) run() {
	// Figure out the next activation times for each entry.
	now := time.Now().Local()
	for _, entry := range c.entries {
		entry.Next = entry.Schedule.Next(now)
	}

	for {
		// Determine the next entry to run.
		sort.Sort(byTime(c.entries))

		var effective time.Time
		if len(c.entries) == 0 || c.entries[0].Next.IsZero() {
			// If there are no entries yet, just sleep - it still handles new entries
			// and stop requests.
			effective = now.AddDate(10, 0, 0)
		} else {
			effective = c.entries[0].Next
		}

		select {
		case now = <-time.After(effective.Sub(now)):
			{
				// Run every entry whose next time was this effective time.
				for _, e := range c.entries {
					if e.Next != effective {
						break
					}
					if e.status == 0 {
						go e.Job.Run()
					}
					e.Prev = e.Next
					e.Next = e.Schedule.Next(effective)
				}
				continue
			}

		case newEntry := <-c.add:
			c.entries = append(c.entries, newEntry)
			newEntry.Next = newEntry.Schedule.Next(now)

		case <-c.snapshot:
			c.snapshot <- c.entrySnapshot()

		case <-c.stop:
			return
		}

		// 'now' should be updated after newEntry and snapshot cases.
		now = time.Now().Local()
	}
}

// Stop the cron scheduler.
func (c *Cron) Stop() {
	c.stop <- struct{}{}
	c.running = false
}

// entrySnapshot returns a copy of the current cron entry list.
func (c *Cron) entrySnapshot() []*Entry {
	entries := []*Entry{}
	for _, e := range c.entries {
		entries = append(entries, &Entry{
			Schedule: e.Schedule,
			Next:     e.Next,
			Prev:     e.Prev,
			Job:      e.Job,
		})
	}
	return entries
}
<|endoftext|>


<commit_before>package dash

import (
	"fmt"
	"io"
	"os"
	"strconv"
)

const (
	WriteCode  = 1 // write a value to the store
	RemoveCode = 2 // remove a value from the store
	CloseCode  = 3 // use to notify the queues to close cleanly
)

const (
	WriteAction  = "ADD" // add a value to the store
	RemoveAction = "DEL" // remove a value from the store
)

//represents the dash database interaction.
type Dash struct {
	store     map[string]string //the map used for the key/value opts.
	workQueue chan pair         //channel for queuing writes to the internal map
	fileQueue chan pair         //channel used for queuing database file writes
	dbFile    string            //the location of the database file. Default is ./dash.db
	DoSync    bool              //should it be fast or safe?
	dbSize    int               //the size of the db file in MB
}

//used to communicate actions across the channels
type pair struct {
	key   string
	value string
	code  int
}

//create a new Dash instance
func New() (*Dash, error) {
	d := Dash{store: make(map[string]string), workQueue: make(chan pair), fileQueue: make(chan pair), dbFile: "dash.db", dbSize: 100}
	d.DoSync = true
	d.loadData()
	go d.start()
	err := d.startDiskQueue()
	return &d, err
}

//add a value to the store
func (d *Dash) Set(key, value string) {
	d.workQueue <- pair{key: key, value: value, code: WriteCode}
}

//get a value from the store
func (d *Dash) Get(key string) string {
	return d.store[key]
}

//remove a value from the store
func (d *Dash) Remove(key string) {
	d.workQueue <- pair{key: key, value: "", code: RemoveCode}
}

//waits for all pending opts to complete and cleans up the file handles
func (d *Dash) Cleanup() {
	d.workQueue <- pair{key: "", value: "", code: CloseCode}
	d.fileQueue <- pair{key: "", value: "", code: CloseCode}
}

//private things related to disk persistence and key value processing

//start the main processing queue
func (d *Dash) start() {
	for {
		select {
		case pair := <-d.workQueue:
			if pair.code == CloseCode {
				return //the database is done being used
			}
			if pair.code == WriteCode {
				d.store[pair.key] = pair.value
			} else {
				delete(d.store, pair.key)
			}
			go d.writePair(pair)
		}
	}
}

//write an opt to the file queue
func (d *Dash) writePair(p pair) {
	d.fileQueue <- p
}

//opens the db file then starts the disk queue
func (d *Dash) startDiskQueue() error {
	f, err := os.OpenFile(d.dbFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		return err
	}
	go d.runDiskQueue(f)
	return nil

}

//channel that waits and preforms file opts that are written to it.
func (d *Dash) runDiskQueue(f *os.File) {
	defer f.Close()
	for {
		select {
		case pair := <-d.fileQueue:
			if pair.code == CloseCode {
				return //the database is done being used
			}
			action := WriteAction
			if pair.code == RemoveCode {
				action = RemoveAction
			}
			f.Write([]byte(fmt.Sprintf("%d\n%s%d\n%s%d\n%s", len(action), action, len(pair.key), pair.key, len(pair.value), pair.value)))
			//we can and should optimize this. e.g: Time interval (1 second) to do a sync, how many opts are pending (only sync every 100 opts when load is high),
			//buffer the written content, etc. allow for varying levels of safety vs speed.
			if d.DoSync {
				f.Sync()
			}
		}
	}
}

//loads the data stored in the db file into the store.
func (d *Dash) loadData() error {
	f, err := os.OpenFile(d.dbFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		return err
	}
	defer f.Close()
	//should probably add some more validations to ensure the file hasn't be tampered with (although that would be very uncommon).
	offset := 0
	sliceStart := 0
	key := ""
	action := ""
	for {
		buffer, err, size := readData(f)
		if err != nil {
			return err
		}
		if size <= 0 {
			return nil
		}
		for offset < size {
			if buffer[offset] == '\n' {
				count := buffer[sliceStart:offset]
				num, err := strconv.Atoi(count)
				if err != nil {
					return err
				}
				//fmt.Println("num is:", num)
				offset++
				//check to see if the buffer needs to be expanded
				for offset+num > size {
					b, err, s := readData(f)
					if err != nil {
						return err
					}
					size = s + (size - offset)
					buffer = buffer[offset:] + b
					offset = 0
				}
				data := buffer[offset:(offset + num)]
				//fmt.Println("data is:", data)
				offset += num
				if action == "" {
					action = data
				} else if key == "" {
					key = data
				} else {
					// fmt.Println("action:", action)
					// fmt.Println("key:", key)
					// fmt.Println("value:", data)
					if action == RemoveAction {
						delete(d.store, key)
					} else {
						d.store[key] = data
					}
					action = ""
					key = ""
				}
				sliceStart = offset
			}
			offset++
		}
	}
	return nil
}

//helper method for loadData(). Reads data from the db file.
func readData(f *os.File) (string, error, int) {
	b := make([]byte, 2048)
	size, err := f.Read(b)
	if err != nil && err != io.EOF {
		return "", err, 0
	}
	if size <= 0 {
		return "", nil, size
	}
	return string(b), nil, size
}
<commit_msg>db resizing logic<commit_after>package dash

import (
	"fmt"
	"io"
	"os"
	"strconv"
)

const (
	WriteCode  = 1 // write a value to the store
	RemoveCode = 2 // remove a value from the store
	CloseCode  = 3 // use to notify the queues to close cleanly
)

const (
	WriteAction  = "ADD" // add a value to the store
	RemoveAction = "DEL" // remove a value from the store
)

//represents the dash database interaction.
type Dash struct {
	store     map[string]string //the map used for the key/value opts.
	workQueue chan pair         //channel for queuing writes to the internal map
	fileQueue chan pair         //channel used for queuing database file writes
	dbFile    string            //the location of the database file. Default is ./dash.db
	DoSync    bool              //should it be fast or safe?
	maxDBSize float64           //the max size of the db file in MB. Default is 100MB. Be careful of making this size to small as it is possible to get caught in a infinite loop of shrinking the db.
}

//used to communicate actions across the channels
type pair struct {
	key   string
	value string
	code  int
}

//create a new Dash instance
func New() (*Dash, error) {
	d := Dash{store: make(map[string]string), workQueue: make(chan pair), fileQueue: make(chan pair), dbFile: "dash.db", maxDBSize: 100}
	d.DoSync = true
	d.loadData()
	go d.start()
	err := d.startDiskQueue()
	return &d, err
}

//add a value to the store
func (d *Dash) Set(key, value string) {
	d.workQueue <- pair{key: key, value: value, code: WriteCode}
}

//get a value from the store
func (d *Dash) Get(key string) string {
	return d.store[key]
}

//remove a value from the store
func (d *Dash) Remove(key string) {
	d.workQueue <- pair{key: key, value: "", code: RemoveCode}
}

//waits for all pending opts to complete and cleans up the file handles
func (d *Dash) Cleanup() {
	d.workQueue <- pair{key: "", value: "", code: CloseCode}
	d.fileQueue <- pair{key: "", value: "", code: CloseCode}
}

//private things related to disk persistence and key value processing

//start the main processing queue
func (d *Dash) start() {
	for {
		select {
		case pair := <-d.workQueue:
			if pair.code == CloseCode {
				return //the database is done being used
			}
			if pair.code == WriteCode {
				d.store[pair.key] = pair.value
			} else {
				delete(d.store, pair.key)
			}
			go d.writePair(pair)
		}
	}
}

//write an opt to the file queue
func (d *Dash) writePair(p pair) {
	d.fileQueue <- p
}

//opens the db file then starts the disk queue
func (d *Dash) startDiskQueue() error {
	f, err := os.OpenFile(d.dbFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		return err
	}
	go d.runDiskQueue(f)
	return nil

}

//channel that waits and preforms file opts that are written to it.
func (d *Dash) runDiskQueue(f *os.File) {
	defer f.Close()
	info, _ := f.Stat()
	size := info.Size()
	for {
		select {
		case pair := <-d.fileQueue:
			if pair.code == CloseCode {
				return //the database is done being used
			}
			action := WriteAction
			if pair.code == RemoveCode {
				action = RemoveAction
			}
			b := buildDiskAction(action, pair.key, pair.value)
			f.Write(b)
			size += int64(len(b))
			//we can and should optimize this. e.g: Time interval (1 second) to do a sync, how many opts are pending (only sync every 100 opts when load is high),
			//buffer the written content, etc. allow for varying levels of safety vs speed.
			if d.DoSync {
				f.Sync()
			}
			//check if the db size hash been exceeded
			mbSize := float64(size) * 0.000001
			if mbSize > d.maxDBSize {
				//transfer the content to the temp.db
				tempPath := "temp.db"
				tempFile, err := os.Create(tempPath)
				if err != nil {
					//not sure what to do here but log
					fmt.Println("unable to create temp db file. That is a very serious error:", err)
				} else {
					for key, val := range d.store {
						b := buildDiskAction(WriteAction, key, val)
						tempFile.Write(b)
					}
					tempFile.Sync()
					os.Remove(d.dbFile)
					os.Rename(tempPath, d.dbFile)
					tempFile.Close()
					f.Close()
					f, _ = os.OpenFile(d.dbFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
					info, _ := f.Stat()
					size = info.Size()
				}
			}
		}
	}
}

//builds a disk action for writing
func buildDiskAction(action, key, value string) []byte {
	return []byte(fmt.Sprintf("%d\n%s%d\n%s%d\n%s", len(action), action, len(key), key, len(value), value))
}

//loads the data stored in the db file into the store.
func (d *Dash) loadData() error {
	f, err := os.OpenFile(d.dbFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		return err
	}
	defer f.Close()
	//should probably add some more validations to ensure the file hasn't be tampered with (although that would be very uncommon).
	offset := 0
	sliceStart := 0
	key := ""
	action := ""
	for {
		buffer, err, size := readData(f)
		if err != nil {
			return err
		}
		if size <= 0 {
			return nil
		}
		for offset < size {
			if buffer[offset] == '\n' {
				count := buffer[sliceStart:offset]
				num, err := strconv.Atoi(count)
				if err != nil {
					return err
				}
				//fmt.Println("num is:", num)
				offset++
				//check to see if the buffer needs to be expanded
				for offset+num > size {
					b, err, s := readData(f)
					if err != nil {
						return err
					}
					size = s + (size - offset)
					buffer = buffer[offset:] + b
					offset = 0
				}
				data := buffer[offset:(offset + num)]
				//fmt.Println("data is:", data)
				offset += num
				if action == "" {
					action = data
				} else if key == "" {
					key = data
				} else {
					// fmt.Println("action:", action)
					// fmt.Println("key:", key)
					// fmt.Println("value:", data)
					if action == RemoveAction {
						delete(d.store, key)
					} else {
						d.store[key] = data
					}
					action = ""
					key = ""
				}
				sliceStart = offset
			}
			offset++
		}
	}
	return nil
}

//helper method for loadData(). Reads data from the db file.
func readData(f *os.File) (string, error, int) {
	b := make([]byte, 2048)
	size, err := f.Read(b)
	if err != nil && err != io.EOF {
		return "", err, 0
	}
	if size <= 0 {
		return "", nil, size
	}
	return string(b), nil, size
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"os"

	"gopkg.in/couchbase/gocb.v1"
	log "gopkg.in/inconshreveable/log15.v2"
)

type Benchmark struct {
	Build     string   `json:"build"`
	BuildURL  string   `json:"buildURL"`
	Component string   `json:"component"`
	DateTime  string   `json:"dateTime"`
	Metric    string   `json:"metric"`
	Snapshots []string `json:"snapshots"`
	Threshold int      `json:"threshold"`
	TestCase  string   `json:"testCase"`
	Value     float64  `json:"value"`
}

type dataStore struct {
	bucket *gocb.Bucket
}

func newDataStore() *dataStore {
	hostname := os.Getenv("CB_HOST")
	if hostname == "" {
		log.Error("missing Couchbase Server hostname")
		os.Exit(1)
	}
	password := os.Getenv("CB_PASS")
	if password == "" {
		log.Error("missing password")
		os.Exit(1)
	}

	connSpecStr := fmt.Sprintf("couchbase://%s", hostname)
	cluster, err := gocb.Connect(connSpecStr)
	if err != nil {
		log.Error("failed to connect to Couchbase Server", "err", err)
		os.Exit(1)
	}

	bucket, err := cluster.OpenBucket("daily", password)
	if err != nil {
		log.Error("failed to connect to bucket", "err", err)
		os.Exit(1)
	}

	return &dataStore{bucket}
}

func hash(strings ...string) string {
	h := md5.New()
	for _, s := range strings {
		h.Write([]byte(s))
	}
	return hex.EncodeToString(h.Sum(nil))

}

func (d *dataStore) addBenchmark(b *Benchmark) error {
	docId := hash(b.Component, b.TestCase, b.Metric, b.Build)

	_, err := d.bucket.Upsert(docId, b, 0)
	if err != nil {
		log.Error("failed to insert metric", "err", err)
	}
	return err

}

type Build struct {
	Build string `json:"build"`
}

func (d *dataStore) getBuilds() (*[]string, error) {
	var builds []string

	query := gocb.NewN1qlQuery(
		"SELECT DISTINCT `build` " +
			"FROM daily " +
			"WHERE `build` IS NOT MISSING " +
			"ORDER BY `build`;")

	rows, err := ds.bucket.ExecuteN1qlQuery(query, []interface{}{})
	if err != nil {
		return nil, err
	}

	var row Build
	for rows.Next(&row) {
		builds = append(builds, row.Build)
	}
	return &builds, nil
}

type Range struct {
	Max string `json:"max"`
	Min string `json:"min"`
}

func (d *dataStore) getRange(testCase string) (*Range, error) {
	query := gocb.NewN1qlQuery(
		"SELECT MAX(`build`) AS `max`, MIN(`build`) AS `min` " +
			"FROM daily " +
			"WHERE testCase = $1;")

	params := []interface{}{testCase}
	rows, err := ds.bucket.ExecuteN1qlQuery(query, params)
	if err != nil {
		return nil, err
	}

	var row Range
	for rows.Next(&row) {
		return &row, nil
	}
	return nil, nil
}

func (d *dataStore) evalIncomplete(metric *Metric, build1, build2 string) string {
	buildRange, err := d.getRange(metric.TestCase)
	if err != nil {
		return "Incomplete"
	}

	var missingBuild string
	if metric.Results[0].Build == build1 {
		missingBuild = build2
	} else {
		missingBuild = build1
	}

	if missingBuild < buildRange.Min {
		return "New Feature"
	}
	return "Incomplete"
}

func (d *dataStore) evalComplete(metric *Metric) string {
	delta := 100 * (metric.Results[1].Value/metric.Results[0].Value - 1)

	if metric.Threshold < 0 && delta < metric.Threshold {
		return "Failed"
	}
	if metric.Threshold > 0 && delta > metric.Threshold {
		return "Failed"
	}

	return "Passed"
}

func (d *dataStore) evalStatus(build1, build2 string, comparison *[]Comparison) {
	for i, item := range *comparison {
		for j, metric := range item.Metrics {
			var status string
			if len(metric.Results) == 1 {
				status = d.evalIncomplete(&metric, build1, build2)
			} else {
				status = d.evalComplete(&metric)
			}
			(*comparison)[i].Metrics[j].Status = status
		}
	}
}

type Result struct {
	Build     string   `json:"build"`
	Snapshots []string `json:"snapshots"`
	Value     float64  `json:"value"`
}

type Metric struct {
	Metric    string   `json:"metric"`
	TestCase  string   `json:"_testCase"`
	Threshold float64  `json:"threshold"`
	Results   []Result `json:"results"`
	Status    string   `json:"status"`
}

type Comparison struct {
	Component string   `json:"component"`
	Metrics   []Metric `json:"metrics"`
}

func (d *dataStore) compare(build1, build2 string) (*[]Comparison, error) {
	comparison := []Comparison{}

	query := gocb.NewN1qlQuery(
		"SELECT q.component, " +
			"ARRAY_AGG({\"metric\": q.metric, \"_testCase\": q.testCase, \"threshold\": q.threshold, \"results\": q.results}) AS metrics " +
			"FROM ( " +
			"SELECT component, metric, testCase, threshold, " +
			"ARRAY_AGG({\"build\": `build`, \"snapshots\": snapshots, \"value\": `value`}) AS results " +
			"FROM daily " +
			"WHERE `build` = $1 OR `build` = $2 " +
			"GROUP BY component, metric, testCase, threshold) AS q " +
			"GROUP BY q.component " +
			"HAVING COUNT(*) > 0 " +
			"ORDER BY q.component;")
	params := []interface{}{build1, build2}

	rows, err := ds.bucket.ExecuteN1qlQuery(query, params)
	if err != nil {
		return nil, err
	}

	var row Comparison
	for rows.Next(&row) {
		comparison = append(comparison, row)
		row = Comparison{}
	}

	return &comparison, nil
}

func (d *dataStore) findPrevBuild(build string) (string, error) {
	previousBuild := ""

	query := gocb.NewN1qlQuery(
		"SELECT MAX(`build`) AS `build` " +
			"FROM daily " +
			"WHERE `build` < $1;")

	params := []interface{}{build}
	rows, err := ds.bucket.ExecuteN1qlQuery(query, params)
	if err != nil {
		return previousBuild, err
	}

	var row Result
	err = rows.One(&row)
	return row.Build, err
}

type Report struct {
	Component string   `json:"component"`
	Metric    string   `json:"metric"`
	TestCase  string   `json:"testCase"`
	Threshold int      `json:"threshold"`
	Results   []Result `json:"results"`
}

func (d *dataStore) getReport(build string) (string, []Report, error) {
	reports := []Report{}

	prevBuild, err := ds.findPrevBuild(build)
	if err != nil {
		return prevBuild, reports, err
	}

	query := gocb.NewN1qlQuery(
		"SELECT component, testCase, metric, threshold, ARRAY_AGG({\"build\": `build`, \"value\": `value`}) AS results " +
			"FROM daily " +
			"WHERE `build` = $1 OR `build` = $2 " +
			"GROUP BY component, testCase, metric, threshold " +
			"HAVING COUNT(*) > 1 " +
			"ORDER BY component, testCase;")

	params := []interface{}{prevBuild, build}
	rows, err := ds.bucket.ExecuteN1qlQuery(query, params)
	if err != nil {
		return prevBuild, reports, err
	}

	var row Report
	for rows.Next(&row) {
		reports = append(reports, row)
		row = Report{}
	}

	return prevBuild, reports, err
}

type History struct {
	Build    string  `json:"build"`
	BuildURL string  `json:"buildURL"`
	Value    float64 `json:"value"`
}

func (d *dataStore) getHistory(component, testCase, metric string) (*[]History, error) {
	history := []History{}

	query := gocb.NewN1qlQuery(
		"SELECT `build`, buildURL, `value` " +
			"FROM daily " +
			"WHERE component = $1 AND testCase = $2 AND metric = $3 " +
			"ORDER BY `build` DESC;")

	params := []interface{}{component, testCase, metric}
	rows, err := ds.bucket.ExecuteN1qlQuery(query, params)
	if err != nil {
		return nil, err
	}

	var row History
	for rows.Next(&row) {
		history = append(history, row)
	}

	return &history, nil
}

func (d *dataStore) getTimeline(component, testCase, metric string) (*[][]interface{}, error) {
	timeline := [][]interface{}{}

	query := gocb.NewN1qlQuery(
		"SELECT `build`, `value` " +
			"FROM daily " +
			"WHERE component = $1 AND testCase = $2 AND metric = $3 " +
			"ORDER BY `build`;")

	params := []interface{}{component, testCase, metric}
	rows, err := ds.bucket.ExecuteN1qlQuery(query, params)
	if err != nil {
		return nil, err
	}

	var row Result
	for rows.Next(&row) {
		timeline = append(timeline, []interface{}{row.Build, row.Value})
		row = Result{}
	}

	return &timeline, nil
}
<commit_msg>Fix cursor for build range<commit_after>package main

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"os"

	"gopkg.in/couchbase/gocb.v1"
	log "gopkg.in/inconshreveable/log15.v2"
)

type Benchmark struct {
	Build     string   `json:"build"`
	BuildURL  string   `json:"buildURL"`
	Component string   `json:"component"`
	DateTime  string   `json:"dateTime"`
	Metric    string   `json:"metric"`
	Snapshots []string `json:"snapshots"`
	Threshold int      `json:"threshold"`
	TestCase  string   `json:"testCase"`
	Value     float64  `json:"value"`
}

type dataStore struct {
	bucket *gocb.Bucket
}

func newDataStore() *dataStore {
	hostname := os.Getenv("CB_HOST")
	if hostname == "" {
		log.Error("missing Couchbase Server hostname")
		os.Exit(1)
	}
	password := os.Getenv("CB_PASS")
	if password == "" {
		log.Error("missing password")
		os.Exit(1)
	}

	connSpecStr := fmt.Sprintf("couchbase://%s", hostname)
	cluster, err := gocb.Connect(connSpecStr)
	if err != nil {
		log.Error("failed to connect to Couchbase Server", "err", err)
		os.Exit(1)
	}

	bucket, err := cluster.OpenBucket("daily", password)
	if err != nil {
		log.Error("failed to connect to bucket", "err", err)
		os.Exit(1)
	}

	return &dataStore{bucket}
}

func hash(strings ...string) string {
	h := md5.New()
	for _, s := range strings {
		h.Write([]byte(s))
	}
	return hex.EncodeToString(h.Sum(nil))

}

func (d *dataStore) addBenchmark(b *Benchmark) error {
	docId := hash(b.Component, b.TestCase, b.Metric, b.Build)

	_, err := d.bucket.Upsert(docId, b, 0)
	if err != nil {
		log.Error("failed to insert metric", "err", err)
	}
	return err

}

type Build struct {
	Build string `json:"build"`
}

func (d *dataStore) getBuilds() (*[]string, error) {
	var builds []string

	query := gocb.NewN1qlQuery(
		"SELECT DISTINCT `build` " +
			"FROM daily " +
			"WHERE `build` IS NOT MISSING " +
			"ORDER BY `build`;")

	rows, err := ds.bucket.ExecuteN1qlQuery(query, []interface{}{})
	if err != nil {
		return nil, err
	}

	var row Build
	for rows.Next(&row) {
		builds = append(builds, row.Build)
	}
	return &builds, nil
}

type Range struct {
	Max string `json:"max"`
	Min string `json:"min"`
}

func (d *dataStore) getRange(testCase string) (*Range, error) {
	query := gocb.NewN1qlQuery(
		"SELECT MAX(`build`) AS `max`, MIN(`build`) AS `min` " +
			"FROM daily " +
			"WHERE testCase = $1;")

	params := []interface{}{testCase}
	rows, err := ds.bucket.ExecuteN1qlQuery(query, params)
	if err != nil {
		return nil, err
	}

	var row Range
	err = rows.One(&row)
	return &row, err
}

func (d *dataStore) evalIncomplete(metric *Metric, build1, build2 string) string {
	buildRange, err := d.getRange(metric.TestCase)
	if err != nil {
		return "Incomplete"
	}

	var missingBuild string
	if metric.Results[0].Build == build1 {
		missingBuild = build2
	} else {
		missingBuild = build1
	}

	if missingBuild < buildRange.Min {
		return "New Feature"
	}
	return "Incomplete"
}

func (d *dataStore) evalComplete(metric *Metric) string {
	delta := 100 * (metric.Results[1].Value/metric.Results[0].Value - 1)

	if metric.Threshold < 0 && delta < metric.Threshold {
		return "Failed"
	}
	if metric.Threshold > 0 && delta > metric.Threshold {
		return "Failed"
	}

	return "Passed"
}

func (d *dataStore) evalStatus(build1, build2 string, comparison *[]Comparison) {
	for i, item := range *comparison {
		for j, metric := range item.Metrics {
			var status string
			if len(metric.Results) == 1 {
				status = d.evalIncomplete(&metric, build1, build2)
			} else {
				status = d.evalComplete(&metric)
			}
			(*comparison)[i].Metrics[j].Status = status
		}
	}
}

type Result struct {
	Build     string   `json:"build"`
	Snapshots []string `json:"snapshots"`
	Value     float64  `json:"value"`
}

type Metric struct {
	Metric    string   `json:"metric"`
	TestCase  string   `json:"_testCase"`
	Threshold float64  `json:"threshold"`
	Results   []Result `json:"results"`
	Status    string   `json:"status"`
}

type Comparison struct {
	Component string   `json:"component"`
	Metrics   []Metric `json:"metrics"`
}

func (d *dataStore) compare(build1, build2 string) (*[]Comparison, error) {
	comparison := []Comparison{}

	query := gocb.NewN1qlQuery(
		"SELECT q.component, " +
			"ARRAY_AGG({\"metric\": q.metric, \"_testCase\": q.testCase, \"threshold\": q.threshold, \"results\": q.results}) AS metrics " +
			"FROM ( " +
			"SELECT component, metric, testCase, threshold, " +
			"ARRAY_AGG({\"build\": `build`, \"snapshots\": snapshots, \"value\": `value`}) AS results " +
			"FROM daily " +
			"WHERE `build` = $1 OR `build` = $2 " +
			"GROUP BY component, metric, testCase, threshold) AS q " +
			"GROUP BY q.component " +
			"HAVING COUNT(*) > 0 " +
			"ORDER BY q.component;")
	params := []interface{}{build1, build2}

	rows, err := ds.bucket.ExecuteN1qlQuery(query, params)
	if err != nil {
		return nil, err
	}

	var row Comparison
	for rows.Next(&row) {
		comparison = append(comparison, row)
		row = Comparison{}
	}

	return &comparison, nil
}

func (d *dataStore) findPrevBuild(build string) (string, error) {
	previousBuild := ""

	query := gocb.NewN1qlQuery(
		"SELECT MAX(`build`) AS `build` " +
			"FROM daily " +
			"WHERE `build` < $1;")

	params := []interface{}{build}
	rows, err := ds.bucket.ExecuteN1qlQuery(query, params)
	if err != nil {
		return previousBuild, err
	}

	var row Result
	err = rows.One(&row)
	return row.Build, err
}

type Report struct {
	Component string   `json:"component"`
	Metric    string   `json:"metric"`
	TestCase  string   `json:"testCase"`
	Threshold int      `json:"threshold"`
	Results   []Result `json:"results"`
}

func (d *dataStore) getReport(build string) (string, []Report, error) {
	reports := []Report{}

	prevBuild, err := ds.findPrevBuild(build)
	if err != nil {
		return prevBuild, reports, err
	}

	query := gocb.NewN1qlQuery(
		"SELECT component, testCase, metric, threshold, ARRAY_AGG({\"build\": `build`, \"value\": `value`}) AS results " +
			"FROM daily " +
			"WHERE `build` = $1 OR `build` = $2 " +
			"GROUP BY component, testCase, metric, threshold " +
			"HAVING COUNT(*) > 1 " +
			"ORDER BY component, testCase;")

	params := []interface{}{prevBuild, build}
	rows, err := ds.bucket.ExecuteN1qlQuery(query, params)
	if err != nil {
		return prevBuild, reports, err
	}

	var row Report
	for rows.Next(&row) {
		reports = append(reports, row)
		row = Report{}
	}

	return prevBuild, reports, err
}

type History struct {
	Build    string  `json:"build"`
	BuildURL string  `json:"buildURL"`
	Value    float64 `json:"value"`
}

func (d *dataStore) getHistory(component, testCase, metric string) (*[]History, error) {
	history := []History{}

	query := gocb.NewN1qlQuery(
		"SELECT `build`, buildURL, `value` " +
			"FROM daily " +
			"WHERE component = $1 AND testCase = $2 AND metric = $3 " +
			"ORDER BY `build` DESC;")

	params := []interface{}{component, testCase, metric}
	rows, err := ds.bucket.ExecuteN1qlQuery(query, params)
	if err != nil {
		return nil, err
	}

	var row History
	for rows.Next(&row) {
		history = append(history, row)
	}

	return &history, nil
}

func (d *dataStore) getTimeline(component, testCase, metric string) (*[][]interface{}, error) {
	timeline := [][]interface{}{}

	query := gocb.NewN1qlQuery(
		"SELECT `build`, `value` " +
			"FROM daily " +
			"WHERE component = $1 AND testCase = $2 AND metric = $3 " +
			"ORDER BY `build`;")

	params := []interface{}{component, testCase, metric}
	rows, err := ds.bucket.ExecuteN1qlQuery(query, params)
	if err != nil {
		return nil, err
	}

	var row Result
	for rows.Next(&row) {
		timeline = append(timeline, []interface{}{row.Build, row.Value})
		row = Result{}
	}

	return &timeline, nil
}
<|endoftext|>