<commit_before>package dns

import (
	"crypto/sha256"
	"crypto/sha512"
	"crypto/x509"
	"encoding/hex"
	"io"
	"net"
	"strconv"
)

// CertificateToDANE converts a certificate to a hex string as used in the TLSA record.
func CertificateToDANE(selector, matchingType uint8, cert *x509.Certificate) string {
	switch matchingType {
	case 0:
		switch selector {
		case 0:
			return hex.EncodeToString(cert.Raw)
		case 1:
			return hex.EncodeToString(cert.RawSubjectPublicKeyInfo)
		}
	case 1:
		h := sha256.New()
		switch selector {
		case 0:
			return hex.EncodeToString(cert.Raw)
		case 1:
			io.WriteString(h, string(cert.RawSubjectPublicKeyInfo))
			return hex.EncodeToString(h.Sum(nil))
		}
	case 2:
		h := sha512.New()
		switch selector {
		case 0:
			return hex.EncodeToString(cert.Raw)
		case 1:
			io.WriteString(h, string(cert.RawSubjectPublicKeyInfo))
			return hex.EncodeToString(h.Sum(nil))
		}
	}
	return ""
}

// Sign creates a TLSA record from an SSL certificate.
func (r *TLSA) Sign(usage, selector, matchingType int, cert *x509.Certificate) error {
	r.Hdr.Rrtype = TypeTLSA
	r.Usage = uint8(usage)
	r.Selector = uint8(selector)
	r.MatchingType = uint8(matchingType)

	r.Certificate = CertificateToDANE(r.Selector, r.MatchingType, cert)
	return nil
}

// Verify verifies a TLSA record against an SSL certificate. If it is OK
// a nil error is returned.
func (r *TLSA) Verify(cert *x509.Certificate) error {
	if r.Certificate == CertificateToDANE(r.Selector, r.MatchingType, cert) {
		return nil
	}
	return ErrSig // ErrSig, really?
}

// TLSAName returns the ownername of a TLSA resource record as per the
// rules specified in RFC 6698, Section 3. When an error occurs the
// empty string is returned.
func TLSAName(name, service, network string) string {
	if !IsFqdn(name) {
		return ""
	}
	p, e := net.LookupPort(network, service)
	if e != nil {
		return ""
	}
	return "_" + strconv.Itoa(p) + "_" + network + "." + name
}
<commit_msg>Just return the error<commit_after>package dns

import (
	"crypto/sha256"
	"crypto/sha512"
	"crypto/x509"
	"encoding/hex"
	"io"
	"net"
	"strconv"
)

// CertificateToDANE converts a certificate to a hex string as used in the TLSA record.
func CertificateToDANE(selector, matchingType uint8, cert *x509.Certificate) string {
	switch matchingType {
	case 0:
		switch selector {
		case 0:
			return hex.EncodeToString(cert.Raw)
		case 1:
			return hex.EncodeToString(cert.RawSubjectPublicKeyInfo)
		}
	case 1:
		h := sha256.New()
		switch selector {
		case 0:
			return hex.EncodeToString(cert.Raw)
		case 1:
			io.WriteString(h, string(cert.RawSubjectPublicKeyInfo))
			return hex.EncodeToString(h.Sum(nil))
		}
	case 2:
		h := sha512.New()
		switch selector {
		case 0:
			return hex.EncodeToString(cert.Raw)
		case 1:
			io.WriteString(h, string(cert.RawSubjectPublicKeyInfo))
			return hex.EncodeToString(h.Sum(nil))
		}
	}
	return ""
}

// Sign creates a TLSA record from an SSL certificate.
func (r *TLSA) Sign(usage, selector, matchingType int, cert *x509.Certificate) error {
	r.Hdr.Rrtype = TypeTLSA
	r.Usage = uint8(usage)
	r.Selector = uint8(selector)
	r.MatchingType = uint8(matchingType)

	r.Certificate = CertificateToDANE(r.Selector, r.MatchingType, cert)
	return nil
}

// Verify verifies a TLSA record against an SSL certificate. If it is OK
// a nil error is returned.
func (r *TLSA) Verify(cert *x509.Certificate) error {
	if r.Certificate == CertificateToDANE(r.Selector, r.MatchingType, cert) {
		return nil
	}
	return ErrSig // ErrSig, really?
}

// TLSAName returns the ownername of a TLSA resource record as per the
// rules specified in RFC 6698, Section 3.
func TLSAName(name, service, network string) (string, error) {
	if !IsFqdn(name) {
		return "", ErrFqdn
	}
	p, e := net.LookupPort(network, service)
	if e != nil {
		return "", err
	}
	return "_" + strconv.Itoa(p) + "_" + network + "." + name, nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"mime"
	"net/http"
	"path"
	"path/filepath"
	"strings"

	"github.com/mattn/go-scan"
)

// JSONRPC is structure
type JSONRPC struct {
	req *http.Request
}

// jsonRPCRequest is structure
type jsonRPCRequest struct {
	Jsonrpc string
	Method  string
	Params  interface{}
	ID      int
}

func (j *JSONRPC) buildFileExt() string {
	var ext string
	var mimeType string

	reqURI := j.req.RequestURI
	clientMimes := j.req.Header["Content-Type"]
	serverMime := Config().Header["Content-Type"]
	extByURI := filepath.Ext(reqURI)

	if extByURI == "" {
		if len(clientMimes) != 0 {
			mimeType = clientMimes[0]
		} else {
			mimeType = serverMime
		}

		exts, err := mime.ExtensionsByType(mimeType)
		if err != nil {
			fmt.Errorf("Error mime to ext %s: %s", mimeType, err)
		} else {
			ext = exts[0]
		}
	} else {
		ext = extByURI
	}

	return ext
}

// ResponseFile returns file path
func (j *JSONRPC) ResponseFile() (string, map[string]string) {
	decoder := json.NewDecoder(j.req.Body)
	var rpcReq jsonRPCRequest
	err := decoder.Decode(&rpcReq)
	if err != nil {
		log.Print("[ERROR] " + fmt.Sprintf("Error json decode: \n%s", err))
	}

	var pathsOrderReal, pathsOrderVirt []string
	dict := make(map[string]string)

	var s string
	for _, v := range Config().Namespaces {
		err = scan.ScanTree(rpcReq.Params, "/"+v, &s)
		if err != nil {
			continue
		}
		dict[v] = s
	}

	var src string
	ext := j.buildFileExt()
	keys := MapKeys(dict)
	vals := MapVals(dict)
	count := len(keys)

	src = path.Join(Config().Root, j.req.RequestURI, rpcReq.Method+ext)
	pathsOrderVirt = append(pathsOrderVirt, src)

	for i := 0; i <= count; i++ {
		normalPath := keys[:(count - i)]
		virtPath := vals[(count - i):]
		dir := strings.Join(append(normalPath, virtPath...), "/")
		src = path.Join(Config().Root, j.req.RequestURI, dir, rpcReq.Method+ext)
		pathsOrderVirt = append(pathsOrderVirt, src)
	}

	pathsOrderReal = ReverseStrings(pathsOrderVirt)
	for _, p := range pathsOrderReal {
		log.Print("[DEBUG] " + fmt.Sprintf("search file: %s", p))
	}

	for _, file := range pathsOrderReal {
		if IsFileExist(file) {
			return file, dict
		}
	}

	return "", dict
}
<commit_msg>refoctoring<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"mime"
	"net/http"
	"path"
	"path/filepath"
	"strings"

	"github.com/mattn/go-scan"
)

// JSONRPC is structure
type JSONRPC struct {
	req    *http.Request
	rpcReq jsonRPCRequest
}

// jsonRPCRequest is structure
type jsonRPCRequest struct {
	Jsonrpc string
	Method  string
	Params  interface{}
	ID      int
}

// ResponseFile returns file path
func (j *JSONRPC) ResponseFile() (string, map[string]string) {
	decoder := json.NewDecoder(j.req.Body)

	err := decoder.Decode(&j.rpcReq)
	if err != nil {
		log.Print("[ERROR] " + fmt.Sprintf("Error json decode: \n%s", err))
	}

	dict := j.dictionary()
	files := j.nominatedFiles(dict)

	for _, file := range files {
		if IsFileExist(file) {
			return file, dict
		}
	}

	return "", dict
}

// fileExt returns a extension
func (j *JSONRPC) fileExt() string {
	var ext string
	var mimeType string

	reqURI := j.req.RequestURI
	clientMimes := j.req.Header["Content-Type"]
	serverMime := Config().Header["Content-Type"]
	extByURI := filepath.Ext(reqURI)

	if extByURI == "" {
		if len(clientMimes) != 0 {
			mimeType = clientMimes[0]
		} else {
			mimeType = serverMime
		}

		exts, err := mime.ExtensionsByType(mimeType)
		if err != nil {
			fmt.Errorf("Error mime to ext %s: %s", mimeType, err)
		} else {
			ext = exts[0]
		}
	} else {
		ext = extByURI
	}

	return ext
}

// dictionary returns resources
func (j *JSONRPC) dictionary() map[string]string {
	dict := make(map[string]string)
	var s string

	for _, v := range Config().Namespaces {
		err := scan.ScanTree(j.rpcReq.Params, "/"+v, &s)
		if err != nil {
			continue
		}
		dict[v] = s
	}

	return dict
}

// nominatedfiles returns file paths
func (j *JSONRPC) nominatedFiles(dict map[string]string) []string {
	var pathsOrderReal, pathsOrderVirt []string
	var src string

	ext := j.fileExt()
	keys := MapKeys(dict)
	vals := MapVals(dict)
	count := len(keys)

	src = path.Join(Config().Root, j.req.RequestURI, j.rpcReq.Method+ext)
	pathsOrderVirt = append(pathsOrderVirt, src)

	for i := 0; i <= count; i++ {
		normalPath := keys[:(count - i)]
		virtPath := vals[(count - i):]
		dir := strings.Join(append(normalPath, virtPath...), "/")
		src = path.Join(Config().Root, j.req.RequestURI, dir, j.rpcReq.Method+ext)
		pathsOrderVirt = append(pathsOrderVirt, src)
	}

	pathsOrderReal = ReverseStrings(pathsOrderVirt)
	for _, p := range pathsOrderReal {
		log.Print("[DEBUG] " + fmt.Sprintf("nominated file: %s", p))
	}

	return pathsOrderReal
}
<|endoftext|>


<commit_before>// Copyright (c) 2015, Ben Morgan. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.

package key

import (
	"errors"
	"fmt"
	"reflect"
	"sort"

	"github.com/goulash/errs"
	"github.com/goulash/twikutil"
)

// Error types {{{

var (
	ErrCatch        = errors.New("hidden: should only be used with TyperFunc")
	ErrKeyExists    = errors.New("key already exists in map")
	ErrMissingValue = errors.New("default value required")
	ErrNotTyper     = errors.New("type checker is invalid")
)

type RequiredError struct {
	Name string
}

func (e RequiredError) Error() string { return fmt.Sprintf("%s: required but unset", e.Name) }

type ImplementsError struct {
	Name  string
	Got   reflect.Type
	Wants string
}

func (e ImplementsError) Error() string {
	return fmt.Sprintf("%s: value (type %s) does not implement %s", e.Got, e.Wants)
}

type TypeError struct {
	Name  string
	Got   reflect.Type
	Wants string
}

func NewTypeError(name string, got interface{}, wants interface{}) *TypeError {
	err := &TypeError{Name: name}
	switch t := got.(type) {
	case reflect.Type:
		err.Got = t
	default:
		err.Got = reflect.TypeOf(t)
	}
	switch w := wants.(type) {
	case reflect.Type:
		err.Wants = w.String()
	case string:
		err.Wants = w
	default:
		err.Wants = reflect.TypeOf(wants).String()
	}
	return err
}

func (e TypeError) Error() string {
	return fmt.Sprintf("%s: value (type %v) is not of type %s", e.Name, e.Got, e.Wants)
}

// }}}

// Mode type {{{

type Mode int

const (
	Reserved Mode = 0
	Read     Mode = 1
	Write    Mode = 2
	Required Mode = 4

	ReadWrite Mode = Read | Write
)

// }}}

// KeyMap {{{

type KeyMap map[string]*Key

func NewKeyMap() KeyMap { return make(KeyMap) }

func (km KeyMap) KeyNames() []string {
	ns := make([]string, 0, len(km))
	for k := range km {
		ns = append(ns, k)
	}
	sort.Strings(ns)
	return ns
}

func (km KeyMap) Create(name string, typer, def interface{}, m Mode, desc string) (*Key, error) {
	if km[name] != nil {
		return nil, ErrKeyExists
	}
	k, err := New(name, typer, def, m, desc)
	if err != nil {
		return nil, err
	}
	km[name] = k
	return k, nil
}

