<commit_before>package appui

import (
	"context"
	"sync"
	"time"

	gizaktermui "github.com/gizak/termui"
	"github.com/moncho/dry/docker"
	"github.com/moncho/dry/ui"
)

//Monitor is a self-refreshing ui component that shows monitoring information about docker
//containers.
type Monitor struct {
	header               *MonitorTableHeader
	rows                 []*ContainerStatsRow
	openChannels         []*docker.StatsChannel
	selectedIndex        int
	offset               int
	x, y                 int
	height, width        int
	startIndex, endIndex int
	sync.RWMutex
}

//NewMonitor creates a new Monitor component that will render itself on the given screen
//at the given position and with the given width.
func NewMonitor(daemon docker.ContainerDaemon, y int) *Monitor {
	height := MainScreenAvailableHeight()
	containers := daemon.Containers(docker.ContainerFilters.Running(), docker.SortByName)
	var rows []*ContainerStatsRow
	var channels []*docker.StatsChannel
	for _, c := range containers {
		statsChan := daemon.OpenChannel(c)
		rows = append(rows, NewSelfUpdatedContainerStatsRow(statsChan, defaultMonitorTableHeader))
		channels = append(channels, statsChan)
	}
	m := &Monitor{
		header:        defaultMonitorTableHeader,
		rows:          rows,
		openChannels:  channels,
		selectedIndex: 0,
		offset:        0,
		x:             0,
		y:             y,
		height:        height,
		width:         ui.ActiveScreen.Dimensions.Width}
	m.align()
	return m
}

//RowCount returns the number of rows of this Monitor.
func (m *Monitor) RowCount() int {
	return len(m.rows)
}

//Align aligns rows
func (m *Monitor) align() {
	y := m.y
	x := m.x
	width := m.width

	m.header.SetWidth(width)
	m.header.SetY(y)
	m.header.SetX(x)

	for _, r := range m.rows {
		r.SetX(x)
		r.SetWidth(width)
	}
}

//Buffer returns the content of this monitor as a termui.Buffer
func (m *Monitor) Buffer() gizaktermui.Buffer {
	m.Lock()
	defer m.Unlock()

	buf := gizaktermui.NewBuffer()
	buf.Merge(defaultMonitorTableHeader.Buffer())
	y := m.y
	y += m.header.GetHeight()

	m.highlightSelectedRow()
	for _, r := range m.visibleRows() {
		r.SetY(y)
		y += r.GetHeight()
		buf.Merge(r.Buffer())
	}

	return buf
}

//RenderLoop makes this monitor to render itself until stopped.
func (m *Monitor) RenderLoop(ctx context.Context) {

	go func() {
		refreshTimer := time.NewTicker(500 * time.Millisecond)
		defer refreshTimer.Stop()
		defer func() {
			for _, c := range m.openChannels {
				c.Done <- struct{}{}
			}
		}()
		for {
			select {
			case <-ctx.Done():
				return
			case <-refreshTimer.C:

				ui.ActiveScreen.RenderBufferer(m)
				ui.ActiveScreen.Flush()
			}
		}
	}()

}
func (m *Monitor) highlightSelectedRow() {
	if m.RowCount() == 0 {
		return
	}
	index := ui.ActiveScreen.Cursor.Position()
	if index > m.RowCount() {
		index = m.RowCount() - 1
	}
	m.rows[m.selectedIndex].NotHighlighted()
	m.selectedIndex = index
	m.rows[m.selectedIndex].Highlighted()
}

func (m *Monitor) visibleRows() []*ContainerStatsRow {

	//no screen
	if m.height < 0 {
		return nil
	}
	rows := m.rows
	count := len(rows)
	cursor := ui.ActiveScreen.Cursor
	selected := cursor.Position()
	//everything fits
	if count <= m.height {
		return rows
	}
	//at the the start
	if selected == 0 {
		//internal state is reset
		m.startIndex = 0
		m.endIndex = m.height
		return rows[m.startIndex : m.endIndex+1]
	}

	if selected >= m.endIndex {
		if selected-m.height >= 0 {
			m.startIndex = selected - m.height
		}
		m.endIndex = selected
	}
	if selected <= m.startIndex {
		m.startIndex = m.startIndex - 1
		if selected+m.height < count {
			m.endIndex = m.startIndex + m.height
		}
	}
	start := m.startIndex
	end := m.endIndex + 1
	return rows[start:end]
}
<commit_msg>Prepare monitor widget for rendering on mount<commit_after>package appui

import (
	"context"
	"sync"
	"time"

	gizaktermui "github.com/gizak/termui"
	"github.com/moncho/dry/docker"
	"github.com/moncho/dry/ui"
)

//Monitor is a self-refreshing ui component that shows monitoring information about docker
//containers.
type Monitor struct {
	header               *MonitorTableHeader
	daemon               docker.ContainerDaemon
	rows                 []*ContainerStatsRow
	openChannels         []*docker.StatsChannel
	selectedIndex        int
	offset               int
	x, y                 int
	height, width        int
	startIndex, endIndex int
	sync.RWMutex
}

//NewMonitor creates a new Monitor component that will render itself on the given screen
//at the given position and with the given width.
func NewMonitor(daemon docker.ContainerDaemon, y int) *Monitor {
	height := MainScreenAvailableHeight()
	m := Monitor{
		header:        defaultMonitorTableHeader,
		daemon:        daemon,
		selectedIndex: 0,
		offset:        0,
		x:             0,
		y:             y,
		height:        height,
		width:         ui.ActiveScreen.Dimensions.Width}
	return &m
}

//Buffer returns the content of this monitor as a termui.Buffer
func (m *Monitor) Buffer() gizaktermui.Buffer {
	m.Lock()
	defer m.Unlock()
	y := m.y
	buf := gizaktermui.NewBuffer()

	widgetHeader := WidgetHeader("Containers", m.RowCount(), "")
	widgetHeader.Y = y
	buf.Merge(widgetHeader.Buffer())
	y += widgetHeader.Height

	m.header.SetY(y)
	buf.Merge(m.header.Buffer())

	y += m.header.Height

	m.highlightSelectedRow()
	for _, r := range m.visibleRows() {
		r.SetY(y)
		y += r.GetHeight()
		buf.Merge(r.Buffer())
	}

	return buf
}

//Mount prepares this widget for rendering
func (m *Monitor) Mount() error {
	daemon := m.daemon
	containers := daemon.Containers(docker.ContainerFilters.Running(), docker.SortByName)
	var rows []*ContainerStatsRow
	var channels []*docker.StatsChannel
	for _, c := range containers {
		statsChan := daemon.OpenChannel(c)
		rows = append(rows, NewSelfUpdatedContainerStatsRow(statsChan, defaultMonitorTableHeader))
		channels = append(channels, statsChan)
	}

	m.rows = rows
	m.openChannels = channels

	m.align()
	return nil
}

//Name returns the name of this widget
func (m *Monitor) Name() string {
	return "Monitor"
}

//OnEvent refreshes the monitor widget. The command is ignored for now.
func (m *Monitor) OnEvent(event EventCommand) error {
	m.refresh()
	return nil
}

//RenderLoop makes this monitor to render itself until stopped.
func (m *Monitor) RenderLoop(ctx context.Context) {

	go func() {
		refreshTimer := time.NewTicker(500 * time.Millisecond)
		defer refreshTimer.Stop()
		defer func() {
			for _, c := range m.openChannels {
				c.Done <- struct{}{}
			}
		}()
		for {
			select {
			case <-ctx.Done():
				return
			case <-refreshTimer.C:
				m.refresh()
			}
		}
	}()

}

//RowCount returns the number of rows of this Monitor.
func (m *Monitor) RowCount() int {
	return len(m.rows)
}

//Unmount tells this widget that it will not be rendering anymore
func (m *Monitor) Unmount() error {
	return nil
}

//Align aligns rows
func (m *Monitor) align() {
	x := m.x
	width := m.width

	m.header.SetWidth(width)
	m.header.SetX(x)

	for _, r := range m.rows {
		r.SetX(x)
		r.SetWidth(width)
	}
}

func (m *Monitor) highlightSelectedRow() {
	if m.RowCount() == 0 {
		return
	}
	index := ui.ActiveScreen.Cursor.Position()
	if index > m.RowCount() {
		index = m.RowCount() - 1
	}
	m.rows[m.selectedIndex].NotHighlighted()
	m.selectedIndex = index
	m.rows[m.selectedIndex].Highlighted()
}

func (m *Monitor) visibleRows() []*ContainerStatsRow {

	//no screen
	if m.height < 0 {
		return nil
	}
	rows := m.rows
	count := len(rows)
	cursor := ui.ActiveScreen.Cursor
	selected := cursor.Position()
	//everything fits
	if count <= m.height {
		return rows
	}
	//at the the start
	if selected == 0 {
		//internal state is reset
		m.startIndex = 0
		m.endIndex = m.height
		return rows[m.startIndex : m.endIndex+1]
	}

	if selected >= m.endIndex {
		if selected-m.height >= 0 {
			m.startIndex = selected - m.height
		}
		m.endIndex = selected
	}
	if selected <= m.startIndex {
		m.startIndex = m.startIndex - 1
		if selected+m.height < count {
			m.endIndex = m.startIndex + m.height
		}
	}
	start := m.startIndex
	end := m.endIndex + 1
	return rows[start:end]
}
func (m *Monitor) refresh() {
	ui.ActiveScreen.RenderBufferer(m)
	ui.ActiveScreen.Flush()
}
<|endoftext|>


<commit_before>package appui

import (
	"context"
	"sync"
	"time"

	gizaktermui "github.com/gizak/termui"
	"github.com/moncho/dry/docker"
	"github.com/moncho/dry/ui"
)

//Monitor is a self-refreshing ui component that shows monitoring information about docker
//containers.
type Monitor struct {
	header               *MonitorTableHeader
	daemon               docker.ContainerDaemon
	rows                 []*ContainerStatsRow
	openChannels         []*docker.StatsChannel
	selectedIndex        int
	offset               int
	x, y                 int
	height, width        int
	startIndex, endIndex int
	sync.RWMutex
}

//NewMonitor creates a new Monitor component that will render itself on the given screen
//at the given position and with the given width.
func NewMonitor(daemon docker.ContainerDaemon, y int) *Monitor {
	height := MainScreenAvailableHeight()
	m := Monitor{
		header:        defaultMonitorTableHeader,
		daemon:        daemon,
		selectedIndex: 0,
		offset:        0,
		x:             0,
		y:             y,
		height:        height,
		width:         ui.ActiveScreen.Dimensions.Width}
	return &m
}

//Buffer returns the content of this monitor as a termui.Buffer
func (m *Monitor) Buffer() gizaktermui.Buffer {
	m.Lock()
	defer m.Unlock()
	y := m.y
	buf := gizaktermui.NewBuffer()

	widgetHeader := WidgetHeader("Containers", m.RowCount(), "")
	widgetHeader.Y = y
	buf.Merge(widgetHeader.Buffer())
	y += widgetHeader.Height

	m.header.SetY(y)
	buf.Merge(m.header.Buffer())

	y += m.header.Height

	m.highlightSelectedRow()
	for _, r := range m.visibleRows() {
		r.SetY(y)
		y += r.GetHeight()
		buf.Merge(r.Buffer())
	}

	return buf
}

//Filter filters the container list by the given filter
func (m *Monitor) Filter(filter string) {

}

//Mount prepares this widget for rendering
func (m *Monitor) Mount() error {
	daemon := m.daemon
	containers := daemon.Containers(
		[]docker.ContainerFilter{docker.ContainerFilters.Running()}, docker.SortByName)
	var rows []*ContainerStatsRow
	var channels []*docker.StatsChannel
	for _, c := range containers {
		statsChan := daemon.OpenChannel(c)
		rows = append(rows, NewSelfUpdatedContainerStatsRow(statsChan, defaultMonitorTableHeader))
		channels = append(channels, statsChan)
	}

	m.rows = rows
	m.openChannels = channels

	m.align()
	return nil
}

//Name returns the name of this widget
func (m *Monitor) Name() string {
	return "Monitor"
}

//OnEvent refreshes the monitor widget. The command is ignored for now.
func (m *Monitor) OnEvent(event EventCommand) error {
	m.refresh()
	return nil
}

