<commit_before>package ui

import (
	"github.com/jroimartin/gocui"
)

// nextView is shared between Playlists and Queue and they all go to Tracks
func nextView(g *gocui.Gui, v *gocui.View) error {
	return gui.enableTracksView()
}

func mainNextViewLeft(g *gocui.Gui, v *gocui.View) error {
	return gui.enableSideView()
}

func mainNextViewRight(g *gocui.Gui, v *gocui.View) error {
	return gui.enableQueueView()
}

func cursorEnd(g *gocui.Gui, v *gocui.View) error {
	if newIndex := getCurrentViewSize(v); newIndex > -1 {
		ox, _ := v.Origin()
		cx, _ := v.Cursor()
		_, sizeY := v.Size()
		sizeY--

		if newIndex > sizeY {
			v.SetOrigin(ox, newIndex-sizeY)
			v.SetCursor(cx, sizeY)
		} else {
			v.SetCursor(cx, newIndex)
		}

		updateTracksView(g, v)
	}
	return nil
}

func cursorHome(g *gocui.Gui, v *gocui.View) error {
	ox, _ := v.Origin()
	cx, _ := v.Cursor()
	v.SetCursor(cx, 0)
	v.SetOrigin(ox, 0)

	updateTracksView(g, v)
	return nil
}

func cursorPgup(g *gocui.Gui, v *gocui.View) error {
	ox, oy := v.Origin()
	cx, cy := v.Cursor()
	_, pageSizeY := v.Size()
	pageSizeY--

	if newOriginY := oy - pageSizeY; newOriginY > 0 {
		v.SetOrigin(ox, newOriginY)
		v.SetCursor(cx, cy)
	} else {
		v.SetOrigin(ox, 0)
		v.SetCursor(cx, cy)
	}
	updateTracksView(g, v)
	return nil
}

func cursorPgdn(g *gocui.Gui, v *gocui.View) error {
	if maxSize := getCurrentViewSize(v); maxSize > -1 {
		ox, oy := v.Origin()
		cx, cy := v.Cursor()
		_, pageSizeY := v.Size()
		pageSizeY--

		newOriginY := oy + pageSizeY

		if hasMorePages(newOriginY, cy, maxSize) {
			v.SetOrigin(ox, newOriginY)
			v.SetCursor(cx, cy)
		} else if isNotInLastPage(oy, pageSizeY, maxSize) {
			v.SetOrigin(ox, maxSize-pageSizeY)
			v.SetCursor(cx, pageSizeY)
		}
		updateTracksView(g, v)
	}
	return nil
}

func updateTracksView(g *gocui.Gui, v *gocui.View) {
	if v == gui.playlistsView {
		gui.updateTracksView()
	}
}

func getCurrentViewSize(v *gocui.View) int {
	if v == gui.tracksView {
		if selectedPlaylist := gui.getSelectedPlaylist(); selectedPlaylist != nil {
			return selectedPlaylist.Tracks() - 1
		}
	} else if v == gui.playlistsView {
		return playlists.Playlists() - 1
	}
	return -1
}

func hasMorePages(newOriginY int, cursorY int, maxSize int) bool {
	return newOriginY+cursorY <= maxSize
}

func isNotInLastPage(originY int, pageSizeY int, maxSize int) bool {
	return originY+pageSizeY <= maxSize
}

func cursorDown(g *gocui.Gui, v *gocui.View) error {
	offset := getOffsetFromTypedNumbers()
	if cx, cy := v.Cursor(); canGoToNewPosition(cy + offset) {
		if err := v.SetCursor(cx, cy+offset); err != nil {
			ox, oy := v.Origin()
			if err := v.SetOrigin(ox, oy+offset); err != nil {
				return err
			}
		}
		if v == gui.playlistsView {
			gui.updateTracksView()
		}
	}
	return nil
}

func cursorUp(g *gocui.Gui, v *gocui.View) error {
	offset := getOffsetFromTypedNumbers()
	ox, oy := v.Origin()
	cx, cy := v.Cursor()
	if err := v.SetCursor(cx, cy-offset); err != nil && oy > 0 {
		if err := v.SetOrigin(ox, oy-offset); err != nil {
			return err
		}
	}
	if v == gui.playlistsView {
		gui.updateTracksView()
	}
	return nil
}

func getOffsetFromTypedNumbers() int {
	if multipleKeysNumber > 1 {
		return multipleKeysNumber
	}
	return 1
}

func canGoToNewPosition(newPosition int) bool {
	currentView := gui.g.CurrentView()
	line, err := currentView.Line(newPosition)
	if err != nil || len(line) == 0 {
		return false
	}
	return true
}

func canGoToAbsolutNewPosition(v *gocui.View, newPosition int) bool {
	switch v {
	case gui.playlistsView:
		return newPosition <= playlists.Playlists()
	case gui.tracksView:
		if currentPlaylist := gui.getSelectedPlaylist(); currentPlaylist != nil {
			return newPosition <= currentPlaylist.Tracks()
		}
	case gui.queueView:
	}
	return true
}

func goTo(g *gocui.Gui, v *gocui.View, position int) error {
	if canGoToAbsolutNewPosition(v, position) {
		position--
		ox, _ := v.Origin()
		cx, _ := v.Cursor()
		v.SetCursor(cx, 0)
		v.SetOrigin(ox, 0)
		if err := v.SetCursor(cx, position); err != nil {
			if err := v.SetOrigin(ox, position); err != nil {
				return err
			}
		}
		if v == gui.playlistsView && gui.tracksView != nil {
			gui.updateTracksView()
		}
	}
	return nil
}

func goToFirstLineCommand(g *gocui.Gui, v *gocui.View) error {
	if multipleKeysNumber <= 0 {
		return cursorHome(g, v)
	}

	return goTo(g, v, multipleKeysNumber)
}

func goToLastLineCommand(g *gocui.Gui, v *gocui.View) error {
	if multipleKeysNumber <= 0 {
		return cursorEnd(g, v)
	}

	return goTo(g, v, multipleKeysNumber)
}
<commit_msg>Count opened subplaylist when counting playlists size<commit_after>package ui

import (
	"github.com/jroimartin/gocui"
)

// nextView is shared between Playlists and Queue and they all go to Tracks
func nextView(g *gocui.Gui, v *gocui.View) error {
	return gui.enableTracksView()
}

func mainNextViewLeft(g *gocui.Gui, v *gocui.View) error {
	return gui.enableSideView()
}

func mainNextViewRight(g *gocui.Gui, v *gocui.View) error {
	return gui.enableQueueView()
}

func cursorEnd(g *gocui.Gui, v *gocui.View) error {
	if newIndex := getCurrentViewSize(v); newIndex > -1 {
		ox, _ := v.Origin()
		cx, _ := v.Cursor()
		_, sizeY := v.Size()
		sizeY--

		if newIndex > sizeY {
			v.SetOrigin(ox, newIndex-sizeY)
			v.SetCursor(cx, sizeY)
		} else {
			v.SetCursor(cx, newIndex)
		}

		updateTracksView(g, v)
	}
	return nil
}

func cursorHome(g *gocui.Gui, v *gocui.View) error {
	ox, _ := v.Origin()
	cx, _ := v.Cursor()
	v.SetCursor(cx, 0)
	v.SetOrigin(ox, 0)

	updateTracksView(g, v)
	return nil
}

func cursorPgup(g *gocui.Gui, v *gocui.View) error {
	ox, oy := v.Origin()
	cx, cy := v.Cursor()
	_, pageSizeY := v.Size()
	pageSizeY--

	if newOriginY := oy - pageSizeY; newOriginY > 0 {
		v.SetOrigin(ox, newOriginY)
		v.SetCursor(cx, cy)
	} else {
		v.SetOrigin(ox, 0)
		v.SetCursor(cx, cy)
	}
	updateTracksView(g, v)
	return nil
}

func cursorPgdn(g *gocui.Gui, v *gocui.View) error {
	if maxSize := getCurrentViewSize(v); maxSize > -1 {
		ox, oy := v.Origin()
		cx, cy := v.Cursor()
		_, pageSizeY := v.Size()
		pageSizeY--

		newOriginY := oy + pageSizeY

		if hasMorePages(newOriginY, cy, maxSize) {
			v.SetOrigin(ox, newOriginY)
			v.SetCursor(cx, cy)
		} else if isNotInLastPage(oy, pageSizeY, maxSize) {
			v.SetOrigin(ox, maxSize-pageSizeY)
			v.SetCursor(cx, pageSizeY)
		}
		updateTracksView(g, v)
	}
	return nil
}

func updateTracksView(g *gocui.Gui, v *gocui.View) {
	if v == gui.playlistsView {
		gui.updateTracksView()
	}
}

func getCurrentViewSize(v *gocui.View) int {
	if v == gui.tracksView {
		return getTracksViewSize(v)
	} else if v == gui.playlistsView {
		return getPlaylistsViewSize(v)
	}
	return -1
}

func getTracksViewSize(v *gocui.View) int {
	if selectedPlaylist := gui.getSelectedPlaylist(); selectedPlaylist != nil {
		return selectedPlaylist.Tracks() - 1
	}
	return -1
}

func getPlaylistsViewSize(v *gocui.View) int {
	subPlaylists := 0
	for _, key := range playlists.Names() {
		playlist := playlists.Get(key)
		if playlist.IsFolder() && playlist.IsFolderOpen() {
			subPlaylists += playlist.Playlists()
		}
	}
	return playlists.Playlists() + subPlaylists - 1
}

func hasMorePages(newOriginY int, cursorY int, maxSize int) bool {
	return newOriginY+cursorY <= maxSize
}

func isNotInLastPage(originY int, pageSizeY int, maxSize int) bool {
	return originY+pageSizeY <= maxSize
}

func cursorDown(g *gocui.Gui, v *gocui.View) error {
	offset := getOffsetFromTypedNumbers()
	if cx, cy := v.Cursor(); canGoToNewPosition(cy + offset) {
		if err := v.SetCursor(cx, cy+offset); err != nil {
			ox, oy := v.Origin()
			if err := v.SetOrigin(ox, oy+offset); err != nil {
				return err
			}
		}
		if v == gui.playlistsView {
			gui.updateTracksView()
		}
	}
	return nil
}

func cursorUp(g *gocui.Gui, v *gocui.View) error {
	offset := getOffsetFromTypedNumbers()
	ox, oy := v.Origin()
	cx, cy := v.Cursor()
	if err := v.SetCursor(cx, cy-offset); err != nil && oy > 0 {
		if err := v.SetOrigin(ox, oy-offset); err != nil {
			return err
		}
	}
	if v == gui.playlistsView {
		gui.updateTracksView()
	}
	return nil
}

func getOffsetFromTypedNumbers() int {
	if multipleKeysNumber > 1 {
		return multipleKeysNumber
	}
	return 1
}

func canGoToNewPosition(newPosition int) bool {
	currentView := gui.g.CurrentView()
	line, err := currentView.Line(newPosition)
	if err != nil || len(line) == 0 {
		return false
	}
	return true
}

func canGoToAbsolutNewPosition(v *gocui.View, newPosition int) bool {
	switch v {
	case gui.playlistsView:
		return newPosition <= playlists.Playlists()
	case gui.tracksView:
		if currentPlaylist := gui.getSelectedPlaylist(); currentPlaylist != nil {
			return newPosition <= currentPlaylist.Tracks()
		}
	case gui.queueView:
	}
	return true
}

func goTo(g *gocui.Gui, v *gocui.View, position int) error {
	if canGoToAbsolutNewPosition(v, position) {
		position--
		ox, _ := v.Origin()
		cx, _ := v.Cursor()
		v.SetCursor(cx, 0)
		v.SetOrigin(ox, 0)
		if err := v.SetCursor(cx, position); err != nil {
			if err := v.SetOrigin(ox, position); err != nil {
				return err
			}
		}
		if v == gui.playlistsView && gui.tracksView != nil {
			gui.updateTracksView()
		}
	}
	return nil
}

