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

package httprouter

import (
	"errors"
	"fmt"
	"net/http"
	"net/http/httptest"
	"reflect"
	"testing"
)

type mockResponseWriter struct{}

func (m *mockResponseWriter) Header() (h http.Header) {
	return http.Header{}
}

func (m *mockResponseWriter) Write(p []byte) (n int, err error) {
	return len(p), nil
}

func (m *mockResponseWriter) WriteString(s string) (n int, err error) {
	return len(s), nil
}

func (m *mockResponseWriter) WriteHeader(int) {}

func TestParams(t *testing.T) {
	ps := Params{
		Param{"param1", "value1"},
		Param{"param2", "value2"},
		Param{"param3", "value3"},
	}
	for i := range ps {
		if val := ps.ByName(ps[i].Key); val != ps[i].Value {
			t.Errorf("Wrong value for %s: Got %s; Want %s", ps[i].Key, val, ps[i].Value)
		}
	}
	if val := ps.ByName("noKey"); val != "" {
		t.Errorf("Expected empty string for not found key; got: %s", val)
	}
}

func TestRouter(t *testing.T) {
	router := New()

	routed := false
	router.Handle("GET", "/user/:name", func(w http.ResponseWriter, r *http.Request, ps Params) {
		routed = true
		want := Params{Param{"name", "gopher"}}
		if !reflect.DeepEqual(ps, want) {
			t.Fatalf("wrong wildcard values: want %v, got %v", want, ps)
		}
	})

	w := new(mockResponseWriter)

	req, _ := http.NewRequest("GET", "/user/gopher", nil)
	router.ServeHTTP(w, req)

	if !routed {
		t.Fatal("routing failed")
	}
}

type handlerStruct struct {
	handeled *bool
}

func (h handlerStruct) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	*h.handeled = true
}

func TestRouterAPI(t *testing.T) {
	var get, head, post, put, patch, delete, handler, handlerFunc bool

	httpHandler := handlerStruct{&handler}

	router := New()
	router.GET("/GET", func(w http.ResponseWriter, r *http.Request, _ Params) {
		get = true
	})
	router.HEAD("/GET", func(w http.ResponseWriter, r *http.Request, _ Params) {
		head = true
	})
	router.POST("/POST", func(w http.ResponseWriter, r *http.Request, _ Params) {
		post = true
	})
	router.PUT("/PUT", func(w http.ResponseWriter, r *http.Request, _ Params) {
		put = true
	})
	router.PATCH("/PATCH", func(w http.ResponseWriter, r *http.Request, _ Params) {
		patch = true
	})
	router.DELETE("/DELETE", func(w http.ResponseWriter, r *http.Request, _ Params) {
		delete = true
	})
	router.Handler("GET", "/Handler", httpHandler)
	router.HandlerFunc("GET", "/HandlerFunc", func(w http.ResponseWriter, r *http.Request) {
		handlerFunc = true
	})

	w := new(mockResponseWriter)

	r, _ := http.NewRequest("GET", "/GET", nil)
	router.ServeHTTP(w, r)
	if !get {
		t.Error("routing GET failed")
	}

	r, _ = http.NewRequest("HEAD", "/GET", nil)
	router.ServeHTTP(w, r)
	if !head {
		t.Error("routing HEAD failed")
	}

	r, _ = http.NewRequest("POST", "/POST", nil)
	router.ServeHTTP(w, r)
	if !post {
		t.Error("routing POST failed")
	}

	r, _ = http.NewRequest("PUT", "/PUT", nil)
	router.ServeHTTP(w, r)
	if !put {
		t.Error("routing PUT failed")
	}

	r, _ = http.NewRequest("PATCH", "/PATCH", nil)
	router.ServeHTTP(w, r)
	if !patch {
		t.Error("routing PATCH failed")
	}

	r, _ = http.NewRequest("DELETE", "/DELETE", nil)
	router.ServeHTTP(w, r)
	if !delete {
		t.Error("routing DELETE failed")
	}

	r, _ = http.NewRequest("GET", "/Handler", nil)
	router.ServeHTTP(w, r)
	if !handler {
		t.Error("routing Handler failed")
	}

	r, _ = http.NewRequest("GET", "/HandlerFunc", nil)
	router.ServeHTTP(w, r)
	if !handlerFunc {
		t.Error("routing HandlerFunc failed")
	}
}

func TestRouterRoot(t *testing.T) {
	router := New()
	recv := catchPanic(func() {
		router.GET("noSlashRoot", nil)
	})
	if recv == nil {
		t.Fatal("registering path not beginning with '/' did not panic")
	}
}

func TestRouterNotFound(t *testing.T) {
	handlerFunc := func(_ http.ResponseWriter, _ *http.Request, _ Params) {}

	router := New()
	router.GET("/path", handlerFunc)
	router.GET("/dir/", handlerFunc)
	router.GET("/", handlerFunc)

	testRoutes := []struct {
		route  string
		code   int
		header string
	}{
		{"/path/", 301, "map[Location:[/path]]"},   // TSR -/
		{"/dir", 301, "map[Location:[/dir/]]"},     // TSR +/
		{"/dir", 301, "map[Location:[/dir/]]"},     // TSR +/
		{"/PATH", 301, "map[Location:[/path]]"},    // Fixed Case
		{"/DIR/", 301, "map[Location:[/dir/]]"},    // Fixed Case
		{"/PATH/", 301, "map[Location:[/path]]"},   // Fixed Case -/
		{"/DIR", 301, "map[Location:[/dir/]]"},     // Fixed Case +/
		{"/../path", 301, "map[Location:[/path]]"}, // CleanPath
		{"/nope", 404, ""},                         // NotFound
	}
	for _, tr := range testRoutes {
		r, _ := http.NewRequest("GET", tr.route, nil)
		w := httptest.NewRecorder()
		router.ServeHTTP(w, r)
		if !(w.Code == tr.code && (w.Code == 404 || fmt.Sprint(w.Header()) == tr.header)) {
			t.Errorf("NotFound handling route %s failed: Code=%d, Header=%v", tr.route, w.Code, w.Header())
		}
	}

	// Test custom not found handler
	var notFound bool
	router.NotFound = func(rw http.ResponseWriter, r *http.Request) {
		rw.WriteHeader(404)
		notFound = true
	}
	r, _ := http.NewRequest("GET", "/nope", nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, r)
	if !(w.Code == 404 && notFound == true) {
		t.Errorf("Custom NotFound handler failed: Code=%d, Header=%v", w.Code, w.Header())
	}

	// Test other method than GET (want 307 instead of 301)
	router.PATCH("/path", handlerFunc)
	r, _ = http.NewRequest("PATCH", "/path/", nil)
	w = httptest.NewRecorder()
	router.ServeHTTP(w, r)
	if !(w.Code == 307 && fmt.Sprint(w.Header()) == "map[Location:[/path]]") {
		t.Errorf("Custom NotFound handler failed: Code=%d, Header=%v", w.Code, w.Header())
	}

	// Test special case where no node for the prefix "/" exists
	router = New()
	router.GET("/a", handlerFunc)
	r, _ = http.NewRequest("GET", "/", nil)
	w = httptest.NewRecorder()
	router.ServeHTTP(w, r)
	if !(w.Code == 404) {
		t.Errorf("NotFound handling route / failed: Code=%d", w.Code)
	}
}

func TestRouterPanicHandler(t *testing.T) {
	router := New()
	panicHandled := false

	router.PanicHandler = func(rw http.ResponseWriter, r *http.Request, p interface{}) {
		panicHandled = true
	}

	router.Handle("PUT", "/user/:name", func(_ http.ResponseWriter, _ *http.Request, _ Params) {
		panic("oops!")
	})

	w := new(mockResponseWriter)
	req, _ := http.NewRequest("PUT", "/user/gopher", nil)

	defer func() {
		if rcv := recover(); rcv != nil {
			t.Fatal("handling panic failed")
		}
	}()

	router.ServeHTTP(w, req)

	if !panicHandled {
		t.Fatal("simulating failed")
	}
}

func TestRouterLookup(t *testing.T) {
	routed := false
	wantHandle := func(_ http.ResponseWriter, _ *http.Request, _ Params) {
		routed = true
	}
	wantParams := Params{Param{"name", "gopher"}}

	router := New()

	// try empty router first
	handle, _, tsr := router.Lookup("GET", "/nope")
	if handle != nil {
		t.Fatalf("Got handle for unregistered pattern: %v", handle)
	}
	if tsr {
		t.Error("Got wrong TSR recommendation!")
	}

	// insert route and try again
	router.GET("/user/:name", wantHandle)

	handle, params, tsr := router.Lookup("GET", "/user/gopher")
	if handle == nil {
		t.Fatal("Got no handle!")
	} else {
		handle(nil, nil, nil)
		if !routed {
			t.Fatal("Routing failed!")
		}
	}

	if !reflect.DeepEqual(params, wantParams) {
		t.Fatalf("Wrong parameter values: want %v, got %v", wantParams, params)
	}

	handle, _, tsr = router.Lookup("GET", "/user/gopher/")
	if handle != nil {
		t.Fatalf("Got handle for unregistered pattern: %v", handle)
	}
	if !tsr {
		t.Error("Got no TSR recommendation!")
	}

	handle, _, tsr = router.Lookup("GET", "/nope")
	if handle != nil {
		t.Fatalf("Got handle for unregistered pattern: %v", handle)
	}
	if tsr {
		t.Error("Got wrong TSR recommendation!")
	}
}

type mockFileSystem struct {
	opened bool
}

func (mfs *mockFileSystem) Open(name string) (http.File, error) {
	mfs.opened = true
	return nil, errors.New("this is just a mock")
}

func TestRouterServeFiles(t *testing.T) {
	router := New()
	mfs := &mockFileSystem{}

	recv := catchPanic(func() {
		router.ServeFiles("/noFilepath", mfs)
	})
	if recv == nil {
		t.Fatal("registering path not ending with '*filepath' did not panic")
	}

	router.ServeFiles("/*filepath", mfs)
	w := new(mockResponseWriter)
	r, _ := http.NewRequest("GET", "/favicon.ico", nil)
	router.ServeHTTP(w, r)
	if !mfs.opened {
		t.Error("serving file failed")
	}
}
<commit_msg>Fix empty path TSR test case<commit_after>// Copyright 2013 Julien Schmidt. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file.

package httprouter

import (
	"errors"
	"fmt"
	"net/http"
	"net/http/httptest"
	"reflect"
	"testing"
)

type mockResponseWriter struct{}

func (m *mockResponseWriter) Header() (h http.Header) {
	return http.Header{}
}

func (m *mockResponseWriter) Write(p []byte) (n int, err error) {
	return len(p), nil
}

func (m *mockResponseWriter) WriteString(s string) (n int, err error) {
	return len(s), nil
}

func (m *mockResponseWriter) WriteHeader(int) {}

func TestParams(t *testing.T) {
	ps := Params{
		Param{"param1", "value1"},
		Param{"param2", "value2"},
		Param{"param3", "value3"},
	}
	for i := range ps {
		if val := ps.ByName(ps[i].Key); val != ps[i].Value {
			t.Errorf("Wrong value for %s: Got %s; Want %s", ps[i].Key, val, ps[i].Value)
		}
	}
	if val := ps.ByName("noKey"); val != "" {
		t.Errorf("Expected empty string for not found key; got: %s", val)
	}
}

func TestRouter(t *testing.T) {
	router := New()

	routed := false
	router.Handle("GET", "/user/:name", func(w http.ResponseWriter, r *http.Request, ps Params) {
		routed = true
		want := Params{Param{"name", "gopher"}}
		if !reflect.DeepEqual(ps, want) {
			t.Fatalf("wrong wildcard values: want %v, got %v", want, ps)
		}
	})

	w := new(mockResponseWriter)

	req, _ := http.NewRequest("GET", "/user/gopher", nil)
	router.ServeHTTP(w, req)

	if !routed {
		t.Fatal("routing failed")
	}
}

type handlerStruct struct {
	handeled *bool
}

func (h handlerStruct) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	*h.handeled = true
}

func TestRouterAPI(t *testing.T) {
	var get, head, post, put, patch, delete, handler, handlerFunc bool

	httpHandler := handlerStruct{&handler}

	router := New()
	router.GET("/GET", func(w http.ResponseWriter, r *http.Request, _ Params) {
		get = true
	})
	router.HEAD("/GET", func(w http.ResponseWriter, r *http.Request, _ Params) {
		head = true
	})
	router.POST("/POST", func(w http.ResponseWriter, r *http.Request, _ Params) {
		post = true
	})
	router.PUT("/PUT", func(w http.ResponseWriter, r *http.Request, _ Params) {
		put = true
	})
	router.PATCH("/PATCH", func(w http.ResponseWriter, r *http.Request, _ Params) {
		patch = true
	})
	router.DELETE("/DELETE", func(w http.ResponseWriter, r *http.Request, _ Params) {
		delete = true
	})
	router.Handler("GET", "/Handler", httpHandler)
	router.HandlerFunc("GET", "/HandlerFunc", func(w http.ResponseWriter, r *http.Request) {
		handlerFunc = true
	})

	w := new(mockResponseWriter)

	r, _ := http.NewRequest("GET", "/GET", nil)
	router.ServeHTTP(w, r)
	if !get {
		t.Error("routing GET failed")
	}

	r, _ = http.NewRequest("HEAD", "/GET", nil)
	router.ServeHTTP(w, r)
	if !head {
		t.Error("routing HEAD failed")
	}

	r, _ = http.NewRequest("POST", "/POST", nil)
	router.ServeHTTP(w, r)
	if !post {
		t.Error("routing POST failed")
	}

	r, _ = http.NewRequest("PUT", "/PUT", nil)
	router.ServeHTTP(w, r)
	if !put {
		t.Error("routing PUT failed")
	}

	r, _ = http.NewRequest("PATCH", "/PATCH", nil)
	router.ServeHTTP(w, r)
	if !patch {
		t.Error("routing PATCH failed")
	}

	r, _ = http.NewRequest("DELETE", "/DELETE", nil)
	router.ServeHTTP(w, r)
	if !delete {
		t.Error("routing DELETE failed")
	}

	r, _ = http.NewRequest("GET", "/Handler", nil)
	router.ServeHTTP(w, r)
	if !handler {
		t.Error("routing Handler failed")
	}

	r, _ = http.NewRequest("GET", "/HandlerFunc", nil)
	router.ServeHTTP(w, r)
	if !handlerFunc {
		t.Error("routing HandlerFunc failed")
	}
}

