<commit_before>package main

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
	"strconv"
	"time"

	"github.com/bluele/slack"
	"github.com/codegangsta/cli"
	"github.com/fatih/color"
	"github.com/skratchdot/open-golang/open"
)

var version = "dev-build"

const (
	base_url  = "https://slack.com/oauth/authorize"
	client_id = "7065709201.17699618306"
	scope     = "channels%3Aread+groups%3Aread+im%3Aread+users%3Aread+chat%3Awrite%3Auser+files%3Awrite%3Auser+files%3Aread"
)

func getConfigPath() string {
	usr, err := user.Current()
	failOnError(err, "unable to determine current user", true)
	return usr.HomeDir + "/.slackcat"
}

func readConfig() string {
	path := getConfigPath()
	file, err := os.Open(path)
	failOnError(err, "unable to read config", true)
	defer file.Close()

	var lines []string
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}

	return lines[0]
}

func configureOA() {
	oa_url := base_url + "?scope=" + scope + "&client_id=" + client_id
	output("Creating token request for Slackcat")
	err := open.Run(oa_url)
	if err != nil {
		output("Please open the below URL in your browser to authorize SlackCat")
		output(oa_url)
	}
	//	_, err := fmt.Scanf("%s", &i)
	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	failOnError(err, "", true)
	fmt.Println(dir)
	os.Exit(0)
}

func readIn(lines chan string, tee bool) {
	scanner := bufio.NewScanner(os.Stdin)
	scanner.Split(bufio.ScanLines)
	for scanner.Scan() {
		lines <- scanner.Text()
		if tee {
			fmt.Println(scanner.Text())
		}
	}
	close(lines)
}

func writeTemp(lines chan string) string {
	tmp, err := ioutil.TempFile(os.TempDir(), "slackcat-")
	failOnError(err, "unable to create tmpfile", false)

	w := bufio.NewWriter(tmp)
	for line := range lines {
		fmt.Fprintln(w, line)
	}
	w.Flush()

	return tmp.Name()
}

func output(s string) {
	cyan := color.New(color.Bold).SprintFunc()
	fmt.Printf("%s %s\n", cyan("slackcat"), s)
}

func failOnError(err error, msg string, appendErr bool) {
	if err != nil {
		if appendErr {
			exit(fmt.Errorf("%s: %s", msg, err))
		} else {
			exit(fmt.Errorf("%s", msg))
		}
	}
}

func exit(err error) {
	output(color.RedString(err.Error()))
	os.Exit(1)
}

func main() {
	app := cli.NewApp()
	app.Name = "slackcat"
	app.Usage = "redirect a file to slack"
	app.Version = version
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "tee, t",
			Usage: "Print stdin to screen before posting",
		},
		cli.BoolFlag{
			Name:  "noop",
			Usage: "Skip posting file to Slack. Useful for testing",
		},
		cli.BoolFlag{
			Name:  "configure",
			Usage: "Configure Slackcat via oauth",
		},
		cli.StringFlag{
			Name:  "channel, c",
			Usage: "Slack channel or group to post to",
		},
		cli.StringFlag{
			Name:  "filename, n",
			Usage: "Filename for upload. Defaults to current timestamp",
		},
	}

	app.Action = func(c *cli.Context) {
		var filePath string
		var fileName string
		var channelId string

		if c.Bool("configure") {
			configureOA()
		}

		token := readConfig()
		api := slack.New(token)

		if c.String("channel") == "" {
			exit(fmt.Errorf("no channel provided!"))
		}

		channel, err := api.FindChannelByName(c.String("channel"))
		if err != nil {
			group, err := api.FindGroupByName(c.String("channel"))
			failOnError(err, "Slack API error", true)
			channelId = group.Id
		} else {
			channelId = channel.Id
		}

		if len(c.Args()) > 0 {
			filePath = c.Args()[0]
			fileName = filepath.Base(filePath)
		} else {
			lines := make(chan string)
			go readIn(lines, c.Bool("tee"))
			filePath = writeTemp(lines)
			fileName = strconv.FormatInt(time.Now().Unix(), 10)
			defer os.Remove(filePath)
		}

		//override default filename with provided option value
		if c.String("filename") != "" {
			fileName = c.String("filename")
		}

		if c.Bool("noop") {
			output(fmt.Sprintf("skipping upload of file %s to %s", fileName, c.String("channel")))
		} else {
			start := time.Now()
			err = api.FilesUpload(&slack.FilesUploadOpt{
				Filepath: filePath,
				Filename: fileName,
				Title:    fileName,
				Channels: []string{channelId},
			})
			failOnError(err, "error uploading file to Slack", true)
			duration := strconv.FormatFloat(time.Since(start).Seconds(), 'f', 3, 64)
			output(fmt.Sprintf("file %s uploaded to %s (%ss)", fileName, c.String("channel"), duration))
		}
	}

	app.Run(os.Args)

}
<commit_msg>replace user.Current with os.Getenv("HOME") for cross-platform compat<commit_after>package main

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

	"github.com/bluele/slack"
	"github.com/codegangsta/cli"
	"github.com/fatih/color"
	"github.com/skratchdot/open-golang/open"
)

var version = "dev-build"

const (
	base_url  = "https://slack.com/oauth/authorize"
	client_id = "7065709201.17699618306"
	scope     = "channels%3Aread+groups%3Aread+im%3Aread+users%3Aread+chat%3Awrite%3Auser+files%3Awrite%3Auser+files%3Aread"
)

func getConfigPath() string {
	homedir := os.Getenv("HOME")
	if homedir == "" {
		exit(fmt.Errorf("$HOME not set"))
	}
	return homedir + "/.slackcat"
}

func readConfig() string {
	path := getConfigPath()
	file, err := os.Open(path)
	failOnError(err, "unable to read config", true)
	defer file.Close()

	var lines []string
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}

	return lines[0]
}

func configureOA() {
	oa_url := base_url + "?scope=" + scope + "&client_id=" + client_id
	output("Creating token request for Slackcat")
	err := open.Run(oa_url)
	if err != nil {
		output("Please open the below URL in your browser to authorize SlackCat")
		output(oa_url)
	}
	//	_, err := fmt.Scanf("%s", &i)
	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	failOnError(err, "", true)
	fmt.Println(dir)
	os.Exit(0)
}

func readIn(lines chan string, tee bool) {
	scanner := bufio.NewScanner(os.Stdin)
	scanner.Split(bufio.ScanLines)
	for scanner.Scan() {
		lines <- scanner.Text()
		if tee {
			fmt.Println(scanner.Text())
		}
	}
	close(lines)
}

func writeTemp(lines chan string) string {
	tmp, err := ioutil.TempFile(os.TempDir(), "slackcat-")
	failOnError(err, "unable to create tmpfile", false)

	w := bufio.NewWriter(tmp)
	for line := range lines {
		fmt.Fprintln(w, line)
	}
	w.Flush()

	return tmp.Name()
}

func output(s string) {
	cyan := color.New(color.Bold).SprintFunc()
	fmt.Printf("%s %s\n", cyan("slackcat"), s)
}

func failOnError(err error, msg string, appendErr bool) {
	if err != nil {
		if appendErr {
			exit(fmt.Errorf("%s: %s", msg, err))
		} else {
			exit(fmt.Errorf("%s", msg))
		}
	}
}

func exit(err error) {
	output(color.RedString(err.Error()))
	os.Exit(1)
}

func main() {
	app := cli.NewApp()
	app.Name = "slackcat"
	app.Usage = "redirect a file to slack"
	app.Version = version
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "tee, t",
			Usage: "Print stdin to screen before posting",
		},
		cli.BoolFlag{
			Name:  "noop",
			Usage: "Skip posting file to Slack. Useful for testing",
		},
		cli.BoolFlag{
			Name:  "configure",
			Usage: "Configure Slackcat via oauth",
		},
		cli.StringFlag{
			Name:  "channel, c",
			Usage: "Slack channel or group to post to",
		},
		cli.StringFlag{
			Name:  "filename, n",
			Usage: "Filename for upload. Defaults to current timestamp",
		},
	}

	app.Action = func(c *cli.Context) {
		var filePath string
		var fileName string
		var channelId string

		if c.Bool("configure") {
			configureOA()
		}

		token := readConfig()
		api := slack.New(token)

		if c.String("channel") == "" {
			exit(fmt.Errorf("no channel provided!"))
		}

		channel, err := api.FindChannelByName(c.String("channel"))
		if err != nil {
			group, err := api.FindGroupByName(c.String("channel"))
			failOnError(err, "Slack API error", true)
			channelId = group.Id
		} else {
			channelId = channel.Id
		}

		if len(c.Args()) > 0 {
			filePath = c.Args()[0]
			fileName = filepath.Base(filePath)
		} else {
			lines := make(chan string)
			go readIn(lines, c.Bool("tee"))
			filePath = writeTemp(lines)
			fileName = strconv.FormatInt(time.Now().Unix(), 10)
			defer os.Remove(filePath)
		}

		//override default filename with provided option value
		if c.String("filename") != "" {
			fileName = c.String("filename")
		}

		if c.Bool("noop") {
			output(fmt.Sprintf("skipping upload of file %s to %s", fileName, c.String("channel")))
		} else {
			start := time.Now()
			err = api.FilesUpload(&slack.FilesUploadOpt{
				Filepath: filePath,
				Filename: fileName,
				Title:    fileName,
				Channels: []string{channelId},
			})
			failOnError(err, "error uploading file to Slack", true)
			duration := strconv.FormatFloat(time.Since(start).Seconds(), 'f', 3, 64)
			output(fmt.Sprintf("file %s uploaded to %s (%ss)", fileName, c.String("channel"), duration))
		}
	}

	app.Run(os.Args)

}
<|endoftext|>


<commit_before>package order

import (
	"errors"
	"fmt"
	"strconv"

	stripe "github.com/stripe/stripe-go"
)

// Client is used to invoke /orders APIs.
type Client struct {
	B   stripe.Backend
	Key string
}

// New POSTs a new order.
// For more details see https://stripe.com/docs/api#create_order.
func New(params *stripe.OrderParams) (*stripe.Order, error) {
	return getC().New(params)
}

// New POSTs a new order.
// For more details see https://stripe.com/docs/api#create_order.
func (c Client) New(params *stripe.OrderParams) (*stripe.Order, error) {
	var body *stripe.RequestValues
	var commonParams *stripe.Params

	if params != nil {
		body = &stripe.RequestValues{}
		commonParams = &params.Params

		if params.Coupon != "" {
			body.Add("coupon", params.Coupon)
		}

		body.Add("currency", string(params.Currency))

		if params.Customer != "" {
			body.Add("customer", params.Customer)
		}

		if params.Email != "" {
			body.Add("email", params.Email)
		}

		if len(params.Items) > 0 {
			for _, item := range params.Items {
				body.Add("items[][description]", item.Description)
				body.Add("items[][type]", string(item.Type))
				body.Add("items[][amount]", strconv.FormatInt(item.Amount, 10))
				if item.Currency != "" {
					body.Add("items[][currency]", string(item.Currency))
				}
				if item.Parent != "" {
					body.Add("items[][parent]", item.Parent)
				}
				if item.Quantity != nil {
					body.Add("items[][quantity]", strconv.FormatInt(*item.Quantity, 10))
				}
			}
		}

		if params.Shipping != nil {
			if params.Shipping.Address != nil {
				body.Add("shipping[address][line1]", params.Shipping.Address.Line1)
				if params.Shipping.Address.Line2 != "" {
					body.Add("shipping[address][line2]", params.Shipping.Address.Line2)
				}
				if params.Shipping.Address.City != "" {
					body.Add("shipping[address][city]", params.Shipping.Address.City)
				}
				if params.Shipping.Address.Country != "" {
					body.Add("shipping[address][country]", params.Shipping.Address.Country)
				}
				if params.Shipping.Address.PostalCode != "" {
					body.Add("shipping[address][postal_code]", params.Shipping.Address.PostalCode)
				}
				if params.Shipping.Address.State != "" {
					body.Add("shipping[address][state]", params.Shipping.Address.State)
				}
			}

			if params.Shipping.Name != "" {
				body.Add("shipping[name]", params.Shipping.Name)
			}
			if params.Shipping.Phone != "" {
				body.Add("shipping[phone]", params.Shipping.Phone)
			}
		}

		params.AppendTo(body)
	}

	p := &stripe.Order{}
	err := c.B.Call("POST", "/orders", c.Key, body, commonParams, p)

	return p, err
}

// Update updates an order's properties.
// For more details see https://stripe.com/docs/api#update_order.
func Update(id string, params *stripe.OrderUpdateParams) (*stripe.Order, error) {
	return getC().Update(id, params)
}

// Update updates an order's properties.
// For more details see https://stripe.com/docs/api#update_order.
func (c Client) Update(id string, params *stripe.OrderUpdateParams) (*stripe.Order, error) {
	var body *stripe.RequestValues
	var commonParams *stripe.Params

	if params != nil {
		body = &stripe.RequestValues{}
		commonParams = &params.Params

		if params.Coupon != "" {
			body.Add("coupon", params.Coupon)
		}

		if params.SelectedShippingMethod != "" {
			body.Add("selected_shipping_method", params.SelectedShippingMethod)
		}

		if params.Status != "" {
			body.Add("status", string(params.Status))
		}

		params.AppendTo(body)
	}

	o := &stripe.Order{}
	err := c.B.Call("POST", "/orders/"+id, c.Key, body, commonParams, o)

	return o, err
}

// Pay pays an order
// For more details see https://stripe.com/docs/api#pay_order.
func Pay(id string, params *stripe.OrderPayParams) (*stripe.Order, error) {
	return getC().Pay(id, params)
}

