<commit_before>package poloniex

import (
	"encoding/json"
	"log"
	"time"

	"github.com/k0kubun/pp"
	"gopkg.in/beatgammit/turnpike.v2"
)

type (
	WSTicker struct {
		Pair          string
		Last          float64
		Ask           float64
		Bid           float64
		PercentChange float64
		BaseVolume    float64
		QuoteVolume   float64
		IsFrozen      bool
		DailyHigh     float64
		DailyLow      float64
	}

	WSTrade struct {
		TradeID string
		Rate    float64 `json:",string"`
		Amount  float64 `json:",string"`
		Type    string
		Date    string
		TS      time.Time
	}
	WSOrderOrTrade []struct {
		Data WSTrade
		Type string
	}
)

//SubscribeTicker subscribes to the ticker feed
func (p *Poloniex) SubscribeTicker(ch chan WSTicker) {
	p.InitWS()
	p.subscribedTo["ticker"] = true
	p.ws.Subscribe("ticker", p.makeTickerHandler(ch))
}

//SubsribeOrder subscribes to the order and trade feed
func (p *Poloniex) SubscribeOrder(code string, ch chan WSOrderOrTrade) {
	p.InitWS()
	p.subscribedTo[code] = true
	p.ws.Subscribe(code, p.makeOrderHandler(code, ch))
}

//UnsubscribeTicker.... I think you can guess
func (p *Poloniex) UnsubscribeTicker() {
	p.InitWS()
	p.Unsubscribe("ticker")
}

//UnsubscribeOrder.... I think you can guess
func (p *Poloniex) UnsubscribeOrder(code string) {
	p.InitWS()
	p.Unsubscribe(code)
}

func (p *Poloniex) Unsubscribe(code string) {
	p.InitWS()
	if p.isSubscribed(code) {
		delete(p.subscribedTo, code)
		p.ws.Unsubscribe(code)
	}
}

//makeTickerHandler takes a WS Order or Trade and send it over the channel sepcified by the user
func (p *Poloniex) makeTickerHandler(ch chan WSTicker) turnpike.EventHandler {
	return func(p []interface{}, n map[string]interface{}) {
		t := WSTicker{
			Pair:          p[0].(string),
			Last:          f(p[1]),
			Ask:           f(p[2]),
			Bid:           f(p[3]),
			PercentChange: f(p[4]) * 100.0,
			BaseVolume:    f(p[5]),
			QuoteVolume:   f(p[6]),
			IsFrozen:      p[7].(float64) != 0.0,
			DailyHigh:     f(p[8]),
			DailyLow:      f(p[9]),
		}
		ch <- t
	}
}

//makeOrderHandler takes a WS Order or Trade and send it over the channel sepcified by the user
func (p *Poloniex) makeOrderHandler(coin string, ch chan WSOrderOrTrade) turnpike.EventHandler {
	return func(p []interface{}, n map[string]interface{}) {
		b, err := json.Marshal(p)
		if err != nil {
			log.Println(err)
			return
		}
		oot := WSOrderOrTrade{}
		err = json.Unmarshal(b, &oot)
		if err != nil {
			log.Println(err)
			return
		}
		ootTmp := WSOrderOrTrade{}
		for _, o := range oot {
			if o.Type == "newTrade" {
				pp.Println("Date:", o.Data.Date)
				d, err := time.Parse("2006-01-02 15:04:05", o.Data.Date)
				if err != nil {
					log.Println(err)
				}
				o.Data.TS = d
			}
			ootTmp = append(ootTmp, o)
		}
		ch <- ootTmp
	}
}
<commit_msg>Subscribe methods  now return a channel to 'listen' to the feed, also added the sequence into the feed.<commit_after>package poloniex

import (
	"encoding/json"
	"log"
	"time"

	"github.com/k0kubun/pp"
	"gopkg.in/beatgammit/turnpike.v2"
)

type (
	//WSTicker describes a ticker item
	WSTicker struct {
		Pair          string
		Last          float64
		Ask           float64
		Bid           float64
		PercentChange float64
		BaseVolume    float64
		QuoteVolume   float64
		IsFrozen      bool
		DailyHigh     float64
		DailyLow      float64
	}

	// WSTickerChan is a onduit through which WSTicker items are sent
	WSTickerChan chan WSTicker

	//WSTrade describes a trade, a new order, or an order update
	WSTrade struct {
		TradeID string
		Rate    float64 `json:",string"`
		Amount  float64 `json:",string"`
		Type    string
		Date    string
		TS      time.Time
	}

	//WSOrderOrTrade is a slice of WSTrades with an indicator of the type (trade, new order, update order)
	WSOrderOrTrade struct {
		Seq    int64
		Orders WSOrders
	}

	WSOrders []struct {
		Data WSTrade
		Type string
	}

	// WSOrderOrTradeChan is a onduit through which WSTicker items are sent
	WSOrderOrTradeChan chan WSOrderOrTrade
)

const (
	//SENTINEL is used to mark items without a sequence number
	SENTINEL = int64(-1)
)

//SubscribeTicker subscribes to the ticker feed and returns a channel over which it will send updates
func (p *Poloniex) SubscribeTicker() WSTickerChan {
	p.InitWS()
	p.subscribedTo["ticker"] = true
	ch := make(WSTickerChan)
	p.ws.Subscribe("ticker", p.makeTickerHandler(ch))
	return ch
}

//SubscribeOrder subscribes to the order and trade feed and returns a channel over which it will send updates
func (p *Poloniex) SubscribeOrder(code string) WSOrderOrTradeChan {
	p.InitWS()
	p.subscribedTo[code] = true
	ch := make(WSOrderOrTradeChan)
	p.ws.Subscribe(code, p.makeOrderHandler(code, ch))
	return ch
}

//UnsubscribeTicker ... I think you can guess
func (p *Poloniex) UnsubscribeTicker() {
	p.InitWS()
	p.Unsubscribe("ticker")
}

//UnsubscribeOrder ... I think you can guess
func (p *Poloniex) UnsubscribeOrder(code string) {
	p.InitWS()
	p.Unsubscribe(code)
}

//Unsubscribe from the relevant feed
func (p *Poloniex) Unsubscribe(code string) {
	p.InitWS()
	if p.isSubscribed(code) {
		delete(p.subscribedTo, code)
		p.ws.Unsubscribe(code)
	}
}

//makeTickerHandler takes a WS Order or Trade and send it over the channel sepcified by the user
func (p *Poloniex) makeTickerHandler(ch chan WSTicker) turnpike.EventHandler {
	return func(p []interface{}, n map[string]interface{}) {
		t := WSTicker{
			Pair:          p[0].(string),
			Last:          f(p[1]),
			Ask:           f(p[2]),
			Bid:           f(p[3]),
			PercentChange: f(p[4]) * 100.0,
			BaseVolume:    f(p[5]),
			QuoteVolume:   f(p[6]),
			IsFrozen:      p[7].(float64) != 0.0,
			DailyHigh:     f(p[8]),
			DailyLow:      f(p[9]),
		}
		ch <- t
	}
}

//makeOrderHandler takes a WS Order or Trade and send it over the channel sepcified by the user
func (p *Poloniex) makeOrderHandler(coin string, ch WSOrderOrTradeChan) turnpike.EventHandler {
	return func(p []interface{}, n map[string]interface{}) {
		seq := int64(SENTINEL)
		if s, ok := n["seq"]; ok {
			seq = int64(s.(float64))
		}
		b, err := json.Marshal(p)
		if err != nil {
			log.Println(err)
			return
		}
		oot := WSOrders{}
		err = json.Unmarshal(b, &oot)
		if err != nil {
			log.Println(err)
			return
		}
		ootTmp := WSOrders{}
		for _, o := range oot {
			if o.Type == "newTrade" {
				pp.Println("Date:", o.Data.Date)
				d, err := time.Parse("2006-01-02 15:04:05", o.Data.Date)
				if err != nil {
					log.Println(err)
				}
				o.Data.TS = d
			}
			ootTmp = append(ootTmp, o)
		}
		o := WSOrderOrTrade{Seq: seq, Orders: ootTmp}
		ch <- o
	}
}
<|endoftext|>


<commit_before>package brightbox

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

	"github.com/brightbox/gobrightbox"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceBrightboxServerGroup() *schema.Resource {
	return &schema.Resource{
		Create: resourceBrightboxServerGroupCreate,
		Read:   resourceBrightboxServerGroupRead,
		Update: resourceBrightboxServerGroupUpdate,
		Delete: resourceBrightboxServerGroupDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},

			"description": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},
		},
	}
}

func resourceBrightboxServerGroupCreate(
	d *schema.ResourceData,
	meta interface{},
) error {
	client := meta.(*CompositeClient).ApiClient

	log.Printf("[INFO] Creating Server Group")
	server_group_opts := &brightbox.ServerGroupOptions{}
	err := addUpdateableServerGroupOptions(d, server_group_opts)
	if err != nil {
		return err
	}

	server_group, err := client.CreateServerGroup(server_group_opts)
	if err != nil {
		return fmt.Errorf("Error creating Server Group: %s", err)
	}

	d.SetId(server_group.Id)

	setServerGroupAttributes(d, server_group)

	return nil
}

func setServerGroupAttributes(
	d *schema.ResourceData,
	server_group *brightbox.ServerGroup,
) {
	d.Set("name", server_group.Name)
	d.Set("description", server_group.Description)

}

func resourceBrightboxServerGroupRead(
	d *schema.ResourceData,
	meta interface{},
) error {
	client := meta.(*CompositeClient).ApiClient

	server_group, err := client.ServerGroup(d.Id())
	if err != nil {
		if strings.HasPrefix(err.Error(), "missing_resource:") {
			log.Printf("[WARN] Server Group not found, removing from state: %s", d.Id())
			d.SetId("")
			return nil
		}
		return fmt.Errorf("Error retrieving Server Group details: %s", err)
	}

	setServerGroupAttributes(d, server_group)

	return nil
}

func resourceBrightboxServerGroupDelete(
	d *schema.ResourceData,
	meta interface{},
) error {
	client := meta.(*CompositeClient).ApiClient

	server_group, err := client.ServerGroup(d.Id())
	if err != nil {
		return fmt.Errorf("Error retrieving Server Group details: %s", err)
	}
	if len(server_group.Servers) > 0 {
		err := clearServerList(client, server_group)
		if err != nil {
			return err
		}
	}

	log.Printf("[INFO] Deleting Server Group %s", d.Id())
	err = client.DestroyServerGroup(d.Id())
	if err != nil {
		return fmt.Errorf("Error deleting Server Group (%s): %s", d.Id(), err)
	}
	return nil
}

func resourceBrightboxServerGroupUpdate(
	d *schema.ResourceData,
	meta interface{},
) error {
	client := meta.(*CompositeClient).ApiClient

	server_group_opts := &brightbox.ServerGroupOptions{
		Id: d.Id(),
	}
	err := addUpdateableServerGroupOptions(d, server_group_opts)
	if err != nil {
		return err
	}
	log.Printf("[DEBUG] Server Group update configuration: %#v", server_group_opts)

	server_group, err := client.UpdateServerGroup(server_group_opts)
	if err != nil {
		return fmt.Errorf("Error updating Server Group (%s): %s", server_group_opts.Id, err)
	}

	setServerGroupAttributes(d, server_group)
	return nil
}

func addUpdateableServerGroupOptions(
	d *schema.ResourceData,
	opts *brightbox.ServerGroupOptions,
) error {
	assign_string(d, &opts.Name, "name")
	assign_string(d, &opts.Description, "description")
	return nil
}

func serverIdList(servers []brightbox.Server) []string {
	var result []string
	for _, srv := range servers {
		result = append(result, srv.Id)
	}
	return result
}

func clearServerList(client *brightbox.Client, initial_server_group *brightbox.ServerGroup) error {
	serverID := initial_server_group.Id
	server_list := initial_server_group.Servers
	serverIds := serverIdList(server_list)
	log.Printf("[INFO] Removing servers %#v from server group %s", serverIds, serverID)
	_, err := client.RemoveServersFromServerGroup(serverID, serverIds)
	if err != nil {
		return fmt.Errorf("Error removing servers from server group %s", serverID)
	}
	// Wait for group to empty
	return resource.Retry(
		1*time.Minute,
		func() *resource.RetryError {
			server_group, err := client.ServerGroup(serverID)
			if err != nil {
				return resource.NonRetryableError(
					fmt.Errorf("Error retrieving Server Group details: %s", err),
				)
			}
			if len(server_group.Servers) > 0 {
				return resource.RetryableError(
					fmt.Errorf("Error: servers %#v still in server group %s", serverIdList(server_group.Servers), server_group.Id),
				)
			}
			return nil
		},
	)
}
<commit_msg>Refactor Server Group<commit_after>package brightbox

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

	"github.com/brightbox/gobrightbox"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceBrightboxServerGroup() *schema.Resource {
	return &schema.Resource{
		Create: resourceBrightboxServerGroupCreate,
		Read:   resourceBrightboxServerGroupRead,
		Update: resourceBrightboxServerGroupUpdate,
		Delete: resourceBrightboxServerGroupDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"name": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},

			"description": &schema.Schema{
				Type:     schema.TypeString,
				Optional: true,
			},
		},
	}
}

func resourceBrightboxServerGroupCreate(
	d *schema.ResourceData,
	meta interface{},
) error {
	client := meta.(*CompositeClient).ApiClient

	log.Printf("[INFO] Creating Server Group")
	server_group_opts := &brightbox.ServerGroupOptions{}
	err := addUpdateableServerGroupOptions(d, server_group_opts)
	if err != nil {
		return err
	}

	server_group, err := client.CreateServerGroup(server_group_opts)
	if err != nil {
		return fmt.Errorf("Error creating Server Group: %s", err)
	}

	d.SetId(server_group.Id)

	return setServerGroupAttributes(d, server_group)
}

