<commit_before>package blobstore_test

import (
	"bytes"
	"io"
	"os"
	"path/filepath"

	. "github.com/cloudfoundry/bosh-utils/blobstore"
	boshlog "github.com/cloudfoundry/bosh-utils/logger"
	boshsys "github.com/cloudfoundry/bosh-utils/system"
	boshsysfake "github.com/cloudfoundry/bosh-utils/system/fakes"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Blob Manager", func() {
	var (
		fs       boshsys.FileSystem
		logger   boshlog.Logger
		basePath string
		blobPath string
		blobId   string
		toWrite  io.Reader
	)

	BeforeEach(func() {
		logger = boshlog.NewLogger(boshlog.LevelNone)
		fs = boshsys.NewOsFileSystem(logger)
		blobId = "105d33ae-655c-493d-bf9f-1df5cf3ca847"
		basePath = os.TempDir()
		blobPath = filepath.Join(basePath, blobId)
		toWrite = bytes.NewReader([]byte("new data"))
	})

	readFile := func(fileIO boshsys.File) []byte {
		fileStat, _ := fileIO.Stat()
		fileBytes := make([]byte, fileStat.Size())
		fileIO.Read(fileBytes)
		return fileBytes
	}

	It("fetches", func() {
		blobManager := NewBlobManager(fs, basePath)
		fs.WriteFileString(blobPath, "some data")

		readOnlyFile, err, _ := blobManager.Fetch(blobId)
		defer fs.RemoveAll(readOnlyFile.Name())

		Expect(err).ToNot(HaveOccurred())
		fileBytes := readFile(readOnlyFile)

		Expect(string(fileBytes)).To(Equal("some data"))
	})

	It("writes", func() {
		blobManager := NewBlobManager(fs, basePath)
		fs.WriteFileString(blobPath, "some data")
		defer fs.RemoveAll(blobPath)

		err := blobManager.Write(blobId, toWrite)
		Expect(err).ToNot(HaveOccurred())

		contents, err := fs.ReadFileString(blobPath)
		Expect(err).ToNot(HaveOccurred())
		Expect(contents).To(Equal("new data"))
	})

	Context("when it writes", func() {
		BeforeEach(func() {
			basePath = filepath.ToSlash(basePath)
			blobPath = filepath.ToSlash(blobPath)
		})

		It("creates and closes the file", func() {
			fs_ := boshsysfake.NewFakeFileSystem()
			blobManager := NewBlobManager(fs_, basePath)
			err := blobManager.Write(blobId, toWrite)
			Expect(err).ToNot(HaveOccurred())
			fileStats, err := fs_.FindFileStats(blobPath)
			Expect(err).ToNot(HaveOccurred())
			Expect(fileStats.Open).To(BeFalse())
		})

		It("creates file with correct permissions", func() {
			fs_ := boshsysfake.NewFakeFileSystem()
			blobManager := NewBlobManager(fs_, basePath)
			err := blobManager.Write(blobId, toWrite)
			fileStats, err := fs_.FindFileStats(blobPath)
			Expect(err).ToNot(HaveOccurred())
			Expect(fileStats.FileMode).To(Equal(os.FileMode(0666)))
			Expect(fileStats.Flags).To(Equal(os.O_WRONLY | os.O_CREATE | os.O_TRUNC))
		})
	})

	Context("GetPath", func() {
		Describe("when file requested does not exist in blobsPath", func() {
			It("returns an error", func() {
				blobManager := NewBlobManager(fs, basePath)

				_, err := blobManager.GetPath(blobId)

				Expect(err).ToNot(BeNil())
				Expect(err.Error()).To(Equal("blob not found"))
			})
		})

		Describe("when file requested exists in blobsPath", func() {
			It("should return the path of a copy of the requested blob", func() {
				blobManager := NewBlobManager(fs, basePath)

				err := fs.WriteFileString(blobPath, "smurf-content-hello")
				Expect(err).To(BeNil())

				filename, err := blobManager.GetPath(blobId)
				Expect(err).To(BeNil())
				Expect(fs.ReadFileString(filename)).To(Equal("smurf-content-hello"))
				Expect(filename).ToNot(Equal(filepath.Join(blobPath, blobId)))
			})
		})
	})

	Context("Delete", func() {
		Describe("when file to be deleted does not exist in blobsPath", func() {
			It("does not freak out", func() {
				blobManager := NewBlobManager(fs, basePath)

				err := blobManager.Delete(blobId)

				Expect(err).To(BeNil())
			})
		})

		Describe("when file to be deleted exists in blobsPath", func() {
			It("should delete the blob", func() {
				blobManager := NewBlobManager(fs, basePath)

				err := fs.WriteFileString(blobPath, "smurf-content-hello")
				Expect(err).To(BeNil())

				err = blobManager.Delete(blobId)

				Expect(err).To(BeNil())
				Expect(fs.FileExists(blobPath)).To(BeFalse())
			})
		})
	})
})
<commit_msg>Remove temp files created in test<commit_after>package blobstore_test

import (
	"bytes"
	"io"
	"os"
	"path/filepath"

	. "github.com/cloudfoundry/bosh-utils/blobstore"
	boshlog "github.com/cloudfoundry/bosh-utils/logger"
	boshsys "github.com/cloudfoundry/bosh-utils/system"
	boshsysfake "github.com/cloudfoundry/bosh-utils/system/fakes"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Blob Manager", func() {
	var (
		fs       boshsys.FileSystem
		logger   boshlog.Logger
		basePath string
		blobPath string
		blobId   string
		toWrite  io.Reader
	)

	BeforeEach(func() {
		logger = boshlog.NewLogger(boshlog.LevelNone)
		fs = boshsys.NewOsFileSystem(logger)
		blobId = "105d33ae-655c-493d-bf9f-1df5cf3ca847"
		basePath = os.TempDir()
		blobPath = filepath.Join(basePath, blobId)
		toWrite = bytes.NewReader([]byte("new data"))
	})

	readFile := func(fileIO boshsys.File) []byte {
		fileStat, _ := fileIO.Stat()
		fileBytes := make([]byte, fileStat.Size())
		fileIO.Read(fileBytes)
		return fileBytes
	}

	It("fetches", func() {
		blobManager := NewBlobManager(fs, basePath)
		fs.WriteFileString(blobPath, "some data")

		readOnlyFile, err, _ := blobManager.Fetch(blobId)
		defer fs.RemoveAll(readOnlyFile.Name())

		Expect(err).ToNot(HaveOccurred())
		fileBytes := readFile(readOnlyFile)

		Expect(string(fileBytes)).To(Equal("some data"))
	})

	It("writes", func() {
		blobManager := NewBlobManager(fs, basePath)
		fs.WriteFileString(blobPath, "some data")
		defer fs.RemoveAll(blobPath)

		err := blobManager.Write(blobId, toWrite)
		Expect(err).ToNot(HaveOccurred())

		contents, err := fs.ReadFileString(blobPath)
		Expect(err).ToNot(HaveOccurred())
		Expect(contents).To(Equal("new data"))
	})

	Context("when it writes", func() {
		BeforeEach(func() {
			basePath = filepath.ToSlash(basePath)
			blobPath = filepath.ToSlash(blobPath)
		})

		It("creates and closes the file", func() {
			fs_ := boshsysfake.NewFakeFileSystem()
			blobManager := NewBlobManager(fs_, basePath)
			err := blobManager.Write(blobId, toWrite)
			Expect(err).ToNot(HaveOccurred())
			fileStats, err := fs_.FindFileStats(blobPath)
			Expect(err).ToNot(HaveOccurred())
			Expect(fileStats.Open).To(BeFalse())
		})

		It("creates file with correct permissions", func() {
			fs_ := boshsysfake.NewFakeFileSystem()
			blobManager := NewBlobManager(fs_, basePath)
			err := blobManager.Write(blobId, toWrite)
			fileStats, err := fs_.FindFileStats(blobPath)
			Expect(err).ToNot(HaveOccurred())
			Expect(fileStats.FileMode).To(Equal(os.FileMode(0666)))
			Expect(fileStats.Flags).To(Equal(os.O_WRONLY | os.O_CREATE | os.O_TRUNC))
		})
	})

	Context("GetPath", func() {
		Describe("when file requested does not exist in blobsPath", func() {
			It("returns an error", func() {
				blobManager := NewBlobManager(fs, basePath)

				_, err := blobManager.GetPath(blobId)

				Expect(err).ToNot(BeNil())
				Expect(err.Error()).To(Equal("blob not found"))
			})
		})

		Describe("when file requested exists in blobsPath", func() {
			It("should return the path of a copy of the requested blob", func() {
				blobManager := NewBlobManager(fs, basePath)

				err := fs.WriteFileString(blobPath, "smurf-content-hello")
				defer fs.RemoveAll(blobPath)

				Expect(err).To(BeNil())

				filename, err := blobManager.GetPath(blobId)
				Expect(err).To(BeNil())
				Expect(fs.ReadFileString(filename)).To(Equal("smurf-content-hello"))
				Expect(filename).ToNot(Equal(filepath.Join(blobPath, blobId)))
			})
		})
	})

	Context("Delete", func() {
		Describe("when file to be deleted does not exist in blobsPath", func() {
			It("does not freak out", func() {
				blobManager := NewBlobManager(fs, basePath)

				err := blobManager.Delete(blobId)

				Expect(err).To(BeNil())
			})
		})

		Describe("when file to be deleted exists in blobsPath", func() {
			It("should delete the blob", func() {
				blobManager := NewBlobManager(fs, basePath)

				err := fs.WriteFileString(blobPath, "smurf-content-hello")
				defer fs.RemoveAll(blobPath)

				Expect(err).To(BeNil())

				err = blobManager.Delete(blobId)

				Expect(err).To(BeNil())
				Expect(fs.FileExists(blobPath)).To(BeFalse())
			})
		})
	})
})
<|endoftext|>


<commit_before>package bitsgo

import (
	"bytes"
	"crypto/sha1"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"github.com/petergtz/bitsgo/logger"
)

type MetricsService interface {
	SendTimingMetric(name string, duration time.Duration)
}

type StateForbiddenError struct {
	error
}

func NewStateForbiddenError() *StateForbiddenError {
	return &StateForbiddenError{fmt.Errorf("StateForbiddenError")}
}

type Updater interface {
	NotifyProcessingUpload(guid string) error
	NotifyUploadSucceeded(guid string, sha1 string, sha2 string) error
	NotifyUploadFailed(guid string, e error) error
}

type NullUpdater struct{}

func (u *NullUpdater) NotifyProcessingUpload(guid string) error                          { return nil }
func (u *NullUpdater) NotifyUploadSucceeded(guid string, sha1 string, sha2 string) error { return nil }
func (u *NullUpdater) NotifyUploadFailed(guid string, e error) error                     { return nil }

type ResourceHandler struct {
	blobstore        Blobstore
	resourceType     string
	metricsService   MetricsService
	maxBodySizeLimit uint64
	updater          Updater
}

type responseBody struct {
	Guid      string    `json:"guid"`
	State     string    `json:"state"`
	Type      string    `json:"type"`
	CreatedAt time.Time `json:"created_at"`
}

func NewResourceHandler(blobstore Blobstore, resourceType string, metricsService MetricsService, maxBodySizeLimit uint64) *ResourceHandler {
	return &ResourceHandler{
		blobstore:        blobstore,
		resourceType:     resourceType,
		metricsService:   metricsService,
		maxBodySizeLimit: maxBodySizeLimit,
		updater:          &NullUpdater{},
	}
}

func NewResourceHandlerWithUpdater(blobstore Blobstore, updater Updater, resourceType string, metricsService MetricsService, maxBodySizeLimit uint64) *ResourceHandler {
	return &ResourceHandler{
		blobstore:        blobstore,
		resourceType:     resourceType,
		metricsService:   metricsService,
		maxBodySizeLimit: maxBodySizeLimit,
		updater:          updater,
	}
}

// TODO: instead of params, we could use `identifier string` to make the interface more type-safe.
//       Here and in the other methods.
func (handler *ResourceHandler) AddOrReplaceWithDigestInHeader(responseWriter http.ResponseWriter, request *http.Request, params map[string]string) {
	if !HandleBodySizeLimits(responseWriter, request, handler.maxBodySizeLimit) {
		return
	}
	logger.From(request).Debugw("Octet-Stream")

	digest := request.Header.Get("Digest")
	if digest == "" {
		badRequest(responseWriter, "No Digest header")
		return
	}
	parts := strings.Split(digest, "=")
	if len(parts) != 2 {
		badRequest(responseWriter, "Digest must have format sha256=value, but is "+digest)
		return
	}
	alg, value := strings.ToLower(parts[0]), parts[1]
	if alg != "sha256" {
		badRequest(responseWriter, "Digest must have format sha256=value, but is "+digest)
		return
	}
	if value == "" {
		badRequest(responseWriter, "Digest must have format sha256=value. Value cannot be empty")
		return
	}

	// TODO this can cause an out of memory panic. Should be smart about writing big files to disk instead.
	content, e := ioutil.ReadAll(request.Body)
	if e != nil {
		internalServerError(responseWriter, e)
		return
	}
	e = handler.blobstore.Put(params["identifier"]+"/"+value, bytes.NewReader(content))
	// TODO use Clock instead:
	writeResponseBasedOn("", e, responseWriter, http.StatusCreated, nil, &responseBody{Guid: params["identifier"], State: "READY", Type: "bits", CreatedAt: time.Now()}, "")
}

