<commit_before>package model

import (
	"crypto/sha256"
	"encoding/hex"
	"errors"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"time"

        "github.com/dustin/go-humanize"
	"github.com/rwcarlsen/goexif/exif"
	"github.com/disintegration/imaging"
)

type File struct {
	Filename		string		`json:"filename"`
	Tag			string		`json:"tag"`
	TagDir			string		`json:"-"`
	Bytes			int64		`json:"bytes"`
	BytesReadable		string		`json:"-"`
	MIME			string		`json:"mime"`
	CreatedReadable		string		`json:"created"`
	CreatedAt		time.Time	`json:"-"`
	Links			[]Link		`json:"links"`
	Checksum		string		`json:"checksum"`
	Algorithm		string		`json:"algorithm"`
	Verified		bool		`json:"verified"`
	RemoteAddr		string		`json:"-"`
	UserAgent		string		`json:"-"`
	Tempfile		string		`json:"-"`
	Extra			interface{}	`json:"extra"`

	// Image specific attributes
        DateTime                time.Time       `json:"-"`
        Longitude               float64         `json:"-"`
        Latitude                float64         `json:"-"`
        Altitude                string          `json:"-"`
        Thumbnail               bool            `json:"-"`
        Exif                    *exif.Exif      `json:"-"`
}

func (f *File) SetTag(s string) error {
        validTag := regexp.MustCompile("^[a-zA-Z0-9-_]{8,}$")
        if validTag.MatchString(s) == false {
                return errors.New("Invalid tag specified. It contains " +
                        "illegal characters or is too short")
        }
        f.Tag = s
        return nil
}

func (f *File) SetTagDir(filedir string) error {
	if f.Tag == "" {
		return errors.New("Tag not set.")
	}
        f.TagDir = filepath.Join(filedir, f.Tag)
	return nil
}

func (f *File) SetFilename(s string) error {
	// Remove all but valid chars
	var valid = regexp.MustCompile("[^A-Za-z0-9-_=,.]")
	var safe = valid.ReplaceAllString(s, "_")
	if safe == "" {
		return errors.New("Invalid filename specified. It contains " +
			"illegal characters or is too short.")
	}

	// Reject illegal filenames
	switch safe {
		case ".", "..":
			return errors.New("Invalid filename specified.")
	}

	// Set filename to the safe variant
	f.Filename = safe

	return nil
}

func (f *File) DetectMIME() error {
	var err error
	if f.TagDir == "" {
		return errors.New("TagDir not set.")
	}

        fpath := filepath.Join(f.TagDir, f.Filename)
        if f.Tempfile != "" {
                fpath = filepath.Join(f.Tempfile)
        }

	fp, err := os.Open(fpath)
	if err != nil {
		return err
	}
	defer fp.Close()
	buffer := make([]byte, 512)
	_, err = fp.Seek(0, 0)
	if err != nil {
		return err
	}
	_, err = fp.Read(buffer)
	if err != nil {
		return err
	}
	f.MIME = http.DetectContentType(buffer)
	return nil
}

func (f *File) MediaType() string {
        s := regexp.MustCompile("/").Split(f.MIME, 2)
        if len(s) > 0 {
                return s[0]
        }
        return ""
}

func (f *File) GenerateLinks(baseurl string) {
	fileLink := Link {}
	fileLink.Rel = "file"
	fileLink.Href = baseurl + "/" + f.Tag + "/" + f.Filename
	f.Links = append(f.Links, fileLink)

	tagLink := Link {}
	tagLink.Rel = "tag"
	tagLink.Href = baseurl + "/" + f.Tag
	f.Links = append(f.Links, tagLink)

	if f.ThumbnailExists() {
		thumbLink := Link {}
		thumbLink.Rel = "thumbnail"
		thumbLink.Href = baseurl + "/" + f.Tag + "/" + f.Filename + "?size=thumbnail"
		f.Links = append(f.Links, thumbLink)
	}
}

func (f *File) EnsureTagDirectoryExists() error {
	var err error

	// Tag directory
	if !isDir(f.TagDir) {
		err = os.Mkdir(f.TagDir, 0700)
		if err != nil {
			return err
		}
	}

	// Tag specific cache directory
	cpath := filepath.Join(f.TagDir, ".cache")
	if !isDir(cpath) {
		err = os.Mkdir(cpath, 0700)
		if err != nil {
			return err
		}
	}
	return err
}

func (f *File) Exists() bool {
	if f.TagDir == "" {
		return false
	}

	if f.Filename == "" {
		return false
	}

	path := filepath.Join(f.TagDir, f.Filename)
	if isFile(path) {
		return true
	}
	return false
}

func (f *File) ThumbnailExists() bool {
        path := f.ThumbnailPath()
	if isFile(path) {
		return true
	}
	return false
}

func (f *File) StatInfo() error {
	if isDir(f.TagDir) == false {
		return errors.New("Tag does not exist.")
	}

	path := filepath.Join(f.TagDir, f.Filename)
	i, err := os.Lstat(path)
	if err != nil {
		return err
	}
	f.CreatedAt = i.ModTime().UTC()
	f.CreatedReadable = humanize.Time(f.CreatedAt)
	f.Bytes = i.Size()
	f.BytesReadable = humanize.Bytes(uint64(f.Bytes))

	//i, err = os.Lstat(f.TagDir)
	//if err != nil {
	//	return err
	//}
	//f.ExpiresAt = i.ModTime().UTC().Add(time.Duration(expiration) * time.Second)
	//f.ExpiresReadable = humanize.Time(f.ExpiresAt)
	return nil
}

func (f *File) Remove() error {
	if f.TagDir == "" {
		return errors.New("Tag dir is not set")
	}

	if !isDir(f.TagDir) {
		return errors.New("Tag dir does not exist")
	}

	path := filepath.Join(f.TagDir, f.Filename)

	err := os.Remove(path)
	return err
}

func (f *File) WriteTempfile(d io.Reader, tempdir string) error {
	fp, err := ioutil.TempFile(tempdir, "upload")
	if err != nil {
		return err
	}
	f.Tempfile = fp.Name()

	f.Bytes, err = io.Copy(fp, d)
	if err != nil {
		return err
	}

	fp.Sync()

	// Store the tempfile path for later
	defer fp.Close()
	return nil
}

func (f *File) calculateSHA256(path string) error {
	var err error
	var result []byte
    	fp, err := os.Open(path)
    	if err != nil {
    	    return err
    	}
    	defer fp.Close()

    	hash := sha256.New()
    	_, err = io.Copy(hash, fp)
	if err != nil {
    	    return err
    	}
	f.Checksum = hex.EncodeToString(hash.Sum(result))
	f.Algorithm = "SHA256"
	return nil
}

func (f *File) VerifySHA256(s string) error {
	var err error
	if f.Checksum == "" {
		err = f.calculateSHA256(f.Tempfile)
		if err != nil {
			return err
		}
	}

	if s == "" {
		f.Verified = false
		return nil
	}

	if f.Checksum == s {
		f.Verified = true
		return nil
	}

	return errors.New("Checksum " + s + " did not match " + f.Checksum)
}

func (f *File) ParseExif() error {
	fpath := filepath.Join(f.TagDir, f.Filename)
	if f.Tempfile != "" {
		fpath = f.Tempfile
	}
	fp, err := os.Open(fpath)
	if err != nil {
		return err
	}
	defer fp.Close()

	f.Exif, err = exif.Decode(fp)
	if err != nil {
		return err
	}
	return nil
}

func (f *File) ExtractDateTime() error {
	var err error
	f.DateTime, err = f.Exif.DateTime()
	return err
}

func (f *File) ExtractLocationInfo() error {
	var err error
	f.Latitude, f.Longitude, err = f.Exif.LatLong()
	return err
}

func (f *File) Publish() error {
	err := CopyFile(f.Tempfile, filepath.Join(f.TagDir, f.Filename))
	return err
}

func (f *File) ClearTemp() error {
	err := os.Remove(f.Tempfile)
	return err
}

func (f *File) ThumbnailPath() string {
	return filepath.Join(f.TagDir, ".cache", "thumb-" + f.Filename)
}

func (f *File) GenerateThumbnail() error {
	fpath := filepath.Join(f.TagDir, f.Filename)

	s, err := imaging.Open(fpath)
	if err != nil {
		return err
	}

	thumb := imaging.Resize(s, 75, 75, imaging.CatmullRom)
	err = imaging.Save(thumb, f.ThumbnailPath())
	return err
}

//func (f *File) GenerateTag() error {
//        var tag = randomString(16)
//        err := f.SetTag(tag)
//        return err
//}

func isDir(path string) bool {
	fi, err := os.Stat(path)
	if err != nil {
		return false
	}
	if fi.IsDir() {
		return true
	} else {
		return false
	}
}

func isFile(path string) bool {
	fi, err := os.Stat(path)
	if err != nil {
		return false
	}
	if fi.IsDir() {
		return false
	} else {
		return true
	}
}

// http://stackoverflow.com/a/21067803
// CopyFile copies a file from src to dst. If src and dst files exist, and are
// the same, then return success. Otherise, attempt to create a hard link
// between the two files. If that fail, copy the file contents from src to dst.
func CopyFile(src, dst string) (err error) {
	sfi, err := os.Stat(src)
	if err != nil {
		return
	}
	if !sfi.Mode().IsRegular() {
		// cannot copy non-regular files (e.g., directories,
		// symlinks, devices, etc.)
		return errors.New("CopyFile: non-regular source file " + sfi.Name() + ": " + sfi.Mode().String())
	}
	dfi, err := os.Stat(dst)
	if err != nil {
		if !os.IsNotExist(err) {
			return
		}
	} else {
		if !(dfi.Mode().IsRegular()) {
			return errors.New("CopyFile: non-regular destination file " + dfi.Name() + ": " + dfi.Mode().String())
		}
		if os.SameFile(sfi, dfi) {
			return
		}
	}
	if err = os.Link(src, dst); err == nil {
		return
	}
	err = copyFileContents(src, dst)
	return err
}

// copyFileContents copies the contents of the file named src to the file named
// by dst. The file will be created if it does not already exist. If the
// destination file exists, all it's contents will be replaced by the contents
// of the source file.
func copyFileContents(src, dst string) (err error) {
	in, err := os.Open(src)
	if err != nil {
		return
	}
	defer in.Close()
	out, err := os.Create(dst)
	if err != nil {
		return
	}
	defer func() {
		cerr := out.Close()
		if err == nil {
			err = cerr
		}
	}()
	_, err = io.Copy(out, in)
	if err != nil {
		return
	}
	err = out.Sync()
	return err
}
<commit_msg>Crop thumbnails to keep aspect ratio<commit_after>package model

import (
	"crypto/sha256"
	"encoding/hex"
	"errors"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"time"

        "github.com/dustin/go-humanize"
	"github.com/rwcarlsen/goexif/exif"
	"github.com/disintegration/imaging"
)

type File struct {
	Filename		string		`json:"filename"`
	Tag			string		`json:"tag"`
	TagDir			string		`json:"-"`
	Bytes			int64		`json:"bytes"`
	BytesReadable		string		`json:"-"`
	MIME			string		`json:"mime"`
	CreatedReadable		string		`json:"created"`
	CreatedAt		time.Time	`json:"-"`
	Links			[]Link		`json:"links"`
	Checksum		string		`json:"checksum"`
	Algorithm		string		`json:"algorithm"`
	Verified		bool		`json:"verified"`
	RemoteAddr		string		`json:"-"`
	UserAgent		string		`json:"-"`
	Tempfile		string		`json:"-"`
	Extra			interface{}	`json:"extra"`

	// Image specific attributes
        DateTime                time.Time       `json:"-"`
        Longitude               float64         `json:"-"`
        Latitude                float64         `json:"-"`
        Altitude                string          `json:"-"`
        Thumbnail               bool            `json:"-"`
        Exif                    *exif.Exif      `json:"-"`
}

func (f *File) SetTag(s string) error {
        validTag := regexp.MustCompile("^[a-zA-Z0-9-_]{8,}$")
        if validTag.MatchString(s) == false {
                return errors.New("Invalid tag specified. It contains " +
                        "illegal characters or is too short")
        }
        f.Tag = s
        return nil
}

func (f *File) SetTagDir(filedir string) error {
	if f.Tag == "" {
		return errors.New("Tag not set.")
	}
        f.TagDir = filepath.Join(filedir, f.Tag)
	return nil
}

func (f *File) SetFilename(s string) error {
	// Remove all but valid chars
	var valid = regexp.MustCompile("[^A-Za-z0-9-_=,.]")
	var safe = valid.ReplaceAllString(s, "_")
	if safe == "" {
		return errors.New("Invalid filename specified. It contains " +
			"illegal characters or is too short.")
	}

	// Reject illegal filenames
	switch safe {
		case ".", "..":
			return errors.New("Invalid filename specified.")
	}

	// Set filename to the safe variant
	f.Filename = safe

	return nil
}

func (f *File) DetectMIME() error {
	var err error
	if f.TagDir == "" {
		return errors.New("TagDir not set.")
	}

        fpath := filepath.Join(f.TagDir, f.Filename)
        if f.Tempfile != "" {
                fpath = filepath.Join(f.Tempfile)
        }

	fp, err := os.Open(fpath)
	if err != nil {
		return err
	}
	defer fp.Close()
	buffer := make([]byte, 512)
	_, err = fp.Seek(0, 0)
	if err != nil {
		return err
	}
	_, err = fp.Read(buffer)
	if err != nil {
		return err
	}
	f.MIME = http.DetectContentType(buffer)
	return nil
}

func (f *File) MediaType() string {
        s := regexp.MustCompile("/").Split(f.MIME, 2)
        if len(s) > 0 {
                return s[0]
        }
        return ""
}

func (f *File) GenerateLinks(baseurl string) {
	fileLink := Link {}
	fileLink.Rel = "file"
	fileLink.Href = baseurl + "/" + f.Tag + "/" + f.Filename
	f.Links = append(f.Links, fileLink)

	tagLink := Link {}
	tagLink.Rel = "tag"
	tagLink.Href = baseurl + "/" + f.Tag
	f.Links = append(f.Links, tagLink)

	if f.ThumbnailExists() {
		thumbLink := Link {}
		thumbLink.Rel = "thumbnail"
		thumbLink.Href = baseurl + "/" + f.Tag + "/" + f.Filename + "?size=thumbnail"
		f.Links = append(f.Links, thumbLink)
	}
}

