<commit_before>package sq

import (
	"encoding/binary"
	"fmt"
	"os"
	"path"
	"sync"
	"syscall"
	"unsafe"
)

var (
	encoder           = binary.LittleEndian
	blank             = struct{}{}
	ChannelNameLenErr = fmt.Errorf("channel name cannot exceed %d characters", MAX_CHANNEL_NAME_SIZE)
	ChannelExistsErr  = fmt.Errorf("channel already exists")
	ChannelCreateErr  = fmt.Errorf("channel count not be created")
	pageSize          = os.Getpagesize()
)

type addChannelWork struct {
	err     error
	channel *Channel
	config  *ChannelConfiguration
	c       chan *addChannelWork
}

type Topic struct {
	path         string
	state        *State
	states       *States
	channelsLock sync.RWMutex
	channels     map[string]*Channel
	segment      *Segment
	segmentsLock sync.RWMutex
	segments     map[uint64]*Segment
	addChannel   chan *addChannelWork
	messageAdded chan struct{}
	segmentDone  chan uint64
	dataLock     sync.RWMutex
}

func OpenTopic(name string, config *TopicConfiguration) (*Topic, error) {
	t := &Topic{
		path:         path.Join(config.path, name),
		channels:     make(map[string]*Channel),
		segments:     make(map[uint64]*Segment),
		addChannel:   make(chan *addChannelWork),
		segmentDone:  make(chan uint64, 2),
		messageAdded: make(chan struct{}, 64),
	}
	err := loadStates(t)
	if err != nil {
		return nil, err
	}

	if id := t.state.segmentId; id == 0 {
		if err := t.expand(); err != nil {
			return nil, err
		}
		if err := t.states.syncTopic(); err != nil {
			return nil, err
		}
	} else {
		t.findWritePosition(openSegment(t, id, false))
		t.segments[id] = t.segment
	}
	go t.worker()
	return t, nil
}

func (t *Topic) Write(data []byte) error {
	length := len(data)

	t.dataLock.Lock()
	start := int(t.state.offset)
	dataStart := start + 4
	dataEnd := dataStart + length

	// do we have enough space in the current segment?
	if dataEnd > MAX_SEGMENT_SIZE {
		if err := t.expand(); err != nil {
			return err
		}
		start = int(SEGMENT_HEADER_SIZE)
		dataStart = start + 4
		dataEnd = dataStart + length
	}

	// write the message length
	encoder.PutUint32(t.segment.data[start:], uint32(length))
	// write the message
	copy(t.segment.data[dataStart:], data)
	// location of the next write
	t.state.offset = uint32(dataEnd)
	t.dataLock.Unlock()

	// sync the part of the data file we just wrote
	from := start / pageSize * pageSize
	to := dataStart + length - from
	_, _, errno := syscall.Syscall(syscall.SYS_MSYNC, uintptr(unsafe.Pointer(&t.segment.data[from])), uintptr(to), syscall.MS_SYNC)
	if errno != 0 {
		return syscall.Errno(errno)
	}

	// notify channels that a new message is waiting
	t.messageAdded <- blank

	return nil
}

func (t *Topic) Channel(name string, config *ChannelConfiguration) (*Channel, error) {
	if len(name) > MAX_CHANNEL_NAME_SIZE {
		return nil, ChannelNameLenErr
	}
	if config == nil {
		config = ConfigureChannel()
	}
	config.name = name
	res := &addChannelWork{
		c:      make(chan *addChannelWork),
		config: config,
	}
	t.addChannel <- res
	<-res.c
	return res.channel, res.err
}

func (t *Topic) expand() error {
	segment := newSegment(t)
	t.segmentsLock.Lock()
	t.segments[segment.id] = segment
	t.segmentsLock.Unlock()
	if t.segment != nil {
		// create a pointer to the next segment id
		t.segment.nextId = segment.id
		t.segment.size = t.state.offset
		if err := t.segment.syncHeader(); err != nil {
			return err
		}
		t.channelsLock.RLock()
		noChannels := len(t.states.channels) == 0
		t.channelsLock.RUnlock()
		if noChannels {
			t.deleteSegment(t.segment.id)
		}
	}
	t.segment = segment
	t.state.offset = SEGMENT_HEADER_SIZE
	t.state.segmentId = segment.id
	return nil
}

func (t *Topic) worker() {
	for {
		select {
		case work := <-t.addChannel:
			work.channel, work.err = t.createChannel(work.config)
			work.c <- work
		case <-t.messageAdded:
			t.channelsLock.RLock()
			for _, c := range t.channels {
				c.notify()
			}
			t.channelsLock.RUnlock()
		case id := <-t.segmentDone:
			t.dataLock.RLock()
			// shortcircuit if this is the topic's segment
			if id == t.segment.id {
				t.dataLock.RUnlock()
				break
			}
			usable := t.isSegmentUsable(id)
			t.dataLock.RUnlock()
			if usable == false {
				t.deleteSegment(id)
			}
		}
	}
}

func (t *Topic) createChannel(config *ChannelConfiguration) (*Channel, error) {
	t.channelsLock.Lock()
	_, exists := t.channels[config.name]
	if exists {
		t.channelsLock.Unlock()
		if config.temp {
			// try again until we've created one
			return t.createChannel(config)
		}
		return nil, ChannelExistsErr
	}
	defer t.channelsLock.Unlock()

	c := newChannel(t, config)
	if config.temp {
		c.state = new(State)
	} else {
		c.state = t.states.getOrCreate(c.name)
		if c.state == nil {
			return nil, ChannelCreateErr
		}
	}

	// if we have a temp channel, or a new channel, set the position to the
	// writer's current position
	if config.temp || c.state.segmentId == 0 {
		t.dataLock.RLock()
		c.state.offset = t.state.offset
		c.state.segmentId = t.state.segmentId
		t.dataLock.RUnlock()
	}
	t.channels[c.name] = c
	return c, nil
}

func (t *Topic) read(channel *Channel) []byte {
	state := channel.state

	// no need to lock the channel here since the state of the channel is only
	// ever changed after this point (either to move to the next segment or message)
	t.dataLock.RLock()
	isCurrentSegment := state.segmentId == t.state.segmentId
	isCurrentOffset := state.offset == t.state.offset
	t.dataLock.RUnlock()

	// are we fully caught up with the topic?
	if isCurrentSegment && isCurrentOffset {
		return nil
	}

	segment := t.loadSegment(state.segmentId)
	// If we aren't on the current segment, we might be at the end of an old
	// segment. We can safely read old segments without locking since the only
	// writer (the topic), is done with it
	if !isCurrentSegment {
		if state.offset >= segment.size {
			previousId := state.segmentId
			segment = t.loadSegment(segment.nextId)
			channel.changeSegment(segment)
			t.segmentDone <- previousId
		}
	}

	l := encoder.Uint32(segment.data[state.offset:])
	start := state.offset + 4
	end := start + uint32(l)
	return segment.data[start:end]
}

func (t *Topic) loadSegment(id uint64) *Segment {
	t.segmentsLock.RLock()
	segment := t.segments[id]
	t.segmentsLock.RUnlock()
	if segment != nil {
		return segment
	}

	t.segmentsLock.Lock()
	defer t.segmentsLock.Unlock()
	if segment := t.segments[id]; segment != nil {
		return segment
	}
	segment = openSegment(t, id, false)
	t.segments[id] = segment
	return segment
}

// A segment is usable if any channel references it or an earlier one
// How we check this depends on whether or not the channel is active
// (currently consuming message). For an active channel, we ask the channel,
// for an inactive channel, we can query the state direclty knowing that
// no one can be writing to it at the same time
func (t *Topic) isSegmentUsable(id uint64) bool {
	t.channelsLock.RLock()
	defer t.channelsLock.RUnlock()
	for name, state := range t.states.channels {
		if channel, active := t.channels[name]; active {
			if channel.isSegmentUsable(id) == true {
				return true
			}
		} else if state.isSegmentUsable(id) == true {
			return true
		}
	}
	return false
}

// The topic's persisted state could be behind from the actual state. However,
// we can determine the actual state based on the data which is guaranteed to be
// up to date (the next reference id and the data itself)
func (t *Topic) findWritePosition(segment *Segment) {
	for segment.nextId != 0 {
		segment = openSegment(t, segment.nextId, false)
	}
	t.segment = segment
	t.state.segmentId = segment.id

	offset := SEGMENT_HEADER_SIZE
	for offset < MAX_SEGMENT_SIZE {
		l := encoder.Uint32(segment.data[offset:])
		if l == 0 {
			break
		}
		offset += 4 + l
	}
	t.state.offset = offset
}

func (t *Topic) deleteSegment(id uint64) {
	t.segmentsLock.Lock()
	segment := t.segments[id]
	delete(t.segments, id)
	t.segmentsLock.Unlock()
	if segment != nil {
		segment.delete()
	}
}
<commit_msg>Since segment deletion caused by expansion now has its own optimized path, there's no reason to have the worker delete segments. Furthermore, since the only time a channel moves past a segment is when there's a newer segment, we don't even have to check if we're deleting the topic's segment. By the simple fact that there's at least 1 more segment, we know that the topic has moved on. This removes the readlock on the critical dataLock when checking to see if a segment can be deleted.<commit_after>package sq

import (
	"encoding/binary"
	"fmt"
	"os"
	"path"
	"sync"
	"syscall"
	"unsafe"
)

var (
	encoder           = binary.LittleEndian
	blank             = struct{}{}
	ChannelNameLenErr = fmt.Errorf("channel name cannot exceed %d characters", MAX_CHANNEL_NAME_SIZE)
	ChannelExistsErr  = fmt.Errorf("channel already exists")
	ChannelCreateErr  = fmt.Errorf("channel count not be created")
	pageSize          = os.Getpagesize()
)

type addChannelWork struct {
	err     error
	channel *Channel
	config  *ChannelConfiguration
	c       chan *addChannelWork
}

type Topic struct {
	path         string
	state        *State
	states       *States
	channelsLock sync.RWMutex
	channels     map[string]*Channel
	segment      *Segment
	segmentsLock sync.RWMutex
	segments     map[uint64]*Segment
	addChannel   chan *addChannelWork
	messageAdded chan struct{}
	dataLock     sync.RWMutex
}

func OpenTopic(name string, config *TopicConfiguration) (*Topic, error) {
	t := &Topic{
		path:         path.Join(config.path, name),
		channels:     make(map[string]*Channel),
		segments:     make(map[uint64]*Segment),
		addChannel:   make(chan *addChannelWork),
		messageAdded: make(chan struct{}, 64),
	}
	err := loadStates(t)
	if err != nil {
		return nil, err
	}

	if id := t.state.segmentId; id == 0 {
		if err := t.expand(); err != nil {
			return nil, err
		}
		if err := t.states.syncTopic(); err != nil {
			return nil, err
		}
	} else {
		t.findWritePosition(openSegment(t, id, false))
		t.segments[id] = t.segment
	}
	go t.worker()
	return t, nil
}

func (t *Topic) Write(data []byte) error {
	length := len(data)

	t.dataLock.Lock()
	start := int(t.state.offset)
	dataStart := start + 4
	dataEnd := dataStart + length

	// do we have enough space in the current segment?
	if dataEnd > MAX_SEGMENT_SIZE {
		if err := t.expand(); err != nil {
			return err
		}
		start = int(SEGMENT_HEADER_SIZE)
		dataStart = start + 4
		dataEnd = dataStart + length
	}

	// write the message length
	encoder.PutUint32(t.segment.data[start:], uint32(length))
	// write the message
	copy(t.segment.data[dataStart:], data)
	// location of the next write
	t.state.offset = uint32(dataEnd)
	t.dataLock.Unlock()

	// sync the part of the data file we just wrote
	from := start / pageSize * pageSize
	to := dataStart + length - from
	_, _, errno := syscall.Syscall(syscall.SYS_MSYNC, uintptr(unsafe.Pointer(&t.segment.data[from])), uintptr(to), syscall.MS_SYNC)
	if errno != 0 {
		return syscall.Errno(errno)
	}

	// notify channels that a new message is waiting
	t.messageAdded <- blank

	return nil
}

func (t *Topic) Channel(name string, config *ChannelConfiguration) (*Channel, error) {
	if len(name) > MAX_CHANNEL_NAME_SIZE {
		return nil, ChannelNameLenErr
	}
	if config == nil {
		config = ConfigureChannel()
	}
	config.name = name
	res := &addChannelWork{
		c:      make(chan *addChannelWork),
		config: config,
	}
	t.addChannel <- res
	<-res.c
	return res.channel, res.err
}

