<commit_before>// Copyright (c) 2017 Arista Networks, Inc.
// Use of this source code is governed by the Apache License 2.0
// that can be found in the COPYING file.

// Package path contains methods for dealing with absolute
// paths elementally.
package path

import (
	"bytes"
	"strings"

	"github.com/aristanetworks/goarista/key"
)

// Path represents an absolute path decomposed into elements
// where each element is a key.Key.
type Path []key.Key

// New constructs a Path from a variable number of elements.
// Each element may either be a key.Key or a value that can
// be wrapped by a key.Key.
func New(elements ...interface{}) Path {
	path := make(Path, len(elements))
	copyElements(path, elements...)
	return path
}

// Append appends a variable number of elements to a Path.
// Each element may either be a key.Key or a value that can
// be wrapped by a key.Key.
func Append(path Path, elements ...interface{}) Path {
	if len(elements) == 0 {
		return path
	}
	n := len(path)
	p := make(Path, n+len(elements))
	copy(p, path)
	copyElements(p[n:], elements...)
	return p
}

// Join joins a variable number of Paths together. Each path
// in the joining is treated as a subpath of its predecessor.
func Join(paths ...Path) Path {
	if len(paths) == 0 {
		return nil
	}
	n := 0
	for _, path := range paths {
		n += len(path)
	}
	joined := make(Path, 0, n)
	for _, path := range paths {
		if len(path) != 0 {
			joined = append(joined, path...)
		}
	}
	return joined
}

// Base returns the last element of the Path. If the Path is
// empty, Base returns nil.
func Base(path Path) key.Key {
	if len(path) > 0 {
		return path[len(path)-1]
	}
	return nil
}

// Clone returns a new Path with the same elements as in the
// provided Path.
func Clone(path Path) Path {
	p := make(Path, len(path))
	copy(p, path)
	return p
}

// Equal returns whether Path a and Path b are the same
// length and whether each element in b corresponds to the
// same element in a.
func Equal(a, b Path) bool {
	return len(a) == len(b) && hasPrefix(a, b)
}

// HasPrefix returns whether Path b is at most the length
// of Path a and whether each element in b corresponds to
// the same element in a.
func HasPrefix(a, b Path) bool {
	return len(a) >= len(b) && hasPrefix(a, b)
}

// Match returns whether Path a and Path b are the same
// length and whether each element in b corresponds to the
// same element or a wildcard in a.
func Match(a, b Path) bool {
	return len(a) == len(b) && matchPrefix(a, b)
}

// MatchPrefix returns whether Path b is at most the length
// of Path a and whether each element in b corresponds to
// the same element or a wildcard in a.
func MatchPrefix(a, b Path) bool {
	return len(a) >= len(b) && matchPrefix(a, b)
}

// FromString constructs a Path from the elements resulting
// from a split of the input string by "/". Strings that do
// not lead with a '/' are accepted but not reconstructable.
func FromString(str string) Path {
	if str == "" {
		return Path{}
	} else if str[0] == '/' {
		str = str[1:]
	}
	elements := strings.Split(str, "/")
	path := make(Path, len(elements))
	for i, element := range elements {
		path[i] = key.New(element)
	}
	return path
}

// String returns the Path as a string.
func (p Path) String() string {
	if len(p) == 0 {
		return "/"
	}
	var buf bytes.Buffer
	for _, element := range p {
		buf.WriteByte('/')
		buf.WriteString(element.String())
	}
	return buf.String()
}

func copyElements(path Path, elements ...interface{}) {
	for i, element := range elements {
		switch val := element.(type) {
		case key.Key:
			path[i] = val
		default:
			path[i] = key.New(val)
		}
	}
}

func hasPrefix(a, b Path) bool {
	for i := range b {
		if !b[i].Equal(a[i]) {
			return false
		}
	}
	return true
}

func matchPrefix(a, b Path) bool {
	for i := range b {
		if !a[i].Equal(Wildcard) && !b[i].Equal(a[i]) {
			return false
		}
	}
	return true
}
<commit_msg>path: Fix comments once again<commit_after>// Copyright (c) 2017 Arista Networks, Inc.
// Use of this source code is governed by the Apache License 2.0
// that can be found in the COPYING file.

// Package path contains methods for dealing with elemental paths.
package path

import (
	"bytes"
	"strings"

	"github.com/aristanetworks/goarista/key"
)

// Path represents a path decomposed into elements where each
// element is a key.Key. A Path can be interpreted as either
// absolute or relative depending on how it is used.
type Path []key.Key

// New constructs a Path from a variable number of elements.
// Each element may either be a key.Key or a value that can
// be wrapped by a key.Key.
func New(elements ...interface{}) Path {
	path := make(Path, len(elements))
	copyElements(path, elements...)
	return path
}

// Append appends a variable number of elements to a Path.
// Each element may either be a key.Key or a value that can
// be wrapped by a key.Key. Note that calling Append on a
// single Path returns that same Path, whereas in all other
// cases a new Path is returned.
func Append(path Path, elements ...interface{}) Path {
	if len(elements) == 0 {
		return path
	}
	n := len(path)
	p := make(Path, n+len(elements))
	copy(p, path)
	copyElements(p[n:], elements...)
	return p
}

// Join joins a variable number of Paths together. Each path
// in the joining is treated as a subpath of its predecessor.
// Calling Join with no arguments returns nil.
func Join(paths ...Path) Path {
	if len(paths) == 0 {
		return nil
	}
	n := 0
	for _, path := range paths {
		n += len(path)
	}
	joined := make(Path, 0, n)
	for _, path := range paths {
		if len(path) != 0 {
			joined = append(joined, path...)
		}
	}
	return joined
}

// Base returns the last element of the Path. If the Path is
// empty, Base returns nil.
func Base(path Path) key.Key {
	if len(path) > 0 {
		return path[len(path)-1]
	}
	return nil
}

// Clone returns a new Path with the same elements as in the
// provided Path.
func Clone(path Path) Path {
	p := make(Path, len(path))
	copy(p, path)
	return p
}

// Equal returns whether Path a and Path b are the same
// length and whether each element in b corresponds to the
// same element in a.
func Equal(a, b Path) bool {
	return len(a) == len(b) && hasPrefix(a, b)
}

// HasPrefix returns whether Path b is at most the length
// of Path a and whether each element in b corresponds to
// the same element in a from the first element.
func HasPrefix(a, b Path) bool {
	return len(a) >= len(b) && hasPrefix(a, b)
}

// Match returns whether Path a and Path b are the same
// length and whether each element in b corresponds to the
// same element or a wildcard in a.
func Match(a, b Path) bool {
	return len(a) == len(b) && matchPrefix(a, b)
}

// MatchPrefix returns whether Path b is at most the length
// of Path a and whether each element in b corresponds to
// the same element or a wildcard in a from the first
// element.
func MatchPrefix(a, b Path) bool {
	return len(a) >= len(b) && matchPrefix(a, b)
}

// FromString constructs a Path from the elements resulting
// from a split of the input string by "/". Strings that do
// not lead with a '/' are accepted but not reconstructable
// with Path.String.
func FromString(str string) Path {
	if str == "" {
		return Path{}
	} else if str[0] == '/' {
		str = str[1:]
	}
	elements := strings.Split(str, "/")
	path := make(Path, len(elements))
	for i, element := range elements {
		path[i] = key.New(element)
	}
	return path
}

// String returns the Path as an absolute path string.
func (p Path) String() string {
	if len(p) == 0 {
		return "/"
	}
	var buf bytes.Buffer
	for _, element := range p {
		buf.WriteByte('/')
		buf.WriteString(element.String())
	}
	return buf.String()
}

func copyElements(path Path, elements ...interface{}) {
	for i, element := range elements {
		switch val := element.(type) {
		case key.Key:
			path[i] = val
		default:
			path[i] = key.New(val)
		}
	}
}

func hasPrefix(a, b Path) bool {
	for i := range b {
		if !b[i].Equal(a[i]) {
			return false
		}
	}
	return true
}

func matchPrefix(a, b Path) bool {
	for i := range b {
		if !a[i].Equal(Wildcard) && !b[i].Equal(a[i]) {
			return false
		}
	}
	return true
}
<|endoftext|>


<commit_before>package gopath

import (
	"fmt"
	"testing"
)

type Person struct {
	Name    string
	Age     int
	Friends []Person
}

func TestSimple(t *testing.T) {
	jim := Person{Name: "Jim", Age: 31}

	if name, ok := NewPath("Name").First(jim); ok {
		if name.(string) != jim.Name {
			t.Fail()
		}
		fmt.Printf("Name => %s\n", name)
	} else {
		t.Fail()
	}

	if age, ok := NewPath("Age").First(jim); ok {
		if age.(int) != jim.Age {
			t.Fail()
		}
		fmt.Printf("Age => %d\n", age)
	} else {
		t.Fail()
	}
}

func TestArray(t *testing.T) {
	jim := Person{Name: "Jim", Age: 31}

	jim.Friends = append(jim.Friends, Person{Name: "John", Age: 44})
	jim.Friends = append(jim.Friends, Person{Name: "Claire", Age: 62})

	it := NewPath("/Friends/*/Name").Iter(jim)
	for i := 0; it.Next(); i++ {
		name := it.Value().(string)
		fmt.Printf("Friend -> %s\n", name)
		if jim.Friends[i].Name != name {
			t.Fail()
		}
	}

	it = NewPath("**/Name").Iter(jim)
	for i := 0; it.Next(); i++ {
		name := it.Value().(string)
		fmt.Printf("Friend names -> %s\n", name)
	}
}

func TestSlice(t *testing.T) {
	s := [][]int{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10}}

	it := NewPath("*").Iter(s)
	for i := 0; it.Next(); i++ {
		fmt.Printf("Array => %s\n", it.Value())
	}

	it = NewPath("**/*").Iter(s)
	for i := 0; it.Next(); i++ {
		fmt.Printf("Array or arrays => %s\n", it.Value())
	}

}

// func TestInplaceUpdate(t *testing.T) {
// 	type Fish struct {
// 		Name    string
// 		Spots   map[int]string
// 		Stripes []int
// 	}

// 	f := Fish{
// 		Name:    "Harold",
// 		Spots:   map[int]string{1: "a", 2: "b", 3: "c"},
// 		Stripes: []int{1, 2, 3, 4, 5},
// 	}

// 	m := Find("Spots", f)[0]
// 	m.(map[int]string)[4] = "d"

// 	if d, ok := f.Spots[4]; !ok {
// 		t.Fail()
// 	} else {
// 		if d != f.Spots[4] {
// 			t.Fail()
// 		}
// 	}
// }
<commit_msg>Updates<commit_after>package gopath

import (
	"fmt"
	"testing"
)

type Person struct {
	Name    string
	Age     int
	Friends []Person
}

func TestSimple(t *testing.T) {
	jim := Person{Name: "Jim", Age: 31}

	if name, ok := NewPath("Name").First(jim); ok {
		if name.(string) != jim.Name {
			t.Fail()
		}
		fmt.Printf("Name => %s\n", name)
	} else {
		t.Fail()
	}

	if age, ok := NewPath("Age").First(jim); ok {
		if age.(int) != jim.Age {
			t.Fail()
		}
		fmt.Printf("Age => %d\n", age)
	} else {
		t.Fail()
	}
}

func TestArray(t *testing.T) {
	jim := Person{Name: "Jim", Age: 31}

	jim.Friends = append(jim.Friends, Person{Name: "John", Age: 44})
	jim.Friends = append(jim.Friends, Person{Name: "Claire", Age: 62})

	it := NewPath("/Friends/*/Name").Iter(jim)
	for i := 0; it.Next(); i++ {
		name := it.Value().(string)
		fmt.Printf("Friend -> %s\n", name)
		if jim.Friends[i].Name != name {
			t.Fail()
		}
	}

	it = NewPath("**/Name").Iter(jim)
	for i := 0; it.Next(); i++ {
		name := it.Value().(string)
		fmt.Printf("Friend names -> %s\n", name)
	}
}

func TestSlice(t *testing.T) {
	s := [][]int{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10}}

	it := NewPath("*").Iter(s)
	for i := 0; it.Next(); i++ {
		fmt.Printf("Array => %s\n", it.Value())
	}

	it = NewPath("**/*/*").Iter(s)
	for i := 0; it.Next(); i++ {
		fmt.Printf("Array or a => %s\n", it.Value())
	}

}

// func TestInplaceUpdate(t *testing.T) {
// 	type Fish struct {
// 		Name    string
// 		Spots   map[int]string
// 		Stripes []int
// 	}

// 	f := Fish{
// 		Name:    "Harold",
// 		Spots:   map[int]string{1: "a", 2: "b", 3: "c"},
// 		Stripes: []int{1, 2, 3, 4, 5},
// 	}

// 	m := Find("Spots", f)[0]
// 	m.(map[int]string)[4] = "d"

// 	if d, ok := f.Spots[4]; !ok {
// 		t.Fail()
// 	} else {
// 		if d != f.Spots[4] {
// 			t.Fail()
// 		}
// 	}
// }
<|endoftext|>


<commit_before>package gopher

import (
	"github.com/gopherlabs/gopher/contracts"
	"net/http"
)

func NewRouter() contracts.Routerable {
	return GetContext().Router.NewRouter()
}

