<commit_before>// Copyright (c) 2017 The visa developers. All rights reserved.
// Project site: https://github.com/gotmc/ivi
// Use of this source code is governed by a MIT-style license that
// can be found in the LICENSE.txt file for the project.

package visa

import (
	"errors"
	"fmt"
	"regexp"
	"strconv"
	"strings"
)

// The ResourceTemplate struct fields come from Table 3.2.1 VISA Template
// Required Attributes in the VPP-4.3 The VISA Library standard.
type ResourceTemplate struct {
	ResourceImplementedVersion viVersion     // VI_ATTR_RSRC_IMPL_VERSION
	ResourceLockState          viAccessMode  // VI_ATTR_RSRC_LOCK_STATE
	ResourceManufacturerID     uint          // VI_ATTR_RSRC_MANF_ID
	ResourceManufacturerName   string        // VI_ATTR_RSRC_MANF_NAME
	ResourceName               viRsrc        // VI_ATTR_RSRC_NAME
	ResourceSpecVersion        viVersion     // VI_ATTR_RSRC_SPEC_VERSION
	resourceClass              resourceClass // VI_ATTR_RSRC_CLASS
}

func (resource ResourceTemplate) GetResourceClass() string {
	return string(resource.resourceClass)
}

type genericInstrResource struct {
	InterfaceNum            int           // VI_ATTR_INTF_NUM
	InterfaceType           interfaceType // VI_ATTR_INTF_TYPE
	InterfaceInstrumentName string        // VI_ATTR_INTF_INST_NAME
}

func determineInterfaceType(address string) (InterfaceType, error) {
	regexString := `^(?P<interfaceType>[A-Za-z]+)(?P<boardIndex>\d*)::` +
		`(?P<allElse>.*)$`
	re := regexp.MustCompile(regexString)
	res := re.FindStringSubmatch(address)
	subexpNames := re.SubexpNames()
	matchMap := map[string]string{}
	for i, n := range res {
		matchMap[subexpNames[i]] = string(n)
	}
	interfaceType := strings.ToUpper(matchMap["interfaceType"])
	switch interfaceType {
	case "USB":
		return USBTMC, nil
	case "TCPIP":
		return TCPIP, nil
	default:
		return ASRL, fmt.Errorf("%s is not a valid VISA interface type.", interfaceType)
	}
}

// NewResource creates a new Resource using the given VISA address.
func NewResource(address string) (Resource, error) {
	interfaceType, err := determineInterfaceType(address)
	if err != nil {
		return nil, errors.New("Problem determining interface type in address.")
	}
	driver, exists := drivers[interfaceType]
	if !exists {
		return nil, fmt.Errorf("The %s interface hasn't been registered.", interfaceType)
	}
	return driver.Open(address)
}

func createTCPIPResource(address string) (Resource, error) {
	return nil, errors.New("createTCPIPResource hasn't been implemented yet")
}

func createUSBResource(boardIndex int, partialResource string) (Resource, error) {
	regexString := `(?P<manufacturerID>[^\s:]+)::` +
		`(?P<modelCode>[^\s:]+)` +
		`(::(?P<serialNumber>[^\s:]+))?` +
		`::(?P<resourceClass>[^\s:]+)$`
	re := regexp.MustCompile(regexString)
	res := re.FindStringSubmatch(partialResource)
	subexpNames := re.SubexpNames()
	matchMap := map[string]string{}
	for i, n := range res {
		matchMap[subexpNames[i]] = string(n)
	}
	if matchMap["resourceClass"] != "INSTR" {
		return nil, errors.New("visa: resource class was not instr")
	}

	var resource UsbInstrResource

	resource.InterfaceNum = boardIndex
	resource.InterfaceType = usbInterface
	resource.resourceClass = instrResource

	if matchMap["manufacturerID"] != "" {
		manufacturerID, err := strconv.ParseUint(matchMap["manufacturerID"], 0, 16)
		if err != nil {
			return resource, errors.New("visa: manufacturerID error")
		}
		resource.ManufacturerID = uint16(manufacturerID)
	}

	if matchMap["modelCode"] != "" {
		modelCode, err := strconv.ParseUint(matchMap["modelCode"], 0, 16)
		if err != nil {
			return resource, errors.New("visa: modelCode error")
		}
		resource.ModelCode = uint16(modelCode)
	}

	// visa.SerialNumber = matchMap["serialNumber"]

	return resource, nil

}
<commit_msg>Delete VISA related properties/methods for now<commit_after>// Copyright (c) 2017 The visa developers. All rights reserved.
// Project site: https://github.com/gotmc/ivi
// Use of this source code is governed by a MIT-style license that
// can be found in the LICENSE.txt file for the project.

package visa

import (
	"errors"
	"fmt"
	"regexp"
	"strconv"
	"strings"
)

func determineInterfaceType(address string) (InterfaceType, error) {
	regexString := `^(?P<interfaceType>[A-Za-z]+)(?P<boardIndex>\d*)::` +
		`(?P<allElse>.*)$`
	re := regexp.MustCompile(regexString)
	res := re.FindStringSubmatch(address)
	subexpNames := re.SubexpNames()
	matchMap := map[string]string{}
	for i, n := range res {
		matchMap[subexpNames[i]] = string(n)
	}
	interfaceType := strings.ToUpper(matchMap["interfaceType"])
	switch interfaceType {
	case "USB":
		return USBTMC, nil
	case "TCPIP":
		return TCPIP, nil
	default:
		return ASRL, fmt.Errorf("%s is not a valid VISA interface type.", interfaceType)
	}
}

// NewResource creates a new Resource using the given VISA address.
func NewResource(address string) (Resource, error) {
	interfaceType, err := determineInterfaceType(address)
	if err != nil {
		return nil, errors.New("Problem determining interface type in address.")
	}
	driver, exists := drivers[interfaceType]
	if !exists {
		return nil, fmt.Errorf("The %s interface hasn't been registered.", interfaceType)
	}
	return driver.Open(address)
}

func createTCPIPResource(address string) (Resource, error) {
	return nil, errors.New("createTCPIPResource hasn't been implemented yet")
}

func createUSBResource(boardIndex int, partialResource string) (Resource, error) {
	regexString := `(?P<manufacturerID>[^\s:]+)::` +
		`(?P<modelCode>[^\s:]+)` +
		`(::(?P<serialNumber>[^\s:]+))?` +
		`::(?P<resourceClass>[^\s:]+)$`
	re := regexp.MustCompile(regexString)
	res := re.FindStringSubmatch(partialResource)
	subexpNames := re.SubexpNames()
	matchMap := map[string]string{}
	for i, n := range res {
		matchMap[subexpNames[i]] = string(n)
	}
	if matchMap["resourceClass"] != "INSTR" {
		return nil, errors.New("visa: resource class was not instr")
	}

	var resource UsbInstrResource

	resource.InterfaceNum = boardIndex
	resource.InterfaceType = usbInterface
	resource.resourceClass = instrResource

	if matchMap["manufacturerID"] != "" {
		manufacturerID, err := strconv.ParseUint(matchMap["manufacturerID"], 0, 16)
		if err != nil {
			return resource, errors.New("visa: manufacturerID error")
		}
		resource.ManufacturerID = uint16(manufacturerID)
	}

	if matchMap["modelCode"] != "" {
		modelCode, err := strconv.ParseUint(matchMap["modelCode"], 0, 16)
		if err != nil {
			return resource, errors.New("visa: modelCode error")
		}
		resource.ModelCode = uint16(modelCode)
	}

	// visa.SerialNumber = matchMap["serialNumber"]

	return resource, nil

}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package ebs

import (
	"encoding/json"
	"errors"
	"log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/ernestio/ernestaws"
	"github.com/ernestio/ernestaws/credentials"
)

var (
	// ErrDatacenterIDInvalid ...
	ErrDatacenterIDInvalid = errors.New("Datacenter VPC ID invalid")
	// ErrDatacenterRegionInvalid ...
	ErrDatacenterRegionInvalid = errors.New("Datacenter Region invalid")
	// ErrDatacenterCredentialsInvalid ...
	ErrDatacenterCredentialsInvalid = errors.New("Datacenter credentials invalid")
	// ErrVolumeIDInvalid ...
	ErrVolumeIDInvalid = errors.New("EBS volume aws id invalid")
	// ErrAvailabilityZoneInvalid ...
	ErrAvailabilityZoneInvalid = errors.New("Availability zone invalid")
	// ErrVolumeNameInvalid ...
	ErrVolumeNameInvalid = errors.New("EBS volume name invalid")
	// ErrVolumeTypeInvalid ...
	ErrVolumeTypeInvalid = errors.New("EBS volume type invalid")
)

// Event stores the template data
type Event struct {
	UUID                  string `json:"_uuid"`
	BatchID               string `json:"_batch_id"`
	ProviderType          string `json:"_type"`
	VPCID                 string `json:"vpc_id"`
	DatacenterRegion      string `json:"datacenter_region"`
	DatacenterAccessKey   string `json:"datacenter_secret"`
	DatacenterAccessToken string `json:"datacenter_token"`
	VolumeAWSID           string `json:"volume_aws_id"`
	Name                  string `json:"name"`
	AvailabilityZone      string `json:"availability_zone"`
	VolumeType            string `json:"volume_type"`
	Size                  *int64 `json:"size"`
	Iops                  *int64 `json:"iops"`
	Encrypted             bool   `json:"encrypted"`
	EncryptionKeyID       string `json:"encryption_key_id"`
	ErrorMessage          string `json:"error,omitempty"`
	Subject               string `json:"-"`
	Body                  []byte `json:"-"`
	CryptoKey             []byte `json:"-"`
}

// New : Constructor
func New(subject string, body, cryptoKey []byte) ernestaws.Event {
	n := Event{Subject: subject, Body: body, CryptoKey: cryptoKey}

	return &n
}

// GetBody : Gets the body for this event
func (ev *Event) GetBody() []byte {
	var err error
	if ev.Body, err = json.Marshal(ev); err != nil {
		log.Println(err.Error())
	}
	return ev.Body
}

// GetSubject : Gets the subject for this event
func (ev *Event) GetSubject() string {
	return ev.Subject
}

// Process : starts processing the current message
func (ev *Event) Process() (err error) {
	if err := json.Unmarshal(ev.Body, &ev); err != nil {
		ev.Error(err)
		return err
	}

	if err := ev.Validate(); err != nil {
		ev.Error(err)
		return err
	}

	return nil
}

// Error : Will respond the current event with an error
func (ev *Event) Error(err error) {
	log.Printf("Error: %s", err.Error())
	ev.ErrorMessage = err.Error()

	ev.Body, err = json.Marshal(ev)
}

// Validate checks if all criteria are met
func (ev *Event) Validate() error {
	if ev.VPCID == "" {
		return ErrDatacenterIDInvalid
	}

	if ev.DatacenterRegion == "" {
		return ErrDatacenterRegionInvalid
	}

	if ev.DatacenterAccessKey == "" || ev.DatacenterAccessToken == "" {
		return ErrDatacenterCredentialsInvalid
	}

	if ev.Subject != "ebs.delete.aws" {
		if ev.VolumeAWSID == "" {
			return ErrVolumeIDInvalid
		}
	}

	if ev.Name == "" {
		return ErrVolumeNameInvalid
	}

	if ev.AvailabilityZone == "" {
		return ErrAvailabilityZoneInvalid
	}

	if ev.VolumeType == "" {
		return ErrVolumeTypeInvalid
	}

	return nil
}

// Create : Creates a instance object on aws
func (ev *Event) Create() error {
	creds, _ := credentials.NewStaticCredentials(ev.DatacenterAccessKey, ev.DatacenterAccessToken, ev.CryptoKey)
	svc := ec2.New(session.New(), &aws.Config{
		Region:      aws.String(ev.DatacenterRegion),
		Credentials: creds,
	})

	req := &ec2.CreateVolumeInput{
		AvailabilityZone: aws.String(ev.AvailabilityZone),
		VolumeType:       aws.String(ev.VolumeType),
		Size:             ev.Size,
		Iops:             ev.Iops,
		Encrypted:        aws.Bool(ev.Encrypted),
		KmsKeyId:         aws.String(ev.EncryptionKeyID),
	}

	resp, err := svc.CreateVolume(req)
	if err != nil {
		return err
	}

	ev.VolumeAWSID = *resp.VolumeId

	return nil
}

// Update : Updates a instance object on aws
func (ev *Event) Update() error {
	return errors.New(ev.Subject + " not supported")
}

// Delete : Deletes a instance object on aws
func (ev *Event) Delete() error {
	creds, _ := credentials.NewStaticCredentials(ev.DatacenterAccessKey, ev.DatacenterAccessToken, ev.CryptoKey)
	svc := ec2.New(session.New(), &aws.Config{
		Region:      aws.String(ev.DatacenterRegion),
		Credentials: creds,
	})

	req := &ec2.DeleteVolumeInput{
		VolumeId: aws.String(ev.VolumeAWSID),
	}

	_, err := svc.DeleteVolume(req)
	if err != nil {
		return err
	}

	return nil
}

