<commit_before>package main

var twoFactorTopic = &Topic{
	Name:        "twofactor",
	Description: "manage two-factor authentication settings",
}

var twoFactorCmd = &Command{
	Topic:     "twofactor",
	NeedsAuth: true,
	Run: func(ctx *Context) {
		req := apiRequest(ctx.APIToken)
		req.Method = "GET"
		req.Uri = req.Uri + "/account"
		res, err := req.Do()
		ExitIfError(err)
		var doc map[string]bool
		res.Body.FromJsonTo(&doc)
		if doc["two_factor_authentication"] {
			Println("Two-factor authentication is enabled")
		} else {
			Println("Two-factor authentication is not enabled")
		}
	},
}

var twoFactorGenerateCmd = &Command{
	Topic:       "twofactor",
	Command:     "generate-recovery-codes",
	Description: "Generates and replaces recovery codes",
	NeedsAuth:   true,
	Run: func(ctx *Context) {
		req := apiRequest(ctx.APIToken)
		req.Method = "POST"
		req.Uri = req.Uri + "/account/recovery-codes"
		req.AddHeader("Heroku-Password", getPassword())
		req.AddHeader("Heroku-Two-Factor-Code", getString("Two-factor code: "))
		res, err := req.Do()
		ExitIfError(err)
		var codes []interface{}
		res.Body.FromJsonTo(&codes)
		Println("Recovery codes:")
		for _, code := range codes {
			Println(code)
		}
	},
}

var twoFactorDisableCmd = &Command{
	Topic:       "twofactor",
	Command:     "disable",
	Description: "Disable two-factor authentication for your account",
	NeedsAuth:   true,
	Run: func(ctx *Context) {
		req := apiRequest(ctx.APIToken)
		req.Method = "PATCH"
		req.Uri = req.Uri + "/account/"
		req.Body = map[string]interface{}{
			"two_factor_authentication": "false",
			"password":                  getPassword(),
		}
		_, err := req.Do()
		ExitIfError(err)
		Println("disabled two-factor authentication")
	},
}
<commit_msg>fixed error handling with twofactor:disable<commit_after>package main

var twoFactorTopic = &Topic{
	Name:        "twofactor",
	Description: "manage two-factor authentication settings",
}

var twoFactorCmd = &Command{
	Topic:     "twofactor",
	NeedsAuth: true,
	Run: func(ctx *Context) {
		req := apiRequest(ctx.APIToken)
		req.Method = "GET"
		req.Uri = req.Uri + "/account"
		res, err := req.Do()
		ExitIfError(err)
		var doc map[string]bool
		res.Body.FromJsonTo(&doc)
		if doc["two_factor_authentication"] {
			Println("Two-factor authentication is enabled")
		} else {
			Println("Two-factor authentication is not enabled")
		}
	},
}

var twoFactorGenerateCmd = &Command{
	Topic:       "twofactor",
	Command:     "generate-recovery-codes",
	Description: "Generates and replaces recovery codes",
	NeedsAuth:   true,
	Run: func(ctx *Context) {
		req := apiRequest(ctx.APIToken)
		req.Method = "POST"
		req.Uri = req.Uri + "/account/recovery-codes"
		req.AddHeader("Heroku-Password", getPassword())
		req.AddHeader("Heroku-Two-Factor-Code", getString("Two-factor code: "))
		res, err := req.Do()
		ExitIfError(err)
		var codes []interface{}
		res.Body.FromJsonTo(&codes)
		Println("Recovery codes:")
		for _, code := range codes {
			Println(code)
		}
	},
}

var twoFactorDisableCmd = &Command{
	Topic:       "twofactor",
	Command:     "disable",
	Description: "Disable two-factor authentication for your account",
	NeedsAuth:   true,
	Run: func(ctx *Context) {
		req := apiRequest(ctx.APIToken)
		req.Method = "PATCH"
		req.Uri = req.Uri + "/account/"
		req.Body = map[string]interface{}{
			"two_factor_authentication": "false",
			"password":                  getPassword(),
		}
		res, err := req.Do()
		ExitIfError(err)
		if res.StatusCode != 200 {
			var doc map[string]string
			res.Body.FromJsonTo(&doc)
			Error(doc["message"])
			return
		}
		Println("disabled two-factor authentication")
	},
}
<|endoftext|>


<commit_before>package telebot

import "fmt"

func (b *Bot) sendText(to Recipient, text string, opt *SendOptions) (*Message, error) {
	params := map[string]string{
		"chat_id": to.Destination(),
		"text":    text,
	}
	embedSendOptions(params, opt)

	respJSON, err := b.sendCommand("sendMessage", params)
	if err != nil {
		return nil, err
	}

	return extractMsgResponse(respJSON)
}

func (p *Photo) Send(b *Bot, to Recipient, opt *SendOptions) (*Message, error) {
	params := map[string]string{
		"chat_id": to.Destination(),
		"caption": p.Caption,
	}

	embedSendOptions(params, opt)

	msg, err := b.sendObject(&p.File, "photo", params)
	if err != nil {
		return nil, err
	}

	thumbnails := msg.Photo
	fname := p.filename
	p.File = thumbnails[len(thumbnails)-1].File
	p.filename = fname

	return msg, nil
}

func (a *Audio) Send(b *Bot, to Recipient, opt *SendOptions) (*Message, error) {
	params := map[string]string{
		"chat_id": to.Destination(),
	}
	embedSendOptions(params, opt)

	msg, err := b.sendObject(&a.File, "audio", params)
	if err != nil {
		return nil, err
	}

	fname := a.filename
	*a = *msg.Audio
	a.filename = fname

	return msg, nil
}

func (d *Document) Send(b *Bot, to Recipient, opt *SendOptions) (*Message, error) {
	params := map[string]string{
		"chat_id": to.Destination(),
	}
	embedSendOptions(params, opt)

	msg, err := b.sendObject(&d.File, "audio", params)
	if err != nil {
		return nil, err
	}

	fname := d.filename
	*d = *msg.Document
	d.filename = fname

	return msg, nil
}

func (s *Sticker) Send(b *Bot, to Recipient, opt *SendOptions) (*Message, error) {
	params := map[string]string{
		"chat_id": to.Destination(),
	}
	embedSendOptions(params, opt)

	msg, err := b.sendObject(&s.File, "sticker", params)
	if err != nil {
		return nil, err
	}

	fname := s.filename
	*s = *msg.Sticker
	s.filename = fname

	return msg, nil
}

func (v *Video) Send(b *Bot, to Recipient, opt *SendOptions) (*Message, error) {
	params := map[string]string{
		"chat_id": to.Destination(),
	}
	embedSendOptions(params, opt)

	msg, err := b.sendObject(&v.File, "video", params)
	if err != nil {
		return nil, err
	}

	fname := v.filename
	*v = *msg.Video
	v.filename = fname

	return msg, nil
}

func (x *Location) Send(b *Bot, to Recipient, opt *SendOptions) (*Message, error) {
	params := map[string]string{
		"chat_id":   to.Destination(),
		"latitude":  fmt.Sprintf("%f", x.Latitude),
		"longitude": fmt.Sprintf("%f", x.Longitude),
	}
	embedSendOptions(params, opt)

	respJSON, err := b.sendCommand("sendLocation", params)
	if err != nil {
		return nil, err
	}

	return extractMsgResponse(respJSON)
}

func (v *Venue) Send(b *Bot, to Recipient, opt *SendOptions) (*Message, error) {
	params := map[string]string{
		"chat_id":       to.Destination(),
		"latitude":      fmt.Sprintf("%f", v.Location.Latitude),
		"longitude":     fmt.Sprintf("%f", v.Location.Longitude),
		"title":         v.Title,
		"address":       v.Address,
		"foursquare_id": v.FoursquareID,
	}
	embedSendOptions(params, opt)

	respJSON, err := b.sendCommand("sendLocation", params)
	if err != nil {
		return nil, err
	}

	return extractMsgResponse(respJSON)
}
<commit_msg>Typos in T.Send()<commit_after>package telebot

import "fmt"

func (b *Bot) sendText(to Recipient, text string, opt *SendOptions) (*Message, error) {
	params := map[string]string{
		"chat_id": to.Destination(),
		"text":    text,
	}
	embedSendOptions(params, opt)

	respJSON, err := b.sendCommand("sendMessage", params)
	if err != nil {
		return nil, err
	}

	return extractMsgResponse(respJSON)
}

func (p *Photo) Send(b *Bot, to Recipient, opt *SendOptions) (*Message, error) {
	params := map[string]string{
		"chat_id": to.Destination(),
		"caption": p.Caption,
	}

	embedSendOptions(params, opt)

	msg, err := b.sendObject(&p.File, "photo", params)
	if err != nil {
		return nil, err
	}

	thumbnails := msg.Photo
	fname := p.filename
	p.File = thumbnails[len(thumbnails)-1].File
	p.filename = fname

	return msg, nil
}

func (a *Audio) Send(b *Bot, to Recipient, opt *SendOptions) (*Message, error) {
	params := map[string]string{
		"chat_id": to.Destination(),
	}
	embedSendOptions(params, opt)

	msg, err := b.sendObject(&a.File, "audio", params)
	if err != nil {
		return nil, err
	}

	fname := a.filename
	*a = *msg.Audio
	a.filename = fname

	return msg, nil
}

func (d *Document) Send(b *Bot, to Recipient, opt *SendOptions) (*Message, error) {
	params := map[string]string{
		"chat_id": to.Destination(),
	}
	embedSendOptions(params, opt)

	msg, err := b.sendObject(&d.File, "document", params)
	if err != nil {
		return nil, err
	}

	fname := d.filename
	*d = *msg.Document
	d.filename = fname

	return msg, nil
}

func (s *Sticker) Send(b *Bot, to Recipient, opt *SendOptions) (*Message, error) {
	params := map[string]string{
		"chat_id": to.Destination(),
	}
	embedSendOptions(params, opt)

	msg, err := b.sendObject(&s.File, "sticker", params)
	if err != nil {
		return nil, err
	}

	fname := s.filename
	*s = *msg.Sticker
	s.filename = fname

	return msg, nil
}

func (v *Video) Send(b *Bot, to Recipient, opt *SendOptions) (*Message, error) {
	params := map[string]string{
		"chat_id": to.Destination(),
	}
	embedSendOptions(params, opt)

	msg, err := b.sendObject(&v.File, "video", params)
	if err != nil {
		return nil, err
	}

	fname := v.filename
	*v = *msg.Video
	v.filename = fname

	return msg, nil
}

func (x *Location) Send(b *Bot, to Recipient, opt *SendOptions) (*Message, error) {
	params := map[string]string{
		"chat_id":   to.Destination(),
		"latitude":  fmt.Sprintf("%f", x.Latitude),
		"longitude": fmt.Sprintf("%f", x.Longitude),
	}
	embedSendOptions(params, opt)

	respJSON, err := b.sendCommand("sendLocation", params)
	if err != nil {
		return nil, err
	}

	return extractMsgResponse(respJSON)
}

func (v *Venue) Send(b *Bot, to Recipient, opt *SendOptions) (*Message, error) {
	params := map[string]string{
		"chat_id":       to.Destination(),
		"latitude":      fmt.Sprintf("%f", v.Location.Latitude),
		"longitude":     fmt.Sprintf("%f", v.Location.Longitude),
		"title":         v.Title,
		"address":       v.Address,
		"foursquare_id": v.FoursquareID,
	}
	embedSendOptions(params, opt)

	respJSON, err := b.sendCommand("sendVenue", params)
	if err != nil {
		return nil, err
	}

	return extractMsgResponse(respJSON)
}
<|endoftext|>


<commit_before>package walnut

import (
	"testing"
	"time"
)

// test suite for ParseBool
var boolTests = []struct {
	in string
	b  bool
	ok bool
}{
	// truthy
	{"true", true, true},
	{"yes", true, true},
	{"on", true, true},

	// falsy
	{"false", false, true},
	{"no", false, true},
	{"off", false, true},

	// invalid
	{"", false, false},
	{"x", false, false},
	{"1", false, false},
}

func TestParseBool(test *testing.T) {
	h := "ParseBool(%#v) ->"

	for _, t := range boolTests {
		b, ok := ParseBool(t.in)

		switch {
		case ok != t.ok:
			test.Errorf(h+" ok != %#v", t.in, t.ok)
		case b != t.b:
			test.Errorf(h+" %#v, want %#v", t.in, b, t.b)
		}
	}
}

// test suite for ParseFloat
var floatTests = []struct {
	in string
	f  float64
	ok bool
}{
	// basic values
	{"1.3", 1.3, true},
	{"100.0", 100, true},
	{"38.002", 38.002, true},
	{"0.1", 0.1, true},

	// signs
	{"-0.0", 0, true},
	{"+1.0", 1, true},
	{"-2.0", -2, true},
	{"-0.1", -0.1, true},
	{"-3.4", -3.4, true},

	// long
	{"100000000000000000000000.0", 1e+23, true},
	{"99999999999999974834176.0", 9.999999999999997e+22, true},
	{"100000000000000000000001.0", 1.0000000000000001e+23, true},
	{"100000000000000008388608.0", 1.0000000000000001e+23, true},
	{"100000000000000016777215.0", 1.0000000000000001e+23, true},
	{"100000000000000016777216.0", 1.0000000000000003e+23, true},
	{"22.222222222222222", 22.22222222222222, true},
	{"-100000000000000000000000.0", -1e+23, true},
	{"-99999999999999974834176.0", -9.999999999999997e+22, true},
	{"-100000000000000000000001.0", -1.0000000000000001e+23, true},
	{"-100000000000000008388608.0", -1.0000000000000001e+23, true},
	{"-100000000000000016777215.0", -1.0000000000000001e+23, true},
	{"-100000000000000016777216.0", -1.0000000000000003e+23, true},
	{"-22.222222222222222", -22.22222222222222, true},

	// invalid
	{"1", 0, false},
	{"987654321", 0, false},
	{"123456700", 0, false},
	{"", 0, false},
	{"0.", 0, false},
	{".1", 0, false},
	{"1a", 0, false},
	{"0x30", 0, false},
	{"1.1.", 0, false},
	{"+-0", 0, false},
	{"-0-", 0, false},
}

