<commit_before>// Copyright (c) 2012-2018 Ugorji Nwoke. All rights reserved.
// Use of this source code is governed by a MIT license found in the LICENSE file.

// codecgen generates codec.Selfer implementations for a set of types.
package main

import (
	"bufio"
	"bytes"
	"errors"
	"flag"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"math/rand"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"text/template"
	"time"
)

const genCodecPkg = "codec1978" // keep this in sync with codec.genCodecPkg

const genFrunMainTmpl = `//+build ignore

// Code generated - temporary main package for codecgen - DO NOT EDIT.

package main
{{ if .Types }}import "{{ .ImportPath }}"{{ end }}
func main() {
	{{ $.PackageName }}.CodecGenTempWrite{{ .RandString }}()
}
`

// const genFrunPkgTmpl = `//+build codecgen
const genFrunPkgTmpl = `

// Code generated - temporary package for codecgen - DO NOT EDIT.

package {{ $.PackageName }}

import (
	{{ if not .CodecPkgFiles }}{{ .CodecPkgName }} "{{ .CodecImportPath }}"{{ end }}
	"os"
	"reflect"
	"bytes"
	"strings"
	"go/format"
)

func CodecGenTempWrite{{ .RandString }}() {
	os.Remove("{{ .OutFile }}")
	fout, err := os.Create("{{ .OutFile }}")
	if err != nil {
		panic(err)
	}
	defer fout.Close()

	var typs []reflect.Type
	var typ reflect.Type
	var numfields int
{{ range $index, $element := .Types }}
	var t{{ $index }} {{ . }}
typ = reflect.TypeOf(t{{ $index }})
	typs = append(typs, typ)
	if typ.Kind() == reflect.Struct { numfields += typ.NumField() } else { numfields += 1 }
{{ end }}

	// println("initializing {{ .OutFile }}, buf size: {{ .AllFilesSize }}*16",
	// 	{{ .AllFilesSize }}*16, "num fields: ", numfields)
	var out = bytes.NewBuffer(make([]byte, 0, numfields*1024)) // {{ .AllFilesSize }}*16
	{{ if not .CodecPkgFiles }}{{ .CodecPkgName }}.{{ end }}Gen(out,
		"{{ .BuildTag }}", "{{ .PackageName }}", "{{ .RandString }}", {{ .NoExtensions }},
		{{ if not .CodecPkgFiles }}{{ .CodecPkgName }}.{{ end }}NewTypeInfos(strings.Split("{{ .StructTags }}", ",")),
		 typs...)

	bout, err := format.Source(out.Bytes())
	// println("... lengths: before formatting: ", len(out.Bytes()), ", after formatting", len(bout))
	if err != nil {
		fout.Write(out.Bytes())
		panic(err)
	}
	fout.Write(bout)
}

`

// Generate is given a list of *.go files to parse, and an output file (fout).
//
// It finds all types T in the files, and it creates 2 tmp files (frun).
//   - main package file passed to 'go run'
//   - package level file which calls *genRunner.Selfer to write Selfer impls for each T.
// We use a package level file so that it can reference unexported types in the package being worked on.
// Tool then executes: "go run __frun__" which creates fout.
// fout contains Codec(En|De)codeSelf implementations for every type T.
//
func Generate(outfile, buildTag, codecPkgPath string,
	uid int64,
	goRunTag string, st string,
	regexName, notRegexName *regexp.Regexp,
	deleteTempFile, noExtensions bool,
	infiles ...string) (err error) {
	// For each file, grab AST, find each type, and write a call to it.
	if len(infiles) == 0 {
		return
	}
	if outfile == "" || codecPkgPath == "" {
		err = errors.New("outfile and codec package path cannot be blank")
		return
	}
	if uid < 0 {
		uid = -uid
	} else if uid == 0 {
		rr := rand.New(rand.NewSource(time.Now().UnixNano()))
		uid = 101 + rr.Int63n(9777)
	}
	// We have to parse dir for package, before opening the temp file for writing (else ImportDir fails).
	// Also, ImportDir(...) must take an absolute path.
	lastdir := filepath.Dir(outfile)
	absdir, err := filepath.Abs(lastdir)
	if err != nil {
		return
	}
	importPath, err := pkgPath(absdir)
	if err != nil {
		return
	}
	type tmplT struct {
		CodecPkgName    string
		CodecImportPath string
		ImportPath      string
		OutFile         string
		PackageName     string
		RandString      string
		BuildTag        string
		StructTags      string
		Types           []string
		AllFilesSize    int64
		CodecPkgFiles   bool
		NoExtensions    bool
	}
	tv := tmplT{
		CodecPkgName:    genCodecPkg,
		OutFile:         outfile,
		CodecImportPath: codecPkgPath,
		BuildTag:        buildTag,
		RandString:      strconv.FormatInt(uid, 10),
		StructTags:      st,
		NoExtensions:    noExtensions,
	}
	tv.ImportPath = importPath
	if tv.ImportPath == tv.CodecImportPath {
		tv.CodecPkgFiles = true
		tv.CodecPkgName = "codec"
	} else {
		// HACK: always handle vendoring. It should be typically on in go 1.6, 1.7
		tv.ImportPath = stripVendor(tv.ImportPath)
	}
	astfiles := make([]*ast.File, len(infiles))
	var fi os.FileInfo
	for i, infile := range infiles {
		if filepath.Dir(infile) != lastdir {
			err = errors.New("in files must all be in same directory as outfile")
			return
		}
		if fi, err = os.Stat(infile); err != nil {
			return
		}
		tv.AllFilesSize += fi.Size()

		fset := token.NewFileSet()
		astfiles[i], err = parser.ParseFile(fset, infile, nil, 0)
		if err != nil {
			return
		}
		if i == 0 {
			tv.PackageName = astfiles[i].Name.Name
			if tv.PackageName == "main" {
				// codecgen cannot be run on types in the 'main' package.
				// A temporary 'main' package must be created, and should reference the fully built
				// package containing the types.
				// Also, the temporary main package will conflict with the main package which already has a main method.
				err = errors.New("codecgen cannot be run on types in the 'main' package")
				return
			}
		}
	}

	// keep track of types with selfer methods
	// selferMethods := []string{"CodecEncodeSelf", "CodecDecodeSelf"}
	selferEncTyps := make(map[string]bool)
	selferDecTyps := make(map[string]bool)
	for _, f := range astfiles {
		for _, d := range f.Decls {
			// if fd, ok := d.(*ast.FuncDecl); ok && fd.Recv != nil && fd.Recv.NumFields() == 1 {
			if fd, ok := d.(*ast.FuncDecl); ok && fd.Recv != nil && len(fd.Recv.List) == 1 {
				recvType := fd.Recv.List[0].Type
				if ptr, ok := recvType.(*ast.StarExpr); ok {
					recvType = ptr.X
				}
				if id, ok := recvType.(*ast.Ident); ok {
					switch fd.Name.Name {
					case "CodecEncodeSelf":
						selferEncTyps[id.Name] = true
					case "CodecDecodeSelf":
						selferDecTyps[id.Name] = true
					}
				}
			}
		}
	}

	// now find types
	for _, f := range astfiles {
		for _, d := range f.Decls {
			if gd, ok := d.(*ast.GenDecl); ok {
				for _, dd := range gd.Specs {
					if td, ok := dd.(*ast.TypeSpec); ok {
						// if len(td.Name.Name) == 0 || td.Name.Name[0] > 'Z' || td.Name.Name[0] < 'A' {
						if len(td.Name.Name) == 0 {
							continue
						}

						// only generate for:
						//   struct: StructType
						//   primitives (numbers, bool, string): Ident
						//   map: MapType
						//   slice, array: ArrayType
						//   chan: ChanType
						// do not generate:
						//   FuncType, InterfaceType, StarExpr (ptr), etc
						//
						// We generate for all these types (not just structs), because they may be a field
						// in another struct which doesn't have codecgen run on it, and it will be nice
						// to take advantage of the fact that the type is a Selfer.
						switch td.Type.(type) {
						case *ast.StructType, *ast.Ident, *ast.MapType, *ast.ArrayType, *ast.ChanType:
							// only add to tv.Types iff
							//   - it matches per the -r parameter
							//   - it doesn't match per the -nr parameter
							//   - it doesn't have any of the Selfer methods in the file
							if regexName.FindStringIndex(td.Name.Name) != nil &&
								notRegexName.FindStringIndex(td.Name.Name) == nil &&
								!selferEncTyps[td.Name.Name] &&
								!selferDecTyps[td.Name.Name] {
								tv.Types = append(tv.Types, td.Name.Name)
							}
						}
					}
				}
			}
		}
	}

	if len(tv.Types) == 0 {
		return
	}

	// we cannot use ioutil.TempFile, because we cannot guarantee the file suffix (.go).
	// Also, we cannot create file in temp directory,
	// because go run will not work (as it needs to see the types here).
	// Consequently, create the temp file in the current directory, and remove when done.

	// frun, err = ioutil.TempFile("", "codecgen-")
	// frunName := filepath.Join(os.TempDir(), "codecgen-"+strconv.FormatInt(time.Now().UnixNano(), 10)+".go")

	frunMainName := "codecgen-main-" + tv.RandString + ".generated.go"
	frunPkgName := "codecgen-pkg-" + tv.RandString + ".generated.go"
	if deleteTempFile {
		defer os.Remove(frunMainName)
		defer os.Remove(frunPkgName)
	}
	// var frunMain, frunPkg *os.File
	if _, err = gen1(frunMainName, genFrunMainTmpl, &tv); err != nil {
		return
	}
	if _, err = gen1(frunPkgName, genFrunPkgTmpl, &tv); err != nil {
		return
	}

	// remove outfile, so "go run ..." will not think that types in outfile already exist.
	os.Remove(outfile)

	// execute go run frun
	cmd := exec.Command("go", "run", "-tags", "codecgen.exec safe "+goRunTag, frunMainName) //, frunPkg.Name())
	var buf bytes.Buffer
	cmd.Stdout = &buf
	cmd.Stderr = &buf
	if err = cmd.Run(); err != nil {
		err = fmt.Errorf("error running 'go run %s': %v, console: %s",
			frunMainName, err, buf.Bytes())
		return
	}
	os.Stdout.Write(buf.Bytes())
	return
}

func gen1(frunName, tmplStr string, tv interface{}) (frun *os.File, err error) {
	os.Remove(frunName)
	if frun, err = os.Create(frunName); err != nil {
		return
	}
	defer frun.Close()

	t := template.New("")
	if t, err = t.Parse(tmplStr); err != nil {
		return
	}
	bw := bufio.NewWriter(frun)
	if err = t.Execute(bw, tv); err != nil {
		bw.Flush()
		return
	}
	if err = bw.Flush(); err != nil {
		return
	}
	return
}

// copied from ../gen.go (keep in sync).
func stripVendor(s string) string {
	// HACK: Misbehaviour occurs in go 1.5. May have to re-visit this later.
	// if s contains /vendor/ OR startsWith vendor/, then return everything after it.
	const vendorStart = "vendor/"
	const vendorInline = "/vendor/"
	if i := strings.LastIndex(s, vendorInline); i >= 0 {
		s = s[i+len(vendorInline):]
	} else if strings.HasPrefix(s, vendorStart) {
		s = s[len(vendorStart):]
	}
	return s
}

func main() {
	o := flag.String("o", "", "out file")
	c := flag.String("c", genCodecPath, "codec path")
	t := flag.String("t", "", "build tag to put in file")
	r := flag.String("r", ".*", "regex for type name to match")
	nr := flag.String("nr", "^$", "regex for type name to exclude")
	rt := flag.String("rt", "", "tags for go run")
	st := flag.String("st", "codec,json", "struct tag keys to introspect")
	x := flag.Bool("x", false, "keep temp file")
	_ = flag.Bool("u", false, "Allow unsafe use. ***IGNORED*** - kept for backwards compatibility: ")
	d := flag.Int64("d", 0, "random identifier for use in generated code")
	nx := flag.Bool("nx", false, "do not support extensions - support of extensions may cause extra allocation")

	flag.Parse()
	err := Generate(*o, *t, *c, *d, *rt, *st,
		regexp.MustCompile(*r), regexp.MustCompile(*nr), !*x, *nx, flag.Args()...)
	if err != nil {
		fmt.Fprintf(os.Stderr, "codecgen error: %v\n", err)
		os.Exit(1)
	}
}
<commit_msg>codecgen: expand documentation and exec command from same dir as input/output files<commit_after>// Copyright (c) 2012-2018 Ugorji Nwoke. All rights reserved.
// Use of this source code is governed by a MIT license found in the LICENSE file.

// codecgen generates static implementations of the encoder and decoder functions
// for a given type, bypassing reflection, and giving some performance benefits in terms of
// wall and cpu time, and memory usage.
//
// Benchmarks (as of Dec 2018) show that codecgen gives about
//
//   - for binary formats (cbor, etc): 25% on encoding and 30% on decoding to/from []byte
//   - for text formats (json, etc): 15% on encoding and 25% on decoding to/from []byte
//
// Note that (as of Dec 2018) codecgen completely ignores
//
// - MissingFielder interface
//   (if you types implements it, codecgen ignores that)
// - decode option PreferArrayOverSlice
//   (we cannot dynamically create non-static arrays without reflection)
//
// In explicit package terms: codecgen generates codec.Selfer implementations for a set of types.
package main

import (
	"bufio"
	"bytes"
	"errors"
	"flag"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"math/rand"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"text/template"
	"time"
)

const genCodecPkg = "codec1978" // keep this in sync with codec.genCodecPkg