func Get(path string, fn func(http.ResponseWriter, *http.Request)) contracts.Routerable {
	return GetContext().Router.Get(path, fn)
}

func Serve() {
	GetContext().Router.Serve()
}
<commit_msg>Cleaned up Routable facade<commit_after>package gopher

import (
	"github.com/gopherlabs/gopher/contracts"
)

func NewRouter() contracts.Routable {
	return GetContext().Router.NewRouter()
}
<|endoftext|>


<commit_before>package pearl

import (
	"crypto/rsa"
	"net"
	"time"

	"github.com/mmcloughlin/pearl/log"
	"github.com/mmcloughlin/pearl/meta"
	"github.com/mmcloughlin/pearl/torconfig"
	"github.com/mmcloughlin/pearl/torcrypto"
	"github.com/mmcloughlin/pearl/tordir"
	"github.com/mmcloughlin/pearl/torexitpolicy"
	"github.com/pkg/errors"
	"github.com/uber-go/tally"
)

// Router is a Tor router.
type Router struct {
	config      *torconfig.Config
	fingerprint []byte

	connections *ConnectionManager

	metrics *Metrics
	scope   tally.Scope
	logger  log.Logger
}

// TODO(mbm): determine which parts of Router struct are required for client and
// server. Perhaps a stripped down struct can be used for client-only.

// NewRouter constructs a router based on the given config.
func NewRouter(config *torconfig.Config, scope tally.Scope, logger log.Logger) (*Router, error) {
	fingerprint, err := torcrypto.Fingerprint(&config.Keys.Identity.PublicKey)
	if err != nil {
		return nil, errors.Wrap(err, "failed to compute fingerprint")
	}

	logger = log.ForComponent(logger, "router")
	return &Router{
		config:      config,
		fingerprint: fingerprint,
		connections: NewConnectionManager(),
		metrics:     NewMetrics(scope, logger),
		scope:       scope,
		logger:      logger,
	}, nil
}

// IdentityKey returns the identity key of the router.
func (r *Router) IdentityKey() *rsa.PrivateKey {
	return r.config.Keys.Identity
}

// Fingerprint returns the router fingerprint.
func (r *Router) Fingerprint() []byte {
	return r.fingerprint
}

// Serve starts a listener and enters a main loop handling connections.
func (r *Router) Serve() error {
	laddr := r.config.ORAddr()
	r.logger.With("laddr", laddr).Info("creating listener")
	ln, err := net.Listen("tcp", laddr)
	if err != nil {
		return errors.Wrap(err, "could not create listener")
	}

	for {
		conn, err := ln.Accept()
		if err != nil {
			return errors.Wrap(err, "error accepting connection")
		}

		c, err := NewServer(r, conn, r.logger)
		if err != nil {
			return errors.Wrap(err, "error building connection")
		}

		go func() {
			if err := c.Serve(); err != nil {
				log.Err(r.logger, err, "error serving connection")
			}
		}()
	}
}

func (r *Router) Connect(raddr string) (*Connection, error) {
	conn, err := net.Dial("tcp", raddr)
	if err != nil {
		return nil, errors.Wrap(err, "dial failed")
	}

	c, err := NewClient(r, conn, r.logger)
	if err != nil {
		return nil, errors.Wrap(err, "building connection failed")
	}

	// TODO(mbm): should we be calling this here?
	err = c.StartClient()
	if err != nil {
		return nil, errors.Wrap(err, "error starting client")
	}

	return c, nil
}

// Connection returns a connection to the indicated relay. Returns an existing
// connection, if it exists. Otherwise opens a connection and returns it.
func (r *Router) Connection(hint ConnectionHint) (*Connection, error) {
	fp, err := hint.Fingerprint()
	if err != nil {
		return nil, errors.Wrap(err, "missing fingerprint from connection hint")
	}

	if conn, ok := r.connections.Connection(fp); ok {
		return conn, nil
	}

	addrs, err := hint.Addresses()
	if err != nil {
		return nil, errors.Wrap(err, "no addresses provided")
	}

	for _, addr := range addrs {
		raddr := addr.String()
		conn, err := r.Connect(raddr)
		if err != nil {
			log.WithErr(r.logger, err).Warn("connection attempt failed")
			continue
		}
		return conn, nil
	}

	return nil, errors.New("all connection attempts failed")
}

// Descriptor returns a server descriptor for this router.
func (r *Router) Descriptor() (*tordir.ServerDescriptor, error) {
	s := tordir.NewServerDescriptor()

	if err := s.SetRouter(r.config.Nickname, r.config.IP, r.config.ORPort, 0); err != nil {
		return nil, err
	}
	if err := s.SetSigningKey(r.IdentityKey()); err != nil {
		return nil, err
	}
	if err := s.SetOnionKey(&r.config.Keys.Onion.PublicKey); err != nil {
		return nil, err
	}

	s.SetNtorOnionKey(r.config.Keys.Ntor)
	s.SetPlatform(r.config.Platform)
	s.SetContact(r.config.Contact)
	s.SetBandwidth(r.config.BandwidthAverage, r.config.BandwidthBurst, r.config.BandwidthAverage) // TODO(mbm): publish real bandwidth values
	s.SetPublishedTime(time.Now())
	s.SetExitPolicy(torexitpolicy.RejectAllPolicy)
	s.SetProtocols(meta.Protocols)

	return s, nil
}
<commit_msg>fix long line<commit_after>package pearl

import (
	"crypto/rsa"
	"net"
	"time"

	"github.com/mmcloughlin/pearl/log"
	"github.com/mmcloughlin/pearl/meta"
	"github.com/mmcloughlin/pearl/torconfig"
	"github.com/mmcloughlin/pearl/torcrypto"
	"github.com/mmcloughlin/pearl/tordir"
	"github.com/mmcloughlin/pearl/torexitpolicy"
	"github.com/pkg/errors"
	"github.com/uber-go/tally"
)

// Router is a Tor router.
type Router struct {
	config      *torconfig.Config
	fingerprint []byte

	connections *ConnectionManager

	metrics *Metrics
	scope   tally.Scope
	logger  log.Logger
}

// TODO(mbm): determine which parts of Router struct are required for client and
// server. Perhaps a stripped down struct can be used for client-only.

// NewRouter constructs a router based on the given config.
func NewRouter(config *torconfig.Config, scope tally.Scope, logger log.Logger) (*Router, error) {
	fingerprint, err := torcrypto.Fingerprint(&config.Keys.Identity.PublicKey)
	if err != nil {
		return nil, errors.Wrap(err, "failed to compute fingerprint")
	}

	logger = log.ForComponent(logger, "router")
	return &Router{
		config:      config,
		fingerprint: fingerprint,
		connections: NewConnectionManager(),
		metrics:     NewMetrics(scope, logger),
		scope:       scope,
		logger:      logger,
	}, nil
}

// IdentityKey returns the identity key of the router.
func (r *Router) IdentityKey() *rsa.PrivateKey {
	return r.config.Keys.Identity
}

// Fingerprint returns the router fingerprint.
func (r *Router) Fingerprint() []byte {
	return r.fingerprint
}

// Serve starts a listener and enters a main loop handling connections.
func (r *Router) Serve() error {
	laddr := r.config.ORAddr()
	r.logger.With("laddr", laddr).Info("creating listener")
	ln, err := net.Listen("tcp", laddr)
	if err != nil {
		return errors.Wrap(err, "could not create listener")
	}

	for {
		conn, err := ln.Accept()
		if err != nil {
			return errors.Wrap(err, "error accepting connection")
		}

		c, err := NewServer(r, conn, r.logger)
		if err != nil {
			return errors.Wrap(err, "error building connection")
		}

		go func() {
			if err := c.Serve(); err != nil {
				log.Err(r.logger, err, "error serving connection")
			}
		}()
	}
}

func (r *Router) Connect(raddr string) (*Connection, error) {
	conn, err := net.Dial("tcp", raddr)
	if err != nil {
		return nil, errors.Wrap(err, "dial failed")
	}

	c, err := NewClient(r, conn, r.logger)
	if err != nil {
		return nil, errors.Wrap(err, "building connection failed")
	}

	// TODO(mbm): should we be calling this here?
	err = c.StartClient()
	if err != nil {
		return nil, errors.Wrap(err, "error starting client")
	}

	return c, nil
}

// Connection returns a connection to the indicated relay. Returns an existing
// connection, if it exists. Otherwise opens a connection and returns it.
func (r *Router) Connection(hint ConnectionHint) (*Connection, error) {
	fp, err := hint.Fingerprint()
	if err != nil {
		return nil, errors.Wrap(err, "missing fingerprint from connection hint")
	}

	if conn, ok := r.connections.Connection(fp); ok {
		return conn, nil
	}

	addrs, err := hint.Addresses()
	if err != nil {
		return nil, errors.Wrap(err, "no addresses provided")
	}

	for _, addr := range addrs {
		raddr := addr.String()
		conn, err := r.Connect(raddr)
		if err != nil {
			log.WithErr(r.logger, err).Warn("connection attempt failed")
			continue
		}
		return conn, nil
	}

	return nil, errors.New("all connection attempts failed")
}

// Descriptor returns a server descriptor for this router.
func (r *Router) Descriptor() (*tordir.ServerDescriptor, error) {
	s := tordir.NewServerDescriptor()

	if err := s.SetRouter(r.config.Nickname, r.config.IP, r.config.ORPort, 0); err != nil {
		return nil, err
	}
	if err := s.SetSigningKey(r.IdentityKey()); err != nil {
		return nil, err
	}
	if err := s.SetOnionKey(&r.config.Keys.Onion.PublicKey); err != nil {
		return nil, err
	}

	s.SetNtorOnionKey(r.config.Keys.Ntor)
	s.SetPlatform(r.config.Platform)
	s.SetContact(r.config.Contact)
	// TODO(mbm): publish real bandwidth values
	s.SetBandwidth(r.config.BandwidthAverage, r.config.BandwidthBurst, r.config.BandwidthAverage)
	s.SetPublishedTime(time.Now())
	s.SetExitPolicy(torexitpolicy.RejectAllPolicy)
	s.SetProtocols(meta.Protocols)

	return s, nil
}
<|endoftext|>


<commit_before>package dashboard

import (
	"flag"
	"html/template"
	"net/http"
	"sync"

	"hkjn.me/googleauth"

	"github.com/golang/glog"
	"github.com/gorilla/mux"
)

var (
	authDisabled   = flag.Bool("no_auth", false, "disables authentication (use for testing only)")
	parseFlagsOnce = sync.Once{}
	indexTmpls     = []string{
		"tmpl/base.tmpl",
		"tmpl/scripts.tmpl",
		"tmpl/style.tmpl",
		"tmpl/index.tmpl",
		"tmpl/links.tmpl",
		"tmpl/prober.tmpl",
	}
	routes = []route{
		newPage("/", indexTmpls, getIndexData),
		simpleRoute{"/connect", "GET", googleauth.ConnectHandler},
	}
)

// NewRouter returns a new router.
func NewRouter() *mux.Router {
	router := mux.NewRouter().StrictSlash(true)
	for _, r := range routes {
		glog.V(1).Infof("registering route for %q on %q\n", r.Method(), r.Pattern())
		router.
			Methods(r.Method()).
			Path(r.Pattern()).
			HandlerFunc(r.HandlerFunc())
	}
	return router
}

// serveISE serves an internal server error to the user.
func serveISE(w http.ResponseWriter) {
	http.Error(w, "Internal server error.", http.StatusInternalServerError)
}

// route describes how to serve HTTP on an endpoint.
type route interface {
	Method() string                // GET, POST, PUT, etc.
	Pattern() string               // URI for the route
	HandlerFunc() http.HandlerFunc // HTTP handler func
}

// simpleRoute implements the route interface for endpoints.
type simpleRoute struct {
	method, pattern string
	handlerFunc     http.HandlerFunc
}

func (r simpleRoute) Method() string { return r.method }

func (r simpleRoute) Pattern() string { return r.pattern }

func (r simpleRoute) HandlerFunc() http.HandlerFunc { return r.handlerFunc }

// getDataFn is a function to get template data.
type getDataFn func(http.ResponseWriter, *http.Request) (interface{}, error)

// page implements the route interface for endpoints that render HTML.
type page struct {
	pattern         string
	tmpl            *template.Template // backing template
	getTemplateData getDataFn
}

// newPage returns a new page.
func newPage(pattern string, tmpls []string, getData getDataFn) *page {
	return &page{
		pattern,
		template.Must(template.ParseFiles(tmpls...)),
		getData,
	}
}

func (p page) Method() string { return "GET" }

func (p page) Pattern() string { return p.pattern }

// HandlerFunc returns the http handler func, which renders the
// template with the data.
func (p page) HandlerFunc() http.HandlerFunc {
	fn := func(w http.ResponseWriter, r *http.Request) {
		data, err := p.getTemplateData(w, r)
		if err != nil {
			glog.Errorf("error getting template data: %v\n", err)
			serveISE(w)
			return
		}
		err = p.tmpl.ExecuteTemplate(w, "base", data)
		if err != nil {
			glog.Errorf("error rendering template: %v\n", err)
			serveISE(w)
			return
		}
	}

	parseFlagsOnce.Do(func() {
		if !flag.Parsed() {
			flag.Parse()
		}
	})
	if *authDisabled {
		glog.Infof("-disabled_auth is set, not checking credentials\n")
	} else {
		fn = googleauth.RequireLogin(fn)
	}
	return fn
}
<commit_msg>better comment<commit_after>package dashboard

