<commit_before>package utils

// transfer2go/utils - Go utilities for transfer2go
//
// Copyright (c) 2017 - Valentin Kuznetsov <vkuznet@gmail.com>

import (
	"bytes"
	"crypto/tls"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httputil"
	"os"
	"os/user"
	"regexp"
	"time"

	"github.com/vkuznet/x509proxy"
)

// VERBOSE variable control verbosity level of client's utilities
var VERBOSE int

// UserDN function parses user Distinguished Name (DN) from client's HTTP request
func UserDN(r *http.Request) string {
	var names []interface{}
	for _, cert := range r.TLS.PeerCertificates {
		for _, name := range cert.Subject.Names {
			switch v := name.Value.(type) {
			case string:
				names = append(names, v)
			}
		}
	}
	return fmt.Sprintf("/DC=%s/DC=%s/OU=%s/OU=%s/CN=%s/CN=%s/CN=%s", names...)
}

// client X509 certificates
func tlsCerts() ([]tls.Certificate, error) {
	uproxy := os.Getenv("X509_USER_PROXY")
	uckey := os.Getenv("X509_USER_KEY")
	ucert := os.Getenv("X509_USER_CERT")

	// check if /tmp/x509up_u$UID exists, if so setup X509_USER_PROXY env
	u, err := user.Current()
	if err == nil {
		fname := fmt.Sprintf("/tmp/x509up_u%s", u.Uid)
		if _, err := os.Stat(fname); err == nil {
			uproxy = fname
		}
	}
	if VERBOSE > 1 {
		log.Println("uproxy", uproxy)
		log.Println("uckey", uckey)
		log.Println("ucert", ucert)
	}

	if uproxy == "" && uckey == "" { // user doesn't have neither proxy or user certs
		return nil, nil
	}
	if uproxy != "" {
		// use local implementation of LoadX409KeyPair instead of tls one
		x509cert, err := x509proxy.LoadX509Proxy(uproxy)
		if err != nil {
			return nil, fmt.Errorf("failed to parse proxy X509 proxy set by X509_USER_PROXY: %v", err)
		}
		return []tls.Certificate{x509cert}, nil
	}
	x509cert, err := tls.LoadX509KeyPair(ucert, uckey)
	if err != nil {
		return nil, fmt.Errorf("failed to parse user X509 certificate: %v", err)
	}
	return []tls.Certificate{x509cert}, nil
}

// HttpClient is HTTP client for urlfetch server
func HttpClient() (client *http.Client) {
	// get X509 certs
	certs, err := tlsCerts()
	if err != nil {
		panic(err.Error())
	}
	if len(certs) == 0 {
		client = &http.Client{}
		return
	}
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{Certificates: certs,
			InsecureSkipVerify: true},
	}
	client = &http.Client{Transport: tr}
	return
}

// create global HTTP client and re-use it through the code
var client = HttpClient()

// ResponseType structure is what we expect to get for our URL call.
// It contains a request URL, the data chunk and possible error from remote
type ResponseType struct {
	Url        string // response url
	Data       []byte // response data, i.e. what we got with Body of the response
	Error      error  // http error, a non-2xx return code is not an error
	Status     string // http status string
	StatusCode int    // http status code
}

func (r *ResponseType) String() string {
	return fmt.Sprintf("<Response: url=%s data=%s error=%v>", r.Url, string(r.Data), r.Error)
}

// UrlRequest structure holds details about url request's attributes
type UrlRequest struct {
	rurl string
	args string
	out  chan<- ResponseType
	ts   int64
}

// FetchResponse fetches data for provided URL, args is a json dump of arguments
func FetchResponse(rurl string, args []byte) ResponseType {
	startTime := time.Now()
	var response ResponseType
	response.Url = rurl
	if validateUrl(rurl) == false {
		response.Error = errors.New("Invalid URL")
		return response
	}
	var req *http.Request
	var e error
	if len(args) > 0 {
		req, e = http.NewRequest("POST", rurl, bytes.NewBuffer(args))
		req.Header.Set("Content-Type", "application/json")
	} else {
		req, e = http.NewRequest("GET", rurl, nil)
		if e != nil {
			log.Println("Unable to make GET request", e)
		}
		req.Header.Add("Accept", "*/*")
	}
	if VERBOSE > 1 {
		dump1, err1 := httputil.DumpRequestOut(req, true)
		log.Println("HTTP request", req, string(dump1), err1)
	}
	resp, err := client.Do(req)
	if err != nil {
		log.Println("HTTP ERROR", err)
		response.Error = err
		return response
	}
	response.Status = resp.Status
	response.StatusCode = resp.StatusCode
	if VERBOSE > 0 {
		if len(args) > 0 {
			log.Println("HTTP POST", rurl, string(args), err, time.Now().Sub(startTime))
		} else {
			log.Println("HTTP GET", rurl, string(args), err, time.Now().Sub(startTime))
		}
	}
	if VERBOSE > 1 {
		dump2, err2 := httputil.DumpResponse(resp, true)
		log.Println("HTTP response", string(dump2), err2)
	}
	if err != nil {
		response.Error = err
		return response
	}
	defer resp.Body.Close()
	response.Data, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		response.Error = err
	}
	return response
}

// Fetch data for provided URL and redirect results to given channel
func Fetch(rurl string, args []byte, ch chan<- ResponseType) {
	urlRetry := 3
	var resp, r ResponseType
	resp = FetchResponse(rurl, args)
	if resp.Error != nil {
		log.Println("TRANSFER2GO WARNING, fail to fetch data", rurl, "error", resp.Error)
		for i := 1; i <= urlRetry; i++ {
			sleep := time.Duration(i) * time.Second
			time.Sleep(sleep)
			r = FetchResponse(rurl, args)
			if r.Error == nil {
				break
			}
			log.Println("TRANSFER2GO WARNING", rurl, "retry", i, "error", r.Error)
		}
		resp = r
	}
	if resp.Error != nil {
		log.Println("TRANSFER2GO ERROR, fail to fetch data", rurl, "retries", urlRetry, "error", resp.Error)
	}
	ch <- resp
}

// Helper function which validates given URL
func validateUrl(rurl string) bool {
	if len(rurl) > 0 {
		pat := "(https|http)://[-A-Za-z0-9_+&@#/%?=~_|!:,.;]*[-A-Za-z0-9+&@#/%=~_|]"
		matched, err := regexp.MatchString(pat, rurl)
		if err == nil {
			if matched == true {
				return true
			}
		}
		log.Println("ERROR invalid URL:", rurl)
	}
	return false
}

// Response represents final response in a form of JSON structure
// we use custorm representation
func Response(rurl string, data []byte) []byte {
	b := []byte(`{"url":`)
	u := []byte(rurl)
	c := []byte(",")
	d := []byte(`"data":`)
	e := []byte(`}`)
	a := [][]byte{b, u, c, d, data, e}
	s := []byte(" ")
	r := bytes.Join(a, s)
	return r

}
<commit_msg>Fix userDN construction<commit_after>package utils

// transfer2go/utils - Go utilities for transfer2go
//
// Copyright (c) 2017 - Valentin Kuznetsov <vkuznet@gmail.com>

import (
	"bytes"
	"crypto/tls"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httputil"
	"os"
	"os/user"
	"regexp"
	"time"

	"github.com/vkuznet/x509proxy"
)

// VERBOSE variable control verbosity level of client's utilities
var VERBOSE int

// UserDN function parses user Distinguished Name (DN) from client's HTTP request
func UserDN(r *http.Request) string {
	var names []interface{}
	for _, cert := range r.TLS.PeerCertificates {
		for _, name := range cert.Issuer.Names {
			switch v := name.Value.(type) {
			case string:
				names = append(names, v)
			}
		}
	}
	parts := names[:7]
	return fmt.Sprintf("/DC=%s/DC=%s/OU=%s/OU=%s/CN=%s/CN=%s/CN=%s", parts...)
}

// client X509 certificates
func tlsCerts() ([]tls.Certificate, error) {
	uproxy := os.Getenv("X509_USER_PROXY")
	uckey := os.Getenv("X509_USER_KEY")
	ucert := os.Getenv("X509_USER_CERT")

	// check if /tmp/x509up_u$UID exists, if so setup X509_USER_PROXY env
	u, err := user.Current()
	if err == nil {
		fname := fmt.Sprintf("/tmp/x509up_u%s", u.Uid)
		if _, err := os.Stat(fname); err == nil {
			uproxy = fname
		}
	}
	if VERBOSE > 1 {
		log.Println("uproxy", uproxy)
		log.Println("uckey", uckey)
		log.Println("ucert", ucert)
	}

	if uproxy == "" && uckey == "" { // user doesn't have neither proxy or user certs
		return nil, nil
	}
	if uproxy != "" {
		// use local implementation of LoadX409KeyPair instead of tls one
		x509cert, err := x509proxy.LoadX509Proxy(uproxy)
		if err != nil {
			return nil, fmt.Errorf("failed to parse proxy X509 proxy set by X509_USER_PROXY: %v", err)
		}
		return []tls.Certificate{x509cert}, nil
	}
	x509cert, err := tls.LoadX509KeyPair(ucert, uckey)
	if err != nil {
		return nil, fmt.Errorf("failed to parse user X509 certificate: %v", err)
	}
	return []tls.Certificate{x509cert}, nil
}

// HttpClient is HTTP client for urlfetch server
func HttpClient() (client *http.Client) {
	// get X509 certs
	certs, err := tlsCerts()
	if err != nil {
		panic(err.Error())
	}
	if len(certs) == 0 {
		client = &http.Client{}
		return
	}
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{Certificates: certs,
			InsecureSkipVerify: true},
	}
	client = &http.Client{Transport: tr}
	return
}

// create global HTTP client and re-use it through the code
var client = HttpClient()

// ResponseType structure is what we expect to get for our URL call.
// It contains a request URL, the data chunk and possible error from remote
type ResponseType struct {
	Url        string // response url
	Data       []byte // response data, i.e. what we got with Body of the response
	Error      error  // http error, a non-2xx return code is not an error
	Status     string // http status string
	StatusCode int    // http status code
}

func (r *ResponseType) String() string {
	return fmt.Sprintf("<Response: url=%s data=%s error=%v>", r.Url, string(r.Data), r.Error)
}

// UrlRequest structure holds details about url request's attributes
type UrlRequest struct {
	rurl string
	args string
	out  chan<- ResponseType
	ts   int64
}

// FetchResponse fetches data for provided URL, args is a json dump of arguments
func FetchResponse(rurl string, args []byte) ResponseType {
	startTime := time.Now()
	var response ResponseType
	response.Url = rurl
	if validateUrl(rurl) == false {
		response.Error = errors.New("Invalid URL")
		return response
	}
	var req *http.Request
	var e error
	if len(args) > 0 {
		req, e = http.NewRequest("POST", rurl, bytes.NewBuffer(args))
		req.Header.Set("Content-Type", "application/json")
	} else {
		req, e = http.NewRequest("GET", rurl, nil)
		if e != nil {
			log.Println("Unable to make GET request", e)
		}
		req.Header.Add("Accept", "*/*")
	}
	if VERBOSE > 1 {
		dump1, err1 := httputil.DumpRequestOut(req, true)
		log.Println("HTTP request", req, string(dump1), err1)
	}
	resp, err := client.Do(req)
	if err != nil {
		log.Println("HTTP ERROR", err)
		response.Error = err
		return response
	}
	response.Status = resp.Status
	response.StatusCode = resp.StatusCode
	if VERBOSE > 0 {
		if len(args) > 0 {
			log.Println("HTTP POST", rurl, string(args), err, time.Now().Sub(startTime))
		} else {
			log.Println("HTTP GET", rurl, string(args), err, time.Now().Sub(startTime))
		}
	}
	if VERBOSE > 1 {
		dump2, err2 := httputil.DumpResponse(resp, true)
		log.Println("HTTP response", string(dump2), err2)
	}
	if err != nil {
		response.Error = err
		return response
	}
	defer resp.Body.Close()
	response.Data, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		response.Error = err
	}
	return response
}

// Fetch data for provided URL and redirect results to given channel
func Fetch(rurl string, args []byte, ch chan<- ResponseType) {
	urlRetry := 3
	var resp, r ResponseType
	resp = FetchResponse(rurl, args)
	if resp.Error != nil {
		log.Println("TRANSFER2GO WARNING, fail to fetch data", rurl, "error", resp.Error)
		for i := 1; i <= urlRetry; i++ {
			sleep := time.Duration(i) * time.Second
			time.Sleep(sleep)
			r = FetchResponse(rurl, args)
			if r.Error == nil {
				break
			}
			log.Println("TRANSFER2GO WARNING", rurl, "retry", i, "error", r.Error)
		}
		resp = r
	}
	if resp.Error != nil {
		log.Println("TRANSFER2GO ERROR, fail to fetch data", rurl, "retries", urlRetry, "error", resp.Error)
	}
	ch <- resp
}

// Helper function which validates given URL
func validateUrl(rurl string) bool {
	if len(rurl) > 0 {
		pat := "(https|http)://[-A-Za-z0-9_+&@#/%?=~_|!:,.;]*[-A-Za-z0-9+&@#/%=~_|]"
		matched, err := regexp.MatchString(pat, rurl)
		if err == nil {
			if matched == true {
				return true
			}
		}
		log.Println("ERROR invalid URL:", rurl)
	}
	return false
}