// Pay pays an order
// For more details see https://stripe.com/docs/api#pay_order.
func (c Client) Pay(id string, params *stripe.OrderPayParams) (*stripe.Order, error) {
	var body *stripe.RequestValues
	var commonParams *stripe.Params

	if params != nil {
		body = &stripe.RequestValues{}
		commonParams = &params.Params
		if params.Source == nil && len(params.Customer) == 0 {
			err := errors.New("Invalid order pay params: either customer or a source must be set")
			return nil, err
		}
		// We can't use `AppendDetails` since that nests under `card`.
		if params.Source != nil {
			if len(params.Source.Token) > 0 {
				body.Add("source", params.Source.Token)
			} else if params.Source.Card != nil {
				c := params.Source.Card

				body.Add("source[object]", "card")
				body.Add("source[number]", c.Number)
				body.Add("source[exp_month]", c.Month)
				body.Add("source[exp_year]", c.Year)

				if len(c.CVC) > 0 {
					body.Add("source[cvc]", c.CVC)
				}

				body.Add("source[name]", c.Name)

				if len(c.Address1) > 0 {
					body.Add("source[address_line1]", c.Address1)
				}

				if len(c.Address2) > 0 {
					body.Add("source[address_line2]", c.Address2)
				}
				if len(c.City) > 0 {
					body.Add("source[address_city]", c.City)
				}

				if len(c.State) > 0 {
					body.Add("source[address_state]", c.State)
				}
				if len(c.Zip) > 0 {
					body.Add("source[address_zip]", c.Zip)
				}
				if len(c.Country) > 0 {
					body.Add("source[address_country]", c.Country)
				}
			}
		}

		if len(params.Customer) > 0 {
			body.Add("customer", params.Customer)
		}

		if params.ApplicationFee > 0 {
			body.Add("application_fee", strconv.FormatInt(params.ApplicationFee, 10))
		}

		if params.Email != "" {
			body.Add("email", params.Email)
		}

		params.AppendTo(body)
	}

	o := &stripe.Order{}
	err := c.B.Call("POST", "/orders/"+id+"/pay", c.Key, body, commonParams, o)

	return o, err
}

// Get returns the details of an order
// For more details see https://stripe.com/docs/api#retrieve_order.
func Get(id string, params *stripe.OrderParams) (*stripe.Order, error) {
	return getC().Get(id, params)
}

func (c Client) Get(id string, params *stripe.OrderParams) (*stripe.Order, error) {
	var body *stripe.RequestValues
	var commonParams *stripe.Params

	if params != nil {
		body = &stripe.RequestValues{}
		commonParams = &params.Params
		params.AppendTo(body)
	}

	order := &stripe.Order{}
	err := c.B.Call("GET", "/orders/"+id, c.Key, body, commonParams, order)
	return order, err
}

// List returns a list of orders.
// For more details see https://stripe.com/docs/api#list_orders
func List(params *stripe.OrderListParams) *Iter {
	return getC().List(params)
}

func (c Client) List(params *stripe.OrderListParams) *Iter {
	var body *stripe.RequestValues
	var lp *stripe.ListParams
	var p *stripe.Params

	if params != nil {
		body = &stripe.RequestValues{}

		for _, id := range params.IDs {
			params.Filters.AddFilter("ids[]", "", id)
		}

		if params.Status != "" {
			params.Filters.AddFilter("status", "", string(params.Status))
		}

		params.AppendTo(body)
		lp = &params.ListParams
		p = params.ToParams()
	}

	return &Iter{stripe.GetIter(lp, body, func(b *stripe.RequestValues) ([]interface{}, stripe.ListMeta, error) {
		list := &stripe.OrderList{}
		err := c.B.Call("GET", "/orders", c.Key, b, p, list)

		ret := make([]interface{}, len(list.Values))
		for i, v := range list.Values {
			ret[i] = v
		}

		return ret, list.ListMeta, err
	})}
}

// Iter is an iterator for lists of Orders.
// The embedded Iter carries methods with it;
// see its documentation for details.
type Iter struct {
	*stripe.Iter
}

// Order returns the most recent Order
// visited by a call to Next.
func (i *Iter) Order() *stripe.Order {
	return i.Current().(*stripe.Order)
}

// Return returns all or part of an order.
// For more details see https://stripe.com/docs/api#return_order.
func Return(id string, params *stripe.OrderReturnParams) (*stripe.OrderReturn, error) {
	return getC().Return(id, params)
}

// Return returns all or part of an order.
// For more details see https://stripe.com/docs/api#return_order.
func (c Client) Return(id string, params *stripe.OrderReturnParams) (*stripe.OrderReturn, error) {
	var body *stripe.RequestValues
	var commonParams *stripe.Params

	if params != nil {
		body = &stripe.RequestValues{}

		if len(params.Items) > 0 {
			for _, item := range params.Items {
				if item.Description != "" {
					body.Add("items[][description]", item.Description)
				}
				body.Add("items[][type]", string(item.Type))
				if item.Amount > 0 {
					body.Add("items[][amount]", strconv.FormatInt(item.Amount, 10))
				}
				if item.Currency != "" {
					body.Add("items[][currency]", string(item.Currency))
				}
				if item.Parent != "" {
					body.Add("items[][parent]", item.Parent)
				}
				if item.Quantity != nil {
					body.Add("items[][quantity]", strconv.FormatInt(*item.Quantity, 10))
				}
			}
		}

		params.AppendTo(body)
	}

	ret := &stripe.OrderReturn{}
	err := c.B.Call("POST", fmt.Sprintf("/orders/%s/returns", id), c.Key, body, commonParams, ret)

	return ret, err
}

func getC() Client {
	return Client{stripe.GetBackend(stripe.APIBackend), stripe.Key}
}
<commit_msg>Order Item description is optional and should not be sent if it's empty.<commit_after>package order

import (
	"errors"
	"fmt"
	"strconv"

	stripe "github.com/stripe/stripe-go"
)

// Client is used to invoke /orders APIs.
type Client struct {
	B   stripe.Backend
	Key string
}

// New POSTs a new order.
// For more details see https://stripe.com/docs/api#create_order.
func New(params *stripe.OrderParams) (*stripe.Order, error) {
	return getC().New(params)
}

// New POSTs a new order.
// For more details see https://stripe.com/docs/api#create_order.
func (c Client) New(params *stripe.OrderParams) (*stripe.Order, error) {
	var body *stripe.RequestValues
	var commonParams *stripe.Params

	if params != nil {
		body = &stripe.RequestValues{}
		commonParams = &params.Params

		if params.Coupon != "" {
			body.Add("coupon", params.Coupon)
		}

		body.Add("currency", string(params.Currency))

		if params.Customer != "" {
			body.Add("customer", params.Customer)
		}

		if params.Email != "" {
			body.Add("email", params.Email)
		}

		if len(params.Items) > 0 {
			for _, item := range params.Items {
				if item.Description != "" {
					body.Add("items[][description]", item.Description)
				}

				body.Add("items[][type]", string(item.Type))
				body.Add("items[][amount]", strconv.FormatInt(item.Amount, 10))
				if item.Currency != "" {
					body.Add("items[][currency]", string(item.Currency))
				}
				if item.Parent != "" {
					body.Add("items[][parent]", item.Parent)
				}
				if item.Quantity != nil {
					body.Add("items[][quantity]", strconv.FormatInt(*item.Quantity, 10))
				}
			}
		}

		if params.Shipping != nil {
			if params.Shipping.Address != nil {
				body.Add("shipping[address][line1]", params.Shipping.Address.Line1)
				if params.Shipping.Address.Line2 != "" {
					body.Add("shipping[address][line2]", params.Shipping.Address.Line2)
				}
				if params.Shipping.Address.City != "" {
					body.Add("shipping[address][city]", params.Shipping.Address.City)
				}
				if params.Shipping.Address.Country != "" {
					body.Add("shipping[address][country]", params.Shipping.Address.Country)
				}
				if params.Shipping.Address.PostalCode != "" {
					body.Add("shipping[address][postal_code]", params.Shipping.Address.PostalCode)
				}
				if params.Shipping.Address.State != "" {
					body.Add("shipping[address][state]", params.Shipping.Address.State)
				}
			}

			if params.Shipping.Name != "" {
				body.Add("shipping[name]", params.Shipping.Name)
			}
			if params.Shipping.Phone != "" {
				body.Add("shipping[phone]", params.Shipping.Phone)
			}
		}

		params.AppendTo(body)
	}

	p := &stripe.Order{}
	err := c.B.Call("POST", "/orders", c.Key, body, commonParams, p)

	return p, err
}

// Update updates an order's properties.
// For more details see https://stripe.com/docs/api#update_order.
func Update(id string, params *stripe.OrderUpdateParams) (*stripe.Order, error) {
	return getC().Update(id, params)
}

// Update updates an order's properties.
// For more details see https://stripe.com/docs/api#update_order.
func (c Client) Update(id string, params *stripe.OrderUpdateParams) (*stripe.Order, error) {
	var body *stripe.RequestValues
	var commonParams *stripe.Params

	if params != nil {
		body = &stripe.RequestValues{}
		commonParams = &params.Params

		if params.Coupon != "" {
			body.Add("coupon", params.Coupon)
		}

		if params.SelectedShippingMethod != "" {
			body.Add("selected_shipping_method", params.SelectedShippingMethod)
		}

		if params.Status != "" {
			body.Add("status", string(params.Status))
		}

		params.AppendTo(body)
	}

	o := &stripe.Order{}
	err := c.B.Call("POST", "/orders/"+id, c.Key, body, commonParams, o)

	return o, err
}

// Pay pays an order
// For more details see https://stripe.com/docs/api#pay_order.
func Pay(id string, params *stripe.OrderPayParams) (*stripe.Order, error) {
	return getC().Pay(id, params)
}

// Pay pays an order
// For more details see https://stripe.com/docs/api#pay_order.
func (c Client) Pay(id string, params *stripe.OrderPayParams) (*stripe.Order, error) {
	var body *stripe.RequestValues
	var commonParams *stripe.Params

	if params != nil {
		body = &stripe.RequestValues{}
		commonParams = &params.Params
		if params.Source == nil && len(params.Customer) == 0 {
			err := errors.New("Invalid order pay params: either customer or a source must be set")
			return nil, err
		}
		// We can't use `AppendDetails` since that nests under `card`.
		if params.Source != nil {
			if len(params.Source.Token) > 0 {
				body.Add("source", params.Source.Token)
			} else if params.Source.Card != nil {
				c := params.Source.Card

				body.Add("source[object]", "card")
				body.Add("source[number]", c.Number)
				body.Add("source[exp_month]", c.Month)
				body.Add("source[exp_year]", c.Year)

				if len(c.CVC) > 0 {
					body.Add("source[cvc]", c.CVC)
				}

				body.Add("source[name]", c.Name)

				if len(c.Address1) > 0 {
					body.Add("source[address_line1]", c.Address1)
				}

				if len(c.Address2) > 0 {
					body.Add("source[address_line2]", c.Address2)
				}
				if len(c.City) > 0 {
					body.Add("source[address_city]", c.City)
				}

				if len(c.State) > 0 {
					body.Add("source[address_state]", c.State)
				}
				if len(c.Zip) > 0 {
					body.Add("source[address_zip]", c.Zip)
				}
				if len(c.Country) > 0 {
					body.Add("source[address_country]", c.Country)
				}
			}
		}

		if len(params.Customer) > 0 {
			body.Add("customer", params.Customer)
		}

		if params.ApplicationFee > 0 {
			body.Add("application_fee", strconv.FormatInt(params.ApplicationFee, 10))
		}

		if params.Email != "" {
			body.Add("email", params.Email)
		}

		params.AppendTo(body)
	}

	o := &stripe.Order{}
	err := c.B.Call("POST", "/orders/"+id+"/pay", c.Key, body, commonParams, o)

	return o, err
}

// Get returns the details of an order
// For more details see https://stripe.com/docs/api#retrieve_order.
func Get(id string, params *stripe.OrderParams) (*stripe.Order, error) {
	return getC().Get(id, params)
}

func (c Client) Get(id string, params *stripe.OrderParams) (*stripe.Order, error) {
	var body *stripe.RequestValues
	var commonParams *stripe.Params

	if params != nil {
		body = &stripe.RequestValues{}
		commonParams = &params.Params
		params.AppendTo(body)
	}

	order := &stripe.Order{}
	err := c.B.Call("GET", "/orders/"+id, c.Key, body, commonParams, order)
	return order, err
}

// List returns a list of orders.
// For more details see https://stripe.com/docs/api#list_orders
func List(params *stripe.OrderListParams) *Iter {
	return getC().List(params)
}

func (c Client) List(params *stripe.OrderListParams) *Iter {
	var body *stripe.RequestValues
	var lp *stripe.ListParams
	var p *stripe.Params

	if params != nil {
		body = &stripe.RequestValues{}

		for _, id := range params.IDs {
			params.Filters.AddFilter("ids[]", "", id)
		}

		if params.Status != "" {
			params.Filters.AddFilter("status", "", string(params.Status))
		}

		params.AppendTo(body)
		lp = &params.ListParams
		p = params.ToParams()
	}

	return &Iter{stripe.GetIter(lp, body, func(b *stripe.RequestValues) ([]interface{}, stripe.ListMeta, error) {
		list := &stripe.OrderList{}
		err := c.B.Call("GET", "/orders", c.Key, b, p, list)

		ret := make([]interface{}, len(list.Values))
		for i, v := range list.Values {
			ret[i] = v
		}

		return ret, list.ListMeta, err
	})}
}

// Iter is an iterator for lists of Orders.
// The embedded Iter carries methods with it;
// see its documentation for details.
type Iter struct {
	*stripe.Iter
}

// Order returns the most recent Order
// visited by a call to Next.
func (i *Iter) Order() *stripe.Order {
	return i.Current().(*stripe.Order)
}

// Return returns all or part of an order.
// For more details see https://stripe.com/docs/api#return_order.
func Return(id string, params *stripe.OrderReturnParams) (*stripe.OrderReturn, error) {
	return getC().Return(id, params)
}

// Return returns all or part of an order.
// For more details see https://stripe.com/docs/api#return_order.
func (c Client) Return(id string, params *stripe.OrderReturnParams) (*stripe.OrderReturn, error) {
	var body *stripe.RequestValues
	var commonParams *stripe.Params

	if params != nil {
		body = &stripe.RequestValues{}

		if len(params.Items) > 0 {
			for _, item := range params.Items {
				if item.Description != "" {
					body.Add("items[][description]", item.Description)
				}
				body.Add("items[][type]", string(item.Type))
				if item.Amount > 0 {
					body.Add("items[][amount]", strconv.FormatInt(item.Amount, 10))
				}
				if item.Currency != "" {
					body.Add("items[][currency]", string(item.Currency))
				}
				if item.Parent != "" {
					body.Add("items[][parent]", item.Parent)
				}
				if item.Quantity != nil {
					body.Add("items[][quantity]", strconv.FormatInt(*item.Quantity, 10))
				}
			}
		}

		params.AppendTo(body)
	}

	ret := &stripe.OrderReturn{}
	err := c.B.Call("POST", fmt.Sprintf("/orders/%s/returns", id), c.Key, body, commonParams, ret)

	return ret, err
}

