<commit_before>// Package minify relates MIME type to minifiers. Several minifiers are provided in the subpackages.
package minify // import "github.com/tdewolff/minify"

import (
	"errors"
	"io"
	"mime"
	"net/http"
	"net/url"
	"os/exec"
	"path"
	"regexp"
	"sync"

	"github.com/tdewolff/buffer"
	"github.com/tdewolff/parse"
)

// ErrNotExist is returned when no minifier exists for a given mimetype.
var ErrNotExist = errors.New("minifier does not exist for mimetype")

////////////////////////////////////////////////////////////////

// MinifierFunc is a function that implements Minifer.
type MinifierFunc func(*M, io.Writer, io.Reader, map[string]string) error

// Minify calls f(m, w, r, params)
func (f MinifierFunc) Minify(m *M, w io.Writer, r io.Reader, params map[string]string) error {
	return f(m, w, r, params)
}

// Minifier is the interface for minifiers.
// The *M parameter is used for minifying embedded resources, such as JS within HTML.
type Minifier interface {
	Minify(*M, io.Writer, io.Reader, map[string]string) error
}

////////////////////////////////////////////////////////////////

type patternMinifier struct {
	pattern *regexp.Regexp
	Minifier
}

type cmdMinifier struct {
	cmd *exec.Cmd
}

func (c *cmdMinifier) Minify(_ *M, w io.Writer, r io.Reader, _ map[string]string) error {
	cmd := &exec.Cmd{}
	*cmd = *c.cmd // concurrency safety
	cmd.Stdout = w
	cmd.Stdin = r
	return cmd.Run()
}

////////////////////////////////////////////////////////////////

// M holds a map of mimetype => function to allow recursive minifier calls of the minifier functions.
type M struct {
	literal map[string]Minifier
	pattern []patternMinifier

	URL *url.URL
}

// New returns a new M.
func New() *M {
	return &M{
		map[string]Minifier{},
		[]patternMinifier{},
		nil,
	}
}

// Add adds a minifier to the mimetype => function map (unsafe for concurrent use).
func (m *M) Add(mimetype string, minifier Minifier) {
	m.literal[mimetype] = minifier
}

// AddFunc adds a minify function to the mimetype => function map (unsafe for concurrent use).
func (m *M) AddFunc(mimetype string, minifier MinifierFunc) {
	m.literal[mimetype] = minifier
}

// AddRegexp adds a minifier to the mimetype => function map (unsafe for concurrent use).
func (m *M) AddRegexp(pattern *regexp.Regexp, minifier Minifier) {
	m.pattern = append(m.pattern, patternMinifier{pattern, minifier})
}

// AddFuncRegexp adds a minify function to the mimetype => function map (unsafe for concurrent use).
func (m *M) AddFuncRegexp(pattern *regexp.Regexp, minifier MinifierFunc) {
	m.pattern = append(m.pattern, patternMinifier{pattern, minifier})
}

// AddCmd adds a minify function to the mimetype => function map (unsafe for concurrent use) that executes a command to process the minification.
// It allows the use of external tools like ClosureCompiler, UglifyCSS, etc. for a specific mimetype.
func (m *M) AddCmd(mimetype string, cmd *exec.Cmd) {
	m.literal[mimetype] = &cmdMinifier{cmd}
}

// AddCmdRegexp adds a minify function to the mimetype => function map (unsafe for concurrent use) that executes a command to process the minification.
// It allows the use of external tools like ClosureCompiler, UglifyCSS, etc. for a specific mimetype regular expression.
func (m *M) AddCmdRegexp(pattern *regexp.Regexp, cmd *exec.Cmd) {
	m.pattern = append(m.pattern, patternMinifier{pattern, &cmdMinifier{cmd}})
}

// Match returns the pattern and minifier that gets matched with the mediatype.
// It returns nil when no matching minifier exists.
// It has the same matching algorithm as Minify.
func (m *M) Match(mediatype string) (string, map[string]string, MinifierFunc) {
	mimetype, params := parse.Mediatype([]byte(mediatype))
	if minifier, ok := m.literal[string(mimetype)]; ok { // string conversion is optimized away
		return string(mimetype), params, minifier.Minify
	}

	for _, minifier := range m.pattern {
		if minifier.pattern.Match(mimetype) {
			return minifier.pattern.String(), params, minifier.Minify
		}
	}
	return string(mimetype), params, nil
}

// Minify minifies the content of a Reader and writes it to a Writer (safe for concurrent use).
// An error is returned when no such mimetype exists (ErrNotExist) or when an error occurred in the minifier function.
// Mediatype may take the form of 'text/plain', 'text/*', '*/*' or 'text/plain; charset=UTF-8; version=2.0'.
func (m *M) Minify(mediatype string, w io.Writer, r io.Reader) error {
	mimetype, params := parse.Mediatype([]byte(mediatype))
	return m.MinifyMimetype(mimetype, w, r, params)
}

// MinifyMimetype minifies the content of a Reader and writes it to a Writer (safe for concurrent use).
// It is a lower level version of Minify and requires the mediatype to be split up into mimetype and parameters.
// It is mostly used internally by minifiers because it is faster (no need to convert a byte-slice to string and vice versa).
func (m *M) MinifyMimetype(mimetype []byte, w io.Writer, r io.Reader, params map[string]string) error {
	err := ErrNotExist
	if minifier, ok := m.literal[string(mimetype)]; ok { // string conversion is optimized away
		err = minifier.Minify(m, w, r, params)
	} else {
		for _, minifier := range m.pattern {
			if minifier.pattern.Match(mimetype) {
				err = minifier.Minify(m, w, r, params)
				break
			}
		}
	}
	return err
}

// Bytes minifies an array of bytes (safe for concurrent use). When an error occurs it return the original array and the error.
// It returns an error when no such mimetype exists (ErrNotExist) or any error occurred in the minifier function.
func (m *M) Bytes(mediatype string, v []byte) ([]byte, error) {
	out := buffer.NewWriter(make([]byte, 0, len(v)))
	if err := m.Minify(mediatype, out, buffer.NewReader(v)); err != nil {
		return v, err
	}
	return out.Bytes(), nil
}

// String minifies a string (safe for concurrent use). When an error occurs it return the original string and the error.
// It returns an error when no such mimetype exists (ErrNotExist) or any error occurred in the minifier function.
func (m *M) String(mediatype string, v string) (string, error) {
	out := buffer.NewWriter(make([]byte, 0, len(v)))
	if err := m.Minify(mediatype, out, buffer.NewReader([]byte(v))); err != nil {
		return v, err
	}
	return string(out.Bytes()), nil
}

// Reader wraps a Reader interface and minifies the stream.
// Errors from the minifier are returned by the reader.
func (m *M) Reader(mediatype string, r io.Reader) io.Reader {
	pr, pw := io.Pipe()
	go func() {
		if err := m.Minify(mediatype, pw, r); err != nil {
			pw.CloseWithError(err)
		} else {
			pw.Close()
		}
	}()
	return pr
}

// minifyWriter makes sure that errors from the minifier are passed down through Close (can be blocking).
type minifyWriter struct {
	pw  *io.PipeWriter
	wg  sync.WaitGroup
	err error
}

// Write intercepts any writes to the writer.
func (w *minifyWriter) Write(b []byte) (int, error) {
	return w.pw.Write(b)
}

// Close must be called when writing has finished. It returns the error from the minifier.
func (w *minifyWriter) Close() error {
	w.pw.Close()
	w.wg.Wait()
	return w.err
}

// Writer wraps a Writer interface and minifies the stream.
// Errors from the minifier are returned by Close on the writer.
// The writer must be closed explicitly.
func (m *M) Writer(mediatype string, w io.Writer) *minifyWriter {
	pr, pw := io.Pipe()
	mw := &minifyWriter{pw, sync.WaitGroup{}, nil}
	mw.wg.Add(1)
	go func() {
		defer mw.wg.Done()

		if err := m.Minify(mediatype, w, pr); err != nil {
			io.Copy(w, pr)
			mw.err = err
		}
		pr.Close()
	}()
	return mw
}

// minifyResponseWriter wraps an http.ResponseWriter and makes sure that errors from the minifier are passed down through Close (can be blocking).
// All writes to the response writer are intercepted and minified on the fly.
// http.ResponseWriter loses all functionality such as Pusher, Hijacker, Flusher, ...
type minifyResponseWriter struct {
	http.ResponseWriter

	writer    *minifyWriter
	m         *M
	mediatype string
}

// WriteHeader intercepts any header writes and removes the Content-Length header.
func (w *minifyResponseWriter) WriteHeader(status int) {
	w.ResponseWriter.Header().Del("Content-Length")
	w.ResponseWriter.WriteHeader(status)
}

// Write intercepts any writes to the response writer.
// The first write will extract the Content-Type as the mediatype. Otherwise it falls back to the RequestURI extension.
func (w *minifyResponseWriter) Write(b []byte) (int, error) {
	if w.writer == nil {
		// first write
		if mediatype := w.ResponseWriter.Header().Get("Content-Type"); mediatype != "" {
			w.mediatype = mediatype
		}
		w.writer = w.m.Writer(w.mediatype, w.ResponseWriter)
	}
	return w.writer.Write(b)
}

// Close must be called when writing has finished. It returns the error from the minifier.
func (w *minifyResponseWriter) Close() error {
	if w.writer != nil {
		return w.writer.Close()
	}
	return nil
}

// ResponseWriter minifies any writes to the http.ResponseWriter.
// http.ResponseWriter loses all functionality such as Pusher, Hijacker, Flusher, ...
// Minification might be slower than just sending the original file! Caching is advised.
func (m *M) ResponseWriter(w http.ResponseWriter, r *http.Request) *minifyResponseWriter {
	mediatype := mime.TypeByExtension(path.Ext(r.RequestURI))
	return &minifyResponseWriter{w, nil, m, mediatype}
}

// Middleware provides a middleware function that minifies content on the fly by intercepting writes to http.ResponseWriter.
// http.ResponseWriter loses all functionality such as Pusher, Hijacker, Flusher, ...
// Minification might be slower than just sending the original file! Caching is advised.
func (m *M) Middleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		mw := m.ResponseWriter(w, r)
		defer mw.Close()

		next.ServeHTTP(mw, r)
	})
}
<commit_msg>Change buffer to parse/buffer<commit_after>// Package minify relates MIME type to minifiers. Several minifiers are provided in the subpackages.
package minify // import "github.com/tdewolff/minify"

import (
	"errors"
	"io"
	"mime"
	"net/http"
	"net/url"
	"os/exec"
	"path"
	"regexp"
	"sync"

	"github.com/tdewolff/parse"
	"github.com/tdewolff/parse/buffer"
)

// ErrNotExist is returned when no minifier exists for a given mimetype.
var ErrNotExist = errors.New("minifier does not exist for mimetype")

////////////////////////////////////////////////////////////////

// MinifierFunc is a function that implements Minifer.
type MinifierFunc func(*M, io.Writer, io.Reader, map[string]string) error

// Minify calls f(m, w, r, params)
func (f MinifierFunc) Minify(m *M, w io.Writer, r io.Reader, params map[string]string) error {
	return f(m, w, r, params)
}

// Minifier is the interface for minifiers.
// The *M parameter is used for minifying embedded resources, such as JS within HTML.
type Minifier interface {
	Minify(*M, io.Writer, io.Reader, map[string]string) error
}

////////////////////////////////////////////////////////////////

type patternMinifier struct {
	pattern *regexp.Regexp
	Minifier
}

type cmdMinifier struct {
	cmd *exec.Cmd
}

func (c *cmdMinifier) Minify(_ *M, w io.Writer, r io.Reader, _ map[string]string) error {
	cmd := &exec.Cmd{}
	*cmd = *c.cmd // concurrency safety
	cmd.Stdout = w
	cmd.Stdin = r
	return cmd.Run()
}

////////////////////////////////////////////////////////////////

// M holds a map of mimetype => function to allow recursive minifier calls of the minifier functions.
type M struct {
	literal map[string]Minifier
	pattern []patternMinifier

	URL *url.URL
}

// New returns a new M.
func New() *M {
	return &M{
		map[string]Minifier{},
		[]patternMinifier{},
		nil,
	}
}

// Add adds a minifier to the mimetype => function map (unsafe for concurrent use).
func (m *M) Add(mimetype string, minifier Minifier) {
	m.literal[mimetype] = minifier
}

// AddFunc adds a minify function to the mimetype => function map (unsafe for concurrent use).
func (m *M) AddFunc(mimetype string, minifier MinifierFunc) {
	m.literal[mimetype] = minifier
}

// AddRegexp adds a minifier to the mimetype => function map (unsafe for concurrent use).
func (m *M) AddRegexp(pattern *regexp.Regexp, minifier Minifier) {
	m.pattern = append(m.pattern, patternMinifier{pattern, minifier})
}

// AddFuncRegexp adds a minify function to the mimetype => function map (unsafe for concurrent use).
func (m *M) AddFuncRegexp(pattern *regexp.Regexp, minifier MinifierFunc) {
	m.pattern = append(m.pattern, patternMinifier{pattern, minifier})
}