// Response represents final response in a form of JSON structure
// we use custorm representation
func Response(rurl string, data []byte) []byte {
	b := []byte(`{"url":`)
	u := []byte(rurl)
	c := []byte(",")
	d := []byte(`"data":`)
	e := []byte(`}`)
	a := [][]byte{b, u, c, d, data, e}
	s := []byte(" ")
	r := bytes.Join(a, s)
	return r

}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"strings"
)

// Truncate the given string to length using … as ellipsis.
func Truncate(s string, length int) string {
	return TruncateWithEllipsis(s, length, "…")
}

// TruncateWithEllipsis truncates the given string to length using provided ellipsis.
func TruncateWithEllipsis(s string, length int, ellipsis string) string {

	l := len(s)
	el := len(ellipsis)
	if l+el > length {
		s = string(s[0:length-el]) + ellipsis
	}
	return s
}

// ToPlural provides the plural version of an English word using some simple rules and a table of exceptions.
func ToPlural(text string) (plural string) {

	// We only deal with lowercase
	word := strings.ToLower(text)

	// Check translations first, and return a direct translation if there is one
	if translations[word] != "" {
		return translations[word]
	}

	// If we have no translation, just follow some basic rules - avoid new rules if possible
	if strings.HasSuffix(word, "s") || strings.HasSuffix(word, "z") || strings.HasSuffix(word, "h") {
		plural = word + "es"
	} else if strings.HasSuffix(word, "y") {
		plural = strings.TrimRight(word, "y") + "ies"
	} else if strings.HasSuffix(word, "um") {
		plural = strings.TrimRight(word, "um") + "a"
	} else {
		plural = word + "s"
	}

	return plural
}

// Which irregulars are important or correct depends on your usage of English
// Some of those below are now considered old-fashioned and many more could be added
// As this is used for database models, it only needs a limited subset of all irregulars
// NB you should not attempt to reverse and singularize, but just use the singular provided
var translations = map[string]string{
	"addendum":    "addenda", // or addendums
	"aircraft":    "aircraft",
	"alumna":      "alumnae",
	"alumnus":     "alumni",
	"analysis":    "analyses",
	"antenna":     "antennae", // or antennas
	"antithesis":  "antitheses",
	"apex":        "apices",     // or apexes
	"appendix":    "appendices", // or appendixes
	"axis":        "axes",
	"bacillus":    "bacilli",
	"bacterium":   "bacteria",
	"basis":       "bases",
	"beau":        "beaux",   // or beaus
	"belief":      "beliefs", // not irregular?
	"bison":       "bison",
	"buffalo":     "buffalo",
	"bureau":      "bureaux", // or bureaus
	"cactus":      "cacti",   // or cactus or cactuses
	"chassis":     "chassis",
	"château":     "châteaux", // or châteaus
	"child":       "children",
	"codex":       "codices",
	"concerto":    "concerti", // or concertos
	"corpus":      "corpora",
	"crisis":      "crises",
	"criterion":   "criteria",  // or criterions
	"curriculum":  "curricula", // or curriculums
	"datum":       "data",
	"day":         "days",
	"deer":        "deer", // or deers
	"die":         "dice",
	"diagnosis":   "diagnoses",
	"die":         "dice",    // or dies
	"dwarf":       "dwarves", // or dwarfs
	"ellipsis":    "ellipses",
	"erratum":     "errata",
	"faux pas":    "faux pas",
	"fez":         "fezzes",   // or fezes
	"fish":        "fish",     // or fishes
	"focus":       "foci",     // or focuses
	"foot":        "feet",     // or foot
	"formula":     "formulae", // or formulas
	"fungus":      "fungi",    // or funguses
	"ganglion":    "ganglia",
	"genus":       "genera", // or genuses
	"goose":       "geese",
	"graffito":    "graffiti",
	"grouse":      "grouse", // or grouses
	"half":        "halves",
	"hero":        "heroes",
	"hoof":        "hooves", // or hoofs
	"hypothesis":  "hypotheses",
	"index":       "indices", // or indexes
	"information": "information",
	"larva":       "larvae",   // or larvas
	"libretto":    "libretti", // or librettos
	"life":        "lives",
	"loaf":        "loaves",
	"locus":       "loci",
	"louse":       "lice",
	"man":         "men",
	"matrix":      "matrices",  // or matrixes
	"medium":      "media",     // or mediums
	"memorandum":  "memoranda", // or memorandums
	"millennium":  "millennia",
	"minutia":     "minutiae",
	"moose":       "moose",
	"money":       "monies", // not irregular?
	"monkey":      "monkeys",
	"mouse":       "mice",
	"nebula":      "nebulae", // or nebulas
	"nucleus":     "nuclei",  // or nucleuses
	"oasis":       "oases",
	"offspring":   "offspring", // or offsprings
	"opus":        "opera",     // or opuses
	"octopus":     "octopodes",
	"ovum":        "ova",
	"ox":          "oxen", // or ox
	"parenthesis": "parentheses",
	"person":      "people",
	"phenomenon":  "phenomena", // or phenomenons
	"phylum":      "phyla",
	"prognosis":   "prognoses",
	"quiz":        "quizzes",
	"radius":      "radii",     // or radiuses
	"referendum":  "referenda", // or referendums
	"salmon":      "salmon",    // or salmons
	"scarf":       "scarves",   // or scarfs
	"self":        "selves",
	"series":      "series",
	"sheep":       "sheep",
	"shrimp":      "shrimp", // or shrimps
	"shelf":       "shelves",
	"species":     "species",
	"stimulus":    "stimuli",
	"stratum":     "strata",
	"swine":       "swine",
	"syllabus":    "syllabi",  // or syllabuses
	"symposium":   "symposia", // or symposiums
	"synopsis":    "synopses",
	"supernova":   "supernovae",
	"tableau":     "tableaux", // or tableaus
	"thesis":      "theses",
	"thief":       "thieves",
	"tooth":       "teeth",
	"trout":       "trout",     // or trouts
	"tuna":        "tuna",      // or tunas
	"vertebra":    "vertebrae", // or vertebras
	"vertex":      "verticies", // or vertexes
	"vita":        "vitae",
	"vortex":      "vorticies", // or vortexes
	"wharf":       "wharves",   // or wharfs
	"wife":        "wives",
	"wolf":        "wolves",
	"woman":       "women",
	// ..etc
}

// ToSnake converts a string from struct field names to corresponding database column names (e.g. FieldName to field_name)
func ToSnake(text string) string {
	b := bytes.NewBufferString("")
	for i, c := range text {
		if i > 0 && c >= 'A' && c <= 'Z' {
			b.WriteRune('_')
		}
		b.WriteRune(c)
	}
	return strings.ToLower(b.String())
}

// ToCamel converts a string from database column names to corresponding struct field names (e.g. field_name to FieldName)
func ToCamel(text string, private ...bool) string {
	lowerCamel := false
	if private != nil {
		lowerCamel = private[0]
	}
	b := bytes.NewBufferString("")
	s := strings.Split(text, "_")
	for i, v := range s {
		if len(v) > 0 {
			s := v[:1]
			if i > 0 || lowerCamel == false {
				s = strings.ToUpper(s)
			}
			b.WriteString(s)
			b.WriteString(v[1:])
		}
	}
	return b.String()
}
<commit_msg>Added an irregular plural for chateau, sans circumflex<commit_after>package main

import (
	"bytes"
	"strings"
)

// Truncate the given string to length using … as ellipsis.
func Truncate(s string, length int) string {
	return TruncateWithEllipsis(s, length, "…")
}

// TruncateWithEllipsis truncates the given string to length using provided ellipsis.
func TruncateWithEllipsis(s string, length int, ellipsis string) string {

	l := len(s)
	el := len(ellipsis)
	if l+el > length {
		s = string(s[0:length-el]) + ellipsis
	}
	return s
}

// ToPlural provides the plural version of an English word using some simple rules and a table of exceptions.
func ToPlural(text string) (plural string) {

	// We only deal with lowercase
	word := strings.ToLower(text)

	// Check translations first, and return a direct translation if there is one
	if translations[word] != "" {
		return translations[word]
	}

	// If we have no translation, just follow some basic rules - avoid new rules if possible
	if strings.HasSuffix(word, "s") || strings.HasSuffix(word, "z") || strings.HasSuffix(word, "h") {
		plural = word + "es"
	} else if strings.HasSuffix(word, "y") {
		plural = strings.TrimRight(word, "y") + "ies"
	} else if strings.HasSuffix(word, "um") {
		plural = strings.TrimRight(word, "um") + "a"
	} else {
		plural = word + "s"
	}

	return plural
}

// Which irregulars are important or correct depends on your usage of English
// Some of those below are now considered old-fashioned and many more could be added
// As this is used for database models, it only needs a limited subset of all irregulars
// NB you should not attempt to reverse and singularize, but just use the singular provided
var translations = map[string]string{
	"addendum":    "addenda", // or addendums
	"aircraft":    "aircraft",
	"alumna":      "alumnae",
	"alumnus":     "alumni",
	"analysis":    "analyses",
	"antenna":     "antennae", // or antennas
	"antithesis":  "antitheses",
	"apex":        "apices",     // or apexes
	"appendix":    "appendices", // or appendixes
	"axis":        "axes",
	"bacillus":    "bacilli",
	"bacterium":   "bacteria",
	"basis":       "bases",
	"beau":        "beaux",   // or beaus
	"belief":      "beliefs", // not irregular?
	"bison":       "bison",
	"buffalo":     "buffalo",
	"bureau":      "bureaux", // or bureaus
	"cactus":      "cacti",   // or cactus or cactuses
	"chassis":     "chassis",
	"chateau":     "chateaux", // or chateaus
	"château":     "châteaux", // or châteaus
	"child":       "children",
	"codex":       "codices",
	"concerto":    "concerti", // or concertos
	"corpus":      "corpora",
	"crisis":      "crises",
	"criterion":   "criteria",  // or criterions
	"curriculum":  "curricula", // or curriculums
	"datum":       "data",
	"day":         "days",
	"deer":        "deer", // or deers
	"die":         "dice",
	"diagnosis":   "diagnoses",
	"die":         "dice",    // or dies
	"dwarf":       "dwarves", // or dwarfs
	"ellipsis":    "ellipses",
	"erratum":     "errata",
	"faux pas":    "faux pas",
	"fez":         "fezzes",   // or fezes
	"fish":        "fish",     // or fishes
	"focus":       "foci",     // or focuses
	"foot":        "feet",     // or foot
	"formula":     "formulae", // or formulas
	"fungus":      "fungi",    // or funguses
	"ganglion":    "ganglia",
	"genus":       "genera", // or genuses
	"goose":       "geese",
	"graffito":    "graffiti",
	"grouse":      "grouse", // or grouses
	"half":        "halves",
	"hero":        "heroes",
	"hoof":        "hooves", // or hoofs
	"hypothesis":  "hypotheses",
	"index":       "indices", // or indexes
	"information": "information",
	"larva":       "larvae",   // or larvas
	"libretto":    "libretti", // or librettos
	"life":        "lives",
	"loaf":        "loaves",
	"locus":       "loci",
	"louse":       "lice",
	"man":         "men",
	"matrix":      "matrices",  // or matrixes
	"medium":      "media",     // or mediums
	"memorandum":  "memoranda", // or memorandums
	"millennium":  "millennia",
	"minutia":     "minutiae",
	"moose":       "moose",
	"money":       "monies", // not irregular?
	"monkey":      "monkeys",
	"mouse":       "mice",
	"nebula":      "nebulae", // or nebulas
	"nucleus":     "nuclei",  // or nucleuses
	"oasis":       "oases",
	"offspring":   "offspring", // or offsprings
	"opus":        "opera",     // or opuses
	"octopus":     "octopodes",
	"ovum":        "ova",
	"ox":          "oxen", // or ox
	"parenthesis": "parentheses",
	"person":      "people",
	"phenomenon":  "phenomena", // or phenomenons
	"phylum":      "phyla",
	"prognosis":   "prognoses",
	"quiz":        "quizzes",
	"radius":      "radii",     // or radiuses
	"referendum":  "referenda", // or referendums
	"salmon":      "salmon",    // or salmons
	"scarf":       "scarves",   // or scarfs
	"self":        "selves",
	"series":      "series",
	"sheep":       "sheep",
	"shrimp":      "shrimp", // or shrimps
	"shelf":       "shelves",
	"species":     "species",
	"stimulus":    "stimuli",
	"stratum":     "strata",
	"swine":       "swine",
	"syllabus":    "syllabi",  // or syllabuses
	"symposium":   "symposia", // or symposiums
	"synopsis":    "synopses",
	"supernova":   "supernovae",
	"tableau":     "tableaux", // or tableaus
	"thesis":      "theses",
	"thief":       "thieves",
	"tooth":       "teeth",
	"trout":       "trout",     // or trouts
	"tuna":        "tuna",      // or tunas
	"vertebra":    "vertebrae", // or vertebras
	"vertex":      "verticies", // or vertexes
	"vita":        "vitae",
	"vortex":      "vorticies", // or vortexes
	"wharf":       "wharves",   // or wharfs
	"wife":        "wives",
	"wolf":        "wolves",
	"woman":       "women",
	// ..etc
}

// ToSnake converts a string from struct field names to corresponding database column names (e.g. FieldName to field_name)
func ToSnake(text string) string {
	b := bytes.NewBufferString("")
	for i, c := range text {
		if i > 0 && c >= 'A' && c <= 'Z' {
			b.WriteRune('_')
		}
		b.WriteRune(c)
	}
	return strings.ToLower(b.String())
}

