<commit_before>/*

The Rep type that models a replay (and everything in it).

*/

package rep

import (
	"encoding/json"
	"errors"
	"io"

	"github.com/icza/mpq"
	"github.com/icza/s2prot"
)

const (
	// ParserVersion is a Semver2 compatible version of the parser.
	ParserVersion = "v1.2.0-dev"
)

var (
	// ErrInvalidRepFile means invalid replay file.
	ErrInvalidRepFile = errors.New("Invalid SC2Replay file")

	// ErrUnsupportedRepVersion means the replay file is valid but its version is not supported.
	ErrUnsupportedRepVersion = errors.New("Unsupported replay version")

	// ErrDecoding means decoding the replay file failed,
	// Most likely because replay file is invalid, but also might be due to an implementation bug
	ErrDecoding = errors.New("Decoding error")
)

// Rep describes a replay.
type Rep struct {
	m *mpq.MPQ // MPQ parser for reading the file

	protocol *s2prot.Protocol // Protocol to decode the replay

	Header   Header   // Replay header (replay game version and length)
	Details  Details  // Game details (overall replay details)
	InitData InitData // Replay init data (the initial lobby)
	AttrEvts AttrEvts // Attributes events

	Metadata Metadata // Game metadata (calculated, confirmed results)

	GameEvts    []s2prot.Event // Game events
	MessageEvts []s2prot.Event // Message events
	TrackerEvts *TrackerEvts   // Tracker events

	GameEvtsErr    bool // Tells if decoding game events had errors
	MessageEvtsErr bool // Tells if decoding message events had errors
	TrackerEvtsErr bool // Tells if decoding tracker events had errors
}

// NewFromFile returns a new Rep constructed from a file.
// All types of events are decoded from the replay.
// The returned Rep must be closed with the Close method!
//
// ErrInvalidRepFile is returned if the specified name does not denote a valid SC2Replay file.
//
// ErrUnsupportedRepVersion is returned if the file exists and is a valid SC2Replay file but its version is not supported.
//
// ErrDecoding is returned if decoding the replay fails. This is most likely because the replay file is invalid, but also might be due to an implementation bug.
func NewFromFile(name string) (*Rep, error) {
	return NewFromFileEvts(name, true, true, true)
}

// NewFromFileEvts returns a new Rep constructed from a file, only the specified types of events decoded.
// The game, message and tracker tells if game events, message events and tracker events are to be decoded.
// Replay header, init data, details, attributes events and game metadata are always decoded.
// The returned Rep must be closed with the Close method!
//
// ErrInvalidRepFile is returned if the specified name does not denote a valid SC2Replay file.
//
// ErrUnsupportedRepVersion is returned if the file exists and is a valid SC2Replay file but its version is not supported.
//
// ErrDecoding is returned if decoding the replay fails. This is most likely because the replay file is invalid, but also might be due to an implementation bug.
func NewFromFileEvts(name string, game, message, tracker bool) (*Rep, error) {
	m, err := mpq.NewFromFile(name)
	if err != nil {
		return nil, ErrInvalidRepFile
	}
	return newRep(m, game, message, tracker)
}

// New returns a new Rep using the specified io.ReadSeeker as the SC2Replay file source.
// All types of events are decoded from the replay.
// The returned Rep must be closed with the Close method!
//
// ErrInvalidRepFile is returned if the input is not a valid SC2Replay file content.
//
// ErrUnsupportedRepVersion is returned if the input is a valid SC2Replay file but its version is not supported.
//
// ErrDecoding is returned if decoding the replay fails. This is most likely because the input is invalid, but also might be due to an implementation bug.
func New(input io.ReadSeeker) (*Rep, error) {
	return NewEvts(input, true, true, true)
}

// NewEvts returns a new Rep using the specified io.ReadSeeker as the SC2Replay file source, only the specified types of events decoded.
// The game, message and tracker tells if game events, message events and tracker events are to be decoded.
// Replay header, init data, details, attributes events and game metadata are always decoded.
// The returned Rep must be closed with the Close method!
//
// ErrInvalidRepFile is returned if the input is not a valid SC2Replay file content.
//
// ErrUnsupportedRepVersion is returned if the input is a valid SC2Replay file but its version is not supported.
//
// ErrDecoding is returned if decoding the replay fails. This is most likely because the input is invalid, but also might be due to an implementation bug.
func NewEvts(input io.ReadSeeker, game, message, tracker bool) (*Rep, error) {
	m, err := mpq.New(input)
	if err != nil {
		return nil, ErrInvalidRepFile
	}
	return newRep(m, game, message, tracker)
}

// newRep returns a new Rep constructed using the specified mpq.MPQ handler of the SC2Replay file, only the specified types of events decoded.
// The game, message and tracker tells if game events, message events and tracker events are to be decoded.
// Replay header, init data, details, attributes events and game metadata are always decoded.
// The returned Rep must be closed with the Close method!
//
// ErrInvalidRepFile is returned if the specified name does not denote a valid SC2Replay file.
//
// ErrUnsupportedRepVersion is returned if the input is a valid SC2Replay file but its version is not supported.
//
// ErrDecoding is returned if decoding the replay fails. This is most likely because the input is invalid, but also might be due to an implementation bug.
func newRep(m *mpq.MPQ, game, message, tracker bool) (parsedRep *Rep, errRes error) {
	closeMPQ := true
	defer func() {
		// If returning due to an error, MPQ must be closed!
		if closeMPQ {
			m.Close()
		}

		// The input is completely untrusted and the decoding implementation omits error checks for efficiency:
		// Protect replay decoding:
		if r := recover(); r != nil {
			errRes = ErrDecoding
		}
	}()

	rep := Rep{m: m}

	rep.Header = Header{Struct: s2prot.DecodeHeader(m.UserData())}
	if rep.Header.Struct == nil {
		return nil, ErrInvalidRepFile
	}

	bb := rep.Header.BaseBuild()
	p := s2prot.GetProtocol(int(bb))
	if p == nil {
		return nil, ErrUnsupportedRepVersion
	}
	rep.protocol = p

	data, err := m.FileByHash(620083690, 3548627612, 4013960850) // "replay.details"
	if err != nil {
		return nil, ErrInvalidRepFile
	}
	rep.Details = Details{Struct: p.DecodeDetails(data)}

	data, err = m.FileByHash(3544165653, 1518242780, 4280631132) // "replay.initData"
	if err != nil {
		return nil, ErrInvalidRepFile
	}
	rep.InitData = NewInitData(p.DecodeInitData(data))

	data, err = m.FileByHash(1306016990, 497594575, 2731474728) // "replay.attributes.events"
	if err != nil {
		return nil, ErrInvalidRepFile
	}
	rep.AttrEvts = NewAttrEvts(p.DecodeAttributesEvts(data))

	data, err = m.FileByHash(3675439372, 3912155403, 1108615308) // "replay.gamemetadata.json"
	if err != nil {
		return nil, ErrInvalidRepFile
	}
	if data != nil { // Might not be present, was added around 3.7
		if err = json.Unmarshal(data, &rep.Metadata.Struct); err != nil {
			return nil, ErrInvalidRepFile
		}
	}

	if game {
		data, err = m.FileByHash(496563520, 2864883019, 4101385109) // "replay.game.events"
		if err != nil {
			return nil, ErrInvalidRepFile
		}
		rep.GameEvts, err = p.DecodeGameEvts(data)
		rep.GameEvtsErr = err != nil
	}

	if message {
		data, err = m.FileByHash(1089231967, 831857289, 1784674979) // "replay.message.events"
		if err != nil {
			return nil, ErrInvalidRepFile
		}
		rep.MessageEvts, err = p.DecodeMessageEvts(data)
		rep.MessageEvtsErr = err != nil
	}

	if tracker {
		data, err = m.FileByHash(1501940595, 4263103390, 1648390237) // "replay.tracker.events"
		if err != nil {
			return nil, ErrInvalidRepFile
		}
		evts, err := p.DecodeTrackerEvts(data)
		rep.TrackerEvts = &TrackerEvts{Evts: evts}
		rep.TrackerEvts.init(&rep)
		rep.TrackerEvtsErr = err != nil
	}

	// Everything went well, Rep is about to be returned, do not close MPQ
	// (it will be the caller's responsibility, done via Rep.Close()).
	closeMPQ = false

	return &rep, nil
}

// Close closes the Rep and its resources.
func (r *Rep) Close() error {
	if r.m == nil {
		return nil
	}
	return r.m.Close()
}

// MPQ gives access to the underlying MPQ parser of the rep.
// Intentionally not a method of Rep to not encourage its use.
func MPQ(r *Rep) *mpq.MPQ {
	return r.m
}
<commit_msg>Improved doc.<commit_after>/*

The Rep type that models a replay (and everything in it).

*/

package rep

import (
	"encoding/json"
	"errors"
	"io"

	"github.com/icza/mpq"
	"github.com/icza/s2prot"
)

const (
	// ParserVersion is a Semver2 compatible version of the parser.
	ParserVersion = "v1.2.0-dev"
)

var (
	// ErrInvalidRepFile means invalid replay file.
	ErrInvalidRepFile = errors.New("Invalid SC2Replay file")

	// ErrUnsupportedRepVersion means the replay file is valid but its version is not supported.
	ErrUnsupportedRepVersion = errors.New("Unsupported replay version")

	// ErrDecoding means decoding the replay file failed,
	// Most likely because replay file is invalid, but also might be due to an implementation bug
	ErrDecoding = errors.New("Decoding error")
)

// Rep describes a replay.
type Rep struct {
	m *mpq.MPQ // MPQ parser for reading the file

	protocol *s2prot.Protocol // Protocol to decode the replay

	Header   Header   // Replay header (replay game version and length)
	Details  Details  // Game details (overall replay details)
	InitData InitData // Replay init data (the initial lobby)
	AttrEvts AttrEvts // Attributes events

	Metadata Metadata // Game metadata (calculated, confirmed results)

	GameEvts    []s2prot.Event // Game events
	MessageEvts []s2prot.Event // Message events
	TrackerEvts *TrackerEvts   // Tracker events

	GameEvtsErr    bool // Tells if decoding game events had errors
	MessageEvtsErr bool // Tells if decoding message events had errors
	TrackerEvtsErr bool // Tells if decoding tracker events had errors
}

// NewFromFile returns a new Rep constructed from a file.
// All types of events are decoded from the replay.
// The returned Rep must be closed with the Close method!
//
// ErrInvalidRepFile is returned if the specified name does not denote a valid SC2Replay file.
//
// ErrUnsupportedRepVersion is returned if the file exists and is a valid SC2Replay file but its version is not supported.
//
// ErrDecoding is returned if decoding the replay fails. This is most likely because the replay file is invalid, but also might be due to an implementation bug.
func NewFromFile(name string) (*Rep, error) {
	return NewFromFileEvts(name, true, true, true)
}

// NewFromFileEvts returns a new Rep constructed from a file, only the specified types of events decoded.
// The game, message and tracker tells if game events, message events and tracker events are to be decoded.
// Replay header, init data, details, attributes events and game metadata are always decoded.
// The returned Rep must be closed with the Close method!
//
// ErrInvalidRepFile is returned if the specified name does not denote a valid SC2Replay file.
//
// ErrUnsupportedRepVersion is returned if the file exists and is a valid SC2Replay file but its version is not supported.
//
// ErrDecoding is returned if decoding the replay fails. This is most likely because the replay file is invalid, but also might be due to an implementation bug.
func NewFromFileEvts(name string, game, message, tracker bool) (*Rep, error) {
	m, err := mpq.NewFromFile(name)
	if err != nil {
		return nil, ErrInvalidRepFile
	}
	return newRep(m, game, message, tracker)
}

// New returns a new Rep using the specified io.ReadSeeker as the SC2Replay file source.
// All types of events are decoded from the replay.
// The returned Rep must be closed with the Close method!
//
// ErrInvalidRepFile is returned if the input is not a valid SC2Replay file content.
//
// ErrUnsupportedRepVersion is returned if the input is a valid SC2Replay file but its version is not supported.
//
// ErrDecoding is returned if decoding the replay fails. This is most likely because the input is invalid, but also might be due to an implementation bug.
func New(input io.ReadSeeker) (*Rep, error) {
	return NewEvts(input, true, true, true)
}

// NewEvts returns a new Rep using the specified io.ReadSeeker as the SC2Replay file source, only the specified types of events decoded.
// The game, message and tracker tells if game events, message events and tracker events are to be decoded.
// Replay header, init data, details, attributes events and game metadata are always decoded.
// The returned Rep must be closed with the Close method!
//
// ErrInvalidRepFile is returned if the input is not a valid SC2Replay file content.
//
// ErrUnsupportedRepVersion is returned if the input is a valid SC2Replay file but its version is not supported.
//
// ErrDecoding is returned if decoding the replay fails. This is most likely because the input is invalid, but also might be due to an implementation bug.
func NewEvts(input io.ReadSeeker, game, message, tracker bool) (*Rep, error) {
	m, err := mpq.New(input)
	if err != nil {
		return nil, ErrInvalidRepFile
	}
	return newRep(m, game, message, tracker)
}

