<commit_before>package main

import (
	"bytes"
	"compress/gzip"
	"flag"
	"go/format"
	"io/ioutil"
	"log"
	"mime"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"
)

type file struct {
	name  string
	data  string
	mime  string
	mtime time.Time
	// size is the result size before compression. If 0, it means the data is uncompressed
	size int64
}

func processDir(c chan [2]string, dir string, parents []string) {
	files, err := ioutil.ReadDir(dir)
	if err != nil {
		log.Fatal(err)
	}
	for _, file := range files {
		if strings.HasPrefix(file.Name(), ".") {
			continue
		}
		n := filepath.Join(dir, file.Name())
		id := append(parents, file.Name())
		if file.IsDir() {
			processDir(c, n, id)
		} else {
			c <- [2]string{n, path.Join(id...)}
		}
	}
}

func main() {
	var outputFile, packageName, buildTags string
	flag.StringVar(&outputFile, "o", "staticfiles.go", "File to write results to.")
	flag.StringVar(&packageName, "package", "", "Package name of the resulting file. Defaults to name of the resulting file directory")
	flag.StringVar(&buildTags, "build-tags", "", "Build tags to write to the file")
	flag.Parse()
	if flag.NArg() != 1 {
		log.Println("Please pass in a directory to process")
		flag.PrintDefaults()
		os.Exit(1)
		return
	}
	if packageName == "" {
		f, err := filepath.Abs(outputFile)
		if err != nil {
			log.Fatal(err)
		}
		packageName = filepath.Base(filepath.Dir(f))
	}
	c := make(chan [2]string, 128)
	go func() {
		for _, arg := range flag.Args() {
			processDir(c, arg, []string{})
		}
		close(c)
	}()

	files := make([]*file, 0, 32)
	var b bytes.Buffer
	var b2 bytes.Buffer
	for asset := range c {
		f, err := os.Open(asset[0])
		if err != nil {
			log.Fatal(err)
		}
		stat, err := f.Stat()
		if err != nil {
			log.Fatal(err)
		}
		if _, err := b.ReadFrom(f); err != nil {
			log.Fatal(err)
		}
		f.Close()
		writer, _ := gzip.NewWriterLevel(&b2, gzip.BestCompression)
		if _, err := writer.Write(b.Bytes()); err != nil {
			log.Fatal(err)
		}
		writer.Close()
		if b2.Len() < b.Len() {
			files = append(files, &file{
				name:  asset[1],
				data:  b2.String(),
				mime:  mime.TypeByExtension(filepath.Ext(asset[0])),
				mtime: stat.ModTime(),
				size:  stat.Size(),
			})
		} else {
			files = append(files, &file{
				name:  asset[1],
				data:  b.String(),
				mime:  mime.TypeByExtension(filepath.Ext(asset[0])),
				mtime: stat.ModTime(),
			})
		}
		b.Reset()
		b2.Reset()
	}
	if err := GenerateTemplate(&b, packageName, files, buildTags); err != nil {
		log.Fatal(err)
	}
	res, err := format.Source(b.Bytes())
	if err != nil {
		log.Fatal(err)
	}
	if err := os.MkdirAll(filepath.Dir(outputFile), 0755); err != nil {
		log.Fatal(err)
	}
	f, err := os.Create(outputFile)
	if err != nil {
		log.Fatal(err)
	}
	if _, err := f.Write(res); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Parallel processing for more 🚀<commit_after>package main

import (
	"bytes"
	"compress/gzip"
	"flag"
	"go/format"
	"io/ioutil"
	"log"
	"mime"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"sort"
	"strings"
	"time"
)

type file struct {
	name  string
	data  string
	mime  string
	mtime time.Time
	// size is the result size before compression. If 0, it means the data is uncompressed
	size int64
}

func processDir(c chan [2]string, dir string, parents []string) {
	files, err := ioutil.ReadDir(dir)
	if err != nil {
		log.Fatal(err)
	}
	for _, file := range files {
		if strings.HasPrefix(file.Name(), ".") {
			continue
		}
		n := filepath.Join(dir, file.Name())
		id := append(parents, file.Name())
		if file.IsDir() {
			processDir(c, n, id)
		} else {
			c <- [2]string{n, path.Join(id...)}
		}
	}
}

type fileSlice []*file

func (a fileSlice) Len() int           { return len(a) }
func (a fileSlice) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a fileSlice) Less(i, j int) bool { return a[i].name < a[j].name }

func main() {
	var outputFile, packageName, buildTags string
	flag.StringVar(&outputFile, "o", "staticfiles.go", "File to write results to.")
	flag.StringVar(&packageName, "package", "", "Package name of the resulting file. Defaults to name of the resulting file directory")
	flag.StringVar(&buildTags, "build-tags", "", "Build tags to write to the file")
	flag.Parse()
	if flag.NArg() != 1 {
		log.Println("Please pass in a directory to process")
		flag.PrintDefaults()
		os.Exit(1)
		return
	}
	if packageName == "" {
		f, err := filepath.Abs(outputFile)
		if err != nil {
			log.Fatal(err)
		}
		packageName = filepath.Base(filepath.Dir(f))
	}
	c := make(chan [2]string, 128)
	go func() {
		for _, arg := range flag.Args() {
			processDir(c, arg, []string{})
		}
		close(c)
	}()

	// Used to communicate back all the files that were read and compressed.
	results := make(chan fileSlice)
	process := func() {
		var b bytes.Buffer
		var b2 bytes.Buffer
		files := make(fileSlice, 0, 32)
		for asset := range c {
			f, err := os.Open(asset[0])
			if err != nil {
				log.Fatal(err)
			}
			stat, err := f.Stat()
			if err != nil {
				log.Fatal(err)
			}
			if _, err := b.ReadFrom(f); err != nil {
				log.Fatal(err)
			}
			f.Close()
			writer, _ := gzip.NewWriterLevel(&b2, gzip.BestCompression)
			if _, err := writer.Write(b.Bytes()); err != nil {
				log.Fatal(err)
			}
			writer.Close()
			if b2.Len() < b.Len() {
				files = append(files, &file{
					name:  asset[1],
					data:  b2.String(),
					mime:  mime.TypeByExtension(filepath.Ext(asset[0])),
					mtime: stat.ModTime(),
					size:  stat.Size(),
				})
			} else {
				files = append(files, &file{
					name:  asset[1],
					data:  b.String(),
					mime:  mime.TypeByExtension(filepath.Ext(asset[0])),
					mtime: stat.ModTime(),
				})
			}
			b.Reset()
			b2.Reset()
		}
		results <- files
	}
	// Concurrency! Read and compress in parallel, and combine all the slices at the end.
	concurrency := runtime.NumCPU()
	for i := 0; i < concurrency; i++ {
		go process()
	}
	var files fileSlice
	for i := 0; i < concurrency; i++ {
		files = append(files, (<-results)...)
	}

	// Should sort to make sure the output is stable
	sort.Sort(files)

	var b bytes.Buffer
	if err := GenerateTemplate(&b, packageName, files, buildTags); err != nil {
		log.Fatal(err)
	}
	res, err := format.Source(b.Bytes())
	if err != nil {
		log.Fatal(err)
	}
	if err := os.MkdirAll(filepath.Dir(outputFile), 0755); err != nil {
		log.Fatal(err)
	}
	f, err := os.Create(outputFile)
	if err != nil {
		log.Fatal(err)
	}
	if _, err := f.Write(res); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package stats

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"
)

const (
	counterString = `"counter"`
	gaugeString   = `"gauge"`
	trendString   = `"trend"`

	defaultString = `"default"`
	timeString    = `"time"`
)

// Possible values for MetricType.
const (
	NoType  = MetricType(iota) // No type; metrics like this are ignored
	Counter                    // A counter that sums its data points
	Gauge                      // A gauge that displays the latest value
	Trend                      // A trend, min/max/avg/med are interesting
)

// Possible values for ValueType.
const (
	Default = ValueType(iota) // Values are presented as-is
	Time                      // Values are timestamps (nanoseconds)
)

// The serialized metric type is invalid.
var ErrInvalidMetricType = errors.New("Invalid metric type")

// The serialized value type is invalid.
var ErrInvalidValueType = errors.New("Invalid value type")

// A MetricType specifies the type of a metric.
type MetricType int

// MarshalJSON serializes a MetricType as a human readable string.
func (t MetricType) MarshalJSON() ([]byte, error) {
	switch t {
	case Counter:
		return []byte(counterString), nil
	case Gauge:
		return []byte(gaugeString), nil
	case Trend:
		return []byte(trendString), nil
	default:
		return nil, ErrInvalidMetricType
	}
}

// UnmarshalJSON deserializes a MetricType from a string representation.
func (t *MetricType) UnmarshalJSON(data []byte) error {
	switch string(data) {
	case counterString:
		*t = Counter
	case gaugeString:
		*t = Gauge
	case trendString:
		*t = Trend
	default:
		return ErrInvalidMetricType
	}

	return nil
}

func (t Metric) String() string {
	switch t.Type {
	case Counter:
		return counterString
	case Gauge:
		return gaugeString
	case Trend:
		return trendString
	default:
		return "[INVALID]"
	}
}

// The type of values a metric contains.
type ValueType int

// MarshalJSON serializes a ValueType as a human readable string.
func (t ValueType) MarshalJSON() ([]byte, error) {
	switch t {
	case Default:
		return []byte(defaultString), nil
	case Time:
		return []byte(timeString), nil
	default:
		return nil, ErrInvalidValueType
	}
}

// UnmarshalJSON deserializes a ValueType from a string representation.
func (t *ValueType) UnmarshalJSON(data []byte) error {
	switch string(data) {
	case defaultString:
		*t = Default
	case timeString:
		*t = Time
	default:
		return ErrInvalidValueType
	}

	return nil
}

// A Sample is a single measurement.
type Sample struct {
	Metric *Metric
	Time   time.Time
	Tags   map[string]string
	Value  float64
}

// A Metric defines the shape of a set of data.
type Metric struct {
	Name     string     `json:"-"`
	Type     MetricType `json:"type"`
	Contains ValueType  `json:"contains"`

	// Filled in by the API when requested, the server side cannot count on its presence.
	Sample map[string]float64 `json:"sample"`
}

func New(name string, typ MetricType, t ...ValueType) *Metric {
	vt := Default
	if len(t) > 0 {
		vt = t[0]
	}
	return &Metric{Name: name, Type: typ, Contains: vt}
}

func (m Metric) Humanize() string {
	sample := m.Sample
	switch len(sample) {
	case 0:
		return ""
	case 1:
		for _, v := range sample {
			return m.HumanizeValue(v)
		}
		return ""
	default:
		parts := make([]string, 0, len(m.Sample))
		for key, val := range m.Sample {
			parts = append(parts, fmt.Sprintf("%s=%s", key, m.HumanizeValue(val)))
		}
		return strings.Join(parts, ", ")
	}
}

func (m Metric) HumanizeValue(v float64) string {
	switch m.Contains {
	case Time:
		return time.Duration(v).String()
	default:
		return strconv.FormatFloat(v, 'f', -1, 64)
	}
}

func (m Metric) GetID() string {
	return m.Name
}

func (m *Metric) SetID(id string) error {
	m.Name = id
	return nil
}
<commit_msg>[fix] MetricType and ValueType can now be printed<commit_after>package stats

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"
)

const (
	counterString = `"counter"`
	gaugeString   = `"gauge"`
	trendString   = `"trend"`

	defaultString = `"default"`
	timeString    = `"time"`
)

// Possible values for MetricType.
const (
	NoType  = MetricType(iota) // No type; metrics like this are ignored
	Counter                    // A counter that sums its data points
	Gauge                      // A gauge that displays the latest value
	Trend                      // A trend, min/max/avg/med are interesting
)

// Possible values for ValueType.
const (
	Default = ValueType(iota) // Values are presented as-is
	Time                      // Values are timestamps (nanoseconds)
)

// The serialized metric type is invalid.
var ErrInvalidMetricType = errors.New("Invalid metric type")

// The serialized value type is invalid.
var ErrInvalidValueType = errors.New("Invalid value type")

// A MetricType specifies the type of a metric.
type MetricType int