// Get : Gets a instance object on aws
func (ev *Event) Get() error {
	return errors.New(ev.Subject + " not supported")
}
<commit_msg>fixed ebs validation<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package ebs

import (
	"encoding/json"
	"errors"
	"log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/ernestio/ernestaws"
	"github.com/ernestio/ernestaws/credentials"
)

var (
	// ErrDatacenterIDInvalid ...
	ErrDatacenterIDInvalid = errors.New("Datacenter VPC ID invalid")
	// ErrDatacenterRegionInvalid ...
	ErrDatacenterRegionInvalid = errors.New("Datacenter Region invalid")
	// ErrDatacenterCredentialsInvalid ...
	ErrDatacenterCredentialsInvalid = errors.New("Datacenter credentials invalid")
	// ErrVolumeIDInvalid ...
	ErrVolumeIDInvalid = errors.New("EBS volume aws id invalid")
	// ErrAvailabilityZoneInvalid ...
	ErrAvailabilityZoneInvalid = errors.New("Availability zone invalid")
	// ErrVolumeNameInvalid ...
	ErrVolumeNameInvalid = errors.New("EBS volume name invalid")
	// ErrVolumeTypeInvalid ...
	ErrVolumeTypeInvalid = errors.New("EBS volume type invalid")
)

// Event stores the template data
type Event struct {
	UUID                  string `json:"_uuid"`
	BatchID               string `json:"_batch_id"`
	ProviderType          string `json:"_type"`
	VPCID                 string `json:"vpc_id"`
	DatacenterRegion      string `json:"datacenter_region"`
	DatacenterAccessKey   string `json:"datacenter_secret"`
	DatacenterAccessToken string `json:"datacenter_token"`
	VolumeAWSID           string `json:"volume_aws_id"`
	Name                  string `json:"name"`
	AvailabilityZone      string `json:"availability_zone"`
	VolumeType            string `json:"volume_type"`
	Size                  *int64 `json:"size"`
	Iops                  *int64 `json:"iops"`
	Encrypted             bool   `json:"encrypted"`
	EncryptionKeyID       string `json:"encryption_key_id"`
	ErrorMessage          string `json:"error,omitempty"`
	Subject               string `json:"-"`
	Body                  []byte `json:"-"`
	CryptoKey             []byte `json:"-"`
}

// New : Constructor
func New(subject string, body, cryptoKey []byte) ernestaws.Event {
	n := Event{Subject: subject, Body: body, CryptoKey: cryptoKey}

	return &n
}

// GetBody : Gets the body for this event
func (ev *Event) GetBody() []byte {
	var err error
	if ev.Body, err = json.Marshal(ev); err != nil {
		log.Println(err.Error())
	}
	return ev.Body
}

// GetSubject : Gets the subject for this event
func (ev *Event) GetSubject() string {
	return ev.Subject
}

// Process : starts processing the current message
func (ev *Event) Process() (err error) {
	if err := json.Unmarshal(ev.Body, &ev); err != nil {
		ev.Error(err)
		return err
	}

	if err := ev.Validate(); err != nil {
		ev.Error(err)
		return err
	}

	return nil
}

// Error : Will respond the current event with an error
func (ev *Event) Error(err error) {
	log.Printf("Error: %s", err.Error())
	ev.ErrorMessage = err.Error()

	ev.Body, err = json.Marshal(ev)
}

// Validate checks if all criteria are met
func (ev *Event) Validate() error {
	if ev.VPCID == "" {
		return ErrDatacenterIDInvalid
	}

	if ev.DatacenterRegion == "" {
		return ErrDatacenterRegionInvalid
	}

	if ev.DatacenterAccessKey == "" || ev.DatacenterAccessToken == "" {
		return ErrDatacenterCredentialsInvalid
	}

	if ev.Subject != "ebs.create.aws" {
		if ev.VolumeAWSID == "" {
			return ErrVolumeIDInvalid
		}
	}

	if ev.Name == "" {
		return ErrVolumeNameInvalid
	}

	if ev.AvailabilityZone == "" {
		return ErrAvailabilityZoneInvalid
	}

	if ev.VolumeType == "" {
		return ErrVolumeTypeInvalid
	}

	return nil
}

// Create : Creates a instance object on aws
func (ev *Event) Create() error {
	creds, _ := credentials.NewStaticCredentials(ev.DatacenterAccessKey, ev.DatacenterAccessToken, ev.CryptoKey)
	svc := ec2.New(session.New(), &aws.Config{
		Region:      aws.String(ev.DatacenterRegion),
		Credentials: creds,
	})

	req := &ec2.CreateVolumeInput{
		AvailabilityZone: aws.String(ev.AvailabilityZone),
		VolumeType:       aws.String(ev.VolumeType),
		Size:             ev.Size,
		Iops:             ev.Iops,
		Encrypted:        aws.Bool(ev.Encrypted),
		KmsKeyId:         aws.String(ev.EncryptionKeyID),
	}

	resp, err := svc.CreateVolume(req)
	if err != nil {
		return err
	}

	ev.VolumeAWSID = *resp.VolumeId

	return nil
}

// Update : Updates a instance object on aws
func (ev *Event) Update() error {
	return errors.New(ev.Subject + " not supported")
}

// Delete : Deletes a instance object on aws
func (ev *Event) Delete() error {
	creds, _ := credentials.NewStaticCredentials(ev.DatacenterAccessKey, ev.DatacenterAccessToken, ev.CryptoKey)
	svc := ec2.New(session.New(), &aws.Config{
		Region:      aws.String(ev.DatacenterRegion),
		Credentials: creds,
	})

	req := &ec2.DeleteVolumeInput{
		VolumeId: aws.String(ev.VolumeAWSID),
	}

	_, err := svc.DeleteVolume(req)
	if err != nil {
		return err
	}

	return nil
}

// Get : Gets a instance object on aws
func (ev *Event) Get() error {
	return errors.New(ev.Subject + " not supported")
}
<|endoftext|>


<commit_before>package eval

// Builtin functions.

import (
	"bufio"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"os"
	"reflect"
	"runtime"
	"strconv"
	"time"

	"github.com/elves/elvish/logutil"
)

var builtinFns []*BuiltinFn

func init() {
	// Needed to work around init loop.
	builtinFns = []*BuiltinFn{
		&BuiltinFn{":", nop},
		&BuiltinFn{"true", nop},

		&BuiltinFn{"print", wrapFn(print)},
		&BuiltinFn{"println", wrapFn(println)},

		&BuiltinFn{"into-lines", wrapFn(intoLines)},
		&BuiltinFn{"from-lines", wrapFn(fromLines)},

		&BuiltinFn{"rat", wrapFn(ratFn)},

		&BuiltinFn{"put", put},
		&BuiltinFn{"put-all", wrapFn(putAll)},
		&BuiltinFn{"unpack", wrapFn(unpack)},

		&BuiltinFn{"from-json", wrapFn(fromJSON)},

		&BuiltinFn{"kind-of", kindOf},

		&BuiltinFn{"fail", wrapFn(fail)},
		&BuiltinFn{"multi-error", wrapFn(multiErrorFn)},
		&BuiltinFn{"return", wrapFn(returnFn)},
		&BuiltinFn{"break", wrapFn(breakFn)},
		&BuiltinFn{"continue", wrapFn(continueFn)},

		&BuiltinFn{"each", wrapFn(each)},

		&BuiltinFn{"cd", cd},
		&BuiltinFn{"dirs", wrapFn(dirs)},
		&BuiltinFn{"jump", wrapFn(jump)},

		&BuiltinFn{"source", wrapFn(source)},

		&BuiltinFn{"+", wrapFn(plus)},
		&BuiltinFn{"-", wrapFn(minus)},
		&BuiltinFn{"mul", wrapFn(times)},
		&BuiltinFn{"div", wrapFn(divide)},

		&BuiltinFn{"=", eq},
		&BuiltinFn{"deepeq", deepeq},

		&BuiltinFn{"take", wrapFn(take)},
		&BuiltinFn{"drop", wrapFn(drop)},

		&BuiltinFn{"-sleep", wrapFn(_sleep)},
		&BuiltinFn{"-stack", wrapFn(_stack)},
		&BuiltinFn{"-log", wrapFn(_log)},
	}
}

var (
	ErrArgs              = errors.New("args error")
	ErrInput             = errors.New("input error")
	ErrStoreNotConnected = errors.New("store not connected")
	ErrNoMatchingDir     = errors.New("no matching directory")
)

var (
	evalCtxType = reflect.TypeOf((*EvalCtx)(nil))
	valueType   = reflect.TypeOf((*Value)(nil)).Elem()
)

// wrapFn wraps an inner function into one suitable as a builtin function. It
// generates argument checking and conversion code according to the signature
// of the inner function. The inner function must accept evalCtx* as the first
// argument and return an exitus.
func wrapFn(inner interface{}) func(*EvalCtx, []Value) {
	type_ := reflect.TypeOf(inner)
	if type_.In(0) != evalCtxType {
		panic("bad func")
	}

	requiredArgs := type_.NumIn() - 1
	isVariadic := type_.IsVariadic()
	var variadicType reflect.Type
	if isVariadic {
		requiredArgs--
		variadicType = type_.In(type_.NumIn() - 1).Elem()
		if !supportedIn(variadicType) {
			panic("bad func argument")
		}
	}

	for i := 0; i < requiredArgs; i++ {
		if !supportedIn(type_.In(i + 1)) {
			panic("bad func argument")
		}
	}

	return func(ec *EvalCtx, args []Value) {
		if len(args) < requiredArgs || (!isVariadic && len(args) > requiredArgs) {
			throw(ErrArgs)
		}
		callArgs := make([]reflect.Value, len(args)+1)
		callArgs[0] = reflect.ValueOf(ec)

		ok := convertArgs(args[:requiredArgs], callArgs[1:],
			func(i int) reflect.Type { return type_.In(i + 1) })
		if !ok {
			throw(ErrArgs)
		}
		if isVariadic {
			ok := convertArgs(args[requiredArgs:], callArgs[1+requiredArgs:],
				func(i int) reflect.Type { return variadicType })
			if !ok {
				throw(ErrArgs)
			}
		}
		reflect.ValueOf(inner).Call(callArgs)
	}
}

func supportedIn(t reflect.Type) bool {
	return t.Kind() == reflect.String ||
		t.Kind() == reflect.Int || t.Kind() == reflect.Float64 ||
		t.Implements(valueType)
}

func convertArgs(args []Value, callArgs []reflect.Value, callType func(int) reflect.Type) bool {
	for i, arg := range args {
		var callArg interface{}
		switch callType(i).Kind() {
		case reflect.String:
			callArg = ToString(arg)
		case reflect.Int:
			var err error
			callArg, err = toInt(arg)
			if err != nil {
				return false
			}
		case reflect.Float64:
			var err error
			callArg, err = toFloat(arg)
			if err != nil {
				return false
				// return err
			}
		default:
			if reflect.TypeOf(arg).ConvertibleTo(callType(i)) {
				callArg = arg
			} else {
				return false
				// return argsError
			}
		}
		callArgs[i] = reflect.ValueOf(callArg)
	}
	return true
}

func nop(ec *EvalCtx, args []Value) {
}

func put(ec *EvalCtx, args []Value) {
	out := ec.ports[1].Chan
	for _, a := range args {
		out <- a
	}
}

func putAll(ec *EvalCtx, lists ...List) {
	out := ec.ports[1].Chan
	for _, list := range lists {
		for _, x := range *list.inner {
			out <- x
		}
	}
}

func kindOf(ec *EvalCtx, args []Value) {
	out := ec.ports[1].Chan
	for _, a := range args {
		out <- String(a.Kind())
	}
}

func fail(ec *EvalCtx, arg Value) {
	throw(errors.New(ToString(arg)))
}

func multiErrorFn(ec *EvalCtx, args ...Error) {
	throw(multiError{args})
}

func returnFn(ec *EvalCtx) {
	throw(Return)
}

func breakFn(ec *EvalCtx) {
	throw(Break)
}

func continueFn(ec *EvalCtx) {
	throw(Continue)
}

func print(ec *EvalCtx, args ...string) {
	out := ec.ports[1].File
	for i, arg := range args {
		if i > 0 {
			out.WriteString(" ")
		}
		out.WriteString(arg)
	}
}

func println(ec *EvalCtx, args ...string) {
	print(ec, args...)
	ec.ports[1].File.WriteString("\n")
}

func intoLines(ec *EvalCtx) {
	in := ec.ports[0].Chan
	out := ec.ports[1].File

	for v := range in {
		fmt.Fprintln(out, ToString(v))
	}
}

