<commit_before>package proto

import (
	"fmt"
	"time"

	"h12.me/kpax/model"
)

type client struct {
	id   string
	doer model.Broker
}

func (c client) Do(req RequestMessage, resp ResponseMessage) error {
	return c.doer.Do(
		&Request{
			ClientID:       c.id,
			RequestMessage: req,
		},
		&Response{
			ResponseMessage: resp,
		},
	)
}

const clientID = "h12.me/kpax"

type Metadata string

func (m Metadata) Fetch(b model.Broker) (*TopicMetadataResponse, error) {
	topic := string(m)
	req := TopicMetadataRequest([]string{topic})
	resp := TopicMetadataResponse{}
	if err := (client{clientID, b}).Do(&req, &resp); err != nil {
		return nil, err
	}
	for i := range resp.TopicMetadatas {
		t := &resp.TopicMetadatas[i]
		if t.TopicName == topic {
			if t.HasError() {
				return nil, t.ErrorCode
			}
			for i := range t.PartitionMetadatas {
				partition := &t.PartitionMetadatas[i]
				if partition.HasError() {
					return nil, partition.ErrorCode
				}
			}
		}
	}
	return &resp, nil
}

type GroupCoordinator string

func (group GroupCoordinator) Fetch(b model.Broker) (*Broker, error) {
	req := GroupCoordinatorRequest(group)
	resp := GroupCoordinatorResponse{}
	if err := (client{clientID, b}).Do(&req, &resp); err != nil {
		return nil, err
	}
	if resp.HasError() {
		return nil, resp.ErrorCode
	}
	return &resp.Broker, nil
}

type Payload struct {
	Topic        string
	Partition    int32
	MessageSet   MessageSet
	RequiredAcks ProduceAckType
	AckTimeout   time.Duration
}

func (p *Payload) Produce(c model.Cluster) error {
	leader, err := c.Leader(p.Topic, p.Partition)
	if err != nil {
		return err
	}
	if err := p.DoProduce(leader); err != nil {
		if IsNotLeader(err) {
			c.LeaderIsDown(p.Topic, p.Partition)
		}
		return err
	}
	return nil
}

func (p *Payload) DoProduce(b model.Broker) error {
	req := ProduceRequest{
		RequiredAcks: int16(p.RequiredAcks),
		Timeout:      int32(p.AckTimeout / time.Millisecond),
		MessageSetInTopics: []MessageSetInTopic{
			{
				TopicName: p.Topic,
				MessageSetInPartitions: []MessageSetInPartition{
					{
						Partition:  p.Partition,
						MessageSet: p.MessageSet,
					},
				},
			},
		},
	}

	if p.RequiredAcks == AckNone {
		return (client{clientID, b}).Do(&req, nil)
	}

	resp := ProduceResponse{}
	if err := (client{clientID, b}).Do(&req, &resp); err != nil {
		return err
	}
	for i := range resp {
		t := &resp[i]
		if t.TopicName != p.Topic {
			continue
		}
		for j := range t.OffsetInPartitions {
			pres := &t.OffsetInPartitions[j]
			if pres.Partition != p.Partition {
				continue
			}
			if pres.HasError() {
				return pres.ErrorCode
			}
			return nil
		}
	}
	return fmt.Errorf("fail to produce to %s, %d", p.Topic, p.Partition)
}

type Messages struct {
	Topic       string
	Partition   int32
	Offset      int64
	MinBytes    int
	MaxBytes    int
	MaxWaitTime time.Duration
}

func (m *Messages) Consume(c model.Cluster) (MessageSet, error) {
	leader, err := c.Leader(m.Topic, m.Partition)
	if err != nil {
		return nil, err
	}
	ms, err := m.DoConsume(leader)
	if err != nil {
		if IsNotLeader(err) {
			c.LeaderIsDown(m.Topic, m.Partition)
		}
		return nil, err
	}
	return ms, nil
}

func (fr *Messages) DoConsume(c model.Broker) (messages MessageSet, err error) {
	req := FetchRequest{
		ReplicaID:   -1,
		MaxWaitTime: int32(fr.MaxWaitTime / time.Millisecond),
		MinBytes:    int32(fr.MinBytes),
		FetchOffsetInTopics: []FetchOffsetInTopic{
			{
				TopicName: fr.Topic,
				FetchOffsetInPartitions: []FetchOffsetInPartition{
					{
						Partition:   fr.Partition,
						FetchOffset: fr.Offset,
						MaxBytes:    int32(fr.MaxBytes),
					},
				},
			},
		},
	}
	resp := FetchResponse{}
	if err := (client{clientID, c}).Do(&req, &resp); err != nil {
		return nil, err
	}
	for i := range resp {
		t := &resp[i]
		if t.TopicName != fr.Topic {
			continue
		}
		for j := range t.FetchMessageSetInPartitions {
			p := &t.FetchMessageSetInPartitions[j]
			if p.Partition != fr.Partition {
				continue
			}
			if p.HasError() {
				return nil, p.ErrorCode
			}
			ms := p.MessageSet
			ms, err := ms.Flatten()
			if err != nil {
				return nil, err
			}
			for k := range ms {
				m := &ms[k]
				if m.Offset == fr.Offset {
					ms = ms[k:]
					break
				}
			}
			if len(ms) == 0 {
				continue
			}
			if ms[0].Offset != fr.Offset {
				return nil, fmt.Errorf("2: OFFSET MISMATCH %d %d", ms[0].Offset, fr.Offset)
			}
			return ms, nil
		}
	}
	return nil, nil
}

type Offset struct {
	Topic     string
	Partition int32
	Group     string
	Offset    int64
	Retention time.Duration
}

func (o *Offset) Commit(c model.Cluster) error {
	coord, err := c.Coordinator(o.Group)
	if err != nil {
		return err
	}
	if err := o.DoCommit(coord); err != nil {
		if IsNotCoordinator(err) {
			c.CoordinatorIsDown(o.Group)
		}
		return err
	}
	return nil
}

func (commit *Offset) DoCommit(b model.Broker) error {
	req := OffsetCommitRequestV1{
		ConsumerGroupID: commit.Group,
		OffsetCommitInTopicV1s: []OffsetCommitInTopicV1{
			{
				TopicName: commit.Topic,
				OffsetCommitInPartitionV1s: []OffsetCommitInPartitionV1{
					{
						Partition: commit.Partition,
						Offset:    commit.Offset,
						// TimeStamp in milliseconds
						TimeStamp: time.Now().Add(commit.Retention).Unix() * 1000,
					},
				},
			},
		},
	}
	resp := OffsetCommitResponse{}
	if err := (client{clientID, b}).Do(&req, &resp); err != nil {
		return err
	}
	for i := range resp {
		t := &resp[i]
		if t.TopicName == commit.Topic {
			for j := range t.ErrorInPartitions {
				p := &t.ErrorInPartitions[j]
				if p.Partition == commit.Partition {
					if p.HasError() {
						return p.ErrorCode
					}
					return nil
				}
			}
		}
	}
	return fmt.Errorf("fail to commit offset: %v", commit)
}

func (o *Offset) Fetch(c model.Cluster) (int64, error) {
	coord, err := c.Coordinator(o.Group)
	if err != nil {
		return -1, err
	}
	offset, err := o.DoFetch(coord)
	if err != nil {
		if IsNotCoordinator(err) {
			c.CoordinatorIsDown(o.Group)
		}
		return -1, err
	}
	return offset, nil
}

func (o *Offset) DoFetch(b model.Broker) (int64, error) {
	req := OffsetFetchRequestV1{
		ConsumerGroup: o.Group,
		PartitionInTopics: []PartitionInTopic{
			{
				TopicName:  o.Topic,
				Partitions: []int32{o.Partition},
			},
		},
	}
	resp := OffsetFetchResponse{}
	if err := (client{clientID, b}).Do(&req, &resp); err != nil {
		return -1, err
	}
	for i := range resp {
		t := &resp[i]
		if t.TopicName == o.Topic {
			for j := range resp[i].OffsetMetadataInPartitions {
				p := &t.OffsetMetadataInPartitions[j]
				if p.HasError() {
					return -1, fmt.Errorf("fail to get offset for (%s, %d): %v", o.Topic, o.Partition, p.ErrorCode)
				}
				return p.Offset, nil
			}
		}
	}

	return -1, fmt.Errorf("fail to get offset for (%s, %d)", o.Topic, o.Partition)
}

type OffsetByTime struct {
	Topic     string
	Partition int32
	Time      time.Time
}

func (o *OffsetByTime) Fetch(c model.Cluster) (int64, error) {
	leader, err := c.Leader(o.Topic, o.Partition)
	if err != nil {
		return -1, err
	}
	offset, err := o.DoFetch(leader)
	if err != nil {
		if IsNotLeader(err) {
			c.LeaderIsDown(o.Topic, o.Partition)
		}
		return -1, err
	}
	return offset, nil
}

func (o *OffsetByTime) DoFetch(b model.Broker) (int64, error) {
	var milliSec int64
	switch o.Time {
	case Latest:
		milliSec = -1
	case Earliest:
		milliSec = -2
	default:
		milliSec = o.Time.UnixNano() / 1000000
	}
	req := OffsetRequest{
		ReplicaID: -1,
		TimeInTopics: []TimeInTopic{
			{
				TopicName: o.Topic,
				TimeInPartitions: []TimeInPartition{
					{
						Partition:          o.Partition,
						Time:               milliSec,
						MaxNumberOfOffsets: 1,
					},
				},
			},
		},
	}
	resp := OffsetResponse{}
	if err := (client{clientID, b}).Do(&req, &resp); err != nil {
		return -1, err
	}
	for _, t := range resp {
		if t.TopicName != o.Topic {
			continue
		}
		for _, p := range t.OffsetsInPartitions {
			if p.Partition != o.Partition {
				continue
			}
			if p.HasError() {
				return -1, p.ErrorCode
			}
			if len(p.Offsets) == 0 {
				return -1, fmt.Errorf("failt to fetch offset for %s, %d", o.Topic, o.Partition)
			}
			return p.Offsets[0], nil
		}
	}
	return -1, fmt.Errorf("failt to fetch offset for %s, %d", o.Topic, o.Partition)
}
<commit_msg>close the broker when it is considered down<commit_after>package proto

import (
	"fmt"
	"time"

	"h12.me/kpax/model"
)

type client struct {
	id   string
	doer model.Broker
}

func (c client) Do(req RequestMessage, resp ResponseMessage) error {
	return c.doer.Do(
		&Request{
			ClientID:       c.id,
			RequestMessage: req,
		},
		&Response{
			ResponseMessage: resp,
		},
	)
}

const clientID = "h12.me/kpax"

type Metadata string

func (m Metadata) Fetch(b model.Broker) (*TopicMetadataResponse, error) {
	topic := string(m)
	req := TopicMetadataRequest([]string{topic})
	resp := TopicMetadataResponse{}
	if err := (client{clientID, b}).Do(&req, &resp); err != nil {
		return nil, err
	}
	for i := range resp.TopicMetadatas {
		t := &resp.TopicMetadatas[i]
		if t.TopicName == topic {
			if t.HasError() {
				return nil, t.ErrorCode
			}
			for i := range t.PartitionMetadatas {
				partition := &t.PartitionMetadatas[i]
				if partition.HasError() {
					return nil, partition.ErrorCode
				}
			}
		}
	}
	return &resp, nil
}

type GroupCoordinator string

func (group GroupCoordinator) Fetch(b model.Broker) (*Broker, error) {
	req := GroupCoordinatorRequest(group)
	resp := GroupCoordinatorResponse{}
	if err := (client{clientID, b}).Do(&req, &resp); err != nil {
		return nil, err
	}
	if resp.HasError() {
		return nil, resp.ErrorCode
	}
	return &resp.Broker, nil
}

type Payload struct {
	Topic        string
	Partition    int32
	MessageSet   MessageSet
	RequiredAcks ProduceAckType
	AckTimeout   time.Duration
}

func (p *Payload) Produce(c model.Cluster) error {
	leader, err := c.Leader(p.Topic, p.Partition)
	if err != nil {
		return err
	}
	if err := p.DoProduce(leader); err != nil {
		if IsNotLeader(err) {
			leader.Close()
			c.LeaderIsDown(p.Topic, p.Partition)
		}
		return err
	}
	return nil
}

func (p *Payload) DoProduce(b model.Broker) error {
	req := ProduceRequest{
		RequiredAcks: int16(p.RequiredAcks),
		Timeout:      int32(p.AckTimeout / time.Millisecond),
		MessageSetInTopics: []MessageSetInTopic{
			{
				TopicName: p.Topic,
				MessageSetInPartitions: []MessageSetInPartition{
					{
						Partition:  p.Partition,
						MessageSet: p.MessageSet,
					},
				},
			},
		},
	}

	if p.RequiredAcks == AckNone {
		return (client{clientID, b}).Do(&req, nil)
	}

	resp := ProduceResponse{}
	if err := (client{clientID, b}).Do(&req, &resp); err != nil {
		return err
	}
	for i := range resp {
		t := &resp[i]
		if t.TopicName != p.Topic {
			continue
		}
		for j := range t.OffsetInPartitions {
			pres := &t.OffsetInPartitions[j]
			if pres.Partition != p.Partition {
				continue
			}
			if pres.HasError() {
				return pres.ErrorCode
			}
			return nil
		}
	}
	return fmt.Errorf("fail to produce to %s, %d", p.Topic, p.Partition)
}

type Messages struct {
	Topic       string
	Partition   int32
	Offset      int64
	MinBytes    int
	MaxBytes    int
	MaxWaitTime time.Duration
}

func (m *Messages) Consume(c model.Cluster) (MessageSet, error) {
	leader, err := c.Leader(m.Topic, m.Partition)
	if err != nil {
		return nil, err
	}
	ms, err := m.DoConsume(leader)
	if err != nil {
		if IsNotLeader(err) {
			leader.Close()
			c.LeaderIsDown(m.Topic, m.Partition)
		}
		return nil, err
	}
	return ms, nil
}