// MarshalJSON serializes a MetricType as a human readable string.
func (t MetricType) MarshalJSON() ([]byte, error) {
	switch t {
	case Counter:
		return []byte(counterString), nil
	case Gauge:
		return []byte(gaugeString), nil
	case Trend:
		return []byte(trendString), nil
	default:
		return nil, ErrInvalidMetricType
	}
}

// UnmarshalJSON deserializes a MetricType from a string representation.
func (t *MetricType) UnmarshalJSON(data []byte) error {
	switch string(data) {
	case counterString:
		*t = Counter
	case gaugeString:
		*t = Gauge
	case trendString:
		*t = Trend
	default:
		return ErrInvalidMetricType
	}

	return nil
}

func (t MetricType) String() string {
	switch t {
	case Counter:
		return counterString
	case Gauge:
		return gaugeString
	case Trend:
		return trendString
	default:
		return "[INVALID]"
	}
}

// The type of values a metric contains.
type ValueType int

// MarshalJSON serializes a ValueType as a human readable string.
func (t ValueType) MarshalJSON() ([]byte, error) {
	switch t {
	case Default:
		return []byte(defaultString), nil
	case Time:
		return []byte(timeString), nil
	default:
		return nil, ErrInvalidValueType
	}
}

// UnmarshalJSON deserializes a ValueType from a string representation.
func (t *ValueType) UnmarshalJSON(data []byte) error {
	switch string(data) {
	case defaultString:
		*t = Default
	case timeString:
		*t = Time
	default:
		return ErrInvalidValueType
	}

	return nil
}

func (t ValueType) String() string {
	switch t.Type {
	case Default:
		return defaultString
	case Time:
		return timeString
	default:
		return "[INVALID]"
	}
}

// A Sample is a single measurement.
type Sample struct {
	Metric *Metric
	Time   time.Time
	Tags   map[string]string
	Value  float64
}

// A Metric defines the shape of a set of data.
type Metric struct {
	Name     string     `json:"-"`
	Type     MetricType `json:"type"`
	Contains ValueType  `json:"contains"`

	// Filled in by the API when requested, the server side cannot count on its presence.
	Sample map[string]float64 `json:"sample"`
}

func New(name string, typ MetricType, t ...ValueType) *Metric {
	vt := Default
	if len(t) > 0 {
		vt = t[0]
	}
	return &Metric{Name: name, Type: typ, Contains: vt}
}

func (m Metric) Humanize() string {
	sample := m.Sample
	switch len(sample) {
	case 0:
		return ""
	case 1:
		for _, v := range sample {
			return m.HumanizeValue(v)
		}
		return ""
	default:
		parts := make([]string, 0, len(m.Sample))
		for key, val := range m.Sample {
			parts = append(parts, fmt.Sprintf("%s=%s", key, m.HumanizeValue(val)))
		}
		return strings.Join(parts, ", ")
	}
}

func (m Metric) HumanizeValue(v float64) string {
	switch m.Contains {
	case Time:
		return time.Duration(v).String()
	default:
		return strconv.FormatFloat(v, 'f', -1, 64)
	}
}

func (m Metric) GetID() string {
	return m.Name
}

func (m *Metric) SetID(id string) error {
	m.Name = id
	return nil
}
<|endoftext|>


<commit_before>package gofakeit

import (
	"fmt"
	"testing"
)

type Basic struct {
	s string
	S string
}

type Nested struct {
	A   string
	B   *Basic
	bar *Basic
}

type BuiltIn struct {
	Uint8   *uint8
	Uint16  *uint16
	Uint32  *uint32
	Uint64  *uint64
	Int     *int
	Int8    *int8
	Int16   *int16
	Int32   *int32
	Int64   *int64
	Float32 *float32
	Float64 *float64
	Bool    *bool
}

type Template struct {
	Number *string `fake:"#"`
	Name   *string `fake:"{firstname}"`
	Const  *string `fake:"ABC"`
}

type StructArray struct {
	Bars    []*Basic
	Builds  []BuiltIn
	Skips   []string    `fake:"skip"`
	Strings []string    `fake:"{firstname}" fakesize:"3"`
	Empty   []*Basic    `fakesize:"0"`
	Multy   []*Template `fakesize:"3"`
}

type NestedArray struct {
	NA []StructArray `fakesize:"2"`
}

func TestStructBasic(t *testing.T) {
	Seed(11)

	var basic Basic
	Struct(&basic)
	if basic.s != "" {
		t.Error("unexported field is not populated")
	}
	if basic.S == "" {
		t.Error("exported field is populated")
	}
}

func TestStructNested(t *testing.T) {
	Seed(11)

	var nested Nested
	Struct(&nested)
	if nested.A == "" {
		t.Error("exported string field is populated")
	}
	if nested.B == nil {
		t.Error("exported struct field is populated")
	}
	if nested.B.S == "" {
		t.Error("nested struct string field is not populated")
	}
	if nested.bar != nil {
		t.Error("nested struct bar should be be populated due to unexported variable")
	}
}

func TestStructBuiltInTypes(t *testing.T) {
	Seed(11)

	var builtIn BuiltIn
	Struct(&builtIn)
	if builtIn.Uint8 == nil {
		t.Error("builtIn Uint8 was not set")
	}
	if builtIn.Uint16 == nil {
		t.Error("builtIn Uint16 was not set")
	}
	if builtIn.Uint32 == nil {
		t.Error("builtIn Uint32 was not set")
	}
	if builtIn.Uint64 == nil {
		t.Error("builtIn Uint64 was not set")
	}
	if builtIn.Int == nil {
		t.Error("builtIn int was not set")
	}
	if builtIn.Int8 == nil {
		t.Error("builtIn int8 was not set")
	}
	if builtIn.Int16 == nil {
		t.Error("builtIn int16 was not set")
	}
	if builtIn.Int32 == nil {
		t.Error("builtIn int32 was not set")
	}
	if builtIn.Int64 == nil {
		t.Error("builtIn int64 was not set")
	}
	if builtIn.Float32 == nil {
		t.Error("builtIn float32 was not set")
	}
	if builtIn.Float64 == nil {
		t.Error("builtIn float64 was not set")
	}
	if builtIn.Bool == nil {
		t.Error("builtIn bool was not set")
	}
}

func TestStructWithTemplate(t *testing.T) {
	Seed(11)

	var template Template
	Struct(&template)
	if *template.Number == "" {
		t.Error("template number should set to number value")
	}
	if *template.Name == "" {
		t.Error("template name should set to name value")
	}
	if *template.Const != "ABC" {
		t.Error("template const should set to fixed value")
	}
}

func TestStructArray(t *testing.T) {
	Seed(11)

	var sa StructArray
	Struct(&sa)
	if len(sa.Bars) != 1 {
		t.Error("sa slice Bars is not populated")
	}
	if len(sa.Builds) == 0 {
		t.Error("sa slice Builds is not populated")
	}
	if len(sa.Strings) != 3 {
		t.Error("sa strings should have a length of 3")
	}
	if len(sa.Strings) == 3 {
		for i, s := range sa.Strings {
			if s == "" {
				t.Errorf("sa strings index %d was empty", i)
			}
		}
	}
	if len(sa.Empty) != 0 {
		t.Error("sa slice Empty is populated")
	}
	if len(sa.Skips) != 0 {
		t.Error("sa slice Skips is populated")
	}
	if len(sa.Multy) != 3 {
		t.Error("sa slice Multy is not fully populated")
	}
}

func TestStructNestedArray(t *testing.T) {
	Seed(11)

	var na NestedArray
	Struct(&na)
	if len(na.NA) != 2 {
		t.Error("na slice NA is not populated")
	}
	for _, elem := range na.NA {
		if len(elem.Builds) == 0 {
			t.Error("a slice Builds is not populated")
		}
		if len(elem.Empty) != 0 {
			t.Error("a slice Empty is populated")
		}
		if len(elem.Skips) != 0 {
			t.Error("a slice Empty is populated")
		}
		if len(elem.Multy) != 3 {
			t.Error("a slice Multy is not fully populated")
		}
	}
}

func TestStructToInt(t *testing.T) {
	Seed(11)

	var si struct {
		Int         int
		IntConst    int8  `fake:"-123"`
		IntGenerate int64 `fake:"{number:1,10}"`
	}
	Struct(&si)
	if si.Int == 0 {
		t.Error("Int should be an actual number greater than 0")
	}
	if si.IntConst != -123 {
		t.Errorf("IntConst should be -123 and instead got %d", si.IntConst)
	}
	if si.IntGenerate == 0 || si.IntGenerate > 10 {
		t.Errorf("IntGenerate should be between 1-10 but got %d", si.IntGenerate)
	}
}

func TestStructToUint(t *testing.T) {
	Seed(11)

	var su struct {
		Uint         uint
		UintConst    uint8  `fake:"123"`
		UintGenerate uint64 `fake:"{number:1,10}"`
	}
	Struct(&su)
	if su.Uint == 0 {
		t.Error("Uint should be an actual number greater than 0")
	}
	if su.UintConst != 123 {
		t.Errorf("UintConst should be 123 and instead got %d", su.UintConst)
	}
	if su.UintGenerate == 0 || su.UintGenerate > 10 {
		t.Errorf("UintGenerate should be between 1-10 but got %d", su.UintGenerate)
	}
}

func TestStructToFloat(t *testing.T) {
	Seed(11)

	var sf struct {
		Float         float32
		FloatConst    float64 `fake:"123.456789"`
		FloatGenerate float32 `fake:"{latitude}"`
	}
	Struct(&sf)
	if sf.Float == 0 {
		t.Error("Float should be an actual number greater than 0")
	}
	if sf.FloatConst != 123.456789 {
		t.Errorf("FloatConst should be 123.456.789 and instead got %e", sf.FloatConst)
	}
	if sf.FloatGenerate != 31.477726 {
		t.Errorf("FloatGenerate should be 31.477726 but got %e", sf.FloatGenerate)
	}
}

func TestStructToBool(t *testing.T) {
	Seed(11)

	var sf struct {
		Bool         bool
		BoolConst    bool `fake:"true"`
		BoolGenerate bool `fake:"{bool}"`
	}
	Struct(&sf)
	if sf.Bool == false {
		t.Error("Bool should be false got true")
	}
	if sf.BoolConst != true {
		t.Errorf("BoolConst should be true got false")
	}
	if sf.BoolGenerate != false {
		t.Errorf("BoolGenerate should be false got true")
	}
}

func Example_struct() {
	Seed(11)

	type Foo struct {
		Bar     string
		Int     int
		Pointer *int
		Name    string  `fake:"{firstname}"`
		Number  string  `fake:"{number:1,10}"`
		Skip    *string `fake:"skip"`
	}

	var f Foo
	Struct(&f)

	fmt.Printf("%s\n", f.Bar)
	fmt.Printf("%d\n", f.Int)
	fmt.Printf("%d\n", *f.Pointer)
	fmt.Printf("%v\n", f.Name)
	fmt.Printf("%v\n", f.Number)
	fmt.Printf("%v\n", f.Skip)

	// Output: RMaR
	// -748872596427141310
	// -6396943744266753635
	// Carole
	// 4
	// <nil>
}

func Example_array() {
	Seed(11)

	type Foo struct {
		Bar    string
		Int    int
		Name   string  `fake:"{firstname}"`
		Number string  `fake:"{number:1,10}"`
		Skip   *string `fake:"skip"`
	}

	type FooMany struct {
		Foos  []Foo    `fakesize:"1"`
		Names []string `fake:"{firstname}" fakesize:"3"`
	}

	var fm FooMany
	Struct(&fm)

	fmt.Printf("%v\n", fm.Foos)
	fmt.Printf("%v\n", fm.Names)

	// Output:
	// [{MaRxH -6396943744266753635 Carole 4 <nil>}]
	// [Amie Alice Zachary]
}
<commit_msg>struct - added test that causes panic<commit_after>package gofakeit

import (
	"fmt"
	"testing"
)

type Basic struct {
	s string
	S string
}

type Nested struct {
	A   string
	B   *Basic
	bar *Basic
}

type BuiltIn struct {
	Uint8   *uint8
	Uint16  *uint16
	Uint32  *uint32
	Uint64  *uint64
	Int     *int
	Int8    *int8
	Int16   *int16
	Int32   *int32
	Int64   *int64
	Float32 *float32
	Float64 *float64
	Bool    *bool
}