//RenderLoop makes this monitor to render itself until stopped.
func (m *Monitor) RenderLoop(ctx context.Context) {

	go func() {
		refreshTimer := time.NewTicker(500 * time.Millisecond)
		defer refreshTimer.Stop()
		defer func() {
			for _, c := range m.openChannels {
				c.Done <- struct{}{}
			}
		}()
		for {
			select {
			case <-ctx.Done():
				return
			case <-refreshTimer.C:
				m.refresh()
			}
		}
	}()

}

//RowCount returns the number of rows of this Monitor.
func (m *Monitor) RowCount() int {
	return len(m.rows)
}

//Sort sorts the container list
func (m *Monitor) Sort() {

}

//Unmount tells this widget that it will not be rendering anymore
func (m *Monitor) Unmount() error {
	return nil
}

//Align aligns rows
func (m *Monitor) align() {
	x := m.x
	width := m.width

	m.header.SetWidth(width)
	m.header.SetX(x)

	for _, r := range m.rows {
		r.SetX(x)
		r.SetWidth(width)
	}
}

func (m *Monitor) highlightSelectedRow() {
	if m.RowCount() == 0 {
		return
	}
	index := ui.ActiveScreen.Cursor.Position()
	if index > m.RowCount() {
		index = m.RowCount() - 1
	}

	m.selectedIndex = index
	for i, im := range m.rows {
		if i != index {
			im.NotHighlighted()
		} else {
			im.Highlighted()
		}
	}
}

func (m *Monitor) visibleRows() []*ContainerStatsRow {

	//no screen
	if m.height < 0 {
		return nil
	}
	rows := m.rows
	count := len(rows)
	cursor := ui.ActiveScreen.Cursor
	selected := cursor.Position()
	//everything fits
	if count <= m.height {
		return rows
	}
	//at the the start
	if selected == 0 {
		//internal state is reset
		m.startIndex = 0
		m.endIndex = m.height
		return rows[m.startIndex : m.endIndex+1]
	}

	if selected >= m.endIndex {
		if selected-m.height >= 0 {
			m.startIndex = selected - m.height
		}
		m.endIndex = selected
	}
	if selected <= m.startIndex {
		m.startIndex = m.startIndex - 1
		if selected+m.height < count {
			m.endIndex = m.startIndex + m.height
		}
	}
	start := m.startIndex
	end := m.endIndex + 1
	return rows[start:end]
}
func (m *Monitor) refresh() {
	ui.ActiveScreen.RenderBufferer(m)
	ui.ActiveScreen.Flush()
}
<commit_msg>Stop auto-render if unmount is called on monitor widget<commit_after>package appui

import (
	"context"
	"sync"
	"time"

	gizaktermui "github.com/gizak/termui"
	"github.com/moncho/dry/docker"
	"github.com/moncho/dry/ui"
)

//Monitor is a self-refreshing ui component that shows monitoring information about docker
//containers.
type Monitor struct {
	header               *MonitorTableHeader
	daemon               docker.ContainerDaemon
	rows                 []*ContainerStatsRow
	openChannels         []*docker.StatsChannel
	unmount              chan struct{}
	selectedIndex        int
	offset               int
	x, y                 int
	height, width        int
	startIndex, endIndex int
	sync.RWMutex
}

//NewMonitor creates a new Monitor component that will render itself on the given screen
//at the given position and with the given width.
func NewMonitor(daemon docker.ContainerDaemon, y int) *Monitor {
	height := MainScreenAvailableHeight()
	m := Monitor{
		header:        defaultMonitorTableHeader,
		daemon:        daemon,
		selectedIndex: 0,
		offset:        0,
		x:             0,
		y:             y,
		height:        height,
		width:         ui.ActiveScreen.Dimensions.Width,
		unmount:       make(chan struct{}),
	}
	return &m
}

//Buffer returns the content of this monitor as a termui.Buffer
func (m *Monitor) Buffer() gizaktermui.Buffer {
	m.Lock()
	defer m.Unlock()
	y := m.y
	buf := gizaktermui.NewBuffer()

	widgetHeader := WidgetHeader("Containers", m.RowCount(), "")
	widgetHeader.Y = y
	buf.Merge(widgetHeader.Buffer())
	y += widgetHeader.Height

	m.header.SetY(y)
	buf.Merge(m.header.Buffer())

	y += m.header.Height

	m.highlightSelectedRow()
	for _, r := range m.visibleRows() {
		r.SetY(y)
		y += r.GetHeight()
		buf.Merge(r.Buffer())
	}

	return buf
}

//Filter filters the container list by the given filter
func (m *Monitor) Filter(filter string) {

}

//Mount prepares this widget for rendering
func (m *Monitor) Mount() error {
	daemon := m.daemon
	containers := daemon.Containers(
		[]docker.ContainerFilter{docker.ContainerFilters.Running()}, docker.SortByName)
	var rows []*ContainerStatsRow
	var channels []*docker.StatsChannel
	for _, c := range containers {
		statsChan := daemon.OpenChannel(c)
		rows = append(rows, NewSelfUpdatedContainerStatsRow(statsChan, defaultMonitorTableHeader))
		channels = append(channels, statsChan)
	}

	m.rows = rows
	m.openChannels = channels

	m.align()
	return nil
}

//Name returns the name of this widget
func (m *Monitor) Name() string {
	return "Monitor"
}

//OnEvent refreshes the monitor widget. The command is ignored for now.
func (m *Monitor) OnEvent(event EventCommand) error {
	m.refresh()
	if m.RowCount() > 0 && event != nil {
		return event(m.visibleRows()[m.selectedIndex].container.ID)
	}
	return nil
}

//RenderLoop makes this monitor to render itself until stopped.
func (m *Monitor) RenderLoop(ctx context.Context) {

	go func() {
		refreshTimer := time.NewTicker(500 * time.Millisecond)
		defer refreshTimer.Stop()
		defer func() {
			for _, c := range m.openChannels {
				c.Done <- struct{}{}
			}
		}()
		for {
			select {
			case <-ctx.Done():
				return
			case <-m.unmount:
				return
			case <-refreshTimer.C:
				m.refresh()
			}
		}
	}()

}

//RowCount returns the number of rows of this Monitor.
func (m *Monitor) RowCount() int {
	return len(m.rows)
}

//Sort sorts the container list
func (m *Monitor) Sort() {

}

//Unmount tells this widget that it will not be rendering anymore
func (m *Monitor) Unmount() error {
	m.Lock()
	defer m.Unlock()
	m.unmount <- struct{}{}
	return nil
}

//Align aligns rows
func (m *Monitor) align() {
	x := m.x
	width := m.width

	m.header.SetWidth(width)
	m.header.SetX(x)

	for _, r := range m.rows {
		r.SetX(x)
		r.SetWidth(width)
	}
}

func (m *Monitor) highlightSelectedRow() {
	if m.RowCount() == 0 {
		return
	}
	index := ui.ActiveScreen.Cursor.Position()
	if index > m.RowCount() {
		index = m.RowCount() - 1
	}

	m.selectedIndex = index
	for i, im := range m.rows {
		if i != index {
			im.NotHighlighted()
		} else {
			im.Highlighted()
		}
	}
}

func (m *Monitor) visibleRows() []*ContainerStatsRow {

	//no screen
	if m.height < 0 {
		return nil
	}
	rows := m.rows
	count := len(rows)
	cursor := ui.ActiveScreen.Cursor
	selected := cursor.Position()
	//everything fits
	if count <= m.height {
		return rows
	}
	//at the the start
	if selected == 0 {
		//internal state is reset
		m.startIndex = 0
		m.endIndex = m.height
		return rows[m.startIndex : m.endIndex+1]
	}

	if selected >= m.endIndex {
		if selected-m.height >= 0 {
			m.startIndex = selected - m.height
		}
		m.endIndex = selected
	}
	if selected <= m.startIndex {
		m.startIndex = m.startIndex - 1
		if selected+m.height < count {
			m.endIndex = m.startIndex + m.height
		}
	}
	start := m.startIndex
	end := m.endIndex + 1
	return rows[start:end]
}
func (m *Monitor) refresh() {
	ui.ActiveScreen.RenderBufferer(m)
	ui.ActiveScreen.Flush()
}
<|endoftext|>


<commit_before>package drivers

import (
	"database/sql"
	"fmt"
	"net/url"
	"strings"

	_ "github.com/denisenkom/go-mssqldb"
	"github.com/pkg/errors"
	"github.com/vattle/sqlboiler/bdb"
)

// MSSQLDriver holds the database connection string and a handle
// to the database connection.
type MSSQLDriver struct {
	connStr string
	dbConn  *sql.DB
}

// NewMSSQLDriver takes the database connection details as parameters and
// returns a pointer to a MSSQLDriver object. Note that it is required to
// call MSSQLDriver.Open() and MSSQLDriver.Close() to open and close
// the database connection once an object has been obtained.
func NewMSSQLDriver(user, pass, dbname, host string, port int, sslmode string) *MSSQLDriver {
	driver := MSSQLDriver{
		connStr: MSSQLBuildQueryString(user, pass, dbname, host, port, sslmode),
	}

	return &driver
}

// MSSQLBuildQueryString builds a query string for MSSQL.
func MSSQLBuildQueryString(user, pass, dbname, host string, port int, sslmode string) string {

	query := url.Values{}
	query.Add("database", dbname)
	query.Add("encrypt", sslmode)

	u := &url.URL{
		Scheme: "sqlserver",
		User:   url.UserPassword(user, pass),
		Host:   fmt.Sprintf("%s:%d", host, port),
		// Path:  instance, // if connecting to an instance instead of a port
		RawQuery: query.Encode(),
	}

	return u.String()
}

// Open opens the database connection using the connection string
func (m *MSSQLDriver) Open() error {
	var err error
	m.dbConn, err = sql.Open("mssql", m.connStr)
	if err != nil {
		return err
	}

	return nil
}

// Close closes the database connection
func (m *MSSQLDriver) Close() {
	m.dbConn.Close()
}

// UseLastInsertID returns false for postgres
func (m *MSSQLDriver) UseLastInsertID() bool {
	return true
}

// TableNames connects to the postgres database and
// retrieves all table names from the information_schema where the
// table schema is public.
func (m *MSSQLDriver) TableNames(schema string, whitelist, blacklist []string) ([]string, error) {
	var names []string

	query := fmt.Sprintf(`select table_name from information_schema.tables where table_schema = ? and table_type = 'BASE TABLE'`)
	args := []interface{}{schema}
	if len(whitelist) > 0 {
		query += fmt.Sprintf(" and table_name in (%s);", strings.Repeat(",?", len(whitelist))[1:])
		for _, w := range whitelist {
			args = append(args, w)
		}
	} else if len(blacklist) > 0 {
		query += fmt.Sprintf(" and table_name not in (%s);", strings.Repeat(",?", len(blacklist))[1:])
		for _, b := range blacklist {
			args = append(args, b)
		}
	}

	rows, err := m.dbConn.Query(query, args...)

	if err != nil {
		return nil, err
	}

	defer rows.Close()
	for rows.Next() {
		var name string
		if err := rows.Scan(&name); err != nil {
			return nil, err
		}
		names = append(names, name)
	}

	return names, nil
}

