<commit_before>package main

import (
	"encoding/csv"
	"fmt"
	"strconv"
	"log"
	"os"
	"strings"
)

const (
	SELLWIRE_TRANSACTION_COLUMN_STATUS=16
	SELLWIRE_TRANSACTION_COLUMN_TRANSACTION_ID=2
	SELLWIRE_TRANSACTION_COLUMN_TIMESTAMP=12
	SELLWIRE_TRANSACTION_COLUMN_CUSTOMER_NAME=3
	SELLWIRE_TRANSACTION_COLUMN_CUSTOMER_AMOUNT=7
	SELLWIRE_TRANSACTION_COLUMN_CUSTOMER_IS_EU=9
	SELLWIRE_TRANSACTION_COLUMN_CUSTOMER_COUNTRY_CODE=10
	SELLWIRE_TRANSACTION_COLUMN_CUSTOMER_TAX_NUMBER=11
)

type Amount struct {
	Dollars int64
	Cents int64
}

type SellwireTransaction struct {
	TransactionId string
	Date string
	CustomerName string
	Amount Amount
	IsEU bool
	IsPrivate bool
	CountryCode string
	TaxNumber string
}

func main() {
	file, err := os.Open("input/SellwireOrders_All_23_Feb_2016_30_Sep_2016.csv")
	if err != nil {
		log.Fatal(err)
	}
	r := csv.NewReader(file)

	records, err := r.ReadAll()
	if err != nil {
		log.Fatal(err)
	}

	var paypalTransactions []SellwireTransaction

	for _, record := range records {
		status := record[SELLWIRE_TRANSACTION_COLUMN_STATUS]
		if status != "complete" {
			// TODO do we need to handle refunded?
			continue
		}
		timestamp := record[SELLWIRE_TRANSACTION_COLUMN_TIMESTAMP]
		timestampParts := strings.Split(timestamp, " ")

		countryCode := record[SELLWIRE_TRANSACTION_COLUMN_CUSTOMER_COUNTRY_CODE]
		taxNumber := record[SELLWIRE_TRANSACTION_COLUMN_CUSTOMER_TAX_NUMBER]

		if taxNumber != "" && !strings.HasPrefix(taxNumber, countryCode) {
			taxNumber = fmt.Sprintf("%s%s", countryCode, taxNumber)
		}

		isEU, _ := strconv.ParseBool(record[SELLWIRE_TRANSACTION_COLUMN_CUSTOMER_IS_EU])

		var amount Amount
		amountStr := record[SELLWIRE_TRANSACTION_COLUMN_CUSTOMER_AMOUNT]
		amountStrStripped := strings.Replace(amountStr, ",", "", -1)
		amountParts := strings.Split(amountStrStripped, ".")
		if len(amountParts) > 2 {
			log.Fatalf("Invalid amount found: %s", amountStr)
		}
		if len(amountParts) > 0 {
			dollars, err := strconv.ParseInt(amountParts[0], 10, 64)
			if err != nil {
				log.Fatal(err)
			}
			amount.Dollars = dollars
			if len(amountParts) > 1 {
				cents, err  := strconv.ParseInt(amountParts[1], 10, 64)
				if err != nil {
					log.Fatal(err)
				}
				amount.Cents = cents
			}
		}

		sellwireRecord := SellwireTransaction{
			TransactionId: record[SELLWIRE_TRANSACTION_COLUMN_TRANSACTION_ID],
			Date: timestampParts[0],
			CustomerName: strings.Title(strings.ToLower(record[SELLWIRE_TRANSACTION_COLUMN_CUSTOMER_NAME])),
			Amount: amount,
			IsEU: isEU,
			IsPrivate: taxNumber == "",
			CountryCode: countryCode,
			TaxNumber: taxNumber,
		}

		if !strings.HasPrefix(sellwireRecord.TransactionId, "ch_") {
			paypalTransactions = append(paypalTransactions, sellwireRecord)
		}
	}

	paypalOutput := [][]string{
		{"Datum", "Kundenname", "Betrag USD", "Land", "EU", "Privat", "USt-ID"},
	}

	for _, tx := range paypalTransactions {
		isEU := ""
		if tx.IsEU {
			isEU = "x"
		}

		isPrivate := ""
		if tx.IsPrivate {
			isPrivate = "x"
		}

		record := []string{
			tx.Date,
			tx.CustomerName,
			fmt.Sprintf("%d,%02d",tx.Amount.Dollars, tx.Amount.Cents),
			tx.CountryCode,
			isEU,
			isPrivate,
			tx.TaxNumber,
		}
		paypalOutput = append(paypalOutput, record)
	}

	outputFile, err := os.Create("output/Paypal.csv")
	if err != nil {
		log.Fatal(err)
	}

	w := csv.NewWriter(outputFile)
	w.WriteAll(paypalOutput)

	if err := w.Error(); err != nil {
		log.Fatalln("error writing paypal output csv:", err)
	}
}<commit_msg>Properly parse and format date<commit_after>package main

import (
	"encoding/csv"
	"fmt"
	"strconv"
	"log"
	"os"
	"strings"
	"time"
)

const (
	SELLWIRE_TIMESTAMP_FORMAT = "2006-01-02 15:04:05"
	SELLWIRE_TRANSACTION_COLUMN_STATUS=16
	SELLWIRE_TRANSACTION_COLUMN_TRANSACTION_ID=2
	SELLWIRE_TRANSACTION_COLUMN_TIMESTAMP=12
	SELLWIRE_TRANSACTION_COLUMN_CUSTOMER_NAME=3
	SELLWIRE_TRANSACTION_COLUMN_CUSTOMER_AMOUNT=7
	SELLWIRE_TRANSACTION_COLUMN_CUSTOMER_IS_EU=9
	SELLWIRE_TRANSACTION_COLUMN_CUSTOMER_COUNTRY_CODE=10
	SELLWIRE_TRANSACTION_COLUMN_CUSTOMER_TAX_NUMBER=11
	PAYPAL_DATE_OUTPUT_FORMAT = "02.01.2006"
)

type Amount struct {
	Dollars int64
	Cents int64
}

type SellwireTransaction struct {
	TransactionId string
	Timestamp time.Time
	CustomerName string
	Amount Amount
	IsEU bool
	IsPrivate bool
	CountryCode string
	TaxNumber string
}

func main() {
	file, err := os.Open("input/SellwireOrders_All_23_Feb_2016_30_Sep_2016.csv")
	if err != nil {
		log.Fatal(err)
	}
	r := csv.NewReader(file)

	records, err := r.ReadAll()
	if err != nil {
		log.Fatal(err)
	}

	var paypalTransactions []SellwireTransaction

	for _, record := range records {
		status := record[SELLWIRE_TRANSACTION_COLUMN_STATUS]
		if status != "complete" {
			// TODO do we need to handle refunded?
			continue
		}
		timestampStr := record[SELLWIRE_TRANSACTION_COLUMN_TIMESTAMP]
		timestamp, err := time.Parse(SELLWIRE_TIMESTAMP_FORMAT, timestampStr)
		if err != nil {
			log.Fatal(err)
		}

		countryCode := record[SELLWIRE_TRANSACTION_COLUMN_CUSTOMER_COUNTRY_CODE]
		taxNumber := record[SELLWIRE_TRANSACTION_COLUMN_CUSTOMER_TAX_NUMBER]

		if taxNumber != "" && !strings.HasPrefix(taxNumber, countryCode) {
			taxNumber = fmt.Sprintf("%s%s", countryCode, taxNumber)
		}

		isEU, _ := strconv.ParseBool(record[SELLWIRE_TRANSACTION_COLUMN_CUSTOMER_IS_EU])

		var amount Amount
		amountStr := record[SELLWIRE_TRANSACTION_COLUMN_CUSTOMER_AMOUNT]
		amountStrStripped := strings.Replace(amountStr, ",", "", -1)
		amountParts := strings.Split(amountStrStripped, ".")
		if len(amountParts) > 2 {
			log.Fatalf("Invalid amount found: %s", amountStr)
		}
		if len(amountParts) > 0 {
			dollars, err := strconv.ParseInt(amountParts[0], 10, 64)
			if err != nil {
				log.Fatal(err)
			}
			amount.Dollars = dollars
			if len(amountParts) > 1 {
				cents, err  := strconv.ParseInt(amountParts[1], 10, 64)
				if err != nil {
					log.Fatal(err)
				}
				amount.Cents = cents
			}
		}

		sellwireRecord := SellwireTransaction{
			TransactionId: record[SELLWIRE_TRANSACTION_COLUMN_TRANSACTION_ID],
			Timestamp: timestamp,
			CustomerName: strings.Title(strings.ToLower(record[SELLWIRE_TRANSACTION_COLUMN_CUSTOMER_NAME])),
			Amount: amount,
			IsEU: isEU,
			IsPrivate: taxNumber == "",
			CountryCode: countryCode,
			TaxNumber: taxNumber,
		}

		if !strings.HasPrefix(sellwireRecord.TransactionId, "ch_") {
			paypalTransactions = append(paypalTransactions, sellwireRecord)
		}
	}

	paypalOutput := [][]string{
		{"Datum", "Kundenname", "Betrag USD", "Land", "EU", "Privat", "USt-ID"},
	}

	for _, tx := range paypalTransactions {
		isEU := ""
		if tx.IsEU {
			isEU = "x"
		}

		isPrivate := ""
		if tx.IsPrivate {
			isPrivate = "x"
		}

		record := []string{
			tx.Timestamp.Format(PAYPAL_DATE_OUTPUT_FORMAT),
			tx.CustomerName,
			fmt.Sprintf("%d,%02d",tx.Amount.Dollars, tx.Amount.Cents),
			tx.CountryCode,
			isEU,
			isPrivate,
			tx.TaxNumber,
		}
		paypalOutput = append(paypalOutput, record)
	}

	outputFile, err := os.Create("output/Paypal.csv")
	if err != nil {
		log.Fatal(err)
	}

	w := csv.NewWriter(outputFile)
	w.WriteAll(paypalOutput)

	if err := w.Error(); err != nil {
		log.Fatalln("error writing paypal output csv:", err)
	}
}<|endoftext|>


<commit_before>package main

import (
	"log"
	"net/http"
	"os/exec"
	"strings"

	"github.com/gin-gonic/gin"
)

func main() {
	config, err := LoadConfig()

	if err != nil {
		log.Fatal(err)
	}

	if config.ReleaseMode {
		gin.SetMode(gin.ReleaseMode)
	}

	etcd, err := NewEtcd(config.EtcdEndpoint)

	if err != nil {
		log.Fatal(err)
	}

	if !etcd.HasKey("/paus") {
		if err = etcd.Mkdir("/paus"); err != nil {
			log.Fatal(err)
		}
	}

	r := gin.Default()
	r.LoadHTMLGlob("templates/*")

	r.GET("/", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.tmpl", gin.H{
			"alert":      false,
			"error":      false,
			"message":    "",
			"baseDomain": config.BaseDomain,
		})
	})

	r.GET("/users/:username", func(c *gin.Context) {
		username := c.Param("username")
		apps, err := Apps(etcd, username)

		if err != nil {
			c.HTML(http.StatusInternalServerError, "user.tmpl", gin.H{
				"error":   true,
				"message": strings.Join([]string{"error: ", err.Error()}, ""),
			})

			return
		}

		c.HTML(http.StatusOK, "user.tmpl", gin.H{
			"error": false,
			"user":  username,
			"apps":  apps,
		})
	})

	r.GET("/users/:username/:appName", func(c *gin.Context) {
		appName := c.Param("appName")
		username := c.Param("username")
		urls, err := AppURLs(etcd, config.URIScheme, config.BaseDomain, username, appName)

		if err != nil {
			c.HTML(http.StatusInternalServerError, "app.tmpl", gin.H{
				"error":   true,
				"message": strings.Join([]string{"error: ", err.Error()}, ""),
			})

			return
		}

		envs, err := EnvironmentVariables(etcd, username, appName)

		if err != nil {
			c.HTML(http.StatusInternalServerError, "app.tmpl", gin.H{
				"error":   true,
				"message": strings.Join([]string{"error: ", err.Error()}, ""),
			})

			return
		}

		buildArgs, err := BuildArgs(etcd, username, appName)

		if err != nil {
			c.HTML(http.StatusInternalServerError, "app.tmpl", gin.H{
				"error":   true,
				"message": strings.Join([]string{"error: ", err.Error()}, ""),
			})

			return
		}

		latestURL := LatestAppURLOfUser(config.URIScheme, config.BaseDomain, username, appName)

		c.HTML(http.StatusOK, "app.tmpl", gin.H{
			"error":     false,
			"user":      username,
			"app":       appName,
			"latestURL": latestURL,
			"urls":      urls,
			"buildArgs": buildArgs,
			"envs":      envs,
		})
	})

	r.POST("/users/:username/:appName/build-args", func(c *gin.Context) {
		appName := c.Param("appName")
		username := c.Param("username")
		key := c.PostForm("key")
		value := c.PostForm("value")

		err := AddBuildArg(etcd, username, appName, key, value)

		if err != nil {
			c.HTML(http.StatusInternalServerError, "app.tmpl", gin.H{
				"alert":   true,
				"error":   true,
				"message": strings.Join([]string{"error: ", err.Error()}, ""),
			})

			return
		}

		c.Redirect(http.StatusMovedPermanently, "/users/"+username+"/"+appName)
	})

	r.POST("/users/:username/:appName/envs", func(c *gin.Context) {
		appName := c.Param("appName")
		username := c.Param("username")
		key := c.PostForm("key")
		value := c.PostForm("value")

		err := AddEnvironmentVariable(etcd, username, appName, key, value)

		if err != nil {
			c.HTML(http.StatusInternalServerError, "app.tmpl", gin.H{
				"alert":   true,
				"error":   true,
				"message": strings.Join([]string{"error: ", err.Error()}, ""),
			})

			return
		}

		c.Redirect(http.StatusMovedPermanently, "/users/"+username+"/"+appName)
	})

	r.POST("/users/:username/:appName/envs/upload", func(c *gin.Context) {
		appName := c.Param("appName")
		username := c.Param("username")

		dotenvFile, _, err := c.Request.FormFile("dotenv")

		if err != nil {
			c.HTML(http.StatusInternalServerError, "app.tmpl", gin.H{
				"alert":   true,
				"error":   true,
				"message": strings.Join([]string{"error: ", err.Error()}, ""),
			})

			return
		}

		if err = LoadDotenv(etcd, username, appName, dotenvFile); err != nil {
			c.HTML(http.StatusInternalServerError, "app.tmpl", gin.H{
				"alert":   true,
				"error":   true,
				"message": strings.Join([]string{"error: ", err.Error()}, ""),
			})

			return
		}

		c.Redirect(http.StatusMovedPermanently, "/users/"+username+"/"+appName)
	})

	r.POST("/submit", func(c *gin.Context) {
		username := c.PostForm("username")
		pubKey := c.PostForm("pubKey")

		// libcompose does not support `docker-compose run`...
		out, err := exec.Command("docker-compose", "-p", "paus", "run", "--rm", "gitreceive-upload-key", username, pubKey).Output()

		if err != nil {
			c.HTML(http.StatusInternalServerError, "index.tmpl", gin.H{
				"alert":   true,
				"error":   true,
				"message": strings.Join([]string{"error: ", err.Error()}, ""),
			})

			return
		}

		c.HTML(http.StatusCreated, "index.tmpl", gin.H{
			"alert":   true,
			"error":   false,
			"message": strings.Join([]string{"fingerprint: ", string(out)}, ""),
		})
	})

	r.Run()
}
<commit_msg>Create /paus/users etcd directory at first<commit_after>package main

