<commit_before>// Copyright 2015 The Vanadium 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 profiles

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

	"v.io/jiri/project"
	"v.io/jiri/tool"
	"v.io/jiri/util"
	"v.io/x/lib/envvar"
)

// GoFlags lists all of the Go environment variables and will be sorted in the
// init function for this package.
var GoFlags = []string{
	"CC",
	"CC_FOR_TARGET",
	"CGO_ENABLED",
	"CXX_FOR_TARGET",
	"GO15VENDOREXPERIMENT",
	"GOARCH",
	"GOBIN",
	"GOEXE",
	"GOGCCFLAGS",
	"GOHOSTARCH",
	"GOHOSTOS",
	"GOOS",
	"GOPATH",
	"GORACE",
	"GOROOT",
	"GOTOOLDIR",
}

type ProfilesMode bool

func (pm *ProfilesMode) Set(s string) error {
	v, err := strconv.ParseBool(s)
	*pm = ProfilesMode(v)
	return err
}

func (pm *ProfilesMode) Get() interface{} { return bool(*pm) }

func (pm *ProfilesMode) String() string { return fmt.Sprintf("%v", *pm) }

func (pm *ProfilesMode) IsBoolFlag() bool { return true }

const (
	UseProfiles  ProfilesMode = false
	SkipProfiles ProfilesMode = true
)

func init() {
	sort.Strings(GoFlags)
}

// UnsetGoEnv unsets Go environment variables in the given environment.
func UnsetGoEnv(env *envvar.Vars) {
	for _, k := range GoFlags {
		env.Set(k, "")
	}
}

// GoEnvironmentFromOS() returns the values of all Go environment variables
// as set via the OS; unset variables are omitted.
func GoEnvironmentFromOS() []string {
	os := envvar.SliceToMap(os.Environ())
	vars := make([]string, 0, len(GoFlags))
	for _, k := range GoFlags {
		v, present := os[k]
		if !present {
			continue
		}
		vars = append(vars, envvar.JoinKeyValue(k, v))
	}
	return vars
}

// ConfigHelper wraps the various sources of configuration and profile
// information to provide convenient methods for determing the environment
// variables to use for a given situation. It creates an initial copy of the OS
// environment that is mutated by its various methods.
type ConfigHelper struct {
	*envvar.Vars
	legacyMode   bool
	profilesMode bool
	root         string
	ctx          *tool.Context
	config       *util.Config
	projects     project.Projects
	tools        project.Tools
}

// NewConfigHelper creates a new config helper. If filename is of non-zero
// length then that file will be read as a profiles manifest file, if not, the
// existing, if any, in-memory profiles information will be used. If SkipProfiles
// is specified for profilesMode, then no profiles are used.
func NewConfigHelper(ctx *tool.Context, profilesMode ProfilesMode, filename string) (*ConfigHelper, error) {
	root, err := project.JiriRoot()
	if err != nil {
		return nil, err
	}
	config, err := util.LoadConfig(ctx)
	if err != nil {
		return nil, err
	}
	projects, tools, err := project.ReadManifest(ctx)
	if err != nil {
		return nil, err
	}
	if profilesMode == UseProfiles && len(filename) > 0 {
		if err := Read(ctx, filepath.Join(root, filename)); err != nil {
			return nil, err
		}
	}
	ch := &ConfigHelper{
		ctx:          ctx,
		root:         root,
		config:       config,
		projects:     projects,
		tools:        tools,
		profilesMode: bool(profilesMode),
	}
	if profilesMode == SkipProfiles {
		return ch, nil
	}
	ch.legacyMode = (SchemaVersion() == Original) || (len(os.Getenv("JIRI_PROFILE")) > 0)
	if ch.legacyMode {
		vars, err := util.JiriLegacyEnvironment(ch.ctx)
		if err != nil {
			return nil, err
		}
		ch.Vars = vars
	} else {
		ch.Vars = envvar.VarsFromOS()
	}
	return ch, nil
}

// Root returns the root of the jiri universe.
func (ch *ConfigHelper) Root() string {
	return ch.root
}

// LegacyProfiles returns true if the old-style profiles are being used.
func (ch *ConfigHelper) LegacyProfiles() bool {
	return ch.legacyMode
}

// SkippingProfiles returns true if no profiles are being used.
func (ch *ConfigHelper) SkippingProfiles() bool {
	return ch.profilesMode == bool(SkipProfiles)
}

// CommonConcatVariables returns a map of variables that are commonly
// used for the concat parameter to SetEnvFromProfilesAndTarget.
func CommonConcatVariables() map[string]string {
	return map[string]string{
		"PATH":         ":",
		"CCFLAGS":      " ",
		"CXXFLAGS":     " ",
		"LDFLAGS":      " ",
		"CGO_CFLAGS":   " ",
		"CGO_CXXFLAGS": " ",
		"CGO_LDFLAGS":  " ",
	}
}

// CommonIgnoreVariables returns a map of variables that are commonly
// used for the ignore parameter to SetEnvFromProfilesAndTarget.
func CommonIgnoreVariables() map[string]bool {
	return map[string]bool{
		"GOPATH": true,
		"GOARCH": true,
		"GOOS":   true,
	}
}

// SetEnvFromProfiles populates the embedded environment with the environment
// variables stored in the specified profiles for the specified target if
// new-style profiles are being used, otherwise it uses compiled in values as per
// the original profiles implementation.
// The profiles parameter contains a comma separated list of profile names; if the
// requested target does not exist for any of these profiles then those profiles
// will be ignored. The 'concat' parameter includes a map of variable names
// whose values are to concatenated with any existing ones rather than
// overwriting them (e.g. CFLAGS for example). The value of the concat map
// is the separator to use for that environment  variable (e.g. space for
// CFLAGs or ':' for PATH-like ones).
func (ch *ConfigHelper) SetEnvFromProfiles(concat map[string]string, ignore map[string]bool, profiles string, target Target) {
	if ch.profilesMode || ch.legacyMode {
		return
	}
	for _, profile := range strings.Split(profiles, ",") {
		t := LookupProfileTarget(profile, target)
		if t == nil {
			continue
		}
		for _, tmp := range t.Env.Vars {
			k, v := envvar.SplitKeyValue(tmp)
			if ignore[k] {
				continue
			}
			if sep := concat[k]; len(sep) > 0 {
				ov := ch.Vars.GetTokens(k, sep)
				nv := envvar.SplitTokens(v, sep)
				ch.Vars.SetTokens(k, append(ov, nv...), " ")
				continue
			}
			ch.Vars.Set(k, v)
		}
	}
}

// ValidateRequestProfilesAndTarget checks that the supplied slice of profiles
// names is supported and that each has the specified target installed taking
// account if runnin in bootstrap mode or with old-style profiles.
func (ch *ConfigHelper) ValidateRequestedProfilesAndTarget(profileNames []string, target Target) error {
	if !ch.profilesMode && !ch.legacyMode {
		return ValidateRequestedProfilesAndTarget(profileNames, target)
	}
	return nil
}

// PrependToPath prepends its argument to the PATH environment variable.
func (ch *ConfigHelper) PrependToPATH(path string) {
	existing := ch.GetTokens("PATH", ":")
	ch.SetTokens("PATH", append([]string{path}, existing...), ":")
}

// SetGoPath computes and sets the GOPATH environment variable based on the
// current jiri configuration.
func (ch *ConfigHelper) SetGoPath() {
	if !ch.profilesMode && !ch.legacyMode {
		ch.pathHelper("GOPATH", ch.root, ch.projects, ch.config.GoWorkspaces(), "")
	}
}

// SetVDLPath computes and sets the VDLPATH environment variable based on the
// current jiri configuration.
func (ch *ConfigHelper) SetVDLPath() {
	if !ch.profilesMode && !ch.legacyMode {
		ch.pathHelper("VDLPATH", ch.root, ch.projects, ch.config.VDLWorkspaces(), "src")
	}
}

// pathHelper is a utility function for determining paths for project workspaces.
func (ch *ConfigHelper) pathHelper(name, root string, projects project.Projects, workspaces []string, suffix string) {
	path := ch.GetTokens(name, ":")
	for _, workspace := range workspaces {
		absWorkspace := filepath.Join(root, workspace, suffix)
		// Only append an entry to the path if the workspace is rooted
		// under a jiri project that exists locally or vice versa.
		for _, project := range projects {
			// We check if <project.Path> is a prefix of <absWorkspace> to
			// account for Go workspaces nested under a single jiri project,
			// such as: $JIRI_ROOT/release/projects/chat/go.
			//
			// We check if <absWorkspace> is a prefix of <project.Path> to
			// account for Go workspaces that span multiple jiri projects,
			// such as: $JIRI_ROOT/release/go.
			if strings.HasPrefix(absWorkspace, project.Path) || strings.HasPrefix(project.Path, absWorkspace) {
				if _, err := ch.ctx.Run().Stat(filepath.Join(absWorkspace)); err == nil {
					path = append(path, absWorkspace)
					break
				}
			}
		}
	}
	ch.SetTokens(name, path, ":")
}

// MergeEnv merges vars with the variables in env taking care to concatenate
// values as per the concat and ignore parameters similarly to SetEnvFromProfiles.
func MergeEnv(concat map[string]string, ignore map[string]bool, env *envvar.Vars, vars ...[]string) {
	for _, ev := range vars {
		for _, tmp := range ev {
			k, v := envvar.SplitKeyValue(tmp)
			if ignore[k] {
				continue
			}
			if sep := concat[k]; len(sep) > 0 {
				ov := env.GetTokens(k, sep)
				nv := envvar.SplitTokens(v, sep)
				env.SetTokens(k, append(ov, nv...), " ")
				continue
			}
			env.Set(k, v)
		}
	}
}

// MergeEnvFromProfiles merges the environment variables stored in the specified
// profiles and target with the env parameter. It uses MergeEnv to do so.
func MergeEnvFromProfiles(concat map[string]string, ignore map[string]bool, env *envvar.Vars, target Target, profileNames ...string) ([]string, error) {
	vars := [][]string{}
	for _, name := range profileNames {
		t := LookupProfileTarget(name, target)
		if t == nil {
			return nil, fmt.Errorf("failed to lookup %v --target=%v", name, target)
		}
		vars = append(vars, t.Env.Vars)
	}
	MergeEnv(concat, ignore, env, vars...)
	return env.ToSlice(), nil
}
<commit_msg>v.io/x/devtools: fix nacl profile.<commit_after>// Copyright 2015 The Vanadium 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 profiles

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

	"v.io/jiri/project"
	"v.io/jiri/tool"
	"v.io/jiri/util"
	"v.io/x/lib/envvar"
)

// GoFlags lists all of the Go environment variables and will be sorted in the
// init function for this package.
var GoFlags = []string{
	"CC",
	"CC_FOR_TARGET",
	"CGO_ENABLED",
	"CXX_FOR_TARGET",
	"GO15VENDOREXPERIMENT",
	"GOARCH",
	"GOBIN",
	"GOEXE",
	"GOGCCFLAGS",
	"GOHOSTARCH",
	"GOHOSTOS",
	"GOOS",
	"GOPATH",
	"GORACE",
	"GOROOT",
	"GOTOOLDIR",
}

type ProfilesMode bool

func (pm *ProfilesMode) Set(s string) error {
	v, err := strconv.ParseBool(s)
	*pm = ProfilesMode(v)
	return err
}

func (pm *ProfilesMode) Get() interface{} { return bool(*pm) }

func (pm *ProfilesMode) String() string { return fmt.Sprintf("%v", *pm) }

func (pm *ProfilesMode) IsBoolFlag() bool { return true }

const (
	UseProfiles  ProfilesMode = false
	SkipProfiles ProfilesMode = true
)

func init() {
	sort.Strings(GoFlags)
}

// UnsetGoEnvVars unsets Go environment variables in the given environment.
func UnsetGoEnvVars(env *envvar.Vars) {
	for _, k := range GoFlags {
		env.Delete(k)
	}
}

// UnsetGoEnvMap unsets Go environment variables in the given environment.
func UnsetGoEnvMap(env map[string]string) {
	for _, k := range GoFlags {
		delete(env, k)
	}
}

// GoEnvironmentFromOS() returns the values of all Go environment variables
// as set via the OS; unset variables are omitted.
func GoEnvironmentFromOS() []string {
	os := envvar.SliceToMap(os.Environ())
	vars := make([]string, 0, len(GoFlags))
	for _, k := range GoFlags {
		v, present := os[k]
		if !present {
			continue
		}
		vars = append(vars, envvar.JoinKeyValue(k, v))
	}
	return vars
}

// ConfigHelper wraps the various sources of configuration and profile
// information to provide convenient methods for determing the environment
// variables to use for a given situation. It creates an initial copy of the OS
// environment that is mutated by its various methods.
type ConfigHelper struct {
	*envvar.Vars
	legacyMode   bool
	profilesMode bool
	root         string
	ctx          *tool.Context
	config       *util.Config
	projects     project.Projects
	tools        project.Tools
}