func (km KeyMap) CreateAuto(name string, def interface{}, m Mode, desc string) (*Key, error) {
	if km[name] != nil {
		return nil, ErrKeyExists
	}
	k, err := NewAuto(name, def, m, desc)
	if err != nil {
		return nil, err
	}
	km[name] = k
	return k, nil
}

func (km KeyMap) Keys() Keys {
	ks := make(Keys, 0, len(km))
	for _, v := range km {
		ks = append(ks, v)
	}
	sort.Sort(ks)
	return ks
}

func (km KeyMap) Acquire(e *twikutil.Executer, h errs.Handler) error {
	return km.Keys().Acquire(e, h)
}

func (km KeyMap) Apply(e *twikutil.Executer) error {
	return km.Keys().Apply(e)
}

func (km KeyMap) Clobber(e *twikutil.Executer) error {
	return km.Keys().Clobber(e)
}

// }}}

// Keys {{{

type Keys []*Key

func (ks Keys) Len() int           { return len(ks) }
func (ks Keys) Less(i, j int) bool { return ks[i].name < ks[j].name }
func (ks Keys) Swap(i, j int)      { ks[i], ks[j] = ks[j], ks[i] }

func (ks Keys) Acquire(e *twikutil.Executer, h errs.Handler) error {
	for _, k := range ks {
		err := k.Acquire(e)
		if err = h(err); err != nil {
			return err
		}
	}
	return nil
}

func (ks Keys) Apply(e *twikutil.Executer) error {
	for _, k := range ks {
		err := k.Apply(e)
		if err != nil {
			return err
		}
	}
	return nil
}

// ApplyOrNil applies all keys, and keys that are read-only are
// still set to nil.
func (ks Keys) ApplyOrNil(e *twikutil.Executer) error {
	for _, k := range ks {
		err := k.ApplyOrNil(e)
		if err != nil {
			return err
		}
	}
	return nil
}

func (ks Keys) Clobber(e *twikutil.Executer) error {
	for _, k := range ks {
		err := k.Clobber(e)
		if err != nil {
			return err
		}
	}
	return nil
}

// }}}

type Key struct {
	name  string
	desc  string
	mode  Mode
	typer interface{}
	val   interface{}
}

// Must panics if err is not nil and returns the key otherwise.
func Must(k *Key, err error) *Key {
	if err != nil {
		panic(err)
	}
	return k
}

func New(name string, typer, def interface{}, m Mode, desc string) (*Key, error) {
	if !IsTyper(typer) {
		return nil, ErrNotTyper
	}
	k := &Key{
		name:  name,
		desc:  desc,
		mode:  m,
		typer: typer,
	}
	return k, k.Set(def)
}

func NewAuto(name string, def interface{}, m Mode, desc string) (*Key, error) {
	if def == nil {
		return nil, ErrMissingValue
	}
	return &Key{
		name:  name,
		desc:  desc,
		mode:  m,
		typer: reflect.TypeOf(def),
		val:   def,
	}, nil
}

func (k Key) Empty() bool     { return k.val == nil }
func (k Key) Type() string    { return k.typer.(fmt.Stringer).String() }
func (k Key) Name() string    { return k.name }
func (k Key) Desc() string    { return k.desc }
func (k Key) Mode() Mode      { return k.mode }
func (k *Key) SetMode(m Mode) { k.mode = m }

// Get returns a value that is either nil or guaranteed to conform to the defined type.
func (k Key) Get() interface{} { return k.val }

func (k Key) GetOr(def interface{}) interface{} {
	if k.val == nil {
		return def
	}
	return k.val
}

func (k *Key) Set(v interface{}) (err error) {
	// If v is nil, then we're effectively deleting the stored value.
	if v != nil {
		v, err = Check(k.name, k.typer, v)
		if err != nil {
			return err
		}
	}

	k.val = v
	return nil
}

func (k *Key) Acquire(e *twikutil.Executer) error {
	if k.mode&Read == 0 {
		// This key does not want to be updated.
		return nil
	}

	if !e.Has(k.name) {
		if k.mode&Required != 0 {
			return &RequiredError{k.name}
		}
		return nil
	}

	v, _ := e.Get(k.name)
	return k.Set(v)
}

func (k *Key) Apply(e *twikutil.Executer) error {
	if k.mode&Write == 0 {
		return nil
	}
	if !e.Has(k.name) {
		return e.Set(k.name, k.val)
	}
	return nil
}

func (k *Key) ApplyOrNil(e *twikutil.Executer) error {
	if k.mode&Write == 0 {
		return e.Set(k.name, nil)
	}
	if !e.Has(k.name) {
		return e.Set(k.name, k.val)
	}
	return nil
}

func (k *Key) Clobber(e *twikutil.Executer) error {
	if k.mode&Write == 0 {
		return nil
	}

	return e.Set(k.name, k.val)
}

func Check(name string, typer interface{}, v interface{}) (interface{}, error) {
	chkType := func(t reflect.Type) error {
		vt := reflect.TypeOf(v)
		if t.Kind() == reflect.Interface {
			if !vt.Implements(t) {
				return ImplementsError{name, vt, t.String()}
			}
		} else if vt != t {
			return NewTypeError(name, vt, t)
		}
		return nil
	}

	switch t := typer.(type) {
	case reflect.Type:
		return v, chkType(t)
	case Typer:
		v, err := t.Coerce(v)
		if err != nil {
			switch et := err.(type) {
			case *TypeError:
				if et.Name == "" {
					et.Name = name
				}
				return v, et
			case *ImplementsError:
				if et.Name == "" {
					et.Name = name
				}
				return v, et
			}
		}
		return v, err
	default:
		return nil, ErrNotTyper
	}

}
<commit_msg>Bugfix: forgot to add ApplyOrNil to KeyMap<commit_after>// Copyright (c) 2015, Ben Morgan. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.

package key

import (
	"errors"
	"fmt"
	"reflect"
	"sort"

	"github.com/goulash/errs"
	"github.com/goulash/twikutil"
)

// Error types {{{

var (
	ErrCatch        = errors.New("hidden: should only be used with TyperFunc")
	ErrKeyExists    = errors.New("key already exists in map")
	ErrMissingValue = errors.New("default value required")
	ErrNotTyper     = errors.New("type checker is invalid")
)

type RequiredError struct {
	Name string
}

func (e RequiredError) Error() string { return fmt.Sprintf("%s: required but unset", e.Name) }

type ImplementsError struct {
	Name  string
	Got   reflect.Type
	Wants string
}

func (e ImplementsError) Error() string {
	return fmt.Sprintf("%s: value (type %s) does not implement %s", e.Got, e.Wants)
}

type TypeError struct {
	Name  string
	Got   reflect.Type
	Wants string
}

func NewTypeError(name string, got interface{}, wants interface{}) *TypeError {
	err := &TypeError{Name: name}
	switch t := got.(type) {
	case reflect.Type:
		err.Got = t
	default:
		err.Got = reflect.TypeOf(t)
	}
	switch w := wants.(type) {
	case reflect.Type:
		err.Wants = w.String()
	case string:
		err.Wants = w
	default:
		err.Wants = reflect.TypeOf(wants).String()
	}
	return err
}

func (e TypeError) Error() string {
	return fmt.Sprintf("%s: value (type %v) is not of type %s", e.Name, e.Got, e.Wants)
}

// }}}

// Mode type {{{

type Mode int

const (
	Reserved Mode = 0
	Read     Mode = 1
	Write    Mode = 2
	Required Mode = 4

	ReadWrite Mode = Read | Write
)

// }}}

// KeyMap {{{

type KeyMap map[string]*Key

func NewKeyMap() KeyMap { return make(KeyMap) }

func (km KeyMap) KeyNames() []string {
	ns := make([]string, 0, len(km))
	for k := range km {
		ns = append(ns, k)
	}
	sort.Strings(ns)
	return ns
}

func (km KeyMap) Create(name string, typer, def interface{}, m Mode, desc string) (*Key, error) {
	if km[name] != nil {
		return nil, ErrKeyExists
	}
	k, err := New(name, typer, def, m, desc)
	if err != nil {
		return nil, err
	}
	km[name] = k
	return k, nil
}

func (km KeyMap) CreateAuto(name string, def interface{}, m Mode, desc string) (*Key, error) {
	if km[name] != nil {
		return nil, ErrKeyExists
	}
	k, err := NewAuto(name, def, m, desc)
	if err != nil {
		return nil, err
	}
	km[name] = k
	return k, nil
}

func (km KeyMap) Keys() Keys {
	ks := make(Keys, 0, len(km))
	for _, v := range km {
		ks = append(ks, v)
	}
	sort.Sort(ks)
	return ks
}

func (km KeyMap) Acquire(e *twikutil.Executer, h errs.Handler) error {
	return km.Keys().Acquire(e, h)
}

func (km KeyMap) Apply(e *twikutil.Executer) error {
	return km.Keys().Apply(e)
}

func (km KeyMap) ApplyOrNil(e *twikutil.Executer) error {
	return km.Keys().ApplyOrNil(e)
}

func (km KeyMap) Clobber(e *twikutil.Executer) error {
	return km.Keys().Clobber(e)
}

// }}}

// Keys {{{

type Keys []*Key

func (ks Keys) Len() int           { return len(ks) }
func (ks Keys) Less(i, j int) bool { return ks[i].name < ks[j].name }
func (ks Keys) Swap(i, j int)      { ks[i], ks[j] = ks[j], ks[i] }

func (ks Keys) Acquire(e *twikutil.Executer, h errs.Handler) error {
	for _, k := range ks {
		err := k.Acquire(e)
		if err = h(err); err != nil {
			return err
		}
	}
	return nil
}

func (ks Keys) Apply(e *twikutil.Executer) error {
	for _, k := range ks {
		err := k.Apply(e)
		if err != nil {
			return err
		}
	}
	return nil
}

// ApplyOrNil applies all keys, and keys that are read-only are
// still set to nil.
func (ks Keys) ApplyOrNil(e *twikutil.Executer) error {
	for _, k := range ks {
		err := k.ApplyOrNil(e)
		if err != nil {
			return err
		}
	}
	return nil
}

func (ks Keys) Clobber(e *twikutil.Executer) error {
	for _, k := range ks {
		err := k.Clobber(e)
		if err != nil {
			return err
		}
	}
	return nil
}

// }}}

type Key struct {
	name  string
	desc  string
	mode  Mode
	typer interface{}
	val   interface{}
}

// Must panics if err is not nil and returns the key otherwise.
func Must(k *Key, err error) *Key {
	if err != nil {
		panic(err)
	}
	return k
}

func New(name string, typer, def interface{}, m Mode, desc string) (*Key, error) {
	if !IsTyper(typer) {
		return nil, ErrNotTyper
	}
	k := &Key{
		name:  name,
		desc:  desc,
		mode:  m,
		typer: typer,
	}
	return k, k.Set(def)
}

func NewAuto(name string, def interface{}, m Mode, desc string) (*Key, error) {
	if def == nil {
		return nil, ErrMissingValue
	}
	return &Key{
		name:  name,
		desc:  desc,
		mode:  m,
		typer: reflect.TypeOf(def),
		val:   def,
	}, nil
}

func (k Key) Empty() bool     { return k.val == nil }
func (k Key) Type() string    { return k.typer.(fmt.Stringer).String() }
func (k Key) Name() string    { return k.name }
func (k Key) Desc() string    { return k.desc }
func (k Key) Mode() Mode      { return k.mode }
func (k *Key) SetMode(m Mode) { k.mode = m }

// Get returns a value that is either nil or guaranteed to conform to the defined type.
func (k Key) Get() interface{} { return k.val }

func (k Key) GetOr(def interface{}) interface{} {
	if k.val == nil {
		return def
	}
	return k.val
}

func (k *Key) Set(v interface{}) (err error) {
	// If v is nil, then we're effectively deleting the stored value.
	if v != nil {
		v, err = Check(k.name, k.typer, v)
		if err != nil {
			return err
		}
	}

	k.val = v
	return nil
}

func (k *Key) Acquire(e *twikutil.Executer) error {
	if k.mode&Read == 0 {
		// This key does not want to be updated.
		return nil
	}

	if !e.Has(k.name) {
		if k.mode&Required != 0 {
			return &RequiredError{k.name}
		}
		return nil
	}

	v, _ := e.Get(k.name)
	return k.Set(v)
}

func (k *Key) Apply(e *twikutil.Executer) error {
	if k.mode&Write == 0 {
		return nil
	}
	if !e.Has(k.name) {
		return e.Set(k.name, k.val)
	}
	return nil
}

func (k *Key) ApplyOrNil(e *twikutil.Executer) error {
	if k.mode&Write == 0 {
		return e.Set(k.name, nil)
	}
	if !e.Has(k.name) {
		return e.Set(k.name, k.val)
	}
	return nil
}

func (k *Key) Clobber(e *twikutil.Executer) error {
	if k.mode&Write == 0 {
		return nil
	}

	return e.Set(k.name, k.val)
}