// Columns takes a table name and attempts to retrieve the table information
// from the database information_schema.columns. It retrieves the column names
// and column types and returns those as a []Column after TranslateColumnType()
// converts the SQL types to Go types, for example: "varchar" to "string"
func (m *MSSQLDriver) Columns(schema, tableName string) ([]bdb.Column, error) {
	var columns []bdb.Column

	rows, err := m.dbConn.Query(`
	SELECT column_name,
       CASE
         WHEN CHARACTER_MAXIMUM_LENGTH IS NULL THEN data_type
         ELSE data_type + '(' + CAST(CHARACTER_MAXIMUM_LENGTH AS VARCHAR) + ')'
       END AS full_type,
       data_type,
	   column_default,
       CASE
         WHEN IS_NULLABLE = 'YES' THEN 1
         ELSE 0
       END AS is_nullable,
       CASE
         WHEN EXISTS (SELECT c.column_name
                      FROM information_schema.table_constraints tc
                        INNER JOIN information_schema.key_column_usage kcu
                                ON tc.constraint_name = kcu.constraint_name
                               AND tc.table_name = kcu.table_name
                               AND tc.table_schema = kcu.table_schema
                      WHERE c.column_name = kcu.column_name
                      AND   tc.table_name = c.table_name
                      AND   (tc.constraint_type = 'PRIMARY KEY' OR tc.constraint_type = 'UNIQUE')
                      AND   (SELECT COUNT(*)
                             FROM information_schema.key_column_usage
                             WHERE table_schema = kcu.table_schema
                             AND   table_name = tc.table_name
                             AND   constraint_name = tc.constraint_name) = 1) THEN 1
         ELSE 0
       END AS is_unique
	FROM INFORMATION_SCHEMA.columns c
	where table_name = ? and table_schema = ?;
	`, tableName, schema)

	if err != nil {
		return nil, err
	}
	defer rows.Close()

	for rows.Next() {
		var colName, colType, colFullType string
		var nullable, unique bool
		var defaultValue *string
		if err := rows.Scan(&colName, &colFullType, &colType, &defaultValue, &nullable, &unique); err != nil {
			return nil, errors.Wrapf(err, "unable to scan for table %s", tableName)
		}

		column := bdb.Column{
			Name:       colName,
			FullDBType: colFullType, // example: tinyint(1) instead of tinyint
			DBType:     colType,
			Nullable:   nullable,
			Unique:     unique,
		}

		if defaultValue != nil && *defaultValue != "NULL" {
			column.Default = *defaultValue
		}

		columns = append(columns, column)
	}

	return columns, nil
}

// PrimaryKeyInfo looks up the primary key for a table.
func (m *MSSQLDriver) PrimaryKeyInfo(schema, tableName string) (*bdb.PrimaryKey, error) {
	pkey := &bdb.PrimaryKey{}
	var err error

	query := `
	select tc.constraint_name
	from information_schema.table_constraints as tc
	where tc.table_name = ? and tc.constraint_type = 'PRIMARY KEY' and tc.table_schema = ?;`

	row := m.dbConn.QueryRow(query, tableName, schema)
	if err = row.Scan(&pkey.Name); err != nil {
		if err == sql.ErrNoRows {
			return nil, nil
		}
		return nil, err
	}

	queryColumns := `
	select kcu.column_name
	from   information_schema.key_column_usage as kcu
	where  table_name = ? and constraint_name = ? and table_schema = ?;`

	var rows *sql.Rows
	if rows, err = m.dbConn.Query(queryColumns, tableName, pkey.Name, schema); err != nil {
		return nil, err
	}
	defer rows.Close()

	var columns []string
	for rows.Next() {
		var column string

		err = rows.Scan(&column)
		if err != nil {
			return nil, err
		}

		columns = append(columns, column)
	}

	if err = rows.Err(); err != nil {
		return nil, err
	}

	pkey.Columns = columns

	return pkey, nil
}

// ForeignKeyInfo retrieves the foreign keys for a given table name.
func (m *MSSQLDriver) ForeignKeyInfo(schema, tableName string) ([]bdb.ForeignKey, error) {
	var fkeys []bdb.ForeignKey

	query := `
	SELECT
		ccu.constraint_name AS SourceConstraint
		,ccu.table_name AS SourceTable
		,ccu.column_name AS SourceColumn
		,kcu.table_name AS TargetTable
		,kcu.column_name AS TargetColumn
	FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE ccu
    INNER JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc
        ON ccu.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
    INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu
        ON kcu.CONSTRAINT_NAME = rc.UNIQUE_CONSTRAINT_NAME
	where ccu.table_schema = ? and ccu.constraint_schema = ? and ccu.table_name = ?
	`

	var rows *sql.Rows
	var err error
	if rows, err = m.dbConn.Query(query, schema, schema, tableName); err != nil {
		return nil, err
	}

	for rows.Next() {
		var fkey bdb.ForeignKey
		var sourceTable string

		fkey.Table = tableName
		err = rows.Scan(&fkey.Name, &sourceTable, &fkey.Column, &fkey.ForeignTable, &fkey.ForeignColumn)
		if err != nil {
			return nil, err
		}

		fkeys = append(fkeys, fkey)
	}

	if err = rows.Err(); err != nil {
		return nil, err
	}

	return fkeys, nil
}

// TranslateColumnType converts postgres database types to Go types, for example
// "varchar" to "string" and "bigint" to "int64". It returns this parsed data
// as a Column object.
func (m *MSSQLDriver) TranslateColumnType(c bdb.Column) bdb.Column {
	if c.Nullable {
		switch c.DBType {
		case "bit":
			c.Type = "bool"
		case "tinyint":
			c.Type = "int8"
		case "smallint":
			c.Type = "null.Int16"
		case "mediumint":
			c.Type = "null.Int32"
		case "int", "integer":
			c.Type = "null.Int"
		case "bigint":
			c.Type = "null.Int64"
		case "float":
			c.Type = "null.Float32"
		case "double", "double precision", "real":
			c.Type = "null.Float64"
		case "boolean", "bool":
			c.Type = "null.Bool"
		case "date", "datetime", "timestamp", "time":
			c.Type = "null.Time"
		case "binary", "varbinary", "tinyblob", "blob", "mediumblob", "longblob":
			c.Type = "null.Bytes"
		case "json":
			c.Type = "types.JSON"
		default:
			c.Type = "null.String"
		}
	} else {
		switch c.DBType {
		case "bit":
			c.Type = "bool"
		case "tinyint":
			c.Type = "int8"
		case "smallint":
			c.Type = "int16"
		case "mediumint":
			c.Type = "int32"
		case "int", "integer":
			c.Type = "int"
		case "bigint":
			c.Type = "int64"
		case "float":
			c.Type = "float32"
		case "double", "double precision", "real":
			c.Type = "float64"
		case "boolean", "bool":
			c.Type = "bool"
		case "date", "datetime", "timestamp", "time":
			c.Type = "time.Time"
		case "binary", "varbinary", "tinyblob", "blob", "mediumblob", "longblob":
			c.Type = "[]byte"
		case "json":
			c.Type = "types.JSON"
		default:
			c.Type = "string"
		}
	}

	return c
}

// RightQuote is the quoting character for the right side of the identifier
func (m *MSSQLDriver) RightQuote() byte {
	return '`'
}

// LeftQuote is the quoting character for the left side of the identifier
func (m *MSSQLDriver) LeftQuote() byte {
	return '`'
}

// IndexPlaceholders returns false to indicate MSSQL doesnt support indexed placeholders
func (m *MSSQLDriver) IndexPlaceholders() bool {
	return false
}
<commit_msg>Correct quote symbols<commit_after>package drivers

import (
	"database/sql"
	"fmt"
	"net/url"
	"strings"

	_ "github.com/denisenkom/go-mssqldb"
	"github.com/pkg/errors"
	"github.com/vattle/sqlboiler/bdb"
)

// MSSQLDriver holds the database connection string and a handle
// to the database connection.
type MSSQLDriver struct {
	connStr string
	dbConn  *sql.DB
}

// NewMSSQLDriver takes the database connection details as parameters and
// returns a pointer to a MSSQLDriver object. Note that it is required to
// call MSSQLDriver.Open() and MSSQLDriver.Close() to open and close
// the database connection once an object has been obtained.
func NewMSSQLDriver(user, pass, dbname, host string, port int, sslmode string) *MSSQLDriver {
	driver := MSSQLDriver{
		connStr: MSSQLBuildQueryString(user, pass, dbname, host, port, sslmode),
	}

	return &driver
}

// MSSQLBuildQueryString builds a query string for MSSQL.
func MSSQLBuildQueryString(user, pass, dbname, host string, port int, sslmode string) string {

	query := url.Values{}
	query.Add("database", dbname)
	query.Add("encrypt", sslmode)

	u := &url.URL{
		Scheme: "sqlserver",
		User:   url.UserPassword(user, pass),
		Host:   fmt.Sprintf("%s:%d", host, port),
		// Path:  instance, // if connecting to an instance instead of a port
		RawQuery: query.Encode(),
	}

	return u.String()
}

// Open opens the database connection using the connection string
func (m *MSSQLDriver) Open() error {
	var err error
	m.dbConn, err = sql.Open("mssql", m.connStr)
	if err != nil {
		return err
	}

	return nil
}

// Close closes the database connection
func (m *MSSQLDriver) Close() {
	m.dbConn.Close()
}

// UseLastInsertID returns false for postgres
func (m *MSSQLDriver) UseLastInsertID() bool {
	return true
}

// TableNames connects to the postgres database and
// retrieves all table names from the information_schema where the
// table schema is public.
func (m *MSSQLDriver) TableNames(schema string, whitelist, blacklist []string) ([]string, error) {
	var names []string

	query := fmt.Sprintf(`select table_name from information_schema.tables where table_schema = ? and table_type = 'BASE TABLE'`)
	args := []interface{}{schema}
	if len(whitelist) > 0 {
		query += fmt.Sprintf(" and table_name in (%s);", strings.Repeat(",?", len(whitelist))[1:])
		for _, w := range whitelist {
			args = append(args, w)
		}
	} else if len(blacklist) > 0 {
		query += fmt.Sprintf(" and table_name not in (%s);", strings.Repeat(",?", len(blacklist))[1:])
		for _, b := range blacklist {
			args = append(args, b)
		}
	}

	rows, err := m.dbConn.Query(query, args...)

	if err != nil {
		return nil, err
	}

	defer rows.Close()
	for rows.Next() {
		var name string
		if err := rows.Scan(&name); err != nil {
			return nil, err
		}
		names = append(names, name)
	}

	return names, nil
}

// Columns takes a table name and attempts to retrieve the table information
// from the database information_schema.columns. It retrieves the column names
// and column types and returns those as a []Column after TranslateColumnType()
// converts the SQL types to Go types, for example: "varchar" to "string"
func (m *MSSQLDriver) Columns(schema, tableName string) ([]bdb.Column, error) {
	var columns []bdb.Column

	rows, err := m.dbConn.Query(`
	SELECT column_name,
       CASE
         WHEN CHARACTER_MAXIMUM_LENGTH IS NULL THEN data_type
         ELSE data_type + '(' + CAST(CHARACTER_MAXIMUM_LENGTH AS VARCHAR) + ')'
       END AS full_type,
       data_type,
	   column_default,
       CASE
         WHEN IS_NULLABLE = 'YES' THEN 1
         ELSE 0
       END AS is_nullable,
       CASE
         WHEN EXISTS (SELECT c.column_name
                      FROM information_schema.table_constraints tc
                        INNER JOIN information_schema.key_column_usage kcu
                                ON tc.constraint_name = kcu.constraint_name
                               AND tc.table_name = kcu.table_name
                               AND tc.table_schema = kcu.table_schema
                      WHERE c.column_name = kcu.column_name
                      AND   tc.table_name = c.table_name
                      AND   (tc.constraint_type = 'PRIMARY KEY' OR tc.constraint_type = 'UNIQUE')
                      AND   (SELECT COUNT(*)
                             FROM information_schema.key_column_usage
                             WHERE table_schema = kcu.table_schema
                             AND   table_name = tc.table_name
                             AND   constraint_name = tc.constraint_name) = 1) THEN 1
         ELSE 0
       END AS is_unique
	FROM INFORMATION_SCHEMA.columns c
	where table_name = ? and table_schema = ?;
	`, tableName, schema)

	if err != nil {
		return nil, err
	}
	defer rows.Close()

	for rows.Next() {
		var colName, colType, colFullType string
		var nullable, unique bool
		var defaultValue *string
		if err := rows.Scan(&colName, &colFullType, &colType, &defaultValue, &nullable, &unique); err != nil {
			return nil, errors.Wrapf(err, "unable to scan for table %s", tableName)
		}

		column := bdb.Column{
			Name:       colName,
			FullDBType: colFullType, // example: tinyint(1) instead of tinyint
			DBType:     colType,
			Nullable:   nullable,
			Unique:     unique,
		}

		if defaultValue != nil && *defaultValue != "NULL" {
			column.Default = *defaultValue
		}

		columns = append(columns, column)
	}

	return columns, nil
}