// NewConfigHelper creates a new config helper. If filename is of non-zero
// length then that file will be read as a profiles manifest file, if not, the
// existing, if any, in-memory profiles information will be used. If SkipProfiles
// is specified for profilesMode, then no profiles are used.
func NewConfigHelper(ctx *tool.Context, profilesMode ProfilesMode, filename string) (*ConfigHelper, error) {
	root, err := project.JiriRoot()
	if err != nil {
		return nil, err
	}
	config, err := util.LoadConfig(ctx)
	if err != nil {
		return nil, err
	}
	projects, tools, err := project.ReadManifest(ctx)
	if err != nil {
		return nil, err
	}
	if profilesMode == UseProfiles && len(filename) > 0 {
		if err := Read(ctx, filepath.Join(root, filename)); err != nil {
			return nil, err
		}
	}
	ch := &ConfigHelper{
		ctx:          ctx,
		root:         root,
		config:       config,
		projects:     projects,
		tools:        tools,
		profilesMode: bool(profilesMode),
	}
	if profilesMode == SkipProfiles {
		return ch, nil
	}
	ch.legacyMode = (SchemaVersion() == Original) || (len(os.Getenv("JIRI_PROFILE")) > 0)
	if ch.legacyMode {
		vars, err := util.JiriLegacyEnvironment(ch.ctx)
		if err != nil {
			return nil, err
		}
		ch.Vars = vars
	} else {
		ch.Vars = envvar.VarsFromOS()
	}
	return ch, nil
}

// Root returns the root of the jiri universe.
func (ch *ConfigHelper) Root() string {
	return ch.root
}

// LegacyProfiles returns true if the old-style profiles are being used.
func (ch *ConfigHelper) LegacyProfiles() bool {
	return ch.legacyMode
}

// SkippingProfiles returns true if no profiles are being used.
func (ch *ConfigHelper) SkippingProfiles() bool {
	return ch.profilesMode == bool(SkipProfiles)
}

// CommonConcatVariables returns a map of variables that are commonly
// used for the concat parameter to SetEnvFromProfilesAndTarget.
func CommonConcatVariables() map[string]string {
	return map[string]string{
		"PATH":         ":",
		"CCFLAGS":      " ",
		"CXXFLAGS":     " ",
		"LDFLAGS":      " ",
		"CGO_CFLAGS":   " ",
		"CGO_CXXFLAGS": " ",
		"CGO_LDFLAGS":  " ",
	}
}

// CommonIgnoreVariables returns a map of variables that are commonly
// used for the ignore parameter to SetEnvFromProfilesAndTarget.
func CommonIgnoreVariables() map[string]bool {
	return map[string]bool{
		"GOPATH": true,
		"GOARCH": true,
		"GOOS":   true,
	}
}

// SetEnvFromProfiles populates the embedded environment with the environment
// variables stored in the specified profiles for the specified target if
// new-style profiles are being used, otherwise it uses compiled in values as per
// the original profiles implementation.
// The profiles parameter contains a comma separated list of profile names; if the
// requested target does not exist for any of these profiles then those profiles
// will be ignored. The 'concat' parameter includes a map of variable names
// whose values are to concatenated with any existing ones rather than
// overwriting them (e.g. CFLAGS for example). The value of the concat map
// is the separator to use for that environment  variable (e.g. space for
// CFLAGs or ':' for PATH-like ones).
func (ch *ConfigHelper) SetEnvFromProfiles(concat map[string]string, ignore map[string]bool, profiles string, target Target) {
	if ch.profilesMode || ch.legacyMode {
		return
	}
	for _, profile := range strings.Split(profiles, ",") {
		t := LookupProfileTarget(profile, target)
		if t == nil {
			continue
		}
		for _, tmp := range t.Env.Vars {
			k, v := envvar.SplitKeyValue(tmp)
			if ignore[k] {
				continue
			}
			if sep := concat[k]; len(sep) > 0 {
				ov := ch.Vars.GetTokens(k, sep)
				nv := envvar.SplitTokens(v, sep)
				ch.Vars.SetTokens(k, append(ov, nv...), " ")
				continue
			}
			ch.Vars.Set(k, v)
		}
	}
}

// ValidateRequestProfilesAndTarget checks that the supplied slice of profiles
// names is supported and that each has the specified target installed taking
// account if runnin in bootstrap mode or with old-style profiles.
func (ch *ConfigHelper) ValidateRequestedProfilesAndTarget(profileNames []string, target Target) error {
	if !ch.profilesMode && !ch.legacyMode {
		return ValidateRequestedProfilesAndTarget(profileNames, target)
	}
	return nil
}

// PrependToPath prepends its argument to the PATH environment variable.
func (ch *ConfigHelper) PrependToPATH(path string) {
	existing := ch.GetTokens("PATH", ":")
	ch.SetTokens("PATH", append([]string{path}, existing...), ":")
}

// SetGoPath computes and sets the GOPATH environment variable based on the
// current jiri configuration.
func (ch *ConfigHelper) SetGoPath() {
	if !ch.profilesMode && !ch.legacyMode {
		ch.pathHelper("GOPATH", ch.root, ch.projects, ch.config.GoWorkspaces(), "")
	}
}

// SetVDLPath computes and sets the VDLPATH environment variable based on the
// current jiri configuration.
func (ch *ConfigHelper) SetVDLPath() {
	if !ch.profilesMode && !ch.legacyMode {
		ch.pathHelper("VDLPATH", ch.root, ch.projects, ch.config.VDLWorkspaces(), "src")
	}
}

// pathHelper is a utility function for determining paths for project workspaces.
func (ch *ConfigHelper) pathHelper(name, root string, projects project.Projects, workspaces []string, suffix string) {
	path := ch.GetTokens(name, ":")
	for _, workspace := range workspaces {
		absWorkspace := filepath.Join(root, workspace, suffix)
		// Only append an entry to the path if the workspace is rooted
		// under a jiri project that exists locally or vice versa.
		for _, project := range projects {
			// We check if <project.Path> is a prefix of <absWorkspace> to
			// account for Go workspaces nested under a single jiri project,
			// such as: $JIRI_ROOT/release/projects/chat/go.
			//
			// We check if <absWorkspace> is a prefix of <project.Path> to
			// account for Go workspaces that span multiple jiri projects,
			// such as: $JIRI_ROOT/release/go.
			if strings.HasPrefix(absWorkspace, project.Path) || strings.HasPrefix(project.Path, absWorkspace) {
				if _, err := ch.ctx.Run().Stat(filepath.Join(absWorkspace)); err == nil {
					path = append(path, absWorkspace)
					break
				}
			}
		}
	}
	ch.SetTokens(name, path, ":")
}

// MergeEnv merges vars with the variables in env taking care to concatenate
// values as per the concat and ignore parameters similarly to SetEnvFromProfiles.
func MergeEnv(concat map[string]string, ignore map[string]bool, env *envvar.Vars, vars ...[]string) {
	for _, ev := range vars {
		for _, tmp := range ev {
			k, v := envvar.SplitKeyValue(tmp)
			if ignore[k] {
				continue
			}
			if sep := concat[k]; len(sep) > 0 {
				ov := env.GetTokens(k, sep)
				nv := envvar.SplitTokens(v, sep)
				env.SetTokens(k, append(ov, nv...), " ")
				continue
			}
			env.Set(k, v)
		}
	}
}

// MergeEnvFromProfiles merges the environment variables stored in the specified
// profiles and target with the env parameter. It uses MergeEnv to do so.
func MergeEnvFromProfiles(concat map[string]string, ignore map[string]bool, env *envvar.Vars, target Target, profileNames ...string) ([]string, error) {
	vars := [][]string{}
	for _, name := range profileNames {
		t := LookupProfileTarget(name, target)
		if t == nil {
			return nil, fmt.Errorf("failed to lookup %v --target=%v", name, target)
		}
		vars = append(vars, t.Env.Vars)
	}
	MergeEnv(concat, ignore, env, vars...)
	return env.ToSlice(), nil
}
<|endoftext|>


<commit_before>package dual

import (
	"fmt"
	"math"
	"strings"

	"github.com/meirizarrygelpi/qtr"
)

// A Hamilton represents a dual Hamilton quaternion as an ordered array of two
// pointers to qtr.Hamilton values.
type Hamilton [2]*qtr.Hamilton

var (
	// Symbols for the canonical dual Hamilton quaternion basis.
	symbHamilton = [8]string{"", "i", "j", "k", "ε", "εi", "εj", "εk"}
)

// String returns the string version of a Hamilton value. If z corresponds to
// the dual Hamilton quaternion a + bi + cj + dk + eε + fεi + gεj + hεk, then
// the string is "(a+bi+cj+dk+eε+fεi+gεj+hεk)", similar to complex128 values.
func (z *Hamilton) String() string {
	v := make([]float64, 8)
	v[0], v[1], v[2], v[3] = (z[0])[0], (z[0])[1], (z[0])[2], (z[0])[3]
	v[4], v[5], v[6], v[7] = (z[1])[0], (z[1])[1], (z[1])[2], (z[1])[3]
	a := make([]string, 17)
	a[0] = "("
	a[1] = fmt.Sprintf("%g", v[0])
	i := 1
	for j := 2; j < 16; j = j + 2 {
		switch {
		case math.Signbit(v[i]):
			a[j] = fmt.Sprintf("%g", v[i])
		case math.IsInf(v[i], +1):
			a[j] = "+Inf"
		default:
			a[j] = fmt.Sprintf("+%g", v[i])
		}
		a[j+1] = symbHamilton[i]
		i++
	}
	a[16] = ")"
	return strings.Join(a, "")
}

// Equals returns true if z and y are equal.
func (z *Hamilton) Equals(y *Hamilton) bool {
	if !z[0].Equals(y[0]) || !z[1].Equals(y[1]) {
		return false
	}
	return true
}

// Copy copies y onto z, and returns z.
func (z *Hamilton) Copy(y *Hamilton) *Hamilton {
	z[0] = new(qtr.Hamilton).Copy(y[0])
	z[1] = new(qtr.Hamilton).Copy(y[1])
	return z
}

// NewHamilton returns a pointer to a Hamilton value made from eight given
// float64 values.
func NewHamilton(a, b, c, d, e, f, g, h float64) *Hamilton {
	z := new(Hamilton)
	z[0] = qtr.NewHamilton(a, b, c, d)
	z[1] = qtr.NewHamilton(e, f, g, h)
	return z
}

// IsHamiltonInf returns true if any of the components of z are infinite.
func (z *Hamilton) IsHamiltonInf() bool {
	if z[0].IsHamiltonInf() || z[1].IsHamiltonInf() {
		return true
	}
	return false
}

// HamiltonInf returns a pointer to a dual Hamilton quaternion infinity value.
func HamiltonInf(a, b, c, d, e, f, g, h int) *Hamilton {
	z := new(Hamilton)
	z[0] = qtr.HamiltonInf(a, b, c, d)
	z[1] = qtr.HamiltonInf(e, f, g, h)
	return z
}

// IsHamiltonNaN returns true if any component of z is NaN and neither is an
// infinity.
func (z *Hamilton) IsHamiltonNaN() bool {
	if z[0].IsHamiltonInf() || z[1].IsHamiltonInf() {
		return false
	}
	if z[0].IsHamiltonNaN() || z[1].IsHamiltonNaN() {
		return true
	}
	return false
}

// HamiltonNaN returns a pointer to a dual Hamilton quaternion NaN value.
func HamiltonNaN() *Hamilton {
	z := new(Hamilton)
	z[0] = qtr.HamiltonNaN()
	z[1] = qtr.HamiltonNaN()
	return z
}

// ScalR sets z equal to y scaled by a on the right, and returns z.
//
// This is a special case of Mul:
// 		ScalR(y, a) = Mul(y, Hamilton{a, 0})
func (z *Hamilton) ScalR(y *Hamilton, a *qtr.Hamilton) *Hamilton {
	z[0] = new(qtr.Hamilton).Mul(y[0], a)
	z[1] = new(qtr.Hamilton).Mul(y[1], a)
	return z
}

// ScalL sets z equal to y scaled by a on the left, and returns z.
//
// This is a special case of Mul:
// 		ScalL(y, a) = Mul(Hamilton{a, 0}, y)
func (z *Hamilton) ScalL(a *qtr.Hamilton, y *Hamilton) *Hamilton {
	z[0] = new(qtr.Hamilton).Mul(a, y[0])
	z[1] = new(qtr.Hamilton).Mul(a, y[1])
	return z
}

// Dil sets z equal to the dilation of y by a, and returns z.
//
// This is a special case of Mul:
// 		Dil(y, a) = Mul(y, Hamilton{qtr.Hamilton{a, 0, 0, 0}, 0})
func (z *Hamilton) Dil(y *Hamilton, a float64) *Hamilton {
	z[0] = new(qtr.Hamilton).Scal(y[0], a)
	z[1] = new(qtr.Hamilton).Scal(y[1], a)
	return z
}

// Neg sets z equal to the negative of y, and returns z.
func (z *Hamilton) Neg(y *Hamilton) *Hamilton {
	return z.Dil(y, -1)
}

// Conj sets z equal to the conjugate of y, and returns z.
func (z *Hamilton) Conj(y *Hamilton) *Hamilton {
	z[0] = new(qtr.Hamilton).Conj(y[0])
	z[1] = new(qtr.Hamilton).Neg(y[1])
	return z
}

// Add sets z equal to the sum of x and y, and returns z.
func (z *Hamilton) Add(x, y *Hamilton) *Hamilton {
	z[0] = new(qtr.Hamilton).Add(x[0], y[0])
	z[1] = new(qtr.Hamilton).Add(x[1], y[1])
	return z
}

// Sub sets z equal to the difference of x and y, and returns z.
func (z *Hamilton) Sub(x, y *Hamilton) *Hamilton {
	z[0] = new(qtr.Hamilton).Sub(x[0], y[0])
	z[1] = new(qtr.Hamilton).Sub(x[1], y[1])
	return z
}

