<commit_before>package puddle

import (
	"context"
	"errors"
	"sync"
)

const (
	resourceStatusCreating  = 0
	resourceStatusAvailable = iota
	resourceStatusBorrowed  = iota
	resourceStatusHijacked  = iota
)

const maxUint = ^uint(0)
const maxInt = int(maxUint >> 1)

// ErrClosedPool occurs on an attempt to get a connection from a closed pool.
var ErrClosedPool = errors.New("cannot get from closed pool")

type Constructor func(ctx context.Context) (res interface{}, err error)
type Destructor func(res interface{})

type Resource struct {
	value  interface{}
	pool   *Pool
	status byte
}

func (res *Resource) Value() interface{} {
	return res.value
}

func (res *Resource) Release() {
	if res.status != resourceStatusBorrowed {
		panic("tried to release resource that is not acquired")
	}
	res.pool.releaseBorrowedResource(res)
}

func (res *Resource) Destroy() {
	if res.status != resourceStatusBorrowed {
		panic("tried to destroy resource that is not acquired")
	}
	res.pool.destroyBorrowedResource(res)
}

// Hijack removes the resource from the pool without destroying it. Caller is
// responsible for cleanup of resource value.
func (res *Resource) Hijack() {
	if res.status != resourceStatusBorrowed {
		panic("tried to hijack resource that is not acquired")
	}
	res.pool.hijackBorrowedResource(res)
}

// Pool is a thread-safe resource pool.
type Pool struct {
	cond       *sync.Cond
	destructWG *sync.WaitGroup

	allResources       []*Resource
	availableResources []*Resource

	minSize int
	maxSize int
	closed  bool

	constructor Constructor
	destructor  Destructor
}

func NewPool(constructor Constructor, destructor Destructor) *Pool {
	return &Pool{
		cond:        sync.NewCond(new(sync.Mutex)),
		destructWG:  &sync.WaitGroup{},
		maxSize:     maxInt,
		constructor: constructor,
		destructor:  destructor,
	}
}

// Close closes all resources in the pool and rejects future Acquire calls.
// Blocks until all resources are returned to pool and closed.
func (p *Pool) Close() {
	p.cond.L.Lock()
	p.closed = true

	for _, res := range p.availableResources {
		p.allResources = removeResource(p.allResources, res)
		go p.destructResourceValue(res.value)
	}
	p.availableResources = nil
	p.cond.L.Unlock()

	// Wake up all go routines waiting for a resource to be returned so they can terminate.
	p.cond.Broadcast()

	p.destructWG.Wait()
}

// Size returns the current size of the pool.
func (p *Pool) Size() int {
	p.cond.L.Lock()
	n := len(p.allResources)
	p.cond.L.Unlock()
	return n
}

// MinSize returns the current minimum size of the pool.
func (p *Pool) MinSize() int {
	p.cond.L.Lock()
	n := p.minSize
	p.cond.L.Unlock()
	return n
}

// SetMinSize sets the minimum size of the pool. It panics if n < 0.
func (p *Pool) SetMinSize(n int) {
	if n < 0 {
		panic("pool MinSize cannot be < 0")
	}
	p.cond.L.Lock()
	p.minSize = n

	p.cond.L.Unlock()
}

// MaxSize returns the current maximum size of the pool.
func (p *Pool) MaxSize() int {
	p.cond.L.Lock()
	n := p.maxSize
	p.cond.L.Unlock()
	return n
}

// SetMaxSize sets the maximum size of the pool. It panics if n < 1.
func (p *Pool) SetMaxSize(n int) {
	if n < 1 {
		panic("pool MaxSize cannot be < 1")
	}
	p.cond.L.Lock()
	p.maxSize = n
	p.cond.L.Unlock()
}

// Acquire gets a resource from the pool. If no resources are available and the pool
// is not at maximum capacity it will create a new resource. If the pool is at
// maximum capacity it will block until a resource is available. ctx can be used
// to cancel the Acquire.
func (p *Pool) Acquire(ctx context.Context) (*Resource, error) {
	if doneChan := ctx.Done(); doneChan != nil {
		select {
		case <-ctx.Done():
			return nil, ctx.Err()
		default:
		}
	}

	p.cond.L.Lock()

	for {
		if p.closed {
			p.cond.L.Unlock()
			return nil, ErrClosedPool
		}

		// If a resource is available now
		if len(p.availableResources) > 0 {
			rw := p.lockedAvailableAcquire()
			p.cond.L.Unlock()
			return rw, nil
		}

		// If there is room to create a resource do so
		if len(p.allResources) < p.maxSize {
			res := &Resource{pool: p, status: resourceStatusCreating}
			p.allResources = append(p.allResources, res)
			p.cond.L.Unlock()

			value, err := p.constructResourceValue(ctx)
			p.cond.L.Lock()
			if err != nil {
				p.allResources = removeResource(p.allResources, res)
				p.cond.L.Unlock()
				return nil, err
			}

			res.value = value
			res.status = resourceStatusBorrowed
			p.cond.L.Unlock()
			return res, nil
		}

		if ctx.Done() == nil {
			p.cond.Wait()
		} else {
			// Convert p.cond.Wait into a channel
			waitChan := make(chan struct{}, 1)
			go func() {
				p.cond.Wait()
				waitChan <- struct{}{}
			}()

			select {
			case <-ctx.Done():
				// Allow goroutine waiting for signal to exit. Re-signal since we couldn't
				// do anything with it. Another goroutine might be waiting.
				go func() {
					<-waitChan
					p.cond.Signal()
					p.cond.L.Unlock()
				}()

				return nil, ctx.Err()
			case <-waitChan:
			}
		}
	}
}

// lockedAvailableAcquire gets the top resource from p.availableResources. p.cond.L
// must already be locked. len(p.availableResources) must be > 0.
func (p *Pool) lockedAvailableAcquire() *Resource {
	rw := p.availableResources[len(p.availableResources)-1]
	p.availableResources = p.availableResources[:len(p.availableResources)-1]
	if rw.status != resourceStatusAvailable {
		panic("BUG: unavailable resource gotten from availableResources")
	}
	rw.status = resourceStatusBorrowed
	return rw
}

// releaseBorrowedResource returns res to the the pool.
func (p *Pool) releaseBorrowedResource(res *Resource) {
	p.cond.L.Lock()

	if !p.closed {
		res.status = resourceStatusAvailable
		p.availableResources = append(p.availableResources, res)
	} else {
		p.allResources = removeResource(p.allResources, res)
		go p.destructResourceValue(res.value)
	}

	p.cond.L.Unlock()
	p.cond.Signal()
}

// Remove removes res from the pool and closes it. If res is not part of the
// pool Remove will panic.
func (p *Pool) destroyBorrowedResource(res *Resource) {
	p.cond.L.Lock()

	p.allResources = removeResource(p.allResources, res)
	go p.destructResourceValue(res.value)

	p.cond.L.Unlock()
	p.cond.Signal()
}

func (p *Pool) hijackBorrowedResource(res *Resource) {
	p.cond.L.Lock()

	p.allResources = removeResource(p.allResources, res)
	res.status = resourceStatusHijacked
	p.destructWG.Done() // not responsible for destructing hijacked resources

	p.cond.L.Unlock()
	p.cond.Signal()
}

func removeResource(slice []*Resource, res *Resource) []*Resource {
	for i := range slice {
		if slice[i] == res {
			slice[i] = slice[len(slice)-1]
			return slice[:len(slice)-1]
		}
	}

	return slice
}

func (p *Pool) constructResourceValue(ctx context.Context) (interface{}, error) {
	value, err := p.constructor(ctx)
	if err != nil {
		return nil, err
	}
	p.destructWG.Add(1)
	return value, nil
}

func (p *Pool) destructResourceValue(value interface{}) {
	p.destructor(value)
	p.destructWG.Done()
}
<commit_msg>Remove min size<commit_after>package puddle

import (
	"context"
	"errors"
	"sync"
)

const (
	resourceStatusCreating  = 0
	resourceStatusAvailable = iota
	resourceStatusBorrowed  = iota
	resourceStatusHijacked  = iota
)

const maxUint = ^uint(0)
const maxInt = int(maxUint >> 1)

// ErrClosedPool occurs on an attempt to get a connection from a closed pool.
var ErrClosedPool = errors.New("cannot get from closed pool")

type Constructor func(ctx context.Context) (res interface{}, err error)
type Destructor func(res interface{})

type Resource struct {
	value  interface{}
	pool   *Pool
	status byte
}

func (res *Resource) Value() interface{} {
	return res.value
}

func (res *Resource) Release() {
	if res.status != resourceStatusBorrowed {
		panic("tried to release resource that is not acquired")
	}
	res.pool.releaseBorrowedResource(res)
}

func (res *Resource) Destroy() {
	if res.status != resourceStatusBorrowed {
		panic("tried to destroy resource that is not acquired")
	}
	res.pool.destroyBorrowedResource(res)
}

// Hijack removes the resource from the pool without destroying it. Caller is
// responsible for cleanup of resource value.
func (res *Resource) Hijack() {
	if res.status != resourceStatusBorrowed {
		panic("tried to hijack resource that is not acquired")
	}
	res.pool.hijackBorrowedResource(res)
}

// Pool is a thread-safe resource pool.
type Pool struct {
	cond       *sync.Cond
	destructWG *sync.WaitGroup

	allResources       []*Resource
	availableResources []*Resource

	maxSize int
	closed  bool

	constructor Constructor
	destructor  Destructor
}

func NewPool(constructor Constructor, destructor Destructor) *Pool {
	return &Pool{
		cond:        sync.NewCond(new(sync.Mutex)),
		destructWG:  &sync.WaitGroup{},
		maxSize:     maxInt,
		constructor: constructor,
		destructor:  destructor,
	}
}

// Close closes all resources in the pool and rejects future Acquire calls.
// Blocks until all resources are returned to pool and closed.
func (p *Pool) Close() {
	p.cond.L.Lock()
	p.closed = true

	for _, res := range p.availableResources {
		p.allResources = removeResource(p.allResources, res)
		go p.destructResourceValue(res.value)
	}
	p.availableResources = nil
	p.cond.L.Unlock()

	// Wake up all go routines waiting for a resource to be returned so they can terminate.
	p.cond.Broadcast()

	p.destructWG.Wait()
}

// Size returns the current size of the pool.
func (p *Pool) Size() int {
	p.cond.L.Lock()
	n := len(p.allResources)
	p.cond.L.Unlock()
	return n
}

// MaxSize returns the current maximum size of the pool.
func (p *Pool) MaxSize() int {
	p.cond.L.Lock()
	n := p.maxSize
	p.cond.L.Unlock()
	return n
}

// SetMaxSize sets the maximum size of the pool. It panics if n < 1.
func (p *Pool) SetMaxSize(n int) {
	if n < 1 {
		panic("pool MaxSize cannot be < 1")
	}
	p.cond.L.Lock()
	p.maxSize = n
	p.cond.L.Unlock()
}

// Acquire gets a resource from the pool. If no resources are available and the pool
// is not at maximum capacity it will create a new resource. If the pool is at
// maximum capacity it will block until a resource is available. ctx can be used
// to cancel the Acquire.
func (p *Pool) Acquire(ctx context.Context) (*Resource, error) {
	if doneChan := ctx.Done(); doneChan != nil {
		select {
		case <-ctx.Done():
			return nil, ctx.Err()
		default:
		}
	}

	p.cond.L.Lock()

	for {
		if p.closed {
			p.cond.L.Unlock()
			return nil, ErrClosedPool
		}

		// If a resource is available now
		if len(p.availableResources) > 0 {
			rw := p.lockedAvailableAcquire()
			p.cond.L.Unlock()
			return rw, nil
		}

		// If there is room to create a resource do so
		if len(p.allResources) < p.maxSize {
			res := &Resource{pool: p, status: resourceStatusCreating}
			p.allResources = append(p.allResources, res)
			p.cond.L.Unlock()

			value, err := p.constructResourceValue(ctx)
			p.cond.L.Lock()
			if err != nil {
				p.allResources = removeResource(p.allResources, res)
				p.cond.L.Unlock()
				return nil, err
			}

			res.value = value
			res.status = resourceStatusBorrowed
			p.cond.L.Unlock()
			return res, nil
		}

		if ctx.Done() == nil {
			p.cond.Wait()
		} else {
			// Convert p.cond.Wait into a channel
			waitChan := make(chan struct{}, 1)
			go func() {
				p.cond.Wait()
				waitChan <- struct{}{}
			}()

			select {
			case <-ctx.Done():
				// Allow goroutine waiting for signal to exit. Re-signal since we couldn't
				// do anything with it. Another goroutine might be waiting.
				go func() {
					<-waitChan
					p.cond.Signal()
					p.cond.L.Unlock()
				}()

				return nil, ctx.Err()
			case <-waitChan:
			}
		}
	}
}

// lockedAvailableAcquire gets the top resource from p.availableResources. p.cond.L
// must already be locked. len(p.availableResources) must be > 0.
func (p *Pool) lockedAvailableAcquire() *Resource {
	rw := p.availableResources[len(p.availableResources)-1]
	p.availableResources = p.availableResources[:len(p.availableResources)-1]
	if rw.status != resourceStatusAvailable {
		panic("BUG: unavailable resource gotten from availableResources")
	}
	rw.status = resourceStatusBorrowed
	return rw
}

// releaseBorrowedResource returns res to the the pool.
func (p *Pool) releaseBorrowedResource(res *Resource) {
	p.cond.L.Lock()

	if !p.closed {
		res.status = resourceStatusAvailable
		p.availableResources = append(p.availableResources, res)
	} else {
		p.allResources = removeResource(p.allResources, res)
		go p.destructResourceValue(res.value)
	}

	p.cond.L.Unlock()
	p.cond.Signal()
}

