<commit_before>// Package game provides a higher level interface to package dominos.
package game

import (
	"errors"
	"fmt"
	"log"

	"github.com/Xe/uuid"
	"github.com/cetacean/magiism/dominos"
)

// Package errors
var (
	ErrGameCreationFailed = errors.New("game: dominos.NewGame failed, please report as a bug")
	ErrNotYourTurn        = errors.New("game: it is not your turn")
	ErrInvalidHandIndex   = errors.New("game: invalid hand index")
	ErrEndOfTurn          = errors.New("game: your turn is now over")
	ErrUnknownAction      = errors.New("game: unknown action")
)

// Action is the kind of turn action the player is taking.
type Action int

// Possible actions a player can take in their turn.
const (
	EndTurn Action = iota
	PlayDomino
	DrawDomino
	Knock
)

// Possible messages to the client, TODO: translations?
const (
	KnockSuccessfulMsg   = "$EVENT_PLAYER_NAME has only one tile left!"
	CannotKnockMsg       = "You cannot knock, you have more than one tile in your hand"
	OutOfTilesMsg        = "Out of tiles, can't draw"
	MustResolveDoubleMsg = "You must resolve this double if you can"
	PlaySuccessfulMsg    = "$EVENT_PLAYER_NAME has played $DOMINO on $PATH_ID_OWNER"
	MustTryDrawingMsg    = "You must try to draw a tile and see if that works before ending your turn"
	SettingTrainMsg      = "Setting train on $EVENT_PLAYER_NAME"
)

// Event is a single user command -> game state event.
type Event struct {
	Action   Action
	PlayerID string // This must be populated by the server, never by the user directly.

	// If PlayDomino is chosen, these next two fields are filled.
	PathID    int
	HandIndex int
}

// Response is the result of the event being run against the game state.
// This structure is safe to json-encode for the user directly.
type Response struct {
	Success bool

	State *dominos.Game

	GlobalMessage string
	UserMessage   string
	PlayerID      string
}

// Game is a high-level wrapper around the dominos.Game struct.
type Game struct {
	*dominos.Game

	ID string

	// These variables are for the currently active player's turn.
	Drawn  bool
	Played bool
}

// Store represents a in-memory or on-database storage for many domino games.
type Store interface {
	GetGame(id string) (*Game, error)
	PutGame(id string, g *Game) error
}

// New creates a new game with given players.
func New(players []string) (*Game, error) {
	dg, err := dominos.NewGame(players)
	if err != nil {
		log.Println("game creation failed: ", err)
		return nil, ErrGameCreationFailed
	}

	g := &Game{
		Game: dg,
		ID:   uuid.New(),
	}

	return g, nil
}

// HandleEvent handles a single game event, failing if it failed.
func (g *Game) HandleEvent(e *Event) (*Response, error) {
	r := &Response{
		State:    g.Game,
		PlayerID: e.PlayerID,
	}

	p := g.GetActivePlayer()

	if e.Action == Knock {
		if e.PlayerID != p.ID {
			kp, ok := g.GetPlayerByID(e.PlayerID)
			if !ok {
				return nil, errors.New("impossible state, tried to look up a player that doesn't exist?")
			}

			if g.Knock(kp) {
				r.GlobalMessage = KnockSuccessfulMsg
				return r, nil
			}
			return nil, ErrNotYourTurn
		}
	}

	if g.GetActivePlayer().ID != e.PlayerID {
		return nil, ErrNotYourTurn
	}

	// switch on e.Action and then take the appropriate actions.
	switch e.Action {
	case EndTurn:
		nagged := false
		for i, d := range p.Hand {
			for j, path := range g.Trains {
				_, err := g.CanPlace(p, d, path)
				if err == nil {
					nagged = true
					r.UserMessage += fmt.Sprintf("you can place tile %s (%d) in your hand on path %d\n", d.Display(), i, j)
				}
			}
		}

		if nagged {
			r.Success = false
			return r, nil
		}

		if !g.Played && !g.Drawn {
			r.UserMessage = MustTryDrawingMsg
			r.Success = false
			return r, nil
		}

		if g.Drawn && !g.Played {
			r.GlobalMessage = SettingTrainMsg
		}

		g.Drawn = false
		g.Played = false

		return r, ErrEndOfTurn

	case PlayDomino:
		path := g.Trains[e.PathID]
		d, ok := p.RemoveFromHand(e.HandIndex)
		if !ok {
			return nil, ErrInvalidHandIndex
		}

		err := g.Place(p, d, path)
		if err != nil {
			p.Hand = append(p.Hand, d)

			return nil, err
		}

		r.GlobalMessage = PlaySuccessfulMsg
		r.Success = true
		g.Played = true

		if d.IsDouble() {
			g.UnresolvedDouble = true
			path.UnresolvedDouble = true
			r.UserMessage = MustResolveDoubleMsg
			g.Played = false

			return r, nil
		}

		_, status := g.NextTurn()
		if status != "" {
			switch status {
			case "noknock":
				r.GlobalMessage += fmt.Sprintf("\n$CURRENT_PLAYER has drawn two tiles for not knocking when they had one tile left")
			}
		}

		return r, ErrEndOfTurn

	case DrawDomino:
		if !g.Drawn {
			g.Drawn = true
			err := g.Draw(p)
			if err != nil {
				r.GlobalMessage = OutOfTilesMsg
				return r, ErrEndOfTurn
			}
		} else {
			r.Success = false
		}

	case Knock:
		if g.Knock(p) {
			r.GlobalMessage = KnockSuccessfulMsg
			r.Success = true
			p.Knocked = true
			return r, nil
		} else {
			r.UserMessage = CannotKnockMsg
			r.Success = false
		}
	default:
		return nil, ErrUnknownAction
	}

	return r, nil
}
<commit_msg>game: prefer appending messages, simplify end of turn<commit_after>// Package game provides a higher level interface to package dominos.
package game

import (
	"errors"
	"fmt"
	"log"

	"github.com/Xe/uuid"
	"github.com/cetacean/magiism/dominos"
)

// Package errors
var (
	ErrGameCreationFailed = errors.New("game: dominos.NewGame failed, please report as a bug")
	ErrNotYourTurn        = errors.New("game: it is not your turn")
	ErrInvalidHandIndex   = errors.New("game: invalid hand index")
	ErrEndOfTurn          = errors.New("game: your turn is now over")
	ErrUnknownAction      = errors.New("game: unknown action")
)

// Action is the kind of turn action the player is taking.
type Action int

// Possible actions a player can take in their turn.
const (
	EndTurn Action = iota
	PlayDomino
	DrawDomino
	Knock
)

// Possible messages to the client, TODO: translations?
const (
	KnockSuccessfulMsg   = "$EVENT_PLAYER_NAME has only one tile left!"
	CannotKnockMsg       = "You cannot knock, you have more than one tile in your hand"
	OutOfTilesMsg        = "Out of tiles, can't draw"
	MustResolveDoubleMsg = "You must resolve this double if you can"
	PlaySuccessfulMsg    = "$EVENT_PLAYER_NAME has played $DOMINO on $PATH_ID_OWNER"
	MustTryDrawingMsg    = "You must try to draw a tile and see if that works before ending your turn"
	SettingTrainMsg      = "Setting train on $EVENT_PLAYER_NAME"
)

// Event is a single user command -> game state event.
type Event struct {
	Action   Action
	PlayerID string // This must be populated by the server, never by the user directly.

	// If PlayDomino is chosen, these next two fields are filled.
	PathID    int
	HandIndex int
}

// Response is the result of the event being run against the game state.
// This structure is safe to json-encode for the user directly.
type Response struct {
	Success bool

	State *dominos.Game

	GlobalMessage string
	UserMessage   string
	PlayerID      string
}

// Game is a high-level wrapper around the dominos.Game struct.
type Game struct {
	*dominos.Game

	ID string

	// These variables are for the currently active player's turn.
	Drawn  bool
	Played bool
}

// Store represents a in-memory or on-database storage for many domino games.
type Store interface {
	GetGame(id string) (*Game, error)
	PutGame(id string, g *Game) error
}

// New creates a new game with given players.
func New(players []string) (*Game, error) {
	dg, err := dominos.NewGame(players)
	if err != nil {
		log.Println("game creation failed: ", err)
		return nil, ErrGameCreationFailed
	}

	g := &Game{
		Game: dg,
		ID:   uuid.New(),
	}

	return g, nil
}

// HandleEvent handles a single game event, failing if it failed.
func (g *Game) HandleEvent(e *Event) (*Response, error) {
	r := &Response{
		State:    g.Game,
		PlayerID: e.PlayerID,
	}

	p := g.GetActivePlayer()

	if e.Action == Knock {
		if e.PlayerID != p.ID {
			kp, ok := g.GetPlayerByID(e.PlayerID)
			if !ok {
				return nil, errors.New("impossible state, tried to look up a player that doesn't exist?")
			}

			if g.Knock(kp) {
				r.GlobalMessage = KnockSuccessfulMsg
				return r, nil
			}
			return nil, ErrNotYourTurn
		}
	}

	if g.GetActivePlayer().ID != e.PlayerID {
		return nil, ErrNotYourTurn
	}

	// switch on e.Action and then take the appropriate actions.
	switch e.Action {
	case EndTurn:
		nagged := false
		for i, d := range p.Hand {
			for j, path := range g.Trains {
				_, err := g.CanPlace(p, d, path)
				if err == nil {
					nagged = true
					r.UserMessage += fmt.Sprintf("you can place tile %s (%d) in your hand on path %d\n", d.Display(), i, j)
				}
			}
		}

		if nagged {
			r.Success = false
			return r, nil
		}

		if !g.Played && !g.Drawn {
			r.UserMessage = MustTryDrawingMsg
			r.Success = false
			return r, nil
		}

		if g.Drawn && !g.Played {
			r.GlobalMessage += "\n" + SettingTrainMsg
		}

		g.endOfTurn(r)
		return r, ErrEndOfTurn

	case PlayDomino:
		path := g.Trains[e.PathID]
		d, ok := p.RemoveFromHand(e.HandIndex)
		if !ok {
			return nil, ErrInvalidHandIndex
		}

		err := g.Place(p, d, path)
		if err != nil {
			p.Hand = append(p.Hand, d)

			return nil, err
		}

		r.GlobalMessage = PlaySuccessfulMsg
		r.Success = true
		g.Played = true

		if d.IsDouble() {
			g.UnresolvedDouble = true
			path.UnresolvedDouble = true
			r.UserMessage = MustResolveDoubleMsg
			g.Played = false

			return r, nil
		}

		g.endOfTurn(r)
		return r, ErrEndOfTurn

	case DrawDomino:
		if !g.Drawn {
			g.Drawn = true
			err := g.Draw(p)
			if err != nil {
				r.GlobalMessage = OutOfTilesMsg
				g.endOfTurn(r)
				return r, ErrEndOfTurn
			}
		} else {
			r.Success = false
		}

	case Knock:
		if g.Knock(p) {
			r.GlobalMessage = KnockSuccessfulMsg
			r.Success = true
			p.Knocked = true
			return r, nil
		} else {
			r.UserMessage = CannotKnockMsg
			r.Success = false
		}
	default:
		return nil, ErrUnknownAction
	}

	return r, nil
}