// newRep returns a new Rep constructed using the specified mpq.MPQ handler of the SC2Replay file, only the specified types of events decoded.
// The game, message and tracker tells if game events, message events and tracker events are to be decoded.
// Replay header, init data, details, attributes events and game metadata are always decoded.
// The returned Rep must be closed with the Close method!
//
// ErrInvalidRepFile is returned if the specified name does not denote a valid SC2Replay file.
//
// ErrUnsupportedRepVersion is returned if the input is a valid SC2Replay file but its version is not supported.
//
// ErrDecoding is returned if decoding the replay fails. This is most likely because the input is invalid, but also might be due to an implementation bug.
func newRep(m *mpq.MPQ, game, message, tracker bool) (parsedRep *Rep, errRes error) {
	closeMPQ := true
	defer func() {
		// If returning due to an error, MPQ must be closed!
		if closeMPQ {
			m.Close()
		}

		// The input is completely untrusted and the decoding implementation omits error checks for efficiency:
		// Protect replay decoding:
		if r := recover(); r != nil {
			errRes = ErrDecoding
		}
	}()

	rep := Rep{m: m}

	rep.Header = Header{Struct: s2prot.DecodeHeader(m.UserData())}
	if rep.Header.Struct == nil {
		return nil, ErrInvalidRepFile
	}

	bb := rep.Header.BaseBuild()
	p := s2prot.GetProtocol(int(bb))
	if p == nil {
		return nil, ErrUnsupportedRepVersion
	}
	rep.protocol = p

	data, err := m.FileByHash(620083690, 3548627612, 4013960850) // "replay.details"
	if err != nil {
		return nil, ErrInvalidRepFile
	}
	rep.Details = Details{Struct: p.DecodeDetails(data)}

	data, err = m.FileByHash(3544165653, 1518242780, 4280631132) // "replay.initData"
	if err != nil {
		return nil, ErrInvalidRepFile
	}
	rep.InitData = NewInitData(p.DecodeInitData(data))

	data, err = m.FileByHash(1306016990, 497594575, 2731474728) // "replay.attributes.events"
	if err != nil {
		return nil, ErrInvalidRepFile
	}
	rep.AttrEvts = NewAttrEvts(p.DecodeAttributesEvts(data))

	data, err = m.FileByHash(3675439372, 3912155403, 1108615308) // "replay.gamemetadata.json"
	if err != nil {
		return nil, ErrInvalidRepFile
	}
	if data != nil { // Might not be present, was added around 3.7
		if err = json.Unmarshal(data, &rep.Metadata.Struct); err != nil {
			return nil, ErrInvalidRepFile
		}
	}

	if game {
		data, err = m.FileByHash(496563520, 2864883019, 4101385109) // "replay.game.events"
		if err != nil {
			return nil, ErrInvalidRepFile
		}
		rep.GameEvts, err = p.DecodeGameEvts(data)
		rep.GameEvtsErr = err != nil
	}

	if message {
		data, err = m.FileByHash(1089231967, 831857289, 1784674979) // "replay.message.events"
		if err != nil {
			return nil, ErrInvalidRepFile
		}
		rep.MessageEvts, err = p.DecodeMessageEvts(data)
		rep.MessageEvtsErr = err != nil
	}

	if tracker {
		data, err = m.FileByHash(1501940595, 4263103390, 1648390237) // "replay.tracker.events"
		if err != nil {
			return nil, ErrInvalidRepFile
		}
		evts, err := p.DecodeTrackerEvts(data)
		rep.TrackerEvts = &TrackerEvts{Evts: evts}
		rep.TrackerEvts.init(&rep)
		rep.TrackerEvtsErr = err != nil
	}

	// Everything went well, Rep is about to be returned, do not close MPQ
	// (it will be the caller's responsibility, done via Rep.Close()).
	closeMPQ = false

	return &rep, nil
}

// Close closes the Rep and its resources.
func (r *Rep) Close() error {
	if r.m == nil {
		return nil
	}
	return r.m.Close()
}

// MPQ gives access to the underlying MPQ parser of the rep.
// Intentionally not a method of Rep to not urge its use.
func MPQ(r *Rep) *mpq.MPQ {
	return r.m
}
<|endoftext|>


<commit_before>package restful

// Copyright 2013 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"io/ioutil"
	"net/http"
	"strings"
)

// Request is a wrapper for a http Request that provides convenience methods
type Request struct {
	Request        *http.Request
	pathParameters map[string]string
	attributes     map[string]interface{} // for storing request-scoped values
}

func newRequest(httpRequest *http.Request) *Request {
	return &Request{httpRequest, map[string]string{}, map[string]interface{}{}} // empty parameters, attributes
}

// PathParameter accesses the Path parameter value by its name
func (r *Request) PathParameter(name string) string {
	return r.pathParameters[name]
}

// PathParameters accesses the Path parameter values
func (r *Request) PathParameters() map[string]string {
	return r.pathParameters
}

// QueryParameter returns the (first) Query parameter value by its name
func (r *Request) QueryParameter(name string) string {
	return r.Request.FormValue(name)
}

// BodyParameter parses the body of the request (once for typically a POST or a PUT) and returns the value of the given name or an error.
func (r *Request) BodyParameter(name string) (string, error) {
	err := r.Request.ParseForm()
	if err != nil {
		return "", err
	}
	return r.Request.PostFormValue(name), nil
}

// HeaderParameter returns the HTTP Header value of a Header name or empty if missing
func (r *Request) HeaderParameter(name string) string {
	return r.Request.Header.Get(name)
}

// ReadEntity checks the Accept header and reads the content into the entityPointer
func (r *Request) ReadEntity(entityPointer interface{}) error {
	contentType := r.Request.Header.Get(HEADER_ContentType)
	buffer, err := ioutil.ReadAll(r.Request.Body)
	if err != nil {
		return err
	}
	if strings.Contains(contentType, MIME_XML) {
		err = xml.Unmarshal(buffer, entityPointer)
	} else {
		if strings.Contains(contentType, MIME_JSON) {
			err = json.Unmarshal(buffer, entityPointer)
		} else {
			err = errors.New("[restful] Unable to unmarshal content of type:" + contentType)
		}
	}
	return err
}

// SetAttribute adds or replaces the attribute with the given value.
func (r *Request) SetAttribute(name string, value interface{}) {
	r.attributes[name] = value
}

// Attribute returns the value associated to the given name. Returns nil if absent.
func (r Request) Attribute(name string) interface{} {
	return r.attributes[name]
}
<commit_msg>#84 - added caching of request body in the event that ReadEntity is called one or more times.<commit_after>package restful

// Copyright 2013 Ernest Micklei. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"io/ioutil"
	"net/http"
	"strings"
)

// Request is a wrapper for a http Request that provides convenience methods
type Request struct {
	Request        *http.Request
	bodyContent    *[]byte // to cache the request body for multiple reads of ReadEntity
	pathParameters map[string]string
	attributes     map[string]interface{} // for storing request-scoped values
}

func newRequest(httpRequest *http.Request) *Request {
	return &Request{
		Request:        httpRequest,
		pathParameters: map[string]string{},
		attributes:     map[string]interface{}{},
	} // empty parameters, attributes
}

// PathParameter accesses the Path parameter value by its name
func (r *Request) PathParameter(name string) string {
	return r.pathParameters[name]
}

// PathParameters accesses the Path parameter values
func (r *Request) PathParameters() map[string]string {
	return r.pathParameters
}

// QueryParameter returns the (first) Query parameter value by its name
func (r *Request) QueryParameter(name string) string {
	return r.Request.FormValue(name)
}

// BodyParameter parses the body of the request (once for typically a POST or a PUT) and returns the value of the given name or an error.
func (r *Request) BodyParameter(name string) (string, error) {
	err := r.Request.ParseForm()
	if err != nil {
		return "", err
	}
	return r.Request.PostFormValue(name), nil
}

// HeaderParameter returns the HTTP Header value of a Header name or empty if missing
func (r *Request) HeaderParameter(name string) string {
	return r.Request.Header.Get(name)
}

// ReadEntity checks the Accept header and reads the content into the entityPointer
func (r *Request) ReadEntity(entityPointer interface{}) (err error) {
	contentType := r.Request.Header.Get(HEADER_ContentType)

	var buffer []byte
	if r.bodyContent != nil {
		buffer = *r.bodyContent
	} else {
		buffer, err = ioutil.ReadAll(r.Request.Body)
		r.bodyContent = &buffer
		if err != nil {
			return err
		}
	}

	if strings.Contains(contentType, MIME_XML) {
		err = xml.Unmarshal(buffer, entityPointer)
	} else {
		if strings.Contains(contentType, MIME_JSON) {
			err = json.Unmarshal(buffer, entityPointer)
		} else {
			err = errors.New("[restful] Unable to unmarshal content of type:" + contentType)
		}
	}
	return err
}

// SetAttribute adds or replaces the attribute with the given value.
func (r *Request) SetAttribute(name string, value interface{}) {
	r.attributes[name] = value
}

// Attribute returns the value associated to the given name. Returns nil if absent.
func (r Request) Attribute(name string) interface{} {
	return r.attributes[name]
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"math"
	"math/rand"
	"net/http"
	"net/http/httputil"
	"net/url"
	"path"
	"strings"
	"time"

	"github.com/boltdb/bolt"
)

type Request struct {
	Service string
	Method  string
	Path    string
	Data    string

	Settings  Settings
	NoQueries bool
	NoHeaders bool

	Alias string

	URL url.URL

	verbose int
}

func (r *Request) Perform() (*http.Response, error) {
	if err := db.Update(request.LoadSettings); err != nil {
		return nil, err
	}

	req, err := r.Prepare()
	if err != nil {
		return nil, err
	}

	switch r.verbose {
	case 1:
		fmt.Println(request.URL.String())
	case 2, 3:
		// at level 3 display the raw request
		extra := false
		if r.verbose >= 3 {
			extra = true
		}

		dump, err := httputil.DumpRequestOut(req, extra)
		if err != nil {
			// this is only the verbose logging, so carry on in case of error
			fmt.Println(err)
			break
		}
		fmt.Println(string(dump))
	}

	return r.retry(req)
}

func (r *Request) retry(req *http.Request) (*http.Response, error) {
	client := &http.Client{}

	var resp *http.Response
	var err error
	maxAttempts := int(r.Settings.Retries.Int64) + 1

	for i := 0; i < maxAttempts; i++ {

		if r.verbose > 0 {
			fmt.Printf("attempt %d: %s %s\n", i, req.Method, req.URL)
		}

		resp, err = client.Do(req)
		if err == nil && resp.StatusCode < 500 {
			return resp, nil
		}

		// TODO: respect the Retry-After header
		delay := r.Settings.RetryDelay.Duration

		if r.Settings.ExponentialBackoff.Bool {
			delay *= time.Duration(math.Exp(float64(i)))
		}

		if r.Settings.RetryJitter.Bool {
			delay = time.Duration(rand.Intn(int(delay)))
		}

		if r.verbose > 0 && delay > 0 && i < maxAttempts {
			fmt.Printf("waiting %s to retry\n", delay)
		}
		<-time.After(delay)
	}

	return resp, err
}

// Prepare the http request.  This will substitute all the parameters,
// addd all the headers and query parameters
func (r *Request) Prepare() (*http.Request, error) {
	// prepare the url
	r.URL = r.Settings.URL()
	params := paramReplacer(r.Settings.Parameters)

	r.URL.Path = path.Join(r.Settings.BasePath.String, params.Replace(r.Path))
	r.Data = params.Replace(r.Data)

	if !r.NoQueries {
		q := r.URL.Query()
		for key, value := range r.Settings.Queries {
			v := params.Replace(value)
			if v[0] != ':' {
				q.Set(key, v)
			}
		}
		r.URL.RawQuery = q.Encode()
	}

	// prepare the request
	req, err := http.NewRequest(
		strings.ToUpper(r.Method),
		r.URL.String(),
		strings.NewReader(r.Data),
	)
	if err != nil {
		return nil, err
	}

	if r.Settings.Username.Valid && r.Settings.Password.Valid {
		req.SetBasicAuth(r.Settings.Username.String, r.Settings.Password.String)
	}

	if !r.NoHeaders {
		for key, value := range r.Settings.Headers {
			v := params.Replace(value)
			if v[0] != ':' {
				req.Header.Set(key, v)
			}
		}
	}

	return req, nil
}

// MakeServiceBucket creates the bucket for the service
func (r *Request) MakeServiceBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
	if r.Service == "" {
		info := tx.Bucket([]byte("info"))
		current := info.Get([]byte("current"))
		r.Service = string(current)
	}

	sb, err := tx.CreateBucketIfNotExists([]byte("services"))
	if err != nil {
		return nil, err
	}

	b, err := sb.CreateBucketIfNotExists([]byte(r.Service))
	if err != nil {
		return nil, err
	}

	return b, nil
}

