<commit_before>package cwl

import (
	"fmt"
	//"github.com/davecgh/go-spew/spew"
	"github.com/MG-RAST/AWE/lib/shock"
	"github.com/davecgh/go-spew/spew"
	"github.com/mitchellh/mapstructure"
	"net/url"
	"strings"
)

// http://www.commonwl.org/v1.0/Workflow.html#File
type File struct {
	CWLType_Impl
	Id             string         `yaml:"id" json:"id"`
	Location       string         `yaml:"location" json:"location"` // An IRI that identifies the file resource.
	Path           string         `yaml:"path" json:"path"`         // dirname + '/' + basename == path This field must be set by the implementation.
	Basename       string         `yaml:"basename" json:"basename"` // dirname + '/' + basename == path // if not defined, take from location
	Dirname        string         `yaml:"dirname" json:"dirname"`   // dirname + '/' + basename == path
	Nameroot       string         `yaml:"nameroot" json:"nameroot"`
	Nameext        string         `yaml:"nameext" json:"nameext"`
	Checksum       string         `yaml:"checksum" json:"checksum"`
	Size           int32          `yaml:"size" json:"size"`
	SecondaryFiles []CWL_location `yaml:"secondaryFiles" json:"secondaryFiles"`
	Format         string         `yaml:"format" json:"format"`
	Contents       string         `yaml:"contents" json:"contents"`
	// Shock node
	Host   string
	Node   string
	Bearer string
	Token  string
}

func (f *File) GetClass() string    { return CWL_File }
func (f *File) GetId() string       { return f.Id }
func (f *File) SetId(id string)     { f.Id = id }
func (f *File) String() string      { return f.Path }
func (f *File) GetLocation() string { return f.Location } // for CWL_location
//func (f *File) Is_Array() bool      { return false }

func (f *File) Is_CommandInputParameterType() {} // for CommandInputParameterType

func NewFile(obj interface{}) (file File, err error) {

	file, err = MakeFile("unknown", obj)
	return
}

func MakeFile(id string, obj interface{}) (file File, err error) {
	file = File{}
	err = mapstructure.Decode(obj, &file)
	if err != nil {
		err = fmt.Errorf("(MakeFile) Could not convert File object %s", id)
		return
	}

	fmt.Println("MakeFile input:")
	spew.Dump(obj)
	fmt.Println("MakeFile output:")
	fmt.Printf("%+v\n", file)

	if file.Location != "" {

		// example shock://shock.metagenomics.anl.gov/node/92a76f64-d221-4947-9fd0-7106c3b9163a
		file_url, errx := url.Parse(file.Location)
		if errx != nil {
			err = fmt.Errorf("Error parsing url %s %s", file.Location, errx.Error())

			return
		}
		scheme := strings.ToLower(file_url.Scheme)
		values := file_url.Query()

		// TODO add "node/uuid" matching to detect shock

		switch scheme {

		case "http":
			_, has_download := values["download"]
			//extract filename ?
			if has_download {

				array := strings.Split(file_url.Path, "/")
				if len(array) != 3 {
					err = fmt.Errorf("shock url cannot be parsed")
					return
				}
				if array[1] != "node" {
					err = fmt.Errorf("node missing in shock url")
					return
				}
				file.Node = array[2]

				file.Host = "http://" + file_url.Host
				shock_client := shock.ShockClient{Host: file.Host} // TODO Token: datatoken
				node, xerr := shock_client.Get_node(file.Node)

				if xerr != nil {
					err = fmt.Errorf("(Get_node) Could not get shock node (%s, %s): %s", file.Host, file.Node, xerr.Error())
					return
				}
				//fmt.Println("---node:")
				//spew.Dump(node)

				if file.Basename == "" {
					// use filename from shocknode
					if node.File.Name != "" {
						fmt.Printf("node.File.Name: %s\n", node.File.Name)
						file.Basename = node.File.Name
					} else {
						// if user does not specify a filename and shock does not, use node_id
						file.Basename = file.Node
					}

				}
			}
		}
	}
	if file.Id == "" {
		file.Id = id
	}
	return
}

type Directory struct {
	Id       string         `yaml:"id"`
	Location string         `yaml:"location"`
	Path     string         `yaml:"path"`
	Basename string         `yaml:"basename"`
	Listing  []CWL_location `yaml:"basename"`
}

func (d Directory) GetClass() string    { return "Directory" }
func (d Directory) GetId() string       { return d.Id }
func (d Directory) String() string      { return d.Path }
func (d Directory) GetLocation() string { return d.Location } // for CWL_location

type FileArray struct {
	CWLType_Impl
	Id   string `yaml:"id" json:"id"`
	Data []File
}

func (f *FileArray) GetClass() string { return CWL_File_array }

//func (f *FileArray) Is_Array() bool   { return true }

func (f *FileArray) Is_CWL_array_type() {}
func (f *FileArray) Get_Array() *[]File {
	return &f.Data
}

func (f *FileArray) GetId() string   { return f.Id }
func (f *FileArray) SetId(id string) { f.Id = id }

//func (f *FileArray) String() string      { return f.Path }
//func (f *FileArray) GetLocation() string { return f.Location } // for CWL_location

func (s *FileArray) Is_CommandInputParameterType() {} // for CommandInputParameterType
<commit_msg>modified debug output<commit_after>package cwl

import (
	"fmt"
	//"github.com/davecgh/go-spew/spew"
	"github.com/MG-RAST/AWE/lib/shock"
	//"github.com/davecgh/go-spew/spew"
	"github.com/mitchellh/mapstructure"
	"net/url"
	"strings"
)

// http://www.commonwl.org/v1.0/Workflow.html#File
type File struct {
	CWLType_Impl
	Id             string         `yaml:"id" json:"id"`
	Location       string         `yaml:"location" json:"location"` // An IRI that identifies the file resource.
	Path           string         `yaml:"path" json:"path"`         // dirname + '/' + basename == path This field must be set by the implementation.
	Basename       string         `yaml:"basename" json:"basename"` // dirname + '/' + basename == path // if not defined, take from location
	Dirname        string         `yaml:"dirname" json:"dirname"`   // dirname + '/' + basename == path
	Nameroot       string         `yaml:"nameroot" json:"nameroot"`
	Nameext        string         `yaml:"nameext" json:"nameext"`
	Checksum       string         `yaml:"checksum" json:"checksum"`
	Size           int32          `yaml:"size" json:"size"`
	SecondaryFiles []CWL_location `yaml:"secondaryFiles" json:"secondaryFiles"`
	Format         string         `yaml:"format" json:"format"`
	Contents       string         `yaml:"contents" json:"contents"`
	// Shock node
	Host   string
	Node   string
	Bearer string
	Token  string
}

func (f *File) GetClass() string    { return CWL_File }
func (f *File) GetId() string       { return f.Id }
func (f *File) SetId(id string)     { f.Id = id }
func (f *File) String() string      { return f.Path }
func (f *File) GetLocation() string { return f.Location } // for CWL_location
//func (f *File) Is_Array() bool      { return false }

func (f *File) Is_CommandInputParameterType() {} // for CommandInputParameterType

func NewFile(obj interface{}) (file File, err error) {

	file, err = MakeFile("unknown", obj)
	return
}

func MakeFile(id string, obj interface{}) (file File, err error) {
	file = File{}
	err = mapstructure.Decode(obj, &file)
	if err != nil {
		err = fmt.Errorf("(MakeFile) Could not convert File object %s", id)
		return
	}

	//fmt.Println("MakeFile input:")
	//spew.Dump(obj)
	//fmt.Println("MakeFile output:")
	//fmt.Printf("%+v\n", file)

	if file.Location != "" {

		// example shock://shock.metagenomics.anl.gov/node/92a76f64-d221-4947-9fd0-7106c3b9163a
		file_url, errx := url.Parse(file.Location)
		if errx != nil {
			err = fmt.Errorf("Error parsing url %s %s", file.Location, errx.Error())

			return
		}
		scheme := strings.ToLower(file_url.Scheme)
		values := file_url.Query()

		// TODO add "node/uuid" matching to detect shock

		switch scheme {

		case "http":
			_, has_download := values["download"]
			//extract filename ?
			if has_download {

				array := strings.Split(file_url.Path, "/")
				if len(array) != 3 {
					err = fmt.Errorf("shock url cannot be parsed")
					return
				}
				if array[1] != "node" {
					err = fmt.Errorf("node missing in shock url")
					return
				}
				file.Node = array[2]

				file.Host = "http://" + file_url.Host
				shock_client := shock.ShockClient{Host: file.Host} // TODO Token: datatoken
				node, xerr := shock_client.Get_node(file.Node)

				if xerr != nil {
					err = fmt.Errorf("(Get_node) Could not get shock node (%s, %s): %s", file.Host, file.Node, xerr.Error())
					return
				}
				//fmt.Println("---node:")
				//spew.Dump(node)

				if file.Basename == "" {
					// use filename from shocknode
					if node.File.Name != "" {
						fmt.Printf("node.File.Name: %s\n", node.File.Name)
						file.Basename = node.File.Name
					} else {
						// if user does not specify a filename and shock does not, use node_id
						file.Basename = file.Node
					}

				}
			}
		}
	}
	if file.Id == "" {
		file.Id = id
	}
	return
}

type Directory struct {
	Id       string         `yaml:"id"`
	Location string         `yaml:"location"`
	Path     string         `yaml:"path"`
	Basename string         `yaml:"basename"`
	Listing  []CWL_location `yaml:"basename"`
}

func (d Directory) GetClass() string    { return "Directory" }
func (d Directory) GetId() string       { return d.Id }
func (d Directory) String() string      { return d.Path }
func (d Directory) GetLocation() string { return d.Location } // for CWL_location

type FileArray struct {
	CWLType_Impl
	Id   string `yaml:"id" json:"id"`
	Data []File
}

func (f *FileArray) GetClass() string { return CWL_File_array }

//func (f *FileArray) Is_Array() bool   { return true }

func (f *FileArray) Is_CWL_array_type() {}
func (f *FileArray) Get_Array() *[]File {
	return &f.Data
}

func (f *FileArray) GetId() string   { return f.Id }
func (f *FileArray) SetId(id string) { f.Id = id }

//func (f *FileArray) String() string      { return f.Path }
//func (f *FileArray) GetLocation() string { return f.Location } // for CWL_location

func (s *FileArray) Is_CommandInputParameterType() {} // for CommandInputParameterType
<|endoftext|>


<commit_before>package redius

import (
	"github.com/mediocregopher/radix.v2/pool"
	"github.com/mediocregopher/radix.v2/redis"
	"log"
)

type Redius struct {
	Addr     string
	Network  string
	Poolsize int
	Password string
	_pool    *pool.Pool
}