import (
	"log"
	"net/http"
	"os/exec"
	"strings"

	"github.com/gin-gonic/gin"
)

func main() {
	config, err := LoadConfig()

	if err != nil {
		log.Fatal(err)
	}

	if config.ReleaseMode {
		gin.SetMode(gin.ReleaseMode)
	}

	etcd, err := NewEtcd(config.EtcdEndpoint)

	if err != nil {
		log.Fatal(err)
	}

	if !etcd.HasKey("/paus") {
		if err = etcd.Mkdir("/paus"); err != nil {
			log.Fatal(err)
		}
	}

	if !etcd.HasKey("/paus/users") {
		if err = etcd.Mkdir("/paus/users"); err != nil {
			log.Fatal(err)
		}
	}

	r := gin.Default()
	r.LoadHTMLGlob("templates/*")

	r.GET("/", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.tmpl", gin.H{
			"alert":      false,
			"error":      false,
			"message":    "",
			"baseDomain": config.BaseDomain,
		})
	})

	r.GET("/users/:username", func(c *gin.Context) {
		username := c.Param("username")
		apps, err := Apps(etcd, username)

		if err != nil {
			c.HTML(http.StatusInternalServerError, "user.tmpl", gin.H{
				"error":   true,
				"message": strings.Join([]string{"error: ", err.Error()}, ""),
			})

			return
		}

		c.HTML(http.StatusOK, "user.tmpl", gin.H{
			"error": false,
			"user":  username,
			"apps":  apps,
		})
	})

	r.GET("/users/:username/:appName", func(c *gin.Context) {
		appName := c.Param("appName")
		username := c.Param("username")
		urls, err := AppURLs(etcd, config.URIScheme, config.BaseDomain, username, appName)

		if err != nil {
			c.HTML(http.StatusInternalServerError, "app.tmpl", gin.H{
				"error":   true,
				"message": strings.Join([]string{"error: ", err.Error()}, ""),
			})

			return
		}

		envs, err := EnvironmentVariables(etcd, username, appName)

		if err != nil {
			c.HTML(http.StatusInternalServerError, "app.tmpl", gin.H{
				"error":   true,
				"message": strings.Join([]string{"error: ", err.Error()}, ""),
			})

			return
		}

		buildArgs, err := BuildArgs(etcd, username, appName)

		if err != nil {
			c.HTML(http.StatusInternalServerError, "app.tmpl", gin.H{
				"error":   true,
				"message": strings.Join([]string{"error: ", err.Error()}, ""),
			})

			return
		}

		latestURL := LatestAppURLOfUser(config.URIScheme, config.BaseDomain, username, appName)

		c.HTML(http.StatusOK, "app.tmpl", gin.H{
			"error":     false,
			"user":      username,
			"app":       appName,
			"latestURL": latestURL,
			"urls":      urls,
			"buildArgs": buildArgs,
			"envs":      envs,
		})
	})

	r.POST("/users/:username/:appName/build-args", func(c *gin.Context) {
		appName := c.Param("appName")
		username := c.Param("username")
		key := c.PostForm("key")
		value := c.PostForm("value")

		err := AddBuildArg(etcd, username, appName, key, value)

		if err != nil {
			c.HTML(http.StatusInternalServerError, "app.tmpl", gin.H{
				"alert":   true,
				"error":   true,
				"message": strings.Join([]string{"error: ", err.Error()}, ""),
			})

			return
		}

		c.Redirect(http.StatusMovedPermanently, "/users/"+username+"/"+appName)
	})

	r.POST("/users/:username/:appName/envs", func(c *gin.Context) {
		appName := c.Param("appName")
		username := c.Param("username")
		key := c.PostForm("key")
		value := c.PostForm("value")

		err := AddEnvironmentVariable(etcd, username, appName, key, value)

		if err != nil {
			c.HTML(http.StatusInternalServerError, "app.tmpl", gin.H{
				"alert":   true,
				"error":   true,
				"message": strings.Join([]string{"error: ", err.Error()}, ""),
			})

			return
		}

		c.Redirect(http.StatusMovedPermanently, "/users/"+username+"/"+appName)
	})

	r.POST("/users/:username/:appName/envs/upload", func(c *gin.Context) {
		appName := c.Param("appName")
		username := c.Param("username")

		dotenvFile, _, err := c.Request.FormFile("dotenv")

		if err != nil {
			c.HTML(http.StatusInternalServerError, "app.tmpl", gin.H{
				"alert":   true,
				"error":   true,
				"message": strings.Join([]string{"error: ", err.Error()}, ""),
			})

			return
		}

		if err = LoadDotenv(etcd, username, appName, dotenvFile); err != nil {
			c.HTML(http.StatusInternalServerError, "app.tmpl", gin.H{
				"alert":   true,
				"error":   true,
				"message": strings.Join([]string{"error: ", err.Error()}, ""),
			})

			return
		}

		c.Redirect(http.StatusMovedPermanently, "/users/"+username+"/"+appName)
	})

	r.POST("/submit", func(c *gin.Context) {
		username := c.PostForm("username")
		pubKey := c.PostForm("pubKey")

		// libcompose does not support `docker-compose run`...
		out, err := exec.Command("docker-compose", "-p", "paus", "run", "--rm", "gitreceive-upload-key", username, pubKey).Output()

		if err != nil {
			c.HTML(http.StatusInternalServerError, "index.tmpl", gin.H{
				"alert":   true,
				"error":   true,
				"message": strings.Join([]string{"error: ", err.Error()}, ""),
			})

			return
		}

		c.HTML(http.StatusCreated, "index.tmpl", gin.H{
			"alert":   true,
			"error":   false,
			"message": strings.Join([]string{"fingerprint: ", string(out)}, ""),
		})
	})

	r.Run()
}
<|endoftext|>


<commit_before>package karmabot

import (
	"flag"
	"fmt"
	"log"
	"os"
	"regexp"
	"strconv"
	"strings"

	"github.com/nlopes/slack"

	"github.com/kamaln7/karmabot/database"
)

var (
	motivateRegexp    = regexp.MustCompile(`^(?:\?|!)m\s+@?([^\s]+?)(?:\:\s)?$`)
	karmaRegexp       = regexp.MustCompile(`^@?([^\s]+?)(?:\:\s)?([\+]{2,}|[\-]{2,})((?: for)? (.*))?$`)
	leaderboardRegexp = regexp.MustCompile(`^karma(?:bot)? (?:leaderboard|top|highscores) ?([0-9]+)?$`)
	slackUserRegexp   = regexp.MustCompile(`^<@([A-Za-z0-9]+)>$`)

	debug                       bool
	maxPoints, leaderboardLimit int
	bot                         *slack.Client
	rtm                         *slack.RTM
	ll                          *log.Logger
)

func main() {
	ll = log.New(os.Stdout, "", log.Lshortfile|log.LstdFlags)

	var (
		flagToken            = flag.String("token", "", "slack RTM token")
		flagDBPath           = flag.String("db", "./db.sqlite3", "path to sqlite database")
		flagMaxPoints        = flag.Int("maxpoints", 6, "the maximum amount of points that users can give/take at once")
		flagLeaderboardLimit = flag.Int("leaderboardlimit", 10, "the default amount of users to list in the leaderboard")
		flagDebug            = flag.Bool("debug", false, "set debug mode")
	)

	flag.Parse()
	maxPoints = *flagMaxPoints
	leaderboardLimit = *flagLeaderboardLimit
	debug = *flagDebug
	if *flagToken == "" {
		ll.Fatalln("please pass the slack RTM token using the -token option")
	}

	database.Init(ll, *flagDBPath)

	bot = slack.New(*flagToken)
	slack.SetLogger(ll)
	bot.SetDebug(debug)

	rtm = bot.NewRTM()
	go rtm.ManageConnection()

	for {
		select {
		case msg := <-rtm.IncomingEvents:
			switch ev := msg.Data.(type) {
			case *slack.MessageEvent:
				go handleMessage(msg)
			case *slack.ConnectedEvent:
				ll.Println("connected!")
				if debug {
					ll.Println("infos:", ev.Info)
					ll.Println("connection counter:", ev.ConnectionCount)
				}
			case *slack.RTMError:
				ll.Printf("Slack RTM error: %s\n", ev.Error())
			case *slack.InvalidAuthEvent:
				ll.Fatalln("invalid slack token")
			default:
			}

		}
	}
}

func handleMessage(msg slack.RTMEvent) {
	ev := msg.Data.(*slack.MessageEvent)

	if ev.Type != "message" {
		return
	}

	// convert motivates into karmabot syntax
	if match := motivateRegexp.FindStringSubmatch(ev.Text); len(match) > 0 {
		ev.Text = match[1] + "++ for doing good work"
	}

	if karmaRegexp.MatchString(ev.Text) {
		givePoints(ev)
	}

	if leaderboardRegexp.MatchString(ev.Text) {
		printLeaderboard(ev)
	}
}

func givePoints(ev *slack.MessageEvent) {
	match := karmaRegexp.FindStringSubmatch(ev.Text)
	if len(match) == 0 {
		return
	}

	from, err := getUserNameByID(ev.User)
	if handleError(err, ev.Channel) {
		return
	}
	to, err := parseUser(match[1])
	if handleError(err, ev.Channel) {
		return
	}
	to = strings.ToLower(to)

	points := min(len(match[2])-1, maxPoints)
	if match[2][0] == '-' {
		points *= -1
	}
	reason := match[4]

	record := database.Points{
		From:   from,
		To:     to,
		Points: points,
		Reason: reason,
	}

	err = database.InsertPoints(record)
	if handleError(err, ev.Channel) {
		return
	}

	userPoints, err := database.GetPoints(to)
	if handleError(err, ev.Channel) {
		return
	}

	text := fmt.Sprintf("%s == %d (")

	if points > 0 {
		text += "+"
	}
	text += strconv.Itoa(points)

	if reason != "" {
		text += fmt.Sprintf(" for %s", reason)
	}
	text += ")"

	rtm.SendMessage(rtm.NewOutgoingMessage(text, ev.Channel))
}

func printLeaderboard(ev *slack.MessageEvent) {
	match := leaderboardRegexp.FindStringSubmatch(ev.Text)
	if len(match) == 0 {
		return
	}

	limit := leaderboardLimit
	if match[1] != "" {
		var err error
		limit, err = strconv.Atoi(match[1])
		if handleError(err, ev.Channel) {
			return
		}
	}
	text := fmt.Sprintf("top %d leaderboard\n", limit)

	leaderboard, err := database.GetLeaderboard(limit)
	if handleError(err, ev.Channel) {
		return
	}

	for _, user := range leaderboard {
		text += fmt.Sprintf("%s == %d\n", munge(user.User), user.Points)
	}

	rtm.SendMessage(rtm.NewOutgoingMessage(text, ev.Channel))
}

func handleError(err error, to string) bool {
	if err != nil {
		ll.Printf("Slack RTM error: %s\n", err.Error())
		rtm.SendMessage(rtm.NewOutgoingMessage("error: "+err.Error(), to))
		return true
	}

	return false
}

func parseUser(user string) (string, error) {
	if match := slackUserRegexp.FindStringSubmatch(user); len(match) > 0 {
		return getUserNameByID(match[1])
	}

	return user, nil
}

func getUserNameByID(id string) (string, error) {
	userInfo, err := bot.GetUserInfo(id)
	if err != nil {
		return "", err
	}

	return userInfo.Name, nil
}
<commit_msg>apparently fmt.Sprintf needs arguments<commit_after>package karmabot

import (
	"flag"
	"fmt"
	"log"
	"os"
	"regexp"
	"strconv"
	"strings"

	"github.com/nlopes/slack"

	"github.com/kamaln7/karmabot/database"
)

var (
	motivateRegexp    = regexp.MustCompile(`^(?:\?|!)m\s+@?([^\s]+?)(?:\:\s)?$`)
	karmaRegexp       = regexp.MustCompile(`^@?([^\s]+?)(?:\:\s)?([\+]{2,}|[\-]{2,})((?: for)? (.*))?$`)
	leaderboardRegexp = regexp.MustCompile(`^karma(?:bot)? (?:leaderboard|top|highscores) ?([0-9]+)?$`)
	slackUserRegexp   = regexp.MustCompile(`^<@([A-Za-z0-9]+)>$`)

	debug                       bool
	maxPoints, leaderboardLimit int
	bot                         *slack.Client
	rtm                         *slack.RTM
	ll                          *log.Logger
)

func main() {
	ll = log.New(os.Stdout, "", log.Lshortfile|log.LstdFlags)

	var (
		flagToken            = flag.String("token", "", "slack RTM token")
		flagDBPath           = flag.String("db", "./db.sqlite3", "path to sqlite database")
		flagMaxPoints        = flag.Int("maxpoints", 6, "the maximum amount of points that users can give/take at once")
		flagLeaderboardLimit = flag.Int("leaderboardlimit", 10, "the default amount of users to list in the leaderboard")
		flagDebug            = flag.Bool("debug", false, "set debug mode")
	)

	flag.Parse()
	maxPoints = *flagMaxPoints
	leaderboardLimit = *flagLeaderboardLimit
	debug = *flagDebug
	if *flagToken == "" {
		ll.Fatalln("please pass the slack RTM token using the -token option")
	}

	database.Init(ll, *flagDBPath)

	bot = slack.New(*flagToken)
	slack.SetLogger(ll)
	bot.SetDebug(debug)

	rtm = bot.NewRTM()
	go rtm.ManageConnection()

	for {
		select {
		case msg := <-rtm.IncomingEvents:
			switch ev := msg.Data.(type) {
			case *slack.MessageEvent:
				go handleMessage(msg)
			case *slack.ConnectedEvent:
				ll.Println("connected!")
				if debug {
					ll.Println("infos:", ev.Info)
					ll.Println("connection counter:", ev.ConnectionCount)
				}
			case *slack.RTMError:
				ll.Printf("Slack RTM error: %s\n", ev.Error())
			case *slack.InvalidAuthEvent:
				ll.Fatalln("invalid slack token")
			default:
			}

		}
	}
}