// TODO: instead of params, we could use `identifier string` to make the interface more type-safe.
//       Here and in the other methods.
func (handler *ResourceHandler) AddOrReplace(responseWriter http.ResponseWriter, request *http.Request, params map[string]string) {
	if !HandleBodySizeLimits(responseWriter, request, handler.maxBodySizeLimit) {
		return
	}
	if strings.Contains(request.Header.Get("Content-Type"), "multipart/form-data") {
		logger.From(request).Debugw("Multipart upload")
		handler.uploadMultipart(responseWriter, request, params["identifier"])
	} else {
		logger.From(request).Debugw("Copy source guid")
		handler.copySourceGuid(responseWriter, request, params["identifier"])
	}
}

func (handler *ResourceHandler) uploadMultipart(responseWriter http.ResponseWriter, request *http.Request, identifier string) {
	file, _, e := request.FormFile(handler.resourceType)
	if e == http.ErrMissingFile {
		file, _, e = request.FormFile("bits")
	}
	if e == http.ErrMissingFile {
		badRequest(responseWriter, "Could not retrieve form parameter '%s' or 'bits", handler.resourceType)
		return
	}
	if e != nil {
		internalServerError(responseWriter, e)
		return
	}
	defer file.Close()

	e = handler.updater.NotifyProcessingUpload(identifier)
	if handleNotificationError(e, responseWriter) {
		return
	}
	buffer, e := ioutil.ReadAll(file)
	if e != nil {
		internalServerError(responseWriter, e)
		return
	}

	startTime := time.Now()
	e = handler.blobstore.Put(identifier, bytes.NewReader(buffer))
	handler.metricsService.SendTimingMetric(handler.resourceType+"-cp_to_blobstore-time", time.Since(startTime))

	if e != nil {
		logger.From(request).Infow("Failed to upload to blobstore.", "error", e)
		notifyErr := handler.updater.NotifyUploadFailed(identifier, e)
		if notifyErr != nil {
			logger.From(request).Errorw("Failed to notifying CC about failed upload.", "error", notifyErr)
		}
		if _, noSpaceLeft := e.(*NoSpaceLeftError); noSpaceLeft {
			responseWriter.WriteHeader(http.StatusInsufficientStorage)
			return
		}
		internalServerError(responseWriter, e)
		return
	}
	sha1, sha256 := sha1.Sum(buffer), sha256.Sum256(buffer)
	e = handler.updater.NotifyUploadSucceeded(identifier, hex.EncodeToString(sha1[:]), hex.EncodeToString(sha256[:]))
	if e != nil {
		internalServerError(responseWriter, e)
		return
	}

	writeResponseBasedOn("", nil, responseWriter, http.StatusCreated, nil, &responseBody{Guid: identifier, State: "READY", Type: "bits", CreatedAt: time.Now()}, "")
}

func handleNotificationError(e error, responseWriter http.ResponseWriter) (wasError bool) {
	switch e.(type) {
	case *StateForbiddenError:
		responseWriter.WriteHeader(http.StatusBadRequest)
		fprintDescriptionAndCodeAsJSON(responseWriter, "290008", "Cannot update an existing package.")
		return true
	case *NotFoundError:
		responseWriter.WriteHeader(http.StatusNotFound)
		fprintDescriptionAndCodeAsJSON(responseWriter, "10010", e.Error())
		return true
	case error:
		internalServerError(responseWriter, e)
		return true
	}
	return false
}

func (handler *ResourceHandler) copySourceGuid(responseWriter http.ResponseWriter, request *http.Request, identifier string) {
	sourceGuid := sourceGuidFrom(request.Body, responseWriter)
	if sourceGuid == "" {
		return // response is already handled in sourceGuidFrom
	}
	e := handler.blobstore.Copy(sourceGuid, identifier)
	// TODO use Clock instead:
	writeResponseBasedOn("", e, responseWriter, http.StatusCreated, nil, &responseBody{Guid: identifier, State: "READY", Type: "bits", CreatedAt: time.Now()}, "")
}

func sourceGuidFrom(body io.ReadCloser, responseWriter http.ResponseWriter) string {
	content, e := ioutil.ReadAll(body)
	if e != nil {
		internalServerError(responseWriter, e)
		return ""
	}
	var payload struct {
		SourceGuid string `json:"source_guid"`
	}
	e = json.Unmarshal(content, &payload)
	if e != nil {
		badRequest(responseWriter, "Body must be valid JSON when request is not multipart/form-data. %+v", e)
		return ""
	}
	return payload.SourceGuid
}

func (handler *ResourceHandler) HeadOrRedirectAsGet(responseWriter http.ResponseWriter, request *http.Request, params map[string]string) {
	redirectLocation, e := handler.blobstore.HeadOrRedirectAsGet(params["identifier"])
	writeResponseBasedOn(redirectLocation, e, responseWriter, http.StatusOK, nil, nil, "")
}

func (handler *ResourceHandler) Get(responseWriter http.ResponseWriter, request *http.Request, params map[string]string) {
	body, redirectLocation, e := handler.blobstore.GetOrRedirect(params["identifier"])
	writeResponseBasedOn(redirectLocation, e, responseWriter, http.StatusOK, body, nil, request.Header.Get("If-None-Modify"))
}

func (handler *ResourceHandler) Delete(responseWriter http.ResponseWriter, request *http.Request, params map[string]string) {
	// TODO nothing should be S3 specific here
	// this check is needed, because S3 does not return a NotFound on a Delete request:
	exists, e := handler.blobstore.Exists(params["identifier"])
	if e != nil {
		internalServerError(responseWriter, e)
		return
	}
	if !exists {
		responseWriter.WriteHeader(http.StatusNotFound)
		return
	}
	e = handler.blobstore.Delete(params["identifier"])
	writeResponseBasedOn("", e, responseWriter, http.StatusNoContent, nil, nil, "")
}

func (handler *ResourceHandler) DeleteDir(responseWriter http.ResponseWriter, request *http.Request, params map[string]string) {
	e := handler.blobstore.DeleteDir(params["identifier"])
	switch e.(type) {
	case *NotFoundError:
		responseWriter.WriteHeader(http.StatusNoContent)
		return
	}
	writeResponseBasedOn("", e, responseWriter, http.StatusNoContent, nil, nil, "")
}

var emptyReader = ioutil.NopCloser(bytes.NewReader(nil))

func writeResponseBasedOn(redirectLocation string, e error, responseWriter http.ResponseWriter, statusCode int, body io.ReadCloser, jsonBody *responseBody, ifNoneModify string) {
	switch e.(type) {
	case *NotFoundError:
		responseWriter.WriteHeader(http.StatusNotFound)
		return
	case *NoSpaceLeftError:
		responseWriter.WriteHeader(http.StatusInsufficientStorage)
		return
	case error:
		internalServerError(responseWriter, e)
		return
	}
	if redirectLocation != "" {
		redirect(responseWriter, redirectLocation)
		return
	}
	if body != nil {
		defer body.Close()
		var buffer bytes.Buffer
		sha := sha1.New()
		_, e := io.Copy(io.MultiWriter(&buffer, sha), body)
		if e != nil {
			internalServerError(responseWriter, e)
			return
		}
		eTag := hex.EncodeToString(sha.Sum(nil))
		logger.Log.Debugw("Cache check", "if-none-modify", ifNoneModify, "etag", eTag)
		if ifNoneModify == eTag {
			responseWriter.WriteHeader(http.StatusNotModified)
			responseWriter.Header().Set("ETag", eTag)
			return
		}
		responseWriter.WriteHeader(statusCode)
		responseWriter.Header().Set("ETag", eTag)
		io.Copy(responseWriter, &buffer)
		return
	}
	if jsonBody != nil {
		respBody, marshallingErr := json.Marshal(jsonBody)
		if marshallingErr != nil {
			internalServerError(responseWriter, marshallingErr)
			return
		}
		responseWriter.WriteHeader(statusCode)
		responseWriter.Write(respBody)
		return
	}
	responseWriter.WriteHeader(statusCode)
}

func redirect(responseWriter http.ResponseWriter, redirectLocation string) {
	responseWriter.Header().Set("Location", redirectLocation)
	responseWriter.WriteHeader(http.StatusFound)
}

func internalServerError(responseWriter http.ResponseWriter, e error) {
	logger.Log.Errorw("Internal Server Error.", "error", fmt.Sprintf("%+v", e))
	responseWriter.WriteHeader(http.StatusInternalServerError)
}

func badRequest(responseWriter http.ResponseWriter, message string, args ...interface{}) {
	responseBody := fmt.Sprintf(message, args...)
	logger.Log.Infow("Bad request", "body", responseBody)
	responseWriter.WriteHeader(http.StatusBadRequest)
	fprintDescriptionAndCodeAsJSON(responseWriter, "290003", message, args...)
}

func fprintDescriptionAndCodeAsJSON(responseWriter http.ResponseWriter, code string, description string, a ...interface{}) {
	fmt.Fprintf(responseWriter, `{"description":"%v","code":%v}`, fmt.Sprintf(description, a...), code)
}
<commit_msg>Fix ETag headers in ResourceHandler<commit_after>package bitsgo

import (
	"bytes"
	"crypto/sha1"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"github.com/petergtz/bitsgo/logger"
)

type MetricsService interface {
	SendTimingMetric(name string, duration time.Duration)
}

type StateForbiddenError struct {
	error
}

func NewStateForbiddenError() *StateForbiddenError {
	return &StateForbiddenError{fmt.Errorf("StateForbiddenError")}
}

type Updater interface {
	NotifyProcessingUpload(guid string) error
	NotifyUploadSucceeded(guid string, sha1 string, sha2 string) error
	NotifyUploadFailed(guid string, e error) error
}

type NullUpdater struct{}

func (u *NullUpdater) NotifyProcessingUpload(guid string) error                          { return nil }
func (u *NullUpdater) NotifyUploadSucceeded(guid string, sha1 string, sha2 string) error { return nil }
func (u *NullUpdater) NotifyUploadFailed(guid string, e error) error                     { return nil }

type ResourceHandler struct {
	blobstore        Blobstore
	resourceType     string
	metricsService   MetricsService
	maxBodySizeLimit uint64
	updater          Updater
}

type responseBody struct {
	Guid      string    `json:"guid"`
	State     string    `json:"state"`
	Type      string    `json:"type"`
	CreatedAt time.Time `json:"created_at"`
}

func NewResourceHandler(blobstore Blobstore, resourceType string, metricsService MetricsService, maxBodySizeLimit uint64) *ResourceHandler {
	return &ResourceHandler{
		blobstore:        blobstore,
		resourceType:     resourceType,
		metricsService:   metricsService,
		maxBodySizeLimit: maxBodySizeLimit,
		updater:          &NullUpdater{},
	}
}

func NewResourceHandlerWithUpdater(blobstore Blobstore, updater Updater, resourceType string, metricsService MetricsService, maxBodySizeLimit uint64) *ResourceHandler {
	return &ResourceHandler{
		blobstore:        blobstore,
		resourceType:     resourceType,
		metricsService:   metricsService,
		maxBodySizeLimit: maxBodySizeLimit,
		updater:          updater,
	}
}

// TODO: instead of params, we could use `identifier string` to make the interface more type-safe.
//       Here and in the other methods.
func (handler *ResourceHandler) AddOrReplaceWithDigestInHeader(responseWriter http.ResponseWriter, request *http.Request, params map[string]string) {
	if !HandleBodySizeLimits(responseWriter, request, handler.maxBodySizeLimit) {
		return
	}
	logger.From(request).Debugw("Octet-Stream")

	digest := request.Header.Get("Digest")
	if digest == "" {
		badRequest(responseWriter, "No Digest header")
		return
	}
	parts := strings.Split(digest, "=")
	if len(parts) != 2 {
		badRequest(responseWriter, "Digest must have format sha256=value, but is "+digest)
		return
	}
	alg, value := strings.ToLower(parts[0]), parts[1]
	if alg != "sha256" {
		badRequest(responseWriter, "Digest must have format sha256=value, but is "+digest)
		return
	}
	if value == "" {
		badRequest(responseWriter, "Digest must have format sha256=value. Value cannot be empty")
		return
	}

	// TODO this can cause an out of memory panic. Should be smart about writing big files to disk instead.
	content, e := ioutil.ReadAll(request.Body)
	if e != nil {
		internalServerError(responseWriter, e)
		return
	}
	e = handler.blobstore.Put(params["identifier"]+"/"+value, bytes.NewReader(content))
	// TODO use Clock instead:
	writeResponseBasedOn("", e, responseWriter, http.StatusCreated, nil, &responseBody{Guid: params["identifier"], State: "READY", Type: "bits", CreatedAt: time.Now()}, "")
}

