<commit_before>package main

import (
	"encoding/json"
	"math/rand"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/go-telegram-bot-api/telegram-bot-api"
	bt "github.com/ikool-cn/gobeanstalk-connection-pool"
)

var (
	telegramBot *TelegramBot
)

// TelegramBot ...
type TelegramBot struct {
	Name        string
	SelfChatID  int64
	ComicPath   string
	DeleteDelay time.Duration
	Client      *tgbotapi.BotAPI
	Queue       *bt.Pool
	Tube        string
}

// NewTelegramBot ...
func NewTelegramBot(cfg *TelegramConfig, btdAddr string) (t *TelegramBot) {
	bot, err := tgbotapi.NewBotAPI(cfg.Token)
	if err != nil {
		logger.Panic("tg bot init failed:", err)
	}
	delay, err := time.ParseDuration(cfg.DeleteDelay)
	if err != nil {
		logger.Panic("delete delay error:", err)
	}

	t = &TelegramBot{
		Name:        bot.Self.UserName,
		SelfChatID:  cfg.SelfChatID,
		ComicPath:   cfg.ComicPath,
		DeleteDelay: delay,
		Client:      bot,
		Tube:        "tg",
	}
	t.Queue = &bt.Pool{
		Dial: func() (*bt.Conn, error) {
			return bt.Dial(btdAddr)
		},
		MaxIdle:     10,
		MaxActive:   100,
		IdleTimeout: 60 * time.Second,
		MaxLifetime: 180 * time.Second,
		Wait:        true,
	}
	return
}

func (t *TelegramBot) putQueue(msg []byte) {
	conn, err := t.Queue.Get()
	if err != nil {
		logger.Error(err, msg)
		return
	}
	conn.Use(t.Tube)
	_, err = conn.Put(msg, 1, t.DeleteDelay, time.Minute)
	if err != nil {
		logger.Error(err)
		return
	}
}

func (t *TelegramBot) sendFile(chat int64, file string, mediaType string) {
	logger.Debugf("send:[%d]%s", chat, file)
	var err error
	switch mediaType {
	case "photo":
		_, err = t.Client.Send(tgbotapi.NewPhotoUpload(chat, file))
	case "video":
		_, err = t.Client.Send(tgbotapi.NewVideoUpload(chat, file))
	case "animated_gif":
		_, err = t.Client.Send(tgbotapi.NewVideoUpload(chat, file))
	default:
		logger.Notice("media type ignored:", mediaType)
	}
	if err != nil {
		logger.Error(err)
	}
}

func (t *TelegramBot) delMessage() {
	for {
		conn, err := t.Queue.Get()
		if err != nil {
			logger.Error(err)
			time.Sleep(3 * time.Second)
			continue
		}
		conn.Watch(t.Tube)
		job, err := conn.Reserve()
		if err != nil {
			logger.Warning(err)
			time.Sleep(3 * time.Second)
			continue
		}
		msg := &tgbotapi.Message{}
		err = json.Unmarshal(job.Body, msg)
		if err != nil {
			logger.Error(err)
			err = conn.Bury(job.ID, 0)
			if err != nil {
				logger.Error(err)
			}
			time.Sleep(3 * time.Second)
			continue
		}
		delMsg := tgbotapi.DeleteMessageConfig{
			ChatID:    msg.Chat.ID,
			MessageID: msg.MessageID,
		}
		if msg.Chat.IsGroup() {
			logger.Infof(":(%s){%s}", msg.Chat.Title, strconv.Quote(msg.Text))
		} else {
			if msg.Chat.UserName != "" {
				logger.Infof(":[%s]{%s}", msg.Chat.UserName, strconv.Quote(msg.Text))
			} else {
				logger.Infof(":[%s]{%s}", msg.Chat.FirstName+msg.Chat.LastName, strconv.Quote(msg.Text))
			}
		}

		_, err = t.Client.DeleteMessage(delMsg)
		if err != nil {
			logger.Error(err)
			err = conn.Bury(job.ID, 0)
			if err != nil {
				logger.Error(err)
			}
			time.Sleep(3 * time.Second)
			continue
		}
		err = conn.Delete(job.ID)
		if err != nil {
			logger.Error(err)
			time.Sleep(3 * time.Second)
		}
		t.Queue.Release(conn, false)
	}
}

func (t *TelegramBot) tgBot() {
	u := tgbotapi.NewUpdate(0)
	u.Timeout = 60
	for {
		updates, err := t.Client.GetUpdatesChan(u)
		if err != nil {
			logger.Error(err)
			time.Sleep(3 * time.Second)
			continue
		}

		for update := range updates {
			if update.Message == nil {
				continue
			}
			if !update.Message.IsCommand() {
				continue
			}

			if update.Message.Chat.IsGroup() {
				logger.Infof(
					"recv:[%s](%s){%s}",
					update.Message.From.String(),
					update.Message.Chat.Title,
					strconv.Quote(update.Message.Text))
			} else {
				logger.Infof(
					"recv:[%s]{%s}",
					update.Message.From.String(),
					strconv.Quote(update.Message.Text),
				)
			}

			switch update.Message.Command() {
			case "start":
				go onStart(t, &update)
			case "comic":
				go onComic(t, &update)
			case "pic":
				go onPic(t, &update)
			default:
				logger.Info("ignore unkown cmd:", update.Message.Command())
				continue

			}
		}
		logger.Warning("tg bot restarted.")
		time.Sleep(3 * time.Second)
	}
}

func onStart(t *TelegramBot, update *tgbotapi.Update) {
	msg := tgbotapi.NewMessage(update.Message.Chat.ID, "呀呀呀")
	msg.ReplyToMessageID = update.Message.MessageID
	t.Client.Send(msg)
}

func onComic(t *TelegramBot, update *tgbotapi.Update) {
	files, err := filepath.Glob(t.ComicPath)
	if err != nil {
		logger.Error(err)
		return
	}
	rand.Seed(time.Now().Unix())
	file := files[rand.Intn(len(files))]
	number := strings.Split(strings.Split(file, "@")[1], ".")[0]
	msg := tgbotapi.NewMessage(update.Message.Chat.ID, "https://nhentai.net/g/"+number)

	message, err := t.Client.Send(msg)
	if err != nil {
		logger.Error(err)
		return
	}
	data, err := json.Marshal(message)
	if err != nil {
		logger.Error(err)
		return
	}
	t.putQueue(data)
}

func onPic(t *TelegramBot, update *tgbotapi.Update) {
	files, err := filepath.Glob(twitterBot.ImgPath + "/*")
	if err != nil {
		logger.Error(err)
		return
	}
	if files == nil {
		logger.Error("find no pics")
	}
	rand.Seed(time.Now().Unix())
	file := files[rand.Intn(len(files))]
	logger.Infof("send:[%s](%s){%s}", update.Message.Chat.UserName, update.Message.Chat.Title, file)
	if strings.HasSuffix(file, ".mp4") {
		t.sendFile(update.Message.Chat.ID, file, "video")
	} else {
		t.sendFile(update.Message.Chat.ID, file, "photo")
	}
}
<commit_msg>fix getmsgtarget<commit_after>package main

import (
	"encoding/json"
	"math/rand"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/go-telegram-bot-api/telegram-bot-api"
	bt "github.com/ikool-cn/gobeanstalk-connection-pool"
)

var (
	telegramBot *TelegramBot
)

// TelegramBot ...
type TelegramBot struct {
	Name        string
	SelfChatID  int64
	ComicPath   string
	DeleteDelay time.Duration
	Client      *tgbotapi.BotAPI
	Queue       *bt.Pool
	Tube        string
}

// NewTelegramBot ...
func NewTelegramBot(cfg *TelegramConfig, btdAddr string) (t *TelegramBot) {
	bot, err := tgbotapi.NewBotAPI(cfg.Token)
	if err != nil {
		logger.Panic("tg bot init failed:", err)
	}
	delay, err := time.ParseDuration(cfg.DeleteDelay)
	if err != nil {
		logger.Panic("delete delay error:", err)
	}

	t = &TelegramBot{
		Name:        bot.Self.UserName,
		SelfChatID:  cfg.SelfChatID,
		ComicPath:   cfg.ComicPath,
		DeleteDelay: delay,
		Client:      bot,
		Tube:        "tg",
	}
	t.Queue = &bt.Pool{
		Dial: func() (*bt.Conn, error) {
			return bt.Dial(btdAddr)
		},
		MaxIdle:     10,
		MaxActive:   100,
		IdleTimeout: 60 * time.Second,
		MaxLifetime: 180 * time.Second,
		Wait:        true,
	}
	return
}

func (t *TelegramBot) putQueue(msg []byte) {
	conn, err := t.Queue.Get()
	if err != nil {
		logger.Error(err, msg)
		return
	}
	conn.Use(t.Tube)
	_, err = conn.Put(msg, 1, t.DeleteDelay, time.Minute)
	if err != nil {
		logger.Error(err)
		return
	}
}

func (t *TelegramBot) sendFile(chat int64, file string, mediaType string) {
	logger.Debugf("[%d]%s", chat, file)
	var err error
	switch mediaType {
	case "photo":
		_, err = t.Client.Send(tgbotapi.NewPhotoUpload(chat, file))
	case "video":
		_, err = t.Client.Send(tgbotapi.NewVideoUpload(chat, file))
	case "animated_gif":
		_, err = t.Client.Send(tgbotapi.NewVideoUpload(chat, file))
	default:
		logger.Notice("media type ignored:", mediaType)
	}
	if err != nil {
		logger.Error(err)
	}
}

func (t *TelegramBot) delMessage() {
	for {
		conn, err := t.Queue.Get()
		if err != nil {
			logger.Error(err)
			time.Sleep(3 * time.Second)
			continue
		}
		conn.Watch(t.Tube)
		job, err := conn.Reserve()
		if err != nil {
			logger.Warning(err)
			time.Sleep(3 * time.Second)
			continue
		}
		msg := &tgbotapi.Message{}
		err = json.Unmarshal(job.Body, msg)
		if err != nil {
			logger.Error(err)
			err = conn.Bury(job.ID, 0)
			if err != nil {
				logger.Error(err)
			}
			time.Sleep(3 * time.Second)
			continue
		}
		delMsg := tgbotapi.DeleteMessageConfig{
			ChatID:    msg.Chat.ID,
			MessageID: msg.MessageID,
		}
		logger.Infof(":[%s]{%s}", getMsgTarget(msg), strconv.Quote(msg.Text))

		_, err = t.Client.DeleteMessage(delMsg)
		if err != nil {
			logger.Error(err)
			err = conn.Bury(job.ID, 0)
			if err != nil {
				logger.Error(err)
			}
			time.Sleep(3 * time.Second)
			continue
		}
		err = conn.Delete(job.ID)
		if err != nil {
			logger.Error(err)
			time.Sleep(3 * time.Second)
		}
		t.Queue.Release(conn, false)
	}
}

func (t *TelegramBot) tgBot() {
	u := tgbotapi.NewUpdate(0)
	u.Timeout = 60
	for {
		updates, err := t.Client.GetUpdatesChan(u)
		if err != nil {
			logger.Error(err)
			time.Sleep(3 * time.Second)
			continue
		}

		for update := range updates {
			if update.Message == nil {
				continue
			}
			if !update.Message.IsCommand() {
				continue
			}

			if update.Message.Chat.IsGroup() {
				logger.Infof(
					"recv:[%s](%s){%s}",
					update.Message.From.String(),
					update.Message.Chat.Title,
					strconv.Quote(update.Message.Text))
			} else {
				logger.Infof(
					"recv:[%s]{%s}",
					update.Message.From.String(),
					strconv.Quote(update.Message.Text),
				)
			}

			switch update.Message.Command() {
			case "start":
				go onStart(t, &update)
			case "comic":
				go onComic(t, &update)
			case "pic":
				go onPic(t, &update)
			default:
				logger.Info("ignore unkown cmd:", update.Message.Command())
				continue

			}
		}
		logger.Warning("tg bot restarted.")
		time.Sleep(3 * time.Second)
	}
}

