<commit_before>package beego_gorelic

import (
	"strings"
	"time"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/context"
	metrics "github.com/yvasiyarov/go-metrics"
	"github.com/yvasiyarov/gorelic"
)

const (
	SEPARATOR = "-"
)

var agent *gorelic.Agent

func InitNewRelicTimer(ctx *context.Context) {
	startTime := time.Now()
	ctx.Input.SetData("newrelic_timer", startTime)
}
func ReportMetricsToNewrelic(ctx *context.Context) {
	startTimeInterface := ctx.Input.GetData("newrelic_timer")
	if startTime, ok := startTimeInterface.(time.Time); ok {
		agent.HTTPTimer.UpdateSince(startTime)
	}
}

func InitNewrelicAgent() {
	var appname string
	license := beego.AppConfig.String("NewrelicLicense")
	if license == "" {
		beego.Warn("Please specify NewRelic license in the application config: NewrelicLicense=7bceac019c7dcafae1ef95be3e3a3ff8866de245")
		return
	}

	agent = gorelic.NewAgent()
	agent.NewrelicLicense = license

	agent.HTTPTimer = metrics.NewTimer()
	agent.CollectHTTPStat = true

	if beego.BConfig.RunMode == "dev" {
		agent.Verbose = true
	}
	if verbose, err := beego.AppConfig.Bool("NewrelicVerbose"); err == nil {
		agent.Verbose = verbose
	}
	// Checking if New Relic appname overrides the default appname
	appname = beego.AppConfig.String("NewrelicAppname")
	if appname == "" {
		// If not set revert to using beego appname as default
		appname = beego.AppConfig.String("appname")
	}
	nameParts := []string{appname}

	switch strings.ToLower(beego.AppConfig.String("NewrelicAppnameRunmode")) {
	case "append":
		nameParts = append(nameParts, beego.BConfig.RunMode)

	case "prepend":
		nameParts = append([]string{beego.BConfig.RunMode}, nameParts...)
	}
	agent.NewrelicName = strings.Join(nameParts, SEPARATOR)
	agent.Run()

	beego.InsertFilter("*", beego.BeforeRouter, InitNewRelicTimer, false)
	beego.InsertFilter("*", beego.FinishRouter, ReportMetricsToNewrelic, false)

	beego.Info("NewRelic agent started")
}
<commit_msg>only using lower case config var names<commit_after>package beego_gorelic

import (
	"strings"
	"time"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/context"
	metrics "github.com/yvasiyarov/go-metrics"
	"github.com/yvasiyarov/gorelic"
)

const (
	SEPARATOR = "-"
)

var agent *gorelic.Agent

func InitNewRelicTimer(ctx *context.Context) {
	startTime := time.Now()
	ctx.Input.SetData("newrelic_timer", startTime)
}
func ReportMetricsToNewrelic(ctx *context.Context) {
	startTimeInterface := ctx.Input.GetData("newrelic_timer")
	if startTime, ok := startTimeInterface.(time.Time); ok {
		agent.HTTPTimer.UpdateSince(startTime)
	}
}

func InitNewrelicAgent() {
	var appname string
	license := beego.AppConfig.String("newrelicLicense")
	if license == "" {
		beego.Warn("Please specify NewRelic license in the application config: NewrelicLicense=7bceac019c7dcafae1ef95be3e3a3ff8866de245")
		return
	}

	agent = gorelic.NewAgent()
	agent.NewrelicLicense = license

	agent.HTTPTimer = metrics.NewTimer()
	agent.CollectHTTPStat = true

	if beego.BConfig.RunMode == "dev" {
		agent.Verbose = true
	}
	if verbose, err := beego.AppConfig.Bool("newrelicVerbose"); err == nil {
		agent.Verbose = verbose
	}
	// Checking if New Relic appname overrides the default appname
	appname = beego.AppConfig.String("newrelicAppname")
	if appname == "" {
		// If not set revert to using beego appname as default
		appname = beego.AppConfig.String("appname")
	}
	nameParts := []string{appname}

	switch strings.ToLower(beego.AppConfig.String("newrelicAppnameRunmode")) {
	case "append":
		nameParts = append(nameParts, beego.BConfig.RunMode)

	case "prepend":
		nameParts = append([]string{beego.BConfig.RunMode}, nameParts...)
	}
	agent.NewrelicName = strings.Join(nameParts, SEPARATOR)
	agent.Run()

	beego.InsertFilter("*", beego.BeforeRouter, InitNewRelicTimer, false)
	beego.InsertFilter("*", beego.FinishRouter, ReportMetricsToNewrelic, false)

	beego.Info("NewRelic agent started")
}
<|endoftext|>


<commit_before>/*
go-start is a high level web-framework for Go,
like Django for Python or Rails for Ruby.

Source: https://github.com/ungerik/go-start/

Documentation: http://go-start.org/pkg/go-start/gostart/

Note: Don't use Go on 32 bit systems in production, it has severe memory leaks.
(If the documentation URL above doesn't work, then godoc -html has has crashed because of that issue)

Intro:
======

Features:

* HTML views can be defined in Go syntax
* Optional template system
* HTML5 Boilerplate page template (Mustache template, will be changed to Go v1 template)
* Unified data model for forms and databases
* Data models are simple Go structs
* MongoDB as default database
* User management/authentication out of the box
* Additional packages for
	* Email (Google Mail defaults): http://github.com/ungerik/go-mail
	* Gravatar: http://github.com/ungerik/go-gravatar
	* RSS parsing: http://github.com/ungerik/go-rss
	* Amiando event management: http://github.com/ungerik/go-amiando
		(used by http://startuplive.in)

Views:
======

The philosophy for creating HTML views is (unlike Rails/Django) that you should
not have to learn yet another language to be able to write templates.
There are several very simple template languages out there that reduce program
code like logic within the template, but it’s still yet another syntax to learn.

In go-start the HTML structure of a page is represented by a structure of
type safe Go objects.
It should feel like writing HTML but using the syntax of Go.
And no, it has nothing to do with the mess of intertwined markup and code in PHP.

Example of a static view:

	view := Views{
		DIV("myclass",
			H1("Example HTML structure"),
			P("This is a paragraph"),
			P(
				HTML("Some unescaped HTML:
"),
				Printf("The number of the beast: %d", 666),
				Escape("Will be escaped: 666 < 999"),
			),
			A_blank("http://go-lang.org", "A very simple link"),
		),
		HR(),
		PRE("	<- pre formated text, followed by a list:"),
		UL("red", "green", "blue"),
		&Template{
			Filename: "mytemplate.html",
			GetContext: func(requestContext *Context) (interface{}, error) {
				return map[string]string{"Key": "Value"}, nil
			},
		},
	}

Example of a dynamic view:

	view := DynamicView(
		func(context *Context) (view View, err error) {
			var names []string
			i := models.Users.Sort("Name.First").Sort("Name.Last").Iterator();
			for doc := i.Next(); doc != nil; doc = i.Next() {
				names = append(names, doc.(*models.User).Name.String())
			}
			if i.Err() != nil {
				return nil, i.Err()
			}
			return &List{	// List = higher level abstraction, UL() = shortcut
				Class: "my-ol",
				Ordered: true,
				Model: EscapeStringsListModel(names),
			}, nil
		},
	)

Beside DynamicView there is also a ModelView. It takes a model.Iterator
and creates a dynamic view for every iterated data item:

	view := &ModelView{
		GetModelIterator: func(context *Context) model.Iterator {
			return models.Users.Sort("Name.First").Sort("Name.Last").Iterator()
		},
		GetModelView: func(model interface{}, context *Context) (view View, err error) {
			user := model.(*models.User)
			return PrintfEscape("%s, ", user.Name), nil
		},
	}

Pages and URLs:
===============

	Homepage := &Page{
		OnPreRender: func(page *Page, context *Context) (err error) {
			context.Data = &PerPageData{...} // Set global page data at request context
		},
		WriteTitle: func(context *Context, writer io.Writer) (err error) {
			writer.Write([]byte(context.Data.(*PerPageData).DynamicTitle))
			return nil
		},
		CSS:          HomepageCSS,
		WriteHeader:  RSS("go-start.org RSS Feed", &RssFeed)
		WriteScripts: PageWriters(
			Config.Page.DefaultWriteScripts,
			JQuery,   // jQuery/UI is built-in
			JQueryUI,
			JQueryUIAutocompleteFromURL(".select-username", IndirectURL(&API_Usernames), 2),
			GoogleAnalytics(GoogleAnalyticsID), // Google Analytics is built-in
		)
		Content: Views{},
	}

	Admin_Auth := NewBasicAuth("go-start.org", "admin", "password123")

	func Paths() *ViewPath {
		return &ViewPath{View: Homepage, Sub: []ViewPath{                           // /
			{Name: "style.css", View: HomepageCSS},                             // /style.css
			{Name: "feed", View: RssFeed},                                      // /feed/
			{Name: "admin", View: Admin, Auth: Admin_Auth, Sub: []ViewPath{     // /admin/
				{Name: "user", Args: 1, View: Admin_User, Auth: Admin_Auth}, // /admin/user/<USER_ID>/
			}},
			{Name: "api", Sub: []ViewPath{                                      // 404 because no view defined
				{Name: "users.json", View: API_Usernames},                  // /api/users.json
			}},
		}
	}

	view.Init("go-start.org", CookieSecret, "pkg/myproject", "pkg/gostart") // Set site name, cookie secret and static paths
	view.Config.RedirectSubdomains = []string{"www"}     // Redirect from www.
	view.Config.Page.DefaultMetaViewport = "width=960px" // Page width for mobile devices
	view.RunConfigFile(Paths(), "run.config")            // Run server with path structure and values from config file

Models:
=======

Data is abstacted as models. The same model abstraction and data validation is
used for HTML forms and for databases. So a model can be loaded from a database,
displayed as an HTML form and saved back to the database after submit.
This is not always a good practice, but it shows how easy things can be.

A model is a simple Go struct that uses gostart/model types as struct members.
Custom model wide validation is done by adding a Validate() method to the
struct type:

	type SignupFormModel struct {
		Email     model.Email    `gostart:"required"`
		Password1 model.Password `gostart:"required|label=Password|minlen=6"`
		Password2 model.Password `gostart:"label=Repeat password"`
	}

	func (self *SignupFormModel) Validate(metaData *model.MetaData) []*model.ValidationError {
		if self.Password1 != self.Password2 {
			return model.NewValidationErrors(os.NewError("Passwords don't match"), metaData)
		}
	errors := NoValidationErrors
		return model.NoValidationErrors
	}

Here is how a HTML form is created that displays input fields for the SignupFormModel:

	form := &Form{
		ButtonText: "Signup",
		FormID:     "user_signup",
		GetModel: func(form *Form, context *Context) (interface{}, error) {
			return &SignupFormModel{}, nil
		},
		OnSubmit: func(form *Form, formModel interface{}, context *Context) (err error) {
			m := formModel.(*SignupFormModel)
			// ... create user in db and send confirmation email ...
			return err
		},
	}

MongoDB is the default database of go-start utilizing Gustavo Niemeyer's
great lib mgo (http://labix.org/mgo).

Mongo collections and queries are encapsulated to make them compatible with the
go-start data model concept, and a little bit easier to use.

Example of a collection and document struct:

	var ExampleDocs *mongo.Collection = mongo.NewCollection("exampledocs", (*ExampleDoc)(nil))

	type ExampleDoc struct {
		mongo.DocumentBase `bson:",inline"`                 // Give it a Mongo ID
		Person             mongo.Ref  `gostart:"to=people"` // Mongo ID ref to a document in "people" collection
		LongerText         model.Text `gostart:"rows=5|cols=80|maxlen=400"`
		Integer            model.Int  `gostart:"min=1|max=100"`
		Email              model.Email    // Normalization + special treament in forms
		PhoneNumber        model.Phone    // Normalization + special treament in forms
		Password           model.Password // Hashed + special treament in forms
		SubDoc             struct {
			Day       model.Date
			Drinks    []mongo.Choice `gostart:"options=Beer,Wine,Water"` // Mongo array of strings
			RealFloat model.Float    `gostart:"valid" // Must be a real float value, not NaN or Inf
		}
	}

Example query:

	i := models.Users.Filter("Name.Last", "Smith").Sort("Name.First").Iterator();
	for doc := i.Next(); doc != nil; doc = i.Next() {
		user := doc.(*models.User)
		// ...
	}
	// Err() returns any error after Next() returned nil:
	if i.Err() != nil {
		panic(i.Err())
	}

A new mongo.Document is always created by the corresponding collection object
to initialize it with meta information about its collection.
This way it is possible to implement Save() or Remove() methods for the document.

Example for creating, modifying and saving a document:

	user := models.Users.NewDocument().(*models.User)

	user.Name.First.Set("Erik")
	user.Name.Last.Set("Unger")

	err := user.Save()

*/
package gostart