func resourceBrightboxServerGroupRead(
	d *schema.ResourceData,
	meta interface{},
) error {
	client := meta.(*CompositeClient).ApiClient

	server_group, err := client.ServerGroup(d.Id())
	if err != nil {
		if strings.HasPrefix(err.Error(), "missing_resource:") {
			log.Printf("[WARN] Server Group not found, removing from state: %s", d.Id())
			d.SetId("")
			return nil
		}
		return fmt.Errorf("Error retrieving Server Group details: %s", err)
	}

	return setServerGroupAttributes(d, server_group)
}

func resourceBrightboxServerGroupDelete(
	d *schema.ResourceData,
	meta interface{},
) error {
	client := meta.(*CompositeClient).ApiClient

	server_group, err := client.ServerGroup(d.Id())
	if err != nil {
		return fmt.Errorf("Error retrieving Server Group details: %s", err)
	}
	if len(server_group.Servers) > 0 {
		err := clearServerList(client, server_group)
		if err != nil {
			return err
		}
	}

	log.Printf("[INFO] Deleting Server Group %s", d.Id())
	err = client.DestroyServerGroup(d.Id())
	if err != nil {
		return fmt.Errorf("Error deleting Server Group (%s): %s", d.Id(), err)
	}
	return nil
}

func resourceBrightboxServerGroupUpdate(
	d *schema.ResourceData,
	meta interface{},
) error {
	client := meta.(*CompositeClient).ApiClient

	server_group_opts := &brightbox.ServerGroupOptions{
		Id: d.Id(),
	}
	err := addUpdateableServerGroupOptions(d, server_group_opts)
	if err != nil {
		return err
	}
	log.Printf("[DEBUG] Server Group update configuration: %#v", server_group_opts)

	server_group, err := client.UpdateServerGroup(server_group_opts)
	if err != nil {
		return fmt.Errorf("Error updating Server Group (%s): %s", server_group_opts.Id, err)
	}

	return setServerGroupAttributes(d, server_group)
}

func addUpdateableServerGroupOptions(
	d *schema.ResourceData,
	opts *brightbox.ServerGroupOptions,
) error {
	assign_string(d, &opts.Name, "name")
	assign_string(d, &opts.Description, "description")
	return nil
}

func setServerGroupAttributes(
	d *schema.ResourceData,
	server_group *brightbox.ServerGroup,
) error {
	d.Set("name", server_group.Name)
	d.Set("description", server_group.Description)
	return nil
}

func serverIdList(servers []brightbox.Server) []string {
	var result []string
	for _, srv := range servers {
		result = append(result, srv.Id)
	}
	return result
}

func clearServerList(client *brightbox.Client, initial_server_group *brightbox.ServerGroup) error {
	serverID := initial_server_group.Id
	server_list := initial_server_group.Servers
	serverIds := serverIdList(server_list)
	log.Printf("[INFO] Removing servers %#v from server group %s", serverIds, serverID)
	_, err := client.RemoveServersFromServerGroup(serverID, serverIds)
	if err != nil {
		return fmt.Errorf("Error removing servers from server group %s", serverID)
	}
	// Wait for group to empty
	return resource.Retry(
		1*time.Minute,
		func() *resource.RetryError {
			server_group, err := client.ServerGroup(serverID)
			if err != nil {
				return resource.NonRetryableError(
					fmt.Errorf("Error retrieving Server Group details: %s", err),
				)
			}
			if len(server_group.Servers) > 0 {
				return resource.RetryableError(
					fmt.Errorf("Error: servers %#v still in server group %s", serverIdList(server_group.Servers), server_group.Id),
				)
			}
			return nil
		},
	)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path"
	"path/filepath"
	"strings"
	"text/template"
	"time"

	"github.com/eknkc/amber"
	"github.com/russross/blackfriday"
	"github.com/yosssi/gcss"
)

const (
	ZSDIR  = ".zs"
	PUBDIR = ".pub"
)

type Vars map[string]string
type Funcs template.FuncMap

// Parses markdown content. Returns parsed header variables and content
func md(path string, globals Vars) (Vars, string, error) {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, "", err
	}
	s := string(b)
	url := path[:len(path)-len(filepath.Ext(path))] + ".html"
	v := Vars{
		"title":       "",
		"description": "",
		"keywords":    "",
	}
	for name, value := range globals {
		v[name] = value
	}
	if _, err := os.Stat(filepath.Join(ZSDIR, "layout.amber")); err == nil {
		v["layout"] = "layout.amber"
	} else {
		v["layout"] = "layout.html"
	}
	v["file"] = path
	v["url"] = url
	v["output"] = filepath.Join(PUBDIR, url)

	if strings.Index(s, "\n\n") == -1 {
		return v, s, nil
	}
	header, body := split2(s, "\n\n")
	for _, line := range strings.Split(header, "\n") {
		key, value := split2(line, ":")
		v[strings.ToLower(strings.TrimSpace(key))] = strings.TrimSpace(value)
	}
	if strings.HasPrefix(v["url"], "./") {
		v["url"] = v["url"][2:]
	}
	return v, body, nil
}

// Use standard Go templates
func render(s string, funcs Funcs, vars Vars) (string, error) {
	f := Funcs{}
	for k, v := range funcs {
		f[k] = v
	}
	for k, v := range vars {
		f[k] = varFunc(v)
	}
	// Plugin functions
	files, _ := ioutil.ReadDir(ZSDIR)
	for _, file := range files {
		if !file.IsDir() {
			name := file.Name()
			if !strings.HasSuffix(name, ".html") && !strings.HasSuffix(name, ".amber") {
				f[strings.TrimSuffix(name, filepath.Ext(name))] = pluginFunc(name, vars)
			}
		}
	}

	tmpl, err := template.New("").Funcs(template.FuncMap(f)).Parse(s)
	if err != nil {
		return "", err
	}
	out := &bytes.Buffer{}
	if err := tmpl.Execute(out, vars); err != nil {
		return "", err
	}
	return string(out.Bytes()), nil
}

// Renders markdown with the given layout into html expanding all the macros
func buildMarkdown(path string, w io.Writer, funcs Funcs, vars Vars) error {
	v, body, err := md(path, vars)
	if err != nil {
		return err
	}
	content, err := render(body, funcs, v)
	if err != nil {
		return err
	}
	v["content"] = string(blackfriday.MarkdownBasic([]byte(content)))
	if w == nil {
		out, err := os.Create(filepath.Join(PUBDIR, renameExt(path, "", ".html")))
		if err != nil {
			return err
		}
		defer out.Close()
		w = out
	}
	if strings.HasSuffix(v["layout"], ".amber") {
		return buildAmber(filepath.Join(ZSDIR, v["layout"]), w, funcs, v)
	} else {
		return buildHTML(filepath.Join(ZSDIR, v["layout"]), w, funcs, v)
	}
}

// Renders text file expanding all variable macros inside it
func buildHTML(path string, w io.Writer, funcs Funcs, vars Vars) error {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}
	content, err := render(string(b), funcs, vars)
	if err != nil {
		return err
	}
	if w == nil {
		f, err := os.Create(filepath.Join(PUBDIR, path))
		if err != nil {
			return err
		}
		defer f.Close()
		w = f
	}
	_, err = io.WriteString(w, content)
	return err
}

// Renders .amber file into .html
func buildAmber(path string, w io.Writer, funcs Funcs, vars Vars) error {
	a := amber.New()
	err := a.ParseFile(path)
	if err != nil {
		return err
	}

	data := map[string]interface{}{}
	for k, v := range vars {
		data[k] = v
	}
	for k, v := range funcs {
		data[k] = v
	}

	t, err := a.Compile()
	if err != nil {
		return err
	}
	if w == nil {
		f, err := os.Create(filepath.Join(PUBDIR, renameExt(path, ".amber", ".html")))
		if err != nil {
			return err
		}
		defer f.Close()
		w = f
	}
	return t.Execute(w, data)
}

// Compiles .gcss into .css
func buildGCSS(path string, w io.Writer) error {
	f, err := os.Open(path)
	if err != nil {
		return err
	}
	defer f.Close()

	if w == nil {
		s := strings.TrimSuffix(path, ".gcss") + ".css"
		css, err := os.Create(filepath.Join(PUBDIR, s))
		if err != nil {
			return err
		}
		defer css.Close()
		w = css
	}
	_, err = gcss.Compile(w, f)
	return err
}

// Copies file as is from path to writer
func buildRaw(path string, w io.Writer) error {
	in, err := os.Open(path)
	if err != nil {
		return err
	}
	defer in.Close()
	if w == nil {
		if out, err := os.Create(filepath.Join(PUBDIR, path)); err != nil {
			return err
		} else {
			defer out.Close()
			w = out
		}
	}
	_, err = io.Copy(w, in)
	return err
}

func build(path string, w io.Writer, funcs Funcs, vars Vars) error {
	ext := filepath.Ext(path)
	if ext == ".md" || ext == ".mkd" {
		return buildMarkdown(path, w, funcs, vars)
	} else if ext == ".html" || ext == ".xml" {
		return buildHTML(path, w, funcs, vars)
	} else if ext == ".amber" {
		return buildAmber(path, w, funcs, vars)
	} else if ext == ".gcss" {
		return buildGCSS(path, w)
	} else {
		return buildRaw(path, w)
	}
}

func buildAll(watch bool) {
	lastModified := time.Unix(0, 0)
	modified := false

	vars := globals()
	for {
		os.Mkdir(PUBDIR, 0755)
		funcs := builtins()
		err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
			// ignore hidden files and directories
			if filepath.Base(path)[0] == '.' || strings.HasPrefix(path, ".") {
				return nil
			}

			if info.IsDir() {
				os.Mkdir(filepath.Join(PUBDIR, path), 0755)
				return nil
			} else if info.ModTime().After(lastModified) {
				if !modified {
					// About to be modified, so run pre-build hook
					// FIXME on windows it might not work well
					run(filepath.Join(ZSDIR, "pre"), []string{}, nil, nil)
					modified = true
				}
				log.Println("build: ", path)
				return build(path, nil, funcs, vars)
			}
			return nil
		})
		if err != nil {
			log.Println("ERROR:", err)
		}
		if modified {
			// Something was modified, so post-build hook
			// FIXME on windows it might not work well
			run(filepath.Join(ZSDIR, "post"), []string{}, nil, nil)
			modified = false
		}
		if !watch {
			break
		}
		lastModified = time.Now()
		time.Sleep(1 * time.Second)
	}
}

func main() {
	if len(os.Args) == 1 {
		fmt.Println(os.Args[0], "<command> [args]")
		return
	}
	cmd := os.Args[1]
	args := os.Args[2:]
	switch cmd {
	case "build":
		if len(args) == 0 {
			buildAll(false)
		} else if len(args) == 1 {
			if err := build(args[0], os.Stdout, builtins(), globals()); err != nil {
				fmt.Println("ERROR: " + err.Error())
			}
		} else {
			fmt.Println("ERROR: too many arguments")
		}
	case "watch":
		buildAll(true)
	case "var":
		fmt.Println(Var(args))
	case "lorem":
		fmt.Println(Lorem(args))
	case "dateparse":
		fmt.Println(DateParse(args))
	case "datefmt":
		fmt.Println(DateFmt(args))
	case "wc":
		fmt.Println(WordCount(args))
	case "timetoread":
		fmt.Println(TimeToRead(args))
	default:
		err := run(path.Join(ZSDIR, cmd), args, globals(), os.Stdout)
		if err != nil {
			log.Println("ERROR:", err)
		}
	}
}
<commit_msg>added check for fs walk errors<commit_after>package main

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path"
	"path/filepath"
	"strings"
	"text/template"
	"time"

	"github.com/eknkc/amber"
	"github.com/russross/blackfriday"
	"github.com/yosssi/gcss"
)

const (
	ZSDIR  = ".zs"
	PUBDIR = ".pub"
)

type Vars map[string]string
type Funcs template.FuncMap

// Parses markdown content. Returns parsed header variables and content
func md(path string, globals Vars) (Vars, string, error) {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, "", err
	}
	s := string(b)
	url := path[:len(path)-len(filepath.Ext(path))] + ".html"
	v := Vars{
		"title":       "",
		"description": "",
		"keywords":    "",
	}
	for name, value := range globals {
		v[name] = value
	}
	if _, err := os.Stat(filepath.Join(ZSDIR, "layout.amber")); err == nil {
		v["layout"] = "layout.amber"
	} else {
		v["layout"] = "layout.html"
	}
	v["file"] = path
	v["url"] = url
	v["output"] = filepath.Join(PUBDIR, url)

	if strings.Index(s, "\n\n") == -1 {
		return v, s, nil
	}
	header, body := split2(s, "\n\n")
	for _, line := range strings.Split(header, "\n") {
		key, value := split2(line, ":")
		v[strings.ToLower(strings.TrimSpace(key))] = strings.TrimSpace(value)
	}
	if strings.HasPrefix(v["url"], "./") {
		v["url"] = v["url"][2:]
	}
	return v, body, nil
}

