<commit_before>package pixel

import (
	"fmt"
	"image/color"
)

// TrianglesData specifies a list of Triangles vertices with three common properties: Position,
// Color and Texture.
type TrianglesData []struct {
	Position Vec
	Color    NRGBA
	Texture  Vec
}

// Len returns the number of vertices in TrianglesData.
func (td *TrianglesData) Len() int {
	return len(*td)
}

// Draw is unimplemented for TrianglesData and panics.
func (td *TrianglesData) Draw() {
	panic(fmt.Errorf("%T.Draw: invalid operation", td))
}

// Update copies vertex properties from the supplied Triangles into this TrianglesData.
//
// TrianglesPosition, TrianglesColor and TrianglesTexture are supported.
func (td *TrianglesData) Update(t Triangles) {
	if t.Len() > td.Len() {
		*td = append(*td, make(TrianglesData, t.Len()-td.Len())...)
	}
	if t.Len() < td.Len() {
		*td = (*td)[:t.Len()]
	}

	// fast path optimization
	if t, ok := t.(*TrianglesData); ok {
		copy(*td, *t)
		return
	}
	if t, ok := t.(*TrianglesColorData); ok {
		for i := range *td {
			(*td)[i].Position = (*t)[i].Position
			(*td)[i].Color = (*t)[i].Color
		}
		return
	}
	if t, ok := t.(*TrianglesTextureData); ok {
		for i := range *td {
			(*td)[i].Position = (*t)[i].Position
			(*td)[i].Texture = (*t)[i].Texture
		}
		return
	}

	// slow path manual copy
	if t, ok := t.(TrianglesPosition); ok {
		for i := range *td {
			(*td)[i].Position = t.Position(i)
		}
	}
	if t, ok := t.(TrianglesColor); ok {
		for i := range *td {
			(*td)[i].Color = NRGBAModel.Convert(t.Color(i)).(NRGBA)
		}
	}
	if t, ok := t.(TrianglesTexture); ok {
		for i := range *td {
			(*td)[i].Texture = t.Texture(i)
		}
	}
}

// Append adds supplied Triangles to the end of the TrianglesData.
func (td *TrianglesData) Append(t Triangles) {
	newTd := make(TrianglesData, t.Len())
	newTd.Update(t)
	*td = append(*td, newTd...)
}

// Copy returns an exact independent copy of this TrianglesData.
func (td *TrianglesData) Copy() Triangles {
	copyTd := make(TrianglesData, td.Len())
	copyTd.Update(td)
	return &copyTd
}

// Position returns the position property of i-th vertex.
func (td *TrianglesData) Position(i int) Vec {
	return (*td)[i].Position
}

// Color returns the color property of i-th vertex.
func (td *TrianglesData) Color(i int) color.Color {
	return (*td)[i].Color
}

// Texture returns the texture property of i-th vertex.
func (td *TrianglesData) Texture(i int) Vec {
	return (*td)[i].Texture
}

// TrianglesColorData is same as TrianglesData, except is lacks Texture property.
type TrianglesColorData TrianglesData

// Len returns the number of vertices in TrianglesColorData.
func (td *TrianglesColorData) Len() int {
	return (*TrianglesData)(td).Len()
}

// Draw is unimplemented for TrianglesColorData and panics.
func (td *TrianglesColorData) Draw() {
	(*TrianglesData)(td).Draw()
}

// Update copies vertex properties from the supplied Triangles into this TrianglesColorData.
func (td *TrianglesColorData) Update(t Triangles) {
	(*TrianglesData)(td).Update(t)
}

// Append adds supplied Triangles to the end of the TrianglesColorData.
func (td *TrianglesColorData) Append(t Triangles) {
	(*TrianglesData)(td).Append(t)
}

// Copy returns an exact independent copy of this TrianglesColorData.
func (td *TrianglesColorData) Copy() Triangles {
	return (*TrianglesColorData)((*TrianglesData)(td).Copy().(*TrianglesData))
}

// Position returns the position property of i-th vertex.
func (td *TrianglesColorData) Position(i int) Vec {
	return (*TrianglesData)(td).Position(i)
}

// Color returns the color property of i-th vertex.
func (td *TrianglesColorData) Color(i int) color.Color {
	return (*TrianglesData)(td).Color(i)
}

// TrianglesTextureData is same as TrianglesData, except is lacks Color property.
type TrianglesTextureData TrianglesData

// Len returns the number of vertices in TrianglesTextureData.
func (td *TrianglesTextureData) Len() int {
	return (*TrianglesData)(td).Len()
}

// Draw is unimplemented for TrianglesTextureData and panics.
func (td *TrianglesTextureData) Draw() {
	(*TrianglesData)(td).Draw()
}

// Update copies vertex properties from the supplied Triangles into this TrianglesTextureData.
func (td *TrianglesTextureData) Update(t Triangles) {
	(*TrianglesData)(td).Update(t)
}

// Append adds supplied Triangles to the end of the TrianglesTextureData.
func (td *TrianglesTextureData) Append(t Triangles) {
	(*TrianglesData)(td).Append(t)
}

// Copy returns an exact independent copy of this TrianglesTextureData.
func (td *TrianglesTextureData) Copy() Triangles {
	return (*TrianglesTextureData)((*TrianglesData)(td).Copy().(*TrianglesData))
}

// Position returns the position property of i-th vertex.
func (td *TrianglesTextureData) Position(i int) Vec {
	return (*TrianglesData)(td).Position(i)
}

// Texture returns the texture property of i-th vertex.
func (td *TrianglesTextureData) Texture(i int) Vec {
	return (*TrianglesData)(td).Texture(i)
}

// TrianglesDrawer is a helper type that wraps Triangles and turns them into a Drawer.
//
// It does so by creating a separate Triangles instance for each Target. The instances are
// correctly updated alongside the wrapped Triangles.
type TrianglesDrawer struct {
	Triangles

	tris  map[Target]Triangles
	dirty bool
}

func (td *TrianglesDrawer) flush() {
	if !td.dirty {
		return
	}
	td.dirty = false

	for _, t := range td.tris {
		t.Update(td.Triangles)
	}
}

// Draw draws the wrapped Triangles onto the provided Target.
func (td *TrianglesDrawer) Draw(target Target) {
	if td.tris == nil {
		td.tris = make(map[Target]Triangles)
	}

	td.flush()

	tri := td.tris[target]
	if tri == nil {
		tri = target.MakeTriangles(td.Triangles)
		td.tris[target] = tri
	}
	tri.Draw()
}

// Update updates the wrapped Triangles with the supplied Triangles.
//
// Call only this method to update the wrapped Triangles, otherwise the TrianglesDrawer will not
// work correctly.
func (td *TrianglesDrawer) Update(t Triangles) {
	td.dirty = true
	td.Triangles.Update(t)
}

// Append appends the supplied Triangles to the wrapped Triangles.
//
// Call only this method to append to the wrapped Triangles, otherwise the TrianglesDrawer will not
// work correctly.
func (td *TrianglesDrawer) Append(t Triangles) {
	td.dirty = true
	td.Triangles.Append(t)
}

// Sprite is a picture, positioned somewhere, with an optional mask color.
type Sprite struct {
	td        TrianglesDrawer
	pic       *Picture
	transform []Transform
	maskColor color.Color
}

// NewSprite creates a Sprite with the supplied Picture. The dimensions of the returned Sprite match
// the dimensions of the Picture.
func NewSprite(pic *Picture) *Sprite {
	s := &Sprite{
		td: TrianglesDrawer{Triangles: &TrianglesTextureData{}},
	}
	s.SetPicture(pic)
	return s
}

// SetPicture changes the Picture of the Sprite and resizes it accordingly.
func (s *Sprite) SetPicture(pic *Picture) {
	w, h := pic.Bounds().Size.XY()
	s.td.Update(&TrianglesTextureData{
		{Position: V(0, 0), Texture: V(0, 0)},
		{Position: V(w, 0), Texture: V(1, 0)},
		{Position: V(w, h), Texture: V(1, 1)},
		{Position: V(0, 0), Texture: V(0, 0)},
		{Position: V(w, h), Texture: V(1, 1)},
		{Position: V(0, h), Texture: V(0, 1)},
	})
	s.pic = pic
}

// Picture returns the current Picture of the Sprite.
func (s *Sprite) Picture() *Picture {
	return s.pic
}

// SetTransform sets a chain of Transforms that will be applied to this Sprite in reverse order.
func (s *Sprite) SetTransform(t ...Transform) {
	s.transform = t
}

// Transform returns the current chain of Transforms that this Sprite is transformed by.
func (s *Sprite) Transform() []Transform {
	return s.transform
}

// SetMaskColor changes the mask color of the Sprite.
func (s *Sprite) SetMaskColor(c color.Color) {
	s.maskColor = c
}

// MaskColor returns the current mask color of the Sprite.
func (s *Sprite) MaskColor() color.Color {
	return s.maskColor
}

// Draw draws the Sprite onto the provided Target.
func (s *Sprite) Draw(target Target) {
	target.SetPicture(s.pic)
	target.SetTransform(s.transform...)
	target.SetMaskColor(s.maskColor)
	s.td.Draw(target)
}
<commit_msg>remove unnecessary and confusing Triangles(Color|Texture)Data<commit_after>package pixel

import (
	"fmt"
	"image/color"
)

// TrianglesData specifies a list of Triangles vertices with three common properties: Position,
// Color and Texture.
type TrianglesData []struct {
	Position Vec
	Color    NRGBA
	Texture  Vec
}

// Len returns the number of vertices in TrianglesData.
func (td *TrianglesData) Len() int {
	return len(*td)
}

// Draw is unimplemented for TrianglesData and panics.
func (td *TrianglesData) Draw() {
	panic(fmt.Errorf("%T.Draw: invalid operation", td))
}

// Update copies vertex properties from the supplied Triangles into this TrianglesData.
//
// TrianglesPosition, TrianglesColor and TrianglesTexture are supported.
func (td *TrianglesData) Update(t Triangles) {
	if t.Len() > td.Len() {
		*td = append(*td, make(TrianglesData, t.Len()-td.Len())...)
	}
	if t.Len() < td.Len() {
		*td = (*td)[:t.Len()]
	}

	// fast path optimization
	if t, ok := t.(*TrianglesData); ok {
		copy(*td, *t)
		return
	}

	// slow path manual copy
	if t, ok := t.(TrianglesPosition); ok {
		for i := range *td {
			(*td)[i].Position = t.Position(i)
		}
	}
	if t, ok := t.(TrianglesColor); ok {
		for i := range *td {
			(*td)[i].Color = NRGBAModel.Convert(t.Color(i)).(NRGBA)
		}
	}
	if t, ok := t.(TrianglesTexture); ok {
		for i := range *td {
			(*td)[i].Texture = t.Texture(i)
		}
	}
}

// Append adds supplied Triangles to the end of the TrianglesData.
func (td *TrianglesData) Append(t Triangles) {
	newTd := make(TrianglesData, t.Len())
	newTd.Update(t)
	*td = append(*td, newTd...)
}

// Copy returns an exact independent copy of this TrianglesData.
func (td *TrianglesData) Copy() Triangles {
	copyTd := make(TrianglesData, td.Len())
	copyTd.Update(td)
	return &copyTd
}

// Position returns the position property of i-th vertex.
func (td *TrianglesData) Position(i int) Vec {
	return (*td)[i].Position
}

// Color returns the color property of i-th vertex.
func (td *TrianglesData) Color(i int) color.Color {
	return (*td)[i].Color
}

// Texture returns the texture property of i-th vertex.
func (td *TrianglesData) Texture(i int) Vec {
	return (*td)[i].Texture
}

// TrianglesDrawer is a helper type that wraps Triangles and turns them into a Drawer.
//
// It does so by creating a separate Triangles instance for each Target. The instances are
// correctly updated alongside the wrapped Triangles.
type TrianglesDrawer struct {
	Triangles

	tris  map[Target]Triangles
	dirty bool
}

func (td *TrianglesDrawer) flush() {
	if !td.dirty {
		return
	}
	td.dirty = false

	for _, t := range td.tris {
		t.Update(td.Triangles)
	}
}

// Draw draws the wrapped Triangles onto the provided Target.
func (td *TrianglesDrawer) Draw(target Target) {
	if td.tris == nil {
		td.tris = make(map[Target]Triangles)
	}

	td.flush()

	tri := td.tris[target]
	if tri == nil {
		tri = target.MakeTriangles(td.Triangles)
		td.tris[target] = tri
	}
	tri.Draw()
}

// Update updates the wrapped Triangles with the supplied Triangles.
//
// Call only this method to update the wrapped Triangles, otherwise the TrianglesDrawer will not
// work correctly.
func (td *TrianglesDrawer) Update(t Triangles) {
	td.dirty = true
	td.Triangles.Update(t)
}