type Template struct {
	Number *string `fake:"#"`
	Name   *string `fake:"{firstname}"`
	Const  *string `fake:"ABC"`
}

type StructArray struct {
	Bars    []*Basic
	Builds  []BuiltIn
	Skips   []string    `fake:"skip"`
	Strings []string    `fake:"{firstname}" fakesize:"3"`
	MaxLen  [5]string   `fake:"{firstname}"`
	Empty   []*Basic    `fakesize:"0"`
	Multy   []*Template `fakesize:"3"`
}

type NestedArray struct {
	NA []StructArray `fakesize:"2"`
}

func TestStructBasic(t *testing.T) {
	Seed(11)

	var basic Basic
	Struct(&basic)
	if basic.s != "" {
		t.Error("unexported field is not populated")
	}
	if basic.S == "" {
		t.Error("exported field is populated")
	}
}

func TestStructNested(t *testing.T) {
	Seed(11)

	var nested Nested
	Struct(&nested)
	if nested.A == "" {
		t.Error("exported string field is populated")
	}
	if nested.B == nil {
		t.Error("exported struct field is populated")
	}
	if nested.B.S == "" {
		t.Error("nested struct string field is not populated")
	}
	if nested.bar != nil {
		t.Error("nested struct bar should be be populated due to unexported variable")
	}
}

func TestStructBuiltInTypes(t *testing.T) {
	Seed(11)

	var builtIn BuiltIn
	Struct(&builtIn)
	if builtIn.Uint8 == nil {
		t.Error("builtIn Uint8 was not set")
	}
	if builtIn.Uint16 == nil {
		t.Error("builtIn Uint16 was not set")
	}
	if builtIn.Uint32 == nil {
		t.Error("builtIn Uint32 was not set")
	}
	if builtIn.Uint64 == nil {
		t.Error("builtIn Uint64 was not set")
	}
	if builtIn.Int == nil {
		t.Error("builtIn int was not set")
	}
	if builtIn.Int8 == nil {
		t.Error("builtIn int8 was not set")
	}
	if builtIn.Int16 == nil {
		t.Error("builtIn int16 was not set")
	}
	if builtIn.Int32 == nil {
		t.Error("builtIn int32 was not set")
	}
	if builtIn.Int64 == nil {
		t.Error("builtIn int64 was not set")
	}
	if builtIn.Float32 == nil {
		t.Error("builtIn float32 was not set")
	}
	if builtIn.Float64 == nil {
		t.Error("builtIn float64 was not set")
	}
	if builtIn.Bool == nil {
		t.Error("builtIn bool was not set")
	}
}

func TestStructWithTemplate(t *testing.T) {
	Seed(11)

	var template Template
	Struct(&template)
	if *template.Number == "" {
		t.Error("template number should set to number value")
	}
	if *template.Name == "" {
		t.Error("template name should set to name value")
	}
	if *template.Const != "ABC" {
		t.Error("template const should set to fixed value")
	}
}

func TestStructArray(t *testing.T) {
	Seed(11)

	var sa StructArray
	Struct(&sa)
	if len(sa.Bars) != 1 {
		t.Error("sa slice Bars is not populated")
	}
	if len(sa.Builds) == 0 {
		t.Error("sa slice Builds is not populated")
	}
	if len(sa.Strings) != 3 {
		t.Error("sa strings should have a length of 3")
	}
	if len(sa.Strings) == 3 {
		for i, s := range sa.Strings {
			if s == "" {
				t.Errorf("sa Strings index %d was empty", i)
			}
		}
	}
	if len(sa.MaxLen) != 5 {
		for i, s := range sa.MaxLen {
			if s == "" {
				t.Errorf("sa MaxLen index %d was empty", i)
			}
		}
	}
	if len(sa.Empty) != 0 {
		t.Error("sa slice Empty is populated")
	}
	if len(sa.Skips) != 0 {
		t.Error("sa slice Skips is populated")
	}
	if len(sa.Multy) != 3 {
		t.Error("sa slice Multy is not fully populated")
	}
}

func TestStructNestedArray(t *testing.T) {
	Seed(11)

	var na NestedArray
	Struct(&na)
	if len(na.NA) != 2 {
		t.Error("na slice NA is not populated")
	}
	for _, elem := range na.NA {
		if len(elem.Builds) == 0 {
			t.Error("a slice Builds is not populated")
		}
		if len(elem.Empty) != 0 {
			t.Error("a slice Empty is populated")
		}
		if len(elem.Skips) != 0 {
			t.Error("a slice Empty is populated")
		}
		if len(elem.Multy) != 3 {
			t.Error("a slice Multy is not fully populated")
		}
	}
}

func TestStructToInt(t *testing.T) {
	Seed(11)

	var si struct {
		Int         int
		IntConst    int8  `fake:"-123"`
		IntGenerate int64 `fake:"{number:1,10}"`
	}
	Struct(&si)
	if si.Int == 0 {
		t.Error("Int should be an actual number greater than 0")
	}
	if si.IntConst != -123 {
		t.Errorf("IntConst should be -123 and instead got %d", si.IntConst)
	}
	if si.IntGenerate == 0 || si.IntGenerate > 10 {
		t.Errorf("IntGenerate should be between 1-10 but got %d", si.IntGenerate)
	}
}

func TestStructToUint(t *testing.T) {
	Seed(11)

	var su struct {
		Uint         uint
		UintConst    uint8  `fake:"123"`
		UintGenerate uint64 `fake:"{number:1,10}"`
	}
	Struct(&su)
	if su.Uint == 0 {
		t.Error("Uint should be an actual number greater than 0")
	}
	if su.UintConst != 123 {
		t.Errorf("UintConst should be 123 and instead got %d", su.UintConst)
	}
	if su.UintGenerate == 0 || su.UintGenerate > 10 {
		t.Errorf("UintGenerate should be between 1-10 but got %d", su.UintGenerate)
	}
}

func TestStructToFloat(t *testing.T) {
	Seed(11)

	var sf struct {
		Float         float32
		FloatConst    float64 `fake:"123.456789"`
		FloatGenerate float32 `fake:"{latitude}"`
	}
	Struct(&sf)
	if sf.Float == 0 {
		t.Error("Float should be an actual number greater than 0")
	}
	if sf.FloatConst != 123.456789 {
		t.Errorf("FloatConst should be 123.456.789 and instead got %e", sf.FloatConst)
	}
	if sf.FloatGenerate != 31.477726 {
		t.Errorf("FloatGenerate should be 31.477726 but got %e", sf.FloatGenerate)
	}
}

func TestStructToBool(t *testing.T) {
	Seed(11)

	var sf struct {
		Bool         bool
		BoolConst    bool `fake:"true"`
		BoolGenerate bool `fake:"{bool}"`
	}
	Struct(&sf)
	if sf.Bool == false {
		t.Error("Bool should be false got true")
	}
	if sf.BoolConst != true {
		t.Errorf("BoolConst should be true got false")
	}
	if sf.BoolGenerate != false {
		t.Errorf("BoolGenerate should be false got true")
	}
}

func Example_struct() {
	Seed(11)

	type Foo struct {
		Bar     string
		Int     int
		Pointer *int
		Name    string  `fake:"{firstname}"`
		Number  string  `fake:"{number:1,10}"`
		Skip    *string `fake:"skip"`
	}

	var f Foo
	Struct(&f)

	fmt.Printf("%s\n", f.Bar)
	fmt.Printf("%d\n", f.Int)
	fmt.Printf("%d\n", *f.Pointer)
	fmt.Printf("%v\n", f.Name)
	fmt.Printf("%v\n", f.Number)
	fmt.Printf("%v\n", f.Skip)

	// Output: RMaR
	// -748872596427141310
	// -6396943744266753635
	// Carole
	// 4
	// <nil>
}

func Example_array() {
	Seed(11)

	type Foo struct {
		Bar    string
		Int    int
		Name   string  `fake:"{firstname}"`
		Number string  `fake:"{number:1,10}"`
		Skip   *string `fake:"skip"`
	}

	type FooMany struct {
		Foos  []Foo    `fakesize:"1"`
		Names []string `fake:"{firstname}" fakesize:"3"`
	}

	var fm FooMany
	Struct(&fm)

	fmt.Printf("%v\n", fm.Foos)
	fmt.Printf("%v\n", fm.Names)

	// Output:
	// [{MaRxH -6396943744266753635 Carole 4 <nil>}]
	// [Amie Alice Zachary]
}
<|endoftext|>


<commit_before>package suite

