<commit_before>package admin

import (
	"errors"
	"fmt"

	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app"
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app/args"
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app/with"
	"github.com/BytemarkHosting/bytemark-client/lib"
	"github.com/BytemarkHosting/bytemark-client/lib/billing"
	"github.com/BytemarkHosting/bytemark-client/lib/brain"
	billingMethods "github.com/BytemarkHosting/bytemark-client/lib/requests/billing"
	brainMethods "github.com/BytemarkHosting/bytemark-client/lib/requests/brain"
	"github.com/BytemarkHosting/bytemark-client/util/log"
	"github.com/urfave/cli"
)

func init() {
	readUpdateFlags := func(c *app.Context) (usageStrategy *string, overcommitRatio *int, label *string) {
		if c.Context.IsSet("usage-strategy") {
			v := c.String("usage-strategy")
			usageStrategy = &v
		}

		if c.Context.IsSet("overcommit-ratio") {
			v := c.Int("overcommit-ratio")
			overcommitRatio = &v
		}

		if c.Context.IsSet("label") {
			v := c.String("label")
			label = &v
		}

		return
	}

	Commands = append(Commands, cli.Command{
		Name:   "update",
		Action: cli.ShowSubcommandHelp,
		Subcommands: []cli.Command{
			{
				Name:      "billing-definition",
				Usage:     "update a bmbilling definition",
				UsageText: "bytemark --admin update billing-definition [flags] [name] [value]",
				Flags: []cli.Flag{
					cli.StringFlag{
						Name:  "name",
						Usage: "the name of the definition to set",
					},
					cli.StringFlag{
						Name:  "value",
						Usage: "the value of the definition to set",
					},
					cli.StringFlag{
						Name:  "group",
						Usage: "the group a user must be in to update the definition",
					},
				},
				Action: app.Action(args.Optional("name", "value"), with.RequiredFlags("name", "value"), with.Auth, func(ctx *app.Context) error {
					def := billing.Definition{
						Name:           ctx.String("name"),
						Value:          ctx.String("value"),
						UpdateGroupReq: ctx.String("group"),
					}
					if _, err := billingMethods.GetDefinition(ctx.Client(), def.Name); err != nil {
						if _, ok := err.(lib.NotFoundError); ok {
							ctx.LogErr("Couldn't find a definition called %s - aborting.", def.Name)
							return nil
						}
						return err
					}
					err := billingMethods.UpdateDefinition(ctx.Client(), def)
					if err == nil {
						ctx.LogErr("Updated %s to %s", def.Name, def.Value)
					}
					return err

				}),
			}, {
				Name:      "head",
				Usage:     "update the settings of a head",
				UsageText: "bytemark --admin update head <head> [--usage-strategy] [--overcommit-ratio] [--label]",
				Flags: []cli.Flag{
					cli.StringFlag{
						Name:  "head",
						Usage: "the ID or label of the head to be updated",
					},
					cli.StringFlag{
						Name:  "usage-strategy",
						Usage: "the usage strategy of the head",
					},
					cli.IntFlag{
						Name:  "overcommit-ratio",
						Usage: "the overcommit ratio of the head",
					},
					cli.StringFlag{
						Name:  "label",
						Usage: "the label of the head",
					},
				},
				Action: app.Action(args.Optional("head", "usage-strategy", "overcommit-ratio", "label"), with.RequiredFlags("head"), with.Auth, func(c *app.Context) error {
					usageStrategy, overcommitRatio, label := readUpdateFlags(c)

					options := lib.UpdateHead{
						UsageStrategy:   usageStrategy,
						OvercommitRatio: overcommitRatio,
						Label:           label,
					}

					if err := c.Client().UpdateHead(c.String("head"), options); err != nil {
						return err
					}

					log.Outputf("Head %s updated\n", c.String("head"))

					return nil
				}),
			},
			{
				Name:      "tail",
				Usage:     "update the settings of a tail",
				UsageText: "bytemark --admin update tail <tail> [--usage-strategy] [--overcommit-ratio] [--label]",
				Flags: []cli.Flag{
					cli.StringFlag{
						Name:  "tail",
						Usage: "the ID or label of the tail to be updated",
					},
					cli.StringFlag{
						Name:  "usage-strategy",
						Usage: "the usage strategy of the tail",
					},
					cli.IntFlag{
						Name:  "overcommit-ratio",
						Usage: "the overcommit ratio of the tail",
					},
					cli.StringFlag{
						Name:  "label",
						Usage: "the label of the tail",
					},
				},
				Action: app.Action(args.Optional("tail", "usage-strategy", "overcommit-ratio", "label"), with.RequiredFlags("tail"), with.Auth, func(c *app.Context) error {
					usageStrategy, overcommitRatio, label := readUpdateFlags(c)

					options := lib.UpdateTail{
						UsageStrategy:   usageStrategy,
						OvercommitRatio: overcommitRatio,
						Label:           label,
					}

					if err := c.Client().UpdateTail(c.String("tail"), options); err != nil {
						return err
					}

					log.Outputf("Tail %s updated\n", c.String("tail"))

					return nil
				}),
			},
			{
				Name:      "storage pool",
				Usage:     "update the settings of a storage pool",
				UsageText: "bytemark --admin update storage pool [--usage-strategy new-strategy] [--overcommit-ratio new-ratio] [--label new-label] [--migration-concurrency new-limit] <storage pool>",
				Flags: []cli.Flag{
					cli.StringFlag{
						Name:  "storage-pool",
						Usage: "the ID or label of the storage pool to be updated",
					},
					cli.StringFlag{
						Name:  "usage-strategy",
						Usage: "the usage strategy of the storage pool",
					},
					cli.IntFlag{
						Name:  "overcommit-ratio",
						Usage: "the overcommit ratio of the storage pool",
					},
					cli.StringFlag{
						Name:  "label",
						Usage: "the label of the storage pool",
					},
					cli.IntFlag{
						Name:  "migration-concurrency",
						Usage: "the number of concurrent migrations the storage pool can handle",
					},
				},
				Action: app.Action(args.Optional("storage-pool", "usage-strategy", "overcommit-ratio"), with.RequiredFlags("storage-pool"), with.Auth, func(c *app.Context) error {

					options := brain.StoragePool{
						UsageStrategy:        c.String("usage-strategy"),
						OvercommitRatio:      c.Int("overcommit-ratio"),
						Label:                c.String("label"),
						MigrationConcurrency: c.Int("migration-concurrency"),
					}

					if err := c.Client().UpdateStoragePool(c.String("storage-pool"), options); err != nil {
						return err
					}

					log.Outputf("Storage pool %s updated\n", c.String("storage-pool"))

					return nil
				}),
			},
			{
				Name:    "server",
				Aliases: []string{"vm"},
				Action:  cli.ShowSubcommandHelp,
				Subcommands: []cli.Command{
					{
						Name:        "migration",
						Usage:       "update the settings of an in-progress migration",
						UsageText:   "bytemark --admin update server migration <name> [--migrate-speed] [--migrate-downtime]",
						Description: `This command migrates a server to a new head. If a new head isn't supplied, a new one is picked automatically.`,
						Flags: []cli.Flag{
							cli.GenericFlag{
								Name:  "server",
								Usage: "the server to migrate",
								Value: new(app.VirtualMachineNameFlag),
							},
							cli.Int64Flag{
								Name:  "migrate-speed",
								Usage: "the max speed to migrate the server at",
							},
							cli.IntFlag{
								Name:  "migrate-downtime",
								Usage: "the max allowed downtime",
							},
						},
						Action: app.Action(args.Optional("server", "migrate-speed", "migrate-downtime"), with.RequiredFlags("server"), with.Auth, func(c *app.Context) error {
							vm := c.VirtualMachineName("server")

							var speed *int64
							var downtime *int

							if c.Context.IsSet("migrate-speed") {
								s := c.Int64("migrate-speed")
								speed = &s
							}
							if c.Context.IsSet("migrate-downtime") {
								d := c.Int("migrate-downtime")
								downtime = &d
							}

							if speed == nil && downtime == nil {
								return errors.New("Nothing to update")
							}

							if err := c.Client().UpdateVMMigration(vm, speed, downtime); err != nil {
								return err
							}

							log.Outputf("Migration for server %s updated\n", vm.String())

							return nil
						}),
					},
				},
			},
			{
				Name:        "migration",
				Usage:       "update a migration",
				UsageText:   "bytemark --admin update migration --id <id> --priority <priority> --cancel-disc <disc> --cancel-pool <pool> --cancel-tail <tail> | --cancel-all",
				Description: `This command allows you to update an ongoing migration job by altering its priority, cancelling migrating discs, pools, tails, or canceling everything for the current job`,
				Flags: []cli.Flag{
					cli.IntFlag{
						Name:  "id",
						Usage: "the id of the migration job",
					},
					cli.IntFlag{
						Name:  "priority",
						Usage: "the priority of the current job",
					},
					cli.StringSliceFlag{
						Name:  "cancel-disc",
						Usage: "the disc(s) to cancel migration of",
					},
					cli.StringSliceFlag{
						Name:  "cancel-pool",
						Usage: "the pool(s) to cancel migration of",
					},
					cli.StringSliceFlag{
						Name:  "cancel-tail",
						Usage: "the tail(s) to cancel migration of",
					},
					cli.BoolFlag{
						Name:  "cancel-all",
						Usage: "cancel the all migrations of the job",
					},
				},
				Action: app.Action(with.RequiredFlags("id"), with.Auth, func(c *app.Context) error {
					id := c.Context.Int("id")
					discs := c.Context.StringSlice("cancel-disc")
					pools := c.Context.StringSlice("cancel-pool")
					tails := c.Context.StringSlice("cancel-tail")

					allCancelled := append(discs, pools...)
					allCancelled = append(allCancelled, tails...)

					if len(allCancelled) == 0 && !c.Context.IsSet("priority") && !c.Context.IsSet("cancel-all") {
						return fmt.Errorf("No Flags have been set. Please specify a priority, ")
					}

					if c.Context.IsSet("cancel-all") {
						if len(allCancelled) > 0 || c.Context.IsSet("priority") {
							return fmt.Errorf("You have set additional flags as well as --cancel-all. Nothing else can be specified when --cancel-all has been set")
						}

						err := brainMethods.CancelMigrationJob(c.Client(), id)
						if err != nil {
							return err
						}
						c.LogErr("All migrations for job %d have been cancelled.", id)
						return err
					}

					modifications := brain.MigrationJobModification{
						Cancel: brain.MigrationJobLocations{
							Discs: stringsToNumberOrStrings(discs),
							Pools: stringsToNumberOrStrings(pools),
							Tails: stringsToNumberOrStrings(tails),
						},
						Options: brain.MigrationJobOptions{
							Priority: c.Context.Int("priority"),
						},
					}

					err := brainMethods.EditMigrationJob(c.Client(), id, modifications)
					if err != nil {
						return err
					}

					if c.Context.IsSet("priority") {
						c.LogErr("Priority updated for job %d", id)
					}

					for _, cancelled := range allCancelled {
						c.LogErr("Migration cancelled for %s on job %d", cancelled, id)
					}

					return err
				}),
			},
		},
	})
}
<commit_msg>use log instead of logerr<commit_after>package admin

import (
	"errors"
	"fmt"

	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app"
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app/args"
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app/with"
	"github.com/BytemarkHosting/bytemark-client/lib"
	"github.com/BytemarkHosting/bytemark-client/lib/billing"
	"github.com/BytemarkHosting/bytemark-client/lib/brain"
	billingMethods "github.com/BytemarkHosting/bytemark-client/lib/requests/billing"
	brainMethods "github.com/BytemarkHosting/bytemark-client/lib/requests/brain"
	"github.com/BytemarkHosting/bytemark-client/util/log"
	"github.com/urfave/cli"
)

