<commit_before>package quest

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/go-libs/progress"
	"github.com/go-libs/syncreader"
)

var defaultTimeout = 30 * time.Second

type HandlerFunc func(*http.Request, *http.Response, *bytes.Buffer, error)
type BytesHandlerFunc func(*http.Request, *http.Response, []byte, error)
type StringHandlerFunc func(*http.Request, *http.Response, string, error)

// A Request manages communication with http service.
type Requester struct {
	// HTTP method
	Method string

	// Base URL for Requests.
	Endpoint string
	Url      *url.URL

	// HTTP client
	client *http.Client

	// Other client, unix
	clientConn *httputil.ClientConn

	// HTTP request
	req *http.Request

	Header  http.Header
	Body    io.ReadCloser
	Length  int64
	rawBody interface{}

	// HTTP response
	res *http.Response

	StatusCode int

	// response body, buffer
	isBodyClosed bool
	Buffer       *bytes.Buffer

	err error

	// Upload
	IsUpload bool
	files    map[string]interface{}

	// Download
	IsDownload  bool
	destination interface{}

	// Cookies
	cookies []*http.Cookie

	// Progress
	pg *progress.Progress

	transport *http.Transport
	tlsconfig *tls.Config
	timeout   time.Duration
}

func (r *Requester) Files(files map[string]interface{}) *Requester {
	r.files = files
	return r
}

func (r *Requester) Destination(destination interface{}) *Requester {
	r.destination = destination
	return r
}

func (r *Requester) Timeout(t time.Duration) *Requester {
	r.timeout = t
	return r
}

func (r *Requester) Set(key, value string) *Requester {
	r.Header.Set(key, value)
	return r
}

func (r *Requester) Query(data interface{}) *Requester {
	qs, err := QueryString(data)
	r.err = err
	r.Url.RawQuery = qs
	return r
}

func (r *Requester) Parameters(data interface{}) *Requester {
	if encodesParametersInURL(r.Method) {
		r.err = errors.New("Must not be GET, HEAD, DELETE methods.")
		return r
	}
	r.rawBody = data
	return r
}

func (r *Requester) packBody() {
	if r.rawBody == nil {
		return
	}
	body, length, err := packBody(r.rawBody)
	r.err = err
	if length > 0 && body != nil {
		r.Body = body
		r.Length = length
	}
}

func (r *Requester) Form(files map[string]interface{}, fields map[string]string) *Requester {
	var data interface{}
	if len(files) > 0 {
		pr, pw := io.Pipe()
		mw := multipart.NewWriter(pw)
		go func() {
			var (
				fp io.Writer
				fr io.Reader
			)
			for fieldname, file := range files {
				fh, name, err := getFile(file)
				if err == nil {
					file = fh
				} else {
					name = fieldname
				}
				fp, err = mw.CreateFormFile(fieldname, filepath.Base(name))
				if err != nil {
					log.Fatal(err)
				}
				fr, _ = file.(io.Reader)
				_, err = io.Copy(fp, ioutil.NopCloser(fr))
				if err != nil {
					log.Fatal(err)
				}
			}

			for k, v := range fields {
				mw.WriteField(k, v)
			}
			mw.Close()
			pw.Close()
		}()
		r.Header.Set("Content-Type", mw.FormDataContentType())
		data = pr
	} else {
		data = fields
	}
	r.rawBody = data
	return r
}

func (r *Requester) Encoding(t string) *Requester {
	t = strings.ToUpper(t)
	if t == "JSON" {
		t = "application/json"
	}
	if t != "" {
		r.Header.Set("Content-Type", t)
	}
	return r
}

func (r *Requester) Authenticate(username, password string) *Requester {
	r.Url.User = url.UserPassword(username, password)
	return r
}

func (r *Requester) Cookie(cookies ...*http.Cookie) *Requester {
	r.cookies = cookies
	return r
}

func (r *Requester) Progress(f progress.HandlerFunc) *Requester {
	r.pg = progress.New()
	r.pg.Progress = f
	return r
}

func (r *Requester) response() (*bytes.Buffer, error) {
	if r.err != nil {
		return r.Buffer, r.err
	}
	if r.isBodyClosed {
		return r.Buffer, nil
	}
	r.isBodyClosed = true
	return r.Do()
}

func (r *Requester) Response(handler HandlerFunc) *Requester {
	_, err := r.response()
	handler(r.req, r.res, r.Buffer, err)
	return r
}

func (r *Requester) ResponseBytes(handler BytesHandlerFunc) *Requester {
	_, err := r.response()
	handler(r.req, r.res, r.Buffer.Bytes(), err)
	return r
}

func (r *Requester) ResponseString(handler StringHandlerFunc) *Requester {
	_, err := r.response()
	handler(r.req, r.res, r.Buffer.String(), err)
	return r
}

func (r *Requester) ResponseJSON(f interface{}) *Requester {
	var (
		fn                    = reflect.ValueOf(f)
		t                     = reflect.TypeOf(f)
		argsNum               = t.NumIn()
		in                    = make([]reflect.Value, argsNum) //Panic if t is not kind of Func
		reqV, resV, dataV, eV reflect.Value
		err                   error
	)
	if argsNum != 4 {
		err = errors.New("ResponseJSON: invalid arguments.")
		return r
	} else {
		_, err = r.response()
		if err != nil {
			dataV = reflect.New(t.In(2)).Elem()
		} else {
			dataT := t.In(2)
			dataK := dataT.Kind()
			if dataK == reflect.Ptr {
				dataT = dataT.Elem()
			}
			dataN := reflect.New(dataT)
			data := dataN.Interface()
			err = json.Unmarshal(r.Buffer.Bytes(), &data)
			dataV = reflect.ValueOf(data)
			if dataK != reflect.Ptr {
				dataV = reflect.Indirect(dataV)
			}
		}
	}
	if err == nil {
		eV = reflect.New(t.In(3)).Elem()
	} else {
		eV = reflect.ValueOf(err)
	}
	reqV = reflect.ValueOf(r.req)
	resV = reflect.ValueOf(r.res)
	in[0] = reqV
	in[1] = resV
	in[2] = dataV
	in[3] = eV
	fn.Call(in)
	return r
}

func (r *Requester) Bytes() ([]byte, error) {
	_, err := r.response()
	return r.Buffer.Bytes(), err
}

func (r *Requester) String() (string, error) {
	_, err := r.response()
	return r.Buffer.String(), err
}

func (r *Requester) JSON(v interface{}) (err error) {
	_, err = r.response()
	if err != nil {
		return
	}
	err = json.Unmarshal(r.Buffer.Bytes(), v)
	return
}

// Status Code
func (r *Requester) ValidateStatusCode(statusCodes ...int) (b bool) {
	r.response()
	if len(statusCodes) > 0 {
		for _, c := range statusCodes {
			if b = r.StatusCode == c; b {
				break
			}
		}
		// 200 <= x < 300
	} else if r.StatusCode >= 200 && r.StatusCode < 300 {
		b = true
	}

	if !b {
		r.err = errors.New("http: invalid status code " + strconv.Itoa(r.StatusCode))
	}
	return
}

func (r *Requester) Do() (*bytes.Buffer, error) {
	// lazy create request
	r.req = &http.Request{
		Method: r.Method,
		URL:    r.Url,
		Header: r.Header,
	}

	// uploading before
	if r.IsUpload {
		fields, _ := r.rawBody.(map[string]string)
		r.Form(r.files, fields)
	}

	if len(r.cookies) > 0 {
		for _, c := range r.cookies {
			r.req.AddCookie(c)
		}
	}

	// pack body
	r.packBody()

	// uploading after
	if r.IsUpload && r.pg != nil {
		r.pg.Total = r.Length
		r.Body = ioutil.NopCloser(syncreader.New(r.Body, r.pg))
	}

	if r.Body != nil {
		if r.Header.Get("Content-Type") == "" {
			r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
		}
		r.req.Body = r.Body
		if r.Length > 0 {
			r.req.ContentLength = r.Length
		}
	}

	var (
		res *http.Response
		err error
	)

	switch r.Url.Scheme {
	case "http", "https":
		r.transport = new(http.Transport)
		if r.tlsconfig != nil {
			r.transport.TLSClientConfig = r.tlsconfig
		}
		r.client = &http.Client{Transport: r.transport, Timeout: r.timeout}
		res, err = r.client.Do(r.req)
		if err != nil {
			return nil, err
		}
		break
	default:
		// TODO @fundon, customzie
		conn, err := net.Dial(r.Url.Scheme, r.Url.Host)
		if err != nil {
			return nil, err
		}
		r.clientConn = httputil.NewClientConn(conn, nil)
		res, err = r.clientConn.Do(r.req)
		if err != nil {
			return nil, err
		}
	}

	defer res.Body.Close()

	r.res = res
	r.StatusCode = res.StatusCode
	r.Buffer = new(bytes.Buffer)
	dw := io.MultiWriter(r.Buffer)

	// downloading
	if r.IsDownload {
		var fw io.Writer
		switch t := r.destination.(type) {
		case string:
			p, err := filepath.Abs(t)
			if err != nil {
				return nil, err
			}
			f, err := os.Create(p)
			defer f.Close()
			if err != nil {
				return nil, err
			}
			fw = f
			break
		default:
			fw, _ = t.(io.Writer)
		}
		if r.pg != nil {
			r.pg.Total = res.ContentLength
		}
		dw = io.MultiWriter(dw, r.pg, fw)
		if err != nil {
			return nil, err
		}
	}

	_, err = io.Copy(dw, res.Body)
	if err != nil {
		return nil, err
	}
	return r.Buffer, nil
}

func (r *Requester) TLSConfig(t *tls.Config) *Requester {
	r.tlsconfig = t
	return r
}

func (r *Requester) Cancel() {
	if r.client != nil {
		r.client.Transport.(*http.Transport).CancelRequest(r.req)
	} else if r.clientConn != nil {
		r.clientConn.Close()
	}
}

func (r *Requester) Println() string {
	s := []string{r.Method, r.Url.String()}

	if r.res != nil {
		s = append(s, strconv.Itoa(r.StatusCode))
	}

	return strings.Join(s, " ")
}

func (r *Requester) DebugPrintln() string {
	s := []string{"$ curl -i"}

	if r.Method != GET {
		s = append(s, "-X "+r.Method)
	}

	if r.Url.User != nil {
		s = append(s, "-u "+r.Url.User.String())
	}

	if r.tlsconfig != nil {
		if r.tlsconfig.InsecureSkipVerify {
			s = append(s, "-k")
		} else if r.tlsconfig.RootCAs != nil {
			s = append(s, "--cacert ca.pem")
		}
		s = append(s, "--cert cert.pem")
		s = append(s, "--key key.pem")
	}

	if len(r.cookies) > 0 {
		var cs []string
		for _, c := range r.cookies {
			cs = append(cs, c.String())
		}
		s = append(s, "-b "+strconv.Quote(strings.Join(cs, "; ")))
	}

	for field, _ := range r.Header {
		s = append(s, "-H "+strconv.Quote(field+": "+r.Header.Get(field)))
	}

	if body, _, err := packBody(r.rawBody); err == nil && body != nil {
		b := new(bytes.Buffer)
		io.Copy(b, body)
		s = append(s, "-d "+strconv.Quote(b.String()))
	}

	s = append(s, strconv.Quote(r.Url.String()))

	return strings.Join(s, " \\\n\t")
}

func (r *Requester) Pipe() {
}

func encodesParametersInURL(method string) bool {
	switch method {
	case GET, HEAD, DELETE:
		return true
	}
	return false
}
<commit_msg>override Header<commit_after>package quest

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/go-libs/progress"
	"github.com/go-libs/syncreader"
)

var defaultTimeout = 30 * time.Second

type HandlerFunc func(*http.Request, *http.Response, *bytes.Buffer, error)
type BytesHandlerFunc func(*http.Request, *http.Response, []byte, error)
type StringHandlerFunc func(*http.Request, *http.Response, string, error)

// A Request manages communication with http service.
type Requester struct {
	// HTTP method
	Method string

	// Base URL for Requests.
	Endpoint string
	Url      *url.URL

	// HTTP client
	client *http.Client

	// Other client, unix
	clientConn *httputil.ClientConn

	// HTTP request
	req *http.Request

	Header  http.Header
	Body    io.ReadCloser
	Length  int64
	rawBody interface{}

	// HTTP response
	res *http.Response

	StatusCode int

	// response body, buffer
	isBodyClosed bool
	Buffer       *bytes.Buffer

	err error

	// Upload
	IsUpload bool
	files    map[string]interface{}

	// Download
	IsDownload  bool
	destination interface{}

	// Cookies
	cookies []*http.Cookie

	// Progress
	pg *progress.Progress

	transport *http.Transport
	tlsconfig *tls.Config
	timeout   time.Duration
}