func onStart(t *TelegramBot, update *tgbotapi.Update) {
	msg := tgbotapi.NewMessage(update.Message.Chat.ID, "呀呀呀")
	msg.ReplyToMessageID = update.Message.MessageID
	t.Client.Send(msg)
}

func onComic(t *TelegramBot, update *tgbotapi.Update) {
	files, err := filepath.Glob(t.ComicPath)
	if err != nil {
		logger.Error(err)
		return
	}
	rand.Seed(time.Now().Unix())
	file := files[rand.Intn(len(files))]
	number := strings.Split(strings.Split(file, "@")[1], ".")[0]
	msg := tgbotapi.NewMessage(update.Message.Chat.ID, "https://nhentai.net/g/"+number)

	message, err := t.Client.Send(msg)
	if err != nil {
		logger.Error(err)
		return
	}
	data, err := json.Marshal(message)
	if err != nil {
		logger.Error(err)
		return
	}
	t.putQueue(data)
}

func onPic(t *TelegramBot, update *tgbotapi.Update) {
	files, err := filepath.Glob(twitterBot.ImgPath + "/*")
	if err != nil {
		logger.Error(err)
		return
	}
	if files == nil {
		logger.Error("find no pics")
	}
	rand.Seed(time.Now().Unix())
	file := files[rand.Intn(len(files))]

	logger.Infof("send:[%s]{%s}", getMsgTarget(update.Message), file)
	if strings.HasSuffix(file, ".mp4") {
		t.sendFile(update.Message.Chat.ID, file, "video")
	} else {
		t.sendFile(update.Message.Chat.ID, file, "photo")
	}
}

func getMsgTarget(m *tgbotapi.Message) string {
	if m.Chat.IsGroup() {
		return m.Chat.Title
	}
	if m.Chat.UserName != "" {
		return m.Chat.UserName
	}
	return m.Chat.FirstName + m.Chat.LastName
}
<|endoftext|>


<commit_before>package transloadit

type Template struct {
	Id      string          `json:"id"`
	Name    string          `json:"name"`
	Content TemplateContent `json:"content"`
}

type TemplateContent struct {
	Steps map[string]interface{} `json:"steps"`
}

type TemplateList struct {
	Templates []Template `json:"items"`
	Count     int        `json:"count"`
}

// NewTemplate returns a new Template struct with initialized values. This
// template will not be saved to Transloadit. Do to so, please use the
// Client.CreateTemplate function.
func NewTemplate() Template {
	return Template{
		Content: TemplateContent{
			make(map[string]interface{}),
		},
	}
}

// AddStep will add the provided step to the Template.Content.Steps map.
func (template *Template) AddStep(name string, step map[string]interface{}) {
	template.Content.Steps[name] = step
}

// CreateTemplate will save the provided template sturct as a new template
// and return the ID of the new template.
func (client *Client) CreateTemplate(template Template) (string, error) {
	content := map[string]interface{}{
		"name":     template.Name,
		"template": template.Content,
	}

	if err := client.request("POST", "templates", content, &template); err != nil {
		return "", err
	}

	return template.Id, nil
}

// GetTemplate will retrieve details about the template associated with the
// provided template ID.
func (client *Client) GetTemplate(templateId string) (template Template, err error) {
	err = client.request("GET", "templates/"+templateId, nil, &template)
	return template, err
}

// DeleteTemplate will delete the template associated with the provided
// template ID.
func (client *Client) DeleteTemplate(templateId string) error {
	return client.request("DELETE", "templates/"+templateId, nil, nil)
}

// UpdateTemplate will update the template associated with the provided
// template ID to match the new name and  new content. Please be aware that you
// are not able to change a template's ID.
func (client *Client) UpdateTemplate(templateId string, newTemplate Template) error {
	// Create signature
	content := map[string]interface{}{
		"name":     newTemplate.Name,
		"template": newTemplate.Content,
	}

	return client.request("PUT", "templates/"+templateId, content, nil)
}

// GetTemplate will retrieve all templates matching the criteria.
func (client *Client) ListTemplates(options *ListOptions) (list TemplateList, err error) {
	err = client.listRequest("templates", options, &list)
	return list, err
}
<commit_msg>Correct minor documentation misspellings<commit_after>package transloadit

type Template struct {
	Id      string          `json:"id"`
	Name    string          `json:"name"`
	Content TemplateContent `json:"content"`
}

type TemplateContent struct {
	Steps map[string]interface{} `json:"steps"`
}

type TemplateList struct {
	Templates []Template `json:"items"`
	Count     int        `json:"count"`
}

// NewTemplate returns a new Template struct with initialized values. This
// template will not be saved to Transloadit. To do so, please use the
// Client.CreateTemplate function.
func NewTemplate() Template {
	return Template{
		Content: TemplateContent{
			make(map[string]interface{}),
		},
	}
}

// AddStep will add the provided step to the Template.Content.Steps map.
func (template *Template) AddStep(name string, step map[string]interface{}) {
	template.Content.Steps[name] = step
}

// CreateTemplate will save the provided template sturct as a new template
// and return the ID of the new template.
func (client *Client) CreateTemplate(template Template) (string, error) {
	content := map[string]interface{}{
		"name":     template.Name,
		"template": template.Content,
	}

	if err := client.request("POST", "templates", content, &template); err != nil {
		return "", err
	}

	return template.Id, nil
}

// GetTemplate will retrieve details about the template associated with the
// provided template ID.
func (client *Client) GetTemplate(templateId string) (template Template, err error) {
	err = client.request("GET", "templates/"+templateId, nil, &template)
	return template, err
}

// DeleteTemplate will delete the template associated with the provided
// template ID.
func (client *Client) DeleteTemplate(templateId string) error {
	return client.request("DELETE", "templates/"+templateId, nil, nil)
}

// UpdateTemplate will update the template associated with the provided
// template ID to match the new name and  new content. Please be aware that you
// are not able to change a template's ID.
func (client *Client) UpdateTemplate(templateId string, newTemplate Template) error {
	// Create signature
	content := map[string]interface{}{
		"name":     newTemplate.Name,
		"template": newTemplate.Content,
	}

	return client.request("PUT", "templates/"+templateId, content, nil)
}

// ListTemplate will retrieve all templates matching the criteria.
func (client *Client) ListTemplates(options *ListOptions) (list TemplateList, err error) {
	err = client.listRequest("templates", options, &list)
	return list, err
}
<|endoftext|>


<commit_before>// Copyright 2012-2014, Rolf Veen and contributors.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ogdl

import (
	"bytes"
)

// NewTemplate parses a text template given as a string and converts it to a Graph.
// Templates have fixed and variable parts. Variables all begin with '$'.
//
// A template is a text file in any format: plain text, HTML, XML, OGDL or
// whatever. The dolar sign acts as an escape character that switches from the
// text to the variable plane. Parsed templates are converted back to text
// by evaluating the variable parts against a Graph object, by means of the
// Process() method.
//
// Template grammar
//
//     template ::= ( text | variable )*
//
//     variable ::= ('$' path) | ('$' '(' expression ')') | ('$' '{' expression '}')
//     path ::= as defined in path.go
//     expression ::= as defined in expression.go
//
// Some variables act as directives: $if, $else, $end, $for, $break.
//
//    $if(expression)
//    $else
//    $end
//
//    $for(destPath,sourcepath)
//      $break
//    $end
//
func NewTemplate(s string) *Graph {
	p := newStringParser(s)
	p.Template()

	t := p.graphTop(TypeTemplate)
	t.ast()
	t.simplify()
	t.flow()

	return t
}

// Process processes the parsed template, returning the resulting text in a byte array.
// The variable parts are resolved out of the Graph given.
func (g *Graph) Process(c *Graph) []byte {

	buffer := &bytes.Buffer{}

	g.process(c, buffer)

	return buffer.Bytes()
}

func (g *Graph) process(c *Graph, buffer *bytes.Buffer) bool {

	falseIf := false

	for _, n := range g.Out {
		s := n.ThisString()

		switch s {
		case TypePath:
			i := c.Eval(n)
			buffer.WriteString(_text(i))

		case TypeExpression:
			// Silent evaluation
			c.Eval(n)
		case TypeIf:
			// evaluate the expression
			b := c.evalBool(n.GetAt(0).GetAt(0))

			if b {
				n.GetAt(1).process(c, buffer)
				falseIf = false
			} else {
				falseIf = true
			}
		case TypeElse:
			// if there was a previous if evaluating to false:
			if falseIf {
				n.process(c, buffer)
				falseIf = false
			}
		case TypeFor:
			// The first subnode (of !g) is a path
			// The second is an expression evaluating to a list of elements
			i := c.Eval(n.GetAt(0).GetAt(1))

			// Check that i is iterable
			gi, ok := i.(*Graph)
			if !ok || gi == nil {
				continue
			}

			// The third is the subtemplate to travel
			// println ("for type: ",reflect.TypeOf(i).String(), "ok",ok)
			// Assing expression value to path
			// XXX if not Graph

			varname := n.GetAt(0).GetAt(0).GetAt(0).String()
			it := c.Node(varname)
			if it == nil {
				it = c.Add(varname)
			}

			for _, ee := range gi.Out {
				it.Out = nil
				it.Add(ee)
				brk := n.GetAt(1).process(c, buffer)
				if brk {
					break
				}
			}
		case TypeBreak:
			return true

		default:
			buffer.WriteString(n.ThisString())
		}
	}
	return false
}

// simplify converts !p TYPE in !TYPE for keywords if, end, else for and break.
func (g *Graph) simplify() {

	if g == nil {
		return
	}

	for _, node := range g.Out {
		if TypePath == node.ThisString() {
			s := node.GetAt(0).ThisString()

			switch s {
			case "if":
				node.This = TypeIf
				node.DeleteAt(0)
			case "end":
				node.This = TypeEnd
				node.DeleteAt(0)
			case "else":
				node.This = TypeElse
				node.DeleteAt(0)
			case "for":
				node.This = TypeFor
				node.DeleteAt(0)
			case "break":
				node.This = TypeBreak
				node.DeleteAt(0)
			}
		}
	}

}

// flow nests 'if' and 'for' loops.
func (g *Graph) flow() {
	n := 0
	var nod *Graph

	for i := 0; i < g.Len(); i++ {

		node := g.Out[i]
		s := node.ThisString()

		if s == TypeIf || s == TypeFor {
			n++
			if n == 1 {
				nod = node.Add(TypeTemplate)
				continue
			}
		}

		if s == TypeElse {
			if n == 1 {
				nod.flow()
				nod = node
				continue
			}
		}

		if s == TypeEnd {
			n--
			if n == 0 {
				nod.flow()
				g.DeleteAt(i)
				i--
				continue
			}
		}

		if n > 0 {
			nod.Add(node)
			g.DeleteAt(i)
			i--
		}
	}

}
<commit_msg>added nil checks<commit_after>// Copyright 2012-2014, Rolf Veen and contributors.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ogdl

import (
	"bytes"
)

// NewTemplate parses a text template given as a string and converts it to a Graph.
// Templates have fixed and variable parts. Variables all begin with '$'.
//
// A template is a text file in any format: plain text, HTML, XML, OGDL or
// whatever. The dolar sign acts as an escape character that switches from the
// text to the variable plane. Parsed templates are converted back to text
// by evaluating the variable parts against a Graph object, by means of the
// Process() method.
//
// Template grammar
//
//     template ::= ( text | variable )*
//
//     variable ::= ('$' path) | ('$' '(' expression ')') | ('$' '{' expression '}')
//     path ::= as defined in path.go
//     expression ::= as defined in expression.go
//
// Some variables act as directives: $if, $else, $end, $for, $break.
//
//    $if(expression)
//    $else
//    $end
//
//    $for(destPath,sourcepath)
//      $break
//    $end
//
func NewTemplate(s string) *Graph {
	p := newStringParser(s)
	p.Template()

	t := p.graphTop(TypeTemplate)
	t.ast()
	t.simplify()
	t.flow()

	return t
}