func init() {
	readUpdateFlags := func(c *app.Context) (usageStrategy *string, overcommitRatio *int, label *string) {
		if c.Context.IsSet("usage-strategy") {
			v := c.String("usage-strategy")
			usageStrategy = &v
		}

		if c.Context.IsSet("overcommit-ratio") {
			v := c.Int("overcommit-ratio")
			overcommitRatio = &v
		}

		if c.Context.IsSet("label") {
			v := c.String("label")
			label = &v
		}

		return
	}

	Commands = append(Commands, cli.Command{
		Name:   "update",
		Action: cli.ShowSubcommandHelp,
		Subcommands: []cli.Command{
			{
				Name:      "billing-definition",
				Usage:     "update a bmbilling definition",
				UsageText: "bytemark --admin update billing-definition [flags] [name] [value]",
				Flags: []cli.Flag{
					cli.StringFlag{
						Name:  "name",
						Usage: "the name of the definition to set",
					},
					cli.StringFlag{
						Name:  "value",
						Usage: "the value of the definition to set",
					},
					cli.StringFlag{
						Name:  "group",
						Usage: "the group a user must be in to update the definition",
					},
				},
				Action: app.Action(args.Optional("name", "value"), with.RequiredFlags("name", "value"), with.Auth, func(ctx *app.Context) error {
					def := billing.Definition{
						Name:           ctx.String("name"),
						Value:          ctx.String("value"),
						UpdateGroupReq: ctx.String("group"),
					}
					if _, err := billingMethods.GetDefinition(ctx.Client(), def.Name); err != nil {
						if _, ok := err.(lib.NotFoundError); ok {
							ctx.LogErr("Couldn't find a definition called %s - aborting.", def.Name)
							return nil
						}
						return err
					}
					err := billingMethods.UpdateDefinition(ctx.Client(), def)
					if err == nil {
						ctx.LogErr("Updated %s to %s", def.Name, def.Value)
					}
					return err

				}),
			}, {
				Name:      "head",
				Usage:     "update the settings of a head",
				UsageText: "bytemark --admin update head <head> [--usage-strategy] [--overcommit-ratio] [--label]",
				Flags: []cli.Flag{
					cli.StringFlag{
						Name:  "head",
						Usage: "the ID or label of the head to be updated",
					},
					cli.StringFlag{
						Name:  "usage-strategy",
						Usage: "the usage strategy of the head",
					},
					cli.IntFlag{
						Name:  "overcommit-ratio",
						Usage: "the overcommit ratio of the head",
					},
					cli.StringFlag{
						Name:  "label",
						Usage: "the label of the head",
					},
				},
				Action: app.Action(args.Optional("head", "usage-strategy", "overcommit-ratio", "label"), with.RequiredFlags("head"), with.Auth, func(c *app.Context) error {
					usageStrategy, overcommitRatio, label := readUpdateFlags(c)

					options := lib.UpdateHead{
						UsageStrategy:   usageStrategy,
						OvercommitRatio: overcommitRatio,
						Label:           label,
					}

					if err := c.Client().UpdateHead(c.String("head"), options); err != nil {
						return err
					}

					log.Outputf("Head %s updated\n", c.String("head"))

					return nil
				}),
			},
			{
				Name:      "tail",
				Usage:     "update the settings of a tail",
				UsageText: "bytemark --admin update tail <tail> [--usage-strategy] [--overcommit-ratio] [--label]",
				Flags: []cli.Flag{
					cli.StringFlag{
						Name:  "tail",
						Usage: "the ID or label of the tail to be updated",
					},
					cli.StringFlag{
						Name:  "usage-strategy",
						Usage: "the usage strategy of the tail",
					},
					cli.IntFlag{
						Name:  "overcommit-ratio",
						Usage: "the overcommit ratio of the tail",
					},
					cli.StringFlag{
						Name:  "label",
						Usage: "the label of the tail",
					},
				},
				Action: app.Action(args.Optional("tail", "usage-strategy", "overcommit-ratio", "label"), with.RequiredFlags("tail"), with.Auth, func(c *app.Context) error {
					usageStrategy, overcommitRatio, label := readUpdateFlags(c)

					options := lib.UpdateTail{
						UsageStrategy:   usageStrategy,
						OvercommitRatio: overcommitRatio,
						Label:           label,
					}

					if err := c.Client().UpdateTail(c.String("tail"), options); err != nil {
						return err
					}

					log.Outputf("Tail %s updated\n", c.String("tail"))

					return nil
				}),
			},
			{
				Name:      "storage pool",
				Usage:     "update the settings of a storage pool",
				UsageText: "bytemark --admin update storage pool [--usage-strategy new-strategy] [--overcommit-ratio new-ratio] [--label new-label] [--migration-concurrency new-limit] <storage pool>",
				Flags: []cli.Flag{
					cli.StringFlag{
						Name:  "storage-pool",
						Usage: "the ID or label of the storage pool to be updated",
					},
					cli.StringFlag{
						Name:  "usage-strategy",
						Usage: "the usage strategy of the storage pool",
					},
					cli.IntFlag{
						Name:  "overcommit-ratio",
						Usage: "the overcommit ratio of the storage pool",
					},
					cli.StringFlag{
						Name:  "label",
						Usage: "the label of the storage pool",
					},
					cli.IntFlag{
						Name:  "migration-concurrency",
						Usage: "the number of concurrent migrations the storage pool can handle",
					},
				},
				Action: app.Action(args.Optional("storage-pool", "usage-strategy", "overcommit-ratio"), with.RequiredFlags("storage-pool"), with.Auth, func(c *app.Context) error {

					options := brain.StoragePool{
						UsageStrategy:        c.String("usage-strategy"),
						OvercommitRatio:      c.Int("overcommit-ratio"),
						Label:                c.String("label"),
						MigrationConcurrency: c.Int("migration-concurrency"),
					}

					if err := c.Client().UpdateStoragePool(c.String("storage-pool"), options); err != nil {
						return err
					}

					log.Outputf("Storage pool %s updated\n", c.String("storage-pool"))

					return nil
				}),
			},
			{
				Name:    "server",
				Aliases: []string{"vm"},
				Action:  cli.ShowSubcommandHelp,
				Subcommands: []cli.Command{
					{
						Name:        "migration",
						Usage:       "update the settings of an in-progress migration",
						UsageText:   "bytemark --admin update server migration <name> [--migrate-speed] [--migrate-downtime]",
						Description: `This command migrates a server to a new head. If a new head isn't supplied, a new one is picked automatically.`,
						Flags: []cli.Flag{
							cli.GenericFlag{
								Name:  "server",
								Usage: "the server to migrate",
								Value: new(app.VirtualMachineNameFlag),
							},
							cli.Int64Flag{
								Name:  "migrate-speed",
								Usage: "the max speed to migrate the server at",
							},
							cli.IntFlag{
								Name:  "migrate-downtime",
								Usage: "the max allowed downtime",
							},
						},
						Action: app.Action(args.Optional("server", "migrate-speed", "migrate-downtime"), with.RequiredFlags("server"), with.Auth, func(c *app.Context) error {
							vm := c.VirtualMachineName("server")

							var speed *int64
							var downtime *int

							if c.Context.IsSet("migrate-speed") {
								s := c.Int64("migrate-speed")
								speed = &s
							}
							if c.Context.IsSet("migrate-downtime") {
								d := c.Int("migrate-downtime")
								downtime = &d
							}

							if speed == nil && downtime == nil {
								return errors.New("Nothing to update")
							}

							if err := c.Client().UpdateVMMigration(vm, speed, downtime); err != nil {
								return err
							}

							log.Outputf("Migration for server %s updated\n", vm.String())

							return nil
						}),
					},
				},
			},
			{
				Name:        "migration",
				Usage:       "update a migration",
				UsageText:   "bytemark --admin update migration --id <id> --priority <priority> --cancel-disc <disc> --cancel-pool <pool> --cancel-tail <tail> | --cancel-all",
				Description: `This command allows you to update an ongoing migration job by altering its priority, cancelling migrating discs, pools, tails, or canceling everything for the current job`,
				Flags: []cli.Flag{
					cli.IntFlag{
						Name:  "id",
						Usage: "the id of the migration job",
					},
					cli.IntFlag{
						Name:  "priority",
						Usage: "the priority of the current job",
					},
					cli.StringSliceFlag{
						Name:  "cancel-disc",
						Usage: "the disc(s) to cancel migration of",
					},
					cli.StringSliceFlag{
						Name:  "cancel-pool",
						Usage: "the pool(s) to cancel migration of",
					},
					cli.StringSliceFlag{
						Name:  "cancel-tail",
						Usage: "the tail(s) to cancel migration of",
					},
					cli.BoolFlag{
						Name:  "cancel-all",
						Usage: "cancel the all migrations of the job",
					},
				},
				Action: app.Action(with.RequiredFlags("id"), with.Auth, func(c *app.Context) error {
					id := c.Context.Int("id")
					discs := c.Context.StringSlice("cancel-disc")
					pools := c.Context.StringSlice("cancel-pool")
					tails := c.Context.StringSlice("cancel-tail")

					allCancelled := append(discs, pools...)
					allCancelled = append(allCancelled, tails...)

					if len(allCancelled) == 0 && !c.Context.IsSet("priority") && !c.Context.IsSet("cancel-all") {
						return fmt.Errorf("No Flags have been set. Please specify a priority, ")
					}

					if c.Context.IsSet("cancel-all") {
						if len(allCancelled) > 0 || c.Context.IsSet("priority") {
							return fmt.Errorf("You have set additional flags as well as --cancel-all. Nothing else can be specified when --cancel-all has been set")
						}

						err := brainMethods.CancelMigrationJob(c.Client(), id)
						if err != nil {
							return err
						}
						c.LogErr("All migrations for job %d have been cancelled.", id)
						return err
					}

					modifications := brain.MigrationJobModification{
						Cancel: brain.MigrationJobLocations{
							Discs: stringsToNumberOrStrings(discs),
							Pools: stringsToNumberOrStrings(pools),
							Tails: stringsToNumberOrStrings(tails),
						},
						Options: brain.MigrationJobOptions{
							Priority: c.Context.Int("priority"),
						},
					}

					err := brainMethods.EditMigrationJob(c.Client(), id, modifications)
					if err != nil {
						return err
					}

					if c.Context.IsSet("priority") {
						c.Log("Priority updated for job %d", id)
					}

					for _, cancelled := range allCancelled {
						c.Log("Migration cancelled for %s on job %d", cancelled, id)
					}

					return err
				}),
			},
		},
	})
}
<|endoftext|>


<commit_before>// Copyright 2017 ibelie, Chen Jie, Joungtao. All rights reserved.
// Use of this source code is governed by The MIT License
// that can be found in the LICENSE file.

package rpc

import (
	"bytes"
	"fmt"
	"path"
	"strings"

	"io/ioutil"

	"github.com/ibelie/rpc/python"
	"github.com/ibelie/tygo"
)

func Python(identName string, input string, pyOut string, ignore []string) (entities []*Entity) {
	pkg := python.Extract(input, pyOut, ignore)
	components := make(map[string]*Component)
	for n, c := range pkg.Components {
		components[n] = &Component{
			Name:    c.Name,
			Path:    c.Package,
			Methods: c.Messages,
		}
	}
	for _, e := range pkg.Entities {
		entity := &Entity{Name: e.Name}
		for _, c := range e.Components {
			if component, ok := components[c]; ok {
				entity.Components = append(entity.Components, component)
			}
		}
		entities = append(entities, entity)
	}

	types := resolveEntities(entities)
	var methods []tygo.Type
	for _, t := range types {
		if object, ok := t.(*tygo.Object); ok {
			for _, field := range object.VisibleFields() {
				methods = append(methods, &tygo.Object{
					Name:   fmt.Sprintf("%s_%s", object.Name, field.Name),
					Parent: &tygo.InstanceType{PkgName: "tygo", PkgPath: tygo.TYGO_PATH, Name: "Tygo"},
					Fields: []*tygo.Field{field},
				})
			}

			for _, method := range object.Methods {
				if len(method.Params) > 0 {
					var params []*tygo.Field
					for i, p := range method.Params {
						params = append(params, &tygo.Field{Type: p, Name: fmt.Sprintf("a%d", i)})
					}
					methods = append(methods, &tygo.Object{
						Name:   fmt.Sprintf("%s_%sParam", object.Name, method.Name),
						Parent: &tygo.InstanceType{PkgName: "tygo", PkgPath: tygo.TYGO_PATH, Name: "Tygo"},
						Fields: params,
					})
				}
				if len(method.Results) > 0 {
					var results []*tygo.Field
					for i, r := range method.Results {
						results = append(results, &tygo.Field{Type: r, Name: fmt.Sprintf("a%d", i)})
					}
					methods = append(methods, &tygo.Object{
						Name:   fmt.Sprintf("%s_%sResult", object.Name, method.Name),
						Parent: &tygo.InstanceType{PkgName: "tygo", PkgPath: tygo.TYGO_PATH, Name: "Tygo"},
						Fields: results,
					})
				}
			}
		}
	}
	types = append(types, methods...)

	tygo.Python(pyOut, "types", types)
	tygo.Typyd(pyOut, "_typy", types)
	injectPython(pyOut, entities)
	return entities
}