// ToCamel converts a string from database column names to corresponding struct field names (e.g. field_name to FieldName)
func ToCamel(text string, private ...bool) string {
	lowerCamel := false
	if private != nil {
		lowerCamel = private[0]
	}
	b := bytes.NewBufferString("")
	s := strings.Split(text, "_")
	for i, v := range s {
		if len(v) > 0 {
			s := v[:1]
			if i > 0 || lowerCamel == false {
				s = strings.ToUpper(s)
			}
			b.WriteString(s)
			b.WriteString(v[1:])
		}
	}
	return b.String()
}
<|endoftext|>


<commit_before>package log

//Logger logger 对象
var Logger LoggerInterface

func init() {
	Logger = &DefaultLogger{}
}

//InitLogger 初始化logger对象
func InitLogger(ILogger LoggerInterface) {
	Logger = ILogger
}

//LoggerInterface 日志接口
type LoggerInterface interface {
	Debugf(format string, params ...interface{})

	Infof(format string, params ...interface{})

	Warnf(format string, params ...interface{}) error

	Errorf(format string, params ...interface{}) error

	Debug(v ...interface{})

	Info(v ...interface{})

	Warn(v ...interface{}) error

	Error(v ...interface{}) error
}

//Debugf debug 格式化
func Debugf(format string, params ...interface{}) {
	Logger.Debugf(format, params)
}

//Infof 打印info
func Infof(format string, params ...interface{}) {
	Logger.Infof(format, params)
}

//Warnf warn格式化
func Warnf(format string, params ...interface{}) error {
	return Logger.Warnf(format, params)
}

//Errorf error格式化
func Errorf(format string, params ...interface{}) error {
	return Logger.Errorf(format, params)
}

//Debug 打印debug
func Debug(v ...interface{}) {
	Logger.Debug(v)
}

//Info 打印Info
func Info(v ...interface{}) {
	Logger.Info(v)
}

//Warn 打印Warn
func Warn(v ...interface{}) {
	Logger.Warn(v)
}

//Error 打印Error
func Error(v ...interface{}) {
	Logger.Error(v)
}

//DefaultLogger 默认日志实现
type DefaultLogger struct {
}

//Debugf debug 格式化
func (d *DefaultLogger) Debugf(format string, params ...interface{}) {

}

//Infof 打印info
func (d *DefaultLogger) Infof(format string, params ...interface{}) {

}

//Warnf warn格式化
func (d *DefaultLogger) Warnf(format string, params ...interface{}) error {
	return nil
}

//Errorf error格式化
func (d *DefaultLogger) Errorf(format string, params ...interface{}) error {
	return nil
}

//Debug 打印debug
func (d *DefaultLogger) Debug(v ...interface{}) {

}

//Info 打印Info
func (d *DefaultLogger) Info(v ...interface{}) {

}

//Warn 打印Warn
func (d *DefaultLogger) Warn(v ...interface{}) error {
	return nil
}

//Error 打印Error
func (d *DefaultLogger) Error(v ...interface{}) error {
	return nil
}
<commit_msg>删除LoggerInterfac接口中方法的返回值<commit_after>package log

//Logger logger 对象
var Logger LoggerInterface

func init() {
	Logger = &DefaultLogger{}
}

//InitLogger 初始化logger对象
func InitLogger(ILogger LoggerInterface) {
	Logger = ILogger
}

//LoggerInterface 日志接口
type LoggerInterface interface {
	Debugf(format string, params ...interface{})

	Infof(format string, params ...interface{})

	Warnf(format string, params ...interface{})

	Errorf(format string, params ...interface{})

	Debug(v ...interface{})

	Info(v ...interface{})

	Warn(v ...interface{})

	Error(v ...interface{})
}

//Debugf debug 格式化
func Debugf(format string, params ...interface{}) {
	Logger.Debugf(format, params)
}

//Infof 打印info
func Infof(format string, params ...interface{}) {
	Logger.Infof(format, params)
}

//Warnf warn格式化
func Warnf(format string, params ...interface{}) {
	Logger.Warnf(format, params)
}

//Errorf error格式化
func Errorf(format string, params ...interface{}) {
	Logger.Errorf(format, params)
}

//Debug 打印debug
func Debug(v ...interface{}) {
	Logger.Debug(v)
}

//Info 打印Info
func Info(v ...interface{}) {
	Logger.Info(v)
}

//Warn 打印Warn
func Warn(v ...interface{}) {
	Logger.Warn(v)
}

//Error 打印Error
func Error(v ...interface{}) {
	Logger.Error(v)
}

//DefaultLogger 默认日志实现
type DefaultLogger struct {
}

//Debugf debug 格式化
func (d *DefaultLogger) Debugf(format string, params ...interface{}) {

}

//Infof 打印info
func (d *DefaultLogger) Infof(format string, params ...interface{}) {

}

//Warnf warn格式化
func (d *DefaultLogger) Warnf(format string, params ...interface{}) {
}

//Errorf error格式化
func (d *DefaultLogger) Errorf(format string, params ...interface{}) {
}

//Debug 打印debug
func (d *DefaultLogger) Debug(v ...interface{}) {

}

//Info 打印Info
func (d *DefaultLogger) Info(v ...interface{}) {

}

//Warn 打印Warn
func (d *DefaultLogger) Warn(v ...interface{}) {
}

//Error 打印Error
func (d *DefaultLogger) Error(v ...interface{}) {
}
<|endoftext|>


<commit_before>package template

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

	"github.com/ironman-project/ironman/pkg/template/engine"
	"github.com/ironman-project/ironman/pkg/template/engine/goengine"
	"github.com/ironman-project/ironman/pkg/template/model"
	"github.com/ironman-project/ironman/pkg/template/values"
	"github.com/pkg/errors"
)

//arbitrary number
const noGeneratorWorkers = 20

//GeneratorData represents the data to be passed to each generator file template
type GeneratorData struct {
	Template  *model.Template
	Generator *model.Generator
	Values    values.Values
}

var _ Generator = (*generator)(nil)

//Generator defines a template generator
type Generator interface {
	Generate(context context.Context) error
}

type generator struct {
	path           string
	generationPath string
	ignore         []string
	data           GeneratorData
	engine         engine.Factory
	out            io.Writer
}

//NewGenerator returns a new instance of a generator
func NewGenerator(path string, generationPath string, data GeneratorData, options ...GeneratorOption) Generator {

	g := &generator{
		path:           path,
		generationPath: generationPath,
		data:           data,
		ignore:         []string{".ironman.yaml"},
		engine: func() engine.Engine {
			return goengine.New("ironman")
		},
		out: os.Stdout,
	}

	for _, option := range options {
		option(g)
	}

	return g
}

type processResult struct {
	bytes              []byte
	templatePathResult templatePathResult
	err                error
}

type writeResult struct {
	pathFrom string
	pathTo   string
	err      error
}

type templatePathResult struct {
	path  string
	isDir bool
}

func (g *generator) Generate(ctx context.Context) error {
	gdata := g.data.Generator
	//Generate a file only if the generator type is file
	if g.data.Generator.TType == model.GeneratorTypeFile {
		if gdata.FileTypeOptions.DefaultTemplateFile == "" {
			return errors.Errorf("The default template file for the file generator %s is not set", gdata.ID)
		}
		templateFilePath := filepath.Join(g.path, gdata.FileTypeOptions.DefaultTemplateFile)
		presult := templatePathResult{templateFilePath, false}
		bytes, err := g.processFile(presult)
		if err != nil {
			return errors.Wrapf(err, "failed to process generator %s for template %s", gdata.ID, templateFilePath)
		}

		wr := g.writeFile(processResult{
			bytes,
			presult,
			nil,
		})

		if wr.err != nil {
			return wr.err
		}

		return nil
	}

	//The default if type is empty is directory
	childCtx, cancelFunc := context.WithCancel(ctx)
	defer cancelFunc()

	paths, errc := g.walkTemplateFiles(childCtx)

	presults := make(chan processResult)

	workersExecute(noGeneratorWorkers, func(w int, wg *sync.WaitGroup) {
		g.processor(childCtx, paths, presults)
		wg.Done()
	}, func() {
		close(presults)

	})

	wresults := make(chan writeResult)
	workersExecute(noGeneratorWorkers, func(w int, wg *sync.WaitGroup) {
		g.write(childCtx, presults, wresults)
		wg.Done()
	},
		func() {
			close(wresults)

		},
	)

	for wresult := range wresults {

		if wresult.err != nil {
			cancelFunc()
			return wresult.err
		}
	}

	err := <-errc

	if err != nil {
		return errors.Wrapf(err, "failed to process generator path templates: %s", g.path)
	}

	return nil
}

func workersExecute(number int, work func(workerID int, wg *sync.WaitGroup), done func()) {
	var wg sync.WaitGroup
	wg.Add(number)
	for i := 0; i < number; i++ {
		go work(i, &wg)
	}
	go func() {
		wg.Wait()
		done()
	}()
}

func (g *generator) walkTemplateFiles(context context.Context) (<-chan templatePathResult, <-chan error) {
	errc := make(chan error, 1)
	paths := make(chan templatePathResult)

	go func() {
		defer close(paths)
		defer close(errc)
		errc <- filepath.Walk(g.path, func(path string, info os.FileInfo, err error) error {

			if err != nil {
				return err
			}

			if info.IsDir() && path == g.path {
				return nil
			}

			if !info.IsDir() && !info.Mode().IsRegular() {
				return nil
			}

			if g.ignoreFile(filepath.Base(path)) {
				return nil
			}

			select {
			case paths <- templatePathResult{path, info.IsDir()}:
			case <-context.Done():
				return errors.New("Walk canceled")

			}
			return nil
		})
	}()

	return paths, errc
}

func (g *generator) ignoreFile(fileName string) bool {
	for _, ignore := range g.ignore {
		if ignore == fileName {
			return true
		}
	}
	return false
}

func (g *generator) processor(context context.Context, paths <-chan templatePathResult, result chan<- processResult) {
	for path := range paths {
		data, err := g.processFile(path)
		select {
		case result <- processResult{data, path, err}:
		case <-context.Done():
			return
		}
	}
}

func (g *generator) processFile(templatePathResult templatePathResult) ([]byte, error) {

	if templatePathResult.isDir {
		return nil, nil
	}

	data, err := ioutil.ReadFile(templatePathResult.path)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to read template contents %s", templatePathResult.path)
	}
	engine := g.engine()
	tmpl, err := engine.Parse(string(data))

	if err != nil {
		return nil, errors.Wrapf(err, "failed to parse template %s ", err)
	}

	var buffer bytes.Buffer
	err = tmpl.Execute(&buffer, g.data)

	if err != nil {
		return nil, errors.Wrapf(err, "failed to execute template processing %s", templatePathResult.path)
	}
	return buffer.Bytes(), nil
}

func (g *generator) write(context context.Context, processResults <-chan processResult, result chan<- writeResult) {

	for processResult := range processResults {
		select {
		case result <- g.writeFile(processResult):
		case <-context.Done():
			return
		}
	}

}

func (g *generator) writeFile(presult processResult) writeResult {

	if presult.err != nil {
		return writeResult{err: presult.err}
	}

	toRelativePath := strings.TrimPrefix(presult.templatePathResult.path, g.path)
	generationDir := g.generationPath
	if g.data.Generator.TType == model.GeneratorTypeFile {
		//Join relative extra path from the defined generation path
		//e.g ironman generate template:controller /path/to/newController.go
		//Generation path => controller.go
		//Base Path => /path/to
		//Generator defined Relative path to base path controllers (directory)
		//output should be /path/to/controllers/newController.go
		basePath := filepath.Dir(toRelativePath)
		fileName := filepath.Base(g.generationPath)
		newPath := filepath.Join(basePath, g.data.Generator.FileTypeOptions.FileGenerationRelativePath, fileName)
		toRelativePath = newPath
		generationDir = filepath.Dir(generationDir)
	}

	toPath := filepath.Join(generationDir, toRelativePath)

	if presult.templatePathResult.isDir {

		return writeResult{pathFrom: presult.templatePathResult.path, pathTo: toPath}
	}

	fmt.Fprint(g.out, "Writing... ", toPath)

	//Create directory
	dir := filepath.Dir(toPath)
	if _, err := os.Stat(dir); os.IsNotExist(err) {

		err := os.Mkdir(dir, os.ModePerm)
		if err != nil && !os.IsExist(err) {
			return writeResult{err: errors.Wrap(err, "failed to create generation directory")}
		}

	}

	err := ioutil.WriteFile(toPath, presult.bytes, os.ModePerm)

	if err != nil {
		return writeResult{err: err}
	}
	return writeResult{pathFrom: presult.templatePathResult.path, pathTo: toPath}
}
<commit_msg> Fix nested directories generation<commit_after>package template

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

	"github.com/ironman-project/ironman/pkg/template/engine"
	"github.com/ironman-project/ironman/pkg/template/engine/goengine"
	"github.com/ironman-project/ironman/pkg/template/model"
	"github.com/ironman-project/ironman/pkg/template/values"
	"github.com/pkg/errors"
)

//arbitrary number
const noGeneratorWorkers = 1

//GeneratorData represents the data to be passed to each generator file template
type GeneratorData struct {
	Template  *model.Template
	Generator *model.Generator
	Values    values.Values
}

var _ Generator = (*generator)(nil)

//Generator defines a template generator
type Generator interface {
	Generate(context context.Context) error
}

type generator struct {
	path           string
	generationPath string
	ignore         []string
	data           GeneratorData
	engine         engine.Factory
	out            io.Writer
}