func (fr *Messages) DoConsume(c model.Broker) (messages MessageSet, err error) {
	req := FetchRequest{
		ReplicaID:   -1,
		MaxWaitTime: int32(fr.MaxWaitTime / time.Millisecond),
		MinBytes:    int32(fr.MinBytes),
		FetchOffsetInTopics: []FetchOffsetInTopic{
			{
				TopicName: fr.Topic,
				FetchOffsetInPartitions: []FetchOffsetInPartition{
					{
						Partition:   fr.Partition,
						FetchOffset: fr.Offset,
						MaxBytes:    int32(fr.MaxBytes),
					},
				},
			},
		},
	}
	resp := FetchResponse{}
	if err := (client{clientID, c}).Do(&req, &resp); err != nil {
		return nil, err
	}
	for i := range resp {
		t := &resp[i]
		if t.TopicName != fr.Topic {
			continue
		}
		for j := range t.FetchMessageSetInPartitions {
			p := &t.FetchMessageSetInPartitions[j]
			if p.Partition != fr.Partition {
				continue
			}
			if p.HasError() {
				return nil, p.ErrorCode
			}
			ms := p.MessageSet
			ms, err := ms.Flatten()
			if err != nil {
				return nil, err
			}
			for k := range ms {
				m := &ms[k]
				if m.Offset == fr.Offset {
					ms = ms[k:]
					break
				}
			}
			if len(ms) == 0 {
				continue
			}
			if ms[0].Offset != fr.Offset {
				return nil, fmt.Errorf("2: OFFSET MISMATCH %d %d", ms[0].Offset, fr.Offset)
			}
			return ms, nil
		}
	}
	return nil, nil
}

type Offset struct {
	Topic     string
	Partition int32
	Group     string
	Offset    int64
	Retention time.Duration
}

func (o *Offset) Commit(c model.Cluster) error {
	coord, err := c.Coordinator(o.Group)
	if err != nil {
		return err
	}
	if err := o.DoCommit(coord); err != nil {
		if IsNotCoordinator(err) {
			coord.Close()
			c.CoordinatorIsDown(o.Group)
		}
		return err
	}
	return nil
}

func (commit *Offset) DoCommit(b model.Broker) error {
	req := OffsetCommitRequestV1{
		ConsumerGroupID: commit.Group,
		OffsetCommitInTopicV1s: []OffsetCommitInTopicV1{
			{
				TopicName: commit.Topic,
				OffsetCommitInPartitionV1s: []OffsetCommitInPartitionV1{
					{
						Partition: commit.Partition,
						Offset:    commit.Offset,
						// TimeStamp in milliseconds
						TimeStamp: time.Now().Add(commit.Retention).Unix() * 1000,
					},
				},
			},
		},
	}
	resp := OffsetCommitResponse{}
	if err := (client{clientID, b}).Do(&req, &resp); err != nil {
		return err
	}
	for i := range resp {
		t := &resp[i]
		if t.TopicName == commit.Topic {
			for j := range t.ErrorInPartitions {
				p := &t.ErrorInPartitions[j]
				if p.Partition == commit.Partition {
					if p.HasError() {
						return p.ErrorCode
					}
					return nil
				}
			}
		}
	}
	return fmt.Errorf("fail to commit offset: %v", commit)
}

func (o *Offset) Fetch(c model.Cluster) (int64, error) {
	coord, err := c.Coordinator(o.Group)
	if err != nil {
		return -1, err
	}
	offset, err := o.DoFetch(coord)
	if err != nil {
		if IsNotCoordinator(err) {
			coord.Close()
			c.CoordinatorIsDown(o.Group)
		}
		return -1, err
	}
	return offset, nil
}

func (o *Offset) DoFetch(b model.Broker) (int64, error) {
	req := OffsetFetchRequestV1{
		ConsumerGroup: o.Group,
		PartitionInTopics: []PartitionInTopic{
			{
				TopicName:  o.Topic,
				Partitions: []int32{o.Partition},
			},
		},
	}
	resp := OffsetFetchResponse{}
	if err := (client{clientID, b}).Do(&req, &resp); err != nil {
		return -1, err
	}
	for i := range resp {
		t := &resp[i]
		if t.TopicName == o.Topic {
			for j := range resp[i].OffsetMetadataInPartitions {
				p := &t.OffsetMetadataInPartitions[j]
				if p.HasError() {
					return -1, fmt.Errorf("fail to get offset for (%s, %d): %v", o.Topic, o.Partition, p.ErrorCode)
				}
				return p.Offset, nil
			}
		}
	}

	return -1, fmt.Errorf("fail to get offset for (%s, %d)", o.Topic, o.Partition)
}

type OffsetByTime struct {
	Topic     string
	Partition int32
	Time      time.Time
}

func (o *OffsetByTime) Fetch(c model.Cluster) (int64, error) {
	leader, err := c.Leader(o.Topic, o.Partition)
	if err != nil {
		return -1, err
	}
	offset, err := o.DoFetch(leader)
	if err != nil {
		if IsNotLeader(err) {
			leader.Close()
			c.LeaderIsDown(o.Topic, o.Partition)
		}
		return -1, err
	}
	return offset, nil
}

func (o *OffsetByTime) DoFetch(b model.Broker) (int64, error) {
	var milliSec int64
	switch o.Time {
	case Latest:
		milliSec = -1
	case Earliest:
		milliSec = -2
	default:
		milliSec = o.Time.UnixNano() / 1000000
	}
	req := OffsetRequest{
		ReplicaID: -1,
		TimeInTopics: []TimeInTopic{
			{
				TopicName: o.Topic,
				TimeInPartitions: []TimeInPartition{
					{
						Partition:          o.Partition,
						Time:               milliSec,
						MaxNumberOfOffsets: 1,
					},
				},
			},
		},
	}
	resp := OffsetResponse{}
	if err := (client{clientID, b}).Do(&req, &resp); err != nil {
		return -1, err
	}
	for _, t := range resp {
		if t.TopicName != o.Topic {
			continue
		}
		for _, p := range t.OffsetsInPartitions {
			if p.Partition != o.Partition {
				continue
			}
			if p.HasError() {
				return -1, p.ErrorCode
			}
			if len(p.Offsets) == 0 {
				return -1, fmt.Errorf("failt to fetch offset for %s, %d", o.Topic, o.Partition)
			}
			return p.Offsets[0], nil
		}
	}
	return -1, fmt.Errorf("failt to fetch offset for %s, %d", o.Topic, o.Partition)
}
<|endoftext|>


<commit_before>package data

import (
	"encoding/csv"
	"time"
	"fmt"
	"strconv"
	"io"
)

func CSVParse(file io.Reader) (labels []string, data []Record) {
	labels, data = csvParse(file)
	return
}

func csvParse(file io.Reader) (labels []string, data []Record) {
	reader := csv.NewReader (file)
	tmpdata, err := reader.ReadAll()
	if  err != nil {
		fmt.Println(err)
	}
	fmt.Println(len(tmpdata) - 1)
	labels = make([]string, 6)
	//labels = tmpdata[0]
	data  = make([]Record,  len(tmpdata)-1)
	for i := 1; i<len(tmpdata)-1; i++ {
		data[i-1].Time, _ = time.Parse(ISO, tmpdata[i][0])
		data[i-1].Radiation, err = strconv.ParseFloat(tmpdata[i][1], 64)
		if err != nil {
			data[i-1].empty = true
		}
		data[i-1].Humidity, err = strconv.ParseFloat(tmpdata[i][2], 64)
		if err != nil {
			data[i-1].empty = true
		}
		data[i-1].Temperature, err = strconv.ParseFloat(tmpdata[i][2], 64)
		if err != nil {
			data[i-1].empty = true
		}
		data[i-1].Wind, err = strconv.ParseFloat(tmpdata[i][2], 64)
		if err != nil {
			data[i-1].empty = true
		}
		data[i-1].Power, err = strconv.ParseFloat(tmpdata[i][2], 64)
		if err != nil {
			data[i-1].Null = true
		}
	}
	fmt.Println(len(data))
	data = fillRecords (data)
	return
}

func fillRecords (emptyData []Record) (data []Record){
	gradRad, gradHumidity, gradTemp, gradWind := 0.0, 0.0, 0.0, 0.0
	for i := 0; i<len(emptyData); i++ {
		if emptyData[i].empty && i > 0 {
			emptyData[i].Radiation = emptyData[i-1].Radiation + gradRad
			emptyData[i].Humidity = emptyData[i-1].Humidity + gradHumidity
			emptyData[i].Temperature = emptyData[i-1].Temperature + gradTemp
			emptyData[i].Wind = emptyData[i-1].Wind + gradWind
			emptyData[i].empty = false
		} else {
			if i + 4 < len (emptyData) {
				gradRad = (emptyData[i+4].Radiation - emptyData[i].Radiation)/4
				gradHumidity = (emptyData[i+4].Humidity - emptyData[i].Humidity)/4
				gradTemp = (emptyData[i+4].Temperature - emptyData[i].Temperature)/4
				gradWind = (emptyData[i+4].Wind - emptyData[i].Wind)/4
			} else {
				gradRad = 0
				gradHumidity = 0
				gradTemp = 0
				gradWind = 0
			}
		}
	}
	return
}
<commit_msg>more checks<commit_after>package data

import (
	"encoding/csv"
	"time"
	"fmt"
	"strconv"
	"io"
)

func CSVParse(file io.Reader) (labels []string, data []Record) {
	labels, data = csvParse(file)
	return
}

func csvParse(file io.Reader) (labels []string, data []Record) {
	reader := csv.NewReader (file)
	tmpdata, err := reader.ReadAll()
	if  err != nil {
		fmt.Println(err)
	}
	fmt.Println(len(tmpdata) - 1)
	labels = make([]string, 6)
	//labels = tmpdata[0]
	data  = make([]Record,  len(tmpdata)-1)
	for i := 1; i<len(tmpdata)-1; i++ {
		data[i-1].Time, _ = time.Parse(ISO, tmpdata[i][0])
		data[i-1].Radiation, err = strconv.ParseFloat(tmpdata[i][1], 64)
		if err != nil {
			data[i-1].empty = true
		}
		data[i-1].Humidity, err = strconv.ParseFloat(tmpdata[i][2], 64)
		if err != nil {
			data[i-1].empty = true
		}
		data[i-1].Temperature, err = strconv.ParseFloat(tmpdata[i][2], 64)
		if err != nil {
			data[i-1].empty = true
		}
		data[i-1].Wind, err = strconv.ParseFloat(tmpdata[i][2], 64)
		if err != nil {
			data[i-1].empty = true
		}
		data[i-1].Power, err = strconv.ParseFloat(tmpdata[i][2], 64)
		if err != nil {
			data[i-1].Null = true
		}
	}
	fmt.Println(len(data))
	data = fillRecords (data)
	fmt.Println(len(data))
	return
}

func fillRecords (emptyData []Record) (data []Record){
	gradRad, gradHumidity, gradTemp, gradWind := 0.0, 0.0, 0.0, 0.0
	for i := 0; i<len(emptyData); i++ {
		if emptyData[i].empty && i > 0 {
			emptyData[i].Radiation = emptyData[i-1].Radiation + gradRad
			emptyData[i].Humidity = emptyData[i-1].Humidity + gradHumidity
			emptyData[i].Temperature = emptyData[i-1].Temperature + gradTemp
			emptyData[i].Wind = emptyData[i-1].Wind + gradWind
			emptyData[i].empty = false
		} else {
			if i + 4 < len (emptyData) {
				gradRad = (emptyData[i+4].Radiation - emptyData[i].Radiation)/4
				gradHumidity = (emptyData[i+4].Humidity - emptyData[i].Humidity)/4
				gradTemp = (emptyData[i+4].Temperature - emptyData[i].Temperature)/4
				gradWind = (emptyData[i+4].Wind - emptyData[i].Wind)/4
			} else {
				gradRad = 0
				gradHumidity = 0
				gradTemp = 0
				gradWind = 0
			}
		}
	}
	return
}
<|endoftext|>


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

package mailer

import (
	"fmt"
	"path"

	"gopkg.in/macaron.v1"

	"github.com/gogits/gogs/models"
	"github.com/gogits/gogs/modules/base"
	"github.com/gogits/gogs/modules/log"
	"github.com/gogits/gogs/modules/setting"
)

const (
	AUTH_ACTIVATE        base.TplName = "mail/auth/activate"
	AUTH_ACTIVATE_EMAIL  base.TplName = "mail/auth/activate_email"
	AUTH_REGISTER_NOTIFY base.TplName = "mail/auth/register_notify"
	AUTH_RESET_PASSWORD  base.TplName = "mail/auth/reset_passwd"

	NOTIFY_COLLABORATOR base.TplName = "mail/notify/collaborator"
	NOTIFY_MENTION      base.TplName = "mail/notify/mention"
)

func ComposeTplData(u *models.User) map[interface{}]interface{} {
	data := make(map[interface{}]interface{}, 10)
	data["AppName"] = setting.AppName
	data["AppVer"] = setting.AppVer
	data["AppUrl"] = setting.AppUrl
	data["ActiveCodeLives"] = setting.Service.ActiveCodeLives / 60
	data["ResetPwdCodeLives"] = setting.Service.ResetPwdCodeLives / 60

	if u != nil {
		data["User"] = u
	}
	return data
}

func SendUserMail(c *macaron.Context, u *models.User, tpl base.TplName, code, subject, info string) {
	data := ComposeTplData(u)
	data["Code"] = code
	body, err := c.HTMLString(string(tpl), data)
	if err != nil {
		log.Error(4, "HTMLString: %v", err)
		return
	}

	msg := NewMessage([]string{u.Email}, subject, body)
	msg.Info = fmt.Sprintf("UID: %d, %s", u.Id, info)

	SendAsync(msg)
}

func SendActivateAccountMail(c *macaron.Context, u *models.User) {
	SendUserMail(c, u, AUTH_ACTIVATE, u.GenerateActivateCode(), c.Tr("mail.activate_account"), "activate account")
}

// SendResetPasswordMail sends reset password e-mail.
func SendResetPasswordMail(c *macaron.Context, u *models.User) {
	SendUserMail(c, u, AUTH_RESET_PASSWORD, u.GenerateActivateCode(), c.Tr("mail.reset_password"), "reset password")
}

// SendRegisterNotifyMail triggers a notify e-mail by admin created a account.
func SendRegisterNotifyMail(c *macaron.Context, u *models.User) {
	body, err := c.HTMLString(string(AUTH_REGISTER_NOTIFY), ComposeTplData(u))
	if err != nil {
		log.Error(4, "HTMLString: %v", err)
		return
	}

	msg := NewMessage([]string{u.Email}, c.Tr("mail.register_notify"), body)
	msg.Info = fmt.Sprintf("UID: %d, registration notify", u.Id)

	SendAsync(msg)
}

