<commit_before>package script

import (
	"github.com/aarzilli/golua/lua"
	anko_core "github.com/mattn/anko/builtins"
	anko_encoding "github.com/mattn/anko/builtins/encoding"
	anko_flag "github.com/mattn/anko/builtins/flag"
	anko_io "github.com/mattn/anko/builtins/io"
	anko_math "github.com/mattn/anko/builtins/math"
	anko_net "github.com/mattn/anko/builtins/net"
	anko_os "github.com/mattn/anko/builtins/os"
	anko_path "github.com/mattn/anko/builtins/path"
	anko_regexp "github.com/mattn/anko/builtins/regexp"
	anko_sort "github.com/mattn/anko/builtins/sort"
	anko_strings "github.com/mattn/anko/builtins/strings"
	anko_term "github.com/mattn/anko/builtins/term"
	anko "github.com/mattn/anko/vm"
	"github.com/robertkrimen/otto"
	"github.com/tyler-sommer/squircy2/squircy/event"
	glispext "github.com/zhemao/glisp/extensions"
	glisp "github.com/zhemao/glisp/interpreter"
	"errors"
	"reflect"
	"strconv"
)

func newJavascriptVm(m *ScriptManager) *otto.Otto {
	jsVm := otto.New()
	jsVm.Set("Http", &m.httpHelper)
	jsVm.Set("Data", &m.dataHelper)
	jsVm.Set("Irc", &m.ircHelper)
	jsVm.Set("bind", func(call otto.FunctionCall) otto.Value {
		eventType := call.Argument(0).String()
		fnName := call.Argument(1).String()
		m.scriptHelper.Bind(Javascript, event.EventType(eventType), fnName)
		return otto.UndefinedValue()
	})
	jsVm.Set("unbind", func(call otto.FunctionCall) otto.Value {
		eventType := call.Argument(0).String()
		fnName := call.Argument(1).String()
		m.scriptHelper.Unbind(Javascript, event.EventType(eventType), fnName)
		return otto.UndefinedValue()
	})

	return jsVm
}

func newLuaVm(m *ScriptManager) *lua.State {
	luaVm := lua.NewState()
	luaVm.OpenLibs()
	luaVm.Register("typename", func(vm *lua.State) int {
		o := vm.Typename(int(vm.Type(1)))
		vm.PushString(o)
		return 1
	})
	luaVm.Register("setex", func(vm *lua.State) int {
		key := vm.ToString(1)
		value := vm.ToString(2)
		m.dataHelper.Set(key, value)
		return 0
	})
	luaVm.Register("getex", func(vm *lua.State) int {
		key := vm.ToString(1)
		value := m.dataHelper.Get(key)
		if value != nil {
			vm.PushString(value.(string))
			return 1
		}
		vm.PushNil()
		return 1
	})
	luaVm.Register("joinchan", func(vm *lua.State) int {
		channel := vm.ToString(1)
		m.ircHelper.Join(channel)
		return 0
	})
	luaVm.Register("partchan", func(vm *lua.State) int {
		channel := vm.ToString(1)
		m.ircHelper.Part(channel)
		return 0
	})
	luaVm.Register("privmsg", func(vm *lua.State) int {
		target := vm.ToString(1)
		message := vm.ToString(2)
		m.ircHelper.Privmsg(target, message)
		return 0
	})
	luaVm.Register("httpget", func(vm *lua.State) int {
		url := vm.ToString(1)
		res := m.httpHelper.Get(url)
		vm.PushString(res)
		return 1
	})
	luaVm.Register("bind", func(vm *lua.State) int {
		eventType := vm.ToString(1)
		fnName := vm.ToString(2)
		m.scriptHelper.Bind(Lua, event.EventType(eventType), fnName)
		return 0
	})
	luaVm.Register("unbind", func(vm *lua.State) int {
		eventType := vm.ToString(1)
		fnName := vm.ToString(2)
		m.scriptHelper.Unbind(Lua, event.EventType(eventType), fnName)
		return 0
	})

	return luaVm
}

func newLispVm(m *ScriptManager) *glisp.Glisp {
	lispVm := glisp.NewGlisp()
	lispVm.ImportEval()
	glispext.ImportRandom(lispVm)
	glispext.ImportTime(lispVm)
	glispext.ImportChannels(lispVm)
	glispext.ImportCoroutines(lispVm)

	lispVm.AddFunction("setex", func(vm *glisp.Glisp, name string, args []glisp.Sexp) (glisp.Sexp, error) {
		if len(args) != 2 {
			return glisp.SexpNull, errors.New("incorrect number of arguments")
		}

		key := sexpToString(args[0])
		val := sexpToInterface(args[1])
		m.dataHelper.Set(key, val)

		return glisp.SexpNull, nil
	})
	lispVm.AddFunction("getex", func(vm *glisp.Glisp, name string, args []glisp.Sexp) (glisp.Sexp, error) {
		if len(args) != 1 {
			return glisp.SexpNull, errors.New("incorrect number of arguments")
		}

		key := sexpToString(args[0])
		val := m.dataHelper.Get(key).(string)

		return glisp.SexpStr(val), nil
	})
	lispVm.AddFunction("joinchan", func(vm *glisp.Glisp, name string, args []glisp.Sexp) (glisp.Sexp, error) {
		if len(args) != 1 {
			return glisp.SexpNull, errors.New("incorrect number of arguments")
		}

		channel := sexpToString(args[0])
		m.ircHelper.Join(channel)

		return glisp.SexpNull, nil
	})
	lispVm.AddFunction("partchan", func(vm *glisp.Glisp, name string, args []glisp.Sexp) (glisp.Sexp, error) {
		if len(args) != 1 {
			return glisp.SexpNull, errors.New("incorrect number of arguments")
		}

		channel := sexpToString(args[0])
		m.ircHelper.Part(channel)

		return glisp.SexpNull, nil
	})
	lispVm.AddFunction("privmsg", func(vm *glisp.Glisp, name string, args []glisp.Sexp) (glisp.Sexp, error) {
		if len(args) != 2 {
			return glisp.SexpNull, errors.New("incorrect number of arguments")
		}

		target := sexpToString(args[0])
		message := sexpToString(args[1])
		m.ircHelper.Privmsg(target, message)

		return glisp.SexpNull, nil
	})
	lispVm.AddFunction("httpget", func(vm *glisp.Glisp, name string, args []glisp.Sexp) (glisp.Sexp, error) {
		if len(args) != 1 {
			return glisp.SexpNull, errors.New("incorrect number of arguments")
		}

		url := sexpToString(args[0])
		resp := m.httpHelper.Get(url)

		return glisp.SexpStr(resp), nil
	})
	lispVm.AddFunction("bind", func(vm *glisp.Glisp, name string, args []glisp.Sexp) (glisp.Sexp, error) {
		if len(args) != 2 {
			return glisp.SexpNull, errors.New("incorrect number of arguments")
		}

		eventType := sexpToString(args[0])
		fnName := sexpToString(args[1])
		m.scriptHelper.Bind(Lisp, event.EventType(eventType), fnName)

		return glisp.SexpNull, nil
	})
	lispVm.AddFunction("unbind", func(vm *glisp.Glisp, name string, args []glisp.Sexp) (glisp.Sexp, error) {
		if len(args) != 2 {
			return glisp.SexpNull, errors.New("incorrect number of arguments")
		}

		eventType := sexpToString(args[0])
		fnName := sexpToString(args[1])
		m.scriptHelper.Unbind(Lisp, event.EventType(eventType), fnName)

		return glisp.SexpNull, nil
	})

	return lispVm
}

func newAnkoVm(m *ScriptManager) *anko.Env {
	ankoVm := anko.NewEnv()
	anko_core.Import(ankoVm)
	anko_flag.Import(ankoVm)
	anko_net.Import(ankoVm)
	anko_encoding.Import(ankoVm)
	anko_os.Import(ankoVm)
	anko_io.Import(ankoVm)
	anko_math.Import(ankoVm)
	anko_path.Import(ankoVm)
	anko_regexp.Import(ankoVm)
	anko_sort.Import(ankoVm)
	anko_strings.Import(ankoVm)
	anko_term.Import(ankoVm)

	mod := ankoVm.NewModule("data")
	mod.Define("Get", reflect.ValueOf(m.dataHelper.Get))
	mod.Define("Set", reflect.ValueOf(m.dataHelper.Set))

	mod = ankoVm.NewModule("irc")
	mod.Define("Join", reflect.ValueOf(func(channel string) {
		m.ircHelper.Join(channel)
	}))
	mod.Define("Part", reflect.ValueOf(func(channel string) {
		m.ircHelper.Part(channel)
	}))
	mod.Define("Privmsg", reflect.ValueOf(func(target, message string) {
		m.ircHelper.Privmsg(target, message)
	}))

	mod = ankoVm.NewModule("strconv")
	mod.Define("ParseInt", reflect.ValueOf(func(s string) int {
		i, _ := strconv.ParseInt(s, 0, 64)
		return int(i)
	}))

	ankoVm.Define("bind", reflect.ValueOf(func(eventType, fnName string) {
		m.scriptHelper.Bind(Anko, event.EventType(eventType), fnName)
	}))

	ankoVm.Define("unbind", reflect.ValueOf(func(eventType, fnName string) {
		m.scriptHelper.Unbind(Anko, event.EventType(eventType), fnName)
	}))

	return ankoVm
}
<commit_msg>Added parse-integer and write-to-string functions to glisp env<commit_after>package script

import (
	"github.com/aarzilli/golua/lua"
	anko_core "github.com/mattn/anko/builtins"
	anko_encoding "github.com/mattn/anko/builtins/encoding"
	anko_flag "github.com/mattn/anko/builtins/flag"
	anko_io "github.com/mattn/anko/builtins/io"
	anko_math "github.com/mattn/anko/builtins/math"
	anko_net "github.com/mattn/anko/builtins/net"
	anko_os "github.com/mattn/anko/builtins/os"
	anko_path "github.com/mattn/anko/builtins/path"
	anko_regexp "github.com/mattn/anko/builtins/regexp"
	anko_sort "github.com/mattn/anko/builtins/sort"
	anko_strings "github.com/mattn/anko/builtins/strings"
	anko_term "github.com/mattn/anko/builtins/term"
	anko "github.com/mattn/anko/vm"
	"github.com/robertkrimen/otto"
	"github.com/tyler-sommer/squircy2/squircy/event"
	glispext "github.com/zhemao/glisp/extensions"
	glisp "github.com/zhemao/glisp/interpreter"
	"errors"
	"reflect"
	"strconv"
	"fmt"
)

func newJavascriptVm(m *ScriptManager) *otto.Otto {
	jsVm := otto.New()
	jsVm.Set("Http", &m.httpHelper)
	jsVm.Set("Data", &m.dataHelper)
	jsVm.Set("Irc", &m.ircHelper)
	jsVm.Set("bind", func(call otto.FunctionCall) otto.Value {
		eventType := call.Argument(0).String()
		fnName := call.Argument(1).String()
		m.scriptHelper.Bind(Javascript, event.EventType(eventType), fnName)
		return otto.UndefinedValue()
	})
	jsVm.Set("unbind", func(call otto.FunctionCall) otto.Value {
		eventType := call.Argument(0).String()
		fnName := call.Argument(1).String()
		m.scriptHelper.Unbind(Javascript, event.EventType(eventType), fnName)
		return otto.UndefinedValue()
	})

	return jsVm
}

func newLuaVm(m *ScriptManager) *lua.State {
	luaVm := lua.NewState()
	luaVm.OpenLibs()
	luaVm.Register("typename", func(vm *lua.State) int {
		o := vm.Typename(int(vm.Type(1)))
		vm.PushString(o)
		return 1
	})
	luaVm.Register("setex", func(vm *lua.State) int {
		key := vm.ToString(1)
		value := vm.ToString(2)
		m.dataHelper.Set(key, value)
		return 0
	})
	luaVm.Register("getex", func(vm *lua.State) int {
		key := vm.ToString(1)
		value := m.dataHelper.Get(key)
		if value != nil {
			vm.PushString(value.(string))
			return 1
		}
		vm.PushNil()
		return 1
	})
	luaVm.Register("joinchan", func(vm *lua.State) int {
		channel := vm.ToString(1)
		m.ircHelper.Join(channel)
		return 0
	})
	luaVm.Register("partchan", func(vm *lua.State) int {
		channel := vm.ToString(1)
		m.ircHelper.Part(channel)
		return 0
	})
	luaVm.Register("privmsg", func(vm *lua.State) int {
		target := vm.ToString(1)
		message := vm.ToString(2)
		m.ircHelper.Privmsg(target, message)
		return 0
	})
	luaVm.Register("httpget", func(vm *lua.State) int {
		url := vm.ToString(1)
		res := m.httpHelper.Get(url)
		vm.PushString(res)
		return 1
	})
	luaVm.Register("bind", func(vm *lua.State) int {
		eventType := vm.ToString(1)
		fnName := vm.ToString(2)
		m.scriptHelper.Bind(Lua, event.EventType(eventType), fnName)
		return 0
	})
	luaVm.Register("unbind", func(vm *lua.State) int {
		eventType := vm.ToString(1)
		fnName := vm.ToString(2)
		m.scriptHelper.Unbind(Lua, event.EventType(eventType), fnName)
		return 0
	})

	return luaVm
}