func (r *Requester) Files(files map[string]interface{}) *Requester {
	r.files = files
	return r
}

func (r *Requester) Destination(destination interface{}) *Requester {
	r.destination = destination
	return r
}

func (r *Requester) Timeout(t time.Duration) *Requester {
	r.timeout = t
	return r
}

func (r *Requester) Set(key, value string) *Requester {
	r.Header.Set(key, value)
	return r
}

func (r *Requester) Query(data interface{}) *Requester {
	qs, err := QueryString(data)
	r.err = err
	r.Url.RawQuery = qs
	return r
}

func (r *Requester) Parameters(data interface{}) *Requester {
	if encodesParametersInURL(r.Method) {
		r.err = errors.New("Must not be GET, HEAD, DELETE methods.")
		return r
	}
	r.rawBody = data
	return r
}

func (r *Requester) packBody() {
	if r.rawBody == nil {
		return
	}
	body, length, err := packBody(r.rawBody)
	r.err = err
	if length > 0 && body != nil {
		r.Body = body
		r.Length = length
	}
}

func (r *Requester) Form(files map[string]interface{}, fields map[string]string) *Requester {
	var data interface{}
	if len(files) > 0 {
		pr, pw := io.Pipe()
		mw := multipart.NewWriter(pw)
		go func() {
			var (
				fp io.Writer
				fr io.Reader
			)
			for fieldname, file := range files {
				fh, name, err := getFile(file)
				if err == nil {
					file = fh
				} else {
					name = fieldname
				}
				fp, err = mw.CreateFormFile(fieldname, filepath.Base(name))
				if err != nil {
					log.Fatal(err)
				}
				fr, _ = file.(io.Reader)
				_, err = io.Copy(fp, ioutil.NopCloser(fr))
				if err != nil {
					log.Fatal(err)
				}
			}

			for k, v := range fields {
				mw.WriteField(k, v)
			}
			mw.Close()
			pw.Close()
		}()
		r.Header.Set("Content-Type", mw.FormDataContentType())
		data = pr
	} else {
		data = fields
	}
	r.rawBody = data
	return r
}

func (r *Requester) Encoding(t string) *Requester {
	t = strings.ToUpper(t)
	if t == "JSON" {
		t = "application/json"
	}
	if t != "" {
		r.Header.Set("Content-Type", t)
	}
	return r
}

func (r *Requester) Authenticate(username, password string) *Requester {
	r.Url.User = url.UserPassword(username, password)
	return r
}

func (r *Requester) Cookie(cookies ...*http.Cookie) *Requester {
	r.cookies = cookies
	return r
}

func (r *Requester) Progress(f progress.HandlerFunc) *Requester {
	r.pg = progress.New()
	r.pg.Progress = f
	return r
}

func (r *Requester) response() (*bytes.Buffer, error) {
	if r.err != nil {
		return r.Buffer, r.err
	}
	if r.isBodyClosed {
		return r.Buffer, nil
	}
	r.isBodyClosed = true
	return r.Do()
}

func (r *Requester) Response(handler HandlerFunc) *Requester {
	_, err := r.response()
	handler(r.req, r.res, r.Buffer, err)
	return r
}

func (r *Requester) ResponseBytes(handler BytesHandlerFunc) *Requester {
	_, err := r.response()
	handler(r.req, r.res, r.Buffer.Bytes(), err)
	return r
}

func (r *Requester) ResponseString(handler StringHandlerFunc) *Requester {
	_, err := r.response()
	handler(r.req, r.res, r.Buffer.String(), err)
	return r
}

func (r *Requester) ResponseJSON(f interface{}) *Requester {
	var (
		fn                    = reflect.ValueOf(f)
		t                     = reflect.TypeOf(f)
		argsNum               = t.NumIn()
		in                    = make([]reflect.Value, argsNum) //Panic if t is not kind of Func
		reqV, resV, dataV, eV reflect.Value
		err                   error
	)
	if argsNum != 4 {
		err = errors.New("ResponseJSON: invalid arguments.")
		return r
	} else {
		_, err = r.response()
		if err != nil {
			dataV = reflect.New(t.In(2)).Elem()
		} else {
			dataT := t.In(2)
			dataK := dataT.Kind()
			if dataK == reflect.Ptr {
				dataT = dataT.Elem()
			}
			dataN := reflect.New(dataT)
			data := dataN.Interface()
			err = json.Unmarshal(r.Buffer.Bytes(), &data)
			dataV = reflect.ValueOf(data)
			if dataK != reflect.Ptr {
				dataV = reflect.Indirect(dataV)
			}
		}
	}
	if err == nil {
		eV = reflect.New(t.In(3)).Elem()
	} else {
		eV = reflect.ValueOf(err)
	}
	reqV = reflect.ValueOf(r.req)
	resV = reflect.ValueOf(r.res)
	in[0] = reqV
	in[1] = resV
	in[2] = dataV
	in[3] = eV
	fn.Call(in)
	return r
}

func (r *Requester) Bytes() ([]byte, error) {
	_, err := r.response()
	return r.Buffer.Bytes(), err
}

func (r *Requester) String() (string, error) {
	_, err := r.response()
	return r.Buffer.String(), err
}

func (r *Requester) JSON(v interface{}) (err error) {
	_, err = r.response()
	if err != nil {
		return
	}
	err = json.Unmarshal(r.Buffer.Bytes(), v)
	return
}

// Status Code
func (r *Requester) ValidateStatusCode(statusCodes ...int) (b bool) {
	r.response()
	if len(statusCodes) > 0 {
		for _, c := range statusCodes {
			if b = r.StatusCode == c; b {
				break
			}
		}
		// 200 <= x < 300
	} else if r.StatusCode >= 200 && r.StatusCode < 300 {
		b = true
	}

	if !b {
		r.err = errors.New("http: invalid status code " + strconv.Itoa(r.StatusCode))
	}
	return
}

func (r *Requester) Do() (*bytes.Buffer, error) {
	// lazy create request
	r.req = &http.Request{
		Method: r.Method,
		URL:    r.Url,
		Header: r.Header,
	}

	// uploading before
	if r.IsUpload {
		fields, _ := r.rawBody.(map[string]string)
		r.Form(r.files, fields)
	}

	if len(r.cookies) > 0 {
		for _, c := range r.cookies {
			r.req.AddCookie(c)
		}
	}

	// pack body
	r.packBody()

	// uploading after
	if r.IsUpload && r.pg != nil {
		r.pg.Total = r.Length
		r.Body = ioutil.NopCloser(syncreader.New(r.Body, r.pg))
	}

	if r.Body != nil {
		if r.Header.Get("Content-Type") == "" {
			r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
		}
		r.req.Body = r.Body
		if r.Length > 0 {
			r.req.ContentLength = r.Length
		}
	}

	var (
		res *http.Response
		err error
	)

	switch r.Url.Scheme {
	case "http", "https":
		r.transport = new(http.Transport)
		if r.tlsconfig != nil {
			r.transport.TLSClientConfig = r.tlsconfig
		}
		r.client = &http.Client{Transport: r.transport, Timeout: r.timeout}
		res, err = r.client.Do(r.req)
		if err != nil {
			return nil, err
		}
		break
	default:
		// TODO @fundon, customzie
		conn, err := net.Dial(r.Url.Scheme, r.Url.Host)
		if err != nil {
			return nil, err
		}
		r.clientConn = httputil.NewClientConn(conn, nil)
		res, err = r.clientConn.Do(r.req)
		if err != nil {
			return nil, err
		}
	}

	defer res.Body.Close()

	r.res = res
	r.StatusCode = res.StatusCode
	// hack, return response header
	r.Header = res.Header
	r.Buffer = new(bytes.Buffer)
	dw := io.MultiWriter(r.Buffer)

	// downloading
	if r.IsDownload {
		var fw io.Writer
		switch t := r.destination.(type) {
		case string:
			p, err := filepath.Abs(t)
			if err != nil {
				return nil, err
			}
			f, err := os.Create(p)
			defer f.Close()
			if err != nil {
				return nil, err
			}
			fw = f
			break
		default:
			fw, _ = t.(io.Writer)
		}
		if r.pg != nil {
			r.pg.Total = res.ContentLength
		}
		dw = io.MultiWriter(dw, r.pg, fw)
		if err != nil {
			return nil, err
		}
	}

	_, err = io.Copy(dw, res.Body)
	if err != nil {
		return nil, err
	}
	return r.Buffer, nil
}

func (r *Requester) TLSConfig(t *tls.Config) *Requester {
	r.tlsconfig = t
	return r
}

func (r *Requester) Cancel() {
	if r.client != nil {
		r.client.Transport.(*http.Transport).CancelRequest(r.req)
	} else if r.clientConn != nil {
		r.clientConn.Close()
	}
}

func (r *Requester) Println() string {
	s := []string{r.Method, r.Url.String()}

	if r.res != nil {
		s = append(s, strconv.Itoa(r.StatusCode))
	}

	return strings.Join(s, " ")
}

func (r *Requester) DebugPrintln() string {
	s := []string{"$ curl -i"}

	if r.Method != GET {
		s = append(s, "-X "+r.Method)
	}

	if r.Url.User != nil {
		s = append(s, "-u "+r.Url.User.String())
	}

	if r.tlsconfig != nil {
		if r.tlsconfig.InsecureSkipVerify {
			s = append(s, "-k")
		} else if r.tlsconfig.RootCAs != nil {
			s = append(s, "--cacert ca.pem")
		}
		s = append(s, "--cert cert.pem")
		s = append(s, "--key key.pem")
	}

	if len(r.cookies) > 0 {
		var cs []string
		for _, c := range r.cookies {
			cs = append(cs, c.String())
		}
		s = append(s, "-b "+strconv.Quote(strings.Join(cs, "; ")))
	}

	for field, _ := range r.Header {
		s = append(s, "-H "+strconv.Quote(field+": "+r.Header.Get(field)))
	}

	if body, _, err := packBody(r.rawBody); err == nil && body != nil {
		b := new(bytes.Buffer)
		io.Copy(b, body)
		s = append(s, "-d "+strconv.Quote(b.String()))
	}

	s = append(s, strconv.Quote(r.Url.String()))

	return strings.Join(s, " \\\n\t")
}

func (r *Requester) Pipe() {
}

