<commit_before>package ethlog

import (
	"fmt"
	"io/ioutil"
	"math/rand"
	"os"
	"sync"
	"testing"
	"time"
)

type TestLogSystem struct {
	mutex  sync.Mutex
	output string
	level  LogLevel
}

func (ls *TestLogSystem) Println(v ...interface{}) {
	ls.mutex.Lock()
	ls.output += fmt.Sprintln(v...)
	ls.mutex.Unlock()
}

func (ls *TestLogSystem) Printf(format string, v ...interface{}) {
	ls.mutex.Lock()
	ls.output += fmt.Sprintf(format, v...)
	ls.mutex.Unlock()
}

func (ls *TestLogSystem) SetLogLevel(i LogLevel) {
	ls.mutex.Lock()
	ls.level = i
	ls.mutex.Unlock()
}

func (ls *TestLogSystem) GetLogLevel() LogLevel {
	ls.mutex.Lock()
	defer ls.mutex.Unlock()
	return ls.level
}

func (ls *TestLogSystem) CheckOutput(t *testing.T, expected string) {
	ls.mutex.Lock()
	output := ls.output
	ls.mutex.Unlock()
	if output != expected {
		t.Errorf("log output mismatch:\n   got: %q\n  want: %q\n", output, expected)
	}
}

}

func TestLoggerFlush(t *testing.T) {
	Reset()

	logger := NewLogger("TEST")
	testLogSystem := &TestLogSystem{level: WarnLevel}
	AddLogSystem(testLogSystem)
	for i := 0; i < 5; i++ {
		logger.Errorf(".")
	}
	Flush()
	output := testLogSystem.Output
	if output != "[TEST] .[TEST] .[TEST] .[TEST] .[TEST] ." {
		t.Error("Expected complete logger output '[TEST] .[TEST] .[TEST] .[TEST] .[TEST] .', got ", output)
	}
}

func TestLoggerPrintln(t *testing.T) {
	Reset()

	logger := NewLogger("TEST")
	testLogSystem := &TestLogSystem{level: WarnLevel}
	AddLogSystem(testLogSystem)
	logger.Errorln("error")
	logger.Warnln("warn")
	logger.Infoln("info")
	logger.Debugln("debug")
	Flush()

	testLogSystem.CheckOutput(t, "[TEST] error\n[TEST] warn\n")
}

func TestLoggerPrintf(t *testing.T) {
	Reset()

	logger := NewLogger("TEST")
	testLogSystem := &TestLogSystem{level: WarnLevel}
	AddLogSystem(testLogSystem)
	logger.Errorf("error to %v\n", []int{1, 2, 3})
	logger.Warnf("warn")
	logger.Infof("info")
	logger.Debugf("debug")
	Flush()
	testLogSystem.CheckOutput(t, "[TEST] error to [1 2 3]\n[TEST] warn")
}

func TestMultipleLogSystems(t *testing.T) {
	Reset()

	logger := NewLogger("TEST")
	testLogSystem0 := &TestLogSystem{level: ErrorLevel}
	testLogSystem1 := &TestLogSystem{level: WarnLevel}
	AddLogSystem(testLogSystem0)
	AddLogSystem(testLogSystem1)
	logger.Errorln("error")
	logger.Warnln("warn")
	Flush()

	testLogSystem0.CheckOutput(t, "[TEST] error\n")
	testLogSystem1.CheckOutput(t, "[TEST] error\n[TEST] warn\n")
}

func TestFileLogSystem(t *testing.T) {
	Reset()

	logger := NewLogger("TEST")
	filename := "test.log"
	file, _ := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, os.ModePerm)
	testLogSystem := NewStdLogSystem(file, 0, WarnLevel)
	AddLogSystem(testLogSystem)
	logger.Errorf("error to %s\n", filename)
	logger.Warnln("warn")
	Flush()
	contents, _ := ioutil.ReadFile(filename)
	output := string(contents)
	if output != "[TEST] error to test.log\n[TEST] warn\n" {
		t.Error("Expected contents of file 'test.log': '[TEST] error to test.log\\n[TEST] warn\\n', got ", output)
	} else {
		os.Remove(filename)
	}
}

func TestNoLogSystem(t *testing.T) {
	Reset()

	logger := NewLogger("TEST")
	logger.Warnln("warn")
	Flush()
}

func TestConcurrentAddSystem(t *testing.T) {
	rand.Seed(time.Now().Unix())
	Reset()

	logger := NewLogger("TEST")
	stop := make(chan struct{})
	writer := func() {
		select {
		case <-stop:
			return
		default:
			logger.Infoln("foo")
			Flush()
		}
	}

	go writer()
	go writer()

	stopTime := time.Now().Add(100 * time.Millisecond)
	for time.Now().Before(stopTime) {
		time.Sleep(time.Duration(rand.Intn(20)) * time.Millisecond)
		AddLogSystem(NewStdLogSystem(ioutil.Discard, 0, InfoLevel))
	}
	close(stop)
}
<commit_msg>ethlog: verify that Flush is blocking in TestLoggerFlush<commit_after>package ethlog

import (
	"fmt"
	"io/ioutil"
	"math/rand"
	"os"
	"sync"
	"testing"
	"time"
)

type TestLogSystem struct {
	mutex  sync.Mutex
	output string
	level  LogLevel
}

func (ls *TestLogSystem) Println(v ...interface{}) {
	ls.mutex.Lock()
	ls.output += fmt.Sprintln(v...)
	ls.mutex.Unlock()
}

func (ls *TestLogSystem) Printf(format string, v ...interface{}) {
	ls.mutex.Lock()
	ls.output += fmt.Sprintf(format, v...)
	ls.mutex.Unlock()
}

func (ls *TestLogSystem) SetLogLevel(i LogLevel) {
	ls.mutex.Lock()
	ls.level = i
	ls.mutex.Unlock()
}

func (ls *TestLogSystem) GetLogLevel() LogLevel {
	ls.mutex.Lock()
	defer ls.mutex.Unlock()
	return ls.level
}

func (ls *TestLogSystem) CheckOutput(t *testing.T, expected string) {
	ls.mutex.Lock()
	output := ls.output
	ls.mutex.Unlock()
	if output != expected {
		t.Errorf("log output mismatch:\n   got: %q\n  want: %q\n", output, expected)
	}
}

type blockedLogSystem struct {
	LogSystem
	unblock chan struct{}
}

func (ls blockedLogSystem) Println(v ...interface{}) {
	<-ls.unblock
	ls.LogSystem.Println(v...)
}

func (ls blockedLogSystem) Printf(fmt string, v ...interface{}) {
	<-ls.unblock
	ls.LogSystem.Printf(fmt, v...)
}

func TestLoggerFlush(t *testing.T) {
	Reset()

	logger := NewLogger("TEST")
	ls := blockedLogSystem{&TestLogSystem{level: WarnLevel}, make(chan struct{})}
	AddLogSystem(ls)
	for i := 0; i < 5; i++ {
		// these writes shouldn't hang even though ls is blocked
		logger.Errorf(".")
	}

	beforeFlush := time.Now()
	time.AfterFunc(80*time.Millisecond, func() { close(ls.unblock) })
	Flush() // this should hang for approx. 80ms
	if blockd := time.Now().Sub(beforeFlush); blockd < 80*time.Millisecond {
		t.Errorf("Flush didn't block long enough, blocked for %v, should've been >= 80ms", blockd)
	}

	ls.LogSystem.(*TestLogSystem).CheckOutput(t, "[TEST] .[TEST] .[TEST] .[TEST] .[TEST] .")
}

func TestLoggerPrintln(t *testing.T) {
	Reset()

	logger := NewLogger("TEST")
	testLogSystem := &TestLogSystem{level: WarnLevel}
	AddLogSystem(testLogSystem)
	logger.Errorln("error")
	logger.Warnln("warn")
	logger.Infoln("info")
	logger.Debugln("debug")
	Flush()

	testLogSystem.CheckOutput(t, "[TEST] error\n[TEST] warn\n")
}

func TestLoggerPrintf(t *testing.T) {
	Reset()

	logger := NewLogger("TEST")
	testLogSystem := &TestLogSystem{level: WarnLevel}
	AddLogSystem(testLogSystem)
	logger.Errorf("error to %v\n", []int{1, 2, 3})
	logger.Warnf("warn")
	logger.Infof("info")
	logger.Debugf("debug")
	Flush()
	testLogSystem.CheckOutput(t, "[TEST] error to [1 2 3]\n[TEST] warn")
}

func TestMultipleLogSystems(t *testing.T) {
	Reset()

	logger := NewLogger("TEST")
	testLogSystem0 := &TestLogSystem{level: ErrorLevel}
	testLogSystem1 := &TestLogSystem{level: WarnLevel}
	AddLogSystem(testLogSystem0)
	AddLogSystem(testLogSystem1)
	logger.Errorln("error")
	logger.Warnln("warn")
	Flush()

	testLogSystem0.CheckOutput(t, "[TEST] error\n")
	testLogSystem1.CheckOutput(t, "[TEST] error\n[TEST] warn\n")
}

func TestFileLogSystem(t *testing.T) {
	Reset()

	logger := NewLogger("TEST")
	filename := "test.log"
	file, _ := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, os.ModePerm)
	testLogSystem := NewStdLogSystem(file, 0, WarnLevel)
	AddLogSystem(testLogSystem)
	logger.Errorf("error to %s\n", filename)
	logger.Warnln("warn")
	Flush()
	contents, _ := ioutil.ReadFile(filename)
	output := string(contents)
	if output != "[TEST] error to test.log\n[TEST] warn\n" {
		t.Error("Expected contents of file 'test.log': '[TEST] error to test.log\\n[TEST] warn\\n', got ", output)
	} else {
		os.Remove(filename)
	}
}

func TestNoLogSystem(t *testing.T) {
	Reset()

	logger := NewLogger("TEST")
	logger.Warnln("warn")
	Flush()
}

func TestConcurrentAddSystem(t *testing.T) {
	rand.Seed(time.Now().Unix())
	Reset()

	logger := NewLogger("TEST")
	stop := make(chan struct{})
	writer := func() {
		select {
		case <-stop:
			return
		default:
			logger.Infoln("foo")
			Flush()
		}
	}

	go writer()
	go writer()

	stopTime := time.Now().Add(100 * time.Millisecond)
	for time.Now().Before(stopTime) {
		time.Sleep(time.Duration(rand.Intn(20)) * time.Millisecond)
		AddLogSystem(NewStdLogSystem(ioutil.Discard, 0, InfoLevel))
	}
	close(stop)
}
<|endoftext|>


<commit_before>package ethlog

import (
	"fmt"
	"io/ioutil"
	"os"
	"testing"
)

type TestLogSystem struct {
	Output string
	level  LogLevel
}

func (t *TestLogSystem) Println(v ...interface{}) {
	t.Output += fmt.Sprintln(v...)
}

func (t *TestLogSystem) Printf(format string, v ...interface{}) {
	t.Output += fmt.Sprintf(format, v...)
}

func (t *TestLogSystem) SetLogLevel(i LogLevel) {
	t.level = i
}

func (t *TestLogSystem) GetLogLevel() LogLevel {
	return t.level
}

func TestLoggerFlush(t *testing.T) {
	logger := NewLogger("TEST")
	testLogSystem := &TestLogSystem{level: WarnLevel}
	AddLogSystem(testLogSystem)
	for i := 0; i < 5; i++ {
		logger.Errorf(".")
	}
	Flush()
	Reset()
	output := testLogSystem.Output
	if output != "[TEST] .[TEST] .[TEST] .[TEST] .[TEST] ." {
		t.Error("Expected complete logger output '[TEST] .[TEST] .[TEST] .[TEST] .[TEST] .', got ", output)
	}
}

func TestLoggerPrintln(t *testing.T) {
	logger := NewLogger("TEST")
	testLogSystem := &TestLogSystem{level: WarnLevel}
	AddLogSystem(testLogSystem)
	logger.Errorln("error")
	logger.Warnln("warn")
	logger.Infoln("info")
	logger.Debugln("debug")
	Flush()
	Reset()
	output := testLogSystem.Output
	fmt.Println(quote(output))
	if output != "[TEST] error\n[TEST] warn\n" {
		t.Error("Expected logger output '[TEST] error\\n[TEST] warn\\n', got ", output)
	}
}

func TestLoggerPrintf(t *testing.T) {
	logger := NewLogger("TEST")
	testLogSystem := &TestLogSystem{level: WarnLevel}
	AddLogSystem(testLogSystem)
	logger.Errorf("error to %v\n", *testLogSystem)
	logger.Warnf("warn")
	logger.Infof("info")
	logger.Debugf("debug")
	Flush()
	Reset()
	output := testLogSystem.Output
	if output != "[TEST] error to { 2}\n[TEST] warn" {
		t.Error("Expected logger output '[TEST] error to { 2}\\n[TEST] warn', got ", output)
	}
}

func TestMultipleLogSystems(t *testing.T) {
	logger := NewLogger("TEST")
	testLogSystem0 := &TestLogSystem{level: ErrorLevel}
	testLogSystem1 := &TestLogSystem{level: WarnLevel}
	AddLogSystem(testLogSystem0)
	AddLogSystem(testLogSystem1)
	logger.Errorln("error")
	logger.Warnln("warn")
	Flush()
	Reset()
	output0 := testLogSystem0.Output
	output1 := testLogSystem1.Output
	if output0 != "[TEST] error\n" {
		t.Error("Expected logger 0 output '[TEST] error\\n', got ", output0)
	}
	if output1 != "[TEST] error\n[TEST] warn\n" {
		t.Error("Expected logger 1 output '[TEST] error\\n[TEST] warn\\n', got ", output1)
	}
}

