<commit_before>package main

import (
	"bytes"
	"io"
	"log"
	"os"
	"path/filepath"
	"time"

	"github.com/nictuku/stardew-rocks/parser"
	"github.com/nictuku/stardew-rocks/stardb"
	"github.com/nictuku/stardew-rocks/view"

	"github.com/streadway/amqp"
)

func failOnError(err error, msg string) {
	if err != nil {
		log.Fatalf("%s: %s", msg, err)
	}
}

func wwwDir() string {
	home := os.Getenv("HOME")
	if home == "" {
		home = string(filepath.Separator)
	}
	return filepath.Clean(filepath.Join(home, "www"))
}

func main() {
	conn, err := amqp.Dial("amqp://guest:guest@amqp.stardew.rocks:5672/")
	failOnError(err, "Failed to connect to RabbitMQ")
	defer conn.Close()

	ch, err := conn.Channel()
	failOnError(err, "Failed to open a channel")
	defer ch.Close()
	for _, exc := range []string{"SaveGameInfo-1", "OtherFiles-1"} {
		err = ch.ExchangeDeclare(
			exc,      // name
			"fanout", // type
			false,    // durable
			false,    // auto-deleted
			false,    // internal
			false,    // no-wait
			nil,      // arguments
		)

		failOnError(err, "Failed to declare an exchange")
	}
	q, err := ch.QueueDeclare(
		"",    // name
		false, // durable
		false, // delete when usused
		true,  // exclusive
		false, // no-wait
		nil,   // arguments
	)
	failOnError(err, "Failed to declare a queue")

	err = ch.QueueBind(
		q.Name,         // queue name
		"",             // routing key
		"OtherFiles-1", // exchange
		false,
		nil)
	failOnError(err, "Failed to bind a queue")

	msgs, err := ch.Consume(
		q.Name, // queue
		"",     // consumer
		true,   // auto-ack
		false,  // exclusive
		false,  // no-local
		false,  // no-wait
		nil,    // args
	)
	failOnError(err, "Failed to register a consumer")

	count := 0

	farmMap := parser.LoadFarmMap()

	go func() {
		for d := range msgs {
			count++
			var reader io.Reader = bytes.NewReader(d.Body)
			// The content is usually gzip encoded by we don't have to worry about that.
			// Apparently rabbitMQ or the Go library will decompress it transparently.
			// d.ContentEncoding == "gzip" {
			saveGame, err := parser.ParseSaveGame(reader)
			if err != nil {
				log.Print("Error parsing saved game:", err)
				continue
			}
			if saveGame.Player.Name == "" {
				log.Print("Ignoring save with blank player name")
				continue
			}

			ts := time.Now()

			farm, _, err := stardb.FindOrCreateFarm(stardb.FarmCollection, saveGame.UniqueIDForThisGame, saveGame.Player.Name, saveGame.Player.FarmName)
			if err != nil {
				log.Print("Error fetching farm ID:", err)
				continue
			}

			// GridFS XML save file write.
			// TODO: broken saves (length 0)
			if err := stardb.WriteSaveFile(farm, d.Body, ts); err != nil {
				log.Print("write save file:", err)
				continue
			}
			// The save file is the most critical and it's been updated, so we should be fine.
			if err := stardb.UpdateFarmTime(farm.InternalID, ts); err != nil {
				log.Print("update farm time:", err)
				continue
			}
			if fi, err := stardb.FarmInfoFromSaveGame(saveGame); err != nil {
				log.Print("farm info from save game:", err)
				continue
			} else {
				if err := stardb.UpdateFarmInfo(farm.InternalID, fi); err != nil {
					log.Print("update farm info:", err)
					continue
				}
			}

			// GridFs screenshot write.
			fs, err := stardb.NewScreenshotWriter(farm, ts)
			if err != nil {
				log.Print("Error writing grid screenshot:", err)
				continue
			}

			if err := view.WriteImage(farmMap, saveGame, fs); err != nil {
				log.Print(err)
				fs.Close()
				continue
			}
			fs.Close()
			log.Printf("Wrote grid map file %v", farm.ScreenshotPath())

		}
		log.Printf("Total messages so far: %d", count)

	}()

	log.Printf(" [*] Waiting for messages. To exit press CTRL+C")
	select {}
}
<commit_msg>Write the screenshot first, optionally. Should help with issue #72<commit_after>package main

import (
	"bytes"
	"io"
	"log"
	"os"
	"path/filepath"
	"time"

	"github.com/nictuku/stardew-rocks/parser"
	"github.com/nictuku/stardew-rocks/stardb"
	"github.com/nictuku/stardew-rocks/view"

	"github.com/streadway/amqp"
)

func failOnError(err error, msg string) {
	if err != nil {
		log.Fatalf("%s: %s", msg, err)
	}
}

func wwwDir() string {
	home := os.Getenv("HOME")
	if home == "" {
		home = string(filepath.Separator)
	}
	return filepath.Clean(filepath.Join(home, "www"))
}

func main() {
	conn, err := amqp.Dial("amqp://guest:guest@amqp.stardew.rocks:5672/")
	failOnError(err, "Failed to connect to RabbitMQ")
	defer conn.Close()

	ch, err := conn.Channel()
	failOnError(err, "Failed to open a channel")
	defer ch.Close()
	for _, exc := range []string{"SaveGameInfo-1", "OtherFiles-1"} {
		err = ch.ExchangeDeclare(
			exc,      // name
			"fanout", // type
			false,    // durable
			false,    // auto-deleted
			false,    // internal
			false,    // no-wait
			nil,      // arguments
		)

		failOnError(err, "Failed to declare an exchange")
	}
	q, err := ch.QueueDeclare(
		"",    // name
		false, // durable
		false, // delete when usused
		true,  // exclusive
		false, // no-wait
		nil,   // arguments
	)
	failOnError(err, "Failed to declare a queue")

	err = ch.QueueBind(
		q.Name,         // queue name
		"",             // routing key
		"OtherFiles-1", // exchange
		false,
		nil)
	failOnError(err, "Failed to bind a queue")

	msgs, err := ch.Consume(
		q.Name, // queue
		"",     // consumer
		true,   // auto-ack
		false,  // exclusive
		false,  // no-local
		false,  // no-wait
		nil,    // args
	)
	failOnError(err, "Failed to register a consumer")

	count := 0

	farmMap := parser.LoadFarmMap()

	go func() {
		for d := range msgs {
			count++
			var reader io.Reader = bytes.NewReader(d.Body)
			// The content is usually gzip encoded by we don't have to worry about that.
			// Apparently rabbitMQ or the Go library will decompress it transparently.
			// d.ContentEncoding == "gzip" {
			saveGame, err := parser.ParseSaveGame(reader)
			if err != nil {
				log.Print("Error parsing saved game:", err)
				continue
			}
			if saveGame.Player.Name == "" {
				log.Print("Ignoring save with blank player name")
				continue
			}

			ts := time.Now()

			farm, _, err := stardb.FindOrCreateFarm(stardb.FarmCollection, saveGame.UniqueIDForThisGame, saveGame.Player.Name, saveGame.Player.FarmName)
			if err != nil {
				log.Print("Error fetching farm ID:", err)
				continue
			}
			// GridFs screenshot write. We write this first because as soon as we change
			// the save game (below) users of the site will be given that save's timestamp and
			// will try to open the screenshot. See issue #72.
			// But we treat this screenshot write as optional - just in case the
			// renderer is broken or something, we continue anyway because the most
			// valuable data are the save games.
			fs, err := stardb.NewScreenshotWriter(farm, ts)
			if err != nil {
				log.Print("Error writing grid screenshot:", err)
			} else {
				if err := view.WriteImage(farmMap, saveGame, fs); err != nil {
					log.Print(err)
				} else {
					log.Printf("Wrote grid map file %v", farm.ScreenshotPath())
				}
				fs.Close()
			}

			// GridFS XML save file write.
			// TODO: broken saves (length 0)
			if err := stardb.WriteSaveFile(farm, d.Body, ts); err != nil {
				log.Print("write save file:", err)
				continue
			}
			// The save file is the most critical and it's been updated, so we should be fine.
			if err := stardb.UpdateFarmTime(farm.InternalID, ts); err != nil {
				log.Print("update farm time:", err)
				continue
			}
			if fi, err := stardb.FarmInfoFromSaveGame(saveGame); err != nil {
				log.Print("farm info from save game:", err)
				continue
			} else {
				if err := stardb.UpdateFarmInfo(farm.InternalID, fi); err != nil {
					log.Print("update farm info:", err)
					continue
				}
			}

		}
		log.Printf("Total messages so far: %d", count)

	}()

	log.Printf(" [*] Waiting for messages. To exit press CTRL+C")
	select {}
}
<|endoftext|>


<commit_before>package lua

import (
	"errors"
	"fmt"
	"unsafe"
)

const dbg = false

var ClosureIsNotAvaliable = errors.New("Can't assign a closure")

var lua_tointegerx = luaDLL.NewProc("lua_tointegerx")

func (this Lua) ToInteger(index int) (int, error) {
	var issucceeded uintptr
	value, _, _ := lua_tointegerx.Call(this.State(), uintptr(index),
		uintptr(unsafe.Pointer(&issucceeded)))
	if issucceeded != 0 {
		return int(value), nil
	} else {
		return 0, errors.New("ToInteger: the value in not integer on the stack")
	}
}

var lua_tolstring = luaDLL.NewProc("lua_tolstring")

func (this Lua) ToBytes(index int) []byte {
	var length uintptr
	p, _, _ := lua_tolstring.Call(this.State(),
		uintptr(index),
		uintptr(unsafe.Pointer(&length)))
	if length <= 0 {
		return []byte{}
	} else {
		return CGoBytes(p, length)
	}
}

func (this Lua) ToString(index int) (string, error) {
	var length uintptr
	p, _, _ := lua_tolstring.Call(this.State(),
		uintptr(index),
		uintptr(unsafe.Pointer(&length)))
	return CGoStringN(p, length), nil
}

type TString string

func (this TString) Push(L Lua) int {
	L.PushString(string(this))
	return 1
}

var lua_touserdata = luaDLL.NewProc("lua_touserdata")

func (this Lua) ToUserData(index int) unsafe.Pointer {
	rv, _, _ := lua_touserdata.Call(this.State(), uintptr(index))
	return unsafe.Pointer(rv)
}

var lua_toboolean = luaDLL.NewProc("lua_toboolean")

func (this Lua) ToBool(index int) bool {
	rv, _, _ := lua_toboolean.Call(this.State(), uintptr(index))
	return rv != 0
}

type TRawString []byte

func (this TRawString) String() (string, error) {
	if len(this) <= 0 {
		return "", nil
	} else {
		return string(this), nil
	}
}

func (this TRawString) Push(L Lua) int {
	L.PushBytes(this)
	return 1
}

var lua_tocfunction = luaDLL.NewProc("lua_tocfunction")

func (this Lua) ToCFunction(index int) uintptr {
	rc, _, _ := lua_tocfunction.Call(this.State(), uintptr(index))
	return rc
}

type TCFunction uintptr

func (this TCFunction) Push(L Lua) int {
	L.PushCFunction(uintptr(this))
	return 1
}

type TLuaFunction []byte

func (this TLuaFunction) Push(L Lua) int {
	if L.LoadBufferX("(annonymous)", this, "b") != nil {
		return 1
	} else {
		return 0
	}
}

type TLightUserData struct {
	Data unsafe.Pointer
}

func (this TLightUserData) Push(L Lua) int {
	L.PushLightUserData(this.Data)
	return 1
}

type TFullUserData []byte

func (this TFullUserData) Push(L Lua) int {
	size := len([]byte(this))
	p := L.NewUserData(uintptr(size))
	for i := 0; i < size; i++ {
		*(*byte)(unsafe.Pointer(uintptr(p) + uintptr(i))) = this[i]
	}
	return 1
}

var lua_next = luaDLL.NewProc("lua_next")

func (this Lua) Next(index int) int {
	rc, _, _ := lua_next.Call(this.State(), uintptr(index))
	return int(rc)
}

var lua_rawlen = luaDLL.NewProc("lua_rawlen")

func (this Lua) RawLen(index int) uintptr {
	size, _, _ := lua_rawlen.Call(this.State(), uintptr(index))
	return size
}

type MetaTableOwner struct {
	Body Object
	Meta *TTable
}