func fromLines(ec *EvalCtx) {
	in := ec.ports[0].File
	out := ec.ports[1].Chan

	bufferedIn := bufio.NewReader(in)
	for {
		line, err := bufferedIn.ReadString('\n')
		if err == io.EOF {
			return
		} else if err != nil {
			throw(err)
		}
		out <- String(line[:len(line)-1])
	}
}

func ratFn(ec *EvalCtx, arg Value) {
	out := ec.ports[1].Chan
	r, err := ToRat(arg)
	if err != nil {
		throw(err)
	}
	out <- r
}

// unpack takes any number of tables and output their list elements.
func unpack(ec *EvalCtx) {
	in := ec.ports[0].Chan
	out := ec.ports[1].Chan

	for v := range in {
		if list, ok := v.(List); !ok {
			throw(ErrInput)
		} else {
			for _, e := range *list.inner {
				out <- e
			}
		}
	}
}

// fromJSON parses a stream of JSON data into Value's.
func fromJSON(ec *EvalCtx) {
	in := ec.ports[0].File
	out := ec.ports[1].Chan

	dec := json.NewDecoder(in)
	var v interface{}
	for {
		err := dec.Decode(&v)
		if err != nil {
			if err == io.EOF {
				return
			}
			throw(err)
		}
		out <- FromJSONInterface(v)
	}
}

// each takes a single closure and applies it to all input values.
func each(ec *EvalCtx, f *Closure) {
	in := ec.ports[0].Chan
in:
	for v := range in {
		newec := ec.fork("closure of each")
		ex := newec.PCall(f, []Value{v})
		ClosePorts(newec.ports)

		switch ex {
		case nil, Continue:
			// nop
		case Break:
			break in
		default:
			throw(ex)
		}
	}
}

func cd(ec *EvalCtx, args []Value) {
	var dir string
	if len(args) == 0 {
		dir = mustGetHome("")
	} else if len(args) == 1 {
		dir = ToString(args[0])
	} else {
		throw(ErrArgs)
	}

	cdInner(dir, ec)
}

func cdInner(dir string, ec *EvalCtx) {
	err := os.Chdir(dir)
	if err != nil {
		throw(err)
	}
	if ec.store != nil {
		pwd, err := os.Getwd()
		// BUG(xiaq): Possible error of os.Getwd after cd-ing is ignored.
		if err == nil {
			ec.store.AddDir(pwd)
		}
	}
}

func dirs(ec *EvalCtx) {
	if ec.store == nil {
		throw(ErrStoreNotConnected)
	}
	dirs, err := ec.store.ListDirs()
	if err != nil {
		throw(errors.New("store error: " + err.Error()))
	}
	out := ec.ports[1].Chan
	for _, dir := range dirs {
		out <- Map{&map[Value]Value{
			String("path"):  String(dir.Path),
			String("score"): String(fmt.Sprint(dir.Score)),
		}}
	}
}

func jump(ec *EvalCtx, arg string) {
	if ec.store == nil {
		throw(ErrStoreNotConnected)
	}
	dirs, err := ec.store.FindDirs(arg)
	if err != nil {
		throw(errors.New("store error: " + err.Error()))
	}
	if len(dirs) == 0 {
		throw(ErrNoMatchingDir)
	}
	dir := dirs[0].Path
	err = os.Chdir(dir)
	// TODO(xiaq): Remove directories that no longer exist
	if err != nil {
		throw(err)
	}
	ec.store.AddDir(dir)
}

func source(ec *EvalCtx, fname string) {
	ec.Source(fname)
}

func toFloat(arg Value) (float64, error) {
	arg, ok := arg.(String)
	if !ok {
		return 0, fmt.Errorf("must be string")
	}
	num, err := strconv.ParseFloat(string(arg.(String)), 64)
	if err != nil {
		return 0, err
	}
	return num, nil
}

func toInt(arg Value) (int, error) {
	arg, ok := arg.(String)
	if !ok {
		return 0, fmt.Errorf("must be string")
	}
	num, err := strconv.Atoi(string(arg.(String)))
	if err != nil {
		return 0, err
	}
	return num, nil
}

func plus(ec *EvalCtx, nums ...float64) {
	out := ec.ports[1].Chan
	sum := 0.0
	for _, f := range nums {
		sum += f
	}
	out <- String(fmt.Sprintf("%g", sum))
}

func minus(ec *EvalCtx, sum float64, nums ...float64) {
	out := ec.ports[1].Chan
	for _, f := range nums {
		sum -= f
	}
	out <- String(fmt.Sprintf("%g", sum))
}

func times(ec *EvalCtx, nums ...float64) {
	out := ec.ports[1].Chan
	prod := 1.0
	for _, f := range nums {
		prod *= f
	}
	out <- String(fmt.Sprintf("%g", prod))
}

func divide(ec *EvalCtx, prod float64, nums ...float64) {
	out := ec.ports[1].Chan
	for _, f := range nums {
		prod /= f
	}
	out <- String(fmt.Sprintf("%g", prod))
}

func eq(ec *EvalCtx, args []Value) {
	out := ec.ports[1].Chan
	if len(args) == 0 {
		throw(ErrArgs)
	}
	for i := 0; i+1 < len(args); i++ {
		if args[i] != args[i+1] {
			out <- Bool(false)
			return
		}
	}
	out <- Bool(true)
}

func deepeq(ec *EvalCtx, args []Value) {
	out := ec.ports[1].Chan
	if len(args) == 0 {
		throw(ErrArgs)
	}
	for i := 0; i+1 < len(args); i++ {
		if !DeepEq(args[i], args[i+1]) {
			out <- Bool(false)
			return
		}
	}
	out <- Bool(true)
}

func take(ec *EvalCtx, n int) {
	in := ec.ports[0].Chan
	out := ec.ports[1].Chan

	i := 0
	for v := range in {
		if i >= n {
			break
		}
		i++
		out <- v
	}
}

func drop(ec *EvalCtx, n int) {
	in := ec.ports[0].Chan
	out := ec.ports[1].Chan

	for i := 0; i < n; i++ {
		<-in
	}
	for v := range in {
		out <- v
	}
}

func _sleep(ec *EvalCtx, t float64) {
	time.Sleep(time.Duration(t) * time.Second)
}

func _stack(ec *EvalCtx) {
	out := ec.ports[1].File

	// XXX dup with main.go
	buf := make([]byte, 1024)
	for runtime.Stack(buf, true) == cap(buf) {
		buf = make([]byte, cap(buf)*2)
	}
	out.Write(buf)
}

func _log(ec *EvalCtx, fname string) {
	maybeThrow(logutil.SetOutputFile(fname))
}
<commit_msg>Add an "-exec" builtin.<commit_after>package eval

// Builtin functions.

import (
	"bufio"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"os"
	"reflect"
	"runtime"
	"strconv"
	"syscall"
	"time"

	"github.com/elves/elvish/logutil"
)

var builtinFns []*BuiltinFn

func init() {
	// Needed to work around init loop.
	builtinFns = []*BuiltinFn{
		&BuiltinFn{":", nop},
		&BuiltinFn{"true", nop},

		&BuiltinFn{"print", wrapFn(print)},
		&BuiltinFn{"println", wrapFn(println)},

		&BuiltinFn{"into-lines", wrapFn(intoLines)},
		&BuiltinFn{"from-lines", wrapFn(fromLines)},

		&BuiltinFn{"rat", wrapFn(ratFn)},

		&BuiltinFn{"put", put},
		&BuiltinFn{"put-all", wrapFn(putAll)},
		&BuiltinFn{"unpack", wrapFn(unpack)},

		&BuiltinFn{"from-json", wrapFn(fromJSON)},

		&BuiltinFn{"kind-of", kindOf},

		&BuiltinFn{"fail", wrapFn(fail)},
		&BuiltinFn{"multi-error", wrapFn(multiErrorFn)},
		&BuiltinFn{"return", wrapFn(returnFn)},
		&BuiltinFn{"break", wrapFn(breakFn)},
		&BuiltinFn{"continue", wrapFn(continueFn)},

		&BuiltinFn{"each", wrapFn(each)},

		&BuiltinFn{"cd", cd},
		&BuiltinFn{"dirs", wrapFn(dirs)},
		&BuiltinFn{"jump", wrapFn(jump)},

		&BuiltinFn{"source", wrapFn(source)},

		&BuiltinFn{"+", wrapFn(plus)},
		&BuiltinFn{"-", wrapFn(minus)},
		&BuiltinFn{"mul", wrapFn(times)},
		&BuiltinFn{"div", wrapFn(divide)},

		&BuiltinFn{"=", eq},
		&BuiltinFn{"deepeq", deepeq},

		&BuiltinFn{"take", wrapFn(take)},
		&BuiltinFn{"drop", wrapFn(drop)},

		&BuiltinFn{"-sleep", wrapFn(_sleep)},
		&BuiltinFn{"-stack", wrapFn(_stack)},
		&BuiltinFn{"-log", wrapFn(_log)},
		&BuiltinFn{"-exec", wrapFn(_exec)},
	}
}

var (
	ErrArgs              = errors.New("args error")
	ErrInput             = errors.New("input error")
	ErrStoreNotConnected = errors.New("store not connected")
	ErrNoMatchingDir     = errors.New("no matching directory")
)

var (
	evalCtxType = reflect.TypeOf((*EvalCtx)(nil))
	valueType   = reflect.TypeOf((*Value)(nil)).Elem()
)

// wrapFn wraps an inner function into one suitable as a builtin function. It
// generates argument checking and conversion code according to the signature
// of the inner function. The inner function must accept evalCtx* as the first
// argument and return an exitus.
func wrapFn(inner interface{}) func(*EvalCtx, []Value) {
	type_ := reflect.TypeOf(inner)
	if type_.In(0) != evalCtxType {
		panic("bad func")
	}

	requiredArgs := type_.NumIn() - 1
	isVariadic := type_.IsVariadic()
	var variadicType reflect.Type
	if isVariadic {
		requiredArgs--
		variadicType = type_.In(type_.NumIn() - 1).Elem()
		if !supportedIn(variadicType) {
			panic("bad func argument")
		}
	}

	for i := 0; i < requiredArgs; i++ {
		if !supportedIn(type_.In(i + 1)) {
			panic("bad func argument")
		}
	}

	return func(ec *EvalCtx, args []Value) {
		if len(args) < requiredArgs || (!isVariadic && len(args) > requiredArgs) {
			throw(ErrArgs)
		}
		callArgs := make([]reflect.Value, len(args)+1)
		callArgs[0] = reflect.ValueOf(ec)

		ok := convertArgs(args[:requiredArgs], callArgs[1:],
			func(i int) reflect.Type { return type_.In(i + 1) })
		if !ok {
			throw(ErrArgs)
		}
		if isVariadic {
			ok := convertArgs(args[requiredArgs:], callArgs[1+requiredArgs:],
				func(i int) reflect.Type { return variadicType })
			if !ok {
				throw(ErrArgs)
			}
		}
		reflect.ValueOf(inner).Call(callArgs)
	}
}

func supportedIn(t reflect.Type) bool {
	return t.Kind() == reflect.String ||
		t.Kind() == reflect.Int || t.Kind() == reflect.Float64 ||
		t.Implements(valueType)
}

func convertArgs(args []Value, callArgs []reflect.Value, callType func(int) reflect.Type) bool {
	for i, arg := range args {
		var callArg interface{}
		switch callType(i).Kind() {
		case reflect.String:
			callArg = ToString(arg)
		case reflect.Int:
			var err error
			callArg, err = toInt(arg)
			if err != nil {
				return false
			}
		case reflect.Float64:
			var err error
			callArg, err = toFloat(arg)
			if err != nil {
				return false
				// return err
			}
		default:
			if reflect.TypeOf(arg).ConvertibleTo(callType(i)) {
				callArg = arg
			} else {
				return false
				// return argsError
			}
		}
		callArgs[i] = reflect.ValueOf(callArg)
	}
	return true
}

func nop(ec *EvalCtx, args []Value) {
}

func put(ec *EvalCtx, args []Value) {
	out := ec.ports[1].Chan
	for _, a := range args {
		out <- a
	}
}

func putAll(ec *EvalCtx, lists ...List) {
	out := ec.ports[1].Chan
	for _, list := range lists {
		for _, x := range *list.inner {
			out <- x
		}
	}
}

func kindOf(ec *EvalCtx, args []Value) {
	out := ec.ports[1].Chan
	for _, a := range args {
		out <- String(a.Kind())
	}
}

func fail(ec *EvalCtx, arg Value) {
	throw(errors.New(ToString(arg)))
}

func multiErrorFn(ec *EvalCtx, args ...Error) {
	throw(multiError{args})
}

func returnFn(ec *EvalCtx) {
	throw(Return)
}

func breakFn(ec *EvalCtx) {
	throw(Break)
}

func continueFn(ec *EvalCtx) {
	throw(Continue)
}

func print(ec *EvalCtx, args ...string) {
	out := ec.ports[1].File
	for i, arg := range args {
		if i > 0 {
			out.WriteString(" ")
		}
		out.WriteString(arg)
	}
}