import (
	"flag"
	"html/template"
	"net/http"
	"sync"

	"hkjn.me/googleauth"

	"github.com/golang/glog"
	"github.com/gorilla/mux"
)

var (
	authDisabled   = flag.Bool("no_auth", false, "disables authentication (use for testing only)")
	parseFlagsOnce = sync.Once{}
	indexTmpls     = []string{
		"tmpl/base.tmpl",
		"tmpl/scripts.tmpl",
		"tmpl/style.tmpl",
		"tmpl/index.tmpl",
		"tmpl/links.tmpl",
		"tmpl/prober.tmpl",
	}
	routes = []route{
		newPage("/", indexTmpls, getIndexData),
		simpleRoute{"/connect", "GET", googleauth.ConnectHandler},
	}
)

// NewRouter returns a new router for the endpoints of the dashboard.
func NewRouter() *mux.Router {
	router := mux.NewRouter().StrictSlash(true)
	for _, r := range routes {
		glog.V(1).Infof("registering route for %q on %q\n", r.Method(), r.Pattern())
		router.
			Methods(r.Method()).
			Path(r.Pattern()).
			HandlerFunc(r.HandlerFunc())
	}
	return router
}

// serveISE serves an internal server error to the user.
func serveISE(w http.ResponseWriter) {
	http.Error(w, "Internal server error.", http.StatusInternalServerError)
}

// route describes how to serve HTTP on an endpoint.
type route interface {
	Method() string                // GET, POST, PUT, etc.
	Pattern() string               // URI for the route
	HandlerFunc() http.HandlerFunc // HTTP handler func
}

// simpleRoute implements the route interface for endpoints.
type simpleRoute struct {
	method, pattern string
	handlerFunc     http.HandlerFunc
}

func (r simpleRoute) Method() string { return r.method }

func (r simpleRoute) Pattern() string { return r.pattern }

func (r simpleRoute) HandlerFunc() http.HandlerFunc { return r.handlerFunc }

// getDataFn is a function to get template data.
type getDataFn func(http.ResponseWriter, *http.Request) (interface{}, error)

// page implements the route interface for endpoints that render HTML.
type page struct {
	pattern         string
	tmpl            *template.Template // backing template
	getTemplateData getDataFn
}

// newPage returns a new page.
func newPage(pattern string, tmpls []string, getData getDataFn) *page {
	return &page{
		pattern,
		template.Must(template.ParseFiles(tmpls...)),
		getData,
	}
}

func (p page) Method() string { return "GET" }

func (p page) Pattern() string { return p.pattern }

// HandlerFunc returns the http handler func, which renders the
// template with the data.
func (p page) HandlerFunc() http.HandlerFunc {
	fn := func(w http.ResponseWriter, r *http.Request) {
		data, err := p.getTemplateData(w, r)
		if err != nil {
			glog.Errorf("error getting template data: %v\n", err)
			serveISE(w)
			return
		}
		err = p.tmpl.ExecuteTemplate(w, "base", data)
		if err != nil {
			glog.Errorf("error rendering template: %v\n", err)
			serveISE(w)
			return
		}
	}

	parseFlagsOnce.Do(func() {
		if !flag.Parsed() {
			flag.Parse()
		}
	})
	if *authDisabled {
		glog.Infof("-disabled_auth is set, not checking credentials\n")
	} else {
		fn = googleauth.RequireLogin(fn)
	}
	return fn
}
<|endoftext|>


<commit_before>package rest

import (
	"errors"
	"fmt"
	"path"
	"sort"
	"strings"
)

// ParamFlag определяет символ, используемый в качестве определения параметра (должен идти
// в самом начале).
const paramFlag byte = ':'

// Param описывает именованный параметр и его значение. В качестве ключа используется имя параметра
// (без символа параметра), а в качестве значения — строка из пути, соответствующая данной позиции.
//
// Я не стал использовать для параметров словарь, т.к. данный метод позволяет сохранить порядок
// следования параметров и использовать параметры с одинаковым именем.
type Param struct {
	Key, Value string
}

// record описывает информацию о пути, в котором есть параметры.
//
// Значение priority задает приоритет для сортировки ключей с одинаковым количеством параметров и
// формируется следующим образом: в старших восьми байтах содержится количество элементов с
// параметрами, а в младших — со статическими путями. Таким образом получается, что элементы
// с меньшим количеством параметров имеют более высокий приоритет и после сортировки будут
// обрабатывается позже, чем элементы с меньшим количеством параметров.
//
// Текущая реализация имеет ограничение на максимальное количество элементов пути — 32767.
// Это связано с методом хранения этого значения в свойстве priority. В принципе, ничего не мешает
// просто увеличить размер priority до uint32 и поправить соответствующие места, где он определен,
// но мне показалось, что для моих задач этого более, чем достаточно.
type record struct {
	params uint16      // количество параметров в пути
	handle interface{} // обработчик запроса
	parts  []string    // путь, разобранный на составные части
}

// records описывает список путей с параметрами и поддерживает сортировку по флагу приоритета.
type records []*record

// поддержка методов для сортировки.
func (n records) Len() int           { return len(n) }
func (n records) Swap(i, j int)      { n[i], n[j] = n[j], n[i] }
func (n records) Less(i, j int) bool { return n[i].params < n[j].params }

// router описывает структуру для быстрого выбора обработчиков по пути запроса.
// Поддерживает как статические пути, так и пути с параметрами.
//
// Текущая реализация не привязана к конкретным типам обработчиков и может хранить любые
// объекты в качестве таких обработчиков.
type router struct {
	// хранилище статических путей, без параметров
	// в качестве ключа используется полный путь
	static map[string]interface{}
	// хранит информацию о путях с параметрами
	// в качестве ключа используется общее количество элементов пути
	fields   map[uint16]records
	maxParts uint16 // максимальное количество частей пути в определениях
}

// add добавляет описание нового пути запроса и ассоциирует его с указанным обработчиком запроса.
// Возвращает ошибку, если количество частей пути больше 32767. В качестве флага для определения
// именованных параметров используется символ ':' и '*' для завершающего параметра, который
// "забирает" в себя весь оставшийся путь.
func (r *router) add(url string, handle interface{}) error {
	parts := split(url) // нормализуем путь и разбиваем его на части
	// проверяем, что количество получившихся частей не превышает поддерживаемое количество.
	length := len(parts)
	if length > (1<<15 - 1) {
		return fmt.Errorf("path parts overflow: %d", len(parts))
	}
	var dynamic uint16 // считаем количество параметров
	for i, value := range parts {
		// if len(value) > 0 { после нормализации пути не должно быть пустых элементов
		switch value[0] {
		case byte('*'):
			if i != length-1 {
				return errors.New("catch-all parameter must be last")
			}
			dynamic |= 1 << 15 // взводим флаг *-параметра
			fallthrough
		case byte(':'):
			dynamic++ // увеличиваем счетчик параметров
		}
		// }
	}
	if dynamic == 0 { // в пути нет параметров — добавляем в статические обработчики
		if r.static == nil {
			// инициализируем статику, если не сделали этого раньше
			r.static = make(map[string]interface{})
		}
		r.static[strings.Join(parts, "/")] = handle
		return nil
	}
	level := uint16(length) // всего элементов пути
	if r.maxParts < level { // запоминаем максимальное количество определенных параметров
		r.maxParts = level
	}
	if r.fields == nil {
		// инициализируем динамические пути, если не сделали этого раньше
		r.fields = make(map[uint16]records)
	}
	// в пути есть динамические параметры — добавляем в список с параметрами
	record := &record{
		params: dynamic,
		handle: handle, // обработчик запроса
		parts:  parts,  // части пути
	}
	// сохраняем в массиве обработчиков с таким же количеством параметров
	r.fields[level] = append(r.fields[level], record)
	sort.Stable(r.fields[level]) // сортируем по количеству параметров
	return nil
}

// lookup возвращает обработчик и список именованных параметров с их значениям. Символ параметра
// из имени при этом изымается. Если подходящего обработчика не найдено, то возвращается nil.
func (r *router) lookup(url string) (interface{}, []Param) {
	parts := split(url) // нормализуем путь и разбиваем его на части
	// сначала ищем среди статических путей
	if r.static != nil { // если статические пути не определены, то пропускаем проверку
		if handle, ok := r.static[strings.Join(parts, "/")]; ok {
			return handle, nil
		}
	}
	if r.fields == nil { // если пути с параметрами не определены, то пропускаем проверку
		return nil, nil
	}
	length := uint16(len(parts))
	var total uint16
	if length > r.maxParts {
		total = r.maxParts
	} else {
		total = length
	}
	// запрашиваем список обработчиков для такого же количества элементов пути
	for l := total; l > 0; l-- {
		records := r.fields[l]
		if len(records) == 0 {
			continue // обработчики для такого пути не зарегистрированы
		}
		catchOnlyAll := l < length // флаг, что ищем только пути со "звездочкой" на конце
	nextRecord:
		for _, record := range records { // перебираем все записи с обработчиками
			if catchOnlyAll && (record.params^(1<<15) != 1<<15) {
				continue // игнорируем, если последний параметр не со звездочкой
			}
			var params []Param // сбрасываем предыдущие значения, если они были
		params:
			for i, part := range record.parts { // перебираем все части пути, заданные в обработчике
				// if len(part) > 0 { // это параметр?
				switch part[0] {
				case byte('*'):
					params = append(params, Param{
						Key:   part[1:],
						Value: strings.Join(parts[i:], "/"),
					})
					break params //
				case byte(':'):
					params = append(params, Param{
						Key:   part[1:],
						Value: parts[i],
					})
					continue // переходим к следующему элементу пути
				}
				// }
				if part != parts[i] { // статическая часть пути не совпадает с запрашиваемой
					continue nextRecord // переходим к следующему обработчику
				}
			}
			return record.handle, params // возвращаем обработчик и параметры
		}
	}
	return nil, nil // ничего подходящего не нашли
}

// split нормализует путь и возвращает его в виде частей.
func split(url string) []string {
	return strings.Split(strings.Trim(path.Clean(url), "/"), "/")
}
<commit_msg>simplify<commit_after>package rest

import (
	"errors"
	"fmt"
	"path"
	"sort"
	"strings"
)

// ParamFlag определяет символ, используемый в качестве определения параметра (должен идти
// в самом начале).
const paramFlag byte = ':'

// Param описывает именованный параметр и его значение. В качестве ключа используется имя параметра
// (без символа параметра), а в качестве значения — строка из пути, соответствующая данной позиции.
//
// Я не стал использовать для параметров словарь, т.к. данный метод позволяет сохранить порядок
// следования параметров и использовать параметры с одинаковым именем.
type Param struct {
	Key, Value string
}

// record описывает информацию о пути, в котором есть параметры.
//
// Значение priority задает приоритет для сортировки ключей с одинаковым количеством параметров и
// формируется следующим образом: в старших восьми байтах содержится количество элементов с
// параметрами, а в младших — со статическими путями. Таким образом получается, что элементы
// с меньшим количеством параметров имеют более высокий приоритет и после сортировки будут
// обрабатывается позже, чем элементы с меньшим количеством параметров.
//
// Текущая реализация имеет ограничение на максимальное количество элементов пути — 32767.
// Это связано с методом хранения этого значения в свойстве priority. В принципе, ничего не мешает
// просто увеличить размер priority до uint32 и поправить соответствующие места, где он определен,
// но мне показалось, что для моих задач этого более, чем достаточно.
type record struct {
	params uint16      // количество параметров в пути
	handle interface{} // обработчик запроса
	parts  []string    // путь, разобранный на составные части
}

// records описывает список путей с параметрами и поддерживает сортировку по флагу приоритета.
type records []*record

// поддержка методов для сортировки.
func (n records) Len() int           { return len(n) }
func (n records) Swap(i, j int)      { n[i], n[j] = n[j], n[i] }
func (n records) Less(i, j int) bool { return n[i].params < n[j].params }

// router описывает структуру для быстрого выбора обработчиков по пути запроса.
// Поддерживает как статические пути, так и пути с параметрами.
//
// Текущая реализация не привязана к конкретным типам обработчиков и может хранить любые
// объекты в качестве таких обработчиков.
type router struct {
	// хранилище статических путей, без параметров
	// в качестве ключа используется полный путь
	static map[string]interface{}
	// хранит информацию о путях с параметрами
	// в качестве ключа используется общее количество элементов пути
	fields   map[uint16]records
	maxParts uint16 // максимальное количество частей пути в определениях
}