func (f *File) EnsureTagDirectoryExists() error {
	var err error

	// Tag directory
	if !isDir(f.TagDir) {
		err = os.Mkdir(f.TagDir, 0700)
		if err != nil {
			return err
		}
	}

	// Tag specific cache directory
	cpath := filepath.Join(f.TagDir, ".cache")
	if !isDir(cpath) {
		err = os.Mkdir(cpath, 0700)
		if err != nil {
			return err
		}
	}
	return err
}

func (f *File) Exists() bool {
	if f.TagDir == "" {
		return false
	}

	if f.Filename == "" {
		return false
	}

	path := filepath.Join(f.TagDir, f.Filename)
	if isFile(path) {
		return true
	}
	return false
}

func (f *File) ThumbnailExists() bool {
        path := f.ThumbnailPath()
	if isFile(path) {
		return true
	}
	return false
}

func (f *File) StatInfo() error {
	if isDir(f.TagDir) == false {
		return errors.New("Tag does not exist.")
	}

	path := filepath.Join(f.TagDir, f.Filename)
	i, err := os.Lstat(path)
	if err != nil {
		return err
	}
	f.CreatedAt = i.ModTime().UTC()
	f.CreatedReadable = humanize.Time(f.CreatedAt)
	f.Bytes = i.Size()
	f.BytesReadable = humanize.Bytes(uint64(f.Bytes))

	//i, err = os.Lstat(f.TagDir)
	//if err != nil {
	//	return err
	//}
	//f.ExpiresAt = i.ModTime().UTC().Add(time.Duration(expiration) * time.Second)
	//f.ExpiresReadable = humanize.Time(f.ExpiresAt)
	return nil
}

func (f *File) Remove() error {
	if f.TagDir == "" {
		return errors.New("Tag dir is not set")
	}

	if !isDir(f.TagDir) {
		return errors.New("Tag dir does not exist")
	}

	path := filepath.Join(f.TagDir, f.Filename)

	err := os.Remove(path)
	return err
}

func (f *File) WriteTempfile(d io.Reader, tempdir string) error {
	fp, err := ioutil.TempFile(tempdir, "upload")
	if err != nil {
		return err
	}
	f.Tempfile = fp.Name()

	f.Bytes, err = io.Copy(fp, d)
	if err != nil {
		return err
	}

	fp.Sync()

	// Store the tempfile path for later
	defer fp.Close()
	return nil
}

func (f *File) calculateSHA256(path string) error {
	var err error
	var result []byte
    	fp, err := os.Open(path)
    	if err != nil {
    	    return err
    	}
    	defer fp.Close()

    	hash := sha256.New()
    	_, err = io.Copy(hash, fp)
	if err != nil {
    	    return err
    	}
	f.Checksum = hex.EncodeToString(hash.Sum(result))
	f.Algorithm = "SHA256"
	return nil
}

func (f *File) VerifySHA256(s string) error {
	var err error
	if f.Checksum == "" {
		err = f.calculateSHA256(f.Tempfile)
		if err != nil {
			return err
		}
	}

	if s == "" {
		f.Verified = false
		return nil
	}

	if f.Checksum == s {
		f.Verified = true
		return nil
	}

	return errors.New("Checksum " + s + " did not match " + f.Checksum)
}

func (f *File) ParseExif() error {
	fpath := filepath.Join(f.TagDir, f.Filename)
	if f.Tempfile != "" {
		fpath = f.Tempfile
	}
	fp, err := os.Open(fpath)
	if err != nil {
		return err
	}
	defer fp.Close()

	f.Exif, err = exif.Decode(fp)
	if err != nil {
		return err
	}
	return nil
}

func (f *File) ExtractDateTime() error {
	var err error
	f.DateTime, err = f.Exif.DateTime()
	return err
}

func (f *File) ExtractLocationInfo() error {
	var err error
	f.Latitude, f.Longitude, err = f.Exif.LatLong()
	return err
}

func (f *File) Publish() error {
	err := CopyFile(f.Tempfile, filepath.Join(f.TagDir, f.Filename))
	return err
}

func (f *File) ClearTemp() error {
	err := os.Remove(f.Tempfile)
	return err
}

func (f *File) ThumbnailPath() string {
	return filepath.Join(f.TagDir, ".cache", "thumb-" + f.Filename)
}

func (f *File) GenerateThumbnail() error {
	fpath := filepath.Join(f.TagDir, f.Filename)

	s, err := imaging.Open(fpath)
	if err != nil {
		return err
	}

	thumb := imaging.Fill(s, 75, 75, imaging.Center, imaging.Lanczos)
	err = imaging.Save(thumb, f.ThumbnailPath())
	return err
}

//func (f *File) GenerateTag() error {
//        var tag = randomString(16)
//        err := f.SetTag(tag)
//        return err
//}

func isDir(path string) bool {
	fi, err := os.Stat(path)
	if err != nil {
		return false
	}
	if fi.IsDir() {
		return true
	} else {
		return false
	}
}

func isFile(path string) bool {
	fi, err := os.Stat(path)
	if err != nil {
		return false
	}
	if fi.IsDir() {
		return false
	} else {
		return true
	}
}

// http://stackoverflow.com/a/21067803
// CopyFile copies a file from src to dst. If src and dst files exist, and are
// the same, then return success. Otherise, attempt to create a hard link
// between the two files. If that fail, copy the file contents from src to dst.
func CopyFile(src, dst string) (err error) {
	sfi, err := os.Stat(src)
	if err != nil {
		return
	}
	if !sfi.Mode().IsRegular() {
		// cannot copy non-regular files (e.g., directories,
		// symlinks, devices, etc.)
		return errors.New("CopyFile: non-regular source file " + sfi.Name() + ": " + sfi.Mode().String())
	}
	dfi, err := os.Stat(dst)
	if err != nil {
		if !os.IsNotExist(err) {
			return
		}
	} else {
		if !(dfi.Mode().IsRegular()) {
			return errors.New("CopyFile: non-regular destination file " + dfi.Name() + ": " + dfi.Mode().String())
		}
		if os.SameFile(sfi, dfi) {
			return
		}
	}
	if err = os.Link(src, dst); err == nil {
		return
	}
	err = copyFileContents(src, dst)
	return err
}

// copyFileContents copies the contents of the file named src to the file named
// by dst. The file will be created if it does not already exist. If the
// destination file exists, all it's contents will be replaced by the contents
// of the source file.
func copyFileContents(src, dst string) (err error) {
	in, err := os.Open(src)
	if err != nil {
		return
	}
	defer in.Close()
	out, err := os.Create(dst)
	if err != nil {
		return
	}
	defer func() {
		cerr := out.Close()
		if err == nil {
			err = cerr
		}
	}()
	_, err = io.Copy(out, in)
	if err != nil {
		return
	}
	err = out.Sync()
	return err
}
<|endoftext|>


<commit_before>// Copyright (c) 2012-2018 Ugorji Nwoke. All rights reserved.
// Use of this source code is governed by a MIT license found in the LICENSE file.

package codec

import (
	"bufio"
	"errors"
	"io"
	"net/rpc"
	"sync"
)

// Rpc provides a rpc Server or Client Codec for rpc communication.
type Rpc interface {
	ServerCodec(conn io.ReadWriteCloser, h Handle) rpc.ServerCodec
	ClientCodec(conn io.ReadWriteCloser, h Handle) rpc.ClientCodec
}

// RPCOptions holds options specific to rpc functionality
type RPCOptions struct {
	// RPCNoBuffer configures whether we attempt to buffer reads and writes during RPC calls.
	//
	// Set RPCNoBuffer=true to turn buffering off.
	// Buffering can still be done if buffered connections are passed in, or
	// buffering is configured on the handle.
	RPCNoBuffer bool
}

// rpcCodec defines the struct members and common methods.
type rpcCodec struct {
	c io.Closer
	r io.Reader
	w io.Writer
	f ioFlusher

	dec *Decoder
	enc *Encoder
	// bw  *bufio.Writer
	// br  *bufio.Reader
	mu sync.Mutex
	h  Handle

	cls    bool
	clsmu  sync.RWMutex
	clsErr error
}

func newRPCCodec(conn io.ReadWriteCloser, h Handle) rpcCodec {
	// return newRPCCodec2(bufio.NewReader(conn), bufio.NewWriter(conn), conn, h)
	return newRPCCodec2(conn, conn, conn, h)
}

func newRPCCodec2(r io.Reader, w io.Writer, c io.Closer, h Handle) rpcCodec {
	// defensive: ensure that jsonH has TermWhitespace turned on.
	if jsonH, ok := h.(*JsonHandle); ok && !jsonH.TermWhitespace {
		panic(errors.New("rpc requires a JsonHandle with TermWhitespace set to true"))
	}
	// always ensure that we use a flusher, and always flush what was written to the connection.
	// we lose nothing by using a buffered writer internally.
	f, ok := w.(ioFlusher)
	bh := h.getBasicHandle()
	if !bh.RPCNoBuffer {
		if bh.WriterBufferSize <= 0 {
			if !ok {
				bw := bufio.NewWriter(w)
				f, w = bw, bw
			}
		}
		if bh.ReaderBufferSize <= 0 {
			if _, ok = w.(ioPeeker); !ok {
				if _, ok = w.(ioBuffered); !ok {
					br := bufio.NewReader(r)
					r = br
				}
			}
		}
	}
	return rpcCodec{
		c:   c,
		w:   w,
		r:   r,
		f:   f,
		h:   h,
		enc: NewEncoder(w, h),
		dec: NewDecoder(r, h),
	}
}

func (c *rpcCodec) write(obj1, obj2 interface{}, writeObj2 bool) (err error) {
	if c.isClosed() {
		return c.clsErr
	}
	err = c.enc.Encode(obj1)
	if err == nil {
		if writeObj2 {
			err = c.enc.Encode(obj2)
		}
		if err == nil && c.f != nil {
			err = c.f.Flush()
		}
	}
	return
}

func (c *rpcCodec) swallow(err *error) {
	defer panicToErr(c.dec, err)
	c.dec.swallow()
}

func (c *rpcCodec) read(obj interface{}) (err error) {
	if c.isClosed() {
		return c.clsErr
	}
	//If nil is passed in, we should read and discard
	if obj == nil {
		// var obj2 interface{}
		// return c.dec.Decode(&obj2)
		c.swallow(&err)
		return
	}
	return c.dec.Decode(obj)
}

func (c *rpcCodec) isClosed() (b bool) {
	if c.c != nil {
		c.clsmu.RLock()
		b = c.cls
		c.clsmu.RUnlock()
	}
	return
}

func (c *rpcCodec) Close() error {
	if c.c == nil || c.isClosed() {
		return c.clsErr
	}
	c.clsmu.Lock()
	c.cls = true
	var fErr error
	if c.f != nil {
		fErr = c.f.Flush()
	}
	_ = fErr
	c.clsErr = c.c.Close()
	if c.clsErr == nil && fErr != nil {
		c.clsErr = fErr
	}
	c.clsmu.Unlock()
	return c.clsErr
}

func (c *rpcCodec) ReadResponseBody(body interface{}) error {
	return c.read(body)
}

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

type goRpcCodec struct {
	rpcCodec
}

func (c *goRpcCodec) WriteRequest(r *rpc.Request, body interface{}) error {
	// Must protect for concurrent access as per API
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.write(r, body, true)
}

func (c *goRpcCodec) WriteResponse(r *rpc.Response, body interface{}) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.write(r, body, true)
}

func (c *goRpcCodec) ReadResponseHeader(r *rpc.Response) error {
	return c.read(r)
}

func (c *goRpcCodec) ReadRequestHeader(r *rpc.Request) error {
	return c.read(r)
}

func (c *goRpcCodec) ReadRequestBody(body interface{}) error {
	return c.read(body)
}

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

// goRpc is the implementation of Rpc that uses the communication protocol
// as defined in net/rpc package.
type goRpc struct{}

// GoRpc implements Rpc using the communication protocol defined in net/rpc package.
//
// Note: network connection (from net.Dial, of type io.ReadWriteCloser) is not buffered.
//
// For performance, you should configure WriterBufferSize and ReaderBufferSize on the handle.
// This ensures we use an adequate buffer during reading and writing.
// If not configured, we will internally initialize and use a buffer during reads and writes.
// This can be turned off via the RPCNoBuffer option on the Handle.
//   var handle codec.JsonHandle
//   handle.RPCNoBuffer = true // turns off attempt by rpc module to initialize a buffer
//
// Example 1: one way of configuring buffering explicitly:
//   var handle codec.JsonHandle // codec handle
//   handle.ReaderBufferSize = 1024
//   handle.WriterBufferSize = 1024
//   var conn io.ReadWriteCloser // connection got from a socket
//   var serverCodec = GoRpc.ServerCodec(conn, handle)
//   var clientCodec = GoRpc.ClientCodec(conn, handle)
//
// Example 2: you can also explicitly create a buffered connection yourself,
// and not worry about configuring the buffer sizes in the Handle.
//   var handle codec.Handle     // codec handle
//   var conn io.ReadWriteCloser // connection got from a socket
//   var bufconn = struct {      // bufconn here is a buffered io.ReadWriteCloser
//       io.Closer
//       *bufio.Reader
//       *bufio.Writer
//   }{conn, bufio.NewReader(conn), bufio.NewWriter(conn)}
//   var serverCodec = GoRpc.ServerCodec(bufconn, handle)
//   var clientCodec = GoRpc.ClientCodec(bufconn, handle)
//
var GoRpc goRpc

func (x goRpc) ServerCodec(conn io.ReadWriteCloser, h Handle) rpc.ServerCodec {
	return &goRpcCodec{newRPCCodec(conn, h)}
}

func (x goRpc) ClientCodec(conn io.ReadWriteCloser, h Handle) rpc.ClientCodec {
	return &goRpcCodec{newRPCCodec(conn, h)}
}
<commit_msg>codec: rpc: Flush is only and always called within write (never by Close)<commit_after>// Copyright (c) 2012-2018 Ugorji Nwoke. All rights reserved.
// Use of this source code is governed by a MIT license found in the LICENSE file.

package codec

import (
	"bufio"
	"errors"
	"io"
	"net/rpc"
	"sync"
)