func injectPython(dir string, entities []*Entity) {
	var buffer bytes.Buffer
	var types []string

	buffer.Write([]byte(fmt.Sprintf(`#-*- coding: utf-8 -*-
# Generated by ibelie-rpc.  DO NOT EDIT!

import typy

%s
`, strings.Join(types, ""))))

	ioutil.WriteFile(path.Join(dir, "proto.py"), buffer.Bytes(), 0666)
}
<commit_msg>change interface<commit_after>// Copyright 2017 ibelie, Chen Jie, Joungtao. All rights reserved.
// Use of this source code is governed by The MIT License
// that can be found in the LICENSE file.

package rpc

import (
	"bytes"
	"fmt"
	"path"

	"io/ioutil"

	"github.com/ibelie/rpc/python"
	"github.com/ibelie/tygo"
)

const PY_HEADER = `#-*- coding: utf-8 -*-
# Generated by ibelie-rpc.  DO NOT EDIT!
`

func Python(identName string, input string, pyOut string, ignore []string) (entities []*Entity) {
	pkg := python.Extract(input, pyOut, ignore)
	components := make(map[string]*Component)
	for n, c := range pkg.Components {
		components[n] = &Component{
			Name:    c.Name,
			Path:    c.Package,
			Methods: c.Messages,
		}
	}
	for _, e := range pkg.Entities {
		entity := &Entity{Name: e.Name}
		for _, c := range e.Components {
			if component, ok := components[c]; ok {
				entity.Components = append(entity.Components, component)
			}
		}
		entities = append(entities, entity)
	}

	types := resolveEntities(entities)
	var methods []tygo.Type
	for _, t := range types {
		if object, ok := t.(*tygo.Object); ok {
			for _, field := range object.VisibleFields() {
				methods = append(methods, &tygo.Object{
					Name:   fmt.Sprintf("%s_%s", object.Name, field.Name),
					Parent: &tygo.InstanceType{PkgName: "tygo", PkgPath: tygo.TYGO_PATH, Name: "Tygo"},
					Fields: []*tygo.Field{field},
				})
			}

			for _, method := range object.Methods {
				if len(method.Params) > 0 {
					var params []*tygo.Field
					for i, p := range method.Params {
						params = append(params, &tygo.Field{Type: p, Name: fmt.Sprintf("a%d", i)})
					}
					methods = append(methods, &tygo.Object{
						Name:   fmt.Sprintf("%s_%sParam", object.Name, method.Name),
						Parent: &tygo.InstanceType{PkgName: "tygo", PkgPath: tygo.TYGO_PATH, Name: "Tygo"},
						Fields: params,
					})
				}
				if len(method.Results) > 0 {
					var results []*tygo.Field
					for i, r := range method.Results {
						results = append(results, &tygo.Field{Type: r, Name: fmt.Sprintf("a%d", i)})
					}
					methods = append(methods, &tygo.Object{
						Name:   fmt.Sprintf("%s_%sResult", object.Name, method.Name),
						Parent: &tygo.InstanceType{PkgName: "tygo", PkgPath: tygo.TYGO_PATH, Name: "Tygo"},
						Fields: results,
					})
				}
			}
		}
	}
	types = append(types, methods...)

	var buffer bytes.Buffer
	buffer.Write([]byte(PY_HEADER))
	buffer.Write([]byte(fmt.Sprintf(`
IDType = '%s'
`, identName)))
	buffer.Write(tygo.Python(types))
	ioutil.WriteFile(path.Join(pyOut, "proto.py"), buffer.Bytes(), 0666)

	buffer.Truncate(0)
	buffer.Write([]byte(PY_HEADER))
	buffer.Write([]byte(fmt.Sprintf(`
IDType = '%s'
`, identName)))
	buffer.Write(tygo.Typyd(types))
	ioutil.WriteFile(path.Join(pyOut, "_proto.py"), buffer.Bytes(), 0666)

	return entities
}
<|endoftext|>


<commit_before>// Copyright 2017 ibelie, Chen Jie, Joungtao. All rights reserved.
// Use of this source code is governed by The MIT License
// that can be found in the LICENSE file.

package tygo

import (
	"bytes"
	"fmt"
	"log"
	"path"
	"sort"
	"strings"

	"io/ioutil"
)

var (
	TS_MODULE     string
	TS_CUR_MODULE string
	TS_EX_TYPE    bool
	TS_OBJECTS    map[string]*Object
	EXTENS_PKG    map[string]string
)

func Typescript(dir string, name string, module string, types []Type, propPre []Type) {
	var buffer bytes.Buffer

	PROP_PRE = propPre
	TS_MODULE = module
	TS_OBJECTS = ObjectMap(types, TS_MODULE == "")

	var pkgTypes map[string][]Type
	var sortedPkgs []string
	if TS_MODULE == "" {
		pkgTypes = PkgTypeMap(types)
		for pkg, _ := range pkgTypes {
			sortedPkgs = append(sortedPkgs, pkg)
		}
		sort.Strings(sortedPkgs)
	} else {
		pkgTypes = map[string][]Type{module: types}
		sortedPkgs = []string{module}
	}

	var modules []string
	for _, pkg := range sortedPkgs {
		ts := pkgTypes[pkg]
		TS_CUR_MODULE = pkg
		TS_EX_TYPE = false

		var codes []string
		for _, t := range ts {
			codes = append(codes, t.Typescript())
		}

		exType := ""
		if TS_EX_TYPE {
			exType = `
	interface Type {
		__class__: string;
		ByteSize(): number;
		Serialize(): Uint8Array;
		Deserialize(data: Uint8Array): void;
	}`
		}

		modules = append(modules, fmt.Sprintf(`
declare module %s {%s%s
}
`, strings.Replace(pkg, "/", ".", -1), exType, strings.Join(codes, "")))
	}

	PROP_PRE = nil
	TS_OBJECTS = nil
	TS_EX_TYPE = false
	TS_MODULE = ""
	TS_CUR_MODULE = ""

	buffer.Write([]byte(fmt.Sprintf(`// Generated for tyts by tygo.  DO NOT EDIT!
%s`, strings.Join(modules, ""))))

	if name == "" {
		name = module
	}
	ioutil.WriteFile(path.Join(dir, name+".d.ts"), buffer.Bytes(), 0666)
	Javascript(dir, name, module, types, propPre)
}

func (t *Enum) Typescript() string {
	var enums []string
	for _, name := range t.Sorted() {
		enums = append(enums, fmt.Sprintf(`
		%s = %d`, name, t.Values[name]))
	}
	return fmt.Sprintf(`

	const enum %s {%s
	}`, t.Name, strings.Join(enums, ","))
}

func typeListTypescript(name string, typ string, ts []Type) string {
	var items []string
	for i, t := range ts {
		items = append(items, fmt.Sprintf("a%d: %s", i, t.Typescript()))
	}
	return fmt.Sprintf(`
		static S_%s%s(%s): Uint8Array;
		static D_%s%s(data: Uint8Array): any;`, name, typ, strings.Join(items, ", "), name, typ)
}

func (t *Object) Typescript() string {
	var parent string
	if t.HasParent() {
		parent = fmt.Sprintf(" extends %s", t.Parent.Typescript())
	}
	var members []string
	for _, field := range t.VisibleFields() {
		members = append(members, fmt.Sprintf(`
		%s: %s;`, field.Name, field.Typescript()))
	}

	if PROP_PRE != nil {
		for _, field := range t.VisibleFields() {
			members = append(members, typeListTypescript(field.Name, "", []Type{field}))
		}
	}

	for _, method := range t.Methods {
		if len(method.Params) > 0 {
			members = append(members, typeListTypescript(method.Name, "Param", method.Params))
		}
		if len(method.Results) > 0 {
			members = append(members, typeListTypescript(method.Name, "Result", method.Results))
		}
	}

	return fmt.Sprintf(`

	class %s%s {
		__class__: string;
		ByteSize(): number;
		Serialize(): Uint8Array;
		Deserialize(data: Uint8Array): void;
%s
	}

	namespace %s {
		function Deserialize(data: Uint8Array): %s;
	}`, t.Name, parent, strings.Join(members, ""), t.Name, t.Name)
}

func (t UnknownType) Typescript() string {
	return ""
}

func (t SimpleType) Typescript() string {
	switch t {
	case SimpleType_INT32:
		fallthrough
	case SimpleType_INT64:
		fallthrough
	case SimpleType_UINT32:
		fallthrough
	case SimpleType_UINT64:
		fallthrough
	case SimpleType_FLOAT32:
		fallthrough
	case SimpleType_FLOAT64:
		return "number"
	case SimpleType_BYTES:
		return "Uint8Array"
	case SimpleType_SYMBOL:
		fallthrough
	case SimpleType_STRING:
		return "string"
	case SimpleType_BOOL:
		return "boolean"
	default:
		log.Fatalf("[Tygo][SimpleType] Unexpect enum value for Typescript: %d", t)
		return "unknown"
	}
}

func (t *EnumType) Typescript() string {
	return t.Name
}

func (t *InstanceType) Typescript() string {
	fullName := t.Name
	if TS_MODULE == "" && t.PkgPath != "" {
		fullName = t.PkgPath + "/" + t.Name
	} else if EXTENS_PKG != nil {
		if pkg, ok := EXTENS_PKG[t.Name]; ok {
			if TS_CUR_MODULE == pkg {
				return t.Name
			} else {
				return strings.Replace(pkg, "/", ".", -1) + "." + t.Name
			}
		}
	}
	if _, ok := TS_OBJECTS[fullName]; ok {
		if TS_CUR_MODULE == t.PkgPath {
			return t.Name
		}
		return strings.Replace(fullName, "/", ".", -1)
	} else {
		TS_EX_TYPE = true
		return "Type"
	}
}

func (t *FixedPointType) Typescript() string {
	return "number"
}

func (t *ListType) Typescript() string {
	return fmt.Sprintf("%s[]", t.E.Typescript())
}

func (t *DictType) Typescript() string {
	return fmt.Sprintf("{[index: %s]: %s}", t.K.Typescript(), t.V.Typescript())
}

func (t *VariantType) Typescript() string {
	return "any"
}
<commit_msg>fix typescript instance type<commit_after>// Copyright 2017 ibelie, Chen Jie, Joungtao. All rights reserved.
// Use of this source code is governed by The MIT License
// that can be found in the LICENSE file.

package tygo

import (
	"bytes"
	"fmt"
	"log"
	"path"
	"sort"
	"strings"

	"io/ioutil"
)

var (
	TS_MODULE     string
	TS_CUR_MODULE string
	TS_EX_TYPE    bool
	TS_OBJECTS    map[string]*Object
	EXTENS_PKG    map[string]string
)

func Typescript(dir string, name string, module string, types []Type, propPre []Type) {
	var buffer bytes.Buffer

	PROP_PRE = propPre
	TS_MODULE = module
	TS_OBJECTS = ObjectMap(types, TS_MODULE == "")

	var pkgTypes map[string][]Type
	var sortedPkgs []string
	if TS_MODULE == "" {
		pkgTypes = PkgTypeMap(types)
		for pkg, _ := range pkgTypes {
			sortedPkgs = append(sortedPkgs, pkg)
		}
		sort.Strings(sortedPkgs)
	} else {
		pkgTypes = map[string][]Type{module: types}
		sortedPkgs = []string{module}
	}

	var modules []string
	for _, pkg := range sortedPkgs {
		ts := pkgTypes[pkg]
		TS_CUR_MODULE = pkg
		TS_EX_TYPE = false

		var codes []string
		for _, t := range ts {
			codes = append(codes, t.Typescript())
		}

		exType := ""
		if TS_EX_TYPE {
			exType = `
	interface Type {
		__class__: string;
		ByteSize(): number;
		Serialize(): Uint8Array;
		Deserialize(data: Uint8Array): void;
	}`
		}

		modules = append(modules, fmt.Sprintf(`
declare module %s {%s%s
}
`, strings.Replace(pkg, "/", ".", -1), exType, strings.Join(codes, "")))
	}

	PROP_PRE = nil
	TS_OBJECTS = nil
	TS_EX_TYPE = false
	TS_MODULE = ""
	TS_CUR_MODULE = ""

	buffer.Write([]byte(fmt.Sprintf(`// Generated for tyts by tygo.  DO NOT EDIT!
%s`, strings.Join(modules, ""))))

	if name == "" {
		name = module
	}
	ioutil.WriteFile(path.Join(dir, name+".d.ts"), buffer.Bytes(), 0666)
	Javascript(dir, name, module, types, propPre)
}

func (t *Enum) Typescript() string {
	var enums []string
	for _, name := range t.Sorted() {
		enums = append(enums, fmt.Sprintf(`
		%s = %d`, name, t.Values[name]))
	}
	return fmt.Sprintf(`

	const enum %s {%s
	}`, t.Name, strings.Join(enums, ","))
}