func Check(name string, typer interface{}, v interface{}) (interface{}, error) {
	chkType := func(t reflect.Type) error {
		vt := reflect.TypeOf(v)
		if t.Kind() == reflect.Interface {
			if !vt.Implements(t) {
				return ImplementsError{name, vt, t.String()}
			}
		} else if vt != t {
			return NewTypeError(name, vt, t)
		}
		return nil
	}

	switch t := typer.(type) {
	case reflect.Type:
		return v, chkType(t)
	case Typer:
		v, err := t.Coerce(v)
		if err != nil {
			switch et := err.(type) {
			case *TypeError:
				if et.Name == "" {
					et.Name = name
				}
				return v, et
			case *ImplementsError:
				if et.Name == "" {
					et.Name = name
				}
				return v, et
			}
		}
		return v, err
	default:
		return nil, ErrNotTyper
	}

}
<|endoftext|>


<commit_before>package keyring

type Keyring interface {
	Get(service, key string) ([]byte, error)
	Set(service, key string, secret []byte) error
	Remove(service, key string) error
}

var DefaultKeyring Keyring
<commit_msg>Cache session credentials in keyring<commit_after>package keyring

import "encoding/json"

type Keyring interface {
	Get(service, key string) ([]byte, error)
	Set(service, key string, secret []byte) error
	Remove(service, key string) error
}

func Marshal(k Keyring, service, key string, obj interface{}) error {
	bytes, err := json.Marshal(obj)
	if err != nil {
		return err
	}
	return k.Set(service, key, bytes)
}

func Unmarshal(k Keyring, service, key string, obj interface{}) error {
	data, err := k.Get(service, key)
	if err != nil {
		return err
	}
	if err = json.Unmarshal(data, obj); err != nil {
		return err
	}
	return nil
}

var DefaultKeyring Keyring
<|endoftext|>


<commit_before>// Package knn implements a K Nearest Neighbors object, capable of both classification
// and regression. It accepts data in the form of a slice of float64s, which are then reshaped
// into a X by Y matrix.
package knn

import (
	"github.com/gonum/matrix/mat64"
	base "github.com/sjwhitworth/golearn/base"
	pairwiseMetrics "github.com/sjwhitworth/golearn/metrics/pairwise"
	util "github.com/sjwhitworth/golearn/utilities"
)

// A KNNClassifier consists of a data matrix, associated labels in the same order as the matrix, and a distance function.
// The accepted distance functions at this time are 'euclidean' and 'manhattan'.
type KNNClassifier struct {
	base.BaseEstimator
	TrainingData      *base.Instances
	DistanceFunc      string
	NearestNeighbours int
}

// NewKnnClassifier returns a new classifier
func NewKnnClassifier(distfunc string, neighbours int) *KNNClassifier {
	KNN := KNNClassifier{}
	KNN.DistanceFunc = distfunc
	KNN.NearestNeighbours = neighbours
	return &KNN
}

// Fit stores the training data for later
func (KNN *KNNClassifier) Fit(trainingData *base.Instances) {
	KNN.TrainingData = trainingData
}

// PredictOne returns a classification for the vector, based on a vector input, using the KNN algorithm.
// See http://en.wikipedia.org/wiki/K-nearest_neighbors_algorithm.
func (KNN *KNNClassifier) PredictOne(vector []float64) string {

	rows := KNN.TrainingData.Rows
	rownumbers := make(map[int]float64)
	labels := make([]string, 0)
	maxmap := make(map[string]int)

	convertedVector := util.FloatsToMatrix(vector)

	// Check what distance function we are using
	switch KNN.DistanceFunc {
	case "euclidean":
		{
			euclidean := pairwiseMetrics.NewEuclidean()
			for i := 0; i < rows; i++ {
				row := KNN.TrainingData.GetRowVectorWithoutClass(i)
				rowMat := util.FloatsToMatrix(row)
				distance := euclidean.Distance(rowMat, convertedVector)
				rownumbers[i] = distance
			}
		}
	case "manhattan":
		{
			manhattan := pairwiseMetrics.NewEuclidean()
			for i := 0; i < rows; i++ {
				row := KNN.TrainingData.GetRowVectorWithoutClass(i)
				rowMat := util.FloatsToMatrix(row)
				distance := manhattan.Distance(rowMat, convertedVector)
				rownumbers[i] = distance
			}
		}
	}

	sorted := util.SortIntMap(rownumbers)
	values := sorted[:KNN.NearestNeighbours]

	for _, elem := range values {
		label := KNN.TrainingData.GetClass(elem)
		labels = append(labels, label)

		if _, ok := maxmap[label]; ok {
			maxmap[label]++
		} else {
			maxmap[label] = 1
		}
	}

	sortedlabels := util.SortStringMap(maxmap)
	label := sortedlabels[0]

	return label
}

func (KNN *KNNClassifier) Predict(what *base.Instances) *base.Instances {
	ret := what.GeneratePredictionVector()
	for i := 0; i < what.Rows; i++ {
		ret.SetAttrStr(i, 0, KNN.PredictOne(what.GetRowVectorWithoutClass(i)))
	}
	return ret
}

// A KNNRegressor consists of a data matrix, associated result variables in the same order as the matrix, and a name.
type KNNRegressor struct {
	base.BaseEstimator
	Values       []float64
	DistanceFunc string
}

// NewKnnRegressor mints a new classifier.
func NewKnnRegressor(distfunc string) *KNNRegressor {
	KNN := KNNRegressor{}
	KNN.DistanceFunc = distfunc
	return &KNN
}

func (KNN *KNNRegressor) Fit(values []float64, numbers []float64, rows int, cols int) {
	if rows != len(values) {
		panic(mat64.ErrShape)
	}

	KNN.Data = mat64.NewDense(rows, cols, numbers)
	KNN.Values = values
}

func (KNN *KNNRegressor) Predict(vector *mat64.Dense, K int) float64 {
	// Get the number of rows
	rows, _ := KNN.Data.Dims()
	rownumbers := make(map[int]float64)
	labels := make([]float64, 0)

	// Check what distance function we are using
	switch KNN.DistanceFunc {
	case "euclidean":
		{
			euclidean := pairwiseMetrics.NewEuclidean()
			for i := 0; i < rows; i++ {
				row := KNN.Data.RowView(i)
				rowMat := util.FloatsToMatrix(row)
				distance := euclidean.Distance(rowMat, vector)
				rownumbers[i] = distance
			}
		}
	case "manhattan":
		{
			manhattan := pairwiseMetrics.NewEuclidean()
			for i := 0; i < rows; i++ {
				row := KNN.Data.RowView(i)
				rowMat := util.FloatsToMatrix(row)
				distance := manhattan.Distance(rowMat, vector)
				rownumbers[i] = distance
			}
		}
	}

	sorted := util.SortIntMap(rownumbers)
	values := sorted[:K]

	var sum float64
	for _, elem := range values {
		value := KNN.Values[elem]
		labels = append(labels, value)
		sum += value
	}

	average := sum / float64(K)
	return average
}
<commit_msg>When the user picks manhattan distance, we should actually use the Manhattan distance function. Also slight refactor to make the code more DRY.<commit_after>// Package knn implements a K Nearest Neighbors object, capable of both classification
// and regression. It accepts data in the form of a slice of float64s, which are then reshaped
// into a X by Y matrix.
package knn

import (
	"github.com/gonum/matrix/mat64"
	base "github.com/sjwhitworth/golearn/base"
	pairwiseMetrics "github.com/sjwhitworth/golearn/metrics/pairwise"
	util "github.com/sjwhitworth/golearn/utilities"
)

// A KNNClassifier consists of a data matrix, associated labels in the same order as the matrix, and a distance function.
// The accepted distance functions at this time are 'euclidean' and 'manhattan'.
type KNNClassifier struct {
	base.BaseEstimator
	TrainingData      *base.Instances
	DistanceFunc      string
	NearestNeighbours int
}

// NewKnnClassifier returns a new classifier
func NewKnnClassifier(distfunc string, neighbours int) *KNNClassifier {
	KNN := KNNClassifier{}
	KNN.DistanceFunc = distfunc
	KNN.NearestNeighbours = neighbours
	return &KNN
}

// Fit stores the training data for later
func (KNN *KNNClassifier) Fit(trainingData *base.Instances) {
	KNN.TrainingData = trainingData
}

// PredictOne returns a classification for the vector, based on a vector input, using the KNN algorithm.
// See http://en.wikipedia.org/wiki/K-nearest_neighbors_algorithm.
func (KNN *KNNClassifier) PredictOne(vector []float64) string {

	rows := KNN.TrainingData.Rows
	rownumbers := make(map[int]float64)
	labels := make([]string, 0)
	maxmap := make(map[string]int)

	convertedVector := util.FloatsToMatrix(vector)

	// Check what distance function we are using
	var distanceFunc pairwiseMetrics.PairwiseDistanceFunc
	switch KNN.DistanceFunc {
	case "euclidean":
		distanceFunc = pairwiseMetrics.NewEuclidean()
	case "manhattan":
		distanceFunc = pairwiseMetrics.NewManhattan()
	default:
		panic("unsupported distance function")
	}

	for i := 0; i < rows; i++ {
		row := KNN.Data.RowView(i)
		rowMat := util.FloatsToMatrix(row)
		distance := distanceFunc.Distance(rowMat, vector)
		rownumbers[i] = distance
	}

	sorted := util.SortIntMap(rownumbers)
	values := sorted[:KNN.NearestNeighbours]

	for _, elem := range values {
		label := KNN.TrainingData.GetClass(elem)
		labels = append(labels, label)

		if _, ok := maxmap[label]; ok {
			maxmap[label]++
		} else {
			maxmap[label] = 1
		}
	}

	sortedlabels := util.SortStringMap(maxmap)
	label := sortedlabels[0]

	return label
}

func (KNN *KNNClassifier) Predict(what *base.Instances) *base.Instances {
	ret := what.GeneratePredictionVector()
	for i := 0; i < what.Rows; i++ {
		ret.SetAttrStr(i, 0, KNN.PredictOne(what.GetRowVectorWithoutClass(i)))
	}
	return ret
}

// A KNNRegressor consists of a data matrix, associated result variables in the same order as the matrix, and a name.
type KNNRegressor struct {
	base.BaseEstimator
	Values       []float64
	DistanceFunc string
}

// NewKnnRegressor mints a new classifier.
func NewKnnRegressor(distfunc string) *KNNRegressor {
	KNN := KNNRegressor{}
	KNN.DistanceFunc = distfunc
	return &KNN
}

func (KNN *KNNRegressor) Fit(values []float64, numbers []float64, rows int, cols int) {
	if rows != len(values) {
		panic(mat64.ErrShape)
	}

	KNN.Data = mat64.NewDense(rows, cols, numbers)
	KNN.Values = values
}

func (KNN *KNNRegressor) Predict(vector *mat64.Dense, K int) float64 {
	// Get the number of rows
	rows, _ := KNN.Data.Dims()
	rownumbers := make(map[int]float64)
	labels := make([]float64, 0)

	// Check what distance function we are using
	var distanceFunc pairwiseMetrics.PairwiseDistanceFunc
	switch KNN.DistanceFunc {
	case "euclidean":
		distanceFunc = pairwiseMetrics.NewEuclidean()
	case "manhattan":
		distanceFunc = pairwiseMetrics.NewManhattan()
	default:
		panic("unsupported distance function")
	}

	for i := 0; i < rows; i++ {
		row := KNN.Data.RowView(i)
		rowMat := util.FloatsToMatrix(row)
		distance := distanceFunc.Distance(rowMat, vector)
		rownumbers[i] = distance
	}

	sorted := util.SortIntMap(rownumbers)
	values := sorted[:K]

	var sum float64
	for _, elem := range values {
		value := KNN.Values[elem]
		labels = append(labels, value)
		sum += value
	}

	average := sum / float64(K)
	return average
}
<|endoftext|>


<commit_before>package kvs

import (
	"bytes"
	"compress/gzip"
	"encoding/gob"
	"fmt"
	"io/ioutil"
	"time"

	"golang.org/x/net/context"

	"google.golang.org/appengine/datastore"
	"google.golang.org/appengine/memcache"
)

const kind = "kvs"

var NotFound = fmt.Errorf("Key-value pair was not found")

// use App Engine's datastore as a simple key-value store

type KV struct {
	Key     string `datastore:",noindex"`
	Value   []byte `datastore:",noindex"`
	Expires time.Time

	Ttl time.Duration `datastore:"-"` // convenient alternative to Expires
}