// Include all packages for build and install
import (
	_ "github.com/ungerik/go-amiando"
	_ "github.com/ungerik/go-gravatar"
	_ "github.com/ungerik/go-mail"
	_ "github.com/ungerik/go-rss"
	_ "github.com/ungerik/goconfig"
	_ "github.com/ungerik/mustache.go"
	_ "github.com/ungerik/web.go"
	_ "launchpad.net/mgo"

	_ "github.com/ungerik/go-start/debug"
	_ "github.com/ungerik/go-start/errs"
	_ "github.com/ungerik/go-start/i18n"
	_ "github.com/ungerik/go-start/media"
	_ "github.com/ungerik/go-start/model"
	_ "github.com/ungerik/go-start/modelext"
	_ "github.com/ungerik/go-start/mongo"
	_ "github.com/ungerik/go-start/mongomedia"
	_ "github.com/ungerik/go-start/templatesystem"
	_ "github.com/ungerik/go-start/user"
	_ "github.com/ungerik/go-start/utils"
	_ "github.com/ungerik/go-start/view"
)
<commit_msg>doc update<commit_after>/*
go-start is a high level web-framework for Go,
like Django for Python or Rails for Ruby.

Source: https://github.com/ungerik/go-start/

Documentation: http://go-start.org/pkg/go-start/gostart/

Note: Don't use Go on 32 bit systems in production, it has severe memory leaks.
(If the documentation URL above doesn't work, then godoc -html has has crashed because of that issue)

## Intro:

Features:

* HTML views can be defined in Go syntax
* Optional template system
* HTML5 Boilerplate page template (Mustache template, will be changed to Go v1 template)
* Unified data model for forms and databases
* Data models are simple Go structs
* MongoDB as default database
* User management/authentication out of the box
* Additional packages for
	* Email (Google Mail defaults): http://github.com/ungerik/go-mail
	* Gravatar: http://github.com/ungerik/go-gravatar
	* RSS parsing: http://github.com/ungerik/go-rss
	* Amiando event management: http://github.com/ungerik/go-amiando
		(used by http://startuplive.in)

## Views:

The philosophy for creating HTML views is (unlike Rails/Django) that you should
not have to learn yet another language to be able to write templates.
There are several very simple template languages out there that reduce program
code like logic within the template, but it’s still yet another syntax to learn.

In go-start the HTML structure of a page is represented by a structure of
type safe Go objects.
It should feel like writing HTML but using the syntax of Go.
And no, it has nothing to do with the mess of intertwined markup and code in PHP.

Example of a static view:

	view := Views{
		DIV("myclass",
			H1("Example HTML structure"),
			P("This is a paragraph"),
			P(
				HTML("Some unescaped HTML:
"),
				Printf("The number of the beast: %d", 666),
				Escape("Will be escaped: 666 < 999"),
			),
			A_blank("http://go-lang.org", "A very simple link"),
		),
		HR(),
		PRE("	<- pre formated text, followed by a list:"),
		UL("red", "green", "blue"),
		&Template{
			Filename: "mytemplate.html",
			GetContext: func(requestContext *Context) (interface{}, error) {
				return map[string]string{"Key": "Value"}, nil
			},
		},
	}

Example of a dynamic view:

	view := DynamicView(
		func(context *Context) (view View, err error) {
			var names []string
			i := models.Users.Sort("Name.First").Sort("Name.Last").Iterator();
			for doc := i.Next(); doc != nil; doc = i.Next() {
				names = append(names, doc.(*models.User).Name.String())
			}
			if i.Err() != nil {
				return nil, i.Err()
			}
			return &List{	// List = higher level abstraction, UL() = shortcut
				Class: "my-ol",
				Ordered: true,
				Model: EscapeStringsListModel(names),
			}, nil
		},
	)

Beside DynamicView there is also a ModelView. It takes a model.Iterator
and creates a dynamic view for every iterated data item:

	view := &ModelView{
		GetModelIterator: func(context *Context) model.Iterator {
			return models.Users.Sort("Name.First").Sort("Name.Last").Iterator()
		},
		GetModelView: func(model interface{}, context *Context) (view View, err error) {
			user := model.(*models.User)
			return PrintfEscape("%s, ", user.Name), nil
		},
	}

## Pages and URLs:

	Homepage := &Page{
		OnPreRender: func(page *Page, context *Context) (err error) {
			context.Data = &PerPageData{...} // Set global page data at request context
		},
		WriteTitle: func(context *Context, writer io.Writer) (err error) {
			writer.Write([]byte(context.Data.(*PerPageData).DynamicTitle))
			return nil
		},
		CSS:          HomepageCSS,
		WriteHeader:  RSS("go-start.org RSS Feed", &RssFeed)
		WriteScripts: PageWriters(
			Config.Page.DefaultWriteScripts,
			JQuery,   // jQuery/UI is built-in
			JQueryUI,
			JQueryUIAutocompleteFromURL(".select-username", IndirectURL(&API_Usernames), 2),
			GoogleAnalytics(GoogleAnalyticsID), // Google Analytics is built-in
		)
		Content: Views{},
	}

	Admin_Auth := NewBasicAuth("go-start.org", "admin", "password123")

	func Paths() *ViewPath {
		return &ViewPath{View: Homepage, Sub: []ViewPath{                           // /
			{Name: "style.css", View: HomepageCSS},                             // /style.css
			{Name: "feed", View: RssFeed},                                      // /feed/
			{Name: "admin", View: Admin, Auth: Admin_Auth, Sub: []ViewPath{     // /admin/
				{Name: "user", Args: 1, View: Admin_User, Auth: Admin_Auth}, // /admin/user/<USER_ID>/
			}},
			{Name: "api", Sub: []ViewPath{                                      // 404 because no view defined
				{Name: "users.json", View: API_Usernames},                  // /api/users.json
			}},
		}
	}

	view.Init("go-start.org", CookieSecret, "pkg/myproject", "pkg/gostart") // Set site name, cookie secret and static paths
	view.Config.RedirectSubdomains = []string{"www"}     // Redirect from www.
	view.Config.Page.DefaultMetaViewport = "width=960px" // Page width for mobile devices
	view.RunConfigFile(Paths(), "run.config")            // Run server with path structure and values from config file

## Models:

Data is abstacted as models. The same model abstraction and data validation is
used for HTML forms and for databases. So a model can be loaded from a database,
displayed as an HTML form and saved back to the database after submit.
This is not always a good practice, but it shows how easy things can be.

A model is a simple Go struct that uses gostart/model types as struct members.
Custom model wide validation is done by adding a Validate() method to the
struct type:

	type SignupFormModel struct {
		Email     model.Email    `gostart:"required"`
		Password1 model.Password `gostart:"required|label=Password|minlen=6"`
		Password2 model.Password `gostart:"label=Repeat password"`
	}

	func (self *SignupFormModel) Validate(metaData *model.MetaData) []*model.ValidationError {
		if self.Password1 != self.Password2 {
			return model.NewValidationErrors(os.NewError("Passwords don't match"), metaData)
		}
	errors := NoValidationErrors
		return model.NoValidationErrors
	}

Here is how a HTML form is created that displays input fields for the SignupFormModel:

	form := &Form{
		ButtonText: "Signup",
		FormID:     "user_signup",
		GetModel: func(form *Form, context *Context) (interface{}, error) {
			return &SignupFormModel{}, nil
		},
		OnSubmit: func(form *Form, formModel interface{}, context *Context) (err error) {
			m := formModel.(*SignupFormModel)
			// ... create user in db and send confirmation email ...
			return err
		},
	}

MongoDB is the default database of go-start utilizing Gustavo Niemeyer's
great lib mgo (http://labix.org/mgo).

Mongo collections and queries are encapsulated to make them compatible with the
go-start data model concept, and a little bit easier to use.

Example of a collection and document struct:

	var ExampleDocs *mongo.Collection = mongo.NewCollection("exampledocs", (*ExampleDoc)(nil))

	type ExampleDoc struct {
		mongo.DocumentBase `bson:",inline"`                 // Give it a Mongo ID
		Person             mongo.Ref  `gostart:"to=people"` // Mongo ID ref to a document in "people" collection
		LongerText         model.Text `gostart:"rows=5|cols=80|maxlen=400"`
		Integer            model.Int  `gostart:"min=1|max=100"`
		Email              model.Email    // Normalization + special treament in forms
		PhoneNumber        model.Phone    // Normalization + special treament in forms
		Password           model.Password // Hashed + special treament in forms
		SubDoc             struct {
			Day       model.Date
			Drinks    []mongo.Choice `gostart:"options=Beer,Wine,Water"` // Mongo array of strings
			RealFloat model.Float    `gostart:"valid" // Must be a real float value, not NaN or Inf
		}
	}

Example query:

	i := models.Users.Filter("Name.Last", "Smith").Sort("Name.First").Iterator();
	for doc := i.Next(); doc != nil; doc = i.Next() {
		user := doc.(*models.User)
		// ...
	}
	// Err() returns any error after Next() returned nil:
	if i.Err() != nil {
		panic(i.Err())
	}

A new mongo.Document is always created by the corresponding collection object
to initialize it with meta information about its collection.
This way it is possible to implement Save() or Remove() methods for the document.

Example for creating, modifying and saving a document:

	user := models.Users.NewDocument().(*models.User)

	user.Name.First.Set("Erik")
	user.Name.Last.Set("Unger")

	err := user.Save()

*/
package gostart

// Include all packages for build and install
import (
	_ "github.com/ungerik/go-amiando"
	_ "github.com/ungerik/go-gravatar"
	_ "github.com/ungerik/go-mail"
	_ "github.com/ungerik/go-rss"
	_ "github.com/ungerik/goconfig"
	_ "github.com/ungerik/mustache.go"
	_ "github.com/ungerik/web.go"
	_ "launchpad.net/mgo"

	_ "github.com/ungerik/go-start/debug"
	_ "github.com/ungerik/go-start/errs"
	_ "github.com/ungerik/go-start/i18n"
	_ "github.com/ungerik/go-start/media"
	_ "github.com/ungerik/go-start/model"
	_ "github.com/ungerik/go-start/modelext"
	_ "github.com/ungerik/go-start/mongo"
	_ "github.com/ungerik/go-start/mongomedia"
	_ "github.com/ungerik/go-start/templatesystem"
	_ "github.com/ungerik/go-start/user"
	_ "github.com/ungerik/go-start/utils"
	_ "github.com/ungerik/go-start/view"
)
<|endoftext|>


<commit_before>package participle

import (
	"fmt"
	"reflect"
	"text/scanner"

	"github.com/alecthomas/participle/lexer"
)

type generatorContext struct {
	lexer.Definition
	typeNodes    map[reflect.Type]node
	symbolsToIDs map[rune]string
}

func newGeneratorContext(lex lexer.Definition) *generatorContext {
	return &generatorContext{
		Definition:   lex,
		typeNodes:    map[reflect.Type]node{},
		symbolsToIDs: lexer.SymbolsByRune(lex),
	}
}

// Takes a type and builds a tree of nodes out of it.
func (g *generatorContext) parseType(t reflect.Type) (_ node, returnedError error) {
	rt := t
	t = indirectType(t)
	if n, ok := g.typeNodes[t]; ok {
		return n, nil
	}
	if rt.Implements(parseableType) {
		return &parseable{rt.Elem()}, nil
	}
	if reflect.PtrTo(rt).Implements(parseableType) {
		return &parseable{rt}, nil
	}
	switch t.Kind() {
	case reflect.Slice, reflect.Ptr:
		t = indirectType(t.Elem())
		if t.Kind() != reflect.Struct {
			return nil, fmt.Errorf("expected a struct but got %T", t)
		}
		fallthrough

	case reflect.Struct:
		slexer, err := lexStruct(t)
		if err != nil {
			return nil, err
		}
		out := &strct{typ: t}
		g.typeNodes[t] = out // Ensure we avoid infinite recursion.
		if slexer.NumField() == 0 {
			return nil, fmt.Errorf("can not parse into empty struct %s", t)
		}
		defer decorate(&returnedError, func() string { return slexer.Field().Name })
		e, err := g.parseDisjunction(slexer)
		if err != nil {
			return nil, err
		}
		if e == nil {
			return nil, fmt.Errorf("no grammar found in %s", t)
		}
		if token, _ := slexer.Peek(); !token.EOF() {
			return nil, fmt.Errorf("unexpected input %q", token.Value)
		}
		out.expr = e
		return out, nil
	}
	return nil, fmt.Errorf("%s should be a struct or should implement the Parseable interface", t)
}

func (g *generatorContext) parseDisjunction(slexer *structLexer) (node, error) {
	out := &disjunction{}
	for {
		n, err := g.parseSequence(slexer)
		if err != nil {
			return nil, err
		}
		out.nodes = append(out.nodes, n)
		if token, _ := slexer.Peek(); token.Type != '|' {
			break
		}
		_, err = slexer.Next() // |
		if err != nil {
			return nil, err
		}
	}
	if len(out.nodes) == 1 {
		return out.nodes[0], nil
	}
	return out, nil
}

func (g *generatorContext) parseSequence(slexer *structLexer) (node, error) {
	head := &sequence{}
	cursor := head
loop:
	for {
		if token, err := slexer.Peek(); err != nil {
			return nil, err
		} else if token.Type == lexer.EOF {
			break loop
		}
		term, err := g.parseTerm(slexer)
		if err != nil {
			return nil, err
		}
		if term == nil {
			break loop
		}
		if cursor.node == nil {
			cursor.head = true
			cursor.node = term
		} else {
			cursor.next = &sequence{node: term}
			cursor = cursor.next
		}

		// An optional or repetition result in some magic.
		switch n := term.(type) {
		case *optional:
			n.next, err = g.parseSequence(slexer)
			if err != nil {
				return nil, err
			}
			break loop
		case *repetition:
			n.next, err = g.parseSequence(slexer)
			if err != nil {
				return nil, err
			}
			break loop
		}
	}
	if head.node == nil {
		return nil, nil
	}
	if head.next == nil {
		return head.node, nil
	}
	return head, nil
}

func (g *generatorContext) parseTerm(slexer *structLexer) (node, error) {
	r, err := slexer.Peek()
	if err != nil {
		return nil, err
	}
	switch r.Type {
	case '@':
		return g.parseCapture(slexer)
	case scanner.String, scanner.RawString, scanner.Char:
		return g.parseLiteral(slexer)
	case '[':
		return g.parseOptional(slexer)
	case '{':
		return g.parseRepetition(slexer)
	case '(':
		return g.parseGroup(slexer)
	case scanner.Ident:
		return g.parseReference(slexer)
	case lexer.EOF:
		_, _ = slexer.Next()
		return nil, nil
	default:
		return nil, nil
	}
}

// @<expression> captures <expression> into the current field.
func (g *generatorContext) parseCapture(slexer *structLexer) (node, error) {
	_, _ = slexer.Next()
	token, err := slexer.Peek()
	if err != nil {
		return nil, err
	}
	field := slexer.Field()
	if token.Type == '@' {
		_, _ = slexer.Next()
		n, err := g.parseType(field.Type)
		if err != nil {
			return nil, err
		}
		return &capture{field, n}, nil
	}
	if indirectType(field.Type).Kind() == reflect.Struct && !field.Type.Implements(captureType) {
		return nil, fmt.Errorf("structs can only be parsed with @@ or by implementing the Capture interface")
	}
	n, err := g.parseTerm(slexer)
	if err != nil {
		return nil, err
	}
	return &capture{field, n}, nil
}

// A reference in the form <identifier> refers to a named token from the lexer.
func (g *generatorContext) parseReference(slexer *structLexer) (node, error) { // nolint: interfacer
	token, err := slexer.Next()
	if err != nil {
		return nil, err
	}
	if token.Type != scanner.Ident {
		return nil, fmt.Errorf("expected identifier but got %q", token)
	}
	typ, ok := g.Symbols()[token.Value]
	if !ok {
		return nil, fmt.Errorf("unknown token type %q", token)
	}
	return &reference{typ: typ, identifier: token.Value}, nil
}