func getC() Client {
	return Client{stripe.GetBackend(stripe.APIBackend), stripe.Key}
}
<|endoftext|>


<commit_before>package sliceops

//import "math"

// Returns the minimum value in the slice and the index of
// the minimum value. If the input slice is empty, zero is returned
// as the minimum value and -1 is returned as the index.
// Use: val,ind := sliceops.Min(slice)
func Min(s []float64) (min float64, ind int) {
	if len(s) == 0 {
		return min, -1 // Ind is -1 to make clear it's not the zeroth index.
	}
	min = s[0]
	ind = 0
	for i, val := range s {
		if val < min {
			min = val
			ind = i
		}
	}
	return min, ind
}

// Returns the maximum value in the slice and the location of
// the maximum value. If the input slice is empty, zero is returned
// as the minimum value and -1 is returned as the index.
// Use: val,ind := sliceops.Max(slice)
func Max(s []float64) (max float64, ind int) {
	if len(s) == 0 {
		return max, -1 // Ind is -1 to make clear it's not the zeroth index.
	}
	max = s[0]
	ind = 0
	for i, val := range s {
		if val > max {
			max = val
			ind = i
		}
	}
	return max, ind
}

// Returns the sum of the elements of the slice
func Sum(s []float64) (sum float64) {
	for _, val := range s {
		sum += val
	}
	return
}

// Returns the product of the elements of the slice
// Returns 0 if the input has length zero
func Prod(s []float64) (prod float64) {
	prod = 1
	for _, val := range s {
		prod *= val
	}
	return prod
}

// Tests if all of the slices have equal length.
// Returns true if there are no input slices
func HasEqLen(slices ...[]float64) bool {
	if len(slices) == 0 {
		return true
	}
	l := len(slices[0])
	for i := 1; i < len(slices); i++ {
		if len(slices[i]) != l {
			return false
		}
	}
	return true
}

// Returns the element-wise sum of the last n slices
// and puts them in place into the first argument. If the
// receiver is nil, a new slice of floats is created.
// For computational efficiency, it is assumed that all of
// the variadic arguments have the same length. If this is
// in doubt, EqLengths can be called. If no slices are input,
// the receiver is unchanged.
func ElemSum(receiver []float64, slices ...[]float64) []float64 {
	if len(slices) == 0 {
		return receiver
	}
	if receiver == nil {
		receiver = make([]float64, len(slices[0]))
	}
	for i, val := range slices[0] {
		receiver[i] = val
	}
	for i := 1; i < len(slices); i++ {
		for j, val := range slices[i] {
			receiver[j] += val
		}
	}
	return receiver
}
<commit_msg>Added CumSum<commit_after>package sliceops

//import "math"

// Returns the minimum value in the slice and the index of
// the minimum value. If the input slice is empty, zero is returned
// as the minimum value and -1 is returned as the index.
// Use: val,ind := sliceops.Min(slice)
func Min(s []float64) (min float64, ind int) {
	if len(s) == 0 {
		return min, -1 // Ind is -1 to make clear it's not the zeroth index.
	}
	min = s[0]
	ind = 0
	for i, val := range s {
		if val < min {
			min = val
			ind = i
		}
	}
	return min, ind
}

// Returns the maximum value in the slice and the location of
// the maximum value. If the input slice is empty, zero is returned
// as the minimum value and -1 is returned as the index.
// Use: val,ind := sliceops.Max(slice)
func Max(s []float64) (max float64, ind int) {
	if len(s) == 0 {
		return max, -1 // Ind is -1 to make clear it's not the zeroth index.
	}
	max = s[0]
	ind = 0
	for i, val := range s {
		if val > max {
			max = val
			ind = i
		}
	}
	return max, ind
}

// Returns the sum of the elements of the slice
func Sum(s []float64) (sum float64) {
	for _, val := range s {
		sum += val
	}
	return
}

// Returns the product of the elements of the slice
// Returns 0 if the input has length zero
func Prod(s []float64) (prod float64) {
	prod = 1
	for _, val := range s {
		prod *= val
	}
	return prod
}

// Finds the cumulative sum of the first i elements in
// s and puts them in place into the ith element of the
// receiver. If the receiver is nil a new slice is created
func CumSum(s, receiver []float64) []float64 {
	if len(s) == 0 {
		return receiver[:0]
	}
	receiver[0] = s[0]
	for i := 1; i < len(s); i++ {
		receiver[i] += receiver[i-1] + s[i]
	}
	return receiver
}

// Tests if all of the slices have equal length.
// Returns true if there are no input slices
func HasEqLen(slices ...[]float64) bool {
	if len(slices) == 0 {
		return true
	}
	l := len(slices[0])
	for i := 1; i < len(slices); i++ {
		if len(slices[i]) != l {
			return false
		}
	}
	return true
}

// Returns the element-wise sum of the last n slices
// and puts them in place into the first argument. If the
// receiver is nil, a new slice of floats is created.
// For computational efficiency, it is assumed that all of
// the variadic arguments have the same length. If this is
// in doubt, EqLengths can be called. If no slices are input,
// the receiver is unchanged.
func ElemSum(receiver []float64, slices ...[]float64) []float64 {
	if len(slices) == 0 {
		return receiver
	}
	if receiver == nil {
		receiver = make([]float64, len(slices[0]))
	}
	for i, val := range slices[0] {
		receiver[i] = val
	}
	for i := 1; i < len(slices); i++ {
		for j, val := range slices[i] {
			receiver[j] += val
		}
	}
	return receiver
}
<|endoftext|>


<commit_before>package app

import (
	"yap/nlp/format/lattice"
	"yap/nlp/format/lex"
	"yap/nlp/format/raw"

	"yap/nlp/parser/ma"
	"yap/nlp/parser/xliter8"
	nlp "yap/nlp/types"
	// "yap/util"

	"fmt"
	"log"
	// "os"

	"github.com/gonuts/commander"
	"github.com/gonuts/flag"
)

var (
	prefixFile, lexiconFile string
	xliter8out, alwaysnnp   bool
	nnpnofeats              bool
	showoov                 bool
	outJSON                 bool
)

func HebMAConfigOut() {
	log.Println("Configuration")
	log.Printf("Heb Lexicon:\t\t%s", prefixFile)
	log.Printf("Heb Prefix:\t\t%s", lexiconFile)
	log.Printf("OOV Strategy:\t%v", "Const:NNP")
	log.Printf("xliter8 out:\t\t%v", xliter8out)
	log.Println()
	log.Printf("Raw Input:\t\t%s", inRawFile)
	log.Printf("Output:\t\t%s", outLatticeFile)
	log.Println()
}

func HebMA(cmd *commander.Command, args []string) error {
	REQUIRED_FLAGS := []string{"prefix", "lexicon", "raw", "out"}
	VerifyFlags(cmd, REQUIRED_FLAGS)
	HebMAConfigOut()
	if outFormat == "ud" {
		// override all skips in HEBLEX
		lex.SKIP_POLAR = false
		lex.SKIP_BINYAN = false
		lex.SKIP_ALL_TYPE = false
		lex.SKIP_TYPES = make(map[string]bool)
		lattice.IGNORE_LEMMA = false
	}
	maData := new(ma.BGULex)
	maData.MAType = outFormat
	log.Println("Reading Morphological Analyzer BGU Prefixes")
	maData.LoadPrefixes(prefixFile)
	log.Println("Reading Morphological Analyzer BGU Lexicon")
	maData.LoadLex(lexiconFile, nnpnofeats)
	log.Println()
	sents, err := raw.ReadFile(inRawFile, limit)
	if err != nil {
		panic(fmt.Sprintf("Failed reading raw file - %v", err))
	}
	log.Println("Running Hebrew Morphological Analysis")
	lattices := make([]nlp.LatticeSentence, len(sents))
	stats := new(ma.AnalyzeStats)
	stats.Init()
	maData.Stats = stats
	maData.AlwaysNNP = alwaysnnp
	maData.LogOOV = showoov
	prefix := log.Prefix()
	for i, sent := range sents {
		log.SetPrefix(fmt.Sprintf("%v graph# %v ", prefix, i))
		lattices[i], _ = maData.Analyze(sent.Tokens())
	}
	log.SetPrefix(prefix)
	log.Println("Analyzed", stats.TotalTokens, "occurences of", len(stats.UniqTokens), "unique tokens")
	log.Println("Encountered", stats.OOVTokens, "occurences of", len(stats.UniqOOVTokens), "unknown tokens")
	var hebrew xliter8.Interface
	if xliter8out {
		hebrew = &xliter8.Hebrew{}
	}
	output := lattice.Sentence2LatticeCorpus(lattices, hebrew)
	if outFormat == "ud" {
		if outJSON {
			lattice.WriteUDJSONFile(outLatticeFile, output)
		} else {
			lattice.WriteUDFile(outLatticeFile, output)
		}
	} else if outFormat == "spmrl" {
		lattice.WriteFile(outLatticeFile, output)
	} else {
		panic(fmt.Sprintf("Unknown lattice output format - %v", outFormat))
	}
	return nil
}

func HebMACmd() *commander.Command {
	cmd := &commander.Command{
		Run:       HebMA,
		UsageLine: "hebma <file options> [arguments]",
		Short:     "run lexicon-based morphological analyzer on raw input",
		Long: `
run lexicon-based morphological analyzer on raw input

	$ ./yap hebma -prefix <prefix file> -lexicon <lexicon file> -raw <raw file> -out <output file> [options]

`,
		Flag: *flag.NewFlagSet("ma", flag.ExitOnError),
	}
	cmd.Flag.StringVar(&prefixFile, "prefix", "", "Prefix file for morphological analyzer")
	cmd.Flag.StringVar(&lexiconFile, "lexicon", "", "Lexicon file for morphological analyzer")
	cmd.Flag.StringVar(&inRawFile, "raw", "", "Input raw (tokenized) file")
	cmd.Flag.StringVar(&outLatticeFile, "out", "", "Output lattice file")
	cmd.Flag.BoolVar(&xliter8out, "xliter8out", false, "Transliterate output lattice file")
	cmd.Flag.BoolVar(&alwaysnnp, "alwaysnnp", false, "Always add NNP to tokens and prefixed subtokens")
	cmd.Flag.BoolVar(&nnpnofeats, "addnnpnofeats", false, "Add NNP in lex but without features")
	cmd.Flag.IntVar(&limit, "limit", 0, "Limit input set")
	cmd.Flag.BoolVar(&showoov, "showoov", false, "Output OOV tokens")
	cmd.Flag.BoolVar(&lex.LOG_FAILURES, "showlexerror", false, "Log errors encountered when loading the lexicon")
	cmd.Flag.StringVar(&outFormat, "format", "spmrl", "Output lattice format [spmrl|ud]")
	cmd.Flag.BoolVar(&outJSON, "json", false, "Output using JSON")
	return cmd
}
<commit_msg>Let HEBLEX app read tokens from conllu file<commit_after>package app

import (
	"yap/nlp/format/conllu"
	"yap/nlp/format/lattice"
	"yap/nlp/format/lex"
	"yap/nlp/format/raw"

	"yap/nlp/parser/ma"
	"yap/nlp/parser/xliter8"
	nlp "yap/nlp/types"
	// "yap/util"

	"fmt"
	"log"
	// "os"

	"github.com/gonuts/commander"
	"github.com/gonuts/flag"
)

var (
	prefixFile, lexiconFile string
	xliter8out, alwaysnnp   bool
	nnpnofeats              bool
	showoov                 bool
	outJSON                 bool
)

func HebMAConfigOut() {
	log.Println("Configuration")
	log.Printf("Heb Lexicon:\t\t%s", prefixFile)
	log.Printf("Heb Prefix:\t\t%s", lexiconFile)
	log.Printf("OOV Strategy:\t%v", "Const:NNP")
	log.Printf("xliter8 out:\t\t%v", xliter8out)
	log.Println()
	if useConllU {
		log.Printf("CoNLL-U Input:\t%s", conlluFile)
	} else {
		log.Printf("Raw Input:\t\t%s", inRawFile)
	}
	log.Printf("Output:\t\t%s", outLatticeFile)
	log.Println()
}

func HebMA(cmd *commander.Command, args []string) error {
	useConllU = len(conlluFile) > 0
	var REQUIRED_FLAGS []string
	if useConllU {
		REQUIRED_FLAGS = []string{"prefix", "lexicon", "conllu", "out"}
	} else {
		REQUIRED_FLAGS = []string{"prefix", "lexicon", "raw", "out"}
	}
	VerifyFlags(cmd, REQUIRED_FLAGS)
	HebMAConfigOut()
	if outFormat == "ud" {
		// override all skips in HEBLEX
		lex.SKIP_POLAR = false
		lex.SKIP_BINYAN = false
		lex.SKIP_ALL_TYPE = false
		lex.SKIP_TYPES = make(map[string]bool)
		lattice.IGNORE_LEMMA = false
	}
	maData := new(ma.BGULex)
	maData.MAType = outFormat
	log.Println("Reading Morphological Analyzer BGU Prefixes")
	maData.LoadPrefixes(prefixFile)
	log.Println("Reading Morphological Analyzer BGU Lexicon")
	maData.LoadLex(lexiconFile, nnpnofeats)
	log.Println()
	var (
		sents []nlp.BasicSentence
		err   error
	)
	if useConllU {
		conllSents, _, err := conllu.ReadFile(conlluFile, limit)
		if err != nil {
			panic(fmt.Sprintf("Failed reading CoNLL-U file - %v", err))
		}
		sents = make([]nlp.BasicSentence, len(conllSents))
		for i, sent := range conllSents {
			newSent := make([]nlp.Token, len(sent.Tokens))
			for j, token := range sent.Tokens {
				newSent[j] = nlp.Token(token)
			}
			sents[i] = newSent
		}
	} else {
		sents, err = raw.ReadFile(inRawFile, limit)
		if err != nil {
			panic(fmt.Sprintf("Failed reading raw file - %v", err))
		}
	}
	log.Println("Running Hebrew Morphological Analysis")
	lattices := make([]nlp.LatticeSentence, len(sents))
	stats := new(ma.AnalyzeStats)
	stats.Init()
	maData.Stats = stats
	maData.AlwaysNNP = alwaysnnp
	maData.LogOOV = showoov
	prefix := log.Prefix()
	for i, sent := range sents {
		log.SetPrefix(fmt.Sprintf("%v graph# %v ", prefix, i))
		lattices[i], _ = maData.Analyze(sent.Tokens())
	}
	log.SetPrefix(prefix)
	log.Println("Analyzed", stats.TotalTokens, "occurences of", len(stats.UniqTokens), "unique tokens")
	log.Println("Encountered", stats.OOVTokens, "occurences of", len(stats.UniqOOVTokens), "unknown tokens")
	var hebrew xliter8.Interface
	if xliter8out {
		hebrew = &xliter8.Hebrew{}
	}
	output := lattice.Sentence2LatticeCorpus(lattices, hebrew)
	if outFormat == "ud" {
		if outJSON {
			lattice.WriteUDJSONFile(outLatticeFile, output)
		} else {
			lattice.WriteUDFile(outLatticeFile, output)
		}
	} else if outFormat == "spmrl" {
		lattice.WriteFile(outLatticeFile, output)
	} else {
		panic(fmt.Sprintf("Unknown lattice output format - %v", outFormat))
	}
	return nil
}