const genFrunMainTmpl = `//+build ignore

// Code generated - temporary main package for codecgen - DO NOT EDIT.

package main
{{ if .Types }}import "{{ .ImportPath }}"{{ end }}
func main() {
	{{ $.PackageName }}.CodecGenTempWrite{{ .RandString }}()
}
`

// const genFrunPkgTmpl = `//+build codecgen
const genFrunPkgTmpl = `

// Code generated - temporary package for codecgen - DO NOT EDIT.

package {{ $.PackageName }}

import (
	{{ if not .CodecPkgFiles }}{{ .CodecPkgName }} "{{ .CodecImportPath }}"{{ end }}
	"os"
	"reflect"
	"bytes"
	"strings"
	"go/format"
)

func CodecGenTempWrite{{ .RandString }}() {
	os.Remove("{{ .OutFile }}")
	fout, err := os.Create("{{ .OutFile }}")
	if err != nil {
		panic(err)
	}
	defer fout.Close()

	var typs []reflect.Type
	var typ reflect.Type
	var numfields int
{{ range $index, $element := .Types }}
	var t{{ $index }} {{ . }}
typ = reflect.TypeOf(t{{ $index }})
	typs = append(typs, typ)
	if typ.Kind() == reflect.Struct { numfields += typ.NumField() } else { numfields += 1 }
{{ end }}

	// println("initializing {{ .OutFile }}, buf size: {{ .AllFilesSize }}*16",
	// 	{{ .AllFilesSize }}*16, "num fields: ", numfields)
	var out = bytes.NewBuffer(make([]byte, 0, numfields*1024)) // {{ .AllFilesSize }}*16
	{{ if not .CodecPkgFiles }}{{ .CodecPkgName }}.{{ end }}Gen(out,
		"{{ .BuildTag }}", "{{ .PackageName }}", "{{ .RandString }}", {{ .NoExtensions }},
		{{ if not .CodecPkgFiles }}{{ .CodecPkgName }}.{{ end }}NewTypeInfos(strings.Split("{{ .StructTags }}", ",")),
		 typs...)

	bout, err := format.Source(out.Bytes())
	// println("... lengths: before formatting: ", len(out.Bytes()), ", after formatting", len(bout))
	if err != nil {
		fout.Write(out.Bytes())
		panic(err)
	}
	fout.Write(bout)
}

`

// Generate is given a list of *.go files to parse, and an output file (fout).
//
// It finds all types T in the files, and it creates 2 tmp files (frun).
//   - main package file passed to 'go run'
//   - package level file which calls *genRunner.Selfer to write Selfer impls for each T.
// We use a package level file so that it can reference unexported types in the package being worked on.
// Tool then executes: "go run __frun__" which creates fout.
// fout contains Codec(En|De)codeSelf implementations for every type T.
//
func Generate(outfile, buildTag, codecPkgPath string,
	uid int64,
	goRunTag string, st string,
	regexName, notRegexName *regexp.Regexp,
	deleteTempFile, noExtensions bool,
	infiles ...string) (err error) {
	// For each file, grab AST, find each type, and write a call to it.
	if len(infiles) == 0 {
		return
	}
	if codecPkgPath == "" {
		return errors.New("codec package path cannot be blank")
	}
	if outfile == "" {
		return errors.New("outfile cannot be blank")
	}
	if uid < 0 {
		uid = -uid
	} else if uid == 0 {
		rr := rand.New(rand.NewSource(time.Now().UnixNano()))
		uid = 101 + rr.Int63n(9777)
	}
	// We have to parse dir for package, before opening the temp file for writing (else ImportDir fails).
	// Also, ImportDir(...) must take an absolute path.
	lastdir := filepath.Dir(outfile)
	absdir, err := filepath.Abs(lastdir)
	if err != nil {
		return
	}
	importPath, err := pkgPath(absdir)
	if err != nil {
		return
	}
	type tmplT struct {
		CodecPkgName    string
		CodecImportPath string
		ImportPath      string
		OutFile         string
		PackageName     string
		RandString      string
		BuildTag        string
		StructTags      string
		Types           []string
		AllFilesSize    int64
		CodecPkgFiles   bool
		NoExtensions    bool
	}
	tv := tmplT{
		CodecPkgName:    genCodecPkg,
		OutFile:         outfile,
		CodecImportPath: codecPkgPath,
		BuildTag:        buildTag,
		RandString:      strconv.FormatInt(uid, 10),
		StructTags:      st,
		NoExtensions:    noExtensions,
	}
	tv.ImportPath = importPath
	if tv.ImportPath == tv.CodecImportPath {
		tv.CodecPkgFiles = true
		tv.CodecPkgName = "codec"
	} else {
		// HACK: always handle vendoring. It should be typically on in go 1.6, 1.7
		tv.ImportPath = stripVendor(tv.ImportPath)
	}
	astfiles := make([]*ast.File, len(infiles))
	var fi os.FileInfo
	for i, infile := range infiles {
		if filepath.Dir(infile) != lastdir {
			err = errors.New("all input files must all be in same directory as output file")
			return
		}
		if fi, err = os.Stat(infile); err != nil {
			return
		}
		tv.AllFilesSize += fi.Size()

		fset := token.NewFileSet()
		astfiles[i], err = parser.ParseFile(fset, infile, nil, 0)
		if err != nil {
			return
		}
		if i == 0 {
			tv.PackageName = astfiles[i].Name.Name
			if tv.PackageName == "main" {
				// codecgen cannot be run on types in the 'main' package.
				// A temporary 'main' package must be created, and should reference the fully built
				// package containing the types.
				// Also, the temporary main package will conflict with the main package which already has a main method.
				err = errors.New("codecgen cannot be run on types in the 'main' package")
				return
			}
		}
	}

	// keep track of types with selfer methods
	// selferMethods := []string{"CodecEncodeSelf", "CodecDecodeSelf"}
	selferEncTyps := make(map[string]bool)
	selferDecTyps := make(map[string]bool)
	for _, f := range astfiles {
		for _, d := range f.Decls {
			// if fd, ok := d.(*ast.FuncDecl); ok && fd.Recv != nil && fd.Recv.NumFields() == 1 {
			if fd, ok := d.(*ast.FuncDecl); ok && fd.Recv != nil && len(fd.Recv.List) == 1 {
				recvType := fd.Recv.List[0].Type
				if ptr, ok := recvType.(*ast.StarExpr); ok {
					recvType = ptr.X
				}
				if id, ok := recvType.(*ast.Ident); ok {
					switch fd.Name.Name {
					case "CodecEncodeSelf":
						selferEncTyps[id.Name] = true
					case "CodecDecodeSelf":
						selferDecTyps[id.Name] = true
					}
				}
			}
		}
	}

	// now find types
	for _, f := range astfiles {
		for _, d := range f.Decls {
			if gd, ok := d.(*ast.GenDecl); ok {
				for _, dd := range gd.Specs {
					if td, ok := dd.(*ast.TypeSpec); ok {
						// if len(td.Name.Name) == 0 || td.Name.Name[0] > 'Z' || td.Name.Name[0] < 'A' {
						if len(td.Name.Name) == 0 {
							continue
						}

						// only generate for:
						//   struct: StructType
						//   primitives (numbers, bool, string): Ident
						//   map: MapType
						//   slice, array: ArrayType
						//   chan: ChanType
						// do not generate:
						//   FuncType, InterfaceType, StarExpr (ptr), etc
						//
						// We generate for all these types (not just structs), because they may be a field
						// in another struct which doesn't have codecgen run on it, and it will be nice
						// to take advantage of the fact that the type is a Selfer.
						switch td.Type.(type) {
						case *ast.StructType, *ast.Ident, *ast.MapType, *ast.ArrayType, *ast.ChanType:
							// only add to tv.Types iff
							//   - it matches per the -r parameter
							//   - it doesn't match per the -nr parameter
							//   - it doesn't have any of the Selfer methods in the file
							if regexName.FindStringIndex(td.Name.Name) != nil &&
								notRegexName.FindStringIndex(td.Name.Name) == nil &&
								!selferEncTyps[td.Name.Name] &&
								!selferDecTyps[td.Name.Name] {
								tv.Types = append(tv.Types, td.Name.Name)
							}
						}
					}
				}
			}
		}
	}

	if len(tv.Types) == 0 {
		return
	}

	// we cannot use ioutil.TempFile, because we cannot guarantee the file suffix (.go).
	// Also, we cannot create file in temp directory,
	// because go run will not work (as it needs to see the types here).
	// Consequently, create the temp file in the current directory, and remove when done.

	// frun, err = ioutil.TempFile("", "codecgen-")
	// frunName := filepath.Join(os.TempDir(), "codecgen-"+strconv.FormatInt(time.Now().UnixNano(), 10)+".go")

	frunMainName := filepath.Join(lastdir, "codecgen-main-"+tv.RandString+".generated.go")
	frunPkgName := filepath.Join(lastdir, "codecgen-pkg-"+tv.RandString+".generated.go")
	if deleteTempFile {
		defer os.Remove(frunMainName)
		defer os.Remove(frunPkgName)
	}
	// var frunMain, frunPkg *os.File
	if _, err = gen1(frunMainName, genFrunMainTmpl, &tv); err != nil {
		return
	}
	if _, err = gen1(frunPkgName, genFrunPkgTmpl, &tv); err != nil {
		return
	}

	// remove outfile, so "go run ..." will not think that types in outfile already exist.
	os.Remove(outfile)

	// execute go run frun
	cmd := exec.Command("go", "run", "-tags", "codecgen.exec safe "+goRunTag, frunMainName) //, frunPkg.Name())
	cmd.Dir = lastdir
	var buf bytes.Buffer
	cmd.Stdout = &buf
	cmd.Stderr = &buf
	if err = cmd.Run(); err != nil {
		err = fmt.Errorf("error running 'go run %s': %v, console: %s",
			frunMainName, err, buf.Bytes())
		return
	}
	os.Stdout.Write(buf.Bytes())
	return
}

func gen1(frunName, tmplStr string, tv interface{}) (frun *os.File, err error) {
	os.Remove(frunName)
	if frun, err = os.Create(frunName); err != nil {
		return
	}
	defer frun.Close()

	t := template.New("")
	if t, err = t.Parse(tmplStr); err != nil {
		return
	}
	bw := bufio.NewWriter(frun)
	if err = t.Execute(bw, tv); err != nil {
		bw.Flush()
		return
	}
	if err = bw.Flush(); err != nil {
		return
	}
	return
}

// copied from ../gen.go (keep in sync).
func stripVendor(s string) string {
	// HACK: Misbehaviour occurs in go 1.5. May have to re-visit this later.
	// if s contains /vendor/ OR startsWith vendor/, then return everything after it.
	const vendorStart = "vendor/"
	const vendorInline = "/vendor/"
	if i := strings.LastIndex(s, vendorInline); i >= 0 {
		s = s[i+len(vendorInline):]
	} else if strings.HasPrefix(s, vendorStart) {
		s = s[len(vendorStart):]
	}
	return s
}