// Append appends the supplied Triangles to the wrapped Triangles.
//
// Call only this method to append to the wrapped Triangles, otherwise the TrianglesDrawer will not
// work correctly.
func (td *TrianglesDrawer) Append(t Triangles) {
	td.dirty = true
	td.Triangles.Append(t)
}

// Sprite is a picture, positioned somewhere, with an optional mask color.
type Sprite struct {
	td        TrianglesDrawer
	pic       *Picture
	transform []Transform
	maskColor color.Color
}

// NewSprite creates a Sprite with the supplied Picture. The dimensions of the returned Sprite match
// the dimensions of the Picture.
func NewSprite(pic *Picture) *Sprite {
	s := &Sprite{
		td: TrianglesDrawer{Triangles: &TrianglesData{}},
	}
	s.SetPicture(pic)
	return s
}

// SetPicture changes the Picture of the Sprite and resizes it accordingly.
func (s *Sprite) SetPicture(pic *Picture) {
	w, h := pic.Bounds().Size.XY()
	s.td.Update(&TrianglesData{
		{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(0, 0)},
		{Position: V(w, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(1, 0)},
		{Position: V(w, h), Color: NRGBA{1, 1, 1, 1}, Texture: V(1, 1)},
		{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(0, 0)},
		{Position: V(w, h), Color: NRGBA{1, 1, 1, 1}, Texture: V(1, 1)},
		{Position: V(0, h), Color: NRGBA{1, 1, 1, 1}, Texture: V(0, 1)},
	})
	s.pic = pic
}

// Picture returns the current Picture of the Sprite.
func (s *Sprite) Picture() *Picture {
	return s.pic
}

// SetTransform sets a chain of Transforms that will be applied to this Sprite in reverse order.
func (s *Sprite) SetTransform(t ...Transform) {
	s.transform = t
}

// Transform returns the current chain of Transforms that this Sprite is transformed by.
func (s *Sprite) Transform() []Transform {
	return s.transform
}

// SetMaskColor changes the mask color of the Sprite.
func (s *Sprite) SetMaskColor(c color.Color) {
	s.maskColor = c
}

// MaskColor returns the current mask color of the Sprite.
func (s *Sprite) MaskColor() color.Color {
	return s.maskColor
}

// Draw draws the Sprite onto the provided Target.
func (s *Sprite) Draw(target Target) {
	target.SetPicture(s.pic)
	target.SetTransform(s.transform...)
	target.SetMaskColor(s.maskColor)
	s.td.Draw(target)
}
<|endoftext|>


<commit_before>/*
Dependencies:
  go get github.com/alixaxel/pagerank

WARNING: This package is not thread safe, so you cannot use *Bag from many goroutines.
*/
package tldr

import (
	"errors"
	"sort"
	"strings"
	"unicode"

	"github.com/alixaxel/pagerank"
)

type Bag struct {
	BagOfWordsPerSentence [][]string
	OriginalSentences     []string
	Dict                  map[string]int
	Nodes                 []*Node
	Edges                 []*Edge
	Ranks                 []int

	MaxCharacters              int
	Algorithm                  string // "centrality" or "pagerank" or "custom"
	Weighing                   string // "hamming" or "jaccard" or "custom"
	Damping                    float64
	Tolerance                  float64
	Threshold                  float64
	SentencesDistanceThreshold float64

	customAlgorithm func(e []*Edge) []int
	customWeighing  func(src, dst []int) float64
	wordTokenizer   func(sentence string) []string

	vectorLength int
}

// The default values of each settings
const (
	VERSION                              = "0.5.0"
	DEFAULT_ALGORITHM                    = "pagerank"
	DEFAULT_WEIGHING                     = "hamming"
	DEFAULT_DAMPING                      = 0.85
	DEFAULT_TOLERANCE                    = 0.0001
	DEFAULT_THRESHOLD                    = 0.001
	DEFAULT_MAX_CHARACTERS               = 0
	DEFAULT_SENTENCES_DISTANCE_THRESHOLD = 0.95
)

func defaultWordTokenizer(sentence string) []string {
	words := strings.Fields(sentence)
	for i, word := range words {
		words[i] = SanitizeWord(word)
	}

	return words
}

// New creates a new summarizer
func New() *Bag {
	return &Bag{
		MaxCharacters:              DEFAULT_MAX_CHARACTERS,
		Algorithm:                  DEFAULT_ALGORITHM,
		Weighing:                   DEFAULT_WEIGHING,
		Damping:                    DEFAULT_DAMPING,
		Tolerance:                  DEFAULT_TOLERANCE,
		Threshold:                  DEFAULT_THRESHOLD,
		SentencesDistanceThreshold: DEFAULT_SENTENCES_DISTANCE_THRESHOLD,
		wordTokenizer:              defaultWordTokenizer,
	}
}

// Set max characters, damping, tolerance, threshold, sentences distance threshold, algorithm, and weighing
func (bag *Bag) Set(m int, d, t, th, sth float64, alg, w string) {
	bag.MaxCharacters = m
	bag.Damping = d
	bag.Tolerance = t
	bag.Threshold = th
	bag.Algorithm = alg
	bag.Weighing = w
	bag.SentencesDistanceThreshold = sth
}

// Useful if you already have your own dictionary (example: from your database)
// Dictionary is a map[string]int where the key is the word and int is the position in vector, starting from 1
func (bag *Bag) SetDictionary(dict map[string]int) {
	bag.Dict = dict
}

func (bag *Bag) SetCustomAlgorithm(f func(e []*Edge) []int) {
	bag.customAlgorithm = f
}

func (bag *Bag) SetCustomWeighing(f func(src, dst []int) float64) {
	bag.customWeighing = f
}

func (bag *Bag) SetWordTokenizer(f func(string) []string) {
	bag.wordTokenizer = f
}

// Summarize the text to num sentences
func (bag *Bag) Summarize(text string, num int) ([]string, error) {
	text = strings.TrimSpace(text)
	if len(text) < 1 && len(bag.OriginalSentences) == 0 {
		return nil, nil
	}

	bag.createSentences(text) // only actually creates sentences if no OrignalSentences

	// If user already provide dictionary, pass creating dictionary
	if len(bag.Dict) < 1 {
		if text == "" {
			text = strings.TrimSpace(strings.Join(bag.OriginalSentences, " "))
		}
		bag.createDictionary(text)
	}

	bag.createNodes()
	bag.createEdges()

	switch bag.Algorithm {
	case "centrality":
		bag.centrality()
		break
	case "pagerank":
		bag.pageRank()
		break
	case "custom":
		bag.Ranks = bag.customAlgorithm(bag.Edges)
		break
	default:
		bag.pageRank()
	}

	// if no ranks, return error
	lenRanks := len(bag.Ranks)
	if lenRanks == 0 {
		return nil, errors.New("Ranks is empty")
	}

	// guard so it won't crash but return only the highest rank sentence
	// if num is invalid
	if num < 1 {
		num = 1
	} else if num > lenRanks {
		// in this case take all
		num = lenRanks
	}

	// get only top num of ranks
	idx := bag.Ranks[:num]
	// sort it ascending by how the sentences appeared on the original text
	sort.Ints(idx)

	return bag.concatResult(idx), nil
}

// concatenate sentences at idx to result string
func (bag *Bag) concatResult(idx []int) []string {
	var res []string
	if bag.MaxCharacters > 0 {
		lenRes := 0
		for i := range idx {
			lenOrig := len([]rune(bag.OriginalSentences[idx[i]]))
			if lenRes+lenOrig <= bag.MaxCharacters {
				res = append(res, bag.OriginalSentences[idx[i]])
			} else {
				n := bag.MaxCharacters - lenRes
				res = append(res, bag.OriginalSentences[idx[i]][:n])
			}
			lenRes += lenOrig
		}
		return res
	}

	for i := range idx {
		res = append(res, bag.OriginalSentences[idx[i]])
	}

	return res
}

type Rank struct {
	idx   int
	score float64
}

func (bag *Bag) centrality() {
	// first remove edges under Threshold weight
	var newEdges []*Edge
	for _, edge := range bag.Edges {
		if edge.weight > bag.Threshold {
			newEdges = append(newEdges, edge)
		}
	}
	// sort them by weight descending
	sort.Sort(ByWeight(newEdges))
	ReverseEdge(newEdges)
	rankBySrc := make([]int, len(newEdges))
	for i, v := range newEdges {
		rankBySrc[i] = v.src
	}
	// uniq it without disturbing the order
	m := make(map[int]bool)
	var uniq []int
	for _, v := range rankBySrc {
		if m[v] {
			continue
		}
		uniq = append(uniq, v)
		m[v] = true
	}
	bag.Ranks = uniq
}

func (bag *Bag) pageRank() {
	// first remove edges under Threshold weight
	var newEdges []*Edge
	for _, edge := range bag.Edges {
		if edge.weight > bag.Threshold {
			newEdges = append(newEdges, edge)
		}
	}
	// then page rank them
	graph := pagerank.NewGraph()
	defer graph.Reset()
	for _, edge := range newEdges {
		graph.Link(uint32(edge.src), uint32(edge.dst), edge.weight)
	}
	var ranks []*Rank
	graph.Rank(bag.Damping, bag.Tolerance, func(sentenceIndex uint32, rank float64) {
		ranks = append(ranks, &Rank{int(sentenceIndex), rank})
	})
	// sort ranks into an array of sentence index, by score descending
	sort.Sort(ByScore(ranks))
	ReverseRank(ranks)
	idx := make([]int, len(ranks))
	for i, v := range ranks {
		idx[i] = v.idx
	}

	bag.Ranks = idx
}

type Edge struct {
	src    int     // index of node
	dst    int     // index of node
	weight float64 // weight of the similarity between two sentences
}

func (bag *Bag) createEdges() {
	for i, src := range bag.Nodes {
		for j, dst := range bag.Nodes {
			// don't compare same node
			if i != j {
				var weight float64
				switch bag.Weighing {
				case "jaccard":
					commonElements := Intersection(src.vector, dst.vector)
					weight = 1.0 - float64(len(commonElements))/((float64(bag.vectorLength)*2)-float64(len(commonElements)))
					break
				case "hamming":
					differentElements := SymmetricDifference(src.vector, dst.vector)
					weight = float64(len(differentElements))
					break
				case "custom":
					weight = bag.customWeighing(src.vector, dst.vector)
					break
				default:
					differentElements := SymmetricDifference(src.vector, dst.vector)
					weight = float64(len(differentElements))
				}
				edge := &Edge{i, j, weight}
				bag.Edges = append(bag.Edges, edge)
			}
		}
	}
}

type Node struct {
	sentenceIndex int   // index of sentence from the bag
	vector        []int // map of word count in respect with dict, should we use map instead of slice?
	// for example :
	/*
		dict = {
			i : 1
			am : 2
			the : 3
			shit : 4
		}
		str = "I am not shit, you effin shit"
		vector = [1, 1, 0, 2] => [1, 1, 0, 1] because should be binary
	*/
}

func (bag *Bag) createNodes() {
	bag.vectorLength = len(bag.Dict)
	for i, sentence := range bag.BagOfWordsPerSentence {
		// vector length is len(dict)
		vector := make([]int, bag.vectorLength)
		// word for word now
		for _, word := range sentence {
			// check word dict position, if doesn't exist, skip
			if bag.Dict[word] == 0 {
				continue
			}
			// minus 1, because array started from 0 and lowest dict is 1
			pos := bag.Dict[word] - 1
			// set 1 to the position
			vector[pos] = 1
		}
		// vector is now created, put it into the node
		node := &Node{i, vector}
		// node is now completed, put into the bag
		bag.Nodes = append(bag.Nodes, node)
	}
}

func (bag *Bag) createSentences(text string) {
	if len(bag.OriginalSentences) == 0 {
		// trim all spaces
		// done by calling func: text = strings.TrimSpace(text)
		// tokenize text as sentences
		// sentence is a group of words separated by whitespaces or punctuation other than !?.
		bag.OriginalSentences = TokenizeSentences(text)
	}

	// from original sentences, explode each sentences into bag of words
	bag.BagOfWordsPerSentence = [][]string{}
	for _, sentence := range bag.OriginalSentences {
		words := bag.wordTokenizer(sentence)
		bag.BagOfWordsPerSentence = append(bag.BagOfWordsPerSentence, words)
	}

	// then uniq it
	UniqSentences(bag.BagOfWordsPerSentence, bag.SentencesDistanceThreshold)
}

func (bag *Bag) createDictionary(text string) {
	// trim all spaces
	// this already done by calling func:	text = strings.TrimSpace(text)
	// lowercase the text
	text = strings.ToLower(text)
	// remove all non alphanumerics but spaces
	var prev rune
	text = strings.Map(func(r rune) rune {
		if r == '-' && (unicode.IsDigit(prev) || unicode.IsLetter(prev)) {
			return r
		}
		if !unicode.IsDigit(r) && !unicode.IsLetter(r) && !unicode.IsSpace(r) {
			return -1
		}
		prev = r
		return r
	}, text)
	// turn it into bag of words
	words := strings.Fields(text)
	// turn it into dictionary
	dict := make(map[string]int)
	i := 1
	for _, word := range words {
		if dict[word] == 0 {
			dict[word] = i
			i++
		}
	}
	bag.Dict = dict
}
<commit_msg>fix: cut summarize result<commit_after>/*
Dependencies:
  go get github.com/alixaxel/pagerank

WARNING: This package is not thread safe, so you cannot use *Bag from many goroutines.
*/
package tldr

import (
	"errors"
	"sort"
	"strings"
	"unicode"

	"github.com/alixaxel/pagerank"
)

type Bag struct {
	BagOfWordsPerSentence [][]string
	OriginalSentences     []string
	Dict                  map[string]int
	Nodes                 []*Node
	Edges                 []*Edge
	Ranks                 []int

	MaxCharacters              int
	Algorithm                  string // "centrality" or "pagerank" or "custom"
	Weighing                   string // "hamming" or "jaccard" or "custom"
	Damping                    float64
	Tolerance                  float64
	Threshold                  float64
	SentencesDistanceThreshold float64

	customAlgorithm func(e []*Edge) []int
	customWeighing  func(src, dst []int) float64
	wordTokenizer   func(sentence string) []string

	vectorLength int
}

// The default values of each settings
const (
	VERSION                              = "0.5.0"
	DEFAULT_ALGORITHM                    = "pagerank"
	DEFAULT_WEIGHING                     = "hamming"
	DEFAULT_DAMPING                      = 0.85
	DEFAULT_TOLERANCE                    = 0.0001
	DEFAULT_THRESHOLD                    = 0.001
	DEFAULT_MAX_CHARACTERS               = 0
	DEFAULT_SENTENCES_DISTANCE_THRESHOLD = 0.95
)

func defaultWordTokenizer(sentence string) []string {
	words := strings.Fields(sentence)
	for i, word := range words {
		words[i] = SanitizeWord(word)
	}

	return words
}

// New creates a new summarizer
func New() *Bag {
	return &Bag{
		MaxCharacters:              DEFAULT_MAX_CHARACTERS,
		Algorithm:                  DEFAULT_ALGORITHM,
		Weighing:                   DEFAULT_WEIGHING,
		Damping:                    DEFAULT_DAMPING,
		Tolerance:                  DEFAULT_TOLERANCE,
		Threshold:                  DEFAULT_THRESHOLD,
		SentencesDistanceThreshold: DEFAULT_SENTENCES_DISTANCE_THRESHOLD,
		wordTokenizer:              defaultWordTokenizer,
	}
}

// Set max characters, damping, tolerance, threshold, sentences distance threshold, algorithm, and weighing
func (bag *Bag) Set(m int, d, t, th, sth float64, alg, w string) {
	bag.MaxCharacters = m
	bag.Damping = d
	bag.Tolerance = t
	bag.Threshold = th
	bag.Algorithm = alg
	bag.Weighing = w
	bag.SentencesDistanceThreshold = sth
}

// Useful if you already have your own dictionary (example: from your database)
// Dictionary is a map[string]int where the key is the word and int is the position in vector, starting from 1
func (bag *Bag) SetDictionary(dict map[string]int) {
	bag.Dict = dict
}

func (bag *Bag) SetCustomAlgorithm(f func(e []*Edge) []int) {
	bag.customAlgorithm = f
}

func (bag *Bag) SetCustomWeighing(f func(src, dst []int) float64) {
	bag.customWeighing = f
}

func (bag *Bag) SetWordTokenizer(f func(string) []string) {
	bag.wordTokenizer = f
}

// Summarize the text to num sentences
func (bag *Bag) Summarize(text string, num int) ([]string, error) {
	text = strings.TrimSpace(text)
	if len(text) < 1 && len(bag.OriginalSentences) == 0 {
		return nil, nil
	}

	bag.createSentences(text) // only actually creates sentences if no OrignalSentences

	// If user already provide dictionary, pass creating dictionary
	if len(bag.Dict) < 1 {
		if text == "" {
			text = strings.TrimSpace(strings.Join(bag.OriginalSentences, " "))
		}
		bag.createDictionary(text)
	}

	bag.createNodes()
	bag.createEdges()

	switch bag.Algorithm {
	case "centrality":
		bag.centrality()
		break
	case "pagerank":
		bag.pageRank()
		break
	case "custom":
		bag.Ranks = bag.customAlgorithm(bag.Edges)
		break
	default:
		bag.pageRank()
	}

	// if no ranks, return error
	lenRanks := len(bag.Ranks)
	if lenRanks == 0 {
		return nil, errors.New("Ranks is empty")
	}

	// guard so it won't crash but return only the highest rank sentence
	// if num is invalid
	if num > lenRanks || num < 1 {
		num = 1
	}

	// get only top num of ranks
	idx := bag.Ranks[:num]
	// sort it ascending by how the sentences appeared on the original text
	sort.Ints(idx)

	return bag.concatResult(idx), nil
}

// concatenate sentences at idx to result string
func (bag *Bag) concatResult(idx []int) []string {
	var res []string
	if bag.MaxCharacters > 0 {
		lenRes := 0
		for i := range idx {
			lenOrig := len([]rune(bag.OriginalSentences[idx[i]]))
			if lenRes+lenOrig <= bag.MaxCharacters {
				res = append(res, bag.OriginalSentences[idx[i]])
			} else {
				n := bag.MaxCharacters - lenRes
				if n > lenOrig {
					n = lenOrig
				}
				res = append(res, bag.OriginalSentences[idx[i]][:n])
				break
			}
			lenRes += lenOrig
		}
		return res
	}

	for i := range idx {
		res = append(res, bag.OriginalSentences[idx[i]])
	}

	return res
}

type Rank struct {
	idx   int
	score float64
}

func (bag *Bag) centrality() {
	// first remove edges under Threshold weight
	var newEdges []*Edge
	for _, edge := range bag.Edges {
		if edge.weight > bag.Threshold {
			newEdges = append(newEdges, edge)
		}
	}
	// sort them by weight descending
	sort.Sort(ByWeight(newEdges))
	ReverseEdge(newEdges)
	rankBySrc := make([]int, len(newEdges))
	for i, v := range newEdges {
		rankBySrc[i] = v.src
	}
	// uniq it without disturbing the order
	m := make(map[int]bool)
	var uniq []int
	for _, v := range rankBySrc {
		if m[v] {
			continue
		}
		uniq = append(uniq, v)
		m[v] = true
	}
	bag.Ranks = uniq
}

func (bag *Bag) pageRank() {
	// first remove edges under Threshold weight
	var newEdges []*Edge
	for _, edge := range bag.Edges {
		if edge.weight > bag.Threshold {
			newEdges = append(newEdges, edge)
		}
	}
	// then page rank them
	graph := pagerank.NewGraph()
	defer graph.Reset()
	for _, edge := range newEdges {
		graph.Link(uint32(edge.src), uint32(edge.dst), edge.weight)
	}
	var ranks []*Rank
	graph.Rank(bag.Damping, bag.Tolerance, func(sentenceIndex uint32, rank float64) {
		ranks = append(ranks, &Rank{int(sentenceIndex), rank})
	})
	// sort ranks into an array of sentence index, by score descending
	sort.Sort(ByScore(ranks))
	ReverseRank(ranks)
	idx := make([]int, len(ranks))
	for i, v := range ranks {
		idx[i] = v.idx
	}

	bag.Ranks = idx
}

type Edge struct {
	src    int     // index of node
	dst    int     // index of node
	weight float64 // weight of the similarity between two sentences
}

func (bag *Bag) createEdges() {
	for i, src := range bag.Nodes {
		for j, dst := range bag.Nodes {
			// don't compare same node
			if i != j {
				var weight float64
				switch bag.Weighing {
				case "jaccard":
					commonElements := Intersection(src.vector, dst.vector)
					weight = 1.0 - float64(len(commonElements))/((float64(bag.vectorLength)*2)-float64(len(commonElements)))
					break
				case "hamming":
					differentElements := SymmetricDifference(src.vector, dst.vector)
					weight = float64(len(differentElements))
					break
				case "custom":
					weight = bag.customWeighing(src.vector, dst.vector)
					break
				default:
					differentElements := SymmetricDifference(src.vector, dst.vector)
					weight = float64(len(differentElements))
				}
				edge := &Edge{i, j, weight}
				bag.Edges = append(bag.Edges, edge)
			}
		}
	}
}

type Node struct {
	sentenceIndex int   // index of sentence from the bag
	vector        []int // map of word count in respect with dict, should we use map instead of slice?
	// for example :
	/*
		dict = {
			i : 1
			am : 2
			the : 3
			shit : 4
		}
		str = "I am not shit, you effin shit"
		vector = [1, 1, 0, 2] => [1, 1, 0, 1] because should be binary
	*/
}

func (bag *Bag) createNodes() {
	bag.vectorLength = len(bag.Dict)
	for i, sentence := range bag.BagOfWordsPerSentence {
		// vector length is len(dict)
		vector := make([]int, bag.vectorLength)
		// word for word now
		for _, word := range sentence {
			// check word dict position, if doesn't exist, skip
			if bag.Dict[word] == 0 {
				continue
			}
			// minus 1, because array started from 0 and lowest dict is 1
			pos := bag.Dict[word] - 1
			// set 1 to the position
			vector[pos] = 1
		}
		// vector is now created, put it into the node
		node := &Node{i, vector}
		// node is now completed, put into the bag
		bag.Nodes = append(bag.Nodes, node)
	}
}

func (bag *Bag) createSentences(text string) {
	if len(bag.OriginalSentences) == 0 {
		// trim all spaces
		// done by calling func: text = strings.TrimSpace(text)
		// tokenize text as sentences
		// sentence is a group of words separated by whitespaces or punctuation other than !?.
		bag.OriginalSentences = TokenizeSentences(text)
	}

	// from original sentences, explode each sentences into bag of words
	bag.BagOfWordsPerSentence = [][]string{}
	for _, sentence := range bag.OriginalSentences {
		words := bag.wordTokenizer(sentence)
		bag.BagOfWordsPerSentence = append(bag.BagOfWordsPerSentence, words)
	}

	// then uniq it
	UniqSentences(bag.BagOfWordsPerSentence, bag.SentencesDistanceThreshold)
}

func (bag *Bag) createDictionary(text string) {
	// trim all spaces
	// this already done by calling func:	text = strings.TrimSpace(text)
	// lowercase the text
	text = strings.ToLower(text)
	// remove all non alphanumerics but spaces
	var prev rune
	text = strings.Map(func(r rune) rune {
		if r == '-' && (unicode.IsDigit(prev) || unicode.IsLetter(prev)) {
			return r
		}
		if !unicode.IsDigit(r) && !unicode.IsLetter(r) && !unicode.IsSpace(r) {
			return -1
		}
		prev = r
		return r
	}, text)
	// turn it into bag of words
	words := strings.Fields(text)
	// turn it into dictionary
	dict := make(map[string]int)
	i := 1
	for _, word := range words {
		if dict[word] == 0 {
			dict[word] = i
			i++
		}
	}
	bag.Dict = dict
}
<|endoftext|>


<commit_before>// Copyright 2015 Bobby Powers. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package tmux

import (
	"bytes"
	"encoding/json"
	"fmt"
)

var NotFound = fmt.Errorf("object not found")

type Session struct {
	Name     string
	NWindows int
	Created  string
	Width    int
	Height   int
}

type Window struct {
	Index       int
	Name        string
	SessionName string
	NPanes      int
	Width       int
	Height      int
	Active      bool
}

type Client struct {
	TTY         string
	SessionName string
	Width       int
	Height      int
	TermName    string
	UTF8        bool
	ReadOnly    bool
}

// StartServer requires you have sessions specified in your tmux
// config, otherwise it is effectively a noop.
func StartServer() error {
	_, err := execCommand("start-server")
	return err
}

func ListSessions() ([]Session, error) {
	out, err := Command("list-sessions", "-F",
		`{"Name":"#{session_name}","NWindow":#{session_windows},`+
			`"Created":"#{session_created_string}",`+
			`"Width":#{session_width},"Height":#{session_height}}`)
	if err != nil {
		return nil, fmt.Errorf("Command: %s", err)
	}
	bufs := bytes.Split(out, []byte{'\n'})
	sessions := make([]Session, len(bufs))
	for i, buf := range bufs {
		err := json.Unmarshal(buf, &sessions[i])
		if err != nil {
			return nil, fmt.Errorf("Unmarshal(%s): %s", string(buf), err)
		}
	}
	return sessions, nil
}

func GetSession(name string) (Session, error) {
	sessions, err := ListSessions()
	if err != nil {
		return Session{}, err
	}
	for _, s := range sessions {
		if s.Name == name {
			return s, nil
		}
	}
	return Session{}, NotFound
}

func ListWindows() ([]Window, error) {
	out, err := Command("list-windows", "-a", "-F",
		`{"Index":#{window_index},"Name":"#{window_name}",`+
			`"SessionName":"#{session_name}",`+
			`"Active":#{?window_active,true,false},`+
			`"NPanes":#{window_panes},"Width":#{window_width},`+
			`"Height":#{window_height}}`)
	if err != nil {
		return nil, fmt.Errorf("Command: %s", err)
	}
	windowBufs := bytes.Split(out, []byte{'\n'})
	windows := make([]Window, len(windowBufs))
	for i, buf := range windowBufs {
		err := json.Unmarshal(buf, &windows[i])
		if err != nil {
			return nil, fmt.Errorf("Unmarshal: %s", err)
		}
	}
	return windows, nil
}

func GetWindow(name string) (Window, error) {
	windows, err := ListWindows()
	if err != nil {
		return Window{}, err
	}
	for _, s := range windows {
		if s.Name == name {
			return s, nil
		}
	}
	return Window{}, NotFound
}

func ListClients() ([]Client, error) {
	out, err := Command("list-clients", "-F",
		`{"TTY":"#{client_tty}",`+
			`"SessionName":"#{session_name}",`+
			`"Width":#{client_width},`+
			`"Height":#{client_height},`+
			`"TermName":"#{client_termname}",`+
			`"UTF8":#{?client_utf8,true,false},`+
			`"ReadOnly":#{?client_readonly,true,false}}`)
	if err != nil {
		return nil, fmt.Errorf("Command: %s", err)
	}
	bufs := bytes.Split(out, []byte{'\n'})
	clients := make([]Client, len(bufs))
	for i, buf := range bufs {
		err := json.Unmarshal(buf, &clients[i])
		if err != nil {
			return nil, fmt.Errorf("Unmarshal(%s): %s", string(buf), err)
		}
	}
	return clients, nil
}

func GetClient(tty string) (Client, error) {
	clients, err := ListClients()
	if err != nil {
		return Client{}, err
	}
	for _, c := range clients {
		if c.TTY == tty {
			return c, nil
		}
	}
	return Client{}, NotFound
}

func SwitchClient(tty, target string) error {
	_, err := Command("switch-client", "-c", tty, "-t", target)
	return err
}

func NewSession(sessionName, windowName string, args ...string) (Session, error) {
	cmdArgs := make([]string, 0, 16)
	cmdArgs = append(cmdArgs, "new-session", "-d", "-s", sessionName)
	if windowName != "" {
		cmdArgs = append(cmdArgs, "-n", windowName)
	}
	cmdArgs = append(cmdArgs, args...)
	_, err := Command(cmdArgs...)
	if err != nil {
		return Session{}, fmt.Errorf("Command(new-session): %s", err)
	}
	return GetSession(sessionName)
}

func NewWindow(target, windowName string, args ...string) (Window, error) {
	cmdArgs := make([]string, 0, 16)
	cmdArgs = append(cmdArgs, "new-window", "-t", target)
	if windowName != "" {
		cmdArgs = append(cmdArgs, "-n", windowName)
	}
	cmdArgs = append(cmdArgs, args...)
	_, err := Command(cmdArgs...)
	if err != nil {
		return Window{}, fmt.Errorf("Command(new-window): %s", err)
	}
	return GetWindow(windowName)
}

func SplitWindow(target string, args ...string) error {
	cmdArgs := make([]string, 0, 3+len(args))
	cmdArgs = append(cmdArgs, "split-window", "-t", target)
	cmdArgs = append(cmdArgs, args...)
	_, err := Command(cmdArgs...)
	return err
}

func ResizePane(target string, args ...string) error {
	cmdArgs := make([]string, 0, 3+len(args))
	cmdArgs = append(cmdArgs, "resize-pane", "-t", target)
	cmdArgs = append(cmdArgs, args...)
	_, err := Command(cmdArgs...)
	return err
}

func SelectPane(target string) error {
	_, err := Command("select-pane", "-t", target)
	return err
}
<commit_msg>new-session: set cwd with '-c /', otherwise we hit a segfault in tmux<commit_after>// Copyright 2015 Bobby Powers. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package tmux

import (
	"bytes"
	"encoding/json"
	"fmt"
)

var NotFound = fmt.Errorf("object not found")

type Session struct {
	Name     string
	NWindows int
	Created  string
	Width    int
	Height   int
}

type Window struct {
	Index       int
	Name        string
	SessionName string
	NPanes      int
	Width       int
	Height      int
	Active      bool
}

type Client struct {
	TTY         string
	SessionName string
	Width       int
	Height      int
	TermName    string
	UTF8        bool
	ReadOnly    bool
}

// StartServer requires you have sessions specified in your tmux
// config, otherwise it is effectively a noop.
func StartServer() error {
	_, err := execCommand("start-server")
	return err
}

func ListSessions() ([]Session, error) {
	out, err := Command("list-sessions", "-F",
		`{"Name":"#{session_name}","NWindow":#{session_windows},`+
			`"Created":"#{session_created_string}",`+
			`"Width":#{session_width},"Height":#{session_height}}`)
	if err != nil {
		return nil, fmt.Errorf("Command: %s", err)
	}
	bufs := bytes.Split(out, []byte{'\n'})
	sessions := make([]Session, len(bufs))
	for i, buf := range bufs {
		err := json.Unmarshal(buf, &sessions[i])
		if err != nil {
			return nil, fmt.Errorf("Unmarshal(%s): %s", string(buf), err)
		}
	}
	return sessions, nil
}

func GetSession(name string) (Session, error) {
	sessions, err := ListSessions()
	if err != nil {
		return Session{}, err
	}
	for _, s := range sessions {
		if s.Name == name {
			return s, nil
		}
	}
	return Session{}, NotFound
}

func ListWindows() ([]Window, error) {
	out, err := Command("list-windows", "-a", "-F",
		`{"Index":#{window_index},"Name":"#{window_name}",`+
			`"SessionName":"#{session_name}",`+
			`"Active":#{?window_active,true,false},`+
			`"NPanes":#{window_panes},"Width":#{window_width},`+
			`"Height":#{window_height}}`)
	if err != nil {
		return nil, fmt.Errorf("Command: %s", err)
	}
	windowBufs := bytes.Split(out, []byte{'\n'})
	windows := make([]Window, len(windowBufs))
	for i, buf := range windowBufs {
		err := json.Unmarshal(buf, &windows[i])
		if err != nil {
			return nil, fmt.Errorf("Unmarshal: %s", err)
		}
	}
	return windows, nil
}

func GetWindow(name string) (Window, error) {
	windows, err := ListWindows()
	if err != nil {
		return Window{}, err
	}
	for _, s := range windows {
		if s.Name == name {
			return s, nil
		}
	}
	return Window{}, NotFound
}

func ListClients() ([]Client, error) {
	out, err := Command("list-clients", "-F",
		`{"TTY":"#{client_tty}",`+
			`"SessionName":"#{session_name}",`+
			`"Width":#{client_width},`+
			`"Height":#{client_height},`+
			`"TermName":"#{client_termname}",`+
			`"UTF8":#{?client_utf8,true,false},`+
			`"ReadOnly":#{?client_readonly,true,false}}`)
	if err != nil {
		return nil, fmt.Errorf("Command: %s", err)
	}
	bufs := bytes.Split(out, []byte{'\n'})
	clients := make([]Client, len(bufs))
	for i, buf := range bufs {
		err := json.Unmarshal(buf, &clients[i])
		if err != nil {
			return nil, fmt.Errorf("Unmarshal(%s): %s", string(buf), err)
		}
	}
	return clients, nil
}

func GetClient(tty string) (Client, error) {
	clients, err := ListClients()
	if err != nil {
		return Client{}, err
	}
	for _, c := range clients {
		if c.TTY == tty {
			return c, nil
		}
	}
	return Client{}, NotFound
}

func SwitchClient(tty, target string) error {
	_, err := Command("switch-client", "-c", tty, "-t", target)
	return err
}

func NewSession(sessionName, windowName string, args ...string) (Session, error) {
	cmdArgs := make([]string, 0, 16)
	cmdArgs = append(cmdArgs, "new-session", "-c", "/", "-d", "-s", sessionName)
	if windowName != "" {
		cmdArgs = append(cmdArgs, "-n", windowName)
	}
	cmdArgs = append(cmdArgs, args...)
	_, err := Command(cmdArgs...)
	if err != nil {
		return Session{}, fmt.Errorf("Command(new-session): %s", err)
	}
	return GetSession(sessionName)
}

func NewWindow(target, windowName string, args ...string) (Window, error) {
	cmdArgs := make([]string, 0, 16)
	cmdArgs = append(cmdArgs, "new-window", "-t", target)
	if windowName != "" {
		cmdArgs = append(cmdArgs, "-n", windowName)
	}
	cmdArgs = append(cmdArgs, args...)
	_, err := Command(cmdArgs...)
	if err != nil {
		return Window{}, fmt.Errorf("Command(new-window): %s", err)
	}
	return GetWindow(windowName)
}

func SplitWindow(target string, args ...string) error {
	cmdArgs := make([]string, 0, 3+len(args))
	cmdArgs = append(cmdArgs, "split-window", "-t", target)
	cmdArgs = append(cmdArgs, args...)
	_, err := Command(cmdArgs...)
	return err
}

func ResizePane(target string, args ...string) error {
	cmdArgs := make([]string, 0, 3+len(args))
	cmdArgs = append(cmdArgs, "resize-pane", "-t", target)
	cmdArgs = append(cmdArgs, args...)
	_, err := Command(cmdArgs...)
	return err
}

func SelectPane(target string) error {
	_, err := Command("select-pane", "-t", target)
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"

	"github.com/pkg/errors"
)

type tool struct {
	Repository string // eg "github.com/tools/godep"
	Commit     string // eg "3020345802e4bff23902cfc1d19e90a79fae714e"
	ref        string // eg "origin/master"
	Fork       string `json:"Fork,omitempty"` // eg "code.jusin.tv/twitch/godep"
}

func (t *tool) path() string {
	return path.Join(cacheDir, "src", t.Repository)
}

func (t *tool) executable() string {
	return path.Base(t.Repository)
}

func setEnvVar(cmd *exec.Cmd, key, val string) {
	var env []string
	if cmd.Env != nil {
		env = cmd.Env
	} else {
		env = os.Environ()
	}

	envSet := false
	for i, envVar := range env {
		if strings.HasPrefix(envVar, key+"=") {
			env[i] = key + "=" + val
			envSet = true
		}
	}
	if !envSet {
		env = append(cmd.Env, key+"="+val)
	}

	cmd.Env = env
}

func get(t *tool) error {
	// If the repo is already downloaded to the cache, then we can exit early
	if _, err := os.Stat(filepath.Join(cacheDir, "src", t.Repository)); err == nil {
		log(t.Repository + " already exists, skipping 'get' step")
		return nil
	}

	log("downloading " + t.Repository)
	cmd := exec.Command("go", "get", "-d", t.Repository)
	setEnvVar(cmd, "GOPATH", cacheDir)
	_, err := cmd.Output()
	if err != nil {
		return errors.Wrap(err, "failed to 'go get' tool")
	}
	return err
}

func setVersion(t *tool) error {
	// If we're using a fork, add it
	if t.Fork != "" {
		cmd := exec.Command("git", "remote", "rm", "fork")
		cmd.Dir = t.path()
		cmd.Output()

		cmd = exec.Command("git", "remote", "add", "-f", "fork", t.Fork)
		cmd.Dir = t.path()
		_, err := cmd.Output()
		if err != nil {
			return err
		}
	}

	log("setting version for " + t.Repository)
	cmd := exec.Command("git", "fetch")
	cmd.Dir = t.path()
	_, err := cmd.Output()
	if err != nil {
		return err
	}

	// If we have a symbolic reference, parse it
	if t.ref != "" {
		log(fmt.Sprintf("parsing revision %q", t.ref))
		cmd = exec.Command("git", "rev-parse", t.ref)
		cmd.Dir = t.path()
		out, err := cmd.Output()
		if err != nil {
			return err
		}
		t.Commit = strings.TrimSpace(string(out))
		log(fmt.Sprintf("parsed as %q", t.Commit))
	}

	cmd = exec.Command("git", "checkout", t.Commit)
	cmd.Dir = t.path()
	_, err = cmd.Output()
	if err != nil {
		return errors.Wrap(err, "failed to 'git checkout' tool")
	}
	return err
}

func download(t *tool) error {
	err := get(t)
	if err != nil {
		fatalExec("go get -d "+t.Repository, err)
	}

	err = setVersion(t)
	if err != nil {
		fatalExec("git checkout "+t.Commit, err)
	}

	return nil
}

func install(t *tool) error {
	log("installing " + t.Repository)
	cmd := exec.Command("go", "install", t.Repository)
	setEnvVar(cmd, "GOPATH", toolDirPath)
	_, err := cmd.Output()
	if err != nil {
		return errors.Wrap(err, "failed to 'go install' tool")
	}
	return err
}
<commit_msg>Run 'go get' a second time after setting the version<commit_after>package main

import (
	"fmt"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"

	"github.com/pkg/errors"
)

type tool struct {
	Repository string // eg "github.com/tools/godep"
	Commit     string // eg "3020345802e4bff23902cfc1d19e90a79fae714e"
	ref        string // eg "origin/master"
	Fork       string `json:"Fork,omitempty"` // eg "code.jusin.tv/twitch/godep"
}

func (t *tool) path() string {
	return path.Join(cacheDir, "src", t.Repository)
}

func (t *tool) executable() string {
	return path.Base(t.Repository)
}

func setEnvVar(cmd *exec.Cmd, key, val string) {
	var env []string
	if cmd.Env != nil {
		env = cmd.Env
	} else {
		env = os.Environ()
	}

	envSet := false
	for i, envVar := range env {
		if strings.HasPrefix(envVar, key+"=") {
			env[i] = key + "=" + val
			envSet = true
		}
	}
	if !envSet {
		env = append(cmd.Env, key+"="+val)
	}

	cmd.Env = env
}

func get(t *tool) error {
	// If the repo is already downloaded to the cache, then we can exit early
	if _, err := os.Stat(filepath.Join(cacheDir, "src", t.Repository)); err == nil {
		log(t.Repository + " already exists, skipping 'get' step")
		return nil
	}

	log("downloading " + t.Repository)
	cmd := exec.Command("go", "get", "-d", t.Repository)
	setEnvVar(cmd, "GOPATH", cacheDir)
	_, err := cmd.Output()
	if err != nil {
		return errors.Wrap(err, "failed to 'go get' tool")
	}
	return err
}

func setVersion(t *tool) error {
	// If we're using a fork, add it
	if t.Fork != "" {
		cmd := exec.Command("git", "remote", "rm", "fork")
		cmd.Dir = t.path()
		cmd.Output()

		cmd = exec.Command("git", "remote", "add", "-f", "fork", t.Fork)
		cmd.Dir = t.path()
		_, err := cmd.Output()
		if err != nil {
			return err
		}
	}

	log("setting version for " + t.Repository)
	cmd := exec.Command("git", "fetch")
	cmd.Dir = t.path()
	_, err := cmd.Output()
	if err != nil {
		return err
	}

	// If we have a symbolic reference, parse it
	if t.ref != "" {
		log(fmt.Sprintf("parsing revision %q", t.ref))
		cmd = exec.Command("git", "rev-parse", t.ref)
		cmd.Dir = t.path()
		out, err := cmd.Output()
		if err != nil {
			return err
		}
		t.Commit = strings.TrimSpace(string(out))
		log(fmt.Sprintf("parsed as %q", t.Commit))
	}

	cmd = exec.Command("git", "checkout", t.Commit)
	cmd.Dir = t.path()
	_, err = cmd.Output()
	if err != nil {
		return errors.Wrap(err, "failed to 'git checkout' tool")
	}

	// Re-run 'go get' in case the new version has a different set of dependencies.
	cmd = exec.Command("go", "get", "-d", t.Repository)
	setEnvVar(cmd, "GOPATH", cacheDir)
	out, err := cmd.Output()
	if err != nil {
		return errors.Wrap(err, "failed to 'go get' tool")
	}
	log(fmt.Sprintf("out: %s", out))

	return err
}

func download(t *tool) error {
	err := get(t)
	if err != nil {
		fatalExec("go get -d "+t.Repository, err)
	}

	err = setVersion(t)
	if err != nil {
		fatalExec("git checkout "+t.Commit, err)
	}

	return nil
}

func install(t *tool) error {
	log("installing " + t.Repository)
	cmd := exec.Command("go", "install", t.Repository)
	setEnvVar(cmd, "GOPATH", toolDirPath)
	_, err := cmd.Output()
	if err != nil {
		return errors.Wrap(err, "failed to 'go install' tool")
	}
	return err
}
<|endoftext|>


<commit_before>package travisci

import (
	"encoding/json"
	"fmt"
	"math"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"

	"github.com/grokify/glip-go-webhook"
	"github.com/grokify/glip-webhook-proxy/config"
	"github.com/valyala/fasthttp"
)

const (
	DISPLAY_NAME = "Travis CI"
	ICON_URL     = "https://blog.travis-ci.com/images/travis-mascot-200px.png"
)

type TravisciOutToGlipHandler struct {
	Config     config.Configuration
	GlipClient glipwebhook.GlipWebhookClient
}

func NewTravisciOutToGlipHandler(cfg config.Configuration, glip glipwebhook.GlipWebhookClient) TravisciOutToGlipHandler {
	return TravisciOutToGlipHandler{Config: cfg, GlipClient: glip}
}

// HandleFastHTTP is the method to respond to a fasthttp request.
func (h *TravisciOutToGlipHandler) HandleFastHTTP(ctx *fasthttp.RequestCtx) {
	srcMsg, err := h.BuildTravisciOutMessage(ctx)
	if err != nil {
		ctx.SetStatusCode(fasthttp.StatusNotAcceptable)
		log.WithFields(log.Fields{
			"type":   "http.response",
			"status": fasthttp.StatusNotAcceptable,
		}).Info("Travis CI request is not acceptable.")
		return
	}
	glipMsg := h.TravisciOutToGlip(srcMsg)

	glipWebhookGuid := fmt.Sprintf("%s", ctx.UserValue("glipguid"))
	glipWebhookGuid = strings.TrimSpace(glipWebhookGuid)

	req, resp, err := h.GlipClient.PostWebhookGUIDFast(glipWebhookGuid, glipMsg)

	if err != nil {
		ctx.SetStatusCode(fasthttp.StatusInternalServerError)
		fasthttp.ReleaseRequest(req)
		fasthttp.ReleaseResponse(resp)
		return
	}
	fmt.Fprintf(ctx, "%s", string(resp.Body()))
	fasthttp.ReleaseRequest(req)
	fasthttp.ReleaseResponse(resp)
}

func (h *TravisciOutToGlipHandler) BuildTravisciOutMessage(ctx *fasthttp.RequestCtx) (TravisciOutMessage, error) {
	return TravisciOutMessageFromBytes(ctx.FormValue("payload"))
}

func (h *TravisciOutToGlipHandler) TravisciOutToGlip(src TravisciOutMessage) glipwebhook.GlipWebhookMessage {
	gmsg := glipwebhook.GlipWebhookMessage{
		Body:     strings.Join([]string{">", src.PushBuildsAsMarkdown()}, " "),
		Activity: DISPLAY_NAME,
		Icon:     ICON_URL}
	return gmsg
}

type TravisciOutMessage struct {
	Id             int                   `json:"id,omitempty"`
	AuthorEmail    string                `json:"author_email,omitempty"`
	AuthorName     string                `json:"author_name,omitempty"`
	Branch         string                `json:"branch,omitempty"`
	BuildUrl       string                `json:"build_url,omitempty"`
	Commit         string                `json:"commit,omitempty"`
	CommitedAt     string                `json:"committed_at,omitempty"`
	CommitterName  string                `json:"committer_name,omitempty"`
	CommitterEmail string                `json:"committer_email,omitempty"`
	CompareUrl     string                `json:"compare_url,omitempty"`
	Config         TravisciOutConfig     `json:"config,omitempty"`
	Duration       int                   `json:"duration,omitempty"`
	FinishedAt     string                `json:"finished_at,omitempty"`
	Matrix         []TravisciOutBuild    `json:"matrix,omitempty"`
	Message        string                `json:"message,omitempty"`
	Number         string                `json:"number,omitempty"`
	Repository     TravisciOutRepository `json:"repository,omitempty"`
	StartedAt      string                `json:"started_at,omitempty"`
	Status         int                   `json:"status"`
	StatusMessage  string                `json:"status_message,omitempty"`
	Type           string                `json:"type,omitempty"`
}

func TravisciOutMessageFromBytes(bytes []byte) (TravisciOutMessage, error) {
	log.WithFields(log.Fields{
		"type":    "message.raw",
		"message": string(bytes),
	}).Debug("Travis CI message.")
	msg := TravisciOutMessage{}
	err := json.Unmarshal(bytes, &msg)
	if err != nil {
		log.WithFields(log.Fields{
			"type":  "message.json.unmarshal",
			"error": fmt.Sprintf("%v\n", err),
		}).Info("Travis CI request is not acceptable.")
	}
	return msg, err
}

type TravisciOutRepository struct {
	Id        int    `json:"id,omitempty"`
	Name      string `json:"name,omitempty"`
	OwnerName string `json:"owner_name,omitempty"`
	Url       string `json:"url,omitempty"`
}

type TravisciOutConfig struct {
	Language      string                   `json:"language,omitempty"`
	Notifications TravisciOutNotifications `json:"notifications,omitempty"`
}

// can Webhooks be array?
type TravisciOutNotifications struct {
	Webhooks string `json:"webhooks,omitempty"`
}

type TravisciOutBuild struct {
	Id     int `json:"id,omitempty"`
	Result int `json:"result,omitempty"`
	Status int `json:"status,omitempty"`
}

// Default template for Push Builds: "Build <%{build_url}|#%{build_number}> (<%{compare_url}|%{commit}>) of %{repository}@%{branch} by %{author} %{result} in %{duration}"

func (msg *TravisciOutMessage) PushBuildsAsMarkdown() string {
	return fmt.Sprintf("Build [#%v](%v) ([%v](%v)) of %v@%v by %v %v in %v", msg.Number, msg.BuildUrl, msg.ShortCommit(), msg.CompareUrl, msg.Repository.Name, msg.Branch, msg.AuthorName, strings.ToLower(msg.StatusMessage), msg.DurationDisplay())
}

func (msg *TravisciOutMessage) ShortCommit() string {
	if len(msg.Commit) < 8 {
		return msg.Commit
	}
	return msg.Commit[0:7]
}

func (msg *TravisciOutMessage) DurationDisplay() string {
	if msg.Duration == 0 {
		return "0 sec"
	}
	dur, err := time.ParseDuration(fmt.Sprintf("%vs", msg.Duration))
	if err != nil {
		return "unknown"
	}
	modSeconds := math.Mod(float64(msg.Duration), float64(60))
	return fmt.Sprintf("%v min %v sec", int(dur.Minutes()), modSeconds)
}

/*

Webhook Notification Reference

https://docs.travis-ci.com/user/notifications#Configuring-webhook-notifications

Format:

"Build <%{build_url}|#%{build_number}> (<%{compare_url}|%{commit}>) of %{repository}@%{branch} by %{author} %{result} in %{duration}"

Payload:

{
  "id": 1,
  "number": "1",
  "status": null,
  "started_at": null,
  "finished_at": null,
  "status_message": "Passed",
  "commit": "62aae5f70ceee39123ef",
  "branch": "master",
  "message": "the commit message",
  "compare_url": "https://github.com/svenfuchs/minimal/compare/master...develop",
  "committed_at": "2011-11-11T11: 11: 11Z",
  "committer_name": "Sven Fuchs",
  "committer_email": "svenfuchs@artweb-design.de",
  "author_name": "Sven Fuchs",
  "author_email": "svenfuchs@artweb-design.de",
  "type": "push",
  "build_url": "https://travis-ci.org/svenfuchs/minimal/builds/1",
  "repository": {
    "id": 1,
    "name": "minimal",
    "owner_name": "svenfuchs",
    "url": "http://github.com/svenfuchs/minimal"
   },
  "config": {
    "notifications": {
      "webhooks": ["http://evome.fr/notifications", "http://example.com/"]
    }
  },
  "matrix": [
    {
      "id": 2,
      "repository_id": 1,
      "number": "1.1",
      "state": "created",
      "started_at": null,
      "finished_at": null,
      "config": {
        "notifications": {
          "webhooks": ["http://evome.fr/notifications", "http://example.com/"]
        }
      },
      "status": null,
      "log": "",
      "result": null,
      "parent_id": 1,
      "commit": "62aae5f70ceee39123ef",
      "branch": "master",
      "message": "the commit message",
      "committed_at": "2011-11-11T11: 11: 11Z",
      "committer_name": "Sven Fuchs",
      "committer_email": "svenfuchs@artweb-design.de",
      "author_name": "Sven Fuchs",
      "author_email": "svenfuchs@artweb-design.de",
      "compare_url": "https://github.com/svenfuchs/minimal/compare/master...develop"
    }
  ]
}

*/
<commit_msg>update message format<commit_after>package travisci

import (
	"encoding/json"
	"fmt"
	"math"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"

	"github.com/grokify/glip-go-webhook"
	"github.com/grokify/glip-webhook-proxy/config"
	"github.com/valyala/fasthttp"
)

const (
	DISPLAY_NAME = "Travis CI"
	ICON_URL     = "https://blog.travis-ci.com/images/travis-mascot-200px.png"
)

type TravisciOutToGlipHandler struct {
	Config     config.Configuration
	GlipClient glipwebhook.GlipWebhookClient
}

func NewTravisciOutToGlipHandler(cfg config.Configuration, glip glipwebhook.GlipWebhookClient) TravisciOutToGlipHandler {
	return TravisciOutToGlipHandler{Config: cfg, GlipClient: glip}
}

// HandleFastHTTP is the method to respond to a fasthttp request.
func (h *TravisciOutToGlipHandler) HandleFastHTTP(ctx *fasthttp.RequestCtx) {
	srcMsg, err := h.BuildTravisciOutMessage(ctx)
	if err != nil {
		ctx.SetStatusCode(fasthttp.StatusNotAcceptable)
		log.WithFields(log.Fields{
			"type":   "http.response",
			"status": fasthttp.StatusNotAcceptable,
		}).Info("Travis CI request is not acceptable.")
		return
	}
	glipMsg := h.TravisciOutToGlip(srcMsg)

	glipWebhookGuid := fmt.Sprintf("%s", ctx.UserValue("glipguid"))
	glipWebhookGuid = strings.TrimSpace(glipWebhookGuid)

	req, resp, err := h.GlipClient.PostWebhookGUIDFast(glipWebhookGuid, glipMsg)

	if err != nil {
		ctx.SetStatusCode(fasthttp.StatusInternalServerError)
		fasthttp.ReleaseRequest(req)
		fasthttp.ReleaseResponse(resp)
		return
	}
	fmt.Fprintf(ctx, "%s", string(resp.Body()))
	fasthttp.ReleaseRequest(req)
	fasthttp.ReleaseResponse(resp)
}

func (h *TravisciOutToGlipHandler) BuildTravisciOutMessage(ctx *fasthttp.RequestCtx) (TravisciOutMessage, error) {
	return TravisciOutMessageFromBytes(ctx.FormValue("payload"))
}

func (h *TravisciOutToGlipHandler) TravisciOutToGlip(src TravisciOutMessage) glipwebhook.GlipWebhookMessage {
	gmsg := glipwebhook.GlipWebhookMessage{
		Body:     strings.Join([]string{">", src.PushBuildsAsMarkdown()}, " "),
		Activity: DISPLAY_NAME,
		Icon:     ICON_URL}
	return gmsg
}

type TravisciOutMessage struct {
	Id             int                   `json:"id,omitempty"`
	AuthorEmail    string                `json:"author_email,omitempty"`
	AuthorName     string                `json:"author_name,omitempty"`
	Branch         string                `json:"branch,omitempty"`
	BuildUrl       string                `json:"build_url,omitempty"`
	Commit         string                `json:"commit,omitempty"`
	CommitedAt     string                `json:"committed_at,omitempty"`
	CommitterName  string                `json:"committer_name,omitempty"`
	CommitterEmail string                `json:"committer_email,omitempty"`
	CompareUrl     string                `json:"compare_url,omitempty"`
	Config         TravisciOutConfig     `json:"config,omitempty"`
	Duration       int                   `json:"duration,omitempty"`
	FinishedAt     string                `json:"finished_at,omitempty"`
	Matrix         []TravisciOutBuild    `json:"matrix,omitempty"`
	Message        string                `json:"message,omitempty"`
	Number         string                `json:"number,omitempty"`
	Repository     TravisciOutRepository `json:"repository,omitempty"`
	StartedAt      string                `json:"started_at,omitempty"`
	Status         int                   `json:"status"`
	StatusMessage  string                `json:"status_message,omitempty"`
	Type           string                `json:"type,omitempty"`
}

func TravisciOutMessageFromBytes(bytes []byte) (TravisciOutMessage, error) {
	log.WithFields(log.Fields{
		"type":    "message.raw",
		"message": string(bytes),
	}).Debug("Travis CI message.")
	msg := TravisciOutMessage{}
	err := json.Unmarshal(bytes, &msg)
	if err != nil {
		log.WithFields(log.Fields{
			"type":  "message.json.unmarshal",
			"error": fmt.Sprintf("%v\n", err),
		}).Warn("Travis CI request unmarshal failure.")
	}
	return msg, err
}

type TravisciOutRepository struct {
	Id        int    `json:"id,omitempty"`
	Name      string `json:"name,omitempty"`
	OwnerName string `json:"owner_name,omitempty"`
	Url       string `json:"url,omitempty"`
}

type TravisciOutConfig struct {
	Language      string                   `json:"language,omitempty"`
	Notifications TravisciOutNotifications `json:"notifications,omitempty"`
}

// can Webhooks can be a string (simple) or a dictionary (secure)
type TravisciOutNotifications struct {
	// Webhooks string `json:"webhooks,omitempty"`
}

type TravisciOutBuild struct {
	Id     int `json:"id,omitempty"`
	Result int `json:"result,omitempty"`
	Status int `json:"status,omitempty"`
}

// Default template for Push Builds: "Build <%{build_url}|#%{build_number}> (<%{compare_url}|%{commit}>) of %{repository}@%{branch} by %{author} %{result} in %{duration}"

func (msg *TravisciOutMessage) PushBuildsAsMarkdown() string {
	return fmt.Sprintf("Build [#%v](%v) ([%v](%v)) of %v@%v by %v %v in %v", msg.Number, msg.BuildUrl, msg.ShortCommit(), msg.CompareUrl, msg.Repository.Name, msg.Branch, msg.AuthorName, strings.ToLower(msg.StatusMessage), msg.DurationDisplay())
}

func (msg *TravisciOutMessage) ShortCommit() string {
	if len(msg.Commit) < 8 {
		return msg.Commit
	}
	return msg.Commit[0:7]
}

func (msg *TravisciOutMessage) DurationDisplay() string {
	if msg.Duration == 0 {
		return "0 sec"
	}
	dur, err := time.ParseDuration(fmt.Sprintf("%vs", msg.Duration))
	if err != nil {
		return "unknown"
	}
	modSeconds := math.Mod(float64(msg.Duration), float64(60))
	return fmt.Sprintf("%v min %v sec", int(dur.Minutes()), modSeconds)
}

/*

Webhook Notification Reference

https://docs.travis-ci.com/user/notifications#Configuring-webhook-notifications

Format:

"Build <%{build_url}|#%{build_number}> (<%{compare_url}|%{commit}>) of %{repository}@%{branch} by %{author} %{result} in %{duration}"

Payload:

{
  "id": 1,
  "number": "1",
  "status": null,
  "started_at": null,
  "finished_at": null,
  "status_message": "Passed",
  "commit": "62aae5f70ceee39123ef",
  "branch": "master",
  "message": "the commit message",
  "compare_url": "https://github.com/svenfuchs/minimal/compare/master...develop",
  "committed_at": "2011-11-11T11: 11: 11Z",
  "committer_name": "Sven Fuchs",
  "committer_email": "svenfuchs@artweb-design.de",
  "author_name": "Sven Fuchs",
  "author_email": "svenfuchs@artweb-design.de",
  "type": "push",
  "build_url": "https://travis-ci.org/svenfuchs/minimal/builds/1",
  "repository": {
    "id": 1,
    "name": "minimal",
    "owner_name": "svenfuchs",
    "url": "http://github.com/svenfuchs/minimal"
   },
  "config": {
    "notifications": {
      "webhooks": ["http://evome.fr/notifications", "http://example.com/"]
    }
  },
  "matrix": [
    {
      "id": 2,
      "repository_id": 1,
      "number": "1.1",
      "state": "created",
      "started_at": null,
      "finished_at": null,
      "config": {
        "notifications": {
          "webhooks": ["http://evome.fr/notifications", "http://example.com/"]
        }
      },
      "status": null,
      "log": "",
      "result": null,
      "parent_id": 1,
      "commit": "62aae5f70ceee39123ef",
      "branch": "master",
      "message": "the commit message",
      "committed_at": "2011-11-11T11: 11: 11Z",
      "committer_name": "Sven Fuchs",
      "committer_email": "svenfuchs@artweb-design.de",
      "author_name": "Sven Fuchs",
      "author_email": "svenfuchs@artweb-design.de",
      "compare_url": "https://github.com/svenfuchs/minimal/compare/master...develop"
    }
  ]
}

*/
<|endoftext|>


<commit_before>package diffence

import "bytes"

// dropCR drops a terminal \r from the data.
func dropCR(data []byte) []byte {
	if len(data) > 0 && data[len(data)-1] == '\r' {
		return data[0 : len(data)-1]
	}
	return data
}

// ScanDiffs splits on the diff of an inidividual file
func ScanDiffs(data []byte, atEOF bool) (advance int, token []byte, err error) {
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}

	k, dataLen := 0, len(data)-1
	for k < dataLen {
		if i := bytes.IndexByte(data[k:], '\n'); i >= 0 {
			if k+i+1 < dataLen && string(data[k+i+1]) == "d" {
				return k + i + 1, dropCR(data[0 : k+i]), nil
				// start at index after last \n char
			}
			k += i + 1
		} else {
			k = dataLen
		}
	}
	// If we're at EOF, we have a final, non-terminated line. Return it.
	if atEOF {
		return len(data), dropCR(data), nil
	}
	// Request more data.
	return 0, nil, nil
}
<commit_msg>Adds better scan comments<commit_after>package diffence