// Process processes the parsed template, returning the resulting text in a byte array.
// The variable parts are resolved out of the Graph given.
func (g *Graph) Process(c *Graph) []byte {

	buffer := &bytes.Buffer{}

	g.process(c, buffer)

	return buffer.Bytes()
}

func (g *Graph) process(c *Graph, buffer *bytes.Buffer) bool {

	if g == nil || g.Out == nil {
		return false
	}

	falseIf := false

	for _, n := range g.Out {
		s := n.ThisString()

		switch s {
		case TypePath:
			i := c.Eval(n)
			buffer.WriteString(_text(i))

		case TypeExpression:
			// Silent evaluation
			c.Eval(n)
		case TypeIf:
			// evaluate the expression
			b := c.evalBool(n.GetAt(0).GetAt(0))

			if b {
				n.GetAt(1).process(c, buffer)
				falseIf = false
			} else {
				falseIf = true
			}
		case TypeElse:
			// if there was a previous if evaluating to false:
			if falseIf {
				n.process(c, buffer)
				falseIf = false
			}
		case TypeFor:
			// The first subnode (of !g) is a path
			// The second is an expression evaluating to a list of elements
			i := c.Eval(n.GetAt(0).GetAt(1))

			// Check that i is iterable
			gi, ok := i.(*Graph)
			if !ok || gi == nil {
				continue
			}

			// The third is the subtemplate to travel
			// println ("for type: ",reflect.TypeOf(i).String(), "ok",ok)
			// Assing expression value to path
			// XXX if not Graph

			varname := n.GetAt(0).GetAt(0).GetAt(0).String()
			it := c.Node(varname)
			if it == nil {
				it = c.Add(varname)
			}

			for _, ee := range gi.Out {
				it.Out = nil
				it.Add(ee)
				brk := n.GetAt(1).process(c, buffer)
				if brk {
					break
				}
			}
		case TypeBreak:
			return true

		default:
			buffer.WriteString(n.ThisString())
		}
	}
	return false
}

// simplify converts !p TYPE in !TYPE for keywords if, end, else for and break.
func (g *Graph) simplify() {

	if g == nil {
		return
	}

	for _, node := range g.Out {
		if TypePath == node.ThisString() {
			s := node.GetAt(0).ThisString()

			switch s {
			case "if":
				node.This = TypeIf
				node.DeleteAt(0)
			case "end":
				node.This = TypeEnd
				node.DeleteAt(0)
			case "else":
				node.This = TypeElse
				node.DeleteAt(0)
			case "for":
				node.This = TypeFor
				node.DeleteAt(0)
			case "break":
				node.This = TypeBreak
				node.DeleteAt(0)
			}
		}
	}

}

// flow nests 'if' and 'for' loops.
func (g *Graph) flow() {
	n := 0
	var nod *Graph

	for i := 0; i < g.Len(); i++ {

		node := g.Out[i]
		s := node.ThisString()

		if s == TypeIf || s == TypeFor {
			n++
			if n == 1 {
				nod = node.Add(TypeTemplate)
				continue
			}
		}

		if s == TypeElse {
			if n == 1 {
				nod.flow()
				nod = node
				continue
			}
		}

		if s == TypeEnd {
			n--
			if n == 0 {
				nod.flow()
				g.DeleteAt(i)
				i--
				continue
			}
		}

		if n > 0 {
			nod.Add(node)
			g.DeleteAt(i)
			i--
		}
	}

}
<|endoftext|>


<commit_before>// Copyright (c) 2017, Mitchell Cooper
package main

