<commit_before>package hugo

import (
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	rice "github.com/GeertJohan/go.rice"
	"github.com/hacdias/filemanager"
	"github.com/hacdias/filemanager/variables"
	"github.com/robfig/cron"
)

type hugo struct {
	// Website root
	Root string `description:"The relative or absolute path to the place where your website is located."`
	// Public folder
	Public string `description:"The relative or absolute path to the public folder."`
	// Hugo executable path
	Exe string `description:"The absolute path to the Hugo executable or the command to execute."`
	// Hugo arguments
	Args []string `description:"The arguments to run when running Hugo"`
	// Indicates if we should clean public before a new publish.
	CleanPublic bool `description:"Indicates if the public folder should be cleaned before publishing the website."`

	// TODO: admin interface to cgange options
}

func (h hugo) BeforeAPI(c *filemanager.RequestContext, w http.ResponseWriter, r *http.Request) (int, error) {
	// If we are using the 'magic url' for the settings, we should redirect the
	// request for the acutual path.
	if r.URL.Path == "/settings/" || r.URL.Path == "/settings" {
		var frontmatter string
		var err error

		if _, err = os.Stat(filepath.Join(h.Root, "config.yaml")); err == nil {
			frontmatter = "yaml"
		}

		if _, err = os.Stat(filepath.Join(h.Root, "config.json")); err == nil {
			frontmatter = "json"
		}

		if _, err = os.Stat(filepath.Join(h.Root, "config.toml")); err == nil {
			frontmatter = "toml"
		}

		r.URL.Path = "/config." + frontmatter
		return 0, nil
	}

	// From here on, we only care about 'hugo' router so we can bypass
	// the others.
	if c.Router != "hugo" {
		return 0, nil
	}

	// If we are not using HTTP Post, we shall return Method Not Allowed
	// since we are only working with this method.
	if r.Method != http.MethodPost {
		return http.StatusMethodNotAllowed, nil
	}

	// If we are creating a file built from an archetype.
	if r.Header.Get("Archetype") != "" {
		if !c.User.AllowNew {
			return http.StatusForbidden, nil
		}

		filename := filepath.Join(string(c.User.FileSystem), r.URL.Path)
		filename = strings.TrimPrefix(filename, "/")
		archetype := r.Header.Get("archetype")

		ext := filepath.Ext(filename)

		// If the request isn't for a markdown file, we can't
		// handle it.
		if ext != ".markdown" && ext != ".md" {
			return http.StatusBadRequest, errUnsupportedFileType
		}

		// Tries to create a new file based on this archetype.
		args := []string{"new", filename, "--kind", archetype}
		if err := Run(h.Exe, args, h.Root); err != nil {
			return http.StatusInternalServerError, err
		}

		// Writes the location of the new file to the Header.
		w.Header().Set("Location", "/files/content/"+filename)
		return http.StatusCreated, nil
	}

	// If we are trying to regenerate the website.
	if r.Header.Get("Regenerate") == "true" {
		if !c.User.Permissions["allowPublish"] {
			return http.StatusForbidden, nil
		}

		filename := filepath.Join(string(c.User.FileSystem), r.URL.Path)
		filename = strings.TrimPrefix(filename, "/")

		// Before save command handler.
		if err := c.FM.Runner("before_publish", filename); err != nil {
			return http.StatusInternalServerError, err
		}

		// We only run undraft command if it is a file.
		if !strings.HasSuffix(filename, "/") {
			args := []string{"undraft", filename}
			if err := Run(h.Exe, args, h.Root); err != nil {
				return http.StatusInternalServerError, err
			}
		}

		// Regenerates the file
		h.run(false)

		// Executed the before publish command.
		if err := c.FM.Runner("before_publish", filename); err != nil {
			return http.StatusInternalServerError, err
		}

		return http.StatusOK, nil
	}

	if r.Header.Get("Schedule") != "" {
		if !c.User.Permissions["allowPublish"] {
			return http.StatusForbidden, nil
		}

		return h.schedule(c, w, r)
	}

	return http.StatusNotFound, nil
}

func (h hugo) AfterAPI(c *filemanager.RequestContext, w http.ResponseWriter, r *http.Request) (int, error) {
	return 0, nil
}

func (h hugo) JavaScript() string {
	return rice.MustFindBox("./").MustString("hugo.js")
}

// run runs Hugo with the define arguments.
func (h hugo) run(force bool) {
	// If the CleanPublic option is enabled, clean it.
	if h.CleanPublic {
		os.RemoveAll(h.Public)
	}

	// Prevent running if watching is enabled
	if b, pos := variables.StringInSlice("--watch", h.Args); b && !force {
		if len(h.Args) > pos && h.Args[pos+1] != "false" {
			return
		}

		if len(h.Args) == pos+1 {
			return
		}
	}

	if err := Run(h.Exe, h.Args, h.Root); err != nil {
		log.Println(err)
	}
}

// schedule schedules a post to be published later.
func (h hugo) schedule(c *filemanager.RequestContext, w http.ResponseWriter, r *http.Request) (int, error) {
	t, err := time.Parse("2006-01-02T15:04", r.Header.Get("Schedule"))
	path := filepath.Join(string(c.User.FileSystem), r.URL.Path)
	path = filepath.Clean(path)

	if err != nil {
		return http.StatusInternalServerError, err
	}

	scheduler := cron.New()
	scheduler.AddFunc(t.Format("05 04 15 02 01 *"), func() {
		args := []string{"undraft", path}
		if err := Run(h.Exe, args, h.Root); err != nil {
			log.Printf(err.Error())
			return
		}

		h.run(false)
	})

	scheduler.Start()
	return http.StatusOK, nil
}
<commit_msg>Don't throw error if it is not a draft<commit_after>package hugo

import (
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	rice "github.com/GeertJohan/go.rice"
	"github.com/hacdias/filemanager"
	"github.com/hacdias/filemanager/variables"
	"github.com/robfig/cron"
)

type hugo struct {
	// Website root
	Root string `description:"The relative or absolute path to the place where your website is located."`
	// Public folder
	Public string `description:"The relative or absolute path to the public folder."`
	// Hugo executable path
	Exe string `description:"The absolute path to the Hugo executable or the command to execute."`
	// Hugo arguments
	Args []string `description:"The arguments to run when running Hugo"`
	// Indicates if we should clean public before a new publish.
	CleanPublic bool `description:"Indicates if the public folder should be cleaned before publishing the website."`

	// TODO: admin interface to cgange options
}

func (h hugo) BeforeAPI(c *filemanager.RequestContext, w http.ResponseWriter, r *http.Request) (int, error) {
	// If we are using the 'magic url' for the settings, we should redirect the
	// request for the acutual path.
	if r.URL.Path == "/settings/" || r.URL.Path == "/settings" {
		var frontmatter string
		var err error

		if _, err = os.Stat(filepath.Join(h.Root, "config.yaml")); err == nil {
			frontmatter = "yaml"
		}

		if _, err = os.Stat(filepath.Join(h.Root, "config.json")); err == nil {
			frontmatter = "json"
		}

		if _, err = os.Stat(filepath.Join(h.Root, "config.toml")); err == nil {
			frontmatter = "toml"
		}

		r.URL.Path = "/config." + frontmatter
		return 0, nil
	}

	// From here on, we only care about 'hugo' router so we can bypass
	// the others.
	if c.Router != "hugo" {
		return 0, nil
	}

	// If we are not using HTTP Post, we shall return Method Not Allowed
	// since we are only working with this method.
	if r.Method != http.MethodPost {
		return http.StatusMethodNotAllowed, nil
	}

	// If we are creating a file built from an archetype.
	if r.Header.Get("Archetype") != "" {
		if !c.User.AllowNew {
			return http.StatusForbidden, nil
		}

		filename := filepath.Join(string(c.User.FileSystem), r.URL.Path)
		filename = strings.TrimPrefix(filename, "/")
		archetype := r.Header.Get("archetype")

		ext := filepath.Ext(filename)

		// If the request isn't for a markdown file, we can't
		// handle it.
		if ext != ".markdown" && ext != ".md" {
			return http.StatusBadRequest, errUnsupportedFileType
		}

		// Tries to create a new file based on this archetype.
		args := []string{"new", filename, "--kind", archetype}
		if err := Run(h.Exe, args, h.Root); err != nil {
			return http.StatusInternalServerError, err
		}

		// Writes the location of the new file to the Header.
		w.Header().Set("Location", "/files/content/"+filename)
		return http.StatusCreated, nil
	}

	// If we are trying to regenerate the website.
	if r.Header.Get("Regenerate") == "true" {
		if !c.User.Permissions["allowPublish"] {
			return http.StatusForbidden, nil
		}

		filename := filepath.Join(string(c.User.FileSystem), r.URL.Path)
		filename = strings.TrimPrefix(filename, "/")

		// Before save command handler.
		if err := c.FM.Runner("before_publish", filename); err != nil {
			return http.StatusInternalServerError, err
		}

		// We only run undraft command if it is a file.
		if !strings.HasSuffix(filename, "/") {
			args := []string{"undraft", filename}
			if err := Run(h.Exe, args, h.Root); err != nil && !strings.Contains(err.Error(), "not a Draft") {
				return http.StatusInternalServerError, err
			}
		}

		// Regenerates the file
		h.run(false)

		// Executed the before publish command.
		if err := c.FM.Runner("before_publish", filename); err != nil {
			return http.StatusInternalServerError, err
		}

		return http.StatusOK, nil
	}

	if r.Header.Get("Schedule") != "" {
		if !c.User.Permissions["allowPublish"] {
			return http.StatusForbidden, nil
		}

		return h.schedule(c, w, r)
	}

	return http.StatusNotFound, nil
}

func (h hugo) AfterAPI(c *filemanager.RequestContext, w http.ResponseWriter, r *http.Request) (int, error) {
	return 0, nil
}

func (h hugo) JavaScript() string {
	return rice.MustFindBox("./").MustString("hugo.js")
}

// run runs Hugo with the define arguments.
func (h hugo) run(force bool) {
	// If the CleanPublic option is enabled, clean it.
	if h.CleanPublic {
		os.RemoveAll(h.Public)
	}

	// Prevent running if watching is enabled
	if b, pos := variables.StringInSlice("--watch", h.Args); b && !force {
		if len(h.Args) > pos && h.Args[pos+1] != "false" {
			return
		}

		if len(h.Args) == pos+1 {
			return
		}
	}

	if err := Run(h.Exe, h.Args, h.Root); err != nil {
		log.Println(err)
	}
}

// schedule schedules a post to be published later.
func (h hugo) schedule(c *filemanager.RequestContext, w http.ResponseWriter, r *http.Request) (int, error) {
	t, err := time.Parse("2006-01-02T15:04", r.Header.Get("Schedule"))
	path := filepath.Join(string(c.User.FileSystem), r.URL.Path)
	path = filepath.Clean(path)

	if err != nil {
		return http.StatusInternalServerError, err
	}

	scheduler := cron.New()
	scheduler.AddFunc(t.Format("05 04 15 02 01 *"), func() {
		args := []string{"undraft", path}
		if err := Run(h.Exe, args, h.Root); err != nil {
			log.Printf(err.Error())
			return
		}

		h.run(false)
	})

	scheduler.Start()
	return http.StatusOK, nil
}
<|endoftext|>


<commit_before>// run

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

// darwin/386 seems to mangle the PC and SP before
// it manages to invoke the signal handler, so this test fails there.
// +build !darwin !386
//
// openbsd/386 and plan9/386 don't work, not sure why.
// +build !openbsd !386
// +build !plan9 !386
//
// windows doesn't work, because Windows exception handling
// delivers signals based on the current PC, and that current PC
// doesn't go into the Go runtime.
// +build !windows

package main

import (
	"runtime"
	"runtime/debug"
	"unsafe"
)

func main() {
	debug.SetPanicOnFault(true)
	defer func() {
		if err := recover(); err == nil {
			panic("not panicking")
		}
		pc, _, _, _ := runtime.Caller(10)
		f := runtime.FuncForPC(pc)
		if f == nil || f.Name() != "main.f" {
			if f == nil {
				println("no func for ", unsafe.Pointer(pc))
			} else {
				println("found func:", f.Name())
			}
			panic("cannot find main.f on stack")
		}
	}()
	f(20)
}

func f(n int) {
	if n > 0 {
		f(n-1)
	}
	var f struct {
		x uintptr
	}
	f.x = uintptr(unsafe.Pointer(&f))
	fn := *(*func())(unsafe.Pointer(&f))
	fn()
}
<commit_msg>test/fixedbugs: disable issue11656.go on arm64<commit_after>// run

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

// darwin/386 seems to mangle the PC and SP before
// it manages to invoke the signal handler, so this test fails there.
// +build !darwin !386
//
// openbsd/386 and plan9/386 don't work, not sure why.
// +build !openbsd !386
// +build !plan9 !386
//
// windows doesn't work, because Windows exception handling
// delivers signals based on the current PC, and that current PC
// doesn't go into the Go runtime.
// +build !windows
//
// arm64 gets "illegal instruction" (why is the data executable?)
// and is unable to do the traceback correctly (why?).
// +build !arm64

package main

import (
	"runtime"
	"runtime/debug"
	"unsafe"
)

func main() {
	debug.SetPanicOnFault(true)
	defer func() {
		if err := recover(); err == nil {
			panic("not panicking")
		}
		pc, _, _, _ := runtime.Caller(10)
		f := runtime.FuncForPC(pc)
		if f == nil || f.Name() != "main.f" {
			if f == nil {
				println("no func for ", unsafe.Pointer(pc))
			} else {
				println("found func:", f.Name())
			}
			panic("cannot find main.f on stack")
		}
	}()
	f(20)
}