import (
	"flag"
	"fmt"
	"os"
	"reflect"
	"regexp"
	"runtime/debug"
	"sync"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

var allTestsFilter = func(_, _ string) (bool, error) { return true, nil }
var matchMethod = flag.String("testify.m", "", "regular expression to select tests of the testify suite to run")

type TestingT interface {
	Run(name string, f func(t *testing.T)) bool
	Errorf(format string, args ...interface{})
	FailNow()
	Log(args ...interface{})
	Logf(format string, args ...interface{})
	Skip(args ...interface{})
}

// Suite is a basic testing suite with methods for storing and
// retrieving the current *testing.T context.
type Suite struct {
	*assert.Assertions
	require *require.Assertions
	t       TestingT
}

// T retrieves the current *testing.T context.
func (suite *Suite) T() TestingT {
	return suite.t
}

// SetT sets the current *testing.T context.
func (suite *Suite) SetT(t TestingT) {
	suite.t = t
	suite.Assertions = assert.New(t)
	suite.require = require.New(t)
}

// Require returns a require context for suite.
func (suite *Suite) Require() *require.Assertions {
	if suite.require == nil {
		suite.require = require.New(suite.T())
	}
	return suite.require
}

// Assert returns an assert context for suite.  Normally, you can call
// `suite.NoError(expected, actual)`, but for situations where the embedded
// methods are overridden (for example, you might want to override
// assert.Assertions with require.Assertions), this method is provided so you
// can call `suite.Assert().NoError()`.
func (suite *Suite) Assert() *assert.Assertions {
	if suite.Assertions == nil {
		suite.Assertions = assert.New(suite.T())
	}
	return suite.Assertions
}

func failOnPanic(t *testing.T) {
	r := recover()
	if r != nil {
		t.Errorf("test panicked: %v\n%s", r, debug.Stack())
		t.FailNow()
	}
}

// Run provides suite functionality around golang subtests.  It should be
// called in place of t.Run(name, func(t *testing.T)) in test suite code.
// The passed-in func will be executed as a subtest with a fresh instance of t.
// Provides compatibility with go test pkg -run TestSuite/TestName/SubTestName.
func (suite *Suite) Run(name string, subtest func()) bool {
	oldT := suite.T()
	defer suite.SetT(oldT)
	return oldT.Run(name, func(t *testing.T) {
		suite.SetT(t)
		subtest()
	})
}

// Run takes a testing suite and runs all of the tests attached
// to it.
func Run(t *testing.T, suite TestingSuite) {
	testsSync := &sync.WaitGroup{}
	suite.SetT(t)
	defer failOnPanic(t)

	suiteSetupDone := false

	methodFinder := reflect.TypeOf(suite)
	tests := []testing.InternalTest{}
	for index := 0; index < methodFinder.NumMethod(); index++ {
		method := methodFinder.Method(index)
		ok, err := methodFilter(method.Name)
		if err != nil {
			fmt.Fprintf(os.Stderr, "testify: invalid regexp for -m: %s\n", err)
			os.Exit(1)
		}
		if !ok {
			continue
		}
		if !suiteSetupDone {
			if setupAllSuite, ok := suite.(SetupAllSuite); ok {
				setupAllSuite.SetupSuite()
			}
			defer func() {
				if tearDownAllSuite, ok := suite.(TearDownAllSuite); ok {
					testsSync.Wait()
					tearDownAllSuite.TearDownSuite()
				}
			}()
			suiteSetupDone = true
		}
		test := testing.InternalTest{
			Name: method.Name,
			F: func(t *testing.T) {
				defer testsSync.Done()
				parentT := suite.T()
				suite.SetT(t)
				defer failOnPanic(t)

				if setupTestSuite, ok := suite.(SetupTestSuite); ok {
					setupTestSuite.SetupTest()
				}
				if beforeTestSuite, ok := suite.(BeforeTest); ok {
					beforeTestSuite.BeforeTest(methodFinder.Elem().Name(), method.Name)
				}
				defer func() {
					if afterTestSuite, ok := suite.(AfterTest); ok {
						afterTestSuite.AfterTest(methodFinder.Elem().Name(), method.Name)
					}
					if tearDownTestSuite, ok := suite.(TearDownTestSuite); ok {
						tearDownTestSuite.TearDownTest()
					}
					suite.SetT(parentT)
				}()
				method.Func.Call([]reflect.Value{reflect.ValueOf(suite)})
			},
		}
		tests = append(tests, test)
		testsSync.Add(1)
	}
	runTests(t, tests)
}

func runTests(t testing.TB, tests []testing.InternalTest) {
	r, ok := t.(runner)
	if !ok { // backwards compatibility with Go 1.6 and below
		if !testing.RunTests(allTestsFilter, tests) {
			t.Fail()
		}
		return
	}

	for _, test := range tests {
		r.Run(test.Name, test.F)
	}
}

// Filtering method according to set regular expression
// specified command-line argument -m
func methodFilter(name string) (bool, error) {
	if ok, _ := regexp.MatchString("^Test", name); !ok {
		return false, nil
	}
	return regexp.MatchString(*matchMethod, name)
}

type runner interface {
	Run(name string, f func(t *testing.T)) bool
}
<commit_msg>fix(suite): Add Fatalf method to suite.TestingT for gomock compatibility<commit_after>package suite

import (
	"flag"
	"fmt"
	"os"
	"reflect"
	"regexp"
	"runtime/debug"
	"sync"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

var allTestsFilter = func(_, _ string) (bool, error) { return true, nil }
var matchMethod = flag.String("testify.m", "", "regular expression to select tests of the testify suite to run")

type TestingT interface {
	Run(name string, f func(t *testing.T)) bool
	Errorf(format string, args ...interface{})
	Fatalf(format string, args ...interface{})
	FailNow()
	Log(args ...interface{})
	Logf(format string, args ...interface{})
	Skip(args ...interface{})
}

// Suite is a basic testing suite with methods for storing and
// retrieving the current *testing.T context.
type Suite struct {
	*assert.Assertions
	require *require.Assertions
	t       TestingT
}

// T retrieves the current *testing.T context.
func (suite *Suite) T() TestingT {
	return suite.t
}

// SetT sets the current *testing.T context.
func (suite *Suite) SetT(t TestingT) {
	suite.t = t
	suite.Assertions = assert.New(t)
	suite.require = require.New(t)
}

// Require returns a require context for suite.
func (suite *Suite) Require() *require.Assertions {
	if suite.require == nil {
		suite.require = require.New(suite.T())
	}
	return suite.require
}

// Assert returns an assert context for suite.  Normally, you can call
// `suite.NoError(expected, actual)`, but for situations where the embedded
// methods are overridden (for example, you might want to override
// assert.Assertions with require.Assertions), this method is provided so you
// can call `suite.Assert().NoError()`.
func (suite *Suite) Assert() *assert.Assertions {
	if suite.Assertions == nil {
		suite.Assertions = assert.New(suite.T())
	}
	return suite.Assertions
}

func failOnPanic(t *testing.T) {
	r := recover()
	if r != nil {
		t.Errorf("test panicked: %v\n%s", r, debug.Stack())
		t.FailNow()
	}
}

// Run provides suite functionality around golang subtests.  It should be
// called in place of t.Run(name, func(t *testing.T)) in test suite code.
// The passed-in func will be executed as a subtest with a fresh instance of t.
// Provides compatibility with go test pkg -run TestSuite/TestName/SubTestName.
func (suite *Suite) Run(name string, subtest func()) bool {
	oldT := suite.T()
	defer suite.SetT(oldT)
	return oldT.Run(name, func(t *testing.T) {
		suite.SetT(t)
		subtest()
	})
}

// Run takes a testing suite and runs all of the tests attached
// to it.
func Run(t *testing.T, suite TestingSuite) {
	testsSync := &sync.WaitGroup{}
	suite.SetT(t)
	defer failOnPanic(t)

	suiteSetupDone := false

	methodFinder := reflect.TypeOf(suite)
	tests := []testing.InternalTest{}
	for index := 0; index < methodFinder.NumMethod(); index++ {
		method := methodFinder.Method(index)
		ok, err := methodFilter(method.Name)
		if err != nil {
			fmt.Fprintf(os.Stderr, "testify: invalid regexp for -m: %s\n", err)
			os.Exit(1)
		}
		if !ok {
			continue
		}
		if !suiteSetupDone {
			if setupAllSuite, ok := suite.(SetupAllSuite); ok {
				setupAllSuite.SetupSuite()
			}
			defer func() {
				if tearDownAllSuite, ok := suite.(TearDownAllSuite); ok {
					testsSync.Wait()
					tearDownAllSuite.TearDownSuite()
				}
			}()
			suiteSetupDone = true
		}
		test := testing.InternalTest{
			Name: method.Name,
			F: func(t *testing.T) {
				defer testsSync.Done()
				parentT := suite.T()
				suite.SetT(t)
				defer failOnPanic(t)

				if setupTestSuite, ok := suite.(SetupTestSuite); ok {
					setupTestSuite.SetupTest()
				}
				if beforeTestSuite, ok := suite.(BeforeTest); ok {
					beforeTestSuite.BeforeTest(methodFinder.Elem().Name(), method.Name)
				}
				defer func() {
					if afterTestSuite, ok := suite.(AfterTest); ok {
						afterTestSuite.AfterTest(methodFinder.Elem().Name(), method.Name)
					}
					if tearDownTestSuite, ok := suite.(TearDownTestSuite); ok {
						tearDownTestSuite.TearDownTest()
					}
					suite.SetT(parentT)
				}()
				method.Func.Call([]reflect.Value{reflect.ValueOf(suite)})
			},
		}
		tests = append(tests, test)
		testsSync.Add(1)
	}
	runTests(t, tests)
}

func runTests(t testing.TB, tests []testing.InternalTest) {
	r, ok := t.(runner)
	if !ok { // backwards compatibility with Go 1.6 and below
		if !testing.RunTests(allTestsFilter, tests) {
			t.Fail()
		}
		return
	}

	for _, test := range tests {
		r.Run(test.Name, test.F)
	}
}

// Filtering method according to set regular expression
// specified command-line argument -m
func methodFilter(name string) (bool, error) {
	if ok, _ := regexp.MatchString("^Test", name); !ok {
		return false, nil
	}
	return regexp.MatchString(*matchMethod, name)
}

type runner interface {
	Run(name string, f func(t *testing.T)) bool
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"net"

	baps3 "github.com/UniversityRadioYork/baps3-go"
)

type Listener struct {
	listener   net.Listener
	requestCh  chan baps3.Message
	responseCh chan []byte
	tok        *baps3.Tokeniser
	clients    []*Client
}

func MakeListener(addr string, port string, reqCh chan baps3.Message) (*Listener, error) {
	netListener, err := net.Listen("tcp", addr+":"+port)
	if err != nil {
		return nil, err
	}
	listener := &Listener{
		listener:   netListener,
		tok:        baps3.NewTokeniser(),
		responseCh: make(chan []byte),
		requestCh:  reqCh,
	}
	return listener, nil
}

func (l *Listener) Broadcast(msg baps3.Message) {
	// Probably should have \n at the end
	for _, c := range l.clients {
		data, err := msg.Pack()
		if err != nil {
			continue // TODO Handle
		}
		c.Outgoing <- data
	}
}

func (l *Listener) ReceiveLoop() {
	for data := range l.responseCh {
		lines, _, err := l.tok.Tokenise(data)
		if err != nil {
			log.Println(err)
			continue // TODO: Do something?
		}
		for _, line := range lines {
			msg, err := baps3.LineToMessage(line)
			if err != nil {
				log.Println(err)
				continue // TODO: Do something?
			}
			// TODO: Something with particular types of msgs
			l.requestCh <- *msg
		}
	}
}

func (s *Listener) ProcessCommand(msg baps3.Message) {
	s.Broadcast(msg)
}

func (l *Listener) run() {
	go l.ReceiveLoop()
	for {
		conn, err := l.listener.Accept()
		log.Println("Opening connection from " + conn.RemoteAddr().String())
		if err != nil {
			log.Println("Error accepting connection: " + err.Error())
			continue
		}
		client := MakeClient(conn, l.responseCh)
		l.clients = append(l.clients, client)
	}
}
<commit_msg>Only pack the message once<commit_after>package main

import (
	"log"
	"net"

	baps3 "github.com/UniversityRadioYork/baps3-go"
)

type Listener struct {
	listener   net.Listener
	requestCh  chan baps3.Message
	responseCh chan []byte
	tok        *baps3.Tokeniser
	clients    []*Client
}

func MakeListener(addr string, port string, reqCh chan baps3.Message) (*Listener, error) {
	netListener, err := net.Listen("tcp", addr+":"+port)
	if err != nil {
		return nil, err
	}
	listener := &Listener{
		listener:   netListener,
		tok:        baps3.NewTokeniser(),
		responseCh: make(chan []byte),
		requestCh:  reqCh,
	}
	return listener, nil
}

func (l *Listener) Broadcast(msg baps3.Message) {
	// Probably should have \n at the end
	data, err := msg.Pack()
	if err != nil {
		return // TODO Handle
	}
	for _, c := range l.clients {
		c.Outgoing <- data
	}
}

func (l *Listener) ReceiveLoop() {
	for data := range l.responseCh {
		lines, _, err := l.tok.Tokenise(data)
		if err != nil {
			log.Println(err)
			continue // TODO: Do something?
		}
		for _, line := range lines {
			msg, err := baps3.LineToMessage(line)
			if err != nil {
				log.Println(err)
				continue // TODO: Do something?
			}
			// TODO: Something with particular types of msgs
			l.requestCh <- *msg
		}
	}
}

func (s *Listener) ProcessCommand(msg baps3.Message) {
	s.Broadcast(msg)
}

func (l *Listener) run() {
	go l.ReceiveLoop()
	for {
		conn, err := l.listener.Accept()
		log.Println("Opening connection from " + conn.RemoteAddr().String())
		if err != nil {
			log.Println("Error accepting connection: " + err.Error())
			continue
		}
		client := MakeClient(conn, l.responseCh)
		l.clients = append(l.clients, client)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/zvin/gocairo"
	"strings"
	"sync"
	"time"
)

const (
	WIDTH  = 2000
	HEIGHT = 3000
)

var (
	locations          = make(map[string]*Location)
	locationsMutex     sync.RWMutex
	CurrentlyUsedSites LockableWebsiteSlice
)

type UserAndEvent struct {
	User  *User
	Event []interface{}
}

type Location struct {
	Join         chan *User
	Quit         chan *User
	Message      chan UserAndEvent
	Close        chan bool
	Url          string
	Users        []*User
	UserCount    int
	FileName     string
	chatFileName string
	Chat         *MessagesLog
	Surface      *cairo.Surface
	delta        []interface{}
}

func init() {
	go func() {
		tick := time.Tick(10 * time.Second)
		for _ = range tick {
			update_currently_used_sites()
		}
	}()
}

func GetLocation(url string) *Location {
	locationsMutex.Lock()
	defer locationsMutex.Unlock()
	location, present := locations[url]
	if present {
		return location
	} else {
		location = NewLocation(url)
		locations[url] = location
	}
	return location
}

func CloseAllLocations() {
	locationsMutex.Lock()
	for _, loc := range locations {
		loc.Close <- true
		<-loc.Close
		delete(locations, loc.Url)
	}
	locationsMutex.Unlock()
}

func CloseLocation(location *Location) {
	locationsMutex.Lock()
	delete(locations, location.Url)
	locationsMutex.Unlock()
}

func update_currently_used_sites() {
	var sites []Website
	locationsMutex.RLock()
	for _, location := range locations {
		count := location.UserCount
		if count > 0 {
			sites = append(sites, Website{Url: location.Url, UserCount: count})
		}
	}
	locationsMutex.RUnlock()
	SortWebsites(sites)
	CurrentlyUsedSites.Mutex.Lock()
	CurrentlyUsedSites.Sites = sites[:MinInt(len(sites), 10)]
	CurrentlyUsedSites.Mutex.Unlock()
}

func NewLocation(url string) *Location {
	loc := new(Location)
	loc.Join = make(chan *User)
	loc.Quit = make(chan *User)
	loc.Message = make(chan UserAndEvent)
	loc.Close = make(chan bool)
	loc.Url = url
	b64fname := Base64Encode(url)
	b64fname = b64fname[:MinInt(len(b64fname), 251)]
	b64fname = strings.Replace(b64fname, "/", "_", -1)
	loc.chatFileName = CHAT_DIR + "/" + b64fname + ".gob"
	loc.Chat = OpenMessagesLog(loc.chatFileName)
	loc.FileName = IMAGES_DIR + "/" + b64fname + ".png" // filename
	Log.Printf("filename: %v", loc.FileName)
	loc.Surface = cairo.NewSurfaceFromPNG(loc.FileName)
	if loc.Surface.SurfaceStatus() != 0 {
		loc.Surface.Finish()
		loc.Surface.Destroy()
		loc.Surface = cairo.NewSurface(cairo.FormatArgB32, WIDTH, HEIGHT)
	}
	loc.Surface.SetSourceRGB(0, 0, 0)
	go loc.main()
	return loc
}

func (location *Location) main() {
	save_tick := time.Tick(1 * time.Minute)
	for {
		select {
		case user := <-location.Join:
			if len(location.Users) >= MAX_USERS_PER_LOCATION {
				user.Error("Too much users at this location, try adding #something at the end of the URL.")
			} else {
				Log.Println("New user", user.UserId, "joins", location.Url)
				location.AddUser(user)
			}
		case user := <-location.Quit:
			location.RemoveUser(user)
			if len(location.Users) == 0 {
				location.Save()
				location.Destroy()
				CloseLocation(location)
				return
			}
		case message := <-location.Message:
			event := message.User.GotMessage(message.Event)
			if event != nil {
				location.broadcast(message.User, event)
			}
		case <-save_tick:
			location.Save()
		case <-location.Close:
			for _, user := range location.Users {
				user.Socket.Close()
			}
			location.Save()
			location.Destroy()
			location.Close <- true
			return
		}
	}
}

func (location *Location) Destroy() {
	location.Surface.Finish()
	location.Surface.Destroy()
}

func (location *Location) broadcast(user *User, event []interface{}) {
	// event.insert(1, user.UserId) ...
	event = append(event[:1], append([]interface{}{user.UserId}, event[1:]...)...)
	for _, other := range location.Users {
		other.SendEvent(event)
	}
}

func (location *Location) AddUser(user *User) {
	user.Location = location
	// Send the list of present users to this user:
	timestamp := Timestamp()
	for _, other := range location.Users {
		user.SendEvent([]interface{}{
			EventTypeJoin,
			other.UserId,
			[]interface{}{other.PositionX, other.PositionY},
			[]interface{}{other.ColorRed, other.ColorGreen, other.ColorBlue},
			other.MouseIsDown,
			false, // not you
			other.Nickname,
			other.UsePen,
			0, // no timestamp
		})
	}
	// Send the delta between the image and now to the new user:
	user.SendEvent([]interface{}{
		EventTypeWelcome,
		location.FileName,
		location.GetDelta(),
		location.Chat.GetMessages(),
	})
	// Send this new user to other users:
	event := []interface{}{
		EventTypeJoin,
		[]interface{}{user.PositionX, user.PositionY},
		[]interface{}{user.ColorRed, user.ColorGreen, user.ColorBlue},
		user.MouseIsDown,
		false, // not you
		user.Nickname,
		user.UsePen,
		timestamp,
	}
	location.broadcast(user, event) // user is not yet in location.Users, so it will not receive this event.
	// Send this user to himself
	event = []interface{}{
		EventTypeJoin,
		user.UserId,
		[]interface{}{user.PositionX, user.PositionY},
		[]interface{}{user.ColorRed, user.ColorGreen, user.ColorBlue},
		user.MouseIsDown,
		true, // you
		user.Nickname,
		user.UsePen,
		timestamp,
	}
	user.SendEvent(event)
	location.Users = append(location.Users, user)
	location.Chat.AddMessage(timestamp, "", "user "+user.Nickname+" joined")
	location.UserCount += 1
}

func (location *Location) RemoveUser(user *User) {
	location.Users = Remove(location.Users, user)
	timestamp := Timestamp()
	location.broadcast(user, []interface{}{EventTypeLeave, timestamp})
	location.Chat.AddMessage(timestamp, "", "user "+user.Nickname+" left")
	location.UserCount -= 1
}

func (location *Location) DrawLine(x1, y1, x2, y2, duration, red, green, blue int, use_pen bool) {
	//    fmt.Printf("draw line duration %d\n", duration)
	if duration <= 0 {
		return
	}
	d := Distance(x1, y1, x2, y2)
	if d <= 0 {
		return
	}
	location.delta = append(location.delta, []interface{}{x1, y1, x2, y2, duration, red, green, blue, use_pen})
	speed := d / float64(duration)
	if !use_pen { // not use_pen: use eraser
		location.Surface.SetOperator(cairo.OperatorDestOut)
	} else {
		location.Surface.SetOperator(cairo.OperatorOver)
		location.Surface.SetSourceRGB(float64(red)/255., float64(green)/255., float64(blue)/255.)
	}
	location.Surface.SetLineWidth(1. / (1.3 + (3. * speed)))
	location.Surface.MoveTo(float64(x1), float64(y1))
	location.Surface.LineTo(float64(x2), float64(y2))
	location.Surface.Stroke()
}

func (location *Location) GetDelta() []interface{} {
	return location.delta
}

func (location *Location) Save() {
	if len(location.delta) > 0 {
		Log.Printf("save %s (delta %d)\n", location.Url, len(location.delta))
		location.Surface.WriteToPNG(location.FileName) // Output to PNG
		location.delta = nil
	}
	location.Chat.Save()
}

func Remove(list []*User, value *User) []*User {
	var i int
	var elem *User
	for i, elem = range list {
		if elem == value {
			break
		}
	}
	return append(list[:i], list[i+1:]...)
}
<commit_msg>change visibility<commit_after>package main

import (
	"github.com/zvin/gocairo"
	"strings"
	"sync"
	"time"
)

const (
	WIDTH  = 2000
	HEIGHT = 3000
)

var (
	locations          = make(map[string]*Location)
	locationsMutex     sync.RWMutex
	CurrentlyUsedSites LockableWebsiteSlice
)

type UserAndEvent struct {
	User  *User
	Event []interface{}
}

type Location struct {
	Join         chan *User
	Quit         chan *User
	Message      chan UserAndEvent
	Close        chan bool
	Url          string
	UserCount    int
	Chat         *MessagesLog
	users        []*User
	fileName     string
	chatFileName string
	surface      *cairo.Surface
	delta        []interface{}
}

func init() {
	go func() {
		tick := time.Tick(10 * time.Second)
		for _ = range tick {
			update_currently_used_sites()
		}
	}()
}

func GetLocation(url string) *Location {
	locationsMutex.Lock()
	defer locationsMutex.Unlock()
	location, present := locations[url]
	if present {
		return location
	} else {
		location = NewLocation(url)
		locations[url] = location
	}
	return location
}

func CloseAllLocations() {
	locationsMutex.Lock()
	for _, loc := range locations {
		loc.Close <- true
		<-loc.Close
		delete(locations, loc.Url)
	}
	locationsMutex.Unlock()
}

func CloseLocation(location *Location) {
	locationsMutex.Lock()
	delete(locations, location.Url)
	locationsMutex.Unlock()
}

func update_currently_used_sites() {
	var sites []Website
	locationsMutex.RLock()
	for _, location := range locations {
		count := location.UserCount
		if count > 0 {
			sites = append(sites, Website{Url: location.Url, UserCount: count})
		}
	}
	locationsMutex.RUnlock()
	SortWebsites(sites)
	CurrentlyUsedSites.Mutex.Lock()
	CurrentlyUsedSites.Sites = sites[:MinInt(len(sites), 10)]
	CurrentlyUsedSites.Mutex.Unlock()
}

func NewLocation(url string) *Location {
	loc := new(Location)
	loc.Join = make(chan *User)
	loc.Quit = make(chan *User)
	loc.Message = make(chan UserAndEvent)
	loc.Close = make(chan bool)
	loc.Url = url
	b64fname := Base64Encode(url)
	b64fname = b64fname[:MinInt(len(b64fname), 251)]
	b64fname = strings.Replace(b64fname, "/", "_", -1)
	loc.chatFileName = CHAT_DIR + "/" + b64fname + ".gob"
	loc.Chat = OpenMessagesLog(loc.chatFileName)
	loc.fileName = IMAGES_DIR + "/" + b64fname + ".png" // filename
	Log.Printf("filename: %v", loc.fileName)
	loc.surface = cairo.NewSurfaceFromPNG(loc.fileName)
	if loc.surface.SurfaceStatus() != 0 {
		loc.surface.Finish()
		loc.surface.Destroy()
		loc.surface = cairo.NewSurface(cairo.FormatArgB32, WIDTH, HEIGHT)
	}
	loc.surface.SetSourceRGB(0, 0, 0)
	go loc.main()
	return loc
}

func (location *Location) main() {
	save_tick := time.Tick(1 * time.Minute)
	for {
		select {
		case user := <-location.Join:
			if len(location.users) >= MAX_USERS_PER_LOCATION {
				user.Error("Too much users at this location, try adding #something at the end of the URL.")
			} else {
				Log.Println("New user", user.UserId, "joins", location.Url)
				location.AddUser(user)
			}
		case user := <-location.Quit:
			location.RemoveUser(user)
			if len(location.users) == 0 {
				location.Save()
				location.Destroy()
				CloseLocation(location)
				return
			}
		case message := <-location.Message:
			event := message.User.GotMessage(message.Event)
			if event != nil {
				location.broadcast(message.User, event)
			}
		case <-save_tick:
			location.Save()
		case <-location.Close:
			for _, user := range location.users {
				user.Socket.Close()
			}
			location.Save()
			location.Destroy()
			location.Close <- true
			return
		}
	}
}

func (location *Location) Destroy() {
	location.surface.Finish()
	location.surface.Destroy()
}

func (location *Location) broadcast(user *User, event []interface{}) {
	// event.insert(1, user.UserId) ...
	event = append(event[:1], append([]interface{}{user.UserId}, event[1:]...)...)
	for _, other := range location.users {
		other.SendEvent(event)
	}
}

func (location *Location) AddUser(user *User) {
	user.Location = location
	// Send the list of present users to this user:
	timestamp := Timestamp()
	for _, other := range location.users {
		user.SendEvent([]interface{}{
			EventTypeJoin,
			other.UserId,
			[]interface{}{other.PositionX, other.PositionY},
			[]interface{}{other.ColorRed, other.ColorGreen, other.ColorBlue},
			other.MouseIsDown,
			false, // not you
			other.Nickname,
			other.UsePen,
			0, // no timestamp
		})
	}
	// Send the delta between the image and now to the new user:
	user.SendEvent([]interface{}{
		EventTypeWelcome,
		location.fileName,
		location.GetDelta(),
		location.Chat.GetMessages(),
	})
	// Send this new user to other users:
	event := []interface{}{
		EventTypeJoin,
		[]interface{}{user.PositionX, user.PositionY},
		[]interface{}{user.ColorRed, user.ColorGreen, user.ColorBlue},
		user.MouseIsDown,
		false, // not you
		user.Nickname,
		user.UsePen,
		timestamp,
	}
	location.broadcast(user, event) // user is not yet in location.users, so it will not receive this event.
	// Send this user to himself
	event = []interface{}{
		EventTypeJoin,
		user.UserId,
		[]interface{}{user.PositionX, user.PositionY},
		[]interface{}{user.ColorRed, user.ColorGreen, user.ColorBlue},
		user.MouseIsDown,
		true, // you
		user.Nickname,
		user.UsePen,
		timestamp,
	}
	user.SendEvent(event)
	location.users = append(location.users, user)
	location.Chat.AddMessage(timestamp, "", "user "+user.Nickname+" joined")
	location.UserCount += 1
}

func (location *Location) RemoveUser(user *User) {
	location.users = Remove(location.users, user)
	timestamp := Timestamp()
	location.broadcast(user, []interface{}{EventTypeLeave, timestamp})
	location.Chat.AddMessage(timestamp, "", "user "+user.Nickname+" left")
	location.UserCount -= 1
}

func (location *Location) DrawLine(x1, y1, x2, y2, duration, red, green, blue int, use_pen bool) {
	//    fmt.Printf("draw line duration %d\n", duration)
	if duration <= 0 {
		return
	}
	d := Distance(x1, y1, x2, y2)
	if d <= 0 {
		return
	}
	location.delta = append(location.delta, []interface{}{x1, y1, x2, y2, duration, red, green, blue, use_pen})
	speed := d / float64(duration)
	if !use_pen { // not use_pen: use eraser
		location.surface.SetOperator(cairo.OperatorDestOut)
	} else {
		location.surface.SetOperator(cairo.OperatorOver)
		location.surface.SetSourceRGB(float64(red)/255., float64(green)/255., float64(blue)/255.)
	}
	location.surface.SetLineWidth(1. / (1.3 + (3. * speed)))
	location.surface.MoveTo(float64(x1), float64(y1))
	location.surface.LineTo(float64(x2), float64(y2))
	location.surface.Stroke()
}

func (location *Location) GetDelta() []interface{} {
	return location.delta
}

func (location *Location) Save() {
	if len(location.delta) > 0 {
		Log.Printf("save %s (delta %d)\n", location.Url, len(location.delta))
		location.surface.WriteToPNG(location.fileName) // Output to PNG
		location.delta = nil
	}
	location.Chat.Save()
}

func Remove(list []*User, value *User) []*User {
	var i int
	var elem *User
	for i, elem = range list {
		if elem == value {
			break
		}
	}
	return append(list[:i], list[i+1:]...)
}
<|endoftext|>


<commit_before>package lockgate

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"path"
	"strings"

	"github.com/andrew-d/go-termutil"
	"github.com/atsaki/golang-cloudstack-library"
	"github.com/atsaki/lockgate/cli"
	"gopkg.in/yaml.v1"
)

type AccountConfig struct {
	URL       string `yaml:"url"`
	Username  string `yaml:"username"`
	Password  string `yaml:"password"`
	APIKey    string `yaml:"apikey"`
	SecretKey string `yaml:"secretkey"`
}

type CommandConfig struct {
	Options map[string]interface{} `yaml:"options"`
	Keys    []string               `yaml:"keys"`
}

type Config struct {
	Account  AccountConfig            `yaml:"account"`
	Commands map[string]CommandConfig `yaml:"command"`
}

func GetConfigFilePath(profile string) string {
	return path.Join(ConfigDir, profile, ConfigFile)
}

func LoadConfig(profile string) (*Config, error) {
	config := new(Config)
	configFilePath := GetConfigFilePath(profile)
	if _, err := os.Stat(configFilePath); os.IsNotExist(err) {
		configFilePath = GetConfigFilePath("default")
	}

	contents, err := ioutil.ReadFile(configFilePath)
	if err != nil {
		msg := fmt.Sprintf("Failed to read %s ...", configFilePath)
		fmt.Fprintln(os.Stderr, msg)
		fmt.Fprintln(os.Stderr, err)
		log.Println(msg)
		log.Println(err)
		return nil, err
	}

	err = yaml.Unmarshal(contents, config)
	if err != nil {
		msg := fmt.Sprintf("Failed to unmarshal the contents of %s ...", configFilePath)
		fmt.Fprintln(os.Stderr, msg)
		fmt.Fprintln(os.Stderr, err)
		log.Println(msg)
		log.Println(err)
		return nil, err
	}
	return config, err
}

func SetLogLevel(c *cli.Context) {
	if !c.App.Flag("debug").Bool() {
		log.SetOutput(ioutil.Discard)
	}
}

func GetProfile(c *cli.Context) string {
	profile := c.App.Flag("profile").String()
	if profile == "" {
		for _, e := range os.Environ() {
			pair := strings.Split(e, "=")
			if pair[0] == "LGPROF" {
				profile = pair[1]
			}
		}
		if profile == "" {
			profile = "default"
		}
	}
	return profile
}

func GetClient(c *cli.Context) (*cloudstack.Client, error) {

	profile := GetProfile(c)

	config, err := LoadConfig(profile)
	if err != nil {
		msg := "Failed to load config. profile: " + profile
		fmt.Fprintln(os.Stderr, msg)
		log.Println(msg)
		log.Fatal(err)
	}

	url, err := url.Parse(config.Account.URL)
	if err != nil {
		msg := "Failed to parse URL: " + config.Account.URL
		fmt.Fprintln(os.Stderr, msg)
		log.Println(msg)
		log.Fatal(err)
	}

	return cloudstack.NewClient(*url,
		config.Account.APIKey, config.Account.SecretKey,
		config.Account.Username, config.Account.Password)
}

func GetArgumentsFromStdin() []string {
	var args []string
	if !termutil.Isatty(os.Stdin.Fd()) {
		scanner := bufio.NewScanner(os.Stdin)
		for scanner.Scan() {
			fields := strings.Fields(scanner.Text())
			if len(fields) > 0 {
				args = append(args, fields[0])
			}
		}
	}
	return args
}
<commit_msg>set log flags to show file name<commit_after>package lockgate

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"path"
	"strings"

	"github.com/andrew-d/go-termutil"
	"github.com/atsaki/golang-cloudstack-library"
	"github.com/atsaki/lockgate/cli"
	"gopkg.in/yaml.v1"
)

type AccountConfig struct {
	URL       string `yaml:"url"`
	Username  string `yaml:"username"`
	Password  string `yaml:"password"`
	APIKey    string `yaml:"apikey"`
	SecretKey string `yaml:"secretkey"`
}

type CommandConfig struct {
	Options map[string]interface{} `yaml:"options"`
	Keys    []string               `yaml:"keys"`
}

type Config struct {
	Account  AccountConfig            `yaml:"account"`
	Commands map[string]CommandConfig `yaml:"command"`
}

func GetConfigFilePath(profile string) string {
	return path.Join(ConfigDir, profile, ConfigFile)
}

func LoadConfig(profile string) (*Config, error) {
	config := new(Config)
	configFilePath := GetConfigFilePath(profile)
	if _, err := os.Stat(configFilePath); os.IsNotExist(err) {
		configFilePath = GetConfigFilePath("default")
	}

	contents, err := ioutil.ReadFile(configFilePath)
	if err != nil {
		msg := fmt.Sprintf("Failed to read %s ...", configFilePath)
		fmt.Fprintln(os.Stderr, msg)
		fmt.Fprintln(os.Stderr, err)
		log.Println(msg)
		log.Println(err)
		return nil, err
	}

	err = yaml.Unmarshal(contents, config)
	if err != nil {
		msg := fmt.Sprintf("Failed to unmarshal the contents of %s ...", configFilePath)
		fmt.Fprintln(os.Stderr, msg)
		fmt.Fprintln(os.Stderr, err)
		log.Println(msg)
		log.Println(err)
		return nil, err
	}
	return config, err
}

func SetLogLevel(c *cli.Context) {
	log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
	if !c.App.Flag("debug").Bool() {
		log.SetOutput(ioutil.Discard)
	}
}

func GetProfile(c *cli.Context) string {
	profile := c.App.Flag("profile").String()
	if profile == "" {
		for _, e := range os.Environ() {
			pair := strings.Split(e, "=")
			if pair[0] == "LGPROF" {
				profile = pair[1]
			}
		}
		if profile == "" {
			profile = "default"
		}
	}
	return profile
}

func GetClient(c *cli.Context) (*cloudstack.Client, error) {

	profile := GetProfile(c)

	config, err := LoadConfig(profile)
	if err != nil {
		msg := "Failed to load config. profile: " + profile
		fmt.Fprintln(os.Stderr, msg)
		log.Println(msg)
		log.Fatal(err)
	}

	url, err := url.Parse(config.Account.URL)
	if err != nil {
		msg := "Failed to parse URL: " + config.Account.URL
		fmt.Fprintln(os.Stderr, msg)
		log.Println(msg)
		log.Fatal(err)
	}

	return cloudstack.NewClient(*url,
		config.Account.APIKey, config.Account.SecretKey,
		config.Account.Username, config.Account.Password)
}

func GetArgumentsFromStdin() []string {
	var args []string
	if !termutil.Isatty(os.Stdin.Fd()) {
		scanner := bufio.NewScanner(os.Stdin)
		for scanner.Scan() {
			fields := strings.Fields(scanner.Text())
			if len(fields) > 0 {
				args = append(args, fields[0])
			}
		}
	}
	return args
}
<|endoftext|>


<commit_before>// syncwatcher.go
package main

import (
	"code.google.com/p/go.exp/fsnotify"
	"os"
	"bufio"
	"io/ioutil"
	"net/http"
	"net/url"
	"crypto/tls"
	"encoding/json"
	"log"
	"fmt"
	"time"
	"flag"
	"runtime"
	"path/filepath"
	"strings"
	"sort"
)

type Configuration struct {
	Version			int
	Repositories	[]RepositoryConfiguration
}

type RepositoryConfiguration struct {
	ID					string
	Directory			string
	ReadOnly			bool
	RescanIntervalS		int
}

// HTTP Authentication
var (
	target		string
	authUser	string
	authPass	string
	csrfToken	string
	csrfFile	string
	apiKey		string
)

// HTTP Debounce
var (
	debounceTimeout = 300*time.Millisecond
	dirVsFiles = 10
)

// Main
var (
	stop = make(chan int)
)

func init() {
	flag.StringVar(&target, "target", "localhost:8080", "Target")
	flag.StringVar(&authUser, "user", "", "Username")
	flag.StringVar(&authPass, "pass", "", "Password")
	flag.StringVar(&csrfFile, "csrf", "", "CSRF token file")
	flag.StringVar(&apiKey, "api", "", "API key")
	flag.Parse()
	if !strings.Contains(target, "://") { target = "http://" + target }
	if len(csrfFile) > 0 {
		fd, err := os.Open(csrfFile)
		if err != nil {
			log.Fatal(err)
		}
		s := bufio.NewScanner(fd)
		for s.Scan() {
			csrfToken = s.Text()
		}
		fd.Close()
	}
}

func main() {

	testWebGuiPost()

	repos := getRepos()
	for i := range repos {
		repo := repos[i]
		repodir := repo.Directory
		repodir = expandTilde(repo.Directory)
		go watchRepo(repo.ID, repodir)
	}

	code := <-stop
	println("Exiting")
	os.Exit(code)

}

func getRepos() []RepositoryConfiguration {
	r, err := http.NewRequest("GET", target+"/rest/config", nil)
	if err != nil {
		log.Fatal(err)
	}
	if len(csrfToken) > 0 {
		r.Header.Set("X-CSRF-Token", csrfToken)
	}
	if len(authUser) > 0 {
		r.SetBasicAuth(authUser, authPass)
	}
	if len(apiKey) > 0 {
		r.Header.Set("X-API-Key", apiKey)
	}
	tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify : true} }
	client := &http.Client{Transport: tr, Timeout: 5*time.Second}
	res, err := client.Do(r)
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		log.Fatalf("Status %d != 200 for GET", res.StatusCode)
	}
	bs, err := ioutil.ReadAll(res.Body)
	if err != nil {
		log.Fatal(err)
	}
	var cfg Configuration
	err = json.Unmarshal(bs, &cfg)
	if err != nil {
		log.Fatal(err)
	}
	return cfg.Repositories
}