func TestParseFloat(test *testing.T) {
	h := "ParseFloat(%#v) ->"

	for _, t := range floatTests {
		f, ok := ParseFloat(t.in)

		switch {
		case ok != t.ok:
			test.Errorf(h+" ok != %#v", t.in, t.ok)
		case f != t.f:
			test.Errorf(h+" %#v, want %#v", t.in, f, t.f)
		}
	}
}

var intTests = []struct {
	in string
	i  int64
	ok bool
}{
	// basic
	{"0", 0, true},
	{"1", 1, true},
	{"12345", 12345, true},
	{"012345", 12345, true},
	{"98765432100", 98765432100, true},

	// signs
	{"-0", 0, true},
	{"-1", -1, true},
	{"-12345", -12345, true},
	{"-012345", -12345, true},
	{"-98765432100", -98765432100, true},

	// long
	{"9223372036854775807", 1<<63 - 1, true},
	{"9223372036854775808", 1<<63 - 1, false},
	{"9223372036854775809", 1<<63 - 1, false},
	{"-9223372036854775807", -(1<<63 - 1), true},
	{"-9223372036854775808", -1 << 63, true},
	{"-9223372036854775809", -1 << 63, false},

	// invalid
	{"1.1", 0, false},
	{"987654321.0", 0, false},
	{"123456700.", 0, false},
	{"", 0, false},
	{".", 0, false},
	{".1", 0, false},
	{"1a", 0, false},
	{"0x30", 0, false},
	{"1.1.", 0, false},
	{"+-0", 0, false},
	{"-0-", 0, false},
}

func TestParseInt(test *testing.T) {
	h := "ParseInt(%#v) ->"

	for _, t := range intTests {
		i, ok := ParseInt(t.in)

		switch {
		case ok != t.ok:
			test.Errorf(h+" ok != %#v", t.in, t.ok)
		case i != t.i:
			test.Errorf(h+" %#v, want %#v", t.in, i, t.i)
		}
	}
}

// test suite for ParseString
var stringTests = []struct {
	in string
	s  string
	ok bool
}{
	// valid
	{`""`, "", true},
	{`"hello"`, "hello", true},
	{`"日本人"`, "日本人", true},
	{`"a\nb"`, "a\nb", true},
	{`"\u00FF"`, "ÿ", true},
	{`"\xFF"`, "\xFF", true},
	{`"\U00010111"`, "\U00010111", true},
	{`"\U0001011111"`, "\U0001011111", true},
	{`"'"`, "'", true},
	{`"\""`, "\"", true},

	// invalid
	{``, "", false},
	{`"lone`, "", false},
	{`hello`, "", false},
	{`"mismatch'`, "", false},
	{`"\"`, "", false},
	{`"\1"`, "", false},
	{`"\19"`, "", false},
	{`"\129"`, "", false},
	{"`a`", "", false},
	{"'b'", "", false},
}

func TestParseString(test *testing.T) {
	h := "ParseString(%#v) ->"

	for _, t := range stringTests {
		s, ok := ParseString(t.in)

		switch {
		case ok != t.ok:
			test.Errorf(h+" ok != %#v", t.in, t.ok)
		case s != t.s:
			test.Errorf(h+" %#v, want %#v", t.in, s, t.s)
		}
	}
}

// test suite for ParseTime
var timeTests = []struct {
	in string
	ok bool
}{
	// basic
	{"1970-01-01 00:00:00 +0000", true},
	{"2001-02-03 04:05:06 +0000", true},
	{"1997-08-28 15:30:27.123 +0000", true},
	{"1997-08-28 14:07:27 -0123", true},

	// invalid
	{"", false},
	{"01:02:03", false},
	{"1970-01-01", false},
	{"1970-01-01 00:00:00", false},
	{"1970-02-48 00:00:00 +0000", false},
	{"70-01-01 00:00:00", false},
	{"1970-01-01 00:00:00 UTC", false},
}

func TestParseTime(test *testing.T) {
	h := "ParseTime(%#v) ->"

	for _, t := range timeTests {
		e, _ := time.Parse("2006-01-02 15:04:05 -0700", t.in)
		a, ok := ParseTime(t.in)

		switch {
		case ok != t.ok:
			test.Errorf(h+" ok != %#v", t.in, t.ok)
		case !a.Equal(e):
			test.Errorf(h+" %s, want %s", t.in, a.String(), e.String())
		}
	}
}

// test suite for ParseDuration
var durationTests = []struct {
	in string
	d  time.Duration
	ok bool
}{
	// simple formats
	{"0s", 0, true},
	{"5s", 5 * time.Second, true},
	{"37s", 37 * time.Second, true},
	{"010s", 10 * time.Second, true},
	{"3d", 3 * 24 * time.Hour, true},

	// all units
	{"10ns", 10 * time.Nanosecond, true},
	{"10µs", 10 * time.Microsecond, true},
	{"10μs", 10 * time.Microsecond, true},
	{"10us", 10 * time.Microsecond, true},
	{"10ms", 10 * time.Millisecond, true},
	{"10s", 10 * time.Second, true},
	{"10m", 10 * time.Minute, true},
	{"10h", 10 * time.Hour, true},
	{"10d", 10 * 24 * time.Hour, true},
	{"10w", 10 * 7 * 24 * time.Hour, true},

	// mixed units
	{"1h1m1s", time.Hour + time.Minute + time.Second, true},
	{"4h30m", 4*time.Hour + 30*time.Minute, true},
	{"1s500ms", time.Second + 500*time.Millisecond, true},
	{"1w1d24h1440m", 10 * 24 * time.Hour, true},

	// allow (ignore) spaces between components
	{"1h 1m1s", time.Hour + time.Minute + time.Second, true},
	{"4h 30m", 4*time.Hour + 30*time.Minute, true},
	{"1s 500ms", time.Second + 500*time.Millisecond, true},
	{"1w 1d 24h 1440m", 10 * 24 * time.Hour, true},

	// disallow signs and decimal values
	{"-3h", 0, false},
	{"+5m", 0, false},
	{"300.5h", 0, false},
	{"1h 1m 1.3s", 0, false},
	{"10w -3d", 0, false},
	{"1.2d20m", 0, false},

	// units out of order
	{"1s2m", 0, false},
	{"1200ms 3s", 0, false},
	{"4h 5d 6w 7m", 0, false},

	// other invalid formats
	{"", 0, false},
	{"1sm", 0, false},
	{" 1h 1m1s", 0, false},
	{"4h  30m", 0, false},
	{"2 m 3 s", 0, false},
	{"4 d5 h", 0, false},
	{"100", 0, false},
	{"1d 200", 0, false},
	{"3 4 5ms", 0, false},
}

func TestParseDuration(test *testing.T) {
	h := "ParseDuration(%#v) ->"

	for _, t := range durationTests {
		d, ok := ParseDuration(t.in)

		switch {
		case ok != t.ok:
			test.Errorf(h+" ok != %#v", t.in, t.ok)
		case d != t.d:
			test.Errorf(h+" %#v, want %#v", t.in, d, t.d)
		}
	}
}
<commit_msg>Remove unnecessary comments from types_test.go<commit_after>package walnut

import (
	"testing"
	"time"
)

var boolTests = []struct {
	in string
	b  bool
	ok bool
}{
	{"true", true, true},
	{"yes", true, true},
	{"on", true, true},
	{"false", false, true},
	{"no", false, true},
	{"off", false, true},

	{"", false, false},
	{"x", false, false},
	{"1", false, false},
}

func TestParseBool(test *testing.T) {
	h := "ParseBool(%#v) ->"

	for _, t := range boolTests {
		b, ok := ParseBool(t.in)

		switch {
		case ok != t.ok:
			test.Errorf(h+" ok != %#v", t.in, t.ok)
		case b != t.b:
			test.Errorf(h+" %#v, want %#v", t.in, b, t.b)
		}
	}
}

var floatTests = []struct {
	in string
	f  float64
	ok bool
}{
	{"1.3", 1.3, true},
	{"100.0", 100, true},
	{"38.002", 38.002, true},
	{"0.1", 0.1, true},
	{"-0.0", 0, true},
	{"+1.0", 1, true},
	{"-2.0", -2, true},
	{"-0.1", -0.1, true},
	{"-3.4", -3.4, true},
	{"100000000000000000000000.0", 1e+23, true},
	{"99999999999999974834176.0", 9.999999999999997e+22, true},
	{"100000000000000000000001.0", 1.0000000000000001e+23, true},
	{"100000000000000008388608.0", 1.0000000000000001e+23, true},
	{"100000000000000016777215.0", 1.0000000000000001e+23, true},
	{"100000000000000016777216.0", 1.0000000000000003e+23, true},
	{"22.222222222222222", 22.22222222222222, true},
	{"-100000000000000000000000.0", -1e+23, true},
	{"-99999999999999974834176.0", -9.999999999999997e+22, true},
	{"-100000000000000000000001.0", -1.0000000000000001e+23, true},
	{"-100000000000000008388608.0", -1.0000000000000001e+23, true},
	{"-100000000000000016777215.0", -1.0000000000000001e+23, true},
	{"-100000000000000016777216.0", -1.0000000000000003e+23, true},
	{"-22.222222222222222", -22.22222222222222, true},

	{"1", 0, false},
	{"987654321", 0, false},
	{"123456700", 0, false},
	{"", 0, false},
	{"0.", 0, false},
	{".1", 0, false},
	{"1a", 0, false},
	{"0x30", 0, false},
	{"1.1.", 0, false},
	{"+-0", 0, false},
	{"-0-", 0, false},
}

func TestParseFloat(test *testing.T) {
	h := "ParseFloat(%#v) ->"

	for _, t := range floatTests {
		f, ok := ParseFloat(t.in)

		switch {
		case ok != t.ok:
			test.Errorf(h+" ok != %#v", t.in, t.ok)
		case f != t.f:
			test.Errorf(h+" %#v, want %#v", t.in, f, t.f)
		}
	}
}

var intTests = []struct {
	in string
	i  int64
	ok bool
}{
	{"0", 0, true},
	{"1", 1, true},
	{"12345", 12345, true},
	{"012345", 12345, true},
	{"98765432100", 98765432100, true},
	{"-0", 0, true},
	{"-1", -1, true},
	{"-12345", -12345, true},
	{"-012345", -12345, true},
	{"-98765432100", -98765432100, true},
	{"9223372036854775807", 1<<63 - 1, true},
	{"9223372036854775808", 1<<63 - 1, false},
	{"9223372036854775809", 1<<63 - 1, false},
	{"-9223372036854775807", -(1<<63 - 1), true},
	{"-9223372036854775808", -1 << 63, true},
	{"-9223372036854775809", -1 << 63, false},

	{"1.1", 0, false},
	{"987654321.0", 0, false},
	{"123456700.", 0, false},
	{"", 0, false},
	{".", 0, false},
	{".1", 0, false},
	{"1a", 0, false},
	{"0x30", 0, false},
	{"1.1.", 0, false},
	{"+-0", 0, false},
	{"-0-", 0, false},
}

func TestParseInt(test *testing.T) {
	h := "ParseInt(%#v) ->"

	for _, t := range intTests {
		i, ok := ParseInt(t.in)

		switch {
		case ok != t.ok:
			test.Errorf(h+" ok != %#v", t.in, t.ok)
		case i != t.i:
			test.Errorf(h+" %#v, want %#v", t.in, i, t.i)
		}
	}
}

var stringTests = []struct {
	in string
	s  string
	ok bool
}{
	{`""`, "", true},
	{`"hello"`, "hello", true},
	{`"日本人"`, "日本人", true},
	{`"a\nb"`, "a\nb", true},
	{`"\u00FF"`, "ÿ", true},
	{`"\xFF"`, "\xFF", true},
	{`"\U00010111"`, "\U00010111", true},
	{`"\U0001011111"`, "\U0001011111", true},
	{`"'"`, "'", true},
	{`"\""`, "\"", true},

	{``, "", false},
	{`"lone`, "", false},
	{`hello`, "", false},
	{`"mismatch'`, "", false},
	{`"\"`, "", false},
	{`"\1"`, "", false},
	{`"\19"`, "", false},
	{`"\129"`, "", false},
	{"`a`", "", false},
	{"'b'", "", false},
}

func TestParseString(test *testing.T) {
	h := "ParseString(%#v) ->"

	for _, t := range stringTests {
		s, ok := ParseString(t.in)

		switch {
		case ok != t.ok:
			test.Errorf(h+" ok != %#v", t.in, t.ok)
		case s != t.s:
			test.Errorf(h+" %#v, want %#v", t.in, s, t.s)
		}
	}
}

var timeTests = []struct {
	in string
	ok bool
}{
	{"1970-01-01 00:00:00 +0000", true},
	{"2001-02-03 04:05:06 +0000", true},
	{"1997-08-28 15:30:27.123 +0000", true},
	{"1997-08-28 14:07:27 -0123", true},

	{"", false},
	{"01:02:03", false},
	{"1970-01-01", false},
	{"1970-01-01 00:00:00", false},
	{"1970-02-48 00:00:00 +0000", false},
	{"70-01-01 00:00:00", false},
	{"1970-01-01 00:00:00 UTC", false},
}

func TestParseTime(test *testing.T) {
	h := "ParseTime(%#v) ->"

	for _, t := range timeTests {
		e, _ := time.Parse("2006-01-02 15:04:05 -0700", t.in)
		a, ok := ParseTime(t.in)

		switch {
		case ok != t.ok:
			test.Errorf(h+" ok != %#v", t.in, t.ok)
		case !a.Equal(e):
			test.Errorf(h+" %s, want %s", t.in, a.String(), e.String())
		}
	}
}

