<commit_before>package relay_test

import (
	"context"
	"net"
	"sync"
	"testing"
	"time"

	libp2p "github.com/libp2p/go-libp2p"
	relay "github.com/libp2p/go-libp2p/p2p/host/relay"

	ggio "github.com/gogo/protobuf/io"
	cid "github.com/ipfs/go-cid"
	autonat "github.com/libp2p/go-libp2p-autonat"
	autonatpb "github.com/libp2p/go-libp2p-autonat/pb"
	circuit "github.com/libp2p/go-libp2p-circuit"
	host "github.com/libp2p/go-libp2p-host"
	inet "github.com/libp2p/go-libp2p-net"
	peer "github.com/libp2p/go-libp2p-peer"
	pstore "github.com/libp2p/go-libp2p-peerstore"
	routing "github.com/libp2p/go-libp2p-routing"
	ma "github.com/multiformats/go-multiaddr"
	manet "github.com/multiformats/go-multiaddr-net"
)

// test specific parameters
func init() {
	autonat.AutoNATIdentifyDelay = 100 * time.Millisecond
	autonat.AutoNATBootDelay = 1 * time.Second
	relay.BootDelay = 1 * time.Second
	manet.Private4 = []*net.IPNet{}
}

// mock routing
type mockRoutingTable struct {
	mx        sync.Mutex
	providers map[string]map[peer.ID]pstore.PeerInfo
}

type mockRouting struct {
	h   host.Host
	tab *mockRoutingTable
}

func newMockRoutingTable() *mockRoutingTable {
	return &mockRoutingTable{providers: make(map[string]map[peer.ID]pstore.PeerInfo)}
}

func newMockRouting(h host.Host, tab *mockRoutingTable) *mockRouting {
	return &mockRouting{h: h, tab: tab}
}

func (m *mockRouting) FindPeer(ctx context.Context, p peer.ID) (pstore.PeerInfo, error) {
	return pstore.PeerInfo{}, routing.ErrNotFound
}

func (m *mockRouting) Provide(ctx context.Context, cid cid.Cid, bcast bool) error {
	m.tab.mx.Lock()
	defer m.tab.mx.Unlock()

	pmap, ok := m.tab.providers[cid.String()]
	if !ok {
		pmap = make(map[peer.ID]pstore.PeerInfo)
		m.tab.providers[cid.String()] = pmap
	}

	pmap[m.h.ID()] = pstore.PeerInfo{ID: m.h.ID(), Addrs: m.h.Addrs()}

	return nil
}

func (m *mockRouting) FindProvidersAsync(ctx context.Context, cid cid.Cid, limit int) <-chan pstore.PeerInfo {
	ch := make(chan pstore.PeerInfo)
	go func() {
		defer close(ch)
		m.tab.mx.Lock()
		defer m.tab.mx.Unlock()

		pmap, ok := m.tab.providers[cid.String()]
		if !ok {
			return
		}

		for _, pi := range pmap {
			select {
			case ch <- pi:
			case <-ctx.Done():
				return
			}
		}
	}()

	return ch
}

// mock autonat
func makeAutoNATServicePrivate(ctx context.Context, t *testing.T) host.Host {
	h, err := libp2p.New(ctx)
	if err != nil {
		t.Fatal(err)
	}
	h.SetStreamHandler(autonat.AutoNATProto, sayAutoNATPrivate)
	return h
}

func sayAutoNATPrivate(s inet.Stream) {
	defer s.Close()
	w := ggio.NewDelimitedWriter(s)
	res := autonatpb.Message{
		Type:         autonatpb.Message_DIAL_RESPONSE.Enum(),
		DialResponse: newDialResponseError(autonatpb.Message_E_DIAL_ERROR, "no dialable addresses"),
	}
	w.WriteMsg(&res)
}

func newDialResponseError(status autonatpb.Message_ResponseStatus, text string) *autonatpb.Message_DialResponse {
	dr := new(autonatpb.Message_DialResponse)
	dr.Status = status.Enum()
	dr.StatusText = &text
	return dr
}

// connector
func connect(t *testing.T, a, b host.Host) {
	pinfo := pstore.PeerInfo{ID: a.ID(), Addrs: a.Addrs()}
	err := b.Connect(context.Background(), pinfo)
	if err != nil {
		t.Fatal(err)
	}
}

// and the actual test!
func TestAutoRelay(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	mtab := newMockRoutingTable()
	makeRouting := func(h host.Host) (routing.PeerRouting, error) {
		mr := newMockRouting(h, mtab)
		return mr, nil
	}

	h1 := makeAutoNATServicePrivate(ctx, t)
	_, err := libp2p.New(ctx, libp2p.EnableRelay(circuit.OptHop), libp2p.Routing(makeRouting))
	if err != nil {
		t.Fatal(err)
	}
	h3, err := libp2p.New(ctx, libp2p.EnableRelay(), libp2p.Routing(makeRouting))
	if err != nil {
		t.Fatal(err)
	}
	h4, err := libp2p.New(ctx, libp2p.EnableRelay())

	connect(t, h1, h3)
	time.Sleep(3 * time.Second)

	// verify that we advertise relay addrs
	haveRelay := false
	for _, addr := range h3.Addrs() {
		_, err := addr.ValueForProtocol(circuit.P_CIRCUIT)
		if err == nil {
			haveRelay = true
			break
		}
	}

	if !haveRelay {
		t.Fatal("No relay addrs advertised")
	}

	// check that we can connect through the relay
	var raddrs []ma.Multiaddr
	for _, addr := range h3.Addrs() {
		_, err := addr.ValueForProtocol(circuit.P_CIRCUIT)
		if err == nil {
			raddrs = append(raddrs, addr)
		}
	}

	err = h4.Connect(ctx, pstore.PeerInfo{ID: h3.ID(), Addrs: raddrs})
	if err != nil {
		t.Fatal(err)
	}
}
<commit_msg>extend autorelay test to verify pushing of relay addrs<commit_after>package relay_test

import (
	"context"
	"net"
	"sync"
	"testing"
	"time"

	libp2p "github.com/libp2p/go-libp2p"
	relay "github.com/libp2p/go-libp2p/p2p/host/relay"

	ggio "github.com/gogo/protobuf/io"
	cid "github.com/ipfs/go-cid"
	autonat "github.com/libp2p/go-libp2p-autonat"
	autonatpb "github.com/libp2p/go-libp2p-autonat/pb"
	circuit "github.com/libp2p/go-libp2p-circuit"
	host "github.com/libp2p/go-libp2p-host"
	inet "github.com/libp2p/go-libp2p-net"
	peer "github.com/libp2p/go-libp2p-peer"
	pstore "github.com/libp2p/go-libp2p-peerstore"
	routing "github.com/libp2p/go-libp2p-routing"
	ma "github.com/multiformats/go-multiaddr"
	manet "github.com/multiformats/go-multiaddr-net"
)

// test specific parameters
func init() {
	autonat.AutoNATIdentifyDelay = 100 * time.Millisecond
	autonat.AutoNATBootDelay = 1 * time.Second
	relay.BootDelay = 1 * time.Second
	manet.Private4 = []*net.IPNet{}
}

// mock routing
type mockRoutingTable struct {
	mx        sync.Mutex
	providers map[string]map[peer.ID]pstore.PeerInfo
}

type mockRouting struct {
	h   host.Host
	tab *mockRoutingTable
}

func newMockRoutingTable() *mockRoutingTable {
	return &mockRoutingTable{providers: make(map[string]map[peer.ID]pstore.PeerInfo)}
}

func newMockRouting(h host.Host, tab *mockRoutingTable) *mockRouting {
	return &mockRouting{h: h, tab: tab}
}

func (m *mockRouting) FindPeer(ctx context.Context, p peer.ID) (pstore.PeerInfo, error) {
	return pstore.PeerInfo{}, routing.ErrNotFound
}

func (m *mockRouting) Provide(ctx context.Context, cid cid.Cid, bcast bool) error {
	m.tab.mx.Lock()
	defer m.tab.mx.Unlock()

	pmap, ok := m.tab.providers[cid.String()]
	if !ok {
		pmap = make(map[peer.ID]pstore.PeerInfo)
		m.tab.providers[cid.String()] = pmap
	}

	pmap[m.h.ID()] = pstore.PeerInfo{ID: m.h.ID(), Addrs: m.h.Addrs()}

	return nil
}

func (m *mockRouting) FindProvidersAsync(ctx context.Context, cid cid.Cid, limit int) <-chan pstore.PeerInfo {
	ch := make(chan pstore.PeerInfo)
	go func() {
		defer close(ch)
		m.tab.mx.Lock()
		defer m.tab.mx.Unlock()

		pmap, ok := m.tab.providers[cid.String()]
		if !ok {
			return
		}

		for _, pi := range pmap {
			select {
			case ch <- pi:
			case <-ctx.Done():
				return
			}
		}
	}()

	return ch
}

// mock autonat
func makeAutoNATServicePrivate(ctx context.Context, t *testing.T) host.Host {
	h, err := libp2p.New(ctx)
	if err != nil {
		t.Fatal(err)
	}
	h.SetStreamHandler(autonat.AutoNATProto, sayAutoNATPrivate)
	return h
}

func sayAutoNATPrivate(s inet.Stream) {
	defer s.Close()
	w := ggio.NewDelimitedWriter(s)
	res := autonatpb.Message{
		Type:         autonatpb.Message_DIAL_RESPONSE.Enum(),
		DialResponse: newDialResponseError(autonatpb.Message_E_DIAL_ERROR, "no dialable addresses"),
	}
	w.WriteMsg(&res)
}

func newDialResponseError(status autonatpb.Message_ResponseStatus, text string) *autonatpb.Message_DialResponse {
	dr := new(autonatpb.Message_DialResponse)
	dr.Status = status.Enum()
	dr.StatusText = &text
	return dr
}

// connector
func connect(t *testing.T, a, b host.Host) {
	pinfo := pstore.PeerInfo{ID: a.ID(), Addrs: a.Addrs()}
	err := b.Connect(context.Background(), pinfo)
	if err != nil {
		t.Fatal(err)
	}
}

// and the actual test!
func TestAutoRelay(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	mtab := newMockRoutingTable()
	makeRouting := func(h host.Host) (routing.PeerRouting, error) {
		mr := newMockRouting(h, mtab)
		return mr, nil
	}

	h1 := makeAutoNATServicePrivate(ctx, t)
	_, err := libp2p.New(ctx, libp2p.EnableRelay(circuit.OptHop), libp2p.Routing(makeRouting))
	if err != nil {
		t.Fatal(err)
	}
	h3, err := libp2p.New(ctx, libp2p.EnableRelay(), libp2p.Routing(makeRouting))
	if err != nil {
		t.Fatal(err)
	}
	h4, err := libp2p.New(ctx, libp2p.EnableRelay())

	// verify that we don't advertise relay addrs initially
	for _, addr := range h3.Addrs() {
		_, err := addr.ValueForProtocol(circuit.P_CIRCUIT)
		if err == nil {
			t.Fatal("relay addr advertised before auto detection")
		}
	}

	// connect to AutoNAT and let detection/discovery work its magic
	connect(t, h1, h3)
	time.Sleep(3 * time.Second)

	// verify that we now advertise relay addrs (but not unspecific relay addrs)
	unspecificRelay, err := ma.NewMultiaddr("/p2p-circuit")
	if err != nil {
		t.Fatal(err)
	}

	haveRelay := false
	for _, addr := range h3.Addrs() {
		if addr.Equal(unspecificRelay) {
			t.Fatal("unspecific relay addr advertised")
		}

		_, err := addr.ValueForProtocol(circuit.P_CIRCUIT)
		if err == nil {
			haveRelay = true
		}
	}

	if !haveRelay {
		t.Fatal("No relay addrs advertised")
	}

	// verify that we can connect through the relay
	var raddrs []ma.Multiaddr
	for _, addr := range h3.Addrs() {
		_, err := addr.ValueForProtocol(circuit.P_CIRCUIT)
		if err == nil {
			raddrs = append(raddrs, addr)
		}
	}

	err = h4.Connect(ctx, pstore.PeerInfo{ID: h3.ID(), Addrs: raddrs})
	if err != nil {
		t.Fatal(err)
	}

	// verify that we have pushed relay addrs to connected peers
	haveRelay = false
	for _, addr := range h1.Peerstore().Addrs(h3.ID()) {
		if addr.Equal(unspecificRelay) {
			t.Fatal("unspecific relay addr advertised")
		}

		_, err := addr.ValueForProtocol(circuit.P_CIRCUIT)
		if err == nil {
			haveRelay = true
		}
	}

	if !haveRelay {
		t.Fatal("No relay addrs pushed")
	}
}
<|endoftext|>


