<commit_before>package blob

import (
	"crypto/sha1"
	"database/sql"
	"encoding/hex"
	"fmt"
	_ "github.com/herenow/go-crate"
	"io"
	"net/http"
	"time"
)

type Driver struct {
	url string
	db  *sql.DB
}

type Table struct {
	Name string
	drv  *Driver
	c    *http.Client
}

// DownloadError represents an error that occurs when downloading a blob.
type DownloadError struct {
	Status string
	StatusCode int
	Message string
}

// Error returns a string representation of the DownloadError.
func (e DownloadError) Error() string {
	return e.Message
}

// New creates a new connection with crate server
func New(crate_url string) (*Driver, error) {
	db, err := sql.Open("crate", crate_url)
	if err != nil {
		return nil, err
	}
	return &Driver{
		url: crate_url,
		db:  db,
	}, nil
}

// NewTable create new blob table with name and extra int to specify
// shards(the second argument) and
// replicas(by the third int argument)
func (d *Driver) NewTable(name string, shards ...int) (*Table, error) {
	sql := fmt.Sprintf(
		"create blob table %s",
		name,
	)
	if len(shards) == 1 {
		sql = fmt.Sprintf(
			"create blob table %s clustered into %d shards",
			name, shards[0],
		)
	}
	if len(shards) >= 2 {
		sql = fmt.Sprintf(
			"create blob table %s clustered into %d shards with (number_of_replicas=%d)",
			name, shards[0], shards[1],
		)
	}
	_, err := d.db.Exec(sql)
	if err != nil {
		return nil, err
	}
	return &Table{
		Name: name,
		drv:  d,
		c:    new(http.Client),
	}, nil
}

// Get an existing table from the crate server
// or error when this table does not exist
func (d *Driver) GetTable(name string) (*Table, error) {
	row := d.db.QueryRow(
		"select table_name from information_schema.tables where table_name = ? and table_schema = 'blob'",
		name,
	)
	if err := row.Scan(&name); err != nil {
		return nil, err
	}
	return &Table{
		Name: name,
		drv:  d,
		c:    new(http.Client),
	}, nil
}

// Close the database connection
func (d *Driver) Close() error {
	if d.db != nil {
		return d.db.Close()
	}
	return nil
}

type Record struct {
	Digest       string
	LastModified time.Time
}

// Sha1Digest calculates the sha1 digest for the io.Reader
func Sha1Digest(r io.Reader) string {
	h := sha1.New()
	io.Copy(h, r)
	return hex.EncodeToString(h.Sum(nil))
}

func (t *Table) url(digest string) string {
	return fmt.Sprintf("%s/_blobs/%s/%s", t.drv.url, t.Name, digest)
}

// Upload upload the blob(r) with sha1 hash(digest)
func (t *Table) Upload(digest string, r io.Reader) (*Record, error) {
	req, err := t.newRequest("PUT", digest, r)
	if err != nil {
		return nil, err
	}
	resp, err := t.c.Do(req)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 201 {
		return nil, fmt.Errorf("Upload failed: %s", resp.Status)
	}
	return &Record{
		Digest: digest,
	}, nil
}

// UploadEx upload a io.ReadSeeker, and computes the sha1 hash automatically
func (t *Table) UploadEx(r io.ReadSeeker) (*Record, error) {
	digest := Sha1Digest(r)
	_, err := r.Seek(0, 0)
	if err != nil {
		return nil, err
	}
	req, err := t.newRequest("PUT", digest, r)
	if err != nil {
		return nil, err
	}
	resp, err = t.c.Do(req)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 201 {
		return nil, fmt.Errorf("Upload failed: %s", resp.Status)
	}
	return &Record{
		Digest: digest,
	}, nil
}

// List all blobs inside a blob table
func (t *Table) List() (*sql.Rows, error) {
	query := fmt.Sprintf("select digest, last_modified from blob.%s", t.Name)
	rows, err := t.drv.db.Query(query)
	if err != nil {
		return nil, err
	}
	return rows, err
}

// Is the blob specified by the digest exist in the table
func (t *Table) Has(digest string) (bool, error) {
	req, err := t.newRequest("HEAD", digest, nil)
	if err != nil {
		return false, err
	}
	resp, err := t.c.Do(req)
	if err != nil {
		return false, err
	}
	if resp.StatusCode == http.StatusOK {
		return true, nil
	}
	return false, nil
}

// Download a blob in a blob table with the specific digest
func (t *Table) Download(digest string) (io.ReadCloser, error) {
	req, err := t.newRequest("GET", digest, nil)
	if err != nil {
		return nil, err
	}
	resp, err := t.c.Do(req)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		return resp.Body, &DownloadError{resp.Status, resp.StatusCode, "problem downloading blob"}
	}
	return resp.Body, nil
}

// Delete a blob in a blob table with the specific digest
func (t *Table) Delete(digest string) error {
	req, err := t.newRequest("DELETE", digest, nil)
	if err != nil {
		return err
	}
	resp, err := t.c.Do(req)
	if err != nil {
		return err
	}
	if resp.StatusCode == http.StatusNoContent {
		return nil
	}
	return fmt.Errorf("%s", resp.Status)
}

// Drop the blob table
func (t *Table) Drop() error {
	sql := fmt.Sprintf("drop blob table \"%s\"", t.Name)
	_, err := t.drv.db.Exec(sql)
	return err
}

func (t *Table) newRequest(method, digest string, body io.Reader) (*http.Request, error) {
	req, err := http.NewRequest(method, t.url(digest), body)
	if err != nil {
		return nil ,err
	}
	// in some cases where trying to upload or download a blob resulted in non 2xx response,
	// it seems Crate was not closing the connection (ticket to be opened soon against Crate)
	// which resulted in strange behavior when requests were made after getting an error.
	//
	// For example, if downloading a blob result in a 404, attempting to download another
	// blob would also result ina  404 even if that second blob absolutely was there.
	req.Header.Set("Connection", "close")
	return req, nil
}
<commit_msg>Update blob.go<commit_after>package blob

import (
	"crypto/sha1"
	"database/sql"
	"encoding/hex"
	"fmt"
	_ "github.com/herenow/go-crate"
	"io"
	"net/http"
	"time"
)

type Driver struct {
	url string
	db  *sql.DB
}

type Table struct {
	Name string
	drv  *Driver
	c    *http.Client
}

// DownloadError represents an error that occurs when downloading a blob.
type DownloadError struct {
	Status string
	StatusCode int
	Message string
}

// Error returns a string representation of the DownloadError.
func (e DownloadError) Error() string {
	return e.Message
}

// New creates a new connection with crate server
func New(crate_url string) (*Driver, error) {
	db, err := sql.Open("crate", crate_url)
	if err != nil {
		return nil, err
	}
	return &Driver{
		url: crate_url,
		db:  db,
	}, nil
}

// NewTable create new blob table with name and extra int to specify
// shards(the second argument) and
// replicas(by the third int argument)
func (d *Driver) NewTable(name string, shards ...int) (*Table, error) {
	sql := fmt.Sprintf(
		"create blob table %s",
		name,
	)
	if len(shards) == 1 {
		sql = fmt.Sprintf(
			"create blob table %s clustered into %d shards",
			name, shards[0],
		)
	}
	if len(shards) >= 2 {
		sql = fmt.Sprintf(
			"create blob table %s clustered into %d shards with (number_of_replicas=%d)",
			name, shards[0], shards[1],
		)
	}
	_, err := d.db.Exec(sql)
	if err != nil {
		return nil, err
	}
	return &Table{
		Name: name,
		drv:  d,
		c:    new(http.Client),
	}, nil
}

// Get an existing table from the crate server
// or error when this table does not exist
func (d *Driver) GetTable(name string) (*Table, error) {
	row := d.db.QueryRow(
		"select table_name from information_schema.tables where table_name = ? and table_schema = 'blob'",
		name,
	)
	if err := row.Scan(&name); err != nil {
		return nil, err
	}
	return &Table{
		Name: name,
		drv:  d,
		c:    new(http.Client),
	}, nil
}

// Close the database connection
func (d *Driver) Close() error {
	if d.db != nil {
		return d.db.Close()
	}
	return nil
}

type Record struct {
	Digest       string
	LastModified time.Time
}

// Sha1Digest calculates the sha1 digest for the io.Reader
func Sha1Digest(r io.Reader) string {
	h := sha1.New()
	io.Copy(h, r)
	return hex.EncodeToString(h.Sum(nil))
}

func (t *Table) url(digest string) string {
	return fmt.Sprintf("%s/_blobs/%s/%s", t.drv.url, t.Name, digest)
}

// Upload upload the blob(r) with sha1 hash(digest)
func (t *Table) Upload(digest string, r io.Reader) (*Record, error) {
	req, err := t.newRequest("PUT", digest, r)
	if err != nil {
		return nil, err
	}
	resp, err := t.c.Do(req)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 201 {
		return nil, fmt.Errorf("Upload failed: %s", resp.Status)
	}
	return &Record{
		Digest: digest,
	}, nil
}

// UploadEx upload a io.ReadSeeker, and computes the sha1 hash automatically
func (t *Table) UploadEx(r io.ReadSeeker) (*Record, error) {
	digest := Sha1Digest(r)
	_, err := r.Seek(0, 0)
	if err != nil {
		return nil, err
	}
	req, err := t.newRequest("PUT", digest, r)
	if err != nil {
		return nil, err
	}
	resp, err := t.c.Do(req)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 201 {
		return nil, fmt.Errorf("Upload failed: %s", resp.Status)
	}
	return &Record{
		Digest: digest,
	}, nil
}

// List all blobs inside a blob table
func (t *Table) List() (*sql.Rows, error) {
	query := fmt.Sprintf("select digest, last_modified from blob.%s", t.Name)
	rows, err := t.drv.db.Query(query)
	if err != nil {
		return nil, err
	}
	return rows, err
}

// Is the blob specified by the digest exist in the table
func (t *Table) Has(digest string) (bool, error) {
	req, err := t.newRequest("HEAD", digest, nil)
	if err != nil {
		return false, err
	}
	resp, err := t.c.Do(req)
	if err != nil {
		return false, err
	}
	if resp.StatusCode == http.StatusOK {
		return true, nil
	}
	return false, nil
}

// Download a blob in a blob table with the specific digest
func (t *Table) Download(digest string) (io.ReadCloser, error) {
	req, err := t.newRequest("GET", digest, nil)
	if err != nil {
		return nil, err
	}
	resp, err := t.c.Do(req)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		return resp.Body, &DownloadError{resp.Status, resp.StatusCode, "problem downloading blob"}
	}
	return resp.Body, nil
}

// Delete a blob in a blob table with the specific digest
func (t *Table) Delete(digest string) error {
	req, err := t.newRequest("DELETE", digest, nil)
	if err != nil {
		return err
	}
	resp, err := t.c.Do(req)
	if err != nil {
		return err
	}
	if resp.StatusCode == http.StatusNoContent {
		return nil
	}
	return fmt.Errorf("%s", resp.Status)
}

// Drop the blob table
func (t *Table) Drop() error {
	sql := fmt.Sprintf("drop blob table \"%s\"", t.Name)
	_, err := t.drv.db.Exec(sql)
	return err
}

func (t *Table) newRequest(method, digest string, body io.Reader) (*http.Request, error) {
	req, err := http.NewRequest(method, t.url(digest), body)
	if err != nil {
		return nil ,err
	}
	// in some cases where trying to upload or download a blob resulted in non 2xx response,
	// it seems Crate was not closing the connection (ticket to be opened soon against Crate)
	// which resulted in strange behavior when requests were made after getting an error.
	//
	// For example, if downloading a blob result in a 404, attempting to download another
	// blob would also result ina  404 even if that second blob absolutely was there.
	req.Header.Set("Connection", "close")
	return req, nil
}
<|endoftext|>


<commit_before>package conn

import (
	"fmt"
	"regexp"

	"github.com/jordwest/imap-server/mailstore"
)

type command struct {
	match   *regexp.Regexp
	handler func(commandArgs, *Conn)
}

type commandArgs []string

func (a commandArgs) FullCommand() string {
	return a[0]
}

func (a commandArgs) ID() string {
	return a[1]
}

func (a commandArgs) Arg(i int) string {
	return a[i+2]
}

func (a commandArgs) DebugPrint(prompt string) {
	fmt.Printf("%s\n", prompt)
	fmt.Printf("\tFull Command: %s\n", a.FullCommand())
	fmt.Printf("\t.ID(): %s\n", a.ID())
	for index, arg := range a {
		if index < 2 {
			continue
		}
		fmt.Printf("\t.Arg(%d): \"%s\"\n", index-2, arg)
	}
}

var commands []command