// Rpc provides a rpc Server or Client Codec for rpc communication.
type Rpc interface {
	ServerCodec(conn io.ReadWriteCloser, h Handle) rpc.ServerCodec
	ClientCodec(conn io.ReadWriteCloser, h Handle) rpc.ClientCodec
}

// RPCOptions holds options specific to rpc functionality
type RPCOptions struct {
	// RPCNoBuffer configures whether we attempt to buffer reads and writes during RPC calls.
	//
	// Set RPCNoBuffer=true to turn buffering off.
	// Buffering can still be done if buffered connections are passed in, or
	// buffering is configured on the handle.
	RPCNoBuffer bool
}

// rpcCodec defines the struct members and common methods.
type rpcCodec struct {
	c io.Closer
	r io.Reader
	w io.Writer
	f ioFlusher

	dec *Decoder
	enc *Encoder
	// bw  *bufio.Writer
	// br  *bufio.Reader
	mu sync.Mutex
	h  Handle

	cls    bool
	clsmu  sync.RWMutex
	clsErr error
}

func newRPCCodec(conn io.ReadWriteCloser, h Handle) rpcCodec {
	// return newRPCCodec2(bufio.NewReader(conn), bufio.NewWriter(conn), conn, h)
	return newRPCCodec2(conn, conn, conn, h)
}

func newRPCCodec2(r io.Reader, w io.Writer, c io.Closer, h Handle) rpcCodec {
	// defensive: ensure that jsonH has TermWhitespace turned on.
	if jsonH, ok := h.(*JsonHandle); ok && !jsonH.TermWhitespace {
		panic(errors.New("rpc requires a JsonHandle with TermWhitespace set to true"))
	}
	// always ensure that we use a flusher, and always flush what was written to the connection.
	// we lose nothing by using a buffered writer internally.
	f, ok := w.(ioFlusher)
	bh := h.getBasicHandle()
	if !bh.RPCNoBuffer {
		if bh.WriterBufferSize <= 0 {
			if !ok {
				bw := bufio.NewWriter(w)
				f, w = bw, bw
			}
		}
		if bh.ReaderBufferSize <= 0 {
			if _, ok = w.(ioPeeker); !ok {
				if _, ok = w.(ioBuffered); !ok {
					br := bufio.NewReader(r)
					r = br
				}
			}
		}
	}
	return rpcCodec{
		c:   c,
		w:   w,
		r:   r,
		f:   f,
		h:   h,
		enc: NewEncoder(w, h),
		dec: NewDecoder(r, h),
	}
}

func (c *rpcCodec) write(obj1, obj2 interface{}, writeObj2 bool) (err error) {
	if c.isClosed() {
		return c.clsErr
	}
	err = c.enc.Encode(obj1)
	if err == nil {
		if writeObj2 {
			err = c.enc.Encode(obj2)
		}
		// if err == nil && c.f != nil {
		// 	err = c.f.Flush()
		// }
	}
	if c.f != nil {
		if err == nil {
			err = c.f.Flush()
		} else {
			c.f.Flush()
		}
	}
	return
}

func (c *rpcCodec) swallow(err *error) {
	defer panicToErr(c.dec, err)
	c.dec.swallow()
}

func (c *rpcCodec) read(obj interface{}) (err error) {
	if c.isClosed() {
		return c.clsErr
	}
	//If nil is passed in, we should read and discard
	if obj == nil {
		// var obj2 interface{}
		// return c.dec.Decode(&obj2)
		c.swallow(&err)
		return
	}
	return c.dec.Decode(obj)
}

func (c *rpcCodec) isClosed() (b bool) {
	if c.c != nil {
		c.clsmu.RLock()
		b = c.cls
		c.clsmu.RUnlock()
	}
	return
}

func (c *rpcCodec) Close() error {
	if c.c == nil || c.isClosed() {
		return c.clsErr
	}
	c.clsmu.Lock()
	c.cls = true
	// var fErr error
	// if c.f != nil {
	// 	fErr = c.f.Flush()
	// }
	// _ = fErr
	// c.clsErr = c.c.Close()
	// if c.clsErr == nil && fErr != nil {
	// 	c.clsErr = fErr
	// }
	c.clsErr = c.c.Close()
	c.clsmu.Unlock()
	return c.clsErr
}

func (c *rpcCodec) ReadResponseBody(body interface{}) error {
	return c.read(body)
}

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

type goRpcCodec struct {
	rpcCodec
}

func (c *goRpcCodec) WriteRequest(r *rpc.Request, body interface{}) error {
	// Must protect for concurrent access as per API
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.write(r, body, true)
}

func (c *goRpcCodec) WriteResponse(r *rpc.Response, body interface{}) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.write(r, body, true)
}

func (c *goRpcCodec) ReadResponseHeader(r *rpc.Response) error {
	return c.read(r)
}

func (c *goRpcCodec) ReadRequestHeader(r *rpc.Request) error {
	return c.read(r)
}

func (c *goRpcCodec) ReadRequestBody(body interface{}) error {
	return c.read(body)
}

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

// goRpc is the implementation of Rpc that uses the communication protocol
// as defined in net/rpc package.
type goRpc struct{}

// GoRpc implements Rpc using the communication protocol defined in net/rpc package.
//
// Note: network connection (from net.Dial, of type io.ReadWriteCloser) is not buffered.
//
// For performance, you should configure WriterBufferSize and ReaderBufferSize on the handle.
// This ensures we use an adequate buffer during reading and writing.
// If not configured, we will internally initialize and use a buffer during reads and writes.
// This can be turned off via the RPCNoBuffer option on the Handle.
//   var handle codec.JsonHandle
//   handle.RPCNoBuffer = true // turns off attempt by rpc module to initialize a buffer
//
// Example 1: one way of configuring buffering explicitly:
//   var handle codec.JsonHandle // codec handle
//   handle.ReaderBufferSize = 1024
//   handle.WriterBufferSize = 1024
//   var conn io.ReadWriteCloser // connection got from a socket
//   var serverCodec = GoRpc.ServerCodec(conn, handle)
//   var clientCodec = GoRpc.ClientCodec(conn, handle)
//
// Example 2: you can also explicitly create a buffered connection yourself,
// and not worry about configuring the buffer sizes in the Handle.
//   var handle codec.Handle     // codec handle
//   var conn io.ReadWriteCloser // connection got from a socket
//   var bufconn = struct {      // bufconn here is a buffered io.ReadWriteCloser
//       io.Closer
//       *bufio.Reader
//       *bufio.Writer
//   }{conn, bufio.NewReader(conn), bufio.NewWriter(conn)}
//   var serverCodec = GoRpc.ServerCodec(bufconn, handle)
//   var clientCodec = GoRpc.ClientCodec(bufconn, handle)
//
var GoRpc goRpc

func (x goRpc) ServerCodec(conn io.ReadWriteCloser, h Handle) rpc.ServerCodec {
	return &goRpcCodec{newRPCCodec(conn, h)}
}

func (x goRpc) ClientCodec(conn io.ReadWriteCloser, h Handle) rpc.ClientCodec {
	return &goRpcCodec{newRPCCodec(conn, h)}
}
<|endoftext|>


<commit_before>package http

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/fsamin/go-dump"
	"github.com/mitchellh/mapstructure"

	"github.com/runabove/venom"
)

// Name of executor
const Name = "http"

// New returns a new Executor
func New() venom.Executor {
	return &Executor{}
}

// Headers represents header HTTP for Request
type Headers map[string]string

// Executor struct
type Executor struct {
	Method  string  `json:"method" yaml:"method"`
	URL     string  `json:"url" yaml:"url"`
	Path    string  `json:"path" yaml:"path"`
	Body    string  `json:"body" yaml:"body"`
	Headers Headers `json:"headers" yaml:"headers"`
}

// Result represents a step result
type Result struct {
	Executor    Executor `json:"executor,omitempty" yaml:"executor,omitempty"`
	TimeSeconds float64  `json:"timeSeconds,omitempty" yaml:"timeSeconds,omitempty"`
	TimeHuman   string   `json:"timeHuman,omitempty" yaml:"timeHuman,omitempty"`
	StatusCode  int      `json:"statusCode,omitempty" yaml:"statusCode,omitempty"`
	Body        string   `json:"body,omitempty" yaml:"body,omitempty"`
	Headers     Headers  `json:"headers,omitempty" yaml:"headers,omitempty"`
	Err         error    `json:"error,omitempty" yaml:"error,omitempty"`
}

// GetDefaultAssertions return default assertions for this executor
// Optional
func (Executor) GetDefaultAssertions() venom.StepAssertions {
	return venom.StepAssertions{Assertions: []string{"result.code ShouldEqual 0"}}
}

// Run execute TestStep
func (Executor) Run(l *log.Entry, aliases venom.Aliases, step venom.TestStep) (venom.ExecutorResult, error) {

	// transform step to Executor Instance
	var t Executor
	if err := mapstructure.Decode(step, &t); err != nil {
		return nil, err
	}

	r := Result{Executor: t}
	var body io.Reader

	path := t.URL + t.Path
	method := t.Method
	if method == "" {
		method = "GET"
	}
	req, err := http.NewRequest(method, path, body)
	if err != nil {
		return nil, err
	}

	for k, v := range t.Headers {
		req.Header.Set(k, v)
	}

	start := time.Now()
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	elapsed := time.Since(start)
	r.TimeSeconds = elapsed.Seconds()
	r.TimeHuman = fmt.Sprintf("%s", elapsed)

	var bb []byte
	if resp.Body != nil {
		defer resp.Body.Close()
		var errr error
		bb, errr = ioutil.ReadAll(resp.Body)
		if errr != nil {
			return nil, errr
		}
		r.Body = string(bb)
	}

	r.Headers = make(map[string]string)

	for k, v := range resp.Header {
		r.Headers[k] = v[0]
	}

	r.StatusCode = resp.StatusCode

	return dump.ToMap(r, dump.WithDefaultLowerCaseFormatter())
}
<commit_msg>feat(executorHTTP): bodyJSON dump<commit_after>package http

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"reflect"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/fsamin/go-dump"
	"github.com/mitchellh/mapstructure"

	"github.com/runabove/venom"
)

// Name of executor
const Name = "http"

// New returns a new Executor
func New() venom.Executor {
	return &Executor{}
}

// Headers represents header HTTP for Request
type Headers map[string]string

// Executor struct
type Executor struct {
	Method  string  `json:"method" yaml:"method"`
	URL     string  `json:"url" yaml:"url"`
	Path    string  `json:"path" yaml:"path"`
	Body    string  `json:"body" yaml:"body"`
	Headers Headers `json:"headers" yaml:"headers"`
}

// Result represents a step result
type Result struct {
	Executor    Executor    `json:"executor,omitempty" yaml:"executor,omitempty"`
	TimeSeconds float64     `json:"timeSeconds,omitempty" yaml:"timeSeconds,omitempty"`
	TimeHuman   string      `json:"timeHuman,omitempty" yaml:"timeHuman,omitempty"`
	StatusCode  int         `json:"statusCode,omitempty" yaml:"statusCode,omitempty"`
	Body        string      `json:"body,omitempty" yaml:"body,omitempty"`
	BodyJSON    interface{} `json:"bodyjson,omitempty" yaml:"bodyjson,omitempty"`
	Headers     Headers     `json:"headers,omitempty" yaml:"headers,omitempty"`
	Err         error       `json:"error,omitempty" yaml:"error,omitempty"`
}

// GetDefaultAssertions return default assertions for this executor
// Optional
func (Executor) GetDefaultAssertions() venom.StepAssertions {
	return venom.StepAssertions{Assertions: []string{"result.code ShouldEqual 0"}}
}

// Run execute TestStep
func (Executor) Run(l *log.Entry, aliases venom.Aliases, step venom.TestStep) (venom.ExecutorResult, error) {

	// transform step to Executor Instance
	var t Executor
	if err := mapstructure.Decode(step, &t); err != nil {
		return nil, err
	}

	r := Result{Executor: t}
	var body io.Reader

	path := t.URL + t.Path
	method := t.Method
	if method == "" {
		method = "GET"
	}
	req, err := http.NewRequest(method, path, body)
	if err != nil {
		return nil, err
	}

	for k, v := range t.Headers {
		req.Header.Set(k, v)
	}

	start := time.Now()
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	elapsed := time.Since(start)
	r.TimeSeconds = elapsed.Seconds()
	r.TimeHuman = fmt.Sprintf("%s", elapsed)

	var bb []byte
	if resp.Body != nil {
		defer resp.Body.Close()
		var errr error
		bb, errr = ioutil.ReadAll(resp.Body)
		if errr != nil {
			return nil, errr
		}
		r.Body = string(bb)

		bodyJSONArray := []interface{}{}
		if err := json.Unmarshal(bb, &bodyJSONArray); err != nil {
			bodyJSONMap := map[string]interface{}{}
			if err2 := json.Unmarshal(bb, &bodyJSONMap); err2 == nil {
				r.BodyJSON = bodyJSONMap
			}
		} else {
			r.BodyJSON = bodyJSONArray
		}
	}

	r.Headers = make(map[string]string)

	for k, v := range resp.Header {
		r.Headers[k] = v[0]
	}

	r.StatusCode = resp.StatusCode

	log.Errorf("-----> %v", reflect.ValueOf(r.BodyJSON).Type())
	log.Errorf("-----> %v", reflect.ValueOf(r.BodyJSON).Kind())

	return dump.ToMap(r, dump.WithDefaultLowerCaseFormatter())
}
<|endoftext|>


<commit_before>package crud

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

// Table 是对CRUD进一层的封装
type Table struct {
	*DataBase
	*Search
	tableName string
}

// Name 返回名称
func (t *Table) Name() string {
	return t.tableName
}

//
// All 返回这张表所有数据
// func (t *Table) All() RowsMap {
// 	return t.Query("SELECT * FROM " + t.tableName).RowsMap()
// }

// // Count 返回表有多少条数据
// func (t *Table) Count() int {
// 	return t.Query("SELECT COUNT(*) FROM " + t.tableName).Int()

// }

// UpdateTime 查找表的更新时间
func (t *Table) UpdateTime() string {
	return t.Query("SELECT `UPDATE_TIME` FROM  INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA =(select database()) AND TABLE_NAME = '" + t.tableName + "';").String()

}

