<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/ast"
	"go/format"
	"go/parser"
	"go/token"
	"go/types"
	"io/ioutil"
	"log"
	"os"
	"regexp"
	"text/template"
)

var (
	input   = flag.String("input", "", "name of input file containing REST API to generate (if absent then Stdin is used)")
	output  = flag.String("output", "", "name of output file containing generated API request and response implementation")
	pkg     = flag.String("pkg", "", "name of output file package (should be the same as input package)")
	funcMap = template.FuncMap{
		"ParamsList": getParamsList,
		"ParamName":  getParamName,
		"ParamKey":   getParamKey,
		"MethodName": getMethodName,
	}
)

func main() {
	flag.Parse()

	if *output == "" {
		flag.Usage()
		fmt.Fprintln(os.Stderr, "Expects valid output filename")
		os.Exit(1)
	}

	if *pkg == "" {
		flag.Usage()
		fmt.Fprintln(os.Stderr, "Expects valid package name")
		os.Exit(1)
	}

	var file *ast.File
	fileset := token.NewFileSet()

	if *input != "" {
		f, err := parser.ParseFile(fileset, *input, nil, parser.ParseComments)
		if err != nil {
			fmt.Fprintln(os.Stderr, "Failed to parse input filename. Is input filename %s valid?", *input)
			os.Exit(1)
		}
		file = f
	} else {
		f, err := parser.ParseFile(fileset, "", os.Stdin, parser.ParseComments)
		if err != nil {
			fmt.Fprintln(os.Stderr, "Failed to parse input file. Is source input valid?")
			os.Exit(1)
		}
		file = f
	}

	buf, err := generate(file, *pkg)
	if err != nil {
		fmt.Fprintln(os.Stderr, "Failed to generate REST API implementation. %s", err)
		os.Exit(1)
	}

	if err := ioutil.WriteFile(*output, buf, 0644); err != nil {
		log.Fatalf("Failed to write generated source to file %s. Reason: %s", *output, err)
	}

	fmt.Println("Generated source written to file " + *output)
}

func generate(file *ast.File, pkg string) ([]byte, error) {
	info := &types.Info{
		Types: make(map[ast.Expr]types.TypeAndValue),
		Defs:  make(map[*ast.Ident]types.Object),
		Uses:  make(map[*ast.Ident]types.Object),
	}

	visitor := newVisitor(info, pkg)
	ast.Walk(visitor, file)

	return generateRequestResponse(visitor.generateInfo)
}

func generateRequestResponse(r *generateInfo) ([]byte, error) {
	var builderTemplate = template.Must(template.New("builder").Funcs(funcMap).Parse(`/*
* CODE GENERATED AUTOMATICALLY WITH GOREST (github.com/jsaund/gorest)
* THIS FILE SHOULD NOT BE EDITED BY HAND
*/

package {{.Pkg}}

import (
	"net/http"
	"net/url"
	"strings"
)

{{ if .CallbackType }}
type {{ $.CallbackType }} interface {
	OnStart()
	OnError(reason string)
	OnSuccess(response {{ $.ResponseType }})
}
{{ end }}

type {{ .RequestType }}Impl struct {
	baseUrl     string
	pathSubstitutions map[string]string
	queryParams url.Values
}

func New{{ .RequestType }}(baseUrl string) {{ .RequestType }} {
	return &{{ .RequestType }}Impl{
		baseUrl: baseUrl,
		pathSubstitutions: make(map[string]string),
		queryParams: url.Values{},
	}
}

{{ range $key, $value := .PathSubstitutions }}
func (b *{{ $.RequestType }}Impl) {{ $key }}({{ ParamsList $value.Type }}) {{ $.RequestType }} {
	b.pathSubstitutions["{{ ParamKey $value }}"] = {{ ParamName $value.Type true 0 }}
	return b
}
{{ end }}

{{ range $key, $value := .QueryParams }}
func (b *{{ $.RequestType }}Impl) {{ $key }}({{ ParamsList $value.Type }}) {{ $.RequestType }} {
	b.queryParams.Add("{{ $value | ParamKey }}", {{ ParamName $value.Type true 0 }})
	return b
}
{{ end }}

func (b *{{ .RequestType }}Impl) applyPathSubstituions(api string) string {
	if len(b.pathSubstitutions) == 0 {
		return api
	}

	for key, value := range b.pathSubstitutions {
		api = strings.Replace(api, "{" + key + "}", value, -1)
	}

	return api
}

func (b *{{ .RequestType }}Impl) build() (*http.Request, error) {
	req, err := http.NewRequest("{{ .HttpMethod }}", b.baseUrl + b.applyPathSubstituions("{{ .ApiEndpoint }}"), nil)
	if err != nil {
		return nil, err
	}
	if len(b.queryParams) > 0 {
		req.URL.RawQuery = b.queryParams.Encode()
	}
	req.Header.Set("Accept", "application/json")
	return req, nil
}

{{ if and .ResponseType .SyncResponse }}
func (b *{{ $.RequestType }}Impl) {{ $.SyncResponse | MethodName }}() ({{ $.ResponseType }}, error) {
	request, err := b.build()
	if err != nil {
		return nil, err
	}
	request.URL.RawQuery = request.URL.Query().Encode()

	response, err := getClient().Do(request)
	if err != nil {
		return nil, err
	}

	defer response.Body.Close()
	result, err := New{{ $.ResponseType }}(response.Body)
	if err != nil {
		return nil, err
	}
	return result, nil
}
{{ end }}

{{ if and .CallbackType .AsyncResponse }}
func (b *{{ $.RequestType }}Impl) {{ $.AsyncResponse | MethodName }}({{ ParamsList $.AsyncResponse.Type }}) {
	if {{ ParamName $.AsyncResponse.Type false 0 }} != nil {
		{{ ParamName $.AsyncResponse.Type false 0 }}.OnStart()
	}

	go func(b *{{ $.RequestType }}Impl) {
		response, err := b.{{ $.SyncResponse | MethodName }}()

		if {{ ParamName $.AsyncResponse.Type false 0 }} != nil {
			if err != nil {
				{{ ParamName $.AsyncResponse.Type false 0 }}.OnError(err.Error())
			} else {
				{{ ParamName $.AsyncResponse.Type false 0 }}.OnSuccess(response)
			}
		}
	}(b)
}
{{ end }}
`))
	var buf bytes.Buffer
	err := builderTemplate.Execute(&buf, r)
	if err != nil {
		log.Fatalf("Failed to generate template: %v", err)
		return nil, err
	}

	formatted, err := format.Source(buf.Bytes())
	if err != nil {
		log.Fatalf("Failed to generate template: %v", err)
		return nil, err
	}

	return formatted, nil
}

// Return true if os.Stdin appears to be interactive
func isInteractive() bool {
	fileInfo, err := os.Stdin.Stat()
	if err != nil {
		return false
	}
	return fileInfo.Mode()&(os.ModeCharDevice|os.ModeCharDevice) != 0
}

func getMethodName(f *ast.Field) string {
	return f.Names[0].Name
}

func getParamKey(f *ast.Field) string {
	re := regexp.MustCompile(pattern)
	comment := f.Doc.Text()
	queryKey := extractQueryParam(re, comment)
	if queryKey != "" {
		return queryKey
	}

	pathKey := extractPathParam(re, comment)
	if pathKey != "" {
		return pathKey
	}

	log.Fatalf("Must have query or path parameter defined.")
	return ""
}

func getParamName(e ast.Expr, forceString bool, index int) string {
	function, ok := e.(*ast.FuncType)
	if !ok {
		log.Fatalf("Expression must be function type")
		return ""
	}
	p := function.Params
	if len(p.List) == 0 {
		log.Fatalf("Function does not have any parameters")
		return ""
	}

	if index >= len(p.List) {
		log.Fatalf("Illegal parameter index %d. Number of parameters for function is %d", index, len(p.List))
		return ""
	}

	paramName := p.List[index].Names[0].Name
	if forceString {
		paramName = fmt.Sprintf("string(%s)", paramName)
	}
	return paramName
}

func getParamsList(e ast.Expr) string {
	p := e.(*ast.FuncType).Params
	var s string
	for i := 0; i < len(p.List); i++ {
		f := p.List[i]
		s += fmt.Sprintf("%s %s", f.Names[0].Name, getParamType(f.Type))
		if i != len(p.List)-1 {
			s += ","
		}
	}
	return s
}

func getParamType(e ast.Expr) string {
	switch v := e.(type) {
	case *ast.Ident:
		return v.Name
	case *ast.StarExpr:
		return "*" + getParamType(v.X)
	case *ast.SelectorExpr:
		return getParamType(v.X) + "." + getParamType(v.Sel)
	default:
		log.Fatalf("Unrecognized expression type: %v", e)
		return ""
	}
}

const (
	sync             string = "SYNC"
	async            string = "ASYNC"
	path             string = "PATH"
	query            string = "QUERY"
	body             string = "BODY"
	httpMethodGet    string = "GET"
	httpMethodPost   string = "POST"
	httpMethodPut    string = "PUT"
	httpMethodDelete string = "DELETE"
	httpMethodHead   string = "HEAD"

	pattern string = `@(\w+)\(\"(.*)\"\)`
)

type generateInfo struct {
	Pkg               string
	RequestType       string
	ApiEndpoint       string
	HttpMethod        string
	PathSubstitutions map[string]*ast.Field
	QueryParams       map[string]*ast.Field
	SyncResponse      *ast.Field
	AsyncResponse     *ast.Field
	CallbackType      string
	ResponseType      string
}

type astVisitor struct {
	info         *types.Info
	generateInfo *generateInfo
	re           *regexp.Regexp
	buildRequest bool
}

func newVisitor(info *types.Info, pkg string) *astVisitor {
	return &astVisitor{
		info: info,
		generateInfo: &generateInfo{
			Pkg:               pkg,
			PathSubstitutions: make(map[string]*ast.Field),
			QueryParams:       make(map[string]*ast.Field),
		},
		re: regexp.MustCompile(pattern),
	}
}

func (v *astVisitor) Visit(node ast.Node) ast.Visitor {
	if node == nil {
		return v
	}

	switch node.(type) {
	case *ast.File:
		// At the start of a file
		// Reset builder flags
		v.buildRequest = false
		break
	case *ast.TypeSpec:
		// Check if we are at the beginning of a request builder declaration
		// or a response / callback declaration
		// This must be an interface
		typeSpec := node.(*ast.TypeSpec)
		switch typeSpec.Type.(type) {
		case *ast.InterfaceType:
			if v.buildRequest {
				v.generateInfo.RequestType = typeSpec.Name.Name
			} else {
				v.buildRequest = false
			}
			break
		}
		break
	case *ast.InterfaceType:
		if !v.buildRequest {
			// Only interested in parsing methods of the Requeset interface
			// Ignore all other types
			break
		}
		// Retain a mapping of interface methods to their fields which contain
		// the query parameter and argument name and type information to implement
		// the interface
		ifc := node.(*ast.InterfaceType)
		methods := ifc.Methods
		for _, f := range methods.List {
			if qp := extractQueryParam(v.re, f.Doc.List[0].Text); qp != "" {
				v.generateInfo.QueryParams[f.Names[0].Name] = f
			} else if pp := extractPathParam(v.re, f.Doc.List[0].Text); pp != "" {
				v.generateInfo.PathSubstitutions[f.Names[0].Name] = f
			} else if asyncTag := extractAsyncTag(v.re, f.Doc.List[0].Text); asyncTag != "" {
				v.generateInfo.AsyncResponse = f
				v.generateInfo.CallbackType = asyncTag
			} else if syncTag := extractSyncTag(v.re, f.Doc.List[0].Text); syncTag != "" {
				v.generateInfo.SyncResponse = f
				v.generateInfo.ResponseType = syncTag
			}
		}
		break
	case *ast.Comment:
		comment := node.(*ast.Comment)
		if httpMethod, api := extractHttpMethodTag(v.re, comment.Text); httpMethod != "" {
			// Extract the HTTP Method and API from the Interface declaration
			v.buildRequest = true
			v.generateInfo.HttpMethod = httpMethod
			v.generateInfo.ApiEndpoint = api
		}
		break
	}

	return v
}