func goToFirstLineCommand(g *gocui.Gui, v *gocui.View) error {
	if multipleKeysNumber <= 0 {
		return cursorHome(g, v)
	}

	return goTo(g, v, multipleKeysNumber)
}

func goToLastLineCommand(g *gocui.Gui, v *gocui.View) error {
	if multipleKeysNumber <= 0 {
		return cursorEnd(g, v)
	}

	return goTo(g, v, multipleKeysNumber)
}
<|endoftext|>


<commit_before>package structs

import "fmt"

// Bitmap is a simple uncompressed bitmap
type Bitmap []byte

// NewBitmap returns a bitmap with up to size indexes
func NewBitmap(size uint) (Bitmap, error) {
	if size == 0 {
		return nil, fmt.Errorf("bitmap must be positive size")
	}
	if size&7 != 0 {
		return nil, fmt.Errorf("bitmap must be byte aligned")
	}
	b := make([]byte, size>>3)
	return Bitmap(b), nil
}

// Copy returns a copy of the Bitmap
func (b Bitmap) Copy() (Bitmap, error) {
	if b == nil {
		return nil, fmt.Errorf("can't copy nil Bitmap")
	}

	raw := make([]byte, len(b))
	copy(raw, b)
	return Bitmap(raw), nil
}

// Size returns the size of the bitmap
func (b Bitmap) Size() uint {
	return uint(len(b) << 3)
}

// Set is used to set the given index of the bitmap
func (b Bitmap) Set(idx uint) {
	bucket := idx >> 3
	mask := byte(1 << (idx & 7))
	b[bucket] |= mask
}

// Check is used to check the given index of the bitmap
func (b Bitmap) Check(idx uint) bool {
	bucket := idx >> 3
	mask := byte(1 << (idx & 7))
	return (b[bucket] & mask) != 0
}

// Clear is used to efficiently clear the bitmap
func (b Bitmap) Clear() {
	for i := range b {
		b[i] = 0
	}
}

// IndexesInRange returns the indexes in which the values are either set or unset based
// on the passed parameter in the passed range
func (b Bitmap) IndexesInRange(set bool, from, to uint) []int {
	var indexes []int
	for i := from; i < to; i++ {
		c := b.Check(i)
		if c && set || !c && !set {
			indexes = append(indexes, int(i))
		}
	}

	return indexes
}
<commit_msg>inclusive range<commit_after>package structs

import "fmt"

// Bitmap is a simple uncompressed bitmap
type Bitmap []byte

// NewBitmap returns a bitmap with up to size indexes
func NewBitmap(size uint) (Bitmap, error) {
	if size == 0 {
		return nil, fmt.Errorf("bitmap must be positive size")
	}
	if size&7 != 0 {
		return nil, fmt.Errorf("bitmap must be byte aligned")
	}
	b := make([]byte, size>>3)
	return Bitmap(b), nil
}

// Copy returns a copy of the Bitmap
func (b Bitmap) Copy() (Bitmap, error) {
	if b == nil {
		return nil, fmt.Errorf("can't copy nil Bitmap")
	}

	raw := make([]byte, len(b))
	copy(raw, b)
	return Bitmap(raw), nil
}

// Size returns the size of the bitmap
func (b Bitmap) Size() uint {
	return uint(len(b) << 3)
}

// Set is used to set the given index of the bitmap
func (b Bitmap) Set(idx uint) {
	bucket := idx >> 3
	mask := byte(1 << (idx & 7))
	b[bucket] |= mask
}

// Check is used to check the given index of the bitmap
func (b Bitmap) Check(idx uint) bool {
	bucket := idx >> 3
	mask := byte(1 << (idx & 7))
	return (b[bucket] & mask) != 0
}

// Clear is used to efficiently clear the bitmap
func (b Bitmap) Clear() {
	for i := range b {
		b[i] = 0
	}
}

// IndexesInRange returns the indexes in which the values are either set or unset based
// on the passed parameter in the passed range
func (b Bitmap) IndexesInRange(set bool, from, to uint) []int {
	var indexes []int
	for i := from; i <= to; i++ {
		c := b.Check(i)
		if c && set || !c && !set {
			indexes = append(indexes, int(i))
		}
	}

	return indexes
}
<|endoftext|>


<commit_before>package dependency

import (
	"encoding/gob"
	"fmt"
	"log"
	"net/url"
	"regexp"
	"sort"
	"strings"

	"github.com/hashicorp/consul/api"
	"github.com/pkg/errors"
)

const (
	HealthAny      = "any"
	HealthPassing  = "passing"
	HealthWarning  = "warning"
	HealthCritical = "critical"
	HealthMaint    = "maintenance"

	NodeMaint    = "_node_maintenance"
	ServiceMaint = "_service_maintenance:"
)

var (
	// Ensure implements
	_ Dependency = (*HealthServiceQuery)(nil)

	// HealthServiceQueryRe is the regular expression to use.
	HealthServiceQueryRe = regexp.MustCompile(`\A` + tagRe + nameRe + dcRe + nearRe + filterRe + `\z`)
)

func init() {
	gob.Register([]*HealthService{})
}

// HealthService is a service entry in Consul.
type HealthService struct {
	Node                string
	NodeID              string
	NodeAddress         string
	NodeTaggedAddresses map[string]string
	NodeMeta            map[string]string
	Address             string
	ID                  string
	Name                string
	Tags                ServiceTags
	Checks              []*api.HealthCheck
	Status              string
	Port                int
}

// HealthServiceQuery is the representation of all a service query in Consul.
type HealthServiceQuery struct {
	stopCh chan struct{}

	dc      string
	filters []string
	name    string
	near    string
	tag     string
}

// NewHealthServiceQuery processes the strings to build a service dependency.
func NewHealthServiceQuery(s string) (*HealthServiceQuery, error) {
	if !HealthServiceQueryRe.MatchString(s) {
		return nil, fmt.Errorf("health.service: invalid format: %q", s)
	}

	m := regexpMatch(HealthServiceQueryRe, s)

	var filters []string
	if filter := m["filter"]; filter != "" {
		split := strings.Split(filter, ",")
		for _, f := range split {
			f = strings.TrimSpace(f)
			switch f {
			case HealthAny,
				HealthPassing,
				HealthWarning,
				HealthCritical,
				HealthMaint:
				filters = append(filters, f)
			case "":
			default:
				return nil, fmt.Errorf("health.service: invalid filter: %q in %q", f, s)
			}
		}
		sort.Strings(filters)
	} else {
		filters = []string{HealthPassing}
	}

	return &HealthServiceQuery{
		stopCh:  make(chan struct{}, 1),
		dc:      m["dc"],
		filters: filters,
		name:    m["name"],
		near:    m["near"],
		tag:     m["tag"],
	}, nil
}

// Fetch queries the Consul API defined by the given client and returns a slice
// of HealthService objects.
func (d *HealthServiceQuery) Fetch(clients *ClientSet, opts *QueryOptions) (interface{}, *ResponseMetadata, error) {
	select {
	case <-d.stopCh:
		return nil, nil, ErrStopped
	default:
	}

	opts = opts.Merge(&QueryOptions{
		Datacenter: d.dc,
		Near:       d.near,
	})

	u := &url.URL{
		Path:     "/v1/health/service/" + d.name,
		RawQuery: opts.String(),
	}
	if d.tag != "" {
		q := u.Query()
		q.Set("tag", d.tag)
		u.RawQuery = q.Encode()
	}
	log.Printf("[TRACE] %s: GET %s", d, u)

	// Check if a user-supplied filter was given. If so, we may be querying for
	// more than healthy services, so we need to implement client-side filtering.
	passingOnly := len(d.filters) == 1 && d.filters[0] == HealthPassing

	entries, qm, err := clients.Consul().Health().Service(d.name, d.tag, passingOnly, opts.ToConsulOpts())
	if err != nil {
		return nil, nil, errors.Wrap(err, d.String())
	}

	log.Printf("[TRACE] %s: returned %d results", d, len(entries))

	list := make([]*HealthService, 0, len(entries))
	for _, entry := range entries {
		// Get the status of this service from its checks.
		status := entry.Checks.AggregatedStatus()

		// If we are not checking only healthy services, filter out services that do
		// not match the given filter.
		if !acceptStatus(d.filters, status) {
			continue
		}

		// Get the address of the service, falling back to the address of the node.
		address := entry.Service.Address
		if address == "" {
			address = entry.Node.Address
		}

		list = append(list, &HealthService{
			Node:                entry.Node.Node,
			NodeID:              entry.Node.ID,
			NodeAddress:         entry.Node.Address,
			NodeTaggedAddresses: entry.Node.TaggedAddresses,
			NodeMeta:            entry.Node.Meta,
			Address:             address,
			ID:                  entry.Service.ID,
			Name:                entry.Service.Service,
			Tags:                ServiceTags(deepCopyAndSortTags(entry.Service.Tags)),
			Status:              status,
			Checks:              entry.Checks,
			Port:                entry.Service.Port,
		})
	}

	log.Printf("[TRACE] %s: returned %d results after filtering", d, len(list))

	sort.Stable(ByNodeThenID(list))

	rm := &ResponseMetadata{
		LastIndex:   qm.LastIndex,
		LastContact: qm.LastContact,
	}

	return list, rm, nil
}

// CanShare returns a boolean if this dependency is shareable.
func (d *HealthServiceQuery) CanShare() bool {
	return true
}

// Stop halts the dependency's fetch function.
func (d *HealthServiceQuery) Stop() {
	close(d.stopCh)
}

// String returns the human-friendly version of this dependency.
func (d *HealthServiceQuery) String() string {
	name := d.name
	if d.tag != "" {
		name = d.tag + "." + name
	}
	if d.dc != "" {
		name = name + "@" + d.dc
	}
	if d.near != "" {
		name = name + "~" + d.near
	}
	if len(d.filters) > 0 {
		name = name + "|" + strings.Join(d.filters, ",")
	}
	return fmt.Sprintf("health.service(%s)", name)
}

// Type returns the type of this dependency.
func (d *HealthServiceQuery) Type() Type {
	return TypeConsul
}

// acceptStatus allows us to check if a slice of health checks pass this filter.
func acceptStatus(list []string, s string) bool {
	for _, status := range list {
		if status == s || status == HealthAny {
			return true
		}
	}
	return false
}

// ByNodeThenID is a sortable slice of Service
type ByNodeThenID []*HealthService

// Len, Swap, and Less are used to implement the sort.Sort interface.
func (s ByNodeThenID) Len() int      { return len(s) }
func (s ByNodeThenID) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s ByNodeThenID) Less(i, j int) bool {
	if s[i].Node < s[j].Node {
		return true
	} else if s[i].Node == s[j].Node {
		return s[i].ID <= s[j].ID
	}
	return false
}
<commit_msg>Use internal type instead<commit_after>package dependency

import (
	"encoding/gob"
	"fmt"
	"log"
	"net/url"
	"regexp"
	"sort"
	"strings"

	"github.com/hashicorp/consul/api"
	"github.com/pkg/errors"
)

const (
	HealthAny      = "any"
	HealthPassing  = "passing"
	HealthWarning  = "warning"
	HealthCritical = "critical"
	HealthMaint    = "maintenance"

	NodeMaint    = "_node_maintenance"
	ServiceMaint = "_service_maintenance:"
)

var (
	// Ensure implements
	_ Dependency = (*HealthServiceQuery)(nil)

	// HealthServiceQueryRe is the regular expression to use.
	HealthServiceQueryRe = regexp.MustCompile(`\A` + tagRe + nameRe + dcRe + nearRe + filterRe + `\z`)
)

func init() {
	gob.Register([]*HealthService{})
}

// HealthService is a service entry in Consul.
type HealthService struct {
	Node                string
	NodeID              string
	NodeAddress         string
	NodeTaggedAddresses map[string]string
	NodeMeta            map[string]string
	Address             string
	ID                  string
	Name                string
	Tags                ServiceTags
	Checks              api.HealthChecks
	Status              string
	Port                int
}

// HealthServiceQuery is the representation of all a service query in Consul.
type HealthServiceQuery struct {
	stopCh chan struct{}

	dc      string
	filters []string
	name    string
	near    string
	tag     string
}