func watchRepo(repo string, directory string) {
	expandedDirectory := expandTilde(directory)
	sw, err := NewSyncWatcher()
	if sw == nil || err != nil {
		log.Fatal(err)
	}
	defer sw.Close()
	err = sw.Watch(expandedDirectory)
	if err != nil {
		log.Fatal(err)
	}
	informChangeDebounced := informChangeDebounce(debounceTimeout, repo, directory, dirVsFiles, informChange)
	log.Println("Watching " + repo + ": " + directory)
	for {
		ev := waitForEvent(sw)
		if ev == nil {
			log.Fatal("fsnotify event is nil")
		}
		log.Println("Change detected in " + ev.Name)
		informChangeDebounced(ev.Name)
	}
}

func waitForEvent(sw *SyncWatcher) (ev *fsnotify.FileEvent) {
	var ok bool
	select {
		case ev, ok = <-sw.Event:
			if !ok {
				log.Fatal("Event: channel closed")
			}
		case err, eok := <-sw.Error:
			log.Fatal(err, eok)
	}
	return
}

func testWebGuiPost() {
	r, err := http.NewRequest("POST", target+"/rest/404", nil)
	if err != nil {
		log.Fatal(err)
	}
	if len(csrfToken) > 0 {
		r.Header.Set("X-CSRF-Token", csrfToken)
	}
	if len(authUser) > 0 {
		r.SetBasicAuth(authUser, authPass)
	}
	if len(apiKey) > 0 {
		r.Header.Set("X-API-Key", apiKey)
	}
	tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify : true} }
	client := &http.Client{Transport: tr, Timeout: 5*time.Second}
	res, err := client.Do(r)
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()
	if res.StatusCode != 404 {
		log.Fatalf("Status %d != 404 for POST", res.StatusCode)
	}
}