// Remove removes res from the pool and closes it. If res is not part of the
// pool Remove will panic.
func (p *Pool) destroyBorrowedResource(res *Resource) {
	p.cond.L.Lock()

	p.allResources = removeResource(p.allResources, res)
	go p.destructResourceValue(res.value)

	p.cond.L.Unlock()
	p.cond.Signal()
}

func (p *Pool) hijackBorrowedResource(res *Resource) {
	p.cond.L.Lock()

	p.allResources = removeResource(p.allResources, res)
	res.status = resourceStatusHijacked
	p.destructWG.Done() // not responsible for destructing hijacked resources

	p.cond.L.Unlock()
	p.cond.Signal()
}

func removeResource(slice []*Resource, res *Resource) []*Resource {
	for i := range slice {
		if slice[i] == res {
			slice[i] = slice[len(slice)-1]
			return slice[:len(slice)-1]
		}
	}

	return slice
}

func (p *Pool) constructResourceValue(ctx context.Context) (interface{}, error) {
	value, err := p.constructor(ctx)
	if err != nil {
		return nil, err
	}
	p.destructWG.Add(1)
	return value, nil
}

func (p *Pool) destructResourceValue(value interface{}) {
	p.destructor(value)
	p.destructWG.Done()
}
<|endoftext|>


<commit_before>package app

import (
	"bytes"
	"code.google.com/p/go.tools/go/types"
	"fmt"
	"gnd.la/internal/gen/genutil"
	"gnd.la/loaders"
	"gnd.la/log"
	"gnd.la/util/yaml"
	"io/ioutil"
	"path/filepath"
	"regexp"
	"strings"
)

const appFilename = "appfile.yaml"

type Templates struct {
	Path  string            `yaml:"path"`
	Hooks map[string]string `yaml:"hooks"`
}

type Translations struct {
	Context string `yaml:"context"`
}

type App struct {
	Dir          string
	Name         string                 `yaml:"name"`
	Handlers     map[string]string      `yaml:"handlers"`
	Vars         map[string]interface{} `yaml:"vars"`
	Templates    *Templates             `yaml:"templates"`
	Translations *Translations          `yaml:"translations"`
	Assets       string                 `yaml:"assets"`
}

func (app *App) writeLoader(buf *bytes.Buffer, dir string, release bool) error {
	if release {
		return loaders.Bake(buf, dir, nil, loaders.CompressTgz)
	}
	abs, err := filepath.Abs(dir)
	if err != nil {
		return err
	}
	fmt.Fprintf(buf, "loaders.MemLoader(loaders.FSLoader(%q))\n", abs)
	return nil
}

func (app *App) Gen(release bool) error {
	pkg, err := genutil.NewPackage(app.Dir)
	if err != nil {
		return err
	}
	var buf bytes.Buffer
	fmt.Fprintf(&buf, "package %s\n\n", pkg.Name())
	buf.WriteString(genutil.AutogenString())
	buf.WriteString("import (\n\"gnd.la/app\"\n\"gnd.la/loaders\"\n\"gnd.la/template\"\n\"gnd.la/template/assets\"\n)\n")
	buf.WriteString("var _ = loaders.FSLoader\n")
	buf.WriteString("var _ = template.New\n")
	buf.WriteString("var _ = assets.NewManager\n")
	fmt.Fprintf(&buf, "var (\n App = app.New()\n)\n")
	buf.WriteString("func init() {\n")
	// TODO: Enable this when we have a solution for
	// executing templates from differnt apps from the
	// same *app.Context, which would need different
	// default translation contexts.
	//
	/*if app.Translations != nil && app.Translations.Context != "" {
		fmt.Fprintf(&buf, "App.TranslationContext = %q\n", app.Translations.Context)
	}*/
	fmt.Fprintf(&buf, "App.SetName(%q)\n", app.Name)
	if app.Assets != "" || (app.Templates != nil && app.Templates.Path != "" && len(app.Templates.Hooks) > 0) {
		buf.WriteString("var manager *assets.Manager\n")
	}
	if app.Assets != "" {
		buf.WriteString("assetsLoader := ")
		if err := app.writeLoader(&buf, filepath.Join(app.Dir, app.Assets), release); err != nil {
			return err
		}
		buf.WriteString("const prefix = \"/assets/\"\n")
		buf.WriteString("manager = assets.NewManager(assetsLoader, prefix)\n")
		buf.WriteString("App.SetAssetsManager(manager)\n")
		buf.WriteString("assetsHandler := assets.Handler(manager)\n")
		buf.WriteString("App.Handle(\"^\"+prefix, func(ctx *app.Context) { assetsHandler(ctx, ctx.R) })\n")
	}
	scope := pkg.Scope()
	if len(app.Vars) > 0 {
		buf.WriteString("App.AddTemplateVars(map[string]interface{}{\n")
		for k, v := range app.Vars {
			ident := k
			name := v
			if name == "" || name == nil {
				name = ident
			}
			obj := scope.Lookup(ident)
			if obj == nil {
				return fmt.Errorf("could not find identifier named %q", ident)
			}
			rhs := ident
			if va, ok := obj.(*types.Var); ok {
				tn := va.Type().String()
				if strings.Contains(tn, ".") {
					tn = "interface{}"
				}
				rhs = fmt.Sprintf("func() %s { return %s }", tn, ident)
			}
			fmt.Fprintf(&buf, "%q: %s,\n", name, rhs)
		}
		buf.WriteString("})\n")
	}
	for k, v := range app.Handlers {
		obj := scope.Lookup(k)
		if obj == nil {
			return fmt.Errorf("could not find handler named %q", k)
		}
		if _, err := regexp.Compile(v); err != nil {
			return fmt.Errorf("invalid pattern %q: %s", v, err)
		}
		switch obj.Type().String() {
		case "*gnd.la/app.HandlerInfo", "gnd.la/app.HandlerInfo":
			fmt.Fprintf(&buf, "App.HandleOptions(%q, %s.Handler, %s.Options)\n", v, obj.Name(), obj.Name())
		default:
			return fmt.Errorf("invalid handler type %s", obj.Type())
		}
	}
	if app.Templates != nil && app.Templates.Path != "" {
		buf.WriteString("templatesLoader := ")
		if err := app.writeLoader(&buf, filepath.Join(app.Dir, app.Templates.Path), release); err != nil {
			return err
		}
		buf.WriteString("App.SetTemplatesLoader(templatesLoader)\n")
		re := regexp.MustCompile("\\W")
		for k, v := range app.Templates.Hooks {
			var pos string
			switch strings.ToLower(v) {
			case "top":
				pos = "assets.Top"
			case "bottom":
				pos = "assets.Bottom"
			case "none":
				pos = "assets.None"
			default:
				return fmt.Errorf("invalid hook position %q", v)
			}
			suffix := re.ReplaceAllString(k, "_")
			name := fmt.Sprintf("tmpl_%s", suffix)
			fmt.Fprintf(&buf, "%s := template.New(templatesLoader, manager)\n", name)
			fmt.Fprintf(&buf, "%s.Funcs(map[string]interface{}{\n", name)
			funcNames := []string{"t", "tn", "tc", "tnc", "reverse"}
			for _, v := range funcNames {
				fmt.Fprintf(&buf, "\"%s\": func(_ ...interface{}) interface{} { return nil },\n", v)
			}
			buf.WriteString("})\n")
			fmt.Fprintf(&buf, "if err := %s.Parse(%q); err != nil {\npanic(err)\n}\n", name, k)
			fmt.Fprintf(&buf, "App.AddHook(&template.Hook{Template: %s, Position: %s})\n", name, pos)
		}
	}
	buf.WriteString("}\n")
	out := filepath.Join(pkg.Dir(), "gondola_app.go")
	log.Debugf("Writing Gondola app to %s", out)
	return genutil.WriteAutogen(out, buf.Bytes())
}

func Parse(dir string) (*App, error) {
	appFile := filepath.Join(dir, appFilename)
	data, err := ioutil.ReadFile(appFile)
	if err != nil {
		return nil, fmt.Errorf("error reading %s: %s", appFilename, err)
	}
	var app *App
	if err := yaml.Unmarshal(data, &app); err != nil {
		return nil, err
	}
	app.Dir = dir
	return app, nil
}
<commit_msg>Add support for exporting template functions from appfile.yaml<commit_after>package app

import (
	"bytes"
	"code.google.com/p/go.tools/go/types"
	"fmt"
	"gnd.la/internal/gen/genutil"
	"gnd.la/loaders"
	"gnd.la/log"
	"gnd.la/util/yaml"
	"io/ioutil"
	"path/filepath"
	"regexp"
	"strings"
)

const appFilename = "appfile.yaml"

type Templates struct {
	Path      string            `yaml:"path"`
	Functions map[string]string `yaml:"functions"`
	Hooks     map[string]string `yaml:"hooks"`
}

type Translations struct {
	Context string `yaml:"context"`
}

type App struct {
	Dir          string
	Name         string                 `yaml:"name"`
	Handlers     map[string]string      `yaml:"handlers"`
	Vars         map[string]interface{} `yaml:"vars"`
	Templates    *Templates             `yaml:"templates"`
	Translations *Translations          `yaml:"translations"`
	Assets       string                 `yaml:"assets"`
}

func (app *App) writeLoader(buf *bytes.Buffer, dir string, release bool) error {
	if release {
		return loaders.Bake(buf, dir, nil, loaders.CompressTgz)
	}
	abs, err := filepath.Abs(dir)
	if err != nil {
		return err
	}
	fmt.Fprintf(buf, "loaders.MemLoader(loaders.FSLoader(%q))\n", abs)
	return nil
}

func (app *App) Gen(release bool) error {
	pkg, err := genutil.NewPackage(app.Dir)
	if err != nil {
		return err
	}
	var buf bytes.Buffer
	fmt.Fprintf(&buf, "package %s\n\n", pkg.Name())
	buf.WriteString(genutil.AutogenString())
	buf.WriteString("import (\n\"gnd.la/app\"\n\"gnd.la/loaders\"\n\"gnd.la/template\"\n\"gnd.la/template/assets\"\n)\n")
	buf.WriteString("var _ = loaders.FSLoader\n")
	buf.WriteString("var _ = template.New\n")
	buf.WriteString("var _ = assets.NewManager\n")
	fmt.Fprintf(&buf, "var (\n App = app.New()\n)\n")
	buf.WriteString("func init() {\n")
	// TODO: Enable this when we have a solution for
	// executing templates from differnt apps from the
	// same *app.Context, which would need different
	// default translation contexts.
	//
	/*if app.Translations != nil && app.Translations.Context != "" {
		fmt.Fprintf(&buf, "App.TranslationContext = %q\n", app.Translations.Context)
	}*/
	fmt.Fprintf(&buf, "App.SetName(%q)\n", app.Name)
	if app.Assets != "" || (app.Templates != nil && app.Templates.Path != "" && len(app.Templates.Hooks) > 0) {
		buf.WriteString("var manager *assets.Manager\n")
	}
	if app.Assets != "" {
		buf.WriteString("assetsLoader := ")
		if err := app.writeLoader(&buf, filepath.Join(app.Dir, app.Assets), release); err != nil {
			return err
		}
		buf.WriteString("const prefix = \"/assets/\"\n")
		buf.WriteString("manager = assets.NewManager(assetsLoader, prefix)\n")
		buf.WriteString("App.SetAssetsManager(manager)\n")
		buf.WriteString("assetsHandler := assets.Handler(manager)\n")
		buf.WriteString("App.Handle(\"^\"+prefix, func(ctx *app.Context) { assetsHandler(ctx, ctx.R) })\n")
	}
	scope := pkg.Scope()
	if len(app.Vars) > 0 {
		buf.WriteString("App.AddTemplateVars(map[string]interface{}{\n")
		for k, v := range app.Vars {
			ident := k
			name := v
			if name == "" || name == nil {
				name = ident
			}
			obj := scope.Lookup(ident)
			if obj == nil {
				return fmt.Errorf("could not find identifier named %q", ident)
			}
			rhs := ident
			if va, ok := obj.(*types.Var); ok {
				tn := va.Type().String()
				if strings.Contains(tn, ".") {
					tn = "interface{}"
				}
				rhs = fmt.Sprintf("func() %s { return %s }", tn, ident)
			}
			fmt.Fprintf(&buf, "%q: %s,\n", name, rhs)
		}
		buf.WriteString("})\n")
	}
	for k, v := range app.Handlers {
		obj := scope.Lookup(k)
		if obj == nil {
			return fmt.Errorf("could not find handler named %q", k)
		}
		if _, err := regexp.Compile(v); err != nil {
			return fmt.Errorf("invalid pattern %q: %s", v, err)
		}
		switch obj.Type().String() {
		case "*gnd.la/app.HandlerInfo", "gnd.la/app.HandlerInfo":
			fmt.Fprintf(&buf, "App.HandleOptions(%q, %s.Handler, %s.Options)\n", v, obj.Name(), obj.Name())
		default:
			return fmt.Errorf("invalid handler type %s", obj.Type())
		}
	}
	if app.Templates != nil {
		if len(app.Templates.Functions) > 0 {
			buf.WriteString("template.AddFuncs(template.FuncMap{\n")
			for k, v := range app.Templates.Functions {
				obj := scope.Lookup(v)
				if obj == nil {
					return fmt.Errorf("could not find function named %q for template function %q", v, k)
				}
				fmt.Fprintf(&buf, "%q: %s,\n", k, v)
			}
			buf.WriteString("})\n")
		}
		if app.Templates.Path != "" {
			buf.WriteString("templatesLoader := ")
			if err := app.writeLoader(&buf, filepath.Join(app.Dir, app.Templates.Path), release); err != nil {
				return err
			}
			buf.WriteString("App.SetTemplatesLoader(templatesLoader)\n")
			re := regexp.MustCompile("\\W")
			for k, v := range app.Templates.Hooks {
				var pos string
				switch strings.ToLower(v) {
				case "top":
					pos = "assets.Top"
				case "bottom":
					pos = "assets.Bottom"
				case "none":
					pos = "assets.None"
				default:
					return fmt.Errorf("invalid hook position %q", v)
				}
				suffix := re.ReplaceAllString(k, "_")
				name := fmt.Sprintf("tmpl_%s", suffix)
				fmt.Fprintf(&buf, "%s := template.New(templatesLoader, manager)\n", name)
				fmt.Fprintf(&buf, "%s.Funcs(map[string]interface{}{\n", name)
				funcNames := []string{"t", "tn", "tc", "tnc", "reverse"}
				for _, v := range funcNames {
					fmt.Fprintf(&buf, "\"%s\": func(_ ...interface{}) interface{} { return nil },\n", v)
				}
				buf.WriteString("})\n")
				fmt.Fprintf(&buf, "if err := %s.Parse(%q); err != nil {\npanic(err)\n}\n", name, k)
				fmt.Fprintf(&buf, "App.AddHook(&template.Hook{Template: %s, Position: %s})\n", name, pos)
			}
		}
	}
	buf.WriteString("}\n")
	out := filepath.Join(pkg.Dir(), "gondola_app.go")
	log.Debugf("Writing Gondola app to %s", out)
	return genutil.WriteAutogen(out, buf.Bytes())
}