func println(ec *EvalCtx, args ...string) {
	print(ec, args...)
	ec.ports[1].File.WriteString("\n")
}

func intoLines(ec *EvalCtx) {
	in := ec.ports[0].Chan
	out := ec.ports[1].File

	for v := range in {
		fmt.Fprintln(out, ToString(v))
	}
}

func fromLines(ec *EvalCtx) {
	in := ec.ports[0].File
	out := ec.ports[1].Chan

	bufferedIn := bufio.NewReader(in)
	for {
		line, err := bufferedIn.ReadString('\n')
		if err == io.EOF {
			return
		} else if err != nil {
			throw(err)
		}
		out <- String(line[:len(line)-1])
	}
}

func ratFn(ec *EvalCtx, arg Value) {
	out := ec.ports[1].Chan
	r, err := ToRat(arg)
	if err != nil {
		throw(err)
	}
	out <- r
}

// unpack takes any number of tables and output their list elements.
func unpack(ec *EvalCtx) {
	in := ec.ports[0].Chan
	out := ec.ports[1].Chan

	for v := range in {
		if list, ok := v.(List); !ok {
			throw(ErrInput)
		} else {
			for _, e := range *list.inner {
				out <- e
			}
		}
	}
}

// fromJSON parses a stream of JSON data into Value's.
func fromJSON(ec *EvalCtx) {
	in := ec.ports[0].File
	out := ec.ports[1].Chan

	dec := json.NewDecoder(in)
	var v interface{}
	for {
		err := dec.Decode(&v)
		if err != nil {
			if err == io.EOF {
				return
			}
			throw(err)
		}
		out <- FromJSONInterface(v)
	}
}

// each takes a single closure and applies it to all input values.
func each(ec *EvalCtx, f *Closure) {
	in := ec.ports[0].Chan
in:
	for v := range in {
		newec := ec.fork("closure of each")
		ex := newec.PCall(f, []Value{v})
		ClosePorts(newec.ports)

		switch ex {
		case nil, Continue:
			// nop
		case Break:
			break in
		default:
			throw(ex)
		}
	}
}

func cd(ec *EvalCtx, args []Value) {
	var dir string
	if len(args) == 0 {
		dir = mustGetHome("")
	} else if len(args) == 1 {
		dir = ToString(args[0])
	} else {
		throw(ErrArgs)
	}

	cdInner(dir, ec)
}

func cdInner(dir string, ec *EvalCtx) {
	err := os.Chdir(dir)
	if err != nil {
		throw(err)
	}
	if ec.store != nil {
		pwd, err := os.Getwd()
		// BUG(xiaq): Possible error of os.Getwd after cd-ing is ignored.
		if err == nil {
			ec.store.AddDir(pwd)
		}
	}
}

func dirs(ec *EvalCtx) {
	if ec.store == nil {
		throw(ErrStoreNotConnected)
	}
	dirs, err := ec.store.ListDirs()
	if err != nil {
		throw(errors.New("store error: " + err.Error()))
	}
	out := ec.ports[1].Chan
	for _, dir := range dirs {
		out <- Map{&map[Value]Value{
			String("path"):  String(dir.Path),
			String("score"): String(fmt.Sprint(dir.Score)),
		}}
	}
}

func jump(ec *EvalCtx, arg string) {
	if ec.store == nil {
		throw(ErrStoreNotConnected)
	}
	dirs, err := ec.store.FindDirs(arg)
	if err != nil {
		throw(errors.New("store error: " + err.Error()))
	}
	if len(dirs) == 0 {
		throw(ErrNoMatchingDir)
	}
	dir := dirs[0].Path
	err = os.Chdir(dir)
	// TODO(xiaq): Remove directories that no longer exist
	if err != nil {
		throw(err)
	}
	ec.store.AddDir(dir)
}

func source(ec *EvalCtx, fname string) {
	ec.Source(fname)
}

func toFloat(arg Value) (float64, error) {
	arg, ok := arg.(String)
	if !ok {
		return 0, fmt.Errorf("must be string")
	}
	num, err := strconv.ParseFloat(string(arg.(String)), 64)
	if err != nil {
		return 0, err
	}
	return num, nil
}

func toInt(arg Value) (int, error) {
	arg, ok := arg.(String)
	if !ok {
		return 0, fmt.Errorf("must be string")
	}
	num, err := strconv.Atoi(string(arg.(String)))
	if err != nil {
		return 0, err
	}
	return num, nil
}

func plus(ec *EvalCtx, nums ...float64) {
	out := ec.ports[1].Chan
	sum := 0.0
	for _, f := range nums {
		sum += f
	}
	out <- String(fmt.Sprintf("%g", sum))
}

func minus(ec *EvalCtx, sum float64, nums ...float64) {
	out := ec.ports[1].Chan
	for _, f := range nums {
		sum -= f
	}
	out <- String(fmt.Sprintf("%g", sum))
}

func times(ec *EvalCtx, nums ...float64) {
	out := ec.ports[1].Chan
	prod := 1.0
	for _, f := range nums {
		prod *= f
	}
	out <- String(fmt.Sprintf("%g", prod))
}

func divide(ec *EvalCtx, prod float64, nums ...float64) {
	out := ec.ports[1].Chan
	for _, f := range nums {
		prod /= f
	}
	out <- String(fmt.Sprintf("%g", prod))
}

func eq(ec *EvalCtx, args []Value) {
	out := ec.ports[1].Chan
	if len(args) == 0 {
		throw(ErrArgs)
	}
	for i := 0; i+1 < len(args); i++ {
		if args[i] != args[i+1] {
			out <- Bool(false)
			return
		}
	}
	out <- Bool(true)
}

func deepeq(ec *EvalCtx, args []Value) {
	out := ec.ports[1].Chan
	if len(args) == 0 {
		throw(ErrArgs)
	}
	for i := 0; i+1 < len(args); i++ {
		if !DeepEq(args[i], args[i+1]) {
			out <- Bool(false)
			return
		}
	}
	out <- Bool(true)
}

func take(ec *EvalCtx, n int) {
	in := ec.ports[0].Chan
	out := ec.ports[1].Chan

	i := 0
	for v := range in {
		if i >= n {
			break
		}
		i++
		out <- v
	}
}

func drop(ec *EvalCtx, n int) {
	in := ec.ports[0].Chan
	out := ec.ports[1].Chan

	for i := 0; i < n; i++ {
		<-in
	}
	for v := range in {
		out <- v
	}
}

func _sleep(ec *EvalCtx, t float64) {
	time.Sleep(time.Duration(t) * time.Second)
}

func _stack(ec *EvalCtx) {
	out := ec.ports[1].File

	// XXX dup with main.go
	buf := make([]byte, 1024)
	for runtime.Stack(buf, true) == cap(buf) {
		buf = make([]byte, cap(buf)*2)
	}
	out.Write(buf)
}

func _log(ec *EvalCtx, fname string) {
	maybeThrow(logutil.SetOutputFile(fname))
}

func _exec(ec *EvalCtx, args ...string) {
	if len(args) == 0 {
		args = []string{"elvish"}
	}
	var err error
	args[0], err = ec.Search(args[0])
	maybeThrow(err)
	err = syscall.Exec(args[0], args, os.Environ())
	maybeThrow(err)
}
<|endoftext|>


<commit_before>package GoSDK

import (
	"os"
	"os/exec"
)

const (
	_EDGES_PREAMBLE        = "/admin/edges/"
	_EDGES_USER_PREAMBLE   = "/api/v/2/edges/"
	_EDGES_SYNC_MANAGEMENT = "/admin/edges/sync/"
)

type EdgeConfig struct {
	EdgeName     string
	EdgeToken    string
	NoviIp       string
	ParentSystem string
	HttpPort     string
	MqttPort     string
	MqttTlsPort  string
	WsPort       string
	WssPort      string
	AuthPort     string
	AuthWsPort   string
	Lean         bool
	Cache        bool
	Stdout       *os.File
	Stderr       *os.File
}

func CreateNewEdge(e EdgeConfig) (*os.Process, error) {
	_, err := exec.LookPath("edge")
	if err != nil {
		println("edge not found in $PATH")
		return nil, err
	}
	cmd := parseEdgeConfig(e)
	return cmd.Process, cmd.Start()
}

func (u *UserClient) GetEdges(systemKey string) ([]interface{}, error) {
	creds, err := u.credentials()
	if err != nil {
		return nil, err
	}
	resp, err := get(u, _EDGES_USER_PREAMBLE+systemKey, nil, creds, nil)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.([]interface{}), nil
}

func (d *DevClient) GetEdges(systemKey string) ([]interface{}, error) {
	creds, err := d.credentials()
	if err != nil {
		return nil, err
	}
	resp, err := get(d, _EDGES_PREAMBLE+systemKey, nil, creds, nil)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.([]interface{}), nil
}

func (d *DevClient) GetEdge(systemKey, name string) (map[string]interface{}, error) {
	creds, err := d.credentials()
	if err != nil {
		return nil, err
	}
	resp, err := get(d, _EDGES_PREAMBLE+systemKey+"/"+name, nil, creds, nil)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.(map[string]interface{}), nil
}

func (d *DevClient) CreateEdge(systemKey, name string,
	data map[string]interface{}) (map[string]interface{}, error) {
	creds, err := d.credentials()
	if err != nil {
		return nil, err
	}
	resp, err := post(d, _EDGES_PREAMBLE+systemKey+"/"+name, data, creds, nil)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.(map[string]interface{}), nil
}

func (d *DevClient) DeleteEdge(systemKey, name string) error {
	creds, err := d.credentials()
	if err != nil {
		return err
	}
	resp, err := delete(d, _EDGES_PREAMBLE+systemKey+"/"+name, nil, creds, nil)
	_, err = mapResponse(resp, err)
	return err
}

func (d *DevClient) UpdateEdge(systemKey, name string, data map[string]interface{}) (map[string]interface{}, error) {
	creds, err := d.credentials()
	if err != nil {
		return nil, err
	}
	resp, err := put(d, _EDGES_PREAMBLE+systemKey+"/"+name, data, creds, nil)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.(map[string]interface{}), nil
}

const (
	ServiceSync = "service"
	LibrarySync = "library"
	TriggerSync = "trigger"
)

func (d *DevClient) GetSyncResourcesForEdge(systemKey string) (map[string]interface{}, error) {
	creds, err := d.credentials()
	if err != nil {
		return nil, err
	}
	resp, err := get(d, _EDGES_SYNC_MANAGEMENT+systemKey, nil, creds, nil)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.(map[string]interface{}), nil
}

func (d *DevClient) SyncResourceToEdge(systemKey, edgeName string, add map[string][]string, remove map[string][]string) (map[string]interface{}, error) {
	creds, err := d.credentials()
	if err != nil {
		return nil, err
	}
	if add == nil {
		add = map[string][]string{}
	}
	if remove == nil {
		remove = map[string][]string{}
	}
	changes := map[string][]map[string]interface{}{
		"add":    mapSyncChanges(add),
		"remove": mapSyncChanges(remove),
	}
	resp, err := put(d, _EDGES_SYNC_MANAGEMENT+systemKey+"/"+edgeName, changes, creds, nil)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.(map[string]interface{}), nil
}
<commit_msg>add custom edge column calls<commit_after>package GoSDK

import (
	"os"
	"os/exec"
)

const (
	_EDGES_PREAMBLE        = "/admin/edges/"
	_EDGES_USER_PREAMBLE   = "/api/v/2/edges/"
	_EDGES_SYNC_MANAGEMENT = "/admin/edges/sync/"
)

type EdgeConfig struct {
	EdgeName     string
	EdgeToken    string
	NoviIp       string
	ParentSystem string
	HttpPort     string
	MqttPort     string
	MqttTlsPort  string
	WsPort       string
	WssPort      string
	AuthPort     string
	AuthWsPort   string
	Lean         bool
	Cache        bool
	Stdout       *os.File
	Stderr       *os.File
}

func CreateNewEdge(e EdgeConfig) (*os.Process, error) {
	_, err := exec.LookPath("edge")
	if err != nil {
		println("edge not found in $PATH")
		return nil, err
	}
	cmd := parseEdgeConfig(e)
	return cmd.Process, cmd.Start()
}

func (u *UserClient) GetEdges(systemKey string) ([]interface{}, error) {
	creds, err := u.credentials()
	if err != nil {
		return nil, err
	}
	resp, err := get(u, _EDGES_USER_PREAMBLE+systemKey, nil, creds, nil)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.([]interface{}), nil
}

func (d *DevClient) GetEdges(systemKey string) ([]interface{}, error) {
	creds, err := d.credentials()
	if err != nil {
		return nil, err
	}
	resp, err := get(d, _EDGES_PREAMBLE+systemKey, nil, creds, nil)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.([]interface{}), nil
}