// SendActivateAccountMail sends confirmation e-mail.
func SendActivateEmailMail(c *macaron.Context, u *models.User, email *models.EmailAddress) {
	data := ComposeTplData(u)
	data["Code"] = u.GenerateEmailActivateCode(email.Email)
	data["Email"] = email.Email
	body, err := c.HTMLString(string(AUTH_ACTIVATE_EMAIL), data)
	if err != nil {
		log.Error(4, "HTMLString: %v", err)
		return
	}

	msg := NewMessage([]string{email.Email}, c.Tr("mail.activate_email"), body)
	msg.Info = fmt.Sprintf("UID: %d, activate email", u.Id)

	SendAsync(msg)
}

// SendIssueNotifyMail sends mail notification of all watchers of repository.
func SendIssueNotifyMail(u, owner *models.User, repo *models.Repository, issue *models.Issue) ([]string, error) {
	ws, err := models.GetWatchers(repo.ID)
	if err != nil {
		return nil, fmt.Errorf("GetWatchers[%d]: %v", repo.ID, err)
	}

	tos := make([]string, 0, len(ws))
	for i := range ws {
		uid := ws[i].UserID
		if u.Id == uid {
			continue
		}
		to, err := models.GetUserByID(uid)
		if err != nil {
			return nil, fmt.Errorf("GetUserByID: %v", err)
		}
		if to.IsOrganization() {
			continue
		}

		tos = append(tos, to.Email)
	}

	if len(tos) == 0 {
		return tos, nil
	}

	subject := fmt.Sprintf("[%s] %s (#%d)", repo.Name, issue.Name, issue.Index)
	content := fmt.Sprintf("%s
-
 View it on Gogs.",
		base.RenderSpecialLink([]byte(issue.Content), owner.Name+"/"+repo.Name, repo.ComposeMetas()),
		setting.AppUrl, owner.Name, repo.Name, issue.Index)
	msg := NewMessage(tos, subject, content)
	msg.Info = fmt.Sprintf("Subject: %s, issue notify", subject)

	SendAsync(msg)
	return tos, nil
}

// SendIssueMentionMail sends mail notification for who are mentioned in issue.
func SendIssueMentionMail(r macaron.Render, u, owner *models.User,
	repo *models.Repository, issue *models.Issue, tos []string) error {

	if len(tos) == 0 {
		return nil
	}

	subject := fmt.Sprintf("[%s] %s (#%d)", repo.Name, issue.Name, issue.Index)

	data := ComposeTplData(nil)
	data["IssueLink"] = fmt.Sprintf("%s/%s/issues/%d", owner.Name, repo.Name, issue.Index)
	data["Subject"] = subject
	data["ActUserName"] = u.DisplayName()
	data["Content"] = string(base.RenderSpecialLink([]byte(issue.Content), owner.Name+"/"+repo.Name, repo.ComposeMetas()))

	body, err := r.HTMLString(string(NOTIFY_MENTION), data)
	if err != nil {
		return fmt.Errorf("HTMLString: %v", err)
	}

	msg := NewMessage(tos, subject, body)
	msg.Info = fmt.Sprintf("Subject: %s, issue mention", subject)

	SendAsync(msg)
	return nil
}

// SendCollaboratorMail sends mail notification to new collaborator.
func SendCollaboratorMail(r macaron.Render, u, doer *models.User, repo *models.Repository) error {
	subject := fmt.Sprintf("%s added you to %s/%s", doer.Name, repo.Owner.Name, repo.Name)

	data := ComposeTplData(nil)
	data["RepoLink"] = path.Join(repo.Owner.Name, repo.Name)
	data["Subject"] = subject

	body, err := r.HTMLString(string(NOTIFY_COLLABORATOR), data)
	if err != nil {
		return fmt.Errorf("HTMLString: %v", err)
	}

	msg := NewMessage([]string{u.Email}, subject, body)
	msg.Info = fmt.Sprintf("UID: %d, add collaborator", u.Id)

	SendAsync(msg)
	return nil
}
<commit_msg>Fix issue email formatting. Addresses #2331<commit_after>// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package mailer

import (
	"fmt"
	"path"
	"strings"

	"gopkg.in/macaron.v1"

	"github.com/gogits/gogs/models"
	"github.com/gogits/gogs/modules/base"
	"github.com/gogits/gogs/modules/log"
	"github.com/gogits/gogs/modules/setting"
)

const (
	AUTH_ACTIVATE        base.TplName = "mail/auth/activate"
	AUTH_ACTIVATE_EMAIL  base.TplName = "mail/auth/activate_email"
	AUTH_REGISTER_NOTIFY base.TplName = "mail/auth/register_notify"
	AUTH_RESET_PASSWORD  base.TplName = "mail/auth/reset_passwd"

	NOTIFY_COLLABORATOR base.TplName = "mail/notify/collaborator"
	NOTIFY_MENTION      base.TplName = "mail/notify/mention"
)

func ComposeTplData(u *models.User) map[interface{}]interface{} {
	data := make(map[interface{}]interface{}, 10)
	data["AppName"] = setting.AppName
	data["AppVer"] = setting.AppVer
	data["AppUrl"] = setting.AppUrl
	data["ActiveCodeLives"] = setting.Service.ActiveCodeLives / 60
	data["ResetPwdCodeLives"] = setting.Service.ResetPwdCodeLives / 60

	if u != nil {
		data["User"] = u
	}
	return data
}

func SendUserMail(c *macaron.Context, u *models.User, tpl base.TplName, code, subject, info string) {
	data := ComposeTplData(u)
	data["Code"] = code
	body, err := c.HTMLString(string(tpl), data)
	if err != nil {
		log.Error(4, "HTMLString: %v", err)
		return
	}

	msg := NewMessage([]string{u.Email}, subject, body)
	msg.Info = fmt.Sprintf("UID: %d, %s", u.Id, info)

	SendAsync(msg)
}

func SendActivateAccountMail(c *macaron.Context, u *models.User) {
	SendUserMail(c, u, AUTH_ACTIVATE, u.GenerateActivateCode(), c.Tr("mail.activate_account"), "activate account")
}

// SendResetPasswordMail sends reset password e-mail.
func SendResetPasswordMail(c *macaron.Context, u *models.User) {
	SendUserMail(c, u, AUTH_RESET_PASSWORD, u.GenerateActivateCode(), c.Tr("mail.reset_password"), "reset password")
}

// SendRegisterNotifyMail triggers a notify e-mail by admin created a account.
func SendRegisterNotifyMail(c *macaron.Context, u *models.User) {
	body, err := c.HTMLString(string(AUTH_REGISTER_NOTIFY), ComposeTplData(u))
	if err != nil {
		log.Error(4, "HTMLString: %v", err)
		return
	}

	msg := NewMessage([]string{u.Email}, c.Tr("mail.register_notify"), body)
	msg.Info = fmt.Sprintf("UID: %d, registration notify", u.Id)

	SendAsync(msg)
}

// SendActivateAccountMail sends confirmation e-mail.
func SendActivateEmailMail(c *macaron.Context, u *models.User, email *models.EmailAddress) {
	data := ComposeTplData(u)
	data["Code"] = u.GenerateEmailActivateCode(email.Email)
	data["Email"] = email.Email
	body, err := c.HTMLString(string(AUTH_ACTIVATE_EMAIL), data)
	if err != nil {
		log.Error(4, "HTMLString: %v", err)
		return
	}

	msg := NewMessage([]string{email.Email}, c.Tr("mail.activate_email"), body)
	msg.Info = fmt.Sprintf("UID: %d, activate email", u.Id)

	SendAsync(msg)
}

