<commit_before>package main

import (
	"os"
	"fmt"
	"time"
	"flag"
	"sort"
	"bufio"
	"strconv"
	"strings"
	"text/template"
)

var (
	scoresPath = "/home/krowbar/Code/irc/tildescores.txt"
)

const (
	scoreDeltasPath = "/home/bear/scoredeltas.txt"
	deltaDelimiter = "+++"
)

func main() {
	fmt.Println("Starting...")

	// Get any arguments
	outPtr := flag.String("o", "tildescores", "Output file name")
	isTestPtr := flag.Bool("t", false, "Specifies we're developing")
	flag.Parse()

	if *isTestPtr {
		scoresPath = "/home/bear/tildescores.txt"
	}

	headers := []string{ "User", "Tildes", "Last Collected", "Last Amt.", "# Asks", "Avg." }

	scoresData := readData(scoresPath, "&^%")
	updatesData := readData(scoreDeltasPath, deltaDelimiter)

	scoresData = checkScoreDelta(scoresData, updatesData)
	scoresTable := buildScoresTable(scoresData, headers)

	generate("tilde collectors", sortScore(scoresTable), *outPtr)
}

type Table struct {
	Headers []string
	Rows []Row
}

type Row struct {
	Data []string
}

type By func(r1, r2 *Row) bool
func (by By) Sort(rows []Row) {
	rs := &rowSorter {
		rows: rows,
		by: by,
	}
	sort.Sort(rs)
}
type rowSorter struct {
	rows []Row
	by func(r1, r2 *Row) bool
}
func (r *rowSorter) Len() int {
	return len(r.rows)
}
func (r *rowSorter) Swap(i, j int) {
	r.rows[i], r.rows[j] = r.rows[j], r.rows[i]
}
func (r *rowSorter) Less(i, j int) bool {
	return r.by(&r.rows[i], &r.rows[j])
}

func sortScore(table *Table) *Table {
	score := func(r1, r2 *Row) bool {
		s1, _ := strconv.Atoi(r1.Data[1])
		s2, _ := strconv.Atoi(r2.Data[1])
		return s1 < s2
	}
	decScore := func(r1, r2 *Row) bool {
		return !score(r1, r2)
	}
	By(decScore).Sort(table.Rows)

	return table
}

func parseTimestamp(ts string) time.Time {
	t, err := strconv.ParseInt(ts, 10, 64)
	if err != nil {
		panic(err)
	}
	return time.Unix(t, 0)
}

type LastScore struct {
	LastUpdate int
	LastScore int
	LastIncrement int
	Times int
	ScoreOffset int
	TimesOffset int
}

func checkScoreDelta(scoreRows *[]Row, deltaRows *[]Row) *[]Row {
	users := make(map[string]LastScore)

	// Read score delta data
	for i := range *deltaRows {
		r := (*deltaRows)[i]

		if len(r.Data) < 4 {
			break
		}

		score, _ := strconv.Atoi(r.Data[1])
		update, _ := strconv.Atoi(r.Data[2])
		inc, _ := strconv.Atoi(r.Data[3])
		times, _ := strconv.Atoi(r.Data[4])
		so, _ := strconv.Atoi(r.Data[5])
		to, _ := strconv.Atoi(r.Data[6])

		users[r.Data[0]] = LastScore{ LastScore: score, LastUpdate: update, LastIncrement: inc, Times: times, ScoreOffset: so, TimesOffset: to }
	}

	for i := range *scoreRows {
		r := (*scoreRows)[i]
		u, exists := users[r.Data[0]]

		score, _ := strconv.Atoi(r.Data[1])
		update, _ := strconv.Atoi(r.Data[2])

		// Fill in any missing users
		if !exists {
			u = LastScore{ LastScore: score, LastIncrement: -1, LastUpdate: update, Times: 0, ScoreOffset: score, TimesOffset: 0 }
			users[r.Data[0]] = u
		}

		// Match up "last collection" with rest of table data
		if update > u.LastUpdate {
			u.LastIncrement = score - u.LastScore
			u.LastUpdate = update
			u.LastScore = score
			u.Times++
		}

		var lastIncStr string
		if u.LastIncrement > -1 {
			lastIncStr = strconv.Itoa(u.LastIncrement)
		} else {
			lastIncStr = "-"
		}
		r.Data = append(r.Data, lastIncStr)

		var asksStr string
		if u.Times > 0 {
			asksStr = strconv.Itoa(u.Times)
		} else {
			asksStr = "-"
		}
		r.Data = append(r.Data, asksStr)

		var avgStr string
		if u.Times > 0 {
			var avg float64 = float64(u.LastScore - u.ScoreOffset) / float64(u.Times)
			avgStr = strconv.FormatFloat(avg, 'f', -1, 32)
		} else {
			avgStr = "-"
		}
		r.Data = append(r.Data, avgStr)

		users[r.Data[0]] = u
		(*scoreRows)[i] = r
	}

	// Write deltas
	f, err := os.OpenFile(scoreDeltasPath, os.O_CREATE | os.O_RDWR, 0644)
	if err != nil {
		fmt.Println(err)
	}
	defer f.Close()

	for k, v := range users {
		userData := fmt.Sprintf("%d%s%d%s%d%s%d%s%d%s%d", v.LastScore, deltaDelimiter, v.LastUpdate, deltaDelimiter, v.LastIncrement, deltaDelimiter, v.Times, deltaDelimiter, v.ScoreOffset, deltaDelimiter, v.TimesOffset)
		_, err = f.WriteString(fmt.Sprintf("%s%s%s\n", k, deltaDelimiter, userData))
		if err != nil {
			fmt.Println(err)
		}
	}

	return scoreRows
}

func buildScoresTable(rows *[]Row, headers []string) *Table {
	table := &Table{Headers: headers, Rows: nil}

	const layout = "Jan 2, 2006 3:04pm MST"
	for i, r := range *rows {
		data := r.Data
		t := parseTimestamp(r.Data[2])
		r.Data[2] = t.UTC().Format(layout)
		row := &Row{Data: data}
		(*rows)[i] = *row
	}
	table.Rows = *rows

	return table
}

func readData(path string, delimiter string) *[]Row {
	f, _ := os.Open(path)

	defer f.Close()

	rows := []Row{}
	s := bufio.NewScanner(f)
	s.Split(bufio.ScanLines)

	for s.Scan() {
		data := strings.Split(s.Text(), delimiter)
		row := &Row{Data: data}
		rows = append(rows, *row)
	}

	return &rows
}

type Page struct {
	Title string
	Table Table
	Updated string
	UpdatedForHumans string
}

func add(x, y int) int {
	return x + y
}

func generate(title string, table *Table, outputFile string) {
	fmt.Println("Generating page.")

	f, err := os.Create(os.Getenv("HOME") + "/public_html/" + outputFile + ".html")
	if err != nil {
		panic(err)
	}

	defer f.Close()

	funcMap := template.FuncMap {
		"add": add,
	}

	w := bufio.NewWriter(f)
	template, err := template.New("").Funcs(funcMap).ParseFiles("templates/table.html")
	if err != nil {
		panic(err)
	}

	// Extra page data
	curTime := time.Now().UTC()
	updatedReadable := curTime.Format(time.RFC1123)
	updated := curTime.Format(time.RFC3339)

	// Generate the page
	page := &Page{Title: title, Table: *table, UpdatedForHumans: updatedReadable, Updated: updated}
	template.ExecuteTemplate(w, "table", page)
	w.Flush()

	fmt.Println("DONE!")
}
<commit_msg>Move "Last Amt." column to end<commit_after>package main

import (
	"os"
	"fmt"
	"time"
	"flag"
	"sort"
	"bufio"
	"strconv"
	"strings"
	"text/template"
)

var (
	scoresPath = "/home/krowbar/Code/irc/tildescores.txt"
)

const (
	scoreDeltasPath = "/home/bear/scoredeltas.txt"
	deltaDelimiter = "+++"
)

func main() {
	fmt.Println("Starting...")

	// Get any arguments
	outPtr := flag.String("o", "tildescores", "Output file name")
	isTestPtr := flag.Bool("t", false, "Specifies we're developing")
	flag.Parse()

	if *isTestPtr {
		scoresPath = "/home/bear/tildescores.txt"
	}

	headers := []string{ "User", "Tildes", "Last Collected", "# Asks", "Avg.", "Last Amt." }

	scoresData := readData(scoresPath, "&^%")
	updatesData := readData(scoreDeltasPath, deltaDelimiter)

	scoresData = checkScoreDelta(scoresData, updatesData)
	scoresTable := buildScoresTable(scoresData, headers)

	generate("tilde collectors", sortScore(scoresTable), *outPtr)
}

type Table struct {
	Headers []string
	Rows []Row
}

type Row struct {
	Data []string
}

type By func(r1, r2 *Row) bool
func (by By) Sort(rows []Row) {
	rs := &rowSorter {
		rows: rows,
		by: by,
	}
	sort.Sort(rs)
}
type rowSorter struct {
	rows []Row
	by func(r1, r2 *Row) bool
}
func (r *rowSorter) Len() int {
	return len(r.rows)
}
func (r *rowSorter) Swap(i, j int) {
	r.rows[i], r.rows[j] = r.rows[j], r.rows[i]
}
func (r *rowSorter) Less(i, j int) bool {
	return r.by(&r.rows[i], &r.rows[j])
}

func sortScore(table *Table) *Table {
	score := func(r1, r2 *Row) bool {
		s1, _ := strconv.Atoi(r1.Data[1])
		s2, _ := strconv.Atoi(r2.Data[1])
		return s1 < s2
	}
	decScore := func(r1, r2 *Row) bool {
		return !score(r1, r2)
	}
	By(decScore).Sort(table.Rows)

	return table
}

func parseTimestamp(ts string) time.Time {
	t, err := strconv.ParseInt(ts, 10, 64)
	if err != nil {
		panic(err)
	}
	return time.Unix(t, 0)
}

type LastScore struct {
	LastUpdate int
	LastScore int
	LastIncrement int
	Times int
	ScoreOffset int
	TimesOffset int
}

func checkScoreDelta(scoreRows *[]Row, deltaRows *[]Row) *[]Row {
	users := make(map[string]LastScore)

	// Read score delta data
	for i := range *deltaRows {
		r := (*deltaRows)[i]

		if len(r.Data) < 4 {
			break
		}

		score, _ := strconv.Atoi(r.Data[1])
		update, _ := strconv.Atoi(r.Data[2])
		inc, _ := strconv.Atoi(r.Data[3])
		times, _ := strconv.Atoi(r.Data[4])
		so, _ := strconv.Atoi(r.Data[5])
		to, _ := strconv.Atoi(r.Data[6])

		users[r.Data[0]] = LastScore{ LastScore: score, LastUpdate: update, LastIncrement: inc, Times: times, ScoreOffset: so, TimesOffset: to }
	}

	for i := range *scoreRows {
		r := (*scoreRows)[i]
		u, exists := users[r.Data[0]]

		score, _ := strconv.Atoi(r.Data[1])
		update, _ := strconv.Atoi(r.Data[2])

		// Fill in any missing users
		if !exists {
			u = LastScore{ LastScore: score, LastIncrement: -1, LastUpdate: update, Times: 0, ScoreOffset: score, TimesOffset: 0 }
			users[r.Data[0]] = u
		}

		// Match up "last collection" with rest of table data
		if update > u.LastUpdate {
			u.LastIncrement = score - u.LastScore
			u.LastUpdate = update
			u.LastScore = score
			u.Times++
		}

		var asksStr string
		if u.Times > 0 {
			asksStr = strconv.Itoa(u.Times)
		} else {
			asksStr = "-"
		}
		r.Data = append(r.Data, asksStr)

		var avgStr string
		if u.Times > 0 {
			var avg float64 = float64(u.LastScore - u.ScoreOffset) / float64(u.Times)
			avgStr = strconv.FormatFloat(avg, 'f', -1, 32)
		} else {
			avgStr = "-"
		}
		r.Data = append(r.Data, avgStr)

		var lastIncStr string
		if u.LastIncrement > -1 {
			lastIncStr = strconv.Itoa(u.LastIncrement)
		} else {
			lastIncStr = "-"
		}
		r.Data = append(r.Data, lastIncStr)

		users[r.Data[0]] = u
		(*scoreRows)[i] = r
	}

	// Write deltas
	f, err := os.OpenFile(scoreDeltasPath, os.O_CREATE | os.O_RDWR, 0644)
	if err != nil {
		fmt.Println(err)
	}
	defer f.Close()

	for k, v := range users {
		userData := fmt.Sprintf("%d%s%d%s%d%s%d%s%d%s%d", v.LastScore, deltaDelimiter, v.LastUpdate, deltaDelimiter, v.LastIncrement, deltaDelimiter, v.Times, deltaDelimiter, v.ScoreOffset, deltaDelimiter, v.TimesOffset)
		_, err = f.WriteString(fmt.Sprintf("%s%s%s\n", k, deltaDelimiter, userData))
		if err != nil {
			fmt.Println(err)
		}
	}

	return scoreRows
}

func buildScoresTable(rows *[]Row, headers []string) *Table {
	table := &Table{Headers: headers, Rows: nil}

	const layout = "Jan 2, 2006 3:04pm MST"
	for i, r := range *rows {
		data := r.Data
		t := parseTimestamp(r.Data[2])
		r.Data[2] = t.UTC().Format(layout)
		row := &Row{Data: data}
		(*rows)[i] = *row
	}
	table.Rows = *rows

	return table
}

func readData(path string, delimiter string) *[]Row {
	f, _ := os.Open(path)

	defer f.Close()

	rows := []Row{}
	s := bufio.NewScanner(f)
	s.Split(bufio.ScanLines)

	for s.Scan() {
		data := strings.Split(s.Text(), delimiter)
		row := &Row{Data: data}
		rows = append(rows, *row)
	}

	return &rows
}

type Page struct {
	Title string
	Table Table
	Updated string
	UpdatedForHumans string
}

func add(x, y int) int {
	return x + y
}