func (d *DevClient) GetEdge(systemKey, name string) (map[string]interface{}, error) {
	creds, err := d.credentials()
	if err != nil {
		return nil, err
	}
	resp, err := get(d, _EDGES_PREAMBLE+systemKey+"/"+name, nil, creds, nil)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.(map[string]interface{}), nil
}

func (d *DevClient) CreateEdge(systemKey, name string,
	data map[string]interface{}) (map[string]interface{}, error) {
	creds, err := d.credentials()
	if err != nil {
		return nil, err
	}
	resp, err := post(d, _EDGES_PREAMBLE+systemKey+"/"+name, data, creds, nil)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.(map[string]interface{}), nil
}

func (d *DevClient) DeleteEdge(systemKey, name string) error {
	creds, err := d.credentials()
	if err != nil {
		return err
	}
	resp, err := delete(d, _EDGES_PREAMBLE+systemKey+"/"+name, nil, creds, nil)
	_, err = mapResponse(resp, err)
	return err
}

func (d *DevClient) UpdateEdge(systemKey, name string, data map[string]interface{}) (map[string]interface{}, error) {
	creds, err := d.credentials()
	if err != nil {
		return nil, err
	}
	resp, err := put(d, _EDGES_PREAMBLE+systemKey+"/"+name, data, creds, nil)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.(map[string]interface{}), nil
}

const (
	ServiceSync = "service"
	LibrarySync = "library"
	TriggerSync = "trigger"
)

func (d *DevClient) GetSyncResourcesForEdge(systemKey string) (map[string]interface{}, error) {
	creds, err := d.credentials()
	if err != nil {
		return nil, err
	}
	resp, err := get(d, _EDGES_SYNC_MANAGEMENT+systemKey, nil, creds, nil)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.(map[string]interface{}), nil
}

func (d *DevClient) SyncResourceToEdge(systemKey, edgeName string, add map[string][]string, remove map[string][]string) (map[string]interface{}, error) {
	creds, err := d.credentials()
	if err != nil {
		return nil, err
	}
	if add == nil {
		add = map[string][]string{}
	}
	if remove == nil {
		remove = map[string][]string{}
	}
	changes := map[string][]map[string]interface{}{
		"add":    mapSyncChanges(add),
		"remove": mapSyncChanges(remove),
	}
	resp, err := put(d, _EDGES_SYNC_MANAGEMENT+systemKey+"/"+edgeName, changes, creds, nil)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.(map[string]interface{}), nil
}

func (d *DevClient) CreateEdgeColumn(systemKey, colName, colType string) error {
	creds, err := d.credentials()
	if err != nil {
		return err
	}
	data := map[string]interface{}{
		"column_name": colName,
		"type":        colType,
	}
	resp, err := post(d, _EDGES_PREAMBLE+systemKey+"/columns", data, creds, nil)
	_, err = mapResponse(resp, err)
	return err
}

func (d *DevClient) DeleteEdgeColumn(systemKey, colName string) error {
	creds, err := d.credentials()
	if err != nil {
		return err
	}
	resp, err := delete(d, _EDGES_PREAMBLE+systemKey+"/columns", map[string]string{"column": colName}, creds, nil)
	_, err = mapResponse(resp, err)
	return err
}

func (d *DevClient) GetEdgeColumns(systemKey string) ([]interface{}, error) {
	creds, err := d.credentials()
	if err != nil {
		return nil, err
	}
	resp, err := get(d, _EDGES_PREAMBLE+systemKey+"/columns", nil, creds, nil)
	resp, err = mapResponse(resp, err)
	if err != nil {
		return nil, err
	}
	return resp.Body.([]interface{}), nil
}
<|endoftext|>


<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 reserved

import (
	"v.io/v23"
	"v.io/v23/context"
	"v.io/v23/rpc"
	"v.io/v23/vdlroot/signature"
)

// Signature invokes the reserved signature RPC on the given name, and returns
// the results.  The client will be used to invoke the RPC - if it is nil, the
// default client from the runtime is used.
func Signature(ctx *context.T, name string, opts ...rpc.CallOpt) ([]signature.Interface, error) {
	call, err := v23.GetClient(ctx).StartCall(ctx, name, rpc.ReservedSignature, nil, opts...)
	if err != nil {
		return nil, err
	}
	var sig []signature.Interface
	if err := call.Finish(&sig); err != nil {
		return nil, err
	}
	return sig, nil
}

// MethodSignature invokes the reserved method signature RPC on the given name,
// and returns the results.  The client will be used to invoke the RPC - if it
// is nil, the default client from the runtime is used.
func MethodSignature(ctx *context.T, name, method string, opts ...rpc.CallOpt) (signature.Method, error) {
	args := []interface{}{method}
	call, err := v23.GetClient(ctx).StartCall(ctx, name, rpc.ReservedMethodSignature, args, opts...)
	if err != nil {
		return signature.Method{}, err
	}
	var sig signature.Method
	if err := call.Finish(&sig); err != nil {
		return signature.Method{}, err
	}
	return sig, nil
}
<commit_msg>TBR v.io/v23 Adding some doc for rpc/reserved<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 reserved defines interfaces for interacting with reserved RPC methods
// such as Signature, MethodSignature and Glob.
package reserved

import (
	"v.io/v23"
	"v.io/v23/context"
	"v.io/v23/rpc"
	"v.io/v23/vdlroot/signature"
)

// Signature invokes the reserved signature RPC on the given name, and returns
// the results.  The client will be used to invoke the RPC - if it is nil, the
// default client from the runtime is used.
func Signature(ctx *context.T, name string, opts ...rpc.CallOpt) ([]signature.Interface, error) {
	call, err := v23.GetClient(ctx).StartCall(ctx, name, rpc.ReservedSignature, nil, opts...)
	if err != nil {
		return nil, err
	}
	var sig []signature.Interface
	if err := call.Finish(&sig); err != nil {
		return nil, err
	}
	return sig, nil
}

// MethodSignature invokes the reserved method signature RPC on the given name,
// and returns the results.  The client will be used to invoke the RPC - if it
// is nil, the default client from the runtime is used.
func MethodSignature(ctx *context.T, name, method string, opts ...rpc.CallOpt) (signature.Method, error) {
	args := []interface{}{method}
	call, err := v23.GetClient(ctx).StartCall(ctx, name, rpc.ReservedMethodSignature, args, opts...)
	if err != nil {
		return signature.Method{}, err
	}
	var sig signature.Method
	if err := call.Finish(&sig); err != nil {
		return signature.Method{}, err
	}
	return sig, nil
}
<|endoftext|>


<commit_before>package rpcserver

import (
	"context"
	"fmt"
	"github.com/getlantern/bytemap"
	"github.com/getlantern/errors"
	"github.com/getlantern/golog"
	"github.com/getlantern/wal"
	"github.com/getlantern/zenodb"
	"github.com/getlantern/zenodb/common"
	"github.com/getlantern/zenodb/core"
	"github.com/getlantern/zenodb/encoding"
	"github.com/getlantern/zenodb/planner"
	"github.com/getlantern/zenodb/rpc"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
	"net"
	"time"
)

var (
	log = golog.LoggerFor("zenodb.rpc")
)

type Opts struct {
	// Password, if specified, is the password that clients must present in order
	// to access the server.
	Password string
}

// DB is an interface for database-like things (implemented by common.DB).
type DB interface {
	InsertRaw(stream string, ts time.Time, dims bytemap.ByteMap, vals bytemap.ByteMap) error

	Query(sqlString string, isSubQuery bool, subQueryResults [][]interface{}, includeMemStore bool) (core.FlatRowSource, error)

	Follow(f *common.Follow, cb func([]byte, wal.Offset) error)

	RegisterQueryHandler(partition int, query planner.QueryClusterFN)
}

func Serve(db DB, l net.Listener, opts *Opts) error {
	l = &rpc.SnappyListener{l}
	gs := grpc.NewServer(grpc.CustomCodec(rpc.Codec))
	gs.RegisterService(&rpc.ServiceDesc, &server{db, opts.Password})
	return gs.Serve(l)
}

type server struct {
	db       DB
	password string
}

func (s *server) Insert(stream grpc.ServerStream) error {
	// No need to authorize, anyone can insert

	now := time.Now()
	streamName := ""

	report := &rpc.InsertReport{
		Errors: make(map[int]string),
	}

	i := -1
	for {
		i++
		insert := &rpc.Insert{}
		err := stream.RecvMsg(insert)
		if err != nil {
			return fmt.Errorf("Error reading insert: %v", err)
		}
		if insert.EndOfInserts {
			// We're done inserting
			return stream.SendMsg(report)
		}
		report.Received++

		if streamName == "" {
			streamName = insert.Stream
			if streamName == "" {
				return fmt.Errorf("Please specify a stream")
			}
		}

		if len(insert.Dims) == 0 {
			report.Errors[i] = fmt.Sprintf("Need at least one dim")
			continue
		}
		if len(insert.Vals) == 0 {
			report.Errors[i] = fmt.Sprintf("Need at least one val")
			continue
		}
		var ts time.Time
		if insert.TS == 0 {
			ts = now
		} else {
			ts = encoding.TimeFromInt(insert.TS)
		}

		// TODO: make sure we don't barf on invalid bytemaps here
		insertErr := s.db.InsertRaw(streamName, ts, bytemap.ByteMap(insert.Dims), bytemap.ByteMap(insert.Vals))
		if insertErr != nil {
			report.Errors[i] = fmt.Sprintf("Unable to insert: %v", insertErr)
			continue
		}
		report.Succeeded++
	}
}

func (s *server) Query(q *rpc.Query, stream grpc.ServerStream) error {
	authorizeErr := s.authorize(stream)
	if authorizeErr != nil {
		return authorizeErr
	}

	source, err := s.db.Query(q.SQLString, q.IsSubQuery, q.SubQueryResults, q.IncludeMemStore)
	if err != nil {
		return err
	}

	rr := &rpc.RemoteQueryResult{}
	err = source.Iterate(stream.Context(), func(fields core.Fields) error {
		// Send query metadata
		md := zenodb.MetaDataFor(source, fields)
		return stream.SendMsg(md)
	}, func(row *core.FlatRow) (bool, error) {
		rr.Row = row
		return true, stream.SendMsg(rr)
	})
	if err != nil {
		return err
	}

	// Send end of results
	rr.Row = nil
	rr.EndOfResults = true
	return stream.SendMsg(rr)
}

func (s *server) Follow(f *common.Follow, stream grpc.ServerStream) error {
	authorizeErr := s.authorize(stream)
	if authorizeErr != nil {
		return authorizeErr
	}

	log.Debugf("Follower %d joined", f.PartitionNumber)
	defer log.Debugf("Follower %d left", f.PartitionNumber)
	s.db.Follow(f, func(data []byte, newOffset wal.Offset) error {
		return stream.SendMsg(&rpc.Point{data, newOffset})
	})
	return nil
}

func (s *server) HandleRemoteQueries(r *rpc.RegisterQueryHandler, stream grpc.ServerStream) error {
	initialResultCh := make(chan *rpc.RemoteQueryResult)
	initialErrCh := make(chan error)
	finalErrCh := make(chan error)

	finish := func(err error) {
		select {
		case finalErrCh <- err:
			// ok
		default:
			// ignore
		}
	}

	s.db.RegisterQueryHandler(r.Partition, func(ctx context.Context, sqlString string, isSubQuery bool, subQueryResults [][]interface{}, unflat bool, onFields core.OnFields, onRow core.OnRow, onFlatRow core.OnFlatRow) error {
		q := &rpc.Query{
			SQLString:       sqlString,
			IsSubQuery:      isSubQuery,
			SubQueryResults: subQueryResults,
			Unflat:          unflat,
			IncludeMemStore: common.ShouldIncludeMemStore(ctx),
		}
		q.Deadline, q.HasDeadline = ctx.Deadline()
		sendErr := stream.SendMsg(q)

		m, recvErr := <-initialResultCh, <-initialErrCh

		// Check send error after reading initial result to avoid blocking
		// unnecessarily
		if sendErr != nil {
			err := errors.New("Unable to send query: %v", sendErr)
			finish(err)
			return err
		}

		var finalErr error

		first := true
	receiveLoop:
		for {
			// Process current result
			if recvErr != nil {
				m.Error = recvErr.Error()
				finalErr = errors.New("Unable to receive result: %v", recvErr)
				break
			}

			if first {
				// First message contains only fields information
				onFields(m.Fields)
				first = false
			} else {
				// Subsequent messages contain data
				if m.EndOfResults {
					break
				}
				var more bool
				var err error
				if unflat {
					more, err = onRow(m.Key, m.Vals)
				} else {
					more, err = onFlatRow(m.Row)
				}
				if !more || err != nil {
					finalErr = err
					break receiveLoop
				}
			}

			// Read next result
			m = &rpc.RemoteQueryResult{}
			recvErr = stream.RecvMsg(m)
		}

		finish(finalErr)
		return finalErr
	})

	// Block on reading initial result to keep connection open
	m := &rpc.RemoteQueryResult{}
	err := stream.RecvMsg(m)
	initialResultCh <- m
	initialErrCh <- err

	if err == nil {
		// Wait for final error so we don't close the connection prematurely
		return <-finalErrCh
	}
	return err
}

