<commit_before>package discover

//
//
// TODO:
//   should be able to guess article link format statistically
//   handle/allow subdomains (eg: www1.politicalbetting.com)
//   filter unwanted navlinks (eg "mirror.co.uk/all-about/fred bloggs")
//   HTTP error handling
//   multiple url formats (eg spectator has multiple cms's)
//   logging

import (
	"code.google.com/p/cascadia"
	"code.google.com/p/go.net/html"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"regexp"
	"strings"
)

type Logger interface {
	Printf(format string, v ...interface{})
}

type NullLogger struct{}

func (l NullLogger) Printf(format string, v ...interface{}) {
}

type DiscovererDef struct {
	Name               string
	URL                string
	ArtPat             []string
	NavSel             string
	BaseErrorThreshold int
}

type Discoverer struct {
	Name               string
	StartURL           url.URL
	ArtPats            []*regexp.Regexp
	NavLinkSel         cascadia.Selector
	BaseErrorThreshold int
	StripFragments     bool
	StripQuery         bool

	ErrorLog Logger
	InfoLog  Logger
	Stats    struct {
		ErrorCount int
		FetchCount int
	}
}

func NewDiscoverer(cfg DiscovererDef) (*Discoverer, error) {
	disc := &Discoverer{}
	u, err := url.Parse(cfg.URL)
	if err != nil {
		return nil, err
	}
	disc.Name = cfg.Name
	disc.StartURL = *u
	disc.ArtPats = make([]*regexp.Regexp, 0, len(cfg.ArtPat))
	for _, pat := range cfg.ArtPat {
		re, err := regexp.Compile(pat)
		if err != nil {
			return nil, err
		}
		disc.ArtPats = append(disc.ArtPats, re)
	}

	if cfg.NavSel == "" {
		disc.NavLinkSel = nil
	} else {
		sel, err := cascadia.Compile(cfg.NavSel)
		if err != nil {
			return nil, err
		}
		disc.NavLinkSel = sel
	}
	disc.BaseErrorThreshold = cfg.BaseErrorThreshold

	// defaults
	disc.StripFragments = true
	disc.StripQuery = true
	disc.ErrorLog = NullLogger{}
	disc.InfoLog = NullLogger{}
	return disc, nil
}

func (disc *Discoverer) Run(client *http.Client) (LinkSet, error) {

	queued := make(LinkSet) // nav pages to scan for article links
	seen := make(LinkSet)   // nav pages we've scanned
	arts := make(LinkSet)   // article links we've found so far

	queued.Add(disc.StartURL)

	for len(queued) > 0 {
		u := queued.Pop()
		seen.Add(u)
		//

		root, err := disc.fetchAndParse(client, &u)
		if err != nil {
			disc.ErrorLog.Printf("%s\n", err.Error())
			disc.Stats.ErrorCount++
			if disc.Stats.ErrorCount > disc.BaseErrorThreshold+(disc.Stats.FetchCount/10) {
				return nil, errors.New("Error threshold exceeded")
			} else {
				continue
			}
		}
		disc.Stats.FetchCount++

		navLinks, err := disc.findNavLinks(root)
		if err != nil {
			return nil, err
		}
		for navLink, _ := range navLinks {
			if _, got := seen[navLink]; !got {
				queued.Add(navLink)
			}
		}

		foo, err := disc.findArticles(root)
		if err != nil {
			return nil, err
		}
		arts.Merge(foo)

		disc.InfoLog.Printf("Visited %s, found %d articles\n", u.String(), len(foo))
	}

	return arts, nil
}

func (disc *Discoverer) fetchAndParse(client *http.Client, pageURL *url.URL) (*html.Node, error) {
	resp, err := client.Get(pageURL.String())
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		err = errors.New(fmt.Sprintf("HTTP code %d (%s)", resp.StatusCode, pageURL.String()))

		return nil, err

	}

	root, err := html.Parse(resp.Body)
	if err != nil {
		return nil, err
	}

	return root, nil
}

var aSel cascadia.Selector = cascadia.MustCompile("a")

func (disc *Discoverer) findArticles(root *html.Node) (LinkSet, error) {
	arts := make(LinkSet)
	for _, a := range aSel.MatchAll(root) {
		// fetch url and extend to absolute
		link, err := disc.StartURL.Parse(GetAttr(a, "href"))
		if err != nil {
			continue
		}

		if link.Host != disc.StartURL.Host {
			continue
		}

		foo := link.RequestURI()
		accept := false
		for _, pat := range disc.ArtPats {
			if pat.MatchString(foo) {
				accept = true
				break
			}
		}
		if !accept {
			continue
		}

		if disc.StripFragments {
			link.Fragment = ""
		}
		if disc.StripQuery {
			link.RawQuery = ""
		}

		arts[*link] = true
	}
	return arts, nil
}

func (disc *Discoverer) findNavLinks(root *html.Node) (LinkSet, error) {
	navLinks := make(LinkSet)
	if disc.NavLinkSel == nil {
		return navLinks, nil
	}
	for _, a := range disc.NavLinkSel.MatchAll(root) {
		link, err := disc.StartURL.Parse(GetAttr(a, "href"))
		if err != nil {
			continue
		}
		if link.Host != disc.StartURL.Host {
			continue
		}

		link.Fragment = ""

		navLinks[*link] = true
	}
	return navLinks, nil
}

// GetAttr retrieved the value of an attribute on a node.
// Returns empty string if attribute doesn't exist.
func GetAttr(n *html.Node, attr string) string {
	for _, a := range n.Attr {
		if a.Key == attr {
			return a.Val
		}
	}
	return ""
}

// GetTextContent recursively fetches the text for a node
func GetTextContent(n *html.Node) string {
	if n.Type == html.TextNode {
		return n.Data
	}
	txt := ""
	for child := n.FirstChild; child != nil; child = child.NextSibling {
		txt += GetTextContent(child)
	}

	return txt
}

// CompressSpace reduces all whitespace sequences (space, tabs, newlines etc) in a string to a single space.
// Leading/trailing space is trimmed.
// Has the effect of converting multiline strings to one line.
func CompressSpace(s string) string {
	multispacePat := regexp.MustCompile(`[\s]+`)
	s = strings.TrimSpace(multispacePat.ReplaceAllLiteralString(s, " "))
	return s
}
<commit_msg>add HostPat option to allow multiple domains<commit_after>package discover

//
//
// TODO:
//   should be able to guess article link format statistically
//   handle/allow subdomains (eg: www1.politicalbetting.com)
//   filter unwanted navlinks (eg "mirror.co.uk/all-about/fred bloggs")
//   HTTP error handling
//   multiple url formats (eg spectator has multiple cms's)
//   logging

import (
	"code.google.com/p/cascadia"
	"code.google.com/p/go.net/html"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"regexp"
	"strings"
)

type Logger interface {
	Printf(format string, v ...interface{})
}

type NullLogger struct{}

func (l NullLogger) Printf(format string, v ...interface{}) {
}

type DiscovererDef struct {
	Name   string
	URL    string
	ArtPat []string
	NavSel string
	// BaseErrorThreshold is starting number of http errors to accept before
	// bailing out.
	// error threshold formula: base + 10% of successful request count
	BaseErrorThreshold int

	// Hostpat is a regex matching accepted domains
	// if empty, reject everything on a different domain
	HostPat string
}

type Discoverer struct {
	Name               string
	StartURL           url.URL
	ArtPats            []*regexp.Regexp
	NavLinkSel         cascadia.Selector
	BaseErrorThreshold int
	StripFragments     bool
	StripQuery         bool
	HostPat            *regexp.Regexp

	ErrorLog Logger
	InfoLog  Logger
	Stats    struct {
		ErrorCount int
		FetchCount int
	}
}

func NewDiscoverer(cfg DiscovererDef) (*Discoverer, error) {
	disc := &Discoverer{}
	u, err := url.Parse(cfg.URL)
	if err != nil {
		return nil, err
	}
	disc.Name = cfg.Name
	disc.StartURL = *u
	disc.ArtPats = make([]*regexp.Regexp, 0, len(cfg.ArtPat))
	for _, pat := range cfg.ArtPat {
		re, err := regexp.Compile(pat)
		if err != nil {
			return nil, err
		}
		disc.ArtPats = append(disc.ArtPats, re)
	}

	if cfg.NavSel == "" {
		disc.NavLinkSel = nil
	} else {
		sel, err := cascadia.Compile(cfg.NavSel)
		if err != nil {
			return nil, err
		}
		disc.NavLinkSel = sel
	}
	disc.BaseErrorThreshold = cfg.BaseErrorThreshold

	if cfg.HostPat != "" {
		re, err := regexp.Compile(cfg.HostPat)
		if err != nil {
			return nil, err
		}
		disc.HostPat = re
	}

	// defaults
	disc.StripFragments = true
	disc.StripQuery = true
	disc.ErrorLog = NullLogger{}
	disc.InfoLog = NullLogger{}
	return disc, nil
}

func (disc *Discoverer) Run(client *http.Client) (LinkSet, error) {

	queued := make(LinkSet) // nav pages to scan for article links
	seen := make(LinkSet)   // nav pages we've scanned
	arts := make(LinkSet)   // article links we've found so far

	queued.Add(disc.StartURL)

	for len(queued) > 0 {
		u := queued.Pop()
		seen.Add(u)
		//

		root, err := disc.fetchAndParse(client, &u)
		if err != nil {
			disc.ErrorLog.Printf("%s\n", err.Error())
			disc.Stats.ErrorCount++
			if disc.Stats.ErrorCount > disc.BaseErrorThreshold+(disc.Stats.FetchCount/10) {
				return nil, errors.New("Error threshold exceeded")
			} else {
				continue
			}
		}
		disc.Stats.FetchCount++

		navLinks, err := disc.findNavLinks(root)
		if err != nil {
			return nil, err
		}
		for navLink, _ := range navLinks {
			if _, got := seen[navLink]; !got {
				queued.Add(navLink)
			}
		}

		foo, err := disc.findArticles(root)
		if err != nil {
			return nil, err
		}
		arts.Merge(foo)

		disc.InfoLog.Printf("Visited %s, found %d articles\n", u.String(), len(foo))
	}

	return arts, nil
}

func (disc *Discoverer) fetchAndParse(client *http.Client, pageURL *url.URL) (*html.Node, error) {
	resp, err := client.Get(pageURL.String())
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		err = errors.New(fmt.Sprintf("HTTP code %d (%s)", resp.StatusCode, pageURL.String()))

		return nil, err

	}

	root, err := html.Parse(resp.Body)
	if err != nil {
		return nil, err
	}

	return root, nil
}

var aSel cascadia.Selector = cascadia.MustCompile("a")

func (disc *Discoverer) findArticles(root *html.Node) (LinkSet, error) {
	arts := make(LinkSet)
	for _, a := range aSel.MatchAll(root) {
		// fetch url and extend to absolute
		link, err := disc.StartURL.Parse(GetAttr(a, "href"))
		if err != nil {
			continue
		}

		if !disc.isHostGood(link.Host) {
			continue
		}

		foo := link.RequestURI()
		accept := false
		for _, pat := range disc.ArtPats {
			if pat.MatchString(foo) {
				accept = true
				break
			}
		}
		if !accept {
			continue
		}

		if disc.StripFragments {
			link.Fragment = ""
		}
		if disc.StripQuery {
			link.RawQuery = ""
		}

		arts[*link] = true
	}
	return arts, nil
}

func (disc *Discoverer) findNavLinks(root *html.Node) (LinkSet, error) {
	navLinks := make(LinkSet)
	if disc.NavLinkSel == nil {
		return navLinks, nil
	}
	for _, a := range disc.NavLinkSel.MatchAll(root) {
		link, err := disc.StartURL.Parse(GetAttr(a, "href"))
		if err != nil {
			continue
		}

		if !disc.isHostGood(link.Host) {
			continue
		}

		link.Fragment = ""

		navLinks[*link] = true
	}
	return navLinks, nil
}

// is host domain one we'll accept?
func (disc *Discoverer) isHostGood(host string) bool {
	if disc.HostPat != nil {
		return disc.HostPat.MatchString(host)
	}
	return host == disc.StartURL.Host
}

// GetAttr retrieved the value of an attribute on a node.
// Returns empty string if attribute doesn't exist.
func GetAttr(n *html.Node, attr string) string {
	for _, a := range n.Attr {
		if a.Key == attr {
			return a.Val
		}
	}
	return ""
}

// GetTextContent recursively fetches the text for a node
func GetTextContent(n *html.Node) string {
	if n.Type == html.TextNode {
		return n.Data
	}
	txt := ""
	for child := n.FirstChild; child != nil; child = child.NextSibling {
		txt += GetTextContent(child)
	}

	return txt
}