func HebMACmd() *commander.Command {
	cmd := &commander.Command{
		Run:       HebMA,
		UsageLine: "hebma <file options> [arguments]",
		Short:     "run lexicon-based morphological analyzer on raw input",
		Long: `
run lexicon-based morphological analyzer on raw input

	$ ./yap hebma -prefix <prefix file> -lexicon <lexicon file> -raw <raw file> -out <output file> [options]

`,
		Flag: *flag.NewFlagSet("ma", flag.ExitOnError),
	}
	cmd.Flag.StringVar(&prefixFile, "prefix", "", "Prefix file for morphological analyzer")
	cmd.Flag.StringVar(&lexiconFile, "lexicon", "", "Lexicon file for morphological analyzer")
	cmd.Flag.StringVar(&inRawFile, "raw", "", "Input raw (tokenized) file")
	cmd.Flag.StringVar(&conlluFile, "conllu", "", "CoNLL-U-format input file")
	cmd.Flag.StringVar(&outLatticeFile, "out", "", "Output lattice file")
	cmd.Flag.BoolVar(&xliter8out, "xliter8out", false, "Transliterate output lattice file")
	cmd.Flag.BoolVar(&alwaysnnp, "alwaysnnp", false, "Always add NNP to tokens and prefixed subtokens")
	cmd.Flag.BoolVar(&nnpnofeats, "addnnpnofeats", false, "Add NNP in lex but without features")
	cmd.Flag.IntVar(&limit, "limit", 0, "Limit input set")
	cmd.Flag.BoolVar(&showoov, "showoov", false, "Output OOV tokens")
	cmd.Flag.BoolVar(&lex.LOG_FAILURES, "showlexerror", false, "Log errors encountered when loading the lexicon")
	cmd.Flag.StringVar(&outFormat, "format", "spmrl", "Output lattice format [spmrl|ud]")
	cmd.Flag.BoolVar(&outJSON, "json", false, "Output using JSON")
	return cmd
}
<|endoftext|>


<commit_before>package parser

import (
	"io"
	"net"
	"net/mail"
	"regexp"
	"strconv"
	"text/scanner"
	"unicode"

	"github.com/yuuki1/gokc/log"
)

var SYMBOL_TABLES = map[string]int{
	"{": LB,
	"}": RB,

	"global_defs": GLOBALDEFS,
	"notification_email": NOTIFICATION_EMAIL,
	"notification_email_from": NOTIFICATION_EMAIL_FROM,
	"smtp_server": SMTP_SERVER,
	"smtp_connect_timeout": SMTP_CONNECT_TIMEOUT,
	"router_id": ROUTER_ID,
	"lvs_id": LVS_ID,

	"vrrp_sync_group": VRRP_SYNC_GROUP,
	"group": GROUP,

	"vrrp_instance": VRRP_INSTANCE,
	"interface": INTERFACE,
	"lvs_sync_daemon_interface": LVS_SYNC_DAEMON_INTERFACE,
	"virtual_router_id": VIRTUAL_ROUTER_ID,
	"nopreempt": NOPREEMPT,
	"priority": PRIORITY,
	"advert_int": ADVERT_INT,
	"virtual_ipaddress": VIRTUAL_IPADDRESS,
	"state": STATE,
	"MASTER": MASTER,
	"BACKUP": BACKUP,
	"garp_master_delay": GARP_MASTER_DELAY,
	"smtp_alert": SMTP_ALERT,
	"authentication": AUTHENTICATION,
	"auth_type": AUTH_TYPE,
	"auth_pass": AUTH_PASS,
	"PASS": PASS,
	"AH": AH,
	"label": LABEL,
	"dev": DEV,
	"brd": BRD,
	"track_interface": TRACK_INTERFACE,
	"track_script": TRACK_SCRIPT,
	"notify_master": NOTIFY_MASTER,
	"notify_backup": NOTIFY_BACKUP,
	"notify_fault": NOTIFY_FAULT,
	"notify_stop": NOTIFY_STOP,
	"notify": NOTIFY,

	"vrrp_script": VRRP_SCRIPT,
	"script": SCRIPT,
	"interval": INTERVAL,
	"fall": FALL,
	"rise": RISE,

	"virtual_server": VIRTUAL_SERVER,
	"delay_loop": DELAY_LOOP,
	"lb_algo": LB_ALGO,
	"lb_kind": LB_KIND,
	"lvs_sched": LVS_SCHED,
	"lvs_method": LVS_METHOD,
	"rr": RR,
	"wrr": WRR,
	"lc": LC,
	"wlc": WLC,
	"lblc": LBLC,
	"sh": SH,
	"dh": DH,
	"NAT": NAT,
	"TUN": TUN,
	"persistence_timeout": PERSISTENCE_TIMEOUT,
	"protocol": PROTOCOL,
	"TCP": TCP,
	"UDP": UDP,
	"sorry_server": SORRY_SERVER,
	"real_server": REAL_SERVER,
	"weight": WEIGHT,
	"HTTP_GET": HTTP_GET,
	"url": URL,
	"path": PATH,
	"digest": DIGEST,
	"status_code": STATUS_CODE,
	"connect_timeout": CONNECT_TIMEOUT,
	"nb_get_retry": NB_GET_RETRY,
	"delay_before_retry": DELAY_BEFORE_RETRY,
}

type Lexer struct {
	scanner.Scanner
	emitter chan int
	errors []LexError
}

type LexError struct {
	Message string
	Line int
	Column int
}

func NewLexer(src io.Reader) *Lexer {
	var lex Lexer
	lex.Init(src)
	lex.Mode &^= scanner.ScanInts | scanner.ScanFloats | scanner.ScanChars | scanner.ScanRawStrings | scanner.ScanComments | scanner.SkipComments
	lex.IsIdentRune = isIdentRune
	lex.emitter = make(chan int)
	return &lex
}

func isIdentRune(ch rune, i int) bool {
	return ch == '_' || ch == '.' || ch == '/' || ch == ':' || ch == '-' || ch == '+' || ch == '*' || ch == '@' || unicode.IsLetter(ch) || unicode.IsDigit(ch)
}

func (l *Lexer) scanNextToken() (int, string) {
	token := int(l.Scan())
	s := l.TokenText()

	for s == "!" || s == "#" {
		l.skipComments()

		token = int(l.Scan())
		s = l.TokenText()
	}

	log.Debugf("token text: %s\n", s)

	return token, s
}

func (l *Lexer) skipComments() {
	ch := l.Next()
	for ch != '\n' && ch >= 0 {
		ch = l.Next()
	}
}

func (l *Lexer) Lex(lval *yySymType) int {
	return <-l.emitter
}

func (l *Lexer) run() {
	for {
		token, s := l.scanNextToken()

		// True end
		if token == scanner.EOF {
			l.emitter <- token
			close(l.emitter)
			break
		}

		if token == scanner.Ident || token == scanner.String {
			token = STRING
		}

		if _, err := strconv.Atoi(s); err == nil {
			token = NUMBER
		}

		if net.ParseIP(s) != nil {
			token = IPADDR
		}

		if _, _, err := net.ParseCIDR(s); err == nil {
			token = IP_CIDR
		}

		if ok, _ := regexp.MatchString("[[:xdigit:]]{32}", s); ok {
			token = HEX32
		}

		if ok, _ := regexp.MatchString("/^([[:alnum:]./-_])*", s); ok {
			token = PATHSTR
		}

		if _, err := mail.ParseAddress(s); err == nil {
			token = EMAIL
		}

		if _, ok := SYMBOL_TABLES[s]; ok {
			token = SYMBOL_TABLES[s]
		}

		l.emitter <- token
	}
}

func (l *Lexer) Error(e string) {
	lexerr := LexError{Line: l.Line, Column: l.Column, Message: e}
	l.errors = append(l.errors, lexerr)
}

<commit_msg>Add 'include' scanner, but it may not work<commit_after>package parser

import (
	"path/filepath"
	"io"
	"net"
	"net/mail"
	"os"
	"regexp"
	"strconv"
	"text/scanner"
	"unicode"

	"github.com/yuuki1/gokc/log"
)

var SYMBOL_TABLES = map[string]int{
	"{": LB,
	"}": RB,

	"global_defs": GLOBALDEFS,
	"notification_email": NOTIFICATION_EMAIL,
	"notification_email_from": NOTIFICATION_EMAIL_FROM,
	"smtp_server": SMTP_SERVER,
	"smtp_connect_timeout": SMTP_CONNECT_TIMEOUT,
	"router_id": ROUTER_ID,
	"lvs_id": LVS_ID,

	"vrrp_sync_group": VRRP_SYNC_GROUP,
	"group": GROUP,

	"vrrp_instance": VRRP_INSTANCE,
	"interface": INTERFACE,
	"lvs_sync_daemon_interface": LVS_SYNC_DAEMON_INTERFACE,
	"virtual_router_id": VIRTUAL_ROUTER_ID,
	"nopreempt": NOPREEMPT,
	"priority": PRIORITY,
	"advert_int": ADVERT_INT,
	"virtual_ipaddress": VIRTUAL_IPADDRESS,
	"state": STATE,
	"MASTER": MASTER,
	"BACKUP": BACKUP,
	"garp_master_delay": GARP_MASTER_DELAY,
	"smtp_alert": SMTP_ALERT,
	"authentication": AUTHENTICATION,
	"auth_type": AUTH_TYPE,
	"auth_pass": AUTH_PASS,
	"PASS": PASS,
	"AH": AH,
	"label": LABEL,
	"dev": DEV,
	"brd": BRD,
	"track_interface": TRACK_INTERFACE,
	"track_script": TRACK_SCRIPT,
	"notify_master": NOTIFY_MASTER,
	"notify_backup": NOTIFY_BACKUP,
	"notify_fault": NOTIFY_FAULT,
	"notify_stop": NOTIFY_STOP,
	"notify": NOTIFY,

	"vrrp_script": VRRP_SCRIPT,
	"script": SCRIPT,
	"interval": INTERVAL,
	"fall": FALL,
	"rise": RISE,

	"virtual_server": VIRTUAL_SERVER,
	"delay_loop": DELAY_LOOP,
	"lb_algo": LB_ALGO,
	"lb_kind": LB_KIND,
	"lvs_sched": LVS_SCHED,
	"lvs_method": LVS_METHOD,
	"rr": RR,
	"wrr": WRR,
	"lc": LC,
	"wlc": WLC,
	"lblc": LBLC,
	"sh": SH,
	"dh": DH,
	"NAT": NAT,
	"TUN": TUN,
	"persistence_timeout": PERSISTENCE_TIMEOUT,
	"protocol": PROTOCOL,
	"TCP": TCP,
	"UDP": UDP,
	"sorry_server": SORRY_SERVER,
	"real_server": REAL_SERVER,
	"weight": WEIGHT,
	"HTTP_GET": HTTP_GET,
	"url": URL,
	"path": PATH,
	"digest": DIGEST,
	"status_code": STATUS_CODE,
	"connect_timeout": CONNECT_TIMEOUT,
	"nb_get_retry": NB_GET_RETRY,
	"delay_before_retry": DELAY_BEFORE_RETRY,
}

type Lexer struct {
	scanner.Scanner
	emitter chan int
	errors []LexError
}

type LexError struct {
	Message string
	Line int
	Column int
}

func NewLexer(src io.Reader) *Lexer {
	var lex Lexer
	lex.scannerInit(src)
	lex.emitter = make(chan int)
	return &lex
}

func isIdentRune(ch rune, i int) bool {
	return ch == '_' || ch == '.' || ch == '/' || ch == ':' || ch == '-' || ch == '+' || ch == '*' || ch == '@' || unicode.IsLetter(ch) || unicode.IsDigit(ch)
}

func (l *Lexer) scannerInit(src io.Reader) {
	l.Init(src)
	l.Mode &^= scanner.ScanInts | scanner.ScanFloats | scanner.ScanChars | scanner.ScanRawStrings | scanner.ScanComments | scanner.SkipComments
	l.IsIdentRune = isIdentRune
}

func (l *Lexer) scanNextToken() (int, string) {
	token := int(l.Scan())
	s := l.TokenText()

	for s == "!" || s == "#" {
		l.skipComments()

		token = int(l.Scan())
		s = l.TokenText()
	}

	log.Debugf("token text: %s\n", s)

	return token, s
}

func (l *Lexer) skipComments() {
	ch := l.Next()
	for ch != '\n' && ch >= 0 {
		ch = l.Next()
	}
}