// Find looks for an existing key-value pair.  Returns
// NotFound if the key does not exist.
func Find(c context.Context, k string) (*KV, error) {
	// is the kv in memcache?
	kv := new(KV)
	memcacheKey := memKey(k)
	item, err := memcache.Get(c, memcacheKey)
	if err == nil {
		kv.Key = k
		kv.Value = item.Value
		return kv, nil
	}

	// nope, look in the datastore
	key := datastore.NewKey(c, kind, k, 0, nil)
	err = datastore.Get(c, key, kv)
	if err == datastore.ErrNoSuchEntity {
		return nil, NotFound
	}
	if err != nil {
		return nil, err
	}
	if !kv.Expires.IsZero() && kv.Expires.Before(time.Now()) {
		// key has expired. pretend it doesn't exist
		return nil, NotFound
	}

	// store result in memcache for later
	item = &memcache.Item{
		Key:   memcacheKey,
		Value: kv.Value,
	}
	if !kv.Expires.IsZero() {
		item.Expiration = kv.Expires.Sub(time.Now())
	}
	err = memcache.Set(c, item)
	_ = err // memcache is an optimization. ignore its errors.

	return kv, nil
}

func (kv *KV) datastoreKey(c context.Context) *datastore.Key {
	return datastore.NewKey(c, kind, kv.Key, 0, nil)
}

// Put stores a key-value pair until its expiration.
func (kv *KV) Put(c context.Context) error {
	// prepare a memcache item for later
	memcacheKey := memKey(kv.Key)
	item := &memcache.Item{
		Key:   memcacheKey,
		Value: kv.Value,
	}

	// calculate key-value expiration time
	if kv.Ttl > 0 {
		item.Expiration = kv.Ttl
		kv.Expires = time.Now().Add(kv.Ttl)
		kv.Ttl = 0
	} else if !kv.Expires.IsZero() {
		item.Expiration = kv.Expires.Sub(time.Now())
	}

	// store kv into datastore for permanent storage
	_, err := datastore.Put(c, kv.datastoreKey(c), kv)
	if err != nil {
		return err
	}

	// cache kv for faster access next time
	err = memcache.Set(c, item)
	_ = err // memcache is an optimization. ignore errors

	return nil
}

// Remove a rule in the datastore
func (kv *KV) Delete(c context.Context) error {
	// delete from datastore
	err := datastore.Delete(c, kv.datastoreKey(c))
	if err != nil {
		return err
	}

	// delete from memcache too
	err = memcache.Delete(c, memKey(kv.Key))
	_ = err // memcache is an optimization. ignore errors.
	return nil
}

// Compress rewrites the Value field by compressing it with gzip.
func (kv *KV) Compress() error {
	var buf bytes.Buffer
	w := gzip.NewWriter(&buf)
	_, err := w.Write(kv.Value)
	if err != nil {
		return err
	}
	err = w.Close()
	if err != nil {
		return err
	}

	kv.Value = buf.Bytes()
	return nil
}

// Decompress rewrites the Value field by decompressing it with gzip.
func (kv *KV) Decompress() error {
	buf := bytes.NewBuffer(kv.Value)
	r, err := gzip.NewReader(buf)
	if err != nil {
		return err
	}
	val, err := ioutil.ReadAll(r)
	if err != nil {
		return err
	}

	kv.Value = val
	return nil
}

// Encode sets the Value field by gob encoding a Go value.
func (kv *KV) Encode(x interface{}) error {
	var buf bytes.Buffer
	err := gob.NewEncoder(&buf).Encode(x)
	if err != nil {
		return err
	}

	kv.Value = buf.Bytes()
	return nil
}

// Decode extracts the Value field by gob decoding into a Go value.
func (kv *KV) Decode(x interface{}) error {
	buf := bytes.NewBuffer(kv.Value)
	return gob.NewDecoder(buf).Decode(x)
}

// returns a key for use with memcache
func memKey(key string) string {
	return fmt.Sprintf("%s: %s", kind, key)
}
<commit_msg>Add kvs.CollectGarbage<commit_after>package kvs

import (
	"bytes"
	"compress/gzip"
	"encoding/gob"
	"errors"
	"fmt"
	"io/ioutil"
	"time"

	"golang.org/x/net/context"

	"google.golang.org/appengine/datastore"
	"google.golang.org/appengine/memcache"
)

const kind = "kvs"

var NotFound = fmt.Errorf("Key-value pair was not found")

// use App Engine's datastore as a simple key-value store

type KV struct {
	Key     string `datastore:",noindex"`
	Value   []byte `datastore:",noindex"`
	Expires time.Time

	Ttl time.Duration `datastore:"-"` // convenient alternative to Expires
}

// Find looks for an existing key-value pair.  Returns
// NotFound if the key does not exist.
func Find(c context.Context, k string) (*KV, error) {
	// is the kv in memcache?
	kv := new(KV)
	memcacheKey := memKey(k)
	item, err := memcache.Get(c, memcacheKey)
	if err == nil {
		kv.Key = k
		kv.Value = item.Value
		return kv, nil
	}

	// nope, look in the datastore
	key := datastore.NewKey(c, kind, k, 0, nil)
	err = datastore.Get(c, key, kv)
	if err == datastore.ErrNoSuchEntity {
		return nil, NotFound
	}
	if err != nil {
		return nil, err
	}
	if !kv.Expires.IsZero() && kv.Expires.Before(time.Now()) {
		// key has expired. pretend it doesn't exist
		return nil, NotFound
	}

	// store result in memcache for later
	item = &memcache.Item{
		Key:   memcacheKey,
		Value: kv.Value,
	}
	if !kv.Expires.IsZero() {
		item.Expiration = kv.Expires.Sub(time.Now())
	}
	err = memcache.Set(c, item)
	_ = err // memcache is an optimization. ignore its errors.

	return kv, nil
}

func (kv *KV) datastoreKey(c context.Context) *datastore.Key {
	return datastore.NewKey(c, kind, kv.Key, 0, nil)
}

// Put stores a key-value pair until its expiration.
func (kv *KV) Put(c context.Context) error {
	// prepare a memcache item for later
	memcacheKey := memKey(kv.Key)
	item := &memcache.Item{
		Key:   memcacheKey,
		Value: kv.Value,
	}

	// calculate key-value expiration time
	if kv.Ttl > 0 {
		item.Expiration = kv.Ttl
		kv.Expires = time.Now().Add(kv.Ttl)
		kv.Ttl = 0
	} else if !kv.Expires.IsZero() {
		item.Expiration = kv.Expires.Sub(time.Now())
	}

	// store kv into datastore for permanent storage
	_, err := datastore.Put(c, kv.datastoreKey(c), kv)
	if err != nil {
		return err
	}

	// cache kv for faster access next time
	err = memcache.Set(c, item)
	_ = err // memcache is an optimization. ignore errors

	return nil
}

// Remove a rule in the datastore
func (kv *KV) Delete(c context.Context) error {
	// delete from datastore
	err := datastore.Delete(c, kv.datastoreKey(c))
	if err != nil {
		return err
	}

	// delete from memcache too
	err = memcache.Delete(c, memKey(kv.Key))
	_ = err // memcache is an optimization. ignore errors.
	return nil
}

// Compress rewrites the Value field by compressing it with gzip.
func (kv *KV) Compress() error {
	var buf bytes.Buffer
	w := gzip.NewWriter(&buf)
	_, err := w.Write(kv.Value)
	if err != nil {
		return err
	}
	err = w.Close()
	if err != nil {
		return err
	}

	kv.Value = buf.Bytes()
	return nil
}

// Decompress rewrites the Value field by decompressing it with gzip.
func (kv *KV) Decompress() error {
	buf := bytes.NewBuffer(kv.Value)
	r, err := gzip.NewReader(buf)
	if err != nil {
		return err
	}
	val, err := ioutil.ReadAll(r)
	if err != nil {
		return err
	}

	kv.Value = val
	return nil
}

// Encode sets the Value field by gob encoding a Go value.
func (kv *KV) Encode(x interface{}) error {
	var buf bytes.Buffer
	err := gob.NewEncoder(&buf).Encode(x)
	if err != nil {
		return err
	}

	kv.Value = buf.Bytes()
	return nil
}

// Decode extracts the Value field by gob decoding into a Go value.
func (kv *KV) Decode(x interface{}) error {
	buf := bytes.NewBuffer(kv.Value)
	return gob.NewDecoder(buf).Decode(x)
}

// returns a key for use with memcache
func memKey(key string) string {
	return fmt.Sprintf("%s: %s", kind, key)
}

var CollectGarbageTimeout = errors.New("CollectGarbage timed out")