// AddCmd adds a minify function to the mimetype => function map (unsafe for concurrent use) that executes a command to process the minification.
// It allows the use of external tools like ClosureCompiler, UglifyCSS, etc. for a specific mimetype.
func (m *M) AddCmd(mimetype string, cmd *exec.Cmd) {
	m.literal[mimetype] = &cmdMinifier{cmd}
}

// AddCmdRegexp adds a minify function to the mimetype => function map (unsafe for concurrent use) that executes a command to process the minification.
// It allows the use of external tools like ClosureCompiler, UglifyCSS, etc. for a specific mimetype regular expression.
func (m *M) AddCmdRegexp(pattern *regexp.Regexp, cmd *exec.Cmd) {
	m.pattern = append(m.pattern, patternMinifier{pattern, &cmdMinifier{cmd}})
}

// Match returns the pattern and minifier that gets matched with the mediatype.
// It returns nil when no matching minifier exists.
// It has the same matching algorithm as Minify.
func (m *M) Match(mediatype string) (string, map[string]string, MinifierFunc) {
	mimetype, params := parse.Mediatype([]byte(mediatype))
	if minifier, ok := m.literal[string(mimetype)]; ok { // string conversion is optimized away
		return string(mimetype), params, minifier.Minify
	}

	for _, minifier := range m.pattern {
		if minifier.pattern.Match(mimetype) {
			return minifier.pattern.String(), params, minifier.Minify
		}
	}
	return string(mimetype), params, nil
}

// Minify minifies the content of a Reader and writes it to a Writer (safe for concurrent use).
// An error is returned when no such mimetype exists (ErrNotExist) or when an error occurred in the minifier function.
// Mediatype may take the form of 'text/plain', 'text/*', '*/*' or 'text/plain; charset=UTF-8; version=2.0'.
func (m *M) Minify(mediatype string, w io.Writer, r io.Reader) error {
	mimetype, params := parse.Mediatype([]byte(mediatype))
	return m.MinifyMimetype(mimetype, w, r, params)
}

// MinifyMimetype minifies the content of a Reader and writes it to a Writer (safe for concurrent use).
// It is a lower level version of Minify and requires the mediatype to be split up into mimetype and parameters.
// It is mostly used internally by minifiers because it is faster (no need to convert a byte-slice to string and vice versa).
func (m *M) MinifyMimetype(mimetype []byte, w io.Writer, r io.Reader, params map[string]string) error {
	err := ErrNotExist
	if minifier, ok := m.literal[string(mimetype)]; ok { // string conversion is optimized away
		err = minifier.Minify(m, w, r, params)
	} else {
		for _, minifier := range m.pattern {
			if minifier.pattern.Match(mimetype) {
				err = minifier.Minify(m, w, r, params)
				break
			}
		}
	}
	return err
}

// Bytes minifies an array of bytes (safe for concurrent use). When an error occurs it return the original array and the error.
// It returns an error when no such mimetype exists (ErrNotExist) or any error occurred in the minifier function.
func (m *M) Bytes(mediatype string, v []byte) ([]byte, error) {
	out := buffer.NewWriter(make([]byte, 0, len(v)))
	if err := m.Minify(mediatype, out, buffer.NewReader(v)); err != nil {
		return v, err
	}
	return out.Bytes(), nil
}

// String minifies a string (safe for concurrent use). When an error occurs it return the original string and the error.
// It returns an error when no such mimetype exists (ErrNotExist) or any error occurred in the minifier function.
func (m *M) String(mediatype string, v string) (string, error) {
	out := buffer.NewWriter(make([]byte, 0, len(v)))
	if err := m.Minify(mediatype, out, buffer.NewReader([]byte(v))); err != nil {
		return v, err
	}
	return string(out.Bytes()), nil
}

// Reader wraps a Reader interface and minifies the stream.
// Errors from the minifier are returned by the reader.
func (m *M) Reader(mediatype string, r io.Reader) io.Reader {
	pr, pw := io.Pipe()
	go func() {
		if err := m.Minify(mediatype, pw, r); err != nil {
			pw.CloseWithError(err)
		} else {
			pw.Close()
		}
	}()
	return pr
}

// minifyWriter makes sure that errors from the minifier are passed down through Close (can be blocking).
type minifyWriter struct {
	pw  *io.PipeWriter
	wg  sync.WaitGroup
	err error
}

// Write intercepts any writes to the writer.
func (w *minifyWriter) Write(b []byte) (int, error) {
	return w.pw.Write(b)
}

// Close must be called when writing has finished. It returns the error from the minifier.
func (w *minifyWriter) Close() error {
	w.pw.Close()
	w.wg.Wait()
	return w.err
}

// Writer wraps a Writer interface and minifies the stream.
// Errors from the minifier are returned by Close on the writer.
// The writer must be closed explicitly.
func (m *M) Writer(mediatype string, w io.Writer) *minifyWriter {
	pr, pw := io.Pipe()
	mw := &minifyWriter{pw, sync.WaitGroup{}, nil}
	mw.wg.Add(1)
	go func() {
		defer mw.wg.Done()

		if err := m.Minify(mediatype, w, pr); err != nil {
			io.Copy(w, pr)
			mw.err = err
		}
		pr.Close()
	}()
	return mw
}

// minifyResponseWriter wraps an http.ResponseWriter and makes sure that errors from the minifier are passed down through Close (can be blocking).
// All writes to the response writer are intercepted and minified on the fly.
// http.ResponseWriter loses all functionality such as Pusher, Hijacker, Flusher, ...
type minifyResponseWriter struct {
	http.ResponseWriter

	writer    *minifyWriter
	m         *M
	mediatype string
}

// WriteHeader intercepts any header writes and removes the Content-Length header.
func (w *minifyResponseWriter) WriteHeader(status int) {
	w.ResponseWriter.Header().Del("Content-Length")
	w.ResponseWriter.WriteHeader(status)
}

// Write intercepts any writes to the response writer.
// The first write will extract the Content-Type as the mediatype. Otherwise it falls back to the RequestURI extension.
func (w *minifyResponseWriter) Write(b []byte) (int, error) {
	if w.writer == nil {
		// first write
		if mediatype := w.ResponseWriter.Header().Get("Content-Type"); mediatype != "" {
			w.mediatype = mediatype
		}
		w.writer = w.m.Writer(w.mediatype, w.ResponseWriter)
	}
	return w.writer.Write(b)
}

// Close must be called when writing has finished. It returns the error from the minifier.
func (w *minifyResponseWriter) Close() error {
	if w.writer != nil {
		return w.writer.Close()
	}
	return nil
}

// ResponseWriter minifies any writes to the http.ResponseWriter.
// http.ResponseWriter loses all functionality such as Pusher, Hijacker, Flusher, ...
// Minification might be slower than just sending the original file! Caching is advised.
func (m *M) ResponseWriter(w http.ResponseWriter, r *http.Request) *minifyResponseWriter {
	mediatype := mime.TypeByExtension(path.Ext(r.RequestURI))
	return &minifyResponseWriter{w, nil, m, mediatype}
}

// Middleware provides a middleware function that minifies content on the fly by intercepting writes to http.ResponseWriter.
// http.ResponseWriter loses all functionality such as Pusher, Hijacker, Flusher, ...
// Minification might be slower than just sending the original file! Caching is advised.
func (m *M) Middleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		mw := m.ResponseWriter(w, r)
		defer mw.Close()

		next.ServeHTTP(mw, r)
	})
}
<|endoftext|>


<commit_before>package annotation

import (
	"bufio"
	"bytes"
	"compress/bzip2"
	"compress/gzip"
	"encoding/csv"
	"fmt"
	"io"
	"log"
	"strconv"
	"unsafe"

	"github.com/dhconnelly/rtreego"
	"github.com/guigolab/bamstats/utils"
)

const (
	peekLen = 4096
)

// FeatureReader is a struct for readinf features
type FeatureReader struct {
	r            *bufio.Reader
	format       Format
	exons, genes [3]*Feature
	line         int
	chrLens      map[string]int
}

// NewFeatureReader returns a new instance of FeatureReader
func NewFeatureReader(r io.Reader, chrs map[string]int) *FeatureReader {
	br := buffReader(r)
	format := scanFormat(br, peekLen)
	return &FeatureReader{
		r:       br,
		format:  format,
		chrLens: chrs,
	}
}

// CheckBytes peeks at a buffered stream and checks if the first read bytes match.
func CheckBytes(b *bufio.Reader, buf []byte) (bool, error) {
	m, err := b.Peek(len(buf))
	if err != nil {
		return false, err
	}
	for i := range buf {
		if m[i] != buf[i] {
			return false, nil
		}
	}
	return true, nil
}

// IsGzip returns true buffered Reader has the gzip magic
func isGzip(b *bufio.Reader) (bool, error) {
	return CheckBytes(b, []byte{0x1f, 0x8b})
}

// IsGzip returns true buffered Reader has the gzip magic
func isBzip2(b *bufio.Reader) (bool, error) {
	return CheckBytes(b, []byte{0x42, 0x5a})
}

func buffReader(r io.Reader) *bufio.Reader {

	br := bufio.NewReader(r)
	if isGz, err := isGzip(br); err != nil {
		log.Fatal(err)
	} else if isGz {
		rdr, err := gzip.NewReader(br)
		utils.Check(err)
		br = bufio.NewReader(rdr)
	} else if isBz, err := isBzip2(br); err != nil {
		log.Fatal(err)
	} else if isBz {
		rdr := bzip2.NewReader(br)
		br = bufio.NewReader(rdr)
	}

	return br
}

func isTab(r rune) bool {
	return r == '\t'
}

func isNewLine(r rune) bool {
	return r == '\n'
}

func scanFormat(r *bufio.Reader, n int) (format Format) {
	b, err := r.Peek(n)
	if err != nil {
		if err != io.EOF {
			panic(err)
		}
	}
	lines := bytes.FieldsFunc(b, isNewLine)
scan:
	for i, line := range lines {
		if line[0] == '#' {
			continue
		}
		if i == len(lines)-1 && !isNewLine(rune(line[len(line)-1])) {
			log.Fatal("Cannot guess type. Try increasing the peek buffer.")
		}
		switch c := bytes.Count(line, []byte{'\t'}); c + 1 {
		case 4:
			format = BED
			break scan
		case 9:
			format = GTF
			break scan
		default:
			format = UNDEF
		}
	}
	return
}

// This function cannot be used to create strings that are expected to persist.
func unsafeString(b []byte) string {
	return *(*string)(unsafe.Pointer(&b))
}

func (r *FeatureReader) Read() (f *Feature, err error) {
	switch r.format {
	case BED:
		f, err = readBed(r)
	case GTF:
		f, err = readGtf(r)
	default:
		err = fmt.Errorf("FeatureReader, %s format error", r.format)
	}
	return
}

func skip(line []byte) bool {
	if len(line) == 0 {
		return true
	}
	if bytes.HasPrefix(line, []byte{'#'}) {
		return true
	}
	return false
}

func parseInterval(b, e []byte) (begin, end float64) {
	var err error
	begin, err = strconv.ParseFloat(unsafeString(b), 64)
	if err != nil {
		return -1, -1
	}
	end, err = strconv.ParseFloat(unsafeString(e), 64)
	if err != nil {
		return -1, -1
	}
	return
}

func parseFeature(chr, element []byte, begin, end float64) (*Feature, error) {
	loc := rtreego.Point{begin}
	size := end - begin
	rect, err := rtreego.NewRect(loc, []float64{size})
	if err != nil {
		return nil, err
	}
	return NewFeature(chr, element, rect), nil
}

func readBed(r *FeatureReader) (f *Feature, err error) {
	var line []byte
	for {
		line, err = r.r.ReadBytes('\n')
		//r.line++
		if err != nil {
			if err == io.EOF {
				return f, err
			}
			return nil, &csv.ParseError{Err: err}
		}
		line = bytes.TrimSpace(line)
		if skip(line) { // ignore blank lines and comment lines
			continue
		} else {
			break
		}
	}
	fields := bytes.Split(line, []byte{'\t'})
	chr := fields[0]
	start := fields[1]
	end := fields[2]
	element := fields[3]

	s, e := parseInterval(start, end)

	return parseFeature(chr, element, s, e)
}

