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

package confd

import (
	"errors"
	"log"
	"net/url"
	"strings"
	"sync"
	"sync/atomic"
)

// BUG(threez) It currently requires to connect directly to the confd database.
// This can be done by connecting through an ssh tunnel and forward the port
// 4472, e.g.:
//
//      ssh -L 4472:127.0.0.1:4472 root@utm

// Conn is the confd connection object
type Conn struct {
	URL       *url.URL    // URL that the connection connects to
	Logger    *log.Logger // Logger if specified, will log confd actions
	Options   *Options    // Options represent connection options
	id        uint64      // json rpc counter
	Transport Transport
	txMu      sync.Mutex // prevent multiple write/read transactions
	sessionMu sync.Mutex // prevent concurrent confd access
}

// NewConn creates a new confd connection (is not acually connecting)
func NewConn(URL string) (conn *Conn, err error) {
	u, err := url.Parse(URL)
	if err != nil {
		return
	}

	conn = &Conn{
		URL:       u,
		Logger:    nil,
		Options:   newOptions(u),
		Transport: &tcpTransport{Timeout: defaultTimeout},
	}
	return
}

// NewAnonymousConn creates a new confd connection (is not acually connecting)
// to http://127.0.0.1:4472/ (Local Connection)
func NewAnonymousConn() (conn *Conn) {
	// error is only for url parsing which can not happen here, therefore ignored
	conn, _ = NewConn(anonymousLocalConn)
	return conn
}

// NewSystemConn creates a new confd connection (is not acually connecting)
// to http://system@127.0.0.1:4472/ (Local Connection)
func NewSystemConn() (conn *Conn) {
	// error is only for url parsing which can not happen here, therefore ignored
	conn, _ = NewConn(systemLocalConn)
	return conn
}

// SimpleRequest sends a simple request (untyped response) to the confd
func (c *Conn) SimpleRequest(method string, params ...interface{}) (interface{}, error) {
	result := new(interface{})
	err := c.Request(method, result, params...)
	return result, err
}

// Request allows to send request with typed (parsed with json) responses
func (c *Conn) Request(method string, result interface{}, params ...interface{}) (err error) {
	// make sure we have a connection to the server
	err = c.connect()
	if err != nil {
		return
	}

	err = c.request(method, result, params...)

	// automatic error handling
	if err == ErrEmptyResponse {
		errs, err := c.ErrList()
		if err != nil {
			return err
		}
		if len(errs) > 0 {
			return errors.New(errs[0].Error())
		}
	}

	if err != nil {
		c.Logger.Printf("Error: %v", err)
	}
	return
}

// Connect creates a new confd session by calling new and get_SID confd calls
func (c *Conn) connect() (err error) {
	if c.Transport.IsConnected() {
		return
	}
	c.sessionMu.Lock()
	defer c.sessionMu.Unlock()
	c.logf("Connect to %s", c.safeURL())
	err = c.Transport.Connect(c.URL)
	if err != nil {
		c.logf("Unable to connect %s", err)
		return
	}
	err = c.request("new", nil, c.Options)
	if err == nil && c.Options.SID == nil {
		// if we got a sid we will use it next time
		err = c.request("get_SID", &c.Options.SID)
	}
	if err != nil {
		c.logf("Unable to create session %v", err)
	}
	return
}

func (c *Conn) request(method string, result interface{}, params ...interface{}) error {
	// request
	id := atomic.AddUint64(&c.id, 1)
	r, err := newRequest(method, params, id)
	if err != nil {
		return err
	}
	c.logf("=> %s", r.String())
	req, err := r.HTTP(c.URL.Host)
	if err != nil {
		return err
	}

	// send request
	resp, err := c.Transport.RoundTrip(req)
	if err != nil {
		// send receive operation failed, conenction will be closed
		_ = c.Transport.Close() // ignore close errors
		return err
	}

	// decode response
	respObj, err := newResponse(resp.Body)
	if err != nil {
		return err
	}
	err = respObj.Decode(result)
	if err != nil {
		return err
	}

	c.logf("<= %v", respObj)

	return nil
}

// Close the confd connection
func (c *Conn) Close() (err error) {
	if c.Transport.IsConnected() {
		c.sessionMu.Lock()
		defer c.sessionMu.Unlock()
		c.logf("Disconnect from %s", c.safeURL())
		_ = c.request("detach", nil) // ignore if we can't detach
		_ = c.Transport.Close()      // ignore close errors
	}
	return
}

func (c *Conn) logf(format string, args ...interface{}) {
	if c.Logger != nil {
		c.Logger.Printf(format, args...)
	}
}

func (c *Conn) safeURL() string {
	if c.Options.Password != "" {
		return strings.Replace(c.URL.String(), c.Options.Password, "********", 1)
	}
	return c.URL.String()
}
<commit_msg>don't use sync/atomic, as it causes problems on some architectures<commit_after>// Copyright 2016 Vincent Landgraf. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package confd

import (
	"errors"
	"log"
	"net/url"
	"strings"
	"sync"
)

// BUG(threez) It currently requires to connect directly to the confd database.
// This can be done by connecting through an ssh tunnel and forward the port
// 4472, e.g.:
//
//      ssh -L 4472:127.0.0.1:4472 root@utm

// Conn is the confd connection object
type Conn struct {
	URL     *url.URL    // URL that the connection connects to
	Logger  *log.Logger // Logger if specified, will log confd actions
	Options *Options    // Options represent connection options
	id      struct {
		Value      uint64 // json rpc counter
		sync.Mutex        // prevent multiple write/read transactions
	}
	Transport Transport
	txMu      sync.Mutex // prevent multiple write/read transactions
	sessionMu sync.Mutex // prevent concurrent confd access
}

// NewConn creates a new confd connection (is not acually connecting)
func NewConn(URL string) (conn *Conn, err error) {
	u, err := url.Parse(URL)
	if err != nil {
		return
	}

	conn = &Conn{
		URL:       u,
		Logger:    nil,
		Options:   newOptions(u),
		Transport: &tcpTransport{Timeout: defaultTimeout},
	}
	return
}

// NewAnonymousConn creates a new confd connection (is not acually connecting)
// to http://127.0.0.1:4472/ (Local Connection)
func NewAnonymousConn() (conn *Conn) {
	// error is only for url parsing which can not happen here, therefore ignored
	conn, _ = NewConn(anonymousLocalConn)
	return conn
}

// NewSystemConn creates a new confd connection (is not acually connecting)
// to http://system@127.0.0.1:4472/ (Local Connection)
func NewSystemConn() (conn *Conn) {
	// error is only for url parsing which can not happen here, therefore ignored
	conn, _ = NewConn(systemLocalConn)
	return conn
}

// SimpleRequest sends a simple request (untyped response) to the confd
func (c *Conn) SimpleRequest(method string, params ...interface{}) (interface{}, error) {
	result := new(interface{})
	err := c.Request(method, result, params...)
	return result, err
}

// Request allows to send request with typed (parsed with json) responses
func (c *Conn) Request(method string, result interface{}, params ...interface{}) (err error) {
	// make sure we have a connection to the server
	err = c.connect()
	if err != nil {
		return
	}

	err = c.request(method, result, params...)

	// automatic error handling
	if err == ErrEmptyResponse {
		errs, err := c.ErrList()
		if err != nil {
			return err
		}
		if len(errs) > 0 {
			return errors.New(errs[0].Error())
		}
	}

	if err != nil {
		c.Logger.Printf("Error: %v", err)
	}
	return
}

// Connect creates a new confd session by calling new and get_SID confd calls
func (c *Conn) connect() (err error) {
	if c.Transport.IsConnected() {
		return
	}
	c.sessionMu.Lock()
	defer c.sessionMu.Unlock()
	c.logf("Connect to %s", c.safeURL())
	err = c.Transport.Connect(c.URL)
	if err != nil {
		c.logf("Unable to connect %s", err)
		return
	}
	err = c.request("new", nil, c.Options)
	if err == nil && c.Options.SID == nil {
		// if we got a sid we will use it next time
		err = c.request("get_SID", &c.Options.SID)
	}
	if err != nil {
		c.logf("Unable to create session %v", err)
	}
	return
}

func (c *Conn) request(method string, result interface{}, params ...interface{}) error {
	// request
	r, err := newRequest(method, params, c.nextID())
	if err != nil {
		return err
	}
	c.logf("=> %s", r.String())
	req, err := r.HTTP(c.URL.Host)
	if err != nil {
		return err
	}

	// send request
	resp, err := c.Transport.RoundTrip(req)
	if err != nil {
		// send receive operation failed, conenction will be closed
		_ = c.Transport.Close() // ignore close errors
		return err
	}

	// decode response
	respObj, err := newResponse(resp.Body)
	if err != nil {
		return err
	}
	err = respObj.Decode(result)
	if err != nil {
		return err
	}

	c.logf("<= %v", respObj)

	return nil
}

// Close the confd connection
func (c *Conn) Close() (err error) {
	if c.Transport.IsConnected() {
		c.sessionMu.Lock()
		defer c.sessionMu.Unlock()
		c.logf("Disconnect from %s", c.safeURL())
		_ = c.request("detach", nil) // ignore if we can't detach
		_ = c.Transport.Close()      // ignore close errors
	}
	return
}

func (c *Conn) logf(format string, args ...interface{}) {
	if c.Logger != nil {
		c.Logger.Printf(format, args...)
	}
}

func (c *Conn) safeURL() string {
	if c.Options.Password != "" {
		return strings.Replace(c.URL.String(), c.Options.Password, "********", 1)
	}
	return c.URL.String()
}

func (c *Conn) nextID() uint64 {
	c.id.Lock()
	defer c.id.Unlock()
	next := c.id.Value
	c.id.Value++
	return next
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"errors"
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	"go/types"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/ziutek/emgo/egc/importer"
	"github.com/ziutek/emgo/gotoc"
)

func egc(ppath string) error {
	srcDir := ""
	if build.IsLocalImport(ppath) {
		var err error
		if srcDir, err = os.Getwd(); err != nil {
			return err
		}
	}
	bp, err := buildCtx.Import(ppath, srcDir, 0)
	if err != nil {
		return err
	}
	return compile(bp)
}

var uptodate = make(map[string]struct{})

var (
	cortexmSizes = &gotoc.StdSizes{4, 8}

	sizesMap = map[string]types.Sizes{
		"cortexm0":  cortexmSizes,
		"cortexm3":  cortexmSizes,
		"cortexm4":  cortexmSizes,
		"cortexm4f": cortexmSizes,
		"cortexm7f": cortexmSizes,
		"cortexm7d": cortexmSizes,

		"amd64": &gotoc.StdSizes{8, 8},
	}
)

func compile(bp *build.Package) error {
	if ok, err := checkPkg(bp); err != nil {
		return err
	} else if ok {
		return nil
	}
	if verbosity > 0 {
		defer fmt.Println(bp.ImportPath)
	}

	// Parse

	flist := make([]*ast.File, 0, len(bp.GoFiles)+1)
	fset := token.NewFileSet()

	for _, fname := range bp.GoFiles {
		fname = filepath.Join(bp.Dir, fname)
		f, err := parser.ParseFile(fset, fname, nil, parser.ParseComments)
		if err != nil {
			return err
		}
		flist = append(flist, f)
	}

	var iimp string

	ppath := bp.ImportPath
	if bp.Name == "main" {
		ppath = "main"
		iimp = `_ "runtime";_ "internal"`
	} else if bp.ImportPath != "internal" {
		iimp = `_ "internal"`
	}

	f, err := parser.ParseFile(
		fset,
		"_iimports.go",
		"package "+bp.Name+";import("+iimp+")",
		0,
	)
	if err != nil {
		return err
	}
	flist = append(flist, f)

	// Type check

	var tcerrors []string

	tc := &types.Config{
		Importer: NewImporter(),
		Sizes:    sizesMap[buildCtx.GOARCH],
		Error:    func(err error) { tcerrors = append(tcerrors, err.Error()) },
	}

	ti := &types.Info{
		Types:      make(map[ast.Expr]types.TypeAndValue),
		Defs:       make(map[*ast.Ident]types.Object),
		Uses:       make(map[*ast.Ident]types.Object),
		Selections: make(map[*ast.SelectorExpr]*types.Selection),
	}

	pkg, err := tc.Check(ppath, fset, flist, ti)
	if err != nil {
		return errors.New(strings.Join(tcerrors, "\n"))
	}

	// Translate to C

	work := filepath.Join(tmpDir, ppath)
	if err = os.MkdirAll(work, 0700); err != nil {
		return err
	}

	var (
		hpath string
		objs  []string
	)

	oat := buildCtx.GOOS + "_" + buildCtx.GOARCH
	if buildCtx.InstallSuffix != "" {
		oat += "_" + buildCtx.InstallSuffix
	}

	if ppath == "main" {
		hpath = filepath.Join(bp.Dir, "__"+oat+".h")
	} else {
		hpath = filepath.Join(bp.PkgRoot, oat, ppath+".h")
		expath := filepath.Join(work, "__.EXPORTS")
		impath := filepath.Join(work, "__.IMPORTS")
		objs = append(objs, expath, impath, hpath)

		err = os.MkdirAll(filepath.Dir(hpath), 0755)
		if err != nil && !os.IsExist(err) {
			return err
		}
		wp, err := os.Create(expath)
		if err != nil {
			return err
		}
		edata := importer.ExportData(pkg)
		_, err = wp.Write(edata)
		if err != nil {
			return err
		}
		wp.Close()
		wp, err = os.Create(impath)
		if err != nil {
			return err
		}
		for _, p := range pkg.Imports() {
			if _, err := io.WriteString(wp, p.Path()+"\n"); err != nil {
				return err
			}
		}
		wp.Close()
	}

	wh, err := os.Create(hpath)
	if err != nil {
		return err
	}
	defer wh.Close()

	cout := "__" + oat + ".c"

	wc, err := os.Create(filepath.Join(bp.Dir, cout))
	if err != nil {
		return err
	}
	defer wc.Close()

	if ppath == "main" {
		h := filepath.Base(hpath)
		_, err = io.WriteString(wc, "#include \""+h+"\"\n\n")
	} else {
		up := gotoc.Upath(ppath)
		_, err = io.WriteString(wh, "#ifndef $"+up+"$\n#define $"+up+"$\n\n")
	}
	if err != nil {
		return err
	}

	gtc := gotoc.NewGTC(fset, pkg, ti, tc.Sizes)
	gtc.SetNoinlineThres(7)
	gtc.SetBoundsCheck(!disableBC)
	if err = gtc.Translate(wh, wc, flist); err != nil {
		return err
	}

	for _, h := range bp.HFiles {
		if !strings.HasSuffix(h, "+.h") {
			continue
		}
		f, err := os.Open(filepath.Join(bp.Dir, h))
		if err != nil {
			return err
		}
		if _, err = io.WriteString(wh, "\n// included "+h+"\n"); err != nil {
			return err
		}
		if _, err = bufio.NewReader(f).WriteTo(wh); err != nil {
			return err
		}
	}

	if ppath != "main" {
		if _, err = io.WriteString(wh, "\n#endif\n"); err != nil {
			return err
		}
	}

	var csfiles = []string{cout}

	for _, c := range bp.CFiles {
		if !strings.HasSuffix(c, "+.c") {
			csfiles = append(csfiles, c)
			continue
		}
		f, err := os.Open(filepath.Join(bp.Dir, c))
		if err != nil {
			return err
		}
		if _, err = io.WriteString(wc, "\n// included "+c+"\n"); err != nil {
			return err
		}
		if _, err = bufio.NewReader(f).WriteTo(wc); err != nil {
			return err
		}
	}
	csfiles = append(csfiles, bp.SFiles...)

	// Build (package or binary)

	bt, err := NewBuildTools(&buildCtx)
	if err != nil {
		return err
	}

	if verbosity > 1 {
		bt.Log = os.Stdout
	}

	for _, c := range csfiles {
		// TODO: avoid recompile up to date objects
		o := filepath.Join(work, c[:len(c)-1]+"o")
		c = filepath.Join(bp.Dir, c)
		if err = bt.Compile(o, c); err != nil {
			return err
		}
		objs = append(objs, o)
	}

	if ppath != "main" {
		if err := bt.Archive(hpath[:len(hpath)-1]+"a", objs...); err != nil {
			return err
		}
		now := time.Now()
		return os.Chtimes(hpath, now, now)
	}

	imports := make([]string, len(pkg.Imports()))
	for i, p := range pkg.Imports() {
		imports[i] = p.Path()
	}
	return bt.Link(
		filepath.Join(bp.Dir, buildCtx.GOARCH+".elf"),
		imports, objs...,
	)
}