// CollectGarbage deletes expired kv entities from the datastore.  It tries to
// spend no more than the allotted time on this task. This function should be
// called regularly to prevent expired kvs from accumulating.
//
// If the timeout is reached, returns CollectGarbageTimeout regardless how many
// entities were expired before then.
func CollectGarbage(c context.Context, ttl time.Duration) error {
	quittingTime := time.Now().Add(ttl)

	q := datastore.NewQuery(kind).
		Filter("Expires<", time.Now()).
		Order("Expires").
		Limit(400).
		KeysOnly()
	for {
		if time.Now().After(quittingTime) {
			return CollectGarbageTimeout
		}

		keys, err := q.GetAll(c, nil)
		if len(keys) > 0 {
			err = datastore.DeleteMulti(c, keys)
			// don't have to clear memcache. it expires on its own
		}
		if err != nil {
			return err
		}
		if len(keys) == 0 {
			break
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright (C) 2012-2014 by krepa098. All rights reserved.
// Use of this source code is governed by a zlib-style
// license that can be found in the license.txt file.

package gosfml2

// #include <SFML/Graphics/Text.h>
// #include <SFML/Graphics/RenderWindow.h>
// #include <SFML/Graphics/RenderTexture.h>
// #include <stdlib.h>
import "C"

import (
	"runtime"
	"unsafe"
)

/////////////////////////////////////
///		CONSTS
/////////////////////////////////////

const (
	TextRegular    = C.sfTextRegular    ///< Regular characters, no style
	TextBold       = C.sfTextBold       ///< Characters are bold
	TextItalic     = C.sfTextItalic     ///< Characters are in italic
	TextUnderlined = C.sfTextUnderlined ///< Characters are underlined
)

type TextStyle uint32

/////////////////////////////////////
///		STRUCTS
/////////////////////////////////////

type Text struct {
	cptr *C.sfText
	font *Font
}

/////////////////////////////////////
///		FUNCS
/////////////////////////////////////

// Create a new text with a given font (can be nil).
func NewText(font *Font) (*Text, error) {
	if cptr := C.sfText_create(); cptr != nil {
		text := &Text{cptr: cptr}
		runtime.SetFinalizer(text, (*Text).destroy)
		text.SetFont(font)

		return text, nil
	}

	return nil, genericError
}

// Destroy an existing text
func (this *Text) destroy() {
	C.sfText_destroy(this.cptr)
}

// Copy an existing text
func (this *Text) Copy() *Text {
	text := &Text{C.sfText_copy(this.cptr), this.font}
	runtime.SetFinalizer(text, (*Text).destroy)
	return text
}

// Set the position of a text
//
// This function completely overwrites the previous position.
// See Text.Move to apply an offset based on the previous position instead.
// The default position of a text Text object is (0, 0).
//
// 	position: New position
func (this *Text) SetPosition(pos Vector2f) {
	C.sfText_setPosition(this.cptr, pos.toC())
}

// Set the scale factors of a text
//
// This function completely overwrites the previous scale.
// See Text.Scale to add a factor based on the previous scale instead.
// The default scale of a text Text object is (1, 1).
//
// 	scale: New scale factors
func (this *Text) SetScale(scale Vector2f) {
	C.sfText_setScale(this.cptr, scale.toC())
}

// Set the local origin of a text
//
// The origin of an object defines the center point for
// all transformations (position, scale, rotation).
// The coordinates of this point must be relative to the
// top-left corner of the object, and ignore all
// transformations (position, scale, rotation).
// The default origin of a text object is (0, 0).
//
// 	origin: New origin
func (this *Text) SetOrigin(orig Vector2f) {
	C.sfText_setOrigin(this.cptr, orig.toC())
}

// Set the orientation of a text
//
// This function completely overwrites the previous rotation.
// See sfText_rotate to add an angle based on the previous rotation instead.
// The default rotation of a text Text object is 0.
//
// 	rot: New rotation, in degrees
func (this *Text) SetRotation(rot float32) {
	C.sfText_setRotation(this.cptr, C.float(rot))
}

// Move a text by a given offset
//
// This function adds to the current position of the object,
// unlike Text.SetPosition which overwrites it.
//
// 	offset: Offset
func (this *Text) Move(offset Vector2f) {
	C.sfText_move(this.cptr, offset.toC())
}

// Scale a text
//
// This function multiplies the current scale of the object,
// unlike Text.SetScale which overwrites it.
//
// 	factor: Scale factors
func (this *Text) Scale(factor Vector2f) {
	C.sfText_scale(this.cptr, factor.toC())
}

// Rotate a text
//
// This function adds to the current rotation of the object,
// unlike Text.SetRotation which overwrites it.
//
// 	angle: Angle of rotation, in degrees
func (this *Text) Rotate(angle float32) {
	C.sfText_rotate(this.cptr, C.float(angle))
}

// Get the orientation of a text
//
// The rotation is always in the range [0, 360].
func (this *Text) GetRotation() float32 {
	return float32(C.sfText_getRotation(this.cptr))
}

// Get the position of a text
func (this *Text) GetPosition() (pos Vector2f) {
	pos.fromC(C.sfText_getPosition(this.cptr))
	return
}

// Get the current scale of a text
func (this *Text) GetScale() (scale Vector2f) {
	scale.fromC(C.sfText_getScale(this.cptr))
	return
}

// Get the local origin of a text
func (this *Text) GetOrigin() (origin Vector2f) {
	origin.fromC(C.sfText_getOrigin(this.cptr))
	return
}

// Get the combined transform of a text
func (this *Text) GetTransform() (trans Transform) {
	trans.fromC(C.sfText_getTransform(this.cptr))
	return
}

// Get the inverse of the combined transform of a text
func (this *Text) GetInverseTransform() (transform Transform) {
	transform.fromC(C.sfText_getInverseTransform(this.cptr))
	return
}

// Set the string of a text (from a unicode string)
func (this *Text) SetString(text string) {
	runes := strToRunes(text)
	C.sfText_setUnicodeString(this.cptr, (*C.sfUint32)(unsafe.Pointer(&runes[0])))
}

// Set the font of a text
func (this *Text) SetFont(font *Font) {
	C.sfText_setFont(this.cptr, font.toCPtr())
	this.font = font
}

// Set the character size of a text
//
// The default size is 30.
func (this *Text) SetCharacterSize(size uint) {
	C.sfText_setCharacterSize(this.cptr, C.uint(size))
}

// Set the style of a text
//
// You can pass a combination of one or more styles, for
// example TextBold | TextItalic.
// The default style is TextRegular.
func (this *Text) SetStyle(style TextStyle) {
	C.sfText_setStyle(this.cptr, C.sfUint32(style))
}

// Set the global color of a text
//
// By default, the text's color is opaque white.
func (this *Text) SetColor(color Color) {
	C.sfText_setColor(this.cptr, color.toC())
}

// Get the string of a text (returns a unicode string)
func (this *Text) GetString() string {
	cstr := C.sfText_getUnicodeString(this.cptr)
	return utf32CString2Go(cstr)
}

// Get the font used by a text
//
// If the text has no font attached, a nil pointer is returned.
func (this *Text) GetFont() *Font {
	return this.font
}

// Get the size of the characters of a text
func (this *Text) GetCharacterSize() uint {
	return uint(C.sfText_getCharacterSize(this.cptr))
}

// Get the style of a text
func (this *Text) GetStyle() TextStyle {
	return TextStyle(C.sfText_getStyle(this.cptr))
}

// Get the global color of a text
func (this *Text) GetColor() (color Color) {
	color.fromC(C.sfText_getColor(this.cptr))
	return
}

// Return the position of the index-th character in a text
//
// This function computes the visual position of a character
// from its index in the string. The returned position is
// in global coordinates (translation, rotation, scale and
// origin are applied).
// If index is out of range, the position of the end of
// the string is returned.
func (this *Text) FindCharacterPos(index uint) (pos Vector2f) {
	pos.fromC(C.sfText_findCharacterPos(this.cptr, C.size_t(index)))
	return
}

// Get the local bounding rectangle of a text
//
// The returned rectangle is in local coordinates, which means
// that it ignores the transformations (translation, rotation,
// scale, ...) that are applied to the entity.
// In other words, this function returns the bounds of the
// entity in the entity's coordinate system.
func (this *Text) GetLocalBounds() (rect FloatRect) {
	rect.fromC(C.sfText_getLocalBounds(this.cptr))
	return
}

// Get the global bounding rectangle of a text
//
// The returned rectangle is in global coordinates, which means
// that it takes in account the transformations (translation,
// rotation, scale, ...) that are applied to the entity.
// In other words, this function returns the bounds of the
// text in the global 2D world's coordinate system.
func (this *Text) GetGlobalBounds() (rect FloatRect) {
	rect.fromC(C.sfText_getGlobalBounds(this.cptr))
	return
}

// Draws a Text on a render target
func (this *Text) Draw(target RenderTarget, renderStates RenderStates) {
	rs := renderStates.toC()
	switch target.(type) {
	case *RenderWindow:
		C.sfRenderWindow_drawText(target.(*RenderWindow).cptr, this.cptr, &rs)
	case *RenderTexture:
		C.sfRenderTexture_drawText(target.(*RenderTexture).cptr, this.cptr, &rs)
	}
}
<commit_msg>added TextStrikeThrough<commit_after>// Copyright (C) 2012-2014 by krepa098. All rights reserved.
// Use of this source code is governed by a zlib-style
// license that can be found in the license.txt file.

package gosfml2

// #include <SFML/Graphics/Text.h>
// #include <SFML/Graphics/RenderWindow.h>
// #include <SFML/Graphics/RenderTexture.h>
// #include <stdlib.h>
import "C"

import (
	"runtime"
	"unsafe"
)

/////////////////////////////////////
///		CONSTS
/////////////////////////////////////

const (
	TextRegular       = C.sfTextRegular       ///< Regular characters, no style
	TextBold          = C.sfTextBold          ///< Characters are bold
	TextItalic        = C.sfTextItalic        ///< Characters are in italic
	TextUnderlined    = C.sfTextUnderlined    ///< Characters are underlined
	TextStrikeThrough = C.sfTextStrikeThrough ///< Strike through characters
)

type TextStyle int

/////////////////////////////////////
///		STRUCTS
/////////////////////////////////////

type Text struct {
	cptr *C.sfText
	font *Font
}

/////////////////////////////////////
///		FUNCS
/////////////////////////////////////

// Create a new text with a given font (can be nil).
func NewText(font *Font) (*Text, error) {
	if cptr := C.sfText_create(); cptr != nil {
		text := &Text{cptr: cptr}
		runtime.SetFinalizer(text, (*Text).destroy)
		text.SetFont(font)

		return text, nil
	}

	return nil, genericError
}

// Destroy an existing text
func (this *Text) destroy() {
	C.sfText_destroy(this.cptr)
}

// Copy an existing text
func (this *Text) Copy() *Text {
	text := &Text{C.sfText_copy(this.cptr), this.font}
	runtime.SetFinalizer(text, (*Text).destroy)
	return text
}

// Set the position of a text
//
// This function completely overwrites the previous position.
// See Text.Move to apply an offset based on the previous position instead.
// The default position of a text Text object is (0, 0).
//
// 	position: New position
func (this *Text) SetPosition(pos Vector2f) {
	C.sfText_setPosition(this.cptr, pos.toC())
}

// Set the scale factors of a text
//
// This function completely overwrites the previous scale.
// See Text.Scale to add a factor based on the previous scale instead.
// The default scale of a text Text object is (1, 1).
//
// 	scale: New scale factors
func (this *Text) SetScale(scale Vector2f) {
	C.sfText_setScale(this.cptr, scale.toC())
}

// Set the local origin of a text
//
// The origin of an object defines the center point for
// all transformations (position, scale, rotation).
// The coordinates of this point must be relative to the
// top-left corner of the object, and ignore all
// transformations (position, scale, rotation).
// The default origin of a text object is (0, 0).
//
// 	origin: New origin
func (this *Text) SetOrigin(orig Vector2f) {
	C.sfText_setOrigin(this.cptr, orig.toC())
}

// Set the orientation of a text
//
// This function completely overwrites the previous rotation.
// See sfText_rotate to add an angle based on the previous rotation instead.
// The default rotation of a text Text object is 0.
//
// 	rot: New rotation, in degrees
func (this *Text) SetRotation(rot float32) {
	C.sfText_setRotation(this.cptr, C.float(rot))
}

// Move a text by a given offset
//
// This function adds to the current position of the object,
// unlike Text.SetPosition which overwrites it.
//
// 	offset: Offset
func (this *Text) Move(offset Vector2f) {
	C.sfText_move(this.cptr, offset.toC())
}

// Scale a text
//
// This function multiplies the current scale of the object,
// unlike Text.SetScale which overwrites it.
//
// 	factor: Scale factors
func (this *Text) Scale(factor Vector2f) {
	C.sfText_scale(this.cptr, factor.toC())
}

// Rotate a text
//
// This function adds to the current rotation of the object,
// unlike Text.SetRotation which overwrites it.
//
// 	angle: Angle of rotation, in degrees
func (this *Text) Rotate(angle float32) {
	C.sfText_rotate(this.cptr, C.float(angle))
}

// Get the orientation of a text
//
// The rotation is always in the range [0, 360].
func (this *Text) GetRotation() float32 {
	return float32(C.sfText_getRotation(this.cptr))
}

// Get the position of a text
func (this *Text) GetPosition() (pos Vector2f) {
	pos.fromC(C.sfText_getPosition(this.cptr))
	return
}

// Get the current scale of a text
func (this *Text) GetScale() (scale Vector2f) {
	scale.fromC(C.sfText_getScale(this.cptr))
	return
}

// Get the local origin of a text
func (this *Text) GetOrigin() (origin Vector2f) {
	origin.fromC(C.sfText_getOrigin(this.cptr))
	return
}

// Get the combined transform of a text
func (this *Text) GetTransform() (trans Transform) {
	trans.fromC(C.sfText_getTransform(this.cptr))
	return
}

// Get the inverse of the combined transform of a text
func (this *Text) GetInverseTransform() (transform Transform) {
	transform.fromC(C.sfText_getInverseTransform(this.cptr))
	return
}

// Set the string of a text (from a unicode string)
func (this *Text) SetString(text string) {
	runes := strToRunes(text)
	C.sfText_setUnicodeString(this.cptr, (*C.sfUint32)(unsafe.Pointer(&runes[0])))
}

// Set the font of a text
func (this *Text) SetFont(font *Font) {
	C.sfText_setFont(this.cptr, font.toCPtr())
	this.font = font
}

// Set the character size of a text
//
// The default size is 30.
func (this *Text) SetCharacterSize(size uint) {
	C.sfText_setCharacterSize(this.cptr, C.uint(size))
}

// Set the style of a text
//
// You can pass a combination of one or more styles, for
// example TextBold | TextItalic.
// The default style is TextRegular.
func (this *Text) SetStyle(style TextStyle) {
	C.sfText_setStyle(this.cptr, C.sfUint32(style))
}

// Set the global color of a text
//
// By default, the text's color is opaque white.
func (this *Text) SetColor(color Color) {
	C.sfText_setColor(this.cptr, color.toC())
}

// Get the string of a text (returns a unicode string)
func (this *Text) GetString() string {
	cstr := C.sfText_getUnicodeString(this.cptr)
	return utf32CString2Go(cstr)
}

// Get the font used by a text
//
// If the text has no font attached, a nil pointer is returned.
func (this *Text) GetFont() *Font {
	return this.font
}

// Get the size of the characters of a text
func (this *Text) GetCharacterSize() uint {
	return uint(C.sfText_getCharacterSize(this.cptr))
}

// Get the style of a text
func (this *Text) GetStyle() TextStyle {
	return TextStyle(C.sfText_getStyle(this.cptr))
}

// Get the global color of a text
func (this *Text) GetColor() (color Color) {
	color.fromC(C.sfText_getColor(this.cptr))
	return
}

// Return the position of the index-th character in a text
//
// This function computes the visual position of a character
// from its index in the string. The returned position is
// in global coordinates (translation, rotation, scale and
// origin are applied).
// If index is out of range, the position of the end of
// the string is returned.
func (this *Text) FindCharacterPos(index uint) (pos Vector2f) {
	pos.fromC(C.sfText_findCharacterPos(this.cptr, C.size_t(index)))
	return
}

// Get the local bounding rectangle of a text
//
// The returned rectangle is in local coordinates, which means
// that it ignores the transformations (translation, rotation,
// scale, ...) that are applied to the entity.
// In other words, this function returns the bounds of the
// entity in the entity's coordinate system.
func (this *Text) GetLocalBounds() (rect FloatRect) {
	rect.fromC(C.sfText_getLocalBounds(this.cptr))
	return
}

// Get the global bounding rectangle of a text
//
// The returned rectangle is in global coordinates, which means
// that it takes in account the transformations (translation,
// rotation, scale, ...) that are applied to the entity.
// In other words, this function returns the bounds of the
// text in the global 2D world's coordinate system.
func (this *Text) GetGlobalBounds() (rect FloatRect) {
	rect.fromC(C.sfText_getGlobalBounds(this.cptr))
	return
}

// Draws a Text on a render target
func (this *Text) Draw(target RenderTarget, renderStates RenderStates) {
	rs := renderStates.toC()
	switch target.(type) {
	case *RenderWindow:
		C.sfRenderWindow_drawText(target.(*RenderWindow).cptr, this.cptr, &rs)
	case *RenderTexture:
		C.sfRenderTexture_drawText(target.(*RenderTexture).cptr, this.cptr, &rs)
	}
}
<|endoftext|>


<commit_before>package pdf2txt

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"strconv"
	"strings"
)

type document struct {
	catalogs      []*catalog
	pagesList     map[string]*pages
	pageList      map[string]*page
	fonts         map[string]*font
	cmaps         map[string]cmap
	contents      map[string][]textsection
	uncategorized map[string]*object
	root          rootnode
}

type catalog struct {
	Pages string
}

type pages struct {
	Kids   []string
	isNull bool
}

type page struct {
	Fonts    map[name]string
	Contents []string
	Parent   string
}

type font struct {
	Encoding  name
	ToUnicode string
}

// Text extracts text from an io.Reader stream of a PDF file
// and outputs it into a new io.Reader filled with the text
// contained in the PDF file.
func Text(r io.Reader) (io.Reader, error) {
	d, err := parse(r)
	if err != nil {
		return nil, err
	}
	//	if err = d.populate(); err != nil {
	//return nil, err
	//}
	return d.getText()
}

func parse(r io.Reader) (*document, error) {
	catalogs := []*catalog{}
	pagesList := make(map[string]*pages)
	pageList := make(map[string]*page)
	fonts := make(map[string]*font)
	cmaps := make(map[string]cmap)
	contents := make(map[string][]textsection)
	uncategorized := make(map[string]*object)
	var root rootnode

	tchan := make(chan interface{}, 100)
	go tokenize(newBufReader(r), tchan)

	for t := range tchan {
		switch v := t.(type) {
		case rootnode:
			root = v
		case *object:
			oType := v.name("/Type")
			switch oType {
			case "/Catalog":
				catalogs = append(catalogs, &catalog{v.objectref("/Pages").refString})

			case "/Pages":
				pagesList[v.refString] = getPages(v)

			case "/Page":
				pItem := getPage(v)
				pageList[v.refString] = pItem
				for i := range pItem.Contents {
					cref := pItem.Contents[i]
					// contents already available, so get text
					if cObj, ok := uncategorized[cref]; ok {
						if err := cObj.decodeStream(); err != nil {
							return nil, err
						}
						c, err := getTextSections(newMemReader(cObj.stream))
						if err != nil {
							return nil, err
						}
						contents[cref] = c
						delete(uncategorized, cref)

						// haven't seen contents yet, so just flag it for later retrieval
					} else {
						contents[cref] = nil
					}
				}
				if pItem.Parent != "" {
					if pagesItem, ok := pagesList[pItem.Parent]; ok {
						if pagesItem.isNull { // null object so add this reference to the list of kids
							pagesItem.Kids = append(pagesItem.Kids, v.refString)
						}
					} else {
						pagesList[pItem.Parent] = &pages{Kids: []string{v.refString}, isNull: true}
					}
				}

			case "/Font":
				f := getFont(v)
				fonts[v.refString] = f
				if f.ToUnicode != "" {
					// cmap already available, so create
					if u, ok := uncategorized[f.ToUnicode]; ok {
						if err := u.decodeStream(); err != nil {
							return nil, err
						}
						cmap, err := getCmap(newMemReader(u.stream))
						if err != nil {
							return nil, err
						}
						cmaps[f.ToUnicode] = cmap
						delete(uncategorized, f.ToUnicode)

						// haven't seen cmap yet, so just flag for later
					} else {
						cmaps[f.ToUnicode] = nil
					}
				}

			case "/XObject": // we don't need
			case "/FontDescriptor": // we don't need
			default:
				if _, ok := contents[v.refString]; ok { // something has already referenced this as content so save as content
					err := v.decodeStream()
					if err != nil {
						return nil, err
					}
					sections, err := getTextSections(newMemReader(v.stream))
					if err != nil {
						fmt.Println("error getting textsection", err)
						return nil, err // maybe I shouldn't error completely if one page is bad
					}
					contents[v.refString] = sections
				} else if _, ok := cmaps[v.refString]; ok {
					err := v.decodeStream()
					if err != nil {
						return nil, err
					}
					cmap, err := getCmap(newMemReader(v.stream))
					if err != nil {
						fmt.Println("error getting cmap", err)
						return nil, err // maybe I shouldn't error completely if one page is bad
					}
					cmaps[v.refString] = cmap
				} else {
					uncategorized[v.refString] = v
				}
			}
		}
	}
	return &document{catalogs: catalogs, pagesList: pagesList, pageList: pageList, fonts: fonts, cmaps: cmaps,
		contents: contents, uncategorized: uncategorized, root: root}, nil
}

func (d *document) getText() (io.Reader, error) {
	var buf bytes.Buffer
	for _, pages := range d.pagesList { // get pages objects
		for _, pageRef := range pages.Kids { // get page objects
			page := d.pageList[pageRef]
			for _, cref := range page.Contents { // get content
				c := d.contents[cref]
				for sIndex := range c { // get text sections
					section := c[sIndex]
					for ai := range section.textArray {
						item := section.textArray[ai]
						switch t := item.(type) {
						case hexdata:
							font := d.fonts[page.Fonts[section.fontName]]
							var cmap map[hexdata]string
							if font != nil && font.ToUnicode != "" && d.cmaps[font.ToUnicode] != nil {
								cmap = d.cmaps[font.ToUnicode]
							}
							for ci := 0; ci+2 <= len(t); ci += 2 {
								if cmap != nil {
									buf.WriteString(cmap[t[ci:ci+2]])
								} else {
									c, _ := strconv.ParseInt(string(t[ci:ci+2]), 16, 16)
									buf.WriteString(fmt.Sprintf("%c", c))
								}
							}
						case string:
							buf.WriteString(t)
						}
					}
				}
			}
			buf.WriteString("\n")
		}
	}
	return &buf, nil
}

func getPages(o *object) *pages {
	k := o.array("/Kids")
	kids := make([]string, len(k))
	for i := range k {
		if oref, ok := k[i].(*objectref); ok {
			kids[i] = oref.refString
		}
	}
	return &pages{Kids: kids}
}

func getPage(o *object) *page {
	page := page{Fonts: make(map[name]string)}
	if res, ok := o.search("/Resources").(dictionary); ok {
		if fonts, ok := res["/Font"].(dictionary); ok {
			for key, value := range fonts {
				if oref, ok := value.(*objectref); ok {
					page.Fonts[key] = oref.refString
				}
			}
		}
	}
	if c := o.search("/Contents"); c != nil {
		if co, ok := c.(*objectref); ok {
			page.Contents = []string{co.refString}
		} else if ca, ok := c.(array); ok {
			for i := range ca {
				if cao, ok := ca[i].(*objectref); ok {
					page.Contents = append(page.Contents, cao.refString)
				}
			}
		}
	}
	if p := o.objectref("/Parent"); p != nil {
		page.Parent = p.refString
	}
	return &page
}

func getFont(o *object) *font {
	font := font{Encoding: o.name("/Encoding")}
	if u := o.objectref("/ToUnicode"); u != nil {
		font.ToUnicode = u.refString
	}
	return &font
}

func getTextSections(r peekingReader) ([]textsection, error) {
	sections := []textsection{}
	var t textsection
	var font name
	var lastArray array
	var lastText text
	var lastName name

	for {
		item := readNext(r)

		switch v := item.(type) {
		case error:
			if v == io.EOF {
				return sections, nil
			}
			return nil, v

		case token:
			switch v {
			case "BT":
				t = textsection{}
			case "Tf":
				font = lastName
			case "TJ":
				t.textArray = append(t.textArray, lastArray...)
				t.textArray = append(t.textArray, " ")
			case "T*":
				t.textArray = append(t.textArray, "\n")
			case "Tj":
				t.textArray = append(t.textArray, lastText)
			case "ET":
				t.fontName = font // use the current global text state
				sections = append(sections, t)
			}

		case array:
			lastArray = v

		case text:
			lastText = v

		case name:
			lastName = v
		}
	}
}

func getCmap(r peekingReader) (cmap, error) {
	cmap := make(cmap)
	var prev token

	for {
		item := readNext(r)

		switch v := item.(type) {
		case error:
			if v == io.EOF {
				return cmap, nil
			}
			return nil, v

		case token:
			switch v {
			case "begincodespacerange":
				length, _ := strconv.Atoi(string(prev))
				for i := 0; i < length*2; i++ {

				}
			case "beginbfchar":
				bfc, err := readbfchar(r, prev)
				if err != nil {
					return nil, err
				}
				for key, value := range bfc {
					cmap[key] = value
				}
			case "beginbfrange":
				bfr, err := readbfrange(r, prev)
				if err != nil {
					return nil, err
				}
				for key, value := range bfr {
					cmap[key] = value
				}
			case "endcmap":
				return cmap, nil
			default:
				prev = v
			}
		}
	}
}

func readbfchar(r peekingReader, length token) (cmap, error) {
	cmap := make(cmap)
	l, _ := strconv.Atoi(string(length))
	var lastKey hexdata
	for i := 0; i < l*2; i++ {
		item := readNext(r)
		switch v := item.(type) {
		case error:
			return nil, v

		case hexdata:
			switch i % 2 {
			case 0: // first item is the key
				lastKey = v
				cmap[v] = ""
			case 1: // second item is the value
				num, _ := strconv.ParseInt(string(v), 16, 16)
				repl := fmt.Sprintf("%c", num)
				cmap[lastKey] = repl
			}

		default:
			return nil, errors.New("invalid bfchar data")
		}
	}
	return cmap, nil
}

func readbfrange(r peekingReader, length token) (cmap, error) {
	cmap := make(cmap)
	l, _ := strconv.Atoi(string(length))
	var start, end int64
	var digits int

	for i := 0; i < l*3; i++ {
		item := readNext(r)
		switch v := item.(type) {
		case error:
			return nil, v

		case hexdata:
			switch i % 3 {
			case 0: // range start
				digits = len(string(v))
				start, _ = strconv.ParseInt(string(v), 16, 16)
			case 1: // range end
				end, _ = strconv.ParseInt(string(v), 16, 16)
			case 2: // values
				repl, _ := strconv.ParseInt(string(v), 16, 16)
				var count int64
				for i := start; i <= end; i++ {
					format := fmt.Sprintf("%%0%dx", digits) // format for however many digits we originally had
					cmap[hexdata(strings.ToUpper(fmt.Sprintf(format, i)))] = fmt.Sprintf("%c", repl+count)
					count++
				}
			}

		default:
			return nil, errors.New("invalid bfrange data")
		}
	}
	return cmap, nil
}
<commit_msg>refactoring<commit_after>package pdf2txt

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"strconv"
	"strings"
)

type document struct {
	catalogs      []*catalog
	pagesList     map[string]*pages
	pageList      map[string]*page
	fonts         map[string]*font
	cmaps         map[string]cmap
	contents      map[string][]textsection
	uncategorized map[string]*object
	root          rootnode
}

type catalog struct {
	Pages string
}

type pages struct {
	Kids   []string
	isNull bool
}

type page struct {
	Fonts    map[name]string
	Contents []string
	Parent   string
}

type font struct {
	Encoding  name
	ToUnicode string
}

// Text extracts text from an io.Reader stream of a PDF file
// and outputs it into a new io.Reader filled with the text
// contained in the PDF file.
func Text(r io.Reader) (io.Reader, error) {
	d, err := parse(r)
	if err != nil {
		return nil, err
	}
	//	if err = d.populate(); err != nil {
	//return nil, err
	//}
	return d.getText()
}

func parse(r io.Reader) (*document, error) {
	catalogs := []*catalog{}
	pagesList := make(map[string]*pages)
	pageList := make(map[string]*page)
	fonts := make(map[string]*font)
	cmaps := make(map[string]cmap)
	contents := make(map[string][]textsection)
	uncategorized := make(map[string]*object)
	var root rootnode

	tchan := make(chan interface{}, 100)
	go tokenize(newBufReader(r), tchan)

	for t := range tchan {
		switch v := t.(type) {
		case rootnode:
			root = v
		case *object:
			oType := v.name("/Type")
			switch oType {
			case "/Catalog":
				catalogs = append(catalogs, &catalog{v.objectref("/Pages").refString})

			case "/Pages":
				pagesList[v.refString] = getPages(v)

			case "/Page":
				pItem := getPage(v)
				pageList[v.refString] = pItem
				if err := handlePageContents(pItem, contents, uncategorized); err != nil {
					return nil, err
				}
				handlePageParent(pItem, v.refString, pagesList)

			case "/Font":
				f := getFont(v)
				fonts[v.refString] = f
				if err := handleToUnicode(f, cmaps, uncategorized); err != nil {
					return nil, err
				}

			case "/XObject": // we don't need
			case "/FontDescriptor": // we don't need
			default:
				// something has already referenced this as content so save as content
				if _, ok := contents[v.refString]; ok {
					if err := saveContents(v, contents); err != nil {
						return nil, err
					}

					// save cmap
				} else if _, ok := cmaps[v.refString]; ok {
					if err := saveCmap(v, cmaps); err != nil {
						return nil, err
					}
				} else {
					uncategorized[v.refString] = v
				}
			}
		}
	}
	return &document{catalogs: catalogs, pagesList: pagesList, pageList: pageList, fonts: fonts, cmaps: cmaps,
		contents: contents, uncategorized: uncategorized, root: root}, nil
}

func (d *document) getText() (io.Reader, error) {
	var buf bytes.Buffer
	for _, pages := range d.pagesList { // get pages objects
		for _, pageRef := range pages.Kids { // get page objects
			page := d.pageList[pageRef]
			for _, cref := range page.Contents { // get content
				c := d.contents[cref]
				for sIndex := range c { // get text sections
					section := c[sIndex]
					for ai := range section.textArray {
						item := section.textArray[ai]
						switch t := item.(type) {
						case hexdata:
							font := d.fonts[page.Fonts[section.fontName]]
							var cmap map[hexdata]string
							if font != nil && font.ToUnicode != "" && d.cmaps[font.ToUnicode] != nil {
								cmap = d.cmaps[font.ToUnicode]
							}
							for ci := 0; ci+2 <= len(t); ci += 2 {
								if cmap != nil {
									buf.WriteString(cmap[t[ci:ci+2]])
								} else {
									c, _ := strconv.ParseInt(string(t[ci:ci+2]), 16, 16)
									buf.WriteString(fmt.Sprintf("%c", c))
								}
							}
						case string:
							buf.WriteString(t)
						}
					}
				}
			}
			buf.WriteString("\n")
		}
	}
	return &buf, nil
}

func getPages(o *object) *pages {
	k := o.array("/Kids")
	kids := make([]string, len(k))
	for i := range k {
		if oref, ok := k[i].(*objectref); ok {
			kids[i] = oref.refString
		}
	}
	return &pages{Kids: kids}
}

func getPage(o *object) *page {
	page := page{Fonts: make(map[name]string)}
	if res, ok := o.search("/Resources").(dictionary); ok {
		if fonts, ok := res["/Font"].(dictionary); ok {
			for key, value := range fonts {
				if oref, ok := value.(*objectref); ok {
					page.Fonts[key] = oref.refString
				}
			}
		}
	}
	// Contents can be either a single object reference
	// or an array of object references
	if c := o.search("/Contents"); c != nil {
		if co, ok := c.(*objectref); ok {
			page.Contents = []string{co.refString}
		} else if ca, ok := c.(array); ok {
			for i := range ca {
				if cao, ok := ca[i].(*objectref); ok {
					page.Contents = append(page.Contents, cao.refString)
				}
			}
		}
	}
	if p := o.objectref("/Parent"); p != nil {
		page.Parent = p.refString
	}
	return &page
}

func handlePageContents(pItem *page, contents map[string][]textsection, uncategorized map[string]*object) error {
	for i := range pItem.Contents {
		cref := pItem.Contents[i]
		// contents already available, so get text
		if cObj, ok := uncategorized[cref]; ok {
			if err := saveContents(cObj, contents); err != nil {
				return err
			}
			delete(uncategorized, cref)

			// haven't seen contents yet, so just flag it for later retrieval
		} else {
			contents[cref] = nil
		}
	}
	return nil
}

func saveContents(v *object, contents map[string][]textsection) error {
	err := v.decodeStream()
	if err != nil {
		return err
	}
	sections, err := getTextSections(newMemReader(v.stream))
	if err != nil {
		return err
	}
	contents[v.refString] = sections
	return nil
}

func handlePageParent(pItem *page, pageRef string, pagesList map[string]*pages) {
	if pItem.Parent != "" {
		if pagesItem, ok := pagesList[pItem.Parent]; ok {
			if pagesItem.isNull { // null object so add this reference to the list of kids
				pagesItem.Kids = append(pagesItem.Kids, pageRef)
			}
		} else {
			pagesList[pItem.Parent] = &pages{Kids: []string{pageRef}, isNull: true}
		}
	}
}

func getFont(o *object) *font {
	font := font{Encoding: o.name("/Encoding")}
	if u := o.objectref("/ToUnicode"); u != nil {
		font.ToUnicode = u.refString
	}
	return &font
}

func handleToUnicode(f *font, cmaps map[string]cmap, uncategorized map[string]*object) error {
	if f.ToUnicode != "" {
		// cmap already available, so create
		if u, ok := uncategorized[f.ToUnicode]; ok {
			if err := saveCmap(u, cmaps); err != nil {
				return err
			}
			delete(uncategorized, f.ToUnicode)

			// haven't seen cmap yet, so just flag for later
		} else {
			cmaps[f.ToUnicode] = nil
		}
	}
	return nil
}

func saveCmap(toUnicode *object, cmaps map[string]cmap) error {
	if err := toUnicode.decodeStream(); err != nil {
		return err
	}
	cmap, err := getCmap(newMemReader(toUnicode.stream))
	if err != nil {
		return err
	}
	cmaps[toUnicode.refString] = cmap
	return nil
}

func getTextSections(r peekingReader) ([]textsection, error) {
	sections := []textsection{}
	var t textsection
	var font name
	var lastArray array
	var lastText text
	var lastName name

	for {
		item := readNext(r)

		switch v := item.(type) {
		case error:
			if v == io.EOF {
				return sections, nil
			}
			return nil, v

		case token:
			switch v {
			case "BT":
				t = textsection{}
			case "Tf":
				font = lastName
			case "TJ":
				t.textArray = append(t.textArray, lastArray...)
				t.textArray = append(t.textArray, " ")
			case "T*":
				t.textArray = append(t.textArray, "\n")
			case "Tj":
				t.textArray = append(t.textArray, lastText)
			case "ET":
				t.fontName = font // use the current global text state
				sections = append(sections, t)
			}

		case array:
			lastArray = v

		case text:
			lastText = v

		case name:
			lastName = v
		}
	}
}

func getCmap(r peekingReader) (cmap, error) {
	cmap := make(cmap)
	var prev token

	for {
		item := readNext(r)

		switch v := item.(type) {
		case error:
			if v == io.EOF {
				return cmap, nil
			}
			return nil, v

		case token:
			switch v {
			case "begincodespacerange":
				length, _ := strconv.Atoi(string(prev))
				for i := 0; i < length*2; i++ {

				}
			case "beginbfchar":
				bfc, err := readbfchar(r, prev)
				if err != nil {
					return nil, err
				}
				for key, value := range bfc {
					cmap[key] = value
				}
			case "beginbfrange":
				bfr, err := readbfrange(r, prev)
				if err != nil {
					return nil, err
				}
				for key, value := range bfr {
					cmap[key] = value
				}
			case "endcmap":
				return cmap, nil
			default:
				prev = v
			}
		}
	}
}

func readbfchar(r peekingReader, length token) (cmap, error) {
	cmap := make(cmap)
	l, _ := strconv.Atoi(string(length))
	var lastKey hexdata
	for i := 0; i < l*2; i++ {
		item := readNext(r)
		switch v := item.(type) {
		case error:
			return nil, v

		case hexdata:
			switch i % 2 {
			case 0: // first item is the key
				lastKey = v
				cmap[v] = ""
			case 1: // second item is the value
				num, _ := strconv.ParseInt(string(v), 16, 16)
				repl := fmt.Sprintf("%c", num)
				cmap[lastKey] = repl
			}

		default:
			return nil, errors.New("invalid bfchar data")
		}
	}
	return cmap, nil
}

func readbfrange(r peekingReader, length token) (cmap, error) {
	cmap := make(cmap)
	l, _ := strconv.Atoi(string(length))
	var start, end int64
	var digits int

	for i := 0; i < l*3; i++ {
		item := readNext(r)
		switch v := item.(type) {
		case error:
			return nil, v

		case hexdata:
			switch i % 3 {
			case 0: // range start
				digits = len(string(v))
				start, _ = strconv.ParseInt(string(v), 16, 16)
			case 1: // range end
				end, _ = strconv.ParseInt(string(v), 16, 16)
			case 2: // values
				repl, _ := strconv.ParseInt(string(v), 16, 16)
				var count int64
				for i := start; i <= end; i++ {
					format := fmt.Sprintf("%%0%dx", digits) // format for however many digits we originally had
					cmap[hexdata(strings.ToUpper(fmt.Sprintf(format, i)))] = fmt.Sprintf("%c", repl+count)
					count++
				}
			}

		default:
			return nil, errors.New("invalid bfrange data")
		}
	}
	return cmap, nil
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/context"
	"github.com/gorilla/mux"
	"github.com/martinp/atbapi/atb"
	"github.com/pmylund/go-cache"
	"log"
	"net/http"
	"strconv"
	"time"
)

type Api struct {
	Client atb.Client
	cache  *cache.Cache
	expiration
}

type expiration struct {
	departures time.Duration
	stops      time.Duration
}

func marshal(data interface{}, indent bool) ([]byte, error) {
	if indent {
		return json.MarshalIndent(data, "", "  ")
	}
	return json.Marshal(data)
}

func (a *Api) getBusStops() (BusStops, error) {
	const cacheKey = "stops"
	cached, ok := a.cache.Get(cacheKey)
	if ok {
		cachedBusStops, ok := cached.(BusStops)
		if !ok {
			return BusStops{}, fmt.Errorf(
				"type assertion of cached value failed")
		}
		return cachedBusStops, nil
	}
	atbBusStops, err := a.Client.GetBusStops()
	if err != nil {
		return BusStops{}, err
	}
	busStops, err := convertBusStops(atbBusStops)
	if err != nil {
		return BusStops{}, err
	}
	// Create a map of nodeIds
	busStops.nodeIds = make(map[int]struct{}, len(busStops.Stops))
	for _, s := range busStops.Stops {
		busStops.nodeIds[s.NodeId] = struct{}{}
	}
	a.cache.Set(cacheKey, busStops, a.expiration.stops)
	return busStops, nil
}

func (a *Api) getDepartures(nodeId int) (Departures, error) {
	cacheKey := strconv.Itoa(nodeId)
	cached, ok := a.cache.Get(cacheKey)
	if ok {
		cachedDepartures, ok := cached.(Departures)
		if !ok {
			return Departures{}, fmt.Errorf(
				"type assertion of cached value failed")
		}
		return cachedDepartures, nil
	}
	forecasts, err := a.Client.GetRealTimeForecast(nodeId)
	if err != nil {
		return Departures{}, err
	}
	departures, err := convertForecasts(forecasts)
	if err != nil {
		return Departures{}, err
	}
	a.cache.Set(cacheKey, departures, cache.DefaultExpiration)
	return departures, nil
}

func (a *Api) BusStopsHandler(w http.ResponseWriter, req *http.Request) *Error {
	busStops, err := a.getBusStops()
	if err != nil {
		log.Print(err)
		return &Error{
			error:   err,
			Status:  http.StatusInternalServerError,
			Message: "failed to get bus stops from atb",
		}
	}
	jsonBlob, err := marshal(busStops, context.Get(req, "indent").(bool))
	if err != nil {
		log.Print(err)
		return &Error{
			error:   err,
			Status:  http.StatusInternalServerError,
			Message: "failed to marshal bus stops",
		}
	}
	w.Write(jsonBlob)
	return nil
}

func (a *Api) DeparturesHandler(w http.ResponseWriter, req *http.Request) *Error {
	vars := mux.Vars(req)
	nodeId, err := strconv.Atoi(vars["nodeId"])
	if err != nil {
		log.Print(err)
		return &Error{
			error:   err,
			Status:  http.StatusBadRequest,
			Message: "missing or invalid nodeId",
		}
	}
	busStops, err := a.getBusStops()
	if err != nil {
		log.Print(err)
		return &Error{
			error:   err,
			Status:  http.StatusInternalServerError,
			Message: "could not get bus stops from atb",
		}
	}
	_, knownBusStop := busStops.nodeIds[nodeId]
	if !knownBusStop {
		msg := fmt.Sprintf("bus stop with nodeId=%d not found", nodeId)
		return &Error{
			error:   err,
			Status:  http.StatusNotFound,
			Message: msg,
		}
	}
	departures, err := a.getDepartures(nodeId)
	if err != nil {
		log.Print(err)
		return &Error{
			error:   err,
			Status:  http.StatusInternalServerError,
			Message: "could not get departures from atb",
		}
	}
	jsonBlob, err := marshal(departures, context.Get(req, "indent").(bool))
	if err != nil {
		log.Print(err)
		return &Error{
			error:   err,
			Status:  http.StatusInternalServerError,
			Message: "failed to marshal departures",
		}
	}
	w.Write(jsonBlob)
	return nil
}

func New(client atb.Client, stopsExpiration, depExpiration time.Duration) Api {
	cache := cache.New(depExpiration, 30*time.Second)
	return Api{
		Client: client,
		cache:  cache,
		expiration: expiration{
			stops:      stopsExpiration,
			departures: depExpiration,
		},
	}
}

type appHandler func(http.ResponseWriter, *http.Request) *Error

func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if e := fn(w, r); e != nil { // e is *Error, not os.Error.
		if e.error != nil {
			log.Print(e.error)
		}
		data, err := marshal(e, true)
		if err != nil {
			// Should never happen
			panic(err)
		}
		w.WriteHeader(e.Status)
		w.Write(data)
	}
}