<commit_before>package disruptor

import "sync/atomic"

type SharedWriter struct {
	capacity  int64
	gate      *Cursor
	mask      int64
	shift     uint8
	committed []int32
	upstream  Barrier
	written   *Cursor
}

func NewSharedWriter(write *SharedWriterBarrier, upstream Barrier) *SharedWriter {
	return &SharedWriter{
		capacity:  write.capacity,
		gate:      NewCursor(),
		mask:      write.mask,
		shift:     write.shift,
		committed: write.committed,
		upstream:  upstream,
		written:   write.written,
	}
}

func (this *SharedWriter) Reserve(count int64) (int64, int64) {
	for {
		previous := this.written.Load()
		upper := previous + count
		wrap := upper - this.capacity

		if wrap > this.gate.Load() {
			min := this.upstream.LoadBarrier(0)
			if wrap > min {
				return InitialSequenceValue, Gating
			}

			this.gate.Store(min)
		}

		if atomic.CompareAndSwapInt64(&this.written.sequence, previous, upper) {
			return previous + 1, upper
		}
	}
}

func (this *SharedWriter) Commit(lower, upper int64) {
	for shift, mask := this.shift, this.mask; lower <= upper; lower++ {
		this.committed[lower&mask] = int32(lower >> shift)
	}
}
<commit_msg>Additional TODO.<commit_after>package disruptor

import "sync/atomic"

type SharedWriter struct {
	capacity  int64
	gate      *Cursor
	mask      int64
	shift     uint8
	committed []int32
	upstream  Barrier
	written   *Cursor
}

func NewSharedWriter(write *SharedWriterBarrier, upstream Barrier) *SharedWriter {
	return &SharedWriter{
		capacity:  write.capacity,
		gate:      NewCursor(),
		mask:      write.mask,
		shift:     write.shift,
		committed: write.committed,
		upstream:  upstream,
		written:   write.written,
	}
}

func (this *SharedWriter) Reserve(count int64) (int64, int64) {
	for {
		previous := this.written.Load()
		upper := previous + count
		wrap := upper - this.capacity

		if wrap > this.gate.Load() {
			min := this.upstream.LoadBarrier(0)
			if wrap > min {
				return InitialSequenceValue, Gating
			}

			this.gate.Store(min)
		}

		if atomic.CompareAndSwapInt64(&this.written.sequence, previous, upper) {
			return previous + 1, upper
		}
	}
}