// ServiceBucket retrieves the bucket for the current requests service
func (r *Request) ServiceBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
	if r.Service == "" {
		info := tx.Bucket([]byte("info"))
		if info == nil {
			return nil, ErrNoInfoBucket
		}
		current := info.Get([]byte("current"))
		r.Service = string(current)
	}

	if r.Service == "" {
		return nil, ErrNoServiceSet
	}

	sb := tx.Bucket([]byte("services"))
	if sb == nil {
		return nil, ErrNoServicesBucket
	}

	b := sb.Bucket([]byte(r.Service))
	if b == nil {
		return nil, ErrNoService{Name: r.Service}
	}

	return b, nil
}

// MakePathBucket creates the bucket for the path
func (r Request) MakePathBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
	s, err := r.ServiceBucket(tx)
	if err != nil {
		return nil, err
	}

	pb, err := s.CreateBucketIfNotExists([]byte("paths"))
	if err != nil {
		return nil, err
	}

	b, err := pb.CreateBucketIfNotExists([]byte(r.Path))
	if err != nil {
		return nil, err
	}

	return b, nil
}

// PathBucket returns the bucket for the request path, creates if needed
func (r Request) PathBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
	s, err := r.ServiceBucket(tx)
	if err != nil {
		return nil, err
	}

	pb := s.Bucket([]byte("paths"))
	if pb == nil {
		return nil, ErrNoPaths
	}

	b := pb.Bucket([]byte(r.Path))
	if b == nil {
		return nil, ErrInvalidPath{Path: r.Path}
	}

	return b, nil
}

// MakeMethodBucket returns the bucket for the request method, creates if needed
func (r Request) MakeMethodBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
	s, err := r.PathBucket(tx)
	if err != nil {
		return nil, err
	}

	b, err := s.CreateBucketIfNotExists([]byte(r.Method))
	if err != nil {
		return nil, err
	}

	return b, nil
}

// LoadSettings from the database
func (r *Request) LoadSettings(tx *bolt.Tx) error {
	sb, pb, mb, err := request.Match(tx)
	if err != nil {
		return err
	}

	// Start with blank settings
	r.Settings = NewSettings()

	// load service settings
	if sb != nil {
		r.Settings = LoadSettings(sb)
	}

	// load path settings
	if pb != nil {
		r.Settings.Merge(LoadSettings(pb))
	}

	// load method settings
	if mb != nil {
		r.Settings.Merge(LoadSettings(mb))
	}

	// load provided cli flags settings
	r.Settings.Merge(settings)

	return nil
}

// Match returns the relavant db buckets for all request settings, it will first check
// for a matching alias, then check generic paths, if there is a matching alias, it
// will be returned in the path bucket.
func (r Request) Match(tx *bolt.Tx) (service, path, method *bolt.Bucket, err error) {
	service, err = r.ServiceBucket(tx)
	if err != nil {
		return nil, nil, nil, err
	}

	// match aliases first, if an alias matches then ignore path or method matches
	ab := service.Bucket([]byte("aliases"))
	if ab != nil {
		c := ab.Cursor()
		for key, _ := c.First(); key != nil; key, _ = c.Next() {
			if string(key) == r.Alias {
				path = ab.Bucket(key)
				return service, path, nil, nil
			}
		}
	}

	// match path
	pb := service.Bucket([]byte("paths"))
	if pb == nil {
		return service, nil, nil, nil
	}

	// Match the path, this will eventually be expanded to better match
	// the path.  So specific paths will be matched before generic ones
	// at the moment it requires an exact match
	c := pb.Cursor()
	for key, _ := c.First(); key != nil; key, _ = c.Next() {
		if string(key) == r.Path {
			path = pb.Bucket(key)
			break
		}
	}

	if path == nil {
		return service, path, nil, nil
	}

	method = path.Bucket([]byte(r.Method))

	return service, path, method, nil
}
<commit_msg>Prevent basic auth on blank username and password<commit_after>package main

import (
	"fmt"
	"math"
	"math/rand"
	"net/http"
	"net/http/httputil"
	"net/url"
	"path"
	"strings"
	"time"

	"github.com/boltdb/bolt"
)

type Request struct {
	Service string
	Method  string
	Path    string
	Data    string

	Settings  Settings
	NoQueries bool
	NoHeaders bool

	Alias string

	URL url.URL

	verbose int
}

func (r *Request) Perform() (*http.Response, error) {
	if err := db.Update(request.LoadSettings); err != nil {
		return nil, err
	}

	req, err := r.Prepare()
	if err != nil {
		return nil, err
	}

	switch r.verbose {
	case 1:
		fmt.Println(request.URL.String())
	case 2, 3:
		// at level 3 display the raw request
		extra := false
		if r.verbose >= 3 {
			extra = true
		}

		dump, err := httputil.DumpRequestOut(req, extra)
		if err != nil {
			// this is only the verbose logging, so carry on in case of error
			fmt.Println(err)
			break
		}
		fmt.Println(string(dump))
	}

	return r.retry(req)
}

func (r *Request) retry(req *http.Request) (*http.Response, error) {
	client := &http.Client{}

	var resp *http.Response
	var err error
	maxAttempts := int(r.Settings.Retries.Int64) + 1

	for i := 0; i < maxAttempts; i++ {

		if r.verbose > 0 {
			fmt.Printf("attempt %d: %s %s\n", i, req.Method, req.URL)
		}

		resp, err = client.Do(req)
		if err == nil && resp.StatusCode < 500 {
			return resp, nil
		}

		// TODO: respect the Retry-After header
		delay := r.Settings.RetryDelay.Duration

		if r.Settings.ExponentialBackoff.Bool {
			delay *= time.Duration(math.Exp(float64(i)))
		}

		if r.Settings.RetryJitter.Bool {
			delay = time.Duration(rand.Intn(int(delay)))
		}

		if r.verbose > 0 && delay > 0 && i < maxAttempts {
			fmt.Printf("waiting %s to retry\n", delay)
		}
		<-time.After(delay)
	}

	return resp, err
}

// Prepare the http request.  This will substitute all the parameters,
// addd all the headers and query parameters
func (r *Request) Prepare() (*http.Request, error) {
	// prepare the url
	r.URL = r.Settings.URL()
	params := paramReplacer(r.Settings.Parameters)

	r.URL.Path = path.Join(r.Settings.BasePath.String, params.Replace(r.Path))
	r.Data = params.Replace(r.Data)

	if !r.NoQueries {
		q := r.URL.Query()
		for key, value := range r.Settings.Queries {
			v := params.Replace(value)
			if v[0] != ':' {
				q.Set(key, v)
			}
		}
		r.URL.RawQuery = q.Encode()
	}

	// prepare the request
	req, err := http.NewRequest(
		strings.ToUpper(r.Method),
		r.URL.String(),
		strings.NewReader(r.Data),
	)
	if err != nil {
		return nil, err
	}

	if r.Settings.Username.Valid && r.Settings.Password.Valid &&
		r.Settings.Username.String != "" && r.Settings.Password.String != "" {
		req.SetBasicAuth(r.Settings.Username.String, r.Settings.Password.String)
	}

	if !r.NoHeaders {
		for key, value := range r.Settings.Headers {
			v := params.Replace(value)
			if v[0] != ':' {
				req.Header.Set(key, v)
			}
		}
	}

	return req, nil
}

// MakeServiceBucket creates the bucket for the service
func (r *Request) MakeServiceBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
	if r.Service == "" {
		info := tx.Bucket([]byte("info"))
		current := info.Get([]byte("current"))
		r.Service = string(current)
	}

	sb, err := tx.CreateBucketIfNotExists([]byte("services"))
	if err != nil {
		return nil, err
	}

	b, err := sb.CreateBucketIfNotExists([]byte(r.Service))
	if err != nil {
		return nil, err
	}

	return b, nil
}

// ServiceBucket retrieves the bucket for the current requests service
func (r *Request) ServiceBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
	if r.Service == "" {
		info := tx.Bucket([]byte("info"))
		if info == nil {
			return nil, ErrNoInfoBucket
		}
		current := info.Get([]byte("current"))
		r.Service = string(current)
	}

	if r.Service == "" {
		return nil, ErrNoServiceSet
	}

	sb := tx.Bucket([]byte("services"))
	if sb == nil {
		return nil, ErrNoServicesBucket
	}

	b := sb.Bucket([]byte(r.Service))
	if b == nil {
		return nil, ErrNoService{Name: r.Service}
	}

	return b, nil
}

// MakePathBucket creates the bucket for the path
func (r Request) MakePathBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
	s, err := r.ServiceBucket(tx)
	if err != nil {
		return nil, err
	}

	pb, err := s.CreateBucketIfNotExists([]byte("paths"))
	if err != nil {
		return nil, err
	}

	b, err := pb.CreateBucketIfNotExists([]byte(r.Path))
	if err != nil {
		return nil, err
	}

	return b, nil
}

// PathBucket returns the bucket for the request path, creates if needed
func (r Request) PathBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
	s, err := r.ServiceBucket(tx)
	if err != nil {
		return nil, err
	}

	pb := s.Bucket([]byte("paths"))
	if pb == nil {
		return nil, ErrNoPaths
	}

	b := pb.Bucket([]byte(r.Path))
	if b == nil {
		return nil, ErrInvalidPath{Path: r.Path}
	}

	return b, nil
}

// MakeMethodBucket returns the bucket for the request method, creates if needed
func (r Request) MakeMethodBucket(tx *bolt.Tx) (*bolt.Bucket, error) {
	s, err := r.PathBucket(tx)
	if err != nil {
		return nil, err
	}

	b, err := s.CreateBucketIfNotExists([]byte(r.Method))
	if err != nil {
		return nil, err
	}

	return b, nil
}

// LoadSettings from the database
func (r *Request) LoadSettings(tx *bolt.Tx) error {
	sb, pb, mb, err := request.Match(tx)
	if err != nil {
		return err
	}

	// Start with blank settings
	r.Settings = NewSettings()

	// load service settings
	if sb != nil {
		r.Settings = LoadSettings(sb)
	}

	// load path settings
	if pb != nil {
		r.Settings.Merge(LoadSettings(pb))
	}

	// load method settings
	if mb != nil {
		r.Settings.Merge(LoadSettings(mb))
	}

	// load provided cli flags settings
	r.Settings.Merge(settings)

	return nil
}

// Match returns the relavant db buckets for all request settings, it will first check
// for a matching alias, then check generic paths, if there is a matching alias, it
// will be returned in the path bucket.
func (r Request) Match(tx *bolt.Tx) (service, path, method *bolt.Bucket, err error) {
	service, err = r.ServiceBucket(tx)
	if err != nil {
		return nil, nil, nil, err
	}

	// match aliases first, if an alias matches then ignore path or method matches
	ab := service.Bucket([]byte("aliases"))
	if ab != nil {
		c := ab.Cursor()
		for key, _ := c.First(); key != nil; key, _ = c.Next() {
			if string(key) == r.Alias {
				path = ab.Bucket(key)
				return service, path, nil, nil
			}
		}
	}

	// match path
	pb := service.Bucket([]byte("paths"))
	if pb == nil {
		return service, nil, nil, nil
	}

	// Match the path, this will eventually be expanded to better match
	// the path.  So specific paths will be matched before generic ones
	// at the moment it requires an exact match
	c := pb.Cursor()
	for key, _ := c.First(); key != nil; key, _ = c.Next() {
		if string(key) == r.Path {
			path = pb.Bucket(key)
			break
		}
	}

	if path == nil {
		return service, path, nil, nil
	}

	method = path.Bucket([]byte(r.Method))

	return service, path, method, nil
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"flag"
	"fmt"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/go-kit/kit/log/level"
	"github.com/goph/emperror"
	"github.com/goph/healthz"
	"github.com/goph/serverz"
	"github.com/goph/stdlib/ext"
	"github.com/kelseyhightower/envconfig"
)