func (this *MetaTableOwner) Push(L Lua) int {
	this.Body.Push(L)
	if nameObj, nameObj_ok := this.Meta.Dict["__name"]; nameObj_ok {
		if name, name_ok := nameObj.(TRawString); name_ok {
			if dbg {
				print("found meta-name: ", string(name), "\n")
			}
			L.NewMetaTable(string(name))
			this.Meta.PushWithoutNewTable(L)
		} else {
			if dbg {
				print("found meta-name, but could not cast\n")
			}
			this.Meta.Push(L)
		}
	} else {
		if dbg {
			print("not meta table\n")
		}
		this.Meta.Push(L)
	}
	L.SetMetaTable(-2)
	return 1
}

type TTable struct {
	Dict  map[string]Object
	Array map[int]Object
}

func (this TTable) PushWithoutNewTable(L Lua) int {
	for key, val := range this.Dict {
		L.PushString(key)
		val.Push(L)
		L.SetTable(-3)
	}
	for key, val := range this.Array {
		L.Push(key)
		val.Push(L)
		L.SetTable(-3)
	}
	return 1
}

func (this TTable) Push(L Lua) int {
	L.NewTable()
	return this.PushWithoutNewTable(L)
}

func (this Lua) ForInDo(index int, proc func(Lua) error) error {
	this.PushNil() // set first key as nil
	if index < 0 {
		index--
	}
	for this.Next(index) != 0 {
		// Next push KEY and VAL
		err := proc(this)
		/* removes 'value'; keeps 'key' for next iteration */
		this.Pop(1)
		if err != nil {
			this.Pop(1)
			return err
		}
		/*
			While traversing a table, do not call lua_tolstring
			directly on a key, unless you know that the key is
			actually a string. Recall that lua_tolstring may
			change the value at the given index; this confuses
			the next call to lua_next.
		*/
	}
	return nil
}

func (this Lua) ToTable(index int) (*TTable, error) {
	table := make(map[string]Object)
	array := make(map[int]Object)

	err := this.ForInDo(index, func(this Lua) error {
		key, err := this.ToObject(-2)
		if err != nil {
			return err
		}
		val, err := this.ToObject(-1)
		if err != nil {
			return err
		}
		switch t := key.(type) {
		case TString:
			table[string(t)] = val
		case TRawString:
			table[string(t)] = val
		case Integer:
			array[int(t)] = val
		case nil:
			table[""] = val
		}
		return nil
	})
	return &TTable{Dict: table, Array: array}, err
}

type TBool struct {
	Value bool
}

func (this TBool) Push(L Lua) int {
	L.PushBool(this.Value)
	return 1
}

type TNil struct{}

func (this TNil) Push(L Lua) int {
	L.PushNil()
	return 1
}

var NG_UPVALUE_NAME = map[string]struct{}{}

func (this Lua) ToObject(index int) (Object, error) {
	seek_metatable := false
	var err error = nil
	var result Object
	switch this.GetType(index) {
	case LUA_TBOOLEAN:
		result = TBool{this.ToBool(index)}
	case LUA_TFUNCTION:
		if p := this.ToCFunction(index); p != 0 {
			// CFunction
			result = TCFunction(p)
		} else {
			// LuaFunction
			upvalues := this.GetUpValues(index)
			for _, u := range upvalues {
				if _, ok := NG_UPVALUE_NAME[u.Name]; ok {
					if dbg {
						print(u.Name, ":", this.TypeName(u.Type), "\n")
					}
					return nil, ClosureIsNotAvaliable
				}
			}
			this.PushValue(index)
			result = TLuaFunction(this.Dump())
			this.Pop(1)
		}
	case LUA_TLIGHTUSERDATA:
		result = TLightUserData{Data: this.ToUserData(index)}
		seek_metatable = true
	case LUA_TNIL:
		result = TNil{}
	case LUA_TNUMBER:
		var int_result int
		int_result, err = this.ToInteger(index)
		result = Integer(int_result)
	case LUA_TSTRING:
		result = TRawString(this.ToBytes(index))
	case LUA_TTABLE:
		result, err = this.ToTable(index)
		seek_metatable = true
	case LUA_TUSERDATA:
		size := this.RawLen(index)
		ptr := this.ToUserData(index)
		result = TFullUserData(CGoBytes(uintptr(ptr), uintptr(size)))
		seek_metatable = true
	default:
		return nil, errors.New("lua.ToSomeThing: Not supported type found.")
	}
	if err != nil {
		return nil, err
	}
	if seek_metatable && this.GetMetaTable(index) {
		metatable, err := this.ToTable(-1)
		defer this.Pop(1)
		if err != nil {
			return nil, err
		}
		result = &MetaTableOwner{Body: result, Meta: metatable}
	}
	return result, nil
}

func (this Lua) ToInterface(index int) (interface{}, error) {
	t := this.GetType(index)
	switch t {
	case LUA_TBOOLEAN:
		return this.ToBool(index), nil
	case LUA_TNIL:
		return nil, nil
	case LUA_TSTRING:
		return this.ToString(index)
	case LUA_TNUMBER:
		intValue, err := this.ToInteger(index)
		if err != nil {
			return nil, err
		}
		return intValue, nil
	case LUA_TTABLE:
		table := map[interface{}]interface{}{}
		err := this.ForInDo(index, func(this Lua) error {
			key, err := this.ToInterface(-2)
			if err != nil {
				return err
			}
			val, err := this.ToInterface(-1)
			if err != nil {
				return err
			}
			table[key] = val
			return nil
		})
		return table, err
	default:
		return nil, fmt.Errorf("Not support Lua type %v", t)
	}
}
<commit_msg>Internal. lua/ : Remove a warning on go vet<commit_after>package lua

import (
	"errors"
	"fmt"
	"unsafe"
)

const dbg = false

var ClosureIsNotAvaliable = errors.New("Can't assign a closure")

var lua_tointegerx = luaDLL.NewProc("lua_tointegerx")

func (this Lua) ToInteger(index int) (int, error) {
	var issucceeded uintptr
	value, _, _ := lua_tointegerx.Call(this.State(), uintptr(index),
		uintptr(unsafe.Pointer(&issucceeded)))
	if issucceeded != 0 {
		return int(value), nil
	} else {
		return 0, errors.New("ToInteger: the value in not integer on the stack")
	}
}

var lua_tolstring = luaDLL.NewProc("lua_tolstring")

func (this Lua) ToBytes(index int) []byte {
	var length uintptr
	p, _, _ := lua_tolstring.Call(this.State(),
		uintptr(index),
		uintptr(unsafe.Pointer(&length)))
	if length <= 0 {
		return []byte{}
	} else {
		return CGoBytes(p, length)
	}
}

func (this Lua) ToString(index int) (string, error) {
	var length uintptr
	p, _, _ := lua_tolstring.Call(this.State(),
		uintptr(index),
		uintptr(unsafe.Pointer(&length)))
	return CGoStringN(p, length), nil
}

type TString string

func (this TString) Push(L Lua) int {
	L.PushString(string(this))
	return 1
}

var lua_touserdata = luaDLL.NewProc("lua_touserdata")

func (this Lua) ToUserData(index int) unsafe.Pointer {
	rv, _, _ := lua_touserdata.Call(this.State(), uintptr(index))
	return unsafe.Pointer(rv)
}

var lua_toboolean = luaDLL.NewProc("lua_toboolean")

func (this Lua) ToBool(index int) bool {
	rv, _, _ := lua_toboolean.Call(this.State(), uintptr(index))
	return rv != 0
}

type TRawString []byte

func (this TRawString) String() (string, error) {
	if len(this) <= 0 {
		return "", nil
	} else {
		return string(this), nil
	}
}

func (this TRawString) Push(L Lua) int {
	L.PushBytes(this)
	return 1
}

var lua_tocfunction = luaDLL.NewProc("lua_tocfunction")

func (this Lua) ToCFunction(index int) uintptr {
	rc, _, _ := lua_tocfunction.Call(this.State(), uintptr(index))
	return rc
}

type TCFunction uintptr

func (this TCFunction) Push(L Lua) int {
	L.PushCFunction(uintptr(this))
	return 1
}

type TLuaFunction []byte

func (this TLuaFunction) Push(L Lua) int {
	if L.LoadBufferX("(annonymous)", this, "b") != nil {
		return 1
	} else {
		return 0
	}
}

type TLightUserData struct {
	Data unsafe.Pointer
}

func (this TLightUserData) Push(L Lua) int {
	L.PushLightUserData(this.Data)
	return 1
}

type TFullUserData []byte

func (this TFullUserData) Push(L Lua) int {
	size := len([]byte(this))
	p := L.NewUserData(uintptr(size))
	copyMemory(p, uintptr(unsafe.Pointer(&this[0])), uintptr(size))
	return 1
}

var lua_next = luaDLL.NewProc("lua_next")

func (this Lua) Next(index int) int {
	rc, _, _ := lua_next.Call(this.State(), uintptr(index))
	return int(rc)
}

var lua_rawlen = luaDLL.NewProc("lua_rawlen")

func (this Lua) RawLen(index int) uintptr {
	size, _, _ := lua_rawlen.Call(this.State(), uintptr(index))
	return size
}

type MetaTableOwner struct {
	Body Object
	Meta *TTable
}

func (this *MetaTableOwner) Push(L Lua) int {
	this.Body.Push(L)
	if nameObj, nameObj_ok := this.Meta.Dict["__name"]; nameObj_ok {
		if name, name_ok := nameObj.(TRawString); name_ok {
			if dbg {
				print("found meta-name: ", string(name), "\n")
			}
			L.NewMetaTable(string(name))
			this.Meta.PushWithoutNewTable(L)
		} else {
			if dbg {
				print("found meta-name, but could not cast\n")
			}
			this.Meta.Push(L)
		}
	} else {
		if dbg {
			print("not meta table\n")
		}
		this.Meta.Push(L)
	}
	L.SetMetaTable(-2)
	return 1
}

type TTable struct {
	Dict  map[string]Object
	Array map[int]Object
}

func (this TTable) PushWithoutNewTable(L Lua) int {
	for key, val := range this.Dict {
		L.PushString(key)
		val.Push(L)
		L.SetTable(-3)
	}
	for key, val := range this.Array {
		L.Push(key)
		val.Push(L)
		L.SetTable(-3)
	}
	return 1
}

func (this TTable) Push(L Lua) int {
	L.NewTable()
	return this.PushWithoutNewTable(L)
}

func (this Lua) ForInDo(index int, proc func(Lua) error) error {
	this.PushNil() // set first key as nil
	if index < 0 {
		index--
	}
	for this.Next(index) != 0 {
		// Next push KEY and VAL
		err := proc(this)
		/* removes 'value'; keeps 'key' for next iteration */
		this.Pop(1)
		if err != nil {
			this.Pop(1)
			return err
		}
		/*
			While traversing a table, do not call lua_tolstring
			directly on a key, unless you know that the key is
			actually a string. Recall that lua_tolstring may
			change the value at the given index; this confuses
			the next call to lua_next.
		*/
	}
	return nil
}

func (this Lua) ToTable(index int) (*TTable, error) {
	table := make(map[string]Object)
	array := make(map[int]Object)

	err := this.ForInDo(index, func(this Lua) error {
		key, err := this.ToObject(-2)
		if err != nil {
			return err
		}
		val, err := this.ToObject(-1)
		if err != nil {
			return err
		}
		switch t := key.(type) {
		case TString:
			table[string(t)] = val
		case TRawString:
			table[string(t)] = val
		case Integer:
			array[int(t)] = val
		case nil:
			table[""] = val
		}
		return nil
	})
	return &TTable{Dict: table, Array: array}, err
}

type TBool struct {
	Value bool
}

func (this TBool) Push(L Lua) int {
	L.PushBool(this.Value)
	return 1
}

type TNil struct{}

func (this TNil) Push(L Lua) int {
	L.PushNil()
	return 1
}

var NG_UPVALUE_NAME = map[string]struct{}{}

func (this Lua) ToObject(index int) (Object, error) {
	seek_metatable := false
	var err error = nil
	var result Object
	switch this.GetType(index) {
	case LUA_TBOOLEAN:
		result = TBool{this.ToBool(index)}
	case LUA_TFUNCTION:
		if p := this.ToCFunction(index); p != 0 {
			// CFunction
			result = TCFunction(p)
		} else {
			// LuaFunction
			upvalues := this.GetUpValues(index)
			for _, u := range upvalues {
				if _, ok := NG_UPVALUE_NAME[u.Name]; ok {
					if dbg {
						print(u.Name, ":", this.TypeName(u.Type), "\n")
					}
					return nil, ClosureIsNotAvaliable
				}
			}
			this.PushValue(index)
			result = TLuaFunction(this.Dump())
			this.Pop(1)
		}
	case LUA_TLIGHTUSERDATA:
		result = TLightUserData{Data: this.ToUserData(index)}
		seek_metatable = true
	case LUA_TNIL:
		result = TNil{}
	case LUA_TNUMBER:
		var int_result int
		int_result, err = this.ToInteger(index)
		result = Integer(int_result)
	case LUA_TSTRING:
		result = TRawString(this.ToBytes(index))
	case LUA_TTABLE:
		result, err = this.ToTable(index)
		seek_metatable = true
	case LUA_TUSERDATA:
		size := this.RawLen(index)
		ptr := this.ToUserData(index)
		result = TFullUserData(CGoBytes(uintptr(ptr), uintptr(size)))
		seek_metatable = true
	default:
		return nil, errors.New("lua.ToSomeThing: Not supported type found.")
	}
	if err != nil {
		return nil, err
	}
	if seek_metatable && this.GetMetaTable(index) {
		metatable, err := this.ToTable(-1)
		defer this.Pop(1)
		if err != nil {
			return nil, err
		}
		result = &MetaTableOwner{Body: result, Meta: metatable}
	}
	return result, nil
}

