<commit_before>// Copyright (c) 2013 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 or at
// https://developers.google.com/open-source/licenses/bsd.

// Package lintutil provides helpers for writing linter command lines.
package lintutil // import "honnef.co/go/tools/lint/lintutil"

import (
	"errors"
	"flag"
	"fmt"
	"go/build"
	"go/types"
	"log"
	"os"
	"runtime/pprof"
	"strconv"
	"strings"
	"time"

	"honnef.co/go/tools/config"
	"honnef.co/go/tools/lint"
	"honnef.co/go/tools/lint/lintutil/format"
	"honnef.co/go/tools/version"

	"golang.org/x/tools/go/packages"
)

func usage(name string, flags *flag.FlagSet) func() {
	return func() {
		fmt.Fprintf(os.Stderr, "Usage of %s:\n", name)
		fmt.Fprintf(os.Stderr, "\t%s [flags] # runs on package in current directory\n", name)
		fmt.Fprintf(os.Stderr, "\t%s [flags] packages\n", name)
		fmt.Fprintf(os.Stderr, "\t%s [flags] directory\n", name)
		fmt.Fprintf(os.Stderr, "\t%s [flags] files... # must be a single package\n", name)
		fmt.Fprintf(os.Stderr, "Flags:\n")
		flags.PrintDefaults()
	}
}

func parseIgnore(s string) ([]lint.Ignore, error) {
	var out []lint.Ignore
	if len(s) == 0 {
		return nil, nil
	}
	for _, part := range strings.Fields(s) {
		p := strings.Split(part, ":")
		if len(p) != 2 {
			return nil, errors.New("malformed ignore string")
		}
		path := p[0]
		checks := strings.Split(p[1], ",")
		out = append(out, &lint.GlobIgnore{Pattern: path, Checks: checks})
	}
	return out, nil
}

type versionFlag int

func (v *versionFlag) String() string {
	return fmt.Sprintf("1.%d", *v)
}

func (v *versionFlag) Set(s string) error {
	if len(s) < 3 {
		return errors.New("invalid Go version")
	}
	if s[0] != '1' {
		return errors.New("invalid Go version")
	}
	if s[1] != '.' {
		return errors.New("invalid Go version")
	}
	i, err := strconv.Atoi(s[2:])
	*v = versionFlag(i)
	return err
}

func (v *versionFlag) Get() interface{} {
	return int(*v)
}

type list []string

func (list *list) String() string {
	return `"` + strings.Join(*list, ",") + `"`
}

func (list *list) Set(s string) error {
	if s == "" {
		*list = nil
		return nil
	}

	*list = strings.Split(s, ",")
	return nil
}

func FlagSet(name string) *flag.FlagSet {
	flags := flag.NewFlagSet("", flag.ExitOnError)
	flags.Usage = usage(name, flags)
	flags.String("tags", "", "List of `build tags`")
	flags.String("ignore", "", "Deprecated: use linter directives instead")
	flags.Bool("tests", true, "Include tests")
	flags.Bool("version", false, "Print version and exit")
	flags.Bool("show-ignored", false, "Don't filter ignored problems")
	flags.String("f", "text", "Output `format` (valid choices are 'text' and 'json')")

	flags.Int("debug.max-concurrent-jobs", 0, "Number of jobs to run concurrently")
	flags.Bool("debug.print-stats", false, "Print debug statistics")
	flags.String("debug.cpuprofile", "", "Write CPU profile to `file`")

	checks := list{"inherit"}
	fail := list{"all"}
	flags.Var(&checks, "checks", "Comma-separated list of `checks` to enable.")
	flags.Var(&fail, "fail", "Comma-separated list of `checks` that can cause a non-zero exit status.")

	tags := build.Default.ReleaseTags
	v := tags[len(tags)-1][2:]
	version := new(versionFlag)
	if err := version.Set(v); err != nil {
		panic(fmt.Sprintf("internal error: %s", err))
	}

	flags.Var(version, "go", "Target Go `version` in the format '1.x'")
	return flags
}

func ProcessFlagSet(cs []lint.Checker, fs *flag.FlagSet) {
	tags := fs.Lookup("tags").Value.(flag.Getter).Get().(string)
	ignore := fs.Lookup("ignore").Value.(flag.Getter).Get().(string)
	tests := fs.Lookup("tests").Value.(flag.Getter).Get().(bool)
	goVersion := fs.Lookup("go").Value.(flag.Getter).Get().(int)
	formatter := fs.Lookup("f").Value.(flag.Getter).Get().(string)
	printVersion := fs.Lookup("version").Value.(flag.Getter).Get().(bool)
	showIgnored := fs.Lookup("show-ignored").Value.(flag.Getter).Get().(bool)

	maxConcurrentJobs := fs.Lookup("debug.max-concurrent-jobs").Value.(flag.Getter).Get().(int)
	printStats := fs.Lookup("debug.print-stats").Value.(flag.Getter).Get().(bool)
	cpuProfile := fs.Lookup("debug.cpuprofile").Value.(flag.Getter).Get().(string)

	cfg := config.Config{}
	cfg.Checks = *fs.Lookup("checks").Value.(*list)

	exit := os.Exit
	if cpuProfile != "" {
		f, err := os.Create(cpuProfile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		exit = func(code int) {
			pprof.StopCPUProfile()
			os.Exit(code)
		}
	}

	if printVersion {
		version.Print()
		exit(0)
	}

	ps, err := Lint(cs, fs.Args(), &Options{
		Tags:          strings.Fields(tags),
		LintTests:     tests,
		Ignores:       ignore,
		GoVersion:     goVersion,
		ReturnIgnored: showIgnored,
		Config:        cfg,

		MaxConcurrentJobs: maxConcurrentJobs,
		PrintStats:        printStats,
	})
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		exit(1)
	}

	var f format.Formatter
	switch formatter {
	case "text":
		f = format.Text{W: os.Stdout}
	case "stylish":
		f = &format.Stylish{W: os.Stdout}
	case "json":
		f = format.JSON{W: os.Stdout}
	default:
		fmt.Fprintf(os.Stderr, "unsupported output format %q\n", formatter)
		exit(2)
	}

	var (
		total    int
		errors   int
		warnings int
	)

	fail := *fs.Lookup("fail").Value.(*list)
	var allChecks []string
	for _, p := range ps {
		allChecks = append(allChecks, p.Check)
	}

	shouldExit := lint.FilterChecks(allChecks, fail)

	total = len(ps)
	for _, p := range ps {
		if shouldExit[p.Check] {
			errors++
		} else {
			p.Severity = lint.Warning
			warnings++
		}
		f.Format(p)
	}
	if f, ok := f.(format.Statter); ok {
		f.Stats(total, errors, warnings)
	}
	if errors > 0 {
		exit(1)
	}
}

type Options struct {
	Config config.Config

	Tags          []string
	LintTests     bool
	Ignores       string
	GoVersion     int
	ReturnIgnored bool

	MaxConcurrentJobs int
	PrintStats        bool
}

func Lint(cs []lint.Checker, paths []string, opt *Options) ([]lint.Problem, error) {
	stats := lint.PerfStats{
		CheckerInits: map[string]time.Duration{},
	}

	if opt == nil {
		opt = &Options{}
	}
	ignores, err := parseIgnore(opt.Ignores)
	if err != nil {
		return nil, err
	}

	ctx := build.Default
	// XXX nothing cares about built tags right now
	ctx.BuildTags = opt.Tags
	conf := &packages.Config{
		Mode:  packages.LoadAllSyntax,
		Tests: opt.LintTests,
		Error: func(err error) {},
	}

	t := time.Now()
	if len(paths) == 0 {
		paths = []string{"."}
	}
	pkgs, err := packages.Load(conf, paths...)
	if err != nil {
		return nil, err
	}
	stats.PackageLoading = time.Since(t)

	var problems []lint.Problem
	workingPkgs := make([]*packages.Package, 0, len(pkgs))
	for _, pkg := range pkgs {
		if pkg.IllTyped {
			problems = append(problems, compileErrors(pkg)...)
		} else {
			workingPkgs = append(workingPkgs, pkg)
		}
	}

	if len(workingPkgs) == 0 {
		return problems, nil
	}

	l := &lint.Linter{
		Checkers:      cs,
		Ignores:       ignores,
		GoVersion:     opt.GoVersion,
		ReturnIgnored: opt.ReturnIgnored,
		Config:        opt.Config,

		MaxConcurrentJobs: opt.MaxConcurrentJobs,
		PrintStats:        opt.PrintStats,
	}
	problems = append(problems, l.Lint(workingPkgs, &stats)...)

	return problems, nil
}

func compileErrors(pkg *packages.Package) []lint.Problem {
	if !pkg.IllTyped {
		return nil
	}
	if len(pkg.Errors) == 0 {
		// transitively ill-typed
		var ps []lint.Problem
		for _, imp := range pkg.Imports {
			ps = append(ps, compileErrors(imp)...)
		}
		return ps
	}
	var ps []lint.Problem
	for _, err := range pkg.Errors {
		var p lint.Problem
		switch err := err.(type) {
		case types.Error:
			p = lint.Problem{
				Position: err.Fset.Position(err.Pos),
				Text:     err.Msg,
				Checker:  "compiler",
				Check:    "compile",
			}
		default:
			fmt.Fprintf(os.Stderr, "internal error: unhandled error type %T\n", err)
		}
		ps = append(ps, p)
	}
	return ps
}

func ProcessArgs(name string, cs []lint.Checker, args []string) {
	flags := FlagSet(name)
	flags.Parse(args)

	ProcessFlagSet(cs, flags)
}
<commit_msg>lint/lintutil: include Stylish formatter in help text<commit_after>// Copyright (c) 2013 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 or at
// https://developers.google.com/open-source/licenses/bsd.

// Package lintutil provides helpers for writing linter command lines.
package lintutil // import "honnef.co/go/tools/lint/lintutil"

import (
	"errors"
	"flag"
	"fmt"
	"go/build"
	"go/types"
	"log"
	"os"
	"runtime/pprof"
	"strconv"
	"strings"
	"time"

	"honnef.co/go/tools/config"
	"honnef.co/go/tools/lint"
	"honnef.co/go/tools/lint/lintutil/format"
	"honnef.co/go/tools/version"

	"golang.org/x/tools/go/packages"
)

func usage(name string, flags *flag.FlagSet) func() {
	return func() {
		fmt.Fprintf(os.Stderr, "Usage of %s:\n", name)
		fmt.Fprintf(os.Stderr, "\t%s [flags] # runs on package in current directory\n", name)
		fmt.Fprintf(os.Stderr, "\t%s [flags] packages\n", name)
		fmt.Fprintf(os.Stderr, "\t%s [flags] directory\n", name)
		fmt.Fprintf(os.Stderr, "\t%s [flags] files... # must be a single package\n", name)
		fmt.Fprintf(os.Stderr, "Flags:\n")
		flags.PrintDefaults()
	}
}

func parseIgnore(s string) ([]lint.Ignore, error) {
	var out []lint.Ignore
	if len(s) == 0 {
		return nil, nil
	}
	for _, part := range strings.Fields(s) {
		p := strings.Split(part, ":")
		if len(p) != 2 {
			return nil, errors.New("malformed ignore string")
		}
		path := p[0]
		checks := strings.Split(p[1], ",")
		out = append(out, &lint.GlobIgnore{Pattern: path, Checks: checks})
	}
	return out, nil
}

type versionFlag int

func (v *versionFlag) String() string {
	return fmt.Sprintf("1.%d", *v)
}

func (v *versionFlag) Set(s string) error {
	if len(s) < 3 {
		return errors.New("invalid Go version")
	}
	if s[0] != '1' {
		return errors.New("invalid Go version")
	}
	if s[1] != '.' {
		return errors.New("invalid Go version")
	}
	i, err := strconv.Atoi(s[2:])
	*v = versionFlag(i)
	return err
}

func (v *versionFlag) Get() interface{} {
	return int(*v)
}

type list []string

func (list *list) String() string {
	return `"` + strings.Join(*list, ",") + `"`
}

func (list *list) Set(s string) error {
	if s == "" {
		*list = nil
		return nil
	}

	*list = strings.Split(s, ",")
	return nil
}

func FlagSet(name string) *flag.FlagSet {
	flags := flag.NewFlagSet("", flag.ExitOnError)
	flags.Usage = usage(name, flags)
	flags.String("tags", "", "List of `build tags`")
	flags.String("ignore", "", "Deprecated: use linter directives instead")
	flags.Bool("tests", true, "Include tests")
	flags.Bool("version", false, "Print version and exit")
	flags.Bool("show-ignored", false, "Don't filter ignored problems")
	flags.String("f", "text", "Output `format` (valid choices are 'stylish', 'text' and 'json')")

	flags.Int("debug.max-concurrent-jobs", 0, "Number of jobs to run concurrently")
	flags.Bool("debug.print-stats", false, "Print debug statistics")
	flags.String("debug.cpuprofile", "", "Write CPU profile to `file`")

	checks := list{"inherit"}
	fail := list{"all"}
	flags.Var(&checks, "checks", "Comma-separated list of `checks` to enable.")
	flags.Var(&fail, "fail", "Comma-separated list of `checks` that can cause a non-zero exit status.")

	tags := build.Default.ReleaseTags
	v := tags[len(tags)-1][2:]
	version := new(versionFlag)
	if err := version.Set(v); err != nil {
		panic(fmt.Sprintf("internal error: %s", err))
	}

	flags.Var(version, "go", "Target Go `version` in the format '1.x'")
	return flags
}