func main() {
	config := &configuration{}

	// Load configuration from environment
	err := envconfig.Process("", config)
	if err != nil {
		panic(err)
	}

	// Load configuration from flags
	flags := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
	config.flags(flags)
	flags.Parse(os.Args[1:])

	if config.Daemon && config.DaemonSchedule <= 0 {
		panic("Daemon mode requires the DAEMON_SCHEDULE environment variable to be set.")
	}

	// Create a new logger
	logger := newLogger(config)
	defer ext.Close(logger)

	// Create a new error handler
	errorHandler := newErrorHandler(config, logger)
	defer ext.Close(errorHandler)

	// Register error handler to recover from panics
	defer emperror.HandleRecover(errorHandler)

	healthCollector := healthz.Collector{}
	tracer := newTracer(config)
	metricsReporter := newMetricsReporter(config)

	// Application context
	appCtx := &application{
		config:          config,
		logger:          logger,
		errorHandler:    errorHandler,
		healthCollector: healthCollector,
		tracer:          tracer,
		metricsReporter: metricsReporter,
	}

	mode := "cron"
	if config.Daemon {
		mode = "daemon"
	}

	level.Info(logger).Log(
		"msg", fmt.Sprintf("Starting %s", FriendlyServiceName),
		"version", Version,
		"commitHash", CommitHash,
		"buildDate", BuildDate,
		"environment", config.Environment,
		"mode", mode,
	)

	job := newJob(appCtx)
	defer ext.Close(job)

	if false == config.Daemon {
		job.Run()
	} else {
		serverQueue := serverz.NewQueue(&serverz.Manager{Logger: logger})

		if config.Debug {
			debugServer := newDebugServer(logger)
			serverQueue.Append(debugServer, config.DebugAddr)
			defer debugServer.Close()
		}

		healthServer, status := newHealthServer(appCtx)
		serverQueue.Prepend(healthServer, config.HealthAddr)
		defer healthServer.Close()

		errChan := serverQueue.Start()

		ticker := time.NewTicker(config.DaemonSchedule)
		quit := make(chan struct{})

		go func() {
			for {
				select {
				case <-quit:
					return
				case <-ticker.C:
					go job.Run()
				}
			}
		}()

		signalChan := make(chan os.Signal, 1)
		signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)

	MainLoop:
		for {
			select {
			case err := <-errChan:
				status.SetStatus(healthz.Unhealthy)
				close(quit)
				level.Debug(logger).Log("msg", "Error received from error channel")
				emperror.HandleIfErr(errorHandler, err)

				// Break the loop, proceed with regular shutdown
				break MainLoop
			case s := <-signalChan:
				level.Info(logger).Log("msg", fmt.Sprintf("Captured %v", s))
				status.SetStatus(healthz.Unhealthy)
				close(quit)

				level.Debug(logger).Log(
					"msg", "Shutting down with timeout",
					"timeout", config.ShutdownTimeout,
				)

				ctx, cancel := context.WithTimeout(context.Background(), config.ShutdownTimeout)

				err := serverQueue.Stop(ctx)
				if err != nil {
					errorHandler.Handle(err)
				}

				// Cancel context if shutdown completed earlier
				cancel()

				// Break the loop, proceed with regular shutdown
				break MainLoop
			}
		}
	}
}
<commit_msg>Always run health and debug servers<commit_after>package main

import (
	"context"
	"flag"
	"fmt"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/go-kit/kit/log/level"
	"github.com/goph/emperror"
	"github.com/goph/healthz"
	"github.com/goph/serverz"
	"github.com/goph/stdlib/ext"
	"github.com/kelseyhightower/envconfig"
)

func main() {
	config := &configuration{}

	// Load configuration from environment
	err := envconfig.Process("", config)
	if err != nil {
		panic(err)
	}

	// Load configuration from flags
	flags := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
	config.flags(flags)
	flags.Parse(os.Args[1:])

	if config.Daemon && config.DaemonSchedule <= 0 {
		panic("Daemon mode requires the DAEMON_SCHEDULE environment variable to be set.")
	}

	// Create a new logger
	logger := newLogger(config)
	defer ext.Close(logger)

	// Create a new error handler
	errorHandler := newErrorHandler(config, logger)
	defer ext.Close(errorHandler)

	// Register error handler to recover from panics
	defer emperror.HandleRecover(errorHandler)

	healthCollector := healthz.Collector{}
	tracer := newTracer(config)
	metricsReporter := newMetricsReporter(config)

	// Application context
	appCtx := &application{
		config:          config,
		logger:          logger,
		errorHandler:    errorHandler,
		healthCollector: healthCollector,
		tracer:          tracer,
		metricsReporter: metricsReporter,
	}

	serverQueue := serverz.NewQueue(&serverz.Manager{Logger: logger})

	mode := "cron"
	if config.Daemon {
		mode = "daemon"
	}

	level.Info(logger).Log(
		"msg", fmt.Sprintf("Starting %s", FriendlyServiceName),
		"version", Version,
		"commitHash", CommitHash,
		"buildDate", BuildDate,
		"environment", config.Environment,
		"mode", mode,
	)

	job := newJob(appCtx)
	defer ext.Close(job)

	if config.Debug {
		debugServer := newDebugServer(logger)
		serverQueue.Append(debugServer, config.DebugAddr)
		defer debugServer.Close()
	}

	healthServer, status := newHealthServer(appCtx)
	serverQueue.Prepend(healthServer, config.HealthAddr)
	defer healthServer.Close()

	errChan := serverQueue.Start()

	// Necessary for daemon mode
	quit := make(chan struct{})

	if false == config.Daemon {
		job.Run()
	} else {
		ticker := time.NewTicker(config.DaemonSchedule)

		go func() {
			for {
				select {
				case <-quit:
					return
				case <-ticker.C:
					go job.Run()
				}
			}
		}()
	}

	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)

MainLoop:
	for {
		select {
		case err := <-errChan:
			status.SetStatus(healthz.Unhealthy)
			close(quit)
			level.Debug(logger).Log("msg", "Error received from error channel")
			emperror.HandleIfErr(errorHandler, err)

			// Break the loop, proceed with regular shutdown
			break MainLoop
		case s := <-signalChan:
			level.Info(logger).Log("msg", fmt.Sprintf("Captured %v", s))
			status.SetStatus(healthz.Unhealthy)
			close(quit)

			level.Debug(logger).Log(
				"msg", "Shutting down with timeout",
				"timeout", config.ShutdownTimeout,
			)

			ctx, cancel := context.WithTimeout(context.Background(), config.ShutdownTimeout)

			err := serverQueue.Stop(ctx)
			if err != nil {
				errorHandler.Handle(err)
			}

			// Cancel context if shutdown completed earlier
			cancel()

			// Break the loop, proceed with regular shutdown
			break MainLoop
		}
	}
}
<|endoftext|>


<commit_before>package crawl

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"

	"golang.org/x/net/context"
)

// Request - HTTP Request.
// Method is "GET" by default.
// URL can be absolute URL or a relative to source URL.
// If Raw is set to false, it means we expect HTML response
// and crawler, if request will be successfull will parse
// Response body as HTML.
// If Form is not empty, request will be send with POST method (if empty)
// and with form encoded body.
// Multipart form is not implemented.
type Request struct {
	Method, URL string
	Raw         bool
	Source      *Response
	Callbacks   []interface{}
	Form        map[string]string
	Context     context.Context
}

// Callbacks - Helper for creating list of interfaces.
func Callbacks(v ...interface{}) []interface{} {
	return v
}

// HTTPRequest - Creates a http.Request structure.
// If Method is empty it's set "GET".
func (req *Request) HTTPRequest() (r *http.Request, err error) {
	u, err := req.GetURL()
	if err != nil {
		return
	}

	// Make http request
	r = &http.Request{
		URL:    u,
		Method: req.GetMethod(),
		Header: make(http.Header),
	}

	// Set referer if any
	if req.Source != nil {
		r.Header.Set("Referer", req.Source.GetURL().String())
	}

	// Return now if no form
	if req.Form == nil {
		return
	}

	// Set Content-Type header
	r.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	// Set request method if currently is empty
	if req.Method == "" {
		r.Method = "POST"
		req.Method = "POST"
	}

	// Copy form values to request form
	formdata := make(url.Values)
	for key, value := range req.Form {
		formdata.Set(key, value)
	}

	// Set request body
	data := strings.NewReader(formdata.Encode())
	r.Body = ioutil.NopCloser(data)

	// Request content length
	r.ContentLength = int64(data.Len())

	return
}

// GetMethod - Returns request Method. If empty returns "GET".
func (req *Request) GetMethod() string {
	if req.Method == "" {
		return "GET"
	}
	return req.Method
}

// ResolveURL - Resolves url
func (req *Request) ResolveURL(uri *url.URL) (u *url.URL, err error) {
	u, err = req.GetURL()
	if uri != nil {
		u = u.ResolveReference(uri)
	}
	return
}

// GetURL - Parses request URL.
// If request Source is set, parsed - URL is resolved
// with reference to source request URL.
func (req *Request) GetURL() (u *url.URL, err error) {
	u, err = url.Parse(req.URL)
	if err != nil {
		return
	}
	if src := req.Source; src != nil {
		u = src.GetURL().ResolveReference(u)
	}
	return
}

// String -
func (req *Request) String() string {
	return fmt.Sprintf("%s %s", req.GetMethod(), req.URL)
}
<commit_msg>Request: json tags + referer field<commit_after>package crawl

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"

	"golang.org/x/net/context"
)

// Request - HTTP Request.
// Method is "GET" by default.
// URL can be absolute URL or a relative to source URL.
// If Raw is set to false, it means we expect HTML response
// and crawler, if request will be successfull will parse
// Response body as HTML.
// If Form is not empty, request will be send with POST method (if empty)
// and with form encoded body.
// Multipart form is not implemented.
type Request struct {
	URL       string            `json:"url,omitempty"`
	Method    string            `json:"method,omitempty"`
	Referer   string            `json:"referer,omitempty"`
	Form      map[string]string `json:"form,omitempty"`
	Raw       bool              `json:"raw,omitempty"`
	Callbacks []interface{}     `json:"callbacks,omitempty"`
	Source    *Response         `json:"-"`
	Context   context.Context   `json:"-"`
}

// Callbacks - Helper for creating list of interfaces.
func Callbacks(v ...interface{}) []interface{} {
	return v
}

// HTTPRequest - Creates a http.Request structure.
// If Method is empty it's set "GET".
func (req *Request) HTTPRequest() (r *http.Request, err error) {
	u, err := req.GetURL()
	if err != nil {
		return
	}

	// Make http request
	r = &http.Request{
		URL:    u,
		Method: req.GetMethod(),
		Header: make(http.Header),
	}

	// Set referer if any
	if req.Referer != "" {
		r.Header.Set("Referer", req.Referer)
	} else if req.Source != nil {
		r.Header.Set("Referer", req.Source.GetURL().String())
	}

	// Return now if no form
	if req.Form == nil {
		return
	}

	// Set Content-Type header
	r.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	// Set request method if currently is empty
	if req.Method == "" {
		r.Method = "POST"
		req.Method = "POST"
	}

	// Copy form values to request form
	formdata := make(url.Values)
	for key, value := range req.Form {
		formdata.Set(key, value)
	}

	// Set request body
	data := strings.NewReader(formdata.Encode())
	r.Body = ioutil.NopCloser(data)

	// Request content length
	r.ContentLength = int64(data.Len())

	return
}

// GetMethod - Returns request Method. If empty returns "GET".
func (req *Request) GetMethod() string {
	if req.Method == "" {
		return "GET"
	}
	return req.Method
}

// ResolveURL - Resolves url
func (req *Request) ResolveURL(uri *url.URL) (u *url.URL, err error) {
	u, err = req.GetURL()
	if uri != nil {
		u = u.ResolveReference(uri)
	}
	return
}

// GetURL - Parses request URL.
// If request Source is set, parsed - URL is resolved
// with reference to source request URL.
func (req *Request) GetURL() (u *url.URL, err error) {
	u, err = url.Parse(req.URL)
	if err != nil {
		return
	}
	if req.Referer != "" {
		ref, err := url.Parse(req.Referer)
		if err != nil {
			return
		}
		u = ref.ResolveReference(u)
	} else if src := req.Source; src != nil {
		u = src.GetURL().ResolveReference(u)
	}
	return
}

// String -
func (req *Request) String() string {
	return fmt.Sprintf("%s %s", req.GetMethod(), req.URL)
}
<|endoftext|>


<commit_before>package main

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

var (
	Input *bufio.Reader
	Namespace string
	Variables map[string]string
)

func prompt(text string) (string, error) {
	fmt.Print(text + " ")
	response, err := Input.ReadString('\n')
	if err != nil {
		return "", err
	}
	return strings.Trim(response, "\n"), nil
}

func namespaceSelector(selector func([]string)(string, error)) error {
	namespaces, err := GetNamespaces()
	if err != nil {
		return err
	}

	targets := make([]string, len(namespaces.Items))
	for num, ns := range namespaces.Items {
		targets[num] = ns.Name
	}

	response, err := selector(targets)
	if err != nil {
		return err
	}
	Namespace = ClosestString(response, targets)
	return nil
}

func pickNamespace() error {
	return namespaceSelector(func(namespaces []string) (string, error) {
		for _, ns := range namespaces {
			fmt.Printf("%s\n", ns)
		}
		return prompt("Select namespace:")
	})
}

func switchNamespace(ns string) error {
	return namespaceSelector(func(namespaces []string) (string, error) {
		return ns, nil
	})
}

func repl() error {
	command, err := prompt(Namespace)
	if err != nil {
		return err
	}

	for from, to := range Variables {
		command = strings.Replace(command, from, to, -1)
	}

	parts := strings.Split(command, " ")
	if parts[0] == "namespace" || parts[0] == "ns" {
		if len(parts) > 1 {
			switchNamespace(parts[1])
		} else {
			pickNamespace()
		}
		return nil
	}

	output, err := KubectlSh(command)

	if strings.HasPrefix(command, "get") {
		variableIndex := 0
		for _, line := range strings.Split(output, "\n") {
			if strings.HasPrefix(line, "NAME ") {
				fmt.Printf("   \t%s\n", line)
			} else {
				variableIndex++
				fmt.Printf("$%v \t%s\n", variableIndex, line)
			}
			key := fmt.Sprintf("$%d", variableIndex)
			Variables[key] = strings.Split(line, " ")[0]
		}
	} else {
		fmt.Println(output)
	}
	return err
}