func requestFilter(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		_, indent := r.URL.Query()["pretty"]
		context.Set(r, "indent", indent)
		w.Header().Set("Content-Type", "application/json")
		next.ServeHTTP(w, r)
	})
}

func (a *Api) ListenAndServe(addr string) error {
	r := mux.NewRouter()
	r.Handle("/api/v1/busstops", appHandler(a.BusStopsHandler))
	r.Handle("/api/v1/departures/{nodeId:[0-9]+}",
		appHandler(a.DeparturesHandler))
	http.Handle("/", requestFilter(r))
	return http.ListenAndServe(addr, nil)
}
<commit_msg>Return custom 404 for unknown routes<commit_after>package api

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/context"
	"github.com/gorilla/mux"
	"github.com/martinp/atbapi/atb"
	"github.com/pmylund/go-cache"
	"log"
	"net/http"
	"strconv"
	"time"
)

type Api struct {
	Client atb.Client
	cache  *cache.Cache
	expiration
}

type expiration struct {
	departures time.Duration
	stops      time.Duration
}

func marshal(data interface{}, indent bool) ([]byte, error) {
	if indent {
		return json.MarshalIndent(data, "", "  ")
	}
	return json.Marshal(data)
}

func (a *Api) getBusStops() (BusStops, error) {
	const cacheKey = "stops"
	cached, ok := a.cache.Get(cacheKey)
	if ok {
		cachedBusStops, ok := cached.(BusStops)
		if !ok {
			return BusStops{}, fmt.Errorf(
				"type assertion of cached value failed")
		}
		return cachedBusStops, nil
	}
	atbBusStops, err := a.Client.GetBusStops()
	if err != nil {
		return BusStops{}, err
	}
	busStops, err := convertBusStops(atbBusStops)
	if err != nil {
		return BusStops{}, err
	}
	// Create a map of nodeIds
	busStops.nodeIds = make(map[int]struct{}, len(busStops.Stops))
	for _, s := range busStops.Stops {
		busStops.nodeIds[s.NodeId] = struct{}{}
	}
	a.cache.Set(cacheKey, busStops, a.expiration.stops)
	return busStops, nil
}