// Use standard Go templates
func render(s string, funcs Funcs, vars Vars) (string, error) {
	f := Funcs{}
	for k, v := range funcs {
		f[k] = v
	}
	for k, v := range vars {
		f[k] = varFunc(v)
	}
	// Plugin functions
	files, _ := ioutil.ReadDir(ZSDIR)
	for _, file := range files {
		if !file.IsDir() {
			name := file.Name()
			if !strings.HasSuffix(name, ".html") && !strings.HasSuffix(name, ".amber") {
				f[strings.TrimSuffix(name, filepath.Ext(name))] = pluginFunc(name, vars)
			}
		}
	}

	tmpl, err := template.New("").Funcs(template.FuncMap(f)).Parse(s)
	if err != nil {
		return "", err
	}
	out := &bytes.Buffer{}
	if err := tmpl.Execute(out, vars); err != nil {
		return "", err
	}
	return string(out.Bytes()), nil
}

// Renders markdown with the given layout into html expanding all the macros
func buildMarkdown(path string, w io.Writer, funcs Funcs, vars Vars) error {
	v, body, err := md(path, vars)
	if err != nil {
		return err
	}
	content, err := render(body, funcs, v)
	if err != nil {
		return err
	}
	v["content"] = string(blackfriday.MarkdownBasic([]byte(content)))
	if w == nil {
		out, err := os.Create(filepath.Join(PUBDIR, renameExt(path, "", ".html")))
		if err != nil {
			return err
		}
		defer out.Close()
		w = out
	}
	if strings.HasSuffix(v["layout"], ".amber") {
		return buildAmber(filepath.Join(ZSDIR, v["layout"]), w, funcs, v)
	} else {
		return buildHTML(filepath.Join(ZSDIR, v["layout"]), w, funcs, v)
	}
}

// Renders text file expanding all variable macros inside it
func buildHTML(path string, w io.Writer, funcs Funcs, vars Vars) error {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}
	content, err := render(string(b), funcs, vars)
	if err != nil {
		return err
	}
	if w == nil {
		f, err := os.Create(filepath.Join(PUBDIR, path))
		if err != nil {
			return err
		}
		defer f.Close()
		w = f
	}
	_, err = io.WriteString(w, content)
	return err
}

// Renders .amber file into .html
func buildAmber(path string, w io.Writer, funcs Funcs, vars Vars) error {
	a := amber.New()
	err := a.ParseFile(path)
	if err != nil {
		return err
	}

	data := map[string]interface{}{}
	for k, v := range vars {
		data[k] = v
	}
	for k, v := range funcs {
		data[k] = v
	}

	t, err := a.Compile()
	if err != nil {
		return err
	}
	if w == nil {
		f, err := os.Create(filepath.Join(PUBDIR, renameExt(path, ".amber", ".html")))
		if err != nil {
			return err
		}
		defer f.Close()
		w = f
	}
	return t.Execute(w, data)
}

// Compiles .gcss into .css
func buildGCSS(path string, w io.Writer) error {
	f, err := os.Open(path)
	if err != nil {
		return err
	}
	defer f.Close()

	if w == nil {
		s := strings.TrimSuffix(path, ".gcss") + ".css"
		css, err := os.Create(filepath.Join(PUBDIR, s))
		if err != nil {
			return err
		}
		defer css.Close()
		w = css
	}
	_, err = gcss.Compile(w, f)
	return err
}

// Copies file as is from path to writer
func buildRaw(path string, w io.Writer) error {
	in, err := os.Open(path)
	if err != nil {
		return err
	}
	defer in.Close()
	if w == nil {
		if out, err := os.Create(filepath.Join(PUBDIR, path)); err != nil {
			return err
		} else {
			defer out.Close()
			w = out
		}
	}
	_, err = io.Copy(w, in)
	return err
}

func build(path string, w io.Writer, funcs Funcs, vars Vars) error {
	ext := filepath.Ext(path)
	if ext == ".md" || ext == ".mkd" {
		return buildMarkdown(path, w, funcs, vars)
	} else if ext == ".html" || ext == ".xml" {
		return buildHTML(path, w, funcs, vars)
	} else if ext == ".amber" {
		return buildAmber(path, w, funcs, vars)
	} else if ext == ".gcss" {
		return buildGCSS(path, w)
	} else {
		return buildRaw(path, w)
	}
}

func buildAll(watch bool) {
	lastModified := time.Unix(0, 0)
	modified := false

	vars := globals()
	for {
		os.Mkdir(PUBDIR, 0755)
		funcs := builtins()
		err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
			// ignore hidden files and directories
			if filepath.Base(path)[0] == '.' || strings.HasPrefix(path, ".") {
				return nil
			}
			// inform user about fs walk errors, but continue iteration
			if err != nil {
				log.Println("ERROR:", err)
				return nil
			}

			if info.IsDir() {
				os.Mkdir(filepath.Join(PUBDIR, path), 0755)
				return nil
			} else if info.ModTime().After(lastModified) {
				if !modified {
					// About to be modified, so run pre-build hook
					// FIXME on windows it might not work well
					run(filepath.Join(ZSDIR, "pre"), []string{}, nil, nil)
					modified = true
				}
				log.Println("build: ", path)
				return build(path, nil, funcs, vars)
			}
			return nil
		})
		if err != nil {
			log.Println("ERROR:", err)
		}
		if modified {
			// Something was modified, so post-build hook
			// FIXME on windows it might not work well
			run(filepath.Join(ZSDIR, "post"), []string{}, nil, nil)
			modified = false
		}
		if !watch {
			break
		}
		lastModified = time.Now()
		time.Sleep(1 * time.Second)
	}
}