func (this *Redius) InitClient() {
	//log.Printf("%d rediu initializing from %s:%s", this.Poolsize, this.Network, this.Addr)

	var err error
	if "" == this.Password {
		this._pool, err = pool.New(this.Network, this.Addr, this.Poolsize)
	} else {
		df := func(network, addr string) (*redis.Client, error) {
			client, err := redis.Dial(network, addr)
			if err != nil {
				return nil, err
			}
			if err = client.Cmd("AUTH", this.Password).Err; err != nil {
				client.Close()
				return nil, err
			}
			return client, nil
		}

		this._pool, err = pool.NewCustom(this.Network, this.Addr, this.Poolsize, df)
	}

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

// SET key
func (this *Redius) SET(key string, val string) error {
	c, err := this._pool.Get()
	if err != nil {
		return err
	}
	err = c.Cmd("SET", key, val).Err
	this._pool.Put(c)
	return err
}

// SETEX key seconds val
func (this *Redius) SETEX(key string, secs int, val string) error {
	c, err := this._pool.Get()
	if err != nil {
		return err
	}
	err = c.Cmd("SETEX", key, secs, val).Err
	this._pool.Put(c)
	return err
}

// GET key
func (this *Redius) GET(key string) (val string, err error) {
	c, err := this._pool.Get()
	if err != nil {
		return
	}
	val, err = c.Cmd("GET", key).Str()
	this._pool.Put(c)
	return
}

func (this *Redius) MGET(keys []string) (val []string, err error) {
	c, err := this._pool.Get()
	if err != nil {
		return
	}
	r := c.Cmd("MGET", keys)
	if r.Err != nil {
		return nil, r.Err
	}
	val, err = r.List()
	this._pool.Put(c)
	return
}

// hset key field val
func (this *Redius) HSET(key, field string, val string) error {
	c, err := this._pool.Get()
	if err != nil {
		return err
	}
	err = c.Cmd("HSET", key, field, val).Err
	this._pool.Put(c)
	return err
}

// HSETNX key field val timeout
func (this *Redius) HSETNX(key, field string, val string) (err error) {
	c, err := this._pool.Get()
	if err != nil {
		return
	}
	err = c.Cmd("HSETNX", key, field, val).Err
	this._pool.Put(c)
	return
}

// HMSET key field val [field val ...]
func (this *Redius) HMSET(key string, val map[string]interface{}) (err error) {
	c, err := this._pool.Get()
	if err != nil {
		return
	}
	err = c.Cmd("HMSET", key, val).Err
	this._pool.Put(c)
	return
}

// HGET key field
func (this *Redius) HGET(key, field string) (val string, err error) {
	c, err := this._pool.Get()
	if err != nil {
		return
	}
	val, err = c.Cmd("HGET", key, field).Str()
	this._pool.Put(c)
	return
}

// HMGET key field [field ...]
func (this *Redius) HMGET(key string, fields []string) (val []string, err error) {
	c, err := this._pool.Get()
	if err != nil {
		return
	}
	val, err = c.Cmd("HMGET", key, fields).List()
	this._pool.Put(c)
	return
}

func (this *Redius) HGETALL(key string) (val []string, err error) {
	c, err := this._pool.Get()
	if err != nil {
		return
	}
	val, err = c.Cmd("HGETALL", key).List()
	this._pool.Put(c)
	return
}

// redis command:
// HDEL key field
func (this *Redius) HDEL(key, field string) error {
	c, err := this._pool.Get()
	if err != nil {
		return err
	}
	err = c.Cmd("HDEL", key, field).Err
	this._pool.Put(c)
	return err
}

// redis command:
// HDELALL key
func (this *Redius) HDELALL(key string) {
	c, err := this._pool.Get()
	if err != nil {
		return
	}
	_ = c.Cmd("DEL", key).Err
	this._pool.Put(c)
}

// redis zset command:
// ZADD key score val
func (this *Redius) ZADD(key string, score int, val string) error {
	c, err := this._pool.Get()
	if err != nil {
		return err
	}
	err = c.Cmd("ZADD", key, score, val).Err
	this._pool.Put(c)
	return err
}

func (this *Redius) ZADDINT(key string, score int, val int) error {
	c, err := this._pool.Get()
	if err != nil {
		return err
	}
	err = c.Cmd("ZADD", key, score, val).Err
	this._pool.Put(c)
	return err
}

// ZINCRBY key increment member
func (this *Redius) ZINCRBY(key string, increment int, val string) error {
	c, err := this._pool.Get()
	if err != nil {
		return err
	}
	err = c.Cmd("ZINCRBY", key, increment, val).Err
	this._pool.Put(c)
	return err
}

// ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]
func (this *Redius) ZREVRANGEBYSCORE(key string, start, step int, withsocres bool) (val []string, err error) {
	c, err := this._pool.Get()
	if err != nil {
		return nil, err
	}

	if withsocres {
		val, err = c.Cmd("ZREVRANGEBYSCORE", key, "+inf", "-inf", "WITHSCORES", "LIMIT", start, step).List()
	} else {
		val, err = c.Cmd("ZREVRANGEBYSCORE", key, "+inf", "-inf", "LIMIT", start, step).List()
	}

	this._pool.Put(c)
	return
}

// 返回有序集 key 中，所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。按 score 值递增(从小到大)次序排列。
// ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
func (this *Redius) ZRANGEBYSCORE(key string, min, max, start, step int, withsocres bool) (val []string, err error) {
	c, err := this._pool.Get()
	if err != nil {
		return nil, err
	}
	if withsocres {
		val, err = c.Cmd("ZRANGEBYSCORE", key, min, max, "WITHSCORES", "LIMIT", start, step).List()
	} else {
		val, err = c.Cmd("ZRANGEBYSCORE", key, min, max, "LIMIT", start, step).List()
	}
	this._pool.Put(c)
	return
}

// 删除
// ZREMRANGEBYSCORE key min max
func (this *Redius) ZREMRANGEBYSCORE(key string, min, max int) (err error) {
	c, err := this._pool.Get()
	if err != nil {
		return err
	}

	err = c.Cmd("ZREMRANGEBYSCORE", key, min, max).Err

	this._pool.Put(c)
	return err
}

// EXPIREAT key timestamp
func (this *Redius) EXPIREAT(key string, timestamp int) {
	c, err := this._pool.Get()
	if err != nil {
		return
	}
	_ = c.Cmd("EXPIREAT", key, timestamp).Err
	this._pool.Put(c)
}

func (this *Redius) KEYS(key string) (val []string, err error) {
	c, err := this._pool.Get()
	if err != nil {
		return nil, err
	}
	val, err = c.Cmd("KEYS", key).List()
	this._pool.Put(c)
	return
}
<commit_msg>[Add] add del key<commit_after>package redius

import (
	"github.com/mediocregopher/radix.v2/pool"
	"github.com/mediocregopher/radix.v2/redis"
	"log"
)

type Redius struct {
	Addr     string
	Network  string
	Poolsize int
	Password string
	_pool    *pool.Pool
}

func (this *Redius) InitClient() {
	//log.Printf("%d rediu initializing from %s:%s", this.Poolsize, this.Network, this.Addr)

	var err error
	if "" == this.Password {
		this._pool, err = pool.New(this.Network, this.Addr, this.Poolsize)
	} else {
		df := func(network, addr string) (*redis.Client, error) {
			client, err := redis.Dial(network, addr)
			if err != nil {
				return nil, err
			}
			if err = client.Cmd("AUTH", this.Password).Err; err != nil {
				client.Close()
				return nil, err
			}
			return client, nil
		}

		this._pool, err = pool.NewCustom(this.Network, this.Addr, this.Poolsize, df)
	}

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

// SET key
func (this *Redius) SET(key string, val string) error {
	c, err := this._pool.Get()
	if err != nil {
		return err
	}
	err = c.Cmd("SET", key, val).Err
	this._pool.Put(c)
	return err
}

// SETEX key seconds val
func (this *Redius) SETEX(key string, secs int, val string) error {
	c, err := this._pool.Get()
	if err != nil {
		return err
	}
	err = c.Cmd("SETEX", key, secs, val).Err
	this._pool.Put(c)
	return err
}

// GET key
func (this *Redius) GET(key string) (val string, err error) {
	c, err := this._pool.Get()
	if err != nil {
		return
	}
	val, err = c.Cmd("GET", key).Str()
	this._pool.Put(c)
	return
}

func (this *Redius) MGET(keys []string) (val []string, err error) {
	c, err := this._pool.Get()
	if err != nil {
		return
	}
	r := c.Cmd("MGET", keys)
	if r.Err != nil {
		return nil, r.Err
	}
	val, err = r.List()
	this._pool.Put(c)
	return
}

//
// hset key field val
func (this *Redius) HSET(key, field string, val string) error {
	c, err := this._pool.Get()
	if err != nil {
		return err
	}
	err = c.Cmd("HSET", key, field, val).Err
	this._pool.Put(c)
	return err
}

// HSETNX key field val timeout
func (this *Redius) HSETNX(key, field string, val string) (err error) {
	c, err := this._pool.Get()
	if err != nil {
		return
	}
	err = c.Cmd("HSETNX", key, field, val).Err
	this._pool.Put(c)
	return
}

// HMSET key field val [field val ...]
func (this *Redius) HMSET(key string, val map[string]interface{}) (err error) {
	c, err := this._pool.Get()
	if err != nil {
		return
	}
	err = c.Cmd("HMSET", key, val).Err
	this._pool.Put(c)
	return
}

// HGET key field
func (this *Redius) HGET(key, field string) (val string, err error) {
	c, err := this._pool.Get()
	if err != nil {
		return
	}
	val, err = c.Cmd("HGET", key, field).Str()
	this._pool.Put(c)
	return
}

// HMGET key field [field ...]
func (this *Redius) HMGET(key string, fields []string) (val []string, err error) {
	c, err := this._pool.Get()
	if err != nil {
		return
	}
	val, err = c.Cmd("HMGET", key, fields).List()
	this._pool.Put(c)
	return
}

func (this *Redius) HGETALL(key string) (val []string, err error) {
	c, err := this._pool.Get()
	if err != nil {
		return
	}
	val, err = c.Cmd("HGETALL", key).List()
	this._pool.Put(c)
	return
}

// redis command:
// HDEL key field
func (this *Redius) HDEL(key, field string) error {
	c, err := this._pool.Get()
	if err != nil {
		return err
	}
	err = c.Cmd("HDEL", key, field).Err
	this._pool.Put(c)
	return err
}

// redis command:
// HDELALL key
func (this *Redius) HDELALL(key string) {
	c, err := this._pool.Get()
	if err != nil {
		return
	}
	_ = c.Cmd("DEL", key).Err
	this._pool.Put(c)
}

// redis zset command:
// ZADD key score val
func (this *Redius) ZADD(key string, score int, val string) error {
	c, err := this._pool.Get()
	if err != nil {
		return err
	}
	err = c.Cmd("ZADD", key, score, val).Err
	this._pool.Put(c)
	return err
}

func (this *Redius) ZADDINT(key string, score int, val int) error {
	c, err := this._pool.Get()
	if err != nil {
		return err
	}
	err = c.Cmd("ZADD", key, score, val).Err
	this._pool.Put(c)
	return err
}

// ZINCRBY key increment member
func (this *Redius) ZINCRBY(key string, increment int, val string) error {
	c, err := this._pool.Get()
	if err != nil {
		return err
	}
	err = c.Cmd("ZINCRBY", key, increment, val).Err
	this._pool.Put(c)
	return err
}

// ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]
func (this *Redius) ZREVRANGEBYSCORE(key string, start, step int, withsocres bool) (val []string, err error) {
	c, err := this._pool.Get()
	if err != nil {
		return nil, err
	}

	if withsocres {
		val, err = c.Cmd("ZREVRANGEBYSCORE", key, "+inf", "-inf", "WITHSCORES", "LIMIT", start, step).List()
	} else {
		val, err = c.Cmd("ZREVRANGEBYSCORE", key, "+inf", "-inf", "LIMIT", start, step).List()
	}

	this._pool.Put(c)
	return
}

// 返回有序集 key 中，所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。按 score 值递增(从小到大)次序排列。
// ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
func (this *Redius) ZRANGEBYSCORE(key string, min, max, start, step int, withsocres bool) (val []string, err error) {
	c, err := this._pool.Get()
	if err != nil {
		return nil, err
	}
	if withsocres {
		val, err = c.Cmd("ZRANGEBYSCORE", key, min, max, "WITHSCORES", "LIMIT", start, step).List()
	} else {
		val, err = c.Cmd("ZRANGEBYSCORE", key, min, max, "LIMIT", start, step).List()
	}
	this._pool.Put(c)
	return
}

// 删除
// ZREMRANGEBYSCORE key min max
func (this *Redius) ZREMRANGEBYSCORE(key string, min, max int) (err error) {
	c, err := this._pool.Get()
	if err != nil {
		return err
	}

	err = c.Cmd("ZREMRANGEBYSCORE", key, min, max).Err

	this._pool.Put(c)
	return err
}

// KEY 操作
func (this *Redius) DEL(key string) (err error) {
	c, err := this._pool.Get()
	if err != nil {
		return err
	}
	err = c.Cmd("DEL", key).Err
	this._pool.Put(c)
	return err
}

func (this *Redius) DELALL(keys []string) (err error) {
	c, err := this._pool.Get()
	if err != nil {
		return err
	}
	err = c.Cmd("DEL", keys).Err
	this._pool.Put(c)
	return err
}

// EXPIREAT key timestamp
func (this *Redius) EXPIREAT(key string, timestamp int) {
	c, err := this._pool.Get()
	if err != nil {
		return
	}
	_ = c.Cmd("EXPIREAT", key, timestamp).Err
	this._pool.Put(c)
}

func (this *Redius) KEYS(key string) (val []string, err error) {
	c, err := this._pool.Get()
	if err != nil {
		return nil, err
	}
	val, err = c.Cmd("KEYS", key).List()
	this._pool.Put(c)
	return
}
<|endoftext|>


<commit_before>// Copyright (c) 2017, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package main

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/importer"
	"go/parser"
	"go/printer"
	"go/token"
	"go/types"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/mvdan/sh/interp"
	"github.com/mvdan/sh/syntax"
)

var (
	rawPrinter = printer.Config{Mode: printer.RawFormat}

	fastTest = false
)

type reducer struct {
	tdir      string
	logOut    io.Writer
	matchRe   *regexp.Regexp
	shellProg *syntax.File

	fset     *token.FileSet
	origFset *token.FileSet
	pkg      *ast.Package
	files    []*ast.File
	file     *ast.File

	tconf types.Config
	info  *types.Info

	useIdents map[types.Object][]*ast.Ident
	revDefs   map[types.Object]*ast.Ident
	parents   map[ast.Node]ast.Node

	dstBuf *bytes.Buffer

	tmpFiles map[*ast.File]*os.File

	tries     int
	didChange bool

	deleteKeepUnderscore func()
	deleteKeepUnchanged  func()

	tried map[string]bool

	walker
}

var errNoReduction = fmt.Errorf("could not reduce program")

func reduce(dir, match string, logOut io.Writer, shellStr string) error {
	r := &reducer{
		tdir:   dir,
		logOut: logOut,
		tried:  make(map[string]bool, 16),
		dstBuf: bytes.NewBuffer(nil),
	}
	var err error
	if r.tdir, err = ioutil.TempDir("", "goreduce"); err != nil {
		return err
	}
	defer os.RemoveAll(r.tdir)
	if r.matchRe, err = regexp.Compile(match); err != nil {
		return err
	}
	r.fset = token.NewFileSet()
	pkgs, err := parser.ParseDir(r.fset, dir, nil, parser.ParseComments)
	if err != nil {
		return err
	}
	if len(pkgs) != 1 {
		return fmt.Errorf("expected 1 package, got %d", len(pkgs))
	}
	for _, pkg := range pkgs {
		r.pkg = pkg
	}
	switch {
	case shellStr != "":
	case r.pkg.Name == "main":
		shellStr = shellStrRun
	default:
		shellStr = shellStrBuild
	}
	r.shellProg, err = syntax.NewParser().Parse(strings.NewReader(shellStr), "")
	if err != nil {
		return err
	}
	r.origFset = token.NewFileSet()
	parser.ParseDir(r.origFset, dir, nil, 0)

	var restoreMain func()
	r.tmpFiles = make(map[*ast.File]*os.File, len(r.pkg.Files))
	for fpath, file := range r.pkg.Files {
		r.files = append(r.files, file)
		tfname := filepath.Join(r.tdir, filepath.Base(fpath))
		f, err := os.Create(tfname)
		if err != nil {
			return err
		}
		if err := rawPrinter.Fprint(f, r.fset, file); err != nil {
			return err
		}
		r.tmpFiles[file] = f
		defer f.Close()
	}
	r.tconf.Importer = importer.Default()
	r.tconf.Error = func(err error) {
		if terr, ok := err.(types.Error); ok && terr.Soft {
			// don't stop type-checking on soft errors
			return
		}
		//panic("types.Check should not error here: " + err.Error())
	}
	// Check that the output matches before we apply any changes
	if !fastTest {
		if err := r.checkRun(); err != nil {
			return err
		}
	}
	r.fillParents()
	if anyChanges := r.reduceLoop(); !anyChanges {
		return errNoReduction
	}
	if restoreMain != nil {
		restoreMain()
	}
	for astFile := range r.tmpFiles {
		astFile.Name.Name = r.pkg.Name
		fname := r.fset.Position(astFile.Pos()).Filename
		f, err := os.Create(fname)
		if err != nil {
			return err
		}
		if err := printer.Fprint(f, r.fset, astFile); err != nil {
			return err
		}
		if err := f.Close(); err != nil {
			return err
		}
	}
	return nil
}

func (r *reducer) logChange(node ast.Node, format string, a ...interface{}) {
	if *verbose {
		pos := r.origFset.Position(node.Pos())
		times := "first try"
		if r.tries != 1 {
			times = fmt.Sprintf("%d tries", r.tries)
		}
		fmt.Fprintf(r.logOut, "%s:%d: %s (%s)\n",
			pos.Filename, pos.Line, fmt.Sprintf(format, a...), times)
	}
	r.tries = 0
}

func (r *reducer) checkRun() error {
	out := r.runCmd()
	if out == nil {
		return fmt.Errorf("expected an error to occur")
	}
	if !r.matchRe.Match(out) {
		return fmt.Errorf("error does not match:\n%s", string(out))
	}
	return nil
}

func (r *reducer) okChangeNoUndo() bool {
	if r.didChange {
		return false
	}
	r.dstBuf.Reset()
	if err := rawPrinter.Fprint(r.dstBuf, r.fset, r.file); err != nil {
		return false
	}
	newSrc := r.dstBuf.String()
	if r.tried[newSrc] {
		return false
	}
	r.tries++
	r.tried[newSrc] = true
	f := r.tmpFiles[r.file]
	if err := f.Truncate(0); err != nil {
		return false
	}
	if _, err := f.Seek(0, io.SeekStart); err != nil {
		return false
	}
	if _, err := f.Write(r.dstBuf.Bytes()); err != nil {
		return false
	}
	if err := r.checkRun(); err != nil {
		return false
	}
	// Reduction worked
	r.didChange = true
	return true
}

func (r *reducer) okChange() bool {
	if r.okChangeNoUndo() {
		r.deleteKeepUnderscore = nil
		r.deleteKeepUnchanged = nil
		return true
	}
	if r.deleteKeepUnderscore != nil {
		r.deleteKeepUnderscore()
		r.deleteKeepUnderscore = nil
		return r.okChange()
	}
	if r.deleteKeepUnchanged != nil {
		r.deleteKeepUnchanged()
		r.deleteKeepUnchanged = nil
	}
	return false
}

func (r *reducer) reduceLoop() (anyChanges bool) {
	r.info = &types.Info{
		Defs: make(map[*ast.Ident]types.Object),
		Uses: make(map[*ast.Ident]types.Object),
	}
	for {
		// Update type info after the AST changes
		r.tconf.Check(r.tdir, r.fset, r.files, r.info)
		r.fillObjs()

		r.didChange = false
		r.walk(r.pkg, r.reduceNode)
		if !r.didChange {
			if *verbose {
				fmt.Fprintf(r.logOut, "gave up after %d final tries\n", r.tries)
			}
			return
		}
		anyChanges = true
	}
}

func (r *reducer) fillObjs() {
	r.revDefs = make(map[types.Object]*ast.Ident, len(r.info.Defs))
	for id, obj := range r.info.Defs {
		if obj == nil {
			continue
		}
		r.revDefs[obj] = id
	}
	r.useIdents = make(map[types.Object][]*ast.Ident, len(r.info.Uses)/2)
	for id, obj := range r.info.Uses {
		if pkg := obj.Pkg(); pkg == nil || pkg.Name() != r.pkg.Name {
			// builtin or declared outside of our pkg
			continue
		}
		r.useIdents[obj] = append(r.useIdents[obj], id)
	}
}

func (r *reducer) fillParents() {
	r.parents = make(map[ast.Node]ast.Node)
	stack := make([]ast.Node, 1, 32)
	ast.Inspect(r.pkg, func(node ast.Node) bool {
		if node == nil {
			stack = stack[:len(stack)-1]
			return true
		}
		r.parents[node] = stack[len(stack)-1]
		stack = append(stack, node)
		return true
	})
}

func (r *reducer) runCmd() []byte {
	var buf bytes.Buffer
	runner := interp.Runner{
		Dir:    r.tdir,
		Stdout: &buf,
		Stderr: &buf,
	}
	runner.Reset()
	runner.Run(r.shellProg)
	return buf.Bytes()
}
<commit_msg>Adapt to sh being moved to mvdan.cc/sh<commit_after>// Copyright (c) 2017, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package main

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/importer"
	"go/parser"
	"go/printer"
	"go/token"
	"go/types"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"mvdan.cc/sh/interp"
	"mvdan.cc/sh/syntax"
)

