<commit_before>package engine

import (
	"context"
	"fmt"
	"regexp"
	"strconv"
	"sync"
	"time"

	"github.com/juju/errors"
	"github.com/temoto/vender/helpers"
	"github.com/temoto/vender/log2"
)

type Engine struct {
	Log     *log2.Log
	lk      sync.RWMutex
	actions map[string]Doer
}

func NewEngine(log *log2.Log) *Engine {
	self := &Engine{
		Log:     log,
		actions: make(map[string]Doer, 128),
	}
	self.actions["ignore(?)"] = FuncArg{
		Name: "ignore(?)",
		F:    func(context.Context, Arg) error { return nil }}
	return self
}

func (self *Engine) Register(action string, d Doer) {
	self.lk.Lock()
	self.actions[action] = d
	self.lk.Unlock()
}

func (self *Engine) RegisterNewSeq(name string, ds ...Doer) {
	tx := NewSeq(name)
	for _, d := range ds {
		tx.Append(d)
	}
	self.Register(name, tx)
}

func (self *Engine) RegisterParse(name, scenario string) error {
	d, err := self.ParseText(name, scenario)
	if err != nil {
		err = errors.Annotatef(err, "engine.RegisterParse() name=%s scenario=%s", name, scenario)
		return err
	}
	self.Register(name, d)
	return nil
}

var reActionArg = regexp.MustCompile(`^(.+)\((\d+)\)$`)

func (self *Engine) resolve(action string) Doer {
	// self.Log.Debugf(action)
	self.lk.RLock()
	defer self.lk.RUnlock()
	d, ok := self.actions[action]
	if ok {
		return d
	}

	match := reActionArg.FindStringSubmatch(action)
	if match == nil {
		return nil
	}

	normalized := match[1] + "(?)"
	d, ok = self.actions[normalized]
	if !ok {
		self.Log.Errorf("resolve action=%s normalized=%s not found", action, normalized)
		return nil
	}
	argn, _ := strconv.Atoi(match[2])
	return ArgApply(d, Arg(argn))
}

func (self *Engine) Resolve(action string) Doer {
	d := self.resolve(action)
	if d == nil {
		self.Log.Errorf("engine.Resolve action=%s not found", action)
	}
	return d
}

func (self *Engine) List() []string {
	self.lk.RLock()
	r := make([]string, 0, len(self.actions))
	for k := range self.actions {
		r = append(r, k)
	}
	self.lk.RUnlock()
	return r
}

var reSleep = regexp.MustCompile(`sleep\((\d+m?s)\)`)

func (self *Engine) ResolveOrLazy(action string) (Doer, error) {
	self.lk.RLock()
	defer self.lk.RUnlock()
	d, ok := self.actions[action]
	if ok {
		return d, nil
	}

	if m := reSleep.FindStringSubmatch(action); len(m) == 2 {
		duration, err := time.ParseDuration(m[1])
		if err != nil {
			return nil, errors.Trace(err)
		}
		return Sleep{duration}, nil
	}

	return &Lazy{Name: action, r: self.resolve}, nil
}
func (self *Engine) MustResolveOrLazy(action string) Doer {
	d, err := self.ResolveOrLazy(action)
	if err != nil {
		return Fail{E: err}
	}
	return d
}

var reNotSpace = regexp.MustCompile(`\S+`)

func (self *Engine) ParseText(tag, text string) (Doer, error) {
	errs := make([]error, 0)
	words := reNotSpace.FindAllString(text, -1)

	tx := NewSeq(tag)
	for _, word := range words {
		d, err := self.ResolveOrLazy(word)
		if err != nil {
			return nil, errors.Annotatef(err, "scenario=%s unparsed=%s", text, word)
		}
		tx.Append(d)
	}
	return tx, helpers.FoldErrors(errs)
}

func (self *Engine) ExecList(ctx context.Context, tag string, list []string) error {
	self.Log.Debugf("engine.ExecList tag=%s list=%v", tag, list)

	errs := make([]error, 0)
	for i, text := range list {
		itemTag := fmt.Sprintf("%s:%d", tag, i)
		d, err := self.ParseText(itemTag, text)
		if err == nil {
			err = d.Do(ctx)
		}
		if err != nil {
			errs = append(errs, err)
		}
	}
	return helpers.FoldErrors(errs)
}
<commit_msg>engine.ExecList call Validate()<commit_after>package engine

import (
	"context"
	"fmt"
	"regexp"
	"strconv"
	"sync"
	"time"

	"github.com/juju/errors"
	"github.com/temoto/vender/helpers"
	"github.com/temoto/vender/log2"
)

type Engine struct {
	Log     *log2.Log
	lk      sync.RWMutex
	actions map[string]Doer
}

func NewEngine(log *log2.Log) *Engine {
	self := &Engine{
		Log:     log,
		actions: make(map[string]Doer, 128),
	}
	self.actions["ignore(?)"] = FuncArg{
		Name: "ignore(?)",
		F:    func(context.Context, Arg) error { return nil }}
	return self
}

func (self *Engine) Register(action string, d Doer) {
	self.lk.Lock()
	self.actions[action] = d
	self.lk.Unlock()
}

func (self *Engine) RegisterNewSeq(name string, ds ...Doer) {
	tx := NewSeq(name)
	for _, d := range ds {
		tx.Append(d)
	}
	self.Register(name, tx)
}

func (self *Engine) RegisterParse(name, scenario string) error {
	d, err := self.ParseText(name, scenario)
	if err != nil {
		err = errors.Annotatef(err, "engine.RegisterParse() name=%s scenario=%s", name, scenario)
		return err
	}
	self.Register(name, d)
	return nil
}

var reActionArg = regexp.MustCompile(`^(.+)\((\d+)\)$`)

func (self *Engine) resolve(action string) Doer {
	// self.Log.Debugf(action)
	self.lk.RLock()
	defer self.lk.RUnlock()
	d, ok := self.actions[action]
	if ok {
		return d
	}

	match := reActionArg.FindStringSubmatch(action)
	if match == nil {
		return nil
	}

	normalized := match[1] + "(?)"
	d, ok = self.actions[normalized]
	if !ok {
		self.Log.Errorf("resolve action=%s normalized=%s not found", action, normalized)
		return nil
	}
	argn, _ := strconv.Atoi(match[2])
	return ArgApply(d, Arg(argn))
}

func (self *Engine) Resolve(action string) Doer {
	d := self.resolve(action)
	if d == nil {
		self.Log.Errorf("engine.Resolve action=%s not found", action)
	}
	return d
}

func (self *Engine) List() []string {
	self.lk.RLock()
	r := make([]string, 0, len(self.actions))
	for k := range self.actions {
		r = append(r, k)
	}
	self.lk.RUnlock()
	return r
}

var reSleep = regexp.MustCompile(`sleep\((\d+m?s)\)`)

func (self *Engine) ResolveOrLazy(action string) (Doer, error) {
	self.lk.RLock()
	defer self.lk.RUnlock()
	d, ok := self.actions[action]
	if ok {
		return d, nil
	}

	if m := reSleep.FindStringSubmatch(action); len(m) == 2 {
		duration, err := time.ParseDuration(m[1])
		if err != nil {
			return nil, errors.Trace(err)
		}
		return Sleep{duration}, nil
	}

	return &Lazy{Name: action, r: self.resolve}, nil
}
func (self *Engine) MustResolveOrLazy(action string) Doer {
	d, err := self.ResolveOrLazy(action)
	if err != nil {
		return Fail{E: err}
	}
	return d
}

var reNotSpace = regexp.MustCompile(`\S+`)

func (self *Engine) ParseText(tag, text string) (Doer, error) {
	errs := make([]error, 0)
	words := reNotSpace.FindAllString(text, -1)

	tx := NewSeq(tag)
	for _, word := range words {
		d, err := self.ResolveOrLazy(word)
		if err != nil {
			return nil, errors.Annotatef(err, "scenario=%s unparsed=%s", text, word)
		}
		tx.Append(d)
	}
	return tx, helpers.FoldErrors(errs)
}

func (self *Engine) ExecList(ctx context.Context, tag string, list []string) error {
	self.Log.Debugf("engine.ExecList tag=%s list=%v", tag, list)

	errs := make([]error, 0)
	for i, text := range list {
		itemTag := fmt.Sprintf("%s:%d", tag, i)
		d, err := self.ParseText(itemTag, text)
		if err == nil {
			err = d.Validate()
		}
		if err == nil {
			err = d.Do(ctx)
		}
		if err != nil {
			errs = append(errs, err)
		}
	}
	return helpers.FoldErrors(errs)
}
<|endoftext|>


<commit_before>// Copyright 2015 - anova r&d bvba. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package engine

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"time"

	"github.com/gertv/charming"
	"github.com/gertv/charming/template"
)

type Request struct {
	uuid     string
	template template.TemplateConfig
	body     []byte
}

type TaskRequest struct {
	Uuid   string
	Input  string
	Style  string
	Output string
}

type Task struct {
	Uuid   string `json:"uuid"`
	Status string `json:"status"`
}

type Engine struct {
	Config charming.CharmingConfig

	database map[string]Task

	requests  chan Request
	responses chan Task
}

func (e Engine) AllTasks() []Task {
	js := make([]Task, len(e.database))
	i := 0
	for _, r := range e.database {
		js[i] = r
		i += 1
	}
	return js
}

func Setup(cc charming.CharmingConfig) *Engine {
	requests := make(chan Request)
	responses := make(chan Task)
	database := make(map[string]Task)

	engine := Engine{cc, database, requests, responses}
	go engine.handleResponses()
	go engine.handleRequests()
	return &engine
}

func (e *Engine) handleResponses() {
	log.Printf("Starting the reponse handler goroutine")
	for {
		select {
		case response, ok := <-e.responses:
			if ok {
				log.Printf("Request %s is now done", response.Uuid)
				e.database[response.Uuid] = response
			}
		}
	}
}

func (e *Engine) handleRequests() {
	log.Printf("Starting the request handler goroutine")
	for {
		select {
		case request, ok := <-e.requests:
			if ok {
				log.Printf("Let's handle %s", request.uuid)
				e.Handle(request)
			}
		}
	}
}

func (e Engine) Task(uuid string) Task {
	return e.database[uuid]
}

func (e Engine) TaskOutput(uuid string) []byte {
	pdf, err := ioutil.ReadFile(fmt.Sprintf("/home/gertv/Projects/WIP/charming/work/%s/output.pdf", uuid))
	if err != nil {
		log.Fatal(err)
	}

	return pdf
}

func (e Engine) Submit(tc template.TemplateConfig, body []byte) Task {
	uuid := strconv.FormatInt(time.Now().UnixNano(), 36)
	task := Task{uuid, "submitted"}
	e.database[uuid] = task

	e.requests <- Request{uuid, tc, body}

	return task
}

func (e Engine) Handle(r Request) {
	tempdir := filepath.Join(e.Config.WorkDir, r.uuid)
	err := os.Mkdir(tempdir, 0755)
	if err != nil {
		log.Printf("Unable to create work directory for request %s", r.uuid)
		e.responses <- Task{r.uuid, "failed"}
	}
	log.Printf("Executing request in %s", tempdir)

	input, err := os.Create(filepath.Join(tempdir, "input.html"))
	ioutil.WriteFile(input.Name(), r.body, 400)
	log.Printf("Creating temp file for input: %s", input.Name())

	output, err := os.Create(filepath.Join(tempdir, "output.pdf"))
	log.Printf("Will be writing to %s", output.Name())

	logfile, err := os.Create(filepath.Join(tempdir, "log.txt"))
	log.Printf("Prince log output will be writter to %s", logfile.Name())

	style := filepath.Join(filepath.Dir(r.template.Source), r.template.Stylesheet)
	log.Printf("Using stylesheet : %s", style)

	cmd := exec.Command("prince", "-s", style, "-i", "html", "-o", output.Name(), input.Name())

	cmd.Stdout = logfile
	cmd.Stderr = logfile
	err = cmd.Run()
	if err != nil {
		log.Fatal(err)
	}

	e.responses <- Task{r.uuid, "done"}
}
<commit_msg>Create workdir if it doesn't exist<commit_after>// Copyright 2015 - anova r&d bvba. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package engine

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"time"

	"github.com/gertv/charming"
	"github.com/gertv/charming/template"
)

type Request struct {
	uuid     string
	template template.TemplateConfig
	body     []byte
}

type TaskRequest struct {
	Uuid   string
	Input  string
	Style  string
	Output string
}

type Task struct {
	Uuid   string `json:"uuid"`
	Status string `json:"status"`
}

type Engine struct {
	Config charming.CharmingConfig

	database map[string]Task

	requests  chan Request
	responses chan Task
}

func (e Engine) AllTasks() []Task {
	js := make([]Task, len(e.database))
	i := 0
	for _, r := range e.database {
		js[i] = r
		i += 1
	}
	return js
}