func TestRouterRoot(t *testing.T) {
	router := New()
	recv := catchPanic(func() {
		router.GET("noSlashRoot", nil)
	})
	if recv == nil {
		t.Fatal("registering path not beginning with '/' did not panic")
	}
}

func TestRouterNotFound(t *testing.T) {
	handlerFunc := func(_ http.ResponseWriter, _ *http.Request, _ Params) {}

	router := New()
	router.GET("/path", handlerFunc)
	router.GET("/dir/", handlerFunc)
	router.GET("/", handlerFunc)

	testRoutes := []struct {
		route  string
		code   int
		header string
	}{
		{"/path/", 301, "map[Location:[/path]]"},   // TSR -/
		{"/dir", 301, "map[Location:[/dir/]]"},     // TSR +/
		{"", 301, "map[Location:[/]]"},             // TSR +/
		{"/PATH", 301, "map[Location:[/path]]"},    // Fixed Case
		{"/DIR/", 301, "map[Location:[/dir/]]"},    // Fixed Case
		{"/PATH/", 301, "map[Location:[/path]]"},   // Fixed Case -/
		{"/DIR", 301, "map[Location:[/dir/]]"},     // Fixed Case +/
		{"/../path", 301, "map[Location:[/path]]"}, // CleanPath
		{"/nope", 404, ""},                         // NotFound
	}
	for _, tr := range testRoutes {
		r, _ := http.NewRequest("GET", tr.route, nil)
		w := httptest.NewRecorder()
		router.ServeHTTP(w, r)
		if !(w.Code == tr.code && (w.Code == 404 || fmt.Sprint(w.Header()) == tr.header)) {
			t.Errorf("NotFound handling route %s failed: Code=%d, Header=%v", tr.route, w.Code, w.Header())
		}
	}

	// Test custom not found handler
	var notFound bool
	router.NotFound = func(rw http.ResponseWriter, r *http.Request) {
		rw.WriteHeader(404)
		notFound = true
	}
	r, _ := http.NewRequest("GET", "/nope", nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, r)
	if !(w.Code == 404 && notFound == true) {
		t.Errorf("Custom NotFound handler failed: Code=%d, Header=%v", w.Code, w.Header())
	}

	// Test other method than GET (want 307 instead of 301)
	router.PATCH("/path", handlerFunc)
	r, _ = http.NewRequest("PATCH", "/path/", nil)
	w = httptest.NewRecorder()
	router.ServeHTTP(w, r)
	if !(w.Code == 307 && fmt.Sprint(w.Header()) == "map[Location:[/path]]") {
		t.Errorf("Custom NotFound handler failed: Code=%d, Header=%v", w.Code, w.Header())
	}

	// Test special case where no node for the prefix "/" exists
	router = New()
	router.GET("/a", handlerFunc)
	r, _ = http.NewRequest("GET", "/", nil)
	w = httptest.NewRecorder()
	router.ServeHTTP(w, r)
	if !(w.Code == 404) {
		t.Errorf("NotFound handling route / failed: Code=%d", w.Code)
	}
}

func TestRouterPanicHandler(t *testing.T) {
	router := New()
	panicHandled := false

	router.PanicHandler = func(rw http.ResponseWriter, r *http.Request, p interface{}) {
		panicHandled = true
	}

	router.Handle("PUT", "/user/:name", func(_ http.ResponseWriter, _ *http.Request, _ Params) {
		panic("oops!")
	})

	w := new(mockResponseWriter)
	req, _ := http.NewRequest("PUT", "/user/gopher", nil)

	defer func() {
		if rcv := recover(); rcv != nil {
			t.Fatal("handling panic failed")
		}
	}()

	router.ServeHTTP(w, req)

	if !panicHandled {
		t.Fatal("simulating failed")
	}
}

func TestRouterLookup(t *testing.T) {
	routed := false
	wantHandle := func(_ http.ResponseWriter, _ *http.Request, _ Params) {
		routed = true
	}
	wantParams := Params{Param{"name", "gopher"}}

	router := New()

	// try empty router first
	handle, _, tsr := router.Lookup("GET", "/nope")
	if handle != nil {
		t.Fatalf("Got handle for unregistered pattern: %v", handle)
	}
	if tsr {
		t.Error("Got wrong TSR recommendation!")
	}

	// insert route and try again
	router.GET("/user/:name", wantHandle)

	handle, params, tsr := router.Lookup("GET", "/user/gopher")
	if handle == nil {
		t.Fatal("Got no handle!")
	} else {
		handle(nil, nil, nil)
		if !routed {
			t.Fatal("Routing failed!")
		}
	}

	if !reflect.DeepEqual(params, wantParams) {
		t.Fatalf("Wrong parameter values: want %v, got %v", wantParams, params)
	}

	handle, _, tsr = router.Lookup("GET", "/user/gopher/")
	if handle != nil {
		t.Fatalf("Got handle for unregistered pattern: %v", handle)
	}
	if !tsr {
		t.Error("Got no TSR recommendation!")
	}

	handle, _, tsr = router.Lookup("GET", "/nope")
	if handle != nil {
		t.Fatalf("Got handle for unregistered pattern: %v", handle)
	}
	if tsr {
		t.Error("Got wrong TSR recommendation!")
	}
}

type mockFileSystem struct {
	opened bool
}

func (mfs *mockFileSystem) Open(name string) (http.File, error) {
	mfs.opened = true
	return nil, errors.New("this is just a mock")
}

func TestRouterServeFiles(t *testing.T) {
	router := New()
	mfs := &mockFileSystem{}

	recv := catchPanic(func() {
		router.ServeFiles("/noFilepath", mfs)
	})
	if recv == nil {
		t.Fatal("registering path not ending with '*filepath' did not panic")
	}

	router.ServeFiles("/*filepath", mfs)
	w := new(mockResponseWriter)
	r, _ := http.NewRequest("GET", "/favicon.ico", nil)
	router.ServeHTTP(w, r)
	if !mfs.opened {
		t.Error("serving file failed")
	}
}
<|endoftext|>


<commit_before>package controller

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/Sirupsen/logrus"
	"github.com/andygrunwald/perseus/config"
	"github.com/andygrunwald/perseus/dependency"
	"github.com/andygrunwald/perseus/dependency/repository"
	"github.com/andygrunwald/perseus/downloader"
	"github.com/andygrunwald/perseus/types/set"
)

// MirrorController reflects the business logic and the Command interface to mirror all configured packages.
// This command is independent from an human interface (CLI, HTTP, etc.)
// The human interfaces will interact with this command.
type MirrorController struct {
	// Config is the main medusa configuration
	Config *config.Medusa
	// Log represents a logger to log messages
	Log logrus.FieldLogger
	// NumOfWorker is the number of worker used for concurrent actions (like resolving the dependency tree)
	NumOfWorker int

	wg sync.WaitGroup
}

// Run is the business logic of MirrorCommand.
func (c *MirrorController) Run() error {
	c.wg = sync.WaitGroup{}
	repos := set.New()

	// Get list of manual entered repositories
	// and add them to the set
	repoList, err := c.Config.GetNamesOfRepositories()
	if err != nil {
		if config.IsNoRepositories(err) {
			c.Log.WithError(err).Info("Configuration")
		} else {
			c.Log.WithError(err).Info("")
		}
	}

	for _, r := range repoList {
		repos.Add(r)
	}

	// Get all required repositories and resolve those dependencies
	pURL := "https://packagist.org/"
	packagistClient, err := repository.NewPackagist(pURL, nil)
	if err != nil {
		c.Log.WithError(err).Info("")
	}

	// Lets get a dependency resolver.
	// If we can't bootstrap one, we are lost anyway.
	// We set the queue length to the number of workers + 1. Why?
	// With this every worker has work, when the queue is filled.
	// During the add command, this is enough in most of the cases.
	d, err := dependency.NewComposerResolver(c.NumOfWorker, packagistClient)
	if err != nil {
		return err
	}
	results := d.GetResultStream()

	require := c.Config.GetRequire()
	// Loop over the packages and add them
	l := []*dependency.Package{}
	for _, r := range require {
		p, _ := dependency.NewPackage(r, "")
		l = append(l, p)
	}

	go d.Resolve(l)

	// Finally we collect all the results of the work.
	for p := range results {
		if p.Error != nil {
			c.Log.WithError(p.Error)
			continue
		}

		repos.Add(p.Package)
	}

	c.Log.WithFields(logrus.Fields{
		"amountPackages": repos.Len(),
		"amountWorker":   c.NumOfWorker,
	}).Info("Start concurrent download process")
	loader, err := downloader.NewGitDownloader(c.NumOfWorker, c.Config.GetString("repodir"))
	if err != nil {
		return err
	}

	loaderResults := loader.GetResultStream()
	flatten := repos.Flatten()
	loaderList := make([]*dependency.Package, 0, len(flatten))
	for _, item := range repos.Flatten() {
		loaderList = append(loaderList, item.(*dependency.Package))
	}
	loader.Download(loaderList)

	var satisRepositories []string
	for i := 1; i <= int(repos.Len()); i++ {
		v := <-loaderResults
		if v.Error != nil {
			if os.IsExist(v.Error) {
				c.Log.WithFields(logrus.Fields{
					"package": v.Package.Name,
				}).Info("Package exists on disk. Try updating it instead. Skipping.")
			} else {
				c.Log.WithFields(logrus.Fields{
					"package": v.Package.Name,
				}).WithError(v.Error).Info("Error while mirroring package")
				// If we have an error, we don't need to add it to satis repositories
				continue
			}
		} else {
			c.Log.WithFields(logrus.Fields{
				"package": v.Package.Name,
			}).Info("Mirroring of package successful")
		}

		satisRepositories = append(satisRepositories, c.getLocalURLForRepository(v.Package.Name))
	}
	loader.Close()

	// And as a final step, write the satis configuration
	err = c.writeSatisConfig(satisRepositories...)
	return err
}

func (c *MirrorController) getLocalURLForRepository(p string) string {
	var r string

	satisURL := c.Config.GetString("satisurl")
	repoDir := c.Config.GetString("repodir")

	if len(satisURL) > 0 {
		r = fmt.Sprintf("%s/%s.git", satisURL, p)
	} else {
		t := fmt.Sprintf("%s/%s.git", repoDir, p)
		t = strings.TrimLeft(filepath.Clean(t), "/")
		r = fmt.Sprintf("file:///%s", t)
	}

	return r
}

func (c *MirrorController) writeSatisConfig(satisRepositories ...string) error {
	// Write Satis file
	satisConfig := c.Config.GetString("satisconfig")
	if len(satisConfig) == 0 {
		c.Log.Info("No Satis configuration specified. Skipping to write a satis configuration.")
		return nil
	}

	satisContent, err := ioutil.ReadFile(satisConfig)
	if err != nil {
		return fmt.Errorf("Can't read Satis configuration %s: %s", satisConfig, err)
	}

	j, err := config.NewJSONProvider(satisContent)
	if err != nil {
		return fmt.Errorf("Error while creating JSONProvider: %s", err)
	}

	s, err := config.NewSatis(j)
	if err != nil {
		return fmt.Errorf("Error while creating Satis object: %s", err)
	}

	s.AddRepositories(satisRepositories...)
	err = s.WriteFile(satisConfig, 0644)
	if err != nil {
		return fmt.Errorf("Writing Satis configuration to %s failed: %s", satisConfig, err)
	}

	c.Log.WithFields(logrus.Fields{
		"path": satisConfig,
	}).Info("Satis configuration successful written")
	return nil
}
<commit_msg>Extended logging when we run into an error while resolving a package dependency<commit_after>package controller

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/Sirupsen/logrus"
	"github.com/andygrunwald/perseus/config"
	"github.com/andygrunwald/perseus/dependency"
	"github.com/andygrunwald/perseus/dependency/repository"
	"github.com/andygrunwald/perseus/downloader"
	"github.com/andygrunwald/perseus/types/set"
)

// MirrorController reflects the business logic and the Command interface to mirror all configured packages.
// This command is independent from an human interface (CLI, HTTP, etc.)
// The human interfaces will interact with this command.
type MirrorController struct {
	// Config is the main medusa configuration
	Config *config.Medusa
	// Log represents a logger to log messages
	Log logrus.FieldLogger
	// NumOfWorker is the number of worker used for concurrent actions (like resolving the dependency tree)
	NumOfWorker int

	wg sync.WaitGroup
}

// Run is the business logic of MirrorCommand.
func (c *MirrorController) Run() error {
	c.wg = sync.WaitGroup{}
	repos := set.New()

	// Get list of manual entered repositories
	// and add them to the set
	repoList, err := c.Config.GetNamesOfRepositories()
	if err != nil {
		if config.IsNoRepositories(err) {
			c.Log.WithError(err).Info("Configuration")
		} else {
			c.Log.WithError(err).Info("")
		}
	}

	for _, r := range repoList {
		repos.Add(r)
	}

	// Get all required repositories and resolve those dependencies
	pURL := "https://packagist.org/"
	packagistClient, err := repository.NewPackagist(pURL, nil)
	if err != nil {
		c.Log.WithError(err).Info("")
	}

	// Lets get a dependency resolver.
	// If we can't bootstrap one, we are lost anyway.
	// We set the queue length to the number of workers + 1. Why?
	// With this every worker has work, when the queue is filled.
	// During the add command, this is enough in most of the cases.
	d, err := dependency.NewComposerResolver(c.NumOfWorker, packagistClient)
	if err != nil {
		return err
	}
	results := d.GetResultStream()

	require := c.Config.GetRequire()
	// Loop over the packages and add them
	l := []*dependency.Package{}
	for _, r := range require {
		p, _ := dependency.NewPackage(r, "")
		l = append(l, p)
	}

	go d.Resolve(l)

	// Finally we collect all the results of the work.
	for p := range results {
		if p.Error != nil {
			c.Log.WithFields(logrus.Fields{
				"package": p.Package.Name,
				"responseCode": p.Response.StatusCode,
			}).WithError(p.Error).Info("Error while resolving dependencies of package")
			continue
		}

		repos.Add(p.Package)
	}

	c.Log.WithFields(logrus.Fields{
		"amountPackages": repos.Len(),
		"amountWorker":   c.NumOfWorker,
	}).Info("Start concurrent download process")
	loader, err := downloader.NewGitDownloader(c.NumOfWorker, c.Config.GetString("repodir"))
	if err != nil {
		return err
	}

	loaderResults := loader.GetResultStream()
	flatten := repos.Flatten()
	loaderList := make([]*dependency.Package, 0, len(flatten))
	for _, item := range repos.Flatten() {
		loaderList = append(loaderList, item.(*dependency.Package))
	}
	loader.Download(loaderList)

	var satisRepositories []string
	for i := 1; i <= int(repos.Len()); i++ {
		v := <-loaderResults
		if v.Error != nil {
			if os.IsExist(v.Error) {
				c.Log.WithFields(logrus.Fields{
					"package": v.Package.Name,
				}).Info("Package exists on disk. Try updating it instead. Skipping.")
			} else {
				c.Log.WithFields(logrus.Fields{
					"package": v.Package.Name,
				}).WithError(v.Error).Info("Error while mirroring package")
				// If we have an error, we don't need to add it to satis repositories
				continue
			}
		} else {
			c.Log.WithFields(logrus.Fields{
				"package": v.Package.Name,
			}).Info("Mirroring of package successful")
		}

		satisRepositories = append(satisRepositories, c.getLocalURLForRepository(v.Package.Name))
	}
	loader.Close()

	// And as a final step, write the satis configuration
	err = c.writeSatisConfig(satisRepositories...)
	return err
}