var (
	rawPrinter = printer.Config{Mode: printer.RawFormat}

	fastTest = false
)

type reducer struct {
	tdir      string
	logOut    io.Writer
	matchRe   *regexp.Regexp
	shellProg *syntax.File

	fset     *token.FileSet
	origFset *token.FileSet
	pkg      *ast.Package
	files    []*ast.File
	file     *ast.File

	tconf types.Config
	info  *types.Info

	useIdents map[types.Object][]*ast.Ident
	revDefs   map[types.Object]*ast.Ident
	parents   map[ast.Node]ast.Node

	dstBuf *bytes.Buffer

	tmpFiles map[*ast.File]*os.File

	tries     int
	didChange bool

	deleteKeepUnderscore func()
	deleteKeepUnchanged  func()

	tried map[string]bool

	walker
}

var errNoReduction = fmt.Errorf("could not reduce program")

func reduce(dir, match string, logOut io.Writer, shellStr string) error {
	r := &reducer{
		tdir:   dir,
		logOut: logOut,
		tried:  make(map[string]bool, 16),
		dstBuf: bytes.NewBuffer(nil),
	}
	var err error
	if r.tdir, err = ioutil.TempDir("", "goreduce"); err != nil {
		return err
	}
	defer os.RemoveAll(r.tdir)
	if r.matchRe, err = regexp.Compile(match); err != nil {
		return err
	}
	r.fset = token.NewFileSet()
	pkgs, err := parser.ParseDir(r.fset, dir, nil, parser.ParseComments)
	if err != nil {
		return err
	}
	if len(pkgs) != 1 {
		return fmt.Errorf("expected 1 package, got %d", len(pkgs))
	}
	for _, pkg := range pkgs {
		r.pkg = pkg
	}
	switch {
	case shellStr != "":
	case r.pkg.Name == "main":
		shellStr = shellStrRun
	default:
		shellStr = shellStrBuild
	}
	r.shellProg, err = syntax.NewParser().Parse(strings.NewReader(shellStr), "")
	if err != nil {
		return err
	}
	r.origFset = token.NewFileSet()
	parser.ParseDir(r.origFset, dir, nil, 0)

	var restoreMain func()
	r.tmpFiles = make(map[*ast.File]*os.File, len(r.pkg.Files))
	for fpath, file := range r.pkg.Files {
		r.files = append(r.files, file)
		tfname := filepath.Join(r.tdir, filepath.Base(fpath))
		f, err := os.Create(tfname)
		if err != nil {
			return err
		}
		if err := rawPrinter.Fprint(f, r.fset, file); err != nil {
			return err
		}
		r.tmpFiles[file] = f
		defer f.Close()
	}
	r.tconf.Importer = importer.Default()
	r.tconf.Error = func(err error) {
		if terr, ok := err.(types.Error); ok && terr.Soft {
			// don't stop type-checking on soft errors
			return
		}
		//panic("types.Check should not error here: " + err.Error())
	}
	// Check that the output matches before we apply any changes
	if !fastTest {
		if err := r.checkRun(); err != nil {
			return err
		}
	}
	r.fillParents()
	if anyChanges := r.reduceLoop(); !anyChanges {
		return errNoReduction
	}
	if restoreMain != nil {
		restoreMain()
	}
	for astFile := range r.tmpFiles {
		astFile.Name.Name = r.pkg.Name
		fname := r.fset.Position(astFile.Pos()).Filename
		f, err := os.Create(fname)
		if err != nil {
			return err
		}
		if err := printer.Fprint(f, r.fset, astFile); err != nil {
			return err
		}
		if err := f.Close(); err != nil {
			return err
		}
	}
	return nil
}

func (r *reducer) logChange(node ast.Node, format string, a ...interface{}) {
	if *verbose {
		pos := r.origFset.Position(node.Pos())
		times := "first try"
		if r.tries != 1 {
			times = fmt.Sprintf("%d tries", r.tries)
		}
		fmt.Fprintf(r.logOut, "%s:%d: %s (%s)\n",
			pos.Filename, pos.Line, fmt.Sprintf(format, a...), times)
	}
	r.tries = 0
}

func (r *reducer) checkRun() error {
	out := r.runCmd()
	if out == nil {
		return fmt.Errorf("expected an error to occur")
	}
	if !r.matchRe.Match(out) {
		return fmt.Errorf("error does not match:\n%s", string(out))
	}
	return nil
}

func (r *reducer) okChangeNoUndo() bool {
	if r.didChange {
		return false
	}
	r.dstBuf.Reset()
	if err := rawPrinter.Fprint(r.dstBuf, r.fset, r.file); err != nil {
		return false
	}
	newSrc := r.dstBuf.String()
	if r.tried[newSrc] {
		return false
	}
	r.tries++
	r.tried[newSrc] = true
	f := r.tmpFiles[r.file]
	if err := f.Truncate(0); err != nil {
		return false
	}
	if _, err := f.Seek(0, io.SeekStart); err != nil {
		return false
	}
	if _, err := f.Write(r.dstBuf.Bytes()); err != nil {
		return false
	}
	if err := r.checkRun(); err != nil {
		return false
	}
	// Reduction worked
	r.didChange = true
	return true
}

func (r *reducer) okChange() bool {
	if r.okChangeNoUndo() {
		r.deleteKeepUnderscore = nil
		r.deleteKeepUnchanged = nil
		return true
	}
	if r.deleteKeepUnderscore != nil {
		r.deleteKeepUnderscore()
		r.deleteKeepUnderscore = nil
		return r.okChange()
	}
	if r.deleteKeepUnchanged != nil {
		r.deleteKeepUnchanged()
		r.deleteKeepUnchanged = nil
	}
	return false
}

func (r *reducer) reduceLoop() (anyChanges bool) {
	r.info = &types.Info{
		Defs: make(map[*ast.Ident]types.Object),
		Uses: make(map[*ast.Ident]types.Object),
	}
	for {
		// Update type info after the AST changes
		r.tconf.Check(r.tdir, r.fset, r.files, r.info)
		r.fillObjs()

		r.didChange = false
		r.walk(r.pkg, r.reduceNode)
		if !r.didChange {
			if *verbose {
				fmt.Fprintf(r.logOut, "gave up after %d final tries\n", r.tries)
			}
			return
		}
		anyChanges = true
	}
}

func (r *reducer) fillObjs() {
	r.revDefs = make(map[types.Object]*ast.Ident, len(r.info.Defs))
	for id, obj := range r.info.Defs {
		if obj == nil {
			continue
		}
		r.revDefs[obj] = id
	}
	r.useIdents = make(map[types.Object][]*ast.Ident, len(r.info.Uses)/2)
	for id, obj := range r.info.Uses {
		if pkg := obj.Pkg(); pkg == nil || pkg.Name() != r.pkg.Name {
			// builtin or declared outside of our pkg
			continue
		}
		r.useIdents[obj] = append(r.useIdents[obj], id)
	}
}

func (r *reducer) fillParents() {
	r.parents = make(map[ast.Node]ast.Node)
	stack := make([]ast.Node, 1, 32)
	ast.Inspect(r.pkg, func(node ast.Node) bool {
		if node == nil {
			stack = stack[:len(stack)-1]
			return true
		}
		r.parents[node] = stack[len(stack)-1]
		stack = append(stack, node)
		return true
	})
}

func (r *reducer) runCmd() []byte {
	var buf bytes.Buffer
	runner := interp.Runner{
		Dir:    r.tdir,
		Stdout: &buf,
		Stderr: &buf,
	}
	runner.Reset()
	runner.Run(r.shellProg)
	return buf.Bytes()
}
<|endoftext|>


<commit_before>package bimg

/*
#cgo pkg-config: vips
#include "vips/vips.h"
*/
import "C"

import (
	"errors"
	"math"
)

func Resize(buf []byte, o Options) ([]byte, error) {
	defer C.vips_thread_shutdown()

	image, imageType, err := vipsRead(buf)
	if err != nil {
		return nil, err
	}

	// defaults
	if o.Quality == 0 {
		o.Quality = QUALITY
	}
	if o.Compression == 0 {
		o.Compression = 6
	}
	if o.Type == 0 {
		o.Type = imageType
	}

	if IsTypeSupported(o.Type) == false {
		return nil, errors.New("Unsupported image output type")
	}

	// get WxH
	inWidth := int(image.Xsize)
	inHeight := int(image.Ysize)

	// image calculations
	factor := imageCalculations(o, inWidth, inHeight)
	shrink := int(math.Max(math.Floor(factor), 1))
	residual := float64(shrink) / factor

	// Do not enlarge the output if the input width *or* height are already less than the required dimensions
	if o.Enlarge == false {
		if inWidth < o.Width && inHeight < o.Height {
			factor = 1
			shrink = 1
			residual = 0
			o.Width = inWidth
			o.Height = inHeight
		}
	}

	// Try to use libjpeg shrink-on-load
	if imageType == JPEG && shrink >= 2 {
		// Recalculate integral shrink and double residual
		tmpImage, factor, err := shrinkJpegImage(buf, factor, shrink)
		if err != nil {
			return nil, err
		}
		if tmpImage != nil {
			image = tmpImage
			factor = math.Max(factor, 1.0)
			shrink = int(math.Floor(factor))
			residual = float64(shrink) / factor
		}
	}

	// Calculate integral box shrink
	windowSize := vipsWindowSize(o.Interpolator.String())
	if factor >= 2 && windowSize > 3 {
		// Shrink less, affine more with interpolators that use at least 4x4 pixel window, e.g. bicubic
		shrink = int(math.Max(float64(math.Floor(factor*3.0/windowSize)), 1))
	}

	// Use vips_shrink with the integral reduction
	if shrink > 1 {
		image, residual, err = shrinkImage(image, o, residual, shrink)
		if err != nil {
			return nil, err
		}
	}

	// Use vips_affine with the remaining float part
	if residual != 0 {
		image, err = vipsAffine(image, residual, o.Interpolator)
		if err != nil {
			return nil, err
		}
	}

	debug("factor: %v, shrink: %v, residual: %v", factor, shrink, residual)

	// Extract image
	image, err = extractImage(image, o)
	if err != nil {
		return nil, err
	}

	rotation, flip := calculateRotationAndFlip(image, o.Rotate)
	if flip {
		o.Flip = HORIZONTAL
	}
	if rotation != D0 {
		o.Rotate = rotation
	}

	if o.Rotate > 0 {
		image, err = vipsRotate(image, getAngle(o.Rotate))
		if err != nil {
			return nil, err
		}
	}

	if o.Flip > 0 {
		image, err = vipsFlip(image, o.Flip)
		if err != nil {
			return nil, err
		}
	}

	saveOptions := vipsSaveOptions{
		Quality:     o.Quality,
		Type:        o.Type,
		Compression: o.Compression,
	}

	buf, err = vipsSave(image, saveOptions)
	if err != nil {
		return nil, err
	}

	return buf, nil
}