// AutoIncrement 查找表的自增ID的值
func (t *Table) AutoIncrement() int {
	return t.Query("SELECT `AUTO_INCREMENT` FROM  INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA =(select database()) AND TABLE_NAME = '" + t.tableName + "';").Int()
}

// SetAutoIncrement 设置自动增长ID
func (t *Table) SetAutoIncrement(id int) error {
	_, err := t.Exec("ALTER TABLE `" + t.tableName + "` AUTO_INCREMENT = " + strconv.Itoa(id)).RowsAffected()
	return err
}

// MaxID 查找表的最大ID，如果为NULL的话则为0
func (t *Table) MaxID() int {
	return t.Query("SELECT IFNULL(MAX(id), 0) as id FROM `" + t.tableName + "`").Int()

}

// IDIn 查找多个ID对应的列
func (t *Table) IDIn(ids ...interface{}) *SQLRows {
	if len(ids) == 0 {
		return &SQLRows{}
	}
	return t.Query(fmt.Sprintf("SELECT * FROM %s WHERE id in (%s)", t.tableName, argslice(len(ids))), ids...)
}

// Create 创建
// check 如果有，则会判断表里面以这几个字段为唯一的话，数据库是否存在此条数据，如果有就不插入了。
// 所有ORM的底层。FormXXX， (*DataBase)CRUD
//
func (t *Table) Create(m map[string]interface{}, checks ...string) (int64, error) {
	//INSERT INTO `feedback` (`task_id`, `template_question_id`, `question_options_id`, `suggestion`, `member_id`) VALUES ('1', '1', '1', '1', '1')
	if len(checks) > 0 {
		names := []string{}
		values := []interface{}{}
		for _, check := range checks {
			names = append(names, "`"+check+"`"+" = ? ")
			values = append(values, m[check])
		}
		// SELECT COUNT(*) FROM `feedback` WHERE `task_id` = ? AND `member_id` = ?
		if t.Query(fmt.Sprintf("SELECT COUNT(*) FROM `%s` WHERE %s", t.tableName, strings.Join(names, "AND ")), values...).Int() > 0 {
			return 0, ErrInsertRepeat
		}
	}
	if t.tableColumns[t.tableName].HaveColumn(CreatedAt) {
		m[CreatedAt] = time.Now().Format(TimeFormat)
	}
	ks, vs := ksvs(m)
	id, err := t.Exec(fmt.Sprintf("INSERT INTO `%s` (%s) VALUES (%s)", t.tableName, strings.Join(ks, ","), argslice(len(ks))), vs...).LastInsertId()
	if err != nil {
		return 0, errors.New("SQL语句异常")
	}
	if id <= 0 {
		return 0, errors.New("插入数据库异常")
	}
	return id, nil
}

// CreateBatch create batch
func (t *Table) CreateBatch(ms []map[string]interface{}) (int, error) {
	return 0, nil
}

//Reads 查找
func (t *Table) Reads(m map[string]interface{}) RowsMap {
	if t.tableColumns[t.tableName].HaveColumn(IsDeleted) {
		m[IsDeleted] = 0
	}
	//SELECT * FROM address WHERE id = 1 AND uid = 27
	ks, vs := ksvs(m, " = ? ")
	return t.Query(fmt.Sprintf("SELECT * FROM %s WHERE %s", t.tableName, strings.Join(ks, "AND")), vs...).RowsMap()
}

func (t *Table) Read(m map[string]interface{}) map[string]string {
	rs := t.Reads(m)
	if len(rs) > 0 {
		return rs[0]
	}
	return map[string]string{}
}

// Update 更新
// 如果map里面有id的话会自动删除id，然后使用id来作为更新的条件。
func (t *Table) Update(m map[string]interface{}, keys ...string) error {
	if len(keys) == 0 {
		keys = append(keys, "id")
	}
	if t.tableColumns[t.tableName].HaveColumn(UpdatedAt) {
		m[UpdatedAt] = time.Now().Format(TimeFormat)
	}
	keysValue := []interface{}{}
	whereks := []string{}
	for _, key := range keys {
		val, ok := m[key]
		if !ok {
			return errors.New("没有更新主键")
		}
		keysValue = append(keysValue, val)
		delete(m, key)
		whereks = append(whereks, "`"+key+"` = ? ")
	}
	//因为在更新的时候最好不要更新ID，而有时候又会将ID传入进来，所以id每次都会被删除，如果要更新id的话使用Exec()
	delete(m, "id")
	ks, vs := ksvs(m, " = ? ")
	for _, val := range keysValue {
		vs = append(vs, val)
	}
	_, err := t.Exec(fmt.Sprintf("UPDATE `%s` SET %s WHERE %s LIMIT 1", t.tableName, strings.Join(ks, ","), strings.Join(whereks, "AND")), vs...).RowsAffected()
	if err != nil {
		return errors.New("SQL语句异常")
	}
	return nil
}

//CreateOrUpdate 创建或者更新
func (t *Table) CreateOrUpdate(m map[string]interface{}, keys ...string) error {
	_, err := t.Create(m, keys...)
	if err != nil {
		if err == ErrInsertRepeat {
			return t.Update(m, keys...)
		}
		return err
	}
	return nil
}

// Delete 删除
func (t *Table) Delete(m map[string]interface{}) (int64, error) {
	ks, vs := ksvs(m, " = ? ")
	if t.tableColumns[t.tableName].HaveColumn(IsDeleted) {
		return t.Exec(fmt.Sprintf("UPDATE `%s` SET is_deleted = '1', deleted_at = '%s' WHERE %s", t.tableName, time.Now().Format(TimeFormat), strings.Join(ks, "AND")), vs...).RowsAffected()
	}
	return t.Exec(fmt.Sprintf("DELETE FROM %s WHERE %s", t.tableName, strings.Join(ks, "AND")), vs...).RowsAffected()
}

// Clone 克隆
// 克隆要保证状态在每个链式操作后都是独立的。
func (t *Table) Clone() *Table {
	newTable := &Table{
		DataBase:  t.DataBase,
		tableName: t.tableName,
	}
	if t.Search == nil {
		newTable.Search = &Search{table: newTable, tableName: t.tableName}
	} else {
		newTable.Search = t.Search.Clone()
		newTable.Search.table = newTable
	}
	return newTable
}

// Where field = arg
func (t *Table) Where(query string, args ...interface{}) *Table {
	return t.Clone().Search.Where(query, args...).table
}

// WhereNotEmpty if arg empty,will do nothing
func (t *Table) WhereNotEmpty(query, arg string) *Table {
	if arg == "" {
		return t
	}
	return t.Clone().Search.Where(query, arg).table
}

// WhereStartEndDay DATE_FORMAT(field, '%Y-%m-%d') >= startTime AND DATE_FORMAT(field, '%Y-%m-%d') <= endTime
// if startDay == "", will do nothing
// if endDay == "", endDay = startDay
// '','' => return
// '2017-07-01', '' => '2017-07-01', '2017-07-01'
// '', '2017-07-02' => '','2017-07-02' (TODO)
// '2017-07-01','2017-07-02' => '2017-07-02','2017-07-01'
func (t *Table) WhereStartEndDay(field, startDay, endDay string) *Table {
	if startDay == "" && endDay == "" {
		return t
	}
	if startDay != "" && endDay == "" {
		endDay = startDay
	}
	return t.Clone().Search.Where("DATE_FORMAT("+field+",'%Y-%m-%d') >= ? AND DATE_FORMAT("+field+",'%Y-%m-%d') <= ?", startDay, endDay).table
}

// WhereStartEndMonth DATE_FORMAT(field, '%Y-%m') >= startMonth AND DATE_FORMAT(field, '%Y-%m') <= endMonth
// if startMonth == "", will do nothing
// if endMonth == "", endMonth = startMonth
func (t *Table) WhereStartEndMonth(field, startMonth, endMonth string) *Table {
	if startMonth == "" && endMonth == "" {
		return t
	}
	if startMonth != "" && endMonth == "" {
		endMonth = startMonth
	}
	return t.Clone().Search.Where("DATE_FORMAT("+field+",'%Y-%m') >= ? AND DATE_FORMAT("+field+",'%Y-%m') <= ?", startMonth, endMonth).table
}

// WhereStartEndTime DATE_FORMAT(field, '%H:%i') >= startTime AND DATE_FORMAT(field, '%H:%i') <= endTime
// if startTime == "", will do nothing
// if endTime == "", endTime = startTime
func (t *Table) WhereStartEndTime(field, startTime, endTime string) *Table {
	if startTime == "" && endTime == "" {
		return t
	}
	if startTime != "" && endTime == "" {
		endTime = startTime
	}
	return t.Clone().Search.Where("DATE_FORMAT("+field+",'%H:%i') >= ? AND DATE_FORMAT("+field+",'%H:%i') <= ?", startTime, endTime).table
}

// WhereToday DATE_FORMAT(field, '%Y-%m-%d') = {today}
func (t *Table) WhereToday(field string) *Table {
	return t.Clone().Search.Where("DATE_FORMAT("+field+",'%Y-%m-%d') = ?", time.Now().Format("2006-01-02")).table
}

// WhereDay DATE_FORMAT(field, '%Y-%m-%d') = day
func (t *Table) WhereDay(field, day string) *Table {
	return t.Clone().Search.Where("DATE_FORMAT("+field+",'%Y-%m-%d') = ?", day).table
}

// WhereMonth DATE_FORMAT(field, '%Y-%m') = month
func (t *Table) WhereMonth(field, month string) *Table {
	return t.Clone().Search.Where("DATE_FORMAT("+field+",'%Y-%m') = ?", month).table
}

// WhereBeforeToday DATE_FORMAT(field, '%Y-%m-%d') < {today}
func (t *Table) WhereBeforeToday(field string) *Table {
	return t.Clone().Search.Where("DATE_FORMAT("+field+",'%Y-%m-%d') < ?", time.Now().Format("2006-01-02")).table
}

// WhereLike field LIKE %like%
// If like == "", will do nothing.
func (t *Table) WhereLike(field, like string) *Table {
	if like == "" {
		return t
	}
	return t.Clone().Search.Where(field+" LIKE ?", "%"+like+"%").table
}

// WhereLikeLeft field LIKE %like
func (t *Table) WhereLikeLeft(field, like string) *Table {
	return t.Clone().Search.Where(field+" LIKE ?", "%"+like).table
}

// WhereLikeRight field LIKE like%
func (t *Table) WhereLikeRight(field, like string) *Table {
	return t.Clone().Search.Where(field+" LIKE ?", like+"%").table
}

// WhereID id = ?
func (t *Table) WhereID(id interface{}) *Table {
	return t.Clone().Search.WhereID(id).table
}

// In In(field, a,b,c)
func (t *Table) In(field string, args ...interface{}) *Table {
	return t.Clone().Search.In(field, args...).table
}

// Joins LEFT JOIN
// with auto join map
func (t *Table) Joins(query string, args ...string) *Table {
	return t.Clone().Search.Joins(query, args...).table
}

// OrderBy ORDER BY
func (t *Table) OrderBy(field string, isDESC ...bool) *Table {
	return t.Clone().Search.OrderBy(field, isDESC...).table
}

// Limit LIMIT
func (t *Table) Limit(n interface{}) *Table {
	return t.Clone().Search.Limit(n).table
}

// Fields fields
func (t *Table) Fields(args ...string) *Table {
	if len(args) == 0 {
		return t
	}
	return t.Clone().Search.Fields(args...).table
}

// FieldCount equal Fields("COUNT(*) AS total")
func (t *Table) FieldCount(as ...string) *Table {
	asWhat := "total"
	if len(as) > 0 {
		sp := strings.Split(as[0], " ")
		asWhat = sp[0]
	}
	return t.Clone().Search.Fields("COUNT(*) AS " + asWhat).table
}

// Group GROUP BY
func (t *Table) Group(fields ...string) *Table {
	return t.Clone().Search.Group(fields...).table
}
<commit_msg>一次创建多个列<commit_after>package crud

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

// Table 是对CRUD进一层的封装
type Table struct {
	*DataBase
	*Search
	tableName string
}

// Name 返回名称
func (t *Table) Name() string {
	return t.tableName
}

//
// All 返回这张表所有数据
// func (t *Table) All() RowsMap {
// 	return t.Query("SELECT * FROM " + t.tableName).RowsMap()
// }

// // Count 返回表有多少条数据
// func (t *Table) Count() int {
// 	return t.Query("SELECT COUNT(*) FROM " + t.tableName).Int()

// }

// UpdateTime 查找表的更新时间
func (t *Table) UpdateTime() string {
	return t.Query("SELECT `UPDATE_TIME` FROM  INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA =(select database()) AND TABLE_NAME = '" + t.tableName + "';").String()

}

// AutoIncrement 查找表的自增ID的值
func (t *Table) AutoIncrement() int {
	return t.Query("SELECT `AUTO_INCREMENT` FROM  INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA =(select database()) AND TABLE_NAME = '" + t.tableName + "';").Int()
}

// SetAutoIncrement 设置自动增长ID
func (t *Table) SetAutoIncrement(id int) error {
	_, err := t.Exec("ALTER TABLE `" + t.tableName + "` AUTO_INCREMENT = " + strconv.Itoa(id)).RowsAffected()
	return err
}

// MaxID 查找表的最大ID，如果为NULL的话则为0
func (t *Table) MaxID() int {
	return t.Query("SELECT IFNULL(MAX(id), 0) as id FROM `" + t.tableName + "`").Int()

}

// IDIn 查找多个ID对应的列
func (t *Table) IDIn(ids ...interface{}) *SQLRows {
	if len(ids) == 0 {
		return &SQLRows{}
	}
	return t.Query(fmt.Sprintf("SELECT * FROM %s WHERE id in (%s)", t.tableName, argslice(len(ids))), ids...)
}