func Setup(cc charming.CharmingConfig) *Engine {
	if stat, err := os.Stat(cc.WorkDir); err == nil && stat.IsDir() {
	  log.Printf("Using work directory %s", cc.WorkDir)
	} else {
		log.Printf("Creating work directory %s", cc.WorkDir)
		err = os.Mkdir(cc.WorkDir, 0755)
		if err != nil {
			log.Fatal(err)
		}
	}

	requests := make(chan Request)
	responses := make(chan Task)
	database := make(map[string]Task)

	engine := Engine{cc, database, requests, responses}
	go engine.handleResponses()
	go engine.handleRequests()
	return &engine
}

func (e *Engine) handleResponses() {
	log.Printf("Starting the reponse handler goroutine")
	for {
		select {
		case response, ok := <-e.responses:
			if ok {
				log.Printf("Request %s is now done", response.Uuid)
				e.database[response.Uuid] = response
			}
		}
	}
}

func (e *Engine) handleRequests() {
	log.Printf("Starting the request handler goroutine")
	for {
		select {
		case request, ok := <-e.requests:
			if ok {
				log.Printf("Let's handle %s", request.uuid)
				e.Handle(request)
			}
		}
	}
}

func (e Engine) Task(uuid string) Task {
	return e.database[uuid]
}

func (e Engine) TaskOutput(uuid string) []byte {
	pdf, err := ioutil.ReadFile(fmt.Sprintf("/home/gertv/Projects/WIP/charming/work/%s/output.pdf", uuid))
	if err != nil {
		log.Fatal(err)
	}

	return pdf
}

func (e Engine) Submit(tc template.TemplateConfig, body []byte) Task {
	uuid := strconv.FormatInt(time.Now().UnixNano(), 36)
	task := Task{uuid, "submitted"}
	e.database[uuid] = task

	e.requests <- Request{uuid, tc, body}

	return task
}

func (e Engine) Handle(r Request) {
	tempdir := filepath.Join(e.Config.WorkDir, r.uuid)
	err := os.Mkdir(tempdir, 0755)
	if err != nil {
		log.Printf("Unable to create work directory for request %s", r.uuid)
		e.responses <- Task{r.uuid, "failed"}
	}
	log.Printf("Executing request in %s", tempdir)

	input, err := os.Create(filepath.Join(tempdir, "input.html"))
	ioutil.WriteFile(input.Name(), r.body, 400)
	log.Printf("Creating temp file for input: %s", input.Name())

	output, err := os.Create(filepath.Join(tempdir, "output.pdf"))
	log.Printf("Will be writing to %s", output.Name())

	logfile, err := os.Create(filepath.Join(tempdir, "log.txt"))
	log.Printf("Prince log output will be writter to %s", logfile.Name())

	style := filepath.Join(filepath.Dir(r.template.Source), r.template.Stylesheet)
	log.Printf("Using stylesheet : %s", style)

	cmd := exec.Command("prince", "-s", style, "-i", "html", "-o", output.Name(), input.Name())

	cmd.Stdout = logfile
	cmd.Stderr = logfile
	err = cmd.Run()
	if err != nil {
		log.Fatal(err)
	}

	e.responses <- Task{r.uuid, "done"}
}
<|endoftext|>


<commit_before>package engine

import (
	"github.com/Cristofori/kmud/combat"
	"github.com/Cristofori/kmud/events"
	"github.com/Cristofori/kmud/model"
	"github.com/Cristofori/kmud/types"
	"github.com/Cristofori/kmud/utils"
	"time"
)

const (
	RoamingProperty = "roaming"
)

func Start() {
	for _, npc := range model.GetNpcs() {
		manageNpc(npc)
	}

	for _, spawner := range model.GetSpawners() {
		manageSpawner(spawner)
	}
}

func manageNpc(npc types.NPC) {
	eventChannel := events.Register(npc)

	go func() {
		defer events.Unregister(npc)

		for {
			event := <-eventChannel
			switch e := event.(type) {
			case events.TickEvent:
				if npc.GetRoaming() {
					room := model.GetRoom(npc.GetRoomId())
					exits := room.GetExits()
					exitToTake := utils.Random(0, len(exits)-1)
					model.MoveCharacter(npc, exits[exitToTake])
				}
			case events.CombatStartEvent:
				if npc == e.Defender {
					combat.StartFight(npc, e.Attacker)
				}
			case events.CombatStopEvent:
				if npc == e.Defender {
					combat.StopFight(npc)
				}
			case events.DeathEvent:
				if npc == e.Character {
					model.DeleteCharacter(npc)
					return
				}
			}
		}
	}()
}

func manageSpawner(spawner types.Spawner) {
	throttler := utils.NewThrottler(5 * time.Second)
	go func() {
		for {
			rooms := model.GetAreaRooms(spawner.GetAreaId())

			if len(rooms) > 0 {
				npcs := model.GetSpawnerNpcs(spawner.GetId())
				diff := spawner.GetCount() - len(npcs)

				for diff > 0 && len(rooms) > 0 {
					npc := model.CreateNpc(spawner.GetName(), rooms[0].GetId(), spawner.GetId())
					npc.SetHealth(spawner.GetHealth())
					manageNpc(npc)
					diff--
				}
			}

			throttler.Sync()
		}
	}()
}
<commit_msg>Spawn mobs in a random room in the area rather than the same one every time<commit_after>package engine

import (
	"github.com/Cristofori/kmud/combat"
	"github.com/Cristofori/kmud/events"
	"github.com/Cristofori/kmud/model"
	"github.com/Cristofori/kmud/types"
	"github.com/Cristofori/kmud/utils"
	"time"
)

const (
	RoamingProperty = "roaming"
)

func Start() {
	for _, npc := range model.GetNpcs() {
		manageNpc(npc)
	}

	for _, spawner := range model.GetSpawners() {
		manageSpawner(spawner)
	}
}

func manageNpc(npc types.NPC) {
	eventChannel := events.Register(npc)

	go func() {
		defer events.Unregister(npc)

		for {
			event := <-eventChannel
			switch e := event.(type) {
			case events.TickEvent:
				if npc.GetRoaming() {
					room := model.GetRoom(npc.GetRoomId())
					exits := room.GetExits()
					exitToTake := utils.Random(0, len(exits)-1)
					model.MoveCharacter(npc, exits[exitToTake])
				}
			case events.CombatStartEvent:
				if npc == e.Defender {
					combat.StartFight(npc, e.Attacker)
				}
			case events.CombatStopEvent:
				if npc == e.Defender {
					combat.StopFight(npc)
				}
			case events.DeathEvent:
				if npc == e.Character {
					model.DeleteCharacter(npc)
					return
				}
			}
		}
	}()
}

func manageSpawner(spawner types.Spawner) {
	throttler := utils.NewThrottler(5 * time.Second)
	go func() {
		for {
			rooms := model.GetAreaRooms(spawner.GetAreaId())

			if len(rooms) > 0 {
				npcs := model.GetSpawnerNpcs(spawner.GetId())
				diff := spawner.GetCount() - len(npcs)

				for diff > 0 && len(rooms) > 0 {
					room := rooms[utils.Random(0, len(rooms)-1)]
					npc := model.CreateNpc(spawner.GetName(), room.GetId(), spawner.GetId())
					npc.SetHealth(spawner.GetHealth())
					manageNpc(npc)
					diff--
				}
			}

			throttler.Sync()
		}
	}()
}
<|endoftext|>


<commit_before>package zk