func generate(title string, table *Table, outputFile string) {
	fmt.Println("Generating page.")

	f, err := os.Create(os.Getenv("HOME") + "/public_html/" + outputFile + ".html")
	if err != nil {
		panic(err)
	}

	defer f.Close()

	funcMap := template.FuncMap {
		"add": add,
	}

	w := bufio.NewWriter(f)
	template, err := template.New("").Funcs(funcMap).ParseFiles("templates/table.html")
	if err != nil {
		panic(err)
	}

	// Extra page data
	curTime := time.Now().UTC()
	updatedReadable := curTime.Format(time.RFC1123)
	updated := curTime.Format(time.RFC3339)

	// Generate the page
	page := &Page{Title: title, Table: *table, UpdatedForHumans: updatedReadable, Updated: updated}
	template.ExecuteTemplate(w, "table", page)
	w.Flush()

	fmt.Println("DONE!")
}
<|endoftext|>


<commit_before>package gotabulate

import "fmt"
import "bytes"
import "github.com/mattn/go-runewidth"
import "math"

// Basic Structure of TableFormat
type TableFormat struct {
	LineTop         Line
	LineBelowHeader Line
	LineBetweenRows Line
	LineBottom      Line
	HeaderRow       Row
	DataRow         Row
	Padding         int
	HeaderHide      bool
	FitScreen       bool
}

// Represents a Line
type Line struct {
	begin string
	hline string
	sep   string
	end   string
}

// Represents a Row
type Row struct {
	begin string
	sep   string
	end   string
}

// Table Formats that are available to the user
// The user can define his own format, just by addind an entry to this map
// and calling it with Render function e.g t.Render("customFormat")
var TableFormats = map[string]TableFormat{
	"simple": TableFormat{
		LineTop:         Line{"", "-", "  ", ""},
		LineBelowHeader: Line{"", "-", "  ", ""},
		LineBottom:      Line{"", "-", "  ", ""},
		HeaderRow:       Row{"", "  ", ""},
		DataRow:         Row{"", "  ", ""},
		Padding:         1,
	},
	"plain": TableFormat{
		HeaderRow: Row{"", "  ", ""},
		DataRow:   Row{"", "  ", ""},
		Padding:   1,
	},
	"grid": TableFormat{
		LineTop:         Line{"+", "-", "+", "+"},
		LineBelowHeader: Line{"+", "=", "+", "+"},
		LineBetweenRows: Line{"+", "-", "+", "+"},
		LineBottom:      Line{"+", "-", "+", "+"},
		HeaderRow:       Row{"|", "|", "|"},
		DataRow:         Row{"|", "|", "|"},
		Padding:         1,
	},
}

// Minimum padding that will be applied
var MIN_PADDING = 5

// Main Tabulate structure
type Tabulate struct {
	Data        []*TabulateRow
	Headers     []string
	FloatFormat byte
	TableFormat TableFormat
	Align       string
	EmptyVar    string
	HideLines   []string
	MaxSize     int
	WrapStrings bool
}

// Represents normalized tabulate Row
type TabulateRow struct {
	Elements  []string
	Continuos bool
	Extra     bool
}

type writeBuffer struct {
	Buffer bytes.Buffer
}

func createBuffer() *writeBuffer {
	return &writeBuffer{}
}

func (b *writeBuffer) Write(str string, count int) *writeBuffer {
	for i := 0; i < count; i++ {
		b.Buffer.WriteString(str)
	}
	return b
}
func (b *writeBuffer) String() string {
	return b.Buffer.String()
}

// Add padding to each cell
func (t *Tabulate) padRow(arr []string, padding int) []string {
	if len(arr) < 1 {
		return arr
	}
	padded := make([]string, len(arr))
	for index, el := range arr {
		b := createBuffer()
		b.Write(" ", padding)
		b.Write(el, 1)
		b.Write(" ", padding)
		padded[index] = b.String()
	}
	return padded
}

// Align right (Add padding left)
func (t *Tabulate) padLeft(width int, str string) string {
	b := createBuffer()
	b.Write(" ", (width - runewidth.StringWidth(str)))
	b.Write(str, 1)
	return b.String()
}

// Align Left (Add padding right)
func (t *Tabulate) padRight(width int, str string) string {
	b := createBuffer()
	b.Write(str, 1)
	b.Write(" ", (width - runewidth.StringWidth(str)))
	return b.String()
}

// Center the element in the cell
func (t *Tabulate) padCenter(width int, str string) string {
	b := createBuffer()
	padding := int(math.Ceil(float64((width - runewidth.StringWidth(str))) / 2.0))
	b.Write(" ", padding)
	b.Write(str, 1)
	b.Write(" ", (width - runewidth.StringWidth(b.String())))

	return b.String()
}

// Build Line based on padded_widths from t.GetWidths()
func (t *Tabulate) buildLine(padded_widths []int, padding []int, l Line) string {
	cells := make([]string, len(padded_widths))

	for i, _ := range cells {
		b := createBuffer()
		b.Write(l.hline, padding[i]+MIN_PADDING)
		cells[i] = b.String()
	}

	var buffer bytes.Buffer
	buffer.WriteString(l.begin)

	// Print contents
	for i := 0; i < len(cells); i++ {
		buffer.WriteString(cells[i])
		if i != len(cells)-1 {
			buffer.WriteString(l.sep)
		}
	}

	buffer.WriteString(l.end)
	return buffer.String()
}

// Build Row based on padded_widths from t.GetWidths()
func (t *Tabulate) buildRow(elements []string, padded_widths []int, paddings []int, d Row) string {

	var buffer bytes.Buffer
	buffer.WriteString(d.begin)
	padFunc := t.getAlignFunc()
	// Print contents
	for i := 0; i < len(padded_widths); i++ {
		output := ""
		if len(elements) <= i || (len(elements) > i && elements[i] == " nil ") {
			output = padFunc(padded_widths[i], t.EmptyVar)
		} else if len(elements) > i {
			output = padFunc(padded_widths[i], elements[i])
		}
		buffer.WriteString(output)
		if i != len(padded_widths)-1 {
			buffer.WriteString(d.sep)
		}
	}

	buffer.WriteString(d.end)
	return buffer.String()
}

// Render the data table
func (t *Tabulate) Render(format ...interface{}) string {
	var lines []string

	// If headers are set use them, otherwise pop the first row
	if len(t.Headers) < 1 {
		t.Headers, t.Data = t.Data[0].Elements, t.Data[1:]
	}

	// Use the format that was passed as parameter, otherwise
	// use the format defined in the struct
	if len(format) > 0 {
		t.TableFormat = TableFormats[format[0].(string)]
	}

	// If Wrap Strings is set to True,then break up the string to multiple cells
	if t.WrapStrings {
		t.Data = t.wrapCellData()
	}

	// Check if Data is present
	if len(t.Data) < 1 {
		panic("No Data specified")
	}

	if len(t.Headers) < len(t.Data[0].Elements) {
		diff := len(t.Data[0].Elements) - len(t.Headers)
		padded_header := make([]string, diff)
		for _, e := range t.Headers {
			padded_header = append(padded_header, e)
		}
		t.Headers = padded_header
	}

	// Get Column widths for all columns
	cols := t.getWidths(t.Headers, t.Data)

	padded_widths := make([]int, len(cols))
	for i, _ := range padded_widths {
		padded_widths[i] = cols[i] + MIN_PADDING*t.TableFormat.Padding
	}

	// Start appending lines

	// Append top line if not hidden
	if !inSlice("top", t.HideLines) {
		lines = append(lines, t.buildLine(padded_widths, cols, t.TableFormat.LineTop))
	}

	// Add Header
	lines = append(lines, t.buildRow(t.padRow(t.Headers, t.TableFormat.Padding), padded_widths, cols, t.TableFormat.HeaderRow))

	// Add Line Below Header if not hidden
	if !inSlice("belowheader", t.HideLines) {
		lines = append(lines, t.buildLine(padded_widths, cols, t.TableFormat.LineBelowHeader))
	}

	// Add Data Rows
	for index, element := range t.Data {
		lines = append(lines, t.buildRow(t.padRow(element.Elements, t.TableFormat.Padding), padded_widths, cols, t.TableFormat.DataRow))
		if index < len(t.Data)-1 {
			if element.Continuos != true {
				lines = append(lines, t.buildLine(padded_widths, cols, t.TableFormat.LineBetweenRows))
			}
		}
	}

	if !inSlice("bottomLine", t.HideLines) {
		lines = append(lines, t.buildLine(padded_widths, cols, t.TableFormat.LineBottom))
	}

	// Join lines
	var buffer bytes.Buffer
	for _, line := range lines {
		buffer.WriteString(line + "\n")
	}

	return buffer.String()
}

// Calculate the max column width for each element
func (t *Tabulate) getWidths(headers []string, data []*TabulateRow) []int {
	widths := make([]int, len(headers))
	current_max := len(t.EmptyVar)
	for i := 0; i < len(headers); i++ {
		current_max = runewidth.StringWidth(headers[i])
		for _, item := range data {
			if len(item.Elements) > i && len(widths) > i {
				element := item.Elements[i]
				if runewidth.StringWidth(element) > current_max {
					widths[i] = runewidth.StringWidth(element)
					current_max = runewidth.StringWidth(element)
				} else {
					widths[i] = current_max
				}
			}
		}
	}

	return widths
}

// Set Headers of the table
// If Headers count is less than the data row count, the headers will be padded to the right
func (t *Tabulate) SetHeaders(headers []string) *Tabulate {
	t.Headers = headers
	return t
}

// Set Float Formatting
// will be used in strconv.FormatFloat(element, format, -1, 64)
func (t *Tabulate) SetFloatFormat(format byte) *Tabulate {
	t.FloatFormat = format
	return t
}

// Set Align Type, Available options: left, right, center
func (t *Tabulate) SetAlign(align string) {
	t.Align = align
}

// Select the padding function based on the align type
func (t *Tabulate) getAlignFunc() func(int, string) string {
	if len(t.Align) < 1 || t.Align == "right" {
		return t.padLeft
	} else if t.Align == "left" {
		return t.padRight
	} else {
		return t.padCenter
	}
}

// Set how an empty cell will be represented
func (t *Tabulate) SetEmptyString(empty string) {
	t.EmptyVar = empty + " "
}

// Set which lines to hide.
// Can be:
// top - Top line of the table,
// belowheader - Line below the header,
// bottom - Bottom line of the table
func (t *Tabulate) SetHideLines(hide []string) {
	t.HideLines = hide
}

func (t *Tabulate) SetWrapStrings(wrap bool) {
	t.WrapStrings = wrap
}

// Sets the maximum size of cell
// If WrapStrings is set to true, then the string inside
// the cell will be split up into multiple cell
func (t *Tabulate) SetMaxCellSize(max int) {
	t.MaxSize = max
}

// If string size is larger than t.MaxSize, then split it to multiple cells (downwards)
func (t *Tabulate) wrapCellData() []*TabulateRow {
	var arr []*TabulateRow
	next := t.Data[0]
	for index := 0; index <= len(t.Data); index++ {
		elements := next.Elements
		new_elements := make([]string, len(elements))

		for i, e := range elements {
			if runewidth.StringWidth(e) > t.MaxSize {
				elements[i] = runewidth.Truncate(e, t.MaxSize, "")
				new_elements[i] = e[len(elements[i]):]
				next.Continuos = true
			}
		}

		if next.Continuos {
			arr = append(arr, next)
			next = &TabulateRow{Elements: new_elements, Extra: true}
			index--
		} else if next.Extra && index+1 < len(t.Data) {
			arr = append(arr, next)
			next = t.Data[index+1]
		} else if index+1 < len(t.Data) {
			arr = append(arr, next)
			next = t.Data[index+1]
		} else if index >= len(t.Data) {
			arr = append(arr, next)
		}

	}
	return arr
}

// Create a new Tabulate Object
// Accepts 2D String Array, 2D Int Array, 2D Int64 Array,
// 2D Bool Array, 2D Float64 Array, 2D interface{} Array,
// Map map[strig]string, Map map[string]interface{},
func Create(data interface{}) *Tabulate {
	t := &Tabulate{FloatFormat: 'f', MaxSize: 30}

	switch v := data.(type) {
	case [][]string:
		t.Data = createFromString(data.([][]string))
	case [][]int32:
		t.Data = createFromInt32(data.([][]int32))
	case [][]int64:
		t.Data = createFromInt64(data.([][]int64))
	case [][]int:
		t.Data = createFromInt(data.([][]int))
	case [][]bool:
		t.Data = createFromBool(data.([][]bool))
	case [][]float64:
		t.Data = createFromFloat64(data.([][]float64), t.FloatFormat)
	case [][]interface{}:
		t.Data = createFromMixed(data.([][]interface{}), t.FloatFormat)
	case []string:
		t.Data = createFromString([][]string{data.([]string)})
	case []interface{}:
		t.Data = createFromMixed([][]interface{}{data.([]interface{})}, t.FloatFormat)
	case map[string][]interface{}:
		t.Headers, t.Data = createFromMapMixed(data.(map[string][]interface{}), t.FloatFormat)
	case map[string][]string:
		t.Headers, t.Data = createFromMapString(data.(map[string][]string))
	default:
		fmt.Println(v)
	}

	return t
}
<commit_msg>Clean up<commit_after>package gotabulate

import "fmt"
import "bytes"
import "github.com/mattn/go-runewidth"
import "math"

// Basic Structure of TableFormat
type TableFormat struct {
	LineTop         Line
	LineBelowHeader Line
	LineBetweenRows Line
	LineBottom      Line
	HeaderRow       Row
	DataRow         Row
	Padding         int
	HeaderHide      bool
	FitScreen       bool
}

// Represents a Line
type Line struct {
	begin string
	hline string
	sep   string
	end   string
}

// Represents a Row
type Row struct {
	begin string
	sep   string
	end   string
}

// Table Formats that are available to the user
// The user can define his own format, just by addind an entry to this map
// and calling it with Render function e.g t.Render("customFormat")
var TableFormats = map[string]TableFormat{
	"simple": TableFormat{
		LineTop:         Line{"", "-", "  ", ""},
		LineBelowHeader: Line{"", "-", "  ", ""},
		LineBottom:      Line{"", "-", "  ", ""},
		HeaderRow:       Row{"", "  ", ""},
		DataRow:         Row{"", "  ", ""},
		Padding:         1,
	},
	"plain": TableFormat{
		HeaderRow: Row{"", "  ", ""},
		DataRow:   Row{"", "  ", ""},
		Padding:   1,
	},
	"grid": TableFormat{
		LineTop:         Line{"+", "-", "+", "+"},
		LineBelowHeader: Line{"+", "=", "+", "+"},
		LineBetweenRows: Line{"+", "-", "+", "+"},
		LineBottom:      Line{"+", "-", "+", "+"},
		HeaderRow:       Row{"|", "|", "|"},
		DataRow:         Row{"|", "|", "|"},
		Padding:         1,
	},
}