// Create 创建
// check 如果有，则会判断表里面以这几个字段为唯一的话，数据库是否存在此条数据，如果有就不插入了。
// 所有ORM的底层。FormXXX， (*DataBase)CRUD
//
func (t *Table) Create(m map[string]interface{}, checks ...string) (int64, error) {
	//INSERT INTO `feedback` (`task_id`, `template_question_id`, `question_options_id`, `suggestion`, `member_id`) VALUES ('1', '1', '1', '1', '1')
	if len(checks) > 0 {
		names := []string{}
		values := []interface{}{}
		for _, check := range checks {
			names = append(names, "`"+check+"`"+" = ? ")
			values = append(values, m[check])
		}
		// SELECT COUNT(*) FROM `feedback` WHERE `task_id` = ? AND `member_id` = ?
		if t.Query(fmt.Sprintf("SELECT COUNT(*) FROM `%s` WHERE %s", t.tableName, strings.Join(names, "AND ")), values...).Int() > 0 {
			return 0, ErrInsertRepeat
		}
	}
	if t.tableColumns[t.tableName].HaveColumn(CreatedAt) {
		m[CreatedAt] = time.Now().Format(TimeFormat)
	}
	ks, vs := ksvs(m)
	id, err := t.Exec(fmt.Sprintf("INSERT INTO `%s` (%s) VALUES (%s)", t.tableName, strings.Join(ks, ","), argslice(len(ks))), vs...).LastInsertId()
	if err != nil {
		return 0, errors.New("SQL语句异常")
	}
	if id <= 0 {
		return 0, errors.New("插入数据库异常")
	}
	return id, nil
}

// Creates 创建多列
func (t *Table) Creates(ms []map[string]interface{}) (int, error) {
	if len(ms) == 0 {
		return 0, nil
	}

	// INSERT INTO `feedback` (`task_id`, `template_question_id`, `question_options_id`, `suggestion`, `member_id`) VALUES ('1', '1', '1', '1', '1'),('1', '1', '1', '1', '1')
	fields := []string{}
	args := []interface{}{}
	sqlFields := []string{}
	sqlArgs := []string{}
	sqlArg := "(" + argslice(len(ms[0])) + ")"
	for i := 0; i < len(ms); i++ {
		sqlArgs = append(sqlArgs, sqlArg)
	}

	for k := range ms[0] {
		fields = append(fields, k)
		sqlFields = append(sqlFields, "`"+k+"`")
	}

	for _, v := range ms {
		for _, field := range fields {
			args = append(args, v[field])
		}
	}

	rows, err := t.Exec(fmt.Sprintf("INSERT INTO `%s` (%s) VALUES %s ", t.tableName, strings.Join(sqlFields, ","), strings.Join(sqlArgs, ",")), args...).RowsAffected()
	return int(rows), err
}

//Reads 查找
func (t *Table) Reads(m map[string]interface{}) RowsMap {
	if t.tableColumns[t.tableName].HaveColumn(IsDeleted) {
		m[IsDeleted] = 0
	}
	//SELECT * FROM address WHERE id = 1 AND uid = 27
	ks, vs := ksvs(m, " = ? ")
	return t.Query(fmt.Sprintf("SELECT * FROM %s WHERE %s", t.tableName, strings.Join(ks, "AND")), vs...).RowsMap()
}

func (t *Table) Read(m map[string]interface{}) map[string]string {
	rs := t.Reads(m)
	if len(rs) > 0 {
		return rs[0]
	}
	return map[string]string{}
}

// Update 更新
// 如果map里面有id的话会自动删除id，然后使用id来作为更新的条件。
func (t *Table) Update(m map[string]interface{}, keys ...string) error {
	if len(keys) == 0 {
		keys = append(keys, "id")
	}
	if t.tableColumns[t.tableName].HaveColumn(UpdatedAt) {
		m[UpdatedAt] = time.Now().Format(TimeFormat)
	}
	keysValue := []interface{}{}
	whereks := []string{}
	for _, key := range keys {
		val, ok := m[key]
		if !ok {
			return errors.New("没有更新主键")
		}
		keysValue = append(keysValue, val)
		delete(m, key)
		whereks = append(whereks, "`"+key+"` = ? ")
	}
	//因为在更新的时候最好不要更新ID，而有时候又会将ID传入进来，所以id每次都会被删除，如果要更新id的话使用Exec()
	delete(m, "id")
	ks, vs := ksvs(m, " = ? ")
	for _, val := range keysValue {
		vs = append(vs, val)
	}
	_, err := t.Exec(fmt.Sprintf("UPDATE `%s` SET %s WHERE %s LIMIT 1", t.tableName, strings.Join(ks, ","), strings.Join(whereks, "AND")), vs...).RowsAffected()
	if err != nil {
		return errors.New("SQL语句异常")
	}
	return nil
}

//CreateOrUpdate 创建或者更新
func (t *Table) CreateOrUpdate(m map[string]interface{}, keys ...string) error {
	_, err := t.Create(m, keys...)
	if err != nil {
		if err == ErrInsertRepeat {
			return t.Update(m, keys...)
		}
		return err
	}
	return nil
}

// Delete 删除
func (t *Table) Delete(m map[string]interface{}) (int64, error) {
	ks, vs := ksvs(m, " = ? ")
	if t.tableColumns[t.tableName].HaveColumn(IsDeleted) {
		return t.Exec(fmt.Sprintf("UPDATE `%s` SET is_deleted = '1', deleted_at = '%s' WHERE %s", t.tableName, time.Now().Format(TimeFormat), strings.Join(ks, "AND")), vs...).RowsAffected()
	}
	return t.Exec(fmt.Sprintf("DELETE FROM %s WHERE %s", t.tableName, strings.Join(ks, "AND")), vs...).RowsAffected()
}

// Clone 克隆
// 克隆要保证状态在每个链式操作后都是独立的。
func (t *Table) Clone() *Table {
	newTable := &Table{
		DataBase:  t.DataBase,
		tableName: t.tableName,
	}
	if t.Search == nil {
		newTable.Search = &Search{table: newTable, tableName: t.tableName}
	} else {
		newTable.Search = t.Search.Clone()
		newTable.Search.table = newTable
	}
	return newTable
}

// Where field = arg
func (t *Table) Where(query string, args ...interface{}) *Table {
	return t.Clone().Search.Where(query, args...).table
}

// WhereNotEmpty if arg empty,will do nothing
func (t *Table) WhereNotEmpty(query, arg string) *Table {
	if arg == "" {
		return t
	}
	return t.Clone().Search.Where(query, arg).table
}

// WhereStartEndDay DATE_FORMAT(field, '%Y-%m-%d') >= startTime AND DATE_FORMAT(field, '%Y-%m-%d') <= endTime
// if startDay == "", will do nothing
// if endDay == "", endDay = startDay
// '','' => return
// '2017-07-01', '' => '2017-07-01', '2017-07-01'
// '', '2017-07-02' => '','2017-07-02' (TODO)
// '2017-07-01','2017-07-02' => '2017-07-02','2017-07-01'
func (t *Table) WhereStartEndDay(field, startDay, endDay string) *Table {
	if startDay == "" && endDay == "" {
		return t
	}
	if startDay != "" && endDay == "" {
		endDay = startDay
	}
	return t.Clone().Search.Where("DATE_FORMAT("+field+",'%Y-%m-%d') >= ? AND DATE_FORMAT("+field+",'%Y-%m-%d') <= ?", startDay, endDay).table
}

// WhereStartEndMonth DATE_FORMAT(field, '%Y-%m') >= startMonth AND DATE_FORMAT(field, '%Y-%m') <= endMonth
// if startMonth == "", will do nothing
// if endMonth == "", endMonth = startMonth
func (t *Table) WhereStartEndMonth(field, startMonth, endMonth string) *Table {
	if startMonth == "" && endMonth == "" {
		return t
	}
	if startMonth != "" && endMonth == "" {
		endMonth = startMonth
	}
	return t.Clone().Search.Where("DATE_FORMAT("+field+",'%Y-%m') >= ? AND DATE_FORMAT("+field+",'%Y-%m') <= ?", startMonth, endMonth).table
}

// WhereStartEndTime DATE_FORMAT(field, '%H:%i') >= startTime AND DATE_FORMAT(field, '%H:%i') <= endTime
// if startTime == "", will do nothing
// if endTime == "", endTime = startTime
func (t *Table) WhereStartEndTime(field, startTime, endTime string) *Table {
	if startTime == "" && endTime == "" {
		return t
	}
	if startTime != "" && endTime == "" {
		endTime = startTime
	}
	return t.Clone().Search.Where("DATE_FORMAT("+field+",'%H:%i') >= ? AND DATE_FORMAT("+field+",'%H:%i') <= ?", startTime, endTime).table
}

// WhereToday DATE_FORMAT(field, '%Y-%m-%d') = {today}
func (t *Table) WhereToday(field string) *Table {
	return t.Clone().Search.Where("DATE_FORMAT("+field+",'%Y-%m-%d') = ?", time.Now().Format("2006-01-02")).table
}

// WhereDay DATE_FORMAT(field, '%Y-%m-%d') = day
func (t *Table) WhereDay(field, day string) *Table {
	return t.Clone().Search.Where("DATE_FORMAT("+field+",'%Y-%m-%d') = ?", day).table
}

// WhereMonth DATE_FORMAT(field, '%Y-%m') = month
func (t *Table) WhereMonth(field, month string) *Table {
	return t.Clone().Search.Where("DATE_FORMAT("+field+",'%Y-%m') = ?", month).table
}

// WhereBeforeToday DATE_FORMAT(field, '%Y-%m-%d') < {today}
func (t *Table) WhereBeforeToday(field string) *Table {
	return t.Clone().Search.Where("DATE_FORMAT("+field+",'%Y-%m-%d') < ?", time.Now().Format("2006-01-02")).table
}

// WhereLike field LIKE %like%
// If like == "", will do nothing.
func (t *Table) WhereLike(field, like string) *Table {
	if like == "" {
		return t
	}
	return t.Clone().Search.Where(field+" LIKE ?", "%"+like+"%").table
}

// WhereLikeLeft field LIKE %like
func (t *Table) WhereLikeLeft(field, like string) *Table {
	return t.Clone().Search.Where(field+" LIKE ?", "%"+like).table
}

// WhereLikeRight field LIKE like%
func (t *Table) WhereLikeRight(field, like string) *Table {
	return t.Clone().Search.Where(field+" LIKE ?", like+"%").table
}

// WhereID id = ?
func (t *Table) WhereID(id interface{}) *Table {
	return t.Clone().Search.WhereID(id).table
}

// In In(field, a,b,c)
func (t *Table) In(field string, args ...interface{}) *Table {
	return t.Clone().Search.In(field, args...).table
}

// Joins LEFT JOIN
// with auto join map
func (t *Table) Joins(query string, args ...string) *Table {
	return t.Clone().Search.Joins(query, args...).table
}

// OrderBy ORDER BY
func (t *Table) OrderBy(field string, isDESC ...bool) *Table {
	return t.Clone().Search.OrderBy(field, isDESC...).table
}

// Limit LIMIT
func (t *Table) Limit(n interface{}) *Table {
	return t.Clone().Search.Limit(n).table
}

// Fields fields
func (t *Table) Fields(args ...string) *Table {
	if len(args) == 0 {
		return t
	}
	return t.Clone().Search.Fields(args...).table
}

// FieldCount equal Fields("COUNT(*) AS total")
func (t *Table) FieldCount(as ...string) *Table {
	asWhat := "total"
	if len(as) > 0 {
		sp := strings.Split(as[0], " ")
		asWhat = sp[0]
	}
	return t.Clone().Search.Fields("COUNT(*) AS " + asWhat).table
}

// Group GROUP BY
func (t *Table) Group(fields ...string) *Table {
	return t.Clone().Search.Group(fields...).table
}
<|endoftext|>


<commit_before>package provider

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/jcelliott/lumber"
	"github.com/nanobox-io/nanobox-golang-stylish"

	"github.com/nanobox-io/nanobox/util/config"
	"github.com/nanobox-io/nanobox/util/display"
)

// Native ...
type Native struct{}

// init ...
func init() {
	Register("native", Native{})
}

// Valid ensures docker-machine is installed and available
func (native Native) Valid() (bool, []string) {
	cmd := exec.Command("docker", "ps")

	//
	if err := cmd.Run(); err != nil {
		return false, []string{"docker"}
	}

	return true, nil
}

func (native Native) Status() string {
	return "Running"
}

func (native Native) BridgeRequired() bool {
	return runtime.GOOS != "linux"
}

func (native Native) IsInstalled() bool {
	cmd := exec.Command("docker", "version")

	//
	err := cmd.Run()

	return err == nil
}

func (native Native) Install() error {
	return nil
}

// Create does nothing for native
func (native Native) Create() error {
	// TODO: maybe some setup stuff???
	return nil
}

// Reboot does nothing for native
func (native Native) Reboot() error {
	// TODO: nothing??
	return nil
}

// Stop does nothing on native
func (native Native) Stop() error {
	// TODO: stop what??
	return nil
}

// implode loops through the docker containers we created
// and removes each one
func (native Native) Implode() error {
	cmd := exec.Command("docker", "ps", "-a")
	bytes, err := cmd.CombinedOutput()
	if err != nil {
		return err
	}

	s := string(bytes)
	parts := strings.Split(s, "\n")
	containers := []string{}

	for _, part := range parts {
		if strings.Contains(part, "nanobox_") {
			containers = append(containers, strings.Fields(part)[0])
		}
	}

	cmdParts := append([]string{"rm", "-f"}, containers...)
	cmd = exec.Command("docker", cmdParts...)
	cmd.Stdout = display.NewStreamer("  ")
	cmd.Stderr = display.NewStreamer("  ")

	return cmd.Run()
}

// Destroy does nothing on native
func (native Native) Destroy() error {
	// TODO: remove nanobox images

	if native.hasNetwork() {
		fmt.Print(stylish.Bullet("Removing custom docker network..."))

		cmd := exec.Command("docker", "network", "rm", "nanobox")

		cmd.Stdout = display.NewStreamer("  ")
		cmd.Stderr = display.NewStreamer("  ")

		if err := cmd.Run(); err != nil {
			return err
		}
	}

	return nil
}

// Start does nothing on native
func (native Native) Start() error {

	// TODO: some networking maybe???
	if !native.hasNetwork() {
		fmt.Print(stylish.Bullet("Setting up custom docker network..."))

		cmd := exec.Command("docker", "network", "create", "--driver=bridge", "--subnet=192.168.0.0/24", "--opt=\"com.docker.network.driver.mtu=1450\"", "--opt=\"com.docker.network.bridge.name=redd0\"", "--gateway=192.168.0.1", "nanobox")

		cmd.Stdout = display.NewStreamer("  ")
		cmd.Stderr = display.NewStreamer("  ")

		if err := cmd.Run(); err != nil {
			return err
		}
	}

	return nil
}

