<commit_before>// Copyright 2015 Dorival de Moraes Pedroso. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package goga

import (
	"encoding/json"

	"github.com/cpmech/gosl/chk"
	"github.com/cpmech/gosl/io"
)

// ConfParams is an auxiliary structure to hold configuration parameters for setting the GA up
type ConfParams struct {

	// initialisation
	Seed   int     // seed to initialise random numbers generator. Seed ≤ 0 means use current time
	Pll    bool    // allow running islands in parallel (go-routines)
	Nisl   int     // number of islands
	Ninds  int     // number of individuals: population size
	Nbases int     // number of bases in chromosome
	Grid   bool    // generate individuals based on grid
	Noise  float64 // apply noise when generate based on grid (if Noise > 0)
	IntOrd bool    // integer chromossome is ordered list

	// time control
	Tf    int // number of generations
	Dtout int // increment of time for output
	Dtmig int // increment of time for migration

	// regeneration
	RegTol    float64 // tolerance for ρ to activate regeneration
	RegPct    float64 // percentage of individuals to be regenerated; e.g. 0.3
	UseStdDev bool    // use standard deviation (σ) instead of average deviation in Stat

	// selection and reproduction
	Pc        float64 // probability of crossover
	Pm        float64 // probability of mutation
	Elite     bool    // use elitism
	Rws       bool    // use Roulette-Wheel selection method
	Rnk       bool    // ranking
	RnkSp     float64 // selective pressure for ranking
	GAtype    string  // type of GA; e.g. "std", "crowd", "sharing"
	CrowdSize int     // crowd size
	ParetoPhi float64 // φ coefficient for probabilistic Pareto comparison
	ShSize    float64 // sharing sample size. percentage of Ninds; e.g. 0.1
	ShAlp     float64 // αshare
	ShSig     float64 // σshare

	// output
	Verbose   bool       // show messages during optimisation
	DoReport  bool       // generate report
	Json      bool       // output results as .json files; not tables
	DirOut    string     // directory to save output files. "" means "/tmp/goga"
	FnKey     string     // filename key for output files. "" means no output files
	DoPlot    bool       // plot results
	PltTi     int        // initial time for plot
	PltTf     int        // final time for plot
	ShowOor   bool       // show oor values when printing results (if any)
	ShowBases bool       // show also bases when printing results (if any)
	ShowNinds int        // number of individuals to show. use -1 to show all
	PostProc  PostProc_t // function to post-process results

	// auxiliary
	Problem  int     // problem ID
	Strategy int     // strategy for implementing constraints
	Ntrials  int     // number of trials
	Eps1     float64 // tolerance # 1; e.g. for strategy # 2 in reliability analyses

	// objective function
	OvaOor Objectives_t // compute objective value (ova) and out-of-range value (oor)

	// crossover
	CxNcuts   map[string]int         // crossover number of cuts for each 'int', 'flt', 'str', 'key', 'byt', 'fun' tag
	CxCuts    map[string][]int       // crossover specific cuts for each 'int', 'flt', 'str', 'key', 'byt', 'fun' tag
	CxProbs   map[string]float64     // crossover probabilities for each 'int', 'flt', 'str', 'key', 'byt', 'fun' tag
	CxFuncs   map[string]interface{} // crossover functions for each 'int', 'flt', 'str', 'key', 'byt', 'fun' tag
	CxExtra   map[string]interface{} // crossover extra parameters for each 'int', 'flt', 'str', 'key', 'byt', 'fun' tag
	CxIntFunc CxIntFunc_t            // crossover function
	CxFltFunc CxFltFunc_t            // crossover function
	CxStrFunc CxStrFunc_t            // crossover function
	CxKeyFunc CxKeyFunc_t            // crossover function
	CxBytFunc CxBytFunc_t            // crossover function
	CxFunFunc CxFunFunc_t            // crossover function

	// mutation
	MtNchanges map[string]int         // mutation number of changes for each 'int', 'flt', 'str', 'key', 'byt', 'fun' tag
	MtProbs    map[string]float64     // mutation probabilities for each 'int', 'flt', 'str', 'key', 'byt', 'fun' tag
	MtExtra    map[string]interface{} // mutation extra parameters for each 'int', 'flt', 'str', 'key', 'byt', 'fun' tag
	MtIntFunc  MtIntFunc_t            // mutation function
	MtFltFunc  MtFltFunc_t            // mutation function
	MtStrFunc  MtStrFunc_t            // mutation function
	MtKeyFunc  MtKeyFunc_t            // mutation function
	MtBytFunc  MtBytFunc_t            // mutation function
	MtFunFunc  MtFunFunc_t            // mutation function

	// generation of individuals
	OrdNints   int         // ordered integer populations: number of integers
	RangeInt   [][]int     // [ngene][2] min and max integers
	RangeFlt   [][]float64 // [ngene][2] min and max float point numbers
	PoolStr    [][]string  // [ngene][nsamples] pool of words to be used in Gene.String
	PoolKey    [][]byte    // [ngene][nsamples] pool of bytes to be used in Gene.Byte
	PoolByt    [][]string  // [ngene][nsamples] pool of byte-words to be used in Gene.Bytes
	PoolFun    [][]Func_t  // [ngene][nsamples] pool of functions
	PopGenArgs interface{} // extra arguments for generation of populations

	// generation of populations
	PopIntGen PopIntGen_t // generate population of integers
	PopOrdGen PopOrdGen_t // generate population of ordered integers
	PopFltGen PopFltGen_t // generate population of float point numbers
	PopStrGen PopStrGen_t // generate population of strings
	PopKeyGen PopKeyGen_t // generate population of keys (bytes)
	PopBytGen PopBytGen_t // generate population of bytes
	PopFunGen PopFunGen_t // generate population of functions
}

// SetDefault sets default parameters
func (o *ConfParams) SetDefault() {

	// initialisation
	o.Seed = 0
	o.Pll = true
	o.Nisl = 4
	o.Ninds = 20
	o.Nbases = 10
	o.Grid = true
	o.Noise = 0.3
	o.IntOrd = false

	// time control
	o.Tf = 100
	o.Dtout = 10
	o.Dtmig = 40

	// regeneration
	o.RegTol = 0
	o.RegPct = 0.3
	o.UseStdDev = false

	// selection and reproduction
	o.Pc = 0.8
	o.Pm = 0.01
	o.Elite = false
	o.Rws = false
	o.Rnk = true
	o.RnkSp = 1.2
	o.GAtype = "crowd"
	o.CrowdSize = 2
	o.ParetoPhi = 0
	o.ShSize = 0.5
	o.ShAlp = 2.0
	o.ShSig = 1.0

	// output
	o.Verbose = true
	o.DoReport = false
	o.Json = false
	o.DirOut = "/tmp/goga"
	o.FnKey = ""
	o.DoPlot = false
	o.PltTi = 0
	o.PltTf = -1
	o.ShowOor = true
	o.ShowBases = false
	o.ShowNinds = -1

	// auxiliary
	o.Problem = 1
	o.Strategy = 1
	o.Ntrials = 100
	o.Eps1 = 0.1

	// number of cuts in chromossome
	o.CxNcuts = map[string]int{"int": 2, "flt": 2, "str": 2, "key": 2, "byt": 2, "fun": 2}
}

// CalcDerived calculates derived quantities
func (o *ConfParams) CalcDerived() {

	// set probabilities
	pc, pm := o.Pc, o.Pm
	o.CxProbs = map[string]float64{"int": pc, "flt": pc, "str": pc, "key": pc, "byt": pc, "fun": pc}
	o.MtProbs = map[string]float64{"int": pm, "flt": pm, "str": pm, "key": pm, "byt": pm, "fun": pm}

	// set specific crossover and mutation functions
	if o.IntOrd {
		o.CxIntFunc = IntOrdCrossover
		o.MtIntFunc = IntOrdMutation
	}
}

// NewConfParams returns a new ConfParams structure, with default values set
func NewConfParams() *ConfParams {
	var o ConfParams
	o.SetDefault()
	o.CalcDerived()
	return &o
}

// ReadConfParams reads configuration parameters from JSON file
func ReadConfParams(filenamepath string) *ConfParams {

	// new params
	var o ConfParams
	o.SetDefault()

	// read file
	b, err := io.ReadFile(filenamepath)
	if err != nil {
		chk.Panic("cannot read parameters file %q", filenamepath)
	}

	// decode
	err = json.Unmarshal(b, &o)
	if err != nil {
		chk.Panic("cannot unmarshal parameters file %q", filenamepath)
	}

	// results
	o.CalcDerived()
	return &o
}
<commit_msg>auxiliary methods added to params<commit_after>// Copyright 2015 Dorival de Moraes Pedroso. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package goga

import (
	"encoding/json"

	"github.com/cpmech/gosl/chk"
	"github.com/cpmech/gosl/io"
)