// checkPkg returns true if the package and its dependences are up to date
// (doesn't need to be (re)compiled). It always returns false for main package.
func checkPkg(bp *build.Package) (bool, error) {
	if bp.Name == "main" {
		return false, nil
	}
	if _, ok := uptodate[bp.ImportPath]; ok {
		return true, nil
	}
	pkgobj := bp.PkgObj
	oi, err := os.Stat(pkgobj)
	if err != nil {
		if os.IsNotExist(err) {
			return false, nil
		}
		return false, err
	}
	if len(bp.GoFiles) == 0 {
		uptodate[bp.ImportPath] = struct{}{}
		return true, nil
	}
	src := append(bp.GoFiles, bp.CFiles...)
	src = append(src, bp.HFiles...)
	src = append(src, bp.SFiles...)
	dir := filepath.Join(bp.SrcRoot, bp.ImportPath)
	for _, s := range src {
		si, err := os.Stat(filepath.Join(dir, s))
		if err != nil {
			return false, err
		}
		if !oi.ModTime().After(si.ModTime()) {
			return false, nil
		}
	}
	h := bp.PkgObj[:len(bp.PkgObj)-1] + "h"
	ok, err := checkH(h, oi.ModTime())
	if err != nil {
		return false, err
	}
	if !ok {
		data, err := arReadFile(bp.PkgObj, filepath.Base(h))
		if err != nil {
			return false, err
		}
		if err = ioutil.WriteFile(h, data, 0644); err != nil {
			return false, err
		}
	}
	if bp.ImportPath == "internal" {
		if len(bp.Imports) > 1 || len(bp.Imports) == 1 && bp.Imports[0] != "unsafe" {
			return false, errors.New("internal can't import other packages")
		}
	} else {
		imports := addPkg(bp.Imports, "internal")
		for _, imp := range imports {
			if imp == "unsafe" {
				continue
			}
			ibp, err := buildCtx.Import(imp, dir, build.AllowBinary)
			if err != nil {
				return false, err
			}
			if ok, err := checkPkg(ibp); err != nil {
				return false, err
			} else if !ok {
				return false, nil
			} else {
				pi, err := os.Stat(ibp.PkgObj)
				if err != nil {
					return false, err
				}
				if !oi.ModTime().After(pi.ModTime()) {
					return false, nil
				}
			}
			uptodate[imp] = struct{}{}
		}
	}
	uptodate[bp.ImportPath] = struct{}{}
	return true, nil
}

func checkH(h string, omt time.Time) (bool, error) {
	hi, err := os.Stat(h)
	if err != nil {
		if os.IsNotExist(err) {
			return false, nil
		}
		return false, err
	}
	return !omt.Before(hi.ModTime()), nil
}

func addPkg(imports []string, pkg string) []string {
	for _, s := range imports {
		if s == pkg {
			return imports
		}
	}
	return append(imports, pkg)
}
<commit_msg>egc: Slightly increaset noinline treshold.<commit_after>package main

import (
	"bufio"
	"errors"
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	"go/types"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/ziutek/emgo/egc/importer"
	"github.com/ziutek/emgo/gotoc"
)

func egc(ppath string) error {
	srcDir := ""
	if build.IsLocalImport(ppath) {
		var err error
		if srcDir, err = os.Getwd(); err != nil {
			return err
		}
	}
	bp, err := buildCtx.Import(ppath, srcDir, 0)
	if err != nil {
		return err
	}
	return compile(bp)
}

var uptodate = make(map[string]struct{})

var (
	cortexmSizes = &gotoc.StdSizes{4, 8}

	sizesMap = map[string]types.Sizes{
		"cortexm0":  cortexmSizes,
		"cortexm3":  cortexmSizes,
		"cortexm4":  cortexmSizes,
		"cortexm4f": cortexmSizes,
		"cortexm7f": cortexmSizes,
		"cortexm7d": cortexmSizes,

		"amd64": &gotoc.StdSizes{8, 8},
	}
)

func compile(bp *build.Package) error {
	if ok, err := checkPkg(bp); err != nil {
		return err
	} else if ok {
		return nil
	}
	if verbosity > 0 {
		defer fmt.Println(bp.ImportPath)
	}

	// Parse

	flist := make([]*ast.File, 0, len(bp.GoFiles)+1)
	fset := token.NewFileSet()

	for _, fname := range bp.GoFiles {
		fname = filepath.Join(bp.Dir, fname)
		f, err := parser.ParseFile(fset, fname, nil, parser.ParseComments)
		if err != nil {
			return err
		}
		flist = append(flist, f)
	}

	var iimp string

	ppath := bp.ImportPath
	if bp.Name == "main" {
		ppath = "main"
		iimp = `_ "runtime";_ "internal"`
	} else if bp.ImportPath != "internal" {
		iimp = `_ "internal"`
	}

	f, err := parser.ParseFile(
		fset,
		"_iimports.go",
		"package "+bp.Name+";import("+iimp+")",
		0,
	)
	if err != nil {
		return err
	}
	flist = append(flist, f)

	// Type check

	var tcerrors []string

	tc := &types.Config{
		Importer: NewImporter(),
		Sizes:    sizesMap[buildCtx.GOARCH],
		Error:    func(err error) { tcerrors = append(tcerrors, err.Error()) },
	}

	ti := &types.Info{
		Types:      make(map[ast.Expr]types.TypeAndValue),
		Defs:       make(map[*ast.Ident]types.Object),
		Uses:       make(map[*ast.Ident]types.Object),
		Selections: make(map[*ast.SelectorExpr]*types.Selection),
	}

	pkg, err := tc.Check(ppath, fset, flist, ti)
	if err != nil {
		return errors.New(strings.Join(tcerrors, "\n"))
	}

	// Translate to C

	work := filepath.Join(tmpDir, ppath)
	if err = os.MkdirAll(work, 0700); err != nil {
		return err
	}

	var (
		hpath string
		objs  []string
	)

	oat := buildCtx.GOOS + "_" + buildCtx.GOARCH
	if buildCtx.InstallSuffix != "" {
		oat += "_" + buildCtx.InstallSuffix
	}

	if ppath == "main" {
		hpath = filepath.Join(bp.Dir, "__"+oat+".h")
	} else {
		hpath = filepath.Join(bp.PkgRoot, oat, ppath+".h")
		expath := filepath.Join(work, "__.EXPORTS")
		impath := filepath.Join(work, "__.IMPORTS")
		objs = append(objs, expath, impath, hpath)

		err = os.MkdirAll(filepath.Dir(hpath), 0755)
		if err != nil && !os.IsExist(err) {
			return err
		}
		wp, err := os.Create(expath)
		if err != nil {
			return err
		}
		edata := importer.ExportData(pkg)
		_, err = wp.Write(edata)
		if err != nil {
			return err
		}
		wp.Close()
		wp, err = os.Create(impath)
		if err != nil {
			return err
		}
		for _, p := range pkg.Imports() {
			if _, err := io.WriteString(wp, p.Path()+"\n"); err != nil {
				return err
			}
		}
		wp.Close()
	}

	wh, err := os.Create(hpath)
	if err != nil {
		return err
	}
	defer wh.Close()

	cout := "__" + oat + ".c"

	wc, err := os.Create(filepath.Join(bp.Dir, cout))
	if err != nil {
		return err
	}
	defer wc.Close()

	if ppath == "main" {
		h := filepath.Base(hpath)
		_, err = io.WriteString(wc, "#include \""+h+"\"\n\n")
	} else {
		up := gotoc.Upath(ppath)
		_, err = io.WriteString(wh, "#ifndef $"+up+"$\n#define $"+up+"$\n\n")
	}
	if err != nil {
		return err
	}

	gtc := gotoc.NewGTC(fset, pkg, ti, tc.Sizes)
	gtc.SetNoinlineThres(8)
	gtc.SetBoundsCheck(!disableBC)
	if err = gtc.Translate(wh, wc, flist); err != nil {
		return err
	}

	for _, h := range bp.HFiles {
		if !strings.HasSuffix(h, "+.h") {
			continue
		}
		f, err := os.Open(filepath.Join(bp.Dir, h))
		if err != nil {
			return err
		}
		if _, err = io.WriteString(wh, "\n// included "+h+"\n"); err != nil {
			return err
		}
		if _, err = bufio.NewReader(f).WriteTo(wh); err != nil {
			return err
		}
	}

	if ppath != "main" {
		if _, err = io.WriteString(wh, "\n#endif\n"); err != nil {
			return err
		}
	}

	var csfiles = []string{cout}

	for _, c := range bp.CFiles {
		if !strings.HasSuffix(c, "+.c") {
			csfiles = append(csfiles, c)
			continue
		}
		f, err := os.Open(filepath.Join(bp.Dir, c))
		if err != nil {
			return err
		}
		if _, err = io.WriteString(wc, "\n// included "+c+"\n"); err != nil {
			return err
		}
		if _, err = bufio.NewReader(f).WriteTo(wc); err != nil {
			return err
		}
	}
	csfiles = append(csfiles, bp.SFiles...)

	// Build (package or binary)

	bt, err := NewBuildTools(&buildCtx)
	if err != nil {
		return err
	}

	if verbosity > 1 {
		bt.Log = os.Stdout
	}

	for _, c := range csfiles {
		// TODO: avoid recompile up to date objects
		o := filepath.Join(work, c[:len(c)-1]+"o")
		c = filepath.Join(bp.Dir, c)
		if err = bt.Compile(o, c); err != nil {
			return err
		}
		objs = append(objs, o)
	}

	if ppath != "main" {
		if err := bt.Archive(hpath[:len(hpath)-1]+"a", objs...); err != nil {
			return err
		}
		now := time.Now()
		return os.Chtimes(hpath, now, now)
	}

	imports := make([]string, len(pkg.Imports()))
	for i, p := range pkg.Imports() {
		imports[i] = p.Path()
	}
	return bt.Link(
		filepath.Join(bp.Dir, buildCtx.GOARCH+".elf"),
		imports, objs...,
	)
}