func (t *Topic) expand() error {
	segment := newSegment(t)
	t.segmentsLock.Lock()
	t.segments[segment.id] = segment
	t.segmentsLock.Unlock()
	if t.segment != nil {
		// create a pointer to the next segment id
		t.segment.nextId = segment.id
		t.segment.size = t.state.offset
		if err := t.segment.syncHeader(); err != nil {
			return err
		}
		t.channelsLock.RLock()
		noChannels := len(t.states.channels) == 0
		t.channelsLock.RUnlock()
		if noChannels {
			t.deleteSegment(t.segment.id)
		}
	}
	t.segment = segment
	t.state.offset = SEGMENT_HEADER_SIZE
	t.state.segmentId = segment.id
	return nil
}

func (t *Topic) worker() {
	for {
		select {
		case work := <-t.addChannel:
			work.channel, work.err = t.createChannel(work.config)
			work.c <- work
		case <-t.messageAdded:
			t.channelsLock.RLock()
			for _, c := range t.channels {
				c.notify()
			}
			t.channelsLock.RUnlock()
		}
	}
}

func (t *Topic) createChannel(config *ChannelConfiguration) (*Channel, error) {
	t.channelsLock.Lock()
	_, exists := t.channels[config.name]
	if exists {
		t.channelsLock.Unlock()
		if config.temp {
			// try again until we've created one
			return t.createChannel(config)
		}
		return nil, ChannelExistsErr
	}
	defer t.channelsLock.Unlock()

	c := newChannel(t, config)
	if config.temp {
		c.state = new(State)
	} else {
		c.state = t.states.getOrCreate(c.name)
		if c.state == nil {
			return nil, ChannelCreateErr
		}
	}

	// if we have a temp channel, or a new channel, set the position to the
	// writer's current position
	if config.temp || c.state.segmentId == 0 {
		t.dataLock.RLock()
		c.state.offset = t.state.offset
		c.state.segmentId = t.state.segmentId
		t.dataLock.RUnlock()
	}
	t.channels[c.name] = c
	return c, nil
}

func (t *Topic) read(channel *Channel) []byte {
	state := channel.state

	// no need to lock the channel here since the state of the channel is only
	// ever changed after this point (either to move to the next segment or message)
	t.dataLock.RLock()
	isCurrentSegment := state.segmentId == t.state.segmentId
	isCurrentOffset := state.offset == t.state.offset
	t.dataLock.RUnlock()

	// are we fully caught up with the topic?
	if isCurrentSegment && isCurrentOffset {
		return nil
	}

	segment := t.loadSegment(state.segmentId)
	// If we aren't on the current segment, we might be at the end of an old
	// segment. We can safely read old segments without locking since the only
	// writer (the topic), is done with it
	if !isCurrentSegment {
		if state.offset >= segment.size {
			previousId := state.segmentId
			segment = t.loadSegment(segment.nextId)
			channel.changeSegment(segment)
			if t.isSegmentUsable(previousId) == false {
				t.deleteSegment(previousId)
			}
		}
	}

	l := encoder.Uint32(segment.data[state.offset:])
	start := state.offset + 4
	end := start + uint32(l)
	return segment.data[start:end]
}

func (t *Topic) loadSegment(id uint64) *Segment {
	t.segmentsLock.RLock()
	segment := t.segments[id]
	t.segmentsLock.RUnlock()
	if segment != nil {
		return segment
	}

	t.segmentsLock.Lock()
	defer t.segmentsLock.Unlock()
	if segment := t.segments[id]; segment != nil {
		return segment
	}
	segment = openSegment(t, id, false)
	t.segments[id] = segment
	return segment
}

// A segment is usable if any channel references it or an earlier one
// How we check this depends on whether or not the channel is active
// (currently consuming message). For an active channel, we ask the channel,
// for an inactive channel, we can query the state direclty knowing that
// no one can be writing to it at the same time
func (t *Topic) isSegmentUsable(id uint64) bool {
	t.channelsLock.RLock()
	defer t.channelsLock.RUnlock()
	for name, state := range t.states.channels {
		if channel, active := t.channels[name]; active {
			if channel.isSegmentUsable(id) == true {
				return true
			}
		} else if state.isSegmentUsable(id) == true {
			return true
		}
	}
	return false
}

// The topic's persisted state could be behind from the actual state. However,
// we can determine the actual state based on the data which is guaranteed to be
// up to date (the next reference id and the data itself)
func (t *Topic) findWritePosition(segment *Segment) {
	for segment.nextId != 0 {
		segment = openSegment(t, segment.nextId, false)
	}
	t.segment = segment
	t.state.segmentId = segment.id

	offset := SEGMENT_HEADER_SIZE
	for offset < MAX_SEGMENT_SIZE {
		l := encoder.Uint32(segment.data[offset:])
		if l == 0 {
			break
		}
		offset += 4 + l
	}
	t.state.offset = offset
}

func (t *Topic) deleteSegment(id uint64) {
	t.segmentsLock.Lock()
	segment := t.segments[id]
	delete(t.segments, id)
	t.segmentsLock.Unlock()
	if segment != nil {
		segment.delete()
	}
}
<|endoftext|>


<commit_before>package crconfig

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import (
	"time"
)

// CRConfig is JSON-serializable as the CRConfig used by Traffic Control. However, it also contains diff timestamps, for the last update time of each field. These can be used to return only fields which have changed since a given time.
type CRConfig struct {
	Config               Config                       `json:"config,omitempty"`
	ConfigTime           time.Time                    `json:"-"`
	ContentServers       map[string]Server            `json:"contentServers,omitempty"`
	ContentServersTime   map[string]time.Time         `json:"-"`
	ContentRouters       map[string]Router            `json:"contentRouters,omitempty"`
	ContentRoutersTime   map[string]time.Time         `json:"-"`
	DeliveryServices     map[string]DeliveryService   `json:"deliveryServices,omitempty"`
	DeliveryServicesTime map[string]time.Time         `json:"-"`
	EdgeLocations        map[string]LatitudeLongitude `json:"edgeLocations,omitempty"`
	EdgeLocationsTime    map[string]time.Time         `json:"-"`
	Monitors             map[string]Monitor           `json:"monitors,omitempty"`
	MonitorsTime         map[string]time.Time         `json:"-"`
	Stats                Stats                        `json:"stats,omitempty"`
	StatsTime            time.Time                    `json:"-"`
}

type MatchSet struct {
	Protocol  string      `json:"protocol"`
	MatchList []MatchType `json:"matchlist"`
}

type MatchType struct {
	MatchType string `json:"match-type"`
	Regex     string `json:"regex"`
}

type Config struct {
	APICacheControlMaxAge                          *int      `json:"api.cache-control.max_age,string,omitempty"`
	APICacheControlMaxAgeLastTime                  time.Time `json:"-"`
	ConsistentDNSRouting                           *bool     `json:"consistent.dns.routing,string,omitempty"`
	ConsistentDNSRoutingTime                       time.Time `json:"-"`
	CoverageZonePollingIntervalSeconds             *int      `json:"coveragezone.polling.interval,string,omitempty"`
	CoverageZonePollingIntervalSecondsTime         time.Time `json:"-"`
	CoverageZonePollingURL                         *string   `json:"coveragezone.polling.url,omitempty"`
	CoverageZonePollingURLTime                     time.Time `json:"-"`
	DNSSecDynamicResponseExpiration                *string   `json:"dnssec.dynamic.response.expiration,omitempty"`
	DNSSecDynamicResponseExpirationTime            time.Time `json:"-"`
	DNSSecEnabled                                  *bool     `json:"dnssec.enabled,string,omitempty"`
	DNSSecEnabledTime                              time.Time `json:"-"`
	DomainName                                     *string   `json:"domain_name,omitempty"`
	DomainNameTime                                 time.Time `json:"-"`
	FederationMappingPollingIntervalSeconds        *int      `json:"federationmapping.polling.interval,string,omitempty"`
	FederationMappingPollingIntervalSecondsTime    time.Time `json:"-"`
	FederationMappingPollingURL                    *string   `json:"federationmapping.polling.url"`
	FederationMappingPollingURLTime                time.Time `json:"-"`
	GeoLocationPollingInterval                     *int      `json:"geolocation.polling.interval,string,omitempty"`
	GeoLocationPollingIntervalTime                 time.Time `json:"-"`
	GeoLocationPollingURL                          *string   `json:"geolocation.polling.url,omitempty"`
	GeoLocationPollingURLTime                      time.Time `json:"-"`
	KeyStoreMaintenanceIntervalSeconds             *int      `json:"keystore.maintenance.interval,string,omitempty"`
	KeyStoreMaintenanceIntervalSecondsTime         time.Time `json:"-"`
	NeustarPollingIntervalSeconds                  *int      `json:"neustar.polling.interval,string,omitempty"`
	NeustarPollingIntervalSecondsTime              time.Time `json:"-"`
	NeustarPollingURL                              *string   `json:"neustar.polling.url,omitempty"`
	NeustarPollingURLTime                          time.Time `json:"-"`
	SOA                                            *SOA      `json:"soa,omitempty"`
	SOATime                                        time.Time `json:"-"`
	DNSSecInceptionSeconds                         *int      `json:"dnssec.inception,string,omitempty"`
	DNSSecInceptionSecondsTime                     time.Time `json:"-"`
	Ttls                                           *TTL      `json:"ttls,omitempty"`
	TtlsTime                                       time.Time `json:"-"`
	Weight                                         *float64  `json:"weight,string,omitempty"`
	WeightTime                                     time.Time `json:"-"`
	ZoneManagerCacheMaintenanceIntervalSeconds     *int      `json:"zonemanager.cache.maintenance.interval,string,omitempty"`
	ZoneManagerCacheMaintenanceIntervalSecondsTime time.Time `json:"-"`
	ZoneManagerThreadpoolScale                     *float64  `json:"zonemanager.threadpool.scale,string,omitempty"`
	ZoneManagerThreadpoolScaleTime                 time.Time `json:"-"`
}

type SOA struct {
	Admin              *string `json:"admin,omitempty"`
	AdminTime          time.Time
	ExpireSeconds      *int `json:"expire,string,omitempty"`
	ExpireSecondsTime  time.Time
	MinimumSeconds     *int `json:"minimum,string,omitempty"`
	MinimumSecondsTime time.Time
	RefreshSeconds     *int `json:"refresh,string,omitempty"`
	RefreshSecondsTime time.Time
	RetrySeconds       *int `json:"retry,string,omitempty"`
	RetrySecondsTime   time.Time
}

type TTL struct {
	ASeconds          *int `json:"A,string,omitempty"`
	ASecondsTime      time.Time
	AAAASeconds       *int `json:"AAAA,string,omitempty"`
	AAAASecondsTime   time.Time
	DNSkeySeconds     *int `json:"DNSKEY,string,omitempty"`
	DNSkeySecondsTime time.Time
	DSSeconds         *int `json:"DS,string,omitempty"`
	DSSecondsTime     time.Time
	NSSeconds         *int `json:"NS,string,omitempty"`
	NSSecondsTime     time.Time
	SOASeconds        *int `json:"SOA,string,omitempty"`
	SOASecondsTime    time.Time
}

type Router struct {
	APIPort       *int `json:"apiPort,string,omitempty"`
	APIPortTime   time.Time
	FQDN          *string `json:"fqdn,omitempty"`
	FQDNTime      time.Time
	HTTPSPort     *int `json:"httpsPort,omitempty"`
	HTTPSPortTime time.Time
	IP            *string `json:"ip,omitempty"`
	IPTime        time.Time
	IP6           *string `json:"ip6,omitempty"`
	IP6Time       time.Time
	Location      *string `json:"location,omitempty"`
	LocationTime  time.Time
	Port          *int `json:"port,omitempty"`
	PortTime      time.Time
	Profile       *string `json:"profile,omitempty"`
	ProfileTime   time.Time
	Status        *Status `json:"status,omitempty"`
	StatusTime    time.Time
}

type Status string

type Server struct {
	CacheGroup           *string             `json:"cacheGroup,omitempty"`
	CacheGroupTime       time.Time           `json:"-"`
	DeliveryServices     map[string][]string `json:"deliveryServices,omitempty"`
	DeliveryServicesTime time.Time           `json:"-"`
	Fqdn                 *string             `json:"fqdn,omitempty"`
	FqdnTime             time.Time           `json:"-"`
	HashCount            *int                `json:"hashCount,omitempty"`
	HashCountTime        time.Time           `json:"-"`
	HashId               *string             `json:"hashId,omitempty"`
	HashIdTime           time.Time           `json:"-"`
	HttpsPort            *int                `json:"httpsPort,omitempty"`
	HttpsPortTime        time.Time           `json:"-"`
	InterfaceName        *string             `json:"interfaceName,omitempty"`
	InterfaceNameTime    time.Time           `json:"-"`
	Ip                   *string             `json:"ip,omitempty"`
	IpTime               time.Time           `json:"-"`
	Ip6                  *string             `json:"ip6,omitempty"`
	Ip6Time              time.Time           `json:"-"`
	LocationId           *string             `json:"locationId,omitempty"`
	LocationIdTime       time.Time           `json:"-"`
	Port                 *int                `json:"port,omitempty"`
	PortTime             time.Time           `json:"-"`
	Profile              *string             `json:"profile,omitempty"`
	ProfileTime          time.Time           `json:"-"`
	Status               *Status             `json:"status,omitempty"`
	StatusTime           time.Time           `json:"-"`
	ServerType           *string             `json:"type,omitempty"`
	ServerTypeTime       time.Time           `json:"-"`
}