func informChange(repo string, sub string) {
	data := url.Values {}
	data.Set("repo", repo)
	data.Set("sub", sub)
	r, err := http.NewRequest("POST", target+"/rest/scan?"+data.Encode(), nil)
	if err != nil {
		log.Fatal(err)
	}
	if len(csrfToken) > 0 {
		r.Header.Set("X-CSRF-Token", csrfToken)
	}
	if len(authUser) > 0 {
		r.SetBasicAuth(authUser, authPass)
	}
	if len(apiKey) > 0 {
		r.Header.Set("X-API-Key", apiKey)
	}
	tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify : true} }
	client := &http.Client{Transport: tr, Timeout: 5*time.Second}
	res, err := client.Do(r)
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		log.Fatalf("Status %d != 200 for POST", res.StatusCode)
	} else {
		log.Println("Syncthing is indexing change in " + repo + ": " + sub)
	}
}

func informChangeDebounce(interval time.Duration, repo string, repoDirectory string, dirVsFiles int, callback func(repo string, sub string)) func(string) {
	debounce := func(f func(paths []string)) func(string) {
		timer := &time.Timer{}
		subs := make([]string, 0)
		return func(sub string) {
			timer.Stop()
			subs = append(subs, sub)
			timer = time.AfterFunc(interval, func() {
				f(subs)
				subs = make([]string, 0)
			})
		}
	}

	return debounce(func(paths []string) {
		// Do not inform Syncthing immediately but wait for debounce
		// Therefore, we need to keep track of the paths that were changed
		// This function optimises tracking in two ways:
		//	- If there are more than `dirVsFiles` changes in a directory, we inform Syncthing to scan the entire directory
		//	- Directories with parent directory changes are aggregated. If A/B has 3 changes and A/C has 8, A will have 11 changes and if this is bigger than dirVsFiles we will scan A.
		if (len(paths) == 0) { return }
		trackedPaths := make(map[string]int) // Map directories to scores; if score == -1 the path is a filename
		sort.Strings(paths) // Make sure parent paths are processed first
		for i := range paths {
			path := paths[i]
			dir := filepath.Dir(path)
			score := 1 // File change counts for 1 per directory
			if dir == filepath.Clean(path) {
				score = dirVsFiles // Is directory itself, should definitely inform
			}
			// Search for existing parent directory relations in the map
			for trackedPath, _ := range trackedPaths {
				if strings.Contains(dir, trackedPath) {
					// Increment score of tracked current/parent directory
					trackedPaths[trackedPath] += score
				}
			}
			_, exists := trackedPaths[dir]
			if !exists {
				trackedPaths[dir] = score
			}
			trackedPaths[path] = -1
		}
		var keys []string
		for k := range trackedPaths {
			keys = append(keys, k)
		}
		sort.Strings(keys) // Sort directories before their own files
		previousDone, previousPath := false, ""
		for i := range keys {
			trackedPath := keys[i]
			trackedPathScore, _ := trackedPaths[trackedPath]
			if previousDone && strings.Contains(trackedPath, previousPath) { continue } // Already informed parent directory change
			if trackedPathScore < dirVsFiles && trackedPathScore != -1 { continue } // Not enough files for this directory or it is a file
			previousDone = trackedPathScore != -1
			previousPath = trackedPath
			sub := strings.TrimPrefix(trackedPath, repoDirectory)
			sub = strings.TrimPrefix(sub, string(os.PathSeparator))
			callback(repo, sub)
		}
	})
}