// Register all supported client command handlers
// with the server. This function is run on server startup and
// panics if a command regex is invalid.
func init() {
	commands = make([]command, 0)

	// A sequence set consists only of digits, colons, stars and commas.
	// eg: 5,9,10:15,256:*,566
	sequenceSet := "[\\d\\:\\*\\,]+"

	registerCommand("(?i:CAPABILITY)", cmdCapability)
	registerCommand("(?i:LOGIN) \"([A-z0-9]+)\" \"([A-z0-9]+)\"", cmdLogin)
	registerCommand("(?i:AUTHENTICATE PLAIN)", cmdAuthPlain)
	registerCommand("(?i:LIST) \"?([A-z0-9]+)?\"? \"?([A-z0-9*]+)?\"?", cmdList)
	registerCommand("(?i:LSUB)", cmdLSub)
	registerCommand("(?i:LOGOUT)", cmdLogout)
	registerCommand("(?i:NOOP)", cmdNoop)
	registerCommand("(?i:CLOSE)", cmdClose)
	registerCommand("(?i:SELECT) \"?([A-z0-9]+)?\"?", cmdSelect)
	registerCommand("(?i:EXAMINE) \"?([A-z0-9]+)\"?", cmdExamine)
	registerCommand("(?i:STATUS) \"?([A-z0-9/]+)\"? \\(([A-z\\s]+)\\)", cmdStatus)
	registerCommand("((?i)UID )?(?i:FETCH) ("+sequenceSet+") \\(([A-z0-9\\s\\(\\)\\[\\]\\.-]+)\\)", cmdFetch)

	// APPEND "INBOX" (\Seen) {310}
	// APPEND "INBOX" (\Seen) "21-Jun-2015 01:00:25 +0900" {310}
	// APPEND "INBOX" {310}
	registerCommand("(?i:APPEND) \"?([A-z0-9/]+)\"?(?: \\(([\\\\A-z\\s]+)\\))?(?: \"([A-Za-z0-9\\-\\:\\+ ]+)\")? {([0-9]+)}", cmdAppend)

	// STORE 2:4 +FLAGS (\Deleted)       Mark messages as deleted
	// STORE 2:4 -FLAGS (\Seen)          Mark messages as unseen
	// STORE 2:4 FLAGS (\Seen \Deleted)  Replace flags
	registerCommand("((?i)UID )?(?i:STORE) ("+sequenceSet+") ([\\+\\-])?(?i:FLAGS(\\.SILENT)?) \\(?([\\\\A-z0-9\\s]+)\\)?", cmdStoreFlags)
}

func registerCommand(matchExpr string, handleFunc func(commandArgs, *Conn)) error {
	// Add command identifier to beginning of command
	matchExpr = "([A-z0-9\\.]+) " + matchExpr

	newRE := regexp.MustCompile(matchExpr)
	c := command{match: newRE, handler: handleFunc}
	commands = append(commands, c)
	return nil
}

// Write out the info for a mailbox (used in both SELECT and EXAMINE)
func writeMailboxInfo(c *Conn, m mailstore.Mailbox) {
	fmt.Fprintf(c, "* %d EXISTS\r\n", m.Messages())
	fmt.Fprintf(c, "* %d RECENT\r\n", m.Recent())
	fmt.Fprintf(c, "* OK [UNSEEN %d]\r\n", m.Unseen())
	fmt.Fprintf(c, "* OK [UIDNEXT %d]\r\n", m.NextUID())
	fmt.Fprintf(c, "* OK [UIDVALIDITY %d]\r\n", 250)
	fmt.Fprintf(c, "* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)\r\n")
}

func cmdNA(args commandArgs, c *Conn) {
	c.writeResponse(args.ID(), "BAD Not implemented")
}
<commit_msg>Return req ID when unrecognised command is attempted. Closes #20<commit_after>package conn

import (
	"fmt"
	"regexp"

	"github.com/jordwest/imap-server/mailstore"
)

type command struct {
	match   *regexp.Regexp
	handler func(commandArgs, *Conn)
}

type commandArgs []string

func (a commandArgs) FullCommand() string {
	return a[0]
}

func (a commandArgs) ID() string {
	return a[1]
}

func (a commandArgs) Arg(i int) string {
	return a[i+2]
}

func (a commandArgs) DebugPrint(prompt string) {
	fmt.Printf("%s\n", prompt)
	fmt.Printf("\tFull Command: %s\n", a.FullCommand())
	fmt.Printf("\t.ID(): %s\n", a.ID())
	for index, arg := range a {
		if index < 2 {
			continue
		}
		fmt.Printf("\t.Arg(%d): \"%s\"\n", index-2, arg)
	}
}

var commands []command

// Register all supported client command handlers
// with the server. This function is run on server startup and
// panics if a command regex is invalid.
func init() {
	commands = make([]command, 0)

	// A sequence set consists only of digits, colons, stars and commas.
	// eg: 5,9,10:15,256:*,566
	sequenceSet := "[\\d\\:\\*\\,]+"

	registerCommand("(?i:CAPABILITY)", cmdCapability)
	registerCommand("(?i:LOGIN) \"([A-z0-9]+)\" \"([A-z0-9]+)\"", cmdLogin)
	registerCommand("(?i:AUTHENTICATE PLAIN)", cmdAuthPlain)
	registerCommand("(?i:LIST) \"?([A-z0-9]+)?\"? \"?([A-z0-9*]+)?\"?", cmdList)
	registerCommand("(?i:LSUB)", cmdLSub)
	registerCommand("(?i:LOGOUT)", cmdLogout)
	registerCommand("(?i:NOOP)", cmdNoop)
	registerCommand("(?i:CLOSE)", cmdClose)
	registerCommand("(?i:SELECT) \"?([A-z0-9]+)?\"?", cmdSelect)
	registerCommand("(?i:EXAMINE) \"?([A-z0-9]+)\"?", cmdExamine)
	registerCommand("(?i:STATUS) \"?([A-z0-9/]+)\"? \\(([A-z\\s]+)\\)", cmdStatus)
	registerCommand("((?i)UID )?(?i:FETCH) ("+sequenceSet+") \\(([A-z0-9\\s\\(\\)\\[\\]\\.-]+)\\)", cmdFetch)

	// APPEND "INBOX" (\Seen) {310}
	// APPEND "INBOX" (\Seen) "21-Jun-2015 01:00:25 +0900" {310}
	// APPEND "INBOX" {310}
	registerCommand("(?i:APPEND) \"?([A-z0-9/]+)\"?(?: \\(([\\\\A-z\\s]+)\\))?(?: \"([A-Za-z0-9\\-\\:\\+ ]+)\")? {([0-9]+)}", cmdAppend)

	// STORE 2:4 +FLAGS (\Deleted)       Mark messages as deleted
	// STORE 2:4 -FLAGS (\Seen)          Mark messages as unseen
	// STORE 2:4 FLAGS (\Seen \Deleted)  Replace flags
	registerCommand("((?i)UID )?(?i:STORE) ("+sequenceSet+") ([\\+\\-])?(?i:FLAGS(\\.SILENT)?) \\(?([\\\\A-z0-9\\s]+)\\)?", cmdStoreFlags)

	registerCommand("", cmdNA)
}

func registerCommand(matchExpr string, handleFunc func(commandArgs, *Conn)) error {
	// Add command identifier to beginning of command
	matchExpr = "([A-z0-9\\.]+) " + matchExpr

	newRE := regexp.MustCompile(matchExpr)
	c := command{match: newRE, handler: handleFunc}
	commands = append(commands, c)
	return nil
}

// Write out the info for a mailbox (used in both SELECT and EXAMINE)
func writeMailboxInfo(c *Conn, m mailstore.Mailbox) {
	fmt.Fprintf(c, "* %d EXISTS\r\n", m.Messages())
	fmt.Fprintf(c, "* %d RECENT\r\n", m.Recent())
	fmt.Fprintf(c, "* OK [UNSEEN %d]\r\n", m.Unseen())
	fmt.Fprintf(c, "* OK [UIDNEXT %d]\r\n", m.NextUID())
	fmt.Fprintf(c, "* OK [UIDVALIDITY %d]\r\n", 250)
	fmt.Fprintf(c, "* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)\r\n")
}

func cmdNA(args commandArgs, c *Conn) {
	c.writeResponse(args.ID(), "BAD Not implemented")
}
<|endoftext|>


<commit_before>package handler

import (
	"strings"
)

func explode(r []rune, s, n string) []string {
	z := string(r)
	if z != "" {
		z += n
	}
	return strings.Split(z+s, n)
}
<commit_msg>Removing unused file<commit_after><|endoftext|>


<commit_before>package slack

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/nlopes/slack"

	"github.com/keel-hq/keel/bot"
	"github.com/keel-hq/keel/constants"
	"github.com/keel-hq/keel/version"

	log "github.com/sirupsen/logrus"
)

// SlackImplementer - implementes slack HTTP functionality, used to
// send messages with attachments
type SlackImplementer interface {
	PostMessage(channel, text string, params slack.PostMessageParameters) (string, string, error)
}

// Bot - main slack bot container
type Bot struct {
	id   string // bot id
	name string // bot name

	users map[string]string

	msgPrefix string

	slackClient *slack.Client
	slackRTM    *slack.RTM

	slackHTTPClient SlackImplementer

	approvalsChannel string // slack approvals channel name

	ctx                context.Context
	botMessagesChannel chan *bot.BotMessage
	approvalsRespCh    chan *bot.ApprovalResponse
}

func init() {
	bot.RegisterBot("slack", &Bot{})
}

func (b *Bot) Configure(approvalsRespCh chan *bot.ApprovalResponse, botMessagesChannel chan *bot.BotMessage) bool {
	if os.Getenv(constants.EnvSlackToken) != "" {

		b.name = "keel"
		if os.Getenv(constants.EnvSlackBotName) != "" {
			b.name = os.Getenv(constants.EnvSlackBotName)
		}

		token := os.Getenv(constants.EnvSlackToken)
		client := slack.New(token)

		b.approvalsChannel = "general"
		if os.Getenv(constants.EnvSlackApprovalsChannel) != "" {
			b.approvalsChannel = os.Getenv(constants.EnvSlackApprovalsChannel)
		}

		b.slackClient = client
		b.slackHTTPClient = client
		b.approvalsRespCh = approvalsRespCh
		b.botMessagesChannel = botMessagesChannel

		return true
	}
	log.Info("bot.slack.Configure(): Slack approval bot is not configured")
	return false
}

// Start - start bot
func (b *Bot) Start(ctx context.Context) error {
	// setting root context
	b.ctx = ctx

	users, err := b.slackClient.GetUsers()
	if err != nil {
		return err
	}

	b.users = map[string]string{}

	for _, user := range users {
		switch user.Name {
		case b.name:
			if user.IsBot {
				b.id = user.ID
			}
		default:
			continue
		}
	}
	if b.id == "" {
		return errors.New("could not find bot in the list of names, check if the bot is called \"" + b.name + "\" ")
	}

	b.msgPrefix = strings.ToLower("<@" + b.id + ">")

	go b.startInternal()

	return nil
}

func (b *Bot) startInternal() error {
	b.slackRTM = b.slackClient.NewRTM()

	go b.slackRTM.ManageConnection()
	for {
		select {
		case <-b.ctx.Done():
			return nil

		case msg := <-b.slackRTM.IncomingEvents:
			switch ev := msg.Data.(type) {
			case *slack.HelloEvent:
				// Ignore hello

			case *slack.ConnectedEvent:
				// fmt.Println("Infos:", ev.Info)
				// fmt.Println("Connection counter:", ev.ConnectionCount)
				// Replace #general with your Channel ID
				// b.slackRTM.SendMessage(b.slackRTM.NewOutgoingMessage("Hello world", "#general"))

			case *slack.MessageEvent:
				b.handleMessage(ev)
			case *slack.PresenceChangeEvent:
				// fmt.Printf("Presence Change: %v\n", ev)

			// case *slack.LatencyReport:
			// 	fmt.Printf("Current latency: %v\n", ev.Value)

			case *slack.RTMError:
				fmt.Printf("Error: %s\n", ev.Error())

			case *slack.InvalidAuthEvent:
				fmt.Printf("Invalid credentials")
				return fmt.Errorf("invalid credentials")

			default:

				// Ignore other events..
				// fmt.Printf("Unexpected: %v\n", msg.Data)
			}
		}
	}
}

func (b *Bot) postMessage(title, message, color string, fields []slack.AttachmentField) error {
	params := slack.NewPostMessageParameters()
	params.Username = b.name

	params.Attachments = []slack.Attachment{
		slack.Attachment{
			Fallback: message,
			Color:    color,
			Fields:   fields,
			Footer:   fmt.Sprintf("https://keel.sh %s", version.GetKeelVersion().Version),
			Ts:       json.Number(strconv.Itoa(int(time.Now().Unix()))),
		},
	}

	_, _, err := b.slackHTTPClient.PostMessage(b.approvalsChannel, "", params)
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
		}).Error("bot.postMessage: failed to send message")
	}
	return err
}

// TODO(k): cache results in a map or get this info on startup. Although
// if channel was then recreated (unlikely), we would miss results
func (b *Bot) isApprovalsChannel(event *slack.MessageEvent) bool {

	info := b.slackRTM.GetInfo()

	for _, ch := range info.Channels {
		if ch.ID == event.Channel && ch.Name == b.approvalsChannel {
			return true
		}
	}

	// checking private channels
	for _, gr := range info.Groups {
		if gr.ID == event.Channel && gr.Name == b.approvalsChannel {
			return true
		}
	}

	return false
}

func (b *Bot) handleMessage(event *slack.MessageEvent) {
	if event.BotID != "" || event.User == "" || event.SubType == "bot_message" {
		log.WithFields(log.Fields{
			"event_bot_ID":  event.BotID,
			"event_user":    event.User,
			"msg":           event.Text,
			"event_subtype": event.SubType,
		}).Info("handleMessage: ignoring message")
		return
	}

	eventText := strings.Trim(strings.ToLower(event.Text), " \n\r")

	if !b.isBotMessage(event, eventText) {
		return
	}

	eventText = b.trimBot(eventText)

	// only accepting approvals from approvals channel
	if b.isApprovalsChannel(event) {
		approval, ok := bot.IsApproval(event.User, eventText)
		if ok {
			b.approvalsRespCh <- approval
			return
		}
	} else {
		log.Warnf("not approvals channel: %s", event.Channel)
	}

	b.botMessagesChannel <- &bot.BotMessage{
		Message: eventText,
		User:    event.User,
		Channel: event.Channel,
		Name:    "slack",
	}
	return
}

func (b *Bot) Respond(text string, channel string) {
	b.slackRTM.SendMessage(b.slackRTM.NewOutgoingMessage(formatAsSnippet(text), channel))
}

func (b *Bot) isBotMessage(event *slack.MessageEvent, eventText string) bool {
	prefixes := []string{
		b.msgPrefix,
		"keel",
	}

	for _, p := range prefixes {
		if strings.HasPrefix(eventText, p) {
			return true
		}
	}

	// Direct message channels always starts with 'D'
	return strings.HasPrefix(event.Channel, "D")
}