func extractImage(image *C.struct__VipsImage, o Options) (*C.struct__VipsImage, error) {
	var err error = nil
	affinedWidth := int(image.Xsize)
	affinedHeight := int(image.Ysize)

	if affinedWidth != o.Width || affinedHeight != o.Height {
		width := int(math.Min(float64(affinedWidth), float64(o.Width)))
		height := int(math.Min(float64(affinedHeight), float64(o.Height)))

		switch {
		case o.Crop:
			left, top := calculateCrop(affinedWidth, affinedHeight, o.Width, o.Height, o.Gravity)
			image, err = vipsExtract(image, left, top, width, height)
			break
		case o.Embed:
			left, top := (o.Width-affinedWidth)/2, (o.Height-affinedHeight)/2
			image, err = vipsEmbed(image, left, top, o.Width, o.Height, o.Extend)
			break
		case o.Top > 0 && o.Left > 0:
			image, err = vipsExtract(image, o.Left, o.Top, width, height)
			break
		}
	}

	return image, err
}

func imageCalculations(o Options, inWidth, inHeight int) float64 {
	factor := 1.0

	switch {
	// Fixed width and height
	case o.Width > 0 && o.Height > 0:
		xf := float64(inWidth) / float64(o.Width)
		yf := float64(inHeight) / float64(o.Height)
		if o.Crop {
			factor = math.Min(xf, yf)
		} else {
			factor = math.Max(xf, yf)
		}
	// Fixed width, auto height
	case o.Width > 0:
		factor = float64(inWidth) / float64(o.Width)
		o.Height = int(math.Floor(float64(inHeight) / factor))
	// Fixed height, auto width
	case o.Height > 0:
		factor = float64(inHeight) / float64(o.Height)
		o.Width = int(math.Floor(float64(inWidth) / factor))
	// Identity transform
	default:
		o.Width = inWidth
		o.Height = inHeight
	}

	return factor
}

func calculateCrop(inWidth, inHeight, outWidth, outHeight int, gravity Gravity) (int, int) {
	left, top := 0, 0

	switch gravity {
	case NORTH:
		left = (inWidth - outWidth + 1) / 2
	case EAST:
		left = inWidth - outWidth
		top = (inHeight - outHeight + 1) / 2
	case SOUTH:
		left = (inWidth - outWidth + 1) / 2
		top = inHeight - outHeight
	case WEST:
		top = (inHeight - outHeight + 1) / 2
	default:
		left = (inWidth - outWidth + 1) / 2
		top = (inHeight - outHeight + 1) / 2
	}

	return left, top
}

func calculateRotationAndFlip(image *C.struct__VipsImage, angle Angle) (Angle, bool) {
	rotate := D0
	flip := false

	if angle == -1 {
		switch vipsExifOrientation(image) {
		case 6:
			rotate = D90
			break
		case 3:
			rotate = D180
			break
		case 8:
			rotate = D270
			break
		case 2:
			flip = true
			break // flip 1
		case 7:
			flip = true
			rotate = D90
			break // flip 6
		case 4:
			flip = true
			rotate = D180
			break // flip 3
		case 5:
			flip = true
			rotate = D270
			break // flip 8
		}
	} else {
		if angle == 90 {
			rotate = D90
		} else if angle == 180 {
			rotate = D180
		} else if angle == 270 {
			rotate = D270
		}
	}

	return rotate, flip
}

func shrinkImage(image *C.struct__VipsImage, o Options, residual float64, shrink int) (*C.struct__VipsImage, float64, error) {
	// Use vips_shrink with the integral reduction
	image, err := vipsShrink(image, shrink)
	if err != nil {
		return nil, 0, err
	}

	// Recalculate residual float based on dimensions of required vs shrunk images
	residualx := float64(o.Width) / float64(image.Xsize)
	residualy := float64(o.Height) / float64(image.Ysize)

	if o.Crop {
		residual = math.Max(residualx, residualy)
	} else {
		residual = math.Min(residualx, residualy)
	}

	return image, residual, nil
}

func shrinkJpegImage(buf []byte, factor float64, shrink int) (*C.struct__VipsImage, float64, error) {
	shrinkOnLoad := 1

	switch {
	case shrink >= 8:
		factor = factor / 8
		shrinkOnLoad = 8
	case shrink >= 4:
		factor = factor / 4
		shrinkOnLoad = 4
	case shrink >= 2:
		factor = factor / 2
		shrinkOnLoad = 2
	}

	if shrinkOnLoad > 1 {
		// Reload input using shrink-on-load
		image, err := vipsShrinkJpeg(buf, shrinkOnLoad)
		if err != nil {
			return nil, factor, err
		}
		return image, factor, err
	}

	return nil, factor, nil
}

func getAngle(angle Angle) Angle {
	divisor := angle % 90
	if divisor != 0 {
		angle = angle - divisor
	}
	return angle
}
<commit_msg>fix(resize): support rotate<commit_after>package bimg

/*
#cgo pkg-config: vips
#include "vips/vips.h"
*/
import "C"

import (
	"errors"
	"math"
)

func Resize(buf []byte, o Options) ([]byte, error) {
	defer C.vips_thread_shutdown()

	image, imageType, err := vipsRead(buf)
	if err != nil {
		return nil, err
	}

	// defaults
	if o.Quality == 0 {
		o.Quality = QUALITY
	}
	if o.Compression == 0 {
		o.Compression = 6
	}
	if o.Type == 0 {
		o.Type = imageType
	}

	if IsTypeSupported(o.Type) == false {
		return nil, errors.New("Unsupported image output type")
	}

	debug("Options: %#v", o)

	// get WxH
	inWidth := int(image.Xsize)
	inHeight := int(image.Ysize)

	// image calculations
	factor := imageCalculations(o, inWidth, inHeight)
	shrink := int(math.Max(math.Floor(factor), 1))
	residual := float64(shrink) / factor

	// Do not enlarge the output if the input width *or* height are already less than the required dimensions
	if o.Enlarge == false {
		if inWidth < o.Width && inHeight < o.Height {
			factor = 1
			shrink = 1
			residual = 0
			o.Width = inWidth
			o.Height = inHeight
		}
	}

	// Try to use libjpeg shrink-on-load
	if imageType == JPEG && shrink >= 2 {
		// Recalculate integral shrink and double residual
		tmpImage, factor, err := shrinkJpegImage(buf, factor, shrink)
		if err != nil {
			return nil, err
		}
		if tmpImage != nil {
			image = tmpImage
			factor = math.Max(factor, 1.0)
			shrink = int(math.Floor(factor))
			residual = float64(shrink) / factor
		}
	}

	// Calculate integral box shrink
	windowSize := vipsWindowSize(o.Interpolator.String())
	if factor >= 2 && windowSize > 3 {
		// Shrink less, affine more with interpolators that use at least 4x4 pixel window, e.g. bicubic
		shrink = int(math.Max(float64(math.Floor(factor*3.0/windowSize)), 1))
	}

	// Use vips_shrink with the integral reduction
	if shrink > 1 {
		image, residual, err = shrinkImage(image, o, residual, shrink)
		if err != nil {
			return nil, err
		}
	}

	// Use vips_affine with the remaining float part
	if residual != 0 {
		image, err = vipsAffine(image, residual, o.Interpolator)
		if err != nil {
			return nil, err
		}
	}

	debug("factor: %v, shrink: %v, residual: %v", factor, shrink, residual)

	// Extract image
	image, err = extractImage(image, o)
	if err != nil {
		return nil, err
	}

	if o.Rotate == 0 {
		rotation, flip := calculateRotationAndFlip(image, o.Rotate)
		if flip {
			o.Flip = HORIZONTAL
		}
		if rotation > D0 {
			o.Rotate = rotation
		}
	}

	if o.Rotate > 0 {
		image, err = vipsRotate(image, getAngle(o.Rotate))
		if err != nil {
			return nil, err
		}
	}

	if o.Flip > 0 {
		image, err = vipsFlip(image, o.Flip)
		if err != nil {
			return nil, err
		}
	}

	saveOptions := vipsSaveOptions{
		Quality:     o.Quality,
		Type:        o.Type,
		Compression: o.Compression,
	}

	buf, err = vipsSave(image, saveOptions)
	if err != nil {
		return nil, err
	}

	return buf, nil
}

func extractImage(image *C.struct__VipsImage, o Options) (*C.struct__VipsImage, error) {
	var err error = nil
	affinedWidth := int(image.Xsize)
	affinedHeight := int(image.Ysize)

	if affinedWidth != o.Width || affinedHeight != o.Height {
		width := int(math.Min(float64(affinedWidth), float64(o.Width)))
		height := int(math.Min(float64(affinedHeight), float64(o.Height)))

		switch {
		case o.Crop:
			left, top := calculateCrop(affinedWidth, affinedHeight, o.Width, o.Height, o.Gravity)
			image, err = vipsExtract(image, left, top, width, height)
			break
		case o.Embed:
			left, top := (o.Width-affinedWidth)/2, (o.Height-affinedHeight)/2
			image, err = vipsEmbed(image, left, top, o.Width, o.Height, o.Extend)
			break
		case o.Top > 0 && o.Left > 0:
			image, err = vipsExtract(image, o.Left, o.Top, width, height)
			break
		}
	}

	return image, err
}

func imageCalculations(o Options, inWidth, inHeight int) float64 {
	factor := 1.0

	switch {
	// Fixed width and height
	case o.Width > 0 && o.Height > 0:
		xf := float64(inWidth) / float64(o.Width)
		yf := float64(inHeight) / float64(o.Height)
		if o.Crop {
			factor = math.Min(xf, yf)
		} else {
			factor = math.Max(xf, yf)
		}
	// Fixed width, auto height
	case o.Width > 0:
		factor = float64(inWidth) / float64(o.Width)
		o.Height = int(math.Floor(float64(inHeight) / factor))
	// Fixed height, auto width
	case o.Height > 0:
		factor = float64(inHeight) / float64(o.Height)
		o.Width = int(math.Floor(float64(inWidth) / factor))
	// Identity transform
	default:
		o.Width = inWidth
		o.Height = inHeight
	}

	return factor
}

func calculateCrop(inWidth, inHeight, outWidth, outHeight int, gravity Gravity) (int, int) {
	left, top := 0, 0

	switch gravity {
	case NORTH:
		left = (inWidth - outWidth + 1) / 2
	case EAST:
		left = inWidth - outWidth
		top = (inHeight - outHeight + 1) / 2
	case SOUTH:
		left = (inWidth - outWidth + 1) / 2
		top = inHeight - outHeight
	case WEST:
		top = (inHeight - outHeight + 1) / 2
	default:
		left = (inWidth - outWidth + 1) / 2
		top = (inHeight - outHeight + 1) / 2
	}

	return left, top
}

func calculateRotationAndFlip(image *C.struct__VipsImage, angle Angle) (Angle, bool) {
	rotate := D0
	flip := false

	if angle == -1 {
		switch vipsExifOrientation(image) {
		case 6:
			rotate = D90
			break
		case 3:
			rotate = D180
			break
		case 8:
			rotate = D270
			break
		case 2:
			flip = true
			break // flip 1
		case 7:
			flip = true
			rotate = D90
			break // flip 6
		case 4:
			flip = true
			rotate = D180
			break // flip 3
		case 5:
			flip = true
			rotate = D270
			break // flip 8
		}
	} else {
		if angle == 90 {
			rotate = D90
		} else if angle == 180 {
			rotate = D180
		} else if angle == 270 {
			rotate = D270
		}
	}

	return rotate, flip
}

func shrinkImage(image *C.struct__VipsImage, o Options, residual float64, shrink int) (*C.struct__VipsImage, float64, error) {
	// Use vips_shrink with the integral reduction
	image, err := vipsShrink(image, shrink)
	if err != nil {
		return nil, 0, err
	}

	// Recalculate residual float based on dimensions of required vs shrunk images
	residualx := float64(o.Width) / float64(image.Xsize)
	residualy := float64(o.Height) / float64(image.Ysize)

	if o.Crop {
		residual = math.Max(residualx, residualy)
	} else {
		residual = math.Min(residualx, residualy)
	}

	return image, residual, nil
}

func shrinkJpegImage(buf []byte, factor float64, shrink int) (*C.struct__VipsImage, float64, error) {
	shrinkOnLoad := 1

	switch {
	case shrink >= 8:
		factor = factor / 8
		shrinkOnLoad = 8
	case shrink >= 4:
		factor = factor / 4
		shrinkOnLoad = 4
	case shrink >= 2:
		factor = factor / 2
		shrinkOnLoad = 2
	}

	if shrinkOnLoad > 1 {
		// Reload input using shrink-on-load
		image, err := vipsShrinkJpeg(buf, shrinkOnLoad)
		if err != nil {
			return nil, factor, err
		}
		return image, factor, err
	}

	return nil, factor, nil
}

func getAngle(angle Angle) Angle {
	divisor := angle % 90
	if divisor != 0 {
		angle = angle - divisor
	}
	return angle
}
<|endoftext|>


<commit_before>package bimg

/*
#cgo pkg-config: vips
#include "vips/vips.h"
*/
import "C"

import (
	"errors"
	"math"
)