func Parse(dir string) (*App, error) {
	appFile := filepath.Join(dir, appFilename)
	data, err := ioutil.ReadFile(appFile)
	if err != nil {
		return nil, fmt.Errorf("error reading %s: %s", appFilename, err)
	}
	var app *App
	if err := yaml.Unmarshal(data, &app); err != nil {
		return nil, err
	}
	app.Dir = dir
	return app, nil
}
<|endoftext|>


<commit_before>// Copyright 2018 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 cmd handles the gopls command line.
// It contains a handler for each of the modes, along with all the flag handling
// and the command line output format.
package cmd

import (
	"context"
	"flag"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"io/ioutil"
	"log"
	"net"
	"os"
	"strings"

	"golang.org/x/tools/go/packages"
	"golang.org/x/tools/internal/jsonrpc2"
	"golang.org/x/tools/internal/lsp"
	"golang.org/x/tools/internal/lsp/protocol"
	"golang.org/x/tools/internal/span"
	"golang.org/x/tools/internal/tool"
)

// Application is the main application as passed to tool.Main
// It handles the main command line parsing and dispatch to the sub commands.
type Application struct {
	// Core application flags

	// Embed the basic profiling flags supported by the tool package
	tool.Profile

	// We include the server configuration directly for now, so the flags work
	// even without the verb.
	// TODO: Remove this when we stop allowing the serve verb by default.
	Serve Serve

	// An initial, common go/packages configuration
	Config packages.Config

	// Support for remote lsp server
	Remote string `flag:"remote" help:"*EXPERIMENTAL* - forward all commands to a remote lsp"`
}

// Name implements tool.Application returning the binary name.
func (app *Application) Name() string { return "gopls" }

// Usage implements tool.Application returning empty extra argument usage.
func (app *Application) Usage() string { return "<command> [command-flags] [command-args]" }

// ShortHelp implements tool.Application returning the main binary help.
func (app *Application) ShortHelp() string {
	return "The Go Language source tools."
}

// DetailedHelp implements tool.Application returning the main binary help.
// This includes the short help for all the sub commands.
func (app *Application) DetailedHelp(f *flag.FlagSet) {
	fmt.Fprint(f.Output(), `
Available commands are:
`)
	for _, c := range app.commands() {
		fmt.Fprintf(f.Output(), "  %s : %v\n", c.Name(), c.ShortHelp())
	}
	fmt.Fprint(f.Output(), `
gopls flags are:
`)
	f.PrintDefaults()
}

// Run takes the args after top level flag processing, and invokes the correct
// sub command as specified by the first argument.
// If no arguments are passed it will invoke the server sub command, as a
// temporary measure for compatibility.
func (app *Application) Run(ctx context.Context, args ...string) error {
	app.Serve.app = app
	if len(args) == 0 {
		tool.Main(ctx, &app.Serve, args)
		return nil
	}
	if app.Config.Dir == "" {
		if wd, err := os.Getwd(); err == nil {
			app.Config.Dir = wd
		}
	}
	app.Config.Mode = packages.LoadSyntax
	app.Config.Tests = true
	if app.Config.Fset == nil {
		app.Config.Fset = token.NewFileSet()
	}
	app.Config.Context = ctx
	app.Config.ParseFile = func(fset *token.FileSet, filename string, src []byte) (*ast.File, error) {
		return parser.ParseFile(fset, filename, src, parser.AllErrors|parser.ParseComments)
	}
	command, args := args[0], args[1:]
	for _, c := range app.commands() {
		if c.Name() == command {
			tool.Main(ctx, c, args)
			return nil
		}
	}
	return tool.CommandLineErrorf("Unknown command %v", command)
}

// commands returns the set of commands supported by the gopls tool on the
// command line.
// The command is specified by the first non flag argument.
func (app *Application) commands() []tool.Application {
	return []tool.Application{
		&app.Serve,
		&query{app: app},
		&check{app: app},
	}
}

type cmdClient interface {
	protocol.Client

	prepare(app *Application, server protocol.Server)
}

func (app *Application) connect(ctx context.Context, client cmdClient) (protocol.Server, error) {
	var server protocol.Server
	switch app.Remote {
	case "":
		server = lsp.NewClientServer(client)
	case "internal":
		cr, sw, _ := os.Pipe()
		sr, cw, _ := os.Pipe()
		var jc *jsonrpc2.Conn
		jc, server, _ = protocol.NewClient(jsonrpc2.NewHeaderStream(cr, cw), client)
		go jc.Run(ctx)
		go lsp.NewServer(jsonrpc2.NewHeaderStream(sr, sw)).Run(ctx)
	default:
		conn, err := net.Dial("tcp", app.Remote)
		if err != nil {
			return nil, err
		}
		stream := jsonrpc2.NewHeaderStream(conn, conn)
		var jc *jsonrpc2.Conn
		jc, server, _ = protocol.NewClient(stream, client)
		if err != nil {
			return nil, err
		}
		go jc.Run(ctx)
	}

	params := &protocol.InitializeParams{}
	params.RootURI = string(span.FileURI(app.Config.Dir))
	params.Capabilities.Workspace.Configuration = true
	params.Capabilities.TextDocument.Hover.ContentFormat = []protocol.MarkupKind{protocol.PlainText}

	client.prepare(app, server)
	if _, err := server.Initialize(ctx, params); err != nil {
		return nil, err
	}
	if err := server.Initialized(ctx, &protocol.InitializedParams{}); err != nil {
		return nil, err
	}
	return server, nil
}

type baseClient struct {
	protocol.Server
	app    *Application
	server protocol.Server
	fset   *token.FileSet
}

func (c *baseClient) ShowMessage(ctx context.Context, p *protocol.ShowMessageParams) error { return nil }
func (c *baseClient) ShowMessageRequest(ctx context.Context, p *protocol.ShowMessageRequestParams) (*protocol.MessageActionItem, error) {
	return nil, nil
}
func (c *baseClient) LogMessage(ctx context.Context, p *protocol.LogMessageParams) error {
	switch p.Type {
	case protocol.Error:
		log.Print("Error:", p.Message)
	case protocol.Warning:
		log.Print("Warning:", p.Message)
	case protocol.Info:
		log.Print("Info:", p.Message)
	case protocol.Log:
		log.Print("Log:", p.Message)
	default:
		log.Print(p.Message)
	}
	return nil
}
func (c *baseClient) Telemetry(ctx context.Context, t interface{}) error { return nil }
func (c *baseClient) RegisterCapability(ctx context.Context, p *protocol.RegistrationParams) error {
	return nil
}
func (c *baseClient) UnregisterCapability(ctx context.Context, p *protocol.UnregistrationParams) error {
	return nil
}
func (c *baseClient) WorkspaceFolders(ctx context.Context) ([]protocol.WorkspaceFolder, error) {
	return nil, nil
}
func (c *baseClient) Configuration(ctx context.Context, p *protocol.ConfigurationParams) ([]interface{}, error) {
	results := make([]interface{}, len(p.Items))
	for i, item := range p.Items {
		if item.Section != "gopls" {
			continue
		}
		env := map[string]interface{}{}
		for _, value := range c.app.Config.Env {
			l := strings.SplitN(value, "=", 2)
			if len(l) != 2 {
				continue
			}
			env[l[0]] = l[1]
		}
		results[i] = map[string]interface{}{"env": env}
	}
	return results, nil
}
func (c *baseClient) ApplyEdit(ctx context.Context, p *protocol.ApplyWorkspaceEditParams) (bool, error) {
	return false, nil
}
func (c *baseClient) PublishDiagnostics(ctx context.Context, p *protocol.PublishDiagnosticsParams) error {
	return nil
}

func (c *baseClient) prepare(app *Application, server protocol.Server) {
	c.app = app
	c.server = server
	c.fset = token.NewFileSet()
}

func (c *baseClient) AddFile(ctx context.Context, uri span.URI) (*protocol.ColumnMapper, error) {
	fname, err := uri.Filename()
	if err != nil {
		return nil, fmt.Errorf("%v: %v", uri, err)
	}
	content, err := ioutil.ReadFile(fname)
	if err != nil {
		return nil, fmt.Errorf("%v: %v", uri, err)
	}
	f := c.fset.AddFile(fname, -1, len(content))
	f.SetLinesForContent(content)
	m := protocol.NewColumnMapper(uri, c.fset, f, content)
	p := &protocol.DidOpenTextDocumentParams{}
	p.TextDocument.URI = string(uri)
	p.TextDocument.Text = string(content)
	if err := c.server.DidOpen(ctx, p); err != nil {
		return nil, fmt.Errorf("%v: %v", uri, err)
	}
	return m, nil
}
<commit_msg>internal/lsp: fix error handling in cmd<commit_after>// Copyright 2018 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 cmd handles the gopls command line.
// It contains a handler for each of the modes, along with all the flag handling
// and the command line output format.
package cmd

import (
	"context"
	"flag"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"io/ioutil"
	"log"
	"net"
	"os"
	"strings"

	"golang.org/x/tools/go/packages"
	"golang.org/x/tools/internal/jsonrpc2"
	"golang.org/x/tools/internal/lsp"
	"golang.org/x/tools/internal/lsp/protocol"
	"golang.org/x/tools/internal/span"
	"golang.org/x/tools/internal/tool"
)

// Application is the main application as passed to tool.Main
// It handles the main command line parsing and dispatch to the sub commands.
type Application struct {
	// Core application flags

	// Embed the basic profiling flags supported by the tool package
	tool.Profile

	// We include the server configuration directly for now, so the flags work
	// even without the verb.
	// TODO: Remove this when we stop allowing the serve verb by default.
	Serve Serve

	// An initial, common go/packages configuration
	Config packages.Config

	// Support for remote lsp server
	Remote string `flag:"remote" help:"*EXPERIMENTAL* - forward all commands to a remote lsp"`
}

// Name implements tool.Application returning the binary name.
func (app *Application) Name() string { return "gopls" }

// Usage implements tool.Application returning empty extra argument usage.
func (app *Application) Usage() string { return "<command> [command-flags] [command-args]" }

// ShortHelp implements tool.Application returning the main binary help.
func (app *Application) ShortHelp() string {
	return "The Go Language source tools."
}

// DetailedHelp implements tool.Application returning the main binary help.
// This includes the short help for all the sub commands.
func (app *Application) DetailedHelp(f *flag.FlagSet) {
	fmt.Fprint(f.Output(), `
Available commands are:
`)
	for _, c := range app.commands() {
		fmt.Fprintf(f.Output(), "  %s : %v\n", c.Name(), c.ShortHelp())
	}
	fmt.Fprint(f.Output(), `
gopls flags are:
`)
	f.PrintDefaults()
}

// Run takes the args after top level flag processing, and invokes the correct
// sub command as specified by the first argument.
// If no arguments are passed it will invoke the server sub command, as a
// temporary measure for compatibility.
func (app *Application) Run(ctx context.Context, args ...string) error {
	app.Serve.app = app
	if len(args) == 0 {
		tool.Main(ctx, &app.Serve, args)
		return nil
	}
	if app.Config.Dir == "" {
		if wd, err := os.Getwd(); err == nil {
			app.Config.Dir = wd
		}
	}
	app.Config.Mode = packages.LoadSyntax
	app.Config.Tests = true
	if app.Config.Fset == nil {
		app.Config.Fset = token.NewFileSet()
	}
	app.Config.Context = ctx
	app.Config.ParseFile = func(fset *token.FileSet, filename string, src []byte) (*ast.File, error) {
		return parser.ParseFile(fset, filename, src, parser.AllErrors|parser.ParseComments)
	}
	command, args := args[0], args[1:]
	for _, c := range app.commands() {
		if c.Name() == command {
			tool.Main(ctx, c, args)
			return nil
		}
	}
	return tool.CommandLineErrorf("Unknown command %v", command)
}

// commands returns the set of commands supported by the gopls tool on the
// command line.
// The command is specified by the first non flag argument.
func (app *Application) commands() []tool.Application {
	return []tool.Application{
		&app.Serve,
		&query{app: app},
		&check{app: app},
	}
}

type cmdClient interface {
	protocol.Client

	prepare(app *Application, server protocol.Server)
}