// add добавляет описание нового пути запроса и ассоциирует его с указанным обработчиком запроса.
// Возвращает ошибку, если количество частей пути больше 32767. В качестве флага для определения
// именованных параметров используется символ ':' и '*' для завершающего параметра, который
// "забирает" в себя весь оставшийся путь.
func (r *router) add(url string, handle interface{}) error {
	parts := split(url) // нормализуем путь и разбиваем его на части
	// проверяем, что количество получившихся частей не превышает поддерживаемое количество.
	length := len(parts)
	if length > (1<<15 - 1) {
		return fmt.Errorf("path parts overflow: %d", len(parts))
	}
	var dynamic uint16 // считаем количество параметров
	for i, value := range parts {
		// if len(value) > 0 { после нормализации пути не должно быть пустых элементов
		switch value[0] {
		case byte('*'):
			if i != length-1 {
				return errors.New("catch-all parameter must be last")
			}
			dynamic |= 1 << 15 // взводим флаг *-параметра
			fallthrough
		case byte(':'):
			dynamic++ // увеличиваем счетчик параметров
		}
		// }
	}
	if dynamic == 0 { // в пути нет параметров — добавляем в статические обработчики
		if r.static == nil {
			// инициализируем статику, если не сделали этого раньше
			r.static = make(map[string]interface{})
		}
		r.static[strings.Join(parts, "/")] = handle
		return nil
	}
	level := uint16(length) // всего элементов пути
	if r.maxParts < level { // запоминаем максимальное количество определенных параметров
		r.maxParts = level
	}
	if r.fields == nil {
		// инициализируем динамические пути, если не сделали этого раньше
		r.fields = make(map[uint16]records)
	}
	// в пути есть динамические параметры — добавляем в список с параметрами
	record := &record{
		params: dynamic,
		handle: handle, // обработчик запроса
		parts:  parts,  // части пути
	}
	// сохраняем в массиве обработчиков с таким же количеством параметров
	r.fields[level] = append(r.fields[level], record)
	sort.Stable(r.fields[level]) // сортируем по количеству параметров
	return nil
}

// lookup возвращает обработчик и список именованных параметров с их значениям. Символ параметра
// из имени при этом изымается. Если подходящего обработчика не найдено, то возвращается nil.
func (r *router) lookup(url string) (interface{}, []Param) {
	parts := split(url) // нормализуем путь и разбиваем его на части
	// сначала ищем среди статических путей
	if r.static != nil { // если статические пути не определены, то пропускаем проверку
		if handle, ok := r.static[strings.Join(parts, "/")]; ok {
			return handle, nil
		}
	}
	if r.fields == nil { // если пути с параметрами не определены, то пропускаем проверку
		return nil, nil
	}
	length := uint16(len(parts))
	var total uint16
	if length > r.maxParts {
		total = r.maxParts
	} else {
		total = length
	}
	// запрашиваем список обработчиков для такого же количества элементов пути
	for l := total; l > 0; l-- {
		records := r.fields[l]
		if len(records) == 0 {
			continue // обработчики для такого пути не зарегистрированы
		}
	nextRecord:
		for _, record := range records { // перебираем все записи с обработчиками
			if l < length && record.params>>15 != 1 {
				continue // игнорируем, если последний параметр не со звездочкой
			}
			var params []Param // сбрасываем предыдущие значения, если они были
		params:
			for i, part := range record.parts { // перебираем все части пути, заданные в обработчике
				// if len(part) > 0 { // это параметр?
				switch part[0] {
				case byte('*'):
					params = append(params, Param{
						Key:   part[1:],
						Value: strings.Join(parts[i:], "/"),
					})
					break params //
				case byte(':'):
					params = append(params, Param{
						Key:   part[1:],
						Value: parts[i],
					})
					continue // переходим к следующему элементу пути
				}
				// }
				if part != parts[i] { // статическая часть пути не совпадает с запрашиваемой
					continue nextRecord // переходим к следующему обработчику
				}
			}
			return record.handle, params // возвращаем обработчик и параметры
		}
	}
	return nil, nil // ничего подходящего не нашли
}

// split нормализует путь и возвращает его в виде частей.
func split(url string) []string {
	return strings.Split(strings.Trim(path.Clean(url), "/"), "/")
}
<|endoftext|>


<commit_before>package water

import (
	"fmt"
	"net/http"
	"strings"
	"sync"
)

var (
	_HTTP_METHODS = map[string]int{
		http.MethodGet:     0,
		http.MethodPost:    1,
		http.MethodDelete:  2,
		http.MethodPut:     3,
		http.MethodPatch:   4,
		http.MethodHead:    5,
		http.MethodOptions: 6,
	}
	_HTTP_METHODS_NAMES = []string{
		http.MethodGet,
		http.MethodPost,
		http.MethodDelete,
		http.MethodPut,
		http.MethodPatch,
		http.MethodHead,
		http.MethodOptions,
	}
)

func MethodIndex(method string) int {
	switch method {
	case http.MethodGet:
		return 0
	case http.MethodPost:
		return 1
	case http.MethodDelete:
		return 2
	case http.MethodPut:
		return 3
	case http.MethodPatch:
		return 4
	case http.MethodHead:
		return 5
	case http.MethodOptions:
		return 6
	default:
		return -1
	}
}

// --- route ---

type route struct {
	method     string
	uri        string // raw uri
	variantUri string // variant uri, httprouter route compatible
	handlers   []Handler
}

// routeStore represents a thread-safe store for route uri.
// to check double route uri and to print route uri
type routeStore struct {
	routeMap   map[string]map[string]*route // [http_method][uri]route
	routeSlice []*route

	lock sync.Mutex
}

func newRouteStore() *routeStore {
	rs := &routeStore{
		routeMap:   make(map[string]map[string]*route),
		routeSlice: make([]*route, 0),
	}

	for m := range _HTTP_METHODS {
		rs.routeMap[m] = make(map[string]*route)
	}

	return rs
}

func (rs *routeStore) add(r *route) {
	rs.lock.Lock()
	defer rs.lock.Unlock()

	if rs.routeMap[r.method][r.uri] != nil {
		panic(fmt.Sprintf("double uri : %s[%s]", r.method, r.uri))
	}

	rs.routeMap[r.method][r.uri] = r
	rs.routeSlice = append(rs.routeSlice, r)
}

// --- router ---

// multiway tree
type Router struct {
	method  string // only in router leaf
	pattern string

	befores  []interface{}
	handlers []interface{} // only in router leaf

	parent *Router
	sub    []*Router
}

func NewRouter() *Router {
	return &Router{}
}

func (r *Router) Group(pattern string, fn func(*Router)) {
	rr := &Router{
		pattern: pattern,
		parent:  r,
	}

	r.sub = append(r.sub, rr)

	fn(rr)
}

func (r *Router) Use(handlers ...interface{}) {
	r.befores = append(r.befores, handlers...)
}

func (r *Router) handle(method, pattern string, handlers []interface{}) {
	for _, v := range handlers {
		if v == nil {
			panic(fmt.Sprintf("handler err : find nil in handlers(%s,%s)", method, pattern))
		}
	}

	rr := &Router{
		method:   method,
		pattern:  pattern,
		parent:   r,
		handlers: handlers,
	}

	r.sub = append(r.sub, rr)
}

var (
	MethodAnyExclude = []string{http.MethodHead, http.MethodOptions}
)

func (r *Router) ANY(pattern string, handlers ...interface{}) {
Skip:
	for _, method := range _HTTP_METHODS_NAMES {
		for _, v := range MethodAnyExclude {
			if method == v {
				continue Skip
			}
		}
		r.handle(method, pattern, handlers)
	}
}

func (r *Router) GET(pattern string, handlers ...interface{}) {
	r.handle(http.MethodGet, pattern, handlers)
}

func (r *Router) POST(pattern string, handlers ...interface{}) {
	r.handle(http.MethodPost, pattern, handlers)
}

func (r *Router) PUT(pattern string, handlers ...interface{}) {
	r.handle(http.MethodPut, pattern, handlers)
}

func (r *Router) PATCH(pattern string, handlers ...interface{}) {
	r.handle(http.MethodPatch, pattern, handlers)
}

func (r *Router) DELETE(pattern string, handlers ...interface{}) {
	r.handle(http.MethodDelete, pattern, handlers)
}

func (r *Router) OPTIONS(pattern string, handlers ...interface{}) {
	r.handle(http.MethodOptions, pattern, handlers)
}

func (r *Router) HEAD(pattern string, handlers ...interface{}) {
	r.handle(http.MethodHead, pattern, handlers)
}

// add all route to routeStore
func dumpRoute(r *Router, rs *routeStore) {
	if r.sub == nil {
		rs.add(getRoute(r))
		return
	}

	for _, v := range r.sub {
		dumpRoute(v, rs)
	}
}

func getRoute(r *Router) *route {
	ps := []string{}
	hs := []interface{}{}

	tmp := r
	for {
		ps = append(ps, strings.TrimSpace(tmp.pattern))

		if len(tmp.handlers) > 0 {
			hs = append(hs, tmp.handlers...)
		}
		if len(tmp.befores) > 0 {
			hstmp := make([]interface{}, len(tmp.befores))

			copy(hstmp, tmp.befores)
			hstmp = append(hstmp, hs...)
			hs = hstmp
		}

		if tmp.parent == nil {
			break
		}

		tmp = tmp.parent
	}

	re := &route{
		method:   r.method,
		uri:      strings.Join(reverseStrings(ps), ""),
		handlers: newHandlers(hs),
	}

	if len(re.handlers) == 0 {
		panic(fmt.Sprintf("handler err : empty handlers in route(%s,%s)", re.method, re.uri))
	}

	return re
}

// to generate router tree.
// r is root router.
func (r *Router) Handler() *Engine {
	if r.parent != nil {
		panic("sub router not allowed: Handler()")
	}

	rs := newRouteStore()

	dumpRoute(r, rs)

	// check uri
	for _, v := range rs.routeSlice {
		if !(v.uri == "/" || checkSplitPattern(v.uri)) {
			panic(fmt.Sprintf("invalid route : [%s : %s]", v.method, v.uri))
		}

		v.variantUri = _VariantUri(v.uri)
		fmt.Println(v.variantUri)
	}

	w := newWater()
	w.rootRouter = r
	w.routeStore = rs
	w.buildTree()

	return w
}

func _VariantUri(raw string) string {
	if !strings.Contains(raw, "/:") && !strings.Contains(raw, "/*") {
		return raw
	}

	ls := strings.Split(raw, "/")

	for i, v := range ls {
		if index := strings.Index(v, ":"); index != -1 {
			ls[i] = "<" + strings.TrimSpace(v[index+1:]) + ">"
		}
		if index := strings.Index(v, "*"); index != -1 {
			ls[i] = "*"
		}
	}

	return strings.Join(ls, "/")
}

func (r *Router) Classic() {
	if r.parent != nil {
		panic("sub router not allowed : Classic()")
	}

	r.Use(Logger())
	r.Use(Recovery())
}
<commit_msg>f 不使用strings.Index(v,*) 避免正则uri<commit_after>package water

import (
	"fmt"
	"net/http"
	"strings"
	"sync"
)

var (
	_HTTP_METHODS = map[string]int{
		http.MethodGet:     0,
		http.MethodPost:    1,
		http.MethodDelete:  2,
		http.MethodPut:     3,
		http.MethodPatch:   4,
		http.MethodHead:    5,
		http.MethodOptions: 6,
	}
	_HTTP_METHODS_NAMES = []string{
		http.MethodGet,
		http.MethodPost,
		http.MethodDelete,
		http.MethodPut,
		http.MethodPatch,
		http.MethodHead,
		http.MethodOptions,
	}
)

func MethodIndex(method string) int {
	switch method {
	case http.MethodGet:
		return 0
	case http.MethodPost:
		return 1
	case http.MethodDelete:
		return 2
	case http.MethodPut:
		return 3
	case http.MethodPatch:
		return 4
	case http.MethodHead:
		return 5
	case http.MethodOptions:
		return 6
	default:
		return -1
	}
}

// --- route ---

type route struct {
	method     string
	uri        string // raw uri
	variantUri string // variant uri, httprouter route compatible
	handlers   []Handler
}

// routeStore represents a thread-safe store for route uri.
// to check double route uri and to print route uri
type routeStore struct {
	routeMap   map[string]map[string]*route // [http_method][uri]route
	routeSlice []*route

	lock sync.Mutex
}

func newRouteStore() *routeStore {
	rs := &routeStore{
		routeMap:   make(map[string]map[string]*route),
		routeSlice: make([]*route, 0),
	}

	for m := range _HTTP_METHODS {
		rs.routeMap[m] = make(map[string]*route)
	}

	return rs
}

func (rs *routeStore) add(r *route) {
	rs.lock.Lock()
	defer rs.lock.Unlock()

	if rs.routeMap[r.method][r.uri] != nil {
		panic(fmt.Sprintf("double uri : %s[%s]", r.method, r.uri))
	}

	rs.routeMap[r.method][r.uri] = r
	rs.routeSlice = append(rs.routeSlice, r)
}

// --- router ---

// multiway tree
type Router struct {
	method  string // only in router leaf
	pattern string

	befores  []interface{}
	handlers []interface{} // only in router leaf

	parent *Router
	sub    []*Router
}

func NewRouter() *Router {
	return &Router{}
}

func (r *Router) Group(pattern string, fn func(*Router)) {
	rr := &Router{
		pattern: pattern,
		parent:  r,
	}

	r.sub = append(r.sub, rr)

	fn(rr)
}

func (r *Router) Use(handlers ...interface{}) {
	r.befores = append(r.befores, handlers...)
}

func (r *Router) handle(method, pattern string, handlers []interface{}) {
	for _, v := range handlers {
		if v == nil {
			panic(fmt.Sprintf("handler err : find nil in handlers(%s,%s)", method, pattern))
		}
	}

	rr := &Router{
		method:   method,
		pattern:  pattern,
		parent:   r,
		handlers: handlers,
	}

	r.sub = append(r.sub, rr)
}

