<commit_before>/*
 Copyright 2020 Qiniu Cloud (qiniu.com)

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

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

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

package golang

import (
	"go/ast"
	"go/token"
	"log"
	"reflect"
	"strconv"
	"strings"

	"github.com/qiniu/qlang/v6/exec.spec"
)

// -----------------------------------------------------------------------------

// Ident instr
func (p *Builder) Ident(name string) *Builder {
	p.rhs.Push(&ast.Ident{Name: name})
	return p
}

// Ident - ast.Ident
func Ident(name string) *ast.Ident {
	return &ast.Ident{Name: name}
}

// GoFuncIdent - ast.Ident or ast.SelectorExpr
func (p *Builder) GoFuncIdent(pkgPath, name string) ast.Expr {
	if pkgPath == "" {
		return Ident(name)
	}
	pkg := p.Import(pkgPath)
	if strings.HasPrefix(name, "(") { // eg. name = "(*Replacer).Replace"
		pos := strings.LastIndexByte(name, '*')
		if pos == -1 {
			pos = 1
		} else {
			pos++
		}
		name = name[:pos] + pkg + "." + name[pos:]
		return Ident(name)
	}
	return &ast.SelectorExpr{
		X:   Ident(pkg),
		Sel: Ident(name),
	}
}

// StringConst - ast.BasicLit
func StringConst(v string) *ast.BasicLit {
	return &ast.BasicLit{
		Kind:  token.STRING,
		Value: strconv.Quote(v),
	}
}

// IntConst - ast.BasicLit
func IntConst(v int64) *ast.BasicLit {
	return &ast.BasicLit{
		Kind:  token.INT,
		Value: strconv.FormatInt(v, 10),
	}
}

// UintConst instr
func UintConst(v uint64) *ast.BasicLit {
	return &ast.BasicLit{
		Kind:  token.INT,
		Value: strconv.FormatUint(v, 10),
	}
}

// FloatConst instr
func FloatConst(v float64) *ast.BasicLit {
	return &ast.BasicLit{
		Kind:  token.FLOAT,
		Value: strconv.FormatFloat(v, 'g', -1, 64),
	}
}

// ImagConst instr
func ImagConst(v float64) *ast.BasicLit {
	return &ast.BasicLit{
		Kind:  token.IMAG,
		Value: strconv.FormatFloat(v, 'g', -1, 64) + "i",
	}
}

// ComplexConst instr
func ComplexConst(v complex128) ast.Expr {
	r, i := real(v), imag(v)
	x, y := FloatConst(r), ImagConst(i)
	return &ast.ParenExpr{X: BinaryOp(token.ADD, x, y)}
}

// Const instr
func Const(p *Builder, val interface{}) ast.Expr {
	if val == nil {
		return Ident("nil")
	}
	v := reflect.ValueOf(val)
	kind := v.Kind()
	if kind == reflect.String {
		return StringConst(val.(string))
	}
	if kind >= reflect.Int && kind <= reflect.Int64 {
		var expr ast.Expr = IntConst(v.Int())
		if t := v.Type(); t != exec.TyInt {
			expr = TypeCast(p, expr, exec.TyInt, t)
		}
		return expr
	}
	if kind >= reflect.Uint && kind <= reflect.Uintptr {
		var expr ast.Expr = UintConst(v.Uint())
		return TypeCast(p, expr, exec.TyInt, v.Type())
	}
	if kind >= reflect.Float32 && kind <= reflect.Float64 {
		var expr ast.Expr = FloatConst(v.Float())
		if t := v.Type(); t != exec.TyFloat64 {
			expr = TypeCast(p, expr, exec.TyFloat64, t)
		}
		return expr
	}
	if kind >= reflect.Complex64 && kind <= reflect.Complex128 {
		var expr ast.Expr = ComplexConst(v.Complex())
		if t := v.Type(); t != exec.TyComplex128 {
			expr = TypeCast(p, expr, exec.TyComplex128, t)
		}
		return expr
	}
	if kind == reflect.Bool {
		if val.(bool) {
			return Ident("true")
		}
		return Ident("false")
	}
	log.Panicln("Const: value type is unknown -", v.Type())
	return nil
}

// Push instr
func (p *Builder) Push(val interface{}) *Builder {
	p.rhs.Push(Const(p, val))
	return p
}

// UnaryOp instr
func (p *Builder) UnaryOp(tok token.Token) *Builder {
	x := p.rhs.Pop().(ast.Expr)
	p.rhs.Push(&ast.UnaryExpr{Op: tok, X: x})
	return p
}

// BinaryOp instr
func (p *Builder) BinaryOp(tok token.Token) *Builder {
	y := p.rhs.Pop().(ast.Expr)
	x := p.rhs.Pop().(ast.Expr)
	p.rhs.Push(&ast.BinaryExpr{Op: tok, X: x, Y: y})
	return p
}

// BinaryOp instr
func BinaryOp(tok token.Token, x, y ast.Expr) *ast.BinaryExpr {
	return &ast.BinaryExpr{Op: tok, X: x, Y: y}
}

// BuiltinOp instr
func (p *Builder) BuiltinOp(kind exec.Kind, op exec.Operator) *Builder {
	tok := opTokens[op]
	if tok == token.ILLEGAL {
		log.Panicln("BuiltinOp: unsupported op -", op)
	}
	oi := op.GetInfo()
	if oi.InSecond == 0 {
		return p.UnaryOp(tok)
	}
	return p.BinaryOp(tok)
}

var opTokens = [...]token.Token{
	exec.OpAdd:       token.ADD,
	exec.OpSub:       token.SUB,
	exec.OpMul:       token.MUL,
	exec.OpDiv:       token.QUO,
	exec.OpMod:       token.REM,
	exec.OpBitAnd:    token.AND,
	exec.OpBitOr:     token.OR,
	exec.OpBitXor:    token.XOR,
	exec.OpBitAndNot: token.AND_NOT,
	exec.OpBitSHL:    token.SHL,
	exec.OpBitSHR:    token.SHR,
	exec.OpLT:        token.LSS,
	exec.OpLE:        token.LEQ,
	exec.OpGT:        token.GTR,
	exec.OpGE:        token.GEQ,
	exec.OpEQ:        token.EQL,
	exec.OpEQNil:     token.ILLEGAL,
	exec.OpNE:        token.NEQ,
	exec.OpNENil:     token.ILLEGAL,
	exec.OpLAnd:      token.LAND,
	exec.OpLOr:       token.LOR,
	exec.OpNeg:       token.SUB,
	exec.OpNot:       token.NOT,
	exec.OpBitNot:    token.XOR,
}

// TypeCast instr
func (p *Builder) TypeCast(from, to reflect.Type) *Builder {
	x := p.rhs.Pop().(ast.Expr)
	TypeCast(p, x, from, to)
	return p
}

// TypeCast instr
func TypeCast(p *Builder, x ast.Expr, from, to reflect.Type) *ast.CallExpr {
	t := Type(p, to)
	return &ast.CallExpr{
		Fun:  t,
		Args: []ast.Expr{x},
	}
}

// Call instr
func (p *Builder) Call(narg int, ellipsis bool, args ...ast.Expr) *Builder {
	fun := p.rhs.Pop().(ast.Expr)
	for _, item := range p.rhs.GetArgs(narg) {
		args = append(args, item.(ast.Expr))
	}
	p.rhs.PopN(narg)
	expr := &ast.CallExpr{Fun: fun, Args: args}
	if ellipsis {
		expr.Ellipsis++
	}
	p.rhs.Push(expr)
	return p
}

// CallGoFunc instr
func (p *Builder) CallGoFunc(fun exec.GoFuncAddr) *Builder {
	gfi := defaultImpl.GetGoFuncInfo(fun)
	pkgPath, name := gfi.Pkg.PkgPath(), gfi.Name
	fn := p.GoFuncIdent(pkgPath, name)
	p.rhs.Push(fn)
	arity := reflect.TypeOf(gfi.This).NumIn()
	return p.Call(arity, false)
}

// CallGoFuncv instr
func (p *Builder) CallGoFuncv(fun exec.GoFuncvAddr, arity int) *Builder {
	gfi := defaultImpl.GetGoFuncvInfo(fun)
	pkgPath, name := gfi.Pkg.PkgPath(), gfi.Name
	if pkgPath == "" {
		if alias, ok := builtinFnvs[name]; ok {
			pkgPath, name = alias[0], alias[1]
		}
	}
	fn := p.GoFuncIdent(pkgPath, name)
	p.rhs.Push(fn)
	ellipsis := arity == -1
	if ellipsis {
		arity = reflect.TypeOf(gfi.This).NumIn()
	}
	return p.Call(arity, ellipsis)
}

var builtinFnvs = map[string][2]string{
	"print":   {"fmt", "Print"},
	"printf":  {"fmt", "Printf"},
	"println": {"fmt", "Println"},
	"fprintf": {"fmt", "Fprintf"},
}

// Append instr
func (p *Builder) Append(typ reflect.Type, arity int) *Builder {
	p.rhs.Push(Ident("append"))
	var ellipsis bool
	if arity == -1 {
		ellipsis = true
		arity = 2
	}
	p.Call(arity, ellipsis)
	return p
}

// Make instr
func (p *Builder) Make(typ reflect.Type, arity int) *Builder {
	p.rhs.Push(Ident("make"))
	p.Call(arity, false, Type(p, typ))
	return p
}

// MakeArray instr
func (p *Builder) MakeArray(typ reflect.Type, arity int) *Builder {
	typExpr := Type(p, typ)
	elts := make([]ast.Expr, arity)
	for i, v := range p.rhs.GetArgs(arity) {
		elts[i] = v.(ast.Expr)
	}
	p.rhs.Ret(arity, &ast.CompositeLit{
		Type: typExpr,
		Elts: elts,
	})
	return p
}

// MakeMap instr
func (p *Builder) MakeMap(typ reflect.Type, arity int) *Builder {
	typExpr := Type(p, typ)
	elts := make([]ast.Expr, arity)
	args := p.rhs.GetArgs(arity << 1)
	for i := 0; i < arity; i++ {
		elts[i] = &ast.KeyValueExpr{
			Key:   args[i<<1].(ast.Expr),
			Value: args[(i<<1)+1].(ast.Expr),
		}
	}
	p.rhs.Ret(arity<<1, &ast.CompositeLit{
		Type: typExpr,
		Elts: elts,
	})
	return p
}

// MapIndex instr
func (p *Builder) MapIndex() *Builder {
	p.rhs.Push(Index(p))
	return p
}

// SetMapIndex instr
func (p *Builder) SetMapIndex() *Builder {
	p.lhs.Push(Index(p))
	return p
}

// Index instr
func (p *Builder) Index(idx int) *Builder {
	p.rhs.Push(IndexWith(p, idx))
	return p
}

// SetIndex instr
func (p *Builder) SetIndex(idx int) *Builder {
	p.lhs.Push(IndexWith(p, idx))
	return p
}

// Index instr
func Index(p *Builder) *ast.IndexExpr {
	idx := p.rhs.Pop().(ast.Expr)
	x := p.rhs.Pop().(ast.Expr)
	return &ast.IndexExpr{
		X:     x,
		Index: idx,
	}
}

// IndexWith instr
func IndexWith(p *Builder, idx int) *ast.IndexExpr {
	if idx == -1 {
		return Index(p)
	}
	x := p.rhs.Pop().(ast.Expr)
	return &ast.IndexExpr{
		X:     x,
		Index: IntConst(int64(idx)),
	}
}

// Slice instr
func (p *Builder) Slice(i, j int) *Builder {
	log.Panicln("todo")
	return p
}

// Slice3 instr
func (p *Builder) Slice3(i, j, k int) *Builder {
	log.Panicln("todo")
	return p
}

// Zero instr
func (p *Builder) Zero(typ reflect.Type) *Builder {
	p.rhs.Push(Zero(p, typ))
	return p
}

// Zero instr
func Zero(p *Builder, typ reflect.Type) ast.Expr {
	kind := typ.Kind()
	if kind == reflect.String {
		return StringConst("")
	}
	if kind >= reflect.Int && kind <= reflect.Complex128 {
		return IntConst(0)
	}
	if kind == reflect.Bool {
		return Ident("false")
	}
	log.Panicln("Zero: unknown -", typ)
	return nil
}

// GoBuiltin instr
func (p *Builder) GoBuiltin(typ reflect.Type, op exec.GoBuiltin) *Builder {
	arity := goBuiltinArities[op]
	p.rhs.Push(Ident(op.String()))
	return p.Call(arity, false)
}

var goBuiltinArities = [...]int{
	exec.GobLen:     1,
	exec.GobCap:     1,
	exec.GobCopy:    2,
	exec.GobDelete:  2,
	exec.GobComplex: 2,
	exec.GobReal:    1,
	exec.GobImag:    1,
	exec.GobClose:   1,
}

// -----------------------------------------------------------------------------
<commit_msg>Slice/Slice3<commit_after>/*
 Copyright 2020 Qiniu Cloud (qiniu.com)

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

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

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

package golang

import (
	"go/ast"
	"go/token"
	"log"
	"reflect"
	"strconv"
	"strings"

	"github.com/qiniu/qlang/v6/exec.spec"
)

// -----------------------------------------------------------------------------

// Ident instr
func (p *Builder) Ident(name string) *Builder {
	p.rhs.Push(&ast.Ident{Name: name})
	return p
}

// Ident - ast.Ident
func Ident(name string) *ast.Ident {
	return &ast.Ident{Name: name}
}

// GoFuncIdent - ast.Ident or ast.SelectorExpr
func (p *Builder) GoFuncIdent(pkgPath, name string) ast.Expr {
	if pkgPath == "" {
		return Ident(name)
	}
	pkg := p.Import(pkgPath)
	if strings.HasPrefix(name, "(") { // eg. name = "(*Replacer).Replace"
		pos := strings.LastIndexByte(name, '*')
		if pos == -1 {
			pos = 1
		} else {
			pos++
		}
		name = name[:pos] + pkg + "." + name[pos:]
		return Ident(name)
	}
	return &ast.SelectorExpr{
		X:   Ident(pkg),
		Sel: Ident(name),
	}
}

// StringConst - ast.BasicLit
func StringConst(v string) *ast.BasicLit {
	return &ast.BasicLit{
		Kind:  token.STRING,
		Value: strconv.Quote(v),
	}
}

// IntConst - ast.BasicLit
func IntConst(v int64) *ast.BasicLit {
	return &ast.BasicLit{
		Kind:  token.INT,
		Value: strconv.FormatInt(v, 10),
	}
}

// UintConst instr
func UintConst(v uint64) *ast.BasicLit {
	return &ast.BasicLit{
		Kind:  token.INT,
		Value: strconv.FormatUint(v, 10),
	}
}

// FloatConst instr
func FloatConst(v float64) *ast.BasicLit {
	return &ast.BasicLit{
		Kind:  token.FLOAT,
		Value: strconv.FormatFloat(v, 'g', -1, 64),
	}
}

// ImagConst instr
func ImagConst(v float64) *ast.BasicLit {
	return &ast.BasicLit{
		Kind:  token.IMAG,
		Value: strconv.FormatFloat(v, 'g', -1, 64) + "i",
	}
}

// ComplexConst instr
func ComplexConst(v complex128) ast.Expr {
	r, i := real(v), imag(v)
	x, y := FloatConst(r), ImagConst(i)
	return &ast.ParenExpr{X: BinaryOp(token.ADD, x, y)}
}

// Const instr
func Const(p *Builder, val interface{}) ast.Expr {
	if val == nil {
		return Ident("nil")
	}
	v := reflect.ValueOf(val)
	kind := v.Kind()
	if kind == reflect.String {
		return StringConst(val.(string))
	}
	if kind >= reflect.Int && kind <= reflect.Int64 {
		var expr ast.Expr = IntConst(v.Int())
		if t := v.Type(); t != exec.TyInt {
			expr = TypeCast(p, expr, exec.TyInt, t)
		}
		return expr
	}
	if kind >= reflect.Uint && kind <= reflect.Uintptr {
		var expr ast.Expr = UintConst(v.Uint())
		return TypeCast(p, expr, exec.TyInt, v.Type())
	}
	if kind >= reflect.Float32 && kind <= reflect.Float64 {
		var expr ast.Expr = FloatConst(v.Float())
		if t := v.Type(); t != exec.TyFloat64 {
			expr = TypeCast(p, expr, exec.TyFloat64, t)
		}
		return expr
	}
	if kind >= reflect.Complex64 && kind <= reflect.Complex128 {
		var expr ast.Expr = ComplexConst(v.Complex())
		if t := v.Type(); t != exec.TyComplex128 {
			expr = TypeCast(p, expr, exec.TyComplex128, t)
		}
		return expr
	}
	if kind == reflect.Bool {
		if val.(bool) {
			return Ident("true")
		}
		return Ident("false")
	}
	log.Panicln("Const: value type is unknown -", v.Type())
	return nil
}

// Push instr
func (p *Builder) Push(val interface{}) *Builder {
	p.rhs.Push(Const(p, val))
	return p
}

// UnaryOp instr
func (p *Builder) UnaryOp(tok token.Token) *Builder {
	x := p.rhs.Pop().(ast.Expr)
	p.rhs.Push(&ast.UnaryExpr{Op: tok, X: x})
	return p
}

// BinaryOp instr
func (p *Builder) BinaryOp(tok token.Token) *Builder {
	y := p.rhs.Pop().(ast.Expr)
	x := p.rhs.Pop().(ast.Expr)
	p.rhs.Push(&ast.BinaryExpr{Op: tok, X: x, Y: y})
	return p
}

// BinaryOp instr
func BinaryOp(tok token.Token, x, y ast.Expr) *ast.BinaryExpr {
	return &ast.BinaryExpr{Op: tok, X: x, Y: y}
}

// BuiltinOp instr
func (p *Builder) BuiltinOp(kind exec.Kind, op exec.Operator) *Builder {
	tok := opTokens[op]
	if tok == token.ILLEGAL {
		log.Panicln("BuiltinOp: unsupported op -", op)
	}
	oi := op.GetInfo()
	if oi.InSecond == 0 {
		return p.UnaryOp(tok)
	}
	return p.BinaryOp(tok)
}

var opTokens = [...]token.Token{
	exec.OpAdd:       token.ADD,
	exec.OpSub:       token.SUB,
	exec.OpMul:       token.MUL,
	exec.OpDiv:       token.QUO,
	exec.OpMod:       token.REM,
	exec.OpBitAnd:    token.AND,
	exec.OpBitOr:     token.OR,
	exec.OpBitXor:    token.XOR,
	exec.OpBitAndNot: token.AND_NOT,
	exec.OpBitSHL:    token.SHL,
	exec.OpBitSHR:    token.SHR,
	exec.OpLT:        token.LSS,
	exec.OpLE:        token.LEQ,
	exec.OpGT:        token.GTR,
	exec.OpGE:        token.GEQ,
	exec.OpEQ:        token.EQL,
	exec.OpEQNil:     token.ILLEGAL,
	exec.OpNE:        token.NEQ,
	exec.OpNENil:     token.ILLEGAL,
	exec.OpLAnd:      token.LAND,
	exec.OpLOr:       token.LOR,
	exec.OpNeg:       token.SUB,
	exec.OpNot:       token.NOT,
	exec.OpBitNot:    token.XOR,
}

// TypeCast instr
func (p *Builder) TypeCast(from, to reflect.Type) *Builder {
	x := p.rhs.Pop().(ast.Expr)
	TypeCast(p, x, from, to)
	return p
}

// TypeCast instr
func TypeCast(p *Builder, x ast.Expr, from, to reflect.Type) *ast.CallExpr {
	t := Type(p, to)
	return &ast.CallExpr{
		Fun:  t,
		Args: []ast.Expr{x},
	}
}

// Call instr
func (p *Builder) Call(narg int, ellipsis bool, args ...ast.Expr) *Builder {
	fun := p.rhs.Pop().(ast.Expr)
	for _, item := range p.rhs.GetArgs(narg) {
		args = append(args, item.(ast.Expr))
	}
	p.rhs.PopN(narg)
	expr := &ast.CallExpr{Fun: fun, Args: args}
	if ellipsis {
		expr.Ellipsis++
	}
	p.rhs.Push(expr)
	return p
}

// CallGoFunc instr
func (p *Builder) CallGoFunc(fun exec.GoFuncAddr) *Builder {
	gfi := defaultImpl.GetGoFuncInfo(fun)
	pkgPath, name := gfi.Pkg.PkgPath(), gfi.Name
	fn := p.GoFuncIdent(pkgPath, name)
	p.rhs.Push(fn)
	arity := reflect.TypeOf(gfi.This).NumIn()
	return p.Call(arity, false)
}

// CallGoFuncv instr
func (p *Builder) CallGoFuncv(fun exec.GoFuncvAddr, arity int) *Builder {
	gfi := defaultImpl.GetGoFuncvInfo(fun)
	pkgPath, name := gfi.Pkg.PkgPath(), gfi.Name
	if pkgPath == "" {
		if alias, ok := builtinFnvs[name]; ok {
			pkgPath, name = alias[0], alias[1]
		}
	}
	fn := p.GoFuncIdent(pkgPath, name)
	p.rhs.Push(fn)
	ellipsis := arity == -1
	if ellipsis {
		arity = reflect.TypeOf(gfi.This).NumIn()
	}
	return p.Call(arity, ellipsis)
}

var builtinFnvs = map[string][2]string{
	"print":   {"fmt", "Print"},
	"printf":  {"fmt", "Printf"},
	"println": {"fmt", "Println"},
	"fprintf": {"fmt", "Fprintf"},
}

// Append instr
func (p *Builder) Append(typ reflect.Type, arity int) *Builder {
	p.rhs.Push(Ident("append"))
	var ellipsis bool
	if arity == -1 {
		ellipsis = true
		arity = 2
	}
	p.Call(arity, ellipsis)
	return p
}

// Make instr
func (p *Builder) Make(typ reflect.Type, arity int) *Builder {
	p.rhs.Push(Ident("make"))
	p.Call(arity, false, Type(p, typ))
	return p
}

// MakeArray instr
func (p *Builder) MakeArray(typ reflect.Type, arity int) *Builder {
	typExpr := Type(p, typ)
	elts := make([]ast.Expr, arity)
	for i, v := range p.rhs.GetArgs(arity) {
		elts[i] = v.(ast.Expr)
	}
	p.rhs.Ret(arity, &ast.CompositeLit{
		Type: typExpr,
		Elts: elts,
	})
	return p
}

// MakeMap instr
func (p *Builder) MakeMap(typ reflect.Type, arity int) *Builder {
	typExpr := Type(p, typ)
	elts := make([]ast.Expr, arity)
	args := p.rhs.GetArgs(arity << 1)
	for i := 0; i < arity; i++ {
		elts[i] = &ast.KeyValueExpr{
			Key:   args[i<<1].(ast.Expr),
			Value: args[(i<<1)+1].(ast.Expr),
		}
	}
	p.rhs.Ret(arity<<1, &ast.CompositeLit{
		Type: typExpr,
		Elts: elts,
	})
	return p
}

// MapIndex instr
func (p *Builder) MapIndex() *Builder {
	p.rhs.Push(Index(p))
	return p
}

// SetMapIndex instr
func (p *Builder) SetMapIndex() *Builder {
	p.lhs.Push(Index(p))
	return p
}

// Index instr
func (p *Builder) Index(idx int) *Builder {
	p.rhs.Push(IndexWith(p, idx))
	return p
}

// SetIndex instr
func (p *Builder) SetIndex(idx int) *Builder {
	p.lhs.Push(IndexWith(p, idx))
	return p
}

// Index instr
func Index(p *Builder) *ast.IndexExpr {
	idx := p.rhs.Pop().(ast.Expr)
	x := p.rhs.Pop().(ast.Expr)
	return &ast.IndexExpr{
		X:     x,
		Index: idx,
	}
}

// IndexWith instr
func IndexWith(p *Builder, idx int) *ast.IndexExpr {
	if idx == -1 {
		return Index(p)
	}
	x := p.rhs.Pop().(ast.Expr)
	return &ast.IndexExpr{
		X:     x,
		Index: IntConst(int64(idx)),
	}
}

// Slice instr
func (p *Builder) Slice(i, j int) *Builder {
	jExpr := SliceIndex(p, j)
	iExpr := SliceIndex(p, i)
	p.rhs.Push(&ast.SliceExpr{
		X:    p.rhs.Pop().(ast.Expr),
		Low:  iExpr,
		High: jExpr,
	})
	return p
}

// Slice3 instr
func (p *Builder) Slice3(i, j, k int) *Builder {
	kExpr := SliceIndex(p, k)
	jExpr := SliceIndex(p, j)
	iExpr := SliceIndex(p, i)
	p.rhs.Push(&ast.SliceExpr{
		X:      p.rhs.Pop().(ast.Expr),
		Low:    iExpr,
		High:   jExpr,
		Max:    kExpr,
		Slice3: true,
	})
	return p
}

// SliceIndex instr
func SliceIndex(p *Builder, idx int) ast.Expr {
	if idx == exec.SliceDefaultIndex {
		return nil
	}
	if idx == -1 {
		return p.rhs.Pop().(ast.Expr)
	}
	return IntConst(int64(idx))
}

// Zero instr
func (p *Builder) Zero(typ reflect.Type) *Builder {
	p.rhs.Push(Zero(p, typ))
	return p
}

// Zero instr
func Zero(p *Builder, typ reflect.Type) ast.Expr {
	kind := typ.Kind()
	if kind == reflect.String {
		return StringConst("")
	}
	if kind >= reflect.Int && kind <= reflect.Complex128 {
		return IntConst(0)
	}
	if kind == reflect.Bool {
		return Ident("false")
	}
	log.Panicln("Zero: unknown -", typ)
	return nil
}

// GoBuiltin instr
func (p *Builder) GoBuiltin(typ reflect.Type, op exec.GoBuiltin) *Builder {
	arity := goBuiltinArities[op]
	p.rhs.Push(Ident(op.String()))
	return p.Call(arity, false)
}

var goBuiltinArities = [...]int{
	exec.GobLen:     1,
	exec.GobCap:     1,
	exec.GobCopy:    2,
	exec.GobDelete:  2,
	exec.GobComplex: 2,
	exec.GobReal:    1,
	exec.GobImag:    1,
	exec.GobClose:   1,
}

// -----------------------------------------------------------------------------
<|endoftext|>


<commit_before>package mapgen

import (
	"buildblast/lib/coords"
	"buildblast/lib/mapgen/noise"
)

type SimplexHills struct {
	simplexNoise *noise.SimplexNoise
}

func NewSimplexHills(seed int64) *SimplexHills {
	sh := new(SimplexHills)
	sh.simplexNoise = noise.NewSimplexNoise(1024, 0.4, seed)
	return sh
}

func (sh *SimplexHills) heightAt(x, z float64) float64 {

	height := sh.simplexNoise.Noise2(x, z)
	return height*50 + float64(coords.ChunkHeight)/2
}

func (sh *SimplexHills) Block(bc coords.Block) Block {
	if bc.X == 0 && bc.Y == 16 && bc.Z == 0 {
		return BLOCK_SPAWN
	}
	height := int(sh.heightAt(float64(bc.X), float64(bc.Z)))
	if height > bc.Y {
		return BLOCK_GRASS
	}
	return BLOCK_AIR
}

func (sh *SimplexHills) Chunk(cc coords.Chunk) *Chunk {
	return generateChunk(sh, cc)
}
<commit_msg>Add some dirt and stone<commit_after>package mapgen

import (
	"buildblast/lib/coords"
	"buildblast/lib/mapgen/noise"
)

type SimplexHills struct {
	simplexNoise *noise.SimplexNoise
}

func NewSimplexHills(seed int64) *SimplexHills {
	sh := new(SimplexHills)
	sh.simplexNoise = noise.NewSimplexNoise(1024, 0.4, seed)
	return sh
}

func (sh *SimplexHills) heightAt(x, z float64) float64 {

	height := sh.simplexNoise.Noise2(x, z)
	return height*50 + float64(coords.ChunkHeight)/2
}

func (sh *SimplexHills) Block(bc coords.Block) Block {
	if bc.X == 0 && bc.Y == 16 && bc.Z == 0 {
		return BLOCK_SPAWN
	}
	height := int(sh.heightAt(float64(bc.X), float64(bc.Z)))
	if bc.Y == height {
		return BLOCK_GRASS
	}
	if bc.Y < height - 3 {
		return BLOCK_STONE
	}
	if height > bc.Y {
		return BLOCK_DIRT
	}
	return BLOCK_AIR
}

func (sh *SimplexHills) Chunk(cc coords.Chunk) *Chunk {
	return generateChunk(sh, cc)
}
<|endoftext|>


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

// Package agent implements a service that runs on a serviced node. It is
// responsible for ensuring that a particular node is running the correct services
// and reporting the state and health of those services back to the master
// serviced.

package isvcs

import (
	"github.com/control-center/serviced/utils"
	"github.com/zenoss/elastigo/cluster"
	"github.com/zenoss/glog"

	"fmt"
	"net/http"
	"os"
	"path"
	"strconv"
	"time"
)

var elasticsearch_logstash *Container
var elasticsearch_serviced *Container

func init() {
	var serviceName string
	var err error

	serviceName = "elasticsearch-serviced"
	elasticsearch_serviced, err = NewContainer(
		ContainerDescription{
			Name:          serviceName,
			Repo:          IMAGE_REPO,
			Tag:           IMAGE_TAG,
			Command:       func() string { return "" },
			Ports:         []int{9200},
			Volumes:       map[string]string{"data": "/opt/elasticsearch-0.90.9/data"},
			Configuration: make(map[string]interface{}),
			HealthCheck:   elasticsearchHealthCheck(9200),
		},
	)
	if err != nil {
		glog.Fatal("Error initializing elasticsearch container: %s", err)
	}
	elasticsearch_serviced.Command = func() string {
		clusterArg := ""
		if clusterName, ok := elasticsearch_serviced.Configuration["cluster"]; ok {
			clusterArg = fmt.Sprintf(" -Des.cluster.name=%s ", clusterName)
		}
		return fmt.Sprintf(`/opt/elasticsearch-0.90.9/bin/elasticsearch -f -Des.node.name=%s %s`, elasticsearch_serviced.Name, clusterArg)
	}

	serviceName = "elasticsearch-logstash"
	elasticsearch_logstash, err = NewContainer(
		ContainerDescription{
			Name:          serviceName,
			Repo:          IMAGE_REPO,
			Tag:           IMAGE_TAG,
			Command:       func() string { return "" },
			Ports:         []int{9100},
			Volumes:       map[string]string{"data": "/opt/elasticsearch-1.3.1/data"},
			Configuration: make(map[string]interface{}),
			HealthCheck:   elasticsearchHealthCheck(9100),
		},
	)
	if err != nil {
		glog.Fatal("Error initializing elasticsearch container: %s", err)
	}
	envPerService[serviceName]["ES_JAVA_OPTS"]="-Xmx4g"
	elasticsearch_logstash.Command = func() string {
		clusterArg := ""
		if clusterName, ok := elasticsearch_logstash.Configuration["cluster"]; ok {
			clusterArg = fmt.Sprintf(" -Des.cluster.name=%s ", clusterName)
		}
		return fmt.Sprintf(`/opt/elasticsearch-1.3.1/bin/elasticsearch -Des.node.name=%s %s`, elasticsearch_logstash.Name, clusterArg)
	}
}

// getEnvVarInt() returns the env var as an int value or the defaultValue if env var is unset
func getEnvVarInt(envVar string, defaultValue int) int {
	envVarValue := os.Getenv(envVar)
	if len(envVarValue) > 0 {
		if value, err := strconv.Atoi(envVarValue); err != nil {
			glog.Errorf("Could not convert env var %s:%s to integer, error:%s", envVar, envVarValue, err)
			return defaultValue
		} else {
			return value
		}
	}
	return defaultValue
}

// elasticsearchHealthCheck() determines if elasticsearch is healthy
func elasticsearchHealthCheck(port int) func() error {
	return func() error {
		start := time.Now()
		lastError := time.Now()
		minUptime := time.Second * 2
		timeout := time.Second * time.Duration(getEnvVarInt("ES_STARTUP_TIMEOUT", 600))
		baseUrl := fmt.Sprintf("http://localhost:%d", port)

		schemaFile := path.Join(utils.ResourcesDir(), "controlplane.json")

		for {
			if healthResponse, err := cluster.Health(true); err == nil && (healthResponse.Status == "green" || healthResponse.Status == "yellow") {
				if buffer, err := os.Open(schemaFile); err != nil {
					glog.Fatalf("problem reading %s", err)
					return err
				} else {
					http.Post(baseUrl+"/controlplane", "application/json", buffer)
					buffer.Close()
				}
			} else {
				lastError = time.Now()
				glog.V(2).Infof("Still trying to connect to elasticsearch container: %v: %s", err, healthResponse)
			}
			if time.Since(lastError) > minUptime {
				break
			}
			if time.Since(start) > timeout {
				return fmt.Errorf("Could not startup elasticsearch container.  waited timeout:%v", timeout)
			}
			time.Sleep(time.Millisecond * 1000)
		}
		glog.Info("elasticsearch container started, browser at %s/_plugin/head/", baseUrl)
		return nil
	}
}

func PurgeLogstashIndices(days int) error {
	container := elasticsearch_logstash
	port := container.Ports[0]
	glog.Infof("Purging logstash entries older than %d days", days)
	return container.RunCommand([]string{
		"/usr/local/bin/curator", "--port", fmt.Sprintf("%d", port),
		"delete", "--older-than", fmt.Sprintf("%d", days)}, false)
}
<commit_msg>fixed typo for elasticsearch url<commit_after>// Copyright 2014 The Serviced Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package agent implements a service that runs on a serviced node. It is
// responsible for ensuring that a particular node is running the correct services
// and reporting the state and health of those services back to the master
// serviced.

package isvcs

import (
	"github.com/control-center/serviced/utils"
	"github.com/zenoss/elastigo/cluster"
	"github.com/zenoss/glog"

	"fmt"
	"net/http"
	"os"
	"path"
	"strconv"
	"time"
)

var elasticsearch_logstash *Container
var elasticsearch_serviced *Container

func init() {
	var serviceName string
	var err error

	serviceName = "elasticsearch-serviced"
	elasticsearch_serviced, err = NewContainer(
		ContainerDescription{
			Name:          serviceName,
			Repo:          IMAGE_REPO,
			Tag:           IMAGE_TAG,
			Command:       func() string { return "" },
			Ports:         []int{9200},
			Volumes:       map[string]string{"data": "/opt/elasticsearch-0.90.9/data"},
			Configuration: make(map[string]interface{}),
			HealthCheck:   elasticsearchHealthCheck(9200),
		},
	)
	if err != nil {
		glog.Fatal("Error initializing elasticsearch container: %s", err)
	}
	elasticsearch_serviced.Command = func() string {
		clusterArg := ""
		if clusterName, ok := elasticsearch_serviced.Configuration["cluster"]; ok {
			clusterArg = fmt.Sprintf(" -Des.cluster.name=%s ", clusterName)
		}
		return fmt.Sprintf(`/opt/elasticsearch-0.90.9/bin/elasticsearch -f -Des.node.name=%s %s`, elasticsearch_serviced.Name, clusterArg)
	}

	serviceName = "elasticsearch-logstash"
	elasticsearch_logstash, err = NewContainer(
		ContainerDescription{
			Name:          serviceName,
			Repo:          IMAGE_REPO,
			Tag:           IMAGE_TAG,
			Command:       func() string { return "" },
			Ports:         []int{9100},
			Volumes:       map[string]string{"data": "/opt/elasticsearch-1.3.1/data"},
			Configuration: make(map[string]interface{}),
			HealthCheck:   elasticsearchHealthCheck(9100),
		},
	)
	if err != nil {
		glog.Fatal("Error initializing elasticsearch container: %s", err)
	}
	envPerService[serviceName]["ES_JAVA_OPTS"] = "-Xmx4g"
	elasticsearch_logstash.Command = func() string {
		clusterArg := ""
		if clusterName, ok := elasticsearch_logstash.Configuration["cluster"]; ok {
			clusterArg = fmt.Sprintf(" -Des.cluster.name=%s ", clusterName)
		}
		return fmt.Sprintf(`/opt/elasticsearch-1.3.1/bin/elasticsearch -Des.node.name=%s %s`, elasticsearch_logstash.Name, clusterArg)
	}
}

// getEnvVarInt() returns the env var as an int value or the defaultValue if env var is unset
func getEnvVarInt(envVar string, defaultValue int) int {
	envVarValue := os.Getenv(envVar)
	if len(envVarValue) > 0 {
		if value, err := strconv.Atoi(envVarValue); err != nil {
			glog.Errorf("Could not convert env var %s:%s to integer, error:%s", envVar, envVarValue, err)
			return defaultValue
		} else {
			return value
		}
	}
	return defaultValue
}

// elasticsearchHealthCheck() determines if elasticsearch is healthy
func elasticsearchHealthCheck(port int) func() error {
	return func() error {
		start := time.Now()
		lastError := time.Now()
		minUptime := time.Second * 2
		timeout := time.Second * time.Duration(getEnvVarInt("ES_STARTUP_TIMEOUT", 600))
		baseUrl := fmt.Sprintf("http://localhost:%d", port)

		schemaFile := path.Join(utils.ResourcesDir(), "controlplane.json")

		for {
			if healthResponse, err := cluster.Health(true); err == nil && (healthResponse.Status == "green" || healthResponse.Status == "yellow") {
				if buffer, err := os.Open(schemaFile); err != nil {
					glog.Fatalf("problem reading %s", err)
					return err
				} else {
					http.Post(baseUrl+"/controlplane", "application/json", buffer)
					buffer.Close()
				}
			} else {
				lastError = time.Now()
				glog.V(2).Infof("Still trying to connect to elasticsearch container: %v: %s", err, healthResponse)
			}
			if time.Since(lastError) > minUptime {
				break
			}
			if time.Since(start) > timeout {
				return fmt.Errorf("Could not startup elasticsearch container.  waited timeout:%v", timeout)
			}
			time.Sleep(time.Millisecond * 1000)
		}
		glog.Infof("elasticsearch container started, browser at %s/_plugin/head/", baseUrl)
		return nil
	}
}

func PurgeLogstashIndices(days int) error {
	container := elasticsearch_logstash
	port := container.Ports[0]
	glog.Infof("Purging logstash entries older than %d days", days)
	return container.RunCommand([]string{
		"/usr/local/bin/curator", "--port", fmt.Sprintf("%d", port),
		"delete", "--older-than", fmt.Sprintf("%d", days)}, false)
}
<|endoftext|>


<commit_before>package navitia

import (
	"context"
	"fmt"
	"net/url"
	"strconv"
	"time"

	"github.com/aabizri/navitia/types"
)

// JourneyResults contains the results of a Journey request
//
// Warning: types.Journey.From / types.Journey.To aren't guaranteed to be filled.
// Based on very basic inspection, it seems they aren't filled when there are sections...
type JourneyResults struct {
	Journeys []types.Journey `json:"journeys"`

	Paging Paging `json:"links"`

	Logging `json:"-"`

	session *Session
}

// String satisfies stringer, pretty-prints JourneyResults
func (jr JourneyResults) String() string {
	var msg string = fmt.Sprintf("There are %d results\n", len(jr.Journeys))
	for i, journey := range jr.Journeys {
		msg += fmt.Sprintf("Journey #%d: %v\n", i, journey)
	}
	return msg
}

// JourneyRequest contain the parameters needed to make a Journey request
type JourneyRequest struct {
	// There must be at least one From or To parameter defined
	// When used with just one of them, the resulting Journey won't have a populated Sections field.
	From types.ID
	To   types.ID

	// When do you want to depart ? Or is DateIsArrival when do you want to arrive at your destination.
	Date          time.Time
	DateIsArrival bool

	// The traveller's type
	Traveler types.TravelerType

	// Define the freshness of data to use to compute journeys
	Freshness types.DataFreshness

	// Forbidden public transport objects
	Forbidden []types.ID

	// Allowed public transport objects
	// Note: This counstraint intersects with Forbidden
	Allowed []types.ID

	// Force the first section mode if it isn't a public transport mode
	// Note: The parameter is inclusive, not exclusive. As such if you want to forbid a mode you have to include all modes except that one.
	FirstSectionModes []types.Mode

	// Same, but for the last section
	LastSectionModes []types.Mode

	// MaxDurationToPT is the maximum allowed duration to reach the public transport.
	// Use this to limit the walking/biking part.
	MaxDurationToPT time.Duration

	// These four following parameters set the speed of each mode (Walking, Bike, BSS & car)
	// In meters per second
	WalkingSpeed   float64
	BikeSpeed      float64
	BikeShareSpeed float64
	CarSpeed       float64

	// Minimum and maximum amounts of journeys suggested
	MinJourneys uint
	MaxJourneys uint

	// Count fixes the amount of journeys to be returned, overriding minimum & maximum amount
	// Note: if Count=0 then it isn't taken into account
	Count uint

	// Maximum number of transfers in each journey
	MaxTransfers uint

	// Maximum duration of a trip
	MaxDuration time.Duration // To seconds

	// Wheelchair restricts the answer to accessible public transports
	Wheelchair bool
}

// toURL formats a journey request to url
// Should be refactored using a switch statement
func (req JourneyRequest) toURL() (url.Values, error) {
	params := url.Values{}

	// Define a few useful functions
	addUint := func(key string, amount uint64) {
		if amount != 0 {
			str := strconv.FormatUint(amount, 10)
			params.Add(key, str)
		}
	}
	addInt := func(key string, amount int64) {
		if amount != 0 {
			str := strconv.FormatInt(amount, 10)
			params.Add(key, str)
		}
	}
	addString := func(key string, str string) {
		if str != "" {
			params.Add(key, str)
		}
	}
	addIDSlice := func(key string, ids []types.ID) {
		if len(ids) != 0 {
			for _, id := range ids {
				params.Add(key, string(id))
			}
		}
	}
	addModes := func(key string, modes []types.Mode) {
		if len(modes) != 0 {
			for _, mode := range modes {
				params.Add(key, string(mode))
			}
		}
	}
	addFloat := func(key string, amount float64) {
		if amount != 0 {
			speedStr := strconv.FormatFloat(amount, 'f', 3, 64)
			params.Add(key, speedStr)
		}
	}

	// Encode the from and to
	if from := req.From; from != "" {
		params.Add("from", string(from))
	}
	if to := req.To; to != "" {
		params.Add("to", string(to))
	}

	if datetime := req.Date; !datetime.IsZero() {
		str := datetime.Format(types.DateTimeFormat)
		params.Add("datetime", str)
		if req.DateIsArrival {
			params.Add("datetime_represents", "arrival")
		}
	}

	addString("traveler_type", string(req.Traveler))

	addString("data_freshness", string(req.Freshness))

	addIDSlice("forbidden_uris[]", req.Forbidden)

	addIDSlice("allowed_id[]", req.Allowed)

	addModes("first_section_mode[]", req.FirstSectionModes)

	addModes("last_section_mode[]", req.LastSectionModes)

	// max_duration_to_pt
	addInt("max_duration_to_pt", int64(req.MaxDurationToPT/time.Second))

	// walking_speed, bike_speed, bss_speed & car_speed
	addFloat("walking_speed", req.WalkingSpeed)
	addFloat("bike_speed", req.BikeSpeed)
	addFloat("bss_speed", req.BikeShareSpeed)
	addFloat("car_speed", req.CarSpeed)

	// If count is defined don't bother with the minimimal and maximum amount of items to return
	if count := req.Count; count != 0 {
		addUint("count", uint64(count))
	} else {
		addUint("min_nb_journeys", uint64(req.MinJourneys))
		addUint("max_nb_journeys", uint64(req.MaxJourneys))
	}

	// max_nb_transfers
	addUint("max_nb_transfers", uint64(req.MaxTransfers))

	// max_duration
	addInt("max_duration", int64(req.MaxDuration/time.Second))

	// wheelchair
	if req.Wheelchair {
		params.Add("wheelchair", "true")
	}

	return params, nil
}

// journeys is the internal function used by Journeys functions
func (s *Session) journeys(ctx context.Context, url string, req JourneyRequest) (*JourneyResults, error) {
	var results = &JourneyResults{session: s}
	err := s.request(ctx, url, req, results)
	return results, err
}

const journeysEndpoint string = "journeys"

// Journeys computes a list of journeys according to the parameters given
func (s *Session) Journeys(ctx context.Context, req JourneyRequest) (*JourneyResults, error) {
	// Create the URL
	url := s.APIURL + "/" + journeysEndpoint

	// Call
	return s.journeys(ctx, url, req)
}

// JourneysR computes a list of journeys according to the parameters given and in a specific region
func (s *Session) JourneysR(ctx context.Context, req JourneyRequest, regionID string) (*JourneyResults, error) {
	// Create the URL
	url := s.APIURL + "/coverage/" + regionID + "/" + journeysEndpoint

	// Call
	return s.journeys(ctx, url, req)
}
<commit_msg>Remove JourneyResults.String Add JourneyResults.Count<commit_after>package navitia

import (
	"context"
	"net/url"
	"strconv"
	"time"

	"github.com/aabizri/navitia/types"
)

// JourneyResults contains the results of a Journey request
//
// Warning: types.Journey.From / types.Journey.To aren't guaranteed to be filled.
// Based on very basic inspection, it seems they aren't filled when there are sections...
type JourneyResults struct {
	Journeys []types.Journey `json:"journeys"`

	Paging Paging `json:"links"`

	Logging `json:"-"`

	session *Session
}

// Count returns the number of results available in a JourneyRequest
func (jr *JourneyResults) Count() int {
	return len(jr.Journeys)
}

// JourneyRequest contain the parameters needed to make a Journey request
type JourneyRequest struct {
	// There must be at least one From or To parameter defined
	// When used with just one of them, the resulting Journey won't have a populated Sections field.
	From types.ID
	To   types.ID

	// When do you want to depart ? Or is DateIsArrival when do you want to arrive at your destination.
	Date          time.Time
	DateIsArrival bool

	// The traveller's type
	Traveler types.TravelerType

	// Define the freshness of data to use to compute journeys
	Freshness types.DataFreshness

	// Forbidden public transport objects
	Forbidden []types.ID

	// Allowed public transport objects
	// Note: This counstraint intersects with Forbidden
	Allowed []types.ID

	// Force the first section mode if it isn't a public transport mode
	// Note: The parameter is inclusive, not exclusive. As such if you want to forbid a mode you have to include all modes except that one.
	FirstSectionModes []types.Mode

	// Same, but for the last section
	LastSectionModes []types.Mode

	// MaxDurationToPT is the maximum allowed duration to reach the public transport.
	// Use this to limit the walking/biking part.
	MaxDurationToPT time.Duration

	// These four following parameters set the speed of each mode (Walking, Bike, BSS & car)
	// In meters per second
	WalkingSpeed   float64
	BikeSpeed      float64
	BikeShareSpeed float64
	CarSpeed       float64

	// Minimum and maximum amounts of journeys suggested
	MinJourneys uint
	MaxJourneys uint

	// Count fixes the amount of journeys to be returned, overriding minimum & maximum amount
	// Note: if Count=0 then it isn't taken into account
	Count uint

	// Maximum number of transfers in each journey
	MaxTransfers uint

	// Maximum duration of a trip
	MaxDuration time.Duration // To seconds

	// Wheelchair restricts the answer to accessible public transports
	Wheelchair bool
}

// toURL formats a journey request to url
// Should be refactored using a switch statement
func (req JourneyRequest) toURL() (url.Values, error) {
	params := url.Values{}

	// Define a few useful functions
	addUint := func(key string, amount uint64) {
		if amount != 0 {
			str := strconv.FormatUint(amount, 10)
			params.Add(key, str)
		}
	}
	addInt := func(key string, amount int64) {
		if amount != 0 {
			str := strconv.FormatInt(amount, 10)
			params.Add(key, str)
		}
	}
	addString := func(key string, str string) {
		if str != "" {
			params.Add(key, str)
		}
	}
	addIDSlice := func(key string, ids []types.ID) {
		if len(ids) != 0 {
			for _, id := range ids {
				params.Add(key, string(id))
			}
		}
	}
	addModes := func(key string, modes []types.Mode) {
		if len(modes) != 0 {
			for _, mode := range modes {
				params.Add(key, string(mode))
			}
		}
	}
	addFloat := func(key string, amount float64) {
		if amount != 0 {
			speedStr := strconv.FormatFloat(amount, 'f', 3, 64)
			params.Add(key, speedStr)
		}
	}

	// Encode the from and to
	if from := req.From; from != "" {
		params.Add("from", string(from))
	}
	if to := req.To; to != "" {
		params.Add("to", string(to))
	}

	if datetime := req.Date; !datetime.IsZero() {
		str := datetime.Format(types.DateTimeFormat)
		params.Add("datetime", str)
		if req.DateIsArrival {
			params.Add("datetime_represents", "arrival")
		}
	}

	addString("traveler_type", string(req.Traveler))

	addString("data_freshness", string(req.Freshness))

	addIDSlice("forbidden_uris[]", req.Forbidden)

	addIDSlice("allowed_id[]", req.Allowed)

	addModes("first_section_mode[]", req.FirstSectionModes)

	addModes("last_section_mode[]", req.LastSectionModes)

	// max_duration_to_pt
	addInt("max_duration_to_pt", int64(req.MaxDurationToPT/time.Second))

	// walking_speed, bike_speed, bss_speed & car_speed
	addFloat("walking_speed", req.WalkingSpeed)
	addFloat("bike_speed", req.BikeSpeed)
	addFloat("bss_speed", req.BikeShareSpeed)
	addFloat("car_speed", req.CarSpeed)

	// If count is defined don't bother with the minimimal and maximum amount of items to return
	if count := req.Count; count != 0 {
		addUint("count", uint64(count))
	} else {
		addUint("min_nb_journeys", uint64(req.MinJourneys))
		addUint("max_nb_journeys", uint64(req.MaxJourneys))
	}

	// max_nb_transfers
	addUint("max_nb_transfers", uint64(req.MaxTransfers))

	// max_duration
	addInt("max_duration", int64(req.MaxDuration/time.Second))

	// wheelchair
	if req.Wheelchair {
		params.Add("wheelchair", "true")
	}

	return params, nil
}

// journeys is the internal function used by Journeys functions
func (s *Session) journeys(ctx context.Context, url string, req JourneyRequest) (*JourneyResults, error) {
	var results = &JourneyResults{session: s}
	err := s.request(ctx, url, req, results)
	return results, err
}

const journeysEndpoint string = "journeys"

// Journeys computes a list of journeys according to the parameters given
func (s *Session) Journeys(ctx context.Context, req JourneyRequest) (*JourneyResults, error) {
	// Create the URL
	url := s.APIURL + "/" + journeysEndpoint

	// Call
	return s.journeys(ctx, url, req)
}

// JourneysR computes a list of journeys according to the parameters given and in a specific region
func (s *Session) JourneysR(ctx context.Context, req JourneyRequest, regionID string) (*JourneyResults, error) {
	// Create the URL
	url := s.APIURL + "/coverage/" + regionID + "/" + journeysEndpoint

	// Call
	return s.journeys(ctx, url, req)
}
<|endoftext|>


<commit_before>package hammer

import (
	"log"
	"time"

	"github.com/antihax/evedata/internal/redisqueue"
)

func (s *Hammer) runTriggers() {
	regions, _, err := s.esi.ESI.UniverseApi.GetUniverseRegions(nil, nil)
	if err != nil {
		log.Fatalln(err)
	}

	marketOrders := time.NewTicker(20 * time.Minute)

	go func() {
		for {
			select {
			case <-marketOrders.C:
				work := []redisqueue.Work{}
				for _, region := range regions {
					work = append(work, redisqueue.Work{Operation: "marketOrders", Parameter: region})
				}

				s.QueueWork(work)
			case <-s.stop:
				marketOrders.Stop()
				return
			}
		}
	}()

}
<commit_msg>start collecting structure information and structure market data<commit_after>package hammer

import (
	"context"
	"log"
	"time"

	"github.com/antihax/evedata/internal/redisqueue"
)

func (s *Hammer) runTriggers() {
	regions, _, err := s.esi.ESI.UniverseApi.GetUniverseRegions(context.TODO(), nil)
	if err != nil {
		log.Fatalln(err)
	}

	marketOrders := time.NewTicker(20 * time.Minute)

	go func() {
		for {
			select {

			// Get market orders every 20 minutes
			case <-marketOrders.C:
				work := []redisqueue.Work{}
				for _, region := range regions {
					work = append(work, redisqueue.Work{Operation: "marketOrders", Parameter: region})
				}
				s.QueueWork(work)

				work = []redisqueue.Work{}
				// Get a list of structures to rake over for market data also
				structures, _, err := s.esi.ESI.UniverseApi.GetUniverseStructures(context.TODO(), nil)
				if err != nil {
					log.Panicln(err)
					continue
				}
				for _, structure := range structures {
					work = append(work, redisqueue.Work{Operation: "structureOrders", Parameter: structure})
					work = append(work, redisqueue.Work{Operation: "structure", Parameter: structure})
				}
				s.QueueWork(work)

			case <-s.stop:
				marketOrders.Stop()
				return
			}
		}
	}()

}
<|endoftext|>


<commit_before>package dt

import (
	"math"
)

// Product represents a product result returned from ElasticSearch. Note that
// because it's an ElasticSearch result, it has a string ID.
type Product struct {
	ID       string
	Name     string
	Size     string
	Stock    uint
	Price    uint64
	VendorID uint64
	Reviews  []struct {
		Score uint
		Body  string
	}
}

type ProductSel struct {
	*Product
	Count uint
}

type ProductSels []ProductSel

func (prods ProductSels) Prices(addr *Address) map[string]uint64 {
	m := map[string]uint64{
		"products": 0,
		"tax":      0,
		"shipping": 0,
		"total":    0,
	}
	for _, prod := range prods {
		m["products"] += prod.Price * uint64(prod.Count)
	}
	// calculate shipping. note that this is vendor specific
	m["shipping"] = 1290 + uint64((len(prods)-1)*120)
	tax := statesTax[addr.State]
	if tax > 0.0 {
		tax *= float64(m["products"])
	}
	m["tax"] = uint64(math.Ceil(tax))
	m["total"] = m["products"] + m["shipping"] + m["tax"]
	return m
}
<commit_msg>Fix nil pointer bug<commit_after>package dt

import (
	"math"
)

// Product represents a product result returned from ElasticSearch. Note that
// because it's an ElasticSearch result, it has a string ID.
type Product struct {
	ID       string
	Name     string
	Size     string
	Stock    uint
	Price    uint64
	VendorID uint64
	Reviews  []struct {
		Score uint
		Body  string
	}
}

type ProductSel struct {
	*Product
	Count uint
}

type ProductSels []ProductSel

func (prods ProductSels) Prices(addr *Address) map[string]uint64 {
	m := map[string]uint64{
		"products": 0,
		"tax":      0,
		"shipping": 0,
		"total":    0,
	}
	for _, prod := range prods {
		m["products"] += prod.Price * uint64(prod.Count)
	}
	// calculate shipping. note that this is vendor specific
	m["shipping"] = 1290 + uint64((len(prods)-1)*120)
	var tax float64
	if addr != nil {
		tax = statesTax[addr.State]
		if tax > 0.0 {
			tax *= float64(m["products"])
		}
	}
	m["tax"] = uint64(math.Ceil(tax))
	m["total"] = m["products"] + m["shipping"] + m["tax"]
	return m
}
<|endoftext|>


<commit_before>package lib

type User struct {
	Username string
	Password string
	Token    string
	Expire   string
}

type Wrapper struct {
	Data  interface{} `json:"D"`
	Error *[]string   `json:"E"`
	//status already parsed in res.Status
}

type WNode struct {
	Data  *Node     `json:"D"`
	Error *[]string `json:"E"`
}

type WAcl struct {
	Data  *acl      `json:"D"`
	Error *[]string `json:"E"`
}

type Node struct {
	Id           string            `bson:"id" json:"id"`
	Version      string            `bson:"version" json:"version"`
	File         file              `bson:"file" json:"file"`
	Attributes   interface{}       `bson:"attributes" json:"attributes"`
	Indexes      indexes           `bson:"indexes" json:"indexes"`
	Acl          acl               `bson:"acl" json:"-"`
	VersionParts map[string]string `bson:"version_parts" json:"-"`
	Tags         []string          `bson:"tags" json:"tags"`
	Revisions    []Node            `bson:"revisions" json:"-"`
	Linkages     []linkage         `bson:"linkage" json:"linkages"`
}

type file struct {
	Name         string            `bson:"name" json:"name"`
	Size         int64             `bson:"size" json:"size"`
	Checksum     map[string]string `bson:"checksum" json:"checksum"`
	Format       string            `bson:"format" json:"format"`
	Path         string            `bson:"path" json:"-"`
	Virtual      bool              `bson:"virtual" json:"virtual"`
	VirtualParts []string          `bson:"virtual_parts" json:"virtual_parts"`
}

type partsList struct {
	Count  int         `json:"count"`
	Length int         `json:"length"`
	Parts  []partsFile `json:"parts"`
}

type linkage struct {
	Type      string   `bson: "relation" json:"relation"`
	Ids       []string `bson:"ids" json:"ids"`
	Operation string   `bson:"operation" json:"operation"`
}

type indexes map[string]IdxInfo

type IdxInfo struct {
	Type        string `bson:"index_type" json:"-"`
	TotalUnits  int64  `bson:"total_units" json:"total_units"`
	AvgUnitSize int64  `bson:"average_unit_size" json:"average_unit_size"`
}

type acl struct {
	Owner  string   `bson:"owner" json:"owner"`
	Read   []string `bson:"read" json:"read"`
	Write  []string `bson:"write" json:"write"`
	Delete []string `bson:"delete" json:"delete"`
}

type partsFile []string

type Token struct {
	AccessToken     string      `json:"access_token"`
	AccessTokenHash string      `json:"access_token_hash"`
	ClientId        string      `json:"client_id"`
	ExpiresIn       int         `json:"expires_in"`
	Expiry          int         `json:"expiry"`
	IssuedOn        int         `json:"issued_on"`
	Lifetime        int         `json:"lifetime"`
	Scopes          interface{} `json:"scopes"`
	TokenId         string      `json:"token_id"`
	TokeType        string      `json:"token_type"`
	UserName        string      `json:"user_name"`
}
<commit_msg>imports server/store for structs<commit_after>package lib

import (
	"github.com/MG-RAST/Shock/shock-server/store"
)

type Node store.Node

type User struct {
	Username string
	Password string
	Token    string
	Expire   string
}

type Wrapper struct {
	Data  interface{} `json:"D"`
	Error *[]string   `json:"E"`
	//status already parsed in res.Status
}

type WNode struct {
	Data  *Node     `json:"D"`
	Error *[]string `json:"E"`
}

type WAcl struct {
	Data  *store.Acl `json:"D"`
	Error *[]string  `json:"E"`
}

type Token struct {
	AccessToken     string      `json:"access_token"`
	AccessTokenHash string      `json:"access_token_hash"`
	ClientId        string      `json:"client_id"`
	ExpiresIn       int         `json:"expires_in"`
	Expiry          int         `json:"expiry"`
	IssuedOn        int         `json:"issued_on"`
	Lifetime        int         `json:"lifetime"`
	Scopes          interface{} `json:"scopes"`
	TokenId         string      `json:"token_id"`
	TokeType        string      `json:"token_type"`
	UserName        string      `json:"user_name"`
}
<|endoftext|>


<commit_before>package shorty

import (
	"bytes"
	"fmt"
	"github.com/golang/glog"
	"github.com/gorilla/mux"
	omni_http "github.com/qorio/omni/http"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"text/template"
	"time"
)

var (
	deeplinkJsTemplate   = template.New("deeplink.js")
	openTestHtmlTemplate = template.New("opentest.html")
)

type appInstallInterstitialContext struct {
	Rule                  *RoutingRule
	IsCrossBrowserContext bool
	Timestamp             int64
}

func init() {
	var err error

	deeplinkJsTemplate, err = deeplinkJsTemplate.Parse(`
function getCookie(name) {
    var value = "; " + document.cookie;
    var parts = value.split("; " + name + "=");
    if (parts.length == 2) return parts.pop().split(";").shift();
}
function getParameterByName(name) {
    name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
    var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
        results = regex.exec(location.search);
    return results == null ? null : decodeURIComponent(results[1].replace(/\+/g, " "));
}
function redirectWithLocation(target) {
    navigator.geolocation.getCurrentPosition(function(position) {
        lat = position.coords.latitude
        lng = position.coords.longitude
        window.location = target + "&lat=" + lat + "&lng=" + lng
    })
}
function onLoad() {
    var deeplink = "{{.Rule.Destination}}";
    var appstore = "{{.Rule.AppStoreUrl}}";
    var interstitialUrl = window.location;
    var didNotDetectApp = getParameterByName('__xrl_noapp') != null;
    if (didNotDetectApp) {
{{if .IsCrossBrowserContext }}
        window.location = appstore;
{{else}}
        var el = document.getElementById("has-app")
        el.innerHTML = "
Still here?  Try open this in Safari.
";
{{end}}

    } else {
        var scheme = deeplink.split("://").shift();
        var shortCode = window.location.pathname.substring(1);
        deeplink += "&__xrlc=" + getCookie("uuid") + "&__xrlp=" + scheme + "&__xrls=" + shortCode;
        setTimeout(function() {
{{if eq .Rule.InterstitialToAppStoreOnTimeout "on"}}
              if (!document.webkitHidden) {
                  setTimeout(function(){
                      redirectWithLocation(interstitialUrl + "&__xrl_noapp=");
                  }, 2000)
                  window.location = appstore;
              }
{{else}}
              if (!document.webkitHidden) {
                  redirectWithLocation(interstitialUrl + "&__xrl_noapp=");
              }
{{end}}
        }, {{.Rule.InterstitialAppLinkTimeoutMillis}});
        window.location = deeplink;
    }
}
`)
	if err != nil {
		glog.Warningln("Bad template for deeplink.js!")
		panic(err)
	}

	openTestHtmlTemplate, err = openTestHtmlTemplate.Parse(`

  <title>Getting content...</title>

  <xmp theme="journal" style="display:none;">

{{if .IsCrossBrowserContext }}
  Install the app here.
{{else}}
  Opening the link in app...  If the app does not open, open this link via Safari.
{{end}}

  </xmp>

`)
	if err != nil {
		glog.Warningln("Bad template for html test/open!")
		panic(err)
	}
}

func (this *ShortyEndPoint) CheckAppInstallInterstitialHandler(resp http.ResponseWriter, req *http.Request) {
	req.ParseForm()
	_, noapp := req.Form[noAppInstallParam]

	vars := mux.Vars(req)
	uuid := vars["uuid"]
	appUrlScheme := vars["scheme"]

	shortUrl, err := this.service.FindUrl(vars["shortCode"])

	if err != nil {
		renderError(resp, req, err.Error(), http.StatusInternalServerError)
		return
	} else if shortUrl == nil {
		if this.settings.Redirect404 != "" {
			originalUrl, err := this.router.Get("redirect").URL("id", vars["id"])
			if err != nil {
				renderError(resp, req, err.Error(), http.StatusInternalServerError)
				return
			}
			url404 := strings.Replace(this.settings.Redirect404,
				"$origURL", url.QueryEscape(fmt.Sprintf("http://%s%s", req.Host, originalUrl.String())), 1)
			http.Redirect(resp, req, url404, http.StatusTemporaryRedirect)
			return
		}
		renderError(resp, req, "No URL was found with that shorty code", http.StatusNotFound)
		return
	}

	var content string = ""

	omni_http.SetNoCachingHeaders(resp)
	cookies := omni_http.NewCookieHandler(secureCookie, resp, req)

	userAgent := omni_http.ParseUserAgent(req)
	origin, _ := this.requestParser.Parse(req)

	matchedRule, _ := shortUrl.MatchRule(this.service, userAgent, origin, cookies)

	// visits, cookied, last, userId := processCookies(cookies, shortUrl)
	_, _, lastViewed, userId := processCookies(cookies, shortUrl.Id)
	glog.Infoln(">>> harvest - processed cookies", lastViewed, userId, shortUrl.Id, "matchedRule=", matchedRule.Id, matchedRule.Comment)

	// Here we check if the two uuids are different.  One uuid is in the url of this request.  This is the uuid
	// from some context (e.g. from FB webview on iOS).  Another uuid is one in the cookie -- either we assigned
	// or read from the client context.  The current context may not be the same as the context of the uuid in
	// the url.  This is because the user could be visiting the same link from another browser (eg. on Safari)
	// after being prompted.
	// If the two uuids do not match -- then we know the contexts are different.  The user is visiting from
	// some context other than the one with the original link.  So in this case, we can do a redirect back to
	// the short link that the user was looking at that got them to see the harvest url in the first place.
	// Otherwise, show the static content which may tell them to try again in a different browser/context.

	if uuid != userId {

		glog.Infoln(">>>> harvest phase, noapp=", noapp)

		// We got the user to come here via a different context (browser) than the one that created
		// this url in the first place.  So link the two ids together and redirect back to the short url.

		this.service.Link(UrlScheme(appUrlScheme), UUID(uuid), UUID(userId), shortUrl.Id)

		// Now, look for an app-open in context of userId.  If we have somehow opened the app
		// before, then we can just create an app-open entry for *this* context (uuid) because
		// we know that the app already exists on the device and was opened in a different context.

		appOpen, found, _ := this.service.FindAppOpen(UrlScheme(appUrlScheme), UUID(userId))

		glog.Infoln("find app-open", appUrlScheme, userId, appOpen, found)

		if found {
			// create a record *as if* the app was also opened in the other context
			appOpen.SourceContext = UUID(uuid)
			appOpen.SourceApplication = origin.Referrer
			this.service.TrackAppOpen(UrlScheme(appUrlScheme), appOpen.AppContext, appOpen)
		}
	}

	// save a fingerprint
	go func() {
		// check and see if we have params for location
		if lat, exists := req.Form["lat"]; exists {
			if lng, exists := req.Form["lng"]; exists {
				if latitude, err := strconv.ParseFloat(lat[0], 64); err == nil {
					if longitude, err := strconv.ParseFloat(lng[0], 64); err == nil {
						origin.Location.Latitude = latitude
						origin.Location.Longitude = longitude
					}
				}
			}
		}

		fingerprint := omni_http.FingerPrint(origin)
		glog.Infoln(">> New fingerprint: ", fingerprint)

		this.service.SaveFingerprintedVisit(&FingerprintedVisit{
			Fingerprint: fingerprint,
			Context:     UUID(userId),
			ShortCode:   shortUrl.Id,
			Timestamp:   timestamp(),
			Referrer:    origin.Referrer,
		})
	}()

	if fetchFromUrl, exists := req.Form["f"]; exists && fetchFromUrl[0] != "" {
		content = omni_http.FetchFromUrl(userAgent.Header, fetchFromUrl[0])
		resp.Write([]byte(content))
		return
	}

	if matchedRule != nil {
		openTestHtmlTemplate.Execute(resp, appInstallInterstitialContext{
			Rule: matchedRule,
			IsCrossBrowserContext: userId != uuid,
			Timestamp:             time.Now().Unix(),
		})
		return
	}

	return
}

func (this *ShortyEndPoint) CheckAppInstallInterstitialJSHandler(resp http.ResponseWriter, req *http.Request) {
	omni_http.SetNoCachingHeaders(resp)

	glog.Infoln(">>>> JS")

	vars := mux.Vars(req)
	shortCode := vars["shortCode"]
	uuid := vars["uuid"]

	shortUrl, err := this.service.FindUrl(shortCode)
	if err != nil || shortUrl == nil {
		renderError(resp, req, err.Error(), http.StatusInternalServerError)
		return
	}

	cookies := omni_http.NewCookieHandler(secureCookie, resp, req)
	userAgent := omni_http.ParseUserAgent(req)
	origin, _ := this.requestParser.Parse(req)

	_, _, _, userId := processCookies(cookies, shortUrl.Id)

	// Hack -- set the referrer to be DIRECT, otherwise it's the interstitial page url
	origin.Referrer = "DIRECT"

	matchedRule, notFound := shortUrl.MatchRule(this.service, userAgent, origin, cookies)
	if notFound != nil {
		renderError(resp, req, "not found", http.StatusNotFound)
		return
	}

	context := &appInstallInterstitialContext{
		Rule: matchedRule,
		IsCrossBrowserContext: userId != uuid,
		Timestamp:             time.Now().Unix(),
	}

	var buff bytes.Buffer
	deeplinkJsTemplate.Execute(&buff, context)
	resp.Write(buff.Bytes())

	fmt.Println(buff.Bytes())
	return
}
<commit_msg>Logging + removed hack that changed referrer<commit_after>package shorty

import (
	"bytes"
	"fmt"
	"github.com/golang/glog"
	"github.com/gorilla/mux"
	omni_http "github.com/qorio/omni/http"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"text/template"
	"time"
)

var (
	deeplinkJsTemplate   = template.New("deeplink.js")
	openTestHtmlTemplate = template.New("opentest.html")
)

type appInstallInterstitialContext struct {
	Rule                  *RoutingRule
	IsCrossBrowserContext bool
	Timestamp             int64
}

func init() {
	var err error

	deeplinkJsTemplate, err = deeplinkJsTemplate.Parse(`
function getCookie(name) {
    var value = "; " + document.cookie;
    var parts = value.split("; " + name + "=");
    if (parts.length == 2) return parts.pop().split(";").shift();
}
function getParameterByName(name) {
    name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
    var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
        results = regex.exec(location.search);
    return results == null ? null : decodeURIComponent(results[1].replace(/\+/g, " "));
}
function redirectWithLocation(target) {
    navigator.geolocation.getCurrentPosition(function(position) {
        lat = position.coords.latitude
        lng = position.coords.longitude
        window.location = target + "&lat=" + lat + "&lng=" + lng
    })
}
function onLoad() {
    var deeplink = "{{.Rule.Destination}}";
    var appstore = "{{.Rule.AppStoreUrl}}";
    var interstitialUrl = window.location;
    var didNotDetectApp = getParameterByName('__xrl_noapp') != null;
    if (didNotDetectApp) {
{{if .IsCrossBrowserContext }}
        window.location = appstore;
{{else}}
        var el = document.getElementById("has-app")
        el.innerHTML = "
Still here?  Try open this in Safari.
";
{{end}}

    } else {
        var scheme = deeplink.split("://").shift();
        var shortCode = window.location.pathname.substring(1);
        deeplink += "&__xrlc=" + getCookie("uuid") + "&__xrlp=" + scheme + "&__xrls=" + shortCode;
        setTimeout(function() {
{{if eq .Rule.InterstitialToAppStoreOnTimeout "on"}}
              if (!document.webkitHidden) {
                  setTimeout(function(){
                      redirectWithLocation(interstitialUrl + "&__xrl_noapp=");
                  }, 2000)
                  window.location = appstore;
              }
{{else}}
              if (!document.webkitHidden) {
                  redirectWithLocation(interstitialUrl + "&__xrl_noapp=");
              }
{{end}}
        }, {{.Rule.InterstitialAppLinkTimeoutMillis}});
        window.location = deeplink;
    }
}
`)
	if err != nil {
		glog.Warningln("Bad template for deeplink.js!")
		panic(err)
	}

	openTestHtmlTemplate, err = openTestHtmlTemplate.Parse(`

  <title>Getting content...</title>

  <xmp theme="journal" style="display:none;">

{{if .IsCrossBrowserContext }}
  Install the app here.
{{else}}
  Opening the link in app...  If the app does not open, open this link via Safari.
{{end}}

  </xmp>

`)
	if err != nil {
		glog.Warningln("Bad template for html test/open!")
		panic(err)
	}
}

func (this *ShortyEndPoint) CheckAppInstallInterstitialHandler(resp http.ResponseWriter, req *http.Request) {
	req.ParseForm()
	_, noapp := req.Form[noAppInstallParam]

	vars := mux.Vars(req)
	uuid := vars["uuid"]
	appUrlScheme := vars["scheme"]

	shortUrl, err := this.service.FindUrl(vars["shortCode"])

	if err != nil {
		renderError(resp, req, err.Error(), http.StatusInternalServerError)
		return
	} else if shortUrl == nil {
		if this.settings.Redirect404 != "" {
			originalUrl, err := this.router.Get("redirect").URL("id", vars["id"])
			if err != nil {
				renderError(resp, req, err.Error(), http.StatusInternalServerError)
				return
			}
			url404 := strings.Replace(this.settings.Redirect404,
				"$origURL", url.QueryEscape(fmt.Sprintf("http://%s%s", req.Host, originalUrl.String())), 1)
			http.Redirect(resp, req, url404, http.StatusTemporaryRedirect)
			return
		}
		renderError(resp, req, "No URL was found with that shorty code", http.StatusNotFound)
		return
	}

	var content string = ""

	omni_http.SetNoCachingHeaders(resp)
	cookies := omni_http.NewCookieHandler(secureCookie, resp, req)

	userAgent := omni_http.ParseUserAgent(req)
	origin, _ := this.requestParser.Parse(req)

	matchedRule, _ := shortUrl.MatchRule(this.service, userAgent, origin, cookies)

	// visits, cookied, last, userId := processCookies(cookies, shortUrl)
	_, _, lastViewed, userId := processCookies(cookies, shortUrl.Id)
	glog.Infoln(">>> harvest - processed cookies", lastViewed, userId, shortUrl.Id, "matchedRule=", matchedRule.Id, matchedRule.Comment)

	// Here we check if the two uuids are different.  One uuid is in the url of this request.  This is the uuid
	// from some context (e.g. from FB webview on iOS).  Another uuid is one in the cookie -- either we assigned
	// or read from the client context.  The current context may not be the same as the context of the uuid in
	// the url.  This is because the user could be visiting the same link from another browser (eg. on Safari)
	// after being prompted.
	// If the two uuids do not match -- then we know the contexts are different.  The user is visiting from
	// some context other than the one with the original link.  So in this case, we can do a redirect back to
	// the short link that the user was looking at that got them to see the harvest url in the first place.
	// Otherwise, show the static content which may tell them to try again in a different browser/context.

	if uuid != userId {

		glog.Infoln(">>>> harvest phase, noapp=", noapp)

		// We got the user to come here via a different context (browser) than the one that created
		// this url in the first place.  So link the two ids together and redirect back to the short url.

		this.service.Link(UrlScheme(appUrlScheme), UUID(uuid), UUID(userId), shortUrl.Id)

		// Now, look for an app-open in context of userId.  If we have somehow opened the app
		// before, then we can just create an app-open entry for *this* context (uuid) because
		// we know that the app already exists on the device and was opened in a different context.

		appOpen, found, _ := this.service.FindAppOpen(UrlScheme(appUrlScheme), UUID(userId))

		glog.Infoln("find app-open", appUrlScheme, userId, appOpen, found)

		if found {
			// create a record *as if* the app was also opened in the other context
			appOpen.SourceContext = UUID(uuid)
			appOpen.SourceApplication = origin.Referrer
			this.service.TrackAppOpen(UrlScheme(appUrlScheme), appOpen.AppContext, appOpen)
		}
	}

	// save a fingerprint
	go func() {
		// check and see if we have params for location
		if lat, exists := req.Form["lat"]; exists {
			if lng, exists := req.Form["lng"]; exists {
				if latitude, err := strconv.ParseFloat(lat[0], 64); err == nil {
					if longitude, err := strconv.ParseFloat(lng[0], 64); err == nil {
						origin.Location.Latitude = latitude
						origin.Location.Longitude = longitude
					}
				}
			}
		}

		fingerprint := omni_http.FingerPrint(origin)
		glog.Infoln(">> New fingerprint: ", fingerprint)

		this.service.SaveFingerprintedVisit(&FingerprintedVisit{
			Fingerprint: fingerprint,
			Context:     UUID(userId),
			ShortCode:   shortUrl.Id,
			Timestamp:   timestamp(),
			Referrer:    origin.Referrer,
		})
	}()

	if fetchFromUrl, exists := req.Form["f"]; exists && fetchFromUrl[0] != "" {
		content = omni_http.FetchFromUrl(userAgent.Header, fetchFromUrl[0])
		resp.Write([]byte(content))
		return
	}

	if matchedRule != nil {
		openTestHtmlTemplate.Execute(resp, appInstallInterstitialContext{
			Rule: matchedRule,
			IsCrossBrowserContext: userId != uuid,
			Timestamp:             time.Now().Unix(),
		})
		return
	}

	return
}

func (this *ShortyEndPoint) CheckAppInstallInterstitialJSHandler(resp http.ResponseWriter, req *http.Request) {
	omni_http.SetNoCachingHeaders(resp)

	glog.Infoln(">>>> JS")

	vars := mux.Vars(req)
	shortCode := vars["shortCode"]
	uuid := vars["uuid"]

	shortUrl, err := this.service.FindUrl(shortCode)
	if err != nil || shortUrl == nil {
		renderError(resp, req, err.Error(), http.StatusInternalServerError)
		return
	}

	cookies := omni_http.NewCookieHandler(secureCookie, resp, req)
	userAgent := omni_http.ParseUserAgent(req)
	origin, _ := this.requestParser.Parse(req)

	_, _, _, userId := processCookies(cookies, shortUrl.Id)

	matchedRule, notFound := shortUrl.MatchRule(this.service, userAgent, origin, cookies)
	if notFound != nil {
		renderError(resp, req, "not found", http.StatusNotFound)
		return
	}

	context := &appInstallInterstitialContext{
		Rule: matchedRule,
		IsCrossBrowserContext: userId != uuid,
		Timestamp:             time.Now().Unix(),
	}

	var buff bytes.Buffer
	deeplinkJsTemplate.Execute(&buff, context)
	resp.Write(buff.Bytes())

	fmt.Println(buff.String())
	return
}
<|endoftext|>


<commit_before>package kite

import (
	"bytes"
	zmq "github.com/pebbe/zmq3"
	"koding/newkite/protocol"
	"sync"
)

// ZeroMQ is a struct that complies with the Messenger interface.
type ZeroMQ struct {
	Subscriber *zmq.Socket
	Dealer     *zmq.Socket
	sync.Mutex // protects zmq send for DEALER socket
}

func NewZeroMQ(kiteID string) *ZeroMQ {
	routerKontrol := "tcp://127.0.0.1:5556"
	subKontrol := "tcp://127.0.0.1:5557"
	sub, _ := zmq.NewSocket(zmq.SUB)
	sub.Connect(subKontrol)

	dealer, _ := zmq.NewSocket(zmq.DEALER)
	dealer.SetIdentity(kiteID) // use our ID also for zmq envelope
	dealer.Connect(routerKontrol)

	return &ZeroMQ{
		Subscriber: sub,
		Dealer:     dealer,
	}
}

func (z *ZeroMQ) Subscribe(filter string) error {
	return z.Subscriber.SetSubscribe(filter)
}

func (z *ZeroMQ) Unsubscribe(filter string) error {
	return z.Subscriber.SetUnsubscribe(filter)
}

func (z *ZeroMQ) Send(msg []byte) []byte {
	z.Lock()
	defer z.Unlock()

	z.Dealer.SendBytes([]byte(""), zmq.SNDMORE)
	z.Dealer.SendBytes(msg, 0)

	z.Dealer.RecvBytes(0) // envelope delimiter
	reply, _ := z.Dealer.RecvBytes(0)
	return reply
}

func (z *ZeroMQ) Consume(handle func([]byte)) {
	for {
		msg, _ := z.Subscriber.RecvBytes(0)
		frames := bytes.SplitN(msg, []byte(protocol.FRAME_SEPARATOR), 2)
		if len(frames) != 2 { // msg is malformed
			continue
		}

		// frames[0] contains the filter string
		// frames[1] contains the msg content
		handle(frames[1])
	}
}
<commit_msg>remove old zeromq code<commit_after><|endoftext|>


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

package pkg

import (
	"compress/bzip2"
	"database/sql"
	"fmt"
	"os"
	"path/filepath"

	"regexp"

	"golang.org/x/text/language"
	"golang.org/x/text/message"

	_ "github.com/mattn/go-sqlite3"
)

type Database struct {
	sqlDb     *sql.DB
	sqlDbPath string
	version   string
}

func OpenDatabase() (*Database, error) {
	db := new(Database)

	err := InstallDatabase(true)
	if err != nil {
		return nil, fmt.Errorf("Database not available; try using db.update command")
	}

	db.sqlDbPath = filepath.Join(Env().McdexDir, "mcdex.dat")
	sqlDb, err := sql.Open("sqlite3", db.sqlDbPath)
	if err != nil {
		return nil, err
	}

	_, err = sqlDb.Exec("PRAGMA integrity_check;")
	if err != nil {
		return nil, err
	}

	db.sqlDb = sqlDb

	return db, nil
}

func InstallDatabase(skipIfExists bool) error {
	if skipIfExists && fileExists(filepath.Join(Env().McdexDir, "mcdex.dat")) {
		return nil
	}

	// Get the latest version
	version, err := ReadStringFromUrl("http://files.mcdex.net/data/latest.v6")
	if err != nil {
		return err
	}

	// Download the latest data file to mcdex/mcdex.dat
	url := fmt.Sprintf("http://files.mcdex.net/data/mcdex-v6-%s.dat.bz2", version)
	res, err := HttpGet(url)
	if err != nil {
		return fmt.Errorf("Failed to retrieve %s data file: %+v", version, err)
	}
	defer res.Body.Close()

	// Stream the data file to mcdex.dat.tmp
	tmpFileName := filepath.Join(Env().McdexDir, "mcdex.dat.tmp")
	err = writeStream(tmpFileName, bzip2.NewReader(res.Body))
	if err != nil {
		return err
	}

	// Open the temporary database and validate it
	tmpDb, err := sql.Open("sqlite3", tmpFileName)
	if err != nil {
		// TODO: Add log entry about the file being corrupt
		return err
	}
	defer tmpDb.Close()

	_, err = tmpDb.Exec("PRAGMA integrity_check;")
	if err != nil {
		return err
	}

	// Force the tmpDb to close so that (on Windows), we can ensure
	// the rename works
	tmpDb.Close()

	// Close the database and rename the tmp file
	err = os.Rename(tmpFileName, filepath.Join(Env().McdexDir, "mcdex.dat"))
	if err != nil {
		return fmt.Errorf("Failed to rename mcdex.dat.tmp: %+v", err)
	}
	return nil
}

func (db *Database) ListForge(mcvsn string, verbose bool) error {
	rows, err := db.sqlDb.Query("select version, isrec from forge where mcvsn = ? order by version desc", mcvsn)
	switch {
	case err == sql.ErrNoRows:
		return fmt.Errorf("No Forge version found for %s", mcvsn)
	case err != nil:
		return err
	}

	latest := false

	defer rows.Close()
	for rows.Next() {
		var version string
		var isrec bool
		err := rows.Scan(&version, &isrec)
		if err != nil {
			return err
		}
		if isrec {
			fmt.Printf("%s (recommended)\n", version)
		} else if !latest {
			fmt.Printf("%s (latest)\n", version)
			latest = true
		} else if verbose {
			fmt.Printf("%s\n", version)
		}
	}
	return nil
}

func (db *Database) lookupForgeVsn(mcvsn string) (string, error) {
	var forgeVsn string
	err := db.sqlDb.QueryRow("select version from forge where mcvsn = ? and isrec = 1", mcvsn).Scan(&forgeVsn)
	switch {
	case err == sql.ErrNoRows:
		return "", fmt.Errorf("No Forge version found for %s", mcvsn)
	case err != nil:
		return "", err
	}
	return forgeVsn, nil
}

func (db *Database) lookupFabricVsn(mcvsn string) (string, error) {
	var fabricVsn string
	err := db.sqlDb.QueryRow("SELECT version FROM fabric_loaders WHERE mcversion = ?", mcvsn).Scan((&fabricVsn))
	switch {
	case err == sql.ErrNoRows:
		return "", fmt.Errorf("No Fabric version found for %s", mcvsn)
	case err != nil:
		return "", err
	}

	return fabricVsn, nil
}

func (db *Database) PrintProjects(slug, mcvsn string, ptype int) error {
	// Turn the name into a pre-compiled regex
	slugRegex, err := regexp.Compile("(?i)" + slug)
	if err != nil {
		return fmt.Errorf("Failed to convert %s into regex: %s", slug, err)
	}

	query := "select slug, description from projects where type = ? and projectid in (select projectid from versions where mcvsn = ?) order by slug"
	if mcvsn == "" {
		query = "select slug, description from projects where type = ? order by slug"
	}

	rows, err := db.sqlDb.Query(query, ptype, mcvsn)
	if err != nil {
		return fmt.Errorf("Query failed: %+v", err)
	}
	defer rows.Close()

	// For each row, check the name against the pre-compiled regex
	for rows.Next() {
		var slug, desc string
		err = rows.Scan(&slug, &desc)
		if err != nil {
			return err
		}

		if slug == "" || slugRegex.MatchString(slug) {
			msg := message.NewPrinter(language.English)
			msg.Printf("%s | %s\n", slug, desc)
		}
	}

	return nil
}

func (db *Database) PrintLatestProjects(mcvsn string, ptype int) error {
	rows, err := db.sqlDb.Query(`select slug, description from projects
									    where type = ? and projectid in
									    (select projectid from files order by tstamp desc) limit 100`, ptype)
	if err != nil {
		return fmt.Errorf("Query failed: %+v", err)
	}
	defer rows.Close()

	for rows.Next() {
		var modSlug, modDesc string

		err = rows.Scan(&modSlug, &modDesc)
		if err != nil {
			return err
		}

		msg := message.NewPrinter(language.English)
		msg.Printf("%s | %s\n", modSlug, modDesc)
	}
	return nil
}

func (db *Database) GetLatestFileTstamp() (int, error) {
	var tstamp int
	err := db.sqlDb.QueryRow("select value from meta where key = 'dbtunix'").Scan(&tstamp)
	return tstamp, err
}

func (db *Database) FindProjectBySlug(slug string, modLoader string, ptype int) (int, error) {
	var modID int
	var supportedModLoader string
	err := db.sqlDb.QueryRow("select projectid, modloader from projects where type = ? and slug = ?", ptype, slug).Scan(&modID, &supportedModLoader)
	switch {
	case err == sql.ErrNoRows:
		return -1, fmt.Errorf("no mod found %s", slug)
	case err != nil:
		return -1, err
	}

	if  modLoader != supportedModLoader && modLoader != "fabric+forge" && supportedModLoader != "fabric+forge" {
		return -1, fmt.Errorf("%s (%s) is not compatible with %s", slug, supportedModLoader, modLoader)
	}

	return modID, nil
}

func (db *Database) findSlugByProject(id int) (string, error) {
	var slug string
	err := db.sqlDb.QueryRow("select slug from projects where projectid = ?", id).Scan(&slug)
	switch {
	case err == sql.ErrNoRows:
		return "", fmt.Errorf("no project found %d", id)
	case err != nil:
		return slug, err
	}
	return slug, nil
}

func (db *Database) findModBySlug(slug string, modLoader string) (int, error) {
	return db.FindProjectBySlug(slug, modLoader, 0)
}

func (db *Database) findModByName(name string) (int, error) {
	var modID int
	err := db.sqlDb.QueryRow("select projectid from projects where type = 0 and (name = ? or slug = ?)", name, name).Scan(&modID)
	switch {
	case err == sql.ErrNoRows:
		return -1, fmt.Errorf("No mod found %s", name)
	case err != nil:
		return -1, err
	}
	return modID, nil
}

func (db *Database) getProjectInfo(projectID int) (string, string, string, error) {
	var slug, name, desc string
	err := db.sqlDb.QueryRow("select slug, name, description from projects where projectid = ? and type = 0", projectID).Scan(&slug, &name, &desc)
	if err != nil {
		return "", "", "", fmt.Errorf("failed to get project info for %d: %+v", projectID, err)
	}

	return slug, name, desc, nil
}

func (db *Database) getDeps(fileID int) ([]string, error) {
	var result []string
	rows, err := db.sqlDb.Query("SELECT projectid, level FROM deps WHERE fileid = ? and level == 1", fileID)

	switch {
	case err == sql.ErrNoRows:
		return []string{}, nil
	case err != nil:
		return []string{}, fmt.Errorf("Failed to query deps for %d: %+v", fileID, err)
	}
	defer rows.Close()

	for rows.Next() {
		var projectID, level int
		err = rows.Scan(&projectID, &level)
		if err != nil {
			return []string{}, fmt.Errorf("Failed to query dep rows for %d: %+v", fileID, err)
		}

		// Resolve the project ID to a slug
		var slug string
		err = db.sqlDb.QueryRow("select slug from projects where projectid = ?", projectID).Scan(&slug)
		if err != nil {
			return []string{}, fmt.Errorf("failed to resolve dep project %d to a slug", projectID)
		}

		result = append(result, slug)
	}

	return result, nil
}

func (db *Database) GetLatestPackURL(slug string) (string, error) {
	// First try to find the pack by looking for the specific slug
	// TODO: Remove forge
	pid, err := db.FindProjectBySlug(slug, "forge",1)
	if err != nil {
		return "", err
	}

	// Find the latest file given the project ID; we don't need to worry about matching the MC version,
	// since modpacks are always locked to a specific version anyways
	var fileID int
	err = db.sqlDb.QueryRow("select fileid from files where projectid = ? order by tstamp desc limit 1", pid).Scan(&fileID)
	switch {
	case err == sql.ErrNoRows:
		return "", fmt.Errorf("No modpack file found for %s", slug)
	case err != nil:
		return "", err
	}

	// Construct a URL using the slug and file ID
	return fmt.Sprintf("https://minecraft.curseforge.com/projects/%d/files/%d/download", pid, fileID), nil

}
<commit_msg>Add two helper functions for generating text-based view of available mods<commit_after>// ***************************************************************************
//
//  Copyright 2017 David (Dizzy) Smith, dizzyd@dizzyd.com
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
// ***************************************************************************

package pkg

import (
	"compress/bzip2"
	"database/sql"
	"fmt"
	"os"
	"path/filepath"

	"regexp"

	"golang.org/x/text/language"
	"golang.org/x/text/message"

	_ "github.com/mattn/go-sqlite3"
)

type Database struct {
	sqlDb     *sql.DB
	sqlDbPath string
	version   string
}

func OpenDatabase() (*Database, error) {
	db := new(Database)

	err := InstallDatabase(true)
	if err != nil {
		return nil, fmt.Errorf("Database not available; try using db.update command")
	}

	db.sqlDbPath = filepath.Join(Env().McdexDir, "mcdex.dat")
	sqlDb, err := sql.Open("sqlite3", db.sqlDbPath)
	if err != nil {
		return nil, err
	}

	_, err = sqlDb.Exec("PRAGMA integrity_check;")
	if err != nil {
		return nil, err
	}

	db.sqlDb = sqlDb

	return db, nil
}

func InstallDatabase(skipIfExists bool) error {
	if skipIfExists && fileExists(filepath.Join(Env().McdexDir, "mcdex.dat")) {
		return nil
	}

	// Get the latest version
	version, err := ReadStringFromUrl("http://files.mcdex.net/data/latest.v6")
	if err != nil {
		return err
	}

	// Download the latest data file to mcdex/mcdex.dat
	url := fmt.Sprintf("http://files.mcdex.net/data/mcdex-v6-%s.dat.bz2", version)
	res, err := HttpGet(url)
	if err != nil {
		return fmt.Errorf("Failed to retrieve %s data file: %+v", version, err)
	}
	defer res.Body.Close()

	// Stream the data file to mcdex.dat.tmp
	tmpFileName := filepath.Join(Env().McdexDir, "mcdex.dat.tmp")
	err = writeStream(tmpFileName, bzip2.NewReader(res.Body))
	if err != nil {
		return err
	}

	// Open the temporary database and validate it
	tmpDb, err := sql.Open("sqlite3", tmpFileName)
	if err != nil {
		// TODO: Add log entry about the file being corrupt
		return err
	}
	defer tmpDb.Close()

	_, err = tmpDb.Exec("PRAGMA integrity_check;")
	if err != nil {
		return err
	}

	// Force the tmpDb to close so that (on Windows), we can ensure
	// the rename works
	tmpDb.Close()

	// Close the database and rename the tmp file
	err = os.Rename(tmpFileName, filepath.Join(Env().McdexDir, "mcdex.dat"))
	if err != nil {
		return fmt.Errorf("Failed to rename mcdex.dat.tmp: %+v", err)
	}
	return nil
}

func (db *Database) ListForge(mcvsn string, verbose bool) error {
	rows, err := db.sqlDb.Query("select version, isrec from forge where mcvsn = ? order by version desc", mcvsn)
	switch {
	case err == sql.ErrNoRows:
		return fmt.Errorf("No Forge version found for %s", mcvsn)
	case err != nil:
		return err
	}

	latest := false

	defer rows.Close()
	for rows.Next() {
		var version string
		var isrec bool
		err := rows.Scan(&version, &isrec)
		if err != nil {
			return err
		}
		if isrec {
			fmt.Printf("%s (recommended)\n", version)
		} else if !latest {
			fmt.Printf("%s (latest)\n", version)
			latest = true
		} else if verbose {
			fmt.Printf("%s\n", version)
		}
	}
	return nil
}

func (db *Database) lookupForgeVsn(mcvsn string) (string, error) {
	var forgeVsn string
	err := db.sqlDb.QueryRow("select version from forge where mcvsn = ? and isrec = 1", mcvsn).Scan(&forgeVsn)
	switch {
	case err == sql.ErrNoRows:
		return "", fmt.Errorf("No Forge version found for %s", mcvsn)
	case err != nil:
		return "", err
	}
	return forgeVsn, nil
}

func (db *Database) lookupFabricVsn(mcvsn string) (string, error) {
	var fabricVsn string
	err := db.sqlDb.QueryRow("SELECT version FROM fabric_loaders WHERE mcversion = ?", mcvsn).Scan((&fabricVsn))
	switch {
	case err == sql.ErrNoRows:
		return "", fmt.Errorf("No Fabric version found for %s", mcvsn)
	case err != nil:
		return "", err
	}

	return fabricVsn, nil
}

func (db *Database) PrintProjects(slug, mcvsn string, ptype int) error {
	// Turn the name into a pre-compiled regex
	slugRegex, err := regexp.Compile("(?i)" + slug)
	if err != nil {
		return fmt.Errorf("Failed to convert %s into regex: %s", slug, err)
	}

	query := "select slug, description from projects where type = ? and projectid in (select projectid from versions where mcvsn = ?) order by slug"
	if mcvsn == "" {
		query = "select slug, description from projects where type = ? order by slug"
	}

	rows, err := db.sqlDb.Query(query, ptype, mcvsn)
	if err != nil {
		return fmt.Errorf("Query failed: %+v", err)
	}
	defer rows.Close()

	// For each row, check the name against the pre-compiled regex
	for rows.Next() {
		var slug, desc string
		err = rows.Scan(&slug, &desc)
		if err != nil {
			return err
		}

		if slug == "" || slugRegex.MatchString(slug) {
			msg := message.NewPrinter(language.English)
			msg.Printf("%s | %s\n", slug, desc)
		}
	}

	return nil
}

func (db *Database) PrintLatestProjects(mcvsn string, ptype int) error {
	rows, err := db.sqlDb.Query(`select slug, description from projects
									    where type = ? and projectid in
									    (select projectid from files order by tstamp desc) limit 100`, ptype)
	if err != nil {
		return fmt.Errorf("Query failed: %+v", err)
	}
	defer rows.Close()

	for rows.Next() {
		var modSlug, modDesc string

		err = rows.Scan(&modSlug, &modDesc)
		if err != nil {
			return err
		}

		msg := message.NewPrinter(language.English)
		msg.Printf("%s | %s\n", modSlug, modDesc)
	}
	return nil
}

func (db *Database) GetLatestFileTstamp() (int, error) {
	var tstamp int
	err := db.sqlDb.QueryRow("select value from meta where key = 'dbtunix'").Scan(&tstamp)
	return tstamp, err
}

func (db *Database) FindProjectBySlug(slug string, modLoader string, ptype int) (int, error) {
	var modID int
	var supportedModLoader string
	err := db.sqlDb.QueryRow("select projectid, modloader from projects where type = ? and slug = ?", ptype, slug).Scan(&modID, &supportedModLoader)
	switch {
	case err == sql.ErrNoRows:
		return -1, fmt.Errorf("no mod found %s", slug)
	case err != nil:
		return -1, err
	}

	if  modLoader != supportedModLoader && modLoader != "fabric+forge" && supportedModLoader != "fabric+forge" {
		return -1, fmt.Errorf("%s (%s) is not compatible with %s", slug, supportedModLoader, modLoader)
	}

	return modID, nil
}

func (db *Database) findSlugByProject(id int) (string, error) {
	var slug string
	err := db.sqlDb.QueryRow("select slug from projects where projectid = ?", id).Scan(&slug)
	switch {
	case err == sql.ErrNoRows:
		return "", fmt.Errorf("no project found %d", id)
	case err != nil:
		return slug, err
	}
	return slug, nil
}

func (db *Database) findModBySlug(slug string, modLoader string) (int, error) {
	return db.FindProjectBySlug(slug, modLoader, 0)
}

func (db *Database) findModByName(name string) (int, error) {
	var modID int
	err := db.sqlDb.QueryRow("select projectid from projects where type = 0 and (name = ? or slug = ?)", name, name).Scan(&modID)
	switch {
	case err == sql.ErrNoRows:
		return -1, fmt.Errorf("No mod found %s", name)
	case err != nil:
		return -1, err
	}
	return modID, nil
}

func (db *Database) getProjectInfo(projectID int) (string, string, string, error) {
	var slug, name, desc string
	err := db.sqlDb.QueryRow("select slug, name, description from projects where projectid = ? and type = 0", projectID).Scan(&slug, &name, &desc)
	if err != nil {
		return "", "", "", fmt.Errorf("failed to get project info for %d: %+v", projectID, err)
	}

	return slug, name, desc, nil
}

func (db *Database) getDeps(fileID int) ([]string, error) {
	var result []string
	rows, err := db.sqlDb.Query("SELECT projectid, level FROM deps WHERE fileid = ? and level == 1", fileID)

	switch {
	case err == sql.ErrNoRows:
		return []string{}, nil
	case err != nil:
		return []string{}, fmt.Errorf("Failed to query deps for %d: %+v", fileID, err)
	}
	defer rows.Close()

	for rows.Next() {
		var projectID, level int
		err = rows.Scan(&projectID, &level)
		if err != nil {
			return []string{}, fmt.Errorf("Failed to query dep rows for %d: %+v", fileID, err)
		}

		// Resolve the project ID to a slug
		var slug string
		err = db.sqlDb.QueryRow("select slug from projects where projectid = ?", projectID).Scan(&slug)
		if err != nil {
			return []string{}, fmt.Errorf("failed to resolve dep project %d to a slug", projectID)
		}

		result = append(result, slug)
	}

	return result, nil
}

func (db *Database) GetLatestPackURL(slug string) (string, error) {
	// First try to find the pack by looking for the specific slug
	// TODO: Remove forge
	pid, err := db.FindProjectBySlug(slug, "forge",1)
	if err != nil {
		return "", err
	}

	// Find the latest file given the project ID; we don't need to worry about matching the MC version,
	// since modpacks are always locked to a specific version anyways
	var fileID int
	err = db.sqlDb.QueryRow("select fileid from files where projectid = ? order by tstamp desc limit 1", pid).Scan(&fileID)
	switch {
	case err == sql.ErrNoRows:
		return "", fmt.Errorf("No modpack file found for %s", slug)
	case err != nil:
		return "", err
	}

	// Construct a URL using the slug and file ID
	return fmt.Sprintf("https://minecraft.curseforge.com/projects/%d/files/%d/download", pid, fileID), nil

}

type ForEachModHandler func(id int, slug string, description string, downloads int) error

func (db *Database) ForEachMod(mcvsn string, loader string, handler ForEachModHandler) (int, error) {
	rows, err := db.sqlDb.Query("select projectid, slug, description, downloads from projects where type = ? and modloader = ? and projectid in (select projectid from versions where mcvsn = ?) order by downloads desc",
		0, loader, mcvsn)

	switch {
	case err == sql.ErrNoRows:
		return 0, nil
	case err != nil:
		return 0, fmt.Errorf("failed to lookup mods: %+v", err)
	}
	defer rows.Close()

	var count int

	for rows.Next() {
		var projectID, downloads int
		var slug, description string
		err = rows.Scan(&projectID, &slug, &description, &downloads)
		if err != nil {
			return 0, fmt.Errorf("failed to scan row: %+v", err)
		}

		count++

		err := handler(projectID, slug, description, downloads)
		if err != nil {
			return count, fmt.Errorf("handler failed: %+v", err)
		}
	}

	return count, nil
}

func (db *Database) GetSupportedMCVersions(loader string) ([]string, error) {
	var query string
	if loader == "forge" {
		query = "select distinct(mcvsn) from forge where isrec=1"
	} else {
		query = "select distinct(mcversion) from fabric_loaders"
	}

	rows, err := db.sqlDb.Query(query)
	switch {
	case err == sql.ErrNoRows:
		return []string{}, nil
	case err != nil:
		return []string{}, fmt.Errorf("failed to lookup mods: %+v", err)
	}
	defer rows.Close()

	var result []string
	for rows.Next() {
		var mcvsn string
		err = rows.Scan(&mcvsn)
		if err != nil {
			return []string{}, fmt.Errorf("failed to scan row: %+v", err)
		}

		result = append(result, mcvsn)
	}

	return result, nil
}<|endoftext|>