func (this *SharedWriter) Commit(lower, upper int64) {
	// POTENTIAL TODO: start from upper and work toward lower
	// this may have the effect of keeping a batch together which
	// might otherwise be split up...
	for shift, mask := this.shift, this.mask; lower <= upper; lower++ {
		this.committed[lower&mask] = int32(lower >> shift)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"go/ast"
	"go/token"
	"fmt"
	"reflect"
	"io"
)

const (
	DECL_CONST = iota
	DECL_VAR
	DECL_TYPE
	DECL_FUNC
	DECL_MODULE
)

var declClassToString = map[int]string{
	0: "const",
	1: "var",
	2: "type",
	3: "func",
	4: "module",
}

type Decl struct {
	Name string
	Type ast.Expr
	Class int

	// functions for interface type, fields+methods for struct type
	// variables with anonymous struct/interface type may have children too
	Children []*Decl

	// if the type is unknown at AST building time, I'm using these
	Value ast.Expr

	// if it's a multiassignment and the Value is a CallExpr, it is being set
	// to an index into the return value tuple, otherwise it's a -1
	ValueIndex int
}

func astDeclType(d ast.Decl) ast.Expr {
	switch t := d.(type) {
	case *ast.GenDecl:
		switch t.Tok {
		case token.CONST, token.VAR:
			c := t.Specs[0].(*ast.ValueSpec)
			return c.Type
		case token.TYPE:
			t := t.Specs[0].(*ast.TypeSpec)
			return t.Type
		}
	case *ast.FuncDecl:
		return t.Type
	}
	panic("unreachable")
	return nil
}

func astDeclClass(d ast.Decl) int {
	switch t := d.(type) {
	case *ast.GenDecl:
		switch t.Tok {
		case token.VAR:
			return DECL_VAR
		case token.CONST:
			return DECL_CONST
		case token.TYPE:
			return DECL_TYPE
		}
	case *ast.FuncDecl:
		return DECL_FUNC
	}
	panic("unreachable")
	return 0
}

func astDeclConvertable(d ast.Decl) bool {
	switch t := d.(type) {
	case *ast.GenDecl:
		switch t.Tok {
		case token.VAR, token.CONST, token.TYPE:
			return true
		}
	case *ast.FuncDecl:
		return true
	}
	return false
}

func astFieldListToDecls(f *ast.FieldList, class int) []*Decl {
	count := 0
	for _, field := range f.List {
		count += len(field.Names)
	}

	decls := make([]*Decl, count)
	i := 0
	for _, field := range f.List {
		for _, name := range field.Names {
			decls[i] = new(Decl)
			decls[i].Name = name.Name()
			decls[i].Type = field.Type
			decls[i].Class = class
			decls[i].Children = astTypeToChildren(field.Type)
			i++
		}
	}
	return decls
}

func astTypeToChildren(ty ast.Expr) []*Decl {
	// TODO: type embedding
	switch t := ty.(type) {
	case *ast.StructType:
		return astFieldListToDecls(t.Fields, DECL_VAR)
	case *ast.InterfaceType:
		return astFieldListToDecls(t.Methods, DECL_FUNC)
	}
	return nil
}

func astDeclToDecl(name string, d ast.Decl, value ast.Expr, vindex int) *Decl {
	if !astDeclConvertable(d) || name == "_" {
		return nil
	}
	decl := new(Decl)
	decl.Name = name
	decl.Type = astDeclType(d)
	decl.Class = astDeclClass(d)
	decl.Children = astTypeToChildren(decl.Type)
	decl.Value = value
	decl.ValueIndex = vindex

	return decl
}

func NewDecl(name string, class int) *Decl {
	decl := new(Decl)
	decl.Name = name
	decl.Class = class
	decl.ValueIndex = -1
	return decl
}

func NewDeclVar(name string, typ ast.Expr, value ast.Expr, vindex int) *Decl {
	if name == "_" {
		return nil
	}
	decl := new(Decl)
	decl.Name = name
	decl.Class = DECL_VAR
	decl.Type = typ
	decl.Value = value
	decl.ValueIndex = vindex
	return decl
}

func MethodOf(d ast.Decl) string {
	if t, ok := d.(*ast.FuncDecl); ok {
		if t.Recv != nil {
			switch t := t.Recv.List[0].Type.(type) {
			case *ast.StarExpr:
				return t.X.(*ast.Ident).Name()
			case *ast.Ident:
				return t.Name()
			default:
				return ""
			}
		}
	}
	return ""
}

// complete deep copy
func (d *Decl) Copy(other *Decl) {
	d.Name = other.Name
	d.Class = other.Class
	d.Type = other.Type
	d.Value = other.Value
	d.ValueIndex = other.ValueIndex
	d.Children = other.Children
}

func (d *Decl) ClassName() string {
	return declClassToString[d.Class]
}

func (d *Decl) Expand(other *Decl) {
	// in case if it's a variable, just replace an old one with a new one
	if d.Class == DECL_VAR {
		d.Copy(other)
		return
	}

	// otherwise apply Type and Class and append Children
	d.Type = other.Type
	d.Class = other.Class

	if other.Children == nil {
		return
	}

	for _, c := range other.Children {
		d.AddChild(c)
	}
}

func (d *Decl) Matches(p string) bool {
	if p != "" && !startsWith(d.Name, p) {
		return false
	}
	return true
}

func (d *Decl) PrettyPrintType(out io.Writer, ac *AutoCompleteContext) {
	switch d.Class {
	case DECL_TYPE:
		switch t := d.Type.(type) {
		case *ast.StructType:
			fmt.Fprintf(out, "struct")
		case *ast.InterfaceType:
			fmt.Fprintf(out, "interface")
		default:
			ac.prettyPrintTypeExpr(out, d.Type)
		}
	case DECL_VAR:
		if d.Type != nil {
			ac.prettyPrintTypeExpr(out, d.Type)
		}
	case DECL_FUNC:
		ac.prettyPrintTypeExpr(out, d.Type)
	}
}

func (d *Decl) AddChild(cd *Decl) {
	if d.FindChild(cd.Name) != nil {
		return
	}

	if d.Children == nil {
		d.Children = make([]*Decl, 0, 4)
	}

	if cap(d.Children) < len(d.Children)+1 {
		newcap := cap(d.Children) * 2
		if newcap == 0 {
			newcap = 4
		}

		s := make([]*Decl, len(d.Children), newcap)
		copy(s, d.Children)
		d.Children = s
	}

	i := len(d.Children)
	d.Children = d.Children[0:i+1]
	d.Children[i] = cd
}

func checkForBuiltinFuncs(c *ast.CallExpr) ast.Expr {
	if t, ok := c.Fun.(*ast.Ident); ok {
		switch t.Name() {
		case "new":
			e := new(ast.StarExpr)
			e.X = c.Args[0]
			return e
		case "make":
			return c.Args[0]
		case "cmplx":
			return ast.NewIdent("complex")
		}
	}
	return nil
}

func funcReturnType(f *ast.FuncType, index int) ast.Expr {
	if index == -1 {
		return f.Results.List[0].Type
	}

	i := 0
	var field *ast.Field
	for _, field = range f.Results.List {
		if i >= index {
			return field.Type
		}
		if field.Names != nil {
			i += len(field.Names)
		} else {
			i++
		}
	}
	if i >= index {
		return field.Type
	}
	return nil
}

func typePath(e ast.Expr) string {
	switch t := e.(type) {
	case *ast.Ident:
		return t.Name()
	case *ast.StarExpr:
		return typePath(t.X)
	case *ast.SelectorExpr:
		path := ""
		if ident, ok := t.X.(*ast.Ident); ok {
			path += ident.Name()
		}
		return path + "." + t.Sel.Name()
	}
	return ""
}

// return expr and true if it's a type or false if it's a value
func inferType(v ast.Expr, index int, topLevel *AutoCompleteContext) (ast.Expr, bool) {
	//ty := reflect.Typeof(v)
	//fmt.Println(ty)
	switch t := v.(type) {
	case *ast.CompositeLit:
		return t.Type, true
	case *ast.Ident:
		if d := topLevel.findDecl(t.Name()); d != nil {
			return d.InferType(topLevel), d.Class == DECL_TYPE
		}
		return t, true // probably a builtin
	case *ast.UnaryExpr:
		switch t.Op {
		case token.AND:
			it, _ := inferType(t.X, -1, topLevel)
			if it == nil {
				break
			}

			e := new(ast.StarExpr)
			e.X = it
			return e, false
		// TODO: channel ops
		}
	case *ast.IndexExpr:
		it, isType := inferType(t.X, -1, topLevel)
		if it == nil {
			break
		}
		switch t := it.(type) {
		case *ast.ArrayType:
			return t.Elt, isType
		case *ast.MapType:
			switch index {
			case -1, 0:
				return t.Value, isType
			case 1:
				return ast.NewIdent("bool"), true
			}
		}
	case *ast.StarExpr:
		it, isType := inferType(t.X, -1, topLevel)
		if it == nil {
			break
		}
		if isType {
			e := new(ast.StarExpr)
			e.X = it
			return e, true
		} else if s, ok := it.(*ast.StarExpr); ok {
			return s.X, false
		}
	case *ast.CallExpr:
		ty := checkForBuiltinFuncs(t)
		if ty != nil {
			return ty, true
		}

		it, _ := inferType(t.Fun, -1, topLevel)
		if it == nil {
			break
		}
		if f, ok := it.(*ast.FuncType); ok {
			return funcReturnType(f, index), true
		} else {
			return it, true
		}
	case *ast.ParenExpr:
		it, isType := inferType(t.X, -1, topLevel)
		if it == nil {
			break
		}
		return it, isType
	case *ast.SelectorExpr:
		it, _ := inferType(t.X, -1, topLevel)
		if it == nil {
			break
		}

		name := typePath(it)
		if d := topLevel.findDeclByPath(name); d != nil {
			c := d.FindChild(t.Sel.Name())
			if c != nil {
				return c.InferType(topLevel), c.Class == DECL_TYPE
			}
		}
	case *ast.FuncLit:
		return t.Type, true
	case *ast.TypeAssertExpr:
		if t.Type == nil {
			return inferType(t.X, -1, topLevel)
		}
		switch index {
		case -1, 0:
			return t.Type, true
		case 1:
			return ast.NewIdent("bool"), true
		}
	// TODO: channels here
	case *ast.ArrayType, *ast.MapType:
		return t, true
	default:
		_ = reflect.Typeof(v)
		//fmt.Println(ty)
	}
	return nil, false
}

func (d *Decl) InferType(topLevel *AutoCompleteContext) ast.Expr {
	if d.Class == DECL_TYPE || d.Class == DECL_MODULE {
		// we're the type itself
		return ast.NewIdent(d.Name)
	}

	// shortcut
	if d.Type != nil {
		return d.Type
	}

	d.Type, _ = inferType(d.Value, d.ValueIndex, topLevel)
	return d.Type
}

func (d *Decl) FindChild(name string) *Decl {
	for _, c := range d.Children {
		if c.Name == name {
			return c
		}
	}
	return nil
}
<commit_msg>Add builtin function "closed" support for type inference.<commit_after>package main

import (
	"go/ast"
	"go/token"
	"fmt"
	"reflect"
	"io"
)

const (
	DECL_CONST = iota
	DECL_VAR
	DECL_TYPE
	DECL_FUNC
	DECL_MODULE
)

var declClassToString = map[int]string{
	0: "const",
	1: "var",
	2: "type",
	3: "func",
	4: "module",
}

type Decl struct {
	Name string
	Type ast.Expr
	Class int

	// functions for interface type, fields+methods for struct type
	// variables with anonymous struct/interface type may have children too
	Children []*Decl

	// if the type is unknown at AST building time, I'm using these
	Value ast.Expr

	// if it's a multiassignment and the Value is a CallExpr, it is being set
	// to an index into the return value tuple, otherwise it's a -1
	ValueIndex int
}

func astDeclType(d ast.Decl) ast.Expr {
	switch t := d.(type) {
	case *ast.GenDecl:
		switch t.Tok {
		case token.CONST, token.VAR:
			c := t.Specs[0].(*ast.ValueSpec)
			return c.Type
		case token.TYPE:
			t := t.Specs[0].(*ast.TypeSpec)
			return t.Type
		}
	case *ast.FuncDecl:
		return t.Type
	}
	panic("unreachable")
	return nil
}

func astDeclClass(d ast.Decl) int {
	switch t := d.(type) {
	case *ast.GenDecl:
		switch t.Tok {
		case token.VAR:
			return DECL_VAR
		case token.CONST:
			return DECL_CONST
		case token.TYPE:
			return DECL_TYPE
		}
	case *ast.FuncDecl:
		return DECL_FUNC
	}
	panic("unreachable")
	return 0
}

func astDeclConvertable(d ast.Decl) bool {
	switch t := d.(type) {
	case *ast.GenDecl:
		switch t.Tok {
		case token.VAR, token.CONST, token.TYPE:
			return true
		}
	case *ast.FuncDecl:
		return true
	}
	return false
}

func astFieldListToDecls(f *ast.FieldList, class int) []*Decl {
	count := 0
	for _, field := range f.List {
		count += len(field.Names)
	}

	decls := make([]*Decl, count)
	i := 0
	for _, field := range f.List {
		for _, name := range field.Names {
			decls[i] = new(Decl)
			decls[i].Name = name.Name()
			decls[i].Type = field.Type
			decls[i].Class = class
			decls[i].Children = astTypeToChildren(field.Type)
			i++
		}
	}
	return decls
}

func astTypeToChildren(ty ast.Expr) []*Decl {
	// TODO: type embedding
	switch t := ty.(type) {
	case *ast.StructType:
		return astFieldListToDecls(t.Fields, DECL_VAR)
	case *ast.InterfaceType:
		return astFieldListToDecls(t.Methods, DECL_FUNC)
	}
	return nil
}

func astDeclToDecl(name string, d ast.Decl, value ast.Expr, vindex int) *Decl {
	if !astDeclConvertable(d) || name == "_" {
		return nil
	}
	decl := new(Decl)
	decl.Name = name
	decl.Type = astDeclType(d)
	decl.Class = astDeclClass(d)
	decl.Children = astTypeToChildren(decl.Type)
	decl.Value = value
	decl.ValueIndex = vindex

	return decl
}

func NewDecl(name string, class int) *Decl {
	decl := new(Decl)
	decl.Name = name
	decl.Class = class
	decl.ValueIndex = -1
	return decl
}

func NewDeclVar(name string, typ ast.Expr, value ast.Expr, vindex int) *Decl {
	if name == "_" {
		return nil
	}
	decl := new(Decl)
	decl.Name = name
	decl.Class = DECL_VAR
	decl.Type = typ
	decl.Value = value
	decl.ValueIndex = vindex
	return decl
}

func MethodOf(d ast.Decl) string {
	if t, ok := d.(*ast.FuncDecl); ok {
		if t.Recv != nil {
			switch t := t.Recv.List[0].Type.(type) {
			case *ast.StarExpr:
				return t.X.(*ast.Ident).Name()
			case *ast.Ident:
				return t.Name()
			default:
				return ""
			}
		}
	}
	return ""
}

// complete deep copy
func (d *Decl) Copy(other *Decl) {
	d.Name = other.Name
	d.Class = other.Class
	d.Type = other.Type
	d.Value = other.Value
	d.ValueIndex = other.ValueIndex
	d.Children = other.Children
}

func (d *Decl) ClassName() string {
	return declClassToString[d.Class]
}

func (d *Decl) Expand(other *Decl) {
	// in case if it's a variable, just replace an old one with a new one
	if d.Class == DECL_VAR {
		d.Copy(other)
		return
	}

	// otherwise apply Type and Class and append Children
	d.Type = other.Type
	d.Class = other.Class

	if other.Children == nil {
		return
	}

	for _, c := range other.Children {
		d.AddChild(c)
	}
}

func (d *Decl) Matches(p string) bool {
	if p != "" && !startsWith(d.Name, p) {
		return false
	}
	return true
}

func (d *Decl) PrettyPrintType(out io.Writer, ac *AutoCompleteContext) {
	switch d.Class {
	case DECL_TYPE:
		switch t := d.Type.(type) {
		case *ast.StructType:
			fmt.Fprintf(out, "struct")
		case *ast.InterfaceType:
			fmt.Fprintf(out, "interface")
		default:
			ac.prettyPrintTypeExpr(out, d.Type)
		}
	case DECL_VAR:
		if d.Type != nil {
			ac.prettyPrintTypeExpr(out, d.Type)
		}
	case DECL_FUNC:
		ac.prettyPrintTypeExpr(out, d.Type)
	}
}

func (d *Decl) AddChild(cd *Decl) {
	if d.FindChild(cd.Name) != nil {
		return
	}

	if d.Children == nil {
		d.Children = make([]*Decl, 0, 4)
	}

	if cap(d.Children) < len(d.Children)+1 {
		newcap := cap(d.Children) * 2
		if newcap == 0 {
			newcap = 4
		}

		s := make([]*Decl, len(d.Children), newcap)
		copy(s, d.Children)
		d.Children = s
	}

	i := len(d.Children)
	d.Children = d.Children[0:i+1]
	d.Children[i] = cd
}

func checkForBuiltinFuncs(c *ast.CallExpr) ast.Expr {
	if t, ok := c.Fun.(*ast.Ident); ok {
		switch t.Name() {
		case "new":
			e := new(ast.StarExpr)
			e.X = c.Args[0]
			return e
		case "make":
			return c.Args[0]
		case "cmplx":
			return ast.NewIdent("complex")
		case "closed":
			return ast.NewIdent("bool")
		}
	}
	return nil
}

func funcReturnType(f *ast.FuncType, index int) ast.Expr {
	if index == -1 {
		return f.Results.List[0].Type
	}

	i := 0
	var field *ast.Field
	for _, field = range f.Results.List {
		if i >= index {
			return field.Type
		}
		if field.Names != nil {
			i += len(field.Names)
		} else {
			i++
		}
	}
	if i >= index {
		return field.Type
	}
	return nil
}

func typePath(e ast.Expr) string {
	switch t := e.(type) {
	case *ast.Ident:
		return t.Name()
	case *ast.StarExpr:
		return typePath(t.X)
	case *ast.SelectorExpr:
		path := ""
		if ident, ok := t.X.(*ast.Ident); ok {
			path += ident.Name()
		}
		return path + "." + t.Sel.Name()
	}
	return ""
}

// return expr and true if it's a type or false if it's a value
func inferType(v ast.Expr, index int, topLevel *AutoCompleteContext) (ast.Expr, bool) {
	//ty := reflect.Typeof(v)
	//fmt.Println(ty)
	switch t := v.(type) {
	case *ast.CompositeLit:
		return t.Type, true
	case *ast.Ident:
		if d := topLevel.findDecl(t.Name()); d != nil {
			return d.InferType(topLevel), d.Class == DECL_TYPE
		}
		return t, true // probably a builtin
	case *ast.UnaryExpr:
		switch t.Op {
		case token.AND:
			it, _ := inferType(t.X, -1, topLevel)
			if it == nil {
				break
			}

			e := new(ast.StarExpr)
			e.X = it
			return e, false
		// TODO: channel ops
		}
	case *ast.IndexExpr:
		it, isType := inferType(t.X, -1, topLevel)
		if it == nil {
			break
		}
		switch t := it.(type) {
		case *ast.ArrayType:
			return t.Elt, isType
		case *ast.MapType:
			switch index {
			case -1, 0:
				return t.Value, isType
			case 1:
				return ast.NewIdent("bool"), true
			}
		}
	case *ast.StarExpr:
		it, isType := inferType(t.X, -1, topLevel)
		if it == nil {
			break
		}
		if isType {
			e := new(ast.StarExpr)
			e.X = it
			return e, true
		} else if s, ok := it.(*ast.StarExpr); ok {
			return s.X, false
		}
	case *ast.CallExpr:
		ty := checkForBuiltinFuncs(t)
		if ty != nil {
			return ty, true
		}

		it, _ := inferType(t.Fun, -1, topLevel)
		if it == nil {
			break
		}
		if f, ok := it.(*ast.FuncType); ok {
			return funcReturnType(f, index), true
		} else {
			return it, true
		}
	case *ast.ParenExpr:
		it, isType := inferType(t.X, -1, topLevel)
		if it == nil {
			break
		}
		return it, isType
	case *ast.SelectorExpr:
		it, _ := inferType(t.X, -1, topLevel)
		if it == nil {
			break
		}

		name := typePath(it)
		if d := topLevel.findDeclByPath(name); d != nil {
			c := d.FindChild(t.Sel.Name())
			if c != nil {
				return c.InferType(topLevel), c.Class == DECL_TYPE
			}
		}
	case *ast.FuncLit:
		return t.Type, true
	case *ast.TypeAssertExpr:
		if t.Type == nil {
			return inferType(t.X, -1, topLevel)
		}
		switch index {
		case -1, 0:
			return t.Type, true
		case 1:
			return ast.NewIdent("bool"), true
		}
	// TODO: channels here
	case *ast.ArrayType, *ast.MapType:
		return t, true
	default:
		_ = reflect.Typeof(v)
		//fmt.Println(ty)
	}
	return nil, false
}

func (d *Decl) InferType(topLevel *AutoCompleteContext) ast.Expr {
	if d.Class == DECL_TYPE || d.Class == DECL_MODULE {
		// we're the type itself
		return ast.NewIdent(d.Name)
	}

	// shortcut
	if d.Type != nil {
		return d.Type
	}

	d.Type, _ = inferType(d.Value, d.ValueIndex, topLevel)
	return d.Type
}

func (d *Decl) FindChild(name string) *Decl {
	for _, c := range d.Children {
		if c.Name == name {
			return c
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package gs

import (
	"bytes"
	"encoding/gob"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"code.google.com/p/goauth2/oauth"
)

var g_config = &oauth.Config{
	ClientId:     "",                                          // Set by --clientid or --clientid_file
	ClientSecret: "",                                          // Set by --secret or --secret_file
	Scope:        "https://www.googleapis.com/auth/moderator", // filled in per-API
	AuthURL:      "https://accounts.google.com/o/oauth2/auth",
	TokenURL:     "https://accounts.google.com/o/oauth2/token",
	AccessType:   "offline",
}

// Flags
var (
	clientId     = flag.String("clientid", "", "OAuth Client ID.  If non-empty, overrides --clientid_file")
	clientIdFile = flag.String("clientid_file", "clientid.dat",
		"Name of a file containing just the project's OAuth Client ID from https://code.google.com/apis/console/")
	secret     = flag.String("secret", "", "OAuth Client Secret.  If non-empty, overrides --secret_file")
	secretFile = flag.String("secret_file", "clientsecret.dat",
		"Name of a file containing just the project's OAuth Client Secret from https://code.google.com/apis/console/")
	cacheToken = flag.Bool("cachetoken", true, "cache the OAuth token")
	debug      = flag.Bool("debug", false, "show HTTP traffic")
)

func newOauthClient(scope string) *http.Client {
	g_config.Scope = scope
	g_config.ClientId = valueOrFileContents(*clientId, *clientIdFile)
	g_config.ClientSecret = valueOrFileContents(*secret, *secretFile)

	return getOAuthClient(g_config)
}

func osUserCacheDir() string {
	switch runtime.GOOS {
	case "darwin":
		return filepath.Join(os.Getenv("HOME"), "Library", "Caches")
	case "linux", "freebsd":
		return filepath.Join(os.Getenv("HOME"), ".cache")
	}
	log.Printf("TODO: osUserCacheDir on GOOS %q", runtime.GOOS)
	return "."
}

func tokenCacheFile(g_config *oauth.Config) string {
	return filepath.Join(osUserCacheDir(), url.QueryEscape(
		fmt.Sprintf("go-api-demo-%s-%s-%s", g_config.ClientId, g_config.ClientSecret, g_config.Scope)))
}

func tokenFromFile(file string) (*oauth.Token, error) {
	if !*cacheToken {
		return nil, errors.New("--cachetoken is false")
	}
	f, err := os.Open(file)
	if err != nil {
		return nil, err
	}
	t := new(oauth.Token)
	err = gob.NewDecoder(f).Decode(t)
	return t, err
}

func saveToken(file string, token *oauth.Token) {
	f, err := os.Create(file)
	if err != nil {
		log.Printf("Warning: failed to cache oauth token: %v", err)
		return
	}
	defer f.Close()
	gob.NewEncoder(f).Encode(token)
}

func condDebugTransport(rt http.RoundTripper) http.RoundTripper {
	if *debug {
		return &logTransport{rt}
	}
	return rt
}

func getOAuthClient(g_config *oauth.Config) *http.Client {
	cacheFile := tokenCacheFile(g_config)
	token, err := tokenFromFile(cacheFile)
	if err != nil {
		token = tokenFromWeb(g_config)
		saveToken(cacheFile, token)
	} else {
		// log.Printf("Using cached token %#v from %q", token, cacheFile)
	}

	t := &oauth.Transport{
		Token:     token,
		Config:    g_config,
		Transport: condDebugTransport(http.DefaultTransport),
	}
	return t.Client()
}

func tokenFromWeb(g_config *oauth.Config) *oauth.Token {
	ch := make(chan string)
	randState := fmt.Sprintf("st%d", time.Now())
	ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		if req.URL.Path == "/favicon.ico" {
			http.Error(rw, "", 404)
			return
		}
		if req.FormValue("state") != randState {
			log.Printf("State doesn't match: req = %#v", req)
			http.Error(rw, "", 500)
			return
		}
		if code := req.FormValue("code"); code != "" {
			fmt.Fprintf(rw, "
Success
Authorized.")
			rw.(http.Flusher).Flush()
			ch <- code
			return
		}
		log.Printf("no code")
		http.Error(rw, "", 500)
	}))
	defer ts.Close()

	g_config.RedirectURL = ts.URL
	fmt.Println(ts.URL)
	authUrl := g_config.AuthCodeURL(randState)
	go openUrl(authUrl)
	log.Printf("Authorize this app at: %s", authUrl)
	code := <-ch
	log.Printf("Got code: %s", code)

	t := &oauth.Transport{
		Config:    g_config,
		Transport: condDebugTransport(http.DefaultTransport),
	}
	_, err := t.Exchange(code)
	if err != nil {
		log.Fatalf("Token exchange error: %v", err)
	}
	return t.Token
}

func openUrl(url string) {
	try := []string{"xdg-open", "google-chrome", "open"}
	for _, bin := range try {
		err := exec.Command(bin, url).Run()
		if err == nil {
			return
		}
	}
	log.Printf("Error opening URL in browser.")
}

func valueOrFileContents(value string, filename string) string {
	if value != "" {
		return value
	}
	slurp, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatalf("Error reading %q: %v", filename, err)
	}
	return strings.TrimSpace(string(slurp))
}

type logTransport struct {
	rt http.RoundTripper
}

func (t *logTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	var buf bytes.Buffer

	os.Stdout.Write([]byte("\n[request]\n"))
	if req.Body != nil {
		req.Body = ioutil.NopCloser(&readButCopy{req.Body, &buf})
	}
	req.Write(os.Stdout)
	if req.Body != nil {
		req.Body = ioutil.NopCloser(&buf)
	}
	os.Stdout.Write([]byte("\n[/request]\n"))

	res, err := t.rt.RoundTrip(req)

	fmt.Printf("[response]\n")
	if err != nil {
		fmt.Printf("ERROR: %v", err)
	} else {
		body := res.Body
		res.Body = nil
		res.Write(os.Stdout)
		if body != nil {
			res.Body = ioutil.NopCloser(&echoAsRead{body})
		}
	}

	return res, err
}

type echoAsRead struct {
	src io.Reader
}

func (r *echoAsRead) Read(p []byte) (int, error) {
	n, err := r.src.Read(p)
	if n > 0 {
		os.Stdout.Write(p[:n])
	}
	if err == io.EOF {
		fmt.Printf("\n[/response]\n")
	}
	return n, err
}

type readButCopy struct {
	src io.Reader
	dst io.Writer
}

func (r *readButCopy) Read(p []byte) (int, error) {
	n, err := r.src.Read(p)
	if n > 0 {
		r.dst.Write(p[:n])
	}
	return n, err
}
<commit_msg>Allow storing token cache in environment variables as well as a flat file<commit_after>package gs

import (
	"bytes"
	"encoding/gob"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"code.google.com/p/goauth2/oauth"
)

var g_config = &oauth.Config{
	ClientId:     "",                                          // Set by --clientid or --clientid_file
	ClientSecret: "",                                          // Set by --secret or --secret_file
	Scope:        "https://www.googleapis.com/auth/moderator", // filled in per-API
	AuthURL:      "https://accounts.google.com/o/oauth2/auth",
	TokenURL:     "https://accounts.google.com/o/oauth2/token",
	AccessType:   "offline",
}

// Flags
var (
	clientId     = flag.String("clientid", "", "OAuth Client ID.  If non-empty, overrides --clientid_file")
	clientIdFile = flag.String("clientid_file", "clientid.dat",
		"Name of a file containing just the project's OAuth Client ID from https://code.google.com/apis/console/")
	secret     = flag.String("secret", "", "OAuth Client Secret.  If non-empty, overrides --secret_file")
	secretFile = flag.String("secret_file", "clientsecret.dat",
		"Name of a file containing just the project's OAuth Client Secret from https://code.google.com/apis/console/")
	cacheToken = flag.Bool("cachetoken", true, "cache the OAuth token")
	debug      = flag.Bool("debug", false, "show HTTP traffic")
)

func newOauthClient(scope string) *http.Client {
	g_config.Scope = scope
	g_config.ClientId = valueOrFileContents(*clientId, *clientIdFile)
	g_config.ClientSecret = valueOrFileContents(*secret, *secretFile)

	return getOAuthClient(g_config)
}

func osUserCacheDir() string {
	switch runtime.GOOS {
	case "darwin":
		return filepath.Join(os.Getenv("HOME"), "Library", "Caches")
	case "linux", "freebsd":
		return filepath.Join(os.Getenv("HOME"), ".cache")
	}
	log.Printf("TODO: osUserCacheDir on GOOS %q", runtime.GOOS)
	return "."
}

func tokenCacheFile(g_config *oauth.Config) string {
	return filepath.Join(osUserCacheDir(), url.QueryEscape(
		fmt.Sprintf("go-api-demo-%s-%s-%s", g_config.ClientId, g_config.ClientSecret, g_config.Scope)))
}

func tokenFromFile(file string) (*oauth.Token, error) {
	if !*cacheToken {
		return nil, errors.New("--cachetoken is false")
	}
	f, err := os.Open(file)
	if err != nil {
		return nil, err
	}
	t := new(oauth.Token)
	err = gob.NewDecoder(f).Decode(t)
	return t, err
}

func saveToken(file string, token *oauth.Token) {
	f, err := os.Create(file)
	if err != nil {
		log.Printf("Warning: failed to cache oauth token: %v", err)
		return
	}
	defer f.Close()
	gob.NewEncoder(f).Encode(token)
}

func condDebugTransport(rt http.RoundTripper) http.RoundTripper {
	if *debug {
		return &logTransport{rt}
	}
	return rt
}

func getOAuthClient(g_config *oauth.Config) *http.Client {
	cacheFile := tokenCacheFile(g_config)

	access := os.Getenv("GS_ACCESS_TOKEN")
	refresh := os.Getenv("GS_REFRESH_TOKEN")

	var token *oauth.Token
	var err error
	if access == "" && refresh == "" {
		token, err = tokenFromFile(cacheFile)
	} else {
		token = &oauth.Token{
			AccessToken:  access,
			RefreshToken: refresh,
		}
	}

	if err != nil {
		token = tokenFromWeb(g_config)
		saveToken(cacheFile, token)
	} else {
		// log.Printf("Using cached token %#v from %q", token, cacheFile)
	}

	t := &oauth.Transport{
		Token:     token,
		Config:    g_config,
		Transport: condDebugTransport(http.DefaultTransport),
	}
	return t.Client()
}

func tokenFromWeb(g_config *oauth.Config) *oauth.Token {
	ch := make(chan string)
	randState := fmt.Sprintf("st%d", time.Now())
	ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		if req.URL.Path == "/favicon.ico" {
			http.Error(rw, "", 404)
			return
		}
		if req.FormValue("state") != randState {
			log.Printf("State doesn't match: req = %#v", req)
			http.Error(rw, "", 500)
			return
		}
		if code := req.FormValue("code"); code != "" {
			fmt.Fprintf(rw, "
Success
Authorized.")
			rw.(http.Flusher).Flush()
			ch <- code
			return
		}
		log.Printf("no code")
		http.Error(rw, "", 500)
	}))
	defer ts.Close()

	g_config.RedirectURL = ts.URL
	fmt.Println(ts.URL)
	authUrl := g_config.AuthCodeURL(randState)
	go openUrl(authUrl)
	log.Printf("Authorize this app at: %s", authUrl)
	code := <-ch
	log.Printf("Got code: %s", code)

	t := &oauth.Transport{
		Config:    g_config,
		Transport: condDebugTransport(http.DefaultTransport),
	}
	_, err := t.Exchange(code)
	if err != nil {
		log.Fatalf("Token exchange error: %v", err)
	}
	return t.Token
}