//NewGenerator returns a new instance of a generator
func NewGenerator(path string, generationPath string, data GeneratorData, options ...GeneratorOption) Generator {

	g := &generator{
		path:           path,
		generationPath: generationPath,
		data:           data,
		ignore:         []string{".ironman.yaml"},
		engine: func() engine.Engine {
			return goengine.New("ironman")
		},
		out: os.Stdout,
	}

	for _, option := range options {
		option(g)
	}

	return g
}

type processResult struct {
	bytes              []byte
	templatePathResult templatePathResult
	err                error
}

type writeResult struct {
	pathFrom string
	pathTo   string
	err      error
}

type templatePathResult struct {
	path  string
	isDir bool
}

func (g *generator) Generate(ctx context.Context) error {
	gdata := g.data.Generator
	//Generate a file only if the generator type is file
	if g.data.Generator.TType == model.GeneratorTypeFile {
		if gdata.FileTypeOptions.DefaultTemplateFile == "" {
			return errors.Errorf("The default template file for the file generator %s is not set", gdata.ID)
		}
		templateFilePath := filepath.Join(g.path, gdata.FileTypeOptions.DefaultTemplateFile)
		presult := templatePathResult{templateFilePath, false}
		bytes, err := g.processFile(presult)
		if err != nil {
			return errors.Wrapf(err, "failed to process generator %s for template %s", gdata.ID, templateFilePath)
		}

		wr := g.writeFile(processResult{
			bytes,
			presult,
			nil,
		})

		if wr.err != nil {
			return wr.err
		}

		return nil
	}

	//The default if type is empty is directory
	childCtx, cancelFunc := context.WithCancel(ctx)
	defer cancelFunc()

	paths, errc := g.walkTemplateFiles(childCtx)

	presults := make(chan processResult)

	workersExecute(noGeneratorWorkers, func(w int, wg *sync.WaitGroup) {
		g.processor(childCtx, paths, presults)
		wg.Done()
	}, func() {
		close(presults)

	})

	wresults := make(chan writeResult)
	workersExecute(noGeneratorWorkers, func(w int, wg *sync.WaitGroup) {
		g.write(childCtx, presults, wresults)
		wg.Done()
	},
		func() {
			close(wresults)

		},
	)

	for wresult := range wresults {

		if wresult.err != nil {
			cancelFunc()
			return wresult.err
		}
	}

	err := <-errc

	if err != nil {
		return errors.Wrapf(err, "failed to process generator path templates: %s", g.path)
	}

	return nil
}

func workersExecute(number int, work func(workerID int, wg *sync.WaitGroup), done func()) {
	var wg sync.WaitGroup
	wg.Add(number)
	for i := 0; i < number; i++ {
		go work(i, &wg)
	}
	go func() {
		wg.Wait()
		done()
	}()
}

func (g *generator) walkTemplateFiles(context context.Context) (<-chan templatePathResult, <-chan error) {
	errc := make(chan error, 1)
	paths := make(chan templatePathResult)

	go func() {
		defer close(paths)
		defer close(errc)
		errc <- filepath.Walk(g.path, func(path string, info os.FileInfo, err error) error {

			if err != nil {
				return err
			}

			if info.IsDir() && path == g.path {
				return nil
			}

			if !info.IsDir() && !info.Mode().IsRegular() {
				return nil
			}

			if g.ignoreFile(filepath.Base(path)) {
				return nil
			}

			select {
			case paths <- templatePathResult{path, info.IsDir()}:
			case <-context.Done():
				return errors.New("Walk canceled")

			}
			return nil
		})
	}()

	return paths, errc
}

func (g *generator) ignoreFile(fileName string) bool {
	for _, ignore := range g.ignore {
		if ignore == fileName {
			return true
		}
	}
	return false
}

func (g *generator) processor(context context.Context, paths <-chan templatePathResult, result chan<- processResult) {
	for path := range paths {
		data, err := g.processFile(path)
		select {
		case result <- processResult{data, path, err}:
		case <-context.Done():
			return
		}
	}
}

func (g *generator) processFile(templatePathResult templatePathResult) ([]byte, error) {

	if templatePathResult.isDir {
		return nil, nil
	}

	data, err := ioutil.ReadFile(templatePathResult.path)
	if err != nil {
		return nil, errors.Wrapf(err, "failed to read template contents %s", templatePathResult.path)
	}
	engine := g.engine()
	tmpl, err := engine.Parse(string(data))

	if err != nil {
		return nil, errors.Wrapf(err, "failed to parse template %s %s ", templatePathResult.path, err)
	}

	var buffer bytes.Buffer
	err = tmpl.Execute(&buffer, g.data)

	if err != nil {
		return nil, errors.Wrapf(err, "failed to execute template processing %s", templatePathResult.path)
	}
	return buffer.Bytes(), nil
}

func (g *generator) write(context context.Context, processResults <-chan processResult, result chan<- writeResult) {

	for processResult := range processResults {
		select {
		case result <- g.writeFile(processResult):
		case <-context.Done():
			return
		}
	}

}

func (g *generator) writeFile(presult processResult) writeResult {

	if presult.err != nil {
		return writeResult{err: presult.err}
	}

	toRelativePath := strings.TrimPrefix(presult.templatePathResult.path, g.path)
	generationDir := g.generationPath
	if g.data.Generator.TType == model.GeneratorTypeFile {
		//Join relative extra path from the defined generation path
		//e.g ironman generate template:controller /path/to/newController.go
		//Generation path => controller.go
		//Base Path => /path/to
		//Generator defined Relative path to base path controllers (directory)
		//output should be /path/to/controllers/newController.go
		basePath := filepath.Dir(toRelativePath)
		fileName := filepath.Base(g.generationPath)
		newPath := filepath.Join(basePath, g.data.Generator.FileTypeOptions.FileGenerationRelativePath, fileName)
		toRelativePath = newPath
		generationDir = filepath.Dir(generationDir)
	}

	toPath := filepath.Join(generationDir, toRelativePath)

	if presult.templatePathResult.isDir {

		return writeResult{pathFrom: presult.templatePathResult.path, pathTo: toPath}
	}

	fmt.Fprintln(g.out, "Writing... ", toPath)

	//Create directory
	dir := filepath.Dir(toPath)
	if _, err := os.Stat(dir); os.IsNotExist(err) {

		err := os.MkdirAll(dir, os.ModePerm)
		if err != nil && !os.IsExist(err) {
			return writeResult{err: errors.Wrap(err, "failed to create generation directory")}
		}

	}

	err := ioutil.WriteFile(toPath, presult.bytes, os.ModePerm)

	if err != nil {
		return writeResult{err: err}
	}
	return writeResult{pathFrom: presult.templatePathResult.path, pathTo: toPath}
}
<|endoftext|>


<commit_before>// Package stock implements a plugin to get stock quotes.
package stock

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strings"

	"github.com/StalkR/goircbot/bot"
	"github.com/StalkR/goircbot/lib/transport"
)

const quoteURL = "https://api.iextrading.com/1.0/stock/%s/quote"

type quote struct {
	Symbol                string
	CompanyName           string
	PrimaryExchange       string
	Sector                string
	CalculationPrice      string
	Open                  float64
	OpenTime              int64
	Close                 float64
	CloseTime             int64
	High                  float64
	Low                   float64
	LatestPrice           float64
	LatestSource          string
	LatestTime            string
	LatestUpdate          int64
	LatestVolume          int64
	IexRealtimePrice      float64
	IexRealtimeSize       int64
	iexLastUpdated        int64
	DelayedPrice          float64
	DelayedPriceTime      int64
	ExtendedPrice         float64
	ExtendedChange        float64
	ExtendedChangePercent float64
	ExtendedPriceTime     int64
	PreviousClose         float64
	Change                float64
	ChangePercent         float64
	IexMarketPercent      float64
	IexVolume             int64
	AvgTotalVolume        int64
	IexBidPrice           float64
	IexBidSize            int64
	IexAskPrice           float64
	IexAskSize            int64
	MarketCap             int64
	PeRatio               float64
	Week52High            float64
	Week52Low             float64
	YtdChange             float64
}

func (q *quote) String() string {
	plus := ""
	if q.Change > 0 {
		plus = "+"
	}
	return fmt.Sprintf("%v (%v): %v (%v%v, %v%v%%)",
		q.Symbol, q.CompanyName, q.LatestPrice, plus, q.Change, plus, q.ChangePercent)
}

func stock(symbol string) (*quote, error) {
	uri := fmt.Sprintf(quoteURL, url.PathEscape(symbol))
	c, err := transport.Client(uri)
	if err != nil {
		return nil, err
	}
	resp, err := c.Get(uri)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("%v", resp.Status)
	}
	var v quote
	if err := json.NewDecoder(resp.Body).Decode(&v); err != nil {
		return nil, err
	}
	return &v, nil
}

func handleStock(e *bot.Event) {
	symbol := strings.TrimSpace(e.Args)
	if len(symbol) == 0 {
		return
	}
	q, err := stock(symbol)
	if err != nil {
		e.Bot.Privmsg(e.Target, fmt.Sprintf("error: %s", err))
		return
	}
	e.Bot.Privmsg(e.Target, q.String())
}

// Register registers the plugin with a bot.
func Register(b bot.Bot) {
	b.Commands().Add("stock", bot.Command{
		Help:    "get trading stock information like price",
		Handler: handleStock,
		Pub:     true,
		Priv:    true,
		Hidden:  false})
}
<commit_msg>plugins: stock: add market cap, volume, P/E and URL<commit_after>// Package stock implements a plugin to get stock quotes.
package stock

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strings"

	"github.com/StalkR/goircbot/bot"
	"github.com/StalkR/goircbot/lib/transport"
)

const quoteURL = "https://api.iextrading.com/1.0/stock/%s/quote"

type quote struct {
	Symbol                string
	CompanyName           string
	PrimaryExchange       string
	Sector                string
	CalculationPrice      string
	Open                  float64
	OpenTime              int64
	Close                 float64
	CloseTime             int64
	High                  float64
	Low                   float64
	LatestPrice           float64
	LatestSource          string
	LatestTime            string
	LatestUpdate          int64
	LatestVolume          int64
	IexRealtimePrice      float64
	IexRealtimeSize       int64
	iexLastUpdated        int64
	DelayedPrice          float64
	DelayedPriceTime      int64
	ExtendedPrice         float64
	ExtendedChange        float64
	ExtendedChangePercent float64
	ExtendedPriceTime     int64
	PreviousClose         float64
	Change                float64
	ChangePercent         float64
	IexMarketPercent      float64
	IexVolume             int64
	AvgTotalVolume        int64
	IexBidPrice           float64
	IexBidSize            int64
	IexAskPrice           float64
	IexAskSize            int64
	MarketCap             int64
	PeRatio               float64
	Week52High            float64
	Week52Low             float64
	YtdChange             float64
}

func (q *quote) String() string {
	plus := ""
	if q.Change > 0 {
		plus = "+"
	}
	return fmt.Sprintf("%v (%v): %v %v (%v%v, %v%.2f%%), %v market cap, %v volume, %.2f P/E - https://iextrading.com/apps/stocks/%v",
		q.Symbol, q.CompanyName,
		q.LatestSource, q.LatestPrice, plus, q.Change, plus, q.ChangePercent*100,
		humanize(q.MarketCap), humanize(q.LatestVolume), q.PeRatio, q.Symbol)
}

func humanize(i int64) string {
	format := func(i, unit int64) string {
		f := "%.0f"
		if i < 10*unit {
			f = "%.2f"
		} else if i < 100*unit {
			f = "%.1f"
		}
		return fmt.Sprintf(f, float64(i)/float64(unit))
	}
	switch {
	case i >= 1e9: // Billion
		return fmt.Sprintf("%vB$", format(i, 1e9))
	case i >= 1e6: // Million
		return fmt.Sprintf("%vM$", format(i, 1e6))
	case i >= 1e3: // Thousand
		return fmt.Sprintf("%vK$", format(i, 1e3))
	}
	return fmt.Sprintf("%v$", i)
}

func stock(symbol string) (*quote, error) {
	uri := fmt.Sprintf(quoteURL, url.PathEscape(symbol))
	c, err := transport.Client(uri)
	if err != nil {
		return nil, err
	}
	resp, err := c.Get(uri)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("%v", resp.Status)
	}
	var v quote
	if err := json.NewDecoder(resp.Body).Decode(&v); err != nil {
		return nil, err
	}
	return &v, nil
}

func handleStock(e *bot.Event) {
	symbol := strings.TrimSpace(e.Args)
	if len(symbol) == 0 {
		return
	}
	q, err := stock(symbol)
	if err != nil {
		e.Bot.Privmsg(e.Target, fmt.Sprintf("error: %s", err))
		return
	}
	e.Bot.Privmsg(e.Target, q.String())
}

// Register registers the plugin with a bot.
func Register(b bot.Bot) {
	b.Commands().Add("stock", bot.Command{
		Help:    "get trading stock information like price",
		Handler: handleStock,
		Pub:     true,
		Priv:    true,
		Hidden:  false})
}
<|endoftext|>


<commit_before>// +build js

package ninchat

import (
	"github.com/gopherjs/gopherjs/js"
)

type duration int64

const (
	second      duration = 1000
	millisecond          = 1
)

type timeTime int64

func timeNow() timeTime {
	return timeTime(js.Global.Get("Date").New().Call("getTime").Int64())
}

func timeAdd(t timeTime, d duration) timeTime {
	return t + timeTime(d)
}