// SendIssueNotifyMail sends mail notification of all watchers of repository.
func SendIssueNotifyMail(u, owner *models.User, repo *models.Repository, issue *models.Issue) ([]string, error) {
	ws, err := models.GetWatchers(repo.ID)
	if err != nil {
		return nil, fmt.Errorf("GetWatchers[%d]: %v", repo.ID, err)
	}

	tos := make([]string, 0, len(ws))
	for i := range ws {
		uid := ws[i].UserID
		if u.Id == uid {
			continue
		}
		to, err := models.GetUserByID(uid)
		if err != nil {
			return nil, fmt.Errorf("GetUserByID: %v", err)
		}
		if to.IsOrganization() {
			continue
		}

		tos = append(tos, to.Email)
	}

	if len(tos) == 0 {
		return tos, nil
	}

	subject := fmt.Sprintf("[%s] %s (#%d)", repo.Name, issue.Name, issue.Index)
	content := fmt.Sprintf("%s
-
 View it on Gogs.",
		base.RenderSpecialLink([]byte(strings.Replace(issue.Content, "\n", "
", -1)), owner.Name+"/"+repo.Name, repo.ComposeMetas()),
		setting.AppUrl, owner.Name, repo.Name, issue.Index)
	msg := NewMessage(tos, subject, content)
	msg.Info = fmt.Sprintf("Subject: %s, issue notify", subject)

	SendAsync(msg)
	return tos, nil
}

// SendIssueMentionMail sends mail notification for who are mentioned in issue.
func SendIssueMentionMail(r macaron.Render, u, owner *models.User,
	repo *models.Repository, issue *models.Issue, tos []string) error {

	if len(tos) == 0 {
		return nil
	}

	subject := fmt.Sprintf("[%s] %s (#%d)", repo.Name, issue.Name, issue.Index)

	data := ComposeTplData(nil)
	data["IssueLink"] = fmt.Sprintf("%s/%s/issues/%d", owner.Name, repo.Name, issue.Index)
	data["Subject"] = subject
	data["ActUserName"] = u.DisplayName()
	data["Content"] = string(base.RenderSpecialLink([]byte(issue.Content), owner.Name+"/"+repo.Name, repo.ComposeMetas()))

	body, err := r.HTMLString(string(NOTIFY_MENTION), data)
	if err != nil {
		return fmt.Errorf("HTMLString: %v", err)
	}

	msg := NewMessage(tos, subject, body)
	msg.Info = fmt.Sprintf("Subject: %s, issue mention", subject)

	SendAsync(msg)
	return nil
}

// SendCollaboratorMail sends mail notification to new collaborator.
func SendCollaboratorMail(r macaron.Render, u, doer *models.User, repo *models.Repository) error {
	subject := fmt.Sprintf("%s added you to %s/%s", doer.Name, repo.Owner.Name, repo.Name)

	data := ComposeTplData(nil)
	data["RepoLink"] = path.Join(repo.Owner.Name, repo.Name)
	data["Subject"] = subject

	body, err := r.HTMLString(string(NOTIFY_COLLABORATOR), data)
	if err != nil {
		return fmt.Errorf("HTMLString: %v", err)
	}

	msg := NewMessage([]string{u.Email}, subject, body)
	msg.Info = fmt.Sprintf("UID: %d, add collaborator", u.Id)

	SendAsync(msg)
	return nil
}
<|endoftext|>


<commit_before>package exec

import (
	"code.cloudfoundry.org/lager"
	"code.cloudfoundry.org/lager/lagerctx"
	"context"
	"errors"
	"fmt"
	"github.com/concourse/concourse/atc"
	"github.com/concourse/concourse/atc/creds"
	"github.com/concourse/concourse/atc/db"
	"github.com/concourse/concourse/atc/exec/artifact"
	"github.com/concourse/concourse/vars"
	"io/ioutil"
	"sigs.k8s.io/yaml"
)

// SetPipelineStep sets a pipeline to current team. If pipeline_name specified
// is "self", then it will self set the current pipeline. This step takes pipeline
// configure file and var files from some resource in the pipeline, like git.
type SetPipelineStep struct {
	planID      atc.PlanID
	plan        atc.SetPipelinePlan
	metadata    StepMetadata
	delegate    BuildStepDelegate
	teamFactory db.TeamFactory
	succeeded   bool
}

func NewSetPipelineStep(
	planID atc.PlanID,
	plan atc.SetPipelinePlan,
	metadata StepMetadata,
	delegate BuildStepDelegate,
	teamFactory db.TeamFactory,
) Step {
	return &SetPipelineStep{
		planID:      planID,
		plan:        plan,
		metadata:    metadata,
		delegate:    delegate,
		teamFactory: teamFactory,
	}
}

func (step *SetPipelineStep) Run(ctx context.Context, state RunState) error {
	logger := lagerctx.FromContext(ctx)
	logger = logger.Session("set-pipeline-step", lager.Data{
		"step-name": step.plan.Name,
		"job-id":    step.metadata.JobID,
	})

	step.delegate.Initializing(logger)

	stdout := step.delegate.Stdout()
	stderr := step.delegate.Stderr()

	source := setPipelineSource{
		ctx:    ctx,
		logger: logger,
		step:   step,
		repo:   state.Artifacts(),
	}

	err := source.Validate()
	if err != nil {
		return err
	}

	err = source.EvaluatePlan(step.delegate.Variables())
	if err != nil {
		return err
	}

	atcConfig, err := source.FetchConfig()
	if err != nil {
		return err
	}

	step.delegate.Starting(logger)

	warnings, errors := atcConfig.Validate()
	for _, warning := range warnings {
		fmt.Fprintf(stderr, "WARNING: %s\n", warning.Message)
	}

	if len(errors) > 0 {
		fmt.Fprintln(step.delegate.Stderr(), "invalid pipeline:")

		for _, e := range errors {
			fmt.Fprintf(stderr, "- %s", e)
		}

		step.delegate.Finished(logger, false)
		return nil
	}

	team := step.teamFactory.GetByID(step.metadata.TeamID)

	fromVersion := db.ConfigVersion(0)
	pipeline, found, err := team.Pipeline(step.plan.Name)
	if err != nil {
		return nil
	}

	var existingConfig atc.Config
	if !found {
		existingConfig = atc.Config{}
	} else {
		fromVersion = pipeline.ConfigVersion()
		existingConfig, err = pipeline.Config()
		if err != nil {
			return err
		}
	}

	diffExists := existingConfig.Diff(stdout, *atcConfig)
	if !diffExists {
		logger.Debug("no-diff")

		fmt.Fprintf(stdout, "No diff found.\n")
		step.succeeded = true
		step.delegate.Finished(logger, true)
		return nil
	}

	fmt.Fprintf(stdout, "Updating the pipeline.\n")
	pipeline, _, err = team.SavePipeline(step.plan.Name, *atcConfig, fromVersion, false)
	if err != nil {
		return err
	}

	fmt.Fprintf(stdout, "Done successfully.\n")
	logger.Info("saved-pipeline", lager.Data{"team": team.Name(), "pipeline": pipeline.Name()})
	step.succeeded = true
	step.delegate.Finished(logger, true)

	return nil
}

func (step *SetPipelineStep) Succeeded() bool {
	return step.succeeded
}

type setPipelineSource struct {
	ctx    context.Context
	logger lager.Logger
	repo   *artifact.Repository
	step   *SetPipelineStep
}

// streamInBytes streams a file from other resource and returns a byte array.
func (s setPipelineSource) streamInBytes(path string) ([]byte, error) {
	stream, err := s.repo.StreamFile(s.ctx, s.logger, path)
	if err != nil {
		return nil, err
	}
	defer stream.Close()

	byteConfig, err := ioutil.ReadAll(stream)
	if err != nil {
		return nil, err
	}

	return byteConfig, nil
}

// FetchConfig streams pipeline configure file and var files from other resources
// and construct an atc.Config object.
func (s setPipelineSource) FetchConfig() (*atc.Config, error) {
	config, err := s.streamInBytes(s.step.plan.File)
	if err != nil {
		return nil, err
	}

	staticVarss := []vars.Variables{}
	if len(s.step.plan.Vars) > 0 {
		staticVarss = append(staticVarss, vars.StaticVariables(s.step.plan.Vars))
	}
	for _, lvf := range s.step.plan.VarFiles {
		bytes, err := s.streamInBytes(lvf)
		if err != nil {
			return nil, err
		}

		sv := vars.StaticVariables{}
		err = yaml.Unmarshal(bytes, &sv)
		if err != nil {
			return nil, err
		}

		staticVarss = append(staticVarss, sv)
	}

	if len(staticVarss) > 0 {
		config, err = vars.NewTemplateResolver(config, staticVarss).Resolve(false, false)
		if err != nil {
			return nil, err
		}
	}

	atcConfig := atc.Config{}
	err = yaml.Unmarshal(config, &atcConfig)
	if err != nil {
		return nil, err
	}

	return &atcConfig, nil
}

func (s setPipelineSource) Validate() error {
	if s.step.plan.File == "" {
		return errors.New("file is not specified")
	}

	return nil
}

func (s setPipelineSource) EvaluatePlan(variables vars.Variables) error {
	params := atc.Params{
		"file":      s.step.plan.File,
		"var_files": s.step.plan.VarFiles,
	}

	params, err := creds.NewParams(variables, params).Evaluate()
	if err != nil {
		return err
	}

	s.step.plan.File = params["file"].(string)
	s.step.plan.VarFiles = []string{}
	for _, ele := range params["var_files"].([]interface{}) {
		s.step.plan.VarFiles = append(s.step.plan.VarFiles, ele.(string))
	}

	if s.step.plan.Name == "self" {
		s.step.plan.Name = s.step.metadata.PipelineName
	}

	return nil
}
<commit_msg>Removed setPipelineSource.EvalutePlan.<commit_after>package exec

import (
	"code.cloudfoundry.org/lager"
	"code.cloudfoundry.org/lager/lagerctx"
	"context"
	"errors"
	"fmt"
	"io/ioutil"
	"sigs.k8s.io/yaml"

	"github.com/concourse/concourse/atc"
	"github.com/concourse/concourse/atc/db"
	"github.com/concourse/concourse/atc/exec/artifact"
	"github.com/concourse/concourse/vars"
)

// SetPipelineStep sets a pipeline to current team. If pipeline_name specified
// is "self", then it will self set the current pipeline. This step takes pipeline
// configure file and var files from some resource in the pipeline, like git.
type SetPipelineStep struct {
	planID      atc.PlanID
	plan        atc.SetPipelinePlan
	metadata    StepMetadata
	delegate    BuildStepDelegate
	teamFactory db.TeamFactory
	succeeded   bool
}

func NewSetPipelineStep(
	planID atc.PlanID,
	plan atc.SetPipelinePlan,
	metadata StepMetadata,
	delegate BuildStepDelegate,
	teamFactory db.TeamFactory,
) Step {
	return &SetPipelineStep{
		planID:      planID,
		plan:        plan,
		metadata:    metadata,
		delegate:    delegate,
		teamFactory: teamFactory,
	}
}

func (step *SetPipelineStep) Run(ctx context.Context, state RunState) error {
	logger := lagerctx.FromContext(ctx)
	logger = logger.Session("set-pipeline-step", lager.Data{
		"step-name": step.plan.Name,
		"job-id":    step.metadata.JobID,
	})

	step.delegate.Initializing(logger)

	stdout := step.delegate.Stdout()
	stderr := step.delegate.Stderr()

	source := setPipelineSource{
		ctx:    ctx,
		logger: logger,
		step:   step,
		repo:   state.Artifacts(),
	}

	err := source.Validate()
	if err != nil {
		return err
	}

	atcConfig, err := source.FetchConfig()
	if err != nil {
		return err
	}

	step.delegate.Starting(logger)

	warnings, errors := atcConfig.Validate()
	for _, warning := range warnings {
		fmt.Fprintf(stderr, "WARNING: %s\n", warning.Message)
	}

	if len(errors) > 0 {
		fmt.Fprintln(step.delegate.Stderr(), "invalid pipeline:")

		for _, e := range errors {
			fmt.Fprintf(stderr, "- %s", e)
		}

		step.delegate.Finished(logger, false)
		return nil
	}

	team := step.teamFactory.GetByID(step.metadata.TeamID)

	fromVersion := db.ConfigVersion(0)
	pipeline, found, err := team.Pipeline(step.plan.Name)
	if err != nil {
		return nil
	}

	var existingConfig atc.Config
	if !found {
		existingConfig = atc.Config{}
	} else {
		fromVersion = pipeline.ConfigVersion()
		existingConfig, err = pipeline.Config()
		if err != nil {
			return err
		}
	}

	diffExists := existingConfig.Diff(stdout, *atcConfig)
	if !diffExists {
		logger.Debug("no-diff")

		fmt.Fprintf(stdout, "No diff found.\n")
		step.succeeded = true
		step.delegate.Finished(logger, true)
		return nil
	}

	fmt.Fprintf(stdout, "Updating the pipeline.\n")
	pipeline, _, err = team.SavePipeline(step.plan.Name, *atcConfig, fromVersion, false)
	if err != nil {
		return err
	}

	fmt.Fprintf(stdout, "Done successfully.\n")
	logger.Info("saved-pipeline", lager.Data{"team": team.Name(), "pipeline": pipeline.Name()})
	step.succeeded = true
	step.delegate.Finished(logger, true)

	return nil
}

func (step *SetPipelineStep) Succeeded() bool {
	return step.succeeded
}

type setPipelineSource struct {
	ctx    context.Context
	logger lager.Logger
	repo   *artifact.Repository
	step   *SetPipelineStep
}

// streamInBytes streams a file from other resource and returns a byte array.
func (s setPipelineSource) streamInBytes(path string) ([]byte, error) {
	stream, err := s.repo.StreamFile(s.ctx, s.logger, path)
	if err != nil {
		return nil, err
	}
	defer stream.Close()

	byteConfig, err := ioutil.ReadAll(stream)
	if err != nil {
		return nil, err
	}

	return byteConfig, nil
}

// FetchConfig streams pipeline configure file and var files from other resources
// and construct an atc.Config object.
func (s setPipelineSource) FetchConfig() (*atc.Config, error) {
	config, err := s.streamInBytes(s.step.plan.File)
	if err != nil {
		return nil, err
	}

	staticVarss := []vars.Variables{}
	if len(s.step.plan.Vars) > 0 {
		staticVarss = append(staticVarss, vars.StaticVariables(s.step.plan.Vars))
	}
	for _, lvf := range s.step.plan.VarFiles {
		bytes, err := s.streamInBytes(lvf)
		if err != nil {
			return nil, err
		}

		sv := vars.StaticVariables{}
		err = yaml.Unmarshal(bytes, &sv)
		if err != nil {
			return nil, err
		}

		staticVarss = append(staticVarss, sv)
	}

	if len(staticVarss) > 0 {
		config, err = vars.NewTemplateResolver(config, staticVarss).Resolve(false, false)
		if err != nil {
			return nil, err
		}
	}

	atcConfig := atc.Config{}
	err = yaml.Unmarshal(config, &atcConfig)
	if err != nil {
		return nil, err
	}

	return &atcConfig, nil
}

func (s setPipelineSource) Validate() error {
	if s.step.plan.File == "" {
		return errors.New("file is not specified")
	}

	return nil
}
<|endoftext|>


<commit_before>package pubsub

import (
	check "gopkg.in/check.v1"
	"testing"
)

var _ = check.Suite(new(Suite))

func Test(t *testing.T) {
	check.TestingT(t)
}

type Suite struct{}

func (s *Suite) TestSub(ch *check.C) {
	pubsub := newPubsub()

	sub1 := pubsub.on("hello")
	pubsub.publish("hello", "world")

	ch.Check(<-sub1, check.Equals, "world")
	pubsub.publish("hello", "globe")

	ch.Check(<-sub1, check.Equals, "globe")

	pubsub.publish("hello", "one")
	pubsub.publish("hello", "two")

	ch.Check(<-sub1, check.Equals, "one")
	ch.Check(<-sub1, check.Equals, "two")
}

func (s *Suite) TestMoreSubs(ch *check.C) {
	pubsub := newPubsub()

	sub1 := pubsub.on("hello")
	sub2 := pubsub.on("hello")
	sub3 := pubsub.on("hello")
	pubsub.publish("hello", "world")

	ch.Check(<-sub1, check.Equals, "world")
	ch.Check(<-sub2, check.Equals, "world")
	pubsub.publish("hello", "globe")

	ch.Check(<-sub1, check.Equals, "globe")
	ch.Check(<-sub2, check.Equals, "globe")
	ch.Check(<-sub3, check.Equals, "world")
	ch.Check(<-sub3, check.Equals, "globe")
}<commit_msg>Tests upgraded<commit_after>package pubsub

import (
	check "gopkg.in/check.v1"
	"testing"
)

var _ = check.Suite(new(Suite))

func Test(t *testing.T) {
	check.TestingT(t)
}

type Suite struct{}

func (s *Suite) TestSub(ch *check.C) {
	pubsub := newPubsub()

	sub1 := pubsub.on("hello")
	pubsub.publish("hello", "world")

	ch.Check(<-sub1, check.Equals, "world")
	pubsub.publish("hello", "globe")

	ch.Check(<-sub1, check.Equals, "globe")

	pubsub.publish("hello", "one")
	pubsub.publish("hello", "two")

	ch.Check(<-sub1, check.Equals, "one")
	ch.Check(<-sub1, check.Equals, "two")
}

func (s *Suite) TestMoreSubs(ch *check.C) {
	pubsub := newPubsub()

	sub1 := pubsub.on("hello")
	sub2 := pubsub.on("hello")
	sub3 := pubsub.on("hello")
	pubsub.publish("hello", "world")

	ch.Check(<-sub1, check.Equals, "world")
	ch.Check(<-sub2, check.Equals, "world")
	pubsub.publish("hello", "globe")

	ch.Check(<-sub1, check.Equals, "globe")
	ch.Check(<-sub2, check.Equals, "globe")
	ch.Check(<-sub3, check.Equals, "world")
	ch.Check(<-sub3, check.Equals, "globe")
}

func (s *Suite) TestChaining(ch *check.C) {
	pubsub := newPubsub()

	sub1 := pubsub.on("hello")
	sub2 := pubsub.on("hello")
	sub3 := pubsub.on("hello")
	pubsub.publish("hello", "world-chaining").publish("hello", "globe")

	ch.Check(<-sub1, check.Equals, "world-chaining")
	ch.Check(<-sub2, check.Equals, "world-chaining")
	ch.Check(<-sub3, check.Equals, "world-chaining")

	ch.Check(<-sub1, check.Equals, "globe")
	ch.Check(<-sub2, check.Equals, "globe")
	ch.Check(<-sub3, check.Equals, "globe")
}<|endoftext|>


<commit_before>package checkntservice

import (
	"os/exec"
	"runtime"
	"testing"
)

func stopFaxService() error {
	_, err := exec.Command("net", "stop", "Fax").CombinedOutput()
	return err
}

func startFaxService() error {
	_, err := exec.Command("net", "start", "Fax").CombinedOutput()
	return err
}

func TestNtService(t *testing.T) {
	if runtime.GOOS != "windows" {
		t.Skip(runtime.GOOS + " doesn't implement Windows NT service")
	}

	ss, err := getServiceState()
	if err != nil {
		t.Errorf("failed to get service status: %v", err)
	}
	for _, s := range ss {
		if s.Name == "Fax" {
			if s.State != "Running" {
				t.Errorf("Fax service should be started in default: %v", s.State)
			}
		}
	}

	err = stopFaxService()
	if err != nil {
		t.Skipf("failed to stop Fax service. But ignore this: %v", err)
	}
	defer startFaxService()

	ss, err = getServiceState()
	if err != nil {
		t.Errorf("failed to get service status: %v", err)
	}
	for _, s := range ss {
		if s.Name == "Fax" {
			if s.State == "Running" {
				t.Error("Fax service should be stopped now")
			}
		}
	}
}
<commit_msg>add test for non-Windows OSs<commit_after>package checkntservice

import (
	"os/exec"
	"runtime"
	"syscall"
	"testing"
)

func stopFaxService() error {
	_, err := exec.Command("net", "stop", "Fax").CombinedOutput()
	return err
}

func startFaxService() error {
	_, err := exec.Command("net", "start", "Fax").CombinedOutput()
	return err
}

func TestNtService(t *testing.T) {
	ss, err := getServiceState()
	if runtime.GOOS != "windows" {
		if err == nil || err != syscall.ENOSYS {
			t.Fatal(runtime.GOOS + " should fail because it's not Windows")
		}
		t.Skip(runtime.GOOS + " doesn't implement Windows NT service")
	}
	if err != nil {
		t.Errorf("failed to get service status: %v", err)
	}
	for _, s := range ss {
		if s.Name == "Fax" {
			if s.State != "Running" {
				t.Errorf("Fax service should be started in default: %v", s.State)
			}
		}
	}

	err = stopFaxService()
	if err != nil {
		t.Skipf("failed to stop Fax service. But ignore this: %v", err)
	}
	defer startFaxService()

	ss, err = getServiceState()
	if err != nil {
		t.Errorf("failed to get service status: %v", err)
	}
	for _, s := range ss {
		if s.Name == "Fax" {
			if s.State == "Running" {
				t.Error("Fax service should be stopped now")
			}
		}
	}
}
<|endoftext|>


<commit_before>package mpb

import (
	"container/heap"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"sync"
	"time"

	"github.com/vbauerster/mpb/cwriter"
)

const (
	// default RefreshRate
	prr = 120 * time.Millisecond
	// default width
	pwidth = 80
)

// Progress represents the container that renders Progress bars
type Progress struct {
	wg           *sync.WaitGroup
	uwg          *sync.WaitGroup
	operateState chan func(*pState)
	done         chan struct{}
}

type pState struct {
	bHeap           *priorityQueue
	shutdownPending []*Bar
	heapUpdated     bool
	zeroWait        bool
	idCounter       int
	width           int
	format          string
	rr              time.Duration
	cw              *cwriter.Writer
	pMatrix         map[int][]chan int
	aMatrix         map[int][]chan int

	// following are provided by user
	uwg              *sync.WaitGroup
	manualRefreshCh  <-chan time.Time
	cancel           <-chan struct{}
	shutdownNotifier chan struct{}
	waitBars         map[*Bar]*Bar
	debugOut         io.Writer
}

// New creates new Progress instance, which orchestrates bars rendering process.
// Accepts mpb.ProgressOption funcs for customization.
func New(options ...ProgressOption) *Progress {
	pq := make(priorityQueue, 0)
	heap.Init(&pq)
	s := &pState{
		bHeap:    &pq,
		width:    pwidth,
		cw:       cwriter.New(os.Stdout),
		rr:       prr,
		waitBars: make(map[*Bar]*Bar),
		debugOut: ioutil.Discard,
	}

	for _, opt := range options {
		if opt != nil {
			opt(s)
		}
	}

	p := &Progress{
		uwg:          s.uwg,
		wg:           new(sync.WaitGroup),
		operateState: make(chan func(*pState)),
		done:         make(chan struct{}),
	}
	go p.serve(s)
	return p
}

// AddBar creates a new progress bar and adds to the container.
func (p *Progress) AddBar(total int64, options ...BarOption) *Bar {
	// make sure filler is initialized first
	args := []BarOption{
		func(s *bState) {
			s.filler = &barFiller{
				format: defaultBarStyle,
			}
		},
	}
	args = append(args, options...)
	return p.add(total, args...)
}

func (p *Progress) AddSpinner(total int64, alignment SpinnerAlignment, options ...BarOption) *Bar {
	// make sure filler is initialized first
	args := []BarOption{
		func(s *bState) {
			s.filler = &spinnerFiller{
				frames:    defaultSpinnerStyle,
				alignment: alignment,
			}
		},
	}
	args = append(args, options...)
	return p.add(total, args...)
}

func (p *Progress) add(total int64, options ...BarOption) *Bar {
	p.wg.Add(1)
	result := make(chan *Bar)
	select {
	case p.operateState <- func(s *pState) {
		b := newBar(p.wg, s.idCounter, s.width, total, s.cancel, options...)
		if b.runningBar != nil {
			s.waitBars[b.runningBar] = b
		} else {
			heap.Push(s.bHeap, b)
			s.heapUpdated = true
		}
		s.idCounter++
		result <- b
	}:
		return <-result
	case <-p.done:
		p.wg.Done()
		return nil
	}
}

// Abort is only effective while bar progress is running,
// it means remove bar now without waiting for its completion.
// If bar is already completed, there is nothing to abort.
// If you need to remove bar after completion, use BarRemoveOnComplete BarOption.
func (p *Progress) Abort(b *Bar, remove bool) {
	select {
	case p.operateState <- func(s *pState) {
		if b.index < 0 {
			return
		}
		if remove {
			s.heapUpdated = heap.Remove(s.bHeap, b.index) != nil
		}
		s.shutdownPending = append(s.shutdownPending, b)
	}:
	case <-p.done:
	}
}

// UpdateBarPriority provides a way to change bar's order position.
// Zero is highest priority, i.e. bar will be on top.
func (p *Progress) UpdateBarPriority(b *Bar, priority int) {
	select {
	case p.operateState <- func(s *pState) { s.bHeap.update(b, priority) }:
	case <-p.done:
	}
}

// BarCount returns bars count
func (p *Progress) BarCount() int {
	result := make(chan int, 1)
	select {
	case p.operateState <- func(s *pState) { result <- s.bHeap.Len() }:
		return <-result
	case <-p.done:
		return 0
	}
}

// Wait first waits for user provided *sync.WaitGroup, if any,
// then waits far all bars to complete and finally shutdowns master goroutine.
// After this method has been called, there is no way to reuse *Progress instance.
func (p *Progress) Wait() {
	if p.uwg != nil {
		p.uwg.Wait()
	}

	p.wg.Wait()

	select {
	case p.operateState <- func(s *pState) { s.zeroWait = true }:
		<-p.done
	case <-p.done:
	}
}

func (s *pState) updateSyncMatrix() {
	s.pMatrix = make(map[int][]chan int)
	s.aMatrix = make(map[int][]chan int)
	for i := 0; i < s.bHeap.Len(); i++ {
		bar := (*s.bHeap)[i]
		table := bar.wSyncTable()
		pRow, aRow := table[0], table[1]

		for i, ch := range pRow {
			s.pMatrix[i] = append(s.pMatrix[i], ch)
		}

		for i, ch := range aRow {
			s.aMatrix[i] = append(s.aMatrix[i], ch)
		}
	}
}

func (s *pState) render(tw int) {
	if s.heapUpdated {
		s.updateSyncMatrix()
		s.heapUpdated = false
	}
	syncWidth(s.pMatrix)
	syncWidth(s.aMatrix)

	for i := 0; i < s.bHeap.Len(); i++ {
		bar := (*s.bHeap)[i]
		go bar.render(s.debugOut, tw)
	}

	if err := s.flush(s.bHeap.Len()); err != nil {
		fmt.Fprintf(s.debugOut, "%s %s %v\n", "[mpb]", time.Now(), err)
	}
}

func (s *pState) flush(lineCount int) error {
	for s.bHeap.Len() > 0 {
		bar := heap.Pop(s.bHeap).(*Bar)
		frameReader := <-bar.frameReaderCh
		defer func() {
			if frameReader.toShutdown {
				// shutdown at next flush, in other words decrement underlying WaitGroup
				// only after the bar with completed state has been flushed.
				// this ensures no bar ends up with less than 100% rendered.
				s.shutdownPending = append(s.shutdownPending, bar)
				if replacementBar, ok := s.waitBars[bar]; ok {
					heap.Push(s.bHeap, replacementBar)
					s.heapUpdated = true
					delete(s.waitBars, bar)
				}
				if frameReader.removeOnComplete {
					s.heapUpdated = true
					return
				}
			}
			heap.Push(s.bHeap, bar)
		}()
		s.cw.ReadFrom(frameReader)
		lineCount += frameReader.extendedLines
	}

	for i := len(s.shutdownPending) - 1; i >= 0; i-- {
		close(s.shutdownPending[i].shutdown)
		s.shutdownPending = s.shutdownPending[:i]
	}

	return s.cw.Flush(lineCount)
}

func syncWidth(matrix map[int][]chan int) {
	for _, column := range matrix {
		column := column
		go func() {
			var maxWidth int
			for _, ch := range column {
				w := <-ch
				if w > maxWidth {
					maxWidth = w
				}
			}
			for _, ch := range column {
				ch <- maxWidth
			}
		}()
	}
}
<commit_msg>AddSpinner comment<commit_after>package mpb

import (
	"container/heap"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"sync"
	"time"

	"github.com/vbauerster/mpb/cwriter"
)

const (
	// default RefreshRate
	prr = 120 * time.Millisecond
	// default width
	pwidth = 80
)

// Progress represents the container that renders Progress bars
type Progress struct {
	wg           *sync.WaitGroup
	uwg          *sync.WaitGroup
	operateState chan func(*pState)
	done         chan struct{}
}

type pState struct {
	bHeap           *priorityQueue
	shutdownPending []*Bar
	heapUpdated     bool
	zeroWait        bool
	idCounter       int
	width           int
	format          string
	rr              time.Duration
	cw              *cwriter.Writer
	pMatrix         map[int][]chan int
	aMatrix         map[int][]chan int

	// following are provided by user
	uwg              *sync.WaitGroup
	manualRefreshCh  <-chan time.Time
	cancel           <-chan struct{}
	shutdownNotifier chan struct{}
	waitBars         map[*Bar]*Bar
	debugOut         io.Writer
}

// New creates new Progress instance, which orchestrates bars rendering process.
// Accepts mpb.ProgressOption funcs for customization.
func New(options ...ProgressOption) *Progress {
	pq := make(priorityQueue, 0)
	heap.Init(&pq)
	s := &pState{
		bHeap:    &pq,
		width:    pwidth,
		cw:       cwriter.New(os.Stdout),
		rr:       prr,
		waitBars: make(map[*Bar]*Bar),
		debugOut: ioutil.Discard,
	}

	for _, opt := range options {
		if opt != nil {
			opt(s)
		}
	}

	p := &Progress{
		uwg:          s.uwg,
		wg:           new(sync.WaitGroup),
		operateState: make(chan func(*pState)),
		done:         make(chan struct{}),
	}
	go p.serve(s)
	return p
}

// AddBar creates a new progress bar and adds to the container.
func (p *Progress) AddBar(total int64, options ...BarOption) *Bar {
	// make sure filler is initialized first
	args := []BarOption{
		func(s *bState) {
			s.filler = &barFiller{
				format: defaultBarStyle,
			}
		},
	}
	args = append(args, options...)
	return p.add(total, args...)
}

// AddSpinner creates a new spinner bar and adds to the container.
func (p *Progress) AddSpinner(total int64, alignment SpinnerAlignment, options ...BarOption) *Bar {
	// make sure filler is initialized first
	args := []BarOption{
		func(s *bState) {
			s.filler = &spinnerFiller{
				frames:    defaultSpinnerStyle,
				alignment: alignment,
			}
		},
	}
	args = append(args, options...)
	return p.add(total, args...)
}

func (p *Progress) add(total int64, options ...BarOption) *Bar {
	p.wg.Add(1)
	result := make(chan *Bar)
	select {
	case p.operateState <- func(s *pState) {
		b := newBar(p.wg, s.idCounter, s.width, total, s.cancel, options...)
		if b.runningBar != nil {
			s.waitBars[b.runningBar] = b
		} else {
			heap.Push(s.bHeap, b)
			s.heapUpdated = true
		}
		s.idCounter++
		result <- b
	}:
		return <-result
	case <-p.done:
		p.wg.Done()
		return nil
	}
}

// Abort is only effective while bar progress is running,
// it means remove bar now without waiting for its completion.
// If bar is already completed, there is nothing to abort.
// If you need to remove bar after completion, use BarRemoveOnComplete BarOption.
func (p *Progress) Abort(b *Bar, remove bool) {
	select {
	case p.operateState <- func(s *pState) {
		if b.index < 0 {
			return
		}
		if remove {
			s.heapUpdated = heap.Remove(s.bHeap, b.index) != nil
		}
		s.shutdownPending = append(s.shutdownPending, b)
	}:
	case <-p.done:
	}
}

// UpdateBarPriority provides a way to change bar's order position.
// Zero is highest priority, i.e. bar will be on top.
func (p *Progress) UpdateBarPriority(b *Bar, priority int) {
	select {
	case p.operateState <- func(s *pState) { s.bHeap.update(b, priority) }:
	case <-p.done:
	}
}

// BarCount returns bars count
func (p *Progress) BarCount() int {
	result := make(chan int, 1)
	select {
	case p.operateState <- func(s *pState) { result <- s.bHeap.Len() }:
		return <-result
	case <-p.done:
		return 0
	}
}

// Wait first waits for user provided *sync.WaitGroup, if any,
// then waits far all bars to complete and finally shutdowns master goroutine.
// After this method has been called, there is no way to reuse *Progress instance.
func (p *Progress) Wait() {
	if p.uwg != nil {
		p.uwg.Wait()
	}

	p.wg.Wait()

	select {
	case p.operateState <- func(s *pState) { s.zeroWait = true }:
		<-p.done
	case <-p.done:
	}
}

func (s *pState) updateSyncMatrix() {
	s.pMatrix = make(map[int][]chan int)
	s.aMatrix = make(map[int][]chan int)
	for i := 0; i < s.bHeap.Len(); i++ {
		bar := (*s.bHeap)[i]
		table := bar.wSyncTable()
		pRow, aRow := table[0], table[1]

		for i, ch := range pRow {
			s.pMatrix[i] = append(s.pMatrix[i], ch)
		}

		for i, ch := range aRow {
			s.aMatrix[i] = append(s.aMatrix[i], ch)
		}
	}
}

func (s *pState) render(tw int) {
	if s.heapUpdated {
		s.updateSyncMatrix()
		s.heapUpdated = false
	}
	syncWidth(s.pMatrix)
	syncWidth(s.aMatrix)

	for i := 0; i < s.bHeap.Len(); i++ {
		bar := (*s.bHeap)[i]
		go bar.render(s.debugOut, tw)
	}

	if err := s.flush(s.bHeap.Len()); err != nil {
		fmt.Fprintf(s.debugOut, "%s %s %v\n", "[mpb]", time.Now(), err)
	}
}

func (s *pState) flush(lineCount int) error {
	for s.bHeap.Len() > 0 {
		bar := heap.Pop(s.bHeap).(*Bar)
		frameReader := <-bar.frameReaderCh
		defer func() {
			if frameReader.toShutdown {
				// shutdown at next flush, in other words decrement underlying WaitGroup
				// only after the bar with completed state has been flushed.
				// this ensures no bar ends up with less than 100% rendered.
				s.shutdownPending = append(s.shutdownPending, bar)
				if replacementBar, ok := s.waitBars[bar]; ok {
					heap.Push(s.bHeap, replacementBar)
					s.heapUpdated = true
					delete(s.waitBars, bar)
				}
				if frameReader.removeOnComplete {
					s.heapUpdated = true
					return
				}
			}
			heap.Push(s.bHeap, bar)
		}()
		s.cw.ReadFrom(frameReader)
		lineCount += frameReader.extendedLines
	}

	for i := len(s.shutdownPending) - 1; i >= 0; i-- {
		close(s.shutdownPending[i].shutdown)
		s.shutdownPending = s.shutdownPending[:i]
	}

	return s.cw.Flush(lineCount)
}

func syncWidth(matrix map[int][]chan int) {
	for _, column := range matrix {
		column := column
		go func() {
			var maxWidth int
			for _, ch := range column {
				w := <-ch
				if w > maxWidth {
					maxWidth = w
				}
			}
			for _, ch := range column {
				ch <- maxWidth
			}
		}()
	}
}
<|endoftext|>


<commit_before>package state

import (
	"time"

	"github.com/boltdb/bolt"
)

// KVStore provides a persistent KeyValue store
type KVStore struct {
	DbFileName string
	BucketName string
	db         *bolt.DB
}

// Init initialises the KeyValue store
func (k *KVStore) Init() error {
	var err error
	k.db, err = bolt.Open(k.DbFileName, 0600, &bolt.Options{Timeout: 1 * time.Second})
	if err != nil {
		return err
	}

	return k.db.Update(func(tx *bolt.Tx) error {
		_, err = tx.CreateBucket([]byte(k.BucketName))
		if err != nil {
			return err
		}
		return nil
	})
}

// Close closes the KeyValue Store ensuring it is persisted to disk
func (k *KVStore) Close() {
	k.db.Close()
}

// Set sets a Key to the defined value
func (k *KVStore) Set(key []byte, value []byte) error {
	return k.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(k.BucketName))
		err := b.Put(key, value)
		return err
	})
}