func f(n int) {
	if n > 0 {
		f(n - 1)
	}
	var f struct {
		x uintptr
	}
	f.x = uintptr(unsafe.Pointer(&f))
	fn := *(*func())(unsafe.Pointer(&f))
	fn()
}
<|endoftext|>


<commit_before>package airbrake // import "gopkg.in/gemnasium/logrus-airbrake-hook.v4"

import (
	"errors"
	"fmt"
	"net/http"
	"os"

	"github.com/airbrake/gobrake/v4"
	"github.com/sirupsen/logrus"
)

// default levels to be fired when logging on
var defaultLevels = []logrus.Level{
	logrus.ErrorLevel,
	logrus.FatalLevel,
	logrus.PanicLevel,
}

// AirbrakeHook to send exceptions to an exception-tracking service compatible
// with the Airbrake API.
type airbrakeHook struct {
	Airbrake *gobrake.Notifier
	levels   []logrus.Level
}

func NewHook(projectID int64, apiKey, env string, levels ...logrus.Level) *airbrakeHook {
	airbrake := gobrake.NewNotifier(projectID, apiKey)
	airbrake.AddFilter(func(notice *gobrake.Notice) *gobrake.Notice {
		if env == "development" {
			return nil
		}
		notice.Context["environment"] = env
		return notice
	})

	if len(levels) == 0 {
		levels = defaultLevels
	}

	hook := &airbrakeHook{
		Airbrake: airbrake,
		levels:   levels,
	}
	return hook
}

func (hook *airbrakeHook) Fire(entry *logrus.Entry) error {
	var notifyErr error
	err, ok := entry.Data["error"].(error)
	if ok {
		notifyErr = err
	} else {
		notifyErr = errors.New(entry.Message)
	}
	var req *http.Request
	for k, v := range entry.Data {
		if r, ok := v.(*http.Request); ok {
			req = r
			delete(entry.Data, k)
			break
		}
	}
	notice := hook.Airbrake.Notice(notifyErr, req, 3)
	for k, v := range entry.Data {
		notice.Context[k] = fmt.Sprintf("%s", v)
	}

	hook.sendNotice(notice)
	return nil
}

func (hook *airbrakeHook) sendNotice(notice *gobrake.Notice) {
	if _, err := hook.Airbrake.SendNotice(notice); err != nil {
		fmt.Fprintf(os.Stderr, "Failed to send error to Airbrake: %v\n", err)
	}
}

func (hook *airbrakeHook) Levels() []logrus.Level {
	return hook.levels
}
<commit_msg>export levels field, to keep same api<commit_after>package airbrake // import "gopkg.in/gemnasium/logrus-airbrake-hook.v4"

import (
	"errors"
	"fmt"
	"net/http"
	"os"

	"github.com/airbrake/gobrake/v4"
	"github.com/sirupsen/logrus"
)

// default levels to be fired when logging on
var defaultLevels = []logrus.Level{
	logrus.ErrorLevel,
	logrus.FatalLevel,
	logrus.PanicLevel,
}

// AirbrakeHook to send exceptions to an exception-tracking service compatible
// with the Airbrake API.
type airbrakeHook struct {
	Airbrake  *gobrake.Notifier
	UseLevels []logrus.Level
}

func NewHook(projectID int64, apiKey, env string) *airbrakeHook {
	airbrake := gobrake.NewNotifier(projectID, apiKey)
	airbrake.AddFilter(func(notice *gobrake.Notice) *gobrake.Notice {
		if env == "development" {
			return nil
		}
		notice.Context["environment"] = env
		return notice
	})

	hook := &airbrakeHook{
		Airbrake:  airbrake,
		UseLevels: defaultLevels,
	}
	return hook
}

func (hook *airbrakeHook) Fire(entry *logrus.Entry) error {
	var notifyErr error
	err, ok := entry.Data["error"].(error)
	if ok {
		notifyErr = err
	} else {
		notifyErr = errors.New(entry.Message)
	}
	var req *http.Request
	for k, v := range entry.Data {
		if r, ok := v.(*http.Request); ok {
			req = r
			delete(entry.Data, k)
			break
		}
	}
	notice := hook.Airbrake.Notice(notifyErr, req, 3)
	for k, v := range entry.Data {
		notice.Context[k] = fmt.Sprintf("%s", v)
	}

	hook.sendNotice(notice)
	return nil
}

func (hook *airbrakeHook) sendNotice(notice *gobrake.Notice) {
	if _, err := hook.Airbrake.SendNotice(notice); err != nil {
		fmt.Fprintf(os.Stderr, "Failed to send error to Airbrake: %v\n", err)
	}
}

func (hook *airbrakeHook) Levels() []logrus.Level {
	return hook.UseLevels
}
<|endoftext|>


<commit_before>// Copyright 2015 Ethan Miller. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package editor

import (
	"errors"

	"github.com/millere/jk/keys"
)

// A ModeFunc is a function that can be executed by a keypress in a mode
type ModeFunc func(v *View, count int) error

// A Mode is used to map keypresses into events in the editor
type Mode struct {
	OnEnter  func(v *View) error
	OnExit   func(v *View) error
	EventMap map[keys.Keypress]ModeFunc
}

// Normal returns a simple normal mode for testing
func Normal() Mode {
	m := make(map[keys.Keypress]ModeFunc)
	m[keys.Keypress{Key: 'h'}] = func(v *View, count int) error {
		v.MoveCursor(-1, 0)
		return nil
	}
	m[keys.Keypress{Key: 'n'}] = func(v *View, count int) error {
		v.MoveCursor(0, 1)
		return nil
	}
	m[keys.Keypress{Key: 'e'}] = func(v *View, count int) error {
		v.MoveCursor(0, -1)
		return nil
	}
	m[keys.Keypress{Key: 'i'}] = func(v *View, count int) error {
		v.MoveCursor(1, 0)
		return nil
	}
	m[keys.Keypress{Key: keys.Esc}] = func(v *View, count int) error {
		return errors.New("Should quit")
	}
	m[keys.Keypress{Key: 't'}] = func(v *View, count int) error {
		v.SetMode((*v.modes)["insert"])
		return nil
	}

	return Mode{
		OnEnter:  nil,
		OnExit:   nil,
		EventMap: m,
	}
}

func Insert() Mode {
	m := make(map[keys.Keypress]ModeFunc)
	m[keys.Keypress{Key: keys.Esc}] = func(v *View, count int) error {
		v.SetMode((*v.modes)["normal"])
		return nil
	}
	m[keys.Keypress{Key: 'a'}] = func(v *View, count int) error {
		for i := 0; i < count; i++ {
			v.InsertChar('a')
		}
		return nil
	}
	return Mode{
		OnEnter:  nil,
		OnExit:   nil,
		EventMap: m,
	}
}

// RegisterMode registers a mode for use in the editor with a name to be referred to as
func (e *Editor) RegisterMode(name string, mode Mode) {
	e.Log("Adding mode:", name)
	e.modes[name] = &mode
	e.Log("Modes:", e.modes)
}
<commit_msg>Add all printable characters! Woohoo!<commit_after>// Copyright 2015 Ethan Miller. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package editor

import (
	"errors"

	"github.com/millere/jk/keys"
)

// A ModeFunc is a function that can be executed by a keypress in a mode
type ModeFunc func(v *View, count int) error

// A Mode is used to map keypresses into events in the editor
type Mode struct {
	OnEnter  func(v *View) error
	OnExit   func(v *View) error
	EventMap map[keys.Keypress]ModeFunc
}

// Normal returns a simple normal mode for testing
func Normal() Mode {
	m := make(map[keys.Keypress]ModeFunc)
	m[keys.Keypress{Key: 'h'}] = func(v *View, count int) error {
		v.MoveCursor(-1, 0)
		return nil
	}
	m[keys.Keypress{Key: 'n'}] = func(v *View, count int) error {
		v.MoveCursor(0, 1)
		return nil
	}
	m[keys.Keypress{Key: 'e'}] = func(v *View, count int) error {
		v.MoveCursor(0, -1)
		return nil
	}
	m[keys.Keypress{Key: 'i'}] = func(v *View, count int) error {
		v.MoveCursor(1, 0)
		return nil
	}
	m[keys.Keypress{Key: keys.Esc}] = func(v *View, count int) error {
		return errors.New("Should quit")
	}
	m[keys.Keypress{Key: 't'}] = func(v *View, count int) error {
		v.SetMode((*v.modes)["insert"])
		return nil
	}

	return Mode{
		OnEnter:  nil,
		OnExit:   nil,
		EventMap: m,
	}
}

func Insert() Mode {
	m := make(map[keys.Keypress]ModeFunc)
	m[keys.Keypress{Key: keys.Esc}] = func(v *View, count int) error {
		v.SetMode((*v.modes)["normal"])
		return nil
	}

	insertable := []rune{}
	for c := rune(0x20); c <= 0x7E; c++ {
		insertable = append(insertable, c)
	}
	LogItAll.Println("Adding insertables:", insertable)

	for _, c := range insertable {
		cc := c
		m[keys.Keypress{Key: cc}] = func(v *View, count int) error {
			for i := 0; i < count; i++ {
				v.InsertChar(cc)
				v.MoveCursor(1, 0)
			}
			return nil
		}
	}
	return Mode{
		OnEnter:  nil,
		OnExit:   nil,
		EventMap: m,
	}
}

// RegisterMode registers a mode for use in the editor with a name to be referred to as
func (e *Editor) RegisterMode(name string, mode Mode) {
	e.Log("Adding mode:", name)
	e.modes[name] = &mode
	e.Log("Modes:", e.modes)
}
<|endoftext|>


<commit_before><commit_msg>move the call of detectURL from NewRemoteAllocator to (*RemoteAllocator).Allocate<commit_after><|endoftext|>


<commit_before>// Package socket provides a concise, powerful and high-performance TCP.
//
// Copyright 2017 HenryLee. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package socket

import (
	"fmt"
	"io"
	"net"
	"sync"
	"sync/atomic"
	"time"

	"github.com/henrylee2cn/goutil"
	"github.com/henrylee2cn/goutil/errors"
)

type (
	// Socket is a generic stream-oriented network connection.
	//
	// Multiple goroutines may invoke methods on a Socket simultaneously.
	Socket interface {
		// LocalAddr returns the local network address.
		LocalAddr() net.Addr
		// RemoteAddr returns the remote network address.
		RemoteAddr() net.Addr
		// SetDeadline sets the read and write deadlines associated
		// with the connection. It is equivalent to calling both
		// SetReadDeadline and SetWriteDeadline.
		//
		// A deadline is an absolute time after which I/O operations
		// fail with a timeout (see type Error) instead of
		// blocking. The deadline applies to all future and pending
		// I/O, not just the immediately following call to Read or
		// Write. After a deadline has been exceeded, the connection
		// can be refreshed by setting a deadline in the future.
		//
		// An idle timeout can be implemented by repeatedly extending
		// the deadline after successful Read or Write calls.
		//
		// A zero value for t means I/O operations will not time out.
		SetDeadline(t time.Time) error
		// SetReadDeadline sets the deadline for future Read calls
		// and any currently-blocked Read call.
		// A zero value for t means Read will not time out.
		SetReadDeadline(t time.Time) error
		// SetWriteDeadline sets the deadline for future Write calls
		// and any currently-blocked Write call.
		// Even if write times out, it may return n > 0, indicating that
		// some of the data was successfully written.
		// A zero value for t means Write will not time out.
		SetWriteDeadline(t time.Time) error
		// WritePacket writes header and body to the connection.
		// Note: must be safe for concurrent use by multiple goroutines.
		WritePacket(packet *Packet) error
		// ReadPacket reads header and body from the connection.
		// Note: must be safe for concurrent use by multiple goroutines.
		ReadPacket(packet *Packet) error
		// Read reads data from the connection.
		// Read can be made to time out and return an Error with Timeout() == true
		// after a fixed time limit; see SetDeadline and SetReadDeadline.
		Read(b []byte) (n int, err error)
		// Write writes data to the connection.
		// Write can be made to time out and return an Error with Timeout() == true
		// after a fixed time limit; see SetDeadline and SetWriteDeadline.
		Write(b []byte) (n int, err error)
		// Close closes the connection socket.
		// Any blocked Read or Write operations will be unblocked and return errors.
		Close() error
		// Public returns temporary public data of Socket.
		Public() goutil.Map
		// PublicLen returns the length of public data of Socket.
		PublicLen() int
		// Id returns the socket id.
		Id() string
		// SetId sets the socket id.
		SetId(string)
		// Reset reset net.Conn and ProtoFunc.
		Reset(netConn net.Conn, protoFunc ...ProtoFunc)
	}
	socket struct {
		net.Conn
		protocol  Proto
		id        string
		idMutex   sync.RWMutex
		ctxPublic goutil.Map
		mu        sync.RWMutex
		curState  int32
		fromPool  bool
	}
)

const (
	normal      int32 = 0
	activeClose int32 = 1
)

var _ net.Conn = Socket(nil)

// ErrProactivelyCloseSocket proactively close the socket error.
var ErrProactivelyCloseSocket = errors.New("socket is closed proactively")

// GetSocket gets a Socket from pool, and reset it.
func GetSocket(c net.Conn, protoFunc ...ProtoFunc) Socket {
	s := socketPool.Get().(*socket)
	s.Reset(c, protoFunc...)
	return s
}

var socketPool = sync.Pool{
	New: func() interface{} {
		s := newSocket(nil, nil)
		s.fromPool = true
		return s
	},
}