func ProcessFlagSet(cs []lint.Checker, fs *flag.FlagSet) {
	tags := fs.Lookup("tags").Value.(flag.Getter).Get().(string)
	ignore := fs.Lookup("ignore").Value.(flag.Getter).Get().(string)
	tests := fs.Lookup("tests").Value.(flag.Getter).Get().(bool)
	goVersion := fs.Lookup("go").Value.(flag.Getter).Get().(int)
	formatter := fs.Lookup("f").Value.(flag.Getter).Get().(string)
	printVersion := fs.Lookup("version").Value.(flag.Getter).Get().(bool)
	showIgnored := fs.Lookup("show-ignored").Value.(flag.Getter).Get().(bool)

	maxConcurrentJobs := fs.Lookup("debug.max-concurrent-jobs").Value.(flag.Getter).Get().(int)
	printStats := fs.Lookup("debug.print-stats").Value.(flag.Getter).Get().(bool)
	cpuProfile := fs.Lookup("debug.cpuprofile").Value.(flag.Getter).Get().(string)

	cfg := config.Config{}
	cfg.Checks = *fs.Lookup("checks").Value.(*list)

	exit := os.Exit
	if cpuProfile != "" {
		f, err := os.Create(cpuProfile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		exit = func(code int) {
			pprof.StopCPUProfile()
			os.Exit(code)
		}
	}

	if printVersion {
		version.Print()
		exit(0)
	}

	ps, err := Lint(cs, fs.Args(), &Options{
		Tags:          strings.Fields(tags),
		LintTests:     tests,
		Ignores:       ignore,
		GoVersion:     goVersion,
		ReturnIgnored: showIgnored,
		Config:        cfg,

		MaxConcurrentJobs: maxConcurrentJobs,
		PrintStats:        printStats,
	})
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		exit(1)
	}

	var f format.Formatter
	switch formatter {
	case "text":
		f = format.Text{W: os.Stdout}
	case "stylish":
		f = &format.Stylish{W: os.Stdout}
	case "json":
		f = format.JSON{W: os.Stdout}
	default:
		fmt.Fprintf(os.Stderr, "unsupported output format %q\n", formatter)
		exit(2)
	}

	var (
		total    int
		errors   int
		warnings int
	)

	fail := *fs.Lookup("fail").Value.(*list)
	var allChecks []string
	for _, p := range ps {
		allChecks = append(allChecks, p.Check)
	}

	shouldExit := lint.FilterChecks(allChecks, fail)

	total = len(ps)
	for _, p := range ps {
		if shouldExit[p.Check] {
			errors++
		} else {
			p.Severity = lint.Warning
			warnings++
		}
		f.Format(p)
	}
	if f, ok := f.(format.Statter); ok {
		f.Stats(total, errors, warnings)
	}
	if errors > 0 {
		exit(1)
	}
}

type Options struct {
	Config config.Config

	Tags          []string
	LintTests     bool
	Ignores       string
	GoVersion     int
	ReturnIgnored bool

	MaxConcurrentJobs int
	PrintStats        bool
}

func Lint(cs []lint.Checker, paths []string, opt *Options) ([]lint.Problem, error) {
	stats := lint.PerfStats{
		CheckerInits: map[string]time.Duration{},
	}

	if opt == nil {
		opt = &Options{}
	}
	ignores, err := parseIgnore(opt.Ignores)
	if err != nil {
		return nil, err
	}

	ctx := build.Default
	// XXX nothing cares about built tags right now
	ctx.BuildTags = opt.Tags
	conf := &packages.Config{
		Mode:  packages.LoadAllSyntax,
		Tests: opt.LintTests,
		Error: func(err error) {},
	}

	t := time.Now()
	if len(paths) == 0 {
		paths = []string{"."}
	}
	pkgs, err := packages.Load(conf, paths...)
	if err != nil {
		return nil, err
	}
	stats.PackageLoading = time.Since(t)

	var problems []lint.Problem
	workingPkgs := make([]*packages.Package, 0, len(pkgs))
	for _, pkg := range pkgs {
		if pkg.IllTyped {
			problems = append(problems, compileErrors(pkg)...)
		} else {
			workingPkgs = append(workingPkgs, pkg)
		}
	}

	if len(workingPkgs) == 0 {
		return problems, nil
	}

	l := &lint.Linter{
		Checkers:      cs,
		Ignores:       ignores,
		GoVersion:     opt.GoVersion,
		ReturnIgnored: opt.ReturnIgnored,
		Config:        opt.Config,

		MaxConcurrentJobs: opt.MaxConcurrentJobs,
		PrintStats:        opt.PrintStats,
	}
	problems = append(problems, l.Lint(workingPkgs, &stats)...)

	return problems, nil
}

func compileErrors(pkg *packages.Package) []lint.Problem {
	if !pkg.IllTyped {
		return nil
	}
	if len(pkg.Errors) == 0 {
		// transitively ill-typed
		var ps []lint.Problem
		for _, imp := range pkg.Imports {
			ps = append(ps, compileErrors(imp)...)
		}
		return ps
	}
	var ps []lint.Problem
	for _, err := range pkg.Errors {
		var p lint.Problem
		switch err := err.(type) {
		case types.Error:
			p = lint.Problem{
				Position: err.Fset.Position(err.Pos),
				Text:     err.Msg,
				Checker:  "compiler",
				Check:    "compile",
			}
		default:
			fmt.Fprintf(os.Stderr, "internal error: unhandled error type %T\n", err)
		}
		ps = append(ps, p)
	}
	return ps
}

func ProcessArgs(name string, cs []lint.Checker, args []string) {
	flags := FlagSet(name)
	flags.Parse(args)

	ProcessFlagSet(cs, flags)
}
<|endoftext|>


<commit_before>package worker

import (
	"bufio"
	"disco/jobutil"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"os"
)

const (
	DEBUG = true
)

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

func debug(prefix string, msg interface{}) {
	if DEBUG {
		file, err := os.OpenFile("/tmp/debug", os.O_RDWR|os.O_APPEND|os.O_CREATE, 0644)
		Check(err)
		defer file.Close()
		fmt.Fprintf(file, "%s: %v\n", prefix, msg)
	}
}

func send(key string, payload interface{}) {
	enc, err := json.Marshal(payload)
	if err != nil {
		panic("could not encode")
	}
	str := fmt.Sprintf("%s %d %s\n", key, len(enc), enc)
	fmt.Printf(str)
	debug("send", str)
}

func recv() (string, int, []byte) {
	var size int
	var status string
	fmt.Scanf("%s %d", &status, &size)
	reader := bufio.NewReader(os.Stdin)
	input := make([]byte, size)
	reader.Read(input)
	debug("recv", fmt.Sprintf("%d ", size)+string(input))
	return status, size, input
}

func send_worker() {
	type WorkerMsg struct {
		Pid     int    `json:"pid"`
		Version string `json:"version"`
	}
	wm := WorkerMsg{os.Getpid(), "1.1"}
	send("WORKER", wm)

	_, _, response := recv()
	if string(response) != "\"ok\"" {
		panic(response)
	}
}

func request_task() *Task {
	task := new(Task)
	send("TASK", "")
	_, _, line := recv()
	json.Unmarshal(line, &task)
	debug("info", task)
	return task
}

func request_input() *Input {
	send("INPUT", "")
	_, _, line := recv()
	var mj []interface{}
	json.Unmarshal(line, &mj)

	flag := mj[0].(string)
	if flag != "done" {
		panic(flag)
	}
	_inputs := mj[1].([]interface{})
	inputs := _inputs[0].([]interface{})

	id := inputs[0].(float64)
	status := inputs[1].(string)

	label := -1
	switch t := inputs[2].(type) {
	case string:
		label = -1
	case float64:
		label = int(t)
	}
	_replicas := inputs[3].([]interface{})

	replicas := _replicas[0].([]interface{})

	//FIXME avoid conversion to float when reading the item
	replica_id := replicas[0].(float64)
	replica_location := replicas[1].(string)

	debug("info", fmt.Sprintln(id, status, label, replica_id, replica_location))

	input := new(Input)
	input.id = int(id)
	input.status = status
	input.label = label
	input.replica_id = int(replica_id)
	input.replica_location = replica_location
	return input
}

func send_output(output *Output) {
	v := make([]interface{}, 3)
	v[0] = output.label
	v[1] = output.output_location //"http://example.com"
	v[2] = output.output_size

	send("OUTPUT", v)
	_, _, line := recv()
	debug("info", string(line))
}

func request_done() {
	send("DONE", "")
	_, _, line := recv()
	debug("info", string(line))
}

type Task struct {
	Host       string
	Master     string
	Jobname    string
	Taskid     int
	Stage      string
	Grouping   string
	Group      string
	Disco_port int
	Put_port   int
	Disco_data string
	Ddfs_data  string
	Jobfile    string
}

type Input struct {
	id               int
	status           string
	label            int
	replica_id       int
	replica_location string
}

type Output struct {
	label           int
	output_location string
	output_size     int64
}

type Worker struct {
	task   *Task
	input  *Input
	output *Output
}

type Process func(string, io.Writer, *Task)

func (w *Worker) runStage(output_name string, process Process) {
	output, err := os.Create(output_name)
	Check(err)
	process(w.input.replica_location, output, w.task)
	output.Close()
	w.output.output_location = "disco://" + output_name[len(w.task.Disco_data)+1:]
	output, err = os.Open(output_name)
	Check(err)
	fileinfo, err := output.Stat()
	Check(err)
	w.output.output_size = fileinfo.Size()
}

func Run(Map Process, Reduce Process) {
	var w Worker
	send_worker()
	w.task = request_task()

	jobutil.SetKeyValue("HOST", w.task.Host)
	master, port := jobutil.HostAndPort(w.task.Master)
	jobutil.SetKeyValue("DISCO_MASTER", master)
	if port != fmt.Sprintf("%d", w.task.Disco_port) {
		panic("port mismatch: " + port)
	}
	jobutil.SetKeyValue("DISCO_PORT", port)
	jobutil.SetKeyValue("PUT_PORT", string(w.task.Put_port))
	jobutil.SetKeyValue("DISCO_DATA", w.task.Disco_data)
	jobutil.SetKeyValue("DDFS_DATA", w.task.Ddfs_data)

	w.input = request_input()

	pwd, err := os.Getwd()
	Check(err)

	w.output = new(Output)
	if w.task.Stage == "map" {
		w.runStage(pwd+"/map_out", Map)
	} else if w.task.Stage == "map_shuffle" {
		w.output.output_location = w.input.replica_location
	} else {
		w.runStage(pwd+"/reduce_out", Reduce)
	}

	send_output(w.output)
	request_done()
}
<commit_msg>fix an import issue.<commit_after>package worker

import (
	"bufio"
	"github.com/discoproject/goworker/jobutil"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"os"
)

const (
	DEBUG = true
)

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

func debug(prefix string, msg interface{}) {
	if DEBUG {
		file, err := os.OpenFile("/tmp/debug", os.O_RDWR|os.O_APPEND|os.O_CREATE, 0644)
		Check(err)
		defer file.Close()
		fmt.Fprintf(file, "%s: %v\n", prefix, msg)
	}
}

func send(key string, payload interface{}) {
	enc, err := json.Marshal(payload)
	if err != nil {
		panic("could not encode")
	}
	str := fmt.Sprintf("%s %d %s\n", key, len(enc), enc)
	fmt.Printf(str)
	debug("send", str)
}

func recv() (string, int, []byte) {
	var size int
	var status string
	fmt.Scanf("%s %d", &status, &size)
	reader := bufio.NewReader(os.Stdin)
	input := make([]byte, size)
	reader.Read(input)
	debug("recv", fmt.Sprintf("%d ", size)+string(input))
	return status, size, input
}

func send_worker() {
	type WorkerMsg struct {
		Pid     int    `json:"pid"`
		Version string `json:"version"`
	}
	wm := WorkerMsg{os.Getpid(), "1.1"}
	send("WORKER", wm)

	_, _, response := recv()
	if string(response) != "\"ok\"" {
		panic(response)
	}
}

func request_task() *Task {
	task := new(Task)
	send("TASK", "")
	_, _, line := recv()
	json.Unmarshal(line, &task)
	debug("info", task)
	return task
}

func request_input() *Input {
	send("INPUT", "")
	_, _, line := recv()
	var mj []interface{}
	json.Unmarshal(line, &mj)

	flag := mj[0].(string)
	if flag != "done" {
		panic(flag)
	}
	_inputs := mj[1].([]interface{})
	inputs := _inputs[0].([]interface{})

	id := inputs[0].(float64)
	status := inputs[1].(string)

	label := -1
	switch t := inputs[2].(type) {
	case string:
		label = -1
	case float64:
		label = int(t)
	}
	_replicas := inputs[3].([]interface{})

	replicas := _replicas[0].([]interface{})

	//FIXME avoid conversion to float when reading the item
	replica_id := replicas[0].(float64)
	replica_location := replicas[1].(string)

	debug("info", fmt.Sprintln(id, status, label, replica_id, replica_location))

	input := new(Input)
	input.id = int(id)
	input.status = status
	input.label = label
	input.replica_id = int(replica_id)
	input.replica_location = replica_location
	return input
}

func send_output(output *Output) {
	v := make([]interface{}, 3)
	v[0] = output.label
	v[1] = output.output_location //"http://example.com"
	v[2] = output.output_size

	send("OUTPUT", v)
	_, _, line := recv()
	debug("info", string(line))
}

func request_done() {
	send("DONE", "")
	_, _, line := recv()
	debug("info", string(line))
}

type Task struct {
	Host       string
	Master     string
	Jobname    string
	Taskid     int
	Stage      string
	Grouping   string
	Group      string
	Disco_port int
	Put_port   int
	Disco_data string
	Ddfs_data  string
	Jobfile    string
}

type Input struct {
	id               int
	status           string
	label            int
	replica_id       int
	replica_location string
}

type Output struct {
	label           int
	output_location string
	output_size     int64
}

type Worker struct {
	task   *Task
	input  *Input
	output *Output
}

type Process func(string, io.Writer, *Task)

func (w *Worker) runStage(output_name string, process Process) {
	output, err := os.Create(output_name)
	Check(err)
	process(w.input.replica_location, output, w.task)
	output.Close()
	w.output.output_location = "disco://" + output_name[len(w.task.Disco_data)+1:]
	output, err = os.Open(output_name)
	Check(err)
	fileinfo, err := output.Stat()
	Check(err)
	w.output.output_size = fileinfo.Size()
}