func (app *Application) connect(ctx context.Context, client cmdClient) (protocol.Server, error) {
	var server protocol.Server
	switch app.Remote {
	case "":
		server = lsp.NewClientServer(client)
	case "internal":
		cr, sw, _ := os.Pipe()
		sr, cw, _ := os.Pipe()
		var jc *jsonrpc2.Conn
		jc, server, _ = protocol.NewClient(jsonrpc2.NewHeaderStream(cr, cw), client)
		go jc.Run(ctx)
		go lsp.NewServer(jsonrpc2.NewHeaderStream(sr, sw)).Run(ctx)
	default:
		conn, err := net.Dial("tcp", app.Remote)
		if err != nil {
			return nil, err
		}
		stream := jsonrpc2.NewHeaderStream(conn, conn)
		var jc *jsonrpc2.Conn
		jc, server, _ = protocol.NewClient(stream, client)
		go jc.Run(ctx)
	}

	params := &protocol.InitializeParams{}
	params.RootURI = string(span.FileURI(app.Config.Dir))
	params.Capabilities.Workspace.Configuration = true
	params.Capabilities.TextDocument.Hover.ContentFormat = []protocol.MarkupKind{protocol.PlainText}

	client.prepare(app, server)
	if _, err := server.Initialize(ctx, params); err != nil {
		return nil, err
	}
	if err := server.Initialized(ctx, &protocol.InitializedParams{}); err != nil {
		return nil, err
	}
	return server, nil
}

type baseClient struct {
	protocol.Server
	app    *Application
	server protocol.Server
	fset   *token.FileSet
}

func (c *baseClient) ShowMessage(ctx context.Context, p *protocol.ShowMessageParams) error { return nil }
func (c *baseClient) ShowMessageRequest(ctx context.Context, p *protocol.ShowMessageRequestParams) (*protocol.MessageActionItem, error) {
	return nil, nil
}
func (c *baseClient) LogMessage(ctx context.Context, p *protocol.LogMessageParams) error {
	switch p.Type {
	case protocol.Error:
		log.Print("Error:", p.Message)
	case protocol.Warning:
		log.Print("Warning:", p.Message)
	case protocol.Info:
		log.Print("Info:", p.Message)
	case protocol.Log:
		log.Print("Log:", p.Message)
	default:
		log.Print(p.Message)
	}
	return nil
}
func (c *baseClient) Telemetry(ctx context.Context, t interface{}) error { return nil }
func (c *baseClient) RegisterCapability(ctx context.Context, p *protocol.RegistrationParams) error {
	return nil
}
func (c *baseClient) UnregisterCapability(ctx context.Context, p *protocol.UnregistrationParams) error {
	return nil
}
func (c *baseClient) WorkspaceFolders(ctx context.Context) ([]protocol.WorkspaceFolder, error) {
	return nil, nil
}
func (c *baseClient) Configuration(ctx context.Context, p *protocol.ConfigurationParams) ([]interface{}, error) {
	results := make([]interface{}, len(p.Items))
	for i, item := range p.Items {
		if item.Section != "gopls" {
			continue
		}
		env := map[string]interface{}{}
		for _, value := range c.app.Config.Env {
			l := strings.SplitN(value, "=", 2)
			if len(l) != 2 {
				continue
			}
			env[l[0]] = l[1]
		}
		results[i] = map[string]interface{}{"env": env}
	}
	return results, nil
}
func (c *baseClient) ApplyEdit(ctx context.Context, p *protocol.ApplyWorkspaceEditParams) (bool, error) {
	return false, nil
}
func (c *baseClient) PublishDiagnostics(ctx context.Context, p *protocol.PublishDiagnosticsParams) error {
	return nil
}

func (c *baseClient) prepare(app *Application, server protocol.Server) {
	c.app = app
	c.server = server
	c.fset = token.NewFileSet()
}

func (c *baseClient) AddFile(ctx context.Context, uri span.URI) (*protocol.ColumnMapper, error) {
	fname, err := uri.Filename()
	if err != nil {
		return nil, fmt.Errorf("%v: %v", uri, err)
	}
	content, err := ioutil.ReadFile(fname)
	if err != nil {
		return nil, fmt.Errorf("%v: %v", uri, err)
	}
	f := c.fset.AddFile(fname, -1, len(content))
	f.SetLinesForContent(content)
	m := protocol.NewColumnMapper(uri, c.fset, f, content)
	p := &protocol.DidOpenTextDocumentParams{}
	p.TextDocument.URI = string(uri)
	p.TextDocument.Text = string(content)
	if err := c.server.DidOpen(ctx, p); err != nil {
		return nil, fmt.Errorf("%v: %v", uri, err)
	}
	return m, nil
}
<|endoftext|>


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

package prefix

import (
	"bytes"
	"io"
	"os"
	"strings"
)

// For some of the common Readers, we wrap and extend them to satisfy the
// compress.BufferedReader interface to improve performance.

type buffer struct {
	*bytes.Buffer
}

type bytesReader struct {
	*bytes.Reader
	pos int64
	buf []byte
	arr [512]byte
}

type stringReader struct {
	*strings.Reader
	pos int64
	buf []byte
	arr [512]byte
}

func (r *buffer) Buffered() int {
	return r.Len()
}

func (r *buffer) Peek(n int) ([]byte, error) {
	b := r.Bytes()
	if len(b) < n {
		return b, io.EOF
	}
	return b[:n], nil
}

func (r *buffer) Discard(n int) (int, error) {
	b := r.Next(n)
	if len(b) < n {
		return len(b), io.EOF
	}
	return n, nil
}

func (r *bytesReader) Buffered() int {
	if r.Len() > len(r.arr) {
		return len(r.arr)
	}
	return r.Len()
}

func (r *bytesReader) Peek(n int) ([]byte, error) {
	if n > len(r.arr) {
		return nil, io.ErrShortBuffer
	}

	pos, _ := r.Seek(0, os.SEEK_CUR)
	if off := pos - r.pos; off > 0 && off < int64(len(r.buf)) {
		r.buf, r.pos = r.buf[off:], pos
	}
	if len(r.buf) >= n && r.pos == pos {
		return r.buf[:n], nil
	}

	cnt, err := r.ReadAt(r.arr[:], pos)
	r.buf, r.pos = r.arr[:cnt], pos
	if cnt < n {
		return r.arr[:cnt], err
	}
	return r.arr[:n], nil
}

func (r *bytesReader) Discard(n int) (int, error) {
	var err error
	if n > r.Len() {
		n, err = r.Len(), io.EOF
	}
	r.Seek(int64(n), os.SEEK_CUR)
	return n, err
}

func (r *stringReader) Buffered() int {
	if r.Len() > len(r.arr) {
		return len(r.arr)
	}
	return r.Len()
}

func (r *stringReader) Peek(n int) ([]byte, error) {
	if n > len(r.arr) {
		return nil, io.ErrShortBuffer
	}

	pos, _ := r.Seek(0, os.SEEK_CUR)
	if off := pos - r.pos; off > 0 && off < int64(len(r.buf)) {
		r.buf, r.pos = r.buf[off:], pos
	}
	if n <= len(r.buf) && pos == r.pos {
		return r.buf[:n], nil
	}

	cnt, err := r.ReadAt(r.arr[:], pos)
	r.buf, r.pos = r.arr[:cnt], pos
	if cnt < n {
		return r.arr[:cnt], err
	}
	return r.arr[:n], nil
}

func (r *stringReader) Discard(n int) (int, error) {
	var err error
	if n > r.Len() {
		n, err = r.Len(), io.EOF
	}
	r.Seek(int64(n), os.SEEK_CUR)
	return n, err
}
<commit_msg>internal/prefix: report correct Buffered<commit_after>// Copyright 2015, Joe Tsai. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE.md file.

package prefix

import (
	"bytes"
	"io"
	"os"
	"strings"
)

// For some of the common Readers, we wrap and extend them to satisfy the
// compress.BufferedReader interface to improve performance.

type buffer struct {
	*bytes.Buffer
}

type bytesReader struct {
	*bytes.Reader
	pos int64
	buf []byte
	arr [512]byte
}

type stringReader struct {
	*strings.Reader
	pos int64
	buf []byte
	arr [512]byte
}

func (r *buffer) Buffered() int {
	return r.Len()
}

func (r *buffer) Peek(n int) ([]byte, error) {
	b := r.Bytes()
	if len(b) < n {
		return b, io.EOF
	}
	return b[:n], nil
}

func (r *buffer) Discard(n int) (int, error) {
	b := r.Next(n)
	if len(b) < n {
		return len(b), io.EOF
	}
	return n, nil
}

func (r *bytesReader) Buffered() int {
	if r.Len() > len(r.buf) {
		return len(r.buf)
	}
	return r.Len()
}

func (r *bytesReader) Peek(n int) ([]byte, error) {
	if n > len(r.arr) {
		return nil, io.ErrShortBuffer
	}

	// Return sub-slice of local buffer if possible.
	pos, _ := r.Seek(0, os.SEEK_CUR)
	if off := pos - r.pos; off > 0 && off < int64(len(r.buf)) {
		r.buf, r.pos = r.buf[off:], pos
	}
	if len(r.buf) >= n && r.pos == pos {
		return r.buf[:n], nil
	}

	// Fill entire local buffer, and return appropriate sub-slice.
	cnt, err := r.ReadAt(r.arr[:], pos)
	r.buf, r.pos = r.arr[:cnt], pos
	if cnt < n {
		return r.arr[:cnt], err
	}
	return r.arr[:n], nil
}

func (r *bytesReader) Discard(n int) (int, error) {
	var err error
	if n > r.Len() {
		n, err = r.Len(), io.EOF
	}
	r.Seek(int64(n), os.SEEK_CUR)
	return n, err
}

func (r *stringReader) Buffered() int {
	if r.Len() > len(r.buf) {
		return len(r.buf)
	}
	return r.Len()
}

func (r *stringReader) Peek(n int) ([]byte, error) {
	if n > len(r.arr) {
		return nil, io.ErrShortBuffer
	}

	// Return sub-slice of local buffer if possible.
	pos, _ := r.Seek(0, os.SEEK_CUR)
	if off := pos - r.pos; off > 0 && off < int64(len(r.buf)) {
		r.buf, r.pos = r.buf[off:], pos
	}
	if len(r.buf) >= n && r.pos == pos {
		return r.buf[:n], nil
	}

	// Fill entire local buffer, and return appropriate sub-slice.
	cnt, err := r.ReadAt(r.arr[:], pos)
	r.buf, r.pos = r.arr[:cnt], pos
	if cnt < n {
		return r.arr[:cnt], err
	}
	return r.arr[:n], nil
}

func (r *stringReader) Discard(n int) (int, error) {
	var err error
	if n > r.Len() {
		n, err = r.Len(), io.EOF
	}
	r.Seek(int64(n), os.SEEK_CUR)
	return n, err
}
<|endoftext|>


<commit_before>package charm

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"launchpad.net/juju/go/log"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
)

// InfoResponse is sent by the charm store in response to charm-info requests.
type InfoResponse struct {
	Revision int      `json:"revision"` // Zero is valid. Can't omitempty.
	Sha256   string   `json:"sha256,omitempty"`
	Errors   []string `json:"errors,omitempty"`
	Warnings []string `json:"warnings,omitempty"`
}

// Repository respresents a collection of charms.
type Repository interface {
	Get(curl *URL) (Charm, error)
	Latest(curl *URL) (int, error)
}

// store is a Repository that talks to the juju charm server (in ../store).
type store struct {
	baseURL   string
	cachePath string
}

const (
	storeURL  = "https://store.juju.ubuntu.com"
	cachePath = "$HOME/.juju/cache"
)

// Store returns a Repository that provides access to the juju charm store.
func Store() Repository {
	return &store{storeURL, os.ExpandEnv(cachePath)}
}

// info returns the revision and SHA256 digest of the charm referenced by curl.
func (s *store) info(curl *URL) (rev int, digest string, err error) {
	key := curl.String()
	resp, err := http.Get(s.baseURL + "/charm-info?charms=" + url.QueryEscape(key))
	if err != nil {
		return
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}
	infos := make(map[string]*InfoResponse)
	if err = json.Unmarshal(body, &infos); err != nil {
		return
	}
	info, found := infos[key]
	if !found {
		err = fmt.Errorf("charm: charm store returned response without charm %q", key)
		return
	}
	for _, w := range info.Warnings {
		log.Printf("WARNING: charm store reports for %q: %s", key, w)
	}
	if info.Errors != nil {
		err = fmt.Errorf(
			"charm info errors for %q: %s", key, strings.Join(info.Errors, "; "),
		)
		return
	}
	return info.Revision, info.Sha256, nil
}

// Latest returns the latest revision of the charm referenced by curl, regardless
// of the revision set on curl itself.
func (s *store) Latest(curl *URL) (int, error) {
	rev, _, err := s.info(curl.WithRevision(-1))
	return rev, err
}

// verify returns an error unless a file exists at path with a hex-encoded
// SHA256 matching digest.
func verify(path, digest string) error {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}
	h := sha256.New()
	h.Write(b)
	if hex.EncodeToString(h.Sum(nil)) != digest {
		return fmt.Errorf("bad SHA256 of %q", path)
	}
	return nil
}