var durationTests = []struct {
	in string
	d  time.Duration
	ok bool
}{
	// simple formats
	{"0s", 0, true},
	{"5s", 5 * time.Second, true},
	{"37s", 37 * time.Second, true},
	{"010s", 10 * time.Second, true},
	{"3d", 3 * 24 * time.Hour, true},

	// all units
	{"10ns", 10 * time.Nanosecond, true},
	{"10µs", 10 * time.Microsecond, true},
	{"10μs", 10 * time.Microsecond, true},
	{"10us", 10 * time.Microsecond, true},
	{"10ms", 10 * time.Millisecond, true},
	{"10s", 10 * time.Second, true},
	{"10m", 10 * time.Minute, true},
	{"10h", 10 * time.Hour, true},
	{"10d", 10 * 24 * time.Hour, true},
	{"10w", 10 * 7 * 24 * time.Hour, true},

	// mixed units
	{"1h1m1s", time.Hour + time.Minute + time.Second, true},
	{"4h30m", 4*time.Hour + 30*time.Minute, true},
	{"1s500ms", time.Second + 500*time.Millisecond, true},
	{"1w1d24h1440m", 10 * 24 * time.Hour, true},

	// allow (ignore) spaces between components
	{"1h 1m1s", time.Hour + time.Minute + time.Second, true},
	{"4h 30m", 4*time.Hour + 30*time.Minute, true},
	{"1s 500ms", time.Second + 500*time.Millisecond, true},
	{"1w 1d 24h 1440m", 10 * 24 * time.Hour, true},

	// disallow signs and decimal values
	{"-3h", 0, false},
	{"+5m", 0, false},
	{"300.5h", 0, false},
	{"1h 1m 1.3s", 0, false},
	{"10w -3d", 0, false},
	{"1.2d20m", 0, false},

	// units out of order
	{"1s2m", 0, false},
	{"1200ms 3s", 0, false},
	{"4h 5d 6w 7m", 0, false},

	// other invalid formats
	{"", 0, false},
	{"1sm", 0, false},
	{" 1h 1m1s", 0, false},
	{"4h  30m", 0, false},
	{"2 m 3 s", 0, false},
	{"4 d5 h", 0, false},
	{"100", 0, false},
	{"1d 200", 0, false},
	{"3 4 5ms", 0, false},
}

func TestParseDuration(test *testing.T) {
	h := "ParseDuration(%#v) ->"

	for _, t := range durationTests {
		d, ok := ParseDuration(t.in)

		switch {
		case ok != t.ok:
			test.Errorf(h+" ok != %#v", t.in, t.ok)
		case d != t.d:
			test.Errorf(h+" %#v, want %#v", t.in, d, t.d)
		}
	}
}
<|endoftext|>


<commit_before>package scummatlas

import (
	"binaryutils"
	"fmt"
	"image"
	"image/color"
	"io/ioutil"
	"log"
	"os"
)

const debugImage = false

const (
	_ = iota
	METHOD_UNKNOWN
	METHOD_UNCOMPRESSED
	METHOD_ONE
	METHOD_TWO
	HORIZONTAL
	VERTICAL
	TRANSP
	NO_TRANSP
)

func parsePalette(data []byte) color.Palette {
	var palette = make(color.Palette, 0, 256)
	for i := 0; i < len(data); i += 3 {
		color := color.RGBA{
			data[i],
			data[i+1],
			data[i+2],
			255,
		}
		palette = append(palette, color)
	}

	return palette
}

func parseImage(data []byte, zBuffers int, width int, height int, pal color.Palette, transpIndex uint8) *image.RGBA {
	// DISABLE LOGGING
	log.SetOutput(os.Stdout)
	log.SetOutput(ioutil.Discard)

	if string(data[8:12]) != "SMAP" {
		panic("No stripe table found")
	}

	stripeCount := width / 8
	log.Println("SmapSize", BE32(data, 12))

	log.Println("There should be ", stripeCount, "stripes")
	offsets := make([]int, 0, stripeCount)
	stripeOffset := 0
	for i := 0; i < stripeCount; i++ {
		stripeOffset = LE32(data, 16+4*i)
		offsets = append(offsets, stripeOffset)
	}

	img := image.NewRGBA(image.Rect(0, 0, width, height))

	log.Println("Decoding image")
	log.Println("Stripes information")
	log.Println("\n#ID\tCode\tMethod\tDirect\tPalInSz\tTransp")
	for i := 0; i < stripeCount; i++ {
		offset := offsets[i]
		size := len(data) - offset
		if i < stripeCount-1 {
			size = offsets[i+1] - offsets[i]
		}
		drawStripe(img, i, data[8+offset:8+offset+size], pal, transpIndex)
	}
	log.Println("image decoded")
	log.SetOutput(os.Stdout)
	return img
}

func drawStripe(img *image.RGBA, stripNumber int, data []byte, pal color.Palette, transpIndex uint8) {

	method, direction, transparent, paletteLength := getCompressionMethod(stripNumber, data[0])

	height := img.Rect.Size().Y
	totalPixels := 8 * height
	currentPixel := 0

	curPal := uint8(data[1])
	curSubs := uint8(1)

	setColor := func() {
		var x, y int
		if direction == HORIZONTAL {
			x = currentPixel % 8
			y = (currentPixel + 1) / 8
		} else {
			y = currentPixel % height
			x = currentPixel / height
		}
		if x == 7 {
			x = -1
		}
		x += 8 * stripNumber
		if curPal >= 0 {
			if transparent == TRANSP && curPal == transpIndex {
				log.Println("TRANSPARENT")
				img.Set(x, y, color.RGBA{0, 0, 0, 0})
			} else {
				img.Set(x, y, pal[curPal])
			}
		} else {
			panic("Out of palette")
		}
		currentPixel++
	}

	bs := binaryutils.NewBitStream(data[2:])

	if method == METHOD_TWO {
		for currentPixel < totalPixels {
			if bs.GetBit() == 1 {
				if bs.GetBit() == 1 {
					palShift := bs.GetBits(3) - 4
					if palShift == 0 {
						length := int(bs.GetBits(8))
						for i := 0; i < length; i++ {
							setColor()
						}
					} else {
						curPal += palShift
						setColor()
					}
				} else { // 10 Read new palette index
					curPal = bs.GetBits(paletteLength)
					setColor()
				}
			} else { // 0 Draw next pixel with current palette index
				setColor()
			}
		}
	} else {
		for currentPixel < totalPixels {
			if bs.GetBit() == 1 {
				if bs.GetBit() == 1 {
					if bs.GetBit() == 1 {
						//Negate the subtraction variable. Subtract it from the palette index, and draw the next pixel.
						curSubs = -curSubs
					}
					//Subtract the subtraction variable from the palette index, and draw the next pixel.
					curPal -= curSubs
				} else { //10, read new palette index
					curPal = bs.GetBits(paletteLength)
				}
			} else { // 0, draw next pixel with current palette index
			}
			setColor()
		}
	}
}

func getCompressionMethod(stripNumber int, code byte) (
	method int,
	direction int,
	transparent int,
	paletteLength uint8) {
	var substraction byte

	direction = HORIZONTAL
	method = METHOD_UNKNOWN
	transparent = NO_TRANSP
	substraction = 0x00
	switch {
	case code == 0x01:
		method = METHOD_UNCOMPRESSED
	case 0x0e <= code && code <= 0x12:
		direction = VERTICAL
		method = METHOD_ONE
		substraction = 0x0a
	case 0x18 <= code && code <= 0x1c:
		method = METHOD_ONE
		substraction = 0x14
	case 0x22 <= code && code <= 0x26:
		method = METHOD_ONE
		direction = VERTICAL
		transparent = TRANSP
		substraction = 0x1e
	case 0x2c <= code && code <= 0x30:
		method = METHOD_ONE
		transparent = TRANSP
		substraction = 0x28
	case 0x40 <= code && code <= 0x44:
		method = METHOD_TWO
		substraction = 0x3c
	case 0x54 <= code && code <= 0x58:
		transparent = TRANSP
		method = METHOD_TWO
		substraction = 0x51
	case 0x68 <= code && code <= 0x6c:
		transparent = TRANSP
		method = METHOD_TWO
		substraction = 0x64
	case 0x7c <= code && code <= 0x80:
		method = METHOD_TWO
		substraction = 0x78
	}
	out := fmt.Sprintf("%v\t0x%X\t", stripNumber, code)
	if method == METHOD_ONE {
		out += "   1"
	} else if method == METHOD_TWO {
		out += "   2"
	}
	if direction == VERTICAL {
		out += "\tVert"
	} else {
		out += "\tHoriz"
	}
	out += "\t" + string(code-substraction)
	if transparent == TRANSP {
		out += "\tYes"
	} else {
		out += "\tNo"
	}
	log.Println(out)
	return method, direction, transparent, code - substraction
}
<commit_msg>cleanup compression method<commit_after>package scummatlas

import (
	"binaryutils"
	"fmt"
	"image"
	"image/color"
	"io/ioutil"
	"log"
	"os"
)

const debugImage = false

const (
	_ = iota
	METHOD_UNKNOWN
	METHOD_UNCOMPRESSED
	METHOD_ONE
	METHOD_TWO
	HORIZONTAL
	VERTICAL
	TRANSP
	NO_TRANSP
)

func parsePalette(data []byte) color.Palette {
	var palette = make(color.Palette, 0, 256)
	for i := 0; i < len(data); i += 3 {
		color := color.RGBA{
			data[i],
			data[i+1],
			data[i+2],
			255,
		}
		palette = append(palette, color)
	}

	return palette
}

func parseImage(data []byte, zBuffers int, width int, height int, pal color.Palette, transpIndex uint8) *image.RGBA {
	// DISABLE LOGGING
	log.SetOutput(os.Stdout)
	log.SetOutput(ioutil.Discard)

	if string(data[8:12]) != "SMAP" {
		panic("No stripe table found")
	}

	stripeCount := width / 8
	log.Println("SmapSize", BE32(data, 12))

	log.Println("There should be ", stripeCount, "stripes")
	offsets := make([]int, 0, stripeCount)
	stripeOffset := 0
	for i := 0; i < stripeCount; i++ {
		stripeOffset = LE32(data, 16+4*i)
		offsets = append(offsets, stripeOffset)
	}

	img := image.NewRGBA(image.Rect(0, 0, width, height))

	log.Println("Decoding image")
	log.Println("Stripes information")
	log.Println("\n#ID\tCode\tMethod\tDirect\tPalInSz\tTransp")
	for i := 0; i < stripeCount; i++ {
		offset := offsets[i]
		size := len(data) - offset
		if i < stripeCount-1 {
			size = offsets[i+1] - offsets[i]
		}
		drawStripe(img, i, data[8+offset:8+offset+size], pal, transpIndex)
	}
	log.Println("image decoded")
	log.SetOutput(os.Stdout)
	return img
}

func drawStripe(img *image.RGBA, stripNumber int, data []byte, pal color.Palette, transpIndex uint8) {

	method, direction, transparent, paletteLength := getCompressionMethod(stripNumber, data[0])

	height := img.Rect.Size().Y
	totalPixels := 8 * height
	currentPixel := 0

	curPal := uint8(data[1])
	curSubs := uint8(1)

	setColor := func() {
		var x, y int
		if direction == HORIZONTAL {
			x = currentPixel % 8
			y = (currentPixel + 1) / 8
		} else {
			y = currentPixel % height
			x = currentPixel / height
		}
		if x == 7 {
			x = -1
		}
		x += 8 * stripNumber
		if curPal >= 0 {
			if transparent == TRANSP && curPal == transpIndex {
				log.Println("TRANSPARENT")
				img.Set(x, y, color.RGBA{0, 0, 0, 0})
			} else {
				img.Set(x, y, pal[curPal])
			}
		} else {
			panic("Out of palette")
		}
		currentPixel++
	}

	bs := binaryutils.NewBitStream(data[2:])

	if method == METHOD_TWO {
		for currentPixel < totalPixels {
			if bs.GetBit() == 1 {
				if bs.GetBit() == 1 {
					palShift := bs.GetBits(3) - 4
					if palShift == 0 {
						length := int(bs.GetBits(8))
						for i := 0; i < length; i++ {
							setColor()
						}
					} else {
						curPal += palShift
						setColor()
					}
				} else { // 10 Read new palette index
					curPal = bs.GetBits(paletteLength)
					setColor()
				}
			} else { // 0 Draw next pixel with current palette index
				setColor()
			}
		}
	} else {
		for currentPixel < totalPixels {
			if bs.GetBit() == 1 {
				if bs.GetBit() == 1 {
					if bs.GetBit() == 1 {
						//Negate the subtraction variable. Subtract it from the palette index, and draw the next pixel.
						curSubs = -curSubs
					}
					//Subtract the subtraction variable from the palette index, and draw the next pixel.
					curPal -= curSubs
				} else { //10, read new palette index
					curPal = bs.GetBits(paletteLength)
				}
			} else { // 0, draw next pixel with current palette index
			}
			setColor()
		}
	}
}

func GetCompressionMethod(stripNumber int, code byte) (
	method int,
	direction int,
	transparent int,
	paletteLength uint8) {
	var substraction byte

	direction = VERTICAL
	method = METHOD_UNKNOWN
	transparent = NO_TRANSP
	substraction = 0x00
	if code == 0x01 {
		method = METHOD_UNCOMPRESSED
	} else if code <= 0x40 {
		method = METHOD_ONE
	} else if code <= 0x80 {
		method = METHOD_TWO
	} else {
		panic("Unknown method " + string(method) + " in stripe " + string(stripNumber))
	}

	if (code >= 0x22 && code <= 0x40) ||
		(code >= 0x54 && code <= 0x6c) {
		transparent = TRANSP
	}

	if (code >= 0x18 && code < 0x1c) || code >= 0x2c {
		direction = HORIZONTAL
	}

	switch {
	case 0x0e <= code && code <= 0x12:
		substraction = 0x0a
	case 0x18 <= code && code <= 0x1c:
		substraction = 0x14
	case 0x22 <= code && code <= 0x26:
		substraction = 0x1e
	case 0x2c <= code && code <= 0x30:
		substraction = 0x28
	case 0x40 <= code && code <= 0x44:
		substraction = 0x3c
	case 0x54 <= code && code <= 0x58:
		substraction = 0x51
	case 0x68 <= code && code <= 0x6c:
		substraction = 0x64
	case 0x7c <= code && code <= 0x80:
		substraction = 0x78
	}
	out := fmt.Sprintf("%v\t0x%X\t", stripNumber, code)
	if method == METHOD_ONE {
		out += "   1"
	} else if method == METHOD_TWO {
		out += "   2"
	}
	if direction == VERTICAL {
		out += "\tVert"
	} else {
		out += "\tHoriz"
	}
	out += "\t" + string(code-substraction)
	if transparent == TRANSP {
		out += "\tYes"
	} else {
		out += "\tNo"
	}
	log.Println(out)
	return method, direction, transparent, code - substraction
}
<|endoftext|>


<commit_before>package structhash

import (
	"crypto/md5"
	"crypto/sha1"
	"fmt"
	"reflect"
	"sort"
	"strconv"
	"strings"
)