// checkPkg returns true if the package and its dependences are up to date
// (doesn't need to be (re)compiled). It always returns false for main package.
func checkPkg(bp *build.Package) (bool, error) {
	if bp.Name == "main" {
		return false, nil
	}
	if _, ok := uptodate[bp.ImportPath]; ok {
		return true, nil
	}
	pkgobj := bp.PkgObj
	oi, err := os.Stat(pkgobj)
	if err != nil {
		if os.IsNotExist(err) {
			return false, nil
		}
		return false, err
	}
	if len(bp.GoFiles) == 0 {
		uptodate[bp.ImportPath] = struct{}{}
		return true, nil
	}
	src := append(bp.GoFiles, bp.CFiles...)
	src = append(src, bp.HFiles...)
	src = append(src, bp.SFiles...)
	dir := filepath.Join(bp.SrcRoot, bp.ImportPath)
	for _, s := range src {
		si, err := os.Stat(filepath.Join(dir, s))
		if err != nil {
			return false, err
		}
		if !oi.ModTime().After(si.ModTime()) {
			return false, nil
		}
	}
	h := bp.PkgObj[:len(bp.PkgObj)-1] + "h"
	ok, err := checkH(h, oi.ModTime())
	if err != nil {
		return false, err
	}
	if !ok {
		data, err := arReadFile(bp.PkgObj, filepath.Base(h))
		if err != nil {
			return false, err
		}
		if err = ioutil.WriteFile(h, data, 0644); err != nil {
			return false, err
		}
	}
	if bp.ImportPath == "internal" {
		if len(bp.Imports) > 1 || len(bp.Imports) == 1 && bp.Imports[0] != "unsafe" {
			return false, errors.New("internal can't import other packages")
		}
	} else {
		imports := addPkg(bp.Imports, "internal")
		for _, imp := range imports {
			if imp == "unsafe" {
				continue
			}
			ibp, err := buildCtx.Import(imp, dir, build.AllowBinary)
			if err != nil {
				return false, err
			}
			if ok, err := checkPkg(ibp); err != nil {
				return false, err
			} else if !ok {
				return false, nil
			} else {
				pi, err := os.Stat(ibp.PkgObj)
				if err != nil {
					return false, err
				}
				if !oi.ModTime().After(pi.ModTime()) {
					return false, nil
				}
			}
			uptodate[imp] = struct{}{}
		}
	}
	uptodate[bp.ImportPath] = struct{}{}
	return true, nil
}

func checkH(h string, omt time.Time) (bool, error) {
	hi, err := os.Stat(h)
	if err != nil {
		if os.IsNotExist(err) {
			return false, nil
		}
		return false, err
	}
	return !omt.Before(hi.ModTime()), nil
}

func addPkg(imports []string, pkg string) []string {
	for _, s := range imports {
		if s == pkg {
			return imports
		}
	}
	return append(imports, pkg)
}
<|endoftext|>


<commit_before>package dynect

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

const (
	DynAPIPrefix = "https://api.dynect.net/REST"
)

// A client for use with DynECT's REST API.
type Client struct {
	Token        string
	CustomerName string
	httpclient   *http.Httpclient
}

// Creates a new Httpclient.
func NewClient(customerName string) *Httpclient {
	return &Httpclient{
		CustomerName: customerName,
		httpclient:   &http.Httpclient{}}
}

// Establishes a new session with the DynECT API.
func (c *Client) Login(username, password string) error {
	var req = LoginBlock{
		Username:     username,
		Password:     password,
		CustomerName: c.CustomerName}

	var resp LoginResponse

	err := c.Do("POST", "Session", req, &resp)
	c.Token = resp.Data.Token
	return nil
}

func (c *Client) LoggedIn() bool {
	return len(c.Token) > 0
}

func (c *Client) Logout() error {
	return c.Do("DELETE", "Session", nil, nil)
}

func (c *Client) Do(method, endpoint string, requestData, responseData interface{}) error {
	if !c.LoggedIn() {
		return errors.New("Will not perform request; httpclient is closed")
	}

	var err error

	// Marshal the request data into a byte slice.
	var js []byte
	js, err = json.Marshal(requestData)
	if err != nil {
		return err
	}

	// Create a new http.Request object, and set the necessary headers to
	// authorize the request, and specify the content type.
	url := fmt.Sprintf("%s/%s", DynAPIPrefix, endpoint)
	var req *http.Request
	req, err = http.NewRequest(method, url, bytes.NewReader(js))
	if err != nil {
		return err
	}
	req.Header.Set("Auth-Token", c.Token)
	req.Header.Set("Content-Type", "application/json")

	var resp *http.Response
	resp, err = c.httpclient.Do(req)
	if err != nil {
		return err
	} else if response.StatusCode != 200 {
		return errors.New(fmt.Sprintf("Bad response, got %q", response.Status))
	}

	// Unmarshal the response data into the provided struct.
	var respBody []byte
	respBody, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	err = json.Unmarshal(respBody, &responseData)
	return err
}
<commit_msg>Bug fixes<commit_after>package dynect

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

const (
	DynAPIPrefix = "https://api.dynect.net/REST"
)

// A client for use with DynECT's REST API.
type Client struct {
	Token        string
	CustomerName string
	httpclient   *http.Client
}

// Creates a new Httpclient.
func NewClient(customerName string) *Client {
	return &Httpclient{
		CustomerName: customerName,
		httpclient:   &http.Client{}}
}

// Establishes a new session with the DynECT API.
func (c *Client) Login(username, password string) error {
	var req = LoginBlock{
		Username:     username,
		Password:     password,
		CustomerName: c.CustomerName}

	var resp LoginResponse

	err := c.Do("POST", "Session", req, &resp)
	c.Token = resp.Data.Token
	return nil
}

func (c *Client) LoggedIn() bool {
	return len(c.Token) > 0
}

func (c *Client) Logout() error {
	return c.Do("DELETE", "Session", nil, nil)
}

func (c *Client) Do(method, endpoint string, requestData, responseData interface{}) error {
	if !c.LoggedIn() {
		return errors.New("Will not perform request; httpclient is closed")
	}

	var err error

	// Marshal the request data into a byte slice.
	var js []byte
	js, err = json.Marshal(requestData)
	if err != nil {
		return err
	}

	// Create a new http.Request object, and set the necessary headers to
	// authorize the request, and specify the content type.
	url := fmt.Sprintf("%s/%s", DynAPIPrefix, endpoint)
	var req *http.Request
	req, err = http.NewRequest(method, url, bytes.NewReader(js))
	if err != nil {
		return err
	}
	req.Header.Set("Auth-Token", c.Token)
	req.Header.Set("Content-Type", "application/json")

	var resp *http.Response
	resp, err = c.httpclient.Do(req)
	if err != nil {
		return err
	} else if resp.StatusCode != 200 {
		return errors.New(fmt.Sprintf("Bad response, got %q", resp.Status))
	}

	// Unmarshal the response data into the provided struct.
	var respBody []byte
	respBody, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	err = json.Unmarshal(respBody, &responseData)
	return err
}
<|endoftext|>


<commit_before>package gocli

import (
	"testing"
	"github.com/stretchr/testify/assert"
)

func TestParseAction(t *testing.T) {
	a := &Action{
	}
	t.Log(a)
	assert.NotNil(t, a)
}
<commit_msg>remove empty test<commit_after><|endoftext|>


<commit_before>package api

import (
	"context"

	"github.com/bytom/errors"
	"github.com/bytom/protocol/bc"
	"github.com/bytom/protocol/bc/types"
)

func (a *API) getWork() Response {
	work, err := a.GetWork()
	if err != nil {
		return NewErrorResponse(err)
	}
	return NewSuccessResponse(work)
}

type SubmitWorkReq struct {
	BlockHeader *types.BlockHeader `json:"block_header"`
}

func (a *API) submitWork(ctx context.Context, req *SubmitWorkReq) Response {
	if err := a.SubmitWork(req.BlockHeader); err != nil {
		return NewErrorResponse(err)
	}
	return NewSuccessResponse(true)
}

func (a *API) getBlockHeaderByHeight(ctx context.Context, req struct {
	Height uint64 `json:"block_height"`
}) Response {
	block, err := a.chain.GetBlockByHeight(req.Height)
	if err != nil {
		return NewErrorResponse(err)
	}

	resp := &BlockHeaderByHeight{
		BlockHeader: &block.BlockHeader,
		Reward:      block.Transactions[0].Outputs[0].Amount,
	}
	return NewSuccessResponse(resp)
}

// GetWorkResp is resp struct for API
type GetWorkResp struct {
	BlockHeader *types.BlockHeader `json:"block_header"`
	Seed        *bc.Hash           `json:"seed"`
}

func (a *API) GetWork() (*GetWorkResp, error) {
	bh, err := a.miningPool.GetWork()
	if err != nil {
		return nil, err
	}

	seed, err := a.chain.CalcNextSeed(&bh.PreviousBlockHash)
	if err != nil {
		return nil, err
	}

	return &GetWorkResp{
		BlockHeader: bh,
		Seed:        seed,
	}, nil
}

func (a *API) SubmitWork(bh *types.BlockHeader) error {
	return a.miningPool.SubmitWork(bh)
}

func (a *API) setMining(in struct {
	IsMining bool `json:"is_mining"`
}) Response {
	if in.IsMining {
		return a.StartMining()
	} else {
		return a.StopMining()
	}
}

func (a *API) StartMining() Response {
	if a.cpuMiner.IsMining() {
		return NewErrorResponse(errors.New("Mining is already running"))
	}
	a.cpuMiner.Start()
	return NewSuccessResponse("")
}

