<commit_before>/*
Copyright 2014 Workiva, LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package rest

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
)

// Resource represents a domain model.
type Resource interface{}

// ResourceHandler specifies the endpoint handlers for working with a resource. This
// consists of the business logic for performing CRUD operations.
type ResourceHandler interface {
	// ResourceName is used to identify what resource a handler corresponds to and is
	// used in the endpoint URLs, i.e. /api/:version/resourceName. This should be
	// unique across all ResourceHandlers.
	ResourceName() string

	// CreateURI returns the URI for creating a resource.
	CreateURI() string

	// CreateDocumentation returns a string describing the handler's create endpoint.
	CreateDocumentation() string

	// ReadURI returns the URI for reading a specific resource.
	ReadURI() string

	// ReadDocumentation returns a string describing the handler's read endpoint.
	ReadDocumentation() string

	// ReadListURI returns the URI for reading a list of resources.
	ReadListURI() string

	// ReadListDocumentation returns a string describing the handler's read list endpoint.
	ReadListDocumentation() string

	// UpdateURI returns the URI for updating a specific resource.
	UpdateURI() string

	// UpdateDocumentation returns a string describing the handler's update endpoint.
	UpdateDocumentation() string

	// UpdateListURI returns the URI for updating a list of resources.
	UpdateListURI() string

	// UpdateListDocumentation returns a string describing the handler's update list
	// endpoint.
	UpdateListDocumentation() string

	// DeleteURI returns the URI for deleting a specific resource.
	DeleteURI() string

	// DeleteDocumentation returns a string describing the handler's delete endpoint.
	DeleteDocumentation() string

	// CreateResource is the logic that corresponds to creating a new resource at
	// POST /api/:version/resourceName. Typically, this would insert a record into a
	// database. It returns the newly created resource or an error if the create failed.
	CreateResource(RequestContext, Payload, string) (Resource, error)

	// ReadResourceList is the logic that corresponds to reading multiple resources,
	// perhaps with specified query parameters accessed through the RequestContext. This
	// is mapped to GET /api/:version/resourceName. Typically, this would make some sort
	// of database query to fetch the resources. It returns the slice of results, a
	// cursor (or empty) string, and error (or nil).
	ReadResourceList(RequestContext, int, string, string) ([]Resource, string, error)

	// ReadResource is the logic that corresponds to reading a single resource by its ID
	// at GET /api/:version/resourceName/{id}. Typically, this would make some sort of
	// database query to load the resource. If the resource doesn't exist, nil should be
	// returned along with an appropriate error.
	ReadResource(RequestContext, string, string) (Resource, error)

	// UpdateResourceList is the logic that corresponds to updating a collection of
	// resources at PUT /api/:version/resourceName. Typically, this would make some
	// sort of database update call. It returns the updated resources or an error if
	// the update failed.
	UpdateResourceList(RequestContext, []Payload, string) ([]Resource, error)

	// UpdateResource is the logic that corresponds to updating an existing resource at
	// PUT /api/:version/resourceName/{id}. Typically, this would make some sort of
	// database update call. It returns the updated resource or an error if the update
	// failed.
	UpdateResource(RequestContext, string, Payload, string) (Resource, error)

	// DeleteResource is the logic that corresponds to deleting an existing resource at
	// DELETE /api/:version/resourceName/{id}. Typically, this would make some sort of
	// database delete call. It returns the deleted resource or an error if the delete
	// failed.
	DeleteResource(RequestContext, string, string) (Resource, error)

	// Authenticate is logic that is used to authenticate requests. The default behavior
	// of Authenticate, seen in BaseResourceHandler, always returns nil, meaning all
	// requests are authenticated. Returning an error means that the request is
	// unauthorized and any error message will be sent back with the response.
	Authenticate(*http.Request) error

	// Rules returns the resource rules to apply to incoming requests and outgoing
	// responses. The default behavior, seen in BaseResourceHandler, is to apply no
	// rules.
	Rules() Rules
}

// requestHandler constructs http.HandlerFuncs responsible for handling HTTP requests.
type requestHandler struct {
	API
}

// handleCreate returns a HandlerFunc which will deserialize the request payload, pass
// it to the provided create function, and then serialize and dispatch the response.
// The serialization mechanism used is specified by the "format" query parameter.
func (h requestHandler) handleCreate(handler ResourceHandler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)
		version := ctx.Version()
		rules := handler.Rules()

		data, err := decodePayload(payloadString(r.Body))
		if err != nil {
			// Payload decoding failed.
			ctx = ctx.setError(BadRequest(err.Error()))
		} else {
			data, err := applyInboundRules(data, rules, version)
			if err != nil {
				// Type coercion failed.
				ctx = ctx.setError(UnprocessableRequest(err.Error()))
			} else {
				resource, err := handler.CreateResource(ctx, data, ctx.Version())
				if err == nil {
					resource = applyOutboundRules(resource, rules, version)
				}
				ctx = ctx.setResult(resource)
				ctx = ctx.setStatus(http.StatusCreated)
				if err != nil {
					ctx = ctx.setError(err)
				}
			}
		}

		h.sendResponse(w, ctx)
	}
}

// handleReadList returns a HandlerFunc which will pass the request context to the
// provided read function and then serialize and dispatch the response. The
// serialization mechanism used is specified by the "format" query parameter.
func (h requestHandler) handleReadList(handler ResourceHandler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)
		version := ctx.Version()
		rules := handler.Rules()

		resources, cursor, err := handler.ReadResourceList(
			ctx, ctx.Limit(), ctx.Cursor(), version)

		if err == nil {
			// Apply rules to results.
			for idx, resource := range resources {
				resources[idx] = applyOutboundRules(resource, rules, version)
			}
		}

		ctx = ctx.setResult(resources)
		ctx = ctx.setCursor(cursor)
		ctx = ctx.setError(err)
		ctx = ctx.setStatus(http.StatusOK)

		h.sendResponse(w, ctx)
	}
}

// handleRead returns a HandlerFunc which will pass the resource id to the provided
// read function and then serialize and dispatch the response. The serialization
// mechanism used is specified by the "format" query parameter.
func (h requestHandler) handleRead(handler ResourceHandler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)
		version := ctx.Version()
		rules := handler.Rules()

		resource, err := handler.ReadResource(ctx, ctx.ResourceID(), version)
		if err == nil {
			resource = applyOutboundRules(resource, rules, version)
		}

		ctx = ctx.setResult(resource)
		ctx = ctx.setError(err)
		ctx = ctx.setStatus(http.StatusOK)

		h.sendResponse(w, ctx)
	}
}

// handleUpdateList returns a HandlerFunc which will deserialize the request payload,
// pass it to the provided update function, and then serialize and dispatch the
// response. The serialization mechanism used is specified by the "format" query
// parameter.
func (h requestHandler) handleUpdateList(handler ResourceHandler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)
		version := ctx.Version()
		rules := handler.Rules()

		payloadStr := payloadString(r.Body)
		var data []Payload
		var err error
		data, err = decodePayloadSlice(payloadStr)
		if err != nil {
			var p Payload
			p, err = decodePayload(payloadStr)
			data = []Payload{p}
		}

		if err != nil {
			// Payload decoding failed.
			ctx = ctx.setError(BadRequest(err.Error()))
		} else {
			for i := range data {
				data[i], err = applyInboundRules(data[i], rules, version)
			}
			if err != nil {
				// Type coercion failed.
				ctx = ctx.setError(UnprocessableRequest(err.Error()))
			} else {
				resources, err := handler.UpdateResourceList(ctx, data, version)
				if err == nil {
					// Apply rules to results.
					for idx, resource := range resources {
						resources[idx] = applyOutboundRules(resource, rules, version)
					}
				}

				ctx = ctx.setResult(resources)
				ctx = ctx.setError(err)
				ctx = ctx.setStatus(http.StatusOK)
			}
		}

		h.sendResponse(w, ctx)
	}
}

// handleUpdate returns a HandlerFunc which will deserialize the request payload,
// pass it to the provided update function, and then serialize and dispatch the
// response. The serialization mechanism used is specified by the "format" query
// parameter.
func (h requestHandler) handleUpdate(handler ResourceHandler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)
		version := ctx.Version()
		rules := handler.Rules()

		data, err := decodePayload(payloadString(r.Body))
		if err != nil {
			// Payload decoding failed.
			ctx = ctx.setError(err)
			ctx = ctx.setStatus(http.StatusInternalServerError)
		} else {
			data, err := applyInboundRules(data, rules, version)
			if err != nil {
				// Type coercion failed.
				ctx = ctx.setError(UnprocessableRequest(err.Error()))
			} else {
				resource, err := handler.UpdateResource(
					ctx, ctx.ResourceID(), data, version)
				if err == nil {
					resource = applyOutboundRules(resource, rules, version)
				}

				ctx = ctx.setResult(resource)
				ctx = ctx.setError(err)
				ctx = ctx.setStatus(http.StatusOK)
			}
		}

		h.sendResponse(w, ctx)
	}
}

// handleDelete returns a HandlerFunc which will pass the resource id to the provided
// delete function and then serialize and dispatch the response. The serialization
// mechanism used is specified by the "format" query parameter.
func (h requestHandler) handleDelete(handler ResourceHandler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)
		version := ctx.Version()
		rules := handler.Rules()

		resource, err := handler.DeleteResource(ctx, ctx.ResourceID(), version)
		if err == nil {
			resource = applyOutboundRules(resource, rules, version)
		}

		ctx = ctx.setResult(resource)
		ctx = ctx.setError(err)
		ctx = ctx.setStatus(http.StatusOK)

		h.sendResponse(w, ctx)
	}
}

// sendResponse writes a success or error response to the provided http.ResponseWriter
// based on the contents of the RequestContext.
func (h requestHandler) sendResponse(w http.ResponseWriter, ctx RequestContext) {
	format := ctx.ResponseFormat()
	serializer, err := h.responseSerializer(format)
	if err != nil {
		// Fall back to json serialization.
		serializer = jsonSerializer{}
		ctx = ctx.setError(BadRequest(fmt.Sprintf("Format not implemented: %s", format)))
	}

	sendResponse(w, NewResponse(ctx), serializer)
}

// sendResponse writes a response to the http.ResponseWriter.
func sendResponse(w http.ResponseWriter, r response, serializer ResponseSerializer) {
	status := r.Status
	contentType := serializer.ContentType()
	response, err := serializer.Serialize(r.Payload)
	if err != nil {
		log.Printf("Response serialization failed: %s", err)
		status = http.StatusInternalServerError
		contentType = "text/plain"
		response = []byte(err.Error())
	}

	w.Header().Set("Content-Type", contentType)
	w.WriteHeader(status)
	w.Write(response)
}

// decodePayload unmarshals the JSON payload and returns the resulting map. If the
// content is empty, an empty map is returned. If decoding fails, nil is returned
// with an error.
func decodePayload(payload []byte) (Payload, error) {
	if len(payload) == 0 {
		return map[string]interface{}{}, nil
	}

	var data Payload
	if err := json.Unmarshal(payload, &data); err != nil {
		return nil, err
	}

	return data, nil
}

// decodePayloadSlice unmarshals the JSON payload and returns the resulting slice.
// If the content is empty, an empty list is returned. If decoding fails, nil is
// returned with an error.
func decodePayloadSlice(payload []byte) ([]Payload, error) {
	if len(payload) == 0 {
		return []Payload{}, nil
	}

	var data []Payload
	if err := json.Unmarshal(payload, &data); err != nil {
		return nil, err
	}

	return data, nil
}

// payloadString returns the given io.Reader as a string. The reader must be rewound
// after calling this in order to be read again.
func payloadString(payload io.Reader) []byte {
	payloadStr, err := ioutil.ReadAll(payload)
	if err != nil {
		return nil
	}
	return payloadStr
}
<commit_msg>fixed 500 status code on PUT as well<commit_after>/*
Copyright 2014 Workiva, LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package rest

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
)

// Resource represents a domain model.
type Resource interface{}

// ResourceHandler specifies the endpoint handlers for working with a resource. This
// consists of the business logic for performing CRUD operations.
type ResourceHandler interface {
	// ResourceName is used to identify what resource a handler corresponds to and is
	// used in the endpoint URLs, i.e. /api/:version/resourceName. This should be
	// unique across all ResourceHandlers.
	ResourceName() string

	// CreateURI returns the URI for creating a resource.
	CreateURI() string

	// CreateDocumentation returns a string describing the handler's create endpoint.
	CreateDocumentation() string

	// ReadURI returns the URI for reading a specific resource.
	ReadURI() string

	// ReadDocumentation returns a string describing the handler's read endpoint.
	ReadDocumentation() string

	// ReadListURI returns the URI for reading a list of resources.
	ReadListURI() string

	// ReadListDocumentation returns a string describing the handler's read list endpoint.
	ReadListDocumentation() string

	// UpdateURI returns the URI for updating a specific resource.
	UpdateURI() string

	// UpdateDocumentation returns a string describing the handler's update endpoint.
	UpdateDocumentation() string

	// UpdateListURI returns the URI for updating a list of resources.
	UpdateListURI() string

	// UpdateListDocumentation returns a string describing the handler's update list
	// endpoint.
	UpdateListDocumentation() string

	// DeleteURI returns the URI for deleting a specific resource.
	DeleteURI() string

	// DeleteDocumentation returns a string describing the handler's delete endpoint.
	DeleteDocumentation() string

	// CreateResource is the logic that corresponds to creating a new resource at
	// POST /api/:version/resourceName. Typically, this would insert a record into a
	// database. It returns the newly created resource or an error if the create failed.
	CreateResource(RequestContext, Payload, string) (Resource, error)

	// ReadResourceList is the logic that corresponds to reading multiple resources,
	// perhaps with specified query parameters accessed through the RequestContext. This
	// is mapped to GET /api/:version/resourceName. Typically, this would make some sort
	// of database query to fetch the resources. It returns the slice of results, a
	// cursor (or empty) string, and error (or nil).
	ReadResourceList(RequestContext, int, string, string) ([]Resource, string, error)

	// ReadResource is the logic that corresponds to reading a single resource by its ID
	// at GET /api/:version/resourceName/{id}. Typically, this would make some sort of
	// database query to load the resource. If the resource doesn't exist, nil should be
	// returned along with an appropriate error.
	ReadResource(RequestContext, string, string) (Resource, error)

	// UpdateResourceList is the logic that corresponds to updating a collection of
	// resources at PUT /api/:version/resourceName. Typically, this would make some
	// sort of database update call. It returns the updated resources or an error if
	// the update failed.
	UpdateResourceList(RequestContext, []Payload, string) ([]Resource, error)

	// UpdateResource is the logic that corresponds to updating an existing resource at
	// PUT /api/:version/resourceName/{id}. Typically, this would make some sort of
	// database update call. It returns the updated resource or an error if the update
	// failed.
	UpdateResource(RequestContext, string, Payload, string) (Resource, error)

	// DeleteResource is the logic that corresponds to deleting an existing resource at
	// DELETE /api/:version/resourceName/{id}. Typically, this would make some sort of
	// database delete call. It returns the deleted resource or an error if the delete
	// failed.
	DeleteResource(RequestContext, string, string) (Resource, error)

	// Authenticate is logic that is used to authenticate requests. The default behavior
	// of Authenticate, seen in BaseResourceHandler, always returns nil, meaning all
	// requests are authenticated. Returning an error means that the request is
	// unauthorized and any error message will be sent back with the response.
	Authenticate(*http.Request) error

	// Rules returns the resource rules to apply to incoming requests and outgoing
	// responses. The default behavior, seen in BaseResourceHandler, is to apply no
	// rules.
	Rules() Rules
}

// requestHandler constructs http.HandlerFuncs responsible for handling HTTP requests.
type requestHandler struct {
	API
}

// handleCreate returns a HandlerFunc which will deserialize the request payload, pass
// it to the provided create function, and then serialize and dispatch the response.
// The serialization mechanism used is specified by the "format" query parameter.
func (h requestHandler) handleCreate(handler ResourceHandler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)
		version := ctx.Version()
		rules := handler.Rules()

		data, err := decodePayload(payloadString(r.Body))
		if err != nil {
			// Payload decoding failed.
			ctx = ctx.setError(BadRequest(err.Error()))
		} else {
			data, err := applyInboundRules(data, rules, version)
			if err != nil {
				// Type coercion failed.
				ctx = ctx.setError(UnprocessableRequest(err.Error()))
			} else {
				resource, err := handler.CreateResource(ctx, data, ctx.Version())
				if err == nil {
					resource = applyOutboundRules(resource, rules, version)
				}
				ctx = ctx.setResult(resource)
				ctx = ctx.setStatus(http.StatusCreated)
				if err != nil {
					ctx = ctx.setError(err)
				}
			}
		}

		h.sendResponse(w, ctx)
	}
}

// handleReadList returns a HandlerFunc which will pass the request context to the
// provided read function and then serialize and dispatch the response. The
// serialization mechanism used is specified by the "format" query parameter.
func (h requestHandler) handleReadList(handler ResourceHandler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)
		version := ctx.Version()
		rules := handler.Rules()

		resources, cursor, err := handler.ReadResourceList(
			ctx, ctx.Limit(), ctx.Cursor(), version)

		if err == nil {
			// Apply rules to results.
			for idx, resource := range resources {
				resources[idx] = applyOutboundRules(resource, rules, version)
			}
		}

		ctx = ctx.setResult(resources)
		ctx = ctx.setCursor(cursor)
		ctx = ctx.setError(err)
		ctx = ctx.setStatus(http.StatusOK)

		h.sendResponse(w, ctx)
	}
}

// handleRead returns a HandlerFunc which will pass the resource id to the provided
// read function and then serialize and dispatch the response. The serialization
// mechanism used is specified by the "format" query parameter.
func (h requestHandler) handleRead(handler ResourceHandler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)
		version := ctx.Version()
		rules := handler.Rules()

		resource, err := handler.ReadResource(ctx, ctx.ResourceID(), version)
		if err == nil {
			resource = applyOutboundRules(resource, rules, version)
		}

		ctx = ctx.setResult(resource)
		ctx = ctx.setError(err)
		ctx = ctx.setStatus(http.StatusOK)

		h.sendResponse(w, ctx)
	}
}

// handleUpdateList returns a HandlerFunc which will deserialize the request payload,
// pass it to the provided update function, and then serialize and dispatch the
// response. The serialization mechanism used is specified by the "format" query
// parameter.
func (h requestHandler) handleUpdateList(handler ResourceHandler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)
		version := ctx.Version()
		rules := handler.Rules()

		payloadStr := payloadString(r.Body)
		var data []Payload
		var err error
		data, err = decodePayloadSlice(payloadStr)
		if err != nil {
			var p Payload
			p, err = decodePayload(payloadStr)
			data = []Payload{p}
		}

		if err != nil {
			// Payload decoding failed.
			ctx = ctx.setError(BadRequest(err.Error()))
		} else {
			for i := range data {
				data[i], err = applyInboundRules(data[i], rules, version)
			}
			if err != nil {
				// Type coercion failed.
				ctx = ctx.setError(UnprocessableRequest(err.Error()))
			} else {
				resources, err := handler.UpdateResourceList(ctx, data, version)
				if err == nil {
					// Apply rules to results.
					for idx, resource := range resources {
						resources[idx] = applyOutboundRules(resource, rules, version)
					}
				}

				ctx = ctx.setResult(resources)
				ctx = ctx.setError(err)
				ctx = ctx.setStatus(http.StatusOK)
			}
		}

		h.sendResponse(w, ctx)
	}
}

// handleUpdate returns a HandlerFunc which will deserialize the request payload,
// pass it to the provided update function, and then serialize and dispatch the
// response. The serialization mechanism used is specified by the "format" query
// parameter.
func (h requestHandler) handleUpdate(handler ResourceHandler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)
		version := ctx.Version()
		rules := handler.Rules()

		data, err := decodePayload(payloadString(r.Body))
		if err != nil {
			// Payload decoding failed.
			ctx = ctx.setError(BadRequest(err.Error()))
		} else {
			data, err := applyInboundRules(data, rules, version)
			if err != nil {
				// Type coercion failed.
				ctx = ctx.setError(UnprocessableRequest(err.Error()))
			} else {
				resource, err := handler.UpdateResource(
					ctx, ctx.ResourceID(), data, version)
				if err == nil {
					resource = applyOutboundRules(resource, rules, version)
				}

				ctx = ctx.setResult(resource)
				ctx = ctx.setError(err)
				ctx = ctx.setStatus(http.StatusOK)
			}
		}

		h.sendResponse(w, ctx)
	}
}

// handleDelete returns a HandlerFunc which will pass the resource id to the provided
// delete function and then serialize and dispatch the response. The serialization
// mechanism used is specified by the "format" query parameter.
func (h requestHandler) handleDelete(handler ResourceHandler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := NewContext(nil, r)
		version := ctx.Version()
		rules := handler.Rules()

		resource, err := handler.DeleteResource(ctx, ctx.ResourceID(), version)
		if err == nil {
			resource = applyOutboundRules(resource, rules, version)
		}

		ctx = ctx.setResult(resource)
		ctx = ctx.setError(err)
		ctx = ctx.setStatus(http.StatusOK)

		h.sendResponse(w, ctx)
	}
}

// sendResponse writes a success or error response to the provided http.ResponseWriter
// based on the contents of the RequestContext.
func (h requestHandler) sendResponse(w http.ResponseWriter, ctx RequestContext) {
	format := ctx.ResponseFormat()
	serializer, err := h.responseSerializer(format)
	if err != nil {
		// Fall back to json serialization.
		serializer = jsonSerializer{}
		ctx = ctx.setError(BadRequest(fmt.Sprintf("Format not implemented: %s", format)))
	}

	sendResponse(w, NewResponse(ctx), serializer)
}

// sendResponse writes a response to the http.ResponseWriter.
func sendResponse(w http.ResponseWriter, r response, serializer ResponseSerializer) {
	status := r.Status
	contentType := serializer.ContentType()
	response, err := serializer.Serialize(r.Payload)
	if err != nil {
		log.Printf("Response serialization failed: %s", err)
		status = http.StatusInternalServerError
		contentType = "text/plain"
		response = []byte(err.Error())
	}

	w.Header().Set("Content-Type", contentType)
	w.WriteHeader(status)
	w.Write(response)
}

// decodePayload unmarshals the JSON payload and returns the resulting map. If the
// content is empty, an empty map is returned. If decoding fails, nil is returned
// with an error.
func decodePayload(payload []byte) (Payload, error) {
	if len(payload) == 0 {
		return map[string]interface{}{}, nil
	}

	var data Payload
	if err := json.Unmarshal(payload, &data); err != nil {
		return nil, err
	}

	return data, nil
}

// decodePayloadSlice unmarshals the JSON payload and returns the resulting slice.
// If the content is empty, an empty list is returned. If decoding fails, nil is
// returned with an error.
func decodePayloadSlice(payload []byte) ([]Payload, error) {
	if len(payload) == 0 {
		return []Payload{}, nil
	}

	var data []Payload
	if err := json.Unmarshal(payload, &data); err != nil {
		return nil, err
	}

	return data, nil
}

// payloadString returns the given io.Reader as a string. The reader must be rewound
// after calling this in order to be read again.
func payloadString(payload io.Reader) []byte {
	payloadStr, err := ioutil.ReadAll(payload)
	if err != nil {
		return nil
	}
	return payloadStr
}
<|endoftext|>


<commit_before>package openstack

const (
	BaseOps = `---
- type: replace
  path: /azs
  value:
  - name: z1
    cloud_properties:
      availability_zone: ((az))
  - name: z2
    cloud_properties:
      availability_zone: ((az))
  - name: z3
    cloud_properties:
      availability_zone: ((az))

- type: replace
  path: /vm_types
  value:
  - name: default
    cloud_properties:
      instance_type: m1.small
  - name: large
    cloud_properties:
      instance_type: m1.xlarge

- type: replace
  path: /compilation
  value:
    workers: 5
    reuse_compilation_vms: true
    az: z1
    vm_type: default
    network: default

- type: replace
  path: /disk_types/name=default/disk_size?
  value: 3000

- type: replace
  path: /networks
  value:
  - name: default
    type: manual
    subnets:
    - range: ((internal_cidr))
      gateway: ((internal_gw))
      azs: [z1, z2, z3]
      dns: [8.8.8.8]
      reserved: [((jumpbox__internal_ip))]
      cloud_properties:
        net_id: ((net_id))

- type: replace
  path: /vm_extensions/-
  value:
    name: cf-router-network-properties

- type: replace
  path: /vm_extensions/-
  value:
    name: cf-tcp-router-network-properties

- type: replace
  path: /vm_extensions/-
  value:
    name: diego-ssh-proxy-network-properties
`
)
<commit_msg>Reserve all IPs between gateway and jumpbox on OpenStack<commit_after>package openstack

const (
	BaseOps = `---
- type: replace
  path: /azs
  value:
  - name: z1
    cloud_properties:
      availability_zone: ((az))
  - name: z2
    cloud_properties:
      availability_zone: ((az))
  - name: z3
    cloud_properties:
      availability_zone: ((az))

- type: replace
  path: /vm_types
  value:
  - name: default
    cloud_properties:
      instance_type: m1.small
  - name: large
    cloud_properties:
      instance_type: m1.xlarge

- type: replace
  path: /compilation
  value:
    workers: 5
    reuse_compilation_vms: true
    az: z1
    vm_type: default
    network: default

- type: replace
  path: /disk_types/name=default/disk_size?
  value: 3000

- type: replace
  path: /networks
  value:
  - name: default
    type: manual
    subnets:
    - range: ((internal_cidr))
      gateway: ((internal_gw))
      azs: [z1, z2, z3]
      dns: [8.8.8.8]
      reserved: ["((internal_gw))-((jumpbox__internal_ip))"]
      cloud_properties:
        net_id: ((net_id))

- type: replace
  path: /vm_extensions/-
  value:
    name: cf-router-network-properties

- type: replace
  path: /vm_extensions/-
  value:
    name: cf-tcp-router-network-properties

- type: replace
  path: /vm_extensions/-
  value:
    name: diego-ssh-proxy-network-properties
`
)
<|endoftext|>


<commit_before>// Package matrix provides functions for simple linear algebra
package matrix

import (
	"bytes"
	"fmt"
	"math"
	"math/rand"
	"strconv"
	"strings"
)

var (
	ErrIncompatibleSizes    = &MatrixError{"Incompatible sizes of matricies"}
	ErrInsertionOutOfBounds = &MatrixError{"The matrix you are trying to insert to hasn't got that row or column."}
	ErrOutOfBounds          = &MatrixError{"The element you are trying to access is out of bounds."}
)

// Describes error during calculations
type MatrixError struct {
	ErrorString string
}

// Return the Error's string
func (err *MatrixError) Error() string { return err.ErrorString }

// Matrix construct that holds all the information about a matrix
type Matrix struct {
	rows, cols int
	values     []float64
}

// A function that will apply an abitary transformation to an element in the matrix
type ApplyFunc func(index int, value float64) float64

// Creates a new Matrix and initializes all values to 0
func Zeros(rows, cols int) *Matrix {
	A := new(Matrix)

	A.rows = rows
	A.cols = cols
	A.values = make([]float64, rows*cols)

	return A
}

// Creates a new Matrix and initializes all values to 1
func Ones(rows, cols int) *Matrix {
	A := Zeros(rows, cols)
	A.AddNum(1)

	return A
}

// Creates a new identity matrix
func Eye(size int) *Matrix {
	A := Zeros(size, size)

	for i := 1; i <= size; i++ {
		A.Set(i, i, 1)
	}

	return A
}

// Constructs a new matrix with random values in range [0;1)
func Rand(rows, cols int) *Matrix {
	A := Zeros(rows, cols)

	for i := range A.values {
		A.values[i] = rand.Float64()
	}

	return A
}

// Constructs a new Matrix from a Matlab style representation
//
//	A := matrix.FromMatlab("[1 2 3; 4 5 6]")
func FromMatlab(str string) *Matrix {
	rows := strings.Split(str, ";")

	for i, row := range rows {
		rows[i] = strings.Replace(row, ",", " ", -1)
	}

	nRows := len(rows)
	nColumns := len(strings.Fields(rows[0]))

	A := Zeros(nRows, nColumns)

	for i, row := range rows {
		row = strings.Trim(row, "[] ")
		strNums := strings.Fields(row)

		for j, num := range strNums {
			n, _ := strconv.ParseFloat(num, 64)
			A.Set(i+1, j+1, n)
		}
	}

	return A
}

// Return a Matlab representation of the matrix
func (A *Matrix) ToMatlab() string {
	buffer := new(bytes.Buffer)
	buffer.WriteString("[")

	for i, v := range A.values {
		buffer.WriteString(fmt.Sprintf("%f ", v))

		if (i+1)%A.cols == 0 {
			buffer.WriteString("; ")
		}
	}

	buffer.WriteString("]")

	return buffer.String()
}

// Gives the dimensions of the matrix
func (A *Matrix) Dim() (int, int) {
	return A.rows, A.cols
}

// Return the number of rows in the matrix
func (A *Matrix) Rows() int {
	return A.rows
}

// Return the number of columns in the matrix
func (A *Matrix) Columns() int {
	return A.cols
}

// Returns an array of all the values
func (A *Matrix) Values() []float64 {
	tmp := make([]float64, len(A.values))
	copy(tmp, A.values)
	return tmp
}

// Returns a string representation of the matrix
func (A *Matrix) String() string {
	buffer := new(bytes.Buffer)

	for i, elem := range A.values {
		buffer.WriteString(fmt.Sprintf("%.3f ", elem))

		if (i+1)%A.cols == 0 {
			buffer.WriteString("\n")
		}
	}

	return buffer.String()
}

// Returns an exact copy of the matrix
func (A *Matrix) Copy() *Matrix {
	B := Zeros(A.rows, A.cols)
	copy(B.values, A.values)

	return B
}

// Insert the given rows into the matrix, returning a new matrix.
// Passing 0 as the second argument is like making the
// passed rows the first few, whereas passing Rows() is like appending
// the additional rows to the matrix.
//
// Warning: This is an unsafe method to use, it does no boundary
// checking what so ever. If you'd like a safe version
// use: SafeInsertRows
func (A *Matrix) InsertRows(rows *Matrix, afterRow int) *Matrix {
	B := Zeros(A.rows+rows.rows, A.cols)

	// copy rows before inserted rows
	copy(B.values[0:afterRow*B.cols], A.values[0:afterRow*B.cols])
	// insert new rows
	copy(B.values[afterRow*B.cols:afterRow*B.cols+rows.rows*B.cols], rows.values)
	// copy rows after inserted rows
	copy(B.values[afterRow*B.cols+rows.rows*B.cols:], A.values[afterRow*B.cols:])

	return B
}

func (A *Matrix) InsertColumns(cols *Matrix, afterCol int) *Matrix {
	return Zeros(1, 1)
}

// Give a function, apply its transformation to every element in the matrix.
//
//	A := matrix.Rand(4, 8)
//	A.Apply(func(index int, value float64) float64 {
//		return sigmoid(value)
//	})
func (A *Matrix) Apply(f ApplyFunc) *Matrix {
	for i, v := range A.values {
		A.values[i] = f(i, v)
	}

	return A
}

// Get the element at [row, col].
//
// Warning: This is an unsafe method to use, it does no boundary
// checking what so ever. If you'd like a safe version
// use: SafeGet
func (A *Matrix) Get(row, col int) float64 {
	return A.values[(row-1)*A.cols+col-1]
}

// Set the element at [row, col] to val.
//
// Warning: This is an unsafe method to use, it does no boundary
// checking what so ever. If you'd like a safe version
// use: SafeSet
func (A *Matrix) Set(row, col int, val float64) {

	A.values[(row-1)*A.cols+col-1] = val

}

// Transpose the matrix
func (A *Matrix) Transpose() *Matrix {
	B := Zeros(A.cols, A.rows)

	for i := 1; i <= A.rows; i++ {
		for j := 1; j <= A.cols; j++ {
			B.values[(j-1)*B.cols+i-1] = A.values[(i-1)*A.cols+j-1]
		}
	}

	return B
}

// Add B to the matrix A, produces new matrix
func (A *Matrix) Add(B *Matrix) (*Matrix, error) {
	if !sameSize(A, B) {
		return nil, ErrIncompatibleSizes
	}

	C := Zeros(A.rows, A.cols)

	for i, val := range A.values {
		C.values[i] = val + B.values[i]
	}

	return C, nil
}

// Subtract B from the matrix A, produces a new matrix
func (A *Matrix) Sub(B *Matrix) (*Matrix, error) {
	if !sameSize(A, B) {
		return nil, ErrIncompatibleSizes
	}

	C := Zeros(A.rows, A.cols)

	for i, val := range A.values {
		C.values[i] = val - B.values[i]
	}

	return C, nil
}

// Multiplies 2 matricies with each other.
// Returns a new matrix.
//
// Warning: This is an unsafe method to use, it does no boundary
// checking what so ever. If you'd like a safe version
// use: SafeMul
func (A *Matrix) Mul(B *Matrix) *Matrix {

	C := Zeros(A.rows, B.cols)

	for i := 0; i < C.rows; i++ {
		for j := 0; j < C.cols; j++ {
			sum := float64(0)

			for k := 0; k < A.cols; k++ {
				sum += A.values[i*A.cols+k] * B.values[k*B.cols+j]
			}

			C.values[i*C.rows+j] = sum
		}
	}

	return C
}

// Standard scalar product of 2 matricies.
// Returns a new matrix.
//
// Warning: This is an unsafe method to use, it does no boundary
// checking what so ever. If you'd like a safe version
// use: SafeDot
func (A *Matrix) Dot(B *Matrix) *Matrix {
	C := Zeros(A.rows, A.cols)

	for i, val := range A.values {
		C.values[i] = val * B.values[i]
	}

	return C
}

// Scale the matrix by the factor f
func (A *Matrix) Scale(f float64) *Matrix {
	B := Zeros(A.rows, A.cols)

	for i, val := range A.values {
		B.values[i] = val * f
	}

	return B
}

// Take every element of the matrix to the power of n
func (A *Matrix) Power(n float64) *Matrix {
	B := Zeros(A.rows, A.cols)

	for i, val := range A.values {
		B.values[i] = math.Pow(val, n)
	}

	return B
}

// Add n to all elements in the matrix (in-place)
func (A *Matrix) AddNum(n float64) *Matrix {
	for i := range A.values {
		A.values[i] += n
	}

	return A
}
<commit_msg>prepared InsertColumns method with comments and generation of result matrix<commit_after>// Package matrix provides functions for simple linear algebra
package matrix

import (
	"bytes"
	"fmt"
	"math"
	"math/rand"
	"strconv"
	"strings"
)

var (
	ErrIncompatibleSizes    = &MatrixError{"Incompatible sizes of matricies"}
	ErrInsertionOutOfBounds = &MatrixError{"The matrix you are trying to insert to hasn't got that row or column."}
	ErrOutOfBounds          = &MatrixError{"The element you are trying to access is out of bounds."}
)

// Describes error during calculations
type MatrixError struct {
	ErrorString string
}

// Return the Error's string
func (err *MatrixError) Error() string { return err.ErrorString }

// Matrix construct that holds all the information about a matrix
type Matrix struct {
	rows, cols int
	values     []float64
}

// A function that will apply an abitary transformation to an element in the matrix
type ApplyFunc func(index int, value float64) float64

// Creates a new Matrix and initializes all values to 0
func Zeros(rows, cols int) *Matrix {
	A := new(Matrix)

	A.rows = rows
	A.cols = cols
	A.values = make([]float64, rows*cols)

	return A
}

// Creates a new Matrix and initializes all values to 1
func Ones(rows, cols int) *Matrix {
	A := Zeros(rows, cols)
	A.AddNum(1)

	return A
}

// Creates a new identity matrix
func Eye(size int) *Matrix {
	A := Zeros(size, size)

	for i := 1; i <= size; i++ {
		A.Set(i, i, 1)
	}

	return A
}

// Constructs a new matrix with random values in range [0;1)
func Rand(rows, cols int) *Matrix {
	A := Zeros(rows, cols)

	for i := range A.values {
		A.values[i] = rand.Float64()
	}

	return A
}

// Constructs a new Matrix from a Matlab style representation
//
//	A := matrix.FromMatlab("[1 2 3; 4 5 6]")
func FromMatlab(str string) *Matrix {
	rows := strings.Split(str, ";")

	for i, row := range rows {
		rows[i] = strings.Replace(row, ",", " ", -1)
	}

	nRows := len(rows)
	nColumns := len(strings.Fields(rows[0]))

	A := Zeros(nRows, nColumns)

	for i, row := range rows {
		row = strings.Trim(row, "[] ")
		strNums := strings.Fields(row)

		for j, num := range strNums {
			n, _ := strconv.ParseFloat(num, 64)
			A.Set(i+1, j+1, n)
		}
	}

	return A
}

// Return a Matlab representation of the matrix
func (A *Matrix) ToMatlab() string {
	buffer := new(bytes.Buffer)
	buffer.WriteString("[")

	for i, v := range A.values {
		buffer.WriteString(fmt.Sprintf("%f ", v))

		if (i+1)%A.cols == 0 {
			buffer.WriteString("; ")
		}
	}

	buffer.WriteString("]")

	return buffer.String()
}

// Gives the dimensions of the matrix
func (A *Matrix) Dim() (int, int) {
	return A.rows, A.cols
}

// Return the number of rows in the matrix
func (A *Matrix) Rows() int {
	return A.rows
}

// Return the number of columns in the matrix
func (A *Matrix) Columns() int {
	return A.cols
}

// Returns an array of all the values
func (A *Matrix) Values() []float64 {
	tmp := make([]float64, len(A.values))
	copy(tmp, A.values)
	return tmp
}

// Returns a string representation of the matrix
func (A *Matrix) String() string {
	buffer := new(bytes.Buffer)

	for i, elem := range A.values {
		buffer.WriteString(fmt.Sprintf("%.3f ", elem))

		if (i+1)%A.cols == 0 {
			buffer.WriteString("\n")
		}
	}

	return buffer.String()
}

// Returns an exact copy of the matrix
func (A *Matrix) Copy() *Matrix {
	B := Zeros(A.rows, A.cols)
	copy(B.values, A.values)

	return B
}

// Insert the given rows into the matrix, returning a new matrix.
// Passing 0 as the second argument is like making the
// passed rows the first few, whereas passing Rows() is like appending
// the additional rows to the matrix.
//
// Warning: This is an unsafe method to use, it does no boundary
// checking what so ever. If you'd like a safe version
// use: SafeInsertRows
func (A *Matrix) InsertRows(rows *Matrix, afterRow int) *Matrix {
	B := Zeros(A.rows+rows.rows, A.cols)

	// copy rows before inserted rows
	copy(B.values[0:afterRow*B.cols], A.values[0:afterRow*B.cols])
	// insert new rows
	copy(B.values[afterRow*B.cols:afterRow*B.cols+rows.rows*B.cols], rows.values)
	// copy rows after inserted rows
	copy(B.values[afterRow*B.cols+rows.rows*B.cols:], A.values[afterRow*B.cols:])

	return B
}

// Insert the given columns into the matrix, returning a new matrix.
// Passing 0 as the second argument is like making the
// passed columns the first few (on the left), whereas passing Columns() is like appending
// the additional columns to the matrix (on the right).
//
// Warning: This is an unsafe method to use, it does no boundary
// checking what so ever. If you'd like a safe version
// use: SafeInsertColumns
func (A *Matrix) InsertColumns(cols *Matrix, afterCol int) *Matrix {
	B := Zeros(A.rows, A.cols+cols.cols)

	return B
}

// Give a function, apply its transformation to every element in the matrix.
//
//	A := matrix.Rand(4, 8)
//	A.Apply(func(index int, value float64) float64 {
//		return sigmoid(value)
//	})
func (A *Matrix) Apply(f ApplyFunc) *Matrix {
	for i, v := range A.values {
		A.values[i] = f(i, v)
	}

	return A
}

// Get the element at [row, col].
//
// Warning: This is an unsafe method to use, it does no boundary
// checking what so ever. If you'd like a safe version
// use: SafeGet
func (A *Matrix) Get(row, col int) float64 {
	return A.values[(row-1)*A.cols+col-1]
}

// Set the element at [row, col] to val.
//
// Warning: This is an unsafe method to use, it does no boundary
// checking what so ever. If you'd like a safe version
// use: SafeSet
func (A *Matrix) Set(row, col int, val float64) {

	A.values[(row-1)*A.cols+col-1] = val

}

// Transpose the matrix
func (A *Matrix) Transpose() *Matrix {
	B := Zeros(A.cols, A.rows)

	for i := 1; i <= A.rows; i++ {
		for j := 1; j <= A.cols; j++ {
			B.values[(j-1)*B.cols+i-1] = A.values[(i-1)*A.cols+j-1]
		}
	}

	return B
}

// Add B to the matrix A, produces new matrix
func (A *Matrix) Add(B *Matrix) (*Matrix, error) {
	if !sameSize(A, B) {
		return nil, ErrIncompatibleSizes
	}

	C := Zeros(A.rows, A.cols)

	for i, val := range A.values {
		C.values[i] = val + B.values[i]
	}

	return C, nil
}

// Subtract B from the matrix A, produces a new matrix
func (A *Matrix) Sub(B *Matrix) (*Matrix, error) {
	if !sameSize(A, B) {
		return nil, ErrIncompatibleSizes
	}

	C := Zeros(A.rows, A.cols)

	for i, val := range A.values {
		C.values[i] = val - B.values[i]
	}

	return C, nil
}

// Multiplies 2 matricies with each other.
// Returns a new matrix.
//
// Warning: This is an unsafe method to use, it does no boundary
// checking what so ever. If you'd like a safe version
// use: SafeMul
func (A *Matrix) Mul(B *Matrix) *Matrix {

	C := Zeros(A.rows, B.cols)

	for i := 0; i < C.rows; i++ {
		for j := 0; j < C.cols; j++ {
			sum := float64(0)

			for k := 0; k < A.cols; k++ {
				sum += A.values[i*A.cols+k] * B.values[k*B.cols+j]
			}

			C.values[i*C.rows+j] = sum
		}
	}

	return C
}

// Standard scalar product of 2 matricies.
// Returns a new matrix.
//
// Warning: This is an unsafe method to use, it does no boundary
// checking what so ever. If you'd like a safe version
// use: SafeDot
func (A *Matrix) Dot(B *Matrix) *Matrix {
	C := Zeros(A.rows, A.cols)

	for i, val := range A.values {
		C.values[i] = val * B.values[i]
	}

	return C
}

// Scale the matrix by the factor f
func (A *Matrix) Scale(f float64) *Matrix {
	B := Zeros(A.rows, A.cols)

	for i, val := range A.values {
		B.values[i] = val * f
	}

	return B
}

// Take every element of the matrix to the power of n
func (A *Matrix) Power(n float64) *Matrix {
	B := Zeros(A.rows, A.cols)

	for i, val := range A.values {
		B.values[i] = math.Pow(val, n)
	}

	return B
}

// Add n to all elements in the matrix (in-place)
func (A *Matrix) AddNum(n float64) *Matrix {
	for i := range A.values {
		A.values[i] += n
	}

	return A
}
<|endoftext|>


<commit_before>/*
Package "matrix" provides types which represent matrix, scalar and iterator of elements.
*/
package matrix