// Get retrieves the specified value
func (k *KVStore) Get(key []byte) []byte {
	var value []byte
	k.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(k.BucketName))
		value = b.Get(key)
		return nil
	})

	return value
}

// ForEach executes the function for each key/value pair
func (k *KVStore) ForEach(function func(k, v []byte) error) error {
	return k.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(k.BucketName))

		return b.ForEach(function)
	})
}

// Delete deletes the given key
func (k *KVStore) Delete(key []byte) {
	k.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(k.BucketName))
		b.Delete(key)
		return nil
	})
}
<commit_msg>Remove unnecessary assignment<commit_after>package state

import (
	"time"

	"github.com/boltdb/bolt"
)

// KVStore provides a persistent KeyValue store
type KVStore struct {
	DbFileName string
	BucketName string
	db         *bolt.DB
}

// Init initialises the KeyValue store
func (k *KVStore) Init() error {
	var err error
	k.db, err = bolt.Open(k.DbFileName, 0600, &bolt.Options{Timeout: 1 * time.Second})
	if err != nil {
		return err
	}

	return k.db.Update(func(tx *bolt.Tx) error {
		_, err = tx.CreateBucket([]byte(k.BucketName))
		if err != nil {
			return err
		}
		return nil
	})
}

// Close closes the KeyValue Store ensuring it is persisted to disk
func (k *KVStore) Close() {
	k.db.Close()
}