func readGtf(r *FeatureReader) (f *Feature, err error) {
	var line []byte
	var fields [][]byte
	var element []byte
	for {
		line, err = r.r.ReadBytes('\n')
		//r.line++
		if err != nil {
			if err == io.EOF {
					break
				}
			return nil, &csv.ParseError{Err: err}
		}
		line = bytes.TrimSpace(line)
		if skip(line) { // ignore blank lines and comment lines
			continue
		} else {
			fields = bytes.Split(line, []byte{'\t'})
			elem := string(fields[2])
			if elem != "gene" && elem != "exon" {
				continue
			}
			element = fields[2]
			chr := fields[0]
			start := fields[3]
			end := fields[4]
			if _, ok := r.chrLens[string(chr)]; !ok {
				continue
			}
			if bytes.Equal(element, []byte("gene")) {
				s, e := parseInterval(start, end)
				f, err = parseFeature(chr, element, s-1, e)
				if r.genes[1] == nil {
					r.genes[1] = f
				} else {
					if f.Chr() != r.genes[1].Chr() {
						tmp := f
						f, err = selectFeature(r.genes[1], &r.genes, []byte("intergenic"), true, r.chrLens)
						r.genes[1] = tmp
						break
					}
					if f.Start() < r.genes[1].Start() {
						logrus.Fatal("Annotation is not sorted")
					}
					if f.Start() <= r.genes[1].End() {
						r.genes[1], err = parseFeature(chr, element, r.genes[1].Start(), float64(utils.Max(int(f.End()), int(r.genes[1].End()))))
					} else {
						f, err = selectFeature(f, &r.genes, []byte("intergenic"), true, r.chrLens)
						break
					}
				}
			} else if bytes.Equal(element, []byte("exon")) {
				s, e := parseInterval(start, end)
				f, err = parseFeature(chr, element, s-1, e)
				if r.exons[1] == nil {
					r.exons[1] = f
				} else {
					if f.Chr() != r.exons[1].Chr() {
						tmp := f
						f, err = selectFeature(r.exons[1], &r.exons, []byte("intron"), false, r.chrLens)
						if f.Element() == "intron" {
							if (r.genes[0] != nil && r.genes[1] != nil && f.Start() == r.genes[0].End() || f.End() == r.genes[1].Start()) || (r.genes[0] != nil && f.End() == r.genes[0].Start()) {
								f = nil
							}
						}
						r.exons[1] = tmp
						break
					}
					if f.Start() < r.exons[1].Start() {
						logrus.Fatal("Annotation is not sorted")
					}
					if f.Start() <= r.exons[1].End() {
						r.exons[1], err = parseFeature(chr, element, r.exons[1].Start(), float64(utils.Max(int(f.End()), int(r.exons[1].End()))))
					} else {
						f, err = selectFeature(f, &r.exons, []byte("intron"), false, r.chrLens)
						if f.Element() == "intron" {
							if (r.genes[0] != nil && r.genes[1] != nil && f.Start() == r.genes[0].End() || f.End() == r.genes[1].Start()) || (r.genes[0] != nil && f.End() == r.genes[0].Start()) {
								f = nil
							}
						}
						break
					}
				}
			}
		}
	}
	return
}

func mergeIntervals(intervals []*Feature) []*Feature {
	sort.Sort(FeatureSlice(intervals))
	out := make([]*Feature, 0)
	x, intervals := intervals[0], intervals[1:]
	for n, i := range intervals {
		if i.Start() <= x.End() {
			loc := rtreego.Point{x.Start()}
			size := float64(utils.Max(int(i.End()), int(x.End()))) - x.Start()
			rect, err := rtreego.NewRect(loc, []float64{size})
			if err != nil {
				log.Panic(err)
			}
			x.SetBounds(rect)
		} else {
			out = append(out, x)
			x = i
		}
		if n == len(intervals)-1 {
			out = append(out, x)
		}
	}
	return out
}
<commit_msg>Fix unupdated annotation reader.go<commit_after>package annotation

import (
	"bufio"
	"bytes"
	"compress/bzip2"
	"compress/gzip"
	"encoding/csv"
	"fmt"
	"io"
	"log"
	"strconv"
	"unsafe"

	"github.com/dhconnelly/rtreego"
	"github.com/guigolab/bamstats/utils"
)

const (
	peekLen = 4096
)

// FeatureReader is a struct for readinf features
type FeatureReader struct {
	r            *bufio.Reader
	format       Format
	exons, genes [3]*Feature
	line         int
	chrLens      map[string]int
}

// NewFeatureReader returns a new instance of FeatureReader
func NewFeatureReader(r io.Reader, chrs map[string]int) *FeatureReader {
	br := buffReader(r)
	format := scanFormat(br, peekLen)
	return &FeatureReader{
		r:       br,
		format:  format,
		chrLens: chrs,
	}
}

// CheckBytes peeks at a buffered stream and checks if the first read bytes match.
func CheckBytes(b *bufio.Reader, buf []byte) (bool, error) {
	m, err := b.Peek(len(buf))
	if err != nil {
		return false, err
	}
	for i := range buf {
		if m[i] != buf[i] {
			return false, nil
		}
	}
	return true, nil
}

// IsGzip returns true buffered Reader has the gzip magic
func isGzip(b *bufio.Reader) (bool, error) {
	return CheckBytes(b, []byte{0x1f, 0x8b})
}

// IsGzip returns true buffered Reader has the gzip magic
func isBzip2(b *bufio.Reader) (bool, error) {
	return CheckBytes(b, []byte{0x42, 0x5a})
}

func buffReader(r io.Reader) *bufio.Reader {

	br := bufio.NewReader(r)
	if isGz, err := isGzip(br); err != nil {
		log.Fatal(err)
	} else if isGz {
		rdr, err := gzip.NewReader(br)
		utils.Check(err)
		br = bufio.NewReader(rdr)
	} else if isBz, err := isBzip2(br); err != nil {
		log.Fatal(err)
	} else if isBz {
		rdr := bzip2.NewReader(br)
		br = bufio.NewReader(rdr)
	}

	return br
}

func isTab(r rune) bool {
	return r == '\t'
}

func isNewLine(r rune) bool {
	return r == '\n'
}

func scanFormat(r *bufio.Reader, n int) (format Format) {
	b, err := r.Peek(n)
	if err != nil {
		if err != io.EOF {
			panic(err)
		}
	}
	lines := bytes.FieldsFunc(b, isNewLine)
scan:
	for i, line := range lines {
		if line[0] == '#' {
			continue
		}
		if i == len(lines)-1 && !isNewLine(rune(line[len(line)-1])) {
			log.Fatal("Cannot guess type. Try increasing the peek buffer.")
		}
		switch c := bytes.Count(line, []byte{'\t'}); c + 1 {
		case 4:
			format = BED
			break scan
		case 9:
			format = GTF
			break scan
		default:
			format = UNDEF
		}
	}
	return
}

// This function cannot be used to create strings that are expected to persist.
func unsafeString(b []byte) string {
	return *(*string)(unsafe.Pointer(&b))
}

func (r *FeatureReader) Read() (f *Feature, err error) {
	switch r.format {
	case BED:
		f, err = readBed(r)
	case GTF:
		f, err = readGtf(r)
	default:
		err = fmt.Errorf("FeatureReader, %s format error", r.format)
	}
	return
}

func skip(line []byte) bool {
	if len(line) == 0 {
		return true
	}
	if bytes.HasPrefix(line, []byte{'#'}) {
		return true
	}
	return false
}

func parseInterval(b, e []byte) (begin, end float64) {
	var err error
	begin, err = strconv.ParseFloat(unsafeString(b), 64)
	if err != nil {
		return -1, -1
	}
	end, err = strconv.ParseFloat(unsafeString(e), 64)
	if err != nil {
		return -1, -1
	}
	return
}

func parseFeature(chr, element []byte, begin, end float64) (*Feature, error) {
	loc := rtreego.Point{begin}
	size := end - begin
	rect, err := rtreego.NewRect(loc, []float64{size})
	if err != nil {
		return nil, err
	}
	return NewFeature(chr, element, rect), nil
}

func readBed(r *FeatureReader) (f *Feature, err error) {
	var line []byte
	for {
		line, err = r.r.ReadBytes('\n')
		//r.line++
		if err != nil {
			if err == io.EOF {
				return f, err
			}
			return nil, &csv.ParseError{Err: err}
		}
		line = bytes.TrimSpace(line)
		if skip(line) { // ignore blank lines and comment lines
			continue
		} else {
			break
		}
	}
	fields := bytes.Split(line, []byte{'\t'})
	chr := fields[0]
	start := fields[1]
	end := fields[2]
	element := fields[3]

	s, e := parseInterval(start, end)

	return parseFeature(chr, element, s, e)
}

func readGtf(r *FeatureReader) (f *Feature, err error) {
	var line []byte
	var fields [][]byte
	var element []byte
	for {
		line, err = r.r.ReadBytes('\n')
		//r.line++
		if err != nil {
			if err == io.EOF {
				break
			}
			return nil, &csv.ParseError{Err: err}
		}
		line = bytes.TrimSpace(line)
		if skip(line) { // ignore blank lines and comment lines
			continue
		} else {
			fields = bytes.Split(line, []byte{'\t'})
			elem := string(fields[2])
			if elem != "gene" && elem != "exon" {
				continue
			}
			element = fields[2]
			chr := fields[0]
			start := fields[3]
			end := fields[4]
			if _, ok := r.chrLens[string(chr)]; !ok {
				continue
			}
			s, e := parseInterval(start, end)
			f, err = parseFeature(chr, element, s-1, e)
			break
		}
	}
	return
}
<|endoftext|>


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

package vector

//export Vector, New;

/*
	import vector "vector"
	v := vector.New();
	v.Insert(0, new(Foo));
	v.Append(new(Foo));
	v.Remove(0);
	for i := 0; i < v.Len(); i++ { f(v.At(i)); }
*/

type Element interface {
}

export type Vector struct {
	elem *[]Element;
}

export func New() *Vector {
	v := new(Vector);
	v.elem = new([]Element, 8) [0 : 0];  // capacity must be > 0!
	return v;
}

func (v *Vector) Len() int {
	return len(v.elem);
}

func (v *Vector) At(i int) Element {
	// range check unnecessary - done by runtime
	return v.elem[i];
}

func (v *Vector) Remove(i int) Element {
	ret := v.elem[i];
	n := v.Len();
	// range check unnecessary - done by runtime
	for j := i + 1; j < n; j++ {
		v.elem[j - 1] = v.elem[j];
	}
	var e Element;
	v.elem[n - 1] = e;  // don't set to nil - may not be legal in the future
	v.elem = v.elem[0 : n - 1];
	return ret;
}

func (v *Vector) Reset() {
	v.elem = v.elem[0:0];
}

func (v *Vector) Insert(i int, e Element) {
	n := v.Len();
	// range check unnecessary - done by runtime

	// grow array by doubling its capacity
	if n == cap(v.elem) {
		a := new([]Element, n*2);
		for j := 0; j < n; j++ {
			a[j] = v.elem[j];
		}
		v.elem = a;
	}

	// make a hole
	v.elem = v.elem[0 : n + 1];
	for j := n; j > i; j-- {
		v.elem[j] = v.elem[j-1];
	}

	v.elem[i] = e;
}

func (v *Vector) Append(e Element) {
	v.Insert(len(v.elem), e);
}

/*
type I struct { val int; };  // BUG: can't be local;

func Test() {
	i0 := new(I); i0.val = 0;
	i1 := new(I); i1.val = 11;
	i2 := new(I); i2.val = 222;
	i3 := new(I); i3.val = 3333;
	i4 := new(I); i4.val = 44444;
	v := New();
	print("hi\n");
	v.Insert(0, i4);
	v.Insert(0, i3);
	v.Insert(0, i2);
	v.Insert(0, i1);
	v.Insert(0, i0);
	for i := 0; i < v.Len(); i++ {
		x := convert(*I, v.At(i));
		print(i, " ", v.At(i).(*I).val, "\n");
	}
}

export Test;
*/
<commit_msg>Added missing Set method for Vector<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package vector

//export Vector, New;

/*
	import vector "vector"
	v := vector.New();
	v.Insert(0, new(Foo));
	v.Append(new(Foo));
	v.Remove(0);
	for i := 0; i < v.Len(); i++ { f(v.At(i)); }
*/

type Element interface {
}

export type Vector struct {
	elem *[]Element;
}

export func New() *Vector {
	v := new(Vector);
	v.elem = new([]Element, 8) [0 : 0];  // capacity must be > 0!
	return v;
}

func (v *Vector) Len() int {
	return len(v.elem);
}

func (v *Vector) At(i int) Element {
	// range check unnecessary - done by runtime
	return v.elem[i];
}

func (v *Vector) Set(i int, e Element) {
	// range check unnecessary - done by runtime
	v.elem[i] = e;
}

func (v *Vector) Remove(i int) Element {
	ret := v.elem[i];
	n := v.Len();
	// range check unnecessary - done by runtime
	for j := i + 1; j < n; j++ {
		v.elem[j - 1] = v.elem[j];
	}
	var e Element;
	v.elem[n - 1] = e;  // don't set to nil - may not be legal in the future
	v.elem = v.elem[0 : n - 1];
	return ret;
}

func (v *Vector) Reset() {
	v.elem = v.elem[0:0];
}

func (v *Vector) Insert(i int, e Element) {
	n := v.Len();
	// range check unnecessary - done by runtime

	// grow array by doubling its capacity
	if n == cap(v.elem) {
		a := new([]Element, n*2);
		for j := 0; j < n; j++ {
			a[j] = v.elem[j];
		}
		v.elem = a;
	}

	// make a hole
	v.elem = v.elem[0 : n + 1];
	for j := n; j > i; j-- {
		v.elem[j] = v.elem[j-1];
	}

	v.elem[i] = e;
}

func (v *Vector) Append(e Element) {
	v.Insert(len(v.elem), e);
}