func assert(v interface{}) {
	if v != nil {
		log.Fatal(v)
	}
}

func main() {
	Variables = make(map[string]string)
	Input = bufio.NewReader(os.Stdin)
	assert(KubernetesSetup())
	assert(pickNamespace())

	for {
		repl()
	}
}
<commit_msg>refactor prompt to replace variables<commit_after>package main

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

var (
	Input *bufio.Reader
	Namespace string
	Variables map[string]string
)

func prompt(text string) (string, error) {
	fmt.Print(text + " ")
	line, err := Input.ReadString('\n')
	if err != nil {
		return "", err
	}
	response := strings.Trim(line, "\n")
	for from, to := range Variables {
		response = strings.Replace(response, from, to, -1)
	}
	return response, nil
}

func namespaceSelector(selector func([]string)(string, error)) error {
	namespaces, err := GetNamespaces()
	if err != nil {
		return err
	}

	targets := make([]string, len(namespaces.Items))
	for num, ns := range namespaces.Items {
		targets[num] = ns.Name
	}

	response, err := selector(targets)
	if err != nil {
		return err
	}
	Namespace = ClosestString(response, targets)
	return nil
}

func pickNamespace() error {
	return namespaceSelector(func(namespaces []string) (string, error) {
		for n, ns := range namespaces {
			key := fmt.Sprintf("$%d", n)
			Variables[key] = ns
			fmt.Printf("%s \t%s\n", key, ns)
		}
		return prompt("Select namespace:")
	})
}

func switchNamespace(ns string) error {
	return namespaceSelector(func(namespaces []string) (string, error) {
		return ns, nil
	})
}

func repl() error {
	command, err := prompt(Namespace)
	if err != nil {
		return err
	}

	parts := strings.Split(command, " ")
	if parts[0] == "namespace" || parts[0] == "ns" {
		if len(parts) > 1 {
			switchNamespace(parts[1])
		} else {
			pickNamespace()
		}
		return nil
	}

	output, err := KubectlSh(command)

	if strings.HasPrefix(command, "get") {
		variableIndex := 0
		for _, line := range strings.Split(output, "\n") {
			if strings.HasPrefix(line, "NAME ") {
				fmt.Printf("   \t%s\n", line)
			} else {
				variableIndex++
				fmt.Printf("$%v \t%s\n", variableIndex, line)
			}
			key := fmt.Sprintf("$%d", variableIndex)
			Variables[key] = strings.Split(line, " ")[0]
		}
	} else {
		fmt.Println(output)
	}
	return err
}

func assert(v interface{}) {
	if v != nil {
		log.Fatal(v)
	}
}

func main() {
	Variables = make(map[string]string)
	Input = bufio.NewReader(os.Stdin)
	assert(KubernetesSetup())
	assert(pickNamespace())

	for {
		repl()
	}
}
<|endoftext|>


<commit_before>// (c) 2021, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package main

import (
	"fmt"
	"log"
	"os"
	"syscall"

	appPlugin "github.com/ava-labs/avalanchego/main/plugin"
	"github.com/ava-labs/avalanchego/main/process"
	"github.com/ava-labs/avalanchego/utils"
	"github.com/ava-labs/avalanchego/utils/logging"
	"github.com/ava-labs/avalanchego/utils/perms"
	"github.com/hashicorp/go-hclog"
	"github.com/hashicorp/go-plugin"
)

// main is the primary entry point to Avalanche.
func main() {
	// parse config using viper
	if err := parseViper(); err != nil {
		// Returns exit code 1
		log.Fatalf("parsing parameters returned with error %s", err)
	}

	// Set the data directory permissions to be read write.
	if err := perms.ChmodR(defaultDataDir, true, perms.ReadWriteExecute); err != nil {
		log.Fatalf("failed to restrict the permissions of the data directory with error %s", err)
	}

	c := Config
	// Create the logger
	logFactory := logging.NewFactory(c.LoggingConfig)
	defer logFactory.Close()

	log, err := logFactory.Make()
	if err != nil {
		fmt.Printf("starting logger failed with: %s\n", err)
		os.Exit(1)
	}
	app := process.NewApp(c, logFactory, log)
	if c.PluginMode {
		plugin.Serve(&plugin.ServeConfig{
			HandshakeConfig: appPlugin.Handshake,
			Plugins: map[string]plugin.Plugin{
				"nodeProcess": appPlugin.New(app),
			},
			// A non-nil value here enables gRPC serving for this plugin
			GRPCServer: plugin.DefaultGRPCServer,
			Logger: hclog.New(&hclog.LoggerOptions{
				Level: hclog.Error,
			}),
		})
		return
	}

	// If we get a a SIGINT or SIGTERM, tell the node to stop.
	// If [app.Start()] has been called, it will return.
	// If not, then when [app.Start()] is called below, it will immediately return 1.
	_ = utils.HandleSignals(
		func(os.Signal) {
			app.Stop()
		},
		syscall.SIGINT, syscall.SIGTERM,
	)
	// Start the node
	exitCode := app.Start()
	os.Exit(exitCode)
}
<commit_msg>reverted header removal<commit_after>// (c) 2021, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package main

import (
	"fmt"
	"log"
	"os"
	"syscall"

	appPlugin "github.com/ava-labs/avalanchego/main/plugin"
	"github.com/ava-labs/avalanchego/main/process"
	"github.com/ava-labs/avalanchego/utils"
	"github.com/ava-labs/avalanchego/utils/logging"
	"github.com/ava-labs/avalanchego/utils/perms"
	"github.com/hashicorp/go-hclog"
	"github.com/hashicorp/go-plugin"
)

const (
	header = "" +
		`     _____               .__                       .__` + "\n" +
		`    /  _  \___  _______  |  | _____    ____   ____ |  |__   ____    ,_ o` + "\n" +
		`   /  /_\  \  \/ /\__  \ |  | \__  \  /    \_/ ___\|  |  \_/ __ \   / //\,` + "\n" +
		`  /    |    \   /  / __ \|  |__/ __ \|   |  \  \___|   Y  \  ___/    \>> |` + "\n" +
		`  \____|__  /\_/  (____  /____(____  /___|  /\___  >___|  /\___  >    \\` + "\n" +
		`          \/           \/          \/     \/     \/     \/     \/`
)

// main is the primary entry point to Avalanche.
func main() {
	// parse config using viper
	if err := parseViper(); err != nil {
		// Returns exit code 1
		log.Fatalf("parsing parameters returned with error %s", err)
	}

	// Set the data directory permissions to be read write.
	if err := perms.ChmodR(defaultDataDir, true, perms.ReadWriteExecute); err != nil {
		log.Fatalf("failed to restrict the permissions of the data directory with error %s", err)
	}

	c := Config
	// Create the logger
	logFactory := logging.NewFactory(c.LoggingConfig)
	defer logFactory.Close()

	log, err := logFactory.Make()
	if err != nil {
		fmt.Printf("starting logger failed with: %s\n", err)
		os.Exit(1)
	}
	app := process.NewApp(c, logFactory, log)
	if c.PluginMode {
		plugin.Serve(&plugin.ServeConfig{
			HandshakeConfig: appPlugin.Handshake,
			Plugins: map[string]plugin.Plugin{
				"nodeProcess": appPlugin.New(app),
			},
			// A non-nil value here enables gRPC serving for this plugin
			GRPCServer: plugin.DefaultGRPCServer,
			Logger: hclog.New(&hclog.LoggerOptions{
				Level: hclog.Error,
			}),
		})
		return
	}

	fmt.Println(header)

	// If we get a a SIGINT or SIGTERM, tell the node to stop.
	// If [app.Start()] has been called, it will return.
	// If not, then when [app.Start()] is called below, it will immediately return 1.
	_ = utils.HandleSignals(
		func(os.Signal) {
			app.Stop()
		},
		syscall.SIGINT, syscall.SIGTERM,
	)
	// Start the node
	exitCode := app.Start()
	os.Exit(exitCode)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/lifeforms/urlcheck/urlcheck"
	"io/ioutil"
	"os"
)

func parseArgs() (manifestfile string, timeout uint, verbose bool) {
	flagi := flag.String("i", "urls.yaml", "Input file with YAML manifest")
	flagt := flag.Uint("t", 5, "Timeout for each HTTP request in seconds, 0 for no timeout")
	flagv := flag.Bool("v", false, "Verbose, prints the result of each test")
	flag.Parse()

	manifestfile = *flagi
	timeout = *flagt
	verbose = *flagv
	return
}

func readManifest(manifestfile string) (manifest urlcheck.Manifest, err error) {
	y, err := ioutil.ReadFile(manifestfile)
	if err == nil {
		manifest, err = urlcheck.FromYAML(y)
	}
	return
}

func main() {
	manifestfile, timeout, verbose := parseArgs()

	urlcheck.Timeout = timeout
	urlcheck.Verbose = verbose
	manifest, err := readManifest(manifestfile)
	if err == nil {
		err = manifest.Test()
	}

	if err != nil {
		fmt.Println("Failures:", err)
		os.Exit(1)
	} else {
		if verbose {
			fmt.Println("OK")
		}
	}
}
<commit_msg>main: choose server in args<commit_after>package main

import (
	"errors"
	"flag"
	"fmt"
	"github.com/lifeforms/urlcheck/urlcheck"
	"io/ioutil"
	"os"
)

func parseArgs() (manifestfile string, server string, timeout uint, verbose bool) {
	flagi := flag.String("i", "manifest.yaml", "Input file with YAML manifest")
	flags := flag.String("s", "", "Only check this server, default check all servers in manifest")
	flagt := flag.Uint("t", 5, "Timeout for each HTTP request in seconds, 0 for no timeout")
	flagv := flag.Bool("v", false, "Verbose, prints the result of each test")
	flag.Parse()

	manifestfile = *flagi
	server = *flags
	timeout = *flagt
	verbose = *flagv
	return
}

func readManifest(manifestfile string) (manifest urlcheck.Manifest, err error) {
	y, err := ioutil.ReadFile(manifestfile)
	if err == nil {
		manifest, err = urlcheck.FromYAML(y)
	}
	return
}

func filterManifest(in urlcheck.Manifest, server string) (out urlcheck.Manifest, err error) {
	if server == "" {
		return in, nil
	}

	for _, s := range in {
		if s.Name == server {
			out = append(out, s)
		}
	}
	if len(out) == 0 {
		return nil, errors.New("Server not in manifest: " + server)
	}
	return
}