// PrimaryKeyInfo looks up the primary key for a table.
func (m *MSSQLDriver) PrimaryKeyInfo(schema, tableName string) (*bdb.PrimaryKey, error) {
	pkey := &bdb.PrimaryKey{}
	var err error

	query := `
	select tc.constraint_name
	from information_schema.table_constraints as tc
	where tc.table_name = ? and tc.constraint_type = 'PRIMARY KEY' and tc.table_schema = ?;`

	row := m.dbConn.QueryRow(query, tableName, schema)
	if err = row.Scan(&pkey.Name); err != nil {
		if err == sql.ErrNoRows {
			return nil, nil
		}
		return nil, err
	}

	queryColumns := `
	select kcu.column_name
	from   information_schema.key_column_usage as kcu
	where  table_name = ? and constraint_name = ? and table_schema = ?;`

	var rows *sql.Rows
	if rows, err = m.dbConn.Query(queryColumns, tableName, pkey.Name, schema); err != nil {
		return nil, err
	}
	defer rows.Close()

	var columns []string
	for rows.Next() {
		var column string

		err = rows.Scan(&column)
		if err != nil {
			return nil, err
		}

		columns = append(columns, column)
	}

	if err = rows.Err(); err != nil {
		return nil, err
	}

	pkey.Columns = columns

	return pkey, nil
}

// ForeignKeyInfo retrieves the foreign keys for a given table name.
func (m *MSSQLDriver) ForeignKeyInfo(schema, tableName string) ([]bdb.ForeignKey, error) {
	var fkeys []bdb.ForeignKey

	query := `
	SELECT
		ccu.constraint_name AS SourceConstraint
		,ccu.table_name AS SourceTable
		,ccu.column_name AS SourceColumn
		,kcu.table_name AS TargetTable
		,kcu.column_name AS TargetColumn
	FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE ccu
    INNER JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc
        ON ccu.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
    INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu
        ON kcu.CONSTRAINT_NAME = rc.UNIQUE_CONSTRAINT_NAME
	where ccu.table_schema = ? and ccu.constraint_schema = ? and ccu.table_name = ?
	`

	var rows *sql.Rows
	var err error
	if rows, err = m.dbConn.Query(query, schema, schema, tableName); err != nil {
		return nil, err
	}

	for rows.Next() {
		var fkey bdb.ForeignKey
		var sourceTable string

		fkey.Table = tableName
		err = rows.Scan(&fkey.Name, &sourceTable, &fkey.Column, &fkey.ForeignTable, &fkey.ForeignColumn)
		if err != nil {
			return nil, err
		}

		fkeys = append(fkeys, fkey)
	}

	if err = rows.Err(); err != nil {
		return nil, err
	}

	return fkeys, nil
}

// TranslateColumnType converts postgres database types to Go types, for example
// "varchar" to "string" and "bigint" to "int64". It returns this parsed data
// as a Column object.
func (m *MSSQLDriver) TranslateColumnType(c bdb.Column) bdb.Column {
	if c.Nullable {
		switch c.DBType {
		case "bit":
			c.Type = "bool"
		case "tinyint":
			c.Type = "int8"
		case "smallint":
			c.Type = "null.Int16"
		case "mediumint":
			c.Type = "null.Int32"
		case "int", "integer":
			c.Type = "null.Int"
		case "bigint":
			c.Type = "null.Int64"
		case "float":
			c.Type = "null.Float32"
		case "double", "double precision", "real":
			c.Type = "null.Float64"
		case "boolean", "bool":
			c.Type = "null.Bool"
		case "date", "datetime", "timestamp", "time":
			c.Type = "null.Time"
		case "binary", "varbinary", "tinyblob", "blob", "mediumblob", "longblob":
			c.Type = "null.Bytes"
		case "json":
			c.Type = "types.JSON"
		default:
			c.Type = "null.String"
		}
	} else {
		switch c.DBType {
		case "bit":
			c.Type = "bool"
		case "tinyint":
			c.Type = "int8"
		case "smallint":
			c.Type = "int16"
		case "mediumint":
			c.Type = "int32"
		case "int", "integer":
			c.Type = "int"
		case "bigint":
			c.Type = "int64"
		case "float":
			c.Type = "float32"
		case "double", "double precision", "real":
			c.Type = "float64"
		case "boolean", "bool":
			c.Type = "bool"
		case "date", "datetime", "timestamp", "time":
			c.Type = "time.Time"
		case "binary", "varbinary", "tinyblob", "blob", "mediumblob", "longblob":
			c.Type = "[]byte"
		case "json":
			c.Type = "types.JSON"
		default:
			c.Type = "string"
		}
	}

	return c
}

// RightQuote is the quoting character for the right side of the identifier
func (m *MSSQLDriver) RightQuote() byte {
	return '\''
}

// LeftQuote is the quoting character for the left side of the identifier
func (m *MSSQLDriver) LeftQuote() byte {
	return '\''
}

// IndexPlaceholders returns false to indicate MSSQL doesnt support indexed placeholders
func (m *MSSQLDriver) IndexPlaceholders() bool {
	return false
}
<|endoftext|>


<commit_before>package datadog

import (
	"bytes"
	"fmt"
	"log"
	"strconv"
	"time"
	"strings"

	"github.com/zorkian/go-datadog-api"

	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/helper/hashcode"
)

// Work around the nested struct in https://github.com/zorkian/go-datadog-api/blob/master/dashboards.go#L16
type GraphDefintionRequests struct {
	Query   string `json:"q"`
	Stacked bool   `json:"stacked"`
}

func resourceDatadogGraph() *schema.Resource {
	return &schema.Resource{
		Create: resourceDatadogGraphCreate,
		Exists: resourceDatadogGraphExists,
		Read:   resourceDatadogGraphRead,
		Delete: resourceDatadogGraphDelete,
		Update: resourceDatadogGraphUpdate,

		Schema: map[string]*schema.Schema{
			"dashboard_id": &schema.Schema{
				Type:     schema.TypeInt,
				//Computed: true,
				Required: true,
				ForceNew: true,
			},
			"description": &schema.Schema{
				Type:     schema.TypeString,
				ForceNew: true,
				Optional: true,
			},
			"title": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"viz": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"request": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				ForceNew: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"query": &schema.Schema{
							Type:     schema.TypeString,
							Required: true,
						},
						"stacked": &schema.Schema{
							Type:     schema.TypeBool,
							Required: true,
						},
					},

				},
				Set: resourceDatadogRequestHash,
			},

			// TODO: support events.
		},
	}
}

func resourceDatadogGraphCreate(d *schema.ResourceData, meta interface{}) error {
	// This should create graphs associated with dashboards.
	// it's a virtual resource, a la "resource_vpn_connection_route"
	// hence we will need to do a bit of hacking to find out what dashboard.

	// TODO: Delete placeholder graph. See https://github.com/ojongerius/terraform-provider-datadog/issues/8

	resourceDatadogGraphUpdate(d, meta)

	Id := int(time.Now().Unix())

	d.SetId(strconv.Itoa(Id)) // Use seconds since Epoch, needs to be a string when saving.

	log.Printf("[INFO] Dashboard ID: %d", Id)

	err := resourceDatadogGraphRetrieve(d, meta)

	if err != nil {
		return err
	}

	return nil
}

func resourceDatadogGraphExists(d *schema.ResourceData, meta interface{}) (bool, error) {
	client := meta.(*datadog.Client)

	// Verify our Dashboard(s) exist
	_, err := client.GetDashboard(d.Get("dashboard_id").(int))

	if err != nil {
		if strings.EqualFold(err.Error(), "API error: 404 Not Found") {
			return false, nil
		}

		return false, fmt.Errorf("Error retrieving dashboard: %s", err)
	}

	// Verify we exist
	err = resourceDatadogGraphRead(d, meta)

	if err != nil {
		return false, err
	}

	return true, nil
}

func resourceDatadogGraphRead(d *schema.ResourceData, meta interface{}) error {
	err := resourceDatadogGraphRetrieve(d, meta)

	if err != nil {
		return err
	}

	return nil
}

func resourceDatadogGraphRetrieve(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*datadog.Client)

	// Get the dashboard(s)
	dashboard, err := client.GetDashboard(d.Get("dashboard_id").(int))

	if err != nil {
		return fmt.Errorf("Error retrieving associated dashboard: %s", err)
	}

	// Walk through the graphs
	for _, g := range dashboard.Graphs {
		// TODO: Using the title as unique identifier is 'suboptimal'. Interested in different strategies.
		if g.Title == d.Get("title") {
			log.Printf("[DEBUG] Found matching title. Start setting/saving state.")
			d.Set("dashboard_id", d.Get("dashboard_id"))
			d.Set("title", g.Title)
			d.Set("description", g.Definition)
			d.Set("viz", g.Definition.Viz)

			// Create an empty schema to hold all the requests.
			request := &schema.Set{F: resourceDatadogRequestHash}

			for _, r := range g.Definition.Requests {
				m := make(map[string]interface{})

				if r.Query != "" {
					m["query"] = r.Query
				}

				m["stacked"] = r.Stacked

				request.Add(m)
			}

			d.Set("request", request)

			return nil

		}
	}

	// If we are still around we've not found ourselves. Set SetId to empty so Terraform will create the resource for us.
	d.SetId("")

	return nil
}

func resourceDatadogGraphUpdate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*datadog.Client)

	// Get the dashboard
	dashboard, err := client.GetDashboard(d.Get("dashboard_id").(int))

	if err != nil {
		return err
	}

	log.Printf("[DEBUG] dashboard before added graph: %#v", dashboard)

	log.Printf("[DEBUG] Checking if requests have changed.")

	// Check if there are changes
	if d.HasChange("request") {

		graph_definition := datadog.Graph{}.Definition
		graph_requests := datadog.Graph{}.Definition.Requests
		graph_definition.Viz = d.Get("viz").(string)

		log.Printf("[DEBUG] Request has changed.")
		o, n := d.GetChange("request")
		ors := o.(*schema.Set).Difference(n.(*schema.Set))
		nrs := n.(*schema.Set).Difference(o.(*schema.Set))

		// Loop through all the old requests and delete any obsolete ones
		for _, request := range ors.List() {
			m := request.(map[string]interface{})

			// TODO: implement
			// Delete the query as it no longer exists in the config
			log.Printf("[DEBUG] Deleting graph query %s", m["query"].(string))
			log.Printf("[DEBUG] Deleting graph stacked %t", m["stacked"].(bool))

		}
		// Loop through all the new requests and append them
		for _, request := range nrs.List() {
			m := request.(map[string]interface{})

			// Add the request
			log.Printf("[DEBUG] Adding graph query %s", m["query"].(string))
			log.Printf("[DEBUG] Adding graph stacked %t", m["stacked"].(bool))
			graph_requests = append(graph_requests, GraphDefintionRequests{Query: m["query"].(string),
				Stacked: m["stacked"].(bool)})
		}

		// Add requests to the graph definition
		graph_definition.Requests = graph_requests
		the_graph := datadog.Graph{Title: d.Get("title").(string), Definition: graph_definition}

		dashboard.Graphs = append(dashboard.Graphs, the_graph) // Should be done for each

		log.Printf("[DEBUG] dashboard after adding graph: %#v", dashboard)

		// Update/commit
		err = client.UpdateDashboard(dashboard)

		if err != nil {
			return err
		}
	} else {
		log.Printf("[DEBUG] No changes detected, nothing to do here.")
	}

	return nil

	// TODO: still need this?
	//return resourceDatadogGraphRead(d, meta)
}

func resourceDatadogGraphDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*datadog.Client)

	// Get the dashboard
	dashboard, err := client.GetDashboard(d.Get("dashboard_id").(int))

	if err != nil {
		return fmt.Errorf("Error retrieving associated dashboard: %s", err)
	}

	// Build a new slice of graphs, without the nominee to deleted.
	// TODO: Use the set for this.
	new_graphs := []datadog.Graph{}
	for _, r := range dashboard.Graphs {
		// TODO: efficiently test if the are the same for this POC we'll just match on title
		if r.Title == d.Get("title") {
			continue
		} else {
			new_graphs = append(new_graphs, r)
		}
	}

	dashboard.Graphs = new_graphs

	// Update/commit
	err = client.UpdateDashboard(dashboard)

	if err != nil {
		return err
	}

	err = resourceDatadogGraphRetrieve(d, meta)

	if err != nil {
		return err
	}

	return nil
}