type DeliveryService struct {
	CoverageZoneOnly        *bool                    `json:"coverageZoneOnly,string,omitempty"`
	CoverageZoneOnlyTime    time.Time                `json:"-"`
	Dispersion              *Dispersion              `json:"dispersion,omitempty"`
	DispersionTime          time.Time                `json:"-"`
	Domains                 []string                 `json:"domains,omitempty"`
	DomainsTime             time.Time                `json:"-"`
	GeoLocationProvider     *string                  `json:"geoLocationProvider,omitempty"`
	GeoLocationProviderTime time.Time                `json:"-"`
	MatchSets               []MatchSet               `json:"matchSets,omitempty"`
	MatchSetsTime           time.Time                `json:"-"`
	MissLocation            *LatitudeLongitude       `json:"missLocation,omitempty"`
	MissLocationTime        time.Time                `json:"-"`
	Protocol                *DeliveryServiceProtocol `json:"protocol,omitempty"`
	ProtocolTime            time.Time                `json:"-"`
	RegionalGeoBlocking     *bool                    `json:"regionalGeoBlocking,string,omitempty"`
	RegionalGeoBlockingTime time.Time                `json:"-"`
	ResponseHeaders         map[string]string        `json:"responseHeaders,omitempty"`
	ResponseHeadersTime     time.Time                `json:"-"`
	Soa                     *SOA                     `json:"soa,omitempty"`
	SoaTime                 time.Time                `json:"-"`
	SSLEnabled              *bool                    `json:"sslEnabled,string,omitempty"`
	SSLEnabledTime          time.Time                `json:"-"`
	TTL                     *int                     `json:"ttl,omitempty"`
	TTLTime                 time.Time                `json:"-"`
	TTLs                    *TTL                     `json:"ttls,omitempty"`
	TTLsTime                time.Time                `json:"-"`
}
type Dispersion struct {
	Limit    int  `json:"limit,omitempty"`
	Shuffled bool `json:"shuffled,string,omitempty"`
}

type LatitudeLongitude struct {
	Lat float64 `json:"latitude"`
	Lon float64 `json:"longitude"`
}

type DeliveryServiceProtocol struct {
	AcceptHTTPS     bool `json:"acceptHttps,string,omitempty"`
	RedirectOnHTTPS bool `json:"redirectOnHttps,string,omitempty"`
}

type Monitor struct {
	FQDN          *string   `json:"fqdn,omitempty"`
	FQDNTime      time.Time `json:"-"`
	HTTPSPort     *int      `json:"httpsPort,omitempty"`
	HTTPSPortTime time.Time `json:"-"`
	IP            *string   `json:"ip,omitempty"`
	IPTime        time.Time `json:"-"`
	IP6           *string   `json:"ip6,omitempty"`
	IP6Time       time.Time `json:"-"`
	Location      *string   `json:"location,omitempty"`
	LocationTime  time.Time `json:"-"`
	Port          *int      `json:"port,omitempty"`
	PortTime      time.Time `json:"-"`
	Profile       *string   `json:"profile,omitempty"`
	ProfileTime   time.Time `json:"-"`
	Status        *Status   `json:"status,omitempty"`
	StatusTime    time.Time `json:"-"`
}

type Stats struct {
	CDNName             *string   `json:"CDN_name,omitempty"`
	CDNNameTime         time.Time `json:"-"`
	DateUnixSeconds     *int64    `json:"date,omitempty"`
	DateUnixSecondsTime time.Time `json:"-"`
	TMHost              *string   `json:"tm_host,omitempty"`
	TMHostTime          time.Time `json:"-"`
	TMPath              *string   `json:"tm_path,omitempty"`
	TMPathTime          time.Time `json:"-"`
	TMUser              *string   `json:"tm_user,omitempty"`
	TMUserTime          time.Time `json:"-"`
	TMVersion           *string   `json:"tm_version,omitempty"`
	TMVersionTime       time.Time `json:"-"`
}
<commit_msg>Fix TM CRConfig for empty string numbers<commit_after>package crconfig

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import (
	"time"
)

// CRConfig is JSON-serializable as the CRConfig used by Traffic Control. However, it also contains diff timestamps, for the last update time of each field. These can be used to return only fields which have changed since a given time.
type CRConfig struct {
	Config               Config                       `json:"config,omitempty"`
	ConfigTime           time.Time                    `json:"-"`
	ContentServers       map[string]Server            `json:"contentServers,omitempty"`
	ContentServersTime   map[string]time.Time         `json:"-"`
	ContentRouters       map[string]Router            `json:"contentRouters,omitempty"`
	ContentRoutersTime   map[string]time.Time         `json:"-"`
	DeliveryServices     map[string]DeliveryService   `json:"deliveryServices,omitempty"`
	DeliveryServicesTime map[string]time.Time         `json:"-"`
	EdgeLocations        map[string]LatitudeLongitude `json:"edgeLocations,omitempty"`
	EdgeLocationsTime    map[string]time.Time         `json:"-"`
	Monitors             map[string]Monitor           `json:"monitors,omitempty"`
	MonitorsTime         map[string]time.Time         `json:"-"`
	Stats                Stats                        `json:"stats,omitempty"`
	StatsTime            time.Time                    `json:"-"`
}

type MatchSet struct {
	Protocol  string      `json:"protocol"`
	MatchList []MatchType `json:"matchlist"`
}

type MatchType struct {
	MatchType string `json:"match-type"`
	Regex     string `json:"regex"`
}

type Config struct {
	APICacheControlMaxAge                          *string   `json:"api.cache-control.max_age,omitempty"`
	APICacheControlMaxAgeLastTime                  time.Time `json:"-"`
	ConsistentDNSRouting                           *string   `json:"consistent.dns.routing,omitempty"`
	ConsistentDNSRoutingTime                       time.Time `json:"-"`
	CoverageZonePollingIntervalSeconds             *string   `json:"coveragezone.polling.interval,omitempty"`
	CoverageZonePollingIntervalSecondsTime         time.Time `json:"-"`
	CoverageZonePollingURL                         *string   `json:"coveragezone.polling.url,omitempty"`
	CoverageZonePollingURLTime                     time.Time `json:"-"`
	DNSSecDynamicResponseExpiration                *string   `json:"dnssec.dynamic.response.expiration,omitempty"`
	DNSSecDynamicResponseExpirationTime            time.Time `json:"-"`
	DNSSecEnabled                                  *string   `json:"dnssec.enabled,omitempty"`
	DNSSecEnabledTime                              time.Time `json:"-"`
	DomainName                                     *string   `json:"domain_name,omitempty"`
	DomainNameTime                                 time.Time `json:"-"`
	FederationMappingPollingIntervalSeconds        *string   `json:"federationmapping.polling.interval,omitempty"`
	FederationMappingPollingIntervalSecondsTime    time.Time `json:"-"`
	FederationMappingPollingURL                    *string   `json:"federationmapping.polling.url"`
	FederationMappingPollingURLTime                time.Time `json:"-"`
	GeoLocationPollingInterval                     *string   `json:"geolocation.polling.interval,omitempty"`
	GeoLocationPollingIntervalTime                 time.Time `json:"-"`
	GeoLocationPollingURL                          *string   `json:"geolocation.polling.url,omitempty"`
	GeoLocationPollingURLTime                      time.Time `json:"-"`
	KeyStoreMaintenanceIntervalSeconds             *string   `json:"keystore.maintenance.interval,omitempty"`
	KeyStoreMaintenanceIntervalSecondsTime         time.Time `json:"-"`
	NeustarPollingIntervalSeconds                  *string   `json:"neustar.polling.interval,omitempty"`
	NeustarPollingIntervalSecondsTime              time.Time `json:"-"`
	NeustarPollingURL                              *string   `json:"neustar.polling.url,omitempty"`
	NeustarPollingURLTime                          time.Time `json:"-"`
	SOA                                            *SOA      `json:"soa,omitempty"`
	SOATime                                        time.Time `json:"-"`
	DNSSecInceptionSeconds                         *string   `json:"dnssec.inception,omitempty"`
	DNSSecInceptionSecondsTime                     time.Time `json:"-"`
	Ttls                                           *TTL      `json:"ttls,omitempty"`
	TtlsTime                                       time.Time `json:"-"`
	Weight                                         *string   `json:"weight,omitempty"`
	WeightTime                                     time.Time `json:"-"`
	ZoneManagerCacheMaintenanceIntervalSeconds     *string   `json:"zonemanager.cache.maintenance.interval,omitempty"`
	ZoneManagerCacheMaintenanceIntervalSecondsTime time.Time `json:"-"`
	ZoneManagerThreadpoolScale                     *string   `json:"zonemanager.threadpool.scale,omitempty"`
	ZoneManagerThreadpoolScaleTime                 time.Time `json:"-"`
}

type SOA struct {
	Admin              *string `json:"admin,omitempty"`
	AdminTime          time.Time
	ExpireSeconds      *string `json:"expire,omitempty"`
	ExpireSecondsTime  time.Time
	MinimumSeconds     *string `json:"minimum,omitempty"`
	MinimumSecondsTime time.Time
	RefreshSeconds     *string `json:"refresh,omitempty"`
	RefreshSecondsTime time.Time
	RetrySeconds       *string `json:"retry,omitempty"`
	RetrySecondsTime   time.Time
}

type TTL struct {
	ASeconds          *string `json:"A,omitempty"`
	ASecondsTime      time.Time
	AAAASeconds       *string `json:"AAAA,omitempty"`
	AAAASecondsTime   time.Time
	DNSkeySeconds     *string `json:"DNSKEY,omitempty"`
	DNSkeySecondsTime time.Time
	DSSeconds         *string `json:"DS,omitempty"`
	DSSecondsTime     time.Time
	NSSeconds         *string `json:"NS,omitempty"`
	NSSecondsTime     time.Time
	SOASeconds        *string `json:"SOA,omitempty"`
	SOASecondsTime    time.Time
}

type Router struct {
	APIPort       *string `json:"apiPort,omitempty"`
	APIPortTime   time.Time
	FQDN          *string `json:"fqdn,omitempty"`
	FQDNTime      time.Time
	HTTPSPort     *int `json:"httpsPort,omitempty"`
	HTTPSPortTime time.Time
	IP            *string `json:"ip,omitempty"`
	IPTime        time.Time
	IP6           *string `json:"ip6,omitempty"`
	IP6Time       time.Time
	Location      *string `json:"location,omitempty"`
	LocationTime  time.Time
	Port          *int `json:"port,omitempty"`
	PortTime      time.Time
	Profile       *string `json:"profile,omitempty"`
	ProfileTime   time.Time
	Status        *Status `json:"status,omitempty"`
	StatusTime    time.Time
}

type Status string

type Server struct {
	CacheGroup           *string             `json:"cacheGroup,omitempty"`
	CacheGroupTime       time.Time           `json:"-"`
	DeliveryServices     map[string][]string `json:"deliveryServices,omitempty"`
	DeliveryServicesTime time.Time           `json:"-"`
	Fqdn                 *string             `json:"fqdn,omitempty"`
	FqdnTime             time.Time           `json:"-"`
	HashCount            *int                `json:"hashCount,omitempty"`
	HashCountTime        time.Time           `json:"-"`
	HashId               *string             `json:"hashId,omitempty"`
	HashIdTime           time.Time           `json:"-"`
	HttpsPort            *int                `json:"httpsPort,omitempty"`
	HttpsPortTime        time.Time           `json:"-"`
	InterfaceName        *string             `json:"interfaceName,omitempty"`
	InterfaceNameTime    time.Time           `json:"-"`
	Ip                   *string             `json:"ip,omitempty"`
	IpTime               time.Time           `json:"-"`
	Ip6                  *string             `json:"ip6,omitempty"`
	Ip6Time              time.Time           `json:"-"`
	LocationId           *string             `json:"locationId,omitempty"`
	LocationIdTime       time.Time           `json:"-"`
	Port                 *int                `json:"port,omitempty"`
	PortTime             time.Time           `json:"-"`
	Profile              *string             `json:"profile,omitempty"`
	ProfileTime          time.Time           `json:"-"`
	Status               *Status             `json:"status,omitempty"`
	StatusTime           time.Time           `json:"-"`
	ServerType           *string             `json:"type,omitempty"`
	ServerTypeTime       time.Time           `json:"-"`
}