// ConfParams is an auxiliary structure to hold configuration parameters for setting the GA up
type ConfParams struct {

	// initialisation
	Seed   int     // seed to initialise random numbers generator. Seed ≤ 0 means use current time
	Pll    bool    // allow running islands in parallel (go-routines)
	Nisl   int     // number of islands
	Ninds  int     // number of individuals: population size
	Nbases int     // number of bases in chromosome
	Grid   bool    // generate individuals based on grid
	Noise  float64 // apply noise when generate based on grid (if Noise > 0)
	IntOrd bool    // integer chromossome is ordered list

	// time control
	Tf    int // number of generations
	Dtout int // increment of time for output
	Dtmig int // increment of time for migration

	// regeneration
	RegTol    float64 // tolerance for ρ to activate regeneration
	RegPct    float64 // percentage of individuals to be regenerated; e.g. 0.3
	UseStdDev bool    // use standard deviation (σ) instead of average deviation in Stat

	// selection and reproduction
	Pc        float64 // probability of crossover
	Pm        float64 // probability of mutation
	Elite     bool    // use elitism
	Rws       bool    // use Roulette-Wheel selection method
	Rnk       bool    // ranking
	RnkSp     float64 // selective pressure for ranking
	GAtype    string  // type of GA; e.g. "std", "crowd", "sharing"
	CrowdSize int     // crowd size
	ParetoPhi float64 // φ coefficient for probabilistic Pareto comparison
	ShSize    float64 // sharing sample size. percentage of Ninds; e.g. 0.1
	ShAlp     float64 // αshare
	ShSig     float64 // σshare

	// output
	Verbose   bool       // show messages during optimisation
	DoReport  bool       // generate report
	Json      bool       // output results as .json files; not tables
	DirOut    string     // directory to save output files. "" means "/tmp/goga"
	FnKey     string     // filename key for output files. "" means no output files
	DoPlot    bool       // plot results
	PltTi     int        // initial time for plot
	PltTf     int        // final time for plot
	ShowOor   bool       // show oor values when printing results (if any)
	ShowBases bool       // show also bases when printing results (if any)
	ShowNinds int        // number of individuals to show. use -1 to show all
	PostProc  PostProc_t // function to post-process results

	// auxiliary
	Problem  int     // problem ID
	Strategy int     // strategy for implementing constraints
	Ntrials  int     // number of trials
	Eps1     float64 // tolerance # 1; e.g. for strategy # 2 in reliability analyses

	// objective function
	OvaOor Objectives_t // compute objective value (ova) and out-of-range value (oor)

	// crossover
	CxNcuts   map[string]int         // crossover number of cuts for each 'int', 'flt', 'str', 'key', 'byt', 'fun' tag
	CxCuts    map[string][]int       // crossover specific cuts for each 'int', 'flt', 'str', 'key', 'byt', 'fun' tag
	CxProbs   map[string]float64     // crossover probabilities for each 'int', 'flt', 'str', 'key', 'byt', 'fun' tag
	CxFuncs   map[string]interface{} // crossover functions for each 'int', 'flt', 'str', 'key', 'byt', 'fun' tag
	CxExtra   map[string]interface{} // crossover extra parameters for each 'int', 'flt', 'str', 'key', 'byt', 'fun' tag
	CxIntFunc CxIntFunc_t            // crossover function
	CxFltFunc CxFltFunc_t            // crossover function
	CxStrFunc CxStrFunc_t            // crossover function
	CxKeyFunc CxKeyFunc_t            // crossover function
	CxBytFunc CxBytFunc_t            // crossover function
	CxFunFunc CxFunFunc_t            // crossover function

	// mutation
	MtNchanges map[string]int         // mutation number of changes for each 'int', 'flt', 'str', 'key', 'byt', 'fun' tag
	MtProbs    map[string]float64     // mutation probabilities for each 'int', 'flt', 'str', 'key', 'byt', 'fun' tag
	MtExtra    map[string]interface{} // mutation extra parameters for each 'int', 'flt', 'str', 'key', 'byt', 'fun' tag
	MtIntFunc  MtIntFunc_t            // mutation function
	MtFltFunc  MtFltFunc_t            // mutation function
	MtStrFunc  MtStrFunc_t            // mutation function
	MtKeyFunc  MtKeyFunc_t            // mutation function
	MtBytFunc  MtBytFunc_t            // mutation function
	MtFunFunc  MtFunFunc_t            // mutation function

	// generation of individuals
	OrdNints   int         // ordered integer populations: number of integers
	RangeInt   [][]int     // [ngene][2] min and max integers
	RangeFlt   [][]float64 // [ngene][2] min and max float point numbers
	PoolStr    [][]string  // [ngene][nsamples] pool of words to be used in Gene.String
	PoolKey    [][]byte    // [ngene][nsamples] pool of bytes to be used in Gene.Byte
	PoolByt    [][]string  // [ngene][nsamples] pool of byte-words to be used in Gene.Bytes
	PoolFun    [][]Func_t  // [ngene][nsamples] pool of functions
	PopGenArgs interface{} // extra arguments for generation of populations

	// generation of populations
	PopIntGen PopIntGen_t // generate population of integers
	PopOrdGen PopOrdGen_t // generate population of ordered integers
	PopFltGen PopFltGen_t // generate population of float point numbers
	PopStrGen PopStrGen_t // generate population of strings
	PopKeyGen PopKeyGen_t // generate population of keys (bytes)
	PopBytGen PopBytGen_t // generate population of bytes
	PopFunGen PopFunGen_t // generate population of functions
}

// SetDefault sets default parameters
func (o *ConfParams) SetDefault() {

	// initialisation
	o.Seed = 0
	o.Pll = true
	o.Nisl = 4
	o.Ninds = 20
	o.Nbases = 10
	o.Grid = true
	o.Noise = 0.3
	o.IntOrd = false

	// time control
	o.Tf = 100
	o.Dtout = 10
	o.Dtmig = 40

	// regeneration
	o.RegTol = 0
	o.RegPct = 0.3
	o.UseStdDev = false

	// selection and reproduction
	o.Pc = 0.8
	o.Pm = 0.01
	o.Elite = false
	o.Rws = false
	o.Rnk = true
	o.RnkSp = 1.2
	o.GAtype = "crowd"
	o.CrowdSize = 2
	o.ParetoPhi = 0
	o.ShSize = 0.5
	o.ShAlp = 2.0
	o.ShSig = 1.0

	// output
	o.Verbose = true
	o.DoReport = false
	o.Json = false
	o.DirOut = "/tmp/goga"
	o.FnKey = ""
	o.DoPlot = false
	o.PltTi = 0
	o.PltTf = -1
	o.ShowOor = true
	o.ShowBases = false
	o.ShowNinds = -1

	// auxiliary
	o.Problem = 1
	o.Strategy = 1
	o.Ntrials = 100
	o.Eps1 = 0.1

	// number of cuts in chromossome
	o.CxNcuts = map[string]int{"int": 2, "flt": 2, "str": 2, "key": 2, "byt": 2, "fun": 2}
}

// CalcDerived calculates derived quantities
func (o *ConfParams) CalcDerived() {

	// set probabilities
	pc, pm := o.Pc, o.Pm
	o.CxProbs = map[string]float64{"int": pc, "flt": pc, "str": pc, "key": pc, "byt": pc, "fun": pc}
	o.MtProbs = map[string]float64{"int": pm, "flt": pm, "str": pm, "key": pm, "byt": pm, "fun": pm}

	// set specific crossover and mutation functions
	if o.IntOrd {
		o.CxIntFunc = IntOrdCrossover
		o.MtIntFunc = IntOrdMutation
	}
}

// SetCxBlx sets crossover method Blx
func (o *ConfParams) SetCxBlx(α float64) {
	o.Nbases = 1 // this crossover method works with 1 basis only
	o.CxExtra = map[string]interface{}{"flt": α}
	o.CxFltFunc = FltCrossoverBlx
}

// SetMtMwicz sets mutation method Michalewicz
func (o *ConfParams) SetMtMwicz(b float64) {
	o.Nbases = 1 // Michalewicz mutation does not work with nbases!=1
	o.MtExtra = map[string]interface{}{"flt": &Michalewicz{float64(o.Tf), b, o.RangeFlt}}
	o.MtFltFunc = FltMutationNonUni
}

// NewConfParams returns a new ConfParams structure, with default values set
func NewConfParams() *ConfParams {
	var o ConfParams
	o.SetDefault()
	o.CalcDerived()
	return &o
}

// ReadConfParams reads configuration parameters from JSON file
func ReadConfParams(filenamepath string) *ConfParams {

	// new params
	var o ConfParams
	o.SetDefault()

	// read file
	b, err := io.ReadFile(filenamepath)
	if err != nil {
		chk.Panic("cannot read parameters file %q", filenamepath)
	}

	// decode
	err = json.Unmarshal(b, &o)
	if err != nil {
		chk.Panic("cannot unmarshal parameters file %q", filenamepath)
	}

	// results
	o.CalcDerived()
	return &o
}
<|endoftext|>


<commit_before>package requests

import (
	"errors"
	"io/ioutil"
	"mime"
	"mime/multipart"
	"net/http"
	"net/url"
)

var multipartMem int64 = 2 << 20 * 10

// MultipartMem returns the current memory limit for multipart form
// data.
func MultipartMem() int64 {
	return multipartMem
}

// SetMultipartMem sets the memory limit for multipart form data.
func SetMultipartMem(mem int64) {
	multipartMem = mem
}

// Body returns the result of ParseBody for this request.  ParseBody
// will only be called the first time Body is called; subsequent calls
// will return the same value as the first call.
func (request *Request) Body() (interface{}, error) {
	if request.body == nil {
		body, err := ParseBody(request.httpRequest)
		if err != nil {
			return nil, err
		}
		request.body = body
	}
	return request.body, nil
}

// Params returns the result of ParseParams for this request.
// ParseParams will only be called the first time Params is called;
// subsequent calls will return the same value as the first call.
func (request *Request) Params() (map[string]interface{}, error) {
	if request.params == nil {
		body, err := request.Body()
		if err != nil {
			return nil, err
		}
		params, err := convertToParams(body)
		if err != nil {
			return nil, err
		}
		request.params = params
	}
	return request.params, nil
}

// ParseBody locates a codec matching the request's Content-Type
// header, then Unmarshals the request's body to an interface{} type.
// The resulting type is unpredictable and will be heavily based on
// the actual data in the request.
//
// There are two exceptions to the above, where no codec lookup is
// used:
//
// * application/x-www-form-urlencoded (or an empty Content-Type)
//
// ** The return value for this type will be the same as
//    "net/http".Request.PostForm after calling ParseForm.
//
// * multipart/form-data
//
// ** The return value for this type will be the same as
//    "net/http".Request.MultipartForm after calling
//    ParseMultipartForm.
func ParseBody(request *http.Request) (interface{}, error) {
	// Handle form data types
	contentType, _, err := mime.ParseMediaType(request.Header.Get("Content-Type"))
	if err != nil {
		return nil, err
	}
	switch contentType {
	case "application/x-www-form-urlencoded", "":
		if err := request.ParseForm(); err != nil {
			return nil, err
		}
		return request.PostForm, nil
	case "multipart/form-data":
		if err := request.ParseMultipartForm(MultipartMem()); err != nil {
			return nil, err
		}
		return request.MultipartForm, nil
	}

	// Now the general case
	codec, err := Codecs().GetCodec(contentType)
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(request.Body)
	if err != nil {
		return nil, err
	}
	var response interface{}
	if err = codec.Unmarshal(body, &response); err != nil {
		return nil, err
	}
	return response, nil
}

// ParseParams returns a map[string]interface{} of values found in a
// request body.  In most cases, this is the equivalent of
// ParseBody(request).(map[string]interface{}).  However, there are
// two exceptions:
//
// * application/x-www-form-urlencoded (or an empty Content-Type)
//
// ** Each value in request.PostForm that has a len() of 1 will be
//    stored instead as the zeroeth index of the value.
//
// * multipart/form-data
//
// ** In addition to the above, files will be stored at the same
//    level as values.  Each value in the resulting map could contain
//    both string and *"mime/multipart".FileHeader values.
//
// The resulting code to parse a form may look like the following:
//
//     params, err := ParseParams(request)
//     // handle err
//     for key, value := range params {
//         switch v := value.(type) {
//         case string:
//             // Do stuff with single string value
//         case *multipart.FileHeader:
//             // Do stuff with single file
//         case []interface{}:
//             // There were multiple string and/or file values at
//             // this key, so deal with that.
//         }
//     }
//
func ParseParams(request *http.Request) (map[string]interface{}, error) {
	if request.Body == nil {
		return nil, nil
	}
	body, err := ParseBody(request)
	if err != nil {
		return nil, err
	}
	return convertToParams(body)
}

