<commit_before>package restructure

import (
	"fmt"
	"reflect"
	"regexp/syntax"

	"github.com/alexflint/go-restructure/regex"
)

// Style determines whether we are in Perl or POSIX or custom mode
type Style int

const (
	Perl Style = iota
	POSIX
	CustomStyle
)

// Options represents optional parameters for compilation
type Options struct {
	Style       Style        // If false then opts.Syntax will be set to perl mode
	SyntaxFlags syntax.Flags // Syntax contains flags that control
}

type region struct {
	begin, end int
}

func (r region) wasMatched() bool {
	return r.begin != -1 && r.end != -1
}

type match struct {
	input    []byte
	captures []region
}

func matchFromIndices(indices []int, input []byte) *match {
	match := &match{
		input: input,
	}
	for i := 0; i < len(indices); i += 2 {
		match.captures = append(match.captures, region{indices[i], indices[i+1]})
	}
	return match
}

// Regexp is a regular expression that captures submatches into struct fields.
type Regexp struct {
	st   *Struct
	re   *regex.Regexp
	t    reflect.Type
	opts Options
}

// Find attempts to match the regular expression against the input string. It
// returns true if there was a match, and also populates the fields of the provided
// struct with the contents of each submatch.

// Find attempts to match the regular expression against the input string. It
// returns true if there was a match, and also populates the fields of the provided
// struct with the contents of each submatch.
func (r *Regexp) Find(dest interface{}, s string) bool {
	v := reflect.ValueOf(dest)
	input := []byte(s)

	// Check the type
	expected := reflect.PtrTo(r.t)
	if v.Type() != expected {
		panic(fmt.Errorf("expected destination to be %s but got %T",
			expected.String(), dest))
	}

	// Execute the regular expression
	indices := r.re.FindSubmatchIndex(input)
	if indices == nil {
		return false
	}

	// Inflate matches into original struct
	match := matchFromIndices(indices, input)

	err := inflateStruct(v, match, r.st)
	if err != nil {
		panic(err)
	}
	return true
}

// String returns a string representation of the regular expression
func (r *Regexp) String() string {
	return r.re.String()
}

// Compile constructs a regular expression from the struct fields on the
// provided struct.
func Compile(proto interface{}, opts Options) (*Regexp, error) {
	return CompileType(reflect.TypeOf(proto), opts)
}

// CompileType is like Compile but takes a reflect.Type instead.
func CompileType(t reflect.Type, opts Options) (*Regexp, error) {
	// We do this so that the zero value for Options gives us Perl mode,
	// which is also the default used by the standard library regexp package
	switch opts.Style {
	case Perl:
		opts.SyntaxFlags = syntax.Perl
	case POSIX:
		opts.SyntaxFlags = syntax.POSIX
	}

	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}

	// Traverse the struct
	b := newBuilder(opts)
	st, expr, err := b.structure(t)
	if err != nil {
		return nil, err
	}

	// Compile regular expression
	re, err := regex.CompileSyntax(expr)
	if err != nil {
		return nil, err
	}

	// Return
	return &Regexp{
		st:   st,
		re:   re,
		t:    t,
		opts: opts,
	}, nil
}

// MustCompile is like Compile but panics if there is a compilation error
func MustCompile(proto interface{}, opts Options) *Regexp {
	re, err := Compile(proto, opts)
	if err != nil {
		panic(err)
	}
	return re
}

// MustCompileType is like CompileType but panics if there is a compilation error
func MustCompileType(t reflect.Type, opts Options) *Regexp {
	re, err := CompileType(t, opts)
	if err != nil {
		panic(err)
	}
	return re
}

// Find constructs a regular expression from the given struct and executes it on the
// given string, placing submatches into the fields of the struct. The first parameter
// must be a non-nil struct pointer. It returns true if the match succeeded. The only
// errors that are returned are compilation errors.
func Find(dest interface{}, s string) (bool, error) {
	re, err := Compile(dest, Options{})
	if err != nil {
		return false, err
	}
	return re.Find(dest, s), nil
}
<commit_msg>adjust comments<commit_after>package restructure

import (
	"fmt"
	"reflect"
	"regexp/syntax"

	"github.com/alexflint/go-restructure/regex"
)

// Style determines whether we are in Perl or POSIX or custom mode
type Style int

const (
	Perl Style = iota
	POSIX
	CustomStyle
)

// Options represents optional parameters for compilation
type Options struct {
	Style       Style // Style can be set to Perl, POSIX, or CustomStyle
	SyntaxFlags syntax.Flags
}

type region struct {
	begin, end int
}

func (r region) wasMatched() bool {
	return r.begin != -1 && r.end != -1
}

type match struct {
	input    []byte
	captures []region
}

func matchFromIndices(indices []int, input []byte) *match {
	match := &match{
		input: input,
	}
	for i := 0; i < len(indices); i += 2 {
		match.captures = append(match.captures, region{indices[i], indices[i+1]})
	}
	return match
}

// Regexp is a regular expression that captures submatches into struct fields.
type Regexp struct {
	st   *Struct
	re   *regex.Regexp
	t    reflect.Type
	opts Options
}

// Find attempts to match the regular expression against the input string. It
// returns true if there was a match, and also populates the fields of the provided
// struct with the contents of each submatch.

// Find attempts to match the regular expression against the input string. It
// returns true if there was a match, and also populates the fields of the provided
// struct with the contents of each submatch.
func (r *Regexp) Find(dest interface{}, s string) bool {
	v := reflect.ValueOf(dest)
	input := []byte(s)

	// Check the type
	expected := reflect.PtrTo(r.t)
	if v.Type() != expected {
		panic(fmt.Errorf("expected destination to be %s but got %T",
			expected.String(), dest))
	}

	// Execute the regular expression
	indices := r.re.FindSubmatchIndex(input)
	if indices == nil {
		return false
	}

	// Inflate matches into original struct
	match := matchFromIndices(indices, input)

	err := inflateStruct(v, match, r.st)
	if err != nil {
		panic(err)
	}
	return true
}

// String returns a string representation of the regular expression
func (r *Regexp) String() string {
	return r.re.String()
}

// Compile constructs a regular expression from the struct fields on the
// provided struct.
func Compile(proto interface{}, opts Options) (*Regexp, error) {
	return CompileType(reflect.TypeOf(proto), opts)
}

// CompileType is like Compile but takes a reflect.Type instead.
func CompileType(t reflect.Type, opts Options) (*Regexp, error) {
	// We do this so that the zero value for Options gives us Perl mode,
	// which is also the default used by the standard library regexp package
	switch opts.Style {
	case Perl:
		opts.SyntaxFlags = syntax.Perl
	case POSIX:
		opts.SyntaxFlags = syntax.POSIX
	}

	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}

	// Traverse the struct
	b := newBuilder(opts)
	st, expr, err := b.structure(t)
	if err != nil {
		return nil, err
	}

	// Compile regular expression
	re, err := regex.CompileSyntax(expr)
	if err != nil {
		return nil, err
	}

	// Return
	return &Regexp{
		st:   st,
		re:   re,
		t:    t,
		opts: opts,
	}, nil
}

// MustCompile is like Compile but panics if there is a compilation error
func MustCompile(proto interface{}, opts Options) *Regexp {
	re, err := Compile(proto, opts)
	if err != nil {
		panic(err)
	}
	return re
}

// MustCompileType is like CompileType but panics if there is a compilation error
func MustCompileType(t reflect.Type, opts Options) *Regexp {
	re, err := CompileType(t, opts)
	if err != nil {
		panic(err)
	}
	return re
}

// Find constructs a regular expression from the given struct and executes it on the
// given string, placing submatches into the fields of the struct. The first parameter
// must be a non-nil struct pointer. It returns true if the match succeeded. The only
// errors that are returned are compilation errors.
func Find(dest interface{}, s string) (bool, error) {
	re, err := Compile(dest, Options{})
	if err != nil {
		return false, err
	}
	return re.Find(dest, s), nil
}
<|endoftext|>


<commit_before>package formatters

import (
	"fmt"
	"testing"
)

type str struct{}

func (s str) String() string {
	return "test"
}

func TestFormatArray(t *testing.T) {
	testCases := []struct {
		name string
		in   interface{}
		ret  string
	}{
		{"array string", []string{"value 1", "value 2", "value 3"}, `{"value 1","value 2","value 3"}`},
		{"array int", []int{10, 20, 30}, `{10,20,30}`},
		{"empty array", []int{}, `{}`},
		{"stringer array", []fmt.Stringer{str{}, str{}, str{}}, `{"test","test","test"}`},
	}
	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			ret := FormatArray(tc.in)
			if ret != tc.ret {
				t.Errorf("expected %v, but got %v", tc.ret, ret)
			}
		})
	}
}
<commit_msg>test: increase postgres/formatters coverage<commit_after>package formatters

import (
	"fmt"
	"testing"
)

type str struct{}

func (s str) String() string {
	return "test"
}

func TestFormatArray(t *testing.T) {
	testCases := []struct {
		name string
		in   interface{}
		ret  string
	}{
		{"array string", []string{"value 1", "value 2", "value 3"}, `{"value 1","value 2","value 3"}`},
		{"array int", []int{10, 20, 30}, `{10,20,30}`},
		{"empty array", []int{}, `{}`},
		{"stringer array", []fmt.Stringer{str{}, str{}, str{}}, `{"test","test","test"}`},
		{"interface array", nil, `{"value 1","value 2","value 3"}`},
		{"nil", nil, ""},
	}

	// define interface array
	values := make([]interface{}, 3)
	values[0] = "value 1"
	values[1] = "value 2"
	values[2] = "value 3"
	testCases[4].in = values

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			ret := FormatArray(tc.in)
			if ret != tc.ret {
				t.Errorf("expected %v, but got %v", tc.ret, ret)
			}
		})
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 The GoReporter 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 engine

// UnitTest is a struct that contains AvgCover, PackagesTestDetail and
// PackagesRaceDetail. The type of AvgCover MUST string that represents
// the code coverage of the entire project. The type of PackagesTestDetail
// MUST map[string]PackageTest(contains pass-status,code-coverage and time).
// and it has all packages' detail information. PackagesRaceDetail contains
// all packages' race cases.
//
// And the UnitTest contains all packages' result.
type UnitTest struct {
	AvgCover           string                 `json:"average_cover"`
	PackagesTestDetail map[string]PackageTest `json:"packages_test_detail"`
	PackagesRaceDetail map[string][]string    `json:"packages_race_detail"`
}

// PackageTest is a struct that contains IsPass, Coverage and Time. The
// type of Time MUST float64.
type PackageTest struct {
	IsPass   bool    `json:"is_pass"`
	Coverage string  `json:"coverage"`
	Time     float64 `json:"time"`
}

// Cycloi is a struct that contains Average and Result. The Average is
// one package's cyclo coverage. And Result is the detail cyclo of the package's
// all function.
type Cycloi struct {
	Average string
	Result  []string
}

// Test is a struct that contains Path, Result, Time and Cover. The type of
// Time and Cover MUST float64. And it is just for one package's display.
type Test struct {
	Path   string
	Result int
	Time   float64
	Cover  float64
}

// File is a struct that contains Color, CycloVal and CycloInfo. And it is just
// for one file's display. The CycloInfo contains all cyclo detail information.
type File struct {
	Color     string
	CycloVal  string
	CycloInfo string
}

// Copycode is a struct that contains Files and Path. The type of Path MUST []string
// that contains more than one file path. The Copycode represents some copyed code
// information.
type Copycode struct {
	Files string
	Path  []string
}

// Race is a struct that contains Pkg, Len, Leng and Info. The type of Info MUST
// []string that represents more than one race case. Len is the number of cases.
type Race struct {
	Pkg  string
	Len  string
	Leng string
	Info []string
}