func (s *server) authorize(stream grpc.ServerStream) error {
	if s.password == "" {
		log.Debug("No password specified, allowing access to world")
		return nil
	}
	md, ok := metadata.FromContext(stream.Context())
	if !ok {
		return log.Error("No metadata provided, unable to authenticate")
	}
	passwords := md[rpc.PasswordKey]
	for _, password := range passwords {
		if password == s.password {
			// authorized
			return nil
		}
	}
	return log.Error("None of the provided passwords matched, not authorized!")
}
<commit_msg>Fixed grpc compile issue<commit_after>package rpcserver

import (
	"context"
	"fmt"
	"github.com/getlantern/bytemap"
	"github.com/getlantern/errors"
	"github.com/getlantern/golog"
	"github.com/getlantern/wal"
	"github.com/getlantern/zenodb"
	"github.com/getlantern/zenodb/common"
	"github.com/getlantern/zenodb/core"
	"github.com/getlantern/zenodb/encoding"
	"github.com/getlantern/zenodb/planner"
	"github.com/getlantern/zenodb/rpc"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
	"net"
	"time"
)

var (
	log = golog.LoggerFor("zenodb.rpc")
)

type Opts struct {
	// Password, if specified, is the password that clients must present in order
	// to access the server.
	Password string
}

// DB is an interface for database-like things (implemented by common.DB).
type DB interface {
	InsertRaw(stream string, ts time.Time, dims bytemap.ByteMap, vals bytemap.ByteMap) error

	Query(sqlString string, isSubQuery bool, subQueryResults [][]interface{}, includeMemStore bool) (core.FlatRowSource, error)

	Follow(f *common.Follow, cb func([]byte, wal.Offset) error)

	RegisterQueryHandler(partition int, query planner.QueryClusterFN)
}

func Serve(db DB, l net.Listener, opts *Opts) error {
	l = &rpc.SnappyListener{l}
	gs := grpc.NewServer(grpc.CustomCodec(rpc.Codec))
	gs.RegisterService(&rpc.ServiceDesc, &server{db, opts.Password})
	return gs.Serve(l)
}

type server struct {
	db       DB
	password string
}

func (s *server) Insert(stream grpc.ServerStream) error {
	// No need to authorize, anyone can insert

	now := time.Now()
	streamName := ""

	report := &rpc.InsertReport{
		Errors: make(map[int]string),
	}

	i := -1
	for {
		i++
		insert := &rpc.Insert{}
		err := stream.RecvMsg(insert)
		if err != nil {
			return fmt.Errorf("Error reading insert: %v", err)
		}
		if insert.EndOfInserts {
			// We're done inserting
			return stream.SendMsg(report)
		}
		report.Received++

		if streamName == "" {
			streamName = insert.Stream
			if streamName == "" {
				return fmt.Errorf("Please specify a stream")
			}
		}

		if len(insert.Dims) == 0 {
			report.Errors[i] = fmt.Sprintf("Need at least one dim")
			continue
		}
		if len(insert.Vals) == 0 {
			report.Errors[i] = fmt.Sprintf("Need at least one val")
			continue
		}
		var ts time.Time
		if insert.TS == 0 {
			ts = now
		} else {
			ts = encoding.TimeFromInt(insert.TS)
		}

		// TODO: make sure we don't barf on invalid bytemaps here
		insertErr := s.db.InsertRaw(streamName, ts, bytemap.ByteMap(insert.Dims), bytemap.ByteMap(insert.Vals))
		if insertErr != nil {
			report.Errors[i] = fmt.Sprintf("Unable to insert: %v", insertErr)
			continue
		}
		report.Succeeded++
	}
}

func (s *server) Query(q *rpc.Query, stream grpc.ServerStream) error {
	authorizeErr := s.authorize(stream)
	if authorizeErr != nil {
		return authorizeErr
	}

	source, err := s.db.Query(q.SQLString, q.IsSubQuery, q.SubQueryResults, q.IncludeMemStore)
	if err != nil {
		return err
	}

	rr := &rpc.RemoteQueryResult{}
	err = source.Iterate(stream.Context(), func(fields core.Fields) error {
		// Send query metadata
		md := zenodb.MetaDataFor(source, fields)
		return stream.SendMsg(md)
	}, func(row *core.FlatRow) (bool, error) {
		rr.Row = row
		return true, stream.SendMsg(rr)
	})
	if err != nil {
		return err
	}

	// Send end of results
	rr.Row = nil
	rr.EndOfResults = true
	return stream.SendMsg(rr)
}

func (s *server) Follow(f *common.Follow, stream grpc.ServerStream) error {
	authorizeErr := s.authorize(stream)
	if authorizeErr != nil {
		return authorizeErr
	}

	log.Debugf("Follower %d joined", f.PartitionNumber)
	defer log.Debugf("Follower %d left", f.PartitionNumber)
	s.db.Follow(f, func(data []byte, newOffset wal.Offset) error {
		return stream.SendMsg(&rpc.Point{data, newOffset})
	})
	return nil
}

func (s *server) HandleRemoteQueries(r *rpc.RegisterQueryHandler, stream grpc.ServerStream) error {
	initialResultCh := make(chan *rpc.RemoteQueryResult)
	initialErrCh := make(chan error)
	finalErrCh := make(chan error)

	finish := func(err error) {
		select {
		case finalErrCh <- err:
			// ok
		default:
			// ignore
		}
	}

	s.db.RegisterQueryHandler(r.Partition, func(ctx context.Context, sqlString string, isSubQuery bool, subQueryResults [][]interface{}, unflat bool, onFields core.OnFields, onRow core.OnRow, onFlatRow core.OnFlatRow) error {
		q := &rpc.Query{
			SQLString:       sqlString,
			IsSubQuery:      isSubQuery,
			SubQueryResults: subQueryResults,
			Unflat:          unflat,
			IncludeMemStore: common.ShouldIncludeMemStore(ctx),
		}
		q.Deadline, q.HasDeadline = ctx.Deadline()
		sendErr := stream.SendMsg(q)

		m, recvErr := <-initialResultCh, <-initialErrCh

		// Check send error after reading initial result to avoid blocking
		// unnecessarily
		if sendErr != nil {
			err := errors.New("Unable to send query: %v", sendErr)
			finish(err)
			return err
		}

		var finalErr error

		first := true
	receiveLoop:
		for {
			// Process current result
			if recvErr != nil {
				m.Error = recvErr.Error()
				finalErr = errors.New("Unable to receive result: %v", recvErr)
				break
			}

			if first {
				// First message contains only fields information
				onFields(m.Fields)
				first = false
			} else {
				// Subsequent messages contain data
				if m.EndOfResults {
					break
				}
				var more bool
				var err error
				if unflat {
					more, err = onRow(m.Key, m.Vals)
				} else {
					more, err = onFlatRow(m.Row)
				}
				if !more || err != nil {
					finalErr = err
					break receiveLoop
				}
			}

			// Read next result
			m = &rpc.RemoteQueryResult{}
			recvErr = stream.RecvMsg(m)
		}

		finish(finalErr)
		return finalErr
	})

	// Block on reading initial result to keep connection open
	m := &rpc.RemoteQueryResult{}
	err := stream.RecvMsg(m)
	initialResultCh <- m
	initialErrCh <- err

	if err == nil {
		// Wait for final error so we don't close the connection prematurely
		return <-finalErrCh
	}
	return err
}

func (s *server) authorize(stream grpc.ServerStream) error {
	if s.password == "" {
		log.Debug("No password specified, allowing access to world")
		return nil
	}
	md, ok := metadata.FromIncomingContext(stream.Context())
	if !ok {
		return log.Error("No metadata provided, unable to authenticate")
	}
	passwords := md[rpc.PasswordKey]
	for _, password := range passwords {
		if password == s.password {
			// authorized
			return nil
		}
	}
	return log.Error("None of the provided passwords matched, not authorized!")
}
<|endoftext|>


<commit_before>package caching

import (
	"errors"
	"sync"
	"time"

	"github.com/taskcluster/taskcluster-worker/runtime"
	"github.com/taskcluster/taskcluster-worker/runtime/gc"
)

// A Constructor is function that given options creates a resource.
//
// Notice, options must be JSON serializable, as they will be hashed to
// determine resource equivalence.
type Constructor func(ctx Context, options interface{}) (Resource, error)

// A Cache is an object that wraps a constructor and manages the life-cycle of
// objects. At creation a cache is given a Constructor, a ResourceTracker and
// the option of being an exclusive or shared-cache, meaning if resources may be
// re-used before they are released.
type Cache struct {
	m           sync.Mutex
	shared      bool
	entries     []*cacheEntry
	constructor Constructor
	tracker     gc.ResourceTracker
	cachehit    runtime.Monitor
}

// New returns a Cache wrapping constructor such that resources
// returned from Require are shared between all calls to Require with the same
// options, if shared is set to true. Otherwise, resources are exclusive.
func New(constructor Constructor, shared bool, tracker gc.ResourceTracker, monitor runtime.Monitor) *Cache {
	return &Cache{
		constructor: constructor,
		tracker:     tracker,
		shared:      shared,
		cachehit:    monitor.WithPrefix("cache-hit"),
	}
}

func (c *Cache) remove(e *cacheEntry) {
	// Lock cache entries
	c.m.Lock()
	defer c.m.Unlock()

	// Remove from cache
	entries := c.entries[:0]
	for _, entry := range c.entries {
		if entry != e {
			entries = append(entries, entry)
		}
	}
	c.entries = entries
}

// Require returns a resource for the given options
//
// If the Cache is exclusive the resource will not be re-used before it is
// returned with a call to Release(resource). If the cache is shared the cache
// will only every create one instance of the resource, unless purged or freed
// by the ResourceTracker.
func (c *Cache) Require(ctx Context, options interface{}) (*Handle, error) {
	optionsHash := hashJSON(options)

	// Lock the entries list
	c.m.Lock()

	// find cache entry, if present
	var entry *cacheEntry
	for _, e := range c.entries {
		e.m.Lock()
		// Ignore if: scheduled to be purged, disposed or options mismatch
		if e.purge || e.disposed || e.optionsHash != optionsHash {
			e.m.Unlock()
			continue
		}
		// Skip if this isn't a shared cache and entry is in-use
		if !c.shared && e.refCount > 0 {
			e.m.Unlock()
			continue
		}
		// Skip if we are too late to join the context (ie. it was canceled), and
		// the resource haven't been created. There is a race here that could
		// cause us to ignore a successfully created resource, but it's unlikely,
		// mostly this is ignoring resource creations that haven't finished, but
		// have been canceled (since we can't uncancel)
		if !e.ctx.AddContext(ctx) && !e.created.IsDone() {
			e.m.Unlock()
			continue
		}

		// Take the entry
		debug("cache entry '%s' found in cache, refCount: %d", optionsHash, e.refCount+1)
		c.monitor.prefix["cache-hit"].Count("counter-1", 1)
		entry = e
		e.refCount++
		e.m.Unlock()
		break
	}

	// Create new resource
	if entry == nil {
		debug("cache entry '%s' is being created", optionsHash)
		entry = &cacheEntry{
			optionsHash: optionsHash,
			refCount:    1,
			lastUsed:    time.Now(),
			ctx:         newContextConjunction(ctx),
			cache:       c,
		}

		// Insert in cache so we can find it again, and re-use the resource
		c.entries = append(c.entries, entry)

		go entry.created.Do(func() {
			defer entry.ctx.dispose() // ensure resources are cleanup when constructor is done

			entry.resource, entry.err = c.constructor(entry.ctx, options)
			if entry.err != nil {
				// Set the entry to be purged, so others will ignore it
				entry.m.Lock()
				entry.purge = true
				entry.disposed = true
				entry.m.Unlock()
				// Remove it from entries, so we don't cache error results
				c.remove(entry)
			} else {
				debug("cache entry '%s' ready with resource type: %T", entry.optionsHash, entry.resource)
				// Insert in garbage collector
				c.tracker.Register(entry)
			}
		})
	}

	// Unlock the entries list, while we wait for the entry to be created
	c.m.Unlock()

	// Wait for the entry to be created
	select {
	case <-entry.created.Done():
		if entry.err != nil {
			entry.release()
			return nil, entry.err
		}
		return &Handle{entry: entry}, nil
	case <-ctx.Done():
		entry.release()
		if err := ctx.Err(); err != nil {
			return nil, err
		}
		panic(errors.New("expected context.Err() != nil when context.Done() is closed"))
	}
}