/*
type I struct { val int; };  // BUG: can't be local;

func Test() {
	i0 := new(I); i0.val = 0;
	i1 := new(I); i1.val = 11;
	i2 := new(I); i2.val = 222;
	i3 := new(I); i3.val = 3333;
	i4 := new(I); i4.val = 44444;
	v := New();
	print("hi\n");
	v.Insert(0, i4);
	v.Insert(0, i3);
	v.Insert(0, i2);
	v.Insert(0, i1);
	v.Insert(0, i0);
	for i := 0; i < v.Len(); i++ {
		x := convert(*I, v.At(i));
		print(i, " ", v.At(i).(*I).val, "\n");
	}
}

export Test;
*/
<|endoftext|>


<commit_before>// Package mixmux wraps HTTPRouter and HTTPTreeMux to provide consistent and
// idiomatic APIs, along with route grouping.  Multiplexer-based parameter
// handling is bypassed.
package mixmux

import (
	"net/http"

	"github.com/dimfeld/httptreemux"
	"github.com/julienschmidt/httprouter"
)

type Options struct {
	RedirectTrailingSlash  bool
	RedirectFixedPath      bool
	HandleMethodNotAllowed bool
	HandleOptions          bool
	NotFound               http.Handler
	MethodNotAllowed       http.Handler
}

// Router wraps HTTPRouter.
type Router struct {
	hr   *httprouter.Router
	path string
}

// NewRouter returns a wrapped HTTPRouter.
func NewRouter(opts *Options) *Router {
	r := &Router{
		path: "",
	}

	if opts == nil {
		r.hr = httprouter.New()

		return r
	}

	r.hr = &httprouter.Router{
		RedirectTrailingSlash:  opts.RedirectTrailingSlash,
		RedirectFixedPath:      opts.RedirectFixedPath,
		HandleMethodNotAllowed: opts.HandleMethodNotAllowed,
		HandleOPTIONS:          opts.HandleOptions,
		NotFound:               opts.NotFound,
		MethodNotAllowed:       opts.MethodNotAllowed,
	}

	return r
}

// Group takes a path and returns a new Router wrapping the original Router.
func (r *Router) Group(path string) *Router {
	return &Router{r.hr, r.path + path}
}

// Options takes a path and http.Handler and adds them to the mux.
func (r *Router) Options(path string, h http.Handler) {
	r.hr.Handler("OPTIONS", r.path+path, h)
}

// Get takes a path and http.Handler and adds them to the mux.
func (r *Router) Get(path string, h http.Handler) {
	r.hr.Handler("GET", r.path+path, h)
}

// Post takes a path and http.Handler and adds them to the mux.
func (r *Router) Post(path string, h http.Handler) {
	r.hr.Handler("POST", r.path+path, h)
}

// Put takes a path and http.Handler and adds them to the mux.
func (r *Router) Put(path string, h http.Handler) {
	r.hr.Handler("PUT", r.path+path, h)
}

// Patch takes a path and http.Handler and adds them to the mux.
func (r *Router) Patch(path string, h http.Handler) {
	r.hr.Handler("PATCH", r.path+path, h)
}

// Delete takes a path and http.Handler and adds them to the mux.
func (r *Router) Delete(path string, h http.Handler) {
	r.hr.Handler("DELETE", r.path+path, h)
}

// Head takes a path and http.Handler and adds them to the mux.
func (r *Router) Head(path string, h http.Handler) {
	r.hr.Handler("HEAD", r.path+path, h)
}

func (r *Router) Handle(method string, path string, h http.Handler) {
	r.hr.Handler(method, path, h)
}

func (mr *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	mr.hr.ServeHTTP(w, r)
}

// TreeMux wraps HTTPTreeMux.
type TreeMux struct {
	tm   *httptreemux.TreeMux
	path string
}

// NewTreeMux returns a wrapped HTTPTreeMux.
func NewTreeMux() *TreeMux {
	return &TreeMux{
		tm:   httptreemux.New(),
		path: "",
	}
}

// Group takes a path and returns a new TreeMux wrapping the original TreeMux.
func (tm *TreeMux) Group(path string) *TreeMux {
	return &TreeMux{tm.tm, tm.path + path}
}

// Options takes a path and http.Handler and adds them to the mux.
func (tm *TreeMux) Options(path string, h http.Handler) {
	tm.tm.Handle("OPTIONS", tm.path+path, treeMuxWrapper(h))
}

// Get takes a path and http.Handler and adds them to the mux.
func (tm *TreeMux) Get(path string, h http.Handler) {
	tm.tm.Handle("GET", tm.path+path, treeMuxWrapper(h))
}

// Post takes a path and http.Handler and adds them to the mux.
func (tm *TreeMux) Post(path string, h http.Handler) {
	tm.tm.Handle("POST", tm.path+path, treeMuxWrapper(h))
}

// Put takes a path and http.Handler and adds them to the mux.
func (tm *TreeMux) Put(path string, h http.Handler) {
	tm.tm.Handle("PUT", tm.path+path, treeMuxWrapper(h))
}

// Patch takes a path and http.Handler and adds them to the mux.
func (tm *TreeMux) Patch(path string, h http.Handler) {
	tm.tm.Handle("PATCH", tm.path+path, treeMuxWrapper(h))
}

// Delete takes a path and http.Handler and adds them to the mux.
func (tm *TreeMux) Delete(path string, h http.Handler) {
	tm.tm.Handle("DELETE", tm.path+path, treeMuxWrapper(h))
}

// Head takes a path and http.Handler and adds them to the mux.
func (tm *TreeMux) Head(path string, h http.Handler) {
	tm.tm.Handle("HEAD", tm.path+path, treeMuxWrapper(h))
}

func (tm *TreeMux) Handle(method string, path string, h http.Handler) {
	tm.tm.Handle(method, path, treeMuxWrapper(h))
}

func (tm *TreeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	tm.tm.ServeHTTP(w, r)
}

func treeMuxWrapper(next http.Handler) httptreemux.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request, _ map[string]string) {
		next.ServeHTTP(w, r)
	}
}
<commit_msg>Complete comments and fix method receiver ref.<commit_after>// Package mixmux wraps HTTPRouter and HTTPTreeMux to provide consistent and
// idiomatic APIs, along with route grouping.  Multiplexer-based parameter
// handling is bypassed.
package mixmux

import (
	"net/http"

	"github.com/dimfeld/httptreemux"
	"github.com/julienschmidt/httprouter"
)

// Options holds available options for a new Router.
type Options struct {
	RedirectTrailingSlash  bool
	RedirectFixedPath      bool
	HandleMethodNotAllowed bool
	HandleOptions          bool
	NotFound               http.Handler
	MethodNotAllowed       http.Handler
}

// Router wraps HTTPRouter.
type Router struct {
	hr   *httprouter.Router
	path string
}

// NewRouter returns a wrapped HTTPRouter.
func NewRouter(opts *Options) *Router {
	r := &Router{
		path: "",
	}

	if opts == nil {
		r.hr = httprouter.New()

		return r
	}

	r.hr = &httprouter.Router{
		RedirectTrailingSlash:  opts.RedirectTrailingSlash,
		RedirectFixedPath:      opts.RedirectFixedPath,
		HandleMethodNotAllowed: opts.HandleMethodNotAllowed,
		HandleOPTIONS:          opts.HandleOptions,
		NotFound:               opts.NotFound,
		MethodNotAllowed:       opts.MethodNotAllowed,
	}

	return r
}

// Group takes a path and returns a new Router wrapping the original Router.
func (r *Router) Group(path string) *Router {
	return &Router{r.hr, r.path + path}
}

// Options takes a path and http.Handler and adds them to the mux.
func (r *Router) Options(path string, h http.Handler) {
	r.hr.Handler("OPTIONS", r.path+path, h)
}

// Get takes a path and http.Handler and adds them to the mux.
func (r *Router) Get(path string, h http.Handler) {
	r.hr.Handler("GET", r.path+path, h)
}

// Post takes a path and http.Handler and adds them to the mux.
func (r *Router) Post(path string, h http.Handler) {
	r.hr.Handler("POST", r.path+path, h)
}

// Put takes a path and http.Handler and adds them to the mux.
func (r *Router) Put(path string, h http.Handler) {
	r.hr.Handler("PUT", r.path+path, h)
}

// Patch takes a path and http.Handler and adds them to the mux.
func (r *Router) Patch(path string, h http.Handler) {
	r.hr.Handler("PATCH", r.path+path, h)
}

// Delete takes a path and http.Handler and adds them to the mux.
func (r *Router) Delete(path string, h http.Handler) {
	r.hr.Handler("DELETE", r.path+path, h)
}

// Head takes a path and http.Handler and adds them to the mux.
func (r *Router) Head(path string, h http.Handler) {
	r.hr.Handler("HEAD", r.path+path, h)
}

// Handle receives an HTTP method, path, and http.Handler and adds them to
// the mux.
func (r *Router) Handle(method string, path string, h http.Handler) {
	r.hr.Handler(method, path, h)
}

// ServeHTTP satisfies the http.Handler interface.
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	r.hr.ServeHTTP(w, req)
}

// TreeMux wraps HTTPTreeMux.
type TreeMux struct {
	tm   *httptreemux.TreeMux
	path string
}

// NewTreeMux returns a wrapped HTTPTreeMux.
func NewTreeMux() *TreeMux {
	return &TreeMux{
		tm:   httptreemux.New(),
		path: "",
	}
}

// Group takes a path and returns a new TreeMux wrapping the original TreeMux.
func (tm *TreeMux) Group(path string) *TreeMux {
	return &TreeMux{tm.tm, tm.path + path}
}

// Options takes a path and http.Handler and adds them to the mux.
func (tm *TreeMux) Options(path string, h http.Handler) {
	tm.tm.Handle("OPTIONS", tm.path+path, treeMuxWrapper(h))
}

// Get takes a path and http.Handler and adds them to the mux.
func (tm *TreeMux) Get(path string, h http.Handler) {
	tm.tm.Handle("GET", tm.path+path, treeMuxWrapper(h))
}

// Post takes a path and http.Handler and adds them to the mux.
func (tm *TreeMux) Post(path string, h http.Handler) {
	tm.tm.Handle("POST", tm.path+path, treeMuxWrapper(h))
}

// Put takes a path and http.Handler and adds them to the mux.
func (tm *TreeMux) Put(path string, h http.Handler) {
	tm.tm.Handle("PUT", tm.path+path, treeMuxWrapper(h))
}

// Patch takes a path and http.Handler and adds them to the mux.
func (tm *TreeMux) Patch(path string, h http.Handler) {
	tm.tm.Handle("PATCH", tm.path+path, treeMuxWrapper(h))
}

// Delete takes a path and http.Handler and adds them to the mux.
func (tm *TreeMux) Delete(path string, h http.Handler) {
	tm.tm.Handle("DELETE", tm.path+path, treeMuxWrapper(h))
}

// Head takes a path and http.Handler and adds them to the mux.
func (tm *TreeMux) Head(path string, h http.Handler) {
	tm.tm.Handle("HEAD", tm.path+path, treeMuxWrapper(h))
}

// Handle receives an HTTP method, path, and http.Handler and adds them to
// the mux.
func (tm *TreeMux) Handle(method string, path string, h http.Handler) {
	tm.tm.Handle(method, path, treeMuxWrapper(h))
}

// ServeHTTP satisfies the http.Handler interface.
func (tm *TreeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	tm.tm.ServeHTTP(w, r)
}

func treeMuxWrapper(next http.Handler) httptreemux.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request, _ map[string]string) {
		next.ServeHTTP(w, r)
	}
}
<|endoftext|>


<commit_before>package similar

import (
	"bytes"
	"math"
)

// Get returns the string similarity from the functions.
// Predefined functions in this package use the scale from 0 to 1
// with higher value for more similar texts.
func Get(txt1, txt2 []byte, functions ...func([]byte, []byte) float64) float64 {
	rs := 0.0
	for _, f := range functions {
		rs += f(txt1, txt2)
	}
	return rs
}

// Cosine converts texts to vectors
// associatting each chracter with its frequncy
// and caculates cosine similarities.
// (https://en.wikipedia.org/wiki/Cosine_similarity)
func Cosine(txt1, txt2 []byte) float64 {
	vect1 := make(map[byte]int)
	for _, t := range txt1 {
		vect1[t]++
	}
	vect2 := make(map[byte]int)
	for _, t := range txt2 {
		vect2[t]++
	}
	//
	// dot-product two vectors
	// map[byte]int return 0 for non-existing key
	// and if two texts are equal, product will be highest
	// and if two texts are totally different, it will be 0
	//
	// to calculate A·B
	dotProduct := 0.0
	for k, v := range vect1 {
		dotProduct += float64(v) * float64(vect2[k])
	}
	// to calculate |A|*|B|
	sum1 := 0.0
	for _, v := range vect1 {
		sum1 += math.Pow(float64(v), 2)
	}
	sum2 := 0.0
	for _, v := range vect2 {
		sum2 += math.Pow(float64(v), 2)
	}
	magnitude := math.Sqrt(sum1) * math.Sqrt(sum2)
	if magnitude == 0 {
		return 0.0
	}
	return float64(dotProduct) / float64(magnitude)
}