// Version returns the version of the supplied hash as an integer
// or -1 on failure
func Version(h string) int {
	if h == "" {
		return -1
	}
	if h[0] != 'v' {
		return -1
	}
	if spos := strings.IndexRune(h[1:], '_'); spos >= 0 {
		n, e := strconv.Atoi(h[1 : spos+1])
		if e != nil {
			return -1
		}
		return n
	}
	return -1
}

// Hash takes a data structure and returns a hash string of that data structure
// at the version asked.
//
// This function uses md5 hashing function and default formatter. See also Dump()
// function.
func Hash(c interface{}, version int) (string, error) {
	return fmt.Sprintf("v%d_%x", version, Md5(c, version)), nil
}

// Dump takes a data structure and returns its byte representation. This can be
// useful if you need to use your own hashing function or formatter.
func Dump(c interface{}, version int) []byte {
	return []byte(serialize(c, version))
}

// Md5 takes a data structure and returns its md5 hash.
// This is a shorthand for md5.Sum(Dump(c, version)).
func Md5(c interface{}, version int) []byte {
	sum := md5.Sum(Dump(c, version))
	return sum[:]
}

// Sha1 takes a data structure and returns its sha1 hash.
// This is a shorthand for sha1.Sum(Dump(c, version)).
func Sha1(c interface{}, version int) []byte {
	sum := sha1.Sum(Dump(c, version))
	return sum[:]
}

type structFieldFilter func(reflect.StructField) (string, bool)

func strValue(val reflect.Value, depth int, fltr structFieldFilter) string {
	switch val.Kind() {
	case reflect.String:
		return "\"" + val.String() + "\""
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return fmt.Sprintf("%d", val.Int())
	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return fmt.Sprintf("%d", val.Uint())
	case reflect.Bool:
		if val.Bool() {
			return "true"
		}
		return "false"
	case reflect.Ptr:
		if !val.IsNil() || val.Type().Elem().Kind() == reflect.Struct {
			return strValue(reflect.Indirect(val), depth, fltr)
		} else {
			return strValue(reflect.Zero(val.Type().Elem()), depth, fltr)
		}
	case reflect.Array, reflect.Slice:
		len := val.Len()
		ret := "["
		for i := 0; i < len; i++ {
			if i != 0 {
				ret = ret + ", "
			}
			ret = ret + strValue(val.Index(i), depth+1, fltr)
		}
		ret = ret + "]"
		return ret
	case reflect.Map:
		len := val.Len()
		mk := val.MapKeys()
		strmap := make(map[string]string, len)
		// Map/serialize all values
		for i := 0; i < len; i++ {
			strmap[strValue(mk[i], depth+1, fltr)] =
				strValue(val.MapIndex(mk[i]), depth+1, fltr)
		}

		// Create array to hold map keys and sort them
		skey := make([]string, 0, len)
		for k := range strmap {
			skey = append(skey, k)
		}
		sort.Strings(skey)

		ret := "["
		for i := 0; i < len; i++ {
			if i != 0 {
				ret = ret + ", "
			}
			ret = ret + skey[i] + ": " + strmap[skey[i]]
		}
		ret = ret + "]"
		return ret
	case reflect.Struct:
		vtype := val.Type()
		flen := vtype.NumField()
		smap := make(map[string]string)
		// Get all fields
		for i := 0; i < flen; i++ {
			field := vtype.Field(i)
			name := field.Name
			if fltr != nil {
				if newname, ok := fltr(field); ok {
					name = newname
				} else {
					continue
				}
			}
			fval := val.Field(i)
			smap[name] = strValue(fval, depth+1, fltr)
		}
		// Get the keys and sort them
		skey := make([]string, len(smap))
		c := 0
		for k := range smap {
			skey[c] = k
			c++
		}
		sort.Strings(skey)

		flen = len(skey)
		ret := "{\n"
		for i := 0; i < flen; i++ {
			if i != 0 {
				ret = ret + ", \n"
			}
			ret = ret + strings.Repeat("  ", depth+1) + "\"" + skey[i] + "\": "
			ret = ret + smap[skey[i]]
		}
		return ret + "\n" + strings.Repeat("  ", depth) + "}"
	default:
		return val.String()
	}
}

func serialize(object interface{}, version int) string {
	return strValue(reflect.ValueOf(object), 0, func(f reflect.StructField) (string, bool) {
		var err error
		name := f.Name
		ver := 0
		lastver := -1
		if str := f.Tag.Get("lastversion"); str != "" {
			if lastver, err = strconv.Atoi(str); err != nil {
				return "", false
			}
		}
		if str := f.Tag.Get("version"); str != "" {
			if ver, err = strconv.Atoi(str); err != nil {
				return "", false
			}
		}
		if str := f.Tag.Get("hash"); str != "" {
			if str == "-" {
				return "", false
			}
			for _, tag := range strings.Split(str, " ") {
				args := strings.Split(strings.TrimSpace(tag), ":")
				if len(args) != 2 {
					return "", false
				}
				switch args[0] {
				case "name":
					name = args[1]
				case "version":
					if ver, err = strconv.Atoi(args[1]); err != nil {
						return "", false
					}
				case "lastversion":
					if lastver, err = strconv.Atoi(args[1]); err != nil {
						return "", false
					}
				}
			}
		}
		if lastver != -1 && lastver < version {
			return "", false
		}
		if ver > version {
			return "", false
		}
		return name, true
	}) + "\n"
}
<commit_msg>use bytes.Buffer instead of string concatenation<commit_after>package structhash

import (
	"bytes"
	"crypto/md5"
	"crypto/sha1"
	"fmt"
	"reflect"
	"sort"
	"strconv"
	"strings"
)

// Version returns the version of the supplied hash as an integer
// or -1 on failure
func Version(h string) int {
	if h == "" {
		return -1
	}
	if h[0] != 'v' {
		return -1
	}
	if spos := strings.IndexRune(h[1:], '_'); spos >= 0 {
		n, e := strconv.Atoi(h[1 : spos+1])
		if e != nil {
			return -1
		}
		return n
	}
	return -1
}

// Hash takes a data structure and returns a hash string of that data structure
// at the version asked.
//
// This function uses md5 hashing function and default formatter. See also Dump()
// function.
func Hash(c interface{}, version int) (string, error) {
	return fmt.Sprintf("v%d_%x", version, Md5(c, version)), nil
}

// Dump takes a data structure and returns its byte representation. This can be
// useful if you need to use your own hashing function or formatter.
func Dump(c interface{}, version int) []byte {
	return serialize(c, version)
}

// Md5 takes a data structure and returns its md5 hash.
// This is a shorthand for md5.Sum(Dump(c, version)).
func Md5(c interface{}, version int) []byte {
	sum := md5.Sum(Dump(c, version))
	return sum[:]
}

// Sha1 takes a data structure and returns its sha1 hash.
// This is a shorthand for sha1.Sum(Dump(c, version)).
func Sha1(c interface{}, version int) []byte {
	sum := sha1.Sum(Dump(c, version))
	return sum[:]
}

type item struct {
	name  string
	value reflect.Value
}

type itemSorter []item

func (s itemSorter) Len() int {
	return len(s)
}

func (s itemSorter) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func (s itemSorter) Less(i, j int) bool {
	return s[i].name < s[j].name
}

type structFieldFilter func(reflect.StructField) (string, bool)

func writeValue(buf *bytes.Buffer, val reflect.Value, depth int, fltr structFieldFilter) {
	switch val.Kind() {
	case reflect.String:
		buf.WriteByte('"')
		buf.WriteString(val.String())
		buf.WriteByte('"')
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		buf.WriteString(strconv.FormatInt(val.Int(), 10))
	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		buf.WriteString(strconv.FormatUint(val.Uint(), 10))
	case reflect.Bool:
		if val.Bool() {
			buf.WriteString("true")
		} else {
			buf.WriteString("false")
		}
	case reflect.Ptr:
		if !val.IsNil() || val.Type().Elem().Kind() == reflect.Struct {
			writeValue(buf, reflect.Indirect(val), depth, fltr)
		} else {
			writeValue(buf, reflect.Zero(val.Type().Elem()), depth, fltr)
		}
	case reflect.Array, reflect.Slice:
		buf.WriteByte('[')
		len := val.Len()
		for i := 0; i < len; i++ {
			if i != 0 {
				buf.WriteString(", ")
			}
			writeValue(buf, val.Index(i), depth+1, fltr)
		}
		buf.WriteByte(']')
	case reflect.Map:
		mk := val.MapKeys()
		items := make([]item, len(mk), len(mk))
		// Get all values
		for i, _ := range items {
			items[i].name = strValue(mk[i], depth+1, fltr)
			items[i].value = val.MapIndex(mk[i])
		}

		// Sort values by key
		sort.Sort(itemSorter(items))

		buf.WriteByte('[')
		for i, _ := range items {
			if i != 0 {
				buf.WriteString(", ")
			}
			buf.WriteString(items[i].name)
			buf.WriteString(": ")
			writeValue(buf, items[i].value, depth+1, fltr)
		}
		buf.WriteByte(']')
	case reflect.Struct:
		vtype := val.Type()
		flen := vtype.NumField()
		items := make([]item, 0, flen)
		// Get all fields
		for i := 0; i < flen; i++ {
			field := vtype.Field(i)
			it := item{field.Name, val.Field(i)}
			if fltr != nil {
				if name, ok := fltr(field); ok {
					it.name = name
				} else {
					continue
				}
			}
			items = append(items, it)
		}
		// Sort fields by name
		sort.Sort(itemSorter(items))

		buf.WriteString("{\n")
		for i, _ := range items {
			if i != 0 {
				buf.WriteString(", \n")
			}
			for i := 0; i < depth+1; i++ {
				buf.WriteString("  ")
			}
			buf.WriteByte('"')
			buf.WriteString(items[i].name)
			buf.WriteString("\": ")
			writeValue(buf, items[i].value, depth+1, fltr)
		}
		buf.WriteByte('\n')
		for i := 0; i < depth; i++ {
			buf.WriteString("  ")
		}
		buf.WriteByte('}')
	default:
		buf.WriteString(val.String())
	}
}

func strValue(val reflect.Value, depth int, fltr structFieldFilter) string {
	switch val.Kind() {
	case reflect.String:
		return "\"" + val.Interface().(string) + "\""
	default:
		buf := new(bytes.Buffer)
		writeValue(buf, val, depth, fltr)
		return string(buf.Bytes())
	}
}

func filterField(f reflect.StructField, version int) (string, bool) {
	var err error
	name := f.Name
	ver := 0
	lastver := -1
	if str := f.Tag.Get("hash"); str != "" {
		if str == "-" {
			return "", false
		}
		for _, tag := range strings.Split(str, " ") {
			args := strings.Split(strings.TrimSpace(tag), ":")
			if len(args) != 2 {
				return "", false
			}
			switch args[0] {
			case "name":
				name = args[1]
			case "version":
				if ver, err = strconv.Atoi(args[1]); err != nil {
					return "", false
				}
			case "lastversion":
				if lastver, err = strconv.Atoi(args[1]); err != nil {
					return "", false
				}
			}
		}
	} else {
		if str := f.Tag.Get("lastversion"); str != "" {
			if lastver, err = strconv.Atoi(str); err != nil {
				return "", false
			}
		}
		if str := f.Tag.Get("version"); str != "" {
			if ver, err = strconv.Atoi(str); err != nil {
				return "", false
			}
		}
	}
	if lastver != -1 && lastver < version {
		return "", false
	}
	if ver > version {
		return "", false
	}
	return name, true
}

func serialize(object interface{}, version int) []byte {
	s := strValue(reflect.ValueOf(object), 0,
		func(f reflect.StructField) (string, bool) {
			return filterField(f, version)
		})
	return []byte(s+"\n")
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"strconv"
	"time"

	"github.com/estafette/estafette-ci-contracts"

	"github.com/rs/zerolog/log"
	"github.com/sethgrid/pester"
)

// EndOfLifeHelper has methods to shutdown the runner after a fatal or successful run
type EndOfLifeHelper interface {
	handleFatal(contracts.BuildLog, error, string)
	sendBuildFinishedEvent(buildStatus string) error
	sendBuildCleanEvent(buildStatus string) error
	sendBuildJobLogEvent(buildLog contracts.BuildLog) error
}

type endOfLifeHelperImpl struct {
	runAsJob bool
	config   contracts.BuilderConfig
}

// NewEndOfLifeHelper returns a new EndOfLifeHelper
func NewEndOfLifeHelper(runAsJob bool, config contracts.BuilderConfig) EndOfLifeHelper {
	return &endOfLifeHelperImpl{
		runAsJob: runAsJob,
		config:   config,
	}
}

func (elh *endOfLifeHelperImpl) handleFatal(buildLog contracts.BuildLog, err error, message string) {

	// add error messages as step to show in logs
	fatalStep := contracts.BuildLogStep{
		Step:     "init",
		LogLines: []contracts.BuildLogLine{},
		ExitCode: -1,
		Status:   "failed",
	}
	lineNumber := 1

	if err != nil {
		fatalStep.LogLines = append(fatalStep.LogLines, contracts.BuildLogLine{
			LineNumber: lineNumber,
			Timestamp:  time.Now().UTC(),
			StreamType: "stderr",
			Text:       err.Error(),
		})
		lineNumber++
	}
	if message != "" {
		fatalStep.LogLines = append(fatalStep.LogLines, contracts.BuildLogLine{
			LineNumber: lineNumber,
			Timestamp:  time.Now().UTC(),
			StreamType: "stderr",
			Text:       message,
		})
		lineNumber++
	}

	buildLog.Steps = append(buildLog.Steps, fatalStep)

	_ = elh.sendBuildFinishedEvent("failed")
	_ = elh.sendBuildJobLogEvent(buildLog)
	_ = elh.sendBuildCleanEvent("failed")

	if elh.runAsJob {
		log.Error().Err(err).Msg(message)
		os.Exit(0)
	} else {
		log.Fatal().Err(err).Msg(message)
	}
}