func (a *Api) getDepartures(nodeId int) (Departures, error) {
	cacheKey := strconv.Itoa(nodeId)
	cached, ok := a.cache.Get(cacheKey)
	if ok {
		cachedDepartures, ok := cached.(Departures)
		if !ok {
			return Departures{}, fmt.Errorf(
				"type assertion of cached value failed")
		}
		return cachedDepartures, nil
	}
	forecasts, err := a.Client.GetRealTimeForecast(nodeId)
	if err != nil {
		return Departures{}, err
	}
	departures, err := convertForecasts(forecasts)
	if err != nil {
		return Departures{}, err
	}
	a.cache.Set(cacheKey, departures, cache.DefaultExpiration)
	return departures, nil
}

func (a *Api) BusStopsHandler(w http.ResponseWriter, req *http.Request) *Error {
	busStops, err := a.getBusStops()
	if err != nil {
		log.Print(err)
		return &Error{
			error:   err,
			Status:  http.StatusInternalServerError,
			Message: "failed to get bus stops from atb",
		}
	}
	jsonBlob, err := marshal(busStops, context.Get(req, "indent").(bool))
	if err != nil {
		log.Print(err)
		return &Error{
			error:   err,
			Status:  http.StatusInternalServerError,
			Message: "failed to marshal bus stops",
		}
	}
	w.Write(jsonBlob)
	return nil
}