func (a *API) StopMining() Response {
	a.cpuMiner.Stop()
	if a.cpuMiner.IsMining() {
		return NewErrorResponse(errors.New("Failed to stop mining"))
	}
	return NewSuccessResponse("")
}
<commit_msg>Optimized mining api (#585)<commit_after>package api

import (
	"context"

	"github.com/bytom/errors"
	"github.com/bytom/protocol/bc"
	"github.com/bytom/protocol/bc/types"
)

func (a *API) getWork() Response {
	work, err := a.GetWork()
	if err != nil {
		return NewErrorResponse(err)
	}
	return NewSuccessResponse(work)
}

type SubmitWorkReq struct {
	BlockHeader *types.BlockHeader `json:"block_header"`
}

func (a *API) submitWork(ctx context.Context, req *SubmitWorkReq) Response {
	if err := a.SubmitWork(req.BlockHeader); err != nil {
		return NewErrorResponse(err)
	}
	return NewSuccessResponse(true)
}

func (a *API) getBlockHeaderByHeight(ctx context.Context, req struct {
	Height uint64 `json:"block_height"`
}) Response {
	block, err := a.chain.GetBlockByHeight(req.Height)
	if err != nil {
		return NewErrorResponse(err)
	}

	resp := &BlockHeaderByHeight{
		BlockHeader: &block.BlockHeader,
		Reward:      block.Transactions[0].Outputs[0].Amount,
	}
	return NewSuccessResponse(resp)
}

// GetWorkResp is resp struct for API
type GetWorkResp struct {
	BlockHeader *types.BlockHeader `json:"block_header"`
	Seed        *bc.Hash           `json:"seed"`
}

func (a *API) GetWork() (*GetWorkResp, error) {
	bh, err := a.miningPool.GetWork()
	if err != nil {
		return nil, err
	}

	seed, err := a.chain.CalcNextSeed(&bh.PreviousBlockHash)
	if err != nil {
		return nil, err
	}

	return &GetWorkResp{
		BlockHeader: bh,
		Seed:        seed,
	}, nil
}

func (a *API) SubmitWork(bh *types.BlockHeader) error {
	return a.miningPool.SubmitWork(bh)
}

func (a *API) setMining(in struct {
	IsMining bool `json:"is_mining"`
}) Response {
	if in.IsMining {
		return a.startMining()
	}
	return a.stopMining()
}

func (a *API) startMining() Response {
	a.cpuMiner.Start()
	if !a.IsMining() {
		return NewErrorResponse(errors.New("Failed to start mining"))
	}
	return NewSuccessResponse("")
}

func (a *API) stopMining() Response {
	a.cpuMiner.Stop()
	if a.IsMining() {
		return NewErrorResponse(errors.New("Failed to stop mining"))
	}
	return NewSuccessResponse("")
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"
	"sort"
	"time"

	"github.com/hashicorp/nomad/nomad/structs"
)

// Nodes is used to query node-related API endpoints
type Nodes struct {
	client *Client
}

// Nodes returns a handle on the node endpoints.
func (c *Client) Nodes() *Nodes {
	return &Nodes{client: c}
}

// List is used to list out all of the nodes
func (n *Nodes) List(q *QueryOptions) ([]*NodeListStub, *QueryMeta, error) {
	var resp NodeIndexSort
	qm, err := n.client.query("/v1/nodes", &resp, q)
	if err != nil {
		return nil, nil, err
	}
	sort.Sort(resp)
	return resp, qm, nil
}

func (n *Nodes) PrefixList(prefix string) ([]*NodeListStub, *QueryMeta, error) {
	return n.List(&QueryOptions{Prefix: prefix})
}

// Info is used to query a specific node by its ID.
func (n *Nodes) Info(nodeID string, q *QueryOptions) (*Node, *QueryMeta, error) {
	var resp Node
	qm, err := n.client.query("/v1/node/"+nodeID, &resp, q)
	if err != nil {
		return nil, nil, err
	}
	return &resp, qm, nil
}

// NodeUpdateDrainRequest is used to update the drain specification for a node.
type NodeUpdateDrainRequest struct {
	// NodeID is the node to update the drain specification for.
	NodeID string

	// DrainSpec is the drain specification to set for the node. A nil DrainSpec
	// will disable draining.
	DrainSpec *DrainSpec

	// MarkEligible marks the node as eligible if removing the drain strategy.
	MarkEligible bool
}

// UpdateDrain is used to update the drain strategy for a given node. If
// markEligible is true and the drain is being removed, the node will be marked
// as having its scheduling being elibile
func (n *Nodes) UpdateDrain(nodeID string, spec *DrainSpec, markEligible bool, q *WriteOptions) (*WriteMeta, error) {
	req := &NodeUpdateDrainRequest{
		NodeID:       nodeID,
		DrainSpec:    spec,
		MarkEligible: markEligible,
	}

	wm, err := n.client.write("/v1/node/"+nodeID+"/drain", req, nil, q)
	if err != nil {
		return nil, err
	}
	return wm, nil
}

// NodeUpdateEligibilityRequest is used to update the drain specification for a node.
type NodeUpdateEligibilityRequest struct {
	// NodeID is the node to update the drain specification for.
	NodeID      string
	Eligibility string
}

// ToggleEligibility is used to update the scheduling eligibility of the node
func (n *Nodes) ToggleEligibility(nodeID string, eligible bool, q *WriteOptions) (*WriteMeta, error) {
	e := structs.NodeSchedulingEligible
	if !eligible {
		e = structs.NodeSchedulingIneligible
	}

	req := &NodeUpdateEligibilityRequest{
		NodeID:      nodeID,
		Eligibility: e,
	}

	wm, err := n.client.write("/v1/node/"+nodeID+"/eligibility", req, nil, q)
	if err != nil {
		return nil, err
	}
	return wm, nil
}

// Allocations is used to return the allocations associated with a node.
func (n *Nodes) Allocations(nodeID string, q *QueryOptions) ([]*Allocation, *QueryMeta, error) {
	var resp []*Allocation
	qm, err := n.client.query("/v1/node/"+nodeID+"/allocations", &resp, q)
	if err != nil {
		return nil, nil, err
	}
	sort.Sort(AllocationSort(resp))
	return resp, qm, nil
}

// ForceEvaluate is used to force-evaluate an existing node.
func (n *Nodes) ForceEvaluate(nodeID string, q *WriteOptions) (string, *WriteMeta, error) {
	var resp nodeEvalResponse
	wm, err := n.client.write("/v1/node/"+nodeID+"/evaluate", nil, &resp, q)
	if err != nil {
		return "", nil, err
	}
	return resp.EvalID, wm, nil
}

func (n *Nodes) Stats(nodeID string, q *QueryOptions) (*HostStats, error) {
	var resp HostStats
	path := fmt.Sprintf("/v1/client/stats?node_id=%s", nodeID)
	if _, err := n.client.query(path, &resp, q); err != nil {
		return nil, err
	}
	return &resp, nil
}

func (n *Nodes) GC(nodeID string, q *QueryOptions) error {
	var resp struct{}
	path := fmt.Sprintf("/v1/client/gc?node_id=%s", nodeID)
	_, err := n.client.query(path, &resp, q)
	return err
}

// TODO Add tests
func (n *Nodes) GcAlloc(allocID string, q *QueryOptions) error {
	var resp struct{}
	path := fmt.Sprintf("/v1/client/allocation/%s/gc", allocID)
	_, err := n.client.query(path, &resp, q)
	return err
}

// DriverInfo is used to deserialize a DriverInfo entry
type DriverInfo struct {
	Attributes        map[string]string
	Detected          bool
	Healthy           bool
	HealthDescription string
	UpdateTime        time.Time
}

// Node is used to deserialize a node entry.
type Node struct {
	ID                    string
	Datacenter            string
	Name                  string
	HTTPAddr              string
	TLSEnabled            bool
	Attributes            map[string]string
	Resources             *Resources
	Reserved              *Resources
	Links                 map[string]string
	Meta                  map[string]string
	NodeClass             string
	Drain                 bool
	DrainStrategy         *DrainStrategy
	SchedulingEligibility string
	Status                string
	StatusDescription     string
	StatusUpdatedAt       int64
	Events                []*NodeEvent
	Drivers               map[string]*DriverInfo
	CreateIndex           uint64
	ModifyIndex           uint64
}

// DrainStrategy describes a Node's drain behavior.
type DrainStrategy struct {
	// DrainSpec is the user declared drain specification
	DrainSpec

	// ForceDeadline is the deadline time for the drain after which drains will
	// be forced
	ForceDeadline time.Time
}

// DrainSpec describes a Node's drain behavior.
type DrainSpec struct {
	// Deadline is the duration after StartTime when the remaining
	// allocations on a draining Node should be told to stop.
	Deadline time.Duration

	// IgnoreSystemJobs allows systems jobs to remain on the node even though it
	// has been marked for draining.
	IgnoreSystemJobs bool
}

const (
	NodeEventSubsystemDrain     = "Drain"
	NodeEventSubsystemDriver    = "Driver"
	NodeEventSubsystemHeartbeat = "Heartbeat"
	NodeEventSubsystemCluster   = "Cluster"
)

// NodeEvent is a single unit representing a node’s state change
type NodeEvent struct {
	Message     string
	Subsystem   string
	Details     map[string]string
	Timestamp   int64
	CreateIndex uint64
}

// HostStats represents resource usage stats of the host running a Nomad client
type HostStats struct {
	Memory           *HostMemoryStats
	CPU              []*HostCPUStats
	DiskStats        []*HostDiskStats
	Uptime           uint64
	CPUTicksConsumed float64
}

type HostMemoryStats struct {
	Total     uint64
	Available uint64
	Used      uint64
	Free      uint64
}

type HostCPUStats struct {
	CPU    string
	User   float64
	System float64
	Idle   float64
}

type HostDiskStats struct {
	Device            string
	Mountpoint        string
	Size              uint64
	Used              uint64
	Available         uint64
	UsedPercent       float64
	InodesUsedPercent float64
}

// NodeListStub is a subset of information returned during
// node list operations.
type NodeListStub struct {
	Address               string
	ID                    string
	Datacenter            string
	Name                  string
	NodeClass             string
	Version               string
	Drain                 bool
	SchedulingEligibility string
	Status                string
	StatusDescription     string
	CreateIndex           uint64
	ModifyIndex           uint64
}

// NodeIndexSort reverse sorts nodes by CreateIndex
type NodeIndexSort []*NodeListStub

func (n NodeIndexSort) Len() int {
	return len(n)
}

func (n NodeIndexSort) Less(i, j int) bool {
	return n[i].CreateIndex > n[j].CreateIndex
}

func (n NodeIndexSort) Swap(i, j int) {
	n[i], n[j] = n[j], n[i]
}

// nodeEvalResponse is used to decode a force-eval.
type nodeEvalResponse struct {
	EvalID string
}

// AllocationSort reverse sorts allocs by CreateIndex.
type AllocationSort []*Allocation

func (a AllocationSort) Len() int {
	return len(a)
}

func (a AllocationSort) Less(i, j int) bool {
	return a[i].CreateIndex > a[j].CreateIndex
}

func (a AllocationSort) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}
<commit_msg>docs: improve DrainRequest.MarkEligible comment<commit_after>package api

import (
	"fmt"
	"sort"
	"time"

	"github.com/hashicorp/nomad/nomad/structs"
)

// Nodes is used to query node-related API endpoints
type Nodes struct {
	client *Client
}

// Nodes returns a handle on the node endpoints.
func (c *Client) Nodes() *Nodes {
	return &Nodes{client: c}
}

// List is used to list out all of the nodes
func (n *Nodes) List(q *QueryOptions) ([]*NodeListStub, *QueryMeta, error) {
	var resp NodeIndexSort
	qm, err := n.client.query("/v1/nodes", &resp, q)
	if err != nil {
		return nil, nil, err
	}
	sort.Sort(resp)
	return resp, qm, nil
}

func (n *Nodes) PrefixList(prefix string) ([]*NodeListStub, *QueryMeta, error) {
	return n.List(&QueryOptions{Prefix: prefix})
}

// Info is used to query a specific node by its ID.
func (n *Nodes) Info(nodeID string, q *QueryOptions) (*Node, *QueryMeta, error) {
	var resp Node
	qm, err := n.client.query("/v1/node/"+nodeID, &resp, q)
	if err != nil {
		return nil, nil, err
	}
	return &resp, qm, nil
}

// NodeUpdateDrainRequest is used to update the drain specification for a node.
type NodeUpdateDrainRequest struct {
	// NodeID is the node to update the drain specification for.
	NodeID string

	// DrainSpec is the drain specification to set for the node. A nil DrainSpec
	// will disable draining.
	DrainSpec *DrainSpec

	// MarkEligible marks the node as eligible for scheduling if removing
	// the drain strategy.
	MarkEligible bool
}

// UpdateDrain is used to update the drain strategy for a given node. If
// markEligible is true and the drain is being removed, the node will be marked
// as having its scheduling being elibile
func (n *Nodes) UpdateDrain(nodeID string, spec *DrainSpec, markEligible bool, q *WriteOptions) (*WriteMeta, error) {
	req := &NodeUpdateDrainRequest{
		NodeID:       nodeID,
		DrainSpec:    spec,
		MarkEligible: markEligible,
	}

	wm, err := n.client.write("/v1/node/"+nodeID+"/drain", req, nil, q)
	if err != nil {
		return nil, err
	}
	return wm, nil
}

// NodeUpdateEligibilityRequest is used to update the drain specification for a node.
type NodeUpdateEligibilityRequest struct {
	// NodeID is the node to update the drain specification for.
	NodeID      string
	Eligibility string
}

// ToggleEligibility is used to update the scheduling eligibility of the node
func (n *Nodes) ToggleEligibility(nodeID string, eligible bool, q *WriteOptions) (*WriteMeta, error) {
	e := structs.NodeSchedulingEligible
	if !eligible {
		e = structs.NodeSchedulingIneligible
	}

	req := &NodeUpdateEligibilityRequest{
		NodeID:      nodeID,
		Eligibility: e,
	}

	wm, err := n.client.write("/v1/node/"+nodeID+"/eligibility", req, nil, q)
	if err != nil {
		return nil, err
	}
	return wm, nil
}

// Allocations is used to return the allocations associated with a node.
func (n *Nodes) Allocations(nodeID string, q *QueryOptions) ([]*Allocation, *QueryMeta, error) {
	var resp []*Allocation
	qm, err := n.client.query("/v1/node/"+nodeID+"/allocations", &resp, q)
	if err != nil {
		return nil, nil, err
	}
	sort.Sort(AllocationSort(resp))
	return resp, qm, nil
}

// ForceEvaluate is used to force-evaluate an existing node.
func (n *Nodes) ForceEvaluate(nodeID string, q *WriteOptions) (string, *WriteMeta, error) {
	var resp nodeEvalResponse
	wm, err := n.client.write("/v1/node/"+nodeID+"/evaluate", nil, &resp, q)
	if err != nil {
		return "", nil, err
	}
	return resp.EvalID, wm, nil
}

func (n *Nodes) Stats(nodeID string, q *QueryOptions) (*HostStats, error) {
	var resp HostStats
	path := fmt.Sprintf("/v1/client/stats?node_id=%s", nodeID)
	if _, err := n.client.query(path, &resp, q); err != nil {
		return nil, err
	}
	return &resp, nil
}

func (n *Nodes) GC(nodeID string, q *QueryOptions) error {
	var resp struct{}
	path := fmt.Sprintf("/v1/client/gc?node_id=%s", nodeID)
	_, err := n.client.query(path, &resp, q)
	return err
}

// TODO Add tests
func (n *Nodes) GcAlloc(allocID string, q *QueryOptions) error {
	var resp struct{}
	path := fmt.Sprintf("/v1/client/allocation/%s/gc", allocID)
	_, err := n.client.query(path, &resp, q)
	return err
}

// DriverInfo is used to deserialize a DriverInfo entry
type DriverInfo struct {
	Attributes        map[string]string
	Detected          bool
	Healthy           bool
	HealthDescription string
	UpdateTime        time.Time
}

// Node is used to deserialize a node entry.
type Node struct {
	ID                    string
	Datacenter            string
	Name                  string
	HTTPAddr              string
	TLSEnabled            bool
	Attributes            map[string]string
	Resources             *Resources
	Reserved              *Resources
	Links                 map[string]string
	Meta                  map[string]string
	NodeClass             string
	Drain                 bool
	DrainStrategy         *DrainStrategy
	SchedulingEligibility string
	Status                string
	StatusDescription     string
	StatusUpdatedAt       int64
	Events                []*NodeEvent
	Drivers               map[string]*DriverInfo
	CreateIndex           uint64
	ModifyIndex           uint64
}

// DrainStrategy describes a Node's drain behavior.
type DrainStrategy struct {
	// DrainSpec is the user declared drain specification
	DrainSpec

	// ForceDeadline is the deadline time for the drain after which drains will
	// be forced
	ForceDeadline time.Time
}

// DrainSpec describes a Node's drain behavior.
type DrainSpec struct {
	// Deadline is the duration after StartTime when the remaining
	// allocations on a draining Node should be told to stop.
	Deadline time.Duration

	// IgnoreSystemJobs allows systems jobs to remain on the node even though it
	// has been marked for draining.
	IgnoreSystemJobs bool
}

const (
	NodeEventSubsystemDrain     = "Drain"
	NodeEventSubsystemDriver    = "Driver"
	NodeEventSubsystemHeartbeat = "Heartbeat"
	NodeEventSubsystemCluster   = "Cluster"
)

// NodeEvent is a single unit representing a node’s state change
type NodeEvent struct {
	Message     string
	Subsystem   string
	Details     map[string]string
	Timestamp   int64
	CreateIndex uint64
}

// HostStats represents resource usage stats of the host running a Nomad client
type HostStats struct {
	Memory           *HostMemoryStats
	CPU              []*HostCPUStats
	DiskStats        []*HostDiskStats
	Uptime           uint64
	CPUTicksConsumed float64
}

type HostMemoryStats struct {
	Total     uint64
	Available uint64
	Used      uint64
	Free      uint64
}

type HostCPUStats struct {
	CPU    string
	User   float64
	System float64
	Idle   float64
}

type HostDiskStats struct {
	Device            string
	Mountpoint        string
	Size              uint64
	Used              uint64
	Available         uint64
	UsedPercent       float64
	InodesUsedPercent float64
}