// NewHealthServiceQuery processes the strings to build a service dependency.
func NewHealthServiceQuery(s string) (*HealthServiceQuery, error) {
	if !HealthServiceQueryRe.MatchString(s) {
		return nil, fmt.Errorf("health.service: invalid format: %q", s)
	}

	m := regexpMatch(HealthServiceQueryRe, s)

	var filters []string
	if filter := m["filter"]; filter != "" {
		split := strings.Split(filter, ",")
		for _, f := range split {
			f = strings.TrimSpace(f)
			switch f {
			case HealthAny,
				HealthPassing,
				HealthWarning,
				HealthCritical,
				HealthMaint:
				filters = append(filters, f)
			case "":
			default:
				return nil, fmt.Errorf("health.service: invalid filter: %q in %q", f, s)
			}
		}
		sort.Strings(filters)
	} else {
		filters = []string{HealthPassing}
	}

	return &HealthServiceQuery{
		stopCh:  make(chan struct{}, 1),
		dc:      m["dc"],
		filters: filters,
		name:    m["name"],
		near:    m["near"],
		tag:     m["tag"],
	}, nil
}

// Fetch queries the Consul API defined by the given client and returns a slice
// of HealthService objects.
func (d *HealthServiceQuery) Fetch(clients *ClientSet, opts *QueryOptions) (interface{}, *ResponseMetadata, error) {
	select {
	case <-d.stopCh:
		return nil, nil, ErrStopped
	default:
	}

	opts = opts.Merge(&QueryOptions{
		Datacenter: d.dc,
		Near:       d.near,
	})

	u := &url.URL{
		Path:     "/v1/health/service/" + d.name,
		RawQuery: opts.String(),
	}
	if d.tag != "" {
		q := u.Query()
		q.Set("tag", d.tag)
		u.RawQuery = q.Encode()
	}
	log.Printf("[TRACE] %s: GET %s", d, u)

	// Check if a user-supplied filter was given. If so, we may be querying for
	// more than healthy services, so we need to implement client-side filtering.
	passingOnly := len(d.filters) == 1 && d.filters[0] == HealthPassing

	entries, qm, err := clients.Consul().Health().Service(d.name, d.tag, passingOnly, opts.ToConsulOpts())
	if err != nil {
		return nil, nil, errors.Wrap(err, d.String())
	}

	log.Printf("[TRACE] %s: returned %d results", d, len(entries))

	list := make([]*HealthService, 0, len(entries))
	for _, entry := range entries {
		// Get the status of this service from its checks.
		status := entry.Checks.AggregatedStatus()

		// If we are not checking only healthy services, filter out services that do
		// not match the given filter.
		if !acceptStatus(d.filters, status) {
			continue
		}

		// Get the address of the service, falling back to the address of the node.
		address := entry.Service.Address
		if address == "" {
			address = entry.Node.Address
		}

		list = append(list, &HealthService{
			Node:                entry.Node.Node,
			NodeID:              entry.Node.ID,
			NodeAddress:         entry.Node.Address,
			NodeTaggedAddresses: entry.Node.TaggedAddresses,
			NodeMeta:            entry.Node.Meta,
			Address:             address,
			ID:                  entry.Service.ID,
			Name:                entry.Service.Service,
			Tags:                ServiceTags(deepCopyAndSortTags(entry.Service.Tags)),
			Status:              status,
			Checks:              entry.Checks,
			Port:                entry.Service.Port,
		})
	}

	log.Printf("[TRACE] %s: returned %d results after filtering", d, len(list))

	sort.Stable(ByNodeThenID(list))

	rm := &ResponseMetadata{
		LastIndex:   qm.LastIndex,
		LastContact: qm.LastContact,
	}

	return list, rm, nil
}

// CanShare returns a boolean if this dependency is shareable.
func (d *HealthServiceQuery) CanShare() bool {
	return true
}

// Stop halts the dependency's fetch function.
func (d *HealthServiceQuery) Stop() {
	close(d.stopCh)
}

// String returns the human-friendly version of this dependency.
func (d *HealthServiceQuery) String() string {
	name := d.name
	if d.tag != "" {
		name = d.tag + "." + name
	}
	if d.dc != "" {
		name = name + "@" + d.dc
	}
	if d.near != "" {
		name = name + "~" + d.near
	}
	if len(d.filters) > 0 {
		name = name + "|" + strings.Join(d.filters, ",")
	}
	return fmt.Sprintf("health.service(%s)", name)
}

// Type returns the type of this dependency.
func (d *HealthServiceQuery) Type() Type {
	return TypeConsul
}

// acceptStatus allows us to check if a slice of health checks pass this filter.
func acceptStatus(list []string, s string) bool {
	for _, status := range list {
		if status == s || status == HealthAny {
			return true
		}
	}
	return false
}

// ByNodeThenID is a sortable slice of Service
type ByNodeThenID []*HealthService

// Len, Swap, and Less are used to implement the sort.Sort interface.
func (s ByNodeThenID) Len() int      { return len(s) }
func (s ByNodeThenID) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s ByNodeThenID) Less(i, j int) bool {
	if s[i].Node < s[j].Node {
		return true
	} else if s[i].Node == s[j].Node {
		return s[i].ID <= s[j].ID
	}
	return false
}
<|endoftext|>


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

package snc

import (
	"bytes"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"math/big"
	"net"
	"time"
)

func generateCert(host string) (*bytes.Buffer, *bytes.Buffer, error) {
	cert := bytes.NewBuffer(nil)
	key := bytes.NewBuffer(nil)

	priv, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
	if err != nil {
		return cert, key, err
	}

	notBefore := time.Now()

	notAfter := notBefore.Add(24 * time.Hour)

	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		return cert, key, err
	}

	template := x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			Organization: []string{"Acme Co"},
		},
		NotBefore: notBefore,
		NotAfter:  notAfter,

		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid: true,
	}

	if ip := net.ParseIP(host); ip != nil {
		template.IPAddresses = append(template.IPAddresses, ip)
	} else {
		template.DNSNames = append(template.DNSNames, host)
	}

	template.IsCA = true
	template.KeyUsage |= x509.KeyUsageCertSign

	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
	if err != nil {
		return cert, key, err
	}

	pem.Encode(cert, &pem.Block{
		Type:  "CERTIFICATE",
		Bytes: derBytes,
	})

	b, err := x509.MarshalECPrivateKey(priv)
	if err != nil {
		return cert, key, err
	}

	pem.Encode(key, &pem.Block{Type: "EC PRIVATE KEY", Bytes: b})
	return cert, key, nil
}
<commit_msg>minor, better readabilty<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package snc

import (
	"bytes"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"math/big"
	"net"
	"time"
)

func generateCert(host string) (*bytes.Buffer, *bytes.Buffer, error) {
	cert := bytes.NewBuffer(nil)
	key := bytes.NewBuffer(nil)

	priv, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
	if err != nil {
		return cert, key, err
	}

	notBefore := time.Now()

	notAfter := notBefore.Add(24 * time.Hour)

	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		return cert, key, err
	}

	template := x509.Certificate{
		SerialNumber: serialNumber,
		Subject: pkix.Name{
			Organization: []string{"Acme Co"},
		},
		NotBefore: notBefore,
		NotAfter:  notAfter,

		KeyUsage:    x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		IsCA:        true,
		BasicConstraintsValid: true,
	}

	if ip := net.ParseIP(host); ip != nil {
		template.IPAddresses = append(template.IPAddresses, ip)
	} else {
		template.DNSNames = append(template.DNSNames, host)
	}

	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
	if err != nil {
		return cert, key, err
	}

	pem.Encode(cert, &pem.Block{
		Type:  "CERTIFICATE",
		Bytes: derBytes,
	})

	b, err := x509.MarshalECPrivateKey(priv)
	if err != nil {
		return cert, key, err
	}

	pem.Encode(key, &pem.Block{
		Type:  "EC PRIVATE KEY",
		Bytes: b,
	})
	return cert, key, nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Jonathan J Lawlor. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package matrixexp

import (
	"github.com/gonum/blas/blas64"
)

// General is a typical matrix literal.
type General struct {
	blas64.General
}

// Dims returns the matrix dimensions.
func (m1 *General) Dims() (r, c int) {
	r, c = m1.Rows, m1.Cols
	return
}

// At returns the value at a given row, column index.
func (m1 *General) At(r, c int) float64 {
	return m1.Data[r*m1.Stride+c]
}

// Set changes the value at a given row, column index.
func (m1 *General) Set(r, c int, v float64) {
	m1.Data[r*m1.Stride+c] = v
}

// Eval returns a matrix literal.
func (m1 *General) Eval() MatrixLiteral {
	return m1
}

// Copy creates a (deep) copy of the Matrix Expression.
func (m1 *General) Copy() MatrixExp {
	v := make([]float64, len(m1.Data))
	copy(v, m1.Data)
	return &General{
		blas64.General{
			Rows:   m1.Rows,
			Cols:   m1.Cols,
			Stride: m1.Stride,
			Data:   v,
		},
	}
}

// Err returns the first error encountered while constructing the matrix expression.
func (m1 *General) Err() error {
	if m1.Rows < 0 {
		return ErrInvalidRows(m1.Rows)
	}
	if m1.Cols < 0 {
		return ErrInvalidCols(m1.Cols)
	}
	if m1.Stride < 1 {
		return ErrInvalidStride(m1.Stride)
	}
	if m1.Stride < m1.Cols {
		return ErrStrideLessThanCols{m1.Stride, m1.Cols}
	}
	if maxLen := (m1.Rows-1)*m1.Stride + m1.Cols; maxLen > len(m1.Data) {
		return ErrInvalidDataLen{len(m1.Data), maxLen}
	}
	return nil
}

// T transposes a matrix.
func (m1 *General) T() MatrixExp {
	return &T{m1}
}

// Add two matrices together.
func (m1 *General) Add(m2 MatrixExp) MatrixExp {
	return &Add{
		Left:  m1,
		Right: m2,
	}
}

// Sub subtracts the right matrix from the left matrix.
func (m1 *General) Sub(m2 MatrixExp) MatrixExp {
	return &Sub{
		Left:  m1,
		Right: m2,
	}
}

// Scale performs scalar multiplication.
func (m1 *General) Scale(c float64) MatrixExp {
	return &Scale{
		C: c,
		M: m1,
	}
}

// Mul performs matrix multiplication.
func (m1 *General) Mul(m2 MatrixExp) MatrixExp {
	return &Mul{
		Left:  m1,
		Right: m2,
	}
}

// MulElem performs element-wise multiplication.
func (m1 *General) MulElem(m2 MatrixExp) MatrixExp {
	return &MulElem{
		Left:  m1,
		Right: m2,
	}
}

// DivElem performs element-wise division.
func (m1 *General) DivElem(m2 MatrixExp) MatrixExp {
	return &DivElem{
		Left:  m1,
		Right: m2,
	}
}

// AsVector returns a copy of the values in the matrix as a []float64, in row order.
func (m1 *General) AsVector() []float64 {
	// TODO(jonlawlor): make use of a pool.
	v := make([]float64, len(m1.Data))
	copy(v, m1.Data)
	return v
}

// AsGeneral returns the matrix as a blas64.General (not a copy!)
func (m1 *General) AsGeneral() blas64.General {
	return m1.General
}
<commit_msg>Fix AsVector for General<commit_after>// Copyright 2015 Jonathan J Lawlor. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package matrixexp

import (
	"github.com/gonum/blas/blas64"
)

// General is a typical matrix literal.
type General struct {
	blas64.General
}

// Dims returns the matrix dimensions.
func (m1 *General) Dims() (r, c int) {
	r, c = m1.Rows, m1.Cols
	return
}

// At returns the value at a given row, column index.
func (m1 *General) At(r, c int) float64 {
	return m1.Data[r*m1.Stride+c]
}

// Set changes the value at a given row, column index.
func (m1 *General) Set(r, c int, v float64) {
	m1.Data[r*m1.Stride+c] = v
}