func Run(Map Process, Reduce Process) {
	var w Worker
	send_worker()
	w.task = request_task()

	jobutil.SetKeyValue("HOST", w.task.Host)
	master, port := jobutil.HostAndPort(w.task.Master)
	jobutil.SetKeyValue("DISCO_MASTER", master)
	if port != fmt.Sprintf("%d", w.task.Disco_port) {
		panic("port mismatch: " + port)
	}
	jobutil.SetKeyValue("DISCO_PORT", port)
	jobutil.SetKeyValue("PUT_PORT", string(w.task.Put_port))
	jobutil.SetKeyValue("DISCO_DATA", w.task.Disco_data)
	jobutil.SetKeyValue("DDFS_DATA", w.task.Ddfs_data)

	w.input = request_input()

	pwd, err := os.Getwd()
	Check(err)

	w.output = new(Output)
	if w.task.Stage == "map" {
		w.runStage(pwd+"/map_out", Map)
	} else if w.task.Stage == "map_shuffle" {
		w.output.output_location = w.input.replica_location
	} else {
		w.runStage(pwd+"/reduce_out", Reduce)
	}

	send_output(w.output)
	request_done()
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"regexp"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/backup"
	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
)

func resourceAwsBackupVaultNotifications() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsBackupVaultNotificationsCreate,
		Read:   resourceAwsBackupVaultNotificationsRead,
		Delete: resourceAwsBackupVaultNotificationsDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"backup_vault_name": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9\-\_\.]{1,50}$`), "must consist of lowercase letters, numbers, and hyphens."),
			},
			"sns_topic_arn": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validateArn,
			},
			"backup_vault_events": {
				Type:     schema.TypeSet,
				Required: true,
				ForceNew: true,
				Elem: &schema.Schema{
					Type: schema.TypeString,
					ValidateFunc: validation.StringInSlice([]string{
						backup.VaultEventBackupJobStarted,
						backup.VaultEventBackupJobCompleted,
						backup.VaultEventBackupJobSuccessful,
						backup.VaultEventBackupJobFailed,
						backup.VaultEventBackupJobExpired,
						backup.VaultEventRestoreJobStarted,
						backup.VaultEventRestoreJobSuccessful,
						backup.VaultEventRestoreJobCompleted,
						backup.VaultEventRestoreJobFailed,
						backup.VaultEventCopyJobFailed,
						backup.VaultEventCopyJobStarted,
						backup.VaultEventCopyJobSuccessful,
						backup.VaultEventRecoveryPointModified,
						backup.VaultEventBackupPlanCreated,
						backup.VaultEventBackupPlanModified,
					}, false),
				},
			},
			"backup_vault_arn": {
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func resourceAwsBackupVaultNotificationsCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).backupconn

	input := &backup.PutBackupVaultNotificationsInput{
		BackupVaultName:   aws.String(d.Get("backup_vault_name").(string)),
		SNSTopicArn:       aws.String(d.Get("sns_topic_arn").(string)),
		BackupVaultEvents: expandStringSet(d.Get("backup_vault_events").(*schema.Set)),
	}

	_, err := conn.PutBackupVaultNotifications(input)
	if err != nil {
		return fmt.Errorf("error creating Backup Vault Notification (%s): %w", d.Id(), err)
	}

	d.SetId(d.Get("backup_vault_name").(string))

	return resourceAwsBackupVaultNotificationsRead(d, meta)
}

func resourceAwsBackupVaultNotificationsRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).backupconn

	input := &backup.GetBackupVaultNotificationsInput{
		BackupVaultName: aws.String(d.Id()),
	}

	resp, err := conn.GetBackupVaultNotifications(input)
	if isAWSErr(err, backup.ErrCodeResourceNotFoundException, "") {
		log.Printf("[WARN] Backup Vault Notifcations %s not found, removing from state", d.Id())
		d.SetId("")
		return nil
	}

	if err != nil {
		return fmt.Errorf("error reading Backup Vault (%s): %w", d.Id(), err)
	}
	d.Set("backup_vault_name", resp.BackupVaultName)
	d.Set("sns_topic_arn", resp.SNSTopicArn)
	d.Set("backup_vault_arn", resp.BackupVaultArn)
	d.Set("backup_vault_events", flattenStringSet(resp.BackupVaultEvents))

	return nil
}

func resourceAwsBackupVaultNotificationsDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).backupconn

	input := &backup.DeleteBackupVaultNotificationsInput{
		BackupVaultName: aws.String(d.Id()),
	}

	_, err := conn.DeleteBackupVaultNotifications(input)
	if err != nil {
		if isAWSErr(err, backup.ErrCodeResourceNotFoundException, "") {
			return nil
		}
		return fmt.Errorf("error deleting Backup Vault Notification (%s): %w", d.Id(), err)
	}

	return nil
}
<commit_msg>Update aws/resource_aws_backup_vault_notifications.go<commit_after>package aws

import (
	"fmt"
	"log"
	"regexp"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/backup"
	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
)

func resourceAwsBackupVaultNotifications() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsBackupVaultNotificationsCreate,
		Read:   resourceAwsBackupVaultNotificationsRead,
		Delete: resourceAwsBackupVaultNotificationsDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"backup_vault_name": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9\-\_\.]{1,50}$`), "must consist of lowercase letters, numbers, and hyphens."),
			},
			"sns_topic_arn": {
				Type:         schema.TypeString,
				Required:     true,
				ForceNew:     true,
				ValidateFunc: validateArn,
			},
			"backup_vault_events": {
				Type:     schema.TypeSet,
				Required: true,
				ForceNew: true,
				Elem: &schema.Schema{
					Type: schema.TypeString,
					ValidateFunc: validation.StringInSlice([]string{
						backup.VaultEventBackupJobStarted,
						backup.VaultEventBackupJobCompleted,
						backup.VaultEventBackupJobSuccessful,
						backup.VaultEventBackupJobFailed,
						backup.VaultEventBackupJobExpired,
						backup.VaultEventRestoreJobStarted,
						backup.VaultEventRestoreJobSuccessful,
						backup.VaultEventRestoreJobCompleted,
						backup.VaultEventRestoreJobFailed,
						backup.VaultEventCopyJobFailed,
						backup.VaultEventCopyJobStarted,
						backup.VaultEventCopyJobSuccessful,
						backup.VaultEventRecoveryPointModified,
						backup.VaultEventBackupPlanCreated,
						backup.VaultEventBackupPlanModified,
					}, false),
				},
			},
			"backup_vault_arn": {
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func resourceAwsBackupVaultNotificationsCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).backupconn

	input := &backup.PutBackupVaultNotificationsInput{
		BackupVaultName:   aws.String(d.Get("backup_vault_name").(string)),
		SNSTopicArn:       aws.String(d.Get("sns_topic_arn").(string)),
		BackupVaultEvents: expandStringSet(d.Get("backup_vault_events").(*schema.Set)),
	}

	_, err := conn.PutBackupVaultNotifications(input)
	if err != nil {
		return fmt.Errorf("error creating Backup Vault Notification (%s): %w", d.Id(), err)
	}

	d.SetId(d.Get("backup_vault_name").(string))

	return resourceAwsBackupVaultNotificationsRead(d, meta)
}

func resourceAwsBackupVaultNotificationsRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).backupconn

	input := &backup.GetBackupVaultNotificationsInput{
		BackupVaultName: aws.String(d.Id()),
	}

	resp, err := conn.GetBackupVaultNotifications(input)
	if isAWSErr(err, backup.ErrCodeResourceNotFoundException, "") {
		log.Printf("[WARN] Backup Vault Notifcations %s not found, removing from state", d.Id())
		d.SetId("")
		return nil
	}

	if err != nil {
		return fmt.Errorf("error reading Backup Vault Notifications (%s): %w", d.Id(), err)
	}
	d.Set("backup_vault_name", resp.BackupVaultName)
	d.Set("sns_topic_arn", resp.SNSTopicArn)
	d.Set("backup_vault_arn", resp.BackupVaultArn)
	d.Set("backup_vault_events", flattenStringSet(resp.BackupVaultEvents))

	return nil
}

func resourceAwsBackupVaultNotificationsDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).backupconn

	input := &backup.DeleteBackupVaultNotificationsInput{
		BackupVaultName: aws.String(d.Id()),
	}

	_, err := conn.DeleteBackupVaultNotifications(input)
	if err != nil {
		if isAWSErr(err, backup.ErrCodeResourceNotFoundException, "") {
			return nil
		}
		return fmt.Errorf("error deleting Backup Vault Notification (%s): %w", d.Id(), err)
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright (c) Ilia Kravets, 2014-2016. All rights reserved. PROVIDED "AS IS"
// WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. See LICENSE file for details.

package sim

import (
	"github.com/ikravets/errs"

	"my/ev/packet"
)

const (
	OA_UNKNOWN = iota
	OA_ORDERS
	OA_BOOKS
)

type SimOperation interface {
	GetMessage() *SimMessage
	GetOptionId() packet.OptionId
	GetOrigOrderId() packet.OrderId
	GetSide() packet.MarketSide
	GetDefaultSizeDelta() int
	GetNewSize(SizeKind) int
	GetPrice() int
	CanAffect(what int) bool
	getOperation() *Operation
}

type Operation struct {
	m           *SimMessage
	sim         Sim
	origOrderId packet.OrderId
	origOrder   *order
	sibling     SimOperation
}

func (op *Operation) GetMessage() *SimMessage {
	return op.m
}
func (op *Operation) GetOrigOrderId() packet.OrderId {
	return op.origOrderId
}
func (op *Operation) populate() {
	if op.origOrder != nil {
		return
	}
	if op.sibling != nil {
		op.sibling.getOperation().populate()
		op.origOrder = op.sibling.getOperation().origOrder
	} else if op.origOrderId != packet.OrderIdUnknown {
		if ord, err := op.sim.OrderDb().findOrder(op.m.Session, op.origOrderId); err == nil {
			op.origOrder = &ord
		}
	}
}
func (op *Operation) origOrderIndex() orderIndex {
	return newOrderIndex(op.sim, op.m.Session, op.origOrderId)
}
func (o *Operation) getOptionId() (oid packet.OptionId) {
	o.populate()
	if o.origOrder != nil {
		return o.origOrder.OptionId
	} else {
		return packet.OptionIdUnknown
	}
}
func (o *Operation) getSide() (side packet.MarketSide) {
	o.populate()
	if o.origOrder != nil {
		side = o.origOrder.Side
	}
	return
}

type OperationAdd struct {
	Operation
	order
}

func (op *OperationAdd) CanAffect(what int) bool {
	return (what == OA_BOOKS || what == OA_ORDERS) && op.GetOptionId().Valid()
}
func (o *OperationAdd) getOperation() *Operation {
	return &o.Operation
}
func (o *OperationAdd) Independent() bool {
	return o.OptionId.Valid()
}
func (o *OperationAdd) GetOptionId() packet.OptionId {
	if o.OptionId.Valid() {
		return o.OptionId
	} else {
		return o.Operation.getOptionId()
	}
}
func (o *OperationAdd) GetSide() (side packet.MarketSide) {
	if o.Side != packet.MarketSideUnknown {
		return o.Side
	} else {
		return o.Operation.getSide()
	}
}
func (o *OperationAdd) GetPrice() int {
	return packet.PriceTo4Dec(o.Price)
}
func (o *OperationAdd) GetDefaultSizeDelta() int {
	return o.Size
}
func (o *OperationAdd) GetNewSize(sk SizeKind) int {
	errs.Check(sk == SizeKindDefault)
	return o.Size
}
func (op *OperationAdd) orderIndex() orderIndex {
	return newOrderIndex(op.sim, op.m.Session, op.OrderId)
}

type OperationRemove struct {
	Operation
}

func (o *OperationRemove) getOperation() *Operation {
	return &o.Operation
}
func (op *OperationRemove) CanAffect(what int) bool {
	return (what == OA_BOOKS || what == OA_ORDERS) && op.GetOptionId().Valid()
}
func (o *OperationRemove) GetOptionId() packet.OptionId {
	return o.Operation.getOptionId()
}
func (o *OperationRemove) GetSide() (side packet.MarketSide) {
	return o.Operation.getSide()
}
func (o *OperationRemove) GetDefaultSizeDelta() int {
	o.Operation.populate()
	errs.Check(o.origOrder != nil)
	return -o.origOrder.Size
}
func (o *OperationRemove) GetNewSize(sk SizeKind) int {
	errs.Check(sk == SizeKindDefault)
	return 0
}
func (o *OperationRemove) GetPrice() int {
	o.Operation.populate()
	errs.Check(o.origOrder != nil)
	return packet.PriceTo4Dec(o.origOrder.Price)
}

type OperationUpdate struct {
	Operation
	sizeChange int
}

func (o *OperationUpdate) getOperation() *Operation {
	return &o.Operation
}
func (op *OperationUpdate) CanAffect(what int) bool {
	return (what == OA_BOOKS || what == OA_ORDERS) && op.GetOptionId().Valid()
}
func (o *OperationUpdate) GetOptionId() packet.OptionId {
	return o.Operation.getOptionId()
}
func (o *OperationUpdate) GetSide() (side packet.MarketSide) {
	return o.Operation.getSide()
}
func (o *OperationUpdate) GetDefaultSizeDelta() int {
	return -o.sizeChange
}
func (o *OperationUpdate) GetNewSize(sk SizeKind) int {
	errs.Check(sk == SizeKindDefault)
	o.Operation.populate()
	errs.Check(o.origOrder != nil)
	return o.origOrder.Size - o.sizeChange
}
func (o *OperationUpdate) GetPrice() int {
	o.Operation.populate()
	errs.Check(o.origOrder != nil)
	return packet.PriceTo4Dec(o.origOrder.Price)
}

type OperationTop struct {
	Operation
	optionId packet.OptionId
	side     packet.MarketSide
	sizes    [SizeKinds]int
	price    packet.Price
}

func (o *OperationTop) getOperation() *Operation {
	return &o.Operation
}
func (op *OperationTop) CanAffect(what int) bool {
	return what == OA_BOOKS && op.GetOptionId().Valid()
}
func (o *OperationTop) GetOptionId() packet.OptionId {
	return o.optionId
}
func (o *OperationTop) GetSide() (side packet.MarketSide) {
	return o.side
}
func (o *OperationTop) GetDefaultSizeDelta() int {
	errs.Check(false)
	return 0
}
func (o *OperationTop) GetNewSize(sk SizeKind) int {
	return o.sizes[sk]
}
func (o *OperationTop) GetPrice() int {
	return packet.PriceTo4Dec(o.price)
}
<commit_msg>sim: add OperationScale<commit_after>// Copyright (c) Ilia Kravets, 2014-2016. All rights reserved. PROVIDED "AS IS"
// WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. See LICENSE file for details.

package sim

import (
	"github.com/ikravets/errs"

	"my/ev/packet"
)

const (
	OA_UNKNOWN = iota
	OA_OPTIONS
	OA_ORDERS
	OA_BOOKS
)

type SimOperation interface {
	GetMessage() *SimMessage
	GetOptionId() packet.OptionId
	GetOrigOrderId() packet.OrderId
	GetSide() packet.MarketSide
	GetDefaultSizeDelta() int
	GetNewSize(SizeKind) int
	GetPrice() int
	CanAffect(what int) bool
	getOperation() *Operation
}

type Operation struct {
	m           *SimMessage
	sim         Sim
	origOrderId packet.OrderId
	origOrder   *order
	sibling     SimOperation
}

func (op *Operation) GetMessage() *SimMessage {
	return op.m
}
func (op *Operation) GetOrigOrderId() packet.OrderId {
	return op.origOrderId
}
func (op *Operation) populate() {
	if op.origOrder != nil {
		return
	}
	if op.sibling != nil {
		op.sibling.getOperation().populate()
		op.origOrder = op.sibling.getOperation().origOrder
	} else if op.origOrderId != packet.OrderIdUnknown {
		if ord, err := op.sim.OrderDb().findOrder(op.m.Session, op.origOrderId); err == nil {
			op.origOrder = &ord
		}
	}
}
func (op *Operation) origOrderIndex() orderIndex {
	return newOrderIndex(op.sim, op.m.Session, op.origOrderId)
}
func (o *Operation) getOptionId() (oid packet.OptionId) {
	o.populate()
	if o.origOrder != nil {
		return o.origOrder.OptionId
	} else {
		return packet.OptionIdUnknown
	}
}
func (o *Operation) getSide() (side packet.MarketSide) {
	o.populate()
	if o.origOrder != nil {
		side = o.origOrder.Side
	}
	return
}

type OperationAdd struct {
	Operation
	order
}

func (op *OperationAdd) CanAffect(what int) bool {
	return (what == OA_BOOKS || what == OA_ORDERS) && op.GetOptionId().Valid()
}
func (o *OperationAdd) getOperation() *Operation {
	return &o.Operation
}
func (o *OperationAdd) Independent() bool {
	return o.OptionId.Valid()
}
func (o *OperationAdd) GetOptionId() packet.OptionId {
	if o.OptionId.Valid() {
		return o.OptionId
	} else {
		return o.Operation.getOptionId()
	}
}
func (o *OperationAdd) GetSide() (side packet.MarketSide) {
	if o.Side != packet.MarketSideUnknown {
		return o.Side
	} else {
		return o.Operation.getSide()
	}
}
func (o *OperationAdd) GetPrice() int {
	return packet.PriceTo4Dec(o.Price)
}
func (o *OperationAdd) GetDefaultSizeDelta() int {
	return o.Size
}
func (o *OperationAdd) GetNewSize(sk SizeKind) int {
	errs.Check(sk == SizeKindDefault)
	return o.Size
}
func (op *OperationAdd) orderIndex() orderIndex {
	return newOrderIndex(op.sim, op.m.Session, op.OrderId)
}

type OperationRemove struct {
	Operation
}

func (o *OperationRemove) getOperation() *Operation {
	return &o.Operation
}
func (op *OperationRemove) CanAffect(what int) bool {
	return (what == OA_BOOKS || what == OA_ORDERS) && op.GetOptionId().Valid()
}
func (o *OperationRemove) GetOptionId() packet.OptionId {
	return o.Operation.getOptionId()
}
func (o *OperationRemove) GetSide() (side packet.MarketSide) {
	return o.Operation.getSide()
}
func (o *OperationRemove) GetDefaultSizeDelta() int {
	o.Operation.populate()
	errs.Check(o.origOrder != nil)
	return -o.origOrder.Size
}
func (o *OperationRemove) GetNewSize(sk SizeKind) int {
	errs.Check(sk == SizeKindDefault)
	return 0
}
func (o *OperationRemove) GetPrice() int {
	o.Operation.populate()
	errs.Check(o.origOrder != nil)
	return packet.PriceTo4Dec(o.origOrder.Price)
}

type OperationUpdate struct {
	Operation
	sizeChange int
}

func (o *OperationUpdate) getOperation() *Operation {
	return &o.Operation
}
func (op *OperationUpdate) CanAffect(what int) bool {
	return (what == OA_BOOKS || what == OA_ORDERS) && op.GetOptionId().Valid()
}
func (o *OperationUpdate) GetOptionId() packet.OptionId {
	return o.Operation.getOptionId()
}
func (o *OperationUpdate) GetSide() (side packet.MarketSide) {
	return o.Operation.getSide()
}
func (o *OperationUpdate) GetDefaultSizeDelta() int {
	return -o.sizeChange
}
func (o *OperationUpdate) GetNewSize(sk SizeKind) int {
	errs.Check(sk == SizeKindDefault)
	o.Operation.populate()
	errs.Check(o.origOrder != nil)
	return o.origOrder.Size - o.sizeChange
}
func (o *OperationUpdate) GetPrice() int {
	o.Operation.populate()
	errs.Check(o.origOrder != nil)
	return packet.PriceTo4Dec(o.origOrder.Price)
}

type OperationTop struct {
	Operation
	optionId packet.OptionId
	side     packet.MarketSide
	sizes    [SizeKinds]int
	price    packet.Price
}

func (o *OperationTop) getOperation() *Operation {
	return &o.Operation
}
func (op *OperationTop) CanAffect(what int) bool {
	return what == OA_BOOKS && op.GetOptionId().Valid()
}
func (o *OperationTop) GetOptionId() packet.OptionId {
	return o.optionId
}
func (o *OperationTop) GetSide() (side packet.MarketSide) {
	return o.side
}
func (o *OperationTop) GetDefaultSizeDelta() int {
	errs.Check(false)
	return 0
}
func (o *OperationTop) GetNewSize(sk SizeKind) int {
	return o.sizes[sk]
}
func (o *OperationTop) GetPrice() int {
	return packet.PriceTo4Dec(o.price)
}

type OperationScale struct {
	Operation
	optionId   packet.OptionId
	priceScale int
}

func (o *OperationScale) getOperation() *Operation {
	return &o.Operation
}
func (op *OperationScale) CanAffect(what int) bool {
	return what == OA_OPTIONS && op.GetOptionId().Valid()
}
func (o *OperationScale) GetOptionId() packet.OptionId {
	return o.optionId
}
func (o *OperationScale) GetSide() (side packet.MarketSide) {
	return packet.MarketSideUnknown
}
func (o *OperationScale) GetDefaultSizeDelta() int {
	errs.Check(false)
	return 0
}
func (o *OperationScale) GetNewSize(sk SizeKind) int {
	errs.Check(false)
	return 0
}
func (o *OperationScale) GetPrice() int {
	return o.priceScale
}
<|endoftext|>


<commit_before>package task

import (
	"fmt"
	"neon/build"
	"neon/util"
	"strings"
	"os"
)

func init() {
	build.TaskMap["classpath"] = build.TaskDescriptor{
		Constructor: Classpath,
		Help: `Build a Java classpath.

Arguments:

- classpath: the name of the property to set with classpath.
- classes: a list of class directories to add in classpath (optional).
- jars: a glob or list of globs of jar files to add to classpath (optional).
# TODO
- dependencies: a list of dependency files to add to classpath (optional).
- repositories: a list of repository URLs to get dependencies from (optional,
  defaults to 'http://repo1.maven.org/maven2').
- scope: the classpath scope (optional, defaults to 'runtime').

Examples:

	# build classpath with classes in build/classes directory
	- classpath: 'classpath'
	  classes:   'build/classes'
    # build classpath with jar files in lib directory
    - classpath: 'classpath'
      jars:      'lib/*.jar'
	# build classpath with a dependencies file
	- classpath:    'classpath'
	  dependencies: 'dependencies.yml'

Notes:

Dependency files should list dependencies as follows:

	- group:    junit
      artifact: junit
      version:  4.12
	  scope:    test

Scopes may be runtime (default), compile, test or provided.`,
	}
}

func Classpath(target *build.Target, args util.Object) (build.Task, error) {
	fields := []string{"classpath", "classes", "jars"}
	if err := CheckFields(args, fields, fields[:1]); err != nil {
		return nil, err
	}
	classpath, err := args.GetString("classpath")
	if err != nil {
		return nil, fmt.Errorf("argument classpath must be a string")
	}
	var classes []string
	if args.HasField("classes") {
		classes, err = args.GetListStringsOrString("classes")
		if err != nil {
			return nil, fmt.Errorf("argument classes of task classpath must be a string or list of strings")
		}
	}
	var jars []string
	if args.HasField("jars") {
		jars, err = args.GetListStringsOrString("jars")
		if err != nil {
			return nil, fmt.Errorf("argument jars of task classpath must be a string or list of strings")
		}
	}
	return func(context *build.Context) error {
		// evaluate arguments
		_classpath, _err := context.EvaluateString(classpath)
		if _err != nil {
			return fmt.Errorf("evaluating classpath argument: %v", _err)
		}
		var _classes []string
		for _, _class := range classes {
			_c, _err := context.EvaluateString(_class)
			if _err != nil {
				return fmt.Errorf("evaluating classes argument: %v", _err)
			}
			_classes = append(_classes, _c)
		}
		_jars, _err := context.FindFiles(".", jars, nil, false)
		if _err != nil {
			return fmt.Errorf("finding jar files: %v", _err)
		}
		// evaluate classpath
		_elements := append(_classes, _jars...)
		_path := strings.Join(_elements, string(os.PathListSeparator))
		context.SetProperty(_classpath, _path)
		return nil
	}, nil
}
<commit_msg>Done classpath task<commit_after>package task

import (
	"fmt"
	"neon/build"
	"neon/util"
	"strings"
	"os"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"path/filepath"
	"net/http"
	"io"
)

var LOCAL_REPOSITORY = util.ExpandUserHome("~/.java/repository")
var DEFAULT_REPOSITORY = "http://repo1.maven.org/maven2"

func init() {
	build.TaskMap["classpath"] = build.TaskDescriptor{
		Constructor: Classpath,
		Help: `Build a Java classpath.

Arguments:

- classpath: the name of the property to set with classpath.
- classes: a list of class directories to add in classpath (optional).
- jars: a glob or list of globs of jar files to add to classpath (optional).
- dependencies: a list of dependency files to add to classpath (optional).
- scopes: the classpath scope (optional, if set will take dependencies without
  scope and listed scopes, if not set, will only take dependencies without
  scope).
- repositories: a list of repository URLs to get dependencies from (optional,
  defaults to 'http://repo1.maven.org/maven2').

Examples:

	# build classpath with classes in build/classes directory
	- classpath: 'classpath'
	  classes:   'build/classes'
    # build classpath with jar files in lib directory
    - classpath: 'classpath'
      jars:      'lib/*.jar'
	# build classpath with a dependencies file
	- classpath:    'classpath'
	  dependencies: 'dependencies.yml'

Notes:

Dependency files should list dependencies as follows:

	- group:    junit
      artifact: junit
      version:  4.12
	  scopes:   test

Scopes is optional. If not set, dependency will always be included. If set,
dependency will be included for classpath with these scopes.`,
	}
}

func Classpath(target *build.Target, args util.Object) (build.Task, error) {
	fields := []string{"classpath", "classes", "jars", "dependencies", "scopes", "repositories"}
	if err := CheckFields(args, fields, fields[:1]); err != nil {
		return nil, err
	}
	classpath, err := args.GetString("classpath")
	if err != nil {
		return nil, fmt.Errorf("argument classpath must be a string")
	}
	var classes []string
	if args.HasField("classes") {
		classes, err = args.GetListStringsOrString("classes")
		if err != nil {
			return nil, fmt.Errorf("argument classes of task classpath must be a string or list of strings")
		}
	}
	var jars []string
	if args.HasField("jars") {
		jars, err = args.GetListStringsOrString("jars")
		if err != nil {
			return nil, fmt.Errorf("argument jars of task classpath must be a string or list of strings")
		}
	}
	var dependencies []string
	if args.HasField("dependencies") {
		dependencies, err = args.GetListStringsOrString("dependencies")
		if err != nil {
			return nil, fmt.Errorf("argument dependencies of task classpath must be a string or list of strings")
		}
	}
	var scopes []string
	if args.HasField("scopes") {
		scopes, err = args.GetListStringsOrString("scopes")
		if err != nil {
			return nil, fmt.Errorf("argument scopes of task classpath must be a string or list of strings")
		}
	}
	var repositories []string
	if args.HasField("repositories") {
		repositories, err = args.GetListStringsOrString("repositories")
		if err != nil {
			return nil, fmt.Errorf("argument repositories of task classpath must be a string or list of strings")
		}
	}
	return func(context *build.Context) error {
		// evaluate arguments
		_classpath, _err := context.EvaluateString(classpath)
		if _err != nil {
			return fmt.Errorf("evaluating classpath argument: %v", _err)
		}
		var _classes []string
		for _, _class := range classes {
			_c, _err := context.EvaluateString(_class)
			if _err != nil {
				return fmt.Errorf("evaluating classes argument: %v", _err)
			}
			_classes = append(_classes, _c)
		}
		_jars, _err := context.FindFiles(".", jars, nil, false)
		if _err != nil {
			return fmt.Errorf("finding jar files: %v", _err)
		}
		var _dependencies []string
		for _, _dependency := range dependencies {
			_d, _err := context.EvaluateString(_dependency)
			if _err != nil {
				return fmt.Errorf("evaluating dependencies argument: %v", _err)
			}
			_dependencies = append(_dependencies, _d)
		}
		var _scopes []string
		for _, _scope := range scopes {
			_s, _err := context.EvaluateString(_scope)
			if _err != nil {
				return fmt.Errorf("evaluating scopes argument: %v", _err)
			}
			_scopes = append(_scopes, _s)
		}
		var _repositories []string
		for _, _repository := range repositories {
			_r, _err := context.EvaluateString(_repository)
			if _err != nil {
				return fmt.Errorf("evaluating repositories argument: %v", _err)
			}
			_repositories = append(_repositories, _r)
		}
		// get dependencies
		_deps, _err := getDependencies(_dependencies, _scopes, _repositories, context)
		if _err != nil {
			return fmt.Errorf("getting dependencies: %v", _err)
		}
		// evaluate classpath
		var _elements []string
		_elements = append(_elements, _classes...)
		_elements = append(_elements, _jars...)
		_elements = append(_elements, _deps...)
		_path := strings.Join(_elements, string(os.PathListSeparator))
		context.SetProperty(_classpath, _path)
		return nil
	}, nil
}

func getDependencies(dependencies, scopes, repositories []string, context *build.Context) ([]string, error) {
	if !util.DirExists(LOCAL_REPOSITORY) {
		os.MkdirAll(LOCAL_REPOSITORY, util.DIR_FILE_MODE)
	}
	var deps []string
	for _, dependency := range dependencies {
		dep, err := getDependency(dependency, scopes, repositories, context)
		if err != nil {
			return nil, err
		}
		deps = append(deps, dep...)
	}
	return deps, nil
}

func getDependency(file string, scopes, repositories []string, context *build.Context) ([]string, error) {
	var dependencies Dependencies
	source, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, err
	}
	err = yaml.Unmarshal(source, &dependencies)
	if err != nil {
		return nil, err
	}
	var paths []string
	for _, dependency := range dependencies {
		if selected(scopes, dependency.Scopes) {
			path := dependency.Path(LOCAL_REPOSITORY)
			paths = append(paths, path)
			if !util.FileExists(path) {
				err = downloadDependency(dependency, repositories, context)
				if err != nil {
					return nil, err
				}
			}
		}
	}
	if err != nil {
		return nil, err
	}
	return paths, nil
}