// CompressSpace reduces all whitespace sequences (space, tabs, newlines etc) in a string to a single space.
// Leading/trailing space is trimmed.
// Has the effect of converting multiline strings to one line.
func CompressSpace(s string) string {
	multispacePat := regexp.MustCompile(`[\s]+`)
	s = strings.TrimSpace(multispacePat.ReplaceAllLiteralString(s, " "))
	return s
}
<|endoftext|>


<commit_before>// The RS232 package lets you access old serial junk from go
package rs232

/*
#include <stdlib.h>
#include <fcntl.h>
#include <termios.h>
*/
import "C"

import (
	"os"
	"syscall"
)

// This is your serial port handle.
type SerialPort struct {
	port *os.File
}

func baudConversion(rate int) (flag _Ctype_speed_t) {
	if C.B9600 != 9600 {
		panic("Baud rates may not map directly.")
	}
	return _Ctype_speed_t(rate)
}

// SerConf represents the basic serial configuration to provide to OpenPort.
type SerConf int

const (
	S_8N1 = iota
	S_7E1
	S_7O1
)

// Opens and returns a non-blocking serial port.
// The device, baud rate, and SerConf is specified.
//
// Example:  rs232.OpenPort("/dev/ttyS0", 115200, rs232.S_8N1)
func OpenPort(port string, baudRate int, serconf SerConf) (rv SerialPort, err error) {
	f, open_err := os.OpenFile(port,
		os.O_RDWR|os.O_NOCTTY|os.O_NDELAY,
		0666)
	if open_err != nil {
		err = open_err
		return
	}
	rv.port = f

	fd := rv.port.Fd()

	var options C.struct_termios
	if C.tcgetattr(C.int(fd), &options) < 0 {
		panic("tcgetattr failed")
	}

	if C.cfsetispeed(&options, baudConversion(baudRate)) < 0 {
		panic("cfsetispeed failed")
	}
	if C.cfsetospeed(&options, baudConversion(baudRate)) < 0 {
		panic("cfsetospeed failed")
	}
	switch serconf {
	case S_8N1:
		{
			options.c_cflag &^= C.PARENB
			options.c_cflag &^= C.CSTOPB
			options.c_cflag &^= C.CSIZE
			options.c_cflag |= C.CS8
		}
	case S_7E1:
		{
			options.c_cflag |= C.PARENB
			options.c_cflag &^= C.PARODD
			options.c_cflag &^= C.CSTOPB
			options.c_cflag &^= C.CSIZE
			options.c_cflag |= C.CS7
		}
	case S_7O1:
		{
			options.c_cflag |= C.PARENB
			options.c_cflag |= C.PARODD
			options.c_cflag &^= C.CSTOPB
			options.c_cflag &^= C.CSIZE
			options.c_cflag |= C.CS7
		}
	}
	// Local
	options.c_cflag |= (C.CLOCAL | C.CREAD)
	// no hardware flow control
	options.c_cflag &^= C.CRTSCTS

	if C.tcsetattr(C.int(fd), C.TCSANOW, &options) < 0 {
		panic("tcsetattr failed")
	}

	if syscall.SetNonblock(fd, false) != nil {
		panic("Error disabling blocking")
	}

	return
}

// Read from the port.
func (port *SerialPort) Read(p []byte) (n int, err error) {
	return port.port.Read(p)
}

// Write to the port.
func (port *SerialPort) Write(p []byte) (n int, err error) {
	return port.port.Write(p)
}

// Close the port.
func (port *SerialPort) Close() error {
	return port.port.Close()
}
<commit_msg>Updates for the next weekly.<commit_after>// The RS232 package lets you access old serial junk from go
package rs232

/*
#include <stdlib.h>
#include <fcntl.h>
#include <termios.h>
*/
import "C"

import (
	"os"
	"syscall"
)

// This is your serial port handle.
type SerialPort struct {
	port *os.File
}

func baudConversion(rate int) (flag _Ctype_speed_t) {
	if C.B9600 != 9600 {
		panic("Baud rates may not map directly.")
	}
	return _Ctype_speed_t(rate)
}

// SerConf represents the basic serial configuration to provide to OpenPort.
type SerConf int

const (
	S_8N1 = iota
	S_7E1
	S_7O1
)

// Opens and returns a non-blocking serial port.
// The device, baud rate, and SerConf is specified.
//
// Example:  rs232.OpenPort("/dev/ttyS0", 115200, rs232.S_8N1)
func OpenPort(port string, baudRate int, serconf SerConf) (rv SerialPort, err error) {
	f, open_err := os.OpenFile(port,
		syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NDELAY,
		0666)
	if open_err != nil {
		err = open_err
		return
	}
	rv.port = f

	fd := rv.port.Fd()

	var options C.struct_termios
	if C.tcgetattr(C.int(fd), &options) < 0 {
		panic("tcgetattr failed")
	}

	if C.cfsetispeed(&options, baudConversion(baudRate)) < 0 {
		panic("cfsetispeed failed")
	}
	if C.cfsetospeed(&options, baudConversion(baudRate)) < 0 {
		panic("cfsetospeed failed")
	}
	switch serconf {
	case S_8N1:
		{
			options.c_cflag &^= C.PARENB
			options.c_cflag &^= C.CSTOPB
			options.c_cflag &^= C.CSIZE
			options.c_cflag |= C.CS8
		}
	case S_7E1:
		{
			options.c_cflag |= C.PARENB
			options.c_cflag &^= C.PARODD
			options.c_cflag &^= C.CSTOPB
			options.c_cflag &^= C.CSIZE
			options.c_cflag |= C.CS7
		}
	case S_7O1:
		{
			options.c_cflag |= C.PARENB
			options.c_cflag |= C.PARODD
			options.c_cflag &^= C.CSTOPB
			options.c_cflag &^= C.CSIZE
			options.c_cflag |= C.CS7
		}
	}
	// Local
	options.c_cflag |= (C.CLOCAL | C.CREAD)
	// no hardware flow control
	options.c_cflag &^= C.CRTSCTS

	if C.tcsetattr(C.int(fd), C.TCSANOW, &options) < 0 {
		panic("tcsetattr failed")
	}

	if syscall.SetNonblock(int(fd), false) != nil {
		panic("Error disabling blocking")
	}

	return
}

// Read from the port.
func (port *SerialPort) Read(p []byte) (n int, err error) {
	return port.port.Read(p)
}

// Write to the port.
func (port *SerialPort) Write(p []byte) (n int, err error) {
	return port.port.Write(p)
}

// Close the port.
func (port *SerialPort) Close() error {
	return port.port.Close()
}
<|endoftext|>


<commit_before>package main

import "github.com/justone/pmb/api"

type IntroducerCommand struct {
	Name string `short:"n" long:"name" description:"Name of this introducer." default:"autogenerated"`
}

var introducerCommand IntroducerCommand

func (x *IntroducerCommand) Execute(args []string) error {
	bus := pmb.GetPMB(urisFromOpts(globalOptions))

	conn, err := bus.GetConnection("introducer")
	if err != nil {
		return err
	}

	introConn, err := bus.GetIntroConnection("introducer")
	if err != nil {
		return err
	}

	return runIntroducer(bus, conn, introConn)
}

func init() {
	parser.AddCommand("introducer",
		"Run an introducer.",
		"",
		&introducerCommand)
}

func runIntroducer(bus *pmb.PMB, conn *pmb.Connection, introConn *pmb.Connection) error {
	for {
		select {
		case message := <-conn.In:
			if message.Contents["type"].(string) == "CopyData" {
				copyToClipboard(message.Contents["data"].(string))

				data := map[string]interface{}{
					"type":   "DataCopied",
					"origin": message.Contents["id"].(string),
				}
				conn.Out <- pmb.Message{Contents: data}
			}

		case message := <-introConn.In:
			if message.Contents["type"].(string) == "RequestAuth" {
				// copy primary uri to clipboard
				copyToClipboard(bus.PrimaryURI())
			}

			// any other message type is an error and ignored
		}
	}

	return nil
}
<commit_msg>use specified name for introducer<commit_after>package main

import "github.com/justone/pmb/api"

type IntroducerCommand struct {
	Name string `short:"n" long:"name" description:"Name of this introducer." default:"introducer"`
}

var introducerCommand IntroducerCommand

func (x *IntroducerCommand) Execute(args []string) error {
	bus := pmb.GetPMB(urisFromOpts(globalOptions))

	conn, err := bus.GetConnection(introducerCommand.Name)
	if err != nil {
		return err
	}

	introConn, err := bus.GetIntroConnection("introducer")
	if err != nil {
		return err
	}

	return runIntroducer(bus, conn, introConn)
}

func init() {
	parser.AddCommand("introducer",
		"Run an introducer.",
		"",
		&introducerCommand)
}