// NodeListStub is a subset of information returned during
// node list operations.
type NodeListStub struct {
	Address               string
	ID                    string
	Datacenter            string
	Name                  string
	NodeClass             string
	Version               string
	Drain                 bool
	SchedulingEligibility string
	Status                string
	StatusDescription     string
	CreateIndex           uint64
	ModifyIndex           uint64
}

// NodeIndexSort reverse sorts nodes by CreateIndex
type NodeIndexSort []*NodeListStub

func (n NodeIndexSort) Len() int {
	return len(n)
}

func (n NodeIndexSort) Less(i, j int) bool {
	return n[i].CreateIndex > n[j].CreateIndex
}

func (n NodeIndexSort) Swap(i, j int) {
	n[i], n[j] = n[j], n[i]
}

// nodeEvalResponse is used to decode a force-eval.
type nodeEvalResponse struct {
	EvalID string
}

// AllocationSort reverse sorts allocs by CreateIndex.
type AllocationSort []*Allocation

func (a AllocationSort) Len() int {
	return len(a)
}

func (a AllocationSort) Less(i, j int) bool {
	return a[i].CreateIndex > a[j].CreateIndex
}

func (a AllocationSort) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}
<|endoftext|>


<commit_before>// Copyright 2017 Xiaomi, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     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 alarm

import (
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	h "github.com/open-falcon/falcon-plus/modules/api/app/helper"
	alm "github.com/open-falcon/falcon-plus/modules/api/app/model/alarm"
	"strings"
)

type APIGetAlarmListsInputs struct {
	StartTime     int64  `json:"startTime" form:"startTime"`
	EndTime       int64  `json:"endTime" form:"endTime"`
	Priority      int    `json:"priority" form:"priority"`
	Status        string `json:"status" form:"status"`
	ProcessStatus string `json:"process_status" form:"process_status"`
	Metrics       string `json:"metrics" form:"metrics"`
	//id
	EventId string `json:"event_id" form:"event_id"`
	//number of reacord's limit on each page
	Limit int `json:"limit" form:"limit"`
	//pagging
	Page int `json:"page" form:"page"`
	//endpoints strategy template
	Endpoints  []string `json:"endpoints" form:"endpoints"`
	StrategyId int      `json:"strategy_id" form:"strategy_id"`
	TemplateId int      `json:"template_id" form:"template_id"`
}

func (input APIGetAlarmListsInputs) checkInputsContain() error {
	if input.StartTime == 0 && input.EndTime == 0 {
		if input.EventId == "" && input.Endpoints == nil && input.StrategyId == 0 && input.TemplateId == 0 {
			return errors.New("startTime, endTime, event_id, endpoints, strategy_id or template_id, You have to at least pick one on the request.")
		}
	}
	return nil
}

func (s APIGetAlarmListsInputs) collectFilters() string {
	tmp := []string{}
	if s.StartTime != 0 {
		tmp = append(tmp, fmt.Sprintf("timestamp >= FROM_UNIXTIME(%v)", s.StartTime))
	}
	if s.EndTime != 0 {
		tmp = append(tmp, fmt.Sprintf("timestamp <= FROM_UNIXTIME(%v)", s.EndTime))
	}
	if s.Priority != -1 {
		tmp = append(tmp, fmt.Sprintf("priority = %d", s.Priority))
	}
	if s.Status != "" {
		status := ""
		statusTmp := strings.Split(s.Status, ",")
		for indx, n := range statusTmp {
			if indx == 0 {
				status = fmt.Sprintf(" status = '%s' ", n)
			} else {
				status = fmt.Sprintf(" %s OR status = '%s' ", status, n)
			}
		}
		status = fmt.Sprintf("( %s )", status)
		tmp = append(tmp, status)
	}
	if s.ProcessStatus != "" {
		pstatus := ""
		pstatusTmp := strings.Split(s.ProcessStatus, ",")
		for indx, n := range pstatusTmp {
			if indx == 0 {
				pstatus = fmt.Sprintf(" process_status = '%s' ", n)
			} else {
				pstatus = fmt.Sprintf(" %s OR process_status = '%s' ", pstatus, n)
			}
		}
		pstatus = fmt.Sprintf("( %s )", pstatus)
		tmp = append(tmp, pstatus)
	}
	if s.Metrics != "" {
		tmp = append(tmp, fmt.Sprintf("metrics regexp '%s'", s.Metrics))
	}
	if s.EventId != "" {
		tmp = append(tmp, fmt.Sprintf("id = '%s'", s.EventId))
	}
	if s.Endpoints != nil && len(s.Endpoints) != 0 {
		for i, ep := range s.Endpoints {
			s.Endpoints[i] = fmt.Sprintf("'%s'", ep)
		}
		tmp = append(tmp, fmt.Sprintf("endpoint in (%s)", strings.Join(s.Endpoints, ", ")))
	}
	if s.StrategyId != 0 {
		tmp = append(tmp, fmt.Sprintf("strategy_id = %d", s.StrategyId))
	}
	if s.TemplateId != 0 {
		tmp = append(tmp, fmt.Sprintf("template_id = %d", s.TemplateId))
	}
	filterStrTmp := strings.Join(tmp, " AND ")
	if filterStrTmp != "" {
		filterStrTmp = fmt.Sprintf("WHERE %s", filterStrTmp)
	}
	return filterStrTmp
}

func AlarmLists(c *gin.Context) {
	var inputs APIGetAlarmListsInputs
	//set default
	inputs.Page = -1
	inputs.Priority = -1
	if err := c.Bind(&inputs); err != nil {
		h.JSONR(c, badstatus, err)
		return
	}
	if err := inputs.checkInputsContain(); err != nil {
		h.JSONR(c, badstatus, err)
		return
	}
	filterCollector := inputs.collectFilters()
	//for get correct table name
	f := alm.EventCases{}
	cevens := []alm.EventCases{}
	perparedSql := ""
	//if no specific, will give return first 2000 records
	if inputs.Page == -1 {
		if inputs.Limit >= 2000 || inputs.Limit == 0 {
			inputs.Limit = 2000
		}
		perparedSql = fmt.Sprintf("select * from %s %s order by timestamp DESC limit %d", f.TableName(), filterCollector, inputs.Limit)
	} else {
		//set the max limit of each page
		if inputs.Limit >= 50 {
			inputs.Limit = 50
		}

		// if page stands for step page
		// {"page":0} for actual page 1
		// step = page * limit
		step := inputs.Page * inputs.Limit

		perparedSql = fmt.Sprintf("select * from %s %s  order by timestamp DESC limit %d,%d", f.TableName(), filterCollector, step, inputs.Limit)
	}
	db.Alarm.Raw(perparedSql).Find(&cevens)
	h.JSONR(c, cevens)
}

type APIEventsGetInputs struct {
	StartTime int64 `json:"startTime" form:"startTime"`
	EndTime   int64 `json:"endTime" form:"endTime"`
	Status    int   `json:"status" form:"status" binding:"gte=-1,lte=1"`
	//event_caseId
	EventId string `json:"event_id" form:"event_id" binding:"required"`
	//number of reacord's limit on each page
	Limit int `json:"limit" form:"limit"`
	//pagging
	Page int `json:"page" form:"page"`
}

func (s APIEventsGetInputs) collectFilters() string {
	tmp := []string{}
	filterStrTmp := ""
	if s.StartTime != 0 {
		tmp = append(tmp, fmt.Sprintf("timestamp >= FROM_UNIXTIME(%v)", s.StartTime))
	}
	if s.EndTime != 0 {
		tmp = append(tmp, fmt.Sprintf("timestamp <= FROM_UNIXTIME(%v)", s.EndTime))
	}
	if s.EventId != "" {
		tmp = append(tmp, fmt.Sprintf("event_caseId = '%s'", s.EventId))
	}
	if s.Status == 0 || s.Status == 1 {
		tmp = append(tmp, fmt.Sprintf("status = %d", s.Status))
	}
	if len(tmp) != 0 {
		filterStrTmp = strings.Join(tmp, " AND ")
		filterStrTmp = fmt.Sprintf("WHERE %s", filterStrTmp)
	}
	return filterStrTmp
}

func EventsGet(c *gin.Context) {
	var inputs APIEventsGetInputs
	inputs.Status = -1
	if err := c.Bind(&inputs); err != nil {
		h.JSONR(c, badstatus, err)
		return
	}
	filterCollector := inputs.collectFilters()
	//for get correct table name
	f := alm.Events{}
	evens := []alm.Events{}
	if inputs.Limit == 0 || inputs.Limit >= 50 {
		inputs.Limit = 50
	}
	perparedSql := fmt.Sprintf("select id, event_caseId, cond, status, timestamp from %s %s order by timestamp DESC limit %d,%d", f.TableName(), filterCollector, inputs.Page, inputs.Limit)
	db.Alarm.Raw(perparedSql).Scan(&evens)
	h.JSONR(c, evens)
}
<commit_msg>gofmt code<commit_after>// Copyright 2017 Xiaomi, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     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 alarm

import (
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	h "github.com/open-falcon/falcon-plus/modules/api/app/helper"
	alm "github.com/open-falcon/falcon-plus/modules/api/app/model/alarm"
	"strings"
)

type APIGetAlarmListsInputs struct {
	StartTime     int64  `json:"startTime" form:"startTime"`
	EndTime       int64  `json:"endTime" form:"endTime"`
	Priority      int    `json:"priority" form:"priority"`
	Status        string `json:"status" form:"status"`
	ProcessStatus string `json:"process_status" form:"process_status"`
	Metrics       string `json:"metrics" form:"metrics"`
	//id
	EventId string `json:"event_id" form:"event_id"`
	//number of reacord's limit on each page
	Limit int `json:"limit" form:"limit"`
	//pagging
	Page int `json:"page" form:"page"`
	//endpoints strategy template
	Endpoints  []string `json:"endpoints" form:"endpoints"`
	StrategyId int      `json:"strategy_id" form:"strategy_id"`
	TemplateId int      `json:"template_id" form:"template_id"`
}

func (input APIGetAlarmListsInputs) checkInputsContain() error {
	if input.StartTime == 0 && input.EndTime == 0 {
		if input.EventId == "" && input.Endpoints == nil && input.StrategyId == 0 && input.TemplateId == 0 {
			return errors.New("startTime, endTime, event_id, endpoints, strategy_id or template_id, You have to at least pick one on the request.")
		}
	}
	return nil
}

func (s APIGetAlarmListsInputs) collectFilters() string {
	tmp := []string{}
	if s.StartTime != 0 {
		tmp = append(tmp, fmt.Sprintf("timestamp >= FROM_UNIXTIME(%v)", s.StartTime))
	}
	if s.EndTime != 0 {
		tmp = append(tmp, fmt.Sprintf("timestamp <= FROM_UNIXTIME(%v)", s.EndTime))
	}
	if s.Priority != -1 {
		tmp = append(tmp, fmt.Sprintf("priority = %d", s.Priority))
	}
	if s.Status != "" {
		status := ""
		statusTmp := strings.Split(s.Status, ",")
		for indx, n := range statusTmp {
			if indx == 0 {
				status = fmt.Sprintf(" status = '%s' ", n)
			} else {
				status = fmt.Sprintf(" %s OR status = '%s' ", status, n)
			}
		}
		status = fmt.Sprintf("( %s )", status)
		tmp = append(tmp, status)
	}
	if s.ProcessStatus != "" {
		pstatus := ""
		pstatusTmp := strings.Split(s.ProcessStatus, ",")
		for indx, n := range pstatusTmp {
			if indx == 0 {
				pstatus = fmt.Sprintf(" process_status = '%s' ", n)
			} else {
				pstatus = fmt.Sprintf(" %s OR process_status = '%s' ", pstatus, n)
			}
		}
		pstatus = fmt.Sprintf("( %s )", pstatus)
		tmp = append(tmp, pstatus)
	}
	if s.Metrics != "" {
		tmp = append(tmp, fmt.Sprintf("metrics regexp '%s'", s.Metrics))
	}
	if s.EventId != "" {
		tmp = append(tmp, fmt.Sprintf("id = '%s'", s.EventId))
	}
	if s.Endpoints != nil && len(s.Endpoints) != 0 {
		for i, ep := range s.Endpoints {
			s.Endpoints[i] = fmt.Sprintf("'%s'", ep)
		}
		tmp = append(tmp, fmt.Sprintf("endpoint in (%s)", strings.Join(s.Endpoints, ", ")))
	}
	if s.StrategyId != 0 {
		tmp = append(tmp, fmt.Sprintf("strategy_id = %d", s.StrategyId))
	}
	if s.TemplateId != 0 {
		tmp = append(tmp, fmt.Sprintf("template_id = %d", s.TemplateId))
	}
	filterStrTmp := strings.Join(tmp, " AND ")
	if filterStrTmp != "" {
		filterStrTmp = fmt.Sprintf("WHERE %s", filterStrTmp)
	}
	return filterStrTmp
}

func AlarmLists(c *gin.Context) {
	var inputs APIGetAlarmListsInputs
	//set default
	inputs.Page = -1
	inputs.Priority = -1
	if err := c.Bind(&inputs); err != nil {
		h.JSONR(c, badstatus, err)
		return
	}
	if err := inputs.checkInputsContain(); err != nil {
		h.JSONR(c, badstatus, err)
		return
	}
	filterCollector := inputs.collectFilters()
	//for get correct table name
	f := alm.EventCases{}
	cevens := []alm.EventCases{}
	perparedSql := ""
	//if no specific, will give return first 2000 records
	if inputs.Page == -1 {
		if inputs.Limit >= 2000 || inputs.Limit == 0 {
			inputs.Limit = 2000
		}
		perparedSql = fmt.Sprintf("select * from %s %s order by timestamp DESC limit %d", f.TableName(), filterCollector, inputs.Limit)
	} else {
		//set the max limit of each page
		if inputs.Limit >= 50 {
			inputs.Limit = 50
		}

		// if page stands for step page
		// {"page":0} for actual page 1
		// step = page * limit
		step := inputs.Page * inputs.Limit

		perparedSql = fmt.Sprintf("select * from %s %s  order by timestamp DESC limit %d,%d", f.TableName(), filterCollector, step, inputs.Limit)
	}
	db.Alarm.Raw(perparedSql).Find(&cevens)
	h.JSONR(c, cevens)
}