func Resize(buf []byte, o Options) ([]byte, error) {
	defer C.vips_thread_shutdown()

	if len(buf) == 0 {
		return nil, errors.New("Image buffer is empty")
	}

	image, imageType, err := vipsRead(buf)
	if err != nil {
		return nil, err
	}

	// Define default options
	applyDefaults(&o, imageType)

	if IsTypeSupported(o.Type) == false {
		return nil, errors.New("Unsupported image output type")
	}

	debug("Options: %#v", o)

	inWidth := int(image.Xsize)
	inHeight := int(image.Ysize)

	// Infer the required operation based on the in/out image sizes for a coherent transformation
	normalizeOperation(&o, inWidth, inHeight)

	// image calculations
	factor := imageCalculations(&o, inWidth, inHeight)
	shrink := calculateShrink(factor, o.Interpolator)
	residual := calculateResidual(factor, shrink)

	// Do not enlarge the output if the input width or height
	// are already less than the required dimensions
	if !o.Enlarge && !o.Force {
		if inWidth < o.Width && inHeight < o.Height {
			factor = 1.0
			shrink = 1
			residual = 0
			o.Width = inWidth
			o.Height = inHeight
		}
	}

	// Try to use libjpeg shrink-on-load
	if imageType == JPEG && shrink >= 2 {
		tmpImage, factor, err := shrinkJpegImage(buf, image, factor, shrink)
		if err != nil {
			return nil, err
		}

		image = tmpImage
		factor = math.Max(factor, 1.0)
		shrink = int(math.Floor(factor))
		residual = float64(shrink) / factor
	}

	// Zoom image, if necessary
	image, err = zoomImage(image, o.Zoom)
	if err != nil {
		return nil, err
	}

	// Rotate / flip image, if necessary
	image, err = rotateAndFlipImage(image, o)
	if err != nil {
		return nil, err
	}

	// Transform image, if necessary
	if shouldTransformImage(o, inWidth, inHeight) {
		image, err = transformImage(image, o, shrink, residual)
		if err != nil {
			return nil, err
		}
	}

	// Apply effects, if necessary
	if shouldApplyEffects(o) {
		image, err = applyEffects(image, o)
		if err != nil {
			return nil, err
		}
	}

	// Add watermark, if necessary
	image, err = watermakImage(image, o.Watermark)
	if err != nil {
		return nil, err
	}

	// Flatten image on a background, if necessary
	image, err = imageFlatten(image, imageType, o)
	if err != nil {
		return nil, err
	}

	saveOptions := vipsSaveOptions{
		Quality:        o.Quality,
		Type:           o.Type,
		Compression:    o.Compression,
		Interlace:      o.Interlace,
		NoProfile:      o.NoProfile,
		Interpretation: o.Interpretation,
	}

	// Finally get the resultant buffer
	buf, err = vipsSave(image, saveOptions)
	if err != nil {
		return nil, err
	}

	return buf, nil
}

func applyDefaults(o *Options, imageType ImageType) {
	if o.Quality == 0 {
		o.Quality = QUALITY
	}
	if o.Compression == 0 {
		o.Compression = 6
	}
	if o.Type == 0 {
		o.Type = imageType
	}
	if o.Interpretation == 0 {
		o.Interpretation = INTERPRETATION_sRGB
	}
}

func normalizeOperation(o *Options, inWidth, inHeight int) {
	if !o.Force && !o.Crop && !o.Embed && !o.Enlarge && o.Rotate == 0 && (o.Width > 0 || o.Height > 0) {
		o.Force = true
	}
}

func shouldTransformImage(o Options, inWidth, inHeight int) bool {
	return o.Force || (o.Width > 0 && o.Width != inWidth) ||
		(o.Height > 0 && o.Height != inHeight) || o.AreaWidth > 0 || o.AreaHeight > 0
}

func shouldApplyEffects(o Options) bool {
	return o.GaussianBlur.Sigma > 0 || o.GaussianBlur.MinAmpl > 0
}

func transformImage(image *C.VipsImage, o Options, shrink int, residual float64) (*C.VipsImage, error) {
	var err error

	// Use vips_shrink with the integral reduction
	if shrink > 1 {
		image, residual, err = shrinkImage(image, o, residual, shrink)
		if err != nil {
			return nil, err
		}
	}

	residualx, residualy := residual, residual
	if o.Force {
		residualx = float64(o.Width) / float64(image.Xsize)
		residualy = float64(o.Height) / float64(image.Ysize)
	}

	if o.Force || residual != 0 {
		image, err = vipsAffine(image, residualx, residualy, o.Interpolator)
		if err != nil {
			return nil, err
		}
	}

	if o.Force {
		o.Crop = false
		o.Embed = false
	}

	image, err = extractOrEmbedImage(image, o)
	if err != nil {
		return nil, err
	}

	debug("Transform: shrink=%v, residual=%v, interpolator=%v",
		shrink, residual, o.Interpolator.String())

	return image, nil
}

func applyEffects(image *C.VipsImage, o Options) (*C.VipsImage, error) {
	var err error

	if o.GaussianBlur.Sigma > 0 || o.GaussianBlur.MinAmpl > 0 {
		image, err = vipsGaussianBlur(image, o.GaussianBlur)
		if err != nil {
			return nil, err
		}
	}

	debug("Effects: gaussSigma=%v, gaussMinAmpl=%v",
		o.GaussianBlur.Sigma, o.GaussianBlur.MinAmpl)

	return image, nil
}

func extractOrEmbedImage(image *C.VipsImage, o Options) (*C.VipsImage, error) {
	var err error = nil
	inWidth := int(image.Xsize)
	inHeight := int(image.Ysize)

	switch {
	case o.Crop:
		width := int(math.Min(float64(inWidth), float64(o.Width)))
		height := int(math.Min(float64(inHeight), float64(o.Height)))
		left, top := calculateCrop(inWidth, inHeight, o.Width, o.Height, o.Gravity)
		left, top = int(math.Max(float64(left), 0)), int(math.Max(float64(top), 0))
		image, err = vipsExtract(image, left, top, width, height)
		break
	case o.Embed:
		left, top := (o.Width-inWidth)/2, (o.Height-inHeight)/2
		image, err = vipsEmbed(image, left, top, o.Width, o.Height, o.Extend)
		break
	case o.Top != 0 || o.Left != 0:
		if o.AreaWidth == 0 {
			o.AreaHeight = o.Width
		}
		if o.AreaHeight == 0 {
			o.AreaHeight = o.Height
		}
		if o.AreaWidth == 0 || o.AreaHeight == 0 {
			return nil, errors.New("Extract area width/height params are required")
		}
		image, err = vipsExtract(image, o.Left, o.Top, o.AreaWidth, o.AreaHeight)
		break
	}

	return image, err
}

func rotateAndFlipImage(image *C.VipsImage, o Options) (*C.VipsImage, error) {
	var err error
	var direction Direction = -1

	if o.NoAutoRotate == false {
		rotation, flip := calculateRotationAndFlip(image, o.Rotate)
		if flip {
			o.Flip = flip
		}
		if rotation > D0 && o.Rotate == 0 {
			o.Rotate = rotation
		}
	}

	if o.Rotate > 0 {
		image, err = vipsRotate(image, getAngle(o.Rotate))
	}

	if o.Flip {
		direction = HORIZONTAL
	} else if o.Flop {
		direction = VERTICAL
	}

	if direction != -1 {
		image, err = vipsFlip(image, direction)
	}

	return image, err
}

func watermakImage(image *C.VipsImage, w Watermark) (*C.VipsImage, error) {
	if w.Text == "" {
		return image, nil
	}

	// Defaults
	if w.Font == "" {
		w.Font = WATERMARK_FONT
	}
	if w.Width == 0 {
		w.Width = int(math.Floor(float64(image.Xsize / 6)))
	}
	if w.DPI == 0 {
		w.DPI = 150
	}
	if w.Margin == 0 {
		w.Margin = w.Width
	}
	if w.Opacity == 0 {
		w.Opacity = 0.25
	} else if w.Opacity > 1 {
		w.Opacity = 1
	}

	image, err := vipsWatermark(image, w)
	if err != nil {
		return nil, err
	}

	return image, nil
}

func imageFlatten(image *C.VipsImage, imageType ImageType, o Options) (*C.VipsImage, error) {
	// Only PNG images are supported for now
	if imageType != PNG || o.Background == ColorBlack {
		return image, nil
	}

	return vipsFlattenBackground(image, o.Background)
}

func zoomImage(image *C.VipsImage, zoom int) (*C.VipsImage, error) {
	if zoom == 0 {
		return image, nil
	}
	return vipsZoom(image, zoom+1)
}

func shrinkImage(image *C.VipsImage, o Options, residual float64, shrink int) (*C.VipsImage, float64, error) {
	// Use vips_shrink with the integral reduction
	image, err := vipsShrink(image, shrink)
	if err != nil {
		return nil, 0, err
	}

	// Recalculate residual float based on dimensions of required vs shrunk images
	residualx := float64(o.Width) / float64(image.Xsize)
	residualy := float64(o.Height) / float64(image.Ysize)

	if o.Crop {
		residual = math.Max(residualx, residualy)
	} else {
		residual = math.Min(residualx, residualy)
	}

	return image, residual, nil
}

func shrinkJpegImage(buf []byte, input *C.VipsImage, factor float64, shrink int) (*C.VipsImage, float64, error) {
	var image *C.VipsImage
	var err error
	shrinkOnLoad := 1

	// Recalculate integral shrink and double residual
	switch {
	case shrink >= 8:
		factor = factor / 8
		shrinkOnLoad = 8
	case shrink >= 4:
		factor = factor / 4
		shrinkOnLoad = 4
	case shrink >= 2:
		factor = factor / 2
		shrinkOnLoad = 2
	}

	// Reload input using shrink-on-load
	if shrinkOnLoad > 1 {
		image, err = vipsShrinkJpeg(buf, input, shrinkOnLoad)
	}

	return image, factor, err
}

func imageCalculations(o *Options, inWidth, inHeight int) float64 {
	factor := 1.0
	xfactor := float64(inWidth) / float64(o.Width)
	yfactor := float64(inHeight) / float64(o.Height)

	switch {
	// Fixed width and height
	case o.Width > 0 && o.Height > 0:
		if o.Crop {
			factor = math.Min(xfactor, yfactor)
		} else {
			factor = math.Max(xfactor, yfactor)
		}
	// Fixed width, auto height
	case o.Width > 0:
		factor = xfactor
		o.Height = int(math.Floor(float64(inHeight) / factor))
	// Fixed height, auto width
	case o.Height > 0:
		factor = yfactor
		o.Width = int(math.Floor(float64(inWidth) / factor))
	// Identity transform
	default:
		o.Width = inWidth
		o.Height = inHeight
		break
	}

	return factor
}

func calculateCrop(inWidth, inHeight, outWidth, outHeight int, gravity Gravity) (int, int) {
	left, top := 0, 0

	switch gravity {
	case NORTH:
		left = (inWidth - outWidth + 1) / 2
	case EAST:
		left = inWidth - outWidth
		top = (inHeight - outHeight + 1) / 2
	case SOUTH:
		left = (inWidth - outWidth + 1) / 2
		top = inHeight - outHeight
	case WEST:
		top = (inHeight - outHeight + 1) / 2
	default:
		left = (inWidth - outWidth + 1) / 2
		top = (inHeight - outHeight + 1) / 2
	}

	return left, top
}

func calculateRotationAndFlip(image *C.VipsImage, angle Angle) (Angle, bool) {
	rotate := D0
	flip := false

	if angle > 0 {
		return rotate, flip
	}

	switch vipsExifOrientation(image) {
	case 6:
		rotate = D90
		break
	case 3:
		rotate = D180
		break
	case 8:
		rotate = D270
		break
	case 2:
		flip = true
		break // flip 1
	case 7:
		flip = true
		rotate = D90
		break // flip 6
	case 4:
		flip = true
		rotate = D180
		break // flip 3
	case 5:
		flip = true
		rotate = D270
		break // flip 8
	}

	return rotate, flip
}

func calculateShrink(factor float64, i Interpolator) int {
	var shrink float64

	// Calculate integral box shrink
	windowSize := vipsWindowSize(i.String())
	if factor >= 2 && windowSize > 3 {
		// Shrink less, affine more with interpolators that use at least 4x4 pixel window, e.g. bicubic
		shrink = float64(math.Floor(factor * 3.0 / windowSize))
	} else {
		shrink = math.Floor(factor)
	}

	return int(math.Max(shrink, 1))
}

func calculateResidual(factor float64, shrink int) float64 {
	return float64(shrink) / factor
}

func getAngle(angle Angle) Angle {
	divisor := angle % 90
	if divisor != 0 {
		angle = angle - divisor
	}
	return Angle(math.Min(float64(angle), 270))
}
<commit_msg>refactor(resize): simplify code<commit_after>package bimg

/*
#cgo pkg-config: vips
#include "vips/vips.h"
*/
import "C"

import (
	"errors"
	"math"
)