func downloadDependency(dependency Dependency, repositories []string, context *build.Context) error {
	context.Message("Downloading dependency '%s'", dependency.String())
	path := dependency.Path(LOCAL_REPOSITORY)
	dir := filepath.Dir(path)
	if !util.DirExists(dir) {
		os.MkdirAll(dir, util.DIR_FILE_MODE)
	}
	if repositories == nil {
		repositories = []string{DEFAULT_REPOSITORY}
	}
	var err error
	for _, repository := range repositories {
		url := dependency.Path(repository)
		err = download(path, url, repository)
		if err == nil {
			return nil
		}
	}
	return err
}

func download(path, url string, repository string) error {
	response, err := http.Get(url)
	if err != nil {
		return fmt.Errorf("getting '%s': %v", url, err)
	}
	defer response.Body.Close()
	if response.StatusCode != 200 {
		return fmt.Errorf("getting '%s': %s", url, response.Status)
	}
	output, err := os.Create(path)
	if err != nil {
		return fmt.Errorf("saving dependency '%s': %v", path, err)
	}
	defer output.Close()
	_, err = io.Copy(output, response.Body)
	if err != nil {
		return fmt.Errorf("saving dependency '%s': %v", path, err)
	}
	return nil
}

type Dependency struct {
	Group    string
	Artifact string
	Version  string
	Scopes   []string
}

func (d *Dependency) Path(root string) string {
	return fmt.Sprintf("%s/%s/%s/%s/%s-%s.jar", root, d.Group, d.Artifact, d.Version, d.Artifact, d.Version)
}

func (d *Dependency) String() string {
	return fmt.Sprintf("%s/%s/%s", d.Group, d.Artifact, d.Version)
}

type Dependencies []Dependency

func selected(scopes1, scopes2 []string) bool {
	for _, scope1 := range scopes1 {
		for _, scope2 := range scopes2 {
			if scope1 == scope2 {
				return true
			}
		}
	}
	return false
}
<|endoftext|>