func (this Lua) ToInterface(index int) (interface{}, error) {
	t := this.GetType(index)
	switch t {
	case LUA_TBOOLEAN:
		return this.ToBool(index), nil
	case LUA_TNIL:
		return nil, nil
	case LUA_TSTRING:
		return this.ToString(index)
	case LUA_TNUMBER:
		intValue, err := this.ToInteger(index)
		if err != nil {
			return nil, err
		}
		return intValue, nil
	case LUA_TTABLE:
		table := map[interface{}]interface{}{}
		err := this.ForInDo(index, func(this Lua) error {
			key, err := this.ToInterface(-2)
			if err != nil {
				return err
			}
			val, err := this.ToInterface(-1)
			if err != nil {
				return err
			}
			table[key] = val
			return nil
		})
		return table, err
	default:
		return nil, fmt.Errorf("Not support Lua type %v", t)
	}
}
<|endoftext|>


<commit_before>package gluahttp

import "github.com/yuin/gopher-lua"
import "net/http"
import "net/http/cookiejar"
import "fmt"
import "io/ioutil"
import "strings"

type httpModule struct {
	client *http.Client
}

func NewHttpModule() *httpModule {
	cookieJar, _ := cookiejar.New(nil)

	return &httpModule{
		client: &http.Client{
			Jar: cookieJar,
		},
	}
}

func (h *httpModule) Loader(L *lua.LState) int {
	mod := L.SetFuncs(L.NewTable(), map[string]lua.LGFunction{
		"get":     h.get,
		"delete":  h.delete,
		"head":    h.head,
		"patch":   h.patch,
		"post":    h.post,
		"put":     h.put,
		"request": h.request,
	})
	L.Push(mod)
	return 1
}

func (h *httpModule) get(L *lua.LState) int {
	return h.doRequest(L, "get", L.ToString(1), L.ToTable(2))
}

func (h *httpModule) delete(L *lua.LState) int {
	return h.doRequest(L, "delete", L.ToString(1), L.ToTable(2))
}

func (h *httpModule) head(L *lua.LState) int {
	return h.doRequest(L, "head", L.ToString(1), L.ToTable(2))
}

func (h *httpModule) patch(L *lua.LState) int {
	return h.doRequest(L, "patch", L.ToString(1), L.ToTable(2))
}

func (h *httpModule) post(L *lua.LState) int {
	return h.doRequest(L, "post", L.ToString(1), L.ToTable(2))
}

func (h *httpModule) put(L *lua.LState) int {
	return h.doRequest(L, "put", L.ToString(1), L.ToTable(2))
}

func (h *httpModule) request(L *lua.LState) int {
	return h.doRequest(L, L.ToString(1), L.ToString(2), L.ToTable(3))
}

func (h *httpModule) doRequest(L *lua.LState, method string, url string, options *lua.LTable) int {
	req, err := http.NewRequest(strings.ToUpper(method), url, nil)
	if err != nil {
		L.Push(lua.LNil)
		L.Push(lua.LString(fmt.Sprintf("%s", err)))
		return 2
	}

	if options != nil {
		if reqHeaders, ok := options.RawGet(lua.LString("headers")).(*lua.LTable); ok {
			reqHeaders.ForEach(func(key lua.LValue, value lua.LValue) {
				req.Header.Set(key.String(), value.String())
			})
		}

		if reqCookies, ok := options.RawGet(lua.LString("cookies")).(*lua.LTable); ok {
			reqCookies.ForEach(func(key lua.LValue, value lua.LValue) {
				req.AddCookie(&http.Cookie{Name: key.String(), Value: value.String()})
			})
		}

		switch reqQuery := options.RawGet(lua.LString("query")).(type) {
		case *lua.LNilType:
			break

		case lua.LString:
			req.URL.RawQuery = reqQuery.String()
			break
		}

		switch reqForm := options.RawGet(lua.LString("form")).(type) {
		case *lua.LNilType:
			break

		case lua.LString:
			req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
			req.Body = ioutil.NopCloser(strings.NewReader(reqForm.String()))
			break
		}
	}

	res, err := h.client.Do(req)
	if err != nil {
		L.Push(lua.LNil)
		L.Push(lua.LString(fmt.Sprintf("%s", err)))
		return 2
	}

	defer res.Body.Close()
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		L.Push(lua.LNil)
		L.Push(lua.LString(fmt.Sprintf("%s", err)))
		return 2
	}

	headers := L.NewTable()
	for key, _ := range res.Header {
		headers.RawSetString(key, lua.LString(res.Header.Get(key)))
	}

	cookies := L.NewTable()
	for _, cookie := range res.Cookies() {
		cookies.RawSetString(cookie.Name, lua.LString(cookie.Value))
	}

	response := L.NewTable()
	response.RawSetString("body", lua.LString(body))
	response.RawSetString("headers", headers)
	response.RawSetString("cookies", cookies)
	response.RawSetString("status_code", lua.LNumber(res.StatusCode))

	L.Push(response)

	return 1
}
<commit_msg>Refactor in preparation for http.request_batch<commit_after>package gluahttp

import "github.com/yuin/gopher-lua"
import "net/http"
import "net/http/cookiejar"
import "fmt"
import "io/ioutil"
import "strings"

type httpModule struct {
	client *http.Client
}

func NewHttpModule() *httpModule {
	cookieJar, _ := cookiejar.New(nil)

	return &httpModule{
		client: &http.Client{
			Jar: cookieJar,
		},
	}
}

func (h *httpModule) Loader(L *lua.LState) int {
	mod := L.SetFuncs(L.NewTable(), map[string]lua.LGFunction{
		"get":     h.get,
		"delete":  h.delete,
		"head":    h.head,
		"patch":   h.patch,
		"post":    h.post,
		"put":     h.put,
		"request": h.request,
	})
	L.Push(mod)
	return 1
}

func (h *httpModule) get(L *lua.LState) int {
	return h.doRequestAndPush(L, "get", L.ToString(1), L.ToTable(2))
}

func (h *httpModule) delete(L *lua.LState) int {
	return h.doRequestAndPush(L, "delete", L.ToString(1), L.ToTable(2))
}

func (h *httpModule) head(L *lua.LState) int {
	return h.doRequestAndPush(L, "head", L.ToString(1), L.ToTable(2))
}

func (h *httpModule) patch(L *lua.LState) int {
	return h.doRequestAndPush(L, "patch", L.ToString(1), L.ToTable(2))
}

func (h *httpModule) post(L *lua.LState) int {
	return h.doRequestAndPush(L, "post", L.ToString(1), L.ToTable(2))
}

func (h *httpModule) put(L *lua.LState) int {
	return h.doRequestAndPush(L, "put", L.ToString(1), L.ToTable(2))
}

func (h *httpModule) request(L *lua.LState) int {
	return h.doRequestAndPush(L, L.ToString(1), L.ToString(2), L.ToTable(3))
}

func (h *httpModule) doRequest(L *lua.LState, method string, url string, options *lua.LTable) (*lua.LTable, error) {
	req, err := http.NewRequest(strings.ToUpper(method), url, nil)
	if err != nil {
		return nil, err
	}

	if options != nil {
		if reqHeaders, ok := options.RawGet(lua.LString("headers")).(*lua.LTable); ok {
			reqHeaders.ForEach(func(key lua.LValue, value lua.LValue) {
				req.Header.Set(key.String(), value.String())
			})
		}

		if reqCookies, ok := options.RawGet(lua.LString("cookies")).(*lua.LTable); ok {
			reqCookies.ForEach(func(key lua.LValue, value lua.LValue) {
				req.AddCookie(&http.Cookie{Name: key.String(), Value: value.String()})
			})
		}

		switch reqQuery := options.RawGet(lua.LString("query")).(type) {
		case *lua.LNilType:
			break

		case lua.LString:
			req.URL.RawQuery = reqQuery.String()
			break
		}

		switch reqForm := options.RawGet(lua.LString("form")).(type) {
		case *lua.LNilType:
			break

		case lua.LString:
			req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
			req.Body = ioutil.NopCloser(strings.NewReader(reqForm.String()))
			break
		}
	}

	res, err := h.client.Do(req)
	if err != nil {
		return nil, err
	}

	defer res.Body.Close()
	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, err
	}

	headers := L.NewTable()
	for key, _ := range res.Header {
		headers.RawSetString(key, lua.LString(res.Header.Get(key)))
	}

	cookies := L.NewTable()
	for _, cookie := range res.Cookies() {
		cookies.RawSetString(cookie.Name, lua.LString(cookie.Value))
	}

	response := L.NewTable()
	response.RawSetString("body", lua.LString(body))
	response.RawSetString("headers", headers)
	response.RawSetString("cookies", cookies)
	response.RawSetString("status_code", lua.LNumber(res.StatusCode))

	return response, nil
}

func (h *httpModule) doRequestAndPush(L *lua.LState, method string, url string, options *lua.LTable) int {
	response, err := h.doRequest(L, method, url, options)

	if err != nil {
		L.Push(lua.LNil)
		L.Push(lua.LString(fmt.Sprintf("%s", err)))
		return 2
	}

	L.Push(response)
	return 1
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
	"strings"
	"time"

	"github.com/patrickmn/go-cache"
)

const (
	roleLowerBody        = "legislatorLowerBody"
	roleUpperBody        = "legislatorUpperBody"
	roleHeadOfGovernment = "headOfGovernment"
	roleLevelCountry     = "country"
	roleLevelState       = "administrativeArea1"
	areaHouse            = "House"
	areaSenate           = "Senate"
	areaGovernor         = "Governor"
)

var baseURL = "https://www.googleapis.com/civicinfo/v2/representatives"
var phoneRegex = regexp.MustCompile(`\((\d{3})\)\s+(\d{3})[\s\-](\d{4})`)
var roleLevel = [2]string{"country", "administrativeArea1"}

// RepFinder provides a mechanism to find local reps given an address.
type RepFinder interface {
	GetReps(address string) (*LocalReps, *Address, error)
}

// APIError is an error returned by the Google civic API, which also
// implements the error interface.
type APIError struct {
	Code    int
	Message string
	Errors  []struct {
		Domain  string
		Reason  string
		Message string
	}
}

func (ae *APIError) Error() string {
	var buf bytes.Buffer
	fmt.Fprintf(&buf, "%d %s", ae.Code, ae.Message)
	for _, e := range ae.Errors {
		if e.Message != ae.Message { // don't duplicate messages
			fmt.Fprintf(&buf, ";[domain=%s, reason=%s: %s]", e.Domain, e.Reason, e.Message)
		}
	}
	return buf.String()
}

type Office struct {
	Name            string
	DivisionId      string
	Levels          []string
	Roles           []string
	OfficialIndices []int
}

type Official struct {
	Name     string
	Address  []Address
	Party    string
	Phones   []string
	PhotoUrl string
	Channels []struct {
		Id   string
		Type string
	}
}

// apiResponse is the response from the civic API. It encapsulates valid
// responses that set the normalized input, offices and officials,
// as well as error responses.
type apiResponse struct {
	NormalizedInput *Address
	Offices         []Office
	Officials       []Official
	Error           *APIError
}

// toLocalReps converts an API response to a set of local reps. In addition,
// it also returns the normalized address for which the response is valid.
func (r *apiResponse) toLocalReps() (*LocalReps, *Address, error) {
	if r.Error != nil {
		return nil, nil, r.Error
	}
	if len(r.Offices) == 0 {
		return nil, nil, fmt.Errorf("no offices found ")
	}
	ret := &LocalReps{}
	for _, o := range r.Offices {
		area := o.Area()
		if area == "" {
			continue
		}
		for _, i := range o.OfficialIndices {
			official := r.Officials[i]
			var phone string
			if len(official.Phones) > 0 {
				phone = official.Phones[0]
			} else {
				continue
			}
			c := &Contact{
				ID:       fmt.Sprintf("%s-%s", r.NormalizedInput.State, strings.Replace(official.Name, " ", "", -1)),
				Name:     official.Name,
				Phone:    reformattedPhone(phone),
				PhotoURL: official.PhotoUrl,
				Party:    official.Party,
				State:    r.NormalizedInput.State,
				Area:     area,
			}
			switch area {
			case areaHouse:
				ret.HouseRep = c
			case areaSenate:
				ret.Senators = append(ret.Senators, c)
			case areaGovernor:
				ret.Governor = c
			}
		}
	}
	return ret, r.NormalizedInput, nil
}