// TODO: instead of params, we could use `identifier string` to make the interface more type-safe.
//       Here and in the other methods.
func (handler *ResourceHandler) AddOrReplace(responseWriter http.ResponseWriter, request *http.Request, params map[string]string) {
	if !HandleBodySizeLimits(responseWriter, request, handler.maxBodySizeLimit) {
		return
	}
	if strings.Contains(request.Header.Get("Content-Type"), "multipart/form-data") {
		logger.From(request).Debugw("Multipart upload")
		handler.uploadMultipart(responseWriter, request, params["identifier"])
	} else {
		logger.From(request).Debugw("Copy source guid")
		handler.copySourceGuid(responseWriter, request, params["identifier"])
	}
}

func (handler *ResourceHandler) uploadMultipart(responseWriter http.ResponseWriter, request *http.Request, identifier string) {
	file, _, e := request.FormFile(handler.resourceType)
	if e == http.ErrMissingFile {
		file, _, e = request.FormFile("bits")
	}
	if e == http.ErrMissingFile {
		badRequest(responseWriter, "Could not retrieve form parameter '%s' or 'bits", handler.resourceType)
		return
	}
	if e != nil {
		internalServerError(responseWriter, e)
		return
	}
	defer file.Close()

	e = handler.updater.NotifyProcessingUpload(identifier)
	if handleNotificationError(e, responseWriter) {
		return
	}
	buffer, e := ioutil.ReadAll(file)
	if e != nil {
		internalServerError(responseWriter, e)
		return
	}

	startTime := time.Now()
	e = handler.blobstore.Put(identifier, bytes.NewReader(buffer))
	handler.metricsService.SendTimingMetric(handler.resourceType+"-cp_to_blobstore-time", time.Since(startTime))

	if e != nil {
		logger.From(request).Infow("Failed to upload to blobstore.", "error", e)
		notifyErr := handler.updater.NotifyUploadFailed(identifier, e)
		if notifyErr != nil {
			logger.From(request).Errorw("Failed to notifying CC about failed upload.", "error", notifyErr)
		}
		if _, noSpaceLeft := e.(*NoSpaceLeftError); noSpaceLeft {
			responseWriter.WriteHeader(http.StatusInsufficientStorage)
			return
		}
		internalServerError(responseWriter, e)
		return
	}
	sha1, sha256 := sha1.Sum(buffer), sha256.Sum256(buffer)
	e = handler.updater.NotifyUploadSucceeded(identifier, hex.EncodeToString(sha1[:]), hex.EncodeToString(sha256[:]))
	if e != nil {
		internalServerError(responseWriter, e)
		return
	}

	writeResponseBasedOn("", nil, responseWriter, http.StatusCreated, nil, &responseBody{Guid: identifier, State: "READY", Type: "bits", CreatedAt: time.Now()}, "")
}

func handleNotificationError(e error, responseWriter http.ResponseWriter) (wasError bool) {
	switch e.(type) {
	case *StateForbiddenError:
		responseWriter.WriteHeader(http.StatusBadRequest)
		fprintDescriptionAndCodeAsJSON(responseWriter, "290008", "Cannot update an existing package.")
		return true
	case *NotFoundError:
		responseWriter.WriteHeader(http.StatusNotFound)
		fprintDescriptionAndCodeAsJSON(responseWriter, "10010", e.Error())
		return true
	case error:
		internalServerError(responseWriter, e)
		return true
	}
	return false
}

func (handler *ResourceHandler) copySourceGuid(responseWriter http.ResponseWriter, request *http.Request, identifier string) {
	sourceGuid := sourceGuidFrom(request.Body, responseWriter)
	if sourceGuid == "" {
		return // response is already handled in sourceGuidFrom
	}
	e := handler.blobstore.Copy(sourceGuid, identifier)
	// TODO use Clock instead:
	writeResponseBasedOn("", e, responseWriter, http.StatusCreated, nil, &responseBody{Guid: identifier, State: "READY", Type: "bits", CreatedAt: time.Now()}, "")
}

func sourceGuidFrom(body io.ReadCloser, responseWriter http.ResponseWriter) string {
	content, e := ioutil.ReadAll(body)
	if e != nil {
		internalServerError(responseWriter, e)
		return ""
	}
	var payload struct {
		SourceGuid string `json:"source_guid"`
	}
	e = json.Unmarshal(content, &payload)
	if e != nil {
		badRequest(responseWriter, "Body must be valid JSON when request is not multipart/form-data. %+v", e)
		return ""
	}
	return payload.SourceGuid
}

func (handler *ResourceHandler) HeadOrRedirectAsGet(responseWriter http.ResponseWriter, request *http.Request, params map[string]string) {
	redirectLocation, e := handler.blobstore.HeadOrRedirectAsGet(params["identifier"])
	writeResponseBasedOn(redirectLocation, e, responseWriter, http.StatusOK, nil, nil, "")
}

func (handler *ResourceHandler) Get(responseWriter http.ResponseWriter, request *http.Request, params map[string]string) {
	body, redirectLocation, e := handler.blobstore.GetOrRedirect(params["identifier"])
	writeResponseBasedOn(redirectLocation, e, responseWriter, http.StatusOK, body, nil, request.Header.Get("If-None-Modify"))
}

func (handler *ResourceHandler) Delete(responseWriter http.ResponseWriter, request *http.Request, params map[string]string) {
	// TODO nothing should be S3 specific here
	// this check is needed, because S3 does not return a NotFound on a Delete request:
	exists, e := handler.blobstore.Exists(params["identifier"])
	if e != nil {
		internalServerError(responseWriter, e)
		return
	}
	if !exists {
		responseWriter.WriteHeader(http.StatusNotFound)
		return
	}
	e = handler.blobstore.Delete(params["identifier"])
	writeResponseBasedOn("", e, responseWriter, http.StatusNoContent, nil, nil, "")
}

func (handler *ResourceHandler) DeleteDir(responseWriter http.ResponseWriter, request *http.Request, params map[string]string) {
	e := handler.blobstore.DeleteDir(params["identifier"])
	switch e.(type) {
	case *NotFoundError:
		responseWriter.WriteHeader(http.StatusNoContent)
		return
	}
	writeResponseBasedOn("", e, responseWriter, http.StatusNoContent, nil, nil, "")
}

var emptyReader = ioutil.NopCloser(bytes.NewReader(nil))

func writeResponseBasedOn(redirectLocation string, e error, responseWriter http.ResponseWriter, statusCode int, body io.ReadCloser, jsonBody *responseBody, ifNoneModify string) {
	switch e.(type) {
	case *NotFoundError:
		responseWriter.WriteHeader(http.StatusNotFound)
		return
	case *NoSpaceLeftError:
		responseWriter.WriteHeader(http.StatusInsufficientStorage)
		return
	case error:
		internalServerError(responseWriter, e)
		return
	}
	if redirectLocation != "" {
		redirect(responseWriter, redirectLocation)
		return
	}
	if body != nil {
		defer body.Close()
		var buffer bytes.Buffer
		sha := sha1.New()
		_, e := io.Copy(io.MultiWriter(&buffer, sha), body)
		if e != nil {
			internalServerError(responseWriter, e)
			return
		}
		eTag := hex.EncodeToString(sha.Sum(nil))
		logger.Log.Debugw("Cache check", "if-none-modify", ifNoneModify, "etag", eTag)
		responseWriter.Header().Set("ETag", eTag)
		if ifNoneModify == eTag {
			responseWriter.WriteHeader(http.StatusNotModified)
			return
		}
		responseWriter.WriteHeader(statusCode)
		io.Copy(responseWriter, &buffer)
		return
	}
	if jsonBody != nil {
		respBody, marshallingErr := json.Marshal(jsonBody)
		if marshallingErr != nil {
			internalServerError(responseWriter, marshallingErr)
			return
		}
		responseWriter.WriteHeader(statusCode)
		responseWriter.Write(respBody)
		return
	}
	responseWriter.WriteHeader(statusCode)
}

func redirect(responseWriter http.ResponseWriter, redirectLocation string) {
	responseWriter.Header().Set("Location", redirectLocation)
	responseWriter.WriteHeader(http.StatusFound)
}

func internalServerError(responseWriter http.ResponseWriter, e error) {
	logger.Log.Errorw("Internal Server Error.", "error", fmt.Sprintf("%+v", e))
	responseWriter.WriteHeader(http.StatusInternalServerError)
}

func badRequest(responseWriter http.ResponseWriter, message string, args ...interface{}) {
	responseBody := fmt.Sprintf(message, args...)
	logger.Log.Infow("Bad request", "body", responseBody)
	responseWriter.WriteHeader(http.StatusBadRequest)
	fprintDescriptionAndCodeAsJSON(responseWriter, "290003", message, args...)
}

func fprintDescriptionAndCodeAsJSON(responseWriter http.ResponseWriter, code string, description string, a ...interface{}) {
	fmt.Fprintf(responseWriter, `{"description":"%v","code":%v}`, fmt.Sprintf(description, a...), code)
}
<|endoftext|>


<commit_before>package boltdbboilerplate

import (
	"log"
	"os"
	"time"

	"github.com/boltdb/bolt"
)

var database string
var fileMode os.FileMode = 0600 // owner can read and write
var db *bolt.DB

// InitBolt inits bolt database. Create the file if not exist.
func InitBolt(path string, buckets []string) error {
	log.Println("Trying to open databse")
	database = path
	var err error
	// open the target file, file mode fileMode, and a 10 seconds timeout period
	db, err = bolt.Open(database, fileMode, &bolt.Options{Timeout: 10 * time.Second})
	if err != nil {
		log.Fatal(err)
	}

	log.Println("Trying to create buckets")
	err = db.Update(func(tx *bolt.Tx) error {
		for _, value := range buckets {
			_, err := tx.CreateBucketIfNotExists([]byte(value))
			if err != nil {
				return err
			}
		}

		return nil
	})

	log.Println("Trying to return error")
	return err
}

// Close bolt db
func Close() {
	db.Close()
}

// Get a []byte value from bucket
func Get(bucket []byte, key []byte) []byte {
	var value []byte

	err := db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket(bucket)
		v := b.Get(key)
		if v != nil {
			value = append(value, b.Get(key)...)
		}
		return nil
	})

	if err != nil {
		log.Fatal(err)
	}

	return value
}

// Put a key/value pair into target bucket
func Put(bucket []byte, key []byte, value []byte) error {
	err := db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket(bucket)
		err := b.Put(key, value)
		return err
	})

	return err
}

// Delete key from bucket
func Delete(bucket []byte, key []byte) error {
	err := db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket(bucket)
		err := b.Delete(key)
		return err
	})

	return err
}

// GetAllKeys get all keys from the target bucket
func GetAllKeys(bucket []byte) [][]byte {
	var keys [][]byte

	db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket(bucket)
		b.ForEach(func(k, v []byte) error {
			// Due to
			// Byte slices returned from Bolt are only valid during a transaction. Once the transaction has been committed or rolled back then the memory they point to can be reused by a new page or can be unmapped from virtual memory and you'll see an unexpected fault address panic when accessing it.
			// We copy the slice to retain it
			dst := make([]byte, len(k))
			copy(dst, k)

			keys = append(keys, dst)
			return nil
		})
		return nil
	})

	return keys
}

// BoltPair is a key/value pair
type BoltPair struct {
	Key   []byte
	Value []byte
}

// GetAllKeyValues get all key/value pairs from a bucket a BoltPair struct format
func GetAllKeyValues(bucket []byte) []BoltPair {
	var pairs []BoltPair

	db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket(bucket)
		b.ForEach(func(k, v []byte) error {
			// Due to
			// Byte slices returned from Bolt are only valid during a transaction. Once the transaction has been committed or rolled back then the memory they point to can be reused by a new page or can be unmapped from virtual memory and you'll see an unexpected fault address panic when accessing it.
			// We copy the slice to retain it
			dstk := make([]byte, len(k))
			dstv := make([]byte, len(v))
			copy(dstk, k)
			copy(dstv, v)

			pair := BoltPair{dstk, dstv}
			pairs = append(pairs, pair)
			return nil
		})

		return nil
	})

	return pairs
}
<commit_msg>Update inline code comments<commit_after>package boltdbboilerplate

import (
	"log"
	"os"
	"time"

	"github.com/boltdb/bolt"
)

var database string
var fileMode os.FileMode = 0600 // owner can read and write
var db *bolt.DB