// Hamming returns the normalized similarity value.
// hamming distance is the number of differing "bits".
// hamming distance is minimum number of substitutions
// required to change one string into the other
// (https://en.wikipedia.org/wiki/Hamming_distance)
func Hamming(txt1, txt2 []byte) float64 {
	switch bytes.Compare(txt1, txt2) {
	case 0: // txt1 == txt2
	case 1: // txt1 > txt2
		temp := make([]byte, len(txt1))
		copy(temp, txt2)
		txt2 = temp
	case -1: // txt1 < txt2
		temp := make([]byte, len(txt2))
		copy(temp, txt1)
		txt1 = temp
	}
	if len(txt1) != len(txt2) {
		panic("Undefined for sequences of unequal length")
	}
	count := 0
	for idx, b1 := range txt1 {
		b2 := txt2[idx]
		xor := b1 ^ b2 // 1 if bits are different
		//
		// bit count (number of 1)
		// http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetNaive
		//
		// repeat shifting from left to right (divide by 2)
		// until all bits are zero
		for x := xor; x > 0; x >>= 1 {
			// check if lowest bit is 1
			if int(x&1) == 1 {
				count++
			}
		}
	}
	if count == 0 {
		// similarity is 1 for equal texts.
		return 1
	}
	return float64(1) / float64(count)
}

// Levenshtein returns the similarity using levenshtein distance.
// (https://en.wikipedia.org/wiki/Levenshtein_distance)
func Levenshtein(txt1, txt2 []byte) float64 {
	// initialize the distance array, with position
	mat := create2Dslice(len(txt1)+1, len(txt2)+1)
	for i := 0; i < len(txt1)+1; i++ {
		mat[i][0] = i
	}
	for i := 0; i < len(txt2)+1; i++ {
		mat[0][i] = i
	}
	for i := 0; i < len(txt1); i++ {
		for j := 0; j < len(txt2); j++ {
			edit := 0
			if txt1[i] != txt2[j] {
				edit = 1
			}
			mat[i+1][j+1] = min(
				mat[i][j+1]+1,  // from txt1
				mat[i+1][j]+1,  // from txt2
				mat[i][j]+edit, // from both
			)
		}
	}
	distance := mat[len(txt1)][len(txt2)]
	if distance == 0 {
		// similarity is 1 for equal texts.
		return 1
	}
	return float64(1) / float64(distance)
}

func min(more ...int) int {
	min := more[0]
	for _, elem := range more {
		if min > elem {
			min = elem
		}
	}
	return min
}

func create2Dslice(row, column int) [][]int {
	mat := make([][]int, row)
	for i := range mat {
		mat[i] = make([]int, column)
	}
	return mat
}
<commit_msg>Update similar.go<commit_after>package similar

import (
	"bytes"
	"math"
)

// Get returns the string similarity from the functions.
// Predefined functions in this package use the scale from 0 to 1
// with higher value for more similar texts.
func Get(txt1, txt2 []byte, functions ...func([]byte, []byte) float64) float64 {
	rs := 0.0
	for _, f := range functions {
		rs += f(txt1, txt2)
	}
	return rs
}

// Cosine converts texts to vectors
// associatting each character with its frequncy
// and caculates cosine similarities.
// (https://en.wikipedia.org/wiki/Cosine_similarity)
func Cosine(txt1, txt2 []byte) float64 {
	vect1 := make(map[byte]int)
	for _, t := range txt1 {
		vect1[t]++
	}
	vect2 := make(map[byte]int)
	for _, t := range txt2 {
		vect2[t]++
	}
	//
	// dot-product two vectors
	// map[byte]int return 0 for non-existing key
	// and if two texts are equal, product will be highest
	// and if two texts are totally different, it will be 0
	//
	// to calculate A·B
	dotProduct := 0.0
	for k, v := range vect1 {
		dotProduct += float64(v) * float64(vect2[k])
	}
	// to calculate |A|*|B|
	sum1 := 0.0
	for _, v := range vect1 {
		sum1 += math.Pow(float64(v), 2)
	}
	sum2 := 0.0
	for _, v := range vect2 {
		sum2 += math.Pow(float64(v), 2)
	}
	magnitude := math.Sqrt(sum1) * math.Sqrt(sum2)
	if magnitude == 0 {
		return 0.0
	}
	return float64(dotProduct) / float64(magnitude)
}

// Hamming returns the normalized similarity value.
// hamming distance is the number of differing "bits".
// hamming distance is minimum number of substitutions
// required to change one string into the other
// (https://en.wikipedia.org/wiki/Hamming_distance)
func Hamming(txt1, txt2 []byte) float64 {
	switch bytes.Compare(txt1, txt2) {
	case 0: // txt1 == txt2
	case 1: // txt1 > txt2
		temp := make([]byte, len(txt1))
		copy(temp, txt2)
		txt2 = temp
	case -1: // txt1 < txt2
		temp := make([]byte, len(txt2))
		copy(temp, txt1)
		txt1 = temp
	}
	if len(txt1) != len(txt2) {
		panic("Undefined for sequences of unequal length")
	}
	count := 0
	for idx, b1 := range txt1 {
		b2 := txt2[idx]
		xor := b1 ^ b2 // 1 if bits are different
		//
		// bit count (number of 1)
		// http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetNaive
		//
		// repeat shifting from left to right (divide by 2)
		// until all bits are zero
		for x := xor; x > 0; x >>= 1 {
			// check if lowest bit is 1
			if int(x&1) == 1 {
				count++
			}
		}
	}
	if count == 0 {
		// similarity is 1 for equal texts.
		return 1
	}
	return float64(1) / float64(count)
}

// Levenshtein returns the similarity using levenshtein distance.
// (https://en.wikipedia.org/wiki/Levenshtein_distance)
func Levenshtein(txt1, txt2 []byte) float64 {
	// initialize the distance array, with position
	mat := create2Dslice(len(txt1)+1, len(txt2)+1)
	for i := 0; i < len(txt1)+1; i++ {
		mat[i][0] = i
	}
	for i := 0; i < len(txt2)+1; i++ {
		mat[0][i] = i
	}
	for i := 0; i < len(txt1); i++ {
		for j := 0; j < len(txt2); j++ {
			edit := 0
			if txt1[i] != txt2[j] {
				edit = 1
			}
			mat[i+1][j+1] = min(
				mat[i][j+1]+1,  // from txt1
				mat[i+1][j]+1,  // from txt2
				mat[i][j]+edit, // from both
			)
		}
	}
	distance := mat[len(txt1)][len(txt2)]
	if distance == 0 {
		// similarity is 1 for equal texts.
		return 1
	}
	return float64(1) / float64(distance)
}

func min(more ...int) int {
	min := more[0]
	for _, elem := range more {
		if min > elem {
			min = elem
		}
	}
	return min
}

func create2Dslice(row, column int) [][]int {
	mat := make([][]int, row)
	for i := range mat {
		mat[i] = make([]int, column)
	}
	return mat
}
<|endoftext|>


<commit_before>package tunnel

import (
	"io"
	"sync"
	"time"

	"github.com/google/uuid"
	"github.com/micro/go-micro/transport"
)

type link struct {
	transport.Socket

	sync.RWMutex
	// stops the link
	closed chan bool
	// send queue
	sendQueue chan *packet
	// receive queue
	recvQueue chan *packet
	// unique id of this link e.g uuid
	// which we define for ourselves
	id string
	// whether its a loopback connection
	// this flag is used by the transport listener
	// which accepts inbound quic connections
	loopback bool
	// whether its actually connected
	// dialled side sets it to connected
	// after sending the message. the
	// listener waits for the connect
	connected bool
	// the last time we received a keepalive
	// on this link from the remote side
	lastKeepAlive time.Time
	// channels keeps a mapping of channels and last seen
	channels map[string]time.Time
	// the weighted moving average roundtrip
	length int64
	// weighted moving average of bits flowing
	rate float64
	// keep an error count on the link
	errCount int
}

// packet send over link
type packet struct {
	// message to send or received
	message *transport.Message

	// status returned when sent
	status chan error

	// receive related error
	err error
}

func newLink(s transport.Socket) *link {
	l := &link{
		Socket:        s,
		id:            uuid.New().String(),
		lastKeepAlive: time.Now(),
		closed:        make(chan bool),
		channels:      make(map[string]time.Time),
		sendQueue:     make(chan *packet, 128),
		recvQueue:     make(chan *packet, 128),
	}
	go l.process()
	go l.expiry()
	return l
}

func (l *link) setRTT(d time.Duration) {
	l.Lock()
	defer l.Unlock()

	if l.length <= 0 {
		l.length = d.Nanoseconds()
		return
	}

	// https://fishi.devtail.io/weblog/2015/04/12/measuring-bandwidth-and-round-trip-time-tcp-connection-inside-application-layer/
	length := 0.8*float64(l.length) + 0.2*float64(d.Nanoseconds())
	// set new length
	l.length = int64(length)
}

// process deals with the send queue
func (l *link) process() {
	// receive messages
	go func() {
		for {
			m := new(transport.Message)
			err := l.recv(m)
			if err != nil {
				l.Lock()
				l.errCount++
				l.Unlock()
			}

			// process new received message

			select {
			case l.recvQueue <- &packet{message: m, err: err}:
			case <-l.closed:
				return
			}
		}
	}()

	// send messages

	for {
		select {
		case pk := <-l.sendQueue:
			// send the message
			pk.status <- l.send(pk.message)
		case <-l.closed:
			return
		}
	}
}

// watches the channel expiry
func (l *link) expiry() {
	t := time.NewTicker(time.Minute)
	defer t.Stop()

	for {
		select {
		case <-l.closed:
			return
		case <-t.C:
			// drop any channel mappings older than 2 minutes
			var kill []string
			killTime := time.Minute * 2

			l.RLock()
			for ch, t := range l.channels {
				if d := time.Since(t); d > killTime {
					kill = append(kill, ch)
				}
			}
			l.RUnlock()

			// if nothing to kill don't bother with a wasted lock
			if len(kill) == 0 {
				continue
			}

			// kill the channels!
			l.Lock()
			for _, ch := range kill {
				delete(l.channels, ch)
			}
			l.Unlock()
		}
	}
}

func (l *link) send(m *transport.Message) error {
	dataSent := len(m.Body)

	// set header length
	for k, v := range m.Header {
		dataSent += (len(k) + len(v))
	}

	// get time now
	now := time.Now()

	// send the message
	err := l.Socket.Send(m)

	l.Lock()
	defer l.Unlock()

	// there's an error increment the counter and bail
	if err != nil {
		l.errCount++
		return err
	}

	// reset the counter
	l.errCount = 0

	// calculate based on data
	if dataSent > 0 {
		// measure time taken
		delta := time.Since(now)

		// bit sent
		bits := dataSent * 1024

		// rate of send in bits per nanosecond
		rate := float64(bits) / float64(delta.Nanoseconds())

		// default the rate if its zero
		if l.rate == 0 {
			// rate per second
			l.rate = rate * 1e9
		} else {
			// set new rate per second
			l.rate = 0.8*l.rate + 0.2*(rate*1e9)
		}
	}

	return err
}

// recv a message on the link
func (l *link) recv(m *transport.Message) error {
	if m.Header == nil {
		m.Header = make(map[string]string)
	}

	// TODO measure the receive latency if possible

	// receive the transport message
	return l.Socket.Recv(m)
}

// Delay is the current load on the link
func (l *link) Delay() int64 {
	return int64(len(l.sendQueue) + len(l.recvQueue))
}

// Current transfer rate as bits per second (lower is better)
func (l *link) Rate() float64 {
	l.RLock()
	defer l.RUnlock()

	return l.rate
}

// Length returns the roundtrip time as nanoseconds (lower is better).
// Returns 0 where no measurement has been taken.
func (l *link) Length() int64 {
	l.RLock()
	defer l.RUnlock()

	return l.length
}

func (l *link) Id() string {
	l.RLock()
	defer l.RUnlock()

	return l.id
}

func (l *link) Close() error {
	select {
	case <-l.closed:
		return nil
	default:
		close(l.closed)
	}

	return nil
}

// Send sencs a message on the link
func (l *link) Send(m *transport.Message) error {
	// create a new packet to send over the link
	p := &packet{
		message: m,
		status:  make(chan error, 1),
	}

	select {
	case <-l.closed:
		return io.EOF
	case l.sendQueue <- p:
		// in the send queue
	}

	// wait for response
	select {
	case <-l.closed:
		return io.EOF
	case err := <-p.status:
		return err
	}

	// never reached
	return nil
}

// Accept accepts a message on the socket
func (l *link) Recv(m *transport.Message) error {
	select {
	case <-l.closed:
		return io.EOF
	case pk := <-l.recvQueue:
		// check the packet receive error
		if pk.err != nil {
			return pk.err
		}
		*m = *pk.message
	}
	return nil
}

// Status can return connected, closed, error
func (l *link) Status() string {
	select {
	case <-l.closed:
		return "closed"
	default:
		l.RLock()
		defer l.RUnlock()
		if l.errCount > 3 {
			return "error"
		}
		return "connected"
	}
}
<commit_msg>rearrange where we account for errors and data sent<commit_after>package tunnel

import (
	"io"
	"sync"
	"time"

	"github.com/google/uuid"
	"github.com/micro/go-micro/transport"
)