func typeListTypescript(name string, typ string, ts []Type) string {
	var items []string
	for i, t := range ts {
		items = append(items, fmt.Sprintf("a%d: %s", i, t.Typescript()))
	}
	return fmt.Sprintf(`
		static S_%s%s(%s): Uint8Array;
		static D_%s%s(data: Uint8Array): any;`, name, typ, strings.Join(items, ", "), name, typ)
}

func (t *Object) Typescript() string {
	var parent string
	if t.HasParent() {
		parent = fmt.Sprintf(" extends %s", t.Parent.Typescript())
	}
	var members []string
	for _, field := range t.VisibleFields() {
		members = append(members, fmt.Sprintf(`
		%s: %s;`, field.Name, field.Typescript()))
	}

	if PROP_PRE != nil {
		for _, field := range t.VisibleFields() {
			members = append(members, typeListTypescript(field.Name, "", []Type{field}))
		}
	}

	for _, method := range t.Methods {
		if len(method.Params) > 0 {
			members = append(members, typeListTypescript(method.Name, "Param", method.Params))
		}
		if len(method.Results) > 0 {
			members = append(members, typeListTypescript(method.Name, "Result", method.Results))
		}
	}

	return fmt.Sprintf(`

	class %s%s {
		__class__: string;
		ByteSize(): number;
		Serialize(): Uint8Array;
		Deserialize(data: Uint8Array): void;
%s
	}

	namespace %s {
		function Deserialize(data: Uint8Array): %s;
	}`, t.Name, parent, strings.Join(members, ""), t.Name, t.Name)
}

func (t UnknownType) Typescript() string {
	return ""
}

func (t SimpleType) Typescript() string {
	switch t {
	case SimpleType_INT32:
		fallthrough
	case SimpleType_INT64:
		fallthrough
	case SimpleType_UINT32:
		fallthrough
	case SimpleType_UINT64:
		fallthrough
	case SimpleType_FLOAT32:
		fallthrough
	case SimpleType_FLOAT64:
		return "number"
	case SimpleType_BYTES:
		return "Uint8Array"
	case SimpleType_SYMBOL:
		fallthrough
	case SimpleType_STRING:
		return "string"
	case SimpleType_BOOL:
		return "boolean"
	default:
		log.Fatalf("[Tygo][SimpleType] Unexpect enum value for Typescript: %d", t)
		return "unknown"
	}
}

func (t *EnumType) Typescript() string {
	return t.Name
}

func (t *InstanceType) Typescript() string {
	fullName := t.Name
	if t.Object != nil {
		fullName = t.Object.FullName()
	} else if TS_MODULE == "" && t.PkgPath != "" {
		fullName = t.PkgPath + "/" + t.Name
	} else if EXTENS_PKG != nil {
		if pkg, ok := EXTENS_PKG[t.Name]; ok {
			if TS_CUR_MODULE == pkg {
				return t.Name
			} else {
				return strings.Replace(pkg, "/", ".", -1) + "." + t.Name
			}
		}
	}
	if _, ok := TS_OBJECTS[fullName]; ok {
		if TS_CUR_MODULE == t.PkgPath {
			return t.Name
		}
		return strings.Replace(fullName, "/", ".", -1)
	} else {
		TS_EX_TYPE = true
		return "Type"
	}
}

func (t *FixedPointType) Typescript() string {
	return "number"
}

func (t *ListType) Typescript() string {
	return fmt.Sprintf("%s[]", t.E.Typescript())
}

func (t *DictType) Typescript() string {
	return fmt.Sprintf("{[index: %s]: %s}", t.K.Typescript(), t.V.Typescript())
}

func (t *VariantType) Typescript() string {
	return "any"
}
<|endoftext|>


<commit_before>package infrastructure

import (
	"database/sql"
	"fmt"
	"time"

	_ "github.com/lib/pq"
	"github.com/webitel/cdr/src/conf"
	"github.com/webitel/cdr/src/interfaces"
	"github.com/webitel/cdr/src/logger"
)

type PostgresHandler struct {
	Conn *sql.DB
}

var pgConfig conf.Postgres

func NewPostgresHandler() (*PostgresHandler, error) {
	pgConfig = conf.GetPostgres()
	psqlInfo := fmt.Sprintf("host=%s port=%d user=%s "+
		"password=%s dbname=%s sslmode=disable",
		pgConfig.Host, pgConfig.Port, pgConfig.User, pgConfig.Password, pgConfig.Database)
	var pgHandler *PostgresHandler
	for c := time.Tick(5 * time.Second); ; <-c {
		dbConnection, err := sql.Open("postgres", psqlInfo)
		if err != nil {
			logger.Error("PostgreSQL Connection: " + err.Error())
			continue
		}
		if err = dbConnection.Ping(); err != nil {
			logger.Error("PostgreSQL Ping: " + err.Error())
			continue
		}
		pgHandler = new(PostgresHandler)
		pgHandler.Conn = dbConnection
		logger.Debug("PostgreSQL: connect to %s:%v", pgConfig.Host, pgConfig.Port)
		break
	}
	return pgHandler, nil
}

func (handler *PostgresHandler) ExecuteQuery(query string, params ...interface{}) error {
	_, err := handler.Conn.Exec(query, params...)
	if err != nil {
		return fmt.Errorf("PostgreSQL. Execute script error.\nError message: %s\n Query: %s\n", err, query)
	}
	return err
}

func (handler *PostgresHandler) GetRows(query string, params ...interface{}) (interfaces.Row, error) {
	rows, err := handler.Conn.Query(query, params...)
	if err != nil {
		return nil, fmt.Errorf("PostgreSQL. Get rows error.\nError message: %s\n Query: %s\n", err, query)
	}
	row := new(PostgresRow)
	row.Rows = rows
	return row, nil
}

func (handler *PostgresHandler) CreateTable(query string) error {
	_, err := handler.Conn.Exec(query)
	if err != nil {
		return fmt.Errorf("PostgreSQL. Create table error: %s", err)
	}
	return err
}

type PostgresRow struct {
	Rows *sql.Rows
}

func (r PostgresRow) Scan(dest ...interface{}) error {
	return r.Rows.Scan(dest...)
}

func (r PostgresRow) Next() bool {
	return r.Rows.Next()
}

func (r PostgresRow) Close() error {
	return r.Rows.Close()
}
<commit_msg>commit update<commit_after>package infrastructure

import (
	"database/sql"
	"fmt"
	"time"

	_ "github.com/lib/pq"
	"github.com/webitel/cdr/src/conf"
	"github.com/webitel/cdr/src/interfaces"
	"github.com/webitel/cdr/src/logger"
)

type PostgresHandler struct {
	Conn *sql.DB
}

var pgConfig conf.Postgres

func NewPostgresHandler() (*PostgresHandler, error) {
	pgConfig = conf.GetPostgres()
	psqlInfo := fmt.Sprintf("host=%s port=%d user=%s "+
		"password=%s dbname=%s sslmode=disable",
		pgConfig.Host, pgConfig.Port, pgConfig.User, pgConfig.Password, pgConfig.Database)
	var pgHandler *PostgresHandler
	for c := time.Tick(5 * time.Second); ; <-c {
		dbConnection, err := sql.Open("postgres", psqlInfo)
		if err != nil {
			logger.Error("PostgreSQL Connection: " + err.Error())
			continue
		}
		if err = dbConnection.Ping(); err != nil {
			logger.Error("PostgreSQL Ping: " + err.Error())
			continue
		}
		pgHandler = new(PostgresHandler)
		pgHandler.Conn = dbConnection
		logger.Debug("PostgreSQL: connect to %s:%v", pgConfig.Host, pgConfig.Port)
		break
	}
	return pgHandler, nil
}

func (handler *PostgresHandler) ExecuteQuery(query string, params ...interface{}) error {
	_, err := handler.Conn.Exec(query, params...)
	if err != nil {
		return fmt.Errorf("PostgreSQL. Execute script error.\nError message: %s\n Query: %s\n", err, query)
	}
	_, err = handler.Conn.Exec("commit;")
	if err != nil {
		return fmt.Errorf("PostgreSQL. Execute script error.\nError message: %s\n Query: %s\n", err, query)
	}
	return err
}

func (handler *PostgresHandler) GetRows(query string, params ...interface{}) (interfaces.Row, error) {
	rows, err := handler.Conn.Query(query, params...)
	if err != nil {
		return nil, fmt.Errorf("PostgreSQL. Get rows error.\nError message: %s\n Query: %s\n", err, query)
	}
	row := new(PostgresRow)
	row.Rows = rows
	return row, nil
}

func (handler *PostgresHandler) CreateTable(query string) error {
	_, err := handler.Conn.Exec(query)
	if err != nil {
		return fmt.Errorf("PostgreSQL. Create table error: %s", err)
	}
	return err
}

type PostgresRow struct {
	Rows *sql.Rows
}

func (r PostgresRow) Scan(dest ...interface{}) error {
	return r.Rows.Scan(dest...)
}

func (r PostgresRow) Next() bool {
	return r.Rows.Next()
}

func (r PostgresRow) Close() error {
	return r.Rows.Close()
}
<|endoftext|>


<commit_before>package native

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"syscall"
	"time"

	"github.com/go-errors/errors"
	"github.com/itchio/butler/buse"
	"github.com/itchio/butler/cmd/launch"
	"github.com/itchio/butler/cmd/operate"
	"github.com/itchio/butler/cmd/wipe"
)

func Register() {
	launch.Register(launch.LaunchStrategyNative, &Launcher{})
}

type Launcher struct{}

var _ launch.Launcher = (*Launcher)(nil)

func (l *Launcher) Do(params *launch.LauncherParams) error {
	ctx := params.Ctx
	conn := params.Conn
	consumer := params.Consumer
	installFolder := params.ParentParams.InstallFolder

	cwd := installFolder
	_, err := filepath.Rel(installFolder, params.FullTargetPath)
	if err != nil {
		// if it's relative, set the cwd to the folder the
		// target is in
		cwd = filepath.Dir(params.FullTargetPath)
	}

	_, err = os.Stat(params.FullTargetPath)
	if err != nil {
		return errors.Wrap(err, 0)
	}

	err = handlePrereqs(params)
	if err != nil {
		if errors.Is(err, operate.ErrAborted) {
			return err
		}

		consumer.Warnf("While handling prereqs: %s", err.Error())

		var r buse.PrereqsFailedResult
		var errorStack string
		if se, ok := err.(*errors.Error); ok {
			errorStack = se.ErrorStack()
		}

		err = conn.Call(ctx, "PrereqsFailed", &buse.PrereqsFailedParams{
			Error:      err.Error(),
			ErrorStack: errorStack,
		}, &r)
		if err != nil {
			return errors.Wrap(err, 0)
		}

		if r.Continue {
			// continue!
			consumer.Warnf("Continuing after prereqs failure because user told us to")
		} else {
			// abort
			consumer.Warnf("Giving up after prereqs failure because user asked us to")
			return operate.ErrAborted
		}
	}

	cmd := exec.Command(params.FullTargetPath, params.Args...)
	cmd.Dir = cwd

	envMap := make(map[string]string)
	for k, v := range params.Env {
		envMap[k] = v
	}

	// give the app its own temporary directory
	tempDir := filepath.Join(params.ParentParams.InstallFolder, ".itch", "temp")
	err = os.MkdirAll(tempDir, 0755)
	if err != nil {
		consumer.Warnf("Could not make temporary directory: %s", err.Error())
	} else {
		defer wipe.Do(consumer, tempDir)
		envMap["TMP"] = tempDir
		envMap["TEMP"] = tempDir
		consumer.Infof("Giving app temp dir (%s)", tempDir)
	}

	var envKeys []string
	for k := range envMap {
		envKeys = append(envKeys, k)
	}
	consumer.Infof("Environment variables passed: %s", strings.Join(envKeys, ", "))

	// TODO: sanitize environment somewhat?
	envBlock := os.Environ()
	for k, v := range envMap {
		envBlock = append(envBlock, fmt.Sprintf("%s=%s", k, v))
	}

	cmd.Env = envBlock

	const maxLines = 40
	stdout := newOutputCollector(maxLines)
	cmd.Stdout = stdout

	stderr := newOutputCollector(maxLines)
	cmd.Stderr = stderr

	err = func() error {
		err = cmd.Start()
		if err != nil {
			return errors.Wrap(err, 0)
		}

		startTime := time.Now()

		conn.Notify(ctx, "LaunchRunning", &buse.LaunchRunningNotification{})
		exitCode, err := waitCommand(cmd)
		conn.Notify(ctx, "LaunchExited", &buse.LaunchExitedNotification{})

		if err != nil {
			return errors.Wrap(err, 0)
		}

		runDuration := time.Since(startTime)

		if exitCode != 0 {
			var signedExitCode = int64(exitCode)
			if runtime.GOOS == "windows" {
				// Windows uses 32-bit unsigned integers as exit codes,[11] although the
				// command interpreter treats them as signed.[12] If a process fails
				// initialization, a Windows system error code may be returned.[13][14]
				signedExitCode = int64(int32(signedExitCode))

				// The line above turns `4294967295` into -1
			}

			exeName := filepath.Base(params.FullTargetPath)
			msg := fmt.Sprintf("Exit code 0x%x (%d) for (%s)", uint32(exitCode), signedExitCode, exeName)
			consumer.Warnf(msg)

			if runDuration.Seconds() > 10 {
				consumer.Warnf("That's after running for %s, ignoring non-zero exit code", runDuration)
			} else {
				return errors.New(msg)
			}
		}

		return nil
	}()

	if err != nil {
		consumer.Errorf("Had error: %s", err.Error())
		if len(stderr.Lines()) == 0 {
			consumer.Errorf("No messages for standard error")
			consumer.Errorf("→ Standard error: empty")
		} else {
			consumer.Errorf("→ Standard error ================")
			for _, l := range stderr.Lines() {
				consumer.Errorf("  %s", l)
			}
			consumer.Errorf("=================================")
		}

		if len(stdout.Lines()) == 0 {
			consumer.Errorf("→ Standard output: empty")
		} else {
			consumer.Errorf("→ Standard output ===============")
			for _, l := range stdout.Lines() {
				consumer.Errorf("  %s", l)
			}
			consumer.Errorf("=================================")
		}
		consumer.Errorf("Relaying launch failure.")
		return errors.Wrap(err, 0)
	}

	return nil
}