func (b *Bot) trimBot(msg string) string {
	msg = strings.Replace(msg, strings.ToLower(b.msgPrefix), "", 1)
	msg = strings.TrimPrefix(msg, b.name)
	msg = strings.Trim(msg, " :\n")

	return msg
}

func formatAsSnippet(response string) string {
	return "```" + response + "```"
}
<commit_msg>no need to show debug messages<commit_after>package slack

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/nlopes/slack"

	"github.com/keel-hq/keel/bot"
	"github.com/keel-hq/keel/constants"
	"github.com/keel-hq/keel/version"

	log "github.com/sirupsen/logrus"
)

// SlackImplementer - implementes slack HTTP functionality, used to
// send messages with attachments
type SlackImplementer interface {
	PostMessage(channel, text string, params slack.PostMessageParameters) (string, string, error)
}

// Bot - main slack bot container
type Bot struct {
	id   string // bot id
	name string // bot name

	users map[string]string

	msgPrefix string

	slackClient *slack.Client
	slackRTM    *slack.RTM

	slackHTTPClient SlackImplementer

	approvalsChannel string // slack approvals channel name

	ctx                context.Context
	botMessagesChannel chan *bot.BotMessage
	approvalsRespCh    chan *bot.ApprovalResponse
}

func init() {
	bot.RegisterBot("slack", &Bot{})
}

func (b *Bot) Configure(approvalsRespCh chan *bot.ApprovalResponse, botMessagesChannel chan *bot.BotMessage) bool {
	if os.Getenv(constants.EnvSlackToken) != "" {

		b.name = "keel"
		if os.Getenv(constants.EnvSlackBotName) != "" {
			b.name = os.Getenv(constants.EnvSlackBotName)
		}

		token := os.Getenv(constants.EnvSlackToken)
		client := slack.New(token)

		b.approvalsChannel = "general"
		if os.Getenv(constants.EnvSlackApprovalsChannel) != "" {
			b.approvalsChannel = os.Getenv(constants.EnvSlackApprovalsChannel)
		}

		b.slackClient = client
		b.slackHTTPClient = client
		b.approvalsRespCh = approvalsRespCh
		b.botMessagesChannel = botMessagesChannel

		return true
	}
	log.Info("bot.slack.Configure(): Slack approval bot is not configured")
	return false
}

// Start - start bot
func (b *Bot) Start(ctx context.Context) error {
	// setting root context
	b.ctx = ctx

	users, err := b.slackClient.GetUsers()
	if err != nil {
		return err
	}

	b.users = map[string]string{}

	for _, user := range users {
		switch user.Name {
		case b.name:
			if user.IsBot {
				b.id = user.ID
			}
		default:
			continue
		}
	}
	if b.id == "" {
		return errors.New("could not find bot in the list of names, check if the bot is called \"" + b.name + "\" ")
	}

	b.msgPrefix = strings.ToLower("<@" + b.id + ">")

	go b.startInternal()

	return nil
}

func (b *Bot) startInternal() error {
	b.slackRTM = b.slackClient.NewRTM()

	go b.slackRTM.ManageConnection()
	for {
		select {
		case <-b.ctx.Done():
			return nil

		case msg := <-b.slackRTM.IncomingEvents:
			switch ev := msg.Data.(type) {
			case *slack.HelloEvent:
				// Ignore hello

			case *slack.ConnectedEvent:
				// fmt.Println("Infos:", ev.Info)
				// fmt.Println("Connection counter:", ev.ConnectionCount)
				// Replace #general with your Channel ID
				// b.slackRTM.SendMessage(b.slackRTM.NewOutgoingMessage("Hello world", "#general"))

			case *slack.MessageEvent:
				b.handleMessage(ev)
			case *slack.PresenceChangeEvent:
				// fmt.Printf("Presence Change: %v\n", ev)

			// case *slack.LatencyReport:
			// 	fmt.Printf("Current latency: %v\n", ev.Value)

			case *slack.RTMError:
				fmt.Printf("Error: %s\n", ev.Error())

			case *slack.InvalidAuthEvent:
				fmt.Printf("Invalid credentials")
				return fmt.Errorf("invalid credentials")

			default:

				// Ignore other events..
				// fmt.Printf("Unexpected: %v\n", msg.Data)
			}
		}
	}
}

func (b *Bot) postMessage(title, message, color string, fields []slack.AttachmentField) error {
	params := slack.NewPostMessageParameters()
	params.Username = b.name

	params.Attachments = []slack.Attachment{
		slack.Attachment{
			Fallback: message,
			Color:    color,
			Fields:   fields,
			Footer:   fmt.Sprintf("https://keel.sh %s", version.GetKeelVersion().Version),
			Ts:       json.Number(strconv.Itoa(int(time.Now().Unix()))),
		},
	}

	_, _, err := b.slackHTTPClient.PostMessage(b.approvalsChannel, "", params)
	if err != nil {
		log.WithFields(log.Fields{
			"error": err,
		}).Error("bot.postMessage: failed to send message")
	}
	return err
}

// TODO(k): cache results in a map or get this info on startup. Although
// if channel was then recreated (unlikely), we would miss results
func (b *Bot) isApprovalsChannel(event *slack.MessageEvent) bool {

	info := b.slackRTM.GetInfo()

	for _, ch := range info.Channels {
		if ch.ID == event.Channel && ch.Name == b.approvalsChannel {
			return true
		}
	}

	// checking private channels
	for _, gr := range info.Groups {
		if gr.ID == event.Channel && gr.Name == b.approvalsChannel {
			return true
		}
	}

	return false
}

func (b *Bot) handleMessage(event *slack.MessageEvent) {
	if event.BotID != "" || event.User == "" || event.SubType == "bot_message" {
		log.WithFields(log.Fields{
			"event_bot_ID":  event.BotID,
			"event_user":    event.User,
			"msg":           event.Text,
			"event_subtype": event.SubType,
		}).Debug("handleMessage: ignoring message")
		return
	}

	eventText := strings.Trim(strings.ToLower(event.Text), " \n\r")

	if !b.isBotMessage(event, eventText) {
		return
	}

	eventText = b.trimBot(eventText)

	// only accepting approvals from approvals channel
	if b.isApprovalsChannel(event) {
		approval, ok := bot.IsApproval(event.User, eventText)
		if ok {
			b.approvalsRespCh <- approval
			return
		}
	} else {
		log.Warnf("not approvals channel: %s", event.Channel)
	}

	b.botMessagesChannel <- &bot.BotMessage{
		Message: eventText,
		User:    event.User,
		Channel: event.Channel,
		Name:    "slack",
	}
	return
}

func (b *Bot) Respond(text string, channel string) {
	b.slackRTM.SendMessage(b.slackRTM.NewOutgoingMessage(formatAsSnippet(text), channel))
}

func (b *Bot) isBotMessage(event *slack.MessageEvent, eventText string) bool {
	prefixes := []string{
		b.msgPrefix,
		"keel",
	}

	for _, p := range prefixes {
		if strings.HasPrefix(eventText, p) {
			return true
		}
	}

	// Direct message channels always starts with 'D'
	return strings.HasPrefix(event.Channel, "D")
}

func (b *Bot) trimBot(msg string) string {
	msg = strings.Replace(msg, strings.ToLower(b.msgPrefix), "", 1)
	msg = strings.TrimPrefix(msg, b.name)
	msg = strings.Trim(msg, " :\n")

	return msg
}

func formatAsSnippet(response string) string {
	return "```" + response + "```"
}
<|endoftext|>


<commit_before>package venom

import (
	"errors"
	"fmt"
	"reflect"
	"strings"

	"github.com/spf13/pflag"
)

const (
	SquashFlagsTag = "++"
)

//
// Structures implementing this interface won't be introspected and this function will be called
// instead.
//
type HasFlags interface {
	Flags() *pflag.FlagSet
}

//
// Parse name for mapstructure tags i.e. fetch banana from:
//
// type Foo struct {
//     foo int `mapstructure:"banana"`
// }
//
func parseMapstructureTag(tag string) (string, bool) {
	parts := strings.SplitN(tag, ",", 2)
	if len(parts) == 0 {
		return "", false
	}
	return parts[0], true
}

type flagInfo struct {
	name      string
	shorthand string
	usage     string
}

//
// Parse flag tag so it later could be used to create cli flag:
//
// type Foo struct {
//     foo int `flag:"foo,f,Do some fooness"`
// }
//
func parseTag(tag string) flagInfo {
	parts := strings.SplitN(tag, ",", 3)

	// flag: bar, b, Some barness -> flag: bar,b,Some barness
	for i, p := range parts {
		parts[i] = strings.TrimSpace(p)
	}

	var f flagInfo
	switch len(parts) {
	case 1:
		// flag: b
		if len(parts[0]) == 1 {
			f.name = ""
			f.shorthand = parts[0]
			f.usage = ""
			return f
		}
		// flag: bar
		f.name = parts[0]
		f.shorthand = ""
		f.usage = ""
		return f
	case 2:
		// flag: b,Some barness
		if len(parts[0]) == 1 {
			f.name = ""
			f.shorthand = parts[0]
			f.usage = parts[1]
			return f
		}
		// flag: bar,b
		if len(parts[1]) == 1 {
			f.name = parts[0]
			f.shorthand = parts[1]
			f.usage = ""
			return f
		}
		// flag: bar,Some barness
		f.name = parts[0]
		f.shorthand = ""
		f.usage = parts[1]
		return f
	case 3:
		// flag: bar,b,Some barness
		f.name = parts[0]
		f.shorthand = parts[1]
		f.usage = parts[2]
		return f
	default:
		return f
	}
}

func DefineFlags(defaults interface{}) *pflag.FlagSet {
	flags, err := NewFlags(defaults)
	if err != nil {
		panic(err)
	}
	return flags
}

func NewFlags(defaults interface{}) (*pflag.FlagSet, error) {
	a := flagsFactory{
		tags: []string{"flag", "pflag"},
	}
	return a.createFlags(defaults)
}

type flagsFactory struct {
	tags []string
}

func (a flagsFactory) lookupTag(tag reflect.StructTag) (string, bool) {
	for _, name := range a.tags {
		v, ok := tag.Lookup(name)
		if ok {
			return v, true
		}
	}
	return "", false
}

func (a flagsFactory) createFlags(defaults interface{}) (*pflag.FlagSet, error) {
	var flags pflag.FlagSet

	//
	// Remove one level of indirection.
	//
	v := reflect.ValueOf(defaults)
	if v.Kind() == reflect.Ptr {
		v = reflect.Indirect(v)
	}

	//
	// Make sure we end up with a struct.
	//
	if v.Kind() != reflect.Struct {
		return nil, errors.New("Struct or pointer to struct expected")
	}

	//
	// For every tagged struct field create a flag.
	//
	for i := 0; i < v.Type().NumField(); i++ {
		structField := v.Type().Field(i)
		fieldType := structField.Type
		fieldValue := v.Field(i)

		tag, ok := a.lookupTag(structField.Tag)
		if !ok {
			continue
		}

		//
		// This means we want to squash all flags from the inner structure so they appear as is they are defined
		// in the outer structure.
		//
		if tag == SquashFlagsTag {
			if fieldType.Kind() != reflect.Struct {
				return nil, fmt.Errorf(`flag:"%s" is supported only for inner structs but is set on: %s`, SquashFlagsTag, fieldType)
			}

			// Check if the struct implements HasFlags right away
			if hasFlags, ok := fieldValue.Interface().(HasFlags); ok {
				innerFlags := hasFlags.Flags()
				flags.AddFlagSet(innerFlags)
				continue
			}

			// Check if struct-ptr implements HasFlags
			if fieldValue.CanAddr() {
				fieldValueAddr := fieldValue.Addr()

				if hasFlags, ok := fieldValueAddr.Interface().(HasFlags); ok {
					innerFlags := hasFlags.Flags()
					flags.AddFlagSet(innerFlags)
					continue
				}
			}

			// Check if inner struct implements HasFlags.
			//
			// I can't manage to get a pointer to inner struct here, it is not addressable and etc. Just as a workaround
			// we make a temporary copy and get a pointer to it instead. Suboptimal but meh, config struct are supposed
			// to be cheap to copy. Note that fieldValueCopy is a pointer.
			//
			fieldValueCopy := reflect.New(fieldType)
			fieldValueCopy.Elem().Set(fieldValue)

			if hasFlags, ok := fieldValueCopy.Interface().(HasFlags); ok {
				innerFlags := hasFlags.Flags()
				flags.AddFlagSet(innerFlags)
				continue
			}

			// No overrides are provided, continue with recursive introspection
			innerFlags, err := a.createFlags(fieldValue.Interface())
			if err != nil {
				return nil, err
			}
			flags.AddFlagSet(innerFlags)
			continue
		}

		//
		// In case we have mapstructure defined it must have exactly the same name as flag has.
		//
		mapTag, ok := structField.Tag.Lookup("mapstructure")
		if ok {
			mapName, ok := parseMapstructureTag(mapTag)
			if ok && !(tag == mapName || strings.HasPrefix(tag, mapName+",")) {
				return nil, fmt.Errorf(`Both "mapstructure" and "flag" tags must have equal names but are different for field: %s`, structField.Name)
				continue
			}
		}

		fi := parseTag(tag)
		err := addFlagForTag(&flags, fi, fieldValue, fieldType)
		if err != nil {
			return nil, err
		}
	}

	return &flags, nil
}