// NewSocket wraps a net.Conn as a Socket.
func NewSocket(c net.Conn, protoFunc ...ProtoFunc) Socket {
	return newSocket(c, protoFunc)
}

func newSocket(c net.Conn, protoFuncs []ProtoFunc) *socket {
	var s = &socket{
		protocol: getProto(protoFuncs, c),
		Conn:     c,
	}
	s.optimize()
	return s
}

// WritePacket writes header and body to the connection.
// WritePacket can be made to time out and return an Error with Timeout() == true
// after a fixed time limit; see SetDeadline and SetWriteDeadline.
// Note:
//  For the byte stream type of body, write directly, do not do any processing;
//  Must be safe for concurrent use by multiple goroutines.
func (s *socket) WritePacket(packet *Packet) error {
	s.mu.RLock()
	protocol := s.protocol
	s.mu.RUnlock()
	err := protocol.Pack(packet)
	if err != nil && s.isActiveClosed() {
		err = ErrProactivelyCloseSocket
	}
	return err
}

// ReadPacket reads header and body from the connection.
// Note:
//  For the byte stream type of body, read directly, do not do any processing;
//  Must be safe for concurrent use by multiple goroutines.
func (s *socket) ReadPacket(packet *Packet) error {
	s.mu.RLock()
	protocol := s.protocol
	s.mu.RUnlock()
	return protocol.Unpack(packet)
}

// Public returns temporary public data of Socket.
func (s *socket) Public() goutil.Map {
	if s.ctxPublic == nil {
		s.ctxPublic = goutil.RwMap()
	}
	return s.ctxPublic
}

// PublicLen returns the length of public data of Socket.
func (s *socket) PublicLen() int {
	if s.ctxPublic == nil {
		return 0
	}
	return s.ctxPublic.Len()
}

// Id returns the socket id.
func (s *socket) Id() string {
	s.idMutex.RLock()
	id := s.id
	if len(id) == 0 {
		id = s.RemoteAddr().String()
	}
	s.idMutex.RUnlock()
	return id
}

// SetId sets the socket id.
func (s *socket) SetId(id string) {
	s.idMutex.Lock()
	s.id = id
	s.idMutex.Unlock()
}

// Reset reset net.Conn and ProtoFunc.
func (s *socket) Reset(netConn net.Conn, protoFunc ...ProtoFunc) {
	atomic.StoreInt32(&s.curState, activeClose)
	if s.Conn != nil {
		s.Conn.Close()
	}
	s.mu.Lock()
	s.Conn = netConn
	s.SetId("")
	s.protocol = getProto(protoFunc, netConn)
	atomic.StoreInt32(&s.curState, normal)
	s.optimize()
	s.mu.Unlock()
}

// Close closes the connection socket.
// Any blocked Read or Write operations will be unblocked and return errors.
// If it is from 'GetSocket()' function(a pool), return itself to pool.
func (s *socket) Close() error {
	if s.isActiveClosed() {
		return nil
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	if s.isActiveClosed() {
		return nil
	}
	atomic.StoreInt32(&s.curState, activeClose)

	var err error
	if s.Conn != nil {
		err = s.Conn.Close()
	}
	if s.fromPool {
		s.Conn = nil
		s.ctxPublic = nil
		s.protocol = nil
		socketPool.Put(s)
	}
	return err
}

func (s *socket) isActiveClosed() bool {
	return atomic.LoadInt32(&s.curState) == activeClose
}

func (s *socket) optimize() {
	if c, ok := s.Conn.(ifaceSetKeepAlive); ok {
		if changeKeepAlive {
			c.SetKeepAlive(keepAlive)
		}
		if keepAlivePeriod >= 0 && keepAlive {
			c.SetKeepAlivePeriod(keepAlivePeriod)
		}
	}
	if c, ok := s.Conn.(ifaceSetBuffer); ok {
		if readBuffer >= 0 {
			c.SetReadBuffer(readBuffer)
		}
		if writeBuffer >= 0 {
			c.SetWriteBuffer(writeBuffer)
		}
	}
	if c, ok := s.Conn.(ifaceSetNoDelay); ok {
		if !noDelay {
			c.SetNoDelay(noDelay)
		}
	}
}

type (
	ifaceSetKeepAlive interface {
		// SetKeepAlive sets whether the operating system should send
		// keepalive messages on the connection.
		SetKeepAlive(keepalive bool) error
		// SetKeepAlivePeriod sets period between keep alives.
		SetKeepAlivePeriod(d time.Duration) error
	}
	ifaceSetBuffer interface {
		// SetReadBuffer sets the size of the operating system's
		// receive buffer associated with the connection.
		SetReadBuffer(bytes int) error
		// SetWriteBuffer sets the size of the operating system's
		// transmit buffer associated with the connection.
		SetWriteBuffer(bytes int) error
	}
	ifaceSetNoDelay interface {
		// SetNoDelay controls whether the operating system should delay
		// packet transmission in hopes of sending fewer packets (Nagle's
		// algorithm).  The default is true (no delay), meaning that data is
		// sent as soon as possible after a Write.
		SetNoDelay(noDelay bool) error
	}
)

// Connection related system configuration
var (
	writeBuffer     int           = -1
	readBuffer      int           = -1
	changeKeepAlive bool          = false
	keepAlive       bool          = true
	keepAlivePeriod time.Duration = -1
	noDelay         bool          = true
)

// SetKeepAlive sets whether the operating system should send
// keepalive messages on the connection.
// Note: If have not called the function, the system defaults are used.
func SetKeepAlive(keepalive bool) {
	changeKeepAlive = true
	keepAlive = keepalive
}

// SetKeepAlivePeriod sets period between keep alives.
// Note: if d<0, don't change the value.
func SetKeepAlivePeriod(d time.Duration) {
	if d >= 0 {
		keepAlivePeriod = d
	} else {
		fmt.Println("socket: SetKeepAlivePeriod: invalid keepAlivePeriod:", d)
	}
}

// ReadBuffer returns the size of the operating system's
// receive buffer associated with the connection.
// Note: if using the system default value, bytes=-1 and isDefault=true.
func ReadBuffer() (bytes int, isDefault bool) {
	return readBuffer, readBuffer == -1
}

// SetReadBuffer sets the size of the operating system's
// receive buffer associated with the connection.
// Note: if bytes<0, don't change the value.
func SetReadBuffer(bytes int) {
	if bytes >= 0 {
		readBuffer = bytes
	} else {
		fmt.Println("socket: SetReadBuffer: invalid readBuffer size:", bytes)
	}
}

// WriteBuffer returns the size of the operating system's
// transmit buffer associated with the connection.
// Note: if using the system default value, bytes=-1 and isDefault=true.
func WriteBuffer() (bytes int, isDefault bool) {
	return writeBuffer, writeBuffer == -1
}

// SetWriteBuffer sets the size of the operating system's
// transmit buffer associated with the connection.
// Note: if bytes<0, don't change the value.
func SetWriteBuffer(bytes int) {
	if bytes >= 0 {
		writeBuffer = bytes
	} else {
		fmt.Println("socket: SetWriteBuffer: invalid writeBuffer size:", bytes)
	}
}

// SetNoDelay controls whether the operating system should delay
// packet transmission in hopes of sending fewer packets (Nagle's
// algorithm).  The default is true (no delay), meaning that data is
// sent as soon as possible after a Write.
func SetNoDelay(_noDelay bool) {
	noDelay = _noDelay
}

func getProto(protoFuncs []ProtoFunc, rw io.ReadWriter) Proto {
	if len(protoFuncs) > 0 && protoFuncs[0] != nil {
		return protoFuncs[0](rw)
	}
	return defaultProtoFunc(rw)
}
<commit_msg>socket: add SyscallConn method<commit_after>// Package socket provides a concise, powerful and high-performance TCP.
//
// Copyright 2017 HenryLee. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package socket

import (
	"fmt"
	"io"
	"net"
	"sync"
	"sync/atomic"
	"syscall"
	"time"

	"github.com/henrylee2cn/goutil"
	"github.com/henrylee2cn/goutil/errors"
)

type (
	// Socket is a generic stream-oriented network connection.
	//
	// Multiple goroutines may invoke methods on a Socket simultaneously.
	Socket interface {
		// // Conn returns the real network connection.
		// Conn() net.Conn

		// SyscallConn returns a raw network connection.
		// This implements the syscall.Conn interface.
		SyscallConn() (syscall.RawConn, error)
		// LocalAddr returns the local network address.
		LocalAddr() net.Addr
		// RemoteAddr returns the remote network address.
		RemoteAddr() net.Addr
		// SetDeadline sets the read and write deadlines associated
		// with the connection. It is equivalent to calling both
		// SetReadDeadline and SetWriteDeadline.
		//
		// A deadline is an absolute time after which I/O operations
		// fail with a timeout (see type Error) instead of
		// blocking. The deadline applies to all future and pending
		// I/O, not just the immediately following call to Read or
		// Write. After a deadline has been exceeded, the connection
		// can be refreshed by setting a deadline in the future.
		//
		// An idle timeout can be implemented by repeatedly extending
		// the deadline after successful Read or Write calls.
		//
		// A zero value for t means I/O operations will not time out.
		SetDeadline(t time.Time) error
		// SetReadDeadline sets the deadline for future Read calls
		// and any currently-blocked Read call.
		// A zero value for t means Read will not time out.
		SetReadDeadline(t time.Time) error
		// SetWriteDeadline sets the deadline for future Write calls
		// and any currently-blocked Write call.
		// Even if write times out, it may return n > 0, indicating that
		// some of the data was successfully written.
		// A zero value for t means Write will not time out.
		SetWriteDeadline(t time.Time) error
		// WritePacket writes header and body to the connection.
		// Note: must be safe for concurrent use by multiple goroutines.
		WritePacket(packet *Packet) error
		// ReadPacket reads header and body from the connection.
		// Note: must be safe for concurrent use by multiple goroutines.
		ReadPacket(packet *Packet) error
		// Read reads data from the connection.
		// Read can be made to time out and return an Error with Timeout() == true
		// after a fixed time limit; see SetDeadline and SetReadDeadline.
		Read(b []byte) (n int, err error)
		// Write writes data to the connection.
		// Write can be made to time out and return an Error with Timeout() == true
		// after a fixed time limit; see SetDeadline and SetWriteDeadline.
		Write(b []byte) (n int, err error)
		// Close closes the connection socket.
		// Any blocked Read or Write operations will be unblocked and return errors.
		Close() error
		// Public returns temporary public data of Socket.
		Public() goutil.Map
		// PublicLen returns the length of public data of Socket.
		PublicLen() int
		// Id returns the socket id.
		Id() string
		// SetId sets the socket id.
		SetId(string)
		// Reset reset net.Conn and ProtoFunc.
		Reset(netConn net.Conn, protoFunc ...ProtoFunc)
	}
	socket struct {
		net.Conn
		protocol  Proto
		id        string
		idMutex   sync.RWMutex
		ctxPublic goutil.Map
		mu        sync.RWMutex
		curState  int32
		fromPool  bool
	}
)

const (
	normal      int32 = 0
	activeClose int32 = 1
)

var _ net.Conn = Socket(nil)

// ErrProactivelyCloseSocket proactively close the socket error.
var ErrProactivelyCloseSocket = errors.New("socket is closed proactively")

// GetSocket gets a Socket from pool, and reset it.
func GetSocket(c net.Conn, protoFunc ...ProtoFunc) Socket {
	s := socketPool.Get().(*socket)
	s.Reset(c, protoFunc...)
	return s
}

var socketPool = sync.Pool{
	New: func() interface{} {
		s := newSocket(nil, nil)
		s.fromPool = true
		return s
	},
}

// NewSocket wraps a net.Conn as a Socket.
func NewSocket(c net.Conn, protoFunc ...ProtoFunc) Socket {
	return newSocket(c, protoFunc)
}

func newSocket(c net.Conn, protoFuncs []ProtoFunc) *socket {
	var s = &socket{
		protocol: getProto(protoFuncs, c),
		Conn:     c,
	}
	s.optimize()
	return s
}

// // Conn returns the real network connection.
// func (s *socket) Conn() net.Conn {
// 	return s.Conn
// }

// SyscallConn returns a raw network connection.
// This implements the syscall.Conn interface.
func (s *socket) SyscallConn() (syscall.RawConn, error) {
	syscallConn, ok := s.Conn.(syscall.Conn)
	if !ok {
		return nil, syscall.EINVAL
	}
	return syscallConn.SyscallConn()
}

// WritePacket writes header and body to the connection.
// WritePacket can be made to time out and return an Error with Timeout() == true
// after a fixed time limit; see SetDeadline and SetWriteDeadline.
// Note:
//  For the byte stream type of body, write directly, do not do any processing;
//  Must be safe for concurrent use by multiple goroutines.
func (s *socket) WritePacket(packet *Packet) error {
	s.mu.RLock()
	protocol := s.protocol
	s.mu.RUnlock()
	err := protocol.Pack(packet)
	if err != nil && s.isActiveClosed() {
		err = ErrProactivelyCloseSocket
	}
	return err
}

// ReadPacket reads header and body from the connection.
// Note:
//  For the byte stream type of body, read directly, do not do any processing;
//  Must be safe for concurrent use by multiple goroutines.
func (s *socket) ReadPacket(packet *Packet) error {
	s.mu.RLock()
	protocol := s.protocol
	s.mu.RUnlock()
	return protocol.Unpack(packet)
}

// Public returns temporary public data of Socket.
func (s *socket) Public() goutil.Map {
	if s.ctxPublic == nil {
		s.ctxPublic = goutil.RwMap()
	}
	return s.ctxPublic
}

// PublicLen returns the length of public data of Socket.
func (s *socket) PublicLen() int {
	if s.ctxPublic == nil {
		return 0
	}
	return s.ctxPublic.Len()
}

// Id returns the socket id.
func (s *socket) Id() string {
	s.idMutex.RLock()
	id := s.id
	if len(id) == 0 {
		id = s.RemoteAddr().String()
	}
	s.idMutex.RUnlock()
	return id
}

// SetId sets the socket id.
func (s *socket) SetId(id string) {
	s.idMutex.Lock()
	s.id = id
	s.idMutex.Unlock()
}

// Reset reset net.Conn and ProtoFunc.
func (s *socket) Reset(netConn net.Conn, protoFunc ...ProtoFunc) {
	atomic.StoreInt32(&s.curState, activeClose)
	if s.Conn != nil {
		s.Conn.Close()
	}
	s.mu.Lock()
	s.Conn = netConn
	s.SetId("")
	s.protocol = getProto(protoFunc, netConn)
	atomic.StoreInt32(&s.curState, normal)
	s.optimize()
	s.mu.Unlock()
}

// Close closes the connection socket.
// Any blocked Read or Write operations will be unblocked and return errors.
// If it is from 'GetSocket()' function(a pool), return itself to pool.
func (s *socket) Close() error {
	if s.isActiveClosed() {
		return nil
	}
	s.mu.Lock()
	defer s.mu.Unlock()
	if s.isActiveClosed() {
		return nil
	}
	atomic.StoreInt32(&s.curState, activeClose)

	var err error
	if s.Conn != nil {
		err = s.Conn.Close()
	}
	if s.fromPool {
		s.Conn = nil
		s.ctxPublic = nil
		s.protocol = nil
		socketPool.Put(s)
	}
	return err
}

func (s *socket) isActiveClosed() bool {
	return atomic.LoadInt32(&s.curState) == activeClose
}

func (s *socket) optimize() {
	if c, ok := s.Conn.(ifaceSetKeepAlive); ok {
		if changeKeepAlive {
			c.SetKeepAlive(keepAlive)
		}
		if keepAlivePeriod >= 0 && keepAlive {
			c.SetKeepAlivePeriod(keepAlivePeriod)
		}
	}
	if c, ok := s.Conn.(ifaceSetBuffer); ok {
		if readBuffer >= 0 {
			c.SetReadBuffer(readBuffer)
		}
		if writeBuffer >= 0 {
			c.SetWriteBuffer(writeBuffer)
		}
	}
	if c, ok := s.Conn.(ifaceSetNoDelay); ok {
		if !noDelay {
			c.SetNoDelay(noDelay)
		}
	}
}

type (
	ifaceSetKeepAlive interface {
		// SetKeepAlive sets whether the operating system should send
		// keepalive messages on the connection.
		SetKeepAlive(keepalive bool) error
		// SetKeepAlivePeriod sets period between keep alives.
		SetKeepAlivePeriod(d time.Duration) error
	}
	ifaceSetBuffer interface {
		// SetReadBuffer sets the size of the operating system's
		// receive buffer associated with the connection.
		SetReadBuffer(bytes int) error
		// SetWriteBuffer sets the size of the operating system's
		// transmit buffer associated with the connection.
		SetWriteBuffer(bytes int) error
	}
	ifaceSetNoDelay interface {
		// SetNoDelay controls whether the operating system should delay
		// packet transmission in hopes of sending fewer packets (Nagle's
		// algorithm).  The default is true (no delay), meaning that data is
		// sent as soon as possible after a Write.
		SetNoDelay(noDelay bool) error
	}
)

// Connection related system configuration
var (
	writeBuffer     int           = -1
	readBuffer      int           = -1
	changeKeepAlive bool          = false
	keepAlive       bool          = true
	keepAlivePeriod time.Duration = -1
	noDelay         bool          = true
)

// SetKeepAlive sets whether the operating system should send
// keepalive messages on the connection.
// Note: If have not called the function, the system defaults are used.
func SetKeepAlive(keepalive bool) {
	changeKeepAlive = true
	keepAlive = keepalive
}

// SetKeepAlivePeriod sets period between keep alives.
// Note: if d<0, don't change the value.
func SetKeepAlivePeriod(d time.Duration) {
	if d >= 0 {
		keepAlivePeriod = d
	} else {
		fmt.Println("socket: SetKeepAlivePeriod: invalid keepAlivePeriod:", d)
	}
}

// ReadBuffer returns the size of the operating system's
// receive buffer associated with the connection.
// Note: if using the system default value, bytes=-1 and isDefault=true.
func ReadBuffer() (bytes int, isDefault bool) {
	return readBuffer, readBuffer == -1
}

// SetReadBuffer sets the size of the operating system's
// receive buffer associated with the connection.
// Note: if bytes<0, don't change the value.
func SetReadBuffer(bytes int) {
	if bytes >= 0 {
		readBuffer = bytes
	} else {
		fmt.Println("socket: SetReadBuffer: invalid readBuffer size:", bytes)
	}
}

// WriteBuffer returns the size of the operating system's
// transmit buffer associated with the connection.
// Note: if using the system default value, bytes=-1 and isDefault=true.
func WriteBuffer() (bytes int, isDefault bool) {
	return writeBuffer, writeBuffer == -1
}

// SetWriteBuffer sets the size of the operating system's
// transmit buffer associated with the connection.
// Note: if bytes<0, don't change the value.
func SetWriteBuffer(bytes int) {
	if bytes >= 0 {
		writeBuffer = bytes
	} else {
		fmt.Println("socket: SetWriteBuffer: invalid writeBuffer size:", bytes)
	}
}

// SetNoDelay controls whether the operating system should delay
// packet transmission in hopes of sending fewer packets (Nagle's
// algorithm).  The default is true (no delay), meaning that data is
// sent as soon as possible after a Write.
func SetNoDelay(_noDelay bool) {
	noDelay = _noDelay
}

func getProto(protoFuncs []ProtoFunc, rw io.ReadWriter) Proto {
	if len(protoFuncs) > 0 && protoFuncs[0] != nil {
		return protoFuncs[0](rw)
	}
	return defaultProtoFunc(rw)
}
<|endoftext|>


<commit_before>package parser

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"strconv"
	"strings"
	"text/scanner"
)