func openUrl(url string) {
	try := []string{"xdg-open", "google-chrome", "open"}
	for _, bin := range try {
		err := exec.Command(bin, url).Run()
		if err == nil {
			return
		}
	}
	log.Printf("Error opening URL in browser.")
}

func valueOrFileContents(value string, filename string) string {
	if value != "" {
		return value
	}
	slurp, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatalf("Error reading %q: %v", filename, err)
	}
	return strings.TrimSpace(string(slurp))
}

type logTransport struct {
	rt http.RoundTripper
}

func (t *logTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	var buf bytes.Buffer

	os.Stdout.Write([]byte("\n[request]\n"))
	if req.Body != nil {
		req.Body = ioutil.NopCloser(&readButCopy{req.Body, &buf})
	}
	req.Write(os.Stdout)
	if req.Body != nil {
		req.Body = ioutil.NopCloser(&buf)
	}
	os.Stdout.Write([]byte("\n[/request]\n"))

	res, err := t.rt.RoundTrip(req)

	fmt.Printf("[response]\n")
	if err != nil {
		fmt.Printf("ERROR: %v", err)
	} else {
		body := res.Body
		res.Body = nil
		res.Write(os.Stdout)
		if body != nil {
			res.Body = ioutil.NopCloser(&echoAsRead{body})
		}
	}

	return res, err
}