func newLispVm(m *ScriptManager) *glisp.Glisp {
	lispVm := glisp.NewGlisp()
	lispVm.ImportEval()
	glispext.ImportRandom(lispVm)
	glispext.ImportTime(lispVm)
	glispext.ImportChannels(lispVm)
	glispext.ImportCoroutines(lispVm)

	lispVm.AddFunction("setex", func(vm *glisp.Glisp, name string, args []glisp.Sexp) (glisp.Sexp, error) {
		if len(args) != 2 {
			return glisp.SexpNull, errors.New("incorrect number of arguments")
		}

		key := sexpToString(args[0])
		val := sexpToInterface(args[1])
		m.dataHelper.Set(key, val)

		return glisp.SexpNull, nil
	})
	lispVm.AddFunction("getex", func(vm *glisp.Glisp, name string, args []glisp.Sexp) (glisp.Sexp, error) {
		if len(args) != 1 {
			return glisp.SexpNull, errors.New("incorrect number of arguments")
		}

		key := sexpToString(args[0])
		val := m.dataHelper.Get(key).(string)

		return glisp.SexpStr(val), nil
	})
	lispVm.AddFunction("joinchan", func(vm *glisp.Glisp, name string, args []glisp.Sexp) (glisp.Sexp, error) {
		if len(args) != 1 {
			return glisp.SexpNull, errors.New("incorrect number of arguments")
		}

		channel := sexpToString(args[0])
		m.ircHelper.Join(channel)

		return glisp.SexpNull, nil
	})
	lispVm.AddFunction("partchan", func(vm *glisp.Glisp, name string, args []glisp.Sexp) (glisp.Sexp, error) {
		if len(args) != 1 {
			return glisp.SexpNull, errors.New("incorrect number of arguments")
		}

		channel := sexpToString(args[0])
		m.ircHelper.Part(channel)

		return glisp.SexpNull, nil
	})
	lispVm.AddFunction("privmsg", func(vm *glisp.Glisp, name string, args []glisp.Sexp) (glisp.Sexp, error) {
		if len(args) != 2 {
			return glisp.SexpNull, errors.New("incorrect number of arguments")
		}

		target := sexpToString(args[0])
		message := sexpToString(args[1])
		m.ircHelper.Privmsg(target, message)

		return glisp.SexpNull, nil
	})
	lispVm.AddFunction("httpget", func(vm *glisp.Glisp, name string, args []glisp.Sexp) (glisp.Sexp, error) {
		if len(args) != 1 {
			return glisp.SexpNull, errors.New("incorrect number of arguments")
		}

		url := sexpToString(args[0])
		resp := m.httpHelper.Get(url)

		return glisp.SexpStr(resp), nil
	})
	lispVm.AddFunction("bind", func(vm *glisp.Glisp, name string, args []glisp.Sexp) (glisp.Sexp, error) {
		if len(args) != 2 {
			return glisp.SexpNull, errors.New("incorrect number of arguments")
		}

		eventType := sexpToString(args[0])
		fnName := sexpToString(args[1])
		m.scriptHelper.Bind(Lisp, event.EventType(eventType), fnName)

		return glisp.SexpNull, nil
	})
	lispVm.AddFunction("unbind", func(vm *glisp.Glisp, name string, args []glisp.Sexp) (glisp.Sexp, error) {
		if len(args) != 2 {
			return glisp.SexpNull, errors.New("incorrect number of arguments")
		}

		eventType := sexpToString(args[0])
		fnName := sexpToString(args[1])
		m.scriptHelper.Unbind(Lisp, event.EventType(eventType), fnName)

		return glisp.SexpNull, nil
	})
	lispVm.AddFunction("parse-integer", func(vm *glisp.Glisp, name string, args[]glisp.Sexp) (glisp.Sexp, error) {
		if len(args) != 1 {
			return glisp.SexpNull, errors.New("incorrect number of arguments")
		}

		switch t := args[0].(type) {
		case glisp.SexpStr:
			val, err := strconv.ParseInt(string(t), 0, 64)
			if err != nil {
				return glisp.SexpNull, err
			}
			return glisp.SexpInt(int(val)), nil

		default:
			return glisp.SexpNull, errors.New(fmt.Sprintf("cannot convert %v to int", t))
		}
	})
	lispVm.AddFunction("write-to-string", func(vm *glisp.Glisp, name string, args[]glisp.Sexp) (glisp.Sexp, error) {
		if len(args) != 1 {
			return glisp.SexpNull, errors.New("incorrect number of arguments")
		}

		switch t := args[0].(type) {
		case glisp.SexpStr:
			return t, nil

		case glisp.SexpInt:
			return glisp.SexpStr(strconv.Itoa(int(t))), nil

		default:
			return glisp.SexpStr(fmt.Sprintf("%v", t))
		}
	})

	return lispVm
}

func newAnkoVm(m *ScriptManager) *anko.Env {
	ankoVm := anko.NewEnv()
	anko_core.Import(ankoVm)
	anko_flag.Import(ankoVm)
	anko_net.Import(ankoVm)
	anko_encoding.Import(ankoVm)
	anko_os.Import(ankoVm)
	anko_io.Import(ankoVm)
	anko_math.Import(ankoVm)
	anko_path.Import(ankoVm)
	anko_regexp.Import(ankoVm)
	anko_sort.Import(ankoVm)
	anko_strings.Import(ankoVm)
	anko_term.Import(ankoVm)

	mod := ankoVm.NewModule("data")
	mod.Define("Get", reflect.ValueOf(m.dataHelper.Get))
	mod.Define("Set", reflect.ValueOf(m.dataHelper.Set))

	mod = ankoVm.NewModule("irc")
	mod.Define("Join", reflect.ValueOf(func(channel string) {
		m.ircHelper.Join(channel)
	}))
	mod.Define("Part", reflect.ValueOf(func(channel string) {
		m.ircHelper.Part(channel)
	}))
	mod.Define("Privmsg", reflect.ValueOf(func(target, message string) {
		m.ircHelper.Privmsg(target, message)
	}))

	mod = ankoVm.NewModule("strconv")
	mod.Define("ParseInt", reflect.ValueOf(func(s string) int {
		i, _ := strconv.ParseInt(s, 0, 64)
		return int(i)
	}))

	ankoVm.Define("bind", reflect.ValueOf(func(eventType, fnName string) {
		m.scriptHelper.Bind(Anko, event.EventType(eventType), fnName)
	}))

	ankoVm.Define("unbind", reflect.ValueOf(func(eventType, fnName string) {
		m.scriptHelper.Unbind(Anko, event.EventType(eventType), fnName)
	}))

	return ankoVm
}
<|endoftext|>


<commit_before>package triematcher

import (
	"os"
	"text/template"
)

var templateFile = template.Must(template.New("file").Parse(`
// DO NOT EDIT!
// Code generated by go-triematcher <https://github.com/Maki-Daisuke/go-triematcher>
// DO NOT EDIT!

package {{ .PackageName }}

import (
  "strings"
)

func {{ .FuncName }}(str string) bool {
  defer func(){
    recover() // Must be "index out of range" error for string.
              // Ignore and return false.
  }()

  goto STATE_{{ with index .States 0 }}{{ .Id }}{{ end }}
{{ range .States }}
  STATE_{{ .Id }}:
  {{ if .IsGoal }}
      return len(str) == 0
  {{ else }}
    switch{
    {{ range .OutBounds }}
      case strings.HasPrefix(str, {{ printf "%q" .Key }}):
        str = str[{{ .Key | len }}:]
        goto STATE_{{ .State.Id }}
    {{ end }}
      default:
        return false
    }
  {{ end }}
{{ end }}
}
`))

func generate(pkg_name, func_name string, st *state) {
	err := templateFile.Execute(os.Stdout, map[string]interface{}{
		"PackageName": pkg_name,
		"FuncName":    func_name,
		"States":      allStates(st),
	})
	if err != nil {
		panic(err)
	}
}
<commit_msg>generate() receives io.Writer for output.<commit_after>package triematcher

import (
	"io"
	"text/template"
)

var templateFile = template.Must(template.New("file").Parse(`
// DO NOT EDIT!
// Code generated by go-triematcher <https://github.com/Maki-Daisuke/go-triematcher>
// DO NOT EDIT!

package {{ .PackageName }}

import (
  "strings"
)

func {{ .FuncName }}(str string) bool {
  defer func(){
    recover() // Must be "index out of range" error for string.
              // Ignore and return false.
  }()

  goto STATE_{{ with index .States 0 }}{{ .Id }}{{ end }}
{{ range .States }}
  STATE_{{ .Id }}:
  {{ if .IsGoal }}
      return len(str) == 0
  {{ else }}
    switch{
    {{ range .OutBounds }}
      case strings.HasPrefix(str, {{ printf "%q" .Key }}):
        str = str[{{ .Key | len }}:]
        goto STATE_{{ .State.Id }}
    {{ end }}
      default:
        return false
    }
  {{ end }}
{{ end }}
}
`))