func waitCommand(cmd *exec.Cmd) (int, error) {
	err := cmd.Wait()
	if err != nil {
		if exitError, ok := err.(*exec.ExitError); ok {
			if status, ok := exitError.Sys().(syscall.WaitStatus); ok {
				return status.ExitStatus(), nil
			}
		}

		return 127, err
	}

	return 0, nil
}
<commit_msg>fix setting cwd<commit_after>package native

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"syscall"
	"time"

	"github.com/go-errors/errors"
	"github.com/itchio/butler/buse"
	"github.com/itchio/butler/cmd/launch"
	"github.com/itchio/butler/cmd/operate"
	"github.com/itchio/butler/cmd/wipe"
)

func Register() {
	launch.Register(launch.LaunchStrategyNative, &Launcher{})
}

type Launcher struct{}

var _ launch.Launcher = (*Launcher)(nil)

func (l *Launcher) Do(params *launch.LauncherParams) error {
	ctx := params.Ctx
	conn := params.Conn
	consumer := params.Consumer
	installFolder := params.ParentParams.InstallFolder

	cwd := installFolder
	_, err := filepath.Rel(installFolder, params.FullTargetPath)
	if err == nil {
		// if it's relative, set the cwd to the folder the
		// target is in
		cwd = filepath.Dir(params.FullTargetPath)
	}

	_, err = os.Stat(params.FullTargetPath)
	if err != nil {
		return errors.Wrap(err, 0)
	}

	err = handlePrereqs(params)
	if err != nil {
		if errors.Is(err, operate.ErrAborted) {
			return err
		}

		consumer.Warnf("While handling prereqs: %s", err.Error())

		var r buse.PrereqsFailedResult
		var errorStack string
		if se, ok := err.(*errors.Error); ok {
			errorStack = se.ErrorStack()
		}

		err = conn.Call(ctx, "PrereqsFailed", &buse.PrereqsFailedParams{
			Error:      err.Error(),
			ErrorStack: errorStack,
		}, &r)
		if err != nil {
			return errors.Wrap(err, 0)
		}

		if r.Continue {
			// continue!
			consumer.Warnf("Continuing after prereqs failure because user told us to")
		} else {
			// abort
			consumer.Warnf("Giving up after prereqs failure because user asked us to")
			return operate.ErrAborted
		}
	}

	cmd := exec.Command(params.FullTargetPath, params.Args...)
	cmd.Dir = cwd

	envMap := make(map[string]string)
	for k, v := range params.Env {
		envMap[k] = v
	}

	// give the app its own temporary directory
	tempDir := filepath.Join(params.ParentParams.InstallFolder, ".itch", "temp")
	err = os.MkdirAll(tempDir, 0755)
	if err != nil {
		consumer.Warnf("Could not make temporary directory: %s", err.Error())
	} else {
		defer wipe.Do(consumer, tempDir)
		envMap["TMP"] = tempDir
		envMap["TEMP"] = tempDir
		consumer.Infof("Giving app temp dir (%s)", tempDir)
	}

	var envKeys []string
	for k := range envMap {
		envKeys = append(envKeys, k)
	}
	consumer.Infof("Environment variables passed: %s", strings.Join(envKeys, ", "))

	// TODO: sanitize environment somewhat?
	envBlock := os.Environ()
	for k, v := range envMap {
		envBlock = append(envBlock, fmt.Sprintf("%s=%s", k, v))
	}

	cmd.Env = envBlock

	const maxLines = 40
	stdout := newOutputCollector(maxLines)
	cmd.Stdout = stdout

	stderr := newOutputCollector(maxLines)
	cmd.Stderr = stderr

	err = func() error {
		err = cmd.Start()
		if err != nil {
			return errors.Wrap(err, 0)
		}

		startTime := time.Now()

		conn.Notify(ctx, "LaunchRunning", &buse.LaunchRunningNotification{})
		exitCode, err := waitCommand(cmd)
		conn.Notify(ctx, "LaunchExited", &buse.LaunchExitedNotification{})

		if err != nil {
			return errors.Wrap(err, 0)
		}

		runDuration := time.Since(startTime)

		if exitCode != 0 {
			var signedExitCode = int64(exitCode)
			if runtime.GOOS == "windows" {
				// Windows uses 32-bit unsigned integers as exit codes,[11] although the
				// command interpreter treats them as signed.[12] If a process fails
				// initialization, a Windows system error code may be returned.[13][14]
				signedExitCode = int64(int32(signedExitCode))

				// The line above turns `4294967295` into -1
			}

			exeName := filepath.Base(params.FullTargetPath)
			msg := fmt.Sprintf("Exit code 0x%x (%d) for (%s)", uint32(exitCode), signedExitCode, exeName)
			consumer.Warnf(msg)

			if runDuration.Seconds() > 10 {
				consumer.Warnf("That's after running for %s, ignoring non-zero exit code", runDuration)
			} else {
				return errors.New(msg)
			}
		}

		return nil
	}()

	if err != nil {
		consumer.Errorf("Had error: %s", err.Error())
		if len(stderr.Lines()) == 0 {
			consumer.Errorf("No messages for standard error")
			consumer.Errorf("→ Standard error: empty")
		} else {
			consumer.Errorf("→ Standard error ================")
			for _, l := range stderr.Lines() {
				consumer.Errorf("  %s", l)
			}
			consumer.Errorf("=================================")
		}

		if len(stdout.Lines()) == 0 {
			consumer.Errorf("→ Standard output: empty")
		} else {
			consumer.Errorf("→ Standard output ===============")
			for _, l := range stdout.Lines() {
				consumer.Errorf("  %s", l)
			}
			consumer.Errorf("=================================")
		}
		consumer.Errorf("Relaying launch failure.")
		return errors.Wrap(err, 0)
	}

	return nil
}

func waitCommand(cmd *exec.Cmd) (int, error) {
	err := cmd.Wait()
	if err != nil {
		if exitError, ok := err.(*exec.ExitError); ok {
			if status, ok := exitError.Sys().(syscall.WaitStatus); ok {
				return status.ExitStatus(), nil
			}
		}

		return 127, err
	}

	return 0, nil
}
<|endoftext|>


<commit_before>package ethutil

import (
	"log"
	"os"
	"os/user"
	"path"
)

type LogType byte

const (
	LogTypeStdIn = 1
	LogTypeFile  = 2
)

// Config struct isn't exposed
type config struct {
	Db Database

	Log      Logger
	ExecPath string
	Debug    bool
	Ver      string
	Pubkey   []byte
	Seed     bool
}

var Config *config

// Read config doesn't read anything yet.
func ReadConfig(base string) *config {
	if Config == nil {
		usr, _ := user.Current()
		path := path.Join(usr.HomeDir, base)

		//Check if the logging directory already exists, create it if not
		_, err := os.Stat(path)
		if err != nil {
			if os.IsNotExist(err) {
				log.Printf("Debug logging directory %s doesn't exist, creating it", path)
				os.Mkdir(path, 0777)
			}
		}

		Config = &config{ExecPath: path, Debug: true, Ver: "0.2.1"}
		Config.Log = NewLogger(LogFile|LogStd, 0)
	}

	return Config
}

type LoggerType byte

const (
	LogFile = 0x1
	LogStd  = 0x2
)

type Logger struct {
	logSys   []*log.Logger
	logLevel int
}