// Minimum padding that will be applied
var MIN_PADDING = 5

// Main Tabulate structure
type Tabulate struct {
	Data        []*TabulateRow
	Headers     []string
	FloatFormat byte
	TableFormat TableFormat
	Align       string
	EmptyVar    string
	HideLines   []string
	MaxSize     int
	WrapStrings bool
}

// Represents normalized tabulate Row
type TabulateRow struct {
	Elements  []string
	Continuos bool
}

type writeBuffer struct {
	Buffer bytes.Buffer
}

func createBuffer() *writeBuffer {
	return &writeBuffer{}
}

func (b *writeBuffer) Write(str string, count int) *writeBuffer {
	for i := 0; i < count; i++ {
		b.Buffer.WriteString(str)
	}
	return b
}
func (b *writeBuffer) String() string {
	return b.Buffer.String()
}

// Add padding to each cell
func (t *Tabulate) padRow(arr []string, padding int) []string {
	if len(arr) < 1 {
		return arr
	}
	padded := make([]string, len(arr))
	for index, el := range arr {
		b := createBuffer()
		b.Write(" ", padding)
		b.Write(el, 1)
		b.Write(" ", padding)
		padded[index] = b.String()
	}
	return padded
}

// Align right (Add padding left)
func (t *Tabulate) padLeft(width int, str string) string {
	b := createBuffer()
	b.Write(" ", (width - runewidth.StringWidth(str)))
	b.Write(str, 1)
	return b.String()
}

// Align Left (Add padding right)
func (t *Tabulate) padRight(width int, str string) string {
	b := createBuffer()
	b.Write(str, 1)
	b.Write(" ", (width - runewidth.StringWidth(str)))
	return b.String()
}

// Center the element in the cell
func (t *Tabulate) padCenter(width int, str string) string {
	b := createBuffer()
	padding := int(math.Ceil(float64((width - runewidth.StringWidth(str))) / 2.0))
	b.Write(" ", padding)
	b.Write(str, 1)
	b.Write(" ", (width - runewidth.StringWidth(b.String())))

	return b.String()
}

// Build Line based on padded_widths from t.GetWidths()
func (t *Tabulate) buildLine(padded_widths []int, padding []int, l Line) string {
	cells := make([]string, len(padded_widths))

	for i, _ := range cells {
		b := createBuffer()
		b.Write(l.hline, padding[i]+MIN_PADDING)
		cells[i] = b.String()
	}

	var buffer bytes.Buffer
	buffer.WriteString(l.begin)

	// Print contents
	for i := 0; i < len(cells); i++ {
		buffer.WriteString(cells[i])
		if i != len(cells)-1 {
			buffer.WriteString(l.sep)
		}
	}

	buffer.WriteString(l.end)
	return buffer.String()
}

// Build Row based on padded_widths from t.GetWidths()
func (t *Tabulate) buildRow(elements []string, padded_widths []int, paddings []int, d Row) string {

	var buffer bytes.Buffer
	buffer.WriteString(d.begin)
	padFunc := t.getAlignFunc()
	// Print contents
	for i := 0; i < len(padded_widths); i++ {
		output := ""
		if len(elements) <= i || (len(elements) > i && elements[i] == " nil ") {
			output = padFunc(padded_widths[i], t.EmptyVar)
		} else if len(elements) > i {
			output = padFunc(padded_widths[i], elements[i])
		}
		buffer.WriteString(output)
		if i != len(padded_widths)-1 {
			buffer.WriteString(d.sep)
		}
	}

	buffer.WriteString(d.end)
	return buffer.String()
}

// Render the data table
func (t *Tabulate) Render(format ...interface{}) string {
	var lines []string

	// If headers are set use them, otherwise pop the first row
	if len(t.Headers) < 1 {
		t.Headers, t.Data = t.Data[0].Elements, t.Data[1:]
	}

	// Use the format that was passed as parameter, otherwise
	// use the format defined in the struct
	if len(format) > 0 {
		t.TableFormat = TableFormats[format[0].(string)]
	}

	// If Wrap Strings is set to True,then break up the string to multiple cells
	if t.WrapStrings {
		t.Data = t.wrapCellData()
	}

	// Check if Data is present
	if len(t.Data) < 1 {
		panic("No Data specified")
	}

	if len(t.Headers) < len(t.Data[0].Elements) {
		diff := len(t.Data[0].Elements) - len(t.Headers)
		padded_header := make([]string, diff)
		for _, e := range t.Headers {
			padded_header = append(padded_header, e)
		}
		t.Headers = padded_header
	}

	// Get Column widths for all columns
	cols := t.getWidths(t.Headers, t.Data)

	padded_widths := make([]int, len(cols))
	for i, _ := range padded_widths {
		padded_widths[i] = cols[i] + MIN_PADDING*t.TableFormat.Padding
	}

	// Start appending lines

	// Append top line if not hidden
	if !inSlice("top", t.HideLines) {
		lines = append(lines, t.buildLine(padded_widths, cols, t.TableFormat.LineTop))
	}

	// Add Header
	lines = append(lines, t.buildRow(t.padRow(t.Headers, t.TableFormat.Padding), padded_widths, cols, t.TableFormat.HeaderRow))

	// Add Line Below Header if not hidden
	if !inSlice("belowheader", t.HideLines) {
		lines = append(lines, t.buildLine(padded_widths, cols, t.TableFormat.LineBelowHeader))
	}

	// Add Data Rows
	for index, element := range t.Data {
		lines = append(lines, t.buildRow(t.padRow(element.Elements, t.TableFormat.Padding), padded_widths, cols, t.TableFormat.DataRow))
		if index < len(t.Data)-1 {
			if element.Continuos != true {
				lines = append(lines, t.buildLine(padded_widths, cols, t.TableFormat.LineBetweenRows))
			}
		}
	}

	if !inSlice("bottomLine", t.HideLines) {
		lines = append(lines, t.buildLine(padded_widths, cols, t.TableFormat.LineBottom))
	}

	// Join lines
	var buffer bytes.Buffer
	for _, line := range lines {
		buffer.WriteString(line + "\n")
	}

	return buffer.String()
}

// Calculate the max column width for each element
func (t *Tabulate) getWidths(headers []string, data []*TabulateRow) []int {
	widths := make([]int, len(headers))
	current_max := len(t.EmptyVar)
	for i := 0; i < len(headers); i++ {
		current_max = runewidth.StringWidth(headers[i])
		for _, item := range data {
			if len(item.Elements) > i && len(widths) > i {
				element := item.Elements[i]
				strLength := runewidth.StringWidth(element)
				if strLength > current_max {
					widths[i] = strLength
					current_max = strLength
				} else {
					widths[i] = current_max
				}
			}
		}
	}

	return widths
}

// Set Headers of the table
// If Headers count is less than the data row count, the headers will be padded to the right
func (t *Tabulate) SetHeaders(headers []string) *Tabulate {
	t.Headers = headers
	return t
}

// Set Float Formatting
// will be used in strconv.FormatFloat(element, format, -1, 64)
func (t *Tabulate) SetFloatFormat(format byte) *Tabulate {
	t.FloatFormat = format
	return t
}

// Set Align Type, Available options: left, right, center
func (t *Tabulate) SetAlign(align string) {
	t.Align = align
}

// Select the padding function based on the align type
func (t *Tabulate) getAlignFunc() func(int, string) string {
	if len(t.Align) < 1 || t.Align == "right" {
		return t.padLeft
	} else if t.Align == "left" {
		return t.padRight
	} else {
		return t.padCenter
	}
}

// Set how an empty cell will be represented
func (t *Tabulate) SetEmptyString(empty string) {
	t.EmptyVar = empty + " "
}

// Set which lines to hide.
// Can be:
// top - Top line of the table,
// belowheader - Line below the header,
// bottom - Bottom line of the table
func (t *Tabulate) SetHideLines(hide []string) {
	t.HideLines = hide
}

func (t *Tabulate) SetWrapStrings(wrap bool) {
	t.WrapStrings = wrap
}

// Sets the maximum size of cell
// If WrapStrings is set to true, then the string inside
// the cell will be split up into multiple cell
func (t *Tabulate) SetMaxCellSize(max int) {
	t.MaxSize = max
}

// If string size is larger than t.MaxSize, then split it to multiple cells (downwards)
func (t *Tabulate) wrapCellData() []*TabulateRow {
	var arr []*TabulateRow
	next := t.Data[0]
	for index := 0; index <= len(t.Data); index++ {
		elements := next.Elements
		new_elements := make([]string, len(elements))

		for i, e := range elements {
			if runewidth.StringWidth(e) > t.MaxSize {
				elements[i] = runewidth.Truncate(e, t.MaxSize, "")
				new_elements[i] = e[len(elements[i]):]
				next.Continuos = true
			}
		}

		if next.Continuos {
			arr = append(arr, next)
			next = &TabulateRow{Elements: new_elements}
			index--
		} else if index+1 < len(t.Data) {
			arr = append(arr, next)
			next = t.Data[index+1]
		} else if index >= len(t.Data) {
			arr = append(arr, next)
		}

	}
	return arr
}

// Create a new Tabulate Object
// Accepts 2D String Array, 2D Int Array, 2D Int64 Array,
// 2D Bool Array, 2D Float64 Array, 2D interface{} Array,
// Map map[strig]string, Map map[string]interface{},
func Create(data interface{}) *Tabulate {
	t := &Tabulate{FloatFormat: 'f', MaxSize: 30}

	switch v := data.(type) {
	case [][]string:
		t.Data = createFromString(data.([][]string))
	case [][]int32:
		t.Data = createFromInt32(data.([][]int32))
	case [][]int64:
		t.Data = createFromInt64(data.([][]int64))
	case [][]int:
		t.Data = createFromInt(data.([][]int))
	case [][]bool:
		t.Data = createFromBool(data.([][]bool))
	case [][]float64:
		t.Data = createFromFloat64(data.([][]float64), t.FloatFormat)
	case [][]interface{}:
		t.Data = createFromMixed(data.([][]interface{}), t.FloatFormat)
	case []string:
		t.Data = createFromString([][]string{data.([]string)})
	case []interface{}:
		t.Data = createFromMixed([][]interface{}{data.([]interface{})}, t.FloatFormat)
	case map[string][]interface{}:
		t.Headers, t.Data = createFromMapMixed(data.(map[string][]interface{}), t.FloatFormat)
	case map[string][]string:
		t.Headers, t.Data = createFromMapString(data.(map[string][]string))
	default:
		fmt.Println(v)
	}

	return t
}
<|endoftext|>