// Eval returns a matrix literal.
func (m1 *General) Eval() MatrixLiteral {
	return m1
}

// Copy creates a (deep) copy of the Matrix Expression.
func (m1 *General) Copy() MatrixExp {
	v := make([]float64, len(m1.Data))
	copy(v, m1.Data)
	return &General{
		blas64.General{
			Rows:   m1.Rows,
			Cols:   m1.Cols,
			Stride: m1.Stride,
			Data:   v,
		},
	}
}

// Err returns the first error encountered while constructing the matrix expression.
func (m1 *General) Err() error {
	if m1.Rows < 0 {
		return ErrInvalidRows(m1.Rows)
	}
	if m1.Cols < 0 {
		return ErrInvalidCols(m1.Cols)
	}
	if m1.Stride < 1 {
		return ErrInvalidStride(m1.Stride)
	}
	if m1.Stride < m1.Cols {
		return ErrStrideLessThanCols{m1.Stride, m1.Cols}
	}
	if maxLen := (m1.Rows-1)*m1.Stride + m1.Cols; maxLen > len(m1.Data) {
		return ErrInvalidDataLen{len(m1.Data), maxLen}
	}
	return nil
}

// T transposes a matrix.
func (m1 *General) T() MatrixExp {
	return &T{m1}
}

// Add two matrices together.
func (m1 *General) Add(m2 MatrixExp) MatrixExp {
	return &Add{
		Left:  m1,
		Right: m2,
	}
}

// Sub subtracts the right matrix from the left matrix.
func (m1 *General) Sub(m2 MatrixExp) MatrixExp {
	return &Sub{
		Left:  m1,
		Right: m2,
	}
}

// Scale performs scalar multiplication.
func (m1 *General) Scale(c float64) MatrixExp {
	return &Scale{
		C: c,
		M: m1,
	}
}

// Mul performs matrix multiplication.
func (m1 *General) Mul(m2 MatrixExp) MatrixExp {
	return &Mul{
		Left:  m1,
		Right: m2,
	}
}

// MulElem performs element-wise multiplication.
func (m1 *General) MulElem(m2 MatrixExp) MatrixExp {
	return &MulElem{
		Left:  m1,
		Right: m2,
	}
}

// DivElem performs element-wise division.
func (m1 *General) DivElem(m2 MatrixExp) MatrixExp {
	return &DivElem{
		Left:  m1,
		Right: m2,
	}
}

// AsVector returns a copy of the values in the matrix as a []float64, in row order.
func (m1 *General) AsVector() []float64 {
	// TODO(jonlawlor): make use of a pool.
	v := make([]float64, m1.Rows*m1.Cols)
	for i := 0; i < m1.Rows; i++ {
		copy(v[i*m1.Cols:(i+1)*m1.Cols], m1.Data[i*m1.Stride:i*m1.Stride+m1.Cols])
	}
	copy(v, m1.Data)
	return v
}

// AsGeneral returns the matrix as a blas64.General (not a copy!)
func (m1 *General) AsGeneral() blas64.General {
	return m1.General
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"gesture/gis"
	"gesture/rewrite"
	"gesture/twitter"
	irc "github.com/fluffle/goirc/client"
	"log"
	"strings"
)

var (
	channels = []string{"#collinjester"}
)

// when an error occurs, calling this method will send the error back to the irc channel
func sendError(conn *irc.Conn, channel string, nick string, err error) {
	log.Print(err)
	conn.Privmsg(channel, fmt.Sprintf("%s: oops: %v", nick, err))
}

// When a message comes in on a channel gesture has joined, this method will be called.
func messageReceived(conn *irc.Conn, line *irc.Line) {
	if len(line.Args) > 1 {
		channel := line.Args[0]
		message := line.Args[1]
		messageSliced := strings.Split(message, " ")
		command := messageSliced[0]
		commandArgs := messageSliced[1:]

		log.Printf(">> %s (%s): %s\n", line.Nick, channel, message)

		switch {
		case command == "gis":
			if len(commandArgs) > 0 {
				link, err := gis.Search(strings.Join(commandArgs, " "))
				if err != nil {
					sendError(conn, channel, line.Nick, err)
				} else {
					conn.Privmsg(channel, fmt.Sprintf("%s: %s", line.Nick, link))
				}
			}
		case command == "echo":
			conn.Privmsg(channel, fmt.Sprintf("%s: %s", line.Nick, rewrite.Rewrite(message)))
		case command == "describe":
			if len(commandArgs) > 0 {
				described, err := twitter.Describe(commandArgs[0])
				if err != nil {
					sendError(conn, channel, line.Nick, err)
				} else {
					conn.Privmsg(channel, fmt.Sprintf("%s: %s", line.Nick, rewrite.Rewrite(described)))
				}
			}
		default:
			// find any shortened links and output the expanded versions
			for _, link := range rewrite.GetRewrittenLinks(message) {
				response := line.Nick + ": " + link
				conn.Privmsg(channel, response)
			}
		}
	}
}

func main() {
	flag.Parse()
	c := irc.SimpleClient("gesturebot")
	c.SSL = true
	c.AddHandler(irc.CONNECTED,
		func(conn *irc.Conn, line *irc.Line) {
			for _, channel := range channels {
				conn.Join(channel)
			}
		})
	quit := make(chan bool)
	c.AddHandler(irc.DISCONNECTED, func(conn *irc.Conn, line *irc.Line) { quit <- true })
	c.AddHandler("PRIVMSG", func(conn *irc.Conn, line *irc.Line) {
		messageReceived(conn, line)
	})
	if err := c.Connect("irc.freenode.net"); err != nil {
		fmt.Printf("Connection error: %s\n", err)
	}
	// Wait for disconnect
	<-quit
}
<commit_msg>Minor code rearranging<commit_after>package main

import (
	"flag"
	"fmt"
	"gesture/gis"
	"gesture/rewrite"
	"gesture/twitter"
	irc "github.com/fluffle/goirc/client"
	"log"
	"strings"
)

var (
	channels = []string{"#collinjester"}
)

func main() {
	flag.Parse()
	c := irc.SimpleClient("gesturebot")
	c.SSL = true
	c.AddHandler(irc.CONNECTED,
		func(conn *irc.Conn, line *irc.Line) {
			for _, channel := range channels {
				conn.Join(channel)
			}
		})
	quit := make(chan bool)
	c.AddHandler(irc.DISCONNECTED, func(conn *irc.Conn, line *irc.Line) { quit <- true })
	c.AddHandler("PRIVMSG", func(conn *irc.Conn, line *irc.Line) {
		messageReceived(conn, line)
	})
	if err := c.Connect("irc.freenode.net"); err != nil {
		fmt.Printf("Connection error: %s\n", err)
	}
	// Wait for disconnect
	<-quit
}

// When a message comes in on a channel gesture has joined, this method will be called.
func messageReceived(conn *irc.Conn, line *irc.Line) {
	if len(line.Args) > 1 {
		channel := line.Args[0]
		message := line.Args[1]
		messageSliced := strings.Split(message, " ")
		command := messageSliced[0]
		commandArgs := messageSliced[1:]

		log.Printf(">> %s (%s): %s\n", line.Nick, channel, message)

		switch {
		case command == "gis":
			if len(commandArgs) > 0 {
				link, err := gis.Search(strings.Join(commandArgs, " "))
				if err != nil {
					sendError(conn, channel, line.Nick, err)
				} else {
					conn.Privmsg(channel, fmt.Sprintf("%s: %s", line.Nick, link))
				}
			}
		case command == "echo":
			conn.Privmsg(channel, fmt.Sprintf("%s: %s", line.Nick, rewrite.Rewrite(message)))
		case command == "describe":
			if len(commandArgs) > 0 {
				described, err := twitter.Describe(commandArgs[0])
				if err != nil {
					sendError(conn, channel, line.Nick, err)
				} else {
					conn.Privmsg(channel, fmt.Sprintf("%s: %s", line.Nick, rewrite.Rewrite(described)))
				}
			}
		default:
			// find any shortened links and output the expanded versions
			for _, link := range rewrite.GetRewrittenLinks(message) {
				response := line.Nick + ": " + link
				conn.Privmsg(channel, response)
			}
		}
	}
}

// when an error occurs, calling this method will send the error back to the irc channel
func sendError(conn *irc.Conn, channel string, nick string, err error) {
	log.Print(err)
	conn.Privmsg(channel, fmt.Sprintf("%s: oops: %v", nick, err))
}

<|endoftext|>


<commit_before>package main

import (
    "net/http"
)

func main() {
    // your http.Handle calls here
    http.ListenAndServe("localhost:4000", nil)
}
<commit_msg>Implement `ServeHTTP`.<commit_after>package main

import (
	"fmt"
	"net/http"
)

type String string

type Struct struct {
	Greeting string
	Punct    string
	Who      string
}

func (h String) ServeHTTP(
	w http.ResponseWriter,
	r *http.Request) {
	fmt.Fprint(w, h)
}

func (h Struct) ServeHTTP(
	w http.ResponseWriter,
	r *http.Request) {
	fmt.Fprint(w, fmt.Sprintf("%s%s%s", h.Greeting, h.Punct, h.Who))
}

func main() {
	http.Handle("/string", String("I'm a frayed knot."))
	http.Handle("/struct", &Struct{"Hello", ":", "Gophers!"})
	http.ListenAndServe("localhost:4000", nil)
}
<|endoftext|>


<commit_before>package main

import (
        "flag"
        "fmt"
        "os"
        "github.com/PuerkitoBio/goquery"
)

type RepoInf struct {
        RepositoryName string
        Description    string
}

const (
        TREND_MAX_NUM = 25
)

var repoInf []RepoInf
var baseUrl string = "https://github.com/trending"

var (
        lang = flag.String("l", "all", "Select language")
        desc = flag.Bool("d", false, "Show description")
        num  = flag.Int("n", 10, "Limit numbers")
        help = flag.Bool("h", false, "Show help message")
)

func main() {
        flag.Usage = func() {
                fmt.Fprint(os.Stderr, `
usage: ghtrend <command> [options] <args>

optional arguments:
  -l    Select language.
  -d    Show description.
  -n    Limit numbers.
  -h    Show help message.
`)
        }
        flag.Parse()

        if *help {
                flag.Usage()
                os.Exit(0)
        }
        n := getNum(*num)
        url := getUrl(*lang)

        repoInf = getMemory(n)

        getPage(url, n)

        showResult()
}

func getUrl(lang string) string {
        if lang == "" {
                return baseUrl
        } else {
                return baseUrl + "?l=" + lang
        }
}

func getNum(num int) int {
        if num > TREND_MAX_NUM {
                num = TREND_MAX_NUM
        }
        return num
}

func getMemory(num int) []RepoInf {
        return make([]RepoInf, num)
}

func getPage(url string, num int) {
        doc, _ := goquery.NewDocument(url)
        doc.Find(".leaderboard-list-content").Each(func(i int, s *goquery.Selection) {
                // fmt.Println(s.Find(".owner-name").Text())
                // fmt.Println(s.Find("span[class='owner-name']").Text())
                // fmt.Println(s.Find("strong").Text())
                if i < num {
                        // fmt.Println(s.Find("a[class='repository-name']").Text())
                        repoInf[i].RepositoryName = s.Find("a[class='repository-name']").Text()
                        repoInf[i].Description = s.Find("p[class='repo-leaderboard-description']").Text()
                }

        })
}

func showResult() {
        fmt.Println("Trending " + *lang + " repositories on GitHub today")
        line := ""
        for i := 0; i < 56; i++ {
                line += "-"
        }
        fmt.Println(line)

        spaces := ""
        for i, rp := range repoInf {
                fmt.Println(fmt.Sprint(i + 1) + ": " + rp.RepositoryName)

                if (i + 1) >= 10 {
                        spaces = "    "
                } else {
                        spaces = "   "
                }
                if *desc {
                        fmt.Println(spaces + rp.Description)
                }
        }
}
<commit_msg>Added comannd option -b and -v<commit_after>package main