func (elh *endOfLifeHelperImpl) sendBuildJobLogEvent(buildLog contracts.BuildLog) (err error) {

	ciServerBuilderPostLogsURL := elh.config.CIServer.PostLogsURL
	ciAPIKey := elh.config.CIServer.APIKey
	jobName := *elh.config.JobName

	if ciServerBuilderPostLogsURL != "" && ciAPIKey != "" && jobName != "" {

		// convert BuildJobLogs to json
		var requestBody io.Reader

		var data []byte
		if *elh.config.Action == "release" {
			// copy buildLog to releaseLog and marshal that
			releaseLog := contracts.ReleaseLog{
				ID:         buildLog.ID,
				RepoSource: buildLog.RepoSource,
				RepoOwner:  buildLog.RepoOwner,
				RepoName:   buildLog.RepoName,
				ReleaseID:  strconv.Itoa(elh.config.ReleaseParams.ReleaseID),
				Steps:      buildLog.Steps,
				InsertedAt: buildLog.InsertedAt,
			}
			data, err = json.Marshal(releaseLog)
			if err != nil {
				log.Error().Err(err).Msgf("Failed marshalling ReleaseLog for job %v", jobName)
				return
			}
		} else {
			data, err = json.Marshal(buildLog)
			if err != nil {
				log.Error().Err(err).Msgf("Failed marshalling BuildLog for job %v", jobName)
				return
			}
		}

		requestBody = bytes.NewReader(data)

		// create client, in order to add headers
		client := pester.New()
		client.MaxRetries = 5
		client.Backoff = pester.ExponentialJitterBackoff
		client.KeepLog = true
		client.Timeout = time.Duration(120) * time.Second
		request, err := http.NewRequest("POST", ciServerBuilderPostLogsURL, requestBody)
		if err != nil {
			log.Error().Err(err).Msgf("Failed creating http client for job %v", jobName)
			return err
		}

		// add headers
		request.Header.Add("Authorization", fmt.Sprintf("Bearer %v", ciAPIKey))
		request.Header.Add("Content-Type", "application/json")

		// perform actual request
		response, err := client.Do(request)
		if err != nil {
			log.Error().Err(err).Str("logs", client.LogString()).Msgf("Failed shipping logs to %v for job %v", ciServerBuilderPostLogsURL, jobName)
			return err
		}

		defer response.Body.Close()

		log.Debug().Str("logs", client.LogString()).Msgf("Successfully shipped logs to %v for job %v", ciServerBuilderPostLogsURL, jobName)
	}

	return nil
}

func (elh *endOfLifeHelperImpl) sendBuildFinishedEvent(buildStatus string) error {
	return elh.sendBuilderEvent(buildStatus, fmt.Sprintf("builder:%v", buildStatus))
}

func (elh *endOfLifeHelperImpl) sendBuildCleanEvent(buildStatus string) error {
	return elh.sendBuilderEvent(buildStatus, "builder:clean")
}

func (elh *endOfLifeHelperImpl) sendBuilderEvent(buildStatus, event string) (err error) {

	ciServerBuilderEventsURL := elh.config.CIServer.BuilderEventsURL
	ciAPIKey := elh.config.CIServer.APIKey
	jobName := *elh.config.JobName

	if ciServerBuilderEventsURL != "" && ciAPIKey != "" && jobName != "" {
		// convert EstafetteCiBuilderEvent to json
		var requestBody io.Reader

		buildID := ""
		if elh.config.BuildParams != nil {
			buildID = strconv.Itoa(elh.config.BuildParams.BuildID)
		}

		releaseID := ""
		if elh.config.ReleaseParams != nil {
			releaseID = strconv.Itoa(elh.config.ReleaseParams.ReleaseID)
		}

		ciBuilderEvent := EstafetteCiBuilderEvent{
			JobName:      jobName,
			RepoSource:   elh.config.Git.RepoSource,
			RepoOwner:    elh.config.Git.RepoOwner,
			RepoName:     elh.config.Git.RepoName,
			RepoBranch:   elh.config.Git.RepoBranch,
			RepoRevision: elh.config.Git.RepoRevision,
			ReleaseID:    releaseID,
			BuildID:      buildID,
			BuildStatus:  buildStatus,
		}
		data, err := json.Marshal(ciBuilderEvent)
		if err != nil {
			log.Error().Err(err).Msgf("Failed marshalling EstafetteCiBuilderEvent for job %v", jobName)
			return err
		}
		requestBody = bytes.NewReader(data)

		// create client, in order to add headers
		client := pester.New()
		client.MaxRetries = 5
		client.Backoff = pester.ExponentialJitterBackoff
		client.KeepLog = true
		client.Timeout = time.Duration(10) * time.Second
		request, err := http.NewRequest("POST", ciServerBuilderEventsURL, requestBody)
		if err != nil {
			log.Error().Err(err).Msgf("Failed creating http client for job %v", jobName)
			return err
		}

		// add headers
		request.Header.Add("X-Estafette-Event", event)
		request.Header.Add("X-Estafette-Event-Job-Name", jobName)
		request.Header.Add("Authorization", fmt.Sprintf("Bearer %v", ciAPIKey))

		// perform actual request
		response, err := client.Do(request)
		if err != nil {
			log.Error().Err(err).Str("pesterLogs", client.LogString()).Msgf("Failed performing http request to %v for job %v", ciServerBuilderEventsURL, jobName)
			return err
		}

		defer response.Body.Close()

		log.Debug().Str("pesterLogs", client.LogString()).Str("url", ciServerBuilderEventsURL).Msgf("Succesfully sent %v event to api", event)
	}

	return nil
}
<commit_msg>nicer timeout specification<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"strconv"
	"time"

	"github.com/estafette/estafette-ci-contracts"

	"github.com/rs/zerolog/log"
	"github.com/sethgrid/pester"
)

// EndOfLifeHelper has methods to shutdown the runner after a fatal or successful run
type EndOfLifeHelper interface {
	handleFatal(contracts.BuildLog, error, string)
	sendBuildFinishedEvent(buildStatus string) error
	sendBuildCleanEvent(buildStatus string) error
	sendBuildJobLogEvent(buildLog contracts.BuildLog) error
}

type endOfLifeHelperImpl struct {
	runAsJob bool
	config   contracts.BuilderConfig
}

// NewEndOfLifeHelper returns a new EndOfLifeHelper
func NewEndOfLifeHelper(runAsJob bool, config contracts.BuilderConfig) EndOfLifeHelper {
	return &endOfLifeHelperImpl{
		runAsJob: runAsJob,
		config:   config,
	}
}

func (elh *endOfLifeHelperImpl) handleFatal(buildLog contracts.BuildLog, err error, message string) {

	// add error messages as step to show in logs
	fatalStep := contracts.BuildLogStep{
		Step:     "init",
		LogLines: []contracts.BuildLogLine{},
		ExitCode: -1,
		Status:   "failed",
	}
	lineNumber := 1

	if err != nil {
		fatalStep.LogLines = append(fatalStep.LogLines, contracts.BuildLogLine{
			LineNumber: lineNumber,
			Timestamp:  time.Now().UTC(),
			StreamType: "stderr",
			Text:       err.Error(),
		})
		lineNumber++
	}
	if message != "" {
		fatalStep.LogLines = append(fatalStep.LogLines, contracts.BuildLogLine{
			LineNumber: lineNumber,
			Timestamp:  time.Now().UTC(),
			StreamType: "stderr",
			Text:       message,
		})
		lineNumber++
	}

	buildLog.Steps = append(buildLog.Steps, fatalStep)

	_ = elh.sendBuildFinishedEvent("failed")
	_ = elh.sendBuildJobLogEvent(buildLog)
	_ = elh.sendBuildCleanEvent("failed")

	if elh.runAsJob {
		log.Error().Err(err).Msg(message)
		os.Exit(0)
	} else {
		log.Fatal().Err(err).Msg(message)
	}
}

func (elh *endOfLifeHelperImpl) sendBuildJobLogEvent(buildLog contracts.BuildLog) (err error) {

	ciServerBuilderPostLogsURL := elh.config.CIServer.PostLogsURL
	ciAPIKey := elh.config.CIServer.APIKey
	jobName := *elh.config.JobName

	if ciServerBuilderPostLogsURL != "" && ciAPIKey != "" && jobName != "" {

		// convert BuildJobLogs to json
		var requestBody io.Reader

		var data []byte
		if *elh.config.Action == "release" {
			// copy buildLog to releaseLog and marshal that
			releaseLog := contracts.ReleaseLog{
				ID:         buildLog.ID,
				RepoSource: buildLog.RepoSource,
				RepoOwner:  buildLog.RepoOwner,
				RepoName:   buildLog.RepoName,
				ReleaseID:  strconv.Itoa(elh.config.ReleaseParams.ReleaseID),
				Steps:      buildLog.Steps,
				InsertedAt: buildLog.InsertedAt,
			}
			data, err = json.Marshal(releaseLog)
			if err != nil {
				log.Error().Err(err).Msgf("Failed marshalling ReleaseLog for job %v", jobName)
				return
			}
		} else {
			data, err = json.Marshal(buildLog)
			if err != nil {
				log.Error().Err(err).Msgf("Failed marshalling BuildLog for job %v", jobName)
				return
			}
		}

		requestBody = bytes.NewReader(data)

		// create client, in order to add headers
		client := pester.New()
		client.MaxRetries = 5
		client.Backoff = pester.ExponentialJitterBackoff
		client.KeepLog = true
		client.Timeout = time.Second * 120
		request, err := http.NewRequest("POST", ciServerBuilderPostLogsURL, requestBody)
		if err != nil {
			log.Error().Err(err).Msgf("Failed creating http client for job %v", jobName)
			return err
		}

		// add headers
		request.Header.Add("Authorization", fmt.Sprintf("Bearer %v", ciAPIKey))
		request.Header.Add("Content-Type", "application/json")

		// perform actual request
		response, err := client.Do(request)
		if err != nil {
			log.Error().Err(err).Str("logs", client.LogString()).Msgf("Failed shipping logs to %v for job %v", ciServerBuilderPostLogsURL, jobName)
			return err
		}

		defer response.Body.Close()

		log.Debug().Str("logs", client.LogString()).Msgf("Successfully shipped logs to %v for job %v", ciServerBuilderPostLogsURL, jobName)
	}

	return nil
}

func (elh *endOfLifeHelperImpl) sendBuildFinishedEvent(buildStatus string) error {
	return elh.sendBuilderEvent(buildStatus, fmt.Sprintf("builder:%v", buildStatus))
}

func (elh *endOfLifeHelperImpl) sendBuildCleanEvent(buildStatus string) error {
	return elh.sendBuilderEvent(buildStatus, "builder:clean")
}

func (elh *endOfLifeHelperImpl) sendBuilderEvent(buildStatus, event string) (err error) {

	ciServerBuilderEventsURL := elh.config.CIServer.BuilderEventsURL
	ciAPIKey := elh.config.CIServer.APIKey
	jobName := *elh.config.JobName

	if ciServerBuilderEventsURL != "" && ciAPIKey != "" && jobName != "" {
		// convert EstafetteCiBuilderEvent to json
		var requestBody io.Reader

		buildID := ""
		if elh.config.BuildParams != nil {
			buildID = strconv.Itoa(elh.config.BuildParams.BuildID)
		}

		releaseID := ""
		if elh.config.ReleaseParams != nil {
			releaseID = strconv.Itoa(elh.config.ReleaseParams.ReleaseID)
		}

		ciBuilderEvent := EstafetteCiBuilderEvent{
			JobName:      jobName,
			RepoSource:   elh.config.Git.RepoSource,
			RepoOwner:    elh.config.Git.RepoOwner,
			RepoName:     elh.config.Git.RepoName,
			RepoBranch:   elh.config.Git.RepoBranch,
			RepoRevision: elh.config.Git.RepoRevision,
			ReleaseID:    releaseID,
			BuildID:      buildID,
			BuildStatus:  buildStatus,
		}
		data, err := json.Marshal(ciBuilderEvent)
		if err != nil {
			log.Error().Err(err).Msgf("Failed marshalling EstafetteCiBuilderEvent for job %v", jobName)
			return err
		}
		requestBody = bytes.NewReader(data)

		// create client, in order to add headers
		client := pester.New()
		client.MaxRetries = 5
		client.Backoff = pester.ExponentialJitterBackoff
		client.KeepLog = true
		client.Timeout = time.Second * 10
		request, err := http.NewRequest("POST", ciServerBuilderEventsURL, requestBody)
		if err != nil {
			log.Error().Err(err).Msgf("Failed creating http client for job %v", jobName)
			return err
		}

		// add headers
		request.Header.Add("X-Estafette-Event", event)
		request.Header.Add("X-Estafette-Event-Job-Name", jobName)
		request.Header.Add("Authorization", fmt.Sprintf("Bearer %v", ciAPIKey))

		// perform actual request
		response, err := client.Do(request)
		if err != nil {
			log.Error().Err(err).Str("pesterLogs", client.LogString()).Msgf("Failed performing http request to %v for job %v", ciServerBuilderEventsURL, jobName)
			return err
		}

		defer response.Body.Close()

		log.Debug().Str("pesterLogs", client.LogString()).Str("url", ciServerBuilderEventsURL).Msgf("Succesfully sent %v event to api", event)
	}

	return nil
}
<|endoftext|>


<commit_before>package appfile

import (
	"fmt"

	"github.com/hashicorp/go-multierror"
)

// Validate validates the Appfile
func (f *File) Validate() error {
	var result error

	// Basic checking for stanzas
	if f.Application == nil {
		result = multierror.Append(result, fmt.Errorf(
			"'application' stanza required for Appfile"))
	}
	if f.Project == nil {
		result = multierror.Append(result, fmt.Errorf(
			"'project' stanza required for Appfile"))
	}
	if f.Infrastructure == nil {
		result = multierror.Append(result, fmt.Errorf(
			"'infrastructure' stanza required for Appfile"))
	}

	// Verify the application itself
	if f.Application != nil {
		if f.Application.Name == "" {
			result = multierror.Append(result, fmt.Errorf(
				"application: name is required"))
		}
		if f.Application.Type == "" {
			result = multierror.Append(result, fmt.Errorf(
				"application: type is required"))
		}
	}

	// Validate the project
	if f.Project != nil {
		if f.Project.Name == "" {
			result = multierror.Append(result, fmt.Errorf(
				"project: name is required"))
		}
		if f.Project.Infrastructure == "" {
			result = multierror.Append(result, fmt.Errorf(
				"project: infrastructure is required"))
		} else {
			found := false
			for _, i := range f.Infrastructure {
				if i.Name == f.Project.Infrastructure {
					found = true
					break
				}
			}
			if !found {
				result = multierror.Append(result, fmt.Errorf(
					"project: infra '%s' has no corresponding infrastruture stanza",
					f.Project.Infrastructure))
			}
		}
	}

	return result
}
<commit_msg>Minor typo fix.<commit_after>package appfile