// Mul sets z equal to the product of x and y, and returns z.
//
// The basic rules are:
// 		i * i = j * j = k * k = -1
// 		i * j = -j * i = k
// 		j * k = -k * j = i
// 		k * i = -i * k = j
// 		ε * ε = 0
// 		ε * i = i * ε = εi
// 		ε * j = j * ε = εj
// 		ε * k = k * ε = εk
// 		εi * i = i * εi = -ε
// 		εj * j = j * εj = -ε
// 		εk * k = k * εk = -ε
// 		εi * j = -j * εi = εk
// 		εj * k = -k * εj = εi
// 		εk * i = -i * εk = εj
// 		ε * εi = εi * ε = 0
// 		ε * εj = εj * ε = 0
// 		ε * εk = εk * ε = 0
// 		εi * εi = εj * εj = εk * εk = 0
// 		εi * εj = εj * εi = 0
// 		εi * εk = εk * εi = 0
// 		εj * εk = εk * εj = 0
// 		εj * εk = εk * εj = 0
// This multiplication rule is noncommutative and nonassociative.
func (z *Hamilton) Mul(x, y *Hamilton) *Hamilton {
	p := new(Hamilton).Copy(x)
	q := new(Hamilton).Copy(y)
	z[0] = new(qtr.Hamilton).Mul(p[0], q[0])
	z[1] = new(qtr.Hamilton).Add(
		new(qtr.Hamilton).Mul(q[1], p[0]),
		new(qtr.Hamilton).Mul(p[1], q[0].Conj(q[0])),
	)
	return z
}

// Commutator sets z equal to the commutator of x and y, and returns z.
func (z *Hamilton) Commutator(x, y *Hamilton) *Hamilton {
	return z.Sub(new(Hamilton).Mul(x, y), new(Hamilton).Mul(y, x))
}

// Associator sets z equal to the associator of w, x, and y, and returns z.
func (z *Hamilton) Associator(w, x, y *Hamilton) *Hamilton {
	return z.Sub(
		new(Hamilton).Mul(new(Hamilton).Mul(w, x), y),
		new(Hamilton).Mul(w, new(Hamilton).Mul(x, y)),
	)
}

// Quad returns the quadrance of z, a float64 value.
func (z *Hamilton) Quad() float64 {
	return z[0].Quad()
}

// IsZeroDiv returns true if z is a zero divisor. This is equivalent to
// z being nilpotent (i.e. z² = 0).
func (z *Hamilton) IsZeroDiv() bool {
	return !z[0].Equals(&qtr.Hamilton{0, 0, 0, 0})
}
<commit_msg>Rename qtr as quat<commit_after>package dual

import (
	"fmt"
	"math"
	"strings"

	"github.com/meirizarrygelpi/quat"
)

// A Hamilton represents a dual Hamilton quaternion as an ordered array of two
// pointers to quat.Hamilton values.
type Hamilton [2]*quat.Hamilton

var (
	// Symbols for the canonical dual Hamilton quaternion basis.
	symbHamilton = [8]string{"", "i", "j", "k", "ε", "εi", "εj", "εk"}
)

// String returns the string version of a Hamilton value. If z corresponds to
// the dual Hamilton quaternion a + bi + cj + dk + eε + fεi + gεj + hεk, then
// the string is "(a+bi+cj+dk+eε+fεi+gεj+hεk)", similar to complex128 values.
func (z *Hamilton) String() string {
	v := make([]float64, 8)
	v[0], v[1] = real((z[0])[0]), imag((z[0])[0])
	v[2], v[3] = real((z[0])[1]), imag((z[0])[1])
	v[4], v[5] = real((z[1])[0]), imag((z[1])[0])
	v[6], v[7] = real((z[1])[1]), imag((z[1])[1])
	a := make([]string, 17)
	a[0] = "("
	a[1] = fmt.Sprintf("%g", v[0])
	i := 1
	for j := 2; j < 16; j = j + 2 {
		switch {
		case math.Signbit(v[i]):
			a[j] = fmt.Sprintf("%g", v[i])
		case math.IsInf(v[i], +1):
			a[j] = "+Inf"
		default:
			a[j] = fmt.Sprintf("+%g", v[i])
		}
		a[j+1] = symbHamilton[i]
		i++
	}
	a[16] = ")"
	return strings.Join(a, "")
}

// Equals returns true if z and y are equal.
func (z *Hamilton) Equals(y *Hamilton) bool {
	if !z[0].Equals(y[0]) || !z[1].Equals(y[1]) {
		return false
	}
	return true
}

// Copy copies y onto z, and returns z.
func (z *Hamilton) Copy(y *Hamilton) *Hamilton {
	z[0] = new(quat.Hamilton).Copy(y[0])
	z[1] = new(quat.Hamilton).Copy(y[1])
	return z
}

// NewHamilton returns a pointer to a Hamilton value made from eight given
// float64 values.
func NewHamilton(a, b, c, d, e, f, g, h float64) *Hamilton {
	z := new(Hamilton)
	z[0] = quat.NewHamilton(a, b, c, d)
	z[1] = quat.NewHamilton(e, f, g, h)
	return z
}

// IsHamiltonInf returns true if any of the components of z are infinite.
func (z *Hamilton) IsHamiltonInf() bool {
	if z[0].IsHamiltonInf() || z[1].IsHamiltonInf() {
		return true
	}
	return false
}

// HamiltonInf returns a pointer to a dual Hamilton quaternion infinity value.
func HamiltonInf(a, b, c, d, e, f, g, h int) *Hamilton {
	z := new(Hamilton)
	z[0] = quat.HamiltonInf(a, b, c, d)
	z[1] = quat.HamiltonInf(e, f, g, h)
	return z
}

// IsHamiltonNaN returns true if any component of z is NaN and neither is an
// infinity.
func (z *Hamilton) IsHamiltonNaN() bool {
	if z[0].IsHamiltonInf() || z[1].IsHamiltonInf() {
		return false
	}
	if z[0].IsHamiltonNaN() || z[1].IsHamiltonNaN() {
		return true
	}
	return false
}

// HamiltonNaN returns a pointer to a dual Hamilton quaternion NaN value.
func HamiltonNaN() *Hamilton {
	z := new(Hamilton)
	z[0] = quat.HamiltonNaN()
	z[1] = quat.HamiltonNaN()
	return z
}

// ScalR sets z equal to y scaled by a on the right, and returns z.
//
// This is a special case of Mul:
// 		ScalR(y, a) = Mul(y, Hamilton{a, 0})
func (z *Hamilton) ScalR(y *Hamilton, a *quat.Hamilton) *Hamilton {
	z[0] = new(quat.Hamilton).Mul(y[0], a)
	z[1] = new(quat.Hamilton).Mul(y[1], a)
	return z
}

// ScalL sets z equal to y scaled by a on the left, and returns z.
//
// This is a special case of Mul:
// 		ScalL(y, a) = Mul(Hamilton{a, 0}, y)
func (z *Hamilton) ScalL(a *quat.Hamilton, y *Hamilton) *Hamilton {
	z[0] = new(quat.Hamilton).Mul(a, y[0])
	z[1] = new(quat.Hamilton).Mul(a, y[1])
	return z
}

// Dil sets z equal to the dilation of y by a, and returns z.
//
// This is a special case of Mul:
// 		Dil(y, a) = Mul(y, Hamilton{quat.Hamilton{a, 0, 0, 0}, 0})
func (z *Hamilton) Dil(y *Hamilton, a float64) *Hamilton {
	z[0] = new(quat.Hamilton).Dil(y[0], a)
	z[1] = new(quat.Hamilton).Dil(y[1], a)
	return z
}

// Neg sets z equal to the negative of y, and returns z.
func (z *Hamilton) Neg(y *Hamilton) *Hamilton {
	return z.Dil(y, -1)
}

// Conj sets z equal to the conjugate of y, and returns z.
func (z *Hamilton) Conj(y *Hamilton) *Hamilton {
	z[0] = new(quat.Hamilton).Conj(y[0])
	z[1] = new(quat.Hamilton).Neg(y[1])
	return z
}

// Add sets z equal to the sum of x and y, and returns z.
func (z *Hamilton) Add(x, y *Hamilton) *Hamilton {
	z[0] = new(quat.Hamilton).Add(x[0], y[0])
	z[1] = new(quat.Hamilton).Add(x[1], y[1])
	return z
}

// Sub sets z equal to the difference of x and y, and returns z.
func (z *Hamilton) Sub(x, y *Hamilton) *Hamilton {
	z[0] = new(quat.Hamilton).Sub(x[0], y[0])
	z[1] = new(quat.Hamilton).Sub(x[1], y[1])
	return z
}

// Mul sets z equal to the product of x and y, and returns z.
//
// The basic rules are:
// 		i * i = j * j = k * k = -1
// 		i * j = -j * i = k
// 		j * k = -k * j = i
// 		k * i = -i * k = j
// 		ε * ε = 0
// 		ε * i = i * ε = εi
// 		ε * j = j * ε = εj
// 		ε * k = k * ε = εk
// 		εi * i = i * εi = -ε
// 		εj * j = j * εj = -ε
// 		εk * k = k * εk = -ε
// 		εi * j = -j * εi = εk
// 		εj * k = -k * εj = εi
// 		εk * i = -i * εk = εj
// 		ε * εi = εi * ε = 0
// 		ε * εj = εj * ε = 0
// 		ε * εk = εk * ε = 0
// 		εi * εi = εj * εj = εk * εk = 0
// 		εi * εj = εj * εi = 0
// 		εi * εk = εk * εi = 0
// 		εj * εk = εk * εj = 0
// 		εj * εk = εk * εj = 0
// This multiplication rule is noncommutative and nonassociative.
func (z *Hamilton) Mul(x, y *Hamilton) *Hamilton {
	p := new(Hamilton).Copy(x)
	q := new(Hamilton).Copy(y)
	z[0] = new(quat.Hamilton).Mul(p[0], q[0])
	z[1] = new(quat.Hamilton).Add(
		new(quat.Hamilton).Mul(q[1], p[0]),
		new(quat.Hamilton).Mul(p[1], q[0].Conj(q[0])),
	)
	return z
}

// Commutator sets z equal to the commutator of x and y, and returns z.
func (z *Hamilton) Commutator(x, y *Hamilton) *Hamilton {
	return z.Sub(new(Hamilton).Mul(x, y), new(Hamilton).Mul(y, x))
}

// Associator sets z equal to the associator of w, x, and y, and returns z.
func (z *Hamilton) Associator(w, x, y *Hamilton) *Hamilton {
	return z.Sub(
		new(Hamilton).Mul(new(Hamilton).Mul(w, x), y),
		new(Hamilton).Mul(w, new(Hamilton).Mul(x, y)),
	)
}

// Quad returns the quadrance of z, a float64 value.
func (z *Hamilton) Quad() float64 {
	return z[0].Quad()
}

// IsZeroDiv returns true if z is a zero divisor. This is equivalent to
// z being nilpotent (i.e. z² = 0).
func (z *Hamilton) IsZeroDiv() bool {
	return !z[0].Equals(&quat.Hamilton{0, 0})
}
<|endoftext|>


<commit_before>package upload

import (
	"github.com/emicklei/go-restful"
	"github.com/inconshreveable/log15"
	"github.com/materials-commons/mcstore/pkg/app"
	"github.com/materials-commons/mcstore/pkg/app/flow"
	"github.com/materials-commons/mcstore/pkg/db/schema"
	"github.com/materials-commons/mcstore/pkg/ws/rest"
	"github.com/materials-commons/mcstore/server/mcstored/service/uploads"
)

// An uploadResource handles all upload requests.
type uploadResource struct {
	uploader  *uploader
	log       log15.Logger
	factory   AssemblerFactory
	idService uploads.IDService
}

// NewResources creates a new upload resource
func NewResource(uploader *uploader, factory AssemblerFactory, idService uploads.IDService) rest.Service {
	return &uploadResource{
		uploader:  uploader,
		log:       app.NewLog("resource", "upload"),
		factory:   factory,
		idService: idService,
	}
}

// WebService creates an instance of the upload web service.
func (r *uploadResource) WebService() *restful.WebService {
	ws := new(restful.WebService)

	ws.Path("/upload").Produces(restful.MIME_JSON).Consumes(restful.MIME_JSON)
	ws.Route(ws.POST("").To(rest.RouteHandler(r.createUploadRequest)).
		Doc("Creates a new upload request").
		Reads(uploadCreateRequest{}).
		Writes(uploadCreateResponse{}))
	ws.Route(ws.POST("/chunk").To(rest.RouteHandler1(r.uploadFileChunk)).
		Consumes("multipart/form-data").
		Doc("Upload a file chunk"))

	return ws
}

// uploadFileChunk uploads a new file chunk.
func (r *uploadResource) uploadFileChunk(request *restful.Request, response *restful.Response, user schema.User) error {
	flowRequest, err := form2FlowRequest(request)
	if err != nil {
		r.log.Error(app.Logf("Error converting form to flow.Request: %s", err))
		return err
	}

	if err := r.uploader.processRequest(flowRequest); err != nil {
		return err
	}

	if r.uploader.allBlocksUploaded(flowRequest) {
		go r.assembler(flowRequest)
	}

	return nil
}

// assembler builds a new Assembler to assemble the pieces of the file.
func (r *uploadResource) assembler(request *flow.Request) {
	if assembler := r.factory.Assembler(request, ""); assembler != nil {
		assembler.Assemble()
	}
}
<commit_msg>Use uploads.UploadService.<commit_after>package upload

import (
	"github.com/emicklei/go-restful"
	"github.com/inconshreveable/log15"
	"github.com/materials-commons/mcstore/pkg/app"
	"github.com/materials-commons/mcstore/pkg/db/schema"
	"github.com/materials-commons/mcstore/pkg/ws/rest"
	"github.com/materials-commons/mcstore/server/mcstored/service/uploads"
)

// An uploadResource handles all upload requests.
type uploadResource struct {
	log           log15.Logger
	idService     uploads.IDService
	uploadService uploads.UploadService
}