func main() {
	o := flag.String("o", "", "out file")
	c := flag.String("c", genCodecPath, "codec path")
	t := flag.String("t", "", "build tag to put in file")
	r := flag.String("r", ".*", "regex for type name to match")
	nr := flag.String("nr", "^$", "regex for type name to exclude")
	rt := flag.String("rt", "", "tags for go run")
	st := flag.String("st", "codec,json", "struct tag keys to introspect")
	x := flag.Bool("x", false, "keep temp file")
	_ = flag.Bool("u", false, "Allow unsafe use. ***IGNORED*** - kept for backwards compatibility: ")
	d := flag.Int64("d", 0, "random identifier for use in generated code")
	nx := flag.Bool("nx", false, "do not support extensions - support of extensions may cause extra allocation")

	flag.Parse()
	err := Generate(*o, *t, *c, *d, *rt, *st,
		regexp.MustCompile(*r), regexp.MustCompile(*nr), !*x, *nx, flag.Args()...)
	if err != nil {
		fmt.Fprintf(os.Stderr, "codecgen error: %v\n", err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package extra

import (
	"fmt"
	"os"
	"os/exec"
	"strconv"

	"github.com/pkg/errors"

	"github.com/elpinal/coco3/extra/ast"
	"github.com/elpinal/coco3/extra/typed"
	"github.com/elpinal/coco3/extra/types"
)

type Env struct {
	cmds map[string]typed.Command
}

func New() Env {
	return Env{cmds: map[string]typed.Command{
		"exec": execCommand,
		"cd":   cdCommand,
		"exit": exitCommand,
		"git":  gitCommand,
	}}
}

func WithoutDefault() Env {
	return Env{cmds: make(map[string]typed.Command)}
}

func (e *Env) Bind(name string, c typed.Command) {
	e.cmds[name] = c
}

func (e *Env) Eval(command *ast.Command) error {
	tc, found := e.cmds[command.Name]
	if !found {
		return fmt.Errorf("no such typed command: %q", command.Name)
	}
	if len(command.Args) != len(tc.Params) {
		return fmt.Errorf("the length of args (%d) != the one of params (%d)", len(command.Args), len(tc.Params))
	}
	for i, arg := range command.Args {
		if arg.Type() != tc.Params[i] {
			return fmt.Errorf("type mismatch: %v != %v", arg.Type(), tc.Params[i])
		}
	}
	return tc.Fn(command.Args)
}

func toSlice(list ast.List) ([]string, error) {
	var ret []string
	for {
		switch x := list.(type) {
		case *ast.Cons:
			ret = append(ret, x.Head)
			list = x.Tail
		case *ast.Empty:
			return ret, nil
		default:
			return nil, fmt.Errorf("unexpected list type: %T", x)
		}
	}
}

var execCommand = typed.Command{
	Params: []types.Type{types.String, types.StringList},
	Fn: func(args []ast.Expr) error {
		cmdArgs, err := toSlice(args[1].(ast.List))
		if err != nil {
			return errors.Wrap(err, "exec")
		}
		cmd := exec.Command(args[0].(*ast.String).Lit, cmdArgs...)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		cmd.Stdin = os.Stdin
		return cmd.Run()
	},
}

var cdCommand = typed.Command{
	Params: []types.Type{types.String},
	Fn: func(args []ast.Expr) error {
		return os.Chdir(args[0].(*ast.String).Lit)
	},
}

var exitCommand = typed.Command{
	Params: []types.Type{types.Int},
	Fn: func(args []ast.Expr) error {
		n, err := strconv.Atoi(args[0].(*ast.Int).Lit)
		if err != nil {
			return err
		}
		os.Exit(n)
		return nil
	},
}

var gitCommand = typed.Command{
	Params: []types.Type{types.Ident, types.StringList},
	Fn: func(args []ast.Expr) error {
		cmdArgs, err := toSlice(args[1].(ast.List))
		if err != nil {
			return errors.Wrap(err, "git")
		}
		var cmd *exec.Cmd
		switch name := args[0].(*ast.Ident); name.Lit {
		case "command":
			cmd = exec.Command("git", cmdArgs...)
		default:
			cmd = exec.Command("git", append([]string{name.Lit}, cmdArgs...)...)
		}
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		cmd.Stdin = os.Stdin
		return cmd.Run()
	},
}
<commit_msg>Support 'cargo' typed command<commit_after>package extra

import (
	"fmt"
	"os"
	"os/exec"
	"strconv"

	"github.com/pkg/errors"

	"github.com/elpinal/coco3/extra/ast"
	"github.com/elpinal/coco3/extra/typed"
	"github.com/elpinal/coco3/extra/types"
)

type Env struct {
	cmds map[string]typed.Command
}

func New() Env {
	return Env{cmds: map[string]typed.Command{
		"exec": execCommand,
		"cd":   cdCommand,
		"exit": exitCommand,

		"git":   gitCommand,
		"cargo": cargoCommand,
	}}
}

func WithoutDefault() Env {
	return Env{cmds: make(map[string]typed.Command)}
}

func (e *Env) Bind(name string, c typed.Command) {
	e.cmds[name] = c
}

func (e *Env) Eval(command *ast.Command) error {
	tc, found := e.cmds[command.Name]
	if !found {
		return fmt.Errorf("no such typed command: %q", command.Name)
	}
	if len(command.Args) != len(tc.Params) {
		return fmt.Errorf("the length of args (%d) != the one of params (%d)", len(command.Args), len(tc.Params))
	}
	for i, arg := range command.Args {
		if arg.Type() != tc.Params[i] {
			return fmt.Errorf("type mismatch: %v != %v", arg.Type(), tc.Params[i])
		}
	}
	return tc.Fn(command.Args)
}

func toSlice(list ast.List) ([]string, error) {
	var ret []string
	for {
		switch x := list.(type) {
		case *ast.Cons:
			ret = append(ret, x.Head)
			list = x.Tail
		case *ast.Empty:
			return ret, nil
		default:
			return nil, fmt.Errorf("unexpected list type: %T", x)
		}
	}
}

var execCommand = typed.Command{
	Params: []types.Type{types.String, types.StringList},
	Fn: func(args []ast.Expr) error {
		cmdArgs, err := toSlice(args[1].(ast.List))
		if err != nil {
			return errors.Wrap(err, "exec")
		}
		cmd := exec.Command(args[0].(*ast.String).Lit, cmdArgs...)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		cmd.Stdin = os.Stdin
		return cmd.Run()
	},
}

var cdCommand = typed.Command{
	Params: []types.Type{types.String},
	Fn: func(args []ast.Expr) error {
		return os.Chdir(args[0].(*ast.String).Lit)
	},
}

var exitCommand = typed.Command{
	Params: []types.Type{types.Int},
	Fn: func(args []ast.Expr) error {
		n, err := strconv.Atoi(args[0].(*ast.Int).Lit)
		if err != nil {
			return err
		}
		os.Exit(n)
		return nil
	},
}

var gitCommand = typed.Command{
	Params: []types.Type{types.Ident, types.StringList},
	Fn: func(args []ast.Expr) error {
		cmdArgs, err := toSlice(args[1].(ast.List))
		if err != nil {
			return errors.Wrap(err, "git")
		}
		var cmd *exec.Cmd
		switch name := args[0].(*ast.Ident); name.Lit {
		case "command":
			cmd = exec.Command("git", cmdArgs...)
		default:
			cmd = exec.Command("git", append([]string{name.Lit}, cmdArgs...)...)
		}
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		cmd.Stdin = os.Stdin
		return cmd.Run()
	},
}

var cargoCommand = typed.Command{
	Params: []types.Type{types.Ident, types.StringList},
	Fn: func(args []ast.Expr) error {
		cmdArgs, err := toSlice(args[1].(ast.List))
		if err != nil {
			return errors.Wrap(err, "cargo")
		}
		var cmd *exec.Cmd
		switch name := args[0].(*ast.Ident); name.Lit {
		case "command":
			cmd = exec.Command("cargo", cmdArgs...)
		default:
			cmd = exec.Command("cargo", append([]string{name.Lit}, cmdArgs...)...)
		}
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		cmd.Stdin = os.Stdin
		return cmd.Run()
	},
}
<|endoftext|>


<commit_before>package api

var Cfg *Config

var (
	BucketsBucketName  = "buckets"
	UsersBucketName    = "users"
	SettingsBucketName = "settings"
	StaticBucketName   = "static"
	PagesBucketName    = "pages"

	ConsoleBucketName = "console"

	GuestUserFileName       = "guestuser"
	RoutingSettingsFileName = "routing"

	// system store names
	// BucketsStoreName  = "boltdb.buckets"
	UsersStoreName = "boltdb.users"
	// SettingsStoreName = "boltdb.settings"

	// System file names
	MainSettingsFileName = "main"
)

type Config struct {
	Address       string
	WorkspacePath string

	Session ApiSessionConfig
	Store   AppStoreConfig
	Search  SearchStore
}

type ApiSessionConfig struct {
	Path     string
	Domain   string
	MaxAge   int
	Secure   bool
	HttpOnly bool

	SecretKey  string
	BucketName string

	Store StoreConfig
}

type StoreConfig struct {
	Provider       string // boltdb
	BoltDBFilePath string
}

type AppStoreConfig struct {
	StoreConfig

	StaticPath string
}

type SearchStore struct {
	Host      string
	IndexName string
}
<commit_msg>app versions<commit_after>package api

var Cfg *Config
var AppVersion string = "dev"

var (
	BucketsBucketName  = "buckets"
	UsersBucketName    = "users"
	SettingsBucketName = "settings"
	StaticBucketName   = "static"
	PagesBucketName    = "pages"

	ConsoleBucketName = "console"

	GuestUserFileName       = "guestuser"
	RoutingSettingsFileName = "routing"

	// system store names
	// BucketsStoreName  = "boltdb.buckets"
	UsersStoreName = "boltdb.users"
	// SettingsStoreName = "boltdb.settings"

	// System file names
	MainSettingsFileName = "main"
)

type Config struct {
	AppVersion string

	Address       string
	WorkspacePath string

	Session ApiSessionConfig
	Store   AppStoreConfig
	Search  SearchStore
}

type ApiSessionConfig struct {
	Path     string
	Domain   string
	MaxAge   int
	Secure   bool
	HttpOnly bool

	SecretKey  string
	BucketName string

	Store StoreConfig
}

type StoreConfig struct {
	Provider       string // boltdb
	BoltDBFilePath string
}

type AppStoreConfig struct {
	StoreConfig

	StaticPath string
}

type SearchStore struct {
	Host      string
	IndexName string
}
<|endoftext|>


<commit_before>/**
 * Copyright 2016 IBM Corp.
 *
 * 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 services

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/user"
	"reflect"
	"runtime"
	"strconv"
	"strings"
	"github.ibm.com/riethm/gopherlayer/config"
)

const DEFAULT_ENDPOINT = "https://api.softlayer.com/rest/v3"

type Options struct {
	ObjectId     *int
	ObjectMask   string
	ObjectFilter string
	ResultLimit  *int
	StartOffset  *int
}

func (r *Options) Id(id int) *Options {
	r.ObjectId = &id
	return r
}

func (r *Options) Mask(mask string) *Options {
	r.ObjectMask = mask
	return r
}

func (r *Options) Filter(filter string) *Options {
	r.ObjectFilter = filter
	return r
}

func (r *Options) Limit(limit int) *Options {
	r.ResultLimit = &limit
	return r
}

func (r *Options) Offset(offset int) *Options {
	r.StartOffset = &offset
	return r
}

type Session struct {
	UserName string
	ApiKey   string
	Endpoint string
	Debug    bool
}

func NewSession(args ...interface{}) Session {
	keys := map[string]int{"username": 0, "api_key": 1, "endpoint_url": 2}
	values := []string{"", "", ""}

	for i := 0; i < len(args); i++ {
		values[i] = args[i].(string)
	}

	// Default to the environment variables
	envFallback("SOFTLAYER_USERNAME", &values[keys["username"]])
	envFallback("SOFTLAYER_API_KEY", &values[keys["api_key"]])
	envFallback("SOFTLAYER_ENDPOINT_URL", &values[keys["endpoint_url"]])

	// Read ~/.softlayer for configuration
	u, err := user.Current()
	if err != nil {
		panic("session: Could not determine current user.")
	}

	configPath := fmt.Sprintf("%s/.softlayer", u.HomeDir)
	if _, err = os.Stat(configPath); !os.IsNotExist(err) {
		// config file exists
		file, err := config.LoadFile(configPath)
		if err != nil {
			log.Println(fmt.Sprintf("[WARN] session: Could not parse %s : %s", configPath, err))
		} else {
			for k, v := range keys {
				value, ok := file.Get("softlayer", k)
				if ok && values[v] == "" {
					values[v] = value
				}
			}
		}
	}

	endpointUrl := values[keys["endpoint_url"]]
	if endpointUrl == "" || !strings.Contains(endpointUrl, "/rest/") {
		endpointUrl = DEFAULT_ENDPOINT
	}

	return Session{
		UserName: values[keys["username"]],
		ApiKey:   values[keys["api_key"]],
		Endpoint: endpointUrl,
	}
}

func (r *Session) String() string {
	return "Username: " + r.UserName +
		", ApiKey: " + r.ApiKey +
		", Endpoint: " + r.Endpoint
}

func (r *Session) DoRequest(service string, method string, args []interface{}, options *Options, pResult interface{}) error {
	restMethod := httpMethod(method, args)

	// Parse any method parameters and determine the HTTP method
	var parameters []byte
	if len(args) > 0 {
		// parse the parameters
		parameters, _ = json.Marshal(
			map[string]interface{}{
				"parameters": args,
			})
	}

	// Start building the request path
	path := service

	if options.ObjectId != nil {
		path = path + "/" + strconv.Itoa(*options.ObjectId)
	}

	// omit the API method name if the method represents one of the basic REST methods
	if method != "getObject" && method != "deleteObject" && method != "createObject" &&
		method != "createObjects" && method != "editObject" && method != "editObjects" {
		path = path + "/" + method
	}

	path = path + ".json"

	resp, code, err := makeHttpRequest(
		r,
		path,
		restMethod,
		bytes.NewBuffer(parameters),
		options)

	if err != nil {
		return fmt.Errorf("Error during HTTP request: %s", err)
	}

	if code < 200 || code > 299 {
		e := Error{StatusCode: code}

		err = json.Unmarshal(resp, &e)

		// If unparseable, wrap the json error
		if err != nil {
			e.wrapped = err
			e.Message = err.Error()
		}

		return e
	}

	returnType := reflect.TypeOf(pResult).String()

	// Some APIs that normally return a collection, omit the []'s when the API returns a single value
	if strings.Index(returnType, "[]") == 1 && strings.Index(string(resp), "[") != 0 {
		resp = []byte("[" + string(resp) + "]")
	}

	// At this point, all that's left to do is parse the return value to the appropriate type, and return
	// any parse errors (or nil if successful)

	switch returnType {
	case "[]byte":
		pResult = &resp
		return nil
	case "*void":
		return nil
	case "*uint":
		*pResult.(*int), err = strconv.Atoi(string(resp))
		if err != nil {
			return Error{Message: err.Error(), wrapped: err}
		}
		return nil
	case "*bool":
		*pResult.(*bool), err = strconv.ParseBool(string(resp))
		if err != nil {
			return Error{Message: err.Error(), wrapped: err}
		}
	case "float64":
		*pResult.(*float64), err = strconv.ParseFloat(string(resp), 64)
		if err != nil {
			return Error{Message: err.Error(), wrapped: err}
		}
	case "string":
		*pResult.(*string) = string(resp)
		return nil
	}

	// Must be a json representation of one of the many softlayer datatypes
	err = json.Unmarshal(resp, pResult)
	if err != nil {
		return Error{Message: err.Error(), wrapped: err}
	}
	return nil
}

type Error struct {
	StatusCode int
	Exception  string `json:"code"`
	Message    string `json:"error"`
	wrapped    error
}

func (r Error) Error() string {
	if r.wrapped != nil {
		return r.wrapped.Error()
	}

	var msg string
	if r.Exception != "" {
		msg = r.Exception + ": "
	}
	if r.Message != "" {
		msg = msg + r.Message + " "
	}
	if r.StatusCode != 0 {
		msg = fmt.Sprintf("%s(HTTP %d)", msg, r.StatusCode)
	}
	return msg
}

func invokeMethod(args []interface{}, session *Session, options *Options, pResult interface{}) error {
	// Get the caller information, which gives us the service and method name
	pc, _, _, _ := runtime.Caller(1)
	f := runtime.FuncForPC(pc)
	segments := strings.Split(f.Name(), ".")
	service, method := segments[len(segments)-2], segments[len(segments)-1]

	// The receiver has the form "(*Type)".  Strip the unnecessary characters
	service = service[2 : len(service)-1]

	// Most services need to be prefixed with "SoftLayer_"
	if service[:6] != "McAfee" {
		service = "SoftLayer_" + service
	}

	// camelCase the method name
	method = strings.ToLower(string(method[0])) + method[1:]

	return session.DoRequest(service, method, args, options, pResult)
}

func envFallback(keyName string, value *string) {
	if *value == "" {
		*value = os.Getenv(keyName)
	}
}

func encodeQuery(opts *Options) string {
	query := new(url.URL).Query()

	if opts.ObjectMask != "" {
		query.Add("objectMask", opts.ObjectMask)
	}

	if opts.ObjectFilter != "" {
		query.Add("objectFilter", opts.ObjectFilter)
	}

	// resultLimit=<offset>,<limit>
	// If offset unspecified, default to 0
	if opts.ResultLimit != nil {
		startOffset := 0
		if opts.StartOffset != nil {
			startOffset = *opts.StartOffset
		}

		query.Add("resultLimit", fmt.Sprintf("%d,%d", startOffset, *opts.ResultLimit))
	}

	return query.Encode()
}

func makeHttpRequest(session *Session, path string, requestType string, requestBody *bytes.Buffer, options *Options) ([]byte, int, error) {
	client := http.DefaultClient

	var url string
	if session.Endpoint == "" {
		url = url + DEFAULT_ENDPOINT
	} else {
		url = url + session.Endpoint
	}
	url = url + "/" + path
	req, err := http.NewRequest(requestType, url, requestBody)
	if err != nil {
		return nil, 0, err
	}

	req.SetBasicAuth(session.UserName, session.ApiKey)

	req.URL.RawQuery = encodeQuery(options)

	if session.Debug {
		log.Println("[DEBUG] Path: ", req.URL)
		log.Println("[DEBUG] Parameters: ", requestBody.String())
	}

	resp, err := client.Do(req)
	if err != nil {
		return nil, 520, err
	}

	defer resp.Body.Close()

	responseBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, resp.StatusCode, err
	}

	return responseBody, resp.StatusCode, nil
}

func httpMethod(name string, args []interface{}) string {
	if name == "deleteObject" {
		return "DELETE"
	} else if name == "editObject" || name == "editObjects" {
		return "PUT"
	} else if name == "createObject" || name == "createObjects" || len(args) > 0 {
		return "POST"
	}

	return "GET"
}
<commit_msg>Refactor switch statement parsing api return value<commit_after>/**
 * Copyright 2016 IBM Corp.
 *
 * 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 services

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/user"
	"reflect"
	"runtime"
	"strconv"
	"strings"
	"github.ibm.com/riethm/gopherlayer/config"
)

const DEFAULT_ENDPOINT = "https://api.softlayer.com/rest/v3"

type Options struct {
	ObjectId     *int
	ObjectMask   string
	ObjectFilter string
	ResultLimit  *int
	StartOffset  *int
}

func (r *Options) Id(id int) *Options {
	r.ObjectId = &id
	return r
}

func (r *Options) Mask(mask string) *Options {
	r.ObjectMask = mask
	return r
}

func (r *Options) Filter(filter string) *Options {
	r.ObjectFilter = filter
	return r
}

func (r *Options) Limit(limit int) *Options {
	r.ResultLimit = &limit
	return r
}

func (r *Options) Offset(offset int) *Options {
	r.StartOffset = &offset
	return r
}

type Session struct {
	UserName string
	ApiKey   string
	Endpoint string
	Debug    bool
}

func NewSession(args ...interface{}) Session {
	keys := map[string]int{"username": 0, "api_key": 1, "endpoint_url": 2}
	values := []string{"", "", ""}

	for i := 0; i < len(args); i++ {
		values[i] = args[i].(string)
	}

	// Default to the environment variables
	envFallback("SOFTLAYER_USERNAME", &values[keys["username"]])
	envFallback("SOFTLAYER_API_KEY", &values[keys["api_key"]])
	envFallback("SOFTLAYER_ENDPOINT_URL", &values[keys["endpoint_url"]])

	// Read ~/.softlayer for configuration
	u, err := user.Current()
	if err != nil {
		panic("session: Could not determine current user.")
	}

	configPath := fmt.Sprintf("%s/.softlayer", u.HomeDir)
	if _, err = os.Stat(configPath); !os.IsNotExist(err) {
		// config file exists
		file, err := config.LoadFile(configPath)
		if err != nil {
			log.Println(fmt.Sprintf("[WARN] session: Could not parse %s : %s", configPath, err))
		} else {
			for k, v := range keys {
				value, ok := file.Get("softlayer", k)
				if ok && values[v] == "" {
					values[v] = value
				}
			}
		}
	}

	endpointUrl := values[keys["endpoint_url"]]
	if endpointUrl == "" || !strings.Contains(endpointUrl, "/rest/") {
		endpointUrl = DEFAULT_ENDPOINT
	}

	return Session{
		UserName: values[keys["username"]],
		ApiKey:   values[keys["api_key"]],
		Endpoint: endpointUrl,
	}
}

func (r *Session) String() string {
	return "Username: " + r.UserName +
		", ApiKey: " + r.ApiKey +
		", Endpoint: " + r.Endpoint
}

func (r *Session) DoRequest(service string, method string, args []interface{}, options *Options, pResult interface{}) error {
	restMethod := httpMethod(method, args)

	// Parse any method parameters and determine the HTTP method
	var parameters []byte
	if len(args) > 0 {
		// parse the parameters
		parameters, _ = json.Marshal(
			map[string]interface{}{
				"parameters": args,
			})
	}

	// Start building the request path
	path := service

	if options.ObjectId != nil {
		path = path + "/" + strconv.Itoa(*options.ObjectId)
	}

	// omit the API method name if the method represents one of the basic REST methods
	if method != "getObject" && method != "deleteObject" && method != "createObject" &&
		method != "createObjects" && method != "editObject" && method != "editObjects" {
		path = path + "/" + method
	}

	path = path + ".json"

	resp, code, err := makeHttpRequest(
		r,
		path,
		restMethod,
		bytes.NewBuffer(parameters),
		options)

	if err != nil {
		return fmt.Errorf("Error during HTTP request: %s", err)
	}

	if code < 200 || code > 299 {
		e := Error{StatusCode: code}

		err = json.Unmarshal(resp, &e)

		// If unparseable, wrap the json error
		if err != nil {
			e.wrapped = err
			e.Message = err.Error()
		}

		return e
	}

	returnType := reflect.TypeOf(pResult).String()

	// Some APIs that normally return a collection, omit the []'s when the API returns a single value
	if strings.Index(returnType, "[]") == 1 && strings.Index(string(resp), "[") != 0 {
		resp = []byte("[" + string(resp) + "]")
	}

	// At this point, all that's left to do is parse the return value to the appropriate type, and return
	// any parse errors (or nil if successful)

	err = nil
	switch returnType {
	case "[]byte":
		pResult = &resp
	case "*void":
	case "*uint":
		*pResult.(*int), err = strconv.Atoi(string(resp))
	case "*bool":
		*pResult.(*bool), err = strconv.ParseBool(string(resp))
	case "float64":
		*pResult.(*float64), err = strconv.ParseFloat(string(resp), 64)
	case "string":
		*pResult.(*string) = string(resp)
	default:
		// Must be a json representation of one of the many softlayer datatypes
		err = json.Unmarshal(resp, pResult)
	}

	if err != nil {
		err = Error{Message: err.Error(), wrapped: err}
	}

	return err
}

type Error struct {
	StatusCode int
	Exception  string `json:"code"`
	Message    string `json:"error"`
	wrapped    error
}

func (r Error) Error() string {
	if r.wrapped != nil {
		return r.wrapped.Error()
	}

	var msg string
	if r.Exception != "" {
		msg = r.Exception + ": "
	}
	if r.Message != "" {
		msg = msg + r.Message + " "
	}
	if r.StatusCode != 0 {
		msg = fmt.Sprintf("%s(HTTP %d)", msg, r.StatusCode)
	}
	return msg
}

func invokeMethod(args []interface{}, session *Session, options *Options, pResult interface{}) error {
	// Get the caller information, which gives us the service and method name
	pc, _, _, _ := runtime.Caller(1)
	f := runtime.FuncForPC(pc)
	segments := strings.Split(f.Name(), ".")
	service, method := segments[len(segments)-2], segments[len(segments)-1]

	// The receiver has the form "(*Type)".  Strip the unnecessary characters
	service = service[2 : len(service)-1]

	// Most services need to be prefixed with "SoftLayer_"
	if service[:6] != "McAfee" {
		service = "SoftLayer_" + service
	}

	// camelCase the method name
	method = strings.ToLower(string(method[0])) + method[1:]

	return session.DoRequest(service, method, args, options, pResult)
}

func envFallback(keyName string, value *string) {
	if *value == "" {
		*value = os.Getenv(keyName)
	}
}

func encodeQuery(opts *Options) string {
	query := new(url.URL).Query()

	if opts.ObjectMask != "" {
		query.Add("objectMask", opts.ObjectMask)
	}

	if opts.ObjectFilter != "" {
		query.Add("objectFilter", opts.ObjectFilter)
	}

	// resultLimit=<offset>,<limit>
	// If offset unspecified, default to 0
	if opts.ResultLimit != nil {
		startOffset := 0
		if opts.StartOffset != nil {
			startOffset = *opts.StartOffset
		}

		query.Add("resultLimit", fmt.Sprintf("%d,%d", startOffset, *opts.ResultLimit))
	}

	return query.Encode()
}

func makeHttpRequest(session *Session, path string, requestType string, requestBody *bytes.Buffer, options *Options) ([]byte, int, error) {
	client := http.DefaultClient

	var url string
	if session.Endpoint == "" {
		url = url + DEFAULT_ENDPOINT
	} else {
		url = url + session.Endpoint
	}
	url = url + "/" + path
	req, err := http.NewRequest(requestType, url, requestBody)
	if err != nil {
		return nil, 0, err
	}

	req.SetBasicAuth(session.UserName, session.ApiKey)

	req.URL.RawQuery = encodeQuery(options)

	if session.Debug {
		log.Println("[DEBUG] Path: ", req.URL)
		log.Println("[DEBUG] Parameters: ", requestBody.String())
	}

	resp, err := client.Do(req)
	if err != nil {
		return nil, 520, err
	}

	defer resp.Body.Close()

	responseBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, resp.StatusCode, err
	}

	return responseBody, resp.StatusCode, nil
}

func httpMethod(name string, args []interface{}) string {
	if name == "deleteObject" {
		return "DELETE"
	} else if name == "editObject" || name == "editObjects" {
		return "PUT"
	} else if name == "createObject" || name == "createObjects" || len(args) > 0 {
		return "POST"
	}

	return "GET"
}
<|endoftext|>


<commit_before>package routers

import (
	"database/sql"
	"errors"
	"fmt"
	"gomp/models"
	"io/ioutil"
	"math/big"
	"mime/multipart"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"gopkg.in/macaron.v1"
)

// RecipeForm encapsulates user input on the Create and Edit recipe screens
type RecipeForm struct {
	Name             string `binding:"Required"`
	Description      string
	Directions       string
	Tags             []string
	IngredientAmount []string `form:"ingredient_amount"`
	IngredientUnit   []int64  `form:"ingredient_unit"`
	IngredientName   []string `form:"ingredient_name"`
}

// NoteForm encapsulates user input for a note on a recipe
type NoteForm struct {
	Note string
}

// AttachmentForm encapsulates user input for attaching a file (image) to a recipe
type AttachmentForm struct {
    FileName    string                `form:"file_name"`
    FileContent *multipart.FileHeader `form:"file_content"`
}

// GetRecipe handles retrieving and rendering a single recipe
func GetRecipe(ctx *macaron.Context) {
	id, err := strconv.ParseInt(ctx.Params("id"), 10, 64)
	if RedirectIfHasError(ctx, err) {
		return
	}

	db, err := models.OpenDatabase()
	if RedirectIfHasError(ctx, err) {
		return
	}
	defer db.Close()

	recipe := &models.Recipe{
		ID: id,
	}
	err = recipe.Read(db)
	if RedirectIfHasError(ctx, err) {
		return
	}
	if recipe == nil {
		NotFound(ctx)
		return
	}

	var notes = new(models.Notes)
	err = notes.List(db, id)
	if RedirectIfHasError(ctx, err) {
		return
	}

	ctx.Data["Recipe"] = recipe
	ctx.Data["Notes"] = notes
	ctx.HTML(http.StatusOK, "recipe/view")
}

// ListRecipes handles retrieving and rending a list of available recipes
func ListRecipes(ctx *macaron.Context) {
	query := ctx.Query("q")
	page := ctx.QueryInt("page")
	if page < 1 {
		page = 1
	}
	count := ctx.QueryInt("count")
	if count < 1 {
		count = 15
	}

	db, err := models.OpenDatabase()
	if RedirectIfHasError(ctx, err) {
		return
	}
	defer db.Close()

	recipes := new(models.Recipes)
	var total int
	if query == "" {
		total, err = recipes.List(db, page, count)
	} else {
		total, err = recipes.Find(db, query, page, count)
	}
	if RedirectIfHasError(ctx, err) {
		return
	}

	ctx.Data["Recipes"] = recipes
	ctx.Data["SearchQuery"] = query
	ctx.Data["ResultCount"] = total
	ctx.HTML(http.StatusOK, "recipe/list")
}

// CreateRecipe handles rendering the create recipe screen
func CreateRecipe(ctx *macaron.Context) {
	db, err := models.OpenDatabase()
	if RedirectIfHasError(ctx, err) {
		return
	}
	defer db.Close()

	units := new(models.Units)
	err = units.List(db)
	if RedirectIfHasError(ctx, err) {
		return
	}
	ctx.Data["Units"] = units
	ctx.HTML(http.StatusOK, "recipe/create")
}

// CreateRecipePost handles processing the supplied
// form input from the create recipe screen
func CreateRecipePost(ctx *macaron.Context, form RecipeForm) {
	db, err := models.OpenDatabase()
	if RedirectIfHasError(ctx, err) {
		return
	}
	defer db.Close()

	tags := make(models.Tags, len(form.Tags))
	for _, tag := range form.Tags {
		tags = append(tags, models.Tag(tag))
	}
	recipe := &models.Recipe{
		Name:        form.Name,
		Description: form.Description,
		Directions:  form.Directions,
		Tags:        tags,
	}

	// TODO: Checks that all the lengths match
	for i := 0; i < len(form.IngredientAmount); i++ {
		// Convert amount string into a floating point number
		amountRat := new(big.Rat)
		amountRat.SetString(form.IngredientAmount[i])
		amount, _ := amountRat.Float64()

		recipe.Ingredients = append(
			recipe.Ingredients,
			models.Ingredient{
				Name:          form.IngredientName[i],
				Amount:        amount,
				AmountDisplay: form.IngredientAmount[i],
				Unit:          models.Unit{ID: form.IngredientUnit[i]},
			})
	}

	err = recipe.Create(db)
	if RedirectIfHasError(ctx, err) {
		return
	}

	ctx.Redirect(fmt.Sprintf("/recipes/%d", recipe.ID))
}

// EditRecipe handles rendering the edit recipe screen
func EditRecipe(ctx *macaron.Context) {
	id, err := strconv.ParseInt(ctx.Params("id"), 10, 64)
	if RedirectIfHasError(ctx, err) {
		return
	}

	db, err := models.OpenDatabase()
	if RedirectIfHasError(ctx, err) {
		return
	}
	defer db.Close()

	recipe := &models.Recipe{ID: id}
	err = recipe.Read(db)
	if err == sql.ErrNoRows {
		NotFound(ctx)
		return
	}
	if RedirectIfHasError(ctx, err) {
		return
	}

	units := new(models.Units)
	err = units.List(db)
	if RedirectIfHasError(ctx, err) {
		return
	}

	ctx.Data["Recipe"] = recipe
	ctx.Data["Units"] = units
	ctx.HTML(http.StatusOK, "recipe/edit")
}

// EditRecipePost handles processing the supplied
// form input from the edit recipe screen
func EditRecipePost(ctx *macaron.Context, form RecipeForm) {
	id, err := strconv.ParseInt(ctx.Params("id"), 10, 64)
	if RedirectIfHasError(ctx, err) {
		return
	}

	db, err := models.OpenDatabase()
	if RedirectIfHasError(ctx, err) {
		return
	}
	defer db.Close()

	tags := make(models.Tags, len(form.Tags))
	for _, tag := range form.Tags {
		tags = append(tags, models.Tag(tag))
	}
	recipe := &models.Recipe{
		ID:          id,
		Name:        form.Name,
		Description: form.Description,
		Directions:  form.Directions,
		Tags:        tags,
	}

	// TODO: Checks that all the lengths match
	for i := 0; i < len(form.IngredientAmount); i++ {
		// Convert amount string into a floating point number
		amountRat := new(big.Rat)
		amountRat, ok := amountRat.SetString(form.IngredientAmount[i])
		var amount float64
		if ok {
			amount, ok = amountRat.Float64()
		}
		if !ok {
			RedirectIfHasError(
				ctx,
				errors.New("Could not convert supplied ingredient amount"))
		}

		recipe.Ingredients = append(
			recipe.Ingredients,
			models.Ingredient{
				Name:          form.IngredientName[i],
				Amount:        amount,
				AmountDisplay: form.IngredientAmount[i],
				RecipeID:      recipe.ID,
				Unit:          models.Unit{ID: form.IngredientUnit[i]},
			})
	}

	err = recipe.Update(db)
	if RedirectIfHasError(ctx, err) {
		return
	}

	ctx.Redirect(fmt.Sprintf("/recipes/%d", id))
}

// DeleteRecipe handles deleting the recipe with the given id
func DeleteRecipe(ctx *macaron.Context) {
	id, err := strconv.ParseInt(ctx.Params("id"), 10, 64)
	if RedirectIfHasError(ctx, err) {
		return
	}

	db, err := models.OpenDatabase()
	if RedirectIfHasError(ctx, err) {
		return
	}
	defer db.Close()

	recipe := &models.Recipe{ID: id}
	err = recipe.Delete(db)
	if RedirectIfHasError(ctx, err) {
		return
	}

	ctx.Redirect("/recipes")
}

func AttachToRecipePost(ctx *macaron.Context, form AttachmentForm) {
	id, err := strconv.ParseInt(ctx.Params("id"), 10, 64)
	if RedirectIfHasError(ctx, err) {
		return
	}

	uploadedFile, err := form.FileContent.Open()
	if RedirectIfHasError(ctx, err) {
		return
	}
	defer uploadedFile.Close()

	uploadedFileData, err := ioutil.ReadAll(uploadedFile)
	if RedirectIfHasError(ctx, err) {
		return
	}
	if ok := isImageFile(uploadedFileData); !ok {
		RedirectIfHasError(ctx, errors.New("Attachment must be an image"))
		return
	}

	destFolderPath := filepath.Join("public", "images", "recipe", strconv.FormatInt(id, 10))
	err = os.MkdirAll(destFolderPath, os.ModePerm)
	if RedirectIfHasError(ctx, err) {
		return
	}

	destFilePath := filepath.Join(destFolderPath, form.FileName)
	destFile, err := os.Create(destFilePath)
	if RedirectIfHasError(ctx, err) {
		return
	}
	defer destFile.Close()
	_, err = destFile.Write(uploadedFileData)
	if RedirectIfHasError(ctx, err) {
		return
	}

	ctx.Redirect(fmt.Sprintf("/recipes/%d", id))
}

func isImageFile(data []byte) bool {
	contentType := http.DetectContentType(data)
	if strings.Index(contentType, "image/") != -1 {
		return  true
	}
	return false
}

func AddNoteToRecipePost(ctx *macaron.Context, form NoteForm) {
	id, err := strconv.ParseInt(ctx.Params("id"), 10, 64)
	if RedirectIfHasError(ctx, err) {
		return
	}

	db, err := models.OpenDatabase()
	if RedirectIfHasError(ctx, err) {
		return
	}

	note := models.Note {
		RecipeID: id,
		Note:     form.Note,
	}
	err = note.Create(db)
	if RedirectIfHasError(ctx, err) {
		return
	}

	ctx.Redirect(fmt.Sprintf("/recipes/%d", id))
}
<commit_msg>Attachments are now saved to the data/ folder<commit_after>package routers

import (
	"database/sql"
	"errors"
	"fmt"
	"gomp/models"
	"io/ioutil"
	"math/big"
	"mime/multipart"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"gopkg.in/macaron.v1"
)

// RecipeForm encapsulates user input on the Create and Edit recipe screens
type RecipeForm struct {
	Name             string `binding:"Required"`
	Description      string
	Directions       string
	Tags             []string
	IngredientAmount []string `form:"ingredient_amount"`
	IngredientUnit   []int64  `form:"ingredient_unit"`
	IngredientName   []string `form:"ingredient_name"`
}

// NoteForm encapsulates user input for a note on a recipe
type NoteForm struct {
	Note string
}

// AttachmentForm encapsulates user input for attaching a file (image) to a recipe
type AttachmentForm struct {
    FileName    string                `form:"file_name"`
    FileContent *multipart.FileHeader `form:"file_content"`
}

// GetRecipe handles retrieving and rendering a single recipe
func GetRecipe(ctx *macaron.Context) {
	id, err := strconv.ParseInt(ctx.Params("id"), 10, 64)
	if RedirectIfHasError(ctx, err) {
		return
	}

	db, err := models.OpenDatabase()
	if RedirectIfHasError(ctx, err) {
		return
	}
	defer db.Close()

	recipe := &models.Recipe{
		ID: id,
	}
	err = recipe.Read(db)
	if RedirectIfHasError(ctx, err) {
		return
	}
	if recipe == nil {
		NotFound(ctx)
		return
	}

	var notes = new(models.Notes)
	err = notes.List(db, id)
	if RedirectIfHasError(ctx, err) {
		return
	}

	ctx.Data["Recipe"] = recipe
	ctx.Data["Notes"] = notes
	ctx.HTML(http.StatusOK, "recipe/view")
}

// ListRecipes handles retrieving and rending a list of available recipes
func ListRecipes(ctx *macaron.Context) {
	query := ctx.Query("q")
	page := ctx.QueryInt("page")
	if page < 1 {
		page = 1
	}
	count := ctx.QueryInt("count")
	if count < 1 {
		count = 15
	}

	db, err := models.OpenDatabase()
	if RedirectIfHasError(ctx, err) {
		return
	}
	defer db.Close()

	recipes := new(models.Recipes)
	var total int
	if query == "" {
		total, err = recipes.List(db, page, count)
	} else {
		total, err = recipes.Find(db, query, page, count)
	}
	if RedirectIfHasError(ctx, err) {
		return
	}

	ctx.Data["Recipes"] = recipes
	ctx.Data["SearchQuery"] = query
	ctx.Data["ResultCount"] = total
	ctx.HTML(http.StatusOK, "recipe/list")
}

// CreateRecipe handles rendering the create recipe screen
func CreateRecipe(ctx *macaron.Context) {
	db, err := models.OpenDatabase()
	if RedirectIfHasError(ctx, err) {
		return
	}
	defer db.Close()

	units := new(models.Units)
	err = units.List(db)
	if RedirectIfHasError(ctx, err) {
		return
	}
	ctx.Data["Units"] = units
	ctx.HTML(http.StatusOK, "recipe/create")
}

// CreateRecipePost handles processing the supplied
// form input from the create recipe screen
func CreateRecipePost(ctx *macaron.Context, form RecipeForm) {
	db, err := models.OpenDatabase()
	if RedirectIfHasError(ctx, err) {
		return
	}
	defer db.Close()

	tags := make(models.Tags, len(form.Tags))
	for _, tag := range form.Tags {
		tags = append(tags, models.Tag(tag))
	}
	recipe := &models.Recipe{
		Name:        form.Name,
		Description: form.Description,
		Directions:  form.Directions,
		Tags:        tags,
	}

	// TODO: Checks that all the lengths match
	for i := 0; i < len(form.IngredientAmount); i++ {
		// Convert amount string into a floating point number
		amountRat := new(big.Rat)
		amountRat.SetString(form.IngredientAmount[i])
		amount, _ := amountRat.Float64()

		recipe.Ingredients = append(
			recipe.Ingredients,
			models.Ingredient{
				Name:          form.IngredientName[i],
				Amount:        amount,
				AmountDisplay: form.IngredientAmount[i],
				Unit:          models.Unit{ID: form.IngredientUnit[i]},
			})
	}

	err = recipe.Create(db)
	if RedirectIfHasError(ctx, err) {
		return
	}

	ctx.Redirect(fmt.Sprintf("/recipes/%d", recipe.ID))
}

// EditRecipe handles rendering the edit recipe screen
func EditRecipe(ctx *macaron.Context) {
	id, err := strconv.ParseInt(ctx.Params("id"), 10, 64)
	if RedirectIfHasError(ctx, err) {
		return
	}

	db, err := models.OpenDatabase()
	if RedirectIfHasError(ctx, err) {
		return
	}
	defer db.Close()

	recipe := &models.Recipe{ID: id}
	err = recipe.Read(db)
	if err == sql.ErrNoRows {
		NotFound(ctx)
		return
	}
	if RedirectIfHasError(ctx, err) {
		return
	}

	units := new(models.Units)
	err = units.List(db)
	if RedirectIfHasError(ctx, err) {
		return
	}

	ctx.Data["Recipe"] = recipe
	ctx.Data["Units"] = units
	ctx.HTML(http.StatusOK, "recipe/edit")
}

// EditRecipePost handles processing the supplied
// form input from the edit recipe screen
func EditRecipePost(ctx *macaron.Context, form RecipeForm) {
	id, err := strconv.ParseInt(ctx.Params("id"), 10, 64)
	if RedirectIfHasError(ctx, err) {
		return
	}

	db, err := models.OpenDatabase()
	if RedirectIfHasError(ctx, err) {
		return
	}
	defer db.Close()

	tags := make(models.Tags, len(form.Tags))
	for _, tag := range form.Tags {
		tags = append(tags, models.Tag(tag))
	}
	recipe := &models.Recipe{
		ID:          id,
		Name:        form.Name,
		Description: form.Description,
		Directions:  form.Directions,
		Tags:        tags,
	}

	// TODO: Checks that all the lengths match
	for i := 0; i < len(form.IngredientAmount); i++ {
		// Convert amount string into a floating point number
		amountRat := new(big.Rat)
		amountRat, ok := amountRat.SetString(form.IngredientAmount[i])
		var amount float64
		if ok {
			amount, ok = amountRat.Float64()
		}
		if !ok {
			RedirectIfHasError(
				ctx,
				errors.New("Could not convert supplied ingredient amount"))
		}

		recipe.Ingredients = append(
			recipe.Ingredients,
			models.Ingredient{
				Name:          form.IngredientName[i],
				Amount:        amount,
				AmountDisplay: form.IngredientAmount[i],
				RecipeID:      recipe.ID,
				Unit:          models.Unit{ID: form.IngredientUnit[i]},
			})
	}

	err = recipe.Update(db)
	if RedirectIfHasError(ctx, err) {
		return
	}

	ctx.Redirect(fmt.Sprintf("/recipes/%d", id))
}

// DeleteRecipe handles deleting the recipe with the given id
func DeleteRecipe(ctx *macaron.Context) {
	id, err := strconv.ParseInt(ctx.Params("id"), 10, 64)
	if RedirectIfHasError(ctx, err) {
		return
	}

	db, err := models.OpenDatabase()
	if RedirectIfHasError(ctx, err) {
		return
	}
	defer db.Close()

	recipe := &models.Recipe{ID: id}
	err = recipe.Delete(db)
	if RedirectIfHasError(ctx, err) {
		return
	}

	ctx.Redirect("/recipes")
}

func AttachToRecipePost(ctx *macaron.Context, form AttachmentForm) {
	id, err := strconv.ParseInt(ctx.Params("id"), 10, 64)
	if RedirectIfHasError(ctx, err) {
		return
	}

	uploadedFile, err := form.FileContent.Open()
	if RedirectIfHasError(ctx, err) {
		return
	}
	defer uploadedFile.Close()

	uploadedFileData, err := ioutil.ReadAll(uploadedFile)
	if RedirectIfHasError(ctx, err) {
		return
	}
	if ok := isImageFile(uploadedFileData); !ok {
		RedirectIfHasError(ctx, errors.New("Attachment must be an image"))
		return
	}

	destFolderPath := filepath.Join("data", "files", "recipe", strconv.FormatInt(id, 10), "images")
	err = os.MkdirAll(destFolderPath, os.ModePerm)
	if RedirectIfHasError(ctx, err) {
		return
	}

	destFilePath := filepath.Join(destFolderPath, form.FileName)
	destFile, err := os.Create(destFilePath)
	if RedirectIfHasError(ctx, err) {
		return
	}
	defer destFile.Close()
	_, err = destFile.Write(uploadedFileData)
	if RedirectIfHasError(ctx, err) {
		return
	}

	ctx.Redirect(fmt.Sprintf("/recipes/%d", id))
}

func isImageFile(data []byte) bool {
	contentType := http.DetectContentType(data)
	if strings.Index(contentType, "image/") != -1 {
		return  true
	}
	return false
}

func AddNoteToRecipePost(ctx *macaron.Context, form NoteForm) {
	id, err := strconv.ParseInt(ctx.Params("id"), 10, 64)
	if RedirectIfHasError(ctx, err) {
		return
	}

	db, err := models.OpenDatabase()
	if RedirectIfHasError(ctx, err) {
		return
	}

	note := models.Note {
		RecipeID: id,
		Note:     form.Note,
	}
	err = note.Create(db)
	if RedirectIfHasError(ctx, err) {
		return
	}

	ctx.Redirect(fmt.Sprintf("/recipes/%d", id))
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/gonuts/commander"
	gocfg "github.com/gonuts/config"
	"github.com/gonuts/flag"
	"github.com/hwaf/hwaf/hwaflib"
)

func hwaf_make_cmd_setup() *commander.Command {
	cmd := &commander.Command{
		Run:       hwaf_run_cmd_setup,
		UsageLine: "setup [options] <workarea>",
		Short:     "setup an existing workarea",
		Long: `
setup sets up an existing workarea.

ex:
 $ hwaf setup
 $ hwaf setup .
 $ hwaf setup my-work-area
 $ hwaf setup -p=/opt/sw/mana/mana-core/20121207 my-work-area
 $ hwaf setup -p=/path1:/path2 my-work-area
 $ hwaf setup -cfg=${HWAF_CFG}/usr.cfg my-work-area
`,
		Flag: *flag.NewFlagSet("hwaf-setup", flag.ExitOnError),
	}
	cmd.Flag.String("p", "", "List of paths to projects to setup against")
	cmd.Flag.String("cfg", "", "Path to a configuration file")
	cmd.Flag.String("cmtpkgdir", "src", "Directory under which to checkout packages")
	cmd.Flag.Bool("q", true, "only print error and warning messages, all other output will be suppressed")

	return cmd
}

func hwaf_run_cmd_setup(cmd *commander.Command, args []string) {
	var err error
	n := "hwaf-" + cmd.Name()
	dirname := "."
	switch len(args) {
	case 0:
		dirname = "."
	case 1:
		dirname = args[0]
	default:
		err = fmt.Errorf("%s: you need to give a directory name", n)
		handle_err(err)
	}

	dirname = os.ExpandEnv(dirname)
	dirname = filepath.Clean(dirname)

	quiet := cmd.Flag.Lookup("q").Value.Get().(bool)
	cfg_fname := cmd.Flag.Lookup("cfg").Value.Get().(string)
	cmt_pkgdir := cmd.Flag.Lookup("cmtpkgdir").Value.Get().(string)

	projdirs := []string{}
	const pathsep = string(os.PathListSeparator)
	for _, v := range strings.Split(cmd.Flag.Lookup("p").Value.Get().(string), pathsep) {
		if v != "" {
			v = os.ExpandEnv(v)
			v = filepath.Clean(v)
			projdirs = append(projdirs, v)
		}
	}

	if !quiet {
		fmt.Printf("%s: setup workarea [%s]...\n", n, dirname)
		fmt.Printf("%s: projects=%v\n", n, projdirs)
		if cfg_fname != "" {
			fmt.Printf("%s: cfg-file=%s\n", n, cfg_fname)
		}
	}

	if cfg_fname != "" && !path_exists(cfg_fname) {
		err = fmt.Errorf("configuration file [%s] does not exist (or is not readable)", cfg_fname)
		handle_err(err)
	}

	for _, projdir := range projdirs {
		if !path_exists(projdir) {
			err = fmt.Errorf("no such directory: [%s]", projdir)
			handle_err(err)
		}

		pinfo := filepath.Join(projdir, "project.info")
		if !path_exists(pinfo) {
			err = fmt.Errorf("no such file: [%s]", pinfo)
			handle_err(err)
		}
	}

	pwd, err := os.Getwd()
	handle_err(err)
	defer os.Chdir(pwd)

	err = os.Chdir(dirname)
	handle_err(err)

	if !quiet {
		fmt.Printf("%s: create local config...\n", n)
	}

	var lcfg *gocfg.Config
	lcfg_fname := "local.conf"

	// if the user provided a configuration file use that as a default
	if cfg_fname != "" && path_exists(cfg_fname) {
		lcfg, err = gocfg.ReadDefault(cfg_fname)
		handle_err(err)
	} else {
		if path_exists(lcfg_fname) {
			lcfg, err = gocfg.ReadDefault(lcfg_fname)
			handle_err(err)
		} else {
			lcfg = gocfg.NewDefault()
		}
	}

	section := "hwaf-cfg"
	if !lcfg.HasSection(section) && !lcfg.AddSection(section) {
		err = fmt.Errorf("%s: could not create section [%s] in file [%s]",
			n, section, lcfg_fname)
		handle_err(err)
	}

	// fetch a few informations from the first project.info
	cmtcfg := g_ctx.Cmtcfg()
	//projvers := time.Now().Format("20060102")
	if len(projdirs) > 0 {
		pinfo, err := hwaflib.NewProjectInfo(filepath.Join(projdirs[0], "project.info"))
		handle_err(err)
		cmtcfg, err = pinfo.Get("CMTCFG")
		handle_err(err)
	}

	for k, v := range map[string]string{
		"projects": strings.Join(projdirs, pathsep),
		"cmtpkgs":  cmt_pkgdir,
		"cmtcfg":   cmtcfg,
	} {
		if lcfg.HasOption(section, k) {
			lcfg.RemoveOption(section, k)
		}
		if !lcfg.AddOption(section, k, v) {
			err := fmt.Errorf("%s: could not add option [%s] to section [%s]",
				n, k, section,
			)
			handle_err(err)
		}
	}

	err = lcfg.WriteFile(lcfg_fname, 0600, "")
	handle_err(err)

	if !quiet {
		fmt.Printf("%s: setup workarea [%s]... [ok]\n", n, dirname)
	}
}

// EOF
<commit_msg>setup: add ability to define VARIANT from command-line. also: -cmtpkgdir -> -pkgdir<commit_after>package main

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/gonuts/commander"
	gocfg "github.com/gonuts/config"
	"github.com/gonuts/flag"
	"github.com/hwaf/hwaf/hwaflib"
)

func hwaf_make_cmd_setup() *commander.Command {
	cmd := &commander.Command{
		Run:       hwaf_run_cmd_setup,
		UsageLine: "setup [options] <workarea>",
		Short:     "setup an existing workarea",
		Long: `
setup sets up an existing workarea.

ex:
 $ hwaf setup
 $ hwaf setup .
 $ hwaf setup my-work-area
 $ hwaf setup -p=/opt/sw/mana/mana-core/20121207 my-work-area
 $ hwaf setup -p=/path1:/path2 my-work-area
 $ hwaf setup -cfg=${HWAF_CFG}/usr.cfg my-work-area
`,
		Flag: *flag.NewFlagSet("hwaf-setup", flag.ExitOnError),
	}
	cmd.Flag.String("p", "", "List of paths to projects to setup against")
	cmd.Flag.String("cfg", "", "Path to a configuration file")
	cmd.Flag.String("pkgdir", "src", "Directory under which to checkout packages")
	cmd.Flag.String("variant", "", "quadruplet (e.g. x86_64-slc6-gcc47-opt) identifying the target to build for")
	cmd.Flag.Bool("q", true, "only print error and warning messages, all other output will be suppressed")

	return cmd
}

func hwaf_run_cmd_setup(cmd *commander.Command, args []string) {
	var err error
	n := "hwaf-" + cmd.Name()
	dirname := "."
	switch len(args) {
	case 0:
		dirname = "."
	case 1:
		dirname = args[0]
	default:
		err = fmt.Errorf("%s: you need to give a directory name", n)
		handle_err(err)
	}

	dirname = os.ExpandEnv(dirname)
	dirname = filepath.Clean(dirname)

	quiet := cmd.Flag.Lookup("q").Value.Get().(bool)
	cfg_fname := cmd.Flag.Lookup("cfg").Value.Get().(string)
	pkgdir := cmd.Flag.Lookup("pkgdir").Value.Get().(string)
	variant := cmd.Flag.Lookup("variant").Value.Get().(string)

	projdirs := []string{}
	const pathsep = string(os.PathListSeparator)
	for _, v := range strings.Split(cmd.Flag.Lookup("p").Value.Get().(string), pathsep) {
		if v != "" {
			v = os.ExpandEnv(v)
			v = filepath.Clean(v)
			projdirs = append(projdirs, v)
		}
	}

	if !quiet {
		fmt.Printf("%s: setup workarea [%s]...\n", n, dirname)
		fmt.Printf("%s: projects=%v\n", n, projdirs)
		if cfg_fname != "" {
			fmt.Printf("%s: cfg-file=%s\n", n, cfg_fname)
		}
	}

	if cfg_fname != "" && !path_exists(cfg_fname) {
		err = fmt.Errorf("configuration file [%s] does not exist (or is not readable)", cfg_fname)
		handle_err(err)
	}

	for _, projdir := range projdirs {
		if !path_exists(projdir) {
			err = fmt.Errorf("no such directory: [%s]", projdir)
			handle_err(err)
		}

		pinfo := filepath.Join(projdir, "project.info")
		if !path_exists(pinfo) {
			err = fmt.Errorf("no such file: [%s]", pinfo)
			handle_err(err)
		}
	}

	pwd, err := os.Getwd()
	handle_err(err)
	defer os.Chdir(pwd)

	err = os.Chdir(dirname)
	handle_err(err)

	if !quiet {
		fmt.Printf("%s: create local config...\n", n)
	}

	var lcfg *gocfg.Config
	lcfg_fname := "local.conf"

	// if the user provided a configuration file use that as a default
	if cfg_fname != "" && path_exists(cfg_fname) {
		lcfg, err = gocfg.ReadDefault(cfg_fname)
		handle_err(err)
	} else {
		if path_exists(lcfg_fname) {
			lcfg, err = gocfg.ReadDefault(lcfg_fname)
			handle_err(err)
		} else {
			lcfg = gocfg.NewDefault()
		}
	}

	section := "hwaf-cfg"
	if !lcfg.HasSection(section) && !lcfg.AddSection(section) {
		err = fmt.Errorf("%s: could not create section [%s] in file [%s]",
			n, section, lcfg_fname)
		handle_err(err)
	}

	// fetch a few informations from the first project.info
	cmtcfg := g_ctx.Cmtcfg()
	//projvers := time.Now().Format("20060102")
	if len(projdirs) > 0 {
		pinfo, err := hwaflib.NewProjectInfo(filepath.Join(projdirs[0], "project.info"))
		handle_err(err)
		cmtcfg, err = pinfo.Get("CMTCFG")
		handle_err(err)
	}

	if variant != "" {
		cmtcfg = variant
	}

	for k, v := range map[string]string{
		"projects": strings.Join(projdirs, pathsep),
		"cmtpkgs":  pkgdir,
		"cmtcfg":   cmtcfg,
	} {
		if lcfg.HasOption(section, k) {
			lcfg.RemoveOption(section, k)
		}
		if !lcfg.AddOption(section, k, v) {
			err := fmt.Errorf("%s: could not add option [%s] to section [%s]",
				n, k, section,
			)
			handle_err(err)
		}
	}

	err = lcfg.WriteFile(lcfg_fname, 0600, "")
	handle_err(err)

	if !quiet {
		fmt.Printf("%s: setup workarea [%s]... [ok]\n", n, dirname)
	}
}

// EOF
<|endoftext|>


<commit_before>package rpc

import (
	"bytes"
	"compress/gzip"
	"io"
	"io/ioutil"
	"sync"
)

type compressor interface {
	Compress([]byte) ([]byte, error)
	Decompress([]byte) ([]byte, error)
}

type gzipCompressor struct {
	readerPool sync.Pool
	writerPool sync.Pool
}

var _ compressor = (*gzipCompressor)(nil)

func newGzipCompressor() *gzipCompressor {
	return &gzipCompressor{
		writerPool: sync.Pool{
			New: func() interface{} {
				return gzip.NewWriter(ioutil.Discard)
			},
		},
		readerPool: sync.Pool{
			New: func() interface{} {
				return new(gzip.Reader)
			},
		},
	}
}

func (c *gzipCompressor) getGzipWriter(writer io.Writer) (*gzip.Writer, func()) {
	gzipWriter := c.writerPool.Get().(*gzip.Writer)
	gzipWriter.Reset(writer)
	return gzipWriter, func() {
		c.writerPool.Put(gzipWriter)
	}
}
func (c *gzipCompressor) getGzipReader(reader io.Reader) (*gzip.Reader, func(), error) {
	gzipReader := c.readerPool.Get().(*gzip.Reader)
	if err := gzipReader.Reset(reader); err != nil {
		return nil, func() {}, err
	}
	return gzipReader, func() {
		c.readerPool.Put(gzipReader)
	}, nil
}

func (c *gzipCompressor) Compress(data []byte) ([]byte, error) {

	var buf bytes.Buffer
	writer, reclaim := c.getGzipWriter(&buf)
	defer reclaim()

	if _, err := writer.Write(data); err != nil {
		return nil, err
	}
	if err := writer.Flush(); err != nil {
		return nil, err
	}
	if err := writer.Close(); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func (c *gzipCompressor) Decompress(data []byte) ([]byte, error) {

	in := bytes.NewReader(data)
	reader, reclaim, err := c.getGzipReader(in)
	if err != nil {
		return nil, err
	}
	defer reclaim()

	var out bytes.Buffer
	if _, err := out.ReadFrom(reader); err != nil {
		return nil, err
	}
	if err := reader.Close(); err != nil {
		return nil, err
	}
	return out.Bytes(), nil
}

type compressorCacher struct {
	sync.Mutex
	algs map[CompressionType]compressor
}

func newCompressorCacher() *compressorCacher {
	return &compressorCacher{
		algs: make(map[CompressionType]compressor),
	}
}

func (c *compressorCacher) getCompressor(ctype CompressionType) compressor {
	c.Lock()
	defer c.Unlock()

	impl, ok := c.algs[ctype]
	if !ok {
		impl = ctype.NewCompressor()
		c.algs[ctype] = impl
	}
	return impl
}
<commit_msg>no flush<commit_after>package rpc

import (
	"bytes"
	"compress/gzip"
	"io"
	"io/ioutil"
	"sync"
)

type compressor interface {
	Compress([]byte) ([]byte, error)
	Decompress([]byte) ([]byte, error)
}

type gzipCompressor struct {
	readerPool sync.Pool
	writerPool sync.Pool
}

var _ compressor = (*gzipCompressor)(nil)

func newGzipCompressor() *gzipCompressor {
	return &gzipCompressor{
		writerPool: sync.Pool{
			New: func() interface{} {
				return gzip.NewWriter(ioutil.Discard)
			},
		},
		readerPool: sync.Pool{
			New: func() interface{} {
				return new(gzip.Reader)
			},
		},
	}
}

func (c *gzipCompressor) getGzipWriter(writer io.Writer) (*gzip.Writer, func()) {
	gzipWriter := c.writerPool.Get().(*gzip.Writer)
	gzipWriter.Reset(writer)
	return gzipWriter, func() {
		c.writerPool.Put(gzipWriter)
	}
}
func (c *gzipCompressor) getGzipReader(reader io.Reader) (*gzip.Reader, func(), error) {
	gzipReader := c.readerPool.Get().(*gzip.Reader)
	if err := gzipReader.Reset(reader); err != nil {
		return nil, func() {}, err
	}
	return gzipReader, func() {
		c.readerPool.Put(gzipReader)
	}, nil
}

func (c *gzipCompressor) Compress(data []byte) ([]byte, error) {

	var buf bytes.Buffer
	writer, reclaim := c.getGzipWriter(&buf)
	defer reclaim()

	if _, err := writer.Write(data); err != nil {
		return nil, err
	}
	if err := writer.Close(); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func (c *gzipCompressor) Decompress(data []byte) ([]byte, error) {

	in := bytes.NewReader(data)
	reader, reclaim, err := c.getGzipReader(in)
	if err != nil {
		return nil, err
	}
	defer reclaim()

	var out bytes.Buffer
	if _, err := out.ReadFrom(reader); err != nil {
		return nil, err
	}
	if err := reader.Close(); err != nil {
		return nil, err
	}
	return out.Bytes(), nil
}

type compressorCacher struct {
	sync.Mutex
	algs map[CompressionType]compressor
}

func newCompressorCacher() *compressorCacher {
	return &compressorCacher{
		algs: make(map[CompressionType]compressor),
	}
}

func (c *compressorCacher) getCompressor(ctype CompressionType) compressor {
	c.Lock()
	defer c.Unlock()

	impl, ok := c.algs[ctype]
	if !ok {
		impl = ctype.NewCompressor()
		c.algs[ctype] = impl
	}
	return impl
}
<|endoftext|>


<commit_before>// Copyright 2015 The Hugo Authors. 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 commands

import (
	"os"
	"runtime/pprof"

	"github.com/spf13/cobra"
)

var (
	benchmarkTimes int
	cpuProfilefile string
	memProfilefile string
)

var benchmarkCmd = &cobra.Command{
	Use:   "benchmark",
	Short: "Benchmark hugo by building a site a number of times.",
	Long: `Hugo can build a site many times over and analyze the running process
creating a benchmark.`,
}

func init() {
	initHugoBuilderFlags(benchmarkCmd)
	initBenchmarkBuildingFlags(benchmarkCmd)

	benchmarkCmd.Flags().StringVar(&cpuProfilefile, "cpuprofile", "", "path/filename for the CPU profile file")
	benchmarkCmd.Flags().StringVar(&memProfilefile, "memprofile", "", "path/filename for the memory profile file")

	benchmarkCmd.Flags().IntVarP(&benchmarkTimes, "count", "n", 13, "number of times to build the site")

	benchmarkCmd.RunE = benchmark
}

func benchmark(cmd *cobra.Command, args []string) error {
	if err := InitializeConfig(benchmarkCmd); err != nil {
		return err
	}

	if memProfilefile != "" {
		f, err := os.Create(memProfilefile)

		if err != nil {
			return err
		}
		for i := 0; i < benchmarkTimes; i++ {
			MainSite = nil
			_ = buildSite()
		}
		pprof.WriteHeapProfile(f)
		f.Close()

	} else {
		if cpuProfilefile == "" {
			cpuProfilefile = "/tmp/hugo-cpuprofile"
		}
		f, err := os.Create(cpuProfilefile)

		if err != nil {
			return err
		}

		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
		for i := 0; i < benchmarkTimes; i++ {
			MainSite = nil
			_ = buildSite()
		}
	}

	return nil

}
<commit_msg>Change hugo to Hugo in help text<commit_after>// Copyright 2015 The Hugo Authors. 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 commands

import (
	"os"
	"runtime/pprof"

	"github.com/spf13/cobra"
)

var (
	benchmarkTimes int
	cpuProfilefile string
	memProfilefile string
)

var benchmarkCmd = &cobra.Command{
	Use:   "benchmark",
	Short: "Benchmark Hugo by building a site a number of times.",
	Long: `Hugo can build a site many times over and analyze the running process
creating a benchmark.`,
}

func init() {
	initHugoBuilderFlags(benchmarkCmd)
	initBenchmarkBuildingFlags(benchmarkCmd)

	benchmarkCmd.Flags().StringVar(&cpuProfilefile, "cpuprofile", "", "path/filename for the CPU profile file")
	benchmarkCmd.Flags().StringVar(&memProfilefile, "memprofile", "", "path/filename for the memory profile file")

	benchmarkCmd.Flags().IntVarP(&benchmarkTimes, "count", "n", 13, "number of times to build the site")

	benchmarkCmd.RunE = benchmark
}

func benchmark(cmd *cobra.Command, args []string) error {
	if err := InitializeConfig(benchmarkCmd); err != nil {
		return err
	}

	if memProfilefile != "" {
		f, err := os.Create(memProfilefile)

		if err != nil {
			return err
		}
		for i := 0; i < benchmarkTimes; i++ {
			MainSite = nil
			_ = buildSite()
		}
		pprof.WriteHeapProfile(f)
		f.Close()

	} else {
		if cpuProfilefile == "" {
			cpuProfilefile = "/tmp/hugo-cpuprofile"
		}
		f, err := os.Create(cpuProfilefile)

		if err != nil {
			return err
		}

		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
		for i := 0; i < benchmarkTimes; i++ {
			MainSite = nil
			_ = buildSite()
		}
	}

	return nil

}
<|endoftext|>


<commit_before>package fleet

import (
	"encoding/json"
	"net"
	"net/http"
)

type Client struct {
	http *http.Client
}

type Unit struct {
	CurrentState string `json:"currentState"`
	DesiredState string `json:"desiredState"`
	MachineID    string `json:"machineID"`
	Name         string `json:"name"`
}

type UnitsResponse struct {
	Units []Unit `json:"units"`
}

func NewClient(path string) Client {
	dialFunc := func(string, string) (net.Conn, error) {
		return net.Dial("unix", path)
	}

	httpClient := http.Client{
		Transport: &http.Transport{
			Dial: dialFunc,
		},
	}

	return Client{&httpClient}
}

func (self *Client) Units() ([]Unit, error) {
	response, err := self.http.Get("http://sock/fleet/v1/units")
	if err != nil {
		return nil, err
	}

	decoder := json.NewDecoder(response.Body)
	var parsedResponse UnitsResponse
	err = decoder.Decode(&parsedResponse)
	if err != nil {
		return nil, err
	}

	return parsedResponse.Units, nil
}
<commit_msg>Add support for starting units via Fleet.<commit_after>package fleet

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net"
	"net/http"
)

type Client struct {
	http *http.Client
}

type Unit struct {
	CurrentState string       `json:"currentState,omitempty"`
	DesiredState string       `json:"desiredState"`
	MachineID    string       `json:"machineID,omitempty"`
	Name         string       `json:"name,omitempty"`
	Options      []UnitOption `json:"options"`
}

type UnitOption struct {
	Section string `json:"section"`
	Name    string `json:"name"`
	Value   string `json:"value"`
}

type UnitsResponse struct {
	Units []Unit `json:"units"`
}

func NewClient(path string) Client {
	dialFunc := func(string, string) (net.Conn, error) {
		return net.Dial("unix", path)
	}

	httpClient := http.Client{
		Transport: &http.Transport{
			Dial: dialFunc,
		},
	}

	return Client{&httpClient}
}

func (self *Client) Units() ([]Unit, error) {
	response, err := self.http.Get("http://sock/fleet/v1/units")
	if err != nil {
		return nil, err
	}

	decoder := json.NewDecoder(response.Body)
	var parsedResponse UnitsResponse
	err = decoder.Decode(&parsedResponse)
	if err != nil {
		return nil, err
	}

	return parsedResponse.Units, nil
}

func (self *Client) StartUnit(name string, options []UnitOption) (*http.Response, error) {
	url := fmt.Sprintf("http://sock/fleet/v1/units/%s@1.service", name)
	unit := Unit{
		DesiredState: "launched",
		Options:      options,
	}

	var b bytes.Buffer
	enc := json.NewEncoder(&b)
	enc.Encode(unit)

	r, err := http.NewRequest("PUT", url, &b)
	if err != nil {
		return nil, err
	}

	r.Header.Add("Content-Type", "application/json")

	return self.http.Do(r)
}
<|endoftext|>


<commit_before>// Copyright 2015, Timothy Bogdala <tdb@animal-machine.com>
// See the LICENSE file for more details.

package cubez

import (
	"math"

	m "github.com/tbogdala/cubez/math"
)

// CollisionPlane represents a plane in space for collisions.
type CollisionPlane struct {
	// Normal is the plane's normal vector
	Normal m.Vector3

	// Offset is the distance of the plane from the origin
	Offset m.Real
}

// CollisionCube represents a cube in space for collisions.
type CollisionCube struct {
	// Body is the RigidBody that is represented by this collision object.
	Body *RigidBody

	// Halfsize holds the cube's half-sizes along each of its local axes.
	HalfSize m.Vector3

	// Offset is the matrix that gives the offset of this primitive from Body.
	Offset m.Matrix3x4

	// transform is calculated by combining the Offset of the primitive with
	// the transform of the Body.
	// NOTE: this is calculated by calling CalculateDerivedData().
	transform m.Matrix3x4
}

/*
==================================================================================================
  COLLISION PLANE
==================================================================================================
*/

// NewCollisionPlane creates a new CollisionPlane object with the
// normal and offset specified.
func NewCollisionPlane(n m.Vector3, o m.Real) *CollisionPlane {
	plane := new(CollisionPlane)
	plane.Normal = n
	plane.Offset = o
	return plane
}

/*
==================================================================================================
  COLLISION CUBE
==================================================================================================
*/

// NewCollisionCube creates a new CollisionCube object with the dimensions specified
// for a given RigidBody. If a RigidBody is not specified, then a new RigidBody
// object is created for the CollisionCube.
func NewCollisionCube(optBody *RigidBody, halfSize m.Vector3) *CollisionCube {
	cube := new(CollisionCube)
	cube.Offset.SetIdentity()
	cube.HalfSize = halfSize
	cube.Body = optBody
	if cube.Body == nil {
		cube.Body = NewRigidBody()
	}
	return cube
}

// GetTransform returns a copy of the transform matrix for the collider object.
func (cube *CollisionCube) GetTransform() m.Matrix3x4 {
	return cube.transform
}

// CalculateDerivedData internal data from public data members.
//
// Constructs a transform matrix based on the RigidBody's transform and the
// collision object's offset.
func (cube *CollisionCube) CalculateDerivedData() {
	transform := cube.Body.GetTransform()
	cube.transform = transform.MulMatrix3x4(&cube.Offset)
}

// CheckAgainstHalfSpace does a collision test on a collision box and a plane representing
// a half-space (i.e. the normal of the plane points out of the half-space).
func (cube *CollisionCube) CheckAgainstHalfSpace(plane *CollisionPlane, existingContacts []*Contact) (bool, []*Contact) {
	// check for an intersection -- if there is none, then we can return
	if !intersectCubeAndHalfSpace(cube, plane) {
		return false, nil
	}

	// Now that we have an intersection, find the points of intersection. This can be
	// done by checking the eight vertices of the cube. If the cube is resting on a plane
	// or and edge it will be reported as four or two contact points.

	// setup an array of vertices
	var mults [8]m.Vector3
	mults[0] = m.Vector3{1.0, 1.0, 1.0}
	mults[1] = m.Vector3{-1.0, 1.0, 1.0}
	mults[2] = m.Vector3{1.0, -1.0, 1.0}
	mults[3] = m.Vector3{-1.0, -1.0, 1.0}
	mults[4] = m.Vector3{1.0, 1.0, -1.0}
	mults[5] = m.Vector3{-1.0, 1.0, -1.0}
	mults[6] = m.Vector3{1.0, -1.0, -1.0}
	mults[7] = m.Vector3{-1.0, -1.0, -1.0}

	contactDetected := false
	contacts := existingContacts
	for _, v := range mults {
		// calculate the position of the vertex
		v.ComponentProduct(&cube.HalfSize)
		vertexPos := cube.transform.MulVector3(&v)

		// calculate the distance from the plane
		vertexDistance := vertexPos.Dot(&plane.Normal)

		// compare it to the plane's distance
		if vertexDistance <= plane.Offset {
			// we have contact
			c := NewContact()

			// the contact point is halfway between the vertex and the plane --
			// we multiply the direction by half the separation distance and
			// add the vertex location.
			c.ContactPoint = plane.Normal
			c.ContactPoint.MulWith(vertexDistance - plane.Offset)
			c.ContactPoint.Add(&vertexPos)
			c.ContactNormal = plane.Normal
			c.Penetration = plane.Offset - vertexDistance
			c.Bodies[0] = cube.Body
			c.Bodies[1] = nil

			contacts = append(contacts, c)
			contactDetected = true

			// FIXME:
			// TODO: c.Friction and c.Restitution set here are test constants
			c.Friction = 0.9
			c.Restitution = 0.1
		}
	}

	return contactDetected, contacts
}

/*
==================================================================================================
  UTILITY
==================================================================================================
*/

// intersectCubeAndHalfSpace tests to see if a cube and plane intersect
func intersectCubeAndHalfSpace(cube *CollisionCube, plane *CollisionPlane) bool {
	// work out the projected radius of the cube onto the plane normal
	projectedRadius := transformToAxis(cube, &plane.Normal)

	// work out how far the box is from the origin
	axis := cube.transform.GetAxis(3)
	cubeDistance := plane.Normal.Dot(&axis) - projectedRadius

	// check for intersection
	return cubeDistance <= plane.Offset
}

func transformToAxis(cube *CollisionCube, axis *m.Vector3) m.Real {
	cubeAxisX := cube.transform.GetAxis(0)
	cubeAxisY := cube.transform.GetAxis(1)
	cubeAxisZ := cube.transform.GetAxis(2)

	return cube.HalfSize[0]*m.Real(math.Abs(float64(axis.Dot(&cubeAxisX)))) +
		cube.HalfSize[1]*m.Real(math.Abs(float64(axis.Dot(&cubeAxisY)))) +
		cube.HalfSize[2]*m.Real(math.Abs(float64(axis.Dot(&cubeAxisZ))))
}
<commit_msg>added basic ColliderSphere support<commit_after>// Copyright 2015, Timothy Bogdala <tdb@animal-machine.com>
// See the LICENSE file for more details.

package cubez

import (
	"math"

	m "github.com/tbogdala/cubez/math"
)

// CollisionPlane represents a plane in space for collisions but doesn't
// have an associated rigid body and is considered to be infinite.
// It's primarily useful for rerepresenting immovable world geometry like
// a giant ground plane.
type CollisionPlane struct {
	// Normal is the plane's normal vector
	Normal m.Vector3

	// Offset is the distance of the plane from the origin
	Offset m.Real
}

// CollisionCube is a rigid body that can be considered an axis-alligned cube
// for contact collision.
type CollisionCube struct {
	// Body is the RigidBody that is represented by this collision object.
	Body *RigidBody

	// Offset is the matrix that gives the offset of this primitive from Body.
	Offset m.Matrix3x4

	// transform is calculated by combining the Offset of the primitive with
	// the transform of the Body.
	// NOTE: this is calculated by calling CalculateDerivedData().
	transform m.Matrix3x4

	// Halfsize holds the cube's half-sizes along each of its local axes.
	HalfSize m.Vector3
}

// CollisionSphere is a rigid body that can be considered a sphere
// for collision detection.
type CollisionSphere struct {
	// Body is the RigidBody that is represented by this collision object.
	Body *RigidBody

	// Offset is the matrix that gives the offset of this primitive from Body.
	Offset m.Matrix3x4

	// transform is calculated by combining the Offset of the primitive with
	// the transform of the Body.
	// NOTE: this is calculated by calling CalculateDerivedData().
	transform m.Matrix3x4

	// Radius is the radius of the sphere.
	Radius m.Real
}

/*
==================================================================================================
  COLLISION PLANE
==================================================================================================
*/

// NewCollisionPlane creates a new CollisionPlane object with the
// normal and offset specified.
func NewCollisionPlane(n m.Vector3, o m.Real) *CollisionPlane {
	plane := new(CollisionPlane)
	plane.Normal = n
	plane.Offset = o
	return plane
}

/*
==================================================================================================
  COLLISION SPHERE
==================================================================================================
*/

// NewCollisionSphere creates a new CollisionSphere object with the radius specified
// for a given RigidBody. If a RigidBody is not specified, then a new RigidBody
// object is created for the new collider object.
func NewCollisionSphere(optBody *RigidBody, radius m.Real) *CollisionSphere {
	s := new(CollisionSphere)
	s.Offset.SetIdentity()
	s.Radius = radius
	s.Body = optBody
	if s.Body == nil {
		s.Body = NewRigidBody()
	}
	return s
}

// GetTransform returns a copy of the transform matrix for the collider object.
func (s *CollisionSphere) GetTransform() m.Matrix3x4 {
	return s.transform
}

// CalculateDerivedData internal data from public data members.
//
// Constructs a transform matrix based on the RigidBody's transform and the
// collision object's offset.
func (s *CollisionSphere) CalculateDerivedData() {
	transform := s.Body.GetTransform()
	s.transform = transform.MulMatrix3x4(&s.Offset)
}

// CheckAgainstHalfSpace does a collision test on a collision sphere and a plane representing
// a half-space (i.e. the normal of the plane points out of the half-space).
func (s *CollisionSphere) CheckAgainstHalfSpace(plane *CollisionPlane, existingContacts []*Contact) (bool, []*Contact) {
	// work out the distance from the origin
	positionAxis := s.transform.GetAxis(3)
	distance := plane.Normal.Dot(&positionAxis) - s.Radius

	// check for intersection
	if distance <= plane.Offset == false {
		return false, existingContacts
	}

	c := NewContact()
	c.ContactPoint = plane.Normal
	c.ContactPoint.MulWith(distance + s.Radius*-1.0)
	c.ContactPoint.Add(&positionAxis)
	c.ContactNormal = plane.Normal
	c.Penetration = -distance
	c.Bodies[0] = s.Body
	c.Bodies[1] = nil

	// FIXME:
	// TODO: c.Friction and c.Restitution set here are test constants
	c.Friction = 0.9
	c.Restitution = 0.1

	contacts := append(existingContacts, c)

	return true, contacts
}

/*
==================================================================================================
  COLLISION CUBE
==================================================================================================
*/

// NewCollisionCube creates a new CollisionCube object with the dimensions specified
// for a given RigidBody. If a RigidBody is not specified, then a new RigidBody
// object is created for the new collider object.
func NewCollisionCube(optBody *RigidBody, halfSize m.Vector3) *CollisionCube {
	cube := new(CollisionCube)
	cube.Offset.SetIdentity()
	cube.HalfSize = halfSize
	cube.Body = optBody
	if cube.Body == nil {
		cube.Body = NewRigidBody()
	}
	return cube
}

// GetTransform returns a copy of the transform matrix for the collider object.
func (cube *CollisionCube) GetTransform() m.Matrix3x4 {
	return cube.transform
}

// CalculateDerivedData internal data from public data members.
//
// Constructs a transform matrix based on the RigidBody's transform and the
// collision object's offset.
func (cube *CollisionCube) CalculateDerivedData() {
	transform := cube.Body.GetTransform()
	cube.transform = transform.MulMatrix3x4(&cube.Offset)
}

// CheckAgainstHalfSpace does a collision test on a collision box and a plane representing
// a half-space (i.e. the normal of the plane points out of the half-space).
func (cube *CollisionCube) CheckAgainstHalfSpace(plane *CollisionPlane, existingContacts []*Contact) (bool, []*Contact) {
	// check for an intersection -- if there is none, then we can return
	if !intersectCubeAndHalfSpace(cube, plane) {
		return false, nil
	}

	// Now that we have an intersection, find the points of intersection. This can be
	// done by checking the eight vertices of the cube. If the cube is resting on a plane
	// or and edge it will be reported as four or two contact points.

	// setup an array of vertices
	var mults [8]m.Vector3
	mults[0] = m.Vector3{1.0, 1.0, 1.0}
	mults[1] = m.Vector3{-1.0, 1.0, 1.0}
	mults[2] = m.Vector3{1.0, -1.0, 1.0}
	mults[3] = m.Vector3{-1.0, -1.0, 1.0}
	mults[4] = m.Vector3{1.0, 1.0, -1.0}
	mults[5] = m.Vector3{-1.0, 1.0, -1.0}
	mults[6] = m.Vector3{1.0, -1.0, -1.0}
	mults[7] = m.Vector3{-1.0, -1.0, -1.0}

	contactDetected := false
	contacts := existingContacts
	for _, v := range mults {
		// calculate the position of the vertex
		v.ComponentProduct(&cube.HalfSize)
		vertexPos := cube.transform.MulVector3(&v)

		// calculate the distance from the plane
		vertexDistance := vertexPos.Dot(&plane.Normal)

		// compare it to the plane's distance
		if vertexDistance <= plane.Offset {
			// we have contact
			c := NewContact()

			// the contact point is halfway between the vertex and the plane --
			// we multiply the direction by half the separation distance and
			// add the vertex location.
			c.ContactPoint = plane.Normal
			c.ContactPoint.MulWith(vertexDistance - plane.Offset)
			c.ContactPoint.Add(&vertexPos)
			c.ContactNormal = plane.Normal
			c.Penetration = plane.Offset - vertexDistance
			c.Bodies[0] = cube.Body
			c.Bodies[1] = nil

			contacts = append(contacts, c)
			contactDetected = true

			// FIXME:
			// TODO: c.Friction and c.Restitution set here are test constants
			c.Friction = 0.9
			c.Restitution = 0.1
		}
	}

	return contactDetected, contacts
}

/*
==================================================================================================
  UTILITY
==================================================================================================
*/

// intersectCubeAndHalfSpace tests to see if a cube and plane intersect
func intersectCubeAndHalfSpace(cube *CollisionCube, plane *CollisionPlane) bool {
	// work out the projected radius of the cube onto the plane normal
	projectedRadius := transformToAxis(cube, &plane.Normal)

	// work out how far the box is from the origin
	axis := cube.transform.GetAxis(3)
	cubeDistance := plane.Normal.Dot(&axis) - projectedRadius

	// check for intersection
	return cubeDistance <= plane.Offset
}

func transformToAxis(cube *CollisionCube, axis *m.Vector3) m.Real {
	cubeAxisX := cube.transform.GetAxis(0)
	cubeAxisY := cube.transform.GetAxis(1)
	cubeAxisZ := cube.transform.GetAxis(2)

	return cube.HalfSize[0]*m.Real(math.Abs(float64(axis.Dot(&cubeAxisX)))) +
		cube.HalfSize[1]*m.Real(math.Abs(float64(axis.Dot(&cubeAxisY)))) +
		cube.HalfSize[2]*m.Real(math.Abs(float64(axis.Dot(&cubeAxisZ))))
}
<|endoftext|>