func convertToParams(body interface{}) (map[string]interface{}, error) {
	switch body.(type) {
	case url.Values, *multipart.Form:
		return convertForm(body), nil
	}
	m, ok := body.(map[string]interface{})
	if !ok {
		return nil, errors.New("The unmarshalled body is not of type map[string]interface{} " +
			"and cannot be converted to params")
	}
	return m, nil
}

func convertForm(body interface{}) map[string]interface{} {
	var (
		params = make(map[string]interface{})
		values map[string][]string
		files  map[string][]*multipart.FileHeader
	)
	switch form := body.(type) {
	case url.Values:
		values = map[string][]string(form)
	case *multipart.Form:
		values = form.Value
		files = form.File
	}
	for key, valueList := range values {
		if len(valueList) == 1 {
			params[key] = valueList[0]
		} else {
			values := make([]interface{}, len(valueList))
			for idx, value := range valueList {
				values[idx] = value
			}
			params[key] = values
		}
	}
	for key, fileList := range files {
		param, ok := params[key]
		if ok || len(fileList) > 1 {
			values := make([]interface{}, 0, len(params)+len(fileList))
			switch params := param.(type) {
			case []interface{}:
				values = append(values, params...)
			default:
				values = append(values, param)
			}
			for _, file := range fileList {
				values = append(values, file)
			}
			param = values
		} else {
			param = fileList[0]
		}
		params[key] = param
	}
	return params
}
<commit_msg>Documentation update, part 4<commit_after>package requests

import (
	"errors"
	"io/ioutil"
	"mime"
	"mime/multipart"
	"net/http"
	"net/url"
)

var multipartMem int64 = 2 << 20 * 10

// MultipartMem returns the current memory limit for multipart form
// data.
func MultipartMem() int64 {
	return multipartMem
}

// SetMultipartMem sets the memory limit for multipart form data.
func SetMultipartMem(mem int64) {
	multipartMem = mem
}

// Body returns the result of ParseBody for this request.  ParseBody
// will only be called the first time Body is called; subsequent calls
// will return the same value as the first call.
func (request *Request) Body() (interface{}, error) {
	if request.body == nil {
		body, err := ParseBody(request.httpRequest)
		if err != nil {
			return nil, err
		}
		request.body = body
	}
	return request.body, nil
}

// Params returns the result of ParseParams for this request.
// ParseParams will only be called the first time Params is called;
// subsequent calls will return the same value as the first call.
func (request *Request) Params() (map[string]interface{}, error) {
	if request.params == nil {
		body, err := request.Body()
		if err != nil {
			return nil, err
		}
		params, err := convertToParams(body)
		if err != nil {
			return nil, err
		}
		request.params = params
	}
	return request.params, nil
}

// ParseBody locates a codec matching the request's Content-Type
// header, then Unmarshals the request's body to an interface{} type.
// The resulting type is unpredictable and will be heavily based on
// the actual data in the request.
//
// There are two exceptions to the above, where no codec lookup is
// used:
//
// * application/x-www-form-urlencoded (or an empty Content-Type)
//
// ** The return value for this type will be the same as
// "net/http".Request.PostForm after calling ParseForm.
//
// * multipart/form-data
//
// ** The return value for this type will be the same as
// "net/http".Request.MultipartForm after calling
// ParseMultipartForm.
func ParseBody(request *http.Request) (interface{}, error) {
	// Handle form data types
	contentType, _, err := mime.ParseMediaType(request.Header.Get("Content-Type"))
	if err != nil {
		return nil, err
	}
	switch contentType {
	case "application/x-www-form-urlencoded", "":
		if err := request.ParseForm(); err != nil {
			return nil, err
		}
		return request.PostForm, nil
	case "multipart/form-data":
		if err := request.ParseMultipartForm(MultipartMem()); err != nil {
			return nil, err
		}
		return request.MultipartForm, nil
	}

	// Now the general case
	codec, err := Codecs().GetCodec(contentType)
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(request.Body)
	if err != nil {
		return nil, err
	}
	var response interface{}
	if err = codec.Unmarshal(body, &response); err != nil {
		return nil, err
	}
	return response, nil
}

// ParseParams returns a map[string]interface{} of values found in a
// request body.  In most cases, this is the equivalent of
// ParseBody(request).(map[string]interface{}).  However, there are
// two exceptions:
//
// * application/x-www-form-urlencoded (or an empty Content-Type)
//
// ** Each value in request.PostForm that has a len() of 1 will be
// stored instead as the zeroeth index of the value.
//
// * multipart/form-data
//
// ** In addition to the above, files will be stored at the same
// level as values.  Each value in the resulting map could contain
// both string and *"mime/multipart".FileHeader values.
//
// The resulting code to parse a form may look like the following:
//
//     params, err := ParseParams(request)
//     // handle err
//     for key, value := range params {
//         switch v := value.(type) {
//         case string:
//             // Do stuff with single string value
//         case *multipart.FileHeader:
//             // Do stuff with single file
//         case []interface{}:
//             // There were multiple string and/or file values at
//             // this key, so deal with that.
//         }
//     }
//
func ParseParams(request *http.Request) (map[string]interface{}, error) {
	if request.Body == nil {
		return nil, nil
	}
	body, err := ParseBody(request)
	if err != nil {
		return nil, err
	}
	return convertToParams(body)
}

func convertToParams(body interface{}) (map[string]interface{}, error) {
	switch body.(type) {
	case url.Values, *multipart.Form:
		return convertForm(body), nil
	}
	m, ok := body.(map[string]interface{})
	if !ok {
		return nil, errors.New("The unmarshalled body is not of type map[string]interface{} " +
			"and cannot be converted to params")
	}
	return m, nil
}

func convertForm(body interface{}) map[string]interface{} {
	var (
		params = make(map[string]interface{})
		values map[string][]string
		files  map[string][]*multipart.FileHeader
	)
	switch form := body.(type) {
	case url.Values:
		values = map[string][]string(form)
	case *multipart.Form:
		values = form.Value
		files = form.File
	}
	for key, valueList := range values {
		if len(valueList) == 1 {
			params[key] = valueList[0]
		} else {
			values := make([]interface{}, len(valueList))
			for idx, value := range valueList {
				values[idx] = value
			}
			params[key] = values
		}
	}
	for key, fileList := range files {
		param, ok := params[key]
		if ok || len(fileList) > 1 {
			values := make([]interface{}, 0, len(params)+len(fileList))
			switch params := param.(type) {
			case []interface{}:
				values = append(values, params...)
			default:
				values = append(values, param)
			}
			for _, file := range fileList {
				values = append(values, file)
			}
			param = values
		} else {
			param = fileList[0]
		}
		params[key] = param
	}
	return params
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"log"
	"regexp"
	"strconv"
	"time"
)

var (
	protocolRegex *regexp.Regexp
	routeRegex    *regexp.Regexp
	uptimeRegex   *regexp.Regexp
)

func init() {
	protocolRegex, _ = regexp.Compile("^([^\\s]+)\\s+(BGP|OSPF)\\s+([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)\\s+(.*?)\\s*$")
	routeRegex, _ = regexp.Compile("^\\s+Routes:\\s+(\\d+) imported, (?:\\d+ filtered, )?(\\d+) exported")
	uptimeRegex, _ = regexp.Compile("^(?:((\\d+):(\\d{2}):(\\d{2}))|\\d+)$")
}

func parseOutput(data []byte, ipVersion int) []*protocol {
	protocols := make([]*protocol, 0)

	reader := bytes.NewReader(data)
	scanner := bufio.NewScanner(reader)
	var current *protocol = nil

	for scanner.Scan() {
		line := scanner.Text()
		if p, ok := parseLineForProtocol(line, ipVersion); ok {
			current = p
			protocols = append(protocols, current)
		}

		if current != nil {
			parseLineForRoutes(line, current)
		}

		if line == "" {
			current = nil
		}
	}

	return protocols
}

func parseLineForProtocol(line string, ipVersion int) (*protocol, bool) {
	match := protocolRegex.FindStringSubmatch(line)

	if match == nil {
		return nil, false
	}

	proto := parseProto(match[2])
	up := parseState(match[6], proto)
	ut := parseUptime(match[5])
	p := &protocol{proto: proto, name: match[1], ipVersion: ipVersion, up: up, uptime: ut, attributes: make(map[string]interface{})}

	return p, true
}

func parseProto(val string) int {
	switch val {
	case "BGP":
		return BGP
	case "OSPF":
		return OSPF
	}

	return PROTO_UNKNOWN
}

func parseLineForRoutes(line string, p *protocol) {
	match := routeRegex.FindStringSubmatch(line)

	if match != nil {
		p.imported, _ = strconv.ParseInt(match[1], 10, 64)
		p.exported, _ = strconv.ParseInt(match[2], 10, 64)
	}
}

func parseState(state string, proto int) int {
	if proto == OSPF || state == "Established" {
		return 1
	} else {
		return 0
	}
}

func parseUptime(value string) int {
	match := uptimeRegex.FindStringSubmatch(value)

	if match == nil {
		return 0
	}

	if match[1] != "" {
		return parseUptimeForDuration(match)
	}

	return parseUptimeForTimestamp(value)
}

func parseUptimeForDuration(duration []string) int {
	h := parseInt(duration[2])
	m := parseInt(duration[3])
	s := parseInt(duration[4])
	str := fmt.Sprintf("%dh%dm%ds", h, m, s)

	d, err := time.ParseDuration(str)

	if err != nil {
		log.Println(err)
		return 0
	}

	return int(d.Seconds())
}

func parseUptimeForTimestamp(timestamp string) int {
	since := parseInt(timestamp)

	s := time.Unix(since, 0)
	d := time.Since(s)
	return int(d.Seconds())
}

func parseInt(value string) int64 {
	i, err := strconv.ParseInt(value, 10, 64)

	if err != nil {
		log.Println(err)
		return 0
	}

	return i
}
<commit_msg>Fix issue #2<commit_after>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"log"
	"regexp"
	"strconv"
	"time"
)

var (
	protocolRegex *regexp.Regexp
	routeRegex    *regexp.Regexp
	uptimeRegex   *regexp.Regexp
)

func init() {
	protocolRegex, _ = regexp.Compile("^([^\\s]+)\\s+(BGP|OSPF)\\s+([^\\s]+)\\s+([^\\s]+)\\s+([^\\s]+)\\s+(.*?)\\s*$")
	routeRegex, _ = regexp.Compile("^\\s+Routes:\\s+(\\d+) imported, (?:\\d+ filtered, )?(\\d+) exported")
	uptimeRegex, _ = regexp.Compile("^(?:((\\d+):(\\d{2}):(\\d{2}))|\\d+)$")
}