// Simple is a struct that contains Path and Info. The type of Path and Info MUST string.
// The Simple represents one can be simpled code case.
type Simple struct {
	Path string
	Info string
}

// Interfacer is a struct that contains Path and Info. The type of Path and Info MUST string.
// The Interfacer warns about the usage of types that are more specific than necessary.
type Interfacer struct {
	Path string
	Info string
}

// Spell is a struct that contains Path and Info. The type of Path and Info MUST string.
// The Spell represents one word is misspelled.
type Spell struct {
	Path string
	Info string
}

// Scan is a struct that contains Path and Info. The type of Path and Info MUST string.
// The Scan represents one defect case.
type Scan struct {
	Path string
	Info string
}

// Deadcode is a struct that contains Path and Info. The type of Path and Info MUST string.
// The Deadcode represents one dead code.
type Deadcode struct {
	Path string
	Info string
}

// Cyclo is a struct that contains Pkg, Size and Info. The type of Info MUST []CycloInfo that represents
// detail information of all function.
type Cyclo struct {
	Pkg  string
	Size int
	Info []CycloInfo
}

// CycloInfo is a struct that contains Comp and Info. The type of Comp MUST int that represents
// the cyclo of one function.The CycloInfo represents one cyclo function information.
type CycloInfo struct {
	Comp int
	Info string
}

// Depth is a struct that contains Pkg, Size and Info. Info is an alias to CycloInfo
type Depth struct {
	Pkg  string
	Size int
	Info []DepthInfo
}

type DepthInfo CycloInfo

// CodeTest is a struct that contains Summary and Content. It represents the result data
// of the project unit test.
type CodeTest struct {
	Summary struct {
		CodeCover    float64 `json:"code_cover"`
		PackageCover float64 `json:"package_cover"`
		TotalTime    float64 `json:"total_time"`
	} `json:"summary"`
	Content struct {
		Pkg    []string  `json:"pkg"`
		Cover  []float64 `json:"cover"`
		Time   []float64 `json:"time"`
		NoTest []string  `json:"no_test"`
	} `json:"content"`
}

// Item is a struct that contains File and Content. It is the code-based infrastructure.
type Item struct {
	File    string `json:"file"`
	Content string `json:"content"`
}

// CodeTest is a struct that contains Summary and Content. It represents the result data
// of the project unit test.
type StyleItem struct {
	Label  string `json:"label"`
	Score  int    `json:"score"`
	Detail []Item `json:"detail"`

	filesNum  int
	issuesNum int
}

// CodeTest is a struct that contains Summary and Content. It represents the result data
// of the project unit test.
type CodeStyle struct {
	Summary struct {
		IssuesNum int    `json:"issues_num"`
		FilesNum  int    `json:"files_num"`
		Quality   string `json:"quality"`
	} `json:"summary"`
	Content struct {
		GoFmt     StyleItem `json:"go_fmt"`
		GoVet     StyleItem `json:"go_vet"`
		GoLint    StyleItem `json:"go_lint"`
		MissSpell StyleItem `json:"miss_spell"`
	} `json:"content"`
}

// CodeTest is a struct that contains Summary and Content. It represents the result data
// of the project unit test.
type CopyItem struct {
	Label  string     `json:"label"`
	Score  int        `json:"score"`
	Detail [][]string `json:"detail"`

	filesNum  int
	issuesNum int
}

// CodeTest is a struct that contains Summary and Content. It represents the result data
// of the project unit test.
type CodeOptimization struct {
	Summary struct {
		IssuesNum int    `json:"issues_num"`
		FilesNum  int    `json:"files_num"`
		Quality   string `json:"quality"`
	} `json:"content"`
	Content struct {
		DeadCode       StyleItem `json:"dead_code"`
		SimpleCode     StyleItem `json:"simple_code"`
		StaticCode     StyleItem `json:"static_code"`
		CopyCode       CopyItem  `json:"copy_code"`
		InterfacerCode StyleItem `json:"interfacer_code"`
	}
}

// CodeCount is a struct that contains Summary and Content. t represents the code
// statistics of the data, you can understand the project from the perspective
// of the number of lines.
type CodeCount struct {
	Summary struct {
		LineCount     int `json:"lint_count"`
		CommentCount  int `json:"comment_count"`
		FunctionCount int `json:"function_count"`
		FileCount     int `json:"file_count"`
	} `json:"summary"`
	Content struct {
		Pkg               []string `json:"pkg"`
		PkgLineCount      []int    `json:"pkg_line_count"`
		PkgCommentCount   []int    `json:"pkg_comment_count"`
		PkgFunctionCount  []int    `json:"pkg_function_count"`
		File              []string `json:"file"`
		FileLineCount     []int    `json:"file_line_count"`
		FileCommentCount  []int    `json:"file_comment_count"`
		FileFunctionCount []int    `json:"file_function_count"`
	} `json:"content"`
}

// CodeSmell is a struct that contains Summary and Content. It represents the taste of
// the code data, you can understand the project from the perspective of complexity.
type CodeSmell struct {
	Summary struct {
		IssuesNum int    `json:"issues_num"`
		FilesNum  int    `json:"files_num"`
		Quality   string `json:"quality"`
	} `json:"summary"`
	Content struct {
		Percentage map[string]int `json:"percentage"`
		Pkg        []string       `json:"pkg"`
		Cyclo      []int          `json:"cyclo"`
		List       map[string]int `json:"list"`
	} `json:"content"`
}
<commit_msg>Fix json tag<commit_after>// Copyright 2017 The GoReporter 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 engine

// UnitTest is a struct that contains AvgCover, PackagesTestDetail and
// PackagesRaceDetail. The type of AvgCover MUST string that represents
// the code coverage of the entire project. The type of PackagesTestDetail
// MUST map[string]PackageTest(contains pass-status,code-coverage and time).
// and it has all packages' detail information. PackagesRaceDetail contains
// all packages' race cases.
//
// And the UnitTest contains all packages' result.
type UnitTest struct {
	AvgCover           string                 `json:"average_cover"`
	PackagesTestDetail map[string]PackageTest `json:"packages_test_detail"`
	PackagesRaceDetail map[string][]string    `json:"packages_race_detail"`
}

// PackageTest is a struct that contains IsPass, Coverage and Time. The
// type of Time MUST float64.
type PackageTest struct {
	IsPass   bool    `json:"is_pass"`
	Coverage string  `json:"coverage"`
	Time     float64 `json:"time"`
}

// Cycloi is a struct that contains Average and Result. The Average is
// one package's cyclo coverage. And Result is the detail cyclo of the package's
// all function.
type Cycloi struct {
	Average string
	Result  []string
}

// Test is a struct that contains Path, Result, Time and Cover. The type of
// Time and Cover MUST float64. And it is just for one package's display.
type Test struct {
	Path   string
	Result int
	Time   float64
	Cover  float64
}

// File is a struct that contains Color, CycloVal and CycloInfo. And it is just
// for one file's display. The CycloInfo contains all cyclo detail information.
type File struct {
	Color     string
	CycloVal  string
	CycloInfo string
}

// Copycode is a struct that contains Files and Path. The type of Path MUST []string
// that contains more than one file path. The Copycode represents some copyed code
// information.
type Copycode struct {
	Files string
	Path  []string
}

// Race is a struct that contains Pkg, Len, Leng and Info. The type of Info MUST
// []string that represents more than one race case. Len is the number of cases.
type Race struct {
	Pkg  string
	Len  string
	Leng string
	Info []string
}

// Simple is a struct that contains Path and Info. The type of Path and Info MUST string.
// The Simple represents one can be simpled code case.
type Simple struct {
	Path string
	Info string
}

// Interfacer is a struct that contains Path and Info. The type of Path and Info MUST string.
// The Interfacer warns about the usage of types that are more specific than necessary.
type Interfacer struct {
	Path string
	Info string
}

// Spell is a struct that contains Path and Info. The type of Path and Info MUST string.
// The Spell represents one word is misspelled.
type Spell struct {
	Path string
	Info string
}

// Scan is a struct that contains Path and Info. The type of Path and Info MUST string.
// The Scan represents one defect case.
type Scan struct {
	Path string
	Info string
}

// Deadcode is a struct that contains Path and Info. The type of Path and Info MUST string.
// The Deadcode represents one dead code.
type Deadcode struct {
	Path string
	Info string
}

// Cyclo is a struct that contains Pkg, Size and Info. The type of Info MUST []CycloInfo that represents
// detail information of all function.
type Cyclo struct {
	Pkg  string
	Size int
	Info []CycloInfo
}

// CycloInfo is a struct that contains Comp and Info. The type of Comp MUST int that represents
// the cyclo of one function.The CycloInfo represents one cyclo function information.
type CycloInfo struct {
	Comp int
	Info string
}

// Depth is a struct that contains Pkg, Size and Info. Info is an alias to CycloInfo
type Depth struct {
	Pkg  string
	Size int
	Info []DepthInfo
}

type DepthInfo CycloInfo

// CodeTest is a struct that contains Summary and Content. It represents the result data
// of the project unit test.
type CodeTest struct {
	Summary struct {
		CodeCover    float64 `json:"code_cover"`
		PackageCover float64 `json:"package_cover"`
		TotalTime    float64 `json:"total_time"`
	} `json:"summary"`
	Content struct {
		Pkg    []string  `json:"pkg"`
		Cover  []float64 `json:"cover"`
		Time   []float64 `json:"time"`
		NoTest []string  `json:"no_test"`
	} `json:"content"`
}

// Item is a struct that contains File and Content. It is the code-based infrastructure.
type Item struct {
	File    string `json:"file"`
	Content string `json:"content"`
}

// CodeTest is a struct that contains Summary and Content. It represents the result data
// of the project unit test.
type StyleItem struct {
	Label  string `json:"label"`
	Score  int    `json:"score"`
	Detail []Item `json:"detail"`

	filesNum  int
	issuesNum int
}

// CodeTest is a struct that contains Summary and Content. It represents the result data
// of the project unit test.
type CodeStyle struct {
	Summary struct {
		IssuesNum int    `json:"issues_num"`
		FilesNum  int    `json:"files_num"`
		Quality   string `json:"quality"`
	} `json:"summary"`
	Content struct {
		GoFmt     StyleItem `json:"go_fmt"`
		GoVet     StyleItem `json:"go_vet"`
		GoLint    StyleItem `json:"go_lint"`
		MissSpell StyleItem `json:"miss_spell"`
	} `json:"content"`
}

// CodeTest is a struct that contains Summary and Content. It represents the result data
// of the project unit test.
type CopyItem struct {
	Label  string     `json:"label"`
	Score  int        `json:"score"`
	Detail [][]string `json:"detail"`

	filesNum  int
	issuesNum int
}

// CodeTest is a struct that contains Summary and Content. It represents the result data
// of the project unit test.
type CodeOptimization struct {
	Summary struct {
		IssuesNum int    `json:"issues_num"`
		FilesNum  int    `json:"files_num"`
		Quality   string `json:"quality"`
	} `json:"content"`
	Content struct {
		DeadCode       StyleItem `json:"dead_code"`
		SimpleCode     StyleItem `json:"simple_code"`
		StaticCode     StyleItem `json:"static_code"`
		CopyCode       CopyItem  `json:"copy_code"`
		InterfacerCode StyleItem `json:"interfacer_code"`
	} `json:"content"`
}