// Set sets a Key to the defined value
func (k *KVStore) Set(key []byte, value []byte) error {
	return k.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(k.BucketName))
		return b.Put(key, value)
	})
}

// Get retrieves the specified value
func (k *KVStore) Get(key []byte) []byte {
	var value []byte
	k.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(k.BucketName))
		value = b.Get(key)
		return nil
	})

	return value
}

// ForEach executes the function for each key/value pair
func (k *KVStore) ForEach(function func(k, v []byte) error) error {
	return k.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(k.BucketName))

		return b.ForEach(function)
	})
}

// Delete deletes the given key
func (k *KVStore) Delete(key []byte) {
	k.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(k.BucketName))
		b.Delete(key)
		return nil
	})
}
<|endoftext|>


<commit_before>/*
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.
*/

package proton

import "fmt"

// EventHandler handles core proton events.
type EventHandler interface {
	// HandleEvent is called with an event.
	// Typically HandleEvent() is implemented as a switch on e.Type()
	HandleEvent(e Event)
}

// MessagingHandler provides an alternative interface to EventHandler.
// it is easier to use for most applications that send and receive messages.
//
// Implement this interface and then wrap your value with a MessagingHandlerDelegator.
// MessagingHandlerDelegator implements EventHandler and can be registered with a Engine.
//
type MessagingHandler interface {
	// HandleMessagingEvent is called with  MessagingEvent.
	// Typically HandleEvent() is implemented as a switch on e.Type()
	HandleMessagingEvent(MessagingEvent, Event)
}

// MessagingEvent provides a set of events that are easier to work with than the
// core events defined by EventType
//
// There are 3 types of "endpoint": Connection, Session and Link.  For each
// endpoint there are 5 events: Opening, Opened, Closing, Closed and Error.
//
// The meaning of these events is as follows:
//
// Opening: The remote end opened, the local end will open automatically.
//
// Opened: Both ends are open, regardless of which end opened first.
//
// Closing: The remote end closed without error, the local end will close automatically.
//
// Error: The remote end closed with an error, the local end will close automatically.
//
// Closed: Both ends are closed, regardless of which end closed first or if there was an error.
// No further events will be received for the endpoint.
//
type MessagingEvent int

const (
	// The event loop starts.
	MStart MessagingEvent = iota
	// The peer closes the connection with an error condition.
	MConnectionError
	// The peer closes the session with an error condition.
	MSessionError
	// The peer closes the link with an error condition.
	MLinkError
	// The peer Initiates the opening of the connection.
	MConnectionOpening
	// The peer initiates the opening of the session.
	MSessionOpening
	// The peer initiates the opening of the link.
	MLinkOpening
	// The connection is opened.
	MConnectionOpened
	// The session is opened.
	MSessionOpened
	// The link is opened.
	MLinkOpened
	// The peer initiates the closing of the connection.
	MConnectionClosing
	// The peer initiates the closing of the session.
	MSessionClosing
	// The peer initiates the closing of the link.
	MLinkClosing
	// Both ends of the connection are closed.
	MConnectionClosed
	// Both ends of the session are closed.
	MSessionClosed
	// Both ends of the link are closed.
	MLinkClosed
	// The sender link has credit and messages can
	// therefore be transferred.
	MSendable
	// The remote peer accepts an outgoing message.
	MAccepted
	// The remote peer rejects an outgoing message.
	MRejected
	// The peer releases an outgoing message. Note that this may be in response to
	// either the RELEASE or MODIFIED state as defined by the AMQP specification.
	MReleased
	// The peer has settled the outgoing message. This is the point at which it
	// should never be re-transmitted.
	MSettled
	// A message is received. Call Event.Delivery().Message() to decode as an amqp.Message.
	// To manage the outcome of this messages (e.g. to accept or reject the message)
	// use Event.Delivery().
	MMessage
	// A network connection was disconnected.
	MDisconnected
)

func (t MessagingEvent) String() string {
	switch t {
	case MStart:
		return "Start"
	case MConnectionError:
		return "ConnectionError"
	case MSessionError:
		return "SessionError"
	case MLinkError:
		return "LinkError"
	case MConnectionOpening:
		return "ConnectionOpening"
	case MSessionOpening:
		return "SessionOpening"
	case MLinkOpening:
		return "LinkOpening"
	case MConnectionOpened:
		return "ConnectionOpened"
	case MSessionOpened:
		return "SessionOpened"
	case MLinkOpened:
		return "LinkOpened"
	case MConnectionClosing:
		return "ConnectionClosing"
	case MSessionClosing:
		return "SessionClosing"
	case MLinkClosing:
		return "LinkClosing"
	case MConnectionClosed:
		return "ConnectionClosed"
	case MSessionClosed:
		return "SessionClosed"
	case MLinkClosed:
		return "LinkClosed"
	case MDisconnected:
		return "Disconnected"
	case MSendable:
		return "Sendable"
	case MAccepted:
		return "Accepted"
	case MRejected:
		return "Rejected"
	case MReleased:
		return "Released"
	case MSettled:
		return "Settled"
	case MMessage:
		return "Message"
	default:
		return "Unknown"
	}
}

// ResourceHandler provides a simple way to track the creation and deletion of
// various proton objects.
// endpointDelegator captures common patterns for endpoints opening/closing
type endpointDelegator struct {
	remoteOpen, remoteClose, localOpen, localClose EventType
	opening, opened, closing, closed, error        MessagingEvent
	endpoint                                       func(Event) Endpoint
	delegator                                      *MessagingAdapter
}

// HandleEvent handles an open/close event for an endpoint in a generic way.
func (d endpointDelegator) HandleEvent(e Event) {
	endpoint := d.endpoint(e)
	state := endpoint.State()

	switch e.Type() {

	case d.localOpen:
		if state.RemoteActive() {
			d.delegator.mhandler.HandleMessagingEvent(d.opened, e)
		}

	case d.remoteOpen:
		d.delegator.mhandler.HandleMessagingEvent(d.opening, e)
		switch {
		case state.LocalActive():
			d.delegator.mhandler.HandleMessagingEvent(d.opened, e)
		case state.LocalUninit():
			if d.delegator.AutoOpen {
				endpoint.Open()
			}
		}

	case d.remoteClose:
		if endpoint.RemoteCondition().IsSet() { // Closed with error
			d.delegator.mhandler.HandleMessagingEvent(d.error, e)
		} else {
			d.delegator.mhandler.HandleMessagingEvent(d.closing, e)
		}
		if state.LocalClosed() {
			d.delegator.mhandler.HandleMessagingEvent(d.closed, e)
		} else if state.LocalActive() {
			endpoint.Close()
		}

	case d.localClose:
		if state.RemoteClosed() {
			d.delegator.mhandler.HandleMessagingEvent(d.closed, e)
		}

	default:
		// We shouldn't be called with any other event type.
		panic(fmt.Errorf("internal error, not an open/close event: %s", e))
	}
}

type flowcontroller struct {
	window, drained int
}

func (d flowcontroller) HandleEvent(e Event) {
	link := e.Link()

	switch e.Type() {
	case ELinkLocalOpen, ELinkRemoteOpen, ELinkFlow, EDelivery:
		if link.IsReceiver() {
			d.drained += link.Drained()
			if d.drained != 0 {
				link.Flow(d.window - link.Credit())
			}
		}
	}
}

// MessagingAdapter implements a EventHandler and delegates to a MessagingHandler.
// You can modify the exported fields before you pass the MessagingAdapter to
// a Engine.
type MessagingAdapter struct {
	mhandler                  MessagingHandler
	connection, session, link endpointDelegator
	flowcontroller            EventHandler

	// AutoSettle (default true) automatically pre-settle outgoing messages.
	AutoSettle bool
	// AutoAccept (default true) automatically accept and settle incoming messages
	// if they are not settled by the delegate.
	AutoAccept bool
	// AutoOpen (default true) automatically open remotely opened endpoints.
	AutoOpen bool
	// Prefetch (default 10) initial credit to issue for incoming links.
	Prefetch int
	// PeerCloseIsError (default false) if true a close by the peer will be treated as an error.
	PeerCloseError bool
}

func NewMessagingAdapter(h MessagingHandler) *MessagingAdapter {
	return &MessagingAdapter{
		mhandler:       h,
		flowcontroller: nil,
		AutoSettle:     true,
		AutoAccept:     true,
		AutoOpen:       true,
		Prefetch:       10,
		PeerCloseError: false,
	}
}

func handleIf(h EventHandler, e Event) {
	if h != nil {
		h.HandleEvent(e)
	}
}

// Handle a proton event by passing the corresponding MessagingEvent(s) to
// the MessagingHandler.
func (d *MessagingAdapter) HandleEvent(e Event) {
	handleIf(d.flowcontroller, e)

	switch e.Type() {

	case EConnectionInit:
		d.connection = endpointDelegator{
			EConnectionRemoteOpen, EConnectionRemoteClose, EConnectionLocalOpen, EConnectionLocalClose,
			MConnectionOpening, MConnectionOpened, MConnectionClosing, MConnectionClosed,
			MConnectionError,
			func(e Event) Endpoint { return e.Connection() },
			d,
		}
		d.session = endpointDelegator{
			ESessionRemoteOpen, ESessionRemoteClose, ESessionLocalOpen, ESessionLocalClose,
			MSessionOpening, MSessionOpened, MSessionClosing, MSessionClosed,
			MSessionError,
			func(e Event) Endpoint { return e.Session() },
			d,
		}
		d.link = endpointDelegator{
			ELinkRemoteOpen, ELinkRemoteClose, ELinkLocalOpen, ELinkLocalClose,
			MLinkOpening, MLinkOpened, MLinkClosing, MLinkClosed,
			MLinkError,
			func(e Event) Endpoint { return e.Link() },
			d,
		}
		if d.Prefetch > 0 {
			d.flowcontroller = flowcontroller{window: d.Prefetch, drained: 0}
		}
		d.mhandler.HandleMessagingEvent(MStart, e)

	case EConnectionRemoteOpen:

		d.connection.HandleEvent(e)

	case EConnectionRemoteClose:
		d.connection.HandleEvent(e)
		e.Connection().Transport().CloseTail()

	case EConnectionLocalOpen, EConnectionLocalClose:
		d.connection.HandleEvent(e)

	case ESessionRemoteOpen, ESessionRemoteClose, ESessionLocalOpen, ESessionLocalClose:
		d.session.HandleEvent(e)

	case ELinkRemoteOpen:
		e.Link().Source().Copy(e.Link().RemoteSource())
		e.Link().Target().Copy(e.Link().RemoteTarget())
		d.link.HandleEvent(e)

	case ELinkRemoteClose, ELinkLocalOpen, ELinkLocalClose:
		d.link.HandleEvent(e)

	case ELinkFlow:
		if e.Link().IsSender() && e.Link().Credit() > 0 {
			d.mhandler.HandleMessagingEvent(MSendable, e)
		}

	case EDelivery:
		if e.Delivery().Link().IsReceiver() {
			d.incoming(e)
		} else {
			d.outgoing(e)
		}

	case ETransportTailClosed:
		if !e.Connection().State().RemoteClosed() { // Unexpected transport closed
			e.Transport().CloseHead() // Complete transport close, no connection close expected
		}

	case ETransportClosed:
		d.mhandler.HandleMessagingEvent(MDisconnected, e)
	}
}