func (x *Office) Area() string {
	for _, level := range x.Levels {
		for _, role := range x.Roles {
			switch {
			case level == roleLevelCountry && role == roleLowerBody:
				return areaHouse
			case level == roleLevelCountry && role == roleUpperBody:
				return areaSenate
			// Civic API returns governor and deputy governor under same
			// role level and role, comparing the name is the best we can do
			// with this dataset
			case level == roleLevelState && role == roleHeadOfGovernment && x.Name == "Governor":
				return areaGovernor
			}
		}
	}
	return ""
}

// civicAPI provides a semantic interface to the Google civic API.
type civicAPI struct {
	key string
	c   *http.Client
}

// NewCivicAPI returns an instance of the civic API.
func NewCivicAPI(key string, client *http.Client) RepFinder {
	return &civicAPI{
		key: key,
		c:   client,
	}
}

// GetReps returns local representatives for the supplied address.
func (c *civicAPI) GetReps(address string) (*LocalReps, *Address, error) {
	var u, _ = url.Parse(baseURL)
	q := u.Query()
	for _, l := range roleLevel {
		q.Add("levels", l)
	}
	q.Set("key", c.key)
	q.Set("address", url.QueryEscape(address))
	u.RawQuery = q.Encode()
	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return nil, nil, err
	}
	res, err := c.c.Do(req)
	if err != nil {
		return nil, nil, err
	}
	defer res.Body.Close()
	b, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, nil, err
	}
	var ar apiResponse
	err = json.Unmarshal(b, &ar)
	if err != nil {
		return nil, nil, err
	}
	return ar.toLocalReps()
}

// repCache implements a cache layer on top of a delegate rep finder.
type repCache struct {
	delegate RepFinder
	cache    *cache.Cache
}

type cacheItem struct {
	reps LocalReps
	addr Address
}

func NewRepCache(delegate RepFinder, ttl time.Duration, gc time.Duration) RepFinder {
	return &repCache{
		delegate: delegate,
		cache:    cache.New(ttl, gc),
	}
}

// reformat phone numbers that come from the google civic API
func reformattedPhone(civicPhone string) string {
	result := phoneRegex.FindStringSubmatch(civicPhone)

	if len(result) >= 3 {
		return fmt.Sprintf("%s-%s-%s", result[1], result[2], result[3])
	}

	return civicPhone
}

// GetReps returns local representatives for the supplied address.
func (r *repCache) GetReps(address string) (*LocalReps, *Address, error) {
	data, ok := r.cache.Get(address)
	if ok {
		ci := data.(*cacheItem)
		reps := ci.reps
		addr := ci.addr
		return &reps, &addr, nil
	}
	reps, addr, err := r.delegate.GetReps(address)
	if err != nil {
		return nil, nil, err
	}
	ci := &cacheItem{reps: *reps, addr: *addr}
	r.cache.Set(address, ci, cache.DefaultExpiration)
	return reps, addr, nil
}
<commit_msg>Add and use Official.Phone and Official.ID<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
	"strings"
	"time"

	"github.com/patrickmn/go-cache"
)

const (
	roleLowerBody        = "legislatorLowerBody"
	roleUpperBody        = "legislatorUpperBody"
	roleHeadOfGovernment = "headOfGovernment"
	roleLevelCountry     = "country"
	roleLevelState       = "administrativeArea1"
	areaHouse            = "House"
	areaSenate           = "Senate"
	areaGovernor         = "Governor"
)

var baseURL = "https://www.googleapis.com/civicinfo/v2/representatives"
var phoneRegex = regexp.MustCompile(`\((\d{3})\)\s+(\d{3})[\s\-](\d{4})`)
var roleLevel = [2]string{"country", "administrativeArea1"}

// RepFinder provides a mechanism to find local reps given an address.
type RepFinder interface {
	GetReps(address string) (*LocalReps, *Address, error)
}

// APIError is an error returned by the Google civic API, which also
// implements the error interface.
type APIError struct {
	Code    int
	Message string
	Errors  []struct {
		Domain  string
		Reason  string
		Message string
	}
}

func (ae *APIError) Error() string {
	var buf bytes.Buffer
	fmt.Fprintf(&buf, "%d %s", ae.Code, ae.Message)
	for _, e := range ae.Errors {
		if e.Message != ae.Message { // don't duplicate messages
			fmt.Fprintf(&buf, ";[domain=%s, reason=%s: %s]", e.Domain, e.Reason, e.Message)
		}
	}
	return buf.String()
}

type Office struct {
	Name            string
	DivisionId      string
	Levels          []string
	Roles           []string
	OfficialIndices []int
}

type Official struct {
	Name     string
	Address  []Address
	Party    string
	Phones   []string
	PhotoUrl string
	Channels []struct {
		Id   string
		Type string
	}
}

// apiResponse is the response from the civic API. It encapsulates valid
// responses that set the normalized input, offices and officials,
// as well as error responses.
type apiResponse struct {
	NormalizedInput *Address
	Offices         []Office
	Officials       []Official
	Error           *APIError
}

// toLocalReps converts an API response to a set of local reps. In addition,
// it also returns the normalized address for which the response is valid.
func (r *apiResponse) toLocalReps() (*LocalReps, *Address, error) {
	if r.Error != nil {
		return nil, nil, r.Error
	}
	if len(r.Offices) == 0 {
		return nil, nil, fmt.Errorf("no offices found ")
	}
	ret := &LocalReps{}
	for _, o := range r.Offices {
		area := o.Area()
		if area == "" {
			continue
		}
		for _, i := range o.OfficialIndices {
			official := r.Officials[i]
			phone := official.Phone()
			if phone == "" {
				continue
			}
			c := &Contact{
				ID:       fmt.Sprintf("%s-%s", r.NormalizedInput.State, official.ID()),
				Name:     official.Name,
				Phone:    phone,
				PhotoURL: official.PhotoUrl,
				Party:    official.Party,
				State:    r.NormalizedInput.State,
				Area:     area,
			}
			switch area {
			case areaHouse:
				ret.HouseRep = c
			case areaSenate:
				ret.Senators = append(ret.Senators, c)
			case areaGovernor:
				ret.Governor = c
			}
		}
	}
	return ret, r.NormalizedInput, nil
}

func (x *Office) Area() string {
	for _, level := range x.Levels {
		for _, role := range x.Roles {
			switch {
			case level == roleLevelCountry && role == roleLowerBody:
				return areaHouse
			case level == roleLevelCountry && role == roleUpperBody:
				return areaSenate
			// Civic API returns governor and deputy governor under same
			// role level and role, comparing the name is the best we can do
			// with this dataset
			case level == roleLevelState && role == roleHeadOfGovernment && x.Name == "Governor":
				return areaGovernor
			}
		}
	}
	return ""
}

// Phone returns a properly formatted phone number for an Official.
// If Phone returns "", no phone number is available.
func (x *Official) Phone() string {
	// Plans for this function:
	// Now: just use whatever the civic API returns.
	// Better: augment with external list of phone numbers, pick at random from them.
	// Mo' betta: select from options based on a combination of precise location
	// and which phone numbers are more likely to get through based on recent history.
	if len(x.Phones) == 0 {
		return ""
	}
	return reformattedPhone(x.Phones[0])
}

var spaceReplacer = strings.NewReplacer(" ", "")

func (x *Official) ID() string {
	return spaceReplacer.Replace(x.Name)
}

// civicAPI provides a semantic interface to the Google civic API.
type civicAPI struct {
	key string
	c   *http.Client
}

// NewCivicAPI returns an instance of the civic API.
func NewCivicAPI(key string, client *http.Client) RepFinder {
	return &civicAPI{
		key: key,
		c:   client,
	}
}

// GetReps returns local representatives for the supplied address.
func (c *civicAPI) GetReps(address string) (*LocalReps, *Address, error) {
	var u, _ = url.Parse(baseURL)
	q := u.Query()
	for _, l := range roleLevel {
		q.Add("levels", l)
	}
	q.Set("key", c.key)
	q.Set("address", url.QueryEscape(address))
	u.RawQuery = q.Encode()
	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return nil, nil, err
	}
	res, err := c.c.Do(req)
	if err != nil {
		return nil, nil, err
	}
	defer res.Body.Close()
	b, err := ioutil.ReadAll(res.Body)
	if err != nil {
		return nil, nil, err
	}
	var ar apiResponse
	err = json.Unmarshal(b, &ar)
	if err != nil {
		return nil, nil, err
	}
	return ar.toLocalReps()
}

// repCache implements a cache layer on top of a delegate rep finder.
type repCache struct {
	delegate RepFinder
	cache    *cache.Cache
}

type cacheItem struct {
	reps LocalReps
	addr Address
}

func NewRepCache(delegate RepFinder, ttl time.Duration, gc time.Duration) RepFinder {
	return &repCache{
		delegate: delegate,
		cache:    cache.New(ttl, gc),
	}
}

// reformat phone numbers that come from the google civic API
// they come back in format (555) 987-6543.
// convert to 555-987-6543.
func reformattedPhone(civicPhone string) string {
	result := phoneRegex.FindStringSubmatch(civicPhone)

	if len(result) >= 3 {
		return fmt.Sprintf("%s-%s-%s", result[1], result[2], result[3])
	}

	return civicPhone
}

// GetReps returns local representatives for the supplied address.
func (r *repCache) GetReps(address string) (*LocalReps, *Address, error) {
	data, ok := r.cache.Get(address)
	if ok {
		ci := data.(*cacheItem)
		reps := ci.reps
		addr := ci.addr
		return &reps, &addr, nil
	}
	reps, addr, err := r.delegate.GetReps(address)
	if err != nil {
		return nil, nil, err
	}
	ci := &cacheItem{reps: *reps, addr: *addr}
	r.cache.Set(address, ci, cache.DefaultExpiration)
	return reps, addr, nil
}
<|endoftext|>


<commit_before>package kyuko

import (
	"time"

	"github.com/PuerkitoBio/goquery"
	goTwitter "github.com/dghubble/go-twitter/twitter"
	"github.com/g-hyoga/kyuko/go/model"
	"github.com/g-hyoga/kyuko/go/scrape"
	"github.com/g-hyoga/kyuko/go/twitter"
)

func Exec(place int, client *goTwitter.Client) ([]model.KyukoData, error) {
	var kyukoData []model.KyukoData

	weekday := weekdayToday()

	doc, err := readHTML(place, weekday)
	if err != nil {
		return kyukoData, err
	}

	kyukoData, err = scraper(doc)
	if err != nil {
		return kyukoData, err
	}

	err = manageDB(kyukoData)
	if err != nil {
		return kyukoData, err
	}

	err = manageTwitter(kyukoData)
	if err != nil {
		return kyukoData, err
	}

	return kyukoData, nil
}

func weekdayToday() int {
	//今日の曜日
	weekday := int(time.Now().Weekday())
	//今の時間
	nowTime := time.Now().Hour()
	// 18:00超えてたら次の日の情報にする
	if nowTime >= 18 {
		weekday += 1
	}
	// 日曜なら月曜の情報にする
	if weekday == 7 {
		weekday = 1
	}
	return weekday
}

func readHTML(place, weekday int) (*goquery.Document, error) {
	//第一引数:校地
	//第二引数:曜日
	url, err := scrape.SetUrl(place, weekday)
	if err != nil {
		return nil, err
	}
	//http
	reader, err := scrape.Get(url)
	if err != nil {
		return nil, err
	}
	doc, err := goquery.NewDocumentFromReader(reader)
	if err != nil {
		return nil, err
	}
	return doc, err
}

func scraper(doc *goquery.Document) ([]model.KyukoData, error) {
	kyukoData, err := scrape.Scrape(doc)
	if err != nil {
		return nil, err
	}
	return kyukoData, nil
}