func extractSyncTag(re *regexp.Regexp, s string) string {
	match := re.FindStringSubmatch(s)
	if len(match) == 3 && isAnnotationSync(match[1]) {
		return match[2]
	}
	return ""
}

func extractAsyncTag(re *regexp.Regexp, s string) string {
	match := re.FindStringSubmatch(s)
	if len(match) == 3 && isAnnotationAsync(match[1]) {
		return match[2]
	}
	return ""
}

func extractHttpMethodTag(re *regexp.Regexp, s string) (string, string) {
	match := re.FindStringSubmatch(s)
	if len(match) == 3 && isAnnotationHttpMethod(match[1]) {
		return match[1], match[2]
	}
	return "", ""
}

func isAnnotationAsync(s string) bool {
	return s == async
}

func isAnnotationSync(s string) bool {
	return s == sync
}

func isAnnotationHttpMethod(s string) bool {
	return s == httpMethodGet || s == httpMethodPost || s == httpMethodPut || s == httpMethodHead || s == httpMethodDelete
}

func extractQueryParam(re *regexp.Regexp, s string) string {
	match := re.FindStringSubmatch(s)
	if len(match) == 3 && match[1] == query {
		return match[2]
	}
	return ""
}

func extractPathParam(re *regexp.Regexp, s string) string {
	match := re.FindStringSubmatch(s)
	if len(match) == 3 && match[1] == path {
		return match[2]
	}
	return ""
}
<commit_msg>Add support for POST with Form Encoded Data<commit_after>package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/ast"
	"go/format"
	"go/parser"
	"go/token"
	"go/types"
	"io/ioutil"
	"log"
	"os"
	"regexp"
	"text/template"
)

var (
	input   = flag.String("input", "", "name of input file containing REST API to generate (if absent then Stdin is used)")
	output  = flag.String("output", "", "name of output file containing generated API request and response implementation")
	pkg     = flag.String("pkg", "", "name of output file package (should be the same as input package)")
	funcMap = template.FuncMap{
		"ParamsList": getParamsList,
		"ParamName":  getParamName,
		"ParamKey":   getParamKey,
		"MethodName": getMethodName,
	}
)

func main() {
	flag.Parse()

	if *output == "" {
		flag.Usage()
		fmt.Fprintln(os.Stderr, "Expects valid output filename")
		os.Exit(1)
	}

	if *pkg == "" {
		flag.Usage()
		fmt.Fprintln(os.Stderr, "Expects valid package name")
		os.Exit(1)
	}

	var file *ast.File
	fileset := token.NewFileSet()

	if *input != "" {
		f, err := parser.ParseFile(fileset, *input, nil, parser.ParseComments)
		if err != nil {
			fmt.Fprintln(os.Stderr, "Failed to parse input filename. Is input filename %s valid?", *input)
			os.Exit(1)
		}
		file = f
	} else {
		f, err := parser.ParseFile(fileset, "", os.Stdin, parser.ParseComments)
		if err != nil {
			fmt.Fprintln(os.Stderr, "Failed to parse input file. Is source input valid?")
			os.Exit(1)
		}
		file = f
	}

	buf, err := generate(file, *pkg)
	if err != nil {
		fmt.Fprintln(os.Stderr, "Failed to generate REST API implementation. %s", err)
		os.Exit(1)
	}

	if err := ioutil.WriteFile(*output, buf, 0644); err != nil {
		log.Fatalf("Failed to write generated source to file %s. Reason: %s", *output, err)
	}

	fmt.Println("Generated source written to file " + *output)
}

func generate(file *ast.File, pkg string) ([]byte, error) {
	info := &types.Info{
		Types: make(map[ast.Expr]types.TypeAndValue),
		Defs:  make(map[*ast.Ident]types.Object),
		Uses:  make(map[*ast.Ident]types.Object),
	}

	visitor := newVisitor(info, pkg)
	ast.Walk(visitor, file)

	return generateRequestResponse(visitor.generateInfo)
}

func generateRequestResponse(r *generateInfo) ([]byte, error) {
	var builderTemplate = template.Must(template.New("builder").Funcs(funcMap).Parse(`/*
* CODE GENERATED AUTOMATICALLY WITH GOREST (github.com/jsaund/gorest)
* THIS FILE SHOULD NOT BE EDITED BY HAND
*/

package {{.Pkg}}

import (
	"net/http"
	"net/url"
	"strings"
)

{{ if .CallbackType }}
type {{ $.CallbackType }} interface {
	OnStart()
	OnError(reason string)
	OnSuccess(response {{ $.ResponseType }})
}
{{ end }}

type {{ .RequestType }}Impl struct {
	baseUrl     string
	pathSubstitutions map[string]string
	queryParams url.Values
	postFormParams url.Values
}

func New{{ .RequestType }}(baseUrl string) {{ .RequestType }} {
	return &{{ .RequestType }}Impl{
		baseUrl: baseUrl,
		pathSubstitutions: make(map[string]string),
		queryParams: url.Values{},
		postFormParams: url.Values{},
	}
}

{{ range $key, $value := .PathSubstitutions }}
func (b *{{ $.RequestType }}Impl) {{ $key }}({{ ParamsList $value.Type }}) {{ $.RequestType }} {
	b.pathSubstitutions["{{ ParamKey $value }}"] = {{ ParamName $value.Type true 0 }}
	return b
}
{{ end }}

{{ range $key, $value := .QueryParams }}
func (b *{{ $.RequestType }}Impl) {{ $key }}({{ ParamsList $value.Type }}) {{ $.RequestType }} {
	b.queryParams.Add("{{ $value | ParamKey }}", {{ ParamName $value.Type true 0 }})
	return b
}
{{ end }}

{{ range $key, $value := .PostFormParams }}
func (b *{{ $.RequestType }}Impl) {{ $key }}({{ ParamsList $value.Type }}) {{ $.RequestType }} {
	b.postFormParams.Add("{{ $value | ParamKey }}", {{ ParamName $value.Type true 0 }})
	return b
}
{{ end }}

func (b *{{ .RequestType }}Impl) applyPathSubstituions(api string) string {
	if len(b.pathSubstitutions) == 0 {
		return api
	}

	for key, value := range b.pathSubstitutions {
		api = strings.Replace(api, "{" + key + "}", value, -1)
	}

	return api
}

func (b *{{ .RequestType }}Impl) build() (*http.Request, error) {
	req, err := http.NewRequest("{{ .HttpMethod }}", b.baseUrl + b.applyPathSubstituions("{{ .ApiEndpoint }}"), nil)
	if err != nil {
		return nil, err
	}
	if len(b.queryParams) > 0 {
		req.URL.RawQuery = b.queryParams.Encode()
	}
	if len(b.postFormParams) > 0 {
		req.URL.RawQuery = b.postFormParams.Encode()
	}
	req.Header.Set("Accept", "application/json")
	return req, nil
}

{{ if and .ResponseType .SyncResponse }}
func (b *{{ $.RequestType }}Impl) {{ $.SyncResponse | MethodName }}() ({{ $.ResponseType }}, error) {
	request, err := b.build()
	if err != nil {
		return nil, err
	}
	request.URL.RawQuery = request.URL.Query().Encode()

	response, err := getClient().Do(request)
	if err != nil {
		return nil, err
	}

	defer response.Body.Close()
	result, err := New{{ $.ResponseType }}(response.Body)
	if err != nil {
		return nil, err
	}
	return result, nil
}
{{ end }}

{{ if and .CallbackType .AsyncResponse }}
func (b *{{ $.RequestType }}Impl) {{ $.AsyncResponse | MethodName }}({{ ParamsList $.AsyncResponse.Type }}) {
	if {{ ParamName $.AsyncResponse.Type false 0 }} != nil {
		{{ ParamName $.AsyncResponse.Type false 0 }}.OnStart()
	}

	go func(b *{{ $.RequestType }}Impl) {
		response, err := b.{{ $.SyncResponse | MethodName }}()

		if {{ ParamName $.AsyncResponse.Type false 0 }} != nil {
			if err != nil {
				{{ ParamName $.AsyncResponse.Type false 0 }}.OnError(err.Error())
			} else {
				{{ ParamName $.AsyncResponse.Type false 0 }}.OnSuccess(response)
			}
		}
	}(b)
}
{{ end }}
`))
	var buf bytes.Buffer
	err := builderTemplate.Execute(&buf, r)
	if err != nil {
		log.Fatalf("Failed to generate template: %v", err)
		return nil, err
	}

	formatted, err := format.Source(buf.Bytes())
	if err != nil {
		log.Fatalf("Failed to generate template: %v", err)
		return nil, err
	}

	return formatted, nil
}

// Return true if os.Stdin appears to be interactive
func isInteractive() bool {
	fileInfo, err := os.Stdin.Stat()
	if err != nil {
		return false
	}
	return fileInfo.Mode()&(os.ModeCharDevice|os.ModeCharDevice) != 0
}

func getMethodName(f *ast.Field) string {
	return f.Names[0].Name
}

func getParamKey(f *ast.Field) string {
	re := regexp.MustCompile(pattern)
	comment := f.Doc.Text()
	queryKey := extractQueryParam(re, comment)
	if queryKey != "" {
		return queryKey
	}

	fieldKey := extractPostFormParam(re, comment)
	if fieldKey != "" {
		return fieldKey
	}

	pathKey := extractPathParam(re, comment)
	if pathKey != "" {
		return pathKey
	}

	log.Fatalf("Must have query or path or field parameter defined.")
	return ""
}

func getParamName(e ast.Expr, forceString bool, index int) string {
	function, ok := e.(*ast.FuncType)
	if !ok {
		log.Fatalf("Expression must be function type")
		return ""
	}
	p := function.Params
	if len(p.List) == 0 {
		log.Fatalf("Function does not have any parameters")
		return ""
	}

	if index >= len(p.List) {
		log.Fatalf("Illegal parameter index %d. Number of parameters for function is %d", index, len(p.List))
		return ""
	}

	paramName := p.List[index].Names[0].Name
	if forceString {
		paramName = fmt.Sprintf("string(%s)", paramName)
	}
	return paramName
}

func getParamsList(e ast.Expr) string {
	p := e.(*ast.FuncType).Params
	var s string
	for i := 0; i < len(p.List); i++ {
		f := p.List[i]
		s += fmt.Sprintf("%s %s", f.Names[0].Name, getParamType(f.Type))
		if i != len(p.List)-1 {
			s += ","
		}
	}
	return s
}