func (d *MessagingAdapter) incoming(e Event) {
	delivery := e.Delivery()
	if delivery.HasMessage() {
		d.mhandler.HandleMessagingEvent(MMessage, e)
		if d.AutoAccept && !delivery.Settled() {
			delivery.Accept()
		}
		if delivery.Current() {
			e.Link().Advance()
		}
	} else if delivery.Updated() && delivery.Settled() {
		d.mhandler.HandleMessagingEvent(MSettled, e)
	}
	return
}

func (d *MessagingAdapter) outgoing(e Event) {
	delivery := e.Delivery()
	if delivery.Updated() {
		switch delivery.Remote().Type() {
		case Accepted:
			d.mhandler.HandleMessagingEvent(MAccepted, e)
		case Rejected:
			d.mhandler.HandleMessagingEvent(MRejected, e)
		case Released, Modified:
			d.mhandler.HandleMessagingEvent(MReleased, e)
		}
		if delivery.Settled() {
			// The delivery was settled remotely, inform the local end.
			d.mhandler.HandleMessagingEvent(MSettled, e)
		}
		if d.AutoSettle {
			delivery.Settle() // Local settle, don't mhandler MSettled till the remote end settles.
		}
	}
	return
}
<commit_msg>PROTON-2063: [go] Sender auto settlement should only happen after receiver settlement<commit_after>/*
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.
*/

package proton

import "fmt"

// EventHandler handles core proton events.
type EventHandler interface {
	// HandleEvent is called with an event.
	// Typically HandleEvent() is implemented as a switch on e.Type()
	HandleEvent(e Event)
}

// MessagingHandler provides an alternative interface to EventHandler.
// it is easier to use for most applications that send and receive messages.
//
// Implement this interface and then wrap your value with a MessagingHandlerDelegator.
// MessagingHandlerDelegator implements EventHandler and can be registered with a Engine.
//
type MessagingHandler interface {
	// HandleMessagingEvent is called with  MessagingEvent.
	// Typically HandleEvent() is implemented as a switch on e.Type()
	HandleMessagingEvent(MessagingEvent, Event)
}

// MessagingEvent provides a set of events that are easier to work with than the
// core events defined by EventType
//
// There are 3 types of "endpoint": Connection, Session and Link.  For each
// endpoint there are 5 events: Opening, Opened, Closing, Closed and Error.
//
// The meaning of these events is as follows:
//
// Opening: The remote end opened, the local end will open automatically.
//
// Opened: Both ends are open, regardless of which end opened first.
//
// Closing: The remote end closed without error, the local end will close automatically.
//
// Error: The remote end closed with an error, the local end will close automatically.
//
// Closed: Both ends are closed, regardless of which end closed first or if there was an error.
// No further events will be received for the endpoint.
//
type MessagingEvent int

const (
	// The event loop starts.
	MStart MessagingEvent = iota
	// The peer closes the connection with an error condition.
	MConnectionError
	// The peer closes the session with an error condition.
	MSessionError
	// The peer closes the link with an error condition.
	MLinkError
	// The peer Initiates the opening of the connection.
	MConnectionOpening
	// The peer initiates the opening of the session.
	MSessionOpening
	// The peer initiates the opening of the link.
	MLinkOpening
	// The connection is opened.
	MConnectionOpened
	// The session is opened.
	MSessionOpened
	// The link is opened.
	MLinkOpened
	// The peer initiates the closing of the connection.
	MConnectionClosing
	// The peer initiates the closing of the session.
	MSessionClosing
	// The peer initiates the closing of the link.
	MLinkClosing
	// Both ends of the connection are closed.
	MConnectionClosed
	// Both ends of the session are closed.
	MSessionClosed
	// Both ends of the link are closed.
	MLinkClosed
	// The sender link has credit and messages can
	// therefore be transferred.
	MSendable
	// The remote peer accepts an outgoing message.
	MAccepted
	// The remote peer rejects an outgoing message.
	MRejected
	// The peer releases an outgoing message. Note that this may be in response to
	// either the RELEASE or MODIFIED state as defined by the AMQP specification.
	MReleased
	// The peer has settled the outgoing message. This is the point at which it
	// should never be re-transmitted.
	MSettled
	// A message is received. Call Event.Delivery().Message() to decode as an amqp.Message.
	// To manage the outcome of this messages (e.g. to accept or reject the message)
	// use Event.Delivery().
	MMessage
	// A network connection was disconnected.
	MDisconnected
)

func (t MessagingEvent) String() string {
	switch t {
	case MStart:
		return "Start"
	case MConnectionError:
		return "ConnectionError"
	case MSessionError:
		return "SessionError"
	case MLinkError:
		return "LinkError"
	case MConnectionOpening:
		return "ConnectionOpening"
	case MSessionOpening:
		return "SessionOpening"
	case MLinkOpening:
		return "LinkOpening"
	case MConnectionOpened:
		return "ConnectionOpened"
	case MSessionOpened:
		return "SessionOpened"
	case MLinkOpened:
		return "LinkOpened"
	case MConnectionClosing:
		return "ConnectionClosing"
	case MSessionClosing:
		return "SessionClosing"
	case MLinkClosing:
		return "LinkClosing"
	case MConnectionClosed:
		return "ConnectionClosed"
	case MSessionClosed:
		return "SessionClosed"
	case MLinkClosed:
		return "LinkClosed"
	case MDisconnected:
		return "Disconnected"
	case MSendable:
		return "Sendable"
	case MAccepted:
		return "Accepted"
	case MRejected:
		return "Rejected"
	case MReleased:
		return "Released"
	case MSettled:
		return "Settled"
	case MMessage:
		return "Message"
	default:
		return "Unknown"
	}
}

// ResourceHandler provides a simple way to track the creation and deletion of
// various proton objects.
// endpointDelegator captures common patterns for endpoints opening/closing
type endpointDelegator struct {
	remoteOpen, remoteClose, localOpen, localClose EventType
	opening, opened, closing, closed, error        MessagingEvent
	endpoint                                       func(Event) Endpoint
	delegator                                      *MessagingAdapter
}

// HandleEvent handles an open/close event for an endpoint in a generic way.
func (d endpointDelegator) HandleEvent(e Event) {
	endpoint := d.endpoint(e)
	state := endpoint.State()

	switch e.Type() {

	case d.localOpen:
		if state.RemoteActive() {
			d.delegator.mhandler.HandleMessagingEvent(d.opened, e)
		}

	case d.remoteOpen:
		d.delegator.mhandler.HandleMessagingEvent(d.opening, e)
		switch {
		case state.LocalActive():
			d.delegator.mhandler.HandleMessagingEvent(d.opened, e)
		case state.LocalUninit():
			if d.delegator.AutoOpen {
				endpoint.Open()
			}
		}

	case d.remoteClose:
		if endpoint.RemoteCondition().IsSet() { // Closed with error
			d.delegator.mhandler.HandleMessagingEvent(d.error, e)
		} else {
			d.delegator.mhandler.HandleMessagingEvent(d.closing, e)
		}
		if state.LocalClosed() {
			d.delegator.mhandler.HandleMessagingEvent(d.closed, e)
		} else if state.LocalActive() {
			endpoint.Close()
		}

	case d.localClose:
		if state.RemoteClosed() {
			d.delegator.mhandler.HandleMessagingEvent(d.closed, e)
		}

	default:
		// We shouldn't be called with any other event type.
		panic(fmt.Errorf("internal error, not an open/close event: %s", e))
	}
}

type flowcontroller struct {
	window, drained int
}

func (d flowcontroller) HandleEvent(e Event) {
	link := e.Link()

	switch e.Type() {
	case ELinkLocalOpen, ELinkRemoteOpen, ELinkFlow, EDelivery:
		if link.IsReceiver() {
			d.drained += link.Drained()
			if d.drained != 0 {
				link.Flow(d.window - link.Credit())
			}
		}
	}
}

// MessagingAdapter implements a EventHandler and delegates to a MessagingHandler.
// You can modify the exported fields before you pass the MessagingAdapter to
// a Engine.
type MessagingAdapter struct {
	mhandler                  MessagingHandler
	connection, session, link endpointDelegator
	flowcontroller            EventHandler

	// AutoSettle (default true) automatically pre-settle outgoing messages.
	AutoSettle bool
	// AutoAccept (default true) automatically accept and settle incoming messages
	// if they are not settled by the delegate.
	AutoAccept bool
	// AutoOpen (default true) automatically open remotely opened endpoints.
	AutoOpen bool
	// Prefetch (default 10) initial credit to issue for incoming links.
	Prefetch int
	// PeerCloseIsError (default false) if true a close by the peer will be treated as an error.
	PeerCloseError bool
}

func NewMessagingAdapter(h MessagingHandler) *MessagingAdapter {
	return &MessagingAdapter{
		mhandler:       h,
		flowcontroller: nil,
		AutoSettle:     true,
		AutoAccept:     true,
		AutoOpen:       true,
		Prefetch:       10,
		PeerCloseError: false,
	}
}

func handleIf(h EventHandler, e Event) {
	if h != nil {
		h.HandleEvent(e)
	}
}

// Handle a proton event by passing the corresponding MessagingEvent(s) to
// the MessagingHandler.
func (d *MessagingAdapter) HandleEvent(e Event) {
	handleIf(d.flowcontroller, e)

	switch e.Type() {

	case EConnectionInit:
		d.connection = endpointDelegator{
			EConnectionRemoteOpen, EConnectionRemoteClose, EConnectionLocalOpen, EConnectionLocalClose,
			MConnectionOpening, MConnectionOpened, MConnectionClosing, MConnectionClosed,
			MConnectionError,
			func(e Event) Endpoint { return e.Connection() },
			d,
		}
		d.session = endpointDelegator{
			ESessionRemoteOpen, ESessionRemoteClose, ESessionLocalOpen, ESessionLocalClose,
			MSessionOpening, MSessionOpened, MSessionClosing, MSessionClosed,
			MSessionError,
			func(e Event) Endpoint { return e.Session() },
			d,
		}
		d.link = endpointDelegator{
			ELinkRemoteOpen, ELinkRemoteClose, ELinkLocalOpen, ELinkLocalClose,
			MLinkOpening, MLinkOpened, MLinkClosing, MLinkClosed,
			MLinkError,
			func(e Event) Endpoint { return e.Link() },
			d,
		}
		if d.Prefetch > 0 {
			d.flowcontroller = flowcontroller{window: d.Prefetch, drained: 0}
		}
		d.mhandler.HandleMessagingEvent(MStart, e)

	case EConnectionRemoteOpen:

		d.connection.HandleEvent(e)

	case EConnectionRemoteClose:
		d.connection.HandleEvent(e)
		e.Connection().Transport().CloseTail()

	case EConnectionLocalOpen, EConnectionLocalClose:
		d.connection.HandleEvent(e)

	case ESessionRemoteOpen, ESessionRemoteClose, ESessionLocalOpen, ESessionLocalClose:
		d.session.HandleEvent(e)

	case ELinkRemoteOpen:
		e.Link().Source().Copy(e.Link().RemoteSource())
		e.Link().Target().Copy(e.Link().RemoteTarget())
		d.link.HandleEvent(e)

	case ELinkRemoteClose, ELinkLocalOpen, ELinkLocalClose:
		d.link.HandleEvent(e)

	case ELinkFlow:
		if e.Link().IsSender() && e.Link().Credit() > 0 {
			d.mhandler.HandleMessagingEvent(MSendable, e)
		}

	case EDelivery:
		if e.Delivery().Link().IsReceiver() {
			d.incoming(e)
		} else {
			d.outgoing(e)
		}

	case ETransportTailClosed:
		if !e.Connection().State().RemoteClosed() { // Unexpected transport closed
			e.Transport().CloseHead() // Complete transport close, no connection close expected
		}

	case ETransportClosed:
		d.mhandler.HandleMessagingEvent(MDisconnected, e)
	}
}

func (d *MessagingAdapter) incoming(e Event) {
	delivery := e.Delivery()
	if delivery.HasMessage() {
		d.mhandler.HandleMessagingEvent(MMessage, e)
		if d.AutoAccept && !delivery.Settled() {
			delivery.Accept()
		}
		if delivery.Current() {
			e.Link().Advance()
		}
	} else if delivery.Updated() && delivery.Settled() {
		d.mhandler.HandleMessagingEvent(MSettled, e)
	}
	return
}

func (d *MessagingAdapter) outgoing(e Event) {
	delivery := e.Delivery()
	if delivery.Updated() {
		switch delivery.Remote().Type() {
		case Accepted:
			d.mhandler.HandleMessagingEvent(MAccepted, e)
		case Rejected:
			d.mhandler.HandleMessagingEvent(MRejected, e)
		case Released, Modified:
			d.mhandler.HandleMessagingEvent(MReleased, e)
		}
		if delivery.Settled() {
			// The delivery was settled remotely, inform the local end.
			d.mhandler.HandleMessagingEvent(MSettled, e)
			if d.AutoSettle {
				delivery.Settle()
			}
		}
	}
	return
}
<|endoftext|>


<commit_before>// Package goublu UbluManager is the gocui manager for Ublu input and output.
package goublu

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"strings"

	"github.com/jwoehr/gocui"
	"github.com/jwoehr/termbox-go"
)

// How far from bottom we reserve our input area
const inputLineOffset = 3

// UbluManager is a gocui Manager for Ublu io.
type UbluManager struct {
	U                 *Ublu
	G                 *gocui.Gui
	Opts              *Options
	Hist              *History
	CommandLineEditor gocui.Editor
	Completor         *Completor
}