func manageDB(kyukoData []model.KyukoData) error {
	var db model.DB
	err := db.Connect()
	if err != nil {
		return err
	}
	defer db.Close()

	for _, data := range kyukoData {
		_, err = db.Insert(data)
		if err != nil {
			return err
		}

		canceledClass, err := model.KyukoToCanceled(data)
		if err != nil {
			return err
		}

		//挿入するデータが存在するのか確認
		id, err := db.ShowCanceledClassID(canceledClass)
		if err != nil {
			return err
		}

		//DBに存在するデータで今日のデータでないなら
		if isExist, _ := db.IsExistToday(id, data.Day); id != -1 && !isExist {
			canceledClass.ID = id
			_, err = db.AddCanceled(canceledClass.ID)
			if err != nil {
				return err
			}
			//reason, dayにも追加
			r := model.Reason{CanceledClassID: id, Reason: data.Reason}
			_, err = db.InsertReason(r)
			if err != nil {
				return err
			}
			d := model.Day{CanceledClassID: id, Date: data.Day}
			_, err := db.InsertDay(d)
			if err != nil {
				return err
			}
			//dbにない時
		} else if id == -1 {
			canceledClass.Canceled = 1
			_, err = db.InsertCanceledClass(canceledClass)
			if err != nil {
				return err
			}
			id, err = db.ShowCanceledClassID(canceledClass)
			if err != nil {
				return err
			}
			//reason, dayにも追加
			r := model.Reason{CanceledClassID: id, Reason: data.Reason}
			_, err = db.InsertReason(r)
			if err != nil {
				return err
			}
			d := model.Day{CanceledClassID: id, Date: data.Day}
			_, err := db.InsertDay(d)
			if err != nil {
				return err
			}
		}

	}
	return nil
}