// CodeCount is a struct that contains Summary and Content. t represents the code
// statistics of the data, you can understand the project from the perspective
// of the number of lines.
type CodeCount struct {
	Summary struct {
		LineCount     int `json:"lint_count"`
		CommentCount  int `json:"comment_count"`
		FunctionCount int `json:"function_count"`
		FileCount     int `json:"file_count"`
	} `json:"summary"`
	Content struct {
		Pkg               []string `json:"pkg"`
		PkgLineCount      []int    `json:"pkg_line_count"`
		PkgCommentCount   []int    `json:"pkg_comment_count"`
		PkgFunctionCount  []int    `json:"pkg_function_count"`
		File              []string `json:"file"`
		FileLineCount     []int    `json:"file_line_count"`
		FileCommentCount  []int    `json:"file_comment_count"`
		FileFunctionCount []int    `json:"file_function_count"`
	} `json:"content"`
}

// CodeSmell is a struct that contains Summary and Content. It represents the taste of
// the code data, you can understand the project from the perspective of complexity.
type CodeSmell struct {
	Summary struct {
		IssuesNum int    `json:"issues_num"`
		FilesNum  int    `json:"files_num"`
		Quality   string `json:"quality"`
	} `json:"summary"`
	Content struct {
		Percentage map[string]int `json:"percentage"`
		Pkg        []string       `json:"pkg"`
		Cyclo      []int          `json:"cyclo"`
		List       map[string]int `json:"list"`
	} `json:"content"`
}
<|endoftext|>


<commit_before>package engine

import (
	"net/http"
	neturl "net/url"
	"sync"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/daohoangson/go-sitemirror/cacher"
	"github.com/daohoangson/go-sitemirror/crawler"
	"github.com/daohoangson/go-sitemirror/web"
	"github.com/tevino/abool"
)

type engine struct {
	logger *logrus.Logger
	mutex  sync.Mutex

	cacher  cacher.Cacher
	crawler crawler.Crawler
	server  web.Server

	hostRewrites        map[string]string
	hostsWhitelist      []string
	bumpTTL             time.Duration
	autoEnqueueInterval time.Duration

	autoEnqueueOnce     sync.Once
	autoEnqueueUrls     []*neturl.URL
	autoEnqueueMutex    sync.Mutex
	stopped             *abool.AtomicBool
	downloadedSomething chan interface{}
}

// New returns a new Engine instance
func New(httpClient *http.Client, logger *logrus.Logger) Engine {
	e := &engine{}
	e.init(httpClient, logger)
	return e
}

func (e *engine) init(httpClient *http.Client, logger *logrus.Logger) {
	if logger == nil {
		logger = logrus.New()
	}
	e.logger = logger

	e.cacher = cacher.NewHTTPCacher(logger)
	e.crawler = crawler.New(httpClient, logger)
	e.server = web.NewServer(e.cacher, logger)

	e.bumpTTL = time.Minute

	e.stopped = abool.New()
	e.downloadedSomething = make(chan interface{})

	e.crawler.SetURLRewriter(func(u *neturl.URL) {
		e.rewriteURLHost(u)
	})

	e.crawler.SetOnURLShouldQueue(func(u *neturl.URL) bool {
		if !e.checkHostWhitelisted(u.Host) {
			e.logger.WithFields(logrus.Fields{
				"host": u.Host,
				"list": e.hostsWhitelist,
			}).Info("Host is not whitelisted")
			return false
		}

		return true
	})

	e.crawler.SetOnURLShouldDownload(func(u *neturl.URL) bool {
		if e.cacher.CheckCacheExists(u) {
			e.logger.WithField("url", u).Info("Cache exists for url")
			return false
		}

		return true
	})

	e.crawler.SetOnDownloaded(func(downloaded *crawler.Downloaded) {
		input := BuildCacherInputFromCrawlerDownloaded(downloaded)
		e.cacher.Write(input)

		e.mutex.Lock()
		if !e.stopped.IsSet() {
			select {
			case e.downloadedSomething <- true:
			default:
			}
		}
		e.mutex.Unlock()
	})

	e.server.SetOnServerIssue(func(issue *web.ServerIssue) {
		switch issue.Type {
		case web.MethodNotAllowed:
			issue.Info.WriteBody([]byte(ResponseBodyMethodNotAllowed))
		case web.CacheNotFound:
			e.cacher.WritePlaceholder(issue.URL, e.bumpTTL)

			downloaded := e.crawler.Download(crawler.QueueItem{
				URL:           issue.URL,
				ForceDownload: true,
			})

			web.ServeDownloaded(downloaded, issue.Info)
		case web.CacheError:
			e.cacher.WritePlaceholder(issue.URL, e.bumpTTL)
			e.crawler.Enqueue(crawler.QueueItem{URL: issue.URL})
		case web.CacheExpired:
			e.cacher.Bump(issue.URL, e.bumpTTL)
			e.crawler.Enqueue(crawler.QueueItem{
				URL:           issue.URL,
				ForceDownload: true,
			})
		}
	})
}

func (e *engine) GetCacher() cacher.Cacher {
	return e.cacher
}

func (e *engine) GetCrawler() crawler.Crawler {
	return e.crawler
}

func (e *engine) GetServer() web.Server {
	return e.server
}

func (e *engine) AddHostRewrite(from string, to string) {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	if e.hostRewrites == nil {
		e.hostRewrites = make(map[string]string)
	}

	e.hostRewrites[from] = to
	e.logger.WithFields(logrus.Fields{
		"from":    from,
		"to":      to,
		"mapping": e.hostRewrites,
	}).Info("Added host rewrite")
}

func (e *engine) GetHostRewrites() map[string]string {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	hostRewrites := make(map[string]string)

	if e.hostRewrites != nil {
		for from, to := range e.hostRewrites {
			hostRewrites[from] = to
		}
	}

	return hostRewrites
}

func (e *engine) AddHostWhitelisted(host string) {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	if e.hostsWhitelist == nil {
		e.hostsWhitelist = make([]string, 1)
		e.hostsWhitelist[0] = host
	} else {
		for _, hostWhitelist := range e.hostsWhitelist {
			if hostWhitelist == host {
				e.logger.WithFields(logrus.Fields{
					"host": host,
					"list": e.hostsWhitelist,
				}).Debug("Cannot add host: already in whitelist")

				return
			}
		}

		e.hostsWhitelist = append(e.hostsWhitelist, host)
	}

	e.logger.WithFields(logrus.Fields{
		"host": host,
		"list": e.hostsWhitelist,
	}).Info("Added host into whitelist")
}

func (e *engine) GetHostsWhitelist() []string {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	hostsWhitelist := make([]string, len(e.hostsWhitelist))
	if e.hostsWhitelist != nil {
		i := 0
		for _, host := range e.hostsWhitelist {
			hostsWhitelist[i] = host
		}
	}

	return hostsWhitelist
}

func (e *engine) SetBumpTTL(ttl time.Duration) {
	e.mutex.Lock()
	e.bumpTTL = ttl
	e.mutex.Unlock()
}

func (e *engine) GetBumpTTL() time.Duration {
	e.mutex.Lock()
	ttl := e.bumpTTL
	e.mutex.Unlock()

	return ttl
}

func (e *engine) SetAutoEnqueueInterval(interval time.Duration) {
	e.mutex.Lock()
	e.autoEnqueueInterval = interval
	e.mutex.Unlock()
}

func (e *engine) GetAutoEnqueueInterval() time.Duration {
	e.mutex.Lock()
	interval := e.autoEnqueueInterval
	e.mutex.Unlock()

	return interval
}

func (e *engine) Mirror(url *neturl.URL, port int) error {
	e.logger.WithFields(logrus.Fields{
		"url":  url,
		"port": port,
	}).Info("Setting up mirror")

	e.autoEnqueue(url)
	e.crawler.Enqueue(crawler.QueueItem{URL: url})

	if port < 0 {
		return nil
	}

	_, err := e.server.ListenAndServe(url, port)

	return err
}

func (e *engine) Stop() {
	if e.stopped.IsSet() {
		return
	}

	for {
		if !e.crawler.IsBusy() {
			e.cleanUp()
			break
		}
		select {
		case <-e.downloadedSomething:
		case <-time.After(10 * time.Millisecond):
		}
	}
}

func (e *engine) autoEnqueue(url *neturl.URL) {
	e.autoEnqueueMutex.Lock()
	interval := e.autoEnqueueInterval
	e.autoEnqueueMutex.Unlock()

	if interval == 0 {
		e.logger.Debug("Engine.autoEnqueue skipped")
		return
	}

	e.autoEnqueueMutex.Lock()
	if e.autoEnqueueUrls == nil {
		e.autoEnqueueUrls = []*neturl.URL{url}
	} else {
		e.autoEnqueueUrls = append(e.autoEnqueueUrls, url)
	}
	e.autoEnqueueMutex.Unlock()

	e.autoEnqueueOnce.Do(func() {
		go func() {
			for {
				<-time.After(interval)

				if e.stopped.IsSet() {
					e.logger.Info("Engine.autoEnqueue stopped")
					return
				}

				e.autoEnqueueMutex.Lock()
				for _, url := range e.autoEnqueueUrls {
					e.GetCrawler().Enqueue(crawler.QueueItem{
						URL:           url,
						ForceDownload: true,
					})
					e.logger.WithField("url", url).Debug("Engine.autoEnqueue enqueued")
				}
				e.autoEnqueueMutex.Unlock()
			}
		}()
	})
}

func (e *engine) rewriteURLHost(url *neturl.URL) {
	e.mutex.Lock()
	hostRewrites := e.hostRewrites
	e.mutex.Unlock()

	if hostRewrites == nil {
		return
	}

	for from, to := range hostRewrites {
		if url.Host == from {
			urlBefore := url.String()
			url.Host = to

			e.logger.WithFields(logrus.Fields{
				"before": urlBefore,
				"after":  url.String(),
			}).Debug("Rewritten url host")

			return
		}
	}
}

func (e *engine) checkHostWhitelisted(host string) bool {
	e.mutex.Lock()
	hostsWhitelist := e.hostsWhitelist
	e.mutex.Unlock()

	if hostsWhitelist == nil {
		return true
	}

	for _, hostWhitelist := range hostsWhitelist {
		if host == hostWhitelist {
			return true
		}
	}

	return false
}

func (e *engine) cleanUp() {
	stoppedAtomicChange := e.stopped.SetToIf(false, true)
	if stoppedAtomicChange {
		e.crawler.Stop()
		e.server.Stop()

		e.mutex.Lock()
		close(e.downloadedSomething)
		e.mutex.Unlock()
	}
}
<commit_msg>Avoid engine processing if nil url given<commit_after>package engine

import (
	"net/http"
	neturl "net/url"
	"sync"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/daohoangson/go-sitemirror/cacher"
	"github.com/daohoangson/go-sitemirror/crawler"
	"github.com/daohoangson/go-sitemirror/web"
	"github.com/tevino/abool"
)

type engine struct {
	logger *logrus.Logger
	mutex  sync.Mutex

	cacher  cacher.Cacher
	crawler crawler.Crawler
	server  web.Server

	hostRewrites        map[string]string
	hostsWhitelist      []string
	bumpTTL             time.Duration
	autoEnqueueInterval time.Duration

	autoEnqueueOnce     sync.Once
	autoEnqueueUrls     []*neturl.URL
	autoEnqueueMutex    sync.Mutex
	stopped             *abool.AtomicBool
	downloadedSomething chan interface{}
}

// New returns a new Engine instance
func New(httpClient *http.Client, logger *logrus.Logger) Engine {
	e := &engine{}
	e.init(httpClient, logger)
	return e
}