func (native Native) IsReady() bool {
	return native.hasNetwork()
}

// HostShareDir ...
func (native Native) HostShareDir() string {
	dir := filepath.ToSlash(filepath.Join(config.GlobalDir(), "share"))
	os.MkdirAll(dir, 0755)

	return dir + "/"
}

// HostMntDir ...
func (native Native) HostMntDir() string {
	dir := filepath.ToSlash(filepath.Join(config.GlobalDir(), "mnt"))
	os.MkdirAll(dir, 0755)

	return dir + "/"
}

// HostIP returns the loopback ip
func (native Native) HostIP() (string, error) {
	return "127.0.0.1", nil
}

func (native Native) ReservedIPs() (rtn []string) {
	return []string{}
}

// DockerEnv docker env should already be configured if docker is installed
func (native Native) DockerEnv() error {
	// ensure setup??
	return nil
}

// AddIP adds an IP into the host for host access
func (native Native) AddIP(ip string) error {
	// TODO: ???
	return nil
}

// RemoveIP removes an IP from the docker-machine vm
func (native Native) RemoveIP(ip string) error {
	// TODO: ???
	return nil
}

func (native Native) SetDefaultIP(ip string) error {
	// nothing is necessary here
	return nil
}

// AddNat adds a nat to make an container accessible to the host network stack
func (native Native) AddNat(ip, containerIP string) error {
	// TODO: ???
	return nil
}

// RemoveNat removes nat from making a container inaccessible to the host network stack
func (native Native) RemoveNat(ip, containerIP string) error {
	// TODO: ???
	return nil
}

// HasMount will return true if the mount already exists
func (native Native) HasMount(path string) bool {
	//
	fi, err := os.Lstat(path)
	if err != nil {
		if os.IsNotExist(err) {
			return false
		}
		lumber.Debug("Error checking mount: %s", err)
	}

	//
	if (fi.Mode() & os.ModeSymlink) > 0 {
		return true
	}

	return false
}

// AddMount adds a mount into the docker-machine vm
func (native Native) AddMount(local, host string) error {

	// TODO: ???
	if !native.HasMount(host) {
		if err := os.MkdirAll(filepath.Dir(host), 0755); err != nil {
			return err
		}

		return os.Symlink(local, host)
	}

	return nil
}

// RemoveMount ...
func (native Native) RemoveMount(_, host string) error {

	// TODO: ???
	if native.HasMount(host) {
		return os.Remove(host)
	}

	return nil
}

// Run will run a command on the local machine (pass-through)
func (native Native) Run(command []string) ([]byte, error) {
	// when we actually run the command, we need to pop off the first item
	cmd := exec.Command(command[0], command[1:]...)

	// run the command and return the output
	return cmd.CombinedOutput()
}

//
func (native Native) RemoveEnvDir(id string) error {
	if id == "" {
		return nil
	}

	return os.RemoveAll(native.HostMntDir() + id)
}

// hasNetwork ...
func (native Native) hasNetwork() bool {

	// docker-machine ssh nanobox docker network inspect nanobox
	cmd := exec.Command("docker", "network", "inspect", "nanobox")
	b, err := cmd.CombinedOutput()

	//
	if err != nil {
		lumber.Debug("hasNetwork output: %s", b)
		return false
	}

	return true
}
<commit_msg>add runtime dependency<commit_after>package provider

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"runtime"

	"github.com/jcelliott/lumber"
	"github.com/nanobox-io/nanobox-golang-stylish"

	"github.com/nanobox-io/nanobox/util/config"
	"github.com/nanobox-io/nanobox/util/display"
)

// Native ...
type Native struct{}

// init ...
func init() {
	Register("native", Native{})
}

// Valid ensures docker-machine is installed and available
func (native Native) Valid() (bool, []string) {
	cmd := exec.Command("docker", "ps")

	//
	if err := cmd.Run(); err != nil {
		return false, []string{"docker"}
	}

	return true, nil
}

func (native Native) Status() string {
	return "Running"
}

func (native Native) BridgeRequired() bool {
	return runtime.GOOS != "linux"
}

func (native Native) IsInstalled() bool {
	cmd := exec.Command("docker", "version")

	//
	err := cmd.Run()

	return err == nil
}

func (native Native) Install() error {
	return nil
}

// Create does nothing for native
func (native Native) Create() error {
	// TODO: maybe some setup stuff???
	return nil
}

// Reboot does nothing for native
func (native Native) Reboot() error {
	// TODO: nothing??
	return nil
}

// Stop does nothing on native
func (native Native) Stop() error {
	// TODO: stop what??
	return nil
}

// implode loops through the docker containers we created
// and removes each one
func (native Native) Implode() error {
	cmd := exec.Command("docker", "ps", "-a")
	bytes, err := cmd.CombinedOutput()
	if err != nil {
		return err
	}

	s := string(bytes)
	parts := strings.Split(s, "\n")
	containers := []string{}

	for _, part := range parts {
		if strings.Contains(part, "nanobox_") {
			containers = append(containers, strings.Fields(part)[0])
		}
	}

	cmdParts := append([]string{"rm", "-f"}, containers...)
	cmd = exec.Command("docker", cmdParts...)
	cmd.Stdout = display.NewStreamer("  ")
	cmd.Stderr = display.NewStreamer("  ")

	return cmd.Run()
}

// Destroy does nothing on native
func (native Native) Destroy() error {
	// TODO: remove nanobox images

	if native.hasNetwork() {
		fmt.Print(stylish.Bullet("Removing custom docker network..."))

		cmd := exec.Command("docker", "network", "rm", "nanobox")

		cmd.Stdout = display.NewStreamer("  ")
		cmd.Stderr = display.NewStreamer("  ")

		if err := cmd.Run(); err != nil {
			return err
		}
	}

	return nil
}

// Start does nothing on native
func (native Native) Start() error {

	// TODO: some networking maybe???
	if !native.hasNetwork() {
		fmt.Print(stylish.Bullet("Setting up custom docker network..."))

		cmd := exec.Command("docker", "network", "create", "--driver=bridge", "--subnet=192.168.0.0/24", "--opt=\"com.docker.network.driver.mtu=1450\"", "--opt=\"com.docker.network.bridge.name=redd0\"", "--gateway=192.168.0.1", "nanobox")

		cmd.Stdout = display.NewStreamer("  ")
		cmd.Stderr = display.NewStreamer("  ")

		if err := cmd.Run(); err != nil {
			return err
		}
	}

	return nil
}

func (native Native) IsReady() bool {
	return native.hasNetwork()
}

// HostShareDir ...
func (native Native) HostShareDir() string {
	dir := filepath.ToSlash(filepath.Join(config.GlobalDir(), "share"))
	os.MkdirAll(dir, 0755)

	return dir + "/"
}

// HostMntDir ...
func (native Native) HostMntDir() string {
	dir := filepath.ToSlash(filepath.Join(config.GlobalDir(), "mnt"))
	os.MkdirAll(dir, 0755)

	return dir + "/"
}

// HostIP returns the loopback ip
func (native Native) HostIP() (string, error) {
	return "127.0.0.1", nil
}

func (native Native) ReservedIPs() (rtn []string) {
	return []string{}
}

// DockerEnv docker env should already be configured if docker is installed
func (native Native) DockerEnv() error {
	// ensure setup??
	return nil
}

// AddIP adds an IP into the host for host access
func (native Native) AddIP(ip string) error {
	// TODO: ???
	return nil
}

// RemoveIP removes an IP from the docker-machine vm
func (native Native) RemoveIP(ip string) error {
	// TODO: ???
	return nil
}

func (native Native) SetDefaultIP(ip string) error {
	// nothing is necessary here
	return nil
}

// AddNat adds a nat to make an container accessible to the host network stack
func (native Native) AddNat(ip, containerIP string) error {
	// TODO: ???
	return nil
}

// RemoveNat removes nat from making a container inaccessible to the host network stack
func (native Native) RemoveNat(ip, containerIP string) error {
	// TODO: ???
	return nil
}

// HasMount will return true if the mount already exists
func (native Native) HasMount(path string) bool {
	//
	fi, err := os.Lstat(path)
	if err != nil {
		if os.IsNotExist(err) {
			return false
		}
		lumber.Debug("Error checking mount: %s", err)
	}

	//
	if (fi.Mode() & os.ModeSymlink) > 0 {
		return true
	}

	return false
}

// AddMount adds a mount into the docker-machine vm
func (native Native) AddMount(local, host string) error {

	// TODO: ???
	if !native.HasMount(host) {
		if err := os.MkdirAll(filepath.Dir(host), 0755); err != nil {
			return err
		}

		return os.Symlink(local, host)
	}

	return nil
}

// RemoveMount ...
func (native Native) RemoveMount(_, host string) error {

	// TODO: ???
	if native.HasMount(host) {
		return os.Remove(host)
	}

	return nil
}

// Run will run a command on the local machine (pass-through)
func (native Native) Run(command []string) ([]byte, error) {
	// when we actually run the command, we need to pop off the first item
	cmd := exec.Command(command[0], command[1:]...)

	// run the command and return the output
	return cmd.CombinedOutput()
}

//
func (native Native) RemoveEnvDir(id string) error {
	if id == "" {
		return nil
	}

	return os.RemoveAll(native.HostMntDir() + id)
}

// hasNetwork ...
func (native Native) hasNetwork() bool {

	// docker-machine ssh nanobox docker network inspect nanobox
	cmd := exec.Command("docker", "network", "inspect", "nanobox")
	b, err := cmd.CombinedOutput()

	//
	if err != nil {
		lumber.Debug("hasNetwork output: %s", b)
		return false
	}

	return true
}
<|endoftext|>


<commit_before>package utility

import (
	"regexp"
	"testing"
	"time"

	"github.com/boatilus/peppercorn/db"
	"github.com/spf13/viper"
	"github.com/stretchr/testify/assert"
)

func TestObfuscateEmail(t *testing.T) {
	cases := []struct {
		email string
		want  string
	}{
		{"user@test.com", "u***@t***.com"},
		{"u@test.com", "u***@t***.com"},
		{"user@t.com", "u***@t***.com"},
		{"@", "@"},
		{"@.", "@."},
		{".@", ".@"},
		{"ad@.com", "ad@.com"},
	}

	for _, c := range cases {
		assert.Equal(t, c.want, ObfuscateEmail(c.email))
	}
}

func benchmarkObfuscateEmail(b *testing.B, v string) {
	for n := 0; n < b.N; n++ {
		ObfuscateEmail(v)
	}
}

func BenchmarkObfuscateEmail_full(b *testing.B)        { benchmarkObfuscateEmail(b, "user@test.com") }
func BenchmarkObfuscateEmail_shortname(b *testing.B)   { benchmarkObfuscateEmail(b, "u@test.com") }
func BenchmarkObfuscateEmail_shortdomain(b *testing.B) { benchmarkObfuscateEmail(b, "user@t.com") }
func BenchmarkObfuscateEmail_justamp(b *testing.B)     { benchmarkObfuscateEmail(b, "@") }

func TestParseUserAgent(t *testing.T) {
	cases := []struct {
		ua          string
		wantBrowser string
		wantOS      string
	}{
		{"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11", "Chrome", "Linux "},
		{"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36", "Chrome", "Windows 10"},
	}

	for _, c := range cases {
		got := ParseUserAgent(c.ua)
		assert.Equal(t, c.wantBrowser, got.Browser)
		assert.Equal(t, c.wantOS, got.OS)
	}
}

func TestFormatTime(t *testing.T) {
	ref, err := time.Parse(time.RubyDate, "Mon Jan 02 15:04:05 -0700 2006")
	assert.Nil(t, err)

	cases := []struct {
		then time.Time
		want string
	}{
		{ref, "less than a minute ago"},
		{ref.Add(-70 * time.Second), "about a minute ago"},
		{ref.Add(-2 * time.Minute), "2 minutes ago"},
		{ref.Add(-40 * time.Minute), "40 minutes ago"},
		{ref.Add(-59 * time.Minute), "59 minutes ago"},
		{ref.Add(-1 * time.Hour), "2:04 PM"},
		{ref.Add(-15 * time.Hour), "12:04 AM"},
		{ref.Add(-16 * time.Hour), "January 1, 2006 at 11:04 PM"},
		{ref.Add(-24 * time.Hour), "January 1, 2006 at 3:04 PM"},
	}

	for _, c := range cases {
		assert.Equal(t, c.want, FormatTime(c.then, ref))
	}
}

func benchmarkFormatTime(b *testing.B, t time.Time, current time.Time) {
	for n := 0; n < b.N; n++ {
		FormatTime(t, current)
	}
}

func setupBenchmarkFormatTime() time.Time {
	ref, err := time.Parse(time.RubyDate, "Mon Jan 02 15:04:05 -0700 2006")
	if err != nil {
		panic(err)
	}

	return ref
}

func BenchmarkFormatTime_LT_min(b *testing.B) {
	ref := setupBenchmarkFormatTime()

	benchmarkFormatTime(b, ref, ref)
}

func BenchmarkFormatTime_about_min(b *testing.B) {
	ref := setupBenchmarkFormatTime()

	benchmarkFormatTime(b, ref.Add(-70*time.Second), ref)
}

func BenchmarkFormatTime_min_ago(b *testing.B) {
	ref := setupBenchmarkFormatTime()

	benchmarkFormatTime(b, ref.Add(-2*time.Minute), ref)
}

func BenchmarkFormatTime_timestamp(b *testing.B) {
	ref := setupBenchmarkFormatTime()

	benchmarkFormatTime(b, ref.Add(-15*time.Hour), ref)
}

func BenchmarkFormatTime_fulldate(b *testing.B) {
	ref := setupBenchmarkFormatTime()

	benchmarkFormatTime(b, ref.Add(-36*time.Hour), ref)
}

func TestGetVersionString(t *testing.T) {
	// We'll just look for a correct format..
	got := GetVersionString()

	assert.Regexp(t, regexp.MustCompile(`\d+.\d+.\d+`), got)
}

func TestGetTitle(t *testing.T) {
	viper.Set("title", "A Given Title")

	got := GetTitle()

	assert.Equal(t, "A Given Title", got)
}