func getHomeDir() string {
	var home string
	switch runtime.GOOS {
		case "windows":
		home = filepath.Join(os.Getenv("HomeDrive"), os.Getenv("HomePath"))
		if home == "" {
			home = os.Getenv("UserProfile")
		}
		default:
			home = os.Getenv("HOME")
	}
	if home == "" {
		log.Fatal("No home directory found - set $HOME (or the platform equivalent).")
	}
	return home
}

func expandTilde(p string) string {
	if p == "~" {
		return getHomeDir()
	}
	p = filepath.FromSlash(p)
	if !strings.HasPrefix(p, fmt.Sprintf("~%c", os.PathSeparator)) {
		return p
	}
	return filepath.Join(getHomeDir(), p[2:])
}
<commit_msg>Do not exit when something goes wrong, worst case we need to wait for the refresh interval till the changes propagate.<commit_after>// syncwatcher.go
package main

import (
	"code.google.com/p/go.exp/fsnotify"
	"os"
	"bufio"
	"io/ioutil"
	"net/http"
	"net/url"
	"crypto/tls"
	"encoding/json"
	"log"
	"fmt"
	"time"
	"flag"
	"runtime"
	"path/filepath"
	"strings"
	"sort"
)

type Configuration struct {
	Version			int
	Repositories	[]RepositoryConfiguration
}

type RepositoryConfiguration struct {
	ID					string
	Directory			string
	ReadOnly			bool
	RescanIntervalS		int
}

// HTTP Authentication
var (
	target		string
	authUser	string
	authPass	string
	csrfToken	string
	csrfFile	string
	apiKey		string
)

// HTTP Debounce
var (
	debounceTimeout = 300*time.Millisecond
	dirVsFiles = 10
)

// Main
var (
	stop = make(chan int)
)

func init() {
	flag.StringVar(&target, "target", "localhost:8080", "Target")
	flag.StringVar(&authUser, "user", "", "Username")
	flag.StringVar(&authPass, "pass", "", "Password")
	flag.StringVar(&csrfFile, "csrf", "", "CSRF token file")
	flag.StringVar(&apiKey, "api", "", "API key")
	flag.Parse()
	if !strings.Contains(target, "://") { target = "http://" + target }
	if len(csrfFile) > 0 {
		fd, err := os.Open(csrfFile)
		if err != nil {
			log.Fatal(err)
		}
		s := bufio.NewScanner(fd)
		for s.Scan() {
			csrfToken = s.Text()
		}
		fd.Close()
	}
}

func main() {

	testWebGuiPost()

	repos := getRepos()
	for i := range repos {
		repo := repos[i]
		repodir := repo.Directory
		repodir = expandTilde(repo.Directory)
		go watchRepo(repo.ID, repodir)
	}

	code := <-stop
	println("Exiting")
	os.Exit(code)

}

func getRepos() []RepositoryConfiguration {
	r, err := http.NewRequest("GET", target+"/rest/config", nil)
	if err != nil {
		log.Fatal(err)
	}
	if len(csrfToken) > 0 {
		r.Header.Set("X-CSRF-Token", csrfToken)
	}
	if len(authUser) > 0 {
		r.SetBasicAuth(authUser, authPass)
	}
	if len(apiKey) > 0 {
		r.Header.Set("X-API-Key", apiKey)
	}
	tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify : true} }
	client := &http.Client{Transport: tr, Timeout: 5*time.Second}
	res, err := client.Do(r)
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		log.Fatalf("Status %d != 200 for GET", res.StatusCode)
	}
	bs, err := ioutil.ReadAll(res.Body)
	if err != nil {
		log.Fatal(err)
	}
	var cfg Configuration
	err = json.Unmarshal(bs, &cfg)
	if err != nil {
		log.Fatal(err)
	}
	return cfg.Repositories
}

func watchRepo(repo string, directory string) {
	expandedDirectory := expandTilde(directory)
	sw, err := NewSyncWatcher()
	if sw == nil || err != nil {
		log.Println(err)
	}
	defer sw.Close()
	err = sw.Watch(expandedDirectory)
	if err != nil {
		log.Println(err)
	}
	informChangeDebounced := informChangeDebounce(debounceTimeout, repo, directory, dirVsFiles, informChange)
	log.Println("Watching " + repo + ": " + directory)
	for {
		ev := waitForEvent(sw)
		if ev == nil {
			log.Println("Error: fsnotify event is nil")
		}
		log.Println("Change detected in " + ev.Name)
		informChangeDebounced(ev.Name)
	}
}

func waitForEvent(sw *SyncWatcher) (ev *fsnotify.FileEvent) {
	var ok bool
	select {
		case ev, ok = <-sw.Event:
			if !ok {
				log.Println("Error: channel closed")
			}
		case err, eok := <-sw.Error:
			log.Println(err, eok)
	}
	return
}

func testWebGuiPost() {
	r, err := http.NewRequest("POST", target+"/rest/404", nil)
	if err != nil {
		log.Fatal(err)
	}
	if len(csrfToken) > 0 {
		r.Header.Set("X-CSRF-Token", csrfToken)
	}
	if len(authUser) > 0 {
		r.SetBasicAuth(authUser, authPass)
	}
	if len(apiKey) > 0 {
		r.Header.Set("X-API-Key", apiKey)
	}
	tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify : true} }
	client := &http.Client{Transport: tr, Timeout: 5*time.Second}
	res, err := client.Do(r)
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()
	if res.StatusCode != 404 {
		log.Fatalf("Status %d != 404 for POST\n", res.StatusCode)
	}
}

func informChange(repo string, sub string) {
	data := url.Values {}
	data.Set("repo", repo)
	data.Set("sub", sub)
	r, err := http.NewRequest("POST", target+"/rest/scan?"+data.Encode(), nil)
	if err != nil {
		log.Println(err)
	}
	if len(csrfToken) > 0 {
		r.Header.Set("X-CSRF-Token", csrfToken)
	}
	if len(authUser) > 0 {
		r.SetBasicAuth(authUser, authPass)
	}
	if len(apiKey) > 0 {
		r.Header.Set("X-API-Key", apiKey)
	}
	tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify : true} }
	client := &http.Client{Transport: tr, Timeout: 5*time.Second}
	res, err := client.Do(r)
	if err != nil {
		log.Println(err)
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		log.Printf("Error: Status %d != 200 for POST.\n" + repo + ": " + sub, res.StatusCode)
	} else {
		log.Println("Syncthing is indexing change in " + repo + ": " + sub)
	}
}