type DeliveryService struct {
	CoverageZoneOnly        *string                  `json:"coverageZoneOnly,omitempty"`
	CoverageZoneOnlyTime    time.Time                `json:"-"`
	Dispersion              *Dispersion              `json:"dispersion,omitempty"`
	DispersionTime          time.Time                `json:"-"`
	Domains                 []string                 `json:"domains,omitempty"`
	DomainsTime             time.Time                `json:"-"`
	GeoLocationProvider     *string                  `json:"geoLocationProvider,omitempty"`
	GeoLocationProviderTime time.Time                `json:"-"`
	MatchSets               []MatchSet               `json:"matchSets,omitempty"`
	MatchSetsTime           time.Time                `json:"-"`
	MissLocation            *LatitudeLongitude       `json:"missLocation,omitempty"`
	MissLocationTime        time.Time                `json:"-"`
	Protocol                *DeliveryServiceProtocol `json:"protocol,omitempty"`
	ProtocolTime            time.Time                `json:"-"`
	RegionalGeoBlocking     *string                  `json:"regionalGeoBlocking,omitempty"`
	RegionalGeoBlockingTime time.Time                `json:"-"`
	ResponseHeaders         map[string]string        `json:"responseHeaders,omitempty"`
	ResponseHeadersTime     time.Time                `json:"-"`
	Soa                     *SOA                     `json:"soa,omitempty"`
	SoaTime                 time.Time                `json:"-"`
	SSLEnabled              *string                  `json:"sslEnabled,omitempty"`
	SSLEnabledTime          time.Time                `json:"-"`
	TTL                     *int                     `json:"ttl,omitempty"`
	TTLTime                 time.Time                `json:"-"`
	TTLs                    *TTL                     `json:"ttls,omitempty"`
	TTLsTime                time.Time                `json:"-"`
}
type Dispersion struct {
	Limit    int     `json:"limit,omitempty"`
	Shuffled *string `json:"shuffled,omitempty"`
}

type LatitudeLongitude struct {
	Lat float64 `json:"latitude"`
	Lon float64 `json:"longitude"`
}

type DeliveryServiceProtocol struct {
	AcceptHTTPS     *string `json:"acceptHttps,omitempty"`
	RedirectOnHTTPS *string `json:"redirectOnHttps,omitempty"`
}

type Monitor struct {
	FQDN          *string   `json:"fqdn,omitempty"`
	FQDNTime      time.Time `json:"-"`
	HTTPSPort     *int      `json:"httpsPort,omitempty"`
	HTTPSPortTime time.Time `json:"-"`
	IP            *string   `json:"ip,omitempty"`
	IPTime        time.Time `json:"-"`
	IP6           *string   `json:"ip6,omitempty"`
	IP6Time       time.Time `json:"-"`
	Location      *string   `json:"location,omitempty"`
	LocationTime  time.Time `json:"-"`
	Port          *int      `json:"port,omitempty"`
	PortTime      time.Time `json:"-"`
	Profile       *string   `json:"profile,omitempty"`
	ProfileTime   time.Time `json:"-"`
	Status        *Status   `json:"status,omitempty"`
	StatusTime    time.Time `json:"-"`
}

type Stats struct {
	CDNName             *string   `json:"CDN_name,omitempty"`
	CDNNameTime         time.Time `json:"-"`
	DateUnixSeconds     *int64    `json:"date,omitempty"`
	DateUnixSecondsTime time.Time `json:"-"`
	TMHost              *string   `json:"tm_host,omitempty"`
	TMHostTime          time.Time `json:"-"`
	TMPath              *string   `json:"tm_path,omitempty"`
	TMPathTime          time.Time `json:"-"`
	TMUser              *string   `json:"tm_user,omitempty"`
	TMUserTime          time.Time `json:"-"`
	TMVersion           *string   `json:"tm_version,omitempty"`
	TMVersionTime       time.Time `json:"-"`
}
<|endoftext|>


<commit_before>package dockerclient

import "time"

type ContainerConfig struct {
	Hostname        string
	Domainname      string
	User            string
	Memory          int64
	MemorySwap      int64
	CpuShares       int64
	Cpuset          string
	AttachStdin     bool
	AttachStdout    bool
	AttachStderr    bool
	PortSpecs       []string
	ExposedPorts    map[string]struct{}
	Tty             bool
	OpenStdin       bool
	StdinOnce       bool
	Env             []string
	Cmd             []string
	Image           string
	Volumes         map[string]struct{}
	WorkingDir      string
	Entrypoint      []string
	NetworkDisabled bool
	OnBuild         []string

	// This is used only by the create command
	HostConfig HostConfig
}

type HostConfig struct {
	Binds           []string
	ContainerIDFile string
	LxcConf         []map[string]string
	Privileged      bool
	PortBindings    map[string][]PortBinding
	Links           []string
	PublishAllPorts bool
	Dns             []string
	DnsSearch       []string
	VolumesFrom     []string
	NetworkMode     string
	RestartPolicy   RestartPolicy
}

type LogOptions struct {
	Follow     bool
	Stdout     bool
	Stderr     bool
	Timestamps bool
	Tail       int64
}

type RestartPolicy struct {
	Name              string
	MaximumRetryCount int64
}

type PortBinding struct {
	HostIp   string
	HostPort string
}

type ContainerInfo struct {
	Id      string
	Created string
	Path    string
	Name    string
	Args    []string
	Config  *ContainerConfig
	State   struct {
		Running    bool
		Paused     bool
		Restarting bool
		Pid        int
		ExitCode   int
		StartedAt  time.Time
		FinishedAt time.Time
		Ghost      bool
	}
	Image           string
	NetworkSettings struct {
		IpAddress   string
		IpPrefixLen int
		Gateway     string
		Bridge      string
		Ports       map[string][]PortBinding
	}
	SysInitPath    string
	ResolvConfPath string
	Volumes        map[string]string
	HostConfig     *HostConfig
}

type Port struct {
	IP          string
	PrivatePort int
	PublicPort  int
	Type        string
}

type Container struct {
	Id         string
	Names      []string
	Image      string
	Command    string
	Created    int64
	Status     string
	Ports      []Port
	SizeRw     int64
	SizeRootFs int64
}

type Event struct {
	Id     string
	Status string
	From   string
	Time   int64
}

type Version struct {
	Version   string
	GitCommit string
	GoVersion string
}

type RespContainersCreate struct {
	Id       string
	Warnings []string
}

type Image struct {
	Created     int64
	Id          string
	ParentId    string
	RepoTags    []string
	Size        int64
	VirtualSize int64
}

type Info struct {
	Containers      int64
	Driver          string
	DriverStatus    [][]string
	ExecutionDriver string
	Images          int64
	KernelVersion   string
	OperatingSystem string
	NCPU            int64
	MemTotal        int64
}
<commit_msg>update info<commit_after>package dockerclient

import "time"

type ContainerConfig struct {
	Hostname        string
	Domainname      string
	User            string
	Memory          int64
	MemorySwap      int64
	CpuShares       int64
	Cpuset          string
	AttachStdin     bool
	AttachStdout    bool
	AttachStderr    bool
	PortSpecs       []string
	ExposedPorts    map[string]struct{}
	Tty             bool
	OpenStdin       bool
	StdinOnce       bool
	Env             []string
	Cmd             []string
	Image           string
	Volumes         map[string]struct{}
	WorkingDir      string
	Entrypoint      []string
	NetworkDisabled bool
	OnBuild         []string

	// This is used only by the create command
	HostConfig HostConfig
}

type HostConfig struct {
	Binds           []string
	ContainerIDFile string
	LxcConf         []map[string]string
	Privileged      bool
	PortBindings    map[string][]PortBinding
	Links           []string
	PublishAllPorts bool
	Dns             []string
	DnsSearch       []string
	VolumesFrom     []string
	NetworkMode     string
	RestartPolicy   RestartPolicy
}

type LogOptions struct {
	Follow     bool
	Stdout     bool
	Stderr     bool
	Timestamps bool
	Tail       int64
}

type RestartPolicy struct {
	Name              string
	MaximumRetryCount int64
}

type PortBinding struct {
	HostIp   string
	HostPort string
}

type ContainerInfo struct {
	Id      string
	Created string
	Path    string
	Name    string
	Args    []string
	Config  *ContainerConfig
	State   struct {
		Running    bool
		Paused     bool
		Restarting bool
		Pid        int
		ExitCode   int
		StartedAt  time.Time
		FinishedAt time.Time
		Ghost      bool
	}
	Image           string
	NetworkSettings struct {
		IpAddress   string
		IpPrefixLen int
		Gateway     string
		Bridge      string
		Ports       map[string][]PortBinding
	}
	SysInitPath    string
	ResolvConfPath string
	Volumes        map[string]string
	HostConfig     *HostConfig
}

type Port struct {
	IP          string
	PrivatePort int
	PublicPort  int
	Type        string
}

type Container struct {
	Id         string
	Names      []string
	Image      string
	Command    string
	Created    int64
	Status     string
	Ports      []Port
	SizeRw     int64
	SizeRootFs int64
}

type Event struct {
	Id     string
	Status string
	From   string
	Time   int64
}

type Version struct {
	Version   string
	GitCommit string
	GoVersion string
}

type RespContainersCreate struct {
	Id       string
	Warnings []string
}

type Image struct {
	Created     int64
	Id          string
	ParentId    string
	RepoTags    []string
	Size        int64
	VirtualSize int64
}

type Info struct {
	ID              string
	Containers      int64
	Driver          string
	DriverStatus    [][]string
	ExecutionDriver string
	Images          int64
	KernelVersion   string
	OperatingSystem string
	NCPU            int64
	MemTotal        int64
	Name            string
}
<|endoftext|>


<commit_before>package tripeg

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

	"github.com/fatih/color"
)

//Hole struct that contains information
//about a hole in the board, its location
//and whether or not it has a peg in it.
type Hole struct {
	Row    int //max of 5
	Col    int //max of 9
	Peg    bool
	Status int
}

const (
	//Dormant short hand for a row/col location that's not involved in a jump move
	Dormant = iota
	//Source shorthand for the source peg row/col for a jump move
	Source
	//Target the empty row/col the source peg will land in for a jump move.
	Target
)

func (b Board) showMove(m, o, t Hole) Board {
	result := Board{}
	result.Rows = b.Rows
	for k, v := range b.Holes {
		b.Holes[k].Status = Dormant
		if v.Row == m.Row && v.Col == m.Col {
			b.Holes[k].Status = Source
		}
		if v.Row == t.Row && v.Col == t.Col {
			b.Holes[k].Status = Target
		}
	}
	result.Holes = b.Holes
	return result

}

//Jump from the Board struct type
func (b Board) Jump(m, o Hole) (Board, Hole, error) {
	result := Board{}
	result.SolveMoves = b.SolveMoves
	result.Rows = b.Rows
	thole := Hole{}
	for _, r := range b.Holes {
		result.Holes = append(result.Holes, r)
	}
	if !m.Peg {
		//If there is no peg in the moveHole, no jump possible
		return result, thole, fmt.Errorf("No Peg in move hole %d,%d\n", o.Row, o.Col)
	}
	if !o.Peg {
		//If there is no peg in the overHole, no jump possible
		return result, thole, fmt.Errorf("No Peg in over hole %d,%d\n", o.Row, o.Col)
	}
	rDif := m.Row - o.Row
	cDif := o.Col - m.Col
	if cDif == 0 && rDif == 0 {
		//Holes are the same, not valid
		return result, thole, fmt.Errorf("Jump peg and over hole are the same\n")
	}
	if math.Abs(float64(rDif)) > 1 {
		//You can't jump over more than 1 row horizontally
		return result, thole, fmt.Errorf("Invalid horizonal movement %d\n", rDif)
	}
	if rDif > 0 && math.Abs(float64(cDif)) > 1 {
		//You can't jump over more than 1 col vertically
		return result, thole, fmt.Errorf("Invalid vertical movement %d\n", cDif)
	}
	if rDif == 0 && math.Abs(float64(cDif)) > 2 {
		return result, thole, fmt.Errorf("Invalid horizantal movement %d\n", rDif)
		//You can't jump more than 2 cols horizontally
	}
	targetR := 0
	targetC := 0
	if rDif == 0 {
		//This is a horizontal jump
		targetR = m.Row
	}
	if rDif > 0 {
		targetR = o.Row - 1
		//This is an up jump
	}
	if rDif < 0 {
		targetR = o.Row + 1
		//This is a jump down
	}
	if cDif < 0 {
		x := 1
		if rDif == 0 {
			x = 2
		}
		targetC = o.Col - x
		//This is a jump left
	}
	if cDif > 0 {
		x := 1
		if rDif == 0 {
			x = 2
		}
		targetC = o.Col + x
		//This is a jump right
	}
	targetHole, err := b.GetHole(targetR, targetC)
	if err != nil {
		return result, thole, fmt.Errorf("Target hole(%d,%d) does not exist\n", targetR, targetC)
	}
	if targetHole.Peg {
		return result, thole, fmt.Errorf("Target hole(%d,%d) has a peg in it\n", targetHole.Row, targetHole.Col)
	}
	for k, bh := range result.Holes {
		if bh.Row == m.Row && bh.Col == m.Col {
			result.Holes[k].Peg = false
		}
		if bh.Row == o.Row && bh.Col == o.Col {
			result.Holes[k].Peg = false
		}
		if bh.Row == targetHole.Row && bh.Col == targetHole.Col {
			result.Holes[k].Peg = true
		}
	}
	return result, targetHole, nil
}