func (l *Lexer) scanInclude(filename string) error {
	curDir, err := filepath.Abs(".")
	if err != nil {
		return err
	}

	paths, err := filepath.Glob(filename)
	if err != nil {
		return err
	}

	for _, path := range paths {
		baseDir := filepath.Base(path)
		os.Chdir(baseDir)
		defer os.Chdir(curDir)

		f, err := os.Open(path)
		if err != nil {
			return err
		}
		defer f.Close()

		l.scannerInit(f)
		l.run()
	}

	return nil
}

func (l *Lexer) Lex(lval *yySymType) int {
	return <-l.emitter
}

func (l *Lexer) run() {
	var startInclude bool

	for {
		token, s := l.scanNextToken()

		// True end
		// Ignore include file's EOF
		if token == scanner.EOF && startInclude == false {
			l.emitter <- token
			close(l.emitter)
			break
		}

		if s == "include" {
			startInclude = true
			token, s = l.scanNextToken()

			if err := l.scanInclude(s); err != nil {
				l.Error(err.Error())
			}

			token, s = l.scanNextToken()
			startInclude = false
		}

		if token == scanner.Ident || token == scanner.String {
			token = STRING
		}

		if _, err := strconv.Atoi(s); err == nil {
			token = NUMBER
		}

		if net.ParseIP(s) != nil {
			token = IPADDR
		}

		if _, _, err := net.ParseCIDR(s); err == nil {
			token = IP_CIDR
		}

		if ok, _ := regexp.MatchString("[[:xdigit:]]{32}", s); ok {
			token = HEX32
		}

		if ok, _ := regexp.MatchString("/^([[:alnum:]./-_])*", s); ok {
			token = PATHSTR
		}

		if _, err := mail.ParseAddress(s); err == nil {
			token = EMAIL
		}

		if _, ok := SYMBOL_TABLES[s]; ok {
			token = SYMBOL_TABLES[s]
		}

		l.emitter <- token
	}
}

func (l *Lexer) Error(e string) {
	lexerr := LexError{Line: l.Line, Column: l.Column, Message: e}
	l.errors = append(l.errors, lexerr)
}

<|endoftext|>


<commit_before>package frame

import (
	"image"
)

// IndexOf returns the chracter index under the
// point pt.
func (f *Frame) IndexOf(pt image.Point) (p int64) {
	pt = f.grid(pt)
	qt := f.r.Min
	bn := 0
	for ; bn < f.Nbox && qt.Y < pt.Y; bn++ {
		b := &f.Box[bn]
		qt = f.wrapMax(qt, b)
		if qt.Y >= pt.Y {
			break
		}
		qt = f.advance(qt, b)
		p += int64(b.Len())
	}

	for ; bn < f.Nbox && qt.X <= pt.X; bn++ {
		b := &f.Box[bn]
		qt = f.wrapMax(qt, b)
		if qt.Y > pt.Y {
			break
		}
		if qt.X+b.Width > pt.X {
			if b.Nrune < 0 {
				qt = f.advance(qt, b)
			} else {
				left := pt.X - qt.X
				p += int64(f.Face.Fits(b.Ptr, left))
				qt.X += left
			}
		} else {
			p += int64(b.Len())
			qt = f.advance(qt, b)
		}
	}
	return p
}
<commit_msg>frame: indexof: improve subjective accuracy<commit_after>package frame

import (
	"image"
)

// IndexOf returns the chracter index under the
// point pt.
func (f *Frame) IndexOf(pt image.Point) (p int64) {
	pt.X+=1
	return f.indexOf(pt)
}

func (f *Frame) indexOf(pt image.Point) (p int64) {
	pt = f.grid(pt)
	qt := f.r.Min
	bn := 0
	for ; bn < f.Nbox && qt.Y < pt.Y; bn++ {
		b := &f.Box[bn]
		qt = f.wrapMax(qt, b)
		if qt.Y >= pt.Y {
			break
		}
		qt = f.advance(qt, b)
		p += int64(b.Len())
	}

	for ; bn < f.Nbox && qt.X <= pt.X; bn++ {
		b := &f.Box[bn]
		qt = f.wrapMax(qt, b)
		if qt.Y > pt.Y {
			break
		}
		if qt.X+b.Width > pt.X {
			if b.Nrune < 0 {
				qt = f.advance(qt, b)
			} else {
				left := pt.X - qt.X
				p += int64(f.Face.Fits(b.Ptr, left))
				qt.X += left
			}
		} else {
			p += int64(b.Len())
			qt = f.advance(qt, b)
		}
	}
	return p
}
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    "github.com/danryan/hal"
)

var isAliveHandler = hear(`is ([A-Za-z0-9\-_\{\}\[\]\\]+) alive`, "is (name) alive", "Find out if a user is alive", func(res *hal.Response) error {
    name := res.Match[1]
    return res.Send(fmt.Sprintf("I can't find %s's heartbeat.. \nBut let's not jump to conclusions", name))
})<commit_msg>Ran isalive.go through goimports<commit_after>package main

import (
	"fmt"

	"github.com/danryan/hal"
)

var isAliveHandler = hear(`is ([A-Za-z0-9\-_\{\}\[\]\\]+) alive`, "is (name) alive", "Find out if a user is alive", func(res *hal.Response) error {
	name := res.Match[1]
	return res.Send(fmt.Sprintf("I can't find %s's heartbeat.. \nBut let's not jump to conclusions", name))
})
<|endoftext|>


<commit_before>// Copyright 2015 Thomas Burke <tburke@tb99.com>. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

// Package iso8211 implements ISO 8211 parsing.
// It is targeted to NOAA IHO S-57 format vector chart files.
//
// ISO 8211 is one of those baroque 1990's era binary file formats.
//   file: LeadRecord, DataRecord...
//   Record : Header, data
//   LeadRecord : Header, FieldType...
//   DataRecord : Header, Field...
//   FieldType : FieldHeader, SubField tags and formats
//   Field : SubFields
//
// References:
//   http://www.iho.int/iho_pubs/standard/S-57Ed3.1/31Main.pdf
//   http://sourceforge.net/projects/py-iso8211/
//   https://www.iso.org/obp/ui/#iso:std:iso-iec:8211:ed-2:v1:en
//   http://mcmcweb.er.usgs.gov/sdts/SDTS_standard_nov97/p3body.html
//   http://www.charts.noaa.gov/ENCs/ENCs.shtml
package iso8211

import (
	"bytes"
	"encoding/binary"
	"errors"
	"io"
	"reflect"
	"regexp"
	"strconv"
)

// RawHeader is a convenience for directly loading the on-disk
// binary Header format.
type RawHeader struct {
	RecordLength                    [5]byte
	InterchangeLevel                byte
	LeaderId                        byte
	InLineCode                       byte
	Version                          byte
	ApplicationIndicator            byte
	FieldControlLength             [2]byte
	BaseAddress                     [5]byte
	ExtendedCharacterSetIndicator [3]byte
	SizeOfFieldLength             byte
	SizeOfFieldPosition           byte
	Reserved                         byte
	SizeOfFieldTag                byte
}

// DirEntry describes each following Field
type DirEntry struct {
	Tag      []byte
	Length   int
	Position int
}

// Header holds the overall layout for a Record.
type Header struct {
	RecordLength                        uint64
	InterchangeLevel                    byte
	LeaderId                            byte
	InLineCode                           byte
	Version                              byte
	ApplicationIndicator                byte
	FieldControlLength                 uint64
	BaseAddress                         uint64
	ExtendedCharacterSetIndicator     []byte
	LengthSize, PositionSize, TagSize int8
	Entries                              []DirEntry
}

// LeadRecord is the first Record in a file. It has metadata for each
// Field in the file.
type LeadRecord struct {
	Header     Header
	FieldTypes map[string]FieldType
}

type Field struct {
	Tag       string
	Length    int
	Position  int
	FieldType FieldType
	SubFields []interface{}
}

// DataRecord contains data for a set of Fields and their SubFields.
type DataRecord struct {
	Header Header
	Lead   *LeadRecord
	Fields []Field
}

type RawFieldHeader struct {
	DataStructure     byte
	DataType          byte
	AuxiliaryControls [2]byte
	PrintableFt       byte
	PrintableUt       byte
	EscapeSeq         [3]byte
}

type SubFieldType struct {
	Kind reflect.Kind
	Size int
	Tag  []byte
}

type FieldType struct {
	Tag                string
	Length             int
	Position           int
	DataStructure     byte
	DataType          byte
	AuxiliaryControls []byte
	PrintableFt       byte
	PrintableUt       byte
	EscapeSeq         []byte
	Name               []byte
	ArrayDescriptor   []byte
	FormatControls    []byte
	SubFields          []SubFieldType
}

// Read loads a binary format RawHeader and its DirEntries into
// the Header model.
func (header *Header) Read(file io.Reader) error {
	var err error
	var ddr RawHeader
	ddrSize := uint64(binary.Size(ddr))
	// Read the header
	err = binary.Read(file, binary.LittleEndian, &ddr)
	if err != nil {
		return err
	}
	header.RecordLength, _ = strconv.ParseUint(string(ddr.RecordLength[:]), 10, 64)
	header.InterchangeLevel = ddr.InterchangeLevel
	header.LeaderId = ddr.LeaderId
	header.InLineCode = ddr.InLineCode
	header.Version = ddr.Version
	header.ApplicationIndicator = ddr.ApplicationIndicator
	header.FieldControlLength, _ = strconv.ParseUint(string(ddr.FieldControlLength[:]), 10, 64)
	header.BaseAddress, _ = strconv.ParseUint(string(ddr.BaseAddress[:]), 10, 64)
	header.ExtendedCharacterSetIndicator = ddr.ExtendedCharacterSetIndicator[:]
	header.LengthSize = int8(ddr.SizeOfFieldLength - '0')
	header.PositionSize = int8(ddr.SizeOfFieldPosition - '0')
	header.TagSize = int8(ddr.SizeOfFieldTag - '0')
	// Read the directory
	entries := (header.BaseAddress - 1 - ddrSize) / uint64(header.LengthSize+header.PositionSize+header.TagSize)
	header.Entries = make([]DirEntry, entries)
	dir := make([]byte, header.BaseAddress-ddrSize)
	file.Read(dir)
	buf := bytes.NewBuffer(dir)
	for idx := uint64(0); idx < entries; idx++ {
		header.Entries[idx].Tag = buf.Next(int(header.TagSize))
		header.Entries[idx].Length, _ = strconv.Atoi(string(buf.Next(int(header.LengthSize))[:]))
		header.Entries[idx].Position, _ = strconv.Atoi(string(buf.Next(int(header.PositionSize))[:]))
	}
	return err
}

// Read loads the LeadRecord Header and the FieldTypes
func (lead *LeadRecord) Read(file io.Reader) error {
	var err error
	err = lead.Header.Read(file)
	if err != nil {
		return err
	}
	if lead.Header.LeaderId != 'L' {
		return errors.New("Record is not a Lead record")
	}
	err = lead.ReadFields(file)
	return err
}

func (lead *LeadRecord) ReadFields(file io.Reader) error {
	var err error
	lead.FieldTypes = make(map[string]FieldType, len(lead.Header.Entries))
	for _, d := range lead.Header.Entries {
		field := FieldType{Tag: string(d.Tag), Length: d.Length, Position: d.Position}
		field.Read(file)
		lead.FieldTypes[field.Tag] = field
	}
	return err
}

func (field *Field) Read(file io.Reader) error {
	var err error
	data := make([]byte, field.Length)
	file.Read(data)
	if field.FieldType.Tag != "" {
		field.SubFields = field.FieldType.Decode(data[:field.Length-1])
	}
	return err
}

func (data *DataRecord) Read(file io.Reader) error {
	var err error
	err = data.Header.Read(file)
	if err != nil {
		return err
	}
	if data.Header.LeaderId != 'D' {
		return errors.New("Record is not a Data record")
	}
	err = data.ReadFields(file)
	return err
}

func (data *DataRecord) ReadFields(file io.Reader) error {
	var err error
	data.Fields = make([]Field, len(data.Header.Entries))
	for i, d := range data.Header.Entries {
		field := Field{Tag: string(d.Tag), Length: d.Length, Position: d.Position}
		if data.Lead != nil {
			field.FieldType = data.Lead.FieldTypes[field.Tag]
		}
		err = field.Read(file)
		data.Fields[i] = field
	}
	return err
}

func (dir *FieldType) Read(file io.Reader) error {
	var field RawFieldHeader
	err := binary.Read(file, binary.LittleEndian, &field)
	dir.DataStructure = field.DataStructure
	dir.DataType = field.DataType
	dir.AuxiliaryControls = field.AuxiliaryControls[:]
	dir.PrintableFt = field.PrintableFt
	dir.PrintableUt = field.PrintableUt
	dir.EscapeSeq = field.EscapeSeq[:]
	fdata := make([]byte, dir.Length-9)
	file.Read(fdata)
	desc := bytes.Split(fdata[:dir.Length-10], []byte{'\x1f'})
	dir.Name = desc[0]
	dir.ArrayDescriptor = desc[1]
	if len(desc) > 2 {
		dir.FormatControls = desc[2]
	}
	return err
}