const (
	// prefixComment is a prefix of a comment.
	prefixComment = "//"

	// vmScanMode is a mode for scanning VM code.
	vmScanMode = scanner.ScanIdents | scanner.ScanInts | scanner.SkipComments
)

// CommandType represents a type of VM command.
type CommandType int

// A list of command types.
const (
	unknown CommandType = iota
	Arithmetic
	Push
	Pop
	Label
	Goto
	If
	Function
	Return
	Call
)

// command has command information.
type command struct {
	typ  CommandType
	arg1 string
	arg2 uint
}

// Parser is a parser for VM code.
// Parser is not thread safe, so it should NOT be used in multiple goroutines.
type Parser struct {
	src    *bufio.Scanner
	line   string
	tokens []string
	cmd    command
}

// New creates a new parser object that reads and parses r.
func New(src io.Reader) *Parser {
	return &Parser{
		src: bufio.NewScanner(src),
	}
}

// HasMoreCommands reports whether there exist more commands in input.
func (p *Parser) HasMoreCommands() bool {
	sc := scanner.Scanner{Mode: vmScanMode}

	// if Scan() == true && Text() is not a comment, return true
	// if Scan() == false, return false
	for p.src.Scan() {
		p.line = p.src.Text()
		p.tokens = p.tokens[:0]

		// prepare a Scanner
		sc.Init(strings.NewReader(p.line))

		// tokenize the current line
		var tok rune
		for tok != scanner.EOF {
			tok = sc.Scan()
			text := sc.TokenText()

			// ignore empty string
			if text == "" {
				continue
			}

			p.tokens = append(p.tokens, text)
		}

		if len(p.tokens) > 0 {
			return true
		}
	}

	p.tokens = p.tokens[:0]
	return false
}

// Advance reads next command from source and set the command to current one.
// If the next command is invalid, it returns an error.
// This method should be called only if HasMoreCommands() returns true.
func (p *Parser) Advance() error {
	cmd, err := p.parse(p.tokens)
	if err != nil {
		return err
	}

	p.cmd = cmd
	return nil
}

// parse parses tokens and returns a command object.
// If it fails to parse tokens, it returns an error.
func (p *Parser) parse(tokens []string) (command, error) {
	// check the length of tokens: should be less than 4
	if len(tokens) == 0 {
		return command{}, errors.New("empty tokens")
	}

	// parse the first token as an opcode
	cmd := tokens[0]
	typ := p.dispatchCommand(cmd)

	switch typ {
	case Arithmetic:
		return p.parseArithmetic(tokens)
	case Push, Pop:
		return p.parsePushPop(typ, tokens)
	case Label:
		return p.parseLabel(tokens)
	default:
		return command{}, fmt.Errorf("unknown command: %s", cmd)
	}
}

// parseArithmetic parses an arithmetic command.
func (p *Parser) parseArithmetic(tokens []string) (command, error) {
	if len(tokens) != 1 {
		return command{}, fmt.Errorf("invalid arithmetic command: %s", p.line)
	}
	return command{typ: Arithmetic, arg1: tokens[0]}, nil
}

// parseLabel parses a label command.
func (p *Parser) parseLabel(tokens []string) (command, error) {
	if len(tokens) != 2 {
		return command{}, fmt.Errorf("invalid label command: %s", p.line)
	}
	return command{typ: Label, arg1: tokens[1]}, nil
}

// parsePushPop parses a push/pop command.
func (p *Parser) parsePushPop(typ CommandType, tokens []string) (command, error) {
	if len(tokens) != 3 {
		return command{}, fmt.Errorf("invalid push/pop command: %s", p.line)
	}

	arg1 := tokens[1]
	if !segs.contains(arg1) {
		return command{}, fmt.Errorf("unknown segment: %s", arg1)
	}

	// parse the third token as an integer
	a := tokens[2]
	i, err := strconv.Atoi(a)
	if i < 0 || err != nil {
		return command{}, fmt.Errorf("not a positive integer: %s", a)
	}

	return command{typ: typ, arg1: arg1, arg2: uint(i)}, nil
}

// dispatchCommand dispatches CommandType from cmd.
// If cmd is not a valid command string, it returns `unknown`.
func (*Parser) dispatchCommand(cmd string) CommandType {
	switch cmd {
	case "add", "sub", "neg", "eq", "gt", "lt", "and", "or", "not":
		return Arithmetic
	case "push":
		return Push
	case "pop":
		return Pop
	case "label":
		return Label
	case "goto":
		return Goto
	case "if-goto":
		return If
	case "function":
		return Function
	case "call":
		return Call
	case "return":
		return Return
	default:
		return unknown
	}
}

// segments is a collection of segments.
type segments []string

// segs is a collection of all the segments on VM.
// TODO use map[string]struct{}
var segs = segments{
	"argument",
	"local",
	"static",
	"constant",
	"this", "that",
	"pointer",
	"temp",
}

// contains reports whether text is contained in s.
func (s segments) contains(text string) bool {
	for _, seg := range s {
		if seg == text {
			return true
		}
	}
	return false
}

// CommandType returns a type of a current VM command. In all arithmetic commands
// it returns Arithmetic.
func (p *Parser) CommandType() CommandType {
	return p.cmd.typ
}

// Arg1 returns the first argument in a current command. If a type of the current command is
// Arithmetic, it returns the command itself. This method should NOT be called if CommandType()
// returns Return.
func (p *Parser) Arg1() string {
	return p.cmd.arg1
}

// Arg2 returns the second argument in a current command. This method should be called
// only if CommandType() returns Push, Pop, Function or Call.
func (p *Parser) Arg2() uint {
	return p.cmd.arg2
}
<commit_msg>vmtranslator/parser: use a map for segments instead of a slice in order to search an existence of a segment by O(1).<commit_after>package parser

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"strconv"
	"strings"
	"text/scanner"
)

const (
	// prefixComment is a prefix of a comment.
	prefixComment = "//"

	// vmScanMode is a mode for scanning VM code.
	vmScanMode = scanner.ScanIdents | scanner.ScanInts | scanner.SkipComments
)

// CommandType represents a type of VM command.
type CommandType int

// A list of command types.
const (
	unknown CommandType = iota
	Arithmetic
	Push
	Pop
	Label
	Goto
	If
	Function
	Return
	Call
)

// command has command information.
type command struct {
	typ  CommandType
	arg1 string
	arg2 uint
}

// Parser is a parser for VM code.
// Parser is not thread safe, so it should NOT be used in multiple goroutines.
type Parser struct {
	src    *bufio.Scanner
	line   string
	tokens []string
	cmd    command
}

// New creates a new parser object that reads and parses r.
func New(src io.Reader) *Parser {
	return &Parser{
		src: bufio.NewScanner(src),
	}
}

// HasMoreCommands reports whether there exist more commands in input.
func (p *Parser) HasMoreCommands() bool {
	sc := scanner.Scanner{Mode: vmScanMode}

	// if Scan() == true && Text() is not a comment, return true
	// if Scan() == false, return false
	for p.src.Scan() {
		p.line = p.src.Text()
		p.tokens = p.tokens[:0]

		// prepare a Scanner
		sc.Init(strings.NewReader(p.line))

		// tokenize the current line
		var tok rune
		for tok != scanner.EOF {
			tok = sc.Scan()
			text := sc.TokenText()

			// ignore empty string
			if text == "" {
				continue
			}

			p.tokens = append(p.tokens, text)
		}

		if len(p.tokens) > 0 {
			return true
		}
	}

	p.tokens = p.tokens[:0]
	return false
}