func handleMessage(msg slack.RTMEvent) {
	ev := msg.Data.(*slack.MessageEvent)

	if ev.Type != "message" {
		return
	}

	// convert motivates into karmabot syntax
	if match := motivateRegexp.FindStringSubmatch(ev.Text); len(match) > 0 {
		ev.Text = match[1] + "++ for doing good work"
	}

	if karmaRegexp.MatchString(ev.Text) {
		givePoints(ev)
	}

	if leaderboardRegexp.MatchString(ev.Text) {
		printLeaderboard(ev)
	}
}

func givePoints(ev *slack.MessageEvent) {
	match := karmaRegexp.FindStringSubmatch(ev.Text)
	if len(match) == 0 {
		return
	}

	from, err := getUserNameByID(ev.User)
	if handleError(err, ev.Channel) {
		return
	}
	to, err := parseUser(match[1])
	if handleError(err, ev.Channel) {
		return
	}
	to = strings.ToLower(to)

	points := min(len(match[2])-1, maxPoints)
	if match[2][0] == '-' {
		points *= -1
	}
	reason := match[4]

	record := database.Points{
		From:   from,
		To:     to,
		Points: points,
		Reason: reason,
	}

	err = database.InsertPoints(record)
	if handleError(err, ev.Channel) {
		return
	}

	userPoints, err := database.GetPoints(to)
	if handleError(err, ev.Channel) {
		return
	}

	text := fmt.Sprintf("%s == %d (", to, userPoints)

	if points > 0 {
		text += "+"
	}
	text += strconv.Itoa(points)

	if reason != "" {
		text += fmt.Sprintf(" for %s", reason)
	}
	text += ")"

	rtm.SendMessage(rtm.NewOutgoingMessage(text, ev.Channel))
}

func printLeaderboard(ev *slack.MessageEvent) {
	match := leaderboardRegexp.FindStringSubmatch(ev.Text)
	if len(match) == 0 {
		return
	}

	limit := leaderboardLimit
	if match[1] != "" {
		var err error
		limit, err = strconv.Atoi(match[1])
		if handleError(err, ev.Channel) {
			return
		}
	}
	text := fmt.Sprintf("top %d leaderboard\n", limit)

	leaderboard, err := database.GetLeaderboard(limit)
	if handleError(err, ev.Channel) {
		return
	}

	for _, user := range leaderboard {
		text += fmt.Sprintf("%s == %d\n", munge(user.User), user.Points)
	}

	rtm.SendMessage(rtm.NewOutgoingMessage(text, ev.Channel))
}

func handleError(err error, to string) bool {
	if err != nil {
		ll.Printf("Slack RTM error: %s\n", err.Error())
		rtm.SendMessage(rtm.NewOutgoingMessage("error: "+err.Error(), to))
		return true
	}

	return false
}

func parseUser(user string) (string, error) {
	if match := slackUserRegexp.FindStringSubmatch(user); len(match) > 0 {
		return getUserNameByID(match[1])
	}

	return user, nil
}

func getUserNameByID(id string) (string, error) {
	userInfo, err := bot.GetUserInfo(id)
	if err != nil {
		return "", err
	}

	return userInfo.Name, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	"github.com/cloudfoundry-community/firehose-to-syslog/caching"
	"github.com/cloudfoundry-community/firehose-to-syslog/eventRouting"
	"github.com/cloudfoundry-community/firehose-to-syslog/firehoseclient"
	"github.com/cloudfoundry-community/firehose-to-syslog/logging"
	"github.com/cloudfoundry-community/go-cfclient"
	"github.com/pivotalservices/firehose-to-loginsight/loginsight"
	"github.com/xchapter7x/lo"
	"gopkg.in/alecthomas/kingpin.v2"
)

var (
	debug                    = kingpin.Flag("debug", "Enable debug mode. This disables forwarding to syslog").Default("false").OverrideDefaultFromEnvar("DEBUG").Bool()
	apiEndpoint              = kingpin.Flag("api-endpoint", "Api endpoint address. For bosh-lite installation of CF: https://api.10.244.0.34.xip.io").OverrideDefaultFromEnvar("API_ENDPOINT").Required().String()
	dopplerEndpoint          = kingpin.Flag("doppler-endpoint", "Overwrite default doppler endpoint return by /v2/info").OverrideDefaultFromEnvar("DOPPLER_ENDPOINT").String()
	subscriptionID           = kingpin.Flag("subscription-id", "Id for the subscription.").Default("firehose-to-loginsight").OverrideDefaultFromEnvar("FIREHOSE_SUBSCRIPTION_ID").String()
	user                     = kingpin.Flag("user", "Admin user.").Default("admin").OverrideDefaultFromEnvar("FIREHOSE_USER").String()
	password                 = kingpin.Flag("password", "Admin password.").Default("admin").OverrideDefaultFromEnvar("FIREHOSE_PASSWORD").String()
	skipSSLValidation        = kingpin.Flag("skip-ssl-validation", "Please don't").Default("false").OverrideDefaultFromEnvar("SKIP_SSL_VALIDATION").Bool()
	keepAlive                = kingpin.Flag("fh-keep-alive", "Keep Alive duration for the firehose consumer").Default("25s").OverrideDefaultFromEnvar("FH_KEEP_ALIVE").Duration()
	logEventTotals           = kingpin.Flag("log-event-totals", "Logs the counters for all selected events since nozzle was last started.").Default("false").OverrideDefaultFromEnvar("LOG_EVENT_TOTALS").Bool()
	logEventTotalsTime       = kingpin.Flag("log-event-totals-time", "How frequently the event totals are calculated (in sec).").Default("30s").OverrideDefaultFromEnvar("LOG_EVENT_TOTALS_TIME").Duration()
	wantedEvents             = kingpin.Flag("events", fmt.Sprintf("Comma separated list of events you would like. Valid options are %s", eventRouting.GetListAuthorizedEventEvents())).Default("LogMessage").OverrideDefaultFromEnvar("EVENTS").String()
	boltDatabasePath         = kingpin.Flag("boltdb-path", "Bolt Database path ").Default("my.db").OverrideDefaultFromEnvar("BOLTDB_PATH").String()
	tickerTime               = kingpin.Flag("cc-pull-time", "CloudController Polling time in sec").Default("60s").OverrideDefaultFromEnvar("CF_PULL_TIME").Duration()
	extraFields              = kingpin.Flag("extra-fields", "Extra fields you want to annotate your events with, example: '--extra-fields=env:dev,something:other ").Default("").OverrideDefaultFromEnvar("EXTRA_FIELDS").String()
	logInsightServer         = kingpin.Flag("insight-server", "log insight server address").OverrideDefaultFromEnvar("INSIGHT_SERVER").String()
	logInsightServerPort     = kingpin.Flag("insight-server-port", "log insight server port").Default("9543").OverrideDefaultFromEnvar("INSIGHT_SERVER_PORT").Int()
	logInsightBatchSize      = kingpin.Flag("insight-batch-size", "log insight batch size").Default("1").OverrideDefaultFromEnvar("INSIGHT_BATCH_SIZE").Int()
	logInsightReservedFields = kingpin.Flag("insight-reserved-fields", "comma delimited list of fields that are reserved").Default("event_type").OverrideDefaultFromEnvar("INSIGHT_RESERVED_FIELDS").String()
	logInsightAgentID        = kingpin.Flag("insight-agent-id", "agent id for log insight").Default("5").OverrideDefaultFromEnvar("INSIGHT_AGENT_ID").String()
	logInsightHasJSONLogMsg  = kingpin.Flag("insight-has-json-log-msg", "app log message can be json").Default("false").OverrideDefaultFromEnvar("INSIGHT_HAS_JSON_LOG_MSG").String()
)

var (
	VERSION = "0.0.0"
)

func main() {
	kingpin.Version(VERSION)
	kingpin.Parse()

	var loggingClient logging.Logging
	//Setup Logging
	loggingClient = loginsight.NewForwarder(*logInsightServer, *logInsightServerPort, *logInsightBatchSize, *logInsightReservedFields, *logInsightAgentID, *logInsightHasJSONLogMsg)
	lo.G.Infof("Starting firehose-to-loginsight %s ", VERSION)

	c := cfclient.Config{
		ApiAddress:        *apiEndpoint,
		Username:          *user,
		Password:          *password,
		SkipSslValidation: *skipSSLValidation,
	}
	cloudFoundryClient, err := cfclient.NewClient(&c)
	if err != nil {
		lo.G.Fatal("Error setting up event routing: ", err)
		os.Exit(1)

	}
	if len(*dopplerEndpoint) > 0 {
		cloudFoundryClient.Endpoint.DopplerEndpoint = *dopplerEndpoint
	}
	lo.G.Infof("Using %s as doppler endpoint", cloudFoundryClient.Endpoint.DopplerEndpoint)

	//Creating Caching
	var cachingClient caching.Caching
	if caching.IsNeeded(*wantedEvents) {
		cachingClient = caching.NewCachingBolt(cloudFoundryClient, *boltDatabasePath)
	} else {
		cachingClient = caching.NewCachingEmpty()
	}
	//Creating Events
	events := eventRouting.NewEventRouting(cachingClient, loggingClient)
	err = events.SetupEventRouting(*wantedEvents)
	if err != nil {
		lo.G.Fatal("Error setting up event routing: ", err)
		os.Exit(1)

	}

	//Set extrafields if needed
	events.SetExtraFields(*extraFields)

	//Enable LogsTotalevent
	if *logEventTotals {
		lo.G.Info("Logging total events % is enabled")
		events.LogEventTotals(*logEventTotalsTime)
	}

	// Parse extra fields from cmd call
	cachingClient.CreateBucket()
	//Let's Update the database the first time
	lo.G.Info("Start filling app/space/org cache.")
	apps := cachingClient.GetAllApp()
	lo.G.Infof("Done filling cache! Found [%d] Apps", len(apps))

	//Let's start the goRoutine
	cachingClient.PerformPoollingCaching(*tickerTime)

	firehoseConfig := &firehoseclient.FirehoseConfig{
		TrafficControllerURL:   *dopplerEndpoint,
		InsecureSSLSkipVerify:  *skipSSLValidation,
		IdleTimeoutSeconds:     *keepAlive,
		FirehoseSubscriptionID: *subscriptionID,
	}

	if loggingClient.Connect() || *debug {

		lo.G.Info("Connecting to Firehose...")
		firehoseClient := firehoseclient.NewFirehoseNozzle(cloudFoundryClient, events, firehoseConfig)
		err = firehoseClient.Start()
		if err != nil {
			lo.G.Error("Failed connecting to Firehose...Please check settings and try again!")

		} else {
			lo.G.Info("Firehose Subscription Succesfull! Routing events...")
		}

	} else {
		lo.G.Error("Failed connecting Log Insight...Please check settings and try again!")
	}

	defer cachingClient.Close()
}
<commit_msg>update to add additional logging<commit_after>package main

import (
	"fmt"
	"os"

	"github.com/cloudfoundry-community/firehose-to-syslog/caching"
	"github.com/cloudfoundry-community/firehose-to-syslog/eventRouting"
	"github.com/cloudfoundry-community/firehose-to-syslog/firehoseclient"
	"github.com/cloudfoundry-community/firehose-to-syslog/logging"
	"github.com/cloudfoundry-community/go-cfclient"
	"github.com/pivotalservices/firehose-to-loginsight/loginsight"
	"github.com/xchapter7x/lo"
	"gopkg.in/alecthomas/kingpin.v2"
)

var (
	debug                    = kingpin.Flag("debug", "Enable debug mode. This disables forwarding to syslog").Default("false").OverrideDefaultFromEnvar("DEBUG").Bool()
	apiEndpoint              = kingpin.Flag("api-endpoint", "Api endpoint address. For bosh-lite installation of CF: https://api.10.244.0.34.xip.io").OverrideDefaultFromEnvar("API_ENDPOINT").Required().String()
	dopplerEndpoint          = kingpin.Flag("doppler-endpoint", "Overwrite default doppler endpoint return by /v2/info").OverrideDefaultFromEnvar("DOPPLER_ENDPOINT").String()
	subscriptionID           = kingpin.Flag("subscription-id", "Id for the subscription.").Default("firehose-to-loginsight").OverrideDefaultFromEnvar("FIREHOSE_SUBSCRIPTION_ID").String()
	user                     = kingpin.Flag("user", "Admin user.").Default("admin").OverrideDefaultFromEnvar("FIREHOSE_USER").String()
	password                 = kingpin.Flag("password", "Admin password.").Default("admin").OverrideDefaultFromEnvar("FIREHOSE_PASSWORD").String()
	skipSSLValidation        = kingpin.Flag("skip-ssl-validation", "Please don't").Default("false").OverrideDefaultFromEnvar("SKIP_SSL_VALIDATION").Bool()
	keepAlive                = kingpin.Flag("fh-keep-alive", "Keep Alive duration for the firehose consumer").Default("25s").OverrideDefaultFromEnvar("FH_KEEP_ALIVE").Duration()
	logEventTotals           = kingpin.Flag("log-event-totals", "Logs the counters for all selected events since nozzle was last started.").Default("false").OverrideDefaultFromEnvar("LOG_EVENT_TOTALS").Bool()
	logEventTotalsTime       = kingpin.Flag("log-event-totals-time", "How frequently the event totals are calculated (in sec).").Default("30s").OverrideDefaultFromEnvar("LOG_EVENT_TOTALS_TIME").Duration()
	wantedEvents             = kingpin.Flag("events", fmt.Sprintf("Comma separated list of events you would like. Valid options are %s", eventRouting.GetListAuthorizedEventEvents())).Default("LogMessage").OverrideDefaultFromEnvar("EVENTS").String()
	boltDatabasePath         = kingpin.Flag("boltdb-path", "Bolt Database path ").Default("my.db").OverrideDefaultFromEnvar("BOLTDB_PATH").String()
	tickerTime               = kingpin.Flag("cc-pull-time", "CloudController Polling time in sec").Default("60s").OverrideDefaultFromEnvar("CF_PULL_TIME").Duration()
	extraFields              = kingpin.Flag("extra-fields", "Extra fields you want to annotate your events with, example: '--extra-fields=env:dev,something:other ").Default("").OverrideDefaultFromEnvar("EXTRA_FIELDS").String()
	logInsightServer         = kingpin.Flag("insight-server", "log insight server address").OverrideDefaultFromEnvar("INSIGHT_SERVER").String()
	logInsightServerPort     = kingpin.Flag("insight-server-port", "log insight server port").Default("9543").OverrideDefaultFromEnvar("INSIGHT_SERVER_PORT").Int()
	logInsightBatchSize      = kingpin.Flag("insight-batch-size", "log insight batch size").Default("1").OverrideDefaultFromEnvar("INSIGHT_BATCH_SIZE").Int()
	logInsightReservedFields = kingpin.Flag("insight-reserved-fields", "comma delimited list of fields that are reserved").Default("event_type").OverrideDefaultFromEnvar("INSIGHT_RESERVED_FIELDS").String()
	logInsightAgentID        = kingpin.Flag("insight-agent-id", "agent id for log insight").Default("5").OverrideDefaultFromEnvar("INSIGHT_AGENT_ID").String()
	logInsightHasJSONLogMsg  = kingpin.Flag("insight-has-json-log-msg", "app log message can be json").Default("false").OverrideDefaultFromEnvar("INSIGHT_HAS_JSON_LOG_MSG").String()
)