func main() {
	manifestfile, server, timeout, verbose := parseArgs()

	urlcheck.Timeout = timeout
	urlcheck.Verbose = verbose
	manifest, err := readManifest(manifestfile)

	if err == nil {
		manifest, err = filterManifest(manifest, server)
	}

	if err == nil {
		err = manifest.Test()
	}

	if err != nil {
		fmt.Println("Failures:", err)
		os.Exit(1)
	} else {
		if verbose {
			fmt.Println("OK")
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
)

var x = 0

func main() {

	x := 40

	foo := func() {
		fmt.Println(x)
	}

	fmt.Println(x)
	{
		x := 1
		y := x
		fmt.Println(x)
		{
			fmt.Println(y)
			foo()
		}

		increment := wrapper()
		fmt.Println(increment())
		fmt.Println(increment())
	}
}

func wrapper() func() int {
	//x := 10
	return func() int {
		x++
		return x
	}
}

<commit_msg>Named returns.<commit_after>package main

import (
	"fmt"
)

func main() {
	fmt.Println(foo(2))

}

func foo(x int) (y, z int) {
	y = x * 10
	z = x * 20
	return
}
<|endoftext|>


<commit_before>package main

import (
	//	bigv "bigv.io/client/lib"
	"flag"
	"fmt"
	"os"
	"os/signal"
)

func main() {
	ch := make(chan os.Signal, 1)
	signal.Notify(ch, os.Interrupt)
	go func() {
		for _ = range ch {
			fmt.Printf("\r\nCaught an interrupt - exiting.\r\n")
			os.Exit(int(E_TRAPPED_INTERRUPT))
		}

	}()

	flags := MakeCommonFlagSet()

	flags.Parse(os.Args[1:])
	configDir := ""
	value := flags.Lookup("config-dir").Value
	if value != nil {
		configDir = value.String()
	}
	config := NewConfig(configDir, flag.CommandLine)

	dispatch := NewDispatcher(config)

	dispatch.Do(flags.Args())
	os.Exit(0)
}
<commit_msg>Actually use the global flags.<commit_after>package main

import (
	//	bigv "bigv.io/client/lib"
	"flag"
	"fmt"
	"os"
	"os/signal"
)

func main() {
	ch := make(chan os.Signal, 1)
	signal.Notify(ch, os.Interrupt)
	go func() {
		for _ = range ch {
			fmt.Printf("\r\nCaught an interrupt - exiting.\r\n")
			os.Exit(int(E_TRAPPED_INTERRUPT))
		}

	}()

	flags := MakeCommonFlagSet()

	flags.Parse(os.Args[1:])

	configDir := ""
	value := flags.Lookup("config-dir").Value
	if value != nil {
		configDir = value.String()
	}
	config := NewConfig(configDir, flag.CommandLine)
	config.ImportFlags(flags)

	dispatch := NewDispatcher(config)

	dispatch.Do(flags.Args())
	os.Exit(0)
}
<|endoftext|>


<commit_before>package junos

// Establishing a session
func Example() {
	jnpr, err := junos.NewSession(host, user, password)
	if err != nil {
		log.Fatal(err)
	}
	defer jnpr.Close()
}

// To View the entire configuration, use the keyword "full" for the first
// argument. If anything else outside of "full" is specified, it will return
// the configuration of the specified top-level stanza only. So "security" would return everything
// under the "security" stanza.
func Example_viewConfiguration() {
	// Output format can be "text" or "xml"
	config, err := jnpr.GetConfig("full", "text")
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(config)
}

// Comparing and Rolling Back Configurations
func Example_rollbackConfigurations() {
	// If you want to view the difference between the current configuration and a rollback
	// one, then you can use the ConfigDiff() function to specify a previous config:
	diff, err := jnpr.ConfigDiff(3)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(diff)

	// You can rollback to a previous state, or the rescue configuration by using
	// the RollbackConfig() function:
	err := jnpr.RollbackConfig(3)
	if err != nil {
		fmt.Println(err)
	}

	// Create a rescue config from the active configuration.
	jnpr.Rescue("save")

	// You can also delete a rescue config.
	jnpr.Rescue("delete")

	// Rollback to the "rescue" configuration.
	err := jnpr.RollbackConfig("rescue")
	if err != nil {
		fmt.Println(err)
	}
}

// Device Configuration
func Example_configuringDevices() {
	// Use the LoadConfig() function to load the configuration from a file.

	// When configuring a device, it is good practice to lock the configuration database,
	// load the config, commit the configuration, and then unlock the configuration database.
	// You can do this with the following functions: Lock(), Commit(), Unlock()

	// Multiple ways to commit a configuration

	// Commit the configuration as normal
	Commit()

	// Check the configuration for any syntax errors (NOTE: you must still issue a Commit() afterwards)
	CommitCheck()

	// Commit at a later time, i.e. 4:30 PM
	CommitAt("16:30:00")

	// Rollback configuration if a Commit() is not issued within the given <minutes>.
	CommitConfirm(15)

	// You can configure the Junos device by uploading a local file, or pulling from an
	// FTP/HTTP server. The LoadConfig() function takes three arguments:

	// filename or URL, format, and a boolean (true/false) "commit-on-load"

	// If you specify a URL, it must be in the following format:

	// ftp://<username>:<password>@hostname/pathname/file-name
	// http://<username>:<password>@hostname/pathname/file-name

	// Note: The default value for the FTP path variable is the user’s home directory. Thus,
	// by default the file path to the configuration file is relative to the user directory.
	// To specify an absolute path when using FTP, start the path with the characters %2F;
	// for example: ftp://username:password@hostname/%2Fpath/filename.

	// The format of the commands within the file must be one of the following types:

	// set
	// system name-server 1.1.1.1

	// text
	// system {
	//     name-server 1.1.1.1;
	// }

	// xml
	// <system>
	//     <name-server>
	//         <name>1.1.1.1</name>
	//     </name-server>
	// </system>

	// If the third option is "true" then after the configuration is loaded, a commit
	// will be issued. If set to "false," you will have to commit the configuration
	// using one of the Commit() functions.
	jnpr.Lock()
	err := jnpr.LoadConfig("path-to-file.txt", "set", true)
	if err != nil {
		fmt.Println(err)
	}
	jnpr.Unlock()
}

// Running Commands on a Device
func Example_runCommands() {
	// You can run operational mode commands such as "show" and "request" by using the
	// Command() function. Output formats can be "text" or "xml":

	// Results returned in text format
	txtOutput, err := jnpr.Command("show chassis hardware", "text")
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(txtOutput)

	// Results returned in XML format
	xmlOutput, err := jnpr.Command("show chassis hardware", "xml")
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(xmlOutput)
}

func Example_PlatformInformation() {
	// When you call the PrintFacts() function, it just prints out the platform
	// and software information to the console.
	jnpr.PrintFacts()

	// You can also loop over the struct field that contains this information yourself:
	fmt.Printf("Hostname: %s", jnpr.Hostname)
	for _, data := range jnpr.Platform {
		fmt.Printf("Model: %s, Version: %s", data.Model, data.Version)
	}
	// Output: Model: SRX240H2, Version: 12.1X47-D10.4
}
<commit_msg>Added more examples<commit_after>package junos

// Establishing a session
func Example() {
	jnpr, err := junos.NewSession(host, user, password)
	if err != nil {
		log.Fatal(err)
	}
	defer jnpr.Close()
}

// To View the entire configuration, use the keyword "full" for the first
// argument. If anything else outside of "full" is specified, it will return
// the configuration of the specified top-level stanza only. So "security" would return everything
// under the "security" stanza.
func Example_viewConfiguration() {
	// Output format can be "text" or "xml"
	config, err := jnpr.GetConfig("full", "text")
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(config)
}

// Comparing and Rolling Back Configurations
func Example_rollbackConfigurations() {
	// If you want to view the difference between the current configuration and a rollback
	// one, then you can use the ConfigDiff() function to specify a previous config:
	diff, err := jnpr.ConfigDiff(3)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(diff)

	// You can rollback to a previous state, or the rescue configuration by using
	// the RollbackConfig() function:
	err := jnpr.RollbackConfig(3)
	if err != nil {
		fmt.Println(err)
	}

	// Create a rescue config from the active configuration.
	jnpr.Rescue("save")

	// You can also delete a rescue config.
	jnpr.Rescue("delete")

	// Rollback to the "rescue" configuration.
	err := jnpr.RollbackConfig("rescue")
	if err != nil {
		fmt.Println(err)
	}
}

// Device Configuration
func Example_configuringDevices() {
	// Use the LoadConfig() function to load the configuration from a file.

	// When configuring a device, it is good practice to lock the configuration database,
	// load the config, commit the configuration, and then unlock the configuration database.
	// You can do this with the following functions: Lock(), Commit(), Unlock()

	// Multiple ways to commit a configuration

	// Commit the configuration as normal
	Commit()

	// Check the configuration for any syntax errors (NOTE: you must still issue a Commit() afterwards)
	CommitCheck()

	// Commit at a later time, i.e. 4:30 PM
	CommitAt("16:30:00")

	// Rollback configuration if a Commit() is not issued within the given <minutes>.
	CommitConfirm(15)

	// You can configure the Junos device by uploading a local file, or pulling from an
	// FTP/HTTP server. The LoadConfig() function takes three arguments:

	// filename or URL, format, and a boolean (true/false) "commit-on-load"

	// If you specify a URL, it must be in the following format:

	// ftp://<username>:<password>@hostname/pathname/file-name
	// http://<username>:<password>@hostname/pathname/file-name

	// Note: The default value for the FTP path variable is the user’s home directory. Thus,
	// by default the file path to the configuration file is relative to the user directory.
	// To specify an absolute path when using FTP, start the path with the characters %2F;
	// for example: ftp://username:password@hostname/%2Fpath/filename.

	// The format of the commands within the file must be one of the following types:

	// set
	// system name-server 1.1.1.1

	// text
	// system {
	//     name-server 1.1.1.1;
	// }

	// xml
	// <system>
	//     <name-server>
	//         <name>1.1.1.1</name>
	//     </name-server>
	// </system>

	// If the third option is "true" then after the configuration is loaded, a commit
	// will be issued. If set to "false," you will have to commit the configuration
	// using one of the Commit() functions.
	jnpr.Lock()
	err := jnpr.LoadConfig("path-to-file.txt", "set", true)
	if err != nil {
		fmt.Println(err)
	}
	jnpr.Unlock()
}

// Running Commands on a Device
func Example_runCommands() {
	// You can run operational mode commands such as "show" and "request" by using the
	// Command() function. Output formats can be "text" or "xml":

	// Results returned in text format
	txtOutput, err := jnpr.Command("show chassis hardware", "text")
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(txtOutput)

	// Results returned in XML format
	xmlOutput, err := jnpr.Command("show chassis hardware", "xml")
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(xmlOutput)
}

func Example_displayPlatform_Information() {
	// When you call the PrintFacts() function, it just prints out the platform
	// and software information to the console.
	jnpr.PrintFacts()

	// You can also loop over the struct field that contains this information yourself:
	fmt.Printf("Hostname: %s", jnpr.Hostname)
	for _, data := range jnpr.Platform {
		fmt.Printf("Model: %s, Version: %s", data.Model, data.Version)
	}
	// Output: Model: SRX240H2, Version: 12.1X47-D10.4
}
<|endoftext|>


<commit_before>package main

import (
    "github.com/ant0ine/go-json-rest/rest"
    "github.com/tbruyelle/hipchat-go/hipchat"
    "bytes"
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "os"
    "strconv"
    "strings"
    "sync"
    "time"
)

/*
To-do list
---------
Manually depart a train
Derail a train
Start a train at a designated time
Tag people when the train leaves
Propose a train that starts if enough people join -> X
-----
Keep track of usage statistics
Look into making your own log to log things

53 so far
Look into moving to AWS
*/

var authKey string = ""
var roomName string = ""

var station *Station = &Station{
	Lock: &sync.Mutex{},
	Trains: make(map[string]*Train),
}

type WebhookMessage struct {
    Item struct {
        MessageStruct struct {
        	From struct {
        		MentionName string `json:"mention_name"`
        	}
        	Message string `json:"message"`
        } `json:"message"`

    }  `json:"item"`
}

type Train struct {
	Lock *sync.Mutex
	LeavingTimer *time.Timer
	ReminderTimer *time.Timer
	MapDestination string
	DisplayDestination string
	Passengers []string
	PassengerSet map[string]struct{}
}

func NewTrain(conductor string, departure int, dest string) *Train {
	timer := time.NewTimer(time.Minute * time.Duration(departure))
	timer2 := time.NewTimer(time.Minute * time.Duration(departure - 1))
	users := []string{conductor}
	trainMap := make(map[string]struct{})
	trainMap[conductor] = struct{}{}
	return &Train{
		Lock: &sync.Mutex{},
		LeavingTimer: timer,
		ReminderTimer: timer2,
		MapDestination: strings.ToLower(dest),
		DisplayDestination: dest,
		Passengers: users,
		PassengerSet: trainMap,
	}
}

func (t *Train) NewPassenger(pass string) error {
	t.Lock.Lock()
	defer t.Lock.Unlock()
	_, ok := t.PassengerSet[pass]
	if !ok {
		t.PassengerSet[pass] = struct{}{}
		t.Passengers = append(t.Passengers, pass)
		return nil
	} else {
		log.Printf("Passenger %s is already on the train\n", pass)
		return fmt.Errorf("Passenger %s is already on the train", pass)
	}
}

func (t *Train) PassengerString() string {
	t.Lock.Lock()
	defer t.Lock.Unlock()
	var buffer bytes.Buffer
	for i, v := range t.Passengers {
	   buffer.WriteString(v)
	   if i != len(t.Passengers) - 1 {
	    	buffer.WriteString(", ")
	    }
	   if i == len(t.Passengers) - 2 {
	    	buffer.WriteString("and ")
	   }
	}
	return buffer.String()

}

type Station struct {
	Lock *sync.Mutex
	Trains map[string]*Train
}

func (s *Station) AddTrain(t *Train) error {
	s.Lock.Lock()
	defer s.Lock.Unlock()
	_, ok := s.Trains[t.MapDestination]
	if !ok {
		s.Trains[t.MapDestination] = t
		return nil
	} else {
		log.Printf("Train to %s already exists", t.DisplayDestination)
		return fmt.Errorf("Train to %s already exists", t.DisplayDestination)
	}
}

func (s *Station) DeleteTrain(dest string) error {
	s.Lock.Lock()
	defer s.Lock.Unlock()
	_, ok := s.Trains[dest]
	if ok {
		 delete(s.Trains, dest)
		 return nil
	} else {
		log.Printf("The train to %s doesn't exist so it can't be removed", dest)
		return fmt.Errorf("The train to %s doesn't exist so it can't be removed", dest)
	}
}

func PostMessage(msg string) {
	c := hipchat.NewClient(authKey)
	msgReq := &hipchat.NotificationRequest{Message: msg}
	_, err := c.Room.Notification(roomName, msgReq)
	if err != nil {
		panic(err)
	}
}

func MonitorTrain(train *Train) {
	for {
		select {
	    case <- train.LeavingTimer.C:
	    	var buffer bytes.Buffer
	    	start := fmt.Sprintf("The train to %v has left the station with ", train.DisplayDestination)
	    	buffer.WriteString(start)
	    	buffer.WriteString(train.PassengerString())
	    	buffer.WriteString(" on it!")
	    	PostMessage(buffer.String())
	    	station.DeleteTrain(train.MapDestination)
	    	return
	    case <- train.ReminderTimer.C:
            PostMessage(fmt.Sprintf("Reminder, the next train to %v leaves in one minute", train.DisplayDestination))
	    default:
		}
	}
}

func GetDestinationAndTime(start int, messageParts []string, getTime bool) (string, int, error) {
	var dest bytes.Buffer
	for i := start; i < len(messageParts); i++ {
		if getTime {
			num, err := strconv.Atoi(messageParts[i])
			if err == nil && i == len(messageParts) - 1 {
				return dest.String(), num, nil
			}
		}
		if i > start {
			dest.WriteString(" ")
		}
		dest.WriteString(messageParts[i])
	}
	if !getTime {
		return dest.String(), 0, nil
	}
	return "", 0, fmt.Errorf("Couldn't parse dest and/or time to departure")
}

func Handler(w rest.ResponseWriter, r *rest.Request) {
	var webMsg WebhookMessage
	decoder := json.NewDecoder(r.Body)
	err := decoder.Decode(&webMsg)
	if err != nil {
		log.Printf(err.Error())
		PostMessage(err.Error())
		return
	}

	author := webMsg.Item.MessageStruct.From.MentionName
	insufficientParams := fmt.Sprintf("%v messed up and forgot to provide the sufficient number of params", author)
	messageParts := strings.Split(webMsg.Item.MessageStruct.Message, " ")

	var msg string
	if len(messageParts) < 2 {
		PostMessage(insufficientParams)
		return
	}
	cmd := strings.ToLower(messageParts[1])
	malformed := "Your command is malformed or not found, please view the help message (/train help) for more details"
	notFound := "That train doesn't exist, please try again"
	switch cmd {
	case "help":
		msg = "Usage: /train start <destination> <#minutes> || /train join <destination> || /train passengers <destination> || /train active"
		PostMessage(msg)
	case "passengers":
		dest, _, err := GetDestinationAndTime(2, messageParts, false)
		if err != nil {
			PostMessage(err.Error())
			break
		}
		train, ok := station.Trains[strings.ToLower(dest)]
		if !ok {
			PostMessage(notFound)
		} else {
			if len(train.Passengers) == 1 {
				msg = fmt.Sprintf("%v is on the train to %v", train.PassengerString(), train.DisplayDestination)
			} else {
				msg = fmt.Sprintf("%v are on the train to %v", train.PassengerString(), train.DisplayDestination)
			}
			PostMessage(msg)
		}
	case "join":
		dest, _, err := GetDestinationAndTime(2, messageParts, false)
		if err != nil {
			PostMessage(err.Error())
			break
		}
		train, ok := station.Trains[strings.ToLower(dest)]
		if ok {
			err := train.NewPassenger(author)
			if err == nil {
				msg = fmt.Sprintf("%s jumped on the train to %s", author, train.DisplayDestination)
				PostMessage(msg)
			} else {
				msg = err.Error()
				PostMessage(msg)
			}
		} else {
			PostMessage(notFound)
		}
	case "start":
		dest, length, err := GetDestinationAndTime(2, messageParts, true)
		if err != nil {
			PostMessage(malformed)
			break
		}
		if length <= 0 {
			msg = fmt.Sprintf("Please specify a time greater than 0 mins")
			PostMessage(msg)
			break
		}
		_, ok := station.Trains[strings.ToLower(dest)]
		if ok {
			msg = fmt.Sprintf("There's already a train to %v!", dest)
			PostMessage(msg)
			break
		} else {
			train := NewTrain(author, length, dest)
			err = station.AddTrain(train)
			if err != nil {
				PostMessage(err.Error())
				break
			} else {
				msg = fmt.Sprintf("%s has started a train to %v that leaves in %v minutes!", author, train.DisplayDestination, length)
				PostMessage(msg)
				go MonitorTrain(train)
			}
		}
	case "active":
		if len(messageParts) != 2 {
			PostMessage(malformed)
			break
		}
		if len(station.Trains) == 0 {
			msg = fmt.Sprintf("There are currently no active trains")
			PostMessage(msg)
		} else {
			var finalMsg bytes.Buffer
			finalMsg.WriteString("There are trains to: ")
			i := 0
			for _, v := range station.Trains {
				if len(station.Trains) == 1 {
					msg = fmt.Sprintf("There is currently a train to %v (with %v on it)", v.DisplayDestination, v.PassengerString())
					PostMessage(msg)
					return
				} else {
					finalMsg.WriteString(fmt.Sprintf("%v (with %v on it)", v.DisplayDestination, v.PassengerString()))
				}
				if i == len(station.Trains) - 2 {
					finalMsg.WriteString("and ")
	 		    }
				if i != len(station.Trains) - 1 {
					finalMsg.WriteString(", ")
				}
				i++

			}
			PostMessage(finalMsg.String())
		}
	default:
		PostMessage(malformed)
	}
}

func ValidityHandler(w rest.ResponseWriter, r *rest.Request) {
	str := "Everything is OK!"
	w.WriteJson(&str)
}

func main() {
   	api := rest.NewApi()
    api.Use(rest.DefaultDevStack...)

    router, err := rest.MakeRouter(
  		rest.Get("/", ValidityHandler),
    	rest.Post("/train", Handler),
    )

    api.SetApp(router)
    ip := os.Getenv("OPENSHIFT_GO_IP")
    port := os.Getenv("OPENSHIFT_GO_PORT")
    if port == "" {
    	port = "8080"
    }
    bind := fmt.Sprintf("%s:%s",ip,port)
	err = http.ListenAndServe(bind, api.MakeHandler())
	if err != nil {
    	log.Println(err)
    }
}<commit_msg>Add unique passenger feature<commit_after>package main

import (
    "github.com/ant0ine/go-json-rest/rest"
    "github.com/tbruyelle/hipchat-go/hipchat"
    "bytes"
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "os"
    "strconv"
    "strings"
    "sync"
    "time"
)

/*
To-do list
---------
Manually depart a train
Start a train at a designated time
Tag people when the train leaves
Propose a train that starts if enough people join -> X
-----
Keep track of usage statistics
Look into making your own log to log things

53 so far
Look into moving to AWS
*/

var authKey string = ""
var roomName string = ""

var station *Station = &Station{
	Lock: &sync.Mutex{},
	Passengers: make(map[string]*Train),
	Trains: make(map[string]*Train),
}

type WebhookMessage struct {
    Item struct {
        MessageStruct struct {
        	From struct {
        		MentionName string `json:"mention_name"`
        	}
        	Message string `json:"message"`
        } `json:"message"`

    }  `json:"item"`
}

type Train struct {
	Lock *sync.Mutex
	LeavingTimer *time.Timer
	ReminderTimer *time.Timer
	Delete chan struct{}
	MapDestination string
	DisplayDestination string
	PassengerSet map[string]struct{}
}

func NewTrain(conductor string, departure int, dest string) *Train {
	timer := time.NewTimer(time.Minute * time.Duration(departure))
	timer2 := time.NewTimer(time.Minute * time.Duration(departure - 1))
	trainMap := make(map[string]struct{})
	trainMap[conductor] = struct{}{}
	return &Train{
		Lock: &sync.Mutex{},
		LeavingTimer: timer,
		ReminderTimer: timer2,
		Delete: make(chan struct{}),
		MapDestination: strings.ToLower(dest),
		DisplayDestination: dest,
		PassengerSet: trainMap,
	}
}

func (t *Train) NewPassenger(pass string) error {
	t.Lock.Lock()
	defer t.Lock.Unlock()
	_, ok := t.PassengerSet[pass]
	if !ok {
		t.PassengerSet[pass] = struct{}{}
		return nil
	} else {
		log.Printf("Passenger %s is already on the train\n", pass)
		return fmt.Errorf("Passenger %s is already on the train", pass)
	}
}

func (t *Train) PassengerString() string {
	t.Lock.Lock()
	defer t.Lock.Unlock()
	var buffer bytes.Buffer
	i := 0
	for v, _ := range t.PassengerSet {
	   buffer.WriteString(v)
	   if i != len(t.PassengerSet) - 1 {
	    	buffer.WriteString(", ")
	    }
	   if i == len(t.PassengerSet) - 2 {
	    	buffer.WriteString("and ")
	   }
	   i++
	}
	return buffer.String()

}

type Station struct {
	Lock *sync.Mutex
	Passengers map[string]*Train
	Trains map[string]*Train
}

func (s *Station) AddTrain(t *Train) error {
	s.Lock.Lock()
	defer s.Lock.Unlock()
	_, ok := s.Trains[t.MapDestination]
	if !ok {
		s.Trains[t.MapDestination] = t
		return nil
	} else {
		log.Printf("Train to %s already exists", t.DisplayDestination)
		return fmt.Errorf("Train to %s already exists", t.DisplayDestination)
	}
}

func (s *Station) DeleteTrain(dest string) error {
	s.Lock.Lock()
	defer s.Lock.Unlock()
	_, ok := s.Trains[dest]
	if ok {
		for k := range s.Trains {
			train := s.Trains[k]
			for passenger, _ := range train.PassengerSet {
				delete(s.Passengers, passenger)
			}
		}
		delete(s.Trains, dest)
		return nil
	} else {
		log.Printf("The train to %s doesn't exist so it can't be removed", dest)
		return fmt.Errorf("The train to %s doesn't exist so it can't be removed", dest)
	}
}

func PostMessage(msg string) {
	c := hipchat.NewClient(authKey)
	msgReq := &hipchat.NotificationRequest{Message: msg}
	_, err := c.Room.Notification(roomName, msgReq)
	if err != nil {
		panic(err)
	}
}

func MonitorTrain(train *Train) {
	for {
		select {
		case <- train.Delete:
			return
	    case <- train.LeavingTimer.C:
	    	var buffer bytes.Buffer
	    	start := fmt.Sprintf("The train to %v has left the station with ", train.DisplayDestination)
	    	buffer.WriteString(start)
	    	buffer.WriteString(train.PassengerString())
	    	buffer.WriteString(" on it!")
	    	PostMessage(buffer.String())
	    	station.DeleteTrain(train.MapDestination)
	    	return
	    case <- train.ReminderTimer.C:
            PostMessage(fmt.Sprintf("Reminder, the next train to %v leaves in one minute", train.DisplayDestination))
	    default:
		}
	}
}

func GetDestinationAndTime(start int, messageParts []string, getTime bool) (string, int, error) {
	var dest bytes.Buffer
	for i := start; i < len(messageParts); i++ {
		if getTime {
			num, err := strconv.Atoi(messageParts[i])
			if err == nil && i == len(messageParts) - 1 {
				return dest.String(), num, nil
			}
		}
		if i > start {
			dest.WriteString(" ")
		}
		dest.WriteString(messageParts[i])
	}
	if !getTime {
		return dest.String(), 0, nil
	}
	return "", 0, fmt.Errorf("Couldn't parse dest and/or time to departure")
}

func DitchTrain(conductor string) {
	old := station.Passengers[conductor]
	var finalMsg bytes.Buffer
	msg := fmt.Sprintf("%v has decided to ditch their train to %v.", conductor, old.DisplayDestination)
	finalMsg.WriteString(msg)
	old.Lock.Lock()
	delete(old.PassengerSet, conductor)
	old.Lock.Unlock()
	if len(old.PassengerSet) == 0 {
		msg = fmt.Sprintf("It crashed and burned. There were no fatalities.")
		finalMsg.WriteString(msg)
		station.DeleteTrain(old.MapDestination)
		old.Delete <- struct{}{}
	}
	PostMessage(msg)
}

func Handler(w rest.ResponseWriter, r *rest.Request) {
	var webMsg WebhookMessage
	decoder := json.NewDecoder(r.Body)
	err := decoder.Decode(&webMsg)
	if err != nil {
		log.Printf(err.Error())
		PostMessage(err.Error())
		return
	}

	conductor := webMsg.Item.MessageStruct.From.MentionName
	insufficientParams := fmt.Sprintf("%v messed up and forgot to provide the sufficient number of params", conductor)
	messageParts := strings.Split(webMsg.Item.MessageStruct.Message, " ")

	var msg string
	if len(messageParts) < 2 {
		PostMessage(insufficientParams)
		return
	}
	cmd := strings.ToLower(messageParts[1])
	malformed := "Your command is malformed or not found, please view the help message (/train help) for more details"
	notFound := "That train doesn't exist, please try again"
	switch cmd {
	case "help":
		msg = "Usage: /train start <destination> <#minutes> || /train join <destination> || /train passengers <destination> || /train active"
		PostMessage(msg)
	case "passengers":
		dest, _, err := GetDestinationAndTime(2, messageParts, false)
		if err != nil {
			PostMessage(err.Error())
			break
		}
		train, ok := station.Trains[strings.ToLower(dest)]
		if !ok {
			PostMessage(notFound)
		} else {
			if len(train.PassengerSet) == 1 {
				msg = fmt.Sprintf("%v is on the train to %v", train.PassengerString(), train.DisplayDestination)
			} else {
				msg = fmt.Sprintf("%v are on the train to %v", train.PassengerString(), train.DisplayDestination)
			}
			PostMessage(msg)
		}
	case "join":
		dest, _, err := GetDestinationAndTime(2, messageParts, false)
		if err != nil {
			PostMessage(err.Error())
			break
		}
		train, ok := station.Trains[strings.ToLower(dest)]
		if ok {
			_, ok := station.Passengers[conductor]
			if ok {
				DitchTrain(conductor)
			}
			err := train.NewPassenger(conductor)
			if err == nil {
				msg = fmt.Sprintf("%s jumped on the train to %s", conductor, train.DisplayDestination)
				station.Lock.Lock()
				station.Passengers[conductor] = train
				station.Lock.Unlock()
				PostMessage(msg)
			} else {
				msg = err.Error()
				PostMessage(msg)
			}
		} else {
			PostMessage(notFound)
		}
	case "start":
		dest, length, err := GetDestinationAndTime(2, messageParts, true)
		if err != nil {
			PostMessage(malformed)
			break
		}
		if length <= 0 {
			msg = fmt.Sprintf("Please specify a time greater than 0 mins")
			PostMessage(msg)
			break
		}
		_, ok := station.Trains[strings.ToLower(dest)]
		if ok {
			msg = fmt.Sprintf("There's already a train to %v!", dest)
			PostMessage(msg)
			break
		} else {
			_, ok := station.Passengers[conductor]
			if ok {
				DitchTrain(conductor)
			}
			train := NewTrain(conductor, length, dest)
			err = station.AddTrain(train)
			station.Lock.Lock()
			station.Passengers[conductor] = train
			station.Lock.Unlock()
			if err != nil {
				PostMessage(err.Error())
				break
			} else {
				msg = fmt.Sprintf("%s has started a train to %v that leaves in %v minutes!", conductor, train.DisplayDestination, length)
				PostMessage(msg)
				go MonitorTrain(train)
			}
		}
	case "active":
		if len(messageParts) != 2 {
			PostMessage(malformed)
			break
		}
		if len(station.Trains) == 0 {
			msg = fmt.Sprintf("There are currently no active trains")
			PostMessage(msg)
		} else {
			var finalMsg bytes.Buffer
			finalMsg.WriteString("There are trains to: ")
			i := 0
			for _, v := range station.Trains {
				if len(station.Trains) == 1 {
					msg = fmt.Sprintf("There is currently a train to %v (with %v on it)", v.DisplayDestination, v.PassengerString())
					PostMessage(msg)
					return
				} else {
					finalMsg.WriteString(fmt.Sprintf("%v (with %v on it)", v.DisplayDestination, v.PassengerString()))
				}
				if i == len(station.Trains) - 2 {
					finalMsg.WriteString("and ")
	 		    }
				if i != len(station.Trains) - 1 {
					finalMsg.WriteString(", ")
				}
				i++

			}
			PostMessage(finalMsg.String())
		}
	default:
		PostMessage(malformed)
	}
}

func ValidityHandler(w rest.ResponseWriter, r *rest.Request) {
	str := "Everything is OK!"
	w.WriteJson(&str)
}

func main() {
   	api := rest.NewApi()
    api.Use(rest.DefaultDevStack...)

    router, err := rest.MakeRouter(
  		rest.Get("/", ValidityHandler),
    	rest.Post("/train", Handler),
    )

    api.SetApp(router)
    ip := os.Getenv("OPENSHIFT_GO_IP")
    port := os.Getenv("OPENSHIFT_GO_PORT")
    if port == "" {
    	port = "8080"
    }
    bind := fmt.Sprintf("%s:%s",ip,port)
	err = http.ListenAndServe(bind, api.MakeHandler())
	if err != nil {
    	log.Println(err)
    }
}<|endoftext|>


<commit_before>package gocode

import (
	"go/ast"
	"go/parser"
	"go/token"
	"gosubli.me/mg"
	"gosubli.me/something-borrowed/gocode"
	"io/ioutil"
	"path/filepath"
	"strings"
)

type mGocode struct {
	Autoinst      bool
	InstallSuffix string
	Env           map[string]string
	Home          string
	Dir           string
	Builtins      bool
	Fn            string
	Src           string
	Pos           int

	calltip bool
}

type calltipVisitor struct {
	offset int
	fset   *token.FileSet
	x      *ast.CallExpr
}

func (v *calltipVisitor) Visit(node ast.Node) (w ast.Visitor) {
	switch x := node.(type) {
	case *ast.CallExpr:
		a := v.fset.Position(x.Pos())
		b := v.fset.Position(x.End())
		if (a.IsValid() && v.offset >= a.Offset) && (!b.IsValid() || v.offset <= b.Offset) {
			v.x = x
		}
	case *ast.AssignStmt:
		check := func(l ast.Node, r ast.Node) bool {
			a := v.fset.Position(l.Pos())
			b := v.fset.Position(l.End())
			if v.offset >= a.Offset && v.offset <= b.Offset {
				if c, ok := r.(*ast.CallExpr); ok {
					v.x = c
				}
				return true
			}
			return false
		}

		if len(x.Rhs) == 1 {
			check(x, x.Rhs[0])
		} else {
			for i, n := range x.Lhs {
				if i >= len(x.Rhs) || check(n, x.Rhs[i]) {
					break
				}
			}
		}
	}
	return v
}

func (m *mGocode) Call() (interface{}, string) {
	if m.Src == "" {
		// this is here for testing, the client should always send the src
		s, _ := ioutil.ReadFile(m.Fn)
		m.Src = string(s)
	}

	if m.Src == "" {
		return nil, "No source"
	}

	pos := mg.BytePos(m.Src, m.Pos)
	if m.Pos < 0 {
		return nil, "Invalid offset"
	}

	src := []byte(m.Src)
	fn := m.Fn
	if !filepath.IsAbs(fn) {
		fn = filepath.Join(mg.OrString(m.Dir, m.Home), mg.OrString(fn, "_.go"))
	}

	res := struct {
		Candidates []gocode.MargoCandidate
	}{}

	if m.calltip {
		res.Candidates = m.calltips(src, fn, pos)
	} else {
		res.Candidates = m.completions(src, fn, pos)
	}

	if m.Autoinst && len(res.Candidates) == 0 {
		mg.AutoInstall(mg.AutoInstOptions{
			Src:           m.Src,
			Env:           m.Env,
			InstallSuffix: m.InstallSuffix,
		})
	}

	return res, ""
}

func (g *mGocode) completions(src []byte, fn string, pos int) []gocode.MargoCandidate {
	c := gocode.MargoConfig{}
	c.InstallSuffix = g.InstallSuffix
	c.Builtins = g.Builtins
	c.GOROOT, c.GOPATHS = mg.RootPaths(g.Env)
	return gocode.Margo.Complete(c, src, fn, pos)
}

func (m *mGocode) calltips(src []byte, fn string, offset int) []gocode.MargoCandidate {
	id, fset, af := identAtOffset(src, offset)
	if id != nil {
		cp := fset.Position(id.End())
		if cp.IsValid() {
			line := offsetLine(fset, af, offset)
			cr := cp.Offset
			cl := m.completions(src, fn, cr)

			if (cp.Line == line || line == 0) && len(cl) > 0 {
				for i, c := range cl {
					if strings.EqualFold(id.Name, c.Name) {
						return cl[i : i+1]
					}
				}
			}
		}
	}

	return []gocode.MargoCandidate{}
}

func identAtOffset(src []byte, offset int) (id *ast.Ident, fset *token.FileSet, af *ast.File) {
	fset = token.NewFileSet()
	af, _ = parser.ParseFile(fset, "<stdin>", src, 0)

	if af == nil {
		return
	}

	vis := &calltipVisitor{
		offset: offset,
		fset:   fset,
	}
	ast.Walk(vis, af)

	if vis.x != nil && vis.x.Fun != nil {
		switch v := vis.x.Fun.(type) {
		case *ast.Ident:
			id = v
		case *ast.SelectorExpr:
			id = v.Sel
		}
	}
	return
}

func offsetLine(fset *token.FileSet, af *ast.File, offset int) (line int) {
	defer func() {
		if err := recover(); err != nil {
			line = 0
		}
	}()
	return fset.File(af.Pos()).Position(token.Pos(offset)).Line
}

func init() {
	mg.Register("gocode_complete", func(b *mg.Broker) mg.Caller {
		return &mGocode{}
	})

	mg.Register("gocode_calltip", func(b *mg.Broker) mg.Caller {
		return &mGocode{calltip: true}
	})
}
<commit_msg>* prep the results for pkg suggestions<commit_after>package gocode

import (
	"go/ast"
	"go/parser"
	"go/token"
	"gosubli.me/mg"
	"gosubli.me/something-borrowed/gocode"
	"io/ioutil"
	"path/filepath"
	"strings"
)

type mGocode struct {
	Autoinst      bool
	InstallSuffix string
	Env           map[string]string
	Home          string
	Dir           string
	Builtins      bool
	Fn            string
	Src           string
	Pos           int

	calltip bool
}

type Res struct {
	Candidates  []gocode.MargoCandidate
	Suggestions []string
}

type calltipVisitor struct {
	offset int
	fset   *token.FileSet
	x      *ast.CallExpr
}

func (v *calltipVisitor) Visit(node ast.Node) (w ast.Visitor) {
	switch x := node.(type) {
	case *ast.CallExpr:
		a := v.fset.Position(x.Pos())
		b := v.fset.Position(x.End())
		if (a.IsValid() && v.offset >= a.Offset) && (!b.IsValid() || v.offset <= b.Offset) {
			v.x = x
		}
	case *ast.AssignStmt:
		check := func(l ast.Node, r ast.Node) bool {
			a := v.fset.Position(l.Pos())
			b := v.fset.Position(l.End())
			if v.offset >= a.Offset && v.offset <= b.Offset {
				if c, ok := r.(*ast.CallExpr); ok {
					v.x = c
				}
				return true
			}
			return false
		}

		if len(x.Rhs) == 1 {
			check(x, x.Rhs[0])
		} else {
			for i, n := range x.Lhs {
				if i >= len(x.Rhs) || check(n, x.Rhs[i]) {
					break
				}
			}
		}
	}
	return v
}

func (m *mGocode) Call() (interface{}, string) {
	if m.Src == "" {
		// this is here for testing, the client should always send the src
		s, _ := ioutil.ReadFile(m.Fn)
		m.Src = string(s)
	}

	if m.Src == "" {
		return nil, "No source"
	}

	pos := mg.BytePos(m.Src, m.Pos)
	if m.Pos < 0 {
		return nil, "Invalid offset"
	}

	src := []byte(m.Src)
	fn := m.Fn
	if !filepath.IsAbs(fn) {
		fn = filepath.Join(mg.OrString(m.Dir, m.Home), mg.OrString(fn, "_.go"))
	}

	res := Res{}
	if m.calltip {
		res.Candidates = m.calltips(src, fn, pos)
	} else {
		res.Candidates = m.completions(src, fn, pos)
	}

	if m.Autoinst && len(res.Candidates) == 0 {
		mg.AutoInstall(mg.AutoInstOptions{
			Src:           m.Src,
			Env:           m.Env,
			InstallSuffix: m.InstallSuffix,
		})
	}

	return res, ""
}

func (g *mGocode) completions(src []byte, fn string, pos int) []gocode.MargoCandidate {
	c := gocode.MargoConfig{}
	c.InstallSuffix = g.InstallSuffix
	c.Builtins = g.Builtins
	c.GOROOT, c.GOPATHS = mg.RootPaths(g.Env)
	return gocode.Margo.Complete(c, src, fn, pos)
}

func (m *mGocode) calltips(src []byte, fn string, offset int) []gocode.MargoCandidate {
	id, fset, af := identAtOffset(src, offset)
	if id != nil {
		cp := fset.Position(id.End())
		if cp.IsValid() {
			line := offsetLine(fset, af, offset)
			cr := cp.Offset
			cl := m.completions(src, fn, cr)

			if (cp.Line == line || line == 0) && len(cl) > 0 {
				for i, c := range cl {
					if strings.EqualFold(id.Name, c.Name) {
						return cl[i : i+1]
					}
				}
			}
		}
	}

	return []gocode.MargoCandidate{}
}

func identAtOffset(src []byte, offset int) (id *ast.Ident, fset *token.FileSet, af *ast.File) {
	fset = token.NewFileSet()
	af, _ = parser.ParseFile(fset, "<stdin>", src, 0)

	if af == nil {
		return
	}

	vis := &calltipVisitor{
		offset: offset,
		fset:   fset,
	}
	ast.Walk(vis, af)

	if vis.x != nil && vis.x.Fun != nil {
		switch v := vis.x.Fun.(type) {
		case *ast.Ident:
			id = v
		case *ast.SelectorExpr:
			id = v.Sel
		}
	}
	return
}

func offsetLine(fset *token.FileSet, af *ast.File, offset int) (line int) {
	defer func() {
		if err := recover(); err != nil {
			line = 0
		}
	}()
	return fset.File(af.Pos()).Position(token.Pos(offset)).Line
}

func init() {
	mg.Register("gocode_complete", func(b *mg.Broker) mg.Caller {
		return &mGocode{}
	})

	mg.Register("gocode_calltip", func(b *mg.Broker) mg.Caller {
		return &mGocode{calltip: true}
	})
}
<|endoftext|>