// Advance reads next command from source and set the command to current one.
// If the next command is invalid, it returns an error.
// This method should be called only if HasMoreCommands() returns true.
func (p *Parser) Advance() error {
	cmd, err := p.parse(p.tokens)
	if err != nil {
		return err
	}

	p.cmd = cmd
	return nil
}

// parse parses tokens and returns a command object.
// If it fails to parse tokens, it returns an error.
func (p *Parser) parse(tokens []string) (command, error) {
	// check the length of tokens: should be less than 4
	if len(tokens) == 0 {
		return command{}, errors.New("empty tokens")
	}

	// parse the first token as an opcode
	cmd := tokens[0]
	typ := p.dispatchCommand(cmd)

	switch typ {
	case Arithmetic:
		return p.parseArithmetic(tokens)
	case Push, Pop:
		return p.parsePushPop(typ, tokens)
	case Label:
		return p.parseLabel(tokens)
	default:
		return command{}, fmt.Errorf("unknown command: %s", cmd)
	}
}

// parseArithmetic parses an arithmetic command.
func (p *Parser) parseArithmetic(tokens []string) (command, error) {
	if len(tokens) != 1 {
		return command{}, fmt.Errorf("invalid arithmetic command: %s", p.line)
	}
	return command{typ: Arithmetic, arg1: tokens[0]}, nil
}

// parseLabel parses a label command.
func (p *Parser) parseLabel(tokens []string) (command, error) {
	if len(tokens) != 2 {
		return command{}, fmt.Errorf("invalid label command: %s", p.line)
	}
	return command{typ: Label, arg1: tokens[1]}, nil
}

// parsePushPop parses a push/pop command.
func (p *Parser) parsePushPop(typ CommandType, tokens []string) (command, error) {
	if len(tokens) != 3 {
		return command{}, fmt.Errorf("invalid push/pop command: %s", p.line)
	}

	arg1 := tokens[1]
	if !segs.contains(arg1) {
		return command{}, fmt.Errorf("unknown segment: %s", arg1)
	}

	// parse the third token as an integer
	a := tokens[2]
	i, err := strconv.Atoi(a)
	if i < 0 || err != nil {
		return command{}, fmt.Errorf("not a positive integer: %s", a)
	}

	return command{typ: typ, arg1: arg1, arg2: uint(i)}, nil
}

// dispatchCommand dispatches CommandType from cmd.
// If cmd is not a valid command string, it returns `unknown`.
func (*Parser) dispatchCommand(cmd string) CommandType {
	switch cmd {
	case "add", "sub", "neg", "eq", "gt", "lt", "and", "or", "not":
		return Arithmetic
	case "push":
		return Push
	case "pop":
		return Pop
	case "label":
		return Label
	case "goto":
		return Goto
	case "if-goto":
		return If
	case "function":
		return Function
	case "call":
		return Call
	case "return":
		return Return
	default:
		return unknown
	}
}

// segments is a collection of segments.
// In order to check an existence of a segment by O(1) segments is a map.
type segments map[string]struct{}

// dummy is a dummy empty struct for segments.
var dummy = struct{}{}

// segs is a collection of all the segments on VM.
var segs = segments{
	"constant": dummy,
	"local":    dummy,
	"argument": dummy,
	"this":     dummy,
	"that":     dummy,
	"temp":     dummy,
	"pointer":  dummy,
	"static":   dummy,
}

// contains reports whether text is contained in s.
func (s segments) contains(text string) bool {
	_, exists := segs[text]
	return exists
}

// CommandType returns a type of a current VM command. In all arithmetic commands
// it returns Arithmetic.
func (p *Parser) CommandType() CommandType {
	return p.cmd.typ
}

// Arg1 returns the first argument in a current command. If a type of the current command is
// Arithmetic, it returns the command itself. This method should NOT be called if CommandType()
// returns Return.
func (p *Parser) Arg1() string {
	return p.cmd.arg1
}

// Arg2 returns the second argument in a current command. This method should be called
// only if CommandType() returns Push, Pop, Function or Call.
func (p *Parser) Arg2() uint {
	return p.cmd.arg2
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"
	"fmt"
	"koding/db/mongodb/modelhelper"
	"socialapi/config"
	"socialapi/models"
	"socialapi/rest"
	"socialapi/workers/common/mux"
	"socialapi/workers/common/tests"
	"socialapi/workers/presence"
	"testing"

	"github.com/koding/runner"
	. "github.com/smartystreets/goconvey/convey"
	"gopkg.in/mgo.v2/bson"
)

func TestPing(t *testing.T) {
	Convey("Given testing user & group", t, func() {
		withTestServer(t, func(endpoint string) {
			withStubData(endpoint, func(username, groupName, sessionID string) {
				Convey("We should be able to send the ping request to", func() {
					externalURL := endpoint + EndpointPresencePing
					privateURL := endpoint + EndpointPresencePingPrivate

					acc := &models.Account{}
					err := acc.ByNick(username)
					tests.ResultedWithNoErrorCheck(acc, err)

					Convey("external endpoint", func() {
						_, err := rest.DoRequestWithAuth("GET", externalURL, nil, sessionID)
						So(err, ShouldBeNil)

						pp := &presence.PrivatePing{
							GroupName: groupName,
							Username:  username,
						}
						req, err := json.Marshal(pp)
						tests.ResultedWithNoErrorCheck(req, err)
						Convey("internal endpoint without auth", func() {
							_, err := rest.DoRequestWithAuth("POST", privateURL, req, "")
							So(err, ShouldBeNil)
						})
					})
				})
			})
		})
	})
}

//TODO(cihangir): below shamelessly copied from payment api tests with small
//modifications, unify them.

func withStubData(endpoint string, f func(username string, groupName string, sessionID string)) {
	acc, _, groupName := models.CreateRandomGroupDataWithChecks()

	group, err := modelhelper.GetGroup(groupName)
	tests.ResultedWithNoErrorCheck(group, err)

	err = modelhelper.MakeAdmin(bson.ObjectIdHex(acc.OldId), group.Id)
	So(err, ShouldBeNil)

	ses, err := modelhelper.FetchOrCreateSession(acc.Nick, groupName)
	tests.ResultedWithNoErrorCheck(ses, err)

	f(acc.Nick, groupName, ses.ClientId)
}

func withTestServer(t *testing.T, f func(url string)) {
	const workerName = "pingtest"

	r := runner.New(workerName)
	if err := r.Init(); err != nil {
		t.Fatal(err)
	}

	c := config.MustRead(r.Conf.Path)
	// init mongo connection
	modelhelper.Initialize(c.Mongo)
	defer modelhelper.Close()

	port := tests.GetFreePort()
	mc := mux.NewConfig(workerName, "localhost", port)
	mc.Debug = r.Conf.Debug
	m := mux.New(mc, r.Log, r.Metrics)

	AddHandlers(m)

	m.Listen()

	go r.Listen()

	f(fmt.Sprintf("http://localhost:%s", port))

	if err := r.Close(); err != nil {
		t.Fatalf("server close errored: %s", err.Error())
	}

	// shutdown server
	m.Close()
}
<commit_msg>socialapi: add test for client<commit_after>package api

import (
	"encoding/json"
	"fmt"
	"koding/db/mongodb/modelhelper"
	"socialapi/config"
	"socialapi/models"
	"socialapi/rest"
	"socialapi/workers/common/mux"
	"socialapi/workers/common/tests"
	"socialapi/workers/presence"
	"socialapi/workers/presence/client"
	"testing"

	"github.com/koding/runner"
	. "github.com/smartystreets/goconvey/convey"
	"gopkg.in/mgo.v2/bson"
)

func TestPing(t *testing.T) {
	Convey("Given testing user & group", t, func() {
		withTestServer(t, func(endpoint string) {
			withStubData(endpoint, func(username, groupName, sessionID string) {
				Convey("We should be able to send the ping request to", func() {
					externalURL := endpoint + EndpointPresencePing
					privateURL := endpoint + EndpointPresencePingPrivate

					acc := &models.Account{}
					err := acc.ByNick(username)
					tests.ResultedWithNoErrorCheck(acc, err)

					Convey("external endpoint", func() {
						_, err := rest.DoRequestWithAuth("GET", externalURL, nil, sessionID)
						So(err, ShouldBeNil)

						pp := &presence.PrivatePing{
							GroupName: groupName,
							Username:  username,
						}
						req, err := json.Marshal(pp)
						tests.ResultedWithNoErrorCheck(req, err)
						Convey("internal endpoint without auth", func() {
							_, err := rest.DoRequestWithAuth("POST", privateURL, req, "")
							So(err, ShouldBeNil)
						})
					})
				})
			})
		})
	})
}

func TestPingWithClient(t *testing.T) {
	Convey("Given testing user & group", t, func() {
		withTestServer(t, func(endpoint string) {
			withStubData(endpoint, func(username, groupName, sessionID string) {
				Convey("We should be able to send the ping request", func() {
					Convey("with public client", func() {
						c := client.NewPublic(endpoint)
						err := c.Ping(sessionID, "groupName")
						So(err, ShouldBeNil)

						Convey("with internal client", func() {
							c := client.NewInternal(endpoint)
							err := c.Ping(username, groupName)
							So(err, ShouldBeNil)
						})
					})
				})
			})
		})
	})
}

//TODO(cihangir): below shamelessly copied from payment api tests with small
//modifications, unify them.

func withStubData(endpoint string, f func(username string, groupName string, sessionID string)) {
	acc, _, groupName := models.CreateRandomGroupDataWithChecks()

	group, err := modelhelper.GetGroup(groupName)
	tests.ResultedWithNoErrorCheck(group, err)

	err = modelhelper.MakeAdmin(bson.ObjectIdHex(acc.OldId), group.Id)
	So(err, ShouldBeNil)

	ses, err := modelhelper.FetchOrCreateSession(acc.Nick, groupName)
	tests.ResultedWithNoErrorCheck(ses, err)

	f(acc.Nick, groupName, ses.ClientId)
}