func Resize(buf []byte, o Options) ([]byte, error) {
	defer C.vips_thread_shutdown()

	if len(buf) == 0 {
		return nil, errors.New("Image buffer is empty")
	}

	image, imageType, err := vipsRead(buf)
	if err != nil {
		return nil, err
	}

	// Define default options
	applyDefaults(&o, imageType)

	if IsTypeSupported(o.Type) == false {
		return nil, errors.New("Unsupported image output type")
	}

	debug("Options: %#v", o)

	inWidth := int(image.Xsize)
	inHeight := int(image.Ysize)

	// Infer the required operation based on the in/out image sizes for a coherent transformation
	normalizeOperation(&o, inWidth, inHeight)

	// image calculations
	factor := imageCalculations(&o, inWidth, inHeight)
	shrink := calculateShrink(factor, o.Interpolator)
	residual := calculateResidual(factor, shrink)

	// Do not enlarge the output if the input width or height
	// are already less than the required dimensions
	if !o.Enlarge && !o.Force {
		if inWidth < o.Width && inHeight < o.Height {
			factor = 1.0
			shrink = 1
			residual = 0
			o.Width = inWidth
			o.Height = inHeight
		}
	}

	// Try to use libjpeg shrink-on-load
	if imageType == JPEG && shrink >= 2 {
		tmpImage, factor, err := shrinkJpegImage(buf, image, factor, shrink)
		if err != nil {
			return nil, err
		}

		image = tmpImage
		factor = math.Max(factor, 1.0)
		shrink = int(math.Floor(factor))
		residual = float64(shrink) / factor
	}

	// Zoom image, if necessary
	image, err = zoomImage(image, o.Zoom)
	if err != nil {
		return nil, err
	}

	// Rotate / flip image, if necessary
	image, err = rotateAndFlipImage(image, o)
	if err != nil {
		return nil, err
	}

	// Transform image, if necessary
	if shouldTransformImage(o, inWidth, inHeight) {
		image, err = transformImage(image, o, shrink, residual)
		if err != nil {
			return nil, err
		}
	}

	// Apply effects, if necessary
	if shouldApplyEffects(o) {
		image, err = applyEffects(image, o)
		if err != nil {
			return nil, err
		}
	}

	// Add watermark, if necessary
	image, err = watermakImage(image, o.Watermark)
	if err != nil {
		return nil, err
	}

	// Flatten image on a background, if necessary
	image, err = imageFlatten(image, imageType, o)
	if err != nil {
		return nil, err
	}

	saveOptions := vipsSaveOptions{
		Quality:        o.Quality,
		Type:           o.Type,
		Compression:    o.Compression,
		Interlace:      o.Interlace,
		NoProfile:      o.NoProfile,
		Interpretation: o.Interpretation,
	}

	// Finally get the resultant buffer
	return vipsSave(image, saveOptions)
}

func applyDefaults(o *Options, imageType ImageType) {
	if o.Quality == 0 {
		o.Quality = QUALITY
	}
	if o.Compression == 0 {
		o.Compression = 6
	}
	if o.Type == 0 {
		o.Type = imageType
	}
	if o.Interpretation == 0 {
		o.Interpretation = INTERPRETATION_sRGB
	}
}

func normalizeOperation(o *Options, inWidth, inHeight int) {
	if !o.Force && !o.Crop && !o.Embed && !o.Enlarge && o.Rotate == 0 && (o.Width > 0 || o.Height > 0) {
		o.Force = true
	}
}

func shouldTransformImage(o Options, inWidth, inHeight int) bool {
	return o.Force || (o.Width > 0 && o.Width != inWidth) ||
		(o.Height > 0 && o.Height != inHeight) || o.AreaWidth > 0 || o.AreaHeight > 0
}

func shouldApplyEffects(o Options) bool {
	return o.GaussianBlur.Sigma > 0 || o.GaussianBlur.MinAmpl > 0
}

func transformImage(image *C.VipsImage, o Options, shrink int, residual float64) (*C.VipsImage, error) {
	var err error

	// Use vips_shrink with the integral reduction
	if shrink > 1 {
		image, residual, err = shrinkImage(image, o, residual, shrink)
		if err != nil {
			return nil, err
		}
	}

	residualx, residualy := residual, residual
	if o.Force {
		residualx = float64(o.Width) / float64(image.Xsize)
		residualy = float64(o.Height) / float64(image.Ysize)
	}

	if o.Force || residual != 0 {
		image, err = vipsAffine(image, residualx, residualy, o.Interpolator)
		if err != nil {
			return nil, err
		}
	}

	if o.Force {
		o.Crop = false
		o.Embed = false
	}

	image, err = extractOrEmbedImage(image, o)
	if err != nil {
		return nil, err
	}

	debug("Transform: shrink=%v, residual=%v, interpolator=%v",
		shrink, residual, o.Interpolator.String())

	return image, nil
}

func applyEffects(image *C.VipsImage, o Options) (*C.VipsImage, error) {
	var err error

	if o.GaussianBlur.Sigma > 0 || o.GaussianBlur.MinAmpl > 0 {
		image, err = vipsGaussianBlur(image, o.GaussianBlur)
		if err != nil {
			return nil, err
		}
	}

	debug("Effects: gaussSigma=%v, gaussMinAmpl=%v",
		o.GaussianBlur.Sigma, o.GaussianBlur.MinAmpl)

	return image, nil
}

func extractOrEmbedImage(image *C.VipsImage, o Options) (*C.VipsImage, error) {
	var err error = nil
	inWidth := int(image.Xsize)
	inHeight := int(image.Ysize)

	switch {
	case o.Crop:
		width := int(math.Min(float64(inWidth), float64(o.Width)))
		height := int(math.Min(float64(inHeight), float64(o.Height)))
		left, top := calculateCrop(inWidth, inHeight, o.Width, o.Height, o.Gravity)
		left, top = int(math.Max(float64(left), 0)), int(math.Max(float64(top), 0))
		image, err = vipsExtract(image, left, top, width, height)
		break
	case o.Embed:
		left, top := (o.Width-inWidth)/2, (o.Height-inHeight)/2
		image, err = vipsEmbed(image, left, top, o.Width, o.Height, o.Extend)
		break
	case o.Top != 0 || o.Left != 0:
		if o.AreaWidth == 0 {
			o.AreaHeight = o.Width
		}
		if o.AreaHeight == 0 {
			o.AreaHeight = o.Height
		}
		if o.AreaWidth == 0 || o.AreaHeight == 0 {
			return nil, errors.New("Extract area width/height params are required")
		}
		image, err = vipsExtract(image, o.Left, o.Top, o.AreaWidth, o.AreaHeight)
		break
	}

	return image, err
}

func rotateAndFlipImage(image *C.VipsImage, o Options) (*C.VipsImage, error) {
	var err error
	var direction Direction = -1

	if o.NoAutoRotate == false {
		rotation, flip := calculateRotationAndFlip(image, o.Rotate)
		if flip {
			o.Flip = flip
		}
		if rotation > D0 && o.Rotate == 0 {
			o.Rotate = rotation
		}
	}

	if o.Rotate > 0 {
		image, err = vipsRotate(image, getAngle(o.Rotate))
	}

	if o.Flip {
		direction = HORIZONTAL
	} else if o.Flop {
		direction = VERTICAL
	}

	if direction != -1 {
		image, err = vipsFlip(image, direction)
	}

	return image, err
}

func watermakImage(image *C.VipsImage, w Watermark) (*C.VipsImage, error) {
	if w.Text == "" {
		return image, nil
	}

	// Defaults
	if w.Font == "" {
		w.Font = WATERMARK_FONT
	}
	if w.Width == 0 {
		w.Width = int(math.Floor(float64(image.Xsize / 6)))
	}
	if w.DPI == 0 {
		w.DPI = 150
	}
	if w.Margin == 0 {
		w.Margin = w.Width
	}
	if w.Opacity == 0 {
		w.Opacity = 0.25
	} else if w.Opacity > 1 {
		w.Opacity = 1
	}

	image, err := vipsWatermark(image, w)
	if err != nil {
		return nil, err
	}

	return image, nil
}

func imageFlatten(image *C.VipsImage, imageType ImageType, o Options) (*C.VipsImage, error) {
	// Only PNG images are supported for now
	if imageType != PNG || o.Background == ColorBlack {
		return image, nil
	}

	return vipsFlattenBackground(image, o.Background)
}

func zoomImage(image *C.VipsImage, zoom int) (*C.VipsImage, error) {
	if zoom == 0 {
		return image, nil
	}
	return vipsZoom(image, zoom+1)
}

func shrinkImage(image *C.VipsImage, o Options, residual float64, shrink int) (*C.VipsImage, float64, error) {
	// Use vips_shrink with the integral reduction
	image, err := vipsShrink(image, shrink)
	if err != nil {
		return nil, 0, err
	}

	// Recalculate residual float based on dimensions of required vs shrunk images
	residualx := float64(o.Width) / float64(image.Xsize)
	residualy := float64(o.Height) / float64(image.Ysize)

	if o.Crop {
		residual = math.Max(residualx, residualy)
	} else {
		residual = math.Min(residualx, residualy)
	}

	return image, residual, nil
}

func shrinkJpegImage(buf []byte, input *C.VipsImage, factor float64, shrink int) (*C.VipsImage, float64, error) {
	var image *C.VipsImage
	var err error
	shrinkOnLoad := 1

	// Recalculate integral shrink and double residual
	switch {
	case shrink >= 8:
		factor = factor / 8
		shrinkOnLoad = 8
	case shrink >= 4:
		factor = factor / 4
		shrinkOnLoad = 4
	case shrink >= 2:
		factor = factor / 2
		shrinkOnLoad = 2
	}

	// Reload input using shrink-on-load
	if shrinkOnLoad > 1 {
		image, err = vipsShrinkJpeg(buf, input, shrinkOnLoad)
	}

	return image, factor, err
}

func imageCalculations(o *Options, inWidth, inHeight int) float64 {
	factor := 1.0
	xfactor := float64(inWidth) / float64(o.Width)
	yfactor := float64(inHeight) / float64(o.Height)

	switch {
	// Fixed width and height
	case o.Width > 0 && o.Height > 0:
		if o.Crop {
			factor = math.Min(xfactor, yfactor)
		} else {
			factor = math.Max(xfactor, yfactor)
		}
	// Fixed width, auto height
	case o.Width > 0:
		factor = xfactor
		o.Height = int(math.Floor(float64(inHeight) / factor))
	// Fixed height, auto width
	case o.Height > 0:
		factor = yfactor
		o.Width = int(math.Floor(float64(inWidth) / factor))
	// Identity transform
	default:
		o.Width = inWidth
		o.Height = inHeight
		break
	}

	return factor
}

func calculateCrop(inWidth, inHeight, outWidth, outHeight int, gravity Gravity) (int, int) {
	left, top := 0, 0

	switch gravity {
	case NORTH:
		left = (inWidth - outWidth + 1) / 2
	case EAST:
		left = inWidth - outWidth
		top = (inHeight - outHeight + 1) / 2
	case SOUTH:
		left = (inWidth - outWidth + 1) / 2
		top = inHeight - outHeight
	case WEST:
		top = (inHeight - outHeight + 1) / 2
	default:
		left = (inWidth - outWidth + 1) / 2
		top = (inHeight - outHeight + 1) / 2
	}

	return left, top
}

func calculateRotationAndFlip(image *C.VipsImage, angle Angle) (Angle, bool) {
	rotate := D0
	flip := false

	if angle > 0 {
		return rotate, flip
	}

	switch vipsExifOrientation(image) {
	case 6:
		rotate = D90
		break
	case 3:
		rotate = D180
		break
	case 8:
		rotate = D270
		break
	case 2:
		flip = true
		break // flip 1
	case 7:
		flip = true
		rotate = D90
		break // flip 6
	case 4:
		flip = true
		rotate = D180
		break // flip 3
	case 5:
		flip = true
		rotate = D270
		break // flip 8
	}

	return rotate, flip
}

func calculateShrink(factor float64, i Interpolator) int {
	var shrink float64

	// Calculate integral box shrink
	windowSize := vipsWindowSize(i.String())
	if factor >= 2 && windowSize > 3 {
		// Shrink less, affine more with interpolators that use at least 4x4 pixel window, e.g. bicubic
		shrink = float64(math.Floor(factor * 3.0 / windowSize))
	} else {
		shrink = math.Floor(factor)
	}

	return int(math.Max(shrink, 1))
}

func calculateResidual(factor float64, shrink int) float64 {
	return float64(shrink) / factor
}

func getAngle(angle Angle) Angle {
	divisor := angle % 90
	if divisor != 0 {
		angle = angle - divisor
	}
	return Angle(math.Min(float64(angle), 270))
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/pclubiitk/student-search/database"

	"gopkg.in/kataras/iris.v5"
	"gopkg.in/pg.v5"
)

// StudentSearchRoute is a route for the search.
func StudentSearchRoute(db *pg.DB) {

	api := iris.Party("/api")

	api.Get("/students", func(ctx *iris.Context) {
		var students []database.Student
		err := db.Model(&students).Select()
		if err != nil {
			panic(err)
		}
		err = ctx.JSON(iris.StatusOK, students)
		if err != nil {
			panic(err)
		}
	})

}
<commit_msg>Add Single User Api Call<commit_after>package main

import (
	"github.com/pclubiitk/student-search/database"

	"gopkg.in/kataras/iris.v5"
	"gopkg.in/pg.v5"
)

// StudentSearchRoute is a route for the search.
func StudentSearchRoute(db *pg.DB) {

	api := iris.Party("/api")

	api.Get("/students", func(ctx *iris.Context) {
		var students []database.Student
		err := db.Model(&students).Select()
		if err != nil {
			panic(err)
		}
		err = ctx.JSON(iris.StatusOK, students)
		if err != nil {
			panic(err)
		}
	})

	api.Get("/student", func(ctx *iris.Context) {
		var students []database.Student
		if ctx.URLParam("username") != "" {
			err := db.Model(&students).Where("username LIKE ?", ctx.URLParam("username")).Select()
			if err != nil {
				ctx.Text(iris.StatusNotFound, "")
				return
			}
			err = ctx.JSON(iris.StatusOK, students[0])
			if err != nil {
				ctx.Text(iris.StatusInternalServerError, "")
			}
			return
		}
		ctx.Text(iris.StatusNotFound, "")
	})

}
<|endoftext|>


<commit_before>/*
package route provides http package-compatible routing library. It can route http requests by by hostname, method, path and headers.

Route defines simple language for matching requests based on Go syntax. Route provides series of matchers that follow the syntax:

   Matcher("value")          // matches value using trie
   Matcher("<string>.value") // uses trie-based matching for a.value and b.value
   MatcherRegexp(".*value")  // uses regexp-based matching

Host matcher:

  Host("<subdomain>.localhost") // trie-based matcher for a.localhost, b.localhost, etc.
  HostRegexp(".*localhost")     // regexp based matcher

Path matcher:

  Path("/hello/<value>")   // trie-based matcher for raw request path
  PathRegexp("/hello/.*")  // regexp-based matcher for raw request path

Method matcher:

  Method("GET")            // trie-based matcher for request method
  MethodRegexp("POST|PUT") // regexp based matcher for request method

Header matcher:

  Header("Content-Type", "application/<subtype>") // trie-based matcher for headers
  HeaderRegexp("Content-Type", "application/.*")  // regexp based matcher for headers

Matchers can be combined using && operator:

  Host("localhost") && Method("POST") && Path("/v1")

Route library will join the trie-based matchers into one trie matcher when possible, for example:

  Host("localhost") && Method("POST") && Path("/v1")
  Host("localhost") && Method("GET") && Path("/v2")

Will be combined into one trie for performance. If you add a third route:

  Host("localhost") && Method("GET") && PathRegexp("/v2/.*")

It wont be joined ito the trie, and would be matched separatedly instead.
*/
package route