type echoAsRead struct {
	src io.Reader
}

func (r *echoAsRead) Read(p []byte) (int, error) {
	n, err := r.src.Read(p)
	if n > 0 {
		os.Stdout.Write(p[:n])
	}
	if err == io.EOF {
		fmt.Printf("\n[/response]\n")
	}
	return n, err
}

type readButCopy struct {
	src io.Reader
	dst io.Writer
}

func (r *readButCopy) Read(p []byte) (int, error) {
	n, err := r.src.Read(p)
	if n > 0 {
		r.dst.Write(p[:n])
	}
	return n, err
}
<|endoftext|>


<commit_before>// A commandline tool for generating table-driven Go tests.
//
// This tool can generate tests for specific Go source files or an entire
// directory. By default, it prints its output to stdout.
//
// Usage:
//
//   $ gotests [options] PATH ...
//
// Available options:
//
//   -all         generate tests for all functions and methods
//
//   -excl        regexp. generate tests for functions and methods that don't
//                match. Takes precedence over -only, -exported, and -all
//
//   -exported    generate tests for exported functions and methods. Takes
//                precedence over -only and -all
//
//   -i           print test inputs in error messages
//
//   -only        regexp. generate tests for functions and methods that match only.
//                Takes precedence over -all
//
//   -nosubtests  disable subtest generation when >= Go 1.7
//
//   -w           write output to (test) files instead of stdout
package main

import (
	"flag"
	"os"

	"github.com/cweill/gotests/gotests/process"
)

var (
	onlyFuncs     = flag.String("only", "", `regexp. generate tests for functions and methods that match only. Takes precedence over -all`)
	exclFuncs     = flag.String("excl", "", `regexp. generate tests for functions and methods that don't match. Takes precedence over -only, -exported, and -all`)
	exportedFuncs = flag.Bool("exported", false, `generate tests for exported functions and methods. Takes precedence over -only and -all`)
	allFuncs      = flag.Bool("all", false, "generate tests for all functions and methods")
	printInputs   = flag.Bool("i", false, "print test inputs in error messages")
	writeOutput   = flag.Bool("w", false, "write output to (test) files instead of stdout")
)

// nosubtests is always set to default value of true when Go < 1.7.
// When >= Go 1.7 the default value is changed to false by the
// flag.BoolVar but can be overriden by setting nosubtests to true
var nosubtests = true

func main() {
	flag.Parse()
	args := flag.Args()

	process.Run(os.Stdout, args, &process.Options{
		OnlyFuncs:     *onlyFuncs,
		ExclFuncs:     *exclFuncs,
		ExportedFuncs: *exportedFuncs,
		AllFuncs:      *allFuncs,
		PrintInputs:   *printInputs,
		Subtests:      !nosubtests,
		WriteOutput:   *writeOutput,
	})
}
<commit_msg>fix typo in comment<commit_after>// A commandline tool for generating table-driven Go tests.
//
// This tool can generate tests for specific Go source files or an entire
// directory. By default, it prints its output to stdout.
//
// Usage:
//
//   $ gotests [options] PATH ...
//
// Available options:
//
//   -all         generate tests for all functions and methods
//
//   -excl        regexp. generate tests for functions and methods that don't
//                match. Takes precedence over -only, -exported, and -all
//
//   -exported    generate tests for exported functions and methods. Takes
//                precedence over -only and -all
//
//   -i           print test inputs in error messages
//
//   -only        regexp. generate tests for functions and methods that match only.
//                Takes precedence over -all
//
//   -nosubtests  disable subtest generation when >= Go 1.7
//
//   -w           write output to (test) files instead of stdout
package main

import (
	"flag"
	"os"

	"github.com/cweill/gotests/gotests/process"
)

var (
	onlyFuncs     = flag.String("only", "", `regexp. generate tests for functions and methods that match only. Takes precedence over -all`)
	exclFuncs     = flag.String("excl", "", `regexp. generate tests for functions and methods that don't match. Takes precedence over -only, -exported, and -all`)
	exportedFuncs = flag.Bool("exported", false, `generate tests for exported functions and methods. Takes precedence over -only and -all`)
	allFuncs      = flag.Bool("all", false, "generate tests for all functions and methods")
	printInputs   = flag.Bool("i", false, "print test inputs in error messages")
	writeOutput   = flag.Bool("w", false, "write output to (test) files instead of stdout")
)

// nosubtests is always set to default value of true when Go < 1.7.
// When >= Go 1.7 the default value is changed to false by the
// flag.BoolVar but can be overridden by setting nosubtests to true
var nosubtests = true

func main() {
	flag.Parse()
	args := flag.Args()

	process.Run(os.Stdout, args, &process.Options{
		OnlyFuncs:     *onlyFuncs,
		ExclFuncs:     *exclFuncs,
		ExportedFuncs: *exportedFuncs,
		AllFuncs:      *allFuncs,
		PrintInputs:   *printInputs,
		Subtests:      !nosubtests,
		WriteOutput:   *writeOutput,
	})
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"time"

	"github.com/adjust/rmq/v2"
)

const (
	unackedLimit = 1000
	numConsumers = 10
	batchSize    = 1000
)

func main() {
	errors := make(chan error, 10)
	go func() {
		for err := range errors {
			switch err := err.(type) {
			case *rmq.ConsumeError:
				log.Print("consume error: ", err)
			case *rmq.HeartbeatError:
				if err.Count == rmq.HeartbeatErrorLimit {
					log.Print("heartbeat error (limit): ", err)
				} else {
					log.Print("heartbeat error: ", err)
				}
			default:
				log.Print("other error: ", err)
			}
		}
	}()

	connection, err := rmq.OpenConnection("consumer", "tcp", "localhost:6379", 2, errors)
	if err != nil {
		panic(err)
	}

	queue, err := connection.OpenQueue("things")
	if err != nil {
		panic(err)
	}

	if err := queue.StartConsuming(unackedLimit, 500*time.Millisecond, errors); err != nil {
		panic(err)
	}

	for i := 0; i < numConsumers; i++ {
		name := fmt.Sprintf("consumer %d", i)
		if _, err := queue.AddConsumer(name, NewConsumer(i)); err != nil {
			panic(err)
		}
	}
	select {}
}

type Consumer struct {
	name   string
	count  int
	before time.Time
}

func NewConsumer(tag int) *Consumer {
	return &Consumer{
		name:   fmt.Sprintf("consumer%d", tag),
		count:  0,
		before: time.Now(),
	}
}

func (consumer *Consumer) Consume(delivery rmq.Delivery) {
	consumer.count++
	if consumer.count%batchSize == 0 {
		duration := time.Now().Sub(consumer.before)
		consumer.before = time.Now()
		perSecond := time.Second / (duration / batchSize)
		log.Printf("%s consumed %d %s %d", consumer.name, consumer.count, delivery.Payload(), perSecond)
	}
	time.Sleep(time.Millisecond)
	if consumer.count%batchSize == 0 {
		if err := delivery.Reject(); err != nil {
			log.Printf("failed to reject: %s", err)
		}
	} else {
		if err := delivery.Ack(); err != nil {
			log.Printf("failed to ack: %s", err)
		}
	}
}
<commit_msg>Update example/consumer to use AckWithRetry()<commit_after>package main

import (
	"context"
	"fmt"
	"log"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/adjust/rmq/v2"
)

const (
	unackedLimit    = 1000
	numConsumers    = 5
	batchSize       = 1000
	consumeDuration = time.Millisecond
	shouldLog       = false
)

func main() {
	errors := make(chan error, 10)
	go func() {
		for err := range errors {
			switch err := err.(type) {
			case *rmq.ConsumeError:
				log.Print("consume error: ", err)
			case *rmq.HeartbeatError:
				if err.Count == rmq.HeartbeatErrorLimit {
					log.Print("heartbeat error (limit): ", err)
				} else {
					log.Print("heartbeat error: ", err)
				}
			case *rmq.DeliveryError:
				log.Print("delivery error: ", err.Delivery, err)
			default:
				log.Print("other error: ", err)
			}
		}
	}()

	connection, err := rmq.OpenConnection("consumer", "tcp", "localhost:6379", 2, errors)
	if err != nil {
		panic(err)
	}

	queue, err := connection.OpenQueue("things")
	if err != nil {
		panic(err)
	}

	if err := queue.StartConsuming(unackedLimit, 500*time.Millisecond, errors); err != nil {
		panic(err)
	}

	ctx, cancel := context.WithCancel(context.Background())

	for i := 0; i < numConsumers; i++ {
		name := fmt.Sprintf("consumer %d", i)
		if _, err := queue.AddConsumer(name, NewConsumer(ctx, errors, i)); err != nil {
			panic(err)
		}
	}

	signals := make(chan os.Signal, 1)
	signal.Notify(signals, syscall.SIGINT)
	defer signal.Stop(signals)

	<-signals // wait for signal
	go func() {
		<-signals // hard exit on second signal (in case shutdown gets stuck)
		os.Exit(1)
	}()

	c := connection.StopAllConsuming()
	// make sure AckWithRetry() and similar calls return with error so that
	// they can be handled and the active Conume() calls can finish
	cancel()
	<-c // wait for all Conume() calls to finish
}

type Consumer struct {
	ctx    context.Context
	errors chan<- error
	name   string
	count  int
	before time.Time
}

func NewConsumer(ctx context.Context, errors chan<- error, tag int) *Consumer {
	return &Consumer{
		ctx:    ctx,
		errors: errors,
		name:   fmt.Sprintf("consumer%d", tag),
		count:  0,
		before: time.Now(),
	}
}

func (consumer *Consumer) Consume(delivery rmq.Delivery) {
	payload := delivery.Payload()
	debugf("start consume %s", payload)
	consumer.count++
	if consumer.count%batchSize == 0 {
		duration := time.Now().Sub(consumer.before)
		consumer.before = time.Now()
		perSecond := time.Second / (duration / batchSize)
		log.Printf("%s consumed %d %s %d", consumer.name, consumer.count, payload, perSecond)
	}
	time.Sleep(consumeDuration)

	if consumer.count%batchSize > 0 {
		if err := delivery.AckWithRetry(consumer.ctx, consumer.errors); err != nil {
			debugf("failed to ack %s: %s", payload, err)
		} else {
			debugf("acked %s", payload)
		}
	} else { // reject one per batch
		if err := delivery.RejectWithRetry(consumer.ctx, consumer.errors); err != nil {
			debugf("failed to reject %s: %s", payload, err)
		} else {
			debugf("rejected %s", payload)
		}
	}
}

func debugf(format string, args ...interface{}) {
	if shouldLog {
		log.Printf(format, args...)
	}
}
<|endoftext|>


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

package middleware

import (
	"net/http"
	fpath "path"
	"regexp"
	"strings"

	"github.com/go-openapi/analysis"
	"github.com/go-openapi/errors"
	"github.com/go-openapi/loads"
	"github.com/go-openapi/runtime"
	"github.com/go-openapi/runtime/middleware/denco"
	"github.com/go-openapi/spec"
	"github.com/go-openapi/strfmt"
)

// RouteParam is a object to capture route params in a framework agnostic way.
// implementations of the muxer should use these route params to communicate with the
// swagger framework
type RouteParam struct {
	Name  string
	Value string
}

// RouteParams the collection of route params
type RouteParams []RouteParam

// Get gets the value for the route param for the specified key
func (r RouteParams) Get(name string) string {
	vv, _, _ := r.GetOK(name)
	if len(vv) > 0 {
		return vv[len(vv)-1]
	}
	return ""
}

// GetOK gets the value but also returns booleans to indicate if a key or value
// is present. This aids in validation and satisfies an interface in use there
//
// The returned values are: data, has key, has value
func (r RouteParams) GetOK(name string) ([]string, bool, bool) {
	for _, p := range r {
		if p.Name == name {
			return []string{p.Value}, true, p.Value != ""
		}
	}
	return nil, false, false
}