//Board contains all the holes that contain the pegs
type Board struct {
	Holes      []Hole
	MoveChart  []string
	SolveMoves int
	Rows       int
}

//GetHole gets a pointer to a hole based on the row,col coordinates
func (b Board) GetHole(r, c int) (Hole, error) {
	if r < 0 || r > b.Rows+1 || c < 0 || c > b.Rows+(b.Rows-1) {
		return Hole{}, fmt.Errorf("Hole %d,%d does not exist\n", r, c)
	}
	for _, v := range b.Holes {
		if v.Col == c && v.Row == r {
			return v, nil
		}
	}
	return Hole{}, fmt.Errorf("Hole %d,%d does not exist\n", r, c)
}

//BuildBoard makes a board of peg holes.
//All holes have a peg except one.
func BuildBoard(rows, empty int) (Board, error) {
	var b Board
	if rows < 5 {
		return b, fmt.Errorf("Invalid rows valid %d, it must be greater than 4\n", rows)
	}
	if rows > 6 {
		//return b, fmt.Errorf("We're going to need a better algorithm before we get to %d rows...\n", rows)
	}
	max := 0
	for i := 1; i < rows+1; i++ {
		max += i
	}
	b.SolveMoves = max - 2
	b.Rows = rows

	if empty < 0 || empty > max {
		return b, fmt.Errorf("1st parameter must be >=0 or <=%d, you supplied %d", max, empty)
	}
	s2 := rand.NewSource(time.Now().UnixNano())
	r2 := rand.New(s2)
	if empty == 0 {
		empty = r2.Intn(max)
	} else {
		empty--
	}
	for r := 1; r < rows+1; r++ {
		for c := 1; c < r+1; c++ {
			offset := 1
			col := rows + (c * 2) - offset - r
			h := Hole{Row: r, Col: col, Peg: true}
			if empty == len(b.Holes) {
				h.Peg = false
			}
			b.Holes = append(b.Holes, h)
		}
	}
	return b, nil
}

type move struct {
	H Hole
	O Hole
	T Hole
}

func (m move) String() string {
	return fmt.Sprintf("[%d,%d] over [%d,%d] to [%d,%d]", m.H.Row, m.H.Col, m.O.Row, m.O.Col, m.T.Row, m.T.Col)
}

//ErrorArray an array of errors that also implements the error interface
type ErrorArray struct {
	Errors []error
}

func (ea ErrorArray) Error() string {
	r := ""
	m := len(ea.Errors)
	c := m
	if m > 11 {
		m = 11
		ea.Errors[10] = fmt.Errorf("Too many errors! Count: %v", c-1)
	}
	for _, v := range ea.Errors[0:m] {
		r += v.Error() + "\n"
	}
	return r[0 : len(r)-1]
}

//Add takes an argument of the error interface and adds it to the array
func (ea *ErrorArray) Add(err error) {
	ea.Errors = append(ea.Errors, err)
}

//Solve does a brute force solving of the game
func (b *Board) Solve() []error {
	high := 0
	s2 := rand.NewSource(time.Now().UnixNano())
	r2 := rand.New(s2)
	var newBoard = b
	var solved = false
	var solveErrors = ErrorArray{}
	validMove := 0
	for {
		func() {
			aMoves := []move{}
			o := Hole{}
			var err error
			for _, v := range newBoard.Holes {
				/*
					Go through all of the holes on the board.
					If the hole doesn't have a peg, it can't
					have a legal move, so skip it.
					If it doesn't have a peg, just to see if it has
					a legal move by jumping left, right, up left, up right, down left or down right.
					If any of these moves are legal, add it to the array of available moves.
					Do this for each hole on the board.
					Randomly select a legal move, color the board and return the new color coded board.
					Keep doing this until we've done SolveMoves legal moves or we run out of availaable moves.
					If no legal moves left, start over and hope for the best.
					If SolveMoves legal moves, then we've solved it, return out of here.
				*/
				if v.Peg {
					//upleft
					o, err = newBoard.GetHole(v.Row-1, v.Col-1)
					if err == nil {
						_, t, errJ := newBoard.Jump(v, o)
						if errJ == nil {
							aMoves = append(aMoves, move{H: v, O: o, T: t})
						}
					}

					//upright
					o, err = newBoard.GetHole(v.Row-1, v.Col+1)
					if err == nil {
						_, t, errJ := newBoard.Jump(v, o)
						if errJ == nil {
							aMoves = append(aMoves, move{H: v, O: o, T: t})
						}
					}

					//left
					o, err = newBoard.GetHole(v.Row, v.Col-2)
					if err == nil {
						_, t, errJ := newBoard.Jump(v, o)
						if errJ == nil {
							aMoves = append(aMoves, move{H: v, O: o, T: t})
						}
					}
					//right
					o, err = newBoard.GetHole(v.Row, v.Col+2)
					if err == nil {
						_, t, errJ := newBoard.Jump(v, o)
						if errJ == nil {
							aMoves = append(aMoves, move{H: v, O: o, T: t})
						}
					}

					//downleft
					o, err = newBoard.GetHole(v.Row+1, v.Col-1)
					if err == nil {
						_, t, errJ := newBoard.Jump(v, o)
						if errJ == nil {
							aMoves = append(aMoves, move{H: v, O: o, T: t})
						}
					}

					//downright
					o, err = newBoard.GetHole(v.Row+1, v.Col+1)
					if err == nil {
						_, t, errJ := newBoard.Jump(v, o)
						if errJ == nil {
							aMoves = append(aMoves, move{H: v, O: o, T: t})
						}
					}
				}
			}
			if len(aMoves) == 0 {
				//No legal moves left
				newBoard = b
				validMove = 0
				b.MoveChart = []string{}
				return
			}
			available := r2.Intn(len(aMoves))
			avs := aMoves[available].H
			avo := aMoves[available].O
			cBoard, th, errN := newBoard.Jump(avs, avo)
			cBoard.Rows = b.Rows
			if errN != nil {
				solveErrors.Add(errN)
			}
			validMove++
			if validMove > high {
				high = validMove
				//fmt.Println(b.SolveMoves, high, b.SolveMoves-high)
			}
			b.MoveChart = append(b.MoveChart, fmt.Sprintf("%v", newBoard.showMove(avs, avo, th)))

			newBoard = &cBoard
			if validMove == b.SolveMoves {
				solved = true
				return
			}
		}()
		if solved {
			break
		}
	}
	return nil
}

func (b Board) String() string {
	result := "\n"
	tar := color.New(color.FgRed).SprintFunc()
	src := color.New(color.FgGreen).SprintFunc()
	dor := color.New(color.FgWhite).SprintFunc()
	offset := 1
	for r := 1; r < b.Rows+1; r++ {
		for c := 1; c < b.Rows*2+offset; c++ {
			h, err := b.GetHole(r, c)
			mark := " "
			if err == nil {
				mark = "O"
				if h.Peg {
					mark = "*"
				}
			}
			switch h.Status {
			case Source:
				result += fmt.Sprintf("%s", src(mark))
			case Target:
				result += fmt.Sprintf("%s", tar(mark))
			case Dormant:
				result += fmt.Sprintf("%s", dor(mark))
			}
		}
		result += "\n"
	}
	return result
}
<commit_msg>sorta works better<commit_after>package tripeg

import (
	"crypto/sha1"
	"fmt"
	"io"
	"math"
	"math/rand"
	"time"

	"github.com/fatih/color"
)

//Hole struct that contains information
//about a hole in the board, its location
//and whether or not it has a peg in it.
type Hole struct {
	Row    int //max of 5
	Col    int //max of 9
	Peg    bool
	Status int
}

const (
	//Dormant short hand for a row/col location that's not involved in a jump move
	Dormant = iota
	//Source shorthand for the source peg row/col for a jump move
	Source
	//Target the empty row/col the source peg will land in for a jump move.
	Target
)

func (b Board) showMove(m, o, t Hole) Board {
	result := Board{}
	result.Rows = b.Rows
	for k, v := range b.Holes {
		b.Holes[k].Status = Dormant
		if v.Row == m.Row && v.Col == m.Col {
			b.Holes[k].Status = Source
		}
		if v.Row == t.Row && v.Col == t.Col {
			b.Holes[k].Status = Target
		}
	}
	result.Holes = b.Holes
	return result

}

//Jump from the Board struct type
func (b Board) Jump(m, o Hole) (Board, Hole, error) {
	result := Board{}
	result.SolveMoves = b.SolveMoves
	result.Rows = b.Rows
	thole := Hole{}
	for _, r := range b.Holes {
		result.Holes = append(result.Holes, r)
	}
	if !m.Peg {
		//If there is no peg in the moveHole, no jump possible
		return result, thole, fmt.Errorf("No Peg in move hole %d,%d\n", o.Row, o.Col)
	}
	if !o.Peg {
		//If there is no peg in the overHole, no jump possible
		return result, thole, fmt.Errorf("No Peg in over hole %d,%d\n", o.Row, o.Col)
	}
	rDif := m.Row - o.Row
	cDif := o.Col - m.Col
	if cDif == 0 && rDif == 0 {
		//Holes are the same, not valid
		return result, thole, fmt.Errorf("Jump peg and over hole are the same\n")
	}
	if math.Abs(float64(rDif)) > 1 {
		//You can't jump over more than 1 row horizontally
		return result, thole, fmt.Errorf("Invalid horizonal movement %d\n", rDif)
	}
	if rDif > 0 && math.Abs(float64(cDif)) > 1 {
		//You can't jump over more than 1 col vertically
		return result, thole, fmt.Errorf("Invalid vertical movement %d\n", cDif)
	}
	if rDif == 0 && math.Abs(float64(cDif)) > 2 {
		return result, thole, fmt.Errorf("Invalid horizantal movement %d\n", rDif)
		//You can't jump more than 2 cols horizontally
	}
	targetR := 0
	targetC := 0
	if rDif == 0 {
		//This is a horizontal jump
		targetR = m.Row
	}
	if rDif > 0 {
		targetR = o.Row - 1
		//This is an up jump
	}
	if rDif < 0 {
		targetR = o.Row + 1
		//This is a jump down
	}
	if cDif < 0 {
		x := 1
		if rDif == 0 {
			x = 2
		}
		targetC = o.Col - x
		//This is a jump left
	}
	if cDif > 0 {
		x := 1
		if rDif == 0 {
			x = 2
		}
		targetC = o.Col + x
		//This is a jump right
	}
	targetHole, err := b.GetHole(targetR, targetC)
	if err != nil {
		return result, thole, fmt.Errorf("Target hole(%d,%d) does not exist\n", targetR, targetC)
	}
	if targetHole.Peg {
		return result, thole, fmt.Errorf("Target hole(%d,%d) has a peg in it\n", targetHole.Row, targetHole.Col)
	}
	for k, bh := range result.Holes {
		if bh.Row == m.Row && bh.Col == m.Col {
			result.Holes[k].Peg = false
		}
		if bh.Row == o.Row && bh.Col == o.Col {
			result.Holes[k].Peg = false
		}
		if bh.Row == targetHole.Row && bh.Col == targetHole.Col {
			result.Holes[k].Peg = true
		}
	}
	return result, targetHole, nil
}

//Board contains all the holes that contain the pegs
type Board struct {
	Holes      []Hole
	MoveChart  []string
	SolveMoves int
	Rows       int
}

//Hash returns a hash value for the board
func (b Board) Hash() string {
	raw := ""
	for _, v := range b.Holes {
		raw += fmt.Sprintf("%v-%v-%v", v.Row, v.Col, v.Peg)
	}
	h := sha1.New()
	io.WriteString(h, raw)
	return fmt.Sprintf("%x", h.Sum(nil))
}

//GetHole gets a pointer to a hole based on the row,col coordinates
func (b Board) GetHole(r, c int) (Hole, error) {
	if r < 0 || r > b.Rows+1 || c < 0 || c > b.Rows+(b.Rows-1) {
		return Hole{}, fmt.Errorf("Hole %d,%d does not exist\n", r, c)
	}
	for _, v := range b.Holes {
		if v.Col == c && v.Row == r {
			return v, nil
		}
	}
	return Hole{}, fmt.Errorf("Hole %d,%d does not exist\n", r, c)
}