// Get returns the charm referenced by curl.
func (s *store) Get(curl *URL) (Charm, error) {
	if err := os.MkdirAll(s.cachePath, 0755); err != nil {
		return nil, err
	}
	rev, digest, err := s.info(curl)
	if err != nil {
		return nil, err
	}
	if curl.Revision == -1 {
		curl = curl.WithRevision(rev)
	} else if curl.Revision != rev {
		return nil, fmt.Errorf("charm: store returned charm with wrong revision for %q", curl.String())
	}
	path := filepath.Join(s.cachePath, Quote(curl.String())+".charm")
	if verify(path, digest) != nil {
		resp, err := http.Get(s.baseURL + "/charm/" + url.QueryEscape(curl.Path()))
		if err != nil {
			return nil, err
		}
		defer resp.Body.Close()
		f, err := ioutil.TempFile(s.cachePath, "charm-download")
		if err != nil {
			return nil, err
		}
		dlPath := f.Name()
		_, err = io.Copy(f, resp.Body)
		if cerr := f.Close(); err == nil {
			err = cerr
		}
		if err != nil {
			os.Remove(dlPath)
			return nil, err
		}
		if err := os.Rename(dlPath, path); err != nil {
			return nil, err
		}
	}
	if err := verify(path, digest); err != nil {
		return nil, err
	}
	return ReadBundle(path)
}

// LocalRepository represents a local directory containing charms organised
// by series.
type LocalRepository struct {
	Path string
}

// Latest returns the latest revision of the charm referenced by curl, regardless
// of the revision set on curl itself.
func (r *LocalRepository) Latest(curl *URL) (int, error) {
	ch, err := r.Get(curl.WithRevision(-1))
	if err != nil {
		return 0, err
	}
	return ch.Revision(), nil
}

func noCharms(curl *URL) (Charm, error) {
	return nil, fmt.Errorf("no charms found matching %q", curl.String())
}

// Get returns a charm matching curl, if one exists. If curl has a revision of
// -1, it returns the latest charm that matches curl. If multiple candidates
// satisfy the foregoing, the first one encountered will be returned.
func (r *LocalRepository) Get(curl *URL) (Charm, error) {
	if curl.Schema != "local" {
		return nil, fmt.Errorf("bad schema: %q", curl.Schema)
	}
	path := filepath.Join(r.Path, curl.Series)
	infos, err := ioutil.ReadDir(path)
	if err != nil {
		return noCharms(curl)
	}
	var latest Charm
	for _, info := range infos {
		chPath := filepath.Join(path, info.Name())
		if ch, err := Read(chPath); err != nil {
			log.Printf("WARNING: failed to load charm at %q: %s", chPath, err)
		} else if ch.Meta().Name == curl.Name {
			if ch.Revision() == curl.Revision {
				return ch, nil
			}
			if latest == nil || ch.Revision() > latest.Revision() {
				latest = ch
			}
		}
	}
	if curl.Revision == -1 && latest != nil {
		return latest, nil
	}
	return noCharms(curl)
}
<commit_msg>mild cleanup<commit_after>package charm

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"launchpad.net/juju/go/log"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
)

// InfoResponse is sent by the charm store in response to charm-info requests.
type InfoResponse struct {
	Revision int      `json:"revision"` // Zero is valid. Can't omitempty.
	Sha256   string   `json:"sha256,omitempty"`
	Errors   []string `json:"errors,omitempty"`
	Warnings []string `json:"warnings,omitempty"`
}

// Repository respresents a collection of charms.
type Repository interface {
	Get(curl *URL) (Charm, error)
	Latest(curl *URL) (int, error)
}

// store is a Repository that talks to the juju charm server (in ../store).
type store struct {
	baseURL   string
	cachePath string
}

const (
	storeURL  = "https://store.juju.ubuntu.com"
	cachePath = "$HOME/.juju/cache"
)

// Store returns a Repository that provides access to the juju charm store.
func Store() Repository {
	return &store{storeURL, os.ExpandEnv(cachePath)}
}

// info returns the revision and SHA256 digest of the charm referenced by curl.
func (s *store) info(curl *URL) (rev int, digest string, err error) {
	key := curl.String()
	resp, err := http.Get(s.baseURL + "/charm-info?charms=" + url.QueryEscape(key))
	if err != nil {
		return
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}
	infos := make(map[string]*InfoResponse)
	if err = json.Unmarshal(body, &infos); err != nil {
		return
	}
	info, found := infos[key]
	if !found {
		err = fmt.Errorf("charm: charm store returned response without charm %q", key)
		return
	}
	for _, w := range info.Warnings {
		log.Printf("WARNING: charm store reports for %q: %s", key, w)
	}
	if info.Errors != nil {
		err = fmt.Errorf(
			"charm info errors for %q: %s", key, strings.Join(info.Errors, "; "),
		)
		return
	}
	return info.Revision, info.Sha256, nil
}

// Latest returns the latest revision of the charm referenced by curl, regardless
// of the revision set on curl itself.
func (s *store) Latest(curl *URL) (int, error) {
	rev, _, err := s.info(curl.WithRevision(-1))
	return rev, err
}

// verify returns an error unless a file exists at path with a hex-encoded
// SHA256 matching digest.
func verify(path, digest string) error {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}
	h := sha256.New()
	h.Write(b)
	if hex.EncodeToString(h.Sum(nil)) != digest {
		return fmt.Errorf("bad SHA256 of %q", path)
	}
	return nil
}

// Get returns the charm referenced by curl.
func (s *store) Get(curl *URL) (Charm, error) {
	if err := os.MkdirAll(s.cachePath, 0755); err != nil {
		return nil, err
	}
	rev, digest, err := s.info(curl)
	if err != nil {
		return nil, err
	}
	if curl.Revision == -1 {
		curl = curl.WithRevision(rev)
	} else if curl.Revision != rev {
		return nil, fmt.Errorf("charm: store returned charm with wrong revision for %q", curl.String())
	}
	path := filepath.Join(s.cachePath, Quote(curl.String())+".charm")
	if verify(path, digest) != nil {
		resp, err := http.Get(s.baseURL + "/charm/" + url.QueryEscape(curl.Path()))
		if err != nil {
			return nil, err
		}
		defer resp.Body.Close()
		f, err := ioutil.TempFile(s.cachePath, "charm-download")
		if err != nil {
			return nil, err
		}
		dlPath := f.Name()
		_, err = io.Copy(f, resp.Body)
		if cerr := f.Close(); err == nil {
			err = cerr
		}
		if err != nil {
			os.Remove(dlPath)
			return nil, err
		}
		if err := os.Rename(dlPath, path); err != nil {
			return nil, err
		}
	}
	if err := verify(path, digest); err != nil {
		return nil, err
	}
	return ReadBundle(path)
}

// LocalRepository represents a local directory containing charms organised
// by series.
type LocalRepository struct {
	Path string
}

// Latest returns the latest revision of the charm referenced by curl, regardless
// of the revision set on curl itself.
func (r *LocalRepository) Latest(curl *URL) (int, error) {
	ch, err := r.Get(curl.WithRevision(-1))
	if err != nil {
		return 0, err
	}
	return ch.Revision(), nil
}

// Get returns a charm matching curl, if one exists. If curl has a revision of
// -1, it returns the latest charm that matches curl. If multiple candidates
// satisfy the foregoing, the first one encountered will be returned.
func (r *LocalRepository) Get(curl *URL) (Charm, error) {
	noCharms := fmt.Errorf("no charms found matching %q", curl.String())
	if curl.Schema != "local" {
		return nil, fmt.Errorf("bad schema: %q", curl.Schema)
	}
	path := filepath.Join(r.Path, curl.Series)
	infos, err := ioutil.ReadDir(path)
	if err != nil {
		return nil, noCharms
	}
	var latest Charm
	for _, info := range infos {
		chPath := filepath.Join(path, info.Name())
		if ch, err := Read(chPath); err != nil {
			log.Printf("WARNING: failed to load charm at %q: %s", chPath, err)
		} else if ch.Meta().Name == curl.Name {
			if ch.Revision() == curl.Revision {
				return ch, nil
			}
			if latest == nil || ch.Revision() > latest.Revision() {
				latest = ch
			}
		}
	}
	if curl.Revision == -1 && latest != nil {
		return latest, nil
	}
	return nil, noCharms
}
<|endoftext|>


<commit_before>package drawing

import (
	"errors"
	"fmt"
	"github.com/yofu/dxf/block"
	"github.com/yofu/dxf/class"
	"github.com/yofu/dxf/color"
	"github.com/yofu/dxf/entity"
	"github.com/yofu/dxf/format"
	"github.com/yofu/dxf/handle"
	"github.com/yofu/dxf/header"
	"github.com/yofu/dxf/object"
	"github.com/yofu/dxf/table"
	"os"
)

type Drawing struct {
	FileName     string
	Layers       map[string]*table.Layer
	Groups       map[string]*object.Group
	Styles       map[string]*table.Style
	CurrentLayer *table.Layer
	CurrentStyle *table.Style
	formatter    *format.Formatter
	Sections     []Section
	dictionary   *object.Dictionary
	groupdict    *object.Dictionary
	plotstyle    handle.Handler
}

func New() *Drawing {
	d := new(Drawing)
	d.Layers = make(map[string]*table.Layer)
	d.Layers["0"] = table.LY_0
	d.Groups = make(map[string]*object.Group)
	d.CurrentLayer = d.Layers["0"]
	d.Styles = make(map[string]*table.Style)
	d.Styles["STANDARD"] = table.ST_STANDARD
	d.CurrentStyle = d.Styles["STANDARD"]
	d.formatter = format.New()
	d.formatter.SetPrecision(16)
	d.Sections = []Section{
		header.New(),
		class.New(),
		table.New(),
		block.New(),
		entity.New(),
		object.New(),
	}
	d.dictionary = object.NewDictionary()
	d.addObject(d.dictionary)
	wd, ph := object.NewAcDbDictionaryWDFLT(d.dictionary)
	d.dictionary.AddItem("ACAD_PLOTSTYLENAME", wd)
	d.addObject(wd)
	d.addObject(ph)
	d.groupdict = object.NewDictionary()
	d.addObject(d.groupdict)
	d.dictionary.AddItem("ACAD_GROUP", d.groupdict)
	d.plotstyle = ph
	d.Layers["0"].SetPlotStyle(d.plotstyle)
	return d
}

func (d *Drawing) saveFile(filename string) error {
	d.setHandle()
	d.formatter.Reset()
	for _, s := range d.Sections {
		s.WriteTo(d.formatter)
	}
	d.formatter.WriteString(0, "EOF")
	w, err := os.Create(filename)
	defer w.Close()
	if err != nil {
		return err
	}
	d.formatter.WriteTo(w)
	return nil
}

func (d *Drawing) Save() error {
	if d.FileName == "" {
		return errors.New("filename is blank, use SaveAs(filename)")
	}
	return d.saveFile(d.FileName)
}

func (d *Drawing) SaveAs(filename string) error {
	d.FileName = filename
	return d.saveFile(filename)
}

func (d *Drawing) setHandle() {
	h := 1
	for _, s := range d.Sections[1:] {
		s.SetHandle(&h)
	}
	d.Sections[0].SetHandle(&h)
}

func (d *Drawing) Layer(name string, cl color.ColorNumber, lt *table.LineType, setcurrent bool) (*table.Layer, error) {
	if l, exist := d.Layers[name]; exist {
		if setcurrent {
			d.CurrentLayer = l
		}
		return l, errors.New(fmt.Sprintf("layer %s already exists", name))
	}
	l := table.NewLayer(name, cl, lt)
	l.SetPlotStyle(d.plotstyle)
	d.Layers[name] = l
	d.Sections[2].(table.Tables).AddLayer(l)
	if setcurrent {
		d.CurrentLayer = l
	}
	return l, nil
}

func (d *Drawing) ChangeLayer(name string) error {
	if l, exist := d.Layers[name]; exist {
		d.CurrentLayer = l
		return nil
	}
	return errors.New(fmt.Sprintf("layer %s doesn't exist", name))
}

func (d *Drawing) LineType(name string) (*table.LineType, error) {
	lt, err := d.Sections[TABLES].(table.Tables)[table.LTYPE].Contains(name)
	if err != nil {
		return nil, fmt.Errorf("linetype %s", err.Error())
	}
	return lt.(*table.LineType), nil
}

func (d *Drawing) AddEntity(e entity.Entity) {
	d.Sections[4] = d.Sections[4].(entity.Entities).Add(e)
}

func (d *Drawing) Point(x, y, z float64) (*entity.Point, error) {
	p := entity.NewPoint()
	p.Coord = []float64{x, y, z}
	p.SetLayer(d.CurrentLayer)
	d.AddEntity(p)
	return p, nil
}

func (d *Drawing) Line(x1, y1, z1, x2, y2, z2 float64) (*entity.Line, error) {
	l := entity.NewLine()
	l.Start = []float64{x1, y1, z1}
	l.End = []float64{x2, y2, z2}
	l.SetLayer(d.CurrentLayer)
	d.AddEntity(l)
	return l, nil
}

func (d *Drawing) Circle(x, y, z, r float64) (*entity.Circle, error) {
	c := entity.NewCircle()
	c.Center = []float64{x, y, z}
	c.Radius = r
	c.SetLayer(d.CurrentLayer)
	d.AddEntity(c)
	return c, nil
}

func (d *Drawing) Polyline(closed bool, vertices ...[]float64) (*entity.Polyline, error) {
	p := entity.NewPolyline()
	p.SetLayer(d.CurrentLayer)
	for _, v := range vertices {
		p.AddVertex(v[0], v[1], v[2])
	}
	if closed {
		p.Close()
	}
	d.AddEntity(p)
	return p, nil
}

func (d *Drawing) LwPolyline(closed bool, vertices ...[]float64) (*entity.LwPolyline, error) {
	size := len(vertices)
	l := entity.NewLwPolyline(size)
	for i := 0; i < size; i++ {
		l.Vertices[i] = vertices[i]
	}
	if closed {
		l.Close()
	}
	l.SetLayer(d.CurrentLayer)
	d.AddEntity(l)
	return l, nil
}