type APIEventsGetInputs struct {
	StartTime int64 `json:"startTime" form:"startTime"`
	EndTime   int64 `json:"endTime" form:"endTime"`
	Status    int   `json:"status" form:"status" binding:"gte=-1,lte=1"`
	//event_caseId
	EventId string `json:"event_id" form:"event_id" binding:"required"`
	//number of reacord's limit on each page
	Limit int `json:"limit" form:"limit"`
	//pagging
	Page int `json:"page" form:"page"`
}

func (s APIEventsGetInputs) collectFilters() string {
	tmp := []string{}
	filterStrTmp := ""
	if s.StartTime != 0 {
		tmp = append(tmp, fmt.Sprintf("timestamp >= FROM_UNIXTIME(%v)", s.StartTime))
	}
	if s.EndTime != 0 {
		tmp = append(tmp, fmt.Sprintf("timestamp <= FROM_UNIXTIME(%v)", s.EndTime))
	}
	if s.EventId != "" {
		tmp = append(tmp, fmt.Sprintf("event_caseId = '%s'", s.EventId))
	}
	if s.Status == 0 || s.Status == 1 {
		tmp = append(tmp, fmt.Sprintf("status = %d", s.Status))
	}
	if len(tmp) != 0 {
		filterStrTmp = strings.Join(tmp, " AND ")
		filterStrTmp = fmt.Sprintf("WHERE %s", filterStrTmp)
	}
	return filterStrTmp
}

func EventsGet(c *gin.Context) {
	var inputs APIEventsGetInputs
	inputs.Status = -1
	if err := c.Bind(&inputs); err != nil {
		h.JSONR(c, badstatus, err)
		return
	}
	filterCollector := inputs.collectFilters()
	//for get correct table name
	f := alm.Events{}
	evens := []alm.Events{}
	if inputs.Limit == 0 || inputs.Limit >= 50 {
		inputs.Limit = 50
	}
	perparedSql := fmt.Sprintf("select id, event_caseId, cond, status, timestamp from %s %s order by timestamp DESC limit %d,%d", f.TableName(), filterCollector, inputs.Page, inputs.Limit)
	db.Alarm.Raw(perparedSql).Scan(&evens)
	h.JSONR(c, evens)
}
<|endoftext|>


<commit_before>package alexa

import (
	"errors"
	"log"
	"math"
	"strconv"
	"time"
)

const sdkVersion = "1.0"
const launchRequestName = "LaunchRequest"
const intentRequestName = "IntentRequest"
const sessionEndedRequestName = "SessionEndedRequest"

var timestampTolerance = 150

// Alexa defines the primary interface to use to create an Alexa request handler.
type Alexa struct {
	ApplicationID   string
	RequestHandler  RequestHandler
	IgnoreTimestamp bool
}

// RequestHandler defines the interface that must be implemented to handle
// Alexa Requests
type RequestHandler interface {
	OnSessionStarted(*Request, *Session, *Response) error
	OnLaunch(*Request, *Session, *Response) error
	OnIntent(*Request, *Session, *Response) error
	OnSessionEnded(*Request, *Session, *Response) error
}

// RequestEnvelope contains the data passed from Alexa to the request handler.
type RequestEnvelope struct {
	Version string   `json:"version"`
	Session *Session `json:"session"`
	Request *Request `json:"request"`
	// TODO Add Request Context
}

// Session containes the session data from the Alexa request.
type Session struct {
	New        bool   `json:"new"`
	SessionID  string `json:"sessionId"`
	Attributes struct {
		String map[string]interface{} `json:"string"`
	} `json:"attributes"`
	User struct {
		UserID      string `json:"userId"`
		AccessToken string `json:"accessToken"`
	} `json:"user"`
	Application struct {
		ApplicationID string `json:"applicationId"`
	} `json:"application"`
}

// Request contines the data in the request within the main request.
type Request struct {
	Locale      string `json:"locale"`
	Timestamp   string `json:"timestamp"`
	Type        string `json:"type"`
	RequestID   string `json:"requestId"`
	DialogState string `json:"dialogState"`
	Intent      Intent `json:"intent"`
	Name        string `json:"name"`
}

// Intent contains the data about the Alexa Intent requested.
type Intent struct {
	Name               string                `json:"name"`
	ConfirmationStatus string                `json:"confirmationStatus,omitempty"`
	Slots              map[string]IntentSlot `json:"slots"`
}

// IntentSlot contains the data for one Slot
type IntentSlot struct {
	Name               string `json:"name"`
	ConfirmationStatus string `json:"confirmationStatus,omitempty"`
	Value              string `json:"value"`
}

// ResponseEnvelope contains the Response and additional attributes.
type ResponseEnvelope struct {
	Version           string                 `json:"version"`
	SessionAttributes map[string]interface{} `json:"sessionAttributes,omitempty"`
	Response          *Response              `json:"response"`
}

// Response contains the body of the response.
type Response struct {
	OutputSpeech     OutputSpeech  `json:"outputSpeech,omitempty"`
	Card             Card          `json:"card,omitempty"`
	Reprompt         Reprompt      `json:"reprompt,omitempty"`
	Directives       []interface{} `json:"directives,omitempty"`
	ShouldSessionEnd bool          `json:"shouldEndSession"`
}

// OutputSpeech contains the data the defines what Alexa should say to the user.
type OutputSpeech struct {
	Type string `json:"type"`
	Text string `json:"text,omitempty"`
	SSML string `json:"ssml,omitempty"`
}

// Card contains the data displayed to the user by the Alexa app.
type Card struct {
	Type    string `json:"type"`
	Title   string `json:"title,omitempty"`
	Content string `json:"content,omitempty"`
	Text    string `json:"text,omitempty"`
	Image   *Image `json:"image,omitempty"`
}

// Image provides URL(s) to the image to display in resposne to the request.
type Image struct {
	SmallImageURL string `json:"smallImageUrl,omitempty"`
	LargeImageURL string `json:"largeImageUrl,omitempty"`
}

// Reprompt contains data about whether Alexa should prompt the user for more data.
type Reprompt struct {
	OutputSpeech OutputSpeech `json:"outputSpeech,omitempty"`
}

// AudioPlayerDirective contains device level instructions on how to handle the response.
type AudioPlayerDirective struct {
	Type         string    `json:"type"`
	PlayBehavior string    `json:"playBehavior,omitempty"`
	AudioItem    AudioItem `json:"audioItem,omitempty"`
}

// AudioItem contains an audio Stream definition for playback.
type AudioItem struct {
	Stream Stream `json:"stream,omitempty"`
}

// Stream contains instructions on playing an audio stream.
type Stream struct {
	Token                string `json:"token"`
	URL                  string `json:"url"`
	OffsetInMilliseconds int    `json:"offsetInMilliseconds"`
}

// DialogDirective contains directives for use in Dialog prompts.
type DialogDirective struct {
	Type          string  `json:"type"`
	SlotToElicit  string  `json:"slotToElicit,omitempty"`
	SlotToConfirm string  `json:"slotToConfirm,omitempty"`
	UpdatedIntent *Intent `json:"updatedIntent,omitempty"`
}

// ProcessRequest handles a request passed from Alexa
func (alexa *Alexa) ProcessRequest(requestEnv *RequestEnvelope) (*ResponseEnvelope, error) {

	err := alexa.verifyApplicationID(requestEnv)
	if err != nil {
		return nil, err
	}
	if !alexa.IgnoreTimestamp {
		err = alexa.verifyTimestamp(requestEnv)
		if err != nil {
			return nil, err
		}
	} else {
		log.Println("Ignoring timestamp verification.")
	}

	request := requestEnv.Request
	session := requestEnv.Session

	responseEnv := &ResponseEnvelope{}
	responseEnv.Version = sdkVersion
	responseEnv.Response = &Response{}
	responseEnv.Response.ShouldSessionEnd = true // Set default value.

	response := responseEnv.Response

	// If it is a new session, invoke onSessionStarted
	if session.New {
		err := alexa.RequestHandler.OnSessionStarted(request, session, response)
		if err != nil {
			log.Println("Error handling OnSessionStarted.", err.Error())
			return nil, err
		}
	}

	switch requestEnv.Request.Type {
	case launchRequestName:
		err := alexa.RequestHandler.OnLaunch(request, session, response)
		if err != nil {
			log.Println("Error handling OnLaunch.", err.Error())
			return nil, err
		}
	case intentRequestName:
		err := alexa.RequestHandler.OnIntent(request, session, response)
		if err != nil {
			log.Println("Error handling OnIntent.", err.Error())
			return nil, err
		}
	case sessionEndedRequestName:
		err := alexa.RequestHandler.OnSessionEnded(request, session, response)
		if err != nil {
			log.Println("Error handling OnSessionEnded.", err.Error())
			return nil, err
		}
	}

	return responseEnv, nil
}

// SetTimestampTolerance sets the maximum number of seconds to allow between
// the current time and the request Timestamp.  Default value is 150 seconds.
func (alexa *Alexa) SetTimestampTolerance(seconds int) {
	timestampTolerance = seconds
}

// SetSimpleCard creates a new simple card with the specified content.
func (r *Response) SetSimpleCard(title string, content string) {
	r.Card = Card{Type: "Simple", Title: title, Content: content}
}

// SetStandardCard creates a new standard card with the specified content.
func (r *Response) SetStandardCard(title string, text string, smallImageURL string, largeImageURL string) {
	r.Card = Card{Type: "Standard", Title: title, Text: text}
	r.Card.Image = &Image{SmallImageURL: smallImageURL, LargeImageURL: largeImageURL}
}

// SetLinkAccountCard creates a new LinkAccount card.
func (r *Response) SetLinkAccountCard() {
	r.Card = Card{Type: "LinkAccount"}
}

// SetOutputText sets the OutputSpeech type to text and sets the value specified.
func (r *Response) SetOutputText(text string) {
	r.OutputSpeech = OutputSpeech{Type: "PlainText", Text: text}
}

// SetOutputSSML sets the OutputSpeech type to ssml and sets the value specified.
func (r *Response) SetOutputSSML(ssml string) {
	r.OutputSpeech = OutputSpeech{Type: "SSML", SSML: ssml}
}

// SetRepromptText created a Reprompt if needed and sets the OutputSpeech type to text and sets the value specified.
func (r *Response) SetRepromptText(text string) {
	r.Reprompt.OutputSpeech = OutputSpeech{Type: "PlainText", Text: text}
}

// SetRepromptSSML created a Reprompt if needed and sets the OutputSpeech type to ssml and sets the value specified.
func (r *Response) SetRepromptSSML(ssml string) {
	r.Reprompt.OutputSpeech = OutputSpeech{Type: "SSML", SSML: ssml}
}

// AddAudioPlayer adds an AudioPlayer directive to the Response.
func (r *Response) AddAudioPlayer(playerType, playBehavior, streamToken, url string, offsetInMilliseconds int) {
	d := AudioPlayerDirective{
		Type:         playerType,
		PlayBehavior: playBehavior,
		AudioItem: AudioItem{
			Stream: Stream{
				Token:                streamToken,
				URL:                  url,
				OffsetInMilliseconds: offsetInMilliseconds,
			},
		},
	}
	r.Directives = append(r.Directives, d)
}

// AddDialogDirective adds a Dialog directive to the Response.
func (r *Response) AddDialogDirective(dialogType, slotToElicit, slotToConfirm string, intent *Intent) {
	d := DialogDirective{
		Type:          dialogType,
		SlotToElicit:  slotToElicit,
		SlotToConfirm: slotToConfirm,
		UpdatedIntent: intent,
	}
	r.Directives = append(r.Directives, d)
}

// verifyApplicationId verifies that the ApplicationID sent in the request
// matches the one configured for this skill.
func (alexa *Alexa) verifyApplicationID(request *RequestEnvelope) error {
	appID := alexa.ApplicationID
	requestAppID := request.Session.Application.ApplicationID
	if appID == "" {
		return errors.New("application ID was set to an empty string")
	}
	if requestAppID == "" {
		return errors.New("request Application ID was set to an empty string")
	}
	if appID != requestAppID {
		return errors.New("request Application ID does not match expected ApplicationId")
	}

	return nil
}

// verifyTimestamp compares the request timestamp to the current timestamp
// and returns an error if they are too far apart.
func (alexa *Alexa) verifyTimestamp(request *RequestEnvelope) error {

	timestamp, err := time.Parse(time.RFC3339, request.Request.Timestamp)
	if err != nil {
		return errors.New("Unable to parse request timestamp.  Err: " + err.Error())
	}
	now := time.Now()
	delta := now.Sub(timestamp)
	deltaSecsAbs := math.Abs(delta.Seconds())
	if deltaSecsAbs > float64(timestampTolerance) {
		return errors.New("Invalid Timestamp. The request timestap " + timestamp.String() + " was off the current time " + now.String() + " by more than " + strconv.FormatInt(int64(timestampTolerance), 10) + " seconds.")
	}

	return nil
}
<commit_msg>Reverted change away from struct pointers. Need pointers to enable 'nil' omitempty behavour.<commit_after>package alexa

import (
	"errors"
	"log"
	"math"
	"strconv"
	"time"
)

const sdkVersion = "1.0"
const launchRequestName = "LaunchRequest"
const intentRequestName = "IntentRequest"
const sessionEndedRequestName = "SessionEndedRequest"