func resourceDatadogRequestHash(v interface{}) int{
	var buf bytes.Buffer
	m := v.(map[string]interface{})

	if v, ok := m["query"];  ok {
		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
	}

	if v, ok := m["stacked"];  ok {
		buf.WriteString(fmt.Sprintf("%t-", v.(bool)))
	}

	return hashcode.String(buf.String())
}
<commit_msg>Graphs do not have descriptions.<commit_after>package datadog

import (
	"bytes"
	"fmt"
	"log"
	"strconv"
	"time"
	"strings"

	"github.com/zorkian/go-datadog-api"

	"github.com/hashicorp/terraform/helper/schema"
	"github.com/hashicorp/terraform/helper/hashcode"
)

// Work around the nested struct in https://github.com/zorkian/go-datadog-api/blob/master/dashboards.go#L16
type GraphDefintionRequests struct {
	Query   string `json:"q"`
	Stacked bool   `json:"stacked"`
}

func resourceDatadogGraph() *schema.Resource {
	return &schema.Resource{
		Create: resourceDatadogGraphCreate,
		Exists: resourceDatadogGraphExists,
		Read:   resourceDatadogGraphRead,
		Delete: resourceDatadogGraphDelete,
		Update: resourceDatadogGraphUpdate,

		Schema: map[string]*schema.Schema{
			"dashboard_id": &schema.Schema{
				Type:     schema.TypeInt,
				//Computed: true,
				Required: true,
				ForceNew: true,
			},
			"title": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"viz": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"request": &schema.Schema{
				Type:     schema.TypeSet,
				Optional: true,
				ForceNew: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"query": &schema.Schema{
							Type:     schema.TypeString,
							Required: true,
						},
						"stacked": &schema.Schema{
							Type:     schema.TypeBool,
							Required: true,
						},
					},

				},
				Set: resourceDatadogRequestHash,
			},

			// TODO: support events.
		},
	}
}

func resourceDatadogGraphCreate(d *schema.ResourceData, meta interface{}) error {
	// This should create graphs associated with dashboards.
	// it's a virtual resource, a la "resource_vpn_connection_route"
	// hence we will need to do a bit of hacking to find out what dashboard.

	// TODO: Delete placeholder graph. See https://github.com/ojongerius/terraform-provider-datadog/issues/8

	resourceDatadogGraphUpdate(d, meta)

	Id := int(time.Now().Unix())

	d.SetId(strconv.Itoa(Id)) // Use seconds since Epoch, needs to be a string when saving.

	log.Printf("[INFO] Dashboard ID: %d", Id)

	err := resourceDatadogGraphRetrieve(d, meta)

	if err != nil {
		return err
	}

	return nil
}

func resourceDatadogGraphExists(d *schema.ResourceData, meta interface{}) (bool, error) {
	client := meta.(*datadog.Client)

	// Verify our Dashboard(s) exist
	_, err := client.GetDashboard(d.Get("dashboard_id").(int))

	if err != nil {
		if strings.EqualFold(err.Error(), "API error: 404 Not Found") {
			return false, nil
		}

		return false, fmt.Errorf("Error retrieving dashboard: %s", err)
	}

	// Verify we exist
	err = resourceDatadogGraphRead(d, meta)

	if err != nil {
		return false, err
	}

	return true, nil
}

func resourceDatadogGraphRead(d *schema.ResourceData, meta interface{}) error {
	err := resourceDatadogGraphRetrieve(d, meta)

	if err != nil {
		return err
	}

	return nil
}

func resourceDatadogGraphRetrieve(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*datadog.Client)

	// Get the dashboard(s)
	dashboard, err := client.GetDashboard(d.Get("dashboard_id").(int))

	if err != nil {
		return fmt.Errorf("Error retrieving associated dashboard: %s", err)
	}

	// Walk through the graphs
	for _, g := range dashboard.Graphs {
		// TODO: Using the title as unique identifier is 'suboptimal'. Interested in different strategies.
		if g.Title == d.Get("title") {
			log.Printf("[DEBUG] Found matching title. Start setting/saving state.")
			d.Set("dashboard_id", d.Get("dashboard_id"))
			d.Set("title", g.Title)
			d.Set("viz", g.Definition.Viz)

			// Create an empty schema to hold all the requests.
			request := &schema.Set{F: resourceDatadogRequestHash}

			for _, r := range g.Definition.Requests {
				m := make(map[string]interface{})

				if r.Query != "" {
					m["query"] = r.Query
				}

				m["stacked"] = r.Stacked

				request.Add(m)
			}

			d.Set("request", request)

			return nil

		}
	}

	// If we are still around we've not found ourselves. Set SetId to empty so Terraform will create the resource for us.
	d.SetId("")

	return nil
}

func resourceDatadogGraphUpdate(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*datadog.Client)

	// Get the dashboard
	dashboard, err := client.GetDashboard(d.Get("dashboard_id").(int))

	if err != nil {
		return err
	}

	log.Printf("[DEBUG] dashboard before added graph: %#v", dashboard)

	log.Printf("[DEBUG] Checking if requests have changed.")

	// Check if there are changes
	if d.HasChange("request") {

		graph_definition := datadog.Graph{}.Definition
		graph_requests := datadog.Graph{}.Definition.Requests
		graph_definition.Viz = d.Get("viz").(string)

		log.Printf("[DEBUG] Request has changed.")
		o, n := d.GetChange("request")
		ors := o.(*schema.Set).Difference(n.(*schema.Set))
		nrs := n.(*schema.Set).Difference(o.(*schema.Set))

		// Loop through all the old requests and delete any obsolete ones
		for _, request := range ors.List() {
			m := request.(map[string]interface{})

			// TODO: implement
			// Delete the query as it no longer exists in the config
			log.Printf("[DEBUG] Deleting graph query %s", m["query"].(string))
			log.Printf("[DEBUG] Deleting graph stacked %t", m["stacked"].(bool))

		}
		// Loop through all the new requests and append them
		for _, request := range nrs.List() {
			m := request.(map[string]interface{})

			// Add the request
			log.Printf("[DEBUG] Adding graph query %s", m["query"].(string))
			log.Printf("[DEBUG] Adding graph stacked %t", m["stacked"].(bool))
			graph_requests = append(graph_requests, GraphDefintionRequests{Query: m["query"].(string),
				Stacked: m["stacked"].(bool)})
		}

		// Add requests to the graph definition
		graph_definition.Requests = graph_requests
		the_graph := datadog.Graph{Title: d.Get("title").(string), Definition: graph_definition}

		dashboard.Graphs = append(dashboard.Graphs, the_graph) // Should be done for each

		log.Printf("[DEBUG] dashboard after adding graph: %#v", dashboard)

		// Update/commit
		err = client.UpdateDashboard(dashboard)

		if err != nil {
			return err
		}
	} else {
		log.Printf("[DEBUG] No changes detected, nothing to do here.")
	}

	return nil

	// TODO: still need this?
	//return resourceDatadogGraphRead(d, meta)
}

func resourceDatadogGraphDelete(d *schema.ResourceData, meta interface{}) error {
	client := meta.(*datadog.Client)

	// Get the dashboard
	dashboard, err := client.GetDashboard(d.Get("dashboard_id").(int))

	if err != nil {
		return fmt.Errorf("Error retrieving associated dashboard: %s", err)
	}

	// Build a new slice of graphs, without the nominee to deleted.
	// TODO: Use the set for this.
	new_graphs := []datadog.Graph{}
	for _, r := range dashboard.Graphs {
		// TODO: efficiently test if the are the same for this POC we'll just match on title
		if r.Title == d.Get("title") {
			continue
		} else {
			new_graphs = append(new_graphs, r)
		}
	}

	dashboard.Graphs = new_graphs

	// Update/commit
	err = client.UpdateDashboard(dashboard)

	if err != nil {
		return err
	}

	err = resourceDatadogGraphRetrieve(d, meta)

	if err != nil {
		return err
	}

	return nil
}

func resourceDatadogRequestHash(v interface{}) int{
	var buf bytes.Buffer
	m := v.(map[string]interface{})

	if v, ok := m["query"];  ok {
		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
	}

	if v, ok := m["stacked"];  ok {
		buf.WriteString(fmt.Sprintf("%t-", v.(bool)))
	}

	return hashcode.String(buf.String())
}
<|endoftext|>


<commit_before>package irc

import (
	"sync"
)

// Channel represents an IRC channel
type Channel struct {
	Name, topic string

	sync.RWMutex
}

// NewChannel creates a new Channel named 'name'
func NewChannel(name string) *Channel {
	return &Channel{Name: name}
}

// Topic sets the channels' topic to 't'
func (c *Channel) Topic(t string) {
	c.Lock()
	defer c.Unlock()

	c.topic = t
}

// GetTopic returns the channels' topic
func (c *Channel) GetTopic() string {
	c.RLock()
	defer c.RUnlock()

	return c.topic
}
<commit_msg>added a user collection to channels<commit_after>package irc

import (
	"sync"
)

// Channel represents an IRC channel
type Channel struct {
	Name, topic string
	users       *Users

	sync.RWMutex
}

// NewChannel creates a new Channel named 'name'
func NewChannel(name string) *Channel {
	return &Channel{
		Name:  name,
		users: &Users{m: make(map[string]*User)},
	}
}

// Users returns the user collection for the channel
func (c *Channel) Users() *Users {
	return c.Users()
}

// Topic sets the channels' topic to 't'
func (c *Channel) Topic(t string) {
	c.Lock()
	defer c.Unlock()

	c.topic = t
}