<commit_before>/*
 * Minimal object storage library (C) 2015 Minio, Inc.
 *
 * Licensed 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 objectstorage

import (
	"bytes"
	"io"
)

// Part - message structure for results from the MultiPart
type Part struct {
	Data io.ReadSeeker
	Err  error
	Len  int64
	Num  int // part number
}

// MultiPart reads from io.Reader, partitions the data into chunks of given chunksize, and sends
// each chunk as io.ReadSeeker to the caller over a channel
//
// This method runs until an EOF or error occurs. If an error occurs,
// the method sends the error over the channel and returns.
// Before returning, the channel is always closed.
func MultiPart(reader io.Reader, chunkSize uint64) <-chan Part {
	ch := make(chan Part)
	go multiPartInRoutine(reader, chunkSize, ch)
	return ch
}

func multiPartInRoutine(reader io.Reader, chunkSize uint64, ch chan Part) {
	defer close(ch)
	packet := make([]byte, chunkSize)
	n, err := io.ReadFull(reader, packet)
	if err == io.EOF || err == io.ErrUnexpectedEOF { // short read, only single part return
		ch <- Part{
			Data: bytes.NewReader(packet[0:n]),
			Err:  nil,
			Len:  int64(n),
			Num:  1,
		}
		return
	}
	// catastrophic error send error and return
	if err != nil {
		ch <- Part{
			Data: nil,
			Err:  err,
			Num:  0,
		}
		return
	}
	// send the first part
	var num = 1
	ch <- Part{
		Data: bytes.NewReader(packet),
		Err:  nil,
		Len:  int64(n),
		Num:  num,
	}
	for err == nil {
		var n int
		packet := make([]byte, chunkSize)
		n, err = io.ReadFull(reader, packet)
		if err != nil {
			if err != io.EOF && err != io.ErrUnexpectedEOF {
				ch <- Part{
					Data: nil,
					Err:  err,
					Num:  0,
				}
				return
			}
		}
		num++
		ch <- Part{
			Data: bytes.NewReader(packet[0:n]),
			Err:  nil,
			Len:  int64(n),
			Num:  num,
		}

	}
}
<commit_msg>Do not upload one more last part, break when we hit io.EOF<commit_after>/*
 * Minimal object storage library (C) 2015 Minio, Inc.
 *
 * Licensed 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 objectstorage

import (
	"bytes"
	"io"
)

// Part - message structure for results from the MultiPart
type Part struct {
	Data io.ReadSeeker
	Err  error
	Len  int64
	Num  int // part number
}

// MultiPart reads from io.Reader, partitions the data into chunks of given chunksize, and sends
// each chunk as io.ReadSeeker to the caller over a channel
//
// This method runs until an EOF or error occurs. If an error occurs,
// the method sends the error over the channel and returns.
// Before returning, the channel is always closed.
func MultiPart(reader io.Reader, chunkSize uint64) <-chan Part {
	ch := make(chan Part)
	go multiPartInRoutine(reader, chunkSize, ch)
	return ch
}

func multiPartInRoutine(reader io.Reader, chunkSize uint64, ch chan Part) {
	defer close(ch)
	packet := make([]byte, chunkSize)
	n, err := io.ReadFull(reader, packet)
	if err == io.EOF || err == io.ErrUnexpectedEOF { // short read, only single part return
		ch <- Part{
			Data: bytes.NewReader(packet[0:n]),
			Err:  nil,
			Len:  int64(n),
			Num:  1,
		}
		return
	}
	// catastrophic error send error and return
	if err != nil {
		ch <- Part{
			Data: nil,
			Err:  err,
			Num:  0,
		}
		return
	}
	// send the first part
	var num = 1
	ch <- Part{
		Data: bytes.NewReader(packet),
		Err:  nil,
		Len:  int64(n),
		Num:  num,
	}
	for err == nil {
		var n int
		packet := make([]byte, chunkSize)
		n, err = io.ReadFull(reader, packet)
		if err != nil {
			if err != io.EOF && err != io.ErrUnexpectedEOF {
				ch <- Part{
					Data: nil,
					Err:  err,
					Num:  0,
				}
				return
			}
			break
		}
		num++
		ch <- Part{
			Data: bytes.NewReader(packet[0:n]),
			Err:  nil,
			Len:  int64(n),
			Num:  num,
		}

	}
}
<|endoftext|>


<commit_before>package blocks

import (
	"encoding/json"
	"log"
	"regexp"
)

type opFunc func(interface{}, interface{}) bool

var operators map[string]opFunc

// Filter queries a message for all values that match the given Path parameter
// and compares those values to a rule given an operator and a comparator. If
// any value passes the filter operation then the message is broadcast. If no
// value satisfies the filter operation the message is ignored.
//
// Filter is capable of traversing arrays that contain elements with and
// without keys. For example, if Path is set to
// 		foo.bar[]
// All elements within the "bar" array will be compared to the filter operation.
// In the case of
//		foo.bar[].baz
// Only the value of the "baz" keys within elements of the "bar" array will be
// used for the filter operation.
//
// There are four filter comparators: greater than "gt", less than "lt", equal
// to "eq" and subset of "subset".
//
// gt, lt, eq operations are available for values of a number type.
// eq, subset operations are avilable for values of a string type.
// eq operations are availble for value of a bool or null type.
func Filter(b *Block) {

	type filterRule struct {
		Operator   string
		Path       string
		Comparator interface{}
		Invert     bool
	}

	operators = make(map[string]opFunc)

	operators["eq"] = equals
	operators["gt"] = greaterthan
	operators["lt"] = lessthan
	operators["subset"] = subsetof
	operators["regex"] = regexmatch
	operators["keyin"] = keyin

	var rule *filterRule

	for {
		select {
		case msg := <-b.InChan:
			if rule == nil {
				break
			}

			values := getKeyValues(msg, rule.Path)
			for _, value := range values {
				if operators[rule.Operator](value, rule.Comparator) == !rule.Invert {
					broadcast(b.OutChans, msg)
					break
				}
			}

			if len(values) == 0 && rule.Invert {
				broadcast(b.OutChans, msg)
			}

		case msg := <-b.Routes["set_rule"]:
			if rule == nil {
				rule = &filterRule{}
			}
			// this is used to send back on the response chan
			var ruleRegexString string
			// we can't use the standard unmarshal(msg, rule) as we need to make
			// sure the regex compiles, if supplied.
			newRule := &filterRule{}
			err := json.Unmarshal(msg.Msg, &newRule)
			if err != nil {
				log.Println("found errors during unmarshalling")
				log.Println(err.Error())
				break
			}
			if newRule.Operator == "regex" {
				// regex is a bit of a special case
				c, ok := newRule.Comparator.(string)
				if !ok {
					log.Println("regex must be a string, not setting rule")
					break
				}
				r, err := regexp.Compile(c)
				if err != nil {
					log.Println("regex did not compile, not setting rule")
					log.Println(err.Error())
					break
				}
				rule = newRule
				rule.Comparator = r
				ruleRegexString = r.String()
			} else {
				// the simpler rules don't need any futzing
				rule = newRule
			}
			// send the rule back for the response
			out_rule := rule
			if rule.Operator == "regex" {
				out_rule.Comparator = ruleRegexString
			}
			m, err := json.Marshal(rule)
			if err != nil {
				log.Println("could not marshal new rule")
				break
			}
			msg.ResponseChan <- m
		case msg := <-b.Routes["get_rule"]:
			if rule == nil {
				marshal(msg, &filterRule{})
			} else {
				marshal(msg, rule)
			}
		case msg := <-b.AddChan:
			updateOutChans(msg, b)
		case <-b.QuitChan:
			quit(b)
			return
		}
	}
}
<commit_msg>pointers ftw<commit_after>package blocks

import (
	"encoding/json"
	"log"
	"regexp"
)

type opFunc func(interface{}, interface{}) bool

var operators map[string]opFunc

// Filter queries a message for all values that match the given Path parameter
// and compares those values to a rule given an operator and a comparator. If
// any value passes the filter operation then the message is broadcast. If no
// value satisfies the filter operation the message is ignored.
//
// Filter is capable of traversing arrays that contain elements with and
// without keys. For example, if Path is set to
// 		foo.bar[]
// All elements within the "bar" array will be compared to the filter operation.
// In the case of
//		foo.bar[].baz
// Only the value of the "baz" keys within elements of the "bar" array will be
// used for the filter operation.
//
// There are four filter comparators: greater than "gt", less than "lt", equal
// to "eq" and subset of "subset".
//
// gt, lt, eq operations are available for values of a number type.
// eq, subset operations are avilable for values of a string type.
// eq operations are availble for value of a bool or null type.
func Filter(b *Block) {

	type filterRule struct {
		Operator   string
		Path       string
		Comparator interface{}
		Invert     bool
	}

	operators = make(map[string]opFunc)

	operators["eq"] = equals
	operators["gt"] = greaterthan
	operators["lt"] = lessthan
	operators["subset"] = subsetof
	operators["regex"] = regexmatch
	operators["keyin"] = keyin

	var rule *filterRule

	for {
		select {
		case msg := <-b.InChan:
			if rule == nil {
				break
			}

			values := getKeyValues(msg, rule.Path)
			for _, value := range values {
				if operators[rule.Operator](value, rule.Comparator) == !rule.Invert {
					broadcast(b.OutChans, msg)
					break
				}
			}

			if len(values) == 0 && rule.Invert {
				broadcast(b.OutChans, msg)
			}

		case msg := <-b.Routes["set_rule"]:
			if rule == nil {
				rule = &filterRule{}
			}
			// this is used to send back on the response chan
			var ruleRegexString string
			// we can't use the standard unmarshal(msg, rule) as we need to make
			// sure the regex compiles, if supplied.
			newRule := &filterRule{}
			err := json.Unmarshal(msg.Msg, &newRule)
			if err != nil {
				log.Println("found errors during unmarshalling")
				log.Println(err.Error())
				break
			}
			if newRule.Operator == "regex" {
				// regex is a bit of a special case
				c, ok := newRule.Comparator.(string)
				if !ok {
					log.Println("regex must be a string, not setting rule")
					break
				}
				r, err := regexp.Compile(c)
				if err != nil {
					log.Println("regex did not compile, not setting rule")
					log.Println(err.Error())
					break
				}
				rule = newRule
				rule.Comparator = r
				ruleRegexString = r.String()
			} else {
				// the simpler rules don't need any futzing
				rule = newRule
			}
			// send the rule back for the response
			out_rule := *rule
			if rule.Operator == "regex" {
				// we replace the regex in the outgoing rule with its string
				// representation so we can marshal it correctly.
				out_rule.Comparator = ruleRegexString
			}
			m, err := json.Marshal(out_rule)
			if err != nil {
				log.Println("could not marshal new rule")
				break
			}
			msg.ResponseChan <- m
		case msg := <-b.Routes["get_rule"]:
			if rule == nil {
				marshal(msg, &filterRule{})
			} else {
				marshal(msg, rule)
			}
		case msg := <-b.AddChan:
			updateOutChans(msg, b)
		case <-b.QuitChan:
			quit(b)
			return
		}
	}
}
<|endoftext|>


<commit_before>package main

import "testing"

func check_eval(f Function, t *testing.T, points, vals []complex128) {
	if len(points) != len(vals) {
		panic("len(points) != len(vals)")
	}

	for i, z := range points {
		val := f.Evaluate(z)
		t.Logf("testing at %v, got %v", z, val)
		if val != vals[i] {
			t.Errorf("did not evaluate to expected %v at %v", vals[i], z)
		}
	}
}

func TestConstantZero(t *testing.T) {
	c := constant(0)
	points := []complex128{0, 1, 1i, 1 + 1i, 200 - 50i}
	vals := []complex128{0, 0, 0, 0, 0}
	check_eval(c, t, points, vals)
}

func TestPolyIntegers(t *testing.T) {
	f := NewPoly(1, 2, 1)
	points := []complex128{0, 1, 1i, 1 + 1i, 2 - 3i}
	vals := []complex128{1, 4, 2i, 3 + 4i, -18i}
	check_eval(f, t, points, vals)
}
<commit_msg>Tidy up tests<commit_after>package main

import "testing"

type Test struct {
	in  complex128
	out complex128
}

func run_tests(f Function, t *testing.T, tests []Test) {
	t.Log("testing", f)
	for _, test := range tests {
		val := f.Evaluate(test.in)
		t.Logf("testing at %v, got %v", test.in, val)
		if val != test.out {
			t.Errorf("did not evaluate to expected %v at %v", test.out, test.in)
		}
	}
}

func TestConstantZero(t *testing.T) {
	c := constant(0)
	tests := []Test{
		{0, 0},
		{1, 0},
		{1i, 0},
		{1 + 1i, 0},
		{200 - 50i, 0},
	}
	run_tests(c, t, tests)
}

func TestPolyIntegers(t *testing.T) {
	f := NewPoly(1, 2, 1)
	tests := []Test{
		{0, 1},
		{1, 4},
		{1i, 2i},
		{1 + 1i, 3 + 4i},
		{2 - 3i, -18i},
	}
	run_tests(f, t, tests)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/tls"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"math/rand"
	"net"
	"net/http"
	"net/url"
	"runtime"
	"time"
)

// Console flags
var (
	listen                = flag.String("l", ":8888", "port to accept requests")
	targetProduction      = flag.String("a", "localhost:8080", "where production traffic goes. http://localhost:8080/production")
	altTarget             = flag.String("b", "localhost:8081", "where testing traffic goes. response are skipped. http://localhost:8081/test")
	debug                 = flag.Bool("debug", false, "more logging, showing ignored output")
	productionTimeout     = flag.Int("a.timeout", 2500, "timeout in milliseconds for production traffic")
	alternateTimeout      = flag.Int("b.timeout", 1000, "timeout in milliseconds for alternate site traffic")
	productionHostRewrite = flag.Bool("a.rewrite", false, "rewrite the host header when proxying production traffic")
	alternateHostRewrite  = flag.Bool("b.rewrite", false, "rewrite the host header when proxying alternate site traffic")
	percent               = flag.Float64("p", 100.0, "float64 percentage of traffic to send to testing")
	tlsPrivateKey         = flag.String("key.file", "", "path to the TLS private key file")
	tlsCertificate        = flag.String("cert.file", "", "path to the TLS certificate file")
)

// Sets the request URL.
//
// This turns a inbound request (a request without URL) into an outbound request.
func setRequestTarget(request *http.Request, target *string) {
	URL, err := url.Parse("http://" + *target + request.URL.String())
	if err != nil {
		fmt.Println(err)
	}
	request.URL = URL
}

// Sends a request and returns the response.
func handleRequest(request *http.Request, timeout time.Duration) (*http.Response) {
	transport := &http.Transport{
		// NOTE(girone): DialTLS is not needed here, because the teeproxy works
		// as an SSL terminator.
		Dial: (&net.Dialer{  // go1.8 deprecated: Use DialContext instead
			Timeout: timeout,
			KeepAlive: 10 * timeout,
		}).Dial,
		// Always close connections to the alternative and production servers.
		DisableKeepAlives: true,
		//IdleConnTimeout: timeout,  // go1.8
		TLSHandshakeTimeout: timeout,
		ResponseHeaderTimeout: timeout,
		ExpectContinueTimeout: timeout,
	}
	// Do not use http.Client here, because it's higher level and processes
	// redirects internally, which is not what we want.
	//client := &http.Client{
	//	Timeout: timeout,
	//	Transport: transport,
	//}
	//response, err := client.Do(request)
	response, err := transport.RoundTrip(request)
	if err != nil {
		fmt.Println("Request failed:", err)
	}
	return response
}

// handler contains the address of the main Target and the one for the Alternative target
type handler struct {
	Target      string
	Alternative string
	Randomizer  rand.Rand
}

// ServeHTTP duplicates the incoming request (req) and does the request to the
// Target and the Alternate target discading the Alternate response
func (h handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	var productionRequest, alternativeRequest *http.Request
	if *percent == 100.0 || h.Randomizer.Float64()*100 < *percent {
		alternativeRequest, productionRequest = DuplicateRequest(req)
		go func() {
			defer func() {
				if r := recover(); r != nil && *debug {
					fmt.Println("Recovered in ServeHTTP(alternate request) from:", r)
				}
			}()

			setRequestTarget(alternativeRequest, altTarget)

			if *alternateHostRewrite {
				alternativeRequest.Host = h.Alternative
			}

			timeout := time.Duration(*alternateTimeout) * time.Millisecond
			// This keeps responses from the alternative target away from the outside world.
			_ = handleRequest(alternativeRequest, timeout)
		}()
	} else {
		productionRequest = req
	}
	defer func() {
		if r := recover(); r != nil && *debug {
			fmt.Println("Recovered in ServeHTTP(production request) from:", r)
		}
	}()

	setRequestTarget(productionRequest, targetProduction)

	if *productionHostRewrite {
		productionRequest.Host = h.Target
	}

	timeout := time.Duration(*productionTimeout) * time.Millisecond
	resp := handleRequest(productionRequest, timeout)

	if resp != nil {
		defer resp.Body.Close()

		// Forward response headers.
		for k, v := range resp.Header {
			w.Header()[k] = v
		}
		w.WriteHeader(resp.StatusCode)

		// Forward response body.
		body, _ := ioutil.ReadAll(resp.Body)
		w.Write(body)
	}
}

func main() {
	flag.Parse()

	fmt.Printf("Starting teeproxy at %s sending to A: %s and B: %s \n",
			   *listen, *targetProduction, *altTarget)

	runtime.GOMAXPROCS(runtime.NumCPU())

	var err error

	var listener net.Listener

	if len(*tlsPrivateKey) > 0 {
		cer, err := tls.LoadX509KeyPair(*tlsCertificate, *tlsPrivateKey)
		if err != nil {
			fmt.Printf("Failed to load certficate: %s and private key: %s", *tlsCertificate, *tlsPrivateKey)
			return
		}

		config := &tls.Config{Certificates: []tls.Certificate{cer}}
		listener, err = tls.Listen("tcp", *listen, config)
		if err != nil {
			fmt.Printf("Failed to listen to %s: %s\n", *listen, err)
			return
		}
	} else {
		listener, err = net.Listen("tcp", *listen)
		if err != nil {
			fmt.Printf("Failed to listen to %s: %s\n", *listen, err)
			return
		}
	}

	h := handler{
		Target:      *targetProduction,
		Alternative: *altTarget,
		Randomizer:  *rand.New(rand.NewSource(time.Now().UnixNano())),
	}

	server := &http.Server{
		Handler: h,
	}
	server.Serve(listener)
}

type nopCloser struct {
	io.Reader
}

func (nopCloser) Close() error { return nil }

func DuplicateRequest(request *http.Request) (request1 *http.Request, request2 *http.Request) {
	b1 := new(bytes.Buffer)
	b2 := new(bytes.Buffer)
	w := io.MultiWriter(b1, b2)
	io.Copy(w, request.Body)
	defer request.Body.Close()
	request1 = &http.Request{
		Method:        request.Method,
		URL:           request.URL,
		Proto:         request.Proto,
		ProtoMajor:    request.ProtoMajor,
		ProtoMinor:    request.ProtoMinor,
		Header:        request.Header,
		Body:          nopCloser{b1},
		Host:          request.Host,
		ContentLength: request.ContentLength,
		Close:         true,
	}
	request2 = &http.Request{
		Method:        request.Method,
		URL:           request.URL,
		Proto:         request.Proto,
		ProtoMajor:    request.ProtoMajor,
		ProtoMinor:    request.ProtoMinor,
		Header:        request.Header,
		Body:          nopCloser{b2},
		Host:          request.Host,
		ContentLength: request.ContentLength,
		Close:         true,
	}
	return
}
<commit_msg>Use package log<commit_after>package main

import (
	"bytes"
	"crypto/tls"
	"flag"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"net"
	"net/http"
	"net/url"
	"runtime"
	"time"
)

// Console flags
var (
	listen                = flag.String("l", ":8888", "port to accept requests")
	targetProduction      = flag.String("a", "localhost:8080", "where production traffic goes. http://localhost:8080/production")
	altTarget             = flag.String("b", "localhost:8081", "where testing traffic goes. response are skipped. http://localhost:8081/test")
	debug                 = flag.Bool("debug", false, "more logging, showing ignored output")
	productionTimeout     = flag.Int("a.timeout", 2500, "timeout in milliseconds for production traffic")
	alternateTimeout      = flag.Int("b.timeout", 1000, "timeout in milliseconds for alternate site traffic")
	productionHostRewrite = flag.Bool("a.rewrite", false, "rewrite the host header when proxying production traffic")
	alternateHostRewrite  = flag.Bool("b.rewrite", false, "rewrite the host header when proxying alternate site traffic")
	percent               = flag.Float64("p", 100.0, "float64 percentage of traffic to send to testing")
	tlsPrivateKey         = flag.String("key.file", "", "path to the TLS private key file")
	tlsCertificate        = flag.String("cert.file", "", "path to the TLS certificate file")
)

// Sets the request URL.
//
// This turns a inbound request (a request without URL) into an outbound request.
func setRequestTarget(request *http.Request, target *string) {
	URL, err := url.Parse("http://" + *target + request.URL.String())
	if err != nil {
		log.Println(err)
	}
	request.URL = URL
}

// Sends a request and returns the response.
func handleRequest(request *http.Request, timeout time.Duration) (*http.Response) {
	transport := &http.Transport{
		// NOTE(girone): DialTLS is not needed here, because the teeproxy works
		// as an SSL terminator.
		Dial: (&net.Dialer{  // go1.8 deprecated: Use DialContext instead
			Timeout: timeout,
			KeepAlive: 10 * timeout,
		}).Dial,
		// Always close connections to the alternative and production servers.
		DisableKeepAlives: true,
		//IdleConnTimeout: timeout,  // go1.8
		TLSHandshakeTimeout: timeout,
		ResponseHeaderTimeout: timeout,
		ExpectContinueTimeout: timeout,
	}
	// Do not use http.Client here, because it's higher level and processes
	// redirects internally, which is not what we want.
	//client := &http.Client{
	//	Timeout: timeout,
	//	Transport: transport,
	//}
	//response, err := client.Do(request)
	response, err := transport.RoundTrip(request)
	if err != nil {
		log.Println("Request failed:", err)
	}
	return response
}

// handler contains the address of the main Target and the one for the Alternative target
type handler struct {
	Target      string
	Alternative string
	Randomizer  rand.Rand
}

// ServeHTTP duplicates the incoming request (req) and does the request to the
// Target and the Alternate target discading the Alternate response
func (h handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	var productionRequest, alternativeRequest *http.Request
	if *percent == 100.0 || h.Randomizer.Float64()*100 < *percent {
		alternativeRequest, productionRequest = DuplicateRequest(req)
		go func() {
			defer func() {
				if r := recover(); r != nil && *debug {
					log.Println("Recovered in ServeHTTP(alternate request) from:", r)
				}
			}()

			setRequestTarget(alternativeRequest, altTarget)

			if *alternateHostRewrite {
				alternativeRequest.Host = h.Alternative
			}

			timeout := time.Duration(*alternateTimeout) * time.Millisecond
			// This keeps responses from the alternative target away from the outside world.
			_ = handleRequest(alternativeRequest, timeout)
		}()
	} else {
		productionRequest = req
	}
	defer func() {
		if r := recover(); r != nil && *debug {
			log.Println("Recovered in ServeHTTP(production request) from:", r)
		}
	}()

	setRequestTarget(productionRequest, targetProduction)

	if *productionHostRewrite {
		productionRequest.Host = h.Target
	}

	timeout := time.Duration(*productionTimeout) * time.Millisecond
	resp := handleRequest(productionRequest, timeout)

	if resp != nil {
		defer resp.Body.Close()

		// Forward response headers.
		for k, v := range resp.Header {
			w.Header()[k] = v
		}
		w.WriteHeader(resp.StatusCode)

		// Forward response body.
		body, _ := ioutil.ReadAll(resp.Body)
		w.Write(body)
	}
}

func main() {
	flag.Parse()

	log.Printf("Starting teeproxy at %s sending to A: %s and B: %s",
	           *listen, *targetProduction, *altTarget)

	runtime.GOMAXPROCS(runtime.NumCPU())

	var err error

	var listener net.Listener

	if len(*tlsPrivateKey) > 0 {
		cer, err := tls.LoadX509KeyPair(*tlsCertificate, *tlsPrivateKey)
		if err != nil {
			log.Fatalf("Failed to load certficate: %s and private key: %s", *tlsCertificate, *tlsPrivateKey)
		}

		config := &tls.Config{Certificates: []tls.Certificate{cer}}
		listener, err = tls.Listen("tcp", *listen, config)
		if err != nil {
			log.Fatalf("Failed to listen to %s: %s", *listen, err)
		}
	} else {
		listener, err = net.Listen("tcp", *listen)
		if err != nil {
			log.Fatalf("Failed to listen to %s: %s", *listen, err)
		}
	}

	h := handler{
		Target:      *targetProduction,
		Alternative: *altTarget,
		Randomizer:  *rand.New(rand.NewSource(time.Now().UnixNano())),
	}

	server := &http.Server{
		Handler: h,
	}
	server.Serve(listener)
}

type nopCloser struct {
	io.Reader
}

func (nopCloser) Close() error { return nil }

func DuplicateRequest(request *http.Request) (request1 *http.Request, request2 *http.Request) {
	b1 := new(bytes.Buffer)
	b2 := new(bytes.Buffer)
	w := io.MultiWriter(b1, b2)
	io.Copy(w, request.Body)
	defer request.Body.Close()
	request1 = &http.Request{
		Method:        request.Method,
		URL:           request.URL,
		Proto:         request.Proto,
		ProtoMajor:    request.ProtoMajor,
		ProtoMinor:    request.ProtoMinor,
		Header:        request.Header,
		Body:          nopCloser{b1},
		Host:          request.Host,
		ContentLength: request.ContentLength,
		Close:         true,
	}
	request2 = &http.Request{
		Method:        request.Method,
		URL:           request.URL,
		Proto:         request.Proto,
		ProtoMajor:    request.ProtoMajor,
		ProtoMinor:    request.ProtoMinor,
		Header:        request.Header,
		Body:          nopCloser{b2},
		Host:          request.Host,
		ContentLength: request.ContentLength,
		Close:         true,
	}
	return
}
<|endoftext|>


<commit_before>package xweb

import (
	"fmt"
	"github.com/howeyc/fsnotify"
	"html/template"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"sync"
	"time"
)

func IsNil(a interface{}) bool {
	switch a.(type) {
	case nil:
		return true
	}
	return false
}

func Add(left interface{}, right interface{}) interface{} {
	var rleft, rright int64
	var fleft, fright float64
	var isInt bool = true
	switch left.(type) {
	case int:
		rleft = int64(left.(int))
	case int8:
		rleft = int64(left.(int8))
	case int16:
		rleft = int64(left.(int16))
	case int32:
		rleft = int64(left.(int32))
	case int64:
		rleft = left.(int64)
	case float32:
		fleft = float64(left.(float32))
		isInt = false
	case float64:
		fleft = left.(float64)
		isInt = false
	}

	switch right.(type) {
	case int:
		rright = int64(right.(int))
	case int8:
		rright = int64(right.(int8))
	case int16:
		rright = int64(right.(int16))
	case int32:
		rright = int64(right.(int32))
	case int64:
		rright = right.(int64)
	case float32:
		fright = float64(left.(float32))
		isInt = false
	case float64:
		fleft = left.(float64)
		isInt = false
	}

	var intSum int64 = rleft + rright

	if isInt {
		return intSum
	} else {
		return fleft + fright + float64(intSum)
	}
}

func Now() time.Time {
	return time.Now()
}

func FormatDate(t time.Time, format string) string {
	return t.Format(format)
}

func Eq(left interface{}, right interface{}) bool {
	leftIsNil := (left == nil)
	rightIsNil := (right == nil)
	if leftIsNil || rightIsNil {
		if leftIsNil && rightIsNil {
			return true
		}
		return false
	}
	return left == right
}

func Html(raw string) template.HTML {
	return template.HTML(raw)
}

var (
	defaultFuncs template.FuncMap = template.FuncMap{
		"Now":        Now,
		"Eq":         Eq,
		"FormatDate": FormatDate,
		"Html":       Html,
		"Add":        Add,
		"IsNil":      IsNil,
	}
)

type TemplateMgr struct {
	Caches   map[string][]byte
	mutex    *sync.Mutex
	RootDir  string
	Ignores  map[string]bool
	IsReload bool
}

func (self *TemplateMgr) Moniter(rootDir string) error {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return err
	}

	done := make(chan bool)
	go func() {
		for {
			select {
			case ev := <-watcher.Event:
				if ev == nil {
					break
				}
				if _, ok := self.Ignores[filepath.Base(ev.Name)]; ok {
					break
				}
				d, err := os.Stat(ev.Name)
				if err != nil {
					break
				}

				if ev.IsCreate() {
					if d.IsDir() {
						watcher.Watch(ev.Name)
					} else {
						tmpl := ev.Name[len(self.RootDir)+1:]
						content, err := ioutil.ReadFile(ev.Name)
						if err != nil {
							break
						}

						self.mutex.Lock()
						self.Caches[tmpl] = content
						self.mutex.Unlock()
					}
				} else if ev.IsDelete() {
					if d.IsDir() {
						watcher.RemoveWatch(ev.Name)
					} else {
						self.mutex.Lock()
						delete(self.Caches, ev.Name[len(self.RootDir)+1:])
						self.mutex.Unlock()
					}
				} else if ev.IsModify() {
					if d.IsDir() {
					} else {
						tmpl := ev.Name[len(self.RootDir)+1:]
						content, err := ioutil.ReadFile(ev.Name)
						if err != nil {
							break
						}

						self.mutex.Lock()
						self.Caches[tmpl] = content
						self.mutex.Unlock()
					}
				} else if ev.IsRename() {
					if d.IsDir() {
						watcher.RemoveWatch(ev.Name)
					} else {
						self.mutex.Lock()
						delete(self.Caches, ev.Name[len(self.RootDir)+1:])
						self.mutex.Unlock()
					}
				}
			case err := <-watcher.Error:
				fmt.Println("error:", err)
			}
		}
	}()

	err = filepath.Walk(self.RootDir, func(f string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return watcher.Watch(f)
		}
		return nil
	})

	if err != nil {
		fmt.Println(err)
		return err
	}

	<-done

	watcher.Close()
	return nil
}

func (self *TemplateMgr) CacheAll(rootDir string) error {
	self.mutex.Lock()
	defer self.mutex.Unlock()
	err := filepath.Walk(rootDir, func(f string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return nil
		}
		tmpl := f[len(rootDir)+1:]
		if _, ok := self.Ignores[filepath.Base(tmpl)]; !ok {
			content, err := ioutil.ReadFile(path.Join(self.RootDir, tmpl))
			if err != nil {
				return err
			}
			self.Caches[tmpl] = content
		}
		return nil
	})
	return err
}

func (self *TemplateMgr) Init(rootDir string, reload bool) error {
	self.RootDir = rootDir
	self.Caches = make(map[string][]byte)
	self.mutex = &sync.Mutex{}
	if dirExists(rootDir) {
		self.CacheAll(rootDir)

		if reload {
			go self.Moniter(rootDir)
		}
	}

	return nil
}

func (self *TemplateMgr) GetTemplate(tmpl string) ([]byte, error) {
	self.mutex.Lock()
	defer self.mutex.Unlock()
	if content, ok := self.Caches[tmpl]; ok {
		return content, nil
	}

	content, err := ioutil.ReadFile(path.Join(self.RootDir, tmpl))
	if err == nil {
		self.Caches[tmpl] = content
	}
	return content, err
}
<commit_msg>add Subtract method for template<commit_after>package xweb

import (
	"fmt"
	"github.com/howeyc/fsnotify"
	"html/template"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"sync"
	"time"
)

func IsNil(a interface{}) bool {
	switch a.(type) {
	case nil:
		return true
	}
	return false
}

func Add(left interface{}, right interface{}) interface{} {
	var rleft, rright int64
	var fleft, fright float64
	var isInt bool = true
	switch left.(type) {
	case int:
		rleft = int64(left.(int))
	case int8:
		rleft = int64(left.(int8))
	case int16:
		rleft = int64(left.(int16))
	case int32:
		rleft = int64(left.(int32))
	case int64:
		rleft = left.(int64)
	case float32:
		fleft = float64(left.(float32))
		isInt = false
	case float64:
		fleft = left.(float64)
		isInt = false
	}

	switch right.(type) {
	case int:
		rright = int64(right.(int))
	case int8:
		rright = int64(right.(int8))
	case int16:
		rright = int64(right.(int16))
	case int32:
		rright = int64(right.(int32))
	case int64:
		rright = right.(int64)
	case float32:
		fright = float64(left.(float32))
		isInt = false
	case float64:
		fleft = left.(float64)
		isInt = false
	}

	var intSum int64 = rleft + rright

	if isInt {
		return intSum
	} else {
		return fleft + fright + float64(intSum)
	}
}

func Subtract(left interface{}, right interface{}) interface{} {
	var rleft, rright int64
	var fleft, fright float64
	var isInt bool = true
	switch left.(type) {
	case int:
		rleft = int64(left.(int))
	case int8:
		rleft = int64(left.(int8))
	case int16:
		rleft = int64(left.(int16))
	case int32:
		rleft = int64(left.(int32))
	case int64:
		rleft = left.(int64)
	case float32:
		fleft = float64(left.(float32))
		isInt = false
	case float64:
		fleft = left.(float64)
		isInt = false
	}

	switch right.(type) {
	case int:
		rright = int64(right.(int))
	case int8:
		rright = int64(right.(int8))
	case int16:
		rright = int64(right.(int16))
	case int32:
		rright = int64(right.(int32))
	case int64:
		rright = right.(int64)
	case float32:
		fright = float64(left.(float32))
		isInt = false
	case float64:
		fleft = left.(float64)
		isInt = false
	}

	if isInt {
		return rleft - rright
	} else {
		return fleft + float64(rleft) - (fright + float64(rright))
	}
}

func Now() time.Time {
	return time.Now()
}

func FormatDate(t time.Time, format string) string {
	return t.Format(format)
}

func Eq(left interface{}, right interface{}) bool {
	leftIsNil := (left == nil)
	rightIsNil := (right == nil)
	if leftIsNil || rightIsNil {
		if leftIsNil && rightIsNil {
			return true
		}
		return false
	}
	return left == right
}

func Html(raw string) template.HTML {
	return template.HTML(raw)
}

var (
	defaultFuncs template.FuncMap = template.FuncMap{
		"Now":        Now,
		"Eq":         Eq,
		"FormatDate": FormatDate,
		"Html":       Html,
		"Add":        Add,
		"Subtract":   Subtract,
		"IsNil":      IsNil,
	}
)

type TemplateMgr struct {
	Caches   map[string][]byte
	mutex    *sync.Mutex
	RootDir  string
	Ignores  map[string]bool
	IsReload bool
}

func (self *TemplateMgr) Moniter(rootDir string) error {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return err
	}

	done := make(chan bool)
	go func() {
		for {
			select {
			case ev := <-watcher.Event:
				if ev == nil {
					break
				}
				if _, ok := self.Ignores[filepath.Base(ev.Name)]; ok {
					break
				}
				d, err := os.Stat(ev.Name)
				if err != nil {
					break
				}

				if ev.IsCreate() {
					if d.IsDir() {
						watcher.Watch(ev.Name)
					} else {
						tmpl := ev.Name[len(self.RootDir)+1:]
						content, err := ioutil.ReadFile(ev.Name)
						if err != nil {
							break
						}

						self.mutex.Lock()
						self.Caches[tmpl] = content
						self.mutex.Unlock()
					}
				} else if ev.IsDelete() {
					if d.IsDir() {
						watcher.RemoveWatch(ev.Name)
					} else {
						self.mutex.Lock()
						delete(self.Caches, ev.Name[len(self.RootDir)+1:])
						self.mutex.Unlock()
					}
				} else if ev.IsModify() {
					if d.IsDir() {
					} else {
						tmpl := ev.Name[len(self.RootDir)+1:]
						content, err := ioutil.ReadFile(ev.Name)
						if err != nil {
							break
						}

						self.mutex.Lock()
						self.Caches[tmpl] = content
						self.mutex.Unlock()
					}
				} else if ev.IsRename() {
					if d.IsDir() {
						watcher.RemoveWatch(ev.Name)
					} else {
						self.mutex.Lock()
						delete(self.Caches, ev.Name[len(self.RootDir)+1:])
						self.mutex.Unlock()
					}
				}
			case err := <-watcher.Error:
				fmt.Println("error:", err)
			}
		}
	}()

	err = filepath.Walk(self.RootDir, func(f string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return watcher.Watch(f)
		}
		return nil
	})

	if err != nil {
		fmt.Println(err)
		return err
	}

	<-done

	watcher.Close()
	return nil
}

func (self *TemplateMgr) CacheAll(rootDir string) error {
	self.mutex.Lock()
	defer self.mutex.Unlock()
	err := filepath.Walk(rootDir, func(f string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return nil
		}
		tmpl := f[len(rootDir)+1:]
		if _, ok := self.Ignores[filepath.Base(tmpl)]; !ok {
			content, err := ioutil.ReadFile(path.Join(self.RootDir, tmpl))
			if err != nil {
				return err
			}
			self.Caches[tmpl] = content
		}
		return nil
	})
	return err
}

func (self *TemplateMgr) Init(rootDir string, reload bool) error {
	self.RootDir = rootDir
	self.Caches = make(map[string][]byte)
	self.mutex = &sync.Mutex{}
	if dirExists(rootDir) {
		self.CacheAll(rootDir)

		if reload {
			go self.Moniter(rootDir)
		}
	}

	return nil
}

func (self *TemplateMgr) GetTemplate(tmpl string) ([]byte, error) {
	self.mutex.Lock()
	defer self.mutex.Unlock()
	if content, ok := self.Caches[tmpl]; ok {
		return content, nil
	}

	content, err := ioutil.ReadFile(path.Join(self.RootDir, tmpl))
	if err == nil {
		self.Caches[tmpl] = content
	}
	return content, err
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"
)

func writeTemplates(dir, gopkg, debsrc, debLib, debProg, debversion string,
	pkgType packageType, dependencies []string, vendorDirs []string,
	dep14, pristineTar bool) error {
	if err := os.Mkdir(filepath.Join(dir, "debian"), 0755); err != nil {
		return err
	}
	if err := os.Mkdir(filepath.Join(dir, "debian", "source"), 0755); err != nil {
		return err
	}

	if err := writeDebianChangelog(dir, debsrc, debversion); err != nil {
		return err
	}
	if err := writeDebianControl(dir, gopkg, debsrc, debLib, debProg, pkgType, dependencies); err != nil {
		return err
	}
	if err := writeDebianCopyright(dir, gopkg, vendorDirs); err != nil {
		return err
	}
	if err := writeDebianRules(dir, pkgType); err != nil {
		return err
	}
	if err := writeDebianSourceFormat(dir); err != nil {
		return err
	}
	if err := writeDebianGbpConf(dir, dep14, pristineTar); err != nil {
		return err
	}
	if err := writeDebianWatch(dir, gopkg, debsrc); err != nil {
		return err
	}
	if err := writeDebianPackageInstall(dir, debLib, debProg, pkgType); err != nil {
		return err
	}
	return nil
}

func writeDebianChangelog(dir, debsrc, debversion string) error {
	f, err := os.Create(filepath.Join(dir, "debian", "changelog"))
	if err != nil {
		return err
	}
	defer f.Close()

	fmt.Fprintf(f, "%s (%s) UNRELEASED; urgency=medium\n", debsrc, debversion)
	fmt.Fprintf(f, "\n")
	fmt.Fprintf(f, "  * Initial release (Closes: TODO)\n")
	fmt.Fprintf(f, "\n")
	fmt.Fprintf(f, " -- %s <%s>  %s\n",
		getDebianName(),
		getDebianEmail(),
		time.Now().Format("Mon, 02 Jan 2006 15:04:05 -0700"))

	return nil
}

func fprintfControlField(f *os.File, field string, valueArray []string) {
	switch wrapAndSort {
	case "a":
		// Current default, also what "cme fix dpkg" generates
		fmt.Fprintf(f, "%s: %s\n", field, strings.Join(valueArray, ",\n"+strings.Repeat(" ", len(field)+2)))
	case "at":
		// -t, --trailing-comma, preferred by Martina Ferrari
		// and currently used in quite a few packages
		fmt.Fprintf(f, "%s: %s,\n", field, strings.Join(valueArray, ",\n"+strings.Repeat(" ", len(field)+2)))
	case "ast":
		// -s, --short-indent too, proposed by Guillem Jover
		fmt.Fprintf(f, "%s:\n %s,\n", field, strings.Join(valueArray, ",\n "))
	default:
		log.Fatalf("%q is not a valid value for -wrap-and-sort, aborting.", wrapAndSort)
	}
}

func addDescription(f *os.File, gopkg, comment string) {
	description, err := getDescriptionForGopkg(gopkg)
	if err != nil {
		log.Printf("Could not determine description for %q: %v\n", gopkg, err)
		description = "TODO: short description"
	}
	fmt.Fprintf(f, "Description: %s %s\n", description, comment)
	longdescription, err := getLongDescriptionForGopkg(gopkg)
	if err != nil {
		log.Printf("Could not determine long description for %q: %v\n", gopkg, err)
		longdescription = "TODO: long description"
	}
	fmt.Fprintf(f, " %s\n", longdescription)
}

func addLibraryPackage(f *os.File, gopkg, debLib string, dependencies []string) {
	fmt.Fprintf(f, "\n")
	fmt.Fprintf(f, "Package: %s\n", debLib)
	deps := []string{"${misc:Depends}"}
	fmt.Fprintf(f, "Architecture: all\n")
	deps = append(deps, dependencies...)
	sort.Strings(deps)
	fprintfControlField(f, "Depends", deps)
	addDescription(f, gopkg, "(library)")
}

func addProgramPackage(f *os.File, gopkg, debProg string, dependencies []string) {
	fmt.Fprintf(f, "\n")
	fmt.Fprintf(f, "Package: %s\n", debProg)
	deps := []string{"${misc:Depends}"}
	fmt.Fprintf(f, "Architecture: any\n")
	deps = append(deps, "${shlibs:Depends}")
	sort.Strings(deps)
	fprintfControlField(f, "Depends", deps)
	fmt.Fprintf(f, "Built-Using: ${misc:Built-Using}\n")
	addDescription(f, gopkg, "(program)")
}

func writeDebianControl(dir, gopkg, debsrc, debLib, debProg string, pkgType packageType, dependencies []string) error {
	f, err := os.Create(filepath.Join(dir, "debian", "control"))
	if err != nil {
		return err
	}
	defer f.Close()

	// Source package:

	fmt.Fprintf(f, "Source: %s\n", debsrc)
	fmt.Fprintf(f, "Maintainer: Debian Go Packaging Team <team+pkg-go@tracker.debian.org>\n")
	fprintfControlField(f, "Uploaders", []string{getDebianName() + " <" + getDebianEmail() + ">"})
	// TODO: change this once we have a “golang” section.
	fmt.Fprintf(f, "Section: devel\n")
	fmt.Fprintf(f, "Testsuite: autopkgtest-pkg-go\n")
	fmt.Fprintf(f, "Priority: optional\n")

	builddeps := []string{"debhelper-compat (= 12)", "dh-golang"}
	builddepsByType := append([]string{"golang-any"}, dependencies...)
	sort.Strings(builddepsByType)
	switch pkgType {
	case typeLibrary, typeProgram:
		fprintfControlField(f, "Build-Depends", builddeps)
		builddepsDepType := "Indep"
		if pkgType == typeProgram {
			builddepsDepType = "Arch"
		}
		fprintfControlField(f, "Build-Depends-"+builddepsDepType, builddepsByType)
	case typeLibraryProgram, typeProgramLibrary:
		builddeps = append(builddeps, builddepsByType...)
		fprintfControlField(f, "Build-Depends", builddeps)
	default:
		log.Fatalf("Invalid pkgType %d in writeDebianControl(), aborting", pkgType)
	}

	fmt.Fprintf(f, "Standards-Version: 4.4.1\n")
	fmt.Fprintf(f, "Vcs-Browser: https://salsa.debian.org/go-team/packages/%s\n", debsrc)
	fmt.Fprintf(f, "Vcs-Git: https://salsa.debian.org/go-team/packages/%s.git\n", debsrc)
	fmt.Fprintf(f, "Homepage: %s\n", getHomepageForGopkg(gopkg))
	fmt.Fprintf(f, "Rules-Requires-Root: no\n")
	fmt.Fprintf(f, "XS-Go-Import-Path: %s\n", gopkg)

	// Binary package(s):

	switch pkgType {
	case typeLibrary:
		addLibraryPackage(f, gopkg, debLib, dependencies)
	case typeProgram:
		addProgramPackage(f, gopkg, debProg, dependencies)
	case typeLibraryProgram:
		addLibraryPackage(f, gopkg, debLib, dependencies)
		addProgramPackage(f, gopkg, debProg, dependencies)
	case typeProgramLibrary:
		addProgramPackage(f, gopkg, debProg, dependencies)
		addLibraryPackage(f, gopkg, debLib, dependencies)
	default:
		log.Fatalf("Invalid pkgType %d in writeDebianControl(), aborting", pkgType)
	}

	return nil
}

func writeDebianCopyright(dir, gopkg string, vendorDirs []string) error {
	license, fulltext, err := getLicenseForGopkg(gopkg)
	if err != nil {
		log.Printf("Could not determine license for %q: %v\n", gopkg, err)
		license = "TODO"
		fulltext = "TODO"
	}

	f, err := os.Create(filepath.Join(dir, "debian", "copyright"))
	if err != nil {
		return err
	}
	defer f.Close()

	_, copyright, err := getAuthorAndCopyrightForGopkg(gopkg)
	if err != nil {
		log.Printf("Could not determine copyright for %q: %v\n", gopkg, err)
		copyright = "TODO"
	}

	var indent = "  "
	var linebreak = ""
	if wrapAndSort == "ast" {
		indent = " "
		linebreak = "\n"
	}

	fmt.Fprintf(f, "Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\n")
	fmt.Fprintf(f, "Source: %s\n", getHomepageForGopkg(gopkg))
	fmt.Fprintf(f, "Upstream-Name: %s\n", filepath.Base(gopkg))
	fmt.Fprintf(f, "Files-Excluded:\n")
	for _, dir := range vendorDirs {
		fmt.Fprintf(f, indent+"%s\n", dir)
	}
	fmt.Fprintf(f, indent+"Godeps/_workspace\n")
	fmt.Fprintf(f, "\n")
	fmt.Fprintf(f, "Files:"+linebreak+" *\n")
	fmt.Fprintf(f, "Copyright:"+linebreak+" %s\n", copyright)
	fmt.Fprintf(f, "License: %s\n", license)
	fmt.Fprintf(f, "\n")
	fmt.Fprintf(f, "Files:"+linebreak+" debian/*\n")
	fmt.Fprintf(f, "Copyright:"+linebreak+" %s %s <%s>\n", time.Now().Format("2006"), getDebianName(), getDebianEmail())
	fmt.Fprintf(f, "License: %s\n", license)
	fmt.Fprintf(f, "Comment: Debian packaging is licensed under the same terms as upstream\n")
	fmt.Fprintf(f, "\n")
	fmt.Fprintf(f, "License: %s\n", license)
	fmt.Fprintf(f, fulltext)

	return nil
}

func writeDebianRules(dir string, pkgType packageType) error {
	f, err := os.Create(filepath.Join(dir, "debian", "rules"))
	if err != nil {
		return err
	}
	defer f.Close()

	fmt.Fprintf(f, "#!/usr/bin/make -f\n")
	fmt.Fprintf(f, "\n")
	fmt.Fprintf(f, "%%:\n")
	fmt.Fprintf(f, "\tdh $@ --builddirectory=_build --buildsystem=golang --with=golang\n")
	if pkgType == typeProgram {
		fmt.Fprintf(f, "\n")
		fmt.Fprintf(f, "override_dh_auto_install:\n")
		fmt.Fprintf(f, "\tdh_auto_install -- --no-source\n")
	}

	if err := os.Chmod(filepath.Join(dir, "debian", "rules"), 0755); err != nil {
		return err
	}

	return nil
}

func writeDebianSourceFormat(dir string) error {
	f, err := os.Create(filepath.Join(dir, "debian", "source", "format"))
	if err != nil {
		return err
	}
	defer f.Close()

	fmt.Fprintf(f, "3.0 (quilt)\n")
	return nil
}

func writeDebianGbpConf(dir string, dep14, pristineTar bool) error {
	f, err := os.Create(filepath.Join(dir, "debian", "gbp.conf"))
	if err != nil {
		return err
	}
	defer f.Close()

	fmt.Fprintf(f, "[DEFAULT]\n")
	if dep14 {
		fmt.Fprintf(f, "debian-branch = debian/sid\n")
	}
	if pristineTar {
		fmt.Fprintf(f, "pristine-tar = True\n")
	}
	return nil
}

func writeDebianWatch(dir, gopkg, debsrc string) error {
	if strings.HasPrefix(gopkg, "github.com/") {
		f, err := os.Create(filepath.Join(dir, "debian", "watch"))
		if err != nil {
			return err
		}
		defer f.Close()

		fmt.Fprintf(f, "version=4\n")
		fmt.Fprintf(f, `opts=filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/%s-\$1\.tar\.gz/,\`+"\n", debsrc)
		fmt.Fprintf(f, `uversionmangle=s/(\d)[_\.\-\+]?(RC|rc|pre|dev|beta|alpha)[.]?(\d*)$/\$1~\$2\$3/ \`+"\n")
		fmt.Fprintf(f, `  https://%s/tags .*/v?(\d\S*)\.tar\.gz`+"\n", gopkg)
	}
	return nil
}

func writeDebianPackageInstall(dir, debLib, debProg string, pkgType packageType) error {
	if pkgType == typeLibraryProgram || pkgType == typeProgramLibrary {
		f, err := os.Create(filepath.Join(dir, "debian", debProg+".install"))
		if err != nil {
			return err
		}
		defer f.Close()
		fmt.Fprintf(f, "usr/bin\n")

		f, err = os.Create(filepath.Join(dir, "debian", debLib+".install"))
		if err != nil {
			return err
		}
		defer f.Close()
		fmt.Fprintf(f, "usr/share\n")
	}
	return nil
}
<commit_msg>Add "dist = DEP14" to debian/gbp.conf template<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"
)

func writeTemplates(dir, gopkg, debsrc, debLib, debProg, debversion string,
	pkgType packageType, dependencies []string, vendorDirs []string,
	dep14, pristineTar bool) error {
	if err := os.Mkdir(filepath.Join(dir, "debian"), 0755); err != nil {
		return err
	}
	if err := os.Mkdir(filepath.Join(dir, "debian", "source"), 0755); err != nil {
		return err
	}

	if err := writeDebianChangelog(dir, debsrc, debversion); err != nil {
		return err
	}
	if err := writeDebianControl(dir, gopkg, debsrc, debLib, debProg, pkgType, dependencies); err != nil {
		return err
	}
	if err := writeDebianCopyright(dir, gopkg, vendorDirs); err != nil {
		return err
	}
	if err := writeDebianRules(dir, pkgType); err != nil {
		return err
	}
	if err := writeDebianSourceFormat(dir); err != nil {
		return err
	}
	if err := writeDebianGbpConf(dir, dep14, pristineTar); err != nil {
		return err
	}
	if err := writeDebianWatch(dir, gopkg, debsrc); err != nil {
		return err
	}
	if err := writeDebianPackageInstall(dir, debLib, debProg, pkgType); err != nil {
		return err
	}
	return nil
}

func writeDebianChangelog(dir, debsrc, debversion string) error {
	f, err := os.Create(filepath.Join(dir, "debian", "changelog"))
	if err != nil {
		return err
	}
	defer f.Close()

	fmt.Fprintf(f, "%s (%s) UNRELEASED; urgency=medium\n", debsrc, debversion)
	fmt.Fprintf(f, "\n")
	fmt.Fprintf(f, "  * Initial release (Closes: TODO)\n")
	fmt.Fprintf(f, "\n")
	fmt.Fprintf(f, " -- %s <%s>  %s\n",
		getDebianName(),
		getDebianEmail(),
		time.Now().Format("Mon, 02 Jan 2006 15:04:05 -0700"))

	return nil
}

func fprintfControlField(f *os.File, field string, valueArray []string) {
	switch wrapAndSort {
	case "a":
		// Current default, also what "cme fix dpkg" generates
		fmt.Fprintf(f, "%s: %s\n", field, strings.Join(valueArray, ",\n"+strings.Repeat(" ", len(field)+2)))
	case "at":
		// -t, --trailing-comma, preferred by Martina Ferrari
		// and currently used in quite a few packages
		fmt.Fprintf(f, "%s: %s,\n", field, strings.Join(valueArray, ",\n"+strings.Repeat(" ", len(field)+2)))
	case "ast":
		// -s, --short-indent too, proposed by Guillem Jover
		fmt.Fprintf(f, "%s:\n %s,\n", field, strings.Join(valueArray, ",\n "))
	default:
		log.Fatalf("%q is not a valid value for -wrap-and-sort, aborting.", wrapAndSort)
	}
}

func addDescription(f *os.File, gopkg, comment string) {
	description, err := getDescriptionForGopkg(gopkg)
	if err != nil {
		log.Printf("Could not determine description for %q: %v\n", gopkg, err)
		description = "TODO: short description"
	}
	fmt.Fprintf(f, "Description: %s %s\n", description, comment)
	longdescription, err := getLongDescriptionForGopkg(gopkg)
	if err != nil {
		log.Printf("Could not determine long description for %q: %v\n", gopkg, err)
		longdescription = "TODO: long description"
	}
	fmt.Fprintf(f, " %s\n", longdescription)
}

func addLibraryPackage(f *os.File, gopkg, debLib string, dependencies []string) {
	fmt.Fprintf(f, "\n")
	fmt.Fprintf(f, "Package: %s\n", debLib)
	deps := []string{"${misc:Depends}"}
	fmt.Fprintf(f, "Architecture: all\n")
	deps = append(deps, dependencies...)
	sort.Strings(deps)
	fprintfControlField(f, "Depends", deps)
	addDescription(f, gopkg, "(library)")
}

func addProgramPackage(f *os.File, gopkg, debProg string, dependencies []string) {
	fmt.Fprintf(f, "\n")
	fmt.Fprintf(f, "Package: %s\n", debProg)
	deps := []string{"${misc:Depends}"}
	fmt.Fprintf(f, "Architecture: any\n")
	deps = append(deps, "${shlibs:Depends}")
	sort.Strings(deps)
	fprintfControlField(f, "Depends", deps)
	fmt.Fprintf(f, "Built-Using: ${misc:Built-Using}\n")
	addDescription(f, gopkg, "(program)")
}

func writeDebianControl(dir, gopkg, debsrc, debLib, debProg string, pkgType packageType, dependencies []string) error {
	f, err := os.Create(filepath.Join(dir, "debian", "control"))
	if err != nil {
		return err
	}
	defer f.Close()

	// Source package:

	fmt.Fprintf(f, "Source: %s\n", debsrc)
	fmt.Fprintf(f, "Maintainer: Debian Go Packaging Team <team+pkg-go@tracker.debian.org>\n")
	fprintfControlField(f, "Uploaders", []string{getDebianName() + " <" + getDebianEmail() + ">"})
	// TODO: change this once we have a “golang” section.
	fmt.Fprintf(f, "Section: devel\n")
	fmt.Fprintf(f, "Testsuite: autopkgtest-pkg-go\n")
	fmt.Fprintf(f, "Priority: optional\n")

	builddeps := []string{"debhelper-compat (= 12)", "dh-golang"}
	builddepsByType := append([]string{"golang-any"}, dependencies...)
	sort.Strings(builddepsByType)
	switch pkgType {
	case typeLibrary, typeProgram:
		fprintfControlField(f, "Build-Depends", builddeps)
		builddepsDepType := "Indep"
		if pkgType == typeProgram {
			builddepsDepType = "Arch"
		}
		fprintfControlField(f, "Build-Depends-"+builddepsDepType, builddepsByType)
	case typeLibraryProgram, typeProgramLibrary:
		builddeps = append(builddeps, builddepsByType...)
		fprintfControlField(f, "Build-Depends", builddeps)
	default:
		log.Fatalf("Invalid pkgType %d in writeDebianControl(), aborting", pkgType)
	}

	fmt.Fprintf(f, "Standards-Version: 4.4.1\n")
	fmt.Fprintf(f, "Vcs-Browser: https://salsa.debian.org/go-team/packages/%s\n", debsrc)
	fmt.Fprintf(f, "Vcs-Git: https://salsa.debian.org/go-team/packages/%s.git\n", debsrc)
	fmt.Fprintf(f, "Homepage: %s\n", getHomepageForGopkg(gopkg))
	fmt.Fprintf(f, "Rules-Requires-Root: no\n")
	fmt.Fprintf(f, "XS-Go-Import-Path: %s\n", gopkg)

	// Binary package(s):

	switch pkgType {
	case typeLibrary:
		addLibraryPackage(f, gopkg, debLib, dependencies)
	case typeProgram:
		addProgramPackage(f, gopkg, debProg, dependencies)
	case typeLibraryProgram:
		addLibraryPackage(f, gopkg, debLib, dependencies)
		addProgramPackage(f, gopkg, debProg, dependencies)
	case typeProgramLibrary:
		addProgramPackage(f, gopkg, debProg, dependencies)
		addLibraryPackage(f, gopkg, debLib, dependencies)
	default:
		log.Fatalf("Invalid pkgType %d in writeDebianControl(), aborting", pkgType)
	}

	return nil
}

func writeDebianCopyright(dir, gopkg string, vendorDirs []string) error {
	license, fulltext, err := getLicenseForGopkg(gopkg)
	if err != nil {
		log.Printf("Could not determine license for %q: %v\n", gopkg, err)
		license = "TODO"
		fulltext = "TODO"
	}

	f, err := os.Create(filepath.Join(dir, "debian", "copyright"))
	if err != nil {
		return err
	}
	defer f.Close()

	_, copyright, err := getAuthorAndCopyrightForGopkg(gopkg)
	if err != nil {
		log.Printf("Could not determine copyright for %q: %v\n", gopkg, err)
		copyright = "TODO"
	}

	var indent = "  "
	var linebreak = ""
	if wrapAndSort == "ast" {
		indent = " "
		linebreak = "\n"
	}

	fmt.Fprintf(f, "Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\n")
	fmt.Fprintf(f, "Source: %s\n", getHomepageForGopkg(gopkg))
	fmt.Fprintf(f, "Upstream-Name: %s\n", filepath.Base(gopkg))
	fmt.Fprintf(f, "Files-Excluded:\n")
	for _, dir := range vendorDirs {
		fmt.Fprintf(f, indent+"%s\n", dir)
	}
	fmt.Fprintf(f, indent+"Godeps/_workspace\n")
	fmt.Fprintf(f, "\n")
	fmt.Fprintf(f, "Files:"+linebreak+" *\n")
	fmt.Fprintf(f, "Copyright:"+linebreak+" %s\n", copyright)
	fmt.Fprintf(f, "License: %s\n", license)
	fmt.Fprintf(f, "\n")
	fmt.Fprintf(f, "Files:"+linebreak+" debian/*\n")
	fmt.Fprintf(f, "Copyright:"+linebreak+" %s %s <%s>\n", time.Now().Format("2006"), getDebianName(), getDebianEmail())
	fmt.Fprintf(f, "License: %s\n", license)
	fmt.Fprintf(f, "Comment: Debian packaging is licensed under the same terms as upstream\n")
	fmt.Fprintf(f, "\n")
	fmt.Fprintf(f, "License: %s\n", license)
	fmt.Fprintf(f, fulltext)

	return nil
}

func writeDebianRules(dir string, pkgType packageType) error {
	f, err := os.Create(filepath.Join(dir, "debian", "rules"))
	if err != nil {
		return err
	}
	defer f.Close()

	fmt.Fprintf(f, "#!/usr/bin/make -f\n")
	fmt.Fprintf(f, "\n")
	fmt.Fprintf(f, "%%:\n")
	fmt.Fprintf(f, "\tdh $@ --builddirectory=_build --buildsystem=golang --with=golang\n")
	if pkgType == typeProgram {
		fmt.Fprintf(f, "\n")
		fmt.Fprintf(f, "override_dh_auto_install:\n")
		fmt.Fprintf(f, "\tdh_auto_install -- --no-source\n")
	}

	if err := os.Chmod(filepath.Join(dir, "debian", "rules"), 0755); err != nil {
		return err
	}

	return nil
}

func writeDebianSourceFormat(dir string) error {
	f, err := os.Create(filepath.Join(dir, "debian", "source", "format"))
	if err != nil {
		return err
	}
	defer f.Close()

	fmt.Fprintf(f, "3.0 (quilt)\n")
	return nil
}

func writeDebianGbpConf(dir string, dep14, pristineTar bool) error {
	f, err := os.Create(filepath.Join(dir, "debian", "gbp.conf"))
	if err != nil {
		return err
	}
	defer f.Close()

	fmt.Fprintf(f, "[DEFAULT]\n")
	if dep14 {
		fmt.Fprintf(f, "debian-branch = debian/sid\n")
		fmt.Fprintf(f, "dist = DEP14\n")
	}
	if pristineTar {
		fmt.Fprintf(f, "pristine-tar = True\n")
	}
	return nil
}

func writeDebianWatch(dir, gopkg, debsrc string) error {
	if strings.HasPrefix(gopkg, "github.com/") {
		f, err := os.Create(filepath.Join(dir, "debian", "watch"))
		if err != nil {
			return err
		}
		defer f.Close()

		fmt.Fprintf(f, "version=4\n")
		fmt.Fprintf(f, `opts=filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/%s-\$1\.tar\.gz/,\`+"\n", debsrc)
		fmt.Fprintf(f, `uversionmangle=s/(\d)[_\.\-\+]?(RC|rc|pre|dev|beta|alpha)[.]?(\d*)$/\$1~\$2\$3/ \`+"\n")
		fmt.Fprintf(f, `  https://%s/tags .*/v?(\d\S*)\.tar\.gz`+"\n", gopkg)
	}
	return nil
}