import (
	"encoding/json"
	"errors"
	wikiclient "github.com/cooper/go-wikiclient"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

var templateDirs string
var templates = make(map[string]wikiTemplate)

var templateFuncs = map[string]interface{}{
	"even": func(i int) bool {
		return i%2 == 0
	},
	"odd": func(i int) bool {
		return i%2 != 0
	},
}

type wikiTemplate struct {
	path       string             // template directory path
	template   *template.Template // master HTML template
	staticPath string             // static file directory path, if any
	staticRoot string             // static file directory HTTP root, if any
	manifest   struct {

		// human-readable template name
		// Name   string

		// template author's name
		// Author string

		// URL to template code on the web, such as GitHub repository
		// Code   string

		// wiki logo info
		Logo struct {

			// ideally one of these dimensions will be specified and the other
			// not. used for the logo specified by the wiki 'logo' directive.
			// usually the height is specified. if both are present, the
			// logo will be generated in those exact dimensions.
			Height int
			Width  int
		}
	}
}

// search all template directories for a template by its name
func findTemplate(name string) (wikiTemplate, error) {

	// template is already cached
	if t, ok := templates[name]; ok {
		return t, nil
	}

	for _, templateDir := range strings.Split(templateDirs, ",") {
		templatePath := templateDir + "/" + name
		t, err := loadTemplate(name, templatePath)

		// an error occurred in loading the template
		if err != nil {
			return t, err
		}

		// no template but no error means try the next directory
		if t.template == nil {
			continue
		}

		return t, nil
	}

	// never found a template
	return wikiTemplate{}, errors.New("unable to find template " + name)
}

// load a template from its known path
func loadTemplate(name, templatePath string) (wikiTemplate, error) {
	var t wikiTemplate
	var tryNextDirectory bool

	// template is already cached
	if t, ok := templates[name]; ok {
		return t, nil
	}

	// parse HTML templates
	tmpl := template.New("")
	err := filepath.Walk(templatePath, func(filePath string, info os.FileInfo, err error) error {

		// walk error, probably missing template
		if err != nil {
			tryNextDirectory = true
			return err
		}

		// found template file
		if strings.HasSuffix(filePath, ".tpl") {

			// error in parsing
			subTmpl, err := tmpl.ParseFiles(filePath)
			if err != nil {
				return err
			}

			// add funcs
			subTmpl.Funcs(templateFuncs)
		}

		// found static content directory
		if info.IsDir() && info.Name() == "static" {
			t.staticPath = filePath
			t.staticRoot = "/tmpl/" + name
			fileServer := http.FileServer(http.Dir(filePath))
			pfx := t.staticRoot + "/"
			http.Handle(pfx, http.StripPrefix(pfx, fileServer))
			log.Printf("[%s] template registered: %s", name, pfx)
		}

		// found manifest
		if info.Name() == "manifest.json" {

			// couldn't read manifest
			contents, err := ioutil.ReadFile(filePath)
			if err != nil {
				return err
			}

			// couldn't parse manifest
			if err := json.Unmarshal(contents, &t.manifest); err != nil {
				return err
			}
		}

		return err
	})

	// not found
	if tryNextDirectory {
		return t, nil
	}

	// other error
	if err != nil {
		return t, err
	}

	// cache the template
	t.path = templatePath
	t.template = tmpl
	templates[name] = t

	return t, nil
}

type wikiPage struct {
	File       string             // page name, with extension
	Name       string             // page name, without extension
	WholeTitle string             // optional, shown in <title> as-is
	Title      string             // page title
	WikiTitle  string             // wiki titled
	WikiLogo   string             // path to wiki logo image
	WikiRoot   string             // wiki HTTP root
	Res        wikiclient.Message // response
	StaticRoot string             // path to static resources
	Pages      []wikiPage         // more pages for category posts
	navigation []interface{}      // slice of nav items [display, url]
}

type navItem struct {
	Display string
	Link    string
}

func (p wikiPage) VisibleTitle() string {
	if p.WholeTitle != "" {
		return p.WholeTitle
	}
	if p.Title == p.WikiTitle {
		return p.Title
	}
	return p.Title + " - " + p.WikiTitle
}

func (p wikiPage) Scripts() []string {
	return []string{
		"/static/mootools.min.js",
		"/static/retina.min.js",
		"/static/wiki.js",
		"https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js",
	}
}

func (p wikiPage) PageCSS() template.CSS {
	return template.CSS(p.Res.Get("css"))
}

func (p wikiPage) HTMLContent() template.HTML {
	return template.HTML(p.Res.Get("content"))
}

func (p wikiPage) Navigation() []navItem {

	// no navigation
	if len(p.navigation) != 2 {
		return nil
	}

	// first item is ordered keys, second is values
	displays := p.navigation[0].([]interface{})
	urls := p.navigation[1].([]interface{})
	items := make([]navItem, len(displays))
	for i := 0; i < len(items); i++ {
		items[i] = navItem{
			displays[i].(string),
			urls[i].(string),
		}
	}

	return items
}
<commit_msg>look for all_css<commit_after>// Copyright (c) 2017, Mitchell Cooper
package main

import (
	"encoding/json"
	"errors"
	wikiclient "github.com/cooper/go-wikiclient"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

var templateDirs string
var templates = make(map[string]wikiTemplate)

var templateFuncs = map[string]interface{}{
	"even": func(i int) bool {
		return i%2 == 0
	},
	"odd": func(i int) bool {
		return i%2 != 0
	},
}

type wikiTemplate struct {
	path       string             // template directory path
	template   *template.Template // master HTML template
	staticPath string             // static file directory path, if any
	staticRoot string             // static file directory HTTP root, if any
	manifest   struct {

		// human-readable template name
		// Name   string

		// template author's name
		// Author string

		// URL to template code on the web, such as GitHub repository
		// Code   string

		// wiki logo info
		Logo struct {

			// ideally one of these dimensions will be specified and the other
			// not. used for the logo specified by the wiki 'logo' directive.
			// usually the height is specified. if both are present, the
			// logo will be generated in those exact dimensions.
			Height int
			Width  int
		}
	}
}

// search all template directories for a template by its name
func findTemplate(name string) (wikiTemplate, error) {

	// template is already cached
	if t, ok := templates[name]; ok {
		return t, nil
	}

	for _, templateDir := range strings.Split(templateDirs, ",") {
		templatePath := templateDir + "/" + name
		t, err := loadTemplate(name, templatePath)

		// an error occurred in loading the template
		if err != nil {
			return t, err
		}

		// no template but no error means try the next directory
		if t.template == nil {
			continue
		}

		return t, nil
	}

	// never found a template
	return wikiTemplate{}, errors.New("unable to find template " + name)
}

// load a template from its known path
func loadTemplate(name, templatePath string) (wikiTemplate, error) {
	var t wikiTemplate
	var tryNextDirectory bool

	// template is already cached
	if t, ok := templates[name]; ok {
		return t, nil
	}

	// parse HTML templates
	tmpl := template.New("")
	err := filepath.Walk(templatePath, func(filePath string, info os.FileInfo, err error) error {

		// walk error, probably missing template
		if err != nil {
			tryNextDirectory = true
			return err
		}

		// found template file
		if strings.HasSuffix(filePath, ".tpl") {

			// error in parsing
			subTmpl, err := tmpl.ParseFiles(filePath)
			if err != nil {
				return err
			}

			// add funcs
			subTmpl.Funcs(templateFuncs)
		}

		// found static content directory
		if info.IsDir() && info.Name() == "static" {
			t.staticPath = filePath
			t.staticRoot = "/tmpl/" + name
			fileServer := http.FileServer(http.Dir(filePath))
			pfx := t.staticRoot + "/"
			http.Handle(pfx, http.StripPrefix(pfx, fileServer))
			log.Printf("[%s] template registered: %s", name, pfx)
		}

		// found manifest
		if info.Name() == "manifest.json" {

			// couldn't read manifest
			contents, err := ioutil.ReadFile(filePath)
			if err != nil {
				return err
			}

			// couldn't parse manifest
			if err := json.Unmarshal(contents, &t.manifest); err != nil {
				return err
			}
		}

		return err
	})

	// not found
	if tryNextDirectory {
		return t, nil
	}

	// other error
	if err != nil {
		return t, err
	}

	// cache the template
	t.path = templatePath
	t.template = tmpl
	templates[name] = t

	return t, nil
}

type wikiPage struct {
	File       string             // page name, with extension
	Name       string             // page name, without extension
	WholeTitle string             // optional, shown in <title> as-is
	Title      string             // page title
	WikiTitle  string             // wiki titled
	WikiLogo   string             // path to wiki logo image
	WikiRoot   string             // wiki HTTP root
	Res        wikiclient.Message // response
	StaticRoot string             // path to static resources
	Pages      []wikiPage         // more pages for category posts
	navigation []interface{}      // slice of nav items [display, url]
}

type navItem struct {
	Display string
	Link    string
}

func (p wikiPage) VisibleTitle() string {
	if p.WholeTitle != "" {
		return p.WholeTitle
	}
	if p.Title == p.WikiTitle || p.Title == "" {
		return p.WikiTitle
	}
	return p.Title + " - " + p.WikiTitle
}

func (p wikiPage) Scripts() []string {
	return []string{
		"/static/mootools.min.js",
		"/static/retina.min.js",
		"/static/wiki.js",
		"https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js",
	}
}

func (p wikiPage) PageCSS() template.CSS {
	css := p.Res.Get("all_css")
	if css == "" {
		css = p.Res.Get("css")
	}
	return template.CSS(css)
}

func (p wikiPage) HTMLContent() template.HTML {
	return template.HTML(p.Res.Get("content"))
}

func (p wikiPage) Navigation() []navItem {

	// no navigation
	if len(p.navigation) != 2 {
		return nil
	}

	// first item is ordered keys, second is values
	displays := p.navigation[0].([]interface{})
	urls := p.navigation[1].([]interface{})
	items := make([]navItem, len(displays))
	for i := 0; i < len(items); i++ {
		items[i] = navItem{
			displays[i].(string),
			urls[i].(string),
		}
	}

	return items
}
<|endoftext|>


<commit_before>// Copyright 2011, Bryan Matsuo. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
/*
 *  Filename:    template.go
 *  Package:     main
 *  Author:      Bryan Matsuo <bmatsuo@soe.ucsc.edu>
 *  Created:     Sun Jul  3 17:55:40 PDT 2011
 *  Description:
 */
import (
	"errors"
	"fmt"
	"io"
	//"log"
	"bytes"
	//"strings"
	//"io/ioutil"
	"path/filepath"
	"runtime"
	"text/template"
	//"github.com/hoisie/mustache.go"
)

const (
	TemplateFileExt = ".t"
)

var (
	NoTemplateError = errors.New("Requested template does not exist")
	ParseError      = errors.New("Couldn't parse template")
)

type Executor interface {
	Execute(io.Writer, interface{}) error
}

type ExecutorSet interface {
	Execute(io.Writer, string, interface{}) error
}

func collectBytes(f func(w io.Writer) error) (p []byte, err error) {
	b := new(bytes.Buffer)
	err = f(b)
	p = b.Bytes()
	return
}

//  Call t.Execute with the given data, writing to a []byte buffer.
func Executed(t Executor, data interface{}) ([]byte, error) {
	return collectBytes(func(w io.Writer) error { return t.Execute(w, data) })
}

//  Call s.Execute with the given name data, writing to a []byte buffer.
func ExecutedSet(s ExecutorSet, name string, data interface{}) ([]byte, error) {
	return collectBytes(func(w io.Writer) error { return s.Execute(w, name, data) })
}

//  Returns an os-specific pattern <root>/"*"/*<TemplateFileExt>
func TemplateGlobPattern(root string) string {
	return filepath.Join(root, "*", fmt.Sprintf("*%s", TemplateFileExt))
}

type TemplateMultiSet []*template.Template

//  Call function CollectTemplates on each given root and create a TemplateMultiSet.
func MakeTemplateMultiSet(f template.FuncMap, roots ...string) (ms TemplateMultiSet, err error) {
	var s *template.Template
	ms = make(TemplateMultiSet, len(roots))
	for i := range roots {
		if s, err = CollectTemplates(roots[i], f); err != nil {
			break
		} else {
			ms[i] = s
		}
	}
	return
}

//  Execute a template in first s in ms for which s.Template(name) is non-nil.
func (ms TemplateMultiSet) Execute(wr io.Writer, name string, data interface{}) error {
	for _, s := range ms {
		if t := s.Lookup(name); t != nil {
			return t.Execute(wr, data)
		}
	}
	return NoTemplateError
}

func emptyTemplate(name string) (*template.Template, error) { return template.New(name).Parse("") }

//  Parse templates <root>/*/*.t, allowing them a given function map.
func CollectTemplates(root string, f template.FuncMap) (s *template.Template, err error) {
	switch s, err = emptyTemplate(root); {
	case err != nil:
		return
	case f != nil:
		s.Funcs(f)
	}
	return s.ParseGlob(TemplateGlobPattern(root))
}

//  The template directory of the goinstall'ed gonew package.
func GetTemplateRoot() []string {
	return []string{runtime.GOROOT(), "src", "pkg",
		"github.com", "bmatsuo", "gonew", "templates"}
}
<commit_msg>Don't panic if a specified directory doesn't exist or is empty.<commit_after>// Copyright 2011, Bryan Matsuo. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
/*
 *  Filename:    template.go
 *  Package:     main
 *  Author:      Bryan Matsuo <bmatsuo@soe.ucsc.edu>
 *  Created:     Sun Jul  3 17:55:40 PDT 2011
 *  Description:
 */
import (
	"errors"
	"fmt"
	"io"
	"os"
	//"log"
	"bytes"
	//"strings"
	//"io/ioutil"
	"path/filepath"
	"runtime"
	"text/template"
	//"github.com/hoisie/mustache.go"
)

const (
	TemplateFileExt = ".t"
)

var (
	NoTemplateError = errors.New("Requested template does not exist")
	ParseError      = errors.New("Couldn't parse template")
)

type Executor interface {
	Execute(io.Writer, interface{}) error
}

type ExecutorSet interface {
	Execute(io.Writer, string, interface{}) error
}

func collectBytes(f func(w io.Writer) error) (p []byte, err error) {
	b := new(bytes.Buffer)
	err = f(b)
	p = b.Bytes()
	return
}

//  Call t.Execute with the given data, writing to a []byte buffer.
func Executed(t Executor, data interface{}) ([]byte, error) {
	return collectBytes(func(w io.Writer) error { return t.Execute(w, data) })
}

//  Call s.Execute with the given name data, writing to a []byte buffer.
func ExecutedSet(s ExecutorSet, name string, data interface{}) ([]byte, error) {
	return collectBytes(func(w io.Writer) error { return s.Execute(w, name, data) })
}

//  Returns an os-specific pattern <root>/"*"/*<TemplateFileExt>
func TemplateGlobPattern(root string) string {
	return filepath.Join(root, "*", fmt.Sprintf("*%s", TemplateFileExt))
}

type TemplateMultiSet []*template.Template

//  Call function CollectTemplates on each given root and create a TemplateMultiSet.
func MakeTemplateMultiSet(f template.FuncMap, roots ...string) (ms TemplateMultiSet, err error) {
	var s *template.Template
	ms = make(TemplateMultiSet, 0, len(roots))
	for i := range roots {
		if s, err = CollectTemplates(roots[i], f); err != nil {
			fmt.Fprintln(os.Stderr, err)
		} else {
			ms = append(ms, s)
		}
	}
	if len(ms) == 0 {
		err = errors.New("No valid templates found")
	}
	return
}

//  Execute a template in first s in ms for which s.Template(name) is non-nil.
func (ms TemplateMultiSet) Execute(wr io.Writer, name string, data interface{}) error {
	for _, s := range ms {
		if t := s.Lookup(name); t != nil {
			return t.Execute(wr, data)
		}
	}
	return NoTemplateError
}

func emptyTemplate(name string) (*template.Template, error) { return template.New(name).Parse("") }

//  Parse templates <root>/*/*.t, allowing them a given function map.
func CollectTemplates(root string, f template.FuncMap) (s *template.Template, err error) {
	switch s, err = emptyTemplate(root); {
	case err != nil:
		return
	case f != nil:
		s.Funcs(f)
	}
	return s.ParseGlob(TemplateGlobPattern(root))
}

//  The template directory of the goinstall'ed gonew package.
func GetTemplateRoot() []string {
	return []string{runtime.GOROOT(), "src", "pkg",
		"github.com", "bmatsuo", "gonew", "templates"}
}
<|endoftext|>


<commit_before>// Package args is the bridge between command line arguments and wallpaper preferences.
package args

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strconv"

	"github.com/alexcormier/setwp/pref"
	"github.com/alexcormier/setwp/pref/event"
	"github.com/alexcormier/setwp/pref/position"
	"github.com/docopt/docopt-go"
)

const (
	programName = "setwp"

	usage = `Sets wallpaper to <wallpaper>, a <directory> of wallpapers or a solid color.
Fills the screen by default.

Usage:
  %[1]s [--fill | --fit | --stretch | --center | --tile] [--color=<hex>] <wallpaper>
  %[1]s (--interval=<s> | --login | --wake) [--random] [--fill | --fit | --stretch | --center | --tile] [--color=<hex>] <directory>
  %[1]s --color=<hex>
  %[1]s --help | --version

Options:
  -C --color=<hex>  Color to fill the screen with, as an RGB hex code.
  -h --help         Show this help message.
  -v --version      Show version information.

Fit options:
  -F --fill         Scale wallpaper to fill screen [default].
  -f --fit          Fit wallpaper to screen.
  -s --stretch      Stretch wallpaper to fill screen.
  -c --center       Center wallpaper, scaling it down if it is too large.
  -t --tile         Tile wallpaper.

Directory options:
  -i --interval=<s>  Interval at which to change wallpaper, in seconds.
  -l --login         Change wallpaper when logging in.
  -w --wake          Change wallpaper when waking from sleep.
  -r --random        Randomize wallpaper selection.

`

	version = "%s version 1.1.1"
)

type argument struct {
	prefs func(value interface{}) (pref.Prefs, error)
}

// Type arg represents the preferences set by an argument.
type argPrefs struct {
	// Preferences to set when this argument is specified.
	flagPrefs pref.Prefs

	// Preferences to set to the value of this argument.
	valuePrefs []pref.KeyType

	// Function to validate and extract the preference's value from the argument's value.
	value func(interface{}) (pref.ValueType, error)
}

var (
	defaultPrefs = pref.Prefs{
		pref.Position: position.Fill,
		pref.SolidColor: true,
	}

	argMap = map[string]argument{
		"--fit": argument{
			prefs: func(value interface{}) (pref.Prefs, error) {
				return pref.Prefs{pref.Position: position.Fit}, nil
			},
		},
		"--stretch": argument{
			prefs: func(value interface{}) (pref.Prefs, error) {
				return pref.Prefs{pref.Position: position.Stretch}, nil
			},
		},
		"--center": argument{
			prefs: func(value interface{}) (pref.Prefs, error) {
				return pref.Prefs{pref.Position: position.Center}, nil
			},
		},
		"--tile": argument{
			prefs: func(value interface{}) (pref.Prefs, error) {
				return pref.Prefs{pref.Position: position.Tile}, nil
			},
		},
		"--color": argument{
			prefs: func(value interface{}) (pref.Prefs, error) {
				colorString := value.(string)
				if len(colorString) == 6 {
					color, err := strconv.ParseUint(colorString, 16, 32)
					if err == nil {
						prefs := pref.Prefs{
							pref.Red: float64((color >> 16) & 0xFF) / 255,
							pref.Green: float64((color >> 8) & 0xFF) / 255,
							pref.Blue: float64(color & 0xFF) / 255,
						}
						return prefs, nil
					}
				}
				return nil, errors.New("invalid color")
			},
		},
		"--interval": argument{
			prefs: func(value interface{}) (pref.Prefs, error) {
				interval, err := strconv.ParseUint(value.(string), 10, 0)
				if err != nil {
					return nil, errors.New("invalid interval")
				}
				prefs := pref.Prefs{
					pref.ChangeEvent: event.Interval,
					pref.Interval: interval,
				}
				return prefs, nil
			},
		},
		"--login": argument{
			prefs: func(value interface{}) (pref.Prefs, error) {
				return pref.Prefs{pref.ChangeEvent: event.Login}, nil
			},
		},
		"--wake": argument{
			prefs: func(value interface{}) (pref.Prefs, error) {
				return pref.Prefs{pref.ChangeEvent: event.Wake}, nil
			},
		},
		"--random": argument{
			prefs: func(value interface{}) (pref.Prefs, error) {
				return pref.Prefs{pref.Random: true}, nil
			},
		},
		"<wallpaper>": argument{
			prefs: func(value interface{}) (pref.Prefs, error) {
				path, err := filepath.Abs(value.(string))
				if err != nil {
					return nil, fmt.Errorf("invalid wallpaper '%s': %v", value, err)
				}
				info, err := os.Stat(path)
				if err != nil {
					return nil, fmt.Errorf("invalid wallpaper '%s': %v", value, err.(*os.PathError).Err)
				}
				if info.IsDir() {
					return nil, fmt.Errorf("invalid wallpaper: %s is a directory", value)
				}
				prefs := pref.Prefs{
					pref.Wallpaper: path,
					pref.SolidColor: false,
				}
				return prefs, nil
			},
		},
		"<directory>": argument{
			prefs: func(value interface{}) (pref.Prefs, error) {
				path, err := filepath.Abs(value.(string))
				if err != nil {
					return nil, fmt.Errorf("invalid wallpaper '%s': %v", value, err)
				}
				info, err := os.Stat(path)
				if err != nil {
					return nil, fmt.Errorf("invalid wallpaper '%s': %v", value, err.(*os.PathError).Err)
				}
				if !info.IsDir() {
					return nil, fmt.Errorf("%s is not a directory", path)
				}
				prefs := pref.Prefs{
					pref.Directory: path,
					pref.SolidColor: false,
				}
				return prefs, nil
			},
		},
	}
)

// Parses command line arguments and returns the preferences to apply or an error if there is any.
// If the help or version flag is passed, the corresponding message is printed and the program exits.
// If the arguments don't match one of the usage patterns, the usage message is printed and the program exits.
func Parse() (pref.Prefs, error) {
	parsedArgs := defaultPrefs

	opts, err := docopt.Parse(fmt.Sprintf(usage, programName), nil, true, fmt.Sprintf(version, programName), true)
	if err != nil {
		return parsedArgs, fmt.Errorf("cannot parse arguments (%s)", err)
	}

	for optKey, optValue := range opts {
		if b, ok := optValue.(bool); !ok && optValue != nil || b {
			// this option has a value or is a flag and was specified
			if argument, ok := argMap[optKey]; ok {
				// specifying this option has an effect that's not default so we process it

				prefs, err := argument.prefs(optValue)
				if err != nil {
					return parsedArgs, err
				}

				for key, value := range prefs {
					parsedArgs[key] = value
				}
			} else {
				// this option was not specified
			}
		}
	}
	return parsedArgs, nil
}
<commit_msg>partially fix bug w/ dirs on recent macOS versions<commit_after>// Package args is the bridge between command line arguments and wallpaper preferences.
package args

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strconv"

	"github.com/alexcormier/setwp/pref"
	"github.com/alexcormier/setwp/pref/event"
	"github.com/alexcormier/setwp/pref/position"
	docopt "github.com/docopt/docopt-go"
)