// NewUbluManager instances a new manager.
func NewUbluManager(ublu *Ublu, g *gocui.Gui, opts *Options, hist *History) (um *UbluManager) {
	um = &UbluManager{
		U:         ublu,
		G:         g,
		Opts:      opts,
		Hist:      hist,
		Completor: NewCompletor(),
	}
	um.CommandLineEditor = gocui.EditorFunc(func(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) {
		cx, cy := v.Cursor()
		text, _ := v.Line(cy)
		text = strings.Trim(strings.TrimSpace(text), "\000")

		switch {
		case ch != 0 && mod == 0:
			v.EditWrite(ch)
		case key == gocui.KeySpace:
			v.EditWrite(' ')
		case key == gocui.KeyBackspace || key == gocui.KeyBackspace2:
			v.EditDelete(true)
		case key == gocui.KeyDelete:
			v.EditDelete(false)
		case key == gocui.KeyInsert:
			v.Overwrite = !v.Overwrite
		case key == gocui.KeyEnter:
			um.Ubluin(um.G, v)
			termbox.Interrupt() // for good luck
		case key == gocui.KeyArrowDown:
			replaceLine(v, cx, um.Hist.Forward())
			// v.Clear()
			// v.MoveCursor(0-cx, 0, false)
			// for _, ch := range um.Hist.Forward() {
			// 	v.EditWrite(ch)
			// }
		case key == gocui.KeyArrowUp:
			replaceLine(v, cx, um.Hist.Back())
			// v.Clear()
			// v.MoveCursor(0-cx, 0, false)
			// for _, ch := range um.Hist.Back() {
			// 	v.EditWrite(ch)
			// }
		case key == gocui.KeyPgup:
			replaceLine(v, cx, um.Hist.First())
			// v.Clear()
			// v.MoveCursor(0-cx, 0, false)
			// for _, ch := range um.Hist.First() {
			// 	v.EditWrite(ch)
			// }
		case key == gocui.KeyPgdn:
			replaceLine(v, cx, um.Hist.Last())
			// v.Clear()
			// v.MoveCursor(0-cx, 0, false)
			// for _, ch := range um.Hist.Last() {
			// 	v.EditWrite(ch)
			// }
		case key == gocui.KeyArrowLeft:
			v.MoveCursor(-1, 0, false)
		case key == gocui.KeyArrowRight:
			v.MoveCursor(1, 0, false)
		case key == gocui.KeyCtrlSpace:
			replaceLine(v, cx, um.tryComplete(text))
			// newtext := um.tryComplete(text)
			// v.Clear()
			// v.MoveCursor(0-cx, 0, false)
			// for _, ch := range newtext {
			// 	v.EditWrite(ch)
			// }
		case key == gocui.KeyCtrlA || key == gocui.KeyHome:
			v.MoveCursor(0-cx, 0, false)
		case key == gocui.KeyCtrlB:
			v.MoveCursor(-1, 0, false)
		case key == gocui.KeyCtrlE || key == gocui.KeyEnd:
			v.MoveCursor(len(text)-cx, 0, false)
		case key == gocui.KeyCtrlF:
			v.MoveCursor(1, 0, false)
		case key == gocui.KeyCtrlK:
			// this isn't quite correct but sorta works
			for i := cy; i < len(text); i++ {
				v.EditDelete(false)
			}
		case key == gocui.KeyF1:
			rm := NewHelpReq(um, um.G)
			rm.StartReq()
		case key == gocui.KeyF2:
			rm := NewAllOutReq(um, um.G)
			rm.StartReq()
		case key == gocui.KeyF4:
			f, err := ioutil.TempFile(um.Opts.SaveOutDir, "goublu.out.")
			if err != nil {
				log.Panicln(err)
			}
			um.Ubluout(um.G, "Saving output to "+f.Name()+"\n")
			f.Write([]byte(um.Hist.AllOut))
			f.Close()
		case key == gocui.KeyF5:
			replaceLine(v, cx, um.tryExpand(text))
			// newtext := um.tryExpand(text)
			// v.Clear()
			// v.MoveCursor(0-cx, 0, false)
			// for _, ch := range newtext {
			// 	v.EditWrite(ch)
			// }
		case key == gocui.KeyF9:
			replaceLine(v, cx, um.Hist.BackWrap())
			// v.Clear()
			// v.MoveCursor(0-cx, 0, false)
			// for _, ch := range um.Hist.BackWrap() {
			// 	v.EditWrite(ch)
			// }
		case key == gocui.MouseLeft:
		case key == gocui.MouseMiddle:
		case key == gocui.MouseRight:
		case key == gocui.MouseRelease:
		case key == gocui.MouseWheelUp:
		case key == gocui.MouseWheelDown:
		}
		if key != gocui.KeyCtrlSpace {
			um.Completor.Valid = false
		}
	})

	return um
}

// Ubluin pipes input to Ublu.
func (um *UbluManager) Ubluin(g *gocui.Gui, v *gocui.View) {
	var l string
	var err error
	cx, cy := v.Cursor()
	_, gy := g.Size()
	if l, err = v.Line(cy); err != nil {
		l = ""
	}
	l = strings.Trim(strings.TrimSpace(l), "\000")
	um.Ubluout(g, l+"\n")
	io.WriteString(um.U.Stdin, l+"\n")
	if l != "" {
		um.Hist.Append(l)
	}
	v.Clear()
	v.MoveCursor(0-cx, (gy-inputLineOffset)-cy, false)
}

// Ubluout writes to console output from Ublu.
func (um *UbluManager) Ubluout(g *gocui.Gui, text string) {
	v, err := g.View("Ubluout")
	if err != nil {
		// handle error
	}
	fmt.Fprint(v, text)
	um.Hist.AppendAllOut(text)
	termbox.Interrupt()
}

// Layout is the obligatory gocui layout redraw function
func (um *UbluManager) Layout(g *gocui.Gui) error {
	maxX, maxY := g.Size()
	if v, err := g.SetView("Ubluout", 0, 0, maxX-1, maxY-inputLineOffset); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}
		v.Title = " Ublu Output  [F1 Goublu Help] [F2 Review Out] [F4 Save Out] [F5 Macro] [F9 Prev Cmd] "
		v.Autoscroll = true
		v.Wrap = true
		v.BgColor = um.Opts.BgColorOut
		v.FgColor = um.Opts.FgColorOut
	}
	if v, err := g.SetView("Ubluin", 0, maxY-inputLineOffset, maxX-1, maxY-1); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}
		v.Title = " Ublu Input "
		v.Autoscroll = true
		v.Editable = true
		v.Editor = um.CommandLineEditor
		v.Wrap = true
		v.BgColor = um.Opts.BgColorIn
		v.FgColor = um.Opts.FgColorIn
	}
	if _, err := g.SetCurrentView("Ubluin"); err != nil {
		return err
	}
	return nil
}

func (um *UbluManager) tryComplete(text string) (newtext string) {
	newtext = text
	if um.Completor.Valid == false {
		um.Completor.Clear()
	}
	if text != "" {
		words := strings.Fields(text)
		lastword := words[len(words)-1]
		candidate := um.Completor.Next()
		if candidate == "" {
			candidate = um.Completor.Complete(lastword)
		}
		if candidate != "" {
			newtext = text[0:strings.LastIndex(text, lastword)] + candidate
		}
	}
	return newtext
}

func (um *UbluManager) tryExpand(text string) (newtext string) {
	newtext = text
	if text != "" {
		words := strings.Fields(text)
		lastword := words[len(words)-1]
		candidate := um.Opts.Macros.Expand(lastword)
		if candidate != "" {
			newtext = text[0:strings.LastIndex(text, lastword)] + candidate
		}
	}
	return newtext
}

func replaceLine(v *gocui.View, cx int, newtext string) {
	v.Clear()
	v.MoveCursor(0-cx, 0, false)
	for _, ch := range newtext {
		v.EditWrite(ch)
	}
}
<commit_msg>cleanup<commit_after>// Package goublu UbluManager is the gocui manager for Ublu input and output.
package goublu

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"strings"

	"github.com/jwoehr/gocui"
	"github.com/jwoehr/termbox-go"
)

// How far from bottom we reserve our input area
const inputLineOffset = 3

// UbluManager is a gocui Manager for Ublu io.
type UbluManager struct {
	U                 *Ublu
	G                 *gocui.Gui
	Opts              *Options
	Hist              *History
	CommandLineEditor gocui.Editor
	Completor         *Completor
}

// NewUbluManager instances a new manager.
func NewUbluManager(ublu *Ublu, g *gocui.Gui, opts *Options, hist *History) (um *UbluManager) {
	um = &UbluManager{
		U:         ublu,
		G:         g,
		Opts:      opts,
		Hist:      hist,
		Completor: NewCompletor(),
	}
	um.CommandLineEditor = gocui.EditorFunc(func(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) {
		cx, cy := v.Cursor()
		text, _ := v.Line(cy)
		text = strings.Trim(strings.TrimSpace(text), "\000")

		switch {
		case ch != 0 && mod == 0:
			v.EditWrite(ch)
		case key == gocui.KeySpace:
			v.EditWrite(' ')
		case key == gocui.KeyBackspace || key == gocui.KeyBackspace2:
			v.EditDelete(true)
		case key == gocui.KeyDelete:
			v.EditDelete(false)
		case key == gocui.KeyInsert:
			v.Overwrite = !v.Overwrite
		case key == gocui.KeyEnter:
			um.Ubluin(um.G, v)
			termbox.Interrupt() // for good luck
		case key == gocui.KeyArrowDown:
			replaceLine(v, cx, um.Hist.Forward())
		case key == gocui.KeyArrowUp:
			replaceLine(v, cx, um.Hist.Back())
		case key == gocui.KeyPgup:
			replaceLine(v, cx, um.Hist.First())
		case key == gocui.KeyPgdn:
			replaceLine(v, cx, um.Hist.Last())
		case key == gocui.KeyArrowLeft:
			v.MoveCursor(-1, 0, false)
		case key == gocui.KeyArrowRight:
			v.MoveCursor(1, 0, false)
		case key == gocui.KeyCtrlSpace:
			replaceLine(v, cx, um.tryComplete(text))
		case key == gocui.KeyCtrlA || key == gocui.KeyHome:
			v.MoveCursor(0-cx, 0, false)
		case key == gocui.KeyCtrlB:
			v.MoveCursor(-1, 0, false)
		case key == gocui.KeyCtrlE || key == gocui.KeyEnd:
			v.MoveCursor(len(text)-cx, 0, false)
		case key == gocui.KeyCtrlF:
			v.MoveCursor(1, 0, false)
		case key == gocui.KeyCtrlK:
			// this isn't quite correct but sorta works
			for i := cy; i < len(text); i++ {
				v.EditDelete(false)
			}
		case key == gocui.KeyF1:
			rm := NewHelpReq(um, um.G)
			rm.StartReq()
		case key == gocui.KeyF2:
			rm := NewAllOutReq(um, um.G)
			rm.StartReq()
		case key == gocui.KeyF4:
			f, err := ioutil.TempFile(um.Opts.SaveOutDir, "goublu.out.")
			if err != nil {
				log.Panicln(err)
			}
			um.Ubluout(um.G, "Saving output to "+f.Name()+"\n")
			f.Write([]byte(um.Hist.AllOut))
			f.Close()
		case key == gocui.KeyF5:
			replaceLine(v, cx, um.tryExpand(text))
		case key == gocui.KeyF9:
			replaceLine(v, cx, um.Hist.BackWrap())
		case key == gocui.MouseLeft:
		case key == gocui.MouseMiddle:
		case key == gocui.MouseRight:
		case key == gocui.MouseRelease:
		case key == gocui.MouseWheelUp:
		case key == gocui.MouseWheelDown:
		}
		if key != gocui.KeyCtrlSpace {
			um.Completor.Valid = false
		}
	})

	return um
}

// Ubluin pipes input to Ublu.
func (um *UbluManager) Ubluin(g *gocui.Gui, v *gocui.View) {
	var l string
	var err error
	cx, cy := v.Cursor()
	_, gy := g.Size()
	if l, err = v.Line(cy); err != nil {
		l = ""
	}
	l = strings.Trim(strings.TrimSpace(l), "\000")
	um.Ubluout(g, l+"\n")
	io.WriteString(um.U.Stdin, l+"\n")
	if l != "" {
		um.Hist.Append(l)
	}
	v.Clear()
	v.MoveCursor(0-cx, (gy-inputLineOffset)-cy, false)
}

// Ubluout writes to console output from Ublu.
func (um *UbluManager) Ubluout(g *gocui.Gui, text string) {
	v, err := g.View("Ubluout")
	if err != nil {
		// handle error
	}
	fmt.Fprint(v, text)
	um.Hist.AppendAllOut(text)
	termbox.Interrupt()
}

// Layout is the obligatory gocui layout redraw function
func (um *UbluManager) Layout(g *gocui.Gui) error {
	maxX, maxY := g.Size()
	if v, err := g.SetView("Ubluout", 0, 0, maxX-1, maxY-inputLineOffset); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}
		v.Title = " Ublu Output  [F1 Goublu Help] [F2 Review Out] [F4 Save Out] [F5 Macro] [F9 Prev Cmd] "
		v.Autoscroll = true
		v.Wrap = true
		v.BgColor = um.Opts.BgColorOut
		v.FgColor = um.Opts.FgColorOut
	}
	if v, err := g.SetView("Ubluin", 0, maxY-inputLineOffset, maxX-1, maxY-1); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}
		v.Title = " Ublu Input "
		v.Autoscroll = true
		v.Editable = true
		v.Editor = um.CommandLineEditor
		v.Wrap = true
		v.BgColor = um.Opts.BgColorIn
		v.FgColor = um.Opts.FgColorIn
	}
	if _, err := g.SetCurrentView("Ubluin"); err != nil {
		return err
	}
	return nil
}

func (um *UbluManager) tryComplete(text string) (newtext string) {
	newtext = text
	if um.Completor.Valid == false {
		um.Completor.Clear()
	}
	if text != "" {
		words := strings.Fields(text)
		lastword := words[len(words)-1]
		candidate := um.Completor.Next()
		if candidate == "" {
			candidate = um.Completor.Complete(lastword)
		}
		if candidate != "" {
			newtext = text[0:strings.LastIndex(text, lastword)] + candidate
		}
	}
	return newtext
}

func (um *UbluManager) tryExpand(text string) (newtext string) {
	newtext = text
	if text != "" {
		words := strings.Fields(text)
		lastword := words[len(words)-1]
		candidate := um.Opts.Macros.Expand(lastword)
		if candidate != "" {
			newtext = text[0:strings.LastIndex(text, lastword)] + candidate
		}
	}
	return newtext
}

func replaceLine(v *gocui.View, cx int, newtext string) {
	v.Clear()
	v.MoveCursor(0-cx, 0, false)
	for _, ch := range newtext {
		v.EditWrite(ch)
	}
}
<|endoftext|>