import (
	"github.com/mitsuse/matrix-go/internal/types"
)

type Matrix interface {
	types.Matrix
}

type Cursor interface {
	types.Cursor
}
<commit_msg>Replace the short description with a sentence to describe the whole of "matirx-go" library.<commit_after>/*
Package "matrix" is an experimental library for matrix manipulation implemented in Golang.
*/
package matrix

import (
	"github.com/mitsuse/matrix-go/internal/types"
)

type Matrix interface {
	types.Matrix
}

type Cursor interface {
	types.Cursor
}
<|endoftext|>


<commit_before>/* Copyright 2018 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package gnmiutil contains helper functions related to gNMI.
package gnmiutil

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"reflect"
	"strconv"

	pb "github.com/openconfig/gnmi/proto/gnmi"
)

// GNMIFullPath builds the full path from the prefix and path.
func GNMIFullPath(prefix, path *pb.Path) *pb.Path {
	if prefix == nil {
		return path
	}

	fullPath := &pb.Path{Origin: path.Origin}
	if path.GetElem() != nil {
		fullPath.Elem = append(prefix.GetElem(), path.GetElem()...)
	}
	return fullPath
}

// ToPbVal convert string to TypedValue defined in gNMI proto.
// Supported types:
//     Integer: "1", "2"
//     Float: "1.5", "2.4"
//     String: "abc", "defg"
//     Boolean: "true", "false"
//     IETF JSON from file: "@ap_config.json"
func ToPbVal(stringVal string) (*pb.TypedValue, error) {
	if stringVal[0] == '@' {
		jsonFile := stringVal[1:]
		jsonConfig, err := ioutil.ReadFile(jsonFile)
		if err != nil {
			return nil, fmt.Errorf("cannot read data from file %v: %v", jsonFile, err)
		}
		jsonConfig = bytes.Trim(jsonConfig, " \r\n\t")
		return &pb.TypedValue{
			Value: &pb.TypedValue_JsonIetfVal{
				JsonIetfVal: jsonConfig,
			},
		}, nil
	}

	if strVal, err := strconv.Unquote(stringVal); err == nil {
		return &pb.TypedValue{
			Value: &pb.TypedValue_StringVal{
				StringVal: strVal,
			},
		}, nil
	}

	if intVal, err := strconv.ParseInt(stringVal, 10, 64); err == nil {
		return &pb.TypedValue{
			Value: &pb.TypedValue_IntVal{
				IntVal: intVal,
			},
		}, nil
	}

	if floatVal, err := strconv.ParseFloat(stringVal, 32); err == nil {
		return &pb.TypedValue{
			Value: &pb.TypedValue_FloatVal{
				FloatVal: float32(floatVal),
			},
		}, nil
	}

	if boolVal, err := strconv.ParseBool(stringVal); err == nil {
		return &pb.TypedValue{
			Value: &pb.TypedValue_BoolVal{
				BoolVal: boolVal,
			},
		}, nil
	}

	return &pb.TypedValue{
		Value: &pb.TypedValue_StringVal{
			StringVal: stringVal,
		},
	}, nil
}

// GNMIPathEquals checks whether the two given gNMI path equal to each other.
func GNMIPathEquals(actual, expected *pb.Path) bool {
	if len(actual.Elem) != len(expected.Elem) {
		return false
	}

	for i := 0; i < len(actual.Elem); i++ {
		actualElem := actual.Elem[i]
		expectedElem := expected.Elem[i]
		if actualElem.Name != expectedElem.Name || !reflect.DeepEqual(actualElem.Key, expectedElem.Key) {
			return false
		}
	}

	return true
}

// ValEqual checks whether the given two input value equal.
// It returns error if values are not equal.
func ValEqual(gnmiPath *pb.Path, actual *pb.TypedValue, expected *pb.TypedValue) error {
	actualValue := actual.Value
	expectedValue := expected.Value

	// JSON value.
	actualJsonValue, okA := actualValue.(*pb.TypedValue_JsonIetfVal)
	expectedJsonValue, okE := expectedValue.(*pb.TypedValue_JsonIetfVal)
	if okA && okE {
		var actualJson, expectedJson interface{}
		if err := json.Unmarshal(actualJsonValue.JsonIetfVal, &actualJson); err != nil {
			return fmt.Errorf("invalid value %v: %v", string(actualJsonValue.JsonIetfVal), err)
		}
		if err := json.Unmarshal(expectedJsonValue.JsonIetfVal, &expectedJson); err != nil {
			return fmt.Errorf("invalid value %v: %v", string(expectedJsonValue.JsonIetfVal), err)
		}
		if !reflect.DeepEqual(actualJson, expectedJson) {
			return fmt.Errorf("incorrect json config value on %v, actual = %v, expected = %v", gnmiPath, string(actualJsonValue.JsonIetfVal), string(expectedJsonValue.JsonIetfVal))
		}
		return nil
	}

	// Other value types.

	// No need to check uint64/int64 type matching.
	if uintValue, ok := actualValue.(*pb.TypedValue_UintVal); ok {
		actualValue = &pb.TypedValue_IntVal{
			IntVal: int64(uintValue.UintVal),
		}
	}
	if !reflect.DeepEqual(actualValue, expectedValue) {
		return fmt.Errorf("incorrect config on %v, actual = %v, expected = %v", gnmiPath, actualValue, expectedValue)
	}
	return nil
}
<commit_msg>Improved gNMI GetResponse JSON payload comparison.<commit_after>/* Copyright 2018 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package gnmiutil contains helper functions related to gNMI.
package gnmiutil

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"reflect"
	"strconv"

	pb "github.com/openconfig/gnmi/proto/gnmi"
)

// GNMIFullPath builds the full path from the prefix and path.
func GNMIFullPath(prefix, path *pb.Path) *pb.Path {
	if prefix == nil {
		return path
	}

	fullPath := &pb.Path{Origin: path.Origin}
	if path.GetElem() != nil {
		fullPath.Elem = append(prefix.GetElem(), path.GetElem()...)
	}
	return fullPath
}

// ToPbVal convert string to TypedValue defined in gNMI proto.
// Supported types:
//     Integer: "1", "2"
//     Float: "1.5", "2.4"
//     String: "abc", "defg"
//     Boolean: "true", "false"
//     IETF JSON from file: "@ap_config.json"
func ToPbVal(stringVal string) (*pb.TypedValue, error) {
	if stringVal[0] == '@' {
		jsonFile := stringVal[1:]
		jsonConfig, err := ioutil.ReadFile(jsonFile)
		if err != nil {
			return nil, fmt.Errorf("cannot read data from file %v: %v", jsonFile, err)
		}
		jsonConfig = bytes.Trim(jsonConfig, " \r\n\t")
		return &pb.TypedValue{
			Value: &pb.TypedValue_JsonIetfVal{
				JsonIetfVal: jsonConfig,
			},
		}, nil
	}

	if strVal, err := strconv.Unquote(stringVal); err == nil {
		return &pb.TypedValue{
			Value: &pb.TypedValue_StringVal{
				StringVal: strVal,
			},
		}, nil
	}

	if intVal, err := strconv.ParseInt(stringVal, 10, 64); err == nil {
		return &pb.TypedValue{
			Value: &pb.TypedValue_IntVal{
				IntVal: intVal,
			},
		}, nil
	}

	if floatVal, err := strconv.ParseFloat(stringVal, 32); err == nil {
		return &pb.TypedValue{
			Value: &pb.TypedValue_FloatVal{
				FloatVal: float32(floatVal),
			},
		}, nil
	}

	if boolVal, err := strconv.ParseBool(stringVal); err == nil {
		return &pb.TypedValue{
			Value: &pb.TypedValue_BoolVal{
				BoolVal: boolVal,
			},
		}, nil
	}

	return &pb.TypedValue{
		Value: &pb.TypedValue_StringVal{
			StringVal: stringVal,
		},
	}, nil
}

// GNMIPathEquals checks whether the two given gNMI path equal to each other.
func GNMIPathEquals(actual, expected *pb.Path) bool {
	if len(actual.Elem) != len(expected.Elem) {
		return false
	}

	for i := 0; i < len(actual.Elem); i++ {
		actualElem := actual.Elem[i]
		expectedElem := expected.Elem[i]
		if actualElem.Name != expectedElem.Name || !reflect.DeepEqual(actualElem.Key, expectedElem.Key) {
			return false
		}
	}

	return true
}

// ValEqual checks whether the given two input value equal.
// It returns error if values are not equal.
func ValEqual(gnmiPath *pb.Path, actual *pb.TypedValue, expected *pb.TypedValue) error {
	actualValue := actual.Value
	expectedValue := expected.Value

	// JSON value.
	actualJsonValue, okA := actualValue.(*pb.TypedValue_JsonIetfVal)
	expectedJsonValue, okE := expectedValue.(*pb.TypedValue_JsonIetfVal)
	if okA && okE {
		var actualJson, expectedJson interface{}
		if err := json.Unmarshal(actualJsonValue.JsonIetfVal, &actualJson); err != nil {
			return fmt.Errorf("invalid value %v: %v", string(actualJsonValue.JsonIetfVal), err)
		}
		if err := json.Unmarshal(expectedJsonValue.JsonIetfVal, &expectedJson); err != nil {
			return fmt.Errorf("invalid value %v: %v", string(expectedJsonValue.JsonIetfVal), err)
		}
		if err := jsonValEquals(actualJson, expectedJson); err != nil {
			return fmt.Errorf("incorrect json config on %v, actual = %v, expected = %v, detail = %v", gnmiPath, string(actualJsonValue.JsonIetfVal), string(expectedJsonValue.JsonIetfVal), err)
		}
		return nil
	}

	// Other value types.

	// No need to check uint64/int64 type matching.
	if uintValue, ok := actualValue.(*pb.TypedValue_UintVal); ok {
		actualValue = &pb.TypedValue_IntVal{
			IntVal: int64(uintValue.UintVal),
		}
	}
	if !reflect.DeepEqual(actualValue, expectedValue) {
		return fmt.Errorf("incorrect config on %v, actual = %v, expected = %v", gnmiPath, actualValue, expectedValue)
	}
	return nil
}

func jsonValEquals(actual, expected interface{}) error {
	// Both are json blob.
	actualJSON, okA := actual.(map[string]interface{})
	expectedJSON, okE := expected.(map[string]interface{})
	if okA && okE {
		for key, expectedVal := range expectedJSON {
			actualVal, ok := actualJSON[key]
			if !ok {
				return fmt.Errorf("miss the property %s, expected = %v", key, expectedVal)
			}
			if err := jsonValEquals(actualVal, expectedVal); err != nil {
				return err
			}
		}
		return nil
	}

	// Other value types.
	if !reflect.DeepEqual(actual, expected) {
		return fmt.Errorf("incorrect value, actual = %v, expected = %v", actual, expected)
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Timo Savola. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package run_test

import (
	"bufio"
	"bytes"
	"context"
	"io"
	"os"
	"path"
	"sync"
	"testing"

	"github.com/tsavola/gate/internal/runtest"
	"github.com/tsavola/gate/packet"
	"github.com/tsavola/gate/run"
	"github.com/tsavola/wag"
	"github.com/tsavola/wag/dewag"
	"github.com/tsavola/wag/wasm"
)

type readWriter struct {
	io.Reader
	io.Writer
}

func openProgram(testName string) (f *os.File) {
	f, err := os.Open(path.Join(os.Getenv("GATE_TEST_DIR"), testName, "prog.wasm"))
	if err != nil {
		panic(err)
	}
	return
}

const (
	dumpText = false
)

func TestAlloc(t *testing.T) {
	output := testRun(t, "alloc")
	t.Log(string(output.Bytes()))
}

func TestHello(t *testing.T) {
	output := testRun(t, "hello")
	if s := string(output.Bytes()); s != "hello world\n" {
		t.Fatalf("output: %#v", s)
	}
}

func TestServices(t *testing.T) {
	testRun(t, "services")
}

func testRun(t *testing.T, testName string) (output bytes.Buffer) {
	const (
		memorySizeLimit = 24 * wasm.Page
		stackSize       = 4096
	)

	rt := runtest.NewRuntime(nil)
	defer rt.Close()

	wasm := openProgram(testName)
	defer wasm.Close()

	var m wag.Module

	err := run.Load(&m, bufio.NewReader(wasm), rt.Runtime, new(bytes.Buffer), nil, nil)
	if err != nil {
		t.Fatalf("load error: %v", err)
	}

	if dumpText && testing.Verbose() {
		dewag.PrintTo(os.Stdout, m.Text(), m.FunctionMap(), nil)
	}

	_, memorySize := m.MemoryLimits()
	if memorySize > memorySizeLimit {
		memorySize = memorySizeLimit
	}

	var (
		image run.Image
		proc  run.Process
	)
	defer image.Release(rt.Runtime)
	defer proc.Kill(rt.Runtime)

	err = image.Init(context.Background(), rt.Runtime)
	if err != nil {
		t.Fatalf("image error: %v", err)
	}

	err = proc.Init(context.Background(), rt.Runtime, &image, os.Stdout)
	if err != nil {
		return
	}

	err = image.Populate(&m, memorySize, stackSize)
	if err != nil {
		t.Fatalf("image error: %v", err)
	}

	exit, trap, err := run.Run(context.Background(), rt.Runtime, &proc, &image, &testServiceRegistry{origin: &output})
	if err != nil {
		t.Fatalf("run error: %v", err)
	} else if trap != 0 {
		t.Fatalf("run trap: %s", trap)
	} else if exit != 0 {
		t.Fatalf("run exit: %d", exit)
	}

	if name := os.Getenv("GATE_TEST_DUMP"); name != "" {
		f, err := os.Create(name)
		if err != nil {
			panic(err)
		}
		defer f.Close()

		if err := image.DumpGlobalsMemoryStack(f); err != nil {
			t.Fatalf("dump error: %v", err)
		}
	}

	return
}

type testServiceRegistry struct {
	origin io.Writer
}

func (services *testServiceRegistry) StartServing(ctx context.Context, ops <-chan packet.Buf, evs chan<- packet.Buf, maxContentSize int,
) run.ServiceDiscoverer {
	d := new(testServiceDiscoverer)

	go func() {
		defer close(evs)

		for op := range ops {
			i := op.Code().Int16()

			d.nameLock.Lock()
			name := d.names[i]
			d.nameLock.Unlock()

			switch name {
			case "origin":
				if _, err := services.origin.Write(op.Content()); err != nil {
					panic(err)
				}
			}
		}
	}()

	return d
}

type testServiceDiscoverer struct {
	services []run.Service
	nameLock sync.Mutex
	names    []string
}

func (d *testServiceDiscoverer) Discover(names []string) []run.Service {
	for _, name := range names {
		var s run.Service

		switch name {
		case "origin":
			s.SetAvailable(0)

		case "test1":
			s.SetAvailable(1337)

		case "test2":
			s.SetAvailable(12765)
		}

		d.services = append(d.services, s)

		d.nameLock.Lock()
		d.names = append(d.names, name)
		d.nameLock.Unlock()
	}

	return d.services
}

func (d *testServiceDiscoverer) NumServices() int {
	return len(d.services)
}
<commit_msg>run: test: dump stacktrace on error<commit_after>// Copyright (c) 2016 Timo Savola. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package run_test

import (
	"bufio"
	"bytes"
	"context"
	"io"
	"os"
	"path"
	"sync"
	"testing"

	"github.com/tsavola/gate/internal/runtest"
	"github.com/tsavola/gate/packet"
	"github.com/tsavola/gate/run"
	"github.com/tsavola/wag"
	"github.com/tsavola/wag/dewag"
	"github.com/tsavola/wag/sections"
	"github.com/tsavola/wag/wasm"
)

type readWriter struct {
	io.Reader
	io.Writer
}

func openProgram(testName string) (f *os.File) {
	f, err := os.Open(path.Join(os.Getenv("GATE_TEST_DIR"), testName, "prog.wasm"))
	if err != nil {
		panic(err)
	}
	return
}

const (
	dumpText = false
)

func TestAlloc(t *testing.T) {
	output := testRun(t, "alloc")
	t.Log(string(output.Bytes()))
}

func TestHello(t *testing.T) {
	output := testRun(t, "hello")
	if s := string(output.Bytes()); s != "hello world\n" {
		t.Fatalf("output: %#v", s)
	}
}

func TestServices(t *testing.T) {
	testRun(t, "services")
}

func testRun(t *testing.T, testName string) (output bytes.Buffer) {
	const (
		memorySizeLimit = 24 * wasm.Page
		stackSize       = 4096
	)

	rt := runtest.NewRuntime(nil)
	defer rt.Close()

	wasm := openProgram(testName)
	defer wasm.Close()

	var nameSection sections.NameSection

	m := wag.Module{
		UnknownSectionLoader: sections.UnknownLoaders{"name": nameSection.Load}.Load,
	}

	err := run.Load(&m, bufio.NewReader(wasm), rt.Runtime, new(bytes.Buffer), nil, nil)
	if err != nil {
		t.Fatalf("load error: %v", err)
	}

	if dumpText && testing.Verbose() {
		dewag.PrintTo(os.Stdout, m.Text(), m.FunctionMap(), nil)
	}

	_, memorySize := m.MemoryLimits()
	if memorySize > memorySizeLimit {
		memorySize = memorySizeLimit
	}

	var (
		image run.Image
		proc  run.Process
	)
	defer image.Release(rt.Runtime)
	defer proc.Kill(rt.Runtime)

	err = image.Init(context.Background(), rt.Runtime)
	if err != nil {
		t.Fatalf("image error: %v", err)
	}

	err = proc.Init(context.Background(), rt.Runtime, &image, os.Stdout)
	if err != nil {
		return
	}

	err = image.Populate(&m, memorySize, stackSize)
	if err != nil {
		t.Fatalf("image error: %v", err)
	}

	if false {
		var buf bytes.Buffer

		if err := dewag.PrintTo(&buf, m.Text(), m.FunctionMap(), &nameSection); err == nil {
			t.Logf("disassembly:\n%s", string(buf.Bytes()))
		} else {
			t.Errorf("disassembly error: %v", err)
		}
	}

	stacktrace := true

	exit, trap, err := run.Run(context.Background(), rt.Runtime, &proc, &image, &testServiceRegistry{origin: &output})
	if err != nil {
		t.Errorf("run error: %v", err)
	} else if trap != 0 {
		t.Errorf("run trap: %s", trap)
	} else if exit != 0 {
		t.Errorf("run exit: %d", exit)
	} else {
		stacktrace = false
	}

	if stacktrace {
		var buf bytes.Buffer

		if err := image.DumpStacktrace(&buf, m.FunctionMap(), m.CallMap(), m.FunctionSignatures(), &nameSection); err == nil {
			t.Logf("stacktrace:\n%s", string(buf.Bytes()))
		} else {
			t.Errorf("stacktrace error: %v", err)
		}
	}

	if name := os.Getenv("GATE_TEST_DUMP"); name != "" {
		f, err := os.Create(name)
		if err != nil {
			panic(err)
		}
		defer f.Close()

		if err := image.DumpGlobalsMemoryStack(f); err != nil {
			t.Errorf("dump error: %v", err)
		}
	}

	return
}

type testServiceRegistry struct {
	origin io.Writer
}

func (services *testServiceRegistry) StartServing(ctx context.Context, ops <-chan packet.Buf, evs chan<- packet.Buf, maxContentSize int,
) run.ServiceDiscoverer {
	d := new(testServiceDiscoverer)

	go func() {
		defer close(evs)

		for op := range ops {
			i := op.Code().Int16()

			d.nameLock.Lock()
			name := d.names[i]
			d.nameLock.Unlock()

			switch name {
			case "origin":
				if _, err := services.origin.Write(op.Content()); err != nil {
					panic(err)
				}
			}
		}
	}()

	return d
}

type testServiceDiscoverer struct {
	services []run.Service
	nameLock sync.Mutex
	names    []string
}

func (d *testServiceDiscoverer) Discover(names []string) []run.Service {
	for _, name := range names {
		var s run.Service

		switch name {
		case "origin":
			s.SetAvailable(0)

		case "test1":
			s.SetAvailable(1337)

		case "test2":
			s.SetAvailable(12765)
		}

		d.services = append(d.services, s)

		d.nameLock.Lock()
		d.names = append(d.names, name)
		d.nameLock.Unlock()
	}

	return d.services
}

func (d *testServiceDiscoverer) NumServices() int {
	return len(d.services)
}
<|endoftext|>


<commit_before>package builder

import (
	"bytes"
	"strings"
)

//BuildQuery - get sql string from expression
func (query *Query) BuildQuery() string {
	var buffer bytes.Buffer
	buffer.WriteString(query.TypeQuery)
	buffer.WriteString(" ")
	columns := interfaceToString(query.Columns)
	buffer.WriteString(strings.Join(columns[:], ", "))

	buffer.WriteString(" From ")
	buffer.WriteString(query.TableName)

	if len(query.WhereCond) != 0 {
		buffer.WriteString(" Where ")
		for index, element := range query.WhereCond {
			if index == 0 {
				buffer.WriteString(
					strings.Replace(element.Expression, "?", convertValueToString(element.Value), -1),
				)
			} else {
				buffer.WriteString(element.Delimiter)
				buffer.WriteString(
					strings.Replace(element.Expression, "?", convertValueToString(element.Value), -1),
				)
			}
		}
	}

	if len(query.GroupByStruct) != 0 {
		buffer.WriteString(" Group By ")
		var res [len(query.GroupByStruct)]string
		for index, element := range query.GroupByStruct {
			res[index] = convertValueToString(element)
		}
		buffer.WriteString(strings.Join(res[:], ", "))
	}
	buffer.WriteString(";")
	return buffer.String()
}
<commit_msg>make array<commit_after>package builder

import (
	"bytes"
	"strings"
)

//BuildQuery - get sql string from expression
func (query *Query) BuildQuery() string {
	var buffer bytes.Buffer
	buffer.WriteString(query.TypeQuery)
	buffer.WriteString(" ")
	columns := interfaceToString(query.Columns)
	buffer.WriteString(strings.Join(columns[:], ", "))

	buffer.WriteString(" From ")
	buffer.WriteString(query.TableName)

	if len(query.WhereCond) != 0 {
		buffer.WriteString(" Where ")
		for index, element := range query.WhereCond {
			if index == 0 {
				buffer.WriteString(
					strings.Replace(element.Expression, "?", convertValueToString(element.Value), -1),
				)
			} else {
				buffer.WriteString(element.Delimiter)
				buffer.WriteString(
					strings.Replace(element.Expression, "?", convertValueToString(element.Value), -1),
				)
			}
		}
	}

	if len(query.GroupByStruct) != 0 {
		buffer.WriteString(" Group By ")
		res := make([]string, len(query.GroupByStruct))

		for index, element := range query.GroupByStruct {
			res[index] = convertValueToString(element)
		}
		buffer.WriteString(strings.Join(res[:], ", "))
	}
	buffer.WriteString(";")
	return buffer.String()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/Tonkpils/snag/vow"
	"github.com/shiena/ansicolor"
	fsn "gopkg.in/fsnotify.v1"
)

var mtimes = map[string]time.Time{}
var clearBuffer = func() {
	fmt.Print("\033c")
}

type Bob struct {
	w        *fsn.Watcher
	mtx      sync.RWMutex
	curVow   *vow.Vow
	done     chan struct{}
	watching map[string]struct{}
	watchDir string

	cmds         [][]string
	ignoredItems []string

	verbose bool
}

func NewBuilder(c config) (*Bob, error) {
	w, err := fsn.NewWatcher()
	if err != nil {
		return nil, err
	}

	cmds := make([][]string, len(c.Script))
	for i, s := range c.Script {
		cmds[i] = strings.Split(s, " ")
	}

	return &Bob{
		w:            w,
		done:         make(chan struct{}),
		watching:     map[string]struct{}{},
		cmds:         cmds,
		ignoredItems: c.IgnoredItems,
		verbose:      c.Verbose,
	}, nil
}

func (b *Bob) Close() error {
	close(b.done)
	return b.w.Close()
}

func (b *Bob) Watch(path string) error {
	b.watchDir = path
	b.watch(path)
	b.execute()

	for {
		select {
		case ev := <-b.w.Events:
			var queueBuild bool
			switch {
			case isCreate(ev.Op):
				queueBuild = b.watch(ev.Name)
			case isDelete(ev.Op):
				if _, ok := b.watching[ev.Name]; ok {
					b.w.Remove(ev.Name)
					delete(b.watching, ev.Name)
				}
				queueBuild = true
			case isModify(ev.Op):
				queueBuild = true
			}
			if queueBuild {
				b.maybeQueue(ev.Name)
			}
		case err := <-b.w.Errors:
			log.Println("error:", err)
		case <-b.done:
			return nil
		}
	}
}

func (b *Bob) maybeQueue(path string) {
	if b.isExcluded(path) {
		return
	}

	stat, err := os.Stat(path)
	if err != nil {
		// we couldn't find the file
		// most likely a deletion
		delete(mtimes, path)
		b.execute()
		return
	}

	mtime := stat.ModTime()
	lasttime := mtimes[path]
	if !mtime.Equal(lasttime) {
		// the file has been modified and the
		// file system event wasn't bogus
		mtimes[path] = mtime
		b.execute()
	}
}

func (b *Bob) stopCurVow() {
	b.mtx.Lock()
	if b.curVow != nil {
		b.curVow.Stop()
	}
	b.mtx.Unlock()
}

func (b *Bob) execute() {
	b.stopCurVow()

	clearBuffer()
	b.mtx.Lock()

	// setup the first command
	firstCmd := b.cmds[0]
	b.curVow = vow.To(firstCmd[0], firstCmd[1:]...)

	// setup the remaining commands
	for i := 1; i < len(b.cmds); i++ {
		cmd := b.cmds[i]
		b.curVow = b.curVow.Then(cmd[0], cmd[1:]...)
	}
	b.curVow.Verbose = b.verbose
	go b.curVow.Exec(ansicolor.NewAnsiColorWriter(os.Stdout))

	b.mtx.Unlock()
}

func (b *Bob) watch(path string) bool {
	var shouldBuild bool
	if _, ok := b.watching[path]; ok {
		return false
	}
	filepath.Walk(path, func(p string, fi os.FileInfo, err error) error {
		if fi == nil {
			return filepath.SkipDir
		}

		if !fi.IsDir() {
			shouldBuild = true
			return nil
		}

		if b.isExcluded(p) {
			return filepath.SkipDir
		}

		if err := b.w.Add(p); err != nil {
			return err
		}
		b.watching[p] = struct{}{}

		return nil
	})
	return shouldBuild
}

func (b *Bob) isExcluded(path string) bool {
	// get the relative path
	path = strings.TrimPrefix(path, b.watchDir+string(filepath.Separator))

	for _, p := range b.ignoredItems {
		if p == path {
			return true
		}
	}
	return false
}

func isCreate(op fsn.Op) bool {
	return op&fsn.Create == fsn.Create
}

func isDelete(op fsn.Op) bool {
	return op&fsn.Remove == fsn.Remove
}

func isModify(op fsn.Op) bool {
	return op&fsn.Write == fsn.Write ||
		op&fsn.Rename == fsn.Rename
}
<commit_msg>expliticly ignoring result when calling b.watch for the first time closes #34<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/Tonkpils/snag/vow"
	"github.com/shiena/ansicolor"
	fsn "gopkg.in/fsnotify.v1"
)

var mtimes = map[string]time.Time{}
var clearBuffer = func() {
	fmt.Print("\033c")
}

type Bob struct {
	w        *fsn.Watcher
	mtx      sync.RWMutex
	curVow   *vow.Vow
	done     chan struct{}
	watching map[string]struct{}
	watchDir string

	cmds         [][]string
	ignoredItems []string

	verbose bool
}

func NewBuilder(c config) (*Bob, error) {
	w, err := fsn.NewWatcher()
	if err != nil {
		return nil, err
	}

	cmds := make([][]string, len(c.Script))
	for i, s := range c.Script {
		cmds[i] = strings.Split(s, " ")
	}

	return &Bob{
		w:            w,
		done:         make(chan struct{}),
		watching:     map[string]struct{}{},
		cmds:         cmds,
		ignoredItems: c.IgnoredItems,
		verbose:      c.Verbose,
	}, nil
}

func (b *Bob) Close() error {
	close(b.done)
	return b.w.Close()
}

func (b *Bob) Watch(path string) error {
	b.watchDir = path
	// this can never return false since we will always
	// have at least one file in the directory (.snag.yml)
	_ = b.watch(path)
	b.execute()

	for {
		select {
		case ev := <-b.w.Events:
			var queueBuild bool
			switch {
			case isCreate(ev.Op):
				queueBuild = b.watch(ev.Name)
			case isDelete(ev.Op):
				if _, ok := b.watching[ev.Name]; ok {
					b.w.Remove(ev.Name)
					delete(b.watching, ev.Name)
				}
				queueBuild = true
			case isModify(ev.Op):
				queueBuild = true
			}
			if queueBuild {
				b.maybeQueue(ev.Name)
			}
		case err := <-b.w.Errors:
			log.Println("error:", err)
		case <-b.done:
			return nil
		}
	}
}

func (b *Bob) maybeQueue(path string) {
	if b.isExcluded(path) {
		return
	}

	stat, err := os.Stat(path)
	if err != nil {
		// we couldn't find the file
		// most likely a deletion
		delete(mtimes, path)
		b.execute()
		return
	}

	mtime := stat.ModTime()
	lasttime := mtimes[path]
	if !mtime.Equal(lasttime) {
		// the file has been modified and the
		// file system event wasn't bogus
		mtimes[path] = mtime
		b.execute()
	}
}

func (b *Bob) stopCurVow() {
	b.mtx.Lock()
	if b.curVow != nil {
		b.curVow.Stop()
	}
	b.mtx.Unlock()
}

func (b *Bob) execute() {
	b.stopCurVow()

	clearBuffer()
	b.mtx.Lock()

	// setup the first command
	firstCmd := b.cmds[0]
	b.curVow = vow.To(firstCmd[0], firstCmd[1:]...)

	// setup the remaining commands
	for i := 1; i < len(b.cmds); i++ {
		cmd := b.cmds[i]
		b.curVow = b.curVow.Then(cmd[0], cmd[1:]...)
	}
	b.curVow.Verbose = b.verbose
	go b.curVow.Exec(ansicolor.NewAnsiColorWriter(os.Stdout))

	b.mtx.Unlock()
}

func (b *Bob) watch(path string) bool {
	var shouldBuild bool
	if _, ok := b.watching[path]; ok {
		return false
	}
	filepath.Walk(path, func(p string, fi os.FileInfo, err error) error {
		if fi == nil {
			return filepath.SkipDir
		}

		if !fi.IsDir() {
			shouldBuild = true
			return nil
		}

		if b.isExcluded(p) {
			return filepath.SkipDir
		}

		if err := b.w.Add(p); err != nil {
			return err
		}
		b.watching[p] = struct{}{}

		return nil
	})
	return shouldBuild
}

func (b *Bob) isExcluded(path string) bool {
	// get the relative path
	path = strings.TrimPrefix(path, b.watchDir+string(filepath.Separator))

	for _, p := range b.ignoredItems {
		if p == path {
			return true
		}
	}
	return false
}

func isCreate(op fsn.Op) bool {
	return op&fsn.Create == fsn.Create
}

func isDelete(op fsn.Op) bool {
	return op&fsn.Remove == fsn.Remove
}

func isModify(op fsn.Op) bool {
	return op&fsn.Write == fsn.Write ||
		op&fsn.Rename == fsn.Rename
}
<|endoftext|>


<commit_before>package js

import (
	"errors"
	"github.com/GeertJohan/go.rice"
	"github.com/loadimpact/speedboat/loadtest"
	"github.com/loadimpact/speedboat/runner"
	"github.com/valyala/fasthttp"
	"golang.org/x/net/context"
	"gopkg.in/olebedev/go-duktape.v2"
	"strconv"
	"time"
)

type Runner struct {
	Client *fasthttp.Client

	lib    *rice.Box
	vendor *rice.Box
}

func New() *Runner {
	return &Runner{
		Client: &fasthttp.Client{
			MaxIdleConnDuration: time.Duration(0),
		},
		lib:    rice.MustFindBox("lib"),
		vendor: rice.MustFindBox("vendor"),
	}
}

func (r *Runner) Run(ctx context.Context, t loadtest.LoadTest, id int64) <-chan runner.Result {
	ch := make(chan runner.Result)

	go func() {
		defer close(ch)

		c := duktape.New()
		defer c.Destroy()

		c.PushGlobalObject()

		c.PushObject()
		{
			pushModules(c, r, ch)
			c.PutPropString(-2, "modules")

			c.PushObject()
			c.PutPropString(-2, "types")

			pushData(c, t, id)
			c.PutPropString(-2, "data")
		}
		c.PutPropString(-2, "__internal__")

		load := map[*rice.Box][]string{
			r.lib:    []string{"require.js", "http.js"},
			r.vendor: []string{"lodash/dist/lodash.min.js"},
		}
		for box, files := range load {
			for _, name := range files {
				src, err := box.String(name)
				if err != nil {
					ch <- runner.Result{Error: err}
					return
				}
				if err = loadFile(c, name, src); err != nil {
					ch <- runner.Result{Error: err}
					return
				}
			}
		}

		// This will probably be moved to a module; global for compatibility
		c.PushGoFunction(func(c *duktape.Context) int {
			t := argNumber(c, 0)
			time.Sleep(time.Duration(t) * time.Second)
			return 0
		})
		c.PutPropString(-2, "sleep")

		if top := c.GetTopIndex(); top != 0 {
			panic("PROGRAMMING ERROR: Excess items on stack: " + strconv.Itoa(top+1))
		}

		// It should be cheaper memory-wise to keep the code on the global object (where it's
		// safe from GC shenanigans) and duplicate the key every iteration, than to keep it on
		// the stack and duplicate the whole function every iteration; just make it ABUNDANTLY
		// CLEAR that the script should NEVER touch that property or dumb things will happen
		codeProp := "__!!__seriously__don't__touch__from__script__!!__"
		c.PushString("script")
		if err := c.PcompileStringFilename(0, t.Source); err != nil {
			ch <- runner.Result{Error: err}
			return
		}
		c.PutPropString(-2, codeProp)

		c.PushString(codeProp)
		for {
			select {
			default:
				c.DupTop()
				if c.PcallProp(-3, 0) != duktape.ErrNone {
					c.GetPropString(-1, "fileName")
					filename := c.SafeToString(-1)
					c.Pop()

					c.GetPropString(-1, "lineNumber")
					line := c.ToNumber(-1)
					c.Pop()

					err := errors.New(c.SafeToString(-1))
					ch <- runner.Result{Error: err, Extra: map[string]interface{}{
						"file": filename,
						"line": line,
					}}
				}
				c.Pop() // Pop return value
			case <-ctx.Done():
				return
			}
		}
	}()

	return ch
}

func pushData(c *duktape.Context, t loadtest.LoadTest, id int64) {
	c.PushObject()

	c.PushInt(int(id))
	c.PutPropString(-2, "id")

	c.PushObject()
	{
		c.PushString(t.URL)
		c.PutPropString(-2, "url")
	}
	c.PutPropString(-2, "test")
}

func pushModules(c *duktape.Context, r *Runner, ch chan<- runner.Result) {
	c.PushObject()

	api := map[string]map[string]apiFunc{
		"http": map[string]apiFunc{
			"do": apiHTTPDo,
			"setMaxConnectionsPerHost": apiHTTPSetMaxConnectionsPerHost,
		},
	}
	for name, mod := range api {
		pushModule(c, r, ch, mod)
		c.PutPropString(-2, name)
	}
}

func pushModule(c *duktape.Context, r *Runner, ch chan<- runner.Result, members map[string]apiFunc) {
	c.PushObject()

	for name, fn := range members {
		fn := fn
		c.PushGoFunction(func(lc *duktape.Context) int {
			return fn(r, lc, ch)
		})
		c.PutPropString(-2, name)
	}
}

func loadFile(c *duktape.Context, name, src string) error {
	c.PushString(name)
	if err := c.PcompileStringFilename(0, src); err != nil {
		return err
	}
	c.Pcall(0)
	c.Pop()
	return nil
}
<commit_msg>[refactor] Function for making JS contexts<commit_after>package js

import (
	"errors"
	"github.com/GeertJohan/go.rice"
	"github.com/loadimpact/speedboat/loadtest"
	"github.com/loadimpact/speedboat/runner"
	"github.com/valyala/fasthttp"
	"golang.org/x/net/context"
	"gopkg.in/olebedev/go-duktape.v2"
	"strconv"
	"time"
)

type Runner struct {
	Client *fasthttp.Client

	lib    *rice.Box
	vendor *rice.Box
}

func New() *Runner {
	return &Runner{
		Client: &fasthttp.Client{
			MaxIdleConnDuration: time.Duration(0),
		},
		lib:    rice.MustFindBox("lib"),
		vendor: rice.MustFindBox("vendor"),
	}
}

func (r *Runner) Run(ctx context.Context, t loadtest.LoadTest, id int64) <-chan runner.Result {
	ch := make(chan runner.Result)

	go func() {
		defer close(ch)

		c, err := r.newJSContext(t, id, ch)
		if err != nil {
			ch <- runner.Result{Error: err}
			return
		}
		defer c.Destroy()

		c.PushGlobalObject()

		// It should be cheaper memory-wise to keep the code on the global object (where it's
		// safe from GC shenanigans) and duplicate the key every iteration, than to keep it on
		// the stack and duplicate the whole function every iteration; just make it ABUNDANTLY
		// CLEAR that the script should NEVER touch that property or dumb things will happen
		codeProp := "__!!__seriously__don't__touch__from__script__!!__"
		c.PushString("script")
		if err := c.PcompileStringFilename(0, t.Source); err != nil {
			ch <- runner.Result{Error: err}
			return
		}
		c.PutPropString(-2, codeProp)

		c.PushString(codeProp)
		for {
			select {
			default:
				c.DupTop()
				if c.PcallProp(-3, 0) != duktape.ErrNone {
					c.GetPropString(-1, "fileName")
					filename := c.SafeToString(-1)
					c.Pop()

					c.GetPropString(-1, "lineNumber")
					line := c.ToNumber(-1)
					c.Pop()

					err := errors.New(c.SafeToString(-1))
					ch <- runner.Result{Error: err, Extra: map[string]interface{}{
						"file": filename,
						"line": line,
					}}
				}
				c.Pop() // Pop return value
			case <-ctx.Done():
				return
			}
		}
	}()

	return ch
}

func (r *Runner) newJSContext(t loadtest.LoadTest, id int64, ch chan<- runner.Result) (*duktape.Context, error) {
	c := duktape.New()
	c.PushGlobalObject()

	c.PushObject()
	{
		pushModules(c, r, ch)
		c.PutPropString(-2, "modules")

		c.PushObject()
		c.PutPropString(-2, "types")

		pushData(c, t, id)
		c.PutPropString(-2, "data")
	}
	c.PutPropString(-2, "__internal__")

	load := map[*rice.Box][]string{
		r.lib:    []string{"require.js", "http.js"},
		r.vendor: []string{"lodash/dist/lodash.min.js"},
	}
	for box, files := range load {
		for _, name := range files {
			src, err := box.String(name)
			if err != nil {
				return nil, err
			}
			if err = loadFile(c, name, src); err != nil {
				return nil, err
			}
		}
	}

	// This will probably be moved to a module; global for compatibility
	c.PushGoFunction(func(c *duktape.Context) int {
		t := argNumber(c, 0)
		time.Sleep(time.Duration(t) * time.Second)
		return 0
	})
	c.PutPropString(-2, "sleep")

	if top := c.GetTopIndex(); top != 0 {
		panic("PROGRAMMING ERROR: Excess items on stack: " + strconv.Itoa(top+1))
	}

	c.Pop() // Global object
	return c, nil
}

func pushData(c *duktape.Context, t loadtest.LoadTest, id int64) {
	c.PushObject()

	c.PushInt(int(id))
	c.PutPropString(-2, "id")

	c.PushObject()
	{
		c.PushString(t.URL)
		c.PutPropString(-2, "url")
	}
	c.PutPropString(-2, "test")
}

func pushModules(c *duktape.Context, r *Runner, ch chan<- runner.Result) {
	c.PushObject()

	api := map[string]map[string]apiFunc{
		"http": map[string]apiFunc{
			"do": apiHTTPDo,
			"setMaxConnectionsPerHost": apiHTTPSetMaxConnectionsPerHost,
		},
	}
	for name, mod := range api {
		pushModule(c, r, ch, mod)
		c.PutPropString(-2, name)
	}
}

func pushModule(c *duktape.Context, r *Runner, ch chan<- runner.Result, members map[string]apiFunc) {
	c.PushObject()

	for name, fn := range members {
		fn := fn
		c.PushGoFunction(func(lc *duktape.Context) int {
			return fn(r, lc, ch)
		})
		c.PutPropString(-2, name)
	}
}

func loadFile(c *duktape.Context, name, src string) error {
	c.PushString(name)
	if err := c.PcompileStringFilename(0, src); err != nil {
		return err
	}
	c.Pcall(0)
	c.Pop()
	return nil
}
<|endoftext|>


<commit_before>package runtime

import (
	"math"
)

// 同时容纳int和引用
type Slot struct {
	number int32
	ref    *Object
}

// 局部变量表
type LocalVars []Slot

func newLocalVars(maxLocals uint) LocalVars {
	if maxLocals > 0 {
		return make([]Slot, maxLocals)
	}
	return nil
}

// 设置整形
func (lv LocalVars) SetInt(index uint, value int32) {
	lv[index].number = value
}

func (lv LocalVars) GetInt(index uint) int32 {
	return lv[index].number
}

func (lv LocalVars) SetFloat(index uint, value float32) {
	bits := math.Float32bits(value)
	lv[index].number = int32(bits)
}

func (lv LocalVars) GetFloat(index uint) float32 {
	bits := uint32(lv[index].number)
	return math.Float32frombits(bits)
}

// 设置长整形，需要两个int的空间
func (lv LocalVars) SetLong(index uint, value int64) {
	lv[index].number = int32(value)
	lv[index + 1].number = int32(value >> 32)
}

// 得到长整型
func (lv LocalVars) GetLong(index uint) int64 {
	low := uint32(lv[index].number)
	high := uint32(lv[index + 1].number)
	return (int64(high) << 32) | int64(low)
}

// 写入64位浮点
func (lv LocalVars) SetDouble(index uint, value float64) {
	bits := math.Float64bits(value)
	lv.SetLong(index, int64(bits))
}

// 得到浮点数
func (lv LocalVars) GetDouble(index uint) float64 {
	bits := uint64(lv.GetLong(index))
	return math.Float64frombits(bits)
}

<commit_msg>增加局部变量中，对于引用的设置和获取接口<commit_after>package runtime

import (
	"math"
)

// 同时容纳int和引用
type Slot struct {
	number int32
	ref    *Object
}

// 局部变量表
type LocalVars []Slot

func newLocalVars(maxLocals uint) LocalVars {
	if maxLocals > 0 {
		return make([]Slot, maxLocals)
	}
	return nil
}

// 设置整形
func (lv LocalVars) SetInt(index uint, value int32) {
	lv[index].number = value
}

func (lv LocalVars) GetInt(index uint) int32 {
	return lv[index].number
}

func (lv LocalVars) SetFloat(index uint, value float32) {
	bits := math.Float32bits(value)
	lv[index].number = int32(bits)
}

func (lv LocalVars) GetFloat(index uint) float32 {
	bits := uint32(lv[index].number)
	return math.Float32frombits(bits)
}

// 设置长整形，需要两个int的空间
func (lv LocalVars) SetLong(index uint, value int64) {
	lv[index].number = int32(value)
	lv[index + 1].number = int32(value >> 32)
}

// 得到长整型
func (lv LocalVars) GetLong(index uint) int64 {
	low := uint32(lv[index].number)
	high := uint32(lv[index + 1].number)
	return (int64(high) << 32) | int64(low)
}

// 写入64位浮点
func (lv LocalVars) SetDouble(index uint, value float64) {
	bits := math.Float64bits(value)
	lv.SetLong(index, int64(bits))
}

// 得到浮点数
func (lv LocalVars) GetDouble(index uint) float64 {
	bits := uint64(lv.GetLong(index))
	return math.Float64frombits(bits)
}

// 设置引用值
func (lv LocalVars) SetRef(index uint, obj *Object) {
	lv[index].ref = obj
}

// 获取引用值
func (lv LocalVars) GetRef(index uint) *Object {
	return lv[index].ref
}<|endoftext|>