func addFlagForTag(flags *pflag.FlagSet, fi flagInfo, fieldValue reflect.Value, fieldType reflect.Type) error {
	name := fi.name
	shorthand := fi.shorthand
	usage := fi.usage

	switch fieldType.Kind() {
	case reflect.Bool:
		value := bool(fieldValue.Bool())
		flags.BoolP(name, shorthand, value, usage)
	case reflect.Int:
		value := int(fieldValue.Int())
		flags.IntP(name, shorthand, value, usage)
	case reflect.Int8:
		value := int8(fieldValue.Int())
		flags.Int8P(name, shorthand, value, usage)
	case reflect.Int16:
		value := int32(fieldValue.Int())
		flags.Int32P(name, shorthand, value, usage) // Not a typo, pflags doesn't have Int16
	case reflect.Int32:
		value := int32(fieldValue.Int())
		flags.Int32P(name, shorthand, value, usage)
	case reflect.Int64:
		value := int64(fieldValue.Int())
		flags.Int64P(name, shorthand, value, usage)
	case reflect.Uint:
		value := uint(fieldValue.Uint())
		flags.UintP(name, shorthand, value, usage)
	case reflect.Uint8:
		value := uint8(fieldValue.Uint())
		flags.Uint8P(name, shorthand, value, usage)
	case reflect.Uint16:
		value := uint16(fieldValue.Uint())
		flags.Uint16P(name, shorthand, value, usage)
	case reflect.Uint32:
		value := uint32(fieldValue.Uint())
		flags.Uint32P(name, shorthand, value, usage)
	case reflect.Uint64:
		value := uint64(fieldValue.Uint())
		flags.Uint64P(name, shorthand, value, usage)
	case reflect.Float32:
		value := float32(fieldValue.Float())
		flags.Float32P(name, shorthand, value, usage)
	case reflect.Float64:
		value := float64(fieldValue.Float())
		flags.Float64P(name, shorthand, value, usage)
	case reflect.String:
		value := string(fieldValue.String())
		flags.StringP(name, shorthand, value, usage)
	default:
		return fmt.Errorf("Unsupported type for field with flag tag %q: %s", name, fieldType)
	}
	return nil
}
<commit_msg>Moar docs<commit_after>package venom

import (
	"errors"
	"fmt"
	"reflect"
	"strings"

	"github.com/spf13/pflag"
)

const (
	SquashFlagsTag = "++"
)

//
// Structures implementing this interface won't be introspected and this function will be called
// instead.
//
type HasFlags interface {
	Flags() *pflag.FlagSet
}

//
// Parse name for mapstructure tags i.e. fetch banana from:
//
// type Foo struct {
//     foo int `mapstructure:"banana"`
// }
//
func parseMapstructureTag(tag string) (string, bool) {
	parts := strings.SplitN(tag, ",", 2)
	if len(parts) == 0 {
		return "", false
	}
	return parts[0], true
}

type flagInfo struct {
	name      string
	shorthand string
	usage     string
}

//
// Parse flag tag so it later could be used to create cli flag:
//
// type Foo struct {
//     foo int `flag:"foo,f,Do some fooness"`
// }
//
func parseTag(tag string) flagInfo {
	parts := strings.SplitN(tag, ",", 3)

	// flag: bar, b, Some barness -> flag: bar,b,Some barness
	for i, p := range parts {
		parts[i] = strings.TrimSpace(p)
	}

	var f flagInfo
	switch len(parts) {
	case 1:
		// flag: b
		if len(parts[0]) == 1 {
			f.name = ""
			f.shorthand = parts[0]
			f.usage = ""
			return f
		}
		// flag: bar
		f.name = parts[0]
		f.shorthand = ""
		f.usage = ""
		return f
	case 2:
		// flag: b,Some barness
		if len(parts[0]) == 1 {
			f.name = ""
			f.shorthand = parts[0]
			f.usage = parts[1]
			return f
		}
		// flag: bar,b
		if len(parts[1]) == 1 {
			f.name = parts[0]
			f.shorthand = parts[1]
			f.usage = ""
			return f
		}
		// flag: bar,Some barness
		f.name = parts[0]
		f.shorthand = ""
		f.usage = parts[1]
		return f
	case 3:
		// flag: bar,b,Some barness
		f.name = parts[0]
		f.shorthand = parts[1]
		f.usage = parts[2]
		return f
	default:
		return f
	}
}

//
// Define new flags based on the provided defaults.
//
// It panics if something goes wrong.
//
func DefineFlags(defaults interface{}) *pflag.FlagSet {
	flags, err := NewFlags(defaults)
	if err != nil {
		panic(err)
	}
	return flags
}

//
// Create new flags based on the provided defaults.
//
func NewFlags(defaults interface{}) (*pflag.FlagSet, error) {
	a := flagsFactory{
		tags: []string{"flag", "pflag"},
	}
	return a.createFlags(defaults)
}

type flagsFactory struct {
	tags []string
}

func (a flagsFactory) lookupTag(tag reflect.StructTag) (string, bool) {
	for _, name := range a.tags {
		v, ok := tag.Lookup(name)
		if ok {
			return v, true
		}
	}
	return "", false
}

func (a flagsFactory) createFlags(defaults interface{}) (*pflag.FlagSet, error) {
	var flags pflag.FlagSet

	//
	// Remove one level of indirection.
	//
	v := reflect.ValueOf(defaults)
	if v.Kind() == reflect.Ptr {
		v = reflect.Indirect(v)
	}

	//
	// Make sure we end up with a struct.
	//
	if v.Kind() != reflect.Struct {
		return nil, errors.New("Struct or pointer to struct expected")
	}

	//
	// For every tagged struct field create a flag.
	//
	for i := 0; i < v.Type().NumField(); i++ {
		structField := v.Type().Field(i)
		fieldType := structField.Type
		fieldValue := v.Field(i)

		tag, ok := a.lookupTag(structField.Tag)
		if !ok {
			continue
		}

		//
		// This means we want to squash all flags from the inner structure so they appear as is they are defined
		// in the outer structure.
		//
		if tag == SquashFlagsTag {
			if fieldType.Kind() != reflect.Struct {
				return nil, fmt.Errorf(`flag:"%s" is supported only for inner structs but is set on: %s`, SquashFlagsTag, fieldType)
			}

			// Check if the struct implements HasFlags right away
			if hasFlags, ok := fieldValue.Interface().(HasFlags); ok {
				innerFlags := hasFlags.Flags()
				flags.AddFlagSet(innerFlags)
				continue
			}

			// Check if struct-ptr implements HasFlags
			if fieldValue.CanAddr() {
				fieldValueAddr := fieldValue.Addr()

				if hasFlags, ok := fieldValueAddr.Interface().(HasFlags); ok {
					innerFlags := hasFlags.Flags()
					flags.AddFlagSet(innerFlags)
					continue
				}
			}

			// Check if inner struct implements HasFlags.
			//
			// I can't manage to get a pointer to inner struct here, it is not addressable and etc. Just as a workaround
			// we make a temporary copy and get a pointer to it instead. Suboptimal but meh, config struct are supposed
			// to be cheap to copy. Note that fieldValueCopy is a pointer.
			//
			fieldValueCopy := reflect.New(fieldType)
			fieldValueCopy.Elem().Set(fieldValue)

			if hasFlags, ok := fieldValueCopy.Interface().(HasFlags); ok {
				innerFlags := hasFlags.Flags()
				flags.AddFlagSet(innerFlags)
				continue
			}

			// No overrides are provided, continue with recursive introspection
			innerFlags, err := a.createFlags(fieldValue.Interface())
			if err != nil {
				return nil, err
			}
			flags.AddFlagSet(innerFlags)
			continue
		}

		//
		// In case we have mapstructure defined it must have exactly the same name as flag has.
		//
		mapTag, ok := structField.Tag.Lookup("mapstructure")
		if ok {
			mapName, ok := parseMapstructureTag(mapTag)
			if ok && !(tag == mapName || strings.HasPrefix(tag, mapName+",")) {
				return nil, fmt.Errorf(`Both "mapstructure" and "flag" tags must have equal names but are different for field: %s`, structField.Name)
				continue
			}
		}

		fi := parseTag(tag)
		err := addFlagForTag(&flags, fi, fieldValue, fieldType)
		if err != nil {
			return nil, err
		}
	}

	return &flags, nil
}

func addFlagForTag(flags *pflag.FlagSet, fi flagInfo, fieldValue reflect.Value, fieldType reflect.Type) error {
	name := fi.name
	shorthand := fi.shorthand
	usage := fi.usage

	switch fieldType.Kind() {
	case reflect.Bool:
		value := bool(fieldValue.Bool())
		flags.BoolP(name, shorthand, value, usage)
	case reflect.Int:
		value := int(fieldValue.Int())
		flags.IntP(name, shorthand, value, usage)
	case reflect.Int8:
		value := int8(fieldValue.Int())
		flags.Int8P(name, shorthand, value, usage)
	case reflect.Int16:
		value := int32(fieldValue.Int())
		flags.Int32P(name, shorthand, value, usage) // Not a typo, pflags doesn't have Int16
	case reflect.Int32:
		value := int32(fieldValue.Int())
		flags.Int32P(name, shorthand, value, usage)
	case reflect.Int64:
		value := int64(fieldValue.Int())
		flags.Int64P(name, shorthand, value, usage)
	case reflect.Uint:
		value := uint(fieldValue.Uint())
		flags.UintP(name, shorthand, value, usage)
	case reflect.Uint8:
		value := uint8(fieldValue.Uint())
		flags.Uint8P(name, shorthand, value, usage)
	case reflect.Uint16:
		value := uint16(fieldValue.Uint())
		flags.Uint16P(name, shorthand, value, usage)
	case reflect.Uint32:
		value := uint32(fieldValue.Uint())
		flags.Uint32P(name, shorthand, value, usage)
	case reflect.Uint64:
		value := uint64(fieldValue.Uint())
		flags.Uint64P(name, shorthand, value, usage)
	case reflect.Float32:
		value := float32(fieldValue.Float())
		flags.Float32P(name, shorthand, value, usage)
	case reflect.Float64:
		value := float64(fieldValue.Float())
		flags.Float64P(name, shorthand, value, usage)
	case reflect.String:
		value := string(fieldValue.String())
		flags.StringP(name, shorthand, value, usage)
	default:
		return fmt.Errorf("Unsupported type for field with flag tag %q: %s", name, fieldType)
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	. "fmt"
	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
	"io/ioutil"
	"time"
)

var client *github.Client

type repoDetail struct {
	Name   string
	Detail []github.WeeklyStats
}

func GetAllRepos() []github.Repository {
	fi, err := ioutil.ReadFile("./token")
	if err != nil {
		panic(err)
	}

	ts := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: string(fi)},
	)
	tc := oauth2.NewClient(oauth2.NoContext, ts)
	client = github.NewClient(tc)
	ReOption := github.RepositoryListOptions{Type: "owner"}

	repos, _, _ := client.Repositories.List("ccqpein", &ReOption)
	return repos
}

func GetWeeklyStats(repos []github.Repository, rD chan repoDetail) {
	for _, repo := range repos {
		var A repoDetail
		name := repo.Name
		reposs, _, _ := client.Repositories.ListCodeFrequency("ccqpein", *name)
		A.Name = *name
		A.Detail = reposs
		//Println(A.Name)
		rD <- A
	}
}

func DoWeeklyStats(repoD chan repoDetail, repos []github.Repository) {
	now := time.Now()
	OneYearAgo := now.AddDate(-1, 0, 0)

	for i := 0; i < len(repos); i++ {
		var sumAdd, sumDel int
		A := <-repoD
		Println(A.Name)
		//Println(now, OneYearAgo)
		for _, codeStatues := range A.Detail {
			we := *codeStatues.Week
			if we.After(OneYearAgo) {
				ad := *codeStatues.Additions
				de := *codeStatues.Deletions
				sumAdd += ad
				sumDel += de
			}
		}
		Println(sumAdd, sumDel)
	}
}

func main() {
	allRepos := GetAllRepos()
	rD := make(chan repoDetail)

	go GetWeeklyStats(allRepos, rD)
	DoWeeklyStats(rD, allRepos)

	//make a folder to collect all chart files, for gochart to use

}
<commit_msg>need study how to create new file for storage data<commit_after>package main

import (
	. "fmt"
	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
	"io/ioutil"
	"os"
	"time"
)

var client *github.Client

type repoDetail struct {
	Name   string
	Detail []github.WeeklyStats
}

func GetAllRepos() []github.Repository {
	fi, err := ioutil.ReadFile("./token")
	if err != nil {
		panic(err)
	}

	ts := oauth2.StaticTokenSource(
		&oauth2.Token{AccessToken: string(fi)},
	)
	tc := oauth2.NewClient(oauth2.NoContext, ts)
	client = github.NewClient(tc)
	ReOption := github.RepositoryListOptions{Type: "owner"}

	repos, _, _ := client.Repositories.List("ccqpein", &ReOption)
	return repos
}

func GetWeeklyStats(repos []github.Repository, rD chan repoDetail) {
	for _, repo := range repos {
		var A repoDetail
		name := repo.Name
		reposs, _, _ := client.Repositories.ListCodeFrequency("ccqpein", *name)
		A.Name = *name
		A.Detail = reposs
		//Println(A.Name)
		rD <- A
	}
}

func DoWeeklyStats(repoD chan repoDetail, repos []github.Repository) {
	now := time.Now()
	OneYearAgo := now.AddDate(-1, 0, 0)

	for i := 0; i < len(repos); i++ {
		var sumAdd, sumDel int
		A := <-repoD
		Println(A.Name)
		//Println(now, OneYearAgo)
		for _, codeStatues := range A.Detail {
			we := *codeStatues.Week
			if we.After(OneYearAgo) {
				ad := *codeStatues.Additions
				de := *codeStatues.Deletions
				sumAdd += ad
				sumDel += de
			}
		}
		Println(sumAdd, sumDel)
	}
}

func check(e error) {
	if e != nil {
		panic(e)
	}
}