/*
Format parses the ISO-8211 format controls and array descriptors.

Based on Section 7.2.2.1 of the IHO S-57 Publication.
http://www.iho.int/iho_pubs/standard/S-57Ed3.1/31Main.pdf

Array Descriptor and Format Controls. The array descriptor is a ! separated
list of tags describing the data field. If it begins with a * the tag list
is repeated. The format controls decribe the format of the data for each tag.

eg: Descriptor AGEN!FIDN!FIDS , Format (b12,b14,b12) is three binary encoded
integers. AGEN is an int16, FIDN an int32 and FIDS an int16. The 'b' indicates
binary int, '1' indicates unsigned, the second digit indicates the number of
bytes.
Decriptor *YCOO!XCOO, Format (2b24) is two binary encoded integers. Both are
int32s, the '2' after the 'b' indicates signed. The * in the descriptor
indicates that pair is repeated to fill the data field.
*/
func (dir *FieldType) Format() []SubFieldType {
	if dir.SubFields != nil {
		return dir.SubFields
	}
	var re = regexp.MustCompile(`(\d*)(\w+)\(*(\d*)\)*`)

	if len(dir.FormatControls) > 2 {
		Tags := bytes.Split(dir.ArrayDescriptor, []byte{'!'})
		Tagidx := 0
		types := make([]SubFieldType, len(Tags))
		for _, a := range re.FindAllSubmatch(dir.FormatControls, -1) {
			i := 1
			if len(a[1]) > 0 {
				i, _ = strconv.Atoi(string(a[1]))
			}
			var size int
			if len(a[3]) > 0 {
				size, _ = strconv.Atoi(string(a[3]))
			}
			for ; i > 0; i-- {
				switch a[2][0] {
				case 'A':
					types[Tagidx] = SubFieldType{reflect.String, size, Tags[Tagidx]}
				case 'I':
				case 'R':
					types[Tagidx] = SubFieldType{reflect.String, size, Tags[Tagidx]}
				case 'B':
					types[Tagidx] = SubFieldType{reflect.Array, size / 8, Tags[Tagidx]}
				case 'b':
					switch string(a[2][1:]) {
					case "11":
						types[Tagidx] = SubFieldType{reflect.Uint8, 1, Tags[Tagidx]}
					case "12":
						types[Tagidx] = SubFieldType{reflect.Uint16, 2, Tags[Tagidx]}
					case "14":
						types[Tagidx] = SubFieldType{reflect.Uint32, 4, Tags[Tagidx]}
					case "21":
						types[Tagidx] = SubFieldType{reflect.Int8, 1, Tags[Tagidx]}
					case "22":
						types[Tagidx] = SubFieldType{reflect.Int16, 2, Tags[Tagidx]}
					case "24":
						types[Tagidx] = SubFieldType{reflect.Int32, 4, Tags[Tagidx]}
					}
				}
				Tagidx++
			}
		}
		dir.SubFields = types
	}
	return dir.SubFields
}

// Decode uses the FieldType Format to convert the binary file format
// SubFields into an array of Go data types.
func (dir FieldType) Decode(buffer []byte) []interface{} {
	buf := bytes.NewBuffer(buffer)
	var values []interface{}
	for buf.Len() > 0 {
		for _, ftype := range dir.Format() {
			switch ftype.Kind {
			case reflect.Uint8:
				{
					var v uint8
					binary.Read(buf, binary.LittleEndian, &v)
					values = append(values, v)
				}
			case reflect.Uint16:
				{
					var v uint16
					binary.Read(buf, binary.LittleEndian, &v)
					values = append(values, v)
				}
			case reflect.Uint32:
				{
					var v uint32
					binary.Read(buf, binary.LittleEndian, &v)
					values = append(values, v)
				}
			case reflect.Int8:
				{
					var v int8
					binary.Read(buf, binary.LittleEndian, &v)
					values = append(values, v)
				}
			case reflect.Int16:
				{
					var v int16
					binary.Read(buf, binary.LittleEndian, &v)
					values = append(values, v)
				}
			case reflect.Int32:
				{
					var v int32
					binary.Read(buf, binary.LittleEndian, &v)
					values = append(values, v)
				}
			default:
				{
					if ftype.Size == 0 {
						i, _ := buf.ReadString('\x1f')
						if len(i) > 0 {
							values = append(values, i[:len(i)-1])
						} else {
							values = append(values, "")
						}
					} else {
						i := buf.Next(ftype.Size)
						values = append(values, string(i))
					}
				}
			}
		}
	}
	return values
}
<commit_msg>gofmt.<commit_after>// Copyright 2015 Thomas Burke <tburke@tb99.com>. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

// Package iso8211 implements ISO 8211 parsing.
// It is targeted to NOAA IHO S-57 format vector chart files.
//
// ISO 8211 is one of those baroque 1990's era binary file formats.
//   file: LeadRecord, DataRecord...
//   Record : Header, data
//   LeadRecord : Header, FieldType...
//   DataRecord : Header, Field...
//   FieldType : FieldHeader, SubField tags and formats
//   Field : SubFields
//
// References:
//   http://www.iho.int/iho_pubs/standard/S-57Ed3.1/31Main.pdf
//   http://sourceforge.net/projects/py-iso8211/
//   https://www.iso.org/obp/ui/#iso:std:iso-iec:8211:ed-2:v1:en
//   http://mcmcweb.er.usgs.gov/sdts/SDTS_standard_nov97/p3body.html
//   http://www.charts.noaa.gov/ENCs/ENCs.shtml
package iso8211

import (
	"bytes"
	"encoding/binary"
	"errors"
	"io"
	"reflect"
	"regexp"
	"strconv"
)

// RawHeader is a convenience for directly loading the on-disk
// binary Header format.
type RawHeader struct {
	RecordLength                  [5]byte
	InterchangeLevel              byte
	LeaderId                      byte
	InLineCode                    byte
	Version                       byte
	ApplicationIndicator          byte
	FieldControlLength            [2]byte
	BaseAddress                   [5]byte
	ExtendedCharacterSetIndicator [3]byte
	SizeOfFieldLength             byte
	SizeOfFieldPosition           byte
	Reserved                      byte
	SizeOfFieldTag                byte
}

// DirEntry describes each following Field
type DirEntry struct {
	Tag      []byte
	Length   int
	Position int
}

// Header holds the overall layout for a Record.
type Header struct {
	RecordLength                      uint64
	InterchangeLevel                  byte
	LeaderId                          byte
	InLineCode                        byte
	Version                           byte
	ApplicationIndicator              byte
	FieldControlLength                uint64
	BaseAddress                       uint64
	ExtendedCharacterSetIndicator     []byte
	LengthSize, PositionSize, TagSize int8
	Entries                           []DirEntry
}

// LeadRecord is the first Record in a file. It has metadata for each
// Field in the file.
type LeadRecord struct {
	Header     Header
	FieldTypes map[string]FieldType
}

type Field struct {
	Tag       string
	Length    int
	Position  int
	FieldType FieldType
	SubFields []interface{}
}

// DataRecord contains data for a set of Fields and their SubFields.
type DataRecord struct {
	Header Header
	Lead   *LeadRecord
	Fields []Field
}

type RawFieldHeader struct {
	DataStructure     byte
	DataType          byte
	AuxiliaryControls [2]byte
	PrintableFt       byte
	PrintableUt       byte
	EscapeSeq         [3]byte
}

type SubFieldType struct {
	Kind reflect.Kind
	Size int
	Tag  []byte
}

type FieldType struct {
	Tag               string
	Length            int
	Position          int
	DataStructure     byte
	DataType          byte
	AuxiliaryControls []byte
	PrintableFt       byte
	PrintableUt       byte
	EscapeSeq         []byte
	Name              []byte
	ArrayDescriptor   []byte
	FormatControls    []byte
	SubFields         []SubFieldType
}

// Read loads a binary format RawHeader and its DirEntries into
// the Header model.
func (header *Header) Read(file io.Reader) error {
	var err error
	var ddr RawHeader
	ddrSize := uint64(binary.Size(ddr))
	// Read the header
	err = binary.Read(file, binary.LittleEndian, &ddr)
	if err != nil {
		return err
	}
	header.RecordLength, _ = strconv.ParseUint(string(ddr.RecordLength[:]), 10, 64)
	header.InterchangeLevel = ddr.InterchangeLevel
	header.LeaderId = ddr.LeaderId
	header.InLineCode = ddr.InLineCode
	header.Version = ddr.Version
	header.ApplicationIndicator = ddr.ApplicationIndicator
	header.FieldControlLength, _ = strconv.ParseUint(string(ddr.FieldControlLength[:]), 10, 64)
	header.BaseAddress, _ = strconv.ParseUint(string(ddr.BaseAddress[:]), 10, 64)
	header.ExtendedCharacterSetIndicator = ddr.ExtendedCharacterSetIndicator[:]
	header.LengthSize = int8(ddr.SizeOfFieldLength - '0')
	header.PositionSize = int8(ddr.SizeOfFieldPosition - '0')
	header.TagSize = int8(ddr.SizeOfFieldTag - '0')
	// Read the directory
	entries := (header.BaseAddress - 1 - ddrSize) / uint64(header.LengthSize+header.PositionSize+header.TagSize)
	header.Entries = make([]DirEntry, entries)
	dir := make([]byte, header.BaseAddress-ddrSize)
	file.Read(dir)
	buf := bytes.NewBuffer(dir)
	for idx := uint64(0); idx < entries; idx++ {
		header.Entries[idx].Tag = buf.Next(int(header.TagSize))
		header.Entries[idx].Length, _ = strconv.Atoi(string(buf.Next(int(header.LengthSize))[:]))
		header.Entries[idx].Position, _ = strconv.Atoi(string(buf.Next(int(header.PositionSize))[:]))
	}
	return err
}

// Read loads the LeadRecord Header and the FieldTypes
func (lead *LeadRecord) Read(file io.Reader) error {
	var err error
	err = lead.Header.Read(file)
	if err != nil {
		return err
	}
	if lead.Header.LeaderId != 'L' {
		return errors.New("Record is not a Lead record")
	}
	err = lead.ReadFields(file)
	return err
}

func (lead *LeadRecord) ReadFields(file io.Reader) error {
	var err error
	lead.FieldTypes = make(map[string]FieldType, len(lead.Header.Entries))
	for _, d := range lead.Header.Entries {
		field := FieldType{Tag: string(d.Tag), Length: d.Length, Position: d.Position}
		field.Read(file)
		lead.FieldTypes[field.Tag] = field
	}
	return err
}

func (field *Field) Read(file io.Reader) error {
	var err error
	data := make([]byte, field.Length)
	file.Read(data)
	if field.FieldType.Tag != "" {
		field.SubFields = field.FieldType.Decode(data[:field.Length-1])
	}
	return err
}

func (data *DataRecord) Read(file io.Reader) error {
	var err error
	err = data.Header.Read(file)
	if err != nil {
		return err
	}
	if data.Header.LeaderId != 'D' {
		return errors.New("Record is not a Data record")
	}
	err = data.ReadFields(file)
	return err
}

func (data *DataRecord) ReadFields(file io.Reader) error {
	var err error
	data.Fields = make([]Field, len(data.Header.Entries))
	for i, d := range data.Header.Entries {
		field := Field{Tag: string(d.Tag), Length: d.Length, Position: d.Position}
		if data.Lead != nil {
			field.FieldType = data.Lead.FieldTypes[field.Tag]
		}
		err = field.Read(file)
		data.Fields[i] = field
	}
	return err
}

func (dir *FieldType) Read(file io.Reader) error {
	var field RawFieldHeader
	err := binary.Read(file, binary.LittleEndian, &field)
	dir.DataStructure = field.DataStructure
	dir.DataType = field.DataType
	dir.AuxiliaryControls = field.AuxiliaryControls[:]
	dir.PrintableFt = field.PrintableFt
	dir.PrintableUt = field.PrintableUt
	dir.EscapeSeq = field.EscapeSeq[:]
	fdata := make([]byte, dir.Length-9)
	file.Read(fdata)
	desc := bytes.Split(fdata[:dir.Length-10], []byte{'\x1f'})
	dir.Name = desc[0]
	dir.ArrayDescriptor = desc[1]
	if len(desc) > 2 {
		dir.FormatControls = desc[2]
	}
	return err
}

/*
Format parses the ISO-8211 format controls and array descriptors.

Based on Section 7.2.2.1 of the IHO S-57 Publication.
http://www.iho.int/iho_pubs/standard/S-57Ed3.1/31Main.pdf

Array Descriptor and Format Controls. The array descriptor is a ! separated
list of tags describing the data field. If it begins with a * the tag list
is repeated. The format controls decribe the format of the data for each tag.

eg: Descriptor AGEN!FIDN!FIDS , Format (b12,b14,b12) is three binary encoded
integers. AGEN is an int16, FIDN an int32 and FIDS an int16. The 'b' indicates
binary int, '1' indicates unsigned, the second digit indicates the number of
bytes.
Decriptor *YCOO!XCOO, Format (2b24) is two binary encoded integers. Both are
int32s, the '2' after the 'b' indicates signed. The * in the descriptor
indicates that pair is repeated to fill the data field.
*/
func (dir *FieldType) Format() []SubFieldType {
	if dir.SubFields != nil {
		return dir.SubFields
	}
	var re = regexp.MustCompile(`(\d*)(\w+)\(*(\d*)\)*`)

	if len(dir.FormatControls) > 2 {
		Tags := bytes.Split(dir.ArrayDescriptor, []byte{'!'})
		Tagidx := 0
		types := make([]SubFieldType, len(Tags))
		for _, a := range re.FindAllSubmatch(dir.FormatControls, -1) {
			i := 1
			if len(a[1]) > 0 {
				i, _ = strconv.Atoi(string(a[1]))
			}
			var size int
			if len(a[3]) > 0 {
				size, _ = strconv.Atoi(string(a[3]))
			}
			for ; i > 0; i-- {
				switch a[2][0] {
				case 'A':
					types[Tagidx] = SubFieldType{reflect.String, size, Tags[Tagidx]}
				case 'I':
				case 'R':
					types[Tagidx] = SubFieldType{reflect.String, size, Tags[Tagidx]}
				case 'B':
					types[Tagidx] = SubFieldType{reflect.Array, size / 8, Tags[Tagidx]}
				case 'b':
					switch string(a[2][1:]) {
					case "11":
						types[Tagidx] = SubFieldType{reflect.Uint8, 1, Tags[Tagidx]}
					case "12":
						types[Tagidx] = SubFieldType{reflect.Uint16, 2, Tags[Tagidx]}
					case "14":
						types[Tagidx] = SubFieldType{reflect.Uint32, 4, Tags[Tagidx]}
					case "21":
						types[Tagidx] = SubFieldType{reflect.Int8, 1, Tags[Tagidx]}
					case "22":
						types[Tagidx] = SubFieldType{reflect.Int16, 2, Tags[Tagidx]}
					case "24":
						types[Tagidx] = SubFieldType{reflect.Int32, 4, Tags[Tagidx]}
					}
				}
				Tagidx++
			}
		}
		dir.SubFields = types
	}
	return dir.SubFields
}