var (
	MethodAnyExclude = []string{http.MethodHead, http.MethodOptions}
)

func (r *Router) ANY(pattern string, handlers ...interface{}) {
Skip:
	for _, method := range _HTTP_METHODS_NAMES {
		for _, v := range MethodAnyExclude {
			if method == v {
				continue Skip
			}
		}
		r.handle(method, pattern, handlers)
	}
}

func (r *Router) GET(pattern string, handlers ...interface{}) {
	r.handle(http.MethodGet, pattern, handlers)
}

func (r *Router) POST(pattern string, handlers ...interface{}) {
	r.handle(http.MethodPost, pattern, handlers)
}

func (r *Router) PUT(pattern string, handlers ...interface{}) {
	r.handle(http.MethodPut, pattern, handlers)
}

func (r *Router) PATCH(pattern string, handlers ...interface{}) {
	r.handle(http.MethodPatch, pattern, handlers)
}

func (r *Router) DELETE(pattern string, handlers ...interface{}) {
	r.handle(http.MethodDelete, pattern, handlers)
}

func (r *Router) OPTIONS(pattern string, handlers ...interface{}) {
	r.handle(http.MethodOptions, pattern, handlers)
}

func (r *Router) HEAD(pattern string, handlers ...interface{}) {
	r.handle(http.MethodHead, pattern, handlers)
}

// add all route to routeStore
func dumpRoute(r *Router, rs *routeStore) {
	if r.sub == nil {
		rs.add(getRoute(r))
		return
	}

	for _, v := range r.sub {
		dumpRoute(v, rs)
	}
}

func getRoute(r *Router) *route {
	ps := []string{}
	hs := []interface{}{}

	tmp := r
	for {
		ps = append(ps, strings.TrimSpace(tmp.pattern))

		if len(tmp.handlers) > 0 {
			hs = append(hs, tmp.handlers...)
		}
		if len(tmp.befores) > 0 {
			hstmp := make([]interface{}, len(tmp.befores))

			copy(hstmp, tmp.befores)
			hstmp = append(hstmp, hs...)
			hs = hstmp
		}

		if tmp.parent == nil {
			break
		}

		tmp = tmp.parent
	}

	re := &route{
		method:   r.method,
		uri:      strings.Join(reverseStrings(ps), ""),
		handlers: newHandlers(hs),
	}

	if len(re.handlers) == 0 {
		panic(fmt.Sprintf("handler err : empty handlers in route(%s,%s)", re.method, re.uri))
	}

	return re
}

// to generate router tree.
// r is root router.
func (r *Router) Handler() *Engine {
	if r.parent != nil {
		panic("sub router not allowed: Handler()")
	}

	rs := newRouteStore()

	dumpRoute(r, rs)

	// check uri
	for _, v := range rs.routeSlice {
		if !(v.uri == "/" || checkSplitPattern(v.uri)) {
			panic(fmt.Sprintf("invalid route : [%s : %s]", v.method, v.uri))
		}

		v.variantUri = _VariantUri(v.uri)
		fmt.Println(v.variantUri)
	}

	w := newWater()
	w.rootRouter = r
	w.routeStore = rs
	w.buildTree()

	return w
}

func _VariantUri(raw string) string {
	if !strings.Contains(raw, "/:") && !strings.Contains(raw, "/*") {
		return raw
	}

	ls := strings.Split(raw, "/")

	for i, v := range ls {
		if strings.HasPrefix(v, ":") {
			ls[i] = "<" + strings.TrimSpace(v[1:]) + ">"
		}
		if strings.HasPrefix(v, "*") {
			ls[i] = "*"
		}
	}

	return strings.Join(ls, "/")
}

func (r *Router) Classic() {
	if r.parent != nil {
		panic("sub router not allowed : Classic()")
	}

	r.Use(Logger())
	r.Use(Recovery())
}
<|endoftext|>


<commit_before>package streambot

import(
	"errors"
	"fmt"
)
import rexster "github.com/sqs/go-rexster-client"

type Database interface {
	SaveChannel(ch *Channel) (err error)
	GetChannelWithUid(uid string) (err error, ch *Channel)
	SaveChannelSubscription(fromChannelId string, toChannelId string, creationTime int64) (err error)
	GetSubscriptionsForChannelWithUid(uid string) (err error, chs []Channel)
}

/* At time of development there is a specialy to consider about the rexster backend server. As
 * rexster runs within the Titan+Cassandra server distribution there is limitation of it using
 * TitanGraphConfiguration that doesn't support manual indices and setting of vertex or edge IDs.
 * Titan creates those IDs and any delivered with creation request is ignored.
 * To keep a unique identifier index on vertices another property named `uid` is supposed to
 * capture that ID and persist in Titan-Cassandra. */

type GraphDatabase struct {
	Graph rexster.Graph
}

func NewGraphDatabase(graph_name string, host string, port uint16) (db *GraphDatabase) {
	var r = rexster.Rexster{host, port, false}
	var g = rexster.Graph{graph_name, r}
	db = &GraphDatabase{g}
	return
}

func (db *GraphDatabase) SaveChannel(ch *Channel) (err error) {
	// Create a vertex in the graph database for the channel
	var properties = map[string]interface{}{"name": ch.Name, "uid": ch.Id}
	vertex := rexster.NewVertex("", properties)
	_, err = db.Graph.CreateOrUpdateVertex(vertex)
	return
}

func (db *GraphDatabase) GetChannelWithUid(uid string) (err error, ch *Channel) {
	res, err := db.Graph.QueryVertices("uid", uid)
	if err != nil {
		err = errors.New(fmt.Sprintf("Failed to query vertices at Rexster:", err))
		return
	}
	if vs := res.Vertices(); vs != nil {
		if len(vs) > 1 {
			errMsgFormat := "Unexpectedly Rexster backend returned more than one vertex, given `%v`"
			errMsg := fmt.Sprintf(errMsgFormat, vs)
			err = errors.New(errMsg)
		} else {
			vertex := vs[0]
			ch = &Channel{vertex.Map["uid"].(string), vertex.Map["name"].(string)}
		}
	} else {
		errMsgFormat := "Unexpectedly Rexster backend returned no vertex, given `%v`"
		errMsg := fmt.Sprintf(errMsgFormat, res)
		err = errors.New(errMsg)
	}
	return
}

func (db *GraphDatabase) SaveChannelSubscription(
	fromChannelId string,
	toChannelId string,
	creationTime int64,
) (err error) {
	script := fmt.Sprintf(
		"subs=g.V('uid', '%s')" +
        	".out('subscribe').has('id', g.V('uid', '%s').next().id);" +
        	"if(!subs.hasNext()){" +
        	"e=g.addEdge(g.V('uid','%s').next(),g.V('uid','%s').next()," +
        	"'subscribe',[time:%d]);g.commit();e" +
			"}else{g.V('uid', '%s').outE('subscribe')}",
		fromChannelId,
		toChannelId,
		fromChannelId,
		toChannelId,
		creationTime,
		fromChannelId,
	)
	_, err = db.Graph.Eval(script)
	if err != nil {
		err = errors.New(fmt.Sprintf("Failed to query vertices at Rexster:", err))
		return
	}
	return
}

func (db *GraphDatabase) GetSubscriptionsForChannelWithUid(uid string) (err error, chs []Channel) {
	script := fmt.Sprintf("g.V(\"uid\",%s).out.loop(1){it.loops < 100}{true}.dedup", uid)
	res, err := db.Graph.Eval(script)
	if err != nil {
		err = errors.New(fmt.Sprintf("Failed to query subscribed channels at Rexster:", err))
	}
	if vs := res.Vertices(); vs != nil {
		chs = make([]Channel, len(vs))
		for i := range vs {
			vertex := vs[i]
			chs[i] = Channel{vertex.Map["uid"].(string), vertex.Map["name"].(string)}
		}
	} else {
		errMsgFormat := "Unexpectedly Rexster backend returned no vertex for channel " +
		"subscription, given `%v`"
		errMsg := fmt.Sprintf(errMsgFormat, res)
		err = errors.New(errMsg)
	}
	return
}<commit_msg>Fixes errors forced panics in database module<commit_after>package streambot

import(
	"errors"
	"fmt"
)
import rexster "github.com/sqs/go-rexster-client"

type Database interface {
	SaveChannel(ch *Channel) (err error)
	GetChannelWithUid(uid string) (err error, ch *Channel)
	SaveChannelSubscription(fromChannelId string, toChannelId string, creationTime int64) (err error)
	GetSubscriptionsForChannelWithUid(uid string) (err error, chs []Channel)
}

/* At time of development there is a specialy to consider about the rexster backend server. As
 * rexster runs within the Titan+Cassandra server distribution there is limitation of it using
 * TitanGraphConfiguration that doesn't support manual indices and setting of vertex or edge IDs.
 * Titan creates those IDs and any delivered with creation request is ignored.
 * To keep a unique identifier index on vertices another property named `uid` is supposed to
 * capture that ID and persist in Titan-Cassandra. */

type GraphDatabase struct {
	Graph rexster.Graph
}

func NewGraphDatabase(graph_name string, host string, port uint16) (db *GraphDatabase) {
	var r = rexster.Rexster{host, port, false}
	var g = rexster.Graph{graph_name, r}
	db = &GraphDatabase{g}
	return
}

func (db *GraphDatabase) SaveChannel(ch *Channel) (err error) {
	// Create a vertex in the graph database for the channel
	var properties = map[string]interface{}{"name": ch.Name, "uid": ch.Id}
	vertex := rexster.NewVertex("", properties)
	_, err = db.Graph.CreateOrUpdateVertex(vertex)
	return
}

func (db *GraphDatabase) GetChannelWithUid(uid string) (err error, ch *Channel) {
	res, err := db.Graph.QueryVertices("uid", uid)
	if err != nil {
		err = errors.New(fmt.Sprintf("Failed to query vertices at Rexster:", err))
		return
	}
	if vs := res.Vertices(); vs != nil {
		numVertices := len(vs)
		if numVertices > 1 {
			errMsgFormat := "Unexpectedly Rexster backend returned more than one vertex, given `%v`"
			errMsg := fmt.Sprintf(errMsgFormat, vs)
			err = errors.New(errMsg)
		} else if numVertices == 1 {
			vertex := vs[0]
			ch = &Channel{vertex.Map["uid"].(string), vertex.Map["name"].(string)}
		}
	} else {
		errMsgFormat := "Unexpectedly Rexster backend returned no vertex, given `%v`"
		errMsg := fmt.Sprintf(errMsgFormat, res)
		err = errors.New(errMsg)
	}
	return
}

func (db *GraphDatabase) SaveChannelSubscription(
	fromChannelId string,
	toChannelId string,
	creationTime int64,
) (err error) {
	script := fmt.Sprintf(
		"subs=g.V('uid', '%s')" +
        	".out('subscribe').has('id', g.V('uid', '%s').next().id);" +
        	"if(!subs.hasNext()){" +
        	"e=g.addEdge(g.V('uid','%s').next(),g.V('uid','%s').next()," +
        	"'subscribe',[time:%d]);g.commit();e" +
			"}else{g.V('uid', '%s').outE('subscribe')}",
		fromChannelId,
		toChannelId,
		fromChannelId,
		toChannelId,
		creationTime,
		fromChannelId,
	)
	_, err = db.Graph.Eval(script)
	if err != nil {
		err = errors.New(fmt.Sprintf("Failed to query vertices at Rexster:", err))
		return
	}
	return
}

func (db *GraphDatabase) GetSubscriptionsForChannelWithUid(uid string) (err error, chs []Channel) {
	script := fmt.Sprintf("g.V(\"uid\",%s).out.loop(1){it.loops < 100}{true}.dedup", uid)
	res, err := db.Graph.Eval(script)
	if err != nil {
		err = errors.New(fmt.Sprintf("Failed to query subscribed channels at Rexster:", err))
	}
	if vs := res.Vertices(); vs != nil {
		numVertices := len(vs)
		if numVertices > 0 {
			chs = make([]Channel, numVertices)
			for i := range vs {
				vertex := vs[i]
				chs[i] = Channel{vertex.Map["uid"].(string), vertex.Map["name"].(string)}
			}
		}
	} else {
		errMsgFormat := "Unexpectedly Rexster backend returned no vertex for channel " +
		"subscription, given `%v`"
		errMsg := fmt.Sprintf(errMsgFormat, res)
		err = errors.New(errMsg)
	}
	return
}<|endoftext|>


<commit_before>/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.

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

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

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

/*
This example program shows how the `view` and `property` packages can
be used to navigate a vSphere inventory structure using govmomi.
*/

package main

import (
	"context"
	"fmt"
	"log"
	"os"
	"text/tabwriter"

	"github.com/vmware/govmomi/examples"
	"github.com/vmware/govmomi/units"
	"github.com/vmware/govmomi/view"
	"github.com/vmware/govmomi/vim25/mo"
)