type ChartFile struct {
	Title, SubTitle, ValueSuffix, YAxisText string
	XAxisNumbers                            []int
	Data                                    map[string][]int
}

func MakeChartFile() {
	os.MkdirAll("~/Desktop/tmp", 0777)
	f, err := os.Create("~/Desktop/tmp/data.chart")
	check(err)
	defer f.Close()

	_, err = f.WriteString("test")
	check(err)
}

func main() {
	allRepos := GetAllRepos()
	rD := make(chan repoDetail)

	go GetWeeklyStats(allRepos, rD)
	DoWeeklyStats(rD, allRepos)

	//make a folder to collect all chart files, for gochart to use
	/*testC := ChartFile{
		Title:        "tt",
		SubTitle:     "ttt",
		ValueSuffix:  "tet",
		YAxisText:    "re",
		XAxisNumbers: []int{1, 2, 3, 4},
		Data:         map[string][]int{"tt": []int{2, 2, 3, 4, 5}},
	}*/
	MakeChartFile()
}
<|endoftext|>


<commit_before>package router

import (
	"errors"
	"html"
	"net/http"
	"strconv"
	"strings"
	"time"

	"sort"

	"github.com/NyaaPantsu/nyaa/config"
	"github.com/NyaaPantsu/nyaa/model"
	userService "github.com/NyaaPantsu/nyaa/service/user"
	"github.com/NyaaPantsu/nyaa/util/categories"
	"github.com/NyaaPantsu/nyaa/util/feeds"
	"github.com/NyaaPantsu/nyaa/util/publicSettings"
	"github.com/NyaaPantsu/nyaa/util/search"
	"github.com/gorilla/feeds"
	"github.com/gorilla/mux"
)

// RSSHandler : Controller for displaying rss feed, accepting common search arguments
func RSSHandler(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()

	// We only get the basic variable for rss based on search param
	torrents, createdAsTime, title, err := getTorrentList(r)

	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	feed := &nyaafeeds.RssFeed{
		Title:   title,
		Link:    config.WebAddress() + "/",
		PubDate: createdAsTime.String(),
	}
	feed.Items = make([]*nyaafeeds.RssItem, len(torrents))

	for i, torrent := range torrents {
		torrentJSON := torrent.ToJSON()
		feed.Items[i] = &nyaafeeds.RssItem{
			Title:       torrentJSON.Name,
			Link:        config.WebAddress() + "/download/" + torrentJSON.Hash,
			Description: string(torrentJSON.Description),
			PubDate:     torrent.Date.Format(time.RFC822),
			GUID:        config.WebAddress() + "view" + strconv.FormatUint(uint64(torrentJSON.ID), 10),
			Enclosure: &nyaafeeds.RssEnclosure{
				URL:    config.WebAddress() + "/download/" + strings.TrimSpace(torrentJSON.Hash),
				Length: strconv.FormatUint(uint64(torrentJSON.Filesize), 10),
				Type:   "application/x-bittorrent",
			},
		}
	}
	// allow cross domain AJAX requests
	w.Header().Set("Access-Control-Allow-Origin", "*")
	rss, rssErr := feeds.ToXML(feed)
	if rssErr != nil {
		http.Error(w, rssErr.Error(), http.StatusInternalServerError)
	}

	_, writeErr := w.Write([]byte(rss))
	if writeErr != nil {
		http.Error(w, writeErr.Error(), http.StatusInternalServerError)
	}
}

// RSSEztvHandler : Controller for displaying rss feed, accepting common search arguments
func RSSEztvHandler(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()

	// We only get the basic variable for rss based on search param
	torrents, createdAsTime, title, err := getTorrentList(r)

	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	feed := &nyaafeeds.RssFeed{
		Title:   title,
		Link:    config.WebAddress() + "/",
		PubDate: createdAsTime.String(),
	}
	feed.Items = make([]*nyaafeeds.RssItem, len(torrents))

	for i, torrent := range torrents {
		torrentJSON := torrent.ToJSON()
		feed.Items[i] = &nyaafeeds.RssItem{
			Title: torrentJSON.Name,
			Link:  config.WebAddress() + "/download/" + torrentJSON.Hash,
			Category: &nyaafeeds.RssCategory{
				Domain: config.WebAddress() + "/search?c=" + torrentJSON.Category + "_" + torrentJSON.SubCategory,
			},
			Description: string(torrentJSON.Description),
			Comments:    config.WebAddress() + "/view/" + strconv.FormatUint(uint64(torrentJSON.ID), 10),
			PubDate:     torrent.Date.Format(time.RFC822),
			GUID:        config.WebAddress() + "/view/" + strconv.FormatUint(uint64(torrentJSON.ID), 10),
			Enclosure: &nyaafeeds.RssEnclosure{
				URL:    config.WebAddress() + "/download/" + strings.TrimSpace(torrentJSON.Hash),
				Length: strconv.FormatUint(uint64(torrentJSON.Filesize), 10),
				Type:   "application/x-bittorrent",
			},
			Torrent: &nyaafeeds.RssTorrent{
				Xmlns:         "http://xmlns.ezrss.it/0.1/",
				FileName:      torrentJSON.Name + ".torrent",
				ContentLength: strconv.FormatUint(uint64(torrentJSON.Filesize), 10),
				InfoHash:      torrentJSON.Hash,
				MagnetURI:     string(torrentJSON.Magnet),
			},
		}
	}
	// allow cross domain AJAX requests
	w.Header().Set("Access-Control-Allow-Origin", "*")
	rss, rssErr := feeds.ToXML(feed)
	if rssErr != nil {
		http.Error(w, rssErr.Error(), http.StatusInternalServerError)
	}

	_, writeErr := w.Write([]byte(rss))
	if writeErr != nil {
		http.Error(w, writeErr.Error(), http.StatusInternalServerError)
	}
}

// RSSTorznabHandler : Controller for displaying rss feed, accepting common search arguments
func RSSTorznabHandler(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()
	t := r.URL.Query().Get("t")
	rss := ""
	title := "Nyaa Pantsu"
	if config.IsSukebei() {
		title = "Sukebei Pantsu"
	}
	if t == "caps" {
		T := publicSettings.GetTfuncFromRequest(r)
		cat := categories.GetCategoriesSelect(true, true)
		var categories []*nyaafeeds.RssCategoryTorznab
		categories = append(categories, &nyaafeeds.RssCategoryTorznab{
			ID:          "5070",
			Name:        "Anime",
			Description: "Anime",
		})
		var keys []string
		for name := range cat {
			keys = append(keys, name)
		}
		sort.Strings(keys)
		last := 0
		for _, key := range keys {
			if len(cat[key]) <= 2 {
				categories = append(categories, &nyaafeeds.RssCategoryTorznab{
					ID:   nyaafeeds.ConvertFromCat(cat[key]),
					Name: string(T(key)),
				})
				last++
			} else {
				categories[last].Subcat = append(categories[last].Subcat, &nyaafeeds.RssSubCat{
					ID:   nyaafeeds.ConvertFromCat(cat[key]),
					Name: string(T(key)),
				})
			}
		}
		feed := &nyaafeeds.RssCaps{
			Server: &nyaafeeds.RssServer{
				Version:   "1.0",
				Title:     title,
				Strapline: "...",
				Email:     config.Conf.Email.From,
				URL:       config.WebAddress(),
				Image:     config.WebAddress() + "/img/logo.png",
			},
			Limits: &nyaafeeds.RssLimits{
				Max:     "300",
				Default: "50",
			},
			Registration: &nyaafeeds.RssRegistration{
				Available: "yes",
				Open:      "yes",
			},
			Searching: &nyaafeeds.RssSearching{
				Search: &nyaafeeds.RssSearch{
					Available:       "yes",
					SupportedParams: "q",
				},
				TvSearch: &nyaafeeds.RssSearch{
					Available: "no",
				},
				MovieSearch: &nyaafeeds.RssSearch{
					Available: "no",
				},
			},
			Categories: &nyaafeeds.RssCategories{
				Category: categories,
			},
		}
		var rssErr error
		rss, rssErr = feeds.ToXML(feed)
		if rssErr != nil {
			http.Error(w, rssErr.Error(), http.StatusInternalServerError)
		}
	} else {
		// We only get the basic variable for rss based on search param
		torrents, createdAsTime, title, err := getTorrentList(r)

		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		feed := &nyaafeeds.RssFeed{
			Title:   title,
			Xmlns:   "http://torznab.com/schemas/2015/feed",
			Link:    config.WebAddress() + "/",
			PubDate: createdAsTime.String(),
		}
		feed.Items = make([]*nyaafeeds.RssItem, len(torrents))

		for i, torrent := range torrents {

			torrentJSON := torrent.ToJSON()
			filesNumber := ""
			if len(torrentJSON.FileList) > 0 {
				filesNumber = strconv.Itoa(len(torrentJSON.FileList))
			}
			seeders := ""
			if torrentJSON.Seeders > 0 {
				seeders = strconv.Itoa(int(torrentJSON.Seeders))
			}
			leechers := ""
			if torrentJSON.Leechers > 0 {
				leechers = strconv.Itoa(int(torrentJSON.Leechers))
			}
			feed.Items[i] = &nyaafeeds.RssItem{
				Title: torrentJSON.Name,
				Link:  config.WebAddress() + "/download/" + torrentJSON.Hash,
				Category: &nyaafeeds.RssCategory{
					Domain: config.WebAddress() + "/search?c=" + torrentJSON.Category + "_" + torrentJSON.SubCategory,
				},
				Description: string(torrentJSON.Description),
				Comments:    config.WebAddress() + "/view/" + strconv.FormatUint(uint64(torrentJSON.ID), 10),
				PubDate:     torrent.Date.Format(time.RFC822),
				GUID:        config.WebAddress() + "/view/" + strconv.FormatUint(uint64(torrentJSON.ID), 10),
				Enclosure: &nyaafeeds.RssEnclosure{
					URL:    config.WebAddress() + "/download/" + strings.TrimSpace(torrentJSON.Hash),
					Length: strconv.FormatUint(uint64(torrentJSON.Filesize), 10),
					Type:   "application/x-bittorrent",
				},
			}
			torznab := []*nyaafeeds.RssTorznab{}
			if torrentJSON.Filesize > 0 {
				torznab = append(torznab, &nyaafeeds.RssTorznab{
					Name:  "size",
					Value: strconv.FormatUint(uint64(torrentJSON.Filesize), 10),
				})
			}
			if filesNumber != "" {
				torznab = append(torznab, &nyaafeeds.RssTorznab{
					Name:  "files",
					Value: filesNumber,
				})
			}
			torznab = append(torznab, &nyaafeeds.RssTorznab{
				Name:  "grabs",
				Value: strconv.Itoa(torrentJSON.Downloads),
			})
			if seeders != "" {
				torznab = append(torznab, &nyaafeeds.RssTorznab{
					Name:  "seeders",
					Value: seeders,
				})
			}
			if leechers != "" {
				torznab = append(torznab, &nyaafeeds.RssTorznab{
					Name:  "leechers",
					Value: leechers,
				})
			}
			if torrentJSON.Hash != "" {
				torznab = append(torznab, &nyaafeeds.RssTorznab{
					Name:  "infohash",
					Value: torrentJSON.Hash,
				})
			}
			if torrentJSON.Magnet != "" {
				torznab = append(torznab, &nyaafeeds.RssTorznab{
					Name:  "magneturl",
					Value: string(torrentJSON.Magnet),
				})
			}
			if len(torznab) > 0 {
				feed.Items[i].Torznab = torznab
			}
		}
		var rssErr error
		rss, rssErr = feeds.ToXML(feed)
		if rssErr != nil {
			http.Error(w, rssErr.Error(), http.StatusInternalServerError)
		}
	}
	// allow cross domain AJAX requests
	w.Header().Set("Access-Control-Allow-Origin", "*")

	_, writeErr := w.Write([]byte(rss))
	if writeErr != nil {
		http.Error(w, writeErr.Error(), http.StatusInternalServerError)
	}
}

func getTorrentList(r *http.Request) (torrents []model.Torrent, createdAsTime time.Time, title string, err error) {
	vars := mux.Vars(r)
	page := vars["page"]
	userID := vars["id"]
	cat := r.URL.Query().Get("cat")
	offset := 0
	if r.URL.Query().Get("offset") != "" {
		offset, err = strconv.Atoi(html.EscapeString(r.URL.Query().Get("offset")))
		if err != nil {
			return
		}
	}

	createdAsTime = time.Now()

	if len(torrents) > 0 {
		createdAsTime = torrents[0].Date
	}

	title = "Nyaa Pantsu"
	if config.IsSukebei() {
		title = "Sukebei Pantsu"
	}

	pagenum := 1
	if page == "" && offset > 0 { // first page for offset is 0
		pagenum = offset + 1
	} else if page != "" {
		pagenum, err = strconv.Atoi(html.EscapeString(page))
		if err != nil {
			return
		}
	}
	if pagenum <= 0 {
		err = errors.New("Page number is invalid")
		return
	}

	if userID != "" {
		userIDnum := 0
		userIDnum, err = strconv.Atoi(html.EscapeString(userID))
		// Should we have a feed for anonymous uploads?
		if err != nil || userIDnum == 0 {
			return
		}

		_, _, err = userService.RetrieveUserForAdmin(userID)
		if err != nil {
			return
		}
		// Set the user ID on the request, so that SearchByQuery finds it.
		query := r.URL.Query()
		query.Set("userID", userID)
		r.URL.RawQuery = query.Encode()
	}

	if cat != "" {
		query := r.URL.Query()
		c := nyaafeeds.ConvertToCat(cat)
		if c == "" {
			return
		}
		query.Set("c", c)
		r.URL.RawQuery = query.Encode()
	}

	_, torrents, err = search.SearchByQueryNoCount(r, pagenum)

	return
}
<commit_msg>Fix RSS link<commit_after>package router