func generate(w io.Writer, pkg_name, func_name string, st *state) error {
	err := templateFile.Execute(w, map[string]interface{}{
		"PackageName": pkg_name,
		"FuncName":    func_name,
		"States":      allStates(st),
	})
	if err != nil {
		return err
	} else {
		return nil
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2021 The GoPlus Authors (goplus.org). All rights reserved.
 *
 * 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 env

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"github.com/goplus/gop/cl"
)

func findGoModFile(dir string) (modfile string, noCacheFile bool, err error) {
	modfile, err = cl.FindGoModFile(dir)

	if err != nil {
		gopRoot, err := findGopRoot()
		if err == nil {
			modfile = filepath.Join(gopRoot, "go.mod")
			return modfile, true, nil
		}
	}
	return
}

// Common testing directory structure:
// testing_root/
//   src/
//	   subdir/
//   valid_goproot/
//	   go.mod
//	   go.sum
//     builtin/
func makeTestDir(t *testing.T) (root string, src string, gopRoot string) {
	root, _ = filepath.EvalSymlinks(t.TempDir())
	src = filepath.Join(root, "src")
	gopRoot = filepath.Join(root, "valid_goproot")
	makeValidGopRoot(gopRoot)
	os.Mkdir(src, 0755)
	return
}

func makeValidGopRoot(root string) {
	os.Mkdir(root, 0755)
	os.Mkdir(filepath.Join(root, "builtin"), 0755)
	ioutil.WriteFile(filepath.Join(root, "go.mod"), []byte(""), 0644)
	ioutil.WriteFile(filepath.Join(root, "go.sum"), []byte(""), 0644)
}

func writeDummyFile(path string) {
	ioutil.WriteFile(path, []byte(""), 0644)
}

func cleanup() {
	os.Setenv("GOPROOT", "")
	os.Setenv("HOME", "")
	defaultGopRoot = ""
}

func TestBasic(t *testing.T) {
	if GOPROOT() == "" {
		t.Fatal("TestBasic failed")
	}
}

func TestFindGoModFileInGoModDir(t *testing.T) {
	cleanup()

	t.Run("the src/ is a valid mod dir", func(tt *testing.T) {
		tt.Cleanup(cleanup)
		_, src, _ := makeTestDir(tt)
		subdir := filepath.Join(src, "subdir")
		writeDummyFile(filepath.Join(src, "go.mod"))
		os.Mkdir(subdir, 0755)

		{
			modfile, noCacheFile, err := findGoModFile(src)

			if err != nil || modfile != filepath.Join(src, "go.mod") || noCacheFile {
				tt.Fatal("got:", modfile, noCacheFile, err)
			}
		}

		{
			// Should found go.mod in parent dir
			modfile, noCacheFile, err := findGoModFile(subdir)

			if err != nil || modfile != filepath.Join(src, "go.mod") || noCacheFile {
				tt.Fatal("got:", modfile, noCacheFile, err)
			}
		}
	})

	t.Run("the src/ is not a valid mod dir", func(tt *testing.T) {
		tt.Cleanup(cleanup)
		_, src, _ := makeTestDir(tt)

		modfile, noCacheFile, err := findGoModFile(src)

		if err == nil {
			tt.Fatal("should not found the mod file, but got:", modfile, noCacheFile)
		}
	})
}

func TestFindGoModFileInGopRoot(t *testing.T) {
	origiExecutable := executable
	t.Cleanup(func() {
		executable = origiExecutable
	})

	home := t.TempDir()
	bin := filepath.Join(home, "bin")

	// Don't find go.mod in gop source dir when testing
	os.Chdir(home)

	cleanupAll := func() {
		cleanup()
		executable = func() (string, error) {
			return filepath.Join(bin, "run"), nil
		}
	}
	cleanupAll()

	t.Run("without gop root", func(tt *testing.T) {
		tt.Cleanup(cleanupAll)
		root, _, _ := makeTestDir(tt)

		modfile, noCacheFile, err := findGoModFile(root)

		if err == nil || noCacheFile || modfile != "" {
			tt.Fatal("should not found go.mod without gop root, got:", modfile, noCacheFile, err)
		}
	})

	t.Run("set GOPROOT to a valid goproot path", func(tt *testing.T) {
		tt.Cleanup(cleanupAll)
		_, src, gopRoot := makeTestDir(tt)

		os.Setenv("GOPROOT", gopRoot)
		modfile, noCacheFile, err := findGoModFile(src)

		if err != nil || modfile != filepath.Join(gopRoot, "go.mod") || !noCacheFile {
			tt.Fatal("should found mod file in GOPROOT, got:", modfile, noCacheFile, err)
		}
	})

	t.Run("set GOPROOT to an invalid goproot path", func(tt *testing.T) {
		tt.Cleanup(cleanupAll)
		root, src, _ := makeTestDir(tt)
		invalidGopRoot := filepath.Join(root, "invalid_goproot")

		defer func() {
			r := recover()
			if r == nil {
				tt.Fatal("should panic, but not")
			}
		}()

		os.Setenv("GOPROOT", invalidGopRoot)
		findGoModFile(src)
	})

	t.Run("set build.GopRoot to a valid goproot path", func(tt *testing.T) {
		tt.Cleanup(cleanupAll)
		_, src, gopRoot := makeTestDir(tt)

		defaultGopRoot = gopRoot
		modfile, noCacheFile, err := findGoModFile(src)

		if err != nil || modfile != filepath.Join(gopRoot, "go.mod") || !noCacheFile {
			tt.Fatal("should found go.mod in the dir of build.GopRoot, got:", modfile, noCacheFile, err)
		}
	})

	t.Run("set build.GopRoot to an invalid path", func(tt *testing.T) {
		tt.Cleanup(cleanupAll)
		root, src, _ := makeTestDir(tt)
		invalidGopRoot := filepath.Join(root, "invalid_goproot")

		defaultGopRoot = invalidGopRoot
		{
			modfile, noCacheFile, err := findGoModFile(src)

			if err == nil || noCacheFile || modfile != "" {
				tt.Fatal("should not found go.mod when build.GopRoot isn't exists, got:", modfile, noCacheFile, err)
			}
		}

		{
			os.Mkdir(invalidGopRoot, 0755)
			modfile, noCacheFile, err := findGoModFile(src)

			if err == nil || noCacheFile || modfile != "" {
				tt.Fatal("should not found go.mod when build.GopRoot isn't an valid gop root dir, got:", modfile, noCacheFile, err)
			}
		}
	})

	t.Run("use $HOME/gop or $HOME/goplus", func(tt *testing.T) {
		tt.Cleanup(cleanupAll)
		root, src, _ := makeTestDir(tt)
		home := filepath.Join(root, "home")
		os.Mkdir(home, 0755)
		os.Setenv("HOME", home)

		{
			gopRoot := filepath.Join(home, "goplus")
			makeValidGopRoot(gopRoot)

			modfile, noCacheFile, err := findGoModFile(src)

			if err != nil || !noCacheFile || modfile != filepath.Join(gopRoot, "go.mod") {
				tt.Fatal("should found go.mod in $HOME/goplus, but got:", modfile, noCacheFile, err)
			}
		}

		{
			gopRoot := filepath.Join(home, "gop")
			makeValidGopRoot(gopRoot)

			modfile, noCacheFile, err := findGoModFile(src)

			if err != nil || !noCacheFile || modfile != filepath.Join(gopRoot, "go.mod") {
				tt.Fatal("should found go.mod in $HOME/gop, but got:", modfile, noCacheFile, err)
			}
		}
	})

	t.Run("check if parent dir of the executable is valid gop root", func(tt *testing.T) {
		tt.Cleanup(cleanupAll)
		_, src, gopRoot := makeTestDir(tt)
		bin := filepath.Join(gopRoot, "bin")
		exePath := filepath.Join(bin, "run")
		os.Mkdir(bin, 0755)
		writeDummyFile(exePath)

		// Mock executable location
		executable = func() (string, error) {
			return exePath, nil
		}

		modfile, noCacheFile, err := findGoModFile(src)

		if err != nil || !noCacheFile || modfile != filepath.Join(gopRoot, "go.mod") {
			tt.Fatal("should found go.mod in gopRoot, but got:", modfile, noCacheFile, err)
		}
	})

	t.Run("test gop root priority", func(tt *testing.T) {
		tt.Cleanup(cleanupAll)
		root, src, _ := makeTestDir(tt)

		tt.Run("without gop root", func(tt *testing.T) {
			modfile, noCacheFile, err := findGoModFile(src)

			if err == nil || noCacheFile || modfile != "" {
				tt.Fatal("should not found go.mod without gop root, got:", modfile, noCacheFile, err)
			}
		})

		tt.Run("set HOME but hasn't $HOME/gop/ and $HOME/goplus/", func(tt *testing.T) {
			os.Setenv("HOME", root)

			modfile, noCacheFile, err := findGoModFile(src)

			if err == nil || noCacheFile || modfile != "" {
				tt.Fatal("should not found go.mod without gop root, got:", modfile, noCacheFile, err)
			}
		})

		tt.Run("set HOME, and has valid $HOME/goplus/", func(tt *testing.T) {
			gopRoot := filepath.Join(root, "goplus")
			makeValidGopRoot(gopRoot)

			modfile, noCacheFile, err := findGoModFile(src)

			if err != nil || !noCacheFile || modfile != filepath.Join(gopRoot, "go.mod") {
				tt.Fatal("should found go.mod in $HOME/goplus, but got:", modfile, noCacheFile, err)
			}
		})

		tt.Run("set HOME, and has valid $HOME/gop/", func(tt *testing.T) {
			gopRoot := filepath.Join(root, "gop")
			makeValidGopRoot(gopRoot)

			modfile, noCacheFile, err := findGoModFile(src)

			if err != nil || !noCacheFile || modfile != filepath.Join(gopRoot, "go.mod") {
				tt.Fatal("should found go.mod in $HOME/gop, but got:", modfile, noCacheFile, err)
			}
		})

		tt.Run("set build.GopRoot to an invalid gop root dir", func(tt *testing.T) {
			gopRoot := filepath.Join(root, "gop")

			defaultGopRoot = filepath.Join(root, "invalid_goproot")
			modfile, noCacheFile, err := findGoModFile(src)

			if err != nil || !noCacheFile || modfile != filepath.Join(gopRoot, "go.mod") {
				tt.Fatal("should found go.mod in $HOME/gop, but got:", modfile, noCacheFile, err)
			}
		})

		tt.Run("set build.GopRoot to a valid gop root dir", func(tt *testing.T) {
			newGopRoot := filepath.Join(root, "new_gop_root")
			makeValidGopRoot(newGopRoot)

			defaultGopRoot = newGopRoot
			modfile, noCacheFile, err := findGoModFile(src)

			if err != nil || !noCacheFile || modfile != filepath.Join(newGopRoot, "go.mod") {
				tt.Fatal("should found go.mod in new_gop_root/, but got:", modfile, noCacheFile, err)
			}
		})

		tt.Run("the executable's parent dir is a valid gop root dir", func(tt *testing.T) {
			newGopRoot2 := filepath.Join(root, "new_gop_root2")
			makeValidGopRoot(newGopRoot2)
			bin := filepath.Join(newGopRoot2, "bin")
			exePath := filepath.Join(bin, "run")
			os.Mkdir(bin, 0755)
			writeDummyFile(exePath)
			// Mock executable location
			executable = func() (string, error) {
				return exePath, nil
			}

			modfile, noCacheFile, err := findGoModFile(src)

			if err != nil || !noCacheFile || modfile != filepath.Join(newGopRoot2, "go.mod") {
				tt.Fatal("should found go.mod in new_gop_root2/, but got:", modfile, noCacheFile, err)
			}
		})

		tt.Run("set GOPROOT to an invalid gop root dir", func(tt *testing.T) {
			newGopRoot3 := filepath.Join(root, "new_gop_root3")

			defer func() {
				r := recover()
				if r == nil {
					tt.Fatal("should panic, but not")
				}
			}()

			os.Setenv("GOPROOT", newGopRoot3)
			findGoModFile(src)
		})

		tt.Run("set GOPROOT to a valid gop root dir", func(tt *testing.T) {
			newGopRoot4 := filepath.Join(root, "new_gop_root4")
			makeValidGopRoot(newGopRoot4)

			os.Setenv("GOPROOT", newGopRoot4)
			modfile, noCacheFile, err := findGoModFile(src)

			if err != nil || !noCacheFile || modfile != filepath.Join(newGopRoot4, "go.mod") {
				tt.Fatal("should found go.mod in new_gop_root3/, but got:", modfile, noCacheFile, err)
			}
		})
	})
}
<commit_msg>fix env.TestBasic<commit_after>/*
 * Copyright (c) 2021 The GoPlus Authors (goplus.org). All rights reserved.
 *
 * 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 env

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"github.com/goplus/gop/cl"
)

func findGoModFile(dir string) (modfile string, noCacheFile bool, err error) {
	modfile, err = cl.FindGoModFile(dir)

	if err != nil {
		gopRoot, err := findGopRoot()
		if err == nil {
			modfile = filepath.Join(gopRoot, "go.mod")
			return modfile, true, nil
		}
	}
	return
}

// Common testing directory structure:
// testing_root/
//   src/
//	   subdir/
//   valid_goproot/
//	   go.mod
//	   go.sum
//     builtin/
func makeTestDir(t *testing.T) (root string, src string, gopRoot string) {
	root, _ = filepath.EvalSymlinks(t.TempDir())
	src = filepath.Join(root, "src")
	gopRoot = filepath.Join(root, "valid_goproot")
	makeValidGopRoot(gopRoot)
	os.Mkdir(src, 0755)
	return
}

func makeValidGopRoot(root string) {
	os.Mkdir(root, 0755)
	os.Mkdir(filepath.Join(root, "builtin"), 0755)
	ioutil.WriteFile(filepath.Join(root, "go.mod"), []byte(""), 0644)
	ioutil.WriteFile(filepath.Join(root, "go.sum"), []byte(""), 0644)
}

func writeDummyFile(path string) {
	ioutil.WriteFile(path, []byte(""), 0644)
}

func cleanup() {
	os.Setenv("GOPROOT", "")
	os.Setenv("HOME", "")
	defaultGopRoot = ""
}

func TestBasic(t *testing.T) {
	defaultGopRoot = ".."
	if GOPROOT() == "" {
		t.Fatal("TestBasic failed")
	}
	defaultGopRoot = ""
}

func TestFindGoModFileInGoModDir(t *testing.T) {
	cleanup()

	t.Run("the src/ is a valid mod dir", func(tt *testing.T) {
		tt.Cleanup(cleanup)
		_, src, _ := makeTestDir(tt)
		subdir := filepath.Join(src, "subdir")
		writeDummyFile(filepath.Join(src, "go.mod"))
		os.Mkdir(subdir, 0755)

		{
			modfile, noCacheFile, err := findGoModFile(src)

			if err != nil || modfile != filepath.Join(src, "go.mod") || noCacheFile {
				tt.Fatal("got:", modfile, noCacheFile, err)
			}
		}

		{
			// Should found go.mod in parent dir
			modfile, noCacheFile, err := findGoModFile(subdir)

			if err != nil || modfile != filepath.Join(src, "go.mod") || noCacheFile {
				tt.Fatal("got:", modfile, noCacheFile, err)
			}
		}
	})

	t.Run("the src/ is not a valid mod dir", func(tt *testing.T) {
		tt.Cleanup(cleanup)
		_, src, _ := makeTestDir(tt)

		modfile, noCacheFile, err := findGoModFile(src)

		if err == nil {
			tt.Fatal("should not found the mod file, but got:", modfile, noCacheFile)
		}
	})
}

func TestFindGoModFileInGopRoot(t *testing.T) {
	origiExecutable := executable
	t.Cleanup(func() {
		executable = origiExecutable
	})

	home := t.TempDir()
	bin := filepath.Join(home, "bin")

	// Don't find go.mod in gop source dir when testing
	os.Chdir(home)

	cleanupAll := func() {
		cleanup()
		executable = func() (string, error) {
			return filepath.Join(bin, "run"), nil
		}
	}
	cleanupAll()

	t.Run("without gop root", func(tt *testing.T) {
		tt.Cleanup(cleanupAll)
		root, _, _ := makeTestDir(tt)

		modfile, noCacheFile, err := findGoModFile(root)

		if err == nil || noCacheFile || modfile != "" {
			tt.Fatal("should not found go.mod without gop root, got:", modfile, noCacheFile, err)
		}
	})

	t.Run("set GOPROOT to a valid goproot path", func(tt *testing.T) {
		tt.Cleanup(cleanupAll)
		_, src, gopRoot := makeTestDir(tt)

		os.Setenv("GOPROOT", gopRoot)
		modfile, noCacheFile, err := findGoModFile(src)

		if err != nil || modfile != filepath.Join(gopRoot, "go.mod") || !noCacheFile {
			tt.Fatal("should found mod file in GOPROOT, got:", modfile, noCacheFile, err)
		}
	})

	t.Run("set GOPROOT to an invalid goproot path", func(tt *testing.T) {
		tt.Cleanup(cleanupAll)
		root, src, _ := makeTestDir(tt)
		invalidGopRoot := filepath.Join(root, "invalid_goproot")

		defer func() {
			r := recover()
			if r == nil {
				tt.Fatal("should panic, but not")
			}
		}()

		os.Setenv("GOPROOT", invalidGopRoot)
		findGoModFile(src)
	})

	t.Run("set build.GopRoot to a valid goproot path", func(tt *testing.T) {
		tt.Cleanup(cleanupAll)
		_, src, gopRoot := makeTestDir(tt)

		defaultGopRoot = gopRoot
		modfile, noCacheFile, err := findGoModFile(src)

		if err != nil || modfile != filepath.Join(gopRoot, "go.mod") || !noCacheFile {
			tt.Fatal("should found go.mod in the dir of build.GopRoot, got:", modfile, noCacheFile, err)
		}
	})

	t.Run("set build.GopRoot to an invalid path", func(tt *testing.T) {
		tt.Cleanup(cleanupAll)
		root, src, _ := makeTestDir(tt)
		invalidGopRoot := filepath.Join(root, "invalid_goproot")

		defaultGopRoot = invalidGopRoot
		{
			modfile, noCacheFile, err := findGoModFile(src)

			if err == nil || noCacheFile || modfile != "" {
				tt.Fatal("should not found go.mod when build.GopRoot isn't exists, got:", modfile, noCacheFile, err)
			}
		}

		{
			os.Mkdir(invalidGopRoot, 0755)
			modfile, noCacheFile, err := findGoModFile(src)

			if err == nil || noCacheFile || modfile != "" {
				tt.Fatal("should not found go.mod when build.GopRoot isn't an valid gop root dir, got:", modfile, noCacheFile, err)
			}
		}
	})

	t.Run("use $HOME/gop or $HOME/goplus", func(tt *testing.T) {
		tt.Cleanup(cleanupAll)
		root, src, _ := makeTestDir(tt)
		home := filepath.Join(root, "home")
		os.Mkdir(home, 0755)
		os.Setenv("HOME", home)

		{
			gopRoot := filepath.Join(home, "goplus")
			makeValidGopRoot(gopRoot)

			modfile, noCacheFile, err := findGoModFile(src)

			if err != nil || !noCacheFile || modfile != filepath.Join(gopRoot, "go.mod") {
				tt.Fatal("should found go.mod in $HOME/goplus, but got:", modfile, noCacheFile, err)
			}
		}

		{
			gopRoot := filepath.Join(home, "gop")
			makeValidGopRoot(gopRoot)

			modfile, noCacheFile, err := findGoModFile(src)

			if err != nil || !noCacheFile || modfile != filepath.Join(gopRoot, "go.mod") {
				tt.Fatal("should found go.mod in $HOME/gop, but got:", modfile, noCacheFile, err)
			}
		}
	})

	t.Run("check if parent dir of the executable is valid gop root", func(tt *testing.T) {
		tt.Cleanup(cleanupAll)
		_, src, gopRoot := makeTestDir(tt)
		bin := filepath.Join(gopRoot, "bin")
		exePath := filepath.Join(bin, "run")
		os.Mkdir(bin, 0755)
		writeDummyFile(exePath)

		// Mock executable location
		executable = func() (string, error) {
			return exePath, nil
		}

		modfile, noCacheFile, err := findGoModFile(src)

		if err != nil || !noCacheFile || modfile != filepath.Join(gopRoot, "go.mod") {
			tt.Fatal("should found go.mod in gopRoot, but got:", modfile, noCacheFile, err)
		}
	})

	t.Run("test gop root priority", func(tt *testing.T) {
		tt.Cleanup(cleanupAll)
		root, src, _ := makeTestDir(tt)

		tt.Run("without gop root", func(tt *testing.T) {
			modfile, noCacheFile, err := findGoModFile(src)

			if err == nil || noCacheFile || modfile != "" {
				tt.Fatal("should not found go.mod without gop root, got:", modfile, noCacheFile, err)
			}
		})

		tt.Run("set HOME but hasn't $HOME/gop/ and $HOME/goplus/", func(tt *testing.T) {
			os.Setenv("HOME", root)

			modfile, noCacheFile, err := findGoModFile(src)

			if err == nil || noCacheFile || modfile != "" {
				tt.Fatal("should not found go.mod without gop root, got:", modfile, noCacheFile, err)
			}
		})

		tt.Run("set HOME, and has valid $HOME/goplus/", func(tt *testing.T) {
			gopRoot := filepath.Join(root, "goplus")
			makeValidGopRoot(gopRoot)

			modfile, noCacheFile, err := findGoModFile(src)

			if err != nil || !noCacheFile || modfile != filepath.Join(gopRoot, "go.mod") {
				tt.Fatal("should found go.mod in $HOME/goplus, but got:", modfile, noCacheFile, err)
			}
		})

		tt.Run("set HOME, and has valid $HOME/gop/", func(tt *testing.T) {
			gopRoot := filepath.Join(root, "gop")
			makeValidGopRoot(gopRoot)

			modfile, noCacheFile, err := findGoModFile(src)

			if err != nil || !noCacheFile || modfile != filepath.Join(gopRoot, "go.mod") {
				tt.Fatal("should found go.mod in $HOME/gop, but got:", modfile, noCacheFile, err)
			}
		})

		tt.Run("set build.GopRoot to an invalid gop root dir", func(tt *testing.T) {
			gopRoot := filepath.Join(root, "gop")

			defaultGopRoot = filepath.Join(root, "invalid_goproot")
			modfile, noCacheFile, err := findGoModFile(src)

			if err != nil || !noCacheFile || modfile != filepath.Join(gopRoot, "go.mod") {
				tt.Fatal("should found go.mod in $HOME/gop, but got:", modfile, noCacheFile, err)
			}
		})

		tt.Run("set build.GopRoot to a valid gop root dir", func(tt *testing.T) {
			newGopRoot := filepath.Join(root, "new_gop_root")
			makeValidGopRoot(newGopRoot)

			defaultGopRoot = newGopRoot
			modfile, noCacheFile, err := findGoModFile(src)

			if err != nil || !noCacheFile || modfile != filepath.Join(newGopRoot, "go.mod") {
				tt.Fatal("should found go.mod in new_gop_root/, but got:", modfile, noCacheFile, err)
			}
		})

		tt.Run("the executable's parent dir is a valid gop root dir", func(tt *testing.T) {
			newGopRoot2 := filepath.Join(root, "new_gop_root2")
			makeValidGopRoot(newGopRoot2)
			bin := filepath.Join(newGopRoot2, "bin")
			exePath := filepath.Join(bin, "run")
			os.Mkdir(bin, 0755)
			writeDummyFile(exePath)
			// Mock executable location
			executable = func() (string, error) {
				return exePath, nil
			}

			modfile, noCacheFile, err := findGoModFile(src)

			if err != nil || !noCacheFile || modfile != filepath.Join(newGopRoot2, "go.mod") {
				tt.Fatal("should found go.mod in new_gop_root2/, but got:", modfile, noCacheFile, err)
			}
		})

		tt.Run("set GOPROOT to an invalid gop root dir", func(tt *testing.T) {
			newGopRoot3 := filepath.Join(root, "new_gop_root3")

			defer func() {
				r := recover()
				if r == nil {
					tt.Fatal("should panic, but not")
				}
			}()

			os.Setenv("GOPROOT", newGopRoot3)
			findGoModFile(src)
		})

		tt.Run("set GOPROOT to a valid gop root dir", func(tt *testing.T) {
			newGopRoot4 := filepath.Join(root, "new_gop_root4")
			makeValidGopRoot(newGopRoot4)

			os.Setenv("GOPROOT", newGopRoot4)
			modfile, noCacheFile, err := findGoModFile(src)

			if err != nil || !noCacheFile || modfile != filepath.Join(newGopRoot4, "go.mod") {
				tt.Fatal("should found go.mod in new_gop_root3/, but got:", modfile, noCacheFile, err)
			}
		})
	})
}
<|endoftext|>


<commit_before>package kdtree

import (
	"ellipsoid"
	"errors"
	"fmt"
	"geo"
	"graph"
	"log"
	"math"
	"mm"
	"path"
)

var (
	e             ellipsoid.Ellipsoid
	clusterKdTree ClusterKdTree
)

func init() {
	e = ellipsoid.Init("WGS84", ellipsoid.Degrees, ellipsoid.Meter, ellipsoid.Longitude_is_symmetric, ellipsoid.Bearing_is_symmetric)
}

func LoadKdTree(clusterGraph *graph.ClusterGraph, base string) error {
	// Load the k-d tree of all clusters
	clusterKdTrees := make([]*KdTree, len(clusterGraph.Cluster))
	for i, g := range clusterGraph.Cluster {
		clusterDir := path.Join(base, fmt.Sprintf("/cluster%d", i+1))
		var encodedSteps []uint64
		var encodedCoordinates []int32
		err := mm.Open(path.Join(clusterDir, "kdtree.ftf"), &encodedSteps)
		if err != nil {
			return err
		}
		err = mm.Open(path.Join(clusterDir, "coordinates.ftf"), &encodedCoordinates)
		if err != nil {
			return err
		}
		clusterKdTrees[i] = &KdTree{
			Graph: g,
			EncodedSteps: encodedSteps,
			EncodedCoordinates: encodedCoordinates,
		}
	}

	// Load the k-d tree of the overlay graph
	var encodedSteps []uint64
	var encodedCoordinates []int32
	err := mm.Open(path.Join(base, "/overlay/kdtree.ftf"), &encodedSteps)
	if err != nil {
		return err
	}
	err = mm.Open(path.Join(base, "/overlay/coordinates.ftf"), &encodedCoordinates)
	if err != nil {
		return err
	}
	overlayKdTree := &KdTree{
		Graph: clusterGraph.Overlay.GraphFile,
		EncodedSteps: encodedSteps,
		EncodedCoordinates: encodedCoordinates,
	}

	// Load the bounding boxes of the clusters
	var bboxesFile []int32
	err = mm.Open(path.Join(base, "bboxes.ftf"), &bboxesFile)
	if err != nil {
		return err
	}
	if len(bboxesFile)/4 != clusterGraph.Overlay.ClusterCount() {
		return errors.New("size of bboxes file does not match cluster count")
	}
	bboxes := make([]geo.BBox, len(bboxesFile)/4)
	for i, _ := range bboxes {
		bboxes[i] = geo.DecodeBBox(bboxesFile[4*i : 4*i+4])
	}

	clusterKdTree = ClusterKdTree{
		Overlay: overlayKdTree,
		Cluster: clusterKdTrees,
		BBoxes: bboxes,
	}
	return nil
}

// NearestNeighbor returns -1 if the way is on the overlay graph
// No fail strategy: a nearest point on the overlay graph is always returned if no point
// is found in the clusters.
func NearestNeighbor(x geo.Coordinate, trans graph.Transport) Location {
	// first search on the overlay graph
	overlay := clusterKdTree.Overlay
	//log.Printf("binarySearch in the overlay graph")
	bestStepIndex, coordOverlay, foundPoint := binarySearch(overlay, x, 0, overlay.EncodedStepLen()-1,
		true /* compareLat */, trans)
	minDistance, _ := e.To(x.Lat, x.Lng, coordOverlay.Lat, coordOverlay.Lng)

	// then search on all clusters where the point is inside the bounding box of the cluster
	clusterIndex := -1
	for i, b := range clusterKdTree.BBoxes {
		if b.Contains(x) {
			kdTree := clusterKdTree.Cluster[i]
			//log.Printf("Point is in bbox %v\n", b)
			//log.Printf("binarySearch in cluster %v", i)
			stepIndex, coord, ok := binarySearch(kdTree, x, 0, kdTree.EncodedStepLen()-1, true /* compareLat */, trans)
			dist, _ := e.To(x.Lat, x.Lng, coord.Lat, coord.Lng)

			if ok && (!foundPoint || dist < minDistance) {
				foundPoint = true
				minDistance = dist
				bestStepIndex = stepIndex
				clusterIndex = i
			}
		}
	}

	if clusterIndex >= 0 {
		kdTree := clusterKdTree.Cluster[clusterIndex]
		return Location{
			Graph:   kdTree.Graph,
			EC:      kdTree.EncodedStep(bestStepIndex),
			Cluster: clusterIndex,
		}
	}
	log.Printf("no matching bounding box found for (%v, %v)", x.Lat, x.Lng)
	return Location{
		Graph:   overlay.Graph,
		EC:      overlay.EncodedStep(bestStepIndex),
		Cluster: clusterIndex,
	}
}