func (e *engine) init(httpClient *http.Client, logger *logrus.Logger) {
	if logger == nil {
		logger = logrus.New()
	}
	e.logger = logger

	e.cacher = cacher.NewHTTPCacher(logger)
	e.crawler = crawler.New(httpClient, logger)
	e.server = web.NewServer(e.cacher, logger)

	e.bumpTTL = time.Minute

	e.stopped = abool.New()
	e.downloadedSomething = make(chan interface{})

	e.crawler.SetURLRewriter(func(u *neturl.URL) {
		e.rewriteURLHost(u)
	})

	e.crawler.SetOnURLShouldQueue(func(u *neturl.URL) bool {
		if !e.checkHostWhitelisted(u.Host) {
			e.logger.WithFields(logrus.Fields{
				"host": u.Host,
				"list": e.hostsWhitelist,
			}).Info("Host is not whitelisted")
			return false
		}

		return true
	})

	e.crawler.SetOnURLShouldDownload(func(u *neturl.URL) bool {
		if e.cacher.CheckCacheExists(u) {
			e.logger.WithField("url", u).Info("Cache exists for url")
			return false
		}

		return true
	})

	e.crawler.SetOnDownloaded(func(downloaded *crawler.Downloaded) {
		input := BuildCacherInputFromCrawlerDownloaded(downloaded)
		e.cacher.Write(input)

		e.mutex.Lock()
		if !e.stopped.IsSet() {
			select {
			case e.downloadedSomething <- true:
			default:
			}
		}
		e.mutex.Unlock()
	})

	e.server.SetOnServerIssue(func(issue *web.ServerIssue) {
		switch issue.Type {
		case web.MethodNotAllowed:
			issue.Info.WriteBody([]byte(ResponseBodyMethodNotAllowed))
		case web.CacheNotFound:
			e.cacher.WritePlaceholder(issue.URL, e.bumpTTL)

			downloaded := e.crawler.Download(crawler.QueueItem{
				URL:           issue.URL,
				ForceDownload: true,
			})

			web.ServeDownloaded(downloaded, issue.Info)
		case web.CacheError:
			e.cacher.WritePlaceholder(issue.URL, e.bumpTTL)
			e.crawler.Enqueue(crawler.QueueItem{URL: issue.URL})
		case web.CacheExpired:
			e.cacher.Bump(issue.URL, e.bumpTTL)
			e.crawler.Enqueue(crawler.QueueItem{
				URL:           issue.URL,
				ForceDownload: true,
			})
		}
	})
}

func (e *engine) GetCacher() cacher.Cacher {
	return e.cacher
}

func (e *engine) GetCrawler() crawler.Crawler {
	return e.crawler
}

func (e *engine) GetServer() web.Server {
	return e.server
}

func (e *engine) AddHostRewrite(from string, to string) {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	if e.hostRewrites == nil {
		e.hostRewrites = make(map[string]string)
	}

	e.hostRewrites[from] = to
	e.logger.WithFields(logrus.Fields{
		"from":    from,
		"to":      to,
		"mapping": e.hostRewrites,
	}).Info("Added host rewrite")
}

func (e *engine) GetHostRewrites() map[string]string {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	hostRewrites := make(map[string]string)

	if e.hostRewrites != nil {
		for from, to := range e.hostRewrites {
			hostRewrites[from] = to
		}
	}

	return hostRewrites
}

func (e *engine) AddHostWhitelisted(host string) {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	if e.hostsWhitelist == nil {
		e.hostsWhitelist = make([]string, 1)
		e.hostsWhitelist[0] = host
	} else {
		for _, hostWhitelist := range e.hostsWhitelist {
			if hostWhitelist == host {
				e.logger.WithFields(logrus.Fields{
					"host": host,
					"list": e.hostsWhitelist,
				}).Debug("Cannot add host: already in whitelist")

				return
			}
		}

		e.hostsWhitelist = append(e.hostsWhitelist, host)
	}

	e.logger.WithFields(logrus.Fields{
		"host": host,
		"list": e.hostsWhitelist,
	}).Info("Added host into whitelist")
}

func (e *engine) GetHostsWhitelist() []string {
	e.mutex.Lock()
	defer e.mutex.Unlock()

	hostsWhitelist := make([]string, len(e.hostsWhitelist))
	if e.hostsWhitelist != nil {
		i := 0
		for _, host := range e.hostsWhitelist {
			hostsWhitelist[i] = host
		}
	}

	return hostsWhitelist
}

func (e *engine) SetBumpTTL(ttl time.Duration) {
	e.mutex.Lock()
	e.bumpTTL = ttl
	e.mutex.Unlock()
}

func (e *engine) GetBumpTTL() time.Duration {
	e.mutex.Lock()
	ttl := e.bumpTTL
	e.mutex.Unlock()

	return ttl
}

func (e *engine) SetAutoEnqueueInterval(interval time.Duration) {
	e.mutex.Lock()
	e.autoEnqueueInterval = interval
	e.mutex.Unlock()
}

func (e *engine) GetAutoEnqueueInterval() time.Duration {
	e.mutex.Lock()
	interval := e.autoEnqueueInterval
	e.mutex.Unlock()

	return interval
}

func (e *engine) Mirror(url *neturl.URL, port int) error {
	e.logger.WithFields(logrus.Fields{
		"url":  url,
		"port": port,
	}).Info("Setting up mirror")

	if url != nil {
		e.autoEnqueue(url)
		e.crawler.Enqueue(crawler.QueueItem{URL: url})
	}

	if port < 0 {
		return nil
	}

	_, err := e.server.ListenAndServe(url, port)

	return err
}

func (e *engine) Stop() {
	if e.stopped.IsSet() {
		return
	}

	for {
		if !e.crawler.IsBusy() {
			e.cleanUp()
			break
		}
		select {
		case <-e.downloadedSomething:
		case <-time.After(10 * time.Millisecond):
		}
	}
}

func (e *engine) autoEnqueue(url *neturl.URL) {
	e.autoEnqueueMutex.Lock()
	interval := e.autoEnqueueInterval
	e.autoEnqueueMutex.Unlock()

	if interval == 0 {
		e.logger.Debug("Engine.autoEnqueue skipped")
		return
	}

	e.autoEnqueueMutex.Lock()
	if e.autoEnqueueUrls == nil {
		e.autoEnqueueUrls = []*neturl.URL{url}
	} else {
		e.autoEnqueueUrls = append(e.autoEnqueueUrls, url)
	}
	e.autoEnqueueMutex.Unlock()

	e.autoEnqueueOnce.Do(func() {
		go func() {
			for {
				<-time.After(interval)

				if e.stopped.IsSet() {
					e.logger.Info("Engine.autoEnqueue stopped")
					return
				}

				e.autoEnqueueMutex.Lock()
				for _, url := range e.autoEnqueueUrls {
					e.GetCrawler().Enqueue(crawler.QueueItem{
						URL:           url,
						ForceDownload: true,
					})
					e.logger.WithField("url", url).Debug("Engine.autoEnqueue enqueued")
				}
				e.autoEnqueueMutex.Unlock()
			}
		}()
	})
}

func (e *engine) rewriteURLHost(url *neturl.URL) {
	e.mutex.Lock()
	hostRewrites := e.hostRewrites
	e.mutex.Unlock()

	if hostRewrites == nil {
		return
	}

	for from, to := range hostRewrites {
		if url.Host == from {
			urlBefore := url.String()
			url.Host = to

			e.logger.WithFields(logrus.Fields{
				"before": urlBefore,
				"after":  url.String(),
			}).Debug("Rewritten url host")

			return
		}
	}
}

func (e *engine) checkHostWhitelisted(host string) bool {
	e.mutex.Lock()
	hostsWhitelist := e.hostsWhitelist
	e.mutex.Unlock()

	if hostsWhitelist == nil {
		return true
	}

	for _, hostWhitelist := range hostsWhitelist {
		if host == hostWhitelist {
			return true
		}
	}

	return false
}

func (e *engine) cleanUp() {
	stoppedAtomicChange := e.stopped.SetToIf(false, true)
	if stoppedAtomicChange {
		e.crawler.Stop()
		e.server.Stop()

		e.mutex.Lock()
		close(e.downloadedSomething)
		e.mutex.Unlock()
	}
}
<|endoftext|>


<commit_before>package engine

import (
	"context"
	"net"

	"github.com/pressly/chi"
	"github.com/pressly/chi/middleware"
	"github.com/ua-parser/uap-go/uaparser"
	"github.com/vaniila/hyper/cache"
	"github.com/vaniila/hyper/dataloader"
	"github.com/vaniila/hyper/fault"
	"github.com/vaniila/hyper/gws"
	"github.com/vaniila/hyper/message"
	"github.com/vaniila/hyper/router"
	"github.com/vaniila/hyper/websocket"

	"golang.org/x/net/http2"

	"net/http"
	"net/textproto"
)

type server struct {
	id         string
	addr       string
	protocol   Protocol
	cors       *cors
	cache      cache.Service
	message    message.Service
	gws        gws.Service
	dataloader dataloader.Service
	router     router.Service
	websocket  websocket.Service
	uaparser   *uaparser.Parser
	ln         *net.Listener
}

func (v *server) handleParameters(c *Context, route router.RouteConfig, params []router.Param) {
	r := c.Req()
	for _, pa := range params {
		conf := pa.Config()
		data := &Value{
			typ: conf.Type(),
			key: conf.Name(),
			fmt: conf.Format(),
		}
		switch conf.Type() {
		case router.ParamBody:
			if vs := r.Form[conf.Name()]; len(vs) > 0 {
				data.val = []byte(vs[0])
				data.has = true
			} else if vs := r.PostForm[conf.Name()]; len(vs) > 0 {
				data.val = []byte(vs[0])
				data.has = true
			}
		case router.ParamParam:
			data.val = []byte(chi.URLParam(r, conf.Name()))
			data.has = true
		case router.ParamQuery:
			if queries := r.URL.Query(); queries != nil {
				if vs, ok := queries[conf.Name()]; ok && len(vs) > 0 {
					data.val = []byte(vs[0])
					data.has = true
				}
			}
		case router.ParamHeader:
			if headers := textproto.MIMEHeader(r.Header); headers != nil {
				if vs, ok := headers[conf.Name()]; ok && len(vs) > 0 {
					data.val = []byte(vs[0])
					data.has = true
				}
			}
		case router.ParamCookie:
			if cookies := r.Cookies(); cookies != nil {
				for _, c := range cookies {
					if c != nil && c.Name == conf.Name() {
						data.val = []byte(c.Value)
						data.has = true
					}
				}
			}
		case router.ParamOneOf:
			if params := conf.OneOf(); len(params) > 0 {
				var fields []int
				var offset = len(c.values)
				v.handleParameters(c, route, params)
				for i := 0; i < len(params); i++ {
					value := c.values[i+offset]
					if value.Has() {
						fields = append(fields, i)
					}
				}
				if len(fields) > 1 {
					for _, field := range fields {
						param := params[field]
						conf := param.Config()
						warning := fault.
							For(fault.Conflict).
							SetResource(conf.Type().String()).
							SetField(conf.Name())
						c.warnings = append(c.warnings, warning)
					}
				}
			}
		}
		if len(data.val) == 0 || data.val == nil {
			if conf.Require() {
				warning := fault.
					For(fault.MissingField).
					SetResource(conf.Type().String()).
					SetField(conf.Name())
				c.warnings = append(c.warnings, warning)
			}
			data.val = conf.Default()
		}
		if len(data.val) != 0 && data.val != nil {
			custom := conf.Custom()
			depson := conf.DependsOn()
			switch parsed, ok := router.Val(conf.Format(), data.val); {
			case ok && (custom == nil || (custom != nil && custom(data.val))):
				data.parsed = parsed
				if len(depson) > 0 {
					for _, dep := range depson {
						idx := route.ValueIndex(dep)
						if len(c.values)-1 >= idx {
							if val := c.values[idx]; val == nil || !val.Has() {
								conf := dep.Config()
								warning := fault.
									For(fault.MissingField).
									SetResource(conf.Type().String()).
									SetField(conf.Name())
								c.warnings = append(c.warnings, warning)
							}
						}
					}
				}
			default:
				warning := fault.
					For(fault.Invalid).
					SetResource(conf.Type().String()).
					SetField(conf.Name())
				c.warnings = append(c.warnings, warning)
			}
		}
		c.values = append(c.values, data)
	}
}