// Purge cached resources, notice that these resources may be in use.
// The filter function should return true if the resource should be purged,
// then it'll purged when it is no-longer in use.
func (c *Cache) Purge(filter func(r Resource) bool) error {
	c.m.Lock()
	defer c.m.Unlock()

	// find entries to delete
	for _, entry := range c.entries {
		entry.m.Lock()
		ignore := !entry.created.IsDone() || entry.purge || entry.disposed
		entry.m.Unlock()

		if !ignore && filter(entry.resource) {
			// Set the bit for it to be disposed when given back to Release
			entry.m.Lock()
			entry.purge = true
			entry.m.Unlock()

			// Remove entry from ResourceTracker
			c.tracker.Unregister(entry)

			if err := entry.Dispose(); err != nil && err != gc.ErrDisposableInUse {
				return err
			}
		}
	}

	return nil
}

// PurgeAll will purge all resources returning the first error, then proceeding
// and purging everything else, before returning the first error, if any.
func (c *Cache) PurgeAll() error {
	c.m.Lock()
	defer c.m.Unlock()

	// find entries to delete
	var err error
	for _, entry := range c.entries {
		entry.m.Lock()
		ignore := !entry.created.IsDone() || entry.purge || entry.disposed
		entry.m.Unlock()

		if !ignore {
			// Set the bit for it to be disposed when given back to Release
			entry.m.Lock()
			entry.purge = true
			entry.m.Unlock()

			// Remove entry from ResourceTracker
			c.tracker.Unregister(entry)

			if derr := entry.Dispose(); derr != nil && derr != gc.ErrDisposableInUse && err == nil {
				err = derr // return only the first error
			}
		}
	}

	return err
}
<commit_msg>Add cache hit and constructor-creation-time metrics<commit_after>package caching

import (
	"errors"
	"sync"
	"time"

	"github.com/taskcluster/taskcluster-worker/runtime"
	"github.com/taskcluster/taskcluster-worker/runtime/gc"
)

// A Constructor is function that given options creates a resource.
//
// Notice, options must be JSON serializable, as they will be hashed to
// determine resource equivalence.
type Constructor func(ctx Context, options interface{}) (Resource, error)

// A Cache is an object that wraps a constructor and manages the life-cycle of
// objects. At creation a cache is given a Constructor, a ResourceTracker and
// the option of being an exclusive or shared-cache, meaning if resources may be
// re-used before they are released.
type Cache struct {
	m               sync.Mutex
	shared          bool
	entries         []*cacheEntry
	constructor     Constructor
	tracker         gc.ResourceTracker
	cachehit        runtime.Monitor
	constructortime runtime.Monitor
	countCachehits  float64
}

// New returns a Cache wrapping constructor such that resources
// returned from Require are shared between all calls to Require with the same
// options, if shared is set to true. Otherwise, resources are exclusive.
func New(constructor Constructor, shared bool, tracker gc.ResourceTracker, monitor runtime.Monitor) *Cache {
	return &Cache{
		constructor:     constructor,
		tracker:         tracker,
		shared:          shared,
		cachehit:        monitor.WithPrefix("cache-hit"),
		constructortime: monitor.WithPrefix("creation-time"),
		countCachehits:  0,
	}
}

func (c *Cache) remove(e *cacheEntry) {
	// Lock cache entries
	c.m.Lock()
	defer c.m.Unlock()

	// Remove from cache
	entries := c.entries[:0]
	for _, entry := range c.entries {
		if entry != e {
			entries = append(entries, entry)
		}
	}
	c.entries = entries
}

// Require returns a resource for the given options
//
// If the Cache is exclusive the resource will not be re-used before it is
// returned with a call to Release(resource). If the cache is shared the cache
// will only every create one instance of the resource, unless purged or freed
// by the ResourceTracker.
func (c *Cache) Require(ctx Context, options interface{}) (*Handle, error) {
	optionsHash := hashJSON(options)

	// Lock the entries list
	c.m.Lock()

	// find cache entry, if present
	var entry *cacheEntry
	for _, e := range c.entries {
		e.m.Lock()
		// Ignore if: scheduled to be purged, disposed or options mismatch
		if e.purge || e.disposed || e.optionsHash != optionsHash {
			e.m.Unlock()
			continue
		}
		// Skip if this isn't a shared cache and entry is in-use
		if !c.shared && e.refCount > 0 {
			e.m.Unlock()
			continue
		}
		// Skip if we are too late to join the context (ie. it was canceled), and
		// the resource haven't been created. There is a race here that could
		// cause us to ignore a successfully created resource, but it's unlikely,
		// mostly this is ignoring resource creations that haven't finished, but
		// have been canceled (since we can't uncancel)
		if !e.ctx.AddContext(ctx) && !e.created.IsDone() {
			e.m.Unlock()
			continue
		}

		// Take the entry
		debug("cache entry '%s' found in cache, refCount: %d", optionsHash, e.refCount+1)
		c.countCachehits++
		c.cachehit.Count("count cache-hit", c.countCachehits)
		entry = e
		e.refCount++
		e.m.Unlock()
		break
	}

	// Create new resource
	if entry == nil {
		debug("cache entry '%s' is being created", optionsHash)
		entry = &cacheEntry{
			optionsHash: optionsHash,
			refCount:    1,
			lastUsed:    time.Now(),
			ctx:         newContextConjunction(ctx),
			cache:       c,
		}

		// Insert in cache so we can find it again, and re-use the resource
		c.entries = append(c.entries, entry)

		go entry.created.Do(func() {
			defer entry.ctx.dispose()
			// ensure resources are cleanup when constructor is done

			c.constructortime.Time("constructor-creation-time", func() {
				entry.resource, entry.err = c.constructor(entry.ctx, options)
			})
			//metric to give creation time of constructor

			if entry.err != nil {
				// Set the entry to be purged, so others will ignore it
				entry.m.Lock()
				entry.purge = true
				entry.disposed = true
				entry.m.Unlock()
				// Remove it from entries, so we don't cache error results
				c.remove(entry)
			} else {
				debug("cache entry '%s' ready with resource type: %T", entry.optionsHash, entry.resource)
				// Insert in garbage collector
				c.tracker.Register(entry)
			}
		})
	}

	// Unlock the entries list, while we wait for the entry to be created
	c.m.Unlock()

	// Wait for the entry to be created
	select {
	case <-entry.created.Done():
		if entry.err != nil {
			entry.release()
			return nil, entry.err
		}
		return &Handle{entry: entry}, nil
	case <-ctx.Done():
		entry.release()
		if err := ctx.Err(); err != nil {
			return nil, err
		}
		panic(errors.New("expected context.Err() != nil when context.Done() is closed"))
	}
}

// Purge cached resources, notice that these resources may be in use.
// The filter function should return true if the resource should be purged,
// then it'll purged when it is no-longer in use.
func (c *Cache) Purge(filter func(r Resource) bool) error {
	c.m.Lock()
	defer c.m.Unlock()

	// find entries to delete
	for _, entry := range c.entries {
		entry.m.Lock()
		ignore := !entry.created.IsDone() || entry.purge || entry.disposed
		entry.m.Unlock()

		if !ignore && filter(entry.resource) {
			// Set the bit for it to be disposed when given back to Release
			entry.m.Lock()
			entry.purge = true
			entry.m.Unlock()

			// Remove entry from ResourceTracker
			c.tracker.Unregister(entry)

			if err := entry.Dispose(); err != nil && err != gc.ErrDisposableInUse {
				return err
			}
		}
	}

	return nil
}

// PurgeAll will purge all resources returning the first error, then proceeding
// and purging everything else, before returning the first error, if any.
func (c *Cache) PurgeAll() error {
	c.m.Lock()
	defer c.m.Unlock()

	// find entries to delete
	var err error
	for _, entry := range c.entries {
		entry.m.Lock()
		ignore := !entry.created.IsDone() || entry.purge || entry.disposed
		entry.m.Unlock()

		if !ignore {
			// Set the bit for it to be disposed when given back to Release
			entry.m.Lock()
			entry.purge = true
			entry.m.Unlock()

			// Remove entry from ResourceTracker
			c.tracker.Unregister(entry)

			if derr := entry.Dispose(); derr != nil && derr != gc.ErrDisposableInUse && err == nil {
				err = derr // return only the first error
			}
		}
	}

	return err
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"image"
	"os"
	"strings"
	"sync"

	"github.com/as/frame"
	"github.com/as/frame/tag"
	"golang.org/x/exp/shiny/driver"
	"golang.org/x/exp/shiny/screen"
	"golang.org/x/image/font/gofont/gomono"
	"golang.org/x/mobile/event/key"
	"golang.org/x/mobile/event/lifecycle"
	"golang.org/x/mobile/event/mouse"
	"golang.org/x/mobile/event/paint"
	"golang.org/x/mobile/event/size"
)

func mkfont(size int) frame.Font {
	return frame.NewTTF(gomono.TTF, size)
}

// Put
var (
	winSize = image.Pt(1900, 1000)
	pad     = image.Pt(25, 5)
	fsize   = 11
)

type Plane interface {
	Loc() image.Rectangle
}

// Put
func active(e mouse.Event, act Plane, list ...Plane) (x Plane) {
	if tag.Buttonsdown != 0 {
		return act
	}
	pt := image.Pt(int(e.X), int(e.Y))
	if act != nil {
		pt = pt.Add(act.Loc().Min)
		list = append([]Plane{act}, list...)
	}
	for i, w := range list {
		r := w.Loc()
		if pt.In(r) {
			return list[i]
		}
	}
	return act
}

type Col struct {
	sp   image.Point
	size image.Point
	wind screen.Window
	List []*tag.Tag
}

var cols = frame.Acme

func NewCol(src screen.Screen, wind screen.Window, ft frame.Font, sp, size image.Point, files ...string) *Col {
	N := len(files)
	dy := size.Y / N
	n := 0
	col := &Col{sp: sp, size: size, wind: wind, List: make([]*tag.Tag, len(files))}
	for i, v := range files {
		sp = image.Pt(sp.X, n*dy)
		dp := image.Pt(size.X, dy)
		n++
		col.List[i] = tag.NewTag(src, wind, ft, sp, dp, pad, cols)
		col.List[i].Open(v)
	}
	return col
}

func (co *Col) Upload() {
	var wg sync.WaitGroup
	wg.Add(len(co.List))
	for _, t := range co.List {
		go func() { t.Upload(co.wind); wg.Done() }()
	}
	wg.Wait()
}

func (co *Col) Resize(size image.Point) {
	N := len(co.List)
	dy := size.Y / N
	sp := image.Pt(0, dy)
	dp := image.Pt(size.X, dy)
	for _, t := range co.List {
		t.Move(sp)
		t.Resize(dp)
		sp = sp.Add(image.Pt(0, dy))
	}
}

// Put
func main() {
	driver.Main(func(src screen.Screen) {
		wind, _ := src.NewWindow(&screen.NewWindowOptions{winSize.X, winSize.Y})
		wind.Send(paint.Event{})
		focused := false
		focused = focused
		ft := mkfont(fsize)
		filename := "/dev/stdin"
		if len(os.Args) > 1 {
			filename = strings.Join(os.Args[1:], " ")
		}

		N := 2
		dy := winSize.Y / N
		n := 0

		sp := image.Pt(0, n*dy)
		dp := image.Pt(winSize.X, dy)
		n++
		wn := tag.NewTag(src, wind, ft, sp, dp, pad, cols)
		sp = sp.Add(image.Pt(0, dy))

		wn2 := tag.NewTag(src, wind, ft, sp, dp, pad, cols)
		wn.Open(filename)
		wn2.Open(`C:\windows\system32\drivers\etc\hosts`)

		// lambda to paint only rectangles changed during a sweep of the mouse
		// Put
		act := wn.W
		actTag := wn

		go func() {
			sc := bufio.NewScanner(os.Stdin)
			for sc.Scan() {
				if x := sc.Text(); x == "u" || x == "r" {
					act.SendFirst(x)
					continue
				}
				act.SendFirst(tag.Cmdparse(sc.Text()))
			}
		}()
		for {
			// Put
			switch e := act.NextEvent().(type) {
			case mouse.Event:
				actTag = active(e, actTag, wn, wn2).(*tag.Tag)
				act = active(e, act, actTag.W, actTag.Wtag).(*tag.Invertable)
				actTag.Handle(act, e)
			case string, *tag.Command, tag.ScrollEvent, key.Event:
				actTag.Handle(act, e)
			case size.Event:
				winSize = image.Pt(e.WidthPx, e.HeightPx)
				N := 2
				dy := winSize.Y / N
				n := 0
				sp := image.Pt(0, n*dy)
				dp := image.Pt(winSize.X, dy)
				n++
				wn.Move(sp)
				wn.Resize(dp)
				sp = sp.Add(image.Pt(0, dy))
				wn2.Move(sp)
				wn2.Resize(dp)
				act.SendFirst(paint.Event{})
			case paint.Event:
				wn.Upload(wind)
				wn2.Upload(wind)
				wind.Publish()
			case lifecycle.Event:
				if e.To == lifecycle.StageDead {
					return
				}
				// NT doesn't repaint the window if another window covers it
				if e.Crosses(lifecycle.StageFocused) == lifecycle.CrossOff {
					focused = false
				} else if e.Crosses(lifecycle.StageFocused) == lifecycle.CrossOn {
					focused = true
				}
			}
		}
	})

}
<commit_msg>grid example: columns<commit_after>package main