import (
	"errors"
	"html"
	"net/http"
	"strconv"
	"strings"
	"time"

	"sort"

	"github.com/NyaaPantsu/nyaa/config"
	"github.com/NyaaPantsu/nyaa/model"
	userService "github.com/NyaaPantsu/nyaa/service/user"
	"github.com/NyaaPantsu/nyaa/util/categories"
	"github.com/NyaaPantsu/nyaa/util/feeds"
	"github.com/NyaaPantsu/nyaa/util/publicSettings"
	"github.com/NyaaPantsu/nyaa/util/search"
	"github.com/gorilla/feeds"
	"github.com/gorilla/mux"
)

// RSSHandler : Controller for displaying rss feed, accepting common search arguments
func RSSHandler(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()

	// We only get the basic variable for rss based on search param
	torrents, createdAsTime, title, err := getTorrentList(r)

	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	feed := &nyaafeeds.RssFeed{
		Title:   title,
		Link:    config.WebAddress() + "/",
		PubDate: createdAsTime.String(),
	}
	feed.Items = make([]*nyaafeeds.RssItem, len(torrents))

	for i, torrent := range torrents {
		torrentJSON := torrent.ToJSON()
		feed.Items[i] = &nyaafeeds.RssItem{
			Title:       torrentJSON.Name,
			Link:        config.WebAddress() + "/download/" + torrentJSON.Hash,
			Description: string(torrentJSON.Description),
			PubDate:     torrent.Date.Format(time.RFC822),
			GUID:        config.WebAddress() + "/view/" + strconv.FormatUint(uint64(torrentJSON.ID), 10),
			Enclosure: &nyaafeeds.RssEnclosure{
				URL:    config.WebAddress() + "/download/" + strings.TrimSpace(torrentJSON.Hash),
				Length: strconv.FormatUint(uint64(torrentJSON.Filesize), 10),
				Type:   "application/x-bittorrent",
			},
		}
	}
	// allow cross domain AJAX requests
	w.Header().Set("Access-Control-Allow-Origin", "*")
	rss, rssErr := feeds.ToXML(feed)
	if rssErr != nil {
		http.Error(w, rssErr.Error(), http.StatusInternalServerError)
	}

	_, writeErr := w.Write([]byte(rss))
	if writeErr != nil {
		http.Error(w, writeErr.Error(), http.StatusInternalServerError)
	}
}

// RSSEztvHandler : Controller for displaying rss feed, accepting common search arguments
func RSSEztvHandler(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()

	// We only get the basic variable for rss based on search param
	torrents, createdAsTime, title, err := getTorrentList(r)

	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	feed := &nyaafeeds.RssFeed{
		Title:   title,
		Link:    config.WebAddress() + "/",
		PubDate: createdAsTime.String(),
	}
	feed.Items = make([]*nyaafeeds.RssItem, len(torrents))

	for i, torrent := range torrents {
		torrentJSON := torrent.ToJSON()
		feed.Items[i] = &nyaafeeds.RssItem{
			Title: torrentJSON.Name,
			Link:  config.WebAddress() + "/download/" + torrentJSON.Hash,
			Category: &nyaafeeds.RssCategory{
				Domain: config.WebAddress() + "/search?c=" + torrentJSON.Category + "_" + torrentJSON.SubCategory,
			},
			Description: string(torrentJSON.Description),
			Comments:    config.WebAddress() + "/view/" + strconv.FormatUint(uint64(torrentJSON.ID), 10),
			PubDate:     torrent.Date.Format(time.RFC822),
			GUID:        config.WebAddress() + "/view/" + strconv.FormatUint(uint64(torrentJSON.ID), 10),
			Enclosure: &nyaafeeds.RssEnclosure{
				URL:    config.WebAddress() + "/download/" + strings.TrimSpace(torrentJSON.Hash),
				Length: strconv.FormatUint(uint64(torrentJSON.Filesize), 10),
				Type:   "application/x-bittorrent",
			},
			Torrent: &nyaafeeds.RssTorrent{
				Xmlns:         "http://xmlns.ezrss.it/0.1/",
				FileName:      torrentJSON.Name + ".torrent",
				ContentLength: strconv.FormatUint(uint64(torrentJSON.Filesize), 10),
				InfoHash:      torrentJSON.Hash,
				MagnetURI:     string(torrentJSON.Magnet),
			},
		}
	}
	// allow cross domain AJAX requests
	w.Header().Set("Access-Control-Allow-Origin", "*")
	rss, rssErr := feeds.ToXML(feed)
	if rssErr != nil {
		http.Error(w, rssErr.Error(), http.StatusInternalServerError)
	}

	_, writeErr := w.Write([]byte(rss))
	if writeErr != nil {
		http.Error(w, writeErr.Error(), http.StatusInternalServerError)
	}
}

// RSSTorznabHandler : Controller for displaying rss feed, accepting common search arguments
func RSSTorznabHandler(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()
	t := r.URL.Query().Get("t")
	rss := ""
	title := "Nyaa Pantsu"
	if config.IsSukebei() {
		title = "Sukebei Pantsu"
	}
	if t == "caps" {
		T := publicSettings.GetTfuncFromRequest(r)
		cat := categories.GetCategoriesSelect(true, true)
		var categories []*nyaafeeds.RssCategoryTorznab
		categories = append(categories, &nyaafeeds.RssCategoryTorznab{
			ID:          "5070",
			Name:        "Anime",
			Description: "Anime",
		})
		var keys []string
		for name := range cat {
			keys = append(keys, name)
		}
		sort.Strings(keys)
		last := 0
		for _, key := range keys {
			if len(cat[key]) <= 2 {
				categories = append(categories, &nyaafeeds.RssCategoryTorznab{
					ID:   nyaafeeds.ConvertFromCat(cat[key]),
					Name: string(T(key)),
				})
				last++
			} else {
				categories[last].Subcat = append(categories[last].Subcat, &nyaafeeds.RssSubCat{
					ID:   nyaafeeds.ConvertFromCat(cat[key]),
					Name: string(T(key)),
				})
			}
		}
		feed := &nyaafeeds.RssCaps{
			Server: &nyaafeeds.RssServer{
				Version:   "1.0",
				Title:     title,
				Strapline: "...",
				Email:     config.Conf.Email.From,
				URL:       config.WebAddress(),
				Image:     config.WebAddress() + "/img/logo.png",
			},
			Limits: &nyaafeeds.RssLimits{
				Max:     "300",
				Default: "50",
			},
			Registration: &nyaafeeds.RssRegistration{
				Available: "yes",
				Open:      "yes",
			},
			Searching: &nyaafeeds.RssSearching{
				Search: &nyaafeeds.RssSearch{
					Available:       "yes",
					SupportedParams: "q",
				},
				TvSearch: &nyaafeeds.RssSearch{
					Available: "no",
				},
				MovieSearch: &nyaafeeds.RssSearch{
					Available: "no",
				},
			},
			Categories: &nyaafeeds.RssCategories{
				Category: categories,
			},
		}
		var rssErr error
		rss, rssErr = feeds.ToXML(feed)
		if rssErr != nil {
			http.Error(w, rssErr.Error(), http.StatusInternalServerError)
		}
	} else {
		// We only get the basic variable for rss based on search param
		torrents, createdAsTime, title, err := getTorrentList(r)

		if err != nil {
			http.Error(w, err.Error(), http.StatusBadRequest)
			return
		}

		feed := &nyaafeeds.RssFeed{
			Title:   title,
			Xmlns:   "http://torznab.com/schemas/2015/feed",
			Link:    config.WebAddress() + "/",
			PubDate: createdAsTime.String(),
		}
		feed.Items = make([]*nyaafeeds.RssItem, len(torrents))

		for i, torrent := range torrents {

			torrentJSON := torrent.ToJSON()
			filesNumber := ""
			if len(torrentJSON.FileList) > 0 {
				filesNumber = strconv.Itoa(len(torrentJSON.FileList))
			}
			seeders := ""
			if torrentJSON.Seeders > 0 {
				seeders = strconv.Itoa(int(torrentJSON.Seeders))
			}
			leechers := ""
			if torrentJSON.Leechers > 0 {
				leechers = strconv.Itoa(int(torrentJSON.Leechers))
			}
			feed.Items[i] = &nyaafeeds.RssItem{
				Title: torrentJSON.Name,
				Link:  config.WebAddress() + "/download/" + torrentJSON.Hash,
				Category: &nyaafeeds.RssCategory{
					Domain: config.WebAddress() + "/search?c=" + torrentJSON.Category + "_" + torrentJSON.SubCategory,
				},
				Description: string(torrentJSON.Description),
				Comments:    config.WebAddress() + "/view/" + strconv.FormatUint(uint64(torrentJSON.ID), 10),
				PubDate:     torrent.Date.Format(time.RFC822),
				GUID:        config.WebAddress() + "/view/" + strconv.FormatUint(uint64(torrentJSON.ID), 10),
				Enclosure: &nyaafeeds.RssEnclosure{
					URL:    config.WebAddress() + "/download/" + strings.TrimSpace(torrentJSON.Hash),
					Length: strconv.FormatUint(uint64(torrentJSON.Filesize), 10),
					Type:   "application/x-bittorrent",
				},
			}
			torznab := []*nyaafeeds.RssTorznab{}
			if torrentJSON.Filesize > 0 {
				torznab = append(torznab, &nyaafeeds.RssTorznab{
					Name:  "size",
					Value: strconv.FormatUint(uint64(torrentJSON.Filesize), 10),
				})
			}
			if filesNumber != "" {
				torznab = append(torznab, &nyaafeeds.RssTorznab{
					Name:  "files",
					Value: filesNumber,
				})
			}
			torznab = append(torznab, &nyaafeeds.RssTorznab{
				Name:  "grabs",
				Value: strconv.Itoa(torrentJSON.Downloads),
			})
			if seeders != "" {
				torznab = append(torznab, &nyaafeeds.RssTorznab{
					Name:  "seeders",
					Value: seeders,
				})
			}
			if leechers != "" {
				torznab = append(torznab, &nyaafeeds.RssTorznab{
					Name:  "leechers",
					Value: leechers,
				})
			}
			if torrentJSON.Hash != "" {
				torznab = append(torznab, &nyaafeeds.RssTorznab{
					Name:  "infohash",
					Value: torrentJSON.Hash,
				})
			}
			if torrentJSON.Magnet != "" {
				torznab = append(torznab, &nyaafeeds.RssTorznab{
					Name:  "magneturl",
					Value: string(torrentJSON.Magnet),
				})
			}
			if len(torznab) > 0 {
				feed.Items[i].Torznab = torznab
			}
		}
		var rssErr error
		rss, rssErr = feeds.ToXML(feed)
		if rssErr != nil {
			http.Error(w, rssErr.Error(), http.StatusInternalServerError)
		}
	}
	// allow cross domain AJAX requests
	w.Header().Set("Access-Control-Allow-Origin", "*")

	_, writeErr := w.Write([]byte(rss))
	if writeErr != nil {
		http.Error(w, writeErr.Error(), http.StatusInternalServerError)
	}
}

func getTorrentList(r *http.Request) (torrents []model.Torrent, createdAsTime time.Time, title string, err error) {
	vars := mux.Vars(r)
	page := vars["page"]
	userID := vars["id"]
	cat := r.URL.Query().Get("cat")
	offset := 0
	if r.URL.Query().Get("offset") != "" {
		offset, err = strconv.Atoi(html.EscapeString(r.URL.Query().Get("offset")))
		if err != nil {
			return
		}
	}

	createdAsTime = time.Now()

	if len(torrents) > 0 {
		createdAsTime = torrents[0].Date
	}

	title = "Nyaa Pantsu"
	if config.IsSukebei() {
		title = "Sukebei Pantsu"
	}

	pagenum := 1
	if page == "" && offset > 0 { // first page for offset is 0
		pagenum = offset + 1
	} else if page != "" {
		pagenum, err = strconv.Atoi(html.EscapeString(page))
		if err != nil {
			return
		}
	}
	if pagenum <= 0 {
		err = errors.New("Page number is invalid")
		return
	}

	if userID != "" {
		userIDnum := 0
		userIDnum, err = strconv.Atoi(html.EscapeString(userID))
		// Should we have a feed for anonymous uploads?
		if err != nil || userIDnum == 0 {
			return
		}

		_, _, err = userService.RetrieveUserForAdmin(userID)
		if err != nil {
			return
		}
		// Set the user ID on the request, so that SearchByQuery finds it.
		query := r.URL.Query()
		query.Set("userID", userID)
		r.URL.RawQuery = query.Encode()
	}

	if cat != "" {
		query := r.URL.Query()
		c := nyaafeeds.ConvertToCat(cat)
		if c == "" {
			return
		}
		query.Set("c", c)
		r.URL.RawQuery = query.Encode()
	}

	_, torrents, err = search.SearchByQueryNoCount(r, pagenum)

	return
}
<|endoftext|>


<commit_before>package venom

import (
	"errors"
	"fmt"
	"reflect"
	"strings"
	"time"

	"github.com/spf13/pflag"
)

const (
	SquashFlagsTag = "++"
)

//
// Structures implementing this interface won't be introspected and this function will be called
// instead.
//
type HasFlags interface {
	Flags() *pflag.FlagSet
}

//
// Define new flags based on the provided defaults.
//
// It panics if something goes wrong.
//
func MustDefineFlags(defaults interface{}) *pflag.FlagSet {
	flags, err := DefineFlags(defaults)
	if err != nil {
		panic(err)
	}
	return flags
}

//
// Define new flags based on the provided defaults.
//
func DefineFlags(defaults interface{}) (*pflag.FlagSet, error) {
	a := flagsFactory{
		tags: []string{"flag", "pflag"},
	}
	return a.createFlags(defaults)
}