func writeDebianPackageInstall(dir, debLib, debProg string, pkgType packageType) error {
	if pkgType == typeLibraryProgram || pkgType == typeProgramLibrary {
		f, err := os.Create(filepath.Join(dir, "debian", debProg+".install"))
		if err != nil {
			return err
		}
		defer f.Close()
		fmt.Fprintf(f, "usr/bin\n")

		f, err = os.Create(filepath.Join(dir, "debian", debLib+".install"))
		if err != nil {
			return err
		}
		defer f.Close()
		fmt.Fprintf(f, "usr/share\n")
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"io/ioutil"
	"strings"
	"github.com/goamz/goamz/aws"
	"github.com/goamz/goamz/s3"
	"github.com/jessevdk/go-flags"
)

type Options struct {
    AccessKey string `short:"a" long:"access-key" description:"AccessKey"`
    SecretKey string `short:"s" long:"secret-key" description:"SecretKey"`
    Bucket    string `short:"b" long:"bucket" description:"s3 bucket"`
    Prefix    string `long:"prefix" description:"prefix of s3 location"`
    Output    string `short:"o" long:"output" description:"output file name"`
}

var opts = Options{}

func main() {
	args, err := flags.ParseArgs(&opts, os.Args)
	if err != nil {
    	panic(err)
    	os.Exit(1)
	}

	if len(args) < 2 {
		help()
		return
	}

	if !setup_opts(&opts) {
		return
	}

	subcmd := args[1]
	if subcmd == "help" {
		help()
	} else if subcmd == "config" {
		config()
	} else if subcmd == "get" {
		CmdGet(opts, args[2])
	} else if subcmd == "put" {
		CmdPut(opts, args[2], args[3])
	} else if subcmd == "del" {
		CmdDel(opts, args[2])
	} else if subcmd == "mv" {
		CmdMove(opts, args[2], args[3])
	} else {  //default
		help()
	}

	return
}

func setup_opts(opts *Options) bool {
	if opts.AccessKey == "" {
		opts.AccessKey = FileConfig("access_key")
	}
	if opts.AccessKey == "" {
		opts.AccessKey = os.Getenv("S3CL_ACCESS_KEY")
	}
	if opts.AccessKey == "" {
		fmt.Println("access key not found")
		return false
	}

	if opts.SecretKey == "" {
		opts.SecretKey = FileConfig("secret_key")
	}
	if opts.SecretKey == "" {
		opts.SecretKey = os.Getenv("S3CL_SECRET_KEY")
	}
	if opts.SecretKey == "" {
		fmt.Println("secret key not found")
		return false
	}

	if opts.Bucket == "" {
		opts.Bucket = FileConfig("bucket")
	}
	if opts.Bucket == "" {
		opts.Bucket = os.Getenv("S3CL_BUCKET_DEFAULT")
	}
	if opts.Bucket == "" {
		fmt.Println("bucket not found")
		return false
	}

	return true
}

func FileConfig(key string)(val string) {
	return ""
}

func print_usage() {
	usage := `
Usage:
    s3cl get key
    s3cl put key file_path
    s3cl del key
    s3cl mv  src_key dest_key

Options:
    --access-key, -a    Access Key
    --secret-key, -s    Secret Key
    --bucket    , -b    Bucket Name
    --prefix            Prefix of s3 location
    --output    , -o    Output file name
    `
	fmt.Println(usage)
}

func help() {
	print_usage()
}

func config() {

}

func CmdGet(opts Options, key string) {
	d, err := Get(opts, key)
	if err != nil {
		panic(err)
	}
	if opts.Output != "" {
		err := ioutil.WriteFile(opts.Output, d, 0666)
		if err != nil {
	    	panic(err)
		}
	} else {
		os.Stdout.Write(d)
		os.Stdout.Sync()
	}
}

func Get(opts Options, key string) (data []byte, err error){
	auth := aws.Auth{AccessKey: opts.AccessKey, SecretKey: opts.SecretKey}
	s3Instance := s3.New(auth, aws.Region{Name: "*", S3Endpoint: "http://s3.amazonaws.com"})
	bkt := s3Instance.Bucket(opts.Bucket)

	return bkt.Get(key)
}

func CmdPut(opts Options, key, file_path string) {
	contType := "text/plain"
	if strings.HasSuffix(file_path, ".xml") {
		contType = "text/xml"
	} else if strings.HasSuffix(file_path,".flv") {
		contType = "video/x-flv"
	}

	d, err := ioutil.ReadFile(file_path)
	if err != nil {
		panic(err)
		return
	}

	Put(opts, key, d, contType)
	if err != nil {
		panic(err)
		return
	}
}

func Put(opts Options, key string, data []byte, contType string) error {
	auth := aws.Auth{AccessKey: opts.AccessKey, SecretKey: opts.SecretKey}
	s3Instance := s3.New(auth, aws.Region{Name: "*", S3Endpoint: "http://s3.amazonaws.com"})
	bkt := s3Instance.Bucket(opts.Bucket)

	return bkt.Put(key, data, contType, s3.BucketOwnerFull, s3.Options{})
}

func CmdMove(opts Options, src_key, dest_key string) {
	d, err := Get(opts, src_key)
	if err != nil {
		panic(err)
	}

	Put(opts, dest_key, d, "")
	if err != nil {
		panic(err)
		return
	}
	Del(opts, src_key)
}

func Del(opts Options, key string) error{
	auth := aws.Auth{AccessKey: opts.AccessKey, SecretKey: opts.SecretKey}
	s3Instance := s3.New(auth, aws.Region{Name: "*", S3Endpoint: "http://s3.amazonaws.com"})
	bkt := s3Instance.Bucket(opts.Bucket)

	return bkt.Del(key)
}

func CmdDel(opts Options, key string) {
	err := Del(opts, key)
	if err != nil {
		panic(err)
	}
}<commit_msg>read the config options from /Users/jerry/.s3cl<commit_after>package main

import (
	"fmt"
	"os"
	"os/user"
	"io/ioutil"
	"path"
	"strings"
	"github.com/goamz/goamz/aws"
	"github.com/goamz/goamz/s3"
	"github.com/jessevdk/go-flags"
)

type Options struct {
    AccessKey string `short:"a" long:"access-key" description:"AccessKey"`
    SecretKey string `short:"s" long:"secret-key" description:"SecretKey"`
    Bucket    string `short:"b" long:"bucket" description:"s3 bucket"`
    Prefix    string `long:"prefix" description:"prefix of s3 location"`
    Output    string `short:"o" long:"output" description:"output file name"`
}

var opts = Options{}

func main() {
	args, err := flags.ParseArgs(&opts, os.Args)
	if err != nil {
    	panic(err)
    	os.Exit(1)
	}

	if len(args) < 2 {
		help()
		return
	}

	if !setup_opts(&opts) {
		return
	}

	//fmt.Println(opts)

	subcmd := args[1]
	if subcmd == "help" {
		help()
	} else if subcmd == "config" {
		config()
	} else if subcmd == "get" {
		CmdGet(opts, args[2])
	} else if subcmd == "put" {
		CmdPut(opts, args[2], args[3])
	} else if subcmd == "del" {
		CmdDel(opts, args[2])
	} else if subcmd == "mv" {
		CmdMove(opts, args[2], args[3])
	} else {  //default
		help()
	}

	return
}

func setup_opts(opts *Options) bool {
	if opts.AccessKey == "" {
		opts.AccessKey = FileConfig("access_key")
	}
	if opts.AccessKey == "" {
		opts.AccessKey = os.Getenv("S3CL_ACCESS_KEY")
	}
	if opts.AccessKey == "" {
		fmt.Println("access key not found")
		return false
	}

	if opts.SecretKey == "" {
		opts.SecretKey = FileConfig("secret_key")
	}
	if opts.SecretKey == "" {
		opts.SecretKey = os.Getenv("S3CL_SECRET_KEY")
	}
	if opts.SecretKey == "" {
		fmt.Println("secret key not found")
		return false
	}

	if opts.Bucket == "" {
		opts.Bucket = FileConfig("bucket")
	}
	if opts.Bucket == "" {
		opts.Bucket = os.Getenv("S3CL_BUCKET_DEFAULT")
	}
	if opts.Bucket == "" {
		fmt.Println("bucket not found")
		return false
	}

	return true
}

func FileConfig(key string)(val string) {
	usr, err := user.Current()
	if err != nil {
		panic(err)
	}

	config_file := path.Join(usr.HomeDir, ".s3cl")
	content, err := ioutil.ReadFile(config_file)
	if os.IsNotExist(err) {
		return ""
	}
	if err != nil {
	    panic(err)
	}

	lines := strings.Split(string(content), "\n")
	for _, line := range lines {
		vals := strings.Split(line, "=")
		if len(vals) != 2 {
			continue
		}
		if strings.TrimSpace(vals[0]) == key {
			return vals[1]
		}
	}

	return ""
}

func print_usage() {
	usage := `
Usage:
    s3cl get key
    s3cl put key file_path
    s3cl del key
    s3cl mv  src_key dest_key

Options:
    --access-key, -a    Access Key
    --secret-key, -s    Secret Key
    --bucket    , -b    Bucket Name
    --prefix            Prefix of s3 location
    --output    , -o    Output file name
    `
	fmt.Println(usage)
}

func help() {
	print_usage()
}

func config() {

}

func CmdGet(opts Options, key string) {
	d, err := Get(opts, key)
	if err != nil {
		panic(err)
	}
	if opts.Output != "" {
		err := ioutil.WriteFile(opts.Output, d, 0666)
		if err != nil {
	    	panic(err)
		}
	} else {
		os.Stdout.Write(d)
		os.Stdout.Sync()
	}
}

func Get(opts Options, key string) (data []byte, err error){
	auth := aws.Auth{AccessKey: opts.AccessKey, SecretKey: opts.SecretKey}
	s3Instance := s3.New(auth, aws.Region{Name: "*", S3Endpoint: "http://s3.amazonaws.com"})
	bkt := s3Instance.Bucket(opts.Bucket)

	return bkt.Get(key)
}

func CmdPut(opts Options, key, file_path string) {
	contType := "text/plain"
	if strings.HasSuffix(file_path, ".xml") {
		contType = "text/xml"
	} else if strings.HasSuffix(file_path,".flv") {
		contType = "video/x-flv"
	}

	d, err := ioutil.ReadFile(file_path)
	if err != nil {
		panic(err)
		return
	}

	Put(opts, key, d, contType)
	if err != nil {
		panic(err)
		return
	}
}

func Put(opts Options, key string, data []byte, contType string) error {
	auth := aws.Auth{AccessKey: opts.AccessKey, SecretKey: opts.SecretKey}
	s3Instance := s3.New(auth, aws.Region{Name: "*", S3Endpoint: "http://s3.amazonaws.com"})
	bkt := s3Instance.Bucket(opts.Bucket)

	return bkt.Put(key, data, contType, s3.BucketOwnerFull, s3.Options{})
}

func CmdMove(opts Options, src_key, dest_key string) {
	d, err := Get(opts, src_key)
	if err != nil {
		panic(err)
	}

	Put(opts, dest_key, d, "")
	if err != nil {
		panic(err)
		return
	}
	Del(opts, src_key)
}

func Del(opts Options, key string) error{
	auth := aws.Auth{AccessKey: opts.AccessKey, SecretKey: opts.SecretKey}
	s3Instance := s3.New(auth, aws.Region{Name: "*", S3Endpoint: "http://s3.amazonaws.com"})
	bkt := s3Instance.Bucket(opts.Bucket)

	return bkt.Del(key)
}

func CmdDel(opts Options, key string) {
	err := Del(opts, key)
	if err != nil {
		panic(err)
	}
}<|endoftext|>