<commit_before>package floats

import "math"

// InsufficientElements is an error type used by FindFirst
type InsufficientElements struct{}

// Error makes InsufficientElements satisfy the error interface
func (i *InsufficientElements) Error() string {
	return "floats: insufficient elements found"
}

// Add returns the element-wise sum of all the slices with the
// results stored in the first slice.
// For computational efficiency, it is assumed that all of
// the variadic arguments have the same length. If this is
// in doubt, EqLen can be called first.
func Add(dst []float64, slices ...[]float64) []float64 {
	if len(slices) == 0 {
		return nil
	}
	if len(dst) != len(slices[0]) {
		panic("floats: length of destination does not match length of the slices")
	}
	for _, slice := range slices {
		for j, val := range slice {
			dst[j] += val
		}
	}
	return dst
}

// AddConst adds a constant to all of the values in s
func AddConst(s []float64, c float64) {
	for i := range s {
		s[i] += c
	}
}

// ApplyFunc applies a function (math.Exp, math.Sin, etc.) to every element
// of the slice
func Apply(s []float64, f func(float64) float64) {
	for i, val := range s {
		s[i] = f(val)
	}
}

// Count counts the number of elements in s for which f is true
func Count(s []float64, f func(float64) bool) int {
	var n int
	for _, val := range s {
		if f(val) {
			n++
		}
	}
	return n
}

// Cumprod finds the cumulative product of the first i elements in
// s and puts them in place into the ith element of the
// destination. Panic will occur if lengths of do not match
func CumProd(dst, s []float64) []float64 {
	if len(dst) != len(s) {
		panic("floats: length of destination does not match length of the source")
	}
	dst[0] = s[0]
	for i := 1; i < len(s); i++ {
		dst[i] = dst[i-1] * s[i]
	}
	return dst
}

// Cumsum finds the cumulative sum of the first i elements in
// s and puts them in place into the ith element of the
// destination. Panic will occur if lengths of arguments do not match
func CumSum(dst, s []float64) []float64 {
	if len(dst) != len(s) {
		panic("floats: length of destination does not match length of the source")
	}
	dst[0] = s[0]
	for i := 1; i < len(s); i++ {
		dst[i] = dst[i-1] + s[i]
	}
	return dst
}

// Dot computes the dot product of s1 and s2, i.e.
// sum_{i = 1}^N s1[n]*s2[n]
// Panic will occur if lengths of arguments do not match
func Dot(s1, s2 []float64) float64 {
	if len(s1) != len(s2) {
		panic("floats: lengths of the slices do not match")
	}
	var sum float64
	for i, val := range s1 {
		sum += val * s2[i]
	}
	return sum
}

// Eq returns false if the slices have different lengths
// or if |s1[i] - s2[i]| > tol for any i.
func Eq(s1, s2 []float64, tol float64) bool {
	if len(s1) != len(s2) {
		return false
	}
	for i, val := range s1 {
		if math.Abs(s2[i]-val) > tol {
			return false
		}
	}
	return true
}

// Eqlen returns true if all of the slices have equal length,
// and false otherwise. Returns true if there are no input slices
func EqLen(slices ...[]float64) bool {
	// This length check is needed: http://play.golang.org/p/sdty6YiLhM
	if len(slices) == 0 {
		return true
	}
	l := len(slices[0])
	for i := 1; i < len(slices); i++ {
		if len(slices[i]) != l {
			return false
		}
	}
	return true
}

// Find finds the first k indices of  s for which
// the function f returns true and stores them in
// inds. If k < 0, all such elements are found.
// Find will reslice inds to have 0 length, and will append
// found indices to inds.
// If k > 0 and there are fewer than k elements in s satisfying f,
// all of the found elements will be returned along with an
// InsufficientElements error
func Find(inds []int, k int, s []float64, f func(float64) bool) ([]int, error) {

	// inds is also returned to allow for calling with nil

	// Reslice inds to have zero length
	inds = inds[:0]

	// If zero elements requested, can just return
	if k == 0 {
		return inds, nil
	}

	// If k < 0, return all of the found indices
	if k < 0 {
		for i, val := range s {
			if f(val) {
				inds = append(inds, i)
			}
		}
		return inds, nil
	}

	// Otherwise, find the first k elements
	nFound := 0
	for i, val := range s {
		if f(val) {
			inds = append(inds, i)
			nFound++
			if nFound == k {
				return inds, nil
			}
		}
	}
	// Finished iterating over the loop, which means k elements were not found
	return inds, &InsufficientElements{}
}

// LogSpan returns a set of N equally spaced points in log space between l and u,
// where N is equal to the len(dst). The first element of the
// resulting dst will be l and the final element of dst will be u.
// If len(dst) = 0, dst will be returned unchanged, and if len(dst) = 1, the
// only element of dst will become l
// Note that this call will return NaNs if either l or u are negative, and
// will return all zeros if l or u is zero.
func LogSpan(dst []float64, l, u float64) []float64 {
	Span(dst, math.Log(l), math.Log(u))
	Apply(dst, math.Exp)
	return dst
}

// Logsumexp returns the log of the sum of the exponentials of the values in s
func LogSumExp(s []float64) (lse float64) {
	// Want to do this in a numerically stable way which avoids
	// overflow and underflow
	// First, find the maximum value in the slice.
	maxval, _ := Max(s)
	if math.IsInf(maxval, 0) {
		// If it's infinity either way, the logsumexp will be infinity as well
		// returning now avoids NaNs
		return maxval
	}
	// Compute the sumexp part
	for _, val := range s {
		lse += math.Exp(val - maxval)
	}
	// Take the log and add back on the constant taken out
	lse = math.Log(lse) + maxval
	return lse
}

// Max returns the maximum value in the slice and the location of
// the maximum value. If the input slice is empty, zero is returned
// as the minimum value and -1 is returned as the index.
func Max(s []float64) (max float64, ind int) {
	if len(s) == 0 {
		return max, -1 // Ind is -1 to make clear it's not the zeroth index.
	}
	max = s[0]
	ind = 0
	for i, val := range s {
		if val > max {
			max = val
			ind = i
		}
	}
	return max, ind
}

// Min returns the minimum value in the slice and the index of
// the minimum value. If the input slice is empty, zero is returned
// as the minimum value and -1 is returned as the index.
func Min(s []float64) (min float64, ind int) {
	if len(s) == 0 {
		return min, -1 // Ind is -1 to make clear it's not the zeroth index.
	}
	min = s[0]
	ind = 0
	for i, val := range s {
		if val < min {
			min = val
			ind = i
		}
	}
	return min, ind
}

// Norm returns the L norm of the slice S, defined as
// (sum_{i=1}^N s[i]^N)^{1/N}
// Special cases:
// L = math.Inf(1) gives the maximum value
// Does not correctly compute the zero norm
func Norm(s []float64, L float64) (norm float64) {
	// Should this complain if L is not positive?
	// Should this be done in log space for better numerical stability?
	//	would be more cost
	//	maybe only if L is high?
	if L == 2 {
		for _, val := range s {
			norm += val * val
		}
		return math.Pow(norm, 0.5)
	}
	if L == 1 {
		for _, val := range s {
			norm += math.Abs(val)
		}
		return norm
	}
	if math.IsInf(L, 1) {
		norm, _ = Max(s)
		return norm
	}
	for _, val := range s {
		norm += math.Pow(math.Abs(val), L)
	}
	return math.Pow(norm, 1/L)
}

// Prod returns the product of the elements of the slice
// Returns 1 if len(s) = 0
func Prod(s []float64) (prod float64) {
	prod = 1
	for _, val := range s {
		prod *= val
	}
	return prod
}

// Scale multiplies every element in s by c
func Scale(s []float64, c float64) {
	for i := range s {
		s[i] *= c
	}
}

// Span returns a set of N equally spaced points between l and u, where N
// is equal to the length of the destination. The first element of the destination
// is l, the final element of the destination is u.
// If len(dst) = 0, dst will be returned unchanged, and if len(dst) = 1, the
// only element of dst will become l
func Span(dst []float64, l, u float64) []float64 {
	n := len(dst)
	if n < 2 {
		panic("floats: destination must have length >1")
	}
	step := (u - l) / float64(n-1)
	for i := range dst {
		dst[i] = l + step*float64(i)
	}
	return dst
}

// Sub subtracts, element-wise, the first argument from the second. Assumes
// the lengths of s and t match (can be tested with EqLen)
func Sub(s, t []float64) {
	if len(s) != len(t) {
		panic("floats: length of the slices do not match")
	}
	for i, val := range t {
		s[i] -= val
	}
}

// SubDst subtracts, element-wise, the first argument from the second and
// store the result in destination. Assumes the lengths of s and t match
// (can be tested with EqLen)
func SubDst(dst, s, t []float64) []float64 {
	if len(s) != len(t) {
		panic("floats: length of subtractor and subtractee do not match")
	}
	if len(dst) != len(s) {
		if dst == nil {
			dst = make([]float64, len(s))
		} else {
			panic("floats: length of destination does not match length of subtractor")
		}
	}
	for i, val := range t {
		dst[i] = s[i] - val
	}
	return dst
}

// Sum returns the sum of the elements of the slice
func Sum(s []float64) (sum float64) {
	for _, val := range s {
		sum += val
	}
	return
}
<commit_msg>Added panic comment for Span and LogSpan<commit_after>package floats

import "math"

// InsufficientElements is an error type used by FindFirst
type InsufficientElements struct{}

// Error makes InsufficientElements satisfy the error interface
func (i *InsufficientElements) Error() string {
	return "floats: insufficient elements found"
}

// Add returns the element-wise sum of all the slices with the
// results stored in the first slice.
// For computational efficiency, it is assumed that all of
// the variadic arguments have the same length. If this is
// in doubt, EqLen can be called first.
func Add(dst []float64, slices ...[]float64) []float64 {
	if len(slices) == 0 {
		return nil
	}
	if len(dst) != len(slices[0]) {
		panic("floats: length of destination does not match length of the slices")
	}
	for _, slice := range slices {
		for j, val := range slice {
			dst[j] += val
		}
	}
	return dst
}

// AddConst adds a constant to all of the values in s
func AddConst(s []float64, c float64) {
	for i := range s {
		s[i] += c
	}
}

// ApplyFunc applies a function (math.Exp, math.Sin, etc.) to every element
// of the slice
func Apply(s []float64, f func(float64) float64) {
	for i, val := range s {
		s[i] = f(val)
	}
}

// Count counts the number of elements in s for which f is true
func Count(s []float64, f func(float64) bool) int {
	var n int
	for _, val := range s {
		if f(val) {
			n++
		}
	}
	return n
}

// Cumprod finds the cumulative product of the first i elements in
// s and puts them in place into the ith element of the
// destination. Panic will occur if lengths of do not match
func CumProd(dst, s []float64) []float64 {
	if len(dst) != len(s) {
		panic("floats: length of destination does not match length of the source")
	}
	dst[0] = s[0]
	for i := 1; i < len(s); i++ {
		dst[i] = dst[i-1] * s[i]
	}
	return dst
}

// Cumsum finds the cumulative sum of the first i elements in
// s and puts them in place into the ith element of the
// destination. Panic will occur if lengths of arguments do not match
func CumSum(dst, s []float64) []float64 {
	if len(dst) != len(s) {
		panic("floats: length of destination does not match length of the source")
	}
	dst[0] = s[0]
	for i := 1; i < len(s); i++ {
		dst[i] = dst[i-1] + s[i]
	}
	return dst
}

// Dot computes the dot product of s1 and s2, i.e.
// sum_{i = 1}^N s1[n]*s2[n]
// Panic will occur if lengths of arguments do not match
func Dot(s1, s2 []float64) float64 {
	if len(s1) != len(s2) {
		panic("floats: lengths of the slices do not match")
	}
	var sum float64
	for i, val := range s1 {
		sum += val * s2[i]
	}
	return sum
}

// Eq returns false if the slices have different lengths
// or if |s1[i] - s2[i]| > tol for any i.
func Eq(s1, s2 []float64, tol float64) bool {
	if len(s1) != len(s2) {
		return false
	}
	for i, val := range s1 {
		if math.Abs(s2[i]-val) > tol {
			return false
		}
	}
	return true
}

// Eqlen returns true if all of the slices have equal length,
// and false otherwise. Returns true if there are no input slices
func EqLen(slices ...[]float64) bool {
	// This length check is needed: http://play.golang.org/p/sdty6YiLhM
	if len(slices) == 0 {
		return true
	}
	l := len(slices[0])
	for i := 1; i < len(slices); i++ {
		if len(slices[i]) != l {
			return false
		}
	}
	return true
}