import "bytes"

// dropCR drops a terminal \r from the data.
func dropCR(data []byte) []byte {
	if len(data) > 0 && data[len(data)-1] == '\r' {
		return data[0 : len(data)-1]
	}
	return data
}

// ScanDiffs splits on the diff of an inidividual file
func ScanDiffs(data []byte, atEOF bool) (advance int, token []byte, err error) {
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}

	k, dataLen := 0, len(data)-1
	for k < dataLen {
		if i := bytes.IndexByte(data[k:], '\n'); i >= 0 {
			// how far advanced already (k)
			// index after last \n char (+ i)
			// start at next byte (+ 1)
			nextFileDiffIndex = k + i + 1
			if nextFileDiffIndex < dataLen && string(data[nextFileDiffIndex]) == "d" {
				return nextFileDiffIndex, dropCR(data[0 : k+i]), nil
			}
			k += i + 1
		} else {
			k = dataLen
		}
	}
	// If we're at EOF, we have a final, non-terminated line. Return it.
	if atEOF {
		return len(data), dropCR(data), nil
	}
	// Request more data.
	return 0, nil, nil
}
<|endoftext|>


<commit_before>// Package github provides authentication strategies using GitHub.
package github

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"

	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/github"

	"github.com/Sirupsen/logrus"
	"github.com/coreos/dex/connector"
)