func (c *MirrorController) getLocalURLForRepository(p string) string {
	var r string

	satisURL := c.Config.GetString("satisurl")
	repoDir := c.Config.GetString("repodir")

	if len(satisURL) > 0 {
		r = fmt.Sprintf("%s/%s.git", satisURL, p)
	} else {
		t := fmt.Sprintf("%s/%s.git", repoDir, p)
		t = strings.TrimLeft(filepath.Clean(t), "/")
		r = fmt.Sprintf("file:///%s", t)
	}

	return r
}

func (c *MirrorController) writeSatisConfig(satisRepositories ...string) error {
	// Write Satis file
	satisConfig := c.Config.GetString("satisconfig")
	if len(satisConfig) == 0 {
		c.Log.Info("No Satis configuration specified. Skipping to write a satis configuration.")
		return nil
	}

	satisContent, err := ioutil.ReadFile(satisConfig)
	if err != nil {
		return fmt.Errorf("Can't read Satis configuration %s: %s", satisConfig, err)
	}

	j, err := config.NewJSONProvider(satisContent)
	if err != nil {
		return fmt.Errorf("Error while creating JSONProvider: %s", err)
	}

	s, err := config.NewSatis(j)
	if err != nil {
		return fmt.Errorf("Error while creating Satis object: %s", err)
	}

	s.AddRepositories(satisRepositories...)
	err = s.WriteFile(satisConfig, 0644)
	if err != nil {
		return fmt.Errorf("Writing Satis configuration to %s failed: %s", satisConfig, err)
	}

	c.Log.WithFields(logrus.Fields{
		"path": satisConfig,
	}).Info("Satis configuration successful written")
	return nil
}
<|endoftext|>


<commit_before>package db

import (
	"sync"
	"time"

	"github.com/couchbaselabs/sync_gateway/base"
	"github.com/couchbaselabs/sync_gateway/channels"
)

var ChannelCacheMinLength = 50         // Keep at least this many entries in cache
var ChannelCacheMaxLength = 500        // Don't put more than this many entries in cache
var ChannelCacheAge = 60 * time.Second // Keep entries at least this long

const NoSeq = uint64(0x7FFFFFFFFFFFFFFF)

type channelCache struct {
	channelName string           // The channel name, duh
	context     *DatabaseContext // Database connection (used for view queries)
	logs        LogEntries       // Log entries in sequence order
	validFrom   uint64           // First sequence that logs is valid for
	lock        sync.RWMutex     // Controls access to logs, validFrom
	viewLock    sync.Mutex       // Ensures only one view query is made at a time
}

func newChannelCache(context *DatabaseContext, channelName string, validFrom uint64) *channelCache {
	return &channelCache{context: context, channelName: channelName, validFrom: validFrom}
}

// Low-level method to add a LogEntry to a single channel's cache.
func (c *channelCache) addToCache(change *LogEntry, isRemoval bool) {
	c.lock.Lock()
	defer c.lock.Unlock()

	if !isRemoval {
		c._appendChange(change)
	} else {
		removalChange := *change
		removalChange.Flags |= channels.Removed
		c._appendChange(&removalChange)
	}
	c._pruneCache()
	base.LogTo("Cache", "    #%d ==> channel %q", change.Sequence, c.channelName)
}

// Internal helper that prunes a single channel's cache. Caller MUST be holding the lock.
func (c *channelCache) _pruneCache() {
	pruned := 0
	for len(c.logs) > ChannelCacheMinLength && time.Since(c.logs[0].TimeReceived) > ChannelCacheAge {
		c.validFrom = c.logs[0].Sequence + 1
		c.logs = c.logs[1:]
		pruned++
	}
	if pruned > 0 {
		base.LogTo("Cache", "Pruned %d old entries from channel %q", pruned, c.channelName)
	}
}

func (c *channelCache) pruneCache() {
	c.lock.Lock()
	c._pruneCache()
	c.lock.Unlock()
}

// Returns all of the cached entries for sequences greater than 'since' in the given channel.
// Entries are returned in increasing-sequence order.
func (c *channelCache) getCachedChanges(options ChangesOptions) (validFrom uint64, result []*LogEntry) {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c._getCachedChanges(options)
}

func (c *channelCache) _getCachedChanges(options ChangesOptions) (validFrom uint64, result []*LogEntry) {
	// Find the first entry in the log to return:
	log := c.logs
	if len(log) == 0 {
		validFrom = c.validFrom
		return // Return nil if nothing is cached
	}
	var start int
	for start = len(log) - 1; start >= 0 && log[start].Sequence > options.Since; start-- {
	}
	start++

	if start > 0 {
		validFrom = log[start-1].Sequence + 1
	} else {
		validFrom = c.validFrom
	}

	n := len(log) - start
	if options.Limit > 0 && n > options.Limit {
		n = options.Limit
	}
	result = make([]*LogEntry, n)
	copy(result[0:], log[start:])
	return
}

// Top-level method to get all the changes in a channel since the sequence 'since'.
// If the cache doesn't go back far enough, the view will be queried.
// View query results may be fed back into the cache if there's room.
// initialSequence is used only if the cache is empty: it gives the max sequence to which the
// view should be queried, because we don't want the view query to outrun the chanceCache's
// nextSequence.
func (c *channelCache) GetChanges(options ChangesOptions) ([]*LogEntry, error) {
	// Use the cache, and return if it fulfilled the entire request:
	cacheValidFrom, resultFromCache := c.getCachedChanges(options)
	numFromCache := len(resultFromCache)
	if numFromCache > 0 || resultFromCache == nil {
		base.LogTo("Cache", "getCachedChanges(%q, %d) --> %d changes valid from #%d",
			c.channelName, options.Since, numFromCache, cacheValidFrom)
	} else if resultFromCache == nil {
		base.LogTo("Cache", "getCachedChanges(%q, %d) --> nothing cached",
			c.channelName, options.Since)
	}
	if cacheValidFrom <= options.Since+1 {
		return resultFromCache, nil
	}

	// Nope, we're going to have to backfill from the view.
	//** First acquire the _view_ lock (not the regular lock!)
	c.viewLock.Lock()
	defer c.viewLock.Unlock()

	// Another goroutine might have gotten the lock first and already queried the view and updated
	// the cache, so repeat the above:
	cacheValidFrom, resultFromCache = c._getCachedChanges(options)
	if len(resultFromCache) > numFromCache {
		base.LogTo("Cache", "2nd getCachedChanges(%q, %d) got %d more, valid from #%d!",
			c.channelName, options.Since, len(resultFromCache)-numFromCache, cacheValidFrom)
	}
	if cacheValidFrom <= options.Since+1 {
		return resultFromCache, nil
	}

	// Now query the view. We set the max sequence equal to cacheValidFrom, so we'll get one
	// overlap, which helps confirm that we've got everything.
	resultFromView, err := c.context.getChangesInChannelFromView(c.channelName, cacheValidFrom,
		options)
	if err != nil {
		return nil, err
	}

	// Cache some of the view results, if there's room in the cache:
	if len(resultFromCache) < ChannelCacheMaxLength {
		c.prependChanges(resultFromView, options.Since+1, options.Limit == 0)
	}

	result := resultFromView
	room := options.Limit - len(result)
	if (options.Limit == 0 || room > 0) && len(resultFromCache) > 0 {
		// Concatenate the view & cache results:
		if resultFromCache[0].Sequence == result[len(result)-1].Sequence {
			resultFromCache = resultFromCache[1:]
		}
		n := len(resultFromCache)
		if options.Limit > 0 && room > 0 && room < n {
			n = room
		}
		result = append(result, resultFromCache[0:n]...)
	}
	base.LogTo("Cache", "GetChangesInChannel(%q) --> %d rows", c.channelName, len(result))
	return result, nil
}

//////// LOGENTRIES:

func (c *channelCache) _adjustFirstSeq(change *LogEntry) {
	if change.Sequence < c.validFrom {
		c.validFrom = change.Sequence
	}
}

// Adds an entry to the end of an array of LogEntries.
// Any existing entry with the same DocID is removed.
func (c *channelCache) _appendChange(change *LogEntry) {
	log := c.logs
	end := len(log) - 1
	if end >= 0 {
		if change.Sequence <= log[end].Sequence {
			base.Warn("LogEntries.appendChange: out-of-order sequence #%d (last is #%d)",
				change.Sequence, log[end].Sequence)
		}
		for i := end; i >= 0; i-- {
			if log[i].DocID == change.DocID {
				copy(log[i:], log[i+1:])
				log[end] = change
				return
			}
		}
	} else {
		c._adjustFirstSeq(change)
	}
	c.logs = append(log, change)
}

// Prepends an array of entries to this one, skipping ones that I already have.
// The new array needs to overlap with my current log, i.e. must contain the same sequence as
// c.logs[0], otherwise nothing will be added because the method can't confirm that there are no
// missing sequences in between.
// Returns the number of entries actually prepended.
func (c *channelCache) prependChanges(changes LogEntries, changesValidFrom uint64, openEnded bool) int {
	c.lock.Lock()
	defer c.lock.Unlock()

	log := c.logs
	if len(log) == 0 {
		// If my cache is empty, just copy the new changes:
		if len(changes) > 0 {
			if !openEnded && changes[len(changes)-1].Sequence < c.validFrom {
				return 0 // changes might not go all the way to the current time
			}
			if excess := len(changes) - ChannelCacheMaxLength; excess > 0 {
				changes = changes[excess:]
				changesValidFrom = changes[0].Sequence
			}
			c.logs = make(LogEntries, len(changes))
			copy(c.logs, changes)
			base.LogTo("Cache", "  Initialized cache of %q with %d entries from view (#%d--#%d)",
				c.channelName, len(changes), changes[0].Sequence, changes[len(changes)-1].Sequence)
		}
		c.validFrom = changesValidFrom
		return len(changes)

	} else if len(changes) == 0 {
		if openEnded && changesValidFrom < c.validFrom {
			c.validFrom = changesValidFrom
		}
		return 0

	} else {
		// Look for an overlap, and prepend everything up to that point:
		firstSequence := log[0].Sequence
		if changes[0].Sequence <= firstSequence {
			for i := len(changes) - 1; i >= 0; i-- {
				if changes[i].Sequence == firstSequence {
					if excess := i + len(log) - ChannelCacheMaxLength; excess > 0 {
						changes = changes[excess:]
						changesValidFrom = changes[0].Sequence
						i -= excess
					}
					if i > 0 {
						newLog := make(LogEntries, 0, i+len(log))
						newLog = append(newLog, changes[0:i]...)
						newLog = append(newLog, log...)
						c.logs = newLog
						base.LogTo("Cache", "  Added %d entries from view (#%d--#%d) to cache of %q",
							i, changes[0].Sequence, changes[i-1].Sequence, c.channelName)
					}
					c.validFrom = changesValidFrom
					return i
				}
			}
		}
		return 0
	}
}
<commit_msg>Fixed an index-out-of-range panic from channelCache<commit_after>package db

import (
	"sync"
	"time"

	"github.com/couchbaselabs/sync_gateway/base"
	"github.com/couchbaselabs/sync_gateway/channels"
)

var ChannelCacheMinLength = 50         // Keep at least this many entries in cache
var ChannelCacheMaxLength = 500        // Don't put more than this many entries in cache
var ChannelCacheAge = 60 * time.Second // Keep entries at least this long

const NoSeq = uint64(0x7FFFFFFFFFFFFFFF)

type channelCache struct {
	channelName string           // The channel name, duh
	context     *DatabaseContext // Database connection (used for view queries)
	logs        LogEntries       // Log entries in sequence order
	validFrom   uint64           // First sequence that logs is valid for
	lock        sync.RWMutex     // Controls access to logs, validFrom
	viewLock    sync.Mutex       // Ensures only one view query is made at a time
}

func newChannelCache(context *DatabaseContext, channelName string, validFrom uint64) *channelCache {
	return &channelCache{context: context, channelName: channelName, validFrom: validFrom}
}

// Low-level method to add a LogEntry to a single channel's cache.
func (c *channelCache) addToCache(change *LogEntry, isRemoval bool) {
	c.lock.Lock()
	defer c.lock.Unlock()

	if !isRemoval {
		c._appendChange(change)
	} else {
		removalChange := *change
		removalChange.Flags |= channels.Removed
		c._appendChange(&removalChange)
	}
	c._pruneCache()
	base.LogTo("Cache", "    #%d ==> channel %q", change.Sequence, c.channelName)
}

// Internal helper that prunes a single channel's cache. Caller MUST be holding the lock.
func (c *channelCache) _pruneCache() {
	pruned := 0
	for len(c.logs) > ChannelCacheMinLength && time.Since(c.logs[0].TimeReceived) > ChannelCacheAge {
		c.validFrom = c.logs[0].Sequence + 1
		c.logs = c.logs[1:]
		pruned++
	}
	if pruned > 0 {
		base.LogTo("Cache", "Pruned %d old entries from channel %q", pruned, c.channelName)
	}
}