var timestampTolerance = 150

// Alexa defines the primary interface to use to create an Alexa request handler.
type Alexa struct {
	ApplicationID   string
	RequestHandler  RequestHandler
	IgnoreTimestamp bool
}

// RequestHandler defines the interface that must be implemented to handle
// Alexa Requests
type RequestHandler interface {
	OnSessionStarted(*Request, *Session, *Response) error
	OnLaunch(*Request, *Session, *Response) error
	OnIntent(*Request, *Session, *Response) error
	OnSessionEnded(*Request, *Session, *Response) error
}

// RequestEnvelope contains the data passed from Alexa to the request handler.
type RequestEnvelope struct {
	Version string   `json:"version"`
	Session *Session `json:"session"`
	Request *Request `json:"request"`
	// TODO Add Request Context
}

// Session containes the session data from the Alexa request.
type Session struct {
	New        bool   `json:"new"`
	SessionID  string `json:"sessionId"`
	Attributes struct {
		String map[string]interface{} `json:"string"`
	} `json:"attributes"`
	User struct {
		UserID      string `json:"userId"`
		AccessToken string `json:"accessToken"`
	} `json:"user"`
	Application struct {
		ApplicationID string `json:"applicationId"`
	} `json:"application"`
}

// Request contines the data in the request within the main request.
type Request struct {
	Locale      string `json:"locale"`
	Timestamp   string `json:"timestamp"`
	Type        string `json:"type"`
	RequestID   string `json:"requestId"`
	DialogState string `json:"dialogState"`
	Intent      Intent `json:"intent"`
	Name        string `json:"name"`
}

// Intent contains the data about the Alexa Intent requested.
type Intent struct {
	Name               string                `json:"name"`
	ConfirmationStatus string                `json:"confirmationStatus,omitempty"`
	Slots              map[string]IntentSlot `json:"slots"`
}

// IntentSlot contains the data for one Slot
type IntentSlot struct {
	Name               string `json:"name"`
	ConfirmationStatus string `json:"confirmationStatus,omitempty"`
	Value              string `json:"value"`
}

// ResponseEnvelope contains the Response and additional attributes.
type ResponseEnvelope struct {
	Version           string                 `json:"version"`
	SessionAttributes map[string]interface{} `json:"sessionAttributes,omitempty"`
	Response          *Response              `json:"response"`
}

// Response contains the body of the response.
type Response struct {
	OutputSpeech     *OutputSpeech `json:"outputSpeech,omitempty"`
	Card             *Card         `json:"card,omitempty"`
	Reprompt         *Reprompt     `json:"reprompt,omitempty"`
	Directives       []interface{} `json:"directives,omitempty"`
	ShouldSessionEnd bool          `json:"shouldEndSession"`
}

// OutputSpeech contains the data the defines what Alexa should say to the user.
type OutputSpeech struct {
	Type string `json:"type"`
	Text string `json:"text,omitempty"`
	SSML string `json:"ssml,omitempty"`
}

// Card contains the data displayed to the user by the Alexa app.
type Card struct {
	Type    string `json:"type"`
	Title   string `json:"title,omitempty"`
	Content string `json:"content,omitempty"`
	Text    string `json:"text,omitempty"`
	Image   *Image `json:"image,omitempty"`
}

// Image provides URL(s) to the image to display in resposne to the request.
type Image struct {
	SmallImageURL string `json:"smallImageUrl,omitempty"`
	LargeImageURL string `json:"largeImageUrl,omitempty"`
}

// Reprompt contains data about whether Alexa should prompt the user for more data.
type Reprompt struct {
	OutputSpeech *OutputSpeech `json:"outputSpeech,omitempty"`
}

// AudioPlayerDirective contains device level instructions on how to handle the response.
type AudioPlayerDirective struct {
	Type         string     `json:"type"`
	PlayBehavior string     `json:"playBehavior,omitempty"`
	AudioItem    *AudioItem `json:"audioItem,omitempty"`
}

// AudioItem contains an audio Stream definition for playback.
type AudioItem struct {
	Stream Stream `json:"stream,omitempty"`
}

// Stream contains instructions on playing an audio stream.
type Stream struct {
	Token                string `json:"token"`
	URL                  string `json:"url"`
	OffsetInMilliseconds int    `json:"offsetInMilliseconds"`
}

// DialogDirective contains directives for use in Dialog prompts.
type DialogDirective struct {
	Type          string  `json:"type"`
	SlotToElicit  string  `json:"slotToElicit,omitempty"`
	SlotToConfirm string  `json:"slotToConfirm,omitempty"`
	UpdatedIntent *Intent `json:"updatedIntent,omitempty"`
}

// ProcessRequest handles a request passed from Alexa
func (alexa *Alexa) ProcessRequest(requestEnv *RequestEnvelope) (*ResponseEnvelope, error) {

	err := alexa.verifyApplicationID(requestEnv)
	if err != nil {
		return nil, err
	}
	if !alexa.IgnoreTimestamp {
		err = alexa.verifyTimestamp(requestEnv)
		if err != nil {
			return nil, err
		}
	} else {
		log.Println("Ignoring timestamp verification.")
	}

	request := requestEnv.Request
	session := requestEnv.Session

	responseEnv := &ResponseEnvelope{}
	responseEnv.Version = sdkVersion
	responseEnv.Response = &Response{}
	responseEnv.Response.ShouldSessionEnd = true // Set default value.

	response := responseEnv.Response

	// If it is a new session, invoke onSessionStarted
	if session.New {
		err := alexa.RequestHandler.OnSessionStarted(request, session, response)
		if err != nil {
			log.Println("Error handling OnSessionStarted.", err.Error())
			return nil, err
		}
	}

	switch requestEnv.Request.Type {
	case launchRequestName:
		err := alexa.RequestHandler.OnLaunch(request, session, response)
		if err != nil {
			log.Println("Error handling OnLaunch.", err.Error())
			return nil, err
		}
	case intentRequestName:
		err := alexa.RequestHandler.OnIntent(request, session, response)
		if err != nil {
			log.Println("Error handling OnIntent.", err.Error())
			return nil, err
		}
	case sessionEndedRequestName:
		err := alexa.RequestHandler.OnSessionEnded(request, session, response)
		if err != nil {
			log.Println("Error handling OnSessionEnded.", err.Error())
			return nil, err
		}
	}

	return responseEnv, nil
}

// SetTimestampTolerance sets the maximum number of seconds to allow between
// the current time and the request Timestamp.  Default value is 150 seconds.
func (alexa *Alexa) SetTimestampTolerance(seconds int) {
	timestampTolerance = seconds
}

// SetSimpleCard creates a new simple card with the specified content.
func (r *Response) SetSimpleCard(title string, content string) {
	r.Card = &Card{Type: "Simple", Title: title, Content: content}
}

// SetStandardCard creates a new standard card with the specified content.
func (r *Response) SetStandardCard(title string, text string, smallImageURL string, largeImageURL string) {
	r.Card = &Card{Type: "Standard", Title: title, Text: text}
	r.Card.Image = &Image{SmallImageURL: smallImageURL, LargeImageURL: largeImageURL}
}

// SetLinkAccountCard creates a new LinkAccount card.
func (r *Response) SetLinkAccountCard() {
	r.Card = &Card{Type: "LinkAccount"}
}

// SetOutputText sets the OutputSpeech type to text and sets the value specified.
func (r *Response) SetOutputText(text string) {
	r.OutputSpeech = &OutputSpeech{Type: "PlainText", Text: text}
}

// SetOutputSSML sets the OutputSpeech type to ssml and sets the value specified.
func (r *Response) SetOutputSSML(ssml string) {
	r.OutputSpeech = &OutputSpeech{Type: "SSML", SSML: ssml}
}

// SetRepromptText created a Reprompt if needed and sets the OutputSpeech type to text and sets the value specified.
func (r *Response) SetRepromptText(text string) {
	if r.Reprompt == nil {
		r.Reprompt = &Reprompt{}
	}
	r.Reprompt.OutputSpeech = &OutputSpeech{Type: "PlainText", Text: text}
}

// SetRepromptSSML created a Reprompt if needed and sets the OutputSpeech type to ssml and sets the value specified.
func (r *Response) SetRepromptSSML(ssml string) {
	if r.Reprompt == nil {
		r.Reprompt = &Reprompt{}
	}
	r.Reprompt.OutputSpeech = &OutputSpeech{Type: "SSML", SSML: ssml}
}

// AddAudioPlayer adds an AudioPlayer directive to the Response.
func (r *Response) AddAudioPlayer(playerType, playBehavior, streamToken, url string, offsetInMilliseconds int) {
	d := AudioPlayerDirective{
		Type:         playerType,
		PlayBehavior: playBehavior,
		AudioItem: &AudioItem{
			Stream: Stream{
				Token:                streamToken,
				URL:                  url,
				OffsetInMilliseconds: offsetInMilliseconds,
			},
		},
	}
	r.Directives = append(r.Directives, d)
}

// AddDialogDirective adds a Dialog directive to the Response.
func (r *Response) AddDialogDirective(dialogType, slotToElicit, slotToConfirm string, intent *Intent) {
	d := DialogDirective{
		Type:          dialogType,
		SlotToElicit:  slotToElicit,
		SlotToConfirm: slotToConfirm,
		UpdatedIntent: intent,
	}
	r.Directives = append(r.Directives, d)
}

// verifyApplicationId verifies that the ApplicationID sent in the request
// matches the one configured for this skill.
func (alexa *Alexa) verifyApplicationID(request *RequestEnvelope) error {
	appID := alexa.ApplicationID
	requestAppID := request.Session.Application.ApplicationID
	if appID == "" {
		return errors.New("application ID was set to an empty string")
	}
	if requestAppID == "" {
		return errors.New("request Application ID was set to an empty string")
	}
	if appID != requestAppID {
		return errors.New("request Application ID does not match expected ApplicationId")
	}

	return nil
}

// verifyTimestamp compares the request timestamp to the current timestamp
// and returns an error if they are too far apart.
func (alexa *Alexa) verifyTimestamp(request *RequestEnvelope) error {

	timestamp, err := time.Parse(time.RFC3339, request.Request.Timestamp)
	if err != nil {
		return errors.New("Unable to parse request timestamp.  Err: " + err.Error())
	}
	now := time.Now()
	delta := now.Sub(timestamp)
	deltaSecsAbs := math.Abs(delta.Seconds())
	if deltaSecsAbs > float64(timestampTolerance) {
		return errors.New("Invalid Timestamp. The request timestap " + timestamp.String() + " was off the current time " + now.String() + " by more than " + strconv.FormatInt(int64(timestampTolerance), 10) + " seconds.")
	}

	return nil
}
<|endoftext|>


<commit_before>package editor

import (
	"github.com/elpinal/coco3/complete"
	"github.com/elpinal/coco3/config"
	"github.com/elpinal/coco3/screen"
)

type insert struct {
	streamSet
	*editor
	s    screen.Screen
	conf *config.Config

	needSave bool

	replaceMode bool
	replacedBuf []rune
}

func (e *insert) Mode() mode {
	return modeInsert
}

func (e *insert) Run() (end continuity, next mode, err error) {
	next = modeInsert
	if e.replaceMode {
		next = modeReplace
	}
	r, _, err := e.streamSet.in.ReadRune()
	if err != nil {
		return end, next, err
	}
start:
	switch r {
	case CharEscape:
		if e.replaceMode {
			e.buf = e.overwrite(e.replacedBuf, e.buf, e.pos-len(e.buf))
		}
		e.move(e.pos - 1)
		next = modeNormal
		if e.needSave {
			e.undoTree.add(e.buf)
		}
	case CharBackspace, CharCtrlH:
		e.deleteChar()
		e.needSave = true
	case CharCtrlM:
		end = execute
		e.needSave = true
	case CharCtrlX:
		r1, _, err := e.streamSet.in.ReadRune()
		if err != nil {
			return end, next, err
		}
		r2, err := e.ctrlX(r1)
		if err != nil {
			return end, next, err
		}
		r = r2
		goto start
	case CharCtrlW:
		e.deleteWord()
		e.needSave = true
	case CharCtrlU:
		e.deleteToBeginning()
		e.needSave = true
	default:
		e.insert([]rune{r}, e.pos)
		e.needSave = true
	}
	return end, next, err
}

func (e *insert) Runes() []rune {
	if e.replaceMode {
		return e.overwrite(e.replacedBuf, e.editor.buf, e.editor.pos-len(e.buf))
	}
	return e.editor.buf
}

func (e *insert) Position() int {
	return e.editor.pos
}

func (e *insert) Message() []rune {
	if e.replaceMode {
		return []rune("-- REPLACE --")
	}
	return []rune("-- INSERT --")
}

func (e *insert) Highlight() *screen.Hi {
	return nil
}

func (e *insert) ctrlX(r rune) (rune, error) {
	var f func([]rune, int) ([]string, error)
	switch r {
	case CharCtrlF:
		f = complete.File
	default:
		return r, nil
	}

	list, err := f(e.buf, e.pos)
	if err != nil {
		return 0, err
	}
	list = append(list, "")
	e.insert([]rune(list[0]), e.pos)
	e.needSave = true
	n := 0
	for {
		e.s.Refresh(e.conf, false, e.buf, e.pos, e.Highlight())
		r1, _, err := e.streamSet.in.ReadRune()
		if err != nil {
			return 0, err
		}
		n1 := n
		switch r1 {
		case CharCtrlN, r:
			n++
			if len(list) <= n {
				n = 0
			}
		case CharCtrlP:
			n--
			if n < 0 {
				n = len(list) - 1
			}
		case CharCtrlY:
			r2, _, err := e.streamSet.in.ReadRune()
			return r2, err
		case CharCtrlE:
			e.delete(e.pos, e.pos-len(list[n1]))
			e.s.Refresh(e.conf, false, e.buf, e.pos, e.Highlight())
			r2, _, err := e.streamSet.in.ReadRune()
			return r2, err
		default:
			return r1, nil
		}
		e.delete(e.pos, e.pos-len(list[n1]))
		e.insert([]rune(list[n]), e.pos)
	}
}