//BuildBoard makes a board of peg holes.
//All holes have a peg except one.
func BuildBoard(rows, empty int) (Board, error) {
	var b Board
	if rows < 5 {
		return b, fmt.Errorf("Invalid rows valid %d, it must be greater than 4\n", rows)
	}
	if rows > 6 {
		//return b, fmt.Errorf("We're going to need a better algorithm before we get to %d rows...\n", rows)
	}
	max := 0
	for i := 1; i < rows+1; i++ {
		max += i
	}
	b.SolveMoves = max - 2
	b.Rows = rows

	if empty < 0 || empty > max {
		return b, fmt.Errorf("1st parameter must be >=0 or <=%d, you supplied %d", max, empty)
	}
	s2 := rand.NewSource(time.Now().UnixNano())
	r2 := rand.New(s2)
	if empty == 0 {
		empty = r2.Intn(max)
	} else {
		empty--
	}
	for r := 1; r < rows+1; r++ {
		for c := 1; c < r+1; c++ {
			offset := 1
			col := rows + (c * 2) - offset - r
			h := Hole{Row: r, Col: col, Peg: true}
			if empty == len(b.Holes) {
				h.Peg = false
			}
			b.Holes = append(b.Holes, h)
		}
	}
	return b, nil
}

type move struct {
	H Hole
	O Hole
	T Hole
}

func (m move) String() string {
	return fmt.Sprintf("[%d,%d] over [%d,%d] to [%d,%d]", m.H.Row, m.H.Col, m.O.Row, m.O.Col, m.T.Row, m.T.Col)
}

//ErrorArray an array of errors that also implements the error interface
type ErrorArray struct {
	Errors []error
}

func (ea ErrorArray) Error() string {
	r := ""
	m := len(ea.Errors)
	c := m
	if m > 11 {
		m = 11
		ea.Errors[10] = fmt.Errorf("Too many errors! Count: %v", c-1)
	}
	for _, v := range ea.Errors[0:m] {
		r += v.Error() + "\n"
	}
	return r[0 : len(r)-1]
}

//Add takes an argument of the error interface and adds it to the array
func (ea *ErrorArray) Add(err error) {
	ea.Errors = append(ea.Errors, err)
}

//Solve does a brute force solving of the game
func (b *Board) Solve() []error {
	ngMap := map[string]string{}
	high := 0
	s2 := rand.NewSource(time.Now().UnixNano())
	r2 := rand.New(s2)
	var newBoard = b
	var solved = false
	var solveErrors = ErrorArray{}
	validMove := 0
	for {
		func() {
			aMoves := []move{}
			o := Hole{}
			var err error
			for _, v := range newBoard.Holes {
				/*
					Go through all of the holes on the board.
					If the hole doesn't have a peg, it can't
					have a legal move, so skip it.
					If it doesn't have a peg, just to see if it has
					a legal move by jumping left, right, up left, up right, down left or down right.
					If any of these moves are legal, add it to the array of available moves.
					Do this for each hole on the board.
					Randomly select a legal move, color the board and return the new color coded board.
					Keep doing this until we've done SolveMoves legal moves or we run out of availaable moves.
					If no legal moves left, start over and hope for the best.
					If SolveMoves legal moves, then we've solved it, return out of here.
				*/
				if v.Peg {
					//upleft
					o, err = newBoard.GetHole(v.Row-1, v.Col-1)
					if err == nil {
						pboard, t, errJ := newBoard.Jump(v, o)
						if errJ == nil {
							if _, ok := ngMap[pboard.Hash()]; !ok {
								aMoves = append(aMoves, move{H: v, O: o, T: t})
							}
						}
					}

					//upright
					o, err = newBoard.GetHole(v.Row-1, v.Col+1)
					if err == nil {
						pboard, t, errJ := newBoard.Jump(v, o)
						if errJ == nil {
							if _, ok := ngMap[pboard.Hash()]; !ok {
								aMoves = append(aMoves, move{H: v, O: o, T: t})
							}
						}
					}

					//left
					o, err = newBoard.GetHole(v.Row, v.Col-2)
					if err == nil {
						pboard, t, errJ := newBoard.Jump(v, o)
						if errJ == nil {
							if _, ok := ngMap[pboard.Hash()]; !ok {
								aMoves = append(aMoves, move{H: v, O: o, T: t})
							}
						}
					}
					//right
					o, err = newBoard.GetHole(v.Row, v.Col+2)
					if err == nil {
						pboard, t, errJ := newBoard.Jump(v, o)
						if errJ == nil {
							if _, ok := ngMap[pboard.Hash()]; !ok {
								aMoves = append(aMoves, move{H: v, O: o, T: t})
							}
						}
					}

					//downleft
					o, err = newBoard.GetHole(v.Row+1, v.Col-1)
					if err == nil {
						pboard, t, errJ := newBoard.Jump(v, o)
						if errJ == nil {
							if _, ok := ngMap[pboard.Hash()]; !ok {
								aMoves = append(aMoves, move{H: v, O: o, T: t})
							}
						}
					}

					//downright
					o, err = newBoard.GetHole(v.Row+1, v.Col+1)
					if err == nil {
						pboard, t, errJ := newBoard.Jump(v, o)
						if errJ == nil {
							if _, ok := ngMap[pboard.Hash()]; !ok {
								aMoves = append(aMoves, move{H: v, O: o, T: t})
							}
						}
					}
				}
			}
			if len(aMoves) == 0 {
				//No legal moves left
				ngMap[newBoard.Hash()] = fmt.Sprintf("%s", newBoard)
				newBoard = b
				validMove = 0
				b.MoveChart = []string{}
				return
			}
			available := r2.Intn(len(aMoves))
			avs := aMoves[available].H
			avo := aMoves[available].O
			cBoard, th, errN := newBoard.Jump(avs, avo)
			cBoard.Rows = b.Rows
			if errN != nil {
				solveErrors.Add(errN)
			}
			validMove++
			if validMove > high {
				high = validMove
				// fmt.Println(b.SolveMoves, high, b.SolveMoves-high)
			}
			b.MoveChart = append(b.MoveChart, fmt.Sprintf("%v", newBoard.showMove(avs, avo, th)))

			newBoard = &cBoard
			if validMove == b.SolveMoves {
				solved = true
				return
			}
		}()
		if solved {
			break
		}
	}
	return nil
}

func (b Board) String() string {
	result := "\n"
	tar := color.New(color.FgRed).SprintFunc()
	src := color.New(color.FgGreen).SprintFunc()
	dor := color.New(color.FgWhite).SprintFunc()
	offset := 1
	for r := 1; r < b.Rows+1; r++ {
		for c := 1; c < b.Rows*2+offset; c++ {
			h, err := b.GetHole(r, c)
			mark := " "
			if err == nil {
				mark = "O"
				if h.Peg {
					mark = "*"
				}
			}
			switch h.Status {
			case Source:
				result += fmt.Sprintf("%s", src(mark))
			case Target:
				result += fmt.Sprintf("%s", tar(mark))
			case Dormant:
				result += fmt.Sprintf("%s", dor(mark))
			}
		}
		result += "\n"
	}
	return result
}
<|endoftext|>


<commit_before>package network

import (
	"bytes"
	"common"
	"testing"
	"time"
)

// a simple message handler
type TestMsgHandler struct {
	id     common.Identifier
	result string
}

func (t *TestMsgHandler) Identifier() common.Identifier {
	return t.id
}

func (t *TestMsgHandler) HandleMessage(payload []byte) {
	t.result = string(payload)
}

// TestTCPSendMessage tests the NewTCPServer and SendMessage functions.
// NewTCPServer must properly initialize a TCP server.
// SendMessage must succesfully deliver a message.
func TestTCPSendMessage(t *testing.T) {
	// create TCPServer and add a message handler
	tcp, err := NewTCPServer(9988)
	if err != nil {
		t.Fatal("Failed to initialize TCPServer:", err)
	}
	defer tcp.Close()

	// create message handler and add it to the TCPServer
	tmh := new(TestMsgHandler)
	tmh.id = 1
	tcp.AddMessageHandler(tmh)

	// send a message to be echoed
	m := common.Message{common.Address{1, "localhost", 9988}, []byte("hello, world!")}
	err = tcp.SendMessage(&m)
	if err != nil {
		t.Fatal("Failed to send message:", err)
	}
	// allow time for message to be processed
	time.Sleep(10 * time.Millisecond)
	resp := tmh.result
	if resp == "" {
		t.Fatal("Bad response: expected \"hello, world!\", got \"\"")
	}
	if tmh.result != "hello, world!" {
		t.Fatal("Bad response: expected \"hello, world!\", got \"" + tmh.result + "\"")
	}

	// send a message that should not trigger a MessageHandler
	tmh.result = ""
	m = common.Message{common.Address{2, "localhost", 9988}, []byte("won't be seen")}
	err = tcp.SendMessage(&m)
	if err != nil {
		t.Fatal("Failed to send message:", err)
	}
	// allow time for message to be processed
	time.Sleep(10 * time.Millisecond)
	if tmh.result != "" {
		t.Fatal("Bad response: expected \"\", got \"" + tmh.result + "\"")
	}

	// send a message longer than 1024 bytes
	tmh.result = ""
	m = common.Message{common.Address{1, "localhost", 9988}, bytes.Repeat([]byte("b"), 9001)}
	err = tcp.SendMessage(&m)
	if err != nil {
		t.Fatal("Failed to send message:", err)
	}
	// wait for handler to be triggered
	<-tmh.done
	if len(tmh.result) != 9001 {
		t.Fatal("Bad response: expected 9001 bytes, got", len(tmh.result))
	}
}
<commit_msg>use a channel instead of time.Sleep()<commit_after>package network

import (
	"bytes"
	"common"
	"testing"
	"time"
)

// a simple message handler
// stores the received message in the result field
// uses a simple channel to signal when handler has been called
type TestMsgHandler struct {
	id     common.Identifier
	result string
	done   chan bool
}

func (tmh *TestMsgHandler) Identifier() common.Identifier {
	return tmh.id
}

func (tmh *TestMsgHandler) HandleMessage(payload []byte) {
	tmh.result = string(payload)
	tmh.done <- true
}

// TestTCPSendMessage tests the NewTCPServer and SendMessage functions.
// NewTCPServer must properly initialize a TCP server.
// SendMessage must succesfully deliver a message.
func TestTCPSendMessage(t *testing.T) {
	// create TCPServer and add a message handler
	tcp, err := NewTCPServer(9988)
	if err != nil {
		t.Fatal("Failed to initialize TCPServer:", err)
	}
	defer tcp.Close()

	// create message handler and add it to the TCPServer
	tmh := new(TestMsgHandler)
	tmh.id = 1
	tmh.done = make(chan bool, 1)
	tcp.AddMessageHandler(tmh)

	// send a message to be echoed
	m := common.Message{common.Address{1, "localhost", 9988}, []byte("hello, world!")}
	err = tcp.SendMessage(&m)
	if err != nil {
		t.Fatal("Failed to send message:", err)
	}
	// wait for handler to be triggered
	<-tmh.done
	if tmh.result == "" {
		t.Fatal("Bad response: expected \"hello, world!\", got \"\"")
	}
	if tmh.result != "hello, world!" {
		t.Fatal("Bad response: expected \"hello, world!\", got \"" + tmh.result + "\"")
	}

	// send a message that should not trigger a MessageHandler
	tmh.result = ""
	m = common.Message{common.Address{2, "localhost", 9988}, []byte("won't be seen")}
	err = tcp.SendMessage(&m)
	if err != nil {
		t.Fatal("Failed to send message:", err)
	}
	// arbitrary wait, because <-tmh.done will block
	time.Sleep(10 * time.Millisecond)
	if tmh.result != "" {
		t.Fatal("Bad response: expected \"\", got \"" + tmh.result + "\"")
	}

	// send a message longer than 1024 bytes
	tmh.result = ""
	m = common.Message{common.Address{1, "localhost", 9988}, bytes.Repeat([]byte("b"), 9001)}
	err = tcp.SendMessage(&m)
	if err != nil {
		t.Fatal("Failed to send message:", err)
	}
	// wait for handler to be triggered
	<-tmh.done
	if len(tmh.result) != 9001 {
		t.Fatal("Bad response: expected 9001 bytes, got", len(tmh.result))
	}
}
<|endoftext|>


<commit_before>package gentests

import (
	"fmt"
	"strings"

	"gopkg.in/yaml.v2"
)

var skipTests map[string][]string

func init() {
	err := yaml.NewDecoder(strings.NewReader(skipTestsYAML)).Decode(&skipTests)
	if err != nil {
		panic(fmt.Sprintf("ERROR: %v", err))
	}
}