func main() {
	ctx := context.Background()

	// Connect and login to ESX or vCenter
	c, err := examples.NewClient(ctx)
	if err != nil {
		log.Fatal(err)
	}

	defer c.Logout(ctx)

	// Create a view of HostSystem objects
	m := view.NewManager(c.Client)

	v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{"HostSystem"}, true)
	if err != nil {
		log.Fatal(err)
	}

	defer v.Destroy(ctx)

	// Retrieve summary property for all hosts
	// Reference: http://pubs.vmware.com/vsphere-60/topic/com.vmware.wssdk.apiref.doc/vim.HostSystem.html
	var hss []mo.HostSystem
	err = v.Retrieve(ctx, []string{"HostSystem"}, []string{"summary"}, &hss)
	if err != nil {
		log.Fatal(err)
	}

	// Print summary per host (see also: govc/host/info.go)

	tw := tabwriter.NewWriter(os.Stdout, 2, 0, 2, ' ', 0)
	fmt.Fprintf(tw, "Name:\tUsed CPU:\tTotal CPU:\tFree CPU:\tUsed Memory:\tTotal Memory:\tFree Memory:\t\n")

	for _, hs := range hss {
		totalCPU := int64(hs.Summary.Hardware.CpuMhz) * int64(hs.Summary.Hardware.NumCpuCores)
		freeCPU := int64(totalCPU) - int64(hs.Summary.QuickStats.OverallCpuUsage)
		freeMemory := int64(hs.Summary.Hardware.MemorySize) - (int64(hs.Summary.QuickStats.OverallMemoryUsage) * 1024 * 1024)
		fmt.Fprintf(tw, "%s\t", hs.Summary.Config.Name)
		fmt.Fprintf(tw, "%d\t", hs.Summary.QuickStats.OverallCpuUsage)
		fmt.Fprintf(tw, "%d\t", totalCPU)
		fmt.Fprintf(tw, "%d\t", freeCPU)
		fmt.Fprintf(tw, "%s\t", units.ByteSize(hs.Summary.QuickStats.OverallMemoryUsage))
		fmt.Fprintf(tw, "%s\t", units.ByteSize(hs.Summary.Hardware.MemorySize))
		fmt.Fprintf(tw, "%d\t", freeMemory)
		fmt.Fprintf(tw, "\n")
	}

	_ = tw.Flush()
}
<commit_msg>example: uniform unit for host memory<commit_after>/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.

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

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

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

/*
This example program shows how the `view` and `property` packages can
be used to navigate a vSphere inventory structure using govmomi.
*/

package main

import (
	"context"
	"fmt"
	"log"
	"os"
	"text/tabwriter"

	"github.com/vmware/govmomi/examples"
	"github.com/vmware/govmomi/units"
	"github.com/vmware/govmomi/view"
	"github.com/vmware/govmomi/vim25/mo"
)

func main() {
	ctx := context.Background()

	// Connect and login to ESX or vCenter
	c, err := examples.NewClient(ctx)
	if err != nil {
		log.Fatal(err)
	}

	defer c.Logout(ctx)

	// Create a view of HostSystem objects
	m := view.NewManager(c.Client)

	v, err := m.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{"HostSystem"}, true)
	if err != nil {
		log.Fatal(err)
	}

	defer v.Destroy(ctx)

	// Retrieve summary property for all hosts
	// Reference: http://pubs.vmware.com/vsphere-60/topic/com.vmware.wssdk.apiref.doc/vim.HostSystem.html
	var hss []mo.HostSystem
	err = v.Retrieve(ctx, []string{"HostSystem"}, []string{"summary"}, &hss)
	if err != nil {
		log.Fatal(err)
	}

	// Print summary per host (see also: govc/host/info.go)

	tw := tabwriter.NewWriter(os.Stdout, 2, 0, 2, ' ', 0)
	fmt.Fprintf(tw, "Name:\tUsed CPU:\tTotal CPU:\tFree CPU:\tUsed Memory:\tTotal Memory:\tFree Memory:\t\n")

	for _, hs := range hss {
		totalCPU := int64(hs.Summary.Hardware.CpuMhz) * int64(hs.Summary.Hardware.NumCpuCores)
		freeCPU := int64(totalCPU) - int64(hs.Summary.QuickStats.OverallCpuUsage)
		freeMemory := int64(hs.Summary.Hardware.MemorySize) - (int64(hs.Summary.QuickStats.OverallMemoryUsage) * 1024 * 1024)
		fmt.Fprintf(tw, "%s\t", hs.Summary.Config.Name)
		fmt.Fprintf(tw, "%d\t", hs.Summary.QuickStats.OverallCpuUsage)
		fmt.Fprintf(tw, "%d\t", totalCPU)
		fmt.Fprintf(tw, "%d\t", freeCPU)
		fmt.Fprintf(tw, "%s\t", (units.ByteSize(hs.Summary.QuickStats.OverallMemoryUsage))*1024*1024)
		fmt.Fprintf(tw, "%s\t", units.ByteSize(hs.Summary.Hardware.MemorySize))
		fmt.Fprintf(tw, "%d\t", freeMemory)
		fmt.Fprintf(tw, "\n")
	}

	_ = tw.Flush()
}
<|endoftext|>


<commit_before>package graphite

import (
	"bytes"
	"fmt"
	"strings"
	"testing"
	"time"

	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/metrics"
	"github.com/go-kit/kit/metrics/teststat"
)

func TestHistogramQuantiles(t *testing.T) {
	prefix := "prefix"
	e := NewEmitter("", "", prefix, time.Second, log.NewNopLogger())
	var (
		name      = "test_histogram_quantiles"
		quantiles = []int{50, 90, 95, 99}
	)
	h, err := e.NewHistogram(name, 0, 100, 3, quantiles...)
	if err != nil {
		t.Fatalf("unable to create test histogram: %v", err)
	}
	h = h.With(metrics.Field{Key: "ignored", Value: "field"})
	const seed, mean, stdev int64 = 424242, 50, 10
	teststat.PopulateNormalHistogram(t, h, seed, mean, stdev)

	// flush the current metrics into a buffer to examine
	var b bytes.Buffer
	e.flush(&b)
	teststat.AssertGraphiteNormalHistogram(t, prefix, name, mean, stdev, quantiles, b.String())
}

func TestCounter(t *testing.T) {
	var (
		prefix = "prefix"
		name   = "m"
		value  = 123
		e      = NewEmitter("", "", prefix, time.Second, log.NewNopLogger())
		b      bytes.Buffer
	)
	e.NewCounter(name).With(metrics.Field{Key: "ignored", Value: "field"}).Add(uint64(value))
	e.flush(&b)
	want := fmt.Sprintf("%s.%s.count %d", prefix, name, value)
	payload := b.String()
	if !strings.HasPrefix(payload, want) {
		t.Errorf("counter %s want\n%s, have\n%s", name, want, payload)
	}
}

func TestGauge(t *testing.T) {
	var (
		prefix = "prefix"
		name   = "xyz"
		value  = 54321
		delta  = 12345
		e      = NewEmitter("", "", prefix, time.Second, log.NewNopLogger())
		b      bytes.Buffer
		g      = e.NewGauge(name).With(metrics.Field{Key: "ignored", Value: "field"})
	)

	g.Set(float64(value))
	g.Add(float64(delta))

	e.flush(&b)
	payload := b.String()

	want := fmt.Sprintf("%s.%s %d", prefix, name, value+delta)
	if !strings.HasPrefix(payload, want) {
		t.Errorf("gauge %s want\n%s, have\n%s", name, want, payload)
	}
}
<commit_msg>TestEmitterStops<commit_after>package graphite

import (
	"bytes"
	"fmt"
	"strings"
	"testing"
	"time"

	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/metrics"
	"github.com/go-kit/kit/metrics/teststat"
)

func TestHistogramQuantiles(t *testing.T) {
	prefix := "prefix"
	e := NewEmitter("", "", prefix, time.Second, log.NewNopLogger())
	var (
		name      = "test_histogram_quantiles"
		quantiles = []int{50, 90, 95, 99}
	)
	h, err := e.NewHistogram(name, 0, 100, 3, quantiles...)
	if err != nil {
		t.Fatalf("unable to create test histogram: %v", err)
	}
	h = h.With(metrics.Field{Key: "ignored", Value: "field"})
	const seed, mean, stdev int64 = 424242, 50, 10
	teststat.PopulateNormalHistogram(t, h, seed, mean, stdev)

	// flush the current metrics into a buffer to examine
	var b bytes.Buffer
	e.flush(&b)
	teststat.AssertGraphiteNormalHistogram(t, prefix, name, mean, stdev, quantiles, b.String())
}

func TestCounter(t *testing.T) {
	var (
		prefix = "prefix"
		name   = "m"
		value  = 123
		e      = NewEmitter("", "", prefix, time.Second, log.NewNopLogger())
		b      bytes.Buffer
	)
	e.NewCounter(name).With(metrics.Field{Key: "ignored", Value: "field"}).Add(uint64(value))
	e.flush(&b)
	want := fmt.Sprintf("%s.%s.count %d", prefix, name, value)
	payload := b.String()
	if !strings.HasPrefix(payload, want) {
		t.Errorf("counter %s want\n%s, have\n%s", name, want, payload)
	}
}

func TestGauge(t *testing.T) {
	var (
		prefix = "prefix"
		name   = "xyz"
		value  = 54321
		delta  = 12345
		e      = NewEmitter("", "", prefix, time.Second, log.NewNopLogger())
		b      bytes.Buffer
		g      = e.NewGauge(name).With(metrics.Field{Key: "ignored", Value: "field"})
	)

	g.Set(float64(value))
	g.Add(float64(delta))

	e.flush(&b)
	payload := b.String()

	want := fmt.Sprintf("%s.%s %d", prefix, name, value+delta)
	if !strings.HasPrefix(payload, want) {
		t.Errorf("gauge %s want\n%s, have\n%s", name, want, payload)
	}
}

func TestEmitterStops(t *testing.T) {
	e := NewEmitter("foo", "bar", "baz", time.Second, log.NewNopLogger())
	time.Sleep(100 * time.Millisecond)
	e.Stop()
}
<|endoftext|>


<commit_before>package main

import (
	"flag"

	"k8s.io/klog/v2"
	"k8s.io/klog/v2/klogr"
)

type myError struct {
	str string
}

func (e myError) Error() string {
	return e.str
}

func main() {
	flag.Set("v", "3")
	flag.Parse()
	log := klogr.New().WithName("MyName").WithValues("user", "you")
	log.Info("hello", "val1", 1, "val2", map[string]int{"k": 1})
	log.V(3).Info("nice to meet you")
	log.Error(nil, "uh oh", "trouble", true, "reasons", []float64{0.1, 0.11, 3.14})
	log.Error(myError{"an error occurred"}, "goodbye", "code", -1)
	klog.Flush()
}
<commit_msg>add klog.InitFlags(nil) to honour flag setting<commit_after>package main

import (
	"flag"

	"k8s.io/klog/v2"
	"k8s.io/klog/v2/klogr"
)

type myError struct {
	str string
}

func (e myError) Error() string {
	return e.str
}

func main() {
	klog.InitFlags(nil)
	flag.Set("v", "3")
	flag.Parse()
	log := klogr.New().WithName("MyName").WithValues("user", "you")
	log.Info("hello", "val1", 1, "val2", map[string]int{"k": 1})
	log.V(3).Info("nice to meet you")
	log.Error(nil, "uh oh", "trouble", true, "reasons", []float64{0.1, 0.11, 3.14})
	log.Error(myError{"an error occurred"}, "goodbye", "code", -1)
	klog.Flush()
}
<|endoftext|>


<commit_before>package model

import (
	"encoding/json"
	"fmt"
	"reflect"
	"sort"
	"strconv"
	"strings"

	"github.com/taskcluster/taskcluster-client-java/codegenerator/utils"
)

type (
	// Note that all members are backed by pointers, so that nil value can signify non-existence.
	// Otherwise we could not differentiate whether a zero value is non-existence or actually the
	// zero value. For example, if a bool is false, we don't know if it was explictly set to false
	// in the json we read, or whether it was not given. Unmarshaling into a pointer means pointer
	// will be nil pointer if it wasn't read, or a pointer to true/false if it was read from json.
	JsonSubSchema struct {
		AdditionalItems      *bool                 `json:"additionalItems"`
		AdditionalProperties *AdditionalProperties `json:"additionalProperties"`
		AllOf                Items                 `json:"allOf"`
		AnyOf                Items                 `json:"anyOf"`
		Default              *interface{}          `json:"default"`
		Description          *string               `json:"description"`
		Enum                 interface{}           `json:"enum"`
		Format               *string               `json:"format"`
		ID                   *string               `json:"id"`
		Items                *JsonSubSchema        `json:"items"`
		Maximum              *int                  `json:"maximum"`
		MaxLength            *int                  `json:"maxLength"`
		Minimum              *int                  `json:"minimum"`
		MinLength            *int                  `json:"minLength"`
		OneOf                Items                 `json:"oneOf"`
		Pattern              *string               `json:"pattern"`
		Properties           *Properties           `json:"properties"`
		Ref                  *string               `json:"$ref"`
		Required             []string              `json:"required"`
		Schema               *string               `json:"$schema"`
		Title                *string               `json:"title"`
		Type                 *string               `json:"type"`

		// non-json fields used for sorting/tracking
		TypeName       string
		IsInputSchema  bool
		IsOutputSchema bool
		SourceURL      string
		RefSubSchema   *JsonSubSchema
		APIDefinition  *APIDefinition
	}

	Items []JsonSubSchema

	Properties struct {
		Properties          map[string]*JsonSubSchema
		SortedPropertyNames []string
	}

	AdditionalProperties struct {
		Boolean    *bool
		Properties *JsonSubSchema
	}
)