// Find finds the first k indices of  s for which
// the function f returns true and stores them in
// inds. If k < 0, all such elements are found.
// Find will reslice inds to have 0 length, and will append
// found indices to inds.
// If k > 0 and there are fewer than k elements in s satisfying f,
// all of the found elements will be returned along with an
// InsufficientElements error
func Find(inds []int, k int, s []float64, f func(float64) bool) ([]int, error) {

	// inds is also returned to allow for calling with nil

	// Reslice inds to have zero length
	inds = inds[:0]

	// If zero elements requested, can just return
	if k == 0 {
		return inds, nil
	}

	// If k < 0, return all of the found indices
	if k < 0 {
		for i, val := range s {
			if f(val) {
				inds = append(inds, i)
			}
		}
		return inds, nil
	}

	// Otherwise, find the first k elements
	nFound := 0
	for i, val := range s {
		if f(val) {
			inds = append(inds, i)
			nFound++
			if nFound == k {
				return inds, nil
			}
		}
	}
	// Finished iterating over the loop, which means k elements were not found
	return inds, &InsufficientElements{}
}

// LogSpan returns a set of N equally spaced points in log space between l and u,
// where N is equal to the len(dst). The first element of the
// resulting dst will be l and the final element of dst will be u.
// Panics if len(dst) < 2
// Note that this call will return NaNs if either l or u are negative, and
// will return all zeros if l or u is zero.
func LogSpan(dst []float64, l, u float64) []float64 {
	Span(dst, math.Log(l), math.Log(u))
	Apply(dst, math.Exp)
	return dst
}

// Logsumexp returns the log of the sum of the exponentials of the values in s
func LogSumExp(s []float64) (lse float64) {
	// Want to do this in a numerically stable way which avoids
	// overflow and underflow
	// First, find the maximum value in the slice.
	maxval, _ := Max(s)
	if math.IsInf(maxval, 0) {
		// If it's infinity either way, the logsumexp will be infinity as well
		// returning now avoids NaNs
		return maxval
	}
	// Compute the sumexp part
	for _, val := range s {
		lse += math.Exp(val - maxval)
	}
	// Take the log and add back on the constant taken out
	lse = math.Log(lse) + maxval
	return lse
}

// Max returns the maximum value in the slice and the location of
// the maximum value. If the input slice is empty, zero is returned
// as the minimum value and -1 is returned as the index.
func Max(s []float64) (max float64, ind int) {
	if len(s) == 0 {
		return max, -1 // Ind is -1 to make clear it's not the zeroth index.
	}
	max = s[0]
	ind = 0
	for i, val := range s {
		if val > max {
			max = val
			ind = i
		}
	}
	return max, ind
}

// Min returns the minimum value in the slice and the index of
// the minimum value. If the input slice is empty, zero is returned
// as the minimum value and -1 is returned as the index.
func Min(s []float64) (min float64, ind int) {
	if len(s) == 0 {
		return min, -1 // Ind is -1 to make clear it's not the zeroth index.
	}
	min = s[0]
	ind = 0
	for i, val := range s {
		if val < min {
			min = val
			ind = i
		}
	}
	return min, ind
}

// Norm returns the L norm of the slice S, defined as
// (sum_{i=1}^N s[i]^N)^{1/N}
// Special cases:
// L = math.Inf(1) gives the maximum value
// Does not correctly compute the zero norm
func Norm(s []float64, L float64) (norm float64) {
	// Should this complain if L is not positive?
	// Should this be done in log space for better numerical stability?
	//	would be more cost
	//	maybe only if L is high?
	if L == 2 {
		for _, val := range s {
			norm += val * val
		}
		return math.Pow(norm, 0.5)
	}
	if L == 1 {
		for _, val := range s {
			norm += math.Abs(val)
		}
		return norm
	}
	if math.IsInf(L, 1) {
		norm, _ = Max(s)
		return norm
	}
	for _, val := range s {
		norm += math.Pow(math.Abs(val), L)
	}
	return math.Pow(norm, 1/L)
}

// Prod returns the product of the elements of the slice
// Returns 1 if len(s) = 0
func Prod(s []float64) (prod float64) {
	prod = 1
	for _, val := range s {
		prod *= val
	}
	return prod
}

// Scale multiplies every element in s by c
func Scale(s []float64, c float64) {
	for i := range s {
		s[i] *= c
	}
}

// Span returns a set of N equally spaced points between l and u, where N
// is equal to the length of the destination. The first element of the destination
// is l, the final element of the destination is u.
// Panics if len(dst) < 2
func Span(dst []float64, l, u float64) []float64 {
	n := len(dst)
	if n < 2 {
		panic("floats: destination must have length >1")
	}
	step := (u - l) / float64(n-1)
	for i := range dst {
		dst[i] = l + step*float64(i)
	}
	return dst
}

// Sub subtracts, element-wise, the first argument from the second. Assumes
// the lengths of s and t match (can be tested with EqLen)
func Sub(s, t []float64) {
	if len(s) != len(t) {
		panic("floats: length of the slices do not match")
	}
	for i, val := range t {
		s[i] -= val
	}
}

// SubDst subtracts, element-wise, the first argument from the second and
// store the result in destination. Assumes the lengths of s and t match
// (can be tested with EqLen)
func SubDst(dst, s, t []float64) []float64 {
	if len(s) != len(t) {
		panic("floats: length of subtractor and subtractee do not match")
	}
	if len(dst) != len(s) {
		if dst == nil {
			dst = make([]float64, len(s))
		} else {
			panic("floats: length of destination does not match length of subtractor")
		}
	}
	for i, val := range t {
		dst[i] = s[i] - val
	}
	return dst
}

// Sum returns the sum of the elements of the slice
func Sum(s []float64) (sum float64) {
	for _, val := range s {
		sum += val
	}
	return
}
<|endoftext|>


<commit_before>package gb

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

// Build builds each of pkgs in succession. If pkg is a command, then the results of build include
// linking the final binary into pkg.Context.Bindir().
func Build(pkgs ...*Package) error {
	targets := make(map[string]PkgTarget)
	roots := make([]Target, 0, len(pkgs))
	for _, pkg := range pkgs {
		target := buildPackage(targets, pkg)
		if pkg.isMain() {
			target = Ld(pkg, target.(PkgTarget))
		}
		roots = append(roots, target)
	}
	for _, root := range roots {
		if err := root.Result(); err != nil {
			return err
		}
	}
	return nil
}

// buildPackage returns a Target repesenting the results of compiling
// pkg and its dependencies.
func buildPackage(targets map[string]PkgTarget, pkg *Package) Target {
	if target, ok := targets[pkg.ImportPath]; ok {
		// already compiled
		return target
	}
	Debugf("buildPackage: %v", pkg.ImportPath)

	deps := buildDependencies(targets, pkg)
	target := Compile(pkg, deps...)
	targets[pkg.ImportPath] = target
	return target
}

// buildDependencies returns a []Target representing the results of
// compiling the dependencies of pkg.
func buildDependencies(targets map[string]PkgTarget, pkg *Package) []Target {
	var deps []Target
	for _, i := range pkg.Imports() {
		deps = append(deps, buildPackage(targets, i))
	}
	return deps
}

// Compile returns a Target representing all the steps required to build a go package.
func Compile(pkg *Package, deps ...Target) PkgTarget {
	if !pkg.Stale {
		return cachedPackage(pkg)
	}
	var gofiles []string
	gofiles = append(gofiles, pkg.GoFiles...)
	var objs []ObjTarget
	if len(pkg.CgoFiles) > 0 {
		return errTarget{
			fmt.Errorf("%v: cgo not suppored, see https://github.com/constabulary/gb/issues/12", pkg.ImportPath),
		}
	}
	objs = append(objs, Gc(pkg, gofiles, deps...))
	for _, sfile := range pkg.SFiles {
		objs = append(objs, Asm(pkg, sfile))
	}
	if pkg.Complete() {
		return Install(pkg, objs[0].(PkgTarget))
	}
	return Install(pkg, Pack(pkg, objs...))
}

// ObjTarget represents a compiled Go object (.5, .6, etc)
type ObjTarget interface {
	Target

	// Objfile is the name of the file that is produced if the target is successful.
	Objfile() string
}

type gc struct {
	target
	pkg     *Package
	gofiles []string
}

func (g *gc) String() string {
	return fmt.Sprintf("compile %v", g.pkg)
}

func (g *gc) compile() error {
	t0 := time.Now()
	if g.pkg.Scope != "test" {
		// only log compilation message if not in test scope
		Infof(g.pkg.ImportPath)
	}
	includes := g.pkg.ctx.IncludePaths()
	importpath := g.pkg.ImportPath
	if g.pkg.Scope == "test" && g.pkg.ExtraIncludes != "" {
		// TODO(dfc) gross
		includes = append([]string{g.pkg.ExtraIncludes}, includes...)
	}
	for i := range g.gofiles {
		g.gofiles[i], _ = filepath.Rel(g.pkg.ctx.Projectdir(), filepath.Join(g.pkg.Dir, g.gofiles[i]))
	}
	err := g.pkg.ctx.tc.Gc(includes, importpath, g.pkg.ctx.Projectdir(), g.Objfile(), g.gofiles, g.pkg.Complete())
	g.pkg.ctx.Record("compile", time.Since(t0))
	return err
}

func (g *gc) Objfile() string {
	return objfile(g.pkg)
}

func (g *gc) Pkgfile() string {
	return g.Objfile()
}

type objpkgtarget interface {
	ObjTarget
	Pkgfile() string // implements PkgTarget
}

// Gc returns a Target representing the result of compiling a set of gofiles with the Context specified gc Compiler.
func Gc(pkg *Package, gofiles []string, deps ...Target) objpkgtarget {
	gc := gc{
		pkg:     pkg,
		gofiles: gofiles,
	}
	gc.target = newTarget(gc.compile, deps...)
	return &gc
}

// PkgTarget represents a Target that produces a pkg (.a) file.
type PkgTarget interface {
	Target

	// Pkgfile returns the name of the file that is produced by the Target if successful.
	Pkgfile() string
}

type pack struct {
	c   chan error
	pkg *Package
}

func (p *pack) Result() error {
	err := <-p.c
	p.c <- err
	return err
}

func (p *pack) pack(objs ...ObjTarget) {
	afiles := make([]string, 0, len(objs))
	for _, obj := range objs {
		err := obj.Result()
		if err != nil {
			p.c <- err
			return
		}
		// pkg.a (compiled Go code) is always first
		afiles = append(afiles, obj.Objfile())
	}
	t0 := time.Now()
	err := p.pkg.ctx.tc.Pack(afiles...)
	p.pkg.ctx.Record("pack", time.Since(t0))
	p.c <- err
}

func (p *pack) Pkgfile() string {
	return objfile(p.pkg)
}

// Pack returns a Target representing the result of packing a
// set of Context specific object files into an archive.
func Pack(pkg *Package, deps ...ObjTarget) PkgTarget {
	pack := pack{
		c:   make(chan error, 1),
		pkg: pkg,
	}
	go pack.pack(deps...)
	return &pack
}

type asm struct {
	target
	pkg   *Package
	sfile string
}

func (a *asm) String() string {
	return fmt.Sprintf("asm %v", a.sfile)
}

func (a *asm) Objfile() string {
	return filepath.Join(a.pkg.ctx.workdir, a.pkg.ImportPath, stripext(a.sfile)+".6")
}

func (a *asm) asm() error {
	t0 := time.Now()
	err := a.pkg.ctx.tc.Asm(a.pkg.Dir, a.Objfile(), filepath.Join(a.pkg.Dir, a.sfile))
	a.pkg.ctx.Record("asm", time.Since(t0))
	return err
}

// Asm returns a Target representing the result of assembling
// sfile with the Context specified asssembler.
func Asm(pkg *Package, sfile string) ObjTarget {
	asm := asm{
		pkg:   pkg,
		sfile: sfile,
	}
	asm.target = newTarget(asm.asm)
	return &asm
}

type ld struct {
	target
	pkg   *Package
	afile PkgTarget
}

func (l *ld) link() error {
	t0 := time.Now()
	target := binfile(l.pkg)
	if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil {
		return err
	}

	includes := l.pkg.ctx.IncludePaths()
	if l.pkg.Scope == "test" && l.pkg.ExtraIncludes != "" {
		// TODO(dfc) gross
		includes = append([]string{l.pkg.ExtraIncludes}, includes...)
		target += ".test"
	}
	err := l.pkg.ctx.tc.Ld(includes, l.pkg.ctx.ldflags, target, l.afile.Pkgfile())
	l.pkg.ctx.Record("link", time.Since(t0))
	return err
}