import (
	"fmt"

	"github.com/hashicorp/go-multierror"
)

// Validate validates the Appfile
func (f *File) Validate() error {
	var result error

	// Basic checking for stanzas
	if f.Application == nil {
		result = multierror.Append(result, fmt.Errorf(
			"'application' stanza required for Appfile"))
	}
	if f.Project == nil {
		result = multierror.Append(result, fmt.Errorf(
			"'project' stanza required for Appfile"))
	}
	if f.Infrastructure == nil {
		result = multierror.Append(result, fmt.Errorf(
			"'infrastructure' stanza required for Appfile"))
	}

	// Verify the application itself
	if f.Application != nil {
		if f.Application.Name == "" {
			result = multierror.Append(result, fmt.Errorf(
				"application: name is required"))
		}
		if f.Application.Type == "" {
			result = multierror.Append(result, fmt.Errorf(
				"application: type is required"))
		}
	}

	// Validate the project
	if f.Project != nil {
		if f.Project.Name == "" {
			result = multierror.Append(result, fmt.Errorf(
				"project: name is required"))
		}
		if f.Project.Infrastructure == "" {
			result = multierror.Append(result, fmt.Errorf(
				"project: infrastructure is required"))
		} else {
			found := false
			for _, i := range f.Infrastructure {
				if i.Name == f.Project.Infrastructure {
					found = true
					break
				}
			}
			if !found {
				result = multierror.Append(result, fmt.Errorf(
					"project: infra '%s' has no corresponding infrastructure stanza",
					f.Project.Infrastructure))
			}
		}
	}

	return result
}
<|endoftext|>


<commit_before>package engine

import (
	"time"

	"github.com/mijia/adoc"
)

type RunState int
type HealthState int

var RestartMaxCount int

const (
	RunStatePending = iota
	RunStateDrift
	RunStateSuccess
	RunStateExit
	RunStateFail
	RunStateInconsistent
	RunStateMissing
	RunStateRemoved
)

const (
	HealthStateNone = iota
	HealthStateStarting
	HealthStateHealthy
	HealthStateUnHealthy
)

func (rs RunState) String() string {
	switch rs {
	case RunStatePending:
		return "RunStatePending"
	case RunStateDrift:
		return "RunStateDrift"
	case RunStateSuccess:
		return "RunStateSuccess"
	case RunStateExit:
		return "RunStateExit"
	case RunStateFail:
		return "RunStateFail"
	case RunStateMissing:
		return "RunStateMissing"
	case RunStateInconsistent:
		return "RunStateInconsistent"
	case RunStateRemoved:
		return "RunStateRemoved"
	default:
		return "Unknown RunState"
	}
}

func (hs HealthState) String() string {
	switch hs {
	case HealthStateNone:
		return "none"
	case HealthStateStarting:
		return "starting"
	case HealthStateHealthy:
		return "healthy"
	case HealthStateUnHealthy:
		return "unhealthy"
	default:
		return "None"
	}
}

type ImRuntime struct {
	BaseRuntime
	DriftCount   int
	RestartCount int
	RestartAt    time.Time
}

type BaseRuntime struct {
	Healthst  HealthState
	State     RunState
	OOMkilled bool
	LastError string
	UpdatedAt time.Time
}

type Container struct {
	// FIXME(mijia): multiple ports supporing, will have multiple entries of <NodePort, ContainerPort, Protocol>
	Id            string
	Runtime       adoc.ContainerDetail
	NodeName      string
	NodeIp        string
	ContainerIp   string
	NodePort      int
	ContainerPort int
	Protocol      string
}

func (c Container) Clone() Container {
	// So far we maybe only care about the basic information like in the Equals
	return c
}

func (c Container) Equals(o Container) bool {
	// The ContainerDetail from adoc change would reflect to the Pod runtime changes
	return c.Id == o.Id &&
		c.NodeName == o.NodeName &&
		c.NodeIp == o.NodeIp &&
		c.ContainerIp == o.ContainerIp &&
		c.NodePort == o.NodePort &&
		c.ContainerPort == o.ContainerPort &&
		c.Protocol == o.Protocol
}

type Pod struct {
	InstanceNo int
	Containers []Container
	ImRuntime
}

func (p Pod) Clone() Pod {
	n := p
	n.Containers = make([]Container, len(p.Containers))
	for i := range p.Containers {
		n.Containers[i] = p.Containers[i].Clone()
	}
	return n
}

func (p Pod) Equals(o Pod) bool {
	if len(p.Containers) != len(o.Containers) {
		return false
	}
	for i := range p.Containers {
		if !p.Containers[i].Equals(o.Containers[i]) {
			return false
		}
	}
	return p.InstanceNo == o.InstanceNo &&
		p.State == o.State &&
		p.LastError == o.LastError &&
		p.DriftCount == o.DriftCount
}

func (pod Pod) ContainerIds() []string {
	ids := make([]string, len(pod.Containers))
	for i, container := range pod.Containers {
		ids[i] = container.Id
	}
	return ids
}

func (pod Pod) NeedRestart(policy RestartPolicy) bool {
	state := pod.State
	if policy == RestartPolicyAlways {
		return state == RunStateExit || state == RunStateFail
	}
	if policy == RestartPolicyOnFail {
		return state == RunStateFail
	}
	return false
}

func (pod Pod) RestartEnoughTimes() bool {
	return pod.RestartCount >= RestartMaxCount
}

func (pod Pod) NodeName() string {
	if len(pod.Containers) > 0 {
		return pod.Containers[0].NodeName
	}
	return ""
}

func (pod Pod) NodeIp() string {
	if len(pod.Containers) > 0 {
		return pod.Containers[0].NodeIp
	}
	return ""
}

func (pod Pod) PodIp() string {
	if len(pod.Containers) > 0 {
		return pod.Containers[0].NodeIp
	}
	return ""
}

type PodGroup struct {
	Pods []Pod
	BaseRuntime
}

func (pg PodGroup) Clone() PodGroup {
	n := pg
	n.Pods = make([]Pod, len(pg.Pods))
	for i := range pg.Pods {
		n.Pods[i] = pg.Pods[i].Clone()
	}
	return n
}

func (pg PodGroup) Equals(o PodGroup) bool {
	if len(pg.Pods) != len(o.Pods) {
		return false
	}
	for i := range pg.Pods {
		if !pg.Pods[i].Equals(o.Pods[i]) {
			return false
		}
	}
	return pg.State == o.State &&
		pg.LastError == o.LastError
}

func (group PodGroup) collectNodes() map[string]string {
	nodes := make(map[string]string)
	for _, pod := range group.Pods {
		name := pod.NodeName()
		ip := pod.NodeIp()
		if name != "" && ip != "" {
			nodes[name] = ip
		}
	}
	return nodes
}

type DependencyEvent struct {
	Type      string // add, remove, verify
	Name      string
	NodeName  string
	Namespace string
}
<commit_msg>Refresh restartcount After double RestartInfoClearInterval running<commit_after>package engine

import (
	"time"

	"github.com/mijia/adoc"
)

type RunState int
type HealthState int

var RestartMaxCount int

const (
	RunStatePending = iota
	RunStateDrift
	RunStateSuccess
	RunStateExit
	RunStateFail
	RunStateInconsistent
	RunStateMissing
	RunStateRemoved
)

const (
	HealthStateNone = iota
	HealthStateStarting
	HealthStateHealthy
	HealthStateUnHealthy
)

func (rs RunState) String() string {
	switch rs {
	case RunStatePending:
		return "RunStatePending"
	case RunStateDrift:
		return "RunStateDrift"
	case RunStateSuccess:
		return "RunStateSuccess"
	case RunStateExit:
		return "RunStateExit"
	case RunStateFail:
		return "RunStateFail"
	case RunStateMissing:
		return "RunStateMissing"
	case RunStateInconsistent:
		return "RunStateInconsistent"
	case RunStateRemoved:
		return "RunStateRemoved"
	default:
		return "Unknown RunState"
	}
}

func (hs HealthState) String() string {
	switch hs {
	case HealthStateNone:
		return "none"
	case HealthStateStarting:
		return "starting"
	case HealthStateHealthy:
		return "healthy"
	case HealthStateUnHealthy:
		return "unhealthy"
	default:
		return "None"
	}
}

type ImRuntime struct {
	BaseRuntime
	DriftCount   int
	RestartCount int
	RestartAt    time.Time
}

type BaseRuntime struct {
	Healthst  HealthState
	State     RunState
	OOMkilled bool
	LastError string
	UpdatedAt time.Time
}

type Container struct {
	// FIXME(mijia): multiple ports supporing, will have multiple entries of <NodePort, ContainerPort, Protocol>
	Id            string
	Runtime       adoc.ContainerDetail
	NodeName      string
	NodeIp        string
	ContainerIp   string
	NodePort      int
	ContainerPort int
	Protocol      string
}

func (c Container) Clone() Container {
	// So far we maybe only care about the basic information like in the Equals
	return c
}

func (c Container) Equals(o Container) bool {
	// The ContainerDetail from adoc change would reflect to the Pod runtime changes
	return c.Id == o.Id &&
		c.NodeName == o.NodeName &&
		c.NodeIp == o.NodeIp &&
		c.ContainerIp == o.ContainerIp &&
		c.NodePort == o.NodePort &&
		c.ContainerPort == o.ContainerPort &&
		c.Protocol == o.Protocol
}

type Pod struct {
	InstanceNo int
	Containers []Container
	ImRuntime
}

func (p Pod) Clone() Pod {
	n := p
	n.Containers = make([]Container, len(p.Containers))
	for i := range p.Containers {
		n.Containers[i] = p.Containers[i].Clone()
	}
	return n
}

func (p Pod) Equals(o Pod) bool {
	if len(p.Containers) != len(o.Containers) {
		return false
	}
	for i := range p.Containers {
		if !p.Containers[i].Equals(o.Containers[i]) {
			return false
		}
	}
	return p.InstanceNo == o.InstanceNo &&
		p.State == o.State &&
		p.LastError == o.LastError &&
		p.DriftCount == o.DriftCount
}

func (pod Pod) ContainerIds() []string {
	ids := make([]string, len(pod.Containers))
	for i, container := range pod.Containers {
		ids[i] = container.Id
	}
	return ids
}

func (pod Pod) NeedRestart(policy RestartPolicy) bool {
	state := pod.State
	if policy == RestartPolicyAlways {
		return state == RunStateExit || state == RunStateFail
	}
	if policy == RestartPolicyOnFail {
		return state == RunStateFail
	}
	return false
}

func (pod Pod) RestartEnoughTimes() bool {
	if len(pod.Containers) > 0 && pod.RestartAt.Add(2*RestartInfoClearInterval).Before(pod.Containers[0].Runtime.State.FinishedAt) {
		return false
	}
	return pod.RestartCount >= RestartMaxCount
}

func (pod Pod) NodeName() string {
	if len(pod.Containers) > 0 {
		return pod.Containers[0].NodeName
	}
	return ""
}

func (pod Pod) NodeIp() string {
	if len(pod.Containers) > 0 {
		return pod.Containers[0].NodeIp
	}
	return ""
}

func (pod Pod) PodIp() string {
	if len(pod.Containers) > 0 {
		return pod.Containers[0].NodeIp
	}
	return ""
}

type PodGroup struct {
	Pods []Pod
	BaseRuntime
}

func (pg PodGroup) Clone() PodGroup {
	n := pg
	n.Pods = make([]Pod, len(pg.Pods))
	for i := range pg.Pods {
		n.Pods[i] = pg.Pods[i].Clone()
	}
	return n
}

func (pg PodGroup) Equals(o PodGroup) bool {
	if len(pg.Pods) != len(o.Pods) {
		return false
	}
	for i := range pg.Pods {
		if !pg.Pods[i].Equals(o.Pods[i]) {
			return false
		}
	}
	return pg.State == o.State &&
		pg.LastError == o.LastError
}

func (group PodGroup) collectNodes() map[string]string {
	nodes := make(map[string]string)
	for _, pod := range group.Pods {
		name := pod.NodeName()
		ip := pod.NodeIp()
		if name != "" && ip != "" {
			nodes[name] = ip
		}
	}
	return nodes
}

type DependencyEvent struct {
	Type      string // add, remove, verify
	Name      string
	NodeName  string
	Namespace string
}
<|endoftext|>


<commit_before>// +build !windows

package archive

import (
	"os"
	"sync"
	"syscall"

	"github.com/containerd/continuity/sysx"
	"github.com/dmcgowan/go-tar"
	"github.com/opencontainers/runc/libcontainer/system"
	"github.com/pkg/errors"
	"golang.org/x/sys/unix"
)

func tarName(p string) (string, error) {
	return p, nil
}

func chmodTarEntry(perm os.FileMode) os.FileMode {
	return perm
}

func setHeaderForSpecialDevice(hdr *tar.Header, name string, fi os.FileInfo) error {
	s, ok := fi.Sys().(*syscall.Stat_t)
	if !ok {
		return errors.New("unsupported stat type")
	}

	// Currently go does not fill in the major/minors
	if s.Mode&syscall.S_IFBLK != 0 ||
		s.Mode&syscall.S_IFCHR != 0 {
		hdr.Devmajor = int64(major(uint64(s.Rdev)))
		hdr.Devminor = int64(minor(uint64(s.Rdev)))
	}

	return nil
}

func major(device uint64) uint64 {
	return (device >> 8) & 0xfff
}

func minor(device uint64) uint64 {
	return (device & 0xff) | ((device >> 12) & 0xfff00)
}

func mkdev(major int64, minor int64) uint32 {
	return uint32(((minor & 0xfff00) << 12) | ((major & 0xfff) << 8) | (minor & 0xff))
}

func open(p string) (*os.File, error) {
	return os.Open(p)
}

func openFile(name string, flag int, perm os.FileMode) (*os.File, error) {
	return os.OpenFile(name, flag, perm)
}

func mkdirAll(path string, perm os.FileMode) error {
	return os.MkdirAll(path, perm)
}

func prepareApply() func() {
	// Unset unmask before doing an apply operation,
	// restore unmask when complete
	oldmask := unix.Umask(0)
	return func() {
		unix.Umask(oldmask)
	}
}

func skipFile(*tar.Header) bool {
	return false
}