func informChangeDebounce(interval time.Duration, repo string, repoDirectory string, dirVsFiles int, callback func(repo string, sub string)) func(string) {
	debounce := func(f func(paths []string)) func(string) {
		timer := &time.Timer{}
		subs := make([]string, 0)
		return func(sub string) {
			timer.Stop()
			subs = append(subs, sub)
			timer = time.AfterFunc(interval, func() {
				f(subs)
				subs = make([]string, 0)
			})
		}
	}

	return debounce(func(paths []string) {
		// Do not inform Syncthing immediately but wait for debounce
		// Therefore, we need to keep track of the paths that were changed
		// This function optimises tracking in two ways:
		//	- If there are more than `dirVsFiles` changes in a directory, we inform Syncthing to scan the entire directory
		//	- Directories with parent directory changes are aggregated. If A/B has 3 changes and A/C has 8, A will have 11 changes and if this is bigger than dirVsFiles we will scan A.
		if (len(paths) == 0) { return }
		trackedPaths := make(map[string]int) // Map directories to scores; if score == -1 the path is a filename
		sort.Strings(paths) // Make sure parent paths are processed first
		for i := range paths {
			path := paths[i]
			dir := filepath.Dir(path)
			score := 1 // File change counts for 1 per directory
			if dir == filepath.Clean(path) {
				score = dirVsFiles // Is directory itself, should definitely inform
			}
			// Search for existing parent directory relations in the map
			for trackedPath, _ := range trackedPaths {
				if strings.Contains(dir, trackedPath) {
					// Increment score of tracked current/parent directory
					trackedPaths[trackedPath] += score
				}
			}
			_, exists := trackedPaths[dir]
			if !exists {
				trackedPaths[dir] = score
			}
			trackedPaths[path] = -1
		}
		var keys []string
		for k := range trackedPaths {
			keys = append(keys, k)
		}
		sort.Strings(keys) // Sort directories before their own files
		previousDone, previousPath := false, ""
		for i := range keys {
			trackedPath := keys[i]
			trackedPathScore, _ := trackedPaths[trackedPath]
			if previousDone && strings.Contains(trackedPath, previousPath) { continue } // Already informed parent directory change
			if trackedPathScore < dirVsFiles && trackedPathScore != -1 { continue } // Not enough files for this directory or it is a file
			previousDone = trackedPathScore != -1
			previousPath = trackedPath
			sub := strings.TrimPrefix(trackedPath, repoDirectory)
			sub = strings.TrimPrefix(sub, string(os.PathSeparator))
			callback(repo, sub)
		}
	})
}

func getHomeDir() string {
	var home string
	switch runtime.GOOS {
		case "windows":
		home = filepath.Join(os.Getenv("HomeDrive"), os.Getenv("HomePath"))
		if home == "" {
			home = os.Getenv("UserProfile")
		}
		default:
			home = os.Getenv("HOME")
	}
	if home == "" {
		log.Fatal("No home directory found - set $HOME (or the platform equivalent).")
	}
	return home
}

func expandTilde(p string) string {
	if p == "~" {
		return getHomeDir()
	}
	p = filepath.FromSlash(p)
	if !strings.HasPrefix(p, fmt.Sprintf("~%c", os.PathSeparator)) {
		return p
	}
	return filepath.Join(getHomeDir(), p[2:])
}
<|endoftext|>


<commit_before>package main

import "bufio"
import "fmt"
import "crypto/md5"
import "os"
import "regexp"
import "strings"

const version = "0.1.0+Go"

type options struct {
	foreground bool
	background bool
	pattern    string
}

func main() {
	interactLinewise(colorLineMaker(parseArgs(os.Args[1:])))
}

func parseArgs(args []string) options {
	options := options{true, false, ""}
	regexps := make([]string, 0)
	acceptingFlags := true
	for _, arg := range args {
		if acceptingFlags {
			if arg == "-h" || arg == "--help" {
				printUsage()
				os.Exit(0)
			} else if arg == "-v" || arg == "--version" {
				fmt.Printf("Synesthesia version %v\n", version)
				os.Exit(0)
			} else if arg == "-f" || arg == "--foreground" {
				options.foreground = true
			} else if arg == "--no-foreground" {
				options.foreground = false
			} else if arg == "-b" || arg == "--background" {
				options.background = true
			} else if arg == "--no-background" {
				options.background = false
			} else if arg == "--" {
				acceptingFlags = false
			} else if strings.HasPrefix(arg, "-") {
				fmt.Printf("Unrecognized flag: %s\n", arg)
				printUsage()
				os.Exit(1)
			} else {
				regexps = append(regexps, arg)
			}
		} else {
			regexps = append(regexps, arg)
		}
	}

	validateRegexps(regexps)
	for i, pattern := range regexps {
		regexps[i] = fmt.Sprintf("(?:%v)", pattern)
	}
	options.pattern = strings.Join(regexps, "|")
	return options
}

func printUsage() {
	fmt.Fprintf(os.Stderr, `Usage: %v ([flags]|[patterns])... [-- [patterns]...]
Color standard input based on the values matched by the provided regular
expressions.
Before --, any parameters starting with a dash will be interpreted as flags.
After it, all parameters are interpreted as patterns.
A pattern is any valid Go regular expression.
Multiples of flags controlling the same option are allowed; the last flag
specified will take precedence over the ones that come before it.
Flags:
-b --background     Turn on or off background colorization. (default off)
   --no-background
-f --foreground     Turn on or off foreground colorization. (default on)
   --no-foreground
-h --help           Print usage information.
`, os.Args[0])
}

func validateRegexps(regexps []string) {
	valid := true
	for _, pattern := range regexps {
		if _, err := regexp.Compile(pattern); err != nil {
			valid = false
			fmt.Fprintf(os.Stderr, "Invalid regex: %v\n", pattern)
		}
	}
	if !valid {
		os.Exit(1)
	}
}

func interactLinewise(transform func([]byte) []byte) {
	input := bufio.NewReader(os.Stdin)
	line, err := input.ReadBytes('\n')
	for err == nil {
		fmt.Print(string(transform(line)))
		line, err = input.ReadBytes('\n')
	}
	fmt.Print(string(transform(line)))
}

func colorLineMaker(options options) func([]byte) []byte {
	pattern := regexp.MustCompile(options.pattern)
	return func(line []byte) []byte {
		return colorPatternInString(pattern, line, options.foreground, options.background)
	}
}

func colorPatternInString(pattern *regexp.Regexp, buf []byte, foreground, background bool) []byte {
	return pattern.ReplaceAllFunc(buf, colorMatchMaker(foreground, background))
}

func colorMatchMaker(foreground, background bool) func([]byte) []byte {
	return func(match []byte) []byte {
		return color(match, foreground, background)
	}
}

func color(buf []byte, foreground, background bool) []byte {
	sum := md5.Sum(buf)
	r, g, b := rgbFrom216Color(int(sum[md5.Size-1] % 216))
	br, bg, bb := 5-r, 5-g, 5-b
	fore_color, back_color := "", ""
	if foreground {
		fore_color = fmt.Sprintf("\033[38;5;%vm", rgbTo216Color(r, g, b)+16)
	}
	if background {
		back_color = fmt.Sprintf("\033[48;5;%vm", rgbTo216Color(br, bg, bb)+16)
	}
	return []byte(fmt.Sprintf("%v%v%v\033[m", fore_color, back_color, string(buf)))
}

func rgbFrom216Color(color int) (int, int, int) {
	return color / 36, color / 6 % 6, color % 6
}

func rgbTo216Color(r, g, b int) int {
	return r*36 + g*6 + b
}
<commit_msg>Abstract color escape generation; add 16 color output<commit_after>package main

import (
	"bufio"
	"crypto/md5"
	"fmt"
	"os"
	"regexp"
	"strings"
)

const version = "0.1.0+Go"

type ansiColorer func(color int) string

type options struct {
	foreground ansiColorer
	background ansiColorer
	pattern    string
}

type colorMode int

const (
	mode16Color colorMode = iota
	mode256color
)

func main() {
	interactLinewise(colorLineMaker(parseArgs(os.Args[1:])))
}

func parseArgs(args []string) options {
	colorForeground, colorBackground := true, false
	regexps := make([]string, 0)
	colorDepth := mode256color

	acceptingFlags := true
	for _, arg := range args {
		if !acceptingFlags {
			regexps = append(regexps, arg)
			continue
		}

		if arg == "-h" || arg == "--help" {
			printUsage()
			os.Exit(0)
		} else if arg == "-v" || arg == "--version" {
			fmt.Printf("Synesthesia version %v\n", version)
			os.Exit(0)
		} else if arg == "-f" || arg == "--foreground" {
			colorForeground = true
		} else if arg == "--no-foreground" {
			colorForeground = false
		} else if arg == "-b" || arg == "--background" {
			colorBackground = true
		} else if arg == "--no-background" {
			colorBackground = false
		} else if arg == "--16" {
			colorDepth = mode16Color
		} else if arg == "--256" {
			colorDepth = mode256color
		} else if arg == "--" {
			acceptingFlags = false
		} else if strings.HasPrefix(arg, "-") {
			fmt.Printf("Unrecognized flag: %s\n", arg)
			printUsage()
			os.Exit(1)
		} else {
			regexps = append(regexps, arg)
		}
	}

	foregroundColorer, backgroundColorer := colorNoop, colorNoop
	if colorDepth == mode16Color {
		if colorForeground {
			foregroundColorer = colorAnsi16Foreground
		}
		if colorBackground {
			backgroundColorer = colorAnsi16Background
		}
	} else if colorDepth == mode256color {
		if colorForeground {
			foregroundColorer = colorAnsi256Foreground
		}
		if colorBackground {
			backgroundColorer = colorAnsi256Background
		}
	}

	validateRegexps(regexps)
	for i, pattern := range regexps {
		regexps[i] = fmt.Sprintf("(?:%v)", pattern)
	}

	return options{foregroundColorer, backgroundColorer, strings.Join(regexps, "|")}
}

func printUsage() {
	fmt.Fprintf(os.Stderr, `Usage: %v ([flags]|[patterns])... [-- [patterns]...]
Color standard input based on the values matched by the provided regular
expressions.
Before --, any parameters starting with a dash will be interpreted as flags.
After it, all parameters are interpreted as patterns.
A pattern is any valid Go regular expression.
Multiples of flags controlling the same option are allowed; the last flag
specified will take precedence over the ones that come before it.
Flags:
-b --background     Turn on or off background colorization. (default off)
   --no-background
-f --foreground     Turn on or off foreground colorization. (default on)
   --no-foreground
-h --help           Print usage information.
`, os.Args[0])
}

func validateRegexps(regexps []string) {
	valid := true
	for _, pattern := range regexps {
		if _, err := regexp.Compile(pattern); err != nil {
			valid = false
			fmt.Fprintf(os.Stderr, "Invalid regex: %v\n", pattern)
		}
	}
	if !valid {
		os.Exit(1)
	}
}

func interactLinewise(transform func([]byte) []byte) {
	input := bufio.NewReader(os.Stdin)
	line, err := input.ReadBytes('\n')
	for err == nil {
		fmt.Print(string(transform(line)))
		line, err = input.ReadBytes('\n')
	}
	fmt.Print(string(transform(line)))
}

func colorLineMaker(options options) func([]byte) []byte {
	pattern := regexp.MustCompile(options.pattern)
	return func(line []byte) []byte {
		return colorPatternInString(pattern, line, options.foreground, options.background)
	}
}

func colorPatternInString(pattern *regexp.Regexp, buf []byte, foreground, background ansiColorer) []byte {
	return pattern.ReplaceAllFunc(buf, colorMatchMaker(foreground, background))
}

func colorMatchMaker(foreground, background ansiColorer) func([]byte) []byte {
	return func(match []byte) []byte {
		return color(match, foreground, background)
	}
}

func color(buf []byte, foreground, background ansiColorer) []byte {
	sum := md5.Sum(buf)
	color := int(sum[md5.Size-3])<<16 | int(sum[md5.Size-2])<<8 | int(sum[md5.Size-1])
	return []byte(fmt.Sprintf("%v%v%v\033[m", foreground(color), background(color), string(buf)))
}

func colorNoop(color int) string {
	return ""
}

func colorAnsi16Foreground(color int) string {
	color = color % 16
	index := color%8 + 30
	if color < 8 {
		return fmt.Sprintf("\033[%vm", index)
	}
	return fmt.Sprintf("\033[%v;1m", index)
}

func colorAnsi16Background(color int) string {
	color = color % 16
	index := color%8 + 40
	if color < 8 {
		return fmt.Sprintf("\033[%vm", index)
	}
	return fmt.Sprintf("\033[%v;1m", index)
}

func colorAnsi256Foreground(color int) string {
	r, g, b := color>>(16+3), color>>(8+3)&0x1F, color>>3&0x1F
	return fmt.Sprintf("\033[38;5;%vm", rgbTo216Color(r, g, b)+16)
}

func colorAnsi256Background(color int) string {
	r, g, b := color>>(16+3), color>>(8+3)&0x1F, color>>3&0x1F
	return fmt.Sprintf("\033[48;5;%vm", rgbTo216Color(r, g, b)+16)
}

func rgbFrom216Color(color int) (int, int, int) {
	return color / 36, color / 6 % 6, color % 6
}

func rgbTo216Color(r, g, b int) int {
	return r*36 + g*6 + b
}
<|endoftext|>