const (
	baseURL    = "https://api.github.com"
	scopeEmail = "user:email"
	scopeOrgs  = "read:org"
)

// Config holds configuration options for github logins.
type Config struct {
	ClientID     string `json:"clientID"`
	ClientSecret string `json:"clientSecret"`
	RedirectURI  string `json:"redirectURI"`
	Org          string `json:"org"`
}

// Open returns a strategy for logging in through GitHub.
func (c *Config) Open(logger logrus.FieldLogger) (connector.Connector, error) {
	return &githubConnector{
		redirectURI:  c.RedirectURI,
		org:          c.Org,
		clientID:     c.ClientID,
		clientSecret: c.ClientSecret,
		logger:       logger,
	}, nil
}

type connectorData struct {
	// GitHub's OAuth2 tokens never expire. We don't need a refresh token.
	AccessToken string `json:"accessToken"`
}

var (
	_ connector.CallbackConnector = (*githubConnector)(nil)
	_ connector.RefreshConnector  = (*githubConnector)(nil)
)

type githubConnector struct {
	redirectURI  string
	org          string
	clientID     string
	clientSecret string
	logger       logrus.FieldLogger
}

func (c *githubConnector) oauth2Config(scopes connector.Scopes) *oauth2.Config {
	var githubScopes []string
	if scopes.Groups {
		githubScopes = []string{scopeEmail, scopeOrgs}
	} else {
		githubScopes = []string{scopeEmail}
	}
	return &oauth2.Config{
		ClientID:     c.clientID,
		ClientSecret: c.clientSecret,
		Endpoint:     github.Endpoint,
		Scopes:       githubScopes,
	}
}