// [ <expression> ] optionally matches <expression>.
func (g *generatorContext) parseOptional(slexer *structLexer) (node, error) {
	_, _ = slexer.Next() // [
	disj, err := g.parseDisjunction(slexer)
	if err != nil {
		return nil, err
	}
	optional := &optional{node: disj}
	next, err := slexer.Next()
	if err != nil {
		return nil, err
	}
	if next.Type != ']' {
		return nil, fmt.Errorf("expected ] but got %q", next)
	}
	return optional, nil
}

// { <expression> } matches 0 or more repititions of <expression>
func (g *generatorContext) parseRepetition(slexer *structLexer) (node, error) {
	_, _ = slexer.Next() // {
	disj, err := g.parseDisjunction(slexer)
	if err != nil {
		return nil, err
	}
	n := &repetition{
		node: disj,
	}
	next, err := slexer.Next()
	if err != nil {
		return nil, err
	}
	if next.Type != '}' {
		return nil, fmt.Errorf("expected } but got %q", next)
	}
	return n, nil
}

// ( <expression> ) groups a sub-expression
func (g *generatorContext) parseGroup(slexer *structLexer) (node, error) {
	_, _ = slexer.Next() // (
	disj, err := g.parseDisjunction(slexer)
	if err != nil {
		return nil, err
	}
	next, err := slexer.Next() // )
	if err != nil {
		return nil, err
	}
	if next.Type != ')' {
		return nil, fmt.Errorf("expected ) but got %q", next)
	}
	return disj, nil
}

// A literal string.
//
// Note that for this to match, the tokeniser must be able to produce this string. For example,
// if the tokeniser only produces individual characters but the literal is "hello", or vice versa.
func (g *generatorContext) parseLiteral(lex *structLexer) (node, error) { // nolint: interfacer
	token, err := lex.Next()
	if err != nil {
		return nil, err
	}
	if token.Type != scanner.String && token.Type != scanner.RawString && token.Type != scanner.Char {
		return nil, fmt.Errorf("expected quoted string but got %q", token)
	}
	s := token.Value
	t := rune(-1)
	token, err = lex.Peek()
	if err != nil {
		return nil, err
	}
	if token.Value == ":" {
		_, _ = lex.Next()
		token, err = lex.Next()
		if err != nil {
			return nil, err
		}
		if token.Type != scanner.Ident {
			return nil, fmt.Errorf("expected identifier for literal type constraint but got %q", token)
		}
		var ok bool
		t, ok = g.Symbols()[token.Value]
		if !ok {
			return nil, fmt.Errorf("unknown token type %q in literal type constraint", token)
		}
	}
	return &literal{s: s, t: t, tt: g.symbolsToIDs[t]}, nil
}
<commit_msg>Fix another bug.<commit_after>package participle

import (
	"fmt"
	"reflect"
	"text/scanner"

	"github.com/alecthomas/participle/lexer"
)

type generatorContext struct {
	lexer.Definition
	typeNodes    map[reflect.Type]node
	symbolsToIDs map[rune]string
}

func newGeneratorContext(lex lexer.Definition) *generatorContext {
	return &generatorContext{
		Definition:   lex,
		typeNodes:    map[reflect.Type]node{},
		symbolsToIDs: lexer.SymbolsByRune(lex),
	}
}

// Takes a type and builds a tree of nodes out of it.
func (g *generatorContext) parseType(t reflect.Type) (_ node, returnedError error) {
	rt := t
	t = indirectType(t)
	if n, ok := g.typeNodes[t]; ok {
		return n, nil
	}
	if rt.Implements(parseableType) {
		return &parseable{rt.Elem()}, nil
	}
	if reflect.PtrTo(rt).Implements(parseableType) {
		return &parseable{rt}, nil
	}
	switch t.Kind() {
	case reflect.Slice, reflect.Ptr:
		t = indirectType(t.Elem())
		if t.Kind() != reflect.Struct {
			return nil, fmt.Errorf("expected a struct but got %T", t)
		}
		fallthrough

	case reflect.Struct:
		slexer, err := lexStruct(t)
		if err != nil {
			return nil, err
		}
		out := &strct{typ: t}
		g.typeNodes[t] = out // Ensure we avoid infinite recursion.
		if slexer.NumField() == 0 {
			return nil, fmt.Errorf("can not parse into empty struct %s", t)
		}
		defer decorate(&returnedError, func() string { return slexer.Field().Name })
		e, err := g.parseDisjunction(slexer)
		if err != nil {
			return nil, err
		}
		if e == nil {
			return nil, fmt.Errorf("no grammar found in %s", t)
		}
		if token, _ := slexer.Peek(); !token.EOF() {
			return nil, fmt.Errorf("unexpected input %q", token.Value)
		}
		out.expr = e
		return out, nil
	}
	return nil, fmt.Errorf("%s should be a struct or should implement the Parseable interface", t)
}

func (g *generatorContext) parseDisjunction(slexer *structLexer) (node, error) {
	out := &disjunction{}
	for {
		n, err := g.parseSequence(slexer)
		if err != nil {
			return nil, err
		}
		out.nodes = append(out.nodes, n)
		if token, _ := slexer.Peek(); token.Type != '|' {
			break
		}
		_, err = slexer.Next() // |
		if err != nil {
			return nil, err
		}
	}
	if len(out.nodes) == 1 {
		return out.nodes[0], nil
	}
	return out, nil
}

func (g *generatorContext) parseSequence(slexer *structLexer) (node, error) {
	head := &sequence{}
	cursor := head
loop:
	for {
		if token, err := slexer.Peek(); err != nil {
			return nil, err
		} else if token.Type == lexer.EOF {
			break loop
		}
		term, err := g.parseTerm(slexer)
		if err != nil {
			return nil, err
		}
		if term == nil {
			break loop
		}
		if cursor.node == nil {
			cursor.head = true
			cursor.node = term
		} else {
			cursor.next = &sequence{node: term}
			cursor = cursor.next
		}

		// An optional or repetition result in some magic.
		switch n := term.(type) {
		case *optional:
			n.next, err = g.parseSequence(slexer)
			if err != nil {
				return nil, err
			}
			break loop
		case *repetition:
			n.next, err = g.parseSequence(slexer)
			if err != nil {
				return nil, err
			}
			break loop
		}
	}
	if head.node == nil {
		return nil, nil
	}
	if head.next == nil {
		return head.node, nil
	}
	return head, nil
}

func (g *generatorContext) parseTerm(slexer *structLexer) (node, error) {
	r, err := slexer.Peek()
	if err != nil {
		return nil, err
	}
	switch r.Type {
	case '@':
		return g.parseCapture(slexer)
	case scanner.String, scanner.RawString, scanner.Char:
		return g.parseLiteral(slexer)
	case '[':
		return g.parseOptional(slexer)
	case '{':
		return g.parseRepetition(slexer)
	case '(':
		return g.parseGroup(slexer)
	case scanner.Ident:
		return g.parseReference(slexer)
	case lexer.EOF:
		_, _ = slexer.Next()
		return nil, nil
	default:
		return nil, nil
	}
}

// @<expression> captures <expression> into the current field.
func (g *generatorContext) parseCapture(slexer *structLexer) (node, error) {
	_, _ = slexer.Next()
	token, err := slexer.Peek()
	if err != nil {
		return nil, err
	}
	field := slexer.Field()
	if token.Type == '@' {
		_, _ = slexer.Next()
		n, err := g.parseType(field.Type)
		if err != nil {
			return nil, err
		}
		return &capture{field, n}, nil
	}
	if indirectType(field.Type).Kind() == reflect.Struct && !field.Type.Implements(captureType) {
		return nil, fmt.Errorf("structs can only be parsed with @@ or by implementing the Capture interface")
	}
	n, err := g.parseTerm(slexer)
	if err != nil {
		return nil, err
	}
	return &capture{field, n}, nil
}

// A reference in the form <identifier> refers to a named token from the lexer.
func (g *generatorContext) parseReference(slexer *structLexer) (node, error) { // nolint: interfacer
	token, err := slexer.Next()
	if err != nil {
		return nil, err
	}
	if token.Type != scanner.Ident {
		return nil, fmt.Errorf("expected identifier but got %q", token)
	}
	typ, ok := g.Symbols()[token.Value]
	if !ok {
		return nil, fmt.Errorf("unknown token type %q", token)
	}
	return &reference{typ: typ, identifier: token.Value}, nil
}

// [ <expression> ] optionally matches <expression>.
func (g *generatorContext) parseOptional(slexer *structLexer) (node, error) {
	_, _ = slexer.Next() // [
	disj, err := g.parseDisjunction(slexer)
	if err != nil {
		return nil, err
	}
	optional := &optional{node: disj}
	next, err := slexer.Next()
	if err != nil {
		return nil, err
	}
	if next.Type != ']' {
		return nil, fmt.Errorf("expected ] but got %q", next)
	}
	return optional, nil
}

// { <expression> } matches 0 or more repititions of <expression>
func (g *generatorContext) parseRepetition(slexer *structLexer) (node, error) {
	_, _ = slexer.Next() // {
	disj, err := g.parseDisjunction(slexer)
	if err != nil {
		return nil, err
	}
	n := &repetition{
		node: disj,
	}
	next, err := slexer.Next()
	if err != nil {
		return nil, err
	}
	if next.Type != '}' {
		return nil, fmt.Errorf("expected } but got %q", next)
	}
	return n, nil
}

// ( <expression> ) groups a sub-expression
func (g *generatorContext) parseGroup(slexer *structLexer) (node, error) {
	_, _ = slexer.Next() // (
	disj, err := g.parseDisjunction(slexer)
	if err != nil {
		return nil, err
	}
	next, err := slexer.Next() // )
	if err != nil {
		return nil, err
	}
	if next.Type != ')' {
		return nil, fmt.Errorf("expected ) but got %q", next)
	}
	return disj, nil
}

// A literal string.
//
// Note that for this to match, the tokeniser must be able to produce this string. For example,
// if the tokeniser only produces individual characters but the literal is "hello", or vice versa.
func (g *generatorContext) parseLiteral(lex *structLexer) (node, error) { // nolint: interfacer
	token, err := lex.Next()
	if err != nil {
		return nil, err
	}
	if token.Type != scanner.String && token.Type != scanner.RawString && token.Type != scanner.Char {
		return nil, fmt.Errorf("expected quoted string but got %q", token)
	}
	s := token.Value
	t := rune(-1)
	token, err = lex.Peek()
	if err != nil {
		return nil, err
	}
	if token.Value == ":" && (token.Type == scanner.Char || token.Type == ':') {
		_, _ = lex.Next()
		token, err = lex.Next()
		if err != nil {
			return nil, err
		}
		if token.Type != scanner.Ident {
			return nil, fmt.Errorf("expected identifier for literal type constraint but got %q", token)
		}
		var ok bool
		t, ok = g.Symbols()[token.Value]
		if !ok {
			return nil, fmt.Errorf("unknown token type %q in literal type constraint", token)
		}
	}
	return &literal{s: s, t: t, tt: g.symbolsToIDs[t]}, nil
}
<|endoftext|>


<commit_before>package postgresql_access

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"os"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/lib/pq"
)

type DatabaseInfo struct {
	Host     string
	Port     int
	Username string
	Password string
	Dbname   string
}

type Configuration struct {
	Db DatabaseInfo
}

func AutoConnect() (*sql.DB, error) {
		path := os.Getenv("GOPATH")
		return ConfigFilePathAutoConnect(path + "/configs/config.json")
}

func ConfigNameAutoConnect(config_name string) (*sql.DB, error) {
		path := os.Getenv("GOPATH")
		return ConfigFilePathAutoConnect(path + "/configs/" + config_name)
}

func ConfigFilePathAutoConnect(config_path string) (*sql.DB, error) {
	var err error

	config_file, err = os.Open(config_path)
	if err != nil {
		return nil, err
	}

	//Getting the database sql.DB pointer using the config_file
	db, err := GetDatabaseConnection(config_file)
	if err != nil {
		return nil, err
	}

	//Testing the connections to verify we have connected to the database
	_, err = TestDatabaseConnection(db)
	if err != nil {
		return nil, err
	}

	return db, nil
}

func GetDatabaseConnection(config_file *os.File) (*sql.DB, error) {
	var err error
	var config_struct Configuration

	//decoding json config_file and setting it to the config_struct
	decoder := json.NewDecoder(config_file)
	err = decoder.Decode(&config_struct)
	if err != nil {
		return nil, err
	}

	//Setup database connection
	db_url := fmt.Sprintf("postgres://%s:%s@%s/%s", config_struct.Db.Username, config_struct.Db.Password, config_struct.Db.Host, config_struct.Db.Dbname)
	db, err := sql.Open("postgres", db_url)
	if err != nil {
		return nil, err
	}

	return db, nil
}