// binarySearch in one k-d tree. The index, the coordinate, and if the returned step/vertex
// is accessible are returned.
func binarySearch(kdTree *KdTree, x geo.Coordinate, start, end int, compareLat bool,
	trans graph.Transport) (int, geo.Coordinate, bool) {

	if end <= start {
		// end of the recursion
		startCoord, startAccessible := decodeCoordinate(kdTree, start, trans)
		return start, startCoord, startAccessible
	}

	middle := (end-start)/2 + start
	middleCoord, middleAccessible := decodeCoordinate(kdTree, middle, trans)

	// exact hit
	if middleAccessible && x.Lat == middleCoord.Lat && x.Lng == middleCoord.Lng {
		return middle, middleCoord, middleAccessible
	}

	middleDist := math.Inf(1)
	if middleAccessible {
		middleDist = x.Distance(middleCoord) //e.To(x.Lat, x.Lng, middleCoord.Lat, middleCoord.Lng)
	}

	// recursion one half and if no accessible point is returned also on the other half
	var left bool
	if compareLat {
		left = x.Lat < middleCoord.Lat
	} else {
		left = x.Lng < middleCoord.Lng
	}
	var recIndex int
	var recCoord geo.Coordinate
	var recAccessible bool
	bothHalfs := false
	if left {
		// left
		recIndex, recCoord, recAccessible = binarySearch(kdTree, x, start, middle-1, !compareLat, trans)
		if !recAccessible {
			// other half -> right
			recIndex, recCoord, recAccessible = binarySearch(kdTree, x, middle+1, end, !compareLat, trans)
			bothHalfs = true
		}
	} else {
		// right
		recIndex, recCoord, recAccessible = binarySearch(kdTree, x, middle+1, end, !compareLat, trans)
		if !recAccessible {
			// other half -> left
			recIndex, recCoord, recAccessible = binarySearch(kdTree, x, start, middle-1, !compareLat, trans)
			bothHalfs = true
		}
	}
	bestDistance := x.Distance(recCoord) //e.To(x.Lat, x.Lng, recCoord.Lat, recCoord.Lng)

	// we are finished if both have already been searched
	if bothHalfs {
		if middleDist < bestDistance {
			return middle, middleCoord, middleAccessible
		} else {
			return recIndex, recCoord, recAccessible
		}
	}

	distToPlane := 0.0
	if compareLat {
		distToPlane = x.Distance(geo.Coordinate{middleCoord.Lat, x.Lng})
	} else {
		distToPlane = x.Distance(geo.Coordinate{x.Lat, middleCoord.Lng})
	}

	var recIndex2 int
	var recCoord2 geo.Coordinate
	recAccessible2 := false
	// test whether the current best distance circle crosses the plane
	if 1.5*bestDistance >= distToPlane {
		// search on the other half
		if !left {
			// left
			recIndex2, recCoord2, recAccessible2 = binarySearch(kdTree, x, start, middle-1, !compareLat, trans)
		} else {
			// right
			recIndex2, recCoord2, recAccessible2 = binarySearch(kdTree, x, middle+1, end, !compareLat, trans)
		}
	}

	bestDistance2 := math.Inf(1)
	if recAccessible2 {
		bestDistance2 = x.Distance(recCoord2)
	}

	if bestDistance < bestDistance2 {
		if middleDist < bestDistance {
			return middle, middleCoord, middleAccessible
		} else {
			return recIndex, recCoord, recAccessible
		}
	}
	if middleDist < bestDistance2 {
		return middle, middleCoord, middleAccessible
	}
	return recIndex2, recCoord2, recAccessible2
}