// NewRouter creates a new context aware router middleware
func NewRouter(ctx *Context, next http.Handler) http.Handler {
	if ctx.router == nil {
		ctx.router = DefaultRouter(ctx.spec, ctx.api)
	}

	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		if _, rCtx, ok := ctx.RouteInfo(r); ok {
			next.ServeHTTP(rw, rCtx)
			return
		}

		// Not found, check if it exists in the other methods first
		if others := ctx.AllowedMethods(r); len(others) > 0 {
			ctx.Respond(rw, r, ctx.analyzer.RequiredProduces(), nil, errors.MethodNotAllowed(r.Method, others))
			return
		}

		ctx.Respond(rw, r, ctx.analyzer.RequiredProduces(), nil, errors.NotFound("path %s was not found", r.URL.EscapedPath()))
	})
}

// RoutableAPI represents an interface for things that can serve
// as a provider of implementations for the swagger router
type RoutableAPI interface {
	HandlerFor(string, string) (http.Handler, bool)
	ServeErrorFor(string) func(http.ResponseWriter, *http.Request, error)
	ConsumersFor([]string) map[string]runtime.Consumer
	ProducersFor([]string) map[string]runtime.Producer
	AuthenticatorsFor(map[string]spec.SecurityScheme) map[string]runtime.Authenticator
	Formats() strfmt.Registry
	DefaultProduces() string
	DefaultConsumes() string
}

// Router represents a swagger aware router
type Router interface {
	Lookup(method, path string) (*MatchedRoute, bool)
	OtherMethods(method, path string) []string
}

type defaultRouteBuilder struct {
	spec     *loads.Document
	analyzer *analysis.Spec
	api      RoutableAPI
	records  map[string][]denco.Record
}

type defaultRouter struct {
	spec    *loads.Document
	api     RoutableAPI
	routers map[string]*denco.Router
}

func newDefaultRouteBuilder(spec *loads.Document, api RoutableAPI) *defaultRouteBuilder {
	return &defaultRouteBuilder{
		spec:     spec,
		analyzer: analysis.New(spec.Spec()),
		api:      api,
		records:  make(map[string][]denco.Record),
	}
}

// DefaultRouter creates a default implemenation of the router
func DefaultRouter(spec *loads.Document, api RoutableAPI) Router {
	builder := newDefaultRouteBuilder(spec, api)
	if spec != nil {
		for method, paths := range builder.analyzer.Operations() {
			for path, operation := range paths {
				fp := fpath.Join(spec.BasePath(), path)
				debugLog("adding route %s %s %q", method, fp, operation.ID)
				builder.AddRoute(method, fp, operation)
			}
		}
	}
	return builder.Build()
}

type routeEntry struct {
	PathPattern    string
	BasePath       string
	Operation      *spec.Operation
	Consumes       []string
	Consumers      map[string]runtime.Consumer
	Produces       []string
	Producers      map[string]runtime.Producer
	Parameters     map[string]spec.Parameter
	Handler        http.Handler
	Formats        strfmt.Registry
	Binder         *untypedRequestBinder
	Authenticators map[string]runtime.Authenticator
	Scopes         map[string][]string
}

// MatchedRoute represents the route that was matched in this request
type MatchedRoute struct {
	routeEntry
	Params   RouteParams
	Consumer runtime.Consumer
	Producer runtime.Producer
}

func (d *defaultRouter) Lookup(method, path string) (*MatchedRoute, bool) {
	mth := strings.ToUpper(method)
	debugLog("looking up route for %s %s", method, path)
	if Debug {
		if len(d.routers) == 0 {
			debugLog("there are no known routers")
		}
		for meth := range d.routers {
			debugLog("got a router for %s", meth)
		}
	}
	if router, ok := d.routers[mth]; ok {
		if m, rp, ok := router.Lookup(fpath.Clean(path)); ok && m != nil {
			if entry, ok := m.(*routeEntry); ok {
				debugLog("found a route for %s %s with %d parameters", method, path, len(entry.Parameters))
				var params RouteParams
				for _, p := range rp {
					v, err := pathUnescape(p.Value)
					if err != nil {
						debugLog("failed to escape %q: %v", p.Value, err)
						v = p.Value
					}
					params = append(params, RouteParam{Name: p.Name, Value: v})
				}
				return &MatchedRoute{routeEntry: *entry, Params: params}, true
			}
		} else {
			debugLog("couldn't find a route by path for %s %s", method, path)
		}
	} else {
		debugLog("couldn't find a route by method for %s %s", method, path)
	}
	return nil, false
}

func (d *defaultRouter) OtherMethods(method, path string) []string {
	mn := strings.ToUpper(method)
	var methods []string
	for k, v := range d.routers {
		if k != mn {
			if _, _, ok := v.Lookup(fpath.Clean(path)); ok {
				methods = append(methods, k)
				continue
			}
		}
	}
	return methods
}

var pathConverter = regexp.MustCompile(`{(.+?)}`)

func (d *defaultRouteBuilder) AddRoute(method, path string, operation *spec.Operation) {
	mn := strings.ToUpper(method)

	bp := fpath.Clean(d.spec.BasePath())
	if len(bp) > 0 && bp[len(bp)-1] == '/' {
		bp = bp[:len(bp)-1]
	}

	debugLog("operation: %#v", *operation)
	if handler, ok := d.api.HandlerFor(method, strings.TrimPrefix(path, bp)); ok {
		consumes := d.analyzer.ConsumesFor(operation)
		produces := d.analyzer.ProducesFor(operation)
		parameters := d.analyzer.ParamsFor(method, strings.TrimPrefix(path, bp))
		definitions := d.analyzer.SecurityDefinitionsFor(operation)
		requirements := d.analyzer.SecurityRequirementsFor(operation)
		scopes := make(map[string][]string, len(requirements))
		for _, v := range requirements {
			scopes[v.Name] = v.Scopes
		}

		record := denco.NewRecord(pathConverter.ReplaceAllString(path, ":$1"), &routeEntry{
			BasePath:       bp,
			PathPattern:    path,
			Operation:      operation,
			Handler:        handler,
			Consumes:       consumes,
			Produces:       produces,
			Consumers:      d.api.ConsumersFor(normalizeOffers(consumes)),
			Producers:      d.api.ProducersFor(normalizeOffers(produces)),
			Parameters:     parameters,
			Formats:        d.api.Formats(),
			Binder:         newUntypedRequestBinder(parameters, d.spec.Spec(), d.api.Formats()),
			Authenticators: d.api.AuthenticatorsFor(definitions),
			Scopes:         scopes,
		})
		d.records[mn] = append(d.records[mn], record)
	}
}

func (d *defaultRouteBuilder) Build() *defaultRouter {
	routers := make(map[string]*denco.Router)
	for method, records := range d.records {
		router := denco.New()
		router.Build(records)
		routers[method] = router
	}
	return &defaultRouter{
		spec:    d.spec,
		routers: routers,
	}
}
<commit_msg>Bugfix: middleware/router removes unused and unchecked errors<commit_after>// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package middleware

import (
	"net/http"
	fpath "path"
	"regexp"
	"strings"

	"github.com/go-openapi/analysis"
	"github.com/go-openapi/errors"
	"github.com/go-openapi/loads"
	"github.com/go-openapi/runtime"
	"github.com/go-openapi/runtime/middleware/denco"
	"github.com/go-openapi/spec"
	"github.com/go-openapi/strfmt"
)

// RouteParam is a object to capture route params in a framework agnostic way.
// implementations of the muxer should use these route params to communicate with the
// swagger framework
type RouteParam struct {
	Name  string
	Value string
}

// RouteParams the collection of route params
type RouteParams []RouteParam

// Get gets the value for the route param for the specified key
func (r RouteParams) Get(name string) string {
	vv, _, _ := r.GetOK(name)
	if len(vv) > 0 {
		return vv[len(vv)-1]
	}
	return ""
}

// GetOK gets the value but also returns booleans to indicate if a key or value
// is present. This aids in validation and satisfies an interface in use there
//
// The returned values are: data, has key, has value
func (r RouteParams) GetOK(name string) ([]string, bool, bool) {
	for _, p := range r {
		if p.Name == name {
			return []string{p.Value}, true, p.Value != ""
		}
	}
	return nil, false, false
}

// NewRouter creates a new context aware router middleware
func NewRouter(ctx *Context, next http.Handler) http.Handler {
	if ctx.router == nil {
		ctx.router = DefaultRouter(ctx.spec, ctx.api)
	}

	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		if _, rCtx, ok := ctx.RouteInfo(r); ok {
			next.ServeHTTP(rw, rCtx)
			return
		}

		// Not found, check if it exists in the other methods first
		if others := ctx.AllowedMethods(r); len(others) > 0 {
			ctx.Respond(rw, r, ctx.analyzer.RequiredProduces(), nil, errors.MethodNotAllowed(r.Method, others))
			return
		}

		ctx.Respond(rw, r, ctx.analyzer.RequiredProduces(), nil, errors.NotFound("path %s was not found", r.URL.EscapedPath()))
	})
}

// RoutableAPI represents an interface for things that can serve
// as a provider of implementations for the swagger router
type RoutableAPI interface {
	HandlerFor(string, string) (http.Handler, bool)
	ServeErrorFor(string) func(http.ResponseWriter, *http.Request, error)
	ConsumersFor([]string) map[string]runtime.Consumer
	ProducersFor([]string) map[string]runtime.Producer
	AuthenticatorsFor(map[string]spec.SecurityScheme) map[string]runtime.Authenticator
	Formats() strfmt.Registry
	DefaultProduces() string
	DefaultConsumes() string
}

// Router represents a swagger aware router
type Router interface {
	Lookup(method, path string) (*MatchedRoute, bool)
	OtherMethods(method, path string) []string
}

type defaultRouteBuilder struct {
	spec     *loads.Document
	analyzer *analysis.Spec
	api      RoutableAPI
	records  map[string][]denco.Record
}

type defaultRouter struct {
	spec    *loads.Document
	routers map[string]*denco.Router
}

func newDefaultRouteBuilder(spec *loads.Document, api RoutableAPI) *defaultRouteBuilder {
	return &defaultRouteBuilder{
		spec:     spec,
		analyzer: analysis.New(spec.Spec()),
		api:      api,
		records:  make(map[string][]denco.Record),
	}
}

// DefaultRouter creates a default implemenation of the router
func DefaultRouter(spec *loads.Document, api RoutableAPI) Router {
	builder := newDefaultRouteBuilder(spec, api)
	if spec != nil {
		for method, paths := range builder.analyzer.Operations() {
			for path, operation := range paths {
				fp := fpath.Join(spec.BasePath(), path)
				debugLog("adding route %s %s %q", method, fp, operation.ID)
				builder.AddRoute(method, fp, operation)
			}
		}
	}
	return builder.Build()
}

type routeEntry struct {
	PathPattern    string
	BasePath       string
	Operation      *spec.Operation
	Consumes       []string
	Consumers      map[string]runtime.Consumer
	Produces       []string
	Producers      map[string]runtime.Producer
	Parameters     map[string]spec.Parameter
	Handler        http.Handler
	Formats        strfmt.Registry
	Binder         *untypedRequestBinder
	Authenticators map[string]runtime.Authenticator
	Scopes         map[string][]string
}

// MatchedRoute represents the route that was matched in this request
type MatchedRoute struct {
	routeEntry
	Params   RouteParams
	Consumer runtime.Consumer
	Producer runtime.Producer
}

func (d *defaultRouter) Lookup(method, path string) (*MatchedRoute, bool) {
	mth := strings.ToUpper(method)
	debugLog("looking up route for %s %s", method, path)
	if Debug {
		if len(d.routers) == 0 {
			debugLog("there are no known routers")
		}
		for meth := range d.routers {
			debugLog("got a router for %s", meth)
		}
	}
	if router, ok := d.routers[mth]; ok {
		if m, rp, ok := router.Lookup(fpath.Clean(path)); ok && m != nil {
			if entry, ok := m.(*routeEntry); ok {
				debugLog("found a route for %s %s with %d parameters", method, path, len(entry.Parameters))
				var params RouteParams
				for _, p := range rp {
					v, err := pathUnescape(p.Value)
					if err != nil {
						debugLog("failed to escape %q: %v", p.Value, err)
						v = p.Value
					}
					params = append(params, RouteParam{Name: p.Name, Value: v})
				}
				return &MatchedRoute{routeEntry: *entry, Params: params}, true
			}
		} else {
			debugLog("couldn't find a route by path for %s %s", method, path)
		}
	} else {
		debugLog("couldn't find a route by method for %s %s", method, path)
	}
	return nil, false
}