// NewResources creates a new upload resource
func NewResource(uploadService uploads.UploadService, idService uploads.IDService) rest.Service {
	return &uploadResource{
		log:           app.NewLog("resource", "upload"),
		idService:     idService,
		uploadService: uploadService,
	}
}

// WebService creates an instance of the upload web service.
func (r *uploadResource) WebService() *restful.WebService {
	ws := new(restful.WebService)

	ws.Path("/upload").Produces(restful.MIME_JSON).Consumes(restful.MIME_JSON)
	ws.Route(ws.POST("").To(rest.RouteHandler(r.createUploadRequest)).
		Doc("Creates a new upload request").
		Reads(uploadCreateRequest{}).
		Writes(uploadCreateResponse{}))
	ws.Route(ws.POST("/chunk").To(rest.RouteHandler1(r.uploadFileChunk)).
		Consumes("multipart/form-data").
		Doc("Upload a file chunk"))

	return ws
}

// uploadFileChunk uploads a new file chunk.
func (r *uploadResource) uploadFileChunk(request *restful.Request, response *restful.Response, user schema.User) error {
	flowRequest, err := form2FlowRequest(request)
	if err != nil {
		r.log.Error(app.Logf("Error converting form to flow.Request: %s", err))
		return err
	}

	req := uploads.UploadRequest{
		Request: flowRequest,
		Owner:   "jfadams@umich.edu",
	}
	return r.uploadService.Upload(&req)
}
<|endoftext|>


<commit_before>package main

// A comment

/* Multi
  line
  comment
*/

// To run type at terminal: go run language.go

import "fmt" // Input and output

// Program entry point
func main() {
	fmt.Println("Hello, World!"); // At terminal you can godoc fmt Println for more info

	// Data Types:
	// Integers: uint, uint8, uint16, uint32, uint64, int, int8, int16, int32, int64
	// String: string
	// Booelan: bool

	// Variables (statically typed: defined and cannot be changed)

	// uint8 is 2^8 possible values, so 0 to 255

	// Declare, then initialize
	var aUint8 uint8
	aUint8 = 0

	// Declare and initialize
	var anInt int = 1

	// Type defined implicitly
	someNumber := 33
	someFloat := 100.01

	// String
	var someString string = "My String"  // We can use

	// Constant
	const HOURS_PER_DAY int = 24

	// Multiple variable declaration
	var (
		fingers = 5
		hands = 2
		lengthOfSomeString = len(someString)
	)

	// Math: +, -, *, /, %
	fingers = fingers + 5 - 5 / 1
	hands = (4 % hands) + 2

	// Printing to console
	fmt.Println(aUint8, " ", anInt, " ", someNumber, " ", someFloat, " ", someString, " ", lengthOfSomeString)
	// Output: 0 1 33 100.01 My String 9

	fmt.Println("Concatenating this string " + "and this other string.")

	fmt.Printf("Humans have %d fingers, %d hands, and %.2f average IQ\n", fingers, hands, someFloat)
	// Output: Humans have 5 fingers, 2 hands, and 100.01 average IQ

	// Output the data type
	fmt.Printf("aUint8 is of type %T\n", aUint8)

	// Output as binary
	fmt.Printf("someNumber in binary is %b \n", someNumber) // 100001
	fmt.Printf("The code 64 code is character %c \n", 64) // @

	// Arrays
	var cards[2] int
	cards[0] = 1
	cards[1] = 2

	cards2 := [2]int {1, 2} // Same as above

	// Going through the array, considering index
	for index, value := range cards {
		fmt.Println(value, index)
	}
	/* Output:
		1 0
		2 1
	*/

	for _, value := range cards2 {
		fmt.Println(value)
	}

	// Logical operators:
	// - And: &&
	// - Or: ||
	// - Not: !

	// Relational operators:
	// - Equality: ==
	// - Not Equal: !=
	// - Less Than: <
	// ... : >, <=, >=

	// For loops
	i:=0
	for i < 10 {
		i++
	}

	for j:=0; j<10; j++ {
		// Stuff to do
	}

	// If else
	if 5 >= 2 {
		// This line runs
	} else {
		// Does not run
	}

	// if else if
	if 4 > 4 {
		// This line doesn't run
	} else if ( 2 < 4) {
		// This line runs
	} else {
		// This line doesn't run
	}

	// switch
	switch(5) {
		case 1: // does not run
		case 5: // this line runs
		default: // does not run
	}

	// Slices: like an array, but no size specified
	slice := []int {0, 1, 2, 3, 4, 5}
	firstSlice := slice[0:3] // 0 up to but not including 3rd index, so length is 3 and has {0, 1, 2}
	fmt.Println(firstSlice[0], " ", firstSlice[2]) // 0 2
	fmt.Println(slice[:3]) // From 0 to but not including 3rd index.  Output: [0, 1, 2]
	fmt.Println(slice[2:]) // From 2 index to end. Output: [2, 3, 4, 5]

	zeroForFirst := 3
	size := 9
	notDefinedValuesSlice := make([]int, zeroForFirst, size)
	fmt.Println(notDefinedValuesSlice[0:]) // [0 0 0]

	copy(notDefinedValuesSlice, slice) // Copy values from slice to notDefinedValuesSlice
	fmt.Println(notDefinedValuesSlice[0:]) // [0 1 2]

	notDefinedValuesSlice = append(notDefinedValuesSlice, 4, 5, 6)
	fmt.Println(notDefinedValuesSlice[0:]) // [0 1 2 4 5 6]

	// Maps

}<commit_msg>Added map<commit_after>package main

// A comment

/* Multi
  line
  comment
*/

// To run type at terminal: go run language.go

import "fmt" // Input and output

// Program entry point
func main() {
	fmt.Println("Hello, World!"); // At terminal you can godoc fmt Println for more info

	// Data Types:
	// Integers: uint, uint8, uint16, uint32, uint64, int, int8, int16, int32, int64
	// String: string
	// Booelan: bool

	// Variables (statically typed: defined and cannot be changed)

	// uint8 is 2^8 possible values, so 0 to 255

	// Declare, then initialize
	var aUint8 uint8
	aUint8 = 0

	// Declare and initialize
	var anInt int = 1

	// Type defined implicitly
	someNumber := 33
	someFloat := 100.01

	// String
	var someString string = "My String"  // We can use

	// Constant
	const HOURS_PER_DAY int = 24

	// Multiple variable declaration
	var (
		fingers = 5
		hands = 2
		lengthOfSomeString = len(someString)
	)

	// Math: +, -, *, /, %
	fingers = fingers + 5 - 5 / 1
	hands = (4 % hands) + 2

	// Printing to console
	fmt.Println(aUint8, " ", anInt, " ", someNumber, " ", someFloat, " ", someString, " ", lengthOfSomeString)
	// Output: 0 1 33 100.01 My String 9

	fmt.Println("Concatenating this string " + "and this other string.")

	fmt.Printf("Humans have %d fingers, %d hands, and %.2f average IQ\n", fingers, hands, someFloat)
	// Output: Humans have 5 fingers, 2 hands, and 100.01 average IQ

	// Output the data type
	fmt.Printf("aUint8 is of type %T\n", aUint8)

	// Output as binary
	fmt.Printf("someNumber in binary is %b \n", someNumber) // 100001
	fmt.Printf("The code 64 code is character %c \n", 64) // @

	// Arrays
	var cards[2] int
	cards[0] = 1
	cards[1] = 2

	cards2 := [2]int {1, 2} // Same as above

	// Going through the array, considering index
	for index, value := range cards {
		fmt.Println(value, index)
	}
	/* Output:
		1 0
		2 1
	*/

	for _, value := range cards2 {
		fmt.Println(value)
	}

	// Logical operators:
	// - And: &&
	// - Or: ||
	// - Not: !

	// Relational operators:
	// - Equality: ==
	// - Not Equal: !=
	// - Less Than: <
	// ... : >, <=, >=

	// For loops
	i:=0
	for i < 10 {
		i++
	}

	for j:=0; j<10; j++ {
		// Stuff to do
	}

	// If else
	if 5 >= 2 {
		// This line runs
	} else {
		// Does not run
	}

	// if else if
	if 4 > 4 {
		// This line doesn't run
	} else if ( 2 < 4) {
		// This line runs
	} else {
		// This line doesn't run
	}

	// switch
	switch(5) {
		case 1: // does not run
		case 5: // this line runs
		default: // does not run
	}

	// Slices: like an array, but no size specified
	slice := []int {0, 1, 2, 3, 4, 5}
	firstSlice := slice[0:3] // 0 up to but not including 3rd index, so length is 3 and has {0, 1, 2}
	fmt.Println(firstSlice[0], " ", firstSlice[2]) // 0 2
	fmt.Println(slice[:3]) // From 0 to but not including 3rd index.  Output: [0, 1, 2]
	fmt.Println(slice[2:]) // From 2 index to end. Output: [2, 3, 4, 5]

	zeroForFirst := 3
	size := 9
	notDefinedValuesSlice := make([]int, zeroForFirst, size)
	fmt.Println(notDefinedValuesSlice[0:]) // [0 0 0]

	copy(notDefinedValuesSlice, slice) // Copy values from slice to notDefinedValuesSlice
	fmt.Println(notDefinedValuesSlice[0:]) // [0 1 2]

	notDefinedValuesSlice = append(notDefinedValuesSlice, 4, 5, 6)
	fmt.Println(notDefinedValuesSlice[0:]) // [0 1 2 4 5 6]

	// Maps
	kidAges := make(map[string] int) // map[keyType] valueType
	kidAges["Tom"] = 12
	kidAges["Tim"] = 13
	fmt.Println(kidAges) // map[Tom:12 Tim:13]
	delete(kidAges, "Tom")
	fmt.Println(kidAges) // map[Tim:13]
	fmt.Println(len(kidAges)) // 1

	// Function call
	fmt.Println( sum(2, 3) ) // 5
}

// Declare a function
// func nameOfFunction(variableName variableType) returnType
func sum(a int, b int) int {
	return a + b
}<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"strings"
	"sync"

	"github.com/inominate/apicache"
)

// Prototype for page specific handlers.
type APIHandler func(url string, params map[string]string) *apicache.Response

// Default straight through handler.
func defaultHandler(url string, params map[string]string) *apicache.Response {
	resp, err := APIReq(url, params)
	if err != nil {
		debugLog.Printf("API Error %s: %s - %+v", err, url, params)
	}

	return resp
}

// Defines valid API pages and what special handler they should use.
// nil handlers will attempt to use defaultHandler which is a straight
// passthrough.
var validPages = map[string]APIHandler{
	//	"/control/":                             controlHandler,
	"/account/accountstatus.xml.aspx": nil,
	"/account/apikeyinfo.xml.aspx":    nil,
	"/account/characters.xml.aspx":    nil,

	"/char/accountbalance.xml.aspx":         nil,
	"/char/assetlist.xml.aspx":              nil,
	"/char/calendareventattendees.xml.aspx": nil,
	"/char/charactersheet.xml.aspx":         nil,
	"/char/contactlist.xml.aspx":            nil,
	"/char/contactnotifications.xml.aspx":   nil,
	"/char/contracts.xml.aspx":              nil,
	"/char/contractitems.xml.aspx":          nil,
	"/char/contractbids.xml.aspx":           nil,
	"/char/facwarstats.xml.aspx":            nil,
	"/char/industryjobs.xml.aspx":           nil,
	"/char/industryjobshistory.xml.aspx":    nil,
	"/char/killlog.xml.aspx":                nil,
	"/char/killmails.xml.aspx":              nil,
	"/char/locations.xml.aspx":              idsListHandler,
	"/char/mailbodies.xml.aspx":             idsListHandler,
	"/char/mailinglists.xml.aspx":           nil,
	"/char/mailmessages.xml.aspx":           nil,
	"/char/marketorders.xml.aspx":           nil,
	"/char/medals.xml.aspx":                 nil,
	"/char/notifications.xml.aspx":          nil,
	"/char/notificationtexts.xml.aspx":      idsListHandler,
	"/char/planetarycolonies.xml.aspx":      nil,
	"/char/planetarylinks.xml.aspx":         nil,
	"/char/planetarypins.xml.aspx":          nil,
	"/char/planetaryroutes.xml.aspx":        nil,
	"/char/research.xml.aspx":               nil,
	"/char/skillintraining.xml.aspx":        nil,
	"/char/skillqueue.xml.aspx":             nil,
	"/char/standings.xml.aspx":              nil,
	"/char/upcomingcalendarevents.xml.aspx": nil,
	"/char/walletjournal.xml.aspx":          nil,
	"/char/wallettransactions.xml.aspx":     nil,

	"/corp/accountbalance.xml.aspx":       nil,
	"/corp/assetlist.xml.aspx":            nil,
	"/corp/contactlist.xml.aspx":          nil,
	"/corp/containerlog.xml.aspx":         nil,
	"/corp/contracts.xml.aspx":            nil,
	"/corp/contractitems.xml.aspx":        nil,
	"/corp/contractbids.xml.aspx":         nil,
	"/corp/corporationsheet.xml.aspx":     nil,
	"/corp/facilities.xml.aspx":           nil,
	"/corp/facwarstats.xml.aspx":          nil,
	"/corp/industryjobs.xml.aspx":         nil,
	"/corp/industryjobshistory.xml.aspx":  nil,
	"/corp/killlog.xml.aspx":              nil,
	"/corp/killmails.xml.aspx":            nil,
	"/corp/locations.xml.aspx":            idsListHandler,
	"/corp/marketorders.xml.aspx":         nil,
	"/corp/medals.xml.aspx":               nil,
	"/corp/membermedals.xml.aspx":         nil,
	"/corp/membersecurity.xml.aspx":       nil,
	"/corp/membersecuritylog.xml.aspx":    nil,
	"/corp/membertracking.xml.aspx":       nil,
	"/corp/outpostlist.xml.aspx":          nil,
	"/corp/outpostservicedetail.xml.aspx": nil,
	"/corp/shareholders.xml.aspx":         nil,
	"/corp/standings.xml.aspx":            nil,
	"/corp/starbasedetail.xml.aspx":       nil,
	"/corp/starbaselist.xml.aspx":         nil,
	"/corp/titles.xml.aspx":               nil,
	"/corp/walletjournal.xml.aspx":        nil,
	"/corp/wallettransactions.xml.aspx":   nil,

	"/eve/alliancelist.xml.aspx":           nil,
	"/eve/certificatetree.xml.aspx":        nil,
	"/eve/characteraffiliation.xml.aspx":   idsListHandler,
	"/eve/characterid.xml.aspx":            nil,
	"/eve/characterinfo.xml.aspx":          nil,
	"/eve/charactername.xml.aspx":          nil,
	"/eve/conquerablestationlist.xml.aspx": nil,
	"/eve/errorlist.xml.aspx":              nil,
	"/eve/facwarstats.xml.aspx":            nil,
	"/eve/facwartopstats.xml.aspx":         nil,
	"/eve/reftypes.xml.aspx":               nil,
	"/eve/skilltree.xml.aspx":              nil,
	"/eve/typename.xml.aspx":               nil,

	"/map/facwarsystems.xml.aspx":     nil,
	"/map/jumps.xml.aspx":             nil,
	"/map/kills.xml.aspx":             nil,
	"/map/sovereignty.xml.aspx":       nil,
	"/map/sovereigntystatus.xml.aspx": nil,

	"/server/serverstatus.xml.aspx": nil,
	"/api/calllist.xml.aspx":        nil,
}