func (e *insert) deleteChar() {
	if !e.replaceMode {
		e.delete(e.pos-1, e.pos)
		return
	}
	if e.pos == 0 {
		return
	}
	e.pos--
	if len(e.buf) > 0 {
		e.buf = e.buf[:len(e.buf)-1]
	}
}

func (e *insert) deleteWord() {
	if !e.replaceMode {
		pos := e.pos
		e.wordBackward()
		e.delete(pos, e.pos)
		return
	}
	if e.pos == 0 {
		return
	}
	off := e.pos - len(e.buf)
	e.pos -= off
	pos := e.pos
	e.wordBackward()
	if len(e.buf) > 0 {
		e.delete(pos, e.pos)
		e.pos += off
	}
}

func (e *insert) deleteToBeginning() {
	if !e.replaceMode {
		e.delete(0, e.pos)
		return
	}
	if e.pos == 0 {
		return
	}
	off := e.pos - len(e.buf)
	e.pos -= off
	if len(e.buf) > 0 {
		e.delete(0, e.pos)
		e.pos += off
	}
}
<commit_msg>Avoid that i_CTRL-J makes appearance broken<commit_after>package editor

import (
	"github.com/elpinal/coco3/complete"
	"github.com/elpinal/coco3/config"
	"github.com/elpinal/coco3/screen"
)

type insert struct {
	streamSet
	*editor
	s    screen.Screen
	conf *config.Config

	needSave bool

	replaceMode bool
	replacedBuf []rune
}

func (e *insert) Mode() mode {
	return modeInsert
}

func (e *insert) Run() (end continuity, next mode, err error) {
	next = modeInsert
	if e.replaceMode {
		next = modeReplace
	}
	r, _, err := e.streamSet.in.ReadRune()
	if err != nil {
		return end, next, err
	}
start:
	switch r {
	case CharEscape:
		if e.replaceMode {
			e.buf = e.overwrite(e.replacedBuf, e.buf, e.pos-len(e.buf))
		}
		e.move(e.pos - 1)
		next = modeNormal
		if e.needSave {
			e.undoTree.add(e.buf)
		}
	case CharBackspace, CharCtrlH:
		e.deleteChar()
		e.needSave = true
	case CharCtrlM, CharCtrlJ:
		end = execute
		e.needSave = true
	case CharCtrlX:
		r1, _, err := e.streamSet.in.ReadRune()
		if err != nil {
			return end, next, err
		}
		r2, err := e.ctrlX(r1)
		if err != nil {
			return end, next, err
		}
		r = r2
		goto start
	case CharCtrlW:
		e.deleteWord()
		e.needSave = true
	case CharCtrlU:
		e.deleteToBeginning()
		e.needSave = true
	default:
		e.insert([]rune{r}, e.pos)
		e.needSave = true
	}
	return end, next, err
}

func (e *insert) Runes() []rune {
	if e.replaceMode {
		return e.overwrite(e.replacedBuf, e.editor.buf, e.editor.pos-len(e.buf))
	}
	return e.editor.buf
}

func (e *insert) Position() int {
	return e.editor.pos
}

func (e *insert) Message() []rune {
	if e.replaceMode {
		return []rune("-- REPLACE --")
	}
	return []rune("-- INSERT --")
}

func (e *insert) Highlight() *screen.Hi {
	return nil
}

func (e *insert) ctrlX(r rune) (rune, error) {
	var f func([]rune, int) ([]string, error)
	switch r {
	case CharCtrlF:
		f = complete.File
	default:
		return r, nil
	}

	list, err := f(e.buf, e.pos)
	if err != nil {
		return 0, err
	}
	list = append(list, "")
	e.insert([]rune(list[0]), e.pos)
	e.needSave = true
	n := 0
	for {
		e.s.Refresh(e.conf, false, e.buf, e.pos, e.Highlight())
		r1, _, err := e.streamSet.in.ReadRune()
		if err != nil {
			return 0, err
		}
		n1 := n
		switch r1 {
		case CharCtrlN, r:
			n++
			if len(list) <= n {
				n = 0
			}
		case CharCtrlP:
			n--
			if n < 0 {
				n = len(list) - 1
			}
		case CharCtrlY:
			r2, _, err := e.streamSet.in.ReadRune()
			return r2, err
		case CharCtrlE:
			e.delete(e.pos, e.pos-len(list[n1]))
			e.s.Refresh(e.conf, false, e.buf, e.pos, e.Highlight())
			r2, _, err := e.streamSet.in.ReadRune()
			return r2, err
		default:
			return r1, nil
		}
		e.delete(e.pos, e.pos-len(list[n1]))
		e.insert([]rune(list[n]), e.pos)
	}
}

func (e *insert) deleteChar() {
	if !e.replaceMode {
		e.delete(e.pos-1, e.pos)
		return
	}
	if e.pos == 0 {
		return
	}
	e.pos--
	if len(e.buf) > 0 {
		e.buf = e.buf[:len(e.buf)-1]
	}
}

func (e *insert) deleteWord() {
	if !e.replaceMode {
		pos := e.pos
		e.wordBackward()
		e.delete(pos, e.pos)
		return
	}
	if e.pos == 0 {
		return
	}
	off := e.pos - len(e.buf)
	e.pos -= off
	pos := e.pos
	e.wordBackward()
	if len(e.buf) > 0 {
		e.delete(pos, e.pos)
		e.pos += off
	}
}

func (e *insert) deleteToBeginning() {
	if !e.replaceMode {
		e.delete(0, e.pos)
		return
	}
	if e.pos == 0 {
		return
	}
	off := e.pos - len(e.buf)
	e.pos -= off
	if len(e.buf) > 0 {
		e.delete(0, e.pos)
		e.pos += off
	}
}
<|endoftext|>


<commit_before>package log

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/treetopllc/elastilog"
)

type ElasticClient struct {
	Basic
	client elastilog.Client
}

func NewElastic(uri string, tags ...string) *ElasticClient {
	c := &ElasticClient{
		client: elastilog.NewClient(uri, tags...),
	}
	c.Basic = NewBasic(c, "", 0)
	return c
}

func (el ElasticClient) Send(e *ElasticEntry) {
	if e != nil {
		e.entry.Attributes["level"] = string(e.level)
		e.entry.Log = string(e.str)
		el.client.Send(e.entry)
	}
}

func (el ElasticClient) Write(msg []byte) (int, error) {
	hostname, _ := os.Hostname()
	el.client.Send(elastilog.Entry{
		Log:        string(msg),
		Host:       hostname,
		Timestamp:  time.Now(),
		Attributes: elastilog.Attributes{"level": "std"},
	})
	return len(msg), nil
}

type ElasticEntry struct {
	level LogLevel
	str   StringLogger
	entry elastilog.Entry
}

func NewElasticEntry() *ElasticEntry {
	hostname, _ := os.Hostname()
	return &ElasticEntry{
		level: INFO,
		entry: elastilog.Entry{
			Host:       hostname,
			Timestamp:  time.Now(),
			Attributes: make(elastilog.Attributes),
		},
	}
}

func (ee *ElasticEntry) set(key string, value string) {
	ee.entry.Attributes[key] = value
}

func (ee *ElasticEntry) SetRequest(req *http.Request) {
	ee.entry.Host = req.URL.Host
	ee.set("request.method", req.Method)
	ee.set("request.path", req.URL.Path)
	ee.set("request.query", req.URL.RawQuery)
	ee.set("request.proto", req.Proto)
	if req.Body != nil {
		b := new(bytes.Buffer)
		io.Copy(b, req.Body)
		rb := b.String()
		if !debug {
			rb = filterRequest(rb)
		}
		ee.set("request.body", rb+" ")
		req.Body = ioutil.NopCloser(b)
	}
	for k, h := range req.Header {
		ee.set("request.header."+k, strings.Join(h, ","))
	}
}
func (ee *ElasticEntry) SetUserID(id string) {
	ee.set("request.user_id", id)
}
func (ee *ElasticEntry) SetResponse(status int, body interface{}) {
	if debug {
		b, _ := json.Marshal(body)
		ee.set("response.body", fmt.Sprintf("%s ", string(b)))
	}
	ee.set("response.status", fmt.Sprintf("%v", status))
	ee.set("duration", fmt.Sprintf("%v", time.Since(ee.entry.Timestamp).Nanoseconds()/1000000)) //1 ms = 1000000ns
}

func (ee *ElasticEntry) Write(b []byte) (int, error) {
	ee.level.Set(INFO)
	ee.str.Log(string(b))
	return len(b), nil
}
func (ee *ElasticEntry) Log(msg ...interface{}) {
	ee.level.Set(INFO)
	ee.str.Log(msg...)
}
func (ee *ElasticEntry) Logf(msg string, args ...interface{}) {
	ee.level.Set(INFO)
	ee.str.Logf(msg, args...)
}
func (ee *ElasticEntry) Error(err error) {
	ee.level.Set(ERROR)
	ee.str.Error(err)
}
func (ee *ElasticEntry) Errorf(msg string, args ...interface{}) {
	ee.level.Set(ERROR)
	ee.str.Errorf(msg, args...)
}
func (ee *ElasticEntry) Panic(msg interface{}) {
	ee.level.Set(PANIC)
	ee.str.Panic(msg)
}
func (ee *ElasticEntry) Panicf(msg string, args ...interface{}) {
	ee.level.Set(PANIC)
	ee.str.Panicf(msg, args...)
}
<commit_msg>Handle string response.body encoding better for elastic<commit_after>package log

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/treetopllc/elastilog"
)

type ElasticClient struct {
	Basic
	client elastilog.Client
}

func NewElastic(uri string, tags ...string) *ElasticClient {
	c := &ElasticClient{
		client: elastilog.NewClient(uri, tags...),
	}
	c.Basic = NewBasic(c, "", 0)
	return c
}

func (el ElasticClient) Send(e *ElasticEntry) {
	if e != nil {
		e.entry.Attributes["level"] = string(e.level)
		e.entry.Log = string(e.str)
		el.client.Send(e.entry)
	}
}

func (el ElasticClient) Write(msg []byte) (int, error) {
	hostname, _ := os.Hostname()
	el.client.Send(elastilog.Entry{
		Log:        string(msg),
		Host:       hostname,
		Timestamp:  time.Now(),
		Attributes: elastilog.Attributes{"level": "std"},
	})
	return len(msg), nil
}

type ElasticEntry struct {
	level LogLevel
	str   StringLogger
	entry elastilog.Entry
}

func NewElasticEntry() *ElasticEntry {
	hostname, _ := os.Hostname()
	return &ElasticEntry{
		level: INFO,
		entry: elastilog.Entry{
			Host:       hostname,
			Timestamp:  time.Now(),
			Attributes: make(elastilog.Attributes),
		},
	}
}

func (ee *ElasticEntry) set(key string, value string) {
	ee.entry.Attributes[key] = value
}

func (ee *ElasticEntry) SetRequest(req *http.Request) {
	ee.entry.Host = req.URL.Host
	ee.set("request.method", req.Method)
	ee.set("request.path", req.URL.Path)
	ee.set("request.query", req.URL.RawQuery)
	ee.set("request.proto", req.Proto)
	if req.Body != nil {
		b := new(bytes.Buffer)
		io.Copy(b, req.Body)
		rb := b.String()
		if !debug {
			rb = filterRequest(rb)
		}
		ee.set("request.body", rb+" ")
		req.Body = ioutil.NopCloser(b)
	}
	for k, h := range req.Header {
		ee.set("request.header."+k, strings.Join(h, ","))
	}
}
func (ee *ElasticEntry) SetUserID(id string) {
	ee.set("request.user_id", id)
}

func (ee *ElasticEntry) SetResponse(status int, body interface{}) {
	if debug || status >= 300 {
		switch body := body.(type) {
		case string:
			ee.set("response.body", fmt.Sprintf("%s ", body))
		default:
			b, _ := json.Marshal(body)
			ee.set("response.body", fmt.Sprintf("%s ", string(b)))
		}
	}
	ee.set("response.status", fmt.Sprintf("%v", status))
	ee.set("duration", fmt.Sprintf("%v", time.Since(ee.entry.Timestamp).Nanoseconds()/1000000)) //1 ms = 1000000ns
}

func (ee *ElasticEntry) Write(b []byte) (int, error) {
	ee.level.Set(INFO)
	ee.str.Log(string(b))
	return len(b), nil
}
func (ee *ElasticEntry) Log(msg ...interface{}) {
	ee.level.Set(INFO)
	ee.str.Log(msg...)
}
func (ee *ElasticEntry) Logf(msg string, args ...interface{}) {
	ee.level.Set(INFO)
	ee.str.Logf(msg, args...)
}
func (ee *ElasticEntry) Error(err error) {
	ee.level.Set(ERROR)
	ee.str.Error(err)
}
func (ee *ElasticEntry) Errorf(msg string, args ...interface{}) {
	ee.level.Set(ERROR)
	ee.str.Errorf(msg, args...)
}
func (ee *ElasticEntry) Panic(msg interface{}) {
	ee.level.Set(PANIC)
	ee.str.Panic(msg)
}
func (ee *ElasticEntry) Panicf(msg string, args ...interface{}) {
	ee.level.Set(PANIC)
	ee.str.Panicf(msg, args...)
}
<|endoftext|>