func TestCommifyInt64(t *testing.T) {
	cases := []struct {
		num  int64
		want string
	}{
		{0, "0"},
		{1, "1"},
		{999, "999"},
		{1000, "1,000"},
		{10000, "10,000"},
		{100000, "100,000"},
		{399313, "399,313"},
		{9223372036854775807, "9,223,372,036,854,775,807"},
		{-1, "-1"},
		{-999, "-999"},
		{-1000, "-1,000"},
		{-10000, "-10,000"},
		{-100000, "-100,000"},
		{-399313, "-399,313"},
		{-9223372036854775808, "-9,223,372,036,854,775,808"},
	}

	for _, c := range cases {
		assert.Equal(t, c.want, CommifyInt64(c.num))
	}
}

func benchmarkCommifyInt64(b *testing.B, v int64) {
	for n := 0; n < b.N; n++ {
		CommifyInt64(v)
	}
}

func BenchmarkCommifyInt64_0(b *testing.B)       { benchmarkCommifyInt64(b, 0) }
func BenchmarkCommifyInt64_8(b *testing.B)       { benchmarkCommifyInt64(b, 8) }
func BenchmarkCommifyInt64_17(b *testing.B)      { benchmarkCommifyInt64(b, 17) }
func BenchmarkCommifyInt64_371(b *testing.B)     { benchmarkCommifyInt64(b, 371) }
func BenchmarkCommifyInt64_1993(b *testing.B)    { benchmarkCommifyInt64(b, 1993) }
func BenchmarkCommifyInt64_72759(b *testing.B)   { benchmarkCommifyInt64(b, 72759) }
func BenchmarkCommifyInt64_497167(b *testing.B)  { benchmarkCommifyInt64(b, 497167) }
func BenchmarkCommifyInt64_8881679(b *testing.B) { benchmarkCommifyInt64(b, 8881679) }

func TestComputePage(t *testing.T) {
	cases := []struct {
		numPosts  db.CountType
		pageEvery db.CountType
		want      db.CountType
	}{
		{1, 5, 1},
		{5, 5, 1},
		{6, 5, 2},
		{7, 5, 2},
		{9, 5, 2},
		{10, 5, 2},
		{11, 5, 3},
	}

	for _, c := range cases {
		assert.Equal(t, c.want, ComputePage(c.numPosts, c.pageEvery))
	}
}

func TestRemoveCRs(t *testing.T) {
	cases := []struct {
		in   string
		want string
	}{
		{"Hello\r\nWorld!", "Hello\nWorld!"},
		{"Hello\r\nWorld! I like...\r\n...cake.", "Hello\nWorld! I like...\n...cake."},
		{"\r\r\n", "\n"},
	}

	for _, c := range cases {
		assert.Equal(t, c.want, RemoveCRs(c.in))
	}
}

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

	tm, err := time.Parse("2006-01-02T15:04:05-0700", "2006-01-02T15:04:05-0700")
	if !assert.NoError(err) {
		t.FailNow()
	}

	s := GetISO8601String(&tm)
	assert.Equal("2006-01-02T15:04:05-0700", s)

	var badTime *time.Time

	s = GetISO8601String(badTime)
	assert.Equal("", s)
}

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

	got := GenerateRandomNonce()

	t.Log(got)
}
<commit_msg>Fix failing utility test<commit_after>package utility

import (
	"regexp"
	"testing"
	"time"

	"github.com/boatilus/peppercorn/db"
	"github.com/spf13/viper"
	"github.com/stretchr/testify/assert"
)

func TestObfuscateEmail(t *testing.T) {
	cases := []struct {
		email string
		want  string
	}{
		{"user@test.com", "u***@t***.com"},
		{"u@test.com", "u***@t***.com"},
		{"user@t.com", "u***@t***.com"},
		{"@", "@"},
		{"@.", "@."},
		{".@", ".@"},
		{"ad@.com", "ad@.com"},
	}

	for _, c := range cases {
		assert.Equal(t, c.want, ObfuscateEmail(c.email))
	}
}

func benchmarkObfuscateEmail(b *testing.B, v string) {
	for n := 0; n < b.N; n++ {
		ObfuscateEmail(v)
	}
}

func BenchmarkObfuscateEmail_full(b *testing.B)        { benchmarkObfuscateEmail(b, "user@test.com") }
func BenchmarkObfuscateEmail_shortname(b *testing.B)   { benchmarkObfuscateEmail(b, "u@test.com") }
func BenchmarkObfuscateEmail_shortdomain(b *testing.B) { benchmarkObfuscateEmail(b, "user@t.com") }
func BenchmarkObfuscateEmail_justamp(b *testing.B)     { benchmarkObfuscateEmail(b, "@") }

func TestParseUserAgent(t *testing.T) {
	cases := []struct {
		ua          string
		wantBrowser string
		wantOS      string
	}{
		{"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11", "Chrome", "Linux "},
		{"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36", "Chrome", "Windows 10"},
	}

	for _, c := range cases {
		got := ParseUserAgent(c.ua)
		assert.Equal(t, c.wantBrowser, got.Browser)
		assert.Equal(t, c.wantOS, got.OS)
	}
}

func TestFormatTime(t *testing.T) {
	ref, err := time.Parse(time.RubyDate, "Mon Jan 02 15:04:05 -0700 2006")
	assert.Nil(t, err)

	cases := []struct {
		then time.Time
		want string
	}{
		{ref, "less than a minute ago"},
		{ref.Add(-70 * time.Second), "about a minute ago"},
		{ref.Add(-2 * time.Minute), "2 minutes ago"},
		{ref.Add(-40 * time.Minute), "40 minutes ago"},
		{ref.Add(-59 * time.Minute), "59 minutes ago"},
		{ref.Add(-1 * time.Hour), "2:04 PM"},
		{ref.Add(-15 * time.Hour), "12:04 AM"},
		{ref.Add(-16 * time.Hour), "January 1, 2006 at 11:04 PM"},
		{ref.Add(-24 * time.Hour), "January 1, 2006 at 3:04 PM"},
	}

	for _, c := range cases {
		assert.Equal(t, c.want, FormatTime(c.then, ref))
	}
}

func benchmarkFormatTime(b *testing.B, t time.Time, current time.Time) {
	for n := 0; n < b.N; n++ {
		FormatTime(t, current)
	}
}

func setupBenchmarkFormatTime() time.Time {
	ref, err := time.Parse(time.RubyDate, "Mon Jan 02 15:04:05 -0700 2006")
	if err != nil {
		panic(err)
	}

	return ref
}

func BenchmarkFormatTime_LT_min(b *testing.B) {
	ref := setupBenchmarkFormatTime()

	benchmarkFormatTime(b, ref, ref)
}

func BenchmarkFormatTime_about_min(b *testing.B) {
	ref := setupBenchmarkFormatTime()

	benchmarkFormatTime(b, ref.Add(-70*time.Second), ref)
}

func BenchmarkFormatTime_min_ago(b *testing.B) {
	ref := setupBenchmarkFormatTime()

	benchmarkFormatTime(b, ref.Add(-2*time.Minute), ref)
}

func BenchmarkFormatTime_timestamp(b *testing.B) {
	ref := setupBenchmarkFormatTime()

	benchmarkFormatTime(b, ref.Add(-15*time.Hour), ref)
}

func BenchmarkFormatTime_fulldate(b *testing.B) {
	ref := setupBenchmarkFormatTime()

	benchmarkFormatTime(b, ref.Add(-36*time.Hour), ref)
}

func TestGetVersionString(t *testing.T) {
	// We'll just look for a correct format..
	got := GetVersionString()

	assert.Regexp(t, regexp.MustCompile(`\d+.\d+.\d+`), got)
}

func TestGetTitle(t *testing.T) {
	viper.Set("title", "A Given Title")

	got := GetTitle()

	assert.Equal(t, "A Given Title", got)
}

func TestCommifyInt64(t *testing.T) {
	cases := []struct {
		num  int64
		want string
	}{
		{0, "0"},
		{1, "1"},
		{999, "999"},
		{1000, "1,000"},
		{10000, "10,000"},
		{100000, "100,000"},
		{399313, "399,313"},
		{9223372036854775807, "9,223,372,036,854,775,807"},
		{-1, "-1"},
		{-999, "-999"},
		{-1000, "-1,000"},
		{-10000, "-10,000"},
		{-100000, "-100,000"},
		{-399313, "-399,313"},
		{-9223372036854775808, "-9,223,372,036,854,775,808"},
	}

	for _, c := range cases {
		assert.Equal(t, c.want, CommifyInt64(c.num))
	}
}

func benchmarkCommifyInt64(b *testing.B, v int64) {
	for n := 0; n < b.N; n++ {
		CommifyInt64(v)
	}
}

func BenchmarkCommifyInt64_0(b *testing.B)       { benchmarkCommifyInt64(b, 0) }
func BenchmarkCommifyInt64_8(b *testing.B)       { benchmarkCommifyInt64(b, 8) }
func BenchmarkCommifyInt64_17(b *testing.B)      { benchmarkCommifyInt64(b, 17) }
func BenchmarkCommifyInt64_371(b *testing.B)     { benchmarkCommifyInt64(b, 371) }
func BenchmarkCommifyInt64_1993(b *testing.B)    { benchmarkCommifyInt64(b, 1993) }
func BenchmarkCommifyInt64_72759(b *testing.B)   { benchmarkCommifyInt64(b, 72759) }
func BenchmarkCommifyInt64_497167(b *testing.B)  { benchmarkCommifyInt64(b, 497167) }
func BenchmarkCommifyInt64_8881679(b *testing.B) { benchmarkCommifyInt64(b, 8881679) }

func TestComputePage(t *testing.T) {
	cases := []struct {
		numPosts  db.CountType
		pageEvery db.CountType
		want      db.CountType
	}{
		{1, 5, 1},
		{5, 5, 1},
		{6, 5, 2},
		{7, 5, 2},
		{9, 5, 2},
		{10, 5, 2},
		{11, 5, 3},
	}

	for _, c := range cases {
		assert.Equal(t, c.want, ComputePage(c.numPosts, c.pageEvery))
	}
}

func TestRemoveCRs(t *testing.T) {
	cases := []struct {
		in   string
		want string
	}{
		{"Hello\r\nWorld!", "Hello\nWorld!"},
		{"Hello\r\nWorld! I like...\r\n...cake.", "Hello\nWorld! I like...\n...cake."},
		{"\r\r\n", "\n"},
	}

	for _, c := range cases {
		assert.Equal(t, c.want, RemoveCRs(c.in))
	}
}

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

	tm, err := time.Parse("2006-01-02T15:04:05-0700", "2006-01-02T15:04:05-0700")
	if !assert.NoError(err) {
		t.FailNow()
	}

	s := GetISO8601String(&tm)
	assert.Equal("2006-01-02T15:04:05-0700", s)

	var badTime *time.Time

	s = GetISO8601String(badTime)
	assert.Equal("", s)
}

func TestGenerateRandomNonce(t *testing.T) {
	got := GenerateRandomNonce()
	assert.NotEmpty(t, got)
}
<|endoftext|>


<commit_before>// Package csv provides CsvReader and CsvWriter to process csv format file
// in the struct declaration style.
package csv

import (
	"encoding/csv"
	"errors"
	"fmt"
	"io"
	"os"
	"reflect"

	"github.com/hoveychen/go-utils"
)

// CsvWriter extends the encoding/csv writer, supporting writting struct, and
// shortcut to write to a file.
type CsvWriter struct {
	*csv.Writer
	Headers  []string
	file     *os.File
	fieldIdx []string
}

func NewCsvWriter(w io.Writer) *CsvWriter {
	return &CsvWriter{
		Writer: csv.NewWriter(w),
	}
}

func NewFileCsvWriter(filename string) *CsvWriter {
	file, err := os.Create(filename)
	if err != nil {
		goutils.LogError(err)
		return nil
	}
	return &CsvWriter{
		Writer: csv.NewWriter(file),
		file:   file,
	}
}

func (w *CsvWriter) buildFieldIndex(val reflect.Value) {
	w.fieldIdx = []string{}
	for i := 0; i < val.Type().NumField(); i++ {
		field := val.Type().Field(i)
		if field.PkgPath != "" {
			// Unexported field will have PkgPath.
			continue
		}
		tag := field.Tag.Get("csv")
		var name string
		if tag == "" {
			name = field.Name
		} else if tag == "-" {
			continue
		} else {
			name = tag
		}

		w.Headers = append(w.Headers, name)
		w.fieldIdx = append(w.fieldIdx, field.Name)
	}
}

func (w *CsvWriter) WriteStruct(i interface{}) error {
	val := reflect.ValueOf(i)
	if val.Kind() == reflect.Ptr {
		val = val.Elem()
	}
	if val.Kind() != reflect.Struct {
		return errors.New("Input need to be a struct")
	}

	if w.Headers == nil {
		w.buildFieldIndex(val)
		w.Write(w.Headers)
	}

	out := []string{}
	for _, name := range w.fieldIdx {
		v := val.FieldByName(name).Interface()
		out = append(out, fmt.Sprintf("%v", v))
	}
	w.Write(out)
	return nil
}

func (w *CsvWriter) Close() error {
	if w.file != nil {
		w.Flush()
		return w.file.Close()
	}
	return nil
}
<commit_msg>[Csv] Fix closing writer error.<commit_after>// Package csv provides CsvReader and CsvWriter to process csv format file
// in the struct declaration style.
package csv

import (
	"encoding/csv"
	"errors"
	"fmt"
	"io"
	"os"
	"reflect"

	"github.com/hoveychen/go-utils"
)

// CsvWriter extends the encoding/csv writer, supporting writting struct, and
// shortcut to write to a file.
type CsvWriter struct {
	*csv.Writer
	Headers  []string
	file     *os.File
	fieldIdx []string
}

func NewCsvWriter(w io.Writer) *CsvWriter {
	return &CsvWriter{
		Writer: csv.NewWriter(w),
	}
}

func NewFileCsvWriter(filename string) *CsvWriter {
	file, err := os.Create(filename)
	if err != nil {
		goutils.LogError(err)
		return nil
	}
	return &CsvWriter{
		Writer: csv.NewWriter(file),
		file:   file,
	}
}