/*
Note that this is a best-attempt number only, actual error count can go
significantly higher as massed concurrent requests run. This isn't to prevent
errors being sent to the API so much as to prevent things from getting out of
control in response to a pathlogical request.
*/
const maxIDErrors = 16

// Bug Correcting Handler for endpoints using comma separated ID lists which
// will fail entirely in case of a single invalid ID.
//
// Note: Can generate many errors so should only be used with applications
// that know to behave themselves. Add a form value of fix with any content
// to enable the correction.
func idsListHandler(url string, params map[string]string) *apicache.Response {
	var runFixer bool
	if _, ok := params["fix"]; ok {
		delete(params, "fix")
		runFixer = true
	}

	resp, err := APIReq(url, params)
	if err != nil {
		debugLog.Printf("API Error %s: %s - %+v", err, url, params)
	}
	if !runFixer {
		return resp
	}

	var ids []string
	if idsParam, ok := params["ids"]; ok {
		ids = strings.Split(idsParam, ",")
	}

	// If we have no ids or just one, we're not doing anything special.
	// If there's more than 250 ids, that's beyond the API limit so we won't
	// touch that either.
	if len(ids) == 0 || len(ids) == 1 || len(ids) > 250 {
		return resp
	}
	// If the request didn't have an invalid id, errorcode 135, there's nothing
	// we can do to help.
	if resp.Error.ErrorCode != 135 {
		return resp
	}

	// If we got this far there's more than one ID, at least one of which is
	// invalid.
	debugLog.Printf("idsListHandler going into action for %d ids: %s", len(ids), params["ids"])

	var errCount errCount
	delete(params, "ids")

	validIDs, err := findValidIDs(url, params, ids, &errCount)
	if err != nil {
		debugLog.Printf("findValidIDs failed: %s", err)
		return resp
	}

	idsBuf := &bytes.Buffer{}
	fmt.Fprintf(idsBuf, "%s", validIDs[0])
	for i := 1; i < len(validIDs); i++ {
		fmt.Fprintf(idsBuf, ",%s", validIDs[i])
	}
	idsParam := idsBuf.String()
	params["ids"] = idsParam

	resp, err = APIReq(url, params)
	if err != nil {
		debugLog.Printf("API Error %s: %s - %+v", err, url, params)
	}
	debugLog.Printf("Completed with: %d errors.", errCount.Get())
	return resp
}

type errCount struct {
	count int
	sync.Mutex
}

func (e *errCount) Get() int {
	e.Lock()
	defer e.Unlock()

	count := e.count
	return count
}

func (e *errCount) Add() int {
	e.Lock()
	defer e.Unlock()

	e.count++
	count := e.count
	return count
}

func findValidIDs(url string, params map[string]string, ids []string, errCount *errCount) ([]string, error) {
	if false && len(ids) == 1 {
		valid, err := isValidIDList(url, params, ids, errCount)
		if valid {
			return ids, err
		} else {
			return nil, err
		}
	}

	if count := errCount.Get(); count >= maxIDErrors {
		return nil, fmt.Errorf("failed to get ids, hit %d errors limit", count)
	}

	var leftIDs, rightIDs []string
	var leftErr, rightErr error

	left := ids[0 : len(ids)/2]
	leftValid, leftErr := isValidIDList(url, params, left, errCount)
	if leftErr != nil {
		return nil, leftErr
	}
	if leftValid {
		leftIDs = left
	} else {
		if len(left) > 1 {
			leftIDs, leftErr = findValidIDs(url, params, left, errCount)
			if rightErr != nil {
				return nil, leftErr
			}
		}
	}

	right := ids[len(ids)/2:]
	rightValid, rightErr := isValidIDList(url, params, right, errCount)
	if rightErr != nil {
		return nil, rightErr
	}
	if rightValid {
		rightIDs = right
	} else {
		if len(right) > 1 {
			rightIDs, rightErr = findValidIDs(url, params, right, errCount)
			if rightErr != nil {
				return nil, rightErr
			}
		}
	}

	validIDs := append(leftIDs, rightIDs...)
	return validIDs, nil
}

func isValidIDList(url string, params map[string]string, ids []string, errCount *errCount) (bool, error) {
	if count := errCount.Get(); count >= maxIDErrors {
		return false, fmt.Errorf("failed to get ids, hit %d errors limit", count)
	}

	idsBuf := &bytes.Buffer{}
	fmt.Fprintf(idsBuf, "%s", ids[0])
	for i := 1; i < len(ids); i++ {
		fmt.Fprintf(idsBuf, ",%s", ids[i])
	}
	idsParam := idsBuf.String()

	var newParams = make(map[string]string)
	for k, v := range params {
		newParams[k] = v
	}
	newParams["ids"] = idsParam

	resp, err := APIReq(url, newParams)
	// Bail completely if the API itself fails for any reason.
	if err != nil {
		return false, err
	}
	// If there is no error then this batch is okay.
	if resp.Error.ErrorCode == 0 {
		return true, nil
	}
	// Bail if we got a non-api failure error other than invalid ID
	if resp.Error.ErrorCode != 135 {
		return false, resp.Error
	}

	debugLog.Printf("Adding Error %d for: %v", errCount.Get(), ids)
	errCount.Add()

	return false, nil
}
<commit_msg>always use fixer on id lists<commit_after>package main

import (
	"bytes"
	"fmt"
	"strings"
	"sync"

	"github.com/inominate/apicache"
)

// Prototype for page specific handlers.
type APIHandler func(url string, params map[string]string) *apicache.Response

// Default straight through handler.
func defaultHandler(url string, params map[string]string) *apicache.Response {
	resp, err := APIReq(url, params)
	if err != nil {
		debugLog.Printf("API Error %s: %s - %+v", err, url, params)
	}

	return resp
}

// Defines valid API pages and what special handler they should use.
// nil handlers will attempt to use defaultHandler which is a straight
// passthrough.
var validPages = map[string]APIHandler{
	//	"/control/":                             controlHandler,
	"/account/accountstatus.xml.aspx": nil,
	"/account/apikeyinfo.xml.aspx":    nil,
	"/account/characters.xml.aspx":    nil,

	"/char/accountbalance.xml.aspx":         nil,
	"/char/assetlist.xml.aspx":              nil,
	"/char/calendareventattendees.xml.aspx": nil,
	"/char/charactersheet.xml.aspx":         nil,
	"/char/contactlist.xml.aspx":            nil,
	"/char/contactnotifications.xml.aspx":   nil,
	"/char/contracts.xml.aspx":              nil,
	"/char/contractitems.xml.aspx":          nil,
	"/char/contractbids.xml.aspx":           nil,
	"/char/facwarstats.xml.aspx":            nil,
	"/char/industryjobs.xml.aspx":           nil,
	"/char/industryjobshistory.xml.aspx":    nil,
	"/char/killlog.xml.aspx":                nil,
	"/char/killmails.xml.aspx":              nil,
	"/char/locations.xml.aspx":              idsListHandler,
	"/char/mailbodies.xml.aspx":             idsListHandler,
	"/char/mailinglists.xml.aspx":           nil,
	"/char/mailmessages.xml.aspx":           nil,
	"/char/marketorders.xml.aspx":           nil,
	"/char/medals.xml.aspx":                 nil,
	"/char/notifications.xml.aspx":          nil,
	"/char/notificationtexts.xml.aspx":      idsListHandler,
	"/char/planetarycolonies.xml.aspx":      nil,
	"/char/planetarylinks.xml.aspx":         nil,
	"/char/planetarypins.xml.aspx":          nil,
	"/char/planetaryroutes.xml.aspx":        nil,
	"/char/research.xml.aspx":               nil,
	"/char/skillintraining.xml.aspx":        nil,
	"/char/skillqueue.xml.aspx":             nil,
	"/char/standings.xml.aspx":              nil,
	"/char/upcomingcalendarevents.xml.aspx": nil,
	"/char/walletjournal.xml.aspx":          nil,
	"/char/wallettransactions.xml.aspx":     nil,

	"/corp/accountbalance.xml.aspx":       nil,
	"/corp/assetlist.xml.aspx":            nil,
	"/corp/contactlist.xml.aspx":          nil,
	"/corp/containerlog.xml.aspx":         nil,
	"/corp/contracts.xml.aspx":            nil,
	"/corp/contractitems.xml.aspx":        nil,
	"/corp/contractbids.xml.aspx":         nil,
	"/corp/corporationsheet.xml.aspx":     nil,
	"/corp/facilities.xml.aspx":           nil,
	"/corp/facwarstats.xml.aspx":          nil,
	"/corp/industryjobs.xml.aspx":         nil,
	"/corp/industryjobshistory.xml.aspx":  nil,
	"/corp/killlog.xml.aspx":              nil,
	"/corp/killmails.xml.aspx":            nil,
	"/corp/locations.xml.aspx":            idsListHandler,
	"/corp/marketorders.xml.aspx":         nil,
	"/corp/medals.xml.aspx":               nil,
	"/corp/membermedals.xml.aspx":         nil,
	"/corp/membersecurity.xml.aspx":       nil,
	"/corp/membersecuritylog.xml.aspx":    nil,
	"/corp/membertracking.xml.aspx":       nil,
	"/corp/outpostlist.xml.aspx":          nil,
	"/corp/outpostservicedetail.xml.aspx": nil,
	"/corp/shareholders.xml.aspx":         nil,
	"/corp/standings.xml.aspx":            nil,
	"/corp/starbasedetail.xml.aspx":       nil,
	"/corp/starbaselist.xml.aspx":         nil,
	"/corp/titles.xml.aspx":               nil,
	"/corp/walletjournal.xml.aspx":        nil,
	"/corp/wallettransactions.xml.aspx":   nil,

	"/eve/alliancelist.xml.aspx":           nil,
	"/eve/certificatetree.xml.aspx":        nil,
	"/eve/characteraffiliation.xml.aspx":   idsListHandler,
	"/eve/characterid.xml.aspx":            nil,
	"/eve/characterinfo.xml.aspx":          nil,
	"/eve/charactername.xml.aspx":          nil,
	"/eve/conquerablestationlist.xml.aspx": nil,
	"/eve/errorlist.xml.aspx":              nil,
	"/eve/facwarstats.xml.aspx":            nil,
	"/eve/facwartopstats.xml.aspx":         nil,
	"/eve/reftypes.xml.aspx":               nil,
	"/eve/skilltree.xml.aspx":              nil,
	"/eve/typename.xml.aspx":               nil,

	"/map/facwarsystems.xml.aspx":     nil,
	"/map/jumps.xml.aspx":             nil,
	"/map/kills.xml.aspx":             nil,
	"/map/sovereignty.xml.aspx":       nil,
	"/map/sovereigntystatus.xml.aspx": nil,

	"/server/serverstatus.xml.aspx": nil,
	"/api/calllist.xml.aspx":        nil,
}

/*
Note that this is a best-attempt number only, actual error count can go
significantly higher as massed concurrent requests run. This isn't to prevent
errors being sent to the API so much as to prevent things from getting out of
control in response to a pathlogical request.
*/
const maxIDErrors = 16