// Decode uses the FieldType Format to convert the binary file format
// SubFields into an array of Go data types.
func (dir FieldType) Decode(buffer []byte) []interface{} {
	buf := bytes.NewBuffer(buffer)
	var values []interface{}
	for buf.Len() > 0 {
		for _, ftype := range dir.Format() {
			switch ftype.Kind {
			case reflect.Uint8:
				{
					var v uint8
					binary.Read(buf, binary.LittleEndian, &v)
					values = append(values, v)
				}
			case reflect.Uint16:
				{
					var v uint16
					binary.Read(buf, binary.LittleEndian, &v)
					values = append(values, v)
				}
			case reflect.Uint32:
				{
					var v uint32
					binary.Read(buf, binary.LittleEndian, &v)
					values = append(values, v)
				}
			case reflect.Int8:
				{
					var v int8
					binary.Read(buf, binary.LittleEndian, &v)
					values = append(values, v)
				}
			case reflect.Int16:
				{
					var v int16
					binary.Read(buf, binary.LittleEndian, &v)
					values = append(values, v)
				}
			case reflect.Int32:
				{
					var v int32
					binary.Read(buf, binary.LittleEndian, &v)
					values = append(values, v)
				}
			default:
				{
					if ftype.Size == 0 {
						i, _ := buf.ReadString('\x1f')
						if len(i) > 0 {
							values = append(values, i[:len(i)-1])
						} else {
							values = append(values, "")
						}
					} else {
						i := buf.Next(ftype.Size)
						values = append(values, string(i))
					}
				}
			}
		}
	}
	return values
}
<|endoftext|>


<commit_before>package main

import (

	"fmt"
	"github.com/cloudfoundry/cli/cf/terminal"
	"github.com/cloudfoundry/cli/plugin"
)

/*
*	This is the struct implementing the interface defined by the core CLI. It can
*	be found at  "github.com/cloudfoundry/cli/plugin/plugin.go"
*
 */
type JanitorPlugin struct{
	ui terminal.UI
}

/*
*	This function must be implemented by any plugin because it is part of the
*	plugin interface defined by the core CLI.
*
*	Run(....) is the entry point when the core CLI is invoking a command defined
*	by a plugin. The first parameter, plugin.CliConnection, is a struct that can
*	be used to invoke cli commands. The second paramter, args, is a slice of
*	strings. args[0] will be the name of the command, and will be followed by
*	any additional arguments a cli user typed in.
*
*	Any error handling should be handled with the plugin itself (this means printing
*	user facing errors). The CLI will exit 0 if the plugin exits 0 and will exit
*	1 should the plugin exits nonzero.
 */
func (c *JanitorPlugin) Run(cliConnection plugin.CliConnection, args []string) {
	// Ensure that we called the command basic-plugin-command
	if args[0] == "janitor" {
		fmt.Println("Running the janitor")
	}
	version, err := cliConnection.ApiVersion()
	if err != nil {
		c.ui.Failed(err.Error())
	}

	fmt.Println("CLI Version : " + version)
}

/*
*	This function must be implemented as part of the	plugin interface
*	defined by the core CLI.
*
*	GetMetadata() returns a PluginMetadata struct. The first field, Name,
*	determines the name of the plugin which should generally be without spaces.
*	If there are spaces in the name a user will need to properly quote the name
*	during uninstall otherwise the name will be treated as seperate arguments.
*	The second value is a slice of Command structs. Our slice only contains one
*	Command Struct, but could contain any number of them. The first field Name
*	defines the command `cf basic-plugin-command` once installed into the CLI. The
*	second field, HelpText, is used by the core CLI to display help information
*	to the user in the core commands `cf help`, `cf`, or `cf -h`.
 */
func (c *JanitorPlugin) GetMetadata() plugin.PluginMetadata {
	return plugin.PluginMetadata{
		Name: "Janitor",
		Version: plugin.VersionType{
			Major: 1,
			Minor: 0,
			Build: 0,
		},
		MinCliVersion: plugin.VersionType{
			Major: 6,
			Minor: 7,
			Build: 0,
		},
		Commands: []plugin.Command{
			plugin.Command{
				Name:     "janitor",
				HelpText: "Janitor command's help text",

				// UsageDetails is optional
				// It is used to show help of usage of each command
				UsageDetails: plugin.Usage{
					Usage: "janitor\n   cf janitor",
				},
			},
		},
	}
}

/*
* Unlike most Go programs, the `Main()` function will not be used to run all of the
* commands provided in your plugin. Main will be used to initialize the plugin
* process, as well as any dependencies you might require for your
* plugin.
 */
func main() {
	// Any initialization for your plugin can be handled here
	//
	// Note: to run the plugin.Start method, we pass in a pointer to the struct
	// implementing the interface defined at "github.com/cloudfoundry/cli/plugin/plugin.go"
	//
	// Note: The plugin's main() method is invoked at install time to collect
	// metadata. The plugin will exit 0 and the Run([]string) method will not be
	// invoked.
	plugin.Start(new(JanitorPlugin))
	// Plugin code should be written in the Run([]string) method,
	// ensuring the plugin environment is bootstrapped.
}
<commit_msg>Begin creating filter function<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"strings"
	"github.com/cloudfoundry/cli/cf/api/resources"
	"github.com/cloudfoundry/cli/cf/terminal"
	"github.com/cloudfoundry/cli/plugin"

)

type JanitorPlugin struct{
	cliConnection plugin.CliConnection
	ui terminal.UI
}

/*
*	This function must be implemented by any plugin because it is part of the
*	plugin interface defined by the core CLI.
*
*	Run(....) is the entry point when the core CLI is invoking a command defined
*	by a plugin. The first parameter, plugin.CliConnection, is a struct that can
*	be used to invoke cli commands. The second parameter, args, is a slice of
*	strings. args[0] will be the name of the command, and will be followed by
*	any additional arguments a cli user typed in.
*
*	Any error handling should be handled with the plugin itself (this means printing
*	user facing errors). The CLI will exit 0 if the plugin exits 0 and will exit
*	1 should the plugin exits nonzero.
 */
func (c *JanitorPlugin) Run(cliConnection plugin.CliConnection, args []string) {
	c.cliConnection = cliConnection

	if args[0] == "janitor" {
		fmt.Println("Running the janitor")
	}
	fmt.Println(len(args))
	if len(args) < 2 {
		cliConnection.CliCommand(args[0], "-h")
	}

	space, err := cliConnection.GetCurrentSpace()
	if err != nil {
		c.ui.Failed(err.Error())
	}

	app := c.filterApps(space.Guid)
	fmt.Println(app)
}

/*
*	This function must be implemented as part of the	plugin interface
*	defined by the core CLI.
*
*	GetMetadata() returns a PluginMetadata struct. The first field, Name,
*	determines the name of the plugin which should generally be without spaces.
*	If there are spaces in the name a user will need to properly quote the name
*	during uninstall otherwise the name will be treated as seperate arguments.
*	The second value is a slice of Command structs. Our slice only contains one
*	Command Struct, but could contain any number of them. The first field Name
*	defines the command `cf basic-plugin-command` once installed into the CLI. The
*	second field, HelpText, is used by the core CLI to display help information
*	to the user in the core commands `cf help`, `cf`, or `cf -h`.
 */
func (c *JanitorPlugin) GetMetadata() plugin.PluginMetadata {
	return plugin.PluginMetadata{
		Name: "Janitor",
		Version: plugin.VersionType{
			Major: 1,
			Minor: 0,
			Build: 0,
		},
		MinCliVersion: plugin.VersionType{
			Major: 6,
			Minor: 7,
			Build: 0,
		},
		Commands: []plugin.Command{
			plugin.Command{
				Name:     "janitor",
				HelpText: "Janitor command's help text",

				// UsageDetails is optional
				// It is used to show help of usage of each command
				UsageDetails: plugin.Usage{
					Usage: "janitor",
				},
			},
		},
	}
}

func (c* JanitorPlugin) filterApps(spaceGuid string) string {

	appCmd := []string{"curl", "/v2/spaces/" + spaceGuid + "/apps"}
	appsJson, err := c.cliConnection.CliCommandWithoutTerminalOutput(appCmd...)

	if err != nil {
		c.ui.Failed(err.Error())
	}

	res := &resources.PaginatedApplicationResources{}
	json.Unmarshal([]byte(strings.Join(appsJson,"")), &res)

	return *res.Resources[0].Entity.Name
}

/*
* Unlike most Go programs, the `Main()` function will not be used to run all of the
* commands provided in your plugin. Main will be used to initialize the plugin
* process, as well as any dependencies you might require for your
* plugin.
 */
func main() {
	// Any initialization for your plugin can be handled here
	//
	// Note: to run the plugin.Start method, we pass in a pointer to the struct
	// implementing the interface defined at "github.com/cloudfoundry/cli/plugin/plugin.go"
	//
	// Note: The plugin's main() method is invoked at install time to collect
	// metadata. The plugin will exit 0 and the Run([]string) method will not be
	// invoked.
	plugin.Start(new(JanitorPlugin))
	// Plugin code should be written in the Run([]string) method,
	// ensuring the plugin environment is bootstrapped.
}
<|endoftext|>


<commit_before>package job

import (
	"bytes"
	"encoding/gob"
	"fmt"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/ajvb/kala/utils/iso8601"
	"github.com/ajvb/kala/utils/logging"

	"github.com/mattn/go-shellwords"
	"github.com/nu7hatch/gouuid"
)

var (
	log = logging.GetLogger("kala.job")

	shParser = shellwords.NewParser()
)

func init() {
	shParser.ParseEnv = true
	shParser.ParseBacktick = true
}

type Job struct {
	Name string `json:"name"`
	Id   string `json:"id"`

	// Command to run
	// e.g. "bash /path/to/my/script.sh"
	Command string `json:"command"`

	// Email of the owner of this job
	// e.g. "admin@example.com"
	Owner string `json:"owner"`

	// Is this job disabled?
	Disabled bool `json:"disabled"`

	// Jobs that are dependent upon this one will be run after this job runs.
	DependentJobs []string `json:"dependent_jobs"`

	// List of ids of jobs that this job is dependent upon.
	ParentJobs []string `json:"parent_jobs"`

	// ISO 8601 String
	// e.g. "R/2014-03-08T20:00:00.000Z/PT2H"
	Schedule     string `json:"schedule"`
	scheduleTime time.Time
	// ISO 8601 Duration struct, used for scheduling
	// job after each run.
	delayDuration *iso8601.Duration

	// Number of times to schedule this job after the
	// first run.
	timesToRepeat int64

	// Number of times to retry on failed attempt for each run.
	Retries uint `json:"retries"`

	// Duration in which it is safe to retry the Job.
	Epsilon         string `json:"epsilon"`
	epsilonDuration *iso8601.Duration

	jobTimer  *time.Timer
	NextRunAt time.Time `json:"next_run_at"`

	// Meta data about successful and failed runs.
	Metadata Metadata `json:"metadata"`

	// Collection of Job Stats
	Stats []*JobStat `json:"stats"`

	lock sync.RWMutex
}

type Metadata struct {
	SuccessCount     uint      `json:"success_count"`
	LastSuccess      time.Time `json:"last_success"`
	ErrorCount       uint      `json:"error_count"`
	LastError        time.Time `json:"last_error"`
	LastAttemptedRun time.Time `json:"last_attempted_run"`
}

// Bytes returns the byte representation of the Job.
func (j Job) Bytes() ([]byte, error) {
	buff := new(bytes.Buffer)
	enc := gob.NewEncoder(buff)
	err := enc.Encode(j)
	if err != nil {
		return nil, err
	}
	return buff.Bytes(), nil
}

// NewFromBytes returns a Job instance from a byte representation.
func NewFromBytes(b []byte) (*Job, error) {
	j := &Job{}

	buf := bytes.NewBuffer(b)
	err := gob.NewDecoder(buf).Decode(j)
	if err != nil {
		return nil, err
	}

	return j, nil
}

// Init fills in the protected fields and parses the iso8601 notation.
// It also adds the job to the Cache
func (j *Job) Init(cache JobCache) error {
	j.lock.Lock()
	defer j.lock.Unlock()

	u4, err := uuid.NewV4()
	if err != nil {
		log.Error("Error occured when generating uuid: %s", err)
		return err
	}
	j.Id = u4.String()

	// Add Job to the cache.
	err = cache.Set(j)
	if err != nil {
		return err
	}

	if len(j.ParentJobs) != 0 {
		// Add new job to parent jobs
		for _, p := range j.ParentJobs {
			parentJob, err := cache.Get(p)
			if err != nil {
				return err
			}
			parentJob.DependentJobs = append(parentJob.DependentJobs, j.Id)
		}

		return nil
	}

	// TODO: Delete from cache after running.
	if j.Schedule == "" {
		// If schedule is empty, its a one-off job.
		go j.Run(cache)
		return nil
	}

	j.lock.Unlock()
	err = j.InitDelayDuration(true)
	j.lock.Lock()
	if err != nil {
		cache.Delete(j.Id)
		return err
	}

	j.lock.Unlock()
	j.StartWaiting(cache)
	j.lock.Lock()

	return nil
}

// InitDelayDuration is used to parsed the iso8601 Schedule notation into its relevent fields in the Job struct.
// If checkTime is true, then it will return an error if the Scheduled time has passed.
func (j *Job) InitDelayDuration(checkTime bool) error {
	j.lock.Lock()
	defer j.lock.Unlock()

	var err error
	splitTime := strings.Split(j.Schedule, "/")
	if len(splitTime) != 3 {
		return fmt.Errorf(
			"Schedule not formatted correctly. Should look like: R/2014-03-08T20:00:00Z/PT2H",
		)
	}

	// Handle Repeat Amount
	if splitTime[0] == "R" {
		// Repeat forever
		j.timesToRepeat = -1
	} else {
		j.timesToRepeat, err = strconv.ParseInt(strings.Split(splitTime[0], "R")[1], 10, 0)
		if err != nil {
			log.Error("Error converting timesToRepeat to an int: %s", err)
			return err
		}
	}
	log.Debug("timesToRepeat: %d", j.timesToRepeat)

	j.scheduleTime, err = time.Parse(time.RFC3339, splitTime[1])
	if err != nil {
		log.Error("Error converting scheduleTime to a time.Time: %s", err)
		return err
	}
	if checkTime {
		if (time.Duration(j.scheduleTime.UnixNano() - time.Now().UnixNano())) < 0 {
			return fmt.Errorf("Schedule time has passed on Job with id of %s", j.Id)
		}
	}
	log.Debug("Schedule Time: %s", j.scheduleTime)

	j.delayDuration, err = iso8601.FromString(splitTime[2])
	if err != nil {
		log.Error("Error converting delayDuration to a iso8601.Duration: %s", err)
		return err
	}
	log.Debug("Delay Duration: %s", j.delayDuration.ToDuration())

	if j.Epsilon != "" {
		j.epsilonDuration, err = iso8601.FromString(j.Epsilon)
		if err != nil {
			log.Error("Error converting j.Epsilon to iso8601.Duration: %s", err)
			return err
		}
	}

	return nil
}