func (d *defaultRouter) OtherMethods(method, path string) []string {
	mn := strings.ToUpper(method)
	var methods []string
	for k, v := range d.routers {
		if k != mn {
			if _, _, ok := v.Lookup(fpath.Clean(path)); ok {
				methods = append(methods, k)
				continue
			}
		}
	}
	return methods
}

var pathConverter = regexp.MustCompile(`{(.+?)}`)

func (d *defaultRouteBuilder) AddRoute(method, path string, operation *spec.Operation) {
	mn := strings.ToUpper(method)

	bp := fpath.Clean(d.spec.BasePath())
	if len(bp) > 0 && bp[len(bp)-1] == '/' {
		bp = bp[:len(bp)-1]
	}

	debugLog("operation: %#v", *operation)
	if handler, ok := d.api.HandlerFor(method, strings.TrimPrefix(path, bp)); ok {
		consumes := d.analyzer.ConsumesFor(operation)
		produces := d.analyzer.ProducesFor(operation)
		parameters := d.analyzer.ParamsFor(method, strings.TrimPrefix(path, bp))
		definitions := d.analyzer.SecurityDefinitionsFor(operation)
		requirements := d.analyzer.SecurityRequirementsFor(operation)
		scopes := make(map[string][]string, len(requirements))
		for _, v := range requirements {
			scopes[v.Name] = v.Scopes
		}

		record := denco.NewRecord(pathConverter.ReplaceAllString(path, ":$1"), &routeEntry{
			BasePath:       bp,
			PathPattern:    path,
			Operation:      operation,
			Handler:        handler,
			Consumes:       consumes,
			Produces:       produces,
			Consumers:      d.api.ConsumersFor(normalizeOffers(consumes)),
			Producers:      d.api.ProducersFor(normalizeOffers(produces)),
			Parameters:     parameters,
			Formats:        d.api.Formats(),
			Binder:         newUntypedRequestBinder(parameters, d.spec.Spec(), d.api.Formats()),
			Authenticators: d.api.AuthenticatorsFor(definitions),
			Scopes:         scopes,
		})
		d.records[mn] = append(d.records[mn], record)
	}
}