// InitBolt inits bolt database. Create the file if not exist.
// By default it opens the file in 0600 mode, with a 10 seconds timeout period
func InitBolt(path string, buckets []string) error {
	log.Println("Trying to open databse")
	database = path
	var err error
	// open the target file, file mode fileMode, and a 10 seconds timeout period
	db, err = bolt.Open(database, fileMode, &bolt.Options{Timeout: 10 * time.Second})
	if err != nil {
		log.Fatal(err)
	}

	log.Println("Trying to create buckets")
	err = db.Update(func(tx *bolt.Tx) error {
		for _, value := range buckets {
			_, err := tx.CreateBucketIfNotExists([]byte(value))
			if err != nil {
				return err
			}
		}

		return nil
	})

	log.Println("Trying to return error")
	return err
}

// Close bolt db
func Close() {
	db.Close()
}

// Get value from bucket by key
func Get(bucket []byte, key []byte) []byte {
	var value []byte

	err := db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket(bucket)
		v := b.Get(key)
		if v != nil {
			value = append(value, b.Get(key)...)
		}
		return nil
	})

	if err != nil {
		log.Fatal(err)
	}

	return value
}

// Put a key/value pair into target bucket
func Put(bucket []byte, key []byte, value []byte) error {
	err := db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket(bucket)
		err := b.Put(key, value)
		return err
	})

	return err
}

// Delete a key from target bucket
func Delete(bucket []byte, key []byte) error {
	err := db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket(bucket)
		err := b.Delete(key)
		return err
	})

	return err
}

// GetAllKeys get all keys from the target bucket
func GetAllKeys(bucket []byte) [][]byte {
	var keys [][]byte

	db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket(bucket)
		b.ForEach(func(k, v []byte) error {
			// Due to
			// Byte slices returned from Bolt are only valid during a transaction. Once the transaction has been committed or rolled back then the memory they point to can be reused by a new page or can be unmapped from virtual memory and you'll see an unexpected fault address panic when accessing it.
			// We copy the slice to retain it
			dst := make([]byte, len(k))
			copy(dst, k)

			keys = append(keys, dst)
			return nil
		})
		return nil
	})

	return keys
}

// BoltPair is a struct to store key/value pair data
type BoltPair struct {
	Key   []byte
	Value []byte
}

// GetAllKeyValues get all key/value pairs from a bucket in BoltPair struct format
func GetAllKeyValues(bucket []byte) []BoltPair {
	var pairs []BoltPair

	db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket(bucket)
		b.ForEach(func(k, v []byte) error {
			// Due to
			// Byte slices returned from Bolt are only valid during a transaction. Once the transaction has been committed or rolled back then the memory they point to can be reused by a new page or can be unmapped from virtual memory and you'll see an unexpected fault address panic when accessing it.
			// We copy the slice to retain it
			dstk := make([]byte, len(k))
			dstv := make([]byte, len(v))
			copy(dstk, k)
			copy(dstv, v)

			pair := BoltPair{dstk, dstv}
			pairs = append(pairs, pair)
			return nil
		})

		return nil
	})

	return pairs
}
<|endoftext|>


<commit_before>package bolt

import (
	"time"

	"github.com/boltdb/bolt"
	"github.com/influxdata/chronograf"
	"github.com/influxdata/chronograf/uuid"
)

// Client is a client for the boltDB data store.
type Client struct {
	Path      string
	db        *bolt.DB
	Now       func() time.Time
	LayoutIDs chronograf.ID

	ExplorationStore *ExplorationStore
	SourcesStore     *SourcesStore
	ServersStore     *ServersStore
	LayoutStore      *LayoutStore
	UsersStore       *UsersStore
	AlertsStore      *AlertsStore
	DashboardsStore  *DashboardsStore
}

func NewClient() *Client {
	c := &Client{Now: time.Now}
	c.ExplorationStore = &ExplorationStore{client: c}
	c.SourcesStore = &SourcesStore{client: c}
	c.ServersStore = &ServersStore{client: c}
	c.AlertsStore = &AlertsStore{client: c}
	c.UsersStore = &UsersStore{client: c}
	c.LayoutStore = &LayoutStore{
		client: c,
		IDs:    &uuid.V4{},
	}
	c.DashboardsStore = &DashboardsStore{client: c}
	return c
}

// Open and initialize boltDB. Initial buckets are created if they do not exist.
func (c *Client) Open() error {
	// Open database file.
	db, err := bolt.Open(c.Path, 0600, &bolt.Options{Timeout: 1 * time.Second})
	if err != nil {
		return err
	}
	c.db = db

	if err := c.db.Update(func(tx *bolt.Tx) error {
		// Always create explorations bucket.
		if _, err := tx.CreateBucketIfNotExists(ExplorationBucket); err != nil {
			return err
		}
		// Always create Sources bucket.
		if _, err := tx.CreateBucketIfNotExists(SourcesBucket); err != nil {
			return err
		}
		// Always create Servers bucket.
		if _, err := tx.CreateBucketIfNotExists(ServersBucket); err != nil {
			return err
		}
		// Always create Layouts bucket.
		if _, err := tx.CreateBucketIfNotExists(LayoutBucket); err != nil {
			return err
		}
		// Always create Dashboards bucket.
		if _, err := tx.CreateBucketIfNotExists(DashboardsBucket); err != nil {
			return err
		}
		// Always create Alerts bucket.
		if _, err := tx.CreateBucketIfNotExists(AlertsBucket); err != nil {
			return err
		}
		// Always create Users bucket.
		if _, err := tx.CreateBucketIfNotExists(UsersBucket); err != nil {
			return err
		}
		return nil
	}); err != nil {
		return err
	}

	return nil
}

func (c *Client) Close() error {
	if c.db != nil {
		return c.db.Close()
	}
	return nil
}
<commit_msg>dashboard, not dashboards<commit_after>package bolt

import (
	"time"

	"github.com/boltdb/bolt"
	"github.com/influxdata/chronograf"
	"github.com/influxdata/chronograf/uuid"
)

// Client is a client for the boltDB data store.
type Client struct {
	Path      string
	db        *bolt.DB
	Now       func() time.Time
	LayoutIDs chronograf.ID

	ExplorationStore *ExplorationStore
	SourcesStore     *SourcesStore
	ServersStore     *ServersStore
	LayoutStore      *LayoutStore
	UsersStore       *UsersStore
	AlertsStore      *AlertsStore
	DashboardsStore  *DashboardsStore
}

func NewClient() *Client {
	c := &Client{Now: time.Now}
	c.ExplorationStore = &ExplorationStore{client: c}
	c.SourcesStore = &SourcesStore{client: c}
	c.ServersStore = &ServersStore{client: c}
	c.AlertsStore = &AlertsStore{client: c}
	c.UsersStore = &UsersStore{client: c}
	c.LayoutStore = &LayoutStore{
		client: c,
		IDs:    &uuid.V4{},
	}
	c.DashboardsStore = &DashboardsStore{client: c}
	return c
}

// Open and initialize boltDB. Initial buckets are created if they do not exist.
func (c *Client) Open() error {
	// Open database file.
	db, err := bolt.Open(c.Path, 0600, &bolt.Options{Timeout: 1 * time.Second})
	if err != nil {
		return err
	}
	c.db = db

	if err := c.db.Update(func(tx *bolt.Tx) error {
		// Always create explorations bucket.
		if _, err := tx.CreateBucketIfNotExists(ExplorationBucket); err != nil {
			return err
		}
		// Always create Sources bucket.
		if _, err := tx.CreateBucketIfNotExists(SourcesBucket); err != nil {
			return err
		}
		// Always create Servers bucket.
		if _, err := tx.CreateBucketIfNotExists(ServersBucket); err != nil {
			return err
		}
		// Always create Layouts bucket.
		if _, err := tx.CreateBucketIfNotExists(LayoutBucket); err != nil {
			return err
		}
		// Always create Dashboards bucket.
		if _, err := tx.CreateBucketIfNotExists(DashboardBucket); err != nil {
			return err
		}
		// Always create Alerts bucket.
		if _, err := tx.CreateBucketIfNotExists(AlertsBucket); err != nil {
			return err
		}
		// Always create Users bucket.
		if _, err := tx.CreateBucketIfNotExists(UsersBucket); err != nil {
			return err
		}
		return nil
	}); err != nil {
		return err
	}

	return nil
}