var (
	VERSION = "0.0.0"
)

func main() {
	kingpin.Version(VERSION)
	kingpin.Parse()

	var loggingClient logging.Logging
	//Setup Logging
	loggingClient = loginsight.NewForwarder(*logInsightServer, *logInsightServerPort, *logInsightBatchSize, *logInsightReservedFields, *logInsightAgentID, *logInsightHasJSONLogMsg)
	lo.G.Infof("Starting firehose-to-loginsight %s ", VERSION)

	if len(*logInsightServer) <= 0 {
		lo.G.Fatal("Must set insight-server property")
		os.Exit(1)
	}
	if len(*apiEndpoint) <= 0 {
		lo.G.Fatal("Must set api-endpoint property")
		os.Exit(1)
	}
	c := cfclient.Config{
		ApiAddress:        *apiEndpoint,
		Username:          *user,
		Password:          *password,
		SkipSslValidation: *skipSSLValidation,
	}
	cloudFoundryClient, err := cfclient.NewClient(&c)
	if err != nil {
		lo.G.Fatal("Error setting up event routing: ", err)
		os.Exit(1)

	}
	if len(*dopplerEndpoint) > 0 {
		cloudFoundryClient.Endpoint.DopplerEndpoint = *dopplerEndpoint
	}

	lo.G.Infof("Using %s:%d as log insight endpoint", *logInsightServer, *logInsightServerPort)
	lo.G.Infof("Using %s as api endpoint", *apiEndpoint)
	lo.G.Infof("Using %s as doppler endpoint", cloudFoundryClient.Endpoint.DopplerEndpoint)

	//Creating Caching
	var cachingClient caching.Caching
	if caching.IsNeeded(*wantedEvents) {
		cachingClient = caching.NewCachingBolt(cloudFoundryClient, *boltDatabasePath)
	} else {
		cachingClient = caching.NewCachingEmpty()
	}
	//Creating Events
	events := eventRouting.NewEventRouting(cachingClient, loggingClient)
	err = events.SetupEventRouting(*wantedEvents)
	if err != nil {
		lo.G.Fatal("Error setting up event routing: ", err)
		os.Exit(1)

	}

	//Set extrafields if needed
	events.SetExtraFields(*extraFields)

	//Enable LogsTotalevent
	if *logEventTotals {
		lo.G.Info("Logging total events % is enabled")
		events.LogEventTotals(*logEventTotalsTime)
	}

	// Parse extra fields from cmd call
	cachingClient.CreateBucket()
	//Let's Update the database the first time
	lo.G.Info("Start filling app/space/org cache.")
	apps := cachingClient.GetAllApp()
	lo.G.Infof("Done filling cache! Found [%d] Apps", len(apps))

	//Let's start the goRoutine
	cachingClient.PerformPoollingCaching(*tickerTime)

	firehoseConfig := &firehoseclient.FirehoseConfig{
		TrafficControllerURL:   *dopplerEndpoint,
		InsecureSSLSkipVerify:  *skipSSLValidation,
		IdleTimeoutSeconds:     *keepAlive,
		FirehoseSubscriptionID: *subscriptionID,
	}

	if loggingClient.Connect() || *debug {

		lo.G.Info("Connecting to Firehose...")
		firehoseClient := firehoseclient.NewFirehoseNozzle(cloudFoundryClient, events, firehoseConfig)
		err = firehoseClient.Start()
		if err != nil {
			lo.G.Error("Failed connecting to Firehose...Please check settings and try again!")

		} else {
			lo.G.Info("Firehose Subscription Succesfull! Routing events...")
		}

	} else {
		lo.G.Error("Failed connecting Log Insight...Please check settings and try again!")
	}

	defer cachingClient.Close()
}
<|endoftext|>


<commit_before>package main

import (
	"time"

	"net"
	"strings"

	"github.com/golang/protobuf/proto"
	"github.com/levenlabs/go-llog"
	"github.com/levenlabs/go-srvclient"
	"github.com/levenlabs/saturn/config"
	lproto "github.com/levenlabs/saturn/proto"
	"github.com/levenlabs/saturn/transaction"
	"github.com/mediocregopher/skyapi/client"
)

func main() {

	transaction.Init()

	if config.IsMaster {
		llog.Info("starting as master")
		go advertise()
		listenSpin()
	} else {
		llog.Info("starting as slave", llog.KV{"master": config.MasterAddr})
		reportSpin()
	}
}

func lookupMaster() *net.UDPAddr {
	//see if they passed a hostname without a port and then do a srv lookup
	masterAddr := config.MasterAddr
	_, _, err := net.SplitHostPort(config.MasterAddr)
	if err != nil && strings.Contains(err.Error(), "missing port") {
		masterAddr, err = srvclient.SRV(config.MasterAddr)
		if err != nil {
			llog.Fatal("error resolving master-addr using srv", llog.KV{"err": err, "addr": config.MasterAddr})
			return nil
		}
	}

	serverAddr, err := net.ResolveUDPAddr("udp", masterAddr)
	if err != nil {
		llog.Fatal("error resolving UDP addr", llog.KV{"err": err, "addr": masterAddr})
	}
	return serverAddr
}

func advertise() {
	if config.SkyAPIAddr != "" {
		skyapiAddr, err := srvclient.SRV(config.SkyAPIAddr)
		if err != nil {
			llog.Fatal("error resolving skyapi-addr", llog.KV{"err": err, "addr": config.SkyAPIAddr})
		}

		llog.Info("connecting to skyapi", llog.KV{"resolvedAddr": skyapiAddr, "thisAddr": config.ListenAddr})

		err = client.ProvideOpts(client.Opts{
			SkyAPIAddr:        skyapiAddr,
			Service:           "saturn",
			ThisAddr:          config.ListenAddr,
			ReconnectAttempts: 3,
		})
		llog.Fatal("skyapi giving up reconnecting", llog.KV{
			"addr":         config.SkyAPIAddr,
			"resolvedAddr": skyapiAddr,
			"err":          err,
		})
	}
}

func marshalAndWrite(msg proto.Message, c *net.UDPConn, dst *net.UDPAddr, kv llog.KV) bool {
	b, err := proto.Marshal(msg)
	if err != nil {
		kv["err"] = err
		llog.Error("error marshaling msg", kv)
		return false
	}

	if dst == nil {
		_, err = c.Write(b)
	} else {
		_, err = c.WriteToUDP(b, dst)
	}

	if err != nil {
		kv["err"] = err
		llog.Error("error writing msg", kv)
		return false
	}
	return true
}

func readAndUnmarshal(c *net.UDPConn, kv llog.KV) (*lproto.TxMsg, *net.UDPAddr, bool) {
	b := make([]byte, 1024)
	n, addr, err := c.ReadFromUDP(b)
	if err != nil {
		if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
			// right now only the slave sets a ReadDeadline, but that's
			// expected so just debug
			llog.Debug("timed out reading from udp socket", kv)
		} else {
			kv["err"] = err
			llog.Error("error reading from udp socket", kv)
		}
		return nil, nil, false
	}

	var msg lproto.TxMsg
	if err := proto.Unmarshal(b[:n], &msg); err != nil {
		kv["err"] = err
		llog.Error("error unmarshaling proto msg", kv)
		return nil, nil, false
	}
	return &msg, addr, true
}

func reportSpin() {
	doSlaveReport()
	for _ = range time.Tick(time.Duration(config.Interval) * time.Second) {
		doSlaveReport()
	}
}

func doSlaveReport() {
	masterAddr := lookupMaster()
	kv := llog.KV{"addr": masterAddr}
	llog.Info("beginning transaction", kv)
	defer llog.Info("ended transaction", kv)

	c, err := net.DialUDP("udp", nil, masterAddr)
	if err != nil {
		kv["err"] = err
		llog.Error("error connecting to master", kv)
		return
	}
	defer c.Close()

	// We're using the localAddr as the prefix for the transaction ID
	addrStr := c.LocalAddr().String()
	ipStr, _, _ := net.SplitHostPort(addrStr)
	firstMsg := transaction.Initiate(addrStr, ipStr)
	kv["txID"] = firstMsg.Id
	if !marshalAndWrite(firstMsg, c, nil, kv) {
		return
	}

	// Todo: instead of relying on read timing out to know when the transaction
	// is done, we should be having the master send a "end" packet
	for {
		// Set a ReadDeadline so we don't wait forever if the master is dead or
		// ended the transaction
		c.SetReadDeadline(time.Now().Add(time.Second * 5))
		msg, _, ok := readAndUnmarshal(c, kv)
		if !ok {
			return
		}

		nextMsg := transaction.IncomingMessage(msg)
		if nextMsg == nil {
			return
		}
		if !marshalAndWrite(nextMsg, c, nil, kv) {
			return
		}
	}
}

func listenSpin() {
	kv := llog.KV{"addr": config.ListenAddr}
	llog.Info("listening on udp", kv)
	lAddr, err := net.ResolveUDPAddr("udp", config.ListenAddr)
	if err != nil {
		kv["err"] = err
		llog.Fatal("error resolving UDP addr", kv)
	}
	conn, err := net.ListenUDP("udp", lAddr)
	if err != nil {
		kv["err"] = err
		llog.Fatal("error listening to UDP port", kv)
	}

	for {
		doMasterInner(conn)
	}
}

func doMasterInner(c *net.UDPConn) {
	kv := llog.KV{"addr": config.ListenAddr}
	//don't set a ReadDeadline since we want it to block until a message
	msg, remoteAddr, ok := readAndUnmarshal(c, kv)
	if !ok {
		llog.Fatal("couldn't read from udp listen socket", kv)
	}

	kv["txID"] = msg.Id
	kv["remoteAddr"] = remoteAddr

	nextMsg := transaction.IncomingMessage(msg)
	if nextMsg == nil {
		return
	}

	marshalAndWrite(nextMsg, c, remoteAddr, kv)
}
<commit_msg>Don't send resolved skyapi addr to Provide<commit_after>package main

import (
	"time"

	"net"
	"strings"

	"github.com/golang/protobuf/proto"
	"github.com/levenlabs/go-llog"
	"github.com/levenlabs/go-srvclient"
	"github.com/levenlabs/saturn/config"
	lproto "github.com/levenlabs/saturn/proto"
	"github.com/levenlabs/saturn/transaction"
	"github.com/mediocregopher/skyapi/client"
)

func main() {

	transaction.Init()

	if config.IsMaster {
		llog.Info("starting as master")
		go advertise()
		listenSpin()
	} else {
		llog.Info("starting as slave", llog.KV{"master": config.MasterAddr})
		reportSpin()
	}
}

func lookupMaster() *net.UDPAddr {
	//see if they passed a hostname without a port and then do a srv lookup
	masterAddr := config.MasterAddr
	_, _, err := net.SplitHostPort(config.MasterAddr)
	if err != nil && strings.Contains(err.Error(), "missing port") {
		masterAddr, err = srvclient.SRV(config.MasterAddr)
		if err != nil {
			llog.Fatal("error resolving master-addr using srv", llog.KV{"err": err, "addr": config.MasterAddr})
			return nil
		}
	}

	serverAddr, err := net.ResolveUDPAddr("udp", masterAddr)
	if err != nil {
		llog.Fatal("error resolving UDP addr", llog.KV{"err": err, "addr": masterAddr})
	}
	return serverAddr
}

func advertise() {
	if config.SkyAPIAddr == "" {
		return
	}
	kv := llog.KV{"skyapiAddr": config.SkyAPIAddr, "listenAddr": config.ListenAddr}
	llog.Info("connecting to skyapi", kv)

	kv["err"] = client.ProvideOpts(client.Opts{
		SkyAPIAddr:        config.SkyAPIAddr,
		Service:           "saturn",
		ThisAddr:          config.ListenAddr,
		ReconnectAttempts: -1,
	})
	llog.Fatal("skyapi giving up reconnecting", kv)
}

func marshalAndWrite(msg proto.Message, c *net.UDPConn, dst *net.UDPAddr, kv llog.KV) bool {
	b, err := proto.Marshal(msg)
	if err != nil {
		kv["err"] = err
		llog.Error("error marshaling msg", kv)
		return false
	}

	if dst == nil {
		_, err = c.Write(b)
	} else {
		_, err = c.WriteToUDP(b, dst)
	}

	if err != nil {
		kv["err"] = err
		llog.Error("error writing msg", kv)
		return false
	}
	return true
}

func readAndUnmarshal(c *net.UDPConn, kv llog.KV) (*lproto.TxMsg, *net.UDPAddr, bool) {
	b := make([]byte, 1024)
	n, addr, err := c.ReadFromUDP(b)
	if err != nil {
		if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
			// right now only the slave sets a ReadDeadline, but that's
			// expected so just debug
			llog.Debug("timed out reading from udp socket", kv)
		} else {
			kv["err"] = err
			llog.Error("error reading from udp socket", kv)
		}
		return nil, nil, false
	}

	var msg lproto.TxMsg
	if err := proto.Unmarshal(b[:n], &msg); err != nil {
		kv["err"] = err
		llog.Error("error unmarshaling proto msg", kv)
		return nil, nil, false
	}
	return &msg, addr, true
}