func (subSchema JsonSubSchema) String() string {
	result := ""
	result += describe("Additional Items", subSchema.AdditionalItems)
	result += describe("Additional Properties", subSchema.AdditionalProperties)
	result += describe("All Of", subSchema.AllOf)
	result += describe("Any Of", subSchema.AnyOf)
	result += describe("Default", subSchema.Default)
	result += describe("Description", subSchema.Description)
	result += describe("Enum", subSchema.Enum)
	result += describe("Format", subSchema.Format)
	result += describe("ID", subSchema.ID)
	result += describeList("Items", subSchema.Items)
	result += describe("Maximum", subSchema.Maximum)
	result += describe("MaxLength", subSchema.MaxLength)
	result += describe("Minimum", subSchema.Minimum)
	result += describe("MinLength", subSchema.MinLength)
	result += describeList("OneOf", subSchema.OneOf)
	result += describe("Pattern", subSchema.Pattern)
	result += describeList("Properties", subSchema.Properties)
	result += describe("Ref", subSchema.Ref)
	result += describe("Required", subSchema.Required)
	result += describe("Schema", subSchema.Schema)
	result += describe("Title", subSchema.Title)
	result += describe("Type", subSchema.Type)
	result += describe("TypeName", &subSchema.TypeName)
	result += describe("IsInputSchema", &subSchema.IsInputSchema)
	result += describe("IsOutputSchema", &subSchema.IsOutputSchema)
	result += describe("SourceURL", &subSchema.SourceURL)
	return result
}

func (jsonSubSchema *JsonSubSchema) TypeDefinition(level int, fromArray bool, extraPackages map[string]bool) (string, map[string]bool, string) {
	content := ""
	if level == 0 && !fromArray {
		content += "/**\n"
		if d := jsonSubSchema.Description; d != nil {
			if desc := *d; desc != "" {
				content += utils.Indent(desc, "* ")
			}
			if content[len(content)-1:] != "\n" {
				content += "\n"
			}
		}
		if url := jsonSubSchema.SourceURL; url != "" {
			content += "*\n* See " + url + "\n"
		}
		content += "*/\n"
	}
	typ := "Object"
	if p := jsonSubSchema.Type; p != nil {
		typ = *p
	}
	if p := jsonSubSchema.RefSubSchema; p != nil {
		typ = p.TypeName
	}
	switch typ {
	case "array":
		if jsonType := jsonSubSchema.Items.Type; jsonType != nil {
			var newType string
			newType, extraPackages, typ = jsonSubSchema.Items.TypeDefinition(level, true, extraPackages)
			if level == 0 {
				if typ == "" {
					content += newType
				}
			} else {
				typ = ""
				content += newType + "[]"
			}
		} else {
			if refSubSchema := jsonSubSchema.Items.RefSubSchema; refSubSchema != nil {
				typ = refSubSchema.TypeName
			}
		}

	case "object":
		if s := jsonSubSchema.Properties; s != nil {
			typ = "" // strings.Title(jsonSubSchema.TypeName)
			def := fmt.Sprintf("class " + strings.Title(jsonSubSchema.TypeName) + " {\n")
			for _, j := range s.SortedPropertyNames {
				// recursive call to build go types inside structs
				var subType string
				subType, extraPackages, _ = s.Properties[j].TypeDefinition(level+1, false, extraPackages)
				// comment the struct member with the description from the json
				if d := s.Properties[j].Description; d != nil {
					def += "\n" + utils.Comment(*d, strings.Repeat("    ", level+1))
				}
				// struct member name and type, as part of struct definition
				def += fmt.Sprintf(strings.Repeat("    ", level+1)+"public %v %v;\n", subType, s.Properties[j].TypeName)
			}
			def += strings.Repeat("    ", level) + "}"
			if level == 0 {
				def = "public " + def
			} else {
				def += "\n\n" + strings.Repeat("    ", level) + "public " + strings.Title(jsonSubSchema.TypeName)
			}
			content += def
		} else {
			typ = "Object"
		}
	case "number":
		typ = "int"
	case "integer":
		typ = "int"
	case "boolean":
		typ = "boolean"
	// json type string maps to go type string, so only need to test case of when
	// string is a json date-time, so we can convert to go type time.Time...
	case "string":
		if f := jsonSubSchema.Format; f != nil && *f == "date-time" {
			typ = "Date"
		} else {
			typ = "String"
		}
	}
	switch typ {
	case "Date":
		extraPackages["java.util.Date"] = true
	}
	content += typ
	// horrible hack until I have fixed the bug properly
	if content == "HookSchedule1" {
		content = "String[]"
		typ = ""
	}
	return content, extraPackages, typ
}

func (p Properties) String() string {
	result := ""
	for _, i := range p.SortedPropertyNames {
		result += "Property '" + i + "' =\n" + utils.Indent(p.Properties[i].String(), "  ")
	}
	return result
}

func (p *Properties) postPopulate(apiDef *APIDefinition) {
	// now all data should be loaded, let's sort the p.Properties
	if p.Properties != nil {
		p.SortedPropertyNames = make([]string, 0, len(p.Properties))
		for propertyName := range p.Properties {
			p.SortedPropertyNames = append(p.SortedPropertyNames, propertyName)
		}
		sort.Strings(p.SortedPropertyNames)
		members := make(map[string]bool, len(p.SortedPropertyNames))
		for _, j := range p.SortedPropertyNames {
			p.Properties[j].TypeName = utils.NormaliseLower(j, members)
			// subschemas also need to be triggered to postPopulate...
			p.Properties[j].postPopulate(apiDef)
		}
	}
}

func (p *Properties) UnmarshalJSON(bytes []byte) (err error) {
	errX := json.Unmarshal(bytes, &p.Properties)
	return errX
}

func (aP *AdditionalProperties) UnmarshalJSON(bytes []byte) (err error) {
	b, p := new(bool), new(JsonSubSchema)
	if err = json.Unmarshal(bytes, b); err == nil {
		aP.Boolean = b
		return
	}
	if err = json.Unmarshal(bytes, p); err == nil {
		aP.Properties = p
	}
	return
}

func (aP AdditionalProperties) String() string {
	if aP.Boolean != nil {
		return strconv.FormatBool(*aP.Boolean)
	}
	return aP.Properties.String()
}

func (items Items) String() string {
	result := ""
	for i, j := range items {
		result += fmt.Sprintf("Item '%v' =\n", i) + utils.Indent(j.String(), "  ")
	}
	return result
}

func (items Items) postPopulate(apiDef *APIDefinition) {
	for i := range items {
		items[i].postPopulate(apiDef)
	}
}

func describeList(name string, value interface{}) string {
	if reflect.ValueOf(value).IsValid() {
		if !reflect.ValueOf(value).IsNil() {
			return fmt.Sprintf("%v\n", name) + utils.Indent(fmt.Sprintf("%v", reflect.Indirect(reflect.ValueOf(value)).Interface()), "  ")
		}
	}
	return ""
}

// If item is not null, then return a description of it. If it is a pointer, dereference it first.
func describe(name string, value interface{}) string {
	if reflect.ValueOf(value).IsValid() {
		if !reflect.ValueOf(value).IsNil() {
			return fmt.Sprintf("%-22v = '%v'\n", name, reflect.Indirect(reflect.ValueOf(value)).Interface())
		}
	}
	return ""
}

type CanPopulate interface {
	postPopulate(*APIDefinition)
}

func postPopulateIfNotNil(canPopulate CanPopulate, apiDef *APIDefinition) {
	if reflect.ValueOf(canPopulate).IsValid() {
		if !reflect.ValueOf(canPopulate).IsNil() {
			canPopulate.postPopulate(apiDef)
		}
	}
}

func (subSchema *JsonSubSchema) postPopulate(apiDef *APIDefinition) {
	if subSchema.TypeName == "" {
		members := make(map[string]bool, 1)
		switch {
		case subSchema.Title != nil && *subSchema.Title != "" && len(*subSchema.Title) < 40:
			subSchema.TypeName = utils.NormaliseLower(*subSchema.Title, members)
		case subSchema.Description != nil && *subSchema.Description != "" && len(*subSchema.Description) < 40:
			subSchema.TypeName = utils.NormaliseLower(*subSchema.Description, members)
		case subSchema.RefSubSchema != nil && subSchema.RefSubSchema.TypeName != "":
			subSchema.TypeName = subSchema.RefSubSchema.TypeName
		default:
			subSchema.TypeName = "X"
		}
	}
	// Arrays should get their name from their parent subschema. Note we set
	// this before calling postPopulate on subSchema.Items, to make sure we get
	// there first! If already set, it won't get updated later.
	if subSchema.Items != nil {
		subSchema.Items.TypeName = subSchema.TypeName
	}
	postPopulateIfNotNil(subSchema.AllOf, apiDef)
	postPopulateIfNotNil(subSchema.AnyOf, apiDef)
	postPopulateIfNotNil(subSchema.OneOf, apiDef)
	postPopulateIfNotNil(subSchema.Items, apiDef)
	postPopulateIfNotNil(subSchema.Properties, apiDef)
	// If we have a $ref pointing to another schema, keep a reference so we can
	// discover TypeName later when we generate the type definition
	subSchema.RefSubSchema = apiDef.cacheJsonSchema(subSchema.Ref)
}
<commit_msg>Bug fix ... oh that was nasty<commit_after>package model

import (
	"encoding/json"
	"fmt"
	"reflect"
	"sort"
	"strconv"
	"strings"

	"github.com/taskcluster/taskcluster-client-java/codegenerator/utils"
)

type (
	// Note that all members are backed by pointers, so that nil value can signify non-existence.
	// Otherwise we could not differentiate whether a zero value is non-existence or actually the
	// zero value. For example, if a bool is false, we don't know if it was explictly set to false
	// in the json we read, or whether it was not given. Unmarshaling into a pointer means pointer
	// will be nil pointer if it wasn't read, or a pointer to true/false if it was read from json.
	JsonSubSchema struct {
		AdditionalItems      *bool                 `json:"additionalItems"`
		AdditionalProperties *AdditionalProperties `json:"additionalProperties"`
		AllOf                Items                 `json:"allOf"`
		AnyOf                Items                 `json:"anyOf"`
		Default              *interface{}          `json:"default"`
		Description          *string               `json:"description"`
		Enum                 interface{}           `json:"enum"`
		Format               *string               `json:"format"`
		ID                   *string               `json:"id"`
		Items                *JsonSubSchema        `json:"items"`
		Maximum              *int                  `json:"maximum"`
		MaxLength            *int                  `json:"maxLength"`
		Minimum              *int                  `json:"minimum"`
		MinLength            *int                  `json:"minLength"`
		OneOf                Items                 `json:"oneOf"`
		Pattern              *string               `json:"pattern"`
		Properties           *Properties           `json:"properties"`
		Ref                  *string               `json:"$ref"`
		Required             []string              `json:"required"`
		Schema               *string               `json:"$schema"`
		Title                *string               `json:"title"`
		Type                 *string               `json:"type"`

		// non-json fields used for sorting/tracking
		TypeName       string
		IsInputSchema  bool
		IsOutputSchema bool
		SourceURL      string
		RefSubSchema   *JsonSubSchema
		APIDefinition  *APIDefinition
	}

	Items []JsonSubSchema

	Properties struct {
		Properties          map[string]*JsonSubSchema
		SortedPropertyNames []string
	}

	AdditionalProperties struct {
		Boolean    *bool
		Properties *JsonSubSchema
	}
)

func (subSchema JsonSubSchema) String() string {
	result := ""
	result += describe("Additional Items", subSchema.AdditionalItems)
	result += describe("Additional Properties", subSchema.AdditionalProperties)
	result += describe("All Of", subSchema.AllOf)
	result += describe("Any Of", subSchema.AnyOf)
	result += describe("Default", subSchema.Default)
	result += describe("Description", subSchema.Description)
	result += describe("Enum", subSchema.Enum)
	result += describe("Format", subSchema.Format)
	result += describe("ID", subSchema.ID)
	result += describeList("Items", subSchema.Items)
	result += describe("Maximum", subSchema.Maximum)
	result += describe("MaxLength", subSchema.MaxLength)
	result += describe("Minimum", subSchema.Minimum)
	result += describe("MinLength", subSchema.MinLength)
	result += describeList("OneOf", subSchema.OneOf)
	result += describe("Pattern", subSchema.Pattern)
	result += describeList("Properties", subSchema.Properties)
	result += describe("Ref", subSchema.Ref)
	result += describe("Required", subSchema.Required)
	result += describe("Schema", subSchema.Schema)
	result += describe("Title", subSchema.Title)
	result += describe("Type", subSchema.Type)
	result += describe("TypeName", &subSchema.TypeName)
	result += describe("IsInputSchema", &subSchema.IsInputSchema)
	result += describe("IsOutputSchema", &subSchema.IsOutputSchema)
	result += describe("SourceURL", &subSchema.SourceURL)
	return result
}