func manageTwitter(kyukoData []model.KyukoData, client *goTwitter.Client) error {
	tws, err := twitter.CreateContent(kyukoData)
	if err != nil {
		return err
	}

	for _, tw := range tws {
		err := twitter.Update(client, tw)
		if err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>繰り返し表現の削除<commit_after>package kyuko

import (
	"time"

	"github.com/PuerkitoBio/goquery"
	goTwitter "github.com/dghubble/go-twitter/twitter"
	"github.com/g-hyoga/kyuko/go/model"
	"github.com/g-hyoga/kyuko/go/scrape"
	"github.com/g-hyoga/kyuko/go/twitter"
)

func Exec(place int, client *goTwitter.Client) ([]model.KyukoData, error) {
	var kyukoData []model.KyukoData

	weekday := weekdayToday()

	doc, err := readHTML(place, weekday)
	if err != nil {
		return kyukoData, err
	}

	kyukoData, err = scraper(doc)
	if err != nil {
		return kyukoData, err
	}

	err = manageDB(kyukoData)
	if err != nil {
		return kyukoData, err
	}

	/*
		err = manageTwitter(kyukoData, client)
		if err != nil {
			return kyukoData, err
		}
	*/

	return kyukoData, nil
}

func weekdayToday() int {
	//今日の曜日
	weekday := int(time.Now().Weekday())
	//今の時間
	nowTime := time.Now().Hour()
	// 18:00超えてたら次の日の情報にする
	if nowTime >= 18 {
		weekday += 1
	}
	// 日曜なら月曜の情報にする
	if weekday == 7 {
		weekday = 1
	}
	return weekday
}

func readHTML(place, weekday int) (*goquery.Document, error) {
	//第一引数:校地
	//第二引数:曜日
	url, err := scrape.SetUrl(place, weekday)
	if err != nil {
		return nil, err
	}
	//http
	reader, err := scrape.Get(url)
	if err != nil {
		return nil, err
	}
	doc, err := goquery.NewDocumentFromReader(reader)
	if err != nil {
		return nil, err
	}
	return doc, err
}

func scraper(doc *goquery.Document) ([]model.KyukoData, error) {
	kyukoData, err := scrape.Scrape(doc)
	if err != nil {
		return nil, err
	}
	return kyukoData, nil
}

//Reason, Dayは一緒に扱う事が多いので
func insertReasonDay(id int, reason, day string) error {
	r := model.Reason{CanceledClassID: id, Reason: reason}
	_, err := db.InsertReason(r)
	if err != nil {
		return err
	}
	d := model.Day{CanceledClassID: id, Date: day}
	_, err = db.InsertDay(d)
	if err != nil {
		return err
	}
	return nil
}

func manageDB(kyukoData []model.KyukoData) error {
	var db model.DB
	err := db.Connect()
	if err != nil {
		return err
	}
	defer db.Close()

	for _, data := range kyukoData {
		_, err = db.Insert(data)
		if err != nil {
			return err
		}

		canceledClass, err := model.KyukoToCanceled(data)
		if err != nil {
			return err
		}

		//挿入するデータが存在するのか確認
		id, err := db.ShowCanceledClassID(canceledClass)
		if err != nil {
			return err
		}

		//DBに存在するデータで今日のデータでないなら
		if isExist, _ := db.IsExistToday(id, data.Day); id != -1 && !isExist {
			canceledClass.ID = id
			_, err = db.AddCanceled(canceledClass.ID)
			if err != nil {
				return err
			}
			//reason, dayにも追加
			err = insertReasonDay(id, data.Reason, data.Day)
			if err != nil {
				return err
			}

			//dbにない時
		} else if id == -1 {
			canceledClass.Canceled = 1
			_, err = db.InsertCanceledClass(canceledClass)
			if err != nil {
				return err
			}
			id, err = db.ShowCanceledClassID(canceledClass)
			if err != nil {
				return err
			}
			//reason, dayにも追加
			err = insertReasonDay(id, data.Reason, data.Day)
			if err != nil {
				return err
			}
		}

	}
	return nil
}

func manageTwitter(kyukoData []model.KyukoData, client *goTwitter.Client) error {
	tws, err := twitter.CreateContent(kyukoData)
	if err != nil {
		return err
	}

	for _, tw := range tws {
		err := twitter.Update(client, tw)
		if err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package libct

import "net"
import "fmt"
import "syscall"
import "sync/atomic"
import prot "code.google.com/p/goprotobuf/proto"

type Session struct {
	sk *net.UnixConn
	resp_map map[uint64]chan *RpcResponse
}

type Container struct {
	s   *Session
	Rid uint64
	pid int32
}

type LibctError struct {
	Code int32
}

func (e LibctError) Error() string {
	return fmt.Sprintf("LibctError: %x", e.Code)
}

func OpenSession() (*Session, error) {
	addr, err := net.ResolveUnixAddr("unixpacket", "/var/run/libct.sock")
	if err != nil {
		return nil, err
	}
	sk, err := net.DialUnix("unixpacket", nil, addr)
	if err != nil {
		return nil, err
	}

	s := &Session{sk, map[uint64]chan *RpcResponse{}}

	// each request has a channel for response. All this channels are
	// collect in a map, where a key value is a request ID.
	go func() {
		for {
			resp, err := s.__recvRes()
			if err != nil {
				return
			}
			s.resp_map[*resp.ReqId] <- resp
			close(s.resp_map[*resp.ReqId])
			delete(s.resp_map, *resp.ReqId)
		}
	}()

	return s, nil
}

var curReqID uint64 = 100;

func getRpcReq() (*RpcRequest) {
	req := &RpcRequest{}
	id := atomic.AddUint64(&curReqID, 1)
	req.ReqId = &id
	return req
}

// Send request to the server
func (s *Session) __sendReq(req *RpcRequest, pipes *Pipes) (error) {
	pkt, err := prot.Marshal(req)
	if err != nil {
		return err
	}

	var rights []byte;
	if pipes != nil {
		rights = syscall.UnixRights(pipes.Stdin, pipes.Stdout, pipes.Stderr)
	} else {
		rights = nil
	}

	_, _, err = s.sk.WriteMsgUnix(pkt, rights, nil)
	if err != nil {
		return err
	}

	return nil
}

// Send request and return a channel with response
func (s *Session)sendReq(req *RpcRequest, pipes *Pipes) (chan *RpcResponse, error) {
	c := make(chan *RpcResponse, 1)
	s.resp_map[*req.ReqId] = c

	err := s.__sendReq(req, pipes)
	if err != nil {
		close(s.resp_map[*req.ReqId])
		delete(s.resp_map, *req.ReqId)
		return nil, err
	}

	return c, nil
}

// Send request and return response
func (s *Session) makeReqWithPipes(req *RpcRequest, pipes *Pipes) (*RpcResponse, error) {
	c, err := s.sendReq(req, pipes)
	if err != nil {
		return nil, err
	}

	resp := <-c
	return resp, nil
}

func (s *Session) makeReq(req *RpcRequest) (*RpcResponse, error) {
	return s.makeReqWithPipes(req, nil)
}

// receive response from the server
func (s *Session) __recvRes() (*RpcResponse, error) {

	pkt := make([]byte, 4096)
	size, err := s.sk.Read(pkt)
	if err != nil {
		return nil, err
	}

	res := &RpcResponse{}
	err = prot.Unmarshal(pkt[0:size], res)
	if err != nil {
		return nil, err
	}

	if !res.GetSuccess() {
		return nil, LibctError{res.GetError()}
	}

	return res, nil
}

func (s *Session) CreateCt(name string) (*Container, error) {
	req := getRpcReq()

	req.Req = ReqType_CT_CREATE.Enum()

	req.Create = &CreateReq{
		Name: prot.String(name),
	}

	res, err := s.makeReq(req)
	if err != nil {
		return nil, err
	}

	return &Container{s, res.Create.GetRid(), 0}, nil
}

func (s *Session) OpenCt(name string) (*Container, error) {
	req := getRpcReq()

	req.Req = ReqType_CT_OPEN.Enum()

	req.Create = &CreateReq{
		Name: prot.String(name),
	}

	res, err := s.makeReq(req)
	if err != nil {
		return nil, err
	}

	return &Container{s, res.Create.GetRid(), 0}, nil
}

type Pipes struct {
	Stdin, Stdout, Stderr int;
}

func (ct *Container) Run(path string, argv []string, env []string, pipes *Pipes) (error) {
	pipes_here := (pipes != nil)
	req := getRpcReq()

	req.Req = ReqType_CT_SPAWN.Enum()
	req.CtRid = &ct.Rid

	req.Execv = &ExecvReq{
		Path: &path,
		Args: argv,
		Env:  env,
		Pipes: &pipes_here,
	}

	_, err := ct.s.makeReqWithPipes(req, pipes)
	return err
}

func (ct *Container) Wait() error {
	req := getRpcReq()

	req.Req = ReqType_CT_WAIT.Enum()
	req.CtRid = &ct.Rid

	_, err := ct.s.makeReq(req)

	return err
}

func (ct *Container) Kill() error {
	req := getRpcReq()

	req.Req = ReqType_CT_KILL.Enum()
	req.CtRid = &ct.Rid

	_, err := ct.s.makeReq(req)

	return err
}

const (
	CT_ERROR int	= -1
	CT_STOPPED	= 0
	CT_RUNNING	= 1
)

func (ct *Container) State() (int, error) {
	req := getRpcReq()

	req.Req = ReqType_CT_GET_STATE.Enum()
	req.CtRid = &ct.Rid

	resp, err := ct.s.makeReq(req)
	if err != nil {
		return CT_ERROR, err
	}

	return int(resp.State.GetState()), nil
}

func (ct *Container) SetNsMask(nsmask uint64) error {
	req := getRpcReq()
	req.Req = ReqType_CT_SETNSMASK.Enum()
	req.CtRid = &ct.Rid
	req.Nsmask = &NsmaskReq{Mask : &nsmask}

	_, err := ct.s.makeReq(req)

	return err
}

func (ct *Container)SetFsRoot(root string) error {
	req := getRpcReq()
	req.Req = ReqType_FS_SETROOT.Enum()
	req.CtRid = &ct.Rid
	req.Setroot = &SetrootReq{Root : &root}

	_, err := ct.s.makeReq(req)

	return err
}

const (
	CT_FS_NONE	= 0
	CT_FS_SUBDIR	= 1
)

func (ct *Container)SetFsPrivate(ptype int32, path string) error {
	req := getRpcReq()
	req.Req = ReqType_FS_SETPRIVATE.Enum()
	req.CtRid = &ct.Rid
	req.Setpriv = &SetprivReq{Type : &ptype, Path : &path}

	_, err := ct.s.makeReq(req)

	return err
}

func (ct *Container)AddMount(src, dst string) error {
	req := getRpcReq()
	req.Req = ReqType_FS_ADD_MOUNT.Enum()
	req.CtRid = &ct.Rid
	flags := int32(0)
	req.Mnt = &MountReq{
				Dst : &dst,
				Src : &src,
				Flags : &flags,
			}

	_, err := ct.s.makeReq(req)

	return err
}

func (ct *Container)SetOption(opt int32) error {
	req := getRpcReq()
	req.Req = ReqType_CT_SET_OPTION.Enum()
	req.CtRid = &ct.Rid
	req.Setopt = &SetoptionReq{ Opt : &opt}

	_, err := ct.s.makeReq(req)

	return err
}
<commit_msg>go: reports errors from requests<commit_after>package libct

import "net"
import "fmt"
import "syscall"
import "sync/atomic"
import prot "code.google.com/p/goprotobuf/proto"

type Session struct {
	sk *net.UnixConn
	resp_map map[uint64]chan *RpcResponse
}

type Container struct {
	s   *Session
	Rid uint64
	pid int32
}

type LibctError struct {
	Code int32
}

func (e LibctError) Error() string {
	return fmt.Sprintf("LibctError: %x", e.Code)
}

func OpenSession() (*Session, error) {
	addr, err := net.ResolveUnixAddr("unixpacket", "/var/run/libct.sock")
	if err != nil {
		return nil, err
	}
	sk, err := net.DialUnix("unixpacket", nil, addr)
	if err != nil {
		return nil, err
	}

	s := &Session{sk, map[uint64]chan *RpcResponse{}}

	// each request has a channel for response. All this channels are
	// collect in a map, where a key value is a request ID.
	go func() {
		for {
			resp, err := s.__recvRes()
			if err != nil {
				return
			}
			s.resp_map[*resp.ReqId] <- resp
			close(s.resp_map[*resp.ReqId])
			delete(s.resp_map, *resp.ReqId)
		}
	}()

	return s, nil
}

var curReqID uint64 = 100;

func getRpcReq() (*RpcRequest) {
	req := &RpcRequest{}
	id := atomic.AddUint64(&curReqID, 1)
	req.ReqId = &id
	return req
}

// Send request to the server
func (s *Session) __sendReq(req *RpcRequest, pipes *Pipes) (error) {
	pkt, err := prot.Marshal(req)
	if err != nil {
		return err
	}

	var rights []byte;
	if pipes != nil {
		rights = syscall.UnixRights(pipes.Stdin, pipes.Stdout, pipes.Stderr)
	} else {
		rights = nil
	}

	_, _, err = s.sk.WriteMsgUnix(pkt, rights, nil)
	if err != nil {
		return err
	}

	return nil
}

// Send request and return a channel with response
func (s *Session)sendReq(req *RpcRequest, pipes *Pipes) (chan *RpcResponse, error) {
	c := make(chan *RpcResponse, 1)
	s.resp_map[*req.ReqId] = c

	err := s.__sendReq(req, pipes)
	if err != nil {
		close(s.resp_map[*req.ReqId])
		delete(s.resp_map, *req.ReqId)
		return nil, err
	}

	return c, nil
}

// Send request and return response
func (s *Session) makeReqWithPipes(req *RpcRequest, pipes *Pipes) (*RpcResponse, error) {
	c, err := s.sendReq(req, pipes)
	if err != nil {
		return nil, err
	}

	resp := <-c

	if resp == nil {
		return nil, LibctError{-1}
	}

	if !(*resp.Success) {
		return nil, LibctError{resp.GetError()}
	}

	return resp, nil
}

func (s *Session) makeReq(req *RpcRequest) (*RpcResponse, error) {
	return s.makeReqWithPipes(req, nil)
}

// receive response from the server
func (s *Session) __recvRes() (*RpcResponse, error) {

	pkt := make([]byte, 4096)
	size, err := s.sk.Read(pkt)
	if err != nil {
		return nil, err
	}

	res := &RpcResponse{}
	err = prot.Unmarshal(pkt[0:size], res)
	if err != nil {
		return nil, err
	}

	if !res.GetSuccess() {
		return nil, LibctError{res.GetError()}
	}

	return res, nil
}

func (s *Session) CreateCt(name string) (*Container, error) {
	req := getRpcReq()

	req.Req = ReqType_CT_CREATE.Enum()

	req.Create = &CreateReq{
		Name: prot.String(name),
	}

	res, err := s.makeReq(req)
	if err != nil {
		return nil, err
	}

	return &Container{s, res.Create.GetRid(), 0}, nil
}

func (s *Session) OpenCt(name string) (*Container, error) {
	req := getRpcReq()

	req.Req = ReqType_CT_OPEN.Enum()

	req.Create = &CreateReq{
		Name: prot.String(name),
	}

	res, err := s.makeReq(req)
	if err != nil {
		return nil, err
	}

	return &Container{s, res.Create.GetRid(), 0}, nil
}

type Pipes struct {
	Stdin, Stdout, Stderr int;
}

func (ct *Container) Run(path string, argv []string, env []string, pipes *Pipes) (error) {
	pipes_here := (pipes != nil)
	req := getRpcReq()

	req.Req = ReqType_CT_SPAWN.Enum()
	req.CtRid = &ct.Rid

	req.Execv = &ExecvReq{
		Path: &path,
		Args: argv,
		Env:  env,
		Pipes: &pipes_here,
	}

	_, err := ct.s.makeReqWithPipes(req, pipes)
	return err
}

func (ct *Container) Wait() error {
	req := getRpcReq()

	req.Req = ReqType_CT_WAIT.Enum()
	req.CtRid = &ct.Rid

	_, err := ct.s.makeReq(req)

	return err
}

func (ct *Container) Kill() error {
	req := getRpcReq()

	req.Req = ReqType_CT_KILL.Enum()
	req.CtRid = &ct.Rid

	_, err := ct.s.makeReq(req)

	return err
}

const (
	CT_ERROR int	= -1
	CT_STOPPED	= 0
	CT_RUNNING	= 1
)

func (ct *Container) State() (int, error) {
	req := getRpcReq()

	req.Req = ReqType_CT_GET_STATE.Enum()
	req.CtRid = &ct.Rid

	resp, err := ct.s.makeReq(req)
	if err != nil {
		return CT_ERROR, err
	}

	return int(resp.State.GetState()), nil
}

func (ct *Container) SetNsMask(nsmask uint64) error {
	req := getRpcReq()
	req.Req = ReqType_CT_SETNSMASK.Enum()
	req.CtRid = &ct.Rid
	req.Nsmask = &NsmaskReq{Mask : &nsmask}

	_, err := ct.s.makeReq(req)

	return err
}

func (ct *Container)SetFsRoot(root string) error {
	req := getRpcReq()
	req.Req = ReqType_FS_SETROOT.Enum()
	req.CtRid = &ct.Rid
	req.Setroot = &SetrootReq{Root : &root}

	_, err := ct.s.makeReq(req)

	return err
}

const (
	CT_FS_NONE	= 0
	CT_FS_SUBDIR	= 1
)

func (ct *Container)SetFsPrivate(ptype int32, path string) error {
	req := getRpcReq()
	req.Req = ReqType_FS_SETPRIVATE.Enum()
	req.CtRid = &ct.Rid
	req.Setpriv = &SetprivReq{Type : &ptype, Path : &path}

	_, err := ct.s.makeReq(req)

	return err
}

func (ct *Container)AddMount(src, dst string) error {
	req := getRpcReq()
	req.Req = ReqType_FS_ADD_MOUNT.Enum()
	req.CtRid = &ct.Rid
	flags := int32(0)
	req.Mnt = &MountReq{
				Dst : &dst,
				Src : &src,
				Flags : &flags,
			}

	_, err := ct.s.makeReq(req)

	return err
}

func (ct *Container)SetOption(opt int32) error {
	req := getRpcReq()
	req.Req = ReqType_CT_SET_OPTION.Enum()
	req.CtRid = &ct.Rid
	req.Setopt = &SetoptionReq{ Opt : &opt}

	_, err := ct.s.makeReq(req)

	return err
}
<|endoftext|>


<commit_before>package goalfred

import (
	"encoding/json"
	"fmt"
)

// Response is the top level domain object.
// Create a new instance by calling NewResponse()
// Add items by calling AddItem on the response object
type Response struct {
	Items []Item `json:"items"`
}

// NewResponse initializes a new instance of Response
func NewResponse() *Response {
	r := new(Response)
	r.Items = []Item{}
	return r
}

// Print should be called last to output the result of the workflow to stdout.
func (r *Response) Print() {
	bytes, _ := json.Marshal(r)
	fmt.Println(string(bytes))
}

// AlfredItem defines that a struct is convertible to an Item
type AlfredItem interface {
	Item() *Item
}

// Item stores informations about on item in the script filter
type Item struct {
	UID          string      `json:"uid,omitempty"`
	Title        string      `json:"title"`
	Subtitle     string      `json:"subtitle"`
	Arg          string      `json:"arg,omitempty"`
	Icon         *Icon       `json:"icon,omitempty"`
	Valid        bool        `json:"valid,omitempty"`
	Autocomplete string      `json:"autocomplete,omitempty"`
	Type         string      `json:"type,omitempty"`
	Mod          ModElements `json:"mods,omitempty"`
	Quicklook    string      `json:"quicklook,omitempty"`
}

// Item is an AlfredItem
func (i Item) Item() *Item {
	return &i
}

// AddItem adds a new Item to the response.
// The order in Alfred will be in the order how you add them.
func (r *Response) AddItem(item AlfredItem) *Response {
	i := item.Item()
	r.Items = append(r.Items, *i)
	return r
}

// ModElements is a collection of the different modifiers for the item
// Alt will be visible when holding the alt-key
// Cmd will be visible when holding the cmd-key
type ModElements struct {
	Alt *ModContent `json:"alt,omitempty"`
	Cmd *ModContent `json:"cmd,omitempty"`
}

// NewModElement returns an initialized ModContent to set to Alt or Cmd modifier of the Item
func NewModElement(arg string, subtitle string) *ModContent {
	m := new(ModContent)
	m.Arg = arg
	m.Subtitle = subtitle
	return m
}

// ModContent holds all informations about a modifier of an Item
type ModContent struct {
	Valid    bool   `json:"valid,omitempty"`
	Arg      string `json:"arg,omitempty"`
	Subtitle string `json:"subtitle,omitempty"`
}

// Icon holds all information about an item's icon
type Icon struct {
	Type string `json:"type,omitempty"`
	Path string `json:"path,omitempty"`
}

// NewItem creates a new Item with the given informations.
// Set modifiers and other informations after calling this function.
func NewItem(title string, subtitle string, arg string) *Item {
	item := new(Item)
	item.Title = title
	item.Subtitle = subtitle
	item.Arg = arg
	return item
}
<commit_msg>Fix bug with valid field not included in output<commit_after>package goalfred

import (
	"encoding/json"
	"fmt"
)

// Response is the top level domain object.
// Create a new instance by calling NewResponse()
// Add items by calling AddItem on the response object
type Response struct {
	Items []Item `json:"items"`
}

// NewResponse initializes a new instance of Response
func NewResponse() *Response {
	r := new(Response)
	r.Items = []Item{}
	return r
}

// Print should be called last to output the result of the workflow to stdout.
func (r *Response) Print() {
	bytes, _ := json.Marshal(r)
	fmt.Println(string(bytes))
}

// AlfredItem defines that a struct is convertible to an Item
type AlfredItem interface {
	Item() *Item
}

// Item stores informations about on item in the script filter
// A possible gotcha here is the `Valid` attribute, which is a pointer to a bool. This ensures it is whatever you set it and it gets included in the output if and only if you set it.
type Item struct {
	UID          string      `json:"uid,omitempty"`
	Title        string      `json:"title"`
	Subtitle     string      `json:"subtitle"`
	Arg          string      `json:"arg,omitempty"`
	Icon         *Icon       `json:"icon,omitempty"`
	Valid        *bool       `json:"valid,omitempty"`
	Autocomplete string      `json:"autocomplete,omitempty"`
	Type         string      `json:"type,omitempty"`
	Mod          ModElements `json:"mods,omitempty"`
	Quicklook    string      `json:"quicklook,omitempty"`
}

// Item is an AlfredItem
func (i Item) Item() *Item {
	return &i
}

// AddItem adds a new Item to the response.
// The order in Alfred will be in the order how you add them.
func (r *Response) AddItem(item AlfredItem) *Response {
	i := item.Item()
	r.Items = append(r.Items, *i)
	return r
}

// ModElements is a collection of the different modifiers for the item
// Alt will be visible when holding the alt-key
// Cmd will be visible when holding the cmd-key
type ModElements struct {
	Alt *ModContent `json:"alt,omitempty"`
	Cmd *ModContent `json:"cmd,omitempty"`
}

// NewModElement returns an initialized ModContent to set to Alt or Cmd modifier of the Item
func NewModElement(arg string, subtitle string) *ModContent {
	m := new(ModContent)
	m.Arg = arg
	m.Subtitle = subtitle
	return m
}

// ModContent holds all informations about a modifier of an Item
type ModContent struct {
	Valid    bool   `json:"valid,omitempty"`
	Arg      string `json:"arg,omitempty"`
	Subtitle string `json:"subtitle,omitempty"`
}

// Icon holds all information about an item's icon
type Icon struct {
	Type string `json:"type,omitempty"`
	Path string `json:"path,omitempty"`
}

// NewItem creates a new Item with the given informations.
// Set modifiers and other informations after calling this function.
func NewItem(title string, subtitle string, arg string) *Item {
	item := new(Item)
	item.Title = title
	item.Subtitle = subtitle
	item.Arg = arg
	return item
}
<|endoftext|>


<commit_before>package coolmaze

import "testing"

func TestFarAway(t *testing.T) {
	for _, z := range []struct {
		latlong1, latlong2 string
		expected           bool
	}{
		// Too far!
		{"37.386051,-122.083851", "0.0,0.0", true},
		{"37.386051,-122.083851", "48.8567,2.3508", true},
		// Close enough.
		{"37.386051,-122.083851", "37.386999,-122.083999", false},
		// Same "location".
		{"37.386051,-122.083851", "37.386051,-122.083851", false},
		{"48.8567,2.3508", "48.8567,2.3508", false},
		// Wrong format. Must be tolerant.
		{"", "", false},
		{"", "37.386051,-122.083851", false},
		{"37.386051,-122.083851", "", false},
		{"37.386051,-122.083851", "48.8567 2.3508", false},
		{"37.386051,-122.083851", "48.8567,,2.3508", false},
	} {
		result := farAway(z.latlong1, z.latlong2)
		if result != z.expected {
			t.Errorf("farAway(%q, %q) is %t, expected %t", z.latlong1, z.latlong2, result, z.expected)
		}
	}
}
<commit_msg>Fix geo UT.<commit_after>package coolmaze

import "testing"

func TestStrDistKm(t *testing.T) {
	for _, z := range []struct {
		latlong1, latlong2 string
		expectedParsed     bool
		expectedAbove500   bool
	}{
		// Too far!
		{"37.386051,-122.083851", "0.0,0.0", true, true},
		{"37.386051,-122.083851", "48.8567,2.3508", true, true},
		// Close enough.
		{"37.386051,-122.083851", "37.386999,-122.083999", true, false},
		// Same "location".
		{"37.386051,-122.083851", "37.386051,-122.083851", true, false},
		{"48.8567,2.3508", "48.8567,2.3508", true, false},
		// Wrong format. Must be tolerant.
		{"", "", false, false},
		{"", "37.386051,-122.083851", false, false},
		{"37.386051,-122.083851", "", false, false},
		{"37.386051,-122.083851", "48.8567 2.3508", false, false},
		{"37.386051,-122.083851", "48.8567,,2.3508", false, false},
	} {
		parsed, result := strDistKm(z.latlong1, z.latlong2)
		if parsed != z.expectedParsed {
			t.Errorf("strDistKm(%q, %q) parsed is %t, expected %t", z.latlong1, z.latlong2, parsed, z.expectedParsed)
		}
		farAway := result > 500.0
		if farAway != z.expectedAbove500 {
			t.Errorf("strDistKm(%q, %q) distance is %t, expected %t", z.latlong1, z.latlong2, farAway != z.expectedAbove500)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package backend

import (
	// "code.google.com/p/log4go" Later
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"reflect"
	"strings"
)

type (
	Package interface {
		// Returns the name of the package for sth
		// like settings is the filename and for
		// plugins is the dir name
		Name() string
		// Returns the useful data that we need
		// from this package for example for a
		// plugin will be the python files or for
		// a keymap will be the file data
		Get() interface{}
		// Returns the path that the package exists
		Path() string
	}

	Plugin struct {
		setting *Setting
		keymap  *KeyMap
		path    string
		files   []os.FileInfo
	}

	Setting struct {
		path string
		data []byte
	}

	KeyMap struct {
		path string
		data []byte
	}
)

const (
	DEFAULT_SUBLIME_SETTINGS    = "../../backend/packages/Default/Default.sublime-settings"
	DEFAULT_SUBLIME_KEYBINDINGS = "../../backend/packages/Default/Default.sublime-keymap"
	SUBLIME_USER_PACKAGES_PATH  = "../../3rdparty/bundles/"
)

var Packages = make(map[string][]Package)

func NewPlugin(path string) *Plugin {
	f, err := os.Open(path)
	if err != nil {
		return nil
	}
	defer f.Close()
	fi, err := f.Readdir(-1)
	if err != nil {
		return nil
	}
	p := &Plugin{path: path}
	files := make([]os.FileInfo, 0)
	for _, f := range fi {
		if strings.HasSuffix(f.Name(), ".py") {
			files = append(files, f)
		} else if strings.HasSuffix(f.Name(), ".sublime-settings") {
			p.setting = NewSetting(path + string(os.PathSeparator) + f.Name())
		} else if strings.HasSuffix(f.Name(), ".sublime-keymap") {
			p.keymap = NewKeyMap(path + string(os.PathSeparator) + f.Name())
		}
	}
	p.files = files
	return p
}

func (p *Plugin) Get() interface{} {
	return p.files
}

func (p *Plugin) Name() string {
	return path.Base(p.path)
}

func (p *Plugin) Path() string {
	return p.path
}

func (p *Plugin) Setting() *Setting {
	return p.setting
}

func (p *Plugin) KeyMap() *KeyMap {
	return p.keymap
}

func NewSetting(path string) *Setting {
	d, err := ioutil.ReadFile(path)
	if err != nil {
		return nil
	}
	return &Setting{path, d}
}

func (p *Setting) Get() interface{} {
	return p.data
}

func (p *Setting) Name() string {
	return path.Base(p.path)
}

func (p *Setting) Path() string {
	return p.path
}

func NewKeyMap(path string) *KeyMap {
	d, err := ioutil.ReadFile(path)
	if err != nil {
		fmt.Println(err)
		return nil
	}
	return &KeyMap{path, d}
}

func (p *KeyMap) Get() interface{} {
	return p.data
}

func (p *KeyMap) Name() string {
	return path.Base(p.path)
}

func (p *KeyMap) Path() string {
	return p.path
}

func add(key string, p Package) {
	if !reflect.ValueOf(p).IsNil() {
		Packages[key] = append(Packages[key], p)
	}
}

func Scanpath(path string) []*Plugin {
	f, err := os.Open(path)
	if err != nil {
		return nil
	}
	defer f.Close()
	dirs, err := f.Readdirnames(-1)
	if err != nil {
		return nil
	}
	plugins := make([]*Plugin, 0)
	for _, dir := range dirs {
		dir2 := path + dir
		f2, err := os.Open(dir2)
		if err != nil {
			continue
		}
		defer f2.Close()
		fi, err := f2.Readdir(-1)
		if err != nil {
			continue
		}
		for _, f := range fi {
			if strings.HasSuffix(f.Name(), ".py") {
				plugins = append(plugins, NewPlugin(dir2))
				break
			}
		}
	}
	return plugins
}

func init() {
	add("settings", NewSetting(DEFAULT_SUBLIME_SETTINGS))
	add("keymaps", NewKeyMap(DEFAULT_SUBLIME_KEYBINDINGS))

	f, err := os.Open(SUBLIME_USER_PACKAGES_PATH)
	if err != nil {
		return
	}
	defer f.Close()
	dirs, err := f.Readdirnames(-1)
	if err != nil {
		return
	}
	for _, dir := range dirs {
		dir2 := SUBLIME_USER_PACKAGES_PATH + dir
		f2, err := os.Open(dir2)
		if err != nil {
			continue
		}
		defer f2.Close()
		fi, err := f2.Readdir(-1)
		if err != nil {
			continue
		}
		for _, f := range fi {
			if strings.HasSuffix(f.Name(), ".py") {
				add("plugins", NewPlugin(dir2))
				break
			}
		}
	}
}
<commit_msg>Added some comments, plugins will now accept multiple settings and keymaps<commit_after>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package backend

import (
	// "code.google.com/p/log4go" Later
	"io/ioutil"
	"os"
	"path"
	"reflect"
	"strings"
)

type (
	Package interface {
		// Returns the name of the package for sth
		// like settings is the filename and for
		// plugins is the dir name
		Name() string
		// Returns the useful data that we need
		// from this package for example for a
		// plugin will be the python files or for
		// a keymap will be the file data
		Get() interface{}
	}

	Plugin struct {
		settings []*Setting
		keymaps  []*KeyMap
		path     string
		files    []os.FileInfo
	}

	Setting struct {
		path string
		data []byte
	}

	KeyMap struct {
		path string
		data []byte
	}
)

const (
	DEFAULT_SUBLIME_SETTINGS    = "../../backend/packages/Default/Default.sublime-settings"
	DEFAULT_SUBLIME_KEYBINDINGS = "../../backend/packages/Default/Default.sublime-keymap"
	SUBLIME_USER_PACKAGES_PATH  = "../../3rdparty/bundles/"
)

// We store all scaned packages here with appropriate
// key like plugins, settings, keymaps, etc
// plugins specific settings or keymaps won't be in here
// we should access them from the plugin itself
var Packages = make(map[string][]Package)

// Initializes a new plugin whith loading all of the
// settings, keymaps and python files inside the path
func NewPlugin(path string) *Plugin {
	f, err := os.Open(path)
	if err != nil {
		return nil
	}
	defer f.Close()
	fi, err := f.Readdir(-1)
	if err != nil {
		return nil
	}
	p := &Plugin{path: path}
	files := make([]os.FileInfo, 0)
	for _, f := range fi {
		if strings.HasSuffix(f.Name(), ".py") {
			files = append(files, f)
		} else if strings.HasSuffix(f.Name(), ".sublime-settings") {
			p.settings = append(p.settings, NewSetting(path+string(os.PathSeparator)+f.Name()))
		} else if strings.HasSuffix(f.Name(), ".sublime-keymap") {
			p.keymaps = append(p.keymaps, NewKeyMap(path+string(os.PathSeparator)+f.Name()))
		}
	}
	p.files = files
	return p
}

func (p *Plugin) Get() interface{} {
	return p.files
}

func (p *Plugin) Name() string {
	return path.Base(p.path)
}

func (p *Plugin) Settings() []*Setting {
	return p.settings
}

func (p *Plugin) KeyMaps() []*KeyMap {
	return p.keymaps
}

func NewSetting(path string) *Setting {
	return &Setting{path, nil}
}

func (p *Setting) Get() interface{} {
	if p.data == nil {
		d, err := ioutil.ReadFile(p.path)
		if err != nil {
			return nil
		}
		p.data = d
	}
	return p.data
}

func (p *Setting) Name() string {
	return path.Base(p.path)
}

func NewKeyMap(path string) *KeyMap {
	return &KeyMap{path, nil}
}

func (p *KeyMap) Get() interface{} {
	if p.data == nil {
		d, err := ioutil.ReadFile(p.path)
		if err != nil {
			return nil
		}
		p.data = d
	}
	return p.data
}

func (p *KeyMap) Name() string {
	return path.Base(p.path)
}

func add(key string, p Package) {
	if !reflect.ValueOf(p).IsNil() {
		Packages[key] = append(Packages[key], p)
	}
}

func Scanpath(path string) []*Plugin {
	f, err := os.Open(path)
	if err != nil {
		return nil
	}
	defer f.Close()
	dirs, err := f.Readdirnames(-1)
	if err != nil {
		return nil
	}
	plugins := make([]*Plugin, 0)
	for _, dir := range dirs {
		dir2 := path + dir
		f2, err := os.Open(dir2)
		if err != nil {
			continue
		}
		defer f2.Close()
		fi, err := f2.Readdir(-1)
		if err != nil {
			continue
		}
		for _, f := range fi {
			if strings.HasSuffix(f.Name(), ".py") {
				plugins = append(plugins, NewPlugin(dir2))
				break
			}
		}
	}
	return plugins
}

func init() {
	add("settings", NewSetting(DEFAULT_SUBLIME_SETTINGS))
	add("keymaps", NewKeyMap(DEFAULT_SUBLIME_KEYBINDINGS))

	f, err := os.Open(SUBLIME_USER_PACKAGES_PATH)
	if err != nil {
		return
	}
	defer f.Close()
	dirs, err := f.Readdirnames(-1)
	if err != nil {
		return
	}
	for _, dir := range dirs {
		dir2 := SUBLIME_USER_PACKAGES_PATH + dir
		f2, err := os.Open(dir2)
		if err != nil {
			continue
		}
		defer f2.Close()
		fi, err := f2.Readdir(-1)
		if err != nil {
			continue
		}
		for _, f := range fi {
			if strings.HasSuffix(f.Name(), ".py") {
				add("plugins", NewPlugin(dir2))
				break
			}
		}
	}
}
<|endoftext|>


<commit_before>package estafette

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

	"github.com/estafette/estafette-ci-api/cockroach"
	"github.com/estafette/estafette-ci-api/config"
	"github.com/gin-gonic/gin"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/rs/zerolog/log"
)

// EventHandler handles events from estafette components
type EventHandler interface {
	Handle(*gin.Context)
	UpdateBuildStatus(CiBuilderEvent) error
}

type eventHandlerImpl struct {
	config                       config.APIServerConfig
	ciBuilderClient              CiBuilderClient
	cockroachDBClient            cockroach.DBClient
	prometheusInboundEventTotals *prometheus.CounterVec
}

// NewEstafetteEventHandler returns a new estafette.EventHandler
func NewEstafetteEventHandler(config config.APIServerConfig, ciBuilderClient CiBuilderClient, cockroachDBClient cockroach.DBClient, prometheusInboundEventTotals *prometheus.CounterVec) EventHandler {
	return &eventHandlerImpl{
		config:                       config,
		ciBuilderClient:              ciBuilderClient,
		cockroachDBClient:            cockroachDBClient,
		prometheusInboundEventTotals: prometheusInboundEventTotals,
	}
}

func (h *eventHandlerImpl) Handle(c *gin.Context) {

	if c.MustGet(gin.AuthUserKey).(string) != "apiKey" {
		log.Error().Msgf("Authentication for /api/commands failed")
		c.AbortWithStatus(http.StatusUnauthorized)
	}

	eventType := c.GetHeader("X-Estafette-Event")
	log.Debug().Msgf("X-Estafette-Event is set to %v", eventType)
	h.prometheusInboundEventTotals.With(prometheus.Labels{"event": eventType, "source": "estafette"}).Inc()

	eventJobname := c.GetHeader("X-Estafette-Event-Job-Name")
	log.Debug().Msgf("X-Estafette-Event-Job-Name is set to %v", eventJobname)

	body, err := ioutil.ReadAll(c.Request.Body)
	if err != nil {
		log.Error().Err(err).Msg("Reading body from Estafette 'build finished' event failed")
		c.String(http.StatusInternalServerError, "Reading body from Estafette 'build finished' event failed")
		return
	}

	log.Debug().Msgf("Read body for /api/commands for job %v", eventJobname)

	switch eventType {
	case
		"builder:nomanifest",
		"builder:succeeded",
		"builder:failed",
		"builder:canceled":

		// unmarshal json body
		var ciBuilderEvent CiBuilderEvent
		err = json.Unmarshal(body, &ciBuilderEvent)
		if err != nil {
			log.Error().Err(err).Str("body", string(body)).Msg("Deserializing body to CiBuilderEvent failed")
			return
		}

		log.Debug().Interface("ciBuilderEvent", ciBuilderEvent).Msgf("Unmarshaled body of /api/commands event %v for job %v", eventType, eventJobname)

		err := h.UpdateBuildStatus(ciBuilderEvent)
		if err != nil {
			errorMessage := fmt.Sprintf("Failed updating build status for job %v to %v, not removing the job", eventJobname, ciBuilderEvent.BuildStatus)
			log.Error().Err(err).Interface("ciBuilderEvent", ciBuilderEvent).Msg(errorMessage)
			c.AbortWithError(http.StatusInternalServerError, fmt.Errorf(errorMessage))
		}

	case "builder:clean":

		// unmarshal json body
		var ciBuilderEvent CiBuilderEvent
		err = json.Unmarshal(body, &ciBuilderEvent)
		if err != nil {
			log.Error().Err(err).Str("body", string(body)).Msg("Deserializing body to CiBuilderEvent failed")
			return
		}

		log.Debug().Interface("ciBuilderEvent", ciBuilderEvent).Msgf("Unmarshaled body of /api/commands event %v for job %v", eventType, eventJobname)

		if ciBuilderEvent.BuildStatus != "canceled" {
			err = h.ciBuilderClient.RemoveCiBuilderJob(eventJobname)
			if err != nil {
				errorMessage := fmt.Sprintf("Failed removing job %v", eventJobname)
				log.Error().Err(err).Interface("ciBuilderEvent", ciBuilderEvent).Msg(errorMessage)
				c.AbortWithError(http.StatusInternalServerError, fmt.Errorf(errorMessage))
			}
		} else {
			log.Info().Msgf("Job %v is already removed by cancellation, no need to remove for event %v", eventJobname, eventType)
		}

	default:
		log.Warn().Str("event", eventType).Msgf("Unsupported Estafette event of type '%v'", eventType)
	}

	c.String(http.StatusOK, "Aye aye!")
}

func (h *eventHandlerImpl) UpdateBuildStatus(ciBuilderEvent CiBuilderEvent) (err error) {

	log.Debug().Interface("ciBuilderEvent", ciBuilderEvent).Msgf("UpdateBuildStatus executing...")

	if ciBuilderEvent.BuildStatus != "" && ciBuilderEvent.ReleaseID != "" {

		releaseID, err := strconv.Atoi(ciBuilderEvent.ReleaseID)
		if err != nil {
			return err
		}

		log.Debug().Msgf("Converted release id %v", releaseID)

		err = h.cockroachDBClient.UpdateReleaseStatus(ciBuilderEvent.RepoSource, ciBuilderEvent.RepoOwner, ciBuilderEvent.RepoName, releaseID, ciBuilderEvent.BuildStatus)
		if err != nil {
			return err
		}

		log.Debug().Msgf("Updated release status for job %v to %v", ciBuilderEvent.JobName, ciBuilderEvent.BuildStatus)

		return err

	} else if ciBuilderEvent.BuildStatus != "" && ciBuilderEvent.BuildID != "" {

		buildID, err := strconv.Atoi(ciBuilderEvent.BuildID)
		if err != nil {
			return err
		}

		log.Debug().Msgf("Converted build id %v", buildID)

		err = h.cockroachDBClient.UpdateBuildStatus(ciBuilderEvent.RepoSource, ciBuilderEvent.RepoOwner, ciBuilderEvent.RepoName, buildID, ciBuilderEvent.BuildStatus)
		if err != nil {
			return err
		}

		log.Debug().Msgf("Updated build status for job %v to %v", ciBuilderEvent.JobName, ciBuilderEvent.BuildStatus)

		return err
	}

	return fmt.Errorf("CiBuilderEvent has invalid state, not updating build status")
}
<commit_msg>don't block response to builder job by waiting for job to finish when cleaning up; it's a catch 22<commit_after>package estafette

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

	"github.com/estafette/estafette-ci-api/cockroach"
	"github.com/estafette/estafette-ci-api/config"
	"github.com/gin-gonic/gin"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/rs/zerolog/log"
)

// EventHandler handles events from estafette components
type EventHandler interface {
	Handle(*gin.Context)
	UpdateBuildStatus(CiBuilderEvent) error
}

type eventHandlerImpl struct {
	config                       config.APIServerConfig
	ciBuilderClient              CiBuilderClient
	cockroachDBClient            cockroach.DBClient
	prometheusInboundEventTotals *prometheus.CounterVec
}

// NewEstafetteEventHandler returns a new estafette.EventHandler
func NewEstafetteEventHandler(config config.APIServerConfig, ciBuilderClient CiBuilderClient, cockroachDBClient cockroach.DBClient, prometheusInboundEventTotals *prometheus.CounterVec) EventHandler {
	return &eventHandlerImpl{
		config:                       config,
		ciBuilderClient:              ciBuilderClient,
		cockroachDBClient:            cockroachDBClient,
		prometheusInboundEventTotals: prometheusInboundEventTotals,
	}
}

func (h *eventHandlerImpl) Handle(c *gin.Context) {

	if c.MustGet(gin.AuthUserKey).(string) != "apiKey" {
		log.Error().Msgf("Authentication for /api/commands failed")
		c.AbortWithStatus(http.StatusUnauthorized)
	}

	eventType := c.GetHeader("X-Estafette-Event")
	log.Debug().Msgf("X-Estafette-Event is set to %v", eventType)
	h.prometheusInboundEventTotals.With(prometheus.Labels{"event": eventType, "source": "estafette"}).Inc()

	eventJobname := c.GetHeader("X-Estafette-Event-Job-Name")
	log.Debug().Msgf("X-Estafette-Event-Job-Name is set to %v", eventJobname)

	body, err := ioutil.ReadAll(c.Request.Body)
	if err != nil {
		log.Error().Err(err).Msg("Reading body from Estafette 'build finished' event failed")
		c.String(http.StatusInternalServerError, "Reading body from Estafette 'build finished' event failed")
		return
	}

	log.Debug().Msgf("Read body for /api/commands for job %v", eventJobname)

	switch eventType {
	case
		"builder:nomanifest",
		"builder:succeeded",
		"builder:failed",
		"builder:canceled":

		// unmarshal json body
		var ciBuilderEvent CiBuilderEvent
		err = json.Unmarshal(body, &ciBuilderEvent)
		if err != nil {
			log.Error().Err(err).Str("body", string(body)).Msg("Deserializing body to CiBuilderEvent failed")
			return
		}

		log.Debug().Interface("ciBuilderEvent", ciBuilderEvent).Msgf("Unmarshaled body of /api/commands event %v for job %v", eventType, eventJobname)

		err := h.UpdateBuildStatus(ciBuilderEvent)
		if err != nil {
			errorMessage := fmt.Sprintf("Failed updating build status for job %v to %v, not removing the job", eventJobname, ciBuilderEvent.BuildStatus)
			log.Error().Err(err).Interface("ciBuilderEvent", ciBuilderEvent).Msg(errorMessage)
			c.AbortWithError(http.StatusInternalServerError, fmt.Errorf(errorMessage))
		}

	case "builder:clean":

		// unmarshal json body
		var ciBuilderEvent CiBuilderEvent
		err = json.Unmarshal(body, &ciBuilderEvent)
		if err != nil {
			log.Error().Err(err).Str("body", string(body)).Msg("Deserializing body to CiBuilderEvent failed")
			return
		}

		log.Debug().Interface("ciBuilderEvent", ciBuilderEvent).Msgf("Unmarshaled body of /api/commands event %v for job %v", eventType, eventJobname)

		if ciBuilderEvent.BuildStatus != "canceled" {
			go func(eventJobname string) {
				err = h.ciBuilderClient.RemoveCiBuilderJob(eventJobname)
				if err != nil {
					errorMessage := fmt.Sprintf("Failed removing job %v", eventJobname)
					log.Error().Err(err).Interface("ciBuilderEvent", ciBuilderEvent).Msg(errorMessage)
				}
			}(eventJobname)
		} else {
			log.Info().Msgf("Job %v is already removed by cancellation, no need to remove for event %v", eventJobname, eventType)
		}

	default:
		log.Warn().Str("event", eventType).Msgf("Unsupported Estafette event of type '%v'", eventType)
	}

	c.String(http.StatusOK, "Aye aye!")
}

func (h *eventHandlerImpl) UpdateBuildStatus(ciBuilderEvent CiBuilderEvent) (err error) {

	log.Debug().Interface("ciBuilderEvent", ciBuilderEvent).Msgf("UpdateBuildStatus executing...")

	if ciBuilderEvent.BuildStatus != "" && ciBuilderEvent.ReleaseID != "" {

		releaseID, err := strconv.Atoi(ciBuilderEvent.ReleaseID)
		if err != nil {
			return err
		}

		log.Debug().Msgf("Converted release id %v", releaseID)

		err = h.cockroachDBClient.UpdateReleaseStatus(ciBuilderEvent.RepoSource, ciBuilderEvent.RepoOwner, ciBuilderEvent.RepoName, releaseID, ciBuilderEvent.BuildStatus)
		if err != nil {
			return err
		}

		log.Debug().Msgf("Updated release status for job %v to %v", ciBuilderEvent.JobName, ciBuilderEvent.BuildStatus)

		return err

	} else if ciBuilderEvent.BuildStatus != "" && ciBuilderEvent.BuildID != "" {

		buildID, err := strconv.Atoi(ciBuilderEvent.BuildID)
		if err != nil {
			return err
		}

		log.Debug().Msgf("Converted build id %v", buildID)

		err = h.cockroachDBClient.UpdateBuildStatus(ciBuilderEvent.RepoSource, ciBuilderEvent.RepoOwner, ciBuilderEvent.RepoName, buildID, ciBuilderEvent.BuildStatus)
		if err != nil {
			return err
		}

		log.Debug().Msgf("Updated build status for job %v to %v", ciBuilderEvent.JobName, ciBuilderEvent.BuildStatus)

		return err
	}

	return fmt.Errorf("CiBuilderEvent has invalid state, not updating build status")
}
<|endoftext|>