func (g *Game) endOfTurn(r *Response) {
	g.Drawn = false
	g.Played = false

	_, status := g.NextTurn()
	if status != "" {
		switch status {
		case "noknock":
			r.GlobalMessage += fmt.Sprintf("\n$CURRENT_PLAYER has drawn two tiles for not knocking when they had one tile left")
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2015 VMware, Inc. All Rights Reserved.

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 object

import (
	"errors"
	"fmt"

	"github.com/vmware/govmomi/property"
	"github.com/vmware/govmomi/vim25"
	"github.com/vmware/govmomi/vim25/methods"
	"github.com/vmware/govmomi/vim25/mo"
	"github.com/vmware/govmomi/vim25/types"
	"golang.org/x/net/context"
)

const (
	PropRuntimePowerState = "summary.runtime.powerState"
)

type VirtualMachine struct {
	Common

	InventoryPath string
}

func (v VirtualMachine) String() string {
	if v.InventoryPath == "" {
		return v.Common.String()
	}
	return fmt.Sprintf("%v @ %v", v.Common, v.InventoryPath)
}

func NewVirtualMachine(c *vim25.Client, ref types.ManagedObjectReference) *VirtualMachine {
	return &VirtualMachine{
		Common: NewCommon(c, ref),
	}
}

func (v VirtualMachine) Name(ctx context.Context) (string, error) {
	var o mo.VirtualMachine

	err := v.Properties(ctx, v.Reference(), []string{"name"}, &o)
	if err != nil {
		return "", err
	}

	return o.Name, nil
}

func (v VirtualMachine) PowerOn(ctx context.Context) (*Task, error) {
	req := types.PowerOnVM_Task{
		This: v.Reference(),
	}

	res, err := methods.PowerOnVM_Task(ctx, v.c, &req)
	if err != nil {
		return nil, err
	}

	return NewTask(v.c, res.Returnval), nil
}

func (v VirtualMachine) PowerOff(ctx context.Context) (*Task, error) {
	req := types.PowerOffVM_Task{
		This: v.Reference(),
	}

	res, err := methods.PowerOffVM_Task(ctx, v.c, &req)
	if err != nil {
		return nil, err
	}

	return NewTask(v.c, res.Returnval), nil
}

func (v VirtualMachine) Reset(ctx context.Context) (*Task, error) {
	req := types.ResetVM_Task{
		This: v.Reference(),
	}

	res, err := methods.ResetVM_Task(ctx, v.c, &req)
	if err != nil {
		return nil, err
	}

	return NewTask(v.c, res.Returnval), nil
}

func (v VirtualMachine) Suspend(ctx context.Context) (*Task, error) {
	req := types.SuspendVM_Task{
		This: v.Reference(),
	}

	res, err := methods.SuspendVM_Task(ctx, v.c, &req)
	if err != nil {
		return nil, err
	}

	return NewTask(v.c, res.Returnval), nil
}

func (v VirtualMachine) ShutdownGuest(ctx context.Context) error {
	req := types.ShutdownGuest{
		This: v.Reference(),
	}

	_, err := methods.ShutdownGuest(ctx, v.c, &req)
	return err
}

func (v VirtualMachine) RebootGuest(ctx context.Context) error {
	req := types.RebootGuest{
		This: v.Reference(),
	}

	_, err := methods.RebootGuest(ctx, v.c, &req)
	return err
}

func (v VirtualMachine) Destroy(ctx context.Context) (*Task, error) {
	req := types.Destroy_Task{
		This: v.Reference(),
	}

	res, err := methods.Destroy_Task(ctx, v.c, &req)
	if err != nil {
		return nil, err
	}

	return NewTask(v.c, res.Returnval), nil
}

func (v VirtualMachine) Clone(ctx context.Context, folder *Folder, name string, config types.VirtualMachineCloneSpec) (*Task, error) {
	req := types.CloneVM_Task{
		This:   v.Reference(),
		Folder: folder.Reference(),
		Name:   name,
		Spec:   config,
	}

	res, err := methods.CloneVM_Task(ctx, v.c, &req)
	if err != nil {
		return nil, err
	}

	return NewTask(v.c, res.Returnval), nil
}

func (v VirtualMachine) Customize(ctx context.Context, spec types.CustomizationSpec) (*Task, error) {
	req := types.CustomizeVM_Task{
		This: v.Reference(),
		Spec: spec,
	}

	res, err := methods.CustomizeVM_Task(ctx, v.c, &req)
	if err != nil {
		return nil, err
	}

	return NewTask(v.c, res.Returnval), nil
}

func (v VirtualMachine) Relocate(ctx context.Context, config types.VirtualMachineRelocateSpec, priority types.VirtualMachineMovePriority) (*Task, error) {
	req := types.RelocateVM_Task{
		This:     v.Reference(),
		Spec:     config,
		Priority: priority,
	}

	res, err := methods.RelocateVM_Task(ctx, v.c, &req)
	if err != nil {
		return nil, err
	}

	return NewTask(v.c, res.Returnval), nil
}

func (v VirtualMachine) Reconfigure(ctx context.Context, config types.VirtualMachineConfigSpec) (*Task, error) {
	req := types.ReconfigVM_Task{
		This: v.Reference(),
		Spec: config,
	}

	res, err := methods.ReconfigVM_Task(ctx, v.c, &req)
	if err != nil {
		return nil, err
	}

	return NewTask(v.c, res.Returnval), nil
}

func (v VirtualMachine) WaitForIP(ctx context.Context) (string, error) {
	var ip string

	p := property.DefaultCollector(v.c)
	err := property.Wait(ctx, p, v.Reference(), []string{"guest.ipAddress"}, func(pc []types.PropertyChange) bool {
		for _, c := range pc {
			if c.Name != "guest.ipAddress" {
				continue
			}
			if c.Op != types.PropertyChangeOpAssign {
				continue
			}
			if c.Val == nil {
				continue
			}

			ip = c.Val.(string)
			return true
		}

		return false
	})

	if err != nil {
		return "", err
	}

	return ip, nil
}

// Device returns the VirtualMachine's config.hardware.device property.
func (v VirtualMachine) Device(ctx context.Context) (VirtualDeviceList, error) {
	var o mo.VirtualMachine

	err := v.Properties(ctx, v.Reference(), []string{"config.hardware.device"}, &o)
	if err != nil {
		return nil, err
	}

	return VirtualDeviceList(o.Config.Hardware.Device), nil
}

func (v VirtualMachine) HostSystem(ctx context.Context) (*HostSystem, error) {
	var o mo.VirtualMachine

	err := v.Properties(ctx, v.Reference(), []string{"summary"}, &o)
	if err != nil {
		return nil, err
	}

	host := o.Summary.Runtime.Host
	if host == nil {
		return nil, errors.New("VM doesn't have a HostSystem")
	}

	return NewHostSystem(v.c, *host), nil
}

func (v VirtualMachine) ResourcePool(ctx context.Context) (*ResourcePool, error) {
	var o mo.VirtualMachine

	err := v.Properties(ctx, v.Reference(), []string{"resourcePool"}, &o)
	if err != nil {
		return nil, err
	}

	rp := o.ResourcePool
	if rp == nil {
		return nil, errors.New("VM doesn't have a resourcePool")
	}

	return NewResourcePool(v.c, *rp), nil
}

func (v VirtualMachine) configureDevice(ctx context.Context, op types.VirtualDeviceConfigSpecOperation, fop types.VirtualDeviceConfigSpecFileOperation, devices ...types.BaseVirtualDevice) error {
	spec := types.VirtualMachineConfigSpec{}

	for _, device := range devices {
		config := &types.VirtualDeviceConfigSpec{
			Device:    device,
			Operation: op,
		}

		if disk, ok := device.(*types.VirtualDisk); ok {
			config.FileOperation = fop

			// Special case to attach an existing disk
			if op == types.VirtualDeviceConfigSpecOperationAdd && disk.CapacityInKB == 0 {
				childDisk := false
				if b, ok := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo); ok {
					childDisk = b.Parent != nil
				}

				if !childDisk {
					config.FileOperation = "" // existing disk
				}
			}
		}

		spec.DeviceChange = append(spec.DeviceChange, config)
	}

	task, err := v.Reconfigure(ctx, spec)
	if err != nil {
		return err
	}

	return task.Wait(ctx)
}

// AddDevice adds the given devices to the VirtualMachine
func (v VirtualMachine) AddDevice(ctx context.Context, device ...types.BaseVirtualDevice) error {
	return v.configureDevice(ctx, types.VirtualDeviceConfigSpecOperationAdd, types.VirtualDeviceConfigSpecFileOperationCreate, device...)
}

// EditDevice edits the given (existing) devices on the VirtualMachine
func (v VirtualMachine) EditDevice(ctx context.Context, device ...types.BaseVirtualDevice) error {
	return v.configureDevice(ctx, types.VirtualDeviceConfigSpecOperationEdit, types.VirtualDeviceConfigSpecFileOperationReplace, device...)
}

// RemoveDevice removes the given devices on the VirtualMachine
func (v VirtualMachine) RemoveDevice(ctx context.Context, device ...types.BaseVirtualDevice) error {
	return v.configureDevice(ctx, types.VirtualDeviceConfigSpecOperationRemove, types.VirtualDeviceConfigSpecFileOperationDestroy, device...)
}

// BootOptions returns the VirtualMachine's config.bootOptions property.
func (v VirtualMachine) BootOptions(ctx context.Context) (*types.VirtualMachineBootOptions, error) {
	var o mo.VirtualMachine

	err := v.Properties(ctx, v.Reference(), []string{"config.bootOptions"}, &o)
	if err != nil {
		return nil, err
	}

	return o.Config.BootOptions, nil
}

// SetBootOptions reconfigures the VirtualMachine with the given options.
func (v VirtualMachine) SetBootOptions(ctx context.Context, options *types.VirtualMachineBootOptions) error {
	spec := types.VirtualMachineConfigSpec{}

	spec.BootOptions = options

	task, err := v.Reconfigure(ctx, spec)
	if err != nil {
		return err
	}

	return task.Wait(ctx)
}

// Answer answers a pending question.
func (v VirtualMachine) Answer(ctx context.Context, id, answer string) error {
	req := types.AnswerVM{
		This:         v.Reference(),
		QuestionId:   id,
		AnswerChoice: answer,
	}

	_, err := methods.AnswerVM(ctx, v.c, &req)
	if err != nil {
		return err
	}

	return nil
}

// CreateSnapshot creates a new snapshot of a virtual machine.
func (v VirtualMachine) CreateSnapshot(ctx context.Context, name string, description string, memory bool, quiesce bool) (*Task, error) {
	req := types.CreateSnapshot_Task{
		This:        v.Reference(),
		Name:        name,
		Description: description,
		Memory:      memory,
		Quiesce:     quiesce,
	}

	res, err := methods.CreateSnapshot_Task(ctx, v.c, &req)
	if err != nil {
		return nil, err
	}

	return NewTask(v.c, res.Returnval), nil
}

// IsToolsRunning returns true if VMware Tools is currently running in the guest OS, and false otherwise.
func (v VirtualMachine) IsToolsRunning(ctx context.Context) (bool, error) {
	var o mo.VirtualMachine

	err := v.Properties(ctx, v.Reference(), []string{"guest.toolsRunningStatus"}, &o)
	if err != nil {
		return false, err
	}

	return o.Guest.ToolsRunningStatus == string(types.VirtualMachineToolsRunningStatusGuestToolsRunning), nil
}

// Wait for the VirtualMachine to change to the desired power state.
func (v VirtualMachine) WaitForPowerState(ctx context.Context, state types.VirtualMachinePowerState) error {
	p := property.DefaultCollector(v.c)
	err := property.Wait(ctx, p, v.Reference(), []string{PropRuntimePowerState}, func(pc []types.PropertyChange) bool {
		for _, c := range pc {
			if c.Name != PropRuntimePowerState {
				continue
			}
			if c.Val == nil {
				continue
			}

			ps := c.Val.(types.VirtualMachinePowerState)
			if ps == state {
				return true
			}
		}
		return false
	})

	return err
}

func (v VirtualMachine) MarkAsTemplate(ctx context.Context) error {
	req := types.MarkAsTemplate{
		This: v.Reference(),
	}

	_, err := methods.MarkAsTemplate(ctx, v.c, &req)
	if err != nil {
		return err
	}

	return nil
}

func (v VirtualMachine) MarkAsVirtualMachine(ctx context.Context, pool ResourcePool, host *HostSystem) error {
	req := types.MarkAsVirtualMachine{
		This: v.Reference(),
		Pool: pool.Reference(),
	}

	if host != nil {
		ref := host.Reference()
		req.Host = &ref
	}

	_, err := methods.MarkAsVirtualMachine(ctx, v.c, &req)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Add helper method to get VM power state<commit_after>/*
Copyright (c) 2015 VMware, Inc. All Rights Reserved.

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 object

import (
	"errors"
	"fmt"

	"github.com/vmware/govmomi/property"
	"github.com/vmware/govmomi/vim25"
	"github.com/vmware/govmomi/vim25/methods"
	"github.com/vmware/govmomi/vim25/mo"
	"github.com/vmware/govmomi/vim25/types"
	"golang.org/x/net/context"
)

const (
	PropRuntimePowerState = "summary.runtime.powerState"
)

type VirtualMachine struct {
	Common

	InventoryPath string
}

func (v VirtualMachine) String() string {
	if v.InventoryPath == "" {
		return v.Common.String()
	}
	return fmt.Sprintf("%v @ %v", v.Common, v.InventoryPath)
}

func NewVirtualMachine(c *vim25.Client, ref types.ManagedObjectReference) *VirtualMachine {
	return &VirtualMachine{
		Common: NewCommon(c, ref),
	}
}

func (v VirtualMachine) Name(ctx context.Context) (string, error) {
	var o mo.VirtualMachine

	err := v.Properties(ctx, v.Reference(), []string{"name"}, &o)
	if err != nil {
		return "", err
	}

	return o.Name, nil
}

func (v VirtualMachine) PowerState(ctx context.Context) (types.VirtualMachinePowerState, error) {
	var o mo.VirtualMachine

	err := v.Properties(ctx, v.Reference(), []string{PropRuntimePowerState}, &o)
	if err != nil {
		return "", err
	}

	return o.Summary.Runtime.PowerState, nil
}

func (v VirtualMachine) PowerOn(ctx context.Context) (*Task, error) {
	req := types.PowerOnVM_Task{
		This: v.Reference(),
	}

	res, err := methods.PowerOnVM_Task(ctx, v.c, &req)
	if err != nil {
		return nil, err
	}

	return NewTask(v.c, res.Returnval), nil
}

func (v VirtualMachine) PowerOff(ctx context.Context) (*Task, error) {
	req := types.PowerOffVM_Task{
		This: v.Reference(),
	}

	res, err := methods.PowerOffVM_Task(ctx, v.c, &req)
	if err != nil {
		return nil, err
	}

	return NewTask(v.c, res.Returnval), nil
}

func (v VirtualMachine) Reset(ctx context.Context) (*Task, error) {
	req := types.ResetVM_Task{
		This: v.Reference(),
	}

	res, err := methods.ResetVM_Task(ctx, v.c, &req)
	if err != nil {
		return nil, err
	}

	return NewTask(v.c, res.Returnval), nil
}

func (v VirtualMachine) Suspend(ctx context.Context) (*Task, error) {
	req := types.SuspendVM_Task{
		This: v.Reference(),
	}

	res, err := methods.SuspendVM_Task(ctx, v.c, &req)
	if err != nil {
		return nil, err
	}

	return NewTask(v.c, res.Returnval), nil
}

func (v VirtualMachine) ShutdownGuest(ctx context.Context) error {
	req := types.ShutdownGuest{
		This: v.Reference(),
	}

	_, err := methods.ShutdownGuest(ctx, v.c, &req)
	return err
}

func (v VirtualMachine) RebootGuest(ctx context.Context) error {
	req := types.RebootGuest{
		This: v.Reference(),
	}

	_, err := methods.RebootGuest(ctx, v.c, &req)
	return err
}

func (v VirtualMachine) Destroy(ctx context.Context) (*Task, error) {
	req := types.Destroy_Task{
		This: v.Reference(),
	}

	res, err := methods.Destroy_Task(ctx, v.c, &req)
	if err != nil {
		return nil, err
	}

	return NewTask(v.c, res.Returnval), nil
}

func (v VirtualMachine) Clone(ctx context.Context, folder *Folder, name string, config types.VirtualMachineCloneSpec) (*Task, error) {
	req := types.CloneVM_Task{
		This:   v.Reference(),
		Folder: folder.Reference(),
		Name:   name,
		Spec:   config,
	}

	res, err := methods.CloneVM_Task(ctx, v.c, &req)
	if err != nil {
		return nil, err
	}

	return NewTask(v.c, res.Returnval), nil
}

func (v VirtualMachine) Customize(ctx context.Context, spec types.CustomizationSpec) (*Task, error) {
	req := types.CustomizeVM_Task{
		This: v.Reference(),
		Spec: spec,
	}

	res, err := methods.CustomizeVM_Task(ctx, v.c, &req)
	if err != nil {
		return nil, err
	}

	return NewTask(v.c, res.Returnval), nil
}

func (v VirtualMachine) Relocate(ctx context.Context, config types.VirtualMachineRelocateSpec, priority types.VirtualMachineMovePriority) (*Task, error) {
	req := types.RelocateVM_Task{
		This:     v.Reference(),
		Spec:     config,
		Priority: priority,
	}

	res, err := methods.RelocateVM_Task(ctx, v.c, &req)
	if err != nil {
		return nil, err
	}

	return NewTask(v.c, res.Returnval), nil
}

func (v VirtualMachine) Reconfigure(ctx context.Context, config types.VirtualMachineConfigSpec) (*Task, error) {
	req := types.ReconfigVM_Task{
		This: v.Reference(),
		Spec: config,
	}

	res, err := methods.ReconfigVM_Task(ctx, v.c, &req)
	if err != nil {
		return nil, err
	}

	return NewTask(v.c, res.Returnval), nil
}

func (v VirtualMachine) WaitForIP(ctx context.Context) (string, error) {
	var ip string

	p := property.DefaultCollector(v.c)
	err := property.Wait(ctx, p, v.Reference(), []string{"guest.ipAddress"}, func(pc []types.PropertyChange) bool {
		for _, c := range pc {
			if c.Name != "guest.ipAddress" {
				continue
			}
			if c.Op != types.PropertyChangeOpAssign {
				continue
			}
			if c.Val == nil {
				continue
			}

			ip = c.Val.(string)
			return true
		}

		return false
	})

	if err != nil {
		return "", err
	}

	return ip, nil
}

// Device returns the VirtualMachine's config.hardware.device property.
func (v VirtualMachine) Device(ctx context.Context) (VirtualDeviceList, error) {
	var o mo.VirtualMachine

	err := v.Properties(ctx, v.Reference(), []string{"config.hardware.device"}, &o)
	if err != nil {
		return nil, err
	}

	return VirtualDeviceList(o.Config.Hardware.Device), nil
}

func (v VirtualMachine) HostSystem(ctx context.Context) (*HostSystem, error) {
	var o mo.VirtualMachine

	err := v.Properties(ctx, v.Reference(), []string{"summary"}, &o)
	if err != nil {
		return nil, err
	}

	host := o.Summary.Runtime.Host
	if host == nil {
		return nil, errors.New("VM doesn't have a HostSystem")
	}

	return NewHostSystem(v.c, *host), nil
}

func (v VirtualMachine) ResourcePool(ctx context.Context) (*ResourcePool, error) {
	var o mo.VirtualMachine

	err := v.Properties(ctx, v.Reference(), []string{"resourcePool"}, &o)
	if err != nil {
		return nil, err
	}

	rp := o.ResourcePool
	if rp == nil {
		return nil, errors.New("VM doesn't have a resourcePool")
	}

	return NewResourcePool(v.c, *rp), nil
}

func (v VirtualMachine) configureDevice(ctx context.Context, op types.VirtualDeviceConfigSpecOperation, fop types.VirtualDeviceConfigSpecFileOperation, devices ...types.BaseVirtualDevice) error {
	spec := types.VirtualMachineConfigSpec{}

	for _, device := range devices {
		config := &types.VirtualDeviceConfigSpec{
			Device:    device,
			Operation: op,
		}

		if disk, ok := device.(*types.VirtualDisk); ok {
			config.FileOperation = fop

			// Special case to attach an existing disk
			if op == types.VirtualDeviceConfigSpecOperationAdd && disk.CapacityInKB == 0 {
				childDisk := false
				if b, ok := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo); ok {
					childDisk = b.Parent != nil
				}

				if !childDisk {
					config.FileOperation = "" // existing disk
				}
			}
		}

		spec.DeviceChange = append(spec.DeviceChange, config)
	}

	task, err := v.Reconfigure(ctx, spec)
	if err != nil {
		return err
	}

	return task.Wait(ctx)
}

// AddDevice adds the given devices to the VirtualMachine
func (v VirtualMachine) AddDevice(ctx context.Context, device ...types.BaseVirtualDevice) error {
	return v.configureDevice(ctx, types.VirtualDeviceConfigSpecOperationAdd, types.VirtualDeviceConfigSpecFileOperationCreate, device...)
}

// EditDevice edits the given (existing) devices on the VirtualMachine
func (v VirtualMachine) EditDevice(ctx context.Context, device ...types.BaseVirtualDevice) error {
	return v.configureDevice(ctx, types.VirtualDeviceConfigSpecOperationEdit, types.VirtualDeviceConfigSpecFileOperationReplace, device...)
}

// RemoveDevice removes the given devices on the VirtualMachine
func (v VirtualMachine) RemoveDevice(ctx context.Context, device ...types.BaseVirtualDevice) error {
	return v.configureDevice(ctx, types.VirtualDeviceConfigSpecOperationRemove, types.VirtualDeviceConfigSpecFileOperationDestroy, device...)
}

// BootOptions returns the VirtualMachine's config.bootOptions property.
func (v VirtualMachine) BootOptions(ctx context.Context) (*types.VirtualMachineBootOptions, error) {
	var o mo.VirtualMachine

	err := v.Properties(ctx, v.Reference(), []string{"config.bootOptions"}, &o)
	if err != nil {
		return nil, err
	}

	return o.Config.BootOptions, nil
}

// SetBootOptions reconfigures the VirtualMachine with the given options.
func (v VirtualMachine) SetBootOptions(ctx context.Context, options *types.VirtualMachineBootOptions) error {
	spec := types.VirtualMachineConfigSpec{}

	spec.BootOptions = options

	task, err := v.Reconfigure(ctx, spec)
	if err != nil {
		return err
	}

	return task.Wait(ctx)
}

// Answer answers a pending question.
func (v VirtualMachine) Answer(ctx context.Context, id, answer string) error {
	req := types.AnswerVM{
		This:         v.Reference(),
		QuestionId:   id,
		AnswerChoice: answer,
	}

	_, err := methods.AnswerVM(ctx, v.c, &req)
	if err != nil {
		return err
	}

	return nil
}

// CreateSnapshot creates a new snapshot of a virtual machine.
func (v VirtualMachine) CreateSnapshot(ctx context.Context, name string, description string, memory bool, quiesce bool) (*Task, error) {
	req := types.CreateSnapshot_Task{
		This:        v.Reference(),
		Name:        name,
		Description: description,
		Memory:      memory,
		Quiesce:     quiesce,
	}

	res, err := methods.CreateSnapshot_Task(ctx, v.c, &req)
	if err != nil {
		return nil, err
	}

	return NewTask(v.c, res.Returnval), nil
}

// IsToolsRunning returns true if VMware Tools is currently running in the guest OS, and false otherwise.
func (v VirtualMachine) IsToolsRunning(ctx context.Context) (bool, error) {
	var o mo.VirtualMachine

	err := v.Properties(ctx, v.Reference(), []string{"guest.toolsRunningStatus"}, &o)
	if err != nil {
		return false, err
	}

	return o.Guest.ToolsRunningStatus == string(types.VirtualMachineToolsRunningStatusGuestToolsRunning), nil
}

// Wait for the VirtualMachine to change to the desired power state.
func (v VirtualMachine) WaitForPowerState(ctx context.Context, state types.VirtualMachinePowerState) error {
	p := property.DefaultCollector(v.c)
	err := property.Wait(ctx, p, v.Reference(), []string{PropRuntimePowerState}, func(pc []types.PropertyChange) bool {
		for _, c := range pc {
			if c.Name != PropRuntimePowerState {
				continue
			}
			if c.Val == nil {
				continue
			}

			ps := c.Val.(types.VirtualMachinePowerState)
			if ps == state {
				return true
			}
		}
		return false
	})

	return err
}

func (v VirtualMachine) MarkAsTemplate(ctx context.Context) error {
	req := types.MarkAsTemplate{
		This: v.Reference(),
	}

	_, err := methods.MarkAsTemplate(ctx, v.c, &req)
	if err != nil {
		return err
	}

	return nil
}

func (v VirtualMachine) MarkAsVirtualMachine(ctx context.Context, pool ResourcePool, host *HostSystem) error {
	req := types.MarkAsVirtualMachine{
		This: v.Reference(),
		Pool: pool.Reference(),
	}

	if host != nil {
		ref := host.Reference()
		req.Host = &ref
	}

	_, err := methods.MarkAsVirtualMachine(ctx, v.c, &req)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package watchers

import (
	k8sErrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/util/workqueue"

	"github.com/cilium/cilium/pkg/k8s/informer"
	slim_corev1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/api/core/v1"
	slim_metav1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1"
)

var (
	// slimNodeStore contains all cluster nodes store as slim_core.Node
	slimNodeStore cache.Store

	nodeController cache.Controller

	nodeQueue = workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "node-queue")
)

type slimNodeGetter interface {
	GetK8sSlimNode(nodeName string) (*slim_corev1.Node, error)
}

type nodeGetter struct{}

// GetK8sSlimNode returns a slim_corev1.Node from the local store.
// The return structure should only be used for read purposes and should never
// be written into it.
func (nodeGetter) GetK8sSlimNode(nodeName string) (*slim_corev1.Node, error) {
	nodeInterface, exists, err := slimNodeStore.GetByKey(nodeName)
	if err != nil {
		return nil, err
	}
	if !exists {
		return nil, k8sErrors.NewNotFound(schema.GroupResource{
			Group:    "core",
			Resource: "Node",
		}, nodeName)
	}
	return nodeInterface.(*slim_corev1.Node), nil
}

// nodesInit starts up a node watcher to handle node events.
func nodesInit(k8sClient kubernetes.Interface, stopCh <-chan struct{}) {
	slimNodeStore, nodeController = informer.NewInformer(
		cache.NewListWatchFromClient(k8sClient.CoreV1().RESTClient(),
			"nodes", metav1.NamespaceAll, fields.Everything()),
		&slim_corev1.Node{},
		0,
		cache.ResourceEventHandlerFuncs{
			AddFunc: func(obj interface{}) {
				key, _ := queueKeyFunc(obj)
				nodeQueue.Add(key)
			},
			UpdateFunc: func(_, newObj interface{}) {
				key, _ := queueKeyFunc(newObj)
				nodeQueue.Add(key)
			},
		},
		convertToNode,
	)
	go nodeController.Run(stopCh)
}

func convertToNode(obj interface{}) interface{} {
	switch concreteObj := obj.(type) {
	case *slim_corev1.Node:
		n := &slim_corev1.Node{
			TypeMeta: concreteObj.TypeMeta,
			ObjectMeta: slim_metav1.ObjectMeta{
				Name:            concreteObj.Name,
				ResourceVersion: concreteObj.ResourceVersion,
			},
			Spec: slim_corev1.NodeSpec{
				Taints: concreteObj.Spec.Taints,
			},
			Status: slim_corev1.NodeStatus{
				Conditions: concreteObj.Status.Conditions,
			},
		}
		*concreteObj = slim_corev1.Node{}
		return n
	case cache.DeletedFinalStateUnknown:
		node, ok := concreteObj.Obj.(*slim_corev1.Node)
		if !ok {
			return obj
		}
		dfsu := cache.DeletedFinalStateUnknown{
			Key: concreteObj.Key,
			Obj: &slim_corev1.Node{
				TypeMeta: node.TypeMeta,
				ObjectMeta: slim_metav1.ObjectMeta{
					Name:            node.Name,
					ResourceVersion: node.ResourceVersion,
				},
				Spec: slim_corev1.NodeSpec{
					Taints: node.Spec.Taints,
				},
				Status: slim_corev1.NodeStatus{
					Conditions: node.Status.Conditions,
				},
			},
		}
		// Small GC optimization
		*node = slim_corev1.Node{}
		return dfsu
	default:
		return obj
	}
}
<commit_msg>operator: Add sync.Once for k8s node watcher<commit_after>package watchers

import (
	"sync"

	k8sErrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/util/workqueue"

	"github.com/cilium/cilium/pkg/k8s/informer"
	slim_corev1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/api/core/v1"
	slim_metav1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1"
)

var (
	// nodeSyncOnce is used to make sure nodesInit is only setup once.
	nodeSyncOnce sync.Once

	// slimNodeStore contains all cluster nodes store as slim_core.Node
	slimNodeStore cache.Store

	// slimNodeStoreSynced is closed once the slimNodeStore is synced
	// with k8s.
	slimNodeStoreSynced = make(chan struct{})

	nodeController cache.Controller

	nodeQueue = workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "node-queue")
)

type slimNodeGetter interface {
	GetK8sSlimNode(nodeName string) (*slim_corev1.Node, error)
}

type nodeGetter struct{}

// GetK8sSlimNode returns a slim_corev1.Node from the local store.
// The return structure should only be used for read purposes and should never
// be written into it.
func (nodeGetter) GetK8sSlimNode(nodeName string) (*slim_corev1.Node, error) {
	nodeInterface, exists, err := slimNodeStore.GetByKey(nodeName)
	if err != nil {
		return nil, err
	}
	if !exists {
		return nil, k8sErrors.NewNotFound(schema.GroupResource{
			Group:    "core",
			Resource: "Node",
		}, nodeName)
	}
	return nodeInterface.(*slim_corev1.Node), nil
}

// nodesInit starts up a node watcher to handle node events.
func nodesInit(k8sClient kubernetes.Interface, stopCh <-chan struct{}) {
	nodeSyncOnce.Do(func() {
		slimNodeStore, nodeController = informer.NewInformer(
			cache.NewListWatchFromClient(k8sClient.CoreV1().RESTClient(),
				"nodes", metav1.NamespaceAll, fields.Everything()),
			&slim_corev1.Node{},
			0,
			cache.ResourceEventHandlerFuncs{
				AddFunc: func(obj interface{}) {
					key, _ := queueKeyFunc(obj)
					nodeQueue.Add(key)
				},
				UpdateFunc: func(_, newObj interface{}) {
					key, _ := queueKeyFunc(newObj)
					nodeQueue.Add(key)
				},
			},
			convertToNode,
		)
		go nodeController.Run(stopCh)

		cache.WaitForCacheSync(stopCh, nodeController.HasSynced)
		close(slimNodeStoreSynced)
	})
}

func convertToNode(obj interface{}) interface{} {
	switch concreteObj := obj.(type) {
	case *slim_corev1.Node:
		n := &slim_corev1.Node{
			TypeMeta: concreteObj.TypeMeta,
			ObjectMeta: slim_metav1.ObjectMeta{
				Name:            concreteObj.Name,
				ResourceVersion: concreteObj.ResourceVersion,
			},
			Spec: slim_corev1.NodeSpec{
				Taints: concreteObj.Spec.Taints,
			},
			Status: slim_corev1.NodeStatus{
				Conditions: concreteObj.Status.Conditions,
			},
		}
		*concreteObj = slim_corev1.Node{}
		return n
	case cache.DeletedFinalStateUnknown:
		node, ok := concreteObj.Obj.(*slim_corev1.Node)
		if !ok {
			return obj
		}
		dfsu := cache.DeletedFinalStateUnknown{
			Key: concreteObj.Key,
			Obj: &slim_corev1.Node{
				TypeMeta: node.TypeMeta,
				ObjectMeta: slim_metav1.ObjectMeta{
					Name:            node.Name,
					ResourceVersion: node.ResourceVersion,
				},
				Spec: slim_corev1.NodeSpec{
					Taints: node.Spec.Taints,
				},
				Status: slim_corev1.NodeStatus{
					Conditions: node.Status.Conditions,
				},
			},
		}
		// Small GC optimization
		*node = slim_corev1.Node{}
		return dfsu
	default:
		return obj
	}
}
<|endoftext|>


<commit_before>package sql

import (
	"fmt"
	"reflect"
	"strings"
	"time"

	"gnd.la/orm/driver"
	"gnd.la/orm/index"
	"gnd.la/util/generic"
	"gnd.la/util/structs"
	"gnd.la/util/types"
)

// Backend is the interface implemented by drivers
// for database/sql orm backends
type Backend interface {
	// Check performs any required sanity checks on the connection.
	Check(*DB) error
	// Name passsed to database/sql.Open
	Name() string
	// Tag returns the struct tag read by this backend
	Tag() string
	// Capabilities returns the backend capabilities not provided
	// by the SQL driver itself.
	Capabilities() driver.Capability
	// Placeholder returns the placeholder for the n'th position
	Placeholder(int) string
	// Placeholders returns a placeholders string for the given number if parameters
	Placeholders(int) string
	// StringQuote returns the character used for quoting strings.
	StringQuote() byte
	// IdentifierQuote returns the character used for quoting identifiers.
	IdentifierQuote() byte
	// Func returns the function which corresponds to the given name and
	// return type at the database level.
	Func(string, reflect.Type) (string, error)
	// DefaultValues returns the string used to signal that a INSERT has no provided
	// values and the default ones should be used.
	DefaultValues() string
	// Inspect returns the table as it exists in the database for the current model. If
	// the table does not exist, the Backend is expected to return (nil, nil).
	Inspect(*DB, driver.Model) (*Table, error)
	// HasIndex returns wheter an index exists using the provided model, index and name.
	HasIndex(*DB, driver.Model, *index.Index, string) (bool, error)
	// DefineField returns the complete field definition as a string, including name, type, options...
	// Field constraints are returned in the secon argument, each constraint should be an item in the
	// returned slice.
	DefineField(*DB, driver.Model, *Table, *Field) (string, []string, error)
	// AddFields adds the given field to the table for the given model. prevTable is the result
	// of Inspect() on the previous table, while newTable is generated from the model definition.
	AddFields(db *DB, m driver.Model, prevTable *Table, newTable *Table, fields []*Field) error
	// Alter field changes oldField to newField, potentially including the name.
	AlterField(db *DB, m driver.Model, table *Table, oldField *Field, newField *Field) error
	// Insert performs an insert on the given database for the given model fields.
	// Most drivers should just return db.Exec(query, args...).
	Insert(*DB, driver.Model, string, ...interface{}) (driver.Result, error)
	// Returns the db type of the given field (e.g. INTEGER)
	FieldType(reflect.Type, *structs.Tag) (string, error)
	// Types that need to be transformed (e.g. sqlite transforms time.Time and bool to integer)
	Transforms() []reflect.Type
	// Scan an int64 from the db to Go
	ScanInt(val int64, goVal *reflect.Value, t *structs.Tag) error
	// Scan a float64 from the db to Go
	ScanFloat(val float64, goVal *reflect.Value, t *structs.Tag) error
	// Scan a bool from the db to Go
	ScanBool(val bool, goVal *reflect.Value, t *structs.Tag) error
	// Scan a []byte from the db to Go
	ScanByteSlice(val []byte, goVal *reflect.Value, t *structs.Tag) error
	// Scan a string from the db to Go
	ScanString(val string, goVal *reflect.Value, t *structs.Tag) error
	// Scan a *time.Time from the db to Go
	ScanTime(val *time.Time, goVal *reflect.Value, t *structs.Tag) error
	// Transform a value from Go to the database
	TransformOutValue(reflect.Value) (interface{}, error)
}

const placeholders = "?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?"

type SqlBackend struct {
}

func (b *SqlBackend) Check(_ *DB) error {
	return nil
}

func (b *SqlBackend) Capabilities() driver.Capability {
	return driver.CAP_DEFAULTS_TEXT
}

func (b *SqlBackend) Placeholder(n int) string {
	return "?"
}

func (b *SqlBackend) Placeholders(n int) string {
	p := placeholders
	if n > 32 {
		p = strings.Repeat("?,", n)
	}
	return p[:2*n-1]
}

func (b *SqlBackend) StringQuote() byte {
	return '\''
}

func (b *SqlBackend) IdentifierQuote() byte {
	return '"'
}

func (b *SqlBackend) Func(fname string, retType reflect.Type) (string, error) {
	return "", ErrFuncNotSupported
}

func (b *SqlBackend) DefaultValues() string {
	return "DEFAULT VALUES"
}

func (b *SqlBackend) Inspect(db *DB, m driver.Model, schema string) (*Table, error) {
	var val int
	name := db.QuoteString(m.Table())
	s := db.QuoteString(schema)
	eq := fmt.Sprintf("SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE "+
		"TABLE_NAME = %s AND TABLE_SCHEMA = %s", name, s)
	err := db.QueryRow(eq).Scan(&val)
	if err != nil {
		if err == ErrNoRows {
			return nil, nil
		}
		return nil, err
	}
	// Select fields with their types
	iq := fmt.Sprintf("SELECT COLUMN_NAME, IS_NULLABLE, DATA_TYPE, "+
		"CHARACTER_MAXIMUM_LENGTH FROM INFORMATION_SCHEMA.COLUMNS "+
		"WHERE TABLE_NAME = %s AND TABLE_SCHEMA = %s", name, s)
	rows, err := db.Query(iq)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var fields []*Field
	fieldsByName := make(map[string]*Field)
	for rows.Next() {
		var f Field
		var nullable string
		var maxLength *int
		if err := rows.Scan(&f.Name, &nullable, &f.Type, &maxLength); err != nil {
			return nil, err
		}
		if maxLength != nil {
			f.Type = fmt.Sprintf("%s (%d)", f.Type, *maxLength)
		}
		f.Type = strings.ToUpper(f.Type)
		if nullable != "YES" {
			f.AddConstraint(ConstraintNotNull)
		}
		fields = append(fields, &f)
		fieldsByName[f.Name] = &f
	}
	// Field constraints
	cq := fmt.Sprintf("SELECT C.CONSTRAINT_NAME, CONSTRAINT_TYPE, COLUMN_NAME "+
		"FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS C JOIN "+
		"INFORMATION_SCHEMA.KEY_COLUMN_USAGE K ON C.CONSTRAINT_NAME = "+
		"K.CONSTRAINT_NAME WHERE C.TABLE_NAME = %s AND K.TABLE_NAME = %s"+
		"AND C.TABLE_SCHEMA = %s", name, name, s)
	rows, err = db.Query(cq)
	if err != nil {
		return nil, err
	}
	foreignKeys := make(map[string]string)
	defer rows.Close()
	for rows.Next() {
		var constraintName string
		var constraintType string
		var name string
		if err := rows.Scan(&constraintName, &constraintType, &name); err != nil {
			return nil, err
		}
		field := fieldsByName[name]
		if field == nil {
			return nil, fmt.Errorf("table %s has constraint on non-existing field %s", m.Table(), name)
		}
		switch strings.ToLower(constraintType) {
		case "primary key":
			field.AddConstraint(ConstraintPrimaryKey)
		case "foreign key":
			foreignKeys[constraintName] = name
		case "unique":
			field.AddConstraint(ConstraintUnique)
		default:
			return nil, fmt.Errorf("unknown constraint type %s on field %s in table %s", constraintType, name, m.Table())
		}
	}
	if len(foreignKeys) > 0 {
		// Resolve FKs
		fks := strings.Join(generic.Map(generic.Keys(foreignKeys).([]string), db.QuoteString).([]string), ", ")
		fq := fmt.Sprintf("SELECT CONSTRAINT_NAME, TABLE_NAME, COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE CONSTRAINT_NAME IN (%s)", fks)
		rows, err := db.Query(fq)
		if err != nil {
			return nil, err
		}
		defer rows.Close()
		for rows.Next() {
			var constraintName string
			var tableName string
			var columnName string
			if err := rows.Scan(&constraintName, &tableName, &columnName); err != nil {
				return nil, err
			}
			fieldName := foreignKeys[constraintName]
			// Field was validated previously, won't be nil
			field := fieldsByName[fieldName]
			field.Constraints = append(field.Constraints, &Constraint{Type: ConstraintForeignKey, References: MakeReference(tableName, columnName)})
		}
	}
	return &Table{Fields: fields}, nil
}

func (b *SqlBackend) DefineField(db *DB, m driver.Model, table *Table, f *Field) (string, []string, error) {
	s := fmt.Sprintf("%s %s", db.QuoteIdentifier(f.Name), f.Type)
	if f.Constraint(ConstraintPrimaryKey) != nil && len(table.PrimaryKeys()) == 1 {
		s += " PRIMARY KEY"
	}
	if f.Constraint(ConstraintUnique) != nil {
		s += " UNIQUE"
	}
	if f.Constraint(ConstraintNotNull) != nil {
		s += " NOT NULL"
	}
	if f.HasOption(OptionAutoIncrement) {
		s += " AUTOINCREMENT"
	}
	if f.Default != "" {
		s += " DEFAULT " + f.Default
	}
	if ref := f.Constraint(ConstraintForeignKey); ref != nil {
		s += fmt.Sprintf(" REFERENCES %s(%s)",
			db.QuoteIdentifier(ref.References.Table()), db.QuoteIdentifier(ref.References.Field()))
	}
	return s, nil, nil
}

func (b *SqlBackend) AddFields(db *DB, m driver.Model, prevTable *Table, newTable *Table, fields []*Field) error {
	modelFields := m.Fields()
	tableName := db.QuoteIdentifier(m.Table())
	for _, v := range fields {
		idx := modelFields.MNameMap[v.Name]
		field := v
		hasDefault := modelFields.HasDefault(idx)
		if hasDefault && v.Constraint(ConstraintNotNull) != nil {
			// ORM level default
			// Must be added as nullable first, then the default value
			// must be set and finally the field has to be altered to be
			// nullable.
			field = field.Copy()
			var constraints []*Constraint
			for _, v := range field.Constraints {
				if v.Type != ConstraintNotNull {
					constraints = append(constraints, v)
				}
			}
			field.Constraints = constraints
		}
		sql, cons, err := field.SQL(db, m, newTable)
		if err != nil {
			return err
		}
		if _, err = db.Exec(fmt.Sprintf("ALTER TABLE %s ADD COLUMN %s", tableName, sql)); err != nil {
			return err
		}
		if hasDefault {
			value := modelFields.DefaultValue(idx)
			fieldName := db.QuoteIdentifier(v.Name)
			if _, err := db.Exec(fmt.Sprintf("UPDATE %s SET %s = ?", tableName, fieldName), value); err != nil {
				return err
			}
			if v.Constraint(ConstraintNotNull) != nil {
				if err := db.Backend().AlterField(db, m, newTable, field, v); err != nil {
					return err
				}
			}
		}
		for _, c := range cons {
			if _, err = db.Exec(fmt.Sprintf("ALTER TABLE %s ADD CONSTRAINT %s", tableName, c)); err != nil {
				return err
			}
		}
	}
	return nil
}

func (b *SqlBackend) AlterField(db *DB, m driver.Model, table *Table, oldField *Field, newField *Field) error {
	return fmt.Errorf("SQL backend %s can't ALTER fields", db.Backend().Name())
}

func (b *SqlBackend) Insert(db *DB, m driver.Model, query string, args ...interface{}) (driver.Result, error) {
	return db.Exec(query, args...)
}

func (b *SqlBackend) Transforms() []reflect.Type {
	return nil
}

// These Scan* methods always assume the type is right. Backends which might
// receive different types (e.g. a string like a []byte) should implement their
// own Scan* methods as required.

func (b *SqlBackend) ScanInt(val int64, goVal *reflect.Value, t *structs.Tag) error {
	if types.Kind(goVal.Kind()) == types.Uint {
		goVal.SetUint(uint64(val))
		return nil
	}
	goVal.SetInt(val)
	return nil
}

func (b *SqlBackend) ScanFloat(val float64, goVal *reflect.Value, t *structs.Tag) error {
	goVal.SetFloat(val)
	return nil
}

func (b *SqlBackend) ScanBool(val bool, goVal *reflect.Value, t *structs.Tag) error {
	goVal.SetBool(val)
	return nil
}

func (b *SqlBackend) ScanByteSlice(val []byte, goVal *reflect.Value, t *structs.Tag) error {
	if goVal.Kind() == reflect.String {
		goVal.SetString(string(val))
		return nil
	}
	if len(val) > 0 && !t.Has("raw") {
		b := make([]byte, len(val))
		copy(b, val)
		val = b
	}
	goVal.Set(reflect.ValueOf(val))
	return nil
}

func (b *SqlBackend) ScanString(val string, goVal *reflect.Value, t *structs.Tag) error {
	goVal.SetString(val)
	return nil
}

func (b *SqlBackend) ScanTime(val *time.Time, goVal *reflect.Value, t *structs.Tag) error {
	goVal.Set(reflect.ValueOf(*val))
	return nil
}

func (b *SqlBackend) TransformOutValue(val reflect.Value) (interface{}, error) {
	return val.Interface(), nil
}
<commit_msg>Add space between placeholder and value<commit_after>package sql

import (
	"fmt"
	"reflect"
	"strings"
	"time"

	"gnd.la/orm/driver"
	"gnd.la/orm/index"
	"gnd.la/util/generic"
	"gnd.la/util/structs"
	"gnd.la/util/types"
)

// Backend is the interface implemented by drivers
// for database/sql orm backends
type Backend interface {
	// Check performs any required sanity checks on the connection.
	Check(*DB) error
	// Name passsed to database/sql.Open
	Name() string
	// Tag returns the struct tag read by this backend
	Tag() string
	// Capabilities returns the backend capabilities not provided
	// by the SQL driver itself.
	Capabilities() driver.Capability
	// Placeholder returns the placeholder for the n'th position
	Placeholder(int) string
	// Placeholders returns a placeholders string for the given number if parameters
	Placeholders(int) string
	// StringQuote returns the character used for quoting strings.
	StringQuote() byte
	// IdentifierQuote returns the character used for quoting identifiers.
	IdentifierQuote() byte
	// Func returns the function which corresponds to the given name and
	// return type at the database level.
	Func(string, reflect.Type) (string, error)
	// DefaultValues returns the string used to signal that a INSERT has no provided
	// values and the default ones should be used.
	DefaultValues() string
	// Inspect returns the table as it exists in the database for the current model. If
	// the table does not exist, the Backend is expected to return (nil, nil).
	Inspect(*DB, driver.Model) (*Table, error)
	// HasIndex returns wheter an index exists using the provided model, index and name.
	HasIndex(*DB, driver.Model, *index.Index, string) (bool, error)
	// DefineField returns the complete field definition as a string, including name, type, options...
	// Field constraints are returned in the secon argument, each constraint should be an item in the
	// returned slice.
	DefineField(*DB, driver.Model, *Table, *Field) (string, []string, error)
	// AddFields adds the given field to the table for the given model. prevTable is the result
	// of Inspect() on the previous table, while newTable is generated from the model definition.
	AddFields(db *DB, m driver.Model, prevTable *Table, newTable *Table, fields []*Field) error
	// Alter field changes oldField to newField, potentially including the name.
	AlterField(db *DB, m driver.Model, table *Table, oldField *Field, newField *Field) error
	// Insert performs an insert on the given database for the given model fields.
	// Most drivers should just return db.Exec(query, args...).
	Insert(*DB, driver.Model, string, ...interface{}) (driver.Result, error)
	// Returns the db type of the given field (e.g. INTEGER)
	FieldType(reflect.Type, *structs.Tag) (string, error)
	// Types that need to be transformed (e.g. sqlite transforms time.Time and bool to integer)
	Transforms() []reflect.Type
	// Scan an int64 from the db to Go
	ScanInt(val int64, goVal *reflect.Value, t *structs.Tag) error
	// Scan a float64 from the db to Go
	ScanFloat(val float64, goVal *reflect.Value, t *structs.Tag) error
	// Scan a bool from the db to Go
	ScanBool(val bool, goVal *reflect.Value, t *structs.Tag) error
	// Scan a []byte from the db to Go
	ScanByteSlice(val []byte, goVal *reflect.Value, t *structs.Tag) error
	// Scan a string from the db to Go
	ScanString(val string, goVal *reflect.Value, t *structs.Tag) error
	// Scan a *time.Time from the db to Go
	ScanTime(val *time.Time, goVal *reflect.Value, t *structs.Tag) error
	// Transform a value from Go to the database
	TransformOutValue(reflect.Value) (interface{}, error)
}

const placeholders = "?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?"

type SqlBackend struct {
}

func (b *SqlBackend) Check(_ *DB) error {
	return nil
}

func (b *SqlBackend) Capabilities() driver.Capability {
	return driver.CAP_DEFAULTS_TEXT
}

func (b *SqlBackend) Placeholder(n int) string {
	return "?"
}

func (b *SqlBackend) Placeholders(n int) string {
	p := placeholders
	if n > 32 {
		p = strings.Repeat("?,", n)
	}
	return p[:2*n-1]
}

func (b *SqlBackend) StringQuote() byte {
	return '\''
}

func (b *SqlBackend) IdentifierQuote() byte {
	return '"'
}

func (b *SqlBackend) Func(fname string, retType reflect.Type) (string, error) {
	return "", ErrFuncNotSupported
}

func (b *SqlBackend) DefaultValues() string {
	return "DEFAULT VALUES"
}

func (b *SqlBackend) Inspect(db *DB, m driver.Model, schema string) (*Table, error) {
	var val int
	name := db.QuoteString(m.Table())
	s := db.QuoteString(schema)
	eq := fmt.Sprintf("SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE "+
		"TABLE_NAME = %s AND TABLE_SCHEMA = %s", name, s)
	err := db.QueryRow(eq).Scan(&val)
	if err != nil {
		if err == ErrNoRows {
			return nil, nil
		}
		return nil, err
	}
	// Select fields with their types
	iq := fmt.Sprintf("SELECT COLUMN_NAME, IS_NULLABLE, DATA_TYPE, "+
		"CHARACTER_MAXIMUM_LENGTH FROM INFORMATION_SCHEMA.COLUMNS "+
		"WHERE TABLE_NAME = %s AND TABLE_SCHEMA = %s", name, s)
	rows, err := db.Query(iq)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var fields []*Field
	fieldsByName := make(map[string]*Field)
	for rows.Next() {
		var f Field
		var nullable string
		var maxLength *int
		if err := rows.Scan(&f.Name, &nullable, &f.Type, &maxLength); err != nil {
			return nil, err
		}
		if maxLength != nil {
			f.Type = fmt.Sprintf("%s (%d)", f.Type, *maxLength)
		}
		f.Type = strings.ToUpper(f.Type)
		if nullable != "YES" {
			f.AddConstraint(ConstraintNotNull)
		}
		fields = append(fields, &f)
		fieldsByName[f.Name] = &f
	}
	// Field constraints
	cq := fmt.Sprintf("SELECT C.CONSTRAINT_NAME, CONSTRAINT_TYPE, COLUMN_NAME "+
		"FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS C JOIN "+
		"INFORMATION_SCHEMA.KEY_COLUMN_USAGE K ON C.CONSTRAINT_NAME = "+
		"K.CONSTRAINT_NAME WHERE C.TABLE_NAME = %s AND K.TABLE_NAME = %s "+
		"AND C.TABLE_SCHEMA = %s", name, name, s)
	rows, err = db.Query(cq)
	if err != nil {
		return nil, err
	}
	foreignKeys := make(map[string]string)
	defer rows.Close()
	for rows.Next() {
		var constraintName string
		var constraintType string
		var name string
		if err := rows.Scan(&constraintName, &constraintType, &name); err != nil {
			return nil, err
		}
		field := fieldsByName[name]
		if field == nil {
			return nil, fmt.Errorf("table %s has constraint on non-existing field %s", m.Table(), name)
		}
		switch strings.ToLower(constraintType) {
		case "primary key":
			field.AddConstraint(ConstraintPrimaryKey)
		case "foreign key":
			foreignKeys[constraintName] = name
		case "unique":
			field.AddConstraint(ConstraintUnique)
		default:
			return nil, fmt.Errorf("unknown constraint type %s on field %s in table %s", constraintType, name, m.Table())
		}
	}
	if len(foreignKeys) > 0 {
		// Resolve FKs
		fks := strings.Join(generic.Map(generic.Keys(foreignKeys).([]string), db.QuoteString).([]string), ", ")
		fq := fmt.Sprintf("SELECT CONSTRAINT_NAME, TABLE_NAME, COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE CONSTRAINT_NAME IN (%s)", fks)
		rows, err := db.Query(fq)
		if err != nil {
			return nil, err
		}
		defer rows.Close()
		for rows.Next() {
			var constraintName string
			var tableName string
			var columnName string
			if err := rows.Scan(&constraintName, &tableName, &columnName); err != nil {
				return nil, err
			}
			fieldName := foreignKeys[constraintName]
			// Field was validated previously, won't be nil
			field := fieldsByName[fieldName]
			field.Constraints = append(field.Constraints, &Constraint{Type: ConstraintForeignKey, References: MakeReference(tableName, columnName)})
		}
	}
	return &Table{Fields: fields}, nil
}

func (b *SqlBackend) DefineField(db *DB, m driver.Model, table *Table, f *Field) (string, []string, error) {
	s := fmt.Sprintf("%s %s", db.QuoteIdentifier(f.Name), f.Type)
	if f.Constraint(ConstraintPrimaryKey) != nil && len(table.PrimaryKeys()) == 1 {
		s += " PRIMARY KEY"
	}
	if f.Constraint(ConstraintUnique) != nil {
		s += " UNIQUE"
	}
	if f.Constraint(ConstraintNotNull) != nil {
		s += " NOT NULL"
	}
	if f.HasOption(OptionAutoIncrement) {
		s += " AUTOINCREMENT"
	}
	if f.Default != "" {
		s += " DEFAULT " + f.Default
	}
	if ref := f.Constraint(ConstraintForeignKey); ref != nil {
		s += fmt.Sprintf(" REFERENCES %s(%s)",
			db.QuoteIdentifier(ref.References.Table()), db.QuoteIdentifier(ref.References.Field()))
	}
	return s, nil, nil
}

func (b *SqlBackend) AddFields(db *DB, m driver.Model, prevTable *Table, newTable *Table, fields []*Field) error {
	modelFields := m.Fields()
	tableName := db.QuoteIdentifier(m.Table())
	for _, v := range fields {
		idx := modelFields.MNameMap[v.Name]
		field := v
		hasDefault := modelFields.HasDefault(idx)
		if hasDefault && v.Constraint(ConstraintNotNull) != nil {
			// ORM level default
			// Must be added as nullable first, then the default value
			// must be set and finally the field has to be altered to be
			// nullable.
			field = field.Copy()
			var constraints []*Constraint
			for _, v := range field.Constraints {
				if v.Type != ConstraintNotNull {
					constraints = append(constraints, v)
				}
			}
			field.Constraints = constraints
		}
		sql, cons, err := field.SQL(db, m, newTable)
		if err != nil {
			return err
		}
		if _, err = db.Exec(fmt.Sprintf("ALTER TABLE %s ADD COLUMN %s", tableName, sql)); err != nil {
			return err
		}
		if hasDefault {
			value := modelFields.DefaultValue(idx)
			fieldName := db.QuoteIdentifier(v.Name)
			if _, err := db.Exec(fmt.Sprintf("UPDATE %s SET %s = ?", tableName, fieldName), value); err != nil {
				return err
			}
			if v.Constraint(ConstraintNotNull) != nil {
				if err := db.Backend().AlterField(db, m, newTable, field, v); err != nil {
					return err
				}
			}
		}
		for _, c := range cons {
			if _, err = db.Exec(fmt.Sprintf("ALTER TABLE %s ADD CONSTRAINT %s", tableName, c)); err != nil {
				return err
			}
		}
	}
	return nil
}

func (b *SqlBackend) AlterField(db *DB, m driver.Model, table *Table, oldField *Field, newField *Field) error {
	return fmt.Errorf("SQL backend %s can't ALTER fields", db.Backend().Name())
}

func (b *SqlBackend) Insert(db *DB, m driver.Model, query string, args ...interface{}) (driver.Result, error) {
	return db.Exec(query, args...)
}

func (b *SqlBackend) Transforms() []reflect.Type {
	return nil
}

// These Scan* methods always assume the type is right. Backends which might
// receive different types (e.g. a string like a []byte) should implement their
// own Scan* methods as required.

func (b *SqlBackend) ScanInt(val int64, goVal *reflect.Value, t *structs.Tag) error {
	if types.Kind(goVal.Kind()) == types.Uint {
		goVal.SetUint(uint64(val))
		return nil
	}
	goVal.SetInt(val)
	return nil
}

func (b *SqlBackend) ScanFloat(val float64, goVal *reflect.Value, t *structs.Tag) error {
	goVal.SetFloat(val)
	return nil
}

func (b *SqlBackend) ScanBool(val bool, goVal *reflect.Value, t *structs.Tag) error {
	goVal.SetBool(val)
	return nil
}

func (b *SqlBackend) ScanByteSlice(val []byte, goVal *reflect.Value, t *structs.Tag) error {
	if goVal.Kind() == reflect.String {
		goVal.SetString(string(val))
		return nil
	}
	if len(val) > 0 && !t.Has("raw") {
		b := make([]byte, len(val))
		copy(b, val)
		val = b
	}
	goVal.Set(reflect.ValueOf(val))
	return nil
}

func (b *SqlBackend) ScanString(val string, goVal *reflect.Value, t *structs.Tag) error {
	goVal.SetString(val)
	return nil
}

func (b *SqlBackend) ScanTime(val *time.Time, goVal *reflect.Value, t *structs.Tag) error {
	goVal.Set(reflect.ValueOf(*val))
	return nil
}

func (b *SqlBackend) TransformOutValue(val reflect.Value) (interface{}, error) {
	return val.Interface(), nil
}
<|endoftext|>


<commit_before>// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com

// This package provides Documize as a single executable.
package main

import (
	"fmt"

	"github.com/documize/community/core/api"
	"github.com/documize/community/core/api/endpoint"
	"github.com/documize/community/core/api/request"
	"github.com/documize/community/core/env"
	"github.com/documize/community/domain/section"
	"github.com/documize/community/edition/boot"
	"github.com/documize/community/edition/logging"
	_ "github.com/documize/community/embed" // the compressed front-end code and static data
	_ "github.com/go-sql-driver/mysql"      // the mysql driver is required behind the scenes
)

var rt env.Runtime

func main() {
	// runtime stores server/application level information
	rt := env.Runtime{}

	// wire up logging implementation
	rt.Log = logging.NewLogger()

	// define product edition details
	rt.Product = env.ProdInfo{}
	rt.Product.Major = "1"
	rt.Product.Minor = "50"
	rt.Product.Patch = "2"
	rt.Product.Version = fmt.Sprintf("%s.%s.%s", rt.Product.Major, rt.Product.Minor, rt.Product.Patch)
	rt.Product.Edition = "Community"
	rt.Product.Title = fmt.Sprintf("%s Edition", rt.Product.Edition)
	rt.Product.License = env.License{}
	rt.Product.License.Seats = 1
	rt.Product.License.Valid = true
	rt.Product.License.Trial = false
	rt.Product.License.Edition = "Community"

	// parse settings from command line and environment
	rt.Flags = env.ParseFlags()
	flagsOK := boot.InitRuntime(&rt)

	if flagsOK {
		// runtime.Log = runtime.Log.SetDB(runtime.Db)
	}

	// temp code repair
	api.Runtime = rt
	request.Db = rt.Db

	section.Register(rt)

	ready := make(chan struct{}, 1) // channel is used for testing
	endpoint.Serve(ready)
}
<commit_msg>comments<commit_after>// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com

// This package provides Documize as a single executable.
package main

import (
	"fmt"

	"github.com/documize/community/core/api"
	"github.com/documize/community/core/api/endpoint"
	"github.com/documize/community/core/api/request"
	"github.com/documize/community/core/env"
	"github.com/documize/community/domain/section"
	"github.com/documize/community/edition/boot"
	"github.com/documize/community/edition/logging"
	_ "github.com/documize/community/embed" // the compressed front-end code and static data
	_ "github.com/go-sql-driver/mysql"      // the mysql driver is required behind the scenes
)

var rt env.Runtime

func main() {
	// runtime stores server/application level information
	rt := env.Runtime{}

	// wire up logging implementation
	rt.Log = logging.NewLogger()

	// define product edition details
	rt.Product = env.ProdInfo{}
	rt.Product.Major = "1"
	rt.Product.Minor = "50"
	rt.Product.Patch = "2"
	rt.Product.Version = fmt.Sprintf("%s.%s.%s", rt.Product.Major, rt.Product.Minor, rt.Product.Patch)
	rt.Product.Edition = "Community"
	rt.Product.Title = fmt.Sprintf("%s Edition", rt.Product.Edition)
	rt.Product.License = env.License{}
	rt.Product.License.Seats = 1
	rt.Product.License.Valid = true
	rt.Product.License.Trial = false
	rt.Product.License.Edition = "Community"

	// parse settings from command line and environment
	rt.Flags = env.ParseFlags()
	flagsOK := boot.InitRuntime(&rt)

	if flagsOK {
		// runtime.Log = runtime.Log.SetDB(runtime.Db)
	}

	// temp code repair
	api.Runtime = rt
	request.Db = rt.Db

	// Register smart sections
	section.Register(rt)

	ready := make(chan struct{}, 1) // channel signals router ready
	endpoint.Serve(ready)
}
<|endoftext|>


<commit_before>// Package postmark provides a convenient wrapper for the Postmark API
package postmark

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
	"net/mail"
	"net/url"
	"strings"
)

const (
	DefaultHost = "api.postmarkapp.com"
)

type Message struct {
	From        *mail.Address
	To          []*mail.Address
	Cc          []*mail.Address
	Bcc         []*mail.Address
	Subject     string
	Tag         string
	HtmlBody    io.Reader
	TextBody    io.Reader
	ReplyTo     *mail.Address
	Headers     mail.Header
	Attachments []Attachment
}

func (m *Message) MarshalJSON() ([]byte, error) {
	doc := &struct {
		From        string
		To          string
		Cc          string
		Bcc         string
		Subject     string
		Tag         string
		HtmlBody    string
		TextBody    string
		ReplyTo     string
		Headers     []map[string]string
		Attachments []Attachment `json:"omitempty"`
	}{}

	doc.From = m.From.String()
	to := []string{}
	for _, addr := range m.To {
		to = append(to, addr.String())
	}
	doc.To = strings.Join(to, ", ")
	cc := []string{}
	for _, addr := range m.Cc {
		cc = append(cc, addr.String())
	}
	doc.Cc = strings.Join(cc, ", ")
	bcc := []string{}
	for _, addr := range m.Bcc {
		bcc = append(bcc, addr.String())
	}
	doc.Bcc = strings.Join(bcc, ", ")
	doc.Subject = m.Subject
	doc.Tag = m.Tag
	if m.HtmlBody != nil {
		htmlBody, err := ioutil.ReadAll(m.HtmlBody)
		if err != nil {
			return nil, err
		}
		doc.HtmlBody = string(htmlBody)
	}
	if m.TextBody != nil {
		textBody, err := ioutil.ReadAll(m.TextBody)
		if err != nil {
			return nil, err
		}
		doc.TextBody = string(textBody)
	}
	if m.ReplyTo != nil {
		doc.ReplyTo = m.ReplyTo.String()
	}
	headers := []map[string]string{}
	for k, vs := range m.Headers {
		for _, v := range vs {
			headers = append(headers, map[string]string{
				"Name":  k,
				"Value": v,
			})
		}
	}
	doc.Headers = headers
	doc.Attachments = m.Attachments

	return json.Marshal(doc)
}

type Attachment struct {
	Name        string
	Content     io.Reader
	ContentType string
}

func (a *Attachment) MarshalJSON() ([]byte, error) {
	doc := &struct {
		Name        string
		Content     string
		ContentType string
	}{}

	doc.Name = a.Name
	content, err := ioutil.ReadAll(a.Content)
	if err != nil {
		return nil, err
	}
	doc.Content = base64.StdEncoding.EncodeToString(content)
	doc.ContentType = a.ContentType

	return json.Marshal(doc)
}

type Client struct {
	ApiKey string
	Secure bool

	Host string // Host for the API endpoints, DefaultHost if ""
}

func (c *Client) endpoint(path string) *url.URL {
	url := &url.URL{}
	if c.Secure {
		url.Scheme = "https"
	} else {
		url.Scheme = "http"
	}

	if c.Host == "" {
		url.Host = HOST
	} else {
		url.Host = c.Host
	}

	url.Path = path

	return url
}

// Send sends a single message
func (c *Client) Send(msg *Message) (*Result, error) {
	buf := bytes.Buffer{}
	err := json.NewEncoder(&buf).Encode(msg)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", c.endpoint("email").String(), &buf)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-Postmark-Server-Token", c.ApiKey)

	resp, err := (&http.Client{}).Do(req)
	if err != nil {
		return nil, err
	}

	res := &Result{}
	json.NewDecoder(resp.Body).Decode(res)
	return res, nil
}

// SendBatch sends multiple messages using the batch API
func (c *Client) SendBatch(msg []*Message) ([]*Result, error) {
	buf := bytes.Buffer{}
	err := json.NewEncoder(&buf).Encode(msg)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", c.endpoint("email/batch").String(), &buf)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-Postmark-Server-Token", c.ApiKey)

	resp, err := (&http.Client{}).Do(req)
	if err != nil {
		return nil, err
	}

	res := []*Result{}
	json.NewDecoder(resp.Body).Decode(res)
	return res, nil
}

type Result struct {
	ErrorCode   int
	Message     string
	MessageID   string
	SubmittedAt string
	To          string
}
<commit_msg>[FIX] - Typo Host was used instead of DefaultHost<commit_after>// Package postmark provides a convenient wrapper for the Postmark API
package postmark

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"
	"net/mail"
	"net/url"
	"strings"
)

const (
	DefaultHost = "api.postmarkapp.com"
)

type Message struct {
	From        *mail.Address
	To          []*mail.Address
	Cc          []*mail.Address
	Bcc         []*mail.Address
	Subject     string
	Tag         string
	HtmlBody    io.Reader
	TextBody    io.Reader
	ReplyTo     *mail.Address
	Headers     mail.Header
	Attachments []Attachment
}

func (m *Message) MarshalJSON() ([]byte, error) {
	doc := &struct {
		From        string
		To          string
		Cc          string
		Bcc         string
		Subject     string
		Tag         string
		HtmlBody    string
		TextBody    string
		ReplyTo     string
		Headers     []map[string]string
		Attachments []Attachment `json:"omitempty"`
	}{}

	doc.From = m.From.String()
	to := []string{}
	for _, addr := range m.To {
		to = append(to, addr.String())
	}
	doc.To = strings.Join(to, ", ")
	cc := []string{}
	for _, addr := range m.Cc {
		cc = append(cc, addr.String())
	}
	doc.Cc = strings.Join(cc, ", ")
	bcc := []string{}
	for _, addr := range m.Bcc {
		bcc = append(bcc, addr.String())
	}
	doc.Bcc = strings.Join(bcc, ", ")
	doc.Subject = m.Subject
	doc.Tag = m.Tag
	if m.HtmlBody != nil {
		htmlBody, err := ioutil.ReadAll(m.HtmlBody)
		if err != nil {
			return nil, err
		}
		doc.HtmlBody = string(htmlBody)
	}
	if m.TextBody != nil {
		textBody, err := ioutil.ReadAll(m.TextBody)
		if err != nil {
			return nil, err
		}
		doc.TextBody = string(textBody)
	}
	if m.ReplyTo != nil {
		doc.ReplyTo = m.ReplyTo.String()
	}
	headers := []map[string]string{}
	for k, vs := range m.Headers {
		for _, v := range vs {
			headers = append(headers, map[string]string{
				"Name":  k,
				"Value": v,
			})
		}
	}
	doc.Headers = headers
	doc.Attachments = m.Attachments

	return json.Marshal(doc)
}

type Attachment struct {
	Name        string
	Content     io.Reader
	ContentType string
}

func (a *Attachment) MarshalJSON() ([]byte, error) {
	doc := &struct {
		Name        string
		Content     string
		ContentType string
	}{}

	doc.Name = a.Name
	content, err := ioutil.ReadAll(a.Content)
	if err != nil {
		return nil, err
	}
	doc.Content = base64.StdEncoding.EncodeToString(content)
	doc.ContentType = a.ContentType

	return json.Marshal(doc)
}

type Client struct {
	ApiKey string
	Secure bool

	Host string // Host for the API endpoints, DefaultHost if ""
}

func (c *Client) endpoint(path string) *url.URL {
	url := &url.URL{}
	if c.Secure {
		url.Scheme = "https"
	} else {
		url.Scheme = "http"
	}

	if c.Host == "" {
		url.Host = DefaultHost
	} else {
		url.Host = c.Host
	}

	url.Path = path

	return url
}

// Send sends a single message
func (c *Client) Send(msg *Message) (*Result, error) {
	buf := bytes.Buffer{}
	err := json.NewEncoder(&buf).Encode(msg)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", c.endpoint("email").String(), &buf)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-Postmark-Server-Token", c.ApiKey)

	resp, err := (&http.Client{}).Do(req)
	if err != nil {
		return nil, err
	}

	res := &Result{}
	json.NewDecoder(resp.Body).Decode(res)
	return res, nil
}

// SendBatch sends multiple messages using the batch API
func (c *Client) SendBatch(msg []*Message) ([]*Result, error) {
	buf := bytes.Buffer{}
	err := json.NewEncoder(&buf).Encode(msg)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("POST", c.endpoint("email/batch").String(), &buf)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Accept", "application/json")
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-Postmark-Server-Token", c.ApiKey)

	resp, err := (&http.Client{}).Do(req)
	if err != nil {
		return nil, err
	}

	res := []*Result{}
	json.NewDecoder(resp.Body).Decode(res)
	return res, nil
}

type Result struct {
	ErrorCode   int
	Message     string
	MessageID   string
	SubmittedAt string
	To          string
}
<|endoftext|>


<commit_before>package main

import (
	"image/color"
	"io"
	"strconv"

	"github.com/MJKWoolnough/gopherjs/xjs"

	"honnef.co/go/js/dom"
)

type ProgressBar struct {
	*dom.HTMLCanvasElement
	percent                int
	ctx                    *dom.CanvasRenderingContext2D
	width, height          int
	foreground, background string
}

func (p *ProgressBar) Percent(i int) {
	p.percent = i
	p.draw()
}

func (p *ProgressBar) draw() {
	p.ctx.FillStyle = p.background
	p.ctx.FillRect(0, 0, p.width, p.height)
	p.ctx.FillStyle = p.foreground
	p.ctx.FillRect(0, 0, p.width*p.percent/100, p.height)
}

func NewProgressBar(fore, back color.Color, width, height int) *ProgressBar {
	c := xjs.CreateElement("canvas").(*dom.HTMLCanvasElement)
	c.SetAttribute("width", strconv.Itoa(width))
	c.SetAttribute("height", strconv.Itoa(height))
	r, g, b, _ := fore.RGBA()
	foreground := "rgb(" + strconv.Itoa(int(r>>8)) + ", " + strconv.Itoa(int(g>>8)) + ", " + strconv.Itoa(int(b>>8)) + ")"
	r, g, b, _ = back.RGBA()
	background := "rgb(" + strconv.Itoa(int(r>>8)) + ", " + strconv.Itoa(int(g>>8)) + ", " + strconv.Itoa(int(b>>8)) + ")"
	pb := &ProgressBar{
		HTMLCanvasElement: c,
		ctx:               c.GetContext2d(),
		width:             width,
		height:            height,
		foreground:        foreground,
		background:        background,
	}
	pb.draw()
	return pb
}

type ProgressReader struct {
	*ProgressBar
	io.Reader
	offset, size int
}

func (p *ProgressBar) Reader(r io.Reader, size int) *ProgressReader {
	return &ProgressReader{p, r, 0, size}
}

func (pr ProgressReader) Read(p []byte) (int, error) {
	n, err := pr.Reader.Read(p)
	pr.offset += n
	pr.Percent(100 * pr.offset / pr.size)
	return n, err
}
<commit_msg>Renamed func to New<commit_after>package main

import (
	"image/color"
	"io"
	"strconv"

	"github.com/MJKWoolnough/gopherjs/xjs"

	"honnef.co/go/js/dom"
)

type ProgressBar struct {
	*dom.HTMLCanvasElement
	percent                int
	ctx                    *dom.CanvasRenderingContext2D
	width, height          int
	foreground, background string
}

func (p *ProgressBar) Percent(i int) {
	p.percent = i
	p.draw()
}

func (p *ProgressBar) draw() {
	p.ctx.FillStyle = p.background
	p.ctx.FillRect(0, 0, p.width, p.height)
	p.ctx.FillStyle = p.foreground
	p.ctx.FillRect(0, 0, p.width*p.percent/100, p.height)
}

func New(fore, back color.Color, width, height int) *ProgressBar {
	c := xjs.CreateElement("canvas").(*dom.HTMLCanvasElement)
	c.SetAttribute("width", strconv.Itoa(width))
	c.SetAttribute("height", strconv.Itoa(height))
	r, g, b, _ := fore.RGBA()
	foreground := "rgb(" + strconv.Itoa(int(r>>8)) + ", " + strconv.Itoa(int(g>>8)) + ", " + strconv.Itoa(int(b>>8)) + ")"
	r, g, b, _ = back.RGBA()
	background := "rgb(" + strconv.Itoa(int(r>>8)) + ", " + strconv.Itoa(int(g>>8)) + ", " + strconv.Itoa(int(b>>8)) + ")"
	pb := &ProgressBar{
		HTMLCanvasElement: c,
		ctx:               c.GetContext2d(),
		width:             width,
		height:            height,
		foreground:        foreground,
		background:        background,
	}
	pb.draw()
	return pb
}

type ProgressReader struct {
	*ProgressBar
	io.Reader
	offset, size int
}

func (p *ProgressBar) Reader(r io.Reader, size int) *ProgressReader {
	return &ProgressReader{p, r, 0, size}
}

func (pr ProgressReader) Read(p []byte) (int, error) {
	n, err := pr.Reader.Read(p)
	pr.offset += n
	pr.Percent(100 * pr.offset / pr.size)
	return n, err
}
<|endoftext|>


<commit_before>package cli

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"
)

func TestParseAndRun(t *testing.T) {
	t.Parallel()
	dir, err := ioutil.TempDir("", "")
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(dir)
	tests := []struct {
		args     []string
		expected string
	}{
		{args: []string{"math", "Sqrt", "25.4"}, expected: "5.039841267341661\n"},
	}
	for _, test := range tests {
		t.Run(strings.Join(test.args, " "), func(t *testing.T) {
			t.Parallel()
			stderr := &bytes.Buffer{}
			stdout := &bytes.Buffer{}
			env := OSEnv{
				Stderr: stderr,
				Stdout: stdout,
				Args:   append([]string{"gorram"}, test.args...),
			}
			code := ParseAndRun(env)
			if code != 0 {
				t.Errorf("unexpected exit code: %v", code)
			}
			out := stdout.String()
			if out != test.expected {
				t.Fatalf("Expected %q but got %q", test.expected, out)
			}
			if msg := stderr.String(); msg != "" {
				t.Errorf("Expected no stderr output but got %q", msg)
			}
		})
	}
}

// func Now() Time
// tests zero arg Function.
// Tests printing of value with ToString method.
func TestTimeNow(t *testing.T) {
	t.Parallel()
	dir, err := ioutil.TempDir("", "")
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(dir)
	stderr := &bytes.Buffer{}
	stdout := &bytes.Buffer{}
	env := OSEnv{
		Stderr: stderr,
		Stdout: stdout,
		Args:   []string{"gorram", "time", "Now"},
	}
	code := ParseAndRun(env)
	if code != 0 {
		t.Errorf("unexpected exit code: %v", code)
	}
	out := stdout.String()
	expected := fmt.Sprint(time.Now()) + "\n"

	// have to fudge the output checking since obviously the two times will not
	// be exactly the same.  We do this by checking the prefix, up to the hours,
	// and the suffix for the time zone.
	if !strings.HasPrefix(out, expected[:15]) {
		t.Errorf("Expected ~%q but got %q", expected, out)
	}
	if !strings.HasSuffix(out, expected[len(expected)-9:]) {
		t.Errorf("Expected ~%q but got %q", expected, out)
	}
	if msg := stderr.String(); msg != "" {
		t.Errorf("Expected no stderr output but got %q", msg)
	}
}

// func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error
// Tests stdin to []byte argument.
// Tests a dst *bytes.Buffer with a []byte src.
// Tests string arguments.
func TestJsonIndentStdin(t *testing.T) {
	t.Parallel()
	dir, err := ioutil.TempDir("", "")
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(dir)
	stderr := &bytes.Buffer{}
	stdout := &bytes.Buffer{}
	env := OSEnv{
		Stderr: stderr,
		Stdout: stdout,
		Stdin:  strings.NewReader(`{"foo":"bar"}`),
		Args:   []string{"gorram", "encoding/json", "Indent", "", "  "},
	}
	code := ParseAndRun(env)
	if code != 0 {
		t.Errorf("unexpected exit code: %v", code)
	}
	out := stdout.String()
	expected := `
{
  "foo": "bar"
}
`[1:]
	if out != expected {
		t.Fatalf("Expected %q but got %q", expected, out)
	}
	if msg := stderr.String(); msg != "" {
		t.Errorf("Expected no stderr output but got %q", msg)
	}
}

// func Get(url string) (resp *Response, err error)
// Tests a single string argument.
// Tests val, err return value.
// Tests struct return value that contains an io.Reader.
func TestNetHTTPGet(t *testing.T) {
	t.Parallel()
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "Hello, client")
	}))
	defer ts.Close()

	dir, err := ioutil.TempDir("", "")
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(dir)
	stderr := &bytes.Buffer{}
	stdout := &bytes.Buffer{}
	env := OSEnv{
		Stderr: stderr,
		Stdout: stdout,
		Args:   []string{"gorram", "net/http", "Get", ts.URL},
	}
	code := ParseAndRun(env)
	if code != 0 {
		t.Errorf("unexpected exit code: %v", code)
	}
	out := stdout.String()
	expected := "Hello, client\n\n"
	if out != expected {
		t.Fatalf("Expected %q but got %q", expected, out)
	}
	if msg := stderr.String(); msg != "" {
		t.Errorf("Expected no stderr output but got %q", msg)
	}
}

// func Get(url string) (resp *Response, err error)
// Tests a single string argument.
// Tests val, err return value.
// Tests template output.
func TestNetHTTPGetWithTemplate(t *testing.T) {
	t.Parallel()
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "Hello, client")
	}))
	defer ts.Close()

	dir, err := ioutil.TempDir("", "")
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(dir)
	stderr := &bytes.Buffer{}
	stdout := &bytes.Buffer{}
	env := OSEnv{
		Stderr: stderr,
		Stdout: stdout,
		Args:   []string{"gorram", "-t", "{{.Status}}", "net/http", "Get", ts.URL},
	}
	code := ParseAndRun(env)
	if code != 0 {
		t.Errorf("unexpected exit code: %v", code)
	}

	out := stdout.String()
	expected := "200 OK\n"
	if out != expected {
		t.Fatalf("Expected %q but got %q", expected, out)
	}
	if msg := stderr.String(); msg != "" {
		t.Errorf("Expected no stderr output but got %q", msg)
	}
}

// func Get(url string) (resp *Response, err error)
// Tests a single string argument.
// Tests val, err return value.
// Tests template output.
func TestNetHTTPGetWithFileTemplate(t *testing.T) {
	t.Parallel()
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "Hello, client")
	}))
	defer ts.Close()

	dir, err := ioutil.TempDir("", "")
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(dir)
	filename := filepath.Join(dir, "template.txt")
	if err := ioutil.WriteFile(filename, []byte("{{.Status}}"), 0600); err != nil {
		t.Fatal(err)
	}
	stderr := &bytes.Buffer{}
	stdout := &bytes.Buffer{}
	env := OSEnv{
		Stderr: stderr,
		Stdout: stdout,
		Args:   []string{"gorram", "-t", filename, "net/http", "Get", ts.URL},
	}
	code := ParseAndRun(env)
	if code != 0 {
		t.Errorf("unexpected exit code: %v", code)
	}

	out := stdout.String()
	expected := "200 OK\n"
	if out != expected {
		t.Fatalf("Expected %q but got %q", expected, out)
	}
	if msg := stderr.String(); msg != "" {
		t.Errorf("Expected no stderr output but got %q", msg)
	}
}

// func (enc *Encoding) EncodeToString(src []byte) string
// Tests calling a method on a global variable.
// Tests passing a filename as a []byte argument.
func TestBase64EncodeToStringFromFilename(t *testing.T) {
	t.Parallel()
	dir, err := ioutil.TempDir("", "")
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(dir)
	filename := filepath.Join(dir, "out.txt")
	if err := ioutil.WriteFile(filename, []byte("12345"), 0600); err != nil {
		t.Fatal(err)
	}

	stderr := &bytes.Buffer{}
	stdout := &bytes.Buffer{}
	env := OSEnv{
		Stderr: stderr,
		Stdout: stdout,
		Args:   []string{"gorram", "encoding/base64", "StdEncoding.EncodeToString", filename},
	}
	code := ParseAndRun(env)
	if code != 0 {
		t.Errorf("unexpected exit code: %v", code)
	}

	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	out := stdout.String()
	expected := "MTIzNDU=\n"
	if out != expected {
		t.Fatalf("Expected %q but got %q", expected, out)
	}
	if msg := stderr.String(); msg != "" {
		t.Errorf("Expected no stderr output but got %q", msg)
	}
}
<commit_msg>update some fatals to be errors so we get better output<commit_after>package cli

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"
)

func TestParseAndRun(t *testing.T) {
	t.Parallel()
	dir, err := ioutil.TempDir("", "")
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(dir)
	tests := []struct {
		args     []string
		expected string
	}{
		{args: []string{"math", "Sqrt", "25.4"}, expected: "5.039841267341661\n"},
	}
	for _, test := range tests {
		t.Run(strings.Join(test.args, " "), func(t *testing.T) {
			t.Parallel()
			stderr := &bytes.Buffer{}
			stdout := &bytes.Buffer{}
			env := OSEnv{
				Stderr: stderr,
				Stdout: stdout,
				Args:   append([]string{"gorram"}, test.args...),
			}
			code := ParseAndRun(env)
			if code != 0 {
				t.Errorf("unexpected exit code: %v", code)
			}
			out := stdout.String()
			if out != test.expected {
				t.Errorf("Expected %q but got %q", test.expected, out)
			}
			if msg := stderr.String(); msg != "" {
				t.Errorf("Expected no stderr output but got %q", msg)
			}
		})
	}
}

// func Now() Time
// tests zero arg Function.
// Tests printing of value with ToString method.
func TestTimeNow(t *testing.T) {
	t.Parallel()
	dir, err := ioutil.TempDir("", "")
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(dir)
	stderr := &bytes.Buffer{}
	stdout := &bytes.Buffer{}
	env := OSEnv{
		Stderr: stderr,
		Stdout: stdout,
		Args:   []string{"gorram", "time", "Now"},
	}
	code := ParseAndRun(env)
	if code != 0 {
		t.Errorf("unexpected exit code: %v", code)
	}
	out := stdout.String()
	expected := fmt.Sprint(time.Now()) + "\n"

	// have to fudge the output checking since obviously the two times will not
	// be exactly the same.  We do this by checking the prefix, up to the hours,
	// and the suffix for the time zone.
	if !strings.HasPrefix(out, expected[:15]) {
		t.Errorf("Expected ~%q but got %q", expected, out)
	}
	if !strings.HasSuffix(out, expected[len(expected)-9:]) {
		t.Errorf("Expected ~%q but got %q", expected, out)
	}
	if msg := stderr.String(); msg != "" {
		t.Errorf("Expected no stderr output but got %q", msg)
	}
}

// func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error
// Tests stdin to []byte argument.
// Tests a dst *bytes.Buffer with a []byte src.
// Tests string arguments.
func TestJsonIndentStdin(t *testing.T) {
	t.Parallel()
	dir, err := ioutil.TempDir("", "")
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(dir)
	stderr := &bytes.Buffer{}
	stdout := &bytes.Buffer{}
	env := OSEnv{
		Stderr: stderr,
		Stdout: stdout,
		Stdin:  strings.NewReader(`{"foo":"bar"}`),
		Args:   []string{"gorram", "encoding/json", "Indent", "", "  "},
	}
	code := ParseAndRun(env)
	if code != 0 {
		t.Errorf("unexpected exit code: %v", code)
	}
	out := stdout.String()
	expected := `
{
  "foo": "bar"
}
`[1:]
	if out != expected {
		t.Errorf("Expected %q but got %q", expected, out)
	}
	if msg := stderr.String(); msg != "" {
		t.Errorf("Expected no stderr output but got %q", msg)
	}
}

// func Get(url string) (resp *Response, err error)
// Tests a single string argument.
// Tests val, err return value.
// Tests struct return value that contains an io.Reader.
func TestNetHTTPGet(t *testing.T) {
	t.Parallel()
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "Hello, client")
	}))
	defer ts.Close()

	dir, err := ioutil.TempDir("", "")
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(dir)
	stderr := &bytes.Buffer{}
	stdout := &bytes.Buffer{}
	env := OSEnv{
		Stderr: stderr,
		Stdout: stdout,
		Args:   []string{"gorram", "net/http", "Get", ts.URL},
	}
	code := ParseAndRun(env)
	if code != 0 {
		t.Errorf("unexpected exit code: %v", code)
	}
	out := stdout.String()
	expected := "Hello, client\n\n"
	if out != expected {
		t.Errorf("Expected %q but got %q", expected, out)
	}
	if msg := stderr.String(); msg != "" {
		t.Errorf("Expected no stderr output but got %q", msg)
	}
}

// func Get(url string) (resp *Response, err error)
// Tests a single string argument.
// Tests val, err return value.
// Tests template output.
func TestNetHTTPGetWithTemplate(t *testing.T) {
	t.Parallel()
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "Hello, client")
	}))
	defer ts.Close()

	dir, err := ioutil.TempDir("", "")
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(dir)
	stderr := &bytes.Buffer{}
	stdout := &bytes.Buffer{}
	env := OSEnv{
		Stderr: stderr,
		Stdout: stdout,
		Args:   []string{"gorram", "-t", "{{.Status}}", "net/http", "Get", ts.URL},
	}
	code := ParseAndRun(env)
	if code != 0 {
		t.Errorf("unexpected exit code: %v", code)
	}

	out := stdout.String()
	expected := "200 OK\n"
	if out != expected {
		t.Errorf("Expected %q but got %q", expected, out)
	}
	if msg := stderr.String(); msg != "" {
		t.Errorf("Expected no stderr output but got %q", msg)
	}
}

// func Get(url string) (resp *Response, err error)
// Tests a single string argument.
// Tests val, err return value.
// Tests template output.
func TestNetHTTPGetWithFileTemplate(t *testing.T) {
	t.Parallel()
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "Hello, client")
	}))
	defer ts.Close()

	dir, err := ioutil.TempDir("", "")
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(dir)
	filename := filepath.Join(dir, "template.txt")
	if err := ioutil.WriteFile(filename, []byte("{{.Status}}"), 0600); err != nil {
		t.Fatal(err)
	}
	stderr := &bytes.Buffer{}
	stdout := &bytes.Buffer{}
	env := OSEnv{
		Stderr: stderr,
		Stdout: stdout,
		Args:   []string{"gorram", "-t", filename, "net/http", "Get", ts.URL},
	}
	code := ParseAndRun(env)
	if code != 0 {
		t.Errorf("unexpected exit code: %v", code)
	}

	out := stdout.String()
	expected := "200 OK\n"
	if out != expected {
		t.Errorf("Expected %q but got %q", expected, out)
	}
	if msg := stderr.String(); msg != "" {
		t.Errorf("Expected no stderr output but got %q", msg)
	}
}

// func (enc *Encoding) EncodeToString(src []byte) string
// Tests calling a method on a global variable.
// Tests passing a filename as a []byte argument.
func TestBase64EncodeToStringFromFilename(t *testing.T) {
	t.Parallel()
	dir, err := ioutil.TempDir("", "")
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(dir)
	filename := filepath.Join(dir, "out.txt")
	if err := ioutil.WriteFile(filename, []byte("12345"), 0600); err != nil {
		t.Fatal(err)
	}

	stderr := &bytes.Buffer{}
	stdout := &bytes.Buffer{}
	env := OSEnv{
		Stderr: stderr,
		Stdout: stdout,
		Args:   []string{"gorram", "encoding/base64", "StdEncoding.EncodeToString", filename},
	}
	code := ParseAndRun(env)
	if code != 0 {
		t.Errorf("unexpected exit code: %v", code)
	}

	out := stdout.String()
	expected := "MTIzNDU=\n"
	if out != expected {
		t.Errorf("Expected %q but got %q", expected, out)
	}
	if msg := stderr.String(); msg != "" {
		t.Errorf("Expected no stderr output but got %q", msg)
	}
}
<|endoftext|>