// StartWaiting begins a timer for when it should execute the Jobs .Run() method.
func (j *Job) StartWaiting(cache JobCache) {
	waitDuration := j.GetWaitDuration()

	j.lock.Lock()
	defer j.lock.Unlock()

	log.Info("Job Scheduled to run in: %s", waitDuration)

	j.NextRunAt = time.Now().Add(waitDuration)

	jobRun := func() { j.Run(cache) }
	j.jobTimer = time.AfterFunc(waitDuration, jobRun)
}

func (j *Job) GetWaitDuration() time.Duration {
	j.lock.RLock()
	defer j.lock.RUnlock()

	waitDuration := time.Duration(j.scheduleTime.UnixNano() - time.Now().UnixNano())

	if waitDuration < 0 {
		// Needs to be recalculated each time because of Months.
		if j.Metadata.LastAttemptedRun.IsZero() {
			waitDuration = j.delayDuration.ToDuration()
		} else {
			lastRun := j.Metadata.LastAttemptedRun
			lastRun = lastRun.Add(j.delayDuration.ToDuration())
			waitDuration = lastRun.Sub(time.Now())
		}
	}

	return waitDuration
}

// Disable stops the job from running by stopping its jobTimer. It also sets Job.Disabled to true,
// which is reflected in the UI.
func (j *Job) Disable() {
	j.lock.Lock()
	defer j.lock.Unlock()

	if j.jobTimer != nil {
		j.jobTimer.Stop()
	}
	j.Disabled = true
}

// DeleteFromParentJobs goes through and deletes the current job from any parent jobs.
func (j *Job) DeleteFromParentJobs(cache JobCache) error {
	j.lock.Lock()
	defer j.lock.Unlock()

	for _, p := range j.ParentJobs {
		parentJob, err := cache.Get(p)

		if err != nil {
			return err
		}

		parentJob.lock.Lock()

		ndx := 0
		for i, id := range parentJob.DependentJobs {
			if id == j.Id {
				ndx = i
				break
			}
		}
		parentJob.DependentJobs = append(
			parentJob.DependentJobs[:ndx], parentJob.DependentJobs[ndx+1:]...,
		)

		parentJob.lock.Unlock()
	}

	return nil
}

// DeleteFromDependentJobs
func (j *Job) DeleteFromDependentJobs(cache JobCache) error {
	j.lock.RLock()
	defer j.lock.RUnlock()

	for _, id := range j.DependentJobs {
		childJob, err := cache.Get(id)
		if err != nil {
			return err
		}

		// If there are no other parent jobs, delete this job.
		if len(childJob.ParentJobs) == 1 {
			cache.Delete(childJob.Id)
			continue
		}

		childJob.lock.Lock()

		ndx := 0
		for i, id := range childJob.ParentJobs {
			if id == j.Id {
				ndx = i
				break
			}
		}
		childJob.ParentJobs = append(
			childJob.ParentJobs[:ndx], childJob.ParentJobs[ndx+1:]...,
		)

		childJob.lock.Unlock()

	}

	return nil
}

func (j *Job) Run(cache JobCache) {
	// TODO: Use a job scheduler
	// Schedule next run
	j.lock.Lock()
	if j.timesToRepeat != 0 {
		j.timesToRepeat--
		go j.StartWaiting(cache)
	}
	j.lock.Unlock()

	j.lock.RLock()
	jobRunner := &JobRunner{job: j, meta: j.Metadata}
	j.lock.RUnlock()
	newStat, newMeta, err := jobRunner.Run(cache)
	if err != nil {
		log.Error("Error running job: %s", err)
	}

	j.lock.Lock()
	j.Metadata = newMeta
	j.Stats = append(j.Stats, newStat)
	j.lock.Unlock()
}

func (j *Job) StopTimer() {
	j.lock.Lock()
	defer j.lock.Unlock()

	if j.jobTimer != nil {
		j.jobTimer.Stop()
	}
}
<commit_msg>Fixed unlocking of already unlocked mutex.<commit_after>package job

import (
	"bytes"
	"encoding/gob"
	"fmt"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/ajvb/kala/utils/iso8601"
	"github.com/ajvb/kala/utils/logging"

	"github.com/mattn/go-shellwords"
	"github.com/nu7hatch/gouuid"
)

var (
	log = logging.GetLogger("kala.job")

	shParser = shellwords.NewParser()
)

func init() {
	shParser.ParseEnv = true
	shParser.ParseBacktick = true
}

type Job struct {
	Name string `json:"name"`
	Id   string `json:"id"`

	// Command to run
	// e.g. "bash /path/to/my/script.sh"
	Command string `json:"command"`

	// Email of the owner of this job
	// e.g. "admin@example.com"
	Owner string `json:"owner"`

	// Is this job disabled?
	Disabled bool `json:"disabled"`

	// Jobs that are dependent upon this one will be run after this job runs.
	DependentJobs []string `json:"dependent_jobs"`

	// List of ids of jobs that this job is dependent upon.
	ParentJobs []string `json:"parent_jobs"`

	// ISO 8601 String
	// e.g. "R/2014-03-08T20:00:00.000Z/PT2H"
	Schedule     string `json:"schedule"`
	scheduleTime time.Time
	// ISO 8601 Duration struct, used for scheduling
	// job after each run.
	delayDuration *iso8601.Duration

	// Number of times to schedule this job after the
	// first run.
	timesToRepeat int64

	// Number of times to retry on failed attempt for each run.
	Retries uint `json:"retries"`

	// Duration in which it is safe to retry the Job.
	Epsilon         string `json:"epsilon"`
	epsilonDuration *iso8601.Duration

	jobTimer  *time.Timer
	NextRunAt time.Time `json:"next_run_at"`

	// Meta data about successful and failed runs.
	Metadata Metadata `json:"metadata"`

	// Collection of Job Stats
	Stats []*JobStat `json:"stats"`

	lock sync.RWMutex
}

type Metadata struct {
	SuccessCount     uint      `json:"success_count"`
	LastSuccess      time.Time `json:"last_success"`
	ErrorCount       uint      `json:"error_count"`
	LastError        time.Time `json:"last_error"`
	LastAttemptedRun time.Time `json:"last_attempted_run"`
}

// Bytes returns the byte representation of the Job.
func (j Job) Bytes() ([]byte, error) {
	buff := new(bytes.Buffer)
	enc := gob.NewEncoder(buff)
	err := enc.Encode(j)
	if err != nil {
		return nil, err
	}
	return buff.Bytes(), nil
}

// NewFromBytes returns a Job instance from a byte representation.
func NewFromBytes(b []byte) (*Job, error) {
	j := &Job{}

	buf := bytes.NewBuffer(b)
	err := gob.NewDecoder(buf).Decode(j)
	if err != nil {
		return nil, err
	}

	return j, nil
}

// Init fills in the protected fields and parses the iso8601 notation.
// It also adds the job to the Cache
func (j *Job) Init(cache JobCache) error {
	j.lock.Lock()
	defer j.lock.Unlock()

	u4, err := uuid.NewV4()
	if err != nil {
		log.Error("Error occured when generating uuid: %s", err)
		return err
	}
	j.Id = u4.String()

	// Add Job to the cache.
	err = cache.Set(j)
	if err != nil {
		return err
	}

	if len(j.ParentJobs) != 0 {
		// Add new job to parent jobs
		for _, p := range j.ParentJobs {
			parentJob, err := cache.Get(p)
			if err != nil {
				return err
			}
			parentJob.DependentJobs = append(parentJob.DependentJobs, j.Id)
		}

		return nil
	}

	// TODO: Delete from cache after running.
	if j.Schedule == "" {
		// If schedule is empty, its a one-off job.
		go j.Run(cache)
		return nil
	}

	j.lock.Unlock()
	err = j.InitDelayDuration(true)
	j.lock.Lock()
	if err != nil {
		j.lock.Unlock()
		cache.Delete(j.Id)
		j.lock.Lock()
		return err
	}

	j.lock.Unlock()
	j.StartWaiting(cache)
	j.lock.Lock()

	return nil
}

// InitDelayDuration is used to parsed the iso8601 Schedule notation into its relevent fields in the Job struct.
// If checkTime is true, then it will return an error if the Scheduled time has passed.
func (j *Job) InitDelayDuration(checkTime bool) error {
	j.lock.Lock()
	defer j.lock.Unlock()

	var err error
	splitTime := strings.Split(j.Schedule, "/")
	if len(splitTime) != 3 {
		return fmt.Errorf(
			"Schedule not formatted correctly. Should look like: R/2014-03-08T20:00:00Z/PT2H",
		)
	}

	// Handle Repeat Amount
	if splitTime[0] == "R" {
		// Repeat forever
		j.timesToRepeat = -1
	} else {
		j.timesToRepeat, err = strconv.ParseInt(strings.Split(splitTime[0], "R")[1], 10, 0)
		if err != nil {
			log.Error("Error converting timesToRepeat to an int: %s", err)
			return err
		}
	}
	log.Debug("timesToRepeat: %d", j.timesToRepeat)

	j.scheduleTime, err = time.Parse(time.RFC3339, splitTime[1])
	if err != nil {
		log.Error("Error converting scheduleTime to a time.Time: %s", err)
		return err
	}
	if checkTime {
		if (time.Duration(j.scheduleTime.UnixNano() - time.Now().UnixNano())) < 0 {
			return fmt.Errorf("Schedule time has passed on Job with id of %s", j.Id)
		}
	}
	log.Debug("Schedule Time: %s", j.scheduleTime)

	j.delayDuration, err = iso8601.FromString(splitTime[2])
	if err != nil {
		log.Error("Error converting delayDuration to a iso8601.Duration: %s", err)
		return err
	}
	log.Debug("Delay Duration: %s", j.delayDuration.ToDuration())

	if j.Epsilon != "" {
		j.epsilonDuration, err = iso8601.FromString(j.Epsilon)
		if err != nil {
			log.Error("Error converting j.Epsilon to iso8601.Duration: %s", err)
			return err
		}
	}

	return nil
}

// StartWaiting begins a timer for when it should execute the Jobs .Run() method.
func (j *Job) StartWaiting(cache JobCache) {
	waitDuration := j.GetWaitDuration()

	j.lock.Lock()
	defer j.lock.Unlock()

	log.Info("Job Scheduled to run in: %s", waitDuration)

	j.NextRunAt = time.Now().Add(waitDuration)

	jobRun := func() { j.Run(cache) }
	j.jobTimer = time.AfterFunc(waitDuration, jobRun)
}

func (j *Job) GetWaitDuration() time.Duration {
	j.lock.RLock()
	defer j.lock.RUnlock()

	waitDuration := time.Duration(j.scheduleTime.UnixNano() - time.Now().UnixNano())

	if waitDuration < 0 {
		// Needs to be recalculated each time because of Months.
		if j.Metadata.LastAttemptedRun.IsZero() {
			waitDuration = j.delayDuration.ToDuration()
		} else {
			lastRun := j.Metadata.LastAttemptedRun
			lastRun = lastRun.Add(j.delayDuration.ToDuration())
			waitDuration = lastRun.Sub(time.Now())
		}
	}

	return waitDuration
}

// Disable stops the job from running by stopping its jobTimer. It also sets Job.Disabled to true,
// which is reflected in the UI.
func (j *Job) Disable() {
	j.lock.Lock()
	defer j.lock.Unlock()

	if j.jobTimer != nil {
		j.jobTimer.Stop()
	}
	j.Disabled = true
}

// DeleteFromParentJobs goes through and deletes the current job from any parent jobs.
func (j *Job) DeleteFromParentJobs(cache JobCache) error {
	j.lock.Lock()
	defer j.lock.Unlock()

	for _, p := range j.ParentJobs {
		parentJob, err := cache.Get(p)

		if err != nil {
			return err
		}

		parentJob.lock.Lock()

		ndx := 0
		for i, id := range parentJob.DependentJobs {
			if id == j.Id {
				ndx = i
				break
			}
		}
		parentJob.DependentJobs = append(
			parentJob.DependentJobs[:ndx], parentJob.DependentJobs[ndx+1:]...,
		)

		parentJob.lock.Unlock()
	}

	return nil
}

// DeleteFromDependentJobs
func (j *Job) DeleteFromDependentJobs(cache JobCache) error {
	j.lock.RLock()
	defer j.lock.RUnlock()

	for _, id := range j.DependentJobs {
		childJob, err := cache.Get(id)
		if err != nil {
			return err
		}

		// If there are no other parent jobs, delete this job.
		if len(childJob.ParentJobs) == 1 {
			cache.Delete(childJob.Id)
			continue
		}

		childJob.lock.Lock()

		ndx := 0
		for i, id := range childJob.ParentJobs {
			if id == j.Id {
				ndx = i
				break
			}
		}
		childJob.ParentJobs = append(
			childJob.ParentJobs[:ndx], childJob.ParentJobs[ndx+1:]...,
		)

		childJob.lock.Unlock()

	}

	return nil
}

func (j *Job) Run(cache JobCache) {
	// TODO: Use a job scheduler
	// Schedule next run
	j.lock.Lock()
	if j.timesToRepeat != 0 {
		j.timesToRepeat--
		go j.StartWaiting(cache)
	}
	j.lock.Unlock()

	j.lock.RLock()
	jobRunner := &JobRunner{job: j, meta: j.Metadata}
	j.lock.RUnlock()
	newStat, newMeta, err := jobRunner.Run(cache)
	if err != nil {
		log.Error("Error running job: %s", err)
	}

	j.lock.Lock()
	j.Metadata = newMeta
	j.Stats = append(j.Stats, newStat)
	j.lock.Unlock()
}

func (j *Job) StopTimer() {
	j.lock.Lock()
	defer j.lock.Unlock()

	if j.jobTimer != nil {
		j.jobTimer.Stop()
	}
}
<|endoftext|>