func (w *CsvWriter) buildFieldIndex(val reflect.Value) {
	w.fieldIdx = []string{}
	for i := 0; i < val.Type().NumField(); i++ {
		field := val.Type().Field(i)
		if field.PkgPath != "" {
			// Unexported field will have PkgPath.
			continue
		}
		tag := field.Tag.Get("csv")
		var name string
		if tag == "" {
			name = field.Name
		} else if tag == "-" {
			continue
		} else {
			name = tag
		}

		w.Headers = append(w.Headers, name)
		w.fieldIdx = append(w.fieldIdx, field.Name)
	}
}

func (w *CsvWriter) WriteStruct(i interface{}) error {
	val := reflect.ValueOf(i)
	if val.Kind() == reflect.Ptr {
		val = val.Elem()
	}
	if val.Kind() != reflect.Struct {
		return errors.New("Input need to be a struct")
	}

	if w.Headers == nil {
		w.buildFieldIndex(val)
		w.Write(w.Headers)
	}

	out := []string{}
	for _, name := range w.fieldIdx {
		v := val.FieldByName(name).Interface()
		out = append(out, fmt.Sprintf("%v", v))
	}
	w.Write(out)
	return nil
}

func (w *CsvWriter) Close() error {
	w.Flush()
	if w.file != nil {
		return w.file.Close()
	}
	return nil
}
<|endoftext|>


<commit_before>package stripe

import "encoding/json"

// TaxIDType is the list of allowed values for the tax id's type..
type TaxIDType string

// List of values that TaxIDType can take.
const (
	TaxIDTypeAETRN   TaxIDType = "ae_trn"
	TaxIDTypeAUABN   TaxIDType = "au_abn"
	TaxIDTypeBRCNPJ  TaxIDType = "br_cnpj"
	TaxIDTypeBRCPF   TaxIDType = "br_cpf"
	TaxIDTypeCABN    TaxIDType = "ca_bn"
	TaxIDTypeCAQST   TaxIDType = "ca_qst"
	TaxIDTypeCHVAT   TaxIDType = "ch_vat"
	TaxIDTypeCLTIN   TaxIDType = "cl_tin"
	TaxIDTypeESCIF   TaxIDType = "es_cif"
	TaxIDTypeEUVAT   TaxIDType = "eu_vat"
	TaxIDTypeHKBR    TaxIDType = "hk_br"
	TaxIDTypeINGST   TaxIDType = "in_gst"
	TaxIDTypeJPCN    TaxIDType = "jp_cn"
	TaxIDTypeKRBRN   TaxIDType = "kr_brn"
	TaxIDTypeLIUID   TaxIDType = "li_uid"
	TaxIDTypeMXRFC   TaxIDType = "mx_rfc"
	TaxIDTypeMYITN   TaxIDType = "my_itn"
	TaxIDTypeMYSST   TaxIDType = "my_sst"
	TaxIDTypeNOVAT   TaxIDType = "no_vat"
	TaxIDTypeNZGST   TaxIDType = "nz_gst"
	TaxIDTypeRUINN   TaxIDType = "ru_inn"
	TaxIDTypeSAVAT   TaxIDType = "sa_vat"
	TaxIDTypeSGUEN   TaxIDType = "sg_uen"
	TaxIDTypeSGGST   TaxIDType = "sg_gst"
	TaxIDTypeTHVAT   TaxIDType = "th_vat"
	TaxIDTypeTWVAT   TaxIDType = "tw_vat"
	TaxIDTypeUSEIN   TaxIDType = "us_ein"
	TaxIDTypeZAVAT   TaxIDType = "za_vat"
	TaxIDTypeUnknown TaxIDType = "unknown"
)

// TaxIDVerificationStatus is the list of allowed values for the tax id's verification status..
type TaxIDVerificationStatus string

// List of values that TaxIDDuration can take.
const (
	TaxIDVerificationStatusPending     TaxIDVerificationStatus = "pending"
	TaxIDVerificationStatusUnavailable TaxIDVerificationStatus = "unavailable"
	TaxIDVerificationStatusUnverified  TaxIDVerificationStatus = "unverified"
	TaxIDVerificationStatusVerified    TaxIDVerificationStatus = "verified"
)

// TaxIDParams is the set of parameters that can be used when creating a tax id.
// For more details see https://stripe.com/docs/api/customers/create_tax_id
type TaxIDParams struct {
	Params   `form:"*"`
	Customer *string `form:"-"`
	Type     *string `form:"type"`
	Value    *string `form:"value"`
}

// TaxIDListParams is the set of parameters that can be used when listing tax ids.
// For more detail see https://stripe.com/docs/api/customers/tax_ids
type TaxIDListParams struct {
	ListParams `form:"*"`
	Customer   *string `form:"-"`
}

// TaxIDVerification represents the verification details of a customer's tax id.
type TaxIDVerification struct {
	Status          TaxIDVerificationStatus `json:"status"`
	VerifiedAddress string                  `json:"verified_address"`
	VerifiedName    string                  `json:"verified_name"`
}

// TaxID is the resource representing a customer's tax id.
// For more details see https://stripe.com/docs/api/customers/tax_id_object
type TaxID struct {
	APIResource
	Country      string             `json:"country"`
	Created      int64              `json:"created"`
	Customer     *Customer          `json:"customer"`
	Deleted      bool               `json:"deleted"`
	ID           string             `json:"id"`
	Livemode     bool               `json:"livemode"`
	Object       string             `json:"object"`
	Type         TaxIDType          `json:"type"`
	Value        string             `json:"value"`
	Verification *TaxIDVerification `json:"verification"`
}

// TaxIDList is a list of tax ids as retrieved from a list endpoint.
type TaxIDList struct {
	APIResource
	ListMeta
	Data []*TaxID `json:"data"`
}

// UnmarshalJSON handles deserialization of a TaxID.
// This custom unmarshaling is needed because the resulting
// property may be an id or the full struct if it was expanded.
func (c *TaxID) UnmarshalJSON(data []byte) error {
	if id, ok := ParseID(data); ok {
		c.ID = id
		return nil
	}

	type taxid TaxID
	var v taxid
	if err := json.Unmarshal(data, &v); err != nil {
		return err
	}

	*c = TaxID(v)
	return nil
}
<commit_msg>Add support for `TaxIDTypeIDNPWP` and `TaxIDTypeMYFRP` on `TaxId`<commit_after>package stripe

import "encoding/json"

// TaxIDType is the list of allowed values for the tax id's type..
type TaxIDType string

// List of values that TaxIDType can take.
const (
	TaxIDTypeAETRN   TaxIDType = "ae_trn"
	TaxIDTypeAUABN   TaxIDType = "au_abn"
	TaxIDTypeBRCNPJ  TaxIDType = "br_cnpj"
	TaxIDTypeBRCPF   TaxIDType = "br_cpf"
	TaxIDTypeCABN    TaxIDType = "ca_bn"
	TaxIDTypeCAQST   TaxIDType = "ca_qst"
	TaxIDTypeCHVAT   TaxIDType = "ch_vat"
	TaxIDTypeCLTIN   TaxIDType = "cl_tin"
	TaxIDTypeESCIF   TaxIDType = "es_cif"
	TaxIDTypeEUVAT   TaxIDType = "eu_vat"
	TaxIDTypeHKBR    TaxIDType = "hk_br"
	TaxIDTypeIDNPWP  TaxIDType = "id_npwp"
	TaxIDTypeINGST   TaxIDType = "in_gst"
	TaxIDTypeJPCN    TaxIDType = "jp_cn"
	TaxIDTypeKRBRN   TaxIDType = "kr_brn"
	TaxIDTypeLIUID   TaxIDType = "li_uid"
	TaxIDTypeMXRFC   TaxIDType = "mx_rfc"
	TaxIDTypeMYITN   TaxIDType = "my_itn"
	TaxIDTypeMYFRP   TaxIDType = "my_frp"
	TaxIDTypeMYSST   TaxIDType = "my_sst"
	TaxIDTypeNOVAT   TaxIDType = "no_vat"
	TaxIDTypeNZGST   TaxIDType = "nz_gst"
	TaxIDTypeRUINN   TaxIDType = "ru_inn"
	TaxIDTypeSAVAT   TaxIDType = "sa_vat"
	TaxIDTypeSGUEN   TaxIDType = "sg_uen"
	TaxIDTypeSGGST   TaxIDType = "sg_gst"
	TaxIDTypeTHVAT   TaxIDType = "th_vat"
	TaxIDTypeTWVAT   TaxIDType = "tw_vat"
	TaxIDTypeUSEIN   TaxIDType = "us_ein"
	TaxIDTypeZAVAT   TaxIDType = "za_vat"
	TaxIDTypeUnknown TaxIDType = "unknown"
)

// TaxIDVerificationStatus is the list of allowed values for the tax id's verification status..
type TaxIDVerificationStatus string

// List of values that TaxIDDuration can take.
const (
	TaxIDVerificationStatusPending     TaxIDVerificationStatus = "pending"
	TaxIDVerificationStatusUnavailable TaxIDVerificationStatus = "unavailable"
	TaxIDVerificationStatusUnverified  TaxIDVerificationStatus = "unverified"
	TaxIDVerificationStatusVerified    TaxIDVerificationStatus = "verified"
)

// TaxIDParams is the set of parameters that can be used when creating a tax id.
// For more details see https://stripe.com/docs/api/customers/create_tax_id
type TaxIDParams struct {
	Params   `form:"*"`
	Customer *string `form:"-"`
	Type     *string `form:"type"`
	Value    *string `form:"value"`
}

// TaxIDListParams is the set of parameters that can be used when listing tax ids.
// For more detail see https://stripe.com/docs/api/customers/tax_ids
type TaxIDListParams struct {
	ListParams `form:"*"`
	Customer   *string `form:"-"`
}

// TaxIDVerification represents the verification details of a customer's tax id.
type TaxIDVerification struct {
	Status          TaxIDVerificationStatus `json:"status"`
	VerifiedAddress string                  `json:"verified_address"`
	VerifiedName    string                  `json:"verified_name"`
}

// TaxID is the resource representing a customer's tax id.
// For more details see https://stripe.com/docs/api/customers/tax_id_object
type TaxID struct {
	APIResource
	Country      string             `json:"country"`
	Created      int64              `json:"created"`
	Customer     *Customer          `json:"customer"`
	Deleted      bool               `json:"deleted"`
	ID           string             `json:"id"`
	Livemode     bool               `json:"livemode"`
	Object       string             `json:"object"`
	Type         TaxIDType          `json:"type"`
	Value        string             `json:"value"`
	Verification *TaxIDVerification `json:"verification"`
}

// TaxIDList is a list of tax ids as retrieved from a list endpoint.
type TaxIDList struct {
	APIResource
	ListMeta
	Data []*TaxID `json:"data"`
}

// UnmarshalJSON handles deserialization of a TaxID.
// This custom unmarshaling is needed because the resulting
// property may be an id or the full struct if it was expanded.
func (c *TaxID) UnmarshalJSON(data []byte) error {
	if id, ok := ParseID(data); ok {
		c.ID = id
		return nil
	}

	type taxid TaxID
	var v taxid
	if err := json.Unmarshal(data, &v); err != nil {
		return err
	}

	*c = TaxID(v)
	return nil
}
<|endoftext|>


<commit_before><commit_msg>App api docs<commit_after><|endoftext|>


<commit_before>/* Copyright (c) 2017 Jason Ish
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package auth

import (
	"github.com/jasonish/evebox/core"
	"github.com/jasonish/evebox/log"
	"github.com/jasonish/evebox/server/sessions"
	"net/http"
)

const username = "anonymous"

// The anonymous authenticator is used when no authentication is desired. Each
// http request will be authenticated with a session without having to login.
type AnonymousAuthenticator struct {
	sessionStore *sessions.SessionStore
}

func NewAnonymousAuthenticator(sessionStore *sessions.SessionStore) *AnonymousAuthenticator {
	return &AnonymousAuthenticator{
		sessionStore: sessionStore,
	}
}

func (a *AnonymousAuthenticator) login(username string) *sessions.Session {
	session := a.sessionStore.NewSession()
	session.Username = username
	session.User = core.User{
		Username: username,
		Id: username,
	}

	a.sessionStore.Put(session)

	return session
}

func (a *AnonymousAuthenticator) Login(r *http.Request) (*sessions.Session, error) {
	log.Info("Logging in anonymous user from %v", r.RemoteAddr)
	session := a.login(username)
	session.RemoteAddr = r.RemoteAddr
	return session, nil
}

func (a *AnonymousAuthenticator) Authenticate(w http.ResponseWriter, r *http.Request) *sessions.Session {

	// Look for an existing session.
	session := a.sessionStore.FindSession(r)
	if session != nil {
		return session
	}

	session, _ = a.Login(r)
	session.RemoteAddr = r.RemoteAddr
	w.Header().Set(SESSION_KEY, session.Id)

	return session
}
<commit_msg>gofmt...<commit_after>/* Copyright (c) 2017 Jason Ish
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package auth

import (
	"github.com/jasonish/evebox/core"
	"github.com/jasonish/evebox/log"
	"github.com/jasonish/evebox/server/sessions"
	"net/http"
)

const username = "anonymous"

// The anonymous authenticator is used when no authentication is desired. Each
// http request will be authenticated with a session without having to login.
type AnonymousAuthenticator struct {
	sessionStore *sessions.SessionStore
}

func NewAnonymousAuthenticator(sessionStore *sessions.SessionStore) *AnonymousAuthenticator {
	return &AnonymousAuthenticator{
		sessionStore: sessionStore,
	}
}

func (a *AnonymousAuthenticator) login(username string) *sessions.Session {
	session := a.sessionStore.NewSession()
	session.Username = username
	session.User = core.User{
		Username: username,
		Id:       username,
	}

	a.sessionStore.Put(session)

	return session
}

func (a *AnonymousAuthenticator) Login(r *http.Request) (*sessions.Session, error) {
	log.Info("Logging in anonymous user from %v", r.RemoteAddr)
	session := a.login(username)
	session.RemoteAddr = r.RemoteAddr
	return session, nil
}

func (a *AnonymousAuthenticator) Authenticate(w http.ResponseWriter, r *http.Request) *sessions.Session {

	// Look for an existing session.
	session := a.sessionStore.FindSession(r)
	if session != nil {
		return session
	}

	session, _ = a.Login(r)
	session.RemoteAddr = r.RemoteAddr
	w.Header().Set(SESSION_KEY, session.Id)

	return session
}
<|endoftext|>