func reportSpin() {
	doSlaveReport()
	for _ = range time.Tick(time.Duration(config.Interval) * time.Second) {
		doSlaveReport()
	}
}

func doSlaveReport() {
	masterAddr := lookupMaster()
	kv := llog.KV{"addr": masterAddr}
	llog.Info("beginning transaction", kv)
	defer llog.Info("ended transaction", kv)

	c, err := net.DialUDP("udp", nil, masterAddr)
	if err != nil {
		kv["err"] = err
		llog.Error("error connecting to master", kv)
		return
	}
	defer c.Close()

	// We're using the localAddr as the prefix for the transaction ID
	addrStr := c.LocalAddr().String()
	ipStr, _, _ := net.SplitHostPort(addrStr)
	firstMsg := transaction.Initiate(addrStr, ipStr)
	kv["txID"] = firstMsg.Id
	if !marshalAndWrite(firstMsg, c, nil, kv) {
		return
	}

	// Todo: instead of relying on read timing out to know when the transaction
	// is done, we should be having the master send a "end" packet
	for {
		// Set a ReadDeadline so we don't wait forever if the master is dead or
		// ended the transaction
		c.SetReadDeadline(time.Now().Add(time.Second * 5))
		msg, _, ok := readAndUnmarshal(c, kv)
		if !ok {
			return
		}

		nextMsg := transaction.IncomingMessage(msg)
		if nextMsg == nil {
			return
		}
		if !marshalAndWrite(nextMsg, c, nil, kv) {
			return
		}
	}
}

func listenSpin() {
	kv := llog.KV{"addr": config.ListenAddr}
	llog.Info("listening on udp", kv)
	lAddr, err := net.ResolveUDPAddr("udp", config.ListenAddr)
	if err != nil {
		kv["err"] = err
		llog.Fatal("error resolving UDP addr", kv)
	}
	conn, err := net.ListenUDP("udp", lAddr)
	if err != nil {
		kv["err"] = err
		llog.Fatal("error listening to UDP port", kv)
	}

	for {
		doMasterInner(conn)
	}
}

func doMasterInner(c *net.UDPConn) {
	kv := llog.KV{"addr": config.ListenAddr}
	//don't set a ReadDeadline since we want it to block until a message
	msg, remoteAddr, ok := readAndUnmarshal(c, kv)
	if !ok {
		llog.Fatal("couldn't read from udp listen socket", kv)
	}

	kv["txID"] = msg.Id
	kv["remoteAddr"] = remoteAddr

	nextMsg := transaction.IncomingMessage(msg)
	if nextMsg == nil {
		return
	}

	marshalAndWrite(nextMsg, c, remoteAddr, kv)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"unicode/utf8"
	"bytes"
	"strings"
	"errors"
	"strconv"
)

type Token int

type Result struct {
	value string
	left_over string
}

type TokenValue struct {
	value string
	token Token
}

type TokenTree struct {
	TokenValue
	branches []*TokenTree
}

type TokenRoot struct {
	branches []*TokenTree
}

type Parser struct {
	token  Token
	action func(string) ([]*Result)
	next   *Parser
}

// Methods for Parser

func (self Token) String() string {
	return strconv.Itoa(int(self))
}

func NewTokenTree(value string, token Token) *TokenTree {
	toktree := new(TokenTree)
	toktree.value = value
	toktree.token = token
	toktree.branches = make([]*TokenTree, 0)
	return toktree
}

func (self *TokenTree) String() string {
	var buffer bytes.Buffer

	buffer.WriteString("'")
	buffer.WriteString(self.value)
	buffer.WriteString("'")
	buffer.WriteString("[")
	buffer.WriteString(self.token.String())
	buffer.WriteString("]")

	buffer.WriteString("(")
	for i, tree := range self.branches {
		if i != 0 {
			buffer.WriteString(" ")
		}
		buffer.WriteString(tree.String())
	}
	buffer.WriteString(")")

	return buffer.String()
}

func (self *TokenRoot) String() string {
	var buffer bytes.Buffer

	buffer.WriteString("TokenRoot (")
	for i := range self.branches {
		if i != 0 {
			buffer.WriteString(" ")
		}
		buffer.WriteString(self.branches[i].String())
	}
	buffer.WriteString(")")

	return buffer.String()
}

func (self *Parser) tokenize(results []*Result) ([]*TokenRoot, error) {
	outputs := make([]*TokenRoot, len(results))
	for i, result := range results {
		outputs[i] = new(TokenRoot)
		outputs[i].branches = []*TokenTree{NewTokenTree(result.value, self.token)}
	}
	var err error
	if len(results) == 0 {
		err = errors.New("Did not parse token " + self.token.String())
	} else {
		err = nil
	}
	return outputs, err
}

func (self *Parser) retokenize(root_list []*TokenRoot, value string) []*TokenRoot {
	for i := range root_list {
		if len(value) > 0 { // Empty strings can be parsed, sure, but they shouldn't be added here.
			root_list[i].branches = append(root_list[i].branches, NewTokenTree(value, self.token))
		}
	}
	return root_list
}

func (self *Parser) StartParse(str string) ([]*TokenRoot, []*Result, error) {
	result_list := self.action(str)
	root_list, err := self.tokenize(result_list)
	return root_list, result_list, err
}

// Meant to be used after StartParse if necessary
// Helpful because StartParse can be used to look ahead
func (self *Parser) FinishParse(result_list []*Result) []*TokenRoot {
	outputs := make([]*TokenRoot, 0)
	for _, a_result := range result_list {
		child_results := self.next.Parse(a_result.left_over)
		// I think this append will work, but I'm not entirely sure.
		outputs = append(outputs, self.retokenize(child_results, a_result.value)...)
	}
	return outputs
}

func (self *Parser) Parse(str string) []*TokenRoot {
	parent_results := self.action(str)
	if self.next != nil {
		return self.FinishParse(parent_results)
	}
	root_list, _ := self.tokenize(parent_results)
	return root_list
}

func (self *Parser) Chain(other *Parser) *Parser {
	if self.next == nil {
		self.next = other
	} else {
		self.next.Chain(other)
	}
	return other // faster for chaining.
}

//////////////////////////////////////////////

// Actual Parsing

// Make a map to get strings from tokens

const (
	EOF = iota
	LEFT
	RIGHT
	SPACE
	NUMBER
)

var eof rune

// Wraps a string -> []string, []string function
// and adds the original string to the left_overs
func optionally(f func(string) []*Result) func(string) []*Result  {
  return func (s string) []*Result {
    results := f(s)
    return append(results, &Result{value: "", left_over: s})
  }
}

func matchString(string_a string) func(string) []*Result {
  return satisfyString(func (string_b string) (string, error) {
    if strings.HasPrefix(string_b, string_a) {
      return string_a, nil
    }
		err := "String " + string_a + " did not start with " + string_b + "."
    return "", errors.New(err)
  })
}

func matchNothing() func(string) []*Result {
	return satisfyString(func (string_b string) (string, error) {
		return "", errors.New("Never Satisfied. Never Graduate")
	})
}

func satisfyString(f func(string) (string, error)) func(string) []*Result {
	return func(str string) []*Result {
		parsed_string, err := f(str)
		if err == nil {
			// Satisfied
			return []*Result{&Result{left_over: str[len([]rune(parsed_string)):], value: parsed_string}}
		}
    return []*Result{} // Did not parse; return empty Result list
	}
}

func satisfyRune(f func(rune) bool) func(string) []*Result {
	return satisfyString(func(s string) (string, error) {
    c, _ := utf8.DecodeRuneInString(s)
    if f(c) {
      return string(c), nil
    } else {
      return "", errors.New("Rune was not satisfied")
    }
	})
}

func isSpace(c rune) bool {
	return c == ' ' || c == '\t' || c == '\n'
}

func isChar(a rune) func(rune) bool {
	return func(c rune) bool {
		return c == a
	}
}

////
// Debugging

func debug_function() func() {
	a := 'a'
	return func() {
		fmt.Printf("%c\n", a)
		a++
	}
}

////

var a func()

func init() {
	eof = rune(0)
	a = debug_function()
}

func main() {
	var p *Parser

	p = new(Parser)
	p.token = SPACE
	p.action = optionally(satisfyRune(isSpace))

	s := new(Parser)
	s.token = SPACE
	s.action = optionally(satisfyRune(isSpace))

	q := new(Parser)
	q.token = LEFT
	q.action = satisfyRune(isChar('c'))

  r := new(Parser)
  r.token = 5 // Just for testing purposes
  r.action = matchString("Hello") // MatchString is no longer greedy!

	t := new(Parser)
	t.token = 0 // eof
	t.action = matchNothing()

	q.Chain(p).Chain(s).Chain(r)

	root_list := q.Parse("c Hello World")

	for i := range root_list {
		fmt.Println(root_list[i])
	}

}
<commit_msg>Not working<commit_after>package main

import (
	"fmt"
	"unicode/utf8"
	"bytes"
	"strings"
	"errors"
	"strconv"
)

type Token int

const (
	OPERATOR Token = ((iota + 1) * -1)
	LEFT_SIDE
	RIGHT_SIDE
)

type Result struct {
	value string
	left_over string
}

type TokenValue struct {
	value string
	token Token
}

type TokenRoot struct {
	branches []*TokenTree
}

type TokenTree struct {
	TokenValue
	branches []*TokenTree
}

type ParserLike interface {
	BeginParse(string) ([]*TokenRoot, []*Result, error)
	FinishParse([]*Result, ParserLike) []*TokenRoot
	Parse(string) []*TokenRoot
	Chain(ParserLike) ParserLike
	Next() ParserLike
}

type Lexer struct {
	token  Token
	action func(string) ([]*Result)
	next   ParserLike
}

//// AbstractParsers

type AbstractParser struct {
	parse_action func(string, *AbstractParser) []*TokenRoot
	next ParserLike
}

// I think compilers should be transparent to other compilers
// when it comes to whether or not they'll work.
func (self *AbstractParser) BeginParse(str string) ([]*TokenRoot, []*Result, error) {
	return self.next.BeginParse(str) // Is this right?
}

func (self *AbstractParser) FinishParse(arg []*Result, other ParserLike) []*TokenRoot {
	return other.FinishParse(arg, other.Next())
}

func (self *AbstractParser) Parse(str string) []*TokenRoot {
	return self.parse_action(str, self)
}

func (self *AbstractParser) Next() ParserLike {
	return self.next
}
func (self *AbstractParser) SetNext(other ParserLike) {
	self.next = other
}

func (self *AbstractParser) Chain(other ParserLike) ParserLike {
	if self.Next() == nil {
		self.SetNext(other)
	} else {
		self.Next().Chain(other)
	}
	return other // faster for chaining.
}

func NewAbstractParser(f func (string, *AbstractParser) []*TokenRoot) *AbstractParser {
	c := new(AbstractParser)
	c.parse_action = f
	return c
}

func Optionally(parser ParserLike) *AbstractParser {
	return NewAbstractParser(func (str string, self *AbstractParser) []*TokenRoot {
		root_list, result_list, err := parser.BeginParse(str)
		if err == nil { // No problem
			parser.Chain(self.next)
			parser.FinishParse(result_list, self.next)
			return root_list
		}
		return self.next.Parse(str)
	})
}

//// Compilers

// Compilers just deal with the tokens and the syntax tree.

func NewCompiler(f func ([]*TokenRoot) []*TokenRoot) *AbstractParser {
	return NewAbstractParser(func (str string, self *AbstractParser) []*TokenRoot {
		roots := self.next.Parse(str)
		return f(roots)
	})
}

func Operator(token Token) *AbstractParser {
	return NewCompiler(func (roots []*TokenRoot) []*TokenRoot {
		for _, root := range roots {
			root.Traverse(func (tree *TokenTree) {
				var first int
				for i, child := range tree.branches {
					if child.token == token {
						first = i
						break
					}
				}
				elem := new(TokenTree)
				elem.token = OPERATOR
				elem.branches = make([]*TokenTree, 3)

				elem.branches[0] = new(TokenTree)
				elem.branches[0].branches = tree.branches[:first]

				elem.branches[1] = new(TokenTree)
				elem.branches[1].token = token

				// This weird bit of array functions gets an empty array when
				// there are none to the right

				elem.branches[2] = new(TokenTree)
				array := tree.branches[first:] // If this doesn't work, pass by reference
				array = append(array, new(TokenTree))
				array = array[:len(array)-1] // Get rid of that element I added
				elem.branches[2].branches = array

				*tree = *elem

				// Well....
				// Values are being dropped and there are duplicate trees?
				// but whatever! It might work in the future!

			})
		}
		return roots // for chaining
	})
}

//// Token Trees

func (self Token) String() string {
	return strconv.Itoa(int(self))
}

func NewTokenTree(value string, token Token) *TokenTree {
	toktree := new(TokenTree)
	toktree.value = value
	toktree.token = token
	toktree.branches = make([]*TokenTree, 0)
	return toktree
}

func (self *TokenTree) Traverse(f func(*TokenTree))  {
	for _, child := range self.branches {
		child.Traverse(f) // #depthfirstsearch #bottomup?
	}
	f(self)
}

func (self *TokenRoot) Tree() *TokenTree {
	tree := new(TokenTree)
	tree.branches = self.branches
	return tree
}

func (self *TokenRoot) Traverse(f func(*TokenTree)) {
	self.Tree().Traverse(f)
}

func (self *TokenTree) String() string {
	var buffer bytes.Buffer

	buffer.WriteString("'")
	buffer.WriteString(self.value)
	buffer.WriteString("'")
	buffer.WriteString("[")
	buffer.WriteString(self.token.String())
	buffer.WriteString("]")

	buffer.WriteString("(")
	for i, tree := range self.branches {
		if i != 0 {
			buffer.WriteString(" ")
		}
		buffer.WriteString(tree.String())
	}
	buffer.WriteString(")")

	return buffer.String()
}

func (self *TokenRoot) String() string {
	var buffer bytes.Buffer

	buffer.WriteString("TokenRoot (")
	for i := range self.branches {
		if i != 0 {
			buffer.WriteString(" ")
		}
		buffer.WriteString(self.branches[i].String())
	}
	buffer.WriteString(")")

	return buffer.String()
}

//// Lexers