func TestFileLogSystem(t *testing.T) {
	logger := NewLogger("TEST")
	filename := "test.log"
	file, _ := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, os.ModePerm)
	testLogSystem := NewStdLogSystem(file, 0, WarnLevel)
	AddLogSystem(testLogSystem)
	logger.Errorf("error to %s\n", filename)
	logger.Warnln("warn")
	Flush()
	contents, _ := ioutil.ReadFile(filename)
	output := string(contents)
	if output != "[TEST] error to test.log\n[TEST] warn\n" {
		t.Error("Expected contents of file 'test.log': '[TEST] error to test.log\\n[TEST] warn\\n', got ", output)
	} else {
		os.Remove(filename)
	}
}

func TestNoLogSystem(t *testing.T) {
	logger := NewLogger("TEST")
	logger.Warnln("warn")
	Flush()
}
<commit_msg>ethlog: fix test compilation error<commit_after>package ethlog

import (
	"fmt"
	"io/ioutil"
	"os"
	"testing"
)

type TestLogSystem struct {
	Output string
	level  LogLevel
}

func (t *TestLogSystem) Println(v ...interface{}) {
	t.Output += fmt.Sprintln(v...)
}

func (t *TestLogSystem) Printf(format string, v ...interface{}) {
	t.Output += fmt.Sprintf(format, v...)
}

func (t *TestLogSystem) SetLogLevel(i LogLevel) {
	t.level = i
}

func (t *TestLogSystem) GetLogLevel() LogLevel {
	return t.level
}

func TestLoggerFlush(t *testing.T) {
	logger := NewLogger("TEST")
	testLogSystem := &TestLogSystem{level: WarnLevel}
	AddLogSystem(testLogSystem)
	for i := 0; i < 5; i++ {
		logger.Errorf(".")
	}
	Flush()
	Reset()
	output := testLogSystem.Output
	if output != "[TEST] .[TEST] .[TEST] .[TEST] .[TEST] ." {
		t.Error("Expected complete logger output '[TEST] .[TEST] .[TEST] .[TEST] .[TEST] .', got ", output)
	}
}

func TestLoggerPrintln(t *testing.T) {
	logger := NewLogger("TEST")
	testLogSystem := &TestLogSystem{level: WarnLevel}
	AddLogSystem(testLogSystem)
	logger.Errorln("error")
	logger.Warnln("warn")
	logger.Infoln("info")
	logger.Debugln("debug")
	Flush()
	Reset()
	output := testLogSystem.Output
	if output != "[TEST] error\n[TEST] warn\n" {
		t.Error("Expected logger output '[TEST] error\\n[TEST] warn\\n', got ", output)
	}
}

func TestLoggerPrintf(t *testing.T) {
	logger := NewLogger("TEST")
	testLogSystem := &TestLogSystem{level: WarnLevel}
	AddLogSystem(testLogSystem)
	logger.Errorf("error to %v\n", *testLogSystem)
	logger.Warnf("warn")
	logger.Infof("info")
	logger.Debugf("debug")
	Flush()
	Reset()
	output := testLogSystem.Output
	if output != "[TEST] error to { 2}\n[TEST] warn" {
		t.Error("Expected logger output '[TEST] error to { 2}\\n[TEST] warn', got ", output)
	}
}

func TestMultipleLogSystems(t *testing.T) {
	logger := NewLogger("TEST")
	testLogSystem0 := &TestLogSystem{level: ErrorLevel}
	testLogSystem1 := &TestLogSystem{level: WarnLevel}
	AddLogSystem(testLogSystem0)
	AddLogSystem(testLogSystem1)
	logger.Errorln("error")
	logger.Warnln("warn")
	Flush()
	Reset()
	output0 := testLogSystem0.Output
	output1 := testLogSystem1.Output
	if output0 != "[TEST] error\n" {
		t.Error("Expected logger 0 output '[TEST] error\\n', got ", output0)
	}
	if output1 != "[TEST] error\n[TEST] warn\n" {
		t.Error("Expected logger 1 output '[TEST] error\\n[TEST] warn\\n', got ", output1)
	}
}

func TestFileLogSystem(t *testing.T) {
	logger := NewLogger("TEST")
	filename := "test.log"
	file, _ := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, os.ModePerm)
	testLogSystem := NewStdLogSystem(file, 0, WarnLevel)
	AddLogSystem(testLogSystem)
	logger.Errorf("error to %s\n", filename)
	logger.Warnln("warn")
	Flush()
	contents, _ := ioutil.ReadFile(filename)
	output := string(contents)
	if output != "[TEST] error to test.log\n[TEST] warn\n" {
		t.Error("Expected contents of file 'test.log': '[TEST] error to test.log\\n[TEST] warn\\n', got ", output)
	} else {
		os.Remove(filename)
	}
}

func TestNoLogSystem(t *testing.T) {
	logger := NewLogger("TEST")
	logger.Warnln("warn")
	Flush()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	fp "path/filepath"
	"strings"
)

type DirStr struct {
	Root      string
	Dirs      []string
	Files     []string
	Images    []string
	AbsDirs   []string
	AbsFiles  []string
	AbsImages []string
}

func NewDirstr(path string) *DirStr {
	fi, err := os.Stat(path)
	if err != nil {
		fmt.Println(err)
		return nil
	}
	if !fi.IsDir() {
		return nil
	} else {
		dir := DirStr{Root: path}
		files, _ := ioutil.ReadDir(path)
		for _, fi := range files {
			absPath := fp.Join(path, fi.Name())
			relPath := fi.Name()
			if fi.IsDir() {
				dir.Dirs = append(dir.Dirs, relPath)
				dir.AbsDirs = append(dir.AbsDirs, absPath)
			} else {
				dir.Files = append(dir.Files, relPath)
				dir.AbsFiles = append(dir.AbsFiles, absPath)
				switch strings.ToLower(fp.Ext(relPath)) {
				case ".jpg", ".png", ".gif":
					dir.Images = append(dir.Images, relPath)
					dir.AbsImages = append(dir.AbsImages, absPath)
				default:
				}
			}
		}
		return &dir
	}
}

func size2text(size int64) string {
	const ratio = 1024
	switch {
	case size < ratio:
		return fmt.Sprintf("%v %v", size, "B")
	case size/ratio < ratio:
		return fmt.Sprintf("%v %v", size/ratio, "KB")
	case size/ratio/ratio < ratio:
		return fmt.Sprintf("%v %v", size/ratio/ratio, "MB")
	case size/ratio/ratio/ratio < ratio:
		return fmt.Sprintf("%v %v", size/ratio/ratio/ratio, "GB")
	default:
		return ""
	}
}

func getSize(path string) int64 {
	fileInfo, err := os.Stat(path)
	if err != nil {
		panic(err)
	}
	return fileInfo.Size()
}

func fileSize(path string) string {
	return size2text(getSize(path))
}

func allFilesSize(files []string) string {
	var total int64
	for _, path := range files {
		total += getSize(path)
	}
	return size2text(total)
}

func dirSize(dir string) string {
	return allFilesSize(NewDirstr(dir).AbsImages)
}
<commit_msg>enhance: float precision<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	fp "path/filepath"
	"strings"
)

type DirStr struct {
	Root      string
	Dirs      []string
	Files     []string
	Images    []string
	AbsDirs   []string
	AbsFiles  []string
	AbsImages []string
}

func NewDirstr(path string) *DirStr {
	fi, err := os.Stat(path)
	if err != nil {
		fmt.Println(err)
		return nil
	}
	if !fi.IsDir() {
		return nil
	} else {
		dir := DirStr{Root: path}
		files, _ := ioutil.ReadDir(path)
		for _, fi := range files {
			absPath := fp.Join(path, fi.Name())
			relPath := fi.Name()
			if fi.IsDir() {
				dir.Dirs = append(dir.Dirs, relPath)
				dir.AbsDirs = append(dir.AbsDirs, absPath)
			} else {
				dir.Files = append(dir.Files, relPath)
				dir.AbsFiles = append(dir.AbsFiles, absPath)
				switch strings.ToLower(fp.Ext(relPath)) {
				case ".jpg", ".png", ".gif":
					dir.Images = append(dir.Images, relPath)
					dir.AbsImages = append(dir.AbsImages, absPath)
				default:
				}
			}
		}
		return &dir
	}
}

func size2text(size int64) string {
	size_float := float64(size)
	const ratio = 1024

	switch {
	case size < ratio:
		return fmt.Sprintf("%.2f %v", size_float, "B")
	case size/ratio < ratio:
		return fmt.Sprintf("%.2f %v", size_float/ratio, "KB")
	case size/ratio/ratio < ratio:
		return fmt.Sprintf("%.2f %v", size_float/ratio/ratio, "MB")
	case size/ratio/ratio/ratio < ratio:
		return fmt.Sprintf("%.2f %v", size_float/ratio/ratio/ratio, "GB")
	default:
		return ""
	}
}

func getSize(path string) int64 {
	fileInfo, err := os.Stat(path)
	if err != nil {
		panic(err)
	}
	return fileInfo.Size()
}

func fileSize(path string) string {
	return size2text(getSize(path))
}

func allFilesSize(files []string) string {
	var total int64
	for _, path := range files {
		total += getSize(path)
	}
	return size2text(total)
}

func dirSize(dir string) string {
	return allFilesSize(NewDirstr(dir).AbsImages)
}
<|endoftext|>


<commit_before>package beater

import (
	"fmt"
	"time"

	"github.com/e-travel/cloudwatchlogsbeat/config"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/client"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
	"github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface"
	"github.com/aws/aws-sdk-go/service/s3"

	"github.com/elastic/beats/libbeat/beat"
	"github.com/elastic/beats/libbeat/common"
	"github.com/elastic/beats/libbeat/logp"
	"github.com/elastic/beats/libbeat/publisher"
)

// Our cloud beat
type Cloudwatchlogsbeat struct {
	// Used to terminate process
	Done chan struct{}

	// Configuration
	Config config.Config

	// Beat publisher client
	Client publisher.Client

	// Beat persistence layer
	Registry Registry

	// Client to amazon cloudwatch logs API
	AWSClient cloudwatchlogsiface.CloudWatchLogsAPI

	// AWS client session
	Session *session.Session

	// the monitoring manager
	Manager *GroupManager
}

// Creates a new cloudwatchlogsbeat
func New(b *beat.Beat, cfg *common.Config) (beat.Beater, error) {
	// Read configuration
	config := config.Config{}
	if err := cfg.Unpack(&config); err != nil {
		return nil, fmt.Errorf("Error reading config file: %v", err)
	}

	// Create AWS session
	if config.AWSRegion == "" {
		config.AWSRegion = "eu-west-1"
	}
	sess := session.Must(session.NewSession(&aws.Config{
		Retryer: client.DefaultRetryer{NumMaxRetries: 10},
		Region:  aws.String(config.AWSRegion),
	}))

	// Create cloudwatch session
	svc := cloudwatchlogs.New(sess)

	// Create beat registry
	registry := NewS3Registry(s3.New(sess), config.S3BucketName)

	// Create instance
	beat := &Cloudwatchlogsbeat{
		Done:      make(chan struct{}),
		Config:    config,
		Session:   sess,
		AWSClient: svc,
		Registry:  registry,
	}

	beat.Manager = NewGroupManager(beat)

	// Validate configuration
	beat.ValidateConfig()

	return beat, nil
}

// Runs continuously our cloud beat
func (beat *Cloudwatchlogsbeat) Run(b *beat.Beat) error {
	logp.Info("cloudwatchlogsbeat is running! Hit CTRL-C to stop it.")

	beat.Client = b.Publisher.Connect()
	ticker := time.NewTicker(beat.Config.GroupRefreshPeriod)

	for {
		select {
		case <-beat.Done:
			return nil
		case <-ticker.C:
		}
		beat.Process()
	}
}

// Stops beat client
func (beat *Cloudwatchlogsbeat) Stop() {
	beat.Client.Close()
	close(beat.Done)
}

// Processes each prospector of our cloud beat
func (beat *Cloudwatchlogsbeat) Process() {
	beat.Manager.Monitor()
}

// Performs basic validation for our configuration, like our
// regular expressions are valid, ...
func (beat *Cloudwatchlogsbeat) ValidateConfig() {
	for _, prospector := range beat.Config.Prospectors {
		ValidateMultiline(prospector.Multiline)
	}
}
<commit_msg>Adds dummy (in-memory) registry<commit_after>package beater

import (
	"fmt"
	"time"

	"github.com/e-travel/cloudwatchlogsbeat/config"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/client"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
	"github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface"
	"github.com/aws/aws-sdk-go/service/s3"

	"github.com/elastic/beats/libbeat/beat"
	"github.com/elastic/beats/libbeat/common"
	"github.com/elastic/beats/libbeat/logp"
	"github.com/elastic/beats/libbeat/publisher"
)

// Our cloud beat
type Cloudwatchlogsbeat struct {
	// Used to terminate process
	Done chan struct{}

	// Configuration
	Config config.Config

	// Beat publisher client
	Client publisher.Client

	// Beat persistence layer
	Registry Registry

	// Client to amazon cloudwatch logs API
	AWSClient cloudwatchlogsiface.CloudWatchLogsAPI

	// AWS client session
	Session *session.Session

	// the monitoring manager
	Manager *GroupManager
}

// Creates a new cloudwatchlogsbeat
func New(b *beat.Beat, cfg *common.Config) (beat.Beater, error) {
	// Read configuration
	config := config.Config{}
	if err := cfg.Unpack(&config); err != nil {
		return nil, fmt.Errorf("Error reading config file: %v", err)
	}

	// Create AWS session
	if config.AWSRegion == "" {
		config.AWSRegion = "eu-west-1"
	}
	sess := session.Must(session.NewSession(&aws.Config{
		Retryer: client.DefaultRetryer{NumMaxRetries: 10},
		Region:  aws.String(config.AWSRegion),
	}))

	// Create cloudwatch session
	svc := cloudwatchlogs.New(sess)
	var registry Registry

	// Create beat registry
	if config.S3BucketName == "" {
		logp.Info("Working with in-memory registry")
		registry = &DummyRegistry{}
	} else {
		logp.Info("Working with s3 registry in bucket %s", config.S3BucketName)
		registry = NewS3Registry(s3.New(sess), config.S3BucketName)
	}

	// Create instance
	beat := &Cloudwatchlogsbeat{
		Done:      make(chan struct{}),
		Config:    config,
		Session:   sess,
		AWSClient: svc,
		Registry:  registry,
	}

	beat.Manager = NewGroupManager(beat)

	// Validate configuration
	beat.ValidateConfig()

	return beat, nil
}