// decodeCoordinate returns the coordinate of the encoded vertex/step and if it is accessible by the
// given transport mode
func decodeCoordinate(t *KdTree, i int, trans graph.Transport) (geo.Coordinate, bool) {
	g     := t.Graph
	ec    := t.EncodedStep(i)
	coord := geo.DecodeCoordinate(t.EncodedCoordinates[2*i], t.EncodedCoordinates[2*i+1])

	vertexIndex := ec >> (EdgeOffsetBits + StepOffsetBits)
	edgeOffset := uint32((ec >> StepOffsetBits) & MaxEdgeOffset)
	stepOffset := ec & MaxStepOffset
	vertex := graph.Vertex(vertexIndex)

	if edgeOffset == MaxEdgeOffset && stepOffset == MaxStepOffset {
		// it is a vertex and not a step
		return coord, g.VertexAccessible(vertex, trans)
	}
	edge := graph.Edge(g.FirstOut[vertex] + edgeOffset)
	edgeAccessible := g.EdgeAccessible(edge, trans)
	return coord, edgeAccessible
}
<commit_msg>fixes in nearest neighbor<commit_after>package kdtree

import (
	"ellipsoid"
	"errors"
	"fmt"
	"geo"
	"graph"
	"log"
	"math"
	"mm"
	"path"
)

var (
	e             ellipsoid.Ellipsoid
	clusterKdTree ClusterKdTree
)

func init() {
	e = ellipsoid.Init("WGS84", ellipsoid.Degrees, ellipsoid.Meter, ellipsoid.Longitude_is_symmetric, ellipsoid.Bearing_is_symmetric)
}

func LoadKdTree(clusterGraph *graph.ClusterGraph, base string) error {
	// Load the k-d tree of all clusters
	clusterKdTrees := make([]*KdTree, len(clusterGraph.Cluster))
	for i, g := range clusterGraph.Cluster {
		clusterDir := path.Join(base, fmt.Sprintf("/cluster%d", i+1))
		var encodedSteps []uint64
		var encodedCoordinates []int32
		err := mm.Open(path.Join(clusterDir, "kdtree.ftf"), &encodedSteps)
		if err != nil {
			return err
		}
		err = mm.Open(path.Join(clusterDir, "coordinates.ftf"), &encodedCoordinates)
		if err != nil {
			return err
		}
		clusterKdTrees[i] = &KdTree{
			Graph:              g,
			EncodedSteps:       encodedSteps,
			EncodedCoordinates: encodedCoordinates,
		}
	}

	// Load the k-d tree of the overlay graph
	var encodedSteps []uint64
	var encodedCoordinates []int32
	err := mm.Open(path.Join(base, "/overlay/kdtree.ftf"), &encodedSteps)
	if err != nil {
		return err
	}
	err = mm.Open(path.Join(base, "/overlay/coordinates.ftf"), &encodedCoordinates)
	if err != nil {
		return err
	}
	overlayKdTree := &KdTree{
		Graph:              clusterGraph.Overlay.GraphFile,
		EncodedSteps:       encodedSteps,
		EncodedCoordinates: encodedCoordinates,
	}

	// Load the bounding boxes of the clusters
	var bboxesFile []int32
	err = mm.Open(path.Join(base, "bboxes.ftf"), &bboxesFile)
	if err != nil {
		return err
	}
	if len(bboxesFile)/4 != clusterGraph.Overlay.ClusterCount() {
		return errors.New("size of bboxes file does not match cluster count")
	}
	bboxes := make([]geo.BBox, len(bboxesFile)/4)
	for i, _ := range bboxes {
		bboxes[i] = geo.DecodeBBox(bboxesFile[4*i : 4*i+4])
	}

	clusterKdTree = ClusterKdTree{
		Overlay: overlayKdTree,
		Cluster: clusterKdTrees,
		BBoxes:  bboxes,
	}
	return nil
}

var linear = 0

// NearestNeighbor returns -1 if the way is on the overlay graph
// No fail strategy: a nearest point on the overlay graph is always returned if no point
// is found in the clusters.
func NearestNeighbor(x geo.Coordinate, trans graph.Transport) Location {
	//log.Printf("nearest neighbor for %v\n", x)
	// first search on the overlay graph
	overlay := clusterKdTree.Overlay
	bestStepIndex, coordOverlay, foundPoint := binarySearch(overlay, x, 0, overlay.EncodedStepLen()-1,
		true /* compareLat */, trans)
	minDistance, _ := e.To(x.Lat, x.Lng, coordOverlay.Lat, coordOverlay.Lng)

	// then search on all clusters where the point is inside the bounding box of the cluster
	clusterIndex := -1
	for i, b := range clusterKdTree.BBoxes {
		if b.Contains(x) {
			kdTree := clusterKdTree.Cluster[i]

			/*minD := math.Inf(1)
			minI := -1
			//var minC geo.Coordinate
			for j := 0; j < kdTree.EncodedStepLen(); j++ {
				c, oki := decodeCoordinate(kdTree, j, trans)
				dd, _ := e.To(x.Lat, x.Lng, c.Lat, c.Lng)
				if oki && dd < minD {
					minD = dd
					minI = j
					//minC = c
				}
			}
			linear = minI*/

			//log.Printf("  point is in bbox %v\n", b)
			stepIndex, coord, ok := binarySearch(kdTree, x, 0, kdTree.EncodedStepLen()-1, true /* compareLat */, trans)
			dist, _ := e.To(x.Lat, x.Lng, coord.Lat, coord.Lng)
			//log.Printf("    dist %v\n", dist)

			if ok && (!foundPoint || dist < minDistance) {
				foundPoint = true
				minDistance = dist
				bestStepIndex = stepIndex
				clusterIndex = i
			}
		}
	}
	log.Printf("nearest neighbor min distance %v\n", minDistance)

	if clusterIndex >= 0 {
		kdTree := clusterKdTree.Cluster[clusterIndex]
		return Location{
			Graph:   kdTree.Graph,
			EC:      kdTree.EncodedStep(bestStepIndex),
			Cluster: clusterIndex,
		}
	}
	log.Printf("no matching bounding box found for (%v, %v)", x.Lat, x.Lng)
	return Location{
		Graph:   overlay.Graph,
		EC:      overlay.EncodedStep(bestStepIndex),
		Cluster: clusterIndex,
	}
}

// binarySearch in one k-d tree. The index, the coordinate, and if the returned step/vertex
// is accessible are returned.
func binarySearch(kdTree *KdTree, x geo.Coordinate, start, end int, compareLat bool,
	trans graph.Transport) (int, geo.Coordinate, bool) {

	if end <= start {
		// end of the recursion
		startCoord, startAccessible := decodeCoordinate(kdTree, start, trans)
		return start, startCoord, startAccessible
	}

	middle := (end-start)/2 + start
	middleCoord, middleAccessible := decodeCoordinate(kdTree, middle, trans)

	// exact hit
	if middleAccessible && x.Lat == middleCoord.Lat && x.Lng == middleCoord.Lng {
		return middle, middleCoord, middleAccessible
	}

	middleDist := math.Inf(1)
	if middleAccessible {
		middleDist = distance(x, middleCoord) //x.Distance(middleCoord)
	}

	// recursion one half and if no accessible point is returned also on the other half
	var left bool
	if compareLat {
		left = x.Lat < middleCoord.Lat
	} else {
		left = x.Lng < middleCoord.Lng
	}
	var recIndex int
	var recCoord geo.Coordinate
	var recAccessible bool
	bothHalfs := false
	if left {
		// left
		recIndex, recCoord, recAccessible = binarySearch(kdTree, x, start, middle-1, !compareLat, trans)
		if !recAccessible {
			// other half -> right
			recIndex, recCoord, recAccessible = binarySearch(kdTree, x, middle+1, end, !compareLat, trans)
			bothHalfs = true
		}
	} else {
		// right
		recIndex, recCoord, recAccessible = binarySearch(kdTree, x, middle+1, end, !compareLat, trans)
		if !recAccessible {
			// other half -> left
			recIndex, recCoord, recAccessible = binarySearch(kdTree, x, start, middle-1, !compareLat, trans)
			bothHalfs = true
		}
	}
	bestDistance := distance(x, recCoord) //x.Distance(recCoord)

	// we are finished if both have already been searched
	if bothHalfs {
		if middleDist < bestDistance {
			return middle, middleCoord, middleAccessible
		} else {
			return recIndex, recCoord, recAccessible
		}
	}

	distToPlane := 0.0
	if compareLat {
		// distance(x, geo.Coordinate{middleCoord.Lat, x.Lng})
		distToPlane = (x.Lat - middleCoord.Lat) * (x.Lat - middleCoord.Lat)
	} else {
		//distance(x, geo.Coordinate{x.Lat, middleCoord.Lng})
		distToPlane = (x.Lng - middleCoord.Lng) * (x.Lng - middleCoord.Lng)
	}

	var recIndex2 int
	var recCoord2 geo.Coordinate
	recAccessible2 := false

	/*if bestDistance < distToPlane && distToPlane >= 1e6 {
		isIn := false
		if !left {
			isIn = start <= linear && linear <= middle-1
		} else {
			isIn = middle+1 <= linear && linear <= end
		}
		if isIn {
			linearCoord, _ := decodeCoordinate(kdTree, linear, trans)
			linearToPlane := distance(x, linearCoord)
			fmt.Printf("problem: [%d, %d] -> %d  x: %v, mid: %v, bestDist: %v, distToPlane: %v, linearToPlane: %v\n", start, end, linear, x, middleCoord, bestDistance, distToPlane, linearToPlane)
		}
	}*/

	// test whether the current best distance circle crosses the plane
	if bestDistance >= distToPlane || distToPlane < 1e6 {
		// search on the other half
		if !left {
			// left
			recIndex2, recCoord2, recAccessible2 = binarySearch(kdTree, x, start, middle-1, !compareLat, trans)
		} else {
			// right
			recIndex2, recCoord2, recAccessible2 = binarySearch(kdTree, x, middle+1, end, !compareLat, trans)
		}
	}

	bestDistance2 := math.Inf(1)
	if recAccessible2 {
		bestDistance2 = distance(x, recCoord2) //x.Distance(recCoord2)
	}

	if bestDistance < bestDistance2 {
		if middleDist < bestDistance {
			return middle, middleCoord, middleAccessible
		} else {
			return recIndex, recCoord, recAccessible
		}
	}
	if middleDist < bestDistance2 {
		return middle, middleCoord, middleAccessible
	}
	return recIndex2, recCoord2, recAccessible2
}

// decodeCoordinate returns the coordinate of the encoded vertex/step and if it is accessible by the
// given transport mode
func decodeCoordinate(t *KdTree, i int, trans graph.Transport) (geo.Coordinate, bool) {
	g := t.Graph
	ec := t.EncodedStep(i)
	coord := geo.DecodeCoordinate(t.EncodedCoordinates[2*i], t.EncodedCoordinates[2*i+1])

	vertexIndex := ec >> (EdgeOffsetBits + StepOffsetBits)
	edgeOffset := uint32((ec >> StepOffsetBits) & MaxEdgeOffset)
	stepOffset := ec & MaxStepOffset
	vertex := graph.Vertex(vertexIndex)

	if edgeOffset == MaxEdgeOffset && stepOffset == MaxStepOffset {
		// it is a vertex and not a step
		return coord, g.VertexAccessible(vertex, trans)
	}
	edge := graph.Edge(g.FirstOut[vertex] + edgeOffset)
	edgeAccessible := g.EdgeAccessible(edge, trans)
	return coord, edgeAccessible
}