func (d *Drawing) ThreeDFace(points [][]float64) (*entity.ThreeDFace, error) {
	f := entity.New3DFace()
	if len(points) < 3 {
		return nil, errors.New("3DFace needs 3 or more points")
	}
	for i := 0; i < 3; i++ {
		f.Points[i] = points[i]
	}
	if len(points) >= 4 {
		f.Points[3] = points[3]
	} else {
		f.Points[3] = points[2]
	}
	f.SetLayer(d.CurrentLayer)
	d.AddEntity(f)
	return f, nil
}

func (d *Drawing) Text(str string, x, y, z, height float64) (*entity.Text, error) {
	t := entity.NewText()
	t.Coord1 = []float64{x, y, z}
	t.Height = height
	t.Value = str
	t.SetLayer(d.CurrentLayer)
	t.Style = d.CurrentStyle
	d.AddEntity(t)
	return t, nil
}

func (d *Drawing) addObject(o object.Object) {
	d.Sections[5] = d.Sections[5].(object.Objects).Add(o)
}

func (d *Drawing) Group(name, desc string, es ...entity.Entity) (*object.Group, error) {
	if g, exist := d.Groups[name]; exist {
		g.AddEntity(es...)
		return g, errors.New(fmt.Sprintf("group %s already exists", name))
	}
	g := object.NewGroup(name, desc, es...)
	d.Groups[name] = g
	g.SetOwner(d.groupdict)
	d.addObject(g)
	return g, nil
}

func (d *Drawing) AddToGroup(name string, es ...entity.Entity) error {
	if g, exist := d.Groups[name]; exist {
		g.AddEntity(es...)
	}
	return errors.New(fmt.Sprintf("group %s doesn't exist", name))
}
<commit_msg>implement drawing.Entities()<commit_after>package drawing

import (
	"errors"
	"fmt"
	"github.com/yofu/dxf/block"
	"github.com/yofu/dxf/class"
	"github.com/yofu/dxf/color"
	"github.com/yofu/dxf/entity"
	"github.com/yofu/dxf/format"
	"github.com/yofu/dxf/handle"
	"github.com/yofu/dxf/header"
	"github.com/yofu/dxf/object"
	"github.com/yofu/dxf/table"
	"os"
)

type Drawing struct {
	FileName     string
	Layers       map[string]*table.Layer
	Groups       map[string]*object.Group
	Styles       map[string]*table.Style
	CurrentLayer *table.Layer
	CurrentStyle *table.Style
	formatter    *format.Formatter
	Sections     []Section
	dictionary   *object.Dictionary
	groupdict    *object.Dictionary
	plotstyle    handle.Handler
}

func New() *Drawing {
	d := new(Drawing)
	d.Layers = make(map[string]*table.Layer)
	d.Layers["0"] = table.LY_0
	d.Groups = make(map[string]*object.Group)
	d.CurrentLayer = d.Layers["0"]
	d.Styles = make(map[string]*table.Style)
	d.Styles["STANDARD"] = table.ST_STANDARD
	d.CurrentStyle = d.Styles["STANDARD"]
	d.formatter = format.New()
	d.formatter.SetPrecision(16)
	d.Sections = []Section{
		header.New(),
		class.New(),
		table.New(),
		block.New(),
		entity.New(),
		object.New(),
	}
	d.dictionary = object.NewDictionary()
	d.addObject(d.dictionary)
	wd, ph := object.NewAcDbDictionaryWDFLT(d.dictionary)
	d.dictionary.AddItem("ACAD_PLOTSTYLENAME", wd)
	d.addObject(wd)
	d.addObject(ph)
	d.groupdict = object.NewDictionary()
	d.addObject(d.groupdict)
	d.dictionary.AddItem("ACAD_GROUP", d.groupdict)
	d.plotstyle = ph
	d.Layers["0"].SetPlotStyle(d.plotstyle)
	return d
}

func (d *Drawing) saveFile(filename string) error {
	d.setHandle()
	d.formatter.Reset()
	for _, s := range d.Sections {
		s.WriteTo(d.formatter)
	}
	d.formatter.WriteString(0, "EOF")
	w, err := os.Create(filename)
	defer w.Close()
	if err != nil {
		return err
	}
	d.formatter.WriteTo(w)
	return nil
}

func (d *Drawing) Save() error {
	if d.FileName == "" {
		return errors.New("filename is blank, use SaveAs(filename)")
	}
	return d.saveFile(d.FileName)
}

func (d *Drawing) SaveAs(filename string) error {
	d.FileName = filename
	return d.saveFile(filename)
}

func (d *Drawing) setHandle() {
	h := 1
	for _, s := range d.Sections[1:] {
		s.SetHandle(&h)
	}
	d.Sections[0].SetHandle(&h)
}

func (d *Drawing) Layer(name string, cl color.ColorNumber, lt *table.LineType, setcurrent bool) (*table.Layer, error) {
	if l, exist := d.Layers[name]; exist {
		if setcurrent {
			d.CurrentLayer = l
		}
		return l, errors.New(fmt.Sprintf("layer %s already exists", name))
	}
	l := table.NewLayer(name, cl, lt)
	l.SetPlotStyle(d.plotstyle)
	d.Layers[name] = l
	d.Sections[2].(table.Tables).AddLayer(l)
	if setcurrent {
		d.CurrentLayer = l
	}
	return l, nil
}

func (d *Drawing) ChangeLayer(name string) error {
	if l, exist := d.Layers[name]; exist {
		d.CurrentLayer = l
		return nil
	}
	return errors.New(fmt.Sprintf("layer %s doesn't exist", name))
}

func (d *Drawing) LineType(name string) (*table.LineType, error) {
	lt, err := d.Sections[TABLES].(table.Tables)[table.LTYPE].Contains(name)
	if err != nil {
		return nil, fmt.Errorf("linetype %s", err.Error())
	}
	return lt.(*table.LineType), nil
}

func (d *Drawing) Entities() (entity.Entities) {
	return d.Sections[ENTITIES].(entity.Entities)
}

func (d *Drawing) AddEntity(e entity.Entity) {
	d.Sections[4] = d.Sections[4].(entity.Entities).Add(e)
}

func (d *Drawing) Point(x, y, z float64) (*entity.Point, error) {
	p := entity.NewPoint()
	p.Coord = []float64{x, y, z}
	p.SetLayer(d.CurrentLayer)
	d.AddEntity(p)
	return p, nil
}

func (d *Drawing) Line(x1, y1, z1, x2, y2, z2 float64) (*entity.Line, error) {
	l := entity.NewLine()
	l.Start = []float64{x1, y1, z1}
	l.End = []float64{x2, y2, z2}
	l.SetLayer(d.CurrentLayer)
	d.AddEntity(l)
	return l, nil
}

func (d *Drawing) Circle(x, y, z, r float64) (*entity.Circle, error) {
	c := entity.NewCircle()
	c.Center = []float64{x, y, z}
	c.Radius = r
	c.SetLayer(d.CurrentLayer)
	d.AddEntity(c)
	return c, nil
}

func (d *Drawing) Polyline(closed bool, vertices ...[]float64) (*entity.Polyline, error) {
	p := entity.NewPolyline()
	p.SetLayer(d.CurrentLayer)
	for _, v := range vertices {
		p.AddVertex(v[0], v[1], v[2])
	}
	if closed {
		p.Close()
	}
	d.AddEntity(p)
	return p, nil
}

func (d *Drawing) LwPolyline(closed bool, vertices ...[]float64) (*entity.LwPolyline, error) {
	size := len(vertices)
	l := entity.NewLwPolyline(size)
	for i := 0; i < size; i++ {
		l.Vertices[i] = vertices[i]
	}
	if closed {
		l.Close()
	}
	l.SetLayer(d.CurrentLayer)
	d.AddEntity(l)
	return l, nil
}

func (d *Drawing) ThreeDFace(points [][]float64) (*entity.ThreeDFace, error) {
	f := entity.New3DFace()
	if len(points) < 3 {
		return nil, errors.New("3DFace needs 3 or more points")
	}
	for i := 0; i < 3; i++ {
		f.Points[i] = points[i]
	}
	if len(points) >= 4 {
		f.Points[3] = points[3]
	} else {
		f.Points[3] = points[2]
	}
	f.SetLayer(d.CurrentLayer)
	d.AddEntity(f)
	return f, nil
}

func (d *Drawing) Text(str string, x, y, z, height float64) (*entity.Text, error) {
	t := entity.NewText()
	t.Coord1 = []float64{x, y, z}
	t.Height = height
	t.Value = str
	t.SetLayer(d.CurrentLayer)
	t.Style = d.CurrentStyle
	d.AddEntity(t)
	return t, nil
}

func (d *Drawing) addObject(o object.Object) {
	d.Sections[5] = d.Sections[5].(object.Objects).Add(o)
}

func (d *Drawing) Group(name, desc string, es ...entity.Entity) (*object.Group, error) {
	if g, exist := d.Groups[name]; exist {
		g.AddEntity(es...)
		return g, errors.New(fmt.Sprintf("group %s already exists", name))
	}
	g := object.NewGroup(name, desc, es...)
	d.Groups[name] = g
	g.SetOwner(d.groupdict)
	d.addObject(g)
	return g, nil
}

func (d *Drawing) AddToGroup(name string, es ...entity.Entity) error {
	if g, exist := d.Groups[name]; exist {
		g.AddEntity(es...)
	}
	return errors.New(fmt.Sprintf("group %s doesn't exist", name))
}
<|endoftext|>


<commit_before>package donut

import (
	"bytes"
	"errors"
	"io"
	"strconv"
	"strings"
	"time"

	"crypto/md5"
	"encoding/hex"

	"github.com/minio-io/minio/pkg/encoding/erasure"
	"github.com/minio-io/minio/pkg/utils/split"
)

// getErasureTechnique - convert technique string into Technique type
func getErasureTechnique(technique string) (erasure.Technique, error) {
	switch true {
	case technique == "Cauchy":
		return erasure.Cauchy, nil
	case technique == "Vandermonde":
		return erasure.Cauchy, nil
	default:
		return erasure.None, errors.New("Invalid erasure technique")
	}
}

// erasureReader - returns aligned streaming reads over a PipeWriter
func erasureReader(readers []io.ReadCloser, donutMetadata map[string]string, writer *io.PipeWriter) {
	totalChunks, err := strconv.Atoi(donutMetadata["chunkCount"])
	if err != nil {
		writer.CloseWithError(err)
		return
	}
	totalLeft, err := strconv.Atoi(donutMetadata["size"])
	if err != nil {
		writer.CloseWithError(err)
		return
	}
	blockSize, err := strconv.Atoi(donutMetadata["blockSize"])
	if err != nil {
		writer.CloseWithError(err)
		return
	}
	k, err := strconv.Atoi(donutMetadata["erasureK"])
	if err != nil {
		writer.CloseWithError(err)
		return
	}
	m, err := strconv.Atoi(donutMetadata["erasureM"])
	if err != nil {
		writer.CloseWithError(err)
		return
	}
	expectedMd5sum, err := hex.DecodeString(donutMetadata["md5"])
	if err != nil {
		writer.CloseWithError(err)
		return
	}
	technique, err := getErasureTechnique(donutMetadata["erasureTechnique"])
	if err != nil {
		writer.CloseWithError(err)
		return
	}
	summer := md5.New()
	params, _ := erasure.ParseEncoderParams(uint8(k), uint8(m), technique)
	encoder := erasure.NewEncoder(params)
	for _, reader := range readers {
		defer reader.Close()
	}
	for i := 0; i < totalChunks; i++ {
		curBlockSize := totalLeft
		if blockSize < totalLeft {
			curBlockSize = blockSize
		}

		curChunkSize := erasure.GetEncodedBlockLen(curBlockSize, uint8(k))

		encodedBytes := make([][]byte, 16)
		for i, reader := range readers {
			var bytesBuffer bytes.Buffer
			_, err := io.CopyN(&bytesBuffer, reader, int64(curChunkSize))
			if err != nil {
				writer.CloseWithError(err)
				return
			}
			encodedBytes[i] = bytesBuffer.Bytes()
		}
		decodedData, err := encoder.Decode(encodedBytes, curBlockSize)
		if err != nil {
			writer.CloseWithError(err)
			return
		}
		summer.Write(decodedData)
		_, err = io.Copy(writer, bytes.NewBuffer(decodedData))
		if err != nil {
			writer.CloseWithError(err)
			return
		}
		totalLeft = totalLeft - blockSize
	}
	actualMd5sum := summer.Sum(nil)
	if bytes.Compare(expectedMd5sum, actualMd5sum) != 0 {
		writer.CloseWithError(errors.New("decoded md5sum did not match"))
		return
	}
	writer.Close()
	return
}

// erasure writer

type erasureWriter struct {
	writers       []Writer
	metadata      map[string]string
	donutMetadata map[string]string // not exposed
	erasureWriter *io.PipeWriter
	isClosed      <-chan bool
}

// newErasureWriter - get a new writer
func newErasureWriter(writers []Writer) ObjectWriter {
	r, w := io.Pipe()
	isClosed := make(chan bool)
	writer := erasureWriter{
		writers:       writers,
		metadata:      make(map[string]string),
		erasureWriter: w,
		isClosed:      isClosed,
	}
	go erasureGoroutine(r, writer, isClosed)
	return writer
}