//
// Parse name for mapstructure tags i.e. fetch banana from:
//
// type Foo struct {
//     foo int `mapstructure:"banana"`
// }
//
func parseMapstructureTag(tag string) (string, bool) {
	parts := strings.SplitN(tag, ",", 2)
	if len(parts) == 0 {
		return "", false
	}
	return parts[0], true
}

type flagInfo struct {
	name      string
	shorthand string
	usage     string
}

//
// Parse flag tag so it later could be used to create cli flag:
//
// type Foo struct {
//     foo int `flag:"foo,f,Do some fooness"`
// }
//
func parseTag(tag string) (flagInfo, error) {
	parts := strings.SplitN(tag, ",", 3)

	// flag: bar, b, Some barness -> flag: bar,b,Some barness
	for i, p := range parts {
		parts[i] = strings.TrimSpace(p)
	}

	var f flagInfo
	switch len(parts) {
	case 1:
		// flag: b
		if len(parts[0]) == 1 {
			f.name = ""
			f.shorthand = parts[0]
			f.usage = ""
			return f, nil
		}
		// flag: bar
		f.name = parts[0]
		f.shorthand = ""
		f.usage = ""
		return f, nil
	case 2:
		// flag: b,Some barness
		if len(parts[0]) == 1 {
			f.name = ""
			f.shorthand = parts[0]
			f.usage = parts[1]
			return f, nil
		}
		// flag: bar,b
		if len(parts[1]) == 1 {
			f.name = parts[0]
			f.shorthand = parts[1]
			f.usage = ""
			return f, nil
		}
		// flag: bar,Some barness
		f.name = parts[0]
		f.shorthand = ""
		f.usage = parts[1]
		return f, nil
	case 3:
		// flag: bar,b,Some barness
		f.name = parts[0]
		f.shorthand = parts[1]
		f.usage = parts[2]
		return f, nil
	default:
		return f, fmt.Errorf("Failed to parse flag tag: %s", tag)
	}
}

type flagsFactory struct {
	tags []string
}

func (a flagsFactory) lookupTag(tag reflect.StructTag) (string, bool) {
	for _, name := range a.tags {
		v, ok := tag.Lookup(name)
		if ok {
			return v, true
		}
	}
	return "", false
}

func (a flagsFactory) createFlags(defaults interface{}) (*pflag.FlagSet, error) {
	var flags pflag.FlagSet

	//
	// Remove one level of indirection.
	//
	v := reflect.ValueOf(defaults)
	if v.Kind() == reflect.Ptr {
		v = reflect.Indirect(v)
	}

	//
	// Make sure we end up with a struct.
	//
	if v.Kind() != reflect.Struct {
		return nil, errors.New("Struct or pointer to struct expected")
	}

	//
	// For every tagged struct field create a flag.
	//
	for i := 0; i < v.Type().NumField(); i++ {
		structField := v.Type().Field(i)
		fieldType := structField.Type
		fieldValue := v.Field(i)

		tag, ok := a.lookupTag(structField.Tag)
		if !ok {
			continue
		}

		//
		// This means we want to squash all flags from the inner structure so they appear as is they are defined
		// in the outer structure.
		//
		if tag == SquashFlagsTag {
			if fieldType.Kind() != reflect.Struct {
				return nil, fmt.Errorf(`flag:"%s" is supported only for inner structs but is set on: %s`, SquashFlagsTag, fieldType)
			}

			// Check if the struct implements HasFlags right away
			if hasFlags, ok := fieldValue.Interface().(HasFlags); ok {
				innerFlags := hasFlags.Flags()
				flags.AddFlagSet(innerFlags)
				continue
			}

			// Check if struct-ptr implements HasFlags
			if fieldValue.CanAddr() {
				fieldValueAddr := fieldValue.Addr()

				if hasFlags, ok := fieldValueAddr.Interface().(HasFlags); ok {
					innerFlags := hasFlags.Flags()
					flags.AddFlagSet(innerFlags)
					continue
				}
			}

			// Check if inner struct implements HasFlags.
			//
			// I can't manage to get a pointer to inner struct here, it is not addressable and etc. Just as a workaround
			// we make a temporary copy and get a pointer to it instead. Suboptimal but meh, config struct are supposed
			// to be cheap to copy. Note that fieldValueCopy is a pointer.
			//
			fieldValueCopy := reflect.New(fieldType)
			fieldValueCopy.Elem().Set(fieldValue)

			if hasFlags, ok := fieldValueCopy.Interface().(HasFlags); ok {
				innerFlags := hasFlags.Flags()
				flags.AddFlagSet(innerFlags)
				continue
			}

			// No overrides are provided, continue with recursive introspection
			innerFlags, err := a.createFlags(fieldValue.Interface())
			if err != nil {
				return nil, err
			}
			flags.AddFlagSet(innerFlags)
			continue
		}

		//
		// In case we have mapstructure defined it must have exactly the same name as flag has.
		//
		mapTag, ok := structField.Tag.Lookup("mapstructure")
		if ok {
			mapName, ok := parseMapstructureTag(mapTag)
			if ok && !(tag == mapName || strings.HasPrefix(tag, mapName+",")) {
				return nil, fmt.Errorf(`Both "mapstructure" and "flag" tags must have equal names but are different for field: %s`, structField.Name)
				continue
			}
		}

		fi, err := parseTag(tag)
		if err != nil {
			return nil, err
		}
		fs, err := a.createFlag(fi, fieldValue, fieldType)
		if err != nil {
			return nil, err
		}
		flags.AddFlagSet(fs)
	}

	return &flags, nil
}

func cloneSlice(slice interface{}) interface{} {
	t, v := reflect.TypeOf(slice), reflect.ValueOf(slice)

	v2 := reflect.MakeSlice(t, v.Len(), v.Len())
	n := reflect.Copy(v2, v)
	if n != v.Len() {
		panic(fmt.Sprintf("Failed to clone slice: %d != %d", n, v.Len()))
	}

	return v2.Interface()
}

//
// Note that we pass both field value and field type as it is defined in the struct. I'm not 100% sure about this and
// just playing safe here:
//
// Probably it is possible to get the value's type i.e. fieldValue.Type() and will be not equal to the fieldType as
// defined in the struct. I think it is possible in case these types are convertible i.e. fieldValue.Type() is
// convertible to fieldType.
//
func (a flagsFactory) createFlag(fi flagInfo, fieldValue reflect.Value, fieldType reflect.Type) (*pflag.FlagSet, error) {
	var flags pflag.FlagSet

	name := fi.name
	shorthand := fi.shorthand
	usage := fi.usage

	//
	// Note that switch on type must be *before* the next one that is on kind. This is to prevent kind capturing
	// types that are simply aliases for native types e.g. time.Duration.
	//
	switch fieldType {
	case reflect.TypeOf(time.Time{}):
		val := fieldValue.Interface().(time.Time)
		p := &time.Time{}

		value := newTimeValue(val, p)
		flags.VarP(value, name, shorthand, usage)
		return &flags, nil
	case reflect.TypeOf(time.Duration(0)):
		val := fieldValue.Interface().(time.Duration)

		d := time.Duration(0)
		p := &d

		value := newDurationValue(val, p)
		flags.VarP(value, name, shorthand, usage)
		return &flags, nil
	}

	switch fieldType.Kind() {
	case reflect.Bool:
		value := bool(fieldValue.Bool())
		flags.BoolP(name, shorthand, value, usage)
	case reflect.Int:
		value := int(fieldValue.Int())
		flags.IntP(name, shorthand, value, usage)
	case reflect.Int8:
		value := int8(fieldValue.Int())
		flags.Int8P(name, shorthand, value, usage)
	case reflect.Int16:
		value := int32(fieldValue.Int())
		flags.Int32P(name, shorthand, value, usage) // Not a typo, pflags doesn't have Int16
	case reflect.Int32:
		value := int32(fieldValue.Int())
		flags.Int32P(name, shorthand, value, usage)
	case reflect.Int64:
		value := int64(fieldValue.Int())
		flags.Int64P(name, shorthand, value, usage)
	case reflect.Uint:
		value := uint(fieldValue.Uint())
		flags.UintP(name, shorthand, value, usage)
	case reflect.Uint8:
		value := uint8(fieldValue.Uint())
		flags.Uint8P(name, shorthand, value, usage)
	case reflect.Uint16:
		value := uint16(fieldValue.Uint())
		flags.Uint16P(name, shorthand, value, usage)
	case reflect.Uint32:
		value := uint32(fieldValue.Uint())
		flags.Uint32P(name, shorthand, value, usage)
	case reflect.Uint64:
		value := uint64(fieldValue.Uint())
		flags.Uint64P(name, shorthand, value, usage)
	case reflect.Float32:
		value := float32(fieldValue.Float())
		flags.Float32P(name, shorthand, value, usage)
	case reflect.Float64:
		value := float64(fieldValue.Float())
		flags.Float64P(name, shorthand, value, usage)
	case reflect.String:
		value := string(fieldValue.String())
		flags.StringP(name, shorthand, value, usage)
	case reflect.Slice:
		switch fieldType.Elem().Kind() {
		case reflect.Bool:
			value := cloneSlice(fieldValue.Interface()).([]bool)
			flags.BoolSliceP(name, shorthand, value, usage)
		case reflect.Int:
			value := cloneSlice(fieldValue.Interface()).([]int)
			flags.IntSliceP(name, shorthand, value, usage)
		case reflect.Uint:
			value := cloneSlice(fieldValue.Interface()).([]uint)
			flags.UintSliceP(name, shorthand, value, usage)
		case reflect.String:
			value := cloneSlice(fieldValue.Interface()).([]string)
			flags.StringSliceP(name, shorthand, value, usage)
		default:
			return nil, fmt.Errorf("Unsupported slice type for field with flag tag %q: %s", name, fieldType)
		}
	default:
		return nil, fmt.Errorf("Unsupported type for field with flag tag %q: %s", name, fieldType)
	}
	return &flags, nil
}
<commit_msg>Rename var<commit_after>package venom

import (
	"errors"
	"fmt"
	"reflect"
	"strings"
	"time"

	"github.com/spf13/pflag"
)

const (
	SquashFlagsTag = "++"
)

//
// Structures implementing this interface won't be introspected and this function will be called
// instead.
//
type HasFlags interface {
	Flags() *pflag.FlagSet
}

//
// Define new flags based on the provided defaults.
//
// It panics if something goes wrong.
//
func MustDefineFlags(defaults interface{}) *pflag.FlagSet {
	flags, err := DefineFlags(defaults)
	if err != nil {
		panic(err)
	}
	return flags
}

//
// Define new flags based on the provided defaults.
//
func DefineFlags(defaults interface{}) (*pflag.FlagSet, error) {
	a := flagsFactory{
		tags: []string{"flag", "pflag"},
	}
	return a.createFlags(defaults)
}

//
// Parse name for mapstructure tags i.e. fetch banana from:
//
// type Foo struct {
//     foo int `mapstructure:"banana"`
// }
//
func parseMapstructureTag(tag string) (string, bool) {
	parts := strings.SplitN(tag, ",", 2)
	if len(parts) == 0 {
		return "", false
	}
	return parts[0], true
}

type flagInfo struct {
	name      string
	shorthand string
	usage     string
}

//
// Parse flag tag so it later could be used to create cli flag:
//
// type Foo struct {
//     foo int `flag:"foo,f,Do some fooness"`
// }
//
func parseTag(tag string) (flagInfo, error) {
	parts := strings.SplitN(tag, ",", 3)

	// flag: bar, b, Some barness -> flag: bar,b,Some barness
	for i, p := range parts {
		parts[i] = strings.TrimSpace(p)
	}

	var f flagInfo
	switch len(parts) {
	case 1:
		// flag: b
		if len(parts[0]) == 1 {
			f.name = ""
			f.shorthand = parts[0]
			f.usage = ""
			return f, nil
		}
		// flag: bar
		f.name = parts[0]
		f.shorthand = ""
		f.usage = ""
		return f, nil
	case 2:
		// flag: b,Some barness
		if len(parts[0]) == 1 {
			f.name = ""
			f.shorthand = parts[0]
			f.usage = parts[1]
			return f, nil
		}
		// flag: bar,b
		if len(parts[1]) == 1 {
			f.name = parts[0]
			f.shorthand = parts[1]
			f.usage = ""
			return f, nil
		}
		// flag: bar,Some barness
		f.name = parts[0]
		f.shorthand = ""
		f.usage = parts[1]
		return f, nil
	case 3:
		// flag: bar,b,Some barness
		f.name = parts[0]
		f.shorthand = parts[1]
		f.usage = parts[2]
		return f, nil
	default:
		return f, fmt.Errorf("Failed to parse flag tag: %s", tag)
	}
}

type flagsFactory struct {
	tags []string
}

func (a flagsFactory) lookupTag(tag reflect.StructTag) (string, bool) {
	for _, name := range a.tags {
		v, ok := tag.Lookup(name)
		if ok {
			return v, true
		}
	}
	return "", false
}