func getParamType(e ast.Expr) string {
	switch v := e.(type) {
	case *ast.Ident:
		return v.Name
	case *ast.StarExpr:
		return "*" + getParamType(v.X)
	case *ast.SelectorExpr:
		return getParamType(v.X) + "." + getParamType(v.Sel)
	default:
		log.Fatalf("Unrecognized expression type: %v", e)
		return ""
	}
}

const (
	sync               string = "SYNC"
	async              string = "ASYNC"
	path               string = "PATH"
	query              string = "QUERY"
	field              string = "FIELD"
	httpMethodGet      string = "GET"
	httpMethodPost     string = "POST"
	httpMethodPostForm string = "POST_FORM"
	httpMethodPut      string = "PUT"
	httpMethodDelete   string = "DELETE"
	httpMethodHead     string = "HEAD"

	pattern string = `@(\w+)\(\"(.*)\"\)`
)

type generateInfo struct {
	Pkg               string
	RequestType       string
	ApiEndpoint       string
	HttpMethod        string
	PathSubstitutions map[string]*ast.Field
	QueryParams       map[string]*ast.Field
	PostFormParams    map[string]*ast.Field
	SyncResponse      *ast.Field
	AsyncResponse     *ast.Field
	CallbackType      string
	ResponseType      string
}

type astVisitor struct {
	info         *types.Info
	generateInfo *generateInfo
	re           *regexp.Regexp
	buildRequest bool
}

func newVisitor(info *types.Info, pkg string) *astVisitor {
	return &astVisitor{
		info: info,
		generateInfo: &generateInfo{
			Pkg:               pkg,
			PathSubstitutions: make(map[string]*ast.Field),
			QueryParams:       make(map[string]*ast.Field),
			PostFormParams:    make(map[string]*ast.Field),
		},
		re: regexp.MustCompile(pattern),
	}
}

func (v *astVisitor) Visit(node ast.Node) ast.Visitor {
	if node == nil {
		return v
	}

	switch node.(type) {
	case *ast.File:
		// At the start of a file
		// Reset builder flags
		v.buildRequest = false
		break
	case *ast.TypeSpec:
		// Check if we are at the beginning of a request builder declaration
		// or a response / callback declaration
		// This must be an interface
		typeSpec := node.(*ast.TypeSpec)
		switch typeSpec.Type.(type) {
		case *ast.InterfaceType:
			if v.buildRequest {
				v.generateInfo.RequestType = typeSpec.Name.Name
			} else {
				v.buildRequest = false
			}
			break
		}
		break
	case *ast.InterfaceType:
		if !v.buildRequest {
			// Only interested in parsing methods of the Requeset interface
			// Ignore all other types
			break
		}
		// Retain a mapping of interface methods to their fields which contain
		// the query parameter and argument name and type information to implement
		// the interface
		ifc := node.(*ast.InterfaceType)
		methods := ifc.Methods
		for _, f := range methods.List {
			if qp := extractQueryParam(v.re, f.Doc.List[0].Text); qp != "" {
				v.generateInfo.QueryParams[f.Names[0].Name] = f
			} else if pfp := extractPostFormParam(v.re, f.Doc.List[0].Text); pfp != "" {
				v.generateInfo.PostFormParams[f.Names[0].Name] = f
			} else if pp := extractPathParam(v.re, f.Doc.List[0].Text); pp != "" {
				v.generateInfo.PathSubstitutions[f.Names[0].Name] = f
			} else if asyncTag := extractAsyncTag(v.re, f.Doc.List[0].Text); asyncTag != "" {
				v.generateInfo.AsyncResponse = f
				v.generateInfo.CallbackType = asyncTag
			} else if syncTag := extractSyncTag(v.re, f.Doc.List[0].Text); syncTag != "" {
				v.generateInfo.SyncResponse = f
				v.generateInfo.ResponseType = syncTag
			}
		}
		break
	case *ast.Comment:
		comment := node.(*ast.Comment)
		if httpMethod, api := extractHttpMethodTag(v.re, comment.Text); httpMethod != "" {
			// Extract the HTTP Method and API from the Interface declaration
			v.buildRequest = true
			v.generateInfo.HttpMethod = httpMethod
			v.generateInfo.ApiEndpoint = api
		}
		break
	}

	return v
}

func extractSyncTag(re *regexp.Regexp, s string) string {
	match := re.FindStringSubmatch(s)
	if len(match) == 3 && isAnnotationSync(match[1]) {
		return match[2]
	}
	return ""
}

func extractAsyncTag(re *regexp.Regexp, s string) string {
	match := re.FindStringSubmatch(s)
	if len(match) == 3 && isAnnotationAsync(match[1]) {
		return match[2]
	}
	return ""
}

func extractHttpMethodTag(re *regexp.Regexp, s string) (string, string) {
	match := re.FindStringSubmatch(s)
	if len(match) == 3 && isAnnotationHttpMethod(match[1]) {
		method := match[1]
		api := match[2]
		if method == httpMethodPostForm {
			method = httpMethodPost
		}
		return method, api
	}
	return "", ""
}

func isAnnotationAsync(s string) bool {
	return s == async
}

func isAnnotationSync(s string) bool {
	return s == sync
}

func isAnnotationHttpMethod(s string) bool {
	return s == httpMethodGet || s == httpMethodPost || s == httpMethodPostForm || s == httpMethodPut || s == httpMethodHead || s == httpMethodDelete
}

func extractQueryParam(re *regexp.Regexp, s string) string {
	match := re.FindStringSubmatch(s)
	if len(match) == 3 && match[1] == query {
		return match[2]
	}
	return ""
}

func extractPostFormParam(re *regexp.Regexp, s string) string {
	match := re.FindStringSubmatch(s)
	if len(match) == 3 && match[1] == field {
		return match[2]
	}
	return ""
}

func extractPathParam(re *regexp.Regexp, s string) string {
	match := re.FindStringSubmatch(s)
	if len(match) == 3 && match[1] == path {
		return match[2]
	}
	return ""
}
<|endoftext|>


<commit_before>package main

import  (
        "fmt"
        "../go-todotxt"
        "github.com/docopt/docopt.go"
)

func main() {

        usage := `Go Todo.txt

Usage:
    gotodo [--sort=<prio|date>]
    gotodo list [--sort=<prio|date>]
    gotodo add <task>
    gotodo (finish|done) <id>
    gotodo --num-tasks
    gotodo -h | --help
    gotodo -v | --version

Options:
    -h --help     Show this screen.
    -v --version  Show version.
    --num-tasks   Show number of tasks.`

        arguments, _ := docopt.Parse(usage, nil, true, "Go Todo.txt 0.1", false)

        //fmt.Println(arguments)

        tasks := todotxt.LoadTaskList("todo.txt")

        if (arguments["--num-tasks"].(bool)) {
                fmt.Println(tasks.Len())
        } else {

                if arguments["--sort"] == nil {
                        tasks.Sort()
                } else {
                        switch arguments["--sort"].(string) {
                        case "prio":
                                tasks.Sort()
                        case "date":
                                tasks.SortByCreateDate()
                        case "len":
                                tasks.SortByLength()
                        }
                }

                for i, task := range tasks {
                        fmt.Println(i, task.RawText())
                }
        }

        //fmt.Println(tasks)
        //fmt.Println(tasks.Count())
}
<commit_msg>extended help & default case for switch<commit_after>package main

import  (
        "fmt"
        "../go-todotxt"
        "github.com/docopt/docopt.go"
)

func main() {

        usage := `Go Todo.txt

Usage:
    gotodo [--sort=<prio|date|len>]
    gotodo list [--sort=<prio|date|len>]
    gotodo add <task>
    gotodo (finish|done) <id>
    gotodo --num-tasks
    gotodo -h | --help
    gotodo -v | --version

Options:
    -h --help     Show this screen.
    -v --version  Show version.
    --num-tasks   Show number of tasks.`

        arguments, _ := docopt.Parse(usage, nil, true, "Go Todo.txt 0.1", false)

        //fmt.Println(arguments)

        tasks := todotxt.LoadTaskList("todo.txt")

        if (arguments["--num-tasks"].(bool)) {
                fmt.Println(tasks.Len())
        } else {

                if arguments["--sort"] == nil {
                        tasks.Sort()
                } else {
                        switch arguments["--sort"].(string) {
                        case "prio":
                                tasks.Sort()
                        case "date":
                                tasks.SortByCreateDate()
                        case "len":
                                tasks.SortByLength()
                        default:
                                tasks.Sort()
                        }
                }

                for i, task := range tasks {
                        fmt.Println(i, task.RawText())
                }
        }

        //fmt.Println(tasks)
        //fmt.Println(tasks.Count())
}
<|endoftext|>


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

// Internet protocol family sockets

package ipsupport

var (
	// supportsIPv4 reports whether the platform supports IPv4
	// networking functionality.
	supportsIPv4 bool

	// supportsIPv6 reports whether the platform supports IPv6
	// networking functionality.
	supportsIPv6 bool

	// supportsIPv4map reports whether the platform supports
	// mapping an IPv4 address inside an IPv6 address at transport
	// layer protocols.  See RFC 4291, RFC 4038 and RFC 3493.
	supportsIPv4map bool
)

func init() {
	supportsIPv4 = probeIPv4Stack()
	supportsIPv6, supportsIPv4map = probeIPv6Stack()
}

// V4 reports whether the platform supports IPv4
// networking functionality.
func V4() bool {
	return supportsIPv4
}

// V6 reports whether the platform supports IPv6
// networking functionality.
func V6() bool {
	return supportsIPv6
}

// V4Map reports whether the platform supports mapping an IPv4
// address inside an IPv6 address at transport layer protocols.
// See RFC 4291, RFC 4038 and RFC 3493.
func V4Map() bool {
	return supportsIPv4map
}
<commit_msg>package doc<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Internet protocol family sockets

// Package ipsupport exposes features of the platform's networking
// functionality using code extraced from the standard library's
// net package.
package ipsupport

var (
	// supportsIPv4 reports whether the platform supports IPv4
	// networking functionality.
	supportsIPv4 bool

	// supportsIPv6 reports whether the platform supports IPv6
	// networking functionality.
	supportsIPv6 bool

	// supportsIPv4map reports whether the platform supports
	// mapping an IPv4 address inside an IPv6 address at transport
	// layer protocols.  See RFC 4291, RFC 4038 and RFC 3493.
	supportsIPv4map bool
)

func init() {
	supportsIPv4 = probeIPv4Stack()
	supportsIPv6, supportsIPv4map = probeIPv6Stack()
}

// V4 reports whether the platform supports IPv4
// networking functionality.
func V4() bool {
	return supportsIPv4
}

// V6 reports whether the platform supports IPv6
// networking functionality.
func V6() bool {
	return supportsIPv6
}

// V4Map reports whether the platform supports mapping an IPv4
// address inside an IPv6 address at transport layer protocols.
// See RFC 4291, RFC 4038 and RFC 3493.
func V4Map() bool {
	return supportsIPv4map
}
<|endoftext|>


<commit_before>package canopus