var skipFiles = []string{
	"update/85_fields_meta.yml",            // Uses non-existing API property
	"update/86_fields_meta_with_types.yml", // --||--

	"ml/jobs_get_result_buckets.yml",    // Passes string value to int variable
	"ml/jobs_get_result_categories.yml", // --||--
	"ml/set_upgrade_mode.yml",           // --||--

	"ml/evaluate_data_frame.yml", // Floats as map keys

	"watcher/stats/10_basic.yml", // Sets "emit_stacktraces" as string ("true"), not bool
}

// TODO: Comments into descriptions for `Skip()`
//
var skipTestsYAML = `
---
# Cannot distinguish between missing value for refresh and an empty string
bulk/50_refresh.yml:
  - refresh=empty string immediately makes changes are visible in search
bulk/51_refresh_with_types.yml:
  - refresh=empty string immediately makes changes are visible in search
create/60_refresh.yml:
  - When refresh url parameter is an empty string that means "refresh immediately"
create/61_refresh_with_types.yml:
  - When refresh url parameter is an empty string that means "refresh immediately"
delete/50_refresh.yml:
  - When refresh url parameter is an empty string that means "refresh immediately"
delete/51_refresh_with_types.yml:
  - When refresh url parameter is an empty string that means "refresh immediately"
index/60_refresh.yml:
  - When refresh url parameter is an empty string that means "refresh immediately"
index/61_refresh_with_types.yml:
  - When refresh url parameter is an empty string that means "refresh immediately"
update/60_refresh.yml:
  - When refresh url parameter is an empty string that means "refresh immediately"
update/61_refresh_with_types.yml:
  - When refresh url parameter is an empty string that means "refresh immediately"

# Stash in value
cluster.reroute/11_explain.yml:
nodes.info/30_settings.yml:
nodes.stats/20_response_filtering.yml:
nodes.stats/30_discovery.yml:
  - Discovery stats
nodes.discovery/30_discovery.yml:
  - Discovery stats

# Arbitrary key
indices.shrink/10_basic.yml:
indices.shrink/20_source_mapping.yml:
indices.shrink/30_copy_settings.yml:
indices.split/30_copy_settings.yml:

# Parsed response is YAML: value is map[interface {}]interface {}, not map[string]interface {}
cat.aliases/20_headers.yml:
  - Simple alias with yaml body through Accept header

# Incorrect int instead of float in match (aggregations.date_range.buckets.0.from: 1000000); TODO: PR
search.aggregation/40_range.yml:
  - Date range

# No support for headers per request yet
tasks.list/10_basic.yml:
  - tasks_list headers

# Not relevant
search/issue4895.yml:
search/issue9606.yml:

# FIXME
bulk/80_cas.yml:
bulk/81_cas_with_types.yml:

# ----- X-Pack ----------------------------------------------------------------

# Stash in body
api_key/10_basic.yml:
  - Test invalidate api key

# Changing password locks out tests
change_password/10_basic.yml:
  - Test user changing their own password

# Missing refreshes in the test
data_frame/transforms_start_stop.yml:
ml/index_layout.yml:

# More QA tests than API tests
data_frame/transforms_stats.yml:
  - Test get multiple transform stats
  - Test get transform stats on missing transform
  - Test get multiple transform stats where one does not have a task

# Invalid license makes subsequent tests fail
license/20_put_license.yml:

# Test tries to match on map from body, but Go keys are not sorted
ml/jobs_crud.yml:
  - Test job with rules

# Test gets stuck every time
ml/jobs_get_stats.yml:

# # status_exception, Cannot process data because job [post-data-job] does not have a corresponding autodetect process
# # resource_already_exists_exception, task with id {job-post-data-job} already exist
# ml/post_data.yml:

# Possible bad test setup, Cannot open job [start-stop-datafeed-job] because it has already been opened
# resource_already_exists_exception, task with id {job-start-stop-datafeed-job-foo-2} already exist
ml/start_stop_datafeed.yml:
  - Test start datafeed when persistent task allocation disabled

# Indexing step doesn't appear to work (getting total.hits=0)
monitoring/bulk/10_basic.yml:
  - Bulk indexing of monitoring data on closed indices should throw an export exception
# Indexing step doesn't appear to work (getting total.hits=0)
monitoring/bulk/20_privileges.yml:
  - Monitoring Bulk API

# Test tries to match on whole body, but map keys are unstable in Go
rollup/security_tests.yml:

# TEMPORARY: Missing 'body: { indices: "test_index" }' payload, TODO: PR
snapshot/10_basic.yml:
  - Create a source only snapshot and then restore it

# illegal_argument_exception: Provided password hash uses [NOOP] but the configured hashing algorithm is [BCRYPT]
users/10_basic.yml:
  - Test put user with password hash

# Slash in index name is not escaped (BUG)
security/authz/13_index_datemath.yml:
  - Test indexing documents with datemath, when permitted

# Test looks for "testnode.crt", but "ca.crt" is returned first
ssl/10_basic.yml:
  - Test get SSL certificates

# class org.elasticsearch.xpack.vectors.query.VectorScriptDocValues$DenseVectorScriptDocValues cannot be cast to class org.elasticsearch.xpack.vectors.query.VectorScriptDocValues$SparseVectorScriptDocValues ...
vectors/30_sparse_vector_basic.yml:
  - Dot Product
# java.lang.IllegalArgumentException: No field found for [my_dense_vector] in mapping
vectors/40_sparse_vector_special_cases.yml:
  - Vectors of different dimensions and data types
  - Dimensions can be sorted differently
  - Distance functions for documents missing vector field should return 0

# Cannot connect to Docker IP
watcher/execute_watch/60_http_input.yml:

# Test tries to match on "tagline", which requires "human=false", which doesn't work in the Go API.
# Also test does too much within a single test, so has to be disabled as whole, unfortunately.
xpack/15_basic.yml:
`
<commit_msg>Generator: Tests: Update the list of skipped tests<commit_after>package gentests

import (
	"fmt"
	"strings"

	"gopkg.in/yaml.v2"
)

var skipTests map[string][]string

func init() {
	err := yaml.NewDecoder(strings.NewReader(skipTestsYAML)).Decode(&skipTests)
	if err != nil {
		panic(fmt.Sprintf("ERROR: %v", err))
	}
}

var skipFiles = []string{
	"update/85_fields_meta.yml",            // Uses non-existing API property
	"update/86_fields_meta_with_types.yml", // --||--

	"ml/jobs_get_result_buckets.yml",    // Passes string value to int variable
	"ml/jobs_get_result_categories.yml", // --||--
	"ml/set_upgrade_mode.yml",           // --||--

	"ml/evaluate_data_frame.yml", // Floats as map keys

	"watcher/stats/10_basic.yml", // Sets "emit_stacktraces" as string ("true"), not bool
}

// TODO: Comments into descriptions for `Skip()`
//
var skipTestsYAML = `
---
# Cannot distinguish between missing value for refresh and an empty string
bulk/50_refresh.yml:
  - refresh=empty string immediately makes changes are visible in search
bulk/51_refresh_with_types.yml:
  - refresh=empty string immediately makes changes are visible in search
create/60_refresh.yml:
  - When refresh url parameter is an empty string that means "refresh immediately"
create/61_refresh_with_types.yml:
  - When refresh url parameter is an empty string that means "refresh immediately"
delete/50_refresh.yml:
  - When refresh url parameter is an empty string that means "refresh immediately"
delete/51_refresh_with_types.yml:
  - When refresh url parameter is an empty string that means "refresh immediately"
index/60_refresh.yml:
  - When refresh url parameter is an empty string that means "refresh immediately"
index/61_refresh_with_types.yml:
  - When refresh url parameter is an empty string that means "refresh immediately"
update/60_refresh.yml:
  - When refresh url parameter is an empty string that means "refresh immediately"
update/61_refresh_with_types.yml:
  - When refresh url parameter is an empty string that means "refresh immediately"

# Stash in value
cluster.reroute/11_explain.yml:
nodes.info/30_settings.yml:
nodes.stats/20_response_filtering.yml:
nodes.stats/30_discovery.yml:
  - Discovery stats
nodes.discovery/30_discovery.yml:
  - Discovery stats

# Arbitrary key
indices.shrink/10_basic.yml:
indices.shrink/20_source_mapping.yml:
indices.shrink/30_copy_settings.yml:
indices.split/30_copy_settings.yml:

# Parsed response is YAML: value is map[interface {}]interface {}, not map[string]interface {}
cat.aliases/20_headers.yml:
  - Simple alias with yaml body through Accept header

# Incorrect int instead of float in match (aggregations.date_range.buckets.0.from: 1000000); TODO: PR
search.aggregation/40_range.yml:
  - Date range

# No support for headers per request yet
tasks.list/10_basic.yml:
  - tasks_list headers

# Not relevant
search/issue4895.yml:
search/issue9606.yml:

# FIXME
bulk/80_cas.yml:
bulk/81_cas_with_types.yml:

# ----- X-Pack ----------------------------------------------------------------

# Stash in body
api_key/10_basic.yml:
  - Test invalidate api key

# Changing password locks out tests
change_password/10_basic.yml:
  - Test user changing their own password

# Missing refreshes in the test
data_frame/transforms_start_stop.yml:
ml/index_layout.yml:

# More QA tests than API tests
data_frame/transforms_stats.yml:
  - Test get multiple transform stats
  - Test get transform stats on missing transform
  - Test get multiple transform stats where one does not have a task

# Invalid license makes subsequent tests fail
license/20_put_license.yml:

# Test tries to match on map from body, but Go keys are not sorted
ml/jobs_crud.yml:
  - Test job with rules

# Test gets stuck every time
ml/jobs_get_stats.yml:

# # status_exception, Cannot process data because job [post-data-job] does not have a corresponding autodetect process
# # resource_already_exists_exception, task with id {job-post-data-job} already exist
# ml/post_data.yml:

# Possible bad test setup, Cannot open job [start-stop-datafeed-job] because it has already been opened
# resource_already_exists_exception, task with id {job-start-stop-datafeed-job-foo-2} already exist
ml/start_stop_datafeed.yml:
  - Test start datafeed when persistent task allocation disabled

# Indexing step doesn't appear to work (getting total.hits=0)
monitoring/bulk/10_basic.yml:
  - Bulk indexing of monitoring data on closed indices should throw an export exception
# Indexing step doesn't appear to work (getting total.hits=0)
monitoring/bulk/20_privileges.yml:
  - Monitoring Bulk API

# Test tries to match on whole body, but map keys are unstable in Go
rollup/security_tests.yml:

# TEMPORARY: Missing 'body: { indices: "test_index" }' payload, TODO: PR
snapshot/10_basic.yml:
  - Create a source only snapshot and then restore it

# illegal_argument_exception: Provided password hash uses [NOOP] but the configured hashing algorithm is [BCRYPT]
users/10_basic.yml:
  - Test put user with password hash

# Slash in index name is not escaped (BUG)
security/authz/13_index_datemath.yml:
  - Test indexing documents with datemath, when permitted

# Possibly a cluster health color mismatch...
security/authz/14_cat_indices.yml:
  - Test empty request while single authorized index

# Test looks for "testnode.crt", but "ca.crt" is returned first
ssl/10_basic.yml:
  - Test get SSL certificates

# class org.elasticsearch.xpack.vectors.query.VectorScriptDocValues$DenseVectorScriptDocValues cannot be cast to class org.elasticsearch.xpack.vectors.query.VectorScriptDocValues$SparseVectorScriptDocValues ...
vectors/30_sparse_vector_basic.yml:
  - Dot Product
# java.lang.IllegalArgumentException: No field found for [my_dense_vector] in mapping
vectors/40_sparse_vector_special_cases.yml:
  - Vectors of different dimensions and data types
  - Dimensions can be sorted differently
  - Distance functions for documents missing vector field should return 0

# Cannot connect to Docker IP
watcher/execute_watch/60_http_input.yml:

# Test tries to match on "tagline", which requires "human=false", which doesn't work in the Go API.
# Also test does too much within a single test, so has to be disabled as whole, unfortunately.
xpack/15_basic.yml:
`
<|endoftext|>


<commit_before>package chromiumpreload

import (
	"bufio"
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"strings"
	"time"
	"unicode"
)

const (
	// ForceHTTPS indicates that all requests should be upgraded from HTTP to
	// HTTPS using the HSTS mechanism (https://tools.ietf.org/html/rfc6797).
	ForceHTTPS = "force-https"
)

// PreloadList contains a parsed form of the Chromium Preload list.
//
// The full list contains information about more than just HSTS, but only
// HSTS-related contents are currently exposed in this struct.
type PreloadList struct {
	Entries []PreloadEntry `json:"entries"`
}

// A Domain in the Chromium preload list. Note that this corresponds
// to "host" in the HSTS spec, and does not contain the scheme or port.
// We call it a "domain" because this makes the meaning more clear to
// web developers, and naturally allows us to refer to domains vs.
// subdomains.
type Domain string