func (c *channelCache) pruneCache() {
	c.lock.Lock()
	c._pruneCache()
	c.lock.Unlock()
}

// Returns all of the cached entries for sequences greater than 'since' in the given channel.
// Entries are returned in increasing-sequence order.
func (c *channelCache) getCachedChanges(options ChangesOptions) (validFrom uint64, result []*LogEntry) {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c._getCachedChanges(options)
}

func (c *channelCache) _getCachedChanges(options ChangesOptions) (validFrom uint64, result []*LogEntry) {
	// Find the first entry in the log to return:
	log := c.logs
	if len(log) == 0 {
		validFrom = c.validFrom
		return // Return nil if nothing is cached
	}
	var start int
	for start = len(log) - 1; start >= 0 && log[start].Sequence > options.Since; start-- {
	}
	start++

	if start > 0 {
		validFrom = log[start-1].Sequence + 1
	} else {
		validFrom = c.validFrom
	}

	n := len(log) - start
	if options.Limit > 0 && n > options.Limit {
		n = options.Limit
	}
	result = make([]*LogEntry, n)
	copy(result[0:], log[start:])
	return
}

// Top-level method to get all the changes in a channel since the sequence 'since'.
// If the cache doesn't go back far enough, the view will be queried.
// View query results may be fed back into the cache if there's room.
// initialSequence is used only if the cache is empty: it gives the max sequence to which the
// view should be queried, because we don't want the view query to outrun the chanceCache's
// nextSequence.
func (c *channelCache) GetChanges(options ChangesOptions) ([]*LogEntry, error) {
	// Use the cache, and return if it fulfilled the entire request:
	cacheValidFrom, resultFromCache := c.getCachedChanges(options)
	numFromCache := len(resultFromCache)
	if numFromCache > 0 || resultFromCache == nil {
		base.LogTo("Cache", "getCachedChanges(%q, %d) --> %d changes valid from #%d",
			c.channelName, options.Since, numFromCache, cacheValidFrom)
	} else if resultFromCache == nil {
		base.LogTo("Cache", "getCachedChanges(%q, %d) --> nothing cached",
			c.channelName, options.Since)
	}
	if cacheValidFrom <= options.Since+1 {
		return resultFromCache, nil
	}

	// Nope, we're going to have to backfill from the view.
	//** First acquire the _view_ lock (not the regular lock!)
	c.viewLock.Lock()
	defer c.viewLock.Unlock()

	// Another goroutine might have gotten the lock first and already queried the view and updated
	// the cache, so repeat the above:
	cacheValidFrom, resultFromCache = c._getCachedChanges(options)
	if len(resultFromCache) > numFromCache {
		base.LogTo("Cache", "2nd getCachedChanges(%q, %d) got %d more, valid from #%d!",
			c.channelName, options.Since, len(resultFromCache)-numFromCache, cacheValidFrom)
	}
	if cacheValidFrom <= options.Since+1 {
		return resultFromCache, nil
	}

	// Now query the view. We set the max sequence equal to cacheValidFrom, so we'll get one
	// overlap, which helps confirm that we've got everything.
	resultFromView, err := c.context.getChangesInChannelFromView(c.channelName, cacheValidFrom,
		options)
	if err != nil {
		return nil, err
	}

	// Cache some of the view results, if there's room in the cache:
	if len(resultFromCache) < ChannelCacheMaxLength {
		c.prependChanges(resultFromView, options.Since+1, options.Limit == 0)
	}

	result := resultFromView
	room := options.Limit - len(result)
	if (options.Limit == 0 || room > 0) && len(resultFromCache) > 0 {
		// Concatenate the view & cache results:
		if len(result) > 0 && resultFromCache[0].Sequence == result[len(result)-1].Sequence {
			resultFromCache = resultFromCache[1:]
		}
		n := len(resultFromCache)
		if options.Limit > 0 && room > 0 && room < n {
			n = room
		}
		result = append(result, resultFromCache[0:n]...)
	}
	base.LogTo("Cache", "GetChangesInChannel(%q) --> %d rows", c.channelName, len(result))
	return result, nil
}

//////// LOGENTRIES:

func (c *channelCache) _adjustFirstSeq(change *LogEntry) {
	if change.Sequence < c.validFrom {
		c.validFrom = change.Sequence
	}
}

// Adds an entry to the end of an array of LogEntries.
// Any existing entry with the same DocID is removed.
func (c *channelCache) _appendChange(change *LogEntry) {
	log := c.logs
	end := len(log) - 1
	if end >= 0 {
		if change.Sequence <= log[end].Sequence {
			base.Warn("LogEntries.appendChange: out-of-order sequence #%d (last is #%d)",
				change.Sequence, log[end].Sequence)
		}
		for i := end; i >= 0; i-- {
			if log[i].DocID == change.DocID {
				copy(log[i:], log[i+1:])
				log[end] = change
				return
			}
		}
	} else {
		c._adjustFirstSeq(change)
	}
	c.logs = append(log, change)
}

// Prepends an array of entries to this one, skipping ones that I already have.
// The new array needs to overlap with my current log, i.e. must contain the same sequence as
// c.logs[0], otherwise nothing will be added because the method can't confirm that there are no
// missing sequences in between.
// Returns the number of entries actually prepended.
func (c *channelCache) prependChanges(changes LogEntries, changesValidFrom uint64, openEnded bool) int {
	c.lock.Lock()
	defer c.lock.Unlock()

	log := c.logs
	if len(log) == 0 {
		// If my cache is empty, just copy the new changes:
		if len(changes) > 0 {
			if !openEnded && changes[len(changes)-1].Sequence < c.validFrom {
				return 0 // changes might not go all the way to the current time
			}
			if excess := len(changes) - ChannelCacheMaxLength; excess > 0 {
				changes = changes[excess:]
				changesValidFrom = changes[0].Sequence
			}
			c.logs = make(LogEntries, len(changes))
			copy(c.logs, changes)
			base.LogTo("Cache", "  Initialized cache of %q with %d entries from view (#%d--#%d)",
				c.channelName, len(changes), changes[0].Sequence, changes[len(changes)-1].Sequence)
		}
		c.validFrom = changesValidFrom
		return len(changes)

	} else if len(changes) == 0 {
		if openEnded && changesValidFrom < c.validFrom {
			c.validFrom = changesValidFrom
		}
		return 0

	} else {
		// Look for an overlap, and prepend everything up to that point:
		firstSequence := log[0].Sequence
		if changes[0].Sequence <= firstSequence {
			for i := len(changes) - 1; i >= 0; i-- {
				if changes[i].Sequence == firstSequence {
					if excess := i + len(log) - ChannelCacheMaxLength; excess > 0 {
						changes = changes[excess:]
						changesValidFrom = changes[0].Sequence
						i -= excess
					}
					if i > 0 {
						newLog := make(LogEntries, 0, i+len(log))
						newLog = append(newLog, changes[0:i]...)
						newLog = append(newLog, log...)
						c.logs = newLog
						base.LogTo("Cache", "  Added %d entries from view (#%d--#%d) to cache of %q",
							i, changes[0].Sequence, changes[i-1].Sequence, c.channelName)
					}
					c.validFrom = changesValidFrom
					return i
				}
			}
		}
		return 0
	}
}
<|endoftext|>


<commit_before>package query

import (
	"fmt"
	"github.com/screwyprof/cafe/entity"
	"github.com/screwyprof/cafe/usecase/intf"
)

type showMenuRepository interface {
	All() map[uint8]entity.OrderedItem
}

type ShowMenuHandler struct {
	//repo entity.UserRepository
	repo showMenuRepository
}

func NewShowMenuHandler(r showMenuRepository) *ShowMenuHandler {
	return &ShowMenuHandler{repo: r}
}

func (h *ShowMenuHandler) Handle(q intf.Query, result interface{}) error {

	r, ok := result.(*ShowMenuResult)
	if !ok {
		return fmt.Errorf("invalid result given")
	}

	var (
		item  MenuItem
		items []MenuItem
	)

	for _, el := range h.repo.All() {
		item = MenuItem{
			Number:      el.MenuNumber,
			Description: el.Description,
			Price:       el.Price,
		}
		items = append(items, item)
	}

	r.Items = items
	result = r

	return nil
}
<commit_msg>Go ineffassign warnings fixed<commit_after>package query

import (
	"fmt"
	"github.com/screwyprof/cafe/entity"
	"github.com/screwyprof/cafe/usecase/intf"
)

type showMenuRepository interface {
	All() map[uint8]entity.OrderedItem
}

type ShowMenuHandler struct {
	//repo entity.UserRepository
	repo showMenuRepository
}

func NewShowMenuHandler(r showMenuRepository) *ShowMenuHandler {
	return &ShowMenuHandler{repo: r}
}

func (h *ShowMenuHandler) Handle(q intf.Query, result interface{}) error {

	r, ok := result.(*ShowMenuResult)
	if !ok {
		return fmt.Errorf("invalid result given")
	}

	var (
		item  MenuItem
		items []MenuItem
	)

	for _, el := range h.repo.All() {
		item = MenuItem{
			Number:      el.MenuNumber,
			Description: el.Description,
			Price:       el.Price,
		}
		items = append(items, item)
	}

	r.Items = items

	return nil
}
<|endoftext|>


<commit_before>package controllers

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/server-may-cry/bubble-go/models"
	"github.com/server-may-cry/bubble-go/platforms"
	"github.com/server-may-cry/bubble-go/storage"
)

type enterRequest struct {
	baseRequest
	AuthRequestPart
	AppFriends uint8  `json:"appFriends,string" binding:"required"`
	Referer    string `json:"referer" binding:"required"`
	SrcExtID   string `json:"srcExtId" binding:"required"`
}

type enterResponse struct {
	ReqMsgID               uint64     `json:"reqMsgId"`
	UserID                 uint32     `json:"userId"`
	ReachedStage01         int8       `json:"reachedStage01,uint8"`
	ReachedStage02         int8       `json:"reachedStage02,uint8"`
	ReachedSubStage01      int8       `json:"reachedSubStage01,uint8"`
	ReachedSubStage02      int8       `json:"reachedSubStage02,uint8"`
	IgnoreSavePointBlock   bool       `json:"ignoreSavePointBlock"`
	RemainingTries         int8       `json:"remainingTries,uint8"`
	Credits                int16      `json:"credits,uint16"`
	InfinityExtra00        int8       `json:"inifinityExtra00,uint8"`
	InfinityExtra01        int8       `json:"inifinityExtra01,uint8"`
	InfinityExtra02        int8       `json:"inifinityExtra02,uint8"`
	InfinityExtra03        int8       `json:"inifinityExtra03,uint8"`
	InfinityExtra04        int8       `json:"inifinityExtra04,uint8"`
	InfinityExtra05        int8       `json:"inifinityExtra05,uint8"`
	InfinityExtra06        int8       `json:"inifinityExtra06,uint8"`
	InfinityExtra07        int8       `json:"inifinityExtra07,uint8"`
	InfinityExtra08        int8       `json:"inifinityExtra08,uint8"`
	InfinityExtra09        int8       `json:"inifinityExtra09,uint8"`
	BonusCredits           int16      `json:"bonusCredits,uint16"`
	AppFriendsBonusCredits int16      `json:"appFriendsBonusCredits,uint16"`
	OfferAvailable         bool       `json:"offerAvailable"`
	FirstGame              bool       `json:"firstGame"`
	StagesProgressStat01   [8]uint32  `json:"stagesProgressStat01"`
	StagesProgressStat02   [8]uint32  `json:"stagesProgressStat02"`
	SubStagesRecordStats01 [8][]uint8 `json:"subStagesRecordStats01"`
	SubStagesRecordStats02 [8][]uint8 `json:"subStagesRecordStats02"`
}

// ReqEnter first request from client. Return user info and user progress
func ReqEnter(w http.ResponseWriter, r *http.Request) {
	request := enterRequest{}
	defer r.Body.Close()
	decoder := json.NewDecoder(r.Body)
	err := decoder.Decode(&request)
	if err != nil {
		http.Error(w, getErrBody(err), http.StatusBadRequest)
		return
	}
	ctx := r.Context()
	value := ctx.Value(User)
	var user models.User
	switch value.(type) {
	case models.User:
		user = value.(models.User)
		// time rewards logic
	case nil:
		platformID := platforms.GetByName(request.SysID)
		user = models.User{
			SysID:                   platformID,
			ExtID:                   request.ExtID,
			ReachedStage01:          0,
			ReachedSubStage01:       0,
			IgnoreSavePointBlock:    0,
			InifinityExtra00:        0,
			InifinityExtra01:        0,
			InifinityExtra02:        0,
			InifinityExtra03:        0,
			InifinityExtra04:        0,
			InifinityExtra05:        0,
			InifinityExtra06:        0,
			InifinityExtra07:        0,
			InifinityExtra08:        0,
			InifinityExtra09:        0,
			RemainingTries:          5,
			RestoreTriesAt:          0,
			Credits:                 0, // TODO
			FriendsBonusCreditsTime: time.Now().Unix(),
			// TODO ProgressStandart:        [][]int8 // json
		}
		success := storage.Gorm.NewRecord(&user)
		if !success {
			panic(fmt.Sprintf("can`t create user %v", user))
		}
	}
	log.Println(user)
	// logic
	// add users progress
	response := enterResponse{
		ReqMsgID: request.MsgID,
	}
	JSON(w, response)
}
<commit_msg>init enter response<commit_after>package controllers

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/server-may-cry/bubble-go/models"
	"github.com/server-may-cry/bubble-go/platforms"
	"github.com/server-may-cry/bubble-go/storage"
)

type enterRequest struct {
	baseRequest
	AuthRequestPart
	AppFriends uint8  `json:"appFriends,string" binding:"required"`
	Referer    string `json:"referer" binding:"required"`
	SrcExtID   string `json:"srcExtId" binding:"required"`
}