func erasureGoroutine(r *io.PipeReader, eWriter erasureWriter, isClosed chan<- bool) {
	chunks := split.Stream(r, 10*1024*1024)
	params, _ := erasure.ParseEncoderParams(8, 8, erasure.Cauchy)
	encoder := erasure.NewEncoder(params)
	chunkCount := 0
	totalLength := 0
	summer := md5.New()
	for chunk := range chunks {
		if chunk.Err == nil {
			totalLength = totalLength + len(chunk.Data)
			encodedBlocks, _ := encoder.Encode(chunk.Data)
			summer.Write(chunk.Data)
			for blockIndex, block := range encodedBlocks {
				io.Copy(eWriter.writers[blockIndex], bytes.NewBuffer(block))
			}
		}
		chunkCount = chunkCount + 1
	}
	dataMd5sum := summer.Sum(nil)
	metadata := make(map[string]string)
	metadata["blockSize"] = strconv.Itoa(10 * 1024 * 1024)
	metadata["chunkCount"] = strconv.Itoa(chunkCount)
	metadata["created"] = time.Now().Format(time.RFC3339Nano)
	metadata["erasureK"] = "8"
	metadata["erasureM"] = "8"
	metadata["erasureTechnique"] = "Cauchy"
	metadata["md5"] = hex.EncodeToString(dataMd5sum)
	metadata["size"] = strconv.Itoa(totalLength)
	for _, nodeWriter := range eWriter.writers {
		if nodeWriter != nil {
			nodeWriter.SetMetadata(eWriter.metadata)
			nodeWriter.SetDonutMetadata(metadata)
			nodeWriter.Close()
		}
	}
	isClosed <- true
}

func (eWriter erasureWriter) Write(data []byte) (int, error) {
	io.Copy(eWriter.erasureWriter, bytes.NewBuffer(data))
	return len(data), nil
}

func (eWriter erasureWriter) Close() error {
	eWriter.erasureWriter.Close()
	<-eWriter.isClosed
	return nil
}

func (eWriter erasureWriter) CloseWithError(err error) error {
	for _, writer := range eWriter.writers {
		if writer != nil {
			writer.CloseWithError(err)
		}
	}
	return nil
}

func (eWriter erasureWriter) SetMetadata(metadata map[string]string) error {
	for k := range metadata {
		if strings.HasPrefix(k, "sys.") {
			return errors.New("Invalid key '" + k + "', cannot start with sys.'")
		}
	}
	for k := range eWriter.metadata {
		delete(eWriter.metadata, k)
	}
	for k, v := range metadata {
		eWriter.metadata[k] = v
	}
	return nil
}

func (eWriter erasureWriter) GetMetadata() (map[string]string, error) {
	metadata := make(map[string]string)
	for k, v := range eWriter.metadata {
		metadata[k] = v
	}
	return metadata, nil
}
<commit_msg>loop only on readers once<commit_after>package donut

import (
	"bytes"
	"errors"
	"io"
	"strconv"
	"strings"
	"time"

	"crypto/md5"
	"encoding/hex"

	"github.com/minio-io/minio/pkg/encoding/erasure"
	"github.com/minio-io/minio/pkg/utils/split"
)

// getErasureTechnique - convert technique string into Technique type
func getErasureTechnique(technique string) (erasure.Technique, error) {
	switch true {
	case technique == "Cauchy":
		return erasure.Cauchy, nil
	case technique == "Vandermonde":
		return erasure.Cauchy, nil
	default:
		return erasure.None, errors.New("Invalid erasure technique")
	}
}

// erasureReader - returns aligned streaming reads over a PipeWriter
func erasureReader(readers []io.ReadCloser, donutMetadata map[string]string, writer *io.PipeWriter) {
	totalChunks, err := strconv.Atoi(donutMetadata["chunkCount"])
	if err != nil {
		writer.CloseWithError(err)
		return
	}
	totalLeft, err := strconv.Atoi(donutMetadata["size"])
	if err != nil {
		writer.CloseWithError(err)
		return
	}
	blockSize, err := strconv.Atoi(donutMetadata["blockSize"])
	if err != nil {
		writer.CloseWithError(err)
		return
	}
	k, err := strconv.Atoi(donutMetadata["erasureK"])
	if err != nil {
		writer.CloseWithError(err)
		return
	}
	m, err := strconv.Atoi(donutMetadata["erasureM"])
	if err != nil {
		writer.CloseWithError(err)
		return
	}
	expectedMd5sum, err := hex.DecodeString(donutMetadata["md5"])
	if err != nil {
		writer.CloseWithError(err)
		return
	}
	technique, err := getErasureTechnique(donutMetadata["erasureTechnique"])
	if err != nil {
		writer.CloseWithError(err)
		return
	}
	summer := md5.New()
	params, _ := erasure.ParseEncoderParams(uint8(k), uint8(m), technique)
	encoder := erasure.NewEncoder(params)
	for i := 0; i < totalChunks; i++ {
		curBlockSize := totalLeft
		if blockSize < totalLeft {
			curBlockSize = blockSize
		}

		curChunkSize := erasure.GetEncodedBlockLen(curBlockSize, uint8(k))

		encodedBytes := make([][]byte, 16)
		for i, reader := range readers {
			defer reader.Close()
			var bytesBuffer bytes.Buffer
			_, err := io.CopyN(&bytesBuffer, reader, int64(curChunkSize))
			if err != nil {
				writer.CloseWithError(err)
				return
			}
			encodedBytes[i] = bytesBuffer.Bytes()
		}
		decodedData, err := encoder.Decode(encodedBytes, curBlockSize)
		if err != nil {
			writer.CloseWithError(err)
			return
		}
		summer.Write(decodedData)
		_, err = io.Copy(writer, bytes.NewBuffer(decodedData))
		if err != nil {
			writer.CloseWithError(err)
			return
		}
		totalLeft = totalLeft - blockSize
	}
	actualMd5sum := summer.Sum(nil)
	if bytes.Compare(expectedMd5sum, actualMd5sum) != 0 {
		writer.CloseWithError(errors.New("decoded md5sum did not match"))
		return
	}
	writer.Close()
	return
}

// erasure writer

type erasureWriter struct {
	writers       []Writer
	metadata      map[string]string
	donutMetadata map[string]string // not exposed
	erasureWriter *io.PipeWriter
	isClosed      <-chan bool
}

// newErasureWriter - get a new writer
func newErasureWriter(writers []Writer) ObjectWriter {
	r, w := io.Pipe()
	isClosed := make(chan bool)
	writer := erasureWriter{
		writers:       writers,
		metadata:      make(map[string]string),
		erasureWriter: w,
		isClosed:      isClosed,
	}
	go erasureGoroutine(r, writer, isClosed)
	return writer
}

func erasureGoroutine(r *io.PipeReader, eWriter erasureWriter, isClosed chan<- bool) {
	chunks := split.Stream(r, 10*1024*1024)
	params, _ := erasure.ParseEncoderParams(8, 8, erasure.Cauchy)
	encoder := erasure.NewEncoder(params)
	chunkCount := 0
	totalLength := 0
	summer := md5.New()
	for chunk := range chunks {
		if chunk.Err == nil {
			totalLength = totalLength + len(chunk.Data)
			encodedBlocks, _ := encoder.Encode(chunk.Data)
			summer.Write(chunk.Data)
			for blockIndex, block := range encodedBlocks {
				io.Copy(eWriter.writers[blockIndex], bytes.NewBuffer(block))
			}
		}
		chunkCount = chunkCount + 1
	}
	dataMd5sum := summer.Sum(nil)
	metadata := make(map[string]string)
	metadata["blockSize"] = strconv.Itoa(10 * 1024 * 1024)
	metadata["chunkCount"] = strconv.Itoa(chunkCount)
	metadata["created"] = time.Now().Format(time.RFC3339Nano)
	metadata["erasureK"] = "8"
	metadata["erasureM"] = "8"
	metadata["erasureTechnique"] = "Cauchy"
	metadata["md5"] = hex.EncodeToString(dataMd5sum)
	metadata["size"] = strconv.Itoa(totalLength)
	for _, nodeWriter := range eWriter.writers {
		if nodeWriter != nil {
			nodeWriter.SetMetadata(eWriter.metadata)
			nodeWriter.SetDonutMetadata(metadata)
			nodeWriter.Close()
		}
	}
	isClosed <- true
}

func (eWriter erasureWriter) Write(data []byte) (int, error) {
	io.Copy(eWriter.erasureWriter, bytes.NewBuffer(data))
	return len(data), nil
}

func (eWriter erasureWriter) Close() error {
	eWriter.erasureWriter.Close()
	<-eWriter.isClosed
	return nil
}

func (eWriter erasureWriter) CloseWithError(err error) error {
	for _, writer := range eWriter.writers {
		if writer != nil {
			writer.CloseWithError(err)
		}
	}
	return nil
}

func (eWriter erasureWriter) SetMetadata(metadata map[string]string) error {
	for k := range metadata {
		if strings.HasPrefix(k, "sys.") {
			return errors.New("Invalid key '" + k + "', cannot start with sys.'")
		}
	}
	for k := range eWriter.metadata {
		delete(eWriter.metadata, k)
	}
	for k, v := range metadata {
		eWriter.metadata[k] = v
	}
	return nil
}

func (eWriter erasureWriter) GetMetadata() (map[string]string, error) {
	metadata := make(map[string]string)
	for k, v := range eWriter.metadata {
		metadata[k] = v
	}
	return metadata, nil
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"math/rand"
	"time"

	"github.com/sirupsen/logrus"
	"github.com/streadway/amqp"
)

type rabbitmqConsumer struct {
	open     func(bool) (*amqp.Connection, error)
	current  *amqp.Connection
	channel  *amqp.Channel
	delivery <-chan amqp.Delivery

	logger logrus.FieldLogger
	qname  string
	do     func(amqp.Delivery)

	shutdown    chan struct{}
	maxInterval time.Duration
}

// Deliver set a deliver func
func (r *rabbitmqConsumer) Deliver(do func(amqp.Delivery)) *rabbitmqConsumer {
	r.do = do
	return r
}

func (r *rabbitmqConsumer) Begin() error {
	if err := r.connect(); err != nil {
		return err
	}
	r.shutdown = make(chan struct{})
	r.maxInterval = time.Minute

	delivery, err := r.channel.Consume(
		r.qname, // queue
		"",      // consumer
		false,   // auto ack
		false,   // exclusive
		false,   // no local
		false,   // no wait
		nil,     // args
	)
	if err != nil {
		return err
	}
	r.delivery = delivery

	r.logger.Infoln("begin to accept messages")
	go func() {
		for {
			select {
			case <-r.shutdown:
				break
			case msg, ok := <-r.delivery:
				if !ok {
					// try to connect ...
					r.waitRepairConnect()
					continue
				}

				r.do(msg)
			}
		}

		r.logger.Infoln("stop to accept messages")
	}()

	return nil
}

func (r *rabbitmqConsumer) waitRepairConnect() {
	retries := 1
	for {
		r.logger.Infof("check network and then accept messages, try %d times", retries)
		if err := r.repairConnect(); err == nil {
			break
		}

		retries++
		d := time.Duration(5+rand.Intn(5)+2*retries) * time.Second
		if d > r.maxInterval {
			d = r.maxInterval
		}
		time.Sleep(d)
	}
}

func (r *rabbitmqConsumer) repairConnect() error {
	if r.current.IsClosed() {
		current, err := r.open(true)
		if err != nil {
			return err
		}
		r.current = current

		channel, err := r.current.Channel()
		if err != nil {
			return err
		}
		r.channel = channel

		delivery, err := r.channel.Consume(
			r.qname, // queue
			"",      // consumer
			false,   // auto ack
			false,   // exclusive
			false,   // no local
			false,   // no wait
			nil,     // args
		)
		if err != nil {
			return err
		}
		r.delivery = delivery
	}

	return nil
}

func (r *rabbitmqConsumer) connect() error {
	var err error
	r.current, err = r.open(false)
	if err != nil {
		return err
	}

	r.channel, err = r.current.Channel()
	return err
}

func (r *rabbitmqConsumer) End() error {
	close(r.shutdown)
	return nil
}

var (
	exchange   = "ses.agent.e"
	qname      = "ses.agent.q"
	routingKey = "ses.agent.aws"
	bindingKey = "ses.agent.aws"
)

func main() {
	var dsn = "amqp://guest:guest@localhost:5672/"

	var conn *amqp.Connection
	open := func(force bool) (*amqp.Connection, error) {
		if !force && conn != nil {
			return conn, nil
		}

		var err error
		conn, err = amqp.Dial(dsn)
		return conn, err
	}

	log.Printf(" [*] Waiting for logs. To exit press CTRL+C")
	c := rabbitmqConsumer{open: open, logger: logrus.New(), qname: qname}
	err := c.Deliver(func(msg amqp.Delivery) {
		log.Printf(" [x] %s", msg.Body)
		msg.Ack(false)
	}).Begin()
	if err != nil {
		panic(err)
	}

	select {}
}

/*
func define(ch *amqp.Channel) {
	err := ch.ExchangeDeclare(
		exchange, // name
		amqp.ExchangeTopic,
		true,  // durable
		false, // auto-deleted
		false, // internal
		false, // no-wait
		nil,   // arguments
	)
	failOnError(err, "Failed to declare an exchange")

	q, err := ch.QueueDeclare(
		qname, // name
		true,  // durable
		false, // delete when unused
		false, // exclusive
		false, // no-wait
		nil,   // arguments
	)
	failOnError(err, "Failed to declare a queue")

	err = ch.QueueBind(
		q.Name,     // queue name
		bindingKey, // routing key
		exchange,   // exchange
		false,
		nil)
	failOnError(err, "Failed to bind a queue")
}
*/
<commit_msg>modify customer<commit_after>package main

import (
	"log"
	"math/rand"
	"time"

	"github.com/sirupsen/logrus"
	"github.com/streadway/amqp"
)

// Consumer is a message queue consumer
type Consumer interface {
	Deliver(do func(amqp.Delivery)) Consumer
	Begin() error
	End() error
}