// Bug Correcting Handler for endpoints using comma separated ID lists which
// will fail entirely in case of a single invalid ID.
//
// Note: Can generate many errors so should only be used with applications
// that know to behave themselves. Add a form value of fix with any content
// to enable the correction.
func idsListHandler(url string, params map[string]string) *apicache.Response {
	var runFixer bool
	runFixer = true

	resp, err := APIReq(url, params)
	if err != nil {
		debugLog.Printf("API Error %s: %s - %+v", err, url, params)
	}
	if !runFixer {
		return resp
	}

	var ids []string
	if idsParam, ok := params["ids"]; ok {
		ids = strings.Split(idsParam, ",")
	}

	// If we have no ids or just one, we're not doing anything special.
	// If there's more than 250 ids, that's beyond the API limit so we won't
	// touch that either.
	if len(ids) == 0 || len(ids) == 1 || len(ids) > 250 {
		return resp
	}
	// If the request didn't have an invalid id, errorcode 135, there's nothing
	// we can do to help.
	if resp.Error.ErrorCode != 135 {
		return resp
	}

	// If we got this far there's more than one ID, at least one of which is
	// invalid.
	debugLog.Printf("idsListHandler going into action for %d ids: %s", len(ids), params["ids"])

	var errCount errCount
	delete(params, "ids")

	validIDs, err := findValidIDs(url, params, ids, &errCount)
	if err != nil {
		debugLog.Printf("findValidIDs failed: %s", err)
		return resp
	}

	idsBuf := &bytes.Buffer{}
	fmt.Fprintf(idsBuf, "%s", validIDs[0])
	for i := 1; i < len(validIDs); i++ {
		fmt.Fprintf(idsBuf, ",%s", validIDs[i])
	}
	idsParam := idsBuf.String()
	params["ids"] = idsParam

	resp, err = APIReq(url, params)
	if err != nil {
		debugLog.Printf("API Error %s: %s - %+v", err, url, params)
	}
	debugLog.Printf("Completed with: %d errors.", errCount.Get())
	return resp
}

type errCount struct {
	count int
	sync.Mutex
}

func (e *errCount) Get() int {
	e.Lock()
	defer e.Unlock()

	count := e.count
	return count
}

func (e *errCount) Add() int {
	e.Lock()
	defer e.Unlock()

	e.count++
	count := e.count
	return count
}

func findValidIDs(url string, params map[string]string, ids []string, errCount *errCount) ([]string, error) {
	if false && len(ids) == 1 {
		valid, err := isValidIDList(url, params, ids, errCount)
		if valid {
			return ids, err
		} else {
			return nil, err
		}
	}

	if count := errCount.Get(); count >= maxIDErrors {
		return nil, fmt.Errorf("failed to get ids, hit %d errors limit", count)
	}

	var leftIDs, rightIDs []string
	var leftErr, rightErr error

	left := ids[0 : len(ids)/2]
	leftValid, leftErr := isValidIDList(url, params, left, errCount)
	if leftErr != nil {
		return nil, leftErr
	}
	if leftValid {
		leftIDs = left
	} else {
		if len(left) > 1 {
			leftIDs, leftErr = findValidIDs(url, params, left, errCount)
			if rightErr != nil {
				return nil, leftErr
			}
		}
	}

	right := ids[len(ids)/2:]
	rightValid, rightErr := isValidIDList(url, params, right, errCount)
	if rightErr != nil {
		return nil, rightErr
	}
	if rightValid {
		rightIDs = right
	} else {
		if len(right) > 1 {
			rightIDs, rightErr = findValidIDs(url, params, right, errCount)
			if rightErr != nil {
				return nil, rightErr
			}
		}
	}

	validIDs := append(leftIDs, rightIDs...)
	return validIDs, nil
}

func isValidIDList(url string, params map[string]string, ids []string, errCount *errCount) (bool, error) {
	if count := errCount.Get(); count >= maxIDErrors {
		return false, fmt.Errorf("failed to get ids, hit %d errors limit", count)
	}

	idsBuf := &bytes.Buffer{}
	fmt.Fprintf(idsBuf, "%s", ids[0])
	for i := 1; i < len(ids); i++ {
		fmt.Fprintf(idsBuf, ",%s", ids[i])
	}
	idsParam := idsBuf.String()

	var newParams = make(map[string]string)
	for k, v := range params {
		newParams[k] = v
	}
	newParams["ids"] = idsParam

	resp, err := APIReq(url, newParams)
	// Bail completely if the API itself fails for any reason.
	if err != nil {
		return false, err
	}
	// If there is no error then this batch is okay.
	if resp.Error.ErrorCode == 0 {
		return true, nil
	}
	// Bail if we got a non-api failure error other than invalid ID
	if resp.Error.ErrorCode != 135 {
		return false, resp.Error
	}

	debugLog.Printf("Adding Error %d for: %v", errCount.Get(), ids)
	errCount.Add()

	return false, nil
}
<|endoftext|>


<commit_before>package gnosis

// package file will contain MarkdownHandler and RawHandler to handle incoming requests
// Whole thing needs to be written

import (
	// "errors"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"strings"

	"github.com/blevesearch/bleve"
	// bleveHttp "github.com/blevesearch/bleve/http"
)

type Page struct {
	Title    string
	ToC      template.HTML
	Body     template.HTML
	Topics   template.HTML
	Keywords template.HTML
}

func MarkdownHandler(responsePipe http.ResponseWriter, request *http.Request, serverConfig ServerSection) {

	var err error

	requestPath := strings.TrimPrefix(request.URL.Path, serverConfig.Prefix)

	// If the request is empty, set it to the default.
	if requestPath == "" {
		requestPath = serverConfig.Default
	log.Printf("replaced the request path - Request path is [%s] of length [%d], comparing against [%s] of length [%d]", requestPath, len(requestPath), "", len(""))
	}

	// If the request doesn't end in .md, add that
	if !strings.HasSuffix(requestPath, ".md") {
		requestPath = requestPath + ".md"
	}

	pdata := new(PageMetadata)
	err = pdata.LoadPage(serverConfig.Path + requestPath)
	if err != nil {
		log.Printf("request [ %s ] points to an bad file target [ %s ] sent to server %s", request.URL.Path, requestPath, serverConfig.Prefix)
		http.Error(responsePipe, err.Error(), 404)
		return
	}

	if pdata.MatchedTag(serverConfig.Restricted) {
		log.Printf("request [ %s ] was against a page [ %s ] with a restricted tag", request.URL.Path, requestPath)
		http.Error(responsePipe, err.Error(), 403)
		return
	}

	// parse any markdown in the input
	body := template.HTML(bodyParseMarkdown(pdata.Page))
	toc := template.HTML(tocParseMarkdown(pdata.Page))
	keywords := pdata.PrintKeywords()
	topics := pdata.PrintTopics(serverConfig.TopicURL)

	// ##TODO## put this template right in the function call
	// Then remove the Page Struct above
	response := Page{Title: "", ToC: toc, Body: body, Keywords: keywords, Topics: topics}
	err = allTemplates.ExecuteTemplate(responsePipe, serverConfig.Template, response)
	if err != nil {
		http.Error(responsePipe, err.Error(), 500)
	}
}

func RawHandler(responsePipe http.ResponseWriter, request *http.Request, serverConfig ServerSection) {

	var err error

	request.URL.Path = strings.TrimPrefix(request.URL.Path, serverConfig.Prefix)

	// If the request is empty, set it to the default.
	if request.URL.Path == "" || request.URL.Path == "/" {
		request.URL.Path = serverConfig.Default
	}

	// If the request is a blocked restriction, shut it down.
	for _, restricted := range serverConfig.Restricted {
		if strings.HasSuffix(request.URL.Path, restricted) {
			log.Printf("request %s was improperly routed to the file handler with an disallowed extension %s", request.URL.Path, restricted)
			http.Error(responsePipe, "Request not allowed", 403)
			return
		}
	}

	// Load the file - 404 on failure.
	contents, err := ioutil.ReadFile(serverConfig.Path + request.URL.Path)
	if err != nil {
		log.Printf("request [ %s ] points to an bad file target sent to server %s - %v", request.URL.Path, serverConfig.Prefix, err)
		http.Error(responsePipe, err.Error(), 404)
		return
	}

	_, err = responsePipe.Write([]byte(contents))
	if err != nil {
		http.Error(responsePipe, err.Error(), 500)
	}
	return
}

func SearchHandler(responsePipe http.ResponseWriter, request *http.Request, serverConfig ServerSection) {

	var err error

	request.URL.Path = strings.TrimPrefix(request.URL.Path, serverConfig.Prefix)

	queryArgs := request.URL.Query()

	query := bleve.NewQueryStringQuery(queryArgs["s"][0])
	searchRequest := bleve.NewSearchRequest(query)

	// validate the query
	err = searchRequest.Query.Validate()
	if err != nil {
		log.Printf("Error validating query: %v", err)
		http.Error(responsePipe, err.Error(), 400)
		return
	}

	index, err := bleve.Open(serverConfig.Path)
	defer index.Close()
	if index == nil {
		log.Printf("no such index '%s'", serverConfig.Default)
		http.Error(responsePipe, err.Error(), 404)
		return
	} else if err != nil {
		log.Printf("no such index '%s'", serverConfig.Path)
		http.Error(responsePipe, err.Error(), 404)
		log.Printf("problem opening index '%s' - %v", serverConfig.Path, err)
		return
	}

	// execute the query
	searchResponse, err := index.Search(searchRequest)
	if err != nil {
		log.Printf("Error executing query: %v", err)
		http.Error(responsePipe, err.Error(), 400)
		return
	}

	err = allTemplates.ExecuteTemplate(responsePipe, serverConfig.Template, searchResponse)
	if err != nil {
		http.Error(responsePipe, err.Error(), 500)
	}
}

func MakeHandler(handlerConfig ServerSection) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		switch handlerConfig.ServerType {
		case "markdown":
			MarkdownHandler(w, r, handlerConfig)
		case "raw":
			RawHandler(w, r, handlerConfig)
		case "simpleSearch":
			SearchHandler(w, r, handlerConfig)
		default:
			log.Printf("Bad server type [%s]", handlerConfig.ServerType)
		}
	}
}
<commit_msg>added a catch for loading the search page without any terms entered<commit_after>package gnosis

// package file will contain MarkdownHandler and RawHandler to handle incoming requests
// Whole thing needs to be written

import (
	// "errors"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"strings"

	"github.com/blevesearch/bleve"
	// bleveHttp "github.com/blevesearch/bleve/http"
)

type Page struct {
	Title    string
	ToC      template.HTML
	Body     template.HTML
	Topics   template.HTML
	Keywords template.HTML
}

func MarkdownHandler(responsePipe http.ResponseWriter, request *http.Request, serverConfig ServerSection) {

	var err error

	requestPath := strings.TrimPrefix(request.URL.Path, serverConfig.Prefix)

	// If the request is empty, set it to the default.
	if requestPath == "" {
		requestPath = serverConfig.Default
	log.Printf("replaced the request path - Request path is [%s] of length [%d], comparing against [%s] of length [%d]", requestPath, len(requestPath), "", len(""))
	}

	// If the request doesn't end in .md, add that
	if !strings.HasSuffix(requestPath, ".md") {
		requestPath = requestPath + ".md"
	}

	pdata := new(PageMetadata)
	err = pdata.LoadPage(serverConfig.Path + requestPath)
	if err != nil {
		log.Printf("request [ %s ] points to an bad file target [ %s ] sent to server %s", request.URL.Path, requestPath, serverConfig.Prefix)
		http.Error(responsePipe, err.Error(), 404)
		return
	}

	if pdata.MatchedTag(serverConfig.Restricted) {
		log.Printf("request [ %s ] was against a page [ %s ] with a restricted tag", request.URL.Path, requestPath)
		http.Error(responsePipe, err.Error(), 403)
		return
	}

	// parse any markdown in the input
	body := template.HTML(bodyParseMarkdown(pdata.Page))
	toc := template.HTML(tocParseMarkdown(pdata.Page))
	keywords := pdata.PrintKeywords()
	topics := pdata.PrintTopics(serverConfig.TopicURL)

	// ##TODO## put this template right in the function call
	// Then remove the Page Struct above
	response := Page{Title: "", ToC: toc, Body: body, Keywords: keywords, Topics: topics}
	err = allTemplates.ExecuteTemplate(responsePipe, serverConfig.Template, response)
	if err != nil {
		http.Error(responsePipe, err.Error(), 500)
	}
}

func RawHandler(responsePipe http.ResponseWriter, request *http.Request, serverConfig ServerSection) {

	var err error

	request.URL.Path = strings.TrimPrefix(request.URL.Path, serverConfig.Prefix)

	// If the request is empty, set it to the default.
	if request.URL.Path == "" || request.URL.Path == "/" {
		request.URL.Path = serverConfig.Default
	}

	// If the request is a blocked restriction, shut it down.
	for _, restricted := range serverConfig.Restricted {
		if strings.HasSuffix(request.URL.Path, restricted) {
			log.Printf("request %s was improperly routed to the file handler with an disallowed extension %s", request.URL.Path, restricted)
			http.Error(responsePipe, "Request not allowed", 403)
			return
		}
	}

	// Load the file - 404 on failure.
	contents, err := ioutil.ReadFile(serverConfig.Path + request.URL.Path)
	if err != nil {
		log.Printf("request [ %s ] points to an bad file target sent to server %s - %v", request.URL.Path, serverConfig.Prefix, err)
		http.Error(responsePipe, err.Error(), 404)
		return
	}

	_, err = responsePipe.Write([]byte(contents))
	if err != nil {
		http.Error(responsePipe, err.Error(), 500)
	}
	return
}