import (
	"bytes"
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestMessage(t *testing.T) {
	assert.NotNil(t, NewMessage(TYPE_CONFIRMABLE, GET, 12345))
	assert.NotNil(t, NewEmptyMessage(12345))
}

func TestInvalidMessage(t *testing.T) {
	_, err := BytesToMessage(make([]byte, 0))
	if err == nil {
		t.Error("Message should be invalid")
	}

	_, err = BytesToMessage(make([]byte, 4))
	if err == nil {
		t.Error("Message should be invalid")
	}
}

func TestMessagValidation(t *testing.T) {
	// ValidateMessage()
}

func TestMessageConversion(t *testing.T) {

	msg := NewBasicConfirmableMessage()
	// Byte 1
	msg.AddOption(OPTION_CONTENT_FORMAT, MEDIATYPE_APPLICATION_LINK_FORMAT)

	// Convert Message to BYte
	b, err := MessageToBytes(msg)

	// Reconvert Back Bytes to Message
	newMsg, err := BytesToMessage(b)
	if err != nil {
		t.Error("An error occured converting bytes to message: ", err)
	}

	if newMsg.MessageType != TYPE_CONFIRMABLE {
		t.Error("Type not the same after reconversion")
	}

	if bytes.NewBuffer(newMsg.Token).String() != "abcd1234" {
		t.Error("Token String not the same after reconversion")
	}

	if newMsg.MessageId != 0xf0f0 {
		t.Error("Message ID not the same after reconversion")
	}

	if len(newMsg.Options) == 0 {
		t.Error("Options should not be 0")
	}
}

func TestMessageBadOptions(t *testing.T) {
	testMsg := NewBasicConfirmableMessage()

	// Unknown Critical Option
	unk := OptionCode(99)
	testMsg.AddOption(unk, 0)
	testMsg.AddOption(OPTION_CONTENT_FORMAT, MEDIATYPE_APPLICATION_LINK_FORMAT)

	_, err := MessageToBytes(testMsg)
	if err == nil {
		if err != ERR_UNKNOWN_CRITICAL_OPTION {
			t.Error("Should throw ERR_UNKNOWN_CRITICAL_OPTION")
		}
	}
}

func TestMessageObject(t *testing.T) {
	msg := &Message{}

	if len(msg.Options) > 0 {
		t.Error("Options expected = 0")
	}

	msg.AddOptions(NewPathOptions("/example"))
	msg.AddOption(OPTION_ACCEPT, MEDIATYPE_APPLICATION_XML)
	msg.AddOption(OPTION_CONTENT_FORMAT, MEDIATYPE_APPLICATION_JSON)
	if len(msg.Options) != 4 {
		t.Error("Options expected == 4")
	}

	opt := msg.GetOption(OPTION_ACCEPT)
	if opt == nil {
		t.Error("Expect ACCEPT option")
	}

	msg.RemoveOptions(OPTION_URI_PATH)
	if len(msg.Options) > 2 {
		t.Error("Options expected = 0")
	}
}

func NewBasicConfirmableMessage() *Message {
	msg := NewMessageOfType(TYPE_CONFIRMABLE, 0xf0f0)
	msg.Code = GET
	msg.Token = []byte("abcd1234")

	return msg
}
<commit_msg>test fixes<commit_after>package canopus

import (
	"bytes"
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestMessage(t *testing.T) {
	assert.NotNil(t, NewMessage(TYPE_CONFIRMABLE, GET, 12345))
	assert.NotNil(t, NewEmptyMessage(12345))
}

func TestInvalidMessage(t *testing.T) {
	_, err := BytesToMessage(make([]byte, 0))

	assert.NotNil(t, err, "Message should be invalid")

	_, err = BytesToMessage(make([]byte, 4))
	assert.NotNil(t, err, "Message should be invalid")
}

func TestMessagValidation(t *testing.T) {
	// ValidateMessage()
}

func TestMessageConversion(t *testing.T) {

	msg := NewBasicConfirmableMessage()
	// Byte 1
	msg.AddOption(OPTION_CONTENT_FORMAT, MEDIATYPE_APPLICATION_LINK_FORMAT)

	// Convert Message to BYte
	b, err := MessageToBytes(msg)

	// Reconvert Back Bytes to Message
	newMsg, err := BytesToMessage(b)
	assert.Nil(t, err, "An error occured converting bytes to message")

	assert.Equal(t, 0, int(newMsg.MessageType))	// 0x0: Type Confirmable
	assert.Equal(t, "abcd1234", bytes.NewBuffer(newMsg.Token).String(), "Token String not the same after reconversion")
	assert.Equal(t, 61680, int(newMsg.MessageId), "Message ID not the same after reconversion")
	assert.NotEqual(t, 0, len(newMsg.Options), "Options should not be 0")
}

func TestMessageBadOptions(t *testing.T) {
	testMsg := NewBasicConfirmableMessage()

	// Unknown Critical Option
	unk := OptionCode(99)
	testMsg.AddOption(unk, 0)
	testMsg.AddOption(OPTION_CONTENT_FORMAT, MEDIATYPE_APPLICATION_LINK_FORMAT)
	_, err := MessageToBytes(testMsg)
	assert.NotNil(t, err, "Should throw ERR_UNKNOWN_CRITICAL_OPTION")
}

func TestMessageObject(t *testing.T) {
	msg := &Message{}

	assert.Equal(t, 0, len(msg.Options))
	msg.AddOptions(NewPathOptions("/example"))
	msg.AddOption(OPTION_ACCEPT, MEDIATYPE_APPLICATION_XML)
	msg.AddOption(OPTION_CONTENT_FORMAT, MEDIATYPE_APPLICATION_JSON)
	assert.Equal(t, 3, len(msg.Options))

	opt := msg.GetOption(OPTION_ACCEPT)
	assert.NotNil(t, opt)

	msg.RemoveOptions(OPTION_URI_PATH)
	assert.Equal(t, 2, len(msg.Options))
}

func NewBasicConfirmableMessage() *Message {
	msg := NewMessageOfType(TYPE_CONFIRMABLE, 0xf0f0)
	msg.Code = GET
	msg.Token = []byte("abcd1234")

	return msg
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Smudge Authors.

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

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

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

package smudge

import (
	"net"
	"reflect"
	"testing"
)

// Identical but distinct instance from node1b
var node1a = Node{
	ip:          net.IP([]byte{127, 0, 0, 1}),
	port:        1234,
	timestamp:   87878787,
	status:      StatusAlive,
	emitCounter: 42}

// Identical but distinct instance from node1a
var node1b = Node{
	ip:          net.IP([]byte{127, 0, 0, 1}),
	port:        1234,
	timestamp:   87878787,
	status:      StatusAlive,
	emitCounter: 42}

// Different from node1a and node1b
var node2 = Node{
	ip:          net.IP([]byte{127, 0, 0, 1}),
	port:        10001,
	timestamp:   GetNowInMillis(),
	status:      StatusAlive,
	emitCounter: 42}

var message1a = message{
	sender:          &node1a,
	senderHeartbeat: 255,
	verb:            verbPing}

var message1b = message{
	sender:          &node1b,
	senderHeartbeat: 255,
	verb:            verbPing}

var message2 = message{
	sender:          &node2,
	senderHeartbeat: 23,
	verb:            verbAck}

// Does deep equality of two different but identical messages return true?
func TestDeepEqualityTrue(t *testing.T) {
	if !reflect.DeepEqual(message1a, message1b) {
		t.Fail()
	}
}

// Does deep equality of two different messages return false?
func TestDeepEqualityFalse(t *testing.T) {
	if reflect.DeepEqual(message1a, message2) {
		t.Fail()
	}
}

// Endode and decode a simple message without any members, and see if
// the input/output match.
func TestEncodeDecodeBasic(t *testing.T) {
	timestamp := uint32(87878787)

	sender := Node{
		ip:        net.IP([]byte{127, 0, 0, 1}),
		port:      1234,
		timestamp: timestamp}

	message := message{
		sender:          &sender,
		senderHeartbeat: 255,
		verb:            verbPing}

	ip := net.IP([]byte{127, 0, 0, 1})
	bytes := message.encode()
	decoded, err := decodeMessage(ip, bytes)
	decoded.sender.timestamp = timestamp

	if err != nil {
		t.Error(err)
	}

	if !reflect.DeepEqual(message, decoded) {
		t.Error("Messages do not match:")

		t.Log(" Input:", message)
		t.Log("Output:", decoded)
		t.Log(" Input node:", message.sender)
		t.Log("Output node:", decoded.sender)
	}
}

// Endode and decode a simple message without one member, and see if
// the input/output match.
func TestEncodeDecode1Member(t *testing.T) {
	timestamp := uint32(87878787)

	sender := Node{
		ip:        net.IP([]byte{127, 0, 0, 1}),
		port:      1234,
		timestamp: timestamp}

	member := Node{
		ip:        net.IP([]byte{127, 0, 0, 2}),
		port:      9000,
		timestamp: timestamp}

	message := message{
		sender:          &sender,
		senderHeartbeat: 255,
		verb:            verbPing}
	message.addMember(&member, StatusDead, 38)

	if len(message.members) != 1 {
		t.Error("No member in the input members list!")
	}

	ip := net.IP([]byte{127, 0, 0, 1})
	bytes := message.encode()
	decoded, err := decodeMessage(ip, bytes)
	decoded.sender.timestamp = timestamp
	decoded.members[0].node.timestamp = timestamp

	if err != nil {
		t.Error(err)
	}

	if len(decoded.members) != 1 {
		t.Error("No member in the output members list!")
	}

	if !reflect.DeepEqual(message, decoded) {
		t.Error("Messages do not match")

		t.Log(" Input:", message.members[0])
		t.Log("Output:", decoded.members[0])
		t.Log(" Input node:", message.members[0].node)
		t.Log("Output node:", decoded.members[0].node)
	}
}

// Endode and decode a simple message without one member, and see if
// the input/output match.
func TestEncodeDecode1MemberBroadcast(t *testing.T) {
	timestamp := uint32(87878787)

	sender := Node{
		ip:        net.IP([]byte{127, 0, 0, 1}),
		port:      1234,
		timestamp: timestamp}

	member := Node{
		ip:        net.IP([]byte{127, 0, 0, 2}),
		port:      9000,
		timestamp: timestamp}

	message := message{
		sender:          &sender,
		senderHeartbeat: 255,
		verb:            verbPing}
	message.addMember(&member, StatusDead, 38)

	broadcast := Broadcast{
		bytes:  []byte("This is a message"),
		origin: &sender,
		index:  42}
	message.addBroadcast(&broadcast)

	if message.broadcast == nil {
		t.Error("Broadcast not set properly")
	}

	ip := net.IP([]byte{127, 0, 0, 1})
	bytes := message.encode()
	decoded, err := decodeMessage(ip, bytes)
	decoded.sender.timestamp = timestamp
	decoded.members[0].node.timestamp = timestamp

	if err != nil {
		t.Error(err)
	}

	if decoded.broadcast == nil {
		t.Error("Broadcast not decoded")
	}

	message.broadcast.origin = nil
	decoded.broadcast.origin = nil

	if !reflect.DeepEqual(message.broadcast, decoded.broadcast) {
		t.Error("Broadcasts do not match:")
		t.Error(" Input bcast:", message.broadcast)
		t.Error("Output bcast:", decoded.broadcast)
	}
}
<commit_msg>Fix test<commit_after>/*
Copyright 2016 The Smudge Authors.

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

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

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

package smudge

import (
	"net"
	"reflect"
	"testing"
)

// Identical but distinct instance from node1b
var node1a = Node{
	ip:          net.IP([]byte{127, 0, 0, 1}),
	port:        1234,
	timestamp:   87878787,
	status:      StatusAlive,
	emitCounter: 42,
	pingMillis:  PingNoData}

// Identical but distinct instance from node1a
var node1b = Node{
	ip:          net.IP([]byte{127, 0, 0, 1}),
	port:        1234,
	timestamp:   87878787,
	status:      StatusAlive,
	emitCounter: 42,
	pingMillis:  PingNoData}

// Different from node1a and node1b
var node2 = Node{
	ip:          net.IP([]byte{127, 0, 0, 1}),
	port:        10001,
	timestamp:   GetNowInMillis(),
	status:      StatusAlive,
	emitCounter: 42,
	pingMillis:  PingNoData}

var message1a = message{
	sender:          &node1a,
	senderHeartbeat: 255,
	verb:            verbPing}

var message1b = message{
	sender:          &node1b,
	senderHeartbeat: 255,
	verb:            verbPing}

var message2 = message{
	sender:          &node2,
	senderHeartbeat: 23,
	verb:            verbAck}

// Does deep equality of two different but identical messages return true?
func TestDeepEqualityTrue(t *testing.T) {
	if !reflect.DeepEqual(message1a, message1b) {
		t.Fail()
	}
}

// Does deep equality of two different messages return false?
func TestDeepEqualityFalse(t *testing.T) {
	if reflect.DeepEqual(message1a, message2) {
		t.Fail()
	}
}

// Endode and decode a simple message without any members, and see if
// the input/output match.
func TestEncodeDecodeBasic(t *testing.T) {
	timestamp := uint32(87878787)

	sender := Node{
		ip:         net.IP([]byte{127, 0, 0, 1}),
		port:       1234,
		timestamp:  timestamp,
		pingMillis: PingNoData,
	}

	message := message{
		sender:          &sender,
		senderHeartbeat: 255,
		verb:            verbPing}

	ip := net.IP([]byte{127, 0, 0, 1})
	bytes := message.encode()
	decoded, err := decodeMessage(ip, bytes)
	decoded.sender.timestamp = timestamp

	if err != nil {
		t.Error(err)
	}

	if !reflect.DeepEqual(message, decoded) {
		t.Error("Messages do not match:")

		t.Log(" Input:", message)
		t.Log("Output:", decoded)
		t.Log(" Input node:", message.sender)
		t.Log("Output node:", decoded.sender)
	}
}

// Endode and decode a simple message without one member, and see if
// the input/output match.
func TestEncodeDecode1Member(t *testing.T) {
	timestamp := uint32(87878787)

	sender := Node{
		ip:         net.IP([]byte{127, 0, 0, 1}),
		port:       1234,
		timestamp:  timestamp,
		pingMillis: PingNoData,
	}

	member := Node{
		ip:         net.IP([]byte{127, 0, 0, 2}),
		port:       9000,
		timestamp:  timestamp,
		pingMillis: PingNoData,
	}

	message := message{
		sender:          &sender,
		senderHeartbeat: 255,
		verb:            verbPing}
	message.addMember(&member, StatusDead, 38)

	if len(message.members) != 1 {
		t.Error("No member in the input members list!")
	}

	ip := net.IP([]byte{127, 0, 0, 1})
	bytes := message.encode()
	decoded, err := decodeMessage(ip, bytes)
	decoded.sender.timestamp = timestamp
	decoded.members[0].node.timestamp = timestamp

	if err != nil {
		t.Error(err)
	}

	if len(decoded.members) != 1 {
		t.Error("No member in the output members list!")
	}

	if !reflect.DeepEqual(message, decoded) {
		t.Error("Messages do not match")

		t.Log(" Input:", message.members[0])
		t.Log("Output:", decoded.members[0])
		t.Log(" Input node:", message.members[0].node)
		t.Log("Output node:", decoded.members[0].node)
	}
}

// Endode and decode a simple message without one member, and see if
// the input/output match.
func TestEncodeDecode1MemberBroadcast(t *testing.T) {
	timestamp := uint32(87878787)

	sender := Node{
		ip:         net.IP([]byte{127, 0, 0, 1}),
		port:       1234,
		timestamp:  timestamp,
		pingMillis: PingNoData}

	member := Node{
		ip:         net.IP([]byte{127, 0, 0, 2}),
		port:       9000,
		timestamp:  timestamp,
		pingMillis: PingNoData}

	message := message{
		sender:          &sender,
		senderHeartbeat: 255,
		verb:            verbPing}
	message.addMember(&member, StatusDead, 38)

	broadcast := Broadcast{
		bytes:  []byte("This is a message"),
		origin: &sender,
		index:  42}
	message.addBroadcast(&broadcast)

	if message.broadcast == nil {
		t.Error("Broadcast not set properly")
	}

	ip := net.IP([]byte{127, 0, 0, 1})
	bytes := message.encode()
	decoded, err := decodeMessage(ip, bytes)
	decoded.sender.timestamp = timestamp
	decoded.members[0].node.timestamp = timestamp

	if err != nil {
		t.Error(err)
	}

	if decoded.broadcast == nil {
		t.Error("Broadcast not decoded")
	}

	message.broadcast.origin = nil
	decoded.broadcast.origin = nil

	if !reflect.DeepEqual(message.broadcast, decoded.broadcast) {
		t.Error("Broadcasts do not match:")
		t.Error(" Input bcast:", message.broadcast)
		t.Error("Output bcast:", decoded.broadcast)
	}
}
<|endoftext|>


<commit_before>// Package ircudf implements parts of the Internet Realy Chat (IRC) protocol
// as defined in rfc1459 (http://tools.ietf.org/html/rfc1459.html)
// and provides basic IRC functions
package ircudf

import (
	"bufio"
	"fmt"
	"log"
	"net"
	"strings"
	"time"
	"io"
)

type Server struct { //IRC Server
	server    string      //Server address
	sendqueue chan string //Message queue
	conn      net.Conn    //Server connection

	nickname string //User nickname
	username string //User username
	realname string //User realname
}

var ( // Events can be changed to custom functions
	EventOnJoin    func(string, string)         // channel, user
	EventOnPart    func(string, string, string) // channel, user, message
	EventOnPrivmsg func(string, string, string) // channel, user, message
	EventOnNotice  func(string, string, string) // channel, user, message
	EventOnReply   func(string, string, string) // number, name, reply
)

// Create sets the server address and user information.
//	// Example: (Nickname!Username@Hostname): Real Name
// 	freenode := ircudf.Create("irc.freenode.org:6667", "Nickname", "Username, "Real Name")
func Create(addr, nickname, username, realname string) *Server {
	ref := &Server{server: addr, sendqueue: make(chan string),
		nickname: nickname, username: username, realname: realname}

	debug("Create:", ref.server, addr, "\n")
	return ref
}

// Connect establishes a connection to the server.
// If no parameter is given the default timeout (5 sec) will be used.
func (sock *Server) Connect(timeout ...int) {
	wait := 5 * time.Second
	if len(timeout) >= 1 {
		wait = time.Duration(timeout[0]) * time.Second
	}

	conn, err := net.DialTimeout("tcp", sock.server, wait)
	sock.conn = conn

	errcheck(err) //ADD: RECONNECT
	debug("Connect:", sock.server, "\n")
}

// Receive receives new messages from the server and forwards them to parse().
func (sock *Server) Receive() {

	go func() {
		debug("Receive:", sock.server, "\n")
		reader := bufio.NewReader(sock.conn)
		sock.sendroutine() //Non-Blocking

		for {
			line, err := reader.ReadString('\n')
			errcheck(err)
			debug("->", line)
			sock.parse(strings.Trim(line, "\r\n")) //Remove \r\n for easier parsing
		}

	}()

	time.Sleep(time.Second)
	sock.Nick(sock.nickname)
	sock.user(sock.username, "0", "0", sock.realname)
}

// parse parses incoming messages from the server and triggers predefined events.
func (sock *Server) parse(line string) {
	split := strings.SplitN(line, " ", 4)
	split = append(split, make([]string, 4-len(split), 4-len(split))...)

	switch true {
	case split[0] == "PING":
		sock.pong(split[1]) //Ping e.g.: PING :B97B6379
	case split[1] == "PRIVMSG" && split[3][1:] == "!ping":
		sock.Privmsg(split[2], "!pong")
	case split[1] == "376":
		sock.Join("#irccs")
	}
}

// Nick sets or changes the nickname. The IRC server might reply an errorcode,
// if the requsted nickname is not valid or in use.
func (sock *Server) Nick(nickname string) {
	sock.nickname = nickname
	sock.Send("NICK " + nickname)
}

// user specifies the userdata at the beginning of a new connection.
// Servername and hostname are likely to be ignored by the IRC server.
// Scheme: (Nickname!Username@Hostname): Real Name
func (sock *Server) user(username, hostname, servername, realname string) {
	sock.Send("USER " + username + " " + hostname + " " + servername + " :" + realname)
}

// Join joins the specified channel(s). Multiple channels need to be
// seperated by a colon. */
func (sock *Server) Join(channel string) {
	sock.Send("JOIN " + channel)
}

// pong answers a ping request with a previously received reply string.
func (sock *Server) pong(reply string) {
	sock.Send("PONG " + reply)
}

// Privmsg sends a private message to a user or a channel.
// The parameter user can also contain multiple receivers seperated by a colon.
func (sock *Server) Privmsg(user string, message string) {
	sock.Send("PRIVMSG " + user + " :" + message)
}

// Send adds the message to the RAW Message queue
func (sock *Server) Send(message string) {
	go func() {
		sock.sendqueue <- message + "\n"
	}()
}

// sendroutine processes the sendqueue and sends the messages to the server.
// sendrouting must be executed as goroutine!
func (sock *Server) sendroutine() {
	go func() {
		for {
			smsg := <-sock.sendqueue
			io.WriteString(sock.conn, smsg)
			debug("<-", smsg)
		}
	}()
}

// DEBUG FUNCTIONS

func errcheck(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func debug(s ...interface{}) {
	fmt.Print(s...) //log.Print adds timestamp
}
<commit_msg>comments<commit_after>// Package ircudf implements parts of the Internet Realy Chat (IRC) protocol
// as defined in rfc1459 (http://tools.ietf.org/html/rfc1459.html)
// and provides basic IRC functions
package ircudf

import (
	"bufio"
	"fmt"
	"log"
	"net"
	"strings"
	"time"
	"io"
)

type Server struct { //IRC Server
	server    string      //Server address
	sendqueue chan string //Message queue
	conn      net.Conn    //Server connection

	nickname string //User nickname
	username string //User username
	realname string //User realname
}

var ( // Events can be changed to custom functions
	EventOnJoin    func(string, string)         // channel, user
	EventOnPart    func(string, string, string) // channel, user, message
	EventOnPrivmsg func(string, string, string) // channel, user, message
	EventOnNotice  func(string, string, string) // channel, user, message
	EventOnReply   func(string, string, string) // number, name, reply
)

// Create sets the server address and user information.
//	// Example: (Nickname!Username@Hostname): Real Name
// 	freenode := ircudf.Create("irc.freenode.org:6667", "Nickname", "Username, "Real Name")
func Create(addr, nickname, username, realname string) *Server {
	ref := &Server{server: addr, sendqueue: make(chan string),
		nickname: nickname, username: username, realname: realname}

	debug("Create:", ref.server, addr, "\n")
	return ref
}

// Connect establishes a connection to the server.
// If no parameter is given the default timeout (5 sec) will be used.
func (sock *Server) Connect(timeout ...int) {
	wait := 5 * time.Second
	if len(timeout) >= 1 {
		wait = time.Duration(timeout[0]) * time.Second
	}

	conn, err := net.DialTimeout("tcp", sock.server, wait)
	sock.conn = conn

	errcheck(err) //ADD: RECONNECT
	debug("Connect:", sock.server, "\n")
}

// Receive receives new messages from the server and forwards them to parse().
func (sock *Server) Receive() {

	go func() {
		debug("Receive:", sock.server, "\n")
		reader := bufio.NewReader(sock.conn)
		sock.sendroutine() //Non-Blocking

		for {
			line, err := reader.ReadString('\n')
			errcheck(err)
			debug("->", line)
			sock.parse(strings.Trim(line, "\r\n")) //Remove \r\n for easier parsing
		}

	}()

	time.Sleep(time.Second)
	sock.Nick(sock.nickname)
	sock.user(sock.username, "0", "0", sock.realname)
}

// parse parses incoming messages from the server and triggers predefined events.
func (sock *Server) parse(line string) {
	split := strings.SplitN(line, " ", 4)
	split = append(split, make([]string, 4-len(split), 4-len(split))...)

	switch true {
	case split[0] == "PING":
		sock.pong(split[1]) //Ping e.g.: PING :B97B6379
	case split[1] == "PRIVMSG" && split[3][1:] == "!ping":
		sock.Privmsg(split[2], "!pong")
	case split[1] == "376":
		sock.Join("#irccs")
	}
}

// Nick sets or changes the nickname. The IRC server might reply an errorcode,
// if the requsted nickname is not valid or in use.
func (sock *Server) Nick(nickname string) {
	sock.nickname = nickname
	sock.Send("NICK " + nickname)
}

// user specifies the userdata at the beginning of a new connection.
// Servername and hostname are likely to be ignored by the IRC server.
// Scheme: (Nickname!Username@Hostname): Real Name
func (sock *Server) user(username, hostname, servername, realname string) {
	sock.Send("USER " + username + " " + hostname + " " + servername + " :" + realname)
}

// Join joins the specified channel(s). Multiple channels need to be
// seperated by a colon.
func (sock *Server) Join(channel string) {
	sock.Send("JOIN " + channel)
}

// pong answers a ping request with a previously received reply string.
func (sock *Server) pong(reply string) {
	sock.Send("PONG " + reply)
}

// Privmsg sends a private message to a user or a channel.
// The parameter user can also contain multiple receivers seperated by a colon.
func (sock *Server) Privmsg(user string, message string) {
	sock.Send("PRIVMSG " + user + " :" + message)
}

// Send adds the message to the RAW Message queue
func (sock *Server) Send(message string) {
	go func() {
		sock.sendqueue <- message + "\n"
	}()
}

// sendroutine sends the messages of the queue to the server.
func (sock *Server) sendroutine() {
	go func() {
		for {
			smsg := <-sock.sendqueue
			io.WriteString(sock.conn, smsg)
			debug("<-", smsg)
		}
	}()
}

// DEBUG FUNCTIONS

func errcheck(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func debug(s ...interface{}) {
	fmt.Print(s...) //log.Print adds timestamp
}
<|endoftext|>


<commit_before>package nodos

import (
	"golang.org/x/sys/windows/registry"
	"strings"
)

func international(key string) (string, error) {
	k, err := registry.OpenKey(
		registry.CURRENT_USER,
		`Control Panel\International`,
		registry.QUERY_VALUE)
	if err != nil {
		return "", err
	}
	defer k.Close()

	val, _, err := k.GetStringValue(key)
	return val, err
}

func osDateLayout() (string, error) {
	layout, err := international("sShortDate")
	if err != nil {
		return "", err
	}
	return table.Replace(layout), nil
}

var table = strings.NewReplacer(
	"yyyy", "2006",
	"MM", "01",
	"dd", "02",
	"d", "2",
	"M", "1",
	"H", "15",
	"mm", "04",
	"ss", "05",
)
<commit_msg>Retry Fix: OsDateFormat<commit_after>package nodos

import (
	"regexp"
	"strings"

	"golang.org/x/sys/windows"
	"golang.org/x/sys/windows/registry"
)

func international(key string) (string, error) {
	k, err := registry.OpenKey(
		registry.CURRENT_USER,
		`Control Panel\International`,
		registry.QUERY_VALUE)
	if err != nil {
		return "", err
	}
	defer k.Close()

	val, _, err := k.GetStringValue(key)
	return val, err
}

var rxHasSingleD = regexp.MustCompile(`\bd\b`)

func osDateLayout() (string, error) {
	layout, err := international("sShortDate")
	if err != nil {
		return "", err
	}

	// When the layout has a single 'd',
	// on the codepage 932, the weekday is appended at the tail.
	// on the codepage 437, the weekday is inserted at the head.
	// The source of the information is
	// https://kurasaba.hatenablog.com/entries/2006/01/31
	if rxHasSingleD.MatchString(layout) {
		if windows.GetACP() == 932 {
			layout = layout + " Mon"
		} else {
			layout = "Mon " + layout
		}
	}
	return table.Replace(layout), nil
}

var table = strings.NewReplacer(
	"yyyy", "2006",
	"MM", "01",
	"dd", "02",
	"d", "02",
	"M", "01",
	"H", "15",
	"mm", "04",
	"ss", "05",
)
<|endoftext|>


<commit_before>// Copyright 2015-2017, Cyrill @ Schumacher.fm and the CoreStore contributors
//
// 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 dmlgen provides code generation templates and library code for
// sql/dml.
//
// To generated the protocol buffer file
// $ protoc --gogo_out=Mgoogle/protobuf/timestamp.proto=github.com/gogo/protobuf/types:. --proto_path=/Users/kiri/GoPro/src/:/Users/kiri/GoPro/src/github.com/gogo/protobuf/protobuf/:. *.proto
package dmlgen
<commit_msg>net/dmlgen: Add todo Generate also protobuf code<commit_after>// Copyright 2015-2017, Cyrill @ Schumacher.fm and the CoreStore contributors
//
// 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 dmlgen provides code generation templates and library code for
// sql/dml.
//
// To generated the protocol buffer file
// $ protoc --gogo_out=Mgoogle/protobuf/timestamp.proto=github.com/gogo/protobuf/types:. --proto_path=/Users/kiri/GoPro/src/:/Users/kiri/GoPro/src/github.com/gogo/protobuf/protobuf/:. *.proto
//
// TODO: Generate also protobuf code for https://github.com/twitchtv/twirp/wiki
package dmlgen
<|endoftext|>


<commit_before>package minify // import "github.com/tdewolff/minify"

import (
	"bytes"
	"encoding/base64"
	"net/url"

	"github.com/tdewolff/parse"
)

// Epsilon is the closest number to zero that is not considered to be zero.
var Epsilon = 0.00001

var (
	zeroBytes = []byte("0")
)

// ContentType minifies a given mediatype by removing all whitespace.
func ContentType(b []byte) []byte {
	j := 0
	start := 0
	inString := false
	for i, c := range b {
		if !inString && parse.IsWhitespace(c) {
			if start != 0 {
				j += copy(b[j:], b[start:i])
			} else {
				j += i
			}
			start = i + 1
		} else if c == '"' {
			inString = !inString
		}
	}
	if start != 0 {
		j += copy(b[j:], b[start:])
		return parse.ToLower(b[:j])
	}
	return parse.ToLower(b)
}

// DataURI minifies a data URI and calls a minifier by the specified mediatype. Specifications: https://www.ietf.org/rfc/rfc2397.txt.
func DataURI(m Minifier, dataURI []byte) []byte {
	if mediatype, data, err := parse.DataURI(dataURI); err == nil {
		dataURI, _ = Bytes(m, string(mediatype), data)
		base64Len := len(";base64") + base64.StdEncoding.EncodedLen(len(dataURI))
		asciiLen := len(dataURI)
		for _, c := range dataURI {
			if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '-' || c == '_' || c == '.' || c == '~' || c == ' ' {
				asciiLen++
			} else {
				asciiLen += 2
			}
			if asciiLen > base64Len {
				break
			}
		}
		if asciiLen > base64Len {
			encoded := make([]byte, base64Len-len(";base64"))
			base64.StdEncoding.Encode(encoded, dataURI)
			dataURI = encoded
			mediatype = append(mediatype, []byte(";base64")...)
		} else {
			dataURI = []byte(url.QueryEscape(string(dataURI)))
			dataURI = bytes.Replace(dataURI, []byte("\""), []byte("\\\""), -1)
		}
		if len(mediatype) >= len("text/plain") && bytes.HasPrefix(mediatype, []byte("text/plain")) {
			mediatype = mediatype[len("text/plain"):]
		}
		dataURI = append(append(append([]byte("data:"), mediatype...), ','), dataURI...)
	}
	return dataURI
}

// Number minifies a given byte slice containing a number (see parse.Number) and removes superfluous characters.
func Number(num []byte) []byte {
	// omit first + and register mantissa start and end, whether it's negative and the exponent
	neg := false
	start := 0
	dot := -1
	end := len(num)
	exp := int64(0)
	if 0 < len(num) && (num[0] == '+' || num[0] == '-') {
		if num[0] == '-' {
			neg = true
			start++
		} else {
			num = num[1:]
			end--
		}
	}
	for i, c := range num {
		if c == '.' {
			dot = i
		} else if c == 'e' || c == 'E' {
			end = i
			i++
			if i < len(num) && num[i] == '+' {
				i++
			}
			var ok bool
			if exp, ok = parse.Int(num[i:]); !ok {
				return num
			}
			break
		}
	}
	if dot == -1 {
		dot = end
	}

	// trim leading zeros but one
	for start < end-1 && num[start] == '0' {
		start++
	}
	// trim trailing zeros
	i := end - 1
	for ; i > dot; i-- {
		if num[i] != '0' {
			end = i + 1
			break
		}
	}
	if i == dot {
		end = dot
		if start == end {
			num[start] = '0'
			return num[start : start+1]
		}
	}
	if start < end && num[start] == '0' {
		return num[start:end]
	}

	// shorten mantissa by increasing/decreasing the exponent
	if end == dot {
		for i := end - 1; i >= start; i-- {
			if num[i] != '0' {
				exp += int64(end - i - 1)
				end = i + 1
				break
			}
		}
	} else {
		exp -= int64(end - dot - 1)
		if start == dot {
			for i = dot + 1; i < end; i++ {
				if num[i] != '0' {
					copy(num[dot:], num[i:end])
					end -= i - dot
					break
				}
			}
		} else {
			copy(num[dot:], num[dot+1:end])
			end--
		}
	}

	// append the exponent or change the mantissa to incorporate the exponent
	relExp := exp + int64(end-start) // exp when the first non-zero digit is directly after the dot
	n := lenInt64(exp)               // number of exp digits
	if exp == 0 {
		if neg {
			start--
			num[start] = '-'
		}
		return num[start:end]
	} else if int(relExp)+n+1 < 0 || 2 < exp { // add exponent for exp 3 and higher and where a lower exp really makes it shorter
		num[end] = 'e'
		end++
		if exp < 0 {
			num[end] = '-'
			end++
			exp = -exp
		}
		for i := end + n - 1; i >= end; i-- {
			num[i] = byte(exp%10) + '0'
			exp /= 10
		}
		end += n
	} else if exp < 0 { // omit exponent
		if relExp > 0 {
			copy(num[start+int(relExp)+1:], num[start+int(relExp):end])
			num[start+int(relExp)] = '.'
			end++
		} else {
			copy(num[start-int(relExp)+1:], num[start:end])
			num[start] = '.'
			for i := 1; i < -int(relExp)+1; i++ {
				num[start+i] = '0'
			}
			end -= int(relExp) - 1
		}
	} else { // for exponent 1 and 2
		num[end] = '0'
		if exp == 2 {
			num[end+1] = '0'
		}
		end += int(exp)
	}

	if neg {
		start--
		num[start] = '-'
	}
	return num[start:end]
}

func lenInt64(i int64) int {
	if i < 0 {
		i = -i
	}
	switch {
	case i < 10:
		return 1
	case i < 100:
		return 2
	case i < 1000:
		return 3
	case i < 10000:
		return 4
	case i < 100000:
		return 5
	case i < 1000000:
		return 6
	case i < 10000000:
		return 7
	case i < 100000000:
		return 8
	case i < 1000000000:
		return 9
	case i < 10000000000:
		return 10
	case i < 100000000000:
		return 11
	case i < 1000000000000:
		return 12
	case i < 10000000000000:
		return 13
	case i < 100000000000000:
		return 14
	case i < 1000000000000000:
		return 15
	case i < 10000000000000000:
		return 16
	case i < 100000000000000000:
		return 17
	case i < 1000000000000000000:
		return 18
	}
	return 19
}
<commit_msg>Small stuff<commit_after>package minify // import "github.com/tdewolff/minify"

import (
	"bytes"
	"encoding/base64"
	"net/url"

	"github.com/tdewolff/parse"
)

// Epsilon is the closest number to zero that is not considered to be zero.
var Epsilon = 0.00001

var (
	zeroBytes = []byte("0")
)

// ContentType minifies a given mediatype by removing all whitespace.
func ContentType(b []byte) []byte {
	j := 0
	start := 0
	inString := false
	for i, c := range b {
		if !inString && parse.IsWhitespace(c) {
			if start != 0 {
				j += copy(b[j:], b[start:i])
			} else {
				j += i
			}
			start = i + 1
		} else if c == '"' {
			inString = !inString
		}
	}
	if start != 0 {
		j += copy(b[j:], b[start:])
		return parse.ToLower(b[:j])
	}
	return parse.ToLower(b)
}

// DataURI minifies a data URI and calls a minifier by the specified mediatype. Specifications: https://www.ietf.org/rfc/rfc2397.txt.
func DataURI(m Minifier, dataURI []byte) []byte {
	if mediatype, data, err := parse.DataURI(dataURI); err == nil {
		dataURI, _ = Bytes(m, string(mediatype), data)
		base64Len := len(";base64") + base64.StdEncoding.EncodedLen(len(dataURI))
		asciiLen := len(dataURI)
		for _, c := range dataURI {
			if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '-' || c == '_' || c == '.' || c == '~' || c == ' ' {
				asciiLen++
			} else {
				asciiLen += 2
			}
			if asciiLen > base64Len {
				break
			}
		}
		if asciiLen > base64Len {
			encoded := make([]byte, base64Len-len(";base64"))
			base64.StdEncoding.Encode(encoded, dataURI)
			dataURI = encoded
			mediatype = append(mediatype, []byte(";base64")...)
		} else {
			dataURI = []byte(url.QueryEscape(string(dataURI)))
			dataURI = bytes.Replace(dataURI, []byte("\""), []byte("\\\""), -1)
		}
		if len(mediatype) >= len("text/plain") && bytes.HasPrefix(mediatype, []byte("text/plain")) {
			mediatype = mediatype[len("text/plain"):]
		}
		dataURI = append(append(append([]byte("data:"), mediatype...), ','), dataURI...)
	}
	return dataURI
}

// Number minifies a given byte slice containing a number (see parse.Number) and removes superfluous characters.
func Number(num []byte) []byte {
	// omit first + and register mantissa start and end, whether it's negative and the exponent
	neg := false
	start := 0
	dot := -1
	end := len(num)
	exp := int64(0)
	if 0 < len(num) && (num[0] == '+' || num[0] == '-') {
		if num[0] == '-' {
			neg = true
			start++
		} else {
			num = num[1:]
			end--
		}
	}
	for i, c := range num {
		if c == '.' {
			dot = i
		} else if c == 'e' || c == 'E' {
			end = i
			i++
			if i < len(num) && num[i] == '+' {
				i++
			}
			var ok bool
			if exp, ok = parse.Int(num[i:]); !ok {
				return num
			}
			break
		}
	}
	if dot == -1 {
		dot = end
	}

	// trim leading zeros but leave at least one digit
	for start < end-1 && num[start] == '0' {
		start++
	}
	// trim trailing zeros
	i := end - 1
	for ; i > dot; i-- {
		if num[i] != '0' {
			end = i + 1
			break
		}
	}
	if i == dot {
		end = dot
		if start == end {
			num[start] = '0'
			return num[start : start+1]
		}
	} else if start == end-1 && num[start] == '0' {
		return num[start:end]
	}

	// shorten mantissa by increasing/decreasing the exponent
	if end == dot {
		for i := end - 1; i >= start; i-- {
			if num[i] != '0' {
				exp += int64(end - i - 1)
				end = i + 1
				break
			}
		}
	} else {
		exp -= int64(end - dot - 1)
		if start == dot {
			for i = dot + 1; i < end; i++ {
				if num[i] != '0' {
					copy(num[dot:], num[i:end])
					end -= i - dot
					break
				}
			}
		} else {
			copy(num[dot:], num[dot+1:end])
			end--
		}
	}

	// append the exponent or change the mantissa to incorporate the exponent
	relExp := exp + int64(end-start) // exp when the first non-zero digit is directly after the dot
	n := lenInt64(exp)               // number of exp digits
	if exp == 0 {
		if neg {
			start--
			num[start] = '-'
		}
		return num[start:end]
	} else if int(relExp)+n+1 < 0 || 2 < exp { // add exponent for exp 3 and higher and where a lower exp really makes it shorter
		num[end] = 'e'
		end++
		if exp < 0 {
			num[end] = '-'
			end++
			exp = -exp
		}
		for i := end + n - 1; i >= end; i-- {
			num[i] = byte(exp%10) + '0'
			exp /= 10
		}
		end += n
	} else if exp < 0 { // omit exponent
		if relExp > 0 {
			copy(num[start+int(relExp)+1:], num[start+int(relExp):end])
			num[start+int(relExp)] = '.'
			end++
		} else {
			copy(num[start-int(relExp)+1:], num[start:end])
			num[start] = '.'
			for i := 1; i < -int(relExp)+1; i++ {
				num[start+i] = '0'
			}
			end -= int(relExp) - 1
		}
	} else { // for exponent 1 and 2
		num[end] = '0'
		if exp == 2 {
			num[end+1] = '0'
		}
		end += int(exp)
	}

	if neg {
		start--
		num[start] = '-'
	}
	return num[start:end]
}

func lenInt64(i int64) int {
	if i < 0 {
		i = -i
	}
	switch {
	case i < 10:
		return 1
	case i < 100:
		return 2
	case i < 1000:
		return 3
	case i < 10000:
		return 4
	case i < 100000:
		return 5
	case i < 1000000:
		return 6
	case i < 10000000:
		return 7
	case i < 100000000:
		return 8
	case i < 1000000000:
		return 9
	case i < 10000000000:
		return 10
	case i < 100000000000:
		return 11
	case i < 1000000000000:
		return 12
	case i < 10000000000000:
		return 13
	case i < 100000000000000:
		return 14
	case i < 1000000000000000:
		return 15
	case i < 10000000000000000:
		return 16
	case i < 100000000000000000:
		return 17
	case i < 1000000000000000000:
		return 18
	}
	return 19
}
<|endoftext|>


<commit_before>package gogt_sdk

import (
	"fmt"
	"net/http"
	"time"
	"crypto/md5"
	"math/rand"
	"strconv"
	"encoding/json"
	"strings"
	"net/url"
	"math"
)

const (
	FN_CHALLENGE = "geetest_challenge"
	FN_VALIDATE = "geetest_validate"
	FN_SECCODE = "geetest_seccode"

	GT_STATUS_SESSION_KEY = "gt_server_status"

	API_URL = "http://api.geetest.com"
	REGISTER_HANDLER = "/register.php"
	VALIDATE_HANDLER = "/validate.php"

	VERSION = "3.2.0"
)

type Geetest struct {
	privateKey  string
	captchaID   string
	sdkVersion  string
	responseStr string
}

//CreateGeeTest 创建GeetestLib实例
func GeetestLib(privateKey, captchaID string) *Geetest {
	return &Geetest{
		privateKey:privateKey,
		captchaID:captchaID,
		sdkVersion:VERSION,
		responseStr:"",
	}
}

//PreProcess 验证初始化预处理.
func (gt *Geetest)PreProcess(userID ...string) int {
	status, challenge := gt.register(userID...)
	gt.responseStr = gt.makeResponseFormat(status, challenge)
	return status
}

func (gt *Geetest)register(userID ...string) (int, string) {
	challenge := gt.registerChallenge(userID...)
	if len(challenge) != 32 {
		return 0, gt.makeFailChallenge()
	}
	return 2, gt.md5Encode(append(challenge, []byte(gt.privateKey)))
}

func (gt *Geetest)GetResponseStr() string {
	return gt.responseStr
}

func (gt *Geetest)makeFailChallenge() string {
	rand.Seed(time.Now().Unix())
	rnd1 := rand.Intn(100)
	rnd2 := rand.Intn(100)
	md5_str1 := gt.md5Encode([]byte(strconv.Itoa(rnd1)))
	md5_str2 := gt.md5Encode([]byte(strconv.Itoa(rnd2)))
	challenge := md5_str1 + md5_str2[0:2]
	return challenge
}

func (gt *Geetest)makeResponseFormat(status int, challenge string) string {
	jsonmap := make(map[string]string)
	jsonmap["success"] = status
	jsonmap["gt"] = gt.captchaID
	jsonmap["challenge"] = challenge
	jsonbyte, _ := json.Marshal(jsonmap)
	return string(jsonbyte)
}

//registerChallenge
func (gt *Geetest)registerChallenge(userID ...string) (respbytes []byte) {
	var registerURL string
	if len(userID) == 1 {
		registerURL = fmt.Sprintf("%s%s?gt=%s&user_id=%s", API_URL, REGISTER_HANDLER, gt.captchaID, userID[0])
	} else {
		registerURL = fmt.Sprintf("%s%s?gt=%s", API_URL, REGISTER_HANDLER, gt.captchaID)
	}
	client := http.Client{Timeout: 2 * time.Second }
	resp, err := client.Get(registerURL)
	if err != nil || resp.StatusCode != 200 {
		return
	}
	_, err = resp.Body.Read(respbytes)
	if err != nil {
		return
	}
	return
}

//SuccessValidate 正常模式的二次验证方式.向geetest server 请求验证结果.
func (gt *Geetest)SuccessValidate(challenge, validate, seccode, userid string) bool {
	if !gt.checkParam(challenge, validate, seccode) || gt.checkResult(challenge, validate) {
		return 0
	}
	validateURL := fmt.Sprintf("%s%s", API_URL, VALIDATE_HANDLER)
	postdata := new(url.Values)
	postdata.Add("seccode", seccode)
	postdata.Add("sdk", "golang_" + VERSION)
	if userid != "" {
		postdata.Add("user_id", userid)
	}
	backinfo := gt.postValues(validateURL, postdata)
	return backinfo == gt.md5Encode([]byte(seccode))
}

func (gt *Geetest)postValues(url string, data url.Values) string {
	var respbyte []byte
	resp, err := http.PostForm(url, data)
	if err != nil {
		return ""
	}
	_, err = resp.Body.Read(respbyte)
	if err != nil {
		return ""
	}
	return string(respbyte)
}

func (gt *Geetest)checkResult(origin, validate string) bool {
	encodeStr := gt.md5Encode([]byte(gt.privateKey + "geetest" + origin))
	return encodeStr == validate
}

//FailbackValidate failback模式的二次验证方式.在本地对轨迹进行简单的判断返回验证结果.
func (gt *Geetest)FailbackValidate(challenge, validate, seccode string) int {
	if !gt.checkParam(challenge, validate, seccode) {
		return false
	}
	validate_str := strings.Split(validate, '_')
	if len(validate_str) < 3 {
		return false
	}
	encodeAns := validate_str[0]
	encodeFbii := validate_str[1]
	encodeIgi := validate_str[2]
	decodeAns := gt.decodeResponse(challenge, encodeAns)
	decodeFbii := gt.decodeResponse(challenge, encodeFbii)
	decodeIgi := gt.decodeResponse(challenge, encodeIgi)
	validateResult := gt.validateFailImage(decodeAns, decodeFbii, decodeIgi)
	return validateResult
}

func (gt *Geetest)checkParam(params ...string) bool {
	for _, param := range params {
		if strings.TrimSpace(param) == "" {
			return false
		}
	}
	return true
}

func (gt *Geetest)validateFailImage(ans, fullBgIndex, imgGrpIndex int) int{
	thread := 3
	fullBgIndexStr := gt.md5Encode([]byte(fullBgIndex))[0:10]
	imgGrpIndexStr := gt.md5Encode([]byte(imgGrpIndex))[0:10]
	var answerDecode string
	for i := 0; i < 10; i++ {
		if i % 2 == 0 {
			answerDecode += fullBgIndexStr[i]
		} else if i % 2 == 1 {
			answerDecode += imgGrpIndexStr[i]
		}
	}
	xDecode := answerDecode[4:]
	xInt := int(strconv.ParseInt(string(xDecode), 16, 32))
	result := xInt % 200
	if result < 40 {
		result = 40
	}
	if math.Abs(float64(ans - result)) < thread {
		return 1
	}
	return 0
}

func (gt *Geetest)md5Encode(values []byte) string {
	return fmt.Sprintf("%x", md5.Sum(values))
}

func (gt *Geetest)decodeRandBase(challenge string) int {
	baseStr := challenge[32:]
	tempList := new([]int)
	for _, char := range baseStr {
		tempChar := byte(char)
		result := tempChar - 48
		if (tempChar > 57) {
			result = tempChar - 87
		}
		tempList = append(tempList, result)
	}
	return tempList[0] * 36 + tempList[1]
}

func (gt *Geetest)decodeResponse(challenge, userresponse string) (res int) {
	if len(userresponse) > 100 {
		return
	}
	digits := []int{1, 2, 5, 10, 50}
	key := make(map[byte]int)
	for _, i := range challenge {
		if exist, _ := key[i]; exist {
			continue
		}
		value := digits[len(key) % 5]
		key[i] = value
	}
	for _, i := range userresponse {
		res += key[i]
	}
	res -= gt.decodeRandBase(challenge)
	return
}

<commit_msg>rename package<commit_after>package gogtsdk

import (
	"fmt"
	"net/http"
	"time"
	"crypto/md5"
	"math/rand"
	"strconv"
	"encoding/json"
	"strings"
	"net/url"
	"math"
)

const (
	FN_CHALLENGE = "geetest_challenge"
	FN_VALIDATE = "geetest_validate"
	FN_SECCODE = "geetest_seccode"

	GT_STATUS_SESSION_KEY = "gt_server_status"

	API_URL = "http://api.geetest.com"
	REGISTER_HANDLER = "/register.php"
	VALIDATE_HANDLER = "/validate.php"

	VERSION = "3.2.0"
)

type Geetest struct {
	privateKey  string
	captchaID   string
	sdkVersion  string
	responseStr string
}

//CreateGeeTest 创建GeetestLib实例
func GeetestLib(privateKey, captchaID string) *Geetest {
	return &Geetest{
		privateKey:privateKey,
		captchaID:captchaID,
		sdkVersion:VERSION,
		responseStr:"",
	}
}

//PreProcess 验证初始化预处理.
func (gt *Geetest)PreProcess(userID ...string) int {
	status, challenge := gt.register(userID...)
	gt.responseStr = gt.makeResponseFormat(status, challenge)
	return status
}

func (gt *Geetest)register(userID ...string) (int, string) {
	challenge := gt.registerChallenge(userID...)
	if len(challenge) != 32 {
		return 0, gt.makeFailChallenge()
	}
	return 2, gt.md5Encode(append(challenge, []byte(gt.privateKey)))
}

func (gt *Geetest)GetResponseStr() string {
	return gt.responseStr
}

func (gt *Geetest)makeFailChallenge() string {
	rand.Seed(time.Now().Unix())
	rnd1 := rand.Intn(100)
	rnd2 := rand.Intn(100)
	md5_str1 := gt.md5Encode([]byte(strconv.Itoa(rnd1)))
	md5_str2 := gt.md5Encode([]byte(strconv.Itoa(rnd2)))
	challenge := md5_str1 + md5_str2[0:2]
	return challenge
}

func (gt *Geetest)makeResponseFormat(status int, challenge string) string {
	jsonmap := make(map[string]string)
	jsonmap["success"] = status
	jsonmap["gt"] = gt.captchaID
	jsonmap["challenge"] = challenge
	jsonbyte, _ := json.Marshal(jsonmap)
	return string(jsonbyte)
}

//registerChallenge
func (gt *Geetest)registerChallenge(userID ...string) (respbytes []byte) {
	var registerURL string
	if len(userID) == 1 {
		registerURL = fmt.Sprintf("%s%s?gt=%s&user_id=%s", API_URL, REGISTER_HANDLER, gt.captchaID, userID[0])
	} else {
		registerURL = fmt.Sprintf("%s%s?gt=%s", API_URL, REGISTER_HANDLER, gt.captchaID)
	}
	client := http.Client{Timeout: 2 * time.Second }
	resp, err := client.Get(registerURL)
	if err != nil || resp.StatusCode != 200 {
		return
	}
	_, err = resp.Body.Read(respbytes)
	if err != nil {
		return
	}
	return
}

//SuccessValidate 正常模式的二次验证方式.向geetest server 请求验证结果.
func (gt *Geetest)SuccessValidate(challenge, validate, seccode, userid string) bool {
	if !gt.checkParam(challenge, validate, seccode) || gt.checkResult(challenge, validate) {
		return 0
	}
	validateURL := fmt.Sprintf("%s%s", API_URL, VALIDATE_HANDLER)
	postdata := new(url.Values)
	postdata.Add("seccode", seccode)
	postdata.Add("sdk", "golang_" + VERSION)
	if userid != "" {
		postdata.Add("user_id", userid)
	}
	backinfo := gt.postValues(validateURL, postdata)
	return backinfo == gt.md5Encode([]byte(seccode))
}

func (gt *Geetest)postValues(url string, data url.Values) string {
	var respbyte []byte
	resp, err := http.PostForm(url, data)
	if err != nil {
		return ""
	}
	_, err = resp.Body.Read(respbyte)
	if err != nil {
		return ""
	}
	return string(respbyte)
}

func (gt *Geetest)checkResult(origin, validate string) bool {
	encodeStr := gt.md5Encode([]byte(gt.privateKey + "geetest" + origin))
	return encodeStr == validate
}

//FailbackValidate failback模式的二次验证方式.在本地对轨迹进行简单的判断返回验证结果.
func (gt *Geetest)FailbackValidate(challenge, validate, seccode string) int {
	if !gt.checkParam(challenge, validate, seccode) {
		return false
	}
	validate_str := strings.Split(validate, '_')
	if len(validate_str) < 3 {
		return false
	}
	encodeAns := validate_str[0]
	encodeFbii := validate_str[1]
	encodeIgi := validate_str[2]
	decodeAns := gt.decodeResponse(challenge, encodeAns)
	decodeFbii := gt.decodeResponse(challenge, encodeFbii)
	decodeIgi := gt.decodeResponse(challenge, encodeIgi)
	validateResult := gt.validateFailImage(decodeAns, decodeFbii, decodeIgi)
	return validateResult
}

func (gt *Geetest)checkParam(params ...string) bool {
	for _, param := range params {
		if strings.TrimSpace(param) == "" {
			return false
		}
	}
	return true
}

func (gt *Geetest)validateFailImage(ans, fullBgIndex, imgGrpIndex int) int{
	thread := 3
	fullBgIndexStr := gt.md5Encode([]byte(fullBgIndex))[0:10]
	imgGrpIndexStr := gt.md5Encode([]byte(imgGrpIndex))[0:10]
	var answerDecode string
	for i := 0; i < 10; i++ {
		if i % 2 == 0 {
			answerDecode += fullBgIndexStr[i]
		} else if i % 2 == 1 {
			answerDecode += imgGrpIndexStr[i]
		}
	}
	xDecode := answerDecode[4:]
	xInt := int(strconv.ParseInt(string(xDecode), 16, 32))
	result := xInt % 200
	if result < 40 {
		result = 40
	}
	if math.Abs(float64(ans - result)) < thread {
		return 1
	}
	return 0
}

func (gt *Geetest)md5Encode(values []byte) string {
	return fmt.Sprintf("%x", md5.Sum(values))
}

func (gt *Geetest)decodeRandBase(challenge string) int {
	baseStr := challenge[32:]
	tempList := new([]int)
	for _, char := range baseStr {
		tempChar := byte(char)
		result := tempChar - 48
		if (tempChar > 57) {
			result = tempChar - 87
		}
		tempList = append(tempList, result)
	}
	return tempList[0] * 36 + tempList[1]
}

func (gt *Geetest)decodeResponse(challenge, userresponse string) (res int) {
	if len(userresponse) > 100 {
		return
	}
	digits := []int{1, 2, 5, 10, 50}
	key := make(map[byte]int)
	for _, i := range challenge {
		if exist, _ := key[i]; exist {
			continue
		}
		value := digits[len(key) % 5]
		key[i] = value
	}
	for _, i := range userresponse {
		res += key[i]
	}
	res -= gt.decodeRandBase(challenge)
	return
}

<|endoftext|>