// Runs continuously our cloud beat
func (beat *Cloudwatchlogsbeat) Run(b *beat.Beat) error {
	logp.Info("cloudwatchlogsbeat is running! Hit CTRL-C to stop it.")

	beat.Client = b.Publisher.Connect()
	ticker := time.NewTicker(beat.Config.GroupRefreshPeriod)

	for {
		select {
		case <-beat.Done:
			return nil
		case <-ticker.C:
		}
		beat.Process()
	}
}

// Stops beat client
func (beat *Cloudwatchlogsbeat) Stop() {
	beat.Client.Close()
	close(beat.Done)
}

// Processes each prospector of our cloud beat
func (beat *Cloudwatchlogsbeat) Process() {
	beat.Manager.Monitor()
}

// Performs basic validation for our configuration, like our
// regular expressions are valid, ...
func (beat *Cloudwatchlogsbeat) ValidateConfig() {
	for _, prospector := range beat.Config.Prospectors {
		ValidateMultiline(prospector.Multiline)
	}
}
<|endoftext|>


<commit_before>package beater

import (
	"errors"
	"fmt"
	"time"

	"github.com/e-travel/cloudwatchlogsbeat/config"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/client"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
	"github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface"
	"github.com/aws/aws-sdk-go/service/s3"

	"github.com/elastic/beats/libbeat/beat"
	"github.com/elastic/beats/libbeat/common"
	"github.com/elastic/beats/libbeat/logp"
	"github.com/elastic/beats/libbeat/publisher"
)

// global report variable
var reportFrequency = 5 * time.Minute

// Our cloud beat
type Cloudwatchlogsbeat struct {
	// Used to terminate process
	Done chan struct{}

	// Configuration
	Config config.Config

	// Beat publisher client
	Client publisher.Client

	// Beat persistence layer
	Registry Registry

	// Client to amazon cloudwatch logs API
	AWSClient cloudwatchlogsiface.CloudWatchLogsAPI

	// AWS client session
	Session *session.Session

	// the monitoring manager
	Manager *GroupManager
}

// Creates a new cloudwatchlogsbeat
func New(b *beat.Beat, cfg *common.Config) (beat.Beater, error) {
	// Read configuration
	config := config.Config{}
	if err := cfg.Unpack(&config); err != nil {
		return nil, fmt.Errorf("Error reading config file: %v", err)
	}

	// Update report frequency
	if config.ReportFrequency > 0 {
		reportFrequency = config.ReportFrequency
	}

	// Stop the program if hot stream horizon has been specified in the config file
	// but the hot stream refresh frequency has not (or is zero)
	if config.HotStreamEventHorizon > 0 && config.HotStreamEventRefreshFrequency == 0 {
		Fatal(errors.New(fmt.Sprintf("HotStreamEventHorizon=%d but HotStreamEventRefreshFrequency=%d",
			config.HotStreamEventHorizon, config.HotStreamEventRefreshFrequency)))
	}

	// Create AWS session
	if config.AWSRegion == "" {
		config.AWSRegion = "eu-west-1"
	}
	sess := session.Must(session.NewSession(&aws.Config{
		Retryer: client.DefaultRetryer{NumMaxRetries: 10},
		Region:  aws.String(config.AWSRegion),
	}))

	// Create cloudwatch session
	svc := cloudwatchlogs.New(sess)
	var registry Registry

	// Create beat registry
	if config.S3BucketName == "" {
		logp.Info("Working with in-memory registry")
		registry = NewDummyRegistry()
	} else {
		logp.Info("Working with s3 registry in bucket %s", config.S3BucketName)
		registry = NewS3Registry(s3.New(sess), config.S3BucketName)
	}

	// Create instance
	beat := &Cloudwatchlogsbeat{
		Done:      make(chan struct{}),
		Config:    config,
		Session:   sess,
		AWSClient: svc,
		Registry:  registry,
	}

	beat.Manager = NewGroupManager(beat)

	// Validate configuration
	beat.ValidateConfig()

	return beat, nil
}

// Runs continuously our cloud beat
func (beat *Cloudwatchlogsbeat) Run(b *beat.Beat) error {
	logp.Info("cloudwatchlogsbeat is running! Hit CTRL-C to stop it.")

	beat.Client = b.Publisher.Connect()
	go beat.Manager.Monitor()
	<-beat.Done
	return nil
}

// Stops beat client
func (beat *Cloudwatchlogsbeat) Stop() {
	beat.Client.Close()
	close(beat.Done)
}

// Performs basic validation for our configuration, like our
// regular expressions are valid, ...
func (beat *Cloudwatchlogsbeat) ValidateConfig() {
	for _, prospector := range beat.Config.Prospectors {
		ValidateMultiline(prospector.Multiline)
	}
}
<commit_msg>Adds/improves initial hotstream-related behaviour and logging<commit_after>package beater

import (
	"errors"
	"fmt"
	"os"
	"time"

	"github.com/e-travel/cloudwatchlogsbeat/config"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/client"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
	"github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface"
	"github.com/aws/aws-sdk-go/service/s3"

	"github.com/elastic/beats/libbeat/beat"
	"github.com/elastic/beats/libbeat/common"
	"github.com/elastic/beats/libbeat/logp"
	"github.com/elastic/beats/libbeat/publisher"
)

// global report variable
var reportFrequency = 5 * time.Minute

// Our cloud beat
type Cloudwatchlogsbeat struct {
	// Used to terminate process
	Done chan struct{}

	// Configuration
	Config config.Config

	// Beat publisher client
	Client publisher.Client

	// Beat persistence layer
	Registry Registry

	// Client to amazon cloudwatch logs API
	AWSClient cloudwatchlogsiface.CloudWatchLogsAPI

	// AWS client session
	Session *session.Session

	// the monitoring manager
	Manager *GroupManager
}

// Creates a new cloudwatchlogsbeat
func New(b *beat.Beat, cfg *common.Config) (beat.Beater, error) {
	// Read configuration
	config := config.Config{}
	if err := cfg.Unpack(&config); err != nil {
		return nil, fmt.Errorf("Error reading config file: %v", err)
	}

	// Update report frequency
	if config.ReportFrequency > 0 {
		reportFrequency = config.ReportFrequency
	}

	// Stop the program if hot stream horizon has been specified in the config file
	// but the hot stream refresh frequency has not (or is zero)
	if config.HotStreamEventHorizon > 0 && config.HotStreamEventRefreshFrequency == 0 {
		err := errors.New(
			fmt.Sprintf("HotStreamEventRefreshFrequency can not be zero while HotStreamEventHorizon=%v. Aborting.", config.HotStreamEventHorizon))
		logp.Critical(err.Error())
		os.Exit(1)
	}

	// log the fact that hot streams are activated
	if config.HotStreamEventHorizon > 0 {
		logp.Info(fmt.Sprintf("Hot streams activated with horizon=%v and freq=%v",
			config.HotStreamEventHorizon, config.HotStreamEventRefreshFrequency))
	}

	// Create AWS session
	if config.AWSRegion == "" {
		config.AWSRegion = "eu-west-1"
	}
	sess := session.Must(session.NewSession(&aws.Config{
		Retryer: client.DefaultRetryer{NumMaxRetries: 10},
		Region:  aws.String(config.AWSRegion),
	}))

	// Create cloudwatch session
	svc := cloudwatchlogs.New(sess)
	var registry Registry

	// Create beat registry
	if config.S3BucketName == "" {
		logp.Info("Working with in-memory registry")
		registry = NewDummyRegistry()
	} else {
		logp.Info("Working with s3 registry in bucket %s", config.S3BucketName)
		registry = NewS3Registry(s3.New(sess), config.S3BucketName)
	}

	// Create instance
	beat := &Cloudwatchlogsbeat{
		Done:      make(chan struct{}),
		Config:    config,
		Session:   sess,
		AWSClient: svc,
		Registry:  registry,
	}

	beat.Manager = NewGroupManager(beat)

	// Validate configuration
	beat.ValidateConfig()

	return beat, nil
}

// Runs continuously our cloud beat
func (beat *Cloudwatchlogsbeat) Run(b *beat.Beat) error {
	logp.Info("cloudwatchlogsbeat is running! Hit CTRL-C to stop it.")

	beat.Client = b.Publisher.Connect()
	go beat.Manager.Monitor()
	<-beat.Done
	return nil
}

// Stops beat client
func (beat *Cloudwatchlogsbeat) Stop() {
	beat.Client.Close()
	close(beat.Done)
}

// Performs basic validation for our configuration, like our
// regular expressions are valid, ...
func (beat *Cloudwatchlogsbeat) ValidateConfig() {
	for _, prospector := range beat.Config.Prospectors {
		ValidateMultiline(prospector.Multiline)
	}
}
<|endoftext|>


<commit_before>package eventloop

import (
	"sync"
	"time"

	"github.com/dop251/goja"
	"github.com/dop251/goja_nodejs/console"
	"github.com/dop251/goja_nodejs/require"
)

type job struct {
	cancelled bool
	fn        func()
}

type Timer struct {
	job
	timer *time.Timer
}

type Interval struct {
	job
	ticker   *time.Ticker
	stopChan chan struct{}
}

type EventLoop struct {
	vm       *goja.Runtime
	jobChan  chan func()
	jobCount int32
	canRun   bool

	auxJobs     []func()
	auxJobsLock sync.Mutex
	wakeup      chan struct{}

	stopCond *sync.Cond
	running  bool

	enableConsole bool
}

func NewEventLoop(opts ...Option) *EventLoop {
	vm := goja.New()

	loop := &EventLoop{
		vm:            vm,
		jobChan:       make(chan func()),
		wakeup:        make(chan struct{}, 1),
		stopCond:      sync.NewCond(&sync.Mutex{}),
		enableConsole: true,
	}

	for _, opt := range opts {
		opt(loop)
	}

	new(require.Registry).Enable(vm)
	if loop.enableConsole {
		console.Enable(vm)
	}
	vm.Set("setTimeout", loop.setTimeout)
	vm.Set("setInterval", loop.setInterval)
	vm.Set("clearTimeout", loop.clearTimeout)
	vm.Set("clearInterval", loop.clearInterval)

	return loop
}

type Option func(*EventLoop)

// EnableConsole controls whether the "console" module is loaded into
// the runtime used by the loop.  By default, loops are created with
// the "console" module loaded, pass EnableConsole(false) to
// NewEventLoop to disable this behavior.
func EnableConsole(enableConsole bool) Option {
	return func(loop *EventLoop) {
		loop.enableConsole = enableConsole
	}
}

func (loop *EventLoop) schedule(call goja.FunctionCall, repeating bool) goja.Value {
	if fn, ok := goja.AssertFunction(call.Argument(0)); ok {
		delay := call.Argument(1).ToInteger()
		var args []goja.Value
		if len(call.Arguments) > 2 {
			args = call.Arguments[2:]
		}
		f := func() { fn(nil, args...) }
		loop.jobCount++
		if repeating {
			return loop.vm.ToValue(loop.addInterval(f, time.Duration(delay)*time.Millisecond))
		} else {
			return loop.vm.ToValue(loop.addTimeout(f, time.Duration(delay)*time.Millisecond))
		}
	}
	return nil
}

func (loop *EventLoop) setTimeout(call goja.FunctionCall) goja.Value {
	return loop.schedule(call, false)
}

func (loop *EventLoop) setInterval(call goja.FunctionCall) goja.Value {
	return loop.schedule(call, true)
}

// SetTimeout schedules to run the specified function in the context
// of the loop as soon as possible after the specified timeout period.
// SetTimeout returns a Timer which can be passed to ClearTimeout.
// The instance of goja.Runtime that is passed to the function and any Values derived
// from it must not be used outside of the function. SetTimeout is
// safe to call inside or outside of the loop.
func (loop *EventLoop) SetTimeout(fn func(*goja.Runtime), timeout time.Duration) *Timer {
	t := loop.addTimeout(func() { fn(loop.vm) }, timeout)
	loop.addAuxJob(func() {
		loop.jobCount++
	})
	return t
}

// ClearTimeout cancels a Timer returned by SetTimeout if it has not run yet.
// ClearTimeout is safe to call inside or outside of the loop.
func (loop *EventLoop) ClearTimeout(t *Timer) {
	loop.addAuxJob(func() {
		loop.clearTimeout(t)
	})
}

// SetInterval schedules to repeatedly run the specified function in
// the context of the loop as soon as possible after every specified
// timeout period.  SetInterval returns an Interval which can be
// passed to ClearInterval. The instance of goja.Runtime that is passed to the
// function and any Values derived from it must not be used outside of
// the function. SetInterval is safe to call inside or outside of the
// loop.
func (loop *EventLoop) SetInterval(fn func(*goja.Runtime), timeout time.Duration) *Interval {
	i := loop.addInterval(func() { fn(loop.vm) }, timeout)
	loop.addAuxJob(func() {
		loop.jobCount++
	})
	return i
}

// ClearInterval cancels an Interval returned by SetInterval.
// ClearInterval is safe to call inside or outside of the loop.
func (loop *EventLoop) ClearInterval(i *Interval) {
	loop.addAuxJob(func() {
		loop.clearInterval(i)
	})
}

func (loop *EventLoop) setRunning() {
	loop.stopCond.L.Lock()
	if loop.running {
		panic("Loop is already started")
	}
	loop.running = true
	loop.stopCond.L.Unlock()
}