const (
	programName = "setwp"

	usage = `Sets wallpaper to <wallpaper>, a <directory> of wallpapers or a solid color.
Fills the screen by default.

Usage:
  %[1]s [--fill | --fit | --stretch | --center | --tile] [--color=<hex>] <wallpaper>
  %[1]s (--interval=<s> | --login | --wake) [--random] [--fill | --fit | --stretch | --center | --tile] [--color=<hex>] <directory>
  %[1]s --color=<hex>
  %[1]s --help | --version

Options:
  -C --color=<hex>  Color to fill the screen with, as an RGB hex code.
  -h --help         Show this help message.
  -v --version      Show version information.

Fit options:
  -F --fill         Scale wallpaper to fill screen [default].
  -f --fit          Fit wallpaper to screen.
  -s --stretch      Stretch wallpaper to fill screen.
  -c --center       Center wallpaper, scaling it down if it is too large.
  -t --tile         Tile wallpaper.

Directory options:
  -i --interval=<s>  Interval at which to change wallpaper, in seconds.
  -l --login         Change wallpaper when logging in.
  -w --wake          Change wallpaper when waking from sleep.
  -r --random        Randomize wallpaper selection.

`

	version = "%s version 1.1.1"
)

type argument struct {
	prefs func(value interface{}) (pref.Prefs, error)
}

// Type arg represents the preferences set by an argument.
type argPrefs struct {
	// Preferences to set when this argument is specified.
	flagPrefs pref.Prefs

	// Preferences to set to the value of this argument.
	valuePrefs []pref.KeyType

	// Function to validate and extract the preference's value from the argument's value.
	value func(interface{}) (pref.ValueType, error)
}

var (
	defaultPrefs = pref.Prefs{
		pref.Position:   position.Fill,
		pref.SolidColor: true,
	}

	argMap = map[string]argument{
		"--fit": argument{
			prefs: func(value interface{}) (pref.Prefs, error) {
				return pref.Prefs{pref.Position: position.Fit}, nil
			},
		},
		"--stretch": argument{
			prefs: func(value interface{}) (pref.Prefs, error) {
				return pref.Prefs{pref.Position: position.Stretch}, nil
			},
		},
		"--center": argument{
			prefs: func(value interface{}) (pref.Prefs, error) {
				return pref.Prefs{pref.Position: position.Center}, nil
			},
		},
		"--tile": argument{
			prefs: func(value interface{}) (pref.Prefs, error) {
				return pref.Prefs{pref.Position: position.Tile}, nil
			},
		},
		"--color": argument{
			prefs: func(value interface{}) (pref.Prefs, error) {
				colorString := value.(string)
				if len(colorString) == 6 {
					color, err := strconv.ParseUint(colorString, 16, 32)
					if err == nil {
						prefs := pref.Prefs{
							pref.Red:   float64((color>>16)&0xFF) / 255,
							pref.Green: float64((color>>8)&0xFF) / 255,
							pref.Blue:  float64(color&0xFF) / 255,
						}
						return prefs, nil
					}
				}
				return nil, errors.New("invalid color")
			},
		},
		"--interval": argument{
			prefs: func(value interface{}) (pref.Prefs, error) {
				interval, err := strconv.ParseUint(value.(string), 10, 0)
				if err != nil {
					return nil, errors.New("invalid interval")
				}
				prefs := pref.Prefs{
					pref.ChangeEvent: event.Interval,
					pref.Interval:    interval,
				}
				return prefs, nil
			},
		},
		"--login": argument{
			prefs: func(value interface{}) (pref.Prefs, error) {
				return pref.Prefs{pref.ChangeEvent: event.Login}, nil
			},
		},
		"--wake": argument{
			prefs: func(value interface{}) (pref.Prefs, error) {
				return pref.Prefs{pref.ChangeEvent: event.Wake}, nil
			},
		},
		"--random": argument{
			prefs: func(value interface{}) (pref.Prefs, error) {
				return pref.Prefs{pref.Random: true}, nil
			},
		},
		"<wallpaper>": argument{
			prefs: func(value interface{}) (pref.Prefs, error) {
				path, err := filepath.Abs(value.(string))
				if err != nil {
					return nil, fmt.Errorf("invalid wallpaper '%s': %v", value, err)
				}
				info, err := os.Stat(path)
				if err != nil {
					return nil, fmt.Errorf("invalid wallpaper '%s': %v", value, err.(*os.PathError).Err)
				}
				if info.IsDir() {
					return nil, fmt.Errorf("invalid wallpaper: %s is a directory", value)
				}
				prefs := pref.Prefs{
					pref.Wallpaper:  path,
					pref.SolidColor: false,
				}
				return prefs, nil
			},
		},
		"<directory>": argument{
			prefs: func(value interface{}) (pref.Prefs, error) {
				path, err := filepath.Abs(value.(string))
				if err != nil {
					return nil, fmt.Errorf("invalid wallpaper '%s': %v", value, err)
				}
				info, err := os.Stat(path)
				if err != nil {
					return nil, fmt.Errorf("invalid wallpaper '%s': %v", value, err.(*os.PathError).Err)
				}
				if !info.IsDir() {
					return nil, fmt.Errorf("%s is not a directory", path)
				}

				prefs := pref.Prefs{
					pref.Directory:  path,
					pref.Current:    path, // partial fix for recent macOS versions (see issue #3)
					pref.SolidColor: false,
				}
				return prefs, nil
			},
		},
	}
)

// Parse command line arguments and returns the preferences to apply or an error if there is any.
// If the help or version flag is passed, the corresponding message is printed and the program exits.
// If the arguments don't match one of the usage patterns, the usage message is printed and the program exits.
func Parse() (pref.Prefs, error) {
	parsedArgs := defaultPrefs

	opts, err := docopt.Parse(fmt.Sprintf(usage, programName), nil, true, fmt.Sprintf(version, programName), true)
	if err != nil {
		return parsedArgs, fmt.Errorf("cannot parse arguments (%s)", err)
	}

	for optKey, optValue := range opts {
		if b, ok := optValue.(bool); !ok && optValue != nil || b {
			// this option has a value or is a flag and was specified
			if argument, ok := argMap[optKey]; ok {
				// specifying this option has an effect that's not default so we process it

				prefs, err := argument.prefs(optValue)
				if err != nil {
					return parsedArgs, err
				}

				for key, value := range prefs {
					parsedArgs[key] = value
				}
			} else {
				// this option was not specified
			}
		}
	}
	return parsedArgs, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os/exec"
	"runtime"

	termbox "github.com/nsf/termbox-go"
)

type Terminal struct {
	Links         []Link
	Selected      int
	Width, Height int
}

var (
	EventChan    = make(chan termbox.Event)
	KeyTab       = termbox.KeyTab
	KeyEnter     = termbox.KeyEnter
	KeyArrowUp   = termbox.KeyArrowUp
	KeyArrowDown = termbox.KeyArrowDown
	KeyCtrlP     = termbox.KeyCtrlP
	KeyCtrlN     = termbox.KeyCtrlN
	KeyCtrlO     = termbox.KeyCtrlO
	KeyCtrlC     = termbox.KeyCtrlC
)

func PollEvent() {
	EventChan <- termbox.PollEvent()
}

func NewTerminal(links *[]Link) *Terminal {
	term := Terminal{
		Links:    *links,
		Selected: 0,
		Width:    0,
		Height:   0,
	}

	return &term
}

func (t *Terminal) Start() error {
	err := termbox.Init()
	if err != nil {
		return err
	}

	return nil
}

func (t *Terminal) Close() {
	termbox.Close()
}

func (t *Terminal) HandleEvent(e termbox.Event) (bool, error) {
	if e.Type == termbox.EventResize {
		t.SetSize()
		t.Render()
	}

	var err error
	if e.Type == termbox.EventKey {
		if e.Ch == 0 {
			switch e.Key {
			case KeyArrowDown:
				t.MoveSelection("down")
				t.Render()
			case KeyCtrlN:
				t.MoveSelection("down")
				t.Render()
			case KeyArrowUp:
				t.MoveSelection("up")
				t.Render()
			case KeyCtrlP:
				t.MoveSelection("up")
				t.Render()
			case KeyEnter:
				err = t.Select()
			case KeyCtrlO:
				err = t.Select()
			case KeyCtrlC:
				return true, nil
			}
		} else {
			switch e.Ch {
			case 'j':
				t.MoveSelection("down")
				t.Render()
			case 'k':
				t.MoveSelection("up")
				t.Render()
			case 'q':
				return true, nil
			}
		}
	}

	return false, err
}

func (t *Terminal) Println(x int, y int, s string) {
	for col, char := range s {
		termbox.SetCell(col+x, y, char, termbox.ColorDefault, termbox.ColorDefault)
	}
}

func (t *Terminal) Render() {
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)

	help := "j/C-n: move down   k/C-p: move up   return/C-o: open url   q: quit"
	url := t.Links[t.Selected].URL
	t.Println(0, 0, help)
	t.Println(0, 2, url)

	var start int
	offset := t.Selected - t.Height + 6
	if t.Selected > t.Height-6 {
		start = offset
	}

	links := t.Links
	for i := start; i < len(links); i++ {
		if t.Selected > t.Height-6 {
			t.Println(0, t.Height-2, "->")
			t.Println(3, i+4-offset, links[i].Text)
		} else {
			t.Println(0, t.Selected+4, "->")
			t.Println(3, i+4, links[i].Text)
		}
	}

	termbox.Flush()
}