func (v *server) handlerRoute(conf router.RouteConfig) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		client := &Client{
			req:      r,
			uaparser: v.uaparser,
		}
		c := &Context{
			machineID:       v.id,
			processID:       newID(),
			ctx:             r.Context(),
			identity:        new(identity),
			req:             r,
			res:             w,
			client:          client,
			values:          make([]router.Value, 0),
			params:          conf.Params(),
			warnings:        make([]fault.Cause, 0),
			cache:           v.cache,
			message:         v.message,
			gqlsubscription: v.gws.Adaptor(),
			dataloader:      v.dataloader.Instance(),
			uaparser:        v.uaparser,
		}
		c.ctx = context.WithValue(c.ctx, router.RequestContext, c)
		h := &Header{
			context: c,
		}
		s := &Cookie{
			context: c,
		}
		c.header = h
		c.cookie = s
		defer func() {
			if err, ok := recover().(error); ok && err != nil && !c.IsAborted() {
				c.recover = err
				if catch := conf.Catch(); catch != nil {
					catch(c)
				} else {
					c.Error(c.recover)
				}
			}
		}()
		switch r.Method {
		case "PUT", "POST", "PATCH", "CONNECT":
			r.ParseMultipartForm(conf.MaxMemory())
		}
		v.handleParameters(c, conf, conf.Params())
		if len(c.warnings) > 0 {
			err := fault.
				New("Unprocessable Entity").
				SetStatus(http.StatusUnprocessableEntity).
				AddCause(c.warnings...)
			c.Error(err)
			return
		}
		for _, md := range conf.Middlewares() {
			if !c.IsAborted() && md != nil {
				md(c)
			}
		}
		if handler := conf.Handler(); !c.IsAborted() && handler != nil {
			handler(c)
		}
	}
}

func (v *server) buildRoutes(mux *chi.Mux, routes []router.Route) {
	for _, route := range routes {
		switch conf := route.Config(); {
		case conf.Namespace():
			r := chi.NewRouter()
			v.buildRoutes(r, conf.Routes())
			mux.Mount(conf.Pattern(), r)
			for _, alias := range conf.Aliases() {
				mux.Mount(alias, r)
			}
		case conf.Method() == "GET":
			mux.Get(conf.Pattern(), v.handlerRoute(conf))
			for _, alias := range conf.Aliases() {
				mux.Get(alias, v.handlerRoute(conf))
			}
		case conf.Method() == "HEAD":
			mux.Head(conf.Pattern(), v.handlerRoute(conf))
			for _, alias := range conf.Aliases() {
				mux.Head(alias, v.handlerRoute(conf))
			}
		case conf.Method() == "OPTIONS":
			mux.Options(conf.Pattern(), v.handlerRoute(conf))
			for _, alias := range conf.Aliases() {
				mux.Options(alias, v.handlerRoute(conf))
			}
		case conf.Method() == "POST":
			mux.Post(conf.Pattern(), v.handlerRoute(conf))
			for _, alias := range conf.Aliases() {
				mux.Post(alias, v.handlerRoute(conf))
			}
		case conf.Method() == "PUT":
			mux.Put(conf.Pattern(), v.handlerRoute(conf))
			for _, alias := range conf.Aliases() {
				mux.Put(alias, v.handlerRoute(conf))
			}
		case conf.Method() == "PATCH":
			mux.Patch(conf.Pattern(), v.handlerRoute(conf))
			for _, alias := range conf.Aliases() {
				mux.Patch(alias, v.handlerRoute(conf))
			}
		case conf.Method() == "DELETE":
			mux.Delete(conf.Pattern(), v.handlerRoute(conf))
			for _, alias := range conf.Aliases() {
				mux.Delete(alias, v.handlerRoute(conf))
			}
		}
	}
	if v.websocket != nil {
		mux.Get("/_s", v.handlerRoute(
			v.router.Get("/_s").
				Name("Websocket").
				Doc(`Websocket endpoint`).
				Summary(`Websocket endpoint`).
				Handle(func(c router.Context) {
					v.websocket.Handle(c)
				}).
				Config(),
		))
	}
}

func (v *server) Start() error {

	d, err := uaparser.NewFromBytes(uas)
	if err != nil {
		return err
	}
	v.uaparser = d

	// create net listener
	ln, err := net.Listen("tcp", v.addr)
	if err != nil {
		return err
	}
	v.ln = &ln

	// create router
	mux := chi.NewRouter()
	mux.Use(middleware.RequestID)
	mux.Use(middleware.RealIP)
	mux.Use(middleware.Recoverer)
	mux.Use(middleware.DefaultCompress)
	mux.Use(middleware.Heartbeat("/healthz"))
	mux.Use(v.cors.Handler)

	v.buildRoutes(mux, v.router.Routes())

	// create http server
	srv := &http.Server{
		Addr:    v.addr,
		Handler: mux,
	}

	// enable http 2.0 if option is enabled
	if v.protocol == HTTP2 {
		http2.ConfigureServer(srv, &http2.Server{})
	}

	go srv.Serve(*v.ln)
	return nil
}

func (v *server) Stop() error {
	if v.ln != nil {
		// close net listener
		ln := *v.ln
		return ln.Close()
	}
	return nil
}

func (v *server) String() string {
	return "Hyper::Engine"
}
<commit_msg>Clear parameters and middleware for websocket route<commit_after>package engine

import (
	"context"
	"net"

	"github.com/pressly/chi"
	"github.com/pressly/chi/middleware"
	"github.com/ua-parser/uap-go/uaparser"
	"github.com/vaniila/hyper/cache"
	"github.com/vaniila/hyper/dataloader"
	"github.com/vaniila/hyper/fault"
	"github.com/vaniila/hyper/gws"
	"github.com/vaniila/hyper/message"
	"github.com/vaniila/hyper/router"
	"github.com/vaniila/hyper/websocket"

	"golang.org/x/net/http2"

	"net/http"
	"net/textproto"
)

type server struct {
	id         string
	addr       string
	protocol   Protocol
	cors       *cors
	cache      cache.Service
	message    message.Service
	gws        gws.Service
	dataloader dataloader.Service
	router     router.Service
	websocket  websocket.Service
	uaparser   *uaparser.Parser
	ln         *net.Listener
}

func (v *server) handleParameters(c *Context, route router.RouteConfig, params []router.Param) {
	r := c.Req()
	for _, pa := range params {
		conf := pa.Config()
		data := &Value{
			typ: conf.Type(),
			key: conf.Name(),
			fmt: conf.Format(),
		}
		switch conf.Type() {
		case router.ParamBody:
			if vs := r.Form[conf.Name()]; len(vs) > 0 {
				data.val = []byte(vs[0])
				data.has = true
			} else if vs := r.PostForm[conf.Name()]; len(vs) > 0 {
				data.val = []byte(vs[0])
				data.has = true
			}
		case router.ParamParam:
			data.val = []byte(chi.URLParam(r, conf.Name()))
			data.has = true
		case router.ParamQuery:
			if queries := r.URL.Query(); queries != nil {
				if vs, ok := queries[conf.Name()]; ok && len(vs) > 0 {
					data.val = []byte(vs[0])
					data.has = true
				}
			}
		case router.ParamHeader:
			if headers := textproto.MIMEHeader(r.Header); headers != nil {
				if vs, ok := headers[conf.Name()]; ok && len(vs) > 0 {
					data.val = []byte(vs[0])
					data.has = true
				}
			}
		case router.ParamCookie:
			if cookies := r.Cookies(); cookies != nil {
				for _, c := range cookies {
					if c != nil && c.Name == conf.Name() {
						data.val = []byte(c.Value)
						data.has = true
					}
				}
			}
		case router.ParamOneOf:
			if params := conf.OneOf(); len(params) > 0 {
				var fields []int
				var offset = len(c.values)
				v.handleParameters(c, route, params)
				for i := 0; i < len(params); i++ {
					value := c.values[i+offset]
					if value.Has() {
						fields = append(fields, i)
					}
				}
				if len(fields) > 1 {
					for _, field := range fields {
						param := params[field]
						conf := param.Config()
						warning := fault.
							For(fault.Conflict).
							SetResource(conf.Type().String()).
							SetField(conf.Name())
						c.warnings = append(c.warnings, warning)
					}
				}
			}
		}
		if len(data.val) == 0 || data.val == nil {
			if conf.Require() {
				warning := fault.
					For(fault.MissingField).
					SetResource(conf.Type().String()).
					SetField(conf.Name())
				c.warnings = append(c.warnings, warning)
			}
			data.val = conf.Default()
		}
		if len(data.val) != 0 && data.val != nil {
			custom := conf.Custom()
			depson := conf.DependsOn()
			switch parsed, ok := router.Val(conf.Format(), data.val); {
			case ok && (custom == nil || (custom != nil && custom(data.val))):
				data.parsed = parsed
				if len(depson) > 0 {
					for _, dep := range depson {
						idx := route.ValueIndex(dep)
						if len(c.values)-1 >= idx {
							if val := c.values[idx]; val == nil || !val.Has() {
								conf := dep.Config()
								warning := fault.
									For(fault.MissingField).
									SetResource(conf.Type().String()).
									SetField(conf.Name())
								c.warnings = append(c.warnings, warning)
							}
						}
					}
				}
			default:
				warning := fault.
					For(fault.Invalid).
					SetResource(conf.Type().String()).
					SetField(conf.Name())
				c.warnings = append(c.warnings, warning)
			}
		}
		c.values = append(c.values, data)
	}
}

func (v *server) handlerRoute(conf router.RouteConfig) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, r *http.Request) {
		client := &Client{
			req:      r,
			uaparser: v.uaparser,
		}
		c := &Context{
			machineID:       v.id,
			processID:       newID(),
			ctx:             r.Context(),
			identity:        new(identity),
			req:             r,
			res:             w,
			client:          client,
			values:          make([]router.Value, 0),
			params:          conf.Params(),
			warnings:        make([]fault.Cause, 0),
			cache:           v.cache,
			message:         v.message,
			gqlsubscription: v.gws.Adaptor(),
			dataloader:      v.dataloader.Instance(),
			uaparser:        v.uaparser,
		}
		c.ctx = context.WithValue(c.ctx, router.RequestContext, c)
		h := &Header{
			context: c,
		}
		s := &Cookie{
			context: c,
		}
		c.header = h
		c.cookie = s
		defer func() {
			if err, ok := recover().(error); ok && err != nil && !c.IsAborted() {
				c.recover = err
				if catch := conf.Catch(); catch != nil {
					catch(c)
				} else {
					c.Error(c.recover)
				}
			}
		}()
		switch r.Method {
		case "PUT", "POST", "PATCH", "CONNECT":
			r.ParseMultipartForm(conf.MaxMemory())
		}
		v.handleParameters(c, conf, conf.Params())
		if len(c.warnings) > 0 {
			err := fault.
				New("Unprocessable Entity").
				SetStatus(http.StatusUnprocessableEntity).
				AddCause(c.warnings...)
			c.Error(err)
			return
		}
		for _, md := range conf.Middlewares() {
			if !c.IsAborted() && md != nil {
				md(c)
			}
		}
		if handler := conf.Handler(); !c.IsAborted() && handler != nil {
			handler(c)
		}
	}
}