// Run calls the specified function, starts the event loop and waits until there are no more delayed jobs to run
// after which it stops the loop and returns.
// The instance of goja.Runtime that is passed to the function and any Values derived from it must not be used outside
// of the function.
// Do NOT use this function while the loop is already running. Use RunOnLoop() instead.
// If the loop is already started it will panic.
func (loop *EventLoop) Run(fn func(*goja.Runtime)) {
	loop.setRunning()
	fn(loop.vm)
	loop.run(false)
}

// Start the event loop in the background. The loop continues to run until Stop() is called.
// If the loop is already started it will panic.
func (loop *EventLoop) Start() {
	loop.setRunning()
	go loop.run(true)
}

// Stop the loop that was started with Start(). After this function returns there will be no more jobs executed
// by the loop. It is possible to call Start() or Run() again after this to resume the execution.
// Note, it does not cancel active timeouts.
// It is not allowed to run Start() and Stop() concurrently.
// Calling Stop() on an already stopped loop or inside the loop will hang.
func (loop *EventLoop) Stop() {
	loop.jobChan <- func() {
		loop.canRun = false
	}

	loop.stopCond.L.Lock()
	for loop.running {
		loop.stopCond.Wait()
	}
	loop.stopCond.L.Unlock()
}

// RunOnLoop schedules to run the specified function in the context of the loop as soon as possible.
// The order of the runs is preserved (i.e. the functions will be called in the same order as calls to RunOnLoop())
// The instance of goja.Runtime that is passed to the function and any Values derived from it must not be used outside
// of the function. It is safe to call inside or outside of the loop.
func (loop *EventLoop) RunOnLoop(fn func(*goja.Runtime)) {
	loop.addAuxJob(func() { fn(loop.vm) })
}

func (loop *EventLoop) runAux() {
	loop.auxJobsLock.Lock()
	jobs := loop.auxJobs
	loop.auxJobs = nil
	loop.auxJobsLock.Unlock()
	for _, job := range jobs {
		job()
	}
}

func (loop *EventLoop) run(inBackground bool) {
	loop.canRun = true
	loop.runAux()

	for loop.canRun && (inBackground || loop.jobCount > 0) {
		select {
		case job := <-loop.jobChan:
			job()
			if loop.canRun {
				select {
				case <-loop.wakeup:
					loop.runAux()
				default:
				}
			}
		case <-loop.wakeup:
			loop.runAux()
		}
	}
	loop.stopCond.L.Lock()
	loop.running = false
	loop.stopCond.L.Unlock()
	loop.stopCond.Broadcast()
}

func (loop *EventLoop) addAuxJob(fn func()) {
	loop.auxJobsLock.Lock()
	loop.auxJobs = append(loop.auxJobs, fn)
	loop.auxJobsLock.Unlock()
	select {
	case loop.wakeup <- struct{}{}:
	default:
	}
}

func (loop *EventLoop) addTimeout(f func(), timeout time.Duration) *Timer {
	t := &Timer{
		job: job{fn: f},
	}
	t.timer = time.AfterFunc(timeout, func() {
		loop.jobChan <- func() {
			loop.doTimeout(t)
		}
	})

	return t
}

func (loop *EventLoop) addInterval(f func(), timeout time.Duration) *Interval {
	i := &Interval{
		job:      job{fn: f},
		ticker:   time.NewTicker(timeout),
		stopChan: make(chan struct{}),
	}

	go i.run(loop)
	return i
}

func (loop *EventLoop) doTimeout(t *Timer) {
	if !t.cancelled {
		t.fn()
		t.cancelled = true
		loop.jobCount--
	}
}

func (loop *EventLoop) doInterval(i *Interval) {
	if !i.cancelled {
		i.fn()
	}
}

func (loop *EventLoop) clearTimeout(t *Timer) {
	if !t.cancelled {
		t.timer.Stop()
		t.cancelled = true
		loop.jobCount--
	}
}

func (loop *EventLoop) clearInterval(i *Interval) {
	if !i.cancelled {
		i.cancelled = true
		close(i.stopChan)
		loop.jobCount--
	}
}

func (i *Interval) run(loop *EventLoop) {
L:
	for {
		select {
		case <-i.stopChan:
			i.ticker.Stop()
			break L
		case <-i.ticker.C:
			loop.jobChan <- func() {
				loop.doInterval(i)
			}
		}
	}
}
<commit_msg>Add nil checks for clearTimeout and clearInterval.<commit_after>package eventloop

import (
	"sync"
	"time"

	"github.com/dop251/goja"
	"github.com/dop251/goja_nodejs/console"
	"github.com/dop251/goja_nodejs/require"
)

type job struct {
	cancelled bool
	fn        func()
}

type Timer struct {
	job
	timer *time.Timer
}

type Interval struct {
	job
	ticker   *time.Ticker
	stopChan chan struct{}
}

type EventLoop struct {
	vm       *goja.Runtime
	jobChan  chan func()
	jobCount int32
	canRun   bool

	auxJobs     []func()
	auxJobsLock sync.Mutex
	wakeup      chan struct{}

	stopCond *sync.Cond
	running  bool

	enableConsole bool
}

func NewEventLoop(opts ...Option) *EventLoop {
	vm := goja.New()

	loop := &EventLoop{
		vm:            vm,
		jobChan:       make(chan func()),
		wakeup:        make(chan struct{}, 1),
		stopCond:      sync.NewCond(&sync.Mutex{}),
		enableConsole: true,
	}

	for _, opt := range opts {
		opt(loop)
	}

	new(require.Registry).Enable(vm)
	if loop.enableConsole {
		console.Enable(vm)
	}
	vm.Set("setTimeout", loop.setTimeout)
	vm.Set("setInterval", loop.setInterval)
	vm.Set("clearTimeout", loop.clearTimeout)
	vm.Set("clearInterval", loop.clearInterval)

	return loop
}

type Option func(*EventLoop)

// EnableConsole controls whether the "console" module is loaded into
// the runtime used by the loop.  By default, loops are created with
// the "console" module loaded, pass EnableConsole(false) to
// NewEventLoop to disable this behavior.
func EnableConsole(enableConsole bool) Option {
	return func(loop *EventLoop) {
		loop.enableConsole = enableConsole
	}
}

func (loop *EventLoop) schedule(call goja.FunctionCall, repeating bool) goja.Value {
	if fn, ok := goja.AssertFunction(call.Argument(0)); ok {
		delay := call.Argument(1).ToInteger()
		var args []goja.Value
		if len(call.Arguments) > 2 {
			args = call.Arguments[2:]
		}
		f := func() { fn(nil, args...) }
		loop.jobCount++
		if repeating {
			return loop.vm.ToValue(loop.addInterval(f, time.Duration(delay)*time.Millisecond))
		} else {
			return loop.vm.ToValue(loop.addTimeout(f, time.Duration(delay)*time.Millisecond))
		}
	}
	return nil
}

func (loop *EventLoop) setTimeout(call goja.FunctionCall) goja.Value {
	return loop.schedule(call, false)
}

func (loop *EventLoop) setInterval(call goja.FunctionCall) goja.Value {
	return loop.schedule(call, true)
}

// SetTimeout schedules to run the specified function in the context
// of the loop as soon as possible after the specified timeout period.
// SetTimeout returns a Timer which can be passed to ClearTimeout.
// The instance of goja.Runtime that is passed to the function and any Values derived
// from it must not be used outside of the function. SetTimeout is
// safe to call inside or outside of the loop.
func (loop *EventLoop) SetTimeout(fn func(*goja.Runtime), timeout time.Duration) *Timer {
	t := loop.addTimeout(func() { fn(loop.vm) }, timeout)
	loop.addAuxJob(func() {
		loop.jobCount++
	})
	return t
}

// ClearTimeout cancels a Timer returned by SetTimeout if it has not run yet.
// ClearTimeout is safe to call inside or outside of the loop.
func (loop *EventLoop) ClearTimeout(t *Timer) {
	loop.addAuxJob(func() {
		loop.clearTimeout(t)
	})
}

// SetInterval schedules to repeatedly run the specified function in
// the context of the loop as soon as possible after every specified
// timeout period.  SetInterval returns an Interval which can be
// passed to ClearInterval. The instance of goja.Runtime that is passed to the
// function and any Values derived from it must not be used outside of
// the function. SetInterval is safe to call inside or outside of the
// loop.
func (loop *EventLoop) SetInterval(fn func(*goja.Runtime), timeout time.Duration) *Interval {
	i := loop.addInterval(func() { fn(loop.vm) }, timeout)
	loop.addAuxJob(func() {
		loop.jobCount++
	})
	return i
}

// ClearInterval cancels an Interval returned by SetInterval.
// ClearInterval is safe to call inside or outside of the loop.
func (loop *EventLoop) ClearInterval(i *Interval) {
	loop.addAuxJob(func() {
		loop.clearInterval(i)
	})
}

func (loop *EventLoop) setRunning() {
	loop.stopCond.L.Lock()
	if loop.running {
		panic("Loop is already started")
	}
	loop.running = true
	loop.stopCond.L.Unlock()
}

// Run calls the specified function, starts the event loop and waits until there are no more delayed jobs to run
// after which it stops the loop and returns.
// The instance of goja.Runtime that is passed to the function and any Values derived from it must not be used outside
// of the function.
// Do NOT use this function while the loop is already running. Use RunOnLoop() instead.
// If the loop is already started it will panic.
func (loop *EventLoop) Run(fn func(*goja.Runtime)) {
	loop.setRunning()
	fn(loop.vm)
	loop.run(false)
}

// Start the event loop in the background. The loop continues to run until Stop() is called.
// If the loop is already started it will panic.
func (loop *EventLoop) Start() {
	loop.setRunning()
	go loop.run(true)
}

// Stop the loop that was started with Start(). After this function returns there will be no more jobs executed
// by the loop. It is possible to call Start() or Run() again after this to resume the execution.
// Note, it does not cancel active timeouts.
// It is not allowed to run Start() and Stop() concurrently.
// Calling Stop() on an already stopped loop or inside the loop will hang.
func (loop *EventLoop) Stop() {
	loop.jobChan <- func() {
		loop.canRun = false
	}

	loop.stopCond.L.Lock()
	for loop.running {
		loop.stopCond.Wait()
	}
	loop.stopCond.L.Unlock()
}

// RunOnLoop schedules to run the specified function in the context of the loop as soon as possible.
// The order of the runs is preserved (i.e. the functions will be called in the same order as calls to RunOnLoop())
// The instance of goja.Runtime that is passed to the function and any Values derived from it must not be used outside
// of the function. It is safe to call inside or outside of the loop.
func (loop *EventLoop) RunOnLoop(fn func(*goja.Runtime)) {
	loop.addAuxJob(func() { fn(loop.vm) })
}

func (loop *EventLoop) runAux() {
	loop.auxJobsLock.Lock()
	jobs := loop.auxJobs
	loop.auxJobs = nil
	loop.auxJobsLock.Unlock()
	for _, job := range jobs {
		job()
	}
}

func (loop *EventLoop) run(inBackground bool) {
	loop.canRun = true
	loop.runAux()

	for loop.canRun && (inBackground || loop.jobCount > 0) {
		select {
		case job := <-loop.jobChan:
			job()
			if loop.canRun {
				select {
				case <-loop.wakeup:
					loop.runAux()
				default:
				}
			}
		case <-loop.wakeup:
			loop.runAux()
		}
	}
	loop.stopCond.L.Lock()
	loop.running = false
	loop.stopCond.L.Unlock()
	loop.stopCond.Broadcast()
}

func (loop *EventLoop) addAuxJob(fn func()) {
	loop.auxJobsLock.Lock()
	loop.auxJobs = append(loop.auxJobs, fn)
	loop.auxJobsLock.Unlock()
	select {
	case loop.wakeup <- struct{}{}:
	default:
	}
}

func (loop *EventLoop) addTimeout(f func(), timeout time.Duration) *Timer {
	t := &Timer{
		job: job{fn: f},
	}
	t.timer = time.AfterFunc(timeout, func() {
		loop.jobChan <- func() {
			loop.doTimeout(t)
		}
	})

	return t
}

func (loop *EventLoop) addInterval(f func(), timeout time.Duration) *Interval {
	i := &Interval{
		job:      job{fn: f},
		ticker:   time.NewTicker(timeout),
		stopChan: make(chan struct{}),
	}

	go i.run(loop)
	return i
}

func (loop *EventLoop) doTimeout(t *Timer) {
	if !t.cancelled {
		t.fn()
		t.cancelled = true
		loop.jobCount--
	}
}

func (loop *EventLoop) doInterval(i *Interval) {
	if !i.cancelled {
		i.fn()
	}
}

func (loop *EventLoop) clearTimeout(t *Timer) {
	if t != nil && !t.cancelled {
		t.timer.Stop()
		t.cancelled = true
		loop.jobCount--
	}
}

func (loop *EventLoop) clearInterval(i *Interval) {
	if i != nil && !i.cancelled {
		i.cancelled = true
		close(i.stopChan)
		loop.jobCount--
	}
}

