<commit_before>package gochecks

import (
	"fmt"
	"net"
	"strings"
	"time"

	"io/ioutil"
	"net/http"
	"net/url"

	"github.com/streadway/amqp"
	"github.com/tatsushid/go-fastping"
)

import (
	"database/sql"

	_ "github.com/go-sql-driver/mysql"
)

const (
	sysName     = "1.3.6.1.2.1.1.5.0"
	maxPingTime = 4 * time.Second
)

type CheckFunction func() Event
type MultiCheckFunction func() []Event

// Tags  returns a new check function that adds the given tags to the result
// generated by the initial check function
func (f CheckFunction) Tags(tags ...string) CheckFunction {
	return func() Event {
		result := f()
		result.Tags = tags
		return result
	}
}

// Attributtes  returns a new check function that adds the attributes map to the result
// generated by the initial check function
func (f CheckFunction) Attributes(attributes map[string]string) CheckFunction {
	return func() Event {
		result := f()
		result.Attributes = attributes
		return result
	}
}

// Attributtes  returns a new check function that adds the given TTL time (in seconds) to the result
// generated by the initial check function
func (f CheckFunction) TTL(ttl float32) CheckFunction {
	return func() Event {
		result := f()
		result.TTL = ttl
		return result
	}
}

func (f CheckFunction) Retry(times int, sleep time.Duration) CheckFunction {
	return func() Event {
		var result Event
		for i := 0; i < times; i++ {
			result = f()
			if result.State == "ok" {
				return result
			}
			time.Sleep(sleep)
		}
		return result
	}
}

func NewPingChecker(host, service, ip string) CheckFunction {
	return func() Event {
		var retRtt time.Duration
		var result = Event{Host: host, Service: service, State: "critical"}

		p := fastping.NewPinger()
		p.MaxRTT = maxPingTime
		ra, err := net.ResolveIPAddr("ip4:icmp", ip)
		if err != nil {
			result.Description = err.Error()
		}

		p.AddIPAddr(ra)
		p.OnRecv = func(addr *net.IPAddr, rtt time.Duration) {
			result.State = "ok"
			result.Metric = float32(retRtt.Nanoseconds() / 1e6)
		}

		err = p.Run()
		if err != nil {
			result.Description = err.Error()
		}
		return result
	}
}

func NewTCPPortChecker(host, service, ip string, port int, timeout time.Duration) CheckFunction {
	return func() Event {
		var err error
		var conn net.Conn

		var t1 = time.Now()
		conn, err = net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), timeout)
		if err == nil {
			conn.Close()
			milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
			return Event{Host: host, Service: service, State: "ok", Metric: milliseconds}
		}
		return Event{Host: host, Service: service, State: "critical"}
	}
}

type ValidateHTTPResponseFunction func(resp *http.Response) (state, description string)

func BodyGreaterThan(minLength int) ValidateHTTPResponseFunction {
	return func(httpResp *http.Response) (state, description string) {
		if httpResp.StatusCode != 200 {
			return "critical", fmt.Sprintf("Response %d", httpResp.StatusCode)
		}
		if httpResp.Body == nil {
			return "critical", fmt.Sprintf("Empty body")
		}
		body, err := ioutil.ReadAll(httpResp.Body)
		if err != nil {
			return "critical", fmt.Sprintf("Error geting body")
		}
		if len(body) < minLength {
			return "critical", fmt.Sprintf("Obtained %d bytes, expected more than %d", len(body), minLength)
		}
		return "ok", ""
	}
}

func NewGenericHTTPChecker(host, service, url string, validationFunc ValidateHTTPResponseFunction) CheckFunction {
	return func() Event {
		var t1 = time.Now()

		response, err := http.Get(url)
		milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
		result := Event{Host: host, Service: service, State: "critical", Metric: milliseconds}
		if err != nil {
			result.Description = err.Error()
		} else {
			if response.Body != nil {
				defer response.Body.Close()
			}
			result.State, result.Description = validationFunc(response)
		}
		return result
	}
}

func NewHTTPChecker(host, service, url string, expectedStatusCode int) CheckFunction {
	return NewGenericHTTPChecker(host, service, url,
		func(httpResp *http.Response) (string, string) {
			if httpResp.StatusCode == expectedStatusCode {
				return "ok", ""
			}
			return "critical", fmt.Sprintf("Response %d", httpResp.StatusCode)
		})
}

type SnmpCheckerConf struct {
	retries    int
	timeout    time.Duration
	oidToCheck string
}

var DefaultSnmpCheckConf = SnmpCheckerConf{
	retries:    1,
	timeout:    1 * time.Second,
	oidToCheck: sysName,
}