func SearchHandler(responsePipe http.ResponseWriter, request *http.Request, serverConfig ServerSection) {

	var err error

	request.URL.Path = strings.TrimPrefix(request.URL.Path, serverConfig.Prefix)

	queryArgs := request.URL.Query()

	query := bleve.NewQueryStringQuery(queryArgs["s"][0])
	if query == "" {
		err = allTemplates.ExecuteTemplate(responsePipe, serverConfig.Template, interface{})
		if err != nil {
			http.Error(responsePipe, err.Error(), 500)
		}
	}
	searchRequest := bleve.NewSearchRequest(query)

	// validate the query
	err = searchRequest.Query.Validate()
	if err != nil {
		log.Printf("Error validating query: %v", err)
		http.Error(responsePipe, err.Error(), 400)
		return
	}

	index, err := bleve.Open(serverConfig.Path)
	defer index.Close()
	if index == nil {
		log.Printf("no such index '%s'", serverConfig.Default)
		http.Error(responsePipe, err.Error(), 404)
		return
	} else if err != nil {
		log.Printf("no such index '%s'", serverConfig.Path)
		http.Error(responsePipe, err.Error(), 404)
		log.Printf("problem opening index '%s' - %v", serverConfig.Path, err)
		return
	}

	// execute the query
	searchResponse, err := index.Search(searchRequest)
	if err != nil {
		log.Printf("Error executing query: %v", err)
		http.Error(responsePipe, err.Error(), 400)
		return
	}

	err = allTemplates.ExecuteTemplate(responsePipe, serverConfig.Template, searchResponse)
	if err != nil {
		http.Error(responsePipe, err.Error(), 500)
	}
}

func MakeHandler(handlerConfig ServerSection) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		switch handlerConfig.ServerType {
		case "markdown":
			MarkdownHandler(w, r, handlerConfig)
		case "raw":
			RawHandler(w, r, handlerConfig)
		case "simpleSearch":
			SearchHandler(w, r, handlerConfig)
		default:
			log.Printf("Bad server type [%s]", handlerConfig.ServerType)
		}
	}
}
<|endoftext|>


<commit_before>package promise

import (
	"errors"
	"testing"
)

// Ensure that the basic properties of a promise holds true if the value is
// already resolved.
func TestCompletedPromise(test *testing.T) {
	value, _ := Completed(10).Then(func(foo interface{}) interface{} {
		i, ok := foo.(int)

		if !ok {
			test.Fatalf("Expected type int")
		}

		return i + 10
	}).Then(func(foo interface{}) interface{} {
		i, ok := foo.(int)

		if !ok {
			test.Fatalf("Expected type int")
		}

		if i != 20 {
			test.Fatalf("Expected 20, saw %d", i)
		}

		return i == 20
	}).Combine(func(foo interface{}) Thenable {
		/* Just a pass-through to say we did it */
		return Completed(foo)
	}).Get()

	result, ok := value.(bool)

	if !ok {
		test.Fatalf("Expected boolean result from .Get()")
	}

	if result != true {
		test.Fatalf("Expected result to be true!")
	}
}

// Ensure that the basic functions of the Promise API work for values that are
// not yet resolved.
func TestCompletablePromise(test *testing.T) {
	promise := Promise()

	squared := promise.Then(func(value interface{}) interface{} {
		val, _ := value.(int)

		return val * val
	})

	cubed := squared.Then(func(value interface{}) interface{} {
		val, _ := value.(int)

		return val * val * val
	})

	combined := promise.Combine(func(value interface{}) Thenable {
		val, _ := value.(int)

		return Completed(val + 3)
	})

	/* And then something happened...in the background! */
	go promise.Complete(2)

	squaredV, _ := squared.Get()
	combinedV, _ := combined.Get()
	cubedV, _ := cubed.Get()

	four, _ := squaredV.(int)
	five, _ := combinedV.(int)
	sixtyfour, _ := cubedV.(int)

	if four != 4 {
		test.Fatalf("Expected result of 2² to be 4")
	}

	if five != 5 {
		test.Fatalf("Expected result of 2 + 3 (%d) to be 5", five)
	}

	if sixtyfour != 64 {
		test.Fatalf("Expected result of 4³ to be 64")
	}
}

// Validate that promise.All works as expected.
func TestAll(test *testing.T) {
	expected := []int{1, 2}

	res, _ := All(Completed(1), Completed(2)).Then(func(result interface{}) interface{} {
		// Traversing through these means first getting a slice of anonymous
		// values...
		values, ok := result.([]interface{})

		if !ok {
			test.Fatalf("Expected a slice of []interface{}")
		}

		// Then looking at each value...
		for i, value := range values {
			observed, ok := value.(int)

			if !ok {
				test.Fatalf("Expected int type")
			}

			if expected[i] != observed {
				test.Fatalf("Expected %d != %d observed", expected[i], observed)
			}
		}

		return true
	}).Get()

	status := res.(bool)

	if !status {
		test.Fatalf("Test cases did not run")
	}
}

// Validate that rejections on completed promises work, and Catch on rejected
// promises works as expected.
func TestRejected(test *testing.T) {
	promise := Promise()

	catchWorked := false
	dependenciesToo := false

	var expected = errors.New("Expected error!")

	promise.Catch(func(err error) {
		if err != expected {
			test.Fatalf("Did not see expected error!")
		}

		catchWorked = true
	})

	promise.Then(func(val interface{}) interface{} {
		i, _ := val.(int)

		i += 1

		return i
	}).Catch(func(err error) {
		dependenciesToo = true
	})

	promise.Reject(expected)

	if !catchWorked {
		test.Fatalf("Did not see expected rejected handler")
	}

	if !dependenciesToo {
		test.Fatalf("Did not see rejected handler on dependency")
	}

	promise.Catch(func(cause error) {
		if cause != expected {
			test.Fatalf("previously rejected promise did not pass on the cause")
		}
	})

	rejectedCalled := false

	Rejected(expected).Catch(func(err error) {
		if err != expected {
			test.Fatalf("Rejected() promise does not relay messages.")
		}

		rejectedCalled = true
	})

	if !rejectedCalled {
		test.Fatalf("Rejected() did not invoke the onreject callback")
	}
}
<commit_msg>Combined promise must propigate error<commit_after>package promise

import (
	"errors"
	"testing"
)

// Ensure that the basic properties of a promise holds true if the value is
// already resolved.
func TestCompletedPromise(test *testing.T) {
	value, _ := Completed(10).Then(func(foo interface{}) interface{} {
		i, ok := foo.(int)

		if !ok {
			test.Fatalf("Expected type int")
		}

		return i + 10
	}).Then(func(foo interface{}) interface{} {
		i, ok := foo.(int)

		if !ok {
			test.Fatalf("Expected type int")
		}

		if i != 20 {
			test.Fatalf("Expected 20, saw %d", i)
		}

		return i == 20
	}).Combine(func(foo interface{}) Thenable {
		/* Just a pass-through to say we did it */
		return Completed(foo)
	}).Get()

	result, ok := value.(bool)

	if !ok {
		test.Fatalf("Expected boolean result from .Get()")
	}

	if result != true {
		test.Fatalf("Expected result to be true!")
	}
}

// Ensure that the basic functions of the Promise API work for values that are
// not yet resolved.
func TestCompletablePromise(test *testing.T) {
	promise := Promise()

	squared := promise.Then(func(value interface{}) interface{} {
		val, _ := value.(int)

		return val * val
	})

	cubed := squared.Then(func(value interface{}) interface{} {
		val, _ := value.(int)

		return val * val * val
	})

	combined := promise.Combine(func(value interface{}) Thenable {
		val, _ := value.(int)

		return Completed(val + 3)
	})

	/* And then something happened...in the background! */
	go promise.Complete(2)

	squaredV, _ := squared.Get()
	combinedV, _ := combined.Get()
	cubedV, _ := cubed.Get()

	four, _ := squaredV.(int)
	five, _ := combinedV.(int)
	sixtyfour, _ := cubedV.(int)

	if four != 4 {
		test.Fatalf("Expected result of 2² to be 4")
	}

	if five != 5 {
		test.Fatalf("Expected result of 2 + 3 (%d) to be 5", five)
	}

	if sixtyfour != 64 {
		test.Fatalf("Expected result of 4³ to be 64")
	}
}

// Validate that promise.All works as expected.
func TestAll(test *testing.T) {
	expected := []int{1, 2}

	res, _ := All(Completed(1), Completed(2)).Then(func(result interface{}) interface{} {
		// Traversing through these means first getting a slice of anonymous
		// values...
		values, ok := result.([]interface{})

		if !ok {
			test.Fatalf("Expected a slice of []interface{}")
		}

		// Then looking at each value...
		for i, value := range values {
			observed, ok := value.(int)

			if !ok {
				test.Fatalf("Expected int type")
			}

			if expected[i] != observed {
				test.Fatalf("Expected %d != %d observed", expected[i], observed)
			}
		}

		return true
	}).Get()

	status := res.(bool)

	if !status {
		test.Fatalf("Test cases did not run")
	}
}

// Validate that rejections on completed promises work, and Catch on rejected
// promises works as expected.
func TestRejected(test *testing.T) {
	promise := Promise()

	catchWorked := false
	dependenciesToo := false

	var expected = errors.New("Expected error!")

	promise.Catch(func(err error) {
		if err != expected {
			test.Fatalf("Did not see expected error!")
		}

		catchWorked = true
	})

	promise.Then(func(val interface{}) interface{} {
		i, _ := val.(int)

		i += 1

		return i
	}).Catch(func(err error) {
		dependenciesToo = true
	})

	alsoRejected := promise.Combine(func(val interface{}) Thenable {
		return Completed(true)
	})

	promise.Reject(expected)

	if !catchWorked {
		test.Fatalf("Did not see expected rejected handler")
	}

	if !dependenciesToo {
		test.Fatalf("Did not see rejected handler on dependency")
	}

	promise.Catch(func(cause error) {
		if cause != expected {
			test.Fatalf("previously rejected promise did not pass on the cause")
		}
	})

	alsoRejected.Catch(func(cause error) {
		if cause != expected {
			test.Fatalf("Expected combined promises to result in errors also")
		}
	})

	rejectedCalled := false

	Rejected(expected).Catch(func(err error) {
		if err != expected {
			test.Fatalf("Rejected() promise does not relay messages.")
		}

		rejectedCalled = true
	})

	if !rejectedCalled {
		test.Fatalf("Rejected() did not invoke the onreject callback")
	}
}
<|endoftext|>


<commit_before>package prompt

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"
)

// YkmanProvider runs ykman to generate a OATH-TOTP token from the Yubikey device
// To set up ykman, first run `ykman oath add`
func YkmanMfaProvider(mfaSerial string) (string, error) {
	args := []string{}

	yubikeyOathCredName := os.Getenv("YKMAN_OATH_CREDENTIAL_NAME")
	if yubikeyOathCredName == "" {
		yubikeyOathCredName = mfaSerial
	}

	// Get the serial number of the yubikey device to use.
	yubikeyDeviceSerial := os.Getenv("YKMAN_OATH_DEVICE_SERIAL")
	if yubikeyDeviceSerial != "" {
		// If the env var was set, extend args to support passing the serial.
		args = append(args, "--device", yubikeyDeviceSerial)
	}

	// default to v4 and above
	ykmanMajorVersion := os.Getenv("AWS_VAULT_YKMAN_VERSION")
	switch ykmanMajorVersion {
	case "1", "2", "3":
		args = append(args, "oath", "code", "--single", yubikeyOathCredName)
	default:
		args = append(args, "oath", "accounts", "code", "--single", yubikeyOathCredName)
	}

	log.Printf("Fetching MFA code using `ykman %s`", strings.Join(args, " "))
	cmd := exec.Command("ykman", args...)
	cmd.Stderr = os.Stderr

	out, err := cmd.Output()
	if err != nil {
		return "", fmt.Errorf("ykman: %w", err)
	}

	return strings.TrimSpace(string(out)), nil
}

func init() {
	Methods["ykman"] = YkmanMfaProvider
}
<commit_msg>Update ykman.go<commit_after>package prompt

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"
)

// YkmanProvider runs ykman to generate a OATH-TOTP token from the Yubikey device
// To set up ykman, first run `ykman oath add`
func YkmanMfaProvider(mfaSerial string) (string, error) {
	args := []string{}

	yubikeyOathCredName := os.Getenv("YKMAN_OATH_CREDENTIAL_NAME")
	if yubikeyOathCredName == "" {
		yubikeyOathCredName = mfaSerial
	}

	// Get the serial number of the yubikey device to use.
	yubikeyDeviceSerial := os.Getenv("YKMAN_OATH_DEVICE_SERIAL")
	if yubikeyDeviceSerial != "" {
		// If the env var was set, extend args to support passing the serial.
		args = append(args, "--device", yubikeyDeviceSerial)
	}

	// default to v4 and above
	switch os.Getenv("AWS_VAULT_YKMAN_VERSION") {
	case "1", "2", "3":
		args = append(args, "oath", "code", "--single", yubikeyOathCredName)
	default:
		args = append(args, "oath", "accounts", "code", "--single", yubikeyOathCredName)
	}

	log.Printf("Fetching MFA code using `ykman %s`", strings.Join(args, " "))
	cmd := exec.Command("ykman", args...)
	cmd.Stderr = os.Stderr

	out, err := cmd.Output()
	if err != nil {
		return "", fmt.Errorf("ykman: %w", err)
	}

	return strings.TrimSpace(string(out)), nil
}

func init() {
	Methods["ykman"] = YkmanMfaProvider
}
<|endoftext|>


<commit_before>// Copyright (C) 2014 Constantin Schomburg <me@cschomburg.com>
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package proto

import (
	"errors"
	"sync"
	"time"
)

type Client struct {
	DeviceId       string
	RequestTimeout time.Duration

	conn                    Conn
	handler                 func(Message)
	log                     Logger
	subs                    []subscription
	onConnectionLostHandler func(error)

	reqMutex *sync.Mutex
	requests map[string]chan Message
}

func NewClient(deviceId string) *Client {
	c := &Client{
		deviceId,
		30 * time.Second,

		nil,
		nil,
		defaultLog,
		make([]subscription, 0),
		nil,

		&sync.Mutex{},
		make(map[string]chan Message),
	}
	c.internalSubscribe("", c.DeviceId, nil)
	c.internalSubscribe("ping", "", c.handlePing)
	return c
}