type link struct {
	transport.Socket

	sync.RWMutex
	// stops the link
	closed chan bool
	// send queue
	sendQueue chan *packet
	// receive queue
	recvQueue chan *packet
	// unique id of this link e.g uuid
	// which we define for ourselves
	id string
	// whether its a loopback connection
	// this flag is used by the transport listener
	// which accepts inbound quic connections
	loopback bool
	// whether its actually connected
	// dialled side sets it to connected
	// after sending the message. the
	// listener waits for the connect
	connected bool
	// the last time we received a keepalive
	// on this link from the remote side
	lastKeepAlive time.Time
	// channels keeps a mapping of channels and last seen
	channels map[string]time.Time
	// the weighted moving average roundtrip
	length int64
	// weighted moving average of bits flowing
	rate float64
	// keep an error count on the link
	errCount int
}

// packet send over link
type packet struct {
	// message to send or received
	message *transport.Message

	// status returned when sent
	status chan error

	// receive related error
	err error
}

func newLink(s transport.Socket) *link {
	l := &link{
		Socket:        s,
		id:            uuid.New().String(),
		lastKeepAlive: time.Now(),
		closed:        make(chan bool),
		channels:      make(map[string]time.Time),
		sendQueue:     make(chan *packet, 128),
		recvQueue:     make(chan *packet, 128),
	}
	go l.process()
	go l.expiry()
	return l
}

func (l *link) setRTT(d time.Duration) {
	l.Lock()
	defer l.Unlock()

	if l.length <= 0 {
		l.length = d.Nanoseconds()
		return
	}

	// https://fishi.devtail.io/weblog/2015/04/12/measuring-bandwidth-and-round-trip-time-tcp-connection-inside-application-layer/
	length := 0.8*float64(l.length) + 0.2*float64(d.Nanoseconds())
	// set new length
	l.length = int64(length)
}

// process deals with the send queue
func (l *link) process() {
	// receive messages
	go func() {
		for {
			m := new(transport.Message)
			err := l.recv(m)
			if err != nil {
				l.Lock()
				l.errCount++
				l.Unlock()
			}

			// process new received message

			select {
			case l.recvQueue <- &packet{message: m, err: err}:
			case <-l.closed:
				return
			}
		}
	}()

	// send messages

	for {
		select {
		case pk := <-l.sendQueue:
			// send the message
			pk.status <- l.send(pk.message)
		case <-l.closed:
			return
		}
	}
}

// watches the channel expiry
func (l *link) expiry() {
	t := time.NewTicker(time.Minute)
	defer t.Stop()

	for {
		select {
		case <-l.closed:
			return
		case <-t.C:
			// drop any channel mappings older than 2 minutes
			var kill []string
			killTime := time.Minute * 2

			l.RLock()
			for ch, t := range l.channels {
				if d := time.Since(t); d > killTime {
					kill = append(kill, ch)
				}
			}
			l.RUnlock()

			// if nothing to kill don't bother with a wasted lock
			if len(kill) == 0 {
				continue
			}

			// kill the channels!
			l.Lock()
			for _, ch := range kill {
				delete(l.channels, ch)
			}
			l.Unlock()
		}
	}
}

func (l *link) send(m *transport.Message) error {
	if m.Header == nil {
		m.Header = make(map[string]string)
	}
	// send the message
	return l.Socket.Send(m)
}

// recv a message on the link
func (l *link) recv(m *transport.Message) error {
	if m.Header == nil {
		m.Header = make(map[string]string)
	}
	// receive the transport message
	return l.Socket.Recv(m)
}

// Delay is the current load on the link
func (l *link) Delay() int64 {
	return int64(len(l.sendQueue) + len(l.recvQueue))
}

// Current transfer rate as bits per second (lower is better)
func (l *link) Rate() float64 {
	l.RLock()
	defer l.RUnlock()

	return l.rate
}

// Length returns the roundtrip time as nanoseconds (lower is better).
// Returns 0 where no measurement has been taken.
func (l *link) Length() int64 {
	l.RLock()
	defer l.RUnlock()

	return l.length
}

func (l *link) Id() string {
	l.RLock()
	defer l.RUnlock()

	return l.id
}

func (l *link) Close() error {
	select {
	case <-l.closed:
		return nil
	default:
		close(l.closed)
	}

	return nil
}

// Send sencs a message on the link
func (l *link) Send(m *transport.Message) error {
	// create a new packet to send over the link
	p := &packet{
		message: m,
		status:  make(chan error, 1),
	}

	// get time now
	now := time.Now()

	// queue the message
	select {
	case <-l.closed:
		return io.EOF
	case l.sendQueue <- p:
		// in the send queue
	}

	// error to use
	var err error

	// wait for response
	select {
	case <-l.closed:
		return io.EOF
	case err = <-p.status:
	}

	l.Lock()
	defer l.Unlock()

	// there's an error increment the counter and bail
	if err != nil {
		l.errCount++
		return err
	}

	// reset the counter
	l.errCount = 0

	// calculate the data sent
	dataSent := len(m.Body)

	// set header length
	for k, v := range m.Header {
		dataSent += (len(k) + len(v))
	}

	// calculate based on data
	if dataSent > 0 {
		// measure time taken
		delta := time.Since(now)

		// bit sent
		bits := dataSent * 1024

		// rate of send in bits per nanosecond
		rate := float64(bits) / float64(delta.Nanoseconds())

		// default the rate if its zero
		if l.rate == 0 {
			// rate per second
			l.rate = rate * 1e9
		} else {
			// set new rate per second
			l.rate = 0.8*l.rate + 0.2*(rate*1e9)
		}
	}

	return nil
}

// Accept accepts a message on the socket
func (l *link) Recv(m *transport.Message) error {
	select {
	case <-l.closed:
		return io.EOF
	case pk := <-l.recvQueue:
		// check the packet receive error
		if pk.err != nil {
			return pk.err
		}
		*m = *pk.message
	}
	return nil
}

// Status can return connected, closed, error
func (l *link) Status() string {
	select {
	case <-l.closed:
		return "closed"
	default:
		l.RLock()
		defer l.RUnlock()
		if l.errCount > 3 {
			return "error"
		}
		return "connected"
	}
}
<|endoftext|>


<commit_before>package currencies

import (
	"encoding/json"
	"errors"
	"fmt"
	"time"
)

// Rates holds data as represented on http://currency.prebid.org/latest.json
// note that `DataAsOfRaw` field is needed when parsing remote JSON as the date format if not standard and requires
// custom parsing to be properly set as Golang time.Time
type Rates struct {
	DataAsOf    time.Time                     `json:"dataAsOf"`
	Conversions map[string]map[string]float64 `json:"conversions"`
}

// NewRates creates a new Rates object holding currencies rates
func NewRates(dataAsOf time.Time, conversions map[string]map[string]float64) *Rates {
	return &Rates{
		DataAsOf:    dataAsOf,
		Conversions: conversions,
	}
}

// UnmarshalJSON unmarshal raw JSON bytes to Rates object
func (r *Rates) UnmarshalJSON(b []byte) error {
	c := &struct {
		DataAsOf    string                        `json:"dataAsOf"`
		Conversions map[string]map[string]float64 `json:"conversions"`
	}{}
	if err := json.Unmarshal(b, &c); err != nil {
		return err
	}

	r.Conversions = c.Conversions

	layout := "2006-01-02"
	if date, err := time.Parse(layout, c.DataAsOf); err == nil {
		r.DataAsOf = date
	}

	return nil
}