func parseOutput(data []byte, ipVersion int) []*protocol {
	protocols := make([]*protocol, 0)

	reader := bytes.NewReader(data)
	scanner := bufio.NewScanner(reader)
	var current *protocol = nil

	for scanner.Scan() {
		line := scanner.Text()
		if p, ok := parseLineForProtocol(line, ipVersion); ok {
			current = p
			protocols = append(protocols, current)
		}

		if current != nil {
			parseLineForRoutes(line, current)
		}

		if line == "" {
			current = nil
		}
	}

	return protocols
}

func parseLineForProtocol(line string, ipVersion int) (*protocol, bool) {
	match := protocolRegex.FindStringSubmatch(line)

	if match == nil {
		return nil, false
	}

	proto := parseProto(match[2])
	up := parseState(match[4], proto)
	ut := parseUptime(match[5])
	p := &protocol{proto: proto, name: match[1], ipVersion: ipVersion, up: up, uptime: ut, attributes: make(map[string]interface{})}

	return p, true
}

func parseProto(val string) int {
	switch val {
	case "BGP":
		return BGP
	case "OSPF":
		return OSPF
	}

	return PROTO_UNKNOWN
}

func parseLineForRoutes(line string, p *protocol) {
	match := routeRegex.FindStringSubmatch(line)

	if match != nil {
		p.imported, _ = strconv.ParseInt(match[1], 10, 64)
		p.exported, _ = strconv.ParseInt(match[2], 10, 64)
	}
}

func parseState(state string, proto int) int {
	if state == "up" {
		return 1
	} else {
		return 0
	}
}

func parseUptime(value string) int {
	match := uptimeRegex.FindStringSubmatch(value)

	if match == nil {
		return 0
	}

	if match[1] != "" {
		return parseUptimeForDuration(match)
	}

	return parseUptimeForTimestamp(value)
}

func parseUptimeForDuration(duration []string) int {
	h := parseInt(duration[2])
	m := parseInt(duration[3])
	s := parseInt(duration[4])
	str := fmt.Sprintf("%dh%dm%ds", h, m, s)

	d, err := time.ParseDuration(str)

	if err != nil {
		log.Println(err)
		return 0
	}

	return int(d.Seconds())
}

func parseUptimeForTimestamp(timestamp string) int {
	since := parseInt(timestamp)

	s := time.Unix(since, 0)
	d := time.Since(s)
	return int(d.Seconds())
}

func parseInt(value string) int64 {
	i, err := strconv.ParseInt(value, 10, 64)

	if err != nil {
		log.Println(err)
		return 0
	}

	return i
}
<|endoftext|>


<commit_before>// Package parser is an extensible parser that unmarshalls
// multiple data formats and extracts it into go maps
package parser

import (
	"encoding/json"
	"errors"

	"github.com/BurntSushi/toml"
	"gopkg.in/yaml.v2"
)

type (
	//ParseFunc is an interface for a function that parses the input
	ParseFunc func(string) (map[string]interface{}, error)
)

//Parser contains the available parsers
type Parser struct {
	parsers map[string]ParseFunc
}

var (
	// ErrUnknownParser is an error indicating that a parser for the requested
	// inputType is not registered.
	ErrUnknownParser = errors.New("parser: no parser for requested input type")
)

//NewParser creates a new Parser instance
func NewParser() *Parser {
	p := &Parser{parsers: make(map[string]ParseFunc)}

	// add the default parsers
	p.Handle("json", JSONHandler)
	p.Handle("toml", TOMLHandler)
	p.Handle("tml", TOMLHandler)
	p.Handle("yaml", YAMLHandler)
	p.Handle("yml", YAMLHandler)

	return p
}

//Handle registers a handler for the given ext
//This is public so new parsers can be registered
func (p *Parser) Handle(inputType string, fn ParseFunc) {
	p.parsers[inputType] = fn
}

// Parse deserializes the data contained in input based on the ext
func (p *Parser) Parse(inputType string, input string) (map[string]interface{}, error) {
	return p.parse(inputType, input)
}

func (p *Parser) parse(inputType string, input string) (data map[string]interface{}, err error) {
	if h, exists := p.parsers[inputType]; exists {
		data, err = h(input)
		if err != nil {
			return nil, err
		}
	} else {
		return nil, ErrUnknownParser
	}

	return data, nil

}

//JSONHandler decodes json intput into a go map[string]interface{}
func JSONHandler(input string) (map[string]interface{}, error) {
	var out interface{}
	err := json.Unmarshal([]byte(input), &out)
	if err != nil {
		return nil, err
	}
	return out.(map[string]interface{}), nil
}

//YAMLHandler decodes yaml input into a go map[string]interface{}
func YAMLHandler(input string) (map[string]interface{}, error) {
	out := make(map[string]interface{})
	err := yaml.Unmarshal([]byte(input), out)
	if err != nil {
		return nil, err
	}
	return out, nil
}

//TOMLHandler decodes toml imput into a go map[string]interface{}
func TOMLHandler(input string) (map[string]interface{}, error) {
	out := make(map[string]interface{})
	_, err := toml.Decode(input, &out)
	if err != nil {
		return nil, err
	}
	return out, nil
}
<commit_msg>no longer exporting custom error<commit_after>// Package parser is an extensible parser that unmarshalls
// multiple data formats and extracts it into go maps
package parser

import (
	"encoding/json"
	"errors"

	"github.com/BurntSushi/toml"
	"gopkg.in/yaml.v2"
)

type (
	//ParseFunc is an interface for a function that parses the input
	ParseFunc func(string) (map[string]interface{}, error)
)

//Parser contains the available parsers
type Parser struct {
	parsers map[string]ParseFunc
}

var (
	// ErrUnknownParser is an error indicating that a parser for the requested
	// inputType is not registered.
	errUnknownParser = errors.New("parser: no parser for requested input type")
)

//NewParser creates a new Parser instance
func NewParser() *Parser {
	p := &Parser{parsers: make(map[string]ParseFunc)}

	// add the default parsers
	p.Handle("json", JSONHandler)
	p.Handle("toml", TOMLHandler)
	p.Handle("tml", TOMLHandler)
	p.Handle("yaml", YAMLHandler)
	p.Handle("yml", YAMLHandler)

	return p
}

//Handle registers a handler for the given ext
//This is public so new parsers can be registered
func (p *Parser) Handle(inputType string, fn ParseFunc) {
	p.parsers[inputType] = fn
}

// Parse deserializes the data contained in input based on the ext
func (p *Parser) Parse(inputType string, input string) (map[string]interface{}, error) {
	return p.parse(inputType, input)
}

func (p *Parser) parse(inputType string, input string) (data map[string]interface{}, err error) {
	if h, exists := p.parsers[inputType]; exists {
		data, err = h(input)
		if err != nil {
			return nil, err
		}
	} else {
		return nil, errUnknownParser
	}

	return data, nil

}

//JSONHandler decodes json intput into a go map[string]interface{}
func JSONHandler(input string) (map[string]interface{}, error) {
	var out interface{}
	err := json.Unmarshal([]byte(input), &out)
	if err != nil {
		return nil, err
	}
	return out.(map[string]interface{}), nil
}

//YAMLHandler decodes yaml input into a go map[string]interface{}
func YAMLHandler(input string) (map[string]interface{}, error) {
	out := make(map[string]interface{})
	err := yaml.Unmarshal([]byte(input), out)
	if err != nil {
		return nil, err
	}
	return out, nil
}

//TOMLHandler decodes toml imput into a go map[string]interface{}
func TOMLHandler(input string) (map[string]interface{}, error) {
	out := make(map[string]interface{})
	_, err := toml.Decode(input, &out)
	if err != nil {
		return nil, err
	}
	return out, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2017 xgfone

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

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

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

package config

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"unicode"
)

// Parser is an parser interface.
//
// If the parser implementation needs to register some options into the config
// manager, it should get the instance of the config manager then register them
// when creating the parser instance, because the config manager does not allow
// anyone to register the option.
//
//    conf := NewConfig(cliParser)
//    parser := NewXxxParser(conf) // Register the options into conf.
//    conf.AddParser(parser)
//    conf.Parse(nil)
type Parser interface {
	// Name returns the name of the parser to identify it.
	Name() string

	// Parse the value of the registered options.
	//
	// The parser can get any information from the argument, config.
	//
	// When the parser parsed out the option value, it should call
	// config.SetOptValue(), which will set the group option.
	// For the default group, the group name may be "" instead,
	//
	// For the CLI parser, it should get the parsed argument by config.CliArgs(),
	// which is a string slice, not nil, but it maybe have no elements.
	// The CLI parser should not use os.Args[1:] as the parsed CLI arguments.
	// If there are the rest CLI arguments, that's those that does not start
	// with the prefix "-", "--" or others, etc, the CLI parser should call
	// config.SetArgs() to set them.
	//
	// If there is any error, the parser should stop to parse and return it.
	//
	// If a certain option has no value, the parser should not return a default
	// one instead. Also, the parser has no need to convert the value to the
	// corresponding specific type, just string is ok. Because the configuration
	// manager will convert the value to the specific type automatically.
	// Certainly, it's not harmless for the parser to convert the value to
	// the specific type.
	Parse(config *Config) error
}

type flagParser struct {
	flagSet    *flag.FlagSet
	name       string
	errhandler flag.ErrorHandling

	underlineToHyphen bool
}

// NewDefaultFlagCliParser returns a new CLI parser based on flag,
// which is equal to NewFlagCliParser("", 0, underlineToHyphen, flag.CommandLine).
func NewDefaultFlagCliParser(underlineToHyphen ...bool) Parser {
	var u2h bool
	if len(underlineToHyphen) > 0 {
		u2h = underlineToHyphen[0]
	}
	return NewFlagCliParser("", 0, u2h, flag.CommandLine)
}

// NewFlagCliParser returns a new CLI parser based on flag.FlagSet.
//
// The arguments is the same as that of flag.NewFlagSet(), but if the name is
// "", it will be filepath.Base(os.Args[0]).
//
// If underlineToHyphen is true, it will convert the underline to the hyphen.
// If giving flagSet, errhandler will be ignore, so you maybe set it to 0.
//
// When other libraries use the default global flag.FlagSet, that's
// flag.CommandLine, such as github.com/golang/glog, please use
// NewDefaultFlagCliParser(), not this function.
func NewFlagCliParser(appName string, errhandler flag.ErrorHandling,
	underlineToHyphen bool, flagSet ...*flag.FlagSet) Parser {

	if appName == "" {
		appName = filepath.Base(os.Args[0])
	}

	var fset *flag.FlagSet
	if len(flagSet) > 0 && flagSet[0] != nil {
		fset = flagSet[0]
	}

	return flagParser{
		name:       appName,
		flagSet:    fset,
		errhandler: errhandler,

		underlineToHyphen: underlineToHyphen,
	}
}