import (
        "flag"
        "fmt"
        "os"
        "github.com/PuerkitoBio/goquery"
        "os/exec"
)

type RepoInf struct {
        RepositoryName string
        Description    string
        RepoUrl        string
}

const (
        TREND_MAX_NUM = 25
        VERSION = "0.0.1"
)

var repoInf []RepoInf
var baseUrl string = "https://github.com/trending"

var (
        lang    = flag.String("l", "all", "Select language")
        desc    = flag.Bool("d", false, "Show description")
        num     = flag.Int("n", 10, "Limit numbers")
        brows   = flag.Int("b", 0, "Show repository on browser")
        help    = flag.Bool("h", false, "Show help message")
        version = flag.Bool("v", false, "Show version")
)

func main() {
        flag.Usage = func() {
                fmt.Fprint(os.Stderr, `
usage: ghtrend <command> [options] <args>

optional arguments:
  -l    Select language.
  -d    Show description.
  -n    Limit numbers.
  -b    Show repository on browser.
  -h    Show help message.
  -v    Show version.
`)
        }
        flag.Parse()

        if *version {
                showVersion()
                os.Exit(0)
        }
        if *help {
                flag.Usage()
                os.Exit(0)
        }

        n := getNum(*num)
        url := getGithubUrl(*lang)

        repoInf = getMemory(n)

        getPage(url, n)

        if *brows > 0 && *brows <= 25 {
                browsUrl := getBrowsUrl(*brows)
                openBrowser(browsUrl)
                os.Exit(0)
        }
        showResult()
}

func getGithubUrl(lang string) string {
        if lang == "" {
                return baseUrl
        } else {
                return baseUrl + "?l=" + lang
        }
}

func getNum(num int) int {
        if num > TREND_MAX_NUM {
                num = TREND_MAX_NUM
        }
        return num
}

func getMemory(num int) []RepoInf {
        return make([]RepoInf, num)
}

func getPage(url string, num int) {
        doc, _ := goquery.NewDocument(url)
        doc.Find(".leaderboard-list-content").Each(func(i int, s *goquery.Selection) {
                if i < num {
                        repoInf[i].RepositoryName = s.Find("a[class='repository-name']").Text()
                        repoInf[i].Description = s.Find("p[class='repo-leaderboard-description']").Text()
                        repoInf[i].RepoUrl = s.Find("a[class='repository-name']").Text()
                }

        })
}

func showResult() {
        fmt.Println("Trending " + *lang + " repositories on GitHub today")
        line := ""
        for i := 0; i < 56; i++ {
                line += "-"
        }
        fmt.Println(line)

        spaces := ""
        for i, rp := range repoInf {
                fmt.Println(fmt.Sprint(i + 1) + ": " + rp.RepositoryName)

                if (i + 1) >= 10 {
                        spaces = "    "
                } else {
                        spaces = "   "
                }
                if *desc {
                        fmt.Println(spaces + rp.Description)
                }
        }
}

func getBrowsUrl(idx int) string {
        return "https://github.com/" + repoInf[idx - 1].RepoUrl
}

func openBrowser(url string) {
        exec.Command("open", url).Run()
}

func showVersion() {
        fmt.Printf("ghtrend Ver %s\n", VERSION)
}
<|endoftext|>


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

// Package git provides types and utilities for dealing with Git repositories.
// It's very limited, and provide some access to git config file, being focused
// on tsuru needs.
package git

import (
	"bufio"
	"errors"
	"fmt"
	"os"
	"path"
	"strings"
)

// DiscoverRepositoryPath finds the path of the repository from a given
// directory. It returns the path to the repository, or an an empty string and
// a non-nil error if it can't find the repository.
func DiscoverRepositoryPath(dir string) (string, error) {
	_, err := os.Stat(dir)
	if os.IsNotExist(err) {
		return "", errors.New("Repository not found.")
	}
	dir = path.Join(dir, ".git")
	for dir != "/.git" {
		fi, err := os.Stat(dir)
		if err == nil && fi.IsDir() {
			return dir, nil
		}
		dir = path.Join(dir, "..", "..", ".git")
	}
	return "", errors.New("Repository not found.")
}

// Repository represents a git repository.
type Repository struct {
	path string
}

// OpenRepository opens a repository by its path. You can use
// DiscoverRepositoryPath to discover the repository from any directory, and
// use the result of this call as parameter for OpenRepository.
//
// OpenRepository will return an error if the given path does not appear to be
// a git repository.
func OpenRepository(p string) (*Repository, error) {
	if !strings.HasSuffix(p, ".git") && !strings.HasSuffix(p, ".git/") {
		p = path.Join(p, ".git")
	}
	p = strings.TrimRight(p, "/")
	fi, err := os.Stat(path.Join(p, "config"))
	if err == nil && !fi.IsDir() {
		return &Repository{path: p}, nil
	}
	return nil, errors.New("Repository not found.")
}

// RemoteURL returns the URL of a remote by its name. Or an error, if the
// remote is not declared.
func (r *Repository) RemoteURL(name string) (string, error) {
	config, err := os.Open(path.Join(r.path, "config"))
	if err != nil {
		return "", err
	}
	defer config.Close()
	line := fmt.Sprintf("[remote %q]", name)
	scanner := bufio.NewScanner(config)
	scanner.Split(bufio.ScanLines)
	for scanner.Scan() {
		if scanner.Text() == line {
			scanner.Scan()
			return strings.Split(scanner.Text(), " = ")[1], nil
		}
	}
	return "", fmt.Errorf("Remote %q not found.", name)
}
<commit_msg>git: use filepath instead of path for joining paths<commit_after>// Copyright 2015 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package git provides types and utilities for dealing with Git repositories.
// It's very limited, and provide some access to git config file, being focused
// on tsuru needs.
package git

import (
	"bufio"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"
)

// DiscoverRepositoryPath finds the path of the repository from a given
// directory. It returns the path to the repository, or an an empty string and
// a non-nil error if it can't find the repository.
func DiscoverRepositoryPath(dir string) (string, error) {
	_, err := os.Stat(dir)
	if os.IsNotExist(err) {
		return "", errors.New("Repository not found.")
	}
	dir = filepath.Join(dir, ".git")
	for dir != "/.git" {
		fi, err := os.Stat(dir)
		if err == nil && fi.IsDir() {
			return dir, nil
		}
		dir = filepath.Join(dir, "..", "..", ".git")
	}
	return "", errors.New("Repository not found.")
}

// Repository represents a git repository.
type Repository struct {
	path string
}

// OpenRepository opens a repository by its filepath. You can use
// DiscoverRepositoryPath to discover the repository from any directory, and
// use the result of this call as parameter for OpenRepository.
//
// OpenRepository will return an error if the given path does not appear to be
// a git repository.
func OpenRepository(p string) (*Repository, error) {
	if !strings.HasSuffix(p, ".git") && !strings.HasSuffix(p, ".git/") {
		p = filepath.Join(p, ".git")
	}
	p = strings.TrimRight(p, "/")
	fi, err := os.Stat(filepath.Join(p, "config"))
	if err == nil && !fi.IsDir() {
		return &Repository{path: p}, nil
	}
	return nil, errors.New("Repository not found.")
}

// RemoteURL returns the URL of a remote by its name. Or an error, if the
// remote is not declared.
func (r *Repository) RemoteURL(name string) (string, error) {
	config, err := os.Open(filepath.Join(r.path, "config"))
	if err != nil {
		return "", err
	}
	defer config.Close()
	line := fmt.Sprintf("[remote %q]", name)
	scanner := bufio.NewScanner(config)
	scanner.Split(bufio.ScanLines)
	for scanner.Scan() {
		if scanner.Text() == line {
			scanner.Scan()
			return strings.Split(scanner.Text(), " = ")[1], nil
		}
	}
	return "", fmt.Errorf("Remote %q not found.", name)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"compress/gzip"
	"encoding/json"
	"fmt"
	"github.com/cdale77/gitmine/Godeps/_workspace/src/github.com/melvinmt/firebase"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"
)

type Event struct {
	Type       string
	Created_at string
	Actor      EventActor
	Payload    EventPayload
}

type EventActor struct {
	Login      string
	Avatar_url string
}

type EventPayload struct {
	Size    int
	Commits []CommitCommit
}

type StoredCommit struct {
	Date    string
	Login   string
	Avatar  string
	Message string
	Url     string
}

type CommitCommit struct {
	Message string
	Url     string
}

func main() {
	fullDate := time.Now().AddDate(0, 0, -1).Format("2006-01-02")
	getData(fullDate)
}

func storeCommit(event Event, commitMessage string, commitUrl string) bool {
	fmt.Println("storing event:")
	fmt.Println(event)
	authToken := os.Getenv("FIREBASE_SECRET")

	url := os.Getenv("FIREBASE_URL")

	fireBase := firebase.NewReference(url).Auth(authToken)

	var storedCommit StoredCommit
	storedCommit.Date = event.Created_at
	storedCommit.Login = event.Actor.Login
	storedCommit.Avatar = event.Actor.Avatar_url
	storedCommit.Message = commitMessage
	storedCommit.Url = commitUrl

	err := fireBase.Push(storedCommit)
	if err != nil {
		fmt.Println("Firebase error")
		fmt.Println(err)
		return false
	} else {
		fmt.Println("Firebase success")
		return true
	}
}

// There must be a better way to do this. Probably sort cussWords alpha
// and use a lookup table.
func isDirty(message string) bool {

	result := false

	cussWords := []string{
		"fuck",
		"bitch",
		"stupid",
		"tits",
		"asshole",
		"cocksucker",
		"cunt",
		"hell",
		"douche",
		"testicle",
		"twat",
		"bastard",
		"sperm",
		"shit",
		"dildo",
		"wanker",
		"prick",
		"penis",
		"vagina",
		"whore"}

	messageWords := strings.Split(message, " ")

	for _, cussWord := range cussWords {
		for _, word := range messageWords {
			if word == cussWord {
				result = true
			}
		}
	}
	return result
}

func parseEvent(line string) {
	var event Event

	jsonErr := json.Unmarshal([]byte(line), &event)
	if jsonErr != nil {
		fmt.Println("Could not parse json.")
		fmt.Println(jsonErr)
	}

	if event.Type == "PushEvent" && event.Payload.Size > 0 {

		// An event can have multiple commits.
		commits := event.Payload.Commits
		for _, commit := range commits {
			if isDirty(commit.Message) {
				fmt.Println(commit.Message)
				storeCommit(event, commit.Message, commit.Url)
			}
		}
	}
}

func parseFile(fName string) {
	// https://groups.google.com/forum/#!topic/golang-nuts/GjIkryuCyAY
	// TODO: standardize use of file api
	// https://stackoverflow.com/questions/1821811/how-to-read-write-from-to-file
	fileOS, err := os.Open(fName)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Can't open %s: error: %s\n", fName, err)
		os.Exit(1)
	}

	//https://stackoverflow.com/questions/1821811/how-to-read-write-from-to-file
	// close fi on exit and check for its returned error
	defer func() {
		if err := fileOS.Close(); err != nil {
			panic(err)
		}
	}()

	fileGzip, err := gzip.NewReader(fileOS)
	if err != nil {
		fmt.Printf("The file %v is not in gzip format.\n", fName)
		os.Exit(1)
	}

	fileRead := bufio.NewReader(fileGzip)
	i := 0
	for {
		line, err := fileRead.ReadString('\n')
		if err != nil {
			fmt.Println("Error reading file.")
			fmt.Println(err)
			break
		}

		parseEvent(line)

		i++
	}

	os.Remove(fName)
}

func getData(fullDate string) {

	urls := makeUrlArray(fullDate)

	for i, value := range urls {

		fmt.Println("fetching url", value)

		resp, archiveErr := http.Get(value)

		if resp != nil {
			defer resp.Body.Close()
		}

		if archiveErr != nil {
			handleError("Error getting github archive", archiveErr)
		}

		contents, readErr := ioutil.ReadAll(resp.Body)

		if readErr != nil {
			handleError("Error converting response", readErr)
		}

		fname := makeFileName(fullDate, i)

		fileErr := ioutil.WriteFile(fname, contents, 0644)

		if fileErr != nil {
			handleError("Error writing response to file", fileErr)
		}

		parseFile(fname)

	}
}