func (d *defaultRouteBuilder) Build() *defaultRouter {
	routers := make(map[string]*denco.Router)
	for method, records := range d.records {
		router := denco.New()
		_ = router.Build(records)
		routers[method] = router
	}
	return &defaultRouter{
		spec:    d.spec,
		routers: routers,
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 Marc-Antoine Ruel. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

// This file defines all the interfaces to be used by the wi editor and to be
// accessable by plugins.

package wi

import (
	"fmt"
)

// UI

// DockingType defines the relative position of a Window relative to its parent.
type DockingType int

const (
	// The Window uses all the available space.
	DockingFill DockingType = iota
	// The Window is not constrained by the parent window size and location.
	DockingFloating
	DockingLeft
	DockingRight
	DockingTop
	DockingBottom
	DockingUnknown
)

func (d DockingType) String() string {
	switch d {
	case DockingFill:
		return "DockingFill"
	case DockingFloating:
		return "DockingFloating"
	case DockingLeft:
		return "DockingLeft"
	case DockingRight:
		return "DockingRight"
	case DockingTop:
		return "DockingTop"
	case DockingBottom:
		return "DockingBottom"
	default:
		return "Unknown DockingType"
	}
}

func StringToDockingType(s string) DockingType {
	switch s {
	case "fill":
		return DockingFill
	case "floating":
		return DockingFloating
	case "left":
		return DockingLeft
	case "right":
		return DockingRight
	case "top":
		return DockingTop
	case "bottom":
		return DockingBottom
	default:
		return DockingUnknown
	}
}

// BorderType defines the type of border for a Window.
type BorderType int

const (
	// Width is 0.
	BorderNone BorderType = iota
	// Width is 1.
	BorderSingle
	// Despite its name, width is 1, only the glyph is different.
	BorderDouble
)

func (b BorderType) String() string {
	switch b {
	case BorderNone:
		return "BorderNone"
	case BorderSingle:
		return "BorderSingle"
	case BorderDouble:
		return "BorderDouble"
	default:
		return "Unknown BorderType"
	}
}

// CommandCategory is used to put commands into sections for help purposes.
type CommandCategory int

const (
	UnknownCategory CommandCategory = iota
	// Commands relating to manipuling windows and UI in general.
	WindowCategory
	// Commands relating to manipulating commands, aliases, keybindings.
	CommandsCategory
	// Commands relating to the editor lifetime.
	EditorCategory
	// Commands relating to debugging the app itself or plugins.
	DebugCategory

	// TODO(maruel): Add other categories.
)

func (c CommandCategory) String() string {
	switch c {
	case UnknownCategory:
		return "UnknownCategory"
	case WindowCategory:
		return "WindowCategory"
	case CommandsCategory:
		return "CommandsCategory"
	case DebugCategory:
		return "DebugCategory"
	default:
		return "Unknown CommandCategory"
	}
}

// Switches keyboard mapping based on the input mode. These modes are hardcode;
// adding a new mode would require rebuilding the editor (2 seconds, really).
type KeyboardMode int

const (
	_ KeyboardMode = iota
	// CommandMode is the mode where typing letters results in commands, not
	// content editing.
	CommandMode
	// EditMode is the mode where typing letters results in content, not commands.
	EditMode
	// AllMode is to bind keys independent of the current mode. It is useful for
	// function keys, Ctrl-<letter>, arrow keys, etc.
	AllMode
)

// CommandDispatcher owns the command queue. Use this interface to enqueue
// commands for execution.
type CommandDispatcher interface {
	// PostCommands appends several Command calls at the end of the queue. Using
	// this function guarantees that all the commands will be executed in order
	// without commands interfering.
	PostCommands(cmds [][]string)
}

// ViewFactory returns a new View.
type ViewFactory func(args ...string) View

type CommandDispatcherFull interface {
	CommandDispatcher

	// ExecuteCommand executes a command now. This is only meant to run a command
	// reentrantly; e.g. running a command triggers another one. This usually
	// happens for key binding, command aliases, when a command triggers an error.
	ExecuteCommand(w Window, cmdName string, args ...string)

	// ActiveWindow returns the current active Window.
	ActiveWindow() Window

	// RegisterViewFactory makes a nwe view available by name.
	RegisterViewFactory(name string, viewFactory ViewFactory) bool

	CurrentLanguage() LanguageMode
}

// Editor is the output device and the main process context. It shows the root
// window which covers the whole screen estate.
type Editor interface {
	CommandDispatcherFull

	KeyboardMode() KeyboardMode

	// Version returns the version number of this build of wi.
	Version() string
}

// Window is a View container. It defines the position, Z-ordering via
// hierarchy and decoration. It can have multiple child windows. The child
// windows are not bounded by the parent window if DockingFloating is used. The
// Window itself doesn't interact with the user, since it only has a non-client
// area (the border). All the client area is covered by the View.
//
// Split view is not supported. A 4-way merge setup can be created with the
// following Window setup as 4 child Window of the root Window:
//
//    +-----------+-----------+------------+
//    |  Remote   |Merge Base*|   Local    |
//    |DockingLeft|DockingFill|DockingRight|
//    |           |           |            |
//    +-----------+-----------+------------+
//    |              Result                |
//    |           DockingBottom            |
//    |                                    |
//    +------------------------------------+
//
// * The Merge Base View can be either:
//   - The root Window's View that is constained.
//   - A child Window set as DockingFill. In this case, the root Window View is
//     not visible.
//
// The end result is that this use case doesn't require any "split" support.
// Further subdivision can be done via Window containment.
//
// The Window interface exists for synchronous query but modifications
// (creation, closing, moving) are done asynchronously via commands. A set of
// privileged commands starting with the prefix "window_" can modify Window
// instances, designating the actual Window by its .Id() method.
type Window interface {
	fmt.Stringer
	CommandDispatcher

	// Id returns the unique id for this Window. The id is guaranteed to be
	// unique through the process lifetime of the editor.
	Id() string

	// Tree returns a textual representation of the Window hierarchy. It is only
	// for debugging purpose.
	// TODO(maruel): Remove.
	Tree() string

	// Parent returns the parent Window.
	Parent() Window
	// ChildrenWindows returns a copy of the slice of children windows.
	ChildrenWindows() []Window

	// Rect returns the position based on the parent Window area, except if
	// Docking() is DockingFloating.
	Rect() Rect

	// Buffer returns the display buffer for this Window. The Window
	// double-buffers the View buffer so it could stale data if the View is slow
	// to draw itself.
	Buffer() *Buffer

	Docking() DockingType

	// SetView replaces the current View with a new one. This forces an
	// invalidation and a redraw.
	// TODO(maruel): Convert to a command.
	SetView(view View)

	View() View
}

// View is content presented in a Window. For example it can be a TextBuffer or
// a command box. View define the key binding and commands supported so it
// responds to user input.
type View interface {
	// Commands returns the commands registered for this specific view. For
	// example a text window will have commands specific to the file type
	// enabled.
	Commands() Commands

	// KeyBindings returns the key bindings registered for this specific key. For
	// example the 'command' view has different behavior on up/down arrow keys
	// than a text editor view.
	KeyBindings() KeyBindings

	// Title is View's title, which can be the current file name or any other
	// relevant detail.
	Title() string
	// IsDirty is true if the content should be saved before quitting.
	IsDirty() bool

	// IsDisabled returns false if the View can be activated to receive user
	// inputs at all.
	IsDisabled() bool

	// Buffer returns the display buffer for this Window.
	Buffer() *Buffer

	// NaturalSize returns the natural size of the content. It can be -1 for as
	// long/large as possible, 0 if indeterminate. The return value of this
	// function is not affected by SetSize().
	NaturalSize() (width, height int)
	// SetSize resets the View Buffer size.
	SetSize(x, y int)

	// OnAttach is called by the Window after it was attached.
	// TODO(maruel): Maybe split in ViewFull?
	OnAttach(w Window)
}

// Config

// Configuration manager.
type Config interface {
	GetInt(name string) int
	GetString(name string) string
	Save()
}

// Control

// CommandHandler executes the command cmd on the Window w.
type CommandHandler func(cd CommandDispatcherFull, w Window, args ...string)

// Command describes a registered command that can be triggered directly at the
// command prompt, via a keybinding or a plugin.
type Command interface {
	// Name is the name of the command.
	Name() string
	// Handle executes the command.
	Handle(cd CommandDispatcherFull, w Window, args ...string)
	// Category returns the category the command should be bucketed in, for help
	// documentation purpose.
	Category(cd CommandDispatcherFull, w Window) CommandCategory
	// ShortDesc returns a short description of the command in the language
	// requested. It defaults to English if the description was not translated in
	// this language.
	ShortDesc(cd CommandDispatcherFull, w Window) string
	// LongDesc returns a long explanation of the command in the language
	// requested. It defaults to English if the description was not translated in
	// this language.
	LongDesc(cd CommandDispatcherFull, w Window) string
}

// Commands stores the known commands. This is where plugins can add new
// commands. Each View contains its own Commands.
type Commands interface {
	// Register registers a command so it can be executed later. In practice
	// commands should normally be registered on startup. Returns false if a
	// command was already registered and was lost.
	Register(cmd Command) bool

	// Get returns a command if registered, nil otherwise.
	Get(cmdName string) Command
}

// KeyBindings stores the mapping between keyboard entry and commands. This
// includes what can be considered "macros" as much as casual things like arrow
// keys.
type KeyBindings interface {
	// Set registers a keyboard mapping. In practice keyboard mappings
	// should normally be registered on startup. Returns false if a key mapping
	// was already registered and was lost. Set cmdName to "" to remove a key
	// binding.
	Set(mode KeyboardMode, keyName string, cmdName string) bool

	// Get returns a command if registered, nil otherwise.
	Get(mode KeyboardMode, keyName string) string
}

// Utility functions.

// PostCommand appends a Command at the end of the queue.
// It is a shortcut to cd.PostCommands([][]string{[]string{cmdName, args...}})
func PostCommand(cd CommandDispatcher, cmdName string, args ...string) {
	line := make([]string, len(args)+1)
	line[0] = cmdName
	copy(line[1:], args)
	cd.PostCommands([][]string{line})
}

// GetCommand traverses the Window hierarchy tree to find a View that has
// the command cmd in its Commands mapping. If Window is nil, it starts with
// the Editor's active Window.
func GetCommand(cd CommandDispatcherFull, w Window, cmdName string) Command {
	if w == nil {
		w = cd.ActiveWindow()
	}
	for {
		cmd := w.View().Commands().Get(cmdName)
		if cmd != nil {
			return cmd
		}
		w := w.Parent()
		if w == nil {
			return nil
		}
	}
}

// GetKeyBindingCommand traverses the Editor's Window tree to find a View that
// has the key binding in its Keyboard mapping.
func GetKeyBindingCommand(e Editor, mode KeyboardMode, keyName string) string {
	active := e.ActiveWindow()
	for {
		cmdName := active.View().KeyBindings().Get(mode, keyName)
		if cmdName != "" {
			return cmdName
		}
		active := active.Parent()
		if active == nil {
			return ""
		}
	}
}

// RootWindow returns the root Window when given any Window in the tree.
func RootWindow(w Window) Window {
	for {
		if w.Parent() == nil {
			return w
		}
		w = w.Parent()
	}
}

// PositionOnScreen returns the exact position on screen of a Window.
func PositionOnScreen(w Window) Rect {
	out := w.Rect()
	if w.Docking() == DockingFloating {
		return out
	}
	for {
		w = w.Parent()
		if w == nil {
			break
		}
		// Take in account the parent Window position.
		r := w.Rect()
		out.X += r.X
		out.Y += r.Y
		if w.Docking() == DockingFloating {
			break
		}
	}
	return out
}
<commit_msg>Reduce the scope of wi.Window.<commit_after>// Copyright 2014 Marc-Antoine Ruel. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

// This file defines all the interfaces to be used by the wi editor and to be
// accessable by plugins.

package wi

import (
	"fmt"
)

// UI

// DockingType defines the relative position of a Window relative to its parent.
type DockingType int

const (
	// The Window uses all the available space.
	DockingFill DockingType = iota
	// The Window is not constrained by the parent window size and location.
	DockingFloating
	DockingLeft
	DockingRight
	DockingTop
	DockingBottom
	DockingUnknown
)

func (d DockingType) String() string {
	switch d {
	case DockingFill:
		return "DockingFill"
	case DockingFloating:
		return "DockingFloating"
	case DockingLeft:
		return "DockingLeft"
	case DockingRight:
		return "DockingRight"
	case DockingTop:
		return "DockingTop"
	case DockingBottom:
		return "DockingBottom"
	default:
		return "Unknown DockingType"
	}
}

func StringToDockingType(s string) DockingType {
	switch s {
	case "fill":
		return DockingFill
	case "floating":
		return DockingFloating
	case "left":
		return DockingLeft
	case "right":
		return DockingRight
	case "top":
		return DockingTop
	case "bottom":
		return DockingBottom
	default:
		return DockingUnknown
	}
}

// BorderType defines the type of border for a Window.
type BorderType int

const (
	// Width is 0.
	BorderNone BorderType = iota
	// Width is 1.
	BorderSingle
	// Despite its name, width is 1, only the glyph is different.
	BorderDouble
)

func (b BorderType) String() string {
	switch b {
	case BorderNone:
		return "BorderNone"
	case BorderSingle:
		return "BorderSingle"
	case BorderDouble:
		return "BorderDouble"
	default:
		return "Unknown BorderType"
	}
}

// CommandCategory is used to put commands into sections for help purposes.
type CommandCategory int

const (
	UnknownCategory CommandCategory = iota
	// Commands relating to manipuling windows and UI in general.
	WindowCategory
	// Commands relating to manipulating commands, aliases, keybindings.
	CommandsCategory
	// Commands relating to the editor lifetime.
	EditorCategory
	// Commands relating to debugging the app itself or plugins.
	DebugCategory

	// TODO(maruel): Add other categories.
)

func (c CommandCategory) String() string {
	switch c {
	case UnknownCategory:
		return "UnknownCategory"
	case WindowCategory:
		return "WindowCategory"
	case CommandsCategory:
		return "CommandsCategory"
	case DebugCategory:
		return "DebugCategory"
	default:
		return "Unknown CommandCategory"
	}
}

// Switches keyboard mapping based on the input mode. These modes are hardcode;
// adding a new mode would require rebuilding the editor (2 seconds, really).
type KeyboardMode int

const (
	_ KeyboardMode = iota
	// CommandMode is the mode where typing letters results in commands, not
	// content editing.
	CommandMode
	// EditMode is the mode where typing letters results in content, not commands.
	EditMode
	// AllMode is to bind keys independent of the current mode. It is useful for
	// function keys, Ctrl-<letter>, arrow keys, etc.
	AllMode
)

// CommandDispatcher owns the command queue. Use this interface to enqueue
// commands for execution.
type CommandDispatcher interface {
	// PostCommands appends several Command calls at the end of the queue. Using
	// this function guarantees that all the commands will be executed in order
	// without commands interfering.
	PostCommands(cmds [][]string)
}

// ViewFactory returns a new View.
type ViewFactory func(args ...string) View

type CommandDispatcherFull interface {
	CommandDispatcher

	// ExecuteCommand executes a command now. This is only meant to run a command
	// reentrantly; e.g. running a command triggers another one. This usually
	// happens for key binding, command aliases, when a command triggers an error.
	ExecuteCommand(w Window, cmdName string, args ...string)

	// ActiveWindow returns the current active Window.
	ActiveWindow() Window

	// RegisterViewFactory makes a nwe view available by name.
	RegisterViewFactory(name string, viewFactory ViewFactory) bool

	CurrentLanguage() LanguageMode
}

// Editor is the output device and the main process context. It shows the root
// window which covers the whole screen estate.
type Editor interface {
	CommandDispatcherFull

	KeyboardMode() KeyboardMode

	// Version returns the version number of this build of wi.
	Version() string
}

// Window is a View container. It defines the position, Z-ordering via
// hierarchy and decoration. It can have multiple child windows. The child
// windows are not bounded by the parent window if DockingFloating is used. The
// Window itself doesn't interact with the user, since it only has a non-client
// area (the border). All the client area is covered by the View.
//
// Split view is not supported. A 4-way merge setup can be created with the
// following Window setup as 4 child Window of the root Window:
//
//    +-----------+-----------+------------+
//    |  Remote   |Merge Base*|   Local    |
//    |DockingLeft|DockingFill|DockingRight|
//    |           |           |            |
//    +-----------+-----------+------------+
//    |              Result                |
//    |           DockingBottom            |
//    |                                    |
//    +------------------------------------+
//
// * The Merge Base View can be either:
//   - The root Window's View that is constained.
//   - A child Window set as DockingFill. In this case, the root Window View is
//     not visible.
//
// The end result is that this use case doesn't require any "split" support.
// Further subdivision can be done via Window containment.
//
// The Window interface exists for synchronous query but modifications
// (creation, closing, moving) are done asynchronously via commands. A set of
// privileged commands starting with the prefix "window_" can modify Window
// instances, designating the actual Window by its .Id() method.
type Window interface {
	fmt.Stringer
	CommandDispatcher

	// Id returns the unique id for this Window. The id is guaranteed to be
	// unique through the process lifetime of the editor.
	Id() string

	// Tree returns a textual representation of the Window hierarchy. It is only
	// for debugging purpose.
	// TODO(maruel): Remove.
	Tree() string

	// Parent returns the parent Window.
	Parent() Window
	// ChildrenWindows returns a copy of the slice of children windows.
	ChildrenWindows() []Window

	// Rect returns the position based on the parent Window area, except if
	// Docking() is DockingFloating.
	Rect() Rect

	Docking() DockingType

	View() View
}

// View is content presented in a Window. For example it can be a TextBuffer or
// a command box. View define the key binding and commands supported so it
// responds to user input.
type View interface {
	// Commands returns the commands registered for this specific view. For
	// example a text window will have commands specific to the file type
	// enabled.
	Commands() Commands

	// KeyBindings returns the key bindings registered for this specific key. For
	// example the 'command' view has different behavior on up/down arrow keys
	// than a text editor view.
	KeyBindings() KeyBindings

	// Title is View's title, which can be the current file name or any other
	// relevant detail.
	Title() string
	// IsDirty is true if the content should be saved before quitting.
	IsDirty() bool

	// IsDisabled returns false if the View can be activated to receive user
	// inputs at all.
	IsDisabled() bool

	// Buffer returns the display buffer for this Window.
	Buffer() *Buffer

	// NaturalSize returns the natural size of the content. It can be -1 for as
	// long/large as possible, 0 if indeterminate. The return value of this
	// function is not affected by SetSize().
	NaturalSize() (width, height int)
	// SetSize resets the View Buffer size.
	SetSize(x, y int)

	// OnAttach is called by the Window after it was attached.
	// TODO(maruel): Maybe split in ViewFull?
	OnAttach(w Window)
}

// Config

// Configuration manager.
type Config interface {
	GetInt(name string) int
	GetString(name string) string
	Save()
}

// Control

// CommandHandler executes the command cmd on the Window w.
type CommandHandler func(cd CommandDispatcherFull, w Window, args ...string)

// Command describes a registered command that can be triggered directly at the
// command prompt, via a keybinding or a plugin.
type Command interface {
	// Name is the name of the command.
	Name() string
	// Handle executes the command.
	Handle(cd CommandDispatcherFull, w Window, args ...string)
	// Category returns the category the command should be bucketed in, for help
	// documentation purpose.
	Category(cd CommandDispatcherFull, w Window) CommandCategory
	// ShortDesc returns a short description of the command in the language
	// requested. It defaults to English if the description was not translated in
	// this language.
	ShortDesc(cd CommandDispatcherFull, w Window) string
	// LongDesc returns a long explanation of the command in the language
	// requested. It defaults to English if the description was not translated in
	// this language.
	LongDesc(cd CommandDispatcherFull, w Window) string
}

// Commands stores the known commands. This is where plugins can add new
// commands. Each View contains its own Commands.
type Commands interface {
	// Register registers a command so it can be executed later. In practice
	// commands should normally be registered on startup. Returns false if a
	// command was already registered and was lost.
	Register(cmd Command) bool

	// Get returns a command if registered, nil otherwise.
	Get(cmdName string) Command
}

// KeyBindings stores the mapping between keyboard entry and commands. This
// includes what can be considered "macros" as much as casual things like arrow
// keys.
type KeyBindings interface {
	// Set registers a keyboard mapping. In practice keyboard mappings
	// should normally be registered on startup. Returns false if a key mapping
	// was already registered and was lost. Set cmdName to "" to remove a key
	// binding.
	Set(mode KeyboardMode, keyName string, cmdName string) bool

	// Get returns a command if registered, nil otherwise.
	Get(mode KeyboardMode, keyName string) string
}

// Utility functions.

// PostCommand appends a Command at the end of the queue.
// It is a shortcut to cd.PostCommands([][]string{[]string{cmdName, args...}})
func PostCommand(cd CommandDispatcher, cmdName string, args ...string) {
	line := make([]string, len(args)+1)
	line[0] = cmdName
	copy(line[1:], args)
	cd.PostCommands([][]string{line})
}

// GetCommand traverses the Window hierarchy tree to find a View that has
// the command cmd in its Commands mapping. If Window is nil, it starts with
// the Editor's active Window.
func GetCommand(cd CommandDispatcherFull, w Window, cmdName string) Command {
	if w == nil {
		w = cd.ActiveWindow()
	}
	for {
		cmd := w.View().Commands().Get(cmdName)
		if cmd != nil {
			return cmd
		}
		w := w.Parent()
		if w == nil {
			return nil
		}
	}
}

// GetKeyBindingCommand traverses the Editor's Window tree to find a View that
// has the key binding in its Keyboard mapping.
func GetKeyBindingCommand(e Editor, mode KeyboardMode, keyName string) string {
	active := e.ActiveWindow()
	for {
		cmdName := active.View().KeyBindings().Get(mode, keyName)
		if cmdName != "" {
			return cmdName
		}
		active := active.Parent()
		if active == nil {
			return ""
		}
	}
}

// RootWindow returns the root Window when given any Window in the tree.
func RootWindow(w Window) Window {
	for {
		if w.Parent() == nil {
			return w
		}
		w = w.Parent()
	}
}

// PositionOnScreen returns the exact position on screen of a Window.
func PositionOnScreen(w Window) Rect {
	out := w.Rect()
	if w.Docking() == DockingFloating {
		return out
	}
	for {
		w = w.Parent()
		if w == nil {
			break
		}
		// Take in account the parent Window position.
		r := w.Rect()
		out.X += r.X
		out.Y += r.Y
		if w.Docking() == DockingFloating {
			break
		}
	}
	return out
}
<|endoftext|>