// GetRate returns the conversion rate between two currencies
// returns an error in case the conversion rate between the two given currencies is not in the currencies rates map
func (r *Rates) GetRate(from string, to string) (float64, error) {
	if r.Conversions != nil {
		if conversion, present := r.Conversions[from][to]; present == true {
			return conversion, nil
		}
		return 0, fmt.Errorf("conversion %s->%s not present in rates dictionary", from, to)
	}
	return 0, errors.New("rates are nil")
}
<commit_msg>[Currency] Changing the default currency rates URL (#786)<commit_after>package currencies

import (
	"encoding/json"
	"errors"
	"fmt"
	"time"
)

// Rates holds data as represented on https://cdn.jsdelivr.net/gh/prebid/currency-file@1/latest.json
// note that `DataAsOfRaw` field is needed when parsing remote JSON as the date format if not standard and requires
// custom parsing to be properly set as Golang time.Time
type Rates struct {
	DataAsOf    time.Time                     `json:"dataAsOf"`
	Conversions map[string]map[string]float64 `json:"conversions"`
}

// NewRates creates a new Rates object holding currencies rates
func NewRates(dataAsOf time.Time, conversions map[string]map[string]float64) *Rates {
	return &Rates{
		DataAsOf:    dataAsOf,
		Conversions: conversions,
	}
}

// UnmarshalJSON unmarshal raw JSON bytes to Rates object
func (r *Rates) UnmarshalJSON(b []byte) error {
	c := &struct {
		DataAsOf    string                        `json:"dataAsOf"`
		Conversions map[string]map[string]float64 `json:"conversions"`
	}{}
	if err := json.Unmarshal(b, &c); err != nil {
		return err
	}

	r.Conversions = c.Conversions

	layout := "2006-01-02"
	if date, err := time.Parse(layout, c.DataAsOf); err == nil {
		r.DataAsOf = date
	}

	return nil
}

// GetRate returns the conversion rate between two currencies
// returns an error in case the conversion rate between the two given currencies is not in the currencies rates map
func (r *Rates) GetRate(from string, to string) (float64, error) {
	if r.Conversions != nil {
		if conversion, present := r.Conversions[from][to]; present == true {
			return conversion, nil
		}
		return 0, fmt.Errorf("conversion %s->%s not present in rates dictionary", from, to)
	}
	return 0, errors.New("rates are nil")
}
<|endoftext|>


<commit_before>package irckit

import (
	"net"
	"strings"
	"time"

	"github.com/42wim/matterbridge-plus/matterclient"
	"github.com/gorilla/websocket"
	"github.com/mattermost/platform/model"
	"github.com/sorcix/irc"
)

type MmInfo struct {
	MmGhostUser    bool
	MmClient       *model.Client
	MmWsClient     *websocket.Conn
	MmWsQuit       bool
	Srv            Server
	MmUsers        map[string]*model.User
	MmUser         *model.User
	MmChannels     *model.ChannelList
	MmMoreChannels *model.ChannelList
	MmTeam         *model.Team
	Credentials    *MmCredentials
	Cfg            *MmCfg
	mc             *matterclient.MMClient
}

type MmCredentials struct {
	Login  string
	Team   string
	Pass   string
	Server string
}

type MmCfg struct {
	AllowedServers []string
	DefaultServer  string
	DefaultTeam    string
	Insecure       bool
}

func NewUserMM(c net.Conn, srv Server, cfg *MmCfg) *User {
	u := NewUser(&conn{
		Conn:    c,
		Encoder: irc.NewEncoder(c),
		Decoder: irc.NewDecoder(c),
	})
	u.Srv = srv
	u.MmInfo.Cfg = cfg

	// used for login
	u.createService("mattermost", "loginservice")
	return u
}

func (u *User) loginToMattermost() (*matterclient.MMClient, error) {
	mc := matterclient.New(u.Credentials.Login, u.Credentials.Pass, u.Credentials.Team, u.Credentials.Server)
	if u.Cfg.Insecure {
		mc.Credentials.NoTLS = true
	}
	if logger.LogDebug() {
		mc.SetLogLevel("debug")
	}
	err := mc.Login()
	if err != nil {
		return nil, err
	}
	u.MmWsQuit = false
	return mc, nil
}

func (u *User) logoutFromMattermost() error {
	logger.Debug("LOGOUT")
	u.mc.Client.Logout()
	u.MmWsQuit = true
	u.mc.WsClient.Close()
	u.mc.WsClient.UnderlyingConn().Close()
	u.mc.WsClient = nil
	u.Srv.Logout(u)
	return nil
}

func (u *User) createMMUser(mmuser *model.User) *User {
	if ghost, ok := u.Srv.HasUser(mmuser.Username); ok {
		return ghost
	}
	ghost := &User{Nick: mmuser.Username, User: mmuser.Id, Real: mmuser.FirstName + " " + mmuser.LastName, Host: u.mc.Client.Url, channels: map[Channel]struct{}{}}
	ghost.MmGhostUser = true
	u.Srv.Add(ghost)
	go u.Srv.Handle(ghost)
	return ghost
}

func (u *User) createService(nick string, what string) {
	service := &User{Nick: nick, User: nick, Real: what, Host: "service", channels: map[Channel]struct{}{}}
	service.MmGhostUser = true
	u.Srv.Add(service)
	go u.Srv.Handle(service)
}

func (u *User) addUserToChannel(user *model.User, channel string) {
	ghost := u.createMMUser(user)
	logger.Info("adding", ghost.Nick, "to #"+channel)
	ch := u.Srv.Channel("#" + channel)
	ch.Join(ghost)
}

func (u *User) addUsersToChannels() {
	srv := u.Srv
	throttle := time.Tick(time.Millisecond * 300)

	for _, mmchannel := range u.mc.Channels.Channels {
		// exclude direct messages
		if strings.Contains(mmchannel.Name, "__") {
			continue
		}
		<-throttle
		go func(mmchannel *model.Channel) {
			u.syncMMChannel(mmchannel.Id, mmchannel.Name)
			ch := srv.Channel("#" + mmchannel.Name)
			// post everything to the channel you haven't seen yet
			postlist := u.mc.GetPostsSince(mmchannel.Id, u.mc.Channels.Members[mmchannel.Id].LastViewedAt)
			if postlist == nil {
				logger.Errorf("something wrong with getMMPostsSince")
				return
			}
			// traverse the order in reverse
			for i := len(postlist.Order) - 1; i >= 0; i-- {
				for _, post := range strings.Split(postlist.Posts[postlist.Order[i]].Message, "\n") {
					ch.SpoofMessage(u.mc.Users[postlist.Posts[postlist.Order[i]].UserId].Username, post)
				}
			}
			u.mc.UpdateLastViewed(mmchannel.Id)

		}(mmchannel)
	}

	// add all users, also who are not on channels
	for _, mmuser := range u.mc.Users {
		// do not add our own nick
		if mmuser.Id == u.mc.User.Id {
			continue
		}
		u.createMMUser(mmuser)
	}
}

func (u *User) WsReceiver() {
	var rmsg model.Message
	for {
		if u.MmWsQuit {
			logger.Debug("exiting WsReceiver")
			return
		}
		logger.Debug("in WsReceiver")
		if err := u.mc.WsClient.ReadJSON(&rmsg); err != nil {
			logger.Critical(err)
			if u.MmWsQuit {
				logger.Debug("exiting WsReceiver - MmWsQuit - ReadJSON")
				return
			}
			// did the user quit
			if _, ok := u.Srv.HasUser(u.Nick); !ok {
				logger.Debug("user has quit, not reconnecting")
				u.mc.WsClient.Close()
				return
			}
			// reconnect
			u.mc, _ = u.loginToMattermost()
			u.addUsersToChannels()
		}
		logger.Debugf("WsReceiver: %#v", rmsg)
		// check if we have the users/channels in our cache. If not update
		u.checkWsActionMessage(&rmsg)
		switch rmsg.Action {
		case model.ACTION_POSTED:
			u.handleWsActionPost(&rmsg)
		case model.ACTION_USER_REMOVED:
			u.handleWsActionUserRemoved(&rmsg)
		case model.ACTION_USER_ADDED:
			u.handleWsActionUserAdded(&rmsg)
		}
	}
}

func (u *User) handleWsActionPost(rmsg *model.Message) {
	data := model.PostFromJson(strings.NewReader(rmsg.Props["post"]))
	logger.Debug("receiving userid", data.UserId)
	if data.UserId == u.mc.User.Id {
		// space + ZWSP
		if strings.Contains(data.Message, " ") {
			logger.Debugf("message is sent from IRC, contains unicode, not relaying %#v", data.Message)
			return
		}
		if data.Type == "system_join_leave" {
			logger.Debugf("our own join/leave message. not relaying %#v", data.Message)
			return
		}
	}
	ghost := u.createMMUser(u.mc.Users[data.UserId])
	// our own message, set our IRC self as user, not our mattermost self
	if data.UserId == u.mc.User.Id {
		ghost = u
	}
	rcvchannel := u.mc.GetChannelName(data.ChannelId)
	// direct message
	if strings.Contains(rcvchannel, "__") {
		// our own message, ignore because we can't handle/fake those on IRC
		if data.UserId == u.mc.User.Id {
			return
		}
		logger.Debug("direct message")
		var rcvuser string
		rcvusers := strings.Split(rcvchannel, "__")
		if rcvusers[0] != u.mc.User.Id {
			rcvuser = u.mc.Users[rcvusers[0]].Username
		} else {
			rcvuser = u.mc.Users[rcvusers[1]].Username
		}
		msgs := strings.Split(data.Message, "\n")
		for _, m := range msgs {
			u.MsgSpoofUser(rcvuser, m)
		}
		return
	}

	logger.Debugf("channel id %#v, name %#v", data.ChannelId, u.mc.GetChannelName(data.ChannelId))
	ch := u.Srv.Channel("#" + rcvchannel)

	// join if not in channel
	if !ch.HasUser(ghost) {
		ch.Join(ghost)
	}
	msgs := strings.Split(data.Message, "\n")

	// check if we have a override_username (from webhooks) and use it
	props := map[string]interface{}(data.Props)
	overrideUsername, _ := props["override_username"].(string)
	for _, m := range msgs {
		if overrideUsername != "" {
			ch.SpoofMessage(overrideUsername, m)
		} else {
			ch.Message(ghost, m)
		}
	}

	if len(data.Filenames) > 0 {
		logger.Debugf("files detected")
		for _, fname := range data.Filenames {
			logger.Debug("filename: ", fname)
			ch.Message(ghost, "download file - https://"+u.Credentials.Server+"/api/v1/files/get"+fname)
		}
	}
	logger.Debug(u.mc.Users[data.UserId].Username, ":", data.Message)
	logger.Debugf("%#v", data)

	// updatelastviewed
	u.mc.UpdateLastViewed(data.ChannelId)
}

func (u *User) handleWsActionUserRemoved(rmsg *model.Message) {
	ch := u.Srv.Channel("#" + u.mc.GetChannelName(rmsg.ChannelId))

	// remove ourselves from the channel
	if rmsg.UserId == u.mc.User.Id {
		return
	}

	ghost := u.createMMUser(u.mc.Users[rmsg.UserId])
	if ghost == nil {
		logger.Debug("couldn't remove user", rmsg.UserId, u.mc.Users[rmsg.UserId].Username)
		return
	}
	ch.Part(ghost, "")
}

func (u *User) handleWsActionUserAdded(rmsg *model.Message) {
	// do not add ourselves to the channel
	if rmsg.UserId == u.mc.User.Id {
		logger.Debug("ACTION_USER_ADDED not adding myself to", u.mc.GetChannelName(rmsg.ChannelId), rmsg.ChannelId)
		return
	}
	u.addUserToChannel(u.mc.Users[rmsg.UserId], u.mc.GetChannelName(rmsg.ChannelId))
}

func (u *User) checkWsActionMessage(rmsg *model.Message) {
	// Don't check pings
	if rmsg.Action == "ping" {
		return
	}
	logger.Debugf("checkWsActionMessage %#v\n", rmsg)
	if u.mc.GetChannelName(rmsg.ChannelId) == "" {
		u.mc.UpdateChannels()
	}
	if u.mc.Users[rmsg.UserId] == nil {
		u.mc.UpdateUsers()
	}
}

func (u *User) MsgUser(toUser *User, msg string) {
	u.Encode(&irc.Message{
		Prefix:   toUser.Prefix(),
		Command:  irc.PRIVMSG,
		Params:   []string{u.Nick},
		Trailing: msg,
	})
}

func (u *User) MsgSpoofUser(rcvuser string, msg string) {
	u.Encode(&irc.Message{
		Prefix:   &irc.Prefix{Name: rcvuser, User: rcvuser, Host: rcvuser},
		Command:  irc.PRIVMSG,
		Params:   []string{u.Nick},
		Trailing: msg,
	})
}

func (u *User) handleMMDM(toUser *User, msg string) {
	var channel string
	// We don't have a DM with this user yet.
	if u.mc.GetChannelId(toUser.User+"__"+u.mc.User.Id) == "" && u.mc.GetChannelId(u.mc.User.Id+"__"+toUser.User) == "" {
		// create DM channel
		_, err := u.mc.Client.CreateDirectChannel(toUser.User)
		if err != nil {
			logger.Debugf("direct message to %#v failed: %s", toUser, err)
		}
		// update our channels
		mmchannels, _ := u.mc.Client.GetChannels("")
		u.mc.Channels = mmchannels.Data.(*model.ChannelList)
	}

	// build the channel name
	if toUser.User > u.mc.User.Id {
		channel = u.mc.User.Id + "__" + toUser.User
	} else {
		channel = toUser.User + "__" + u.mc.User.Id
	}
	// build & send the message
	msg = strings.Replace(msg, "\r", "", -1)
	post := &model.Post{ChannelId: u.mc.GetChannelId(channel), Message: msg}
	u.mc.Client.CreatePost(post)
}

// sync IRC with mattermost channel state
func (u *User) syncMMChannel(id string, name string) {
	srv := u.Srv
	edata, _ := u.mc.Client.GetChannelExtraInfo(id, -1, "")
	for _, d := range edata.Data.(*model.ChannelExtra).Members {
		// join all the channels we're on on MM
		if d.Id == u.mc.User.Id {
			ch := srv.Channel("#" + name)
			ch.Topic(u, u.mc.GetChannelHeader(id))
			// only join when we're not yet on the channel
			if !ch.HasUser(u) {
				logger.Debug("syncMMChannel adding myself to ", name, id)
				ch.Join(u)
			}
			continue
		}
		u.addUserToChannel(u.mc.Users[d.Id], name)
	}
}

func (u *User) joinMMChannel(channel string) error {
	u.mc.JoinChannel(channel)
	return nil
}

func (u *User) isValidMMServer(server string) bool {
	if len(u.Cfg.AllowedServers) > 0 {
		logger.Debug("allowedservers:", u.Cfg.AllowedServers)
		for _, srv := range u.Cfg.AllowedServers {
			if srv == server {
				return true
			}
		}
		return false
	}
	return true
}
<commit_msg>Fix IRC join channel issue. Closes 42wim/matterircd#51<commit_after>package irckit

import (
	"net"
	"strings"
	"time"

	"github.com/42wim/matterbridge-plus/matterclient"
	"github.com/gorilla/websocket"
	"github.com/mattermost/platform/model"
	"github.com/sorcix/irc"
)

type MmInfo struct {
	MmGhostUser    bool
	MmClient       *model.Client
	MmWsClient     *websocket.Conn
	MmWsQuit       bool
	Srv            Server
	MmUsers        map[string]*model.User
	MmUser         *model.User
	MmChannels     *model.ChannelList
	MmMoreChannels *model.ChannelList
	MmTeam         *model.Team
	Credentials    *MmCredentials
	Cfg            *MmCfg
	mc             *matterclient.MMClient
}

type MmCredentials struct {
	Login  string
	Team   string
	Pass   string
	Server string
}

type MmCfg struct {
	AllowedServers []string
	DefaultServer  string
	DefaultTeam    string
	Insecure       bool
}

func NewUserMM(c net.Conn, srv Server, cfg *MmCfg) *User {
	u := NewUser(&conn{
		Conn:    c,
		Encoder: irc.NewEncoder(c),
		Decoder: irc.NewDecoder(c),
	})
	u.Srv = srv
	u.MmInfo.Cfg = cfg

	// used for login
	u.createService("mattermost", "loginservice")
	return u
}

func (u *User) loginToMattermost() (*matterclient.MMClient, error) {
	mc := matterclient.New(u.Credentials.Login, u.Credentials.Pass, u.Credentials.Team, u.Credentials.Server)
	if u.Cfg.Insecure {
		mc.Credentials.NoTLS = true
	}
	if logger.LogDebug() {
		mc.SetLogLevel("debug")
	}
	err := mc.Login()
	if err != nil {
		return nil, err
	}
	u.MmWsQuit = false
	return mc, nil
}

func (u *User) logoutFromMattermost() error {
	logger.Debug("LOGOUT")
	u.mc.Client.Logout()
	u.MmWsQuit = true
	u.mc.WsClient.Close()
	u.mc.WsClient.UnderlyingConn().Close()
	u.mc.WsClient = nil
	u.Srv.Logout(u)
	return nil
}

func (u *User) createMMUser(mmuser *model.User) *User {
	if ghost, ok := u.Srv.HasUser(mmuser.Username); ok {
		return ghost
	}
	ghost := &User{Nick: mmuser.Username, User: mmuser.Id, Real: mmuser.FirstName + " " + mmuser.LastName, Host: u.mc.Client.Url, channels: map[Channel]struct{}{}}
	ghost.MmGhostUser = true
	u.Srv.Add(ghost)
	go u.Srv.Handle(ghost)
	return ghost
}

func (u *User) createService(nick string, what string) {
	service := &User{Nick: nick, User: nick, Real: what, Host: "service", channels: map[Channel]struct{}{}}
	service.MmGhostUser = true
	u.Srv.Add(service)
	go u.Srv.Handle(service)
}

func (u *User) addUserToChannel(user *model.User, channel string) {
	ghost := u.createMMUser(user)
	logger.Info("adding", ghost.Nick, "to #"+channel)
	ch := u.Srv.Channel("#" + channel)
	ch.Join(ghost)
}

func (u *User) addUsersToChannels() {
	srv := u.Srv
	throttle := time.Tick(time.Millisecond * 300)

	for _, mmchannel := range u.mc.Channels.Channels {
		// exclude direct messages
		if strings.Contains(mmchannel.Name, "__") {
			continue
		}
		<-throttle
		go func(mmchannel *model.Channel) {
			u.syncMMChannel(mmchannel.Id, mmchannel.Name)
			ch := srv.Channel("#" + mmchannel.Name)
			// post everything to the channel you haven't seen yet
			postlist := u.mc.GetPostsSince(mmchannel.Id, u.mc.Channels.Members[mmchannel.Id].LastViewedAt)
			if postlist == nil {
				logger.Errorf("something wrong with getMMPostsSince")
				return
			}
			// traverse the order in reverse
			for i := len(postlist.Order) - 1; i >= 0; i-- {
				for _, post := range strings.Split(postlist.Posts[postlist.Order[i]].Message, "\n") {
					ch.SpoofMessage(u.mc.Users[postlist.Posts[postlist.Order[i]].UserId].Username, post)
				}
			}
			u.mc.UpdateLastViewed(mmchannel.Id)

		}(mmchannel)
	}

	// add all users, also who are not on channels
	for _, mmuser := range u.mc.Users {
		// do not add our own nick
		if mmuser.Id == u.mc.User.Id {
			continue
		}
		u.createMMUser(mmuser)
	}
}

func (u *User) WsReceiver() {
	var rmsg model.Message
	for {
		if u.MmWsQuit {
			logger.Debug("exiting WsReceiver")
			return
		}
		logger.Debug("in WsReceiver")
		if err := u.mc.WsClient.ReadJSON(&rmsg); err != nil {
			logger.Critical(err)
			if u.MmWsQuit {
				logger.Debug("exiting WsReceiver - MmWsQuit - ReadJSON")
				return
			}
			// did the user quit
			if _, ok := u.Srv.HasUser(u.Nick); !ok {
				logger.Debug("user has quit, not reconnecting")
				u.mc.WsClient.Close()
				return
			}
			// reconnect
			u.mc, _ = u.loginToMattermost()
			u.addUsersToChannels()
		}
		logger.Debugf("WsReceiver: %#v", rmsg)
		// check if we have the users/channels in our cache. If not update
		u.checkWsActionMessage(&rmsg)
		switch rmsg.Action {
		case model.ACTION_POSTED:
			u.handleWsActionPost(&rmsg)
		case model.ACTION_USER_REMOVED:
			u.handleWsActionUserRemoved(&rmsg)
		case model.ACTION_USER_ADDED:
			u.handleWsActionUserAdded(&rmsg)
		}
	}
}

func (u *User) handleWsActionPost(rmsg *model.Message) {
	data := model.PostFromJson(strings.NewReader(rmsg.Props["post"]))
	logger.Debug("receiving userid", data.UserId)
	if data.UserId == u.mc.User.Id {
		// space + ZWSP
		if strings.Contains(data.Message, " ") {
			logger.Debugf("message is sent from IRC, contains unicode, not relaying %#v", data.Message)
			return
		}
		if data.Type == "system_join_leave" {
			logger.Debugf("our own join/leave message. not relaying %#v", data.Message)
			return
		}
	}
	ghost := u.createMMUser(u.mc.Users[data.UserId])
	// our own message, set our IRC self as user, not our mattermost self
	if data.UserId == u.mc.User.Id {
		ghost = u
	}
	rcvchannel := u.mc.GetChannelName(data.ChannelId)
	// direct message
	if strings.Contains(rcvchannel, "__") {
		// our own message, ignore because we can't handle/fake those on IRC
		if data.UserId == u.mc.User.Id {
			return
		}
		logger.Debug("direct message")
		var rcvuser string
		rcvusers := strings.Split(rcvchannel, "__")
		if rcvusers[0] != u.mc.User.Id {
			rcvuser = u.mc.Users[rcvusers[0]].Username
		} else {
			rcvuser = u.mc.Users[rcvusers[1]].Username
		}
		msgs := strings.Split(data.Message, "\n")
		for _, m := range msgs {
			u.MsgSpoofUser(rcvuser, m)
		}
		return
	}

	logger.Debugf("channel id %#v, name %#v", data.ChannelId, u.mc.GetChannelName(data.ChannelId))
	ch := u.Srv.Channel("#" + rcvchannel)

	// join if not in channel
	if !ch.HasUser(ghost) {
		ch.Join(ghost)
	}
	msgs := strings.Split(data.Message, "\n")

	// check if we have a override_username (from webhooks) and use it
	props := map[string]interface{}(data.Props)
	overrideUsername, _ := props["override_username"].(string)
	for _, m := range msgs {
		if overrideUsername != "" {
			ch.SpoofMessage(overrideUsername, m)
		} else {
			ch.Message(ghost, m)
		}
	}

	if len(data.Filenames) > 0 {
		logger.Debugf("files detected")
		for _, fname := range data.Filenames {
			logger.Debug("filename: ", fname)
			ch.Message(ghost, "download file - https://"+u.Credentials.Server+"/api/v1/files/get"+fname)
		}
	}
	logger.Debug(u.mc.Users[data.UserId].Username, ":", data.Message)
	logger.Debugf("%#v", data)

	// updatelastviewed
	u.mc.UpdateLastViewed(data.ChannelId)
}

func (u *User) handleWsActionUserRemoved(rmsg *model.Message) {
	ch := u.Srv.Channel("#" + u.mc.GetChannelName(rmsg.ChannelId))

	// remove ourselves from the channel
	if rmsg.UserId == u.mc.User.Id {
		return
	}

	ghost := u.createMMUser(u.mc.Users[rmsg.UserId])
	if ghost == nil {
		logger.Debug("couldn't remove user", rmsg.UserId, u.mc.Users[rmsg.UserId].Username)
		return
	}
	ch.Part(ghost, "")
}

func (u *User) handleWsActionUserAdded(rmsg *model.Message) {
	// do not add ourselves to the channel
	if rmsg.UserId == u.mc.User.Id {
		logger.Debug("ACTION_USER_ADDED not adding myself to", u.mc.GetChannelName(rmsg.ChannelId), rmsg.ChannelId)
		return
	}
	u.addUserToChannel(u.mc.Users[rmsg.UserId], u.mc.GetChannelName(rmsg.ChannelId))
}

func (u *User) checkWsActionMessage(rmsg *model.Message) {
	// Don't check pings
	if rmsg.Action == "ping" {
		return
	}
	logger.Debugf("checkWsActionMessage %#v\n", rmsg)
	if u.mc.GetChannelName(rmsg.ChannelId) == "" {
		u.mc.UpdateChannels()
	}
	if u.mc.Users[rmsg.UserId] == nil {
		u.mc.UpdateUsers()
	}
}

func (u *User) MsgUser(toUser *User, msg string) {
	u.Encode(&irc.Message{
		Prefix:   toUser.Prefix(),
		Command:  irc.PRIVMSG,
		Params:   []string{u.Nick},
		Trailing: msg,
	})
}

func (u *User) MsgSpoofUser(rcvuser string, msg string) {
	u.Encode(&irc.Message{
		Prefix:   &irc.Prefix{Name: rcvuser, User: rcvuser, Host: rcvuser},
		Command:  irc.PRIVMSG,
		Params:   []string{u.Nick},
		Trailing: msg,
	})
}

func (u *User) handleMMDM(toUser *User, msg string) {
	var channel string
	// We don't have a DM with this user yet.
	if u.mc.GetChannelId(toUser.User+"__"+u.mc.User.Id) == "" && u.mc.GetChannelId(u.mc.User.Id+"__"+toUser.User) == "" {
		// create DM channel
		_, err := u.mc.Client.CreateDirectChannel(toUser.User)
		if err != nil {
			logger.Debugf("direct message to %#v failed: %s", toUser, err)
		}
		// update our channels
		mmchannels, _ := u.mc.Client.GetChannels("")
		u.mc.Channels = mmchannels.Data.(*model.ChannelList)
	}

	// build the channel name
	if toUser.User > u.mc.User.Id {
		channel = u.mc.User.Id + "__" + toUser.User
	} else {
		channel = toUser.User + "__" + u.mc.User.Id
	}
	// build & send the message
	msg = strings.Replace(msg, "\r", "", -1)
	post := &model.Post{ChannelId: u.mc.GetChannelId(channel), Message: msg}
	u.mc.Client.CreatePost(post)
}

// sync IRC with mattermost channel state
func (u *User) syncMMChannel(id string, name string) {
	srv := u.Srv
	edata, _ := u.mc.Client.GetChannelExtraInfo(id, -1, "")
	for _, d := range edata.Data.(*model.ChannelExtra).Members {
		// join all the channels we're on on MM
		if d.Id == u.mc.User.Id {
			ch := srv.Channel("#" + name)
			ch.Topic(u, u.mc.GetChannelHeader(id))
			// only join when we're not yet on the channel
			if !ch.HasUser(u) {
				logger.Debug("syncMMChannel adding myself to ", name, id)
				ch.Join(u)
			}
			continue
		}
		u.addUserToChannel(u.mc.Users[d.Id], name)
	}
}

func (u *User) joinMMChannel(channel string) error {
	return u.mc.JoinChannel(channel)
}

func (u *User) isValidMMServer(server string) bool {
	if len(u.Cfg.AllowedServers) > 0 {
		logger.Debug("allowedservers:", u.Cfg.AllowedServers)
		for _, srv := range u.Cfg.AllowedServers {
			if srv == server {
				return true
			}
		}
		return false
	}
	return true
}
<|endoftext|>


<commit_before>package geocodio

type Geocodio struct {
	ApiKey string
}

type Address struct {
	Components   Components `json:"address_components"`
	Formatted    string     `json:"formatted_address"`
	Location     Location   `json:"location"`
	Accuracy     float64    `json:"accuracy"`
	AccuracyType string     `json:"accuracy_type"`
	Source       string     `json:"source"`
	Fields       Fields     `json:"fields,omitempty"`
}

type Components struct {
	Number          string `json:"number"`
	Street          string `json:"street"`
	Suffix          string `json:"suffix"`
	PostDirectional string `json:"postdirectional"`
	FormattedStreet string `json:"formatted_street"`
	City            string `json:"city"`
	County          string `json:"county"`
	State           string `json:"state"`
	Zip             string `json:"zip"`
	Country         string `json:"country"`
}

type Location struct {
	Latitude  float64 `json:"lat"`
	Longitude float64 `json:"lng"`
}

// Fields
type Fields struct {
	Timezone                  Timezone                  `json:"timezone,omitempty"`
	CongressionalDistrict     CongressionalDistrict     `json:"congressional_district,omitempty"`
	StateLegislativeDistricts StateLegislativeDistricts `json:"state_legislative_districts,omitempty"`
}

type Timezone struct {
	Name        string `json:"name"`
	UTCOffset   int    `json:"utc_offset"`
	ObservesDST bool   `json:"observes_dst"`
}

type CongressionalDistrict struct {
	Name           string `json:"name"`
	DistrictNumber int    `json:"district_number"`
	CongressNumber string `json:"congress_number"`
	CongressYears  string `json:"congress_years"`
}

type StateLegislativeDistricts struct {
	House  StateLegislativeDistrict `json:"house"`
	Senate StateLegislativeDistrict `json:"senate"`
}

type StateLegislativeDistrict struct {
	Name           string `json:"name"`
	DistrictNumber string `json:"district_number"`
}

type GeocodeResult struct {
	Input   Components `json:"input,omitempty"`
	Results []Address  `json:"results"`
	Debug   struct {
		RequestedURL string `json:"requested_url"`
		Status       string `json:"status"`
		StatusCode   int    `json:"status_code"`
	} `json:"-"`
}

type ReverseGeocodeResult struct {
	Results []Address `json:"results"`
}
<commit_msg>remove unnecessary struct<commit_after>package geocodio

type Geocodio struct {
	ApiKey string
}

type Address struct {
	Components   Components `json:"address_components"`
	Formatted    string     `json:"formatted_address"`
	Location     Location   `json:"location"`
	Accuracy     float64    `json:"accuracy"`
	AccuracyType string     `json:"accuracy_type"`
	Source       string     `json:"source"`
	Fields       Fields     `json:"fields,omitempty"`
}

type Components struct {
	Number          string `json:"number"`
	Street          string `json:"street"`
	Suffix          string `json:"suffix"`
	PostDirectional string `json:"postdirectional"`
	FormattedStreet string `json:"formatted_street"`
	City            string `json:"city"`
	County          string `json:"county"`
	State           string `json:"state"`
	Zip             string `json:"zip"`
	Country         string `json:"country"`
}

type Location struct {
	Latitude  float64 `json:"lat"`
	Longitude float64 `json:"lng"`
}

// Fields
type Fields struct {
	Timezone                  Timezone                  `json:"timezone,omitempty"`
	CongressionalDistrict     CongressionalDistrict     `json:"congressional_district,omitempty"`
	StateLegislativeDistricts StateLegislativeDistricts `json:"state_legislative_districts,omitempty"`
}

type Timezone struct {
	Name        string `json:"name"`
	UTCOffset   int    `json:"utc_offset"`
	ObservesDST bool   `json:"observes_dst"`
}

type CongressionalDistrict struct {
	Name           string `json:"name"`
	DistrictNumber int    `json:"district_number"`
	CongressNumber string `json:"congress_number"`
	CongressYears  string `json:"congress_years"`
}

type StateLegislativeDistricts struct {
	House  StateLegislativeDistrict `json:"house"`
	Senate StateLegislativeDistrict `json:"senate"`
}

type StateLegislativeDistrict struct {
	Name           string `json:"name"`
	DistrictNumber string `json:"district_number"`
}

type GeocodeResult struct {
	Input   Components `json:"input,omitempty"`
	Results []Address  `json:"results"`
	Debug   struct {
		RequestedURL string `json:"requested_url"`
		Status       string `json:"status"`
		StatusCode   int    `json:"status_code"`
	} `json:"-"`
}
<|endoftext|>