// Ld returns a Target representing the result of linking a
// Package into a command with the Context provided linker.
func Ld(pkg *Package, afile PkgTarget) Target {
	ld := ld{
		pkg:   pkg,
		afile: afile,
	}
	ld.target = newTarget(ld.link, afile)
	return &ld
}

func stripext(path string) string {
	ext := filepath.Ext(path)
	return path[:len(ext)]
}

// objfile returns the name of the object file for this package
func objfile(pkg *Package) string {
	return filepath.Join(objdir(pkg), objname(pkg))
}

// objdir returns the destination for object files compiled for this Package.
func objdir(pkg *Package) string {
	switch pkg.Scope {
	case "test":
		ip := strings.TrimSuffix(filepath.FromSlash(pkg.ImportPath), "_test")
		return filepath.Join(pkg.ctx.workdir, ip, "_test", filepath.Dir(filepath.FromSlash(pkg.ImportPath)))
	default:
		return filepath.Join(pkg.ctx.workdir, filepath.Dir(filepath.FromSlash(pkg.ImportPath)))
	}
}

func objname(pkg *Package) string {
	switch pkg.Name {
	case "main":
		return filepath.Join(filepath.Base(filepath.FromSlash(pkg.ImportPath)), "main.a")
	default:
		return filepath.Base(filepath.FromSlash(pkg.ImportPath)) + ".a"
	}
}

func binfile(pkg *Package) string {
	var target string
	switch pkg.Scope {
	case "test":
		target = filepath.Join(pkg.ctx.workdir, filepath.FromSlash(pkg.ImportPath), "_test", binname(pkg))
	default:
		target = filepath.Join(pkg.ctx.Bindir(), binname(pkg))
	}
	if pkg.ctx.GOOS == "windows" {
		target += ".exe"
	}
	return target
}

func binname(pkg *Package) string {
	switch {
	case pkg.Name == "main":
		return filepath.Base(filepath.FromSlash(pkg.ImportPath))
	case pkg.Scope == "test":
		return pkg.Name
	default:
		panic("binname called with non main package: " + pkg.ImportPath)
	}
}
<commit_msg>Return better error from gc.Pack if used incorrectly<commit_after>package gb

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

// Build builds each of pkgs in succession. If pkg is a command, then the results of build include
// linking the final binary into pkg.Context.Bindir().
func Build(pkgs ...*Package) error {
	targets := make(map[string]PkgTarget)
	roots := make([]Target, 0, len(pkgs))
	for _, pkg := range pkgs {
		target := buildPackage(targets, pkg)
		if pkg.isMain() {
			target = Ld(pkg, target.(PkgTarget))
		}
		roots = append(roots, target)
	}
	for _, root := range roots {
		if err := root.Result(); err != nil {
			return err
		}
	}
	return nil
}

// buildPackage returns a Target repesenting the results of compiling
// pkg and its dependencies.
func buildPackage(targets map[string]PkgTarget, pkg *Package) Target {
	if target, ok := targets[pkg.ImportPath]; ok {
		// already compiled
		return target
	}
	Debugf("buildPackage: %v", pkg.ImportPath)

	deps := buildDependencies(targets, pkg)
	target := Compile(pkg, deps...)
	targets[pkg.ImportPath] = target
	return target
}

// buildDependencies returns a []Target representing the results of
// compiling the dependencies of pkg.
func buildDependencies(targets map[string]PkgTarget, pkg *Package) []Target {
	var deps []Target
	for _, i := range pkg.Imports() {
		deps = append(deps, buildPackage(targets, i))
	}
	return deps
}

// Compile returns a Target representing all the steps required to build a go package.
func Compile(pkg *Package, deps ...Target) PkgTarget {
	if !pkg.Stale {
		return cachedPackage(pkg)
	}
	var gofiles []string
	gofiles = append(gofiles, pkg.GoFiles...)
	var objs []ObjTarget
	if len(pkg.CgoFiles) > 0 {
		return errTarget{
			fmt.Errorf("%v: cgo not suppored, see https://github.com/constabulary/gb/issues/12", pkg.ImportPath),
		}
	}
	objs = append(objs, Gc(pkg, gofiles, deps...))
	for _, sfile := range pkg.SFiles {
		objs = append(objs, Asm(pkg, sfile))
	}
	if pkg.Complete() {
		return Install(pkg, objs[0].(PkgTarget))
	}
	return Install(pkg, Pack(pkg, objs...))
}

// ObjTarget represents a compiled Go object (.5, .6, etc)
type ObjTarget interface {
	Target

	// Objfile is the name of the file that is produced if the target is successful.
	Objfile() string
}

type gc struct {
	target
	pkg     *Package
	gofiles []string
}

func (g *gc) String() string {
	return fmt.Sprintf("compile %v", g.pkg)
}

func (g *gc) compile() error {
	t0 := time.Now()
	if g.pkg.Scope != "test" {
		// only log compilation message if not in test scope
		Infof(g.pkg.ImportPath)
	}
	includes := g.pkg.ctx.IncludePaths()
	importpath := g.pkg.ImportPath
	if g.pkg.Scope == "test" && g.pkg.ExtraIncludes != "" {
		// TODO(dfc) gross
		includes = append([]string{g.pkg.ExtraIncludes}, includes...)
	}
	for i := range g.gofiles {
		g.gofiles[i], _ = filepath.Rel(g.pkg.ctx.Projectdir(), filepath.Join(g.pkg.Dir, g.gofiles[i]))
	}
	err := g.pkg.ctx.tc.Gc(includes, importpath, g.pkg.ctx.Projectdir(), g.Objfile(), g.gofiles, g.pkg.Complete())
	g.pkg.ctx.Record("compile", time.Since(t0))
	return err
}

func (g *gc) Objfile() string {
	return objfile(g.pkg)
}

func (g *gc) Pkgfile() string {
	return g.Objfile()
}

type objpkgtarget interface {
	ObjTarget
	Pkgfile() string // implements PkgTarget
}

// Gc returns a Target representing the result of compiling a set of gofiles with the Context specified gc Compiler.
func Gc(pkg *Package, gofiles []string, deps ...Target) objpkgtarget {
	gc := gc{
		pkg:     pkg,
		gofiles: gofiles,
	}
	gc.target = newTarget(gc.compile, deps...)
	return &gc
}

// PkgTarget represents a Target that produces a pkg (.a) file.
type PkgTarget interface {
	Target

	// Pkgfile returns the name of the file that is produced by the Target if successful.
	Pkgfile() string
}

type pack struct {
	c   chan error
	pkg *Package
}

func (p *pack) Result() error {
	err := <-p.c
	p.c <- err
	return err
}

func (p *pack) pack(objs ...ObjTarget) {
	afiles := make([]string, 0, len(objs))
	for _, obj := range objs {
		err := obj.Result()
		if err != nil {
			p.c <- err
			return
		}
		// pkg.a (compiled Go code) is always first
		afiles = append(afiles, obj.Objfile())
	}
	t0 := time.Now()
	err := p.pkg.ctx.tc.Pack(afiles...)
	p.pkg.ctx.Record("pack", time.Since(t0))
	p.c <- err
}

func (p *pack) Pkgfile() string {
	return objfile(p.pkg)
}

// Pack returns a Target representing the result of packing a
// set of Context specific object files into an archive.
func Pack(pkg *Package, deps ...ObjTarget) PkgTarget {
	if len(deps) < 2 {
		return errTarget{
			fmt.Errorf("Pack requires at least two arguments: %v", deps)}
	}
	pack := pack{
		c:   make(chan error, 1),
		pkg: pkg,
	}
	go pack.pack(deps...)
	return &pack
}

type asm struct {
	target
	pkg   *Package
	sfile string
}

func (a *asm) String() string {
	return fmt.Sprintf("asm %v", a.sfile)
}

func (a *asm) Objfile() string {
	return filepath.Join(a.pkg.ctx.workdir, a.pkg.ImportPath, stripext(a.sfile)+".6")
}

func (a *asm) asm() error {
	t0 := time.Now()
	err := a.pkg.ctx.tc.Asm(a.pkg.Dir, a.Objfile(), filepath.Join(a.pkg.Dir, a.sfile))
	a.pkg.ctx.Record("asm", time.Since(t0))
	return err
}

// Asm returns a Target representing the result of assembling
// sfile with the Context specified asssembler.
func Asm(pkg *Package, sfile string) ObjTarget {
	asm := asm{
		pkg:   pkg,
		sfile: sfile,
	}
	asm.target = newTarget(asm.asm)
	return &asm
}

type ld struct {
	target
	pkg   *Package
	afile PkgTarget
}

func (l *ld) link() error {
	t0 := time.Now()
	target := binfile(l.pkg)
	if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil {
		return err
	}

	includes := l.pkg.ctx.IncludePaths()
	if l.pkg.Scope == "test" && l.pkg.ExtraIncludes != "" {
		// TODO(dfc) gross
		includes = append([]string{l.pkg.ExtraIncludes}, includes...)
		target += ".test"
	}
	err := l.pkg.ctx.tc.Ld(includes, l.pkg.ctx.ldflags, target, l.afile.Pkgfile())
	l.pkg.ctx.Record("link", time.Since(t0))
	return err
}

// Ld returns a Target representing the result of linking a
// Package into a command with the Context provided linker.
func Ld(pkg *Package, afile PkgTarget) Target {
	ld := ld{
		pkg:   pkg,
		afile: afile,
	}
	ld.target = newTarget(ld.link, afile)
	return &ld
}

func stripext(path string) string {
	ext := filepath.Ext(path)
	return path[:len(ext)]
}

// objfile returns the name of the object file for this package
func objfile(pkg *Package) string {
	return filepath.Join(objdir(pkg), objname(pkg))
}

// objdir returns the destination for object files compiled for this Package.
func objdir(pkg *Package) string {
	switch pkg.Scope {
	case "test":
		ip := strings.TrimSuffix(filepath.FromSlash(pkg.ImportPath), "_test")
		return filepath.Join(pkg.ctx.workdir, ip, "_test", filepath.Dir(filepath.FromSlash(pkg.ImportPath)))
	default:
		return filepath.Join(pkg.ctx.workdir, filepath.Dir(filepath.FromSlash(pkg.ImportPath)))
	}
}

func objname(pkg *Package) string {
	switch pkg.Name {
	case "main":
		return filepath.Join(filepath.Base(filepath.FromSlash(pkg.ImportPath)), "main.a")
	default:
		return filepath.Base(filepath.FromSlash(pkg.ImportPath)) + ".a"
	}
}

func binfile(pkg *Package) string {
	var target string
	switch pkg.Scope {
	case "test":
		target = filepath.Join(pkg.ctx.workdir, filepath.FromSlash(pkg.ImportPath), "_test", binname(pkg))
	default:
		target = filepath.Join(pkg.ctx.Bindir(), binname(pkg))
	}
	if pkg.ctx.GOOS == "windows" {
		target += ".exe"
	}
	return target
}

func binname(pkg *Package) string {
	switch {
	case pkg.Name == "main":
		return filepath.Base(filepath.FromSlash(pkg.ImportPath))
	case pkg.Scope == "test":
		return pkg.Name
	default:
		panic("binname called with non main package: " + pkg.ImportPath)
	}
}
<|endoftext|>


<commit_before>package leetcode

import "testing"

func Test_strStr(t *testing.T) {
	haystacks := []string{"", "abc", "abcdabcdf"}
	needles := []string{"", "b", "abcdf"}
	results := []int{0, 1, 4}
	for i := 0; i < len(haystacks); i++ {
		if r := strStr(haystacks[i], needles[i]); r != results[i] {
			t.Log(haystacks[i], needles[i], results[i], "output is:", r)
			t.Fail()
		}
	}
}
<commit_msg>enhance test<commit_after>package leetcode

import "testing"

func Test_strStr(t *testing.T) {
	haystacks := []string{"a", "", "abc", "abcdabcdf"}
	needles := []string{"b", "", "b", "abcdf"}
	results := []int{-1, 0, 1, 4}
	for i := 0; i < len(haystacks); i++ {
		if r := strStr(haystacks[i], needles[i]); r != results[i] {
			t.Log(haystacks[i], needles[i], results[i], "output is:", r)
			t.Fail()
		}
	}
}
<|endoftext|>