import (
	"encoding/json"
	"errors"
	"github.com/golang/glog"
	"github.com/samuel/go-zookeeper/zk"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

var (
	ErrNotConnected   = errors.New("zk-not-initialized")
	ErrNotExist       = zk.ErrNoNode
	ErrConflict       = errors.New("error-conflict")
	ErrZkDisconnected = errors.New("error-zk-disconnected")

	event_types = map[zk.EventType]string{
		zk.EventNodeCreated:         "node-created",
		zk.EventNodeDeleted:         "node-deleted",
		zk.EventNodeDataChanged:     "node-data-changed",
		zk.EventNodeChildrenChanged: "node-children-changed",
		zk.EventSession:             "session",
		zk.EventNotWatching:         "not-watching",
	}
	states = map[zk.State]string{
		zk.StateUnknown:           "state-unknown",
		zk.StateDisconnected:      "state-disconnected",
		zk.StateAuthFailed:        "state-auth-failed",
		zk.StateConnectedReadOnly: "state-connected-readonly",
		zk.StateSaslAuthenticated: "state-sasl-authenticated",
		zk.StateExpired:           "state-expired",
		zk.StateConnected:         "state-connected",
		zk.StateHasSession:        "state-has-session",
	}
)

const (
	StateUnknown           = zk.StateUnknown
	StateDisconnected      = zk.StateDisconnected
	StateConnecting        = zk.StateConnecting
	StateAuthFailed        = zk.StateAuthFailed
	StateConnectedReadOnly = zk.StateConnectedReadOnly
	StateSaslAuthenticated = zk.StateSaslAuthenticated
	StateExpired           = zk.StateExpired
	StateConnected         = zk.StateConnected
	StateHasSession        = zk.StateHasSession
)

type ZK interface {
	Reconnect() error
	Close() error
	Events() <-chan Event
	Create(string, []byte) (*Node, error)
	CreateEphemeral(string, []byte) (*Node, error)
	Get(string) (*Node, error)
	Watch(string, func(Event)) (chan<- bool, error)
	WatchChildren(string, func(Event)) (chan<- bool, error)
	KeepWatch(string, func(Event) bool, ...func(error)) (chan<- bool, error)
	Delete(string) error
}

type zookeeper struct {
	conn           *zk.Conn
	servers        []string
	timeout        time.Duration
	events         chan Event
	ephemeralNodes map[string][]byte
	disconnected   bool
	retry          chan *kv
	retry_stop     chan int
	stop           chan int
	lock           sync.RWMutex
	running        bool
}

type kv struct {
	key   string
	value []byte
}

type Event zk.Event

func (e Event) AsMap() map[string]interface{} {
	return map[string]interface{}{
		"type":   event_types[e.Type],
		"state":  states[e.State],
		"path":   e.Path,
		"error":  e.Err,
		"server": e.Server,
	}
}

func (e Event) JSON() string {
	buff, _ := json.Marshal(e.AsMap())
	return string(buff)
}

func (this *zookeeper) on_disconnect() {
	this.disconnected = true
}

func (this *zookeeper) on_connect() {
	if this.disconnected {
		this.lock.RLock()
		defer this.lock.RUnlock()
		for k, v := range this.ephemeralNodes {
			this.retry <- &kv{key: k, value: v}
		}
		this.disconnected = false
	}
}

func (this *zookeeper) track_ephemeral(zn *Node) {
	if zn.Stats.EphemeralOwner > 0 {
		this.lock.Lock()
		defer this.lock.Unlock()
		this.ephemeralNodes[zn.Path] = zn.Value
	}
}

func (this *zookeeper) untrack_ephemeral(path string) {
	this.lock.Lock()
	defer this.lock.Unlock()
	delete(this.ephemeralNodes, path)
}

func Connect(servers []string, timeout time.Duration) (*zookeeper, error) {
	conn, events, err := zk.Connect(servers, timeout)
	if err != nil {
		return nil, err
	}
	zz := &zookeeper{
		conn:           conn,
		servers:        servers,
		timeout:        timeout,
		events:         make(chan Event),
		stop:           make(chan int),
		ephemeralNodes: map[string][]byte{},
		retry:          make(chan *kv),
		retry_stop:     make(chan int),
	}
	go func() {
		for {
			select {
			case evt := <-events:

				switch evt.State {
				case StateHasSession:
					glog.Warningln("ZK state has-session")
					zz.on_connect()
				case StateDisconnected:
					glog.Warningln("ZK state disconnected")
					zz.on_disconnect()
				}
				zz.events <- Event(evt)
			case <-zz.stop:
				return
			}
		}
	}()
	go func() {
		for {
			select {
			case r := <-zz.retry:
				glog.Infoln("Re-creating ephemeral znode: key=", r.key, ",value=", string(r.value))
				zz.CreateEphemeral(r.key, r.value)
			case <-zz.retry_stop:
				return
			}
		}
	}()

	glog.Infoln("Connected to zk:", servers)
	return zz, nil
}

func (this *zookeeper) check() error {
	if this.conn == nil {
		return ErrNotConnected
	}
	return nil
}

func (this *zookeeper) Events() <-chan Event {
	return this.events
}

func (this *zookeeper) Close() error {
	this.stop <- 1
	this.retry_stop <- 1
	this.conn.Close()
	this.conn = nil
	return nil
}

func (this *zookeeper) Reconnect() error {
	p, err := Connect(this.servers, this.timeout)
	if err != nil {
		return err
	} else {
		this = p
		return nil
	}
}

func (this *zookeeper) Get(path string) (*Node, error) {
	if err := this.check(); err != nil {
		return nil, err
	}

	exists, _, err := this.conn.Exists(path)
	if err != nil {
		return nil, err
	}
	if !exists {
		return nil, ErrNotExist
	}
	value, stats, err := this.conn.Get(path)
	if err != nil {
		return nil, err
	}
	return &Node{Path: path, Value: value, Stats: stats, zk: this}, nil
}

func (this *zookeeper) Watch(path string, f func(Event)) (chan<- bool, error) {
	if err := this.check(); err != nil {
		return nil, err
	}
	_, _, event_chan, err := this.conn.ExistsW(path)
	if err != nil {
		return nil, err
	}
	return run_watch(f, event_chan)
}

func (this *zookeeper) WatchChildren(path string, f func(Event)) (chan<- bool, error) {
	if err := this.check(); err != nil {
		return nil, err
	}

	_, _, event_chan, err := this.conn.ChildrenW(path)
	switch {

	case err == ErrNotExist:
		_, _, event_chan0, err0 := this.conn.ExistsW(path)
		if err0 != nil {
			return nil, err0
		}
		// First watch for creation
		// Use a common stop
		stop1 := make(chan bool)
		_, err1 := run_watch(func(e Event) {
			if e.Type == zk.EventNodeCreated {
				if _, _, event_chan2, err2 := this.conn.ChildrenW(path); err2 == nil {
					// then watch for children
					run_watch(f, event_chan2, stop1)
				}
			}
		}, event_chan0, stop1)
		return stop1, err1

	case err == nil:
		return run_watch(f, event_chan)

	default:
		return nil, err
	}
}

func (this *zookeeper) KeepWatch(path string, f func(Event) bool, alerts ...func(error)) (chan<- bool, error) {
	if err := this.check(); err != nil {
		return nil, err
	}
	if f == nil {
		return nil, errors.New("error-nil-watcher")
	}

	_, _, event_chan, err := this.conn.ExistsW(path)
	if err != nil {
		go func() {
			for _, a := range alerts {
				a(err)
			}
		}()
		return nil, err
	}
	stop := make(chan bool, 1)
	go func() {
		glog.Infoln("Starting watch on:", path)
		for {
			select {
			case event := <-event_chan:

				more := false
				switch event.State {
				case zk.StateDisconnected:
					go func() {
						for _, a := range alerts {
							a(ErrZkDisconnected)
						}
					}()
					more = true
				default:
					more = f(Event(event))
				}
				if more {
					// Retry loop
					for {
						glog.Infoln("Trying to set watch on", path)
						_, _, event_chan, err = this.conn.ExistsW(path)
						if err == nil {
							glog.Infoln("Continue watching", path)
							break
						} else {
							glog.Warningln("Error on watch", path, err)
							go func() {
								for _, a := range alerts {
									a(err)
								}
							}()
							// Wait a little
							time.Sleep(1 * time.Second)
						}
					}
				}

			case b := <-stop:
				if b {
					glog.Infoln("Watch terminated:", path)
					return
				}
			}
		}
	}()
	return stop, nil
}

func (this *zookeeper) Create(path string, value []byte) (*Node, error) {
	if err := this.check(); err != nil {
		return nil, err
	}
	if err := this.build_parents(path); err != nil {
		return nil, err
	}
	return this.create(path, value, false)
}

func (this *zookeeper) CreateEphemeral(path string, value []byte) (*Node, error) {
	if err := this.check(); err != nil {
		return nil, err
	}
	if err := this.build_parents(path); err != nil {
		return nil, err
	}
	return this.create(path, value, true)
}

func (this *zookeeper) Delete(path string) error {
	if err := this.check(); err != nil {
		return err
	}
	this.untrack_ephemeral(path)
	return this.conn.Delete(path, -1)
}

func get_targets(path string) []string {
	p := path
	if p[0:1] != "/" {
		p = "/" + path // Must begin with /
	}
	pp := strings.Split(p, "/")
	t := []string{}
	root := ""
	for _, x := range pp[1:] {
		z := root + "/" + x
		root = z
		t = append(t, z)
	}
	return t
}

func (this *zookeeper) build_parents(path string) error {
	dir := filepath.Dir(path)
	if dir == "." {
		return nil
	}
	for _, p := range get_targets(dir) {
		exists, _, err := this.conn.Exists(p)
		if err != nil {
			return err
		}
		if !exists {
			_, err := this.create(p, []byte{}, false)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func filter_err(err error) error {
	switch {
	case err == zk.ErrNoNode:
		return ErrNotExist
	default:
		return err
	}
}

func (this *zookeeper) create(path string, value []byte, ephemeral bool) (*Node, error) {
	key := path
	flags := int32(0)
	if ephemeral {
		flags = int32(zk.FlagEphemeral)
	}
	acl := zk.WorldACL(zk.PermAll) // TODO - PermAll permission
	p, err := this.conn.Create(key, value, flags, acl)
	if err != nil {
		return nil, err
	}
	zn := &Node{Path: p, Value: value, zk: this}
	err = zn.Get()
	if err != nil {
		return nil, err
	}

	this.track_ephemeral(zn)
	return zn, nil
}

func run_watch(f func(Event), event_chan <-chan zk.Event, optionalStop ...chan bool) (chan bool, error) {
	if f == nil {
		return nil, nil
	}

	stop := make(chan bool, 1)
	if len(optionalStop) > 0 {
		stop = optionalStop[0]
	}

	go func() {
		// Note ZK only fires once and after that we need to reschedule.
		// With this api this may mean we get a new event channel.
		// Therefore, there's no point looping in here for more than 1 event.
		select {
		case event := <-event_chan:
			f(Event(event))
		case b := <-stop:
			if b {
				glog.Infoln("Watch terminated")
				return
			}
		}
	}()
	return stop, nil
}

func append_string_slices(a, b []string) []string {
	l := len(a)
	ll := make([]string, l+len(b))
	copy(ll, a)
	for i, n := range b {
		ll[i+l] = n
	}
	return ll
}

func append_node_slices(a, b []*Node) []*Node {
	l := len(a)
	ll := make([]*Node, l+len(b))
	copy(ll, a)
	for i, n := range b {
		ll[i+l] = n
	}
	return ll
}
<commit_msg>Closing channels properly<commit_after>package zk

import (
	"encoding/json"
	"errors"
	"github.com/golang/glog"
	"github.com/samuel/go-zookeeper/zk"
	"path/filepath"
	"strings"
	"sync"
	"time"
)

var (
	ErrNotConnected   = errors.New("zk-not-initialized")
	ErrNotExist       = zk.ErrNoNode
	ErrConflict       = errors.New("error-conflict")
	ErrZkDisconnected = errors.New("error-zk-disconnected")

	event_types = map[zk.EventType]string{
		zk.EventNodeCreated:         "node-created",
		zk.EventNodeDeleted:         "node-deleted",
		zk.EventNodeDataChanged:     "node-data-changed",
		zk.EventNodeChildrenChanged: "node-children-changed",
		zk.EventSession:             "session",
		zk.EventNotWatching:         "not-watching",
	}
	states = map[zk.State]string{
		zk.StateUnknown:           "state-unknown",
		zk.StateDisconnected:      "state-disconnected",
		zk.StateAuthFailed:        "state-auth-failed",
		zk.StateConnectedReadOnly: "state-connected-readonly",
		zk.StateSaslAuthenticated: "state-sasl-authenticated",
		zk.StateExpired:           "state-expired",
		zk.StateConnected:         "state-connected",
		zk.StateHasSession:        "state-has-session",
	}
)

const (
	StateUnknown           = zk.StateUnknown
	StateDisconnected      = zk.StateDisconnected
	StateConnecting        = zk.StateConnecting
	StateAuthFailed        = zk.StateAuthFailed
	StateConnectedReadOnly = zk.StateConnectedReadOnly
	StateSaslAuthenticated = zk.StateSaslAuthenticated
	StateExpired           = zk.StateExpired
	StateConnected         = zk.StateConnected
	StateHasSession        = zk.StateHasSession
)

type ZK interface {
	Reconnect() error
	Close() error
	Events() <-chan Event
	Create(string, []byte) (*Node, error)
	CreateEphemeral(string, []byte) (*Node, error)
	Get(string) (*Node, error)
	Watch(string, func(Event)) (chan<- bool, error)
	WatchChildren(string, func(Event)) (chan<- bool, error)
	KeepWatch(string, func(Event) bool, ...func(error)) (chan<- bool, error)
	Delete(string) error
}

type zookeeper struct {
	conn           *zk.Conn
	servers        []string
	timeout        time.Duration
	events         chan Event
	ephemeralNodes map[string][]byte
	disconnected   bool
	retry          chan *kv
	retry_stop     chan int
	stop           chan int
	lock           sync.RWMutex
	running        bool
}

type kv struct {
	key   string
	value []byte
}

type Event zk.Event

func (e Event) AsMap() map[string]interface{} {
	return map[string]interface{}{
		"type":   event_types[e.Type],
		"state":  states[e.State],
		"path":   e.Path,
		"error":  e.Err,
		"server": e.Server,
	}
}

func (e Event) JSON() string {
	buff, _ := json.Marshal(e.AsMap())
	return string(buff)
}

func (this *zookeeper) on_disconnect() {
	this.disconnected = true
}

func (this *zookeeper) on_connect() {
	if this.disconnected {
		this.lock.RLock()
		defer this.lock.RUnlock()
		for k, v := range this.ephemeralNodes {
			this.retry <- &kv{key: k, value: v}
		}
		this.disconnected = false
	}
}

func (this *zookeeper) track_ephemeral(zn *Node) {
	if zn.Stats.EphemeralOwner > 0 {
		this.lock.Lock()
		defer this.lock.Unlock()
		this.ephemeralNodes[zn.Path] = zn.Value
	}
}

func (this *zookeeper) untrack_ephemeral(path string) {
	this.lock.Lock()
	defer this.lock.Unlock()
	delete(this.ephemeralNodes, path)
}

func Connect(servers []string, timeout time.Duration) (*zookeeper, error) {
	conn, events, err := zk.Connect(servers, timeout)
	if err != nil {
		return nil, err
	}
	zz := &zookeeper{
		conn:           conn,
		servers:        servers,
		timeout:        timeout,
		events:         make(chan Event),
		stop:           make(chan int),
		ephemeralNodes: map[string][]byte{},
		retry:          make(chan *kv),
		retry_stop:     make(chan int),
	}
	go func() {
		for {
			select {
			case evt := <-events:

				switch evt.State {
				case StateHasSession:
					glog.Warningln("ZK state has-session")
					zz.on_connect()
				case StateDisconnected:
					glog.Warningln("ZK state disconnected")
					zz.on_disconnect()
				}
				zz.events <- Event(evt)
			case <-zz.stop:
				return
			}
		}
	}()
	go func() {
		for {
			select {
			case r := <-zz.retry:
				if r == nil {
					glog.Infoln("Stopping retry")
					return
				}
				glog.Infoln("Re-creating ephemeral znode: key=", r.key, ",value=", string(r.value))
				zz.CreateEphemeral(r.key, r.value)
			case <-zz.retry_stop:
				return
			}
		}
	}()

	glog.Infoln("Connected to zk:", servers)
	return zz, nil
}

func (this *zookeeper) check() error {
	if this.conn == nil {
		return ErrNotConnected
	}
	return nil
}

func (this *zookeeper) Events() <-chan Event {
	return this.events
}

func (this *zookeeper) Close() error {
	glog.Infoln("Shutting down...")
	close(this.stop)
	close(this.retry_stop)
	this.conn.Close()
	this.conn = nil
	glog.Infoln("Finished shutting down")
	return nil
}

func (this *zookeeper) Reconnect() error {
	p, err := Connect(this.servers, this.timeout)
	if err != nil {
		return err
	} else {
		this = p
		return nil
	}
}

func (this *zookeeper) Get(path string) (*Node, error) {
	if err := this.check(); err != nil {
		return nil, err
	}

	exists, _, err := this.conn.Exists(path)
	if err != nil {
		return nil, err
	}
	if !exists {
		return nil, ErrNotExist
	}
	value, stats, err := this.conn.Get(path)
	if err != nil {
		return nil, err
	}
	return &Node{Path: path, Value: value, Stats: stats, zk: this}, nil
}

func (this *zookeeper) Watch(path string, f func(Event)) (chan<- bool, error) {
	if err := this.check(); err != nil {
		return nil, err
	}
	_, _, event_chan, err := this.conn.ExistsW(path)
	if err != nil {
		return nil, err
	}
	return run_watch(f, event_chan)
}

func (this *zookeeper) WatchChildren(path string, f func(Event)) (chan<- bool, error) {
	if err := this.check(); err != nil {
		return nil, err
	}

	_, _, event_chan, err := this.conn.ChildrenW(path)
	switch {

	case err == ErrNotExist:
		_, _, event_chan0, err0 := this.conn.ExistsW(path)
		if err0 != nil {
			return nil, err0
		}
		// First watch for creation
		// Use a common stop
		stop1 := make(chan bool)
		_, err1 := run_watch(func(e Event) {
			if e.Type == zk.EventNodeCreated {
				if _, _, event_chan2, err2 := this.conn.ChildrenW(path); err2 == nil {
					// then watch for children
					run_watch(f, event_chan2, stop1)
				}
			}
		}, event_chan0, stop1)
		return stop1, err1

	case err == nil:
		return run_watch(f, event_chan)

	default:
		return nil, err
	}
}

func (this *zookeeper) KeepWatch(path string, f func(Event) bool, alerts ...func(error)) (chan<- bool, error) {
	if err := this.check(); err != nil {
		return nil, err
	}
	if f == nil {
		return nil, errors.New("error-nil-watcher")
	}

	_, _, event_chan, err := this.conn.ExistsW(path)
	if err != nil {
		go func() {
			for _, a := range alerts {
				a(err)
			}
		}()
		return nil, err
	}
	stop := make(chan bool, 1)
	go func() {
		glog.Infoln("Starting watch on:", path)
		for {
			select {
			case event := <-event_chan:

				more := false
				switch event.State {
				case zk.StateDisconnected:
					go func() {
						for _, a := range alerts {
							a(ErrZkDisconnected)
						}
					}()
					more = true
				default:
					more = f(Event(event))
				}
				if more {
					// Retry loop
					for {
						glog.Infoln("Trying to set watch on", path)
						_, _, event_chan, err = this.conn.ExistsW(path)
						if err == nil {
							glog.Infoln("Continue watching", path)
							break
						} else {
							glog.Warningln("Error on watch", path, err)
							go func() {
								for _, a := range alerts {
									a(err)
								}
							}()
							// Wait a little
							time.Sleep(1 * time.Second)
						}
					}
				}

			case b := <-stop:
				if b {
					glog.Infoln("Watch terminated:", path)
					return
				}
			}
		}
	}()
	return stop, nil
}

func (this *zookeeper) Create(path string, value []byte) (*Node, error) {
	if err := this.check(); err != nil {
		return nil, err
	}
	if err := this.build_parents(path); err != nil {
		return nil, err
	}
	return this.create(path, value, false)
}

func (this *zookeeper) CreateEphemeral(path string, value []byte) (*Node, error) {
	if err := this.check(); err != nil {
		return nil, err
	}
	if err := this.build_parents(path); err != nil {
		return nil, err
	}
	return this.create(path, value, true)
}

func (this *zookeeper) Delete(path string) error {
	if err := this.check(); err != nil {
		return err
	}
	this.untrack_ephemeral(path)
	return this.conn.Delete(path, -1)
}

func get_targets(path string) []string {
	p := path
	if p[0:1] != "/" {
		p = "/" + path // Must begin with /
	}
	pp := strings.Split(p, "/")
	t := []string{}
	root := ""
	for _, x := range pp[1:] {
		z := root + "/" + x
		root = z
		t = append(t, z)
	}
	return t
}

func (this *zookeeper) build_parents(path string) error {
	dir := filepath.Dir(path)
	if dir == "." {
		return nil
	}
	for _, p := range get_targets(dir) {
		exists, _, err := this.conn.Exists(p)
		if err != nil {
			return err
		}
		if !exists {
			_, err := this.create(p, []byte{}, false)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func filter_err(err error) error {
	switch {
	case err == zk.ErrNoNode:
		return ErrNotExist
	default:
		return err
	}
}

func (this *zookeeper) create(path string, value []byte, ephemeral bool) (*Node, error) {
	key := path
	flags := int32(0)
	if ephemeral {
		flags = int32(zk.FlagEphemeral)
	}
	acl := zk.WorldACL(zk.PermAll) // TODO - PermAll permission
	p, err := this.conn.Create(key, value, flags, acl)
	if err != nil {
		return nil, err
	}
	zn := &Node{Path: p, Value: value, zk: this}
	err = zn.Get()
	if err != nil {
		return nil, err
	}

	this.track_ephemeral(zn)
	return zn, nil
}

func run_watch(f func(Event), event_chan <-chan zk.Event, optionalStop ...chan bool) (chan bool, error) {
	if f == nil {
		return nil, nil
	}

	stop := make(chan bool, 1)
	if len(optionalStop) > 0 {
		stop = optionalStop[0]
	}

	go func() {
		// Note ZK only fires once and after that we need to reschedule.
		// With this api this may mean we get a new event channel.
		// Therefore, there's no point looping in here for more than 1 event.
		select {
		case event := <-event_chan:
			f(Event(event))
		case b := <-stop:
			if b {
				glog.Infoln("Watch terminated")
				return
			}
		}
	}()
	return stop, nil
}

func append_string_slices(a, b []string) []string {
	l := len(a)
	ll := make([]string, l+len(b))
	copy(ll, a)
	for i, n := range b {
		ll[i+l] = n
	}
	return ll
}

func append_node_slices(a, b []*Node) []*Node {
	l := len(a)
	ll := make([]*Node, l+len(b))
	copy(ll, a)
	for i, n := range b {
		ll[i+l] = n
	}
	return ll
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"os/exec"
	"strings"

	"github.com/cego/zfs-cleaner/conf"
	"github.com/spf13/cobra"
)

func init() {
	rootCmd.AddCommand(plancheckCmd)
}

var plancheckCmd = &cobra.Command{
	Use:   "plancheck [config file]",
	Short: "Print mounts that have no plan and exit",
	RunE: func(cmd *cobra.Command, args []string) error {
		if len(args) != 1 {
			return fmt.Errorf("%s /path/to/config.conf", cmd.Name())
		}

		confFile, err := os.Open(args[0])
		if err != nil {
			return fmt.Errorf("failed to open %s: %s", args[0], err.Error())
		}
		defer confFile.Close()

		conf, err := readConf(confFile)
		if err != nil {
			return err
		}
		return planCheck(conf)
	},
}

func planCheck(conf *conf.Config) error {
	args := []string{"list", "-t", "filesystem", "-o", "name", "-H"}

	output, err := exec.Command(commandName, args...).Output()
	if err != nil {
		return err
	}

	m := map[string]bool{}

	for _, plan := range conf.Plans {
		for _, path := range plan.Paths {
			m[path] = true
		}
	}

	for _, store := range strings.Fields(string(output)) {
		if !m[store] {
			fmt.Printf("No plan found for path: '%s'\n", store)
		}
	}

	return nil
}
<commit_msg>Add --ignore-empty to plancheck.<commit_after>package main

import (
	"fmt"
	"os"
	"os/exec"
	"strings"

	"github.com/cego/zfs-cleaner/conf"
	"github.com/spf13/cobra"
)

func init() {
	ignoreEmpty := false
	plancheckCmd := &cobra.Command{
		Use:   "plancheck [config file]",
		Short: "Print mounts that have no plan and exit",
		RunE: func(cmd *cobra.Command, args []string) error {
			if len(args) != 1 {
				return fmt.Errorf("%s /path/to/config.conf", cmd.Name())
			}

			confFile, err := os.Open(args[0])
			if err != nil {
				return fmt.Errorf("failed to open %s: %s", args[0], err.Error())
			}
			defer confFile.Close()

			conf, err := readConf(confFile)
			if err != nil {
				return err
			}

			return planCheck(conf, ignoreEmpty)
		},
	}
	plancheckCmd.PersistentFlags().BoolVar(&ignoreEmpty, "ignore-empty", false, "Ignore file systems with no snapshots")

	rootCmd.AddCommand(plancheckCmd)
}

func hasSnapshots(path string) bool {
	argsStr := fmt.Sprintf("list -t snapshot -o name %s -H -d 1", path)
	args := strings.Fields(argsStr)

	output, err := exec.Command(commandName, args...).Output()
	if err != nil {
		return false
	}

	return len(output) > 0
}

func planCheck(conf *conf.Config, ignoreEmpty bool) error {
	args := []string{"list", "-t", "filesystem", "-o", "name", "-H"}

	output, err := exec.Command(commandName, args...).Output()
	if err != nil {
		return err
	}

	m := map[string]bool{}

	for _, plan := range conf.Plans {
		for _, path := range plan.Paths {
			m[path] = true
		}
	}

	for _, store := range strings.Fields(string(output)) {
		if !m[store] {
			if ignoreEmpty && !hasSnapshots(store) {
				continue
			}

			fmt.Printf("No plan found for path: '%s'\n", store)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package logrus

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"time"
)

// Defines the key when adding errors using WithError.
var ErrorKey = "error"

// An entry is the final or intermediate Logrus logging entry. It contains all
// the fields passed with WithField{,s}. It's finally logged when Debug, Info,
// Warn, Error, Fatal or Panic is called on it. These objects can be reused and
// passed around as much as you wish to avoid field duplication.
type Entry struct {
	Logger *Logger

	// Contains all the fields set by the user.
	Data Fields

	// Time at which the log entry was created
	Time time.Time

	// Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic
	Level Level

	// Message passed to Debug, Info, Warn, Error, Fatal or Panic
	Message string
}

func NewEntry(logger *Logger) *Entry {
	return &Entry{
		Logger: logger,
		// Default is three fields, give a little extra room
		Data: make(Fields, 5),
	}
}

// Returns a reader for the entry, which is a proxy to the formatter.
func (entry *Entry) Reader() (*bytes.Buffer, error) {
	serialized, err := entry.Logger.Formatter.Format(entry)
	return bytes.NewBuffer(serialized), err
}

// Returns the string representation from the reader and ultimately the
// formatter.
func (entry *Entry) String() (string, error) {
	reader, err := entry.Reader()
	if err != nil {
		return "", err
	}

	return reader.String(), err
}

// Add an error as single field (using the key defined in ErrorKey) to the Entry.
func (entry *Entry) WithError(err error) *Entry {
	return entry.WithField(ErrorKey, err)
}

// Add a single field to the Entry.
func (entry *Entry) WithField(key string, value interface{}) *Entry {
	return entry.WithFields(Fields{key: value})
}

// Add a map of fields to the Entry.
func (entry *Entry) WithFields(fields Fields) *Entry {
	data := Fields{}
	for k, v := range entry.Data {
		data[k] = v
	}
	for k, v := range fields {
		data[k] = v
	}
	return &Entry{Logger: entry.Logger, Data: data}
}

// This function is not declared with a pointer value because otherwise
// race conditions will occur when using multiple goroutines
func (entry Entry) log(level Level, msg string) {
	entry.Time = time.Now()
	entry.Level = level
	entry.Message = msg

	if err := entry.Logger.Hooks.Fire(level, &entry); err != nil {
		entry.Logger.mu.Lock()
		fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
		entry.Logger.mu.Unlock()
	}

	reader, err := entry.Reader()
	if err != nil {
		entry.Logger.mu.Lock()
		fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
		entry.Logger.mu.Unlock()
	}

	entry.Logger.mu.Lock()
	defer entry.Logger.mu.Unlock()

	_, err = io.Copy(entry.Logger.Out, reader)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
	}

	// To avoid Entry#log() returning a value that only would make sense for
	// panic() to use in Entry#Panic(), we avoid the allocation by checking
	// directly here.
	if level <= PanicLevel {
		panic(&entry)
	}
}

func (entry *Entry) Debug(args ...interface{}) {
	if entry.Logger.Level >= DebugLevel {
		entry.log(DebugLevel, fmt.Sprint(args...))
	}
}

func (entry *Entry) Print(args ...interface{}) {
	entry.Info(args...)
}

func (entry *Entry) Info(args ...interface{}) {
	if entry.Logger.Level >= InfoLevel {
		entry.log(InfoLevel, fmt.Sprint(args...))
	}
}

func (entry *Entry) Warn(args ...interface{}) {
	if entry.Logger.Level >= WarnLevel {
		entry.log(WarnLevel, fmt.Sprint(args...))
	}
}

func (entry *Entry) Warning(args ...interface{}) {
	entry.Warn(args...)
}

func (entry *Entry) Error(args ...interface{}) {
	if entry.Logger.Level >= ErrorLevel {
		entry.log(ErrorLevel, fmt.Sprint(args...))
	}
}

func (entry *Entry) Fatal(args ...interface{}) {
	if entry.Logger.Level >= FatalLevel {
		entry.log(FatalLevel, fmt.Sprint(args...))
	}
	os.Exit(1)
}

func (entry *Entry) Panic(args ...interface{}) {
	if entry.Logger.Level >= PanicLevel {
		entry.log(PanicLevel, fmt.Sprint(args...))
	}
	panic(fmt.Sprint(args...))
}

// Entry Printf family functions

func (entry *Entry) Debugf(format string, args ...interface{}) {
	if entry.Logger.Level >= DebugLevel {
		entry.Debug(fmt.Sprintf(format, args...))
	}
}

func (entry *Entry) Infof(format string, args ...interface{}) {
	if entry.Logger.Level >= InfoLevel {
		entry.Info(fmt.Sprintf(format, args...))
	}
}

func (entry *Entry) Printf(format string, args ...interface{}) {
	entry.Infof(format, args...)
}

func (entry *Entry) Warnf(format string, args ...interface{}) {
	if entry.Logger.Level >= WarnLevel {
		entry.Warn(fmt.Sprintf(format, args...))
	}
}

func (entry *Entry) Warningf(format string, args ...interface{}) {
	entry.Warnf(format, args...)
}

func (entry *Entry) Errorf(format string, args ...interface{}) {
	if entry.Logger.Level >= ErrorLevel {
		entry.Error(fmt.Sprintf(format, args...))
	}
}

func (entry *Entry) Fatalf(format string, args ...interface{}) {
	if entry.Logger.Level >= FatalLevel {
		entry.Fatal(fmt.Sprintf(format, args...))
	}
	os.Exit(1)
}

func (entry *Entry) Panicf(format string, args ...interface{}) {
	if entry.Logger.Level >= PanicLevel {
		entry.Panic(fmt.Sprintf(format, args...))
	}
}

// Entry Println family functions

func (entry *Entry) Debugln(args ...interface{}) {
	if entry.Logger.Level >= DebugLevel {
		entry.Debug(entry.sprintlnn(args...))
	}
}

func (entry *Entry) Infoln(args ...interface{}) {
	if entry.Logger.Level >= InfoLevel {
		entry.Info(entry.sprintlnn(args...))
	}
}

func (entry *Entry) Println(args ...interface{}) {
	entry.Infoln(args...)
}

func (entry *Entry) Warnln(args ...interface{}) {
	if entry.Logger.Level >= WarnLevel {
		entry.Warn(entry.sprintlnn(args...))
	}
}

func (entry *Entry) Warningln(args ...interface{}) {
	entry.Warnln(args...)
}

func (entry *Entry) Errorln(args ...interface{}) {
	if entry.Logger.Level >= ErrorLevel {
		entry.Error(entry.sprintlnn(args...))
	}
}

func (entry *Entry) Fatalln(args ...interface{}) {
	if entry.Logger.Level >= FatalLevel {
		entry.Fatal(entry.sprintlnn(args...))
	}
	os.Exit(1)
}

func (entry *Entry) Panicln(args ...interface{}) {
	if entry.Logger.Level >= PanicLevel {
		entry.Panic(entry.sprintlnn(args...))
	}
}

// Sprintlnn => Sprint no newline. This is to get the behavior of how
// fmt.Sprintln where spaces are always added between operands, regardless of
// their type. Instead of vendoring the Sprintln implementation to spare a
// string allocation, we do the simplest thing.
func (entry *Entry) sprintlnn(args ...interface{}) string {
	msg := fmt.Sprintln(args...)
	return msg[:len(msg)-1]
}
<commit_msg>Avoid re-allocations<commit_after>package logrus

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"time"
)

// Defines the key when adding errors using WithError.
var ErrorKey = "error"

// An entry is the final or intermediate Logrus logging entry. It contains all
// the fields passed with WithField{,s}. It's finally logged when Debug, Info,
// Warn, Error, Fatal or Panic is called on it. These objects can be reused and
// passed around as much as you wish to avoid field duplication.
type Entry struct {
	Logger *Logger

	// Contains all the fields set by the user.
	Data Fields

	// Time at which the log entry was created
	Time time.Time

	// Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic
	Level Level

	// Message passed to Debug, Info, Warn, Error, Fatal or Panic
	Message string
}

func NewEntry(logger *Logger) *Entry {
	return &Entry{
		Logger: logger,
		// Default is three fields, give a little extra room
		Data: make(Fields, 5),
	}
}

// Returns a reader for the entry, which is a proxy to the formatter.
func (entry *Entry) Reader() (*bytes.Buffer, error) {
	serialized, err := entry.Logger.Formatter.Format(entry)
	return bytes.NewBuffer(serialized), err
}

// Returns the string representation from the reader and ultimately the
// formatter.
func (entry *Entry) String() (string, error) {
	reader, err := entry.Reader()
	if err != nil {
		return "", err
	}

	return reader.String(), err
}

// Add an error as single field (using the key defined in ErrorKey) to the Entry.
func (entry *Entry) WithError(err error) *Entry {
	return entry.WithField(ErrorKey, err)
}

// Add a single field to the Entry.
func (entry *Entry) WithField(key string, value interface{}) *Entry {
	return entry.WithFields(Fields{key: value})
}

// Add a map of fields to the Entry.
func (entry *Entry) WithFields(fields Fields) *Entry {
	data := make(Fields, len(entry.Data)+len(fields))
	for k, v := range entry.Data {
		data[k] = v
	}
	for k, v := range fields {
		data[k] = v
	}
	return &Entry{Logger: entry.Logger, Data: data}
}

// This function is not declared with a pointer value because otherwise
// race conditions will occur when using multiple goroutines
func (entry Entry) log(level Level, msg string) {
	entry.Time = time.Now()
	entry.Level = level
	entry.Message = msg

	if err := entry.Logger.Hooks.Fire(level, &entry); err != nil {
		entry.Logger.mu.Lock()
		fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
		entry.Logger.mu.Unlock()
	}

	reader, err := entry.Reader()
	if err != nil {
		entry.Logger.mu.Lock()
		fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
		entry.Logger.mu.Unlock()
	}

	entry.Logger.mu.Lock()
	defer entry.Logger.mu.Unlock()

	_, err = io.Copy(entry.Logger.Out, reader)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
	}

	// To avoid Entry#log() returning a value that only would make sense for
	// panic() to use in Entry#Panic(), we avoid the allocation by checking
	// directly here.
	if level <= PanicLevel {
		panic(&entry)
	}
}

func (entry *Entry) Debug(args ...interface{}) {
	if entry.Logger.Level >= DebugLevel {
		entry.log(DebugLevel, fmt.Sprint(args...))
	}
}

func (entry *Entry) Print(args ...interface{}) {
	entry.Info(args...)
}

func (entry *Entry) Info(args ...interface{}) {
	if entry.Logger.Level >= InfoLevel {
		entry.log(InfoLevel, fmt.Sprint(args...))
	}
}

func (entry *Entry) Warn(args ...interface{}) {
	if entry.Logger.Level >= WarnLevel {
		entry.log(WarnLevel, fmt.Sprint(args...))
	}
}

func (entry *Entry) Warning(args ...interface{}) {
	entry.Warn(args...)
}

func (entry *Entry) Error(args ...interface{}) {
	if entry.Logger.Level >= ErrorLevel {
		entry.log(ErrorLevel, fmt.Sprint(args...))
	}
}

func (entry *Entry) Fatal(args ...interface{}) {
	if entry.Logger.Level >= FatalLevel {
		entry.log(FatalLevel, fmt.Sprint(args...))
	}
	os.Exit(1)
}

func (entry *Entry) Panic(args ...interface{}) {
	if entry.Logger.Level >= PanicLevel {
		entry.log(PanicLevel, fmt.Sprint(args...))
	}
	panic(fmt.Sprint(args...))
}

// Entry Printf family functions

func (entry *Entry) Debugf(format string, args ...interface{}) {
	if entry.Logger.Level >= DebugLevel {
		entry.Debug(fmt.Sprintf(format, args...))
	}
}

func (entry *Entry) Infof(format string, args ...interface{}) {
	if entry.Logger.Level >= InfoLevel {
		entry.Info(fmt.Sprintf(format, args...))
	}
}

func (entry *Entry) Printf(format string, args ...interface{}) {
	entry.Infof(format, args...)
}

func (entry *Entry) Warnf(format string, args ...interface{}) {
	if entry.Logger.Level >= WarnLevel {
		entry.Warn(fmt.Sprintf(format, args...))
	}
}

func (entry *Entry) Warningf(format string, args ...interface{}) {
	entry.Warnf(format, args...)
}

func (entry *Entry) Errorf(format string, args ...interface{}) {
	if entry.Logger.Level >= ErrorLevel {
		entry.Error(fmt.Sprintf(format, args...))
	}
}

func (entry *Entry) Fatalf(format string, args ...interface{}) {
	if entry.Logger.Level >= FatalLevel {
		entry.Fatal(fmt.Sprintf(format, args...))
	}
	os.Exit(1)
}

func (entry *Entry) Panicf(format string, args ...interface{}) {
	if entry.Logger.Level >= PanicLevel {
		entry.Panic(fmt.Sprintf(format, args...))
	}
}

// Entry Println family functions

func (entry *Entry) Debugln(args ...interface{}) {
	if entry.Logger.Level >= DebugLevel {
		entry.Debug(entry.sprintlnn(args...))
	}
}

func (entry *Entry) Infoln(args ...interface{}) {
	if entry.Logger.Level >= InfoLevel {
		entry.Info(entry.sprintlnn(args...))
	}
}

func (entry *Entry) Println(args ...interface{}) {
	entry.Infoln(args...)
}

func (entry *Entry) Warnln(args ...interface{}) {
	if entry.Logger.Level >= WarnLevel {
		entry.Warn(entry.sprintlnn(args...))
	}
}

func (entry *Entry) Warningln(args ...interface{}) {
	entry.Warnln(args...)
}

func (entry *Entry) Errorln(args ...interface{}) {
	if entry.Logger.Level >= ErrorLevel {
		entry.Error(entry.sprintlnn(args...))
	}
}

func (entry *Entry) Fatalln(args ...interface{}) {
	if entry.Logger.Level >= FatalLevel {
		entry.Fatal(entry.sprintlnn(args...))
	}
	os.Exit(1)
}

func (entry *Entry) Panicln(args ...interface{}) {
	if entry.Logger.Level >= PanicLevel {
		entry.Panic(entry.sprintlnn(args...))
	}
}

// Sprintlnn => Sprint no newline. This is to get the behavior of how
// fmt.Sprintln where spaces are always added between operands, regardless of
// their type. Instead of vendoring the Sprintln implementation to spare a
// string allocation, we do the simplest thing.
func (entry *Entry) sprintlnn(args ...interface{}) string {
	msg := fmt.Sprintln(args...)
	return msg[:len(msg)-1]
}
<|endoftext|>


<commit_before>package mailgun

import (
	"encoding/json"
	"fmt"
	"net"
	"strings"
	"time"
)

type EventType uint8

const (
	EventUnknown EventType = iota
	EventAccepted
	EventRejected
	EventDelivered
	EventFailed
	EventOpened
	EventClicked
	EventUnsubscribed
	EventComplained
	EventStored
	EventDropped
)

var eventTypes = []string{
	"unknown",
	"accepted",
	"rejected",
	"delivered",
	"failed",
	"opened",
	"clicked",
	"unsubscribed",
	"complained",
	"stored",
	"dropped",
}

func (et EventType) String() string {
	return eventTypes[et]
}

// MarshalText satisfies TextMarshaler
func (et EventType) MarshalText() ([]byte, error) {
	return []byte(et.String()), nil
}

// UnmarshalText satisfies TextUnmarshaler
func (et *EventType) UnmarshalText(text []byte) error {
	enum := string(text)
	for i := 0; i < len(eventTypes); i++ {
		if enum == eventTypes[i] {
			*et = EventType(i)
			return nil
		}
	}
	return fmt.Errorf("unknown event type '%s'", enum)
}

type TimestampNano time.Time

// MarshalText satisfies JSONMarshaler
func (tn TimestampNano) MarshalJSON() ([]byte, error) {
	t := time.Time(tn)
	v := float64(t.Unix()) + float64(t.Nanosecond())/float64(time.Nanosecond)
	return json.Marshal(v)
}

// UnmarshalText satisfies JSONUnmarshaler
func (tn *TimestampNano) UnmarshalJSON(data []byte) error {
	var v float64
	err := json.Unmarshal(data, &v)
	if err == nil {
		*tn = TimestampNano(time.Unix(0, int64(v*float64(time.Second))))
	}
	return err
}

type IP net.IP

// MarshalText satisfies TextMarshaler
func (i IP) MarshalText() ([]byte, error) {
	return []byte(net.IP(i).String()), nil
}

// UnmarshalText satisfies TextUnmarshaler
func (i *IP) UnmarshalText(text []byte) error {
	s := strings.Trim(string(text), "\"")
	v := net.ParseIP(s)
	if v != nil {
		*i = IP(v)
	}
	return nil
}

type Method uint8

const (
	MethodUnknown Method = iota
	MethodSMTP
	MethodHTTP
)

var methods = []string{
	"unknown",
	"smtp",
	"http",
}

func (m Method) String() string {
	return methods[m]
}

// MarshalText satisfies TextMarshaler
func (m Method) MarshalText() ([]byte, error) {
	return []byte(m.String()), nil
}

// UnmarshalText satisfies TextUnmarshaler
func (m *Method) UnmarshalText(text []byte) error {
	enum := string(text)
	for i := 0; i < len(methods); i++ {
		if enum == methods[i] {
			*m = Method(i)
			return nil
		}
	}
	return fmt.Errorf("unknown event method '%s'", enum)
}

type EventSeverity uint8

const (
	SeverityUnknown EventSeverity = iota
	SeverityTemporary
	SeverityPermanent
	SeverityInternal
)

var severities = []string{
	"unknown",
	"temporary",
	"permanent",
	"internal",
}

func (es EventSeverity) String() string {
	return severities[es]
}

// MarshalText satisfies TextMarshaler
func (es EventSeverity) MarshalText() ([]byte, error) {
	return []byte(es.String()), nil
}

// UnmarshalText satisfies TextUnmarshaler
func (es *EventSeverity) UnmarshalText(text []byte) error {
	enum := string(text)
	for i := 0; i < len(severities); i++ {
		if enum == severities[i] {
			*es = EventSeverity(i)
			return nil
		}
	}
	return fmt.Errorf("unknown event severity '%s'", enum)
}

type EventReason uint8

const (
	ReasonUnknown EventReason = iota
	ReasonGeneric
	ReasonBounce
	ReasonESPBlock
	ReasonSuppressBounce
	ReasonSuppressComplaint
	ReasonSuppressUnsubscribe
	ReasonOld
	ReasonHardFail
)

var eventReasons = []string{
	"unknown",
	"generic",
	"bounce",
	"espblock",
	"suppress-bounce",
	"suppress-complaint",
	"suppress-unsubscribe",
	"old",
	"hardfail",
}

func (er EventReason) String() string {
	return eventReasons[er]
}

// MarshalText satisfies TextMarshaler
func (er EventReason) MarshalText() ([]byte, error) {
	return []byte(er.String()), nil
}

// UnmarshalText satisfies TextUnmarshaler
func (er *EventReason) UnmarshalText(text []byte) error {
	enum := string(text)
	for i := 0; i < len(eventReasons); i++ {
		if enum == eventReasons[i] {
			*er = EventReason(i)
			return nil
		}
	}
	return fmt.Errorf("unknown event reason '%s'", enum)
}

type ClientType uint

const (
	ClientUnknown ClientType = iota
	ClientMobileBrowser
	ClientBrowser
	ClientEmail
	ClientLibrary
)

var clientTypes = []string{
	"unknown",
	"mobile browser",
	"browser",
	"email client",
	"library",
}

func (ct ClientType) String() string {
	return clientTypes[ct]
}

// MarshalText satisfies TextMarshaler
func (ct ClientType) MarshalText() ([]byte, error) {
	return []byte(ct.String()), nil
}

// UnmarshalText satisfies TextUnmarshaler
func (ct *ClientType) UnmarshalText(text []byte) error {
	enum := string(text)
	for i := 0; i < len(clientTypes); i++ {
		if enum == clientTypes[i] {
			*ct = ClientType(i)
			return nil
		}
	}
	return fmt.Errorf("unknown client type '%s'", enum)
}

type DeviceType uint

const (
	DeviceUnknown DeviceType = iota
	DeviceMobileBrowser
	DeviceBrowser
	DeviceEmail
	DeviceOther
)

var deviceTypes = []string{
	"unknown",
	"desktop",
	"mobile",
	"tablet",
	"other",
}

func (ct DeviceType) String() string {
	return deviceTypes[ct]
}

// MarshalText satisfies TextMarshaler
func (ct DeviceType) MarshalText() ([]byte, error) {
	return []byte(ct.String()), nil
}

// UnmarshalText satisfies TextUnmarshaler
func (ct *DeviceType) UnmarshalText(text []byte) error {
	enum := string(text)
	for i := 0; i < len(deviceTypes); i++ {
		if enum == deviceTypes[i] {
			*ct = DeviceType(i)
			return nil
		}
	}
	return fmt.Errorf("unknown device type '%s'", enum)
}

type TransportMethod uint

const (
	TransportUnknown TransportMethod = iota
	TransportHTTP
	TransportSMTP
)

var transportMethods = []string{
	"unknown",
	"http",
	"smtp",
}

func (tm TransportMethod) String() string {
	return transportMethods[tm]
}

// MarshalText satisfies TextMarshaler
func (tm TransportMethod) MarshalText() ([]byte, error) {
	return []byte(tm.String()), nil
}

// UnmarshalText satisfies TextUnmarshaler
func (tm *TransportMethod) UnmarshalText(text []byte) error {
	enum := string(text)
	for i := 0; i < len(transportMethods); i++ {
		if enum == transportMethods[i] {
			*tm = TransportMethod(i)
			return nil
		}
	}
	return fmt.Errorf("unknown transport method '%s'", enum)
}
<commit_msg>Add client type: robot<commit_after>package mailgun

import (
	"encoding/json"
	"fmt"
	"net"
	"strings"
	"time"
)

type EventType uint8

const (
	EventUnknown EventType = iota
	EventAccepted
	EventRejected
	EventDelivered
	EventFailed
	EventOpened
	EventClicked
	EventUnsubscribed
	EventComplained
	EventStored
	EventDropped
)

var eventTypes = []string{
	"unknown",
	"accepted",
	"rejected",
	"delivered",
	"failed",
	"opened",
	"clicked",
	"unsubscribed",
	"complained",
	"stored",
	"dropped",
}

func (et EventType) String() string {
	return eventTypes[et]
}

// MarshalText satisfies TextMarshaler
func (et EventType) MarshalText() ([]byte, error) {
	return []byte(et.String()), nil
}

// UnmarshalText satisfies TextUnmarshaler
func (et *EventType) UnmarshalText(text []byte) error {
	enum := string(text)
	for i := 0; i < len(eventTypes); i++ {
		if enum == eventTypes[i] {
			*et = EventType(i)
			return nil
		}
	}
	return fmt.Errorf("unknown event type '%s'", enum)
}

type TimestampNano time.Time

// MarshalText satisfies JSONMarshaler
func (tn TimestampNano) MarshalJSON() ([]byte, error) {
	t := time.Time(tn)
	v := float64(t.Unix()) + float64(t.Nanosecond())/float64(time.Nanosecond)
	return json.Marshal(v)
}

// UnmarshalText satisfies JSONUnmarshaler
func (tn *TimestampNano) UnmarshalJSON(data []byte) error {
	var v float64
	err := json.Unmarshal(data, &v)
	if err == nil {
		*tn = TimestampNano(time.Unix(0, int64(v*float64(time.Second))))
	}
	return err
}

type IP net.IP

// MarshalText satisfies TextMarshaler
func (i IP) MarshalText() ([]byte, error) {
	return []byte(net.IP(i).String()), nil
}

// UnmarshalText satisfies TextUnmarshaler
func (i *IP) UnmarshalText(text []byte) error {
	s := strings.Trim(string(text), "\"")
	v := net.ParseIP(s)
	if v != nil {
		*i = IP(v)
	}
	return nil
}

type Method uint8

const (
	MethodUnknown Method = iota
	MethodSMTP
	MethodHTTP
)

var methods = []string{
	"unknown",
	"smtp",
	"http",
}

func (m Method) String() string {
	return methods[m]
}

// MarshalText satisfies TextMarshaler
func (m Method) MarshalText() ([]byte, error) {
	return []byte(m.String()), nil
}

// UnmarshalText satisfies TextUnmarshaler
func (m *Method) UnmarshalText(text []byte) error {
	enum := string(text)
	for i := 0; i < len(methods); i++ {
		if enum == methods[i] {
			*m = Method(i)
			return nil
		}
	}
	return fmt.Errorf("unknown event method '%s'", enum)
}

type EventSeverity uint8

const (
	SeverityUnknown EventSeverity = iota
	SeverityTemporary
	SeverityPermanent
	SeverityInternal
)

var severities = []string{
	"unknown",
	"temporary",
	"permanent",
	"internal",
}

func (es EventSeverity) String() string {
	return severities[es]
}

// MarshalText satisfies TextMarshaler
func (es EventSeverity) MarshalText() ([]byte, error) {
	return []byte(es.String()), nil
}

// UnmarshalText satisfies TextUnmarshaler
func (es *EventSeverity) UnmarshalText(text []byte) error {
	enum := string(text)
	for i := 0; i < len(severities); i++ {
		if enum == severities[i] {
			*es = EventSeverity(i)
			return nil
		}
	}
	return fmt.Errorf("unknown event severity '%s'", enum)
}

type EventReason uint8

const (
	ReasonUnknown EventReason = iota
	ReasonGeneric
	ReasonBounce
	ReasonESPBlock
	ReasonSuppressBounce
	ReasonSuppressComplaint
	ReasonSuppressUnsubscribe
	ReasonOld
	ReasonHardFail
)

var eventReasons = []string{
	"unknown",
	"generic",
	"bounce",
	"espblock",
	"suppress-bounce",
	"suppress-complaint",
	"suppress-unsubscribe",
	"old",
	"hardfail",
}

func (er EventReason) String() string {
	return eventReasons[er]
}

// MarshalText satisfies TextMarshaler
func (er EventReason) MarshalText() ([]byte, error) {
	return []byte(er.String()), nil
}

// UnmarshalText satisfies TextUnmarshaler
func (er *EventReason) UnmarshalText(text []byte) error {
	enum := string(text)
	for i := 0; i < len(eventReasons); i++ {
		if enum == eventReasons[i] {
			*er = EventReason(i)
			return nil
		}
	}
	return fmt.Errorf("unknown event reason '%s'", enum)
}

type ClientType uint

const (
	ClientUnknown ClientType = iota
	ClientMobileBrowser
	ClientBrowser
	ClientEmail
	ClientLibrary
	ClientRobot
)

var clientTypes = []string{
	"unknown",
	"mobile browser",
	"browser",
	"email client",
	"library",
	"robot",
}

func (ct ClientType) String() string {
	return clientTypes[ct]
}

// MarshalText satisfies TextMarshaler
func (ct ClientType) MarshalText() ([]byte, error) {
	return []byte(ct.String()), nil
}

// UnmarshalText satisfies TextUnmarshaler
func (ct *ClientType) UnmarshalText(text []byte) error {
	enum := string(text)
	for i := 0; i < len(clientTypes); i++ {
		if enum == clientTypes[i] {
			*ct = ClientType(i)
			return nil
		}
	}
	return fmt.Errorf("unknown client type '%s'", enum)
}

type DeviceType uint

const (
	DeviceUnknown DeviceType = iota
	DeviceMobileBrowser
	DeviceBrowser
	DeviceEmail
	DeviceOther
)

var deviceTypes = []string{
	"unknown",
	"desktop",
	"mobile",
	"tablet",
	"other",
}

func (ct DeviceType) String() string {
	return deviceTypes[ct]
}

// MarshalText satisfies TextMarshaler
func (ct DeviceType) MarshalText() ([]byte, error) {
	return []byte(ct.String()), nil
}

// UnmarshalText satisfies TextUnmarshaler
func (ct *DeviceType) UnmarshalText(text []byte) error {
	enum := string(text)
	for i := 0; i < len(deviceTypes); i++ {
		if enum == deviceTypes[i] {
			*ct = DeviceType(i)
			return nil
		}
	}
	return fmt.Errorf("unknown device type '%s'", enum)
}

type TransportMethod uint

const (
	TransportUnknown TransportMethod = iota
	TransportHTTP
	TransportSMTP
)

var transportMethods = []string{
	"unknown",
	"http",
	"smtp",
}

func (tm TransportMethod) String() string {
	return transportMethods[tm]
}

// MarshalText satisfies TextMarshaler
func (tm TransportMethod) MarshalText() ([]byte, error) {
	return []byte(tm.String()), nil
}

// UnmarshalText satisfies TextUnmarshaler
func (tm *TransportMethod) UnmarshalText(text []byte) error {
	enum := string(text)
	for i := 0; i < len(transportMethods); i++ {
		if enum == transportMethods[i] {
			*tm = TransportMethod(i)
			return nil
		}
	}
	return fmt.Errorf("unknown transport method '%s'", enum)
}
<|endoftext|>


<commit_before>package gitlab

import (
	"fmt"
	"time"
)

// EpicsService handles communication with the epic related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html
type EpicsService struct {
	client *Client
}

// EpicAuthor represents a author of the epic.
type EpicAuthor struct {
	ID        int    `json:"id"`
	State     string `json:"state"`
	WebURL    string `json:"web_url"`
	Name      string `json:"name"`
	AvatarURL string `json:"avatar_url"`
	Username  string `json:"username"`
}

// Epic represents a GitLab epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html
type Epic struct {
	ID                      int         `json:"id"`
	IID                     int         `json:"iid"`
	GroupID                 int         `json:"group_id"`
	Author                  *EpicAuthor `json:"author"`
        ParentID		int	    `json:"parent_id"`
	Description             string      `json:"description"`
	State                   string      `json:"state"`
	WebURL                  string      `json:"web_url"`
	Upvotes                 int         `json:"upvotes"`
	Downvotes               int         `json:"downvotes"`
	Labels                  []string    `json:"labels"`
	Title                   string      `json:"title"`
	UpdatedAt               *time.Time  `json:"updated_at"`
	CreatedAt               *time.Time  `json:"created_at"`
	UserNotesCount          int         `json:"user_notes_count"`
	StartDate               *ISOTime    `json:"start_date"`
	StartDateIsFixed        bool        `json:"start_date_is_fixed"`
	StartDateFixed          *ISOTime    `json:"start_date_fixed"`
	StartDateFromMilestones *ISOTime    `json:"start_date_from_milestones"`
	DueDate                 *ISOTime    `json:"due_date"`
	DueDateIsFixed          bool        `json:"due_date_is_fixed"`
	DueDateFixed            *ISOTime    `json:"due_date_fixed"`
	DueDateFromMilestones   *ISOTime    `json:"due_date_from_milestones"`
}

func (e Epic) String() string {
	return Stringify(e)
}

// ListGroupEpicsOptions represents the available ListGroupEpics() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#list-epics-for-a-group
type ListGroupEpicsOptions struct {
	ListOptions
	AuthorID                *int       `url:"author_id,omitempty" json:"author_id,omitempty"`
	Labels                  Labels     `url:"labels,comma,omitempty" json:"labels,omitempty"`
	WithLabelDetails        *bool      `url:"with_labels_details,omitempty" json:"with_labels_details,omitempty"`
	OrderBy                 *string    `url:"order_by,omitempty" json:"order_by,omitempty"`
	Sort                    *string    `url:"sort,omitempty" json:"sort,omitempty"`
	Search                  *string    `url:"search,omitempty" json:"search,omitempty"`
	State                   *string    `url:"state,omitempty" json:"state,omitempty"`
	CreatedAfter            *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
	CreatedBefore           *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
	UpdatedAfter            *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
	UpdatedBefore           *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
	IncludeAncestorGroups   *bool      `url:"include_ancestor_groups,omitempty" json:"include_ancestor_groups,omitempty"`
	IncludeDescendantGroups *bool      `url:"include_descendant_groups,omitempty" json:"include_descendant_groups,omitempty"`
	MyReactionEmoji         *string    `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
}

// ListGroupEpics gets a list of group epics. This function accepts pagination
// parameters page and per_page to return the list of group epics.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#list-epics-for-a-group
func (s *EpicsService) ListGroupEpics(gid interface{}, opt *ListGroupEpicsOptions, options ...RequestOptionFunc) ([]*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics", pathEscape(group))

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var es []*Epic
	resp, err := s.client.Do(req, &es)
	if err != nil {
		return nil, resp, err
	}

	return es, resp, err
}

// GetEpic gets a single group epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#single-epic
func (s *EpicsService) GetEpic(gid interface{}, epic int, options ...RequestOptionFunc) (*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d", pathEscape(group), epic)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	e := new(Epic)
	resp, err := s.client.Do(req, e)
	if err != nil {
		return nil, resp, err
	}

	return e, resp, err
}

// GetEpicLinks gets all child epics of an epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epic_links.html
func (s *EpicsService) GetEpicLinks(gid interface{}, epic int, options ...RequestOptionFunc) ([]*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d/epics", pathEscape(group), epic)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var e []*Epic
	resp, err := s.client.Do(req, &e)
	if err != nil {
		return nil, resp, err
	}

	return e, resp, err
}

// CreateEpicOptions represents the available CreateEpic() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#new-epic
type CreateEpicOptions struct {
	Title            *string  `url:"title,omitempty" json:"title,omitempty"`
	Description      *string  `url:"description,omitempty" json:"description,omitempty"`
	Labels           Labels   `url:"labels,comma,omitempty" json:"labels,omitempty"`
	StartDateIsFixed *bool    `url:"start_date_is_fixed,omitempty" json:"start_date_is_fixed,omitempty"`
	StartDateFixed   *ISOTime `url:"start_date_fixed,omitempty" json:"start_date_fixed,omitempty"`
	DueDateIsFixed   *bool    `url:"due_date_is_fixed,omitempty" json:"due_date_is_fixed,omitempty"`
	DueDateFixed     *ISOTime `url:"due_date_fixed,omitempty" json:"due_date_fixed,omitempty"`
}

// CreateEpic creates a new group epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#new-epic
func (s *EpicsService) CreateEpic(gid interface{}, opt *CreateEpicOptions, options ...RequestOptionFunc) (*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics", pathEscape(group))

	req, err := s.client.NewRequest("POST", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	e := new(Epic)
	resp, err := s.client.Do(req, e)
	if err != nil {
		return nil, resp, err
	}

	return e, resp, err
}

// UpdateEpicOptions represents the available UpdateEpic() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#update-epic
type UpdateEpicOptions struct {
	Title            *string  `url:"title,omitempty" json:"title,omitempty"`
	Description      *string  `url:"description,omitempty" json:"description,omitempty"`
	Labels           Labels   `url:"labels,comma,omitempty" json:"labels,omitempty"`
	StartDateIsFixed *bool    `url:"start_date_is_fixed,omitempty" json:"start_date_is_fixed,omitempty"`
	StartDateFixed   *ISOTime `url:"start_date_fixed,omitempty" json:"start_date_fixed,omitempty"`
	DueDateIsFixed   *bool    `url:"due_date_is_fixed,omitempty" json:"due_date_is_fixed,omitempty"`
	DueDateFixed     *ISOTime `url:"due_date_fixed,omitempty" json:"due_date_fixed,omitempty"`
	StateEvent       *string  `url:"state_event,omitempty" json:"state_event,omitempty"`
}

// UpdateEpic updates an existing group epic. This function is also used
// to mark an epic as closed.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#update-epic
func (s *EpicsService) UpdateEpic(gid interface{}, epic int, opt *UpdateEpicOptions, options ...RequestOptionFunc) (*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d", pathEscape(group), epic)

	req, err := s.client.NewRequest("PUT", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	e := new(Epic)
	resp, err := s.client.Do(req, e)
	if err != nil {
		return nil, resp, err
	}

	return e, resp, err
}

// DeleteEpic deletes a single group epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#delete-epic
func (s *EpicsService) DeleteEpic(gid interface{}, epic int, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d", pathEscape(group), epic)

	req, err := s.client.NewRequest("DELETE", u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
<commit_msg>Added URL to Epic Struct<commit_after>package gitlab

import (
	"fmt"
	"time"
)

// EpicsService handles communication with the epic related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html
type EpicsService struct {
	client *Client
}

// EpicAuthor represents a author of the epic.
type EpicAuthor struct {
	ID        int    `json:"id"`
	State     string `json:"state"`
	WebURL    string `json:"web_url"`
	Name      string `json:"name"`
	AvatarURL string `json:"avatar_url"`
	Username  string `json:"username"`
}

// Epic represents a GitLab epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html
type Epic struct {
	ID                      int         `json:"id"`
	IID                     int         `json:"iid"`
	GroupID                 int         `json:"group_id"`
	Author                  *EpicAuthor `json:"author"`
	ParentID                int         `json:"parent_id"`
	Description             string      `json:"description"`
	State                   string      `json:"state"`
	WebURL                  string      `json:"web_url"`
	Upvotes                 int         `json:"upvotes"`
	Downvotes               int         `json:"downvotes"`
	Labels                  []string    `json:"labels"`
	Title                   string      `json:"title"`
	UpdatedAt               *time.Time  `json:"updated_at"`
	CreatedAt               *time.Time  `json:"created_at"`
	UserNotesCount          int         `json:"user_notes_count"`
	StartDate               *ISOTime    `json:"start_date"`
	StartDateIsFixed        bool        `json:"start_date_is_fixed"`
	StartDateFixed          *ISOTime    `json:"start_date_fixed"`
	StartDateFromMilestones *ISOTime    `json:"start_date_from_milestones"`
	DueDate                 *ISOTime    `json:"due_date"`
	DueDateIsFixed          bool        `json:"due_date_is_fixed"`
	DueDateFixed            *ISOTime    `json:"due_date_fixed"`
	DueDateFromMilestones   *ISOTime    `json:"due_date_from_milestones"`
	URL                     string      `json:"url"`
}

func (e Epic) String() string {
	return Stringify(e)
}

// ListGroupEpicsOptions represents the available ListGroupEpics() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#list-epics-for-a-group
type ListGroupEpicsOptions struct {
	ListOptions
	AuthorID                *int       `url:"author_id,omitempty" json:"author_id,omitempty"`
	Labels                  Labels     `url:"labels,comma,omitempty" json:"labels,omitempty"`
	WithLabelDetails        *bool      `url:"with_labels_details,omitempty" json:"with_labels_details,omitempty"`
	OrderBy                 *string    `url:"order_by,omitempty" json:"order_by,omitempty"`
	Sort                    *string    `url:"sort,omitempty" json:"sort,omitempty"`
	Search                  *string    `url:"search,omitempty" json:"search,omitempty"`
	State                   *string    `url:"state,omitempty" json:"state,omitempty"`
	CreatedAfter            *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
	CreatedBefore           *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
	UpdatedAfter            *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
	UpdatedBefore           *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
	IncludeAncestorGroups   *bool      `url:"include_ancestor_groups,omitempty" json:"include_ancestor_groups,omitempty"`
	IncludeDescendantGroups *bool      `url:"include_descendant_groups,omitempty" json:"include_descendant_groups,omitempty"`
	MyReactionEmoji         *string    `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
}

// ListGroupEpics gets a list of group epics. This function accepts pagination
// parameters page and per_page to return the list of group epics.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#list-epics-for-a-group
func (s *EpicsService) ListGroupEpics(gid interface{}, opt *ListGroupEpicsOptions, options ...RequestOptionFunc) ([]*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics", pathEscape(group))

	req, err := s.client.NewRequest("GET", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	var es []*Epic
	resp, err := s.client.Do(req, &es)
	if err != nil {
		return nil, resp, err
	}

	return es, resp, err
}

// GetEpic gets a single group epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#single-epic
func (s *EpicsService) GetEpic(gid interface{}, epic int, options ...RequestOptionFunc) (*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d", pathEscape(group), epic)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	e := new(Epic)
	resp, err := s.client.Do(req, e)
	if err != nil {
		return nil, resp, err
	}

	return e, resp, err
}

// GetEpicLinks gets all child epics of an epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epic_links.html
func (s *EpicsService) GetEpicLinks(gid interface{}, epic int, options ...RequestOptionFunc) ([]*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d/epics", pathEscape(group), epic)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var e []*Epic
	resp, err := s.client.Do(req, &e)
	if err != nil {
		return nil, resp, err
	}

	return e, resp, err
}

// CreateEpicOptions represents the available CreateEpic() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#new-epic
type CreateEpicOptions struct {
	Title            *string  `url:"title,omitempty" json:"title,omitempty"`
	Description      *string  `url:"description,omitempty" json:"description,omitempty"`
	Labels           Labels   `url:"labels,comma,omitempty" json:"labels,omitempty"`
	StartDateIsFixed *bool    `url:"start_date_is_fixed,omitempty" json:"start_date_is_fixed,omitempty"`
	StartDateFixed   *ISOTime `url:"start_date_fixed,omitempty" json:"start_date_fixed,omitempty"`
	DueDateIsFixed   *bool    `url:"due_date_is_fixed,omitempty" json:"due_date_is_fixed,omitempty"`
	DueDateFixed     *ISOTime `url:"due_date_fixed,omitempty" json:"due_date_fixed,omitempty"`
}

// CreateEpic creates a new group epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#new-epic
func (s *EpicsService) CreateEpic(gid interface{}, opt *CreateEpicOptions, options ...RequestOptionFunc) (*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics", pathEscape(group))

	req, err := s.client.NewRequest("POST", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	e := new(Epic)
	resp, err := s.client.Do(req, e)
	if err != nil {
		return nil, resp, err
	}

	return e, resp, err
}

// UpdateEpicOptions represents the available UpdateEpic() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#update-epic
type UpdateEpicOptions struct {
	Title            *string  `url:"title,omitempty" json:"title,omitempty"`
	Description      *string  `url:"description,omitempty" json:"description,omitempty"`
	Labels           Labels   `url:"labels,comma,omitempty" json:"labels,omitempty"`
	StartDateIsFixed *bool    `url:"start_date_is_fixed,omitempty" json:"start_date_is_fixed,omitempty"`
	StartDateFixed   *ISOTime `url:"start_date_fixed,omitempty" json:"start_date_fixed,omitempty"`
	DueDateIsFixed   *bool    `url:"due_date_is_fixed,omitempty" json:"due_date_is_fixed,omitempty"`
	DueDateFixed     *ISOTime `url:"due_date_fixed,omitempty" json:"due_date_fixed,omitempty"`
	StateEvent       *string  `url:"state_event,omitempty" json:"state_event,omitempty"`
}

// UpdateEpic updates an existing group epic. This function is also used
// to mark an epic as closed.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#update-epic
func (s *EpicsService) UpdateEpic(gid interface{}, epic int, opt *UpdateEpicOptions, options ...RequestOptionFunc) (*Epic, *Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d", pathEscape(group), epic)

	req, err := s.client.NewRequest("PUT", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	e := new(Epic)
	resp, err := s.client.Do(req, e)
	if err != nil {
		return nil, resp, err
	}

	return e, resp, err
}

// DeleteEpic deletes a single group epic.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/epics.html#delete-epic
func (s *EpicsService) DeleteEpic(gid interface{}, epic int, options ...RequestOptionFunc) (*Response, error) {
	group, err := parseID(gid)
	if err != nil {
		return nil, err
	}
	u := fmt.Sprintf("groups/%s/epics/%d", pathEscape(group), epic)

	req, err := s.client.NewRequest("DELETE", u, nil, options)
	if err != nil {
		return nil, err
	}

	return s.client.Do(req, nil)
}
<|endoftext|>


<commit_before>package models

import (
	"time"

	"github.com/google/uuid"
)

// Post struct based on posts table in database
type Post struct {
	ID          uuid.UUID `db:"id"`
	UserID      uuid.UUID `db:"user_id"`
	Title       string    `db:"title"`
	Slug        string    `db:"slug"`
	SubTitle    string    `db:"sub_title"`
	Short       string    `db:"short"`
	PostContent string    `db:"post_content"`
	Digest      string    `db:"digest"`
	Published   bool      `db:"published"`
	UpdatedAt   time.Time `db:"updated_at"`
	CreatedAt   time.Time `db:"created_at"`
}

// Image struct based on image table in database
type Image struct {
	ID        uuid.UUID `db:"id"`
	UserID    uuid.UUID `db:"user_id"`
	URL       string    `db:"url"`
	Medium    string    `db:"medium"`
	Small     string    `db:"small"`
	Caption   string    `db:"caption"`
	UpdatedAt time.Time `db:"updated_at"`
	CreatedAt time.Time `db:"created_at"`
}

// Tag struct based on tag table in database
type Tag struct {
	ID   uuid.UUID `db:"id"`
	Name string    `db:"name"`
	Slug string    `db:"slug"`
}

// FindPost ...
func (db *DB) FindPost(id uuid.UUID) (*Post, error) {
	p := new(Post)
	sql := "SELECT * FROM posts WHERE id = $1"
	err := db.Get(p, sql, id)
	return p, err
}

// FindPostsByUser ...
func (db *DB) FindPostsByUser(user uuid.UUID) (*[]Post, error) {
	p := new([]Post)
	sql := "SELECT * FROM posts WHERE user_id = $1"
	err := db.Select(p, sql, user)
	return p, err
}

// InsertPost ...
func (db *DB) InsertPost(user uuid.UUID, title string, slug string, subtitle string, short string, content string, digest string, published bool) (*Post, error) {
	p := new(Post)
	sql := "INSERT INTO posts (user_id, title, slug, subtitle, short, content, digest, published) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING *"
	err := db.Get(p, sql, user, title, slug, subtitle, short, content, digest, published)
	return p, err
}

// UpdatePost ...
func (db *DB) UpdatePost(id uuid.UUID, user uuid.UUID, title string, slug string, subtitle string, short string, content string, digest string, published bool) (*Post, error) {
	p := new(Post)
	sql := "UPDATE posts SET (user_id, title, slug, subtitle, short, content, digest, published) VALUES ($2, $3, $4, $5, $6, $7, $8) WHERE id = $1 RETURNING *"
	err := db.Get(p, sql, id, user, title, slug, subtitle, short, content, digest, published)
	return p, err
}

// DeletePost ...
func (db *DB) DeletePost(id uuid.UUID) (*Post, error) {
	p := new(Post)
	sql := "DELETE FROM posts WHERE id = $1 RETURNING *"
	err := db.Get(p, sql, id)
	return p, err
}

// FindImage ...
func (db *DB) FindImage(id uuid.UUID) (*Image, error) {

}

// FindImagesByUser
func (db *DB) FindImagesByUser(user uuid.UUID) (*[]Image, error) {

}

// InsertImage
func (db *DB) InsertImage(user uuid.UUID, url string, medium string, small string, caption string) (*Image, error) {

}

//DeleteImage
func (db *DB) DeleteImage(id uuid.UUID) (*Image, error) {

}
<commit_msg>add image database functions<commit_after>package models

import (
	"time"

	"github.com/google/uuid"
)

// Post struct based on posts table in database
type Post struct {
	ID          uuid.UUID `db:"id"`
	UserID      uuid.UUID `db:"user_id"`
	Title       string    `db:"title"`
	Slug        string    `db:"slug"`
	SubTitle    string    `db:"sub_title"`
	Short       string    `db:"short"`
	PostContent string    `db:"post_content"`
	Digest      string    `db:"digest"`
	Published   bool      `db:"published"`
	UpdatedAt   time.Time `db:"updated_at"`
	CreatedAt   time.Time `db:"created_at"`
}

// Image struct based on image table in database
type Image struct {
	ID        uuid.UUID `db:"id"`
	UserID    uuid.UUID `db:"user_id"`
	URL       string    `db:"url"`
	Medium    string    `db:"medium"`
	Small     string    `db:"small"`
	Caption   string    `db:"caption"`
	UpdatedAt time.Time `db:"updated_at"`
	CreatedAt time.Time `db:"created_at"`
}

// Tag struct based on tag table in database
type Tag struct {
	ID   uuid.UUID `db:"id"`
	Name string    `db:"name"`
	Slug string    `db:"slug"`
}

// FindPost ...
func (db *DB) FindPost(id uuid.UUID) (*Post, error) {
	p := new(Post)
	sql := "SELECT * FROM posts WHERE id = $1"
	err := db.Get(p, sql, id)
	return p, err
}

// FindPostsByUser ...
func (db *DB) FindPostsByUser(user uuid.UUID) (*[]Post, error) {
	p := new([]Post)
	sql := "SELECT * FROM posts WHERE user_id = $1"
	err := db.Select(p, sql, user)
	return p, err
}

// InsertPost ...
func (db *DB) InsertPost(user uuid.UUID, title string, slug string, subtitle string, short string, content string, digest string, published bool) (*Post, error) {
	p := new(Post)
	sql := "INSERT INTO posts (user_id, title, slug, subtitle, short, content, digest, published) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING *"
	err := db.Get(p, sql, user, title, slug, subtitle, short, content, digest, published)
	return p, err
}

// UpdatePost ...
func (db *DB) UpdatePost(id uuid.UUID, user uuid.UUID, title string, slug string, subtitle string, short string, content string, digest string, published bool) (*Post, error) {
	p := new(Post)
	sql := "UPDATE posts SET (user_id, title, slug, subtitle, short, content, digest, published) VALUES ($2, $3, $4, $5, $6, $7, $8) WHERE id = $1 RETURNING *"
	err := db.Get(p, sql, id, user, title, slug, subtitle, short, content, digest, published)
	return p, err
}

// DeletePost ...
func (db *DB) DeletePost(id uuid.UUID) (*Post, error) {
	p := new(Post)
	sql := "DELETE FROM posts WHERE id = $1 RETURNING *"
	err := db.Get(p, sql, id)
	return p, err
}

// FindImage ...
func (db *DB) FindImage(id uuid.UUID) (*Image, error) {
	i := new(Image)
	sql := "SELECT * FROM images WHERE id = $1"
	err := db.Get(i, sql, id)
	return i, err
}

// FindImagesByUser
func (db *DB) FindImagesByUser(user uuid.UUID) (*[]Image, error) {
	i := new([]Image)
	sql := "SELECT * FROM images WHERE user_id = $1"
	err := db.Select(i, sql, user)
	return i, err
}

// InsertImage
func (db *DB) InsertImage(user uuid.UUID, url string, medium string, small string, caption string) (*Image, error) {
	i := new(Image)
	sql := "INSERT INTO images (user_id, url, medium, small, caption) VALUES ($1, $2, $3, $4, $5) RETURNING *"
	err := db.Get(i, sql, user)
	return i, err
}

//DeleteImage
func (db *DB) DeleteImage(id uuid.UUID) (*Image, error) {
	i := new(Image)
	sql := "DELETE FROM images WHERE id = $1"
	err := db.Get(i, sql, id)
	return i, err
}
<|endoftext|>


<commit_before>package common

import (
	"log"
	"path"
	"strconv"
	"strings"
)

const (
	//HeaderToken security token
	HeaderToken = "JRDWP_TOKEN"
	//HeaderPort jdwp port
	HeaderPort = "JDWP_PORT"
	//PublicKeyDir default client key path
	PublicKeyDir = "."
	//PublicKeyFile filename
	PublicKeyFile = ".jrdwp_key"
)

//PublicKeyPath concat file path of client key
func PublicKeyPath() string {
	return path.Join(PublicKeyDir, PublicKeyFile)
}

//SplitToInt split comma delimited string to int array
func SplitToInt(commaDelimitedString string) []int {
	ports := []int{}

	for _, token := range strings.Split(commaDelimitedString, ",") {
		port, err := strconv.Atoi(string(strings.TrimSpace(token)))
		if err != nil {
			log.Fatalln("bad integer", token, err.Error)
		} else {
			ports = append(ports, port)
		}
	}

	return ports
}
<commit_msg>changed header name for compatibility<commit_after>package common

import (
	"log"
	"path"
	"strconv"
	"strings"
)

const (
	//HeaderToken security token
	HeaderToken = "jrdwptoken"
	//HeaderPort jdwp port
	HeaderPort = "jdwpport"
	//PublicKeyDir default client key path
	PublicKeyDir = "."
	//PublicKeyFile filename
	PublicKeyFile = ".jrdwp_key"
)

//PublicKeyPath concat file path of client key
func PublicKeyPath() string {
	return path.Join(PublicKeyDir, PublicKeyFile)
}

//SplitToInt split comma delimited string to int array
func SplitToInt(commaDelimitedString string) []int {
	ports := []int{}

	for _, token := range strings.Split(commaDelimitedString, ",") {
		port, err := strconv.Atoi(string(strings.TrimSpace(token)))
		if err != nil {
			log.Fatalln("bad integer", token, err.Error)
		} else {
			ports = append(ports, port)
		}
	}

	return ports
}
<|endoftext|>


<commit_before>package common

import (
	"io"
	"log"
	"os"

	"github.com/parkr/auto-reply/Godeps/_workspace/src/github.com/google/go-github/github"
	"github.com/parkr/auto-reply/Godeps/_workspace/src/golang.org/x/oauth2"
)

const accessTokenEnvVar = "GITHUB_ACCESS_TOKEN"

func GitHubToken() string {
	return os.Getenv(accessTokenEnvVar)
}

func NewClient() *github.Client {
	if token := GitHubToken(); token != "" {
		return github.NewClient(oauth2.NewClient(
			oauth2.NoContext,
			oauth2.StaticTokenSource(
				&oauth2.Token{AccessToken: os.Getenv("GITHUB_ACCESS_TOKEN")},
			),
		))
	} else {
		log.Fatalf("%s required", accessTokenEnvVar)
		return nil
	}
}

func SliceLookup(data []string) map[string]bool {
	mapping := map[string]bool{}
	for _, datum := range data {
		mapping[datum] = true
	}
	return mapping
}

func ClearJSONRepoOrgField(reader io.Reader) []byte {
	// workaround for https://github.com/google/go-github/issues/131
	var o map[string]interface{}
	dec := json.NewDecoder(reader)
	dec.UseNumber()
	dec.Decode(&o)
	if o != nil {
		repo := o["repository"]
		if repo != nil {
			if repo, ok := repo.(map[string]interface{}); ok {
				delete(repo, "organization")
			}
		}
	}
	b, _ := json.MarshalIndent(o, "", "  ")
	return b
}
<commit_msg>Fix import issue.<commit_after>package common

import (
	"encoding/json"
	"io"
	"log"
	"os"

	"github.com/parkr/auto-reply/Godeps/_workspace/src/github.com/google/go-github/github"
	"github.com/parkr/auto-reply/Godeps/_workspace/src/golang.org/x/oauth2"
)

const accessTokenEnvVar = "GITHUB_ACCESS_TOKEN"

func GitHubToken() string {
	return os.Getenv(accessTokenEnvVar)
}

func NewClient() *github.Client {
	if token := GitHubToken(); token != "" {
		return github.NewClient(oauth2.NewClient(
			oauth2.NoContext,
			oauth2.StaticTokenSource(
				&oauth2.Token{AccessToken: os.Getenv("GITHUB_ACCESS_TOKEN")},
			),
		))
	} else {
		log.Fatalf("%s required", accessTokenEnvVar)
		return nil
	}
}

func SliceLookup(data []string) map[string]bool {
	mapping := map[string]bool{}
	for _, datum := range data {
		mapping[datum] = true
	}
	return mapping
}

func ClearJSONRepoOrgField(reader io.Reader) []byte {
	// workaround for https://github.com/google/go-github/issues/131
	var o map[string]interface{}
	dec := json.NewDecoder(reader)
	dec.UseNumber()
	dec.Decode(&o)
	if o != nil {
		repo := o["repository"]
		if repo != nil {
			if repo, ok := repo.(map[string]interface{}); ok {
				delete(repo, "organization")
			}
		}
	}
	b, _ := json.MarshalIndent(o, "", "  ")
	return b
}
<|endoftext|>