func (a flagsFactory) createFlags(defaults interface{}) (*pflag.FlagSet, error) {
	var flags pflag.FlagSet

	//
	// Remove one level of indirection.
	//
	v := reflect.ValueOf(defaults)
	if v.Kind() == reflect.Ptr {
		v = reflect.Indirect(v)
	}

	//
	// Make sure we end up with a struct.
	//
	if v.Kind() != reflect.Struct {
		return nil, errors.New("Struct or pointer to struct expected")
	}

	//
	// For every tagged struct field create a flag.
	//
	for i := 0; i < v.Type().NumField(); i++ {
		structField := v.Type().Field(i)
		fieldType := structField.Type
		fieldValue := v.Field(i)

		tag, ok := a.lookupTag(structField.Tag)
		if !ok {
			continue
		}

		//
		// This means we want to squash all flags from the inner structure so they appear as is they are defined
		// in the outer structure.
		//
		if tag == SquashFlagsTag {
			if fieldType.Kind() != reflect.Struct {
				return nil, fmt.Errorf(`flag:"%s" is supported only for inner structs but is set on: %s`, SquashFlagsTag, fieldType)
			}

			// Check if the struct implements HasFlags right away
			if hasFlags, ok := fieldValue.Interface().(HasFlags); ok {
				innerFlags := hasFlags.Flags()
				flags.AddFlagSet(innerFlags)
				continue
			}

			// Check if struct-ptr implements HasFlags
			if fieldValue.CanAddr() {
				fieldValuePtr := fieldValue.Addr()

				if hasFlags, ok := fieldValuePtr.Interface().(HasFlags); ok {
					innerFlags := hasFlags.Flags()
					flags.AddFlagSet(innerFlags)
					continue
				}
			}

			// Check if inner struct implements HasFlags.
			//
			// I can't manage to get a pointer to inner struct here, it is not addressable and etc. Just as a workaround
			// we make a temporary copy and get a pointer to it instead. Suboptimal but meh, config struct are supposed
			// to be cheap to copy. Note that fieldValueCopy is a pointer.
			//
			fieldValueCopy := reflect.New(fieldType)
			fieldValueCopy.Elem().Set(fieldValue)

			if hasFlags, ok := fieldValueCopy.Interface().(HasFlags); ok {
				innerFlags := hasFlags.Flags()
				flags.AddFlagSet(innerFlags)
				continue
			}

			// No overrides are provided, continue with recursive introspection
			innerFlags, err := a.createFlags(fieldValue.Interface())
			if err != nil {
				return nil, err
			}
			flags.AddFlagSet(innerFlags)
			continue
		}

		//
		// In case we have mapstructure defined it must have exactly the same name as flag has.
		//
		mapTag, ok := structField.Tag.Lookup("mapstructure")
		if ok {
			mapName, ok := parseMapstructureTag(mapTag)
			if ok && !(tag == mapName || strings.HasPrefix(tag, mapName+",")) {
				return nil, fmt.Errorf(`Both "mapstructure" and "flag" tags must have equal names but are different for field: %s`, structField.Name)
				continue
			}
		}

		fi, err := parseTag(tag)
		if err != nil {
			return nil, err
		}
		fs, err := a.createFlag(fi, fieldValue, fieldType)
		if err != nil {
			return nil, err
		}
		flags.AddFlagSet(fs)
	}

	return &flags, nil
}

func cloneSlice(slice interface{}) interface{} {
	t, v := reflect.TypeOf(slice), reflect.ValueOf(slice)

	v2 := reflect.MakeSlice(t, v.Len(), v.Len())
	n := reflect.Copy(v2, v)
	if n != v.Len() {
		panic(fmt.Sprintf("Failed to clone slice: %d != %d", n, v.Len()))
	}

	return v2.Interface()
}

//
// Note that we pass both field value and field type as it is defined in the struct. I'm not 100% sure about this and
// just playing safe here:
//
// Probably it is possible to get the value's type i.e. fieldValue.Type() and will be not equal to the fieldType as
// defined in the struct. I think it is possible in case these types are convertible i.e. fieldValue.Type() is
// convertible to fieldType.
//
func (a flagsFactory) createFlag(fi flagInfo, fieldValue reflect.Value, fieldType reflect.Type) (*pflag.FlagSet, error) {
	var flags pflag.FlagSet

	name := fi.name
	shorthand := fi.shorthand
	usage := fi.usage

	//
	// Note that switch on type must be *before* the next one that is on kind. This is to prevent kind capturing
	// types that are simply aliases for native types e.g. time.Duration.
	//
	switch fieldType {
	case reflect.TypeOf(time.Time{}):
		val := fieldValue.Interface().(time.Time)
		p := &time.Time{}

		value := newTimeValue(val, p)
		flags.VarP(value, name, shorthand, usage)
		return &flags, nil
	case reflect.TypeOf(time.Duration(0)):
		val := fieldValue.Interface().(time.Duration)

		d := time.Duration(0)
		p := &d

		value := newDurationValue(val, p)
		flags.VarP(value, name, shorthand, usage)
		return &flags, nil
	}

	switch fieldType.Kind() {
	case reflect.Bool:
		value := bool(fieldValue.Bool())
		flags.BoolP(name, shorthand, value, usage)
	case reflect.Int:
		value := int(fieldValue.Int())
		flags.IntP(name, shorthand, value, usage)
	case reflect.Int8:
		value := int8(fieldValue.Int())
		flags.Int8P(name, shorthand, value, usage)
	case reflect.Int16:
		value := int32(fieldValue.Int())
		flags.Int32P(name, shorthand, value, usage) // Not a typo, pflags doesn't have Int16
	case reflect.Int32:
		value := int32(fieldValue.Int())
		flags.Int32P(name, shorthand, value, usage)
	case reflect.Int64:
		value := int64(fieldValue.Int())
		flags.Int64P(name, shorthand, value, usage)
	case reflect.Uint:
		value := uint(fieldValue.Uint())
		flags.UintP(name, shorthand, value, usage)
	case reflect.Uint8:
		value := uint8(fieldValue.Uint())
		flags.Uint8P(name, shorthand, value, usage)
	case reflect.Uint16:
		value := uint16(fieldValue.Uint())
		flags.Uint16P(name, shorthand, value, usage)
	case reflect.Uint32:
		value := uint32(fieldValue.Uint())
		flags.Uint32P(name, shorthand, value, usage)
	case reflect.Uint64:
		value := uint64(fieldValue.Uint())
		flags.Uint64P(name, shorthand, value, usage)
	case reflect.Float32:
		value := float32(fieldValue.Float())
		flags.Float32P(name, shorthand, value, usage)
	case reflect.Float64:
		value := float64(fieldValue.Float())
		flags.Float64P(name, shorthand, value, usage)
	case reflect.String:
		value := string(fieldValue.String())
		flags.StringP(name, shorthand, value, usage)
	case reflect.Slice:
		switch fieldType.Elem().Kind() {
		case reflect.Bool:
			value := cloneSlice(fieldValue.Interface()).([]bool)
			flags.BoolSliceP(name, shorthand, value, usage)
		case reflect.Int:
			value := cloneSlice(fieldValue.Interface()).([]int)
			flags.IntSliceP(name, shorthand, value, usage)
		case reflect.Uint:
			value := cloneSlice(fieldValue.Interface()).([]uint)
			flags.UintSliceP(name, shorthand, value, usage)
		case reflect.String:
			value := cloneSlice(fieldValue.Interface()).([]string)
			flags.StringSliceP(name, shorthand, value, usage)
		default:
			return nil, fmt.Errorf("Unsupported slice type for field with flag tag %q: %s", name, fieldType)
		}
	default:
		return nil, fmt.Errorf("Unsupported type for field with flag tag %q: %s", name, fieldType)
	}
	return &flags, nil
}
<|endoftext|>


<commit_before>// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package bootstrap

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/google/blueprint"
)

func bootstrapVariable(name string, value func(BootstrapConfig) string) blueprint.Variable {
	return pctx.VariableFunc(name, func(config interface{}) (string, error) {
		c, ok := config.(BootstrapConfig)
		if !ok {
			panic(fmt.Sprintf("Bootstrap rules were passed a configuration that does not include theirs, config=%q",
				config))
		}
		return value(c), nil
	})
}

var (
	// These variables are the only configuration needed by the bootstrap
	// modules.
	srcDirVariable = bootstrapVariable("srcDir", func(c BootstrapConfig) string {
		return c.SrcDir()
	})
	buildDirVariable = bootstrapVariable("buildDir", func(c BootstrapConfig) string {
		return c.BuildDir()
	})
	ninjaBuildDirVariable = bootstrapVariable("ninjaBuildDir", func(c BootstrapConfig) string {
		return c.NinjaBuildDir()
	})
	goRootVariable = bootstrapVariable("goRoot", func(c BootstrapConfig) string {
		goroot := runtime.GOROOT()
		// Prefer to omit absolute paths from the ninja file
		if cwd, err := os.Getwd(); err == nil {
			if relpath, err := filepath.Rel(cwd, goroot); err == nil {
				if !strings.HasPrefix(relpath, "../") {
					goroot = relpath
				}
			}
		}
		return goroot
	})
	compileCmdVariable = bootstrapVariable("compileCmd", func(c BootstrapConfig) string {
		return "$goRoot/pkg/tool/" + runtime.GOOS + "_" + runtime.GOARCH + "/compile"
	})
	linkCmdVariable = bootstrapVariable("linkCmd", func(c BootstrapConfig) string {
		return "$goRoot/pkg/tool/" + runtime.GOOS + "_" + runtime.GOARCH + "/link"
	})
	debugFlagsVariable = bootstrapVariable("debugFlags", func(c BootstrapConfig) string {
		if c.DebugCompilation() {
			return "-N -l"
		} else {
			return ""
		}
	})
)

type BootstrapConfig interface {
	// The top-level directory of the source tree
	SrcDir() string

	// The directory where files emitted during bootstrapping are located.
	// Usually NinjaBuildDir() + "/soong".
	BuildDir() string

	// The output directory for the build.
	NinjaBuildDir() string

	// Whether to compile Go code in such a way that it can be debugged
	DebugCompilation() bool
}

type ConfigRemoveAbandonedFilesUnder interface {
	// RemoveAbandonedFilesUnder should return two slices:
	// - a slice of path prefixes that will be cleaned of files that are no
	//   longer active targets, but are listed in the .ninja_log.
	// - a slice of paths that are exempt from cleaning
	RemoveAbandonedFilesUnder(buildDir string) (under, except []string)
}

type ConfigBlueprintToolLocation interface {
	// BlueprintToolLocation can return a path name to install blueprint tools
	// designed for end users (bpfmt, bpmodify, and anything else using
	// blueprint_go_binary).
	BlueprintToolLocation() string
}

type StopBefore int

const (
	StopBeforePrepareBuildActions StopBefore = 1
	StopBeforeWriteNinja          StopBefore = 2
)

type ConfigStopBefore interface {
	StopBefore() StopBefore
}

type Stage int

const (
	StagePrimary Stage = iota
	StageMain
)

type Config struct {
	stage Stage

	topLevelBlueprintsFile string

	emptyNinjaFile bool
	runGoTests     bool
	useValidations bool
	moduleListFile string
}
<commit_msg>Add comment about what -N -l does.<commit_after>// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package bootstrap

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/google/blueprint"
)

func bootstrapVariable(name string, value func(BootstrapConfig) string) blueprint.Variable {
	return pctx.VariableFunc(name, func(config interface{}) (string, error) {
		c, ok := config.(BootstrapConfig)
		if !ok {
			panic(fmt.Sprintf("Bootstrap rules were passed a configuration that does not include theirs, config=%q",
				config))
		}
		return value(c), nil
	})
}

var (
	// These variables are the only configuration needed by the bootstrap
	// modules.
	srcDirVariable = bootstrapVariable("srcDir", func(c BootstrapConfig) string {
		return c.SrcDir()
	})
	buildDirVariable = bootstrapVariable("buildDir", func(c BootstrapConfig) string {
		return c.BuildDir()
	})
	ninjaBuildDirVariable = bootstrapVariable("ninjaBuildDir", func(c BootstrapConfig) string {
		return c.NinjaBuildDir()
	})
	goRootVariable = bootstrapVariable("goRoot", func(c BootstrapConfig) string {
		goroot := runtime.GOROOT()
		// Prefer to omit absolute paths from the ninja file
		if cwd, err := os.Getwd(); err == nil {
			if relpath, err := filepath.Rel(cwd, goroot); err == nil {
				if !strings.HasPrefix(relpath, "../") {
					goroot = relpath
				}
			}
		}
		return goroot
	})
	compileCmdVariable = bootstrapVariable("compileCmd", func(c BootstrapConfig) string {
		return "$goRoot/pkg/tool/" + runtime.GOOS + "_" + runtime.GOARCH + "/compile"
	})
	linkCmdVariable = bootstrapVariable("linkCmd", func(c BootstrapConfig) string {
		return "$goRoot/pkg/tool/" + runtime.GOOS + "_" + runtime.GOARCH + "/link"
	})
	debugFlagsVariable = bootstrapVariable("debugFlags", func(c BootstrapConfig) string {
		if c.DebugCompilation() {
			// -N: disable optimizations, -l: disable inlining
			return "-N -l"
		} else {
			return ""
		}
	})
)

type BootstrapConfig interface {
	// The top-level directory of the source tree
	SrcDir() string

	// The directory where files emitted during bootstrapping are located.
	// Usually NinjaBuildDir() + "/soong".
	BuildDir() string

	// The output directory for the build.
	NinjaBuildDir() string

	// Whether to compile Go code in such a way that it can be debugged
	DebugCompilation() bool
}

type ConfigRemoveAbandonedFilesUnder interface {
	// RemoveAbandonedFilesUnder should return two slices:
	// - a slice of path prefixes that will be cleaned of files that are no
	//   longer active targets, but are listed in the .ninja_log.
	// - a slice of paths that are exempt from cleaning
	RemoveAbandonedFilesUnder(buildDir string) (under, except []string)
}

type ConfigBlueprintToolLocation interface {
	// BlueprintToolLocation can return a path name to install blueprint tools
	// designed for end users (bpfmt, bpmodify, and anything else using
	// blueprint_go_binary).
	BlueprintToolLocation() string
}

type StopBefore int

const (
	StopBeforePrepareBuildActions StopBefore = 1
	StopBeforeWriteNinja          StopBefore = 2
)

type ConfigStopBefore interface {
	StopBefore() StopBefore
}

type Stage int

const (
	StagePrimary Stage = iota
	StageMain
)

type Config struct {
	stage Stage

	topLevelBlueprintsFile string

	emptyNinjaFile bool
	runGoTests     bool
	useValidations bool
	moduleListFile string
}
<|endoftext|>