func (i *Interval) run(loop *EventLoop) {
L:
	for {
		select {
		case <-i.stopChan:
			i.ticker.Stop()
			break L
		case <-i.ticker.C:
			loop.jobChan <- func() {
				loop.doInterval(i)
			}
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

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 e2e

import (
	"fmt"
	"io/ioutil"
	"net"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"time"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/util"

	. "github.com/onsi/ginkgo"
)

const (
	podName = "pfpod"
)

// TODO support other ports besides 80
var portForwardRegexp = regexp.MustCompile("Forwarding from 127.0.0.1:([0-9]+) -> 80")

func pfPod(expectedClientData, chunks, chunkSize, chunkIntervalMillis string) *api.Pod {
	return &api.Pod{
		ObjectMeta: api.ObjectMeta{
			Name:   podName,
			Labels: map[string]string{"name": podName},
		},
		Spec: api.PodSpec{
			Containers: []api.Container{
				{
					Name:  "portforwardtester",
					Image: "gcr.io/google_containers/portforwardtester:1.0",
					Env: []api.EnvVar{
						{
							Name:  "BIND_PORT",
							Value: "80",
						},
						{
							Name:  "EXPECTED_CLIENT_DATA",
							Value: expectedClientData,
						},
						{
							Name:  "CHUNKS",
							Value: chunks,
						},
						{
							Name:  "CHUNK_SIZE",
							Value: chunkSize,
						},
						{
							Name:  "CHUNK_INTERVAL",
							Value: chunkIntervalMillis,
						},
					},
				},
			},
			RestartPolicy: api.RestartPolicyNever,
		},
	}
}

func runPortForward(ns, podName string, port int) (*exec.Cmd, int) {
	cmd := kubectlCmd("port-forward", fmt.Sprintf("--namespace=%v", ns), podName, fmt.Sprintf(":%d", port))
	// This is somewhat ugly but is the only way to retrieve the port that was picked
	// by the port-forward command. We don't want to hard code the port as we have no
	// way of guaranteeing we can pick one that isn't in use, particularly on Jenkins.
	Logf("starting port-forward command and streaming output")
	stdout, stderr, err := startCmdAndStreamOutput(cmd)
	if err != nil {
		Failf("Failed to start port-forward command: %v", err)
	}
	defer stdout.Close()
	defer stderr.Close()

	buf := make([]byte, 128)
	var n int
	Logf("reading from `kubectl port-forward` command's stderr")
	if n, err = stderr.Read(buf); err != nil {
		Failf("Failed to read from kubectl port-forward stderr: %v", err)
	}
	portForwardOutput := string(buf[:n])
	match := portForwardRegexp.FindStringSubmatch(portForwardOutput)
	if len(match) != 2 {
		Failf("Failed to parse kubectl port-forward output: %s", portForwardOutput)
	}

	listenPort, err := strconv.Atoi(match[1])
	if err != nil {
		Failf("Error converting %s to an int: %v", match[1], err)
	}

	return cmd, listenPort
}

func runKubectlWithTimeout(timeout time.Duration, args ...string) string {
	logOutput := make(chan string)
	go func() {
		logOutput <- runKubectl(args...)
	}()
	select {
	case <-time.After(timeout):
		Failf("kubectl timed out")
		return ""
	case o := <-logOutput:
		return o
	}
}

var _ = Describe("Port forwarding", func() {
	framework := NewFramework("port-forwarding")

	Describe("With a server that expects a client request", func() {
		It("should support a client that connects, sends no data, and disconnects [Conformance]", func() {
			By("creating the target pod")
			pod := pfPod("abc", "1", "1", "1")
			framework.Client.Pods(framework.Namespace.Name).Create(pod)
			framework.WaitForPodRunning(pod.Name)

			By("Running 'kubectl port-forward'")
			cmd, listenPort := runPortForward(framework.Namespace.Name, pod.Name, 80)
			defer tryKill(cmd)

			By("Dialing the local port")
			conn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", listenPort))
			if err != nil {
				Failf("Couldn't connect to port %d: %v", listenPort, err)
			}

			By("Closing the connection to the local port")
			conn.Close()

			logOutput := runKubectlWithTimeout(util.ForeverTestTimeout, "logs", fmt.Sprintf("--namespace=%v", framework.Namespace.Name), "-f", podName)
			verifyLogMessage(logOutput, "Accepted client connection")
			verifyLogMessage(logOutput, "Expected to read 3 bytes from client, but got 0 instead")
		})

		It("should support a client that connects, sends data, and disconnects [Conformance]", func() {
			By("creating the target pod")
			pod := pfPod("abc", "10", "10", "100")
			framework.Client.Pods(framework.Namespace.Name).Create(pod)
			framework.WaitForPodRunning(pod.Name)

			By("Running 'kubectl port-forward'")
			cmd, listenPort := runPortForward(framework.Namespace.Name, pod.Name, 80)
			defer tryKill(cmd)

			By("Dialing the local port")
			addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("127.0.0.1:%d", listenPort))
			if err != nil {
				Failf("Error resolving tcp addr: %v", err)
			}
			conn, err := net.DialTCP("tcp", nil, addr)
			if err != nil {
				Failf("Couldn't connect to port %d: %v", listenPort, err)
			}
			defer func() {
				By("Closing the connection to the local port")
				conn.Close()
			}()

			By("Sending the expected data to the local port")
			fmt.Fprint(conn, "abc")

			By("Closing the write half of the client's connection")
			conn.CloseWrite()

			By("Reading data from the local port")
			fromServer, err := ioutil.ReadAll(conn)
			if err != nil {
				Failf("Unexpected error reading data from the server: %v", err)
			}

			if e, a := strings.Repeat("x", 100), string(fromServer); e != a {
				Failf("Expected %q from server, got %q", e, a)
			}

			logOutput := runKubectlWithTimeout(util.ForeverTestTimeout, "logs", fmt.Sprintf("--namespace=%v", framework.Namespace.Name), "-f", podName)
			verifyLogMessage(logOutput, "^Accepted client connection$")
			verifyLogMessage(logOutput, "^Received expected client data$")
			verifyLogMessage(logOutput, "^Done$")
		})
	})
	Describe("With a server that expects no client request", func() {
		It("should support a client that connects, sends no data, and disconnects [Conformance]", func() {
			By("creating the target pod")
			pod := pfPod("", "10", "10", "100")
			framework.Client.Pods(framework.Namespace.Name).Create(pod)
			framework.WaitForPodRunning(pod.Name)

			By("Running 'kubectl port-forward'")
			cmd, listenPort := runPortForward(framework.Namespace.Name, pod.Name, 80)
			defer tryKill(cmd)

			By("Dialing the local port")
			conn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", listenPort))
			if err != nil {
				Failf("Couldn't connect to port %d: %v", listenPort, err)
			}
			defer func() {
				By("Closing the connection to the local port")
				conn.Close()
			}()

			By("Reading data from the local port")
			fromServer, err := ioutil.ReadAll(conn)
			if err != nil {
				Failf("Unexpected error reading data from the server: %v", err)
			}

			if e, a := strings.Repeat("x", 100), string(fromServer); e != a {
				Failf("Expected %q from server, got %q", e, a)
			}

			logOutput := runKubectlWithTimeout(util.ForeverTestTimeout, "logs", fmt.Sprintf("--namespace=%v", framework.Namespace.Name), "-f", podName)
			verifyLogMessage(logOutput, "Accepted client connection")
			verifyLogMessage(logOutput, "Done")
		})
	})
})

func verifyLogMessage(log, expected string) {
	re := regexp.MustCompile(expected)
	lines := strings.Split(log, "\n")
	for i := range lines {
		if re.MatchString(lines[i]) {
			return
		}
	}
	Failf("Missing %q from log: %s", expected, log)
}
<commit_msg>Defer GinkgoRecover call in runKubectlWithTimeout to handle panic<commit_after>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

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 e2e

import (
	"fmt"
	"io/ioutil"
	"net"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
	"time"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/util"

	. "github.com/onsi/ginkgo"
)

const (
	podName = "pfpod"
)

// TODO support other ports besides 80
var portForwardRegexp = regexp.MustCompile("Forwarding from 127.0.0.1:([0-9]+) -> 80")

func pfPod(expectedClientData, chunks, chunkSize, chunkIntervalMillis string) *api.Pod {
	return &api.Pod{
		ObjectMeta: api.ObjectMeta{
			Name:   podName,
			Labels: map[string]string{"name": podName},
		},
		Spec: api.PodSpec{
			Containers: []api.Container{
				{
					Name:  "portforwardtester",
					Image: "gcr.io/google_containers/portforwardtester:1.0",
					Env: []api.EnvVar{
						{
							Name:  "BIND_PORT",
							Value: "80",
						},
						{
							Name:  "EXPECTED_CLIENT_DATA",
							Value: expectedClientData,
						},
						{
							Name:  "CHUNKS",
							Value: chunks,
						},
						{
							Name:  "CHUNK_SIZE",
							Value: chunkSize,
						},
						{
							Name:  "CHUNK_INTERVAL",
							Value: chunkIntervalMillis,
						},
					},
				},
			},
			RestartPolicy: api.RestartPolicyNever,
		},
	}
}

func runPortForward(ns, podName string, port int) (*exec.Cmd, int) {
	cmd := kubectlCmd("port-forward", fmt.Sprintf("--namespace=%v", ns), podName, fmt.Sprintf(":%d", port))
	// This is somewhat ugly but is the only way to retrieve the port that was picked
	// by the port-forward command. We don't want to hard code the port as we have no
	// way of guaranteeing we can pick one that isn't in use, particularly on Jenkins.
	Logf("starting port-forward command and streaming output")
	stdout, stderr, err := startCmdAndStreamOutput(cmd)
	if err != nil {
		Failf("Failed to start port-forward command: %v", err)
	}
	defer stdout.Close()
	defer stderr.Close()

	buf := make([]byte, 128)
	var n int
	Logf("reading from `kubectl port-forward` command's stderr")
	if n, err = stderr.Read(buf); err != nil {
		Failf("Failed to read from kubectl port-forward stderr: %v", err)
	}
	portForwardOutput := string(buf[:n])
	match := portForwardRegexp.FindStringSubmatch(portForwardOutput)
	if len(match) != 2 {
		Failf("Failed to parse kubectl port-forward output: %s", portForwardOutput)
	}

	listenPort, err := strconv.Atoi(match[1])
	if err != nil {
		Failf("Error converting %s to an int: %v", match[1], err)
	}

	return cmd, listenPort
}

func runKubectlWithTimeout(timeout time.Duration, args ...string) string {
	logOutput := make(chan string)
	go func() {
		defer GinkgoRecover()
		logOutput <- runKubectl(args...)
	}()
	select {
	case <-time.After(timeout):
		Failf("kubectl timed out")
		return ""
	case o := <-logOutput:
		return o
	}
}

var _ = Describe("Port forwarding", func() {
	framework := NewFramework("port-forwarding")

	Describe("With a server that expects a client request", func() {
		It("should support a client that connects, sends no data, and disconnects [Conformance]", func() {
			By("creating the target pod")
			pod := pfPod("abc", "1", "1", "1")
			framework.Client.Pods(framework.Namespace.Name).Create(pod)
			framework.WaitForPodRunning(pod.Name)

			By("Running 'kubectl port-forward'")
			cmd, listenPort := runPortForward(framework.Namespace.Name, pod.Name, 80)
			defer tryKill(cmd)

			By("Dialing the local port")
			conn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", listenPort))
			if err != nil {
				Failf("Couldn't connect to port %d: %v", listenPort, err)
			}

			By("Closing the connection to the local port")
			conn.Close()

			logOutput := runKubectlWithTimeout(util.ForeverTestTimeout, "logs", fmt.Sprintf("--namespace=%v", framework.Namespace.Name), "-f", podName)
			verifyLogMessage(logOutput, "Accepted client connection")
			verifyLogMessage(logOutput, "Expected to read 3 bytes from client, but got 0 instead")
		})

		It("should support a client that connects, sends data, and disconnects [Conformance]", func() {
			By("creating the target pod")
			pod := pfPod("abc", "10", "10", "100")
			framework.Client.Pods(framework.Namespace.Name).Create(pod)
			framework.WaitForPodRunning(pod.Name)

			By("Running 'kubectl port-forward'")
			cmd, listenPort := runPortForward(framework.Namespace.Name, pod.Name, 80)
			defer tryKill(cmd)

			By("Dialing the local port")
			addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("127.0.0.1:%d", listenPort))
			if err != nil {
				Failf("Error resolving tcp addr: %v", err)
			}
			conn, err := net.DialTCP("tcp", nil, addr)
			if err != nil {
				Failf("Couldn't connect to port %d: %v", listenPort, err)
			}
			defer func() {
				By("Closing the connection to the local port")
				conn.Close()
			}()

			By("Sending the expected data to the local port")
			fmt.Fprint(conn, "abc")

			By("Closing the write half of the client's connection")
			conn.CloseWrite()

			By("Reading data from the local port")
			fromServer, err := ioutil.ReadAll(conn)
			if err != nil {
				Failf("Unexpected error reading data from the server: %v", err)
			}

			if e, a := strings.Repeat("x", 100), string(fromServer); e != a {
				Failf("Expected %q from server, got %q", e, a)
			}

			logOutput := runKubectlWithTimeout(util.ForeverTestTimeout, "logs", fmt.Sprintf("--namespace=%v", framework.Namespace.Name), "-f", podName)
			verifyLogMessage(logOutput, "^Accepted client connection$")
			verifyLogMessage(logOutput, "^Received expected client data$")
			verifyLogMessage(logOutput, "^Done$")
		})
	})
	Describe("With a server that expects no client request", func() {
		It("should support a client that connects, sends no data, and disconnects [Conformance]", func() {
			By("creating the target pod")
			pod := pfPod("", "10", "10", "100")
			framework.Client.Pods(framework.Namespace.Name).Create(pod)
			framework.WaitForPodRunning(pod.Name)

			By("Running 'kubectl port-forward'")
			cmd, listenPort := runPortForward(framework.Namespace.Name, pod.Name, 80)
			defer tryKill(cmd)

			By("Dialing the local port")
			conn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", listenPort))
			if err != nil {
				Failf("Couldn't connect to port %d: %v", listenPort, err)
			}
			defer func() {
				By("Closing the connection to the local port")
				conn.Close()
			}()

			By("Reading data from the local port")
			fromServer, err := ioutil.ReadAll(conn)
			if err != nil {
				Failf("Unexpected error reading data from the server: %v", err)
			}

			if e, a := strings.Repeat("x", 100), string(fromServer); e != a {
				Failf("Expected %q from server, got %q", e, a)
			}

			logOutput := runKubectlWithTimeout(util.ForeverTestTimeout, "logs", fmt.Sprintf("--namespace=%v", framework.Namespace.Name), "-f", podName)
			verifyLogMessage(logOutput, "Accepted client connection")
			verifyLogMessage(logOutput, "Done")
		})
	})
})

func verifyLogMessage(log, expected string) {
	re := regexp.MustCompile(expected)
	lines := strings.Split(log, "\n")
	for i := range lines {
		if re.MatchString(lines[i]) {
			return
		}
	}
	Failf("Missing %q from log: %s", expected, log)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"

	"github.com/af83/edwig/api"
	"github.com/af83/edwig/siri"
)

func main() {
	flag.Parse()

	command := flag.Args()[0]

	var err error
	switch command {
	case "check":
		err = checkStatus(flag.Args()[1])
	}

	if err != nil {
		panic(err)
	}
}

func checkStatus(url string) error {
	client := siri.NewSOAPClient(url)
	request := &siri.SIRICheckStatusRequest{
		RequestorRef:      "Edwig",
		RequestTimestamp:  api.DefaultClock().Now(),
		MessageIdentifier: "Edwig:Message::6ba7b814-9dad-11d1-0-00c04fd430c8:LOC",
	}
	response, err := client.CheckStatus(request)
	if err != nil {
		return err
	}
	fmt.Println(response)
	return nil
}
<commit_msg>add -testuuid flag to edwig.go<commit_after>package main

import (
	"flag"
	"fmt"

	"github.com/af83/edwig/api"
	"github.com/af83/edwig/siri"
)

func main() {
	uuidPtr := flag.Bool("testuuid", false, "use the test uuid generator")

	flag.Parse()

	if *uuidPtr {
		api.SetDefaultUUIDGenerator(api.NewFakeUUIDGenerator())
	}

	fmt.Println(api.DefaultUUIDGenerator().NewUUID())

	command := flag.Args()[0]

	var err error
	switch command {
	case "check":
		err = checkStatus(flag.Args()[1])
	}

	if err != nil {
		panic(err)
	}
}

func checkStatus(url string) error {
	client := siri.NewSOAPClient(url)
	request := &siri.SIRICheckStatusRequest{
		RequestorRef:      "Edwig",
		RequestTimestamp:  api.DefaultClock().Now(),
		MessageIdentifier: "Edwig:Message::6ba7b814-9dad-11d1-0-00c04fd430c8:LOC",
	}
	response, err := client.CheckStatus(request)
	if err != nil {
		return err
	}
	fmt.Println(response)
	return nil
}
<|endoftext|>


<commit_before>// Package email is designed to provide an "email interface for humans."
// Designed to be robust and flexible, the email package aims to make sending email easy without getting in the way.
package email

import (
	"bytes"
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"mime"
	"mime/multipart"
	"net/mail"
	"net/smtp"
	"net/textproto"
	"os"
	"path"
	"path/filepath"
	"strings"
)

const (
	// MaxLineLength is the maximum line length per RFC 2045
	MaxLineLength = 76
)

// Email is the type used for email messages
type Email struct {
	From        string
	To          []string
	Bcc         []string
	Cc          []string
	Subject     string
	Text        string // Plaintext message (optional)
	HTML        string // Html message (optional)
	Headers     textproto.MIMEHeader
	Attachments map[string]*Attachment
	ReadReceipt []string
}

// NewEmail creates an Email, and returns the pointer to it.
func NewEmail() *Email {
	return &Email{Attachments: make(map[string]*Attachment), Headers: textproto.MIMEHeader{}}
}

// Attach is used to attach content from an io.Reader to the email.
// Required parameters include an io.Reader, the desired filename for the attachment, and the Content-Type
// The function will return the created Attachment for reference, as well as nil for the error, if successful.
func (e *Email) Attach(r io.Reader, filename string, c string) (a *Attachment, err error) {
	var buffer bytes.Buffer
	if _, err = io.Copy(&buffer, r); err != nil {
		return
	}
	e.Attachments[filename] = &Attachment{
		Filename: filename,
		Header:   textproto.MIMEHeader{},
		Content:  buffer.Bytes()}
	at := e.Attachments[filename]
	// Get the Content-Type to be used in the MIMEHeader
	if c != "" {
		at.Header.Set("Content-Type", c)
	} else {
		// If the Content-Type is blank, set the Content-Type to "application/octet-stream"
		at.Header.Set("Content-Type", "application/octet-stream")
	}
	at.Header.Set("Content-Disposition", fmt.Sprintf("attachment;\r\n filename=\"%s\"", filename))
	at.Header.Set("Content-Transfer-Encoding", "base64")
	return at, nil
}

// AttachFile is used to attach content to the email.
// It attempts to open the file referenced by filename and, if successful, creates an Attachment.
// This Attachment is then appended to the slice of Email.Attachments.
// The function will then return the Attachment for reference, as well as nil for the error, if successful.
func (e *Email) AttachFile(filename string) (a *Attachment, err error) {
	f, err := os.Open(filename)
	if err != nil {
		return
	}
	ct := mime.TypeByExtension(filepath.Ext(filename))
	basename := path.Base(filename)
	return e.Attach(f, basename, ct)
}

// Bytes converts the Email object to a []byte representation, including all needed MIMEHeaders, boundaries, etc.
func (e *Email) Bytes() ([]byte, error) {
	buff := &bytes.Buffer{}
	w := multipart.NewWriter(buff)
	// Set the appropriate headers (overwriting any conflicts)
	// Leave out Bcc (only included in envelope headers)
	e.Headers.Set("To", strings.Join(e.To, ","))
	if e.Cc != nil {
		e.Headers.Set("Cc", strings.Join(e.Cc, ","))
	}
	e.Headers.Set("From", e.From)
	e.Headers.Set("Subject", e.Subject)
	if len(e.ReadReceipt) != 0 {
		e.Headers.Set("Disposition-Notification-To", strings.Join(e.ReadReceipt, ","))
	}
	e.Headers.Set("MIME-Version", "1.0")
	e.Headers.Set("Content-Type", fmt.Sprintf("multipart/mixed;\r\n boundary=%s\r\n", w.Boundary()))

	// Write the envelope headers (including any custom headers)
	if err := headerToBytes(buff, e.Headers); err != nil {
	}
	// Start the multipart/mixed part
	fmt.Fprintf(buff, "--%s\r\n", w.Boundary())
	header := textproto.MIMEHeader{}
	// Check to see if there is a Text or HTML field
	if e.Text != "" || e.HTML != "" {
		subWriter := multipart.NewWriter(buff)
		// Create the multipart alternative part
		header.Set("Content-Type", fmt.Sprintf("multipart/alternative;\r\n boundary=%s\r\n", subWriter.Boundary()))
		// Write the header
		if err := headerToBytes(buff, header); err != nil {

		}
		// Create the body sections
		if e.Text != "" {
			header.Set("Content-Type", fmt.Sprintf("text/plain; charset=UTF-8"))
			header.Set("Content-Transfer-Encoding", "quoted-printable")
			if _, err := subWriter.CreatePart(header); err != nil {
				return nil, err
			}
			// Write the text
			if err := quotePrintEncode(buff, e.Text); err != nil {
				return nil, err
			}
		}
		if e.HTML != "" {
			header.Set("Content-Type", fmt.Sprintf("text/html; charset=UTF-8"))
			header.Set("Content-Transfer-Encoding", "quoted-printable")
			if _, err := subWriter.CreatePart(header); err != nil {
				return nil, err
			}
			// Write the text
			if err := quotePrintEncode(buff, e.HTML); err != nil {
				return nil, err
			}
		}
		if err := subWriter.Close(); err != nil {
			return nil, err
		}
	}
	// Create attachment part, if necessary
	if e.Attachments != nil {
		for _, a := range e.Attachments {
			ap, err := w.CreatePart(a.Header)
			if err != nil {
				return nil, err
			}
			// Write the base64Wrapped content to the part
			base64Wrap(ap, a.Content)
		}
	}
	if err := w.Close(); err != nil {
		return nil, err
	}
	return buff.Bytes(), nil
}

// Send an email using the given host and SMTP auth (optional), returns any error thrown by smtp.SendMail
// This function merges the To, Cc, and Bcc fields and calls the smtp.SendMail function using the Email.Bytes() output as the message
func (e *Email) Send(addr string, a smtp.Auth) error {
	// Merge the To, Cc, and Bcc fields
	to := make([]string, 0, len(e.To)+len(e.Cc)+len(e.Bcc))
	to = append(append(append(to, e.To...), e.Cc...), e.Bcc...)
	// Check to make sure there is at least one recipient and one "From" address
	if e.From == "" || len(to) == 0 {
		return errors.New("Must specify at least one From address and one To address")
	}
	from, err := mail.ParseAddress(e.From)
	if err != nil {
		return err
	}
	raw, err := e.Bytes()
	if err != nil {
		return err
	}
	return smtp.SendMail(addr, a, from.Address, to, raw)
}

// Attachment is a struct representing an email attachment.
// Based on the mime/multipart.FileHeader struct, Attachment contains the name, MIMEHeader, and content of the attachment in question
type Attachment struct {
	Filename string
	Header   textproto.MIMEHeader
	Content  []byte
}

// quotePrintEncode writes the quoted-printable text to the IO Writer (according to RFC 2045)
func quotePrintEncode(w io.Writer, s string) error {
	mc := 0
	for _, c := range s {
		// Handle the soft break for the EOL, if needed
		if mc == MaxLineLength-1 || (!isPrintable(c) && mc+len(fmt.Sprintf("%s%X", "=", c)) > MaxLineLength-1) {
			if _, err := fmt.Fprintf(w, "%s", "=\r\n"); err != nil {
				return err
			}
			mc = 0
		}
		// append the appropriate character
		if isPrintable(c) {
			// Printable character
			if _, err := fmt.Fprintf(w, "%s", string(c)); err != nil {
				return err
			}
			// Reset the counter if we wrote a newline
			if c == '\n' {
				mc = 0
			}
			mc++
			continue
		} else {
			// non-printable.. encode it (TODO)
			es := fmt.Sprintf("%s%X", "=", c)
			if _, err := fmt.Fprintf(w, "%s", es); err != nil {
				return err
			}
			// todo - increment correctly
			mc += len(es)
		}
	}
	return nil
}

// isPrintable returns true if the rune given is "printable" according to RFC 2045, false otherwise
func isPrintable(c rune) bool {
	return (c >= '!' && c <= '<') || (c >= '>' && c <= '~') || (c == ' ' || c == '\n' || c == '\t')
}

// base64Wrap encodeds the attachment content, and wraps it according to RFC 2045 standards (every 76 chars)
// The output is then written to the specified io.Writer
func base64Wrap(w io.Writer, b []byte) {
	encoded := base64.StdEncoding.EncodeToString(b)
	for i := 0; i < len(encoded); i += MaxLineLength {
		// Do we need to print 76 characters, or the rest of the string?
		if len(encoded)-i < MaxLineLength {
			fmt.Fprintf(w, "%s\r\n", encoded[i:])
		} else {
			fmt.Fprintf(w, "%s\r\n", encoded[i:i+MaxLineLength])
		}
	}
}

// headerToBytes enumerates the key and values in the header, and writes the results to the IO Writer
func headerToBytes(w io.Writer, t textproto.MIMEHeader) error {
	for k, v := range t {
		// Write the header key
		_, err := fmt.Fprintf(w, "%s:", k)
		if err != nil {
			return err
		}
		// Write each value in the header
		for _, c := range v {
			_, err := fmt.Fprintf(w, " %s\r\n", c)
			if err != nil {
				return err
			}
		}
	}
	return nil
}
<commit_msg>Make Email.Attachments a slice<commit_after>// Package email is designed to provide an "email interface for humans."
// Designed to be robust and flexible, the email package aims to make sending email easy without getting in the way.
package email

import (
	"bytes"
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"mime"
	"mime/multipart"
	"net/mail"
	"net/smtp"
	"net/textproto"
	"os"
	"path"
	"path/filepath"
	"strings"
)

const (
	// MaxLineLength is the maximum line length per RFC 2045
	MaxLineLength = 76
)

// Email is the type used for email messages
type Email struct {
	From        string
	To          []string
	Bcc         []string
	Cc          []string
	Subject     string
	Text        string // Plaintext message (optional)
	HTML        string // Html message (optional)
	Headers     textproto.MIMEHeader
	Attachments []*Attachment
	ReadReceipt []string
}

// NewEmail creates an Email, and returns the pointer to it.
func NewEmail() *Email {
	return &Email{Headers: textproto.MIMEHeader{}}
}

// Attach is used to attach content from an io.Reader to the email.
// Required parameters include an io.Reader, the desired filename for the attachment, and the Content-Type
// The function will return the created Attachment for reference, as well as nil for the error, if successful.
func (e *Email) Attach(r io.Reader, filename string, c string) (a *Attachment, err error) {
	var buffer bytes.Buffer
	if _, err = io.Copy(&buffer, r); err != nil {
		return
	}
	at := &Attachment{
		Filename: filename,
		Header:   textproto.MIMEHeader{},
		Content:  buffer.Bytes(),
	}
	// Get the Content-Type to be used in the MIMEHeader
	if c != "" {
		at.Header.Set("Content-Type", c)
	} else {
		// If the Content-Type is blank, set the Content-Type to "application/octet-stream"
		at.Header.Set("Content-Type", "application/octet-stream")
	}
	at.Header.Set("Content-Disposition", fmt.Sprintf("attachment;\r\n filename=\"%s\"", filename))
	at.Header.Set("Content-Transfer-Encoding", "base64")
	e.Attachments = append(e.Attachments, at)
	return at, nil
}

// AttachFile is used to attach content to the email.
// It attempts to open the file referenced by filename and, if successful, creates an Attachment.
// This Attachment is then appended to the slice of Email.Attachments.
// The function will then return the Attachment for reference, as well as nil for the error, if successful.
func (e *Email) AttachFile(filename string) (a *Attachment, err error) {
	f, err := os.Open(filename)
	if err != nil {
		return
	}
	ct := mime.TypeByExtension(filepath.Ext(filename))
	basename := path.Base(filename)
	return e.Attach(f, basename, ct)
}

// Bytes converts the Email object to a []byte representation, including all needed MIMEHeaders, boundaries, etc.
func (e *Email) Bytes() ([]byte, error) {
	buff := &bytes.Buffer{}
	w := multipart.NewWriter(buff)
	// Set the appropriate headers (overwriting any conflicts)
	// Leave out Bcc (only included in envelope headers)
	e.Headers.Set("To", strings.Join(e.To, ","))
	if e.Cc != nil {
		e.Headers.Set("Cc", strings.Join(e.Cc, ","))
	}
	e.Headers.Set("From", e.From)
	e.Headers.Set("Subject", e.Subject)
	if len(e.ReadReceipt) != 0 {
		e.Headers.Set("Disposition-Notification-To", strings.Join(e.ReadReceipt, ","))
	}
	e.Headers.Set("MIME-Version", "1.0")
	e.Headers.Set("Content-Type", fmt.Sprintf("multipart/mixed;\r\n boundary=%s\r\n", w.Boundary()))

	// Write the envelope headers (including any custom headers)
	if err := headerToBytes(buff, e.Headers); err != nil {
	}
	// Start the multipart/mixed part
	fmt.Fprintf(buff, "--%s\r\n", w.Boundary())
	header := textproto.MIMEHeader{}
	// Check to see if there is a Text or HTML field
	if e.Text != "" || e.HTML != "" {
		subWriter := multipart.NewWriter(buff)
		// Create the multipart alternative part
		header.Set("Content-Type", fmt.Sprintf("multipart/alternative;\r\n boundary=%s\r\n", subWriter.Boundary()))
		// Write the header
		if err := headerToBytes(buff, header); err != nil {

		}
		// Create the body sections
		if e.Text != "" {
			header.Set("Content-Type", fmt.Sprintf("text/plain; charset=UTF-8"))
			header.Set("Content-Transfer-Encoding", "quoted-printable")
			if _, err := subWriter.CreatePart(header); err != nil {
				return nil, err
			}
			// Write the text
			if err := quotePrintEncode(buff, e.Text); err != nil {
				return nil, err
			}
		}
		if e.HTML != "" {
			header.Set("Content-Type", fmt.Sprintf("text/html; charset=UTF-8"))
			header.Set("Content-Transfer-Encoding", "quoted-printable")
			if _, err := subWriter.CreatePart(header); err != nil {
				return nil, err
			}
			// Write the text
			if err := quotePrintEncode(buff, e.HTML); err != nil {
				return nil, err
			}
		}
		if err := subWriter.Close(); err != nil {
			return nil, err
		}
	}
	// Create attachment part, if necessary
	for _, a := range e.Attachments {
		ap, err := w.CreatePart(a.Header)
		if err != nil {
			return nil, err
		}
		// Write the base64Wrapped content to the part
		base64Wrap(ap, a.Content)
	}
	if err := w.Close(); err != nil {
		return nil, err
	}
	return buff.Bytes(), nil
}

// Send an email using the given host and SMTP auth (optional), returns any error thrown by smtp.SendMail
// This function merges the To, Cc, and Bcc fields and calls the smtp.SendMail function using the Email.Bytes() output as the message
func (e *Email) Send(addr string, a smtp.Auth) error {
	// Merge the To, Cc, and Bcc fields
	to := make([]string, 0, len(e.To)+len(e.Cc)+len(e.Bcc))
	to = append(append(append(to, e.To...), e.Cc...), e.Bcc...)
	// Check to make sure there is at least one recipient and one "From" address
	if e.From == "" || len(to) == 0 {
		return errors.New("Must specify at least one From address and one To address")
	}
	from, err := mail.ParseAddress(e.From)
	if err != nil {
		return err
	}
	raw, err := e.Bytes()
	if err != nil {
		return err
	}
	return smtp.SendMail(addr, a, from.Address, to, raw)
}

// Attachment is a struct representing an email attachment.
// Based on the mime/multipart.FileHeader struct, Attachment contains the name, MIMEHeader, and content of the attachment in question
type Attachment struct {
	Filename string
	Header   textproto.MIMEHeader
	Content  []byte
}

// quotePrintEncode writes the quoted-printable text to the IO Writer (according to RFC 2045)
func quotePrintEncode(w io.Writer, s string) error {
	mc := 0
	for _, c := range s {
		// Handle the soft break for the EOL, if needed
		if mc == MaxLineLength-1 || (!isPrintable(c) && mc+len(fmt.Sprintf("%s%X", "=", c)) > MaxLineLength-1) {
			if _, err := fmt.Fprintf(w, "%s", "=\r\n"); err != nil {
				return err
			}
			mc = 0
		}
		// append the appropriate character
		if isPrintable(c) {
			// Printable character
			if _, err := fmt.Fprintf(w, "%s", string(c)); err != nil {
				return err
			}
			// Reset the counter if we wrote a newline
			if c == '\n' {
				mc = 0
			}
			mc++
			continue
		} else {
			// non-printable.. encode it (TODO)
			es := fmt.Sprintf("%s%X", "=", c)
			if _, err := fmt.Fprintf(w, "%s", es); err != nil {
				return err
			}
			// todo - increment correctly
			mc += len(es)
		}
	}
	return nil
}

// isPrintable returns true if the rune given is "printable" according to RFC 2045, false otherwise
func isPrintable(c rune) bool {
	return (c >= '!' && c <= '<') || (c >= '>' && c <= '~') || (c == ' ' || c == '\n' || c == '\t')
}

// base64Wrap encodeds the attachment content, and wraps it according to RFC 2045 standards (every 76 chars)
// The output is then written to the specified io.Writer
func base64Wrap(w io.Writer, b []byte) {
	encoded := base64.StdEncoding.EncodeToString(b)
	for i := 0; i < len(encoded); i += MaxLineLength {
		// Do we need to print 76 characters, or the rest of the string?
		if len(encoded)-i < MaxLineLength {
			fmt.Fprintf(w, "%s\r\n", encoded[i:])
		} else {
			fmt.Fprintf(w, "%s\r\n", encoded[i:i+MaxLineLength])
		}
	}
}

// headerToBytes enumerates the key and values in the header, and writes the results to the IO Writer
func headerToBytes(w io.Writer, t textproto.MIMEHeader) error {
	for k, v := range t {
		// Write the header key
		_, err := fmt.Fprintf(w, "%s:", k)
		if err != nil {
			return err
		}
		// Write each value in the header
		for _, c := range v {
			_, err := fmt.Fprintf(w, " %s\r\n", c)
			if err != nil {
				return err
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package emogo

import (
	"errors"
	"unsafe"
	"syscall"
)

// #include <emokit/emokit.h>
// #include <stdint.h>
// #cgo LDFLAGS: -lemokit
import "C"

// These are defined in emokit.h and reproduced here as cgo isn't
// linking them in for some reason.
const (
	EMOKIT_VID int = 0x21a1
	EMOKIT_PID int = 0x0001
	EmokitPacketSize = 32
)

// EmokitContext represents a connection to an EPOC device.
type EmokitContext struct {
	eeg *C.struct_emokit_device
}

func NewEmokitContext() (*EmokitContext, error) {
	e := new(EmokitContext)
	e.eeg = C.emokit_create()
	ret := C.emokit_open(e.eeg, C.int(EMOKIT_VID), C.int(EMOKIT_PID), 0)
	if ret != 0 {
		return nil, errors.New("Cannot access device.")
	}
	return e, nil
}

type EmokitFrame struct {
	raw []byte
	rendered C.struct_emokit_frame
}

func NewEmokitFrame() *EmokitFrame {
	f := new(EmokitFrame)
	f.raw = make([]byte, EmokitPacketSize)
	return f
}

// readData reads data from the EPOC dongle and returns the number of
// bytes read.
func (e *EmokitContext) readData() int {
	n := C.emokit_read_data(e.eeg)
	return int(n)
}

func (e *EmokitContext) getNextFrame() (*EmokitFrame, error) {
	f := NewEmokitFrame()
	f.rendered = C.emokit_get_next_frame(e.eeg)
	if f.rendered.counter == 0 {
		return nil, errors.New("Could not read raw packet.")
	}
	C.emokit_get_raw_frame(e.eeg, (*C.uchar)(unsafe.Pointer(&f.raw[0])))
	return f, nil
}

// GetFrame returns the next available EPOC frame. If there is no frame
// to be read, the error value will be EAGAIN.
func (e *EmokitContext) GetFrame() (*EmokitFrame, error) {
	if e.readData() > 0 {
		f, err := e.getNextFrame()
		if err != nil {
			return nil, err
		}
		return f, nil
	}
	return nil, syscall.EAGAIN
}
<commit_msg>Add package comment<commit_after>/*
The emogo package provides go bindings for emokit
(https://github.com/openyou/emokit).
*/
package emogo

import (
	"errors"
	"unsafe"
	"syscall"
)

// #include <emokit/emokit.h>
// #include <stdint.h>
// #cgo LDFLAGS: -lemokit
import "C"

// These are defined in emokit.h and reproduced here as cgo isn't
// linking them in for some reason.
const (
	EMOKIT_VID int = 0x21a1
	EMOKIT_PID int = 0x0001
	EmokitPacketSize = 32
)

// EmokitContext represents a connection to an EPOC device.
type EmokitContext struct {
	eeg *C.struct_emokit_device
}

func NewEmokitContext() (*EmokitContext, error) {
	e := new(EmokitContext)
	e.eeg = C.emokit_create()
	ret := C.emokit_open(e.eeg, C.int(EMOKIT_VID), C.int(EMOKIT_PID), 0)
	if ret != 0 {
		return nil, errors.New("Cannot access device.")
	}
	return e, nil
}

type EmokitFrame struct {
	raw []byte
	rendered C.struct_emokit_frame
}

func NewEmokitFrame() *EmokitFrame {
	f := new(EmokitFrame)
	f.raw = make([]byte, EmokitPacketSize)
	return f
}

// readData reads data from the EPOC dongle and returns the number of
// bytes read.
func (e *EmokitContext) readData() int {
	n := C.emokit_read_data(e.eeg)
	return int(n)
}

func (e *EmokitContext) getNextFrame() (*EmokitFrame, error) {
	f := NewEmokitFrame()
	f.rendered = C.emokit_get_next_frame(e.eeg)
	if f.rendered.counter == 0 {
		return nil, errors.New("Could not read raw packet.")
	}
	C.emokit_get_raw_frame(e.eeg, (*C.uchar)(unsafe.Pointer(&f.raw[0])))
	return f, nil
}

// GetFrame returns the next available EPOC frame. If there is no frame
// to be read, the error value will be EAGAIN.
func (e *EmokitContext) GetFrame() (*EmokitFrame, error) {
	if e.readData() > 0 {
		f, err := e.getNextFrame()
		if err != nil {
			return nil, err
		}
		return f, nil
	}
	return nil, syscall.EAGAIN
}
<|endoftext|>


<commit_before>package gomoji

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"regexp"
	"unicode"
)

// Replace Padding character for emoji.
const (
	ReplacePadding = " "
)

var re *regexp.Regexp

// CodeMap gets the underlying map of emoji.
func CodeMap() map[string]string {
	return emojiCodeMap
}

func emojize(x string) string {
	str, ok := emojiCodeMap[x]
	if ok {
		return str + ReplacePadding
	}
	return x
}

func replaseEmoji(input *bytes.Buffer) string {
	emoji := bytes.NewBufferString(":")
	for {
		i, _, err := input.ReadRune()
		if err != nil {
			// not replase
			return emoji.String()
		}

		if i == ':' && emoji.Len() == 1 {
			return emoji.String() + replaseEmoji(input)
		}

		emoji.WriteRune(i)
		switch {
		case unicode.IsSpace(i):
			return emoji.String()
		case i == ':':
			return emojize(emoji.String())
		}
	}
}

func compile(x string) string {
	if x == "" {
		return ""
	}

	input := bytes.NewBufferString(x)
	output := bytes.NewBufferString("")

	for {
		i, _, err := input.ReadRune()
		if err != nil {
			break
		}
		switch i {
		default:
			output.WriteRune(i)
		case ':':
			output.WriteString(replaseEmoji(input))
		}
	}
	return output.String()
}

func compileValues(a *[]interface{}) {
	for i, x := range *a {
		if str, ok := x.(string); ok {
			(*a)[i] = compile(str)
		}
	}
}

// Print is fmt.Print which supports emoji
func Print(a ...interface{}) (int, error) {
	compileValues(&a)
	return fmt.Print(a...)
}

// Println is fmt.Println which supports emoji
func Println(a ...interface{}) (int, error) {
	compileValues(&a)
	return fmt.Println(a...)
}

// Printf is fmt.Printf which supports emoji
func Printf(format string, a ...interface{}) (int, error) {
	format = compile(format)
	return fmt.Printf(format, a...)
}

// Fprint is fmt.Fprint which supports emoji
func Fprint(w io.Writer, a ...interface{}) (int, error) {
	compileValues(&a)
	return fmt.Fprint(w, a...)
}

// Fprintln is fmt.Fprintln which supports emoji
func Fprintln(w io.Writer, a ...interface{}) (int, error) {
	compileValues(&a)
	return fmt.Fprintln(w, a...)
}

// Fprintf is fmt.Fprintf which supports emoji
func Fprintf(w io.Writer, format string, a ...interface{}) (int, error) {
	format = compile(format)
	return fmt.Fprintf(w, format, a...)
}

// Sprint is fmt.Sprint which supports emoji
func Sprint(a ...interface{}) string {
	compileValues(&a)
	return fmt.Sprint(a...)
}

// Sprintf is fmt.Sprintf which supports emoji
func Sprintf(format string, a ...interface{}) string {
	format = compile(format)
	return fmt.Sprintf(format, a...)
}

// Errorf is fmt.Errorf which supports emoji
func Errorf(format string, a ...interface{}) error {
	return errors.New(Sprintf(format, a...))
}

func Demojize(a ...interface{}) string {
	str := Sprint(a)
	converted := re.ReplaceAllStringFunc(str, func(m string) string {
		unicode := fmt.Sprintf("%+q", m)
		s := UnicodeEmojeMap[unicode]
		if len(s) != 0 {
			return s
		} else {
			return m
		}
	})
	re := regexp.MustCompile("[*]")
	res := re.Split(converted, -1)
	return res[0][1 : len(res[0])-1]
}

func init() {
	for k, v := range emojiCodeMap {
		s := fmt.Sprintf("%+q", v)
		UnicodeEmojeMap[s] = k
	}
	re = regexp.MustCompile(`[[:^ascii:]]`)
}
<commit_msg>fix bug<commit_after>package gomoji

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"regexp"
	"unicode"
)

// Replace Padding character for emoji.
const (
	ReplacePadding = " "
)

var re *regexp.Regexp

// CodeMap gets the underlying map of emoji.
func CodeMap() map[string]string {
	return emojiCodeMap
}

func emojize(x string) string {
	str, ok := emojiCodeMap[x]
	if ok {
		return str + ReplacePadding
	}
	return x
}

func replaseEmoji(input *bytes.Buffer) string {
	emoji := bytes.NewBufferString(":")
	for {
		i, _, err := input.ReadRune()
		if err != nil {
			// not replase
			return emoji.String()
		}

		if i == ':' && emoji.Len() == 1 {
			return emoji.String() + replaseEmoji(input)
		}

		emoji.WriteRune(i)
		switch {
		case unicode.IsSpace(i):
			return emoji.String()
		case i == ':':
			return emojize(emoji.String())
		}
	}
}

func compile(x string) string {
	if x == "" {
		return ""
	}

	input := bytes.NewBufferString(x)
	output := bytes.NewBufferString("")

	for {
		i, _, err := input.ReadRune()
		if err != nil {
			break
		}
		switch i {
		default:
			output.WriteRune(i)
		case ':':
			output.WriteString(replaseEmoji(input))
		}
	}
	return output.String()
}

func compileValues(a *[]interface{}) {
	for i, x := range *a {
		if str, ok := x.(string); ok {
			(*a)[i] = compile(str)
		}
	}
}

// Print is fmt.Print which supports emoji
func Print(a ...interface{}) (int, error) {
	compileValues(&a)
	return fmt.Print(a...)
}

// Println is fmt.Println which supports emoji
func Println(a ...interface{}) (int, error) {
	compileValues(&a)
	return fmt.Println(a...)
}

// Printf is fmt.Printf which supports emoji
func Printf(format string, a ...interface{}) (int, error) {
	format = compile(format)
	return fmt.Printf(format, a...)
}

// Fprint is fmt.Fprint which supports emoji
func Fprint(w io.Writer, a ...interface{}) (int, error) {
	compileValues(&a)
	return fmt.Fprint(w, a...)
}

// Fprintln is fmt.Fprintln which supports emoji
func Fprintln(w io.Writer, a ...interface{}) (int, error) {
	compileValues(&a)
	return fmt.Fprintln(w, a...)
}

// Fprintf is fmt.Fprintf which supports emoji
func Fprintf(w io.Writer, format string, a ...interface{}) (int, error) {
	format = compile(format)
	return fmt.Fprintf(w, format, a...)
}

// Sprint is fmt.Sprint which supports emoji
func Sprint(a ...interface{}) string {
	compileValues(&a)
	return fmt.Sprint(a...)
}

// Sprintf is fmt.Sprintf which supports emoji
func Sprintf(format string, a ...interface{}) string {
	format = compile(format)
	return fmt.Sprintf(format, a...)
}

// Errorf is fmt.Errorf which supports emoji
func Errorf(format string, a ...interface{}) error {
	return errors.New(Sprintf(format, a...))
}

func Demojize(a ...interface{}) string {
	str := Sprint(a)
	fmt.Println(str)
	converted := re.ReplaceAllStringFunc(str, func(m string) string {
		unicode := fmt.Sprintf("%+q", m)
		s := UnicodeEmojeMap[unicode]
		if len(s) != 0 {
			return s
		} else {
			return m
		}
	})
	return converted[1 : len(converted)-1]
}

func init() {
	for k, v := range emojiCodeMap {
		s := fmt.Sprintf("%+q", v)
		UnicodeEmojeMap[s] = k
	}
	re = regexp.MustCompile(`[[:^ascii:]]`)
}
<|endoftext|>


<commit_before>package cairo

//#cgo pkg-config: cairo
//#include <cairo/cairo.h>
import "C"

import (
	"errors"
)

const (
	errSuccess                 = C.CAIRO_STATUS_SUCCESS
	errNoMem                   = C.CAIRO_STATUS_NO_MEMORY
	errInvalidRestore          = C.CAIRO_STATUS_INVALID_RESTORE
	errInvalidPopGroup         = C.CAIRO_STATUS_INVALID_POP_GROUP
	errNoCurrentPoint          = C.CAIRO_STATUS_NO_CURRENT_POINT
	errInvalidMatrix           = C.CAIRO_STATUS_INVALID_MATRIX
	errInvalidStatus           = C.CAIRO_STATUS_INVALID_STATUS //seriously?
	errNullPointer             = C.CAIRO_STATUS_NULL_POINTER
	errInvalidString           = C.CAIRO_STATUS_INVALID_STRING
	errInvalidPathData         = C.CAIRO_STATUS_INVALID_PATH_DATA
	errReadError               = C.CAIRO_STATUS_READ_ERROR
	errWriteError              = C.CAIRO_STATUS_WRITE_ERROR
	errSurfaceFinished         = C.CAIRO_STATUS_SURFACE_FINISHED
	errSurfaceTypeMismatch     = C.CAIRO_STATUS_SURFACE_TYPE_MISMATCH
	errPatternTypeMismatch     = C.CAIRO_STATUS_PATTERN_TYPE_MISMATCH
	errInvalidContent          = C.CAIRO_STATUS_INVALID_CONTENT
	errInvalidFormat           = C.CAIRO_STATUS_INVALID_FORMAT
	errInvalidVisual           = C.CAIRO_STATUS_INVALID_VISUAL
	errFileNotFound            = C.CAIRO_STATUS_FILE_NOT_FOUND
	errInvalidDash             = C.CAIRO_STATUS_INVALID_DASH
	errInvalidDSCComment       = C.CAIRO_STATUS_INVALID_DSC_COMMENT
	errInvalidIndex            = C.CAIRO_STATUS_INVALID_INDEX
	errClipNotRepresentable    = C.CAIRO_STATUS_CLIP_NOT_REPRESENTABLE
	errTempFileError           = C.CAIRO_STATUS_TEMP_FILE_ERROR
	errInvalidStride           = C.CAIRO_STATUS_INVALID_STRIDE
	errFontTypeMismatch        = C.CAIRO_STATUS_FONT_TYPE_MISMATCH
	errUserFontImmutable       = C.CAIRO_STATUS_USER_FONT_IMMUTABLE
	errUserFontError           = C.CAIRO_STATUS_USER_FONT_ERROR
	errNegativeCount           = C.CAIRO_STATUS_NEGATIVE_COUNT
	errInvalidClusters         = C.CAIRO_STATUS_INVALID_CLUSTERS
	errInvalidSlant            = C.CAIRO_STATUS_INVALID_SLANT
	errInvalidWeight           = C.CAIRO_STATUS_INVALID_WEIGHT
	errInvalidSize             = C.CAIRO_STATUS_INVALID_SIZE
	errUserFontNotImplemented  = C.CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED
	errDeviceTypeMismatch      = C.CAIRO_STATUS_DEVICE_TYPE_MISMATCH
	errDeviceError             = C.CAIRO_STATUS_DEVICE_ERROR
	errInvalidMeshConstruction = C.CAIRO_STATUS_INVALID_MESH_CONSTRUCTION
	errDeviceFinished          = C.CAIRO_STATUS_DEVICE_FINISHED
	errLastStatus              = C.CAIRO_STATUS_LAST_STATUS
)

var (
	//TODO define common ones as Err* for user cmps

	ErrInvalidPathData = mkerr(errInvalidPathData)
)

func st2str(st C.cairo_status_t) string {
	return C.GoString(C.cairo_status_to_string(st))
}

func mkerr(st C.cairo_status_t) error {
	return errors.New(st2str(st))
}

//BUG(jmf): return any of special ones defined in above TODO and handle conversion to io/os
//errors for the file stuff
func toerr(st C.cairo_status_t) error {
	switch int(st) {
	case errSuccess:
		return nil
	case errInvalidRestore, errInvalidPopGroup, errNoCurrentPoint, errInvalidMatrix, errInvalidString, errSurfaceFinished:
		panic(st2str(st))
	case errInvalidPathData:
		return ErrInvalidPathData
	}
	return errors.New(st2str(st))
}
<commit_msg>added another named type<commit_after>package cairo

//#cgo pkg-config: cairo
//#include <cairo/cairo.h>
import "C"

import (
	"errors"
)

const (
	errSuccess                 = C.CAIRO_STATUS_SUCCESS
	errNoMem                   = C.CAIRO_STATUS_NO_MEMORY
	errInvalidRestore          = C.CAIRO_STATUS_INVALID_RESTORE
	errInvalidPopGroup         = C.CAIRO_STATUS_INVALID_POP_GROUP
	errNoCurrentPoint          = C.CAIRO_STATUS_NO_CURRENT_POINT
	errInvalidMatrix           = C.CAIRO_STATUS_INVALID_MATRIX
	errInvalidStatus           = C.CAIRO_STATUS_INVALID_STATUS //seriously?
	errNullPointer             = C.CAIRO_STATUS_NULL_POINTER
	errInvalidString           = C.CAIRO_STATUS_INVALID_STRING
	errInvalidPathData         = C.CAIRO_STATUS_INVALID_PATH_DATA
	errReadError               = C.CAIRO_STATUS_READ_ERROR
	errWriteError              = C.CAIRO_STATUS_WRITE_ERROR
	errSurfaceFinished         = C.CAIRO_STATUS_SURFACE_FINISHED
	errSurfaceTypeMismatch     = C.CAIRO_STATUS_SURFACE_TYPE_MISMATCH
	errPatternTypeMismatch     = C.CAIRO_STATUS_PATTERN_TYPE_MISMATCH
	errInvalidContent          = C.CAIRO_STATUS_INVALID_CONTENT
	errInvalidFormat           = C.CAIRO_STATUS_INVALID_FORMAT
	errInvalidVisual           = C.CAIRO_STATUS_INVALID_VISUAL
	errFileNotFound            = C.CAIRO_STATUS_FILE_NOT_FOUND
	errInvalidDash             = C.CAIRO_STATUS_INVALID_DASH
	errInvalidDSCComment       = C.CAIRO_STATUS_INVALID_DSC_COMMENT
	errInvalidIndex            = C.CAIRO_STATUS_INVALID_INDEX
	errClipNotRepresentable    = C.CAIRO_STATUS_CLIP_NOT_REPRESENTABLE
	errTempFileError           = C.CAIRO_STATUS_TEMP_FILE_ERROR
	errInvalidStride           = C.CAIRO_STATUS_INVALID_STRIDE
	errFontTypeMismatch        = C.CAIRO_STATUS_FONT_TYPE_MISMATCH
	errUserFontImmutable       = C.CAIRO_STATUS_USER_FONT_IMMUTABLE
	errUserFontError           = C.CAIRO_STATUS_USER_FONT_ERROR
	errNegativeCount           = C.CAIRO_STATUS_NEGATIVE_COUNT
	errInvalidClusters         = C.CAIRO_STATUS_INVALID_CLUSTERS
	errInvalidSlant            = C.CAIRO_STATUS_INVALID_SLANT
	errInvalidWeight           = C.CAIRO_STATUS_INVALID_WEIGHT
	errInvalidSize             = C.CAIRO_STATUS_INVALID_SIZE
	errUserFontNotImplemented  = C.CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED
	errDeviceTypeMismatch      = C.CAIRO_STATUS_DEVICE_TYPE_MISMATCH
	errDeviceError             = C.CAIRO_STATUS_DEVICE_ERROR
	errInvalidMeshConstruction = C.CAIRO_STATUS_INVALID_MESH_CONSTRUCTION
	errDeviceFinished          = C.CAIRO_STATUS_DEVICE_FINISHED
	errLastStatus              = C.CAIRO_STATUS_LAST_STATUS
)

var (
	//TODO define common ones as Err* for user cmps

	ErrInvalidPathData = mkerr(errInvalidPathData)
	ErrInvalidDash     = mkerr(errInvalidDash)
)

func st2str(st C.cairo_status_t) string {
	return C.GoString(C.cairo_status_to_string(st))
}

func mkerr(st C.cairo_status_t) error {
	return errors.New(st2str(st))
}

//BUG(jmf): return any of special ones defined in above TODO and handle conversion to io/os
//errors for the file stuff
func toerr(st C.cairo_status_t) error {
	switch int(st) {
	case errSuccess:
		return nil
	case errInvalidRestore, errInvalidPopGroup, errNoCurrentPoint, errInvalidMatrix, errInvalidString, errSurfaceFinished:
		panic(st2str(st))
	case errInvalidPathData:
		return ErrInvalidPathData
	case errInvalidDash:
		return ErrInvalidDash
	}
	return errors.New(st2str(st))
}
<|endoftext|>