type rabbitmqConsumer struct {
	open     func(bool) (*amqp.Connection, error)
	current  *amqp.Connection
	channel  *amqp.Channel
	delivery <-chan amqp.Delivery

	logger logrus.FieldLogger
	qname  string
	do     func(amqp.Delivery)

	shutdown    chan struct{}
	maxInterval time.Duration
}

// Deliver set a deliver func
func (r *rabbitmqConsumer) Deliver(do func(amqp.Delivery)) *rabbitmqConsumer {
	r.do = do
	return r
}

func (r *rabbitmqConsumer) Begin() error {
	if err := r.connect(); err != nil {
		return err
	}
	r.shutdown = make(chan struct{})
	r.maxInterval = time.Minute

	delivery, err := r.channel.Consume(
		r.qname, // queue
		"",      // consumer
		false,   // auto ack
		false,   // exclusive
		false,   // no local
		false,   // no wait
		nil,     // args
	)
	if err != nil {
		return err
	}
	r.delivery = delivery

	r.logger.Infoln("begin to accept messages")
	go func() {
		for {
			select {
			case <-r.shutdown:
				break
			case msg, ok := <-r.delivery:
				if !ok {
					// try to connect ...
					r.waitRepairConnect()
					continue
				}

				r.do(msg)
			}
		}

		r.logger.Infoln("stop to accept messages")
	}()

	return nil
}

func (r *rabbitmqConsumer) waitRepairConnect() {
	retries := 1
	for {
		r.logger.Infof("check network and then accept messages, try %d times", retries)
		if err := r.repairConnect(); err == nil {
			break
		}

		retries++
		d := time.Duration(5+rand.Intn(5)+2*retries) * time.Second
		if d > r.maxInterval {
			d = r.maxInterval
		}
		time.Sleep(d)
	}
}

func (r *rabbitmqConsumer) repairConnect() error {
	if r.current.IsClosed() {
		current, err := r.open(true)
		if err != nil {
			return err
		}
		r.current = current

		channel, err := r.current.Channel()
		if err != nil {
			return err
		}
		r.channel = channel

		delivery, err := r.channel.Consume(
			r.qname, // queue
			"",      // consumer
			false,   // auto ack
			false,   // exclusive
			false,   // no local
			false,   // no wait
			nil,     // args
		)
		if err != nil {
			return err
		}
		r.delivery = delivery
	}

	return nil
}

func (r *rabbitmqConsumer) connect() error {
	var err error
	r.current, err = r.open(false)
	if err != nil {
		return err
	}

	r.channel, err = r.current.Channel()
	return err
}

func (r *rabbitmqConsumer) End() error {
	close(r.shutdown)
	return nil
}

var (
	exchange   = "ses.agent.e"
	qname      = "ses.agent.q"
	routingKey = "ses.agent.aws"
	bindingKey = "ses.agent.aws"
)

func main() {
	var dsn = "amqp://guest:guest@localhost:5672/"

	var conn *amqp.Connection
	open := func(force bool) (*amqp.Connection, error) {
		if !force && conn != nil {
			return conn, nil
		}

		var err error
		conn, err = amqp.Dial(dsn)
		return conn, err
	}

	log.Printf(" [*] Waiting for logs. To exit press CTRL+C")
	c := rabbitmqConsumer{open: open, logger: logrus.New(), qname: qname}
	err := c.Deliver(func(msg amqp.Delivery) {
		log.Printf(" [x] %s", msg.Body)
		msg.Ack(false)
	}).Begin()
	if err != nil {
		panic(err)
	}

	select {}
}
<|endoftext|>


<commit_before>package websockets

import (
	"encoding/json"

	"github.com/rubblelabs/ripple/data"
)

// Fields from subscribed ledger stream messages
type LedgerStreamMsg struct {
	FeeBase          uint64          `json:"fee_base"`
	FeeRef           uint64          `json:"fee_ref"`
	LedgerSequence   uint32          `json:"ledger_index"`
	LedgerHash       data.Hash256    `json:"ledger_hash"`
	LedgerTime       data.RippleTime `json:"ledger_time"`
	ReserveBase      uint64          `json:"reserve_base"`
	ReserveIncrement uint64          `json:"reserve_inc"`
	ValidatedLedgers string          `json:"validated_ledgers"`
	TxnCount         uint32          `json:"txn_count"` // Only streamed, not in the subscribe result.
}

// Fields from subscribed transaction stream messages
type TransactionStreamMsg struct {
	Transaction         data.TransactionWithMetaData `json:"transaction"`
	EngineResult        data.TransactionResult       `json:"engine_result"`
	EngineResultCode    int                          `json:"engine_result_code"`
	EngineResultMessage string                       `json:"engine_result_message"`
	LedgerHash          data.Hash256                 `json:"ledger_hash"`
	LedgerSequence      uint32                       `json:"ledger_index"`
	Status              string
	Validated           bool
}

// Fields from subscribed server status stream messages
type ServerStreamMsg struct {
	Status     string `json:"server_status"`
	LoadBase   int    `json:"load_base"`
	LoadFactor int    `json:"load_factor"`
	HostID     string `json:"hostid"`
}

// Map message types to the appropriate data structure
var streamMessageFactory = map[string]func() interface{}{
	"ledgerClosed": func() interface{} { return &LedgerStreamMsg{} },
	"transaction":  func() interface{} { return &TransactionStreamMsg{} },
	"serverStatus": func() interface{} { return &ServerStreamMsg{} },
}

type SubscribeCommand struct {
	*Command
	Streams []string                `json:"streams"`
	Books   []OrderBookSubscription `json:"books,omitempty"`
	Result  *SubscribeResult        `json:"result,omitempty"`
}

type SubscribeResult struct {
	// Contains one or both of these, depending what streams were subscribed
	*LedgerStreamMsg
	*ServerStreamMsg
	Asks []data.OrderBookOffer
	Bids []data.OrderBookOffer
}

// Wrapper to stop recursive unmarshalling
type txStreamJSON TransactionStreamMsg

func (msg *TransactionStreamMsg) UnmarshalJSON(b []byte) error {
	var extract struct {
		*txStreamJSON
		MetaData *data.MetaData `json:"meta"`
	}
	extract.txStreamJSON = (*txStreamJSON)(msg)
	extract.MetaData = &msg.Transaction.MetaData
	return json.Unmarshal(b, &extract)
}
<commit_msg>Sometimes subscribe result contains "offers"<commit_after>package websockets

import (
	"encoding/json"

	"github.com/rubblelabs/ripple/data"
)

// Fields from subscribed ledger stream messages
type LedgerStreamMsg struct {
	FeeBase          uint64          `json:"fee_base"`
	FeeRef           uint64          `json:"fee_ref"`
	LedgerSequence   uint32          `json:"ledger_index"`
	LedgerHash       data.Hash256    `json:"ledger_hash"`
	LedgerTime       data.RippleTime `json:"ledger_time"`
	ReserveBase      uint64          `json:"reserve_base"`
	ReserveIncrement uint64          `json:"reserve_inc"`
	ValidatedLedgers string          `json:"validated_ledgers"`
	TxnCount         uint32          `json:"txn_count"` // Only streamed, not in the subscribe result.
}

// Fields from subscribed transaction stream messages
type TransactionStreamMsg struct {
	Transaction         data.TransactionWithMetaData `json:"transaction"`
	EngineResult        data.TransactionResult       `json:"engine_result"`
	EngineResultCode    int                          `json:"engine_result_code"`
	EngineResultMessage string                       `json:"engine_result_message"`
	LedgerHash          data.Hash256                 `json:"ledger_hash"`
	LedgerSequence      uint32                       `json:"ledger_index"`
	Status              string
	Validated           bool
}

// Fields from subscribed server status stream messages
type ServerStreamMsg struct {
	Status     string `json:"server_status"`
	LoadBase   int    `json:"load_base"`
	LoadFactor int    `json:"load_factor"`
	HostID     string `json:"hostid"`
}

// Map message types to the appropriate data structure
var streamMessageFactory = map[string]func() interface{}{
	"ledgerClosed": func() interface{} { return &LedgerStreamMsg{} },
	"transaction":  func() interface{} { return &TransactionStreamMsg{} },
	"serverStatus": func() interface{} { return &ServerStreamMsg{} },
}

type SubscribeCommand struct {
	*Command
	Streams []string                `json:"streams"`
	Books   []OrderBookSubscription `json:"books,omitempty"`
	Result  *SubscribeResult        `json:"result,omitempty"`
}

type SubscribeResult struct {
	// Contains one or both of these, depending what streams were subscribed
	*LedgerStreamMsg
	*ServerStreamMsg
	// Contains "bids" and "asks" when "both" is true.
	Asks []data.OrderBookOffer
	Bids []data.OrderBookOffer
	// Contains "offers" when "both" is false.
	Offers []data.OrderBookOffer
}

// Wrapper to stop recursive unmarshalling
type txStreamJSON TransactionStreamMsg

func (msg *TransactionStreamMsg) UnmarshalJSON(b []byte) error {
	var extract struct {
		*txStreamJSON
		MetaData *data.MetaData `json:"meta"`
	}
	extract.txStreamJSON = (*txStreamJSON)(msg)
	extract.MetaData = &msg.Transaction.MetaData
	return json.Unmarshal(b, &extract)
}
<|endoftext|>


<commit_before>package commands

import (
	"github.com/jfrogdev/jfrog-cli-go/jfrog-cli/artifactory/utils"
	"github.com/jfrogdev/jfrog-cli-go/jfrog-cli/artifactory/utils/spec"
	"github.com/jfrogdev/jfrog-cli-go/jfrog-cli/utils/config"
	clientutils "github.com/jfrogdev/jfrog-cli-go/jfrog-client/artifactory/services/utils"
	"github.com/jfrogdev/jfrog-cli-go/jfrog-client/utils/log"
)

type SearchResult struct {
	Path       string            `json:"path,omitempty"`
	Properties map[string]string `json:"properties,omitempty"`
}

func Search(searchSpec *spec.SpecFiles, artDetails *config.ArtifactoryDetails) ([]SearchResult, error) {
	servicesManager, err := utils.CreateServiceManager(artDetails, false)
	if err != nil {
		return nil, err
	}
	log.Info("Searching artifacts...")
	var resultItems []clientutils.ResultItem
	for i := 0; i < len(searchSpec.Files); i++ {
		params, err := searchSpec.Get(i).ToArtifatorySearchParams()
		if err != nil {
			return nil, err
		}
		currentResultItems, err := servicesManager.Search(&clientutils.SearchParamsImpl{ArtifactoryCommonParams: params})
		if err != nil {
			return nil, err
		}
		resultItems = append(resultItems, currentResultItems...)
	}

	result := aqlResultToSearchResult(resultItems)
	clientutils.LogSearchResults(len(resultItems))
	return result, err
}

func aqlResultToSearchResult(aqlResult []clientutils.ResultItem) (result []SearchResult) {
	result = make([]SearchResult, len(aqlResult))
	for i, v := range aqlResult {
		tempResult := new(SearchResult)
		if v.Path != "." {
			tempResult.Path = v.Repo + "/" + v.Path + "/" + v.Name
		} else {
			tempResult.Path = v.Repo + "/" + v.Name
		}
		tempResult.Properties = make(map[string]string, len(v.Properties))
		for _, prop := range v.Properties {
			tempResult.Properties[prop.Key] = prop.Value
		}
		result[i] = *tempResult
	}
	return
}
<commit_msg>Change on top of pull request #104 from sks/feature/get_props<commit_after>package commands

import (
	"github.com/jfrogdev/jfrog-cli-go/jfrog-cli/artifactory/utils"
	"github.com/jfrogdev/jfrog-cli-go/jfrog-cli/artifactory/utils/spec"
	"github.com/jfrogdev/jfrog-cli-go/jfrog-cli/utils/config"
	clientutils "github.com/jfrogdev/jfrog-cli-go/jfrog-client/artifactory/services/utils"
	"github.com/jfrogdev/jfrog-cli-go/jfrog-client/utils/log"
)

type SearchResult struct {
	Path  string            `json:"path,omitempty"`
	Props map[string]string `json:"props,omitempty"`
}

func Search(searchSpec *spec.SpecFiles, artDetails *config.ArtifactoryDetails) ([]SearchResult, error) {
	servicesManager, err := utils.CreateServiceManager(artDetails, false)
	if err != nil {
		return nil, err
	}
	log.Info("Searching artifacts...")
	var resultItems []clientutils.ResultItem
	for i := 0; i < len(searchSpec.Files); i++ {
		params, err := searchSpec.Get(i).ToArtifatorySearchParams()
		if err != nil {
			return nil, err
		}
		currentResultItems, err := servicesManager.Search(&clientutils.SearchParamsImpl{ArtifactoryCommonParams: params})
		if err != nil {
			return nil, err
		}
		resultItems = append(resultItems, currentResultItems...)
	}

	result := aqlResultToSearchResult(resultItems)
	clientutils.LogSearchResults(len(resultItems))
	return result, err
}

func aqlResultToSearchResult(aqlResult []clientutils.ResultItem) (result []SearchResult) {
	result = make([]SearchResult, len(aqlResult))
	for i, v := range aqlResult {
		tempResult := new(SearchResult)
		if v.Path != "." {
			tempResult.Path = v.Repo + "/" + v.Path + "/" + v.Name
		} else {
			tempResult.Path = v.Repo + "/" + v.Name
		}
		tempResult.Props = make(map[string]string, len(v.Properties))
		for _, prop := range v.Properties {
			tempResult.Props[prop.Key] = prop.Value
		}
		result[i] = *tempResult
	}
	return
}
<|endoftext|>