func runIntroducer(bus *pmb.PMB, conn *pmb.Connection, introConn *pmb.Connection) error {
	for {
		select {
		case message := <-conn.In:
			if message.Contents["type"].(string) == "CopyData" {
				copyToClipboard(message.Contents["data"].(string))

				data := map[string]interface{}{
					"type":   "DataCopied",
					"origin": message.Contents["id"].(string),
				}
				conn.Out <- pmb.Message{Contents: data}
			}

		case message := <-introConn.In:
			if message.Contents["type"].(string) == "RequestAuth" {
				// copy primary uri to clipboard
				copyToClipboard(bus.PrimaryURI())
			}

			// any other message type is an error and ignored
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package iocap

import (
	"bytes"
	"crypto/rand"
	"fmt"
	"sync"
	"testing"
	"time"
)

func TestReader(t *testing.T) {
	// Create some random data for our reader.
	data := make([]byte, 512)
	if _, err := rand.Read(data); err != nil {
		t.Fatalf("err: %v", err)
	}
	buf := bytes.NewBuffer(data)

	// Create the Reader with a rate limit applied.
	r := NewReader(buf, RateOpts{Interval: 100 * time.Millisecond, Size: 128})
	out := make([]byte, 512)

	// Record the start time and execute the read.
	start := time.Now()
	n, err := r.Read(out)

	// Check that we actually rate limited the read. 300ms because
	// initially we can read 128 bytes, then 3 bucket drains block
	// at 100ms a pop.
	if d := time.Since(start); d < 300*time.Millisecond {
		t.Fatalf("read returned to quickly in %s", d)
	}

	// Check the return values and data.
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if n != 512 {
		t.Fatalf("expect 512, got: %d", n)
	}
	if !bytes.Equal(data, out) {
		t.Fatal("unexpected data read")
	}
}

func TestWriter(t *testing.T) {
	// Create some random data to write.
	data := make([]byte, 512)
	if _, err := rand.Read(data); err != nil {
		t.Fatalf("err: %v", err)
	}

	// Create the writer with an applied rate limit.
	buf := new(bytes.Buffer)
	w := NewWriter(buf, RateOpts{Interval: 100 * time.Millisecond, Size: 128})

	// Record the start time and perform the write.
	start := time.Now()
	n, err := w.Write(data)

	// Check that we rate limited the write.
	if d := time.Since(start); d < 300*time.Millisecond {
		t.Fatalf("write returned too quickly in %s", d)
	}

	// Check errors and data values.
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if n != 512 {
		t.Fatalf("expect 512, got: %d", n)
	}
	if !bytes.Equal(buf.Bytes(), data) {
		t.Fatal("unexpected data written")
	}
}

func TestGroup(t *testing.T) {
	// Create the rate limiting group.
	g := NewGroup(RateOpts{Interval: 100 * time.Millisecond, Size: 8})

	// Create two buffers; one for the reader, one for the writer. The
	// idea is that these are completely separate buffers, maybe completely
	// unrelated in terms of the operation being performed on them.
	// Regardless, we want to ensure that operations across both share the
	// rate limit of the group.
	bufW := new(bytes.Buffer)
	bufR := new(bytes.Buffer)

	// Set up the data for the reader/writer
	in := []byte("hello world!")
	bufR.Write(in)
	out := make([]byte, len(in))

	// Create a reader and writer in the group against the buffers.
	w := g.NewWriter(bufW)
	r := g.NewReader(bufR)

	// Mark the start time and perform the read and write in parallel.
	start := time.Now()

	var wg sync.WaitGroup
	wg.Add(2)

	go func() {
		defer wg.Done()

		if _, err := w.Write(in); err != nil {
			t.Fatalf("err: %v", err)
		}
	}()

	go func() {
		defer wg.Done()

		if _, err := r.Read(out); err != nil {
			t.Fatalf("err: %v", err)
		}
	}()

	// Wait for both operations to finish.
	wg.Wait()

	// Make sure we blocked for at least 2 intervals. If each operation
	// had its own 8B/s limit, we would finish in ~100ms, but because we
	// are sharing the limit, pushing 24 bytes through is going to require
	// two bucket drains before it completes.
	if d := time.Since(start); d < 200*time.Millisecond {
		t.Fatalf("finished too quickly in %s", d)
	}
}

func TestPerSecond(t *testing.T) {
	ro := PerSecond(128)
	if ro.Interval != time.Second {
		t.Fatalf("expect 1s, got: %s", ro.Interval)
	}
	if ro.Size != 128 {
		t.Fatalf("expect 128, got: %d", ro.Size)
	}
}

func ExampleReader() {
	// Create a buffer to read from.
	buf := bytes.NewBufferString("hello world!")

	// Create the rate limited reader.
	rate := PerSecond(8) // 8B/s
	r := NewReader(buf, rate)

	// Read from the reader.
	out := make([]byte, buf.Len())
	n, err := r.Read(out)
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println(n, string(out))
	// Output: 12 hello world!
}

func ExampleWriter() {
	// Create the buffer to write to.
	buf := new(bytes.Buffer)

	// Create the rate limited writer.
	rate := PerSecond(8) // 8B/s
	r := NewWriter(buf, rate)

	// Write data into the writer.
	n, err := r.Write([]byte("hello world!"))
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println(n, buf.String())
	// Output: 12 hello world!
}
<commit_msg>Update examples<commit_after>package iocap

import (
	"bytes"
	"crypto/rand"
	"fmt"
	"sync"
	"testing"
	"time"
)

func TestReader(t *testing.T) {
	// Create some random data for our reader.
	data := make([]byte, 512)
	if _, err := rand.Read(data); err != nil {
		t.Fatalf("err: %v", err)
	}
	buf := bytes.NewBuffer(data)

	// Create the Reader with a rate limit applied.
	r := NewReader(buf, RateOpts{Interval: 100 * time.Millisecond, Size: 128})
	out := make([]byte, 512)

	// Record the start time and execute the read.
	start := time.Now()
	n, err := r.Read(out)

	// Check that we actually rate limited the read. 300ms because
	// initially we can read 128 bytes, then 3 bucket drains block
	// at 100ms a pop.
	if d := time.Since(start); d < 300*time.Millisecond {
		t.Fatalf("read returned to quickly in %s", d)
	}

	// Check the return values and data.
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if n != 512 {
		t.Fatalf("expect 512, got: %d", n)
	}
	if !bytes.Equal(data, out) {
		t.Fatal("unexpected data read")
	}
}

func TestWriter(t *testing.T) {
	// Create some random data to write.
	data := make([]byte, 512)
	if _, err := rand.Read(data); err != nil {
		t.Fatalf("err: %v", err)
	}

	// Create the writer with an applied rate limit.
	buf := new(bytes.Buffer)
	w := NewWriter(buf, RateOpts{Interval: 100 * time.Millisecond, Size: 128})

	// Record the start time and perform the write.
	start := time.Now()
	n, err := w.Write(data)

	// Check that we rate limited the write.
	if d := time.Since(start); d < 300*time.Millisecond {
		t.Fatalf("write returned too quickly in %s", d)
	}

	// Check errors and data values.
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if n != 512 {
		t.Fatalf("expect 512, got: %d", n)
	}
	if !bytes.Equal(buf.Bytes(), data) {
		t.Fatal("unexpected data written")
	}
}

func TestGroup(t *testing.T) {
	// Create the rate limiting group.
	g := NewGroup(RateOpts{Interval: 100 * time.Millisecond, Size: 8})

	// Create two buffers; one for the reader, one for the writer. The
	// idea is that these are completely separate buffers, maybe completely
	// unrelated in terms of the operation being performed on them.
	// Regardless, we want to ensure that operations across both share the
	// rate limit of the group.
	bufW := new(bytes.Buffer)
	bufR := new(bytes.Buffer)

	// Set up the data for the reader/writer
	in := []byte("hello world!")
	bufR.Write(in)
	out := make([]byte, len(in))

	// Create a reader and writer in the group against the buffers.
	w := g.NewWriter(bufW)
	r := g.NewReader(bufR)

	// Mark the start time and perform the read and write in parallel.
	start := time.Now()

	var wg sync.WaitGroup
	wg.Add(2)

	go func() {
		defer wg.Done()

		if _, err := w.Write(in); err != nil {
			t.Fatalf("err: %v", err)
		}
	}()

	go func() {
		defer wg.Done()

		if _, err := r.Read(out); err != nil {
			t.Fatalf("err: %v", err)
		}
	}()

	// Wait for both operations to finish.
	wg.Wait()

	// Make sure we blocked for at least 2 intervals. If each operation
	// had its own 8B/s limit, we would finish in ~100ms, but because we
	// are sharing the limit, pushing 24 bytes through is going to require
	// two bucket drains before it completes.
	if d := time.Since(start); d < 200*time.Millisecond {
		t.Fatalf("finished too quickly in %s", d)
	}
}

func TestPerSecond(t *testing.T) {
	ro := PerSecond(128)
	if ro.Interval != time.Second {
		t.Fatalf("expect 1s, got: %s", ro.Interval)
	}
	if ro.Size != 128 {
		t.Fatalf("expect 128, got: %d", ro.Size)
	}
}

func ExampleReader() {
	// Create a buffer to read from.
	buf := bytes.NewBufferString("hello world!")

	// Create the rate limited reader.
	rate := PerSecond(128 * 1024) // 128K/s
	r := NewReader(buf, rate)

	// Read from the reader.
	out := make([]byte, buf.Len())
	n, err := r.Read(out)
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println(n, string(out))
	// Output: 12 hello world!
}

func ExampleWriter() {
	// Create the buffer to write to.
	buf := new(bytes.Buffer)

	// Create the rate limited writer.
	rate := PerSecond(128 * 1024) // 128K/s
	r := NewWriter(buf, rate)

	// Write data into the writer.
	n, err := r.Write([]byte("hello world!"))
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println(n, buf.String())
	// Output: 12 hello world!
}

func ExampleGroup() {
	// Create a rate limiting group.
	rate := PerSecond(128 * 1024) // 128K/s
	g := NewGroup(rate)

	// Create a new reader and writer on the group.
	buf := new(bytes.Buffer)
	r := g.NewReader(buf)
	w := g.NewWriter(buf)

	// Reader and writer are rate limited together
	n, err := w.Write([]byte("hello world!"))
	if err != nil {
		fmt.Println(err)
		return
	}
	out := make([]byte, n)
	_, err = r.Read(out)
	if err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println(string(out))
	// Output: hello world!
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"github.com/hanwen/termite/termite"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
)

func main() {
	home := os.Getenv("HOME")
	cachedir := flag.String("cachedir",
		filepath.Join(home, ".cache", "termite-master"), "content cache")
	workers := flag.String("workers", "", "comma separated list of worker addresses")
	coordinator := flag.String("coordinator", "localhost:1233",
		"address of coordinator. Overrides -workers")
	socket := flag.String("socket", ".termite-socket", "socket to listen for commands")
	exclude := flag.String("exclude", "sys,proc,dev,selinux,cgroup", "prefixes to not export.")
	secretFile := flag.String("secret", "secret.txt", "file containing password.")
	srcRoot := flag.String("sourcedir", "", "root of corresponding source directory")
	jobs := flag.Int("jobs", 1, "number of jobs to run")
	port := flag.Int("port", 1237, "http status port")
	houseHoldPeriod := flag.Float64("time.household", 60.0, "how often to do house hold tasks.")
	keepAlive := flag.Float64("time.keepalive", 60.0, "for how long to keep workers reserved.")

	flag.Parse()

	log.SetPrefix("M")

	secret, err := ioutil.ReadFile(*secretFile)
	if err != nil {
		log.Fatal("ReadFile", err)
	}

	workerList := strings.Split(*workers, ",")
	excludeList := strings.Split(*exclude, ",")
	c := termite.NewContentCache(*cachedir)
	master := termite.NewMaster(
		c, *coordinator, workerList, secret, excludeList, *jobs)
	master.SetSrcRoot(*srcRoot)
	master.SetKeepAlive(*keepAlive, *houseHoldPeriod)

	log.Println(termite.Version())

	go master.ServeHTTP(*port)
	master.Start(*socket)
}
<commit_msg>Add usr/lib/locale-archive to the exclusion list.<commit_after>package main

import (
	"flag"
	"github.com/hanwen/termite/termite"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
)

func main() {
	home := os.Getenv("HOME")
	cachedir := flag.String("cachedir",
		filepath.Join(home, ".cache", "termite-master"), "content cache")
	workers := flag.String("workers", "", "comma separated list of worker addresses")
	coordinator := flag.String("coordinator", "localhost:1233",
		"address of coordinator. Overrides -workers")
	socket := flag.String("socket", ".termite-socket", "socket to listen for commands")
	exclude := flag.String("exclude",
		"usr/lib/locale/locale-archive,sys,proc,dev,selinux,cgroup", "prefixes to not export.")
	secretFile := flag.String("secret", "secret.txt", "file containing password.")
	srcRoot := flag.String("sourcedir", "", "root of corresponding source directory")
	jobs := flag.Int("jobs", 1, "number of jobs to run")
	port := flag.Int("port", 1237, "http status port")
	houseHoldPeriod := flag.Float64("time.household", 60.0, "how often to do house hold tasks.")
	keepAlive := flag.Float64("time.keepalive", 60.0, "for how long to keep workers reserved.")

	flag.Parse()

	log.SetPrefix("M")

	secret, err := ioutil.ReadFile(*secretFile)
	if err != nil {
		log.Fatal("ReadFile", err)
	}

	workerList := strings.Split(*workers, ",")
	excludeList := strings.Split(*exclude, ",")
	c := termite.NewContentCache(*cachedir)
	master := termite.NewMaster(
		c, *coordinator, workerList, secret, excludeList, *jobs)
	master.SetSrcRoot(*srcRoot)
	master.SetKeepAlive(*keepAlive, *houseHoldPeriod)

	log.Println(termite.Version())

	go master.ServeHTTP(*port)
	master.Start(*socket)
}
<|endoftext|>


<commit_before>package resource

import (
	"fmt"
	"io"

	"github.com/hashicorp/hcl/hcl/ast"
)

// Registry contains all known resources
var Registry = make(map[string]RegistryItem)

// provider is used to create new resources from an HCL AST object item
type provider func(title string, item *ast.ObjectItem) (Resource, error)

// RegistryItem type represents an item from the registry
type RegistryItem struct {
	// Name of the resource type
	Name string

	// Short desription of the resource
	Description string

	// Resource provider
	Provider provider
}

// Register adds a resource type to the registry
func Register(item RegistryItem) error {
	_, ok := Registry[item.Name]
	if ok {
		return fmt.Errorf("Resource type '%s' is already registered", item.Name)
	}

	Registry[item.Name] = item

	return nil
}

// Resource is the interface type for resources
type Resource interface {
	// ResourceID returns the unique identifier of a resource
	ResourceID() string

	// ResourceType returns the type of the resource
	ResourceType() string

	// ResourceTitle returns the title of the resource
	ResourceTitle() string

	// Returns the resources before which this resource shoud be processed
	WantBefore() []string

	// Returns the resources after which this resource should be processed
	WantAfter() []string

	// Evaluates the resource
	Evaluate() (State, error)

	// Creates the resource
	Create(w io.Writer) error

	// Deletes the resource
	Delete(w io.Writer) error

	// Updates the resource
	Update(w io.Writer) error
}

// BaseResource is the base resource type for all resources
// The purpose of this type is to be embedded into other resources
// Partially implements the Resource interface
type BaseResource struct {
	// Type of the resource
	Type string `json:"-"`

	// Title of the resource
	Title string `json:"-"`

	// Desired state of the resource
	State string `hcl:"state" json:"state"`

	// Resources before which this resource should be processed
	Before []string `hcl:"before" json:"before,omitempty"`

	// Resources after which this resource should be processed
	After []string `hcl:"after" json:"after,omitempty"`
}

// ResourceID returns the unique resource id
func (b *BaseResource) ResourceID() string {
	return fmt.Sprintf("%s[%s]", b.Type, b.Title)
}

// ResourceType returns the resource type
func (b *BaseResource) ResourceType() string {
	return b.Type
}

// ResourceTitle returns the resource title
func (b *BaseResource) ResourceTitle() string {
	return b.Title
}

// WantBefore returns the resources before which this resource
// should be processed
func (b *BaseResource) WantBefore() []string {
	return b.Before
}

// WantAfter returns the resources after which this resource
// should be processed
func (b *BaseResource) WantAfter() []string {
	return b.After
}

// Printf works just like fmt.Printf except that it writes to the
// given resource writer object and prepends the
// resource id to the output
func (b *BaseResource) Printf(w io.Writer, format string, a ...interface{}) (int, error) {
	fmt.Fprintf(w, "%s ", b.ResourceID())

	return fmt.Fprintf(w, format, a...)
}
<commit_msg>resource: resources accept options that can be used during processing<commit_after>package resource

import (
	"fmt"
	"io"

	"github.com/hashicorp/hcl/hcl/ast"
)

// Registry contains all known resources
var Registry = make(map[string]RegistryItem)

// provider is used to create new resources from an HCL AST object item
type provider func(title string, item *ast.ObjectItem) (Resource, error)

// RegistryItem type represents an item from the registry
type RegistryItem struct {
	// Name of the resource type
	Name string

	// Short desription of the resource
	Description string

	// Resource provider
	Provider provider
}

// Register adds a resource type to the registry
func Register(item RegistryItem) error {
	_, ok := Registry[item.Name]
	if ok {
		return fmt.Errorf("Resource type '%s' is already registered", item.Name)
	}

	Registry[item.Name] = item

	return nil
}

// Resource is the interface type for resources
type Resource interface {
	// ResourceID returns the unique identifier of a resource
	ResourceID() string

	// ResourceType returns the type of the resource
	ResourceType() string

	// ResourceTitle returns the title of the resource
	ResourceTitle() string

	// Returns the resources before which this resource shoud be processed
	WantBefore() []string

	// Returns the resources after which this resource should be processed
	WantAfter() []string

	// Evaluates the resource
	Evaluate(w io.Writer, opts *Options) (State, error)

	// Creates the resource
	Create(w io.Writer, opts *Options) error

	// Deletes the resource
	Delete(w io.Writer, opts *Options) error

	// Updates the resource
	Update(w io.Writer, opts *Options) error
}

// Options sent to resources during processing
type Options struct {
	// The site directory which contains module and data files
	SiteDir string

	// Do not take any action, just report what would be done
	DryRun bool
}

// BaseResource is the base resource type for all resources
// The purpose of this type is to be embedded into other resources
// Partially implements the Resource interface
type BaseResource struct {
	// Type of the resource
	Type string `json:"-"`

	// Title of the resource
	Title string `json:"-"`

	// Desired state of the resource
	State string `hcl:"state" json:"state"`

	// Resources before which this resource should be processed
	Before []string `hcl:"before" json:"before,omitempty"`

	// Resources after which this resource should be processed
	After []string `hcl:"after" json:"after,omitempty"`
}

// ResourceID returns the unique resource id
func (b *BaseResource) ResourceID() string {
	return fmt.Sprintf("%s[%s]", b.Type, b.Title)
}

// ResourceType returns the resource type
func (b *BaseResource) ResourceType() string {
	return b.Type
}

// ResourceTitle returns the resource title
func (b *BaseResource) ResourceTitle() string {
	return b.Title
}

// WantBefore returns the resources before which this resource
// should be processed
func (b *BaseResource) WantBefore() []string {
	return b.Before
}

// WantAfter returns the resources after which this resource
// should be processed
func (b *BaseResource) WantAfter() []string {
	return b.After
}

// Printf works just like fmt.Printf except that it writes to the
// given resource writer object and prepends the
// resource id to the output
func (b *BaseResource) Printf(w io.Writer, format string, a ...interface{}) (int, error) {
	fmt.Fprintf(w, "%s ", b.ResourceID())

	return fmt.Fprintf(w, format, a...)
}
<|endoftext|>


<commit_before>package resource

import (
	"errors"
	"fmt"
	"log"
	"os"
)

// State type represents the current and wanted states of a resource
type State struct {
	// Current state of the resource
	Current string

	// Wanted state of the resource
	Want string

	// Indicates that a resource is in the desired state, but is
	// out of date and needs to be updated, e.g. a file resource is
	// present, but its permissions need to be corrected.
	Update bool
}

// Resource is the interface type for resources
type Resource interface {
	// ID returns the unique identifier of the resource
	ID() string

	// Validate validates the resource
	Validate() error

	// GetBefore returns the list of resources before which this
	// resource shoud be processed
	GetBefore() []string

	// GetAfter returns the list of resources after which this
	// resource should be processed
	GetAfter() []string

	// GetPresentStates returns the list of states, for which the
	// resource is considered to be present
	GetPresentStates() []string

	// GetAbsentStates returns the list of states, for which the
	// resource is considered to be absent
	GetAbsentStates() []string

	// Evaluates the resource
	Evaluate() (State, error)

	// Creates the resource
	Create() error

	// Deletes the resource
	Delete() error

	// Updates the resource
	Update() error

	// Log logs events
	Log(format string, a ...interface{})
}

// Config type contains various settings used by the resources
type Config struct {
	// The site repo which contains module and data files
	SiteRepo string

	// Logger used by the resources to log events
	Logger *log.Logger
}

// DefaultLogger is the default logger instance used for
// logging events from the resources
var DefaultLogger = log.New(os.Stdout, "", log.LstdFlags)

// DefaultConfig is the default configuration used by the resources
var DefaultConfig = &Config{
	Logger: DefaultLogger,
}

// Log logs an event using the default resource logger
func Log(format string, a ...interface{}) {
	DefaultConfig.Logger.Printf(format, a...)
}

// Base is the base resource type for all resources
// The purpose of this type is to be embedded into other resources
// Partially implements the Resource interface
type Base struct {
	// Type of the resource
	Type string `luar:"-"`

	// Name of the resource
	Name string `luar:"-"`

	// Desired state of the resource
	State string `luar:"state"`

	// Resources before which this resource should be processed
	Before []string `luar:"before"`

	// PresentStates contains the list of states, for which the
	// resource is considered to be present
	PresentStates []string `luar:"-"`

	// AbsentStates contains the list of states, for which the
	// resource is considered to be absent
	AbsentStates []string `luar:"-"`

	// Resources after which this resource should be processed
	After []string `luar:"after"`
}

// ID returns the unique resource id
func (b *Base) ID() string {
	return fmt.Sprintf("%s[%s]", b.Type, b.Name)
}

// Validate validates the resource
func (b *Base) Validate() error {
	if b.Type == "" {
		return errors.New("Invalid resource type")
	}

	if b.Name == "" {
		return errors.New("Invalid resource name")
	}

	return nil
}

// GetBefore returns the list of resources before which this resource
// should be processed
func (b *Base) GetBefore() []string {
	return b.Before
}

// GetAfter returns the list of resources after which this resource
// should be processed
func (b *Base) GetAfter() []string {
	return b.After
}

// GetPresentStates returns the list of states, for which the
// resource is considered to be present
func (b *Base) GetPresentStates() []string {
	return b.PresentStates
}

// GetAbsentStates returns the list of states, for which the
// resource is considered to be absent
func (b *Base) GetAbsentStates() []string {
	return b.AbsentStates
}

// Log writes to the default config writer object and
// prepends the resource id to the output
func (b *Base) Log(format string, a ...interface{}) {
	f := fmt.Sprintf("%s %s", b.ID(), format)
	Log(f, a...)
}
<commit_msg>resource: validate the resource state<commit_after>package resource

import (
	"errors"
	"fmt"
	"log"
	"os"

	"github.com/dnaeon/gru/utils"
)

// State type represents the current and wanted states of a resource
type State struct {
	// Current state of the resource
	Current string

	// Wanted state of the resource
	Want string

	// Indicates that a resource is in the desired state, but is
	// out of date and needs to be updated, e.g. a file resource is
	// present, but its permissions need to be corrected.
	Update bool
}

// Resource is the interface type for resources
type Resource interface {
	// ID returns the unique identifier of the resource
	ID() string

	// Validate validates the resource
	Validate() error

	// GetBefore returns the list of resources before which this
	// resource shoud be processed
	GetBefore() []string

	// GetAfter returns the list of resources after which this
	// resource should be processed
	GetAfter() []string

	// GetPresentStates returns the list of states, for which the
	// resource is considered to be present
	GetPresentStates() []string

	// GetAbsentStates returns the list of states, for which the
	// resource is considered to be absent
	GetAbsentStates() []string

	// Evaluates the resource
	Evaluate() (State, error)

	// Creates the resource
	Create() error

	// Deletes the resource
	Delete() error

	// Updates the resource
	Update() error

	// Log logs events
	Log(format string, a ...interface{})
}

// Config type contains various settings used by the resources
type Config struct {
	// The site repo which contains module and data files
	SiteRepo string

	// Logger used by the resources to log events
	Logger *log.Logger
}

// DefaultLogger is the default logger instance used for
// logging events from the resources
var DefaultLogger = log.New(os.Stdout, "", log.LstdFlags)

// DefaultConfig is the default configuration used by the resources
var DefaultConfig = &Config{
	Logger: DefaultLogger,
}

// Log logs an event using the default resource logger
func Log(format string, a ...interface{}) {
	DefaultConfig.Logger.Printf(format, a...)
}

// Base is the base resource type for all resources
// The purpose of this type is to be embedded into other resources
// Partially implements the Resource interface
type Base struct {
	// Type of the resource
	Type string `luar:"-"`

	// Name of the resource
	Name string `luar:"-"`

	// Desired state of the resource
	State string `luar:"state"`

	// Resources before which this resource should be processed
	Before []string `luar:"before"`

	// PresentStates contains the list of states, for which the
	// resource is considered to be present
	PresentStates []string `luar:"-"`

	// AbsentStates contains the list of states, for which the
	// resource is considered to be absent
	AbsentStates []string `luar:"-"`

	// Resources after which this resource should be processed
	After []string `luar:"after"`
}

// ID returns the unique resource id
func (b *Base) ID() string {
	return fmt.Sprintf("%s[%s]", b.Type, b.Name)
}

// Validate validates the resource
func (b *Base) Validate() error {
	if b.Type == "" {
		return errors.New("Invalid resource type")
	}

	if b.Name == "" {
		return errors.New("Invalid resource name")
	}

	states := append(b.PresentStates, b.AbsentStates...)
	if !utils.NewList(states).Contains(b.State) {
		return fmt.Errorf("Invalid state '%s' for resource %s", b.State, b.ID())
	}

	return nil
}

// GetBefore returns the list of resources before which this resource
// should be processed
func (b *Base) GetBefore() []string {
	return b.Before
}

// GetAfter returns the list of resources after which this resource
// should be processed
func (b *Base) GetAfter() []string {
	return b.After
}

// GetPresentStates returns the list of states, for which the
// resource is considered to be present
func (b *Base) GetPresentStates() []string {
	return b.PresentStates
}

// GetAbsentStates returns the list of states, for which the
// resource is considered to be absent
func (b *Base) GetAbsentStates() []string {
	return b.AbsentStates
}

// Log writes to the default config writer object and
// prepends the resource id to the output
func (b *Base) Log(format string, a ...interface{}) {
	f := fmt.Sprintf("%s %s", b.ID(), format)
	Log(f, a...)
}
<|endoftext|>


<commit_before>package goresponse

type HTTPResponse struct {
	Tag    string      `json:"tag"`     // tag
	Result bool        `json:"success"` // 请求是否成功处理
	State  *State      `json:"state"`   // 状态元
	Meta   *Meta       `json:"meta"`    // 扩展元
	Data   interface{} `json:"data"`    // 核心数据
	Error  string      `json:"error"`   // 错误描述
}

func NewHTTPResponse() *HTTPResponse {
	return &HTTPResponse{
		Result: false,
		State:  NewState(),
		Meta:   nil,
		Data:   "",
		Error:  "",
	}
}

// 设置状态元
func (r *HTTPResponse) S(s *State) *HTTPResponse {
	r.State = s
	r.Result = r.IsSuccess()
	return r
}

// 设置扩展元
func (r *HTTPResponse) ME(m *Meta) *HTTPResponse {
	if m != nil {
		r.Meta = m
	}
	return r
}

// 设置核心数据
func (r *HTTPResponse) D(d interface{}) *HTTPResponse {
	if d != nil {
		r.Data = d
	}
	return r
}

// 设置错误描述
func (r *HTTPResponse) E(err error) *HTTPResponse {
	if err != nil {
		r.Error = err.Error()
	} else {
		r.Error = ""
	}
	return r
}

// 设置执行成功
func (r *HTTPResponse) Success() *HTTPResponse {
	r.State.Success()
	r.E(nil).Result = true
	return r
}

func (r *HTTPResponse) IsSuccess() bool {
	return (r.State.Status == Status_success || r.State.Status == Status_ignore)
}

func (r *HTTPResponse) IsInvalidToken() bool {
	return (r.State.Status == Status_invalid_token)
}

func (r *HTTPResponse) IsTimeoutToken() bool {
	return (r.State.Status == Status_token_timeout)
}
<commit_msg>feat: modify http response<commit_after>package goresponse

type HTTPResponse struct {
	Tag    string      `json:"tag"`     // tag
	Result bool        `json:"success"` // 请求是否成功处理
	State  *State      `json:"state"`   // 状态元
	Meta   *Meta       `json:"meta"`    // 扩展元
	Data   interface{} `json:"data"`    // 核心数据
	Token  string      `json:"token"`   // 身份令牌
	Error  string      `json:"error"`   // 错误描述
}

func NewHTTPResponse() *HTTPResponse {
	return &HTTPResponse{
		Result: false,
		State:  NewState(),
		Meta:   nil,
		Data:   "",
		Error:  "",
	}
}

// 设置状态元
func (r *HTTPResponse) S(s *State) *HTTPResponse {
	r.State = s
	r.Result = r.IsSuccess()
	return r
}

// 设置扩展元
func (r *HTTPResponse) ME(m *Meta) *HTTPResponse {
	if m != nil {
		r.Meta = m
	}
	return r
}

// 设置核心数据
func (r *HTTPResponse) D(d interface{}) *HTTPResponse {
	if d != nil {
		r.Data = d
	}
	return r
}

// 设置新身份令牌
func (r *HTTPResponse) T(t string) *HTTPResponse {
	r.Token = t
	return r
}

// 设置错误描述
func (r *HTTPResponse) E(err error) *HTTPResponse {
	if err != nil {
		r.Error = err.Error()
	} else {
		r.Error = ""
	}
	return r
}

// 设置执行成功
func (r *HTTPResponse) Success() *HTTPResponse {
	r.State.Success()
	r.E(nil).Result = true
	return r
}

func (r *HTTPResponse) IsSuccess() bool {
	return (r.State.Status == Status_success || r.State.Status == Status_ignore)
}

func (r *HTTPResponse) IsInvalidToken() bool {
	return (r.State.Status == Status_invalid_token)
}

func (r *HTTPResponse) IsTimeoutToken() bool {
	return (r.State.Status == Status_token_timeout)
}
<|endoftext|>


<commit_before>package ipnsfs

import (
	"errors"
	"fmt"
	"os"
	"sync"

	dag "github.com/jbenet/go-ipfs/merkledag"
	ft "github.com/jbenet/go-ipfs/unixfs"
	ufspb "github.com/jbenet/go-ipfs/unixfs/pb"
)

var ErrNotYetImplemented = errors.New("not yet implemented")
var ErrInvalidChild = errors.New("invalid child node")

type Directory struct {
	fs     *Filesystem
	parent childCloser

	childDirs map[string]*Directory
	files     map[string]*File

	lock sync.Mutex
	node *dag.Node

	name string
}

func NewDirectory(name string, node *dag.Node, parent childCloser, fs *Filesystem) *Directory {
	return &Directory{
		fs:        fs,
		name:      name,
		node:      node,
		parent:    parent,
		childDirs: make(map[string]*Directory),
		files:     make(map[string]*File),
	}
}

// Open opens a file at the given path 'tpath'
func (d *Directory) Open(tpath []string, mode int) (*File, error) {
	if len(tpath) == 0 {
		return nil, ErrIsDirectory
	}
	if len(tpath) == 1 {
		fi, err := d.childFile(tpath[0])
		if err == nil {
			return fi, nil
		}

		if mode|os.O_CREATE != 0 {
			fnode := new(dag.Node)
			fnode.Data = ft.FilePBData(nil, 0)
			nfi, err := NewFile(tpath[0], fnode, d, d.fs)
			if err != nil {
				return nil, err
			}
			d.files[tpath[0]] = nfi
			return nfi, nil
		}

		return nil, ErrNoSuch
	}

	dir, err := d.childDir(tpath[0])
	if err != nil {
		return nil, err
	}
	return dir.Open(tpath[1:], mode)
}

// consider combining into a single method...
type childCloser interface {
	closeChild(string, *dag.Node) error
}

func (d *Directory) closeChild(name string, nd *dag.Node) error {
	_, err := d.fs.dserv.Add(nd)
	if err != nil {
		return err
	}

	d.lock.Lock()
	err = d.node.RemoveNodeLink(name)
	if err != nil && err != dag.ErrNotFound {
		d.lock.Unlock()
		return err
	}

	err = d.node.AddNodeLinkClean(name, nd)
	if err != nil {
		d.lock.Unlock()
		return err
	}
	d.lock.Unlock()

	return d.parent.closeChild(d.name, d.node)
}

func (d *Directory) Type() NodeType {
	return TDir
}

func (d *Directory) childFile(name string) (*File, error) {
	fi, ok := d.files[name]
	if ok {
		return fi, nil
	}

	// search dag
	for _, lnk := range d.node.Links {
		if lnk.Name == name {
			nd, err := lnk.GetNode(d.fs.dserv)
			if err != nil {
				return nil, err
			}
			i, err := ft.FromBytes(nd.Data)
			if err != nil {
				return nil, err
			}

			switch i.GetType() {
			case ufspb.Data_Directory:
				return nil, ErrIsDirectory
			case ufspb.Data_File:
				nfi, err := NewFile(name, nd, d, d.fs)
				if err != nil {
					return nil, err
				}
				d.files[name] = nfi
				return nfi, nil
			case ufspb.Data_Metadata:
				return nil, ErrNotYetImplemented
			default:
				return nil, ErrInvalidChild
			}
		}
	}
	return nil, ErrNoSuch
}

func (d *Directory) childDir(name string) (*Directory, error) {
	dir, ok := d.childDirs[name]
	if ok {
		return dir, nil
	}

	for _, lnk := range d.node.Links {
		if lnk.Name == name {
			nd, err := lnk.GetNode(d.fs.dserv)
			if err != nil {
				return nil, err
			}
			i, err := ft.FromBytes(nd.Data)
			if err != nil {
				return nil, err
			}

			switch i.GetType() {
			case ufspb.Data_Directory:
				ndir := NewDirectory(name, nd, d, d.fs)
				d.childDirs[name] = ndir
				return ndir, nil
			case ufspb.Data_File:
				return nil, fmt.Errorf("%s is not a directory", name)
			case ufspb.Data_Metadata:
				return nil, ErrNotYetImplemented
			default:
				return nil, ErrInvalidChild
			}
		}

	}

	return nil, ErrNoSuch
}

func (d *Directory) Child(name string) (FSNode, error) {
	d.lock.Lock()
	defer d.lock.Unlock()
	dir, err := d.childDir(name)
	if err == nil {
		return dir, nil
	}
	fi, err := d.childFile(name)
	if err == nil {
		return fi, nil
	}

	return nil, ErrNoSuch
}

func (d *Directory) List() []string {
	d.lock.Lock()
	defer d.lock.Unlock()

	var out []string
	for _, lnk := range d.node.Links {
		out = append(out, lnk.Name)
	}
	return out
}

func (d *Directory) Mkdir(name string) (*Directory, error) {
	d.lock.Lock()

	_, err := d.childDir(name)
	if err == nil {
		d.lock.Unlock()
		return nil, errors.New("directory by that name already exists")
	}
	_, err = d.childFile(name)
	if err == nil {
		d.lock.Unlock()
		return nil, errors.New("file by that name already exists")
	}

	ndir := &dag.Node{Data: ft.FolderPBData()}
	err = d.node.AddNodeLinkClean(name, ndir)
	if err != nil {
		d.lock.Unlock()
		return nil, err
	}
	d.lock.Unlock()

	err = d.parent.closeChild(d.name, d.node)
	if err != nil {
		return nil, err
	}

	d.lock.Lock()
	defer d.lock.Unlock()

	return d.childDir(name)
}

func (d *Directory) Unlink(name string) error {
	d.lock.Lock()
	delete(d.childDirs, name)
	delete(d.files, name)

	err := d.node.RemoveNodeLink(name)
	if err != nil {
		d.lock.Unlock()
		return err
	}
	d.lock.Unlock()

	return d.parent.closeChild(d.name, d.node)
}

// RenameEntry renames the child by 'oldname' of this directory to 'newname'
func (d *Directory) RenameEntry(oldname, newname string) error {
	// Is the child a directory?
	dir, err := d.childDir(oldname)
	if err == nil {
		dir.name = newname

		err := d.node.RemoveNodeLink(oldname)
		if err != nil {
			return err
		}
		err = d.node.AddNodeLinkClean(newname, dir.node)
		if err != nil {
			return err
		}

		delete(d.childDirs, oldname)
		d.childDirs[newname] = dir
		return d.parent.closeChild(d.name, d.node)
	}

	// Is the child a file?
	fi, err := d.childFile(oldname)
	if err == nil {
		fi.name = newname

		err := d.node.RemoveNodeLink(oldname)
		if err != nil {
			return err
		}

		nd, err := fi.GetNode()
		if err != nil {
			return err
		}

		err = d.node.AddNodeLinkClean(newname, nd)
		if err != nil {
			return err
		}

		delete(d.childDirs, oldname)
		d.files[newname] = fi
		return d.parent.closeChild(d.name, d.node)
	}
	return ErrNoSuch
}

// AddChild adds the node 'nd' under this directory giving it the name 'name'
func (d *Directory) AddChild(name string, nd *dag.Node) error {
	pbn, err := ft.FromBytes(nd.Data)
	if err != nil {
		return err
	}

	_, err = d.Child(name)
	if err == nil {
		return errors.New("directory already has entry by that name")
	}

	err = d.node.AddNodeLinkClean(name, nd)
	if err != nil {
		return err
	}

	switch pbn.GetType() {
	case ft.TDirectory:
		d.childDirs[name] = NewDirectory(name, nd, d, d.fs)
	case ft.TFile, ft.TMetadata, ft.TRaw:
		nfi, err := NewFile(name, nd, d, d.fs)
		if err != nil {
			return err
		}
		d.files[name] = nfi
	default:
		return ErrInvalidChild
	}
	return d.parent.closeChild(d.name, d.node)
}

func (d *Directory) GetNode() (*dag.Node, error) {
	return d.node, nil
}

func (d *Directory) Lock() {
	d.lock.Lock()
}

func (d *Directory) Unlock() {
	d.lock.Unlock()
}
<commit_msg>fix locking<commit_after>package ipnsfs

import (
	"errors"
	"fmt"
	"os"
	"sync"

	dag "github.com/jbenet/go-ipfs/merkledag"
	ft "github.com/jbenet/go-ipfs/unixfs"
	ufspb "github.com/jbenet/go-ipfs/unixfs/pb"
)

var ErrNotYetImplemented = errors.New("not yet implemented")
var ErrInvalidChild = errors.New("invalid child node")

type Directory struct {
	fs     *Filesystem
	parent childCloser

	childDirs map[string]*Directory
	files     map[string]*File

	lock sync.Mutex
	node *dag.Node

	name string
}

func NewDirectory(name string, node *dag.Node, parent childCloser, fs *Filesystem) *Directory {
	return &Directory{
		fs:        fs,
		name:      name,
		node:      node,
		parent:    parent,
		childDirs: make(map[string]*Directory),
		files:     make(map[string]*File),
	}
}

// Open opens a file at the given path 'tpath'
func (d *Directory) Open(tpath []string, mode int) (*File, error) {
	if len(tpath) == 0 {
		return nil, ErrIsDirectory
	}
	if len(tpath) == 1 {
		fi, err := d.childFile(tpath[0])
		if err == nil {
			return fi, nil
		}

		if mode|os.O_CREATE != 0 {
			fnode := new(dag.Node)
			fnode.Data = ft.FilePBData(nil, 0)
			nfi, err := NewFile(tpath[0], fnode, d, d.fs)
			if err != nil {
				return nil, err
			}
			d.files[tpath[0]] = nfi
			return nfi, nil
		}

		return nil, ErrNoSuch
	}

	dir, err := d.childDir(tpath[0])
	if err != nil {
		return nil, err
	}
	return dir.Open(tpath[1:], mode)
}

type childCloser interface {
	closeChild(string, *dag.Node) error
}

func (d *Directory) closeChild(name string, nd *dag.Node) error {
	_, err := d.fs.dserv.Add(nd)
	if err != nil {
		return err
	}

	d.lock.Lock()
	defer d.lock.Unlock()
	err = d.node.RemoveNodeLink(name)
	if err != nil && err != dag.ErrNotFound {
		return err
	}

	err = d.node.AddNodeLinkClean(name, nd)
	if err != nil {
		return err
	}

	return d.parent.closeChild(d.name, d.node)
}

func (d *Directory) Type() NodeType {
	return TDir
}

func (d *Directory) childFile(name string) (*File, error) {
	fi, ok := d.files[name]
	if ok {
		return fi, nil
	}

	// search dag
	for _, lnk := range d.node.Links {
		if lnk.Name == name {
			nd, err := lnk.GetNode(d.fs.dserv)
			if err != nil {
				return nil, err
			}
			i, err := ft.FromBytes(nd.Data)
			if err != nil {
				return nil, err
			}

			switch i.GetType() {
			case ufspb.Data_Directory:
				return nil, ErrIsDirectory
			case ufspb.Data_File:
				nfi, err := NewFile(name, nd, d, d.fs)
				if err != nil {
					return nil, err
				}
				d.files[name] = nfi
				return nfi, nil
			case ufspb.Data_Metadata:
				return nil, ErrNotYetImplemented
			default:
				return nil, ErrInvalidChild
			}
		}
	}
	return nil, ErrNoSuch
}

func (d *Directory) childDir(name string) (*Directory, error) {
	dir, ok := d.childDirs[name]
	if ok {
		return dir, nil
	}

	for _, lnk := range d.node.Links {
		if lnk.Name == name {
			nd, err := lnk.GetNode(d.fs.dserv)
			if err != nil {
				return nil, err
			}
			i, err := ft.FromBytes(nd.Data)
			if err != nil {
				return nil, err
			}

			switch i.GetType() {
			case ufspb.Data_Directory:
				ndir := NewDirectory(name, nd, d, d.fs)
				d.childDirs[name] = ndir
				return ndir, nil
			case ufspb.Data_File:
				return nil, fmt.Errorf("%s is not a directory", name)
			case ufspb.Data_Metadata:
				return nil, ErrNotYetImplemented
			default:
				return nil, ErrInvalidChild
			}
		}

	}

	return nil, ErrNoSuch
}

func (d *Directory) Child(name string) (FSNode, error) {
	d.lock.Lock()
	defer d.lock.Unlock()
	return d.childUnsync(name)
}

func (d *Directory) childUnsync(name string) (FSNode, error) {
	dir, err := d.childDir(name)
	if err == nil {
		return dir, nil
	}
	fi, err := d.childFile(name)
	if err == nil {
		return fi, nil
	}

	return nil, ErrNoSuch
}

func (d *Directory) List() []string {
	d.lock.Lock()
	defer d.lock.Unlock()

	var out []string
	for _, lnk := range d.node.Links {
		out = append(out, lnk.Name)
	}
	return out
}

func (d *Directory) Mkdir(name string) (*Directory, error) {
	d.lock.Lock()
	defer d.lock.Unlock()

	_, err := d.childDir(name)
	if err == nil {
		return nil, os.ErrExist
	}
	_, err = d.childFile(name)
	if err == nil {
		return nil, os.ErrExist
	}

	ndir := &dag.Node{Data: ft.FolderPBData()}
	err = d.node.AddNodeLinkClean(name, ndir)
	if err != nil {
		return nil, err
	}

	err = d.parent.closeChild(d.name, d.node)
	if err != nil {
		return nil, err
	}

	return d.childDir(name)
}

func (d *Directory) Unlink(name string) error {
	d.lock.Lock()
	defer d.lock.Unlock()

	delete(d.childDirs, name)
	delete(d.files, name)

	err := d.node.RemoveNodeLink(name)
	if err != nil {
		return err
	}

	return d.parent.closeChild(d.name, d.node)
}

// RenameEntry renames the child by 'oldname' of this directory to 'newname'
func (d *Directory) RenameEntry(oldname, newname string) error {
	d.Lock()
	defer d.Unlock()
	// Is the child a directory?
	dir, err := d.childDir(oldname)
	if err == nil {
		dir.name = newname

		err := d.node.RemoveNodeLink(oldname)
		if err != nil {
			return err
		}
		err = d.node.AddNodeLinkClean(newname, dir.node)
		if err != nil {
			return err
		}

		delete(d.childDirs, oldname)
		d.childDirs[newname] = dir
		return d.parent.closeChild(d.name, d.node)
	}

	// Is the child a file?
	fi, err := d.childFile(oldname)
	if err == nil {
		fi.name = newname

		err := d.node.RemoveNodeLink(oldname)
		if err != nil {
			return err
		}

		nd, err := fi.GetNode()
		if err != nil {
			return err
		}

		err = d.node.AddNodeLinkClean(newname, nd)
		if err != nil {
			return err
		}

		delete(d.childDirs, oldname)
		d.files[newname] = fi
		return d.parent.closeChild(d.name, d.node)
	}
	return ErrNoSuch
}

// AddChild adds the node 'nd' under this directory giving it the name 'name'
func (d *Directory) AddChild(name string, nd *dag.Node) error {
	d.Lock()
	defer d.Unlock()
	pbn, err := ft.FromBytes(nd.Data)
	if err != nil {
		return err
	}

	_, err = d.childUnsync(name)
	if err == nil {
		return errors.New("directory already has entry by that name")
	}

	err = d.node.AddNodeLinkClean(name, nd)
	if err != nil {
		return err
	}

	switch pbn.GetType() {
	case ft.TDirectory:
		d.childDirs[name] = NewDirectory(name, nd, d, d.fs)
	case ft.TFile, ft.TMetadata, ft.TRaw:
		nfi, err := NewFile(name, nd, d, d.fs)
		if err != nil {
			return err
		}
		d.files[name] = nfi
	default:
		return ErrInvalidChild
	}
	return d.parent.closeChild(d.name, d.node)
}

func (d *Directory) GetNode() (*dag.Node, error) {
	return d.node, nil
}

func (d *Directory) Lock() {
	d.lock.Lock()
}

func (d *Directory) Unlock() {
	d.lock.Unlock()
}
<|endoftext|>


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

package config

import (
	"github.com/neovim/go-client/nvim"
)

// Config represents a config variable for nvim-go.
// Each type must be exported for plugin.HandleAutocmd Eval option.
// Also it does not support embeded type.
type Config struct {
	Global *Global

	Build    *build
	Cover    *cover
	Fmt      *fmt
	Generate *generate
	Guru     *guru
	Iferr    *iferr
	Lint     *lint
	Rename   *rename
	Terminal *terminal
	Test     *test

	Debug *debug
}

// Global represents a global config variable.
type Global struct {
	ChannelID     int
	ServerName    string `eval:"v:servername"`
	ErrorListType string `eval:"g:go#global#errorlisttype"`
}

// build GoBuild command config variable.
type build struct {
	Appengine int64    `eval:"g:go#build#appengine"`
	Autosave  int64    `eval:"g:go#build#autosave"`
	Force     int64    `eval:"g:go#build#force"`
	Flags     []string `eval:"g:go#build#flags"`
}

type cover struct {
	Flags []string `eval:"g:go#cover#flags"`
	Mode  string   `eval:"g:go#cover#mode"`
}

// fmt represents a GoFmt command config variable.
type fmt struct {
	Autosave int64  `eval:"g:go#fmt#autosave"`
	Mode     string `eval:"g:go#fmt#mode"`
}

// generate represents a GoGenerate command config variables.
type generate struct {
	TestAllFuncs      int64  `eval:"g:go#generate#test#allfuncs"`
	TestExclFuncs     string `eval:"g:go#generate#test#exclude"`
	TestExportedFuncs int64  `eval:"g:go#generate#test#exportedfuncs"`
	TestSubTest       int64  `eval:"g:go#generate#test#subtest"`
}

// guru represents a GoGuru command config variable.
type guru struct {
	Reflection int64            `eval:"g:go#guru#reflection"`
	KeepCursor map[string]int64 `eval:"g:go#guru#keep_cursor"`
	JumpFirst  int64            `eval:"g:go#guru#jump_first"`
}

// iferr represents a GoIferr command config variable.
type iferr struct {
	Autosave int64 `eval:"g:go#iferr#autosave"`
}

// lint represents a code lint commands config variable.
type lint struct {
	GolintAutosave          bool     `eval:"g:go#lint#golint#autosave"`
	GolintIgnore            []string `eval:"g:go#lint#golint#ignore"`
	GolintMinConfidence     float64  `eval:"g:go#lint#golint#min_confidence"`
	GolintMode              string   `eval:"g:go#lint#golint#mode"`
	GoVetAutosave           int64    `eval:"g:go#lint#govet#autosave"`
	GoVetFlags              []string `eval:"g:go#lint#govet#flags"`
	GoVetIgnore             []string `eval:"g:go#lint#govet#ignore"`
	MetalinterAutosave      int64    `eval:"g:go#lint#metalinter#autosave"`
	MetalinterAutosaveTools []string `eval:"g:go#lint#metalinter#autosave#tools"`
	MetalinterTools         []string `eval:"g:go#lint#metalinter#tools"`
	MetalinterDeadline      string   `eval:"g:go#lint#metalinter#deadline"`
	MetalinterSkipDir       []string `eval:"g:go#lint#metalinter#skip_dir"`
}

// rename represents a GoRename command config variable.
type rename struct {
	Prefill int64 `eval:"g:go#rename#prefill"`
}

// terminal represents a configure of Neovim terminal buffer.
type terminal struct {
	Mode       string `eval:"g:go#terminal#mode"`
	Position   string `eval:"g:go#terminal#position"`
	Height     int64  `eval:"g:go#terminal#height"`
	Width      int64  `eval:"g:go#terminal#width"`
	StopInsert int64  `eval:"g:go#terminal#stop_insert"`
}

// Test represents a GoTest command config variables.
type test struct {
	AllPackage int64    `eval:"g:go#test#all_package"`
	Autosave   int64    `eval:"g:go#test#autosave"`
	Flags      []string `eval:"g:go#test#flags"`
}

// Debug represents a debug of nvim-go config variable.
type debug struct {
	Enable int64 `eval:"g:go#debug"`
	Pprof  int64 `eval:"g:go#debug#pprof"`
}

var (
	// ChannelID remote plugins channel id.
	ChannelID int
	// ServerName Neovim socket listen location.
	ServerName string
	// ErrorListType type of error list window.
	ErrorListType string

	// BuildAppengine enable appengine bulid.
	BuildAppengine bool
	// BuildAutosave call the GoBuild command automatically at during the BufWritePost.
	BuildAutosave bool
	// BuildForce builds the binary instead of fake(use ioutil.TempFiile) build.
	BuildForce bool
	// BuildFlags flag of compile tools build command.
	BuildFlags []string

	// CoverFlags flags for cover command.
	CoverFlags []string
	// CoverMode mode of cover command.
	CoverMode string

	// FmtAutosave call the GoFmt command automatically at during the BufWritePre.
	FmtAutosave bool
	// FmtMode formatting mode of Fmt command.
	FmtMode string

	// GenerateTestAllFuncs accept all functions to the GenerateTest.
	GenerateTestAllFuncs bool
	// GenerateTestExclFuncs exclude function of GenerateTest.
	GenerateTestExclFuncs string
	// GenerateTestExportedFuncs accept exported functions to the GenerateTest.
	GenerateTestExportedFuncs bool
	// GenerateTestSubTest whether the use Go subtest idiom or not.
	GenerateTestSubTest bool

	// GuruReflection use the type reflection on GoGuru commmands.
	GuruReflection bool
	// GuruKeepCursor keep the cursor focus to source buffer instead of quickfix or locationlist.
	GuruKeepCursor map[string]int64
	// GuruJumpFirst jump the first error position on GoGuru commands.
	GuruJumpFirst bool

	// IferrAutosave call the GoIferr command automatically at during the BufWritePre.
	IferrAutosave bool

	// GolintAutosave call the GoLint command automatically at during the BufWritePost.
	GolintAutosave bool
	// GolintIgnore ignore file for lint command.
	GolintIgnore []string
	// GolintMinConfidence minimum confidence of a problem to print it
	GolintMinConfidence float64
	// GolintMode mode of golint. available value are "root", "current" and "recursive".
	GolintMode string
	// GoVetAutosave call the GoVet command automatically at during the BufWritePost.
	GoVetAutosave bool
	// GoVetFlags default flags for GoVet commands
	GoVetFlags []string
	// GoVetIgnore ignore directories for go vet command.
	GoVetIgnore []string
	// MetalinterAutosave call the GoMetaLinter command automatically at during the BufWritePre.
	MetalinterAutosave bool
	// MetalinterAutosaveTools lint tool list for MetalinterAutosave.
	MetalinterAutosaveTools []string
	// MetalinterTools lint tool list for GoMetaLinter command.
	MetalinterTools []string
	// MetalinterDeadline deadline of GoMetaLinter command timeout.
	MetalinterDeadline string
	// MetalinterSkipDir skips of lint of the directory.
	MetalinterSkipDir []string

	// RenamePrefill Enable naming prefill.
	RenamePrefill bool

	// TerminalMode open the terminal window mode.
	TerminalMode string
	// TerminalPosition open the terminal window position.
	TerminalPosition string
	// TerminalHeight open the terminal window height.
	TerminalHeight int64
	// TerminalWidth open the terminal window width.
	TerminalWidth int64
	// TerminalStopInsert workaround if users set "autocmd BufEnter term://* startinsert".
	TerminalStopInsert bool

	// TestAutosave call the GoBuild command automatically at during the BufWritePost.
	TestAutosave bool
	// TestAll enable all package test on GoTest. similar "go test ./...", but ignored vendor and testdata.
	TestAll bool
	// TestFlags test command default flags.
	TestFlags []string

	// DebugEnable Enable debugging.
	DebugEnable bool
	// DebugPprof Enable net/http/pprof debugging.
	DebugPprof bool
)

// Get gets the user config variables and convert to global varialble.
func Get(v *nvim.Nvim, cfg *Config) {
	// Client
	ChannelID = cfg.Global.ChannelID
	ServerName = cfg.Global.ServerName
	ErrorListType = cfg.Global.ErrorListType

	// Build
	BuildAppengine = itob(cfg.Build.Appengine)
	BuildAutosave = itob(cfg.Build.Autosave)
	BuildForce = itob(cfg.Build.Force)
	BuildFlags = cfg.Build.Flags

	// Cover
	CoverFlags = cfg.Cover.Flags
	CoverMode = cfg.Cover.Mode

	// Fmt
	FmtAutosave = itob(cfg.Fmt.Autosave)
	FmtMode = cfg.Fmt.Mode

	// Generate
	GenerateTestAllFuncs = itob(cfg.Generate.TestAllFuncs)
	GenerateTestExclFuncs = cfg.Generate.TestExclFuncs
	GenerateTestExportedFuncs = itob(cfg.Generate.TestExportedFuncs)
	GenerateTestSubTest = itob(cfg.Generate.TestSubTest)

	// Guru
	GuruReflection = itob(cfg.Guru.Reflection)
	GuruKeepCursor = cfg.Guru.KeepCursor
	GuruJumpFirst = itob(cfg.Guru.JumpFirst)

	// Iferr
	IferrAutosave = itob(cfg.Iferr.Autosave)

	// Lint
	GolintAutosave = cfg.Lint.GolintAutosave
	GolintIgnore = cfg.Lint.GolintIgnore
	GolintMinConfidence = cfg.Lint.GolintMinConfidence
	GolintMode = cfg.Lint.GolintMode
	GoVetAutosave = itob(cfg.Lint.GoVetAutosave)
	GoVetFlags = cfg.Lint.GoVetFlags
	GoVetIgnore = cfg.Lint.GoVetIgnore
	MetalinterAutosave = itob(cfg.Lint.MetalinterAutosave)
	MetalinterAutosaveTools = cfg.Lint.MetalinterAutosaveTools
	MetalinterTools = cfg.Lint.MetalinterTools
	MetalinterDeadline = cfg.Lint.MetalinterDeadline
	MetalinterSkipDir = cfg.Lint.MetalinterSkipDir

	// Rename
	RenamePrefill = itob(cfg.Rename.Prefill)

	// Terminal
	TerminalMode = cfg.Terminal.Mode
	TerminalPosition = cfg.Terminal.Position
	TerminalHeight = cfg.Terminal.Height
	TerminalWidth = cfg.Terminal.Width
	TerminalStopInsert = itob(cfg.Terminal.StopInsert)

	// Test
	TestAutosave = itob(cfg.Test.Autosave)
	TestAll = itob(cfg.Test.AllPackage)
	TestFlags = cfg.Test.Flags

	// Debug
	DebugEnable = itob(cfg.Debug.Enable)
	DebugPprof = itob(cfg.Debug.Pprof)
}

func itob(i int64) bool { return i != int64(0) }
<commit_msg>config: fix GolintAutosave type to int64<commit_after>// Copyright 2016 The nvim-go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package config

import (
	"github.com/neovim/go-client/nvim"
)

// Config represents a config variable for nvim-go.
// Each type must be exported for plugin.HandleAutocmd Eval option.
// Also it does not support embeded type.
type Config struct {
	Global *Global

	Build    *build
	Cover    *cover
	Fmt      *fmt
	Generate *generate
	Guru     *guru
	Iferr    *iferr
	Lint     *lint
	Rename   *rename
	Terminal *terminal
	Test     *test

	Debug *debug
}

// Global represents a global config variable.
type Global struct {
	ChannelID     int
	ServerName    string `eval:"v:servername"`
	ErrorListType string `eval:"g:go#global#errorlisttype"`
}

// build GoBuild command config variable.
type build struct {
	Appengine int64    `eval:"g:go#build#appengine"`
	Autosave  int64    `eval:"g:go#build#autosave"`
	Force     int64    `eval:"g:go#build#force"`
	Flags     []string `eval:"g:go#build#flags"`
}

type cover struct {
	Flags []string `eval:"g:go#cover#flags"`
	Mode  string   `eval:"g:go#cover#mode"`
}

// fmt represents a GoFmt command config variable.
type fmt struct {
	Autosave int64  `eval:"g:go#fmt#autosave"`
	Mode     string `eval:"g:go#fmt#mode"`
}

// generate represents a GoGenerate command config variables.
type generate struct {
	TestAllFuncs      int64  `eval:"g:go#generate#test#allfuncs"`
	TestExclFuncs     string `eval:"g:go#generate#test#exclude"`
	TestExportedFuncs int64  `eval:"g:go#generate#test#exportedfuncs"`
	TestSubTest       int64  `eval:"g:go#generate#test#subtest"`
}

// guru represents a GoGuru command config variable.
type guru struct {
	Reflection int64            `eval:"g:go#guru#reflection"`
	KeepCursor map[string]int64 `eval:"g:go#guru#keep_cursor"`
	JumpFirst  int64            `eval:"g:go#guru#jump_first"`
}

// iferr represents a GoIferr command config variable.
type iferr struct {
	Autosave int64 `eval:"g:go#iferr#autosave"`
}

// lint represents a code lint commands config variable.
type lint struct {
	GolintAutosave          int64    `eval:"g:go#lint#golint#autosave"`
	GolintIgnore            []string `eval:"g:go#lint#golint#ignore"`
	GolintMinConfidence     float64  `eval:"g:go#lint#golint#min_confidence"`
	GolintMode              string   `eval:"g:go#lint#golint#mode"`
	GoVetAutosave           int64    `eval:"g:go#lint#govet#autosave"`
	GoVetFlags              []string `eval:"g:go#lint#govet#flags"`
	GoVetIgnore             []string `eval:"g:go#lint#govet#ignore"`
	MetalinterAutosave      int64    `eval:"g:go#lint#metalinter#autosave"`
	MetalinterAutosaveTools []string `eval:"g:go#lint#metalinter#autosave#tools"`
	MetalinterTools         []string `eval:"g:go#lint#metalinter#tools"`
	MetalinterDeadline      string   `eval:"g:go#lint#metalinter#deadline"`
	MetalinterSkipDir       []string `eval:"g:go#lint#metalinter#skip_dir"`
}

// rename represents a GoRename command config variable.
type rename struct {
	Prefill int64 `eval:"g:go#rename#prefill"`
}

// terminal represents a configure of Neovim terminal buffer.
type terminal struct {
	Mode       string `eval:"g:go#terminal#mode"`
	Position   string `eval:"g:go#terminal#position"`
	Height     int64  `eval:"g:go#terminal#height"`
	Width      int64  `eval:"g:go#terminal#width"`
	StopInsert int64  `eval:"g:go#terminal#stop_insert"`
}

// Test represents a GoTest command config variables.
type test struct {
	AllPackage int64    `eval:"g:go#test#all_package"`
	Autosave   int64    `eval:"g:go#test#autosave"`
	Flags      []string `eval:"g:go#test#flags"`
}

// Debug represents a debug of nvim-go config variable.
type debug struct {
	Enable int64 `eval:"g:go#debug"`
	Pprof  int64 `eval:"g:go#debug#pprof"`
}

var (
	// ChannelID remote plugins channel id.
	ChannelID int
	// ServerName Neovim socket listen location.
	ServerName string
	// ErrorListType type of error list window.
	ErrorListType string

	// BuildAppengine enable appengine bulid.
	BuildAppengine bool
	// BuildAutosave call the GoBuild command automatically at during the BufWritePost.
	BuildAutosave bool
	// BuildForce builds the binary instead of fake(use ioutil.TempFiile) build.
	BuildForce bool
	// BuildFlags flag of compile tools build command.
	BuildFlags []string

	// CoverFlags flags for cover command.
	CoverFlags []string
	// CoverMode mode of cover command.
	CoverMode string

	// FmtAutosave call the GoFmt command automatically at during the BufWritePre.
	FmtAutosave bool
	// FmtMode formatting mode of Fmt command.
	FmtMode string

	// GenerateTestAllFuncs accept all functions to the GenerateTest.
	GenerateTestAllFuncs bool
	// GenerateTestExclFuncs exclude function of GenerateTest.
	GenerateTestExclFuncs string
	// GenerateTestExportedFuncs accept exported functions to the GenerateTest.
	GenerateTestExportedFuncs bool
	// GenerateTestSubTest whether the use Go subtest idiom or not.
	GenerateTestSubTest bool

	// GuruReflection use the type reflection on GoGuru commmands.
	GuruReflection bool
	// GuruKeepCursor keep the cursor focus to source buffer instead of quickfix or locationlist.
	GuruKeepCursor map[string]int64
	// GuruJumpFirst jump the first error position on GoGuru commands.
	GuruJumpFirst bool

	// IferrAutosave call the GoIferr command automatically at during the BufWritePre.
	IferrAutosave bool

	// GolintAutosave call the GoLint command automatically at during the BufWritePost.
	GolintAutosave bool
	// GolintIgnore ignore file for lint command.
	GolintIgnore []string
	// GolintMinConfidence minimum confidence of a problem to print it
	GolintMinConfidence float64
	// GolintMode mode of golint. available value are "root", "current" and "recursive".
	GolintMode string
	// GoVetAutosave call the GoVet command automatically at during the BufWritePost.
	GoVetAutosave bool
	// GoVetFlags default flags for GoVet commands
	GoVetFlags []string
	// GoVetIgnore ignore directories for go vet command.
	GoVetIgnore []string
	// MetalinterAutosave call the GoMetaLinter command automatically at during the BufWritePre.
	MetalinterAutosave bool
	// MetalinterAutosaveTools lint tool list for MetalinterAutosave.
	MetalinterAutosaveTools []string
	// MetalinterTools lint tool list for GoMetaLinter command.
	MetalinterTools []string
	// MetalinterDeadline deadline of GoMetaLinter command timeout.
	MetalinterDeadline string
	// MetalinterSkipDir skips of lint of the directory.
	MetalinterSkipDir []string

	// RenamePrefill Enable naming prefill.
	RenamePrefill bool

	// TerminalMode open the terminal window mode.
	TerminalMode string
	// TerminalPosition open the terminal window position.
	TerminalPosition string
	// TerminalHeight open the terminal window height.
	TerminalHeight int64
	// TerminalWidth open the terminal window width.
	TerminalWidth int64
	// TerminalStopInsert workaround if users set "autocmd BufEnter term://* startinsert".
	TerminalStopInsert bool

	// TestAutosave call the GoBuild command automatically at during the BufWritePost.
	TestAutosave bool
	// TestAll enable all package test on GoTest. similar "go test ./...", but ignored vendor and testdata.
	TestAll bool
	// TestFlags test command default flags.
	TestFlags []string

	// DebugEnable Enable debugging.
	DebugEnable bool
	// DebugPprof Enable net/http/pprof debugging.
	DebugPprof bool
)

// Get gets the user config variables and convert to global varialble.
func Get(v *nvim.Nvim, cfg *Config) {
	// Client
	ChannelID = cfg.Global.ChannelID
	ServerName = cfg.Global.ServerName
	ErrorListType = cfg.Global.ErrorListType

	// Build
	BuildAppengine = itob(cfg.Build.Appengine)
	BuildAutosave = itob(cfg.Build.Autosave)
	BuildForce = itob(cfg.Build.Force)
	BuildFlags = cfg.Build.Flags

	// Cover
	CoverFlags = cfg.Cover.Flags
	CoverMode = cfg.Cover.Mode

	// Fmt
	FmtAutosave = itob(cfg.Fmt.Autosave)
	FmtMode = cfg.Fmt.Mode

	// Generate
	GenerateTestAllFuncs = itob(cfg.Generate.TestAllFuncs)
	GenerateTestExclFuncs = cfg.Generate.TestExclFuncs
	GenerateTestExportedFuncs = itob(cfg.Generate.TestExportedFuncs)
	GenerateTestSubTest = itob(cfg.Generate.TestSubTest)

	// Guru
	GuruReflection = itob(cfg.Guru.Reflection)
	GuruKeepCursor = cfg.Guru.KeepCursor
	GuruJumpFirst = itob(cfg.Guru.JumpFirst)

	// Iferr
	IferrAutosave = itob(cfg.Iferr.Autosave)

	// Lint
	GolintAutosave = itob(cfg.Lint.GolintAutosave)
	GolintIgnore = cfg.Lint.GolintIgnore
	GolintMinConfidence = cfg.Lint.GolintMinConfidence
	GolintMode = cfg.Lint.GolintMode
	GoVetAutosave = itob(cfg.Lint.GoVetAutosave)
	GoVetFlags = cfg.Lint.GoVetFlags
	GoVetIgnore = cfg.Lint.GoVetIgnore
	MetalinterAutosave = itob(cfg.Lint.MetalinterAutosave)
	MetalinterAutosaveTools = cfg.Lint.MetalinterAutosaveTools
	MetalinterTools = cfg.Lint.MetalinterTools
	MetalinterDeadline = cfg.Lint.MetalinterDeadline
	MetalinterSkipDir = cfg.Lint.MetalinterSkipDir

	// Rename
	RenamePrefill = itob(cfg.Rename.Prefill)

	// Terminal
	TerminalMode = cfg.Terminal.Mode
	TerminalPosition = cfg.Terminal.Position
	TerminalHeight = cfg.Terminal.Height
	TerminalWidth = cfg.Terminal.Width
	TerminalStopInsert = itob(cfg.Terminal.StopInsert)

	// Test
	TestAutosave = itob(cfg.Test.Autosave)
	TestAll = itob(cfg.Test.AllPackage)
	TestFlags = cfg.Test.Flags

	// Debug
	DebugEnable = itob(cfg.Debug.Enable)
	DebugPprof = itob(cfg.Debug.Pprof)
}

func itob(i int64) bool { return i != int64(0) }
<|endoftext|>


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

// Package utf8string provides an efficient way to index strings by rune rather than by byte.
package utf8string

import (
	"errors"
	"unicode/utf8"
)

// String wraps a regular string with a small structure that provides more
// efficient indexing by code point index, as opposed to byte index.
// Scanning incrementally forwards or backwards is O(1) per index operation
// (although not as fast a range clause going forwards).  Random access is
// O(N) in the length of the string, but the overhead is less than always
// scanning from the beginning.
// If the string is ASCII, random access is O(1).
// Unlike the built-in string type, String has internal mutable state and
// is not thread-safe.
type String struct {
	str      string
	numRunes int
	// If width > 0, the rune at runePos starts at bytePos and has the specified width.
	width    int
	bytePos  int
	runePos  int
	nonASCII int // byte index of the first non-ASCII rune.
}

// NewString returns a new UTF-8 string with the provided contents.
func NewString(contents string) *String {
	return new(String).Init(contents)
}

// Init initializes an existing String to hold the provided contents.
// It returns a pointer to the initialized String.
func (s *String) Init(contents string) *String {
	s.str = contents
	s.bytePos = 0
	s.runePos = 0
	for i := 0; i < len(contents); i++ {
		if contents[i] >= utf8.RuneSelf {
			// Not ASCII.
			s.numRunes = utf8.RuneCountInString(contents)
			_, s.width = utf8.DecodeRuneInString(contents)
			s.nonASCII = i
			return s
		}
	}
	// ASCII is simple.  Also, the empty string is ASCII.
	s.numRunes = len(contents)
	s.width = 0
	s.nonASCII = len(contents)
	return s
}

// String returns the contents of the String.  This method also means the
// String is directly printable by fmt.Print.
func (s *String) String() string {
	return s.str
}

// RuneCount returns the number of runes (Unicode code points) in the String.
func (s *String) RuneCount() int {
	return s.numRunes
}

// IsASCII returns a boolean indicating whether the String contains only ASCII bytes.
func (s *String) IsASCII() bool {
	return s.width == 0
}

// Slice returns the string sliced at rune positions [i:j].
func (s *String) Slice(i, j int) string {
	// ASCII is easy.  Let the compiler catch the indexing error if there is one.
	if j < s.nonASCII {
		return s.str[i:j]
	}
	if i < 0 || j > s.numRunes || i > j {
		panic(sliceOutOfRange)
	}
	if i == j {
		return ""
	}
	// For non-ASCII, after At(i), bytePos is always the position of the indexed character.
	var low, high int
	switch {
	case i < s.nonASCII:
		low = i
	case i == s.numRunes:
		low = len(s.str)
	default:
		s.At(i)
		low = s.bytePos
	}
	switch {
	case j == s.numRunes:
		high = len(s.str)
	default:
		s.At(j)
		high = s.bytePos
	}
	return s.str[low:high]
}

// At returns the rune with index i in the String.  The sequence of runes is the same
// as iterating over the contents with a "for range" clause.
func (s *String) At(i int) rune {
	// ASCII is easy.  Let the compiler catch the indexing error if there is one.
	if i < s.nonASCII {
		return rune(s.str[i])
	}

	// Now we do need to know the index is valid.
	if i < 0 || i >= s.numRunes {
		panic(outOfRange)
	}

	var r rune

	// Five easy common cases: within 1 spot of bytePos/runePos, or the beginning, or the end.
	// With these cases, all scans from beginning or end work in O(1) time per rune.
	switch {

	case i == s.runePos-1: // backing up one rune
		r, s.width = utf8.DecodeLastRuneInString(s.str[0:s.bytePos])
		s.runePos = i
		s.bytePos -= s.width
		return r
	case i == s.runePos+1: // moving ahead one rune
		s.runePos = i
		s.bytePos += s.width
		fallthrough
	case i == s.runePos:
		r, s.width = utf8.DecodeRuneInString(s.str[s.bytePos:])
		return r
	case i == 0: // start of string
		r, s.width = utf8.DecodeRuneInString(s.str)
		s.runePos = 0
		s.bytePos = 0
		return r

	case i == s.numRunes-1: // last rune in string
		r, s.width = utf8.DecodeLastRuneInString(s.str)
		s.runePos = i
		s.bytePos = len(s.str) - s.width
		return r
	}

	// We need to do a linear scan.  There are three places to start from:
	// 1) The beginning
	// 2) bytePos/runePos.
	// 3) The end
	// Choose the closest in rune count, scanning backwards if necessary.
	forward := true
	if i < s.runePos {
		// Between beginning and pos.  Which is closer?
		// Since both i and runePos are guaranteed >= nonASCII, that's the
		// lowest location we need to start from.
		if i < (s.runePos-s.nonASCII)/2 {
			// Scan forward from beginning
			s.bytePos, s.runePos = s.nonASCII, s.nonASCII
		} else {
			// Scan backwards from where we are
			forward = false
		}
	} else {
		// Between pos and end.  Which is closer?
		if i-s.runePos < (s.numRunes-s.runePos)/2 {
			// Scan forward from pos
		} else {
			// Scan backwards from end
			s.bytePos, s.runePos = len(s.str), s.numRunes
			forward = false
		}
	}
	if forward {
		// TODO: Is it much faster to use a range loop for this scan?
		for {
			r, s.width = utf8.DecodeRuneInString(s.str[s.bytePos:])
			if s.runePos == i {
				break
			}
			s.runePos++
			s.bytePos += s.width
		}
	} else {
		for {
			r, s.width = utf8.DecodeLastRuneInString(s.str[0:s.bytePos])
			s.runePos--
			s.bytePos -= s.width
			if s.runePos == i {
				break
			}
		}
	}
	return r
}

var outOfRange = errors.New("utf8.String: index out of range")
var sliceOutOfRange = errors.New("utf8.String: slice index out of range")
<commit_msg>exp/utf8string: Correct package name in error messages<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package utf8string provides an efficient way to index strings by rune rather than by byte.
package utf8string

import (
	"errors"
	"unicode/utf8"
)

// String wraps a regular string with a small structure that provides more
// efficient indexing by code point index, as opposed to byte index.
// Scanning incrementally forwards or backwards is O(1) per index operation
// (although not as fast a range clause going forwards).  Random access is
// O(N) in the length of the string, but the overhead is less than always
// scanning from the beginning.
// If the string is ASCII, random access is O(1).
// Unlike the built-in string type, String has internal mutable state and
// is not thread-safe.
type String struct {
	str      string
	numRunes int
	// If width > 0, the rune at runePos starts at bytePos and has the specified width.
	width    int
	bytePos  int
	runePos  int
	nonASCII int // byte index of the first non-ASCII rune.
}

// NewString returns a new UTF-8 string with the provided contents.
func NewString(contents string) *String {
	return new(String).Init(contents)
}

// Init initializes an existing String to hold the provided contents.
// It returns a pointer to the initialized String.
func (s *String) Init(contents string) *String {
	s.str = contents
	s.bytePos = 0
	s.runePos = 0
	for i := 0; i < len(contents); i++ {
		if contents[i] >= utf8.RuneSelf {
			// Not ASCII.
			s.numRunes = utf8.RuneCountInString(contents)
			_, s.width = utf8.DecodeRuneInString(contents)
			s.nonASCII = i
			return s
		}
	}
	// ASCII is simple.  Also, the empty string is ASCII.
	s.numRunes = len(contents)
	s.width = 0
	s.nonASCII = len(contents)
	return s
}

// String returns the contents of the String.  This method also means the
// String is directly printable by fmt.Print.
func (s *String) String() string {
	return s.str
}

// RuneCount returns the number of runes (Unicode code points) in the String.
func (s *String) RuneCount() int {
	return s.numRunes
}

// IsASCII returns a boolean indicating whether the String contains only ASCII bytes.
func (s *String) IsASCII() bool {
	return s.width == 0
}

// Slice returns the string sliced at rune positions [i:j].
func (s *String) Slice(i, j int) string {
	// ASCII is easy.  Let the compiler catch the indexing error if there is one.
	if j < s.nonASCII {
		return s.str[i:j]
	}
	if i < 0 || j > s.numRunes || i > j {
		panic(sliceOutOfRange)
	}
	if i == j {
		return ""
	}
	// For non-ASCII, after At(i), bytePos is always the position of the indexed character.
	var low, high int
	switch {
	case i < s.nonASCII:
		low = i
	case i == s.numRunes:
		low = len(s.str)
	default:
		s.At(i)
		low = s.bytePos
	}
	switch {
	case j == s.numRunes:
		high = len(s.str)
	default:
		s.At(j)
		high = s.bytePos
	}
	return s.str[low:high]
}

// At returns the rune with index i in the String.  The sequence of runes is the same
// as iterating over the contents with a "for range" clause.
func (s *String) At(i int) rune {
	// ASCII is easy.  Let the compiler catch the indexing error if there is one.
	if i < s.nonASCII {
		return rune(s.str[i])
	}

	// Now we do need to know the index is valid.
	if i < 0 || i >= s.numRunes {
		panic(outOfRange)
	}

	var r rune

	// Five easy common cases: within 1 spot of bytePos/runePos, or the beginning, or the end.
	// With these cases, all scans from beginning or end work in O(1) time per rune.
	switch {

	case i == s.runePos-1: // backing up one rune
		r, s.width = utf8.DecodeLastRuneInString(s.str[0:s.bytePos])
		s.runePos = i
		s.bytePos -= s.width
		return r
	case i == s.runePos+1: // moving ahead one rune
		s.runePos = i
		s.bytePos += s.width
		fallthrough
	case i == s.runePos:
		r, s.width = utf8.DecodeRuneInString(s.str[s.bytePos:])
		return r
	case i == 0: // start of string
		r, s.width = utf8.DecodeRuneInString(s.str)
		s.runePos = 0
		s.bytePos = 0
		return r

	case i == s.numRunes-1: // last rune in string
		r, s.width = utf8.DecodeLastRuneInString(s.str)
		s.runePos = i
		s.bytePos = len(s.str) - s.width
		return r
	}

	// We need to do a linear scan.  There are three places to start from:
	// 1) The beginning
	// 2) bytePos/runePos.
	// 3) The end
	// Choose the closest in rune count, scanning backwards if necessary.
	forward := true
	if i < s.runePos {
		// Between beginning and pos.  Which is closer?
		// Since both i and runePos are guaranteed >= nonASCII, that's the
		// lowest location we need to start from.
		if i < (s.runePos-s.nonASCII)/2 {
			// Scan forward from beginning
			s.bytePos, s.runePos = s.nonASCII, s.nonASCII
		} else {
			// Scan backwards from where we are
			forward = false
		}
	} else {
		// Between pos and end.  Which is closer?
		if i-s.runePos < (s.numRunes-s.runePos)/2 {
			// Scan forward from pos
		} else {
			// Scan backwards from end
			s.bytePos, s.runePos = len(s.str), s.numRunes
			forward = false
		}
	}
	if forward {
		// TODO: Is it much faster to use a range loop for this scan?
		for {
			r, s.width = utf8.DecodeRuneInString(s.str[s.bytePos:])
			if s.runePos == i {
				break
			}
			s.runePos++
			s.bytePos += s.width
		}
	} else {
		for {
			r, s.width = utf8.DecodeLastRuneInString(s.str[0:s.bytePos])
			s.runePos--
			s.bytePos -= s.width
			if s.runePos == i {
				break
			}
		}
	}
	return r
}

var outOfRange = errors.New("utf8string: index out of range")
var sliceOutOfRange = errors.New("utf8string: slice index out of range")
<|endoftext|>