func (v *server) buildRoutes(mux *chi.Mux, routes []router.Route) {
	for _, route := range routes {
		switch conf := route.Config(); {
		case conf.Namespace():
			r := chi.NewRouter()
			v.buildRoutes(r, conf.Routes())
			mux.Mount(conf.Pattern(), r)
			for _, alias := range conf.Aliases() {
				mux.Mount(alias, r)
			}
		case conf.Method() == "GET":
			mux.Get(conf.Pattern(), v.handlerRoute(conf))
			for _, alias := range conf.Aliases() {
				mux.Get(alias, v.handlerRoute(conf))
			}
		case conf.Method() == "HEAD":
			mux.Head(conf.Pattern(), v.handlerRoute(conf))
			for _, alias := range conf.Aliases() {
				mux.Head(alias, v.handlerRoute(conf))
			}
		case conf.Method() == "OPTIONS":
			mux.Options(conf.Pattern(), v.handlerRoute(conf))
			for _, alias := range conf.Aliases() {
				mux.Options(alias, v.handlerRoute(conf))
			}
		case conf.Method() == "POST":
			mux.Post(conf.Pattern(), v.handlerRoute(conf))
			for _, alias := range conf.Aliases() {
				mux.Post(alias, v.handlerRoute(conf))
			}
		case conf.Method() == "PUT":
			mux.Put(conf.Pattern(), v.handlerRoute(conf))
			for _, alias := range conf.Aliases() {
				mux.Put(alias, v.handlerRoute(conf))
			}
		case conf.Method() == "PATCH":
			mux.Patch(conf.Pattern(), v.handlerRoute(conf))
			for _, alias := range conf.Aliases() {
				mux.Patch(alias, v.handlerRoute(conf))
			}
		case conf.Method() == "DELETE":
			mux.Delete(conf.Pattern(), v.handlerRoute(conf))
			for _, alias := range conf.Aliases() {
				mux.Delete(alias, v.handlerRoute(conf))
			}
		}
	}
	if v.websocket != nil {
		mux.Get("/_s", v.handlerRoute(
			v.router.Get("/_s").
				Name("Websocket").
				Doc(`Websocket endpoint`).
				Summary(`Websocket endpoint`).
				ClearParams().
				ClearMiddleware().
				Handle(func(c router.Context) {
					v.websocket.Handle(c)
				}).
				Config(),
		))
	}
}

func (v *server) Start() error {

	d, err := uaparser.NewFromBytes(uas)
	if err != nil {
		return err
	}
	v.uaparser = d

	// create net listener
	ln, err := net.Listen("tcp", v.addr)
	if err != nil {
		return err
	}
	v.ln = &ln

	// create router
	mux := chi.NewRouter()
	mux.Use(middleware.RequestID)
	mux.Use(middleware.RealIP)
	mux.Use(middleware.Recoverer)
	mux.Use(middleware.DefaultCompress)
	mux.Use(middleware.Heartbeat("/healthz"))
	mux.Use(v.cors.Handler)

	v.buildRoutes(mux, v.router.Routes())

	// create http server
	srv := &http.Server{
		Addr:    v.addr,
		Handler: mux,
	}

	// enable http 2.0 if option is enabled
	if v.protocol == HTTP2 {
		http2.ConfigureServer(srv, &http2.Server{})
	}

	go srv.Serve(*v.ln)
	return nil
}

func (v *server) Stop() error {
	if v.ln != nil {
		// close net listener
		ln := *v.ln
		return ln.Close()
	}
	return nil
}

func (v *server) String() string {
	return "Hyper::Engine"
}
<|endoftext|>


<commit_before>package main

import (
	"archive/zip"
	"fmt"
	"github.com/daaku/go.zipexe"
	"go/build"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"time"
)

func operationAppend(pkg *build.Package) {
	if runtime.GOOS == "windows" {
		fmt.Println("#### WARNING ! ####")
		fmt.Println("`rice append` is known not to work under windows because the `zip` command is not available. Please let me know if you got this to work (and how).")
	}

	// MARKED FOR DELETION
	// This is actually not required, the append command now has the option --exec required.
	// // check if package is a command
	// if !pkg.IsCommand() {
	// 	fmt.Println("Error: can not append to non-main package. Please follow instructions at github.com/GeertJohan/go.rice")
	// 	os.Exit(1)
	// }

	// find boxes for this command
	boxMap := findBoxes(pkg)

	// notify user when no calls to rice.FindBox are made (is this an error and therefore os.Exit(1) ?
	if len(boxMap) == 0 {
		fmt.Println("no calls to rice.FindBox() found")
	}

	verbosef("\n")

	// create tmp zipfile
	tmpZipfileName := filepath.Join(os.TempDir(), fmt.Sprintf("ricebox-%d-%s.zip", time.Now().Unix(), randomString(10)))
	verbosef("Will create tmp zipfile: %s\n", tmpZipfileName)
	tmpZipfile, err := os.Create(tmpZipfileName)
	if err != nil {
		fmt.Printf("Error creating tmp zipfile: %s\n", err)
		os.Exit(1)
	}

	// find abs path for binary file
	binfileName, err := filepath.Abs(flags.Append.Executable)
	if err != nil {
		fmt.Printf("Error finding absolute path for executable to append: %s\n", err)
		os.Exit(1)
	}
	verbosef("Will append to file: %s\n", binfileName)

	// check that command doesn't already have zip appended
	if rd, _ := zipexe.Open(binfileName); rd != nil {
		fmt.Printf("Cannot append to already appended executable. Please remove %s and build a fresh one.\n", binfileName)
		os.Exit(1)
	}

	// open binfile
	binfile, err := os.OpenFile(binfileName, os.O_WRONLY, os.ModeAppend)
	if err != nil {
		fmt.Printf("Error: unable to open executable file: %s\n", err)
		os.Exit(1)
	}

	// create zip.Writer
	zipWriter := zip.NewWriter(tmpZipfile)

	for boxname := range boxMap {
		appendedBoxName := strings.Replace(boxname, `/`, `-`, -1)

		// walk box path's and insert files
		boxPath := filepath.Clean(filepath.Join(pkg.Dir, boxname))
		filepath.Walk(boxPath, func(path string, info os.FileInfo, err error) error {
			// create zipFilename
			zipFileName := filepath.Join(appendedBoxName, strings.TrimPrefix(path, boxPath))

			// write directories as empty file with comment "dir"
			if info.IsDir() {
				_, err := zipWriter.CreateHeader(&zip.FileHeader{
					Name:    zipFileName,
					Comment: "dir",
				})
				if err != nil {
					fmt.Printf("Error creating dir in tmp zip: %s\n", err)
					os.Exit(1)
				}
				return nil
			}

			// create zipFileWriter
			zipFileWriter, err := zipWriter.Create(zipFileName)
			if err != nil {
				fmt.Printf("Error creating file in tmp zip: %s\n", err)
				os.Exit(1)
			}
			srcFile, err := os.Open(path)
			if err != nil {
				fmt.Printf("Error opening file to append: %s\n", err)
				os.Exit(1)
			}
			_, err = io.Copy(zipFileWriter, srcFile)
			if err != nil {
				fmt.Printf("Error copying file contents to zip: %s\n", err)
				os.Exit(1)
			}
			srcFile.Close()

			return nil
		})
	}

	err = zipWriter.Close()
	if err != nil {
		fmt.Printf("Error closing tmp zipfile: %s\n", err)
		os.Exit(1)
	}

	err = tmpZipfile.Sync()
	if err != nil {
		fmt.Printf("Error syncing tmp zipfile: %s\n", err)
		os.Exit(1)
	}
	_, err = tmpZipfile.Seek(0, 0)
	if err != nil {
		fmt.Printf("Error seeking tmp zipfile: %s\n", err)
		os.Exit(1)
	}
	_, err = binfile.Seek(0, 2)
	if err != nil {
		fmt.Printf("Error seeking bin file: %s\n", err)
		os.Exit(1)
	}

	_, err = io.Copy(binfile, tmpZipfile)
	if err != nil {
		fmt.Printf("Error appending zipfile to executable: %s\n", err)
		os.Exit(1)
	}

	zipA := exec.Command("zip", "-A", binfileName)
	err = zipA.Run()
	if err != nil {
		fmt.Printf("Error setting zip offset: %s\n", err)
		os.Exit(1)
	}
}
<commit_msg>Don't proceed when no calls to rice.FindBox() were found.<commit_after>package main

import (
	"archive/zip"
	"fmt"
	"github.com/daaku/go.zipexe"
	"go/build"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"time"
)