// GetTopic returns the channels' topic
func (c *Channel) GetTopic() string {
	c.RLock()
	defer c.RUnlock()

	return c.topic
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Shivaram Lingamneni <slingamn@cs.stanford.edu>
// released under the MIT license

package irc

import (
	"github.com/oragono/oragono/irc/isupport"
	"github.com/oragono/oragono/irc/modes"
)

func (server *Server) Config() *Config {
	server.configurableStateMutex.RLock()
	defer server.configurableStateMutex.RUnlock()
	return server.config
}

func (server *Server) ISupport() *isupport.List {
	server.configurableStateMutex.RLock()
	defer server.configurableStateMutex.RUnlock()
	return server.isupport
}

func (server *Server) Limits() Limits {
	return server.Config().Limits
}

func (server *Server) Password() []byte {
	return server.Config().Server.passwordBytes
}

func (server *Server) RecoverFromErrors() bool {
	return *server.Config().Debug.RecoverFromErrors
}

func (server *Server) DefaultChannelModes() modes.Modes {
	return server.Config().Channels.defaultModes
}

func (server *Server) ChannelRegistrationEnabled() bool {
	return server.Config().Channels.Registration.Enabled
}

func (server *Server) AccountConfig() *AccountConfig {
	return &server.Config().Accounts
}

func (server *Server) FakelagConfig() *FakelagConfig {
	return &server.Config().Fakelag
}

func (server *Server) GetOperator(name string) (oper *Oper) {
	name, err := CasefoldName(name)
	if err != nil {
		return
	}
	server.configurableStateMutex.RLock()
	defer server.configurableStateMutex.RUnlock()
	return server.config.operators[name]
}

func (client *Client) Nick() string {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	return client.nick
}

func (client *Client) NickMaskString() string {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	return client.nickMaskString
}

func (client *Client) NickCasefolded() string {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	return client.nickCasefolded
}

func (client *Client) NickMaskCasefolded() string {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	return client.nickMaskCasefolded
}

func (client *Client) Username() string {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	return client.username
}

func (client *Client) Hostname() string {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	return client.hostname
}

func (client *Client) Realname() string {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	return client.realname
}

// uniqueIdentifiers returns the strings for which the server enforces per-client
// uniqueness/ownership; no two clients can have colliding casefolded nicks or
// skeletons.
func (client *Client) uniqueIdentifiers() (nickCasefolded string, skeleton string) {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	return client.nickCasefolded, client.skeleton
}

func (client *Client) ResumeToken() string {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	return client.resumeToken
}

func (client *Client) Oper() *Oper {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	return client.oper
}

func (client *Client) Registered() bool {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	return client.registered
}

func (client *Client) Destroyed() bool {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	return client.isDestroyed
}

func (client *Client) Account() string {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	return client.account
}

func (client *Client) AccountName() string {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	return client.accountName
}

func (client *Client) SetAccountName(account string) (changed bool) {
	var casefoldedAccount string
	var err error
	if account != "" {
		if casefoldedAccount, err = CasefoldName(account); err != nil {
			return
		}
	}

	client.stateMutex.Lock()
	defer client.stateMutex.Unlock()
	changed = client.account != casefoldedAccount
	client.account = casefoldedAccount
	client.accountName = account
	return
}

func (client *Client) HasMode(mode modes.Mode) bool {
	// client.flags has its own synch
	return client.flags.HasMode(mode)
}

func (client *Client) SetMode(mode modes.Mode, on bool) bool {
	return client.flags.SetMode(mode, on)
}

func (client *Client) Channels() (result []*Channel) {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	length := len(client.channels)
	result = make([]*Channel, length)
	i := 0
	for channel := range client.channels {
		result[i] = channel
		i++
	}
	return
}

func (client *Client) WhoWas() (result WhoWas) {
	return client.Details().WhoWas
}

func (client *Client) Details() (result ClientDetails) {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()

	result.nick = client.nick
	result.nickCasefolded = client.nickCasefolded
	result.username = client.username
	result.hostname = client.username
	result.realname = client.realname
	result.nickMask = client.nickMaskString
	result.nickMaskCasefolded = client.nickMaskCasefolded
	result.account = client.account
	result.accountName = client.accountName
	return
}

func (channel *Channel) Name() string {
	channel.stateMutex.RLock()
	defer channel.stateMutex.RUnlock()
	return channel.name
}

func (channel *Channel) setName(name string) {
	channel.stateMutex.Lock()
	defer channel.stateMutex.Unlock()
	channel.name = name
}

func (channel *Channel) NameCasefolded() string {
	channel.stateMutex.RLock()
	defer channel.stateMutex.RUnlock()
	return channel.nameCasefolded
}

func (channel *Channel) setNameCasefolded(nameCasefolded string) {
	channel.stateMutex.Lock()
	defer channel.stateMutex.Unlock()
	channel.nameCasefolded = nameCasefolded
}

func (channel *Channel) Members() (result []*Client) {
	channel.stateMutex.RLock()
	defer channel.stateMutex.RUnlock()
	return channel.membersCache
}

func (channel *Channel) setUserLimit(limit int) {
	channel.stateMutex.Lock()
	channel.userLimit = limit
	channel.stateMutex.Unlock()
}

func (channel *Channel) Key() string {
	channel.stateMutex.RLock()
	defer channel.stateMutex.RUnlock()
	return channel.key
}

func (channel *Channel) setKey(key string) {
	channel.stateMutex.Lock()
	defer channel.stateMutex.Unlock()
	channel.key = key
}

func (channel *Channel) Founder() string {
	channel.stateMutex.RLock()
	defer channel.stateMutex.RUnlock()
	return channel.registeredFounder
}
<commit_msg>Fix usernames being used in place of hostnames.<commit_after>// Copyright (c) 2017 Shivaram Lingamneni <slingamn@cs.stanford.edu>
// released under the MIT license

package irc

import (
	"github.com/oragono/oragono/irc/isupport"
	"github.com/oragono/oragono/irc/modes"
)

func (server *Server) Config() *Config {
	server.configurableStateMutex.RLock()
	defer server.configurableStateMutex.RUnlock()
	return server.config
}

func (server *Server) ISupport() *isupport.List {
	server.configurableStateMutex.RLock()
	defer server.configurableStateMutex.RUnlock()
	return server.isupport
}

func (server *Server) Limits() Limits {
	return server.Config().Limits
}

func (server *Server) Password() []byte {
	return server.Config().Server.passwordBytes
}

func (server *Server) RecoverFromErrors() bool {
	return *server.Config().Debug.RecoverFromErrors
}

func (server *Server) DefaultChannelModes() modes.Modes {
	return server.Config().Channels.defaultModes
}

func (server *Server) ChannelRegistrationEnabled() bool {
	return server.Config().Channels.Registration.Enabled
}

func (server *Server) AccountConfig() *AccountConfig {
	return &server.Config().Accounts
}

func (server *Server) FakelagConfig() *FakelagConfig {
	return &server.Config().Fakelag
}

func (server *Server) GetOperator(name string) (oper *Oper) {
	name, err := CasefoldName(name)
	if err != nil {
		return
	}
	server.configurableStateMutex.RLock()
	defer server.configurableStateMutex.RUnlock()
	return server.config.operators[name]
}

func (client *Client) Nick() string {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	return client.nick
}

func (client *Client) NickMaskString() string {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	return client.nickMaskString
}

func (client *Client) NickCasefolded() string {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	return client.nickCasefolded
}

func (client *Client) NickMaskCasefolded() string {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	return client.nickMaskCasefolded
}

func (client *Client) Username() string {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	return client.username
}

func (client *Client) Hostname() string {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	return client.hostname
}

func (client *Client) Realname() string {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	return client.realname
}

// uniqueIdentifiers returns the strings for which the server enforces per-client
// uniqueness/ownership; no two clients can have colliding casefolded nicks or
// skeletons.
func (client *Client) uniqueIdentifiers() (nickCasefolded string, skeleton string) {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	return client.nickCasefolded, client.skeleton
}

func (client *Client) ResumeToken() string {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	return client.resumeToken
}

func (client *Client) Oper() *Oper {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	return client.oper
}

func (client *Client) Registered() bool {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	return client.registered
}

func (client *Client) Destroyed() bool {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	return client.isDestroyed
}

func (client *Client) Account() string {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	return client.account
}

func (client *Client) AccountName() string {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	return client.accountName
}

func (client *Client) SetAccountName(account string) (changed bool) {
	var casefoldedAccount string
	var err error
	if account != "" {
		if casefoldedAccount, err = CasefoldName(account); err != nil {
			return
		}
	}

	client.stateMutex.Lock()
	defer client.stateMutex.Unlock()
	changed = client.account != casefoldedAccount
	client.account = casefoldedAccount
	client.accountName = account
	return
}

func (client *Client) HasMode(mode modes.Mode) bool {
	// client.flags has its own synch
	return client.flags.HasMode(mode)
}

func (client *Client) SetMode(mode modes.Mode, on bool) bool {
	return client.flags.SetMode(mode, on)
}

func (client *Client) Channels() (result []*Channel) {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()
	length := len(client.channels)
	result = make([]*Channel, length)
	i := 0
	for channel := range client.channels {
		result[i] = channel
		i++
	}
	return
}

func (client *Client) WhoWas() (result WhoWas) {
	return client.Details().WhoWas
}

func (client *Client) Details() (result ClientDetails) {
	client.stateMutex.RLock()
	defer client.stateMutex.RUnlock()

	result.nick = client.nick
	result.nickCasefolded = client.nickCasefolded
	result.username = client.username
	result.hostname = client.hostname
	result.realname = client.realname
	result.nickMask = client.nickMaskString
	result.nickMaskCasefolded = client.nickMaskCasefolded
	result.account = client.account
	result.accountName = client.accountName
	return
}

func (channel *Channel) Name() string {
	channel.stateMutex.RLock()
	defer channel.stateMutex.RUnlock()
	return channel.name
}

func (channel *Channel) setName(name string) {
	channel.stateMutex.Lock()
	defer channel.stateMutex.Unlock()
	channel.name = name
}

func (channel *Channel) NameCasefolded() string {
	channel.stateMutex.RLock()
	defer channel.stateMutex.RUnlock()
	return channel.nameCasefolded
}

func (channel *Channel) setNameCasefolded(nameCasefolded string) {
	channel.stateMutex.Lock()
	defer channel.stateMutex.Unlock()
	channel.nameCasefolded = nameCasefolded
}

func (channel *Channel) Members() (result []*Client) {
	channel.stateMutex.RLock()
	defer channel.stateMutex.RUnlock()
	return channel.membersCache
}

func (channel *Channel) setUserLimit(limit int) {
	channel.stateMutex.Lock()
	channel.userLimit = limit
	channel.stateMutex.Unlock()
}

func (channel *Channel) Key() string {
	channel.stateMutex.RLock()
	defer channel.stateMutex.RUnlock()
	return channel.key
}

func (channel *Channel) setKey(key string) {
	channel.stateMutex.Lock()
	defer channel.stateMutex.Unlock()
	channel.key = key
}

func (channel *Channel) Founder() string {
	channel.stateMutex.RLock()
	defer channel.stateMutex.RUnlock()
	return channel.registeredFounder
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"io"
	"net"
	"time"

	"google.golang.org/grpc"

	log "github.com/Sirupsen/logrus"
	"github.com/stianeikeland/go-rpio"
	pb "github.com/viru/berrybot/proto"
)

// server is used to implement hellowrld.GreeterServer.
type server struct{}

func (s *server) Drive(stream pb.Driver_DriveServer) error {
	err := rpio.Open()
	if err != nil {
		log.Fatalf("can't open rpio: %v", err)
	}
	defer rpio.Close()
	leftOn := rpio.Pin(23)
	leftFwd := rpio.Pin(4)
	rightOn := rpio.Pin(24)
	rightFwd := rpio.Pin(17)
	leftOn.Output()
	leftFwd.Output()
	rightOn.Output()
	rightFwd.Output()
	for {
		d, err := stream.Recv()
		if err == io.EOF {
			return stream.SendAndClose(&pb.DirectionReply{
				Ok: true,
			})
		}
		if err != nil {
			return err
		}
		log.WithFields(log.Fields{
			"dx": d.Dx,
			"dy": d.Dy,
		}).Info("Direction")
		switch {
		// Full stop.
		case d.Dy > -5 && d.Dy < 5 && d.Dx > -5 && d.Dx < 5:
			leftOn.Low()
			rightOn.Low()
		// Forward.
		case d.Dy > 5 && d.Dx > -5 && d.Dx < 5:
			leftOn.High()
			leftFwd.High()
			rightOn.High()
			rightFwd.High()
		// Backward.
		case d.Dy < -5 && d.Dx > -5 && d.Dx < 5:
			leftOn.High()
			leftFwd.Low()
			rightOn.High()
			rightFwd.Low()
		// Sharp right.
		case d.Dx > 5 && d.Dy > -5 && d.Dy < 5:
			leftOn.High()
			leftFwd.High()
			rightOn.High()
			rightFwd.Low()
		// Sharp left.
		case d.Dx < -5 && d.Dy > -5 && d.Dy < 5:
			leftOn.High()
			leftFwd.High()
			rightOn.High()
			rightFwd.Low()
		// Forward + right.
		case d.Dx > 5 && d.Dy > 5:
			leftOn.High()
			leftFwd.High()
			rightOn.Low()
			rightFwd.High()
		// Forward + left.
		case d.Dx < -5 && d.Dy > 5:
			leftOn.Low()
			leftFwd.High()
			rightOn.High()
			rightFwd.High()
		// Backward + right.
		case d.Dx > 5 && d.Dy < -5:
			leftOn.High()
			leftFwd.Low()
			rightOn.Low()
			rightFwd.Low()
		// Backward + left.
		case d.Dx < -5 && d.Dy < -5:
			leftOn.Low()
			leftFwd.Low()
			rightOn.High()
			rightFwd.Low()
		}
	}
}

var grpcPort = flag.String("grpc-port", "31337", "gRPC listen port")

func main() {
	flag.Parse()
	// Listen for GRPC connections.
	lis, err := net.Listen("tcp", ":"+*grpcPort)
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	s := grpc.NewServer()
	pb.RegisterDriverServer(s, &server{})

	// Open broadcast connection.
	c, err := net.ListenPacket("udp", ":0")
	if err != nil {
		log.Fatal(err)
	}
	defer c.Close()

	broadcastAddr := "255.255.255.255:8032"
	dst, err := net.ResolveUDPAddr("udp", broadcastAddr)
	if err != nil {
		log.Fatal(err)
	}

	go func() {
		log.Infof("Starting to broadcast our port %s on %s", grpcPort, broadcastAddr)
		for {
			if _, err := c.WriteTo([]byte(*grpcPort), dst); err != nil {
				log.Fatal(err)
			}
			time.Sleep(time.Second)
		}
	}()

	// Start serving GRPC.
	log.Fatal(s.Serve(lis))
}
<commit_msg>Don't exit when unable to broadcast our port<commit_after>package main

import (
	"flag"
	"io"
	"net"
	"time"

	"google.golang.org/grpc"

	log "github.com/Sirupsen/logrus"
	"github.com/stianeikeland/go-rpio"
	pb "github.com/viru/berrybot/proto"
)

// server is used to implement hellowrld.GreeterServer.
type server struct{}

func (s *server) Drive(stream pb.Driver_DriveServer) error {
	err := rpio.Open()
	if err != nil {
		log.Fatalf("can't open rpio: %v", err)
	}
	defer rpio.Close()
	leftOn := rpio.Pin(23)
	leftFwd := rpio.Pin(4)
	rightOn := rpio.Pin(24)
	rightFwd := rpio.Pin(17)
	leftOn.Output()
	leftFwd.Output()
	rightOn.Output()
	rightFwd.Output()
	for {
		d, err := stream.Recv()
		if err == io.EOF {
			return stream.SendAndClose(&pb.DirectionReply{
				Ok: true,
			})
		}
		if err != nil {
			return err
		}
		log.WithFields(log.Fields{
			"dx": d.Dx,
			"dy": d.Dy,
		}).Info("Direction")
		switch {
		// Full stop.
		case d.Dy > -5 && d.Dy < 5 && d.Dx > -5 && d.Dx < 5:
			leftOn.Low()
			rightOn.Low()
		// Forward.
		case d.Dy > 5 && d.Dx > -5 && d.Dx < 5:
			leftOn.High()
			leftFwd.High()
			rightOn.High()
			rightFwd.High()
		// Backward.
		case d.Dy < -5 && d.Dx > -5 && d.Dx < 5:
			leftOn.High()
			leftFwd.Low()
			rightOn.High()
			rightFwd.Low()
		// Sharp right.
		case d.Dx > 5 && d.Dy > -5 && d.Dy < 5:
			leftOn.High()
			leftFwd.High()
			rightOn.High()
			rightFwd.Low()
		// Sharp left.
		case d.Dx < -5 && d.Dy > -5 && d.Dy < 5:
			leftOn.High()
			leftFwd.High()
			rightOn.High()
			rightFwd.Low()
		// Forward + right.
		case d.Dx > 5 && d.Dy > 5:
			leftOn.High()
			leftFwd.High()
			rightOn.Low()
			rightFwd.High()
		// Forward + left.
		case d.Dx < -5 && d.Dy > 5:
			leftOn.Low()
			leftFwd.High()
			rightOn.High()
			rightFwd.High()
		// Backward + right.
		case d.Dx > 5 && d.Dy < -5:
			leftOn.High()
			leftFwd.Low()
			rightOn.Low()
			rightFwd.Low()
		// Backward + left.
		case d.Dx < -5 && d.Dy < -5:
			leftOn.Low()
			leftFwd.Low()
			rightOn.High()
			rightFwd.Low()
		}
	}
}

var grpcPort = flag.String("grpc-port", "31337", "gRPC listen port")

func main() {
	flag.Parse()
	// Listen for GRPC connections.
	lis, err := net.Listen("tcp", ":"+*grpcPort)
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	s := grpc.NewServer()
	pb.RegisterDriverServer(s, &server{})

	// Open broadcast connection.
	c, err := net.ListenPacket("udp", ":0")
	if err != nil {
		log.Fatal(err)
	}
	defer c.Close()

	broadcastAddr := "255.255.255.255:8032"
	dst, err := net.ResolveUDPAddr("udp", broadcastAddr)
	if err != nil {
		log.Fatal(err)
	}

	go func() {
		log.Infof("Starting to broadcast our port %s on %s", *grpcPort, broadcastAddr)
		for {
			if _, err := c.WriteTo([]byte(*grpcPort), dst); err != nil {
				log.Warn(err)
			}
			time.Sleep(time.Second)
		}
	}()

	// Start serving GRPC.
	log.Fatal(s.Serve(lis))
}
<|endoftext|>


<commit_before>package upload

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/dustin/go-humanize"
	"github.com/meatballhat/artifacts/path"
	"github.com/mitchellh/goamz/aws"
	"github.com/mitchellh/goamz/s3"
)