func (self *Lexer) tokenize(results []*Result) ([]*TokenRoot, error) {
	outputs := make([]*TokenRoot, len(results))
	for i, result := range results {
		outputs[i] = new(TokenRoot)
		outputs[i].branches = []*TokenTree{NewTokenTree(result.value, self.token)}
	}
	var err error
	if len(results) == 0 {
		err = errors.New("Did not parse token " + self.token.String())
	} else {
		err = nil
	}
	return outputs, err
}

func (self *Lexer) retokenize(root_list []*TokenRoot, value string) []*TokenRoot {
	for i := range root_list {
		if len(value) > 0 { // Empty strings can be parsed, sure, but they shouldn't be added here.
			root_list[i].branches = append(root_list[i].branches, NewTokenTree(value, self.token))
		}
	}
	return root_list
}

func (self *Lexer) BeginParse(str string) ([]*TokenRoot, []*Result, error) {
	result_list := self.action(str)
	root_list, err := self.tokenize(result_list)
	return root_list, result_list, err
}

// Meant to be used after BeginParse if necessary
// Helpful because BeginParse can be used to look ahead
func (self *Lexer) FinishParse(result_list []*Result, other ParserLike) []*TokenRoot {
	outputs := make([]*TokenRoot, 0)
	for _, a_result := range result_list {
		child_results := other.Parse(a_result.left_over)
		// I think this append will work, but I'm not entirely sure.
		outputs = append(outputs, self.retokenize(child_results, a_result.value)...)
	}
	return outputs
}

func (self *Lexer) Parse(str string) []*TokenRoot {
	a()
	parent_results := self.action(str)

	for _, result := range parent_results {
		fmt.Println(result)
	}

	if self.next != nil {
		return self.FinishParse(parent_results, self)
	}
	root_list, _ := self.tokenize(parent_results)
	return root_list
}

func (self *Lexer) Chain(other ParserLike) ParserLike {
	if self.next == nil {
		self.next = other
	} else {
		self.next.Chain(other)
	}
	return other // faster for chaining.
}

func (self *Lexer) Next() ParserLike {
	return self.next
}

func (self *Lexer) SetNext(other ParserLike) {
	self.next = other
}

//////////////////////////////////////////////

//// Functional Parsing

const (
	EOF = iota
	LEFT
	RIGHT
	SPACE
	NUMBER
)

var eof rune

// Wraps a string -> []string, []string function
// and adds the original string to the left_overs
func optionally(f func(string) []*Result) func(string) []*Result  {
  return func (s string) []*Result {
    results := f(s)
    return append(results, &Result{value: "", left_over: s})
  }
}

func matchString(string_a string) func(string) []*Result {
  return satisfyString(func (string_b string) (string, error) {
    if strings.HasPrefix(string_b, string_a) {
      return string_a, nil
    }
		err := "String " + string_a + " did not start with " + string_b + "."
    return "", errors.New(err)
  })
}

func matchNothing() func(string) []*Result {
	return satisfyString(func (string_b string) (string, error) {
		return "", errors.New("Never Satisfied. Never Graduate")
	})
}

func satisfyString(f func(string) (string, error)) func(string) []*Result {
	return func(str string) []*Result {
		parsed_string, err := f(str)
		if err == nil {
			// Satisfied
			return []*Result{&Result{left_over: str[len([]rune(parsed_string)):], value: parsed_string}}
		}
    return []*Result{} // Did not parse; return empty Result list
	}
}

func satisfyRune(f func(rune) bool) func(string) []*Result {
	return satisfyString(func(s string) (string, error) {
    c, _ := utf8.DecodeRuneInString(s)
    if f(c) {
      return string(c), nil
    } else {
      return "", errors.New("Rune was not satisfied")
    }
	})
}

func isSpace(c rune) bool {
	return c == ' ' || c == '\t' || c == '\n'
}

func isChar(a rune) func(rune) bool {
	return func(c rune) bool {
		return c == a
	}
}

////
// Debugging

func debug_function() func() {
	a := 'a'
	return func() {
		fmt.Printf("%c\n", a)
		a++
	}
}

////

var a func()

func init() {
	eof = rune(0)
	a = debug_function()
}

func main() {
	var p *Lexer

	p = new(Lexer)
	p.token = SPACE
	p.action = satisfyRune(isSpace)

	s := new(Lexer)
	s.token = SPACE
	s.action = satisfyRune(isSpace)

	q := new(Lexer)
	q.token = LEFT
	q.action = satisfyRune(isChar('c'))

  r := new(Lexer)
  r.token = 5 // Just for testing purposes
  r.action = matchString("Hello") // MatchString is no longer greedy!

	t := new(Lexer)
	t.token = 0 // eof
	t.action = matchNothing()

	plus_op := new(Lexer)
	plus_op.token = 9 // Plus
	plus_op.action = satisfyRune(isChar('+'))

	c := Operator(9)

	c.Chain(q).Chain(p).Chain(plus_op).Chain(Optionally(s)).Chain(r)

	root_list := q.Parse("c + Hello World")

	for i := range root_list {
		fmt.Println(root_list[i])
	}

}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"net/url"
	"os"
	"strconv"
	"strings"

	"github.com/cloudfoundry-incubator/routing-api"
	"github.com/cloudfoundry-incubator/routing-api-cli/commands"
	"github.com/cloudfoundry-incubator/routing-api/db"
	token_fetcher "github.com/cloudfoundry-incubator/uaa-token-fetcher"
	"github.com/codegangsta/cli"
)

var flags = []cli.Flag{
	cli.StringFlag{
		Name:  "api",
		Usage: "Endpoint for the routing-api. (required)",
	},
	cli.StringFlag{
		Name:  "oauth-name",
		Usage: "Name of the OAuth client. (required)",
	},
	cli.StringFlag{
		Name:  "oauth-password",
		Usage: "Password for OAuth client. (required)",
	},
	cli.StringFlag{
		Name:  "oauth-url",
		Usage: "URL for OAuth client. (required)",
	},
}

var cliCommands = []cli.Command{
	{
		Name:  "register",
		Usage: "Registers routes with the routing-api",
		Description: `Routes must be specified in JSON format, like so:
'[{"route":"foo.com", "port":12345, "ip":"1.2.3.4", "ttl":5, "log_guid":"log-guid"}]'`,
		Action: registerRoutes,
		Flags:  flags,
	},
	{
		Name:  "unregister",
		Usage: "Unregisters routes with the routing-api",
		Description: `Routes must be specified in JSON format, like so:
'[{"route":"foo.com", "port":12345, "ip":"1.2.3.4"]'`,
		Action: unregisterRoutes,
		Flags:  flags,
	},
	{
		Name:   "list",
		Usage:  "Lists the currently registered routes",
		Action: listRoutes,
		Flags:  flags,
	},
}

func main() {
	fmt.Println()
	app := cli.NewApp()
	app.Name = "rtr"
	app.Usage = "A CLI for the Router API server."
	authors := []cli.Author{cli.Author{Name: "Cloud Foundry Runtime Team", Email: "vcap-dev@cloudfoundry.org"}}
	app.Authors = authors
	app.Commands = cliCommands
	app.CommandNotFound = commandNotFound

	app.Run(os.Args)
	os.Exit(0)
}

func registerRoutes(c *cli.Context) {
	issues := checkFlags(c)
	issues = append(issues, checkArguments(c, "register")...)

	if len(issues) > 0 {
		printHelpForCommand(c, issues, "register")
	}

	client := routing_api.NewClient(c.String("api"))

	config := buildOauthConfig(c)
	fetcher := token_fetcher.NewTokenFetcher(&config)

	desiredRoutes := c.Args().First()
	var routes []db.Route

	err := json.Unmarshal([]byte(desiredRoutes), &routes)
	if err != nil {
		fmt.Println("Invalid json format.")
		os.Exit(3)
	}

	err = commands.Register(client, fetcher, routes)
	if err != nil {
		fmt.Println("route registration failed:", err)
		os.Exit(3)
	}

	fmt.Printf("Successfully registered routes: %s", desiredRoutes)
}

func unregisterRoutes(c *cli.Context) {
	issues := checkFlags(c)
	issues = append(issues, checkArguments(c, "unregister")...)

	if len(issues) > 0 {
		printHelpForCommand(c, issues, "unregister")
	}

	client := routing_api.NewClient(c.String("api"))

	config := buildOauthConfig(c)
	fetcher := token_fetcher.NewTokenFetcher(&config)

	desiredRoutes := c.Args().First()
	var routes []db.Route
	err := json.Unmarshal([]byte(desiredRoutes), &routes)
	if err != nil {
		fmt.Println("Invalid json format.")
		os.Exit(3)
	}

	err = commands.UnRegister(client, fetcher, routes)
	if err != nil {
		fmt.Println("route unregistration failed:", err)
		os.Exit(3)
	}

	fmt.Printf("Successfully unregistered routes: %s", desiredRoutes)
}

func listRoutes(c *cli.Context) {
	issues := checkFlags(c)
	issues = append(issues, checkArguments(c, "list")...)

	if len(issues) > 0 {
		printHelpForCommand(c, issues, "list")
	}

	client := routing_api.NewClient(c.String("api"))

	config := buildOauthConfig(c)
	fetcher := token_fetcher.NewTokenFetcher(&config)
	routes, err := commands.List(client, fetcher)
	if err != nil {
		fmt.Println("listing routes failed:", err)
		os.Exit(3)
	}

	prettyRoutes, _ := json.Marshal(routes)

	fmt.Printf("%v", string(prettyRoutes))
}

func buildOauthConfig(c *cli.Context) token_fetcher.OAuthConfig {
	var port int
	oauthUrl, _ := url.Parse(c.String("oauth-url"))
	addr := strings.Split(oauthUrl.Host, ":")
	host := addr[0]

	if len(addr) > 1 {
		port, _ = strconv.Atoi(addr[1])
	} else {
		if strings.ToLower(oauthUrl.Scheme) == "https" {
			port = 443
		} else if strings.ToLower(oauthUrl.Scheme) == "http" {
			port = 80
		}
	}

	return token_fetcher.OAuthConfig{
		TokenEndpoint: oauthUrl.Scheme + "://" + host,
		ClientName:    c.String("oauth-name"),
		ClientSecret:  c.String("oauth-password"),
		Port:          port,
	}
}

func checkFlags(c *cli.Context) []string {
	var issues []string

	if c.String("api") == "" {
		issues = append(issues, "Must provide an API endpoint for the routing-api component.")
	}

	if c.String("oauth-name") == "" {
		issues = append(issues, "Must provide the name of an OAuth client.")
	}

	if c.String("oauth-password") == "" {
		issues = append(issues, "Must provide an OAuth password/secret.")
	}

	if c.String("oauth-url") == "" {
		issues = append(issues, "Must provide an URL to the OAuth client.")
	}

	_, err := url.Parse(c.String("oauth-url"))
	if err != nil {
		issues = append(issues, "Invalid OAuth client URL")
	}

	return issues
}

func checkArguments(c *cli.Context, cmd string) []string {
	var issues []string

	switch cmd {
	case "register", "unregister":
		if len(c.Args()) > 1 {
			issues = append(issues, "Unexpected arguments.")
		} else if len(c.Args()) < 1 {
			issues = append(issues, "Must provide routes JSON.")
		}
	case "list":
		if len(c.Args()) > 0 {
			issues = append(issues, "Unexpected arguments.")
		}
	}

	return issues
}

func printHelpForCommand(c *cli.Context, issues []string, cmd string) {
	for _, issue := range issues {
		fmt.Println(issue)
	}
	fmt.Println()
	cli.ShowCommandHelp(c, cmd)
	os.Exit(1)
}

func commandNotFound(c *cli.Context, cmd string) {
	fmt.Println("Not a valid command:", cmd)
	os.Exit(1)
}
<commit_msg>Set version to 0.0.5<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"net/url"
	"os"
	"strconv"
	"strings"

	"github.com/cloudfoundry-incubator/routing-api"
	"github.com/cloudfoundry-incubator/routing-api-cli/commands"
	"github.com/cloudfoundry-incubator/routing-api/db"
	token_fetcher "github.com/cloudfoundry-incubator/uaa-token-fetcher"
	"github.com/codegangsta/cli"
)

var flags = []cli.Flag{
	cli.StringFlag{
		Name:  "api",
		Usage: "Endpoint for the routing-api. (required)",
	},
	cli.StringFlag{
		Name:  "oauth-name",
		Usage: "Name of the OAuth client. (required)",
	},
	cli.StringFlag{
		Name:  "oauth-password",
		Usage: "Password for OAuth client. (required)",
	},
	cli.StringFlag{
		Name:  "oauth-url",
		Usage: "URL for OAuth client. (required)",
	},
}

var cliCommands = []cli.Command{
	{
		Name:  "register",
		Usage: "Registers routes with the routing-api",
		Description: `Routes must be specified in JSON format, like so:
'[{"route":"foo.com", "port":12345, "ip":"1.2.3.4", "ttl":5, "log_guid":"log-guid"}]'`,
		Action: registerRoutes,
		Flags:  flags,
	},
	{
		Name:  "unregister",
		Usage: "Unregisters routes with the routing-api",
		Description: `Routes must be specified in JSON format, like so:
'[{"route":"foo.com", "port":12345, "ip":"1.2.3.4"]'`,
		Action: unregisterRoutes,
		Flags:  flags,
	},
	{
		Name:   "list",
		Usage:  "Lists the currently registered routes",
		Action: listRoutes,
		Flags:  flags,
	},
}

func main() {
	fmt.Println()
	app := cli.NewApp()
	app.Name = "rtr"
	app.Usage = "A CLI for the Router API server."
	authors := []cli.Author{cli.Author{Name: "Cloud Foundry Runtime Team", Email: "vcap-dev@cloudfoundry.org"}}
	app.Authors = authors
	app.Commands = cliCommands
	app.CommandNotFound = commandNotFound
	app.Version = "0.0.5"

	app.Run(os.Args)
	os.Exit(0)
}

func registerRoutes(c *cli.Context) {
	issues := checkFlags(c)
	issues = append(issues, checkArguments(c, "register")...)

	if len(issues) > 0 {
		printHelpForCommand(c, issues, "register")
	}

	client := routing_api.NewClient(c.String("api"))

	config := buildOauthConfig(c)
	fetcher := token_fetcher.NewTokenFetcher(&config)

	desiredRoutes := c.Args().First()
	var routes []db.Route

	err := json.Unmarshal([]byte(desiredRoutes), &routes)
	if err != nil {
		fmt.Println("Invalid json format.")
		os.Exit(3)
	}

	err = commands.Register(client, fetcher, routes)
	if err != nil {
		fmt.Println("route registration failed:", err)
		os.Exit(3)
	}

	fmt.Printf("Successfully registered routes: %s", desiredRoutes)
}