func (f flagParser) Name() string {
	return "flag"
}

func (f flagParser) Parse(c *Config) (err error) {
	// Register the options into flag.FlagSet.
	flagSet := f.flagSet
	if flagSet == nil {
		flagSet = flag.NewFlagSet(f.name, f.errhandler)
	}

	// Convert the option name.
	name2group := make(map[string]string, 8)
	name2opt := make(map[string]string, 8)
	for gname, group := range c.Groups() {
		for name, opt := range group.CliOpts() {
			if gname != c.GetDefaultGroupName() {
				name = fmt.Sprintf("%s_%s", gname, name)
			}

			if f.underlineToHyphen {
				name = strings.Replace(name, "_", "-", -1)
			}

			name2group[name] = gname
			name2opt[name] = opt.Name()

			if opt.IsBool() {
				var _default bool
				if v := opt.Default(); v != nil {
					_default = v.(bool)
				}
				flagSet.Bool(name, _default, opt.Help())
			} else {
				_default := ""
				if opt.Default() != nil {
					_default = fmt.Sprintf("%v", opt.Default())
				}
				flagSet.String(name, _default, opt.Help())
			}
		}
	}

	// Register the version option.
	name, version, help := c.GetVersion()
	_version := flagSet.Bool(name, false, help)

	// Parse the CLI arguments.
	if err = flagSet.Parse(c.CliArgs()); err != nil {
		return
	}

	if *_version {
		fmt.Println(version)
		os.Exit(0)
	}

	// Acquire the result.
	c.SetArgs(flagSet.Args())
	flagSet.Visit(func(fg *flag.Flag) {
		gname := name2group[fg.Name]
		optname := name2opt[fg.Name]
		if gname != "" && optname != "" && fg.Name != name {
			c.SetOptValue(gname, optname, fg.Value.String())
		}
	})

	return
}

type iniParser struct {
	sep     string
	optName string
	fmtKey  func(string) string
}

// NewSimpleIniParser returns a new ini parser based on the file.
//
// The argument is the option name which the parser needs. It should be
// registered, and parsed before this parser runs.
//
// The ini parser supports the line comments starting with "#", "//" or ";".
// The key and the value is separated by an equal sign, that's =. The key must
// be in one of _, -, number and letter. If giving fmtKey, it can convert
// the key in the ini file to the new one.
//
// If the value ends with "\", it will continue the next line. The lines will
// be joined by "\n" together.
//
// Notice: the options that have not been assigned to a certain group will be
// divided into the default group.
func NewSimpleIniParser(optName string, fmtKey ...func(string) string) Parser {
	f := func(key string) string { return key }
	if len(fmtKey) > 0 && fmtKey[0] != nil {
		f = fmtKey[0]
	}
	return iniParser{optName: optName, sep: "=", fmtKey: f}
}

func (p iniParser) Name() string {
	return "ini"
}

// func (p iniParser) Parse(_default string, opts map[string][]Opt,
// 	conf map[string]interface{}) (results map[string]map[string]interface{},
// 	err error) {
func (p iniParser) Parse(c *Config) error {
	// Read the content of the config file.
	filename := c.Group("").StringD(p.optName, "")
	if filename == "" {
		return nil
	}
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		return err
	}

	// Convert the format of the optons.
	options := make(map[string]map[string]struct{}, len(c.Groups()))
	for gname, group := range c.Groups() {
		opts := group.AllOpts()
		g, ok := options[gname]
		if !ok {
			g = make(map[string]struct{}, len(opts))
			options[gname] = g
		}
		for _, opt := range opts {
			g[opt.Name()] = struct{}{}
		}
	}

	// Parse the config file.
	gname := c.GetDefaultGroupName()
	lines := strings.Split(string(data), "\n")
	for index, maxIndex := 0, len(lines); index < maxIndex; {
		line := strings.TrimSpace(lines[index])
		index++

		// Ignore the empty line.
		if len(line) == 0 {
			continue
		}

		// Ignore the line comments starting with "#" or "//".
		if (line[0] == '#') || (line[0] == ';') ||
			(len(line) > 1 && line[0] == '/' && line[1] == '/') {
			continue
		}

		// Start a new group
		if line[0] == '[' && line[len(line)-1] == ']' {
			gname = strings.TrimSpace(line[1 : len(line)-1])
			if gname == "" {
				return fmt.Errorf("the group is empty")
			}
			continue
		}

		n := strings.Index(line, p.sep)
		if n == -1 {
			return fmt.Errorf("the line misses the separator %s", p.sep)
		}

		key := strings.TrimSpace(line[0:n])
		for _, r := range key {
			if r != '_' && r != '-' && !unicode.IsNumber(r) && !unicode.IsLetter(r) {
				return fmt.Errorf("valid identifier key '%s'", key)
			}
		}
		value := strings.TrimSpace(line[n+len(p.sep) : len(line)])

		// The continuation line
		if value != "" && value[len(value)-1] == '\\' {
			vs := []string{strings.TrimSpace(strings.TrimRight(value, "\\"))}
			for index < maxIndex {
				value = strings.TrimSpace(lines[index])
				vs = append(vs, strings.TrimSpace(strings.TrimRight(value, "\\")))
				index++
				if value == "" || value[len(value)-1] != '\\' {
					break
				}
			}
			value = strings.TrimSpace(strings.Join(vs, "\n"))
		}

		if newkey := p.fmtKey(key); newkey != "" {
			key = newkey
		} else {
			panic(fmt.Errorf("convert the key '%s' to ''", key))
		}

		c.SetOptValue(gname, key, value)
	}

	return nil
}

type envVarParser struct {
	prefix string
}

// NewEnvVarParser returns a new environment variable parser.
//
// For the environment variable name, it's the format "PREFIX_GROUP_OPTION".
// If the prefix is empty, it's "GROUP_OPTION". For the default group, it's
// "PREFIX_OPTION". When the prefix is empty and the group is the default,
// it's "OPTION". "GROUP" is the group name, and "OPTION" is the option name.
//
// Notice: the prefix, the group name and the option name will be converted to
// the upper.
func NewEnvVarParser(prefix string) Parser {
	return envVarParser{prefix: prefix}
}

func (e envVarParser) Name() string {
	return "env"
}