func (t *Terminal) SetSize() {
	t.Width, t.Height = termbox.Size()
}

func (t *Terminal) MoveSelection(direction string) {
	switch direction {
	case "up":
		t.Selected--
	case "down":
		t.Selected++
	}

	if t.Selected >= len(t.Links) {
		t.Selected = len(t.Links) - 1
	}

	if t.Selected < 0 {
		t.Selected = 0
	}
}

func (t *Terminal) Select() error {
	var err error
	url := t.Links[t.Selected].URL

	switch runtime.GOOS {
	case "linux":
		err = exec.Command("xdg-open", url).Start()
	case "windows":
		err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
	case "darwin":
		err = exec.Command("open", url).Start()
	default:
		err = fmt.Errorf("can't open browser: unsupported platform")
	}

	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Add show full link option<commit_after>package main

import (
	"fmt"
	"os/exec"
	"runtime"

	termbox "github.com/nsf/termbox-go"
)

type Terminal struct {
	Links         []Link
	Selected      int
	Width, Height int
}

const help string = "j/C-n: move down   k/C-p: move up   tab: show URL   return/C-o: open url   q: quit"

var (
	EventChan    = make(chan termbox.Event)
	KeyTab       = termbox.KeyTab
	KeyEnter     = termbox.KeyEnter
	KeyArrowUp   = termbox.KeyArrowUp
	KeyArrowDown = termbox.KeyArrowDown
	KeyCtrlP     = termbox.KeyCtrlP
	KeyCtrlN     = termbox.KeyCtrlN
	KeyCtrlO     = termbox.KeyCtrlO
	KeyCtrlC     = termbox.KeyCtrlC
)

func PollEvent() {
	EventChan <- termbox.PollEvent()
}

func NewTerminal(links *[]Link) *Terminal {
	term := Terminal{
		Links:    *links,
		Selected: 0,
		Width:    0,
		Height:   0,
	}

	return &term
}

func (t *Terminal) Start() error {
	err := termbox.Init()
	if err != nil {
		return err
	}

	return nil
}

func (t *Terminal) Close() {
	termbox.Close()
}

func (t *Terminal) HandleEvent(e termbox.Event) (bool, error) {
	if e.Type == termbox.EventResize {
		t.SetSize()
		t.Render()
	}

	var err error
	if e.Type == termbox.EventKey {
		if e.Ch == 0 {
			switch e.Key {
			case KeyArrowDown:
				t.MoveSelection("down")
				t.Render()
			case KeyCtrlN:
				t.MoveSelection("down")
				t.Render()
			case KeyArrowUp:
				t.MoveSelection("up")
				t.Render()
			case KeyCtrlP:
				t.MoveSelection("up")
				t.Render()
			case KeyTab:
				t.ShowFullLink()
			case KeyEnter:
				err = t.Select()
			case KeyCtrlO:
				err = t.Select()
			case KeyCtrlC:
				return true, nil
			}
		} else {
			switch e.Ch {
			case 'j':
				t.MoveSelection("down")
				t.Render()
			case 'k':
				t.MoveSelection("up")
				t.Render()
			case 'q':
				return true, nil
			}
		}
	}

	return false, err
}

func (t *Terminal) Println(x int, y int, s string) {
	for col, char := range s {
		termbox.SetCell(col+x, y, char, termbox.ColorDefault, termbox.ColorDefault)
	}
}

func (t *Terminal) ShowFullLink() {
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)

	t.Println(0, 0, help)

	url := t.Links[t.Selected].URL
	row := 2
	col := 0
	for _, char := range url {
		if col >= t.Width {
			row++
			col = 0
		}
		termbox.SetCell(col, row, char, termbox.ColorDefault, termbox.ColorDefault)
		col++
	}

	termbox.Flush()
}

func (t *Terminal) Render() {
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)

	url := t.Links[t.Selected].URL
	t.Println(0, 0, help)
	t.Println(0, 2, url)

	var start int
	offset := t.Selected - t.Height + 6
	if t.Selected > t.Height-6 {
		start = offset
	}

	links := t.Links
	for i := start; i < len(links); i++ {
		if t.Selected > t.Height-6 {
			t.Println(0, t.Height-2, "->")
			t.Println(3, i+4-offset, links[i].Text)
		} else {
			t.Println(0, t.Selected+4, "->")
			t.Println(3, i+4, links[i].Text)
		}
	}

	termbox.Flush()
}

func (t *Terminal) SetSize() {
	t.Width, t.Height = termbox.Size()
}

func (t *Terminal) MoveSelection(direction string) {
	switch direction {
	case "up":
		t.Selected--
	case "down":
		t.Selected++
	}

	if t.Selected >= len(t.Links) {
		t.Selected = len(t.Links) - 1
	}

	if t.Selected < 0 {
		t.Selected = 0
	}
}