type enterResponse struct {
	ReqMsgID               uint64     `json:"reqMsgId"`
	UserID                 uint64     `json:"userId"`
	ReachedStage01         int8       `json:"reachedStage01,uint8"`
	ReachedStage02         int8       `json:"reachedStage02,uint8"`
	ReachedSubStage01      int8       `json:"reachedSubStage01,uint8"`
	ReachedSubStage02      int8       `json:"reachedSubStage02,uint8"`
	IgnoreSavePointBlock   int8       `json:"ignoreSavePointBlock,bool"`
	RemainingTries         int8       `json:"remainingTries,uint8"`
	Credits                int16      `json:"credits,uint16"`
	InfinityExtra00        int8       `json:"inifinityExtra00,uint8"`
	InfinityExtra01        int8       `json:"inifinityExtra01,uint8"`
	InfinityExtra02        int8       `json:"inifinityExtra02,uint8"`
	InfinityExtra03        int8       `json:"inifinityExtra03,uint8"`
	InfinityExtra04        int8       `json:"inifinityExtra04,uint8"`
	InfinityExtra05        int8       `json:"inifinityExtra05,uint8"`
	InfinityExtra06        int8       `json:"inifinityExtra06,uint8"`
	InfinityExtra07        int8       `json:"inifinityExtra07,uint8"`
	InfinityExtra08        int8       `json:"inifinityExtra08,uint8"`
	InfinityExtra09        int8       `json:"inifinityExtra09,uint8"`
	BonusCredits           int16      `json:"bonusCredits,uint16"`
	AppFriendsBonusCredits int16      `json:"appFriendsBonusCredits,uint16"`
	OfferAvailable         bool       `json:"offerAvailable,uint8"`
	FirstGame              bool       `json:"firstGame"`
	StagesProgressStat01   [8]uint32  `json:"stagesProgressStat01"`
	StagesProgressStat02   [8]uint32  `json:"stagesProgressStat02"`
	SubStagesRecordStats01 [8][]uint8 `json:"subStagesRecordStats01"`
	SubStagesRecordStats02 [8][]uint8 `json:"subStagesRecordStats02"`
}

// ReqEnter first request from client. Return user info and user progress
func ReqEnter(w http.ResponseWriter, r *http.Request) {
	request := enterRequest{}
	defer r.Body.Close()
	decoder := json.NewDecoder(r.Body)
	err := decoder.Decode(&request)
	if err != nil {
		http.Error(w, getErrBody(err), http.StatusBadRequest)
		return
	}
	var firstGame bool
	ctx := r.Context()
	value := ctx.Value(User)
	var user models.User
	switch value.(type) {
	case models.User:
		user = value.(models.User)
		// time rewards logic
	case nil:
		firstGame = true
		platformID := platforms.GetByName(request.SysID)
		user = models.User{
			SysID:                   platformID,
			ExtID:                   request.ExtID,
			ReachedStage01:          0,
			ReachedSubStage01:       0,
			IgnoreSavePointBlock:    0,
			InifinityExtra00:        0,
			InifinityExtra01:        0,
			InifinityExtra02:        0,
			InifinityExtra03:        0,
			InifinityExtra04:        0,
			InifinityExtra05:        0,
			InifinityExtra06:        0,
			InifinityExtra07:        0,
			InifinityExtra08:        0,
			InifinityExtra09:        0,
			RemainingTries:          5,
			RestoreTriesAt:          0,
			Credits:                 0, // TODO
			FriendsBonusCreditsTime: time.Now().Unix(),
			// TODO ProgressStandart:        [][]int8 // json
		}
		success := storage.Gorm.NewRecord(&user)
		if !success {
			panic(fmt.Sprintf("can`t create user %v", user))
		}
	}
	log.Println(user)
	// TODO logic
	// TODO add users progress
	response := enterResponse{
		ReqMsgID:             request.MsgID,
		UserID:               user.ID,
		ReachedStage01:       user.ReachedStage01,
		ReachedStage02:       user.ReachedStage02,
		ReachedSubStage01:    user.ReachedSubStage01,
		ReachedSubStage02:    user.ReachedSubStage02,
		IgnoreSavePointBlock: user.IgnoreSavePointBlock,
		RemainingTries:       user.RemainingTries,
		Credits:              user.Credits,
		InfinityExtra00:      user.InifinityExtra00,
		InfinityExtra01:      user.InifinityExtra01,
		InfinityExtra02:      user.InifinityExtra02,
		InfinityExtra03:      user.InifinityExtra03,
		InfinityExtra04:      user.InifinityExtra04,
		InfinityExtra05:      user.InifinityExtra05,
		InfinityExtra06:      user.InifinityExtra06,
		InfinityExtra07:      user.InifinityExtra07,
		InfinityExtra08:      user.InifinityExtra08,
		InfinityExtra09:      user.InifinityExtra09,
		OfferAvailable:       false,
		FirstGame:            firstGame,
		// BonusCredits   TODO
		// AppFriendsBonusCredits TODO
		// StagesProgressStat01 TODO
		// StagesProgressStat02   TODO
		// SubStagesRecordStats01 TODO
		// SubStagesRecordStats02 TODO
	}
	JSON(w, response)
}
<|endoftext|>


<commit_before>package rain_test

import (
	"io/ioutil"
	"net"
	"net/http"
	"os/exec"
	"path/filepath"
	"testing"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/cenkalti/log"
	"github.com/crosbymichael/tracker"
	"github.com/crosbymichael/tracker/server"

	"github.com/cenkalti/rain"
)

const (
	port1 = 6881
	port2 = 6882
)

var (
	trackerAddr    = ":5000"
	torrentFile    = filepath.Join("testfiles", "sample_torrent.torrent")
	torrentDataDir = "testfiles"
	torrentName    = "sample_torrent"
)

func init() {
	rain.SetLogLevel(log.DEBUG)
}

func TestDownload(t *testing.T) {
	c1 := rain.DefaultConfig
	c1.Port = port1

	c2 := rain.DefaultConfig
	c2.Port = port2

	r1, err := rain.New(&c1) // Seeder
	if err != nil {
		t.Fatal(err)
	}

	err = r1.Listen()
	if err != nil {
		t.Fatal(err)
	}

	r2, err := rain.New(&c2) // Leecher
	if err != nil {
		t.Fatal(err)
	}

	// Start tracker
	logger := logrus.New()
	logger.Level = logrus.DebugLevel
	registry := tracker.NewInMemoryRegistry()
	s := server.New(120, 30, registry, logger)
	l, err := net.Listen("tcp", trackerAddr)
	if err != nil {
		t.Fatal(err)
	}
	go func() {
		if err := http.Serve(l, s); err != nil {
			t.Fatal(err)
		}
	}()

	t1, err := r1.Add(torrentFile, torrentDataDir)
	if err != nil {
		t.Fatal(err)
	}
	r1.Start(t1)

	// Wait for r1 to announce to tracker.
	time.Sleep(1 * time.Second)

	where, err := ioutil.TempDir("", "rain-")
	if err != nil {
		t.Fatal(err)
	}

	t2, err := r2.Add(torrentFile, where)
	if err != nil {
		t.Fatal(err)
	}
	r2.Start(t2)

	select {
	case <-t2.Finished:
	case <-time.After(10 * time.Second):
		t.FailNow()
	}

	cmd := exec.Command("diff", "-rq",
		filepath.Join(torrentDataDir, torrentName),
		filepath.Join(where, torrentName))
	err = cmd.Run()
	if err != nil {
		t.Fatal(err)
	}
}
<commit_msg>comments<commit_after>package rain_test

import (
	"io/ioutil"
	"net"
	"net/http"
	"os/exec"
	"path/filepath"
	"testing"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/cenkalti/log"
	"github.com/crosbymichael/tracker"
	"github.com/crosbymichael/tracker/server"

	"github.com/cenkalti/rain"
)

const (
	port1 = 6881
	port2 = 6882
)

var (
	trackerAddr    = ":5000"
	torrentFile    = filepath.Join("testfiles", "sample_torrent.torrent")
	torrentDataDir = "testfiles"
	torrentName    = "sample_torrent"
)

func init() {
	rain.SetLogLevel(log.DEBUG)
}

func TestDownload(t *testing.T) {
	c1 := rain.DefaultConfig
	c1.Port = port1

	c2 := rain.DefaultConfig
	c2.Port = port2

	seeder, err := rain.New(&c1)
	if err != nil {
		t.Fatal(err)
	}

	err = seeder.Listen()
	if err != nil {
		t.Fatal(err)
	}

	leecher, err := rain.New(&c2)
	if err != nil {
		t.Fatal(err)
	}

	// Start tracker
	logger := logrus.New()
	logger.Level = logrus.DebugLevel
	registry := tracker.NewInMemoryRegistry()
	s := server.New(120, 30, registry, logger)
	l, err := net.Listen("tcp", trackerAddr)
	if err != nil {
		t.Fatal(err)
	}
	go func() {
		if err := http.Serve(l, s); err != nil {
			t.Fatal(err)
		}
	}()

	t1, err := seeder.Add(torrentFile, torrentDataDir)
	if err != nil {
		t.Fatal(err)
	}
	seeder.Start(t1)

	// Wait for seeder to announce to tracker.
	time.Sleep(1 * time.Second)

	where, err := ioutil.TempDir("", "rain-")
	if err != nil {
		t.Fatal(err)
	}

	t2, err := leecher.Add(torrentFile, where)
	if err != nil {
		t.Fatal(err)
	}
	leecher.Start(t2)

	select {
	case <-t2.Finished:
	case <-time.After(10 * time.Second):
		t.FailNow()
	}

	cmd := exec.Command("diff", "-rq",
		filepath.Join(torrentDataDir, torrentName),
		filepath.Join(where, torrentName))
	err = cmd.Run()
	if err != nil {
		t.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"os/user"
	"text/tabwriter"
	"io"
	"io/ioutil"
	"net/http"
	"log"
	"encoding/xml"
	"strings"
	"strconv"
	"net/url"
	"reflect"
	"time"
	"flag"
	"regexp"
)

type Stock struct {
	XMLName xml.Name `xml:"query"`
	Data []Data `xml:"results>row"`
}

var stockContainer Stock

type Data struct {
	Symbol string `xml:"symbol"`
	Open string `xml:"open"`
	High string `xml:"high"`
	Low string `xml:"low"`
	Date string `xml:"lastTradeDate"`
	Time string `xml:"lastTradeTime"`
	Last string `xml:"lastTradePrice"`
	Change string `xml:"change"`
	Pct string `xml:"changePct"`
}

var intervalFlag time.Duration

func init() {
	flag.DurationVar(&intervalFlag, "interval", 3*time.Second, "interval to refresh list")
	flag.DurationVar(&intervalFlag, "i", 3*time.Second, "interval to refresh list")
}

func main() {

	flag.Parse()

	duration := intervalFlag

	usr, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}

	data, err := ioutil.ReadFile(usr.HomeDir + "/stocks.txt")

	if err != nil {
		log.Fatal(err)
	}

	loadData(data)

	for _ = range time.Tick(duration) {
		loadData(data)
	}
}

func loadData(data []byte) {

	symbols := strings.Join(strings.Split(string(data), "\n"), ",")

	format := make([]string, 9)
	format[0] = "s" 	// Symbol
	format[1] = "d1" 	// Last Trade Date
	format[2] = "t1"	// Last Trade Time
	format[3] = "l1"	// Last Trade (Price Only)
	format[4] = "c6"	// Change (Realtime)
	format[5] = "p2"	// Change Percent (Realtime)
	format[6] = "o"		// Open
	format[7] = "h"		// Day's High
	format[8] = "g"		// Day's Low

	v := url.Values{}

	v.Set("q", "select * from csv where url='http://download.finance.yahoo.com/d/quotes.csv?f=" + strings.Join(format, "") + "&s=" + symbols + "&e=.csv' and columns='symbol,lastTradeDate,lastTradeTime,lastTradePrice,change,changePct,open,high,low'")
	v.Add("format", "xml")
	v.Add("env", "store://datatables.org/alltableswithkeys")

	resp, err := http.Get("https://query.yahooapis.com/v1/public/yql?" + v.Encode())

	if err != nil {
		log.Fatal(err)
	}

	defer resp.Body.Close() // Defer the closing of the request

	stocks := decodeXml(resp.Body)

	formatOutput(stocks)
}

func decodeXml(body io.ReadCloser) Stock {

	XMLdata := xml.NewDecoder(body)

	// reset value of stockContainer
	stockContainer = Stock{}

	err := XMLdata.Decode(&stockContainer)
	if err != nil {
		log.Fatal(err)
	}

	return stockContainer
}

func formatOutput (s Stock) {
	w := new(tabwriter.Writer)

	w.Init(os.Stdout, 0, 8, 1, '\t', 0)

	fmt.Print("\033[2J\033[H")

	fmt.Fprintln(w, time.Now().Round(time.Second).String())

	var d Data
	v := reflect.ValueOf(d) // reflect lets us iterate on the struct

	var value, separator, header string

	for i := 0; i < v.NumField(); i++ {
		value = v.Type().Field(i).Name
		if (i < (v.NumField() - 1)) {
			separator = "\t"
		} else {
			separator = ""
		}

		// Print the header labels underlined
		header += fmt.Sprintf("\033[4m%s\033[0m%s", value, separator)
	}

	fmt.Fprintln(w, header)

	// run the stock through String()
	for _, stock := range s.Data {
		fmt.Fprintln(w, stock)
	}

	w.Flush()
}

func (d Data) String() string {

	v := reflect.ValueOf(d)

	var fs, s, ansi, value string

	// We're starting at 1 to skip the XML field name
	for i := 0; i < v.NumField(); i++ {
		value = v.Field(i).String()
		switch v.Type().Field(i).Name {

			case "Change":
				val, err := strconv.ParseFloat(value, 64)
				if err != nil {
					value = value
				} else {
					value = strconv.FormatFloat(val, 'f', 2, 64)
				}

				if val > 0 {
					ansi = "32"
					value = "+" + value
				} else {
					ansi = "31"

				}

			case "Pct":
				val, err := strconv.ParseFloat(strings.Replace(value, "%", "", 1), 64)
				if err != nil {
					log.Fatal(err)
					value = value
				} else {
					value = strconv.FormatFloat(val, 'f', 2, 64)
				}

				if val > 0 {
					ansi = "32"
					value = "+" + value
				} else {
					ansi = "31"
				}

				value = value + "%"

			case "Symbol":
				ansi = "1"
				maxLen := maxSymbolLength(stockContainer)
				if len(value) < maxLen {
					diff := maxLen - len(value)
					value = value + strings.Repeat(" ", diff)

				}

			case "Open", "High", "Low", "Last":
				ansi = "0"
				val, err := strconv.ParseFloat(value, 64)
				if err != nil {
					value = "N/A"
				} else {
					value = strconv.FormatFloat(val, 'f', 2, 64)
				}

			case "Date":
				reg, err := regexp.Compile("\\d{2}(\\d{2})$")
				if err != nil {
					value = value
				} else {
					value = reg.ReplaceAllString(value, "$1")
				}

			default:
				ansi = "0"
		}

		fs = "\033[%sm%s\033[0m\t"
		s += fmt.Sprintf(fs, ansi, value)
	}

	return s
}