var (
	inUserNS bool
	nsOnce   sync.Once
)

func setInUserNS() {
	inUserNS = system.RunningInUserNS()
}

// handleTarTypeBlockCharFifo is an OS-specific helper function used by
// createTarFile to handle the following types of header: Block; Char; Fifo
func handleTarTypeBlockCharFifo(hdr *tar.Header, path string) error {
	nsOnce.Do(setInUserNS)
	if inUserNS {
		// cannot create a device if running in user namespace
		return nil
	}

	mode := uint32(hdr.Mode & 07777)
	switch hdr.Typeflag {
	case tar.TypeBlock:
		mode |= unix.S_IFBLK
	case tar.TypeChar:
		mode |= unix.S_IFCHR
	case tar.TypeFifo:
		mode |= unix.S_IFIFO
	}

	return unix.Mknod(path, mode, int(mkdev(hdr.Devmajor, hdr.Devminor)))
}

func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error {
	if hdr.Typeflag == tar.TypeLink {
		if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) {
			if err := os.Chmod(path, hdrInfo.Mode()); err != nil {
				return err
			}
		}
	} else if hdr.Typeflag != tar.TypeSymlink {
		if err := os.Chmod(path, hdrInfo.Mode()); err != nil {
			return err
		}
	}
	return nil
}

func getxattr(path, attr string) ([]byte, error) {
	b, err := sysx.LGetxattr(path, attr)
	if err == unix.ENOTSUP || err == sysx.ENODATA {
		return nil, nil
	}
	return b, err
}

func setxattr(path, key, value string) error {
	return sysx.LSetxattr(path, key, []byte(value), 0)
}
<commit_msg>archive: use Mkdev, Major and Minor functions from golang.org/x/sys/unix<commit_after>// +build !windows

package archive

import (
	"os"
	"sync"
	"syscall"

	"github.com/containerd/continuity/sysx"
	"github.com/dmcgowan/go-tar"
	"github.com/opencontainers/runc/libcontainer/system"
	"github.com/pkg/errors"
	"golang.org/x/sys/unix"
)

func tarName(p string) (string, error) {
	return p, nil
}

func chmodTarEntry(perm os.FileMode) os.FileMode {
	return perm
}

func setHeaderForSpecialDevice(hdr *tar.Header, name string, fi os.FileInfo) error {
	s, ok := fi.Sys().(*syscall.Stat_t)
	if !ok {
		return errors.New("unsupported stat type")
	}

	// Currently go does not fill in the major/minors
	if s.Mode&syscall.S_IFBLK != 0 ||
		s.Mode&syscall.S_IFCHR != 0 {
		hdr.Devmajor = int64(unix.Major(uint64(s.Rdev)))
		hdr.Devminor = int64(unix.Minor(uint64(s.Rdev)))
	}

	return nil
}

func open(p string) (*os.File, error) {
	return os.Open(p)
}

func openFile(name string, flag int, perm os.FileMode) (*os.File, error) {
	return os.OpenFile(name, flag, perm)
}

func mkdirAll(path string, perm os.FileMode) error {
	return os.MkdirAll(path, perm)
}

func prepareApply() func() {
	// Unset unmask before doing an apply operation,
	// restore unmask when complete
	oldmask := unix.Umask(0)
	return func() {
		unix.Umask(oldmask)
	}
}

func skipFile(*tar.Header) bool {
	return false
}

var (
	inUserNS bool
	nsOnce   sync.Once
)

func setInUserNS() {
	inUserNS = system.RunningInUserNS()
}

// handleTarTypeBlockCharFifo is an OS-specific helper function used by
// createTarFile to handle the following types of header: Block; Char; Fifo
func handleTarTypeBlockCharFifo(hdr *tar.Header, path string) error {
	nsOnce.Do(setInUserNS)
	if inUserNS {
		// cannot create a device if running in user namespace
		return nil
	}

	mode := uint32(hdr.Mode & 07777)
	switch hdr.Typeflag {
	case tar.TypeBlock:
		mode |= unix.S_IFBLK
	case tar.TypeChar:
		mode |= unix.S_IFCHR
	case tar.TypeFifo:
		mode |= unix.S_IFIFO
	}

	return unix.Mknod(path, mode, int(unix.Mkdev(uint32(hdr.Devmajor), uint32(hdr.Devminor))))
}

func handleLChmod(hdr *tar.Header, path string, hdrInfo os.FileInfo) error {
	if hdr.Typeflag == tar.TypeLink {
		if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) {
			if err := os.Chmod(path, hdrInfo.Mode()); err != nil {
				return err
			}
		}
	} else if hdr.Typeflag != tar.TypeSymlink {
		if err := os.Chmod(path, hdrInfo.Mode()); err != nil {
			return err
		}
	}
	return nil
}

func getxattr(path, attr string) ([]byte, error) {
	b, err := sysx.LGetxattr(path, attr)
	if err == unix.ENOTSUP || err == sysx.ENODATA {
		return nil, nil
	}
	return b, err
}

func setxattr(path, key, value string) error {
	return sysx.LSetxattr(path, key, []byte(value), 0)
}
<|endoftext|>


<commit_before>package util

import (
	"fmt"
	"image"
	"image/color"
	"image/draw"
	"image/gif"
	"image/png"
	"math"
	"os"
	"strings"
	"time"
)

// Approx. framerate of the display
const fps = 30

// How long each frame is displayed for
const frameTime = time.Second / fps

// If a frame's delay is under this duration, we will display it
// for a certain number of frames, rather than for a time.
const adjustDelayUnder = time.Millisecond * 300

type Image interface {
	GetNextFrame() *image.RGBA
	GetPositionFrame(position float64, blend bool) *image.RGBA
}

type SingleImage struct {
	frame *image.RGBA
}

func NewSingleImage(frame *image.RGBA) *SingleImage {
	return &SingleImage{frame}
}

func (i *SingleImage) GetNextFrame() *image.RGBA {
	return i.frame
}

func (i *SingleImage) GetPositionFrame(position float64, blend bool) *image.RGBA {
	return i.frame
}

/* MaskImage wraps an image so that the red level is used as the transparency.
It allows animated gifs to be used as masks, with 255 levels of alpha. */
type MaskImage struct {
	Image Image
}

func (i *MaskImage) GetNextFrame() *image.RGBA {
	in := i.Image.GetNextFrame()
	out := image.NewRGBA(image.Rect(0, 0, 16, 16))

	for pos := 0; (pos + 4) < len(in.Pix); pos = pos + 4 {
		out.Pix[pos+3] = 255 - in.Pix[pos]
	}

	return out
}

func (i *MaskImage) GetPositionFrame(position float64, blend bool) *image.RGBA {
	return i.Image.GetPositionFrame(position, blend)
}

type AnimatedImage struct {
	frameRequest    chan bool
	pos             int
	frames          []*image.RGBA
	delays          []int
	remainingLoops  int
	started         bool
	delayAdjustment float64
}

func (i *AnimatedImage) GetNextFrame() *image.RGBA {

	if !i.started {
		i.started = true
		i.start()
	}

	frame := i.frames[i.pos]

	select {
	case i.frameRequest <- true:
	default:
	}

	return frame
}

func (i *AnimatedImage) start() {

	go func() {
		for {
			delay := time.Duration(float64(i.delays[i.pos])*i.delayAdjustment) * 10 * time.Millisecond

			if delay < adjustDelayUnder {

				// Rounded to nearest int
				framesToDisplay := int(math.Floor((float64(delay) / float64(frameTime)) + 0.5))

				// Show for at least one frame
				if framesToDisplay == 0 {
					framesToDisplay = 1
				}

				//log.Printf("Frame wanted a delay of %d so showing for %d frames", delay, framesToDisplay)

				for x := 0; x < framesToDisplay; x++ {
					// Wait till this frame has been taken
					<-i.frameRequest
				}

			} else {
				// Just sleep. At these amounts noone will notice +-1 frame
				//log.Printf("Sleeping frame %d for %d", i.pos, delay)
				time.Sleep(delay)
			}

			// That was the last frame
			if i.pos == len(i.frames)-1 {

				if i.remainingLoops == -1 {
					// Start again, we are looping forever
					i.pos = 0
				} else if i.remainingLoops > 0 {
					// Start again, we still have at least one loop remaining
					i.pos = 0
					i.remainingLoops--
				} else {
					// We're done, this frame gets shown forever *drops mic*.
					break
				}
			} else {
				i.pos++
			}

		}
	}()

}

// GetPositionFrame returns the frame corresponding to the position given 0....1
func (i *AnimatedImage) GetPositionFrame(position float64, blend bool) *image.RGBA {

	relativePosition := position * float64(len(i.frames)-1)

	previousFrame := int(math.Floor(relativePosition))
	nextFrame := int(math.Ceil(relativePosition))

	framePosition := math.Mod(relativePosition, 1)

	//log.Debugf("GetPositionFrame. Frames:%d Position:%f RelativePosition:%f FramePosition:%f PreviousFrame:%d NextFrame:%d", len(i.frames), position, relativePosition, framePosition, previousFrame, nextFrame)
	if !blend || previousFrame == nextFrame {
		// We can just send back a single frame
		return i.frames[previousFrame]
	}

	maskNext := image.NewUniform(color.Alpha{uint8(255 * framePosition)})

	frame := image.NewRGBA(image.Rect(0, 0, 16, 16))

	draw.Draw(frame, frame.Bounds(), i.frames[previousFrame], image.Point{0, 0}, draw.Src)
	draw.DrawMask(frame, frame.Bounds(), i.frames[nextFrame], image.Point{0, 0}, maskNext, image.Point{0, 0}, draw.Over)

	return frame
}

func LoadImage(src string) Image {
	srcLower := strings.ToLower(src)

	if strings.Contains(srcLower, ".gif") {
		return LoadGif(src)
	} else if strings.Contains(srcLower, ".png") {
		return LoadPng(src)
	} else {
		log.HandleError(fmt.Errorf(src), "Unknown image format")
	}
	return nil
}

func LoadPng(src string) Image {

	file, err := os.Open(src)

	if err != nil {
		log.Fatalf("Could not open png '%s' : %s", src, err)
	}

	img, err := png.Decode(file)
	if err != nil {
		log.Fatalf("PNG decoding failed on image '%s' : %s", src, err)
	}

	return &SingleImage{
		frame: toRGBA(img),
	}
}

func LoadGif(src string) *AnimatedImage {

	file, err := os.Open(src)

	if err != nil {
		log.Fatalf("Could not open gif '%s' : %s", src, err)
	}

	img, err := gif.DecodeAll(file)
	if err != nil {
		log.Fatalf("Gif decoding failed on image '%s' : %s", src, err)
	}

	var frames = []*image.RGBA{}

	for _, frame := range img.Image {
		frames = append(frames, toRGBA(frame))
	}

	loops := img.LoopCount
	if loops == 0 {
		loops = -1
	}

	return &AnimatedImage{
		frames:          frames,
		delays:          img.Delay,
		remainingLoops:  loops,
		frameRequest:    make(chan bool),
		delayAdjustment: 1.0,
	}
}

func toRGBA(in image.Image) *image.RGBA {
	bounds := in.Bounds()
	out := image.NewRGBA(image.Rect(0, 0, bounds.Dx(), bounds.Dy()))
	draw.Draw(out, out.Bounds(), in, bounds.Min, draw.Over)
	return out
}

/*

Alt version using different gif decoder... doesn't work

package util

import (
"bytes"
"fmt"
"image"
"image/color"
"image/draw"
"image/png"
"io/ioutil"
"math"
"os"
"strings"
"time"

"github.com/xanthousphoenix/go-gif"
)

// Approx. framerate of the display
const fps = 30

// How long each frame is displayed for
const frameTime = time.Second / fps

// If a frame's delay is under this duration, we will display it
// for a certain number of frames, rather than for a time.
const adjustDelayUnder = time.Millisecond * 300

type Image interface {
GetNextFrame() *image.RGBA
GetPositionFrame(position float64, blend bool) *image.RGBA
}

type SingleImage struct {
frame *image.RGBA
}

func NewSingleImage(frame *image.RGBA) *SingleImage {
return &SingleImage{frame}
}

func (i *SingleImage) GetNextFrame() *image.RGBA {
return i.frame
}

func (i *SingleImage) GetPositionFrame(position float64, blend bool) *image.RGBA {
return i.frame
}

type AnimatedImage struct {
frameRequest    chan bool
pos             int
frames          []*image.RGBA
delays          []int
remainingLoops  int
started         bool
delayAdjustment float64
}

func (i *AnimatedImage) GetNextFrame() *image.RGBA {

if !i.started {
i.started = true
i.start()
}

frame := i.frames[i.pos]

select {
case i.frameRequest <- true:
default:
}

return frame
}

func (i *AnimatedImage) start() {

go func() {
for {
delay := time.Duration(float64(i.delays[i.pos])*i.delayAdjustment) * 10 * time.Millisecond

if delay < adjustDelayUnder {

// Rounded to nearest int
framesToDisplay := int(math.Floor((float64(delay) / float64(frameTime)) + 0.5))

// Show for at least one frame
if framesToDisplay == 0 {
framesToDisplay = 1
}

//log.Printf("Frame wanted a delay of %d so showing for %d frames", delay, framesToDisplay)

for x := 0; x < framesToDisplay; x++ {
// Wait till this frame has been taken
<-i.frameRequest
}

} else {
// Just sleep. At these amounts noone will notice +-1 frame
//log.Printf("Sleeping frame %d for %d", i.pos, delay)
time.Sleep(delay)
}

// That was the last frame
if i.pos == len(i.frames)-1 {

if i.remainingLoops == -1 {
// Start again, we are looping forever
i.pos = 0
} else if i.remainingLoops > 0 {
// Start again, we still have at least one loop remaining
i.pos = 0
i.remainingLoops--
} else {
// We're done, this frame gets shown forever *drops mic*.
break
}
} else {
i.pos++
}

}
}()

}

// GetPositionFrame returns the frame corresponding to the position given 0....1
func (i *AnimatedImage) GetPositionFrame(position float64, blend bool) *image.RGBA {

relativePosition := position * float64(len(i.frames)-1)

previousFrame := int(math.Floor(relativePosition))
nextFrame := int(math.Ceil(relativePosition))

framePosition := math.Mod(relativePosition, 1)

//log.Debugf("GetPositionFrame. Frames:%d Position:%f RelativePosition:%f FramePosition:%f PreviousFrame:%d NextFrame:%d", len(i.frames), position, relativePosition, framePosition, previousFrame, nextFrame)
if !blend || previousFrame == nextFrame {
// We can just send back a single frame
return i.frames[previousFrame]
}

maskNext := image.NewUniform(color.Alpha{uint8(255 * framePosition)})

frame := image.NewRGBA(image.Rect(0, 0, 16, 16))

draw.Draw(frame, frame.Bounds(), i.frames[previousFrame], image.Point{0, 0}, draw.Src)
draw.DrawMask(frame, frame.Bounds(), i.frames[nextFrame], image.Point{0, 0}, maskNext, image.Point{0, 0}, draw.Over)

return frame
}

func LoadImage(src string) Image {
srcLower := strings.ToLower(src)

if strings.Contains(srcLower, ".gif") {
return LoadGif(src)
} else if strings.Contains(srcLower, ".png") {
return LoadPng(src)
} else {
log.HandleError(fmt.Errorf(src), "Unknown image format")
}
return nil
}

func LoadPng(src string) Image {

file, err := os.Open(src)

if err != nil {
log.Fatalf("Could not open png '%s' : %s", src, err)
}

img, err := png.Decode(file)
if err != nil {
log.Fatalf("PNG decoding failed on image '%s' : %s", src, err)
}

return &SingleImage{
frame: toRGBA(img),
}
}

func LoadGif(src string) *AnimatedImage {

b, err := ioutil.ReadFile(src)
if err != nil {
log.Fatalf("Could not open gif '%s' : %s", src, err)
}

reader := bytes.NewReader(b)

img, err := gif.DecodeAll(reader)
if err != nil {
log.Fatalf("Gif decoding failed on image '%s' : %s", src, err)
}

var delays = []int{}
var frames = []*image.RGBA{}

for _, frame := range img.Frames {
delays = append(delays, int(frame.DelayTime))
frames = append(frames, toRGBA(frame.FrameImage))
}

loops := img.Header.LoopCount
if loops == 0 {
loops = -1
}

return &AnimatedImage{
frames:          frames,
delays:          delays,
remainingLoops:  loops,
frameRequest:    make(chan bool),
delayAdjustment: 1.0,
}
}

func toRGBA(in image.Image) *image.RGBA {
bounds := in.Bounds()
out := image.NewRGBA(image.Rect(0, 0, bounds.Dx(), bounds.Dy()))
draw.Draw(out, out.Bounds(), in, bounds.Min, draw.Over)
return out
}

*/
<commit_msg>Blerghch<commit_after>package util