<commit_before>/*
   Copyright 2020 Docker Compose CLI 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 compose

import (
	"context"
	"path/filepath"
	"strings"

	"github.com/docker/compose-cli/api/compose"

	"github.com/docker/compose-cli/progress"

	"github.com/compose-spec/compose-go/cli"
	"github.com/compose-spec/compose-go/types"
	moby "github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/filters"
	"golang.org/x/sync/errgroup"
)

func (s *composeService) Down(ctx context.Context, projectName string, options compose.DownOptions) error {
	eg, _ := errgroup.WithContext(ctx)
	w := progress.ContextWriter(ctx)

	project, err := s.projectFromContainerLabels(ctx, projectName)
	if err != nil {
		return err
	}

	containers, err := s.apiClient.ContainerList(ctx, moby.ContainerListOptions{
		Filters: filters.NewArgs(projectFilter(project.Name)),
		All:     true,
	})
	if err != nil {
		return err
	}

	err = InReverseDependencyOrder(ctx, project, func(c context.Context, service types.ServiceConfig) error {
		serviceContainers, others := split(containers, isService(service.Name))
		err := s.removeContainers(ctx, w, eg, serviceContainers)
		containers = others
		return err
	})

	if options.RemoveOrphans {
		err := s.removeContainers(ctx, w, eg, containers)
		if err != nil {
			return err
		}
	}

	if err != nil {
		return err
	}
	err = eg.Wait()
	if err != nil {
		return err
	}
	networks, err := s.apiClient.NetworkList(ctx, moby.NetworkListOptions{
		Filters: filters.NewArgs(
			projectFilter(projectName),
		),
	})
	if err != nil {
		return err
	}
	for _, n := range networks {
		networkID := n.ID
		networkName := n.Name
		eg.Go(func() error {
			return s.ensureNetworkDown(ctx, networkID, networkName)
		})
	}

	return eg.Wait()
}

func (s *composeService) removeContainers(ctx context.Context, w progress.Writer, eg *errgroup.Group, containers []moby.Container) error {
	for _, container := range containers {
		toDelete := container
		eg.Go(func() error {
			eventName := "Container " + getContainerName(toDelete)
			w.Event(progress.StoppingEvent(eventName))
			err := s.apiClient.ContainerStop(ctx, toDelete.ID, nil)
			if err != nil {
				w.Event(progress.ErrorMessageEvent(eventName, "Error while Stopping"))
				return err
			}
			w.Event(progress.RemovingEvent(eventName))
			err = s.apiClient.ContainerRemove(ctx, toDelete.ID, moby.ContainerRemoveOptions{})
			if err != nil {
				w.Event(progress.ErrorMessageEvent(eventName, "Error while Removing"))
				return err
			}
			w.Event(progress.RemovedEvent(eventName))
			return nil
		})
	}
	return nil
}

func (s *composeService) projectFromContainerLabels(ctx context.Context, projectName string) (*types.Project, error) {
	containers, err := s.apiClient.ContainerList(ctx, moby.ContainerListOptions{
		Filters: filters.NewArgs(
			projectFilter(projectName),
		),
		All: true,
	})
	if err != nil {
		return nil, err
	}
	fakeProject := &types.Project{
		Name: projectName,
	}
	if len(containers) == 0 {
		return fakeProject, nil
	}
	options, err := loadProjectOptionsFromLabels(containers[0])
	if err != nil {
		return nil, err
	}
	if options.ConfigPaths[0] == "-" {
		for _, container := range containers {
			fakeProject.Services = append(fakeProject.Services, types.ServiceConfig{
				Name: container.Labels[serviceLabel],
			})
		}
		return fakeProject, nil
	}
	project, err := cli.ProjectFromOptions(options)
	if err != nil {
		return nil, err
	}

	return project, nil
}

func loadProjectOptionsFromLabels(c moby.Container) (*cli.ProjectOptions, error) {
	var configFiles []string
	relativePathConfigFiles := strings.Split(c.Labels[configFilesLabel], ",")
	for _, c := range relativePathConfigFiles {
		configFiles = append(configFiles, filepath.Base(c))
	}
	return cli.NewProjectOptions(configFiles,
		cli.WithOsEnv,
		cli.WithWorkingDirectory(c.Labels[workingDirLabel]),
		cli.WithName(c.Labels[projectLabel]))
}

type containerPredicate func(c moby.Container) bool

func isService(service string) containerPredicate {
	return func(c moby.Container) bool {
		return c.Labels[serviceLabel] == service
	}
}

// split return a container slice with elements to match predicate
func split(containers []moby.Container, predicate containerPredicate) ([]moby.Container, []moby.Container) {
	var right []moby.Container
	var left []moby.Container
	for _, c := range containers {
		if predicate(c) {
			right = append(right, c)
		} else {
			left = append(left, c)
		}
	}
	return right, left
}
<commit_msg>When running compose down, remove containers with Force=true in case some container is still up for any reason (happened in some E2E test once)<commit_after>/*
   Copyright 2020 Docker Compose CLI 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 compose

import (
	"context"
	"path/filepath"
	"strings"

	"github.com/docker/compose-cli/api/compose"

	"github.com/docker/compose-cli/progress"

	"github.com/compose-spec/compose-go/cli"
	"github.com/compose-spec/compose-go/types"
	moby "github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/filters"
	"golang.org/x/sync/errgroup"
)

func (s *composeService) Down(ctx context.Context, projectName string, options compose.DownOptions) error {
	eg, _ := errgroup.WithContext(ctx)
	w := progress.ContextWriter(ctx)

	project, err := s.projectFromContainerLabels(ctx, projectName)
	if err != nil {
		return err
	}

	containers, err := s.apiClient.ContainerList(ctx, moby.ContainerListOptions{
		Filters: filters.NewArgs(projectFilter(project.Name)),
		All:     true,
	})
	if err != nil {
		return err
	}

	err = InReverseDependencyOrder(ctx, project, func(c context.Context, service types.ServiceConfig) error {
		serviceContainers, others := split(containers, isService(service.Name))
		err := s.removeContainers(ctx, w, eg, serviceContainers)
		containers = others
		return err
	})

	if options.RemoveOrphans {
		err := s.removeContainers(ctx, w, eg, containers)
		if err != nil {
			return err
		}
	}

	if err != nil {
		return err
	}
	err = eg.Wait()
	if err != nil {
		return err
	}
	networks, err := s.apiClient.NetworkList(ctx, moby.NetworkListOptions{
		Filters: filters.NewArgs(
			projectFilter(projectName),
		),
	})
	if err != nil {
		return err
	}
	for _, n := range networks {
		networkID := n.ID
		networkName := n.Name
		eg.Go(func() error {
			return s.ensureNetworkDown(ctx, networkID, networkName)
		})
	}

	return eg.Wait()
}

func (s *composeService) removeContainers(ctx context.Context, w progress.Writer, eg *errgroup.Group, containers []moby.Container) error {
	for _, container := range containers {
		toDelete := container
		eg.Go(func() error {
			eventName := "Container " + getContainerName(toDelete)
			w.Event(progress.StoppingEvent(eventName))
			err := s.apiClient.ContainerStop(ctx, toDelete.ID, nil)
			if err != nil {
				w.Event(progress.ErrorMessageEvent(eventName, "Error while Stopping"))
				return err
			}
			w.Event(progress.RemovingEvent(eventName))
			err = s.apiClient.ContainerRemove(ctx, toDelete.ID, moby.ContainerRemoveOptions{Force: true})
			if err != nil {
				w.Event(progress.ErrorMessageEvent(eventName, "Error while Removing"))
				return err
			}
			w.Event(progress.RemovedEvent(eventName))
			return nil
		})
	}
	return nil
}

func (s *composeService) projectFromContainerLabels(ctx context.Context, projectName string) (*types.Project, error) {
	containers, err := s.apiClient.ContainerList(ctx, moby.ContainerListOptions{
		Filters: filters.NewArgs(
			projectFilter(projectName),
		),
		All: true,
	})
	if err != nil {
		return nil, err
	}
	fakeProject := &types.Project{
		Name: projectName,
	}
	if len(containers) == 0 {
		return fakeProject, nil
	}
	options, err := loadProjectOptionsFromLabels(containers[0])
	if err != nil {
		return nil, err
	}
	if options.ConfigPaths[0] == "-" {
		for _, container := range containers {
			fakeProject.Services = append(fakeProject.Services, types.ServiceConfig{
				Name: container.Labels[serviceLabel],
			})
		}
		return fakeProject, nil
	}
	project, err := cli.ProjectFromOptions(options)
	if err != nil {
		return nil, err
	}

	return project, nil
}

func loadProjectOptionsFromLabels(c moby.Container) (*cli.ProjectOptions, error) {
	var configFiles []string
	relativePathConfigFiles := strings.Split(c.Labels[configFilesLabel], ",")
	for _, c := range relativePathConfigFiles {
		configFiles = append(configFiles, filepath.Base(c))
	}
	return cli.NewProjectOptions(configFiles,
		cli.WithOsEnv,
		cli.WithWorkingDirectory(c.Labels[workingDirLabel]),
		cli.WithName(c.Labels[projectLabel]))
}

type containerPredicate func(c moby.Container) bool

func isService(service string) containerPredicate {
	return func(c moby.Container) bool {
		return c.Labels[serviceLabel] == service
	}
}

// split return a container slice with elements to match predicate
func split(containers []moby.Container, predicate containerPredicate) ([]moby.Container, []moby.Container) {
	var right []moby.Container
	var left []moby.Container
	for _, c := range containers {
		if predicate(c) {
			right = append(right, c)
		} else {
			left = append(left, c)
		}
	}
	return right, left
}
<|endoftext|>


<commit_before>// Copyright 2013 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.

package locale

import (
	"fmt"
	"strings"
	"testing"
)

var strdata = []string{
	"aa  ",
	"aaa ",
	"aaaa",
	"aaab",
	"aab ",
	"ab  ",
	"ba  ",
	"xxxx",
}

var strtests = map[string]int{
	"    ": 0,
	"a":    0,
	"aa":   0,
	"aaa":  4,
	"aa ":  0,
	"aaaa": 8,
	"aaab": 12,
	"aaax": 16,
	"b":    24,
	"ba":   24,
	"bbbb": 28,
}

func TestSearch(t *testing.T) {
	for k, v := range strtests {
		if i := search(strings.Join(strdata, ""), k); i != v {
			t.Errorf("%s: found %d; want %d", k, i, v)
		}
	}
}

func TestIndex(t *testing.T) {
	strtests["    "] = -1
	strtests["aaax"] = -1
	strtests["bbbb"] = -1
	for k, v := range strtests {
		if i := index(strings.Join(strdata, ""), k); i != v {
			t.Errorf("%s: found %d; want %d", k, i, v)
		}
	}
}

func TestFixCase(t *testing.T) {
	tests := []string{
		"aaaa", "AbCD", "abcd",
		"Zzzz", "AbCD", "Abcd",
		"Zzzz", "AbC", "Zzzz",
		"XXX", "ab ", "XXX",
		"XXX", "usd", "USD",
		"cmn", "AB ", "cmn",
		"gsw", "CMN", "cmn",
	}
	for i := 0; i+3 < len(tests); i += 3 {
		tt := tests[i:]
		if res := fixCase(tt[0], tt[1]); res != tt[2] {
			t.Errorf("%s+%s: found %q; want %q", tt[0], tt[1], res, tt[2])
		}
	}
}

func TestLangID(t *testing.T) {
	tests := []struct{ id, bcp47, iso3, norm string }{
		{id: "", bcp47: "und", iso3: "und"},
		{id: "  ", bcp47: "und", iso3: "und"},
		{id: "   ", bcp47: "und", iso3: "und"},
		{id: "    ", bcp47: "und", iso3: "und"},
		{id: "und", bcp47: "und", iso3: "und"},
		{id: "aju", bcp47: "aju", iso3: "aju", norm: "jrb"},
		{id: "jrb", bcp47: "jrb", iso3: "jrb"},
		{id: "es", bcp47: "es", iso3: "spa"},
		{id: "spa", bcp47: "es", iso3: "spa"},
		{id: "ji", bcp47: "yi", iso3: "yid"},
		{id: "jw", bcp47: "jv", iso3: "jav"},
		{id: "ar", bcp47: "ar", iso3: "ara"},
		{id: "arb", bcp47: "arb", iso3: "arb", norm: "ar"},
		{id: "ar", bcp47: "ar", iso3: "ara"},
		{id: "kur", bcp47: "ku", iso3: "kur"},
		{id: "nl", bcp47: "nl", iso3: "nld"},
		{id: "NL", bcp47: "nl", iso3: "nld"},
		{id: "gsw", bcp47: "gsw", iso3: "gsw"},
		{id: "gSW", bcp47: "gsw", iso3: "gsw"},
		{id: "und", bcp47: "und", iso3: "und"},
		{id: "sh", bcp47: "sh", iso3: "scr", norm: "sr"},
		{id: "scr", bcp47: "sh", iso3: "scr", norm: "sr"},
		{id: "no", bcp47: "no", iso3: "nor", norm: "nb"},
		{id: "nor", bcp47: "no", iso3: "nor", norm: "nb"},
		{id: "cmn", bcp47: "cmn", iso3: "cmn", norm: "zh"},
	}
	for i, tt := range tests {
		want := getLangID(tt.id)
		if id := getLangISO2(tt.bcp47); len(tt.bcp47) == 2 && want != id {
			t.Errorf("%d:getISO2(%s): found %v; want %v", i, tt.bcp47, id, want)
		}
		if id := getLangISO3(tt.iso3); want != id {
			t.Errorf("%d:getISO3(%s): found %v; want %v", i, tt.iso3, id, want)
		}
		if id := getLangID(tt.iso3); want != id {
			t.Errorf("%d:getID3(%s): found %v; want %v", i, tt.iso3, id, want)
		}
		norm := want
		if tt.norm != "" {
			norm = getLangID(tt.norm)
		}
		if id := normLang(tt.id); id != norm {
			t.Errorf("%d:norm(%s): found %v; want %v", i, tt.id, id, norm)
		}
		if id := want.String(); tt.bcp47 != id {
			t.Errorf("%d:String(): found %s; want %s", i, id, tt.bcp47)
		}
		if id := want.iso3(); tt.iso3 != id {
			t.Errorf("%d:iso3(): found %s; want %s", i, id, tt.iso3)
		}
	}
}

func TestRegionID(t *testing.T) {
	tests := []struct {
		id, iso2, iso3 string
		m49            int
	}{
		{"AA", "AA", "AAA", 958},
		{"IC", "IC", "", 0},
		{"ZZ", "ZZ", "ZZZ", 999},
		{"EU", "EU", "QUU", 967},
		{"419", "", "", 419},
	}
	for i, tt := range tests {
		want := getRegionID(tt.id)
		if id := getRegionISO2(tt.iso2); len(tt.iso2) == 2 && want != id {
			t.Errorf("%d:getISO2(%s): found %d; want %d", i, tt.iso2, id, want)
		}
		if id := getRegionISO3(tt.iso3); len(tt.iso3) == 3 && want != id {
			t.Errorf("%d:getISO3(%s): found %d; want %d", i, tt.iso3, id, want)
		}
		if id := getRegionID(tt.iso3); len(tt.iso3) == 3 && want != id {
			t.Errorf("%d:getID3(%s): found %d; want %d", i, tt.iso3, id, want)
		}
		if id := getRegionM49(tt.m49); tt.m49 != 0 && want != id {
			t.Errorf("%d:getM49(%d): found %d; want %d", i, tt.m49, id, want)
		}
		if len(tt.iso2) == 2 {
			if id := want.String(); tt.iso2 != id {
				t.Errorf("%d:String(): found %s; want %s", i, id, tt.iso2)
			}
		} else {
			if id := want.String(); fmt.Sprintf("%03d", tt.m49) != id {
				t.Errorf("%d:String(): found %s; want %03d", i, id, tt.m49)
			}
		}
		if id := want.iso3(); tt.iso3 != id {
			t.Errorf("%d:iso3(): found %s; want %s", i, id, tt.iso3)
		}
		if id := int(want.m49()); tt.m49 != id {
			t.Errorf("%d:m49(): found %d; want %d", i, id, tt.m49)
		}
	}
}

func TestScript(t *testing.T) {
	script = "BbbbDdddEeeeZzzz\xff\xff\xff\xff"
	const und = 3
	tests := []struct {
		in  string
		out scriptID
	}{
		{"    ", und},
		{"      ", und},
		{"  ", und},
		{"", und},
		{"Bbbb", 0},
		{"Dddd", 1},
		{"dddd", 1},
		{"dDDD", 1},
		{"Eeee", 2},
		{"Zzzz", 3},
	}
	for i, tt := range tests {
		if id := getScriptID(tt.in); id != tt.out {
			t.Errorf("%d:%s: found %d; want %d", i, tt.in, id, tt.out)
		}
	}
}

func TestCurrency(t *testing.T) {
	curInfo := func(round, dec int) string {
		return string(round<<2 + dec)
	}
	currency = strings.Join([]string{
		"BBB" + curInfo(5, 2),
		"DDD\x00",
		"XXX\x00",
		"ZZZ\x00",
		"\xff\xff\xff\xff",
	}, "")
	const und = 2
	tests := []struct {
		in         string
		out        currencyID
		round, dec int
	}{
		{"   ", und, 0, 0},
		{"     ", und, 0, 0},
		{" ", und, 0, 0},
		{"", und, 0, 0},
		{"BBB", 0, 5, 2},
		{"DDD", 1, 0, 0},
		{"dDd", 1, 0, 0},
		{"ddd", 1, 0, 0},
		{"XXX", 2, 0, 0},
		{"Zzz", 3, 0, 0},
	}
	for i, tt := range tests {
		id := getCurrencyID(tt.in)
		if id != tt.out {
			t.Errorf("%d:%s: found %d; want %d", i, tt.in, id, tt.out)
		}
		if d := id.decimals(); d != tt.dec {
			t.Errorf("%d:dec(%s): found %d; want %d", i, tt.in, d, tt.dec)
		}
		if d := id.round(); d != tt.round {
			t.Errorf("%d:round(%s): found %d; want %d", i, tt.in, d, tt.round)
		}
	}
}
<commit_msg>go.exp/locale: fix go test -cpu 1,1 -run=TestIndex<commit_after>// Copyright 2013 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.

package locale

import (
	"fmt"
	"strings"
	"testing"
)

var strdata = []string{
	"aa  ",
	"aaa ",
	"aaaa",
	"aaab",
	"aab ",
	"ab  ",
	"ba  ",
	"xxxx",
}

func strtests() map[string]int {
	return map[string]int{
		"    ": 0,
		"a":    0,
		"aa":   0,
		"aaa":  4,
		"aa ":  0,
		"aaaa": 8,
		"aaab": 12,
		"aaax": 16,
		"b":    24,
		"ba":   24,
		"bbbb": 28,
	}
}

func TestSearch(t *testing.T) {
	for k, v := range strtests() {
		if i := search(strings.Join(strdata, ""), k); i != v {
			t.Errorf("%s: found %d; want %d", k, i, v)
		}
	}
}

func TestIndex(t *testing.T) {
	strtests := strtests()
	strtests["    "] = -1
	strtests["aaax"] = -1
	strtests["bbbb"] = -1
	for k, v := range strtests {
		if i := index(strings.Join(strdata, ""), k); i != v {
			t.Errorf("%s: found %d; want %d", k, i, v)
		}
	}
}

func TestFixCase(t *testing.T) {
	tests := []string{
		"aaaa", "AbCD", "abcd",
		"Zzzz", "AbCD", "Abcd",
		"Zzzz", "AbC", "Zzzz",
		"XXX", "ab ", "XXX",
		"XXX", "usd", "USD",
		"cmn", "AB ", "cmn",
		"gsw", "CMN", "cmn",
	}
	for i := 0; i+3 < len(tests); i += 3 {
		tt := tests[i:]
		if res := fixCase(tt[0], tt[1]); res != tt[2] {
			t.Errorf("%s+%s: found %q; want %q", tt[0], tt[1], res, tt[2])
		}
	}
}

func TestLangID(t *testing.T) {
	tests := []struct{ id, bcp47, iso3, norm string }{
		{id: "", bcp47: "und", iso3: "und"},
		{id: "  ", bcp47: "und", iso3: "und"},
		{id: "   ", bcp47: "und", iso3: "und"},
		{id: "    ", bcp47: "und", iso3: "und"},
		{id: "und", bcp47: "und", iso3: "und"},
		{id: "aju", bcp47: "aju", iso3: "aju", norm: "jrb"},
		{id: "jrb", bcp47: "jrb", iso3: "jrb"},
		{id: "es", bcp47: "es", iso3: "spa"},
		{id: "spa", bcp47: "es", iso3: "spa"},
		{id: "ji", bcp47: "yi", iso3: "yid"},
		{id: "jw", bcp47: "jv", iso3: "jav"},
		{id: "ar", bcp47: "ar", iso3: "ara"},
		{id: "arb", bcp47: "arb", iso3: "arb", norm: "ar"},
		{id: "ar", bcp47: "ar", iso3: "ara"},
		{id: "kur", bcp47: "ku", iso3: "kur"},
		{id: "nl", bcp47: "nl", iso3: "nld"},
		{id: "NL", bcp47: "nl", iso3: "nld"},
		{id: "gsw", bcp47: "gsw", iso3: "gsw"},
		{id: "gSW", bcp47: "gsw", iso3: "gsw"},
		{id: "und", bcp47: "und", iso3: "und"},
		{id: "sh", bcp47: "sh", iso3: "scr", norm: "sr"},
		{id: "scr", bcp47: "sh", iso3: "scr", norm: "sr"},
		{id: "no", bcp47: "no", iso3: "nor", norm: "nb"},
		{id: "nor", bcp47: "no", iso3: "nor", norm: "nb"},
		{id: "cmn", bcp47: "cmn", iso3: "cmn", norm: "zh"},
	}
	for i, tt := range tests {
		want := getLangID(tt.id)
		if id := getLangISO2(tt.bcp47); len(tt.bcp47) == 2 && want != id {
			t.Errorf("%d:getISO2(%s): found %v; want %v", i, tt.bcp47, id, want)
		}
		if id := getLangISO3(tt.iso3); want != id {
			t.Errorf("%d:getISO3(%s): found %v; want %v", i, tt.iso3, id, want)
		}
		if id := getLangID(tt.iso3); want != id {
			t.Errorf("%d:getID3(%s): found %v; want %v", i, tt.iso3, id, want)
		}
		norm := want
		if tt.norm != "" {
			norm = getLangID(tt.norm)
		}
		if id := normLang(tt.id); id != norm {
			t.Errorf("%d:norm(%s): found %v; want %v", i, tt.id, id, norm)
		}
		if id := want.String(); tt.bcp47 != id {
			t.Errorf("%d:String(): found %s; want %s", i, id, tt.bcp47)
		}
		if id := want.iso3(); tt.iso3 != id {
			t.Errorf("%d:iso3(): found %s; want %s", i, id, tt.iso3)
		}
	}
}

func TestRegionID(t *testing.T) {
	tests := []struct {
		id, iso2, iso3 string
		m49            int
	}{
		{"AA", "AA", "AAA", 958},
		{"IC", "IC", "", 0},
		{"ZZ", "ZZ", "ZZZ", 999},
		{"EU", "EU", "QUU", 967},
		{"419", "", "", 419},
	}
	for i, tt := range tests {
		want := getRegionID(tt.id)
		if id := getRegionISO2(tt.iso2); len(tt.iso2) == 2 && want != id {
			t.Errorf("%d:getISO2(%s): found %d; want %d", i, tt.iso2, id, want)
		}
		if id := getRegionISO3(tt.iso3); len(tt.iso3) == 3 && want != id {
			t.Errorf("%d:getISO3(%s): found %d; want %d", i, tt.iso3, id, want)
		}
		if id := getRegionID(tt.iso3); len(tt.iso3) == 3 && want != id {
			t.Errorf("%d:getID3(%s): found %d; want %d", i, tt.iso3, id, want)
		}
		if id := getRegionM49(tt.m49); tt.m49 != 0 && want != id {
			t.Errorf("%d:getM49(%d): found %d; want %d", i, tt.m49, id, want)
		}
		if len(tt.iso2) == 2 {
			if id := want.String(); tt.iso2 != id {
				t.Errorf("%d:String(): found %s; want %s", i, id, tt.iso2)
			}
		} else {
			if id := want.String(); fmt.Sprintf("%03d", tt.m49) != id {
				t.Errorf("%d:String(): found %s; want %03d", i, id, tt.m49)
			}
		}
		if id := want.iso3(); tt.iso3 != id {
			t.Errorf("%d:iso3(): found %s; want %s", i, id, tt.iso3)
		}
		if id := int(want.m49()); tt.m49 != id {
			t.Errorf("%d:m49(): found %d; want %d", i, id, tt.m49)
		}
	}
}

func TestScript(t *testing.T) {
	script = "BbbbDdddEeeeZzzz\xff\xff\xff\xff"
	const und = 3
	tests := []struct {
		in  string
		out scriptID
	}{
		{"    ", und},
		{"      ", und},
		{"  ", und},
		{"", und},
		{"Bbbb", 0},
		{"Dddd", 1},
		{"dddd", 1},
		{"dDDD", 1},
		{"Eeee", 2},
		{"Zzzz", 3},
	}
	for i, tt := range tests {
		if id := getScriptID(tt.in); id != tt.out {
			t.Errorf("%d:%s: found %d; want %d", i, tt.in, id, tt.out)
		}
	}
}

func TestCurrency(t *testing.T) {
	curInfo := func(round, dec int) string {
		return string(round<<2 + dec)
	}
	currency = strings.Join([]string{
		"BBB" + curInfo(5, 2),
		"DDD\x00",
		"XXX\x00",
		"ZZZ\x00",
		"\xff\xff\xff\xff",
	}, "")
	const und = 2
	tests := []struct {
		in         string
		out        currencyID
		round, dec int
	}{
		{"   ", und, 0, 0},
		{"     ", und, 0, 0},
		{" ", und, 0, 0},
		{"", und, 0, 0},
		{"BBB", 0, 5, 2},
		{"DDD", 1, 0, 0},
		{"dDd", 1, 0, 0},
		{"ddd", 1, 0, 0},
		{"XXX", 2, 0, 0},
		{"Zzz", 3, 0, 0},
	}
	for i, tt := range tests {
		id := getCurrencyID(tt.in)
		if id != tt.out {
			t.Errorf("%d:%s: found %d; want %d", i, tt.in, id, tt.out)
		}
		if d := id.decimals(); d != tt.dec {
			t.Errorf("%d:dec(%s): found %d; want %d", i, tt.in, d, tt.dec)
		}
		if d := id.round(); d != tt.round {
			t.Errorf("%d:round(%s): found %d; want %d", i, tt.in, d, tt.round)
		}
	}
}
<|endoftext|>


<commit_before>package arn

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/aerogo/http/client"
	"github.com/animenotifier/arn/autocorrect"
	"github.com/animenotifier/arn/validator"
	"github.com/animenotifier/osu"
	gravatar "github.com/ungerik/go-gravatar"
)

var setNickMutex sync.Mutex
var setEmailMutex sync.Mutex

// User ...
type User struct {
	ID           string       `json:"id"`
	Nick         string       `json:"nick" editable:"true"`
	FirstName    string       `json:"firstName"`
	LastName     string       `json:"lastName"`
	Email        string       `json:"email"`
	Role         string       `json:"role"`
	Registered   string       `json:"registered"`
	LastLogin    string       `json:"lastLogin"`
	LastSeen     string       `json:"lastSeen"`
	ProExpires   string       `json:"proExpires"`
	Gender       string       `json:"gender"`
	Language     string       `json:"language"`
	Tagline      string       `json:"tagline" editable:"true"`
	Introduction string       `json:"introduction" editable:"true" type:"textarea"`
	Website      string       `json:"website" editable:"true"`
	IP           string       `json:"ip"`
	UserAgent    string       `json:"agent"`
	Balance      int          `json:"balance"`
	Avatar       UserAvatar   `json:"avatar"`
	Cover        UserCover    `json:"cover"`
	AgeRange     UserAgeRange `json:"ageRange"`
	Location     Location     `json:"location"`
	Accounts     UserAccounts `json:"accounts"`
	Browser      UserBrowser  `json:"browser"`
	OS           UserOS       `json:"os"`
	Following    []string     `json:"following"`
}

// NewUser creates an empty user object with a unique ID.
func NewUser() *User {
	user := &User{
		ID: GenerateID("User"),

		// Avoid nil value fields
		Following: make([]string, 0),
	}

	return user
}

// RegisterUser registers a new user in the database and sets up all the required references.
func RegisterUser(user *User) {
	user.Registered = DateTimeUTC()
	user.LastLogin = user.Registered
	user.LastSeen = user.Registered

	// Save nick in NickToUser table
	DB.Set("NickToUser", user.Nick, &NickToUser{
		Nick:   user.Nick,
		UserID: user.ID,
	})

	// Save email in EmailToUser table
	if user.Email != "" {
		DB.Set("EmailToUser", user.Email, &EmailToUser{
			Email:  user.Email,
			UserID: user.ID,
		})
	}

	// Create default settings
	NewSettings(user).Save()

	// Add empty anime list
	DB.Set("AnimeList", user.ID, &AnimeList{
		UserID: user.ID,
		Items:  []*AnimeListItem{},
	})

	// Add empty inventory
	NewInventory(user.ID).Save()

	// Add draft index
	NewDraftIndex(user.ID).Save()

	// Add empty push subscriptions
	DB.Set("PushSubscriptions", user.ID, &PushSubscriptions{
		UserID: user.ID,
		Items:  []*PushSubscription{},
	})

	// Add empty follow list
	NewUserFollows(user.ID).Save()

	// Add empty notifications list
	NewUserNotifications(user.ID).Save()

	// Fetch gravatar
	if user.Email != "" {
		gravatarURL := gravatar.Url(user.Email) + "?s=" + fmt.Sprint(AvatarMaxSize) + "&d=404&r=pg"
		gravatarURL = strings.Replace(gravatarURL, "http://", "https://", 1)

		response, err := client.Get(gravatarURL).End()

		if err == nil && response.StatusCode() == http.StatusOK {
			data := response.Bytes()
			user.SetAvatarBytes(data)
		}
	}
}

// SendNotification accepts a PushNotification and generates a new Notification object.
// The notification is then sent to all registered push devices.
func (user *User) SendNotification(pushNotification *PushNotification) {
	// Don't ever send notifications in development mode
	if IsDevelopment() && user.ID != "4J6qpK1ve" {
		return
	}

	// Save notification in database
	notification := NewNotification(user.ID, pushNotification)
	notification.Save()

	userNotifications := user.Notifications()
	userNotifications.Add(notification.ID)
	userNotifications.Save()

	// Send push notification
	subs := user.PushSubscriptions()
	expired := []*PushSubscription{}

	for _, sub := range subs.Items {
		resp, err := sub.SendNotification(pushNotification)

		if resp != nil && resp.StatusCode == http.StatusGone {
			expired = append(expired, sub)
			continue
		}

		// Print errors
		if err != nil {
			fmt.Println(err)
			continue
		}

		// Print bad status codes
		if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
			body, _ := ioutil.ReadAll(resp.Body)
			fmt.Println(resp.StatusCode, string(body))
			continue
		}

		sub.LastSuccess = DateTimeUTC()
	}

	// Remove expired items
	if len(expired) > 0 {
		for _, sub := range expired {
			subs.Remove(sub.ID())
		}
	}

	// Save changes
	subs.Save()
}

// RealName returns the real name of the user.
func (user *User) RealName() string {
	if user.LastName == "" {
		return user.FirstName
	}

	if user.FirstName == "" {
		return user.LastName
	}

	return user.FirstName + " " + user.LastName
}

// RegisteredTime returns the time the user registered his account.
func (user *User) RegisteredTime() time.Time {
	reg, _ := time.Parse(time.RFC3339, user.Registered)
	return reg
}

// LastSeenTime returns the time the user was last seen on the site.
func (user *User) LastSeenTime() time.Time {
	lastSeen, _ := time.Parse(time.RFC3339, user.LastSeen)
	return lastSeen
}

// IsActive tells you whether the user is active.
func (user *User) IsActive() bool {
	lastSeen, _ := time.Parse(time.RFC3339, user.LastSeen)
	twoWeeksAgo := time.Now().Add(-14 * 24 * time.Hour)

	if lastSeen.Unix() < twoWeeksAgo.Unix() {
		return false
	}

	if len(user.AnimeList().Items) == 0 {
		return false
	}

	return true
}

// IsPro returns whether the user is a PRO user or not.
func (user *User) IsPro() bool {
	if user.ProExpires == "" {
		return false
	}

	return DateTimeUTC() < user.ProExpires
}

// ExtendProDuration extends the PRO account duration by the given duration.
func (user *User) ExtendProDuration(duration time.Duration) {
	var startDate time.Time

	if user.ProExpires == "" {
		startDate = time.Now().UTC()
	} else {
		startDate, _ = time.Parse(time.RFC3339, user.ProExpires)
	}

	user.ProExpires = startDate.Add(duration).Format(time.RFC3339)
}

// TimeSinceRegistered returns the duration since the user registered his account.
func (user *User) TimeSinceRegistered() time.Duration {
	registered, _ := time.Parse(time.RFC3339, user.Registered)
	return time.Since(registered)
}

// HasNick returns whether the user has a custom nickname.
func (user *User) HasNick() bool {
	return !strings.HasPrefix(user.Nick, "g") && !strings.HasPrefix(user.Nick, "fb") && !strings.HasPrefix(user.Nick, "t") && user.Nick != ""
}

// WebsiteURL adds https:// to the URL.
func (user *User) WebsiteURL() string {
	return "https://" + user.WebsiteShortURL()
}

// WebsiteShortURL returns the user website without the protocol.
func (user *User) WebsiteShortURL() string {
	return strings.Replace(strings.Replace(user.Website, "https://", "", 1), "http://", "", 1)
}

// Link returns the URI to the user page.
func (user *User) Link() string {
	return "/+" + user.Nick
}

// HasAvatar tells you whether the user has an avatar or not.
func (user *User) HasAvatar() bool {
	return user.Avatar.Extension != ""
}

// AvatarLink returns the URL to the user avatar.
// Expects "small" (50 x 50) or "large" (560 x 560) for the size parameter.
func (user *User) AvatarLink(size string) string {
	if user.HasAvatar() {
		return fmt.Sprintf("//%s/images/avatars/%s/%s%s?%v", MediaHost, size, user.ID, user.Avatar.Extension, user.Avatar.LastModified)
	}

	return fmt.Sprintf("//%s/images/elements/no-avatar.svg", MediaHost)
}

// CoverLink ...
func (user *User) CoverLink(size string) string {
	if user.Cover.Extension != "" {
		return fmt.Sprintf("//%s/images/covers/%s/%s%s?%v", MediaHost, size, user.ID, user.Cover.Extension, user.Cover.LastModified)
	}

	return "/images/elements/default-cover.jpg"
}

// Gravatar returns the URL to the gravatar if an email has been registered.
func (user *User) Gravatar() string {
	if user.Email == "" {
		return ""
	}

	return gravatar.SecureUrl(user.Email) + "?s=" + fmt.Sprint(AvatarMaxSize)
}

// EditorScore returns the editor score.
func (user *User) EditorScore() int {
	ignoreDifferences := FilterIgnoreAnimeDifferences(func(entry *IgnoreAnimeDifference) bool {
		return entry.CreatedBy == user.ID
	})

	score := len(ignoreDifferences) * IgnoreAnimeDifferenceEditorScore

	logEntries := FilterEditLogEntries(func(entry *EditLogEntry) bool {
		return entry.UserID == user.ID
	})

	for _, entry := range logEntries {
		score += entry.EditorScore()
	}

	return score
}

// ActivateItemEffect activates an item in the user inventory by the given item ID.
func (user *User) ActivateItemEffect(itemID string) error {
	month := 30 * 24 * time.Hour

	switch itemID {
	case "pro-account-3":
		user.ExtendProDuration(3 * month)
		user.Save()
		return nil

	case "pro-account-6":
		user.ExtendProDuration(6 * month)
		user.Save()
		return nil

	case "pro-account-12":
		user.ExtendProDuration(12 * month)
		user.Save()
		return nil

	case "pro-account-24":
		user.ExtendProDuration(24 * month)
		user.Save()
		return nil

	default:
		return errors.New("Can't activate unknown item: " + itemID)
	}
}

// SetNick changes the user's nickname safely.
func (user *User) SetNick(newName string) error {
	setNickMutex.Lock()
	defer setNickMutex.Unlock()

	newName = autocorrect.FixUserNick(newName)

	if !validator.IsValidNick(newName) {
		return errors.New("Invalid nickname")
	}

	if newName == user.Nick {
		return nil
	}

	// Make sure the nickname doesn't exist already
	_, err := GetUserByNick(newName)

	// If there was no error: the username exists.
	// If "not found" is not included in the error message it's a different error type.
	if err == nil || strings.Index(err.Error(), "not found") == -1 {
		return errors.New("Username '" + newName + "' is taken already")
	}

	user.ForceSetNick(newName)
	return nil
}

// ForceSetNick forces a nickname overwrite.
func (user *User) ForceSetNick(newName string) {
	// Delete old nick reference
	DB.Delete("NickToUser", user.Nick)

	// Set new nick
	user.Nick = newName

	DB.Set("NickToUser", user.Nick, &NickToUser{
		Nick:   user.Nick,
		UserID: user.ID,
	})
}

// SetEmail changes the user's email safely.
func (user *User) SetEmail(newName string) error {
	setEmailMutex.Lock()
	defer setEmailMutex.Unlock()

	if !validator.IsValidEmail(user.Email) {
		return errors.New("Invalid email address")
	}

	// Delete old email reference
	DB.Delete("EmailToUser", user.Email)

	// Set new email
	user.Email = newName

	DB.Set("EmailToUser", user.Email, &EmailToUser{
		Email:  user.Email,
		UserID: user.ID,
	})

	return nil
}

// RefreshOsuInfo refreshes a user's Osu information.
func (user *User) RefreshOsuInfo() error {
	if user.Accounts.Osu.Nick == "" {
		return nil
	}

	osu, err := osu.GetUser(user.Accounts.Osu.Nick)

	if err != nil {
		return err
	}

	user.Accounts.Osu.PP, _ = strconv.ParseFloat(osu.PPRaw, 64)
	user.Accounts.Osu.Level, _ = strconv.ParseFloat(osu.Level, 64)
	user.Accounts.Osu.Accuracy, _ = strconv.ParseFloat(osu.Accuracy, 64)

	return nil
}
<commit_msg>Fixed PRO duration extension<commit_after>package arn

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/aerogo/http/client"
	"github.com/animenotifier/arn/autocorrect"
	"github.com/animenotifier/arn/validator"
	"github.com/animenotifier/osu"
	gravatar "github.com/ungerik/go-gravatar"
)

var setNickMutex sync.Mutex
var setEmailMutex sync.Mutex

// User ...
type User struct {
	ID           string       `json:"id"`
	Nick         string       `json:"nick" editable:"true"`
	FirstName    string       `json:"firstName"`
	LastName     string       `json:"lastName"`
	Email        string       `json:"email"`
	Role         string       `json:"role"`
	Registered   string       `json:"registered"`
	LastLogin    string       `json:"lastLogin"`
	LastSeen     string       `json:"lastSeen"`
	ProExpires   string       `json:"proExpires"`
	Gender       string       `json:"gender"`
	Language     string       `json:"language"`
	Tagline      string       `json:"tagline" editable:"true"`
	Introduction string       `json:"introduction" editable:"true" type:"textarea"`
	Website      string       `json:"website" editable:"true"`
	IP           string       `json:"ip"`
	UserAgent    string       `json:"agent"`
	Balance      int          `json:"balance"`
	Avatar       UserAvatar   `json:"avatar"`
	Cover        UserCover    `json:"cover"`
	AgeRange     UserAgeRange `json:"ageRange"`
	Location     Location     `json:"location"`
	Accounts     UserAccounts `json:"accounts"`
	Browser      UserBrowser  `json:"browser"`
	OS           UserOS       `json:"os"`
	Following    []string     `json:"following"`
}

// NewUser creates an empty user object with a unique ID.
func NewUser() *User {
	user := &User{
		ID: GenerateID("User"),

		// Avoid nil value fields
		Following: make([]string, 0),
	}

	return user
}

// RegisterUser registers a new user in the database and sets up all the required references.
func RegisterUser(user *User) {
	user.Registered = DateTimeUTC()
	user.LastLogin = user.Registered
	user.LastSeen = user.Registered

	// Save nick in NickToUser table
	DB.Set("NickToUser", user.Nick, &NickToUser{
		Nick:   user.Nick,
		UserID: user.ID,
	})

	// Save email in EmailToUser table
	if user.Email != "" {
		DB.Set("EmailToUser", user.Email, &EmailToUser{
			Email:  user.Email,
			UserID: user.ID,
		})
	}

	// Create default settings
	NewSettings(user).Save()

	// Add empty anime list
	DB.Set("AnimeList", user.ID, &AnimeList{
		UserID: user.ID,
		Items:  []*AnimeListItem{},
	})

	// Add empty inventory
	NewInventory(user.ID).Save()

	// Add draft index
	NewDraftIndex(user.ID).Save()

	// Add empty push subscriptions
	DB.Set("PushSubscriptions", user.ID, &PushSubscriptions{
		UserID: user.ID,
		Items:  []*PushSubscription{},
	})

	// Add empty follow list
	NewUserFollows(user.ID).Save()

	// Add empty notifications list
	NewUserNotifications(user.ID).Save()

	// Fetch gravatar
	if user.Email != "" {
		gravatarURL := gravatar.Url(user.Email) + "?s=" + fmt.Sprint(AvatarMaxSize) + "&d=404&r=pg"
		gravatarURL = strings.Replace(gravatarURL, "http://", "https://", 1)

		response, err := client.Get(gravatarURL).End()

		if err == nil && response.StatusCode() == http.StatusOK {
			data := response.Bytes()
			user.SetAvatarBytes(data)
		}
	}
}

// SendNotification accepts a PushNotification and generates a new Notification object.
// The notification is then sent to all registered push devices.
func (user *User) SendNotification(pushNotification *PushNotification) {
	// Don't ever send notifications in development mode
	if IsDevelopment() && user.ID != "4J6qpK1ve" {
		return
	}

	// Save notification in database
	notification := NewNotification(user.ID, pushNotification)
	notification.Save()

	userNotifications := user.Notifications()
	userNotifications.Add(notification.ID)
	userNotifications.Save()

	// Send push notification
	subs := user.PushSubscriptions()
	expired := []*PushSubscription{}

	for _, sub := range subs.Items {
		resp, err := sub.SendNotification(pushNotification)

		if resp != nil && resp.StatusCode == http.StatusGone {
			expired = append(expired, sub)
			continue
		}

		// Print errors
		if err != nil {
			fmt.Println(err)
			continue
		}

		// Print bad status codes
		if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
			body, _ := ioutil.ReadAll(resp.Body)
			fmt.Println(resp.StatusCode, string(body))
			continue
		}

		sub.LastSuccess = DateTimeUTC()
	}

	// Remove expired items
	if len(expired) > 0 {
		for _, sub := range expired {
			subs.Remove(sub.ID())
		}
	}

	// Save changes
	subs.Save()
}

// RealName returns the real name of the user.
func (user *User) RealName() string {
	if user.LastName == "" {
		return user.FirstName
	}

	if user.FirstName == "" {
		return user.LastName
	}

	return user.FirstName + " " + user.LastName
}

// RegisteredTime returns the time the user registered his account.
func (user *User) RegisteredTime() time.Time {
	reg, _ := time.Parse(time.RFC3339, user.Registered)
	return reg
}

// LastSeenTime returns the time the user was last seen on the site.
func (user *User) LastSeenTime() time.Time {
	lastSeen, _ := time.Parse(time.RFC3339, user.LastSeen)
	return lastSeen
}

// IsActive tells you whether the user is active.
func (user *User) IsActive() bool {
	lastSeen, _ := time.Parse(time.RFC3339, user.LastSeen)
	twoWeeksAgo := time.Now().Add(-14 * 24 * time.Hour)

	if lastSeen.Unix() < twoWeeksAgo.Unix() {
		return false
	}

	if len(user.AnimeList().Items) == 0 {
		return false
	}

	return true
}

// IsPro returns whether the user is a PRO user or not.
func (user *User) IsPro() bool {
	if user.ProExpires == "" {
		return false
	}

	return DateTimeUTC() < user.ProExpires
}

// ExtendProDuration extends the PRO account duration by the given duration.
func (user *User) ExtendProDuration(duration time.Duration) {
	now := time.Now().UTC()
	expires, _ := time.Parse(time.RFC3339, user.ProExpires)

	// If the user never had a PRO account yet or if it already expired,
	// use current time as the start time.
	if user.ProExpires == "" || now.Unix() > expires.Unix() {
		expires = now
	}

	user.ProExpires = expires.Add(duration).Format(time.RFC3339)
}

// TimeSinceRegistered returns the duration since the user registered his account.
func (user *User) TimeSinceRegistered() time.Duration {
	registered, _ := time.Parse(time.RFC3339, user.Registered)
	return time.Since(registered)
}

// HasNick returns whether the user has a custom nickname.
func (user *User) HasNick() bool {
	return !strings.HasPrefix(user.Nick, "g") && !strings.HasPrefix(user.Nick, "fb") && !strings.HasPrefix(user.Nick, "t") && user.Nick != ""
}

// WebsiteURL adds https:// to the URL.
func (user *User) WebsiteURL() string {
	return "https://" + user.WebsiteShortURL()
}

// WebsiteShortURL returns the user website without the protocol.
func (user *User) WebsiteShortURL() string {
	return strings.Replace(strings.Replace(user.Website, "https://", "", 1), "http://", "", 1)
}

// Link returns the URI to the user page.
func (user *User) Link() string {
	return "/+" + user.Nick
}

// HasAvatar tells you whether the user has an avatar or not.
func (user *User) HasAvatar() bool {
	return user.Avatar.Extension != ""
}

// AvatarLink returns the URL to the user avatar.
// Expects "small" (50 x 50) or "large" (560 x 560) for the size parameter.
func (user *User) AvatarLink(size string) string {
	if user.HasAvatar() {
		return fmt.Sprintf("//%s/images/avatars/%s/%s%s?%v", MediaHost, size, user.ID, user.Avatar.Extension, user.Avatar.LastModified)
	}

	return fmt.Sprintf("//%s/images/elements/no-avatar.svg", MediaHost)
}

// CoverLink ...
func (user *User) CoverLink(size string) string {
	if user.Cover.Extension != "" {
		return fmt.Sprintf("//%s/images/covers/%s/%s%s?%v", MediaHost, size, user.ID, user.Cover.Extension, user.Cover.LastModified)
	}

	return "/images/elements/default-cover.jpg"
}

// Gravatar returns the URL to the gravatar if an email has been registered.
func (user *User) Gravatar() string {
	if user.Email == "" {
		return ""
	}

	return gravatar.SecureUrl(user.Email) + "?s=" + fmt.Sprint(AvatarMaxSize)
}

// EditorScore returns the editor score.
func (user *User) EditorScore() int {
	ignoreDifferences := FilterIgnoreAnimeDifferences(func(entry *IgnoreAnimeDifference) bool {
		return entry.CreatedBy == user.ID
	})

	score := len(ignoreDifferences) * IgnoreAnimeDifferenceEditorScore

	logEntries := FilterEditLogEntries(func(entry *EditLogEntry) bool {
		return entry.UserID == user.ID
	})

	for _, entry := range logEntries {
		score += entry.EditorScore()
	}

	return score
}

// ActivateItemEffect activates an item in the user inventory by the given item ID.
func (user *User) ActivateItemEffect(itemID string) error {
	month := 30 * 24 * time.Hour

	switch itemID {
	case "pro-account-3":
		user.ExtendProDuration(3 * month)
		user.Save()
		return nil

	case "pro-account-6":
		user.ExtendProDuration(6 * month)
		user.Save()
		return nil

	case "pro-account-12":
		user.ExtendProDuration(12 * month)
		user.Save()
		return nil

	case "pro-account-24":
		user.ExtendProDuration(24 * month)
		user.Save()
		return nil

	default:
		return errors.New("Can't activate unknown item: " + itemID)
	}
}

// SetNick changes the user's nickname safely.
func (user *User) SetNick(newName string) error {
	setNickMutex.Lock()
	defer setNickMutex.Unlock()

	newName = autocorrect.FixUserNick(newName)

	if !validator.IsValidNick(newName) {
		return errors.New("Invalid nickname")
	}

	if newName == user.Nick {
		return nil
	}

	// Make sure the nickname doesn't exist already
	_, err := GetUserByNick(newName)

	// If there was no error: the username exists.
	// If "not found" is not included in the error message it's a different error type.
	if err == nil || strings.Index(err.Error(), "not found") == -1 {
		return errors.New("Username '" + newName + "' is taken already")
	}

	user.ForceSetNick(newName)
	return nil
}

// ForceSetNick forces a nickname overwrite.
func (user *User) ForceSetNick(newName string) {
	// Delete old nick reference
	DB.Delete("NickToUser", user.Nick)

	// Set new nick
	user.Nick = newName

	DB.Set("NickToUser", user.Nick, &NickToUser{
		Nick:   user.Nick,
		UserID: user.ID,
	})
}

// SetEmail changes the user's email safely.
func (user *User) SetEmail(newName string) error {
	setEmailMutex.Lock()
	defer setEmailMutex.Unlock()

	if !validator.IsValidEmail(user.Email) {
		return errors.New("Invalid email address")
	}

	// Delete old email reference
	DB.Delete("EmailToUser", user.Email)

	// Set new email
	user.Email = newName

	DB.Set("EmailToUser", user.Email, &EmailToUser{
		Email:  user.Email,
		UserID: user.ID,
	})

	return nil
}

// RefreshOsuInfo refreshes a user's Osu information.
func (user *User) RefreshOsuInfo() error {
	if user.Accounts.Osu.Nick == "" {
		return nil
	}

	osu, err := osu.GetUser(user.Accounts.Osu.Nick)

	if err != nil {
		return err
	}

	user.Accounts.Osu.PP, _ = strconv.ParseFloat(osu.PPRaw, 64)
	user.Accounts.Osu.Level, _ = strconv.ParseFloat(osu.Level, 64)
	user.Accounts.Osu.Accuracy, _ = strconv.ParseFloat(osu.Accuracy, 64)

	return nil
}
<|endoftext|>