<commit_before>package workers

import (
	"encoding/json"
	"fmt"
	"strconv"
	"time"

	"github.com/pborman/uuid"
	"github.com/stripe/stripe-go"

	"github.com/ghmeier/bloodlines/gateways"
	"github.com/ghmeier/bloodlines/handlers"
	bmodels "github.com/ghmeier/bloodlines/models"
	b "github.com/ghmeier/bloodlines/workers"
	cg "github.com/ghmeier/coinage/gateways"
	"github.com/ghmeier/coinage/helpers"
	towncenter "github.com/jakelong95/TownCenter/gateways"
	covenant "github.com/yuderekyu/covenant/gateways"
	cmodels "github.com/yuderekyu/covenant/models"
)

var Events = map[string]bool{
	"invoice.created":           true,
	"invoice.payment_failed":    true,
	"invoice.payment_succeeded": true,
	"invoice.send":              true,
	"invoice.updated":           true,
}

type eventWorker struct {
	RB       gateways.RabbitI
	B        gateways.Bloodlines
	C        covenant.Covenant
	TC       towncenter.TownCenterI
	Stripe   cg.Stripe
	Customer *helpers.Customer
}

func NewEvent(ctx *handlers.GatewayContext) b.Worker {
	worker := &eventWorker{
		RB:       ctx.Rabbit,
		B:        ctx.Bloodlines,
		C:        ctx.Covenant,
		TC:       ctx.TownCenter,
		Stripe:   ctx.Stripe,
		Customer: helpers.NewCustomer(ctx.Sql, ctx.Stripe, ctx.TownCenter),
	}

	return &b.BaseWorker{
		HandleFunc: b.HandleFunc(worker.handle),
		RB:         ctx.Rabbit,
	}
}

func (e *eventWorker) handle(body []byte) {
	var event stripe.Event
	err := json.Unmarshal(body, &event)
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	switch event.Type {
	case "invoice.created":
		e.invoiceCreate(&event)

	}
}

//invoiceCreate is dispatched when an invoice for a subscription is created
func (e *eventWorker) invoiceCreate(event *stripe.Event) {
	buf, _ := event.Data.Raw.MarshalJSON()

	invoice := &stripe.Invoice{}
	err := invoice.UnmarshalJSON(buf)
	if err != nil {
		fmt.Println("ERROR: unable to parse invoice json")
		fmt.Println(err.Error())
	}

	customerID := event.Data.Obj["customer"].(string)
	for _, item := range invoice.Lines.Values {
		if item.Type != "subscription" {
			continue
		}

		err = e.createOrder(customerID, item)
		if err != nil {
			fmt.Println(err.Error())
		}
	}
}

func (e *eventWorker) createOrder(customerID string, subscription *stripe.InvoiceLine) error {
	subscribed, err := e.Customer.GetSubscribedFromConnected(customerID)
	if err != nil {
		return err
	}
	if subscribed == nil {
		return fmt.Errorf("No subscribed for connectedId %s", customerID)
	}
	customer, err := e.Customer.GetByCustomerID(subscribed.CustomerID)
	if err != nil {
		return err
	}
	if customer == nil {
		return fmt.Errorf("No customer for customerId %s", subscribed.CustomerID)
	}

	itemID := uuid.Parse(subscription.Plan.Meta["itemId"])
	userID := customer.UserID
	user, err := e.TC.GetUser(userID)
	fmt.Printf("Received invoice for %s, on for %s\n", userID, itemID)
	_, err = e.B.ActivateTrigger("invoice_create", &bmodels.Receipt{
		UserID: userID,
		Values: map[string]string{
			"first_name": user.FirstName,
			"last_name":  user.LastName,
			"amount":     strconv.Itoa(int(subscription.Amount)),
			"date":       time.Now().Local().Format("Mon Jan 2 15:04:05 MST 2006"),
		},
	})
	if err != nil {
		fmt.Println(err.Error())
	}

	r := &cmodels.RequestOrder{
		UserID:    userID,
		ItemID:    itemID,
		NextOrder: time.Unix(subscription.Period.End, 0),
	}
	_, err = e.C.NewOrder(r)
	return err
}
<commit_msg>Fix format of price<commit_after>package workers

import (
	"encoding/json"
	"fmt"
	"time"

	"github.com/pborman/uuid"
	"github.com/stripe/stripe-go"

	"github.com/ghmeier/bloodlines/gateways"
	"github.com/ghmeier/bloodlines/handlers"
	bmodels "github.com/ghmeier/bloodlines/models"
	b "github.com/ghmeier/bloodlines/workers"
	cg "github.com/ghmeier/coinage/gateways"
	"github.com/ghmeier/coinage/helpers"
	towncenter "github.com/jakelong95/TownCenter/gateways"
	covenant "github.com/yuderekyu/covenant/gateways"
	cmodels "github.com/yuderekyu/covenant/models"
)

var Events = map[string]bool{
	"invoice.created":           true,
	"invoice.payment_failed":    true,
	"invoice.payment_succeeded": true,
	"invoice.send":              true,
	"invoice.updated":           true,
}

type eventWorker struct {
	RB       gateways.RabbitI
	B        gateways.Bloodlines
	C        covenant.Covenant
	TC       towncenter.TownCenterI
	Stripe   cg.Stripe
	Customer *helpers.Customer
}

func NewEvent(ctx *handlers.GatewayContext) b.Worker {
	worker := &eventWorker{
		RB:       ctx.Rabbit,
		B:        ctx.Bloodlines,
		C:        ctx.Covenant,
		TC:       ctx.TownCenter,
		Stripe:   ctx.Stripe,
		Customer: helpers.NewCustomer(ctx.Sql, ctx.Stripe, ctx.TownCenter),
	}

	return &b.BaseWorker{
		HandleFunc: b.HandleFunc(worker.handle),
		RB:         ctx.Rabbit,
	}
}

func (e *eventWorker) handle(body []byte) {
	var event stripe.Event
	err := json.Unmarshal(body, &event)
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	switch event.Type {
	case "invoice.created":
		e.invoiceCreate(&event)

	}
}

//invoiceCreate is dispatched when an invoice for a subscription is created
func (e *eventWorker) invoiceCreate(event *stripe.Event) {
	buf, _ := event.Data.Raw.MarshalJSON()

	invoice := &stripe.Invoice{}
	err := invoice.UnmarshalJSON(buf)
	if err != nil {
		fmt.Println("ERROR: unable to parse invoice json")
		fmt.Println(err.Error())
	}

	customerID := event.Data.Obj["customer"].(string)
	for _, item := range invoice.Lines.Values {
		if item.Type != "subscription" {
			continue
		}

		err = e.createOrder(customerID, item)
		if err != nil {
			fmt.Println(err.Error())
		}
	}
}

func (e *eventWorker) createOrder(customerID string, subscription *stripe.InvoiceLine) error {
	subscribed, err := e.Customer.GetSubscribedFromConnected(customerID)
	if err != nil {
		return err
	}
	if subscribed == nil {
		return fmt.Errorf("No subscribed for connectedId %s", customerID)
	}
	customer, err := e.Customer.GetByCustomerID(subscribed.CustomerID)
	if err != nil {
		return err
	}
	if customer == nil {
		return fmt.Errorf("No customer for customerId %s", subscribed.CustomerID)
	}

	itemID := uuid.Parse(subscription.Plan.Meta["itemId"])
	userID := customer.UserID
	user, err := e.TC.GetUser(userID)
	fmt.Printf("Received invoice for %s, on for %s\n", userID, itemID)
	_, err = e.B.ActivateTrigger("invoice_create", &bmodels.Receipt{
		UserID: userID,
		Values: map[string]string{
			"first_name": user.FirstName,
			"last_name":  user.LastName,
			"amount":     fmt.Sprintf("%.2f", int(subscription.Amount)/10),
			"date":       time.Now().Local().Format("Mon Jan 2 15:04:05 MST 2006"),
		},
	})
	if err != nil {
		fmt.Println(err.Error())
	}

	r := &cmodels.RequestOrder{
		UserID:    userID,
		ItemID:    itemID,
		NextOrder: time.Unix(subscription.Period.End, 0),
	}
	_, err = e.C.NewOrder(r)
	return err
}
<|endoftext|>


<commit_before>// Copyright 2015 Brian J. Downs
//
// 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 openweathermap

import (
	"fmt"
	"io"
	"net/url"
	"strconv"
	"strings"
)

// ForecastSys area population
type ForecastSys struct {
	Population int `json:"population"`
}

// Temperature holds returned termperate sure stats
type Temperature struct {
	Day   float64 `json:"day"`
	Min   float64 `json:"min"`
	Max   float64 `json:"max"`
	Night float64 `json:"night"`
	Eve   float64 `json:"eve"`
	Morn  float64 `json:"morn"`
}

// City data for given location
type City struct {
	ID         int         `json:"id"`
	Name       string      `json:"name"`
	Coord      Coordinates `json:"coord"`
	Country    string      `json:"country"`
	Population int         `json:"population"`
	Sys        ForecastSys `json:"sys"`
}

type ForecastWeather interface {
	DailyByName(location string, days int) error
	DailyByCoordinates(location *Coordinates, days int) error
	DailyByID(id, days int) error
}

// json served by OWM API can take different forms, so all of them must be matched
// by corresponding data type and unmarshall method
type ForecastWeatherJson interface {
	Decode(r io.Reader) error
}

type ForecastWeatherData struct {
	Unit    string
	Lang    string
	Key     string
	baseURL string
	*Settings
	ForecastWeatherJson
}

// NewForecast returns a new HistoricalWeatherData pointer with
// the supplied arguments.
func NewForecast(forecastType, unit, lang, key string, options ...Option) (*ForecastWeatherData, error) {
	unitChoice := strings.ToUpper(unit)
	langChoice := strings.ToUpper(lang)

	if forecastType != "16" && forecastType != "5" {
		return nil, errForecastUnavailable
	}

	if !ValidDataUnit(unitChoice) {
		return nil, errUnitUnavailable
	}

	if !ValidLangCode(langChoice) {
		return nil, errLangUnavailable
	}

	settings := NewSettings()
	if err := setOptions(settings, options); err != nil {
		return nil, err
	}

	var err error
	k, err := setKey(key)
	if err != nil {
		return nil, err
	}
	forecastData := ForecastWeatherData{
		Unit:     DataUnits[unitChoice],
		Lang:     langChoice,
		Key:      k,
		Settings: settings,
	}

	if forecastType == "16" {
		forecastData.baseURL = forecast16Base
		forecastData.ForecastWeatherJson = &Forecast16WeatherData{}
	} else {
		forecastData.baseURL = forecast5Base
		forecastData.ForecastWeatherJson = &Forecast5WeatherData{}
	}

	return &forecastData, nil
}

// DailyByName will provide a forecast for the location given for the
// number of days given.
func (f *ForecastWeatherData) DailyByName(location string, days int) error {
	response, err := f.client.Get(fmt.Sprintf(f.baseURL, f.Key, fmt.Sprintf("%s=%s", "q", url.QueryEscape(location)), f.Unit, f.Lang, days))
	if err != nil {
		return err
	}
	defer response.Body.Close()

	return f.ForecastWeatherJson.Decode(response.Body)
}

// DailyByCoordinates will provide a forecast for the coordinates ID give
// for the number of days given.
func (f *ForecastWeatherData) DailyByCoordinates(location *Coordinates, days int) error {
	response, err := f.client.Get(fmt.Sprintf(f.baseURL, f.Key, fmt.Sprintf("lat=%f&lon=%f", location.Latitude, location.Longitude), f.Unit, f.Lang, days))
	if err != nil {
		return err
	}
	defer response.Body.Close()

	return f.ForecastWeatherJson.Decode(response.Body)
}

// DailyByID will provide a forecast for the location ID give for the
// number of days given.
func (f *ForecastWeatherData) DailyByID(id, days int) error {
	response, err := f.client.Get(fmt.Sprintf(f.baseURL, f.Key, fmt.Sprintf("%s=%s", "id", strconv.Itoa(id)), f.Unit, f.Lang, days))
	if err != nil {
		return err
	}
	defer response.Body.Close()

	return f.ForecastWeatherJson.Decode(response.Body)
}
<commit_msg>added DailyByZip<commit_after>// Copyright 2015 Brian J. Downs
//
// 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 openweathermap

import (
	"fmt"
	"io"
	"net/url"
	"strconv"
	"strings"
)

// ForecastSys area population
type ForecastSys struct {
	Population int `json:"population"`
}