func (t *Terminal) Select() error {
	var err error
	url := t.Links[t.Selected].URL

	switch runtime.GOOS {
	case "linux":
		err = exec.Command("xdg-open", url).Start()
	case "windows":
		err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
	case "darwin":
		err = exec.Command("open", url).Start()
	default:
		err = fmt.Errorf("can't open browser: unsupported platform")
	}

	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package venom

import (
	"fmt"
	"reflect"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/fsamin/go-dump"
	"github.com/mitchellh/mapstructure"
	"github.com/smartystreets/assertions"
)

type testingT struct {
	ErrorS []string
}

func (t *testingT) Error(args ...interface{}) {
	for _, a := range args {
		switch v := a.(type) {
		case string:
			t.ErrorS = append(t.ErrorS, v)
		default:
			t.ErrorS = append(t.ErrorS, fmt.Sprintf("%s", v))
		}
	}
}

// applyChecks apply checks on result, return true if all assertions are OK, false otherwise
func applyChecks(executorResult *ExecutorResult, step TestStep, defaultAssertions *StepAssertions, l *log.Entry) (bool, []Failure, []Failure, string, string) {
	isOK, errors, failures, systemout, systemerr := applyAssertions(*executorResult, step, defaultAssertions, l)
	if !isOK {
		return isOK, errors, failures, systemout, systemerr
	}

	isOKExtract, errorsExtract, failuresExtract := applyExtracts(executorResult, step, l)

	errors = append(errors, errorsExtract...)
	failures = append(failures, failuresExtract...)

	return isOKExtract, errors, failures, systemout, systemerr
}

func applyAssertions(executorResult ExecutorResult, step TestStep, defaultAssertions *StepAssertions, l *log.Entry) (bool, []Failure, []Failure, string, string) {
	var sa StepAssertions
	var errors []Failure
	var failures []Failure
	var systemerr, systemout string

	if err := mapstructure.Decode(step, &sa); err != nil {
		return false, []Failure{{Value: fmt.Sprintf("error decoding assertions: %s", err)}}, failures, systemout, systemerr
	}

	if len(sa.Assertions) == 0 && defaultAssertions != nil {
		sa = *defaultAssertions
	}

	isOK := true
	for _, assertion := range sa.Assertions {
		errs, fails := check(assertion, executorResult, l)
		if errs != nil {
			errors = append(errors, *errs)
			isOK = false
		}
		if fails != nil {
			failures = append(failures, *fails)
			isOK = false
		}
	}

	if _, ok := executorResult["result.systemerr"]; ok {
		systemerr = executorResult["result.systemerr"]
	}

	if _, ok := executorResult["result.systemout"]; ok {
		systemout = executorResult["result.systemout"]
	}

	return isOK, errors, failures, systemout, systemerr
}

func check(assertion string, executorResult ExecutorResult, l *log.Entry) (*Failure, *Failure) {
	assert := strings.Split(assertion, " ")
	if len(assert) < 2 {
		return &Failure{Value: fmt.Sprintf("invalid assertion '%s' len:'%d'", assertion, len(assert))}, nil
	}

	actual, ok := executorResult[assert[0]]
	if !ok {
		if assert[1] == "ShouldNotExist" {
			return nil, nil
		}
		return &Failure{Value: fmt.Sprintf("key '%s' does not exist in result of executor: %+v", assert[0], executorResult)}, nil
	} else if assert[1] == "ShouldNotExist" {
		return &Failure{Value: fmt.Sprintf("key '%s' should not exist in result of executor. Value: %+v", assert[0], actual)}, nil
	}

	f, ok := assertMap[assert[1]]
	if !ok {
		return &Failure{Value: fmt.Sprintf("Method not found '%s'", assert[1])}, nil
	}
	args := make([]interface{}, len(assert[2:]))
	for i, v := range assert[2:] { // convert []string to []interface for assertions.func()...
		args[i] = v
	}

	out := f(actual, args...)

	if out != "" {
		prefix := "assertion: " + assertion
		sdump, _ := dump.Sdump(executorResult)
		return nil, &Failure{Value: prefix + "\n" + out + "\n" + sdump}
	}
	return nil, nil
}

// assertMap contains list of assertions func
var assertMap = map[string]func(actual interface{}, expected ...interface{}) string{
	// "ShouldNotExist" see func check
	"ShouldEqual":                  assertions.ShouldEqual,
	"ShouldNotEqual":               assertions.ShouldNotEqual,
	"ShouldAlmostEqual":            assertions.ShouldAlmostEqual,
	"ShouldNotAlmostEqual":         assertions.ShouldNotAlmostEqual,
	"ShouldResemble":               assertions.ShouldResemble,
	"ShouldNotResemble":            assertions.ShouldNotResemble,
	"ShouldPointTo":                assertions.ShouldPointTo,
	"ShouldNotPointTo":             assertions.ShouldNotPointTo,
	"ShouldBeTrue":                 assertions.ShouldBeTrue,
	"ShouldBeFalse":                assertions.ShouldBeFalse,
	"ShouldBeZeroValue":            assertions.ShouldBeZeroValue,
	"ShouldBeGreaterThan":          assertions.ShouldBeGreaterThan,
	"ShouldBeGreaterThanOrEqualTo": assertions.ShouldBeGreaterThanOrEqualTo,
	"ShouldBeLessThan":             assertions.ShouldBeLessThan,
	"ShouldBeLessThanOrEqualTo":    assertions.ShouldBeLessThanOrEqualTo,
	"ShouldBeBetween":              assertions.ShouldBeBetween,
	"ShouldNotBeBetween":           assertions.ShouldNotBeBetween,
	"ShouldBeBetweenOrEqual":       assertions.ShouldBeBetweenOrEqual,
	"ShouldNotBeBetweenOrEqual":    assertions.ShouldNotBeBetweenOrEqual,
	"ShouldContain":                assertions.ShouldContain,
	"ShouldNotContain":             assertions.ShouldNotContain,
	"ShouldContainKey":             assertions.ShouldContainKey,
	"ShouldNotContainKey":          assertions.ShouldNotContainKey,
	"ShouldBeIn":                   assertions.ShouldBeIn,
	"ShouldNotBeIn":                assertions.ShouldNotBeIn,
	"ShouldBeEmpty":                assertions.ShouldBeEmpty,
	"ShouldNotBeEmpty":             assertions.ShouldNotBeEmpty,
	"ShouldHaveLength":             assertions.ShouldHaveLength,
	"ShouldStartWith":              assertions.ShouldStartWith,
	"ShouldNotStartWith":           assertions.ShouldNotStartWith,
	"ShouldEndWith":                assertions.ShouldEndWith,
	"ShouldNotEndWith":             assertions.ShouldNotEndWith,
	"ShouldBeBlank":                assertions.ShouldBeBlank,
	"ShouldNotBeBlank":             assertions.ShouldNotBeBlank,
	"ShouldContainSubstring":       ShouldContainSubstring,
	"ShouldNotContainSubstring":    assertions.ShouldNotContainSubstring,
	"ShouldEqualWithout":           assertions.ShouldEqualWithout,
	"ShouldEqualTrimSpace":         assertions.ShouldEqualTrimSpace,
	"ShouldHappenBefore":           assertions.ShouldHappenBefore,
	"ShouldHappenOnOrBefore":       assertions.ShouldHappenOnOrBefore,
	"ShouldHappenAfter":            assertions.ShouldHappenAfter,
	"ShouldHappenOnOrAfter":        assertions.ShouldHappenOnOrAfter,
	"ShouldHappenBetween":          assertions.ShouldHappenBetween,
	"ShouldHappenOnOrBetween":      assertions.ShouldHappenOnOrBetween,
	"ShouldNotHappenOnOrBetween":   assertions.ShouldNotHappenOnOrBetween,
	"ShouldHappenWithin":           assertions.ShouldHappenWithin,
	"ShouldNotHappenWithin":        assertions.ShouldNotHappenWithin,
	"ShouldBeChronological":        assertions.ShouldBeChronological,
}

// ShouldContainSubstring receives exactly more than 2 string parameters and ensures that the first contains the second as a substring.
func ShouldContainSubstring(actual interface{}, expected ...interface{}) string {
	if len(expected) == 0 {
		return "This assertion requires at least 1 comparison value (you provided 0)."
	}

	var arg string
	for _, e := range expected {
		arg = fmt.Sprintf("%s %v", arg, e)
	}

	long, longOk := actual.(string)
	if !longOk {
		return fmt.Sprintf("Both arguments to this assertion must be strings (you provided %v and %v).", reflect.TypeOf(actual), reflect.TypeOf(arg))
	}

	if !strings.Contains(long, arg) {
		return fmt.Sprintf("Expected '%s' to contain substring '%s' (but it didn't)!", long, arg)
	}
	return ""
}
<commit_msg>feat (assertions): redefine ShouldContainSubstring<commit_after>package venom

import (
	"fmt"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/fsamin/go-dump"
	"github.com/mitchellh/mapstructure"
	"github.com/smartystreets/assertions"
)

type testingT struct {
	ErrorS []string
}

func (t *testingT) Error(args ...interface{}) {
	for _, a := range args {
		switch v := a.(type) {
		case string:
			t.ErrorS = append(t.ErrorS, v)
		default:
			t.ErrorS = append(t.ErrorS, fmt.Sprintf("%s", v))
		}
	}
}

// applyChecks apply checks on result, return true if all assertions are OK, false otherwise
func applyChecks(executorResult *ExecutorResult, step TestStep, defaultAssertions *StepAssertions, l *log.Entry) (bool, []Failure, []Failure, string, string) {
	isOK, errors, failures, systemout, systemerr := applyAssertions(*executorResult, step, defaultAssertions, l)
	if !isOK {
		return isOK, errors, failures, systemout, systemerr
	}

	isOKExtract, errorsExtract, failuresExtract := applyExtracts(executorResult, step, l)

	errors = append(errors, errorsExtract...)
	failures = append(failures, failuresExtract...)

	return isOKExtract, errors, failures, systemout, systemerr
}

func applyAssertions(executorResult ExecutorResult, step TestStep, defaultAssertions *StepAssertions, l *log.Entry) (bool, []Failure, []Failure, string, string) {
	var sa StepAssertions
	var errors []Failure
	var failures []Failure
	var systemerr, systemout string

	if err := mapstructure.Decode(step, &sa); err != nil {
		return false, []Failure{{Value: fmt.Sprintf("error decoding assertions: %s", err)}}, failures, systemout, systemerr
	}

	if len(sa.Assertions) == 0 && defaultAssertions != nil {
		sa = *defaultAssertions
	}

	isOK := true
	for _, assertion := range sa.Assertions {
		errs, fails := check(assertion, executorResult, l)
		if errs != nil {
			errors = append(errors, *errs)
			isOK = false
		}
		if fails != nil {
			failures = append(failures, *fails)
			isOK = false
		}
	}

	if _, ok := executorResult["result.systemerr"]; ok {
		systemerr = executorResult["result.systemerr"]
	}

	if _, ok := executorResult["result.systemout"]; ok {
		systemout = executorResult["result.systemout"]
	}

	return isOK, errors, failures, systemout, systemerr
}

func check(assertion string, executorResult ExecutorResult, l *log.Entry) (*Failure, *Failure) {
	assert := strings.Split(assertion, " ")
	if len(assert) < 2 {
		return &Failure{Value: fmt.Sprintf("invalid assertion '%s' len:'%d'", assertion, len(assert))}, nil
	}

	actual, ok := executorResult[assert[0]]
	if !ok {
		if assert[1] == "ShouldNotExist" {
			return nil, nil
		}
		return &Failure{Value: fmt.Sprintf("key '%s' does not exist in result of executor: %+v", assert[0], executorResult)}, nil
	} else if assert[1] == "ShouldNotExist" {
		return &Failure{Value: fmt.Sprintf("key '%s' should not exist in result of executor. Value: %+v", assert[0], actual)}, nil
	}

	f, ok := assertMap[assert[1]]
	if !ok {
		return &Failure{Value: fmt.Sprintf("Method not found '%s'", assert[1])}, nil
	}
	args := make([]interface{}, len(assert[2:]))
	for i, v := range assert[2:] { // convert []string to []interface for assertions.func()...
		args[i] = v
	}

	out := f(actual, args...)

	if out != "" {
		prefix := "assertion: " + assertion
		sdump, _ := dump.Sdump(executorResult)
		return nil, &Failure{Value: prefix + "\n" + out + "\n" + sdump}
	}
	return nil, nil
}

// assertMap contains list of assertions func
var assertMap = map[string]func(actual interface{}, expected ...interface{}) string{
	// "ShouldNotExist" see func check
	"ShouldEqual":                  assertions.ShouldEqual,
	"ShouldNotEqual":               assertions.ShouldNotEqual,
	"ShouldAlmostEqual":            assertions.ShouldAlmostEqual,
	"ShouldNotAlmostEqual":         assertions.ShouldNotAlmostEqual,
	"ShouldResemble":               assertions.ShouldResemble,
	"ShouldNotResemble":            assertions.ShouldNotResemble,
	"ShouldPointTo":                assertions.ShouldPointTo,
	"ShouldNotPointTo":             assertions.ShouldNotPointTo,
	"ShouldBeTrue":                 assertions.ShouldBeTrue,
	"ShouldBeFalse":                assertions.ShouldBeFalse,
	"ShouldBeZeroValue":            assertions.ShouldBeZeroValue,
	"ShouldBeGreaterThan":          assertions.ShouldBeGreaterThan,
	"ShouldBeGreaterThanOrEqualTo": assertions.ShouldBeGreaterThanOrEqualTo,
	"ShouldBeLessThan":             assertions.ShouldBeLessThan,
	"ShouldBeLessThanOrEqualTo":    assertions.ShouldBeLessThanOrEqualTo,
	"ShouldBeBetween":              assertions.ShouldBeBetween,
	"ShouldNotBeBetween":           assertions.ShouldNotBeBetween,
	"ShouldBeBetweenOrEqual":       assertions.ShouldBeBetweenOrEqual,
	"ShouldNotBeBetweenOrEqual":    assertions.ShouldNotBeBetweenOrEqual,
	"ShouldContain":                assertions.ShouldContain,
	"ShouldNotContain":             assertions.ShouldNotContain,
	"ShouldContainKey":             assertions.ShouldContainKey,
	"ShouldNotContainKey":          assertions.ShouldNotContainKey,
	"ShouldBeIn":                   assertions.ShouldBeIn,
	"ShouldNotBeIn":                assertions.ShouldNotBeIn,
	"ShouldBeEmpty":                assertions.ShouldBeEmpty,
	"ShouldNotBeEmpty":             assertions.ShouldNotBeEmpty,
	"ShouldHaveLength":             assertions.ShouldHaveLength,
	"ShouldStartWith":              assertions.ShouldStartWith,
	"ShouldNotStartWith":           assertions.ShouldNotStartWith,
	"ShouldEndWith":                assertions.ShouldEndWith,
	"ShouldNotEndWith":             assertions.ShouldNotEndWith,
	"ShouldBeBlank":                assertions.ShouldBeBlank,
	"ShouldNotBeBlank":             assertions.ShouldNotBeBlank,
	"ShouldContainSubstring":       ShouldContainSubstring,
	"ShouldNotContainSubstring":    assertions.ShouldNotContainSubstring,
	"ShouldEqualWithout":           assertions.ShouldEqualWithout,
	"ShouldEqualTrimSpace":         assertions.ShouldEqualTrimSpace,
	"ShouldHappenBefore":           assertions.ShouldHappenBefore,
	"ShouldHappenOnOrBefore":       assertions.ShouldHappenOnOrBefore,
	"ShouldHappenAfter":            assertions.ShouldHappenAfter,
	"ShouldHappenOnOrAfter":        assertions.ShouldHappenOnOrAfter,
	"ShouldHappenBetween":          assertions.ShouldHappenBetween,
	"ShouldHappenOnOrBetween":      assertions.ShouldHappenOnOrBetween,
	"ShouldNotHappenOnOrBetween":   assertions.ShouldNotHappenOnOrBetween,
	"ShouldHappenWithin":           assertions.ShouldHappenWithin,
	"ShouldNotHappenWithin":        assertions.ShouldNotHappenWithin,
	"ShouldBeChronological":        assertions.ShouldBeChronological,
}

// ShouldContainSubstring receives exactly more than 2 string parameters and ensures that the first contains the second as a substring.
func ShouldContainSubstring(actual interface{}, expected ...interface{}) string {
	var arg string
	for _, e := range expected {
		arg = fmt.Sprintf("%s %v", arg, e)
	}
	return assertions.ShouldContainSubstring(actual, arg)
}
<|endoftext|>


<commit_before>package target

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"os/exec"
)

// Create a target for local provisioning.
func NewLocalTarget() *localTarget {
	return &localTarget{}
}

type localTarget struct {
	cachedUser string
}

func (c *localTarget) String() string {
	return "LOCAL"
}

func (c *localTarget) User() string {
	if c.cachedUser == "" {
		out := &bytes.Buffer{}
		err := &bytes.Buffer{}
		cmd := exec.Command("whoami")
		cmd.Stdout = out
		cmd.Stderr = err
		e := cmd.Run()
		if e != nil {
			fmt.Printf("error reading login name: err=%q out=%q e=%q", err.String(), out.String(), e)
			os.Exit(1)
		}
		c.cachedUser = out.String()
	}
	return c.cachedUser
}

func (c *localTarget) Command(cmd string) (ExecCommand, error) {
	return &localCommand{
		command: exec.Command("bash", "-c", cmd),
	}, nil
}

func (c *localTarget) Reset() (e error) {
	return nil
}

type localCommand struct {
	command *exec.Cmd
}

func (c *localCommand) StdoutPipe() (io.Reader, error) {
	return c.command.StdoutPipe()
}

func (c *localCommand) StderrPipe() (io.Reader, error) {
	return c.command.StderrPipe()
}

func (c *localCommand) StdinPipe() (io.WriteCloser, error) {
	return c.command.StdinPipe()
}

func (c *localCommand) SetStdout(w io.Writer) {
	c.command.Stdout = w
}

func (c *localCommand) SetStderr(w io.Writer) {
	c.command.Stderr = w
}

func (c *localCommand) SetStdin(r io.Reader) {
	c.command.Stdin = r
}

func (c *localCommand) Wait() error {
	return c.command.Wait()
}

func (c *localCommand) Start() error {
	return c.command.Start()
}

func (c *localCommand) Run() error {
	return c.command.Run()
}
<commit_msg>refactor getting current user<commit_after>package target

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"strings"
)

// Create a target for local provisioning.
func NewLocalTarget() *localTarget {
	return &localTarget{}
}

type localTarget struct {
	cachedUser string
}

func (c *localTarget) String() string {
	return "LOCAL"
}

func (c *localTarget) User() string {
	if c.cachedUser == "" {
		var err error
		c.cachedUser, err = whoami()
		if err != nil {
			log.Fatal(err.Error())
		}
	}
	return c.cachedUser
}