func (c *githubConnector) LoginURL(scopes connector.Scopes, callbackURL, state string) (string, error) {
	if c.redirectURI != callbackURL {
		return "", fmt.Errorf("expected callback URL did not match the URL in the config")
	}
	return c.oauth2Config(scopes).AuthCodeURL(state), nil
}

type oauth2Error struct {
	error            string
	errorDescription string
}

func (e *oauth2Error) Error() string {
	if e.errorDescription == "" {
		return e.error
	}
	return e.error + ": " + e.errorDescription
}

func (c *githubConnector) HandleCallback(s connector.Scopes, r *http.Request) (identity connector.Identity, err error) {
	q := r.URL.Query()
	if errType := q.Get("error"); errType != "" {
		return identity, &oauth2Error{errType, q.Get("error_description")}
	}

	oauth2Config := c.oauth2Config(s)
	ctx := r.Context()

	token, err := oauth2Config.Exchange(ctx, q.Get("code"))
	if err != nil {
		return identity, fmt.Errorf("github: failed to get token: %v", err)
	}

	client := oauth2Config.Client(ctx, token)

	user, err := c.user(ctx, client)
	if err != nil {
		return identity, fmt.Errorf("github: get user: %v", err)
	}

	username := user.Name
	if username == "" {
		username = user.Login
	}
	identity = connector.Identity{
		UserID:        strconv.Itoa(user.ID),
		Username:      username,
		Email:         user.Email,
		EmailVerified: true,
	}

	if s.Groups && c.org != "" {
		groups, err := c.teams(ctx, client, c.org)
		if err != nil {
			return identity, fmt.Errorf("github: get teams: %v", err)
		}
		identity.Groups = groups
	}

	if s.OfflineAccess {
		data := connectorData{AccessToken: token.AccessToken}
		connData, err := json.Marshal(data)
		if err != nil {
			return identity, fmt.Errorf("marshal connector data: %v", err)
		}
		identity.ConnectorData = connData
	}

	return identity, nil
}