func (a *Api) DeparturesHandler(w http.ResponseWriter, req *http.Request) *Error {
	vars := mux.Vars(req)
	nodeId, err := strconv.Atoi(vars["nodeId"])
	if err != nil {
		log.Print(err)
		return &Error{
			error:   err,
			Status:  http.StatusBadRequest,
			Message: "missing or invalid nodeId",
		}
	}
	busStops, err := a.getBusStops()
	if err != nil {
		log.Print(err)
		return &Error{
			error:   err,
			Status:  http.StatusInternalServerError,
			Message: "could not get bus stops from atb",
		}
	}
	_, knownBusStop := busStops.nodeIds[nodeId]
	if !knownBusStop {
		msg := fmt.Sprintf("bus stop with nodeId=%d not found", nodeId)
		return &Error{
			error:   err,
			Status:  http.StatusNotFound,
			Message: msg,
		}
	}
	departures, err := a.getDepartures(nodeId)
	if err != nil {
		log.Print(err)
		return &Error{
			error:   err,
			Status:  http.StatusInternalServerError,
			Message: "could not get departures from atb",
		}
	}
	jsonBlob, err := marshal(departures, context.Get(req, "indent").(bool))
	if err != nil {
		log.Print(err)
		return &Error{
			error:   err,
			Status:  http.StatusInternalServerError,
			Message: "failed to marshal departures",
		}
	}
	w.Write(jsonBlob)
	return nil
}

func (a *Api) NotFoundHandler(w http.ResponseWriter, req *http.Request) *Error {
	return &Error{
		error:   nil,
		Status:  http.StatusNotFound,
		Message: "route not found",
	}
}

func New(client atb.Client, stopsExpiration, depExpiration time.Duration) Api {
	cache := cache.New(depExpiration, 30*time.Second)
	return Api{
		Client: client,
		cache:  cache,
		expiration: expiration{
			stops:      stopsExpiration,
			departures: depExpiration,
		},
	}
}

type appHandler func(http.ResponseWriter, *http.Request) *Error

func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if e := fn(w, r); e != nil { // e is *Error, not os.Error.
		if e.error != nil {
			log.Print(e.error)
		}
		data, err := marshal(e, true)
		if err != nil {
			// Should never happen
			panic(err)
		}
		w.WriteHeader(e.Status)
		w.Write(data)
	}
}

func requestFilter(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		_, indent := r.URL.Query()["pretty"]
		context.Set(r, "indent", indent)
		w.Header().Set("Content-Type", "application/json")
		next.ServeHTTP(w, r)
	})
}

func (a *Api) ListenAndServe(addr string) error {
	r := mux.NewRouter()
	r.Handle("/api/v1/busstops", appHandler(a.BusStopsHandler))
	r.Handle("/api/v1/departures/{nodeId:[0-9]+}",
		appHandler(a.DeparturesHandler))
	r.NotFoundHandler = appHandler(a.NotFoundHandler)
	http.Handle("/", requestFilter(r))
	return http.ListenAndServe(addr, nil)
}
<|endoftext|>


<commit_before>package api

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"time"
)

const endpoint = "api.uptimerobot.com"

// Config is used to configure the creation of a client
type Config struct {
	// Address is the address of the UptimeRobot service
	Address string

	// HttpClient is the client to use. Default will be
	// used if not provided.
	HttpClient *http.Client

	// WaitTime limits how long a Watch will block. If not provided,
	// the agent default values will be used.
	WaitTime time.Duration

	// API key is used to provide a per-request authentication
	APIKey string
}

// Client provides a client to the UptimeRobot API
type Client struct {
	config Config
}

// request is used to help build up a request
type request struct {
	config *Config
	method string
	url    *url.URL
	params url.Values
	body   io.Reader
	obj    interface{}
}

// NewClient returns a new client
func NewClient(apikey string) (*Client, error) {
	config := &Config{
		Address:    endpoint,
		HttpClient: http.DefaultClient,
		APIKey:     apikey,
	}

	client := &Client{
		config: *config,
	}
	return client, nil
}

// toHTTP converts the request to an HTTP request
func (r *request) toHTTP() (*http.Request, error) {
	// Encode the query parameters
	r.url.RawQuery = r.params.Encode()

	// Create the HTTP request
	req, err := http.NewRequest(r.method, r.url.RequestURI(), r.body)
	if err != nil {
		return nil, err
	}

	req.URL.Host = r.url.Host
	req.URL.Scheme = r.url.Scheme
	req.Host = r.url.Host

	return req, nil
}

func (c *Client) newRequest(method, path string) *request {
	r := &request{
		config: &c.config,
		method: method,
		url: &url.URL{
			Scheme: "http",
			Host:   c.config.Address,
			Path:   path,
		},
		params: make(map[string][]string),
	}
	if c.config.APIKey != "" {
		r.params.Set("apiKey", r.config.APIKey)
	}
	// Hardcode to xml output. Can't get nested json to decode correctly into nested structs.
	r.params.Set("format", "xml")
	//r.params.Set("noJsonCallback", "1")
	return r
}

// doRequest runs a request with our client
func (c *Client) doRequest(r *request) (time.Duration, *http.Response, error) {
	req, err := r.toHTTP()
	if err != nil {
		return 0, nil, err
	}
	start := time.Now()
	resp, err := c.config.HttpClient.Do(req)
	diff := time.Now().Sub(start)
	return diff, resp, err
}

// decodeBody is used to XML decode a body
func decodeBody(resp *http.Response, out interface{}) error {
	dec := xml.NewDecoder(resp.Body)
	return dec.Decode(out)
}

// requireOK is used to wrap doRequest and check for a 200
func requireOK(d time.Duration, resp *http.Response, e error) (time.Duration, *http.Response, error) {
	if e != nil {
		if resp != nil {
			resp.Body.Close()
		}
		return d, nil, e
	}
	if resp.StatusCode != 200 {
		var buf bytes.Buffer
		io.Copy(&buf, resp.Body)
		resp.Body.Close()
		return d, nil, fmt.Errorf("Unexpected response code: %d (%s)", resp.StatusCode, buf.Bytes())
	}
	return d, resp, nil
}
<commit_msg>Switch to HTTPS by default.<commit_after>package api

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"time"
)

const endpoint = "api.uptimerobot.com"

// Config is used to configure the creation of a client
type Config struct {
	// Address is the address of the UptimeRobot service
	Address string

	// HttpClient is the client to use. Default will be
	// used if not provided.
	HttpClient *http.Client

	// WaitTime limits how long a Watch will block. If not provided,
	// the agent default values will be used.
	WaitTime time.Duration

	// API key is used to provide a per-request authentication
	APIKey string
}

// Client provides a client to the UptimeRobot API
type Client struct {
	config Config
}

// request is used to help build up a request
type request struct {
	config *Config
	method string
	url    *url.URL
	params url.Values
	body   io.Reader
	obj    interface{}
}

// NewClient returns a new client
func NewClient(apikey string) (*Client, error) {
	config := &Config{
		Address:    endpoint,
		HttpClient: http.DefaultClient,
		APIKey:     apikey,
	}

	client := &Client{
		config: *config,
	}
	return client, nil
}

// toHTTP converts the request to an HTTP request
func (r *request) toHTTP() (*http.Request, error) {
	// Encode the query parameters
	r.url.RawQuery = r.params.Encode()

	// Create the HTTP request
	req, err := http.NewRequest(r.method, r.url.RequestURI(), r.body)
	if err != nil {
		return nil, err
	}

	req.URL.Host = r.url.Host
	req.URL.Scheme = r.url.Scheme
	req.Host = r.url.Host

	return req, nil
}

func (c *Client) newRequest(method, path string) *request {
	r := &request{
		config: &c.config,
		method: method,
		url: &url.URL{
			Scheme: "https",
			Host:   c.config.Address,
			Path:   path,
		},
		params: make(map[string][]string),
	}
	if c.config.APIKey != "" {
		r.params.Set("apiKey", r.config.APIKey)
	}
	// Hardcode to xml output. Can't get nested json to decode correctly into nested structs.
	r.params.Set("format", "xml")
	//r.params.Set("noJsonCallback", "1")
	return r
}

// doRequest runs a request with our client
func (c *Client) doRequest(r *request) (time.Duration, *http.Response, error) {
	req, err := r.toHTTP()
	if err != nil {
		return 0, nil, err
	}
	start := time.Now()
	resp, err := c.config.HttpClient.Do(req)
	diff := time.Now().Sub(start)
	return diff, resp, err
}

// decodeBody is used to XML decode a body
func decodeBody(resp *http.Response, out interface{}) error {
	dec := xml.NewDecoder(resp.Body)
	return dec.Decode(out)
}

// requireOK is used to wrap doRequest and check for a 200
func requireOK(d time.Duration, resp *http.Response, e error) (time.Duration, *http.Response, error) {
	if e != nil {
		if resp != nil {
			resp.Body.Close()
		}
		return d, nil, e
	}
	if resp.StatusCode != 200 {
		var buf bytes.Buffer
		io.Copy(&buf, resp.Body)
		resp.Body.Close()
		return d, nil, fmt.Errorf("Unexpected response code: %d (%s)", resp.StatusCode, buf.Bytes())
	}
	return d, resp, nil
}
<|endoftext|>