// A PreloadEntry contains the data from an entry in the Chromium
// Preload list.
//
// - Name: The domain name.
//
// - Mode: The only valid non-empty value is ForceHTTPS
//
// - IncludeSubDomains: If Mode == ForceHTTPS, forces HSTS to apply to
//   all subdomains.
type PreloadEntry struct {
	Name              Domain `json:"name"`
	Mode              string `json:"mode"`
	IncludeSubDomains bool   `json:"include_subdomains"`
}

// IndexedPreloadEntries is case-insensitive index of
// the entries from the given PreloadList.
type IndexedPreloadEntries struct {
	index map[string]PreloadEntry
}

// Index creates an index out of the given list.
func (p PreloadList) Index() (idx IndexedPreloadEntries) {
	m := make(map[string]PreloadEntry)
	for _, entry := range p.Entries {
		d := strings.ToLower(string(entry.Name))
		m[d] = entry
	}
	return IndexedPreloadEntries{
		index: m,
	}
}

// Get acts similar to map access: it returns an entry from the index preload
// list (if it is present), along with a boolean indicating if the entry is
// present.
func (idx IndexedPreloadEntries) Get(domain string) (PreloadEntry, bool) {
	entry, ok := idx.index[strings.ToLower(domain)]
	return entry, ok
}

const (
	latestChromiumListURL = "https://chromium.googlesource.com/chromium/src/+/master/net/http/transport_security_state_static.json?format=TEXT"
)

// GetLatest retrieves the latest PreloadList from the Chromium source at
// https://chromium.googlesource.com/chromium/src/+/master/net/http/transport_security_state_static.json
//
// Note that this list may be up to 12 weeks fresher than the list used
// by the current stable version of Chrome. See
// https://www.chromium.org/developers/calendar for a calendar of releases.
func GetLatest() (PreloadList, error) {
	var list PreloadList

	client := http.Client{
		Timeout: time.Second * 10,
	}

	resp, err := client.Get(latestChromiumListURL)
	if err != nil {
		return list, err
	}

	if resp.StatusCode != 200 {
		return list, fmt.Errorf("status code %d", resp.StatusCode)
	}

	body := base64.NewDecoder(base64.StdEncoding, resp.Body)
	jsonBytes, err := removeComments(body)
	if err != nil {
		return list, errors.New("could not decode body")
	}

	if err := json.Unmarshal(jsonBytes, &list); err != nil {
		return list, err
	}

	return list, nil
}

// removeComments reads the contents of |r| and removes any lines beginning
// with optional whitespace followed by "//"
func removeComments(r io.Reader) ([]byte, error) {
	var buf bytes.Buffer

	sc := bufio.NewScanner(r)
	for sc.Scan() {
		line := sc.Text()
		if isCommentLine(line) {
			fmt.Fprintln(&buf, line)
		}
	}
	if err := sc.Err(); err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

func isCommentLine(line string) bool {
	trimmed := strings.TrimLeftFunc(line, unicode.IsSpace)
	return !strings.HasPrefix(trimmed, "//")
}
<commit_msg>Remove the Domain typedef and use a string directly.<commit_after>package chromiumpreload

import (
	"bufio"
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"strings"
	"time"
	"unicode"
)

const (
	// ForceHTTPS indicates that all requests should be upgraded from HTTP to
	// HTTPS using the HSTS mechanism (https://tools.ietf.org/html/rfc6797).
	ForceHTTPS = "force-https"
)

// PreloadList contains a parsed form of the Chromium Preload list.
//
// The full list contains information about more than just HSTS, but only
// HSTS-related contents are currently exposed in this struct.
type PreloadList struct {
	Entries []PreloadEntry `json:"entries"`
}

// A PreloadEntry contains the data from an entry in the Chromium
// Preload list.
//
// - Name: The domain name.
//
// - Mode: The only valid non-empty value is ForceHTTPS
//
// - IncludeSubDomains: If Mode == ForceHTTPS, forces HSTS to apply to
//   all subdomains.
type PreloadEntry struct {
	Name              string `json:"name"`
	Mode              string `json:"mode"`
	IncludeSubDomains bool   `json:"include_subdomains"`
}

// IndexedPreloadEntries is case-insensitive index of
// the entries from the given PreloadList.
type IndexedPreloadEntries struct {
	index map[string]PreloadEntry
}

// Index creates an index out of the given list.
func (p PreloadList) Index() (idx IndexedPreloadEntries) {
	m := make(map[string]PreloadEntry)
	for _, entry := range p.Entries {
		d := strings.ToLower(string(entry.Name))
		m[d] = entry
	}
	return IndexedPreloadEntries{
		index: m,
	}
}

// Get acts similar to map access: it returns an entry from the index preload
// list (if it is present), along with a boolean indicating if the entry is
// present.
func (idx IndexedPreloadEntries) Get(domain string) (PreloadEntry, bool) {
	entry, ok := idx.index[strings.ToLower(domain)]
	return entry, ok
}

const (
	latestChromiumListURL = "https://chromium.googlesource.com/chromium/src/+/master/net/http/transport_security_state_static.json?format=TEXT"
)

// GetLatest retrieves the latest PreloadList from the Chromium source at
// https://chromium.googlesource.com/chromium/src/+/master/net/http/transport_security_state_static.json
//
// Note that this list may be up to 12 weeks fresher than the list used
// by the current stable version of Chrome. See
// https://www.chromium.org/developers/calendar for a calendar of releases.
func GetLatest() (PreloadList, error) {
	var list PreloadList

	client := http.Client{
		Timeout: time.Second * 10,
	}

	resp, err := client.Get(latestChromiumListURL)
	if err != nil {
		return list, err
	}

	if resp.StatusCode != 200 {
		return list, fmt.Errorf("status code %d", resp.StatusCode)
	}

	body := base64.NewDecoder(base64.StdEncoding, resp.Body)
	jsonBytes, err := removeComments(body)
	if err != nil {
		return list, errors.New("could not decode body")
	}

	if err := json.Unmarshal(jsonBytes, &list); err != nil {
		return list, err
	}

	return list, nil
}

// removeComments reads the contents of |r| and removes any lines beginning
// with optional whitespace followed by "//"
func removeComments(r io.Reader) ([]byte, error) {
	var buf bytes.Buffer

	sc := bufio.NewScanner(r)
	for sc.Scan() {
		line := sc.Text()
		if isCommentLine(line) {
			fmt.Fprintln(&buf, line)
		}
	}
	if err := sc.Err(); err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

func isCommentLine(line string) bool {
	trimmed := strings.TrimLeftFunc(line, unicode.IsSpace)
	return !strings.HasPrefix(trimmed, "//")
}
<|endoftext|>


<commit_before>// Copyright © 2016 Aaron Longwell
//
// Use of this source code is governed by an MIT licese.
// Details in the LICENSE file.

package trello

import (
	"fmt"
	"time"
)

type Board struct {
	client         *Client
	ID             string `json:"id"`
	Name           string `json:"name"`
	Desc           string `json:"desc"`
	Closed         bool   `json:"closed"`
	IdOrganization string `json:"idOrganization"`
	Pinned         bool   `json:"pinned"`
	Url            string `json:"url"`
	ShortUrl       string `json:"shortUrl"`
	Prefs          struct {
		PermissionLevel       string            `json:"permissionLevel"`
		Voting                string            `json:"voting"`
		Comments              string            `json:"comments"`
		Invitations           string            `json:"invitations"`
		SelfJoin              bool              `json:"selfjoin"`
		CardCovers            bool              `json:"cardCovers"`
		CardAging             string            `json:"cardAging"`
		CalendarFeedEnabled   bool              `json:"calendarFeedEnabled"`
		Background            string            `json:"background"`
		BackgroundColor       string            `json:"backgroundColor"`
		BackgroundImage       string            `json:"backgroundImage"`
		BackgroundImageScaled []BackgroundImage `json:"backgroundImageScaled"`
		BackgroundTile        bool              `json:"backgroundTile"`
		BackgroundBrightness  string            `json:"backgroundBrightness"`
		CanBePublic           bool              `json:"canBePublic"`
		CanBeOrg              bool              `json:"canBeOrg"`
		CanBePrivate          bool              `json:"canBePrivate"`
		CanInvite             bool              `json:"canInvite"`
	} `json:"prefs"`
	LabelNames struct {
		Black  string `json:"black,omitempty"`
		Blue   string `json:"blue,omitempty"`
		Green  string `json:"green,omitempty"`
		Lime   string `json:"lime,omitempty"`
		Orange string `json:"orange,omitempty"`
		Pink   string `json:"pink,omitempty"`
		Purple string `json:"purple,omitempty"`
		Red    string `json:"red,omitempty"`
		Sky    string `json:"sky,omitempty"`
		Yellow string `json:"yellow,omitempty"`
	} `json:"labelNames"`
	Lists   []*List   `json:"lists"`
	Actions []*Action `json:"actions"`
}

type BackgroundImage struct {
	Width  int    `json:"width"`
	Height int    `json:"height"`
	URL    string `json:"url"`
}

func (b *Board) CreatedAt() time.Time {
	t, _ := IDToTime(b.ID)
	return t
}

/**
 * Board retrieves a Trello board by its ID.
 */
func (c *Client) GetBoard(boardID string, args Arguments) (board *Board, err error) {
	path := fmt.Sprintf("boards/%s", boardID)
	err = c.Get(path, args, &board)
	if board != nil {
		board.client = c
	}
	return
}

func (m *Member) GetBoards(args Arguments) (boards []*Board, err error) {
	path := fmt.Sprintf("members/%s/boards", m.ID)
	err = m.client.Get(path, args, &boards)
	for i := range boards {
		boards[i].client = m.client
	}
	return
}
<commit_msg>board: added organization to board struct<commit_after>// Copyright © 2016 Aaron Longwell
//
// Use of this source code is governed by an MIT licese.
// Details in the LICENSE file.

package trello

import (
	"fmt"
	"time"
)

type Board struct {
	client         *Client
	ID             string `json:"id"`
	Name           string `json:"name"`
	Desc           string `json:"desc"`
	Closed         bool   `json:"closed"`
	IdOrganization string `json:"idOrganization"`
	Pinned         bool   `json:"pinned"`
	Url            string `json:"url"`
	ShortUrl       string `json:"shortUrl"`
	Prefs          struct {
		PermissionLevel       string            `json:"permissionLevel"`
		Voting                string            `json:"voting"`
		Comments              string            `json:"comments"`
		Invitations           string            `json:"invitations"`
		SelfJoin              bool              `json:"selfjoin"`
		CardCovers            bool              `json:"cardCovers"`
		CardAging             string            `json:"cardAging"`
		CalendarFeedEnabled   bool              `json:"calendarFeedEnabled"`
		Background            string            `json:"background"`
		BackgroundColor       string            `json:"backgroundColor"`
		BackgroundImage       string            `json:"backgroundImage"`
		BackgroundImageScaled []BackgroundImage `json:"backgroundImageScaled"`
		BackgroundTile        bool              `json:"backgroundTile"`
		BackgroundBrightness  string            `json:"backgroundBrightness"`
		CanBePublic           bool              `json:"canBePublic"`
		CanBeOrg              bool              `json:"canBeOrg"`
		CanBePrivate          bool              `json:"canBePrivate"`
		CanInvite             bool              `json:"canInvite"`
	} `json:"prefs"`
	LabelNames struct {
		Black  string `json:"black,omitempty"`
		Blue   string `json:"blue,omitempty"`
		Green  string `json:"green,omitempty"`
		Lime   string `json:"lime,omitempty"`
		Orange string `json:"orange,omitempty"`
		Pink   string `json:"pink,omitempty"`
		Purple string `json:"purple,omitempty"`
		Red    string `json:"red,omitempty"`
		Sky    string `json:"sky,omitempty"`
		Yellow string `json:"yellow,omitempty"`
	} `json:"labelNames"`
	Lists   []*List   `json:"lists"`
	Actions []*Action `json:"actions"`
    Organization Organization `json:"organization"`

}

type BackgroundImage struct {
	Width  int    `json:"width"`
	Height int    `json:"height"`
	URL    string `json:"url"`
}

func (b *Board) CreatedAt() time.Time {
	t, _ := IDToTime(b.ID)
	return t
}

/**
 * Board retrieves a Trello board by its ID.
 */
func (c *Client) GetBoard(boardID string, args Arguments) (board *Board, err error) {
	path := fmt.Sprintf("boards/%s", boardID)
	err = c.Get(path, args, &board)
	if board != nil {
		board.client = c
	}
	return
}

func (m *Member) GetBoards(args Arguments) (boards []*Board, err error) {
	path := fmt.Sprintf("members/%s/boards", m.ID)
	err = m.client.Get(path, args, &boards)
	for i := range boards {
		boards[i].client = m.client
	}
	return
}
<|endoftext|>