func timeSub(t1, t2 timeTime) duration {
	return duration(t1 - t2)
}

type timer struct {
	C chan struct{}

	id *js.Object
}

func newTimer(timeout duration) (t *timer) {
	t = &timer{
		C: make(chan struct{}),
	}

	if timeout >= 0 {
		t.Reset(timeout)
	}

	return
}

func (timer *timer) Active() bool {
	return timer.id != nil
}

func (timer *timer) Reset(timeout duration) {
	timer.Stop()

	timer.id = js.Global.Call("setTimeout", func() {
		timer.id = nil

		go func() {
			timer.C <- struct{}{}
		}()
	}, timeout)
}

func (timer *timer) Stop() {
	if timer.id != nil {
		js.Global.Call("clearTimeout", timer.id)
		timer.id = nil
	}
}
<commit_msg>Fix JavaScript timer to never leak resources<commit_after>// +build js

package ninchat

import (
	"github.com/gopherjs/gopherjs/js"
)

type duration int64

const (
	second      duration = 1000
	millisecond          = 1
)

type timeTime int64

func timeNow() timeTime {
	return timeTime(js.Global.Get("Date").New().Call("getTime").Int64())
}

func timeAdd(t timeTime, d duration) timeTime {
	return t + timeTime(d)
}

func timeSub(t1, t2 timeTime) duration {
	return duration(t1 - t2)
}

type timer struct {
	C chan struct{}

	id *js.Object
}

func newTimer(timeout duration) (t *timer) {
	t = &timer{
		C: make(chan struct{}, 1),
	}

	if timeout >= 0 {
		t.Reset(timeout)
	}

	return
}

func (timer *timer) Active() bool {
	return timer.id != nil
}

func (timer *timer) Reset(timeout duration) {
	timer.Stop()

	timer.id = js.Global.Call("setTimeout", func() {
		timer.id = nil
		timer.C <- struct{}{}
	}, timeout)
}

func (timer *timer) Stop() {
	if timer.id != nil {
		js.Global.Call("clearTimeout", timer.id)
		timer.id = nil
	}
}
<|endoftext|>


<commit_before>package benchdb

import (
	"bytes"
	"database/sql"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strings"
	"time"

	_ "github.com/lib/pq"

	"golang.org/x/tools/benchmark/parse"
)

// A BenchDB manges the execution of benchmark tests using go test and
// writing a parse.Set to a database.
//
// Implementations of BenchDB for different databases are allowed by way
// of the WriteSet method.
type BenchDB interface {
	// Run executes go test bench for benchmarks matching regex in the current
	// directory. By default it does not run unit tests by way of setting test.run
	// to XXX in the call to go test. It also parses the benchSet and calls WriteSet
	// to write the benchmark data to a database. It returns any error encountered.
	Run(regex string) error

	// WriteSet is responsible for opening a postgres database connection and writing
	// a parsed benchSet to a db table. It closes the connection, returns the number of
	// benchmark tests written, and any error encountered.
	WriteSet(parse.Set) (int, error)
}

// BenchPSQL represents a BenchDB that writes benchmarks to a postgres
// database.
type BenchPSQL struct {
	Driver    string // database driver name
	ConnStr   string // sql connection string
	TableName string // database table name

	dbConn *sql.DB
}

// Run runs go test benchmarks matching regex in the current directory and writes
// benchmark data to a PSQL database by calling WriteSet. It returns any error
// encountered.
func (benchdb *BenchPSQL) Run(regex string) error {
	// Exec a subprocess for go test bench and write
	// to both stdout and a byte buffer.
	cmd := exec.Command("go", "test", "-bench", regex,
		"-test.run", "XXX", "-benchmem")
	var out bytes.Buffer
	cmd.Stdout = io.MultiWriter(os.Stdout, &out)
	err := cmd.Run()
	if err != nil {
		fmt.Printf("%s", out.String())
		return fmt.Errorf("command failed: %v", err)
	}

	benchSet, err := parse.ParseSet(&out)
	if err != nil {
		return fmt.Errorf("failed to parse benchmark data: %v", err)
	}

	// Writes parse set to sql database.
	_, err = benchdb.WriteSet(benchSet)
	if err != nil {
		return fmt.Errorf("failed to write benchSet to db: %v", err)
	}
	return nil
}

// WriteSet is responsible for opening a postgres database connection and writing
// a parsed benchSet to a db table. It closes the connection, returns the number of
// benchmark tests written, and any error encountered.
//
// A new sql transaction is created and committed per Benchmark in benchSet. This way if a
// db failure occurs all data from the benchSet is not lost.
func (benchdb *BenchPSQL) WriteSet(benchSet parse.Set) (int, error) {
	sqlDB, err := sql.Open(benchdb.Driver, benchdb.ConnStr)
	if err != nil {
		return 0, fmt.Errorf("could not connect to db: %v", err)
	}
	defer sqlDB.Close()
	benchdb.dbConn = sqlDB

	cnt := 0
	for _, b := range benchSet {
		n := len(b)
		for i := 0; i < n; i++ {
			val := b[i]
			err := saveBenchmark(benchdb.dbConn, benchdb.TableName, *val)
			if err != nil {
				return 0, fmt.Errorf("failed to save benchmark: %v", err)
			}
			cnt++
		}
	}
	return cnt, nil
}

func saveBenchmark(dbConn *sql.DB, table string, b parse.Benchmark) error {
	// Create a transaction per Benchmark.
	tx, err := dbConn.Begin()
	if err != nil {
		return err
	}
	defer tx.Rollback()

	q := fmt.Sprintf(`
        INSERT INTO %s
        (datetime, name, n, ns_op, allocated_bytes_op, allocs_op)
        VALUES
        ($1, $2, $3, $4, $5, $6)
        `, table)

	// Strips of leading Benchmark string in Benchmark.Name
	name := strings.TrimPrefix(strings.TrimSpace(b.Name), "Benchmark")
	ts := time.Now().UTC()

	_, err = tx.Exec(q,
		ts, name, b.N, b.NsPerOp, b.AllocedBytesPerOp, b.AllocsPerOp)
	if err != nil {
		return err
	}
	return tx.Commit()
}
<commit_msg>stderr<commit_after>package benchdb

import (
	"bytes"
	"database/sql"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strings"
	"time"

	_ "github.com/lib/pq"

	"golang.org/x/tools/benchmark/parse"
)

// A BenchDB manges the execution of benchmark tests using go test and
// writing a parse.Set to a database.
//
// Implementations of BenchDB for different databases are allowed by way
// of the WriteSet method.
type BenchDB interface {
	// Run executes go test bench for benchmarks matching regex in the current
	// directory. By default it does not run unit tests by way of setting test.run
	// to XXX in the call to go test. It also parses the benchSet and calls WriteSet
	// to write the benchmark data to a database. It returns any error encountered.
	Run(regex string) error

	// WriteSet is responsible for opening a postgres database connection and writing
	// a parsed benchSet to a db table. It closes the connection, returns the number of
	// benchmark tests written, and any error encountered.
	WriteSet(parse.Set) (int, error)
}

// BenchPSQL represents a BenchDB that writes benchmarks to a postgres
// database.
type BenchPSQL struct {
	Driver    string // database driver name
	ConnStr   string // sql connection string
	TableName string // database table name

	dbConn *sql.DB
}

// Run runs go test benchmarks matching regex in the current directory and writes
// benchmark data to a PSQL database by calling WriteSet. It returns any error
// encountered.
func (benchdb *BenchPSQL) Run(regex string) error {
	// Exec a subprocess for go test bench and write
	// to both stdout and a byte buffer.
	cmd := exec.Command("go", "test", "-bench", regex,
		"-test.run", "XXX", "-benchmem")
	var out bytes.Buffer
	var stderr bytes.Buffer
	cmd.Stdout = io.MultiWriter(os.Stdout, &out)
	cmd.Stderr = io.Writer(&stderr)
	err := cmd.Run()
	if err != nil {
		fmt.Printf("%s", out.String())
		fmt.Printf("%s", stderr.String())
		return fmt.Errorf("command failed: %v", err)
	}

	benchSet, err := parse.ParseSet(&out)
	if err != nil {
		return fmt.Errorf("failed to parse benchmark data: %v", err)
	}

	// Writes parse set to sql database.
	_, err = benchdb.WriteSet(benchSet)
	if err != nil {
		return fmt.Errorf("failed to write benchSet to db: %v", err)
	}
	return nil
}

// WriteSet is responsible for opening a postgres database connection and writing
// a parsed benchSet to a db table. It closes the connection, returns the number of
// benchmark tests written, and any error encountered.
//
// A new sql transaction is created and committed per Benchmark in benchSet. This way if a
// db failure occurs all data from the benchSet is not lost.
func (benchdb *BenchPSQL) WriteSet(benchSet parse.Set) (int, error) {
	sqlDB, err := sql.Open(benchdb.Driver, benchdb.ConnStr)
	if err != nil {
		return 0, fmt.Errorf("could not connect to db: %v", err)
	}
	defer sqlDB.Close()
	benchdb.dbConn = sqlDB

	cnt := 0
	for _, b := range benchSet {
		n := len(b)
		for i := 0; i < n; i++ {
			val := b[i]
			err := saveBenchmark(benchdb.dbConn, benchdb.TableName, *val)
			if err != nil {
				return 0, fmt.Errorf("failed to save benchmark: %v", err)
			}
			cnt++
		}
	}
	return cnt, nil
}