func whoami() (string, error) {
	u := os.Getenv("$USER")
	if u != "" {
		return u, nil
	}
	out := &bytes.Buffer{}
	err := &bytes.Buffer{}
	cmd := exec.Command("whoami")
	cmd.Stdout = out
	cmd.Stderr = err
	e := cmd.Run()
	if e != nil {
		return "", fmt.Errorf("error reading login name: err=%q out=%q e=%q", err.String(), out.String(), e)
	}
	return strings.TrimSpace(out.String()), nil
}

func (c *localTarget) Command(cmd string) (ExecCommand, error) {
	return &localCommand{
		command: exec.Command("bash", "-c", cmd),
	}, nil
}

func (c *localTarget) Reset() (e error) {
	return nil
}

type localCommand struct {
	command *exec.Cmd
}

func (c *localCommand) StdoutPipe() (io.Reader, error) {
	return c.command.StdoutPipe()
}

func (c *localCommand) StderrPipe() (io.Reader, error) {
	return c.command.StderrPipe()
}

func (c *localCommand) StdinPipe() (io.WriteCloser, error) {
	return c.command.StdinPipe()
}

func (c *localCommand) SetStdout(w io.Writer) {
	c.command.Stdout = w
}

func (c *localCommand) SetStderr(w io.Writer) {
	c.command.Stderr = w
}

func (c *localCommand) SetStdin(r io.Reader) {
	c.command.Stdin = r
}

func (c *localCommand) Wait() error {
	return c.command.Wait()
}

func (c *localCommand) Start() error {
	return c.command.Start()
}

func (c *localCommand) Run() error {
	return c.command.Run()
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/csv"
  "fmt"
  "strings"
  "io/ioutil"
  "io"
  "log"
)

type Version struct {
  Id []string
  Text []string
  Latin []string
  Greek []string
  Other []string
  Title []string
}

func main() {
  var output Version
  data, err := ioutil.ReadFile("data/pg.Vol.-1.coo.31924054872803_ocr.csv")
  if err != nil {
    fmt.Println(err)
  }
  str := string(data)
  reader := csv.NewReader(strings.NewReader(str))
  reader.Comma = '#'
  reader.LazyQuotes = true

	for {
		line, error := reader.Read()
		if error == io.EOF {
			break
		} else if error != nil {
			log.Fatal(error)
		}
    output.Id = append(output.Id, line[0])
    output.Text = append(output.Text, line[1])
    output.Latin = append(output.Latin, line[2])
    output.Greek = append(output.Greek, line[3])
    output.Other = append(output.Other, line[4])
    output.Title = append(output.Title, line[5])
	}
  fmt.Println(output.Title)
}
<commit_msg>match<commit_after>package main

import (
	"encoding/csv"
  "fmt"
  "strings"
  "io/ioutil"
  "io"
  "log"
)

type Version struct {
  Id []string
  Text []string
  Latin []string
  Greek []string
  Other []string
  Title []string
}

func loadCSV(s string) Version{
  var output Version
  data, err := ioutil.ReadFile(s)
  if err != nil {
    fmt.Println(err)
  }
  str := string(data)
  reader := csv.NewReader(strings.NewReader(str))
  reader.Comma = '#'
  reader.LazyQuotes = true

	for {
		line, error := reader.Read()
		if error == io.EOF {
			break
		} else if error != nil {
			log.Fatal(error)
		}
    output.Id = append(output.Id, line[0])
    output.Text = append(output.Text, line[1])
    output.Latin = append(output.Latin, line[2])
    output.Greek = append(output.Greek, line[3])
    output.Other = append(output.Other, line[4])
    output.Title = append(output.Title, line[5])
	}
  return output
}

func main() {
  output := loadCSV("data/pg.Vol.-1.coo.31924054872803_ocr.csv")
  output2 := loadCSV("data/pg.Vol.-1.hvd.32044015466733_ocr.csv")
  fmt.Println(output.Id[2])
  fmt.Println(output2.Id[2])
}
<|endoftext|>


<commit_before>package blobstore

import (
	"bytes"
	"io"
)

// Structure used to generate static file blobs
type FileBlobWriter struct {

	// Buffer for storing data before we can hash it
	buffer bytes.Buffer

	// Storage object
	Storage BlobStorage

	// List of partial file blobs
	partialBids, partialKeys []string

	// Overall number of bytes written so far
	totalBytes int64
}

// Performing a write operation on the file blob
func (f *FileBlobWriter) Write(p []byte) (n int, err error) {

	bufferSpaceLeft := maxSimpleFileDataSize - f.buffer.Len()
	written := 0
	for len(p) > 0 {

		// Let's see how much can we chop this time
		partialSize := len(p)
		if partialSize > bufferSpaceLeft {
			partialSize = bufferSpaceLeft
		}

		// Chop off the next part
		f.buffer.Write(p[:partialSize])
		p = p[partialSize:]
		bufferSpaceLeft -= partialSize
		written += partialSize

		// Check out if we should emit next partial buffer
		if bufferSpaceLeft <= 0 {
			if err := f.finalizePartialBuffer(); err != nil {
				f.Cancel()
				return 0, err
			}
			bufferSpaceLeft = maxSimpleFileDataSize
		}
	}
	return written, nil
}

// Write the current content of internal buffer into a blob,
// save it's id and key in a list of partial blobs
func (f *FileBlobWriter) finalizePartialBuffer() error {

	// Create the header
	var hdr bytes.Buffer
	hdr.WriteByte(blobTypeSimpleStaticFile)

	// Generate the blob
	readerGen := func() io.Reader {
		headerReader := bytes.NewReader(hdr.Bytes())
		contentReader := bytes.NewReader(f.buffer.Bytes())
		return io.MultiReader(headerReader, contentReader)
	}
	bid, key, err := createHashValidatedBlobFromReaderGenerator(readerGen, f.Storage)
	if err != nil {
		return err
	}

	// Queue the blob on a list of partial blobs
	f.addPartialBlob(bid, key)

	// Increase the counter of bytes thrown out so far
	f.totalBytes += int64(f.buffer.Len())

	// Cleanup
	f.buffer.Reset()

	return nil
}

// Save bid and key into a list of partial blobs
func (f *FileBlobWriter) addPartialBlob(bid, key string) {
	f.partialBids = append(f.partialBids, bid)
	f.partialKeys = append(f.partialKeys, key)
}

// Finalize the generation of this file blob
func (f *FileBlobWriter) Finalize() (bid string, key string, err error) {

	// Throw out the last partial if needed
	if f.buffer.Len() > 0 || len(f.partialBids) == 0 {
		if err := f.finalizePartialBuffer(); err != nil {
			f.Cancel()
			return "", "", err
		}
	}

	// If there's only one partial in the list, we don't have to create
	// any split file blobs
	if len(f.partialBids) == 1 {
		return f.partialBids[0], f.partialKeys[0], nil
	}

	// Create split file blob
	return f.finalizeSplitFile()
}

// Finalize blob generation in case we've created split file blob
func (f *FileBlobWriter) finalizeSplitFile() (bid string, key string, err error) {
	var b bytes.Buffer

	// Blob type id
	b.WriteByte(blobTypeSplitStaticFile)

	// Total file size
	serializeInt(f.totalBytes, &b)

	// Number of partial blobs
	serializeInt(int64(len(f.partialBids)), &b)

	// Partial blobs list
	for i, bid := range f.partialBids {
		serializeString(bid, &b)
		serializeString(f.partialKeys[i], &b)
	}

	// Write it all to the storage
	return createHashValidatedBlobFromReaderGenerator(
		func() io.Reader { return bytes.NewReader(b.Bytes()) },
		f.Storage)
}

// Cancel the generation of file blob.
//
// Note that if there were blobs generated so far, they won't be removed.
// The current implementation allows such garbage favouring the simplicity
// of implementation.
func (f *FileBlobWriter) Cancel() {

	f.partialBids = nil
	f.partialKeys = nil
	f.partialDup = nil
	f.buffer.Reset()
	f.totalBytes = 0
}
<commit_msg>Compilation fix<commit_after>package blobstore

import (
	"bytes"
	"io"
)

// Structure used to generate static file blobs
type FileBlobWriter struct {

	// Buffer for storing data before we can hash it
	buffer bytes.Buffer

	// Storage object
	Storage BlobStorage

	// List of partial file blobs
	partialBids, partialKeys []string

	// Overall number of bytes written so far
	totalBytes int64
}

// Performing a write operation on the file blob
func (f *FileBlobWriter) Write(p []byte) (n int, err error) {

	bufferSpaceLeft := maxSimpleFileDataSize - f.buffer.Len()
	written := 0
	for len(p) > 0 {

		// Let's see how much can we chop this time
		partialSize := len(p)
		if partialSize > bufferSpaceLeft {
			partialSize = bufferSpaceLeft
		}

		// Chop off the next part
		f.buffer.Write(p[:partialSize])
		p = p[partialSize:]
		bufferSpaceLeft -= partialSize
		written += partialSize

		// Check out if we should emit next partial buffer
		if bufferSpaceLeft <= 0 {
			if err := f.finalizePartialBuffer(); err != nil {
				f.Cancel()
				return 0, err
			}
			bufferSpaceLeft = maxSimpleFileDataSize
		}
	}
	return written, nil
}

// Write the current content of internal buffer into a blob,
// save it's id and key in a list of partial blobs
func (f *FileBlobWriter) finalizePartialBuffer() error {

	// Create the header
	var hdr bytes.Buffer
	hdr.WriteByte(blobTypeSimpleStaticFile)

	// Generate the blob
	readerGen := func() io.Reader {
		headerReader := bytes.NewReader(hdr.Bytes())
		contentReader := bytes.NewReader(f.buffer.Bytes())
		return io.MultiReader(headerReader, contentReader)
	}
	bid, key, err := createHashValidatedBlobFromReaderGenerator(readerGen, f.Storage)
	if err != nil {
		return err
	}

	// Queue the blob on a list of partial blobs
	f.addPartialBlob(bid, key)

	// Increase the counter of bytes thrown out so far
	f.totalBytes += int64(f.buffer.Len())

	// Cleanup
	f.buffer.Reset()

	return nil
}

// Save bid and key into a list of partial blobs
func (f *FileBlobWriter) addPartialBlob(bid, key string) {
	f.partialBids = append(f.partialBids, bid)
	f.partialKeys = append(f.partialKeys, key)
}

// Finalize the generation of this file blob
func (f *FileBlobWriter) Finalize() (bid string, key string, err error) {

	// Throw out the last partial if needed
	if f.buffer.Len() > 0 || len(f.partialBids) == 0 {
		if err := f.finalizePartialBuffer(); err != nil {
			f.Cancel()
			return "", "", err
		}
	}

	// If there's only one partial in the list, we don't have to create
	// any split file blobs
	if len(f.partialBids) == 1 {
		return f.partialBids[0], f.partialKeys[0], nil
	}

	// Create split file blob
	return f.finalizeSplitFile()
}

// Finalize blob generation in case we've created split file blob
func (f *FileBlobWriter) finalizeSplitFile() (bid string, key string, err error) {
	var b bytes.Buffer

	// Blob type id
	b.WriteByte(blobTypeSplitStaticFile)

	// Total file size
	serializeInt(f.totalBytes, &b)

	// Number of partial blobs
	serializeInt(int64(len(f.partialBids)), &b)

	// Partial blobs list
	for i, bid := range f.partialBids {
		serializeString(bid, &b)
		serializeString(f.partialKeys[i], &b)
	}

	// Write it all to the storage
	return createHashValidatedBlobFromReaderGenerator(
		func() io.Reader { return bytes.NewReader(b.Bytes()) },
		f.Storage)
}

// Cancel the generation of file blob.
//
// Note that if there were blobs generated so far, they won't be removed.
// The current implementation allows such garbage favouring the simplicity
// of implementation.
func (f *FileBlobWriter) Cancel() {

	f.partialBids = nil
	f.partialKeys = nil
	f.buffer.Reset()
	f.totalBytes = 0
}
<|endoftext|>