func maxSymbolLength(s Stock) int {
	size := 0
	for _, stock := range s.Data {
		if len(stock.Symbol) > size {
			size = len(stock.Symbol)
		}
	}
	return size
}
<commit_msg>cleaning lines<commit_after>package main

import (
	"fmt"
	"os"
	"os/user"
	"text/tabwriter"
	"io"
	"io/ioutil"
	"net/http"
	"log"
	"encoding/xml"
	"strings"
	"strconv"
	"net/url"
	"reflect"
	"time"
	"flag"
	"regexp"
)

type Stock struct {
	XMLName xml.Name `xml:"query"`
	Data []Data `xml:"results>row"`
}

var stockContainer Stock

type Data struct {
	Symbol string `xml:"symbol"`
	Open string `xml:"open"`
	High string `xml:"high"`
	Low string `xml:"low"`
	Date string `xml:"lastTradeDate"`
	Time string `xml:"lastTradeTime"`
	Last string `xml:"lastTradePrice"`
	Change string `xml:"change"`
	Pct string `xml:"changePct"`
}

var intervalFlag time.Duration

func init() {
	flag.DurationVar(&intervalFlag, "interval", 3*time.Second, "interval to refresh list")
	flag.DurationVar(&intervalFlag, "i", 3*time.Second, "interval to refresh list")
}

func main() {

	flag.Parse()

	duration := intervalFlag

	usr, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}

	data, err := ioutil.ReadFile(usr.HomeDir + "/stocks.txt")

	if err != nil {
		log.Fatal(err)
	}

	loadData(data)

	for _ = range time.Tick(duration) {
		loadData(data)
	}
}

func loadData(data []byte) {

	symbols := strings.Join(strings.Split(string(data), "\n"), ",")

	format := make([]string, 9)
	format[0] = "s" 	// Symbol
	format[1] = "d1" 	// Last Trade Date
	format[2] = "t1"	// Last Trade Time
	format[3] = "l1"	// Last Trade (Price Only)
	format[4] = "c6"	// Change (Realtime)
	format[5] = "p2"	// Change Percent (Realtime)
	format[6] = "o"		// Open
	format[7] = "h"		// Day's High
	format[8] = "g"		// Day's Low

	v := url.Values{}

	v.Set("q", "select * from csv where url='http://download.finance.yahoo.com/d/quotes.csv?f=" + strings.Join(format, "") + "&s=" + symbols + "&e=.csv' and columns='symbol,lastTradeDate,lastTradeTime,lastTradePrice,change,changePct,open,high,low'")
	v.Add("format", "xml")
	v.Add("env", "store://datatables.org/alltableswithkeys")

	resp, err := http.Get("https://query.yahooapis.com/v1/public/yql?" + v.Encode())

	if err != nil {
		log.Fatal(err)
	}

	defer resp.Body.Close() // Defer the closing of the request

	stocks := decodeXml(resp.Body)

	formatOutput(stocks)
}

func decodeXml(body io.ReadCloser) Stock {

	XMLdata := xml.NewDecoder(body)

	// reset value of stockContainer
	stockContainer = Stock{}

	err := XMLdata.Decode(&stockContainer)
	if err != nil {
		log.Fatal(err)
	}

	return stockContainer
}

func formatOutput (s Stock) {
	w := new(tabwriter.Writer)

	w.Init(os.Stdout, 0, 8, 1, '\t', 0)

	fmt.Print("\033[2J\033[H")

	fmt.Fprintln(w, time.Now().Round(time.Second).String())

	var d Data
	v := reflect.ValueOf(d) // reflect lets us iterate on the struct

	var value, separator, header string

	for i := 0; i < v.NumField(); i++ {
		value = v.Type().Field(i).Name
		if (i < (v.NumField() - 1)) {
			separator = "\t"
		} else {
			separator = ""
		}

		// Print the header labels underlined
		header += fmt.Sprintf("\033[4m%s\033[0m%s", value, separator)
	}

	fmt.Fprintln(w, header)

	// run the stock through String()
	for _, stock := range s.Data {
		fmt.Fprintln(w, stock)
	}

	w.Flush()
}

func (d Data) String() string {

	v := reflect.ValueOf(d)

	var fs, s, ansi, value string

	// We're starting at 1 to skip the XML field name
	for i := 0; i < v.NumField(); i++ {
		value = v.Field(i).String()
		switch v.Type().Field(i).Name {

			case "Change":
				val, err := strconv.ParseFloat(value, 64)
				if err != nil {
					value = value
				} else {
					value = strconv.FormatFloat(val, 'f', 2, 64)
				}

				if val > 0 {
					ansi = "32"
					value = "+" + value
				} else {
					ansi = "31"
				}

			case "Pct":
				val, err := strconv.ParseFloat(strings.Replace(value, "%", "", 1), 64)
				if err != nil {
					log.Fatal(err)
					value = value
				} else {
					value = strconv.FormatFloat(val, 'f', 2, 64)
				}

				if val > 0 {
					ansi = "32"
					value = "+" + value
				} else {
					ansi = "31"
				}

				value = value + "%"

			case "Symbol":
				ansi = "1"
				maxLen := maxSymbolLength(stockContainer)
				if len(value) < maxLen {
					diff := maxLen - len(value)
					value = value + strings.Repeat(" ", diff)
				}

			case "Open", "High", "Low", "Last":
				ansi = "0"
				val, err := strconv.ParseFloat(value, 64)
				if err != nil {
					value = "N/A"
				} else {
					value = strconv.FormatFloat(val, 'f', 2, 64)
				}

			case "Date":
				reg, err := regexp.Compile("\\d{2}(\\d{2})$")
				if err != nil {
					value = value
				} else {
					value = reg.ReplaceAllString(value, "$1")
				}

			default:
				ansi = "0"
		}

		fs = "\033[%sm%s\033[0m\t"
		s += fmt.Sprintf(fs, ansi, value)
	}

	return s
}

func maxSymbolLength(s Stock) int {
	size := 0
	for _, stock := range s.Data {
		if len(stock.Symbol) > size {
			size = len(stock.Symbol)
		}
	}
	return size
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"encoding/json"
	"io/ioutil"
	"os"
	"path"
	"strings"

	"github.com/codegangsta/cli"
	"github.com/xenolf/lego/acme"
)

func checkFolder(path string) error {
	if _, err := os.Stat(path); os.IsNotExist(err) {
		return os.MkdirAll(path, 0700)
	}
	return nil
}

func setup(c *cli.Context) (*Configuration, *Account, *acme.Client) {
	err := checkFolder(c.GlobalString("path"))
	if err != nil {
		logger().Fatalf("Cound not check/create path: %v", err)
	}

	conf := NewConfiguration(c)
	if !c.GlobalIsSet("email") {
		logger().Fatal("You have to pass an account (email address) to the program using --email or -m")
	}

	//TODO: move to account struct? Currently MUST pass email.
	acc := NewAccount(c.GlobalString("email"), conf)
	return conf, acc, acme.NewClient(c.GlobalString("server"), acc, conf.RsaBits(), conf.OptPort())
}

func saveCertRes(certRes acme.CertificateResource, conf *Configuration) {
	// We store the certificate, private key and metadata in different files
	// as web servers would not be able to work with a combined file.
	certOut := path.Join(conf.CertPath(), certRes.Domain+".crt")
	privOut := path.Join(conf.CertPath(), certRes.Domain+".key")
	metaOut := path.Join(conf.CertPath(), certRes.Domain+".json")

	err := ioutil.WriteFile(certOut, certRes.Certificate, 0600)
	if err != nil {
		logger().Printf("Unable to save Certificate for domain %s\n\t%v", certRes.Domain, err)
	}

	err = ioutil.WriteFile(privOut, certRes.PrivateKey, 0600)
	if err != nil {
		logger().Printf("Unable to save PrivateKey for domain %s\n\t%v", certRes.Domain, err)
	}

	jsonBytes, err := json.MarshalIndent(certRes, "", "\t")
	if err != nil {
		logger().Printf("Unable to marshal CertResource for domain %s\n\t%v", certRes.Domain, err)
	}

	err = ioutil.WriteFile(metaOut, jsonBytes, 0600)
	if err != nil {
		logger().Printf("Unable to save CertResource for domain %s\n\t%v", certRes.Domain, err)
	}
}

func run(c *cli.Context) {

	conf, acc, client := setup(c)
	if acc.Registration == nil {
		reg, err := client.Register()
		if err != nil {
			logger().Fatalf("Could not complete registration\n\t%v", err)
		}

		acc.Registration = reg
		acc.Save()

		logger().Print("!!!! HEADS UP !!!!")
		logger().Printf(`
		Your account credentials have been saved in your Let's Encrypt
		configuration directory at "%s".
		You should make a secure backup	of this folder now. This
		configuration directory will also contain certificates and
		private keys obtained from Let's Encrypt so making regular
		backups of this folder is ideal.`, conf.AccountPath(c.GlobalString("email")))

	}

	if acc.Registration.Body.Agreement == "" {
		reader := bufio.NewReader(os.Stdin)
		logger().Printf("Please review the TOS at %s", acc.Registration.TosURL)

		for {
			logger().Println("Do you accept the TOS? Y/n")
			text, err := reader.ReadString('\n')
			if err != nil {
				logger().Fatalf("Could not read from console -> %v", err)
			}

			text = strings.Trim(text, "\r\n")

			if text == "n" {
				logger().Fatal("You did not accept the TOS. Unable to proceed.")
			}

			if text == "Y" || text == "y" || text == "" {
				err = client.AgreeToTOS()
				if err != nil {
					logger().Fatalf("Could not agree to tos -> %v", err)
				}
				acc.Save()
				break
			}

			logger().Println("Your input was invalid. Please answer with one of Y/y, n or by pressing enter.")
		}
	}

	if !c.GlobalIsSet("domains") {
		logger().Fatal("Please specify --domains")
	}

	certs, err := client.ObtainCertificates(c.GlobalStringSlice("domains"), true)
	if err != nil {
		logger().Fatalf("Could not obtain certificates\n\t%v", err)
	}

	err = checkFolder(conf.CertPath())
	if err != nil {
		logger().Fatalf("Cound not check/create path: %v", err)
	}

	for _, certRes := range certs {
		saveCertRes(certRes, conf)
	}
}

func revoke(c *cli.Context) {

	conf, _, client := setup(c)

	err := checkFolder(conf.CertPath())
	if err != nil {
		logger().Fatalf("Cound not check/create path: %v", err)
	}

	for _, domain := range c.GlobalStringSlice("domains") {
		logger().Printf("Trying to revoke certificate for domain %s", domain)

		certPath := path.Join(conf.CertPath(), domain+".crt")
		certBytes, err := ioutil.ReadFile(certPath)

		err = client.RevokeCertificate(certBytes)
		if err != nil {
			logger().Printf("Error while revoking the certificate for domain %s\n\t%v", domain, err)
		} else {
			logger().Print("Certificate was revoked.")
		}
	}
}

func renew(c *cli.Context) {
	conf, _, client := setup(c)

	for _, domain := range c.GlobalStringSlice("domains") {
		// load the cert resource from files.
		// We store the certificate, private key and metadata in different files
		// as web servers would not be able to work with a combined file.
		certPath := path.Join(conf.CertPath(), domain+".crt")
		privPath := path.Join(conf.CertPath(), domain+".key")
		metaPath := path.Join(conf.CertPath(), domain+".json")

		certBytes, err := ioutil.ReadFile(certPath)
		if err != nil {
			logger().Printf("Error while loading the certificate for domain %s\n\t%v", domain, err)
			return
		}

		keyBytes, err := ioutil.ReadFile(privPath)
		if err != nil {
			logger().Printf("Error while loading the private key for domain %s\n\t%v", domain, err)
			return
		}

		metaBytes, err := ioutil.ReadFile(metaPath)
		if err != nil {
			logger().Printf("Error while loading the meta data for domain %s\n\t%v", domain, err)
			return
		}

		var certRes acme.CertificateResource
		err = json.Unmarshal(metaBytes, &certRes)
		if err != nil {
			logger().Printf("Error while marshalling the meta data for domain %s\n\t%v", domain, err)
			return
		}

		certRes.PrivateKey = keyBytes
		certRes.Certificate = certBytes

		newCert, err := client.RenewCertificate(certRes, true, true)
		if err != nil {
			logger().Printf("%v", err)
			return
		}

		saveCertRes(newCert, conf)
	}
}
<commit_msg>Update CLI for new NewClient signature<commit_after>package main

import (
	"bufio"
	"encoding/json"
	"io/ioutil"
	"os"
	"path"
	"strings"

	"github.com/codegangsta/cli"
	"github.com/xenolf/lego/acme"
)

func checkFolder(path string) error {
	if _, err := os.Stat(path); os.IsNotExist(err) {
		return os.MkdirAll(path, 0700)
	}
	return nil
}

func setup(c *cli.Context) (*Configuration, *Account, *acme.Client) {
	err := checkFolder(c.GlobalString("path"))
	if err != nil {
		logger().Fatalf("Cound not check/create path: %v", err)
	}

	conf := NewConfiguration(c)
	if !c.GlobalIsSet("email") {
		logger().Fatal("You have to pass an account (email address) to the program using --email or -m")
	}

	//TODO: move to account struct? Currently MUST pass email.
	acc := NewAccount(c.GlobalString("email"), conf)

	client, err := acme.NewClient(c.GlobalString("server"), acc, conf.RsaBits(), conf.OptPort())
	if err != nil {
		logger().Fatal("Could not create client:", err)
	}

	return conf, acc, client
}