func saveBenchmark(dbConn *sql.DB, table string, b parse.Benchmark) error {
	// Create a transaction per Benchmark.
	tx, err := dbConn.Begin()
	if err != nil {
		return err
	}
	defer tx.Rollback()

	q := fmt.Sprintf(`
        INSERT INTO %s
        (datetime, name, n, ns_op, allocated_bytes_op, allocs_op)
        VALUES
        ($1, $2, $3, $4, $5, $6)
        `, table)

	// Strips of leading Benchmark string in Benchmark.Name
	name := strings.TrimPrefix(strings.TrimSpace(b.Name), "Benchmark")
	ts := time.Now().UTC()

	_, err = tx.Exec(q,
		ts, name, b.N, b.NsPerOp, b.AllocedBytesPerOp, b.AllocsPerOp)
	if err != nil {
		return err
	}
	return tx.Commit()
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2017 The btcsuite developers
// Copyright (c) 2015-2016 The Decred developers
// Heavily inspired by https://github.com/btcsuite/btcd/blob/master/version.go
// Copyright (C) 2015-2017 The Lightning Network Developers

package build

import (
	"fmt"
	"strings"
)

var (
	// Commit stores the current commit of this build, which includes the
	// most recent tag, the number of commits since that tag (if non-zero),
	// the commit hash, and a dirty marker. This should be set using the
	// -ldflags during compilation.
	Commit string

	// CommitHash stores the current commit hash of this build, this should
	// be set using the -ldflags during compilation.
	CommitHash string

	// RawTags contains the raw set of build tags, separated by commas. This
	// should be set using -ldflags during compilation.
	RawTags string

	// GoVersion stores the go version that the executable was compiled
	// with. This hsould be set using -ldflags during compilation.
	GoVersion string
)

// semanticAlphabet is the set of characters that are permitted for use in an
// AppPreRelease.
const semanticAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-."

// These constants define the application version and follow the semantic
// versioning 2.0.0 spec (http://semver.org/).
const (
	// AppMajor defines the major version of this binary.
	AppMajor uint = 0

	// AppMinor defines the minor version of this binary.
	AppMinor uint = 14

	// AppPatch defines the application patch for this binary.
	AppPatch uint = 00

	// AppPreRelease MUST only contain characters from semanticAlphabet
	// per the semantic versioning spec.
	AppPreRelease = "beta.rc3"
)

func init() {
	// Assert that AppPreRelease is valid according to the semantic
	// versioning guidelines for pre-release version and build metadata
	// strings. In particular it MUST only contain characters in
	// semanticAlphabet.
	for _, r := range AppPreRelease {
		if !strings.ContainsRune(semanticAlphabet, r) {
			panic(fmt.Errorf("rune: %v is not in the semantic "+
				"alphabet", r))
		}
	}
}

// Version returns the application version as a properly formed string per the
// semantic versioning 2.0.0 spec (http://semver.org/).
func Version() string {
	// Start with the major, minor, and patch versions.
	version := fmt.Sprintf("%d.%d.%d", AppMajor, AppMinor, AppPatch)

	// Append pre-release version if there is one. The hyphen called for by
	// the semantic versioning spec is automatically appended and should not
	// be contained in the pre-release string.
	if AppPreRelease != "" {
		version = fmt.Sprintf("%s-%s", version, AppPreRelease)
	}

	return version
}

// Tags returns the list of build tags that were compiled into the executable.
func Tags() []string {
	if len(RawTags) == 0 {
		return nil
	}

	return strings.Split(RawTags, ",")
}
<commit_msg>build: bump version to v0.14.0-beta.rc4<commit_after>// Copyright (c) 2013-2017 The btcsuite developers
// Copyright (c) 2015-2016 The Decred developers
// Heavily inspired by https://github.com/btcsuite/btcd/blob/master/version.go
// Copyright (C) 2015-2017 The Lightning Network Developers

package build

import (
	"fmt"
	"strings"
)

var (
	// Commit stores the current commit of this build, which includes the
	// most recent tag, the number of commits since that tag (if non-zero),
	// the commit hash, and a dirty marker. This should be set using the
	// -ldflags during compilation.
	Commit string

	// CommitHash stores the current commit hash of this build, this should
	// be set using the -ldflags during compilation.
	CommitHash string

	// RawTags contains the raw set of build tags, separated by commas. This
	// should be set using -ldflags during compilation.
	RawTags string

	// GoVersion stores the go version that the executable was compiled
	// with. This hsould be set using -ldflags during compilation.
	GoVersion string
)

// semanticAlphabet is the set of characters that are permitted for use in an
// AppPreRelease.
const semanticAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-."

// These constants define the application version and follow the semantic
// versioning 2.0.0 spec (http://semver.org/).
const (
	// AppMajor defines the major version of this binary.
	AppMajor uint = 0

	// AppMinor defines the minor version of this binary.
	AppMinor uint = 14

	// AppPatch defines the application patch for this binary.
	AppPatch uint = 00

	// AppPreRelease MUST only contain characters from semanticAlphabet
	// per the semantic versioning spec.
	AppPreRelease = "beta.rc4"
)

func init() {
	// Assert that AppPreRelease is valid according to the semantic
	// versioning guidelines for pre-release version and build metadata
	// strings. In particular it MUST only contain characters in
	// semanticAlphabet.
	for _, r := range AppPreRelease {
		if !strings.ContainsRune(semanticAlphabet, r) {
			panic(fmt.Errorf("rune: %v is not in the semantic "+
				"alphabet", r))
		}
	}
}

// Version returns the application version as a properly formed string per the
// semantic versioning 2.0.0 spec (http://semver.org/).
func Version() string {
	// Start with the major, minor, and patch versions.
	version := fmt.Sprintf("%d.%d.%d", AppMajor, AppMinor, AppPatch)

	// Append pre-release version if there is one. The hyphen called for by
	// the semantic versioning spec is automatically appended and should not
	// be contained in the pre-release string.
	if AppPreRelease != "" {
		version = fmt.Sprintf("%s-%s", version, AppPreRelease)
	}

	return version
}

// Tags returns the list of build tags that were compiled into the executable.
func Tags() []string {
	if len(RawTags) == 0 {
		return nil
	}

	return strings.Split(RawTags, ",")
}
<|endoftext|>


<commit_before>package turn

import (
	"io"
	"testing"
)

func TestChannelData_Encode(t *testing.T) {
	d := &ChannelData{
		Data:   []byte{1, 2, 3, 4},
		Number: minChannelNumber + 1,
	}
	d.Encode()
	b := &ChannelData{}
	b.Raw = append(b.Raw, d.Raw...)
	if err := b.Decode(); err != nil {
		t.Error(err)
	}
	if !b.Equal(d) {
		t.Error("not equal")
	}
	if !IsChannelData(b.Raw) || !IsChannelData(d.Raw) {
		t.Error("unexpected IsChannelData")
	}
}

func TestChannelData_Equal(t *testing.T) {
	for _, tc := range []struct {
		name  string
		a, b  *ChannelData
		value bool
	}{
		{
			name:  "nil",
			value: true,
		},
		{
			name: "nil to non-nil",
			b:    &ChannelData{},
		},
		{
			name: "equal",
			b: &ChannelData{
				Number: minChannelNumber,
				Data:   []byte{1, 2, 3},
			},
			a: &ChannelData{
				Number: minChannelNumber,
				Data:   []byte{1, 2, 3},
			},
			value: true,
		},
		{
			name: "number",
			b: &ChannelData{
				Number: minChannelNumber,
				Data:   []byte{1, 2, 3},
			},
			a: &ChannelData{
				Number: minChannelNumber + 1,
				Data:   []byte{1, 2, 3},
			},
		},
		{
			name: "length",
			b: &ChannelData{
				Number: minChannelNumber,
				Data:   []byte{1, 2, 3},
			},
			a: &ChannelData{
				Number: minChannelNumber,
				Data:   []byte{1, 2, 3, 4},
			},
		},
		{
			name: "data",
			b: &ChannelData{
				Number: minChannelNumber,
				Data:   []byte{1, 2, 3},
			},
			a: &ChannelData{
				Number: minChannelNumber,
				Data:   []byte{1, 2, 2},
			},
		},
	} {
		t.Run(tc.name, func(t *testing.T) {
			if v := tc.a.Equal(tc.b); v != tc.value {
				t.Errorf("unexpected: %v != %v", tc.value, v)
			}
		})
	}
}

func TestChannelData_Decode(t *testing.T) {
	for _, tc := range []struct {
		name string
		buf  []byte
		err  error
	}{
		{
			name: "nil",
			err:  io.ErrUnexpectedEOF,
		},
		{
			name: "small",
			buf:  []byte{1, 2, 3, 4},
			err:  io.ErrUnexpectedEOF,
		},
		{
			name: "zeroes",
			buf:  []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
			err:  ErrBadChannelDataLength,
		},
		{
			name: "bad chan number",
			buf:  []byte{63, 255, 0, 0, 0, 4, 0, 0, 1, 2, 3, 4},
			err:  ErrInvalidChannelNumber,
		},
	} {
		t.Run(tc.name, func(t *testing.T) {
			m := &ChannelData{
				Raw: tc.buf,
			}
			if err := m.Decode(); err != tc.err {
				t.Errorf("unexpected: %v != %v", tc.err, err)
			}
		})
	}
}

func TestChannelData_Reset(t *testing.T) {
	d := &ChannelData{
		Data:   []byte{1, 2, 3, 4},
		Number: minChannelNumber + 1,
	}
	d.Encode()
	buf := make([]byte, len(d.Raw))
	copy(buf, d.Raw)
	d.Reset()
	d.Raw = buf
	if err := d.Decode(); err != nil {
		t.Fatal(err)
	}
}

func TestIsChannelData(t *testing.T) {
	for _, tc := range []struct {
		name  string
		buf   []byte
		value bool
	}{
		{
			name: "nil",
		},
		{
			name: "small",
			buf:  []byte{1, 2, 3, 4},
		},
		{
			name: "zeroes",
			buf:  []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		},
		{
			name: "bad length",
			buf:  []byte{64, 0, 0, 0, 0, 4, 0, 0, 1, 2, 3},
		},
	} {
		t.Run(tc.name, func(t *testing.T) {
			if v := IsChannelData(tc.buf); v != tc.value {
				t.Errorf("unexpected: %v != %v", tc.value, v)
			}
		})
	}
}

func BenchmarkIsChannelData(b *testing.B) {
	buf := []byte{64, 0, 0, 0, 0, 4, 0, 0, 1, 2, 3}
	b.ReportAllocs()
	b.SetBytes(int64(len(buf)))
	for i := 0; i < b.N; i++ {
		IsChannelData(buf)
	}
}
<commit_msg>chandata: add BenchmarkChannelData_Encode<commit_after>package turn

import (
	"io"
	"testing"
)

func TestChannelData_Encode(t *testing.T) {
	d := &ChannelData{
		Data:   []byte{1, 2, 3, 4},
		Number: minChannelNumber + 1,
	}
	d.Encode()
	b := &ChannelData{}
	b.Raw = append(b.Raw, d.Raw...)
	if err := b.Decode(); err != nil {
		t.Error(err)
	}
	if !b.Equal(d) {
		t.Error("not equal")
	}
	if !IsChannelData(b.Raw) || !IsChannelData(d.Raw) {
		t.Error("unexpected IsChannelData")
	}
}

func TestChannelData_Equal(t *testing.T) {
	for _, tc := range []struct {
		name  string
		a, b  *ChannelData
		value bool
	}{
		{
			name:  "nil",
			value: true,
		},
		{
			name: "nil to non-nil",
			b:    &ChannelData{},
		},
		{
			name: "equal",
			b: &ChannelData{
				Number: minChannelNumber,
				Data:   []byte{1, 2, 3},
			},
			a: &ChannelData{
				Number: minChannelNumber,
				Data:   []byte{1, 2, 3},
			},
			value: true,
		},
		{
			name: "number",
			b: &ChannelData{
				Number: minChannelNumber,
				Data:   []byte{1, 2, 3},
			},
			a: &ChannelData{
				Number: minChannelNumber + 1,
				Data:   []byte{1, 2, 3},
			},
		},
		{
			name: "length",
			b: &ChannelData{
				Number: minChannelNumber,
				Data:   []byte{1, 2, 3},
			},
			a: &ChannelData{
				Number: minChannelNumber,
				Data:   []byte{1, 2, 3, 4},
			},
		},
		{
			name: "data",
			b: &ChannelData{
				Number: minChannelNumber,
				Data:   []byte{1, 2, 3},
			},
			a: &ChannelData{
				Number: minChannelNumber,
				Data:   []byte{1, 2, 2},
			},
		},
	} {
		t.Run(tc.name, func(t *testing.T) {
			if v := tc.a.Equal(tc.b); v != tc.value {
				t.Errorf("unexpected: %v != %v", tc.value, v)
			}
		})
	}
}

func TestChannelData_Decode(t *testing.T) {
	for _, tc := range []struct {
		name string
		buf  []byte
		err  error
	}{
		{
			name: "nil",
			err:  io.ErrUnexpectedEOF,
		},
		{
			name: "small",
			buf:  []byte{1, 2, 3, 4},
			err:  io.ErrUnexpectedEOF,
		},
		{
			name: "zeroes",
			buf:  []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
			err:  ErrBadChannelDataLength,
		},
		{
			name: "bad chan number",
			buf:  []byte{63, 255, 0, 0, 0, 4, 0, 0, 1, 2, 3, 4},
			err:  ErrInvalidChannelNumber,
		},
	} {
		t.Run(tc.name, func(t *testing.T) {
			m := &ChannelData{
				Raw: tc.buf,
			}
			if err := m.Decode(); err != tc.err {
				t.Errorf("unexpected: %v != %v", tc.err, err)
			}
		})
	}
}

func TestChannelData_Reset(t *testing.T) {
	d := &ChannelData{
		Data:   []byte{1, 2, 3, 4},
		Number: minChannelNumber + 1,
	}
	d.Encode()
	buf := make([]byte, len(d.Raw))
	copy(buf, d.Raw)
	d.Reset()
	d.Raw = buf
	if err := d.Decode(); err != nil {
		t.Fatal(err)
	}
}

func TestIsChannelData(t *testing.T) {
	for _, tc := range []struct {
		name  string
		buf   []byte
		value bool
	}{
		{
			name: "nil",
		},
		{
			name: "small",
			buf:  []byte{1, 2, 3, 4},
		},
		{
			name: "zeroes",
			buf:  []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		},
		{
			name: "bad length",
			buf:  []byte{64, 0, 0, 0, 0, 4, 0, 0, 1, 2, 3},
		},
	} {
		t.Run(tc.name, func(t *testing.T) {
			if v := IsChannelData(tc.buf); v != tc.value {
				t.Errorf("unexpected: %v != %v", tc.value, v)
			}
		})
	}
}

func BenchmarkIsChannelData(b *testing.B) {
	buf := []byte{64, 0, 0, 0, 0, 4, 0, 0, 1, 2, 3}
	b.ReportAllocs()
	b.SetBytes(int64(len(buf)))
	for i := 0; i < b.N; i++ {
		IsChannelData(buf)
	}
}

func BenchmarkChannelData_Encode(b *testing.B) {
	d := &ChannelData{
		Data:   []byte{1, 2, 3, 4},
		Number: minChannelNumber + 1,
	}
	b.ReportAllocs()
	b.SetBytes(4 + channelDataHeaderSize)
	for i := 0; i < b.N; i++ {
		d.Encode()
	}
}<|endoftext|>


<commit_before>package builder

// nginx
const (
	NginxVersion           = "1.17.3"
	NginxDownloadURLPrefix = "https://nginx.org/download"
)

// pcre
const (
	PcreVersion           = "8.43"
	PcreDownloadURLPrefix = "https://ftp.pcre.org/pub/pcre"
)

// openssl
const (
	OpenSSLVersion           = "1.0.2r"
	OpenSSLDownloadURLPrefix = "https://www.openssl.org/source"
)

// libressl
const (
	LibreSSLVersion           = "2.8.3"
	LibreSSLDownloadURLPrefix = "https://ftp.openbsd.org/pub/OpenBSD/LibreSSL"
)

// zlib
const (
	ZlibVersion           = "1.2.11"
	ZlibDownloadURLPrefix = "https://zlib.net/fossils"
)

// openResty
const (
	OpenRestyVersion           = "1.15.8.1"
	OpenRestyDownloadURLPrefix = "https://openresty.org/download"
)

// tengine
const (
	TengineVersion           = "2.3.0"
	TengineDownloadURLPrefix = "https://tengine.taobao.org/download"
)

// component enumerations
const (
	ComponentNginx = iota
	ComponentOpenResty
	ComponentTengine
	ComponentPcre
	ComponentOpenSSL
	ComponentLibreSSL
	ComponentZlib
	ComponentMax
)
<commit_msg>bumped default openssl version to 1.0.2s.<commit_after>package builder

// nginx
const (
	NginxVersion           = "1.17.3"
	NginxDownloadURLPrefix = "https://nginx.org/download"
)

// pcre
const (
	PcreVersion           = "8.43"
	PcreDownloadURLPrefix = "https://ftp.pcre.org/pub/pcre"
)

// openssl
const (
	OpenSSLVersion           = "1.0.2s"
	OpenSSLDownloadURLPrefix = "https://www.openssl.org/source"
)

// libressl
const (
	LibreSSLVersion           = "2.8.3"
	LibreSSLDownloadURLPrefix = "https://ftp.openbsd.org/pub/OpenBSD/LibreSSL"
)

// zlib
const (
	ZlibVersion           = "1.2.11"
	ZlibDownloadURLPrefix = "https://zlib.net/fossils"
)

// openResty
const (
	OpenRestyVersion           = "1.15.8.1"
	OpenRestyDownloadURLPrefix = "https://openresty.org/download"
)

// tengine
const (
	TengineVersion           = "2.3.0"
	TengineDownloadURLPrefix = "https://tengine.taobao.org/download"
)

// component enumerations
const (
	ComponentNginx = iota
	ComponentOpenResty
	ComponentTengine
	ComponentPcre
	ComponentOpenSSL
	ComponentLibreSSL
	ComponentZlib
	ComponentMax
)
<|endoftext|>


<commit_before>package builtin

import (
	// "github.com/oleiade/lane"
	// "log"
	"math/rand"
	// "strconv"
	// "sync"
	"time"

	"github.com/wanliu/flow/context"

	flow "github.com/wanliu/goflow"
)

func NewFinal() interface{} {
	return new(Final)
}

type Final struct {
	// sync.RWMutex

	flow.Component

	delayMin int
	delayMax int

	In       <-chan context.Request
	DelayMin <-chan float64
	DelayMax <-chan float64
}

func (s *Final) OnIn(req context.Request) {
	newReq := context.Request{
		Id:   req.Id,
		Text: req.Text,
	}
	// ctx := req.Ctx
	// req.Ctx = nil
	ctx.Post(req.Res.Reply, req.Res.Data, newReq)
}

func (s *Final) OnDelayMin(min float64) {
	s.delayMin = int(min)
}

func (s *Final) OnDelayMax(max float64) {
	s.delayMax = int(max)
}

func (s Final) DelayRange() int {
	rand.Seed(time.Now().UnixNano())

	if s.delayMin == 0 {
		return 3 + rand.Intn(2)
	} else {
		if s.delayMax > s.delayMin {
			return s.delayMin + rand.Intn(s.delayMax-s.delayMin)
		} else {
			return s.delayMin + rand.Intn(3)
		}
	}
}
<commit_msg>fix json recursive<commit_after>package builtin

import (
	// "github.com/oleiade/lane"
	// "log"
	"math/rand"
	// "strconv"
	// "sync"
	"time"

	"github.com/wanliu/flow/context"

	flow "github.com/wanliu/goflow"
)

func NewFinal() interface{} {
	return new(Final)
}

type Final struct {
	// sync.RWMutex

	flow.Component

	delayMin int
	delayMax int

	In       <-chan context.Request
	DelayMin <-chan float64
	DelayMax <-chan float64
}

func (s *Final) OnIn(req context.Request) {
	newReq := context.Request{
		Id:   req.Id,
		Text: req.Text,
	}
	// ctx := req.Ctx
	// req.Ctx = nil
	req.ctx.Post(req.Res.Reply, req.Res.Data, newReq)
}

func (s *Final) OnDelayMin(min float64) {
	s.delayMin = int(min)
}

func (s *Final) OnDelayMax(max float64) {
	s.delayMax = int(max)
}

func (s Final) DelayRange() int {
	rand.Seed(time.Now().UnixNano())

	if s.delayMin == 0 {
		return 3 + rand.Intn(2)
	} else {
		if s.delayMax > s.delayMin {
			return s.delayMin + rand.Intn(s.delayMax-s.delayMin)
		} else {
			return s.delayMin + rand.Intn(3)
		}
	}
}
<|endoftext|>


<commit_before>package onthefly

import (
	"fmt"
	"strconv"
)

func NewTinySVG(x, y, w, h int) (*Page, *Tag) {
	page := NewPage("", `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">`)
	svg := page.root.AddNewTag("svg")
	svg.AddAttrib("xmlns", "http://www.w3.org/2000/svg")
	svg.AddAttrib("version", "1.1")
	svg.AddAttrib("baseProfile", "tiny")
	sx := strconv.Itoa(x)
	sy := strconv.Itoa(y)
	sw := strconv.Itoa(w)
	sh := strconv.Itoa(h)
	svg.AddAttrib("viewBox", sx+" "+sy+" "+sw+" "+sh)
	return page, svg
}

func (svg *Tag) AddRect(x, y, w, h int) *Tag {
	sx := strconv.Itoa(x)
	sy := strconv.Itoa(y)
	sw := strconv.Itoa(w)
	sh := strconv.Itoa(h)
	rect := svg.AddNewTag("rect")
	rect.AddAttrib("x", sx)
	rect.AddAttrib("y", sy)
	rect.AddAttrib("width", sw)
	rect.AddAttrib("height", sh)
	return rect
}

// Add a box/rectangle, given x and y position, width, height and color
func (svg *Tag) Box(x, y, w, h int, color string) *Tag {
	rect := svg.AddRect(x, y, w, h)
	rect.Fill(color)
	return rect
}

func (svg *Tag) AddCircle(x, y, radius int) *Tag {
	sx := strconv.Itoa(x)
	sy := strconv.Itoa(y)
	sradius := strconv.Itoa(radius)
	circle := svg.AddNewTag("circle")
	circle.AddAttrib("cx", sx)
	circle.AddAttrib("cy", sy)
	circle.AddAttrib("r", sradius)
	return circle
}

// Add a line from (x1, y1) to (x2, y2) with a given stroke width and color
func (svg *Tag) Line(x1, y1, x2, y2, thickness int, color string) *Tag {
	sx1 := strconv.Itoa(x1)
	sy1 := strconv.Itoa(y1)
	sx2 := strconv.Itoa(x2)
	sy2 := strconv.Itoa(y2)
	sw := strconv.Itoa(thickness)
	line := svg.AddNewTag("line")
	line.AddAttrib("x1", sx1)
	line.AddAttrib("y1", sy1)
	line.AddAttrib("x2", sx2)
	line.AddAttrib("y2", sy2)
	line.AddAttrib("stroke-width", sw)
	line.AddAttrib("stroke", color)
	return line
}

func (svg *Tag) Triangle(x1, y1, x2, y2, x3, y3 int, color string) *Tag {
	triangle := svg.AddNewTag("path")
	triangle.AddAttrib("d", fmt.Sprintf("M %d %d L %d %d L %d %d L %d %d", x1, y1, x2, y2, x3, y3, x1, y1))
	triangle.AddAttrib("fill", color)
	return triangle
}

// Add a circle, given x and y position, radius and color
func (svg *Tag) Circle(x, y, radius int, color string) *Tag {
	circle := svg.AddCircle(x, y, radius)
	circle.Fill(color)
	return circle
}

func (rect *Tag) Fill(color string) {
	rect.AddAttrib("fill", color)
}

// Converts r, g and b which are integers in the range from 0..255
// to a color-string on the form "#nnnnnn".
func ColorString(r, g, b int) string {
	rs := strconv.FormatInt(int64(r), 16)
	gs := strconv.FormatInt(int64(g), 16)
	bs := strconv.FormatInt(int64(b), 16)
	if len(rs) == 1 {
		rs = "0" + rs
	}
	if len(gs) == 1 {
		gs = "0" + gs
	}
	if len(bs) == 1 {
		bs = "0" + bs
	}
	return "#" + rs + gs + bs
}

// Creates a rectangle that is 1 wide with the given color
// Note that the size of the "pixel" depends on how large the viewBox is
func (svg *Tag) Pixel(x, y, r, g, b int) *Tag {
	color := ColorString(r, g, b)
	rect := svg.Box(x, y, 1, 1, color)
	return rect
}

func (svg *Tag) AlphaDot(cx, cy, r, g, b int, a float32) *Tag {
	color := fmt.Sprintf("rgba(%d, %d, %d, %f)", r, g, b, a)
	circle := svg.AddCircle(cx, cy, 1)
	circle.Fill(color)
	return circle
}

func (svg *Tag) Dot(cx, cy, r, g, b int) *Tag {
	color := ColorString(r, g, b)
	circle := svg.AddCircle(cx, cy, 1)
	circle.Fill(color)
	return circle
}

func SampleSVG1() *Page {
	page, svg := NewTinySVG(0, 0, 30, 30)
	desc := svg.AddNewTag("desc")
	desc.AddContent("Sample SVG file 1")
	rect := svg.AddRect(10, 10, 10, 10)
	rect.Fill("green")
	svg.Pixel(10, 10, 255, 0, 0)
	svg.AlphaDot(5, 5, 0, 0, 255, 0.5)
	return page
}

func SampleSVG2() *Page {
	w := 160
	h := 90
	stepx := 8
	stepy := 8
	page, svg := NewTinySVG(0, 0, w, h)
	desc := svg.AddNewTag("desc")
	desc.AddContent("Sample SVG file 2")
	increase := 0
	decrease := 0
	for y := stepy; y < h; y += stepy {
		for x := stepx; x < w; x += stepx {
			increase = int((float32(x) / float32(w)) * 255.0)
			decrease = 255 - increase
			svg.Dot(x, y, 255, decrease, increase)
		}
	}
	return page
}
<commit_msg>Added Ellipse and Poly4<commit_after>package onthefly

import (
	"fmt"
	"strconv"
)

func NewTinySVG(x, y, w, h int) (*Page, *Tag) {
	page := NewPage("", `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">`)
	svg := page.root.AddNewTag("svg")
	svg.AddAttrib("xmlns", "http://www.w3.org/2000/svg")
	svg.AddAttrib("version", "1.1")
	svg.AddAttrib("baseProfile", "tiny")
	sx := strconv.Itoa(x)
	sy := strconv.Itoa(y)
	sw := strconv.Itoa(w)
	sh := strconv.Itoa(h)
	svg.AddAttrib("viewBox", sx+" "+sy+" "+sw+" "+sh)
	return page, svg
}

func (svg *Tag) AddRect(x, y, w, h int) *Tag {
	sx := strconv.Itoa(x)
	sy := strconv.Itoa(y)
	sw := strconv.Itoa(w)
	sh := strconv.Itoa(h)
	rect := svg.AddNewTag("rect")
	rect.AddAttrib("x", sx)
	rect.AddAttrib("y", sy)
	rect.AddAttrib("width", sw)
	rect.AddAttrib("height", sh)
	return rect
}

// Add a box/rectangle, given x and y position, width, height and color
func (svg *Tag) Box(x, y, w, h int, color string) *Tag {
	rect := svg.AddRect(x, y, w, h)
	rect.Fill(color)
	return rect
}

func (svg *Tag) AddCircle(x, y, radius int) *Tag {
	sx := strconv.Itoa(x)
	sy := strconv.Itoa(y)
	sradius := strconv.Itoa(radius)
	circle := svg.AddNewTag("circle")
	circle.AddAttrib("cx", sx)
	circle.AddAttrib("cy", sy)
	circle.AddAttrib("r", sradius)
	return circle
}

func (svg *Tag) AddEllipse(x, y, rx, ry int) *Tag {
	sx := strconv.Itoa(x)
	sy := strconv.Itoa(y)
	srx := strconv.Itoa(rx)
	sry := strconv.Itoa(ry)
	ellipse := svg.AddNewTag("ellipse")
	ellipse.AddAttrib("cx", sx)
	ellipse.AddAttrib("cy", sy)
	ellipse.AddAttrib("rx", srx)
	ellipse.AddAttrib("ry", sry)
	return ellipse
}

// Add a line from (x1, y1) to (x2, y2) with a given stroke width and color
func (svg *Tag) Line(x1, y1, x2, y2, thickness int, color string) *Tag {
	sx1 := strconv.Itoa(x1)
	sy1 := strconv.Itoa(y1)
	sx2 := strconv.Itoa(x2)
	sy2 := strconv.Itoa(y2)
	sw := strconv.Itoa(thickness)
	line := svg.AddNewTag("line")
	line.AddAttrib("x1", sx1)
	line.AddAttrib("y1", sy1)
	line.AddAttrib("x2", sx2)
	line.AddAttrib("y2", sy2)
	line.AddAttrib("stroke-width", sw)
	line.AddAttrib("stroke", color)
	return line
}

func (svg *Tag) Triangle(x1, y1, x2, y2, x3, y3 int, color string) *Tag {
	triangle := svg.AddNewTag("path")
	triangle.AddAttrib("d", fmt.Sprintf("M %d %d L %d %d L %d %d L %d %d", x1, y1, x2, y2, x3, y3, x1, y1))
	triangle.AddAttrib("fill", color)
	return triangle
}

func (svg *Tag) Poly4(x1, y1, x2, y2, x3, y3, x4, y4 int, color string) *Tag {
	poly4 := svg.AddNewTag("path")
	poly4.AddAttrib("d", fmt.Sprintf("M %d %d L %d %d L %d %d L %d %d L %d %d", x1, y1, x2, y2, x3, y3, x4, y4, x1, y1))
	poly4.AddAttrib("fill", color)
	return poly4
}

// Add a circle, given x and y position, radius and color
func (svg *Tag) Circle(x, y, radius int, color string) *Tag {
	circle := svg.AddCircle(x, y, radius)
	circle.Fill(color)
	return circle
}

// Add an ellipse, given x and y position, x radius, y radius and color
func (svg *Tag) Ellipse(x, y, xr, yr int, color string) *Tag {
	ellipse := svg.AddEllipse(x, y, xr, yr)
	ellipse.Fill(color)
	return ellipse
}

func (rect *Tag) Fill(color string) {
	rect.AddAttrib("fill", color)
}

// Converts r, g and b which are integers in the range from 0..255
// to a color-string on the form "#nnnnnn".
func ColorString(r, g, b int) string {
	rs := strconv.FormatInt(int64(r), 16)
	gs := strconv.FormatInt(int64(g), 16)
	bs := strconv.FormatInt(int64(b), 16)
	if len(rs) == 1 {
		rs = "0" + rs
	}
	if len(gs) == 1 {
		gs = "0" + gs
	}
	if len(bs) == 1 {
		bs = "0" + bs
	}
	return "#" + rs + gs + bs
}

// Creates a rectangle that is 1 wide with the given color
// Note that the size of the "pixel" depends on how large the viewBox is
func (svg *Tag) Pixel(x, y, r, g, b int) *Tag {
	color := ColorString(r, g, b)
	rect := svg.Box(x, y, 1, 1, color)
	return rect
}

func (svg *Tag) AlphaDot(cx, cy, r, g, b int, a float32) *Tag {
	color := fmt.Sprintf("rgba(%d, %d, %d, %f)", r, g, b, a)
	circle := svg.AddCircle(cx, cy, 1)
	circle.Fill(color)
	return circle
}

func (svg *Tag) Dot(cx, cy, r, g, b int) *Tag {
	color := ColorString(r, g, b)
	circle := svg.AddCircle(cx, cy, 1)
	circle.Fill(color)
	return circle
}

func SampleSVG1() *Page {
	page, svg := NewTinySVG(0, 0, 30, 30)
	desc := svg.AddNewTag("desc")
	desc.AddContent("Sample SVG file 1")
	rect := svg.AddRect(10, 10, 10, 10)
	rect.Fill("green")
	svg.Pixel(10, 10, 255, 0, 0)
	svg.AlphaDot(5, 5, 0, 0, 255, 0.5)
	return page
}

func SampleSVG2() *Page {
	w := 160
	h := 90
	stepx := 8
	stepy := 8
	page, svg := NewTinySVG(0, 0, w, h)
	desc := svg.AddNewTag("desc")
	desc.AddContent("Sample SVG file 2")
	increase := 0
	decrease := 0
	for y := stepy; y < h; y += stepy {
		for x := stepx; x < w; x += stepx {
			increase = int((float32(x) / float32(w)) * 255.0)
			decrease = 255 - increase
			svg.Dot(x, y, 255, decrease, increase)
		}
	}
	return page
}
<|endoftext|>


<commit_before>package libfuse

import (
	"os"
	"sync"
	"time"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"github.com/keybase/kbfs/libkbfs"
	"golang.org/x/net/context"
)

// FolderList is a node that can list all of the logged-in user's
// favorite top-level folders, on either a public or private basis.
type FolderList struct {
	fs *FS
	// only accept public folders
	public bool

	mu      sync.Mutex
	folders map[string]*Dir
}

var _ fs.Node = (*FolderList)(nil)

// Attr implements the fs.Node interface.
func (*FolderList) Attr(ctx context.Context, a *fuse.Attr) error {
	a.Mode = os.ModeDir | 0755
	return nil
}

var _ fs.NodeRequestLookuper = (*FolderList)(nil)

// Lookup implements the fs.NodeRequestLookuper interface.
func (fl *FolderList) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (node fs.Node, err error) {
	ctx = NewContextWithOpID(ctx, fl.fs.log)
	fl.fs.log.CDebugf(ctx, "FL Lookup %s", req.Name)
	defer func() { fl.fs.reportErr(ctx, err) }()
	fl.mu.Lock()
	defer fl.mu.Unlock()

	if req.Name == libkbfs.ErrorFile {
		return NewErrorFile(fl.fs, resp), nil
	}

	if req.Name == MetricsFileName {
		return NewMetricsFile(fl.fs, resp), nil
	}

	if child, ok := fl.folders[req.Name]; ok {
		return child, nil
	}

	rootNode, _, err :=
		fl.fs.config.KBFSOps().GetOrCreateRootNode(
			ctx, req.Name, fl.public, libkbfs.MasterBranch)
	switch err := err.(type) {
	case nil:
		// No error.
		break

	case libkbfs.TlfNameNotCanonical:
		// Non-canonical name.
		n := &Alias{
			canon: err.NameToTry,
		}
		return n, nil

	case libkbfs.NoSuchNameError:
		// Invalid public TLF.
		return nil, fuse.ENOENT

	case libkbfs.WriteAccessError:
		// No permissions to create TLF.
		fl.fs.log.CDebugf(ctx, "Local user doesn't have permission to create "+
			" %s and it doesn't exist yet, so making an empty folder", req.Name)
		// Only cache this empty folder for a minute, in case a valid
		// writer comes along and creates it.
		resp.EntryValid = 60 * time.Second
		return &EmptyFolder{fl.fs}, nil

	default:
		// Some other error.
		return nil, err
	}

	folderBranch := rootNode.GetFolderBranch()
	folder := &Folder{
		fs:           fl.fs,
		list:         fl,
		name:         req.Name,
		folderBranch: folderBranch,
		nodes:        map[libkbfs.NodeID]fs.Node{},
	}

	// TODO unregister all at unmount
	if err := fl.fs.config.Notifier().RegisterForChanges([]libkbfs.FolderBranch{folderBranch}, folder); err != nil {
		return nil, err
	}

	child := newDir(folder, rootNode)
	folder.nodes[rootNode.GetID()] = child

	fl.folders[req.Name] = child
	return child, nil
}

func (fl *FolderList) forgetFolder(f *Folder) {
	fl.mu.Lock()
	defer fl.mu.Unlock()

	if err := fl.fs.config.Notifier().UnregisterFromChanges([]libkbfs.FolderBranch{f.folderBranch}, f); err != nil {
		fl.fs.log.Info("cannot unregister change notifier for folder %q: %v",
			f.name, err)
	}
	delete(fl.folders, f.name)
}

var _ fs.Handle = (*FolderList)(nil)

var _ fs.HandleReadDirAller = (*FolderList)(nil)

func (fl *FolderList) getDirent(ctx context.Context, work <-chan *libkbfs.Favorite, results chan<- fuse.Dirent) error {
	for {
		select {
		case fav, ok := <-work:
			if !ok {
				return nil
			}
			results <- fuse.Dirent{
				Type: fuse.DT_Dir,
				Name: fav.Name,
			}
		case <-ctx.Done():
			return ctx.Err()
		}
	}
}

// ReadDirAll implements the ReadDirAll interface.
func (fl *FolderList) ReadDirAll(ctx context.Context) (res []fuse.Dirent, err error) {
	ctx = NewContextWithOpID(ctx, fl.fs.log)
	fl.fs.log.CDebugf(ctx, "FL ReadDirAll")
	defer func() { fl.fs.reportErr(ctx, err) }()
	favs, err := fl.fs.config.KBFSOps().GetFavorites(ctx)
	if err != nil {
		return nil, err
	}
	work := make(chan *libkbfs.Favorite)
	results := make(chan fuse.Dirent)
	errCh := make(chan error, 1)
	const maxWorkers = 10
	var wg sync.WaitGroup
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()
	for i := 0; i < maxWorkers && i < len(favs); i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			if err := fl.getDirent(ctx, work, results); err != nil {
				select {
				case errCh <- err:
				default:
				}
			}
		}()
	}

	go func() {
		// feed work
		for _, fav := range favs {
			if fl.public != fav.Public {
				continue
			}
			work <- fav
		}
		close(work)
		wg.Wait()
		// workers are done
		close(results)
	}()