func main() {
	if len(os.Args) == 1 {
		fmt.Println(os.Args[0], "<command> [args]")
		return
	}
	cmd := os.Args[1]
	args := os.Args[2:]
	switch cmd {
	case "build":
		if len(args) == 0 {
			buildAll(false)
		} else if len(args) == 1 {
			if err := build(args[0], os.Stdout, builtins(), globals()); err != nil {
				fmt.Println("ERROR: " + err.Error())
			}
		} else {
			fmt.Println("ERROR: too many arguments")
		}
	case "watch":
		buildAll(true)
	case "var":
		fmt.Println(Var(args))
	case "lorem":
		fmt.Println(Lorem(args))
	case "dateparse":
		fmt.Println(DateParse(args))
	case "datefmt":
		fmt.Println(DateFmt(args))
	case "wc":
		fmt.Println(WordCount(args))
	case "timetoread":
		fmt.Println(TimeToRead(args))
	default:
		err := run(path.Join(ZSDIR, cmd), args, globals(), os.Stdout)
		if err != nil {
			log.Println("ERROR:", err)
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package source

import (
	"testing"

	"github.com/kubernetes-incubator/external-dns/endpoint"
	"k8s.io/client-go/kubernetes/fake"
	"k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/pkg/apis/extensions/v1beta1"
)

func TestIngress(t *testing.T) {
	t.Run("endpointsFromIngress", testEndpointsFromIngress)
	t.Run("Endpoints", testIngressEndpoints)
}

func testEndpointsFromIngress(t *testing.T) {
	for _, ti := range []struct {
		title    string
		ingress  fakeIngress
		expected []endpoint.Endpoint
	}{
		{
			title: "one rule.host one lb.hostname",
			ingress: fakeIngress{
				dnsnames:  []string{"foo.bar"},
				hostnames: []string{"lb.com"},
			},
			expected: []endpoint.Endpoint{
				{
					DNSName: "foo.bar",
					Target:  "lb.com",
				},
			},
		},
		{
			title: "one rule.host one lb.IP",
			ingress: fakeIngress{
				dnsnames: []string{"foo.bar"},
				ips:      []string{"8.8.8.8"},
			},
			expected: []endpoint.Endpoint{
				{
					DNSName: "foo.bar",
					Target:  "8.8.8.8",
				},
			},
		},
		{
			title: "one rule.host two lb.IP and two lb.Hostname",
			ingress: fakeIngress{
				dnsnames:  []string{"foo.bar"},
				ips:       []string{"8.8.8.8", "127.0.0.1"},
				hostnames: []string{"elb.com", "alb.com"},
			},
			expected: []endpoint.Endpoint{
				{
					DNSName: "foo.bar",
					Target:  "8.8.8.8",
				},
				{
					DNSName: "foo.bar",
					Target:  "127.0.0.1",
				},
				{
					DNSName: "foo.bar",
					Target:  "elb.com",
				},
				{
					DNSName: "foo.bar",
					Target:  "alb.com",
				},
			},
		},
		{
			title: "no rule.host",
			ingress: fakeIngress{
				ips:       []string{"8.8.8.8", "127.0.0.1"},
				hostnames: []string{"elb.com", "alb.com"},
			},
			expected: []endpoint.Endpoint{},
		},
		{
			title: "one empty rule.host",
			ingress: fakeIngress{
				dnsnames:  []string{""},
				ips:       []string{"8.8.8.8", "127.0.0.1"},
				hostnames: []string{"elb.com", "alb.com"},
			},
			expected: []endpoint.Endpoint{},
		},
		{
			title: "no targets",
			ingress: fakeIngress{
				dnsnames: []string{""},
			},
			expected: []endpoint.Endpoint{},
		},
	} {
		t.Run(ti.title, func(t *testing.T) {
			realIngress := ti.ingress.Ingress()
			validateEndpoints(t, endpointsFromIngress(realIngress), ti.expected)
		})
	}
}

func testIngressEndpoints(t *testing.T) {
	namespace := "testing"
	for _, ti := range []struct {
		title        string
		ingressItems []fakeIngress
		expected     []endpoint.Endpoint
	}{
		{
			title: "no ingress",
		},
		{
			title: "two simple ingresses",
			ingressItems: []fakeIngress{
				{
					name:      "fake1",
					namespace: namespace,
					dnsnames:  []string{"example.org"},
					ips:       []string{"8.8.8.8"},
				},
				{
					name:      "fake2",
					namespace: namespace,
					dnsnames:  []string{"new.org"},
					hostnames: []string{"lb.com"},
				},
			},
			expected: []endpoint.Endpoint{
				{
					DNSName: "example.org",
					Target:  "8.8.8.8",
				},
				{
					DNSName: "new.org",
					Target:  "lb.com",
				},
			},
		},
	} {
		t.Run(ti.title, func(t *testing.T) {
			ingresses := make([]*v1beta1.Ingress, 0)
			for _, item := range ti.ingressItems {
				ingresses = append(ingresses, item.Ingress())
			}

			fakeClient := fake.NewSimpleClientset()
			ingressSource := &IngressSource{
				Client: fakeClient,
			}
			for _, ingress := range ingresses {
				_, err := fakeClient.Extensions().Ingresses(ingress.Namespace).Create(ingress)
				if err != nil {
					t.Errorf("fake kubernetes ingress creation should not fail. Ingress %v. Error: %v", *ingress, err)
				}
			}

			res, err := ingressSource.Endpoints()
			if err != nil {
				t.Errorf("ingress endpoints should not fail on valid fake client call")
			}
			validateEndpoints(t, res, ti.expected)

		})
	}
}

// ingress specific helper functions
type fakeIngress struct {
	dnsnames  []string
	ips       []string
	hostnames []string
	namespace string
	name      string
}

func (ing fakeIngress) Ingress() *v1beta1.Ingress {
	ingress := &v1beta1.Ingress{
		ObjectMeta: v1.ObjectMeta{
			Namespace: ing.namespace,
			Name:      ing.name,
		},
		Spec: v1beta1.IngressSpec{
			Rules: []v1beta1.IngressRule{},
		},
		Status: v1beta1.IngressStatus{
			LoadBalancer: v1.LoadBalancerStatus{
				Ingress: []v1.LoadBalancerIngress{},
			},
		},
	}
	for _, dnsname := range ing.dnsnames {
		ingress.Spec.Rules = append(ingress.Spec.Rules, v1beta1.IngressRule{
			Host: dnsname,
		})
	}
	for _, ip := range ing.ips {
		ingress.Status.LoadBalancer.Ingress = append(ingress.Status.LoadBalancer.Ingress, v1.LoadBalancerIngress{
			IP: ip,
		})
	}
	for _, hostname := range ing.hostnames {
		ingress.Status.LoadBalancer.Ingress = append(ingress.Status.LoadBalancer.Ingress, v1.LoadBalancerIngress{
			Hostname: hostname,
		})
	}
	return ingress
}
<commit_msg>test ingress on diff namespaces<commit_after>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package source

import (
	"testing"

	"github.com/kubernetes-incubator/external-dns/endpoint"
	"k8s.io/client-go/kubernetes/fake"
	"k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/pkg/apis/extensions/v1beta1"
)

func TestIngress(t *testing.T) {
	t.Run("endpointsFromIngress", testEndpointsFromIngress)
	t.Run("Endpoints", testIngressEndpoints)
}

func testEndpointsFromIngress(t *testing.T) {
	for _, ti := range []struct {
		title    string
		ingress  fakeIngress
		expected []endpoint.Endpoint
	}{
		{
			title: "one rule.host one lb.hostname",
			ingress: fakeIngress{
				dnsnames:  []string{"foo.bar"},
				hostnames: []string{"lb.com"},
			},
			expected: []endpoint.Endpoint{
				{
					DNSName: "foo.bar",
					Target:  "lb.com",
				},
			},
		},
		{
			title: "one rule.host one lb.IP",
			ingress: fakeIngress{
				dnsnames: []string{"foo.bar"},
				ips:      []string{"8.8.8.8"},
			},
			expected: []endpoint.Endpoint{
				{
					DNSName: "foo.bar",
					Target:  "8.8.8.8",
				},
			},
		},
		{
			title: "one rule.host two lb.IP and two lb.Hostname",
			ingress: fakeIngress{
				dnsnames:  []string{"foo.bar"},
				ips:       []string{"8.8.8.8", "127.0.0.1"},
				hostnames: []string{"elb.com", "alb.com"},
			},
			expected: []endpoint.Endpoint{
				{
					DNSName: "foo.bar",
					Target:  "8.8.8.8",
				},
				{
					DNSName: "foo.bar",
					Target:  "127.0.0.1",
				},
				{
					DNSName: "foo.bar",
					Target:  "elb.com",
				},
				{
					DNSName: "foo.bar",
					Target:  "alb.com",
				},
			},
		},
		{
			title: "no rule.host",
			ingress: fakeIngress{
				ips:       []string{"8.8.8.8", "127.0.0.1"},
				hostnames: []string{"elb.com", "alb.com"},
			},
			expected: []endpoint.Endpoint{},
		},
		{
			title: "one empty rule.host",
			ingress: fakeIngress{
				dnsnames:  []string{""},
				ips:       []string{"8.8.8.8", "127.0.0.1"},
				hostnames: []string{"elb.com", "alb.com"},
			},
			expected: []endpoint.Endpoint{},
		},
		{
			title: "no targets",
			ingress: fakeIngress{
				dnsnames: []string{""},
			},
			expected: []endpoint.Endpoint{},
		},
	} {
		t.Run(ti.title, func(t *testing.T) {
			realIngress := ti.ingress.Ingress()
			validateEndpoints(t, endpointsFromIngress(realIngress), ti.expected)
		})
	}
}

func testIngressEndpoints(t *testing.T) {
	namespace := "testing"
	for _, ti := range []struct {
		title        string
		ingressItems []fakeIngress
		expected     []endpoint.Endpoint
	}{
		{
			title: "no ingress",
		},
		{
			title: "two simple ingresses",
			ingressItems: []fakeIngress{
				{
					name:      "fake1",
					namespace: namespace,
					dnsnames:  []string{"example.org"},
					ips:       []string{"8.8.8.8"},
				},
				{
					name:      "fake2",
					namespace: namespace,
					dnsnames:  []string{"new.org"},
					hostnames: []string{"lb.com"},
				},
			},
			expected: []endpoint.Endpoint{
				{
					DNSName: "example.org",
					Target:  "8.8.8.8",
				},
				{
					DNSName: "new.org",
					Target:  "lb.com",
				},
			},
		},
		{
			title: "two simple ingresses on different namespaces",
			ingressItems: []fakeIngress{
				{
					name:      "fake1",
					namespace: "testing1",
					dnsnames:  []string{"example.org"},
					ips:       []string{"8.8.8.8"},
				},
				{
					name:      "fake2",
					namespace: "testing2",
					dnsnames:  []string{"new.org"},
					hostnames: []string{"lb.com"},
				},
			},
			expected: []endpoint.Endpoint{
				{
					DNSName: "example.org",
					Target:  "8.8.8.8",
				},
				{
					DNSName: "new.org",
					Target:  "lb.com",
				},
			},
		},
	} {
		t.Run(ti.title, func(t *testing.T) {
			ingresses := make([]*v1beta1.Ingress, 0)
			for _, item := range ti.ingressItems {
				ingresses = append(ingresses, item.Ingress())
			}

			fakeClient := fake.NewSimpleClientset()
			ingressSource := &IngressSource{
				Client: fakeClient,
			}
			for _, ingress := range ingresses {
				_, err := fakeClient.Extensions().Ingresses(ingress.Namespace).Create(ingress)
				if err != nil {
					t.Errorf("fake kubernetes ingress creation should not fail. Ingress %v. Error: %v", *ingress, err)
				}
			}

			res, err := ingressSource.Endpoints()
			if err != nil {
				t.Errorf("ingress endpoints should not fail on valid fake client call")
			}
			validateEndpoints(t, res, ti.expected)

		})
	}
}

// ingress specific helper functions
type fakeIngress struct {
	dnsnames  []string
	ips       []string
	hostnames []string
	namespace string
	name      string
}

func (ing fakeIngress) Ingress() *v1beta1.Ingress {
	ingress := &v1beta1.Ingress{
		ObjectMeta: v1.ObjectMeta{
			Namespace: ing.namespace,
			Name:      ing.name,
		},
		Spec: v1beta1.IngressSpec{
			Rules: []v1beta1.IngressRule{},
		},
		Status: v1beta1.IngressStatus{
			LoadBalancer: v1.LoadBalancerStatus{
				Ingress: []v1.LoadBalancerIngress{},
			},
		},
	}
	for _, dnsname := range ing.dnsnames {
		ingress.Spec.Rules = append(ingress.Spec.Rules, v1beta1.IngressRule{
			Host: dnsname,
		})
	}
	for _, ip := range ing.ips {
		ingress.Status.LoadBalancer.Ingress = append(ingress.Status.LoadBalancer.Ingress, v1.LoadBalancerIngress{
			IP: ip,
		})
	}
	for _, hostname := range ing.hostnames {
		ingress.Status.LoadBalancer.Ingress = append(ingress.Status.LoadBalancer.Ingress, v1.LoadBalancerIngress{
			Hostname: hostname,
		})
	}
	return ingress
}
<|endoftext|>


<commit_before>package gc

import (
	"strconv"
	"strings"
	"sync"
	"time"
)

var (
	PurgeHitResponse    = Empty(200)
	PurgeMissResponse   = Empty(204)
	NotModifiedResponse = Empty(304)

	hitHeaderValue = []string{"hit"}
	zero           time.Time
)

type CacheStorage interface {
	Get(primary, secondary string) CachedResponse
	Set(primary string, secondary string, response CachedResponse)
	Delete(primary, secondary string) bool
	DeleteAll(primary string) bool
}

type CachedResponse interface {
	Response
	Size() int
	Expire(at time.Time)
	Expires() time.Time
}

// A function that generates cache keys from a request
type CacheKeyLookup func(req *Request) (string, string)

// A function that purges the cache
// Returning a nil response means that the request will be forward onwards
type PurgeHandler func(req *Request, lookup CacheKeyLookup, cache CacheStorage) Response

func DefaultCacheKeyLookup(req *Request) (string, string) {
	return req.URL.Path, req.URL.RawQuery
}

type Cache struct {
	sync.Mutex
	downloads    map[string]time.Time
	Storage      CacheStorage
	Saint        bool
	GraceTTL     time.Duration
	PurgeHandler PurgeHandler
}

func NewCache() *Cache {
	return &Cache{
		downloads: make(map[string]time.Time),
	}
}

func (c *Cache) Set(primary string, secondary string, config *RouteCache, res Response) {
	ttl := c.ttl(config, res)
	if ttl == 0 {
		return
	}

	cacheable := res.ToCacheable(time.Now().Add(ttl))
	c.Storage.Set(primary, secondary, cacheable)
}

func (c *Cache) ttl(config *RouteCache, res Response) time.Duration {
	status := res.Status()
	if status >= 200 && status <= 400 && config.TTL > 0 {
		return config.TTL
	}

	cc := res.Header()["Cache-Control"]
	if len(cc) == 0 {
		return 0
	}

	for _, value := range cc {
		if strings.Contains(value, "private") {
			break
		}
		if index := strings.Index(value, "max-age="); index > -1 {
			if seconds, err := strconv.Atoi(value[index+8:]); err == nil {
				return time.Second * time.Duration(seconds)
			} else {
				Log.Warnf("invalid cache control header %q", value)
				break
			}
		}
	}
	return 0
}

// A clone is critical since the original request is likely to be closed
// before we're finishing with Grace and we might end up with a request
// that contains data from multiple sources.
func (c *Cache) Grace(primary string, secondary string, req *Request, next Middleware) {
	key := primary + secondary
	if c.reserveDownload(key) == false {
		return
	}
	go c.grace(key, primary, secondary, req.Clone(), next)
}

func (c *Cache) grace(key string, primary string, secondary string, req *Request, next Middleware) {
	defer func() {
		c.Lock()
		delete(c.downloads, key)
		c.Unlock()
	}()

	res := next(req)
	if res == nil {
		Log.Errorf("grace nil response for %q", req.URL)
		return
	}
	defer res.Close()
	if res.Status() >= 500 {
		Log.Errorf("grace error for %q", req.URL)
	} else {
		c.Set(primary, secondary, req.Route.Cache, res)
	}
}

func (c *Cache) reserveDownload(key string) bool {
	now := time.Now()
	c.Lock()
	defer c.Unlock()
	if expires, exists := c.downloads[key]; exists && expires.After(now) {
		return false
	}
	c.downloads[key] = now.Add(time.Second * 30)
	return true
}
<commit_msg>closed cloned grace requests<commit_after>package gc

import (
	"strconv"
	"strings"
	"sync"
	"time"
)

var (
	PurgeHitResponse    = Empty(200)
	PurgeMissResponse   = Empty(204)
	NotModifiedResponse = Empty(304)

	hitHeaderValue = []string{"hit"}
	zero           time.Time
)

type CacheStorage interface {
	Get(primary, secondary string) CachedResponse
	Set(primary string, secondary string, response CachedResponse)
	Delete(primary, secondary string) bool
	DeleteAll(primary string) bool
}

type CachedResponse interface {
	Response
	Size() int
	Expire(at time.Time)
	Expires() time.Time
}

// A function that generates cache keys from a request
type CacheKeyLookup func(req *Request) (string, string)

// A function that purges the cache
// Returning a nil response means that the request will be forward onwards
type PurgeHandler func(req *Request, lookup CacheKeyLookup, cache CacheStorage) Response

func DefaultCacheKeyLookup(req *Request) (string, string) {
	return req.URL.Path, req.URL.RawQuery
}

type Cache struct {
	sync.Mutex
	downloads    map[string]time.Time
	Storage      CacheStorage
	Saint        bool
	GraceTTL     time.Duration
	PurgeHandler PurgeHandler
}

func NewCache() *Cache {
	return &Cache{
		downloads: make(map[string]time.Time),
	}
}

func (c *Cache) Set(primary string, secondary string, config *RouteCache, res Response) {
	ttl := c.ttl(config, res)
	if ttl == 0 {
		return
	}

	cacheable := res.ToCacheable(time.Now().Add(ttl))
	c.Storage.Set(primary, secondary, cacheable)
}

func (c *Cache) ttl(config *RouteCache, res Response) time.Duration {
	status := res.Status()
	if status >= 200 && status <= 400 && config.TTL > 0 {
		return config.TTL
	}

	cc := res.Header()["Cache-Control"]
	if len(cc) == 0 {
		return 0
	}

	for _, value := range cc {
		if strings.Contains(value, "private") {
			break
		}
		if index := strings.Index(value, "max-age="); index > -1 {
			if seconds, err := strconv.Atoi(value[index+8:]); err == nil {
				return time.Second * time.Duration(seconds)
			} else {
				Log.Warnf("invalid cache control header %q", value)
				break
			}
		}
	}
	return 0
}

// A clone is critical since the original request is likely to be closed
// before we're finishing with Grace and we might end up with a request
// that contains data from multiple sources.
func (c *Cache) Grace(primary string, secondary string, req *Request, next Middleware) {
	key := primary + secondary
	if c.reserveDownload(key) == false {
		return
	}
	go c.grace(key, primary, secondary, req.Clone(), next)
}

func (c *Cache) grace(key string, primary string, secondary string, req *Request, next Middleware) {
	defer func() {
		req.Close()
		c.Lock()
		delete(c.downloads, key)
		c.Unlock()
	}()

	res := next(req)
	if res == nil {
		Log.Errorf("grace nil response for %q", req.URL)
		return
	}
	defer res.Close()
	if res.Status() >= 500 {
		Log.Errorf("grace error for %q", req.URL)
	} else {
		c.Set(primary, secondary, req.Route.Cache, res)
	}
}

func (c *Cache) reserveDownload(key string) bool {
	now := time.Now()
	c.Lock()
	defer c.Unlock()
	if expires, exists := c.downloads[key]; exists && expires.After(now) {
		return false
	}
	c.downloads[key] = now.Add(time.Second * 30)
	return true
}
<|endoftext|>


<commit_before>package ebase

import (
	"encoding/json"
	"fmt"
	"reflect"
	"strconv"
)

type KeyValue struct {
	Key   interface{}
	Value interface{}
}

type Generals map[string]interface{}

func NewSettings() Generals {
	return Generals{}
}

func (set Generals) Get(name string, value interface{}) (err error) {
	tmp, ok := set[name]
	if !ok {
		return fmt.Errorf("keys not found.[%s]", name)
	}

	var v interface{}

	tmpType := getTypeOf(tmp)
	valueType := getTypeOf(value)

	//
	if tmpType == valueType {
		refValue := reflect.Indirect(reflect.ValueOf(value))
		refValue.Set(reflect.Indirect(reflect.ValueOf(tmp)))
		return
	}

	if tmpType == "string" {
		switch valueType {
		case "struct", "map": // for struct and map, using json.Unmarshal
			return json.Unmarshal([]byte(tmp.(string)), value)
		case "int":
			v, err = strconv.Atoi(tmp.(string))
		case "string":
			v = tmp
		case "bool":
			var a int
			a, err = strconv.Atoi(tmp.(string))
			if err != nil {
				return
			}
			if a == 0 {
				v = false
			} else {
				v = true
			}

		default:
			err = fmt.Errorf("unspport value type[%s]", valueType)
		}
	} else {
		err = fmt.Errorf("unspport source type[%s]", tmpType)
	}

	// 给传递进来的参数赋值
	if err == nil {
		refValue := reflect.Indirect(reflect.ValueOf(value))
		refValue.Set(reflect.Indirect(reflect.ValueOf(v)))
	}

	return
}

func getTypeOf(val interface{}) (typeName string) {

	tp := reflect.TypeOf(val)
	typeName = tp.Kind().String()

	if typeName == "ptr" {
		typeName = tp.Elem().Kind().String()
	}

	return
}
<commit_msg>changed Generals to Map<commit_after>package ebase

import (
	"encoding/json"
	"fmt"
	"reflect"
	"strconv"
)

type KeyValue struct {
	Key   interface{}
	Value interface{}
}

type Map map[string]interface{}

func NewMap() Map {
	return Map{}
}

func (set Map) Get(name string, value interface{}) (err error) {
	tmp, ok := set[name]
	if !ok {
		return fmt.Errorf("keys not found.[%s]", name)
	}

	var v interface{}

	tmpType := getTypeOf(tmp)
	valueType := getTypeOf(value)

	//
	if tmpType == valueType {
		refValue := reflect.Indirect(reflect.ValueOf(value))
		refValue.Set(reflect.Indirect(reflect.ValueOf(tmp)))
		return
	}

	if tmpType == "string" {
		switch valueType {
		case "struct", "map": // for struct and map, using json.Unmarshal
			return json.Unmarshal([]byte(tmp.(string)), value)
		case "int":
			v, err = strconv.Atoi(tmp.(string))
		case "string":
			v = tmp
		case "bool":
			var a int
			a, err = strconv.Atoi(tmp.(string))
			if err != nil {
				return
			}
			if a == 0 {
				v = false
			} else {
				v = true
			}

		default:
			err = fmt.Errorf("unspport value type[%s]", valueType)
		}
	} else {
		err = fmt.Errorf("unspport source type[%s]", tmpType)
	}

	// 给传递进来的参数赋值
	if err == nil {
		refValue := reflect.Indirect(reflect.ValueOf(value))
		refValue.Set(reflect.Indirect(reflect.ValueOf(v)))
	}

	return
}

func getTypeOf(val interface{}) (typeName string) {

	tp := reflect.TypeOf(val)
	typeName = tp.Kind().String()

	if typeName == "ptr" {
		typeName = tp.Elem().Kind().String()
	}

	return
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"runtime"

	"github.com/mrunalp/ocitools/Godeps/_workspace/src/github.com/Sirupsen/logrus"
	"github.com/mrunalp/ocitools/Godeps/_workspace/src/github.com/codegangsta/cli"
	"github.com/mrunalp/ocitools/Godeps/_workspace/src/github.com/opencontainers/specs"
)

var generateCommand = cli.Command{
	Name:  "generate",
	Usage: "generate a OCI spec file",
	Action: func(context *cli.Context) {
		spec := specs.LinuxSpec{
			Spec: specs.Spec{
				Version: specs.Version,
				Platform: specs.Platform{
					OS:   runtime.GOOS,
					Arch: runtime.GOARCH,
				},
				Root: specs.Root{
					Path:     "rootfs",
					Readonly: true,
				},
				Process: specs.Process{
					Terminal: true,
					User:     specs.User{},
					Args: []string{
						"sh",
					},
					Env: []string{
						"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
						"TERM=xterm",
					},
				},
				Hostname: "shell",
				Mounts: []specs.MountPoint{
					{
						Name: "proc",
						Path: "/proc",
					},
					{
						Name: "dev",
						Path: "/dev",
					},
					{
						Name: "devpts",
						Path: "/dev/pts",
					},
					{
						Name: "shm",
						Path: "/dev/shm",
					},
					{
						Name: "mqueue",
						Path: "/dev/mqueue",
					},
					{
						Name: "sysfs",
						Path: "/sys",
					},
					{
						Name: "cgroup",
						Path: "/sys/fs/cgroup",
					},
				},
			},
			Linux: specs.Linux{
				Capabilities: []string{
					"AUDIT_WRITE",
					"KILL",
					"NET_BIND_SERVICE",
				},
			},
		}
		rspec := specs.LinuxRuntimeSpec{
			RuntimeSpec: specs.RuntimeSpec{
				Mounts: map[string]specs.Mount{
					"proc": {
						Type:    "proc",
						Source:  "proc",
						Options: nil,
					},
					"dev": {
						Type:    "tmpfs",
						Source:  "tmpfs",
						Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"},
					},
					"devpts": {
						Type:    "devpts",
						Source:  "devpts",
						Options: []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"},
					},
					"shm": {
						Type:    "tmpfs",
						Source:  "shm",
						Options: []string{"nosuid", "noexec", "nodev", "mode=1777", "size=65536k"},
					},
					"mqueue": {
						Type:    "mqueue",
						Source:  "mqueue",
						Options: []string{"nosuid", "noexec", "nodev"},
					},
					"sysfs": {
						Type:    "sysfs",
						Source:  "sysfs",
						Options: []string{"nosuid", "noexec", "nodev"},
					},
					"cgroup": {
						Type:    "cgroup",
						Source:  "cgroup",
						Options: []string{"nosuid", "noexec", "nodev", "relatime", "ro"},
					},
				},
			},
			Linux: specs.LinuxRuntime{
				Namespaces: []specs.Namespace{
					{
						Type: "pid",
					},
					{
						Type: "network",
					},
					{
						Type: "ipc",
					},
					{
						Type: "uts",
					},
					{
						Type: "mount",
					},
				},
				Rlimits: []specs.Rlimit{
					{
						Type: "RLIMIT_NOFILE",
						Hard: uint64(1024),
						Soft: uint64(1024),
					},
				},
				Devices: []specs.Device{
					{
						Type:        'c',
						Path:        "/dev/null",
						Major:       1,
						Minor:       3,
						Permissions: "rwm",
						FileMode:    0666,
						UID:         0,
						GID:         0,
					},
					{
						Type:        'c',
						Path:        "/dev/random",
						Major:       1,
						Minor:       8,
						Permissions: "rwm",
						FileMode:    0666,
						UID:         0,
						GID:         0,
					},
					{
						Type:        'c',
						Path:        "/dev/full",
						Major:       1,
						Minor:       7,
						Permissions: "rwm",
						FileMode:    0666,
						UID:         0,
						GID:         0,
					},
					{
						Type:        'c',
						Path:        "/dev/tty",
						Major:       5,
						Minor:       0,
						Permissions: "rwm",
						FileMode:    0666,
						UID:         0,
						GID:         0,
					},
					{
						Type:        'c',
						Path:        "/dev/zero",
						Major:       1,
						Minor:       5,
						Permissions: "rwm",
						FileMode:    0666,
						UID:         0,
						GID:         0,
					},
					{
						Type:        'c',
						Path:        "/dev/urandom",
						Major:       1,
						Minor:       9,
						Permissions: "rwm",
						FileMode:    0666,
						UID:         0,
						GID:         0,
					},
				},
				Resources: &specs.Resources{
					Memory: specs.Memory{
						Swappiness: -1,
					},
				},
				Seccomp: specs.Seccomp{
					DefaultAction: "SCMP_ACT_ALLOW",
					Syscalls:      []*specs.Syscall{},
				},
			},
		}
		checkNoFile := func(name string) error {
			_, err := os.Stat(name)
			if err == nil {
				return fmt.Errorf("File %s exists. Remove it first", name)
			}
			if !os.IsNotExist(err) {
				return err
			}
			return nil
		}
		cName := "config.json"
		rName := "runtime.json"
		if err := checkNoFile(cName); err != nil {
			logrus.Fatal(err)
		}
		if err := checkNoFile(rName); err != nil {
			logrus.Fatal(err)
		}
		data, err := json.MarshalIndent(&spec, "", "\t")
		if err != nil {
			logrus.Fatal(err)
		}
		if err := ioutil.WriteFile(cName, data, 0666); err != nil {
			logrus.Fatal(err)
		}
		rdata, err := json.MarshalIndent(&rspec, "", "\t")
		if err != nil {
			logrus.Fatal(err)
		}
		if err := ioutil.WriteFile(rName, rdata, 0666); err != nil {
			logrus.Fatal(err)
		}
	},
}
<commit_msg>Create default template and change some default settings<commit_after>package main

import (
	"encoding/json"
	"io/ioutil"
	"runtime"

	"github.com/mrunalp/ocitools/Godeps/_workspace/src/github.com/Sirupsen/logrus"
	"github.com/mrunalp/ocitools/Godeps/_workspace/src/github.com/codegangsta/cli"
	"github.com/mrunalp/ocitools/Godeps/_workspace/src/github.com/opencontainers/specs"
)

var generateCommand = cli.Command{
	Name:  "generate",
	Usage: "generate a OCI spec file",
	Action: func(context *cli.Context) {
		spec, rspec := getDefaultTemplate()
		cName := "config.json"
		rName := "runtime.json"
		data, err := json.MarshalIndent(&spec, "", "\t")
		if err != nil {
			logrus.Fatal(err)
		}
		if err := ioutil.WriteFile(cName, data, 0666); err != nil {
			logrus.Fatal(err)
		}
		rdata, err := json.MarshalIndent(&rspec, "", "\t")
		if err != nil {
			logrus.Fatal(err)
		}
		if err := ioutil.WriteFile(rName, rdata, 0666); err != nil {
			logrus.Fatal(err)
		}
	},
}

func getDefaultTemplate() (specs.LinuxSpec, specs.LinuxRuntimeSpec) {
	spec := specs.LinuxSpec{
		Spec: specs.Spec{
			Version: specs.Version,
			Platform: specs.Platform{
				OS:   runtime.GOOS,
				Arch: runtime.GOARCH,
			},
			Root: specs.Root{
				Path:     "",
				Readonly: false,
			},
			Process: specs.Process{
				Terminal: true,
				User:     specs.User{},
				Args: []string{
					"sh",
				},
				Env: []string{
					"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
					"TERM=xterm",
				},
			},
			Hostname: "shell",
			Mounts: []specs.MountPoint{
				{
					Name: "proc",
					Path: "/proc",
				},
				{
					Name: "dev",
					Path: "/dev",
				},
				{
					Name: "devpts",
					Path: "/dev/pts",
				},
				{
					Name: "shm",
					Path: "/dev/shm",
				},
				{
					Name: "mqueue",
					Path: "/dev/mqueue",
				},
				{
					Name: "sysfs",
					Path: "/sys",
				},
				{
					Name: "cgroup",
					Path: "/sys/fs/cgroup",
				},
			},
		},
		Linux: specs.Linux{
			Capabilities: []string{
				"AUDIT_WRITE",
				"KILL",
				"NET_BIND_SERVICE",
			},
		},
	}
	rspec := specs.LinuxRuntimeSpec{
		RuntimeSpec: specs.RuntimeSpec{
			Mounts: map[string]specs.Mount{
				"proc": {
					Type:    "proc",
					Source:  "proc",
					Options: nil,
				},
				"dev": {
					Type:    "tmpfs",
					Source:  "tmpfs",
					Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"},
				},
				"devpts": {
					Type:    "devpts",
					Source:  "devpts",
					Options: []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"},
				},
				"shm": {
					Type:    "tmpfs",
					Source:  "shm",
					Options: []string{"nosuid", "noexec", "nodev", "mode=1777", "size=65536k"},
				},
				"mqueue": {
					Type:    "mqueue",
					Source:  "mqueue",
					Options: []string{"nosuid", "noexec", "nodev"},
				},
				"sysfs": {
					Type:    "sysfs",
					Source:  "sysfs",
					Options: []string{"nosuid", "noexec", "nodev"},
				},
				"cgroup": {
					Type:    "cgroup",
					Source:  "cgroup",
					Options: []string{"nosuid", "noexec", "nodev", "relatime", "ro"},
				},
			},
		},
		Linux: specs.LinuxRuntime{
			Namespaces: []specs.Namespace{
				{
					Type: "pid",
				},
				{
					Type: "network",
				},
				{
					Type: "ipc",
				},
				{
					Type: "uts",
				},
				{
					Type: "mount",
				},
			},
			Rlimits: []specs.Rlimit{
				{
					Type: "RLIMIT_NOFILE",
					Hard: uint64(1024),
					Soft: uint64(1024),
				},
			},
			Devices: []specs.Device{
				{
					Type:        'c',
					Path:        "/dev/null",
					Major:       1,
					Minor:       3,
					Permissions: "rwm",
					FileMode:    0666,
					UID:         0,
					GID:         0,
				},
				{
					Type:        'c',
					Path:        "/dev/random",
					Major:       1,
					Minor:       8,
					Permissions: "rwm",
					FileMode:    0666,
					UID:         0,
					GID:         0,
				},
				{
					Type:        'c',
					Path:        "/dev/full",
					Major:       1,
					Minor:       7,
					Permissions: "rwm",
					FileMode:    0666,
					UID:         0,
					GID:         0,
				},
				{
					Type:        'c',
					Path:        "/dev/tty",
					Major:       5,
					Minor:       0,
					Permissions: "rwm",
					FileMode:    0666,
					UID:         0,
					GID:         0,
				},
				{
					Type:        'c',
					Path:        "/dev/zero",
					Major:       1,
					Minor:       5,
					Permissions: "rwm",
					FileMode:    0666,
					UID:         0,
					GID:         0,
				},
				{
					Type:        'c',
					Path:        "/dev/urandom",
					Major:       1,
					Minor:       9,
					Permissions: "rwm",
					FileMode:    0666,
					UID:         0,
					GID:         0,
				},
			},
			Resources: &specs.Resources{
				Memory: specs.Memory{
					Swappiness: -1,
				},
			},
			Seccomp: specs.Seccomp{
				DefaultAction: "SCMP_ACT_ALLOW",
				Syscalls:      []*specs.Syscall{},
			},
		},
	}
	return spec, rspec
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"io/ioutil"
	"os/exec"
	"path"
	"strconv"
	"strings"
	"text/template"
	"time"
)

var informationTemplate = `{{.Artist}}
{{.Date}}
{{.Album}}
{{.Tour}}

Lineage:

Notes:

This source is considered Source 1 for this date:
https://www.depechemode-live.com/wiki/{{wikiescape .Date}}_{{wikiescape .Album}}/Source_1

Track list:

{{range .Tracks}}{{.Prefix}}{{printf "%02d" .Index}} [{{.Duration}}] {{.Title}}{{if .HasAlternateLeadVocalist}} (*){{end}}
{{end}}Total time: {{.Duration}}

Torrent downloaded from https://www.depechemode-live.com
`

type AlbumData struct {
	Artist   string
	Date     string
	Album    string
	Tour     string
	Tracks   []TrackData
	Duration string
}

type TrackData struct {
	Title                    string
	Duration                 string
	HasAlternateLeadVocalist bool
	Prefix                   string
	Index                    int
}

func generateInformation(c *cli.Context) {
	fileInfo, filepath := checkFilepathArgument(c)
	if fileInfo == nil {
		return
	}

	tourName := c.String("tour")
	if tourName == "" {
		cli.ShowSubcommandHelp(c)
		return
	}

	mode := "batch"
	if c.GlobalBool("single") {
		mode = "single"
	}

	tourfile := c.String("tour-file")
	if tourfile != "" {
		fileInfo, tourfile = getFileOfType(tourfile, false, "tour-file")
		if fileInfo == nil {
			return
		}

		fmt.Println("Processing tours from:", tourfile)
	}

	fmt.Println("The current tour is:", tourName)
	fmt.Printf("The following filepath (%s mode) will be processed: %s\n", mode, filepath)
	notifyDeleteMode(c)

	if !shouldContinue(c) {
		return
	}

	tour := new(Tour)
	tour.Name = tourName
	if tourfile != "" { // tourFile is only for reading "alternate vocalists" into tracks map
		if err := getTourFromTourFile(tourfile, tour); err != nil {
			fmt.Println("[Error]", err)
			if !shouldContinue(c) {
				return
			}
		}
	}

	// Stupid windows
	informationTemplate = strings.Replace(informationTemplate, "\n", "\r\n", -1)

	if mode == "single" {
		generateFile(filepath, fileInfo.Name(), *tour, c.GlobalBool("delete"))
		return
	}

	files, _ := ioutil.ReadDir(filepath)
	for _, file := range files {
		if file.IsDir() {
			name := file.Name()
			generateFile(path.Join(filepath, name), name, *tour, c.GlobalBool("delete"))
		}
	}
}

func generateFile(filepath string, name string, tour Tour, deleteMode bool) {
	outputFilename := path.Join(filepath, name+".txt")
	if deleteMode {
		removeFile(outputFilename)
		return
	}

	album := new(AlbumData)
	album.Tour = tour.Name

	var duration int64 = 0 // duration incrementer for the album

	usesCDNames := 0
	folders := make([]string, 0)
	files := make([]string, 0)
	directoryContents, _ := ioutil.ReadDir(filepath)
	for _, fileinfo := range directoryContents {
		filename := fileinfo.Name()
		isDir := fileinfo.IsDir()
		if isDir {
			folders = append(folders, filename)
			if strings.HasPrefix(filename, "CD") {
				usesCDNames += 1
			}
		} else if (path.Ext(filename) == ".flac") && !isDir {
			files = append(files, filename)
		}
	}

	iterating := files
	if usesCDNames > 0 {

		if len(files) > 0 {
			// Contains extra files not in a specific CD
			// Do something!
		}

		// TODO: should we check subfolders inside
		// "CD1"?

		files := make([]string, 0)
		subfolders := make([]string, 0)
		for _, dirName := range folders {
			subdirectory, _ := ioutil.ReadDir(path.Join(filepath, dirName))
			for _, fileinfo := range subdirectory {
				subdirPath := path.Join(dirName, fileinfo.Name())
				if fileinfo.IsDir() {
					subfolders = append(subfolders, subdirPath)
				} else {
					files = append(files, subdirPath)
				}
			}
		}

		if len(subfolders) > 0 {
			fmt.Printf("Skipping! Filepath has depth=3 folders (%s)\n", filepath)
			return
		}

		iterating = files // set it to the new files

	}

	if len(folders) > usesCDNames {
		// Contains extra folders, do something!
		// There's probably a folder like "Bonus"
	}

	for _, file := range iterating {
		// if usesCDNames > 0 {
		// 	continue
		// }

		track := getTagsFromFile(path.Join(filepath, file), album, &duration)

		if tour.Tracks != nil {
			_, containsAlternateLeadVocalist := tour.Tracks[track.Title]
			track.HasAlternateLeadVocalist = containsAlternateLeadVocalist
		}

		if usesCDNames > 0 {
			track.Prefix = strings.TrimPrefix(path.Dir(file), "CD") + "."
		}

		// Finally, add the new track to the album
		album.Tracks = append(album.Tracks, track)
	}

	if len(album.Tracks) == 0 {
		fmt.Println("Could not create album - aborting creation of", outputFilename)
		return
	}

	format := "4:05" // minute:0second
	if duration >= 3600 {
		format = "15:04:05" // duration is longer than an hour
	}
	album.Duration = time.Unix(duration, 0).Format(format)

	funcMap := template.FuncMap{"wikiescape": wikiescape}
	t := template.Must(template.New("generate").Funcs(funcMap).Parse(informationTemplate))

	infoFile := createFile(outputFilename)
	defer infoFile.Close()
	err := t.Execute(infoFile, album)
	if err != nil {
		panic(err)
	}
}

// tags: http://age.hobba.nl/audio/tag_frame_reference.html
func getTagsFromFile(filepath string, album *AlbumData, albumDuration *int64) TrackData {
	args := []string{
		"--show-total-samples",
		"--show-sample-rate",
	}

	nonTagArgs := len(args)
	tags := []string{"TITLE"}

	getAlbumData := album.Artist == ""
	if getAlbumData {
		tags = append(tags,
			"ARTIST",
			"DATE",
			"ALBUM",
			"tracknumber",
		)
	}

	args = append(args, filepath)
	for _, tag := range tags {
		args = append(args, "--show-tag="+tag)
	}

	data, err := exec.Command(
		"metaflac",
		args[:]...,
	).Output()

	if err != nil {
		panic(err)
	}

	var track TrackData

	lines := strings.Split(string(data), "\r\n")
	if len(lines) != len(args) {
		panic(fmt.Sprintf("[invalid metaflac output] Expected %d lines, got %d", len(args), len(lines)-1))
		// todo, return a bool to delete this file
		// and say that the current file is being skipped
		// perhaps an --ignore flag to enable this feature
		// false by default, to make it cancel the whole procedure?
	}

	var samples, sampleRate int64
	for i, line := range lines {
		line = strings.TrimSpace(line)

		switch {
		case i <= 1:
			value, err := strconv.Atoi(line)
			if err != nil {
				panic(err)
			}

			if i == 0 {
				samples = int64(value)
			} else {
				sampleRate = int64(value)
			}
		case i < len(args)-1:
			tagName := tags[i-nonTagArgs]
			prefix := tagName + "="
			tagValue := ifTrimPrefix(line, prefix)

			switch tagName {
			case "TITLE":
				track.Title = tagValue
			case "ARTIST":
				album.Artist = tagValue
			case "DATE":
				album.Date = tagValue
			case "ALBUM":
				album.Album = ifTrimPrefix(tagValue, album.Date+" ")
			case "tracknumber":
				num, err := strconv.Atoi(tagValue)
				if err != nil {
					panic(err)
				}

				track.Index = num
			}
		}
	}
	duration := samples / sampleRate
	*albumDuration += duration
	track.Duration = time.Unix(duration, 0).Format("4:05")

	return track
}
<commit_msg>Fix tracknumber = 0 where tracknumber > 1<commit_after>package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"io/ioutil"
	"os/exec"
	"path"
	"strconv"
	"strings"
	"text/template"
	"time"
)

var informationTemplate = `{{.Artist}}
{{.Date}}
{{.Album}}
{{.Tour}}

Lineage:

Notes:

This source is considered Source 1 for this date:
https://www.depechemode-live.com/wiki/{{wikiescape .Date}}_{{wikiescape .Album}}/Source_1

Track list:

{{range .Tracks}}{{.Prefix}}{{printf "%02d" .Index}} [{{.Duration}}] {{.Title}}{{if .HasAlternateLeadVocalist}} (*){{end}}
{{end}}Total time: {{.Duration}}

Torrent downloaded from https://www.depechemode-live.com
`

type AlbumData struct {
	Artist   string
	Date     string
	Album    string
	Tour     string
	Tracks   []TrackData
	Duration string
}

type TrackData struct {
	Title                    string
	Duration                 string
	HasAlternateLeadVocalist bool
	Prefix                   string
	Index                    int
}

func generateInformation(c *cli.Context) {
	fileInfo, filepath := checkFilepathArgument(c)
	if fileInfo == nil {
		return
	}

	tourName := c.String("tour")
	if tourName == "" {
		cli.ShowSubcommandHelp(c)
		return
	}

	mode := "batch"
	if c.GlobalBool("single") {
		mode = "single"
	}

	tourfile := c.String("tour-file")
	if tourfile != "" {
		fileInfo, tourfile = getFileOfType(tourfile, false, "tour-file")
		if fileInfo == nil {
			return
		}

		fmt.Println("Processing tours from:", tourfile)
	}

	fmt.Println("The current tour is:", tourName)
	fmt.Printf("The following filepath (%s mode) will be processed: %s\n", mode, filepath)
	notifyDeleteMode(c)

	if !shouldContinue(c) {
		return
	}

	tour := new(Tour)
	tour.Name = tourName
	if tourfile != "" { // tourFile is only for reading "alternate vocalists" into tracks map
		if err := getTourFromTourFile(tourfile, tour); err != nil {
			fmt.Println("[Error]", err)
			if !shouldContinue(c) {
				return
			}
		}
	}

	// Stupid windows
	informationTemplate = strings.Replace(informationTemplate, "\n", "\r\n", -1)

	if mode == "single" {
		generateFile(filepath, fileInfo.Name(), *tour, c.GlobalBool("delete"))
		return
	}

	files, _ := ioutil.ReadDir(filepath)
	for _, file := range files {
		if file.IsDir() {
			name := file.Name()
			generateFile(path.Join(filepath, name), name, *tour, c.GlobalBool("delete"))
		}
	}
}

func generateFile(filepath string, name string, tour Tour, deleteMode bool) {
	outputFilename := path.Join(filepath, name+".txt")
	if deleteMode {
		removeFile(outputFilename)
		return
	}

	album := new(AlbumData)
	album.Tour = tour.Name

	var duration int64 = 0 // duration incrementer for the album

	usesCDNames := 0
	folders := make([]string, 0)
	files := make([]string, 0)
	directoryContents, _ := ioutil.ReadDir(filepath)
	for _, fileinfo := range directoryContents {
		filename := fileinfo.Name()
		isDir := fileinfo.IsDir()
		if isDir {
			folders = append(folders, filename)
			if strings.HasPrefix(filename, "CD") {
				usesCDNames += 1
			}
		} else if (path.Ext(filename) == ".flac") && !isDir {
			files = append(files, filename)
		}
	}

	iterating := files
	if usesCDNames > 0 {

		if len(files) > 0 {
			// Contains extra files not in a specific CD
			// Do something!
		}

		// TODO: should we check subfolders inside
		// "CD1"?

		files := make([]string, 0)
		subfolders := make([]string, 0)
		for _, dirName := range folders {
			subdirectory, _ := ioutil.ReadDir(path.Join(filepath, dirName))
			for _, fileinfo := range subdirectory {
				subdirPath := path.Join(dirName, fileinfo.Name())
				if fileinfo.IsDir() {
					subfolders = append(subfolders, subdirPath)
				} else {
					files = append(files, subdirPath)
				}
			}
		}

		if len(subfolders) > 0 {
			fmt.Printf("Skipping! Filepath has depth=3 folders (%s)\n", filepath)
			return
		}

		iterating = files // set it to the new files

	}

	if len(folders) > usesCDNames {
		// Contains extra folders, do something!
		// There's probably a folder like "Bonus"
	}

	for _, file := range iterating {
		// if usesCDNames > 0 {
		// 	continue
		// }

		track := getTagsFromFile(path.Join(filepath, file), album, &duration)

		if tour.Tracks != nil {
			_, containsAlternateLeadVocalist := tour.Tracks[track.Title]
			track.HasAlternateLeadVocalist = containsAlternateLeadVocalist
		}

		if usesCDNames > 0 {
			track.Prefix = strings.TrimPrefix(path.Dir(file), "CD") + "."
		}

		// Finally, add the new track to the album
		album.Tracks = append(album.Tracks, track)
	}

	if len(album.Tracks) == 0 {
		fmt.Println("Could not create album - aborting creation of", outputFilename)
		return
	}

	format := "4:05" // minute:0second
	if duration >= 3600 {
		format = "15:04:05" // duration is longer than an hour
	}
	album.Duration = time.Unix(duration, 0).Format(format)

	funcMap := template.FuncMap{"wikiescape": wikiescape}
	t := template.Must(template.New("generate").Funcs(funcMap).Parse(informationTemplate))

	infoFile := createFile(outputFilename)
	defer infoFile.Close()
	err := t.Execute(infoFile, album)
	if err != nil {
		panic(err)
	}
}

// tags: http://age.hobba.nl/audio/tag_frame_reference.html
func getTagsFromFile(filepath string, album *AlbumData, albumDuration *int64) TrackData {
	args := []string{
		"--show-total-samples",
		"--show-sample-rate",
	}

	nonTagArgs := len(args)
	tags := []string{"TITLE", "tracknumber"}

	getAlbumData := album.Artist == ""
	if getAlbumData {
		tags = append(tags,
			"ARTIST",
			"DATE",
			"ALBUM",
		)
	}

	args = append(args, filepath)
	for _, tag := range tags {
		args = append(args, "--show-tag="+tag)
	}

	data, err := exec.Command(
		"metaflac",
		args[:]...,
	).Output()

	if err != nil {
		panic(err)
	}

	var track TrackData

	lines := strings.Split(string(data), "\r\n")
	if len(lines) != len(args) {
		panic(fmt.Sprintf("[invalid metaflac output] Expected %d lines, got %d", len(args), len(lines)-1))
		// todo, return a bool to delete this file
		// and say that the current file is being skipped
		// perhaps an --ignore flag to enable this feature
		// false by default, to make it cancel the whole procedure?
	}

	var samples, sampleRate int64
	for i, line := range lines {
		line = strings.TrimSpace(line)

		switch {
		case i <= 1:
			value, err := strconv.Atoi(line)
			if err != nil {
				panic(err)
			}

			if i == 0 {
				samples = int64(value)
			} else {
				sampleRate = int64(value)
			}
		case i < len(args)-1:
			tagName := tags[i-nonTagArgs]
			prefix := tagName + "="
			tagValue := ifTrimPrefix(line, prefix)

			switch tagName {
			case "TITLE":
				track.Title = tagValue
			case "tracknumber":
				num, err := strconv.Atoi(tagValue)
				if err != nil {
					panic(err)
				}

				track.Index = num
			case "ARTIST":
				album.Artist = tagValue
			case "DATE":
				album.Date = tagValue
			case "ALBUM":
				album.Album = ifTrimPrefix(tagValue, album.Date+" ")
			}
		}
	}
	duration := samples / sampleRate
	*albumDuration += duration
	track.Duration = time.Unix(duration, 0).Format("4:05")

	return track
}
<|endoftext|>


<commit_before>/*
Package geom provides vectors and matrices, and their associated operations.

It follows the conventions used by GLSL, by using similar type names, and
most importantly, the same memory layout (i.e. column-major for matrices).

All types are pure values: there's no heap allocation, and no hidden data.

Matrices

Since they are pure values, there is no constructors, only literals. Be aware
that they are specified and stored in column-major order, just like GLSL.
So when writing literals, remember to use the transpose of the mathematical
notation. In other words:
    m := Mat3{
		{a, b, c},
		{d, e, f},
		{g, h, i},
    }
... corresponds to the following mathematical notation:
	⎡ a  d  g ⎤
	⎢ b  e  h ⎥
	⎣ c  f  i ⎦

The same inversion happens with indices: m[column][row] corresponds to the
mathematical indices (row,column).

Although all methods returns their result by value, they take their receiver and
parameters by reference, for efficiency. They are never modified.
*/
package geom
<commit_msg>Rewrite geom doc<commit_after>/*
Package geom provides vectors and matrices, and their associated operations.

All types defined in this package use the same memory layout than the
corresponding GLSL type. They are pure values (no hidden data).

The notation also tries to be as close to GLSL as possible: literals use the
same component order, function names are similar, and component access for
matrices is identical: m[2][3] means the same thing in Go than in GLSL.

Transformation Matrices

As is usual in GLSL, to transform a vector use left-multiplication by a
matrix:
	T := Translation4(10, 15, 2)
	v := Vec4{1, 2, 3}
	vTrans := T.Transform(v)

When writing literals, remember to use the transpose of the mathematical
notation. In other words the following mathematical notation:
	⎡ a11  a12  a13 ⎤
	⎢ a21  a22  a23 ⎥
	⎣ a31  a32  a33 ⎦
Translates to:
    m := Mat3{
		{a11, a21, a31},
		{a12, a22, a32},
		{a13, a23, a33},
    }

Note that the same inversion happens with indices: the last component
of the first column is written a31 in math but accessed with m[0][2] in Go
(and GLSL).

Finally, although all methods returns their result by value, they take their
receiver and parameters by reference, for efficiency. They are never modified.

Note: Some describes this convention as "column-major". This can be confusing,
because it depends on the meaning assigned to the indices of 2D arrays. E.g.
Wikipedia describes C as row-major, but in doing so assumes that arrays are
accessed with a[row][col]. OpenGL, defined as column-major, uses (inGLSL) the
exact same data structure and notation than C, but assumes arrays are accessed
with a[col][row]. What really matters is the underlying memory layout, and the
order of matrix-vector multiplication. Yet another way to describe the
situation is to say that vectors are treated as column-vectors.
*/
package geom
<|endoftext|>


<commit_before>package geojson

import (
	"errors"
	"fmt"
	"reflect"
)

const (
	INIT_GEOM_CAP = 10
)

type CoordType float64

// Function which will try convert interface{} to CoordType object.
// If conversion is not possible then raise Panic.
func Coord(obj interface{}) (ct CoordType) {
	switch num := obj.(type) {
	case float64:
		ct = CoordType(num)
		break
	case int:
		ct = CoordType(num)
		break
	case float32:
		ct = CoordType(num)
		break
	case int64:
		ct = CoordType(num)
		break
	default:
		panic(fmt.Sprintf("Error: Cannot parse object: '%v' type: '%v' to CoordType!", obj, reflect.TypeOf(obj)))
	}
	return
}

// Type to represent one coordinate (x,y).
// To simplify create new instance.
// c := Coordinate{x, y}
type Coordinate [2]CoordType

// Slice of coordinates.
// Simply creation:
// c := Coordinates{{1, 2}, {2,2}}
// or
// c := Coordinates{Coordinate{1, 2}, Coordinate{2,2}}
type Coordinates []Coordinate

// Representation of set of lines
type MultiLine []Coordinates

type Geometry interface {
	GetType() string
	AddGeometry(interface{}) error
	//GetGeometry() interface{}
}

//Point coordinates are in x, y order
//(easting, northing for projected coordinates,
//longitude, latitude for geographic coordinates)
//Out example:
//   { "type": "Point", "coordinates": [100.0, 0.0] }
type Point struct {
	Type        string     `json:"type" bson:"type"`
	Coordinates Coordinate `json:"coordinates" bson:"coordinates"`
	Crs         *CRS       `json:"crs,omitempty" bson:"crs,omitempty"`
}

// Add geometry to coordinates.
// New value will replace existing
func (t *Point) AddGeometry(g interface{}) error {
	if c, ok := g.(Coordinate); ok {
		t.Coordinates = c
	} else {
		return errors.New(fmt.Sprintf("AssertionError: %v to %v",
			g, "Coordinate"))
	}
	return nil
}

func (t Point) GetType() string {
	return t.Type
}

func (t Point) GetGeometry() interface{} {
	return t.Coordinates
}

//Factory function to create new object
func NewPoint(c Coordinate) *Point {
	return &Point{Type: "Point", Coordinates: c}
}

//Coordinates of a MultiPoint are an array of positions:
// Out example:
//    { "type": "MultiPoint",
//		"coordinates": [ [100.0, 0.0], [101.0, 1.0] ]
// 	  }
type MultiPoint struct {
	Type        string      `json:"type" bson:"type"`
	Coordinates Coordinates `json:"coordinates" bson:"coordinates"`
	Crs         *CRS        `json:"crs,omitempty" bson:"crs,omitempty"`
}

// Add geometry to MultiPoint, if paremeter will be append to coordinates
func (t *MultiPoint) AddGeometry(g interface{}) error {
	switch c := g.(type) {
	case Coordinate:
		t.AddCoordinates(c)
		break
	case Coordinates:
		t.AddCoordinates(c...)
		break
	default:
		return errors.New(fmt.Sprintf("AssertionError %v", g))
	}
	return nil
}

// Add new point to MultiPoint object
func (t *MultiPoint) AddCoordinates(p ...Coordinate) {
	t.Coordinates = append(t.Coordinates, p...)
}

func (t MultiPoint) GetType() string {
	return t.Type
}

func (t MultiPoint) GetGeometry() interface{} {
	return t.Coordinates
}

//Factory function to create new object
func NewMultiPoint(coordinates Coordinates) *MultiPoint {
	if coordinates == nil {
		coordinates = make(Coordinates, 0, INIT_GEOM_CAP)
	}
	return &MultiPoint{Type: "MultiPoint", Coordinates: coordinates}
}

//Coordinates of LineString are an array of positions
// Out example:
//    { "type": "LineString",
//      "coordinates": [ [100.0, 0.0], [101.0, 1.0] ]
//    }
type LineString struct {
	Type        string      `json:"type" bson:"type"`
	Coordinates Coordinates `json:"coordinates" bson:"coordinates"`
	Crs         *CRS        `json:"crs,omitempty" bson:"crs,omitempty"`
}

// Add new position to LineString
func (t *LineString) AddCoordinates(c ...Coordinate) {
	t.Coordinates = append(t.Coordinates, c...)
}

// Add new position to LineString
func (t *LineString) AddGeometry(g interface{}) error {
	switch c := g.(type) {
	case Coordinate:
		t.AddCoordinates(c)
		break
	case Coordinates:
		t.AddCoordinates(c...)
		break
	default:
		return errors.New(fmt.Sprintf("AssertionError %v", g))
	}
	return nil
}

func (t LineString) GetType() string {
	return t.Type
}

func (t LineString) GetGeometry() interface{} {
	return t.Coordinates
}

//Factory function to create new object with points
func NewLineString(coordinates Coordinates) *LineString {
	if coordinates == nil {
		coordinates = make(Coordinates, 0, INIT_GEOM_CAP)
	}
	return &LineString{Type: "LineString", Coordinates: coordinates}
}

// For type "MultiLineString", the "coordinates" member must be an array
// of LineString coordinate arrays.
// Out example:
//	 { "type": "MultiLineString",
//	  "coordinates": [
//	      [ [100.0, 0.0], [101.0, 1.0] ],
//	      [ [102.0, 2.0], [103.0, 3.0] ]
//	    ]
//	  }
type MultiLineString struct {
	Type        string    `json:"type" bson:"type"`
	Coordinates MultiLine `json:"coordinates" bson:"coordinates"`
	Crs         *CRS      `json:"crs,omitempty" bson:"crs,omitempty"`
}

// Add new line or line to MultiLineString
func (t *MultiLineString) AddGeometry(g interface{}) error {
	switch c := g.(type) {
	case MultiLine:
		t.AddCoordinates(c...)
		break
	case Coordinates:
		t.AddCoordinates(c)
		break
	default:
		return errors.New(fmt.Sprintf("AssertionError %v", g))
	}
	return nil
}

// Add collection of coordinates to MultiLineString
// new data are append
func (t *MultiLineString) AddCoordinates(coordinates ...Coordinates) {
	t.Coordinates = append(t.Coordinates, coordinates...)
}

func (t MultiLineString) GetType() string {
	return t.Type
}

func (t MultiLineString) GetGeometry() interface{} {
	return t.Coordinates
}

// Factory function for type MultiLineString
func NewMultiLineString(coordinates MultiLine) *MultiLineString {
	if coordinates == nil {
		coordinates = make(MultiLine, 0, INIT_GEOM_CAP)
	}
	return &MultiLineString{Type: "MultiLineString", Coordinates: coordinates}
}

// For type "Polygon", the "coordinates" member must be an array of LinearRing
// coordinate arrays. For Polygons with multiple rings, the first must be
// the exterior ring and any others must be interior rings or holes.
// Out example:
//{ "type": "Polygon",
//  "coordinates": [
//    				[ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0],
//					[100.0, 1.0], [100.0, 0.0] ]
//    ]
// }
type Polygon struct {
	Type        string    `json:"type" bson:"type"`
	Coordinates MultiLine `json:"coordinates,float" bson:"coordinates,float"`
	Crs         *CRS      `json:"crs,omitempty" bson:"crs,omitempty"`
}

// Add new polygon  or hole to Polygon
func (t *Polygon) AddGeometry(g interface{}) error {
	switch c := g.(type) {
	case MultiLine:
		t.AddCoordinates(c...)
		break
	case Coordinates:
		t.AddCoordinates(c)
		break
	default:
		return errors.New(fmt.Sprintf("AssertionError %v", g))
	}
	return nil
}

// add new polygon or hole.
// new values are append
func (t *Polygon) AddCoordinates(coordinates ...Coordinates) {
	t.Coordinates = append(t.Coordinates, coordinates...)
}

func (t Polygon) GetType() string {
	return t.Type
}

func (t Polygon) GetGeometry() interface{} {
	return t.Coordinates
}

// factory function
func NewPolygon(coordinates MultiLine) *Polygon {
	if coordinates == nil {
		coordinates = make(MultiLine, 0, INIT_GEOM_CAP)
	}
	return &Polygon{Type: "Polygon", Coordinates: coordinates}
}

// For type "MultiPolygon", the "coordinates" member must
// be an array of Polygon coordinate arrays.
// Out example
//{ "type": "MultiPolygon",
//  "coordinates": [
//    [[[102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0]]],
//    [[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]],
//     [[100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2]]]
//    ]
//  }
type MultiPolygon struct {
	Type        string      `json:"type" bson:"type"`
	Coordinates []MultiLine `json:"coordinates" bson:"coordinates"`
	Crs         *CRS        `json:"crs,omitempty" bson:"crs,omitempty"`
}

// add new polygon or hole.
// new values are append
func (t *MultiPolygon) AddCoordinates(lines ...MultiLine) {
	t.Coordinates = append(t.Coordinates, lines...)
}

// Add new polygon  or hole to Polygon
func (t *MultiPolygon) AddGeometry(g interface{}) error {
	switch c := g.(type) {
	case []MultiLine:
		t.AddCoordinates(c...)
		break
	case MultiLine:
		t.AddCoordinates(c)
		break
	default:
		return errors.New(fmt.Sprintf("AssertionError %v", g))
	}
	return nil
}

func (t MultiPolygon) GetType() string {
	return t.Type
}

func (t MultiPolygon) GetGeometry() interface{} {
	return t.Coordinates
}

// factory function
func NewMultiPolygon(coordinates []MultiLine) *MultiPolygon {
	if coordinates == nil {
		coordinates = make([]MultiLine, 0, INIT_GEOM_CAP)
	}
	return &MultiPolygon{Type: "MultiPolygon", Coordinates: coordinates}
}

// A GeoJSON object with type "GeometryCollection" is a geometry object
// which represents a collection of geometry objects.
// A geometry collection must have a member with the name
// "geometries". The value corresponding to "geometries" is an array.
// Each element in this array is a GeoJSON geometry object.
// Out example:
//{ "type": "GeometryCollection",
//  "geometries": [
//    { "type": "Point",
//      "coordinates": [100.0, 0.0]
//      },
//    { "type": "LineString",
//      "coordinates": [ [101.0, 0.0], [102.0, 1.0] ]
//      }
//  ]
//}
type GeometryCollection struct {
	Type       string        `json:"type" bson:"type"`
	Geometries []interface{} `json:"geometries" bson:"geometries"`
	Crs        *CRS          `json:"crs,omitempty" bson:"crs,omitempty"`
}

// new values are append
func (t *GeometryCollection) AddGeometries(g ...interface{}) {
	t.Geometries = append(t.Geometries, g...)
}

// Add new geometry  or hole to GeometryCollection
func (t *GeometryCollection) AddGeometry(g interface{}) error {
	switch c := g.(type) {
	case []interface{}:
		t.AddGeometries(c...)
		break
	case interface{}:
		t.AddGeometries(c)
		break
	default:
		return errors.New(fmt.Sprintf("AssertionError %v", g))
	}
	return nil
}

func (t GeometryCollection) GetType() string {
	return t.Type
}

// factory function
func NewGeometryCollection(g []interface{}) *GeometryCollection {
	if g == nil {
		g = make([]interface{}, 0, 10)
	}
	return &GeometryCollection{Type: "GeometryCollection", Geometries: g}
}
<commit_msg>removed unsupported bson flag<commit_after>package geojson

import (
	"errors"
	"fmt"
	"reflect"
)

const (
	INIT_GEOM_CAP = 10
)

type CoordType float64

// Function which will try convert interface{} to CoordType object.
// If conversion is not possible then raise Panic.
func Coord(obj interface{}) (ct CoordType) {
	switch num := obj.(type) {
	case float64:
		ct = CoordType(num)
		break
	case int:
		ct = CoordType(num)
		break
	case float32:
		ct = CoordType(num)
		break
	case int64:
		ct = CoordType(num)
		break
	default:
		panic(fmt.Sprintf("Error: Cannot parse object: '%v' type: '%v' to CoordType!", obj, reflect.TypeOf(obj)))
	}
	return
}

// Type to represent one coordinate (x,y).
// To simplify create new instance.
// c := Coordinate{x, y}
type Coordinate [2]CoordType

// Slice of coordinates.
// Simply creation:
// c := Coordinates{{1, 2}, {2,2}}
// or
// c := Coordinates{Coordinate{1, 2}, Coordinate{2,2}}
type Coordinates []Coordinate

// Representation of set of lines
type MultiLine []Coordinates

type Geometry interface {
	GetType() string
	AddGeometry(interface{}) error
	//GetGeometry() interface{}
}

//Point coordinates are in x, y order
//(easting, northing for projected coordinates,
//longitude, latitude for geographic coordinates)
//Out example:
//   { "type": "Point", "coordinates": [100.0, 0.0] }
type Point struct {
	Type        string     `json:"type" bson:"type"`
	Coordinates Coordinate `json:"coordinates" bson:"coordinates"`
	Crs         *CRS       `json:"crs,omitempty" bson:"crs,omitempty"`
}

// Add geometry to coordinates.
// New value will replace existing
func (t *Point) AddGeometry(g interface{}) error {
	if c, ok := g.(Coordinate); ok {
		t.Coordinates = c
	} else {
		return errors.New(fmt.Sprintf("AssertionError: %v to %v",
			g, "Coordinate"))
	}
	return nil
}

func (t Point) GetType() string {
	return t.Type
}

func (t Point) GetGeometry() interface{} {
	return t.Coordinates
}

//Factory function to create new object
func NewPoint(c Coordinate) *Point {
	return &Point{Type: "Point", Coordinates: c}
}

//Coordinates of a MultiPoint are an array of positions:
// Out example:
//    { "type": "MultiPoint",
//		"coordinates": [ [100.0, 0.0], [101.0, 1.0] ]
// 	  }
type MultiPoint struct {
	Type        string      `json:"type" bson:"type"`
	Coordinates Coordinates `json:"coordinates" bson:"coordinates"`
	Crs         *CRS        `json:"crs,omitempty" bson:"crs,omitempty"`
}

// Add geometry to MultiPoint, if paremeter will be append to coordinates
func (t *MultiPoint) AddGeometry(g interface{}) error {
	switch c := g.(type) {
	case Coordinate:
		t.AddCoordinates(c)
		break
	case Coordinates:
		t.AddCoordinates(c...)
		break
	default:
		return errors.New(fmt.Sprintf("AssertionError %v", g))
	}
	return nil
}

// Add new point to MultiPoint object
func (t *MultiPoint) AddCoordinates(p ...Coordinate) {
	t.Coordinates = append(t.Coordinates, p...)
}

func (t MultiPoint) GetType() string {
	return t.Type
}

func (t MultiPoint) GetGeometry() interface{} {
	return t.Coordinates
}

//Factory function to create new object
func NewMultiPoint(coordinates Coordinates) *MultiPoint {
	if coordinates == nil {
		coordinates = make(Coordinates, 0, INIT_GEOM_CAP)
	}
	return &MultiPoint{Type: "MultiPoint", Coordinates: coordinates}
}

//Coordinates of LineString are an array of positions
// Out example:
//    { "type": "LineString",
//      "coordinates": [ [100.0, 0.0], [101.0, 1.0] ]
//    }
type LineString struct {
	Type        string      `json:"type" bson:"type"`
	Coordinates Coordinates `json:"coordinates" bson:"coordinates"`
	Crs         *CRS        `json:"crs,omitempty" bson:"crs,omitempty"`
}

// Add new position to LineString
func (t *LineString) AddCoordinates(c ...Coordinate) {
	t.Coordinates = append(t.Coordinates, c...)
}

// Add new position to LineString
func (t *LineString) AddGeometry(g interface{}) error {
	switch c := g.(type) {
	case Coordinate:
		t.AddCoordinates(c)
		break
	case Coordinates:
		t.AddCoordinates(c...)
		break
	default:
		return errors.New(fmt.Sprintf("AssertionError %v", g))
	}
	return nil
}

func (t LineString) GetType() string {
	return t.Type
}

func (t LineString) GetGeometry() interface{} {
	return t.Coordinates
}

//Factory function to create new object with points
func NewLineString(coordinates Coordinates) *LineString {
	if coordinates == nil {
		coordinates = make(Coordinates, 0, INIT_GEOM_CAP)
	}
	return &LineString{Type: "LineString", Coordinates: coordinates}
}

// For type "MultiLineString", the "coordinates" member must be an array
// of LineString coordinate arrays.
// Out example:
//	 { "type": "MultiLineString",
//	  "coordinates": [
//	      [ [100.0, 0.0], [101.0, 1.0] ],
//	      [ [102.0, 2.0], [103.0, 3.0] ]
//	    ]
//	  }
type MultiLineString struct {
	Type        string    `json:"type" bson:"type"`
	Coordinates MultiLine `json:"coordinates" bson:"coordinates"`
	Crs         *CRS      `json:"crs,omitempty" bson:"crs,omitempty"`
}

// Add new line or line to MultiLineString
func (t *MultiLineString) AddGeometry(g interface{}) error {
	switch c := g.(type) {
	case MultiLine:
		t.AddCoordinates(c...)
		break
	case Coordinates:
		t.AddCoordinates(c)
		break
	default:
		return errors.New(fmt.Sprintf("AssertionError %v", g))
	}
	return nil
}

// Add collection of coordinates to MultiLineString
// new data are append
func (t *MultiLineString) AddCoordinates(coordinates ...Coordinates) {
	t.Coordinates = append(t.Coordinates, coordinates...)
}

func (t MultiLineString) GetType() string {
	return t.Type
}

func (t MultiLineString) GetGeometry() interface{} {
	return t.Coordinates
}

// Factory function for type MultiLineString
func NewMultiLineString(coordinates MultiLine) *MultiLineString {
	if coordinates == nil {
		coordinates = make(MultiLine, 0, INIT_GEOM_CAP)
	}
	return &MultiLineString{Type: "MultiLineString", Coordinates: coordinates}
}

// For type "Polygon", the "coordinates" member must be an array of LinearRing
// coordinate arrays. For Polygons with multiple rings, the first must be
// the exterior ring and any others must be interior rings or holes.
// Out example:
//{ "type": "Polygon",
//  "coordinates": [
//    				[ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0],
//					[100.0, 1.0], [100.0, 0.0] ]
//    ]
// }
type Polygon struct {
	Type        string    `json:"type" bson:"type"`
	Coordinates MultiLine `json:"coordinates,float" bson:"coordinates"`
	Crs         *CRS      `json:"crs,omitempty" bson:"crs,omitempty"`
}

// Add new polygon  or hole to Polygon
func (t *Polygon) AddGeometry(g interface{}) error {
	switch c := g.(type) {
	case MultiLine:
		t.AddCoordinates(c...)
		break
	case Coordinates:
		t.AddCoordinates(c)
		break
	default:
		return errors.New(fmt.Sprintf("AssertionError %v", g))
	}
	return nil
}

// add new polygon or hole.
// new values are append
func (t *Polygon) AddCoordinates(coordinates ...Coordinates) {
	t.Coordinates = append(t.Coordinates, coordinates...)
}

func (t Polygon) GetType() string {
	return t.Type
}

func (t Polygon) GetGeometry() interface{} {
	return t.Coordinates
}

// factory function
func NewPolygon(coordinates MultiLine) *Polygon {
	if coordinates == nil {
		coordinates = make(MultiLine, 0, INIT_GEOM_CAP)
	}
	return &Polygon{Type: "Polygon", Coordinates: coordinates}
}

// For type "MultiPolygon", the "coordinates" member must
// be an array of Polygon coordinate arrays.
// Out example
//{ "type": "MultiPolygon",
//  "coordinates": [
//    [[[102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0]]],
//    [[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]],
//     [[100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2]]]
//    ]
//  }
type MultiPolygon struct {
	Type        string      `json:"type" bson:"type"`
	Coordinates []MultiLine `json:"coordinates" bson:"coordinates"`
	Crs         *CRS        `json:"crs,omitempty" bson:"crs,omitempty"`
}

// add new polygon or hole.
// new values are append
func (t *MultiPolygon) AddCoordinates(lines ...MultiLine) {
	t.Coordinates = append(t.Coordinates, lines...)
}

// Add new polygon  or hole to Polygon
func (t *MultiPolygon) AddGeometry(g interface{}) error {
	switch c := g.(type) {
	case []MultiLine:
		t.AddCoordinates(c...)
		break
	case MultiLine:
		t.AddCoordinates(c)
		break
	default:
		return errors.New(fmt.Sprintf("AssertionError %v", g))
	}
	return nil
}

func (t MultiPolygon) GetType() string {
	return t.Type
}

func (t MultiPolygon) GetGeometry() interface{} {
	return t.Coordinates
}

// factory function
func NewMultiPolygon(coordinates []MultiLine) *MultiPolygon {
	if coordinates == nil {
		coordinates = make([]MultiLine, 0, INIT_GEOM_CAP)
	}
	return &MultiPolygon{Type: "MultiPolygon", Coordinates: coordinates}
}

// A GeoJSON object with type "GeometryCollection" is a geometry object
// which represents a collection of geometry objects.
// A geometry collection must have a member with the name
// "geometries". The value corresponding to "geometries" is an array.
// Each element in this array is a GeoJSON geometry object.
// Out example:
//{ "type": "GeometryCollection",
//  "geometries": [
//    { "type": "Point",
//      "coordinates": [100.0, 0.0]
//      },
//    { "type": "LineString",
//      "coordinates": [ [101.0, 0.0], [102.0, 1.0] ]
//      }
//  ]
//}
type GeometryCollection struct {
	Type       string        `json:"type" bson:"type"`
	Geometries []interface{} `json:"geometries" bson:"geometries"`
	Crs        *CRS          `json:"crs,omitempty" bson:"crs,omitempty"`
}

// new values are append
func (t *GeometryCollection) AddGeometries(g ...interface{}) {
	t.Geometries = append(t.Geometries, g...)
}

// Add new geometry  or hole to GeometryCollection
func (t *GeometryCollection) AddGeometry(g interface{}) error {
	switch c := g.(type) {
	case []interface{}:
		t.AddGeometries(c...)
		break
	case interface{}:
		t.AddGeometries(c)
		break
	default:
		return errors.New(fmt.Sprintf("AssertionError %v", g))
	}
	return nil
}

func (t GeometryCollection) GetType() string {
	return t.Type
}

// factory function
func NewGeometryCollection(g []interface{}) *GeometryCollection {
	if g == nil {
		g = make([]interface{}, 0, 10)
	}
	return &GeometryCollection{Type: "GeometryCollection", Geometries: g}
}
<|endoftext|>