func (c *githubConnector) Refresh(ctx context.Context, s connector.Scopes, ident connector.Identity) (connector.Identity, error) {
	if len(ident.ConnectorData) == 0 {
		return ident, errors.New("no upstream access token found")
	}

	var data connectorData
	if err := json.Unmarshal(ident.ConnectorData, &data); err != nil {
		return ident, fmt.Errorf("github: unmarshal access token: %v", err)
	}

	client := c.oauth2Config(s).Client(ctx, &oauth2.Token{AccessToken: data.AccessToken})
	user, err := c.user(ctx, client)
	if err != nil {
		return ident, fmt.Errorf("github: get user: %v", err)
	}

	username := user.Name
	if username == "" {
		username = user.Login
	}
	ident.Username = username
	ident.Email = user.Email

	if s.Groups && c.org != "" {
		groups, err := c.teams(ctx, client, c.org)
		if err != nil {
			return ident, fmt.Errorf("github: get teams: %v", err)
		}
		ident.Groups = groups
	}
	return ident, nil
}

type user struct {
	Name  string `json:"name"`
	Login string `json:"login"`
	ID    int    `json:"id"`
	Email string `json:"email"`
}

// user queries the GitHub API for profile information using the provided client. The HTTP
// client is expected to be constructed by the golang.org/x/oauth2 package, which inserts
// a bearer token as part of the request.
func (c *githubConnector) user(ctx context.Context, client *http.Client) (user, error) {
	var u user
	req, err := http.NewRequest("GET", baseURL+"/user", nil)
	if err != nil {
		return u, fmt.Errorf("github: new req: %v", err)
	}
	req = req.WithContext(ctx)
	resp, err := client.Do(req)
	if err != nil {
		return u, fmt.Errorf("github: get URL %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return u, fmt.Errorf("github: read body: %v", err)
		}
		return u, fmt.Errorf("%s: %s", resp.Status, body)
	}

	if err := json.NewDecoder(resp.Body).Decode(&u); err != nil {
		return u, fmt.Errorf("failed to decode response: %v", err)
	}
	return u, nil
}