func saveCertRes(certRes acme.CertificateResource, conf *Configuration) {
	// We store the certificate, private key and metadata in different files
	// as web servers would not be able to work with a combined file.
	certOut := path.Join(conf.CertPath(), certRes.Domain+".crt")
	privOut := path.Join(conf.CertPath(), certRes.Domain+".key")
	metaOut := path.Join(conf.CertPath(), certRes.Domain+".json")

	err := ioutil.WriteFile(certOut, certRes.Certificate, 0600)
	if err != nil {
		logger().Printf("Unable to save Certificate for domain %s\n\t%v", certRes.Domain, err)
	}

	err = ioutil.WriteFile(privOut, certRes.PrivateKey, 0600)
	if err != nil {
		logger().Printf("Unable to save PrivateKey for domain %s\n\t%v", certRes.Domain, err)
	}

	jsonBytes, err := json.MarshalIndent(certRes, "", "\t")
	if err != nil {
		logger().Printf("Unable to marshal CertResource for domain %s\n\t%v", certRes.Domain, err)
	}

	err = ioutil.WriteFile(metaOut, jsonBytes, 0600)
	if err != nil {
		logger().Printf("Unable to save CertResource for domain %s\n\t%v", certRes.Domain, err)
	}
}

func run(c *cli.Context) {

	conf, acc, client := setup(c)
	if acc.Registration == nil {
		reg, err := client.Register()
		if err != nil {
			logger().Fatalf("Could not complete registration\n\t%v", err)
		}

		acc.Registration = reg
		acc.Save()

		logger().Print("!!!! HEADS UP !!!!")
		logger().Printf(`
		Your account credentials have been saved in your Let's Encrypt
		configuration directory at "%s".
		You should make a secure backup	of this folder now. This
		configuration directory will also contain certificates and
		private keys obtained from Let's Encrypt so making regular
		backups of this folder is ideal.`, conf.AccountPath(c.GlobalString("email")))

	}

	if acc.Registration.Body.Agreement == "" {
		reader := bufio.NewReader(os.Stdin)
		logger().Printf("Please review the TOS at %s", acc.Registration.TosURL)

		for {
			logger().Println("Do you accept the TOS? Y/n")
			text, err := reader.ReadString('\n')
			if err != nil {
				logger().Fatalf("Could not read from console -> %v", err)
			}

			text = strings.Trim(text, "\r\n")

			if text == "n" {
				logger().Fatal("You did not accept the TOS. Unable to proceed.")
			}

			if text == "Y" || text == "y" || text == "" {
				err = client.AgreeToTOS()
				if err != nil {
					logger().Fatalf("Could not agree to tos -> %v", err)
				}
				acc.Save()
				break
			}

			logger().Println("Your input was invalid. Please answer with one of Y/y, n or by pressing enter.")
		}
	}

	if !c.GlobalIsSet("domains") {
		logger().Fatal("Please specify --domains")
	}

	certs, err := client.ObtainCertificates(c.GlobalStringSlice("domains"), true)
	if err != nil {
		logger().Fatalf("Could not obtain certificates\n\t%v", err)
	}

	err = checkFolder(conf.CertPath())
	if err != nil {
		logger().Fatalf("Cound not check/create path: %v", err)
	}

	for _, certRes := range certs {
		saveCertRes(certRes, conf)
	}
}

func revoke(c *cli.Context) {

	conf, _, client := setup(c)

	err := checkFolder(conf.CertPath())
	if err != nil {
		logger().Fatalf("Cound not check/create path: %v", err)
	}

	for _, domain := range c.GlobalStringSlice("domains") {
		logger().Printf("Trying to revoke certificate for domain %s", domain)

		certPath := path.Join(conf.CertPath(), domain+".crt")
		certBytes, err := ioutil.ReadFile(certPath)

		err = client.RevokeCertificate(certBytes)
		if err != nil {
			logger().Printf("Error while revoking the certificate for domain %s\n\t%v", domain, err)
		} else {
			logger().Print("Certificate was revoked.")
		}
	}
}

func renew(c *cli.Context) {
	conf, _, client := setup(c)

	for _, domain := range c.GlobalStringSlice("domains") {
		// load the cert resource from files.
		// We store the certificate, private key and metadata in different files
		// as web servers would not be able to work with a combined file.
		certPath := path.Join(conf.CertPath(), domain+".crt")
		privPath := path.Join(conf.CertPath(), domain+".key")
		metaPath := path.Join(conf.CertPath(), domain+".json")

		certBytes, err := ioutil.ReadFile(certPath)
		if err != nil {
			logger().Printf("Error while loading the certificate for domain %s\n\t%v", domain, err)
			return
		}

		keyBytes, err := ioutil.ReadFile(privPath)
		if err != nil {
			logger().Printf("Error while loading the private key for domain %s\n\t%v", domain, err)
			return
		}

		metaBytes, err := ioutil.ReadFile(metaPath)
		if err != nil {
			logger().Printf("Error while loading the meta data for domain %s\n\t%v", domain, err)
			return
		}

		var certRes acme.CertificateResource
		err = json.Unmarshal(metaBytes, &certRes)
		if err != nil {
			logger().Printf("Error while marshalling the meta data for domain %s\n\t%v", domain, err)
			return
		}

		certRes.PrivateKey = keyBytes
		certRes.Certificate = certBytes

		newCert, err := client.RenewCertificate(certRes, true, true)
		if err != nil {
			logger().Printf("%v", err)
			return
		}

		saveCertRes(newCert, conf)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/cyberdelia/heroku-go/v3"
)

type Bundle struct {
	config  map[string]string
	release *heroku.Release
	argv    []string
}

type DynoState int

const (
	NeverStarted DynoState = iota
	Started
	Stopped
)

func FindDynoDriver(name string) (DynoDriver, error) {
	switch name {
	case "simple":
		// Simple Driver: downloads environment, minus $PATH
		// so that local programs work as expected, and runs
		// the argv.
		return NewSimpleDynoDriver(), nil
	case "docker":
		return NewDockerDynoDriver(), nil
	default:
		return nil, fmt.Errorf("could not locate driver. "+
			"specified by the user: %v", name)
	}
}

type DynoDriver interface {
	Start(*Bundle) error
	Stop() error
	State() DynoState
}
<commit_msg>Remove bogus comment<commit_after>package main

import (
	"fmt"
	"github.com/cyberdelia/heroku-go/v3"
)

type Bundle struct {
	config  map[string]string
	release *heroku.Release
	argv    []string
}

type DynoState int

const (
	NeverStarted DynoState = iota
	Started
	Stopped
)

func FindDynoDriver(name string) (DynoDriver, error) {
	switch name {
	case "simple":
		return NewSimpleDynoDriver(), nil
	case "docker":
		return NewDockerDynoDriver(), nil
	default:
		return nil, fmt.Errorf("could not locate driver. "+
			"specified by the user: %v", name)
	}
}

type DynoDriver interface {
	Start(*Bundle) error
	Stop() error
	State() DynoState
}
<|endoftext|>


<commit_before>package main

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

	"github.com/ianremmler/setgo"
	"golang.org/x/mobile/app"
	"golang.org/x/mobile/event"
	"golang.org/x/mobile/f32"
	"golang.org/x/mobile/geom"
	"golang.org/x/mobile/gl"
	"golang.org/x/mobile/gl/glutil"
)

const (
	cardAspRat = 1.4
)

var (
	set       *setgo.SetGo
	field     []setgo.Card
	candidate = map[int]struct{}{}

	program  gl.Program
	position gl.Attrib
	color    gl.Uniform
	shading  gl.Uniform
	mvMat    gl.Uniform

	cardShape    = shape{verts: cardVerts}
	cardColor    = []float32{1, 1, 1, 1}
	selectColor  = []float32{0, 1, 1, 0.25}
	invalidColor = []float32{1, 0, 0, 0.25}
)

var colors = [][]float32{
	{1, 0, 0, 1},
	{0, 0.75, 0, 1},
	{0, 0, 1, 1},
}

var shapes = []shape{
	{verts: squareVerts},
	{verts: triVerts},
	{verts: hexVerts},
}

type shape struct {
	verts []float32
	buf   gl.Buffer
}

type state int

const (
	normal state = iota
	selected
	invalid
)

func main() {
	app.Run(app.Callbacks{
		Start: start,
		Stop:  stop,
		Draw:  draw,
		Touch: touch,
	})
}

func start() {
	rand.Seed(time.Now().UnixNano())
	set = setgo.NewStd()
	set.Shuffle()
	set.Deal()
	field = set.Field()

	var err error
	program, err = glutil.CreateProgram(vertShader, fragShader)
	if err != nil {
		log.Printf("error creating GL program: %v", err)
		return
	}

	gl.Enable(gl.BLEND)
	gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
	gl.LineWidth(4)

	cardShape.buf = gl.CreateBuffer()
	vertBytes := f32.Bytes(binary.LittleEndian, cardShape.verts...)
	gl.BindBuffer(gl.ARRAY_BUFFER, cardShape.buf)
	gl.BufferData(gl.ARRAY_BUFFER, vertBytes, gl.STATIC_DRAW)
	for i := range shapes {
		shapes[i].buf = gl.CreateBuffer()
		vertBytes = f32.Bytes(binary.LittleEndian, shapes[i].verts...)
		gl.BindBuffer(gl.ARRAY_BUFFER, shapes[i].buf)
		gl.BufferData(gl.ARRAY_BUFFER, vertBytes, gl.STATIC_DRAW)
	}

	position = gl.GetAttribLocation(program, "position")
	color = gl.GetUniformLocation(program, "color")
	shading = gl.GetUniformLocation(program, "shading")
	mvMat = gl.GetUniformLocation(program, "mvMat")
}

func stop() {
	gl.DeleteProgram(program)
	gl.DeleteBuffer(cardShape.buf)
	for i := range shapes {
		gl.DeleteBuffer(shapes[i].buf)
	}
}

func touch(evt event.Touch) {
	if evt.Type != event.TouchEnd {
		return
	}

	w, h, fw, fh := viewDims()
	rows, cols := 3, len(field)/3
	s := float32(evt.Loc.X) / float32(geom.Width)    // x fraction across display
	t := float32(evt.Loc.Y) / float32(geom.Height)   // y fraction across display
	marginX, marginY := 0.5*(w-fw)/fw, 0.5*(h-fh)/fh // "letterbox", if any
	c := int((s*w/fw - marginX) * float32(cols))
	r := int((t*h/fh - marginY) * float32(rows))

	idx := -1
	if r >= 0 && r < rows && c >= 0 && c < cols {
		idx = 3*c + (2 - r)
	}

	if idx >= 0 && idx < len(field) && !field[idx].Blank {
		updateCandidate(idx)
	}
}

func updateCandidate(idx int) {
	if _, ok := candidate[idx]; ok {
		delete(candidate, idx)
	} else if len(candidate) < 3 {
		candidate[idx] = struct{}{}
	}
	if len(candidate) < 3 {
		return
	}
	check := []int{}
	for idx := range candidate {
		check = append(check, idx)
	}
	if !set.IsSet(check) {
		return
	}
	// still here... we got a set!
	candidate = map[int]struct{}{}
	set.Remove(check)
	set.Deal()
	if set.NumSets() == 0 {
		// we won!  play again...
		set.Shuffle()
		set.Deal()
	}
	field = set.Field()
}

func drawCard(mat *f32.Mat4, card *setgo.Card, st state) {
	num, clr, shp, fil := card.Attr[0], card.Attr[1], card.Attr[2], card.Attr[3]

	// card base
	mvMat.WriteMat4(mat)
	gl.BindBuffer(gl.ARRAY_BUFFER, cardShape.buf)
	gl.EnableVertexAttribArray(position)
	gl.VertexAttribPointer(position, 3, gl.FLOAT, false, 0, 0)
	gl.Uniform1i(shading, 2)
	gl.Uniform4fv(color, cardColor)
	gl.DrawArrays(gl.TRIANGLE_FAN, 0, len(cardShape.verts)/3)
	gl.DisableVertexAttribArray(position)

	// symbols
	gl.BindBuffer(gl.ARRAY_BUFFER, shapes[shp].buf)
	gl.EnableVertexAttribArray(position)
	gl.VertexAttribPointer(position, 3, gl.FLOAT, false, 0, 0)
	gl.Uniform4fv(color, colors[clr])
	for i := 0; i <= num; i++ {
		shapeMat := *mat
		offset := float32(i+1) / (float32(num) + 2)
		shapeMat.Translate(&shapeMat, 0.5, offset*cardAspRat, 0)
		shapeMat.Scale(&shapeMat, 0.1, 0.1, 0)
		mvMat.WriteMat4(&shapeMat)
		gl.Uniform1i(shading, fil)
		gl.DrawArrays(gl.TRIANGLE_FAN, 0, len(shapes[shp].verts)/3)
		gl.Uniform1i(shading, 2)
		gl.DrawArrays(gl.LINE_LOOP, 0, len(shapes[shp].verts)/3)
	}
	gl.DisableVertexAttribArray(position)

	if st == normal {
		return
	}

	// candidate card highlighting
	mvMat.WriteMat4(mat)
	gl.BindBuffer(gl.ARRAY_BUFFER, cardShape.buf)
	gl.EnableVertexAttribArray(position)
	gl.VertexAttribPointer(position, 3, gl.FLOAT, false, 0, 0)
	switch st {
	case selected:
		gl.Uniform4fv(color, selectColor)
	case invalid:
		gl.Uniform4fv(color, invalidColor)
	}
	gl.DrawArrays(gl.TRIANGLE_FAN, 0, len(cardShape.verts)/3)
	gl.DisableVertexAttribArray(position)
}

func viewDims() (float32, float32, float32, float32) {
	cols := len(field) / 3
	fieldAspRat := float32(cols) / (3 * cardAspRat)
	dispAspRat := float32(geom.Width / geom.Height)

	fieldWidth, fieldHeight := float32(cols), float32(3*cardAspRat)
	width, height := fieldWidth, fieldHeight
	// add letterboxing to preserve aspect ratio
	if dispAspRat > fieldAspRat {
		width = fieldHeight * dispAspRat
	} else {
		height = fieldWidth / dispAspRat
	}
	return width, height, fieldWidth, fieldHeight
}