func operationAppend(pkg *build.Package) {
	if runtime.GOOS == "windows" {
		fmt.Println("#### WARNING ! ####")
		fmt.Println("`rice append` is known not to work under windows because the `zip` command is not available. Please let me know if you got this to work (and how).")
	}

	// MARKED FOR DELETION
	// This is actually not required, the append command now has the option --exec required.
	// // check if package is a command
	// if !pkg.IsCommand() {
	// 	fmt.Println("Error: can not append to non-main package. Please follow instructions at github.com/GeertJohan/go.rice")
	// 	os.Exit(1)
	// }

	// find boxes for this command
	boxMap := findBoxes(pkg)

	// notify user when no calls to rice.FindBox are made (is this an error and therefore os.Exit(1) ?
	if len(boxMap) == 0 {
		fmt.Println("no calls to rice.FindBox() found")
		return
	}

	verbosef("\n")

	// create tmp zipfile
	tmpZipfileName := filepath.Join(os.TempDir(), fmt.Sprintf("ricebox-%d-%s.zip", time.Now().Unix(), randomString(10)))
	verbosef("Will create tmp zipfile: %s\n", tmpZipfileName)
	tmpZipfile, err := os.Create(tmpZipfileName)
	if err != nil {
		fmt.Printf("Error creating tmp zipfile: %s\n", err)
		os.Exit(1)
	}

	// find abs path for binary file
	binfileName, err := filepath.Abs(flags.Append.Executable)
	if err != nil {
		fmt.Printf("Error finding absolute path for executable to append: %s\n", err)
		os.Exit(1)
	}
	verbosef("Will append to file: %s\n", binfileName)

	// check that command doesn't already have zip appended
	if rd, _ := zipexe.Open(binfileName); rd != nil {
		fmt.Printf("Cannot append to already appended executable. Please remove %s and build a fresh one.\n", binfileName)
		os.Exit(1)
	}

	// open binfile
	binfile, err := os.OpenFile(binfileName, os.O_WRONLY, os.ModeAppend)
	if err != nil {
		fmt.Printf("Error: unable to open executable file: %s\n", err)
		os.Exit(1)
	}

	// create zip.Writer
	zipWriter := zip.NewWriter(tmpZipfile)

	for boxname := range boxMap {
		appendedBoxName := strings.Replace(boxname, `/`, `-`, -1)

		// walk box path's and insert files
		boxPath := filepath.Clean(filepath.Join(pkg.Dir, boxname))
		filepath.Walk(boxPath, func(path string, info os.FileInfo, err error) error {
			// create zipFilename
			zipFileName := filepath.Join(appendedBoxName, strings.TrimPrefix(path, boxPath))

			// write directories as empty file with comment "dir"
			if info.IsDir() {
				_, err := zipWriter.CreateHeader(&zip.FileHeader{
					Name:    zipFileName,
					Comment: "dir",
				})
				if err != nil {
					fmt.Printf("Error creating dir in tmp zip: %s\n", err)
					os.Exit(1)
				}
				return nil
			}

			// create zipFileWriter
			zipFileWriter, err := zipWriter.Create(zipFileName)
			if err != nil {
				fmt.Printf("Error creating file in tmp zip: %s\n", err)
				os.Exit(1)
			}
			srcFile, err := os.Open(path)
			if err != nil {
				fmt.Printf("Error opening file to append: %s\n", err)
				os.Exit(1)
			}
			_, err = io.Copy(zipFileWriter, srcFile)
			if err != nil {
				fmt.Printf("Error copying file contents to zip: %s\n", err)
				os.Exit(1)
			}
			srcFile.Close()

			return nil
		})
	}

	err = zipWriter.Close()
	if err != nil {
		fmt.Printf("Error closing tmp zipfile: %s\n", err)
		os.Exit(1)
	}

	err = tmpZipfile.Sync()
	if err != nil {
		fmt.Printf("Error syncing tmp zipfile: %s\n", err)
		os.Exit(1)
	}
	_, err = tmpZipfile.Seek(0, 0)
	if err != nil {
		fmt.Printf("Error seeking tmp zipfile: %s\n", err)
		os.Exit(1)
	}
	_, err = binfile.Seek(0, 2)
	if err != nil {
		fmt.Printf("Error seeking bin file: %s\n", err)
		os.Exit(1)
	}

	_, err = io.Copy(binfile, tmpZipfile)
	if err != nil {
		fmt.Printf("Error appending zipfile to executable: %s\n", err)
		os.Exit(1)
	}

	zipA := exec.Command("zip", "-A", binfileName)
	err = zipA.Run()
	if err != nil {
		fmt.Printf("Error setting zip offset: %s\n", err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package scene

import (
	"fmt"
	"image"
	"sync"
	"time"

	"github.com/pankona/gomo-simra/simra"
)

type effect struct {
	game       *game
	animations map[string]*simra.AnimationSet
	effects    map[string]*simra.Sprite
	mu         sync.Mutex
}

func (e *effect) initialize() {
	e.animations = make(map[string]*simra.AnimationSet)
	e.effects = make(map[string]*simra.Sprite)

	// smoke animation
	numOfAnimation := 3
	w := 512 / numOfAnimation
	h := 528 / 4
	resource := "smoke.png"
	animationSet := simra.NewAnimationSet()
	for i := 0; i < numOfAnimation; i++ {
		animationSet.AddTexture(simra.NewImageTexture(resource,
			image.Rect((int)(w)*i, 0, ((int)(w)*(i+1))-1, int(h))))
	}
	// TODO: don't relay on time. use fps based animation control
	animationSet.SetInterval(100 * time.Millisecond)
	e.animations[resource] = animationSet

	// attack animation (1)
	numOfAnimation = 5
	w = 600 / numOfAnimation
	h = 120
	resource = "atkeffect1.png"
	animationSet = simra.NewAnimationSet()
	for i := 0; i < numOfAnimation; i++ {
		animationSet.AddTexture(simra.NewImageTexture(resource,
			image.Rect((int)(w)*i, 0, ((int)(w)*(i+1))-1, int(h))))
	}
	// TODO: don't relay on time. use fps based animation control
	animationSet.SetInterval(100 * time.Millisecond)
	e.animations[resource] = animationSet

	// attack animation (2)
	numOfAnimation = 7
	w = 840 / numOfAnimation
	h = 120
	resource = "atkeffect2.png"
	animationSet = simra.NewAnimationSet()
	for i := 0; i < numOfAnimation; i++ {
		animationSet.AddTexture(simra.NewImageTexture(resource,
			image.Rect((int)(w)*i, 0, ((int)(w)*(i+1))-1, int(h))))
	}
	// TODO: don't relay on time. use fps based animation control
	animationSet.SetInterval(100 * time.Millisecond)
	e.animations[resource] = animationSet

	// attack animation (3)
	numOfAnimation = 8
	w = 960 / numOfAnimation
	h = 120
	resource = "atkeffect3.png"
	animationSet = simra.NewAnimationSet()
	for i := 0; i < numOfAnimation; i++ {
		animationSet.AddTexture(simra.NewImageTexture(resource,
			image.Rect((int)(w)*i, 0, ((int)(w)*(i+1))-1, int(h))))
	}
	// TODO: don't relay on time. use fps based animation control
	animationSet.SetInterval(100 * time.Millisecond)
	e.animations[resource] = animationSet

	// attack animation (4)
	w = 600 / 5
	h = 120
	resource = "atkeffect4.png"
	animationSet = simra.NewAnimationSet()
	for i := 0; i < 5; i++ {
		animationSet.AddTexture(simra.NewImageTexture(resource,
			image.Rect((int)(w)*i, 0, ((int)(w)*(i+1))-1, int(h))))
	}
	for i := 0; i < 3; i++ {
		animationSet.AddTexture(simra.NewImageTexture(resource,
			image.Rect((int)(w)*i, h, ((int)(w)*(i+1))-1, int(h))))
	}
	// TODO: don't relay on time. use fps based animation control
	animationSet.SetInterval(100 * time.Millisecond)
	e.animations[resource] = animationSet

	// attack animation (5)
	w = 600 / 6
	h = 120
	resource = "atkeffect5.png"
	animationSet = simra.NewAnimationSet()
	for i := 0; i < 6; i++ {
		animationSet.AddTexture(simra.NewImageTexture(resource,
			image.Rect((int)(w)*i, 0, ((int)(w)*(i+1))-1, int(h))))
	}
	for i := 0; i < 6; i++ {
		animationSet.AddTexture(simra.NewImageTexture(resource,
			image.Rect((int)(w)*i, h, ((int)(w)*(i+1))-1, int(h))))
	}
	// TODO: don't relay on time. use fps based animation control
	animationSet.SetInterval(100 * time.Millisecond)
	e.animations[resource] = animationSet

}

func (e *effect) OnEvent(i interface{}) {
	c, ok := i.(*command)
	if !ok {
		panic("unexpected command received. fatal.")
	}

	switch c.commandtype {
	case commandSpawn:
		fallthrough
	case commandRecall:
		p, ok := c.data.(uniter)
		if !ok {
			// ignore
			break
		}
		sprite := simra.NewSprite()
		sprite.W = 512 / 3
		sprite.H = 528 / 4
		x, y := p.GetPosition()
		sprite.X, sprite.Y = x-10, y+20

		animationSet := e.animations["smoke.png"]
		sprite.AddAnimationSet("smoke.png", animationSet)
		simra.GetInstance().AddSprite2(sprite)
		doneChan := make(chan struct{})
		sprite.StartAnimation("smoke.png", true, func() {
			doneChan <- struct{}{}
		})

		effectID := fmt.Sprintf("%s_spawn", p.GetID())
		go func() {
			select {
			case <-doneChan:
			case <-time.After(10 * time.Second):
				simra.LogError("animation has not been stopped! effectID = %s_spawn\n", p.GetID())
				func() {
					e.mu.Lock()
					defer e.mu.Unlock()
					sprite = e.effects[effectID]
					delete(e.effects, effectID)
				}()
				sprite.StopAnimation()
				simra.GetInstance().RemoveSprite(sprite)
			}
		}()

		func() {
			e.mu.Lock()
			defer e.mu.Unlock()
			e.effects[effectID] = sprite
		}()

	case commandSpawned:
		fallthrough
	case commandRecalled:
		p, ok := c.data.(uniter)
		if !ok {
			// ignore
			break
		}
		effectID := fmt.Sprintf("%s_spawn", p.GetID())
		var sprite *simra.Sprite
		func() {
			e.mu.Lock()
			defer e.mu.Unlock()
			sprite = e.effects[effectID]
			delete(e.effects, effectID)
		}()
		sprite.StopAnimation()
		simra.GetInstance().RemoveSprite(sprite)

	case commandDead:
		p, ok := c.data.(uniter)
		if !ok {
			// ignore
			break
		}
		sprite := simra.NewSprite()
		sprite.W = 512 / 3
		sprite.H = 528 / 4
		x, y := p.GetPosition()
		sprite.X, sprite.Y = x-10, y+20

		animationSet := e.animations["smoke.png"]
		sprite.AddAnimationSet("smoke.png", animationSet)
		simra.GetInstance().AddSprite2(sprite)
		effectID := fmt.Sprintf("%s_dead", p.GetID())
		func() {
			e.mu.Lock()
			defer e.mu.Unlock()
			e.effects[effectID] = sprite
		}()
		sprite.StartAnimation("smoke.png", false, func() {
			e.mu.Lock()
			defer e.mu.Unlock()
			delete(e.effects, effectID)
			simra.GetInstance().RemoveSprite(sprite)
		})

	case commandAttacking:
		p, ok := c.data.(uniter)
		if !ok {
			// ignore
			break
		}
		target := p.GetTarget()
		tx, ty := target.GetPosition()

		sprite := simra.NewSprite()
		sprite.W, sprite.H = 64, 64
		sprite.X, sprite.Y = tx, ty
		var atkeffect string
		switch p.GetUnitType() {
		case "player1":
			atkeffect = "atkeffect1.png"
		case "player2":
			atkeffect = "atkeffect2.png"
		case "player3":
			atkeffect = "atkeffect3.png"
		case "enemy1":
			atkeffect = "atkeffect4.png"
		case "enemy2":
			atkeffect = "atkeffect5.png"
		default:
			simra.LogError("[%s]'s atkeffect is not loaded!", p.GetUnitType())
			panic("atkeffect is not loaded!")
		}

		animationSet := e.animations[atkeffect]
		sprite.AddAnimationSet(atkeffect, animationSet)
		simra.GetInstance().AddSprite2(sprite)
		sprite.StartAnimation(atkeffect, false, func() {
			simra.GetInstance().RemoveSprite(sprite)
		})
	}
}
<commit_msg>use fps for animation interval<commit_after>package scene

import (
	"fmt"
	"image"
	"sync"
	"time"

	"github.com/pankona/gomo-simra/simra"
)

type effect struct {
	game       *game
	animations map[string]*simra.AnimationSet
	effects    map[string]*simra.Sprite
	mu         sync.Mutex
}

func (e *effect) initialize() {
	e.animations = make(map[string]*simra.AnimationSet)
	e.effects = make(map[string]*simra.Sprite)

	// smoke animation
	numOfAnimation := 3
	w := 512 / numOfAnimation
	h := 528 / 4
	resource := "smoke.png"
	animationSet := simra.NewAnimationSet()
	for i := 0; i < numOfAnimation; i++ {
		animationSet.AddTexture(simra.NewImageTexture(resource,
			image.Rect((int)(w)*i, 0, ((int)(w)*(i+1))-1, int(h))))
	}
	animationSet.SetInterval(6)
	e.animations[resource] = animationSet

	// attack animation (1)
	numOfAnimation = 5
	w = 600 / numOfAnimation
	h = 120
	resource = "atkeffect1.png"
	animationSet = simra.NewAnimationSet()
	for i := 0; i < numOfAnimation; i++ {
		animationSet.AddTexture(simra.NewImageTexture(resource,
			image.Rect((int)(w)*i, 0, ((int)(w)*(i+1))-1, int(h))))
	}
	animationSet.SetInterval(6)
	e.animations[resource] = animationSet

	// attack animation (2)
	numOfAnimation = 7
	w = 840 / numOfAnimation
	h = 120
	resource = "atkeffect2.png"
	animationSet = simra.NewAnimationSet()
	for i := 0; i < numOfAnimation; i++ {
		animationSet.AddTexture(simra.NewImageTexture(resource,
			image.Rect((int)(w)*i, 0, ((int)(w)*(i+1))-1, int(h))))
	}
	animationSet.SetInterval(6)
	e.animations[resource] = animationSet

	// attack animation (3)
	numOfAnimation = 8
	w = 960 / numOfAnimation
	h = 120
	resource = "atkeffect3.png"
	animationSet = simra.NewAnimationSet()
	for i := 0; i < numOfAnimation; i++ {
		animationSet.AddTexture(simra.NewImageTexture(resource,
			image.Rect((int)(w)*i, 0, ((int)(w)*(i+1))-1, int(h))))
	}
	animationSet.SetInterval(6)
	e.animations[resource] = animationSet

	// attack animation (4)
	w = 600 / 5
	h = 120
	resource = "atkeffect4.png"
	animationSet = simra.NewAnimationSet()
	for i := 0; i < 5; i++ {
		animationSet.AddTexture(simra.NewImageTexture(resource,
			image.Rect((int)(w)*i, 0, ((int)(w)*(i+1))-1, int(h))))
	}
	for i := 0; i < 3; i++ {
		animationSet.AddTexture(simra.NewImageTexture(resource,
			image.Rect((int)(w)*i, h, ((int)(w)*(i+1))-1, int(h))))
	}
	animationSet.SetInterval(6)
	e.animations[resource] = animationSet

	// attack animation (5)
	w = 600 / 6
	h = 120
	resource = "atkeffect5.png"
	animationSet = simra.NewAnimationSet()
	for i := 0; i < 6; i++ {
		animationSet.AddTexture(simra.NewImageTexture(resource,
			image.Rect((int)(w)*i, 0, ((int)(w)*(i+1))-1, int(h))))
	}
	for i := 0; i < 6; i++ {
		animationSet.AddTexture(simra.NewImageTexture(resource,
			image.Rect((int)(w)*i, h, ((int)(w)*(i+1))-1, int(h))))
	}
	animationSet.SetInterval(6)
	e.animations[resource] = animationSet

}

func (e *effect) OnEvent(i interface{}) {
	c, ok := i.(*command)
	if !ok {
		panic("unexpected command received. fatal.")
	}

	switch c.commandtype {
	case commandSpawn:
		fallthrough
	case commandRecall:
		p, ok := c.data.(uniter)
		if !ok {
			// ignore
			break
		}
		sprite := simra.NewSprite()
		sprite.W = 512 / 3
		sprite.H = 528 / 4
		x, y := p.GetPosition()
		sprite.X, sprite.Y = x-10, y+20

		animationSet := e.animations["smoke.png"]
		sprite.AddAnimationSet("smoke.png", animationSet)
		simra.GetInstance().AddSprite2(sprite)
		doneChan := make(chan struct{})
		sprite.StartAnimation("smoke.png", true, func() {
			doneChan <- struct{}{}
		})

		effectID := fmt.Sprintf("%s_spawn", p.GetID())
		go func() {
			select {
			case <-doneChan:
			case <-time.After(10 * time.Second):
				simra.LogError("animation has not been stopped! effectID = %s_spawn\n", p.GetID())
				func() {
					e.mu.Lock()
					defer e.mu.Unlock()
					sprite = e.effects[effectID]
					delete(e.effects, effectID)
				}()
				sprite.StopAnimation()
				simra.GetInstance().RemoveSprite(sprite)
			}
		}()

		func() {
			e.mu.Lock()
			defer e.mu.Unlock()
			e.effects[effectID] = sprite
		}()

	case commandSpawned:
		fallthrough
	case commandRecalled:
		p, ok := c.data.(uniter)
		if !ok {
			// ignore
			break
		}
		effectID := fmt.Sprintf("%s_spawn", p.GetID())
		var sprite *simra.Sprite
		func() {
			e.mu.Lock()
			defer e.mu.Unlock()
			sprite = e.effects[effectID]
			delete(e.effects, effectID)
		}()
		sprite.StopAnimation()
		simra.GetInstance().RemoveSprite(sprite)

	case commandDead:
		p, ok := c.data.(uniter)
		if !ok {
			// ignore
			break
		}
		sprite := simra.NewSprite()
		sprite.W = 512 / 3
		sprite.H = 528 / 4
		x, y := p.GetPosition()
		sprite.X, sprite.Y = x-10, y+20

		animationSet := e.animations["smoke.png"]
		sprite.AddAnimationSet("smoke.png", animationSet)
		simra.GetInstance().AddSprite2(sprite)
		effectID := fmt.Sprintf("%s_dead", p.GetID())
		func() {
			e.mu.Lock()
			defer e.mu.Unlock()
			e.effects[effectID] = sprite
		}()
		sprite.StartAnimation("smoke.png", false, func() {
			e.mu.Lock()
			defer e.mu.Unlock()
			delete(e.effects, effectID)
			simra.GetInstance().RemoveSprite(sprite)
		})

	case commandAttacking:
		p, ok := c.data.(uniter)
		if !ok {
			// ignore
			break
		}
		target := p.GetTarget()
		tx, ty := target.GetPosition()

		sprite := simra.NewSprite()
		sprite.W, sprite.H = 64, 64
		sprite.X, sprite.Y = tx, ty
		var atkeffect string
		switch p.GetUnitType() {
		case "player1":
			atkeffect = "atkeffect1.png"
		case "player2":
			atkeffect = "atkeffect2.png"
		case "player3":
			atkeffect = "atkeffect3.png"
		case "enemy1":
			atkeffect = "atkeffect4.png"
		case "enemy2":
			atkeffect = "atkeffect5.png"
		default:
			simra.LogError("[%s]'s atkeffect is not loaded!", p.GetUnitType())
			panic("atkeffect is not loaded!")
		}

		animationSet := e.animations[atkeffect]
		sprite.AddAnimationSet(atkeffect, animationSet)
		simra.GetInstance().AddSprite2(sprite)
		sprite.StartAnimation(atkeffect, false, func() {
			simra.GetInstance().RemoveSprite(sprite)
		})
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/koding/kite"
	"fmt"
	"os"
	"sync"
	"log"
	"github.com/bitly/go-nsq"
	"runtime"
        "github.com/nsabine/microservices/controller/controllerlib"

)

var client *kite.Client
var k 	   *kite.Kite

func main() {
	fmt.Println("Starting Robber")

	k = kite.New("robber", "1.0.0")
	k.Config.Port = 6001
	k.Config.DisableAuthentication = true

        client = k.NewClient("http://" + os.Getenv("CONTROLLER_SERVICE_HOST")  + ":" + os.Getenv("CONTROLLER_SERVICE_PORT") + "/kite")
        connected, err := client.DialForever()
        if err != nil {
                k.Log.Fatal(err.Error())
        }

        // Wait until connected
        <-connected

	runtime.GOMAXPROCS(2)
	wg := &sync.WaitGroup{}
	wg.Add(2)
	go startKite()
	go startMessaging()
	wg.Wait()
}

func startKite() {
	k.HandleFunc("hello", hello)
	fmt.Println("Robber starting kite")
	k.Run()
}

func startMessaging() {
	fmt.Println("Robber configuring NSQ")

	config := nsq.NewConfig()
	q, _ := nsq.NewConsumer("tick", "ch", config)
	q.AddHandler(nsq.HandlerFunc(func(message *nsq.Message) error {
		log.Printf("Got a message: %s", string(message.Body[:]))
		tick()
		return nil
	}))

	fmt.Println("Trying to connect to NSQ: " + os.Getenv("MESSAGING_SERVICE_HOST") + ":4150")

	err := q.ConnectToNSQD(os.Getenv("MESSAGING_SERVICE_HOST") + ":4150")
	if err != nil {
		log.Panic("Could not connect")
	}
	fmt.Println("Robber starting NSQ")
}

func hello(r *kite.Request) (interface{}, error) {

	fmt.Println("Robber got hello")

	// You can return anything as result, as long as it is JSON marshalable.
	return nil, nil
}

func tick() {
	result, err := client.Tell("getState")
        var GameState [][]controllerlib.UpdateRequest
        if err := result.Unmarshal(&GameState); err != nil {
                panic(err)
        }

	x,y := controllerlib.WhereAmI(GameState, controllerlib.UpdateRequest{
		MyName: os.Getenv("HOSTNAME"),
		Type: "Robber",
		XPos: -1,
		YPos: -1,
	})

	me := controllerlib.UpdateRequest{
                MyName: os.Getenv("HOSTNAME"),
                Type: "Robber",
                XPos: x,
                YPos: y,
        }

        fmt.Println("I am here:", x, y)
	nearestCop := controllerlib.WhereNearest(GameState, me, "Cop")
	if nearestCop.XPos > me.XPos {
		x--
	} else {
		x++
	}
	if nearestCop.YPos > me.YPos {
		y--
	} else {
		y++
	}

	// make sure we didn't leave the board
	if x<0 {
		x = controllerlib.XSize
	}
	if y<0 {
		y = controllerlib.YSize
	}
	if x==controllerlib.XSize-1 {
		x = 0
	}
	if y==controllerlib.YSize-1 {
		y = 0
	}
	_, err = client.Tell("update", &controllerlib.UpdateRequest{
		MyName: os.Getenv("HOSTNAME"),
		Type: "Robber",
		XPos: x,
		YPos: y,
	})
	if err != nil {
		panic(err)
	}
}
<commit_msg>fix syntax<commit_after>package main

import (
	"github.com/koding/kite"
	"fmt"
	"os"
	"sync"
	"log"
	"github.com/bitly/go-nsq"
	"runtime"
        "github.com/nsabine/microservices/controller/controllerlib"

)

var client *kite.Client
var k 	   *kite.Kite

func main() {
	fmt.Println("Starting Robber")

	k = kite.New("robber", "1.0.0")
	k.Config.Port = 6001
	k.Config.DisableAuthentication = true

        client = k.NewClient("http://" + os.Getenv("CONTROLLER_SERVICE_HOST")  + ":" + os.Getenv("CONTROLLER_SERVICE_PORT") + "/kite")
        connected, err := client.DialForever()
        if err != nil {
                k.Log.Fatal(err.Error())
        }

        // Wait until connected
        <-connected

	runtime.GOMAXPROCS(2)
	wg := &sync.WaitGroup{}
	wg.Add(2)
	go startKite()
	go startMessaging()
	wg.Wait()
}

func startKite() {
	k.HandleFunc("hello", hello)
	fmt.Println("Robber starting kite")
	k.Run()
}

func startMessaging() {
	fmt.Println("Robber configuring NSQ")

	config := nsq.NewConfig()
	q, _ := nsq.NewConsumer("tick", "ch", config)
	q.AddHandler(nsq.HandlerFunc(func(message *nsq.Message) error {
		log.Printf("Got a message: %s", string(message.Body[:]))
		tick()
		return nil
	}))

	fmt.Println("Trying to connect to NSQ: " + os.Getenv("MESSAGING_SERVICE_HOST") + ":4150")

	err := q.ConnectToNSQD(os.Getenv("MESSAGING_SERVICE_HOST") + ":4150")
	if err != nil {
		log.Panic("Could not connect")
	}
	fmt.Println("Robber starting NSQ")
}

func hello(r *kite.Request) (interface{}, error) {

	fmt.Println("Robber got hello")

	// You can return anything as result, as long as it is JSON marshalable.
	return nil, nil
}

func tick() {
	result, err := client.Tell("getState")
        var GameState [][]controllerlib.UpdateRequest
        if err := result.Unmarshal(&GameState); err != nil {
                panic(err)
        }

	x,y := controllerlib.WhereAmI(GameState, controllerlib.UpdateRequest{
		MyName: os.Getenv("HOSTNAME"),
		Type: "Robber",
		XPos: -1,
		YPos: -1,
	})

	me := controllerlib.UpdateRequest{
                MyName: os.Getenv("HOSTNAME"),
                Type: "Robber",
                XPos: x,
                YPos: y,
        }

        fmt.Println("I am here:", x, y)
	nearestCop := controllerlib.WhereNearest(GameState, me, "Cop")
	if nearestCop.XPos > me.XPos {
		x--
	} else {
		x++
	}
	if nearestCop.YPos > me.YPos {
		y--
	} else {
		y++
	}

	// make sure we didn't leave the board
	if x<0 {
		x = controllerlib.XSize-1
	}
	if y<0 {
		y = controllerlib.YSize-1
	}
	if x==controllerlib.XSize-1 {
		x = 0
	}
	if y==controllerlib.YSize-1 {
		y = 0
	}
	_, err = client.Tell("update", &controllerlib.UpdateRequest{
		MyName: os.Getenv("HOSTNAME"),
		Type: "Robber",
		XPos: x,
		YPos: y,
	})
	if err != nil {
		panic(err)
	}
}
<|endoftext|>