func unregisterRoutes(c *cli.Context) {
	issues := checkFlags(c)
	issues = append(issues, checkArguments(c, "unregister")...)

	if len(issues) > 0 {
		printHelpForCommand(c, issues, "unregister")
	}

	client := routing_api.NewClient(c.String("api"))

	config := buildOauthConfig(c)
	fetcher := token_fetcher.NewTokenFetcher(&config)

	desiredRoutes := c.Args().First()
	var routes []db.Route
	err := json.Unmarshal([]byte(desiredRoutes), &routes)
	if err != nil {
		fmt.Println("Invalid json format.")
		os.Exit(3)
	}

	err = commands.UnRegister(client, fetcher, routes)
	if err != nil {
		fmt.Println("route unregistration failed:", err)
		os.Exit(3)
	}

	fmt.Printf("Successfully unregistered routes: %s", desiredRoutes)
}

func listRoutes(c *cli.Context) {
	issues := checkFlags(c)
	issues = append(issues, checkArguments(c, "list")...)

	if len(issues) > 0 {
		printHelpForCommand(c, issues, "list")
	}

	client := routing_api.NewClient(c.String("api"))

	config := buildOauthConfig(c)
	fetcher := token_fetcher.NewTokenFetcher(&config)
	routes, err := commands.List(client, fetcher)
	if err != nil {
		fmt.Println("listing routes failed:", err)
		os.Exit(3)
	}

	prettyRoutes, _ := json.Marshal(routes)

	fmt.Printf("%v", string(prettyRoutes))
}

func buildOauthConfig(c *cli.Context) token_fetcher.OAuthConfig {
	var port int
	oauthUrl, _ := url.Parse(c.String("oauth-url"))
	addr := strings.Split(oauthUrl.Host, ":")
	host := addr[0]

	if len(addr) > 1 {
		port, _ = strconv.Atoi(addr[1])
	} else {
		if strings.ToLower(oauthUrl.Scheme) == "https" {
			port = 443
		} else if strings.ToLower(oauthUrl.Scheme) == "http" {
			port = 80
		}
	}

	return token_fetcher.OAuthConfig{
		TokenEndpoint: oauthUrl.Scheme + "://" + host,
		ClientName:    c.String("oauth-name"),
		ClientSecret:  c.String("oauth-password"),
		Port:          port,
	}
}

func checkFlags(c *cli.Context) []string {
	var issues []string

	if c.String("api") == "" {
		issues = append(issues, "Must provide an API endpoint for the routing-api component.")
	}

	if c.String("oauth-name") == "" {
		issues = append(issues, "Must provide the name of an OAuth client.")
	}

	if c.String("oauth-password") == "" {
		issues = append(issues, "Must provide an OAuth password/secret.")
	}

	if c.String("oauth-url") == "" {
		issues = append(issues, "Must provide an URL to the OAuth client.")
	}

	_, err := url.Parse(c.String("oauth-url"))
	if err != nil {
		issues = append(issues, "Invalid OAuth client URL")
	}

	return issues
}

func checkArguments(c *cli.Context, cmd string) []string {
	var issues []string

	switch cmd {
	case "register", "unregister":
		if len(c.Args()) > 1 {
			issues = append(issues, "Unexpected arguments.")
		} else if len(c.Args()) < 1 {
			issues = append(issues, "Must provide routes JSON.")
		}
	case "list":
		if len(c.Args()) > 0 {
			issues = append(issues, "Unexpected arguments.")
		}
	}

	return issues
}

func printHelpForCommand(c *cli.Context, issues []string, cmd string) {
	for _, issue := range issues {
		fmt.Println(issue)
	}
	fmt.Println()
	cli.ShowCommandHelp(c, cmd)
	os.Exit(1)
}

func commandNotFound(c *cli.Context, cmd string) {
	fmt.Println("Not a valid command:", cmd)
	os.Exit(1)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"os"
	"sort"
)

func main() {
	const (
		oldRevID = "HEAD~1"
		newRevID = "HEAD"
	)

	// new vcs
	var vcs git

	newDecls, err := parse(vcs, newRevID)
	if err != nil {
		fmt.Printf("Error parsing %s: %s\n", newRevID, err.Error())
		os.Exit(1)
	}

	oldDecls, err := parse(vcs, oldRevID)
	if err != nil {
		fmt.Printf("Error parsing %s: %s\n", oldRevID, err.Error())
		os.Exit(1)
	}

	for pkgName, decls := range oldDecls {
		if _, ok := newDecls[pkgName]; ok {
			changes := diff(decls, newDecls[pkgName])
			sort.Sort(byID(changes))
			for _, change := range changes {
				fmt.Println(change)
			}
		}
	}
}

func parse(vcs vcs, revision string) (map[string]decls, error) {
	files, err := vcs.ReadDir(revision, "")
	if err != nil {
		return nil, err
	}

	pkgs, err := parseFiles(vcs, revision, files)
	if err != nil {
		return nil, err
	}

	decls := make(map[string]decls) // package to id to decls
	for pkgName, pkg := range pkgs {
		for _, file := range pkg.Files {
			if decls[pkgName] == nil {
				decls[pkgName] = make(map[string]ast.Decl)
			}
			for id, decl := range getDecls(file.Decls) {
				decls[pkgName][id] = decl
			}
		}
	}

	return decls, nil
}

// TODO(bradleyfalzon): move this to a method, which already has vcs and other options set
func parseFiles(vcs vcs, rev string, files []string) (map[string]*ast.Package, error) {
	fset := token.NewFileSet()
	pkgs := make(map[string]*ast.Package)
	for _, file := range files {
		contents, err := vcs.ReadFile(rev, file)
		if err != nil {
			return nil, fmt.Errorf("could not read file %s at revision %s: %s", file, rev, err)
		}

		filename := rev + ":" + file
		src, err := parser.ParseFile(fset, filename, contents, 0)
		if err != nil {
			return nil, fmt.Errorf("could not parse file %s at revision %s: %s", file, rev, err)
		}

		pkgName := src.Name.Name
		pkg, found := pkgs[pkgName]
		if !found {
			pkg = &ast.Package{
				Name:  pkgName,
				Files: make(map[string]*ast.File),
			}
			pkgs[pkgName] = pkg
		}
		pkg.Files[filename] = src
	}

	return pkgs, nil
}

func getDecls(astDecls []ast.Decl) decls {
	decls := make(map[string]ast.Decl)
	for _, astDecl := range astDecls {
		switch d := astDecl.(type) {
		case *ast.GenDecl:
			for i := range d.Specs {
				var (
					id string
					// gdecl splits declaration blocks into individual declarations to view
					// only changed declarations, instead of all, I don't imagine it's needed
					// for TypeSpec (just ValueSpec
					gdecl *ast.GenDecl
				)
				switch s := d.Specs[i].(type) {
				case *ast.ValueSpec:
					// var / const
					id = s.Names[0].Name
					gdecl = &ast.GenDecl{Tok: d.Tok, Specs: []ast.Spec{s}}
				case *ast.TypeSpec:
					// type struct/interface/etc
					id = s.Name.Name
					gdecl = &ast.GenDecl{Tok: d.Tok, Specs: []ast.Spec{s}}
				default:
					// import or possibly other
					continue
				}
				if ast.IsExported(id) {
					decls[id] = gdecl
				}
			}
		case *ast.FuncDecl:
			// function or method
			var (
				id   string = d.Name.Name
				recv string
			)
			if d.Recv != nil {
				expr := d.Recv.List[0].Type
				switch e := expr.(type) {
				case *ast.Ident:
					recv = e.Name
				case *ast.StarExpr:
					recv = e.X.(*ast.Ident).Name
				}
				id = recv + "." + id
			}
			// If it's exported and it's either not a receiver OR the receiver is also exported
			if ast.IsExported(id) && recv == "" || ast.IsExported(recv) {
				// We're not interested in the body, nil it, alternatively we could set an
				// Body.List, but that included parenthesis on different lines when printed
				astDecl.(*ast.FuncDecl).Body = nil
				decls[id] = astDecl
			}
		default:
			panic(fmt.Errorf("Unknown decl type: %#v", astDecl))
		}
	}
	return decls
}
<commit_msg>Write errors to Stderr<commit_after>package main

import (
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"os"
	"sort"
)

func main() {
	const (
		oldRevID = "HEAD~1"
		newRevID = "HEAD"
	)

	// new vcs
	var vcs git

	newDecls, err := parse(vcs, newRevID)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error parsing %s: %s\n", newRevID, err.Error())
		os.Exit(1)
	}

	oldDecls, err := parse(vcs, oldRevID)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error parsing %s: %s\n", oldRevID, err.Error())
		os.Exit(1)
	}

	for pkgName, decls := range oldDecls {
		if _, ok := newDecls[pkgName]; ok {
			changes := diff(decls, newDecls[pkgName])
			sort.Sort(byID(changes))
			for _, change := range changes {
				fmt.Println(change)
			}
		}
	}
}

func parse(vcs vcs, revision string) (map[string]decls, error) {
	files, err := vcs.ReadDir(revision, "")
	if err != nil {
		return nil, err
	}

	pkgs, err := parseFiles(vcs, revision, files)
	if err != nil {
		return nil, err
	}

	decls := make(map[string]decls) // package to id to decls
	for pkgName, pkg := range pkgs {
		for _, file := range pkg.Files {
			if decls[pkgName] == nil {
				decls[pkgName] = make(map[string]ast.Decl)
			}
			for id, decl := range getDecls(file.Decls) {
				decls[pkgName][id] = decl
			}
		}
	}

	return decls, nil
}

// TODO(bradleyfalzon): move this to a method, which already has vcs and other options set
func parseFiles(vcs vcs, rev string, files []string) (map[string]*ast.Package, error) {
	fset := token.NewFileSet()
	pkgs := make(map[string]*ast.Package)
	for _, file := range files {
		contents, err := vcs.ReadFile(rev, file)
		if err != nil {
			return nil, fmt.Errorf("could not read file %s at revision %s: %s", file, rev, err)
		}

		filename := rev + ":" + file
		src, err := parser.ParseFile(fset, filename, contents, 0)
		if err != nil {
			return nil, fmt.Errorf("could not parse file %s at revision %s: %s", file, rev, err)
		}

		pkgName := src.Name.Name
		pkg, found := pkgs[pkgName]
		if !found {
			pkg = &ast.Package{
				Name:  pkgName,
				Files: make(map[string]*ast.File),
			}
			pkgs[pkgName] = pkg
		}
		pkg.Files[filename] = src
	}

	return pkgs, nil
}

func getDecls(astDecls []ast.Decl) decls {
	decls := make(map[string]ast.Decl)
	for _, astDecl := range astDecls {
		switch d := astDecl.(type) {
		case *ast.GenDecl:
			for i := range d.Specs {
				var (
					id string
					// gdecl splits declaration blocks into individual declarations to view
					// only changed declarations, instead of all, I don't imagine it's needed
					// for TypeSpec (just ValueSpec
					gdecl *ast.GenDecl
				)
				switch s := d.Specs[i].(type) {
				case *ast.ValueSpec:
					// var / const
					id = s.Names[0].Name
					gdecl = &ast.GenDecl{Tok: d.Tok, Specs: []ast.Spec{s}}
				case *ast.TypeSpec:
					// type struct/interface/etc
					id = s.Name.Name
					gdecl = &ast.GenDecl{Tok: d.Tok, Specs: []ast.Spec{s}}
				default:
					// import or possibly other
					continue
				}
				if ast.IsExported(id) {
					decls[id] = gdecl
				}
			}
		case *ast.FuncDecl:
			// function or method
			var (
				id   string = d.Name.Name
				recv string
			)
			if d.Recv != nil {
				expr := d.Recv.List[0].Type
				switch e := expr.(type) {
				case *ast.Ident:
					recv = e.Name
				case *ast.StarExpr:
					recv = e.X.(*ast.Ident).Name
				}
				id = recv + "." + id
			}
			// If it's exported and it's either not a receiver OR the receiver is also exported
			if ast.IsExported(id) && recv == "" || ast.IsExported(recv) {
				// We're not interested in the body, nil it, alternatively we could set an
				// Body.List, but that included parenthesis on different lines when printed
				astDecl.(*ast.FuncDecl).Body = nil
				decls[id] = astDecl
			}
		default:
			panic(fmt.Errorf("Unknown decl type: %#v", astDecl))
		}
	}
	return decls
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/go.tools/go/types"
	"github.com/kisielk/gotool"
	"honnef.co/go/importer"

	"flag"
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	"os"
	"path/filepath"
	"sort"
	"strings"
)

type stringSlice []string

func (s *stringSlice) String() string {
	return strings.Join(*s, ",")
}

func (s *stringSlice) Set(val string) error {
	*s = strings.Split(val, ",")
	return nil
}

var (
	packages  stringSlice
	arguments stringSlice
	returns   stringSlice
	and       bool
)

func init() {
	flag.Var(&packages, "pkgs", "Comma-separated list of packages to search for functions.")
	flag.Var(&arguments, "args", "Comma-separated list of argument types to match.")
	flag.Var(&returns, "rets", "Comma-separated list of return types to match.")
	flag.BoolVar(&and, "and", false, "Use AND instead of OR for matching functions.")

	flag.Parse()
}

func parseFile(fset *token.FileSet, fileName string) (f *ast.File, err error) {
	astFile, err := parser.ParseFile(fset, fileName, nil, 0)
	if err != nil {
		return f, fmt.Errorf("could not parse: %s", err)
	}

	return astFile, nil
}

type Type struct {
	Object   types.Object
	TypeName *types.TypeName
	Pointer  *types.Pointer
}

type Context struct {
	allImports map[string]*types.Package
	context    types.Context
}

func NewContext() *Context {
	importer := importer.NewImporter()
	ctx := &Context{
		allImports: importer.Imports,
		context: types.Context{
			Import: importer.Import,
		},
	}

	return ctx
}

func check(ctx *Context, name string, fset *token.FileSet, astFiles []*ast.File) (pkg *types.Package, err error) {
	return ctx.context.Check(name, fset, astFiles...)
}