func withTestServer(t *testing.T, f func(url string)) {
	const workerName = "pingtest"

	r := runner.New(workerName)
	if err := r.Init(); err != nil {
		t.Fatal(err)
	}

	c := config.MustRead(r.Conf.Path)
	// init mongo connection
	modelhelper.Initialize(c.Mongo)
	defer modelhelper.Close()

	port := tests.GetFreePort()
	mc := mux.NewConfig(workerName, "localhost", port)
	mc.Debug = r.Conf.Debug
	m := mux.New(mc, r.Log, r.Metrics)

	AddHandlers(m)

	m.Listen()

	go r.Listen()

	f(fmt.Sprintf("http://localhost:%s", port))

	if err := r.Close(); err != nil {
		t.Fatalf("server close errored: %s", err.Error())
	}

	// shutdown server
	m.Close()
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The Vitess Authors.

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

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

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

import (
	"flag"
	"os"
	"testing"
	"time"

	"vitess.io/vitess/go/vt/schema"
	"vitess.io/vitess/go/vt/sqlparser"

	"vitess.io/vitess/go/test/endtoend/cluster"

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

var (
	clusterInstance *cluster.LocalProcessCluster
	masterTablet    cluster.Vttablet
	replicaTablet   cluster.Vttablet
	hostname        = "localhost"
	keyspaceName    = "ks"
	cell            = "zone1"
	sqlSchema       = `
	create table if not exists t1(
		id bigint not null auto_increment,
		value varchar(32),
		primary key(id)
	) Engine=InnoDB;
`

	vSchema = `
	{
    "sharded": true,
    "vindexes": {
      "hash": {
        "type": "hash"
      }
    },
    "tables": {
      "t1": {
        "column_vindexes": [
          {
            "column": "id",
            "name": "hash"
          }
        ]
      }
    }
	}`
)

func TestMain(m *testing.M) {
	defer cluster.PanicHandler(nil)
	flag.Parse()

	exitCode := func() int {
		clusterInstance = cluster.NewCluster(cell, hostname)
		defer clusterInstance.Teardown()

		// Start topo server
		err := clusterInstance.StartTopo()
		if err != nil {
			return 1
		}

		// Set extra tablet args for lock timeout
		clusterInstance.VtTabletExtraArgs = []string{
			"-lock_tables_timeout", "5s",
			"-watch_replication_stream",
			"-enable_replication_reporter",
			"-heartbeat_enable",
			"-heartbeat_interval", "250ms",
			"-gc_check_interval", "5s",
			"-table_gc_lifecycle", "hold,purge,evac,drop",
		}
		// We do not need semiSync for this test case.
		clusterInstance.EnableSemiSync = false

		// Start keyspace
		keyspace := &cluster.Keyspace{
			Name:      keyspaceName,
			SchemaSQL: sqlSchema,
			VSchema:   vSchema,
		}

		if err = clusterInstance.StartUnshardedKeyspace(*keyspace, 1, false); err != nil {
			return 1
		}

		// Collect table paths and ports
		tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets
		for _, tablet := range tablets {
			if tablet.Type == "master" {
				masterTablet = *tablet
			} else if tablet.Type != "rdonly" {
				replicaTablet = *tablet
			}
		}

		return m.Run()
	}()
	os.Exit(exitCode)
}

func checkTableRows(t *testing.T, tableName string, expect int64) {
	require.NotEmpty(t, tableName)
	query := `select count(*) as c from %a`
	parsed := sqlparser.BuildParsedQuery(query, tableName)
	rs, err := masterTablet.VttabletProcess.QueryTablet(parsed.Query, keyspaceName, true)
	require.NoError(t, err)
	count := rs.Named().Row().AsInt64("c", 0)
	assert.Equal(t, expect, count)
}

func populateTable(t *testing.T) {
	_, err := masterTablet.VttabletProcess.QueryTablet(sqlSchema, keyspaceName, true)
	require.NoError(t, err)
	_, err = masterTablet.VttabletProcess.QueryTablet("delete from t1", keyspaceName, true)
	require.NoError(t, err)
	_, err = masterTablet.VttabletProcess.QueryTablet("insert into t1 (id, value) values (null, md5(rand()))", keyspaceName, true)
	require.NoError(t, err)
	for i := 0; i < 10; i++ {
		_, err = masterTablet.VttabletProcess.QueryTablet("insert into t1 (id, value) select null, md5(rand()) from t1", keyspaceName, true)
		require.NoError(t, err)
	}
	checkTableRows(t, "t1", 1024)
}

// tableExists sees that a given table exists in MySQL
func tableExists(tableExpr string) (exists bool, tableName string, err error) {
	query := `show table status like '%a'`
	parsed := sqlparser.BuildParsedQuery(query, tableExpr)
	rs, err := masterTablet.VttabletProcess.QueryTablet(parsed.Query, keyspaceName, true)
	if err != nil {
		return false, "", err
	}
	row := rs.Named().Row()
	if row == nil {
		return false, "", nil
	}
	return true, row.AsString("Name", ""), nil
}

// tableExists sees that a given table exists in MySQL
func dropTable(tableName string) (err error) {
	query := `drop table if exists %a`
	parsed := sqlparser.BuildParsedQuery(query, tableName)
	_, err = masterTablet.VttabletProcess.QueryTablet(parsed.Query, keyspaceName, true)
	return err
}

func TestPopulateTable(t *testing.T) {
	populateTable(t)
	{
		exists, _, err := tableExists("t1")
		assert.NoError(t, err)
		assert.True(t, exists)
	}
	{
		exists, _, err := tableExists("no_such_table")
		assert.NoError(t, err)
		assert.False(t, exists)
	}
}

func TestHold(t *testing.T) {
	populateTable(t)
	query, tableName, err := schema.GenerateRenameStatement("t1", schema.HoldTableGCState, time.Now().UTC().Add(10*time.Second))
	assert.NoError(t, err)

	_, err = masterTablet.VttabletProcess.QueryTablet(query, keyspaceName, true)
	assert.NoError(t, err)

	{
		exists, _, err := tableExists("t1")
		assert.NoError(t, err)
		assert.False(t, exists)
	}
	{
		exists, _, err := tableExists(tableName)
		assert.NoError(t, err)
		assert.True(t, exists)
	}

	time.Sleep(5 * time.Second)
	{
		// Table was created with +10s timestamp, so it should still exist
		exists, _, err := tableExists(tableName)
		assert.NoError(t, err)
		assert.True(t, exists)

		checkTableRows(t, tableName, 1024)
	}

	time.Sleep(10 * time.Second)
	{
		// We're now both beyond table's timestamp as well as a tableGC interval
		exists, _, err := tableExists(tableName)
		assert.NoError(t, err)
		assert.False(t, exists)
	}
	{
		// Table should be renamed as _vt_PURGE_...
		exists, purgeTableName, err := tableExists(`\_vt\_PURGE\_%`)
		assert.NoError(t, err)
		assert.True(t, exists)
		err = dropTable(purgeTableName)
		assert.NoError(t, err)
	}
}

func TestEvac(t *testing.T) {
	populateTable(t)
	query, tableName, err := schema.GenerateRenameStatement("t1", schema.EvacTableGCState, time.Now().UTC().Add(10*time.Second))
	assert.NoError(t, err)

	_, err = masterTablet.VttabletProcess.QueryTablet(query, keyspaceName, true)
	assert.NoError(t, err)

	{
		exists, _, err := tableExists("t1")
		assert.NoError(t, err)
		assert.False(t, exists)
	}
	{
		exists, _, err := tableExists(tableName)
		assert.NoError(t, err)
		assert.True(t, exists)
	}

	time.Sleep(5 * time.Second)
	{
		// Table was created with +10s timestamp, so it should still exist
		exists, _, err := tableExists(tableName)
		assert.NoError(t, err)
		assert.True(t, exists)

		checkTableRows(t, tableName, 1024)
	}

	time.Sleep(10 * time.Second)
	{
		// We're now both beyond table's timestamp as well as a tableGC interval
		exists, _, err := tableExists(tableName)
		assert.NoError(t, err)
		assert.False(t, exists)
	}
	time.Sleep(5 * time.Second)
	{
		// Table should be renamed as _vt_DROP_... and then dropped!
		exists, _, err := tableExists(`\_vt\_DROP\_%`)
		assert.NoError(t, err)
		assert.False(t, exists)
	}
}

func TestDrop(t *testing.T) {
	populateTable(t)
	query, tableName, err := schema.GenerateRenameStatement("t1", schema.DropTableGCState, time.Now().UTC().Add(10*time.Second))
	assert.NoError(t, err)

	_, err = masterTablet.VttabletProcess.QueryTablet(query, keyspaceName, true)
	assert.NoError(t, err)

	{
		exists, _, err := tableExists("t1")
		assert.NoError(t, err)
		assert.False(t, exists)
	}

	time.Sleep(20 * time.Second) // 10s for timestamp to pass, then 10s for checkTables and drop of table
	{
		// We're now both beyond table's timestamp as well as a tableGC interval
		exists, _, err := tableExists(tableName)
		assert.NoError(t, err)
		assert.False(t, exists)
	}
}

func TestPurge(t *testing.T) {
	populateTable(t)
	query, tableName, err := schema.GenerateRenameStatement("t1", schema.PurgeTableGCState, time.Now().UTC().Add(10*time.Second))
	require.NoError(t, err)

	_, err = masterTablet.VttabletProcess.QueryTablet(query, keyspaceName, true)
	require.NoError(t, err)

	{
		exists, _, err := tableExists("t1")
		require.NoError(t, err)
		require.False(t, exists)
	}
	{
		exists, _, err := tableExists(tableName)
		require.NoError(t, err)
		require.True(t, exists)
	}

	time.Sleep(5 * time.Second)
	{
		// Table was created with +10s timestamp, so it should still exist
		exists, _, err := tableExists(tableName)
		require.NoError(t, err)
		require.True(t, exists)

		checkTableRows(t, tableName, 1024)
	}

	time.Sleep(1 * time.Minute) // purgeReentranceInterval
	{
		// We're now both beyond table's timestamp as well as a tableGC interval
		exists, _, err := tableExists(tableName)
		require.NoError(t, err)
		require.False(t, exists)
	}
	{
		// Table should be renamed as _vt_EVAC_...
		exists, evacTableName, err := tableExists(`\_vt\_EVAC\_%`)
		require.NoError(t, err)
		require.True(t, exists)
		checkTableRows(t, evacTableName, 0)
		err = dropTable(evacTableName)
		require.NoError(t, err)
	}
}
<commit_msg>more sleep time<commit_after>/*
Copyright 2020 The Vitess Authors.

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

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

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

import (
	"flag"
	"os"
	"testing"
	"time"

	"vitess.io/vitess/go/vt/schema"
	"vitess.io/vitess/go/vt/sqlparser"

	"vitess.io/vitess/go/test/endtoend/cluster"

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

var (
	clusterInstance *cluster.LocalProcessCluster
	masterTablet    cluster.Vttablet
	replicaTablet   cluster.Vttablet
	hostname        = "localhost"
	keyspaceName    = "ks"
	cell            = "zone1"
	sqlSchema       = `
	create table if not exists t1(
		id bigint not null auto_increment,
		value varchar(32),
		primary key(id)
	) Engine=InnoDB;
`

	vSchema = `
	{
    "sharded": true,
    "vindexes": {
      "hash": {
        "type": "hash"
      }
    },
    "tables": {
      "t1": {
        "column_vindexes": [
          {
            "column": "id",
            "name": "hash"
          }
        ]
      }
    }
	}`
)

func TestMain(m *testing.M) {
	defer cluster.PanicHandler(nil)
	flag.Parse()

	exitCode := func() int {
		clusterInstance = cluster.NewCluster(cell, hostname)
		defer clusterInstance.Teardown()

		// Start topo server
		err := clusterInstance.StartTopo()
		if err != nil {
			return 1
		}

		// Set extra tablet args for lock timeout
		clusterInstance.VtTabletExtraArgs = []string{
			"-lock_tables_timeout", "5s",
			"-watch_replication_stream",
			"-enable_replication_reporter",
			"-heartbeat_enable",
			"-heartbeat_interval", "250ms",
			"-gc_check_interval", "5s",
			"-table_gc_lifecycle", "hold,purge,evac,drop",
		}
		// We do not need semiSync for this test case.
		clusterInstance.EnableSemiSync = false

		// Start keyspace
		keyspace := &cluster.Keyspace{
			Name:      keyspaceName,
			SchemaSQL: sqlSchema,
			VSchema:   vSchema,
		}

		if err = clusterInstance.StartUnshardedKeyspace(*keyspace, 1, false); err != nil {
			return 1
		}

		// Collect table paths and ports
		tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets
		for _, tablet := range tablets {
			if tablet.Type == "master" {
				masterTablet = *tablet
			} else if tablet.Type != "rdonly" {
				replicaTablet = *tablet
			}
		}

		return m.Run()
	}()
	os.Exit(exitCode)
}

func checkTableRows(t *testing.T, tableName string, expect int64) {
	require.NotEmpty(t, tableName)
	query := `select count(*) as c from %a`
	parsed := sqlparser.BuildParsedQuery(query, tableName)
	rs, err := masterTablet.VttabletProcess.QueryTablet(parsed.Query, keyspaceName, true)
	require.NoError(t, err)
	count := rs.Named().Row().AsInt64("c", 0)
	assert.Equal(t, expect, count)
}

func populateTable(t *testing.T) {
	_, err := masterTablet.VttabletProcess.QueryTablet(sqlSchema, keyspaceName, true)
	require.NoError(t, err)
	_, err = masterTablet.VttabletProcess.QueryTablet("delete from t1", keyspaceName, true)
	require.NoError(t, err)
	_, err = masterTablet.VttabletProcess.QueryTablet("insert into t1 (id, value) values (null, md5(rand()))", keyspaceName, true)
	require.NoError(t, err)
	for i := 0; i < 10; i++ {
		_, err = masterTablet.VttabletProcess.QueryTablet("insert into t1 (id, value) select null, md5(rand()) from t1", keyspaceName, true)
		require.NoError(t, err)
	}
	checkTableRows(t, "t1", 1024)
}

// tableExists sees that a given table exists in MySQL
func tableExists(tableExpr string) (exists bool, tableName string, err error) {
	query := `show table status like '%a'`
	parsed := sqlparser.BuildParsedQuery(query, tableExpr)
	rs, err := masterTablet.VttabletProcess.QueryTablet(parsed.Query, keyspaceName, true)
	if err != nil {
		return false, "", err
	}
	row := rs.Named().Row()
	if row == nil {
		return false, "", nil
	}
	return true, row.AsString("Name", ""), nil
}

// tableExists sees that a given table exists in MySQL
func dropTable(tableName string) (err error) {
	query := `drop table if exists %a`
	parsed := sqlparser.BuildParsedQuery(query, tableName)
	_, err = masterTablet.VttabletProcess.QueryTablet(parsed.Query, keyspaceName, true)
	return err
}

func TestPopulateTable(t *testing.T) {
	populateTable(t)
	{
		exists, _, err := tableExists("t1")
		assert.NoError(t, err)
		assert.True(t, exists)
	}
	{
		exists, _, err := tableExists("no_such_table")
		assert.NoError(t, err)
		assert.False(t, exists)
	}
}

func TestHold(t *testing.T) {
	populateTable(t)
	query, tableName, err := schema.GenerateRenameStatement("t1", schema.HoldTableGCState, time.Now().UTC().Add(10*time.Second))
	assert.NoError(t, err)

	_, err = masterTablet.VttabletProcess.QueryTablet(query, keyspaceName, true)
	assert.NoError(t, err)

	{
		exists, _, err := tableExists("t1")
		assert.NoError(t, err)
		assert.False(t, exists)
	}
	{
		exists, _, err := tableExists(tableName)
		assert.NoError(t, err)
		assert.True(t, exists)
	}

	time.Sleep(5 * time.Second)
	{
		// Table was created with +10s timestamp, so it should still exist
		exists, _, err := tableExists(tableName)
		assert.NoError(t, err)
		assert.True(t, exists)

		checkTableRows(t, tableName, 1024)
	}

	time.Sleep(10 * time.Second)
	{
		// We're now both beyond table's timestamp as well as a tableGC interval
		exists, _, err := tableExists(tableName)
		assert.NoError(t, err)
		assert.False(t, exists)
	}
	{
		// Table should be renamed as _vt_PURGE_...
		exists, purgeTableName, err := tableExists(`\_vt\_PURGE\_%`)
		assert.NoError(t, err)
		assert.True(t, exists)
		err = dropTable(purgeTableName)
		assert.NoError(t, err)
	}
}

func TestEvac(t *testing.T) {
	populateTable(t)
	query, tableName, err := schema.GenerateRenameStatement("t1", schema.EvacTableGCState, time.Now().UTC().Add(10*time.Second))
	assert.NoError(t, err)

	_, err = masterTablet.VttabletProcess.QueryTablet(query, keyspaceName, true)
	assert.NoError(t, err)

	{
		exists, _, err := tableExists("t1")
		assert.NoError(t, err)
		assert.False(t, exists)
	}
	{
		exists, _, err := tableExists(tableName)
		assert.NoError(t, err)
		assert.True(t, exists)
	}

	time.Sleep(5 * time.Second)
	{
		// Table was created with +10s timestamp, so it should still exist
		exists, _, err := tableExists(tableName)
		assert.NoError(t, err)
		assert.True(t, exists)

		checkTableRows(t, tableName, 1024)
	}

	time.Sleep(10 * time.Second)
	{
		// We're now both beyond table's timestamp as well as a tableGC interval
		exists, _, err := tableExists(tableName)
		assert.NoError(t, err)
		assert.False(t, exists)
	}
	time.Sleep(5 * time.Second)
	{
		// Table should be renamed as _vt_DROP_... and then dropped!
		exists, _, err := tableExists(`\_vt\_DROP\_%`)
		assert.NoError(t, err)
		assert.False(t, exists)
	}
}

func TestDrop(t *testing.T) {
	populateTable(t)
	query, tableName, err := schema.GenerateRenameStatement("t1", schema.DropTableGCState, time.Now().UTC().Add(10*time.Second))
	assert.NoError(t, err)

	_, err = masterTablet.VttabletProcess.QueryTablet(query, keyspaceName, true)
	assert.NoError(t, err)

	{
		exists, _, err := tableExists("t1")
		assert.NoError(t, err)
		assert.False(t, exists)
	}

	time.Sleep(20 * time.Second) // 10s for timestamp to pass, then 10s for checkTables and drop of table
	{
		// We're now both beyond table's timestamp as well as a tableGC interval
		exists, _, err := tableExists(tableName)
		assert.NoError(t, err)
		assert.False(t, exists)
	}
}

func TestPurge(t *testing.T) {
	populateTable(t)
	query, tableName, err := schema.GenerateRenameStatement("t1", schema.PurgeTableGCState, time.Now().UTC().Add(10*time.Second))
	require.NoError(t, err)

	_, err = masterTablet.VttabletProcess.QueryTablet(query, keyspaceName, true)
	require.NoError(t, err)

	{
		exists, _, err := tableExists("t1")
		require.NoError(t, err)
		require.False(t, exists)
	}
	{
		exists, _, err := tableExists(tableName)
		require.NoError(t, err)
		require.True(t, exists)
	}

	time.Sleep(5 * time.Second)
	{
		// Table was created with +10s timestamp, so it should still exist
		exists, _, err := tableExists(tableName)
		require.NoError(t, err)
		require.True(t, exists)

		checkTableRows(t, tableName, 1024)
	}

	time.Sleep(2 * time.Minute) // purgeReentranceInterval
	{
		// We're now both beyond table's timestamp as well as a tableGC interval
		exists, _, err := tableExists(tableName)
		require.NoError(t, err)
		require.False(t, exists)
	}
	{
		// Table should be renamed as _vt_EVAC_...
		exists, evacTableName, err := tableExists(`\_vt\_EVAC\_%`)
		require.NoError(t, err)
		require.True(t, exists)
		checkTableRows(t, evacTableName, 0)
		err = dropTable(evacTableName)
		require.NoError(t, err)
	}
}
<|endoftext|>


<commit_before>package cmd

import (
	"github.com/markbates/pop/soda/cmd/generate"
	"github.com/spf13/cobra"
)

var dialect string

var initCmd = &cobra.Command{
	Use:   "init",
	Short: "Sets up a new Pop/Soda Project",
	RunE: func(cmd *cobra.Command, args []string) error {
		err := generate.ConfigCmd.RunE(cmd, args)
		return err
	},
}

func init() {
	RootCmd.AddCommand(initCmd)
	initCmd.Flags().StringVarP(&dialect, "type", "t", "postgres", "What type of database do you want to use? (postgres, mysql, sqlite3)")
}
<commit_msg>got rid of the init command<commit_after><|endoftext|>


<commit_before>package sortedmap

import (
	"fmt"
	"time"
	mrand "math/rand"
)

func randStr(n int) string {
    mrand.Seed(time.Now().UTC().UnixNano())

	const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_+=~[]{}|:;<>,./?"
	result := make([]byte, n)

	for i := 0; i < n; i++ {
		result[i] = chars[mrand.Intn(len(chars))]
	}
    return string(result)
}

func randRecord() Record {
	year := mrand.Intn(2129)
	if year < 1 {
		year++
	}
	mth := time.Month(mrand.Intn(12))
	if mth < 1 {
		mth++
	}
	day := mrand.Intn(28)
	if day < 1 {
		day++
	}
	return Record{
		Key: randStr(42),
		Val: time.Date(year, mth, day, 0, 0, 0, 0, time.UTC),
	}
}

func randRecords(n int) []*Record {
	records := make([]*Record, n)
	for i := range records {
		rec := randRecord()
		records[i] = &rec
	}
	return records
}

func verifyRecords(ch <-chan Record) error {
	previousRec := Record{}

	for rec := range ch {
		if previousRec.Key != "" {
			if previousRec.Val.(time.Time).After(rec.Val.(time.Time)) {
				return fmt.Errorf("%v %v",
					unsortedErr,
					fmt.Sprintf("prev: %+v, current: %+v.", previousRec, rec),
				)
			}
		}
		previousRec = rec
	}

	return nil
}

func newSortedMapFromRandRecords(n int) *SortedMap {
	records := randRecords(n)
	sm := New(nil)
	sm.BatchReplace(records...)

	return sm
}<commit_msg>fixed spacing<commit_after>package sortedmap

import (
	"fmt"
	"time"
	mrand "math/rand"
)

func randStr(n int) string {
	mrand.Seed(time.Now().UTC().UnixNano())

	const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_+=~[]{}|:;<>,./?"
	result := make([]byte, n)

	for i := 0; i < n; i++ {
		result[i] = chars[mrand.Intn(len(chars))]
	}
    return string(result)
}

func randRecord() Record {
	year := mrand.Intn(2129)
	if year < 1 {
		year++
	}
	mth := time.Month(mrand.Intn(12))
	if mth < 1 {
		mth++
	}
	day := mrand.Intn(28)
	if day < 1 {
		day++
	}
	return Record{
		Key: randStr(42),
		Val: time.Date(year, mth, day, 0, 0, 0, 0, time.UTC),
	}
}

func randRecords(n int) []*Record {
	records := make([]*Record, n)
	for i := range records {
		rec := randRecord()
		records[i] = &rec
	}
	return records
}

func verifyRecords(ch <-chan Record) error {
	previousRec := Record{}

	for rec := range ch {
		if previousRec.Key != "" {
			if previousRec.Val.(time.Time).After(rec.Val.(time.Time)) {
				return fmt.Errorf("%v %v",
					unsortedErr,
					fmt.Sprintf("prev: %+v, current: %+v.", previousRec, rec),
				)
			}
		}
		previousRec = rec
	}

	return nil
}

func newSortedMapFromRandRecords(n int) *SortedMap {
	records := randRecords(n)
	sm := New(nil)
	sm.BatchReplace(records...)

	return sm
}<|endoftext|>


<commit_before>package cassandra

import (
	"fmt"
	bulkDataGenIot "github.com/influxdata/influxdb-comparisons/bulk_data_gen/iot"
	bulkQuerygen "github.com/influxdata/influxdb-comparisons/bulk_query_gen"
	"math/rand"
	"strings"
	"time"
)

// CassandraIot produces Cassandra-specific queries for all the devops query types.
type CassandraIot struct {
	bulkQuerygen.CommonParams
	KeyspaceName string
}

// NewCassandraIot makes an CassandraIot object ready to generate Queries.
func newCassandraIotCommon(dbConfig bulkQuerygen.DatabaseConfig, queriesFullRange bulkQuerygen.TimeInterval, queryInterval time.Duration, scaleVar int) bulkQuerygen.QueryGenerator {

	return &CassandraIot{
		CommonParams: *bulkQuerygen.NewCommonParams(queriesFullRange, scaleVar),
		KeyspaceName: dbConfig[bulkQuerygen.DatabaseName],
	}
}

// Dispatch fulfills the QueryGenerator interface.
func (d *CassandraIot) Dispatch(i int) bulkQuerygen.Query {
	q := NewCassandraQuery() // from pool
	bulkQuerygen.IotDispatchAll(d, i, q, d.ScaleVar)
	return q
}

func (d *CassandraIot) AverageTemperatureDayByHourOneHome(q bulkQuerygen.Query) {
	d.averageTemperatureDayByHourNHomes(q.(*CassandraQuery), 1, time.Hour)
}

// averageTemperatureHourByMinuteNHomes populates a Query with a query that looks like:
// SELECT avg(temperature) from air_condition_room where (home_id = '$HHOME_ID_1' or ... or hostname = '$HOSTNAME_N') and time >= '$HOUR_START' and time < '$HOUR_END' group by time(1h)
func (d *CassandraIot) averageTemperatureDayByHourNHomes(qi bulkQuerygen.Query, nHomes int, timeRange time.Duration) {
	interval := d.AllInterval.RandWindow(timeRange)
	nn := rand.Perm(d.ScaleVar)[:nHomes]

	homes := []string{}
	for _, n := range nn {
		homes = append(homes, fmt.Sprintf(bulkDataGenIot.SmartHomeIdFormat, n))
	}

	homeClauses := []string{}
	for _, s := range homes {
		homeClauses = append(homeClauses, fmt.Sprintf("home_id = '%s'", s))
	}

	combinedHomesClause := strings.Join(homeClauses, " or ")

	humanLabel := fmt.Sprintf("Cassandra average temperature, rand %4d homes, rand %s by 1h", nHomes, timeRange)
	q := qi.(*CassandraQuery)
	q.HumanLabel = []byte(humanLabel)
	q.HumanDescription = []byte(fmt.Sprintf("%s: %s", humanLabel, interval.StartString()))

	q.AggregationType = []byte("avg")
	q.MeasurementName = []byte("air_condition_room")
	q.FieldName = []byte("temperature")

	q.TimeStart = interval.Start
	q.TimeEnd = interval.End
	q.GroupByDuration = time.Hour

	q.TagsCondition = []byte(combinedHomesClause)
}
<commit_msg>Increasing query time range for iot use-case<commit_after>package cassandra

import (
	"fmt"
	bulkDataGenIot "github.com/influxdata/influxdb-comparisons/bulk_data_gen/iot"
	bulkQuerygen "github.com/influxdata/influxdb-comparisons/bulk_query_gen"
	"math/rand"
	"strings"
	"time"
)

// CassandraIot produces Cassandra-specific queries for all the devops query types.
type CassandraIot struct {
	bulkQuerygen.CommonParams
	KeyspaceName string
}

// NewCassandraIot makes an CassandraIot object ready to generate Queries.
func newCassandraIotCommon(dbConfig bulkQuerygen.DatabaseConfig, queriesFullRange bulkQuerygen.TimeInterval, queryInterval time.Duration, scaleVar int) bulkQuerygen.QueryGenerator {

	return &CassandraIot{
		CommonParams: *bulkQuerygen.NewCommonParams(queriesFullRange, scaleVar),
		KeyspaceName: dbConfig[bulkQuerygen.DatabaseName],
	}
}

// Dispatch fulfills the QueryGenerator interface.
func (d *CassandraIot) Dispatch(i int) bulkQuerygen.Query {
	q := NewCassandraQuery() // from pool
	bulkQuerygen.IotDispatchAll(d, i, q, d.ScaleVar)
	return q
}

func (d *CassandraIot) AverageTemperatureDayByHourOneHome(q bulkQuerygen.Query) {
	d.averageTemperatureDayByHourNHomes(q.(*CassandraQuery), 1, time.Hour*6)
}

// averageTemperatureHourByMinuteNHomes populates a Query with a query that looks like:
// SELECT avg(temperature) from air_condition_room where (home_id = '$HHOME_ID_1' or ... or hostname = '$HOSTNAME_N') and time >= '$HOUR_START' and time < '$HOUR_END' group by time(1h)
func (d *CassandraIot) averageTemperatureDayByHourNHomes(qi bulkQuerygen.Query, nHomes int, timeRange time.Duration) {
	interval := d.AllInterval.RandWindow(timeRange)
	nn := rand.Perm(d.ScaleVar)[:nHomes]

	homes := []string{}
	for _, n := range nn {
		homes = append(homes, fmt.Sprintf(bulkDataGenIot.SmartHomeIdFormat, n))
	}

	homeClauses := []string{}
	for _, s := range homes {
		homeClauses = append(homeClauses, fmt.Sprintf("home_id = '%s'", s))
	}

	combinedHomesClause := strings.Join(homeClauses, " or ")

	humanLabel := fmt.Sprintf("Cassandra average temperature, rand %4d homes, rand %s by 1h", nHomes, timeRange)
	q := qi.(*CassandraQuery)
	q.HumanLabel = []byte(humanLabel)
	q.HumanDescription = []byte(fmt.Sprintf("%s: %s", humanLabel, interval.StartString()))

	q.AggregationType = []byte("avg")
	q.MeasurementName = []byte("air_condition_room")
	q.FieldName = []byte("temperature")

	q.TimeStart = interval.Start
	q.TimeEnd = interval.End
	q.GroupByDuration = time.Hour

	q.TagsCondition = []byte(combinedHomesClause)
}
<|endoftext|>


<commit_before>package email

import (
	"bytes"
	"crypto/tls"
	"fmt"
	"html/template"
	"net/mail"
	"net/smtp"

	"net"

	"time"

	"github.com/labstack/gommon/random"
)

type (
	Email struct {
		Auth        smtp.Auth
		Header      map[string]string
		Template    *template.Template
		smtpAddress string
	}

	Message struct {
		ID          string
		From        string
		To          string
		CC          string
		Subject     string
		Text        string
		HTML        string
		Inlines     []*File
		Attachments []*File
		buffer      *bytes.Buffer
		boundary    string
	}

	File struct {
		Name    string
		Type    string
		Content string
	}
)

func New(smtpAddress string) *Email {
	return &Email{
		smtpAddress: smtpAddress,
		Header:      map[string]string{},
	}
}

func (m *Message) writeText(content string, contentType string) {
	m.buffer.WriteString(fmt.Sprintf("--%s\n", m.boundary))
	m.buffer.WriteString(fmt.Sprintf("Content-Type: %s; charset=UTF-8\n", contentType))
	m.buffer.WriteString("Content-Transfer-Encoding: quoted-printable\n")
	m.buffer.WriteString("\n")
	m.buffer.WriteString(content + "\n")
}

func (m *Message) writeFile(f *File, disposition string) {
	m.buffer.WriteString(fmt.Sprintf("--%s\n", m.boundary))
	m.buffer.WriteString(fmt.Sprintf("Content-Type: %s; name=%s\n", f.Type, f.Name))
	m.buffer.WriteString(fmt.Sprintf("Content-Disposition: %s; filename=%s\n", disposition, f.Name))
	m.buffer.WriteString("Content-Transfer-Encoding: base64\n")
	m.buffer.WriteString("\n")
	m.buffer.WriteString(f.Content + "\n")
}

func (e *Email) Send(m *Message) (err error) {
	// Message header
	m.buffer = new(bytes.Buffer)
	m.boundary = random.String(16)
	m.buffer.WriteString("MIME-Version: 1.0\n")
	m.buffer.WriteString(fmt.Sprintf("Message-ID: %s\n", m.ID))
	m.buffer.WriteString(fmt.Sprintf("Date: %s\n", time.Now().Format(time.RFC1123Z)))
	m.buffer.WriteString(fmt.Sprintf("From: %s\n", m.From))
	m.buffer.WriteString(fmt.Sprintf("To: %s\n", m.To))
	if m.CC != "" {
		m.buffer.WriteString(fmt.Sprintf("CC: %s\n", m.CC))
	}
	if m.Subject != "" {
		m.buffer.WriteString(fmt.Sprintf("Subject: %s\n", m.Subject))
	}
	// Extra
	for k, v := range e.Header {
		m.buffer.WriteString(fmt.Sprintf("%s: %s\n", k, v))
	}
	m.buffer.WriteString(fmt.Sprintf("Content-Type: multipart/mixed; boundary=%s\n", m.boundary))
	m.buffer.WriteString("\n")

	// Message body
	if m.Text != "" {
		m.writeText(m.Text, "text/plain")
	} else if m.HTML != "" {
		m.writeText(m.HTML, "text/html")
	} else {
		// TODO:
	}

	// Attachments / inlines
	for _, f := range m.Inlines {
		m.writeFile(f, "inline")
	}
	for _, f := range m.Attachments {
		m.writeFile(f, "disposition")
	}
	m.buffer.WriteString("\n")
	m.buffer.WriteString("--" + m.boundary + "--")

	// Dial
	c, err := smtp.Dial(e.smtpAddress)
	if err != nil {
		return
	}
	defer c.Close()

	// Check if TLS is required
	if ok, _ := c.Extension("STARTTLS"); ok {
		host, _, _ := net.SplitHostPort(e.smtpAddress)
		config := &tls.Config{ServerName: host}
		if err = c.StartTLS(config); err != nil {
			return err
		}
	}

	// Authenticate
	if e.Auth != nil {
		if err = c.Auth(e.Auth); err != nil {
			return
		}
	}

	// Send message
	from, err := mail.ParseAddress(m.From)
	if err != nil {
		return
	}
	if err = c.Mail(from.Address); err != nil {
		return
	}
	to, err := mail.ParseAddressList(m.To)
	if err != nil {
		return
	}
	for _, a := range to {
		if err = c.Rcpt(a.Address); err != nil {
			return
		}
	}
	wc, err := c.Data()
	if err != nil {
		return
	}
	defer wc.Close()
	_, err = m.buffer.WriteTo(wc)
	return
}
<commit_msg>Revert "Email: using only newline"<commit_after>package email

import (
	"bytes"
	"crypto/tls"
	"fmt"
	"html/template"
	"net/mail"
	"net/smtp"

	"net"

	"time"

	"github.com/labstack/gommon/random"
)

type (
	Email struct {
		Auth        smtp.Auth
		Header      map[string]string
		Template    *template.Template
		smtpAddress string
	}

	Message struct {
		ID          string
		From        string
		To          string
		CC          string
		Subject     string
		Text        string
		HTML        string
		Inlines     []*File
		Attachments []*File
		buffer      *bytes.Buffer
		boundary    string
	}

	File struct {
		Name    string
		Type    string
		Content string
	}
)

func New(smtpAddress string) *Email {
	return &Email{
		smtpAddress: smtpAddress,
		Header:      map[string]string{},
	}
}

func (m *Message) writeText(content string, contentType string) {
	m.buffer.WriteString(fmt.Sprintf("--%s\r\n", m.boundary))
	m.buffer.WriteString(fmt.Sprintf("Content-Type: %s; charset=UTF-8\r\n", contentType))
	m.buffer.WriteString("Content-Transfer-Encoding: quoted-printable\r\n")
	m.buffer.WriteString("\r\n")
	m.buffer.WriteString(content + "\r\n")
}

func (m *Message) writeFile(f *File, disposition string) {
	m.buffer.WriteString(fmt.Sprintf("--%s\r\n", m.boundary))
	m.buffer.WriteString(fmt.Sprintf("Content-Type: %s; name=%s\r\n", f.Type, f.Name))
	m.buffer.WriteString(fmt.Sprintf("Content-Disposition: %s; filename=%s\r\n", disposition, f.Name))
	m.buffer.WriteString("Content-Transfer-Encoding: base64\r\n")
	m.buffer.WriteString("\r\n")
	m.buffer.WriteString(f.Content + "\r\n")
}

func (e *Email) Send(m *Message) (err error) {
	// Message header
	m.buffer = new(bytes.Buffer)
	m.boundary = random.String(16)
	m.buffer.WriteString("MIME-Version: 1.0\r\n")
	m.buffer.WriteString(fmt.Sprintf("Message-ID: %s\r\n", m.ID))
	m.buffer.WriteString(fmt.Sprintf("Date: %s\r\n", time.Now().Format(time.RFC1123Z)))
	m.buffer.WriteString(fmt.Sprintf("From: %s\r\n", m.From))
	m.buffer.WriteString(fmt.Sprintf("To: %s\r\n", m.To))
	if m.CC != "" {
		m.buffer.WriteString(fmt.Sprintf("CC: %s\r\n", m.CC))
	}
	if m.Subject != "" {
		m.buffer.WriteString(fmt.Sprintf("Subject: %s\r\n", m.Subject))
	}
	// Extra
	for k, v := range e.Header {
		m.buffer.WriteString(fmt.Sprintf("%s: %s\r\n", k, v))
	}
	m.buffer.WriteString(fmt.Sprintf("Content-Type: multipart/mixed; boundary=%s\r\n", m.boundary))
	m.buffer.WriteString("\r\n")

	// Message body
	if m.Text != "" {
		m.writeText(m.Text, "text/plain")
	} else if m.HTML != "" {
		m.writeText(m.HTML, "text/html")
	} else {
		// TODO:
	}

	// Attachments / inlines
	for _, f := range m.Inlines {
		m.writeFile(f, "inline")
	}
	for _, f := range m.Attachments {
		m.writeFile(f, "disposition")
	}
	m.buffer.WriteString("\r\n")
	m.buffer.WriteString("--" + m.boundary + "--")

	// Dial
	c, err := smtp.Dial(e.smtpAddress)
	if err != nil {
		return
	}
	defer c.Close()

	// Check if TLS is required
	if ok, _ := c.Extension("STARTTLS"); ok {
		host, _, _ := net.SplitHostPort(e.smtpAddress)
		config := &tls.Config{ServerName: host}
		if err = c.StartTLS(config); err != nil {
			return err
		}
	}

	// Authenticate
	if e.Auth != nil {
		if err = c.Auth(e.Auth); err != nil {
			return
		}
	}

	// Send message
	from, err := mail.ParseAddress(m.From)
	if err != nil {
		return
	}
	if err = c.Mail(from.Address); err != nil {
		return
	}
	to, err := mail.ParseAddressList(m.To)
	if err != nil {
		return
	}
	for _, a := range to {
		if err = c.Rcpt(a.Address); err != nil {
			return
		}
	}
	wc, err := c.Data()
	if err != nil {
		return
	}
	defer wc.Close()
	_, err = m.buffer.WriteTo(wc)
	return
}
<|endoftext|>


<commit_before>package pg_test

import (
	"fmt"

	"github.com/go-pg/pg"
	"github.com/go-pg/pg/orm"
)

func init() {
	// Register many to many model so ORM can better recognize m2m relation.
	// This should be done before dependant models are used.
	orm.RegisterTable((*OrderToItem)(nil))
}

type Order struct {
	Id    int
	Items []Item `pg:"many2many:order_to_items"`
}

type Item struct {
	Id int
}

type OrderToItem struct {
	OrderId int
	ItemId  int
}

func createManyToManyTables(db *pg.DB) error {
	models := []interface{}{
		(*Order)(nil),
		(*Item)(nil),
		(*OrderToItem)(nil),
	}
	for _, model := range models {
		err := db.CreateTable(model, &orm.CreateTableOptions{
			Temp: true,
		})
		if err != nil {
			return err
		}
	}
	return nil
}

func ExampleDB_Model_manyToMany() {
	db := connect()
	defer db.Close()

	if err := createManyToManyTables(db); err != nil {
		panic(err)
	}

	values := []interface{}{
		&Item{Id: 1},
		&Item{Id: 2},
		&Order{Id: 1},
		&OrderToItem{OrderId: 1, ItemId: 1},
		&OrderToItem{OrderId: 1, ItemId: 2},
	}
	for _, v := range values {
		err := db.Insert(v)
		if err != nil {
			panic(err)
		}
	}

	// Select order and all items with following queries:
	//
	// SELECT "order"."id" FROM "orders" AS "order" ORDER BY "order"."id" LIMIT 1
	//
	// SELECT order_to_items.*, "item"."id" FROM "items" AS "item"
	// JOIN order_to_items AS order_to_items ON (order_to_items."order_id") IN (1)
	// WHERE ("item"."id" = order_to_items."item_id")

	order := new(Order)
	err := db.Model(order).Relation("Items").First()
	if err != nil {
		panic(err)
	}
	fmt.Println("Order", order.Id)
	fmt.Println("Items", order.Items[0].Id, order.Items[1].Id)
	// Output: Order 1
	// Items 1 2
}
<commit_msg>Improve many2many example<commit_after>package pg_test

import (
	"fmt"

	"github.com/go-pg/pg"
	"github.com/go-pg/pg/orm"
)

func init() {
	// Register many to many model so ORM can better recognize m2m relation.
	// This should be done before dependant models are used.
	orm.RegisterTable((*OrderToItem)(nil))
}

type Order struct {
	Id    int
	Items []Item `pg:"many2many:order_to_items"`
}

type Item struct {
	Id int
}

type OrderToItem struct {
	OrderId int
	ItemId  int
}

func createManyToManyTables(db *pg.DB) error {
	models := []interface{}{
		(*Order)(nil),
		(*Item)(nil),
		(*OrderToItem)(nil),
	}
	for _, model := range models {
		err := db.CreateTable(model, &orm.CreateTableOptions{
			Temp: true,
		})
		if err != nil {
			return err
		}
	}
	return nil
}

func ExampleDB_Model_manyToMany() {
	db := connect()
	defer db.Close()

	if err := createManyToManyTables(db); err != nil {
		panic(err)
	}

	values := []interface{}{
		&Item{Id: 1},
		&Item{Id: 2},
		&Order{Id: 1},
		&OrderToItem{OrderId: 1, ItemId: 1},
		&OrderToItem{OrderId: 1, ItemId: 2},
	}
	for _, v := range values {
		err := db.Insert(v)
		if err != nil {
			panic(err)
		}
	}

	// Select order and all items with following queries:
	//
	// SELECT "order"."id" FROM "orders" AS "order" ORDER BY "order"."id" LIMIT 1
	//
	// SELECT order_to_items.*, "item"."id" FROM "items" AS "item"
	// JOIN order_to_items AS order_to_items ON (order_to_items."order_id") IN (1)
	// WHERE ("item"."id" = order_to_items."item_id")

	order := new(Order)
	err := db.Model(order).Relation("Items").First()
	if err != nil {
		panic(err)
	}
	fmt.Println("Order", order.Id, "Items", order.Items[0].Id, order.Items[1].Id)

	// Select order and all items sorted by id with following queries:
	//
	// SELECT "order"."id" FROM "orders" AS "order" ORDER BY "order"."id" LIMIT 1
	//
	// SELECT order_to_items.*, "item"."id" FROM "items" AS "item"
	// JOIN order_to_items AS order_to_items ON (order_to_items."order_id") IN (1)
	// WHERE ("item"."id" = order_to_items."item_id")
	// ORDER BY item.id DESC

	order = new(Order)
	err = db.Model(order).
		Relation("Items", func(q *orm.Query) (*orm.Query, error) {
			q = q.OrderExpr("item.id DESC")
			return q, nil
		}).
		First()
	if err != nil {
		panic(err)
	}
	fmt.Println("Order", order.Id, "Items", order.Items[0].Id, order.Items[1].Id)

	// Output: Order 1 Items 1 2
	// Order 1 Items 2 1
}
<|endoftext|>