func distance(x, y geo.Coordinate) float64 {
	return (x.Lat-y.Lat)*(x.Lat-y.Lat) + (x.Lng-y.Lng)*(x.Lng-y.Lng)
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// 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
//
// https://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 allowlisting

func Foo() {} // want "excluded"

func Bar() {} // want "excluded"
<commit_msg>rename package to correct name<commit_after>// Copyright 2020 Google LLC
//
// 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
//
// https://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 exclusion

func Foo() {} // want "excluded"

func Bar() {} // want "excluded"
<|endoftext|>


<commit_before>package git

import "path/filepath"
import "strings"
import "github.com/codeskyblue/go-sh"

// Repo allows you to exectue actions on a specific git project
type Repo struct {
	path string
}

// NewGitRepo constructs a git repo for you
func NewGitRepo(path string) *Repo {
	repo := new(Repo)
	repo.path = path
	return repo
}

// Path returns the repos full path
func (r Repo) Path() string {
	return r.path
}

// Name returns the name of the repo, this is just the base folder name
func (r Repo) Name() string {
	return filepath.Base(r.path)
}

// HasUserSet returns if repo has a user set
func (r Repo) HasUserSet() bool {
	value, err := sh.Command(git, "-C", r.path, "config",
		"--local", "user.name").Output()
	if err == nil && len(value) > 0 {
		return true
	}
	return false
}

// GetUser returns the repo user
func (r Repo) GetUser() (user string, email string) {
	out, err := sh.Command(git, "config", "user.name").Output()
	if err == nil {
		user = strings.TrimSpace(string(out))
	}
	out, err = sh.Command(git, "config", "user.email").Output()
	if err == nil {
		email = strings.TrimSpace(string(out))
	}
	return
}

// SetUser sets the user for a git repo
func (r Repo) SetUser(user string, email string) {
	sh.Command(git, "-C", r.path, "config", "user.name", user).Run()
	sh.Command(git, "-C", r.path, "config", "user.email", email).Run()
}

// RemoveUser removes the set user from the git repo
func (r Repo) RemoveUser() {
	sh.Command(git, "-C", r.path, "config", "--remove-section", "user").Run()
}
<commit_msg>change path name to be absolute<commit_after>package git

import "path/filepath"
import "strings"
import "github.com/codeskyblue/go-sh"

// Repo allows you to exectue actions on a specific git project
type Repo struct {
	path string
}

// NewGitRepo constructs a git repo for you
func NewGitRepo(path string) *Repo {
	repo := new(Repo)
	repo.path = path
	return repo
}

// Path returns the repos full path
func (r Repo) Path() string {
	return r.path
}

// Name returns the name of the repo, this is just the base folder name
func (r Repo) Name() string {
	absPath := r.path
	absPath, err := filepath.Abs(r.path)
	if err != nil {
		absPath = r.path
	}
	return filepath.Base(absPath)
}

// HasUserSet returns if repo has a user set
func (r Repo) HasUserSet() bool {
	value, err := sh.Command(git, "-C", r.path, "config",
		"--local", "user.name").Output()
	if err == nil && len(value) > 0 {
		return true
	}
	return false
}

// GetUser returns the repo user
func (r Repo) GetUser() (user string, email string) {
	out, err := sh.Command(git, "config", "user.name").Output()
	if err == nil {
		user = strings.TrimSpace(string(out))
	}
	out, err = sh.Command(git, "config", "user.email").Output()
	if err == nil {
		email = strings.TrimSpace(string(out))
	}
	return
}

// SetUser sets the user for a git repo
func (r Repo) SetUser(user string, email string) {
	sh.Command(git, "-C", r.path, "config", "user.name", user).Run()
	sh.Command(git, "-C", r.path, "config", "user.email", email).Run()
}

// RemoveUser removes the set user from the git repo
func (r Repo) RemoveUser() {
	sh.Command(git, "-C", r.path, "config", "--remove-section", "user").Run()
}
<|endoftext|>


<commit_before>package edit

import "github.com/elves/elvish/parse"

func formHead(n parse.Node) (parse.Node, string) {
	if _, ok := n.(*parse.Chunk); ok {
		return n, ""
	}

	if primary, ok := n.(*parse.Primary); ok {
		compound, head := simpleCompound(primary)
		if form, ok := compound.Parent().(*parse.Form); ok {
			if form.Head == compound {
				return compound, head
			}
		}
	}

	return nil, ""
}

func simpleCompound(pn *parse.Primary) (*parse.Compound, string) {
	thisIndexed, ok := pn.Parent().(*parse.Indexed)
	if !ok {
		return nil, ""
	}

	thisCompound, ok := thisIndexed.Parent().(*parse.Compound)
	if !ok {
		return nil, ""
	}

	head := ""
	for _, in := range thisCompound.Indexeds {
		if len(in.Indicies) > 0 {
			return nil, ""
		}
		typ := in.Head.Type
		if typ != parse.Bareword &&
			typ != parse.SingleQuoted &&
			typ != parse.DoubleQuoted {
			return nil, ""
		}
		head += in.Head.Value
		if in == thisIndexed {
			break
		}
	}
	return thisCompound, head
}
<commit_msg>edit: fix a panic in nodeutil.go<commit_after>package edit

import "github.com/elves/elvish/parse"

func formHead(n parse.Node) (parse.Node, string) {
	if _, ok := n.(*parse.Chunk); ok {
		return n, ""
	}

	if primary, ok := n.(*parse.Primary); ok {
		if compound, head := simpleCompound(primary); compound != nil {
			if form, ok := compound.Parent().(*parse.Form); ok {
				if form.Head == compound {
					return compound, head
				}
			}
		}
	}

	return nil, ""
}

func simpleCompound(pn *parse.Primary) (*parse.Compound, string) {
	thisIndexed, ok := pn.Parent().(*parse.Indexed)
	if !ok {
		return nil, ""
	}

	thisCompound, ok := thisIndexed.Parent().(*parse.Compound)
	if !ok {
		return nil, ""
	}

	head := ""
	for _, in := range thisCompound.Indexeds {
		if len(in.Indicies) > 0 {
			return nil, ""
		}
		typ := in.Head.Type
		if typ != parse.Bareword &&
			typ != parse.SingleQuoted &&
			typ != parse.DoubleQuoted {
			return nil, ""
		}
		head += in.Head.Value
		if in == thisIndexed {
			break
		}
	}
	return thisCompound, head
}
<|endoftext|>


<commit_before>// All names loose the CK_ prefix
// All names loose the hungarian notation
// All the defines are kept from the C package so: C.CKM_RSA_X_509
// All struct's get a Go variant
//
package pkcs11

/*
#define CK_PTR *
#ifndef NULL_PTR
#define NULL_PTR 0
#endif
#define CK_DEFINE_FUNCTION(returnType, name) returnType name
#define CK_DECLARE_FUNCTION(returnType, name) returnType name
#define CK_DECLARE_FUNCTION_POINTER(returnType, name) returnType (* name)
#define CK_CALLBACK_FUNCTION(returnType, name) returnType (* name)

#include <stdlib.h>
#include "pkcs11.h"
*/
import "C"

// A Void is used a lot in the PKCS#11 library, it always consists of a *void and a length.
// We use []byte as a subsitite in Go.
type Void []byte

type SlotID C.CK_ULONG

type Error C.CK_RV

type Version struct {
	Major byte
	Minor byte
}

type Info struct {
	// TODO
}

type SlotInfo struct {
	SlotDescription [64]byte
	ManufacturerID  [32]byte
	Flags           C.CK_FLAGS
	HardwareVersion Version
	FirmwareVersion Version
}

type TokenInfo struct {
	Label              [32]byte
	ManufacturerID     [32]byte
	Model              [16]byte
	SerialNumber       [16]byte
	Flags              C.CK_FLAGS
	MaxSessionCount    C.CK_ULONG
	SessionCount       C.CK_ULONG
	MaxRwSessionCount  C.CK_ULONG
	RwSessionCount     C.CK_ULONG
	MaxPinLen          C.CK_ULONG
	MinPinLen          C.CK_ULONG
	TotalPublicMemory  C.CK_ULONG
	FreePublicMemory   C.CK_ULONG
	TotalPrivateMemory C.CK_ULONG
	FreePrivateMemory  C.CK_ULONG
	hardwareVersion    Version
	firmwareVersion    Version
	UTCTime            [16]byte
}

type SessionInfo struct {
	SlotID      C.CK_SLOT_ID
	Sate        C.CK_STATE
	Flags       C.CK_FLAGS
	DeviceError C.CK_ULONG
}

type Attribute struct {
	Type  C.CK_ATTRIBUTE_TYPE
	Value Void
}

type Date struct {
	// TODO
}

type Mechanism struct {
	Type      C.CK_MECHANISM_TYPE
	Parameter Void
}

type MechanismInfo struct {
	MinKeySize C.CK_ULONG
	MaxKeySize C.CK_ULONG
	Flags      C.CK_FLAGS
}

// stopped after this one
<commit_msg>more stuff<commit_after>// All names loose the CK_ prefix
// All names loose the hungarian notation
// All the defines are kept from the C package so: C.CKM_RSA_X_509
// All struct's get a Go variant
//
package pkcs11

/*
#define CK_PTR *
#ifndef NULL_PTR
#define NULL_PTR 0
#endif
#define CK_DEFINE_FUNCTION(returnType, name) returnType name
#define CK_DECLARE_FUNCTION(returnType, name) returnType name
#define CK_DECLARE_FUNCTION_POINTER(returnType, name) returnType (* name)
#define CK_CALLBACK_FUNCTION(returnType, name) returnType (* name)

#include <stdlib.h>
#include "pkcs11.h"
*/
import "C"

// A Void is used a lot in the PKCS#11 library, it always consists of a *void and a length.
// We use []byte as a subsitite in Go.
type Void []byte

type SlotID C.CK_ULONG

type Error C.CK_RV

type SessionHandle uint

type ObjectHandle uint

type Version struct {
	Major byte
	Minor byte
}

type Info struct {
	// TODO
}

type SlotInfo struct {
	SlotDescription [64]byte
	ManufacturerID  [32]byte
	Flags           C.CK_FLAGS
	HardwareVersion Version
	FirmwareVersion Version
}

type TokenInfo struct {
	Label              [32]byte
	ManufacturerID     [32]byte
	Model              [16]byte
	SerialNumber       [16]byte
	Flags              C.CK_FLAGS
	MaxSessionCount    C.CK_ULONG
	SessionCount       C.CK_ULONG
	MaxRwSessionCount  C.CK_ULONG
	RwSessionCount     C.CK_ULONG
	MaxPinLen          C.CK_ULONG
	MinPinLen          C.CK_ULONG
	TotalPublicMemory  C.CK_ULONG
	FreePublicMemory   C.CK_ULONG
	TotalPrivateMemory C.CK_ULONG
	FreePrivateMemory  C.CK_ULONG
	hardwareVersion    Version
	firmwareVersion    Version
	UTCTime            [16]byte
}

type SessionInfo struct {
	SlotID      C.CK_SLOT_ID
	Sate        C.CK_STATE
	Flags       C.CK_FLAGS
	DeviceError C.CK_ULONG
}

type Attribute struct {
	Type  C.CK_ATTRIBUTE_TYPE
	Value Void
}

type Date struct {
	// TODO
}

type Mechanism struct {
	Type      C.CK_MECHANISM_TYPE
	Parameter Void
}

type MechanismInfo struct {
	MinKeySize C.CK_ULONG
	MaxKeySize C.CK_ULONG
	Flags      C.CK_FLAGS
}

// stopped after this one
<|endoftext|>


<commit_before>package itchio

import "time"

// User represents an itch.io account, with basic profile info
type User struct {
	// Site-wide unique identifier generated by itch.io
	ID int64 `json:"id"`

	// The user's username (used for login)
	Username string `json:"username"`
	// The user's display name: human-friendly, may contain spaces, unicode etc.
	DisplayName string `json:"displayName"`

	// Has the user opted into creating games?
	Developer bool `json:"developer" gorm:"-"`
	// Is the user part of itch.io's press program?
	PressUser bool `json:"pressUser" gorm:"-"`

	// The address of the user's page on itch.io
	URL string `json:"url"`
	// User's avatar, may be a GIF
	CoverURL string `json:"coverUrl"`
	// Static version of user's avatar, only set if the main cover URL is a GIF
	StillCoverURL string `json:"stillCoverUrl"`
}

// Game represents a page on itch.io, it could be a game,
// a tool, a comic, etc.
type Game struct {
	// Site-wide unique identifier generated by itch.io
	ID int64 `json:"id"`
	// Canonical address of the game's page on itch.io
	URL string `json:"url"`

	// Human-friendly title (may contain any character)
	Title string `json:"title"`
	// Human-friendly short description
	ShortText string `json:"shortText"`
	// Downloadable game, html game, etc.
	Type GameType `json:"type"`
	// Classification: game, tool, comic, etc.
	Classification GameClassification `json:"classification"`

	// Configuration for embedded (HTML5) games
	// @optional
	Embed *GameEmbedData `json:"embed,omitempty"`

	// Cover url (might be a GIF)
	CoverURL string `json:"coverUrl"`
	// Non-gif cover url, only set if main cover url is a GIF
	StillCoverURL string `json:"stillCoverUrl"`

	// Date the game was created
	CreatedAt time.Time `json:"createdAt"`
	// Date the game was published, empty if not currently published
	PublishedAt time.Time `json:"publishedAt"`

	// Price in cents of a dollar
	MinPrice int64 `json:"minPrice"`
	// Can this game be bought?
	CanBeBought bool `json:"canBeBought"`
	// Is this game downloadable by press users for free?
	InPressSystem bool `json:"inPressSystem"`
	// Does this game have a demo that can be downloaded for free?
	HasDemo bool `json:"hasDemo"`

	// Does this game have an upload tagged with 'macOS compatible'? (creator-controlled)
	OSX bool `json:"pOsx"`
	// Does this game have an upload tagged with 'Linux compatible'? (creator-controlled)
	Linux bool `json:"pLinux"`
	// Does this game have an upload tagged with 'Windows compatible'? (creator-controlled)
	Windows bool `json:"pWindows"`
	// Does this game have an upload tagged with 'Android compatible'? (creator-controlled)
	Android bool `json:"pAndroid"`

	// The user account this game is associated to
	// @optional
	User *User `json:"user,omitempty"`

	// ID of the user account this game is associated to
	UserID int64 `json:"userId"`

	// The best current sale for this game
	// @optional
	Sale *Sale `json:"sale,omitempty"`

	// Owner-only fields

	ViewsCount     int64 `json:"viewsCount,omitempty" gorm:"-"`
	DownloadsCount int64 `json:"downloadsCount,omitempt" gorm:"-"`
	PurchasesCount int64 `json:"purchasesCount,omitempt" gorm:"-"`

	Published bool `json:"published,omitempty" gorm:"-"`
}

// Type of an itch.io game page, mostly related to
// how it should be presented on web (downloadable or embed)
type GameType string

const (
	// downloadable
	GameTypeDefault GameType = "default"
	// .swf (legacy)
	GameTypeFlash GameType = "flash"
	// .unity3d (legacy)
	GameTypeUnity GameType = "unity"
	// .jar (legacy)
	GameTypeJava GameType = "java"
	// .html (thriving)
	GameTypeHTML GameType = "html"
)

// Creator-picked classification for a page
type GameClassification string

const (
	// something you can play
	GameClassificationGame GameClassification = "game"
	// all software pretty much
	GameClassificationTool GameClassification = "tool"
	// assets: graphics, sounds, etc.
	GameClassificationAssets GameClassification = "assets"
	// game mod (no link to game, purely creator tagging)
	GameClassificationGameMod GameClassification = "game_mod"
	// printable / board / card game
	GameClassificationPhysicalGame GameClassification = "physical_game"
	// bunch of music files
	GameClassificationSoundtrack GameClassification = "soundtrack"
	// anything that creators think don't fit in any other category
	GameClassificationOther GameClassification = "other"
	// comic book (pdf, jpg, specific comic formats, etc.)
	GameClassificationComic GameClassification = "comic"
	// book (pdf, jpg, specific e-book formats, etc.)
	GameClassificationBook GameClassification = "book"
)

// Presentation information for embed games
type GameEmbedData struct {
	// Game this embed info is for
	GameID int64 `json:"gameId" gorm:"primary_key;auto_increment:false"`

	// width of the initial viewport, in pixels
	Width int64 `json:"width"`

	// height of the initial viewport, in pixels
	Height int64 `json:"height"`

	// for itch.io website, whether or not a fullscreen button should be shown
	Fullscreen bool `json:"fullscreen"`
}

// Describes a discount for a game.
type Sale struct {
	// Site-wide unique identifier generated by itch.io
	ID int64 `json:"id"`

	// Game this sale is for
	GameID int64 `json:"gameId"`

	// Discount rate in percent.
	// Can be negative, see https://itch.io/updates/introducing-reverse-sales
	Rate float64 `json:"rate"`
	// Timestamp the sale started at
	StartDate string `json:"startDate"`
	// Timestamp the sale ends at
	EndDate string `json:"endDate"`
}

// An Upload is a downloadable file. Some are wharf-enabled, which means
// they're actually a "channel" that may contain multiple builds, pushed
// with <https://github.com/itchio/butler>
type Upload struct {
	// Site-wide unique identifier generated by itch.io
	ID int64 `json:"id"`
	// Original file name (example: `Overland_x64.zip`)
	Filename string `json:"filename"`
	// Human-friendly name set by developer (example: `Overland for Windows 64-bit`)
	DisplayName string `json:"displayName"`
	// Size of upload in bytes. For wharf-enabled uploads, it's the archive size.
	Size int64 `json:"size"`
	// Name of the wharf channel for this upload, if it's a wharf-enabled upload
	ChannelName string `json:"channelName"`
	// Latest build for this upload, if it's a wharf-enabled upload
	Build *Build `json:"build"`
	// Is this upload a demo that can be downloaded for free?
	Demo bool `json:"demo"`
	// Is this upload a pre-order placeholder?
	Preorder bool `json:"preorder"`

	// Upload type: default, soundtrack, etc.
	Type string `json:"type"`

	// Is this upload tagged with 'macOS compatible'? (creator-controlled)
	OSX bool `json:"pOsx"`
	// Is this upload tagged with 'Linux compatible'? (creator-controlled)
	Linux bool `json:"pLinux"`
	// Is this upload tagged with 'Windows compatible'? (creator-controlled)
	Windows bool `json:"pWindows"`
	// Is this upload tagged with 'Android compatible'? (creator-controlled)
	Android bool `json:"pAndroid"`

	// Date this upload was created at
	CreatedAt time.Time `json:"createdAt"`
	// Date this upload was last updated at (order changed, display name set, etc.)
	UpdatedAt time.Time `json:"updatedAt"`
}

// A Collection is a set of games, curated by humans.
type Collection struct {
	// Site-wide unique identifier generated by itch.io
	ID int64 `json:"id"`

	// Human-friendly title for collection, for example `Couch coop games`
	Title string `json:"title"`

	// Date this collection was created at
	CreatedAt time.Time `json:"createdAt"`
	// Date this collection was last updated at (item added, title set, etc.)
	UpdatedAt time.Time `json:"updatedAt"`

	// Number of games in the collection. This might not be accurate
	// as some games might not be accessible to whoever is asking (project
	// page deleted, visibility level changed, etc.)
	GamesCount int64 `json:"gamesCount"`

	// Games in this collection: filled in API response
	Games []*Game `json:"games,omitempty" gorm:"many2many:collection_games"`

	// Games in this collection, with additional info
	CollectionGames []*CollectionGame `json:"collectionGames,omitempty"`

	UserID int64 `json:"userId"`
	User   *User `json:"user,omitempty"`
}

type CollectionGame struct {
	CollectionID int64       `json:"collectionId" gorm:"primary_key;auto_increment:false"`
	Collection   *Collection `json:"collection,omitempty"`

	GameID int64 `json:"gameId" gorm:"primary_key;auto_increment:false"`
	Game   *Game `json:"game,omitempty"`

	Position int64 `json:"position"`

	CreatedAt time.Time `json:"createdAt"`
	UpdatedAt time.Time `json:"updatedAt"`

	Blurb  string `json:"blurb"`
	UserID int64  `json:"userId"`
}

// A download key is often generated when a purchase is made, it
// allows downloading uploads for a game that are not available
// for free.
type DownloadKey struct {
	// Site-wide unique identifier generated by itch.io
	ID int64 `json:"id"`

	// Identifier of the game to which this download key grants access
	GameID int64 `json:"gameId"`

	// Game to which this download key grants access
	Game *Game `json:"game,omitempty"`

	// Date this key was created at (often coincides with purchase time)
	CreatedAt time.Time `json:"createdAt"`
	// Date this key was last updated at
	UpdatedAt time.Time `json:"updatedAt"`

	// Identifier of the itch.io user to which this key belongs
	OwnerID int64 `json:"ownerId"`
}

// Build contains information about a specific build
type Build struct {
	// Site-wide unique identifier generated by itch.io
	ID int64 `json:"id"`
	// Identifier of the build before this one on the same channel,
	// or 0 if this is the initial build.
	ParentBuildID int64 `json:"parentBuildId"`
	// State of the build: started, processing, etc.
	State BuildState `json:"state"`

	// Automatically-incremented version number, starting with 1
	Version int64 `json:"version"`
	// Value specified by developer with `--userversion` when pushing a build
	// Might not be unique across builds of a given channel.
	UserVersion string `json:"userVersion"`

	// Files associated with this build - often at least an archive,
	// a signature, and a patch. Some might be missing while the build
	// is still processing or if processing has failed.
	Files []*BuildFile `json:"files"`

	// User who pushed the build
	User User `json:"user"`
	// Timestamp the build was created at
	CreatedAt time.Time `json:"createdAt"`
	// Timestamp the build was last updated at
	UpdatedAt time.Time `json:"updatedAt"`
}

// BuildState describes the state of a build, relative to its initial upload, and
// its processing.
type BuildState string

const (
	// BuildStateStarted is the state of a build from its creation until the initial upload is complete
	BuildStateStarted BuildState = "started"
	// BuildStateProcessing is the state of a build from the initial upload's completion to its fully-processed state.
	// This state does not mean the build is actually being processed right now, it's just queued for processing.
	BuildStateProcessing BuildState = "processing"
	// BuildStateCompleted means the build was successfully processed. Its patch hasn't necessarily been
	// rediff'd yet, but we have the holy (patch,signature,archive) trinity.
	BuildStateCompleted BuildState = "completed"
	// BuildStateFailed means something went wrong with the build. A failing build will not update the channel
	// head and can be requeued by the itch.io team, although if a new build is pushed before they do,
	// that new build will "win".
	BuildStateFailed BuildState = "failed"
)

// BuildFile contains information about a build's "file", which could be its
// archive, its signature, its patch, etc.
type BuildFile struct {
	// Site-wide unique identifier generated by itch.io
	ID int64 `json:"id"`
	// Size of this build file
	Size int64 `json:"size"`
	// State of this file: created, uploading, uploaded, etc.
	State BuildFileState `json:"state"`
	// Type of this build file: archive, signature, patch, etc.
	Type BuildFileType `json:"type"`
	// Subtype of this build file, usually indicates compression
	SubType BuildFileSubType `json:"subType"`

	// Date this build file was created at
	CreatedAt time.Time `json:"createdAt"`
	// Date this build file was last updated at
	UpdatedAt time.Time `json:"updatedAt"`
}

// BuildFileState describes the state of a specific file for a build
type BuildFileState string

const (
	// BuildFileStateCreated means the file entry exists on itch.io
	BuildFileStateCreated BuildFileState = "created"
	// BuildFileStateUploading means the file is currently being uploaded to storage
	BuildFileStateUploading BuildFileState = "uploading"
	// BuildFileStateUploaded means the file is ready
	BuildFileStateUploaded BuildFileState = "uploaded"
	// BuildFileStateFailed means the file failed uploading
	BuildFileStateFailed BuildFileState = "failed"
)

// BuildFileType describes the type of a build file: patch, archive, signature, etc.
type BuildFileType string

const (
	// BuildFileTypePatch describes wharf patch files (.pwr)
	BuildFileTypePatch BuildFileType = "patch"
	// BuildFileTypeArchive describes canonical archive form (.zip)
	BuildFileTypeArchive BuildFileType = "archive"
	// BuildFileTypeSignature describes wharf signature files (.pws)
	BuildFileTypeSignature BuildFileType = "signature"
	// BuildFileTypeManifest is reserved
	BuildFileTypeManifest BuildFileType = "manifest"
	// BuildFileTypeUnpacked describes the single file that is in the build (if it was just a single file)
	BuildFileTypeUnpacked BuildFileType = "unpacked"
)

// BuildFileSubType describes the subtype of a build file: mostly its compression
// level. For example, rediff'd patches are "optimized", whereas initial patches are "default"
type BuildFileSubType string

const (
	// BuildFileSubTypeDefault describes default compression (rsync patches)
	BuildFileSubTypeDefault BuildFileSubType = "default"
	// BuildFileSubTypeGzip is reserved
	BuildFileSubTypeGzip BuildFileSubType = "gzip"
	// BuildFileSubTypeOptimized describes optimized compression (rediff'd / bsdiff patches)
	BuildFileSubTypeOptimized BuildFileSubType = "optimized"
)
<commit_msg>Build: make User field a *User (pointer type)<commit_after>package itchio

import "time"

// User represents an itch.io account, with basic profile info
type User struct {
	// Site-wide unique identifier generated by itch.io
	ID int64 `json:"id"`

	// The user's username (used for login)
	Username string `json:"username"`
	// The user's display name: human-friendly, may contain spaces, unicode etc.
	DisplayName string `json:"displayName"`

	// Has the user opted into creating games?
	Developer bool `json:"developer" gorm:"-"`
	// Is the user part of itch.io's press program?
	PressUser bool `json:"pressUser" gorm:"-"`

	// The address of the user's page on itch.io
	URL string `json:"url"`
	// User's avatar, may be a GIF
	CoverURL string `json:"coverUrl"`
	// Static version of user's avatar, only set if the main cover URL is a GIF
	StillCoverURL string `json:"stillCoverUrl"`
}

// Game represents a page on itch.io, it could be a game,
// a tool, a comic, etc.
type Game struct {
	// Site-wide unique identifier generated by itch.io
	ID int64 `json:"id"`
	// Canonical address of the game's page on itch.io
	URL string `json:"url"`

	// Human-friendly title (may contain any character)
	Title string `json:"title"`
	// Human-friendly short description
	ShortText string `json:"shortText"`
	// Downloadable game, html game, etc.
	Type GameType `json:"type"`
	// Classification: game, tool, comic, etc.
	Classification GameClassification `json:"classification"`

	// Configuration for embedded (HTML5) games
	// @optional
	Embed *GameEmbedData `json:"embed,omitempty"`

	// Cover url (might be a GIF)
	CoverURL string `json:"coverUrl"`
	// Non-gif cover url, only set if main cover url is a GIF
	StillCoverURL string `json:"stillCoverUrl"`

	// Date the game was created
	CreatedAt time.Time `json:"createdAt"`
	// Date the game was published, empty if not currently published
	PublishedAt time.Time `json:"publishedAt"`

	// Price in cents of a dollar
	MinPrice int64 `json:"minPrice"`
	// Can this game be bought?
	CanBeBought bool `json:"canBeBought"`
	// Is this game downloadable by press users for free?
	InPressSystem bool `json:"inPressSystem"`
	// Does this game have a demo that can be downloaded for free?
	HasDemo bool `json:"hasDemo"`

	// Does this game have an upload tagged with 'macOS compatible'? (creator-controlled)
	OSX bool `json:"pOsx"`
	// Does this game have an upload tagged with 'Linux compatible'? (creator-controlled)
	Linux bool `json:"pLinux"`
	// Does this game have an upload tagged with 'Windows compatible'? (creator-controlled)
	Windows bool `json:"pWindows"`
	// Does this game have an upload tagged with 'Android compatible'? (creator-controlled)
	Android bool `json:"pAndroid"`

	// The user account this game is associated to
	// @optional
	User *User `json:"user,omitempty"`

	// ID of the user account this game is associated to
	UserID int64 `json:"userId"`

	// The best current sale for this game
	// @optional
	Sale *Sale `json:"sale,omitempty"`

	// Owner-only fields

	ViewsCount     int64 `json:"viewsCount,omitempty" gorm:"-"`
	DownloadsCount int64 `json:"downloadsCount,omitempt" gorm:"-"`
	PurchasesCount int64 `json:"purchasesCount,omitempt" gorm:"-"`

	Published bool `json:"published,omitempty" gorm:"-"`
}

// Type of an itch.io game page, mostly related to
// how it should be presented on web (downloadable or embed)
type GameType string

const (
	// downloadable
	GameTypeDefault GameType = "default"
	// .swf (legacy)
	GameTypeFlash GameType = "flash"
	// .unity3d (legacy)
	GameTypeUnity GameType = "unity"
	// .jar (legacy)
	GameTypeJava GameType = "java"
	// .html (thriving)
	GameTypeHTML GameType = "html"
)

// Creator-picked classification for a page
type GameClassification string

const (
	// something you can play
	GameClassificationGame GameClassification = "game"
	// all software pretty much
	GameClassificationTool GameClassification = "tool"
	// assets: graphics, sounds, etc.
	GameClassificationAssets GameClassification = "assets"
	// game mod (no link to game, purely creator tagging)
	GameClassificationGameMod GameClassification = "game_mod"
	// printable / board / card game
	GameClassificationPhysicalGame GameClassification = "physical_game"
	// bunch of music files
	GameClassificationSoundtrack GameClassification = "soundtrack"
	// anything that creators think don't fit in any other category
	GameClassificationOther GameClassification = "other"
	// comic book (pdf, jpg, specific comic formats, etc.)
	GameClassificationComic GameClassification = "comic"
	// book (pdf, jpg, specific e-book formats, etc.)
	GameClassificationBook GameClassification = "book"
)

// Presentation information for embed games
type GameEmbedData struct {
	// Game this embed info is for
	GameID int64 `json:"gameId" gorm:"primary_key;auto_increment:false"`

	// width of the initial viewport, in pixels
	Width int64 `json:"width"`

	// height of the initial viewport, in pixels
	Height int64 `json:"height"`

	// for itch.io website, whether or not a fullscreen button should be shown
	Fullscreen bool `json:"fullscreen"`
}

// Describes a discount for a game.
type Sale struct {
	// Site-wide unique identifier generated by itch.io
	ID int64 `json:"id"`

	// Game this sale is for
	GameID int64 `json:"gameId"`

	// Discount rate in percent.
	// Can be negative, see https://itch.io/updates/introducing-reverse-sales
	Rate float64 `json:"rate"`
	// Timestamp the sale started at
	StartDate string `json:"startDate"`
	// Timestamp the sale ends at
	EndDate string `json:"endDate"`
}

// An Upload is a downloadable file. Some are wharf-enabled, which means
// they're actually a "channel" that may contain multiple builds, pushed
// with <https://github.com/itchio/butler>
type Upload struct {
	// Site-wide unique identifier generated by itch.io
	ID int64 `json:"id"`
	// Original file name (example: `Overland_x64.zip`)
	Filename string `json:"filename"`
	// Human-friendly name set by developer (example: `Overland for Windows 64-bit`)
	DisplayName string `json:"displayName"`
	// Size of upload in bytes. For wharf-enabled uploads, it's the archive size.
	Size int64 `json:"size"`
	// Name of the wharf channel for this upload, if it's a wharf-enabled upload
	ChannelName string `json:"channelName"`
	// Latest build for this upload, if it's a wharf-enabled upload
	Build *Build `json:"build"`
	// Is this upload a demo that can be downloaded for free?
	Demo bool `json:"demo"`
	// Is this upload a pre-order placeholder?
	Preorder bool `json:"preorder"`

	// Upload type: default, soundtrack, etc.
	Type string `json:"type"`

	// Is this upload tagged with 'macOS compatible'? (creator-controlled)
	OSX bool `json:"pOsx"`
	// Is this upload tagged with 'Linux compatible'? (creator-controlled)
	Linux bool `json:"pLinux"`
	// Is this upload tagged with 'Windows compatible'? (creator-controlled)
	Windows bool `json:"pWindows"`
	// Is this upload tagged with 'Android compatible'? (creator-controlled)
	Android bool `json:"pAndroid"`

	// Date this upload was created at
	CreatedAt time.Time `json:"createdAt"`
	// Date this upload was last updated at (order changed, display name set, etc.)
	UpdatedAt time.Time `json:"updatedAt"`
}

// A Collection is a set of games, curated by humans.
type Collection struct {
	// Site-wide unique identifier generated by itch.io
	ID int64 `json:"id"`

	// Human-friendly title for collection, for example `Couch coop games`
	Title string `json:"title"`

	// Date this collection was created at
	CreatedAt time.Time `json:"createdAt"`
	// Date this collection was last updated at (item added, title set, etc.)
	UpdatedAt time.Time `json:"updatedAt"`

	// Number of games in the collection. This might not be accurate
	// as some games might not be accessible to whoever is asking (project
	// page deleted, visibility level changed, etc.)
	GamesCount int64 `json:"gamesCount"`

	// Games in this collection: filled in API response
	Games []*Game `json:"games,omitempty" gorm:"many2many:collection_games"`

	// Games in this collection, with additional info
	CollectionGames []*CollectionGame `json:"collectionGames,omitempty"`

	UserID int64 `json:"userId"`
	User   *User `json:"user,omitempty"`
}

type CollectionGame struct {
	CollectionID int64       `json:"collectionId" gorm:"primary_key;auto_increment:false"`
	Collection   *Collection `json:"collection,omitempty"`

	GameID int64 `json:"gameId" gorm:"primary_key;auto_increment:false"`
	Game   *Game `json:"game,omitempty"`

	Position int64 `json:"position"`

	CreatedAt time.Time `json:"createdAt"`
	UpdatedAt time.Time `json:"updatedAt"`

	Blurb  string `json:"blurb"`
	UserID int64  `json:"userId"`
}

// A download key is often generated when a purchase is made, it
// allows downloading uploads for a game that are not available
// for free.
type DownloadKey struct {
	// Site-wide unique identifier generated by itch.io
	ID int64 `json:"id"`

	// Identifier of the game to which this download key grants access
	GameID int64 `json:"gameId"`

	// Game to which this download key grants access
	Game *Game `json:"game,omitempty"`

	// Date this key was created at (often coincides with purchase time)
	CreatedAt time.Time `json:"createdAt"`
	// Date this key was last updated at
	UpdatedAt time.Time `json:"updatedAt"`

	// Identifier of the itch.io user to which this key belongs
	OwnerID int64 `json:"ownerId"`
}

// Build contains information about a specific build
type Build struct {
	// Site-wide unique identifier generated by itch.io
	ID int64 `json:"id"`
	// Identifier of the build before this one on the same channel,
	// or 0 if this is the initial build.
	ParentBuildID int64 `json:"parentBuildId"`
	// State of the build: started, processing, etc.
	State BuildState `json:"state"`

	// Automatically-incremented version number, starting with 1
	Version int64 `json:"version"`
	// Value specified by developer with `--userversion` when pushing a build
	// Might not be unique across builds of a given channel.
	UserVersion string `json:"userVersion"`

	// Files associated with this build - often at least an archive,
	// a signature, and a patch. Some might be missing while the build
	// is still processing or if processing has failed.
	Files []*BuildFile `json:"files"`

	// User who pushed the build
	User *User `json:"user"`
	// Timestamp the build was created at
	CreatedAt time.Time `json:"createdAt"`
	// Timestamp the build was last updated at
	UpdatedAt time.Time `json:"updatedAt"`
}

// BuildState describes the state of a build, relative to its initial upload, and
// its processing.
type BuildState string

const (
	// BuildStateStarted is the state of a build from its creation until the initial upload is complete
	BuildStateStarted BuildState = "started"
	// BuildStateProcessing is the state of a build from the initial upload's completion to its fully-processed state.
	// This state does not mean the build is actually being processed right now, it's just queued for processing.
	BuildStateProcessing BuildState = "processing"
	// BuildStateCompleted means the build was successfully processed. Its patch hasn't necessarily been
	// rediff'd yet, but we have the holy (patch,signature,archive) trinity.
	BuildStateCompleted BuildState = "completed"
	// BuildStateFailed means something went wrong with the build. A failing build will not update the channel
	// head and can be requeued by the itch.io team, although if a new build is pushed before they do,
	// that new build will "win".
	BuildStateFailed BuildState = "failed"
)

// BuildFile contains information about a build's "file", which could be its
// archive, its signature, its patch, etc.
type BuildFile struct {
	// Site-wide unique identifier generated by itch.io
	ID int64 `json:"id"`
	// Size of this build file
	Size int64 `json:"size"`
	// State of this file: created, uploading, uploaded, etc.
	State BuildFileState `json:"state"`
	// Type of this build file: archive, signature, patch, etc.
	Type BuildFileType `json:"type"`
	// Subtype of this build file, usually indicates compression
	SubType BuildFileSubType `json:"subType"`

	// Date this build file was created at
	CreatedAt time.Time `json:"createdAt"`
	// Date this build file was last updated at
	UpdatedAt time.Time `json:"updatedAt"`
}

// BuildFileState describes the state of a specific file for a build
type BuildFileState string

const (
	// BuildFileStateCreated means the file entry exists on itch.io
	BuildFileStateCreated BuildFileState = "created"
	// BuildFileStateUploading means the file is currently being uploaded to storage
	BuildFileStateUploading BuildFileState = "uploading"
	// BuildFileStateUploaded means the file is ready
	BuildFileStateUploaded BuildFileState = "uploaded"
	// BuildFileStateFailed means the file failed uploading
	BuildFileStateFailed BuildFileState = "failed"
)

// BuildFileType describes the type of a build file: patch, archive, signature, etc.
type BuildFileType string

const (
	// BuildFileTypePatch describes wharf patch files (.pwr)
	BuildFileTypePatch BuildFileType = "patch"
	// BuildFileTypeArchive describes canonical archive form (.zip)
	BuildFileTypeArchive BuildFileType = "archive"
	// BuildFileTypeSignature describes wharf signature files (.pws)
	BuildFileTypeSignature BuildFileType = "signature"
	// BuildFileTypeManifest is reserved
	BuildFileTypeManifest BuildFileType = "manifest"
	// BuildFileTypeUnpacked describes the single file that is in the build (if it was just a single file)
	BuildFileTypeUnpacked BuildFileType = "unpacked"
)

// BuildFileSubType describes the subtype of a build file: mostly its compression
// level. For example, rediff'd patches are "optimized", whereas initial patches are "default"
type BuildFileSubType string

const (
	// BuildFileSubTypeDefault describes default compression (rsync patches)
	BuildFileSubTypeDefault BuildFileSubType = "default"
	// BuildFileSubTypeGzip is reserved
	BuildFileSubTypeGzip BuildFileSubType = "gzip"
	// BuildFileSubTypeOptimized describes optimized compression (rediff'd / bsdiff patches)
	BuildFileSubTypeOptimized BuildFileSubType = "optimized"
)
<|endoftext|>