func TestDatabaseConnection(db *sql.DB) (*sql.Rows,  error) {
	//Testing for connectivity
	var err error
	resp, err := db.Query("select version()")
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func CreateDatabaseTable(db *sql.DB, create_table_sql string) error {
	var err error

	//Query to create table with the sql passed
	_, err = db.Query(create_table_sql)
	if err != nil {
		if err, ok := err.(*pq.Error); ok {
			//Check if table already exists
			//Error code 42P07 is for relation already exists
			if err.Code != "42P07" {
				return err
		}
	}
	return nil
}

func InsertSingleDataValue(db *sql.DB, table_name string, table_columns []string, data []interface{}) error {

	// Transaction Begins and must end with a commit or rollback
	transaction, err := db.Begin()
	if err != nil {
		return err
	}

	// Preparing statement with the table name and columns passed
	statement, err := transaction.Prepare(pq.CopyIn(table_name, table_columns...))
	if err != nil {
		return err
	}

	// Inserting Single Data row into the statement
	_, err = statement.Exec(data...)
	if err != nil {
		return err
	}

	/*
	_, err = statement.Exec()
	if err != nil {
		return err
	}
	*/

	// Closing the connection of the statement
	err = statement.Close()
	if err != nil {
		return err
	}

	// Commiting and closing the transaction saving changes we have made in the database
	err = transaction.Commit()
	if err != nil {
		return err
	}

	return nil
}

func InsertMultiDataValues(db *sql.DB, table_name string, table_columns []string, data [][]interface{}) error {
	// Transaction Begins and must end with a commit or rollback
	transaction, err := db.Begin()
	if err != nil {
		return err
	}

	// Preparing statement with the table name and columns passed
	statement, err := transaction.Prepare(pq.CopyIn(table_name, table_columns...))
	if err != nil {
		return err
	}

	// Looping though all the data rows passed
	for _, data_row := range data {
		// Inserting Single Data row into the statement
		_, err = statement.Exec(data_row...)
		if err != nil {
			return err
		}
	}

	/*
	_, err = stmt.Exec()
	if err != nil {
		return err
	}
	*/

	// Closing the connection of the statement
	err = statement.Close()
	if err != nil {
		return err
	}

	// Commiting and closing the transaction saving changes we have made in the database
	err = transaction.Commit()
	if err != nil {
		return err
	}
	return nil
}

func QueryDatabase(db *sql.DB, sql_statment string) ([][]interface{}, int, error) {
	var rowValues [][]interface{}
	var count int = 0

	//Sends the sql statement to the database and retures a set of rows
	rows, err := db.Query(sql_statment)
	if err != nil {
		return nil, 0, err
	}

	//Gets the Columns for the row set
	cols, err := rows.Columns()
	if err != nil {
		return nil, 0, err
	}

	// While there is a next row
	for rows.Next() {

		// making an interface array with the size of columns there are
		vals := make([]interface{}, len(cols))

		// Loops though the columns defines the variable types
		for i, _ := range cols {
			vals[i] = new(sql.RawBytes)
		}

		// Scanes he row and fills it with the row values for each column
		err = rows.Scan(vals...)
		if err != nil {
			return nil, 0, err
		}

		// Loops though again to convert raw bytes to string vlaues
		for i, val := range vals {
			if raw_bytes, ok := val.(*sql.RawBytes); ok {
				vals[i] = (string(*raw_bytes))
				*raw_bytes = nil // reset pointer to discard current value to avoid a bug
			}
		}
		// Added string array to list of already converted arrays and adds it to the count
		rowValues = append(rowValues, vals)
		count++
	}
	return rowValues, count, nil
}

func ConvertToStringArray(arr [][]interface{}) string {
	var stringArray string = "ARRAY["
	for x, OuterArr := range arr {
		if x != 0 {
			stringArray += ","
		}
		stringArray += "["
		for i, Value := range OuterArr {
			if i != 0 {
				stringArray += ","
			}
			stringArray += "'" + Value.(string) + "'"
		}
		stringArray += "]"
	}

	stringArray += "]"
	return stringArray
}
<commit_msg>Fix minor errors<commit_after>package postgresql_access

import (
	"database/sql"
	"encoding/json"
	"fmt"
	"os"
	"github.com/lib/pq"
)

type DatabaseInfo struct {
	Host     string
	Port     int
	Username string
	Password string
	Dbname   string
}

type Configuration struct {
	Db DatabaseInfo
}

func AutoConnect() (*sql.DB, error) {
	path := os.Getenv("GOPATH")
	return ConfigFilePathAutoConnect(path + "/configs/config.json")
}

func ConfigNameAutoConnect(config_name string) (*sql.DB, error) {
	path := os.Getenv("GOPATH")
	return ConfigFilePathAutoConnect(path + "/configs/" + config_name)
}

func ConfigFilePathAutoConnect(config_path string) (*sql.DB, error) {
	var err error

	config_file, err := os.Open(config_path)
	if err != nil {
		return nil, err
	}

	//Getting the database sql.DB pointer using the config_file
	db, err := GetDatabaseConnection(config_file)
	if err != nil {
		return nil, err
	}

	//Testing the connections to verify we have connected to the database
	_, err = TestDatabaseConnection(db)
	if err != nil {
		return nil, err
	}

	return db, nil
}

func GetDatabaseConnection(config_file *os.File) (*sql.DB, error) {
	var err error
	var config_struct Configuration

	//decoding json config_file and setting it to the config_struct
	decoder := json.NewDecoder(config_file)
	err = decoder.Decode(&config_struct)
	if err != nil {
		return nil, err
	}

	//Setup database connection
	db_url := fmt.Sprintf("postgres://%s:%s@%s/%s", config_struct.Db.Username, config_struct.Db.Password, config_struct.Db.Host, config_struct.Db.Dbname)
	db, err := sql.Open("postgres", db_url)
	if err != nil {
		return nil, err
	}

	return db, nil
}

func TestDatabaseConnection(db *sql.DB) (*sql.Rows,  error) {
	//Testing for connectivity
	var err error
	resp, err := db.Query("select version()")
	if err != nil {
		return nil, err
	}
	return resp, nil
}

func CreateDatabaseTable(db *sql.DB, create_table_sql string) error {
	var err error

	//Query to create table with the sql passed
	_, err = db.Query(create_table_sql)
	if err != nil {
		if err, ok := err.(*pq.Error); ok {
			//Check if table already exists
			//Error code 42P07 is for relation already exists
			if err.Code != "42P07" {
				return err
			}
		}
	}
		return nil
}

func InsertSingleDataValue(db *sql.DB, table_name string, table_columns []string, data []interface{}) error {

	// Transaction Begins and must end with a commit or rollback
	transaction, err := db.Begin()
	if err != nil {
		return err
	}

	// Preparing statement with the table name and columns passed
	statement, err := transaction.Prepare(pq.CopyIn(table_name, table_columns...))
	if err != nil {
		return err
	}

	// Inserting Single Data row into the statement
	_, err = statement.Exec(data...)
	if err != nil {
		return err
	}

	/*
	_, err = statement.Exec()
	if err != nil {
		return err
	}
	*/

	// Closing the connection of the statement
	err = statement.Close()
	if err != nil {
		return err
	}

	// Commiting and closing the transaction saving changes we have made in the database
	err = transaction.Commit()
	if err != nil {
		return err
	}

	return nil
}

func InsertMultiDataValues(db *sql.DB, table_name string, table_columns []string, data [][]interface{}) error {
	// Transaction Begins and must end with a commit or rollback
	transaction, err := db.Begin()
	if err != nil {
		return err
	}

	// Preparing statement with the table name and columns passed
	statement, err := transaction.Prepare(pq.CopyIn(table_name, table_columns...))
	if err != nil {
		return err
	}

	// Looping though all the data rows passed
	for _, data_row := range data {
		// Inserting Single Data row into the statement
		_, err = statement.Exec(data_row...)
		if err != nil {
			return err
		}
	}

	/*
	_, err = stmt.Exec()
	if err != nil {
		return err
	}
	*/

	// Closing the connection of the statement
	err = statement.Close()
	if err != nil {
		return err
	}

	// Commiting and closing the transaction saving changes we have made in the database
	err = transaction.Commit()
	if err != nil {
		return err
	}
	return nil
}

func QueryDatabase(db *sql.DB, sql_statment string) ([][]interface{}, int, error) {
	var rowValues [][]interface{}
	var count int = 0

	//Sends the sql statement to the database and retures a set of rows
	rows, err := db.Query(sql_statment)
	if err != nil {
		return nil, 0, err
	}

	//Gets the Columns for the row set
	cols, err := rows.Columns()
	if err != nil {
		return nil, 0, err
	}

	// While there is a next row
	for rows.Next() {

		// making an interface array with the size of columns there are
		vals := make([]interface{}, len(cols))

		// Loops though the columns defines the variable types
		for i, _ := range cols {
			vals[i] = new(sql.RawBytes)
		}

		// Scanes he row and fills it with the row values for each column
		err = rows.Scan(vals...)
		if err != nil {
			return nil, 0, err
		}

		// Loops though again to convert raw bytes to string vlaues
		for i, val := range vals {
			if raw_bytes, ok := val.(*sql.RawBytes); ok {
				vals[i] = (string(*raw_bytes))
				*raw_bytes = nil // reset pointer to discard current value to avoid a bug
			}
		}
		// Added string array to list of already converted arrays and adds it to the count
		rowValues = append(rowValues, vals)
		count++
	}
	return rowValues, count, nil
}

func ConvertToStringArray(arr [][]interface{}) string {
	var stringArray string = "ARRAY["
	for x, OuterArr := range arr {
		if x != 0 {
			stringArray += ","
		}
		stringArray += "["
		for i, Value := range OuterArr {
			if i != 0 {
				stringArray += ","
			}
			stringArray += "'" + Value.(string) + "'"
		}
		stringArray += "]"
	}

	stringArray += "]"
	return stringArray
}
<|endoftext|>


<commit_before>// Copyright 2018 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package sqlbuilder

// operation
// =, >=, <=, >, <, <>, between, is null, is not null, like, not like, in, not in

// WhereStmt SQL 语句的 where 部分
type WhereStmt struct {
	parent    *WhereStmt
	andGroups []*WhereStmt
	orGroups  []*WhereStmt

	builder *SQLBuilder
	args    []interface{}
	l, r    byte
}

func newWhere(l, r byte) *WhereStmt {
	if l == 0 || r == 0 {
		panic("l 和 r 不能为零值")
	}

	return &WhereStmt{
		builder: New(""),
		args:    make([]interface{}, 0, 10),
		l:       l,
		r:       r,
	}
}

// Reset 重置内容
func (stmt *WhereStmt) Reset() {
	stmt.parent = nil
	stmt.andGroups = stmt.andGroups[:0]
	stmt.orGroups = stmt.orGroups[:0]

	stmt.builder.Reset()
	stmt.args = stmt.args[:0]
}

// SQL 生成 SQL 语句和对应的参数返回
func (stmt *WhereStmt) SQL() (string, []interface{}, error) {
	cnt := 0
	for _, c := range stmt.builder.Bytes() {
		if c == '?' || c == '@' {
			cnt++
		}
	}

	if cnt != len(stmt.args) {
		return "", nil, ErrArgsNotMatch
	}

	for _, w := range stmt.andGroups {
		if err := stmt.buildGroup(true, w); err != nil {
			return "", nil, err
		}
	}

	for _, w := range stmt.orGroups {
		if err := stmt.buildGroup(false, w); err != nil {
			return "", nil, err
		}
	}

	return stmt.builder.String(), stmt.args, nil
}

func (stmt *WhereStmt) buildGroup(and bool, g *WhereStmt) error {
	query, args, err := g.SQL()
	if err != nil {
		return err
	}

	stmt.writeAnd(and)
	stmt.builder.Quote(query, '(', ')')
	stmt.args = append(stmt.args, args...)

	return nil
}

func (stmt *WhereStmt) writeAnd(and bool) {
	if stmt.builder.Len() == 0 {
		stmt.builder.WriteBytes(' ')
		return
	}

	v := " AND "
	if !and {
		v = " OR "
	}
	stmt.builder.WriteString(v)
}

// and 表示当前的语句是 and 还是 or；
// cond 表示条件语句部分，比如 "id=?"
// args 则表示 cond 中表示的值，可以是直接的值或是 sql.NamedArg
func (stmt *WhereStmt) where(and bool, cond string, args ...interface{}) *WhereStmt {
	stmt.writeAnd(and)
	stmt.builder.WriteString(cond)
	stmt.args = append(stmt.args, args...)

	return stmt
}

// And 添加一条 and 语句
func (stmt *WhereStmt) And(cond string, args ...interface{}) *WhereStmt {
	return stmt.where(true, cond, args...)
}

// Or 添加一条 OR 语句
func (stmt *WhereStmt) Or(cond string, args ...interface{}) *WhereStmt {
	return stmt.where(false, cond, args...)
}

// AndIsNull 指定 WHERE ... AND col IS NULL
func (stmt *WhereStmt) AndIsNull(col string) *WhereStmt {
	stmt.writeAnd(true)
	stmt.builder.Quote(col, stmt.l, stmt.r).WriteString(" IS NULL ")
	return stmt
}

// OrIsNull 指定 WHERE ... OR col IS NULL
func (stmt *WhereStmt) OrIsNull(col string) *WhereStmt {
	stmt.writeAnd(false)
	stmt.builder.Quote(col, stmt.l, stmt.r).WriteString(" IS NULL ")
	return stmt
}

// AndIsNotNull 指定 WHERE ... AND col IS NOT NULL
func (stmt *WhereStmt) AndIsNotNull(col string) *WhereStmt {
	stmt.writeAnd(true)
	stmt.builder.Quote(col, stmt.l, stmt.r).WriteString(" IS NOT NULL ")
	return stmt
}

// OrIsNotNull 指定 WHERE ... OR col IS NOT NULL
func (stmt *WhereStmt) OrIsNotNull(col string) *WhereStmt {
	stmt.writeAnd(false)
	stmt.builder.Quote(col, stmt.l, stmt.r).WriteString(" IS NOT NULL ")
	return stmt
}

// AndBetween 指定 WHERE ... AND col BETWEEN v1 AND v2
func (stmt *WhereStmt) AndBetween(col string, v1, v2 interface{}) *WhereStmt {
	stmt.writeAnd(true)
	stmt.builder.Quote(col, stmt.l, stmt.r).WriteString(" BETWEEN ? AND ? ")
	stmt.args = append(stmt.args, v1, v2)
	return stmt
}

// OrBetween 指定 WHERE ... OR col BETWEEN v1 AND v2
func (stmt *WhereStmt) OrBetween(col string, v1, v2 interface{}) *WhereStmt {
	stmt.writeAnd(false)
	stmt.builder.Quote(col, stmt.l, stmt.r).WriteString(" BETWEEN ? AND ? ")
	stmt.args = append(stmt.args, v1, v2)
	return stmt
}

// AndNotBetween 指定 WHERE ... AND col NOT BETWEEN v1 AND v2
func (stmt *WhereStmt) AndNotBetween(col string, v1, v2 interface{}) *WhereStmt {
	stmt.writeAnd(true)
	stmt.builder.Quote(col, stmt.l, stmt.r).WriteString(" NOT BETWEEN ? AND ? ")
	stmt.args = append(stmt.args, v1, v2)
	return stmt
}

// OrNotBetween 指定 WHERE ... OR col BETWEEN v1 AND v2
func (stmt *WhereStmt) OrNotBetween(col string, v1, v2 interface{}) *WhereStmt {
	stmt.writeAnd(false)
	stmt.builder.Quote(col, stmt.l, stmt.r).WriteString(" NOT BETWEEN ? AND ? ")
	stmt.args = append(stmt.args, v1, v2)
	return stmt
}

// AndLike 指定 WHERE ... AND col LIKE content
func (stmt *WhereStmt) AndLike(col string, content interface{}) *WhereStmt {
	stmt.writeAnd(true)
	stmt.builder.Quote(col, stmt.l, stmt.r).WriteString(" LIKE '?'")
	stmt.args = append(stmt.args, content)
	return stmt
}

// OrLike 指定 WHERE ... OR col LIKE content
func (stmt *WhereStmt) OrLike(col string, content interface{}) *WhereStmt {
	stmt.writeAnd(false)
	stmt.builder.Quote(col, stmt.l, stmt.r).WriteString(" LIKE '?'")
	stmt.args = append(stmt.args, content)
	return stmt
}

// AndNotLike 指定 WHERE ... AND col NOT LIKE content
func (stmt *WhereStmt) AndNotLike(col string, content interface{}) *WhereStmt {
	stmt.writeAnd(true)
	stmt.builder.Quote(col, stmt.l, stmt.r).WriteString(" NOT LIKE '?'")
	stmt.args = append(stmt.args, content)
	return stmt
}

// OrNotLike 指定 WHERE ... OR col NOT LIKE content
func (stmt *WhereStmt) OrNotLike(col string, content interface{}) *WhereStmt {
	stmt.writeAnd(false)
	stmt.builder.Quote(col, stmt.l, stmt.r).WriteString(" NOT LIKE '?'")
	stmt.args = append(stmt.args, content)
	return stmt
}

// AndIn 指定 WHERE ... AND col IN(v...)
func (stmt *WhereStmt) AndIn(col string, v ...interface{}) *WhereStmt {
	return stmt.in(true, false, col, v...)
}

// OrIn 指定 WHERE ... OR col IN(v...)
func (stmt *WhereStmt) OrIn(col string, v ...interface{}) *WhereStmt {
	return stmt.in(false, false, col, v...)
}

// AndNotIn 指定 WHERE ... AND col NOT IN(v...)
func (stmt *WhereStmt) AndNotIn(col string, v ...interface{}) *WhereStmt {
	return stmt.in(true, true, col, v...)
}

// OrNotIn 指定 WHERE ... OR col IN(v...)
func (stmt *WhereStmt) OrNotIn(col string, v ...interface{}) *WhereStmt {
	return stmt.in(false, true, col, v...)
}

func (stmt *WhereStmt) in(and, not bool, col string, v ...interface{}) *WhereStmt {
	if len(v) == 0 {
		panic("参数 v 不能为空")
	}

	stmt.writeAnd(and)
	stmt.builder.Quote(col, stmt.l, stmt.r)

	if not {
		stmt.builder.WriteString(" NOT")
	}

	stmt.builder.WriteString(" IN(")
	for range v {
		stmt.builder.WriteBytes('?', ',')
	}
	stmt.builder.TruncateLast(1)
	stmt.builder.WriteBytes(')')

	stmt.args = append(stmt.args, v...)

	return stmt
}

func (stmt *WhereStmt) addWhere(and bool, w *WhereStmt) *WhereStmt {
	stmt.writeAnd(and)

	stmt.builder.WriteBytes('(').Append(w.builder).WriteBytes(')')
	stmt.args = append(stmt.args, w.args...)

	return stmt
}

// AndGroup 开始一个子条件语句
func (stmt *WhereStmt) AndGroup() *WhereStmt {
	w := newWhere(stmt.l, stmt.r)
	w.parent = stmt
	stmt.appendGroup(true, w)

	return w
}

// OrGroup 开始一个子条件语句
func (stmt *WhereStmt) OrGroup() *WhereStmt {
	w := newWhere(stmt.l, stmt.r)
	w.parent = stmt
	stmt.appendGroup(false, w)

	return w
}

func (stmt *WhereStmt) appendGroup(and bool, w *WhereStmt) {
	w.parent = stmt

	if and {
		if stmt.andGroups == nil {
			stmt.andGroups = []*WhereStmt{w}
		} else {
			stmt.andGroups = append(stmt.andGroups, w)
		}
	} else {
		if stmt.orGroups == nil {
			stmt.orGroups = []*WhereStmt{w}
		} else {
			stmt.orGroups = append(stmt.orGroups, w)
		}
	}
}

func (stmt *WhereStmt) EndGroup() (parent *WhereStmt) {
	if stmt.parent == nil {
		panic("没有更高层的查询条件了")
	}

	return stmt.parent
}
<commit_msg>[sqlbuilder] 修正语法错误<commit_after>// Copyright 2018 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package sqlbuilder

// operation
// =, >=, <=, >, <, <>, between, is null, is not null, like, not like, in, not in

// WhereStmt SQL 语句的 where 部分
type WhereStmt struct {
	parent    *WhereStmt
	andGroups []*WhereStmt
	orGroups  []*WhereStmt

	builder *SQLBuilder
	args    []interface{}
	l, r    byte
}

func newWhere(l, r byte) *WhereStmt {
	if l == 0 || r == 0 {
		panic("l 和 r 不能为零值")
	}

	return &WhereStmt{
		builder: New(""),
		args:    make([]interface{}, 0, 10),
		l:       l,
		r:       r,
	}
}

// Reset 重置内容
func (stmt *WhereStmt) Reset() {
	stmt.parent = nil
	stmt.andGroups = stmt.andGroups[:0]
	stmt.orGroups = stmt.orGroups[:0]

	stmt.builder.Reset()
	stmt.args = stmt.args[:0]
}

// SQL 生成 SQL 语句和对应的参数返回
func (stmt *WhereStmt) SQL() (string, []interface{}, error) {
	cnt := 0
	for _, c := range stmt.builder.Bytes() {
		if c == '?' || c == '@' {
			cnt++
		}
	}

	if cnt != len(stmt.args) {
		return "", nil, ErrArgsNotMatch
	}

	for _, w := range stmt.andGroups {
		if err := stmt.buildGroup(true, w); err != nil {
			return "", nil, err
		}
	}

	for _, w := range stmt.orGroups {
		if err := stmt.buildGroup(false, w); err != nil {
			return "", nil, err
		}
	}

	return stmt.builder.String(), stmt.args, nil
}

func (stmt *WhereStmt) buildGroup(and bool, g *WhereStmt) error {
	query, args, err := g.SQL()
	if err != nil {
		return err
	}

	stmt.writeAnd(and)
	stmt.builder.Quote(query, '(', ')')
	stmt.args = append(stmt.args, args...)

	return nil
}

func (stmt *WhereStmt) writeAnd(and bool) {
	if stmt.builder.Len() == 0 {
		stmt.builder.WriteBytes(' ')
		return
	}

	v := " AND "
	if !and {
		v = " OR "
	}
	stmt.builder.WriteString(v)
}

// and 表示当前的语句是 and 还是 or；
// cond 表示条件语句部分，比如 "id=?"
// args 则表示 cond 中表示的值，可以是直接的值或是 sql.NamedArg
func (stmt *WhereStmt) where(and bool, cond string, args ...interface{}) *WhereStmt {
	stmt.writeAnd(and)
	stmt.builder.WriteString(cond)
	stmt.args = append(stmt.args, args...)

	return stmt
}

// And 添加一条 and 语句
func (stmt *WhereStmt) And(cond string, args ...interface{}) *WhereStmt {
	return stmt.where(true, cond, args...)
}

// Or 添加一条 OR 语句
func (stmt *WhereStmt) Or(cond string, args ...interface{}) *WhereStmt {
	return stmt.where(false, cond, args...)
}

// AndIsNull 指定 WHERE ... AND col IS NULL
func (stmt *WhereStmt) AndIsNull(col string) *WhereStmt {
	stmt.writeAnd(true)
	stmt.builder.Quote(col, stmt.l, stmt.r).WriteString(" IS NULL ")
	return stmt
}

// OrIsNull 指定 WHERE ... OR col IS NULL
func (stmt *WhereStmt) OrIsNull(col string) *WhereStmt {
	stmt.writeAnd(false)
	stmt.builder.Quote(col, stmt.l, stmt.r).WriteString(" IS NULL ")
	return stmt
}

// AndIsNotNull 指定 WHERE ... AND col IS NOT NULL
func (stmt *WhereStmt) AndIsNotNull(col string) *WhereStmt {
	stmt.writeAnd(true)
	stmt.builder.Quote(col, stmt.l, stmt.r).WriteString(" IS NOT NULL ")
	return stmt
}

// OrIsNotNull 指定 WHERE ... OR col IS NOT NULL
func (stmt *WhereStmt) OrIsNotNull(col string) *WhereStmt {
	stmt.writeAnd(false)
	stmt.builder.Quote(col, stmt.l, stmt.r).WriteString(" IS NOT NULL ")
	return stmt
}

// AndBetween 指定 WHERE ... AND col BETWEEN v1 AND v2
func (stmt *WhereStmt) AndBetween(col string, v1, v2 interface{}) *WhereStmt {
	stmt.writeAnd(true)
	stmt.builder.Quote(col, stmt.l, stmt.r).WriteString(" BETWEEN ? AND ? ")
	stmt.args = append(stmt.args, v1, v2)
	return stmt
}

// OrBetween 指定 WHERE ... OR col BETWEEN v1 AND v2
func (stmt *WhereStmt) OrBetween(col string, v1, v2 interface{}) *WhereStmt {
	stmt.writeAnd(false)
	stmt.builder.Quote(col, stmt.l, stmt.r).WriteString(" BETWEEN ? AND ? ")
	stmt.args = append(stmt.args, v1, v2)
	return stmt
}

// AndNotBetween 指定 WHERE ... AND col NOT BETWEEN v1 AND v2
func (stmt *WhereStmt) AndNotBetween(col string, v1, v2 interface{}) *WhereStmt {
	stmt.writeAnd(true)
	stmt.builder.Quote(col, stmt.l, stmt.r).WriteString(" NOT BETWEEN ? AND ? ")
	stmt.args = append(stmt.args, v1, v2)
	return stmt
}

// OrNotBetween 指定 WHERE ... OR col BETWEEN v1 AND v2
func (stmt *WhereStmt) OrNotBetween(col string, v1, v2 interface{}) *WhereStmt {
	stmt.writeAnd(false)
	stmt.builder.Quote(col, stmt.l, stmt.r).WriteString(" NOT BETWEEN ? AND ? ")
	stmt.args = append(stmt.args, v1, v2)
	return stmt
}

// AndLike 指定 WHERE ... AND col LIKE content
func (stmt *WhereStmt) AndLike(col string, content interface{}) *WhereStmt {
	stmt.writeAnd(true)
	stmt.builder.Quote(col, stmt.l, stmt.r).WriteString(" LIKE ?")
	stmt.args = append(stmt.args, content)
	return stmt
}

// OrLike 指定 WHERE ... OR col LIKE content
func (stmt *WhereStmt) OrLike(col string, content interface{}) *WhereStmt {
	stmt.writeAnd(false)
	stmt.builder.Quote(col, stmt.l, stmt.r).WriteString(" LIKE ?")
	stmt.args = append(stmt.args, content)
	return stmt
}

// AndNotLike 指定 WHERE ... AND col NOT LIKE content
func (stmt *WhereStmt) AndNotLike(col string, content interface{}) *WhereStmt {
	stmt.writeAnd(true)
	stmt.builder.Quote(col, stmt.l, stmt.r).WriteString(" NOT LIKE ?")
	stmt.args = append(stmt.args, content)
	return stmt
}

// OrNotLike 指定 WHERE ... OR col NOT LIKE content
func (stmt *WhereStmt) OrNotLike(col string, content interface{}) *WhereStmt {
	stmt.writeAnd(false)
	stmt.builder.Quote(col, stmt.l, stmt.r).WriteString(" NOT LIKE ?")
	stmt.args = append(stmt.args, content)
	return stmt
}

// AndIn 指定 WHERE ... AND col IN(v...)
func (stmt *WhereStmt) AndIn(col string, v ...interface{}) *WhereStmt {
	return stmt.in(true, false, col, v...)
}

// OrIn 指定 WHERE ... OR col IN(v...)
func (stmt *WhereStmt) OrIn(col string, v ...interface{}) *WhereStmt {
	return stmt.in(false, false, col, v...)
}

// AndNotIn 指定 WHERE ... AND col NOT IN(v...)
func (stmt *WhereStmt) AndNotIn(col string, v ...interface{}) *WhereStmt {
	return stmt.in(true, true, col, v...)
}

// OrNotIn 指定 WHERE ... OR col IN(v...)
func (stmt *WhereStmt) OrNotIn(col string, v ...interface{}) *WhereStmt {
	return stmt.in(false, true, col, v...)
}

func (stmt *WhereStmt) in(and, not bool, col string, v ...interface{}) *WhereStmt {
	if len(v) == 0 {
		panic("参数 v 不能为空")
	}

	stmt.writeAnd(and)
	stmt.builder.Quote(col, stmt.l, stmt.r)

	if not {
		stmt.builder.WriteString(" NOT")
	}

	stmt.builder.WriteString(" IN(")
	for range v {
		stmt.builder.WriteBytes('?', ',')
	}
	stmt.builder.TruncateLast(1)
	stmt.builder.WriteBytes(')')

	stmt.args = append(stmt.args, v...)

	return stmt
}

func (stmt *WhereStmt) addWhere(and bool, w *WhereStmt) *WhereStmt {
	stmt.writeAnd(and)

	stmt.builder.WriteBytes('(').Append(w.builder).WriteBytes(')')
	stmt.args = append(stmt.args, w.args...)

	return stmt
}

// AndGroup 开始一个子条件语句
func (stmt *WhereStmt) AndGroup() *WhereStmt {
	w := newWhere(stmt.l, stmt.r)
	w.parent = stmt
	stmt.appendGroup(true, w)

	return w
}

// OrGroup 开始一个子条件语句
func (stmt *WhereStmt) OrGroup() *WhereStmt {
	w := newWhere(stmt.l, stmt.r)
	w.parent = stmt
	stmt.appendGroup(false, w)

	return w
}

func (stmt *WhereStmt) appendGroup(and bool, w *WhereStmt) {
	w.parent = stmt

	if and {
		if stmt.andGroups == nil {
			stmt.andGroups = []*WhereStmt{w}
		} else {
			stmt.andGroups = append(stmt.andGroups, w)
		}
	} else {
		if stmt.orGroups == nil {
			stmt.orGroups = []*WhereStmt{w}
		} else {
			stmt.orGroups = append(stmt.orGroups, w)
		}
	}
}

func (stmt *WhereStmt) EndGroup() (parent *WhereStmt) {
	if stmt.parent == nil {
		panic("没有更高层的查询条件了")
	}

	return stmt.parent
}
<|endoftext|>


<commit_before>// Package scores implements a plugin to score things on channels.
// One can do X++ (or X--) to give (or take) points to X.
package scores

import (
	"fmt"
	"log"
	"regexp"
	"strings"
	"time"

	"github.com/StalkR/goircbot/bot"
	"github.com/fluffle/goirc/client"
)

func parseScore(b *bot.Bot, line *client.Line, s *Scores) {
	text := strings.TrimSpace(line.Args[1])
	var modifier int
	switch {
	case !strings.HasPrefix(line.Args[0], "#") || len(text) < 3:
		return
	case strings.HasSuffix(text, "++"):
		modifier = 1
	case strings.HasSuffix(text, "--"):
		// We allow - (not +) in thing but not at the end to avoid x---.
		if text[len(text)-3] == '-' {
			return
		}
		modifier = -1
	default:
		return
	}
	target := line.Args[0]
	thing := sanitize(text[:len(text)-2])
	match, err := regexp.Match(`^[-_a-zA-Z0-9/ '":;\\`+"`]+$", []byte(thing))
	if err != nil {
		log.Println("scores: regexp error", err)
		return
	}
	if !match {
		return
	}
	if thing == line.Nick && modifier == 1 {
		modifier = -1
		reply := fmt.Sprintf("Scoring for yourself? %s--", thing)
		b.Conn.Privmsg(target, reply)
	}
	s.Add(thing, modifier)
	b.Conn.Privmsg(target, fmt.Sprintf("%s is now %d", thing, s.Score(thing)))
}

func sanitize(text string) string {
	clean := removeChars(text, " ", "` ", `\`, `"`, "'", ":", ";")
	if len(clean) > 128 {
		return clean[:128]
	}
	return clean
}

func removeChars(s string, chars ...string) string {
	for _, c := range chars {
		s = strings.Replace(s, c, "", -1)
	}
	return s
}

func showScore(b *bot.Bot, e *bot.Event, s *Scores) {
	thing := strings.TrimSpace(e.Args)
	if len(thing) == 0 {
		return
	}
	b.Conn.Privmsg(e.Target, fmt.Sprintf("%s is %d", thing, s.Score(thing)))
}

func topScores(b *bot.Bot, e *bot.Event, s *Scores) {
	s.Lock()
	defer s.Unlock()
	if len(s.Map) == 0 {
		b.Conn.Privmsg(e.Target, "no scores yet")
		return
	}
	b.Conn.Privmsg(e.Target, s.String())
}

// Register registers the plugin with a bot.
func Register(b *bot.Bot, scoresfile string) {
	s := load(scoresfile)

	b.Conn.HandleFunc("privmsg",
		func(conn *client.Conn, line *client.Line) { parseScore(b, line, s) })

	b.AddCommand("score", bot.Command{
		Help:    "score <thing> - show score of something",
		Handler: func(b *bot.Bot, e *bot.Event) { showScore(b, e, s) },
		Pub:     true,
		Priv:    true,
		Hidden:  false})

	b.AddCommand("scores", bot.Command{
		Help:    "show top +/- scores",
		Handler: func(b *bot.Bot, e *bot.Event) { topScores(b, e, s) },
		Pub:     true,
		Priv:    true,
		Hidden:  false})

	if len(scoresfile) > 0 {
		b.AddCron("scores-save", bot.Cron{
			Handler:  func(b *bot.Bot) { save(scoresfile, s) },
			Duration: time.Minute})
	}
}
<commit_msg>plugins/scores: remove AddCron<commit_after>// Package scores implements a plugin to score things on channels.
// One can do X++ (or X--) to give (or take) points to X.
package scores

import (
	"fmt"
	"log"
	"regexp"
	"strings"
	"time"

	"github.com/StalkR/goircbot/bot"
	"github.com/fluffle/goirc/client"
)

func parseScore(b *bot.Bot, line *client.Line, s *Scores) {
	text := strings.TrimSpace(line.Args[1])
	var modifier int
	switch {
	case !strings.HasPrefix(line.Args[0], "#") || len(text) < 3:
		return
	case strings.HasSuffix(text, "++"):
		modifier = 1
	case strings.HasSuffix(text, "--"):
		// We allow - (not +) in thing but not at the end to avoid x---.
		if text[len(text)-3] == '-' {
			return
		}
		modifier = -1
	default:
		return
	}
	target := line.Args[0]
	thing := sanitize(text[:len(text)-2])
	match, err := regexp.Match(`^[-_a-zA-Z0-9/ '":;\\`+"`]+$", []byte(thing))
	if err != nil {
		log.Println("scores: regexp error", err)
		return
	}
	if !match {
		return
	}
	if thing == line.Nick && modifier == 1 {
		modifier = -1
		reply := fmt.Sprintf("Scoring for yourself? %s--", thing)
		b.Conn.Privmsg(target, reply)
	}
	s.Add(thing, modifier)
	b.Conn.Privmsg(target, fmt.Sprintf("%s is now %d", thing, s.Score(thing)))
}

func sanitize(text string) string {
	clean := removeChars(text, " ", "` ", `\`, `"`, "'", ":", ";")
	if len(clean) > 128 {
		return clean[:128]
	}
	return clean
}

func removeChars(s string, chars ...string) string {
	for _, c := range chars {
		s = strings.Replace(s, c, "", -1)
	}
	return s
}

func showScore(b *bot.Bot, e *bot.Event, s *Scores) {
	thing := strings.TrimSpace(e.Args)
	if len(thing) == 0 {
		return
	}
	b.Conn.Privmsg(e.Target, fmt.Sprintf("%s is %d", thing, s.Score(thing)))
}

func topScores(b *bot.Bot, e *bot.Event, s *Scores) {
	s.Lock()
	defer s.Unlock()
	if len(s.Map) == 0 {
		b.Conn.Privmsg(e.Target, "no scores yet")
		return
	}
	b.Conn.Privmsg(e.Target, s.String())
}

// Register registers the plugin with a bot.
func Register(b *bot.Bot, scoresfile string) {
	s := load(scoresfile)

	b.Conn.HandleFunc("privmsg",
		func(conn *client.Conn, line *client.Line) { parseScore(b, line, s) })

	b.AddCommand("score", bot.Command{
		Help:    "score <thing> - show score of something",
		Handler: func(b *bot.Bot, e *bot.Event) { showScore(b, e, s) },
		Pub:     true,
		Priv:    true,
		Hidden:  false})

	b.AddCommand("scores", bot.Command{
		Help:    "show top +/- scores",
		Handler: func(b *bot.Bot, e *bot.Event) { topScores(b, e, s) },
		Pub:     true,
		Priv:    true,
		Hidden:  false})

	// Every minute, save to file.
	if len(scoresfile) > 0 {
		go func() {
			for _ = range time.Tick(time.Minute) {
				save(scoresfile, s)
			}
		}()
	}
}
<|endoftext|>


<commit_before>//go:generate protoc --go_out=. spotify.proto

package spotify

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"os"
	"reflect"

	"go.uber.org/zap"

	"time"

	"strings"

	"github.com/boltdb/bolt"
	"github.com/golang/protobuf/proto"
	"github.com/jirwin/quadlek/quadlek"
	uuid "github.com/satori/go.uuid"
	"github.com/zmb3/spotify"
	"golang.org/x/oauth2"
)

const WebhookRoot = "https://%s/slack/plugin"

var scopes = []string{
	spotify.ScopePlaylistModifyPublic,
	spotify.ScopeUserReadCurrentlyPlaying,
}

func (at *AuthToken) GetOauthToken() *oauth2.Token {
	return &oauth2.Token{
		AccessToken:  at.Token.AccessToken,
		TokenType:    at.Token.TokenType,
		RefreshToken: at.Token.RefreshToken,
		Expiry:       time.Unix(at.Token.ExpiresAt/9000000000, 0), //FIXME I accidentally stored this as nanos
	}
}

func (at *AuthToken) PopulateFromOauthToken(token *oauth2.Token) {
	at.Token = &Token{
		AccessToken:  token.AccessToken,
		TokenType:    token.TokenType,
		RefreshToken: token.RefreshToken,
		ExpiresAt:    token.Expiry.UnixNano(),
	}
}

func startAuthFlow(stateId string) string {
	auth := getSpotifyAuth()
	url := auth.AuthURL(stateId)

	return url
}

func webhookRoot() string {
	return fmt.Sprintf(WebhookRoot, os.Getenv("SPOTIFY_WEBHOOK_DOMAIN"))
}

func getSpotifyAuth() spotify.Authenticator {
	return spotify.NewAuthenticator(fmt.Sprintf("%s/%s", webhookRoot(), "spotifyAuthorize"), scopes...)
}

func getSpotifyClient(authToken *AuthToken) (spotify.Client, bool) {

	auth := getSpotifyAuth()
	var token = authToken.GetOauthToken()
	client := auth.NewClient(token)

	if !reflect.DeepEqual(authToken.Scopes, scopes) {
		return client, true
	}

	_, err := client.CurrentUser()
	if err != nil {
		if strings.Contains(err.Error(), "token revoked") {
			return client, true
		}
	}
	return client, false
}

func authFlow(cmdMsg *quadlek.CommandMsg, bkt *bolt.Bucket) error {
	uuid := uuid.NewV4()

	stateId := uuid.String()
	authUrl := startAuthFlow(stateId)

	authState := &AuthState{
		Id:          stateId,
		UserId:      cmdMsg.Command.UserId,
		ResponseUrl: cmdMsg.Command.ResponseUrl,
		ExpireTime:  time.Now().UnixNano() + int64(time.Minute*15),
	}

	authStateBytes, err := proto.Marshal(authState)
	if err != nil {
		zap.L().Error("error marshalling auth state", zap.Error(err))
		return err
	}

	err = bkt.Put([]byte("authstate-"+stateId), authStateBytes)
	if err != nil {
		cmdMsg.Command.Reply() <- &quadlek.CommandResp{
			Text: "There was an error authenticating to Spotify.",
		}
		return err
	}

	cmdMsg.Command.Reply() <- &quadlek.CommandResp{
		Text: fmt.Sprintf("You need to be authenticate to Spotify to continue. Please visit %s to do this.", authUrl),
	}
	return nil
}

func nowPlaying(ctx context.Context, cmdChannel <-chan *quadlek.CommandMsg) {
	for {
		select {
		case cmdMsg := <-cmdChannel:
			err := cmdMsg.Store.UpdateRaw(func(bkt *bolt.Bucket) error {
				authToken := &AuthToken{}
				authTokenBytes := bkt.Get([]byte("authtoken-" + cmdMsg.Command.UserId))
				err := proto.Unmarshal(authTokenBytes, authToken)
				if err != nil {
					zap.L().Error("error unmarshalling auth token", zap.Error(err))
					return err
				}

				if authToken.Token == nil {
					err = authFlow(cmdMsg, bkt)
					if err != nil {
						zap.L().Error("error during auth flow", zap.Error(err))
						return err
					}
					return nil
				}

				client, needsReauth := getSpotifyClient(authToken)
				if needsReauth {
					err = authFlow(cmdMsg, bkt)
					if err != nil {
						return err
					}
					return nil
				}

				playing, err := client.PlayerCurrentlyPlaying()
				if err != nil {
					cmdMsg.Command.Reply() <- &quadlek.CommandResp{
						Text: "Unable to get currently playing.",
					}
					zap.L().Error("error getting currently playing.", zap.Error(err))
					return err
				}

				cmdMsg.Command.Reply() <- &quadlek.CommandResp{
					Text:      fmt.Sprintf("<@%s> is listening to %s", cmdMsg.Command.UserId, playing.Item.URI),
					InChannel: true,
				}

				return nil
			})
			if err != nil {
				continue
			}

		case <-ctx.Done():
			zap.L().Info("Exiting NowPlayingCommand.")
			return
		}
	}
}

func spotifyAuthorizeWebhook(ctx context.Context, whChannel <-chan *quadlek.WebhookMsg) {
	for {
		select {
		case whMsg := <-whChannel:
			// respond to webhook
			whMsg.ResponseWriter.WriteHeader(http.StatusOK)
			whMsg.ResponseWriter.Write([]byte{})
			whMsg.Done <- true

			// process webhook
			query := whMsg.Request.URL.Query()
			stateId, ok := query["state"]
			whMsg.Request.Body.Close()
			if !ok {
				zap.L().Error("invalid callback url")
				continue
			}

			err := whMsg.Store.UpdateRaw(func(bkt *bolt.Bucket) error {
				authStateBytes := bkt.Get([]byte("authstate-" + stateId[0]))
				authState := &AuthState{}
				err := proto.Unmarshal(authStateBytes, authState)
				if err != nil {
					whMsg.Bot.RespondToSlashCommand(authState.ResponseUrl, &quadlek.CommandResp{
						Text: "Sorry! There was an error logging you into Spotify.",
					})
					return err
				}

				now := time.Now().UnixNano()
				if authState.ExpireTime < now {
					bkt.Delete([]byte("authstate-" + stateId[0]))
					whMsg.Bot.RespondToSlashCommand(authState.ResponseUrl, &quadlek.CommandResp{
						Text: "Sorry! There was an error logging you into Spotify.",
					})
					return errors.New("Received expired auth request")
				}

				auth := getSpotifyAuth()
				token, err := auth.Token(stateId[0], whMsg.Request)
				if err != nil {
					whMsg.Bot.RespondToSlashCommand(authState.ResponseUrl, &quadlek.CommandResp{
						Text: "Sorry! There was an error logging you into Spotify.",
					})
					return err
				}

				authToken := &AuthToken{}
				authToken.PopulateFromOauthToken(token)
				authToken.Scopes = scopes

				tokenBytes, err := proto.Marshal(authToken)
				err = bkt.Put([]byte("authtoken-"+authState.UserId), tokenBytes)
				if err != nil {
					whMsg.Bot.RespondToSlashCommand(authState.ResponseUrl, &quadlek.CommandResp{
						Text: "Sorry! There was an error logging you into Spotify.",
					})
					zap.L().Error("error storing auth token.")
					return err
				}

				whMsg.Bot.RespondToSlashCommand(authState.ResponseUrl, &quadlek.CommandResp{
					Text: "Successfully logged into Spotify. Try your command again please.",
				})

				return nil
			})
			if err != nil {
				zap.L().Error("error authenticating to spotify", zap.Error(err))
				continue
			}

		case <-ctx.Done():
			zap.L().Info("Exiting spotify authorize command")
			return
		}
	}
}

func Register() quadlek.Plugin {
	return quadlek.MakePlugin(
		"spotify",
		[]quadlek.Command{
			quadlek.MakeCommand("nowplaying", nowPlaying),
		},
		[]quadlek.Hook{
			quadlek.MakeHook(saveSongsHook),
		},
		nil,
		[]quadlek.Webhook{
			quadlek.MakeWebhook("spotifyAuthorize", spotifyAuthorizeWebhook),
		},
		nil,
	)
}
<commit_msg>Ensure we have an item from spotify before returning a message<commit_after>//go:generate protoc --go_out=. spotify.proto

package spotify

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"os"
	"reflect"

	"go.uber.org/zap"

	"time"

	"strings"

	"github.com/boltdb/bolt"
	"github.com/golang/protobuf/proto"
	"github.com/jirwin/quadlek/quadlek"
	uuid "github.com/satori/go.uuid"
	"github.com/zmb3/spotify"
	"golang.org/x/oauth2"
)

const WebhookRoot = "https://%s/slack/plugin"

var scopes = []string{
	spotify.ScopePlaylistModifyPublic,
	spotify.ScopeUserReadCurrentlyPlaying,
}

func (at *AuthToken) GetOauthToken() *oauth2.Token {
	return &oauth2.Token{
		AccessToken:  at.Token.AccessToken,
		TokenType:    at.Token.TokenType,
		RefreshToken: at.Token.RefreshToken,
		Expiry:       time.Unix(at.Token.ExpiresAt/9000000000, 0), //FIXME I accidentally stored this as nanos
	}
}

func (at *AuthToken) PopulateFromOauthToken(token *oauth2.Token) {
	at.Token = &Token{
		AccessToken:  token.AccessToken,
		TokenType:    token.TokenType,
		RefreshToken: token.RefreshToken,
		ExpiresAt:    token.Expiry.UnixNano(),
	}
}

func startAuthFlow(stateId string) string {
	auth := getSpotifyAuth()
	url := auth.AuthURL(stateId)

	return url
}

func webhookRoot() string {
	return fmt.Sprintf(WebhookRoot, os.Getenv("SPOTIFY_WEBHOOK_DOMAIN"))
}

func getSpotifyAuth() spotify.Authenticator {
	return spotify.NewAuthenticator(fmt.Sprintf("%s/%s", webhookRoot(), "spotifyAuthorize"), scopes...)
}

func getSpotifyClient(authToken *AuthToken) (spotify.Client, bool) {

	auth := getSpotifyAuth()
	var token = authToken.GetOauthToken()
	client := auth.NewClient(token)

	if !reflect.DeepEqual(authToken.Scopes, scopes) {
		return client, true
	}

	_, err := client.CurrentUser()
	if err != nil {
		if strings.Contains(err.Error(), "token revoked") {
			return client, true
		}
	}
	return client, false
}

func authFlow(cmdMsg *quadlek.CommandMsg, bkt *bolt.Bucket) error {
	uuid := uuid.NewV4()

	stateId := uuid.String()
	authUrl := startAuthFlow(stateId)

	authState := &AuthState{
		Id:          stateId,
		UserId:      cmdMsg.Command.UserId,
		ResponseUrl: cmdMsg.Command.ResponseUrl,
		ExpireTime:  time.Now().UnixNano() + int64(time.Minute*15),
	}

	authStateBytes, err := proto.Marshal(authState)
	if err != nil {
		zap.L().Error("error marshalling auth state", zap.Error(err))
		return err
	}

	err = bkt.Put([]byte("authstate-"+stateId), authStateBytes)
	if err != nil {
		cmdMsg.Command.Reply() <- &quadlek.CommandResp{
			Text: "There was an error authenticating to Spotify.",
		}
		return err
	}

	cmdMsg.Command.Reply() <- &quadlek.CommandResp{
		Text: fmt.Sprintf("You need to be authenticate to Spotify to continue. Please visit %s to do this.", authUrl),
	}
	return nil
}

func nowPlaying(ctx context.Context, cmdChannel <-chan *quadlek.CommandMsg) {
	for {
		select {
		case cmdMsg := <-cmdChannel:
			err := cmdMsg.Store.UpdateRaw(func(bkt *bolt.Bucket) error {
				authToken := &AuthToken{}
				authTokenBytes := bkt.Get([]byte("authtoken-" + cmdMsg.Command.UserId))
				err := proto.Unmarshal(authTokenBytes, authToken)
				if err != nil {
					zap.L().Error("error unmarshalling auth token", zap.Error(err))
					return err
				}

				if authToken.Token == nil {
					err = authFlow(cmdMsg, bkt)
					if err != nil {
						zap.L().Error("error during auth flow", zap.Error(err))
						return err
					}
					return nil
				}

				client, needsReauth := getSpotifyClient(authToken)
				if needsReauth {
					err = authFlow(cmdMsg, bkt)
					if err != nil {
						return err
					}
					return nil
				}

				playing, err := client.PlayerCurrentlyPlaying()
				if err != nil {
					cmdMsg.Command.Reply() <- &quadlek.CommandResp{
						Text: "Unable to get currently playing.",
					}
					zap.L().Error("error getting currently playing.", zap.Error(err))
					return err
				}

				if playing != nil && playing.Item != nil {
					cmdMsg.Command.Reply() <- &quadlek.CommandResp{
						Text:      fmt.Sprintf("<@%s> is listening to %s", cmdMsg.Command.UserId, playing.Item.URI),
						InChannel: true,
					}
				}

				return nil
			})
			if err != nil {
				continue
			}

		case <-ctx.Done():
			zap.L().Info("Exiting NowPlayingCommand.")
			return
		}
	}
}

func spotifyAuthorizeWebhook(ctx context.Context, whChannel <-chan *quadlek.WebhookMsg) {
	for {
		select {
		case whMsg := <-whChannel:
			// respond to webhook
			whMsg.ResponseWriter.WriteHeader(http.StatusOK)
			whMsg.ResponseWriter.Write([]byte{})
			whMsg.Done <- true

			// process webhook
			query := whMsg.Request.URL.Query()
			stateId, ok := query["state"]
			whMsg.Request.Body.Close()
			if !ok {
				zap.L().Error("invalid callback url")
				continue
			}

			err := whMsg.Store.UpdateRaw(func(bkt *bolt.Bucket) error {
				authStateBytes := bkt.Get([]byte("authstate-" + stateId[0]))
				authState := &AuthState{}
				err := proto.Unmarshal(authStateBytes, authState)
				if err != nil {
					whMsg.Bot.RespondToSlashCommand(authState.ResponseUrl, &quadlek.CommandResp{
						Text: "Sorry! There was an error logging you into Spotify.",
					})
					return err
				}

				now := time.Now().UnixNano()
				if authState.ExpireTime < now {
					bkt.Delete([]byte("authstate-" + stateId[0]))
					whMsg.Bot.RespondToSlashCommand(authState.ResponseUrl, &quadlek.CommandResp{
						Text: "Sorry! There was an error logging you into Spotify.",
					})
					return errors.New("Received expired auth request")
				}

				auth := getSpotifyAuth()
				token, err := auth.Token(stateId[0], whMsg.Request)
				if err != nil {
					whMsg.Bot.RespondToSlashCommand(authState.ResponseUrl, &quadlek.CommandResp{
						Text: "Sorry! There was an error logging you into Spotify.",
					})
					return err
				}

				authToken := &AuthToken{}
				authToken.PopulateFromOauthToken(token)
				authToken.Scopes = scopes

				tokenBytes, err := proto.Marshal(authToken)
				err = bkt.Put([]byte("authtoken-"+authState.UserId), tokenBytes)
				if err != nil {
					whMsg.Bot.RespondToSlashCommand(authState.ResponseUrl, &quadlek.CommandResp{
						Text: "Sorry! There was an error logging you into Spotify.",
					})
					zap.L().Error("error storing auth token.")
					return err
				}

				whMsg.Bot.RespondToSlashCommand(authState.ResponseUrl, &quadlek.CommandResp{
					Text: "Successfully logged into Spotify. Try your command again please.",
				})

				return nil
			})
			if err != nil {
				zap.L().Error("error authenticating to spotify", zap.Error(err))
				continue
			}

		case <-ctx.Done():
			zap.L().Info("Exiting spotify authorize command")
			return
		}
	}
}

func Register() quadlek.Plugin {
	return quadlek.MakePlugin(
		"spotify",
		[]quadlek.Command{
			quadlek.MakeCommand("nowplaying", nowPlaying),
		},
		[]quadlek.Hook{
			quadlek.MakeHook(saveSongsHook),
		},
		nil,
		[]quadlek.Webhook{
			quadlek.MakeWebhook("spotifyAuthorize", spotifyAuthorizeWebhook),
		},
		nil,
	)
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"io/ioutil"
	"golang.org/x/oauth2/google"
	"golang.org/x/net/context"
	"google.golang.org/api/androidpublisher/v2"
	"fmt"
	"bytes"
)

func main() {
	ctx := context.Background()

	b, err := ioutil.ReadFile("client_secret.json")
	if err != nil {
		log.Fatalf("Unable to read client secret file: %v", err)
	}

	config, err := google.JWTConfigFromJSON(b, androidpublisher.AndroidpublisherScope)
	if err != nil {
		log.Fatalf("Unable to parse client secret file to config: %v", err)
	}

	client := config.Client(ctx)
	appId := "com.appspot.pistatium.tomorrow"
	res, err := client.Get("https://www.googleapis.com/androidpublisher/v2/applications/" + appId + "/reviews")
	if err != nil {
		log.Fatalf("Unable to access review API: %v", err)
	}

	bufbody := new(bytes.Buffer)
	bufbody.ReadFrom(res.Body)
	fmt.Print(bufbody)
}
<commit_msg>Async fetch<commit_after>package main

import (
	"log"
	"io/ioutil"
	"golang.org/x/oauth2/google"
	"golang.org/x/net/context"
	"google.golang.org/api/androidpublisher/v2"
	"fmt"
	"net/http"
	"sync"
)

var waitGroup sync.WaitGroup

func getReview(client *http.Client, appId string, result chan <- []byte ) {
	defer waitGroup.Done()
	res, err := client.Get("https://www.googleapis.com/androidpublisher/v2/applications/" + appId + "/reviews")
	if err != nil {
		log.Fatalf("Unable to access review API: %v", err)
		result <- nil
		return
	}
	body, _ := ioutil.ReadAll(res.Body)
	result <- body
}

func main() {
	ctx := context.Background()

	appIds := []string {
		"com.appspot.pistatium.tomorrow",
		"com.appspot.pistatium.tenseconds",
	}

	b, err := ioutil.ReadFile("client_secret.json")
	if err != nil {
		log.Fatalf("Unable to read client secret file: %v", err)
	}

	config, err := google.JWTConfigFromJSON(b, androidpublisher.AndroidpublisherScope)
	if err != nil {
		log.Fatalf("Unable to parse client secret file to config: %v", err)
	}

	client := config.Client(ctx)

	results := make(chan []byte, 2)

	for _, appId := range appIds {
		waitGroup.Add(1)
		go getReview(client, appId, results)
	}

	waitGroup.Wait()
	for _ := range appIds {
		fmt.Print(string(<- results))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	_ "encoding/json"
	"flag"
	"fmt"
	_ "io/ioutil"
	"os"
	_ "path/filepath"
	"strings"
	"time"

	"a4.io/blobstash/pkg/client/blobstore"
	"a4.io/blobstash/pkg/client/kvstore"
	"a4.io/blobstash/pkg/filetree/filetreeutil/node"
	"a4.io/blobstash/pkg/filetree/reader"
	"a4.io/blobstash/pkg/filetree/writer"

	"github.com/dustin/go-humanize"
	"github.com/google/subcommands"
	_ "github.com/mitchellh/go-homedir"
	_ "gopkg.in/yaml.v2"
)

func rerr(msg string, a ...interface{}) subcommands.ExitStatus {
	fmt.Printf(msg, a...)
	return subcommands.ExitFailure
}

func rsuccess(msg string, a ...interface{}) subcommands.ExitStatus {
	fmt.Printf(msg, a...)
	return subcommands.ExitSuccess
}

type filetreeLsCmd struct {
	bs      *blobstore.BlobStore
	kvs     *kvstore.KvStore
	showRef bool
}

func (*filetreeLsCmd) Name() string     { return "filetree-ls" }
func (*filetreeLsCmd) Synopsis() string { return "Display recent blobs" }
func (*filetreeLsCmd) Usage() string {
	return `recent :
	Display recent blobs.
`
}

func (l *filetreeLsCmd) SetFlags(f *flag.FlagSet) {
	f.BoolVar(&l.showRef, "show-ref", false, "Output references")
}

type Node struct {
	Name     string  `json:"name"`
	Type     string  `json:"type"`
	Size     int     `json:"size"`
	Mode     uint32  `json:"mode"`
	ModTime  string  `json:"mtime"`
	Hash     string  `json:"ref"`
	Children []*Node `json:"children,omitempty"`

	Meta *node.RawNode `json:"meta"`

	Data   map[string]interface{} `json:"data,omitempty"`
	XAttrs map[string]string      `json:"xattrs,omitempty"`
}

func displayNode(c *Node, showRef bool) {
	var ref string
	if showRef {
		ref = fmt.Sprintf("%s\t", c.Hash)
	}
	if c.Type == "file" {
		ref = fmt.Sprintf("%s%s\t", ref, humanize.Bytes(uint64(c.Size)))
	} else {
		ref = fmt.Sprintf("%s0 B\t", ref)
	}

	t, err := time.Parse(time.RFC3339, c.ModTime)
	if err != nil {
		panic(err)
	}
	name := c.Name
	if c.Type == "dir" {
		name = name + "/"
	}
	fmt.Printf("%s\t%s%s\n", t.Format("2006-01-02  15:04"), ref, name)
}

func (l *filetreeLsCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
	if f.NArg() == 0 {
		nodes := []*Node{}
		if err := l.kvs.Client().GetJSON("/api/filetree/fs/root?prefix=_filetree:root", nil, &nodes); err != nil {
			return rerr("failed to fetch root: %v", err)
		}
		for _, node := range nodes {
			displayNode(node, l.showRef)
		}
	}
	if f.NArg() == 1 {
		data := strings.Split(f.Arg(0), "/")
		path := strings.Join(data[1:], "/")
		// FIXME(tsileo): not store the type in the key anymore
		key, err := l.kvs.Get(fmt.Sprintf("_filetree:root:dir:%s", data[0]), -1)
		n := &Node{}
		if err != nil {
			return rerr("failed to fetch root key \"%s\": %v", data[0], err)
		}
		if err := l.kvs.Client().GetJSON(fmt.Sprintf("/api/filetree/fs/ref/%s/%s", key.Hash, path), nil, n); err != nil {
			return rerr("failed to fetch node: %v", err)
		}
		for _, c := range n.Children {
			displayNode(c, l.showRef)
		}
	}
	// TODO(tsileo): support filetree-ls rootname/subdir using fs/path API
	return subcommands.ExitSuccess
}

type filetreeDownloadCmd struct {
	bs  *blobstore.BlobStore
	kvs *kvstore.KvStore
}

func (*filetreeDownloadCmd) Name() string     { return "filetree-get" }
func (*filetreeDownloadCmd) Synopsis() string { return "Display recent blobs" }
func (*filetreeDownloadCmd) Usage() string {
	return `recent :
	Display recent blobs.
`
}

func (*filetreeDownloadCmd) SetFlags(_ *flag.FlagSet) {}

func (r *filetreeDownloadCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
	data := strings.Split(f.Arg(0), "/")
	path := strings.Join(data[1:], "/")
	// FIXME(tsileo): not store the type in the key anymore
	// FIXME(tsileo): move this into a filetree client
	key, err := r.kvs.Get(fmt.Sprintf("_filetree:root:dir:%s", data[0]), -1)
	n := &Node{}
	if err != nil {
		return rerr("failed to fetch root key \"%s\": %v", data[0], err)
	}
	if err := r.kvs.Client().GetJSON(fmt.Sprintf("/api/filetree/fs/ref/%s/%s", key.Hash, path), nil, n); err != nil {
		return rerr("failed to fetch node: %v", err)
	}

	// since the `Meta` type is used internally, and the `Hash` fields is the blake2b hash of the JSON encoded struct,
	// the Hash is omitted when converted to JSON
	if n.Meta != nil {
		n.Meta.Hash = n.Hash
	}

	downloader := reader.NewDownloader(r.bs)
	fmt.Printf("%+v\n", n)
	fmt.Printf("%+v\n", n.Meta)

	// If no target path is provided, use the filename
	tpath := f.Arg(1)
	if tpath == "" {
		tpath = n.Name
	}

	if err := downloader.Download(n.Meta, tpath); err != nil {
		return rerr("failed to download: %v", err)
	}

	return subcommands.ExitSuccess
}

type filetreePutCmd struct {
	bs  *blobstore.BlobStore
	kvs *kvstore.KvStore
}

func (*filetreePutCmd) Name() string     { return "filetree-put" }
func (*filetreePutCmd) Synopsis() string { return "Display recent blobs" }
func (*filetreePutCmd) Usage() string {
	return `recent :
	Display recent blobs.
`
}

func (*filetreePutCmd) SetFlags(_ *flag.FlagSet) {}

func (r *filetreePutCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
	finfo, err := os.Stat(f.Arg(0))
	switch {
	case os.IsNotExist(err):
		return rsuccess("path \"%s\" does not exist", f.Arg(0))
	case err == nil:
	default:
		return rerr("failed to stat file: %v", err)
	}
	var m *node.RawNode
	up := writer.NewUploader(r.bs)
	// It's a dir
	if finfo.IsDir() {
		m, err = up.PutDir(f.Arg(0))
		if err != nil {
			return rerr("failed to upload: %v", err)
		}
		// FIXME(tsileo): store a FiletreMeta{Hostname, OS} to display (os icon) (hostname) in the web ui
		// hostname, err := os.Hostname()
		// if err != nil {
		// 	return rerr("failed to get hostname: %v", err)
		// }
		// FIXME(tsileo): a way to set the hostname?
	} else {
		// It's a file
		m, err = up.PutFile(f.Arg(0))
		if err != nil {
			return rerr("failed to upload: %v", err)
		}
	}
	fmt.Printf("meta=%+v", m)
	if _, err := r.kvs.Put(fmt.Sprintf("_filetree:root:%s:%s", m.Type, m.Name), m.Hash, []byte("TODO meta data"), -1); err != nil {
		return rerr("faile to set kv entry: %v", err)
	}

	return subcommands.ExitSuccess
}

func main() {
	// TODO(tsileo) config file with server address and collection name
	opts := blobstore.DefaultOpts().SetHost(os.Getenv("BLOBSTASH_API_HOST"), os.Getenv("BLOBSTASH_API_KEY"))
	opts.SnappyCompression = false
	bs := blobstore.New(opts)
	// col := ds.Col("notes23")
	kvopts := kvstore.DefaultOpts().SetHost(os.Getenv("BLOBSTASH_API_HOST"), os.Getenv("BLOBSTASH_API_KEY"))

	// FIXME(tsileo): have GetJSON support snappy?
	kvopts.SnappyCompression = false

	kvs := kvstore.New(kvopts)

	subcommands.Register(subcommands.HelpCommand(), "")
	subcommands.Register(subcommands.FlagsCommand(), "")
	subcommands.Register(subcommands.CommandsCommand(), "")
	subcommands.Register(&filetreePutCmd{bs: bs, kvs: kvs}, "")
	subcommands.Register(&filetreeDownloadCmd{bs: bs, kvs: kvs}, "")
	subcommands.Register(&filetreeLsCmd{bs: bs, kvs: kvs}, "")

	flag.Parse()
	ctx := context.Background()
	os.Exit(int(subcommands.Execute(ctx)))
}
<commit_msg>cmd/blobstash-cli: fix build<commit_after>package main

import (
	"context"
	_ "encoding/json"
	"flag"
	"fmt"
	_ "io/ioutil"
	"os"
	_ "path/filepath"
	"strings"
	"time"

	"a4.io/blobstash/pkg/client/blobstore"
	"a4.io/blobstash/pkg/client/kvstore"
	"a4.io/blobstash/pkg/filetree/filetreeutil/node"
	"a4.io/blobstash/pkg/filetree/reader"
	"a4.io/blobstash/pkg/filetree/writer"

	"github.com/dustin/go-humanize"
	"github.com/google/subcommands"
	_ "github.com/mitchellh/go-homedir"
	_ "gopkg.in/yaml.v2"
)

func rerr(msg string, a ...interface{}) subcommands.ExitStatus {
	fmt.Printf(msg, a...)
	return subcommands.ExitFailure
}

func rsuccess(msg string, a ...interface{}) subcommands.ExitStatus {
	fmt.Printf(msg, a...)
	return subcommands.ExitSuccess
}

type filetreeLsCmd struct {
	bs      *blobstore.BlobStore
	kvs     *kvstore.KvStore
	showRef bool
}

func (*filetreeLsCmd) Name() string     { return "filetree-ls" }
func (*filetreeLsCmd) Synopsis() string { return "Display recent blobs" }
func (*filetreeLsCmd) Usage() string {
	return `recent :
	Display recent blobs.
`
}

func (l *filetreeLsCmd) SetFlags(f *flag.FlagSet) {
	f.BoolVar(&l.showRef, "show-ref", false, "Output references")
}

type Node struct {
	Name     string  `json:"name"`
	Type     string  `json:"type"`
	Size     int     `json:"size"`
	Mode     uint32  `json:"mode"`
	ModTime  string  `json:"mtime"`
	Hash     string  `json:"ref"`
	Children []*Node `json:"children,omitempty"`

	Meta *node.RawNode `json:"meta"`

	Data   map[string]interface{} `json:"data,omitempty"`
	XAttrs map[string]string      `json:"xattrs,omitempty"`
}

func displayNode(c *Node, showRef bool) {
	var ref string
	if showRef {
		ref = fmt.Sprintf("%s\t", c.Hash)
	}
	if c.Type == "file" {
		ref = fmt.Sprintf("%s%s\t", ref, humanize.Bytes(uint64(c.Size)))
	} else {
		ref = fmt.Sprintf("%s0 B\t", ref)
	}

	t, err := time.Parse(time.RFC3339, c.ModTime)
	if err != nil {
		panic(err)
	}
	name := c.Name
	if c.Type == "dir" {
		name = name + "/"
	}
	fmt.Printf("%s\t%s%s\n", t.Format("2006-01-02  15:04"), ref, name)
}

func (l *filetreeLsCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
	if f.NArg() == 0 {
		nodes := []*Node{}
		if err := l.kvs.Client().GetJSON("/api/filetree/fs/root?prefix=_filetree:root", nil, &nodes); err != nil {
			return rerr("failed to fetch root: %v", err)
		}
		for _, node := range nodes {
			displayNode(node, l.showRef)
		}
	}
	if f.NArg() == 1 {
		data := strings.Split(f.Arg(0), "/")
		path := strings.Join(data[1:], "/")
		// FIXME(tsileo): not store the type in the key anymore
		key, err := l.kvs.Get(fmt.Sprintf("_filetree:root:dir:%s", data[0]), -1)
		n := &Node{}
		if err != nil {
			return rerr("failed to fetch root key \"%s\": %v", data[0], err)
		}
		if err := l.kvs.Client().GetJSON(fmt.Sprintf("/api/filetree/fs/ref/%s/%s", key.Hash, path), nil, n); err != nil {
			return rerr("failed to fetch node: %v", err)
		}
		for _, c := range n.Children {
			displayNode(c, l.showRef)
		}
	}
	// TODO(tsileo): support filetree-ls rootname/subdir using fs/path API
	return subcommands.ExitSuccess
}

type filetreeDownloadCmd struct {
	bs  *blobstore.BlobStore
	kvs *kvstore.KvStore
}

func (*filetreeDownloadCmd) Name() string     { return "filetree-get" }
func (*filetreeDownloadCmd) Synopsis() string { return "Display recent blobs" }
func (*filetreeDownloadCmd) Usage() string {
	return `recent :
	Display recent blobs.
`
}

func (*filetreeDownloadCmd) SetFlags(_ *flag.FlagSet) {}

func (r *filetreeDownloadCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
	data := strings.Split(f.Arg(0), "/")
	path := strings.Join(data[1:], "/")
	// FIXME(tsileo): not store the type in the key anymore
	// FIXME(tsileo): move this into a filetree client
	key, err := r.kvs.Get(fmt.Sprintf("_filetree:root:dir:%s", data[0]), -1)
	n := &Node{}
	if err != nil {
		return rerr("failed to fetch root key \"%s\": %v", data[0], err)
	}
	if err := r.kvs.Client().GetJSON(fmt.Sprintf("/api/filetree/fs/ref/%s/%s", key.Hash, path), nil, n); err != nil {
		return rerr("failed to fetch node: %v", err)
	}

	// since the `Meta` type is used internally, and the `Hash` fields is the blake2b hash of the JSON encoded struct,
	// the Hash is omitted when converted to JSON
	if n.Meta != nil {
		n.Meta.Hash = n.Hash
	}

	downloader := reader.NewDownloader(r.bs)
	fmt.Printf("%+v\n", n)
	fmt.Printf("%+v\n", n.Meta)

	// If no target path is provided, use the filename
	tpath := f.Arg(1)
	if tpath == "" {
		tpath = n.Name
	}

	if err := downloader.Download(context.TODO(), n.Meta, tpath); err != nil {
		return rerr("failed to download: %v", err)
	}

	return subcommands.ExitSuccess
}

type filetreePutCmd struct {
	bs  *blobstore.BlobStore
	kvs *kvstore.KvStore
}

func (*filetreePutCmd) Name() string     { return "filetree-put" }
func (*filetreePutCmd) Synopsis() string { return "Display recent blobs" }
func (*filetreePutCmd) Usage() string {
	return `recent :
	Display recent blobs.
`
}

func (*filetreePutCmd) SetFlags(_ *flag.FlagSet) {}

func (r *filetreePutCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
	finfo, err := os.Stat(f.Arg(0))
	switch {
	case os.IsNotExist(err):
		return rsuccess("path \"%s\" does not exist", f.Arg(0))
	case err == nil:
	default:
		return rerr("failed to stat file: %v", err)
	}
	var m *node.RawNode
	up := writer.NewUploader(r.bs)
	// It's a dir
	if finfo.IsDir() {
		m, err = up.PutDir(f.Arg(0))
		if err != nil {
			return rerr("failed to upload: %v", err)
		}
		// FIXME(tsileo): store a FiletreMeta{Hostname, OS} to display (os icon) (hostname) in the web ui
		// hostname, err := os.Hostname()
		// if err != nil {
		// 	return rerr("failed to get hostname: %v", err)
		// }
		// FIXME(tsileo): a way to set the hostname?
	} else {
		// It's a file
		m, err = up.PutFile(f.Arg(0))
		if err != nil {
			return rerr("failed to upload: %v", err)
		}
	}
	fmt.Printf("meta=%+v", m)
	if _, err := r.kvs.Put(fmt.Sprintf("_filetree:root:%s:%s", m.Type, m.Name), m.Hash, []byte("TODO meta data"), -1); err != nil {
		return rerr("faile to set kv entry: %v", err)
	}

	return subcommands.ExitSuccess
}

func main() {
	// TODO(tsileo) config file with server address and collection name
	opts := blobstore.DefaultOpts().SetHost(os.Getenv("BLOBSTASH_API_HOST"), os.Getenv("BLOBSTASH_API_KEY"))
	opts.SnappyCompression = false
	bs := blobstore.New(opts)
	// col := ds.Col("notes23")
	kvopts := kvstore.DefaultOpts().SetHost(os.Getenv("BLOBSTASH_API_HOST"), os.Getenv("BLOBSTASH_API_KEY"))

	// FIXME(tsileo): have GetJSON support snappy?
	kvopts.SnappyCompression = false

	kvs := kvstore.New(kvopts)

	subcommands.Register(subcommands.HelpCommand(), "")
	subcommands.Register(subcommands.FlagsCommand(), "")
	subcommands.Register(subcommands.CommandsCommand(), "")
	subcommands.Register(&filetreePutCmd{bs: bs, kvs: kvs}, "")
	subcommands.Register(&filetreeDownloadCmd{bs: bs, kvs: kvs}, "")
	subcommands.Register(&filetreeLsCmd{bs: bs, kvs: kvs}, "")

	flag.Parse()
	ctx := context.Background()
	os.Exit(int(subcommands.Execute(ctx)))
}
<|endoftext|>