// Temperature holds returned termperate sure stats
type Temperature struct {
	Day   float64 `json:"day"`
	Min   float64 `json:"min"`
	Max   float64 `json:"max"`
	Night float64 `json:"night"`
	Eve   float64 `json:"eve"`
	Morn  float64 `json:"morn"`
}

// City data for given location
type City struct {
	ID         int         `json:"id"`
	Name       string      `json:"name"`
	Coord      Coordinates `json:"coord"`
	Country    string      `json:"country"`
	Population int         `json:"population"`
	Sys        ForecastSys `json:"sys"`
}

type ForecastWeather interface {
	DailyByName(location string, days int) error
	DailyByCoordinates(location *Coordinates, days int) error
	DailyByID(id, days int) error
}

// json served by OWM API can take different forms, so all of them must be matched
// by corresponding data type and unmarshall method
type ForecastWeatherJson interface {
	Decode(r io.Reader) error
}

type ForecastWeatherData struct {
	Unit    string
	Lang    string
	Key     string
	baseURL string
	*Settings
	ForecastWeatherJson
}

// NewForecast returns a new HistoricalWeatherData pointer with
// the supplied arguments.
func NewForecast(forecastType, unit, lang, key string, options ...Option) (*ForecastWeatherData, error) {
	unitChoice := strings.ToUpper(unit)
	langChoice := strings.ToUpper(lang)

	if forecastType != "16" && forecastType != "5" {
		return nil, errForecastUnavailable
	}

	if !ValidDataUnit(unitChoice) {
		return nil, errUnitUnavailable
	}

	if !ValidLangCode(langChoice) {
		return nil, errLangUnavailable
	}

	settings := NewSettings()
	if err := setOptions(settings, options); err != nil {
		return nil, err
	}

	var err error
	k, err := setKey(key)
	if err != nil {
		return nil, err
	}
	forecastData := ForecastWeatherData{
		Unit:     DataUnits[unitChoice],
		Lang:     langChoice,
		Key:      k,
		Settings: settings,
	}

	if forecastType == "16" {
		forecastData.baseURL = forecast16Base
		forecastData.ForecastWeatherJson = &Forecast16WeatherData{}
	} else {
		forecastData.baseURL = forecast5Base
		forecastData.ForecastWeatherJson = &Forecast5WeatherData{}
	}

	return &forecastData, nil
}

// DailyByName will provide a forecast for the location given for the
// number of days given.
func (f *ForecastWeatherData) DailyByName(location string, days int) error {
	response, err := f.client.Get(fmt.Sprintf(f.baseURL, f.Key, fmt.Sprintf("%s=%s", "q", url.QueryEscape(location)), f.Unit, f.Lang, days))
	if err != nil {
		return err
	}
	defer response.Body.Close()

	return f.ForecastWeatherJson.Decode(response.Body)
}

// DailyByCoordinates will provide a forecast for the coordinates ID give
// for the number of days given.
func (f *ForecastWeatherData) DailyByCoordinates(location *Coordinates, days int) error {
	response, err := f.client.Get(fmt.Sprintf(f.baseURL, f.Key, fmt.Sprintf("lat=%f&lon=%f", location.Latitude, location.Longitude), f.Unit, f.Lang, days))
	if err != nil {
		return err
	}
	defer response.Body.Close()

	return f.ForecastWeatherJson.Decode(response.Body)
}

// DailyByID will provide a forecast for the location ID give for the
// number of days given.
func (f *ForecastWeatherData) DailyByID(id, days int) error {
	response, err := f.client.Get(fmt.Sprintf(f.baseURL, f.Key, fmt.Sprintf("%s=%s", "id", strconv.Itoa(id)), f.Unit, f.Lang, days))
	if err != nil {
		return err
	}
	defer response.Body.Close()

	return f.ForecastWeatherJson.Decode(response.Body)
}

// DailyByZip will provide a forecast for the provided zip code.
func (f *ForecastWeatherData) DailyByZip(zip int, countryCode string, days int) error {
	response, err := f.client.Get(fmt.Sprintf(f.baseURL, f.Key, fmt.Sprintf("zip=%d,%s", zip, countryCode), f.Unit, f.Lang, days))
	if err != nil {
		return err
	}
	defer response.Body.Close()

	return f.ForecastWeatherJson.Decode(response.Body)
}
<|endoftext|>


<commit_before>package jap

import (
	"crypto/rsa"
	"encoding/json"
	"net/http"
	"net/url"

	"golang.org/x/net/context"
	"golang.org/x/net/context/ctxhttp"
	"golang.org/x/net/trace"
)

// GoogleLogin returns a handler which attempts to extract a client ID from its
// context and sends the information to Google to validate the user. If no
// client ID exists in the context it panics.
//
// The handler may return one of the following errors:
//
//   400 BadRequest          – If the id_token form param is missing.
//   408 RequestTimeout      – If the contexts deadline was exceeded.
//   500 InternalServerError – If the upstream returns a response we don't understand.
//   502 BadGateway          – If an upstream service fails to respond for another reason.
func GoogleLogin(ctx context.Context, key *rsa.PrivateKey) func(http.ResponseWriter, *http.Request) {
	cid, ok := CIDFromContext(ctx)
	if !ok {
		panic("No client ID found in the context")
	}

	return func(w http.ResponseWriter, r *http.Request) {
		tr := trace.New("jap.tokenlogin", r.URL.Path)
		defer tr.Finish()
		ctx = trace.NewContext(ctx, tr)

		idtoken := r.FormValue("id_token")
		if idtoken == "" {
			writeError(ctx, w, "id_token login param missing", http.StatusBadRequest)
			return
		}

		// TODO: We should skip the Post request and verify the JWT signature with
		//       Google's public key.
		tr.LazyPrintf("Starting Google token validation…")
		inforesp, err := ctxhttp.PostForm(ctx, http.DefaultClient,
			"https://www.googleapis.com/oauth2/v3/tokeninfo",
			url.Values{
				"id_token": []string{idtoken},
			},
		)
		if err != nil {
			switch err {
			case context.DeadlineExceeded:
				writeError(ctx, w, "The deadline was exceeded", http.StatusRequestTimeout)
			default:
				writeError(ctx, w, "Upstream request failed", http.StatusBadGateway)
			}
			return
		}
		if inforesp.StatusCode < 200 || inforesp.StatusCode > 299 {
			writeError(ctx, w, "Unexpected response from upstream", http.StatusInternalServerError)
			return
		}
		tr.LazyPrintf("Received claims from Google…")
		claims := struct {
			Aud          string `json:"aud"`
			Email        string `json:"email"`
			HostedDomain string `json:"hd"`
			Locale       string `json:"locale"`
		}{}
		if err := json.NewDecoder(inforesp.Body).Decode(&claims); err != nil {
			writeError(ctx, w, "Error decoding upstream response", http.StatusInternalServerError)
			return
		}
		if claims.Aud != cid {
			writeError(ctx, w, "Error decoding upstream response", http.StatusInternalServerError)
			return
		}
	}
}
<commit_msg>Fix resource leak with http clients<commit_after>package jap

import (
	"crypto/rsa"
	"encoding/json"
	"net/http"
	"net/url"

	"golang.org/x/net/context"
	"golang.org/x/net/context/ctxhttp"
	"golang.org/x/net/trace"
)