type uploader struct {
	Opts          *Options
	Paths         *path.PathSet
	RetryInterval time.Duration

	log *logrus.Logger
}

type maxSizeTracker struct {
	sync.Mutex
	Current uint64
}

// Upload does the deed!
func Upload(opts *Options, log *logrus.Logger) error {
	return newUploader(opts, log).Upload()
}

func newUploader(opts *Options, log *logrus.Logger) *uploader {
	u := &uploader{
		Opts:          opts,
		Paths:         path.NewPathSet(),
		RetryInterval: 3 * time.Second,

		log: log,
	}

	if opts.Private {
		opts.CacheControl = "private"
	}

	if opts.CacheControl == "" {
		opts.CacheControl = "public, max-age=315360000"
	}

	for _, s := range opts.Paths {
		parts := strings.SplitN(s, ":", 2)
		if len(parts) < 2 {
			parts = append(parts, "")
		}
		u.Paths.Add(path.NewPath(opts.WorkingDir, parts[0], parts[1]))
	}

	return u
}

func (u *uploader) Upload() error {
	u.log.Debug("starting upload")
	done := make(chan bool)
	allDone := uint64(0)
	fileChan := u.files()

	for i := uint64(0); i < u.Opts.Concurrency; i++ {
		u.log.WithFields(logrus.Fields{
			"uploader": i,
		}).Debug("starting uploader worker")

		go func() {
			uploader := 0 + i
			auth, err := aws.GetAuth(u.Opts.AccessKey, u.Opts.SecretKey)
			if err != nil {
				u.log.WithFields(logrus.Fields{
					"uploader": uploader,
					"err":      err,
				}).Error("uploader failed to get aws auth")
				done <- true
				return
			}

			conn := s3.New(auth, aws.USEast)
			bucket := conn.Bucket(u.Opts.BucketName)

			if bucket == nil {
				u.log.WithFields(logrus.Fields{
					"uploader": i,
				}).Warn("uploader failed to get bucket")
				done <- true
				return
			}

			for artifact := range fileChan {
				u.uploadFile(bucket, artifact)
			}

			done <- true
		}()
	}

	for {
		select {
		case <-done:
			allDone += 1
			if allDone >= u.Opts.Concurrency {
				return nil
			}
		}
	}

	return nil
}

func (u *uploader) artifactFeederLoop(path *path.Path, artifacts chan *artifact, curSize *maxSizeTracker) error {
	to, from, root := path.To, path.From, path.Root
	if path.IsDir() {
		root = filepath.Join(root, from)
		if strings.HasSuffix(root, "/") {
			root = root + "/"
		}
	}

	filepath.Walk(path.Fullpath(), func(f string, info os.FileInfo, err error) error {
		if info != nil && info.IsDir() {
			return nil
		}

		relPath := strings.Replace(strings.Replace(f, root, "", -1), root+"/", "", -1)
		destination := relPath
		if len(to) > 0 {
			if path.IsDir() {
				destination = filepath.Join(to, relPath)
			} else {
				destination = to
			}
		}

		for _, targetPath := range u.Opts.TargetPaths {
			return func() error {
				curSize.Lock()
				defer curSize.Unlock()

				artifact := newArtifact(root, relPath, targetPath, destination, u.Opts.Perm)
				size := artifact.Size()
				curSize.Current += size
				logFields := logrus.Fields{
					"current_size":     humanize.Bytes(curSize.Current),
					"max_size":         humanize.Bytes(u.Opts.MaxSize),
					"percent_max_size": pctMax(size, u.Opts.MaxSize),
					"artifact":         relPath,
					"artifact_size":    humanize.Bytes(artifact.Size()),
				}

				if curSize.Current > u.Opts.MaxSize {
					msg := "max-size would be exceeded"
					u.log.WithFields(logFields).Error(msg)
					return fmt.Errorf(msg)
				}

				u.log.WithFields(logFields).Debug("queueing artifact")
				artifacts <- artifact
				return nil
			}()
		}
		return nil
	})

	return nil
}

func (u *uploader) artifactFeeder(artifacts chan *artifact) error {
	curSize := &maxSizeTracker{Current: uint64(0)}

	for _, path := range u.Paths.All() {
		u.artifactFeederLoop(path, artifacts, curSize)
	}

	close(artifacts)
	return nil
}

func (u *uploader) files() chan *artifact {
	artifacts := make(chan *artifact)
	go u.artifactFeeder(artifacts)
	return artifacts
}

func (u *uploader) uploadFile(b *s3.Bucket, a *artifact) error {
	retries := uint64(0)

	for {
		err := u.rawUpload(b, a)
		if err != nil {
			if retries < u.Opts.Retries {
				retries += 1
				u.log.WithFields(logrus.Fields{
					"artifact": a.Source,
					"retry":    retries,
				}).Debug("retrying")
				time.Sleep(u.RetryInterval)
				continue
			} else {
				return err
			}
		}
		return nil
	}
}

func (u *uploader) rawUpload(b *s3.Bucket, a *artifact) error {
	destination := a.FullDestination()
	reader, err := a.Reader()
	if err != nil {
		return err
	}

	ctype := a.ContentType()

	u.log.WithFields(logrus.Fields{
		"artifact_size":    humanize.Bytes(a.Size()),
		"percent_max_size": pctMax(a.Size(), u.Opts.MaxSize),
		"max_size":         humanize.Bytes(u.Opts.MaxSize),
		"source":           a.Source,
		"dest":             destination,
		"bucket":           b.Name,
		"content_type":     ctype,
		"cache_control":    u.Opts.CacheControl,
	}).Info("uploading to s3")

	err = b.PutReaderHeader(destination, reader, int64(a.Size()),
		map[string][]string{
			"Content-Type":  []string{ctype},
			"Cache-Control": []string{u.Opts.CacheControl},
		}, a.Perm)
	if err != nil {
		u.log.WithFields(logrus.Fields{"err": err}).Error("failed to upload")
		return err
	}

	return nil
}

func pctMax(artifactSize, maxSize uint64) float64 {
	return float64(100.0) * (float64(artifactSize) / float64(maxSize))
}
<commit_msg>Adding a bit more debugging for figuring out paths<commit_after>package upload

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/dustin/go-humanize"
	"github.com/meatballhat/artifacts/path"
	"github.com/mitchellh/goamz/aws"
	"github.com/mitchellh/goamz/s3"
)

type uploader struct {
	Opts          *Options
	Paths         *path.PathSet
	RetryInterval time.Duration

	log *logrus.Logger
}

type maxSizeTracker struct {
	sync.Mutex
	Current uint64
}

// Upload does the deed!
func Upload(opts *Options, log *logrus.Logger) error {
	return newUploader(opts, log).Upload()
}

func newUploader(opts *Options, log *logrus.Logger) *uploader {
	u := &uploader{
		Opts:          opts,
		Paths:         path.NewPathSet(),
		RetryInterval: 3 * time.Second,

		log: log,
	}

	if opts.Private {
		opts.CacheControl = "private"
	}

	if opts.CacheControl == "" {
		opts.CacheControl = "public, max-age=315360000"
	}

	for _, s := range opts.Paths {
		parts := strings.SplitN(s, ":", 2)
		if len(parts) < 2 {
			parts = append(parts, "")
		}
		u.Paths.Add(path.NewPath(opts.WorkingDir, parts[0], parts[1]))
	}

	return u
}