func makeUrlArray(fullDate string) [24]string {

	baseUrl := makeUrlBase(fullDate)
	urlEnd := ".json.gz"

	var urls [24]string

	for i := 0; i < 24; i++ {

		var buffer bytes.Buffer
		buffer.WriteString(baseUrl)
		buffer.WriteString("-")
		buffer.WriteString(strconv.Itoa(i))
		buffer.WriteString(urlEnd)
		url := buffer.String()

		urls[i] = url
	}

	return urls
}

func makeUrlBase(fullDate string) string {
	split := strings.Split(fullDate, "-")

	var buffer bytes.Buffer
	buffer.WriteString("http://data.githubarchive.org/")
	buffer.WriteString(split[0]) //year
	buffer.WriteString("-")
	buffer.WriteString(split[1]) //month
	buffer.WriteString("-")
	buffer.WriteString(split[2]) //day

	return buffer.String()
}

func makeFileName(fullDate string, i int) string {
	var buffer bytes.Buffer
	buffer.WriteString("data-")
	buffer.WriteString(fullDate)
	buffer.WriteString("-")
	buffer.WriteString(strconv.Itoa(i))
	buffer.WriteString(".gz")

	return buffer.String()

}

func handleError(message string, err error) {
	fmt.Println(message, err)
	os.Exit(1)
}
<commit_msg>deduce html url<commit_after>package main

import (
	"bufio"
	"bytes"
	"compress/gzip"
	"encoding/json"
	"fmt"
	"github.com/cdale77/gitmine/Godeps/_workspace/src/github.com/melvinmt/firebase"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"
)

type Event struct {
	Type       string
	Created_at string
	Actor      EventActor
	Payload    EventPayload
}

type EventActor struct {
	Login      string
	Avatar_url string
}

type EventPayload struct {
	Size    int
	Commits []CommitCommit
}

type StoredCommit struct {
	Date    string
	Login   string
	Avatar  string
	Message string
	Url     string
}

type CommitCommit struct {
	Message string
	Url     string
}

func main() {
	fullDate := time.Now().AddDate(0, 0, -1).Format("2006-01-02")
	getData(fullDate)
}

func storeCommit(event Event, commitMessage string, commitUrl string) bool {
	fmt.Println("storing event:")
	fmt.Println(event)
	authToken := os.Getenv("FIREBASE_SECRET")

	url := os.Getenv("FIREBASE_URL")

	fireBase := firebase.NewReference(url).Auth(authToken)

	var storedCommit StoredCommit
	storedCommit.Date = event.Created_at
	storedCommit.Login = event.Actor.Login
	storedCommit.Avatar = event.Actor.Avatar_url
	storedCommit.Message = commitMessage
	storedCommit.Url = commitUrl

	err := fireBase.Push(storedCommit)
	if err != nil {
		fmt.Println("Firebase error")
		fmt.Println(err)
		return false
	} else {
		fmt.Println("Firebase success")
		return true
	}
}

// There must be a better way to do this. Probably sort cussWords alpha
// and use a lookup table.
func isDirty(message string) bool {

	result := false

	cussWords := []string{
		"fuck",
		"bitch",
		"stupid",
		"tits",
		"asshole",
		"cocksucker",
		"cunt",
		"hell",
		"douche",
		"testicle",
		"twat",
		"bastard",
		"sperm",
		"shit",
		"dildo",
		"wanker",
		"prick",
		"penis",
		"vagina",
		"whore"}

	messageWords := strings.Split(message, " ")

	for _, cussWord := range cussWords {
		for _, word := range messageWords {
			if word == cussWord {
				result = true
			}
		}
	}
	return result
}

func parseEvent(line string) {
	var event Event

	jsonErr := json.Unmarshal([]byte(line), &event)
	if jsonErr != nil {
		fmt.Println("Could not parse json.")
		fmt.Println(jsonErr)
	}

	if event.Type == "PushEvent" && event.Payload.Size > 0 {

		// An event can have multiple commits.
		commits := event.Payload.Commits
		for _, commit := range commits {
			if isDirty(commit.Message) {
				fmt.Println(commit.Message)
				htmlUrl := makeHtmlUrl(commit.Url)
				storeCommit(event, commit.Message, htmlUrl)
			}
		}
	}
}

func parseFile(fName string) {
	// https://groups.google.com/forum/#!topic/golang-nuts/GjIkryuCyAY
	// TODO: standardize use of file api
	// https://stackoverflow.com/questions/1821811/how-to-read-write-from-to-file
	fileOS, err := os.Open(fName)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Can't open %s: error: %s\n", fName, err)
		os.Exit(1)
	}

	//https://stackoverflow.com/questions/1821811/how-to-read-write-from-to-file
	// close fi on exit and check for its returned error
	defer func() {
		if err := fileOS.Close(); err != nil {
			panic(err)
		}
	}()

	fileGzip, err := gzip.NewReader(fileOS)
	if err != nil {
		fmt.Printf("The file %v is not in gzip format.\n", fName)
		os.Exit(1)
	}

	fileRead := bufio.NewReader(fileGzip)
	i := 0
	for {
		line, err := fileRead.ReadString('\n')
		if err != nil {
			fmt.Println("Error reading file.")
			fmt.Println(err)
			break
		}

		parseEvent(line)

		i++
	}

	os.Remove(fName)
}

func getData(fullDate string) {

	urls := makeUrlArray(fullDate)

	for i, value := range urls {

		fmt.Println("fetching url", value)

		resp, archiveErr := http.Get(value)

		if resp != nil {
			defer resp.Body.Close()
		}

		if archiveErr != nil {
			handleError("Error getting github archive", archiveErr)
		}

		contents, readErr := ioutil.ReadAll(resp.Body)

		if readErr != nil {
			handleError("Error converting response", readErr)
		}

		fname := makeFileName(fullDate, i)

		fileErr := ioutil.WriteFile(fname, contents, 0644)

		if fileErr != nil {
			handleError("Error writing response to file", fileErr)
		}

		parseFile(fname)

	}
}

func makeUrlArray(fullDate string) [24]string {

	baseUrl := makeUrlBase(fullDate)
	urlEnd := ".json.gz"

	var urls [24]string

	for i := 0; i < 24; i++ {

		var buffer bytes.Buffer
		buffer.WriteString(baseUrl)
		buffer.WriteString("-")
		buffer.WriteString(strconv.Itoa(i))
		buffer.WriteString(urlEnd)
		url := buffer.String()

		urls[i] = url
	}

	return urls
}

func makeUrlBase(fullDate string) string {
	split := strings.Split(fullDate, "-")

	var buffer bytes.Buffer
	buffer.WriteString("http://data.githubarchive.org/")
	buffer.WriteString(split[0]) //year
	buffer.WriteString("-")
	buffer.WriteString(split[1]) //month
	buffer.WriteString("-")
	buffer.WriteString(split[2]) //day

	return buffer.String()
}

func makeFileName(fullDate string, i int) string {
	var buffer bytes.Buffer
	buffer.WriteString("data-")
	buffer.WriteString(fullDate)
	buffer.WriteString("-")
	buffer.WriteString(strconv.Itoa(i))
	buffer.WriteString(".gz")

	return buffer.String()

}

// The data does not contain an url to make a proper html page. But we can
// deduce it from the supplied api url (which makes json)
func makeHtmlUrl(apiUrl string) string {
	newUrl1 := strings.Replace(apiUrl, "api.", "", 1)
	newUrl2 := strings.Replace(newUrl1, "repos/", "", 1)
	newUrl3 := strings.Replace(newUrl2, "commits", "commit", 1)
	return newUrl3
}

func handleError(message string, err error) {
	fmt.Println(message, err)
	os.Exit(1)
}
<|endoftext|>


<commit_before>package glasses

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

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
)

const (
	CLOUD_VISION_ENDPOINT = "https://vision.googleapis.com/v1alpha1/images:annotate"
)

type Glasses struct {
	Client *http.Client
}

type CloudVisionRequest struct {
	Requests []*AnnotateRequest `json:"requests"`
	User     string             `json:"user"`
}

type AnnotateRequest struct {
	Image        *Image        `json:"image"`
	Features     []Feature     `json:"features"`
	ImageContext *ImageContext `json:"imageContext,omitempty"`
}

type Feature struct {
	Type       string `json:"type"`
	MaxResults int    `json:"maxResults"`
}

type ImageContext struct {
	LatLongRect                 interface{} `json:"latLongRect"`
	ImageContextSearchExtension interface{} `imageContextSearchExtension`
}

//type AnnotateResponse struct {
//FaceAnnotations      []FaceAnnotation     `json:"faceAnnotations"`
//LandmarkAnnotations  []LandmarkAnnotation `json:"landmarkAnnotation"`
//LogoAnnotations      []LogoAnnotation     `json:"logoAnnotations"`
//LabelAnnotations     []LabelAnnotation    `json:"labelAnnotations"`
//TextAnnotations      []TextAnnotation     `json:"textAnnotations"`
//SafeSearchAnnotation SafeSearchAnnotation `json:"safeSearchAnnotation"`
//SuggestAnnotations   []SuggestAnnotation  `json:"suggestAnnotations"`
//QueryAnnotation      QueryAnnotation      `json:"queryAnnotation"`
//Error                Status               `json:"error"`
//}

type AnnotateResponses struct {
	Responses []AnnotateResponse `json:"responses"`
}

type AnnotateResponse struct {
	FaceAnnotations      []interface{} `json:"faceAnnotations"`
	LandmarkAnnotations  []interface{} `json:"landmarkAnnotation"`
	LogoAnnotations      []interface{} `json:"logoAnnotations"`
	LabelAnnotations     []interface{} `json:"labelAnnotations"`
	TextAnnotations      []interface{} `json:"textAnnotations"`
	SafeSearchAnnotation interface{}   `json:"safeSearchAnnotation"`
	SuggestAnnotations   []interface{} `json:"suggestAnnotations"`
	QueryAnnotation      interface{}   `json:"queryAnnotation"`
	Error                interface{}   `json:"error"`
}

func NewGlasses() (*Glasses, error) {
	client, err := google.DefaultClient(oauth2.NoContext, "https://www.googleapis.com/auth/cloud-platform")
	if err != nil {
		return nil, err
	}

	return &Glasses{client}, nil
}

func (g *Glasses) Do(r *CloudVisionRequest) (*AnnotateResponses, error) {

	jR, err := json.Marshal(r)
	if err != nil {
		return nil, err
	}

	rawResp, err := g.Client.Post(CLOUD_VISION_ENDPOINT, "application/json", bytes.NewBuffer(jR))
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(rawResp.Body)
	if err != nil {
		return nil, err
	}

	//log.Println("Body:", string(body))

	var resp *AnnotateResponses

	err = json.Unmarshal(body, &resp)
	if err != nil {
		return nil, err
	}

	return resp, nil
}
<commit_msg>fix struct tag<commit_after>package glasses

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

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
)

const (
	CLOUD_VISION_ENDPOINT = "https://vision.googleapis.com/v1alpha1/images:annotate"
)

type Glasses struct {
	Client *http.Client
}

type CloudVisionRequest struct {
	Requests []*AnnotateRequest `json:"requests"`
	User     string             `json:"user"`
}

type AnnotateRequest struct {
	Image        *Image        `json:"image"`
	Features     []Feature     `json:"features"`
	ImageContext *ImageContext `json:"imageContext,omitempty"`
}

type Feature struct {
	Type       string `json:"type"`
	MaxResults int    `json:"maxResults"`
}

type ImageContext struct {
	LatLongRect                 interface{} `json:"latLongRect"`
	ImageContextSearchExtension interface{} `json:"imageContextSearchExtension"`
}