import (
	"fmt"
	"net/http"
	"sort"
	"sync"
)

// Router implements http request routing and operations. It is a generic router not conforming to http.Handler interface, to get a handler
// conforming to http.Handler interface, use Mux router instead.
type Router interface {
	// GetRoute returns a route by a given expression, returns nil if expresison is not found
	GetRoute(string) interface{}

	// AddRoute adds a route to match by expression, returns error if the expression already defined, or route expression is incorrect
	AddRoute(string, interface{}) error

	// RemoveRoute removes a route for a given expression
	RemoveRoute(string) error

	// UpsertRoute updates an existing route or adds a new route by given expression
	UpsertRoute(string, interface{}) error

	// Route takes a request and matches it against requests, returns matched route in case if found, nil if there's no matching route or error in case of internal error.
	Route(*http.Request) (interface{}, error)
}

type router struct {
	mutex    *sync.RWMutex
	matchers []matcher
	routes   map[string]*match
}

// New creates a new Router instance
func New() Router {
	return &router{
		mutex:  &sync.RWMutex{},
		routes: make(map[string]*match),
	}
}

func (e *router) GetRoute(expr string) interface{} {
	e.mutex.RLock()
	defer e.mutex.RUnlock()

	res, ok := e.routes[expr]
	if ok {
		return res.val
	}
	return nil
}

func (e *router) AddRoute(expr string, val interface{}) error {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	if _, ok := e.routes[expr]; ok {
		return fmt.Errorf("Expression '%s' already exists", expr)
	}
	result := &match{val: val}
	if _, err := parse(expr, result); err != nil {
		return err
	}
	e.routes[expr] = result
	if err := e.compile(); err != nil {
		delete(e.routes, expr)
		return err
	}
	return nil
}

func (e *router) UpsertRoute(expr string, val interface{}) error {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	result := &match{val: val}
	if _, err := parse(expr, result); err != nil {
		return err
	}
	prev, existed := e.routes[expr]

	e.routes[expr] = result
	if err := e.compile(); err != nil {
		if existed {
			e.routes[expr] = prev
		} else {
			delete(e.routes, expr)
		}
		return err
	}
	return nil
}

func (e *router) compile() error {
	var exprs = []string{}
	for expr, _ := range e.routes {
		exprs = append(exprs, expr)
	}
	sort.Sort(sort.Reverse(sort.StringSlice(exprs)))

	matchers := []matcher{}
	i := 0
	for _, expr := range exprs {
		result := e.routes[expr]
		matcher, err := parse(expr, result)
		if err != nil {
			return err
		}

		// Merge the previous and new matcher if that's possible
		if i > 0 && matchers[i-1].canMerge(matcher) {
			m, err := matchers[i-1].merge(matcher)
			if err != nil {
				return err
			}
			matchers[i-1] = m
		} else {
			matchers = append(matchers, matcher)
			i += 1
		}
	}

	e.matchers = matchers
	return nil
}

func (e *router) RemoveRoute(expr string) error {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	delete(e.routes, expr)
	return e.compile()
}

func (e *router) Route(req *http.Request) (interface{}, error) {
	e.mutex.RLock()
	defer e.mutex.RUnlock()

	if len(e.matchers) == 0 {
		return nil, nil
	}

	for _, m := range e.matchers {
		if l := m.match(req); l != nil {
			return l.val, nil
		}
	}
	return nil, nil
}
<commit_msg>by by -> by<commit_after>/*
package route provides http package-compatible routing library. It can route http requests by hostname, method, path and headers.

Route defines simple language for matching requests based on Go syntax. Route provides series of matchers that follow the syntax:

   Matcher("value")          // matches value using trie
   Matcher("<string>.value") // uses trie-based matching for a.value and b.value
   MatcherRegexp(".*value")  // uses regexp-based matching

Host matcher:

  Host("<subdomain>.localhost") // trie-based matcher for a.localhost, b.localhost, etc.
  HostRegexp(".*localhost")     // regexp based matcher

Path matcher:

  Path("/hello/<value>")   // trie-based matcher for raw request path
  PathRegexp("/hello/.*")  // regexp-based matcher for raw request path

Method matcher:

  Method("GET")            // trie-based matcher for request method
  MethodRegexp("POST|PUT") // regexp based matcher for request method

Header matcher:

  Header("Content-Type", "application/<subtype>") // trie-based matcher for headers
  HeaderRegexp("Content-Type", "application/.*")  // regexp based matcher for headers

Matchers can be combined using && operator:

  Host("localhost") && Method("POST") && Path("/v1")

Route library will join the trie-based matchers into one trie matcher when possible, for example:

  Host("localhost") && Method("POST") && Path("/v1")
  Host("localhost") && Method("GET") && Path("/v2")

Will be combined into one trie for performance. If you add a third route:

  Host("localhost") && Method("GET") && PathRegexp("/v2/.*")

It wont be joined ito the trie, and would be matched separatedly instead.
*/
package route

import (
	"fmt"
	"net/http"
	"sort"
	"sync"
)

// Router implements http request routing and operations. It is a generic router not conforming to http.Handler interface, to get a handler
// conforming to http.Handler interface, use Mux router instead.
type Router interface {
	// GetRoute returns a route by a given expression, returns nil if expresison is not found
	GetRoute(string) interface{}

	// AddRoute adds a route to match by expression, returns error if the expression already defined, or route expression is incorrect
	AddRoute(string, interface{}) error

	// RemoveRoute removes a route for a given expression
	RemoveRoute(string) error

	// UpsertRoute updates an existing route or adds a new route by given expression
	UpsertRoute(string, interface{}) error

	// Route takes a request and matches it against requests, returns matched route in case if found, nil if there's no matching route or error in case of internal error.
	Route(*http.Request) (interface{}, error)
}

type router struct {
	mutex    *sync.RWMutex
	matchers []matcher
	routes   map[string]*match
}

// New creates a new Router instance
func New() Router {
	return &router{
		mutex:  &sync.RWMutex{},
		routes: make(map[string]*match),
	}
}

func (e *router) GetRoute(expr string) interface{} {
	e.mutex.RLock()
	defer e.mutex.RUnlock()

	res, ok := e.routes[expr]
	if ok {
		return res.val
	}
	return nil
}

func (e *router) AddRoute(expr string, val interface{}) error {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	if _, ok := e.routes[expr]; ok {
		return fmt.Errorf("Expression '%s' already exists", expr)
	}
	result := &match{val: val}
	if _, err := parse(expr, result); err != nil {
		return err
	}
	e.routes[expr] = result
	if err := e.compile(); err != nil {
		delete(e.routes, expr)
		return err
	}
	return nil
}

func (e *router) UpsertRoute(expr string, val interface{}) error {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	result := &match{val: val}
	if _, err := parse(expr, result); err != nil {
		return err
	}
	prev, existed := e.routes[expr]

	e.routes[expr] = result
	if err := e.compile(); err != nil {
		if existed {
			e.routes[expr] = prev
		} else {
			delete(e.routes, expr)
		}
		return err
	}
	return nil
}

func (e *router) compile() error {
	var exprs = []string{}
	for expr, _ := range e.routes {
		exprs = append(exprs, expr)
	}
	sort.Sort(sort.Reverse(sort.StringSlice(exprs)))

	matchers := []matcher{}
	i := 0
	for _, expr := range exprs {
		result := e.routes[expr]
		matcher, err := parse(expr, result)
		if err != nil {
			return err
		}

		// Merge the previous and new matcher if that's possible
		if i > 0 && matchers[i-1].canMerge(matcher) {
			m, err := matchers[i-1].merge(matcher)
			if err != nil {
				return err
			}
			matchers[i-1] = m
		} else {
			matchers = append(matchers, matcher)
			i += 1
		}
	}

	e.matchers = matchers
	return nil
}

func (e *router) RemoveRoute(expr string) error {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	delete(e.routes, expr)
	return e.compile()
}

func (e *router) Route(req *http.Request) (interface{}, error) {
	e.mutex.RLock()
	defer e.mutex.RUnlock()

	if len(e.matchers) == 0 {
		return nil, nil
	}

	for _, m := range e.matchers {
		if l := m.match(req); l != nil {
			return l.val, nil
		}
	}
	return nil, nil
}
<|endoftext|>


<commit_before>package main

import (
    "net/http"
    log "github.com/sirupsen/logrus"
    "github.com/gin-gonic/gin"
    csh_auth "github.com/liam-middlebrook/csh-auth"
)

func protectedProfile(c *gin.Context){
    claims, ok := c.Value(csh_auth.AuthKey).(csh_auth.CSHClaims)
    if !ok {
        log.Fatal("error finding claims")
        return
    }
    c.String(http.StatusOK, "uid %s email %s name %s uuid %s", claims.UserInfo.Username, claims.UserInfo.Email, claims.UserInfo.FullName, claims.UserInfo.Subject)
}