outer:
	for {
		select {
		case dirent, ok := <-results:
			if !ok {
				break outer
			}
			res = append(res, dirent)
		case err := <-errCh:
			return nil, err
		}
	}
	return res, nil
}

var _ fs.NodeRemover = (*Dir)(nil)

// Remove implements the fs.NodeRemover interface for FolderList.
func (fl *FolderList) Remove(ctx context.Context, req *fuse.RemoveRequest) (err error) {
	return fuse.EPERM
}
<commit_msg>folderlist: shortcut to ENOENT for OSX finder prefix<commit_after>package libfuse

import (
	"os"
	"strings"
	"sync"
	"time"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"github.com/keybase/kbfs/libkbfs"
	"golang.org/x/net/context"
)

// FolderList is a node that can list all of the logged-in user's
// favorite top-level folders, on either a public or private basis.
type FolderList struct {
	fs *FS
	// only accept public folders
	public bool

	mu      sync.Mutex
	folders map[string]*Dir
}

var _ fs.Node = (*FolderList)(nil)

// Attr implements the fs.Node interface.
func (*FolderList) Attr(ctx context.Context, a *fuse.Attr) error {
	a.Mode = os.ModeDir | 0755
	return nil
}

var _ fs.NodeRequestLookuper = (*FolderList)(nil)