//type AnnotateResponse struct {
//FaceAnnotations      []FaceAnnotation     `json:"faceAnnotations"`
//LandmarkAnnotations  []LandmarkAnnotation `json:"landmarkAnnotation"`
//LogoAnnotations      []LogoAnnotation     `json:"logoAnnotations"`
//LabelAnnotations     []LabelAnnotation    `json:"labelAnnotations"`
//TextAnnotations      []TextAnnotation     `json:"textAnnotations"`
//SafeSearchAnnotation SafeSearchAnnotation `json:"safeSearchAnnotation"`
//SuggestAnnotations   []SuggestAnnotation  `json:"suggestAnnotations"`
//QueryAnnotation      QueryAnnotation      `json:"queryAnnotation"`
//Error                Status               `json:"error"`
//}

type AnnotateResponses struct {
	Responses []AnnotateResponse `json:"responses"`
}

type AnnotateResponse struct {
	FaceAnnotations      []interface{} `json:"faceAnnotations"`
	LandmarkAnnotations  []interface{} `json:"landmarkAnnotation"`
	LogoAnnotations      []interface{} `json:"logoAnnotations"`
	LabelAnnotations     []interface{} `json:"labelAnnotations"`
	TextAnnotations      []interface{} `json:"textAnnotations"`
	SafeSearchAnnotation interface{}   `json:"safeSearchAnnotation"`
	SuggestAnnotations   []interface{} `json:"suggestAnnotations"`
	QueryAnnotation      interface{}   `json:"queryAnnotation"`
	Error                interface{}   `json:"error"`
}

func NewGlasses() (*Glasses, error) {
	client, err := google.DefaultClient(oauth2.NoContext, "https://www.googleapis.com/auth/cloud-platform")
	if err != nil {
		return nil, err
	}

	return &Glasses{client}, nil
}

func (g *Glasses) Do(r *CloudVisionRequest) (*AnnotateResponses, error) {

	jR, err := json.Marshal(r)
	if err != nil {
		return nil, err
	}

	rawResp, err := g.Client.Post(CLOUD_VISION_ENDPOINT, "application/json", bytes.NewBuffer(jR))
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(rawResp.Body)
	if err != nil {
		return nil, err
	}

	//log.Println("Body:", string(body))

	var resp *AnnotateResponses

	err = json.Unmarshal(body, &resp)
	if err != nil {
		return nil, err
	}

	return resp, nil
}
<|endoftext|>


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

package buf

import (
	"context"

	"github.com/bufbuild/buf/internal/pkg/app/appcmd"
	"github.com/bufbuild/buf/internal/pkg/app/appflag"
)

const version = "0.16.0-dev"

// Main is the main.
func Main(use string, options ...RootCommandOption) {
	appcmd.Main(context.Background(), newRootCommand(use, options...), version)
}

// RootCommandOption is an option for a root Command.
type RootCommandOption func(*appcmd.Command, appflag.Builder)
<commit_msg>Update to v0.16.0<commit_after>// Copyright 2020 Buf Technologies Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package buf

import (
	"context"

	"github.com/bufbuild/buf/internal/pkg/app/appcmd"
	"github.com/bufbuild/buf/internal/pkg/app/appflag"
)

const version = "0.16.0"

// Main is the main.
func Main(use string, options ...RootCommandOption) {
	appcmd.Main(context.Background(), newRootCommand(use, options...), version)
}

// RootCommandOption is an option for a root Command.
type RootCommandOption func(*appcmd.Command, appflag.Builder)
<|endoftext|>


<commit_before>package console

import (
	"errors"
	"fmt"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/cenkalti/rain/internal/jsonutil"
	"github.com/cenkalti/rain/rainrpc"
	"github.com/cenkalti/rain/torrent"
	"github.com/jroimartin/gocui"
)

const (
	// tabs
	general int = iota
	trackers
	peers
)

type Console struct {
	client          *rainrpc.Client
	torrents        []rainrpc.Torrent
	errTorrents     error
	selectedID      uint64
	selectedTab     int
	stats           torrent.Stats
	trackers        []torrent.Tracker
	peers           []torrent.Peer
	errDetails      error
	m               sync.Mutex
	updateTorrentsC chan struct{}
	updateDetailsC  chan struct{}
}

func New(clt *rainrpc.Client) *Console {
	return &Console{
		client:          clt,
		updateTorrentsC: make(chan struct{}),
		updateDetailsC:  make(chan struct{}),
	}
}

func (c *Console) Run() error {
	g, err := gocui.NewGui(gocui.OutputNormal)
	if err != nil {
		return err
	}
	defer g.Close()

	g.SetManagerFunc(c.layout)

	g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit)
	g.SetKeybinding("", 'q', gocui.ModNone, quit)
	g.SetKeybinding("torrents", 'j', gocui.ModNone, c.cursorDown)
	g.SetKeybinding("torrents", 'k', gocui.ModNone, c.cursorUp)
	g.SetKeybinding("torrents", 'R', gocui.ModNone, c.removeTorrent)
	g.SetKeybinding("torrents", 's', gocui.ModNone, c.startTorrent)
	g.SetKeybinding("torrents", 'S', gocui.ModNone, c.stopTorrent)
	g.SetKeybinding("torrents", gocui.KeyCtrlG, gocui.ModNone, c.switchGeneral)
	g.SetKeybinding("torrents", gocui.KeyCtrlT, gocui.ModNone, c.switchTrackers)
	g.SetKeybinding("torrents", gocui.KeyCtrlP, gocui.ModNone, c.switchPeers)

	go c.updateLoop(g)

	err = g.MainLoop()
	if err == gocui.ErrQuit {
		err = nil
	}
	return err
}

func (c *Console) layout(g *gocui.Gui) error {
	err := c.drawTorrents(g)
	if err != nil {
		return err
	}
	err = c.drawDetails(g)
	if err != nil {
		return err
	}
	_, err = g.SetCurrentView("torrents")
	return err
}

func (c *Console) drawTorrents(g *gocui.Gui) error {
	c.m.Lock()
	defer c.m.Unlock()

	maxX, maxY := g.Size()
	halfY := maxY / 2
	if v, err := g.SetView("torrents", -1, -1, maxX, halfY); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}
		v.Highlight = true
		v.SelBgColor = gocui.ColorGreen
		v.SelFgColor = gocui.ColorBlack
		fmt.Fprintln(v, "loading torrents...")
	} else {
		v.Clear()
		if c.errTorrents != nil {
			fmt.Fprintln(v, "error:", c.errTorrents)
			c.selectedID = 0
		} else {
			for _, t := range c.torrents {
				fmt.Fprintf(v, "%5d %s %5d %s\n", t.ID, t.InfoHash, t.Port, t.Name)
			}
			_, cy := v.Cursor()
			_, oy := v.Origin()
			selectedRow := cy + oy
			if selectedRow < len(c.torrents) {
				c.setSelectedID(c.torrents[selectedRow].ID)
			}
		}
	}
	return nil
}

func (c *Console) drawDetails(g *gocui.Gui) error {
	c.m.Lock()
	defer c.m.Unlock()

	maxX, maxY := g.Size()
	halfY := maxY / 2
	if v, err := g.SetView("details", -1, halfY, maxX, maxY); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}
		v.Wrap = true
		fmt.Fprintln(v, "loading details...")
	} else {
		v.Clear()
		if c.errDetails != nil {
			fmt.Fprintln(v, "error:", c.errDetails)
		} else {
			switch c.selectedTab {
			case general:
				b, err := jsonutil.MarshalCompactPretty(c.stats)
				if err != nil {
					fmt.Fprintln(v, "error:", c.errDetails)
				} else {
					fmt.Fprintln(v, string(b))
				}
			case trackers:
				for i, t := range c.trackers {
					fmt.Fprintf(v, "#%d [%s] Status: %s, Seeders: %d, Leechers: %d\n", i, t.URL, t.Status, t.Seeders, t.Leechers)
				}
			case peers:
				for i, p := range c.peers {
					fmt.Fprintf(v, "#%d Addr: %s\n", i, p.Addr)
				}
			}
		}
	}
	return nil
}

func (c *Console) updateLoop(g *gocui.Gui) {
	c.updateTorrents(g)
	c.updateDetails(g)

	ticker := time.NewTicker(time.Second)
	for {
		select {
		case <-ticker.C:
			c.updateTorrents(g)
			c.updateDetails(g)
		case <-c.updateTorrentsC:
			c.updateTorrents(g)
		case <-c.updateDetailsC:
			c.updateDetails(g)
		}
	}
}

func (c *Console) updateTorrents(g *gocui.Gui) {
	resp, err := c.client.ListTorrents()

	sort.Slice(resp.Torrents, func(i, j int) bool { return resp.Torrents[i].ID < resp.Torrents[j].ID })

	c.m.Lock()
	c.torrents = resp.Torrents
	c.errTorrents = err
	if len(c.torrents) == 0 {
		c.setSelectedID(0)
	} else if c.selectedID == 0 {
		c.setSelectedID(c.torrents[0].ID)
	}
	c.m.Unlock()

	g.Update(c.drawTorrents)
}

func (c *Console) updateDetails(g *gocui.Gui) {
	c.m.Lock()
	selectedID := c.selectedID
	c.m.Unlock()

	if selectedID != 0 {
		switch c.selectedTab {
		case general:
			resp, err := c.client.GetTorrentStats(selectedID)
			c.m.Lock()
			c.stats = resp.Stats
			c.errDetails = err
			c.m.Unlock()
		case trackers:
			resp, err := c.client.GetTorrentTrackers(selectedID)
			sort.Slice(resp.Trackers, func(i, j int) bool { return strings.Compare(resp.Trackers[i].URL, resp.Trackers[j].URL) < 0 })
			c.m.Lock()
			c.trackers = resp.Trackers
			c.errDetails = err
			c.m.Unlock()
		case peers:
			resp, err := c.client.GetTorrentPeers(selectedID)
			sort.Slice(resp.Peers, func(i, j int) bool { return strings.Compare(resp.Peers[i].Addr, resp.Peers[j].Addr) < 0 })
			c.m.Lock()
			c.peers = resp.Peers
			c.errDetails = err
			c.m.Unlock()
		}
	} else {
		c.m.Lock()
		c.errDetails = errors.New("no torrent selected")
		c.m.Unlock()
	}

	g.Update(c.drawDetails)
}

func quit(g *gocui.Gui, v *gocui.View) error {
	return gocui.ErrQuit
}

func (c *Console) cursorDown(g *gocui.Gui, v *gocui.View) error {
	c.m.Lock()
	defer c.m.Unlock()

	cx, cy := v.Cursor()
	ox, oy := v.Origin()
	if cy+oy >= len(c.torrents)-1 {
		return nil
	}
	if err := v.SetCursor(cx, cy+1); err != nil {
		if err := v.SetOrigin(ox, oy+1); err != nil {
			return err
		}
	}
	row := cy + oy + 1
	if row >= 0 && row < len(c.torrents) {
		c.setSelectedID(c.torrents[row].ID)
	}
	return nil
}

func (c *Console) cursorUp(g *gocui.Gui, v *gocui.View) error {
	c.m.Lock()
	defer c.m.Unlock()

	cx, cy := v.Cursor()
	ox, oy := v.Origin()
	if cy+oy <= 0 {
		return nil
	}
	if err := v.SetCursor(cx, cy-1); err != nil && oy > 0 {
		if err := v.SetOrigin(ox, oy-1); err != nil {
			return err
		}
	}
	row := cy + oy - 1
	if row >= 0 && row < len(c.torrents) {
		c.setSelectedID(c.torrents[row].ID)
	}
	return nil
}

func (c *Console) removeTorrent(g *gocui.Gui, v *gocui.View) error {
	c.m.Lock()
	id := c.selectedID
	c.m.Unlock()

	_, err := c.client.RemoveTorrent(id)
	if err != nil {
		return err
	}
	c.triggerUpdateTorrents()
	return nil
}

func (c *Console) setSelectedID(id uint64) {
	changed := id != c.selectedID
	c.selectedID = id
	if changed {
		c.triggerUpdateDetails()
	}
}