func index(c *gin.Context){
    c.Data(http.StatusOK, "text/html", []byte("

Upload Feature Coming Soon™

Fork me on GitHub!
"))
}

func action(c *gin.Context){
    plug := GetPlug()
    url := S3PresignPlug(plug)
    c.Redirect(http.StatusFound, url.String())
}
<commit_msg>log plug pre-signing<commit_after>package main

import (
    "net/http"
    log "github.com/sirupsen/logrus"
    "github.com/gin-gonic/gin"
    csh_auth "github.com/liam-middlebrook/csh-auth"
)

func protectedProfile(c *gin.Context){
    claims, ok := c.Value(csh_auth.AuthKey).(csh_auth.CSHClaims)
    if !ok {
        log.Fatal("error finding claims")
        return
    }
    c.String(http.StatusOK, "uid %s email %s name %s uuid %s", claims.UserInfo.Username, claims.UserInfo.Email, claims.UserInfo.FullName, claims.UserInfo.Subject)
}

func index(c *gin.Context){
    c.Data(http.StatusOK, "text/html", []byte("

Upload Feature Coming Soon™

Fork me on GitHub!
"))
}

func action(c *gin.Context){
    plug := GetPlug()
    url := S3PresignPlug(plug)

    claims, ok := c.Value(csh_auth.AuthKey).(csh_auth.CSHClaims)
    if !ok {
        log.Fatal("error finding claims")
        return
    }
    log.WithFields(log.Fields{
        "uid": claims.UserInfo.Username,
        "plug_id": plug.ID,
        "plug_s3id": plug.S3ID,
        "presigned_uri": url.String(),
    }).Info("Presigned URI Generated")
    c.Redirect(http.StatusFound, url.String())
}
<|endoftext|>


<commit_before>package flotilla

import (
	"fmt"
	"net/url"
	"path/filepath"
	"regexp"

	"strings"
	"sync"

	"github.com/thrisp/engine"
)

var (
	regParam = regexp.MustCompile(`:[^/#?()\.\\]+|\(\?P<[a-zA-Z0-9]+>.*\)`)
	regSplat = regexp.MustCompile(`\*[^/#?()\.\\]+|\(\?P<[a-zA-Z0-9]+>.*\)`)
)

type (
	// Data about a route for use & reuse within App.
	Route struct {
		cache       sync.Pool
		routergroup *RouterGroup
		static      bool
		method      string
		base        string
		path        string
		handlers    []HandlerFunc
		Name        string
	}

	// A map of Route instances keyed by a string.
	Routes map[string]*Route

	// A RouterGroup is data about gathering any number routes around a prefix
	// and an array of group specific handlers.
	RouterGroup struct {
		app    *App
		prefix string
		//parent   *RouterGroup
		children []*RouterGroup
		routes   Routes
		group    *engine.Group
		Handlers []HandlerFunc
	}

	RouterGroups []*RouterGroup
)

func (r *Route) handle(c *engine.Ctx) {
	rq := r.getR(c)
	rq.Next()
	r.putR(rq)
}

func NewRoute(method string, path string, static bool, handlers []HandlerFunc) *Route {
	r := &Route{method: method, static: static, handlers: handlers}
	if static {
		if fp := strings.Split(path, "/"); fp[len(fp)-1] != "*filepath" {
			r.base = filepath.Join(path, "/*filepath")
		} else {
			r.base = path
		}
	} else {
		r.base = path
	}
	return r
}

func (r *Route) Named() string {
	name := strings.Split(r.path, "/")
	name = append(name, strings.ToLower(r.method))
	for index, value := range name {
		if regSplat.MatchString(value) {
			name[index] = "s"
		}
		if regParam.MatchString(value) {
			name[index] = "p"
		}
	}
	return strings.Join(name, `\`)
}

// Takes string parameters and applies them to a Route. First to any :parameter
// params, then *splat params. If any params are left over(not the case with a
// *splat), and the route method is GET, a query string of key=value is appended
// to the end of the url with arbitrary assigned keys(e.g. value1=param) where
// no key is provided
//
// e.g.
// r1 := NewRoute("GET", /my/:mysterious/path, false, [AHandlerFunc])
// r2 := NewRoute("GET", /my/*path, false, [AHandlerFunc])
// u1, _ := r1.Url("hello", "world=are" "you=there", "sayhi")
// u2, _ := r2.Url("hello", "world", "are" "you", "there")
// fmt.Printf("url1: %s\n", u1)
//
//	/my/hello/path?world=are&you=there&value3=sayhi
//
// fmt.Printf("url2: %s\n", u2)
//
//	/my/hello/world/are/you/there
func (r *Route) Url(params ...string) (*url.URL, error) {
	paramCount := len(params)
	i := 0
	rurl := regParam.ReplaceAllStringFunc(r.path, func(m string) string {
		var val string
		if i < paramCount {
			val = params[i]
		}
		i += 1
		return fmt.Sprintf(`%s`, val)
	})
	rurl = regSplat.ReplaceAllStringFunc(rurl, func(m string) string {
		splat := params[i:(len(params))]
		i += len(splat)
		return fmt.Sprintf(strings.Join(splat, "/"))
	})
	u, err := url.Parse(rurl)
	if err != nil {
		return nil, err
	}
	if i < len(params) && r.method == "GET" {
		providedquerystring := params[i:(len(params))]
		var querystring []string
		qsi := 0
		for qi, qs := range providedquerystring {
			if len(strings.Split(qs, "=")) != 2 {
				qs = fmt.Sprintf("value%d=%s", qi+1, qs)
			}
			querystring = append(querystring, url.QueryEscape(qs))
			qsi += 1
		}
		u.RawQuery = strings.Join(querystring, "&")
	}
	return u, nil
}

func (rg *RouterGroup) combineHandlers(handlers []HandlerFunc) []HandlerFunc {
	s := len(rg.Handlers) + len(handlers)
	h := make([]HandlerFunc, 0, s)
	h = append(h, rg.Handlers...)
	h = append(h, handlers...)
	return h
}

func (rg *RouterGroup) handlerExists(outside HandlerFunc) bool {
	for _, inside := range rg.Handlers {
		if funcEqual(inside, outside) {
			return true
		}
	}
	return false
}

func (rg *RouterGroup) pathFor(path string) string {
	joined := filepath.Join(rg.prefix, path)
	// Append a '/' if the last component had one, but only if it's not there already
	if len(path) > 0 && path[len(path)-1] == '/' && joined[len(joined)-1] != '/' {
		return joined + "/"
	}
	return joined
}

func (rg *RouterGroup) pathNoLeadingSlash(path string) string {
	return strings.TrimLeft(strings.Join([]string{rg.prefix, path}, "/"), "/")
}

// NewRouterGroup attaches a new RouterGroup to the App with the prefix.
func NewRouterGroup(prefix string, app *App) *RouterGroup {
	return &RouterGroup{prefix: prefix,
		app:    app,
		group:  app.engine.Group.New(prefix),
		routes: make(Routes),
	}
}

// New Creates a new child router group with the base component string.
func (rg *RouterGroup) New(component string, handlers ...HandlerFunc) *RouterGroup {
	prefix := rg.pathFor(component)

	newrg := NewRouterGroup(prefix, rg.app)
	//newrg.parent = rg
	newrg.Handlers = rg.combineHandlers(handlers)

	rg.children = append(rg.children, newrg)

	return newrg
}

// Use adds any number of HandlerFunc to the RouterGroup which will be run before
// route handlers for all Route attached to the RouterGroup.
func (rg *RouterGroup) Use(middlewares ...HandlerFunc) {
	for _, handler := range middlewares {
		if !rg.handlerExists(handler) {
			rg.Handlers = append(rg.Handlers, handler)
		}
	}
}

// UseAt adds any number of HandlerFunc to the RouterGroup as middleware when you
// must control the position in relation to other middleware.
func (rg *RouterGroup) UseAt(index int, middlewares ...HandlerFunc) {
	if index > len(rg.Handlers) {
		rg.Use(middlewares...)
		return
	}

	var newh []HandlerFunc

	for _, handler := range middlewares {
		if !rg.handlerExists(handler) {
			newh = append(newh, handler)
		}
	}

	before := rg.Handlers[:index]
	after := append(newh, rg.Handlers[index:]...)
	rg.Handlers = append(before, after...)
}

func (rg *RouterGroup) addRoute(r *Route) {
	if r.Name != "" {
		rg.routes[r.Name] = r
	} else {
		rg.routes[r.Named()] = r
	}
}

// Handle registers new handlers and/or middlewares with the given path and
// method. For GET, POST, PUT, PATCH and DELETE requests the respective shortcut
// functions can be used.
func (rg *RouterGroup) Handle(route *Route) {
	// finalize Route with RouterGroup specific information
	route.routergroup = rg
	route.handlers = rg.combineHandlers(route.handlers)
	route.path = rg.pathFor(route.base)
	route.cache.New = route.newR
	rg.addRoute(route)
	rg.group.Handle(route.base, route.method, route.handle)
}

func (rg *RouterGroup) POST(path string, handlers ...HandlerFunc) {
	rg.Handle(NewRoute("POST", path, false, handlers))
}

func (rg *RouterGroup) GET(path string, handlers ...HandlerFunc) {
	rg.Handle(NewRoute("GET", path, false, handlers))
}

func (rg *RouterGroup) DELETE(path string, handlers ...HandlerFunc) {
	rg.Handle(NewRoute("DELETE", path, false, handlers))
}

func (rg *RouterGroup) PATCH(path string, handlers ...HandlerFunc) {
	rg.Handle(NewRoute("PATCH", path, false, handlers))
}

func (rg *RouterGroup) PUT(path string, handlers ...HandlerFunc) {
	rg.Handle(NewRoute("PUT", path, false, handlers))
}

func (rg *RouterGroup) OPTIONS(path string, handlers ...HandlerFunc) {
	rg.Handle(NewRoute("OPTIONS", path, false, handlers))
}

func (rg *RouterGroup) HEAD(path string, handlers ...HandlerFunc) {
	rg.Handle(NewRoute("HEAD", path, false, handlers))
}

// Adds a Static route handled by the router, based on the group prefix.
func (rg *RouterGroup) STATIC(path string) {
	rg.app.AddStaticDir(pathDropFilepathSplat(path))
	rg.Handle(NewRoute("GET", path, true, []HandlerFunc{handleStatic}))
}
<commit_msg>incorporate local - development workflow for public consumption<commit_after>package flotilla

import (
	"fmt"
	"net/url"
	"path/filepath"
	"regexp"
	"strings"
	"sync"

	"github.com/thrisp/engine"
)

var (
	regParam = regexp.MustCompile(`:[^/#?()\.\\]+|\(\?P<[a-zA-Z0-9]+>.*\)`)
	regSplat = regexp.MustCompile(`\*[^/#?()\.\\]+|\(\?P<[a-zA-Z0-9]+>.*\)`)
)

type (
	// Data about a route for use & reuse within App.
	Route struct {
		cache       sync.Pool
		routergroup *RouterGroup
		static      bool
		method      string
		base        string
		path        string
		handlers    []HandlerFunc
		Name        string
	}

	// A map of Route instances keyed by a string.
	Routes map[string]*Route

	// A RouterGroup is data about gathering any number routes around a prefix
	// and an array of group specific handlers.
	RouterGroup struct {
		app    *App
		prefix string
		//parent   *RouterGroup
		children []*RouterGroup
		routes   Routes
		group    *engine.Group
		Handlers []HandlerFunc
	}

	RouterGroups []*RouterGroup
)

func (r *Route) handle(c *engine.Ctx) {
	rq := r.getR(c)
	rq.Next()
	r.putR(rq)
}

func NewRoute(method string, path string, static bool, handlers []HandlerFunc) *Route {
	r := &Route{method: method, static: static, handlers: handlers}
	if static {
		if fp := strings.Split(path, "/"); fp[len(fp)-1] != "*filepath" {
			r.base = filepath.Join(path, "/*filepath")
		} else {
			r.base = path
		}
	} else {
		r.base = path
	}
	return r
}

func (r *Route) Named() string {
	name := strings.Split(r.path, "/")
	name = append(name, strings.ToLower(r.method))
	for index, value := range name {
		if regSplat.MatchString(value) {
			name[index] = "s"
		}
		if regParam.MatchString(value) {
			name[index] = "p"
		}
	}
	return strings.Join(name, `\`)
}

// Takes string parameters and applies them to a Route. First to any :parameter
// params, then *splat params. If any params are left over(not the case with a
// *splat), and the route method is GET, a query string of key=value is appended
// to the end of the url with arbitrary assigned keys(e.g. value1=param) where
// no key is provided
//
// e.g.
// r1 := NewRoute("GET", /my/:mysterious/path, false, [AHandlerFunc])
// r2 := NewRoute("GET", /my/*path, false, [AHandlerFunc])
// u1, _ := r1.Url("hello", "world=are" "you=there", "sayhi")
// u2, _ := r2.Url("hello", "world", "are" "you", "there")
// fmt.Printf("url1: %s\n", u1)
//
//	/my/hello/path?world=are&you=there&value3=sayhi
//
// fmt.Printf("url2: %s\n", u2)
//
//	/my/hello/world/are/you/there
func (r *Route) Url(params ...string) (*url.URL, error) {
	paramCount := len(params)
	i := 0
	rurl := regParam.ReplaceAllStringFunc(r.path, func(m string) string {
		var val string
		if i < paramCount {
			val = params[i]
		}
		i += 1
		return fmt.Sprintf(`%s`, val)
	})
	rurl = regSplat.ReplaceAllStringFunc(rurl, func(m string) string {
		splat := params[i:(len(params))]
		i += len(splat)
		return fmt.Sprintf(strings.Join(splat, "/"))
	})
	u, err := url.Parse(rurl)
	if err != nil {
		return nil, err
	}
	if i < len(params) && r.method == "GET" {
		providedquerystring := params[i:(len(params))]
		var querystring []string
		qsi := 0
		for qi, qs := range providedquerystring {
			if len(strings.Split(qs, "=")) != 2 {
				qs = fmt.Sprintf("value%d=%s", qi+1, qs)
			}
			querystring = append(querystring, url.QueryEscape(qs))
			qsi += 1
		}
		u.RawQuery = strings.Join(querystring, "&")
	}
	return u, nil
}

func (rg *RouterGroup) combineHandlers(handlers []HandlerFunc) []HandlerFunc {
	s := len(rg.Handlers) + len(handlers)
	h := make([]HandlerFunc, 0, s)
	h = append(h, rg.Handlers...)
	h = append(h, handlers...)
	return h
}

func (rg *RouterGroup) handlerExists(outside HandlerFunc) bool {
	for _, inside := range rg.Handlers {
		if funcEqual(inside, outside) {
			return true
		}
	}
	return false
}

func (rg *RouterGroup) pathFor(path string) string {
	joined := filepath.Join(rg.prefix, path)
	// Append a '/' if the last component had one, but only if it's not there already
	if len(path) > 0 && path[len(path)-1] == '/' && joined[len(joined)-1] != '/' {
		return joined + "/"
	}
	return joined
}

func (rg *RouterGroup) pathNoLeadingSlash(path string) string {
	return strings.TrimLeft(strings.Join([]string{rg.prefix, path}, "/"), "/")
}

// NewRouterGroup attaches a new RouterGroup to the App with the prefix.
func NewRouterGroup(prefix string, app *App) *RouterGroup {
	return &RouterGroup{prefix: prefix,
		app:    app,
		group:  app.engine.Group.New(prefix),
		routes: make(Routes),
	}
}

// New Creates a new child router group with the base component string.
func (rg *RouterGroup) New(component string, handlers ...HandlerFunc) *RouterGroup {
	prefix := rg.pathFor(component)

	newrg := NewRouterGroup(prefix, rg.app)
	//newrg.parent = rg
	newrg.Handlers = rg.combineHandlers(handlers)

	rg.children = append(rg.children, newrg)

	return newrg
}

// Use adds any number of HandlerFunc to the RouterGroup which will be run before
// route handlers for all Route attached to the RouterGroup.
func (rg *RouterGroup) Use(middlewares ...HandlerFunc) {
	for _, handler := range middlewares {
		if !rg.handlerExists(handler) {
			rg.Handlers = append(rg.Handlers, handler)
		}
	}
}

// UseAt adds any number of HandlerFunc to the RouterGroup as middleware when you
// must control the position in relation to other middleware.
func (rg *RouterGroup) UseAt(index int, middlewares ...HandlerFunc) {
	if index > len(rg.Handlers) {
		rg.Use(middlewares...)
		return
	}

	var newh []HandlerFunc

	for _, handler := range middlewares {
		if !rg.handlerExists(handler) {
			newh = append(newh, handler)
		}
	}

	before := rg.Handlers[:index]
	after := append(newh, rg.Handlers[index:]...)
	rg.Handlers = append(before, after...)
}

func (rg *RouterGroup) addRoute(r *Route) {
	if r.Name != "" {
		rg.routes[r.Name] = r
	} else {
		rg.routes[r.Named()] = r
	}
}

// Handle registers new handlers and/or middlewares with the given path and
// method. For GET, POST, PUT, PATCH and DELETE requests the respective shortcut
// functions can be used.
func (rg *RouterGroup) Handle(route *Route) {
	// finalize Route with RouterGroup specific information
	route.routergroup = rg
	route.handlers = rg.combineHandlers(route.handlers)
	route.path = rg.pathFor(route.base)
	route.cache.New = route.newR
	rg.addRoute(route)
	rg.group.Handle(route.base, route.method, route.handle)
}

func (rg *RouterGroup) POST(path string, handlers ...HandlerFunc) {
	rg.Handle(NewRoute("POST", path, false, handlers))
}

func (rg *RouterGroup) GET(path string, handlers ...HandlerFunc) {
	rg.Handle(NewRoute("GET", path, false, handlers))
}

func (rg *RouterGroup) DELETE(path string, handlers ...HandlerFunc) {
	rg.Handle(NewRoute("DELETE", path, false, handlers))
}

func (rg *RouterGroup) PATCH(path string, handlers ...HandlerFunc) {
	rg.Handle(NewRoute("PATCH", path, false, handlers))
}

func (rg *RouterGroup) PUT(path string, handlers ...HandlerFunc) {
	rg.Handle(NewRoute("PUT", path, false, handlers))
}

func (rg *RouterGroup) OPTIONS(path string, handlers ...HandlerFunc) {
	rg.Handle(NewRoute("OPTIONS", path, false, handlers))
}

func (rg *RouterGroup) HEAD(path string, handlers ...HandlerFunc) {
	rg.Handle(NewRoute("HEAD", path, false, handlers))
}

// Adds a Static route handled by the router, based on the group prefix.
func (rg *RouterGroup) STATIC(path string) {
	rg.app.AddStaticDir(pathDropFilepathSplat(path))
	rg.Handle(NewRoute("GET", path, true, []HandlerFunc{handleStatic}))
}
<|endoftext|>