func NewSnmpChecker(host, service, ip, community string, conf SnmpCheckerConf) CheckFunction {
	return func() Event {

		_, err := snmpGet(ip, community, []string{conf.oidToCheck}, conf.timeout, conf.retries)
		if err == nil {
			return Event{Host: host, Service: service, State: "ok", Description: err.Error()}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

func NewC4CMTSTempChecker(host, service, ip, community string, maxAllowedTemp int) CheckFunction {
	return func() Event {

		result, err := snmpWalk(ip, community, "1.3.6.1.4.1.4998.1.1.10.1.4.2.1.29", 2*time.Second, 1)

		if err == nil {
			max := 0
			for _, r := range result {
				if r.Value.(int) != 999 && r.Value.(int) > max {
					max = r.Value.(int)
				}
			}
			var state = "critical"
			if max < maxAllowedTemp {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

func getMaxValueFromSnmpWalk(oid, ip, community string) (uint, error) {
	result, err := snmpWalk(ip, community, oid, 2*time.Second, 1)
	if err == nil {
		max := uint(0)
		for _, r := range result {
			if r.Value.(uint) > max {
				max = r.Value.(uint)
			}
		}
		return max, nil
	}
	return 0, err
}

func NewJuniperTempChecker(host, service, ip, community string, maxAllowedTemp uint) CheckFunction {
	return func() Event {
		max, err := getMaxValueFromSnmpWalk("1.3.6.1.4.1.2636.3.1.13.1.7", ip, community)
		if err == nil {
			var state = "critical"
			if max < maxAllowedTemp {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

func NewJuniperCPUChecker(host, service, ip, community string, maxAllowedTemp uint) CheckFunction {
	return func() Event {
		max, err := getMaxValueFromSnmpWalk("1.3.6.1.4.1.2636.3.1.13.1.8", ip, community)
		if err == nil {
			var state = "critical"
			if max < maxAllowedTemp {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

func NewRabbitMQQueueLenCheck(host, service, amqpuri, queue string, max int) CheckFunction {
	return func() Event {
		result := Event{Host: host, Service: service}

		conn, err := amqp.Dial(amqpuri)
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}

		ch, err := conn.Channel()
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}
		defer ch.Close()
		defer conn.Close()

		queueInfo, err := ch.QueueInspect(queue)
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}

		var state = "critical"
		if queueInfo.Messages <= max {
			state = "ok"
		}
		return Event{Host: host, Service: service, State: state, Metric: float32(queueInfo.Messages)}
	}
}

func NewMysqlConnectionCheck(host, service, mysqluri string) CheckFunction {
	return func() Event {
		u, err := url.Parse(mysqluri)
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
		}

		if u.User == nil {
			return Event{Host: host, Service: service, State: "critical", Description: "No user defined"}
		}
		password, hasPassword := u.User.Password()
		if !hasPassword {
			return Event{Host: host, Service: service, State: "critical", Description: "No password defined"}
		}
		hostAndPort := u.Host
		if !strings.Contains(hostAndPort, ":") {
			hostAndPort = hostAndPort + ":3306"
		}
		var t1 = time.Now()
		con, err := sql.Open("mysql", u.User.Username()+":"+password+"@"+"tcp("+hostAndPort+")"+u.Path)
		defer con.Close()
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
		}
		q := `select CURTIME()`
		row := con.QueryRow(q)
		var date string
		err = row.Scan(&date)
		milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error(), Metric: milliseconds}
		}
		return Event{Host: host, Service: service, State: "ok", Metric: milliseconds}
	}
}

type ObtainMetricFunction func() float32
type CalculateStateFunction func(float32) string

func NewGenericCheck(host, service string, metricFunc ObtainMetricFunction, stateFunc CalculateStateFunction) CheckFunction {
	return func() Event {
		value := metricFunc()
		var state = stateFunc(value)
		return Event{Host: host, Service: service, State: state, Metric: value}
	}
}
<commit_msg>Added a bit doc<commit_after>package gochecks

import (
	"fmt"
	"net"
	"strings"
	"time"

	"io/ioutil"
	"net/http"
	"net/url"

	"github.com/streadway/amqp"
	"github.com/tatsushid/go-fastping"
)

import (
	"database/sql"

	_ "github.com/go-sql-driver/mysql"
)

const (
	sysName     = "1.3.6.1.2.1.1.5.0"
	maxPingTime = 4 * time.Second
)

// CheckFunction type for a function that return a event
type CheckFunction func() Event
// MultiCheckFunction type for a function that return an array of events
type MultiCheckFunction func() []Event

// Tags  returns a new check function that adds the given tags to the result
// generated by the initial check function
func (f CheckFunction) Tags(tags ...string) CheckFunction {
	return func() Event {
		result := f()
		result.Tags = tags
		return result
	}
}

// Attributtes  returns a new check function that adds the attributes map to the result
// generated by the initial check function
func (f CheckFunction) Attributes(attributes map[string]string) CheckFunction {
	return func() Event {
		result := f()
		result.Attributes = attributes
		return result
	}
}

// Attributtes  returns a new check function that adds the given TTL time (in seconds) to the result
// generated by the initial check function
func (f CheckFunction) TTL(ttl float32) CheckFunction {
	return func() Event {
		result := f()
		result.TTL = ttl
		return result
	}
}

// Retry returns a new check funciont that execute the given function up to a given retry times or
// until the first execution that returns a ok (whichever comes first). The new function will return
// the event of the last execution
func (f CheckFunction) Retry(times int, sleep time.Duration) CheckFunction {
	return func() Event {
		var result Event
		for i := 0; i < times; i++ {
			result = f()
			if result.State == "ok" {
				return result
			}
			time.Sleep(sleep)
		}
		return result
	}
}

func NewPingChecker(host, service, ip string) CheckFunction {
	return func() Event {
		var retRtt time.Duration
		var result = Event{Host: host, Service: service, State: "critical"}

		p := fastping.NewPinger()
		p.MaxRTT = maxPingTime
		ra, err := net.ResolveIPAddr("ip4:icmp", ip)
		if err != nil {
			result.Description = err.Error()
		}

		p.AddIPAddr(ra)
		p.OnRecv = func(addr *net.IPAddr, rtt time.Duration) {
			result.State = "ok"
			result.Metric = float32(retRtt.Nanoseconds() / 1e6)
		}

		err = p.Run()
		if err != nil {
			result.Description = err.Error()
		}
		return result
	}
}

func NewTCPPortChecker(host, service, ip string, port int, timeout time.Duration) CheckFunction {
	return func() Event {
		var err error
		var conn net.Conn

		var t1 = time.Now()
		conn, err = net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), timeout)
		if err == nil {
			conn.Close()
			milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
			return Event{Host: host, Service: service, State: "ok", Metric: milliseconds}
		}
		return Event{Host: host, Service: service, State: "critical"}
	}
}

type ValidateHTTPResponseFunction func(resp *http.Response) (state, description string)

func BodyGreaterThan(minLength int) ValidateHTTPResponseFunction {
	return func(httpResp *http.Response) (state, description string) {
		if httpResp.StatusCode != 200 {
			return "critical", fmt.Sprintf("Response %d", httpResp.StatusCode)
		}
		if httpResp.Body == nil {
			return "critical", fmt.Sprintf("Empty body")
		}
		body, err := ioutil.ReadAll(httpResp.Body)
		if err != nil {
			return "critical", fmt.Sprintf("Error geting body")
		}
		if len(body) < minLength {
			return "critical", fmt.Sprintf("Obtained %d bytes, expected more than %d", len(body), minLength)
		}
		return "ok", ""
	}
}

func NewGenericHTTPChecker(host, service, url string, validationFunc ValidateHTTPResponseFunction) CheckFunction {
	return func() Event {
		var t1 = time.Now()

		response, err := http.Get(url)
		milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
		result := Event{Host: host, Service: service, State: "critical", Metric: milliseconds}
		if err != nil {
			result.Description = err.Error()
		} else {
			if response.Body != nil {
				defer response.Body.Close()
			}
			result.State, result.Description = validationFunc(response)
		}
		return result
	}
}

func NewHTTPChecker(host, service, url string, expectedStatusCode int) CheckFunction {
	return NewGenericHTTPChecker(host, service, url,
		func(httpResp *http.Response) (string, string) {
			if httpResp.StatusCode == expectedStatusCode {
				return "ok", ""
			}
			return "critical", fmt.Sprintf("Response %d", httpResp.StatusCode)
		})
}

type SnmpCheckerConf struct {
	retries    int
	timeout    time.Duration
	oidToCheck string
}

var DefaultSnmpCheckConf = SnmpCheckerConf{
	retries:    1,
	timeout:    1 * time.Second,
	oidToCheck: sysName,
}

func NewSnmpChecker(host, service, ip, community string, conf SnmpCheckerConf) CheckFunction {
	return func() Event {

		_, err := snmpGet(ip, community, []string{conf.oidToCheck}, conf.timeout, conf.retries)
		if err == nil {
			return Event{Host: host, Service: service, State: "ok", Description: err.Error()}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

func NewC4CMTSTempChecker(host, service, ip, community string, maxAllowedTemp int) CheckFunction {
	return func() Event {

		result, err := snmpWalk(ip, community, "1.3.6.1.4.1.4998.1.1.10.1.4.2.1.29", 2*time.Second, 1)

		if err == nil {
			max := 0
			for _, r := range result {
				if r.Value.(int) != 999 && r.Value.(int) > max {
					max = r.Value.(int)
				}
			}
			var state = "critical"
			if max < maxAllowedTemp {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

func getMaxValueFromSnmpWalk(oid, ip, community string) (uint, error) {
	result, err := snmpWalk(ip, community, oid, 2*time.Second, 1)
	if err == nil {
		max := uint(0)
		for _, r := range result {
			if r.Value.(uint) > max {
				max = r.Value.(uint)
			}
		}
		return max, nil
	}
	return 0, err
}

func NewJuniperTempChecker(host, service, ip, community string, maxAllowedTemp uint) CheckFunction {
	return func() Event {
		max, err := getMaxValueFromSnmpWalk("1.3.6.1.4.1.2636.3.1.13.1.7", ip, community)
		if err == nil {
			var state = "critical"
			if max < maxAllowedTemp {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

func NewJuniperCPUChecker(host, service, ip, community string, maxAllowedTemp uint) CheckFunction {
	return func() Event {
		max, err := getMaxValueFromSnmpWalk("1.3.6.1.4.1.2636.3.1.13.1.8", ip, community)
		if err == nil {
			var state = "critical"
			if max < maxAllowedTemp {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

func NewRabbitMQQueueLenCheck(host, service, amqpuri, queue string, max int) CheckFunction {
	return func() Event {
		result := Event{Host: host, Service: service}

		conn, err := amqp.Dial(amqpuri)
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}

		ch, err := conn.Channel()
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}
		defer ch.Close()
		defer conn.Close()

		queueInfo, err := ch.QueueInspect(queue)
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}

		var state = "critical"
		if queueInfo.Messages <= max {
			state = "ok"
		}
		return Event{Host: host, Service: service, State: state, Metric: float32(queueInfo.Messages)}
	}
}

func NewMysqlConnectionCheck(host, service, mysqluri string) CheckFunction {
	return func() Event {
		u, err := url.Parse(mysqluri)
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
		}

		if u.User == nil {
			return Event{Host: host, Service: service, State: "critical", Description: "No user defined"}
		}
		password, hasPassword := u.User.Password()
		if !hasPassword {
			return Event{Host: host, Service: service, State: "critical", Description: "No password defined"}
		}
		hostAndPort := u.Host
		if !strings.Contains(hostAndPort, ":") {
			hostAndPort = hostAndPort + ":3306"
		}
		var t1 = time.Now()
		con, err := sql.Open("mysql", u.User.Username()+":"+password+"@"+"tcp("+hostAndPort+")"+u.Path)
		defer con.Close()
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
		}
		q := `select CURTIME()`
		row := con.QueryRow(q)
		var date string
		err = row.Scan(&date)
		milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error(), Metric: milliseconds}
		}
		return Event{Host: host, Service: service, State: "ok", Metric: milliseconds}
	}
}

type ObtainMetricFunction func() float32
type CalculateStateFunction func(float32) string

func NewGenericCheck(host, service string, metricFunc ObtainMetricFunction, stateFunc CalculateStateFunction) CheckFunction {
	return func() Event {
		value := metricFunc()
		var state = stateFunc(value)
		return Event{Host: host, Service: service, State: state, Metric: value}
	}
}
<|endoftext|>


<commit_before>package astits

import (
	"fmt"

	"github.com/asticode/go-astilog"
	"github.com/pkg/errors"
)

// PSI table IDs
const (
	PSITableTypeBAT     = "BAT"
	PSITableTypeDIT     = "DIT"
	PSITableTypeEIT     = "EIT"
	PSITableTypeNIT     = "NIT"
	PSITableTypeNull    = "Null"
	PSITableTypePAT     = "PAT"
	PSITableTypePMT     = "PMT"
	PSITableTypeRST     = "RST"
	PSITableTypeSDT     = "SDT"
	PSITableTypeSIT     = "SIT"
	PSITableTypeST      = "ST"
	PSITableTypeTDT     = "TDT"
	PSITableTypeTOT     = "TOT"
	PSITableTypeUnknown = "Unknown"
)

// PSIData represents a PSI data
// https://en.wikipedia.org/wiki/Program-specific_information
type PSIData struct {
	PointerField int // Present at the start of the TS packet payload signaled by the payload_unit_start_indicator bit in the TS header. Used to set packet alignment bytes or content before the start of tabled payload data.
	Sections     []*PSISection
}

// PSISection represents a PSI section
type PSISection struct {
	CRC32  uint32 // A checksum of the entire table excluding the pointer field, pointer filler bytes and the trailing CRC32.
	Header *PSISectionHeader
	Syntax *PSISectionSyntax
}

// PSISectionHeader represents a PSI section header
type PSISectionHeader struct {
	PrivateBit             bool   // The PAT, PMT, and CAT all set this to 0. Other tables set this to 1.
	SectionLength          uint16 // The number of bytes that follow for the syntax section (with CRC value) and/or table data. These bytes must not exceed a value of 1021.
	SectionSyntaxIndicator bool   // A flag that indicates if the syntax section follows the section length. The PAT, PMT, and CAT all set this to 1.
	TableID                int    // Table Identifier, that defines the structure of the syntax section and other contained data. As an exception, if this is the byte that immediately follow previous table section and is set to 0xFF, then it indicates that the repeat of table section end here and the rest of TS data payload shall be stuffed with 0xFF. Consequently the value 0xFF shall not be used for the Table Identifier.
	TableType              string
}

// PSISectionSyntax represents a PSI section syntax
type PSISectionSyntax struct {
	Data   *PSISectionSyntaxData
	Header *PSISectionSyntaxHeader
}

// PSISectionSyntaxHeader represents a PSI section syntax header
type PSISectionSyntaxHeader struct {
	CurrentNextIndicator bool   // Indicates if data is current in effect or is for future use. If the bit is flagged on, then the data is to be used at the present moment.
	LastSectionNumber    uint8  // This indicates which table is the last table in the sequence of tables.
	SectionNumber        uint8  // This is an index indicating which table this is in a related sequence of tables. The first table starts from 0.
	TableIDExtension     uint16 // Informational only identifier. The PAT uses this for the transport stream identifier and the PMT uses this for the Program number.
	VersionNumber        uint8  // Syntax version number. Incremented when data is changed and wrapped around on overflow for values greater than 32.
}

// PSISectionSyntaxData represents a PSI section syntax data
type PSISectionSyntaxData struct {
	EIT *EITData
	NIT *NITData
	PAT *PATData
	PMT *PMTData
	SDT *SDTData
	TOT *TOTData
}

// parsePSIData parses a PSI data
func parsePSIData(i []byte) (d *PSIData, err error) {
	// Init data
	d = &PSIData{}
	var offset int

	// Pointer field
	d.PointerField = int(i[offset])
	offset += 1

	// Pointer filler bytes
	offset += d.PointerField

	// Parse sections
	var s *PSISection
	var stop bool
	for offset < len(i) && !stop {
		if s, stop, err = parsePSISection(i, &offset); err != nil {
			err = errors.Wrap(err, "astits: parsing PSI table failed")
			return
		}
		d.Sections = append(d.Sections, s)
	}
	return
}

// parsePSISection parses a PSI section
func parsePSISection(i []byte, offset *int) (s *PSISection, stop bool, err error) {
	// Init section
	s = &PSISection{}

	// Parse header
	var offsetStart, offsetSectionsEnd, offsetEnd int
	s.Header, offsetStart, _, offsetSectionsEnd, offsetEnd = parsePSISectionHeader(i, offset)

	// Check whether we need to stop the parsing
	if shouldStopPSIParsing(s.Header.TableType) {
		stop = true
		return
	}

	// Check whether there's a syntax section
	if s.Header.SectionLength > 0 && s.Header.SectionSyntaxIndicator {
		// Parse syntax
		s.Syntax = parsePSISectionSyntax(i, offset, s.Header, offsetSectionsEnd)

		// Process CRC32
		if hasCRC32(s.Header.TableType) {
			// Parse CRC32
			s.CRC32 = parseCRC32(i[offsetSectionsEnd:offsetEnd])
			*offset += 4

			// Check CRC32
			var c = computeCRC32(i[offsetStart:offsetSectionsEnd])
			if c != s.CRC32 {
				err = fmt.Errorf("astits: Table CRC32 %x != computed CRC32 %x", s.CRC32, c)
				return
			}
		}
	}
	return
}

// parseCRC32 parses a CRC32
func parseCRC32(i []byte) uint32 {
	return uint32(i[len(i)-4])<<24 | uint32(i[len(i)-3])<<16 | uint32(i[len(i)-2])<<8 | uint32(i[len(i)-1])
}

// computeCRC32 computes a CRC32
// https://stackoverflow.com/questions/35034042/how-to-calculate-crc32-in-psi-si-packet
func computeCRC32(i []byte) (o uint32) {
	o = uint32(0xffffffff)
	for _, b := range i {
		for i := 0; i < 8; i++ {
			if (o >= uint32(0x80000000)) != (b >= uint8(0x80)) {
				o = (o << 1) ^ 0x04C11DB7
			} else {
				o = o << 1
			}
			b <<= 1
		}
	}
	return
}

// shouldStopPSIParsing checks whether the PSI parsing should be stopped
func shouldStopPSIParsing(tableType string) bool {
	return tableType == PSITableTypeNull || tableType == PSITableTypeUnknown
}

// parsePSISectionHeader parses a PSI section header
func parsePSISectionHeader(i []byte, offset *int) (h *PSISectionHeader, offsetStart, offsetSectionsStart, offsetSectionsEnd, offsetEnd int) {
	// Init
	h = &PSISectionHeader{}
	offsetStart = *offset

	// Table ID
	h.TableID = int(i[*offset])
	*offset += 1

	// Table type
	h.TableType = psiTableType(h.TableID)

	// Check whether we need to stop the parsing
	if shouldStopPSIParsing(h.TableType) {
		return
	}

	// Section syntax indicator
	h.SectionSyntaxIndicator = i[*offset]&0x80 > 0

	// Private bit
	h.PrivateBit = i[*offset]&0x40 > 0

	// Section length
	h.SectionLength = uint16(i[*offset]&0xf)<<8 | uint16(i[*offset+1])
	*offset += 2

	// Offsets
	offsetSectionsStart = *offset
	offsetEnd = offsetSectionsStart + int(h.SectionLength)
	offsetSectionsEnd = offsetEnd
	if hasCRC32(h.TableType) {
		offsetSectionsEnd -= 4
	}
	return
}

// hasCRC32 checks whether the table has a CRC32
func hasCRC32(tableType string) bool {
	return tableType == PSITableTypePAT ||
		tableType == PSITableTypePMT ||
		tableType == PSITableTypeEIT ||
		tableType == PSITableTypeNIT ||
		tableType == PSITableTypeTOT ||
		tableType == PSITableTypeSDT
}

// psiTableType returns the psi table type based on the table id
func psiTableType(tableID int) string {
	switch {
	case tableID == 0x4a:
		return PSITableTypeBAT
	case tableID >= 0x4e && tableID <= 0x6f:
		return PSITableTypeEIT
	case tableID == 0x7e:
		return PSITableTypeDIT
	case tableID == 0x40, tableID == 0x41:
		return PSITableTypeNIT
	case tableID == 0xff:
		return PSITableTypeNull
	case tableID == 0:
		return PSITableTypePAT
	case tableID == 2:
		return PSITableTypePMT
	case tableID == 0x71:
		return PSITableTypeRST
	case tableID == 0x42, tableID == 0x46:
		return PSITableTypeSDT
	case tableID == 0x7f:
		return PSITableTypeSIT
	case tableID == 0x72:
		return PSITableTypeST
	case tableID == 0x70:
		return PSITableTypeTDT
	case tableID == 0x73:
		return PSITableTypeTOT
	}
	// TODO Remove this log
	astilog.Debugf("unlisted PSI table ID %d", tableID)
	return PSITableTypeUnknown
}

// parsePSISectionSyntax parses a PSI section syntax
func parsePSISectionSyntax(i []byte, offset *int, h *PSISectionHeader, offsetSectionsEnd int) (s *PSISectionSyntax) {
	// Init
	s = &PSISectionSyntax{}

	// Header
	if hasPSISyntaxHeader(h.TableType) {
		s.Header = parsePSISectionSyntaxHeader(i, offset)
	}

	// Parse data
	s.Data = parsePSISectionSyntaxData(i, offset, h, s.Header, offsetSectionsEnd)
	return
}

// hasPSISyntaxHeader checks whether the section has a syntax header
func hasPSISyntaxHeader(tableType string) bool {
	return tableType == PSITableTypeEIT ||
		tableType == PSITableTypeNIT ||
		tableType == PSITableTypePAT ||
		tableType == PSITableTypePMT ||
		tableType == PSITableTypeSDT
}

// parsePSISectionSyntaxHeader parses a PSI section syntax header
func parsePSISectionSyntaxHeader(i []byte, offset *int) (h *PSISectionSyntaxHeader) {
	// Init
	h = &PSISectionSyntaxHeader{}

	// Table ID extension
	h.TableIDExtension = uint16(i[*offset])<<8 | uint16(i[*offset+1])
	*offset += 2

	// Version number
	h.VersionNumber = uint8(i[*offset]&0x3f) >> 1

	// Current/Next indicator
	h.CurrentNextIndicator = i[*offset]&0x1 > 0
	*offset += 1

	// Section number
	h.SectionNumber = uint8(i[*offset])
	*offset += 1

	// Last section number
	h.LastSectionNumber = uint8(i[*offset])
	*offset += 1
	return
}

// parsePSISectionSyntaxData parses a PSI section data
func parsePSISectionSyntaxData(i []byte, offset *int, h *PSISectionHeader, sh *PSISectionSyntaxHeader, offsetSectionsEnd int) (d *PSISectionSyntaxData) {
	// Init
	d = &PSISectionSyntaxData{}

	// Switch on table type
	switch h.TableType {
	case PSITableTypeBAT:
		// TODO Parse BAT
	case PSITableTypeDIT:
		// TODO Parse DIT
	case PSITableTypeEIT:
		d.EIT = parseEITSection(i, offset, offsetSectionsEnd, sh.TableIDExtension)
	case PSITableTypeNIT:
		d.NIT = parseNITSection(i, offset, sh.TableIDExtension)
	case PSITableTypePAT:
		d.PAT = parsePATSection(i, offset, offsetSectionsEnd, sh.TableIDExtension)
	case PSITableTypePMT:
		d.PMT = parsePMTSection(i, offset, offsetSectionsEnd, sh.TableIDExtension)
	case PSITableTypeRST:
		// TODO Parse RST
	case PSITableTypeSDT:
		d.SDT = parseSDTSection(i, offset, offsetSectionsEnd, sh.TableIDExtension)
	case PSITableTypeSIT:
		// TODO Parse SIT
	case PSITableTypeST:
		// TODO Parse ST
	case PSITableTypeTOT:
		d.TOT = parseTOTSection(i, offset)
	case PSITableTypeTDT:
		// TODO Parse TDT
	}
	return
}

// toData parses the PSI tables and returns a set of Data
func (d *PSIData) toData(pid uint16) (ds []*Data) {
	// Loop through sections
	for _, s := range d.Sections {
		// Switch on table type
		switch s.Header.TableType {
		case PSITableTypeEIT:
			ds = append(ds, &Data{EIT: s.Syntax.Data.EIT, PID: pid})
		case PSITableTypeNIT:
			ds = append(ds, &Data{NIT: s.Syntax.Data.NIT, PID: pid})
		case PSITableTypePAT:
			ds = append(ds, &Data{PAT: s.Syntax.Data.PAT, PID: pid})
		case PSITableTypePMT:
			ds = append(ds, &Data{PID: pid, PMT: s.Syntax.Data.PMT})
		case PSITableTypeSDT:
			ds = append(ds, &Data{PID: pid, SDT: s.Syntax.Data.SDT})
		case PSITableTypeTOT:
			ds = append(ds, &Data{PID: pid, TOT: s.Syntax.Data.TOT})
		}
	}
	return
}
<commit_msg>Not using Section syntax indicator anymore<commit_after>package astits

import (
	"fmt"

	"github.com/asticode/go-astilog"
	"github.com/pkg/errors"
)

// PSI table IDs
const (
	PSITableTypeBAT     = "BAT"
	PSITableTypeDIT     = "DIT"
	PSITableTypeEIT     = "EIT"
	PSITableTypeNIT     = "NIT"
	PSITableTypeNull    = "Null"
	PSITableTypePAT     = "PAT"
	PSITableTypePMT     = "PMT"
	PSITableTypeRST     = "RST"
	PSITableTypeSDT     = "SDT"
	PSITableTypeSIT     = "SIT"
	PSITableTypeST      = "ST"
	PSITableTypeTDT     = "TDT"
	PSITableTypeTOT     = "TOT"
	PSITableTypeUnknown = "Unknown"
)

// PSIData represents a PSI data
// https://en.wikipedia.org/wiki/Program-specific_information
type PSIData struct {
	PointerField int // Present at the start of the TS packet payload signaled by the payload_unit_start_indicator bit in the TS header. Used to set packet alignment bytes or content before the start of tabled payload data.
	Sections     []*PSISection
}

// PSISection represents a PSI section
type PSISection struct {
	CRC32  uint32 // A checksum of the entire table excluding the pointer field, pointer filler bytes and the trailing CRC32.
	Header *PSISectionHeader
	Syntax *PSISectionSyntax
}

// PSISectionHeader represents a PSI section header
type PSISectionHeader struct {
	PrivateBit             bool   // The PAT, PMT, and CAT all set this to 0. Other tables set this to 1.
	SectionLength          uint16 // The number of bytes that follow for the syntax section (with CRC value) and/or table data. These bytes must not exceed a value of 1021.
	SectionSyntaxIndicator bool   // A flag that indicates if the syntax section follows the section length. The PAT, PMT, and CAT all set this to 1.
	TableID                int    // Table Identifier, that defines the structure of the syntax section and other contained data. As an exception, if this is the byte that immediately follow previous table section and is set to 0xFF, then it indicates that the repeat of table section end here and the rest of TS data payload shall be stuffed with 0xFF. Consequently the value 0xFF shall not be used for the Table Identifier.
	TableType              string
}

// PSISectionSyntax represents a PSI section syntax
type PSISectionSyntax struct {
	Data   *PSISectionSyntaxData
	Header *PSISectionSyntaxHeader
}

// PSISectionSyntaxHeader represents a PSI section syntax header
type PSISectionSyntaxHeader struct {
	CurrentNextIndicator bool   // Indicates if data is current in effect or is for future use. If the bit is flagged on, then the data is to be used at the present moment.
	LastSectionNumber    uint8  // This indicates which table is the last table in the sequence of tables.
	SectionNumber        uint8  // This is an index indicating which table this is in a related sequence of tables. The first table starts from 0.
	TableIDExtension     uint16 // Informational only identifier. The PAT uses this for the transport stream identifier and the PMT uses this for the Program number.
	VersionNumber        uint8  // Syntax version number. Incremented when data is changed and wrapped around on overflow for values greater than 32.
}

// PSISectionSyntaxData represents a PSI section syntax data
type PSISectionSyntaxData struct {
	EIT *EITData
	NIT *NITData
	PAT *PATData
	PMT *PMTData
	SDT *SDTData
	TOT *TOTData
}

// parsePSIData parses a PSI data
func parsePSIData(i []byte) (d *PSIData, err error) {
	// Init data
	d = &PSIData{}
	var offset int

	// Pointer field
	d.PointerField = int(i[offset])
	offset += 1

	// Pointer filler bytes
	offset += d.PointerField

	// Parse sections
	var s *PSISection
	var stop bool
	for offset < len(i) && !stop {
		if s, stop, err = parsePSISection(i, &offset); err != nil {
			err = errors.Wrap(err, "astits: parsing PSI table failed")
			return
		}
		d.Sections = append(d.Sections, s)
	}
	return
}

// parsePSISection parses a PSI section
func parsePSISection(i []byte, offset *int) (s *PSISection, stop bool, err error) {
	// Init section
	s = &PSISection{}

	// Parse header
	var offsetStart, offsetSectionsEnd, offsetEnd int
	s.Header, offsetStart, _, offsetSectionsEnd, offsetEnd = parsePSISectionHeader(i, offset)

	// Check whether we need to stop the parsing
	if shouldStopPSIParsing(s.Header.TableType) {
		stop = true
		return
	}

	// Check whether there's a syntax section
	if s.Header.SectionLength > 0 {
		// Parse syntax
		s.Syntax = parsePSISectionSyntax(i, offset, s.Header, offsetSectionsEnd)

		// Process CRC32
		if hasCRC32(s.Header.TableType) {
			// Parse CRC32
			s.CRC32 = parseCRC32(i[offsetSectionsEnd:offsetEnd])
			*offset += 4

			// Check CRC32
			var c = computeCRC32(i[offsetStart:offsetSectionsEnd])
			if c != s.CRC32 {
				err = fmt.Errorf("astits: Table CRC32 %x != computed CRC32 %x", s.CRC32, c)
				return
			}
		}
	}
	return
}

// parseCRC32 parses a CRC32
func parseCRC32(i []byte) uint32 {
	return uint32(i[len(i)-4])<<24 | uint32(i[len(i)-3])<<16 | uint32(i[len(i)-2])<<8 | uint32(i[len(i)-1])
}

// computeCRC32 computes a CRC32
// https://stackoverflow.com/questions/35034042/how-to-calculate-crc32-in-psi-si-packet
func computeCRC32(i []byte) (o uint32) {
	o = uint32(0xffffffff)
	for _, b := range i {
		for i := 0; i < 8; i++ {
			if (o >= uint32(0x80000000)) != (b >= uint8(0x80)) {
				o = (o << 1) ^ 0x04C11DB7
			} else {
				o = o << 1
			}
			b <<= 1
		}
	}
	return
}

// shouldStopPSIParsing checks whether the PSI parsing should be stopped
func shouldStopPSIParsing(tableType string) bool {
	return tableType == PSITableTypeNull || tableType == PSITableTypeUnknown
}

// parsePSISectionHeader parses a PSI section header
func parsePSISectionHeader(i []byte, offset *int) (h *PSISectionHeader, offsetStart, offsetSectionsStart, offsetSectionsEnd, offsetEnd int) {
	// Init
	h = &PSISectionHeader{}
	offsetStart = *offset

	// Table ID
	h.TableID = int(i[*offset])
	*offset += 1

	// Table type
	h.TableType = psiTableType(h.TableID)

	// Check whether we need to stop the parsing
	if shouldStopPSIParsing(h.TableType) {
		return
	}

	// Section syntax indicator
	h.SectionSyntaxIndicator = i[*offset]&0x80 > 0

	// Private bit
	h.PrivateBit = i[*offset]&0x40 > 0

	// Section length
	h.SectionLength = uint16(i[*offset]&0xf)<<8 | uint16(i[*offset+1])
	*offset += 2

	// Offsets
	offsetSectionsStart = *offset
	offsetEnd = offsetSectionsStart + int(h.SectionLength)
	offsetSectionsEnd = offsetEnd
	if hasCRC32(h.TableType) {
		offsetSectionsEnd -= 4
	}
	return
}

// hasCRC32 checks whether the table has a CRC32
func hasCRC32(tableType string) bool {
	return tableType == PSITableTypePAT ||
		tableType == PSITableTypePMT ||
		tableType == PSITableTypeEIT ||
		tableType == PSITableTypeNIT ||
		tableType == PSITableTypeTOT ||
		tableType == PSITableTypeSDT
}

// psiTableType returns the psi table type based on the table id
func psiTableType(tableID int) string {
	switch {
	case tableID == 0x4a:
		return PSITableTypeBAT
	case tableID >= 0x4e && tableID <= 0x6f:
		return PSITableTypeEIT
	case tableID == 0x7e:
		return PSITableTypeDIT
	case tableID == 0x40, tableID == 0x41:
		return PSITableTypeNIT
	case tableID == 0xff:
		return PSITableTypeNull
	case tableID == 0:
		return PSITableTypePAT
	case tableID == 2:
		return PSITableTypePMT
	case tableID == 0x71:
		return PSITableTypeRST
	case tableID == 0x42, tableID == 0x46:
		return PSITableTypeSDT
	case tableID == 0x7f:
		return PSITableTypeSIT
	case tableID == 0x72:
		return PSITableTypeST
	case tableID == 0x70:
		return PSITableTypeTDT
	case tableID == 0x73:
		return PSITableTypeTOT
	}
	// TODO Remove this log
	astilog.Debugf("unlisted PSI table ID %d", tableID)
	return PSITableTypeUnknown
}

// parsePSISectionSyntax parses a PSI section syntax
func parsePSISectionSyntax(i []byte, offset *int, h *PSISectionHeader, offsetSectionsEnd int) (s *PSISectionSyntax) {
	// Init
	s = &PSISectionSyntax{}

	// Header
	if hasPSISyntaxHeader(h.TableType) {
		s.Header = parsePSISectionSyntaxHeader(i, offset)
	}

	// Parse data
	s.Data = parsePSISectionSyntaxData(i, offset, h, s.Header, offsetSectionsEnd)
	return
}

// hasPSISyntaxHeader checks whether the section has a syntax header
func hasPSISyntaxHeader(tableType string) bool {
	return tableType == PSITableTypeEIT ||
		tableType == PSITableTypeNIT ||
		tableType == PSITableTypePAT ||
		tableType == PSITableTypePMT ||
		tableType == PSITableTypeSDT
}

// parsePSISectionSyntaxHeader parses a PSI section syntax header
func parsePSISectionSyntaxHeader(i []byte, offset *int) (h *PSISectionSyntaxHeader) {
	// Init
	h = &PSISectionSyntaxHeader{}

	// Table ID extension
	h.TableIDExtension = uint16(i[*offset])<<8 | uint16(i[*offset+1])
	*offset += 2

	// Version number
	h.VersionNumber = uint8(i[*offset]&0x3f) >> 1

	// Current/Next indicator
	h.CurrentNextIndicator = i[*offset]&0x1 > 0
	*offset += 1

	// Section number
	h.SectionNumber = uint8(i[*offset])
	*offset += 1

	// Last section number
	h.LastSectionNumber = uint8(i[*offset])
	*offset += 1
	return
}

// parsePSISectionSyntaxData parses a PSI section data
func parsePSISectionSyntaxData(i []byte, offset *int, h *PSISectionHeader, sh *PSISectionSyntaxHeader, offsetSectionsEnd int) (d *PSISectionSyntaxData) {
	// Init
	d = &PSISectionSyntaxData{}

	// Switch on table type
	switch h.TableType {
	case PSITableTypeBAT:
		// TODO Parse BAT
	case PSITableTypeDIT:
		// TODO Parse DIT
	case PSITableTypeEIT:
		d.EIT = parseEITSection(i, offset, offsetSectionsEnd, sh.TableIDExtension)
	case PSITableTypeNIT:
		d.NIT = parseNITSection(i, offset, sh.TableIDExtension)
	case PSITableTypePAT:
		d.PAT = parsePATSection(i, offset, offsetSectionsEnd, sh.TableIDExtension)
	case PSITableTypePMT:
		d.PMT = parsePMTSection(i, offset, offsetSectionsEnd, sh.TableIDExtension)
	case PSITableTypeRST:
		// TODO Parse RST
	case PSITableTypeSDT:
		d.SDT = parseSDTSection(i, offset, offsetSectionsEnd, sh.TableIDExtension)
	case PSITableTypeSIT:
		// TODO Parse SIT
	case PSITableTypeST:
		// TODO Parse ST
	case PSITableTypeTOT:
		d.TOT = parseTOTSection(i, offset)
	case PSITableTypeTDT:
		// TODO Parse TDT
	}
	return
}

// toData parses the PSI tables and returns a set of Data
func (d *PSIData) toData(pid uint16) (ds []*Data) {
	// Loop through sections
	for _, s := range d.Sections {
		// Switch on table type
		switch s.Header.TableType {
		case PSITableTypeEIT:
			ds = append(ds, &Data{EIT: s.Syntax.Data.EIT, PID: pid})
		case PSITableTypeNIT:
			ds = append(ds, &Data{NIT: s.Syntax.Data.NIT, PID: pid})
		case PSITableTypePAT:
			ds = append(ds, &Data{PAT: s.Syntax.Data.PAT, PID: pid})
		case PSITableTypePMT:
			ds = append(ds, &Data{PID: pid, PMT: s.Syntax.Data.PMT})
		case PSITableTypeSDT:
			ds = append(ds, &Data{PID: pid, SDT: s.Syntax.Data.SDT})
		case PSITableTypeTOT:
			ds = append(ds, &Data{PID: pid, TOT: s.Syntax.Data.TOT})
		}
	}
	return
}
<|endoftext|>


<commit_before>package lob

import "strconv"

// Check represents a printed check in Lob's system.
type Check struct {
	Amount               string              `json:"amount"`
	BankAccount          *BankAccount        `json:"bank_account"`
	CheckNumber          int                 `json:"check_number"`
	DateCreated          string              `json:"date_created"`
	DateModified         string              `json:"date_modified"`
	ExpectedDeliveryDate string              `json:"expected_delivery_date"`
	ID                   string              `json:"id"`
	Memo                 string              `json:"memo"`
	Message              string              `json:"message"`
	Name                 string              `json:"name"`
	Object               string              `json:"object"`
	Price                string              `json:"price"`
	Status               string              `json:"status"`
	Thumbnails           []map[string]string `json:"thumbnails"`
	To                   *Address            `json:"to"`
	Tracking             *Tracking           `json:"tracking"`
	URL                  string              `json:"url"`
}

// Tracking provides information on shipment tracking for a check.
type Tracking struct {
	Carrier        string        `json:"carrier"`
	Events         []interface{} `json:"events"`
	ID             string        `json:"id"`
	Link           string        `json:"link"`
	Object         string        `json:"object"`
	TrackingNumber string        `json:"tracking_number"`
}

// CreateCheckRequest specifies options for creating a check.
type CreateCheckRequest struct {
	Name          string `json:"name"`
	CheckNumber   string `json:"check_number"`
	BankAccountID string `json:"bank_account"`
	ToAddressID   string `json:"to"`
	Amount        string `json:"amount"`
	Message       string `json:"message"` // 400 chars, at top
	Memo          string `json:"memo"`    // 40 chars in memo line
	Logo          string `json:"logo"`    // url or multiform. Square, RGB / CMYK, >= 100x100, transparent bg, PNG or JPEG, and will be grayscaled
}

// CreateCheck requests for a new check to be printed and mailed.
func (lob *Lob) CreateCheck(req *CreateCheckRequest) (*Check, error) {
	resp := new(Check)
	return resp, Metrics.CreateCheck.Call(func() error {
		return lob.Post("checks/", json2form(*req), resp)
	})
}

// GetCheck gets information about a particulr check.
func (lob *Lob) GetCheck(id string) (*Check, error) {
	resp := new(Check)
	return resp, Metrics.GetCheck.Call(func() error {
		return lob.Get("checks/"+id, nil, resp)
	})
}

// ListChecksResponse details all of the checks we've ever mailed and printed.
type ListChecksResponse struct {
	Data        []Check `json:"data"`
	Object      string  `json:"object"`
	NextURL     string  `json:"next_url"`
	PreviousURL string  `json:"next_url"`
	Count       int     `json:"count"`
}

// ListChecks retrieves information on all checks we've ever made, in reverse chrono order.
func (lob *Lob) ListChecks(count, offset int) (*ListChecksResponse, error) {
	if count <= 0 {
		count = 10
	}
	if offset < 0 {
		offset = 0
	}
	resp := new(ListChecksResponse)
	return resp, Metrics.ListChecks.Call(func() error {
		return lob.Get("checks", map[string]string{
			"count":  strconv.Itoa(count),
			"offset": strconv.Itoa(offset),
		}, resp)
	})
}
<commit_msg>Amount and price on checks are numbers now<commit_after>package lob

import "strconv"

// Check represents a printed check in Lob's system.
type Check struct {
	Amount               float64             `json:"amount"`
	BankAccount          *BankAccount        `json:"bank_account"`
	CheckNumber          int                 `json:"check_number"`
	DateCreated          string              `json:"date_created"`
	DateModified         string              `json:"date_modified"`
	ExpectedDeliveryDate string              `json:"expected_delivery_date"`
	ID                   string              `json:"id"`
	Memo                 string              `json:"memo"`
	Message              string              `json:"message"`
	Name                 string              `json:"name"`
	Object               string              `json:"object"`
	Price                float64             `json:"price"`
	Status               string              `json:"status"`
	Thumbnails           []map[string]string `json:"thumbnails"`
	To                   *Address            `json:"to"`
	Tracking             *Tracking           `json:"tracking"`
	URL                  string              `json:"url"`
}

// Tracking provides information on shipment tracking for a check.
type Tracking struct {
	Carrier        string        `json:"carrier"`
	Events         []interface{} `json:"events"`
	ID             string        `json:"id"`
	Link           string        `json:"link"`
	Object         string        `json:"object"`
	TrackingNumber string        `json:"tracking_number"`
}

// CreateCheckRequest specifies options for creating a check.
type CreateCheckRequest struct {
	Name          string `json:"name"`
	CheckNumber   string `json:"check_number"`
	BankAccountID string `json:"bank_account"`
	ToAddressID   string `json:"to"`
	Amount        string `json:"amount"`
	Message       string `json:"message"` // 400 chars, at top
	Memo          string `json:"memo"`    // 40 chars in memo line
	Logo          string `json:"logo"`    // url or multiform. Square, RGB / CMYK, >= 100x100, transparent bg, PNG or JPEG, and will be grayscaled
}

// CreateCheck requests for a new check to be printed and mailed.
func (lob *Lob) CreateCheck(req *CreateCheckRequest) (*Check, error) {
	resp := new(Check)
	return resp, Metrics.CreateCheck.Call(func() error {
		return lob.Post("checks/", json2form(*req), resp)
	})
}

// GetCheck gets information about a particulr check.
func (lob *Lob) GetCheck(id string) (*Check, error) {
	resp := new(Check)
	return resp, Metrics.GetCheck.Call(func() error {
		return lob.Get("checks/"+id, nil, resp)
	})
}

// ListChecksResponse details all of the checks we've ever mailed and printed.
type ListChecksResponse struct {
	Data        []Check `json:"data"`
	Object      string  `json:"object"`
	NextURL     string  `json:"next_url"`
	PreviousURL string  `json:"next_url"`
	Count       int     `json:"count"`
}

// ListChecks retrieves information on all checks we've ever made, in reverse chrono order.
func (lob *Lob) ListChecks(count, offset int) (*ListChecksResponse, error) {
	if count <= 0 {
		count = 10
	}
	if offset < 0 {
		offset = 0
	}
	resp := new(ListChecksResponse)
	return resp, Metrics.ListChecks.Call(func() error {
		return lob.Get("checks", map[string]string{
			"count":  strconv.Itoa(count),
			"offset": strconv.Itoa(offset),
		}, resp)
	})
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"

	"github.com/hashicorp/terraform/helper/config"
	"github.com/hashicorp/terraform/terraform"

	clc "github.com/CenturyLinkCloud/clc-sdk"
	"github.com/CenturyLinkCloud/clc-sdk/api"
	"github.com/CenturyLinkCloud/clc-sdk/server"
	"github.com/CenturyLinkCloud/clc-sdk/status"

	"github.com/mitchellh/mapstructure"

	"github.com/kr/pretty"
)

type Provisioner struct {
	Username          string            `mapstructure:"username"`
	Password          string            `mapstructure:"password"`
	Account           string            `mapstructure:"account"`
	Package           string            `mapstructure:"package"`
	PackageParameters map[string]string `mapstructure:"parameters"`
}

type ResourceProvisioner struct{}

func (r *ResourceProvisioner) Apply(
	o terraform.UIOutput,
	s *terraform.InstanceState,
	c *terraform.ResourceConfig) error {

	log.Printf("Got to Apply for clc_exec provisioner")

	log.Printf("InstanceState looks like:")
	// fmt.Printf("%+v\n", s)
	// fmt.Printf("InstanceState: %# v", pretty.Formatter(s))
	pretty.Log(s)

	var err error

	// Decode the raw config for this provisioner
	p, err := r.decodeConfig(c)
	if err != nil {
		return err
	}

	log.Print("Provisioner looks like:")
	pretty.Log(p)

	log.Printf("Username = %s, Password = %s, Alias = %s", p.Username, p.Password, p.Account)
	log.Printf("Attempting to execute package %s", p.Package)
	log.Print("Package Parameters looks like:")
	pretty.Log(p.PackageParameters)

	server_id := s.ID
	log.Printf("Server name = %s", server_id)
	o.Output(fmt.Sprintf("Executing package '%s' on server '%s'", p.Package, server_id))

	// Create a CLC config
	config, err := api.NewConfig(p.Username, p.Password, p.Account, "")
	if err != nil {
		return fmt.Errorf("Failed to create CLC config with provided details: %v", err)
	}

	// Create a new CLC Client
	client := clc.New(config)

	// Make sure we can authentication
	err = client.Authenticate()
	if err != nil {
		return fmt.Errorf("Failed authenticated with provided credentials: %v", err)
	}

	// Create the pkg structure
	package_exec_spec := server.Package{
		ID:     p.Package,
		Params: p.PackageParameters,
	}

	// Execute the package
	pkg_resp, err := client.Server.ExecutePackage(package_exec_spec, server_id)
	if err != nil || !pkg_resp.IsQueued {
		return fmt.Errorf("Failed executing package: %v", err)
	}

	// Check status
	ok, st := pkg_resp.GetStatusID()
	if !ok {
		return fmt.Errorf("Failed extracting status to poll on %v: %v", pkg_resp, err)
	}
	err = waitStatus(client, st)
	if err != nil {
		return err
	}

	o.Output(fmt.Sprintf("Package %s successfully executed on %s", p.Package, server_id))

	return nil
}

func (r *ResourceProvisioner) Validate(c *terraform.ResourceConfig) (ws []string, es []error) {
	v := &config.Validator{
		Required: []string{
			"username", "password", "account", "package",
		},
		Optional: []string{
			"parameters.*",
		},
	}
	return v.Validate(c)
}

func (r *ResourceProvisioner) decodeConfig(c *terraform.ResourceConfig) (*Provisioner, error) {
	log.Printf("Got to decodeConfig for clc_exec")

	log.Print("ResourceConfig looks like:")
	pretty.Log(c)

	p := new(Provisioner)

	decConf := &mapstructure.DecoderConfig{
		ErrorUnused:      true,
		WeaklyTypedInput: true,
		Result:           p,
	}

	dec, err := mapstructure.NewDecoder(decConf)
	if err != nil {
		return nil, err
	}

	m := make(map[string]interface{})
	for k, v := range c.Raw {
		m[k] = v
	}

	for k, v := range c.Config {
		m[k] = v
	}

	if err := dec.Decode(m); err != nil {
		return nil, err
	}

	return p, nil

}

// package utility functions

func waitStatus(client *clc.Client, id string) error {
	// block until queue is processed and server is up
	poll := make(chan *status.Response, 1)
	err := client.Status.Poll(id, poll)
	if err != nil {
		return nil
	}
	status := <-poll
	log.Printf("[DEBUG] status %v", status)
	if status.Failed() {
		return fmt.Errorf("unsuccessful job %v failed with status: %v", id, status.Status)
	}
	return nil
}
<commit_msg>Fix issue with trying to access value from an array response<commit_after>package main

import (
	"fmt"
	"log"

	"github.com/hashicorp/terraform/helper/config"
	"github.com/hashicorp/terraform/terraform"

	clc "github.com/CenturyLinkCloud/clc-sdk"
	"github.com/CenturyLinkCloud/clc-sdk/api"
	"github.com/CenturyLinkCloud/clc-sdk/server"
	"github.com/CenturyLinkCloud/clc-sdk/status"

	"github.com/mitchellh/mapstructure"

	"github.com/kr/pretty"
)

type Provisioner struct {
	Username          string            `mapstructure:"username"`
	Password          string            `mapstructure:"password"`
	Account           string            `mapstructure:"account"`
	Package           string            `mapstructure:"package"`
	PackageParameters map[string]string `mapstructure:"parameters"`
}

type ResourceProvisioner struct{}

func (r *ResourceProvisioner) Apply(
	o terraform.UIOutput,
	s *terraform.InstanceState,
	c *terraform.ResourceConfig) error {

	log.Printf("Got to Apply for clc_exec provisioner")

	log.Printf("InstanceState looks like:")
	// fmt.Printf("%+v\n", s)
	// fmt.Printf("InstanceState: %# v", pretty.Formatter(s))
	pretty.Log(s)

	var err error

	// Decode the raw config for this provisioner
	p, err := r.decodeConfig(c)
	if err != nil {
		return err
	}

	log.Print("Provisioner looks like:")
	pretty.Log(p)

	log.Printf("Username = %s, Password = %s, Alias = %s", p.Username, p.Password, p.Account)
	log.Printf("Attempting to execute package %s", p.Package)
	log.Print("Package Parameters looks like:")
	pretty.Log(p.PackageParameters)

	server_id := s.ID
	log.Printf("Server name = %s", server_id)
	o.Output(fmt.Sprintf("Executing package '%s' on server '%s'", p.Package, server_id))

	// Create a CLC config
	config, err := api.NewConfig(p.Username, p.Password, p.Account, "")
	if err != nil {
		return fmt.Errorf("Failed to create CLC config with provided details: %v", err)
	}

	// Create a new CLC Client
	client := clc.New(config)

	// Make sure we can authentication
	err = client.Authenticate()
	if err != nil {
		return fmt.Errorf("Failed authenticated with provided credentials: %v", err)
	}

	// Create the pkg structure
	package_exec_spec := server.Package{
		ID:     p.Package,
		Params: p.PackageParameters,
	}

	// Execute the package
	// TODO: Is this a bit hacky just picking the first array entry?
	resp, err := client.Server.ExecutePackage(package_exec_spec, server_id)
	if err != nil || !resp[0].IsQueued {
		return fmt.Errorf("Failed executing package: %v", err)
	}

	// Check status
	// TODO: Is this a bit hacky just picking the first array entry?
	ok, st := resp[0].GetStatusID()
	if !ok {
		return fmt.Errorf("Failed extracting status to poll on %v: %v", resp, err)
	}
	err = waitStatus(client, st)
	if err != nil {
		return err
	}

	o.Output(fmt.Sprintf("Package %s successfully executed on %s", p.Package, server_id))

	return nil
}

func (r *ResourceProvisioner) Validate(c *terraform.ResourceConfig) (ws []string, es []error) {
	v := &config.Validator{
		Required: []string{
			"username", "password", "account", "package",
		},
		Optional: []string{
			"parameters.*",
		},
	}
	return v.Validate(c)
}

func (r *ResourceProvisioner) decodeConfig(c *terraform.ResourceConfig) (*Provisioner, error) {
	log.Printf("Got to decodeConfig for clc_exec")

	log.Print("ResourceConfig looks like:")
	pretty.Log(c)

	p := new(Provisioner)

	decConf := &mapstructure.DecoderConfig{
		ErrorUnused:      true,
		WeaklyTypedInput: true,
		Result:           p,
	}

	dec, err := mapstructure.NewDecoder(decConf)
	if err != nil {
		return nil, err
	}

	m := make(map[string]interface{})
	for k, v := range c.Raw {
		m[k] = v
	}

	for k, v := range c.Config {
		m[k] = v
	}

	if err := dec.Decode(m); err != nil {
		return nil, err
	}

	return p, nil

}

// package utility functions

func waitStatus(client *clc.Client, id string) error {
	// block until queue is processed and server is up
	poll := make(chan *status.Response, 1)
	err := client.Status.Poll(id, poll)
	if err != nil {
		return nil
	}
	status := <-poll
	log.Printf("[DEBUG] status %v", status)
	if status.Failed() {
		return fmt.Errorf("unsuccessful job %v failed with status: %v", id, status.Status)
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"math/rand"
	"strconv"
	"time"

	"github.com/carlmjohnson/go-utils/normalizedsort"
)

var notes = [12]string{"C", "C#", "D", "Eb", "E", "F", "F#", "G", "G#", "A", "Bb", "B"}

func NoteName(index int) string {
	return notes[index%12]
}

func NoteIndex(note string) int {
	for i, n := range notes {
		if n == note {
			return i
		}
	}
	return -1
}

var offsets = [6]int{4, 9, 2, 7, 11, 4}

func NotePos(note string, s int) int {
	return (NoteIndex(note) - offsets[s] + 12) % 12
}

type Chord struct {
	Name string
	Pos  [][6]int
}

func (c Chord) String() string {
	s := c.Name

	for i := 5; i >= 0; i-- {
		s += "\n  -"
		for _, pos := range c.Pos {
			note := pos[i]
			if note == -1 {
				s += "-X--"
			} else {
				n := strconv.Itoa(note)
				if len(n) == 1 {
					s += "-" + n + "--"
				} else {
					s += n + "--"
				}
			}
		}
	}

	return s + "\n"
}

func Major7th(root string) Chord {
	index0, index1, index2 := NotePos(root, 0), NotePos(root, 1), NotePos(root, 2)

	return Chord{
		Name: root + " Maj7",
		Pos: [][6]int{
			// X R 2 1 2 X
			{-1, index1, index1 + 2, index1 + 1, index1 + 2, -1},
			// X X R 2 2 2
			{-1, -1, index2, index2 + 2, index2 + 2, index2 + 2},
			// R X 1 1 0 X
			{index0, -1, index0 + 1, index0 + 1, index0, -1},
			// X R X 1 2 0
			{-1, index1, -1, index1 + 1, index1 + 2, index1},
		},
	}
}

func Major6th(root string) Chord {
	index0, index1, index2 := NotePos(root, 0), NotePos(root, 1), NotePos(root, 2)

	return Chord{
		Name: root + "Maj6",
		Pos: [][6]int{
			// X R 2 1 2 X
			{-1, index1, index1 + 2, index1 + 1, index1 + 2, -1},
			// X X R 2 0 2
			{-1, -1, index2, index2 + 2, index2, index2 + 2},
			// R X -1 1 0 X
			{index0, -1, index0 - 1, index0 + 1, index0, -1},
			// X R X -1 2 0
			{-1, index1, -1, index1 - 1, index1 + 2, index1},
		},
	}
}

func Minor7th(root string) Chord {
	index0, index1, index2 := NotePos(root, 0), NotePos(root, 1), NotePos(root, 2)

	return Chord{
		Name: root + "Min7",
		Pos: [][6]int{
			// X R 2 0 1 X
			{-1, index1, index1 + 2, index1, index1 + 1, index1},
			// X X R 2 1 1
			{-1, -1, index2, index2 + 2, index2 + 1, index2 + 1},
			// R X 0 0 0 X
			{index0, -1, index0, index0, index0, -1},
			// X R X 0 1 0
			{-1, index1, -1, index1, index1 + 1, index1},
		},
	}
}

func Minor6th(root string) Chord {
	index0, index1, index2 := NotePos(root, 0), NotePos(root, 1), NotePos(root, 2)

	return Chord{
		Name: root + "Min6",
		Pos: [][6]int{
			// X R 2 -1 1 X
			{-1, index1, index1 + 2, index1 - 1, index1 + 1, -1},
			// X X R 2 0 1
			{-1, -1, index2, index2 + 2, index2, index2 + 1},
			// R X -1 0 0 X
			{index0, -1, index0 - 1, index0, index0, -1},
			// X R X -1 1 0
			{-1, index1, -1, index1 - 1, index1 + 1, index1},
		},
	}
}

func Minor9th(root string) Chord {
	index1, index2 := NotePos(root, 1), NotePos(root, 2)

	return Chord{
		Name: root + "Min9",
		Pos: [][6]int{
			// X R -2 0 0 X
			{-1, index1, index1 - 2, index1, index1, -1},
			// X X R -2 1 0
			{-1, -1, index2, index2 - 2, index2 + 1, index2},
		},
	}
}

func Minor11th(root string) Chord {
	index0, index1 := NotePos(root, 0), NotePos(root, 1)

	return Chord{
		Name: root + "Min11",
		Pos: [][6]int{
			// R X 0 0 -2 X
			{index0, -1, index0, index0, index0 - 2, -1},
			// X R X 0 1 -2
			{-1, index1, -1, index1, index1 + 1, index1 - 2},
		},
	}
}

func Dominant7th(root string) Chord {
	index0, index1, index2 := NotePos(root, 0), NotePos(root, 1), NotePos(root, 2)

	return Chord{
		Name: root + "7",
		Pos: [][6]int{
			// X R 2 0 2 X
			{-1, index1, index1 + 2, index1, index1 + 2, -1},
			// X X R 2 1 2
			{-1, -1, index2, index2 + 2, index2 + 1, index2 + 2},
			// R X 0 1 0 X
			{index0, -1, index0, index0 + 1, index0, -1},
			// X R X 0 2 0
			{-1, index1, -1, index1, index1 + 2, index1},
		},
	}
}

func Dominant9th(root string) Chord {
	index0, index1, index2 := NotePos(root, 0), NotePos(root, 1), NotePos(root, 2)

	return Chord{
		Name: root + "9",
		Pos: [][6]int{
			// X R -1 0 0 X
			{-1, index1, index1 - 1, index1, index1, -1},
			// X X R -1 1 0
			{-1, -1, index2, index2 - 1, index2 + 1, index2},
			// R X 0 -1 -3 X
			{index0, -1, index0, index0 - 1, index0 - 3, -1},
			// X R X 0 0 -3
			{-1, index1, -1, index1, index1, index1 - 3},
		},
	}
}

func Dominant13th(root string) Chord {
	index0, index1 := NotePos(root, 0), NotePos(root, 1)

	return Chord{
		Name: root + "13",
		Pos: [][6]int{
			// X R -1 0 0 2
			{-1, index1, index1 - 1, index1, index1, index1 + 2},
			// X R X 0 2 2
			{-1, index1, -1, index1, index1 + 2, index1 + 2},
			// R X 0 1 2 X
			{index0, -1, index0, index0 + 1, index0 + 2, -1},
		},
	}
}

func Minor7thFlat5(root string) Chord {
	index0, index1, index2 := NotePos(root, 0), NotePos(root, 1), NotePos(root, 2)

	return Chord{
		Name: root + "Min7(b5)",
		Pos: [][6]int{
			// X R 1 0 1 0 X
			{-1, index1, index1 + 1, index1, index1 + 1, -1},
			// X X R 1 1 1 1
			{-1, -1, index2, index2 + 1, index2 + 1, index2 + 1},
			// R X 0 0 -1 X
			{index0, -1, index0, index0, index0 - 1, -1},
			// X R X 0 1 -1
			{-1, index1, -1, index1, index1 + 1, index1 - 1},
		},
	}
}

var Variants = []func(string) Chord{
	Major7th,
	Major6th,
	Minor7th,
	Minor6th,
	Minor9th,
	Minor11th,
	Dominant7th,
	Dominant9th,
	Dominant13th,
	Minor7thFlat5,
}

var rnd = rand.New(rand.NewSource(time.Now().UnixNano()))

func Random(root string) Chord {
	i := rnd.Intn(len(Variants))
	return Variants[i](root)
}

func main() {
	copyNotes := make([]string, 12)
	copy(copyNotes, notes[:])

	normalizedsort.Sort(copyNotes, nil)

	fmt.Println(Random(copyNotes[0]))
	fmt.Println(Random(copyNotes[1]))
	fmt.Println(Random(copyNotes[2]))
}
<commit_msg>Shuffle!; and make interactive<commit_after>package main

import (
	"fmt"
	"log"
	"math/rand"
	"strconv"
	"time"
)

var notes = [12]string{"C", "C#", "D", "Eb", "E", "F", "F#", "G", "G#", "A", "Bb", "B"}

func NoteName(index int) string {
	return notes[index%12]
}

func NoteIndex(note string) int {
	for i, n := range notes {
		if n == note {
			return i
		}
	}
	return -1
}

var offsets = [6]int{4, 9, 2, 7, 11, 4}

func NotePos(note string, s int) int {
	return (NoteIndex(note) - offsets[s] + 12) % 12
}

type Chord struct {
	Name string
	Pos  [][6]int
}

func (c Chord) String() string {
	s := ""

	for i := 5; i >= 0; i-- {
		s += "\n  -"
		for _, pos := range c.Pos {
			note := pos[i]
			if note == -1 {
				s += "-X--"
			} else {
				n := strconv.Itoa(note)
				if len(n) == 1 {
					s += "-" + n + "--"
				} else {
					s += n + "--"
				}
			}
		}
	}

	return s + "\n"
}

func Major7th(root string) Chord {
	index0, index1, index2 := NotePos(root, 0), NotePos(root, 1), NotePos(root, 2)

	return Chord{
		Name: root + " Maj7",
		Pos: [][6]int{
			// X R 2 1 2 X
			{-1, index1, index1 + 2, index1 + 1, index1 + 2, -1},
			// X X R 2 2 2
			{-1, -1, index2, index2 + 2, index2 + 2, index2 + 2},
			// R X 1 1 0 X
			{index0, -1, index0 + 1, index0 + 1, index0, -1},
			// X R X 1 2 0
			{-1, index1, -1, index1 + 1, index1 + 2, index1},
		},
	}
}

func Major6th(root string) Chord {
	index0, index1, index2 := NotePos(root, 0), NotePos(root, 1), NotePos(root, 2)

	return Chord{
		Name: root + "Maj6",
		Pos: [][6]int{
			// X R 2 1 2 X
			{-1, index1, index1 + 2, index1 + 1, index1 + 2, -1},
			// X X R 2 0 2
			{-1, -1, index2, index2 + 2, index2, index2 + 2},
			// R X -1 1 0 X
			{index0, -1, index0 - 1, index0 + 1, index0, -1},
			// X R X -1 2 0
			{-1, index1, -1, index1 - 1, index1 + 2, index1},
		},
	}
}

func Minor7th(root string) Chord {
	index0, index1, index2 := NotePos(root, 0), NotePos(root, 1), NotePos(root, 2)

	return Chord{
		Name: root + "Min7",
		Pos: [][6]int{
			// X R 2 0 1 X
			{-1, index1, index1 + 2, index1, index1 + 1, index1},
			// X X R 2 1 1
			{-1, -1, index2, index2 + 2, index2 + 1, index2 + 1},
			// R X 0 0 0 X
			{index0, -1, index0, index0, index0, -1},
			// X R X 0 1 0
			{-1, index1, -1, index1, index1 + 1, index1},
		},
	}
}

func Minor6th(root string) Chord {
	index0, index1, index2 := NotePos(root, 0), NotePos(root, 1), NotePos(root, 2)

	return Chord{
		Name: root + "Min6",
		Pos: [][6]int{
			// X R 2 -1 1 X
			{-1, index1, index1 + 2, index1 - 1, index1 + 1, -1},
			// X X R 2 0 1
			{-1, -1, index2, index2 + 2, index2, index2 + 1},
			// R X -1 0 0 X
			{index0, -1, index0 - 1, index0, index0, -1},
			// X R X -1 1 0
			{-1, index1, -1, index1 - 1, index1 + 1, index1},
		},
	}
}

func Minor9th(root string) Chord {
	index1, index2 := NotePos(root, 1), NotePos(root, 2)

	return Chord{
		Name: root + "Min9",
		Pos: [][6]int{
			// X R -2 0 0 X
			{-1, index1, index1 - 2, index1, index1, -1},
			// X X R -2 1 0
			{-1, -1, index2, index2 - 2, index2 + 1, index2},
		},
	}
}

func Minor11th(root string) Chord {
	index0, index1 := NotePos(root, 0), NotePos(root, 1)

	return Chord{
		Name: root + "Min11",
		Pos: [][6]int{
			// R X 0 0 -2 X
			{index0, -1, index0, index0, index0 - 2, -1},
			// X R X 0 1 -2
			{-1, index1, -1, index1, index1 + 1, index1 - 2},
		},
	}
}

func Dominant7th(root string) Chord {
	index0, index1, index2 := NotePos(root, 0), NotePos(root, 1), NotePos(root, 2)

	return Chord{
		Name: root + "7",
		Pos: [][6]int{
			// X R 2 0 2 X
			{-1, index1, index1 + 2, index1, index1 + 2, -1},
			// X X R 2 1 2
			{-1, -1, index2, index2 + 2, index2 + 1, index2 + 2},
			// R X 0 1 0 X
			{index0, -1, index0, index0 + 1, index0, -1},
			// X R X 0 2 0
			{-1, index1, -1, index1, index1 + 2, index1},
		},
	}
}

func Dominant9th(root string) Chord {
	index0, index1, index2 := NotePos(root, 0), NotePos(root, 1), NotePos(root, 2)

	return Chord{
		Name: root + "9",
		Pos: [][6]int{
			// X R -1 0 0 X
			{-1, index1, index1 - 1, index1, index1, -1},
			// X X R -1 1 0
			{-1, -1, index2, index2 - 1, index2 + 1, index2},
			// R X 0 -1 -3 X
			{index0, -1, index0, index0 - 1, index0 - 3, -1},
			// X R X 0 0 -3
			{-1, index1, -1, index1, index1, index1 - 3},
		},
	}
}

func Dominant13th(root string) Chord {
	index0, index1 := NotePos(root, 0), NotePos(root, 1)

	return Chord{
		Name: root + "13",
		Pos: [][6]int{
			// X R -1 0 0 2
			{-1, index1, index1 - 1, index1, index1, index1 + 2},
			// X R X 0 2 2
			{-1, index1, -1, index1, index1 + 2, index1 + 2},
			// R X 0 1 2 X
			{index0, -1, index0, index0 + 1, index0 + 2, -1},
		},
	}
}

func Minor7thFlat5(root string) Chord {
	index0, index1, index2 := NotePos(root, 0), NotePos(root, 1), NotePos(root, 2)

	return Chord{
		Name: root + "Min7(b5)",
		Pos: [][6]int{
			// X R 1 0 1 0 X
			{-1, index1, index1 + 1, index1, index1 + 1, -1},
			// X X R 1 1 1 1
			{-1, -1, index2, index2 + 1, index2 + 1, index2 + 1},
			// R X 0 0 -1 X
			{index0, -1, index0, index0, index0 - 1, -1},
			// X R X 0 1 -1
			{-1, index1, -1, index1, index1 + 1, index1 - 1},
		},
	}
}

var Variants = []func(string) Chord{
	Major7th,
	Major6th,
	Minor7th,
	Minor6th,
	Minor9th,
	Minor11th,
	Dominant7th,
	Dominant9th,
	Dominant13th,
	Minor7thFlat5,
}

var rnd = rand.New(rand.NewSource(time.Now().UnixNano()))

func Random(root string) Chord {
	i := rnd.Intn(len(Variants))
	return Variants[i](root)
}

func Shuffle(s []string) {
	for i := len(s) - 1; i > 0; i-- {
		j := rnd.Intn(i + 1)
		s[i], s[j] = s[j], s[i]
	}
}

func main() {
	copyNotes := make([]string, 12)
	copy(copyNotes, notes[:])

	Shuffle(copyNotes)
	log.Println(copyNotes)

	for i := 0; ; i = (i + 1) % 12 {
		chord := Random(copyNotes[i])
		fmt.Println(chord.Name)
		fmt.Print("Show?")
		fmt.Scanf("Show?")

		fmt.Println(chord)
	}
}
<|endoftext|>


<commit_before>package clever

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os/exec"
	"runtime"
)

const debug = false

var unameCache []byte

// Clever wraps the Clever API at the specified URL e.g. "https://api.clever.com"
type Clever struct {
	client *http.Client
	url    string
}

// BasicAuthTransport is an http.Transport that performs HTTP Basic Auth.
type BasicAuthTransport struct {
	Username string
	Password string
}

func setTrackingHeaders(req *http.Request) {
	// Want to swallow errors here because desired behavior is nil --> blank string if value doesn't exist
	if unameCache == nil {
		unameCache, _ = exec.Command("uname", "-a").Output()
	}
	customUA, _ := json.Marshal(map[string]string{
		"lang":             "go",
		"lang_version":     runtime.Version(),
		"platform":         runtime.GOOS,
		"uname":            fmt.Sprintf("%s", unameCache),
		"publisher":        "clever",
		"bindings_version": Version,
	})
	req.Header.Add("User-Agent", "Clever/GoBindings/"+Version)
	req.Header.Add("X-Clever-Client-User-Agent", string(customUA))
}

// RoundTrip makes a request and returns the response
func (bat BasicAuthTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	req.Header.Set("Authorization", fmt.Sprintf("Basic %s",
		base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s",
			bat.Username, bat.Password)))))
	setTrackingHeaders(req)
	return http.DefaultTransport.RoundTrip(req)
}

// Client returns a new Client object for the specified BasicAuthTransport
func (bat *BasicAuthTransport) Client() *http.Client {
	return &http.Client{Transport: bat}
}

// New returns a new Clever object to make requests with. URL must be a valid base url, e.g. "https://api.clever.com"
func New(client *http.Client, url string) *Clever {
	return &Clever{client, url}
}

// CleverError contains an error that occurred within the Clever API
type CleverError struct {
	Code    int
	Message string `json:"error"`
}

// Error returns a string representation of a CleverError
func (err *CleverError) Error() string {
	if err.Code == 0 {
		return err.Message
	}
	return fmt.Sprintf("%s (%d)", err.Message, err.Code)
}

// TooManyRequestsError indicates the number of requests has exceeded the rate limit
type TooManyRequestsError struct {
	Header http.Header
}

// TooManyRequestsError creates a TooManyRequestsError
func (err *TooManyRequestsError) Error() string {
	errString := "Too Many Requests"
	for bucketIndex, bucketName := range err.Header[http.CanonicalHeaderKey("X-Ratelimit-Bucket")] {
		errString += fmt.Sprintf("\nBucket: %s", bucketName)
		for _, prop := range []string{"Remaining", "Limit", "Reset"} {
			headersForProp := err.Header[http.CanonicalHeaderKey("X-Ratelimit-"+prop)]
			if bucketIndex < len(headersForProp) {
				errString += fmt.Sprintf(", %s: %s", prop, headersForProp[bucketIndex])
			}
		}
	}
	return errString
}

// Paging contains information for paging a response
type Paging struct {
	Count   int
	Current int
	Total   int
}

// Link represents a stable link for querying the API
type Link struct {
	Rel string
	URI string
}

// DistrictResp wraps the response given when the user queries for a District
type DistrictResp struct {
	District District `json:"data"`
	Links    []Link
	URI      string
}

// District corresponds to the District resource in the Clever data schema: clever.com/schema
type District struct {
	ID        string
	Name      string
	MdrNumber string `json:"mdr_number"`
}

// SchoolResp wraps the response given when the user queries for a School
type SchoolResp struct {
	Links  []Link
	School School `json:"data"`
	URI    string
}

// School corresponds to the School resource in the Clever data schema: clever.com/schema
type School struct {
	Created      string
	District     string
	HighGrade    string `json:"high_grade"`
	ID           string
	LastModified string `json:"last_modified"`
	Location     Location
	LowGrade     string `json:"low_grade"`
	Name         string
	NcesID       string `json:"nces_id"`
	Phone        string
	SchoolNumber string `json:"school_number"`
	SisID        string `json:"sis_id"`
	StateID      string `json:"state_id"`
	MdrNumber    string `json:"mdr_number"`
}

// TeacherResp wraps the response given when the user queries for a Teacher
type TeacherResp struct {
	Links   []Link
	Teacher Teacher `json:"data"`
	URI     string
}

// Teacher corresponds to the Teacher resource in the Clever data schema: clever.com/schema
type Teacher struct {
	Created       string
	District      string
	Email         string
	ID            string
	LastModified  string `json:"last_modified"`
	Name          Name
	School        string
	SisID         string `json:"sis_id"`
	TeacherNumber string `json:"teacher_number"`
	Title         string
}

// StudentResp wraps the response given when the user queries for a Student
type StudentResp struct {
	Links   []Link
	Student Student `json:"data"`
	URI     string
}

// Student corresponds to the Student resource in the Clever data schema: clever.com/schema
type Student struct {
	Created           string
	District          string
	Dob               string
	Email             string
	FrlStatus         string `json:"frl_status"`
	Gender            string
	Grade             string
	HispanicEthnicity string `json:"hispanic_ethnicity"`
	ID                string
	LastModified      string `json:"last_modified"`
	Location          Location
	Name              Name
	Race              string
	School            string
	SisID             string `json:"sis_id"`
	StateID           string `json:"state_id"`
	StudentNumber     string `json:"student_number"`
}

// SectionResp wraps the response given when the user queries for a Section
type SectionResp struct {
	Links   []Link
	Section Section `json:"data"`
	URI     string
}

// Section corresponds to the Section resource in the Clever data schema: clever.com/schema
type Section struct {
	CourseName   string `json:"course_name"`
	CourseNumber string `json:"course_number"`
	Created      string
	District     string
	Grade        string
	ID           string
	LastModified string `json:"last_modified"`
	Name         string
	School       string
	SisID        string `json:"sis_id"`
	Students     []string
	Subject      string
	Teacher      string
	Term
}

// EventResp represents an HTTP response returning data on one Event.
type EventResp struct {
	Links []Link
	Event Event `json:"data"`
	URI   string
}

// Event represents a data change on an underlying collection.
// Example types include students.created and teachers.deleted.
type Event struct {
	Type    string
	Created string
	ID      string
	Data    struct {
		Object map[string]interface{}
	}
}

// Location represents a complete address for use with the Student and School resources
type Location struct {
	Address string
	City    string
	State   string
	Zip     string
}

// Name represents the full name of a Student or Teacher resource
type Name struct {
	First  string
	Middle string
	Last   string
}

// Term holds information about the duration of a school term
type Term struct {
	Name      string
	StartDate string `json:"start_date"`
	EndDate   string `json:"end_date"`
}

// Query makes a GET request to Clever using the Request function (see below)
func (clever *Clever) Query(path string, params url.Values, resp interface{}) error {
	return clever.Request("GET", path, params, nil, resp)
}

// Request makes a request to Clever given a Clever object, http method,
// endpoint path, parameters (pass in nil for no parameters), and a body .
func (clever *Clever) Request(method string, path string, params url.Values, body interface{}, resp interface{}) error {

	// Create request URI from Clever base, path, params
	uri := fmt.Sprintf("%s%s", clever.url, path)
	if params != nil {
		uri = fmt.Sprintf("%s%s?%s", clever.url, path, params.Encode())
	}

	var bodyReader io.Reader
	if body != nil {
		rawBody, err := json.Marshal(body)
		if err != nil {
			return err
		}
		bodyReader = bytes.NewReader(rawBody)
	}

	// Ensure authentication is provided
	req, _ := http.NewRequest(method, uri, bodyReader)
	req.Header.Add("Content-Type", "application/json")
	setTrackingHeaders(req)
	if debug {
		dump, _ := httputil.DumpRequestOut(req, true)
		log.Printf("request:\n")
		log.Printf("%v\n", string(dump))
	}
	r, err := clever.client.Do(req)
	if err != nil {
		return err
	}
	defer r.Body.Close()
	if debug {
		dump, _ := httputil.DumpResponse(r, true)
		log.Printf("response:\n")
		log.Printf("%v\n", string(dump))
	}
	if r.StatusCode == 429 {
		return &TooManyRequestsError{r.Header}
	} else if r.StatusCode != 200 {
		var error CleverError
		if err := json.NewDecoder(r.Body).Decode(&error); err != nil {
			return err
		}
		return &error
	}
	err = json.NewDecoder(r.Body).Decode(resp)
	return err
}

// PagedResult wraps a response. It allows for paged reading of a response in conjunction with QueryAll() and Next()
type PagedResult struct {
	clever         Clever
	nextPagePath   string
	lastData       []map[string]interface{}
	lastDataCursor int
	lastError      error
}

// QueryAll returns a PagedResult to allow for paged reading of large responses from the Clever API
func (clever *Clever) QueryAll(path string, params url.Values) PagedResult {
	paramString := ""
	if params != nil {
		paramString = "?" + params.Encode()
	}

	return PagedResult{clever: *clever, nextPagePath: path + paramString, lastDataCursor: -1}
}

// Next returns true if a PagedResult contains additional data and false if the
// cursor has reached the end of the available data for this response.
func (r *PagedResult) Next() bool {
	if r.lastDataCursor != -1 && r.lastDataCursor < len(r.lastData)-1 {
		r.lastDataCursor++
		return true
	}

	if r.nextPagePath == "" {
		return false
	}

	resp := &struct {
		Data   []map[string]interface{}
		Links  []Link
		Paging Paging
	}{}
	r.lastError = r.clever.Query(r.nextPagePath, nil, resp)
	if r.lastError != nil {
		return false
	}
	r.lastData = resp.Data
	r.lastDataCursor = 0
	r.nextPagePath = ""
	for _, link := range resp.Links {
		if link.Rel == "next" {
			r.nextPagePath = link.URI
			break
		}
	}
	return len(r.lastData) > 0
}

// Scan parses the next page of results in a PagedResult r into result. Scan throws an error if r is invalid JSON.
func (r *PagedResult) Scan(result interface{}) error {
	data, err := json.Marshal(r.lastData[r.lastDataCursor]["data"])
	if err != nil {
		return err
	}
	return json.Unmarshal(data, result)
}

// Error returns the error in a response, if there is one
func (r *PagedResult) Error() error {
	return r.lastError
}
<commit_msg>Add additional models and fields<commit_after>package clever

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os/exec"
	"runtime"
)

const debug = false

var unameCache []byte

// Clever wraps the Clever API at the specified URL e.g. "https://api.clever.com"
type Clever struct {
	client *http.Client
	url    string
}

// BasicAuthTransport is an http.Transport that performs HTTP Basic Auth.
type BasicAuthTransport struct {
	Username string
	Password string
}

func setTrackingHeaders(req *http.Request) {
	// Want to swallow errors here because desired behavior is nil --> blank string if value doesn't exist
	if unameCache == nil {
		unameCache, _ = exec.Command("uname", "-a").Output()
	}
	customUA, _ := json.Marshal(map[string]string{
		"lang":             "go",
		"lang_version":     runtime.Version(),
		"platform":         runtime.GOOS,
		"uname":            fmt.Sprintf("%s", unameCache),
		"publisher":        "clever",
		"bindings_version": Version,
	})
	req.Header.Add("User-Agent", "Clever/GoBindings/"+Version)
	req.Header.Add("X-Clever-Client-User-Agent", string(customUA))
}

// RoundTrip makes a request and returns the response
func (bat BasicAuthTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	req.Header.Set("Authorization", fmt.Sprintf("Basic %s",
		base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s",
			bat.Username, bat.Password)))))
	setTrackingHeaders(req)
	return http.DefaultTransport.RoundTrip(req)
}

// Client returns a new Client object for the specified BasicAuthTransport
func (bat *BasicAuthTransport) Client() *http.Client {
	return &http.Client{Transport: bat}
}

// New returns a new Clever object to make requests with. URL must be a valid base url, e.g. "https://api.clever.com"
func New(client *http.Client, url string) *Clever {
	return &Clever{client, url}
}

// CleverError contains an error that occurred within the Clever API
type CleverError struct {
	Code    int
	Message string `json:"error"`
}

// Error returns a string representation of a CleverError
func (err *CleverError) Error() string {
	if err.Code == 0 {
		return err.Message
	}
	return fmt.Sprintf("%s (%d)", err.Message, err.Code)
}

// TooManyRequestsError indicates the number of requests has exceeded the rate limit
type TooManyRequestsError struct {
	Header http.Header
}

// TooManyRequestsError creates a TooManyRequestsError
func (err *TooManyRequestsError) Error() string {
	errString := "Too Many Requests"
	for bucketIndex, bucketName := range err.Header[http.CanonicalHeaderKey("X-Ratelimit-Bucket")] {
		errString += fmt.Sprintf("\nBucket: %s", bucketName)
		for _, prop := range []string{"Remaining", "Limit", "Reset"} {
			headersForProp := err.Header[http.CanonicalHeaderKey("X-Ratelimit-"+prop)]
			if bucketIndex < len(headersForProp) {
				errString += fmt.Sprintf(", %s: %s", prop, headersForProp[bucketIndex])
			}
		}
	}
	return errString
}

// Paging contains information for paging a response
type Paging struct {
	Count   int
	Current int
	Total   int
}

// Link represents a stable link for querying the API
type Link struct {
	Rel string
	URI string
}

// DistrictResp wraps the response given when the user queries for a District
type DistrictResp struct {
	District District `json:"data"`
	Links    []Link
	URI      string
}

// District corresponds to the District resource in the Clever data schema: clever.com/schema
type District struct {
	ID        string
	Name      string
	MdrNumber string `json:"mdr_number"`
}

// SchoolResp wraps the response given when the user queries for a School
type SchoolResp struct {
	Links  []Link
	School School `json:"data"`
	URI    string
}

// School corresponds to the School resource in the Clever data schema: clever.com/schema
type School struct {
	Created      string
	District     string
	HighGrade    string `json:"high_grade"`
	ID           string
	LastModified string `json:"last_modified"`
	Location     Location
	LowGrade     string `json:"low_grade"`
	Name         string
	NcesID       string `json:"nces_id"`
	Phone        string
	SchoolNumber string `json:"school_number"`
	SisID        string `json:"sis_id"`
	StateID      string `json:"state_id"`
	MdrNumber    string `json:"mdr_number"`
	Principal    Principal
}

// Principal represents a principal for a school
type Principal struct {
	Name  string
	Email string
}

// TeacherResp wraps the response given when the user queries for a Teacher
type TeacherResp struct {
	Links   []Link
	Teacher Teacher `json:"data"`
	URI     string
}

// Teacher corresponds to the Teacher resource in the Clever data schema: clever.com/schema
type Teacher struct {
	Created       string
	District      string
	Email         string
	ID            string
	LastModified  string `json:"last_modified"`
	Name          Name
	School        string
	SisID         string `json:"sis_id"`
	TeacherNumber string `json:"teacher_number"`
	Title         string
	StateID       string `json:"state_id"`
	Credentials   Credentials
}

// Credentials corresponds to credentials for a Student or Teacher
type Credentials struct {
	DistrictUsername string `json:"district_username"`
}

// StudentResp wraps the response given when the user queries for a Student
type StudentResp struct {
	Links   []Link
	Student Student `json:"data"`
	URI     string
}

// Student corresponds to the Student resource in the Clever data schema: clever.com/schema
type Student struct {
	Created           string
	District          string
	Dob               string
	Email             string
	FrlStatus         string `json:"frl_status"`
	Gender            string
	Grade             string
	HispanicEthnicity string `json:"hispanic_ethnicity"`
	ID                string
	LastModified      string `json:"last_modified"`
	Location          Location
	Name              Name
	Race              string
	School            string
	SisID             string `json:"sis_id"`
	StateID           string `json:"state_id"`
	StudentNumber     string `json:"student_number"`
	EllStatus         string `json:"ell_status"`
	Credentials       Credentials
}

// SectionResp wraps the response given when the user queries for a Section
type SectionResp struct {
	Links   []Link
	Section Section `json:"data"`
	URI     string
}

// Section corresponds to the Section resource in the Clever data schema: clever.com/schema
type Section struct {
	CourseName        string `json:"course_name"`
	CourseNumber      string `json:"course_number"`
	Created           string
	District          string
	Grade             string
	ID                string
	LastModified      string `json:"last_modified"`
	Name              string
	School            string
	SisID             string `json:"sis_id"`
	Students          []string
	Subject           string
	Teacher           string
	Term              Term
	Teachers          []string
	CourseDescription string `json:"course_description"`
	Period            string
	SectionNumber     string `json:"section_number"`
}

// EventResp represents an HTTP response returning data on one Event.
type EventResp struct {
	Links []Link
	Event Event `json:"data"`
	URI   string
}

// Event represents a data change on an underlying collection.
// Example types include students.created and teachers.deleted.
type Event struct {
	Type    string
	Created string
	ID      string
	Data    struct {
		Object map[string]interface{}
	}
}

// Location represents a complete address for use with the Student and School resources
type Location struct {
	Address string
	City    string
	State   string
	Zip     string
	Lon     string
	Lat     string
}

// Name represents the full name of a Student or Teacher resource
type Name struct {
	First  string
	Middle string
	Last   string
}

// Term holds information about the duration of a school term
type Term struct {
	Name      string
	StartDate string `json:"start_date"`
	EndDate   string `json:"end_date"`
}

// SchoolAdminResp wraps the response given when the user queries for a SchoolAdmin
type SchoolAdminResp struct {
	Links       []Link
	SchoolAdmin SchoolAdmin `json:"data"`
}

// SchoolAdmin corresponds to the SchoolAdmin resource in the Clever data schema: clever.com/schema
type SchoolAdmin struct {
	ID       string
	District string
	Name     Name
	Title    string
	Email    string
	Schools  []string
	StaffID  string `json:"staff_id"`
}

// DistrictAdminResp wraps the response given when the user queries for a DistrictAdmin
type DistrictAdminResp struct {
	Links         []Link
	DistrictAdmin DistrictAdmin `json:"data"`
}

// DistrictAdmin corresponds to the DistrictAdmin resource in the Clever data schema: clever.com/schema
type DistrictAdmin struct {
	ID       string
	District string
	Name     Name
	Title    string
	Email    string
}

// ContactResp wraps the response given when the user queries for a Contact
type ContactResp struct {
	Links   []Link
	Contact Contact `json:"data"`
}

// Contact corresponds to the Contact resource in the Clever data schema: clever.com/schema
type Contact struct {
	ID           string
	Student      string
	Type         string
	Name         string
	Relationship string
	Phone        string
	PhoneType    string `json:"phone_type"`
	Email        string
	District     string
}

// Query makes a GET request to Clever using the Request function (see below)
func (clever *Clever) Query(path string, params url.Values, resp interface{}) error {
	return clever.Request("GET", path, params, nil, resp)
}

// Request makes a request to Clever given a Clever object, http method,
// endpoint path, parameters (pass in nil for no parameters), and a body .
func (clever *Clever) Request(method string, path string, params url.Values, body interface{}, resp interface{}) error {

	// Create request URI from Clever base, path, params
	uri := fmt.Sprintf("%s%s", clever.url, path)
	if params != nil {
		uri = fmt.Sprintf("%s%s?%s", clever.url, path, params.Encode())
	}

	var bodyReader io.Reader
	if body != nil {
		rawBody, err := json.Marshal(body)
		if err != nil {
			return err
		}
		bodyReader = bytes.NewReader(rawBody)
	}

	// Ensure authentication is provided
	req, _ := http.NewRequest(method, uri, bodyReader)
	req.Header.Add("Content-Type", "application/json")
	setTrackingHeaders(req)
	if debug {
		dump, _ := httputil.DumpRequestOut(req, true)
		log.Printf("request:\n")
		log.Printf("%v\n", string(dump))
	}
	r, err := clever.client.Do(req)
	if err != nil {
		return err
	}
	defer r.Body.Close()
	if debug {
		dump, _ := httputil.DumpResponse(r, true)
		log.Printf("response:\n")
		log.Printf("%v\n", string(dump))
	}
	if r.StatusCode == 429 {
		return &TooManyRequestsError{r.Header}
	} else if r.StatusCode != 200 {
		var error CleverError
		if err := json.NewDecoder(r.Body).Decode(&error); err != nil {
			return err
		}
		return &error
	}
	err = json.NewDecoder(r.Body).Decode(resp)
	return err
}

// PagedResult wraps a response. It allows for paged reading of a response in conjunction with QueryAll() and Next()
type PagedResult struct {
	clever         Clever
	nextPagePath   string
	lastData       []map[string]interface{}
	lastDataCursor int
	lastError      error
}

// QueryAll returns a PagedResult to allow for paged reading of large responses from the Clever API
func (clever *Clever) QueryAll(path string, params url.Values) PagedResult {
	paramString := ""
	if params != nil {
		paramString = "?" + params.Encode()
	}

	return PagedResult{clever: *clever, nextPagePath: path + paramString, lastDataCursor: -1}
}

// Next returns true if a PagedResult contains additional data and false if the
// cursor has reached the end of the available data for this response.
func (r *PagedResult) Next() bool {
	if r.lastDataCursor != -1 && r.lastDataCursor < len(r.lastData)-1 {
		r.lastDataCursor++
		return true
	}

	if r.nextPagePath == "" {
		return false
	}

	resp := &struct {
		Data   []map[string]interface{}
		Links  []Link
		Paging Paging
	}{}
	r.lastError = r.clever.Query(r.nextPagePath, nil, resp)
	if r.lastError != nil {
		return false
	}
	r.lastData = resp.Data
	r.lastDataCursor = 0
	r.nextPagePath = ""
	for _, link := range resp.Links {
		if link.Rel == "next" {
			r.nextPagePath = link.URI
			break
		}
	}
	return len(r.lastData) > 0
}

// Scan parses the next page of results in a PagedResult r into result. Scan throws an error if r is invalid JSON.
func (r *PagedResult) Scan(result interface{}) error {
	data, err := json.Marshal(r.lastData[r.lastDataCursor]["data"])
	if err != nil {
		return err
	}
	return json.Unmarshal(data, result)
}

// Error returns the error in a response, if there is one
func (r *PagedResult) Error() error {
	return r.lastError
}
<|endoftext|>