func (u *uploader) Upload() error {
	u.log.Debug("starting upload")
	done := make(chan bool)
	allDone := uint64(0)
	fileChan := u.files()

	for i := uint64(0); i < u.Opts.Concurrency; i++ {
		u.log.WithFields(logrus.Fields{
			"uploader": i,
		}).Debug("starting uploader worker")

		go func() {
			uploader := 0 + i
			auth, err := aws.GetAuth(u.Opts.AccessKey, u.Opts.SecretKey)
			if err != nil {
				u.log.WithFields(logrus.Fields{
					"uploader": uploader,
					"err":      err,
				}).Error("uploader failed to get aws auth")
				done <- true
				return
			}

			conn := s3.New(auth, aws.USEast)
			bucket := conn.Bucket(u.Opts.BucketName)

			if bucket == nil {
				u.log.WithFields(logrus.Fields{
					"uploader": i,
				}).Warn("uploader failed to get bucket")
				done <- true
				return
			}

			for artifact := range fileChan {
				u.uploadFile(bucket, artifact)
			}

			done <- true
		}()
	}

	for {
		select {
		case <-done:
			allDone += 1
			if allDone >= u.Opts.Concurrency {
				return nil
			}
		}
	}

	return nil
}

func (u *uploader) artifactFeederLoop(path *path.Path, artifacts chan *artifact, curSize *maxSizeTracker) error {
	to, from, root := path.To, path.From, path.Root
	if path.IsDir() {
		root = filepath.Join(root, from)
		if strings.HasSuffix(root, "/") {
			root = root + "/"
		}
	}

	filepath.Walk(path.Fullpath(), func(f string, info os.FileInfo, err error) error {
		if info != nil && info.IsDir() {
			return nil
		}

		relPath := strings.Replace(strings.Replace(f, root, "", -1), root+"/", "", -1)
		destination := relPath
		if len(to) > 0 {
			if path.IsDir() {
				destination = filepath.Join(to, relPath)
			} else {
				destination = to
			}
		}

		for _, targetPath := range u.Opts.TargetPaths {
			return func() error {
				curSize.Lock()
				defer curSize.Unlock()

				artifact := newArtifact(root, relPath, targetPath, destination, u.Opts.Perm)
				size := artifact.Size()
				curSize.Current += size
				logFields := logrus.Fields{
					"current_size":     humanize.Bytes(curSize.Current),
					"max_size":         humanize.Bytes(u.Opts.MaxSize),
					"percent_max_size": pctMax(size, u.Opts.MaxSize),
					"artifact":         relPath,
					"artifact_size":    humanize.Bytes(artifact.Size()),
				}

				if curSize.Current > u.Opts.MaxSize {
					msg := "max-size would be exceeded"
					u.log.WithFields(logFields).Error(msg)
					return fmt.Errorf(msg)
				}

				u.log.WithFields(logFields).Debug("queueing artifact")
				artifacts <- artifact
				return nil
			}()
		}
		return nil
	})

	return nil
}

func (u *uploader) artifactFeeder(artifacts chan *artifact) error {
	curSize := &maxSizeTracker{Current: uint64(0)}

	i := 0
	for _, path := range u.Paths.All() {
		u.artifactFeederLoop(path, artifacts, curSize)
		i += 1
	}

	u.log.WithFields(logrus.Fields{
		"total_size": curSize.Current,
		"count":      i,
	}).Debug("done feeding artifacts")

	close(artifacts)
	return nil
}

func (u *uploader) files() chan *artifact {
	artifacts := make(chan *artifact)
	go u.artifactFeeder(artifacts)
	return artifacts
}

func (u *uploader) uploadFile(b *s3.Bucket, a *artifact) error {
	retries := uint64(0)

	for {
		err := u.rawUpload(b, a)
		if err != nil {
			if retries < u.Opts.Retries {
				retries += 1
				u.log.WithFields(logrus.Fields{
					"artifact": a.Source,
					"retry":    retries,
				}).Debug("retrying")
				time.Sleep(u.RetryInterval)
				continue
			} else {
				return err
			}
		}
		return nil
	}
}

func (u *uploader) rawUpload(b *s3.Bucket, a *artifact) error {
	destination := a.FullDestination()
	reader, err := a.Reader()
	if err != nil {
		return err
	}

	ctype := a.ContentType()

	u.log.WithFields(logrus.Fields{
		"artifact_size":    humanize.Bytes(a.Size()),
		"percent_max_size": pctMax(a.Size(), u.Opts.MaxSize),
		"max_size":         humanize.Bytes(u.Opts.MaxSize),
		"source":           a.Source,
		"dest":             destination,
		"bucket":           b.Name,
		"content_type":     ctype,
		"cache_control":    u.Opts.CacheControl,
	}).Info("uploading to s3")

	err = b.PutReaderHeader(destination, reader, int64(a.Size()),
		map[string][]string{
			"Content-Type":  []string{ctype},
			"Cache-Control": []string{u.Opts.CacheControl},
		}, a.Perm)
	if err != nil {
		u.log.WithFields(logrus.Fields{"err": err}).Error("failed to upload")
		return err
	}

	return nil
}

func pctMax(artifactSize, maxSize uint64) float64 {
	return float64(100.0) * (float64(artifactSize) / float64(maxSize))
}
<|endoftext|>


<commit_before>package gdux

import (
	"sync"

	"github.com/pkg/errors"
)

type Store struct {
	currentState     State
	currentStateLock sync.RWMutex //covers: 'currentState'

	reducerFunction             Reducer
	channelSubscriptionRegistry []RegistrableStateChannel

	TriggerChan chan Action
	//quitChan    chan struct{}
}

func NewStore(state State, reducer Reducer) (*Store, error) {
	if state == nil || reducer == nil {
		return nil, errors.New("Error: Cannot create store with nil state or reducer.")
	}
	store := &Store{
		currentState:    state,
		reducerFunction: reducer,
		TriggerChan:     make(chan Action),
		//quitChan:        make(chan struct{}),
	}
	go func(s *Store) {
		for n := range s.TriggerChan {
			s.Trigger(n)
		}
	}(store)
	return store, nil
}

func (store *Store) GetState() State {
	store.currentStateLock.RLock()
	defer store.currentStateLock.RUnlock()
	return store.currentState
}

func (store *Store) SetState(state State) {
	store.currentStateLock.Lock()
	defer store.currentStateLock.Unlock()
	store.currentState = state
}

//Register Subscription Channel
func (store *Store) SubscribeChannel(registree RegistrableStateChannel) {
	store.registerSubscriptionChannel(registree)
}

func (store *Store) registerSubscriptionChannel(registree RegistrableStateChannel) {
	store.channelSubscriptionRegistry = append(store.channelSubscriptionRegistry, registree)
}

//todo (cbergoon): need something to listen on these listener channels. fan-in mechanism. needs to be in order received.

//Triggers
func (store *Store) Trigger(action Action) {
	store.trigger(action)
}

func (store *Store) trigger(action Action) {
	store.currentStateLock.Lock()
	newState, err := store.reducerFunction(store.currentState, action)
	if err != nil {
		//no-op on state
	}
	store.currentState = newState

	store.currentStateLock.Unlock()
	store.NotifySubscribers()
}

func (store *Store) NotifySubscribers() {
	send := func(s *Store, sendChannel RegistrableStateChannel) {
		store.currentStateLock.RLock()
		sendChannel <- s.currentState
		store.currentStateLock.RUnlock()
	}
	for _, c := range store.channelSubscriptionRegistry {
		send(store, c)
	}
}

type State interface {
	Clone() State
}

type Action interface {
	GetType() string
}

type Reducer func(state State, action Action) (State, error)

//type RegistrableFunction func(state State)//thought (might not want or need these)
type RegistrableStateChannel chan State
type RegistrableActionChannel <-chan Action

func BasicReducer(state State, action Action) (State, error) {
	if action.GetType() == "INC" {
		tmp := state.(BasicState)
		tmp.Accumulator += 1
		tmp.LastAction = "INC"
		return tmp, nil
	} else if action.GetType() == "DEC" {
		tmp := state.(BasicState)
		tmp.Accumulator -= 1
		tmp.LastAction = "DEC"
		return tmp, nil
	} else {
		return state, nil
	}
}

type BasicAction struct {
	T string
}

func (action BasicAction) GetType() string {
	return action.T
}

type BasicState struct {
	Accumulator int
	LastAction  string
}

func (state BasicState) Clone() State {
	return state
}
<commit_msg>Changes to gdux, refactoring, preserve order.<commit_after>package gdux

import (
	"sync"

	"github.com/pkg/errors"
)

//Store represents the Gdux state container.
type Store struct {
	currentState     State        //currentState represents the state
	currentStateLock sync.RWMutex //currentStateLock covers: 'currentState'

	reducerFunction             Reducer                   //reducerFunction executed for each action
	channelSubscriptionRegistry []RegistrableStateChannel //channelSubscriptionRegistry list of channels to send updated state on

	options Options //options represents settings and configuration for store

	actionRecord []Action //actionRecord history of actions if enabled

	TriggerChan chan Action //triggerChan channel store receives actions
}

//Options represents options for the state.
type Options struct {
	Record     bool //Record indicates whether action history is stored
	RecordSize int  //RecordSize sets limit on action history; 0 for no limit
}

//NewStore creates a new store with the initial state value and reducer function. Starts go routine listening for action.
//Returns a pointer to the store and a possible error.
func NewStore(state State, reducer Reducer) (*Store, error) {
	if state == nil || reducer == nil {
		return nil, errors.New("Error: Cannot create store with nil state or reducer.")
	}
	store := &Store{
		currentState:    state,
		reducerFunction: reducer,
		TriggerChan:     make(chan Action),
		options:         Options{},
	}
	go func(s *Store) {
		for n := range s.TriggerChan {
			s.Trigger(n)
		}
	}(store)
	return store, nil
}

//WithOptions sets options on the store.
func (store *Store) WithOptions(options Options) {
	store.options = options
}

//GetState returns the current state.
func (store *Store) GetState() State {
	store.currentStateLock.RLock()
	defer store.currentStateLock.RUnlock()
	return store.currentState
}

/*
func (store *Store) SetState(state State) {
	store.currentStateLock.Lock()
	defer store.currentStateLock.Unlock()
	store.currentState = state
}
*/

//SubscriberChannel adds a channel to the list of channels that any updates to the state will be sent on.
func (store *Store) SubscribeChannel(registree RegistrableStateChannel) {
	store.registerSubscriptionChannel(registree)
}

//registerSubscriptionChannel internal only function to add a RegisterableStateChannel to the subscriber slice.
func (store *Store) registerSubscriptionChannel(registree RegistrableStateChannel) {
	store.channelSubscriptionRegistry = append(store.channelSubscriptionRegistry, registree)
}

//Trigger invokes a change in state with the provided action.
func (store *Store) Trigger(action Action) {
	store.trigger(action)
}

//triggerinternal only function that manages state changes.
func (store *Store) trigger(action Action) {
	store.currentStateLock.Lock()
	newState, err := store.reducerFunction(store.currentState, action)
	if err != nil {
		//no-op on state
	}
	store.currentState = newState
	if store.options.Record {
		store.actionRecord = append(store.actionRecord, action)
	}
	if store.options.RecordSize != 0 {
		for len(store.actionRecord) > store.options.RecordSize {
			store.actionRecord = store.actionRecord[1:]
		}
	}
	store.triggerNotifySubscribors()
	//store.currentStateLock.Unlock()

}

//triggerNotifySubscribers called by trigger and sends changes to the state to the subscribers.
func (store *Store) triggerNotifySubscribors() {
	x := store.currentState
	store.currentStateLock.Unlock()
	send := func(s State, sendChannel RegistrableStateChannel) {
		//store.currentStateLock.RLock()
		sendChannel <- s
		//store.currentStateLock.RUnlock()
	}
	for _, c := range store.channelSubscriptionRegistry {
		send(x, c)
	}
}

//NotifySubscribors sends changes to the state to the subscribers.
func (store *Store) NotifySubscribers() {
	store.currentStateLock.Lock()
	x := store.currentState
	store.currentStateLock.Unlock()
	send := func(s State, sendChannel RegistrableStateChannel) {
		//store.currentStateLock.RLock()
		sendChannel <- s
		//store.currentStateLock.RUnlock()
	}
	for _, c := range store.channelSubscriptionRegistry {
		send(x, c)
	}
}

//State is the interface that must be implemented for the state type.
type State interface {
	//Clone() State
}

//Actions is the interface all actions shouls implement.
type Action interface {
	GetType() string
}

//Reducer represents the signature for all reducer functions.
type Reducer func(state State, action Action) (State, error)

type RegistrableStateChannel chan State
type RegistrableActionChannel <-chan Action
<|endoftext|>