func (c *Console) startTorrent(g *gocui.Gui, v *gocui.View) error {
	c.m.Lock()
	id := c.selectedID
	c.m.Unlock()

	_, err := c.client.StartTorrent(id)
	if err != nil {
		return err
	}
	c.triggerUpdateDetails()
	return nil
}

func (c *Console) stopTorrent(g *gocui.Gui, v *gocui.View) error {
	c.m.Lock()
	id := c.selectedID
	c.m.Unlock()

	_, err := c.client.StopTorrent(id)
	if err != nil {
		return err
	}
	c.triggerUpdateDetails()
	return nil
}

func (c *Console) switchGeneral(g *gocui.Gui, v *gocui.View) error {
	c.m.Lock()
	c.selectedTab = general
	c.m.Unlock()
	c.triggerUpdateDetails()
	return nil
}

func (c *Console) switchTrackers(g *gocui.Gui, v *gocui.View) error {
	c.m.Lock()
	c.selectedTab = trackers
	c.m.Unlock()
	c.triggerUpdateDetails()
	return nil
}

func (c *Console) switchPeers(g *gocui.Gui, v *gocui.View) error {
	c.m.Lock()
	c.selectedTab = peers
	c.m.Unlock()
	c.triggerUpdateDetails()
	return nil
}

func (c *Console) triggerUpdateDetails() {
	select {
	case c.updateDetailsC <- struct{}{}:
	default:
	}
}

func (c *Console) triggerUpdateTorrents() {
	select {
	case c.updateTorrentsC <- struct{}{}:
	default:
	}
}
<commit_msg>more responsive console<commit_after>package console

import (
	"errors"
	"fmt"
	"sort"
	"strings"
	"sync"
	"time"

	"github.com/cenkalti/rain/internal/jsonutil"
	"github.com/cenkalti/rain/rainrpc"
	"github.com/cenkalti/rain/torrent"
	"github.com/jroimartin/gocui"
)

const (
	// tabs
	general int = iota
	trackers
	peers
)

type Console struct {
	client          *rainrpc.Client
	torrents        []rainrpc.Torrent
	errTorrents     error
	selectedID      uint64
	selectedTab     int
	stats           torrent.Stats
	trackers        []torrent.Tracker
	peers           []torrent.Peer
	errDetails      error
	updatingDetails bool
	m               sync.Mutex
	updateTorrentsC chan struct{}
	updateDetailsC  chan struct{}
}

func New(clt *rainrpc.Client) *Console {
	return &Console{
		client:          clt,
		updateTorrentsC: make(chan struct{}),
		updateDetailsC:  make(chan struct{}),
	}
}

func (c *Console) Run() error {
	g, err := gocui.NewGui(gocui.OutputNormal)
	if err != nil {
		return err
	}
	defer g.Close()

	g.SetManagerFunc(c.layout)

	g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit)
	g.SetKeybinding("", 'q', gocui.ModNone, quit)
	g.SetKeybinding("torrents", 'j', gocui.ModNone, c.cursorDown)
	g.SetKeybinding("torrents", 'k', gocui.ModNone, c.cursorUp)
	g.SetKeybinding("torrents", 'R', gocui.ModNone, c.removeTorrent)
	g.SetKeybinding("torrents", 's', gocui.ModNone, c.startTorrent)
	g.SetKeybinding("torrents", 'S', gocui.ModNone, c.stopTorrent)
	g.SetKeybinding("torrents", gocui.KeyCtrlG, gocui.ModNone, c.switchGeneral)
	g.SetKeybinding("torrents", gocui.KeyCtrlT, gocui.ModNone, c.switchTrackers)
	g.SetKeybinding("torrents", gocui.KeyCtrlP, gocui.ModNone, c.switchPeers)

	go c.updateLoop(g)

	err = g.MainLoop()
	if err == gocui.ErrQuit {
		err = nil
	}
	return err
}

func (c *Console) layout(g *gocui.Gui) error {
	err := c.drawTorrents(g)
	if err != nil {
		return err
	}
	err = c.drawDetails(g)
	if err != nil {
		return err
	}
	_, err = g.SetCurrentView("torrents")
	return err
}

func (c *Console) drawTorrents(g *gocui.Gui) error {
	c.m.Lock()
	defer c.m.Unlock()

	maxX, maxY := g.Size()
	halfY := maxY / 2
	if v, err := g.SetView("torrents", -1, -1, maxX, halfY); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}
		v.Highlight = true
		v.SelBgColor = gocui.ColorGreen
		v.SelFgColor = gocui.ColorBlack
		fmt.Fprintln(v, "loading torrents...")
	} else {
		v.Clear()
		if c.errTorrents != nil {
			fmt.Fprintln(v, "error:", c.errTorrents)
			c.selectedID = 0
		} else {
			for _, t := range c.torrents {
				fmt.Fprintf(v, "%5d %s %5d %s\n", t.ID, t.InfoHash, t.Port, t.Name)
			}
			_, cy := v.Cursor()
			_, oy := v.Origin()
			selectedRow := cy + oy
			if selectedRow < len(c.torrents) {
				c.setSelectedID(c.torrents[selectedRow].ID)
			}
		}
	}
	return nil
}

func (c *Console) drawDetails(g *gocui.Gui) error {
	c.m.Lock()
	defer c.m.Unlock()

	maxX, maxY := g.Size()
	halfY := maxY / 2
	if v, err := g.SetView("details", -1, halfY, maxX, maxY); err != nil {
		if err != gocui.ErrUnknownView {
			return err
		}
		v.Wrap = true
		fmt.Fprintln(v, "loading details...")
	} else {
		v.Clear()
		if c.updatingDetails {
			fmt.Fprintln(v, "refreshing...")
			return nil
		}
		if c.errDetails != nil {
			fmt.Fprintln(v, "error:", c.errDetails)
		} else {
			switch c.selectedTab {
			case general:
				b, err := jsonutil.MarshalCompactPretty(c.stats)
				if err != nil {
					fmt.Fprintln(v, "error:", c.errDetails)
				} else {
					fmt.Fprintln(v, string(b))
				}
			case trackers:
				for i, t := range c.trackers {
					fmt.Fprintf(v, "#%d [%s] Status: %s, Seeders: %d, Leechers: %d\n", i, t.URL, t.Status, t.Seeders, t.Leechers)
				}
			case peers:
				for i, p := range c.peers {
					fmt.Fprintf(v, "#%d Addr: %s\n", i, p.Addr)
				}
			}
		}
	}
	return nil
}

func (c *Console) updateLoop(g *gocui.Gui) {
	c.updateTorrents(g)
	c.updateDetails(g)

	ticker := time.NewTicker(time.Second)
	for {
		select {
		case <-ticker.C:
			c.updateTorrents(g)
			c.updateDetails(g)
		case <-c.updateTorrentsC:
			c.updateTorrents(g)
		case <-c.updateDetailsC:
			c.updateDetails(g)
		}
	}
}

func (c *Console) updateTorrents(g *gocui.Gui) {
	resp, err := c.client.ListTorrents()

	sort.Slice(resp.Torrents, func(i, j int) bool { return resp.Torrents[i].ID < resp.Torrents[j].ID })

	c.m.Lock()
	c.torrents = resp.Torrents
	c.errTorrents = err
	if len(c.torrents) == 0 {
		c.setSelectedID(0)
	} else if c.selectedID == 0 {
		c.setSelectedID(c.torrents[0].ID)
	}
	c.m.Unlock()

	g.Update(c.drawTorrents)
}

func (c *Console) updateDetails(g *gocui.Gui) {
	c.m.Lock()
	selectedID := c.selectedID
	c.m.Unlock()

	if selectedID != 0 {
		switch c.selectedTab {
		case general:
			resp, err := c.client.GetTorrentStats(selectedID)
			c.m.Lock()
			c.stats = resp.Stats
			c.errDetails = err
			c.m.Unlock()
		case trackers:
			resp, err := c.client.GetTorrentTrackers(selectedID)
			sort.Slice(resp.Trackers, func(i, j int) bool { return strings.Compare(resp.Trackers[i].URL, resp.Trackers[j].URL) < 0 })
			c.m.Lock()
			c.trackers = resp.Trackers
			c.errDetails = err
			c.m.Unlock()
		case peers:
			resp, err := c.client.GetTorrentPeers(selectedID)
			sort.Slice(resp.Peers, func(i, j int) bool { return strings.Compare(resp.Peers[i].Addr, resp.Peers[j].Addr) < 0 })
			c.m.Lock()
			c.peers = resp.Peers
			c.errDetails = err
			c.m.Unlock()
		}
	} else {
		c.m.Lock()
		c.errDetails = errors.New("no torrent selected")
		c.m.Unlock()
	}

	c.m.Lock()
	c.updatingDetails = false
	c.m.Unlock()
	g.Update(c.drawDetails)
}

func quit(g *gocui.Gui, v *gocui.View) error {
	return gocui.ErrQuit
}

func (c *Console) cursorDown(g *gocui.Gui, v *gocui.View) error {
	c.m.Lock()
	defer c.m.Unlock()

	cx, cy := v.Cursor()
	ox, oy := v.Origin()
	if cy+oy >= len(c.torrents)-1 {
		return nil
	}
	if err := v.SetCursor(cx, cy+1); err != nil {
		if err := v.SetOrigin(ox, oy+1); err != nil {
			return err
		}
	}
	row := cy + oy + 1
	if row >= 0 && row < len(c.torrents) {
		c.updatingDetails = true
		c.setSelectedID(c.torrents[row].ID)
	}
	return nil
}

func (c *Console) cursorUp(g *gocui.Gui, v *gocui.View) error {
	c.m.Lock()
	defer c.m.Unlock()

	cx, cy := v.Cursor()
	ox, oy := v.Origin()
	if cy+oy <= 0 {
		return nil
	}
	if err := v.SetCursor(cx, cy-1); err != nil && oy > 0 {
		if err := v.SetOrigin(ox, oy-1); err != nil {
			return err
		}
	}
	row := cy + oy - 1
	if row >= 0 && row < len(c.torrents) {
		c.updatingDetails = true
		c.setSelectedID(c.torrents[row].ID)
	}
	return nil
}

func (c *Console) removeTorrent(g *gocui.Gui, v *gocui.View) error {
	c.m.Lock()
	id := c.selectedID
	c.m.Unlock()

	_, err := c.client.RemoveTorrent(id)
	if err != nil {
		return err
	}
	c.triggerUpdateTorrents()
	return nil
}

func (c *Console) setSelectedID(id uint64) {
	changed := id != c.selectedID
	c.selectedID = id
	if changed {
		c.triggerUpdateDetails()
	}
}

func (c *Console) startTorrent(g *gocui.Gui, v *gocui.View) error {
	c.m.Lock()
	id := c.selectedID
	c.m.Unlock()

	_, err := c.client.StartTorrent(id)
	if err != nil {
		return err
	}
	c.triggerUpdateDetails()
	return nil
}

func (c *Console) stopTorrent(g *gocui.Gui, v *gocui.View) error {
	c.m.Lock()
	id := c.selectedID
	c.m.Unlock()

	_, err := c.client.StopTorrent(id)
	if err != nil {
		return err
	}
	c.triggerUpdateDetails()
	return nil
}

func (c *Console) switchGeneral(g *gocui.Gui, v *gocui.View) error {
	c.m.Lock()
	c.selectedTab = general
	c.m.Unlock()
	c.triggerUpdateDetails()
	return nil
}

func (c *Console) switchTrackers(g *gocui.Gui, v *gocui.View) error {
	c.m.Lock()
	c.selectedTab = trackers
	c.m.Unlock()
	c.triggerUpdateDetails()
	return nil
}

func (c *Console) switchPeers(g *gocui.Gui, v *gocui.View) error {
	c.m.Lock()
	c.selectedTab = peers
	c.m.Unlock()
	c.triggerUpdateDetails()
	return nil
}

func (c *Console) triggerUpdateDetails() {
	select {
	case c.updateDetailsC <- struct{}{}:
	default:
	}
}

func (c *Console) triggerUpdateTorrents() {
	select {
	case c.updateTorrentsC <- struct{}{}:
	default:
	}
}
<|endoftext|>