import (
	"fmt"
	"image"
	"image/color"
	"image/draw"
	"image/gif"
	"image/png"
	"math"
	"os"
	"strings"
	"time"
)

// Approx. framerate of the display
const fps = 30

// How long each frame is displayed for
const frameTime = time.Second / fps

// If a frame's delay is under this duration, we will display it
// for a certain number of frames, rather than for a time.
const adjustDelayUnder = time.Millisecond * 300

type Image interface {
	GetNextFrame() *image.RGBA
	GetPositionFrame(position float64, blend bool) *image.RGBA
}

type SingleImage struct {
	frame *image.RGBA
}

func NewSingleImage(frame *image.RGBA) *SingleImage {
	return &SingleImage{frame}
}

func (i *SingleImage) GetNextFrame() *image.RGBA {
	return i.frame
}

func (i *SingleImage) GetPositionFrame(position float64, blend bool) *image.RGBA {
	return i.frame
}

/* MaskImage wraps an image so that the red level is used as the transparency.
It allows animated gifs to be used as masks, with 255 levels of alpha. */
type MaskImage struct {
	Image Image
}

func (i *MaskImage) GetNextFrame() *image.RGBA {
	in := i.Image.GetNextFrame()
	out := image.NewRGBA(image.Rect(0, 0, 16, 16))

	for pos := 0; (pos + 3) < len(in.Pix); pos = pos + 4 {
		out.Pix[pos+3] = 255 - in.Pix[pos]
	}

	return out
}

func (i *MaskImage) GetPositionFrame(position float64, blend bool) *image.RGBA {
	return i.Image.GetPositionFrame(position, blend)
}

type AnimatedImage struct {
	frameRequest    chan bool
	pos             int
	frames          []*image.RGBA
	delays          []int
	remainingLoops  int
	started         bool
	delayAdjustment float64
}

func (i *AnimatedImage) GetNextFrame() *image.RGBA {

	if !i.started {
		i.started = true
		i.start()
	}

	frame := i.frames[i.pos]

	select {
	case i.frameRequest <- true:
	default:
	}

	return frame
}

func (i *AnimatedImage) start() {

	go func() {
		for {
			delay := time.Duration(float64(i.delays[i.pos])*i.delayAdjustment) * 10 * time.Millisecond

			if delay < adjustDelayUnder {

				// Rounded to nearest int
				framesToDisplay := int(math.Floor((float64(delay) / float64(frameTime)) + 0.5))

				// Show for at least one frame
				if framesToDisplay == 0 {
					framesToDisplay = 1
				}

				//log.Printf("Frame wanted a delay of %d so showing for %d frames", delay, framesToDisplay)

				for x := 0; x < framesToDisplay; x++ {
					// Wait till this frame has been taken
					<-i.frameRequest
				}

			} else {
				// Just sleep. At these amounts noone will notice +-1 frame
				//log.Printf("Sleeping frame %d for %d", i.pos, delay)
				time.Sleep(delay)
			}

			// That was the last frame
			if i.pos == len(i.frames)-1 {

				if i.remainingLoops == -1 {
					// Start again, we are looping forever
					i.pos = 0
				} else if i.remainingLoops > 0 {
					// Start again, we still have at least one loop remaining
					i.pos = 0
					i.remainingLoops--
				} else {
					// We're done, this frame gets shown forever *drops mic*.
					break
				}
			} else {
				i.pos++
			}

		}
	}()

}

// GetPositionFrame returns the frame corresponding to the position given 0....1
func (i *AnimatedImage) GetPositionFrame(position float64, blend bool) *image.RGBA {

	relativePosition := position * float64(len(i.frames)-1)

	previousFrame := int(math.Floor(relativePosition))
	nextFrame := int(math.Ceil(relativePosition))

	framePosition := math.Mod(relativePosition, 1)

	//log.Debugf("GetPositionFrame. Frames:%d Position:%f RelativePosition:%f FramePosition:%f PreviousFrame:%d NextFrame:%d", len(i.frames), position, relativePosition, framePosition, previousFrame, nextFrame)
	if !blend || previousFrame == nextFrame {
		// We can just send back a single frame
		return i.frames[previousFrame]
	}

	maskNext := image.NewUniform(color.Alpha{uint8(255 * framePosition)})

	frame := image.NewRGBA(image.Rect(0, 0, 16, 16))

	draw.Draw(frame, frame.Bounds(), i.frames[previousFrame], image.Point{0, 0}, draw.Src)
	draw.DrawMask(frame, frame.Bounds(), i.frames[nextFrame], image.Point{0, 0}, maskNext, image.Point{0, 0}, draw.Over)

	return frame
}

func LoadImage(src string) Image {
	srcLower := strings.ToLower(src)

	if strings.Contains(srcLower, ".gif") {
		return LoadGif(src)
	} else if strings.Contains(srcLower, ".png") {
		return LoadPng(src)
	} else {
		log.HandleError(fmt.Errorf(src), "Unknown image format")
	}
	return nil
}

func LoadPng(src string) Image {

	file, err := os.Open(src)

	if err != nil {
		log.Fatalf("Could not open png '%s' : %s", src, err)
	}

	img, err := png.Decode(file)
	if err != nil {
		log.Fatalf("PNG decoding failed on image '%s' : %s", src, err)
	}

	return &SingleImage{
		frame: toRGBA(img),
	}
}

func LoadGif(src string) *AnimatedImage {

	file, err := os.Open(src)

	if err != nil {
		log.Fatalf("Could not open gif '%s' : %s", src, err)
	}

	img, err := gif.DecodeAll(file)
	if err != nil {
		log.Fatalf("Gif decoding failed on image '%s' : %s", src, err)
	}

	var frames = []*image.RGBA{}

	for _, frame := range img.Image {
		frames = append(frames, toRGBA(frame))
	}

	loops := img.LoopCount
	if loops == 0 {
		loops = -1
	}

	return &AnimatedImage{
		frames:          frames,
		delays:          img.Delay,
		remainingLoops:  loops,
		frameRequest:    make(chan bool),
		delayAdjustment: 1.0,
	}
}

func toRGBA(in image.Image) *image.RGBA {
	bounds := in.Bounds()
	out := image.NewRGBA(image.Rect(0, 0, bounds.Dx(), bounds.Dy()))
	draw.Draw(out, out.Bounds(), in, bounds.Min, draw.Over)
	return out
}

/*

Alt version using different gif decoder... doesn't work

package util

import (
"bytes"
"fmt"
"image"
"image/color"
"image/draw"
"image/png"
"io/ioutil"
"math"
"os"
"strings"
"time"

"github.com/xanthousphoenix/go-gif"
)

// Approx. framerate of the display
const fps = 30

// How long each frame is displayed for
const frameTime = time.Second / fps

// If a frame's delay is under this duration, we will display it
// for a certain number of frames, rather than for a time.
const adjustDelayUnder = time.Millisecond * 300

type Image interface {
GetNextFrame() *image.RGBA
GetPositionFrame(position float64, blend bool) *image.RGBA
}

type SingleImage struct {
frame *image.RGBA
}

func NewSingleImage(frame *image.RGBA) *SingleImage {
return &SingleImage{frame}
}

func (i *SingleImage) GetNextFrame() *image.RGBA {
return i.frame
}

func (i *SingleImage) GetPositionFrame(position float64, blend bool) *image.RGBA {
return i.frame
}

type AnimatedImage struct {
frameRequest    chan bool
pos             int
frames          []*image.RGBA
delays          []int
remainingLoops  int
started         bool
delayAdjustment float64
}

func (i *AnimatedImage) GetNextFrame() *image.RGBA {

if !i.started {
i.started = true
i.start()
}

frame := i.frames[i.pos]

select {
case i.frameRequest <- true:
default:
}

return frame
}

func (i *AnimatedImage) start() {

go func() {
for {
delay := time.Duration(float64(i.delays[i.pos])*i.delayAdjustment) * 10 * time.Millisecond

if delay < adjustDelayUnder {

// Rounded to nearest int
framesToDisplay := int(math.Floor((float64(delay) / float64(frameTime)) + 0.5))

// Show for at least one frame
if framesToDisplay == 0 {
framesToDisplay = 1
}

//log.Printf("Frame wanted a delay of %d so showing for %d frames", delay, framesToDisplay)

for x := 0; x < framesToDisplay; x++ {
// Wait till this frame has been taken
<-i.frameRequest
}

} else {
// Just sleep. At these amounts noone will notice +-1 frame
//log.Printf("Sleeping frame %d for %d", i.pos, delay)
time.Sleep(delay)
}

// That was the last frame
if i.pos == len(i.frames)-1 {

if i.remainingLoops == -1 {
// Start again, we are looping forever
i.pos = 0
} else if i.remainingLoops > 0 {
// Start again, we still have at least one loop remaining
i.pos = 0
i.remainingLoops--
} else {
// We're done, this frame gets shown forever *drops mic*.
break
}
} else {
i.pos++
}

}
}()

}

// GetPositionFrame returns the frame corresponding to the position given 0....1
func (i *AnimatedImage) GetPositionFrame(position float64, blend bool) *image.RGBA {

relativePosition := position * float64(len(i.frames)-1)

previousFrame := int(math.Floor(relativePosition))
nextFrame := int(math.Ceil(relativePosition))

framePosition := math.Mod(relativePosition, 1)

//log.Debugf("GetPositionFrame. Frames:%d Position:%f RelativePosition:%f FramePosition:%f PreviousFrame:%d NextFrame:%d", len(i.frames), position, relativePosition, framePosition, previousFrame, nextFrame)
if !blend || previousFrame == nextFrame {
// We can just send back a single frame
return i.frames[previousFrame]
}

maskNext := image.NewUniform(color.Alpha{uint8(255 * framePosition)})

frame := image.NewRGBA(image.Rect(0, 0, 16, 16))

draw.Draw(frame, frame.Bounds(), i.frames[previousFrame], image.Point{0, 0}, draw.Src)
draw.DrawMask(frame, frame.Bounds(), i.frames[nextFrame], image.Point{0, 0}, maskNext, image.Point{0, 0}, draw.Over)

return frame
}

func LoadImage(src string) Image {
srcLower := strings.ToLower(src)

if strings.Contains(srcLower, ".gif") {
return LoadGif(src)
} else if strings.Contains(srcLower, ".png") {
return LoadPng(src)
} else {
log.HandleError(fmt.Errorf(src), "Unknown image format")
}
return nil
}

func LoadPng(src string) Image {

file, err := os.Open(src)

if err != nil {
log.Fatalf("Could not open png '%s' : %s", src, err)
}

img, err := png.Decode(file)
if err != nil {
log.Fatalf("PNG decoding failed on image '%s' : %s", src, err)
}

return &SingleImage{
frame: toRGBA(img),
}
}

func LoadGif(src string) *AnimatedImage {

b, err := ioutil.ReadFile(src)
if err != nil {
log.Fatalf("Could not open gif '%s' : %s", src, err)
}

reader := bytes.NewReader(b)

img, err := gif.DecodeAll(reader)
if err != nil {
log.Fatalf("Gif decoding failed on image '%s' : %s", src, err)
}

var delays = []int{}
var frames = []*image.RGBA{}

for _, frame := range img.Frames {
delays = append(delays, int(frame.DelayTime))
frames = append(frames, toRGBA(frame.FrameImage))
}

loops := img.Header.LoopCount
if loops == 0 {
loops = -1
}

return &AnimatedImage{
frames:          frames,
delays:          delays,
remainingLoops:  loops,
frameRequest:    make(chan bool),
delayAdjustment: 1.0,
}
}

func toRGBA(in image.Image) *image.RGBA {
bounds := in.Bounds()
out := image.NewRGBA(image.Rect(0, 0, bounds.Dx(), bounds.Dy()))
draw.Draw(out, out.Bounds(), in, bounds.Min, draw.Over)
return out
}

*/
<|endoftext|>