// Lookup implements the fs.NodeRequestLookuper interface.
func (fl *FolderList) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (node fs.Node, err error) {
	ctx = NewContextWithOpID(ctx, fl.fs.log)
	fl.fs.log.CDebugf(ctx, "FL Lookup %s", req.Name)
	defer func() { fl.fs.reportErr(ctx, err) }()
	fl.mu.Lock()
	defer fl.mu.Unlock()

	if req.Name == libkbfs.ErrorFile {
		return NewErrorFile(fl.fs, resp), nil
	}

	if req.Name == MetricsFileName {
		return NewMetricsFile(fl.fs, resp), nil
	}

	if child, ok := fl.folders[req.Name]; ok {
		return child, nil
	}

	// Shortcut for dreaded extraneous OSX finder lookups
	if strings.HasPrefix(req.Name, "._") {
		return nil, fuse.ENOENT
	}

	rootNode, _, err :=
		fl.fs.config.KBFSOps().GetOrCreateRootNode(
			ctx, req.Name, fl.public, libkbfs.MasterBranch)
	switch err := err.(type) {
	case nil:
		// No error.
		break

	case libkbfs.TlfNameNotCanonical:
		// Non-canonical name.
		n := &Alias{
			canon: err.NameToTry,
		}
		return n, nil

	case libkbfs.NoSuchNameError:
		// Invalid public TLF.
		return nil, fuse.ENOENT

	case libkbfs.WriteAccessError:
		// No permissions to create TLF.
		fl.fs.log.CDebugf(ctx, "Local user doesn't have permission to create "+
			" %s and it doesn't exist yet, so making an empty folder", req.Name)
		// Only cache this empty folder for a minute, in case a valid
		// writer comes along and creates it.
		resp.EntryValid = 60 * time.Second
		return &EmptyFolder{fl.fs}, nil

	default:
		// Some other error.
		return nil, err
	}

	folderBranch := rootNode.GetFolderBranch()
	folder := &Folder{
		fs:           fl.fs,
		list:         fl,
		name:         req.Name,
		folderBranch: folderBranch,
		nodes:        map[libkbfs.NodeID]fs.Node{},
	}

	// TODO unregister all at unmount
	if err := fl.fs.config.Notifier().RegisterForChanges([]libkbfs.FolderBranch{folderBranch}, folder); err != nil {
		return nil, err
	}

	child := newDir(folder, rootNode)
	folder.nodes[rootNode.GetID()] = child

	fl.folders[req.Name] = child
	return child, nil
}

func (fl *FolderList) forgetFolder(f *Folder) {
	fl.mu.Lock()
	defer fl.mu.Unlock()

	if err := fl.fs.config.Notifier().UnregisterFromChanges([]libkbfs.FolderBranch{f.folderBranch}, f); err != nil {
		fl.fs.log.Info("cannot unregister change notifier for folder %q: %v",
			f.name, err)
	}
	delete(fl.folders, f.name)
}

var _ fs.Handle = (*FolderList)(nil)

var _ fs.HandleReadDirAller = (*FolderList)(nil)

func (fl *FolderList) getDirent(ctx context.Context, work <-chan *libkbfs.Favorite, results chan<- fuse.Dirent) error {
	for {
		select {
		case fav, ok := <-work:
			if !ok {
				return nil
			}
			results <- fuse.Dirent{
				Type: fuse.DT_Dir,
				Name: fav.Name,
			}
		case <-ctx.Done():
			return ctx.Err()
		}
	}
}

// ReadDirAll implements the ReadDirAll interface.
func (fl *FolderList) ReadDirAll(ctx context.Context) (res []fuse.Dirent, err error) {
	ctx = NewContextWithOpID(ctx, fl.fs.log)
	fl.fs.log.CDebugf(ctx, "FL ReadDirAll")
	defer func() { fl.fs.reportErr(ctx, err) }()
	favs, err := fl.fs.config.KBFSOps().GetFavorites(ctx)
	if err != nil {
		return nil, err
	}
	work := make(chan *libkbfs.Favorite)
	results := make(chan fuse.Dirent)
	errCh := make(chan error, 1)
	const maxWorkers = 10
	var wg sync.WaitGroup
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()
	for i := 0; i < maxWorkers && i < len(favs); i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			if err := fl.getDirent(ctx, work, results); err != nil {
				select {
				case errCh <- err:
				default:
				}
			}
		}()
	}

	go func() {
		// feed work
		for _, fav := range favs {
			if fl.public != fav.Public {
				continue
			}
			work <- fav
		}
		close(work)
		wg.Wait()
		// workers are done
		close(results)
	}()

outer:
	for {
		select {
		case dirent, ok := <-results:
			if !ok {
				break outer
			}
			res = append(res, dirent)
		case err := <-errCh:
			return nil, err
		}
	}
	return res, nil
}

var _ fs.NodeRemover = (*Dir)(nil)

// Remove implements the fs.NodeRemover interface for FolderList.
func (fl *FolderList) Remove(ctx context.Context, req *fuse.RemoveRequest) (err error) {
	return fuse.EPERM
}
<|endoftext|>