func encodesParametersInURL(method string) bool {
	switch method {
	case GET, HEAD, DELETE:
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package balanced

import (
	"fmt"
	"net/http"
	"time"
)

type ReversalService struct {
	client *Client
}

type Reversal struct {
	Amount      int               `json:"amount,omitempty"`
	Description string            `json:"description,omitempty"`
	Meta        map[string]string `json:"meta,omitempty"`
	Links       *ReversalLinks    `json:"links,omitempty"`
	Id          string            `json:"id,omitempty"`
	Href        string            `json:"href,omitempty"`
	CreatedAt   *time.Time        `json:"created_at,omitempty"`
	UpdatedAt   *time.Time        `json:"updated_at,omitempty"`
}

type ReversalLinks struct {
	Credit string `json:"credit"`
	Order  string `json:"order,omitempty"`
}

type ReversalResponse struct {
	Reversals []Reversal             `json:"reversals"`
	Links     *ReversalResponseLinks `json:"links"`
	Meta      map[string]interface{} `json:"meta,omitempty"`
}

type ReversalResponseLinks struct {
	Credit string `json:"reversals.credit"`
	Events string `json:"reversals.events"`
	Order  string `json:"reversal.order"`
}

type ReversalPage struct {
	Reversals []Reversal
	*PaginationParams
}

func (s *ReversalService) Create(creditId string, reversal *Reversal) (*Reversal, *http.Response, error) {
	path := fmt.Sprintf("/credits/%v/reversals", creditId)
	reversalResponse := new(ReversalResponse)
	httpResponse, err := s.client.POST(path, nil, reversal, reversalResponse)
	if err != nil {
		return nil, httpResponse, err
	}
	return &reversalResponse.Reversals[0], httpResponse, nil
}

func (s *ReversalService) Fetch(reversalId string) (*Reversal, *http.Response, error) {
	path := fmt.Sprintf("/reversals/%v", reversalId)
	reversalResponse := new(ReversalResponse)
	httpResponse, err := s.client.GET(path, nil, nil, reversalResponse)
	if err != nil {
		return nil, httpResponse, err
	}
	return &reversalResponse.Reversals[0], httpResponse, nil
}

func (s *ReversalService) List(args ...interface{}) (*ReversalPage, *http.Response, error) {
	// Turns args into a map[string]int with "offset" and "limit" keys
	query := paginatedArgsToQuery(args)
	reversalResponse := new(ReversalResponse)
	httpResponse, err := s.client.GET("/reversals", query, nil, reversalResponse)
	if err != nil {
		return nil, httpResponse, err
	}
	return &ReversalPage{
		Reversals:        reversalResponse.Reversals,
		PaginationParams: NewPaginationParams(reversalResponse.Meta),
	}, httpResponse, nil
}

func (s *ReversalService) Update(reversalId string, params map[string]interface{}) (*Reversal, *http.Response, error) {
	path := fmt.Sprintf("/reversals/%v", reversalId)
	reversalResponse := new(ReversalResponse)
	httpResponse, err := s.client.PUT(path, nil, params, reversalResponse)
	if err != nil {
		return nil, httpResponse, err
	}
	return &reversalResponse.Reversals[0], httpResponse, nil
}
<commit_msg>Rm unnecessary omitempty annotations<commit_after>package balanced

import (
	"fmt"
	"net/http"
	"time"
)

type ReversalService struct {
	client *Client
}

type Reversal struct {
	Amount      int               `json:"amount,omitempty"`
	Description string            `json:"description,omitempty"`
	Meta        map[string]string `json:"meta,omitempty"`
	Links       *ReversalLinks    `json:"links,omitempty"`
	Id          string            `json:"id,omitempty"`
	Href        string            `json:"href,omitempty"`
	CreatedAt   *time.Time        `json:"created_at,omitempty"`
	UpdatedAt   *time.Time        `json:"updated_at,omitempty"`
}

type ReversalLinks struct {
	Credit string `json:"credit"`
	Order  string `json:"order"`
}

type ReversalResponse struct {
	Reversals []Reversal             `json:"reversals"`
	Links     *ReversalResponseLinks `json:"links"`
	Meta      map[string]interface{} `json:"meta"`
}

type ReversalResponseLinks struct {
	Credit string `json:"reversals.credit"`
	Events string `json:"reversals.events"`
	Order  string `json:"reversal.order"`
}

type ReversalPage struct {
	Reversals []Reversal
	*PaginationParams
}

func (s *ReversalService) Create(creditId string, reversal *Reversal) (*Reversal, *http.Response, error) {
	path := fmt.Sprintf("/credits/%v/reversals", creditId)
	reversalResponse := new(ReversalResponse)
	httpResponse, err := s.client.POST(path, nil, reversal, reversalResponse)
	if err != nil {
		return nil, httpResponse, err
	}
	return &reversalResponse.Reversals[0], httpResponse, nil
}

func (s *ReversalService) Fetch(reversalId string) (*Reversal, *http.Response, error) {
	path := fmt.Sprintf("/reversals/%v", reversalId)
	reversalResponse := new(ReversalResponse)
	httpResponse, err := s.client.GET(path, nil, nil, reversalResponse)
	if err != nil {
		return nil, httpResponse, err
	}
	return &reversalResponse.Reversals[0], httpResponse, nil
}

func (s *ReversalService) List(args ...interface{}) (*ReversalPage, *http.Response, error) {
	// Turns args into a map[string]int with "offset" and "limit" keys
	query := paginatedArgsToQuery(args)
	reversalResponse := new(ReversalResponse)
	httpResponse, err := s.client.GET("/reversals", query, nil, reversalResponse)
	if err != nil {
		return nil, httpResponse, err
	}
	return &ReversalPage{
		Reversals:        reversalResponse.Reversals,
		PaginationParams: NewPaginationParams(reversalResponse.Meta),
	}, httpResponse, nil
}

func (s *ReversalService) Update(reversalId string, params map[string]interface{}) (*Reversal, *http.Response, error) {
	path := fmt.Sprintf("/reversals/%v", reversalId)
	reversalResponse := new(ReversalResponse)
	httpResponse, err := s.client.PUT(path, nil, params, reversalResponse)
	if err != nil {
		return nil, httpResponse, err
	}
	return &reversalResponse.Reversals[0], httpResponse, nil
}
<|endoftext|>


<commit_before>package riot

import (
    "log"
    "net/http"
)
import c "vs/config"

const Url = "https://na1.api.riotgames.com/lol"

var config = c.GetConfig()
var Version = config.Version

func AddHeaders(req *http.Request) {
    req.Header.Add("X-Riot-Token", config.Api.Token)
}

// TODO: Maybe move these over to another thing
// Not sure if this is the best practice
var Client = &http.Client{
//    CheckRedirect: redirectPolicyFunc,
}

type defaultapi func() ([]byte, error)

func DefaultOutputHandler(fn defaultapi) (http.HandlerFunc) {
    var handler = func(w http.ResponseWriter, r *http.Request) {
        body, err := fn()
        if err != nil {
            log.Fatalf("DefaultOutputHandler found an err: %v", err)
        }

        w.Write(body)
    }

    return http.HandlerFunc(handler)
}
<commit_msg>Standard Get Data function<commit_after>package riot

import (
    "fmt"
    "log"
    "io/ioutil"
    "net/http"
)
import c "vs/config"

const Url = "https://na1.api.riotgames.com/lol"

var config = c.GetConfig()
var Version = config.Version

func add_headers(req *http.Request) {
    req.Header.Add("X-Riot-Token", config.Api.Token)
}

// TODO: Maybe move these over to another thing
// Not sure if this is the best practice
var Client = &http.Client{
//    CheckRedirect: redirectPolicyFunc,
}

type defaultapi func() ([]byte, error)

func DefaultOutputHandler(fn defaultapi) (http.HandlerFunc) {
    var handler = func(w http.ResponseWriter, r *http.Request) {
        body, err := fn()
        if err != nil {
            log.Fatalf("DefaultOutputHandler found an err: %v", err)
        }

        w.Write(body)
    }

    return http.HandlerFunc(handler)
}

func GetData(http_method string, uri string) ([]byte, error) {
    var err error

    url := fmt.Sprintf("%s%s", Url, uri)

    req, _ := http.NewRequest("GET", url, nil)
    add_headers(req)
    resp, _ := Client.Do(req)

    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)

    return body, err
}
<|endoftext|>


<commit_before>package risk

import "time"

const (
	// GetScoresEnpoint location of the getscores endpoint
	GetScoresEnpoint = "v1/score/getScores"
)

// MetricPoint defines the data needed for points on a single risk point
type MetricPoint struct {
	Name   string `json:"name" xml:"name"`
	Points int    `json:"points" xml:"points"`
}

// MetricPoints defines the data needed for points on a single risk point
type MetricPoints struct {
	Metrics     []MetricPoint `json:"metrics" xml:"metrics"`
	ProcessedAt time.Time     `json:"processed_at" xml:"processed_at"`
}

// Scores top level struct for modeling the score tree
type Scores struct {
	Name   string  `json:"name"`
	Value  float64 `json:"value"`
	Scopes []Scope `json:"scopes"`
}

// Scope second tier struct for modeling the score tree
type Scope struct {
	Name       string     `json:"name"`
	Value      float64    `json:"value"`
	Categories []Category `json:"-"`
}

// GetScope returns a Scope value based on the name supplied
func (s *Scores) GetScope(name string) *Scope {
	var scope *Scope
	for _, sco := range s.Scopes {
		if sco.Name == name {
			scope = &sco
			break
		}
	}

	if scope == nil {
		scope = &Scope{Name: name}
	}
	return scope
}

// Category third tier struct for modeling the score tree
type Category struct {
	Name       string      `json:"name"`
	Value      float64     `json:"value"`
	Attributes []Attribute `json:"-"`
}

// Attribute leaf tier struct for modeling the score tree
type Attribute struct {
	Name  string  `json:"name"`
	Value float64 `json:"value"`
}
<commit_msg>Adding constants for better access<commit_after>package risk

import "time"

const (
	// GetScoresEnpoint location of the getscores endpoint
	GetScoresEnpoint = "v1/score/getScores"

	// EcosystemScope constant with name of scope for ecosystem
	EcosystemScope = "ecosystem"

	// SupplyChainScope constant with name of scope for supply chain
	SupplyChainScope = "supply chain"

	// TechnologyScope constant with name of scope for technology
	TechnologyScope = "technology"
)

// MetricPoint defines the data needed for points on a single risk point
type MetricPoint struct {
	Name   string `json:"name" xml:"name"`
	Points int    `json:"points" xml:"points"`
}

// MetricPoints defines the data needed for points on a single risk point
type MetricPoints struct {
	Metrics     []MetricPoint `json:"metrics" xml:"metrics"`
	ProcessedAt time.Time     `json:"processed_at" xml:"processed_at"`
}

// Scores top level struct for modeling the score tree
type Scores struct {
	Name   string  `json:"name"`
	Value  float64 `json:"value"`
	Scopes []Scope `json:"scopes"`
}

// Scope second tier struct for modeling the score tree
type Scope struct {
	Name       string     `json:"name"`
	Value      float64    `json:"value"`
	Categories []Category `json:"-"`
}

// GetScope returns a Scope value based on the name supplied
func (s *Scores) GetScope(name string) *Scope {
	var scope *Scope
	for _, sco := range s.Scopes {
		if sco.Name == name {
			scope = &sco
			break
		}
	}

	if scope == nil {
		scope = &Scope{Name: name}
	}
	return scope
}

// Category third tier struct for modeling the score tree
type Category struct {
	Name       string      `json:"name"`
	Value      float64     `json:"value"`
	Attributes []Attribute `json:"-"`
}

// Attribute leaf tier struct for modeling the score tree
type Attribute struct {
	Name  string  `json:"name"`
	Value float64 `json:"value"`
}
<|endoftext|>


<commit_before>package ruler

import "testing"

func TestPluck(t *testing.T) {
	exps := []struct {
		o       map[string]interface{}
		seeking string
		value   interface{}
		name    string
	}{

		{
			map[string]interface{}{
				"hey": map[string]interface{}{
					"hello": "bob",
				},
			},
			"hey.hello",
			"bob",
			`test extracting a simple property`,
		},

		{
			map[string]interface{}{
				"hey": map[string]interface{}{
					"hello": "bob",
				},
			},
			"hey.nope",
			nil,
			`test getting a nonexistent property
			on an existing object`,
		},
		{
			map[string]interface{}{
				"hey": map[string]interface{}{
					"hello": "bob",
				},
			},
			"hey.what.something.very.important",
			nil,
			`test getting a nonexistent property
			for a nonexistent object`,
		},
		{
			map[string]interface{}{
				"hey": map[string]interface{}{
					"hello": "bob",
				},
			},
			"hey.hello.something.very.important",
			nil,
			`test getting a property on a thing
			that isn't or doesn't assert to be a map`,
		},

		{
			map[string]interface{}{
				"hey": map[string]interface{}{
					"sup": 1,
				},
			},
			"hey.sup",
			1,
			`test plucking something that isn't
			a string`,
		},
		{
			map[string]interface{}{},
			"hey.lol",
			nil,
			`test plucking where the base obj doesn't exist`,
		},
	}

	for _, e := range exps {
		res := pluck(e.o, e.seeking)
		if res != e.value {
			t.Errorf(
				"error with: %s\nfailed to pluck! %s != %s",
				e.name,
				res,
				e.value,
			)
		}
	}
}
<commit_msg>add deeply nested test, benchmarks<commit_after>package ruler

import "testing"

func TestPluck(t *testing.T) {
	exps := []struct {
		o       map[string]interface{}
		seeking string
		value   interface{}
		name    string
	}{

		{
			map[string]interface{}{
				"hey": map[string]interface{}{
					"hello": "bob",
				},
			},
			"hey.hello",
			"bob",
			`test extracting a simple property`,
		},

		{
			map[string]interface{}{
				"hey": map[string]interface{}{
					"hello": "bob",
				},
			},
			"hey.nope",
			nil,
			`test getting a nonexistent property
			on an existing object`,
		},
		{
			map[string]interface{}{
				"hey": map[string]interface{}{
					"hello": "bob",
				},
			},
			"hey.what.something.very.important",
			nil,
			`test getting a nonexistent property
			for a nonexistent object`,
		},
		{
			map[string]interface{}{
				"hey": map[string]interface{}{
					"hello": "bob",
				},
			},
			"hey.hello.something.very.important",
			nil,
			`test getting a property on a thing
			that isn't or doesn't assert to be a map`,
		},

		{
			map[string]interface{}{
				"hey": map[string]interface{}{
					"sup": 1,
				},
			},
			"hey.sup",
			1,
			`test plucking something that isn't
			a string`,
		},
		{
			map[string]interface{}{},
			"hey.lol",
			nil,
			`test plucking where the base obj doesn't exist`,
		},
		{
			map[string]interface{}{
				"test": map[string]interface{}{
					"thing": map[string]interface{}{
						"here": map[string]interface{}{
							"today": map[string]interface{}{
								"is": map[string]interface{}{
									"awesome": map[string]interface{}{
										"with": map[string]interface{}{
											"thestuff": "no dice",
										},
									},
								},
							},
						},
					},
				},
			},
			"test.thing.here.today.is.awesome.with.thestuff",
			"no dice",
			"testing deeply nested property",
		},
	}

	for _, e := range exps {
		res := pluck(e.o, e.seeking)
		if res != e.value {
			t.Errorf(
				"error with: %s\nfailed to pluck! %s != %s",
				e.name,
				res,
				e.value,
			)
		}
	}
}

func BenchmarkPluckShallow(b *testing.B) {
	o := map[string]interface{}{
		"hey": map[string]interface{}{
			"there": 4,
		},
	}

	for i := 0; i < b.N; i += 1 {
		r := pluck(o, "hey.there")
		if r != 4 {
			b.Errorf("fail bench, val was %s", r)
		}
	}
}

func BenchmarkPluckDeep(b *testing.B) {
	o := map[string]interface{}{
		"test": map[string]interface{}{
			"thing": map[string]interface{}{
				"here": map[string]interface{}{
					"today": map[string]interface{}{
						"is": map[string]interface{}{
							"awesome": map[string]interface{}{
								"with": map[string]interface{}{
									"thestuff": "no dice",
								},
							},
						},
					},
				},
			},
		},
	}

	for i := 0; i < b.N; i += 1 {
		r := pluck(o, "test.thing.here.today.is.awesome.with.thestuff")
		if r != "no dice" {
			b.Errorf("fail bench, val was %s", r)
		}
	}
}
<|endoftext|>


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

package s3

// Region represents a regional endpoint to S3. Resources created within one
// region are entirely independent of those created in others. You should use
// one of the Region* constants defined by this package when referring to
// regions.
//
// See here for more info:
//
//     http://goo.gl/FhIRw
//
type Region string

const (
	RegionUsStandard           Region = "s3.amazonaws.com"
	RegionUsWestOregon         Region = "s3-us-west-2.amazonaws.com"
	RegionUsWestNorCal         Region = "s3-us-west-1.amazonaws.com"
	RegionEuIreland            Region = "s3-eu-west-1.amazonaws.com"
	RegionApacSingapore        Region = "s3-ap-southeast-1.amazonaws.com"
	RegionApacTokyo            Region = "s3-ap-northeast-1.amazonaws.com"
	RegionSouthAmericaSaoPaulo Region = "s3-sa-east-1.amazonaws.com"
)
<commit_msg>Fixed up a confusing comment.<commit_after>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package s3

// Region represents a regional endpoint to S3. Resources created within one
// region are entirely independent of those created in others. You should use
// one of the region constants defined by this package when referring to
// regions.
//
// See here for more info:
//
//     http://goo.gl/FhIRw
//
type Region string

const (
	RegionUsStandard           Region = "s3.amazonaws.com"
	RegionUsWestOregon         Region = "s3-us-west-2.amazonaws.com"
	RegionUsWestNorCal         Region = "s3-us-west-1.amazonaws.com"
	RegionEuIreland            Region = "s3-eu-west-1.amazonaws.com"
	RegionApacSingapore        Region = "s3-ap-southeast-1.amazonaws.com"
	RegionApacTokyo            Region = "s3-ap-northeast-1.amazonaws.com"
	RegionSouthAmericaSaoPaulo Region = "s3-sa-east-1.amazonaws.com"
)
<|endoftext|>


<commit_before>package storm

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestPrefixScan(t *testing.T) {
	db, cleanup := createDB(t)
	defer cleanup()

	node := db.From("node")

	doTestPrefixScan(t, node)
	doTestPrefixScan(t, db)

	nodeWithTransaction, _ := db.Begin(true)
	defer nodeWithTransaction.Commit()

	doTestPrefixScan(t, nodeWithTransaction)
}

func doTestPrefixScan(t *testing.T, node bucketScanner) {
	for i := 1; i < 3; i++ {
		n := node.From(fmt.Sprintf("%d%02d", 2015, i))
		err := n.Save(&SimpleUser{ID: i, Name: "John"})
		assert.NoError(t, err)
	}

	for i := 1; i < 4; i++ {
		n := node.From(fmt.Sprintf("%d%02d", 2016, i))
		err := n.Save(&SimpleUser{ID: i, Name: "John"})
		assert.NoError(t, err)
	}

	assert.Len(t, node.PrefixScan("2015"), 2)
	assert.Len(t, node.PrefixScan("20"), 5)

	buckets2016 := node.PrefixScan("2016")
	assert.Len(t, buckets2016, 3)
	count, err := buckets2016[1].Count(&SimpleUser{})

	assert.NoError(t, err)
	assert.Equal(t, 1, count)

	assert.NoError(t, buckets2016[1].One("ID", 2, &SimpleUser{}))
}

func TestRangeScan(t *testing.T) {
	db, cleanup := createDB(t)
	defer cleanup()

	node := db.From("node")

	doTestRangeScan(t, node)
	doTestRangeScan(t, db)

	nodeWithTransaction, _ := db.Begin(true)
	defer nodeWithTransaction.Commit()

	doTestRangeScan(t, nodeWithTransaction)
}

func doTestRangeScan(t *testing.T, node bucketScanner) {

	for y := 2012; y <= 2016; y++ {
		for m := 1; m <= 12; m++ {
			n := node.From(fmt.Sprintf("%d%02d", y, m))
			assert.NoError(t, n.Save(&SimpleUser{ID: m, Name: "John"}))
		}
	}

	assert.Len(t, node.RangeScan("2015", "2016"), 12)
	assert.Len(t, node.RangeScan("201201", "201203"), 3)
	assert.Len(t, node.RangeScan("2012", "201612"), 60)
	assert.Len(t, node.RangeScan("2012", "2017"), 60)

	secondIn2015 := node.RangeScan("2015", "2016")[1]
	assert.NoError(t, secondIn2015.One("ID", 2, &SimpleUser{}))
}

type bucketScanner interface {
	From(addend ...string) Node
	PrefixScan(prefix string) []Node
	RangeScan(min, max string) []Node
}
<commit_msg>Remove superflous test-interface<commit_after>package storm

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestPrefixScan(t *testing.T) {
	db, cleanup := createDB(t)
	defer cleanup()

	node := db.From("node")

	doTestPrefixScan(t, node)
	doTestPrefixScan(t, db)

	nodeWithTransaction, _ := db.Begin(true)
	defer nodeWithTransaction.Commit()

	doTestPrefixScan(t, nodeWithTransaction)
}

func doTestPrefixScan(t *testing.T, node Node) {
	for i := 1; i < 3; i++ {
		n := node.From(fmt.Sprintf("%d%02d", 2015, i))
		err := n.Save(&SimpleUser{ID: i, Name: "John"})
		assert.NoError(t, err)
	}

	for i := 1; i < 4; i++ {
		n := node.From(fmt.Sprintf("%d%02d", 2016, i))
		err := n.Save(&SimpleUser{ID: i, Name: "John"})
		assert.NoError(t, err)
	}

	assert.Len(t, node.PrefixScan("2015"), 2)
	assert.Len(t, node.PrefixScan("20"), 5)

	buckets2016 := node.PrefixScan("2016")
	assert.Len(t, buckets2016, 3)
	count, err := buckets2016[1].Count(&SimpleUser{})

	assert.NoError(t, err)
	assert.Equal(t, 1, count)

	assert.NoError(t, buckets2016[1].One("ID", 2, &SimpleUser{}))
}

func TestRangeScan(t *testing.T) {
	db, cleanup := createDB(t)
	defer cleanup()

	node := db.From("node")

	doTestRangeScan(t, node)
	doTestRangeScan(t, db)

	nodeWithTransaction, _ := db.Begin(true)
	defer nodeWithTransaction.Commit()

	doTestRangeScan(t, nodeWithTransaction)
}

func doTestRangeScan(t *testing.T, node Node) {

	for y := 2012; y <= 2016; y++ {
		for m := 1; m <= 12; m++ {
			n := node.From(fmt.Sprintf("%d%02d", y, m))
			assert.NoError(t, n.Save(&SimpleUser{ID: m, Name: "John"}))
		}
	}

	assert.Len(t, node.RangeScan("2015", "2016"), 12)
	assert.Len(t, node.RangeScan("201201", "201203"), 3)
	assert.Len(t, node.RangeScan("2012", "201612"), 60)
	assert.Len(t, node.RangeScan("2012", "2017"), 60)

	secondIn2015 := node.RangeScan("2015", "2016")[1]
	assert.NoError(t, secondIn2015.One("ID", 2, &SimpleUser{}))
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"os"
	"os/exec"
	"strings"
	"sync"
	"time"
)

type rCommand struct {
	command   *exec.Cmd
	output    string
	startTime int64
	endTime   int64
	err       error
	mainOut   string
}

//Scheduler the main task scheduler
type Scheduler struct {
	commands []*rCommand
	cMutex   *sync.Mutex
	rMutex   *sync.Mutex
	rMap     map[string]*rCommand
	Log      func(string)
}

func (s *Scheduler) getState(key string) string {
	s.rMutex.Lock()
	defer s.rMutex.Unlock()
	return fmt.Sprintf("%v -> %v", s.rMap[key].endTime, s.rMap[key].output)
}

func (s *Scheduler) markComplete(key string) {
	s.rMutex.Lock()
	if val, ok := s.rMap[key]; ok {
		val.endTime = time.Now().Unix()
		val.output = key
	} else {
		s.rMap[key] = &rCommand{endTime: time.Now().Unix(), output: key}
	}
	s.rMutex.Unlock()
}

// Schedule schedules a task
func (s *Scheduler) Schedule(c *rCommand) string {
	key := fmt.Sprintf("%v-%v", time.Now().Nanosecond(), c.command.Path)
	s.commands = append(s.commands, c)
	s.rMutex.Lock()
	s.rMap[key] = c
	s.rMutex.Unlock()
	s.processCommands()
	return key
}

func (s *Scheduler) getOutput(key string) string {
	s.rMutex.Lock()
	if val, ok := s.rMap[key]; ok {
		s.rMutex.Unlock()
		return val.output + " (" + val.mainOut + ")"
	}

	s.rMutex.Unlock()
	return "NOT_IN_MAP"
}

func (s *Scheduler) killJob(key string) {
	s.rMutex.Lock()
	if val, ok := s.rMap[key]; ok {
		val.command.Process.Kill()
		val.command.Process.Wait()
	}
	s.rMutex.Unlock()
}

func (s *Scheduler) removeJob(key string) {
	delete(s.rMap, key)
}

func (s *Scheduler) schedulerComplete(key string) bool {
	s.rMutex.Lock()
	defer s.rMutex.Unlock()
	if val, ok := s.rMap[key]; ok {
		if val.endTime > 0 {
			delete(s.rMap, key)
		}
		return val.endTime > 0
	}

	return false
}

func (s *Scheduler) processCommands() {
	s.cMutex.Lock()
	if len(s.commands) > 0 {
		c := s.commands[0]
		s.commands = s.commands[1:]
		run(c)
	}
	s.cMutex.Unlock()
}

func run(c *rCommand) error {
	env := os.Environ()
	home := ""
	for _, s := range env {
		if strings.HasPrefix(s, "HOME=") {
			home = s[5:]
		}
	}

	gpath := home + "/gobuild"
	c.command.Path = strings.Replace(c.command.Path, "$GOPATH", gpath, -1)
	for i := range c.command.Args {
		c.command.Args[i] = strings.Replace(c.command.Args[i], "$GOPATH", gpath, -1)
	}
	path := fmt.Sprintf("GOPATH=" + home + "/gobuild")
	pathbin := fmt.Sprintf("GOBIN=" + home + "/gobuild/bin")
	found := false
	for i, blah := range env {
		if strings.HasPrefix(blah, "GOPATH") {
			env[i] = path
			found = true
		}
		if strings.HasPrefix(blah, "GOBIN") {
			env[i] = pathbin
			found = true
		}
	}
	if !found {
		env = append(env, path)
	}
	c.command.Env = env

	out, err := c.command.StderrPipe()
	outr, err := c.command.StdoutPipe()

	if out != nil {
		scanner := bufio.NewScanner(out)
		go func() {
			for scanner != nil && scanner.Scan() {
				c.output += scanner.Text()
			}
			out.Close()
		}()
	}

	if outr != nil {
		scanner2 := bufio.NewScanner(outr)
		go func() {
			for scanner2 != nil && scanner2.Scan() {
				c.mainOut += scanner2.Text()
			}
			outr.Close()
		}()
	}

	err = c.command.Start()
	if err != nil {
		return err
	}
	c.startTime = time.Now().Unix()

	// Monitor the job and report completion
	go func() {
		err := c.command.Wait()
		if err != nil {
			c.err = err
		}
		c.endTime = time.Now().Unix()
	}()

	return nil
}
<commit_msg>Keep this deleted<commit_after>package main

import (
	"bufio"
	"fmt"
	"os"
	"os/exec"
	"strings"
	"sync"
	"time"
)

type rCommand struct {
	command   *exec.Cmd
	output    string
	startTime int64
	endTime   int64
	err       error
	mainOut   string
}

//Scheduler the main task scheduler
type Scheduler struct {
	commands []*rCommand
	cMutex   *sync.Mutex
	rMutex   *sync.Mutex
	rMap     map[string]*rCommand
	Log      func(string)
}

func (s *Scheduler) getState(key string) string {
	s.rMutex.Lock()
	defer s.rMutex.Unlock()
	return fmt.Sprintf("%v -> %v", s.rMap[key].endTime, s.rMap[key].output)
}

func (s *Scheduler) markComplete(key string) {
	s.rMutex.Lock()
	if val, ok := s.rMap[key]; ok {
		val.endTime = time.Now().Unix()
		val.output = key
	} else {
		s.rMap[key] = &rCommand{endTime: time.Now().Unix(), output: key}
	}
	s.rMutex.Unlock()
}

// Schedule schedules a task
func (s *Scheduler) Schedule(c *rCommand) string {
	key := fmt.Sprintf("%v-%v", time.Now().Nanosecond(), c.command.Path)
	s.commands = append(s.commands, c)
	s.rMutex.Lock()
	s.rMap[key] = c
	s.rMutex.Unlock()
	s.processCommands()
	return key
}

func (s *Scheduler) getOutput(key string) string {
	s.rMutex.Lock()
	if val, ok := s.rMap[key]; ok {
		s.rMutex.Unlock()
		return val.output + " (" + val.mainOut + ")"
	}

	s.rMutex.Unlock()
	return "NOT_IN_MAP"
}

func (s *Scheduler) killJob(key string) {
	s.rMutex.Lock()
	if val, ok := s.rMap[key]; ok {
		val.command.Process.Kill()
		val.command.Process.Wait()
	}
	s.rMutex.Unlock()
}

func (s *Scheduler) removeJob(key string) {
	delete(s.rMap, key)
}

func (s *Scheduler) schedulerComplete(key string) bool {
	s.rMutex.Lock()
	defer s.rMutex.Unlock()
	if val, ok := s.rMap[key]; ok {
		return val.endTime > 0
	}

	return false
}

func (s *Scheduler) processCommands() {
	s.cMutex.Lock()
	if len(s.commands) > 0 {
		c := s.commands[0]
		s.commands = s.commands[1:]
		run(c)
	}
	s.cMutex.Unlock()
}

func run(c *rCommand) error {
	env := os.Environ()
	home := ""
	for _, s := range env {
		if strings.HasPrefix(s, "HOME=") {
			home = s[5:]
		}
	}

	gpath := home + "/gobuild"
	c.command.Path = strings.Replace(c.command.Path, "$GOPATH", gpath, -1)
	for i := range c.command.Args {
		c.command.Args[i] = strings.Replace(c.command.Args[i], "$GOPATH", gpath, -1)
	}
	path := fmt.Sprintf("GOPATH=" + home + "/gobuild")
	pathbin := fmt.Sprintf("GOBIN=" + home + "/gobuild/bin")
	found := false
	for i, blah := range env {
		if strings.HasPrefix(blah, "GOPATH") {
			env[i] = path
			found = true
		}
		if strings.HasPrefix(blah, "GOBIN") {
			env[i] = pathbin
			found = true
		}
	}
	if !found {
		env = append(env, path)
	}
	c.command.Env = env

	out, err := c.command.StderrPipe()
	outr, err := c.command.StdoutPipe()

	if out != nil {
		scanner := bufio.NewScanner(out)
		go func() {
			for scanner != nil && scanner.Scan() {
				c.output += scanner.Text()
			}
			out.Close()
		}()
	}

	if outr != nil {
		scanner2 := bufio.NewScanner(outr)
		go func() {
			for scanner2 != nil && scanner2.Scan() {
				c.mainOut += scanner2.Text()
			}
			outr.Close()
		}()
	}

	err = c.command.Start()
	if err != nil {
		return err
	}
	c.startTime = time.Now().Unix()

	// Monitor the job and report completion
	go func() {
		err := c.command.Wait()
		if err != nil {
			c.err = err
		}
		c.endTime = time.Now().Unix()
	}()

	return nil
}
<|endoftext|>


<commit_before>package center

import (
	"strings"
	"time"

	"github.com/cozy/cozy-stack/pkg/apps"
	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/couchdb/mango"
	"github.com/cozy/cozy-stack/pkg/globals"
	"github.com/cozy/cozy-stack/pkg/instance"
	"github.com/cozy/cozy-stack/pkg/jobs"
	"github.com/cozy/cozy-stack/pkg/notification"
	"github.com/cozy/cozy-stack/pkg/oauth"
	"github.com/cozy/cozy-stack/pkg/permissions"
	"github.com/cozy/cozy-stack/pkg/workers/mails"
	"github.com/cozy/cozy-stack/pkg/workers/push"
	multierror "github.com/hashicorp/go-multierror"
)

// Push creates and send a new notification in database. This method verifies
// the permissions associated with this creation in order to check that it is
// granted to create a notification and to extract its source.
func Push(inst *instance.Instance, perm *permissions.Permission, n *notification.Notification) error {
	if n.Title == "" {
		return ErrBadNotification
	}

	// XXX Retro-compatible notifications with content/content_html fields.
	retroCompatMode := n.Content != "" || n.ContentHTML != ""

	var p *notification.Properties
	if !retroCompatMode {

		switch perm.Type {
		// Applications and services have TypeWebapp permissions
		case permissions.TypeWebapp:
			slug := strings.TrimPrefix(perm.SourceID, consts.Apps)
			man, err := apps.GetWebappBySlug(inst, slug)
			if err != nil {
				return err
			}
			var ok bool
			p, ok = man.Notifications[n.Category]
			if !ok {
				return ErrUnauthorized
			}
		default:
			return ErrUnauthorized
		}

		if p.Stateful {
			l, err := findLastNotification(inst, n.Source())
			if err != nil {
				return err
			}
			// when the state is the same for the last notification from this source,
			// we do not bother sending or creating a new notification.
			if l != nil && l.State == n.State {
				return nil
			}
		}
	}

	preferedChannel := n.PreferedChannel

	n.NID = ""
	n.NRev = ""
	n.SourceID = n.Source()
	n.CreatedAt = time.Now()
	n.PreferedChannel = ""

	if err := couchdb.CreateDoc(inst, n); err != nil {
		return err
	}

	if !retroCompatMode && preferedChannel == "mobile" {
		return sendPush(inst, p.Collapsible, n)
	}
	return sendMail(inst, n)
}

func findLastNotification(inst *instance.Instance, source string) (*notification.Notification, error) {
	var notifs []*notification.Notification
	req := &couchdb.FindRequest{
		UseIndex: "by-source-id",
		Selector: mango.Equal("source", source),
		Sort: mango.SortBy{
			{Field: "source_id", Direction: mango.Desc},
			{Field: "created_at", Direction: mango.Desc},
		},
		Limit: 1,
	}
	err := couchdb.FindDocs(inst, consts.Notifications, req, &notifs)
	if err != nil {
		return nil, err
	}
	if len(notifs) == 0 {
		return nil, nil
	}
	return notifs[0], nil
}

func sendPush(inst *instance.Instance, collapsible bool, n *notification.Notification) error {
	clients, err := oauth.GetNotifiables(inst)
	if err != nil {
		return err
	}
	var errm error
	for _, c := range clients {
		push := push.Message{
			Title:       n.Title,
			Message:     n.Message,
			Priority:    n.Priority,
			Sound:       n.Sound,
			Collapsible: collapsible,
			Platform:    c.NotificationPlatform,
			DeviceToken: c.NotificationDeviceToken,
		}
		msg, err := jobs.NewMessage(&push)
		if err != nil {
			return err
		}
		_, err = globals.GetBroker().PushJob(&jobs.JobRequest{
			Domain:     inst.Domain,
			WorkerType: "push",
			Message:    msg,
		})
		if err != nil {
			errm = multierror.Append(errm, err)
		}
	}
	return errm
}

func sendMail(inst *instance.Instance, n *notification.Notification) error {
	var parts []*mails.Part
	if n.ContentHTML == "" {
		parts = []*mails.Part{
			{Body: n.Content, Type: "text/plain"},
		}
	} else {
		parts = []*mails.Part{
			{Body: n.ContentHTML, Type: "text/html"},
			{Body: n.Content, Type: "text/plain"},
		}
	}
	mail := mails.Options{
		Mode:    mails.ModeNoReply,
		Subject: n.Title,
		Parts:   parts,
	}
	msg, err := jobs.NewMessage(&mail)
	if err != nil {
		return err
	}
	_, err = globals.GetBroker().PushJob(&jobs.JobRequest{
		Domain:     inst.Domain,
		WorkerType: "sendmail",
		Message:    msg,
	})
	return err
}
<commit_msg>Little refactoring of the sendMail method<commit_after>package center

import (
	"strings"
	"time"

	"github.com/cozy/cozy-stack/pkg/apps"
	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/couchdb/mango"
	"github.com/cozy/cozy-stack/pkg/globals"
	"github.com/cozy/cozy-stack/pkg/instance"
	"github.com/cozy/cozy-stack/pkg/jobs"
	"github.com/cozy/cozy-stack/pkg/notification"
	"github.com/cozy/cozy-stack/pkg/oauth"
	"github.com/cozy/cozy-stack/pkg/permissions"
	"github.com/cozy/cozy-stack/pkg/workers/mails"
	"github.com/cozy/cozy-stack/pkg/workers/push"
	multierror "github.com/hashicorp/go-multierror"
)

// Push creates and send a new notification in database. This method verifies
// the permissions associated with this creation in order to check that it is
// granted to create a notification and to extract its source.
func Push(inst *instance.Instance, perm *permissions.Permission, n *notification.Notification) error {
	if n.Title == "" {
		return ErrBadNotification
	}

	// XXX Retro-compatible notifications with content/content_html fields.
	retroCompatMode := n.Content != "" || n.ContentHTML != ""

	var p *notification.Properties
	if !retroCompatMode {

		switch perm.Type {
		// Applications and services have TypeWebapp permissions
		case permissions.TypeWebapp:
			slug := strings.TrimPrefix(perm.SourceID, consts.Apps)
			man, err := apps.GetWebappBySlug(inst, slug)
			if err != nil {
				return err
			}
			var ok bool
			p, ok = man.Notifications[n.Category]
			if !ok {
				return ErrUnauthorized
			}
		default:
			return ErrUnauthorized
		}

		if p.Stateful {
			l, err := findLastNotification(inst, n.Source())
			if err != nil {
				return err
			}
			// when the state is the same for the last notification from this source,
			// we do not bother sending or creating a new notification.
			if l != nil && l.State == n.State {
				return nil
			}
		}
	}

	preferedChannel := n.PreferedChannel

	n.NID = ""
	n.NRev = ""
	n.SourceID = n.Source()
	n.CreatedAt = time.Now()
	n.PreferedChannel = ""

	if err := couchdb.CreateDoc(inst, n); err != nil {
		return err
	}

	if !retroCompatMode && preferedChannel == "mobile" {
		return sendPush(inst, p.Collapsible, n)
	}
	return sendMail(inst, n)
}

func findLastNotification(inst *instance.Instance, source string) (*notification.Notification, error) {
	var notifs []*notification.Notification
	req := &couchdb.FindRequest{
		UseIndex: "by-source-id",
		Selector: mango.Equal("source", source),
		Sort: mango.SortBy{
			{Field: "source_id", Direction: mango.Desc},
			{Field: "created_at", Direction: mango.Desc},
		},
		Limit: 1,
	}
	err := couchdb.FindDocs(inst, consts.Notifications, req, &notifs)
	if err != nil {
		return nil, err
	}
	if len(notifs) == 0 {
		return nil, nil
	}
	return notifs[0], nil
}

func sendPush(inst *instance.Instance, collapsible bool, n *notification.Notification) error {
	clients, err := oauth.GetNotifiables(inst)
	if err != nil {
		return err
	}
	var errm error
	for _, c := range clients {
		push := push.Message{
			Title:       n.Title,
			Message:     n.Message,
			Priority:    n.Priority,
			Sound:       n.Sound,
			Collapsible: collapsible,
			Platform:    c.NotificationPlatform,
			DeviceToken: c.NotificationDeviceToken,
		}
		msg, err := jobs.NewMessage(&push)
		if err != nil {
			return err
		}
		_, err = globals.GetBroker().PushJob(&jobs.JobRequest{
			Domain:     inst.Domain,
			WorkerType: "push",
			Message:    msg,
		})
		if err != nil {
			errm = multierror.Append(errm, err)
		}
	}
	return errm
}

func sendMail(inst *instance.Instance, n *notification.Notification) error {
	parts := []*mails.Part{
		{Body: n.Content, Type: "text/plain"},
	}
	if n.ContentHTML == "" {
		parts = append(parts, &mails.Part{Body: n.ContentHTML, Type: "text/html"})
	}
	mail := mails.Options{
		Mode:    mails.ModeNoReply,
		Subject: n.Title,
		Parts:   parts,
	}
	msg, err := jobs.NewMessage(&mail)
	if err != nil {
		return err
	}
	_, err = globals.GetBroker().PushJob(&jobs.JobRequest{
		Domain:     inst.Domain,
		WorkerType: "sendmail",
		Message:    msg,
	})
	return err
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors.

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

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

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

package storage

import (
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apiserver/pkg/registry/generic"
	genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
	"k8s.io/apiserver/pkg/registry/rest"
	"k8s.io/apiserver/pkg/storage"
	api "k8s.io/kubernetes/pkg/apis/core"
	"k8s.io/kubernetes/pkg/printers"
	printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
	printerstorage "k8s.io/kubernetes/pkg/printers/storage"
	"k8s.io/kubernetes/pkg/registry/core/configmap"
)

// REST implements a RESTStorage for ConfigMap
type REST struct {
	*genericregistry.Store
}

// NewREST returns a RESTStorage object that will work with ConfigMap objects.
func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, error) {
	store := &genericregistry.Store{
		NewFunc:                  func() runtime.Object { return &api.ConfigMap{} },
		NewListFunc:              func() runtime.Object { return &api.ConfigMapList{} },
		DefaultQualifiedResource: api.Resource("configmaps"),

		CreateStrategy: configmap.Strategy,
		UpdateStrategy: configmap.Strategy,
		DeleteStrategy: configmap.Strategy,

		TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
	}
	options := &generic.StoreOptions{
		RESTOptions: optsGetter,
		AttrFunc:    configmap.GetAttrs,
		TriggerFunc: map[string]storage.IndexerFunc{"metadata.name": configmap.NameTriggerFunc},
	}
	if err := store.CompleteWithOptions(options); err != nil {
		return nil, err
	}
	return &REST{store}, nil
}

// Implement ShortNamesProvider
var _ rest.ShortNamesProvider = &REST{}

// ShortNames implements the ShortNamesProvider interface. Returns a list of short names for a resource.
func (r *REST) ShortNames() []string {
	return []string{"cm"}
}
<commit_msg>add PredicateFunc for configmap<commit_after>/*
Copyright 2015 The Kubernetes Authors.

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

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

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

package storage

import (
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apiserver/pkg/registry/generic"
	genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
	"k8s.io/apiserver/pkg/registry/rest"
	"k8s.io/apiserver/pkg/storage"
	api "k8s.io/kubernetes/pkg/apis/core"
	"k8s.io/kubernetes/pkg/printers"
	printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
	printerstorage "k8s.io/kubernetes/pkg/printers/storage"
	"k8s.io/kubernetes/pkg/registry/core/configmap"
)

// REST implements a RESTStorage for ConfigMap
type REST struct {
	*genericregistry.Store
}

// NewREST returns a RESTStorage object that will work with ConfigMap objects.
func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, error) {
	store := &genericregistry.Store{
		NewFunc:                  func() runtime.Object { return &api.ConfigMap{} },
		NewListFunc:              func() runtime.Object { return &api.ConfigMapList{} },
		PredicateFunc:            configmap.Matcher,
		DefaultQualifiedResource: api.Resource("configmaps"),

		CreateStrategy: configmap.Strategy,
		UpdateStrategy: configmap.Strategy,
		DeleteStrategy: configmap.Strategy,

		TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
	}
	options := &generic.StoreOptions{
		RESTOptions: optsGetter,
		AttrFunc:    configmap.GetAttrs,
		TriggerFunc: map[string]storage.IndexerFunc{"metadata.name": configmap.NameTriggerFunc},
	}
	if err := store.CompleteWithOptions(options); err != nil {
		return nil, err
	}
	return &REST{store}, nil
}

// Implement ShortNamesProvider
var _ rest.ShortNamesProvider = &REST{}

// ShortNames implements the ShortNamesProvider interface. Returns a list of short names for a resource.
func (r *REST) ShortNames() []string {
	return []string{"cm"}
}
<|endoftext|>


<commit_before>package arn

import (
	"errors"
	"strings"
	"sync"
	"time"
)

var setNickMutex sync.Mutex

// User ...
type User struct {
	ID         string       `json:"id"`
	Nick       string       `json:"nick" editable:"true"`
	FirstName  string       `json:"firstName"`
	LastName   string       `json:"lastName"`
	Email      string       `json:"email"`
	Role       string       `json:"role"`
	Registered string       `json:"registered"`
	LastLogin  string       `json:"lastLogin"`
	LastSeen   string       `json:"lastSeen"`
	Gender     string       `json:"gender"`
	Language   string       `json:"language"`
	Avatar     string       `json:"avatar"`
	Tagline    string       `json:"tagline" editable:"true"`
	Website    string       `json:"website" editable:"true"`
	IP         string       `json:"ip"`
	UserAgent  string       `json:"agent"`
	AgeRange   UserAgeRange `json:"ageRange"`
	Location   UserLocation `json:"location"`
	Accounts   UserAccounts `json:"accounts"`
	Browser    UserBrowser  `json:"browser"`
	OS         UserOS       `json:"os"`
	Following  []string     `json:"following"`
}

// NewUser creates an empty user object with a unique ID.
func NewUser() *User {
	user := &User{
		ID: GenerateID("User"),

		// Avoid nil value fields
		Following: make([]string, 0),
	}

	return user
}

// RegisterUser registers a new user in the database and sets up all the required references.
func RegisterUser(user *User) error {
	var err error

	user.Registered = DateTimeUTC()
	user.LastLogin = user.Registered
	user.LastSeen = user.Registered

	// Save nick in NickToUser table
	err = DB.Set("NickToUser", user.Nick, &NickToUser{
		Nick:   user.Nick,
		UserID: user.ID,
	})

	if err != nil {
		return err
	}

	// Save email in EmailToUser table
	err = DB.Set("EmailToUser", user.Email, &EmailToUser{
		Email:  user.Email,
		UserID: user.ID,
	})

	if err != nil {
		return err
	}

	// Create default settings
	err = NewSettings(user.ID).Save()

	if err != nil {
		return err
	}

	// Add empty anime list
	err = DB.Set("AnimeList", user.ID, &AnimeList{
		UserID: user.ID,
		Items:  make([]*AnimeListItem, 0),
	})

	if err != nil {
		return err
	}

	return nil
}

// RealName returns the real name of the user.
func (user *User) RealName() string {
	if user.LastName == "" {
		return user.FirstName
	}

	if user.FirstName == "" {
		return user.LastName
	}

	return user.FirstName + " " + user.LastName
}

// RegisteredTime ...
func (user *User) RegisteredTime() time.Time {
	reg, _ := time.Parse(time.RFC3339, user.Registered)
	return reg
}

// IsActive ...
func (user *User) IsActive() bool {
	// Exclude people who didn't change their nickname.
	if strings.HasPrefix(user.Nick, "g") || strings.HasPrefix(user.Nick, "fb") || strings.HasPrefix(user.Nick, "t") {
		return false
	}

	return true
}

// WebsiteURL adds https:// to the URL.
func (user *User) WebsiteURL() string {
	return "https://" + user.Website
}

// Link returns the URI to the user page.
func (user *User) Link() string {
	return "/+" + user.Nick
}

// CoverImageURL ...
func (user *User) CoverImageURL() string {
	return "/images/cover/default"
}

// HasAvatar ...
func (user *User) HasAvatar() bool {
	return user.Avatar != ""
}

// SmallAvatar ...
func (user *User) SmallAvatar() string {
	return "//media.notify.moe/images/avatars/small/" + user.ID + ".webp"
}

// LargeAvatar ...
func (user *User) LargeAvatar() string {
	return "//media.notify.moe/images/avatars/large/" + user.ID + ".webp"
}

// SetNick changes the user's nickname safely.
func (user *User) SetNick(newName string) error {
	setNickMutex.Lock()
	defer setNickMutex.Unlock()

	newName = FixUserNick(newName)

	if !IsValidNick(newName) {
		return errors.New("Invalid nickname")
	}

	if newName == user.Nick {
		return nil
	}

	// Make sure the nickname doesn't exist already
	_, err := GetUserByNick(newName)

	// If there was no error: the username exists.
	// If "not found" is not included in the error message it's a different error type.
	if err == nil || strings.Index(err.Error(), "not found") == -1 {
		return errors.New("Username '" + newName + "' is taken already")
	}

	return user.ForceSetNick(newName)
}

// ForceSetNick forces a nickname overwrite.
func (user *User) ForceSetNick(newName string) error {
	// Delete old nick reference
	DB.Delete("NickToUser", user.Nick)

	// Set new nick
	user.Nick = newName

	return DB.Set("NickToUser", user.Nick, &NickToUser{
		Nick:   user.Nick,
		UserID: user.ID,
	})
}

// SetEmail changes the user's email safely.
func (user *User) SetEmail(newName string) error {
	if !IsValidEmail(user.Email) {
		return errors.New("Invalid email address")
	}

	// Delete old email reference
	DB.Delete("EmailToUser", user.Email)

	// Set new email
	user.Email = newName

	return DB.Set("EmailToUser", user.Email, &EmailToUser{
		Email:  user.Email,
		UserID: user.ID,
	})
}
<commit_msg>Added RefreshOsuInfo<commit_after>package arn

import (
	"errors"
	"strconv"
	"strings"
	"sync"
	"time"
)

var setNickMutex sync.Mutex

// User ...
type User struct {
	ID         string       `json:"id"`
	Nick       string       `json:"nick" editable:"true"`
	FirstName  string       `json:"firstName"`
	LastName   string       `json:"lastName"`
	Email      string       `json:"email"`
	Role       string       `json:"role"`
	Registered string       `json:"registered"`
	LastLogin  string       `json:"lastLogin"`
	LastSeen   string       `json:"lastSeen"`
	Gender     string       `json:"gender"`
	Language   string       `json:"language"`
	Avatar     string       `json:"avatar"`
	Tagline    string       `json:"tagline" editable:"true"`
	Website    string       `json:"website" editable:"true"`
	IP         string       `json:"ip"`
	UserAgent  string       `json:"agent"`
	AgeRange   UserAgeRange `json:"ageRange"`
	Location   UserLocation `json:"location"`
	Accounts   UserAccounts `json:"accounts"`
	Browser    UserBrowser  `json:"browser"`
	OS         UserOS       `json:"os"`
	Following  []string     `json:"following"`
}

// NewUser creates an empty user object with a unique ID.
func NewUser() *User {
	user := &User{
		ID: GenerateID("User"),

		// Avoid nil value fields
		Following: make([]string, 0),
	}

	return user
}

// RegisterUser registers a new user in the database and sets up all the required references.
func RegisterUser(user *User) error {
	var err error

	user.Registered = DateTimeUTC()
	user.LastLogin = user.Registered
	user.LastSeen = user.Registered

	// Save nick in NickToUser table
	err = DB.Set("NickToUser", user.Nick, &NickToUser{
		Nick:   user.Nick,
		UserID: user.ID,
	})

	if err != nil {
		return err
	}

	// Save email in EmailToUser table
	err = DB.Set("EmailToUser", user.Email, &EmailToUser{
		Email:  user.Email,
		UserID: user.ID,
	})

	if err != nil {
		return err
	}

	// Create default settings
	err = NewSettings(user.ID).Save()

	if err != nil {
		return err
	}

	// Add empty anime list
	err = DB.Set("AnimeList", user.ID, &AnimeList{
		UserID: user.ID,
		Items:  make([]*AnimeListItem, 0),
	})

	if err != nil {
		return err
	}

	return nil
}

// RealName returns the real name of the user.
func (user *User) RealName() string {
	if user.LastName == "" {
		return user.FirstName
	}

	if user.FirstName == "" {
		return user.LastName
	}

	return user.FirstName + " " + user.LastName
}

// RegisteredTime ...
func (user *User) RegisteredTime() time.Time {
	reg, _ := time.Parse(time.RFC3339, user.Registered)
	return reg
}

// IsActive ...
func (user *User) IsActive() bool {
	// Exclude people who didn't change their nickname.
	if strings.HasPrefix(user.Nick, "g") || strings.HasPrefix(user.Nick, "fb") || strings.HasPrefix(user.Nick, "t") {
		return false
	}

	return true
}

// WebsiteURL adds https:// to the URL.
func (user *User) WebsiteURL() string {
	return "https://" + user.Website
}

// Link returns the URI to the user page.
func (user *User) Link() string {
	return "/+" + user.Nick
}

// CoverImageURL ...
func (user *User) CoverImageURL() string {
	return "/images/cover/default"
}

// HasAvatar ...
func (user *User) HasAvatar() bool {
	return user.Avatar != ""
}

// SmallAvatar ...
func (user *User) SmallAvatar() string {
	return "//media.notify.moe/images/avatars/small/" + user.ID + ".webp"
}

// LargeAvatar ...
func (user *User) LargeAvatar() string {
	return "//media.notify.moe/images/avatars/large/" + user.ID + ".webp"
}

// SetNick changes the user's nickname safely.
func (user *User) SetNick(newName string) error {
	setNickMutex.Lock()
	defer setNickMutex.Unlock()

	newName = FixUserNick(newName)

	if !IsValidNick(newName) {
		return errors.New("Invalid nickname")
	}

	if newName == user.Nick {
		return nil
	}

	// Make sure the nickname doesn't exist already
	_, err := GetUserByNick(newName)

	// If there was no error: the username exists.
	// If "not found" is not included in the error message it's a different error type.
	if err == nil || strings.Index(err.Error(), "not found") == -1 {
		return errors.New("Username '" + newName + "' is taken already")
	}

	return user.ForceSetNick(newName)
}

// ForceSetNick forces a nickname overwrite.
func (user *User) ForceSetNick(newName string) error {
	// Delete old nick reference
	DB.Delete("NickToUser", user.Nick)

	// Set new nick
	user.Nick = newName

	return DB.Set("NickToUser", user.Nick, &NickToUser{
		Nick:   user.Nick,
		UserID: user.ID,
	})
}

// RefreshOsuInfo refreshes a user's Osu information.
func (user *User) RefreshOsuInfo() error {
	if user.Accounts.Osu.Nick == "" {
		return nil
	}

	osu, err := GetOsuUser(user.Accounts.Osu.Nick)

	if err != nil {
		return err
	}

	user.Accounts.Osu.PP, _ = strconv.ParseFloat(osu.PPRaw, 64)
	user.Accounts.Osu.Level, _ = strconv.ParseFloat(osu.Level, 64)
	user.Accounts.Osu.Accuracy, _ = strconv.ParseFloat(osu.Accuracy, 64)

	return nil
}

// SetEmail changes the user's email safely.
func (user *User) SetEmail(newName string) error {
	if !IsValidEmail(user.Email) {
		return errors.New("Invalid email address")
	}

	// Delete old email reference
	DB.Delete("EmailToUser", user.Email)

	// Set new email
	user.Email = newName

	return DB.Set("EmailToUser", user.Email, &EmailToUser{
		Email:  user.Email,
		UserID: user.ID,
	})
}
<|endoftext|>


<commit_before>package octokit

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestRootEmojisService_All(t *testing.T) {
	setup()
	defer tearDown()

	stubGet(t, "/emojis", "emojis", nil)

	url, err := EmojisURL.Expand(nil)
	assert.NoError(t, err)

	emojis, result := client.Emojis(url).All()
	assert.False(t, result.HasError())
	var penguin = "https://github.global.ssl.fastly.net/images/icons/emoji/penguin.png?v5"
	var metal = "https://github.global.ssl.fastly.net/images/icons/emoji/metal.png?v5"
	assert.Equal(t, emojis["penguin"], penguin)
	assert.Equal(t, emojis["metal"], metal)
}
<commit_msg>reverted my mistake on emojis_test<commit_after>package octokit

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestRootEmojisService_All(t *testing.T) {
	setup()
	defer tearDown()

	stubGet(t, "/emojis", "emojis", nil)

	url, err := EmojisURL.Expand(nil)
	assert.NoError(t, err)

	emojis, result := client.Emojis(url).All()
	assert.False(t, result.HasError())
	var penguin = "https://github.global.ssl.fastly.net/images/icons/emoji/penguin.png?v5"
	var metal = "https://github.global.ssl.fastly.net/images/icons/emoji/metal.png?v5"
	assert.Equal(t, penguin, emojis["penguin"])
	assert.Equal(t, metal, emojis["metal"])
}
<|endoftext|>


<commit_before>package zipfs

import (
	"github.com/hanwen/go-fuse/fuse"
	"os"
	"testing"
)

func setupZipfs() (mountPoint string, cleanup func()) {
	wd, err := os.Getwd()
	CheckSuccess(err)
	zfs, err := NewArchiveFileSystem(wd + "/test.zip")
	CheckSuccess(err)

	mountPoint = fuse.MakeTempDir()

	state, _, err := fuse.MountFileSystem(mountPoint, zfs, nil)

	state.Debug = true
	go state.Loop(false)

	return mountPoint, func() {
		state.Unmount()
		os.RemoveAll(mountPoint)
	}
}

func TestZipFs(t *testing.T) {
	mountPoint, clean := setupZipfs()
	defer clean()

	d, err := os.Open(mountPoint)
	CheckSuccess(err)

	names, err := d.Readdirnames(-1)
	CheckSuccess(err)
	err = d.Close()
	CheckSuccess(err)

	if len(names) != 2 {
		t.Error("wrong length", names)
	}
	fi, err := os.Stat(mountPoint + "/subdir")
	CheckSuccess(err)
	if !fi.IsDirectory() {
		t.Error("directory type", fi)
	}

	fi, err = os.Stat(mountPoint + "/file.txt")
	CheckSuccess(err)

	if !fi.IsRegular() {
		t.Error("file type", fi)
	}

	f, err := os.Open(mountPoint + "/file.txt")
	CheckSuccess(err)

	b := make([]byte, 1024)
	n, err := f.Read(b)

	b = b[:n]
	if string(b) != "hello\n" {
		t.Error("content fail", b[:n])
	}
	f.Close()
}

func TestLinkCount(t *testing.T) {
	t.Log("TestLinkCount")
	mp, clean := setupZipfs()
	defer clean()

	fi, err := os.Stat(mp + "/file.txt")
	CheckSuccess(err)
	if fi.Nlink != 1 {
		t.Fatal("wrong link count", fi.Nlink)
	}
}
<commit_msg>trim zipfs test.<commit_after>package zipfs

import (
	"github.com/hanwen/go-fuse/fuse"
	"io/ioutil"
	"os"
	"testing"
)

func setupZipfs() (mountPoint string, cleanup func()) {
	wd, err := os.Getwd()
	CheckSuccess(err)
	zfs, err := NewArchiveFileSystem(wd + "/test.zip")
	CheckSuccess(err)

	mountPoint = fuse.MakeTempDir()

	state, _, err := fuse.MountFileSystem(mountPoint, zfs, nil)

	state.Debug = true
	go state.Loop(false)

	return mountPoint, func() {
		state.Unmount()
		os.RemoveAll(mountPoint)
	}
}

func TestZipFs(t *testing.T) {
	mountPoint, clean := setupZipfs()
	defer clean()
	entries, err := ioutil.ReadDir(mountPoint)
	CheckSuccess(err)

	if len(entries) != 2 {
		t.Error("wrong length", entries)
	}
	fi, err := os.Stat(mountPoint + "/subdir")
	CheckSuccess(err)
	if !fi.IsDirectory() {
		t.Error("directory type", fi)
	}

	fi, err = os.Stat(mountPoint + "/file.txt")
	CheckSuccess(err)

	if !fi.IsRegular() {
		t.Error("file type", fi)
	}

	f, err := os.Open(mountPoint + "/file.txt")
	CheckSuccess(err)

	b := make([]byte, 1024)
	n, err := f.Read(b)

	b = b[:n]
	if string(b) != "hello\n" {
		t.Error("content fail", b[:n])
	}
	f.Close()
}

func TestLinkCount(t *testing.T) {
	t.Log("TestLinkCount")
	mp, clean := setupZipfs()
	defer clean()

	fi, err := os.Stat(mp + "/file.txt")
	CheckSuccess(err)
	if fi.Nlink != 1 {
		t.Fatal("wrong link count", fi.Nlink)
	}
}
<|endoftext|>


<commit_before>package types

import (
	"encoding/json"
	"fmt"
	"testing"
)

var placeCountainers map[string]*PlaceCountainer

func loadPC() error {
	// Get the input
	data := testData["place"].known
	if len(data) == 0 {
		return nil
	}

	pcs := make(map[string]*PlaceCountainer, len(data))
	// For each of them, unmarshal and add to placeCountainers
	for name, datum := range data {
		var pc = PlaceCountainer{}

		err := json.Unmarshal(datum.raw, &pc)
		if err != nil {
			return fmt.Errorf("Error while unmarshalling: %v", err)
		}

		pcs[name] = &pc
	}

	placeCountainers = pcs

	return nil
}

// TestPlaceCountainer_Place_NoCompare tries to unmarshal all json test data for this type, and then call a few functions on it.
func TestPlaceCountainer_Place_NoCompare(t *testing.T) {
	// Get the input
	data := placeCountainers
	if len(data) == 0 {
		t.Skip("No data to test")
	}

	// Create the run function generator, allowing us to run it in parallel
	rgen := func(pc *PlaceCountainer) func(t *testing.T) {
		return func(t *testing.T) {
			place, err := pc.Place()
			if err != nil {
				t.Errorf("Error while calling .Place(): %v", err)
			}

			placeName := place.PlaceName()
			placeType := place.PlaceType()
			placeID := place.PlaceID()

			_, _, _ = placeName, placeType, placeID
		}
	}

	// For each of them, let's run a subtest
	for name, datum := range data {
		// Create the run function
		rfunc := rgen(datum)

		// Run !
		t.Run(name, rfunc)
	}
}

func TestPlaceCountainer_Check_NoCompare(t *testing.T) {
	// Get the input
	data := placeCountainers
	if len(data) == 0 {
		t.Skip("No data to test")
	}

	// Create the run function generator, allowing us to run it in parallel
	rgen := func(pc *PlaceCountainer) func(t *testing.T) {
		return func(t *testing.T) {
			err := pc.Check()
			if err != nil {
				t.Errorf("Check gave us invalid results: %v", err)
			}
		}
	}

	// For each of them, let's run a subtest
	for name, datum := range data {
		// Create the run function
		rfunc := rgen(datum)

		// Run !
		t.Run(name, rfunc)
	}
}
<commit_msg>Better comments for TestPlaceCountainer_XXX<commit_after>package types

import (
	"encoding/json"
	"fmt"
	"testing"
)

var placeCountainers map[string]*PlaceCountainer

func loadPC() error {
	// Get the input
	data := testData["place"].known
	if len(data) == 0 {
		return nil
	}

	pcs := make(map[string]*PlaceCountainer, len(data))
	// For each of them, unmarshal and add to placeCountainers
	for name, datum := range data {
		var pc = PlaceCountainer{}

		err := json.Unmarshal(datum.raw, &pc)
		if err != nil {
			return fmt.Errorf("Error while unmarshalling: %v", err)
		}

		pcs[name] = &pc
	}

	placeCountainers = pcs

	return nil
}

// TestPlaceCountainer_Place_NoCompare tests the PlaceCountainer.Place method
func TestPlaceCountainer_Place_NoCompare(t *testing.T) {
	// Get the input
	data := placeCountainers
	if len(data) == 0 {
		t.Skip("No data to test")
	}

	// Create the run function generator, allowing us to run it in parallel
	rgen := func(pc *PlaceCountainer) func(t *testing.T) {
		return func(t *testing.T) {
			place, err := pc.Place()
			if err != nil {
				t.Errorf("Error while calling .Place(): %v", err)
			}

			placeName := place.PlaceName()
			placeType := place.PlaceType()
			placeID := place.PlaceID()

			_, _, _ = placeName, placeType, placeID
		}
	}

	// For each of them, let's run a subtest
	for name, datum := range data {
		// Create the run function
		rfunc := rgen(datum)

		// Run !
		t.Run(name, rfunc)
	}
}

// TestPlaceCountainer_Check_NoCompare tests the PlaceCountainer.Check method
func TestPlaceCountainer_Check_NoCompare(t *testing.T) {
	// Get the input
	data := placeCountainers
	if len(data) == 0 {
		t.Skip("No data to test")
	}

	// Create the run function generator, allowing us to run it in parallel
	rgen := func(pc *PlaceCountainer) func(t *testing.T) {
		return func(t *testing.T) {
			err := pc.Check()
			if err != nil {
				t.Errorf("Check gave us invalid results: %v", err)
			}
		}
	}

	// For each of them, let's run a subtest
	for name, datum := range data {
		// Create the run function
		rfunc := rgen(datum)

		// Run !
		t.Run(name, rfunc)
	}
}
<|endoftext|>


<commit_before>package clui

import (
	xs "github.com/huandu/xstrings"
	term "github.com/nsf/termbox-go"
)

type TextReader struct {
	BaseControl
	colorized bool
	topLine   int
	lineCount int

	onDrawLine func(int) string
}

func CreateTextReader(parent Control, width, height int, scale int) *TextReader {
	l := new(TextReader)

	if height == AutoSize {
		height = 10
	}
	if width == AutoSize {
		width = 20
	}

	l.SetSize(width, height)
	l.SetConstraints(width, height)
	l.parent = parent

	l.SetScale(scale)

	if parent != nil {
		parent.AddChild(l)
	}

	l.onDrawLine = nil

	return l
}

func (l *TextReader) drawText() {
	if l.onDrawLine == nil {
		return
	}

	PushAttributes()
	defer PopAttributes()

	bg, fg := RealColor(l.bg, ColorEditBack), RealColor(l.fg, ColorEditText)
	if l.Active() {
		bg, fg = RealColor(l.bg, ColorEditActiveBack), RealColor(l.fg, ColorEditActiveText)
	}
	SetTextColor(fg)
	SetBackColor(bg)

	ind := 0
	for ind < l.height {
		var str string
		if ind+l.topLine < l.lineCount {
			str = l.onDrawLine(ind + l.topLine)
		} else {
			if ind+l.topLine == l.lineCount+5 {
				str = xs.Center("--- THE END ---", l.width, " ")
			} else {
				str = ""
			}
		}

		if str != "" {
			str = SliceColorized(str, 0, l.width)
			DrawText(l.x, l.y+ind, str)
		}

		ind++
	}
}

// Repaint draws the control on its View surface
func (l *TextReader) Draw() {
	PushAttributes()
	defer PopAttributes()

	x, y := l.Pos()
	w, h := l.Size()

	bg, fg := RealColor(l.bg, ColorEditBack), RealColor(l.fg, ColorEditText)
	if l.Active() {
		bg, fg = RealColor(l.bg, ColorEditActiveBack), RealColor(l.fg, ColorEditActiveText)
	}

	SetTextColor(fg)
	SetBackColor(bg)
	FillRect(x, y, w, h, ' ')
	l.drawText()
}

func (l *TextReader) home() {
	if l.topLine != 0 {
		l.topLine = 0
	}
}

func (l *TextReader) end() {
	if l.lineCount > 0 && l.topLine != l.lineCount-1 {
		l.topLine = l.lineCount - 1
	}
}

func (l *TextReader) moveUp(count int) {
	if l.topLine != 0 {
		l.topLine -= count
		if l.topLine < 0 {
			l.topLine = 0
		}
	}
}

func (l *TextReader) moveDown(count int) {
	if l.lineCount > 0 && l.topLine != l.lineCount-1 {
		l.topLine += count
		if l.topLine > l.lineCount-1 {
			l.topLine = l.lineCount - 1
		}
	}
}

func (l *TextReader) processMouseClick(ev Event) bool {
	if ev.Key != term.MouseLeft {
		return false
	}

	dy := ev.Y - l.y
	ww := l.height

	if dy < l.height/2 {
		l.moveUp(ww - 1)
	} else {
		l.moveDown(ww - 1)
	}

	return true
}

/*
ProcessEvent processes all events come from the control parent. If a control
processes an event it should return true. If the method returns false it means
that the control do not want or cannot process the event and the caller sends
the event to the control parent
*/
func (l *TextReader) ProcessEvent(event Event) bool {
	if !l.Active() || !l.Enabled() {
		return false
	}

	switch event.Type {
	case EventKey:
		switch event.Key {
		case term.KeyHome:
			l.home()
			return true
		case term.KeyEnd:
			l.end()
			return true
		case term.KeyArrowUp:
			l.moveUp(1)
			return true
		case term.KeyArrowDown:
			l.moveDown(1)
			return true
		case term.KeyPgup:
			l.moveUp(l.height - 1)
			return true
		case term.KeyPgdn, term.KeySpace:
			l.moveDown(l.height - 1)
			return true
		}

		switch event.Ch {
		case 'k', 'K':
			l.moveUp(1)
			return true
		case 'j', 'J':
			l.moveDown(1)
			return true
		case 'u', 'U':
			l.moveUp(l.height - 1)
			return true
		case 'd', 'D':
			l.moveDown(l.height - 1)
			return true
		default:
			return false
		}
	case EventMouse:
		return l.processMouseClick(event)
	}

	return false
}

// OnDrawLine is called every time the reader is going to display a line
// the argument of the function is the line number to display
func (l *TextReader) OnDrawLine(fn func(int) string) {
	l.onDrawLine = fn
}

func (l *TextReader) LineCount() int {
	return l.lineCount
}

func (l *TextReader) SetLineCount(lineNo int) {
	if lineNo < l.topLine-1 {
		l.topLine = lineNo - 1
	}
	l.lineCount = lineNo
}

func (l *TextReader) TopLine() int {
	return l.topLine
}
<commit_msg>added callback for reader when text position changes<commit_after>package clui

import (
	xs "github.com/huandu/xstrings"
	term "github.com/nsf/termbox-go"
)

type TextReader struct {
	BaseControl
	colorized bool
	topLine   int
	lineCount int

	onDrawLine        func(int) string
	onPositionChanged func(int, int)
}

func CreateTextReader(parent Control, width, height int, scale int) *TextReader {
	l := new(TextReader)

	if height == AutoSize {
		height = 10
	}
	if width == AutoSize {
		width = 20
	}

	l.SetSize(width, height)
	l.SetConstraints(width, height)
	l.parent = parent

	l.SetScale(scale)

	if parent != nil {
		parent.AddChild(l)
	}

	l.onDrawLine = nil
	l.onPositionChanged = nil

	return l
}

func (l *TextReader) drawText() {
	if l.onDrawLine == nil {
		return
	}

	PushAttributes()
	defer PopAttributes()

	bg, fg := RealColor(l.bg, ColorEditBack), RealColor(l.fg, ColorEditText)
	if l.Active() {
		bg, fg = RealColor(l.bg, ColorEditActiveBack), RealColor(l.fg, ColorEditActiveText)
	}
	SetTextColor(fg)
	SetBackColor(bg)

	ind := 0
	for ind < l.height {
		var str string
		if ind+l.topLine < l.lineCount {
			str = l.onDrawLine(ind + l.topLine)
		} else {
			if ind+l.topLine == l.lineCount+5 {
				str = xs.Center("--- THE END ---", l.width, " ")
			} else {
				str = ""
			}
		}

		if str != "" {
			str = SliceColorized(str, 0, l.width)
			DrawText(l.x, l.y+ind, str)
		}

		ind++
	}
}

// Repaint draws the control on its View surface
func (l *TextReader) Draw() {
	PushAttributes()
	defer PopAttributes()

	x, y := l.Pos()
	w, h := l.Size()

	bg, fg := RealColor(l.bg, ColorEditBack), RealColor(l.fg, ColorEditText)
	if l.Active() {
		bg, fg = RealColor(l.bg, ColorEditActiveBack), RealColor(l.fg, ColorEditActiveText)
	}

	SetTextColor(fg)
	SetBackColor(bg)
	FillRect(x, y, w, h, ' ')
	l.drawText()
}

func (l *TextReader) home() {
	if l.topLine != 0 {
		l.topLine = 0

		if l.onPositionChanged != nil {
			l.onPositionChanged(l.topLine, l.lineCount)
		}
	}
}

func (l *TextReader) end() {
	if l.lineCount > 0 && l.topLine != l.lineCount-1 {
		l.topLine = l.lineCount - 1

		if l.onPositionChanged != nil {
			l.onPositionChanged(l.topLine, l.lineCount)
		}
	}
}

func (l *TextReader) moveUp(count int) {
	if l.topLine != 0 {
		l.topLine -= count
		if l.topLine < 0 {
			l.topLine = 0
		}

		if l.onPositionChanged != nil {
			l.onPositionChanged(l.topLine, l.lineCount)
		}
	}
}

func (l *TextReader) moveDown(count int) {
	if l.lineCount > 0 && l.topLine != l.lineCount-1 {
		l.topLine += count
		if l.topLine > l.lineCount-1 {
			l.topLine = l.lineCount - 1
		}

		if l.onPositionChanged != nil {
			l.onPositionChanged(l.topLine, l.lineCount)
		}
	}
}

func (l *TextReader) processMouseClick(ev Event) bool {
	if ev.Key != term.MouseLeft {
		return false
	}

	dy := ev.Y - l.y
	ww := l.height

	if dy < l.height/2 {
		l.moveUp(ww - 1)
	} else {
		l.moveDown(ww - 1)
	}

	return true
}

/*
ProcessEvent processes all events come from the control parent. If a control
processes an event it should return true. If the method returns false it means
that the control do not want or cannot process the event and the caller sends
the event to the control parent
*/
func (l *TextReader) ProcessEvent(event Event) bool {
	if !l.Active() || !l.Enabled() {
		return false
	}

	switch event.Type {
	case EventKey:
		switch event.Key {
		case term.KeyHome:
			l.home()
			return true
		case term.KeyEnd:
			l.end()
			return true
		case term.KeyArrowUp:
			l.moveUp(1)
			return true
		case term.KeyArrowDown:
			l.moveDown(1)
			return true
		case term.KeyPgup:
			l.moveUp(l.height - 1)
			return true
		case term.KeyPgdn, term.KeySpace:
			l.moveDown(l.height - 1)
			return true
		}

		switch event.Ch {
		case 'k', 'K':
			l.moveUp(1)
			return true
		case 'j', 'J':
			l.moveDown(1)
			return true
		case 'u', 'U':
			l.moveUp(l.height - 1)
			return true
		case 'd', 'D':
			l.moveDown(l.height - 1)
			return true
		default:
			return false
		}
	case EventMouse:
		return l.processMouseClick(event)
	}

	return false
}

// OnDrawLine is called every time the reader is going to display a line
// the argument of the function is the line number to display
func (l *TextReader) OnDrawLine(fn func(int) string) {
	l.onDrawLine = fn
}

// OnPositionChanged is called every time the reader changes the top line or
// the total number of lines is changed
// Callback gets two numbers: the current top line, and the total number of
// lines. Top line number starts from 0.
func (l *TextReader) OnPositionChanged(fn func(int, int)) {
	l.onPositionChanged = fn
}

func (l *TextReader) LineCount() int {
	return l.lineCount
}

func (l *TextReader) SetLineCount(lineNo int) {
	if l.topLine == lineNo {
		return
	}

	if lineNo < l.topLine-1 {
		l.topLine = lineNo - 1
	}
	l.lineCount = lineNo

	if l.onPositionChanged != nil {
		l.onPositionChanged(l.topLine, l.lineCount)
	}
}

func (l *TextReader) TopLine() int {
	return l.topLine
}
<|endoftext|>