// GoogleLogin returns a handler which attempts to extract a client ID from its
// context and sends the information to Google to validate the user. If no
// client ID exists in the context it panics.
//
// The handler may return one of the following errors:
//
//   400 BadRequest          – If the id_token form param is missing.
//   408 RequestTimeout      – If the contexts deadline was exceeded.
//   500 InternalServerError – If the upstream returns a response we don't understand.
//   502 BadGateway          – If an upstream service fails to respond for another reason.
func GoogleLogin(ctx context.Context, key *rsa.PrivateKey) func(http.ResponseWriter, *http.Request) {
	cid, ok := CIDFromContext(ctx)
	if !ok {
		panic("No client ID found in the context")
	}

	return func(w http.ResponseWriter, r *http.Request) {
		tr := trace.New("jap.tokenlogin", r.URL.Path)
		defer tr.Finish()
		ctx = trace.NewContext(ctx, tr)

		idtoken := r.FormValue("id_token")
		if idtoken == "" {
			writeError(ctx, w, "id_token login param missing", http.StatusBadRequest)
			return
		}

		// TODO: We should skip the Post request and verify the JWT signature with
		//       Google's public key.
		tr.LazyPrintf("Starting Google token validation…")
		inforesp, err := ctxhttp.PostForm(ctx, http.DefaultClient,
			"https://www.googleapis.com/oauth2/v3/tokeninfo",
			url.Values{
				"id_token": []string{idtoken},
			},
		)
		if err != nil {
			switch err {
			case context.DeadlineExceeded:
				writeError(ctx, w, "The deadline was exceeded", http.StatusRequestTimeout)
			default:
				writeError(ctx, w, "Upstream request failed", http.StatusBadGateway)
			}
			return
		}
		defer inforesp.Body.Close()
		if inforesp.StatusCode < 200 || inforesp.StatusCode > 299 {
			writeError(ctx, w, "Unexpected response from upstream", http.StatusInternalServerError)
			return
		}
		tr.LazyPrintf("Received claims from Google…")
		claims := struct {
			Aud          string `json:"aud"`
			Email        string `json:"email"`
			HostedDomain string `json:"hd"`
			Locale       string `json:"locale"`
		}{}
		if err := json.NewDecoder(inforesp.Body).Decode(&claims); err != nil {
			writeError(ctx, w, "Error decoding upstream response", http.StatusInternalServerError)
			return
		}
		if claims.Aud != cid {
			writeError(ctx, w, "Error decoding upstream response", http.StatusInternalServerError)
			return
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2013 The rerun AUTHORS. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"github.com/howeyc/fsnotify"
	"go/build"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
)

var (
	do_tests      = flag.Bool("test", false, "Run tests before running program.")
	test_only     = flag.Bool("test-only", false, "Only run tests.")
	build_only    = flag.Bool("build-only", false, "Only run tests.")
	race_detector = flag.Bool("race", false, "Run program and tests with the race detector")
)

func install(buildpath, lastError string) (installed bool, errorOutput string, err error) {
	cmdline := []string{"go", "get"}

	if *race_detector {
		cmdline = append(cmdline, "-race")
	}
	cmdline = append(cmdline, buildpath)

	// setup the build command, use a shared buffer for both stdOut and stdErr
	cmd := exec.Command("go", cmdline[1:]...)
	buf := bytes.NewBuffer([]byte{})
	cmd.Stdout = buf
	cmd.Stderr = buf

	err = cmd.Run()

	// when there is any output, the go command failed.
	if buf.Len() > 0 {
		errorOutput = buf.String()
		if errorOutput != lastError {
			fmt.Print(errorOutput)
		}
		err = errors.New("compile error")
		return
	}

	// all seems fine
	installed = true
	return
}

func test(buildpath string) (passed bool, err error) {
	cmdline := []string{"go", "test"}

	if *race_detector {
		cmdline = append(cmdline, "-race")
	}
	cmdline = append(cmdline, "-v", buildpath)

	// setup the build command, use a shared buffer for both stdOut and stdErr
	cmd := exec.Command("go", cmdline[1:]...)
	buf := bytes.NewBuffer([]byte{})
	cmd.Stdout = buf
	cmd.Stderr = buf

	err = cmd.Run()
	passed = err == nil

	if !passed {
		fmt.Println(buf)
	} else {
		log.Println("tests passed")
	}

	return
}

func gobuild(buildpath string) (passed bool, err error) {
	cmdline := []string{"go", "build"}

	if *race_detector {
		cmdline = append(cmdline, "-race")
	}
	cmdline = append(cmdline, "-v", buildpath)

	// setup the build command, use a shared buffer for both stdOut and stdErr
	cmd := exec.Command("go", cmdline[1:]...)
	buf := bytes.NewBuffer([]byte{})
	cmd.Stdout = buf
	cmd.Stderr = buf

	err = cmd.Run()
	passed = err == nil

	if !passed {
		fmt.Println(buf)
	} else {
		log.Println("build passed")
	}

	return
}

func run(binName, binPath string, args []string) (runch chan bool) {
	runch = make(chan bool)
	go func() {
		cmdline := append([]string{binName}, args...)
		var proc *os.Process
		for relaunch := range runch {
			if proc != nil {
				err := proc.Signal(os.Interrupt)
				if err != nil {
					log.Printf("error on sending signal to process: '%s', will now hard-kill the process\n", err)
					proc.Kill()
				}
				proc.Wait()
			}
			if !relaunch {
				continue
			}
			cmd := exec.Command(binPath, args...)
			cmd.Stdout = os.Stdout
			cmd.Stderr = os.Stderr
			log.Print(cmdline)
			err := cmd.Start()
			if err != nil {
				log.Printf("error on starting process: '%s'\n", err)
			}
			proc = cmd.Process
		}
	}()
	return
}

func getWatcher(buildpath string) (watcher *fsnotify.Watcher, err error) {
	watcher, err = fsnotify.NewWatcher()
	addToWatcher(watcher, buildpath, map[string]bool{})
	return
}

func addToWatcher(watcher *fsnotify.Watcher, importpath string, watching map[string]bool) {
	pkg, err := build.Import(importpath, "", 0)
	if err != nil {
		return
	}
	if pkg.Goroot {
		return
	}
	watcher.Watch(pkg.Dir)
	watching[importpath] = true
	for _, imp := range pkg.Imports {
		if !watching[imp] {
			addToWatcher(watcher, imp, watching)
		}
	}
}

func rerun(buildpath string, args []string) (err error) {
	log.Printf("setting up %s %v", buildpath, args)

	pkg, err := build.Import(buildpath, "", 0)
	if err != nil {
		return
	}

	if pkg.Name != "main" {
		err = errors.New(fmt.Sprintf("expected package %q, got %q", "main", pkg.Name))
		return
	}

	_, binName := path.Split(buildpath)
	var binPath string
	if gobin := os.Getenv("GOBIN"); gobin != "" {
		binPath = filepath.Join(gobin, binName)
	} else {
		binPath = filepath.Join(pkg.BinDir, binName)
	}

	var runch chan bool
	if !(*test_only) && !(*build_only) {
		runch = run(binName, binPath, args)
	}

	no_run := false
	if *do_tests {
		passed, _ := test(buildpath)
		if !passed {
			no_run = true
		}
	}

	if *build_only {
		gobuild(buildpath)
	}

	var errorOutput string
	_, errorOutput, ierr := install(buildpath, errorOutput)
	if !no_run && !(*test_only) && !(*build_only) && ierr == nil {
		runch <- true
	}

	var watcher *fsnotify.Watcher
	watcher, err = getWatcher(buildpath)
	if err != nil {
		return
	}

	for {
		// read event from the watcher
		we, _ := <-watcher.Event
		// other files in the directory don't count - we watch the whole thing in case new .go files appear.
		if filepath.Ext(we.Name) != ".go" {
			continue
		}

		log.Print(we.Name)

		// close the watcher
		watcher.Close()
		// to clean things up: read events from the watcher until events chan is closed.
		go func(events chan *fsnotify.FileEvent) {
			for _ = range events {

			}
		}(watcher.Event)
		// create a new watcher
		log.Println("rescanning")
		watcher, err = getWatcher(buildpath)
		if err != nil {
			return
		}

		// we don't need the errors from the new watcher.
		// we continiously discard them from the channel to avoid a deadlock.
		go func(errors chan error) {
			for _ = range errors {

			}
		}(watcher.Error)

		var installed bool
		// rebuild
		installed, errorOutput, _ = install(buildpath, errorOutput)
		if !installed {
			continue
		}

		if *do_tests {
			passed, _ := test(buildpath)
			if !passed {
				continue
			}
		}

		if *build_only {
			gobuild(buildpath)
		}

		// rerun. if we're only testing, sending
		if !(*test_only) && !(*build_only) {
			runch <- true
		}
	}
	return
}

func main() {
	flag.Parse()
	if *test_only {
		*do_tests = true
	}

	if *test_only && *build_only {
		log.Fatal("Cannot combine options --test-only and --build-only.")
	}

	if len(flag.Args()) < 1 {
		log.Fatal("Usage: rerun [--test] [--test-only] [--race] <import path> [arg]*")
	}

	buildpath := flag.Args()[0]
	args := flag.Args()[1:]
	err := rerun(buildpath, args)
	if err != nil {
		log.Print(err)
	}
}
<commit_msg>Change flag --build-only description. Change Usage text<commit_after>// Copyright 2013 The rerun AUTHORS. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"github.com/howeyc/fsnotify"
	"go/build"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
)

var (
	do_tests      = flag.Bool("test", false, "Run tests before running program.")
	test_only     = flag.Bool("test-only", false, "Only run tests.")
	build_only    = flag.Bool("build-only", false, "Only build program.")
	race_detector = flag.Bool("race", false, "Run program and tests with the race detector")
)

func install(buildpath, lastError string) (installed bool, errorOutput string, err error) {
	cmdline := []string{"go", "get"}

	if *race_detector {
		cmdline = append(cmdline, "-race")
	}
	cmdline = append(cmdline, buildpath)

	// setup the build command, use a shared buffer for both stdOut and stdErr
	cmd := exec.Command("go", cmdline[1:]...)
	buf := bytes.NewBuffer([]byte{})
	cmd.Stdout = buf
	cmd.Stderr = buf

	err = cmd.Run()

	// when there is any output, the go command failed.
	if buf.Len() > 0 {
		errorOutput = buf.String()
		if errorOutput != lastError {
			fmt.Print(errorOutput)
		}
		err = errors.New("compile error")
		return
	}

	// all seems fine
	installed = true
	return
}

func test(buildpath string) (passed bool, err error) {
	cmdline := []string{"go", "test"}

	if *race_detector {
		cmdline = append(cmdline, "-race")
	}
	cmdline = append(cmdline, "-v", buildpath)

	// setup the build command, use a shared buffer for both stdOut and stdErr
	cmd := exec.Command("go", cmdline[1:]...)
	buf := bytes.NewBuffer([]byte{})
	cmd.Stdout = buf
	cmd.Stderr = buf

	err = cmd.Run()
	passed = err == nil

	if !passed {
		fmt.Println(buf)
	} else {
		log.Println("tests passed")
	}

	return
}

func gobuild(buildpath string) (passed bool, err error) {
	cmdline := []string{"go", "build"}

	if *race_detector {
		cmdline = append(cmdline, "-race")
	}
	cmdline = append(cmdline, "-v", buildpath)

	// setup the build command, use a shared buffer for both stdOut and stdErr
	cmd := exec.Command("go", cmdline[1:]...)
	buf := bytes.NewBuffer([]byte{})
	cmd.Stdout = buf
	cmd.Stderr = buf

	err = cmd.Run()
	passed = err == nil

	if !passed {
		fmt.Println(buf)
	} else {
		log.Println("build passed")
	}

	return
}

func run(binName, binPath string, args []string) (runch chan bool) {
	runch = make(chan bool)
	go func() {
		cmdline := append([]string{binName}, args...)
		var proc *os.Process
		for relaunch := range runch {
			if proc != nil {
				err := proc.Signal(os.Interrupt)
				if err != nil {
					log.Printf("error on sending signal to process: '%s', will now hard-kill the process\n", err)
					proc.Kill()
				}
				proc.Wait()
			}
			if !relaunch {
				continue
			}
			cmd := exec.Command(binPath, args...)
			cmd.Stdout = os.Stdout
			cmd.Stderr = os.Stderr
			log.Print(cmdline)
			err := cmd.Start()
			if err != nil {
				log.Printf("error on starting process: '%s'\n", err)
			}
			proc = cmd.Process
		}
	}()
	return
}

func getWatcher(buildpath string) (watcher *fsnotify.Watcher, err error) {
	watcher, err = fsnotify.NewWatcher()
	addToWatcher(watcher, buildpath, map[string]bool{})
	return
}

func addToWatcher(watcher *fsnotify.Watcher, importpath string, watching map[string]bool) {
	pkg, err := build.Import(importpath, "", 0)
	if err != nil {
		return
	}
	if pkg.Goroot {
		return
	}
	watcher.Watch(pkg.Dir)
	watching[importpath] = true
	for _, imp := range pkg.Imports {
		if !watching[imp] {
			addToWatcher(watcher, imp, watching)
		}
	}
}

func rerun(buildpath string, args []string) (err error) {
	log.Printf("setting up %s %v", buildpath, args)

	pkg, err := build.Import(buildpath, "", 0)
	if err != nil {
		return
	}

	if pkg.Name != "main" {
		err = errors.New(fmt.Sprintf("expected package %q, got %q", "main", pkg.Name))
		return
	}

	_, binName := path.Split(buildpath)
	var binPath string
	if gobin := os.Getenv("GOBIN"); gobin != "" {
		binPath = filepath.Join(gobin, binName)
	} else {
		binPath = filepath.Join(pkg.BinDir, binName)
	}

	var runch chan bool
	if !(*test_only) && !(*build_only) {
		runch = run(binName, binPath, args)
	}

	no_run := false
	if *do_tests {
		passed, _ := test(buildpath)
		if !passed {
			no_run = true
		}
	}

	if *build_only {
		gobuild(buildpath)
	}

	var errorOutput string
	_, errorOutput, ierr := install(buildpath, errorOutput)
	if !no_run && !(*test_only) && !(*build_only) && ierr == nil {
		runch <- true
	}

	var watcher *fsnotify.Watcher
	watcher, err = getWatcher(buildpath)
	if err != nil {
		return
	}

	for {
		// read event from the watcher
		we, _ := <-watcher.Event
		// other files in the directory don't count - we watch the whole thing in case new .go files appear.
		if filepath.Ext(we.Name) != ".go" {
			continue
		}

		log.Print(we.Name)

		// close the watcher
		watcher.Close()
		// to clean things up: read events from the watcher until events chan is closed.
		go func(events chan *fsnotify.FileEvent) {
			for _ = range events {

			}
		}(watcher.Event)
		// create a new watcher
		log.Println("rescanning")
		watcher, err = getWatcher(buildpath)
		if err != nil {
			return
		}

		// we don't need the errors from the new watcher.
		// we continiously discard them from the channel to avoid a deadlock.
		go func(errors chan error) {
			for _ = range errors {

			}
		}(watcher.Error)

		var installed bool
		// rebuild
		installed, errorOutput, _ = install(buildpath, errorOutput)
		if !installed {
			continue
		}

		if *do_tests {
			passed, _ := test(buildpath)
			if !passed {
				continue
			}
		}

		if *build_only {
			gobuild(buildpath)
		}

		// rerun. if we're only testing, sending
		if !(*test_only) && !(*build_only) {
			runch <- true
		}
	}
	return
}

func main() {
	flag.Parse()
	if *test_only {
		*do_tests = true
	}

	if *test_only && *build_only {
		log.Fatal("Cannot combine options --test-only and --build-only.")
	}

	if len(flag.Args()) < 1 {
		log.Fatal("Usage: rerun [--test] [--test-only] [--build-only] [--race] <import path> [arg]*")
	}

	buildpath := flag.Args()[0]
	args := flag.Args()[1:]
	err := rerun(buildpath, args)
	if err != nil {
		log.Print(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/howeyc/fsnotify"
	"go/build"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
)

func install(buildpath, lastError string) (installed bool, errorOutput string, err error) {
	cmdline := []string{"go", "get", "-v", buildpath}

	cmd := exec.Command("go", cmdline[1:]...)
	bufOut := bytes.NewBuffer([]byte{})
	bufErr := bytes.NewBuffer([]byte{})
	cmd.Stdout = bufOut
	cmd.Stderr = bufErr

	err = cmd.Run()

	if bufOut.Len() != 0 {
		errorOutput = bufOut.String()
		if errorOutput != lastError {
			fmt.Print(bufOut)
		}
		err = errors.New("compile error")
		return
	}

	installed = bufErr.Len() != 0

	return
}

func run(binName, binPath string, args []string) (runch chan bool) {
	runch = make(chan bool)
	go func() {
		cmdline := append([]string{binName}, args...)
		var proc *os.Process
		for _ = range runch {
			if proc != nil {
				proc.Kill()
			}
			cmd := exec.Command(binPath, args...)
			cmd.Stdout = os.Stdout
			cmd.Stderr = os.Stderr
			log.Print(cmdline)
			cmd.Start()
			proc = cmd.Process
		}
	}()
	return
}

func getWatcher(buildpath string) (watcher *fsnotify.Watcher, err error) {
	watcher, err = fsnotify.NewWatcher()
	addToWatcher(watcher, buildpath, map[string]bool{})
	return
}

func addToWatcher(watcher *fsnotify.Watcher, importpath string, watching map[string]bool) {
	pkg, err := build.Import(importpath, "", 0)
	if err != nil {
		return
	}
	if pkg.Goroot {
		return
	}
	watcher.Watch(pkg.Dir)
	watching[importpath] = true
	for _, imp := range pkg.Imports {
		if !watching[imp] {
			addToWatcher(watcher, imp, watching)
		}
	}
}

func rerun(buildpath string, args []string) (err error) {
	log.Printf("setting up %s %v", buildpath, args)

	pkg, err := build.Import(buildpath, "", 0)
	if err != nil {
		return
	}

	if pkg.Name != "main" {
		err = errors.New(fmt.Sprintf("expected package %q, got %q", "main", pkg.Name))
		return
	}

	_, binName := path.Split(buildpath)
	binPath := filepath.Join(pkg.BinDir, binName)

	runch := run(binName, binPath, args)

	var errorOutput string
	_, errorOutput, ierr := install(buildpath, errorOutput)
	if ierr == nil {
		runch <- true
	}

	var watcher *fsnotify.Watcher
	watcher, err = getWatcher(buildpath)
	if err != nil {
		return
	}

	for {
		we, _ := <-watcher.Event
		var installed bool
		installed, errorOutput, _ = install(buildpath, errorOutput)
		if installed {
			log.Print(we.Name)
			runch <- true
			watcher.Close()
			/* empty the buffer */
			go func(events chan *fsnotify.FileEvent) {
				for _ = range events {

				}
			}(watcher.Event)
			go func(errors chan error) {
				for _ = range errors {

				}
			}(watcher.Error)
			/* rescan */
			log.Println("rescanning")
			watcher, err = getWatcher(buildpath)
			if err != nil {
				return
			}
		}
	}
	return
}

func main() {
	if len(os.Args) < 2 {
		log.Fatal("Usage: rerun <import path> [arg]*")
	}
	err := rerun(os.Args[1], os.Args[2:])
	if err != nil {
		log.Print(err)
	}
}
<commit_msg>Added support for the GOBIN environment variable. 	"If the GOBIN environment variable is set, commands are installed to the directory it names instead of DIR/bin" (http://golang.org/cmd/go/#hdr-GOPATH_environment_variable). Added error logging on cmd.Start()<commit_after>package main

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/howeyc/fsnotify"
	"go/build"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
)

func install(buildpath, lastError string) (installed bool, errorOutput string, err error) {
	cmdline := []string{"go", "get", "-v", buildpath}

	cmd := exec.Command("go", cmdline[1:]...)
	bufOut := bytes.NewBuffer([]byte{})
	bufErr := bytes.NewBuffer([]byte{})
	cmd.Stdout = bufOut
	cmd.Stderr = bufErr

	err = cmd.Run()

	if bufOut.Len() != 0 {
		errorOutput = bufOut.String()
		if errorOutput != lastError {
			fmt.Print(bufOut)
		}
		err = errors.New("compile error")
		return
	}

	installed = bufErr.Len() != 0

	return
}

func run(binName, binPath string, args []string) (runch chan bool) {
	runch = make(chan bool)
	go func() {
		cmdline := append([]string{binName}, args...)
		var proc *os.Process
		for _ = range runch {
			if proc != nil {
				proc.Kill()
			}
			cmd := exec.Command(binPath, args...)
			cmd.Stdout = os.Stdout
			cmd.Stderr = os.Stderr
			log.Print(cmdline)
			err := cmd.Start()
			if err != nil {
				log.Printf("error on starting process: '%s'\n", err)
			}
			proc = cmd.Process
		}
	}()
	return
}

func getWatcher(buildpath string) (watcher *fsnotify.Watcher, err error) {
	watcher, err = fsnotify.NewWatcher()
	addToWatcher(watcher, buildpath, map[string]bool{})
	return
}

func addToWatcher(watcher *fsnotify.Watcher, importpath string, watching map[string]bool) {
	pkg, err := build.Import(importpath, "", 0)
	if err != nil {
		return
	}
	if pkg.Goroot {
		return
	}
	watcher.Watch(pkg.Dir)
	watching[importpath] = true
	for _, imp := range pkg.Imports {
		if !watching[imp] {
			addToWatcher(watcher, imp, watching)
		}
	}
}

func rerun(buildpath string, args []string) (err error) {
	log.Printf("setting up %s %v", buildpath, args)

	pkg, err := build.Import(buildpath, "", 0)
	if err != nil {
		return
	}

	if pkg.Name != "main" {
		err = errors.New(fmt.Sprintf("expected package %q, got %q", "main", pkg.Name))
		return
	}

	_, binName := path.Split(buildpath)
	var binPath string
	if gobin := os.Getenv("GOBIN"); gobin != "" {
		binPath = filepath.Join(gobin, binName)
	} else {
		binPath = filepath.Join(pkg.BinDir, binName)
	}

	runch := run(binName, binPath, args)

	var errorOutput string
	_, errorOutput, ierr := install(buildpath, errorOutput)
	if ierr == nil {
		runch <- true
	}

	var watcher *fsnotify.Watcher
	watcher, err = getWatcher(buildpath)
	if err != nil {
		return
	}

	for {
		we, _ := <-watcher.Event
		var installed bool
		installed, errorOutput, _ = install(buildpath, errorOutput)
		if installed {
			log.Print(we.Name)
			runch <- true
			watcher.Close()
			/* empty the buffer */
			go func(events chan *fsnotify.FileEvent) {
				for _ = range events {

				}
			}(watcher.Event)
			go func(errors chan error) {
				for _ = range errors {

				}
			}(watcher.Error)
			/* rescan */
			log.Println("rescanning")
			watcher, err = getWatcher(buildpath)
			if err != nil {
				return
			}
		}
	}
	return
}

func main() {
	if len(os.Args) < 2 {
		log.Fatal("Usage: rerun <import path> [arg]*")
	}
	err := rerun(os.Args[1], os.Args[2:])
	if err != nil {
		log.Print(err)
	}
}
<|endoftext|>


<commit_before>package gofast

import (
	"net"
	"path"
	"path/filepath"
	"regexp"
	"strings"
)

// SessionHandler handles the gofast *Reqeust with the provided given Client.
// The Client should properly handle the transport to the fastcgi application.
// Should do proper routing or other parameter mapping here.
type SessionHandler func(client Client, req *Request) (resp *ResponsePipe, err error)

// Handle implements SessionHandler
func (handler SessionHandler) Handle(client Client, req *Request) (*ResponsePipe, error) {
	return handler(client, req)
}

// Middleware transform a SessionHandler as another SessionHandler
type Middleware func(SessionHandler) SessionHandler

// Chain chains middlewares into a single middleware
func Chain(middlewares ...Middleware) Middleware {
	if len(middlewares) == 0 {
		return nil
	}
	return func(inner SessionHandler) (out SessionHandler) {
		out = inner
		for i := len(middlewares) - 1; i >= 0; i-- {
			out = middlewares[i](out)
		}
		return
	}
}

// BasicSession implements SessionHandlerFunc
func BasicSession(client Client, req *Request) (*ResponsePipe, error) {

	r := req.Raw

	var isHTTPS string
	if r.URL.Scheme == "https" || r.URL.Scheme == "wss" {
		isHTTPS = "on"
	}

	remoteAddr, remotePort, _ := net.SplitHostPort(r.RemoteAddr)
	_, serverPort, err := net.SplitHostPort(r.URL.Host)
	if err != nil {
		if r.URL.Scheme == "https" || r.URL.Scheme == "wss" {
			serverPort = "443"
		} else {
			serverPort = "80"
		}
	}

	// the basic information here
	req.Params["CONTENT_TYPE"] = r.Header.Get("Content-Type")
	req.Params["CONTENT_LENGTH"] = r.Header.Get("Content-Length")
	req.Params["HTTPS"] = isHTTPS
	req.Params["GATEWAY_INTERFACE"] = "CGI/1.1"
	req.Params["REMOTE_ADDR"] = remoteAddr
	req.Params["REMOTE_PORT"] = remotePort
	req.Params["SERVER_PORT"] = serverPort
	req.Params["SERVER_NAME"] = r.Host
	req.Params["SERVER_PROTOCOL"] = r.Proto
	req.Params["SERVER_SOFTWARE"] = "gofast"
	req.Params["REDIRECT_STATUS"] = "200"
	req.Params["REQUEST_METHOD"] = r.Method
	req.Params["REQUEST_URI"] = r.RequestURI
	req.Params["QUERY_STRING"] = r.URL.RawQuery

	return client.Do(req)
}

// FileSystemRouter handles ordinary filesystem based router
// like an old fashion PHP hosting environment
type FileSystemRouter struct {

	// DocRoot stores the ordinary Apache DocumentRoot parameter
	DocRoot string

	// Exts stores accepted extensions
	Exts []string

	// DirIndex stores ordinary Apache DirectoryIndex parameter
	// for to identify file to show in directory
	DirIndex []string
}

// Router returns Middleware
func (fs *FileSystemRouter) Router() Middleware {
	return func(inner SessionHandler) SessionHandler {
		return func(client Client, req *Request) (*ResponsePipe, error) {

			// define some required cgi parameters
			// with the given http request

			// refer from nginx fastcgi_params
			// fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
			// fastcgi_split_path_info ^(.+\.php)(/?.+)$;
			// fastcgi_param PATH_INFO $fastcgi_path_info;
			// fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
			// fastcgi_param  QUERY_STRING       $query_string;
			// fastcgi_param  REQUEST_METHOD     $request_method;
			// fastcgi_param  CONTENT_TYPE       $content_type;
			// fastcgi_param  CONTENT_LENGTH     $content_length;
			// fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
			// fastcgi_param  REQUEST_URI        $request_uri;
			// fastcgi_param  DOCUMENT_URI       $document_uri;
			// fastcgi_param  DOCUMENT_ROOT      $document_root;
			// fastcgi_param  SERVER_PROTOCOL    $server_protocol;
			// fastcgi_param  HTTPS              $https if_not_empty;
			// fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
			// fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;
			// fastcgi_param  REMOTE_ADDR        $remote_addr;
			// fastcgi_param  REMOTE_PORT        $remote_port;
			// fastcgi_param  SERVER_ADDR        $server_addr;
			// fastcgi_param  SERVER_PORT        $server_port;
			// fastcgi_param  SERVER_NAME        $server_name;
			// # PHP only, required if PHP was built with --enable-force-cgi-redirect
			// fastcgi_param  REDIRECT_STATUS    200;

			r := req.Raw
			fastcgiScriptName := r.URL.Path

			var fastcgiPathInfo string
			pathinfoRe := regexp.MustCompile(`^(.+\.php)(/?.+)$`)
			if matches := pathinfoRe.FindStringSubmatch(fastcgiScriptName); len(matches) > 0 {
				fastcgiScriptName, fastcgiPathInfo = matches[1], matches[2]
			}

			// should be handled by fs.Inner
			//req.Params["CONTENT_TYPE"] = r.Header.Get("Content-Type")
			//req.Params["CONTENT_LENGTH"] = r.Header.Get("Content-Length")
			//req.Params["HTTPS"] = isHTTPS
			//req.Params["GATEWAY_INTERFACE"] = "CGI/1.1"
			//req.Params["SERVER_SOFTWARE"] = "gofast"
			//req.Params["REMOTE_ADDR"] = remoteAddr
			//req.Params["REMOTE_PORT"] = remotePort
			//req.Params["SERVER_PORT"] = serverPort
			//req.Params["SERVER_NAME"] = r.Host
			//req.Params["SERVER_PROTOCOL"] = r.Proto
			//req.Params["REDIRECT_STATUS"] = "200"
			//req.Params["REQUEST_METHOD"] = r.Method
			//req.Params["REQUEST_URI"] = r.RequestURI
			//req.Params["QUERY_STRING"] = r.URL.RawQuery

			req.Params["PATH_INFO"] = fastcgiPathInfo
			req.Params["PATH_TRANSLATED"] = filepath.Join(fs.DocRoot, fastcgiPathInfo)
			req.Params["SCRIPT_NAME"] = fastcgiScriptName
			req.Params["SCRIPT_FILENAME"] = filepath.Join(fs.DocRoot, fastcgiScriptName)
			req.Params["DOCUMENT_URI"] = r.URL.Path
			req.Params["DOCUMENT_ROOT"] = fs.DocRoot

			// handle directory index
			urlPath := r.URL.Path
			if strings.HasSuffix(urlPath, "/") {
				urlPath = path.Join(urlPath, "index.php")
			}
			req.Params["SCRIPT_FILENAME"] = path.Join(fs.DocRoot, urlPath)

			return inner.Handle(client, req)
		}
	}
}

// MapHeader implement Middleware to map header field SomeRandomField to
// HTTP_SOME_RANDOM_FIELD
func MapHeader(inner SessionHandler) SessionHandler {
	return func(client Client, req *Request) (*ResponsePipe, error) {
		r := req.Raw

		// http header
		for k, v := range r.Header {
			formattedKey := strings.Replace(strings.ToUpper(k), "-", "_", -1)
			if formattedKey == "CONTENT_TYPE" || formattedKey == "CONTENT_LENGTH" {
				continue
			}

			key := "HTTP_" + formattedKey
			var value string
			if len(v) > 0 {
				//   refer to https://tools.ietf.org/html/rfc7230#section-3.2.2
				//
				//   A recipient MAY combine multiple header fields with the same field
				//   name into one "field-name: field-value" pair, without changing the
				//   semantics of the message, by appending each subsequent field value to
				//   the combined field value in order, separated by a comma.  The order
				//   in which header fields with the same field name are received is
				//   therefore significant to the interpretation of the combined field
				//   value; a proxy MUST NOT change the order of these field values when
				//   forwarding a message.
				value = strings.Join(v, ",")
			}
			req.Params[key] = value
		}

		return inner.Handle(client, req)
	}
}

// NewPHPFS returns a SessionHandler provides a classic PHP hosting
// environment for a HTTP session
func NewPHPFS(root string) SessionHandler {
	fs := &FileSystemRouter{
		DocRoot:  root,
		Exts:     []string{"php"},
		DirIndex: []string{"index.php"},
	}
	middleware := Chain(
		fs.Router(),
		MapHeader,
	)
	return middleware(BasicSession)
}
<commit_msg>Separate all logic from BasicSession<commit_after>package gofast

import (
	"net"
	"path"
	"path/filepath"
	"regexp"
	"strings"
)

// SessionHandler handles the gofast *Reqeust with the provided given Client.
// The Client should properly handle the transport to the fastcgi application.
// Should do proper routing or other parameter mapping here.
type SessionHandler func(client Client, req *Request) (resp *ResponsePipe, err error)

// Handle implements SessionHandler
func (handler SessionHandler) Handle(client Client, req *Request) (*ResponsePipe, error) {
	return handler(client, req)
}

// Middleware transform a SessionHandler as another SessionHandler
type Middleware func(SessionHandler) SessionHandler

// Chain chains middlewares into a single middleware
func Chain(middlewares ...Middleware) Middleware {
	if len(middlewares) == 0 {
		return nil
	}
	return func(inner SessionHandler) (out SessionHandler) {
		out = inner
		for i := len(middlewares) - 1; i >= 0; i-- {
			out = middlewares[i](out)
		}
		return
	}
}

// BasicSession implements SessionHandlerFunc
func BasicSession(client Client, req *Request) (*ResponsePipe, error) {
	return client.Do(req)
}

// BasicParamsMap maps basic parameters
func BasicParamsMap(inner SessionHandler) SessionHandler {
	return func(client Client, req *Request) (*ResponsePipe, error) {

		r := req.Raw

		var isHTTPS string
		if r.URL.Scheme == "https" || r.URL.Scheme == "wss" {
			isHTTPS = "on"
		}

		remoteAddr, remotePort, _ := net.SplitHostPort(r.RemoteAddr)
		_, serverPort, err := net.SplitHostPort(r.URL.Host)
		if err != nil {
			if r.URL.Scheme == "https" || r.URL.Scheme == "wss" {
				serverPort = "443"
			} else {
				serverPort = "80"
			}
		}

		// the basic information here
		req.Params["CONTENT_TYPE"] = r.Header.Get("Content-Type")
		req.Params["CONTENT_LENGTH"] = r.Header.Get("Content-Length")
		req.Params["HTTPS"] = isHTTPS
		req.Params["GATEWAY_INTERFACE"] = "CGI/1.1"
		req.Params["REMOTE_ADDR"] = remoteAddr
		req.Params["REMOTE_PORT"] = remotePort
		req.Params["SERVER_PORT"] = serverPort
		req.Params["SERVER_NAME"] = r.Host
		req.Params["SERVER_PROTOCOL"] = r.Proto
		req.Params["SERVER_SOFTWARE"] = "gofast"
		req.Params["REDIRECT_STATUS"] = "200"
		req.Params["REQUEST_METHOD"] = r.Method
		req.Params["REQUEST_URI"] = r.RequestURI
		req.Params["QUERY_STRING"] = r.URL.RawQuery

		return inner.Handle(client, req)
	}
}

// FileSystemRouter handles ordinary filesystem based router
// like an old fashion PHP hosting environment
type FileSystemRouter struct {

	// DocRoot stores the ordinary Apache DocumentRoot parameter
	DocRoot string

	// Exts stores accepted extensions
	Exts []string

	// DirIndex stores ordinary Apache DirectoryIndex parameter
	// for to identify file to show in directory
	DirIndex []string
}

// Router returns Middleware
func (fs *FileSystemRouter) Router() Middleware {
	return func(inner SessionHandler) SessionHandler {
		return func(client Client, req *Request) (*ResponsePipe, error) {

			// define some required cgi parameters
			// with the given http request

			// refer from nginx fastcgi_params
			// fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
			// fastcgi_split_path_info ^(.+\.php)(/?.+)$;
			// fastcgi_param PATH_INFO $fastcgi_path_info;
			// fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
			// fastcgi_param  QUERY_STRING       $query_string;
			// fastcgi_param  REQUEST_METHOD     $request_method;
			// fastcgi_param  CONTENT_TYPE       $content_type;
			// fastcgi_param  CONTENT_LENGTH     $content_length;
			// fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
			// fastcgi_param  REQUEST_URI        $request_uri;
			// fastcgi_param  DOCUMENT_URI       $document_uri;
			// fastcgi_param  DOCUMENT_ROOT      $document_root;
			// fastcgi_param  SERVER_PROTOCOL    $server_protocol;
			// fastcgi_param  HTTPS              $https if_not_empty;
			// fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
			// fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;
			// fastcgi_param  REMOTE_ADDR        $remote_addr;
			// fastcgi_param  REMOTE_PORT        $remote_port;
			// fastcgi_param  SERVER_ADDR        $server_addr;
			// fastcgi_param  SERVER_PORT        $server_port;
			// fastcgi_param  SERVER_NAME        $server_name;
			// # PHP only, required if PHP was built with --enable-force-cgi-redirect
			// fastcgi_param  REDIRECT_STATUS    200;

			r := req.Raw
			fastcgiScriptName := r.URL.Path

			var fastcgiPathInfo string
			pathinfoRe := regexp.MustCompile(`^(.+\.php)(/?.+)$`)
			if matches := pathinfoRe.FindStringSubmatch(fastcgiScriptName); len(matches) > 0 {
				fastcgiScriptName, fastcgiPathInfo = matches[1], matches[2]
			}

			// should be handled by fs.Inner
			//req.Params["CONTENT_TYPE"] = r.Header.Get("Content-Type")
			//req.Params["CONTENT_LENGTH"] = r.Header.Get("Content-Length")
			//req.Params["HTTPS"] = isHTTPS
			//req.Params["GATEWAY_INTERFACE"] = "CGI/1.1"
			//req.Params["SERVER_SOFTWARE"] = "gofast"
			//req.Params["REMOTE_ADDR"] = remoteAddr
			//req.Params["REMOTE_PORT"] = remotePort
			//req.Params["SERVER_PORT"] = serverPort
			//req.Params["SERVER_NAME"] = r.Host
			//req.Params["SERVER_PROTOCOL"] = r.Proto
			//req.Params["REDIRECT_STATUS"] = "200"
			//req.Params["REQUEST_METHOD"] = r.Method
			//req.Params["REQUEST_URI"] = r.RequestURI
			//req.Params["QUERY_STRING"] = r.URL.RawQuery

			req.Params["PATH_INFO"] = fastcgiPathInfo
			req.Params["PATH_TRANSLATED"] = filepath.Join(fs.DocRoot, fastcgiPathInfo)
			req.Params["SCRIPT_NAME"] = fastcgiScriptName
			req.Params["SCRIPT_FILENAME"] = filepath.Join(fs.DocRoot, fastcgiScriptName)
			req.Params["DOCUMENT_URI"] = r.URL.Path
			req.Params["DOCUMENT_ROOT"] = fs.DocRoot

			// handle directory index
			urlPath := r.URL.Path
			if strings.HasSuffix(urlPath, "/") {
				urlPath = path.Join(urlPath, "index.php")
			}
			req.Params["SCRIPT_FILENAME"] = path.Join(fs.DocRoot, urlPath)

			return inner.Handle(client, req)
		}
	}
}

// MapHeader implement Middleware to map header field SomeRandomField to
// HTTP_SOME_RANDOM_FIELD
func MapHeader(inner SessionHandler) SessionHandler {
	return func(client Client, req *Request) (*ResponsePipe, error) {
		r := req.Raw

		// http header
		for k, v := range r.Header {
			formattedKey := strings.Replace(strings.ToUpper(k), "-", "_", -1)
			if formattedKey == "CONTENT_TYPE" || formattedKey == "CONTENT_LENGTH" {
				continue
			}

			key := "HTTP_" + formattedKey
			var value string
			if len(v) > 0 {
				//   refer to https://tools.ietf.org/html/rfc7230#section-3.2.2
				//
				//   A recipient MAY combine multiple header fields with the same field
				//   name into one "field-name: field-value" pair, without changing the
				//   semantics of the message, by appending each subsequent field value to
				//   the combined field value in order, separated by a comma.  The order
				//   in which header fields with the same field name are received is
				//   therefore significant to the interpretation of the combined field
				//   value; a proxy MUST NOT change the order of these field values when
				//   forwarding a message.
				value = strings.Join(v, ",")
			}
			req.Params[key] = value
		}

		return inner.Handle(client, req)
	}
}

// NewPHPFS returns a SessionHandler provides a classic PHP hosting
// environment for a HTTP session
func NewPHPFS(root string) SessionHandler {
	fs := &FileSystemRouter{
		DocRoot:  root,
		Exts:     []string{"php"},
		DirIndex: []string{"index.php"},
	}
	middleware := Chain(
		BasicParamsMap,
		MapHeader,
		fs.Router(),
	)
	return middleware(BasicSession)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
)

type session struct {
	sp       *subProcess
	conn     *connection
	messages chan string
}

func NewSession(conn *connection) (s *session) {
	s = &session{
		conn: conn,
	}

	sp := NewSubProcess("bash")
	s.sp = sp

	return
}

func (s *session) start() (err error) {
	s.sendMessage("hilo")

	err = s.sp.start()
	if err != nil {
		return
	}

	go func() {
		line := ""
		for {
			message := s.conn.Read()
			if len(message) == 0 {
				fmt.Println("connection closed, terminating subprocess")
				s.sp.kill()
				break
			}

			if message[0] == 13 {
				fmt.Println("message received: ", line)
				s.sp.input <- line + "\n"
				line = ""
			} else {
				msg := string(message)
				line += msg
			}
		}
	}()

	go func() {
		for line := range s.sp.output {
			fmt.Print(line)
			s.conn.Write(line)
		}
	}()

	return
}

func (s *session) sendMessage(message string) {
	s.conn.Write(message)
}

func (s *session) End() {
	s.sp.kill()
}
<commit_msg>use common method<commit_after>package main

import (
	"fmt"
)

type session struct {
	sp       *subProcess
	conn     *connection
	messages chan string
}

func NewSession(conn *connection) (s *session) {
	s = &session{
		conn: conn,
	}

	sp := NewSubProcess("bash")
	s.sp = sp

	return
}

func (s *session) start() (err error) {
	s.sendMessage("hilo")

	err = s.sp.start()
	if err != nil {
		return
	}

	go func() {
		line := ""
		for {
			message := s.conn.Read()
			if len(message) == 0 {
				fmt.Println("connection closed, terminating subprocess")
				s.sp.kill()
				break
			}

			if message[0] == 13 {
				fmt.Println("message received: ", line)
				s.sp.input <- line + "\n"
				line = ""
			} else {
				msg := string(message)
				line += msg
			}
		}
	}()

	go func() {
		for line := range s.sp.output {
			fmt.Print(line)
			s.sendMessage(line)
		}
	}()

	return
}

func (s *session) sendMessage(message string) {
	s.conn.Write(message)
}

func (s *session) End() {
	s.sp.kill()
}
<|endoftext|>