func (c *Client) Close() error {
	if c.db != nil {
		return c.db.Close()
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package libkbfs

import (
	"github.com/keybase/client/go/protocol/keybase1"
	"golang.org/x/net/context"
)

type fileBlockGetter func(context.Context, KeyMetadata, BlockPointer,
	path, blockReqType) (*FileBlock, error)
type dirtyBlockCacher func(ptr BlockPointer, block Block) error

// fileData is a helper struct for accessing and manipulating data
// within a file.  It's meant for use within a single scope, not for
// long-term storage.  The caller must ensure goroutine-safety.
type fileData struct {
	file   path
	uid    keybase1.UID
	crypto cryptoPure
	kmd    KeyMetadata
	getter fileBlockGetter
	cacher dirtyBlockCacher
}

func newFileData(file path, uid keybase1.UID, crypto cryptoPure,
	kmd KeyMetadata, getter fileBlockGetter,
	cacher dirtyBlockCacher) *fileData {
	return &fileData{
		file:   file,
		uid:    uid,
		crypto: crypto,
		kmd:    kmd,
		getter: getter,
		cacher: cacher,
	}
}

type parentBlockAndChildIndex struct {
	pblock     *FileBlock
	childIndex int
}

func (fd *fileData) getFileBlockAtOffset(ctx context.Context,
	topBlock *FileBlock, off int64, rtype blockReqType) (
	ptr BlockPointer, parentBlocks []parentBlockAndChildIndex,
	block *FileBlock, nextBlockStartOff, startOff int64, err error) {
	// find the block matching the offset, if it exists
	ptr = fd.file.tailPointer()
	block = topBlock
	nextBlockStartOff = -1
	startOff = 0
	// search until it's not an indirect block
	for block.IsInd {
		nextIndex := len(block.IPtrs) - 1
		for i, iptr := range block.IPtrs {
			if iptr.Off == off {
				// small optimization to avoid iterating past the right ptr
				nextIndex = i
				break
			} else if iptr.Off > off {
				// i can never be 0, because the first ptr always has
				// an offset at the beginning of the range
				nextIndex = i - 1
				break
			}
		}
		nextPtr := block.IPtrs[nextIndex]
		parentBlocks = append(parentBlocks,
			parentBlockAndChildIndex{block, nextIndex})
		startOff = nextPtr.Off
		// there is more to read if we ever took a path through a
		// ptr that wasn't the final ptr in its respective list
		if nextIndex != len(block.IPtrs)-1 {
			nextBlockStartOff = block.IPtrs[nextIndex+1].Off
		}
		ptr = nextPtr.BlockPointer
		block, err = fd.getter(ctx, fd.kmd, ptr, fd.file, rtype)
		if err != nil {
			return zeroPtr, nil, nil, 0, 0, err
		}
	}

	return ptr, parentBlocks, block, nextBlockStartOff, startOff, nil
}

// createIndirectBlock creates a new indirect block and pick a new id
// for the existing block, and use the existing block's ID for the new
// indirect block that becomes the parent.
func (fd *fileData) createIndirectBlock(
	df *dirtyFile, dver DataVer) (*FileBlock, error) {
	newID, err := fd.crypto.MakeTemporaryBlockID()
	if err != nil {
		return nil, err
	}
	fblock := &FileBlock{
		CommonBlock: CommonBlock{
			IsInd: true,
		},
		IPtrs: []IndirectFilePtr{
			{
				BlockInfo: BlockInfo{
					BlockPointer: BlockPointer{
						ID:      newID,
						KeyGen:  fd.kmd.LatestKeyGeneration(),
						DataVer: dver,
						BlockContext: BlockContext{
							Creator:  fd.uid,
							RefNonce: ZeroBlockRefNonce,
						},
					},
					EncodedSize: 0,
				},
				Off: 0,
			},
		},
	}

	// Mark the old block ID as not dirty, so that we will treat the
	// old block ID as newly dirtied in cacheBlockIfNotYetDirtyLocked.
	df.setBlockNotDirty(fd.file.tailPointer())
	err = fd.cacher(fd.file.tailPointer(), fblock)
	if err != nil {
		return nil, err
	}

	return fblock, nil
}

// newRightBlock appends a new block (with a temporary ID) to the end
// of this file.
func (fd *fileData) newRightBlock(
	ctx context.Context, ptr BlockPointer, pblock *FileBlock,
	off int64) error {
	newRID, err := fd.crypto.MakeTemporaryBlockID()
	if err != nil {
		return err
	}
	rblock := &FileBlock{}

	newPtr := BlockPointer{
		ID:      newRID,
		KeyGen:  fd.kmd.LatestKeyGeneration(),
		DataVer: DefaultNewBlockDataVersion(false),
		BlockContext: BlockContext{
			Creator:  fd.uid,
			RefNonce: ZeroBlockRefNonce,
		},
	}

	pblock.IPtrs = append(pblock.IPtrs, IndirectFilePtr{
		BlockInfo: BlockInfo{
			BlockPointer: newPtr,
			EncodedSize:  0,
		},
		Off: off,
	})

	err = fd.cacher(newPtr, rblock)
	if err != nil {
		return err
	}
	err = fd.cacher(ptr, pblock)
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>file_data: comment for parentBlockAndChildIndex<commit_after>// Copyright 2016 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package libkbfs

import (
	"github.com/keybase/client/go/protocol/keybase1"
	"golang.org/x/net/context"
)

type fileBlockGetter func(context.Context, KeyMetadata, BlockPointer,
	path, blockReqType) (*FileBlock, error)
type dirtyBlockCacher func(ptr BlockPointer, block Block) error

// fileData is a helper struct for accessing and manipulating data
// within a file.  It's meant for use within a single scope, not for
// long-term storage.  The caller must ensure goroutine-safety.
type fileData struct {
	file   path
	uid    keybase1.UID
	crypto cryptoPure
	kmd    KeyMetadata
	getter fileBlockGetter
	cacher dirtyBlockCacher
}

func newFileData(file path, uid keybase1.UID, crypto cryptoPure,
	kmd KeyMetadata, getter fileBlockGetter,
	cacher dirtyBlockCacher) *fileData {
	return &fileData{
		file:   file,
		uid:    uid,
		crypto: crypto,
		kmd:    kmd,
		getter: getter,
		cacher: cacher,
	}
}

// parentBlockAndChildIndex is a node on a path down the tree to a
// particular leaf node.  `pblock` is an indirect block corresponding
// to one of that leaf node's parents, and `childIndex` is an index
// into `pblock.IPtrs` to the next node along the path.
type parentBlockAndChildIndex struct {
	pblock     *FileBlock
	childIndex int
}

func (fd *fileData) getFileBlockAtOffset(ctx context.Context,
	topBlock *FileBlock, off int64, rtype blockReqType) (
	ptr BlockPointer, parentBlocks []parentBlockAndChildIndex,
	block *FileBlock, nextBlockStartOff, startOff int64, err error) {
	// find the block matching the offset, if it exists
	ptr = fd.file.tailPointer()
	block = topBlock
	nextBlockStartOff = -1
	startOff = 0
	// search until it's not an indirect block
	for block.IsInd {
		nextIndex := len(block.IPtrs) - 1
		for i, iptr := range block.IPtrs {
			if iptr.Off == off {
				// small optimization to avoid iterating past the right ptr
				nextIndex = i
				break
			} else if iptr.Off > off {
				// i can never be 0, because the first ptr always has
				// an offset at the beginning of the range
				nextIndex = i - 1
				break
			}
		}
		nextPtr := block.IPtrs[nextIndex]
		parentBlocks = append(parentBlocks,
			parentBlockAndChildIndex{block, nextIndex})
		startOff = nextPtr.Off
		// there is more to read if we ever took a path through a
		// ptr that wasn't the final ptr in its respective list
		if nextIndex != len(block.IPtrs)-1 {
			nextBlockStartOff = block.IPtrs[nextIndex+1].Off
		}
		ptr = nextPtr.BlockPointer
		block, err = fd.getter(ctx, fd.kmd, ptr, fd.file, rtype)
		if err != nil {
			return zeroPtr, nil, nil, 0, 0, err
		}
	}

	return ptr, parentBlocks, block, nextBlockStartOff, startOff, nil
}

// createIndirectBlock creates a new indirect block and pick a new id
// for the existing block, and use the existing block's ID for the new
// indirect block that becomes the parent.
func (fd *fileData) createIndirectBlock(
	df *dirtyFile, dver DataVer) (*FileBlock, error) {
	newID, err := fd.crypto.MakeTemporaryBlockID()
	if err != nil {
		return nil, err
	}
	fblock := &FileBlock{
		CommonBlock: CommonBlock{
			IsInd: true,
		},
		IPtrs: []IndirectFilePtr{
			{
				BlockInfo: BlockInfo{
					BlockPointer: BlockPointer{
						ID:      newID,
						KeyGen:  fd.kmd.LatestKeyGeneration(),
						DataVer: dver,
						BlockContext: BlockContext{
							Creator:  fd.uid,
							RefNonce: ZeroBlockRefNonce,
						},
					},
					EncodedSize: 0,
				},
				Off: 0,
			},
		},
	}

	// Mark the old block ID as not dirty, so that we will treat the
	// old block ID as newly dirtied in cacheBlockIfNotYetDirtyLocked.
	df.setBlockNotDirty(fd.file.tailPointer())
	err = fd.cacher(fd.file.tailPointer(), fblock)
	if err != nil {
		return nil, err
	}

	return fblock, nil
}

// newRightBlock appends a new block (with a temporary ID) to the end
// of this file.
func (fd *fileData) newRightBlock(
	ctx context.Context, ptr BlockPointer, pblock *FileBlock,
	off int64) error {
	newRID, err := fd.crypto.MakeTemporaryBlockID()
	if err != nil {
		return err
	}
	rblock := &FileBlock{}

	newPtr := BlockPointer{
		ID:      newRID,
		KeyGen:  fd.kmd.LatestKeyGeneration(),
		DataVer: DefaultNewBlockDataVersion(false),
		BlockContext: BlockContext{
			Creator:  fd.uid,
			RefNonce: ZeroBlockRefNonce,
		},
	}

	pblock.IPtrs = append(pblock.IPtrs, IndirectFilePtr{
		BlockInfo: BlockInfo{
			BlockPointer: newPtr,
			EncodedSize:  0,
		},
		Off: off,
	})

	err = fd.cacher(newPtr, rblock)
	if err != nil {
		return err
	}
	err = fd.cacher(ptr, pblock)
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2017 Jayson Grace. All rights reserved
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package main

import (
	"bufio"
	"fmt"
	"os"
)

// readLines reads an input file into memory from the specified path.
// Upon successfully reading the file in, it will return the lines which
// make up the file in the form of a []string.
func readLines(filePath string) ([]string, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	var lines []string
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		lines = append(lines, scanner.Text())
	}
	return lines, scanner.Err()
}

// writeLines writes the contents of an input slice to a specified file.
func writeLines(lines []string, filePath string) error {
	file, err := os.Create(filePath)
	if err != nil {
		return err
	}
	defer file.Close()

	w := bufio.NewWriter(file)
	for _, line := range lines {
		fmt.Fprintln(w, line)
	}
	return w.Flush()
}
<commit_msg>Here is a suggestion for the fileOps<commit_after>// Copyright 2017 Jayson Grace. All rights reserved
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package main

import (
	"io/ioutil"
	"strings"
)

// readLines reads an input file into memory from the specified path.
// Upon successfully reading the file in, it will return the lines which
// make up the file in the form of a []string.
func readLines(filePath string) ([]string, error) {
	b, err := ioutil.ReadFile(filePath)
	if err != nil {
		return nil, err
	}

	return strings.Split(string(b), "\n"), nil
}

// writeLines writes the contents of an input slice to a specified file.
// note the Go convention is the file name should probably come first.x
func writeLines(lines []string, filePath string) error {
	return ioutil.WriteFile(filePath, []byte(strings.Join(lines, "\n")), 0644)
}
<|endoftext|>


<commit_before>package main

import(
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"log"
	"flag"
	"regexp"
	"strings"
	"strconv"
	"path/filepath"
)

var main_colCount = 15
var main_dir string
var main_outHeaderFilename = "out.h"
var main_outHeaderFile *os.File
var main_outModuleFilename = "out.c"
var main_outModuleFile *os.File

func process(fi os.FileInfo) {
	fmt.Println("  processing " + fi.Name())

	f, err := os.Open(main_dir + "/" + fi.Name())
	if err != nil {
		fmt.Println("    error opening file, skipping")
		return
	}
	defer f.Close()

	// Replacing dots to underscores in module name.
	moduleName := strings.ToLower(strings.Replace(strings.TrimSuffix(main_outModuleFilename, filepath.Ext(main_outModuleFilename)), ".", "_", -1))
	moduleVarName := moduleName + "_" + strings.ToLower(strings.Replace(fi.Name(), ".", "_", -1))
	// Replacing spaces to underscores in module variable name.
	moduleVarName = strings.Replace(moduleVarName, " ", "_", -1)
	if _, err := strconv.Atoi(string([]rune(moduleVarName)[0])); err == nil {
		// If the module name starts with a number, we prepend an underscore.
		moduleVarName = "_" + moduleVarName
	}

	arrayDefine := "uint8_t " + moduleVarName + "[" + strconv.FormatInt(fi.Size(), 10) + "]"

	out := "\n" + arrayDefine + " = {\n\t"
	main_outModuleFile.WriteString(out)

	b := make([]byte, 1) // We read to this 1 byte buffer.
	i := 0
	for ; true; i++ {
		bytesRead, err := f.Read(b)
		if bytesRead != 1 {
			if err != io.EOF {
				log.Panic("    error during file read")
				return
			}
			break
		}

		if i > 0 {
			if i < main_colCount {
				main_outModuleFile.WriteString(", ")
			} else {
				main_outModuleFile.WriteString(",")
			}
		}

		if i == main_colCount {
			main_outModuleFile.WriteString("\n\t")
			i = 0
		}

		out := fmt.Sprintf("0x%.2x", b)
		main_outModuleFile.WriteString(out)
	}
	main_outModuleFile.WriteString("\n};\n")

	main_outHeaderFile.WriteString("extern " + arrayDefine + ";\n")
}

func initHeaderFile() {
	main_outHeaderFile.WriteString("#ifndef " + strings.ToUpper(strings.Replace(main_outHeaderFilename, ".", "_", -1)) + "__\n")
	main_outHeaderFile.WriteString("#define " + strings.ToUpper(strings.Replace(main_outHeaderFilename, ".", "_", -1)) + "__\n\n")
	main_outHeaderFile.WriteString("#include <stdint.h>\n\n")
}

func initModuleFile() {
	main_outModuleFile.WriteString("#include \"" + main_outHeaderFilename + "\"\n");
}

func main() {
	flag.IntVar(&main_colCount, "c", main_colCount, "hex value column count")
	flag.StringVar(&main_dir, "d", main_dir, "convert files in this directory")
	flag.StringVar(&main_outHeaderFilename, "h", main_outHeaderFilename, "output header filename")
	flag.StringVar(&main_outModuleFilename, "m", main_outModuleFilename, "output module filename")
	flag.Parse()

	if main_dir == "" {
		log.Fatal("no convert directory defined, see help")
	}

	var err error
	main_outHeaderFile, err = os.Create(main_outHeaderFilename)
	if err != nil {
		log.Fatal("can't create header file " + main_outHeaderFilename)
	}
	defer main_outHeaderFile.Close()
	initHeaderFile()

	main_outModuleFile, err = os.Create(main_outModuleFilename)
	if err != nil {
		log.Fatal("can't create module file " + main_outModuleFilename)
	}
	defer main_outModuleFile.Close()
	initModuleFile()

	files, err := ioutil.ReadDir(main_dir)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("files2c processing directory " + main_dir)

	// Ignoring .go files.
	regexpGo, _ := regexp.Compile("\\.go$")

    for _, fi := range files {
    	if !fi.Mode().IsRegular() {
    		continue
    	}
    	if fi.Name() == main_outHeaderFilename || fi.Name() == main_outModuleFilename {
    		continue
    	}
    	if regexpGo.MatchString(fi.Name()) {
    		continue
    	}
		process(fi)
	}

	main_outHeaderFile.WriteString("\n#endif\n")

	fmt.Println("files2c done")
}
<commit_msg>Add const keyword<commit_after>package main

import(
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"log"
	"flag"
	"regexp"
	"strings"
	"strconv"
	"path/filepath"
)

var main_colCount = 15
var main_dir string
var main_outHeaderFilename = "out.h"
var main_outHeaderFile *os.File
var main_outModuleFilename = "out.c"
var main_outModuleFile *os.File

func process(fi os.FileInfo) {
	fmt.Println("  processing " + fi.Name())

	f, err := os.Open(main_dir + "/" + fi.Name())
	if err != nil {
		fmt.Println("    error opening file, skipping")
		return
	}
	defer f.Close()

	// Replacing dots to underscores in module name.
	moduleName := strings.ToLower(strings.Replace(strings.TrimSuffix(main_outModuleFilename, filepath.Ext(main_outModuleFilename)), ".", "_", -1))
	moduleVarName := moduleName + "_" + strings.ToLower(strings.Replace(fi.Name(), ".", "_", -1))
	// Replacing spaces to underscores in module variable name.
	moduleVarName = strings.Replace(moduleVarName, " ", "_", -1)
	if _, err := strconv.Atoi(string([]rune(moduleVarName)[0])); err == nil {
		// If the module name starts with a number, we prepend an underscore.
		moduleVarName = "_" + moduleVarName
	}

	arrayDefine := "const uint8_t " + moduleVarName + "[" + strconv.FormatInt(fi.Size(), 10) + "]"

	out := "\n" + arrayDefine + " = {\n\t"
	main_outModuleFile.WriteString(out)

	b := make([]byte, 1) // We read to this 1 byte buffer.
	i := 0
	for ; true; i++ {
		bytesRead, err := f.Read(b)
		if bytesRead != 1 {
			if err != io.EOF {
				log.Panic("    error during file read")
				return
			}
			break
		}

		if i > 0 {
			if i < main_colCount {
				main_outModuleFile.WriteString(", ")
			} else {
				main_outModuleFile.WriteString(",")
			}
		}

		if i == main_colCount {
			main_outModuleFile.WriteString("\n\t")
			i = 0
		}

		out := fmt.Sprintf("0x%.2x", b)
		main_outModuleFile.WriteString(out)
	}
	main_outModuleFile.WriteString("\n};\n")

	main_outHeaderFile.WriteString("extern " + arrayDefine + ";\n")
}

func initHeaderFile() {
	main_outHeaderFile.WriteString("#ifndef " + strings.ToUpper(strings.Replace(main_outHeaderFilename, ".", "_", -1)) + "__\n")
	main_outHeaderFile.WriteString("#define " + strings.ToUpper(strings.Replace(main_outHeaderFilename, ".", "_", -1)) + "__\n\n")
	main_outHeaderFile.WriteString("#include <stdint.h>\n\n")
}

func initModuleFile() {
	main_outModuleFile.WriteString("#include \"" + main_outHeaderFilename + "\"\n");
}

func main() {
	flag.IntVar(&main_colCount, "c", main_colCount, "hex value column count")
	flag.StringVar(&main_dir, "d", main_dir, "convert files in this directory")
	flag.StringVar(&main_outHeaderFilename, "h", main_outHeaderFilename, "output header filename")
	flag.StringVar(&main_outModuleFilename, "m", main_outModuleFilename, "output module filename")
	flag.Parse()

	if main_dir == "" {
		log.Fatal("no convert directory defined, see help")
	}

	var err error
	main_outHeaderFile, err = os.Create(main_outHeaderFilename)
	if err != nil {
		log.Fatal("can't create header file " + main_outHeaderFilename)
	}
	defer main_outHeaderFile.Close()
	initHeaderFile()

	main_outModuleFile, err = os.Create(main_outModuleFilename)
	if err != nil {
		log.Fatal("can't create module file " + main_outModuleFilename)
	}
	defer main_outModuleFile.Close()
	initModuleFile()

	files, err := ioutil.ReadDir(main_dir)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("files2c processing directory " + main_dir)

	// Ignoring .go files.
	regexpGo, _ := regexp.Compile("\\.go$")

    for _, fi := range files {
    	if !fi.Mode().IsRegular() {
    		continue
    	}
    	if fi.Name() == main_outHeaderFilename || fi.Name() == main_outModuleFilename {
    		continue
    	}
    	if regexpGo.MatchString(fi.Name()) {
    		continue
    	}
		process(fi)
	}

	main_outHeaderFile.WriteString("\n#endif\n")

	fmt.Println("files2c done")
}
<|endoftext|>


<commit_before>package pop

import (
	"fmt"
	"reflect"
	"regexp"
	"strconv"

	"github.com/markbates/pop/columns"

	"github.com/satori/go.uuid"
)

var rLimitOffset = regexp.MustCompile("(?i)(limit [0-9]+ offset [0-9]+)$")
var rLimit = regexp.MustCompile("(?i)(limit [0-9]+)$")

// Find the first record of the model in the database with a particular id.
//
//	c.Find(&User{}, 1)
func (c *Connection) Find(model interface{}, id interface{}) error {
	return Q(c).Find(model, id)
}

// FindPreload finds the first record of the model in the database with a particular id.
// and preload tables associated records.
//
//	c.FindPreload(&User{}, 1)
func (c *Connection) FindPreload(model interface{}, id interface{}) error {
	return Q(c).FindPreload(model, id)
}

// Find the first record of the model in the database with a particular id.
//
//	q.Find(&User{}, 1)
func (q *Query) Find(model interface{}, id interface{}) error {
	m := &Model{Value: model}
	idq := fmt.Sprintf("%s.id = ?", m.TableName())
	switch t := id.(type) {
	case uuid.UUID:
		return q.Where(idq, t.String()).First(model)
	case string:
		var err error
		id, err = strconv.Atoi(t)
		if err != nil {
			return q.Where(idq, t).First(model)
		}
	}
	return q.Where(idq, id).First(model)
}

// FindPreload finds the first record of the model in the database with a particular id.
// and preload tables associated records.
func (q *Query) FindPreload(model interface{}, id interface{}) error {
	err := q.Find(model, id)
	if err != nil {
		return err
	}

	v := reflect.ValueOf(model)
	if v.Kind() == reflect.Ptr {
		v = v.Elem()
	}

	m := &Model{Value: model}
	cols := columns.ColumnsForStructWithAlias(model, m.TableName(), m.As).Preload()

	for name := range cols.Cols {
		f := v.FieldByName(name)
		i := f.Addr().Interface()

		newQuery := Q(q.Connection)
		newQuery = newQuery.Where(fmt.Sprintf("%s = ?", m.associationName()), id)

		if f.Kind() == reflect.Slice || f.Kind() == reflect.Array {
			err = newQuery.All(i)
			f.Set(reflect.ValueOf(i).Elem())
		}

		if f.Kind() == reflect.Struct {
			err = newQuery.First(i)
			f.Set(reflect.ValueOf(i).Elem())
		}

		if err != nil {
			return err
		}
	}

	return err
}

// First record of the model in the database that matches the query.
//
//	c.First(&User{})
func (c *Connection) First(model interface{}) error {
	return Q(c).First(model)
}

// First record of the model in the database that matches the query.
//
//	q.Where("name = ?", "mark").First(&User{})
func (q *Query) First(model interface{}) error {
	return q.Connection.timeFunc("First", func() error {
		q.Limit(1)
		m := &Model{Value: model}
		if err := q.Connection.Dialect.SelectOne(q.Connection.Store, m, *q); err != nil {
			return err
		}
		return m.afterFind(q.Connection)
	})
}

// Last record of the model in the database that matches the query.
//
//	c.Last(&User{})
func (c *Connection) Last(model interface{}) error {
	return Q(c).Last(model)
}

// Last record of the model in the database that matches the query.
//
//	q.Where("name = ?", "mark").Last(&User{})
func (q *Query) Last(model interface{}) error {
	return q.Connection.timeFunc("Last", func() error {
		q.Limit(1)
		q.Order("id desc")
		m := &Model{Value: model}
		if err := q.Connection.Dialect.SelectOne(q.Connection.Store, m, *q); err != nil {
			return err
		}
		return m.afterFind(q.Connection)
	})
}

// All retrieves all of the records in the database that match the query.
//
//	c.All(&[]User{})
func (c *Connection) All(models interface{}) error {
	return Q(c).All(models)
}

// All retrieves all of the records in the database that match the query.
//
//	q.Where("name = ?", "mark").All(&[]User{})
func (q *Query) All(models interface{}) error {
	return q.Connection.timeFunc("All", func() error {
		m := &Model{Value: models}
		err := q.Connection.Dialect.SelectMany(q.Connection.Store, m, *q)
		if err == nil && q.Paginator != nil {
			ct, err := q.Count(models)
			if err == nil {
				q.Paginator.TotalEntriesSize = ct
				st := reflect.ValueOf(models).Elem()
				q.Paginator.CurrentEntriesSize = st.Len()
				q.Paginator.TotalPages = (q.Paginator.TotalEntriesSize / q.Paginator.PerPage)
				if q.Paginator.TotalEntriesSize%q.Paginator.PerPage > 0 {
					q.Paginator.TotalPages = q.Paginator.TotalPages + 1
				}
			}
		}
		if err != nil {
			return err
		}
		return m.afterFind(q.Connection)
	})
}

// Exists returns true/false if a record exists in the database that matches
// the query.
//
// 	q.Where("name = ?", "mark").Exists(&User{})
func (q *Query) Exists(model interface{}) (bool, error) {
	i, err := q.Count(model)
	return i != 0, err
}

// Count the number of records in the database.
//
//	c.Count(&User{})
func (c *Connection) Count(model interface{}) (int, error) {
	return Q(c).Count(model)
}

// Count the number of records in the database.
//
//	q.Where("name = ?", "mark").Count(&User{})
func (q Query) Count(model interface{}) (int, error) {
	return q.CountByField(model, "*")
}

// CountByField counts the number of records in the database, for a given field.
//
//	q.Where("sex = ?", "f").Count(&User{}, "name")
func (q Query) CountByField(model interface{}, field string) (int, error) {
	tmpQuery := Q(q.Connection)
	q.Clone(tmpQuery) //avoid mendling with original query

	res := &rowCount{}

	err := tmpQuery.Connection.timeFunc("CountByField", func() error {
		tmpQuery.Paginator = nil
		tmpQuery.orderClauses = clauses{}
		tmpQuery.limitResults = 0
		query, args := tmpQuery.ToSQL(&Model{Value: model})
		//when query contains custom selected fields / executed using RawQuery,
		//	sql may already contains limit and offset

		if rLimitOffset.MatchString(query) {
			foundLimit := rLimitOffset.FindString(query)
			query = query[0 : len(query)-len(foundLimit)]
		} else if rLimit.MatchString(query) {
			foundLimit := rLimit.FindString(query)
			query = query[0 : len(query)-len(foundLimit)]
		}

		countQuery := fmt.Sprintf("select count(%s) as row_count from (%s) a", field, query)
		Log(countQuery, args...)
		return q.Connection.Store.Get(res, countQuery, args...)
	})
	return res.Count, err
}

type rowCount struct {
	Count int `db:"row_count"`
}
<commit_msg>add pointer definitions for finders<commit_after>package pop

import (
	"fmt"
	"reflect"
	"regexp"
	"strconv"

	"github.com/markbates/pop/columns"

	"github.com/satori/go.uuid"
)

var rLimitOffset = regexp.MustCompile("(?i)(limit [0-9]+ offset [0-9]+)$")
var rLimit = regexp.MustCompile("(?i)(limit [0-9]+)$")

// Find the first record of the model in the database with a particular id.
//
//	c.Find(&User{}, 1)
func (c *Connection) Find(model interface{}, id interface{}) error {
	return Q(c).Find(model, id)
}

// FindPreload finds the first record of the model in the database with a particular id.
// and preload tables associated records.
//
//	c.FindPreload(&User{}, 1)
func (c *Connection) FindPreload(model interface{}, id interface{}) error {
	return Q(c).FindPreload(model, id)
}

// Find the first record of the model in the database with a particular id.
//
//	q.Find(&User{}, 1)
func (q *Query) Find(model interface{}, id interface{}) error {
	m := &Model{Value: model}
	idq := fmt.Sprintf("%s.id = ?", m.TableName())
	switch t := id.(type) {
	case uuid.UUID:
		return q.Where(idq, t.String()).First(model)
	case string:
		var err error
		id, err = strconv.Atoi(t)
		if err != nil {
			return q.Where(idq, t).First(model)
		}
	}
	return q.Where(idq, id).First(model)
}

// FindPreload finds the first record of the model in the database with a particular id.
// and preload tables associated records.
func (q *Query) FindPreload(model interface{}, id interface{}) error {
	err := q.Find(model, id)
	if err != nil {
		return err
	}

	v := reflect.ValueOf(model)
	if v.Kind() == reflect.Ptr {
		v = v.Elem()
	}

	m := &Model{Value: model}
	cols := columns.ColumnsForStructWithAlias(model, m.TableName(), m.As).Preload()

	for name := range cols.Cols {
		var i interface{}
		f := v.FieldByName(name)

		newQuery := Q(q.Connection)
		newQuery = newQuery.Where(fmt.Sprintf("%s = ?", m.associationName()), id)

		if f.Kind() == reflect.Ptr {
			el := reflect.New(f.Type().Elem())
			i = el.Interface()

			if el.Elem().Kind() == reflect.Slice || el.Elem().Kind() == reflect.Array {
				err = newQuery.All(i)
				f.Set(reflect.ValueOf(i))
			}

			if el.Elem().Kind() == reflect.Struct {
				err = newQuery.First(i)
				f.Set(reflect.ValueOf(i))
			}
			continue
		}

		i = f.Addr().Interface()
		if f.Kind() == reflect.Slice || f.Kind() == reflect.Array {
			err = newQuery.All(i)
			f.Set(reflect.ValueOf(i).Elem())
		}

		if f.Kind() == reflect.Struct {
			err = newQuery.First(i)
			f.Set(reflect.ValueOf(i).Elem())
		}

		if err != nil {
			return err
		}
	}

	return err
}

// First record of the model in the database that matches the query.
//
//	c.First(&User{})
func (c *Connection) First(model interface{}) error {
	return Q(c).First(model)
}

// First record of the model in the database that matches the query.
//
//	q.Where("name = ?", "mark").First(&User{})
func (q *Query) First(model interface{}) error {
	return q.Connection.timeFunc("First", func() error {
		q.Limit(1)
		m := &Model{Value: model}
		if err := q.Connection.Dialect.SelectOne(q.Connection.Store, m, *q); err != nil {
			return err
		}
		return m.afterFind(q.Connection)
	})
}

// Last record of the model in the database that matches the query.
//
//	c.Last(&User{})
func (c *Connection) Last(model interface{}) error {
	return Q(c).Last(model)
}

// Last record of the model in the database that matches the query.
//
//	q.Where("name = ?", "mark").Last(&User{})
func (q *Query) Last(model interface{}) error {
	return q.Connection.timeFunc("Last", func() error {
		q.Limit(1)
		q.Order("id desc")
		m := &Model{Value: model}
		if err := q.Connection.Dialect.SelectOne(q.Connection.Store, m, *q); err != nil {
			return err
		}
		return m.afterFind(q.Connection)
	})
}

// All retrieves all of the records in the database that match the query.
//
//	c.All(&[]User{})
func (c *Connection) All(models interface{}) error {
	return Q(c).All(models)
}

// All retrieves all of the records in the database that match the query.
//
//	q.Where("name = ?", "mark").All(&[]User{})
func (q *Query) All(models interface{}) error {
	return q.Connection.timeFunc("All", func() error {
		m := &Model{Value: models}
		err := q.Connection.Dialect.SelectMany(q.Connection.Store, m, *q)
		if err == nil && q.Paginator != nil {
			ct, err := q.Count(models)
			if err == nil {
				q.Paginator.TotalEntriesSize = ct
				st := reflect.ValueOf(models).Elem()
				q.Paginator.CurrentEntriesSize = st.Len()
				q.Paginator.TotalPages = (q.Paginator.TotalEntriesSize / q.Paginator.PerPage)
				if q.Paginator.TotalEntriesSize%q.Paginator.PerPage > 0 {
					q.Paginator.TotalPages = q.Paginator.TotalPages + 1
				}
			}
		}
		if err != nil {
			return err
		}
		return m.afterFind(q.Connection)
	})
}

// Exists returns true/false if a record exists in the database that matches
// the query.
//
// 	q.Where("name = ?", "mark").Exists(&User{})
func (q *Query) Exists(model interface{}) (bool, error) {
	i, err := q.Count(model)
	return i != 0, err
}

// Count the number of records in the database.
//
//	c.Count(&User{})
func (c *Connection) Count(model interface{}) (int, error) {
	return Q(c).Count(model)
}

// Count the number of records in the database.
//
//	q.Where("name = ?", "mark").Count(&User{})
func (q Query) Count(model interface{}) (int, error) {
	return q.CountByField(model, "*")
}

// CountByField counts the number of records in the database, for a given field.
//
//	q.Where("sex = ?", "f").Count(&User{}, "name")
func (q Query) CountByField(model interface{}, field string) (int, error) {
	tmpQuery := Q(q.Connection)
	q.Clone(tmpQuery) //avoid mendling with original query

	res := &rowCount{}

	err := tmpQuery.Connection.timeFunc("CountByField", func() error {
		tmpQuery.Paginator = nil
		tmpQuery.orderClauses = clauses{}
		tmpQuery.limitResults = 0
		query, args := tmpQuery.ToSQL(&Model{Value: model})
		//when query contains custom selected fields / executed using RawQuery,
		//	sql may already contains limit and offset

		if rLimitOffset.MatchString(query) {
			foundLimit := rLimitOffset.FindString(query)
			query = query[0 : len(query)-len(foundLimit)]
		} else if rLimit.MatchString(query) {
			foundLimit := rLimit.FindString(query)
			query = query[0 : len(query)-len(foundLimit)]
		}

		countQuery := fmt.Sprintf("select count(%s) as row_count from (%s) a", field, query)
		Log(countQuery, args...)
		return q.Connection.Store.Get(res, countQuery, args...)
	})
	return res.Count, err
}

type rowCount struct {
	Count int `db:"row_count"`
}
<|endoftext|>


<commit_before>package crescent

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestUnit(t *testing.T) {
	assert := assert.New(t)

	class := &Class{
		Name:                 "Healer",
		Health:               1000,
		HealthRegeneration:   5,
		Mana:                 400,
		ManaRegeneration:     2,
		Armor:                DefaultArmor,
		MagicResistance:      DefaultMagicResistance,
		CriticalStrikeChance: DefaultCriticalStrikeChance,
		CriticalStrikeFactor: DefaultCriticalStrikeFactor,
		CooldownReduction:    DefaultCooldownReduction,
		DamageThreatFactor:   DefaultDamageThreatFactor,
		HealingThreatFactor:  DefaultHealingThreatFactor,
		Abilities:            []*Ability{},
	}
	u := NewUnit(100, 0, "user", class, MakeEventDispatcher())

	assert.Implements((*Subject)(nil), u)
	assert.Implements((*Object)(nil), u)

	assert.Equal(u, u.Subject())
	assert.Equal(u, u.Object())
	assert.Equal(UnitID(100), u.ID())
	assert.Equal(UnitGroup(0), u.Group())
	assert.Equal(UnitName("user"), u.Name())
	assert.Equal(ClassName("Healer"), u.ClassName())
	assert.True(u.IsAlive())
	assert.False(u.IsDead())
	assert.Equal(Statistic(1000), u.Health())
	assert.Equal(Statistic(1000), u.HealthMax())
	assert.Equal(Statistic(5), u.HealthRegeneration())
	assert.Equal(Statistic(400), u.Mana())
	assert.Equal(Statistic(400), u.ManaMax())
	assert.Equal(Statistic(2), u.ManaRegeneration())
	assert.Equal(DefaultArmor, u.Armor())
	assert.Equal(DefaultMagicResistance, u.MagicResistance())
	assert.Equal(damageReductionFactor(DefaultArmor), u.PhysicalDamageReductionFactor())
	assert.Equal(damageReductionFactor(DefaultMagicResistance), u.MagicDamageReductionFactor())
	assert.Equal(DefaultCriticalStrikeChance, u.CriticalStrikeChance())
	assert.Equal(DefaultCriticalStrikeFactor, u.CriticalStrikeFactor())
	assert.Equal(DefaultCooldownReduction, u.CooldownReduction())
	assert.Equal(DefaultDamageThreatFactor, u.DamageThreatFactor())
	assert.Equal(DefaultHealingThreatFactor, u.HealingThreatFactor())
	assert.Empty(u.Abilities())
	assert.Nil(u.Ability("Q"))

	u.UpdateCorrection(UnitCorrection{
		Armor:                75,
		MagicResistance:      25,
		CriticalStrikeChance: 0.05,
		CriticalStrikeFactor: 0.5,
		CooldownReduction:    0.1,
		DamageThreatFactor:   2,
		HealingThreatFactor:  1,
	})
	assert.Equal(75+DefaultArmor, u.Armor())
	assert.Equal(25+DefaultMagicResistance, u.MagicResistance())
	assert.Equal(damageReductionFactor(75+DefaultArmor), u.PhysicalDamageReductionFactor())
	assert.Equal(damageReductionFactor(25+DefaultMagicResistance), u.MagicDamageReductionFactor())
	assert.Equal(0.05+DefaultCriticalStrikeChance, u.CriticalStrikeChance())
	assert.Equal(0.5+DefaultCriticalStrikeFactor, u.CriticalStrikeFactor())
	assert.Equal(0.1+DefaultCooldownReduction, u.CooldownReduction())
	assert.Equal(2+DefaultDamageThreatFactor, u.DamageThreatFactor())
	assert.Equal(1+DefaultHealingThreatFactor, u.HealingThreatFactor())

	w := new(MockedInstanceOutputWriter)

	{
		before, after, err := u.ModifyMana(w, 800)
		if assert.Nil(err) {
			assert.Equal(Statistic(400), before)
			assert.Equal(Statistic(400), after)
		}
		assert.Equal(Statistic(400), u.Mana())
		assert.Equal(Statistic(400), u.ManaMax())
	}
	{
		before, after, err := u.ModifyHealth(w, 2000)
		if assert.Nil(err) {
			assert.Equal(Statistic(1000), before)
			assert.Equal(Statistic(1000), after)
		}
		assert.Equal(Statistic(1000), u.Health())
		assert.Equal(Statistic(1000), u.HealthMax())
	}

	{
		w.On("Write", OutputUnitResource{
			UnitID: 100,
			Health: 1000,
			Mana:   200,
		}).Return()
		before, after, err := u.ModifyMana(w, -200)
		if assert.Nil(err) {
			assert.Equal(Statistic(400), before)
			assert.Equal(Statistic(200), after)
		}
		assert.Equal(Statistic(200), u.Mana())
		assert.Equal(Statistic(400), u.ManaMax())
		w.AssertExpectations(t)
	}

	{
		w.On("Write", OutputUnitResource{
			UnitID: 100,
			Health: 500,
			Mana:   200,
		}).Return()
		before, after, err := u.ModifyHealth(w, -500)
		if assert.Nil(err) {
			assert.Equal(Statistic(1000), before)
			assert.Equal(Statistic(500), after)
		}
		assert.Equal(Statistic(500), u.Health())
		assert.Equal(Statistic(1000), u.HealthMax())
		w.AssertExpectations(t)
	}

	{
		w.On("Write", OutputUnitResource{
			UnitID: 100,
			Health: 500,
			Mana:   0,
		}).Return()
		before, after, err := u.ModifyMana(w, -400)
		if assert.Nil(err) {
			assert.Equal(Statistic(200), before)
			assert.Equal(Statistic(0), after)
		}
		assert.Equal(Statistic(0), u.Mana())
		assert.Equal(Statistic(400), u.ManaMax())
		w.AssertExpectations(t)
	}

	{
		w.On("Write", OutputUnitResource{
			UnitID: 100,
			Health: 0,
			Mana:   0,
		}).Return()
		before, after, err := u.ModifyHealth(w, -2000)
		if assert.Nil(err) {
			assert.Equal(Statistic(500), before)
			assert.Equal(Statistic(0), after)
		}
		assert.Equal(Statistic(0), u.Health())
		assert.Equal(Statistic(1000), u.HealthMax())
		w.AssertExpectations(t)
	}

	assert.False(u.IsAlive())
	assert.True(u.IsDead())

	{
		_, _, err := u.ModifyMana(w, 400)
		assert.NotNil(err)
	}

	{
		_, _, err := u.ModifyHealth(w, 2000)
		assert.NotNil(err)
	}
}
<commit_msg>Add tests for Unit<commit_after>package crescent

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestUnit(t *testing.T) {
	assert := assert.New(t)

	class := &Class{
		Name:                 "Healer",
		Health:               1000,
		HealthRegeneration:   5,
		Mana:                 400,
		ManaRegeneration:     2,
		Armor:                DefaultArmor,
		MagicResistance:      DefaultMagicResistance,
		CriticalStrikeChance: DefaultCriticalStrikeChance,
		CriticalStrikeFactor: DefaultCriticalStrikeFactor,
		CooldownReduction:    DefaultCooldownReduction,
		DamageThreatFactor:   DefaultDamageThreatFactor,
		HealingThreatFactor:  DefaultHealingThreatFactor,
		Abilities:            []*Ability{},
	}
	dispatcher := new(MockedEventDispatcher)
	u := NewUnit(100, 0, "user", class, dispatcher)

	assert.Implements((*Subject)(nil), u)
	assert.Implements((*Object)(nil), u)
	assert.Implements((*EventDispatcher)(nil), u)

	assert.Equal(u, u.Subject())
	assert.Equal(u, u.Object())

	h := new(MockedEventHandler)
	dispatcher.On("Register", h).Return().Once()
	dispatcher.On("Dispatch", (interface{})("payload")).Return().Once()
	dispatcher.On("Unregister", h).Return().Once()
	u.Register(h)
	u.Dispatch("payload")
	u.Unregister(h)
	dispatcher.AssertExpectations(t)
	h.AssertExpectations(t)

	assert.Equal(UnitID(100), u.ID())
	assert.Equal(UnitGroup(0), u.Group())
	assert.Equal(UnitName("user"), u.Name())
	assert.Equal(ClassName("Healer"), u.ClassName())
	assert.True(u.IsAlive())
	assert.False(u.IsDead())
	assert.Equal(Statistic(1000), u.Health())
	assert.Equal(Statistic(1000), u.HealthMax())
	assert.Equal(Statistic(5), u.HealthRegeneration())
	assert.Equal(Statistic(400), u.Mana())
	assert.Equal(Statistic(400), u.ManaMax())
	assert.Equal(Statistic(2), u.ManaRegeneration())
	assert.Equal(DefaultArmor, u.Armor())
	assert.Equal(DefaultMagicResistance, u.MagicResistance())
	assert.Equal(damageReductionFactor(DefaultArmor), u.PhysicalDamageReductionFactor())
	assert.Equal(damageReductionFactor(DefaultMagicResistance), u.MagicDamageReductionFactor())
	assert.Equal(DefaultCriticalStrikeChance, u.CriticalStrikeChance())
	assert.Equal(DefaultCriticalStrikeFactor, u.CriticalStrikeFactor())
	assert.Equal(DefaultCooldownReduction, u.CooldownReduction())
	assert.Equal(DefaultDamageThreatFactor, u.DamageThreatFactor())
	assert.Equal(DefaultHealingThreatFactor, u.HealingThreatFactor())
	assert.Empty(u.Abilities())
	assert.Nil(u.Ability("Q"))

	u.UpdateCorrection(UnitCorrection{
		Armor:                75,
		MagicResistance:      25,
		CriticalStrikeChance: 0.05,
		CriticalStrikeFactor: 0.5,
		CooldownReduction:    0.1,
		DamageThreatFactor:   2,
		HealingThreatFactor:  1,
	})
	assert.Equal(75+DefaultArmor, u.Armor())
	assert.Equal(25+DefaultMagicResistance, u.MagicResistance())
	assert.Equal(damageReductionFactor(75+DefaultArmor), u.PhysicalDamageReductionFactor())
	assert.Equal(damageReductionFactor(25+DefaultMagicResistance), u.MagicDamageReductionFactor())
	assert.Equal(0.05+DefaultCriticalStrikeChance, u.CriticalStrikeChance())
	assert.Equal(0.5+DefaultCriticalStrikeFactor, u.CriticalStrikeFactor())
	assert.Equal(0.1+DefaultCooldownReduction, u.CooldownReduction())
	assert.Equal(2+DefaultDamageThreatFactor, u.DamageThreatFactor())
	assert.Equal(1+DefaultHealingThreatFactor, u.HealingThreatFactor())

	w := new(MockedInstanceOutputWriter)

	{
		before, after, err := u.ModifyMana(w, 800)
		if assert.Nil(err) {
			assert.Equal(Statistic(400), before)
			assert.Equal(Statistic(400), after)
		}
		assert.Equal(Statistic(400), u.Mana())
		assert.Equal(Statistic(400), u.ManaMax())
	}
	{
		before, after, err := u.ModifyHealth(w, 2000)
		if assert.Nil(err) {
			assert.Equal(Statistic(1000), before)
			assert.Equal(Statistic(1000), after)
		}
		assert.Equal(Statistic(1000), u.Health())
		assert.Equal(Statistic(1000), u.HealthMax())
	}

	{
		w.On("Write", OutputUnitResource{
			UnitID: 100,
			Health: 1000,
			Mana:   200,
		}).Return()
		before, after, err := u.ModifyMana(w, -200)
		if assert.Nil(err) {
			assert.Equal(Statistic(400), before)
			assert.Equal(Statistic(200), after)
		}
		assert.Equal(Statistic(200), u.Mana())
		assert.Equal(Statistic(400), u.ManaMax())
		w.AssertExpectations(t)
	}

	{
		w.On("Write", OutputUnitResource{
			UnitID: 100,
			Health: 500,
			Mana:   200,
		}).Return()
		before, after, err := u.ModifyHealth(w, -500)
		if assert.Nil(err) {
			assert.Equal(Statistic(1000), before)
			assert.Equal(Statistic(500), after)
		}
		assert.Equal(Statistic(500), u.Health())
		assert.Equal(Statistic(1000), u.HealthMax())
		w.AssertExpectations(t)
	}

	{
		w.On("Write", OutputUnitResource{
			UnitID: 100,
			Health: 500,
			Mana:   0,
		}).Return()
		before, after, err := u.ModifyMana(w, -400)
		if assert.Nil(err) {
			assert.Equal(Statistic(200), before)
			assert.Equal(Statistic(0), after)
		}
		assert.Equal(Statistic(0), u.Mana())
		assert.Equal(Statistic(400), u.ManaMax())
		w.AssertExpectations(t)
	}

	{
		w.On("Write", OutputUnitResource{
			UnitID: 100,
			Health: 0,
			Mana:   0,
		}).Return()
		before, after, err := u.ModifyHealth(w, -2000)
		if assert.Nil(err) {
			assert.Equal(Statistic(500), before)
			assert.Equal(Statistic(0), after)
		}
		assert.Equal(Statistic(0), u.Health())
		assert.Equal(Statistic(1000), u.HealthMax())
		w.AssertExpectations(t)
	}

	assert.False(u.IsAlive())
	assert.True(u.IsDead())

	{
		_, _, err := u.ModifyMana(w, 400)
		assert.NotNil(err)
	}

	{
		_, _, err := u.ModifyHealth(w, 2000)
		assert.NotNil(err)
	}
}
<|endoftext|>


<commit_before>package redis

import "time"

// UniversalOptions information is required by UniversalClient to establish
// connections.
type UniversalOptions struct {
	// Either a single address or a seed list of host:port addresses
	// of cluster/sentinel nodes.
	Addrs []string

	// The sentinel master name.
	// Only failover clients.
	MasterName string

	// Database to be selected after connecting to the server.
	// Only single-node and failover clients.
	DB int

	// Enables read only queries on slave nodes.
	// Only cluster and single-node clients.
	ReadOnly bool

	// Only cluster clients.

	MaxRedirects   int
	RouteByLatency bool

	// Common options

	MaxRetries         int
	Password           string
	DialTimeout        time.Duration
	ReadTimeout        time.Duration
	WriteTimeout       time.Duration
	PoolSize           int
	PoolTimeout        time.Duration
	IdleTimeout        time.Duration
	IdleCheckFrequency time.Duration
}

func (o *UniversalOptions) cluster() *ClusterOptions {
	if len(o.Addrs) == 0 {
		o.Addrs = []string{"127.0.0.1:6379"}
	}

	return &ClusterOptions{
		Addrs:          o.Addrs,
		MaxRedirects:   o.MaxRedirects,
		RouteByLatency: o.RouteByLatency,
		ReadOnly:       o.ReadOnly,

		MaxRetries:         o.MaxRetries,
		Password:           o.Password,
		DialTimeout:        o.DialTimeout,
		ReadTimeout:        o.ReadTimeout,
		WriteTimeout:       o.WriteTimeout,
		PoolSize:           o.PoolSize,
		PoolTimeout:        o.PoolTimeout,
		IdleTimeout:        o.IdleTimeout,
		IdleCheckFrequency: o.IdleCheckFrequency,
	}
}

func (o *UniversalOptions) failover() *FailoverOptions {
	if len(o.Addrs) == 0 {
		o.Addrs = []string{"127.0.0.1:26379"}
	}

	return &FailoverOptions{
		SentinelAddrs: o.Addrs,
		MasterName:    o.MasterName,
		DB:            o.DB,

		MaxRetries:         o.MaxRetries,
		Password:           o.Password,
		DialTimeout:        o.DialTimeout,
		ReadTimeout:        o.ReadTimeout,
		WriteTimeout:       o.WriteTimeout,
		PoolSize:           o.PoolSize,
		PoolTimeout:        o.PoolTimeout,
		IdleTimeout:        o.IdleTimeout,
		IdleCheckFrequency: o.IdleCheckFrequency,
	}
}

func (o *UniversalOptions) simple() *Options {
	addr := "127.0.0.1:6379"
	if len(o.Addrs) > 0 {
		addr = o.Addrs[0]
	}

	return &Options{
		Addr:     addr,
		DB:       o.DB,
		ReadOnly: o.ReadOnly,

		MaxRetries:         o.MaxRetries,
		Password:           o.Password,
		DialTimeout:        o.DialTimeout,
		ReadTimeout:        o.ReadTimeout,
		WriteTimeout:       o.WriteTimeout,
		PoolSize:           o.PoolSize,
		PoolTimeout:        o.PoolTimeout,
		IdleTimeout:        o.IdleTimeout,
		IdleCheckFrequency: o.IdleCheckFrequency,
	}
}

// --------------------------------------------------------------------

// UniversalClient is an abstract client which - based on the provided options -
// can connect to either clusters, or sentinel-backed failover instances or simple
// single-instance servers. This can be useful for testing cluster-specific
// applications locally.
type UniversalClient interface {
	Cmdable
	Close() error
}

// NewUniversalClient returns a new multi client. The type of client returned depends
// on the following three conditions:
//
// 1. if a MasterName is passed a sentinel-backed FailoverClient will be returned
// 2. if the number of Addrs is two or more, a ClusterClient will be returned
// 3. otherwise, a single-node redis Client will be returned.
func NewUniversalClient(opts *UniversalOptions) UniversalClient {
	if opts.MasterName != "" {
		return NewFailoverClient(opts.failover())
	} else if len(opts.Addrs) > 1 {
		return NewClusterClient(opts.cluster())
	}
	return NewClient(opts.simple())
}
<commit_msg>Expose Process on UniversalClient<commit_after>package redis

import "time"

// UniversalOptions information is required by UniversalClient to establish
// connections.
type UniversalOptions struct {
	// Either a single address or a seed list of host:port addresses
	// of cluster/sentinel nodes.
	Addrs []string

	// The sentinel master name.
	// Only failover clients.
	MasterName string

	// Database to be selected after connecting to the server.
	// Only single-node and failover clients.
	DB int

	// Enables read only queries on slave nodes.
	// Only cluster and single-node clients.
	ReadOnly bool

	// Only cluster clients.

	MaxRedirects   int
	RouteByLatency bool

	// Common options

	MaxRetries         int
	Password           string
	DialTimeout        time.Duration
	ReadTimeout        time.Duration
	WriteTimeout       time.Duration
	PoolSize           int
	PoolTimeout        time.Duration
	IdleTimeout        time.Duration
	IdleCheckFrequency time.Duration
}

func (o *UniversalOptions) cluster() *ClusterOptions {
	if len(o.Addrs) == 0 {
		o.Addrs = []string{"127.0.0.1:6379"}
	}

	return &ClusterOptions{
		Addrs:          o.Addrs,
		MaxRedirects:   o.MaxRedirects,
		RouteByLatency: o.RouteByLatency,
		ReadOnly:       o.ReadOnly,

		MaxRetries:         o.MaxRetries,
		Password:           o.Password,
		DialTimeout:        o.DialTimeout,
		ReadTimeout:        o.ReadTimeout,
		WriteTimeout:       o.WriteTimeout,
		PoolSize:           o.PoolSize,
		PoolTimeout:        o.PoolTimeout,
		IdleTimeout:        o.IdleTimeout,
		IdleCheckFrequency: o.IdleCheckFrequency,
	}
}

func (o *UniversalOptions) failover() *FailoverOptions {
	if len(o.Addrs) == 0 {
		o.Addrs = []string{"127.0.0.1:26379"}
	}

	return &FailoverOptions{
		SentinelAddrs: o.Addrs,
		MasterName:    o.MasterName,
		DB:            o.DB,

		MaxRetries:         o.MaxRetries,
		Password:           o.Password,
		DialTimeout:        o.DialTimeout,
		ReadTimeout:        o.ReadTimeout,
		WriteTimeout:       o.WriteTimeout,
		PoolSize:           o.PoolSize,
		PoolTimeout:        o.PoolTimeout,
		IdleTimeout:        o.IdleTimeout,
		IdleCheckFrequency: o.IdleCheckFrequency,
	}
}

func (o *UniversalOptions) simple() *Options {
	addr := "127.0.0.1:6379"
	if len(o.Addrs) > 0 {
		addr = o.Addrs[0]
	}

	return &Options{
		Addr:     addr,
		DB:       o.DB,
		ReadOnly: o.ReadOnly,

		MaxRetries:         o.MaxRetries,
		Password:           o.Password,
		DialTimeout:        o.DialTimeout,
		ReadTimeout:        o.ReadTimeout,
		WriteTimeout:       o.WriteTimeout,
		PoolSize:           o.PoolSize,
		PoolTimeout:        o.PoolTimeout,
		IdleTimeout:        o.IdleTimeout,
		IdleCheckFrequency: o.IdleCheckFrequency,
	}
}

// --------------------------------------------------------------------

// UniversalClient is an abstract client which - based on the provided options -
// can connect to either clusters, or sentinel-backed failover instances or simple
// single-instance servers. This can be useful for testing cluster-specific
// applications locally.
type UniversalClient interface {
	Cmdable
	Process(cmd Cmder) error
	Close() error
}

// NewUniversalClient returns a new multi client. The type of client returned depends
// on the following three conditions:
//
// 1. if a MasterName is passed a sentinel-backed FailoverClient will be returned
// 2. if the number of Addrs is two or more, a ClusterClient will be returned
// 3. otherwise, a single-node redis Client will be returned.
func NewUniversalClient(opts *UniversalOptions) UniversalClient {
	if opts.MasterName != "" {
		return NewFailoverClient(opts.failover())
	} else if len(opts.Addrs) > 1 {
		return NewClusterClient(opts.cluster())
	}
	return NewClient(opts.simple())
}
<|endoftext|>