import (
	"bufio"
	"fmt"
	"image"
	"os"

	"github.com/as/frame"
	"github.com/as/frame/tag"
	"golang.org/x/exp/shiny/driver"
	"golang.org/x/exp/shiny/screen"
	"golang.org/x/image/font/gofont/gomono"
	"golang.org/x/mobile/event/key"
	"golang.org/x/mobile/event/lifecycle"
	"golang.org/x/mobile/event/mouse"
	"golang.org/x/mobile/event/paint"
	"golang.org/x/mobile/event/size"
)

func mkfont(size int) frame.Font {
	return frame.NewTTF(gomono.TTF, size)
}

// Put
var (
	winSize = image.Pt(1900, 1000)
	pad     = image.Pt(25, 5)
	fsize   = 11
)

type Plane interface {
	Loc() image.Rectangle
	Move(image.Point)
	Resize(image.Point)
}

// Put
func active(e mouse.Event, act Plane, list ...Plane) (x Plane) {
	if tag.Buttonsdown != 0 {
		return act
	}
	pt := image.Pt(int(e.X), int(e.Y))
	if act != nil {
		pt = pt.Add(act.Loc().Min)
		list = append([]Plane{act}, list...)
	}
	for i, w := range list {
		r := w.Loc()
		if pt.In(r) {
			return list[i]
		}
	}
	return act
}

type Col struct {
	sp   image.Point
	size image.Point
	wind screen.Window
	List []Plane
}

var cols = frame.Acme

func NewCol(src screen.Screen, wind screen.Window, ft frame.Font, sp, size image.Point, files ...string) *Col {
	N := len(files)
	dy := size.Y / N
	n := 0
	col := &Col{sp: sp, size: size, wind: wind, List: make([]Plane, len(files))}
	for i, v := range files {
		sp = image.Pt(sp.X, n*dy)
		dp := image.Pt(size.X, dy)
		n++
		fmt.Printf("sp=%s size=%s\n", sp, dp)
		t := tag.NewTag(src, wind, ft, sp, dp, pad, cols)
		t.Open(v)
		col.List[i] = t
	}
	return col
}

func (co *Col) Upload(wind screen.Window) {
	for _, t := range co.List {
		t.(*tag.Tag).Upload(wind)
	}
}

func (co *Col) Resize(size image.Point) {
	co.size = size
	N := len(co.List)
	dy := size.Y / N
	sp := image.Pt(co.sp.X, co.sp.Y)
	dp := image.Pt(size.X, dy)
	for _, t := range co.List {
		t.Move(sp)
		t.Resize(dp)
		sp = sp.Add(image.Pt(0, dy))
	}
}

// Put
func main() {
	driver.Main(func(src screen.Screen) {
		wind, _ := src.NewWindow(&screen.NewWindowOptions{winSize.X, winSize.Y})
		wind.Send(paint.Event{})
		focused := false
		focused = focused
		ft := mkfont(fsize)

		list := []string{"/dev/stdin"}
		if len(os.Args) > 1 {
			list = os.Args[1:]
		}
		co := NewCol(src, wind, ft, image.ZP, winSize, list...)
		act := co.List[0].(*tag.Tag).W
		actTag := co.List[0]

		go func() {
			sc := bufio.NewScanner(os.Stdin)
			for sc.Scan() {
				if x := sc.Text(); x == "u" || x == "r" {
					act.SendFirst(x)
					continue
				}
				act.SendFirst(tag.Cmdparse(sc.Text()))
			}
		}()
		for {
			// Put
			switch e := act.NextEvent().(type) {
			case mouse.Event:
				actTag = active(e, actTag, co.List...).(*tag.Tag)
				act = active(e, act, actTag.(*tag.Tag).W, actTag.(*tag.Tag).Wtag).(*tag.Invertable)
				actTag.(*tag.Tag).Handle(act, e)
			case string, *tag.Command, tag.ScrollEvent, key.Event:
				actTag.(*tag.Tag).Handle(act, e)
			case size.Event:
				winSize = image.Pt(e.WidthPx, e.HeightPx)
				co.Resize(winSize)
				act.SendFirst(paint.Event{})
			case paint.Event:
				co.Upload(wind)
				wind.Publish()
			case lifecycle.Event:
				if e.To == lifecycle.StageDead {
					return
				}
				// NT doesn't repaint the window if another window covers it
				if e.Crosses(lifecycle.StageFocused) == lifecycle.CrossOff {
					focused = false
				} else if e.Crosses(lifecycle.StageFocused) == lifecycle.CrossOn {
					focused = true
				}
			}
		}
	})

}
<|endoftext|>


<commit_before>package database

import (
	"database/sql"
	"time"

	"github.com/hackedu/backend/model"
)

const userGetByIDStmt = `SELECT id, created, updated, first_name, last_name,
email, type, github, twitter, password FROM users WHERE id = $1`

const userGetByEmailStmt = `SELECT id, created, updated, first_name, last_name,
email, type, github, twitter, password FROM users WHERE email ilike $1`

const userCreateStmt = `INSERT INTO users (created, updated, first_name,
last_name, email, type, github, twitter, password) VALUES ($1, $2, $3, $4, $5,
$6, $7, $8, $9) RETURNING id`

// GetUser gets the user from the database with the provided ID.
func GetUser(id int64) (*model.User, error) {
	u := new(model.User)
	row := db.QueryRow(userGetByIDStmt, id)
	if err := row.Scan(&u.ID, &u.Created, &u.Updated, &u.FirstName, &u.LastName,
		&u.Email, &u.Type, &u.GitHub, &u.Twitter, &u.Password); err != nil {
		return nil, err
	}
	return u, nil
}

// GetUserByEmail gets the user from the database with the provided email.
func GetUserByEmail(email string) (*model.User, error) {
	u := new(model.User)
	row := db.QueryRow(userGetByEmailStmt, email)
	if err := row.Scan(&u.ID, &u.Created, &u.Updated, &u.FirstName, &u.LastName,
		&u.Email, &u.Type, &u.GitHub, &u.Twitter, &u.Password); err != nil {
		return nil, err
	}
	return u, nil
}

// SaveUser saves the provided user to the database. If the user is a new
// user, then the user.Created field is set to the current time. The
// user.Updated field is set to the current time regardless.
//
// If the user is a new user, then SaveUser also verifies that the user's
// email is unique and returns model.ErrInvalidEmail accordingly.
func SaveUser(u *model.User) error {
	if u.ID == 0 {
		// TODO: Should do this in the user.validate() method, but with the
		// current architecture of the application, that causes an import cycle.
		//
		// Check if email is unique.
		_, err := GetUserByEmail(u.Email)
		if err != nil {
			if err != sql.ErrNoRows {
				return model.ErrInvalidUserEmail
			}
			return err
		}

		u.Created = time.Now()
	}
	u.Updated = time.Now()

	rows, err := db.Query(userCreateStmt, u.Created, u.Updated, u.FirstName,
		u.LastName, u.Email, u.Type, u.GitHub, u.Twitter, u.Password)
	if err != nil {
		return err
	}
	for rows.Next() {
		if err := rows.Scan(&u.ID); err != nil {
			return err
		}
	}
	if err := rows.Err(); err != nil {
		return err
	}

	return nil
}
<commit_msg>Fix email address uniqueness validation.<commit_after>package database

import (
	"database/sql"
	"time"

	"github.com/hackedu/backend/model"
)

const userGetByIDStmt = `SELECT id, created, updated, first_name, last_name,
email, type, github, twitter, password FROM users WHERE id = $1`

const userGetByEmailStmt = `SELECT id, created, updated, first_name, last_name,
email, type, github, twitter, password FROM users WHERE email ilike $1`

const userCreateStmt = `INSERT INTO users (created, updated, first_name,
last_name, email, type, github, twitter, password) VALUES ($1, $2, $3, $4, $5,
$6, $7, $8, $9) RETURNING id`

// GetUser gets the user from the database with the provided ID.
func GetUser(id int64) (*model.User, error) {
	u := new(model.User)
	row := db.QueryRow(userGetByIDStmt, id)
	if err := row.Scan(&u.ID, &u.Created, &u.Updated, &u.FirstName, &u.LastName,
		&u.Email, &u.Type, &u.GitHub, &u.Twitter, &u.Password); err != nil {
		return nil, err
	}
	return u, nil
}

// GetUserByEmail gets the user from the database with the provided email.
func GetUserByEmail(email string) (*model.User, error) {
	u := new(model.User)
	row := db.QueryRow(userGetByEmailStmt, email)
	if err := row.Scan(&u.ID, &u.Created, &u.Updated, &u.FirstName, &u.LastName,
		&u.Email, &u.Type, &u.GitHub, &u.Twitter, &u.Password); err != nil {
		return nil, err
	}
	return u, nil
}

// SaveUser saves the provided user to the database. If the user is a new
// user, then the user.Created field is set to the current time. The
// user.Updated field is set to the current time regardless.
//
// If the user is a new user, then SaveUser also verifies that the user's
// email is unique and returns model.ErrInvalidEmail accordingly.
func SaveUser(u *model.User) error {
	if u.ID == 0 {
		// TODO: Should do this in the user.validate() method, but with the
		// current architecture of the application, that causes an import cycle.
		//
		// Check if email is unique.
		_, err := GetUserByEmail(u.Email)
		if err == nil {
			return model.ErrInvalidUserEmail
		} else if err != sql.ErrNoRows && err != nil {
			return err
		}

		u.Created = time.Now()
	}
	u.Updated = time.Now()

	rows, err := db.Query(userCreateStmt, u.Created, u.Updated, u.FirstName,
		u.LastName, u.Email, u.Type, u.GitHub, u.Twitter, u.Password)
	if err != nil {
		return err
	}
	for rows.Next() {
		if err := rows.Scan(&u.ID); err != nil {
			return err
		}
	}
	if err := rows.Err(); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>// errchk $G -e $D/$F.go

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Static error messages about interface conversions.

package main

type T struct {
	a int
}

var t *T

type I interface {
	M()
}

var i I

type I2 interface {
	M()
	N()
}

var i2 I2

type E interface{}

var e E

func main() {
	e = t // ok
	t = e // ERROR "need explicit|need type assertion"

	// neither of these can work,
	// because i has an extra method
	// that t does not, so i cannot contain a t.
	i = t // ERROR "incompatible|missing M method"
	t = i // ERROR "incompatible|need type assertion"

	i = i2 // ok
	i2 = i // ERROR "missing N method"

	i = I(i2)  // ok
	i2 = I2(i) // ERROR "missing N method"

	e = E(t) // ok
	t = T(e) // ERROR "need explicit|need type assertion|incompatible"
}

type M interface { M() }
var m M

var _ = m.(int)	// ERROR "impossible type assertion"

type Int int
func (Int) M(float) {}

var _ = m.(Int)	// ERROR "impossible type assertion"

var ii int
var jj Int

var m1 M = ii	// ERROR "missing"
var m2 M = jj	// ERROR "wrong type for M method"

var m3 = M(ii)	// ERROR "missing"
var m4 = M(jj)	// ERROR "wrong type for M method"
<commit_msg>test: Match gccgo error messages.<commit_after>// errchk $G -e $D/$F.go

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Static error messages about interface conversions.

package main

type T struct {
	a int
}

var t *T

type I interface {
	M()
}

var i I

type I2 interface {
	M()
	N()
}

var i2 I2

type E interface{}

var e E

func main() {
	e = t // ok
	t = e // ERROR "need explicit|need type assertion"

	// neither of these can work,
	// because i has an extra method
	// that t does not, so i cannot contain a t.
	i = t // ERROR "incompatible|missing M method"
	t = i // ERROR "incompatible|need type assertion"

	i = i2 // ok
	i2 = i // ERROR "incompatible|missing N method"

	i = I(i2)  // ok
	i2 = I2(i) // ERROR "invalid|missing N method"

	e = E(t) // ok
	t = T(e) // ERROR "need explicit|need type assertion|incompatible"
}

type M interface { M() }
var m M

var _ = m.(int)	// ERROR "impossible type assertion"

type Int int
func (Int) M(float) {}

var _ = m.(Int)	// ERROR "impossible type assertion"

var ii int
var jj Int

var m1 M = ii	// ERROR "incompatible|missing"
var m2 M = jj	// ERROR "incompatible|wrong type for M method"

var m3 = M(ii)	// ERROR "invalid|missing"
var m4 = M(jj)	// ERROR "invalid|wrong type for M method"
<|endoftext|>