func draw() {
	gl.ClearColor(0, 0, 0, 1)
	gl.Clear(gl.COLOR_BUFFER_BIT)
	gl.UseProgram(program)

	w, h, fw, fh := viewDims()
	mat := f32.Mat4{}
	mat.Identity()
	mat.Scale(&mat, 1.0/(0.5*w), 1.0/(0.5*h), 1)

	for i := range field {
		if field[i].Blank {
			continue
		}
		x, y := float32(i/3), cardAspRat*float32(i%3)
		cardMat := mat
		cardMat.Translate(&cardMat, x-0.5*fw, y-0.5*fh, 0)
		// shrink just a bit to separate cards
		cardMat.Translate(&cardMat, 0.5, 0.5*cardAspRat, 0)
		cardMat.Scale(&cardMat, 1.0-0.02*cardAspRat, 1.0-0.02, 1)
		cardMat.Translate(&cardMat, -0.5, -0.5*cardAspRat, 0)

		st := normal
		if _, ok := candidate[i]; ok {
			if len(candidate) < 3 {
				st = selected
			} else {
				st = invalid
			}
		}
		drawCard(&cardMat, &field[i], st)
	}
}

var cardVerts = []float32{
	0, 0, 0,
	1, 0, 0,
	1, cardAspRat, 0,
	0, cardAspRat, 0,
}

var sin60 = float32(math.Sqrt(3) / 2)

var squareVerts = []float32{
	-sin60, -sin60, 0,
	sin60, -sin60, 0,
	sin60, sin60, 0,
	-sin60, sin60, 0,
}

var triVerts = []float32{
	-sin60, -sin60, 0,
	0, sin60, 0,
	sin60, -sin60, 0,
}

var hexVerts = []float32{
	-0.5, -sin60, 0,
	-1, 0, 0,
	-0.5, sin60, 0,
	0.5, sin60, 0,
	1, 0, 0,
	0.5, -sin60, 0,
}

const vertShader = `
	#version 100
	uniform mat4 mvMat;

	attribute vec4 position;
	void main() {
		gl_Position = mvMat * position;
	}`

const fragShader = `
	#version 100
	precision mediump float;

	uniform vec4 color;
	uniform int shading;

	void main() {
		if (shading == 0) {
			discard;
		}
		if (shading == 1) {
			if (mod((gl_FragCoord.x + gl_FragCoord.y) / 8.0, 2.0) < 1.0) {
				discard;
			}
		}
		gl_FragColor = color;
	}`
<commit_msg>Fix row/col rounding.<commit_after>package main

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

	"github.com/ianremmler/setgo"
	"golang.org/x/mobile/app"
	"golang.org/x/mobile/event"
	"golang.org/x/mobile/f32"
	"golang.org/x/mobile/geom"
	"golang.org/x/mobile/gl"
	"golang.org/x/mobile/gl/glutil"
)

const (
	cardAspRat = 1.4
)

var (
	set       *setgo.SetGo
	field     []setgo.Card
	candidate = map[int]struct{}{}

	program  gl.Program
	position gl.Attrib
	color    gl.Uniform
	shading  gl.Uniform
	mvMat    gl.Uniform

	cardShape    = shape{verts: cardVerts}
	cardColor    = []float32{1, 1, 1, 1}
	selectColor  = []float32{0, 1, 1, 0.25}
	invalidColor = []float32{1, 0, 0, 0.25}
)

var colors = [][]float32{
	{1, 0, 0, 1},
	{0, 0.75, 0, 1},
	{0, 0, 1, 1},
}

var shapes = []shape{
	{verts: squareVerts},
	{verts: triVerts},
	{verts: hexVerts},
}

type shape struct {
	verts []float32
	buf   gl.Buffer
}

type state int

const (
	normal state = iota
	selected
	invalid
)

func main() {
	app.Run(app.Callbacks{
		Start: start,
		Stop:  stop,
		Draw:  draw,
		Touch: touch,
	})
}

func start() {
	rand.Seed(time.Now().UnixNano())
	set = setgo.NewStd()
	set.Shuffle()
	set.Deal()
	field = set.Field()

	var err error
	program, err = glutil.CreateProgram(vertShader, fragShader)
	if err != nil {
		log.Printf("error creating GL program: %v", err)
		return
	}

	gl.Enable(gl.BLEND)
	gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
	gl.LineWidth(4)

	cardShape.buf = gl.CreateBuffer()
	vertBytes := f32.Bytes(binary.LittleEndian, cardShape.verts...)
	gl.BindBuffer(gl.ARRAY_BUFFER, cardShape.buf)
	gl.BufferData(gl.ARRAY_BUFFER, vertBytes, gl.STATIC_DRAW)
	for i := range shapes {
		shapes[i].buf = gl.CreateBuffer()
		vertBytes = f32.Bytes(binary.LittleEndian, shapes[i].verts...)
		gl.BindBuffer(gl.ARRAY_BUFFER, shapes[i].buf)
		gl.BufferData(gl.ARRAY_BUFFER, vertBytes, gl.STATIC_DRAW)
	}

	position = gl.GetAttribLocation(program, "position")
	color = gl.GetUniformLocation(program, "color")
	shading = gl.GetUniformLocation(program, "shading")
	mvMat = gl.GetUniformLocation(program, "mvMat")
}

func stop() {
	gl.DeleteProgram(program)
	gl.DeleteBuffer(cardShape.buf)
	for i := range shapes {
		gl.DeleteBuffer(shapes[i].buf)
	}
}

func touch(evt event.Touch) {
	if evt.Type != event.TouchEnd {
		return
	}

	w, h, fw, fh := viewDims()
	rows, cols := 3, len(field)/3
	s := float32(evt.Loc.X) / float32(geom.Width)    // x fraction across display
	t := float32(evt.Loc.Y) / float32(geom.Height)   // y fraction across display
	marginX, marginY := 0.5*(w-fw)/fw, 0.5*(h-fh)/fh // "letterbox", if any
	c := int(math.Floor(float64(s*w/fw-marginX) * float64(cols)))
	r := int(math.Floor(float64(t*h/fh-marginY) * float64(rows)))

	idx := -1
	if r >= 0 && r < rows && c >= 0 && c < cols {
		idx = 3*c + (2 - r)
	}

	if idx >= 0 && idx < len(field) && !field[idx].Blank {
		updateCandidate(idx)
	}
}

func updateCandidate(idx int) {
	if _, ok := candidate[idx]; ok {
		delete(candidate, idx)
	} else if len(candidate) < 3 {
		candidate[idx] = struct{}{}
	}
	if len(candidate) < 3 {
		return
	}
	check := []int{}
	for idx := range candidate {
		check = append(check, idx)
	}
	if !set.IsSet(check) {
		return
	}
	// still here... we got a set!
	candidate = map[int]struct{}{}
	set.Remove(check)
	set.Deal()
	if set.NumSets() == 0 {
		// we won!  play again...
		set.Shuffle()
		set.Deal()
	}
	field = set.Field()
}

func drawCard(mat *f32.Mat4, card *setgo.Card, st state) {
	num, clr, shp, fil := card.Attr[0], card.Attr[1], card.Attr[2], card.Attr[3]

	// card base
	mvMat.WriteMat4(mat)
	gl.BindBuffer(gl.ARRAY_BUFFER, cardShape.buf)
	gl.EnableVertexAttribArray(position)
	gl.VertexAttribPointer(position, 3, gl.FLOAT, false, 0, 0)
	gl.Uniform1i(shading, 2)
	gl.Uniform4fv(color, cardColor)
	gl.DrawArrays(gl.TRIANGLE_FAN, 0, len(cardShape.verts)/3)
	gl.DisableVertexAttribArray(position)

	// symbols
	gl.BindBuffer(gl.ARRAY_BUFFER, shapes[shp].buf)
	gl.EnableVertexAttribArray(position)
	gl.VertexAttribPointer(position, 3, gl.FLOAT, false, 0, 0)
	gl.Uniform4fv(color, colors[clr])
	for i := 0; i <= num; i++ {
		shapeMat := *mat
		offset := float32(i+1) / (float32(num) + 2)
		shapeMat.Translate(&shapeMat, 0.5, offset*cardAspRat, 0)
		shapeMat.Scale(&shapeMat, 0.1, 0.1, 0)
		mvMat.WriteMat4(&shapeMat)
		gl.Uniform1i(shading, fil)
		gl.DrawArrays(gl.TRIANGLE_FAN, 0, len(shapes[shp].verts)/3)
		gl.Uniform1i(shading, 2)
		gl.DrawArrays(gl.LINE_LOOP, 0, len(shapes[shp].verts)/3)
	}
	gl.DisableVertexAttribArray(position)

	if st == normal {
		return
	}

	// candidate card highlighting
	mvMat.WriteMat4(mat)
	gl.BindBuffer(gl.ARRAY_BUFFER, cardShape.buf)
	gl.EnableVertexAttribArray(position)
	gl.VertexAttribPointer(position, 3, gl.FLOAT, false, 0, 0)
	switch st {
	case selected:
		gl.Uniform4fv(color, selectColor)
	case invalid:
		gl.Uniform4fv(color, invalidColor)
	}
	gl.DrawArrays(gl.TRIANGLE_FAN, 0, len(cardShape.verts)/3)
	gl.DisableVertexAttribArray(position)
}

// viewDims returns the display width/height and field width/height in units
// based on the width of a card.
func viewDims() (float32, float32, float32, float32) {
	cols := len(field) / 3
	fieldAspRat := float32(cols) / (3 * cardAspRat)
	dispAspRat := float32(geom.Width / geom.Height)

	fieldWidth, fieldHeight := float32(cols), float32(3*cardAspRat)
	width, height := fieldWidth, fieldHeight
	// add letterboxing to preserve aspect ratio
	if dispAspRat > fieldAspRat {
		width = fieldHeight * dispAspRat
	} else {
		height = fieldWidth / dispAspRat
	}
	return width, height, fieldWidth, fieldHeight
}

func draw() {
	gl.ClearColor(0, 0, 0, 1)
	gl.Clear(gl.COLOR_BUFFER_BIT)
	gl.UseProgram(program)

	w, h, fw, fh := viewDims()
	mat := f32.Mat4{}
	mat.Identity()
	mat.Scale(&mat, 1.0/(0.5*w), 1.0/(0.5*h), 1)

	for i := range field {
		if field[i].Blank {
			continue
		}
		x, y := float32(i/3), cardAspRat*float32(i%3)
		cardMat := mat
		cardMat.Translate(&cardMat, x-0.5*fw, y-0.5*fh, 0)
		// shrink just a bit to separate cards
		cardMat.Translate(&cardMat, 0.5, 0.5*cardAspRat, 0)
		cardMat.Scale(&cardMat, 1.0-0.02*cardAspRat, 1.0-0.02, 1)
		cardMat.Translate(&cardMat, -0.5, -0.5*cardAspRat, 0)

		st := normal
		if _, ok := candidate[i]; ok {
			if len(candidate) < 3 {
				st = selected
			} else {
				st = invalid
			}
		}
		drawCard(&cardMat, &field[i], st)
	}
}

var cardVerts = []float32{
	0, 0, 0,
	1, 0, 0,
	1, cardAspRat, 0,
	0, cardAspRat, 0,
}

var sin60 = float32(math.Sqrt(3) / 2)

var squareVerts = []float32{
	-sin60, -sin60, 0,
	sin60, -sin60, 0,
	sin60, sin60, 0,
	-sin60, sin60, 0,
}

var triVerts = []float32{
	-sin60, -sin60, 0,
	0, sin60, 0,
	sin60, -sin60, 0,
}

var hexVerts = []float32{
	-0.5, -sin60, 0,
	-1, 0, 0,
	-0.5, sin60, 0,
	0.5, sin60, 0,
	1, 0, 0,
	0.5, -sin60, 0,
}

const vertShader = `
	#version 100
	uniform mat4 mvMat;

	attribute vec4 position;
	void main() {
		gl_Position = mvMat * position;
	}`

const fragShader = `
	#version 100
	precision mediump float;

	uniform vec4 color;
	uniform int shading;

	void main() {
		if (shading == 0) {
			discard;
		}
		if (shading == 1) {
			if (mod((gl_FragCoord.x + gl_FragCoord.y) / 8.0, 2.0) < 1.0) {
				discard;
			}
		}
		gl_FragColor = color;
	}`
<|endoftext|>


<commit_before>
// Go program to print its own source code ( inspirted by TPOP (https://www.amazon.com/Practice-Programming-Addison-Wesley-Professional-Computing/dp/020161586X) 9-15)

package main

import (
	"bufio"
	"fmt"
	"os"
	"strings"
	"regexp"
)

func main() {
	// ensure source filename entered on command line
	if len(os.Args) < 1 {
		fmt.Println("Cannot read source file name from input arguments")
		return
	}

	// extract source file name from os.Args[0]
	// note: os.Args[0] would either be /log/path/name/progname (go run) or ./progname (compiled)
	// source file name is progname.go
 	r1, _ := regexp.Compile("(?i)[A-Z]+(?-i)$")
	fn := strings.TrimSpace(r1.FindString(os.Args[0])) + ".go"

	// open source file fopr reading
	file, err := os.Open(fn)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error reading self source file: %v\n", err)
		return
	}
	defer file.Close()

	// setup scanner to read source file
	scanner := bufio.NewScanner(file)

	// read each line and print it out
	for scanner.Scan() {
		// read line
		line := strings.TrimSpace(scanner.Text())
		fmt.Printf("%s\n", line)
	}
}
<commit_msg>Update to preserve indentation in source output<commit_after>
// Go program to print its own source code ( inspirted by TPOP (https://www.amazon.com/Practice-Programming-Addison-Wesley-Professional-Computing/dp/020161586X) 9-15)

package main

import (
	"bufio"
	"fmt"
	"os"
	"strings"
	"regexp"
)

func main() {
	// ensure source filename entered on command line
	if len(os.Args) < 1 {
		fmt.Println("Cannot read source file name from input arguments")
		return
	}

	// extract source file name from os.Args[0]
	// note: os.Args[0] would either be /log/path/name/progname (go run) or ./progname (compiled)
	// source file name is progname.go
 	r1, _ := regexp.Compile("(?i)[A-Z]+(?-i)$")
	fn := strings.TrimSpace(r1.FindString(os.Args[0])) + ".go"

	// open source file fopr reading
	file, err := os.Open(fn)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error reading self source file: %v\n", err)
		return
	}
	defer file.Close()

	// setup scanner to read source file
	scanner := bufio.NewScanner(file)

	// read each line and print it out
	for scanner.Scan() {
		// read line
		// line := strings.TrimSpace(scanner.Text())	// do not trimspace if output has to have indenting
		line := scanner.Text()
		fmt.Printf("%s\n", line)
	}
}
<|endoftext|>