// func (e envVarParser) Parse(_default string, opts map[string][]Opt,
// 	conf map[string]interface{}) (results map[string]map[string]interface{},
// 	err error) {
func (e envVarParser) Parse(c *Config) error {
	// Initialize the prefix
	prefix := e.prefix
	if prefix != "" {
		prefix += "_"
	}

	// Convert the option to the variable name
	env2opts := make(map[string][]string, len(c.Groups())*8)
	for gname, group := range c.Groups() {
		_gname := ""
		if gname != c.GetDefaultGroupName() {
			_gname = gname + "_"
		}
		for name := range group.AllOpts() {
			e := fmt.Sprintf("%s%s%s", prefix, _gname, name)
			env2opts[strings.ToUpper(e)] = []string{gname, name}
		}
	}

	// Get the option value from the environment variable.
	envs := os.Environ()
	for _, env := range envs {
		items := strings.SplitN(env, "=", 2)
		if len(items) == 2 {
			if info, ok := env2opts[items[0]]; ok {
				c.SetOptValue(info[0], info[1], items[1])
			}
		}
	}

	return nil
}
<commit_msg>fix: do not add the version cli option when no setting version<commit_after>/*
Copyright 2017 xgfone

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

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

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

package config

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"unicode"
)

// Parser is an parser interface.
//
// If the parser implementation needs to register some options into the config
// manager, it should get the instance of the config manager then register them
// when creating the parser instance, because the config manager does not allow
// anyone to register the option.
//
//    conf := NewConfig(cliParser)
//    parser := NewXxxParser(conf) // Register the options into conf.
//    conf.AddParser(parser)
//    conf.Parse(nil)
type Parser interface {
	// Name returns the name of the parser to identify it.
	Name() string

	// Parse the value of the registered options.
	//
	// The parser can get any information from the argument, config.
	//
	// When the parser parsed out the option value, it should call
	// config.SetOptValue(), which will set the group option.
	// For the default group, the group name may be "" instead,
	//
	// For the CLI parser, it should get the parsed argument by config.CliArgs(),
	// which is a string slice, not nil, but it maybe have no elements.
	// The CLI parser should not use os.Args[1:] as the parsed CLI arguments.
	// If there are the rest CLI arguments, that's those that does not start
	// with the prefix "-", "--" or others, etc, the CLI parser should call
	// config.SetArgs() to set them.
	//
	// If there is any error, the parser should stop to parse and return it.
	//
	// If a certain option has no value, the parser should not return a default
	// one instead. Also, the parser has no need to convert the value to the
	// corresponding specific type, just string is ok. Because the configuration
	// manager will convert the value to the specific type automatically.
	// Certainly, it's not harmless for the parser to convert the value to
	// the specific type.
	Parse(config *Config) error
}

type flagParser struct {
	flagSet    *flag.FlagSet
	name       string
	errhandler flag.ErrorHandling

	underlineToHyphen bool
}

// NewDefaultFlagCliParser returns a new CLI parser based on flag,
// which is equal to NewFlagCliParser("", 0, underlineToHyphen, flag.CommandLine).
func NewDefaultFlagCliParser(underlineToHyphen ...bool) Parser {
	var u2h bool
	if len(underlineToHyphen) > 0 {
		u2h = underlineToHyphen[0]
	}
	return NewFlagCliParser("", 0, u2h, flag.CommandLine)
}

// NewFlagCliParser returns a new CLI parser based on flag.FlagSet.
//
// The arguments is the same as that of flag.NewFlagSet(), but if the name is
// "", it will be filepath.Base(os.Args[0]).
//
// If underlineToHyphen is true, it will convert the underline to the hyphen.
// If giving flagSet, errhandler will be ignore, so you maybe set it to 0.
//
// When other libraries use the default global flag.FlagSet, that's
// flag.CommandLine, such as github.com/golang/glog, please use
// NewDefaultFlagCliParser(), not this function.
func NewFlagCliParser(appName string, errhandler flag.ErrorHandling,
	underlineToHyphen bool, flagSet ...*flag.FlagSet) Parser {

	if appName == "" {
		appName = filepath.Base(os.Args[0])
	}

	var fset *flag.FlagSet
	if len(flagSet) > 0 && flagSet[0] != nil {
		fset = flagSet[0]
	}

	return flagParser{
		name:       appName,
		flagSet:    fset,
		errhandler: errhandler,

		underlineToHyphen: underlineToHyphen,
	}
}

func (f flagParser) Name() string {
	return "flag"
}

func (f flagParser) Parse(c *Config) (err error) {
	// Register the options into flag.FlagSet.
	flagSet := f.flagSet
	if flagSet == nil {
		flagSet = flag.NewFlagSet(f.name, f.errhandler)
	}

	// Convert the option name.
	name2group := make(map[string]string, 8)
	name2opt := make(map[string]string, 8)
	for gname, group := range c.Groups() {
		for name, opt := range group.CliOpts() {
			if gname != c.GetDefaultGroupName() {
				name = fmt.Sprintf("%s_%s", gname, name)
			}

			if f.underlineToHyphen {
				name = strings.Replace(name, "_", "-", -1)
			}

			name2group[name] = gname
			name2opt[name] = opt.Name()

			if opt.IsBool() {
				var _default bool
				if v := opt.Default(); v != nil {
					_default = v.(bool)
				}
				flagSet.Bool(name, _default, opt.Help())
			} else {
				_default := ""
				if opt.Default() != nil {
					_default = fmt.Sprintf("%v", opt.Default())
				}
				flagSet.String(name, _default, opt.Help())
			}
		}
	}

	// Register the version option.
	var _version *bool
	name, version, help := c.GetVersion()
	if name != "" {
		_version = flagSet.Bool(name, false, help)
	}

	// Parse the CLI arguments.
	if err = flagSet.Parse(c.CliArgs()); err != nil {
		return
	}

	if _version != nil && *_version {
		fmt.Println(version)
		os.Exit(0)
	}

	// Acquire the result.
	c.SetArgs(flagSet.Args())
	flagSet.Visit(func(fg *flag.Flag) {
		gname := name2group[fg.Name]
		optname := name2opt[fg.Name]
		if gname != "" && optname != "" && fg.Name != name {
			c.SetOptValue(gname, optname, fg.Value.String())
		}
	})

	return
}

type iniParser struct {
	sep     string
	optName string
	fmtKey  func(string) string
}

// NewSimpleIniParser returns a new ini parser based on the file.
//
// The argument is the option name which the parser needs. It should be
// registered, and parsed before this parser runs.
//
// The ini parser supports the line comments starting with "#", "//" or ";".
// The key and the value is separated by an equal sign, that's =. The key must
// be in one of _, -, number and letter. If giving fmtKey, it can convert
// the key in the ini file to the new one.
//
// If the value ends with "\", it will continue the next line. The lines will
// be joined by "\n" together.
//
// Notice: the options that have not been assigned to a certain group will be
// divided into the default group.
func NewSimpleIniParser(optName string, fmtKey ...func(string) string) Parser {
	f := func(key string) string { return key }
	if len(fmtKey) > 0 && fmtKey[0] != nil {
		f = fmtKey[0]
	}
	return iniParser{optName: optName, sep: "=", fmtKey: f}
}

func (p iniParser) Name() string {
	return "ini"
}

// func (p iniParser) Parse(_default string, opts map[string][]Opt,
// 	conf map[string]interface{}) (results map[string]map[string]interface{},
// 	err error) {
func (p iniParser) Parse(c *Config) error {
	// Read the content of the config file.
	filename := c.Group("").StringD(p.optName, "")
	if filename == "" {
		return nil
	}
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		return err
	}

	// Convert the format of the optons.
	options := make(map[string]map[string]struct{}, len(c.Groups()))
	for gname, group := range c.Groups() {
		opts := group.AllOpts()
		g, ok := options[gname]
		if !ok {
			g = make(map[string]struct{}, len(opts))
			options[gname] = g
		}
		for _, opt := range opts {
			g[opt.Name()] = struct{}{}
		}
	}

	// Parse the config file.
	gname := c.GetDefaultGroupName()
	lines := strings.Split(string(data), "\n")
	for index, maxIndex := 0, len(lines); index < maxIndex; {
		line := strings.TrimSpace(lines[index])
		index++

		// Ignore the empty line.
		if len(line) == 0 {
			continue
		}

		// Ignore the line comments starting with "#" or "//".
		if (line[0] == '#') || (line[0] == ';') ||
			(len(line) > 1 && line[0] == '/' && line[1] == '/') {
			continue
		}

		// Start a new group
		if line[0] == '[' && line[len(line)-1] == ']' {
			gname = strings.TrimSpace(line[1 : len(line)-1])
			if gname == "" {
				return fmt.Errorf("the group is empty")
			}
			continue
		}

		n := strings.Index(line, p.sep)
		if n == -1 {
			return fmt.Errorf("the line misses the separator %s", p.sep)
		}

		key := strings.TrimSpace(line[0:n])
		for _, r := range key {
			if r != '_' && r != '-' && !unicode.IsNumber(r) && !unicode.IsLetter(r) {
				return fmt.Errorf("valid identifier key '%s'", key)
			}
		}
		value := strings.TrimSpace(line[n+len(p.sep) : len(line)])

		// The continuation line
		if value != "" && value[len(value)-1] == '\\' {
			vs := []string{strings.TrimSpace(strings.TrimRight(value, "\\"))}
			for index < maxIndex {
				value = strings.TrimSpace(lines[index])
				vs = append(vs, strings.TrimSpace(strings.TrimRight(value, "\\")))
				index++
				if value == "" || value[len(value)-1] != '\\' {
					break
				}
			}
			value = strings.TrimSpace(strings.Join(vs, "\n"))
		}

		if newkey := p.fmtKey(key); newkey != "" {
			key = newkey
		} else {
			panic(fmt.Errorf("convert the key '%s' to ''", key))
		}

		c.SetOptValue(gname, key, value)
	}

	return nil
}

type envVarParser struct {
	prefix string
}

// NewEnvVarParser returns a new environment variable parser.
//
// For the environment variable name, it's the format "PREFIX_GROUP_OPTION".
// If the prefix is empty, it's "GROUP_OPTION". For the default group, it's
// "PREFIX_OPTION". When the prefix is empty and the group is the default,
// it's "OPTION". "GROUP" is the group name, and "OPTION" is the option name.
//
// Notice: the prefix, the group name and the option name will be converted to
// the upper.
func NewEnvVarParser(prefix string) Parser {
	return envVarParser{prefix: prefix}
}

func (e envVarParser) Name() string {
	return "env"
}

// func (e envVarParser) Parse(_default string, opts map[string][]Opt,
// 	conf map[string]interface{}) (results map[string]map[string]interface{},
// 	err error) {
func (e envVarParser) Parse(c *Config) error {
	// Initialize the prefix
	prefix := e.prefix
	if prefix != "" {
		prefix += "_"
	}

	// Convert the option to the variable name
	env2opts := make(map[string][]string, len(c.Groups())*8)
	for gname, group := range c.Groups() {
		_gname := ""
		if gname != c.GetDefaultGroupName() {
			_gname = gname + "_"
		}
		for name := range group.AllOpts() {
			e := fmt.Sprintf("%s%s%s", prefix, _gname, name)
			env2opts[strings.ToUpper(e)] = []string{gname, name}
		}
	}

	// Get the option value from the environment variable.
	envs := os.Environ()
	for _, env := range envs {
		items := strings.SplitN(env, "=", 2)
		if len(items) == 2 {
			if info, ok := env2opts[items[0]]; ok {
				c.SetOptValue(info[0], info[1], items[1])
			}
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"os/user"
	"strconv"
	"strings"
	"syscall"
	"time"
)

var uidMap = map[string]user.User{}
var headerEndChar = []byte{")"[0]}

const (
	HEADER_MIN_LENGTH = 7               // Minimum length of an audit header
	HEADER_START_POS  = 6               // Position in the audit header that the data starts
	COMPLETE_AFTER    = time.Second * 2 // Log a message after this time or EOE
)

type AuditMessage struct {
	Type uint16 `json:"type"`
	Data string `json:"data"`
	Seq int `json:"-"`
	AuditTime string `json:"-"`
}

type AuditMessageGroup struct {
	Seq           int `json:"sequence"`
	AuditTime     string `json:"timestamp"`
	CompleteAfter time.Time `json:"-"`
	Msgs          []*AuditMessage `json:"messages"`
	UidMap        map[string]string `json:"uid_map"`
}

func NewAuditMessageGroup(am *AuditMessage) *AuditMessageGroup {
	//TODO: allocating 6 msgs per group is lame and we _should_ know ahead of time roughly how many we need
	amg := &AuditMessageGroup{
		Seq:           am.Seq,
		AuditTime:     am.AuditTime,
		CompleteAfter: time.Now().Add(COMPLETE_AFTER),
		Msgs:          make([]*AuditMessage, 6), // 6 msgs per execve is common
		UidMap:        make(map[string]string, 2), // Usually only 2 individual uids per execve
	}

	amg.AddMessage(am)
	return amg
}

func NewAuditMessage(nlm *syscall.NetlinkMessage) *AuditMessage {
	aTime, seq := parseAuditHeader(nlm)
	return &AuditMessage{
		Type: nlm.Header.Type,
		Data: string(nlm.Data),
		Seq: seq,
		AuditTime: aTime,
	}
}

func parseAuditHeader(msg *syscall.NetlinkMessage) (time string, seq int) {
	headerStop := bytes.Index(msg.Data, headerEndChar)
	// If the position the header appears to stop is less than the minimum length of a header, bail out
	if headerStop < HEADER_MIN_LENGTH {
		return
	}

	header := string(msg.Data[:headerStop])
	if header[:HEADER_START_POS] == "audit(" {
		//TODO: out of range check, possibly fully binary?
		sep := strings.IndexByte(header, ":"[0])
		time = header[HEADER_START_POS:sep]
		seq, _ = strconv.Atoi(header[sep + 1:])

		// Remove the header from data
		msg.Data = msg.Data[headerStop+2:]
	}

	return time, seq
}

func (amg *AuditMessageGroup) AddMessage(am *AuditMessage) {
	amg.Msgs = append(amg.Msgs, am)
	amg.mapUids(am)
}

//This takes the map to modify and a key name and adds the username to a new key with "_username"
func (amg *AuditMessageGroup) mapUids(am *AuditMessage) {
	data := am.Data
	start := 0
	end := 0

	for {
		if start = strings.Index(data, "uid="); start < 0 {
			break
		}

		start += 4
		if end = strings.IndexByte(data[start:], " "[0]); end < 0 {
			break
		}

		uid := data[start:start + end]
		// Don't bother re-adding if the existing group already has the mapping
		if _, ok := amg.UidMap[uid]; !ok {
			amg.UidMap[uid] = findUid(data[start:start + end])
		}

		next := start + end + 1
		if (next >= len(data)) {
			break
		}
		data = data[next:]
	}

}

func findUid(uid string) (string) {
	uname := "UNKNOWN_USER"

	//Make sure we have a uid element to work with.
	//Give a default value in case we don't find something.
	if lUser, ok := uidMap[uid]; ok {
		uname = lUser.Username
	} else {
		lUser, err := user.LookupId(uid)
		if err == nil {
			uname = lUser.Username
			uidMap[uid] = *lUser
		} else {
			uidMap[uid] = user.User{Username: uname}
		}
	}

	return uname
}
<commit_msg>Ignore a few message types that don't have uids<commit_after>package main

import (
	"bytes"
	"os/user"
	"strconv"
	"strings"
	"syscall"
	"time"
)

var uidMap = map[string]user.User{}
var headerEndChar = []byte{")"[0]}

const (
	HEADER_MIN_LENGTH = 7               // Minimum length of an audit header
	HEADER_START_POS  = 6               // Position in the audit header that the data starts
	COMPLETE_AFTER    = time.Second * 2 // Log a message after this time or EOE
)

type AuditMessage struct {
	Type uint16 `json:"type"`
	Data string `json:"data"`
	Seq int `json:"-"`
	AuditTime string `json:"-"`
}

type AuditMessageGroup struct {
	Seq           int `json:"sequence"`
	AuditTime     string `json:"timestamp"`
	CompleteAfter time.Time `json:"-"`
	Msgs          []*AuditMessage `json:"messages"`
	UidMap        map[string]string `json:"uid_map"`
}

func NewAuditMessageGroup(am *AuditMessage) *AuditMessageGroup {
	//TODO: allocating 6 msgs per group is lame and we _should_ know ahead of time roughly how many we need
	amg := &AuditMessageGroup{
		Seq:           am.Seq,
		AuditTime:     am.AuditTime,
		CompleteAfter: time.Now().Add(COMPLETE_AFTER),
		Msgs:          make([]*AuditMessage, 6), // 6 msgs per execve is common
		UidMap:        make(map[string]string, 2), // Usually only 2 individual uids per execve
	}

	amg.AddMessage(am)
	return amg
}

func NewAuditMessage(nlm *syscall.NetlinkMessage) *AuditMessage {
	aTime, seq := parseAuditHeader(nlm)
	return &AuditMessage{
		Type: nlm.Header.Type,
		Data: string(nlm.Data),
		Seq: seq,
		AuditTime: aTime,
	}
}

func parseAuditHeader(msg *syscall.NetlinkMessage) (time string, seq int) {
	headerStop := bytes.Index(msg.Data, headerEndChar)
	// If the position the header appears to stop is less than the minimum length of a header, bail out
	if headerStop < HEADER_MIN_LENGTH {
		return
	}

	header := string(msg.Data[:headerStop])
	if header[:HEADER_START_POS] == "audit(" {
		//TODO: out of range check, possibly fully binary?
		sep := strings.IndexByte(header, ":"[0])
		time = header[HEADER_START_POS:sep]
		seq, _ = strconv.Atoi(header[sep + 1:])

		// Remove the header from data
		msg.Data = msg.Data[headerStop+2:]
	}

	return time, seq
}

func (amg *AuditMessageGroup) AddMessage(am *AuditMessage) {
	amg.Msgs = append(amg.Msgs, am)
	//TODO: need to find more message types that won't contain uids, also make these constants
	switch am.Type {
	case 1309, 1307:
		// Don't map uids here
	default:
		amg.mapUids(am)
	}
}

//This takes the map to modify and a key name and adds the username to a new key with "_username"
func (amg *AuditMessageGroup) mapUids(am *AuditMessage) {
	data := am.Data
	start := 0
	end := 0

	for {
		if start = strings.Index(data, "uid="); start < 0 {
			break
		}

		start += 4
		if end = strings.IndexByte(data[start:], " "[0]); end < 0 {
			break
		}

		uid := data[start:start + end]
		// Don't bother re-adding if the existing group already has the mapping
		if _, ok := amg.UidMap[uid]; !ok {
			amg.UidMap[uid] = findUid(data[start:start + end])
		}

		next := start + end + 1
		if (next >= len(data)) {
			break
		}
		data = data[next:]
	}

}

func findUid(uid string) (string) {
	uname := "UNKNOWN_USER"

	//Make sure we have a uid element to work with.
	//Give a default value in case we don't find something.
	if lUser, ok := uidMap[uid]; ok {
		uname = lUser.Username
	} else {
		lUser, err := user.LookupId(uid)
		if err == nil {
			uname = lUser.Username
			uidMap[uid] = *lUser
		} else {
			uidMap[uid] = user.User{Username: uname}
		}
	}

	return uname
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"strconv"
	"strings"
)

type move struct {
	direction byte
	amount    int
}

type position struct {
	x int
	y int
}

func abs_int(x int) int {
	if x < 0 {
		return -x
	}
	return x
}

func (p *position) dist() int {
	return abs_int(p.x) + abs_int(p.y)
}

func read_moves() []move {
	input_data, err := ioutil.ReadFile("input.txt")
	if err != nil {
		panic(err)
	}

	input_data_str := strings.Split(strings.TrimSpace(string(input_data)), ", ")

	moves := make([]move, len(input_data_str))
	for i := 0; i < len(input_data_str); i++ {
		move_amount, _ := strconv.Atoi(input_data_str[i][1:])
		moves[i] = move{
			direction: input_data_str[i][0],
			amount:    move_amount,
		}
	}

	return moves
}

func main() {
	current_position := position{
		x: 0,
		y: 0,
	}
	dirs := []byte{'N', 'E', 'S', 'W'}
	facing := 0
	visited := make(map[position]bool)
MoveLoop:
	for _, move := range read_moves() {
		//fmt.Printf("%c: %d\n", move.direction, move.amount)
		switch move.direction {
		case 'R':
			facing++
		case 'L':
			facing--
		default:
			panic("Bad direction given")
		}

		if facing > 3 {
			facing = 0
		} else if facing < 0 {
			facing = 3
		}

		for i := 0; i < move.amount; i++ {
			switch dirs[facing] {
			case 'N':
				current_position.y++
			case 'E':
				current_position.x++
			case 'S':
				current_position.y--
			case 'W':
				current_position.x--
			}

			if visited[current_position] {
				fmt.Printf("Visited twice: X: %d, Y: %d, distance: %d\n",
					current_position.x, current_position.y, current_position.dist())
				break MoveLoop
			}

			visited[current_position] = true
		}
	}
}
<commit_msg>Moved abs value to a method on an int type alias<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"strconv"
	"strings"
)

type move struct {
	direction byte
	amount    int
}

type Int int

func (x Int) abs() Int {
	if x < 0 {
		return -x
	}
	return x
}

type position struct {
	x Int
	y Int
}

func (p *position) dist() Int {
	return p.x.abs() + p.y.abs()
}

func read_moves() []move {
	input_data, err := ioutil.ReadFile("input.txt")
	if err != nil {
		panic(err)
	}

	input_data_str := strings.Split(strings.TrimSpace(string(input_data)), ", ")

	moves := make([]move, len(input_data_str))
	for i := 0; i < len(input_data_str); i++ {
		move_amount, _ := strconv.Atoi(input_data_str[i][1:])
		moves[i] = move{
			direction: input_data_str[i][0],
			amount:    move_amount,
		}
	}

	return moves
}

func main() {
	current_position := position{
		x: 0,
		y: 0,
	}
	dirs := []byte{'N', 'E', 'S', 'W'}
	facing := 0
	visited := make(map[position]bool)
MoveLoop:
	for _, move := range read_moves() {
		//fmt.Printf("%c: %d\n", move.direction, move.amount)
		switch move.direction {
		case 'R':
			facing++
		case 'L':
			facing--
		default:
			panic("Bad direction given")
		}

		if facing > 3 {
			facing = 0
		} else if facing < 0 {
			facing = 3
		}

		for i := 0; i < move.amount; i++ {
			switch dirs[facing] {
			case 'N':
				current_position.y++
			case 'E':
				current_position.x++
			case 'S':
				current_position.y--
			case 'W':
				current_position.x--
			}

			if visited[current_position] {
				fmt.Printf("Visited twice: X: %d, Y: %d, distance: %d\n",
					current_position.x, current_position.y, current_position.dist())
				break MoveLoop
			}

			visited[current_position] = true
		}
	}
}
<|endoftext|>


<commit_before>package cienv

import (
	"os"
	"reflect"
	"testing"
)

func setupEnvs() (cleanup func()) {
	var cleanEnvs = []string{
		"CIRCLE_BRANCH",
		"CIRCLE_PROJECT_REPONAME",
		"CIRCLE_PROJECT_USERNAME",
		"CIRCLE_PR_NUMBER",
		"CIRCLE_SHA1",
		"CI_BRANCH",
		"CI_COMMIT",
		"CI_COMMIT_SHA",
		"CI_PROJECT_NAME",
		"CI_PROJECT_NAMESPACE",
		"CI_PULL_REQUEST",
		"CI_REPO_NAME",
		"CI_REPO_OWNER",
		"DRONE_COMMIT",
		"DRONE_COMMIT_BRANCH",
		"DRONE_PULL_REQUEST",
		"DRONE_REPO",
		"DRONE_REPO_NAME",
		"DRONE_REPO_OWNER",
		"TRAVIS_COMMIT",
		"TRAVIS_PULL_REQUEST",
		"TRAVIS_PULL_REQUEST_BRANCH",
		"TRAVIS_PULL_REQUEST_SHA",
		"TRAVIS_REPO_SLUG",
	}
	saveEnvs := make(map[string]string)
	for _, key := range cleanEnvs {
		saveEnvs[key] = os.Getenv(key)
		os.Unsetenv(key)
	}
	return func() {
		for key, value := range saveEnvs {
			os.Setenv(key, value)
		}
	}
}

func TestGetBuildInfo_travis(t *testing.T) {
	cleanup := setupEnvs()
	defer cleanup()

	os.Setenv("TRAVIS_REPO_SLUG", "invalid repo slug")

	if _, _, err := GetBuildInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("TRAVIS_REPO_SLUG", "haya14busa/reviewdog")

	if _, _, err := GetBuildInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("TRAVIS_PULL_REQUEST_SHA", "sha")

	_, isPR, err := GetBuildInfo()
	if err != nil {
		t.Errorf("got unexpected err: %v", err)
	}
	if isPR {
		t.Errorf("isPR = %v, want false", isPR)
	}

	os.Setenv("TRAVIS_PULL_REQUEST", "str")

	_, isPR, err = GetBuildInfo()
	if err != nil {
		t.Errorf("got unexpected error: %v", err)
	}
	if isPR {
		t.Errorf("isPR = %v, want false", isPR)
	}

	os.Setenv("TRAVIS_PULL_REQUEST", "1")

	if _, isPR, err = GetBuildInfo(); err != nil {
		t.Errorf("got unexpected err: %v", err)
	}
	if !isPR {
		t.Error("should be pull request build")
	}

	os.Setenv("TRAVIS_PULL_REQUEST", "false")

	_, isPR, err = GetBuildInfo()
	if err != nil {
		t.Errorf("got unexpected err: %v", err)
	}
	if isPR {
		t.Errorf("isPR = %v, want false", isPR)
	}
}

func TestGetBuildInfo_circleci(t *testing.T) {
	cleanup := setupEnvs()
	defer cleanup()

	if _, isPR, err := GetBuildInfo(); isPR {
		t.Errorf("should be non pull-request build. error: %v", err)
	}

	os.Setenv("CIRCLE_PR_NUMBER", "1")
	if _, _, err := GetBuildInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("CIRCLE_PROJECT_USERNAME", "haya14busa")
	if _, _, err := GetBuildInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("CIRCLE_PROJECT_REPONAME", "reviewdog")
	if _, _, err := GetBuildInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("CIRCLE_SHA1", "sha1")
	g, isPR, err := GetBuildInfo()
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if !isPR {
		t.Error("should be pull request build")
	}
	want := &BuildInfo{
		Owner:       "haya14busa",
		Repo:        "reviewdog",
		PullRequest: 1,
		SHA:         "sha1",
	}
	if !reflect.DeepEqual(g, want) {
		t.Errorf("got: %#v, want: %#v", g, want)
	}
}

func TestGetBuildInfo_droneio(t *testing.T) {
	cleanup := setupEnvs()
	defer cleanup()

	if _, isPR, err := GetBuildInfo(); isPR {
		t.Errorf("should be non pull-request build. error: %v", err)
	}

	os.Setenv("DRONE_PULL_REQUEST", "1")
	if _, _, err := GetBuildInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	// Drone <= 0.4 without valid repo
	os.Setenv("DRONE_REPO", "invalid")
	if _, _, err := GetBuildInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}
	os.Unsetenv("DRONE_REPO")

	// Drone > 0.4 without DRONE_REPO_NAME
	os.Setenv("DRONE_REPO_OWNER", "haya14busa")
	if _, _, err := GetBuildInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}
	os.Unsetenv("DRONE_REPO_OWNER")

	// Drone > 0.4 without DRONE_REPO_OWNER
	os.Setenv("DRONE_REPO_NAME", "reviewdog")
	if _, _, err := GetBuildInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	// Drone > 0.4 have valid variables
	os.Setenv("DRONE_REPO_NAME", "reviewdog")
	os.Setenv("DRONE_REPO_OWNER", "haya14busa")

	os.Setenv("DRONE_COMMIT", "sha1")
	g, isPR, err := GetBuildInfo()
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if !isPR {
		t.Error("should be pull request build")
	}
	want := &BuildInfo{
		Owner:       "haya14busa",
		Repo:        "reviewdog",
		PullRequest: 1,
		SHA:         "sha1",
	}
	if !reflect.DeepEqual(g, want) {
		t.Errorf("got: %#v, want: %#v", g, want)
	}
}

func TestGetBuildInfo_common(t *testing.T) {
	cleanup := setupEnvs()
	defer cleanup()

	if _, isPR, err := GetBuildInfo(); isPR {
		t.Errorf("should be non pull-request build. error: %v", err)
	}

	os.Setenv("CI_PULL_REQUEST", "1")
	if _, _, err := GetBuildInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("CI_REPO_OWNER", "haya14busa")
	if _, _, err := GetBuildInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("CI_REPO_NAME", "reviewdog")
	if _, _, err := GetBuildInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("CI_COMMIT", "sha1")
	g, isPR, err := GetBuildInfo()
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if !isPR {
		t.Error("should be pull request build")
	}
	want := &BuildInfo{
		Owner:       "haya14busa",
		Repo:        "reviewdog",
		PullRequest: 1,
		SHA:         "sha1",
	}
	if !reflect.DeepEqual(g, want) {
		t.Errorf("got: %#v, want: %#v", g, want)
	}
}
<commit_msg>Fix cienv test on circleci 2.0<commit_after>package cienv

import (
	"os"
	"reflect"
	"testing"
)

func setupEnvs() (cleanup func()) {
	var cleanEnvs = []string{
		"CIRCLE_BRANCH",
		"CIRCLE_PROJECT_REPONAME",
		"CIRCLE_PROJECT_USERNAME",
		"CIRCLE_PR_NUMBER",
		"CIRCLE_PULL_REQUEST",
		"CIRCLE_SHA1",
		"CI_BRANCH",
		"CI_COMMIT",
		"CI_COMMIT_SHA",
		"CI_PROJECT_NAME",
		"CI_PROJECT_NAMESPACE",
		"CI_PULL_REQUEST",
		"CI_REPO_NAME",
		"CI_REPO_OWNER",
		"DRONE_COMMIT",
		"DRONE_COMMIT_BRANCH",
		"DRONE_PULL_REQUEST",
		"DRONE_REPO",
		"DRONE_REPO_NAME",
		"DRONE_REPO_OWNER",
		"TRAVIS_COMMIT",
		"TRAVIS_PULL_REQUEST",
		"TRAVIS_PULL_REQUEST_BRANCH",
		"TRAVIS_PULL_REQUEST_SHA",
		"TRAVIS_REPO_SLUG",
	}
	saveEnvs := make(map[string]string)
	for _, key := range cleanEnvs {
		saveEnvs[key] = os.Getenv(key)
		os.Unsetenv(key)
	}
	return func() {
		for key, value := range saveEnvs {
			os.Setenv(key, value)
		}
	}
}

func TestGetBuildInfo_travis(t *testing.T) {
	cleanup := setupEnvs()
	defer cleanup()

	os.Setenv("TRAVIS_REPO_SLUG", "invalid repo slug")

	if _, _, err := GetBuildInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("TRAVIS_REPO_SLUG", "haya14busa/reviewdog")

	if _, _, err := GetBuildInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("TRAVIS_PULL_REQUEST_SHA", "sha")

	_, isPR, err := GetBuildInfo()
	if err != nil {
		t.Errorf("got unexpected err: %v", err)
	}
	if isPR {
		t.Errorf("isPR = %v, want false", isPR)
	}

	os.Setenv("TRAVIS_PULL_REQUEST", "str")

	_, isPR, err = GetBuildInfo()
	if err != nil {
		t.Errorf("got unexpected error: %v", err)
	}
	if isPR {
		t.Errorf("isPR = %v, want false", isPR)
	}

	os.Setenv("TRAVIS_PULL_REQUEST", "1")

	if _, isPR, err = GetBuildInfo(); err != nil {
		t.Errorf("got unexpected err: %v", err)
	}
	if !isPR {
		t.Error("should be pull request build")
	}

	os.Setenv("TRAVIS_PULL_REQUEST", "false")

	_, isPR, err = GetBuildInfo()
	if err != nil {
		t.Errorf("got unexpected err: %v", err)
	}
	if isPR {
		t.Errorf("isPR = %v, want false", isPR)
	}
}

func TestGetBuildInfo_circleci(t *testing.T) {
	cleanup := setupEnvs()
	defer cleanup()

	if _, isPR, err := GetBuildInfo(); isPR {
		t.Errorf("should be non pull-request build. error: %v", err)
	}

	os.Setenv("CIRCLE_PR_NUMBER", "1")
	if _, _, err := GetBuildInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("CIRCLE_PROJECT_USERNAME", "haya14busa")
	if _, _, err := GetBuildInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("CIRCLE_PROJECT_REPONAME", "reviewdog")
	if _, _, err := GetBuildInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("CIRCLE_SHA1", "sha1")
	g, isPR, err := GetBuildInfo()
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if !isPR {
		t.Error("should be pull request build")
	}
	want := &BuildInfo{
		Owner:       "haya14busa",
		Repo:        "reviewdog",
		PullRequest: 1,
		SHA:         "sha1",
	}
	if !reflect.DeepEqual(g, want) {
		t.Errorf("got: %#v, want: %#v", g, want)
	}
}

func TestGetBuildInfo_droneio(t *testing.T) {
	cleanup := setupEnvs()
	defer cleanup()

	if _, isPR, err := GetBuildInfo(); isPR {
		t.Errorf("should be non pull-request build. error: %v", err)
	}

	os.Setenv("DRONE_PULL_REQUEST", "1")
	if _, _, err := GetBuildInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	// Drone <= 0.4 without valid repo
	os.Setenv("DRONE_REPO", "invalid")
	if _, _, err := GetBuildInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}
	os.Unsetenv("DRONE_REPO")

	// Drone > 0.4 without DRONE_REPO_NAME
	os.Setenv("DRONE_REPO_OWNER", "haya14busa")
	if _, _, err := GetBuildInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}
	os.Unsetenv("DRONE_REPO_OWNER")

	// Drone > 0.4 without DRONE_REPO_OWNER
	os.Setenv("DRONE_REPO_NAME", "reviewdog")
	if _, _, err := GetBuildInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	// Drone > 0.4 have valid variables
	os.Setenv("DRONE_REPO_NAME", "reviewdog")
	os.Setenv("DRONE_REPO_OWNER", "haya14busa")

	os.Setenv("DRONE_COMMIT", "sha1")
	g, isPR, err := GetBuildInfo()
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if !isPR {
		t.Error("should be pull request build")
	}
	want := &BuildInfo{
		Owner:       "haya14busa",
		Repo:        "reviewdog",
		PullRequest: 1,
		SHA:         "sha1",
	}
	if !reflect.DeepEqual(g, want) {
		t.Errorf("got: %#v, want: %#v", g, want)
	}
}

func TestGetBuildInfo_common(t *testing.T) {
	cleanup := setupEnvs()
	defer cleanup()

	if _, isPR, err := GetBuildInfo(); isPR {
		t.Errorf("should be non pull-request build. error: %v", err)
	}

	os.Setenv("CI_PULL_REQUEST", "1")
	if _, _, err := GetBuildInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("CI_REPO_OWNER", "haya14busa")
	if _, _, err := GetBuildInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("CI_REPO_NAME", "reviewdog")
	if _, _, err := GetBuildInfo(); err == nil {
		t.Error("error expected but got nil")
	} else {
		t.Log(err)
	}

	os.Setenv("CI_COMMIT", "sha1")
	g, isPR, err := GetBuildInfo()
	if err != nil {
		t.Errorf("unexpected error: %v", err)
	}
	if !isPR {
		t.Error("should be pull request build")
	}
	want := &BuildInfo{
		Owner:       "haya14busa",
		Repo:        "reviewdog",
		PullRequest: 1,
		SHA:         "sha1",
	}
	if !reflect.DeepEqual(g, want) {
		t.Errorf("got: %#v, want: %#v", g, want)
	}
}
<|endoftext|>