func (jsonSubSchema *JsonSubSchema) TypeDefinition(level int, fromArray bool, extraPackages map[string]bool) (string, map[string]bool, string) {
	content := ""
	if level == 0 && !fromArray {
		content += "/**\n"
		if d := jsonSubSchema.Description; d != nil {
			if desc := *d; desc != "" {
				content += utils.Indent(desc, "* ")
			}
			if content[len(content)-1:] != "\n" {
				content += "\n"
			}
		}
		if url := jsonSubSchema.SourceURL; url != "" {
			content += "*\n* See " + url + "\n"
		}
		content += "*/\n"
	}
	typ := "Object"
	if p := jsonSubSchema.Type; p != nil {
		typ = *p
	}
	if p := jsonSubSchema.RefSubSchema; p != nil {
		_, _, possSimpleType := p.TypeDefinition(1, true, make(map[string]bool))
		switch possSimpleType {
		case "":
			typ = p.TypeName
		default:
			typ = possSimpleType
		}
	}
	switch typ {
	case "array":
		if jsonType := jsonSubSchema.Items.Type; jsonType != nil {
			var newType string
			newType, extraPackages, typ = jsonSubSchema.Items.TypeDefinition(level, true, extraPackages)
			if level == 0 {
				if typ == "" {
					content += newType
				}
			} else {
				typ = newType + "[]"
			}
		} else {
			if refSubSchema := jsonSubSchema.Items.RefSubSchema; refSubSchema != nil {
				typ = refSubSchema.TypeName
			}
		}

	case "object":
		if s := jsonSubSchema.Properties; s != nil {
			typ = "" // strings.Title(jsonSubSchema.TypeName)
			def := fmt.Sprintf("class " + strings.Title(jsonSubSchema.TypeName) + " {\n")
			for _, j := range s.SortedPropertyNames {
				// recursive call to build go types inside structs
				var subType string
				subType, extraPackages, _ = s.Properties[j].TypeDefinition(level+1, false, extraPackages)
				// comment the struct member with the description from the json
				if d := s.Properties[j].Description; d != nil {
					def += "\n" + utils.Comment(*d, strings.Repeat("    ", level+1))
				}
				// struct member name and type, as part of struct definition
				def += fmt.Sprintf(strings.Repeat("    ", level+1)+"public %v %v;\n", subType, s.Properties[j].TypeName)
			}
			def += strings.Repeat("    ", level) + "}"
			if level == 0 {
				def = "public " + def
			} else {
				def += "\n\n" + strings.Repeat("    ", level) + "public " + strings.Title(jsonSubSchema.TypeName)
			}
			content += def
		} else {
			typ = "Object"
		}
	case "number":
		typ = "int"
	case "integer":
		typ = "int"
	case "boolean":
		typ = "boolean"
	// json type string maps to go type string, so only need to test case of when
	// string is a json date-time, so we can convert to go type time.Time...
	case "string":
		if f := jsonSubSchema.Format; f != nil && *f == "date-time" {
			typ = "Date"
		} else {
			typ = "String"
		}
	}
	switch typ {
	case "Date":
		extraPackages["java.util.Date"] = true
	}
	content += typ
	return content, extraPackages, typ
}

func (p Properties) String() string {
	result := ""
	for _, i := range p.SortedPropertyNames {
		result += "Property '" + i + "' =\n" + utils.Indent(p.Properties[i].String(), "  ")
	}
	return result
}

func (p *Properties) postPopulate(apiDef *APIDefinition) {
	// now all data should be loaded, let's sort the p.Properties
	if p.Properties != nil {
		p.SortedPropertyNames = make([]string, 0, len(p.Properties))
		for propertyName := range p.Properties {
			p.SortedPropertyNames = append(p.SortedPropertyNames, propertyName)
		}
		sort.Strings(p.SortedPropertyNames)
		members := make(map[string]bool, len(p.SortedPropertyNames))
		for _, j := range p.SortedPropertyNames {
			p.Properties[j].TypeName = utils.NormaliseLower(j, members)
			// subschemas also need to be triggered to postPopulate...
			p.Properties[j].postPopulate(apiDef)
		}
	}
}

func (p *Properties) UnmarshalJSON(bytes []byte) (err error) {
	errX := json.Unmarshal(bytes, &p.Properties)
	return errX
}

func (aP *AdditionalProperties) UnmarshalJSON(bytes []byte) (err error) {
	b, p := new(bool), new(JsonSubSchema)
	if err = json.Unmarshal(bytes, b); err == nil {
		aP.Boolean = b
		return
	}
	if err = json.Unmarshal(bytes, p); err == nil {
		aP.Properties = p
	}
	return
}

func (aP AdditionalProperties) String() string {
	if aP.Boolean != nil {
		return strconv.FormatBool(*aP.Boolean)
	}
	return aP.Properties.String()
}

func (items Items) String() string {
	result := ""
	for i, j := range items {
		result += fmt.Sprintf("Item '%v' =\n", i) + utils.Indent(j.String(), "  ")
	}
	return result
}

func (items Items) postPopulate(apiDef *APIDefinition) {
	for i := range items {
		items[i].postPopulate(apiDef)
	}
}

func describeList(name string, value interface{}) string {
	if reflect.ValueOf(value).IsValid() {
		if !reflect.ValueOf(value).IsNil() {
			return fmt.Sprintf("%v\n", name) + utils.Indent(fmt.Sprintf("%v", reflect.Indirect(reflect.ValueOf(value)).Interface()), "  ")
		}
	}
	return ""
}

// If item is not null, then return a description of it. If it is a pointer, dereference it first.
func describe(name string, value interface{}) string {
	if reflect.ValueOf(value).IsValid() {
		if !reflect.ValueOf(value).IsNil() {
			return fmt.Sprintf("%-22v = '%v'\n", name, reflect.Indirect(reflect.ValueOf(value)).Interface())
		}
	}
	return ""
}

type CanPopulate interface {
	postPopulate(*APIDefinition)
}

func postPopulateIfNotNil(canPopulate CanPopulate, apiDef *APIDefinition) {
	if reflect.ValueOf(canPopulate).IsValid() {
		if !reflect.ValueOf(canPopulate).IsNil() {
			canPopulate.postPopulate(apiDef)
		}
	}
}

func (subSchema *JsonSubSchema) postPopulate(apiDef *APIDefinition) {
	if subSchema.TypeName == "" {
		members := make(map[string]bool, 1)
		switch {
		case subSchema.Title != nil && *subSchema.Title != "" && len(*subSchema.Title) < 40:
			subSchema.TypeName = utils.NormaliseLower(*subSchema.Title, members)
		case subSchema.Description != nil && *subSchema.Description != "" && len(*subSchema.Description) < 40:
			subSchema.TypeName = utils.NormaliseLower(*subSchema.Description, members)
		case subSchema.RefSubSchema != nil && subSchema.RefSubSchema.TypeName != "":
			subSchema.TypeName = subSchema.RefSubSchema.TypeName
		default:
			subSchema.TypeName = "X"
		}
	}
	// Arrays should get their name from their parent subschema. Note we set
	// this before calling postPopulate on subSchema.Items, to make sure we get
	// there first! If already set, it won't get updated later.
	if subSchema.Items != nil {
		subSchema.Items.TypeName = subSchema.TypeName
	}
	postPopulateIfNotNil(subSchema.AllOf, apiDef)
	postPopulateIfNotNil(subSchema.AnyOf, apiDef)
	postPopulateIfNotNil(subSchema.OneOf, apiDef)
	postPopulateIfNotNil(subSchema.Items, apiDef)
	postPopulateIfNotNil(subSchema.Properties, apiDef)
	// If we have a $ref pointing to another schema, keep a reference so we can
	// discover TypeName later when we generate the type definition
	subSchema.RefSubSchema = apiDef.cacheJsonSchema(subSchema.Ref)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/xconstruct/go-pushbullet"
	"log"
	"os"
)

type Config struct {
	ApiKey  string   `json:"api_key"`
	Devices []Device `json:"devices"`
}

type Device struct {
	Id   int    `json:"id"`
	Name string `json:"name"`
}

func getArg(i int, fallback string) string {
	if len(os.Args) <= i {
		return ""
	}
	return os.Args[i]
}

func main() {
	cmd := getArg(1, "")

	switch cmd {
	case "login":
		login()
	case "note":
		pushNote()
	default:
		printHelp()
	}
}

func login() {
	key := getArg(2, "")
	var cfg Config

	cfg.ApiKey = key
	cfg.Devices = make([]Device, 0)

	if key == "" {
		writeConfig(cfg)
		return
	}

	pb := pushbullet.New(key)
	devs, err := pb.Devices()
	if err != nil {
		log.Fatalln(err)
	}
	for _, dev := range devs {
		name := dev.Extras.Nickname
		if name == "" {
			name = dev.Extras.Model
		}
		cfg.Devices = append(cfg.Devices, Device{
			Id:   dev.Id,
			Name: name,
		})
	}
	writeConfig(cfg)
}

func readConfig() (Config, error) {
	path := os.Getenv("XDG_CONFIG_HOME") + "/pushb"
	f, err := os.Open(path + "/config.json")
	if err != nil {
		return Config{}, err
	}
	defer func() {
		f.Close()
	}()

	var cfg Config
	dec := json.NewDecoder(f)
	if err = dec.Decode(&cfg); err != nil {
		return Config{}, err
	}
	return cfg, nil
}

func writeConfig(cfg Config) {
	path := os.Getenv("XDG_CONFIG_HOME") + "/pushb"
	f, err := os.OpenFile(path+"/config.json", os.O_WRONLY|os.O_CREATE, 0600)
	if err != nil {
		log.Fatalln(err)
	}
	defer func() {
		f.Close()
	}()

	enc := json.NewEncoder(f)
	if err = enc.Encode(cfg); err != nil {
		log.Fatalln(err)
	}
}

func pushNote() {
	cfg, err := readConfig()
	if err != nil {
		log.Fatalln(err)
	}

	title := getArg(2, "")
	body := getArg(3, "")
	pb := pushbullet.New(cfg.ApiKey)
	err = pb.PushNote(cfg.Devices[0].Id, title, body)
	if err != nil {
		log.Fatalln(err)
	}
}

func printHelp() {
	topic := getArg(2, "")

	switch topic {
	default:
		fmt.Println(`Pushb is a simple client for PushBullet.

Usage:
    pushb command [flags] [arguments]

Commands:
    login      Saves the api key in the config
    devices    Shows a list of registered devices
    help       Shows this help

    address    Pushes an address to a device
    link       Pushes a link to a device
    list       Pushes a list to a device
    note       Pushes a note to a device

Use "pushb help [topic] for more information about that topic.`)
	}
}
<commit_msg>pushb: improve config file handling<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/xconstruct/go-pushbullet"
	"log"
	"os"
	"path/filepath"
)

type Config struct {
	ApiKey  string   `json:"api_key"`
	Devices []Device `json:"devices"`
}

type Device struct {
	Id   int    `json:"id"`
	Name string `json:"name"`
}

func getArg(i int, fallback string) string {
	if len(os.Args) <= i {
		return ""
	}
	return os.Args[i]
}

func main() {
	cmd := getArg(1, "")

	switch cmd {
	case "login":
		login()
	case "note":
		pushNote()
	default:
		printHelp()
	}
}

func login() {
	key := getArg(2, "")
	var cfg Config

	cfg.ApiKey = key
	cfg.Devices = make([]Device, 0)

	if key == "" {
		writeConfig(cfg)
		return
	}

	pb := pushbullet.New(key)
	devs, err := pb.Devices()
	if err != nil {
		log.Fatalln(err)
	}
	for _, dev := range devs {
		name := dev.Extras.Nickname
		if name == "" {
			name = dev.Extras.Model
		}
		cfg.Devices = append(cfg.Devices, Device{
			Id:   dev.Id,
			Name: name,
		})
	}
	writeConfig(cfg)
}

func readConfig() (Config, error) {
	cfgfile := filepath.Join(os.Getenv("HOME"), ".pushb.config.json")
	f, err := os.Open(cfgfile)
	if err != nil {
		return Config{}, err
	}
	defer func() {
		f.Close()
	}()

	var cfg Config
	dec := json.NewDecoder(f)
	if err = dec.Decode(&cfg); err != nil {
		return Config{}, err
	}
	return cfg, nil
}

func writeConfig(cfg Config) {
	cfgfile := filepath.Join(os.Getenv("HOME"), ".pushb.config.json")
	f, err := os.OpenFile(cfgfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		log.Fatalln(err)
	}
	defer func() {
		f.Close()
	}()

	enc := json.NewEncoder(f)
	if err = enc.Encode(cfg); err != nil {
		log.Fatalln(err)
	}
}

func pushNote() {
	cfg, err := readConfig()
	if err != nil {
		log.Fatalln(err)
	}

	title := getArg(2, "")
	body := getArg(3, "")
	pb := pushbullet.New(cfg.ApiKey)
	err = pb.PushNote(cfg.Devices[0].Id, title, body)
	if err != nil {
		log.Fatalln(err)
	}
}

func printHelp() {
	topic := getArg(2, "")

	switch topic {
	default:
		fmt.Println(`Pushb is a simple client for PushBullet.

Usage:
    pushb command [flags] [arguments]

Commands:
    login      Saves the api key in the config
    devices    Shows a list of registered devices
    help       Shows this help

    address    Pushes an address to a device
    link       Pushes a link to a device
    list       Pushes a list to a device
    note       Pushes a note to a device

Use "pushb help [topic] for more information about that topic.`)
	}
}
<|endoftext|>