func NewLogger(flag LoggerType, level int) Logger {
	var loggers []*log.Logger

	flags := log.LstdFlags | log.Lshortfile

	if flag&LogFile > 0 {
		file, err := os.OpenFile(path.Join(Config.ExecPath, "debug.log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, os.ModePerm)
		if err != nil {
			log.Panic("unable to create file logger", err)
		}

		log := log.New(file, "[ETH]", flags)

		loggers = append(loggers, log)
	}
	if flag&LogStd > 0 {
		log := log.New(os.Stdout, "[ETH]", flags)
		loggers = append(loggers, log)
	}

	return Logger{logSys: loggers, logLevel: level}
}

func (log Logger) Debugln(v ...interface{}) {
	if log.logLevel != 0 {
		return
	}

	for _, logger := range log.logSys {
		logger.Println(v...)
	}
}

func (log Logger) Debugf(format string, v ...interface{}) {
	if log.logLevel != 0 {
		return
	}

	for _, logger := range log.logSys {
		logger.Printf(format, v...)
	}
}
<commit_msg>Bumped version number<commit_after>package ethutil

import (
	"log"
	"os"
	"os/user"
	"path"
)

type LogType byte

const (
	LogTypeStdIn = 1
	LogTypeFile  = 2
)

// Config struct isn't exposed
type config struct {
	Db Database

	Log      Logger
	ExecPath string
	Debug    bool
	Ver      string
	Pubkey   []byte
	Seed     bool
}

var Config *config

// Read config doesn't read anything yet.
func ReadConfig(base string) *config {
	if Config == nil {
		usr, _ := user.Current()
		path := path.Join(usr.HomeDir, base)

		//Check if the logging directory already exists, create it if not
		_, err := os.Stat(path)
		if err != nil {
			if os.IsNotExist(err) {
				log.Printf("Debug logging directory %s doesn't exist, creating it", path)
				os.Mkdir(path, 0777)
			}
		}

		Config = &config{ExecPath: path, Debug: true, Ver: "0.2.2"}
		Config.Log = NewLogger(LogFile|LogStd, 0)
	}

	return Config
}

type LoggerType byte

const (
	LogFile = 0x1
	LogStd  = 0x2
)

type Logger struct {
	logSys   []*log.Logger
	logLevel int
}

func NewLogger(flag LoggerType, level int) Logger {
	var loggers []*log.Logger

	flags := log.LstdFlags | log.Lshortfile

	if flag&LogFile > 0 {
		file, err := os.OpenFile(path.Join(Config.ExecPath, "debug.log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, os.ModePerm)
		if err != nil {
			log.Panic("unable to create file logger", err)
		}

		log := log.New(file, "[ETH]", flags)

		loggers = append(loggers, log)
	}
	if flag&LogStd > 0 {
		log := log.New(os.Stdout, "[ETH]", flags)
		loggers = append(loggers, log)
	}

	return Logger{logSys: loggers, logLevel: level}
}

func (log Logger) Debugln(v ...interface{}) {
	if log.logLevel != 0 {
		return
	}

	for _, logger := range log.logSys {
		logger.Println(v...)
	}
}

func (log Logger) Debugf(format string, v ...interface{}) {
	if log.logLevel != 0 {
		return
	}

	for _, logger := range log.logSys {
		logger.Printf(format, v...)
	}
}
<|endoftext|>


<commit_before>package lang

import "fmt"

func Compile(mod Module) bytecode {
	if virt, ok := mod.(*VirtualModule); ok {
		main := compileProgram(virt.Scope(), virt.ast)
		fmt.Println(main.String())
		return main
	}

	return bytecode{}
}

func compileProgram(s Scope, prog *RootNode) bytecode {
	blob := bytecode{}
	for _, name := range s.GetLocalVariableNames() {
		symbol := s.GetLocalVariableReference(name)
		blob.write(InstrReserve{name, symbol})
	}

	blob.append(compileStmts(s, prog.Stmts))
	blob.write(InstrHalt{})
	return blob
}

func compileStmts(s Scope, stmts []Stmt) (blob bytecode) {
	for _, stmt := range stmts {
		blob.append(compileStmt(s, stmt))
	}
	return blob
}

func compileStmt(s Scope, stmt Stmt) bytecode {
	switch stmt := stmt.(type) {
	case *IfStmt:
		return compileIfStmt(s, stmt)
	case *ReturnStmt:
		return compileReturnStmt(s, stmt)
	case *DeclarationStmt:
		return compileDeclarationStmt(s, stmt)
	case *ExprStmt:
		return compileExprStmt(s, stmt)
	default:
		panic(fmt.Sprintf("cannot compile %T", stmt))
	}
}

func compileIfStmt(s Scope, stmt *IfStmt) bytecode {
	blob := compileExpr(s, stmt.Cond)
	jump := blob.write(InstrNOP{}) // Pending jump to end of clause
	done := blob.append(compileStmts(s, stmt.Clause.Stmts))
	blob.overwrite(jump, InstrJumpFalse{done})
	return blob
}

func compileReturnStmt(s Scope, stmt *ReturnStmt) (blob bytecode) {
	if stmt.Expr != nil {
		blob.append(compileExpr(s, stmt.Expr))
	}
	blob.write(InstrReturn{})
	return blob
}

func compileDeclarationStmt(s Scope, stmt *DeclarationStmt) bytecode {
	blob := compileExpr(s, stmt.Expr)
	symbol := s.GetVariableReference(stmt.Name.Name)
	blob.write(InstrStore{stmt.Name.Name, symbol})
	return blob
}

func compileExprStmt(s Scope, stmt *ExprStmt) bytecode {
	blob := compileExpr(s, stmt.Expr)
	blob.write(InstrPop{})
	return blob
}

func compileExpr(s Scope, expr Expr) bytecode {
	switch expr := expr.(type) {
	case *FunctionExpr:
		return compileFunctionExpr(s, expr)
	case *DispatchExpr:
		return compileDispatchExpr(s, expr)
	case *AssignExpr:
		return compileAssignExpr(s, expr)
	case *BinaryExpr:
		return compileBinaryExpr(s, expr)
	case *SelfExpr:
		return compileSelfExpr(s, expr)
	case *IdentExpr:
		return compileIdentExpr(s, expr)
	case *NumberExpr:
		return compileNumberExpr(s, expr)
	case *StringExpr:
		return compileStringExpr(s, expr)
	case *BooleanExpr:
		return compileBoolExpr(s, expr)
	default:
		panic(fmt.Sprintf("cannot transform expression %T", expr))
	}
}

func compileFunctionExpr(s Scope, expr *FunctionExpr) (blob bytecode) {
	local := s.GetChild(expr)
	var params []*UniqueSymbol
	for _, param := range expr.Params {
		name := param.Name.Name
		symbol := local.GetLocalVariableReference(name)
		params = append(params, symbol)
	}

	bodyBlob := bytecode{}
	for _, name := range local.GetLocalVariableNames() {
		isParam := false
		for _, param := range expr.Params {
			if param.Name.Name == name {
				isParam = true
				break
			}
		}

		if isParam == false {
			symbol := local.GetLocalVariableReference(name)
			bodyBlob.write(InstrReserve{name, symbol})
		}
	}

	bodyBlob.append(compileStmts(local, expr.Block.Stmts))
	bodyBlob.write(InstrReturn{})

	function := ObjectFunction{
		params:   params,
		bytecode: bodyBlob,
	}

	fmt.Println(bodyBlob.String())
	fmt.Println("---")

	blob.write(InstrPush{function})
	return blob
}

func compileDispatchExpr(s Scope, expr *DispatchExpr) (blob bytecode) {
	for _, arg := range expr.Args {
		blob.append(compileExpr(s, arg))
	}
	blob.append(compileExpr(s, expr.Callee))
	blob.write(InstrDispatch{args: len(expr.Args)})
	return blob
}

func compileAssignExpr(s Scope, expr *AssignExpr) bytecode {
	blob := compileExpr(s, expr.Right)
	symbol := s.GetVariableReference(expr.Left.Name)
	blob.write(InstrCopy{})
	blob.write(InstrStore{expr.Left.Name, symbol})
	return blob
}

func compileBinaryExpr(s Scope, expr *BinaryExpr) bytecode {
	blob := compileExpr(s, expr.Left)
	blob.append(compileExpr(s, expr.Right))

	switch expr.Oper {
	case "+":
		blob.write(InstrAdd{})
	case "-":
		blob.write(InstrSub{})
	case "*":
		blob.write(InstrMul{})
	case "<":
		blob.write(InstrLT{})
	case "<=":
		blob.write(InstrLTEquals{})
	case ">":
		blob.write(InstrGT{})
	case ">=":
		blob.write(InstrGTEquals{})
	default:
		panic(fmt.Sprintf("cannot compile %T with '%s'", expr, expr.Oper))
	}

	return blob
}

func compileSelfExpr(s Scope, expr *SelfExpr) bytecode {
	blob := bytecode{}
	blob.write(InstrLoadSelf{})
	return blob
}

func compileIdentExpr(s Scope, expr *IdentExpr) bytecode {
	blob := bytecode{}
	symbol := s.GetVariableReference(expr.Name)
	blob.write(InstrLoad{expr.Name, symbol})
	return blob
}

func compileNumberExpr(s Scope, expr *NumberExpr) (blob bytecode) {
	blob.write(InstrPush{ObjectInt{int64(expr.Val)}})
	return blob
}

func compileStringExpr(s Scope, expr *StringExpr) (blob bytecode) {
	blob.write(InstrPush{ObjectStr{expr.Val}})
	return blob
}

func compileBoolExpr(s Scope, expr *BooleanExpr) (blob bytecode) {
	blob.write(InstrPush{ObjectBool{expr.Val}})
	return blob
}
<commit_msg>compile pub statements<commit_after>package lang

import "fmt"

func Compile(mod Module) bytecode {
	if virt, ok := mod.(*VirtualModule); ok {
		main := compileProgram(virt.Scope(), virt.ast)
		fmt.Println(main.String())
		return main
	}

	return bytecode{}
}

func compileProgram(s Scope, prog *RootNode) bytecode {
	blob := bytecode{}
	for _, name := range s.GetLocalVariableNames() {
		symbol := s.GetLocalVariableReference(name)
		blob.write(InstrReserve{name, symbol})
	}

	blob.append(compileStmts(s, prog.Stmts))
	blob.write(InstrHalt{})
	return blob
}

func compileStmts(s Scope, stmts []Stmt) (blob bytecode) {
	for _, stmt := range stmts {
		blob.append(compileStmt(s, stmt))
	}
	return blob
}

func compileStmt(s Scope, stmt Stmt) bytecode {
	switch stmt := stmt.(type) {
	case *PubStmt:
		return compilePubStmt(s, stmt)
	case *IfStmt:
		return compileIfStmt(s, stmt)
	case *ReturnStmt:
		return compileReturnStmt(s, stmt)
	case *DeclarationStmt:
		return compileDeclarationStmt(s, stmt)
	case *ExprStmt:
		return compileExprStmt(s, stmt)
	default:
		panic(fmt.Sprintf("cannot compile %T", stmt))
	}
}

func compilePubStmt(s Scope, stmt *PubStmt) bytecode {
	return compileStmt(s, stmt.Stmt)
}

func compileIfStmt(s Scope, stmt *IfStmt) bytecode {
	blob := compileExpr(s, stmt.Cond)
	jump := blob.write(InstrNOP{}) // Pending jump to end of clause
	done := blob.append(compileStmts(s, stmt.Clause.Stmts))
	blob.overwrite(jump, InstrJumpFalse{done})
	return blob
}

func compileReturnStmt(s Scope, stmt *ReturnStmt) (blob bytecode) {
	if stmt.Expr != nil {
		blob.append(compileExpr(s, stmt.Expr))
	}
	blob.write(InstrReturn{})
	return blob
}

func compileDeclarationStmt(s Scope, stmt *DeclarationStmt) bytecode {
	blob := compileExpr(s, stmt.Expr)
	symbol := s.GetVariableReference(stmt.Name.Name)
	blob.write(InstrStore{stmt.Name.Name, symbol})
	return blob
}

func compileExprStmt(s Scope, stmt *ExprStmt) bytecode {
	blob := compileExpr(s, stmt.Expr)
	blob.write(InstrPop{})
	return blob
}

func compileExpr(s Scope, expr Expr) bytecode {
	switch expr := expr.(type) {
	case *FunctionExpr:
		return compileFunctionExpr(s, expr)
	case *DispatchExpr:
		return compileDispatchExpr(s, expr)
	case *AssignExpr:
		return compileAssignExpr(s, expr)
	case *BinaryExpr:
		return compileBinaryExpr(s, expr)
	case *SelfExpr:
		return compileSelfExpr(s, expr)
	case *IdentExpr:
		return compileIdentExpr(s, expr)
	case *NumberExpr:
		return compileNumberExpr(s, expr)
	case *StringExpr:
		return compileStringExpr(s, expr)
	case *BooleanExpr:
		return compileBoolExpr(s, expr)
	default:
		panic(fmt.Sprintf("cannot transform expression %T", expr))
	}
}

func compileFunctionExpr(s Scope, expr *FunctionExpr) (blob bytecode) {
	local := s.GetChild(expr)
	var params []*UniqueSymbol
	for _, param := range expr.Params {
		name := param.Name.Name
		symbol := local.GetLocalVariableReference(name)
		params = append(params, symbol)
	}

	bodyBlob := bytecode{}
	for _, name := range local.GetLocalVariableNames() {
		isParam := false
		for _, param := range expr.Params {
			if param.Name.Name == name {
				isParam = true
				break
			}
		}

		if isParam == false {
			symbol := local.GetLocalVariableReference(name)
			bodyBlob.write(InstrReserve{name, symbol})
		}
	}

	bodyBlob.append(compileStmts(local, expr.Block.Stmts))
	bodyBlob.write(InstrReturn{})

	function := ObjectFunction{
		params:   params,
		bytecode: bodyBlob,
	}

	fmt.Println(bodyBlob.String())
	fmt.Println("---")

	blob.write(InstrPush{function})
	return blob
}

func compileDispatchExpr(s Scope, expr *DispatchExpr) (blob bytecode) {
	for _, arg := range expr.Args {
		blob.append(compileExpr(s, arg))
	}
	blob.append(compileExpr(s, expr.Callee))
	blob.write(InstrDispatch{args: len(expr.Args)})
	return blob
}

func compileAssignExpr(s Scope, expr *AssignExpr) bytecode {
	blob := compileExpr(s, expr.Right)
	symbol := s.GetVariableReference(expr.Left.Name)
	blob.write(InstrCopy{})
	blob.write(InstrStore{expr.Left.Name, symbol})
	return blob
}

func compileBinaryExpr(s Scope, expr *BinaryExpr) bytecode {
	blob := compileExpr(s, expr.Left)
	blob.append(compileExpr(s, expr.Right))

	switch expr.Oper {
	case "+":
		blob.write(InstrAdd{})
	case "-":
		blob.write(InstrSub{})
	case "*":
		blob.write(InstrMul{})
	case "<":
		blob.write(InstrLT{})
	case "<=":
		blob.write(InstrLTEquals{})
	case ">":
		blob.write(InstrGT{})
	case ">=":
		blob.write(InstrGTEquals{})
	default:
		panic(fmt.Sprintf("cannot compile %T with '%s'", expr, expr.Oper))
	}

	return blob
}

func compileSelfExpr(s Scope, expr *SelfExpr) bytecode {
	blob := bytecode{}
	blob.write(InstrLoadSelf{})
	return blob
}

func compileIdentExpr(s Scope, expr *IdentExpr) bytecode {
	blob := bytecode{}
	symbol := s.GetVariableReference(expr.Name)
	blob.write(InstrLoad{expr.Name, symbol})
	return blob
}

func compileNumberExpr(s Scope, expr *NumberExpr) (blob bytecode) {
	blob.write(InstrPush{ObjectInt{int64(expr.Val)}})
	return blob
}

func compileStringExpr(s Scope, expr *StringExpr) (blob bytecode) {
	blob.write(InstrPush{ObjectStr{expr.Val}})
	return blob
}

func compileBoolExpr(s Scope, expr *BooleanExpr) (blob bytecode) {
	blob.write(InstrPush{ObjectBool{expr.Val}})
	return blob
}
<|endoftext|>


<commit_before>package negronilogrus

import (
	"fmt"
	"net/http"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/codegangsta/negroni"
)

// Middleware is a middleware handler that logs the request as it goes in and the response as it goes out.
type Middleware struct {
	// Logger is the log.Logger instance used to log messages with the Logger middleware
	Logger *logrus.Logger
	// Name is the name of the application as recorded in latency metrics
	Name string
}

// NewMiddleware returns a new *Middleware, yay!
func NewMiddleware() *Middleware {
	return NewCustomMiddleware(logrus.InfoLevel, &logrus.TextFormatter{}, "web")
}

// NewCustomMiddleware builds a *Middleware with the given level and formatter
func NewCustomMiddleware(level logrus.Level, formatter logrus.Formatter, name string) *Middleware {
	log := logrus.New()
	log.Level = level
	log.Formatter = formatter

	return &Middleware{Logger: log, Name: name}
}

func (l *Middleware) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	start := time.Now()
	l.Logger.WithFields(logrus.Fields{
		"method":  r.Method,
		"request": r.RequestURI,
		"remote":  r.RemoteAddr,
	}).Info("started handling request")

	next(rw, r)

	latency := time.Since(start)
	res := rw.(negroni.ResponseWriter)
	l.Logger.WithFields(logrus.Fields{
		"status":      res.Status(),
		"method":      r.Method,
		"request":     r.RequestURI,
		"remote":      r.RemoteAddr,
		"text_status": http.StatusText(res.Status()),
		"took":        latency,
		fmt.Sprintf("measure#%s.latency", l.Name): latency.Nanoseconds(),
	}).Info("completed handling request")
}
<commit_msg>Add x-request-id field<commit_after>package negronilogrus

import (
	"fmt"
	"net/http"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/codegangsta/negroni"
)

// Middleware is a middleware handler that logs the request as it goes in and the response as it goes out.
type Middleware struct {
	// Logger is the log.Logger instance used to log messages with the Logger middleware
	Logger *logrus.Logger
	// Name is the name of the application as recorded in latency metrics
	Name string
}

// NewMiddleware returns a new *Middleware, yay!
func NewMiddleware() *Middleware {
	return NewCustomMiddleware(logrus.InfoLevel, &logrus.TextFormatter{}, "web")
}

// NewCustomMiddleware builds a *Middleware with the given level and formatter
func NewCustomMiddleware(level logrus.Level, formatter logrus.Formatter, name string) *Middleware {
	log := logrus.New()
	log.Level = level
	log.Formatter = formatter

	return &Middleware{Logger: log, Name: name}
}

func (l *Middleware) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	start := time.Now()
	l.Logger.WithFields(logrus.Fields{
		"method":     r.Method,
		"request":    r.RequestURI,
		"request_id": r.Header.Get("X-Request-Id"),
		"remote":     r.RemoteAddr,
	}).Info("started handling request")

	next(rw, r)

	latency := time.Since(start)
	res := rw.(negroni.ResponseWriter)
	l.Logger.WithFields(logrus.Fields{
		"status":      res.Status(),
		"method":      r.Method,
		"request":     r.RequestURI,
		"request_id":  r.Header.Get("X-Request-Id"),
		"remote":      r.RemoteAddr,
		"text_status": http.StatusText(res.Status()),
		"took":        latency,
		fmt.Sprintf("measure#%s.latency", l.Name): latency.Nanoseconds(),
	}).Info("completed handling request")
}
<|endoftext|>


<commit_before>package db

import "net/http"

// Database interfaces

type IList interface {
	Add(value string) error
	GetAll() ([]string, error)
	GetLast() (string, error)
	GetLastN(n int) ([]string, error)
	Remove() error
	Clear() error
}

type ISet interface {
	Add(value string) error
	Has(value string) (bool, error)
	GetAll() ([]string, error)
	Del(value string) error
	Remove() error
	Clear() error
}

type IHashMap interface {
	Set(owner, key, value string) error
	Get(owner, key string) (string, error)
	Has(owner, key string) (bool, error)
	Exists(owner string) (bool, error)
	GetAll() ([]string, error)
	DelKey(owner, key string) error
	Del(key string) error
	Remove() error
	Clear() error
}

type IKeyValue interface {
	Set(key, value string) error
	Get(key string) (string, error)
	Del(key string) error
	Remove() error
	Clear() error
}

// Interface for making it possible to depend on different versions of the permission package, or other packages that implement userstates.
type IUserState interface {
	UserRights(req *http.Request) bool
	HasUser(username string) bool
	BooleanField(username, fieldname string) bool
	SetBooleanField(username, fieldname string, val bool)
	IsConfirmed(username string) bool
	IsLoggedIn(username string) bool
	AdminRights(req *http.Request) bool
	IsAdmin(username string) bool
	UsernameCookie(req *http.Request) (string, error)
	SetUsernameCookie(w http.ResponseWriter, username string) error
	AllUsernames() ([]string, error)
	Email(username string) (string, error)
	PasswordHash(username string) (string, error)
	AllUnconfirmedUsernames() ([]string, error)
	ConfirmationCode(username string) (string, error)
	AddUnconfirmed(username, confirmationCode string)
	RemoveUnconfirmed(username string)
	MarkConfirmed(username string)
	RemoveUser(username string)
	SetAdminStatus(username string)
	RemoveAdminStatus(username string)
	addUserUnchecked(username, passwordHash, email string)
	AddUser(username, password, email string)
	SetLoggedIn(username string)
	SetLoggedOut(username string)
	Login(w http.ResponseWriter, username string)
	Logout(username string)
	Username(req *http.Request) string
	CookieTimeout(username string) int64
	SetCookieTimeout(cookieTime int64)
	PasswordAlgo() string
	SetPasswordAlgo(algorithm string) error
	HashPassword(username, password string) string
	CorrectPassword(username, password string) bool
	AlreadyHasConfirmationCode(confirmationCode string) bool
	FindUserByConfirmationCode(confirmationcode string) (string, error)
	Confirm(username string)
	ConfirmUserByConfirmationCode(confirmationcode string) error
	SetMinimumConfirmationCodeLength(length int)
	GenerateUniqueConfirmationCode() (string, error)

	// Related to the database backend
	Users() *IHashMap
	Host() *IHost
}

// A database host
type IHost interface {
	Ping() error
	Close()
}
<commit_msg>Adjustments to the interface<commit_after>package db

import "net/http"

// Database interfaces

type IList interface {
	Add(value string) error
	GetAll() ([]string, error)
	GetLast() (string, error)
	GetLastN(n int) ([]string, error)
	Remove() error
	Clear() error
}

type ISet interface {
	Add(value string) error
	Has(value string) (bool, error)
	GetAll() ([]string, error)
	Del(value string) error
	Remove() error
	Clear() error
}

type IHashMap interface {
	Set(owner, key, value string) error
	Get(owner, key string) (string, error)
	Has(owner, key string) (bool, error)
	Exists(owner string) (bool, error)
	GetAll() ([]string, error)
	DelKey(owner, key string) error
	Del(key string) error
	Remove() error
	Clear() error
}

type IKeyValue interface {
	Set(key, value string) error
	Get(key string) (string, error)
	Del(key string) error
	Remove() error
	Clear() error
}

// Interface for making it possible to depend on different versions of the permission package, or other packages that implement userstates.
type IUserState interface {
	UserRights(req *http.Request) bool
	HasUser(username string) bool
	BooleanField(username, fieldname string) bool
	SetBooleanField(username, fieldname string, val bool)
	IsConfirmed(username string) bool
	IsLoggedIn(username string) bool
	AdminRights(req *http.Request) bool
	IsAdmin(username string) bool
	UsernameCookie(req *http.Request) (string, error)
	SetUsernameCookie(w http.ResponseWriter, username string) error
	AllUsernames() ([]string, error)
	Email(username string) (string, error)
	PasswordHash(username string) (string, error)
	AllUnconfirmedUsernames() ([]string, error)
	ConfirmationCode(username string) (string, error)
	AddUnconfirmed(username, confirmationCode string)
	RemoveUnconfirmed(username string)
	MarkConfirmed(username string)
	RemoveUser(username string)
	SetAdminStatus(username string)
	RemoveAdminStatus(username string)
	addUserUnchecked(username, passwordHash, email string)
	AddUser(username, password, email string)
	SetLoggedIn(username string)
	SetLoggedOut(username string)
	Login(w http.ResponseWriter, username string)
	Logout(username string)
	Username(req *http.Request) string
	CookieTimeout(username string) int64
	SetCookieTimeout(cookieTime int64)
	PasswordAlgo() string
	SetPasswordAlgo(algorithm string) error
	HashPassword(username, password string) string
	CorrectPassword(username, password string) bool
	AlreadyHasConfirmationCode(confirmationCode string) bool
	FindUserByConfirmationCode(confirmationcode string) (string, error)
	Confirm(username string)
	ConfirmUserByConfirmationCode(confirmationcode string) error
	SetMinimumConfirmationCodeLength(length int)
	GenerateUniqueConfirmationCode() (string, error)

	// Related to the database backend
	Users() IHashMap
	Host() IHost
}

// A database host
type IHost interface {
	Ping() error
	Close()
}
<|endoftext|>


<commit_before>// Copyright 2015 CoreOS, Inc.
//
// 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 rafthttp

import (
	"errors"
	"fmt"
	"io"
	"log"
	"math"
	"net/http"
	"net/url"
	"path"
	"strconv"
	"sync"
	"time"

	"github.com/coreos/etcd/etcdserver/stats"
	"github.com/coreos/etcd/pkg/types"
	"github.com/coreos/etcd/raft/raftpb"

	"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
)

const (
	streamBufSize = 4096
)

// TODO: a stream might hava one stream server or one stream client, but not both.
type stream struct {
	sync.Mutex
	w       *streamWriter
	r       *streamReader
	stopped bool
}

func (s *stream) open(from, to, cid types.ID, term uint64, tr http.RoundTripper, u string, r Raft) error {
	rd, err := newStreamReader(from, to, cid, term, tr, u, r)
	if err != nil {
		log.Printf("stream: error opening stream: %v", err)
		return err
	}

	s.Lock()
	defer s.Unlock()
	if s.stopped {
		rd.stop()
		return errors.New("stream: stopped")
	}
	if s.r != nil {
		panic("open: stream is open")
	}
	s.r = rd
	return nil
}

func (s *stream) attach(sw *streamWriter) error {
	s.Lock()
	defer s.Unlock()
	if s.stopped {
		return errors.New("stream: stopped")
	}
	if s.w != nil {
		// ignore lower-term streaming request
		if sw.term < s.w.term {
			return fmt.Errorf("cannot attach out of data stream server [%d / %d]", sw.term, s.w.term)
		}
		s.w.stop()
	}
	s.w = sw
	return nil
}

func (s *stream) write(m raftpb.Message) bool {
	s.Lock()
	defer s.Unlock()
	if s.stopped {
		return false
	}
	if s.w == nil {
		return false
	}
	if m.Term != s.w.term {
		if m.Term > s.w.term {
			panic("expected server to be invalidated when there is a higher term message")
		}
		return false
	}
	// todo: early unlock?
	if err := s.w.send(m.Entries); err != nil {
		log.Printf("stream: error sending message: %v", err)
		log.Printf("stream: stopping the stream server...")
		s.w.stop()
		s.w = nil
		return false
	}
	return true
}

// invalidate stops the sever/client that is running at
// a term lower than the given term.
func (s *stream) invalidate(term uint64) {
	s.Lock()
	defer s.Unlock()
	if s.w != nil {
		if s.w.term < term {
			s.w.stop()
			s.w = nil
		}
	}
	if s.r != nil {
		if s.r.term < term {
			s.r.stop()
			s.r = nil
		}
	}
	if term == math.MaxUint64 {
		s.stopped = true
	}
}

func (s *stream) stop() {
	s.invalidate(math.MaxUint64)
}

func (s *stream) isOpen() bool {
	s.Lock()
	defer s.Unlock()
	if s.r != nil && s.r.isStopped() {
		s.r = nil
	}
	return s.r != nil
}

type WriteFlusher interface {
	io.Writer
	http.Flusher
}

// TODO: replace fs with stream stats
type streamWriter struct {
	to   types.ID
	term uint64
	fs   *stats.FollowerStats
	q    chan []raftpb.Entry
	done chan struct{}
}

// newStreamWriter starts and returns a new unstarted stream writer.
// The caller should call stop when finished, to shut it down.
func newStreamWriter(to types.ID, term uint64) *streamWriter {
	s := &streamWriter{
		to:   to,
		term: term,
		q:    make(chan []raftpb.Entry, streamBufSize),
		done: make(chan struct{}),
	}
	return s
}

func (s *streamWriter) send(ents []raftpb.Entry) error {
	select {
	case <-s.done:
		return fmt.Errorf("stopped")
	default:
	}
	select {
	case s.q <- ents:
		return nil
	default:
		log.Printf("rafthttp: maximum number of stream buffer entries to %d has been reached", s.to)
		return fmt.Errorf("maximum number of stream buffer entries has been reached")
	}
}

func (s *streamWriter) handle(w WriteFlusher) {
	defer func() {
		close(s.done)
		log.Printf("rafthttp: server streaming to %s at term %d has been stopped", s.to, s.term)
	}()

	ew := newEntryWriter(w, s.to)
	defer ew.stop()
	for ents := range s.q {
		start := time.Now()
		if err := ew.writeEntries(ents); err != nil {
			log.Printf("rafthttp: encountered error writing to server log stream: %v", err)
			return
		}
		w.Flush()
		s.fs.Succ(time.Since(start))
	}
}

func (s *streamWriter) stop() {
	close(s.q)
	<-s.done
}

func (s *streamWriter) stopNotify() <-chan struct{} { return s.done }

// TODO: move the raft interface out of the reader.
type streamReader struct {
	id   types.ID
	to   types.ID
	term uint64
	r    Raft

	closer io.Closer
	done   chan struct{}
}

// newStreamClient starts and returns a new started stream client.
// The caller should call stop when finished, to shut it down.
func newStreamReader(id, to, cid types.ID, term uint64, tr http.RoundTripper, u string, r Raft) (*streamReader, error) {
	s := &streamReader{
		id:   id,
		to:   to,
		term: term,
		r:    r,
		done: make(chan struct{}),
	}

	uu, err := url.Parse(u)
	if err != nil {
		return nil, fmt.Errorf("parse url %s error: %v", u, err)
	}
	uu.Path = path.Join(RaftStreamPrefix, s.id.String())
	req, err := http.NewRequest("GET", uu.String(), nil)
	if err != nil {
		return nil, fmt.Errorf("new request to %s error: %v", u, err)
	}
	req.Header.Set("X-Etcd-Cluster-ID", cid.String())
	req.Header.Set("X-Raft-To", s.to.String())
	req.Header.Set("X-Raft-Term", strconv.FormatUint(s.term, 10))
	resp, err := tr.RoundTrip(req)
	if err != nil {
		return nil, fmt.Errorf("error posting to %q: %v", u, err)
	}
	if resp.StatusCode != http.StatusOK {
		resp.Body.Close()
		return nil, fmt.Errorf("unhandled http status %d", resp.StatusCode)
	}
	s.closer = resp.Body
	go s.handle(resp.Body)
	log.Printf("rafthttp: starting client stream to %s at term %d", s.to, s.term)
	return s, nil
}

func (s *streamReader) stop() {
	s.closer.Close()
	<-s.done
}

func (s *streamReader) isStopped() bool {
	select {
	case <-s.done:
		return true
	default:
		return false
	}
}

func (s *streamReader) handle(r io.Reader) {
	defer func() {
		close(s.done)
		log.Printf("rafthttp: client streaming to %s at term %d has been stopped", s.to, s.term)
	}()

	er := newEntryReader(r, s.to)
	defer er.stop()
	for {
		ents, err := er.readEntries()
		if err != nil {
			if err != io.EOF {
				log.Printf("rafthttp: encountered error reading the client log stream: %v", err)
			}
			return
		}
		// Considering Commit in MsgApp is not recovered, zero-entry appendEntry
		// messages have no use to raft state machine. Drop it here because
		// we don't have easy way to recover its Index easily.
		if len(ents) == 0 {
			continue
		}
		// The commit index field in appendEntry message is not recovered.
		// The follower updates its commit index through heartbeat.
		msg := raftpb.Message{
			Type:    raftpb.MsgApp,
			From:    uint64(s.to),
			To:      uint64(s.id),
			Term:    s.term,
			LogTerm: s.term,
			Index:   ents[0].Index - 1,
			Entries: ents,
		}
		if err := s.r.Process(context.TODO(), msg); err != nil {
			log.Printf("rafthttp: process raft message error: %v", err)
			return
		}
	}
}

func shouldInitStream(m raftpb.Message) bool {
	return m.Type == raftpb.MsgAppResp && m.Reject == false
}

func canUseStream(m raftpb.Message) bool {
	return m.Type == raftpb.MsgApp && m.Index > 0 && m.Term == m.LogTerm
}
<commit_msg>rafthttp: not send 0-entry MsgApp using stream<commit_after>// Copyright 2015 CoreOS, Inc.
//
// 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 rafthttp

import (
	"errors"
	"fmt"
	"io"
	"log"
	"math"
	"net/http"
	"net/url"
	"path"
	"strconv"
	"sync"
	"time"

	"github.com/coreos/etcd/etcdserver/stats"
	"github.com/coreos/etcd/pkg/types"
	"github.com/coreos/etcd/raft/raftpb"

	"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
)

const (
	streamBufSize = 4096
)

// TODO: a stream might hava one stream server or one stream client, but not both.
type stream struct {
	sync.Mutex
	w       *streamWriter
	r       *streamReader
	stopped bool
}

func (s *stream) open(from, to, cid types.ID, term uint64, tr http.RoundTripper, u string, r Raft) error {
	rd, err := newStreamReader(from, to, cid, term, tr, u, r)
	if err != nil {
		log.Printf("stream: error opening stream: %v", err)
		return err
	}

	s.Lock()
	defer s.Unlock()
	if s.stopped {
		rd.stop()
		return errors.New("stream: stopped")
	}
	if s.r != nil {
		panic("open: stream is open")
	}
	s.r = rd
	return nil
}

func (s *stream) attach(sw *streamWriter) error {
	s.Lock()
	defer s.Unlock()
	if s.stopped {
		return errors.New("stream: stopped")
	}
	if s.w != nil {
		// ignore lower-term streaming request
		if sw.term < s.w.term {
			return fmt.Errorf("cannot attach out of data stream server [%d / %d]", sw.term, s.w.term)
		}
		s.w.stop()
	}
	s.w = sw
	return nil
}

func (s *stream) write(m raftpb.Message) bool {
	s.Lock()
	defer s.Unlock()
	if s.stopped {
		return false
	}
	if s.w == nil {
		return false
	}
	if m.Term != s.w.term {
		if m.Term > s.w.term {
			panic("expected server to be invalidated when there is a higher term message")
		}
		return false
	}
	// todo: early unlock?
	if err := s.w.send(m.Entries); err != nil {
		log.Printf("stream: error sending message: %v", err)
		log.Printf("stream: stopping the stream server...")
		s.w.stop()
		s.w = nil
		return false
	}
	return true
}

// invalidate stops the sever/client that is running at
// a term lower than the given term.
func (s *stream) invalidate(term uint64) {
	s.Lock()
	defer s.Unlock()
	if s.w != nil {
		if s.w.term < term {
			s.w.stop()
			s.w = nil
		}
	}
	if s.r != nil {
		if s.r.term < term {
			s.r.stop()
			s.r = nil
		}
	}
	if term == math.MaxUint64 {
		s.stopped = true
	}
}

func (s *stream) stop() {
	s.invalidate(math.MaxUint64)
}

func (s *stream) isOpen() bool {
	s.Lock()
	defer s.Unlock()
	if s.r != nil && s.r.isStopped() {
		s.r = nil
	}
	return s.r != nil
}

type WriteFlusher interface {
	io.Writer
	http.Flusher
}

// TODO: replace fs with stream stats
type streamWriter struct {
	to   types.ID
	term uint64
	fs   *stats.FollowerStats
	q    chan []raftpb.Entry
	done chan struct{}
}

// newStreamWriter starts and returns a new unstarted stream writer.
// The caller should call stop when finished, to shut it down.
func newStreamWriter(to types.ID, term uint64) *streamWriter {
	s := &streamWriter{
		to:   to,
		term: term,
		q:    make(chan []raftpb.Entry, streamBufSize),
		done: make(chan struct{}),
	}
	return s
}

func (s *streamWriter) send(ents []raftpb.Entry) error {
	select {
	case <-s.done:
		return fmt.Errorf("stopped")
	default:
	}
	select {
	case s.q <- ents:
		return nil
	default:
		log.Printf("rafthttp: maximum number of stream buffer entries to %d has been reached", s.to)
		return fmt.Errorf("maximum number of stream buffer entries has been reached")
	}
}

func (s *streamWriter) handle(w WriteFlusher) {
	defer func() {
		close(s.done)
		log.Printf("rafthttp: server streaming to %s at term %d has been stopped", s.to, s.term)
	}()

	ew := newEntryWriter(w, s.to)
	defer ew.stop()
	for ents := range s.q {
		// Considering Commit in MsgApp is not recovered when received,
		// zero-entry appendEntry messages have no use to raft state machine.
		// Drop it here because it is useless.
		if len(ents) == 0 {
			continue
		}
		start := time.Now()
		if err := ew.writeEntries(ents); err != nil {
			log.Printf("rafthttp: encountered error writing to server log stream: %v", err)
			return
		}
		w.Flush()
		s.fs.Succ(time.Since(start))
	}
}

func (s *streamWriter) stop() {
	close(s.q)
	<-s.done
}

func (s *streamWriter) stopNotify() <-chan struct{} { return s.done }

// TODO: move the raft interface out of the reader.
type streamReader struct {
	id   types.ID
	to   types.ID
	term uint64
	r    Raft

	closer io.Closer
	done   chan struct{}
}

// newStreamClient starts and returns a new started stream client.
// The caller should call stop when finished, to shut it down.
func newStreamReader(id, to, cid types.ID, term uint64, tr http.RoundTripper, u string, r Raft) (*streamReader, error) {
	s := &streamReader{
		id:   id,
		to:   to,
		term: term,
		r:    r,
		done: make(chan struct{}),
	}

	uu, err := url.Parse(u)
	if err != nil {
		return nil, fmt.Errorf("parse url %s error: %v", u, err)
	}
	uu.Path = path.Join(RaftStreamPrefix, s.id.String())
	req, err := http.NewRequest("GET", uu.String(), nil)
	if err != nil {
		return nil, fmt.Errorf("new request to %s error: %v", u, err)
	}
	req.Header.Set("X-Etcd-Cluster-ID", cid.String())
	req.Header.Set("X-Raft-To", s.to.String())
	req.Header.Set("X-Raft-Term", strconv.FormatUint(s.term, 10))
	resp, err := tr.RoundTrip(req)
	if err != nil {
		return nil, fmt.Errorf("error posting to %q: %v", u, err)
	}
	if resp.StatusCode != http.StatusOK {
		resp.Body.Close()
		return nil, fmt.Errorf("unhandled http status %d", resp.StatusCode)
	}
	s.closer = resp.Body
	go s.handle(resp.Body)
	log.Printf("rafthttp: starting client stream to %s at term %d", s.to, s.term)
	return s, nil
}

func (s *streamReader) stop() {
	s.closer.Close()
	<-s.done
}

func (s *streamReader) isStopped() bool {
	select {
	case <-s.done:
		return true
	default:
		return false
	}
}

func (s *streamReader) handle(r io.Reader) {
	defer func() {
		close(s.done)
		log.Printf("rafthttp: client streaming to %s at term %d has been stopped", s.to, s.term)
	}()

	er := newEntryReader(r, s.to)
	defer er.stop()
	for {
		ents, err := er.readEntries()
		if err != nil {
			if err != io.EOF {
				log.Printf("rafthttp: encountered error reading the client log stream: %v", err)
			}
			return
		}
		// The commit index field in appendEntry message is not recovered.
		// The follower updates its commit index through heartbeat.
		msg := raftpb.Message{
			Type:    raftpb.MsgApp,
			From:    uint64(s.to),
			To:      uint64(s.id),
			Term:    s.term,
			LogTerm: s.term,
			Index:   ents[0].Index - 1,
			Entries: ents,
		}
		if err := s.r.Process(context.TODO(), msg); err != nil {
			log.Printf("rafthttp: process raft message error: %v", err)
			return
		}
	}
}

func shouldInitStream(m raftpb.Message) bool {
	return m.Type == raftpb.MsgAppResp && m.Reject == false
}

func canUseStream(m raftpb.Message) bool {
	return m.Type == raftpb.MsgApp && m.Index > 0 && m.Term == m.LogTerm
}
<|endoftext|>