// teams queries the GitHub API for team membership within a specific organization.
//
// The HTTP passed client is expected to be constructed by the golang.org/x/oauth2 package,
// which inserts a bearer token as part of the request.
func (c *githubConnector) teams(ctx context.Context, client *http.Client, org string) ([]string, error) {
	req, err := http.NewRequest("GET", baseURL+"/user/teams", nil)
	if err != nil {
		return nil, fmt.Errorf("github: new req: %v", err)
	}
	req = req.WithContext(ctx)
	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("github: get teams: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return nil, fmt.Errorf("github: read body: %v", err)
		}
		return nil, fmt.Errorf("%s: %s", resp.Status, body)
	}

	// https://developer.github.com/v3/orgs/teams/#response-12
	var teams []struct {
		Name string `json:"name"`
		Org  struct {
			Login string `json:"login"`
		} `json:"organization"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&teams); err != nil {
		return nil, fmt.Errorf("github: unmarshal groups: %v", err)
	}
	groups := []string{}
	for _, team := range teams {
		if team.Org.Login == org {
			groups = append(groups, team.Name)
		}
	}
	return groups, nil
}
<commit_msg>Fixes #706<commit_after>// Package github provides authentication strategies using GitHub.
package github

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"regexp"
	"strconv"

	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/github"

	"github.com/Sirupsen/logrus"
	"github.com/coreos/dex/connector"
)

const (
	baseURL    = "https://api.github.com"
	scopeEmail = "user:email"
	scopeOrgs  = "read:org"
)

// Config holds configuration options for github logins.
type Config struct {
	ClientID     string `json:"clientID"`
	ClientSecret string `json:"clientSecret"`
	RedirectURI  string `json:"redirectURI"`
	Org          string `json:"org"`
}

// Open returns a strategy for logging in through GitHub.
func (c *Config) Open(logger logrus.FieldLogger) (connector.Connector, error) {
	return &githubConnector{
		redirectURI:  c.RedirectURI,
		org:          c.Org,
		clientID:     c.ClientID,
		clientSecret: c.ClientSecret,
		logger:       logger,
	}, nil
}

type connectorData struct {
	// GitHub's OAuth2 tokens never expire. We don't need a refresh token.
	AccessToken string `json:"accessToken"`
}

var (
	_ connector.CallbackConnector = (*githubConnector)(nil)
	_ connector.RefreshConnector  = (*githubConnector)(nil)
)

type githubConnector struct {
	redirectURI  string
	org          string
	clientID     string
	clientSecret string
	logger       logrus.FieldLogger
}

func (c *githubConnector) oauth2Config(scopes connector.Scopes) *oauth2.Config {
	var githubScopes []string
	if scopes.Groups {
		githubScopes = []string{scopeEmail, scopeOrgs}
	} else {
		githubScopes = []string{scopeEmail}
	}
	return &oauth2.Config{
		ClientID:     c.clientID,
		ClientSecret: c.clientSecret,
		Endpoint:     github.Endpoint,
		Scopes:       githubScopes,
	}
}

func (c *githubConnector) LoginURL(scopes connector.Scopes, callbackURL, state string) (string, error) {
	if c.redirectURI != callbackURL {
		return "", fmt.Errorf("expected callback URL did not match the URL in the config")
	}
	return c.oauth2Config(scopes).AuthCodeURL(state), nil
}

type oauth2Error struct {
	error            string
	errorDescription string
}

func (e *oauth2Error) Error() string {
	if e.errorDescription == "" {
		return e.error
	}
	return e.error + ": " + e.errorDescription
}

func (c *githubConnector) HandleCallback(s connector.Scopes, r *http.Request) (identity connector.Identity, err error) {
	q := r.URL.Query()
	if errType := q.Get("error"); errType != "" {
		return identity, &oauth2Error{errType, q.Get("error_description")}
	}

	oauth2Config := c.oauth2Config(s)
	ctx := r.Context()

	token, err := oauth2Config.Exchange(ctx, q.Get("code"))
	if err != nil {
		return identity, fmt.Errorf("github: failed to get token: %v", err)
	}

	client := oauth2Config.Client(ctx, token)

	user, err := c.user(ctx, client)
	if err != nil {
		return identity, fmt.Errorf("github: get user: %v", err)
	}

	username := user.Name
	if username == "" {
		username = user.Login
	}
	identity = connector.Identity{
		UserID:        strconv.Itoa(user.ID),
		Username:      username,
		Email:         user.Email,
		EmailVerified: true,
	}

	if s.Groups && c.org != "" {
		groups, err := c.teams(ctx, client, c.org)
		if err != nil {
			return identity, fmt.Errorf("github: get teams: %v", err)
		}
		identity.Groups = groups
	}

	if s.OfflineAccess {
		data := connectorData{AccessToken: token.AccessToken}
		connData, err := json.Marshal(data)
		if err != nil {
			return identity, fmt.Errorf("marshal connector data: %v", err)
		}
		identity.ConnectorData = connData
	}

	return identity, nil
}

func (c *githubConnector) Refresh(ctx context.Context, s connector.Scopes, ident connector.Identity) (connector.Identity, error) {
	if len(ident.ConnectorData) == 0 {
		return ident, errors.New("no upstream access token found")
	}

	var data connectorData
	if err := json.Unmarshal(ident.ConnectorData, &data); err != nil {
		return ident, fmt.Errorf("github: unmarshal access token: %v", err)
	}

	client := c.oauth2Config(s).Client(ctx, &oauth2.Token{AccessToken: data.AccessToken})
	user, err := c.user(ctx, client)
	if err != nil {
		return ident, fmt.Errorf("github: get user: %v", err)
	}

	username := user.Name
	if username == "" {
		username = user.Login
	}
	ident.Username = username
	ident.Email = user.Email

	if s.Groups && c.org != "" {
		groups, err := c.teams(ctx, client, c.org)
		if err != nil {
			return ident, fmt.Errorf("github: get teams: %v", err)
		}
		ident.Groups = groups
	}
	return ident, nil
}

type user struct {
	Name  string `json:"name"`
	Login string `json:"login"`
	ID    int    `json:"id"`
	Email string `json:"email"`
}

// user queries the GitHub API for profile information using the provided client. The HTTP
// client is expected to be constructed by the golang.org/x/oauth2 package, which inserts
// a bearer token as part of the request.
func (c *githubConnector) user(ctx context.Context, client *http.Client) (user, error) {
	var u user
	req, err := http.NewRequest("GET", baseURL+"/user", nil)
	if err != nil {
		return u, fmt.Errorf("github: new req: %v", err)
	}
	req = req.WithContext(ctx)
	resp, err := client.Do(req)
	if err != nil {
		return u, fmt.Errorf("github: get URL %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return u, fmt.Errorf("github: read body: %v", err)
		}
		return u, fmt.Errorf("%s: %s", resp.Status, body)
	}

	if err := json.NewDecoder(resp.Body).Decode(&u); err != nil {
		return u, fmt.Errorf("failed to decode response: %v", err)
	}
	return u, nil
}

// teams queries the GitHub API for team membership within a specific organization.
//
// The HTTP passed client is expected to be constructed by the golang.org/x/oauth2 package,
// which inserts a bearer token as part of the request.
func (c *githubConnector) teams(ctx context.Context, client *http.Client, org string) ([]string, error) {

	groups := []string{}

	// https://developer.github.com/v3/#pagination
	reNext := regexp.MustCompile("<(.*)>; rel=\"next\"")
	reLast := regexp.MustCompile("<(.*)>; rel=\"last\"")
	apiURL := baseURL + "/user/teams"

	for {
		req, err := http.NewRequest("GET", apiURL, nil)

		if err != nil {
			return nil, fmt.Errorf("github: new req: %v", err)
		}
		req = req.WithContext(ctx)
		resp, err := client.Do(req)
		if err != nil {
			return nil, fmt.Errorf("github: get teams: %v", err)
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusOK {
			body, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				return nil, fmt.Errorf("github: read body: %v", err)
			}
			return nil, fmt.Errorf("%s: %s", resp.Status, body)
		}

		// https://developer.github.com/v3/orgs/teams/#response-12
		var teams []struct {
			Name string `json:"name"`
			Org  struct {
				Login string `json:"login"`
			} `json:"organization"`
		}
		if err := json.NewDecoder(resp.Body).Decode(&teams); err != nil {
			return nil, fmt.Errorf("github: unmarshal groups: %v", err)
		}

		for _, team := range teams {
			if team.Org.Login == org {
				groups = append(groups, team.Name)
			}
		}

		links := resp.Header.Get("Link")
		if len(reLast.FindStringSubmatch(links)) > 1 {
			lastPageURL := reLast.FindStringSubmatch(links)[1]
			if apiURL == lastPageURL {
				break
			}
		} else {
			break
		}

		if len(reNext.FindStringSubmatch(links)) > 1 {
			apiURL = reNext.FindStringSubmatch(links)[1]
		} else {
			break
		}
	}
	return groups, nil
}
<|endoftext|>


<commit_before>package main

type FileEvent struct {
  path string
  host string
  line []byte
}
<commit_msg>no sense in having 'host' in a file event<commit_after>package main

type FileEvent struct {
  path string
  line []byte
}
<|endoftext|>


<commit_before>// This executable provides an HTTP server that watches for file system changes
// to .go files within the working directory (and all nested go packages).
// Navigating to the configured host and port in a web browser will display the
// latest results of running `go test` in each go package.

package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/smartystreets/goconvey/web/server/api"
	"github.com/smartystreets/goconvey/web/server/contract"
	executor "github.com/smartystreets/goconvey/web/server/executor"
	parser "github.com/smartystreets/goconvey/web/server/parser"
	"github.com/smartystreets/goconvey/web/server/system"
	watcher "github.com/smartystreets/goconvey/web/server/watcher"
)

func init() {
	flags()
	folders()
}
func flags() {
	flag.IntVar(&port, "port", 8080, "The port at which to serve http.")
	flag.StringVar(&host, "host", "127.0.0.1", "The host at which to serve http.")
	flag.DurationVar(&nap, "poll", quarterSecond, "The interval to wait between polling the file system for changes (default: 250ms).")
	flag.IntVar(&packages, "packages", 10, "The number of packages to test in parallel. Higher == faster but more costly in terms of computing. (default: 10)")
	flag.StringVar(&gobin, "gobin", "go", "The path to the 'go' binary (default: search on the PATH).")
	flag.BoolVar(&cover, "cover", true, "Enable package-level coverage statistics. Requires Go 1.2+ and the go cover tool. (default: true)")
	flag.IntVar(&depth, "depth", -1, "The directory scanning depth. If -1, scan infinitely deep directory structures. 0: scan working directory. 1+: Scan into nested directories, limited to value. (default: -1)")
	flag.BoolVar(&short, "short", false, "Configures the `testing.Short()` function to return `true`, allowing you to call `t.Skip()` on long-running tests.")

	log.SetOutput(os.Stdout)
	log.SetFlags(log.LstdFlags | log.Lshortfile)
}
func folders() {
	_, file, _, _ := runtime.Caller(0)
	here := filepath.Dir(file)
	static = filepath.Join(here, "/web/client")
	reports = filepath.Join(static, "reports")
}

func main() {
	flag.Parse()

	log.Printf(initialConfiguration, host, port, nap, cover, short)

	monitor, server := wireup()

	go monitor.ScanForever()

	serveHTTP(server)
}

func serveHTTP(server contract.Server) {
	serveStaticResources()
	serveAjaxMethods(server)
	activateServer()
}

func serveStaticResources() {
	http.Handle("/", http.FileServer(http.Dir(static)))
}

func serveAjaxMethods(server contract.Server) {
	http.HandleFunc("/watch", server.Watch)
	http.HandleFunc("/ignore", server.Ignore)
	http.HandleFunc("/reinstate", server.Reinstate)
	http.HandleFunc("/latest", server.Results)
	http.HandleFunc("/execute", server.Execute)
	http.HandleFunc("/status", server.Status)
	http.HandleFunc("/status/poll", server.LongPollStatus)
	http.HandleFunc("/pause", server.TogglePause)
}

func activateServer() {
	log.Printf("Serving HTTP at: http://%s:%d\n", host, port)
	err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
	if err != nil {
		fmt.Println(err)
	}
}

func wireup() (*contract.Monitor, contract.Server) {
	log.Println("Constructing components...")
	working, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}

	cover = coverageEnabled(cover, reports)

	depthLimit := system.NewDepthLimit(system.NewFileSystem(), depth)
	shell := system.NewShell(gobin, short, cover, reports)

	watcher := watcher.NewWatcher(depthLimit, shell)
	watcher.Adjust(working)

	parser := parser.NewParser(parser.ParsePackageResults)
	tester := executor.NewConcurrentTester(shell)
	tester.SetBatchSize(packages)

	longpollChan, pauseUpdate := make(chan chan string), make(chan bool, 1)
	executor := executor.NewExecutor(tester, parser, longpollChan)
	server := api.NewHTTPServer(watcher, executor, longpollChan, pauseUpdate)
	scanner := watcher.NewScanner(depthLimit, watcher)
	monitor := contract.NewMonitor(scanner, watcher, executor, server, pauseUpdate, sleeper)

	return monitor, server
}

func coverageEnabled(cover bool, reports string) bool {
	return (cover &&
		goVersion_1_2_orGreater() &&
		coverToolInstalled() &&
		ensureReportDirectoryExists(reports))
}
func goVersion_1_2_orGreater() bool {
	version := runtime.Version() // 'go1.2....'
	major, minor := version[2], version[4]
	version_1_2 := major >= byte('1') && minor >= byte('2')
	if !version_1_2 {
		log.Printf(pleaseUpgradeGoVersion, version)
		return false
	}
	return true
}
func coverToolInstalled() bool {
	working, err := os.Getwd()
	if err != nil {
		working = "."
	}
	output, _ := execute(working, "go", "tool", "cover")
	installed := strings.Contains(output, "Usage of 'go tool cover':")
	if !installed {
		log.Print(coverToolMissing)
		return false
	}
	return true
}
func ensureReportDirectoryExists(reports string) bool {
	if exists(reports) {
		return true
	}

	if err := os.Mkdir(reports, 0755); err == nil {
		return true
	}

	log.Printf(reportDirectoryUnavailable, reports)
	return false
}
func exists(path string) bool {
	_, err := os.Stat(path)
	if err == nil {
		return true
	}
	if os.IsNotExist(err) {
		return false
	}
	return false
}

func sleeper() {
	time.Sleep(nap)
}

var (
	port     int
	host     string
	gobin    string
	nap      time.Duration
	packages int
	cover    bool
	depth    int
	short    bool

	static  string
	reports string

	quarterSecond = time.Millisecond * 250
)

const (
	initialConfiguration = "Initial configuration: [host: %s] [port: %d] [poll: %v] [cover: %v] [short: %v]\n"
)
<commit_msg>Fixed compilation error.<commit_after>// This executable provides an HTTP server that watches for file system changes
// to .go files within the working directory (and all nested go packages).
// Navigating to the configured host and port in a web browser will display the
// latest results of running `go test` in each go package.

package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/smartystreets/goconvey/web/server/api"
	"github.com/smartystreets/goconvey/web/server/contract"
	executor "github.com/smartystreets/goconvey/web/server/executor"
	parser "github.com/smartystreets/goconvey/web/server/parser"
	"github.com/smartystreets/goconvey/web/server/system"
	watch "github.com/smartystreets/goconvey/web/server/watcher"
)

func init() {
	flags()
	folders()
}
func flags() {
	flag.IntVar(&port, "port", 8080, "The port at which to serve http.")
	flag.StringVar(&host, "host", "127.0.0.1", "The host at which to serve http.")
	flag.DurationVar(&nap, "poll", quarterSecond, "The interval to wait between polling the file system for changes (default: 250ms).")
	flag.IntVar(&packages, "packages", 10, "The number of packages to test in parallel. Higher == faster but more costly in terms of computing. (default: 10)")
	flag.StringVar(&gobin, "gobin", "go", "The path to the 'go' binary (default: search on the PATH).")
	flag.BoolVar(&cover, "cover", true, "Enable package-level coverage statistics. Requires Go 1.2+ and the go cover tool. (default: true)")
	flag.IntVar(&depth, "depth", -1, "The directory scanning depth. If -1, scan infinitely deep directory structures. 0: scan working directory. 1+: Scan into nested directories, limited to value. (default: -1)")
	flag.BoolVar(&short, "short", false, "Configures the `testing.Short()` function to return `true`, allowing you to call `t.Skip()` on long-running tests.")

	log.SetOutput(os.Stdout)
	log.SetFlags(log.LstdFlags | log.Lshortfile)
}
func folders() {
	_, file, _, _ := runtime.Caller(0)
	here := filepath.Dir(file)
	static = filepath.Join(here, "/web/client")
	reports = filepath.Join(static, "reports")
}

func main() {
	flag.Parse()

	log.Printf(initialConfiguration, host, port, nap, cover, short)

	monitor, server := wireup()

	go monitor.ScanForever()

	serveHTTP(server)
}

func serveHTTP(server contract.Server) {
	serveStaticResources()
	serveAjaxMethods(server)
	activateServer()
}

func serveStaticResources() {
	http.Handle("/", http.FileServer(http.Dir(static)))
}

func serveAjaxMethods(server contract.Server) {
	http.HandleFunc("/watch", server.Watch)
	http.HandleFunc("/ignore", server.Ignore)
	http.HandleFunc("/reinstate", server.Reinstate)
	http.HandleFunc("/latest", server.Results)
	http.HandleFunc("/execute", server.Execute)
	http.HandleFunc("/status", server.Status)
	http.HandleFunc("/status/poll", server.LongPollStatus)
	http.HandleFunc("/pause", server.TogglePause)
}

func activateServer() {
	log.Printf("Serving HTTP at: http://%s:%d\n", host, port)
	err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
	if err != nil {
		fmt.Println(err)
	}
}

func wireup() (*contract.Monitor, contract.Server) {
	log.Println("Constructing components...")
	working, err := os.Getwd()
	if err != nil {
		log.Fatal(err)
	}

	cover = coverageEnabled(cover, reports)

	depthLimit := system.NewDepthLimit(system.NewFileSystem(), depth)
	shell := system.NewShell(gobin, short, cover, reports)

	watcher := watch.NewWatcher(depthLimit, shell)
	watcher.Adjust(working)

	parser := parser.NewParser(parser.ParsePackageResults)
	tester := executor.NewConcurrentTester(shell)
	tester.SetBatchSize(packages)

	longpollChan, pauseUpdate := make(chan chan string), make(chan bool, 1)
	executor := executor.NewExecutor(tester, parser, longpollChan)
	server := api.NewHTTPServer(watcher, executor, longpollChan, pauseUpdate)
	scanner := watch.NewScanner(depthLimit, watcher)
	monitor := contract.NewMonitor(scanner, watcher, executor, server, pauseUpdate, sleeper)

	return monitor, server
}

func coverageEnabled(cover bool, reports string) bool {
	return (cover &&
		goVersion_1_2_orGreater() &&
		coverToolInstalled() &&
		ensureReportDirectoryExists(reports))
}
func goVersion_1_2_orGreater() bool {
	version := runtime.Version() // 'go1.2....'
	major, minor := version[2], version[4]
	version_1_2 := major >= byte('1') && minor >= byte('2')
	if !version_1_2 {
		log.Printf(pleaseUpgradeGoVersion, version)
		return false
	}
	return true
}
func coverToolInstalled() bool {
	working, err := os.Getwd()
	if err != nil {
		working = "."
	}
	output, _ := execute(working, "go", "tool", "cover")
	installed := strings.Contains(output, "Usage of 'go tool cover':")
	if !installed {
		log.Print(coverToolMissing)
		return false
	}
	return true
}
func ensureReportDirectoryExists(reports string) bool {
	if exists(reports) {
		return true
	}

	if err := os.Mkdir(reports, 0755); err == nil {
		return true
	}

	log.Printf(reportDirectoryUnavailable, reports)
	return false
}
func exists(path string) bool {
	_, err := os.Stat(path)
	if err == nil {
		return true
	}
	if os.IsNotExist(err) {
		return false
	}
	return false
}

func sleeper() {
	time.Sleep(nap)
}

var (
	port     int
	host     string
	gobin    string
	nap      time.Duration
	packages int
	cover    bool
	depth    int
	short    bool

	static  string
	reports string

	quarterSecond = time.Millisecond * 250
)

const (
	initialConfiguration = "Initial configuration: [host: %s] [port: %d] [poll: %v] [cover: %v] [short: %v]\n"
)
<|endoftext|>