func (ctx *Context) getObjects(paths []string) ([]types.Object, []error) {
	var errors []error
	var objects []types.Object

pathLoop:
	for _, path := range paths {
		buildPkg, err := build.Import(path, ".", 0)
		if err != nil {
			errors = append(errors, fmt.Errorf("Couldn't import %s: %s", path, err))
			continue
		}
		fset := token.NewFileSet()
		var astFiles []*ast.File
		var pkg *types.Package
		if buildPkg.Goroot {
			// TODO what if the compiled package in GoRoot is
			// outdated?
			pkg, err = types.GcImport(ctx.allImports, path)
			if err != nil {
				errors = append(errors, fmt.Errorf("Couldn't import %s: %s", path, err))
				continue
			}
		} else {
			if len(buildPkg.GoFiles) == 0 {
				errors = append(errors, fmt.Errorf("Couldn't parse %s: No (non cgo) Go files", path))
				continue pathLoop
			}
			for _, file := range buildPkg.GoFiles {
				astFile, err := parseFile(fset, filepath.Join(buildPkg.Dir, file))
				if err != nil {
					errors = append(errors, fmt.Errorf("Couldn't parse %s: %s", err))
					continue pathLoop
				}
				astFiles = append(astFiles, astFile)
			}
			pkg, err = check(ctx, path, fset, astFiles)
			if err != nil {
				errors = append(errors, fmt.Errorf("Couldn't parse %s: %s\n", path, err))
				continue pathLoop
			}
		}

		scope := pkg.Scope()
		for i := 0; i < scope.NumEntries(); i++ {
			obj := scope.At(i)
			objects = append(objects, obj)
		}
	}

	return objects, errors
}

// This struct only exists to work around issue 5815 (go/types: (*Func).Pkg() returns
// nil for methods from GcImport'ed packages)
type function struct {
	*types.Func
	Pkg *types.Package
}

func (ctx *Context) getFunctions(paths []string) ([]function, []error) {
	var funcs []function

	objects, errors := ctx.getObjects(paths)

	for _, obj := range objects {
		if fnc, ok := obj.(*types.Func); ok {
			funcs = append(funcs, function{fnc, obj.Pkg()})
		} else {
			typ, ok := obj.(*types.TypeName)
			if !ok {
				continue
			}

			named, ok := typ.Type().(*types.Named)
			if !ok {
				continue
			}

			for i := 0; i < named.NumMethods(); i++ {
				funcs = append(funcs, function{named.Method(i), obj.Pkg()})
			}
		}
	}

	return funcs, errors
}

func listErrors(errors []error) {
	for _, err := range errors {
		fmt.Println(err)
	}
}

func noDot(s string) string {
	index := strings.Index(s, "·")
	if index == -1 {
		return s
	}

	return s[:index]
}

func argsToString(args *types.Tuple) string {
	ret := make([]string, args.Len())
	for i := 0; i < args.Len(); i++ {
		name := noDot(args.At(i).Name())
		typ := args.At(i).Type().String()

		if len(name) == 0 {
			ret[i] = typ
		} else {
			ret[i] = name + " " + typ
		}
	}

	return strings.Join(ret, ", ")
}

func checkTypes(args *types.Tuple, types []string) (any, all bool) {
	matched := make([]bool, len(types))
	for i := 0; i < args.Len(); i++ {
		for k, toCheck := range types {
			if args.At(i).Type().String() == toCheck {
				matched[k] = true
				any = true
			}
		}
	}

	for _, b := range matched {
		if !b {
			return any, false
		}
	}

	return any, true
}

func sortedKeys(m map[string][]string) []string {
	keys := make([]string, 0, len(m))
	for k := range m {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	return keys
}

func main() {
	if len(packages) == 0 {
		fmt.Fprintln(os.Stderr, "Need to specify at least one package to check.")
		flag.Usage()
		os.Exit(1)
	}

	if len(arguments)+len(returns) == 0 {
		fmt.Fprintln(os.Stderr, "Need at least one type to search for.")
		flag.Usage()
		os.Exit(1)
	}

	var typesToCheck []string
	typesToCheck = append(typesToCheck, arguments...)
	typesToCheck = append(typesToCheck, returns...)

	ctx := NewContext()
	funcs, errs := ctx.getFunctions(gotool.ImportPaths(packages))
	listErrors(errs)

	signatures := make(map[string][]string)

	for _, fnc := range funcs {
		sig, ok := fnc.Type().(*types.Signature)
		if !ok {
			// Skipping over builtins
			continue
		}

		anyArg, allArg := checkTypes(sig.Params(), arguments)
		anyRet, allRet := checkTypes(sig.Results(), returns)

		if (!and && (anyArg || anyRet)) || (and && allArg && allRet) {
			prefix := ""
			if sig.Recv() != nil {
				prefix = fmt.Sprintf("(%s %s) ", noDot(sig.Recv().Name()), sig.Recv().Type().String())
			}

			signatures[fnc.Pkg.Path()] = append(signatures[fnc.Pkg.Path()],
				fmt.Sprintf("%s%s(%s) (%s)",
					prefix,
					fnc.Name(),
					argsToString(sig.Params()),
					argsToString(sig.Results())))
		}
	}

	for _, path := range sortedKeys(signatures) {
		sigs := signatures[path]
		fmt.Println(path + ":")
		for _, sig := range sigs {
			fmt.Println("\t" + sig)
		}
		fmt.Println()
	}
}
<commit_msg>update for new go/types API<commit_after>package main

import (
	"code.google.com/p/go.tools/go/types"
	"github.com/kisielk/gotool"
	"honnef.co/go/importer"

	"flag"
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	"os"
	"path/filepath"
	"sort"
	"strings"
)

type stringSlice []string

func (s *stringSlice) String() string {
	return strings.Join(*s, ",")
}

func (s *stringSlice) Set(val string) error {
	*s = strings.Split(val, ",")
	return nil
}

var (
	packages  stringSlice
	arguments stringSlice
	returns   stringSlice
	and       bool
)

func init() {
	flag.Var(&packages, "pkgs", "Comma-separated list of packages to search for functions.")
	flag.Var(&arguments, "args", "Comma-separated list of argument types to match.")
	flag.Var(&returns, "rets", "Comma-separated list of return types to match.")
	flag.BoolVar(&and, "and", false, "Use AND instead of OR for matching functions.")

	flag.Parse()
}

func parseFile(fset *token.FileSet, fileName string) (f *ast.File, err error) {
	astFile, err := parser.ParseFile(fset, fileName, nil, 0)
	if err != nil {
		return f, fmt.Errorf("could not parse: %s", err)
	}

	return astFile, nil
}

type Type struct {
	Object   types.Object
	TypeName *types.TypeName
	Pointer  *types.Pointer
}

type Context struct {
	allImports map[string]*types.Package
	context    types.Context
}

func NewContext() *Context {
	importer := importer.NewImporter()
	ctx := &Context{
		allImports: importer.Imports,
		context: types.Context{
			Import: importer.Import,
		},
	}

	return ctx
}

func check(ctx *Context, name string, fset *token.FileSet, astFiles []*ast.File) (pkg *types.Package, err error) {
	return ctx.context.Check(name, fset, astFiles, nil)
}

func (ctx *Context) getObjects(paths []string) ([]types.Object, []error) {
	var errors []error
	var objects []types.Object

pathLoop:
	for _, path := range paths {
		buildPkg, err := build.Import(path, ".", 0)
		if err != nil {
			errors = append(errors, fmt.Errorf("Couldn't import %s: %s", path, err))
			continue
		}
		fset := token.NewFileSet()
		var astFiles []*ast.File
		var pkg *types.Package
		if buildPkg.Goroot {
			// TODO what if the compiled package in GoRoot is
			// outdated?
			pkg, err = types.GcImport(ctx.allImports, path)
			if err != nil {
				errors = append(errors, fmt.Errorf("Couldn't import %s: %s", path, err))
				continue
			}
		} else {
			if len(buildPkg.GoFiles) == 0 {
				errors = append(errors, fmt.Errorf("Couldn't parse %s: No (non cgo) Go files", path))
				continue pathLoop
			}
			for _, file := range buildPkg.GoFiles {
				astFile, err := parseFile(fset, filepath.Join(buildPkg.Dir, file))
				if err != nil {
					errors = append(errors, fmt.Errorf("Couldn't parse %s: %s", err))
					continue pathLoop
				}
				astFiles = append(astFiles, astFile)
			}
			pkg, err = check(ctx, path, fset, astFiles)
			if err != nil {
				errors = append(errors, fmt.Errorf("Couldn't parse %s: %s\n", path, err))
				continue pathLoop
			}
		}

		scope := pkg.Scope()
		for i := 0; i < scope.NumEntries(); i++ {
			obj := scope.At(i)
			objects = append(objects, obj)
		}
	}

	return objects, errors
}

// This struct only exists to work around issue 5815 (go/types: (*Func).Pkg() returns
// nil for methods from GcImport'ed packages)
type function struct {
	*types.Func
	Pkg *types.Package
}

func (ctx *Context) getFunctions(paths []string) ([]function, []error) {
	var funcs []function

	objects, errors := ctx.getObjects(paths)

	for _, obj := range objects {
		if fnc, ok := obj.(*types.Func); ok {
			funcs = append(funcs, function{fnc, obj.Pkg()})
		} else {
			typ, ok := obj.(*types.TypeName)
			if !ok {
				continue
			}

			named, ok := typ.Type().(*types.Named)
			if !ok {
				continue
			}

			for i := 0; i < named.NumMethods(); i++ {
				funcs = append(funcs, function{named.Method(i), obj.Pkg()})
			}
		}
	}

	return funcs, errors
}

func listErrors(errors []error) {
	for _, err := range errors {
		fmt.Println(err)
	}
}

func noDot(s string) string {
	index := strings.Index(s, "·")
	if index == -1 {
		return s
	}

	return s[:index]
}

func argsToString(args *types.Tuple) string {
	ret := make([]string, args.Len())
	for i := 0; i < args.Len(); i++ {
		name := noDot(args.At(i).Name())
		typ := args.At(i).Type().String()

		if len(name) == 0 {
			ret[i] = typ
		} else {
			ret[i] = name + " " + typ
		}
	}

	return strings.Join(ret, ", ")
}

func checkTypes(args *types.Tuple, types []string) (any, all bool) {
	matched := make([]bool, len(types))
	for i := 0; i < args.Len(); i++ {
		for k, toCheck := range types {
			if args.At(i).Type().String() == toCheck {
				matched[k] = true
				any = true
			}
		}
	}

	for _, b := range matched {
		if !b {
			return any, false
		}
	}

	return any, true
}

func sortedKeys(m map[string][]string) []string {
	keys := make([]string, 0, len(m))
	for k := range m {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	return keys
}

func main() {
	if len(packages) == 0 {
		fmt.Fprintln(os.Stderr, "Need to specify at least one package to check.")
		flag.Usage()
		os.Exit(1)
	}

	if len(arguments)+len(returns) == 0 {
		fmt.Fprintln(os.Stderr, "Need at least one type to search for.")
		flag.Usage()
		os.Exit(1)
	}

	var typesToCheck []string
	typesToCheck = append(typesToCheck, arguments...)
	typesToCheck = append(typesToCheck, returns...)

	ctx := NewContext()
	funcs, errs := ctx.getFunctions(gotool.ImportPaths(packages))
	listErrors(errs)

	signatures := make(map[string][]string)

	for _, fnc := range funcs {
		sig, ok := fnc.Type().(*types.Signature)
		if !ok {
			// Skipping over builtins
			continue
		}

		anyArg, allArg := checkTypes(sig.Params(), arguments)
		anyRet, allRet := checkTypes(sig.Results(), returns)

		if (!and && (anyArg || anyRet)) || (and && allArg && allRet) {
			prefix := ""
			if sig.Recv() != nil {
				prefix = fmt.Sprintf("(%s %s) ", noDot(sig.Recv().Name()), sig.Recv().Type().String())
			}

			signatures[fnc.Pkg.Path()] = append(signatures[fnc.Pkg.Path()],
				fmt.Sprintf("%s%s(%s) (%s)",
					prefix,
					fnc.Name(),
					argsToString(sig.Params()),
					argsToString(sig.Results())))
		}
	}

	for _, path := range sortedKeys(signatures) {
		sigs := signatures[path]
		fmt.Println(path + ":")
		for _, sig := range sigs {
			fmt.Println("\t" + sig)
		}
		fmt.Println()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"log"
	"os"

	"github.com/jessevdk/go-flags"
	"github.com/tarm/serial"
)

type Options struct {
	Port string `short:"p" long:"port" description:"Serial Port"`
	Baud int    `short:"b" long:"baud" description:"Baud Rate"`
}

var opts Options

func main() {
	_, err := flags.Parse(&opts)
	if err != nil {
		os.Exit(1)
	}

	c := &serial.Config{Name: opts.Port, Baud: opts.Baud}
	s, err := serial.OpenPort(c)
	if err != nil {
		log.Fatal(err)
	}

	done := make(chan bool)
	buf := make([]byte, 256)
	scanner := bufio.NewScanner(os.Stdin)

	for value := 0; ; {
		value++
		go func() {
			for scanner.Scan() {
				_, err := s.Write([]byte(scanner.Text()))
				if err != nil {
					log.Fatal(err)
				}
			}
		}()

		go func() {
			n, err := s.Read(buf)
			if err != nil {
				log.Fatal(err)
			}
			if n > 0 {
				fmt.Fprintf(os.Stdout, "%s", buf[:n])
			}
			done <- true
		}()
		<-done
	}
}
<commit_msg>Fix infinite loop writing style<commit_after>package main

import (
	"bufio"
	"fmt"
	"log"
	"os"

	"github.com/jessevdk/go-flags"
	"github.com/tarm/serial"
)

type Options struct {
	Port string `short:"p" long:"port" description:"Serial Port"`
	Baud int    `short:"b" long:"baud" description:"Baud Rate"`
}

var opts Options

func main() {
	_, err := flags.Parse(&opts)
	if err != nil {
		os.Exit(1)
	}

	c := &serial.Config{Name: opts.Port, Baud: opts.Baud}
	s, err := serial.OpenPort(c)
	if err != nil {
		log.Fatal(err)
	}

	done := make(chan bool)
	buf := make([]byte, 256)
	scanner := bufio.NewScanner(os.Stdin)

	for {
		go func() {
			for scanner.Scan() {
				_, err := s.Write([]byte(scanner.Text()))
				if err != nil {
					log.Fatal(err)
				}
			}
		}()

		go func() {
			n, err := s.Read(buf)
			if err != nil {
				log.Fatal(err)
			}
			if n > 0 {
				fmt.Fprintf(os.Stdout, "%s", buf[:n])
			}
			done <- true
		}()
		<-done
	}
}
<|endoftext|>