func (c *Client) OnConnectionLost(f func(error)) {
	c.onConnectionLostHandler = f
}

func (c *Client) Dial(cfg *NetConfig) error {
	conn, err := Dial(cfg)
	if err != nil {
		return err
	}
	return c.Connect(conn)
}

func (c *Client) Connect(conn Conn) error {
	c.conn = conn
	if err := c.Publish(CreateMessage("proto/subs", c.subs)); err != nil {
		return err
	}

	go c.listen(conn)
	return nil
}

func (c *Client) Disconnect() error {
	if c.conn == nil {
		return nil
	}
	err := c.conn.Close()
	c.conn = nil
	return err
}

func (c *Client) listen(conn Conn) error {
	for {
		msg, err := conn.Read()
		if err != nil {
			c.conn = nil
			c.log.Errorln("[client] read:", err)
			if c.onConnectionLostHandler != nil {
				c.onConnectionLostHandler(err)
			}
			return err
		}
		go c.handle(msg)
	}
}

func (c *Client) SetLogger(l Logger) {
	c.log = l
}

func (c *Client) fillMessage(msg *Message) {
	if msg.Version == "" {
		msg.Version = VERSION
	}
	if msg.Id == "" {
		msg.Id = GenerateId()
	}
	if msg.Source == "" {
		msg.Source = c.DeviceId
	}
}

func (c *Client) Publish(msg Message) error {
	c.fillMessage(&msg)
	if c.conn == nil {
		err := errors.New("not connected")
		c.log.Errorf("[client %s] publish error: ", c.DeviceId, err)
		return err
	}
	if err := c.conn.Write(msg); err != nil {
		c.log.Errorf("[client %s] publish error: %v", c.DeviceId, err)
		return err
	}
	return nil
}

func (c *Client) handle(msg Message) {
	if ok := c.resolveRequest(msg.CorrId, msg); ok {
		return
	}

	for _, s := range c.subs {
		if s.Matches(msg) && s.Handler != nil {
			s.Handler(msg)
		}
	}
}

func (c *Client) handlePing(msg Message) {
	c.log.Debugf("%s got ping", c.DeviceId)
	c.Reply(msg, CreateMessage("ack", nil))
}

func (c *Client) internalSubscribe(action, device string, h func(Message)) {
	if device == "" && action != "" {
		c.internalSubscribe(action, c.DeviceId, h)
	}
	c.subs = append(c.subs, subscription{
		action,
		device,
		h,
	})
}

func (c *Client) Subscribe(action, device string, h func(Message)) error {
	if h == nil {
		return errors.New("Invalid argument: no handler specified")
	}
	if device == "self" {
		device = c.DeviceId
	}
	c.internalSubscribe(action, device, h)
	return c.Publish(Subscribe(action, device))
}

func (c *Client) Reply(orig, reply Message) error {
	return c.Publish(orig.Reply(reply))
}

func (c *Client) ReplyBadRequest(orig Message, err error) error {
	c.log.Warnf("[client %s] bad request: %v, %v", c.DeviceId, orig, err)
	reply := orig.Reply(BadRequest(err))
	return c.Publish(reply)
}

func (c *Client) ReplyInternalError(orig Message, err error) error {
	c.log.Errorf("[client %s] internal error: %v, %v", c.DeviceId, orig, err)
	reply := orig.Reply(InternalError(err))
	return c.Publish(reply)
}

func (c *Client) Request(msg Message) <-chan Message {
	c.fillMessage(&msg)
	ch := make(chan Message, 1)
	if err := c.Publish(msg); err != nil {
		close(ch)
		return ch
	}

	go func(id string) {
		time.Sleep(c.RequestTimeout)
		c.resolveRequest(id, Message{})
	}(msg.Id)

	c.reqMutex.Lock()
	defer c.reqMutex.Unlock()
	c.requests[msg.Id] = ch
	return ch
}

func (c *Client) resolveRequest(id string, msg Message) bool {
	if id == "" {
		return false
	}

	c.reqMutex.Lock()
	defer c.reqMutex.Unlock()
	ch, ok := c.requests[id]
	if !ok {
		return false
	}

	if msg.Action != "" {
		ch <- msg
	} else {
		delete(c.requests, id)
		close(ch)
	}
	return true
}
<commit_msg>Proto: Minimalistic service discovery.<commit_after>// Copyright (C) 2014 Constantin Schomburg <me@cschomburg.com>
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package proto

import (
	"errors"
	"strings"
	"sync"
	"time"
)

type Client struct {
	DeviceId       string
	RequestTimeout time.Duration

	conn                    Conn
	handler                 func(Message)
	log                     Logger
	subs                    []subscription
	onConnectionLostHandler func(error)

	reqMutex *sync.Mutex
	requests map[string]chan Message
}

func NewClient(deviceId string) *Client {
	c := &Client{
		deviceId,
		30 * time.Second,

		nil,
		nil,
		defaultLog,
		make([]subscription, 0),
		nil,

		&sync.Mutex{},
		make(map[string]chan Message),
	}
	c.internalSubscribe("", c.DeviceId, nil)
	c.internalSubscribe("ping", "", c.handlePing)
	return c
}

func (c *Client) OnConnectionLost(f func(error)) {
	c.onConnectionLostHandler = f
}

func (c *Client) Dial(cfg *NetConfig) error {
	conn, err := Dial(cfg)
	if err != nil {
		return err
	}
	return c.Connect(conn)
}

func (c *Client) Connect(conn Conn) error {
	c.conn = conn
	if err := c.Publish(CreateMessage("proto/subs", c.subs)); err != nil {
		return err
	}

	go c.listen(conn)
	return nil
}

func (c *Client) Disconnect() error {
	if c.conn == nil {
		return nil
	}
	err := c.conn.Close()
	c.conn = nil
	return err
}

func (c *Client) listen(conn Conn) error {
	for {
		msg, err := conn.Read()
		if err != nil {
			c.conn = nil
			c.log.Errorln("[client] read:", err)
			if c.onConnectionLostHandler != nil {
				c.onConnectionLostHandler(err)
			}
			return err
		}
		go c.handle(msg)
	}
}

func (c *Client) SetLogger(l Logger) {
	c.log = l
}

func (c *Client) fillMessage(msg *Message) {
	if msg.Version == "" {
		msg.Version = VERSION
	}
	if msg.Id == "" {
		msg.Id = GenerateId()
	}
	if msg.Source == "" {
		msg.Source = c.DeviceId
	}
}

func (c *Client) Publish(msg Message) error {
	c.fillMessage(&msg)
	if c.conn == nil {
		err := errors.New("not connected")
		c.log.Errorf("[client %s] publish error: ", c.DeviceId, err)
		return err
	}
	if err := c.conn.Write(msg); err != nil {
		c.log.Errorf("[client %s] publish error: %v", c.DeviceId, err)
		return err
	}
	return nil
}

func (c *Client) handle(msg Message) {
	if ok := c.resolveRequest(msg.CorrId, msg); ok {
		return
	}

	for _, s := range c.subs {
		if s.Matches(msg) && s.Handler != nil {
			s.Handler(msg)
		}
	}
}

func (c *Client) handlePing(msg Message) {
	c.log.Debugf("%s got ping", c.DeviceId)
	c.Reply(msg, CreateMessage("ack", nil))
}

func (c *Client) internalSubscribe(action, device string, h func(Message)) {
	if device == "" && action != "" {
		c.internalSubscribe(action, c.DeviceId, h)
	}
	c.subs = append(c.subs, subscription{
		action,
		device,
		h,
	})
}

func (c *Client) Subscribe(action, device string, h func(Message)) error {
	if h == nil {
		return errors.New("Invalid argument: no handler specified")
	}
	if device == "self" {
		device = c.DeviceId
	}
	c.internalSubscribe(action, device, h)
	if err := c.Publish(Subscribe(action, device)); err != nil {
		return err
	}
	if !strings.HasPrefix(action, "proto/discover/") {
		return c.Subscribe("proto/discover/"+action, "", c.handlePing)
	}
	return nil
}

func (c *Client) Reply(orig, reply Message) error {
	return c.Publish(orig.Reply(reply))
}

func (c *Client) ReplyBadRequest(orig Message, err error) error {
	c.log.Warnf("[client %s] bad request: %v, %v", c.DeviceId, orig, err)
	reply := orig.Reply(BadRequest(err))
	return c.Publish(reply)
}

func (c *Client) ReplyInternalError(orig Message, err error) error {
	c.log.Errorf("[client %s] internal error: %v, %v", c.DeviceId, orig, err)
	reply := orig.Reply(InternalError(err))
	return c.Publish(reply)
}

func (c *Client) Request(msg Message) <-chan Message {
	c.fillMessage(&msg)
	ch := make(chan Message, 1)
	if err := c.Publish(msg); err != nil {
		close(ch)
		return ch
	}

	go func(id string) {
		time.Sleep(c.RequestTimeout)
		c.resolveRequest(id, Message{})
	}(msg.Id)

	c.reqMutex.Lock()
	defer c.reqMutex.Unlock()
	c.requests[msg.Id] = ch
	return ch
}

func (c *Client) resolveRequest(id string, msg Message) bool {
	if id == "" {
		return false
	}

	c.reqMutex.Lock()
	defer c.reqMutex.Unlock()
	ch, ok := c.requests[id]
	if !ok {
		return false
	}

	if msg.Action != "" {
		ch <- msg
	} else {
		delete(c.requests, id)
		close(ch)
	}
	return true
}
<|endoftext|>


<commit_before>package proxy

import (
	"encoding/json"
	"flag"
	"io/ioutil"

	"github.com/pkg/errors"
)

// Config describes the JSON config file selected via `-config` flag.
type Config struct {
	// AliasToARN maps human-friendly names to IAM ARNs.
	AliasToARN map[string]string `json:"aliasToARN"`
	// DefaultAlias is a AliasToARN key to select the default role for containers whose
	// metadata does not specify one.
	DefaultAlias string `json:"defaultAlias"`
	// DefaultPolicy restricts the effective role's permissions to the intersection of
	// the role's policy and this JSON policy.
	DefaultPolicy string `json:"defaultPolicy"`
	// DockerHost is a valid DOCKER_HOST string.
	DockerHost string `json:"dockerHost"`
	// ListenAddr is a TCP network address.
	ListenAddr string `json:"listen"`
	// Verbose enables request/response logging to standard out.
	Verbose bool
}

// NewConfigFromFlag constructs a new Config from the JSON file obtained via `-config` CLI flag.
// It also validates the unmarshaled Config fields.
func NewConfigFromFlag() (c Config, err error) {
	var configFile string

	flag.StringVar(&configFile, "c", "", "Path to JSON config file.")
	flag.Parse()

	configBytes, err := ioutil.ReadFile(configFile)
	if err != nil {
		return c, errors.Wrapf(err, "Error reading config file [%s]", configFile)
	}
	err = json.Unmarshal(configBytes, &c)
	if err != nil {
		return c, errors.Wrapf(err, "Error parsing config file JSON [%s]", configFile)
	}

	if c.ListenAddr == "" {
		return c, errors.New("Config file must select a server address ('listen', ex. ':18000').")
	}
	if len(c.AliasToARN) == 0 {
		return c, errors.New("Config file must include at least one 'aliasToARN' mapping.")
	}
	if c.AliasToARN[c.DefaultAlias] == "" {
		return c, errors.Errorf("Config file selected an default alias [%s] not mapped in `aliasToARN'.", c.DefaultAlias)
	}

	return c, nil
}
<commit_msg>fix: missing error message about missing config file<commit_after>package proxy

import (
	"encoding/json"
	"flag"
	"io/ioutil"

	"github.com/pkg/errors"
)

// Config describes the JSON config file selected via `-config` flag.
type Config struct {
	// AliasToARN maps human-friendly names to IAM ARNs.
	AliasToARN map[string]string `json:"aliasToARN"`
	// DefaultAlias is a AliasToARN key to select the default role for containers whose
	// metadata does not specify one.
	DefaultAlias string `json:"defaultAlias"`
	// DefaultPolicy restricts the effective role's permissions to the intersection of
	// the role's policy and this JSON policy.
	DefaultPolicy string `json:"defaultPolicy"`
	// DockerHost is a valid DOCKER_HOST string.
	DockerHost string `json:"dockerHost"`
	// ListenAddr is a TCP network address.
	ListenAddr string `json:"listen"`
	// Verbose enables request/response logging to standard out.
	Verbose bool
}

// NewConfigFromFlag constructs a new Config from the JSON file obtained via `-config` CLI flag.
// It also validates the unmarshaled Config fields.
func NewConfigFromFlag() (c Config, err error) {
	var configFile string

	flag.StringVar(&configFile, "c", "", "Path to JSON config file.")
	flag.Parse()

	if configFile == "" {
		return c, errors.New("'-config <file>' flag is required.")
	}

	configBytes, err := ioutil.ReadFile(configFile)
	if err != nil {
		return c, errors.Wrapf(err, "Error reading config file [%s]", configFile)
	}
	err = json.Unmarshal(configBytes, &c)
	if err != nil {
		return c, errors.Wrapf(err, "Error parsing config file JSON [%s]", configFile)
	}

	if c.ListenAddr == "" {
		return c, errors.New("Config file must select a server address ('listen', ex. ':18000').")
	}
	if len(c.AliasToARN) == 0 {
		return c, errors.New("Config file must include at least one 'aliasToARN' mapping.")
	}
	if c.AliasToARN[c.DefaultAlias] == "" {
		return c, errors.Errorf("Config file selected an default alias [%s] not mapped in `aliasToARN'.", c.DefaultAlias)
	}

	return c, nil
}
<|endoftext|>