<commit_before>package operate

import (
	"context"
	"time"

	"github.com/go-errors/errors"
	"github.com/itchio/butler/buse"
	"github.com/itchio/butler/cmd/operate/harness"
	"github.com/itchio/go-itchio"
	"github.com/itchio/wharf/state"
)

func CheckUpdate(params *buse.CheckUpdateParams, consumer *state.Consumer, harness harness.Harness, ctx context.Context, conn Conn) (*buse.CheckUpdateResult, error) {
	res := &buse.CheckUpdateResult{}

	for _, item := range params.Items {
		ml := &memoryLogger{}
		update, err := checkUpdateItem(ml.Consumer(), item)
		if err != nil {
			res.Warnings = append(res.Warnings, err.Error())
			if se, ok := err.(*errors.Error); ok {
				consumer.Warnf("An update check failed: %s", se.ErrorStack())
			} else {
				consumer.Warnf("An update check failed: %s", err.Error())
			}
			consumer.Warnf("Log follows:")
			ml.Copy(consumer)
			consumer.Warnf("End of log")
		} else {
			if update != nil {
				res.Updates = append(res.Updates, update)
				err := conn.Notify(ctx, "GameUpdateAvailable", update)
				if err != nil {
					consumer.Warnf("Could not send GameUpdateAvailable notification: %s", err.Error())
				}
			}
		}
	}

	return res, nil
}

func checkUpdateItem(consumer *state.Consumer, item *buse.CheckUpdateItem) (*buse.GameUpdate, error) {
	// TODO: respect upload successors, use upcoming check-update endpoint

	if item.ItemID == "" {
		return nil, errors.New("missing itemId")
	}

	if item.Credentials == nil {
		return nil, errors.New("missing credentials")
	}

	if item.Game == nil {
		return nil, errors.New("missing game")
	}

	if item.Upload == nil {
		return nil, errors.New("missing upload")
	}

	consumer.Statf("Checking for updates to (%s)", GameToString(item.Game))
	consumer.Statf("Item ID (%s)", item.ItemID)
	consumer.Infof("→ Cached upload:")
	LogUpload(consumer, item.Upload, item.Build)

	if item.Credentials.DownloadKey > 0 {
		consumer.Infof("→ Has download key (game is owned)")
	} else {
		consumer.Infof("→ Searching without download key")
	}

	installedAt, err := buse.FromDateTime(item.InstalledAt)
	if err != nil {
		consumer.Warnf("Could not parse installedAt: %s", err.Error())
		installedAt = time.Unix(0, 0)
		consumer.Warnf("Assuming unix 0 time")
	}
	consumer.Infof("→ Last install operation at (%s)", installedAt)

	client, err := ClientFromCredentials(item.Credentials)
	if err != nil {
		return nil, errors.Wrap(err, 0)
	}

	listUploadsRes, err := client.ListGameUploads(&itchio.ListGameUploadsParams{
		GameID: item.Game.ID,
	})
	if err != nil {
		return nil, errors.Wrap(err, 0)
	}

	var freshUpload *itchio.Upload
	for _, u := range listUploadsRes.Uploads {
		if u.ID == item.Upload.ID {
			freshUpload = u
			break
		}
	}

	if freshUpload == nil {
		consumer.Infof("No update found (upload disappeared)")
		return nil, nil
	}

	consumer.Infof("→ Fresh upload:")
	LogUpload(consumer, freshUpload, freshUpload.Build)

	// non-wharf updates
	if item.Build == nil {
		consumer.Infof("We have no build installed, comparing timestamps...")
		if freshUpload.Build != nil {
			return nil, errors.New("We have no build installed but fresh upload has one. This shouldn't happen")
		}

		// TODO: don't do that, use the upload's hashes instead

		updatedAt, err := itchio.ParseDate(freshUpload.UpdatedAt)
		if err != nil {
			return nil, errors.Wrap(err, 0)
		}
		consumer.Infof("→ Upload updated at (%s)", installedAt)

		if updatedAt.After(installedAt) {
			consumer.Statf("↑ Upload was updated after last install, it's an update!")
			res := &buse.GameUpdate{
				ItemID: item.ItemID,
				Game:   item.Game,
				Upload: freshUpload,
				Build:  nil,
			}
			return res, nil
		}
		return nil, nil
	}

	// wharf updates
	{
		consumer.Infof("We have no build installed, comparing build numbers...")
		if freshUpload.Build == nil {
			return nil, errors.New("We have a build installed but fresh upload has none. This shouldn't happen")
		}

		if freshUpload.Build.ID > item.Build.ID {
			consumer.Statf("↑ A more recent build (#%d) than ours (#%d) is available, it's an update!",
				freshUpload.Build.ID,
				item.Build.ID,
			)
			res := &buse.GameUpdate{
				ItemID: item.ItemID,
				Game:   item.Game,
				Upload: freshUpload,
				Build:  freshUpload.Build,
			}
			return res, nil
		}
	}

	return nil, nil
}
<commit_msg>fix type of GameUpdateAvailable notification payload<commit_after>package operate

import (
	"context"
	"time"

	"github.com/go-errors/errors"
	"github.com/itchio/butler/buse"
	"github.com/itchio/butler/cmd/operate/harness"
	"github.com/itchio/go-itchio"
	"github.com/itchio/wharf/state"
)

func CheckUpdate(params *buse.CheckUpdateParams, consumer *state.Consumer, harness harness.Harness, ctx context.Context, conn Conn) (*buse.CheckUpdateResult, error) {
	res := &buse.CheckUpdateResult{}

	for _, item := range params.Items {
		ml := &memoryLogger{}
		update, err := checkUpdateItem(ml.Consumer(), item)
		if err != nil {
			res.Warnings = append(res.Warnings, err.Error())
			if se, ok := err.(*errors.Error); ok {
				consumer.Warnf("An update check failed: %s", se.ErrorStack())
			} else {
				consumer.Warnf("An update check failed: %s", err.Error())
			}
			consumer.Warnf("Log follows:")
			ml.Copy(consumer)
			consumer.Warnf("End of log")
		} else {
			if update != nil {
				res.Updates = append(res.Updates, update)
				err := conn.Notify(ctx, "GameUpdateAvailable", &buse.GameUpdateAvailableNotification{
					Update: update,
				})
				if err != nil {
					consumer.Warnf("Could not send GameUpdateAvailable notification: %s", err.Error())
				}
			}
		}
	}

	return res, nil
}

func checkUpdateItem(consumer *state.Consumer, item *buse.CheckUpdateItem) (*buse.GameUpdate, error) {
	// TODO: respect upload successors, use upcoming check-update endpoint

	if item.ItemID == "" {
		return nil, errors.New("missing itemId")
	}

	if item.Credentials == nil {
		return nil, errors.New("missing credentials")
	}

	if item.Game == nil {
		return nil, errors.New("missing game")
	}

	if item.Upload == nil {
		return nil, errors.New("missing upload")
	}

	consumer.Statf("Checking for updates to (%s)", GameToString(item.Game))
	consumer.Statf("Item ID (%s)", item.ItemID)
	consumer.Infof("→ Cached upload:")
	LogUpload(consumer, item.Upload, item.Build)

	if item.Credentials.DownloadKey > 0 {
		consumer.Infof("→ Has download key (game is owned)")
	} else {
		consumer.Infof("→ Searching without download key")
	}

	installedAt, err := buse.FromDateTime(item.InstalledAt)
	if err != nil {
		consumer.Warnf("Could not parse installedAt: %s", err.Error())
		installedAt = time.Unix(0, 0)
		consumer.Warnf("Assuming unix 0 time")
	}
	consumer.Infof("→ Last install operation at (%s)", installedAt)

	client, err := ClientFromCredentials(item.Credentials)
	if err != nil {
		return nil, errors.Wrap(err, 0)
	}

	listUploadsRes, err := client.ListGameUploads(&itchio.ListGameUploadsParams{
		GameID: item.Game.ID,
	})
	if err != nil {
		return nil, errors.Wrap(err, 0)
	}

	var freshUpload *itchio.Upload
	for _, u := range listUploadsRes.Uploads {
		if u.ID == item.Upload.ID {
			freshUpload = u
			break
		}
	}

	if freshUpload == nil {
		consumer.Infof("No update found (upload disappeared)")
		return nil, nil
	}

	consumer.Infof("→ Fresh upload:")
	LogUpload(consumer, freshUpload, freshUpload.Build)

	// non-wharf updates
	if item.Build == nil {
		consumer.Infof("We have no build installed, comparing timestamps...")
		if freshUpload.Build != nil {
			return nil, errors.New("We have no build installed but fresh upload has one. This shouldn't happen")
		}

		// TODO: don't do that, use the upload's hashes instead

		updatedAt, err := itchio.ParseDate(freshUpload.UpdatedAt)
		if err != nil {
			return nil, errors.Wrap(err, 0)
		}
		consumer.Infof("→ Upload updated at (%s)", installedAt)

		if updatedAt.After(installedAt) {
			consumer.Statf("↑ Upload was updated after last install, it's an update!")
			res := &buse.GameUpdate{
				ItemID: item.ItemID,
				Game:   item.Game,
				Upload: freshUpload,
				Build:  nil,
			}
			return res, nil
		}
		return nil, nil
	}

	// wharf updates
	{
		consumer.Infof("We have no build installed, comparing build numbers...")
		if freshUpload.Build == nil {
			return nil, errors.New("We have a build installed but fresh upload has none. This shouldn't happen")
		}

		if freshUpload.Build.ID > item.Build.ID {
			consumer.Statf("↑ A more recent build (#%d) than ours (#%d) is available, it's an update!",
				freshUpload.Build.ID,
				item.Build.ID,
			)
			res := &buse.GameUpdate{
				ItemID: item.ItemID,
				Game:   item.Game,
				Upload: freshUpload,
				Build:  freshUpload.Build,
			}
			return res, nil
		}
	}

	return nil, nil
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"sort"

	"github.com/restic/restic/internal/restic"
	"github.com/spf13/cobra"
)

var cmdSnapshots = &cobra.Command{
	Use:   "snapshots [snapshotID ...]",
	Short: "list all snapshots",
	Long: `
The "snapshots" command lists all snapshots stored in the repository.
`,
	DisableAutoGenTag: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		return runSnapshots(snapshotOptions, globalOptions, args)
	},
}

// SnapshotOptions bundles all options for the snapshots command.
type SnapshotOptions struct {
	Host  string
	Tags  restic.TagLists
	Paths []string
	Compact bool
}

var snapshotOptions SnapshotOptions

func init() {
	cmdRoot.AddCommand(cmdSnapshots)

	f := cmdSnapshots.Flags()
	f.StringVarP(&snapshotOptions.Host, "host", "H", "", "only consider snapshots for this `host`")
	f.Var(&snapshotOptions.Tags, "tag", "only consider snapshots which include this `taglist` (can be specified multiple times)")
	f.StringArrayVar(&snapshotOptions.Paths, "path", nil, "only consider snapshots for this `path` (can be specified multiple times)")
	f.BoolVarP(&snapshotOptions.Compact, "compact", "c", false, "use compact format")
}

func runSnapshots(opts SnapshotOptions, gopts GlobalOptions, args []string) error {
	repo, err := OpenRepository(gopts)
	if err != nil {
		return err
	}

	if !gopts.NoLock {
		lock, err := lockRepo(repo)
		defer unlockRepo(lock)
		if err != nil {
			return err
		}
	}

	ctx, cancel := context.WithCancel(gopts.ctx)
	defer cancel()

	var list restic.Snapshots
	for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, args) {
		list = append(list, sn)
	}
	sort.Sort(sort.Reverse(list))

	if gopts.JSON {
		err := printSnapshotsJSON(gopts.stdout, list)
		if err != nil {
			Warnf("error printing snapshot: %v\n", err)
		}
		return nil
	}
	PrintSnapshots(gopts.stdout, list, opts.Compact)

	return nil
}

// PrintSnapshots prints a text table of the snapshots in list to stdout.
func PrintSnapshots(stdout io.Writer, list restic.Snapshots, compact bool) {

	// Determine the max widths for host and tag.
	maxHost, maxTag := 10, 6
	for _, sn := range list {
		if len(sn.Hostname) > maxHost {
			maxHost = len(sn.Hostname)
		}
		for _, tag := range sn.Tags {
			if len(tag) > maxTag {
				maxTag = len(tag)
			}
		}
	}

	tab := NewTable()
	if !compact {
		tab.Header = fmt.Sprintf("%-8s  %-19s  %-*s  %-*s  %-3s %s", "ID", "Date", -maxHost, "Host", -maxTag, "Tags", "", "Directory")
		tab.RowFormat = fmt.Sprintf("%%-8s  %%-19s  %%%ds  %%%ds  %%-3s %%s", -maxHost, -maxTag)
	} else {
		tab.Header = fmt.Sprintf("%-8s  %-19s  %-*s  %-*s", "ID", "Date", -maxHost, "Host", -maxTag, "Tags")
		tab.RowFormat = fmt.Sprintf("%%-8s  %%-19s  %%%ds  %%s", -maxHost)
	}

	for _, sn := range list {
		if len(sn.Paths) == 0 {
			continue
		}

		firstTag := ""
		if len(sn.Tags) > 0 {
			firstTag = sn.Tags[0]
		}

		rows := len(sn.Paths)
		if rows < len(sn.Tags) {
			rows = len(sn.Tags)
		}

		treeElement := "   "
		if rows != 1 {
			treeElement = "┌──"
		}

		if !compact {
			tab.Rows = append(tab.Rows, []interface{}{sn.ID().Str(), sn.Time.Format(TimeFormat), sn.Hostname, firstTag, treeElement, sn.Paths[0]})
		} else {
			allTags := ""
			for _, tag := range sn.Tags {
				allTags += tag + " "
			}
			tab.Rows = append(tab.Rows, []interface{}{sn.ID().Str(), sn.Time.Format(TimeFormat), sn.Hostname, allTags})
			continue
		}

		if len(sn.Tags) > rows {
			rows = len(sn.Tags)
		}

		for i := 1; i < rows; i++ {
			path := ""
			if len(sn.Paths) > i {
				path = sn.Paths[i]
			}

			tag := ""
			if len(sn.Tags) > i {
				tag = sn.Tags[i]
			}

			treeElement := "│"
			if i == (rows - 1) {
				treeElement = "└──"
			}

			tab.Rows = append(tab.Rows, []interface{}{"", "", "", tag, treeElement, path})
		}
	}

	tab.Write(stdout)
}

// Snapshot helps to print Snaphots as JSON with their ID included.
type Snapshot struct {
	*restic.Snapshot

	ID *restic.ID `json:"id"`
}

// printSnapshotsJSON writes the JSON representation of list to stdout.
func printSnapshotsJSON(stdout io.Writer, list restic.Snapshots) error {

	var snapshots []Snapshot

	for _, sn := range list {

		k := Snapshot{
			Snapshot: sn,
			ID:       sn.ID(),
		}
		snapshots = append(snapshots, k)
	}

	return json.NewEncoder(stdout).Encode(snapshots)
}
<commit_msg>Run changes through gofmt<commit_after>package main

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"sort"

	"github.com/restic/restic/internal/restic"
	"github.com/spf13/cobra"
)

var cmdSnapshots = &cobra.Command{
	Use:   "snapshots [snapshotID ...]",
	Short: "list all snapshots",
	Long: `
The "snapshots" command lists all snapshots stored in the repository.
`,
	DisableAutoGenTag: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		return runSnapshots(snapshotOptions, globalOptions, args)
	},
}

// SnapshotOptions bundles all options for the snapshots command.
type SnapshotOptions struct {
	Host    string
	Tags    restic.TagLists
	Paths   []string
	Compact bool
}

var snapshotOptions SnapshotOptions

func init() {
	cmdRoot.AddCommand(cmdSnapshots)

	f := cmdSnapshots.Flags()
	f.StringVarP(&snapshotOptions.Host, "host", "H", "", "only consider snapshots for this `host`")
	f.Var(&snapshotOptions.Tags, "tag", "only consider snapshots which include this `taglist` (can be specified multiple times)")
	f.StringArrayVar(&snapshotOptions.Paths, "path", nil, "only consider snapshots for this `path` (can be specified multiple times)")
	f.BoolVarP(&snapshotOptions.Compact, "compact", "c", false, "use compact format")
}

func runSnapshots(opts SnapshotOptions, gopts GlobalOptions, args []string) error {
	repo, err := OpenRepository(gopts)
	if err != nil {
		return err
	}

	if !gopts.NoLock {
		lock, err := lockRepo(repo)
		defer unlockRepo(lock)
		if err != nil {
			return err
		}
	}

	ctx, cancel := context.WithCancel(gopts.ctx)
	defer cancel()

	var list restic.Snapshots
	for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, args) {
		list = append(list, sn)
	}
	sort.Sort(sort.Reverse(list))

	if gopts.JSON {
		err := printSnapshotsJSON(gopts.stdout, list)
		if err != nil {
			Warnf("error printing snapshot: %v\n", err)
		}
		return nil
	}
	PrintSnapshots(gopts.stdout, list, opts.Compact)

	return nil
}

// PrintSnapshots prints a text table of the snapshots in list to stdout.
func PrintSnapshots(stdout io.Writer, list restic.Snapshots, compact bool) {

	// Determine the max widths for host and tag.
	maxHost, maxTag := 10, 6
	for _, sn := range list {
		if len(sn.Hostname) > maxHost {
			maxHost = len(sn.Hostname)
		}
		for _, tag := range sn.Tags {
			if len(tag) > maxTag {
				maxTag = len(tag)
			}
		}
	}

	tab := NewTable()
	if !compact {
		tab.Header = fmt.Sprintf("%-8s  %-19s  %-*s  %-*s  %-3s %s", "ID", "Date", -maxHost, "Host", -maxTag, "Tags", "", "Directory")
		tab.RowFormat = fmt.Sprintf("%%-8s  %%-19s  %%%ds  %%%ds  %%-3s %%s", -maxHost, -maxTag)
	} else {
		tab.Header = fmt.Sprintf("%-8s  %-19s  %-*s  %-*s", "ID", "Date", -maxHost, "Host", -maxTag, "Tags")
		tab.RowFormat = fmt.Sprintf("%%-8s  %%-19s  %%%ds  %%s", -maxHost)
	}

	for _, sn := range list {
		if len(sn.Paths) == 0 {
			continue
		}

		firstTag := ""
		if len(sn.Tags) > 0 {
			firstTag = sn.Tags[0]
		}

		rows := len(sn.Paths)
		if rows < len(sn.Tags) {
			rows = len(sn.Tags)
		}

		treeElement := "   "
		if rows != 1 {
			treeElement = "┌──"
		}

		if !compact {
			tab.Rows = append(tab.Rows, []interface{}{sn.ID().Str(), sn.Time.Format(TimeFormat), sn.Hostname, firstTag, treeElement, sn.Paths[0]})
		} else {
			allTags := ""
			for _, tag := range sn.Tags {
				allTags += tag + " "
			}
			tab.Rows = append(tab.Rows, []interface{}{sn.ID().Str(), sn.Time.Format(TimeFormat), sn.Hostname, allTags})
			continue
		}

		if len(sn.Tags) > rows {
			rows = len(sn.Tags)
		}

		for i := 1; i < rows; i++ {
			path := ""
			if len(sn.Paths) > i {
				path = sn.Paths[i]
			}

			tag := ""
			if len(sn.Tags) > i {
				tag = sn.Tags[i]
			}

			treeElement := "│"
			if i == (rows - 1) {
				treeElement = "└──"
			}

			tab.Rows = append(tab.Rows, []interface{}{"", "", "", tag, treeElement, path})
		}
	}

	tab.Write(stdout)
}

// Snapshot helps to print Snaphots as JSON with their ID included.
type Snapshot struct {
	*restic.Snapshot

	ID *restic.ID `json:"id"`
}

// printSnapshotsJSON writes the JSON representation of list to stdout.
func printSnapshotsJSON(stdout io.Writer, list restic.Snapshots) error {

	var snapshots []Snapshot

	for _, sn := range list {

		k := Snapshot{
			Snapshot: sn,
			ID:       sn.ID(),
		}
		snapshots = append(snapshots, k)
	}

	return json.NewEncoder(stdout).Encode(snapshots)
}
<|endoftext|>


<commit_before>package io

import (
	"fmt"
	"math"
	"strconv"
	"strings"

	artio "github.com/phil-mansfield/go-artio"
	"github.com/phil-mansfield/shellfish/cosmo"
)

const (
	// emulateHubble is used for debugging purposes. I've never had access to
	// a cosmological simulation, so this is necessary. Don't worry: even if
	// this flag is set, an error will still be returned if called on invalid
	// header contents. It will just occur late enough to allow for illustrative
	// logging.
	emulateHubble = true
)

type ARTIOBuffer struct {
	open    bool
	xsBuf   [][3]float32
	msBuf   []float32
	xsBufs  [][][3]float32
	msBufs  [][]float32
	sMasses []float32
	sFlags  []bool // True if the species is "N-BODY" type.
	fileset, exFilename string
}

func NewARTIOBuffer(filename string) (VectorBuffer, error) {
	fileset, _, err := parseARTIOFilename(filename)
	if err != nil {
		return nil, err
	}

	h, err := artio.FilesetOpen(fileset, artio.OpenHeader, artio.NullContext)
	if err != nil {
		return nil, err
	}
	defer h.Close()

	numSpecies := h.GetInt(h.Key("num_particle_species"))[0]
	sMasses := h.GetFloat(h.Key("particle_species_mass"))

	var h100 float64
	if !h.HasKey("hubble") {
		if emulateHubble {
			h100 = 0.7
		} else {
			return nil, fmt.Errorf(
				"ARTIO header does not contain 'hubble' field.",
			)
		}
	} else {
		h100 = h.GetDouble(h.Key("hubble"))[0]
	}
	massUnit := (h100 / cosmo.MSunMks * 1000) *
		h.GetDouble(h.Key("mass_unit"))[0]
	for i := range sMasses {
		sMasses[i] *= float32(massUnit)
	}

	sFlags, err := nBodyFlags(h, fileset)
	if err != nil {
		return nil, err
	}

	return &ARTIOBuffer{
		xsBufs:  make([][][3]float32, numSpecies),
		msBufs: make([][]float32, numSpecies),
		sMasses: sMasses,
		sFlags:  sFlags,
		fileset: fileset,
		exFilename: filename,
	}, nil
}

func parseARTIOFilename(fname string) (fileset string, block int, err error) {
	split := strings.LastIndex(fname, ".")
	if split == -1 || split == len(fname)-1 {
		return "", -1, fmt.Errorf(
			"'%s' is not the name of an ARTIO block.", fname,
		)
	}

	fileset, blockString := fname[:split], fname[split+1:]
	block, err = strconv.Atoi(strings.Trim(blockString, "p"))
	if err != nil {
		return "", -1, fmt.Errorf(
			"'%s' is not the name of an ARTIO block.", fname,
		)
	}

	return fileset, block, nil
}

func (buf *ARTIOBuffer) Read(
	filename string,
) ([][3]float32, []float32, error) {
	// Open the file.
	if buf.open {
		panic("Buffer already open.")
	}
	buf.open = true

	h, err := artio.FilesetOpen(
		buf.fileset, artio.OpenHeader, artio.NullContext,
	)
	if err != nil {
		return nil, nil, err
	}
	defer h.Close()

	// I'm not sure if this can just be replaced with putting an
	// artio.OpenParticles flag in artio.FilesetOpen(). Someone with more
	// knowledge about ARTIO than me should figure this out.
	err = h.OpenParticles()
	if err != nil {
		return nil, nil, err
	}

	// Flag N_BODY particles.
	flags, err := nBodyFlags(h, buf.fileset)
	if err != nil {
		return nil, nil, err
	}

	// Get SFC range.
	_, fIdx, err := parseARTIOFilename(filename)
	if err != nil { return nil, nil, err }
	fileIdxs := h.GetLong(h.Key("particle_file_sfc_index"))
	sfcStart, sfcEnd := fileIdxs[fIdx], fileIdxs[fIdx+1]-1

	// Counts and buffer manipulation. Do the reading.
	sCounts, err := h.CountInRange(sfcStart, sfcEnd)
	totCount := int64(0)

	for i := range sCounts {
		if flags[i] {
			totCount += sCounts[i]
			buf.xsBufs[i] = expandVectors(buf.xsBufs[i][:0], int(sCounts[i]))
			err = h.GetPositionsAt(i, sfcStart, sfcEnd, buf.xsBufs[i])
			if err != nil {
				return nil, nil, err
			}

			buf.msBufs[i] = expandScalars(buf.msBufs[i][:0], int(sCounts[i]))
			for j := range buf.msBufs[i] {
				buf.msBufs[i][j] = buf.sMasses[i]
			}
		}
	}

	// Copy to output buffer.
	buf.xsBuf = expandVectors(buf.xsBuf[:0], int(totCount))
	buf.msBuf = expandScalars(buf.msBuf[:0], int(totCount))
	k := 0
	for j := range buf.xsBufs {
		for i := range buf.xsBufs[j] {
			buf.xsBuf[k] = buf.xsBufs[j][i]
			buf.msBuf[k] = buf.msBufs[j][i]
			k++
		}
	}

	var h100 float32
	if !h.HasKey("hubble") {
		if emulateHubble {
			h100 = 0.7
		} else {
			return nil, nil, fmt.Errorf(
				"ARTIO header does not contain 'hubble' field.",
			)
		}
	} else {
		h100 = float32(h.GetDouble(h.Key("hubble"))[0])
	}

	lengthUnit := float32(h100) / (cosmo.MpcMks * 100) *
		float32(h.GetDouble(h.Key("length_unit"))[0])
	for i := range buf.xsBuf {
		buf.xsBuf[i][0] *= lengthUnit
		buf.xsBuf[i][1] *= lengthUnit
		buf.xsBuf[i][2] *= lengthUnit
	}

	return buf.xsBuf, buf.msBuf, nil
}

func nBodyFlags(h artio.Fileset, fname string) ([]bool, error) {
	speciesLabels := h.GetString(h.Key("particle_species_labels"))
	isNBody, nBodyCount := make([]bool, len(speciesLabels)), 0
	for i := range isNBody {
		isNBody[i] = speciesLabels[i] == "N-BODY"
		nBodyCount++
	}
	if nBodyCount == 0 {
		return nil, fmt.Errorf("ARTIO fileset '%s' does not contain any "+
			"particle species of type 'N-BODY'.", fname)
	}
	return isNBody, nil
}

func (buf *ARTIOBuffer) Close() {
	if !buf.open {
		panic("Buffer not open.")
	}
	buf.open = false
}

func (buf *ARTIOBuffer) IsOpen() bool {
	return buf.open
}

func (buf *ARTIOBuffer) ReadHeader(fileNumStr string, out *Header) error {
	xs, _, err := buf.Read(fileNumStr)
	defer buf.Close()

	h, err := artio.FilesetOpen(
		buf.fileset, artio.OpenHeader, artio.NullContext,
	)
	if err != nil {
		return err
	}
	defer h.Close()

	var h100 float64
	if !h.HasKey("hubble") {
		if emulateHubble {
			h100 = 0.7
		} else {
			return fmt.Errorf(
				"ARTIO header does not contain 'hubble' field.",
			)
		}
	} else {
		h100 = h.GetDouble(h.Key("hubble"))[0]
	}

	out.TotalWidth = h.GetDouble(h.Key("box_size"))[0] *
		(h100 / (cosmo.MpcMks * 100))
	out.Origin, out.Width = boundingBox(xs, out.TotalWidth)
	out.N = int64(len(xs))

	min, max := xs[0], xs[0]
	for i := range xs {
		for j := 0; j < 3; j++ {
			if xs[i][j] < min[j] { min[j] = xs[i][j] }
			if xs[i][j] > max[j] { max[j] = xs[i][j] }
		}
	}

	switch {
	case !h.HasKey("auni"):
		return fmt.Errorf("ARTIO header does not contain 'auni' field.")
	case !h.HasKey("OmegaM"):
		return fmt.Errorf("ARTIO header does not contain 'OmegaM' field.")
	case !h.HasKey("OmegaL"):
		return fmt.Errorf("ARTIO header does not contain 'OmegaL' field.")

	}

	out.Cosmo.Z = 1/h.GetDouble(h.Key("auni"))[0] - 1
	out.Cosmo.OmegaM = h.GetDouble(h.Key("OmegaM"))[0]
	out.Cosmo.OmegaL = h.GetDouble(h.Key("OmegaL"))[0]
	out.Cosmo.H100 = h.GetDouble(h.Key("hubble"))[0]

	if out.Cosmo.H100 > 10 {
		panic("Oops, Phil misunderstood the meaning of an ARTIO field. " +
			"Please submit an issue.")
	}

	return nil
}

func (buf *ARTIOBuffer) MinMass() float32 {
	minMass := float32(math.Inf(+1))
	for i := range buf.sMasses {
		if buf.sFlags[i] && buf.sMasses[i] < minMass {
			minMass = buf.sMasses[i]
		}
	}
	return minMass
}
<commit_msg>Fixed units bug in ARTIO Header reader.<commit_after>package io

import (
	"fmt"
	"math"
	"strconv"
	"strings"

	artio "github.com/phil-mansfield/go-artio"
	"github.com/phil-mansfield/shellfish/cosmo"
)

const (
	// emulateHubble is used for debugging purposes. I've never had access to
	// a cosmological simulation, so this is necessary. Don't worry: even if
	// this flag is set, an error will still be returned if called on invalid
	// header contents. It will just occur late enough to allow for illustrative
	// logging.
	emulateHubble = true
)

type ARTIOBuffer struct {
	open    bool
	xsBuf   [][3]float32
	msBuf   []float32
	xsBufs  [][][3]float32
	msBufs  [][]float32
	sMasses []float32
	sFlags  []bool // True if the species is "N-BODY" type.
	fileset, exFilename string
}

func NewARTIOBuffer(filename string) (VectorBuffer, error) {
	fileset, _, err := parseARTIOFilename(filename)
	if err != nil {
		return nil, err
	}

	h, err := artio.FilesetOpen(fileset, artio.OpenHeader, artio.NullContext)
	if err != nil {
		return nil, err
	}
	defer h.Close()

	numSpecies := h.GetInt(h.Key("num_particle_species"))[0]
	sMasses := h.GetFloat(h.Key("particle_species_mass"))

	var h100 float64
	if !h.HasKey("hubble") {
		if emulateHubble {
			h100 = 0.7
		} else {
			return nil, fmt.Errorf(
				"ARTIO header does not contain 'hubble' field.",
			)
		}
	} else {
		h100 = h.GetDouble(h.Key("hubble"))[0]
	}
	massUnit := (h100 / (cosmo.MSunMks * 1000)) *
		h.GetDouble(h.Key("mass_unit"))[0]
	for i := range sMasses {
		sMasses[i] *= float32(massUnit)
	}

	sFlags, err := nBodyFlags(h, fileset)
	if err != nil {
		return nil, err
	}

	return &ARTIOBuffer{
		xsBufs:  make([][][3]float32, numSpecies),
		msBufs: make([][]float32, numSpecies),
		sMasses: sMasses,
		sFlags:  sFlags,
		fileset: fileset,
		exFilename: filename,
	}, nil
}

func parseARTIOFilename(fname string) (fileset string, block int, err error) {
	split := strings.LastIndex(fname, ".")
	if split == -1 || split == len(fname)-1 {
		return "", -1, fmt.Errorf(
			"'%s' is not the name of an ARTIO block.", fname,
		)
	}

	fileset, blockString := fname[:split], fname[split+1:]
	block, err = strconv.Atoi(strings.Trim(blockString, "p"))
	if err != nil {
		return "", -1, fmt.Errorf(
			"'%s' is not the name of an ARTIO block.", fname,
		)
	}

	return fileset, block, nil
}

func (buf *ARTIOBuffer) Read(
	filename string,
) ([][3]float32, []float32, error) {
	// Open the file.
	if buf.open {
		panic("Buffer already open.")
	}
	buf.open = true

	h, err := artio.FilesetOpen(
		buf.fileset, artio.OpenHeader, artio.NullContext,
	)
	if err != nil {
		return nil, nil, err
	}
	defer h.Close()

	// I'm not sure if this can just be replaced with putting an
	// artio.OpenParticles flag in artio.FilesetOpen(). Someone with more
	// knowledge about ARTIO than me should figure this out.
	err = h.OpenParticles()
	if err != nil {
		return nil, nil, err
	}

	// Flag N_BODY particles.
	flags, err := nBodyFlags(h, buf.fileset)
	if err != nil {
		return nil, nil, err
	}

	// Get SFC range.
	_, fIdx, err := parseARTIOFilename(filename)
	if err != nil { return nil, nil, err }
	fileIdxs := h.GetLong(h.Key("particle_file_sfc_index"))
	sfcStart, sfcEnd := fileIdxs[fIdx], fileIdxs[fIdx+1]-1

	// Counts and buffer manipulation. Do the reading.
	sCounts, err := h.CountInRange(sfcStart, sfcEnd)
	totCount := int64(0)

	for i := range sCounts {
		if flags[i] {
			totCount += sCounts[i]
			buf.xsBufs[i] = expandVectors(buf.xsBufs[i][:0], int(sCounts[i]))
			err = h.GetPositionsAt(i, sfcStart, sfcEnd, buf.xsBufs[i])
			if err != nil {
				return nil, nil, err
			}

			buf.msBufs[i] = expandScalars(buf.msBufs[i][:0], int(sCounts[i]))
			for j := range buf.msBufs[i] {
				buf.msBufs[i][j] = buf.sMasses[i]
			}
		}
	}

	// Copy to output buffer.
	buf.xsBuf = expandVectors(buf.xsBuf[:0], int(totCount))
	buf.msBuf = expandScalars(buf.msBuf[:0], int(totCount))
	k := 0
	for j := range buf.xsBufs {
		for i := range buf.xsBufs[j] {
			buf.xsBuf[k] = buf.xsBufs[j][i]
			buf.msBuf[k] = buf.msBufs[j][i]
			k++
		}
	}

	var h100 float32
	if !h.HasKey("hubble") {
		if emulateHubble {
			h100 = 0.7
		} else {
			return nil, nil, fmt.Errorf(
				"ARTIO header does not contain 'hubble' field.",
			)
		}
	} else {
		h100 = float32(h.GetDouble(h.Key("hubble"))[0])
	}

	lengthUnit := float32(h100) / (cosmo.MpcMks * 100) *
		float32(h.GetDouble(h.Key("length_unit"))[0])
	for i := range buf.xsBuf {
		buf.xsBuf[i][0] *= lengthUnit
		buf.xsBuf[i][1] *= lengthUnit
		buf.xsBuf[i][2] *= lengthUnit
	}

	return buf.xsBuf, buf.msBuf, nil
}

func nBodyFlags(h artio.Fileset, fname string) ([]bool, error) {
	speciesLabels := h.GetString(h.Key("particle_species_labels"))
	isNBody, nBodyCount := make([]bool, len(speciesLabels)), 0
	for i := range isNBody {
		isNBody[i] = speciesLabels[i] == "N-BODY"
		nBodyCount++
	}
	if nBodyCount == 0 {
		return nil, fmt.Errorf("ARTIO fileset '%s' does not contain any "+
			"particle species of type 'N-BODY'.", fname)
	}
	return isNBody, nil
}

func (buf *ARTIOBuffer) Close() {
	if !buf.open {
		panic("Buffer not open.")
	}
	buf.open = false
}

func (buf *ARTIOBuffer) IsOpen() bool {
	return buf.open
}

func (buf *ARTIOBuffer) ReadHeader(fileNumStr string, out *Header) error {
	xs, _, err := buf.Read(fileNumStr)
	defer buf.Close()

	h, err := artio.FilesetOpen(
		buf.fileset, artio.OpenHeader, artio.NullContext,
	)
	if err != nil {
		return err
	}
	defer h.Close()

	var h100 float64
	if !h.HasKey("hubble") {
		if emulateHubble {
			h100 = 0.7
		} else {
			return fmt.Errorf(
				"ARTIO header does not contain 'hubble' field.",
			)
		}
	} else {
		h100 = h.GetDouble(h.Key("hubble"))[0]
	}

	out.TotalWidth = h.GetDouble(h.Key("box_size"))[0] *
		(h100 / (cosmo.MpcMks * 100))
	out.Origin, out.Width = boundingBox(xs, out.TotalWidth)
	out.N = int64(len(xs))

	min, max := xs[0], xs[0]
	for i := range xs {
		for j := 0; j < 3; j++ {
			if xs[i][j] < min[j] { min[j] = xs[i][j] }
			if xs[i][j] > max[j] { max[j] = xs[i][j] }
		}
	}

	switch {
	case !h.HasKey("auni"):
		return fmt.Errorf("ARTIO header does not contain 'auni' field.")
	case !h.HasKey("OmegaM"):
		return fmt.Errorf("ARTIO header does not contain 'OmegaM' field.")
	case !h.HasKey("OmegaL"):
		return fmt.Errorf("ARTIO header does not contain 'OmegaL' field.")

	}

	out.Cosmo.Z = 1/h.GetDouble(h.Key("auni"))[0] - 1
	out.Cosmo.OmegaM = h.GetDouble(h.Key("OmegaM"))[0]
	out.Cosmo.OmegaL = h.GetDouble(h.Key("OmegaL"))[0]
	out.Cosmo.H100 = h.GetDouble(h.Key("hubble"))[0]

	if out.Cosmo.H100 > 10 {
		panic("Oops, Phil misunderstood the meaning of an ARTIO field. " +
			"Please submit an issue.")
	}

	return nil
}

func (buf *ARTIOBuffer) MinMass() float32 {
	minMass := float32(math.Inf(+1))
	for i := range buf.sMasses {
		if buf.sFlags[i] && buf.sMasses[i] < minMass {
			minMass = buf.sMasses[i]
		}
	}
	return minMass
}
<|endoftext|>


<commit_before>// Copyright (c) Alex Ellis 2017. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

package commands

import (
	"archive/zip"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/openfaas/faas-cli/proxy"
)

const (
	defaultTemplateRepository = "https://github.com/openfaas/faas-cli"
	templateDirectory         = "./template/"
	rootLanguageDirSplitCount = 3
)

type ExtractAction int

const (
	ShouldExtractData ExtractAction = iota
	NewTemplateFound
	DirectoryAlreadyExists
	SkipWritingData
)

// fetchTemplates fetch code templates from GitHub master zip file.
func fetchTemplates(templateURL string, overwrite bool) error {

	if len(templateURL) == 0 {
		templateURL = defaultTemplateRepository
	}

	archive, err := fetchMasterZip(templateURL)
	if err != nil {
		removeArchive(archive)
		return err
	}

	log.Printf("Attempting to expand templates from %s\n", archive)

	preExistingLanguages, fetchedLanguages, err := expandTemplatesFromZip(archive, overwrite)
	if err != nil {
		return err
	}

	if len(preExistingLanguages) > 0 {
		log.Printf("Cannot overwrite the following %d directories: %v\n", len(preExistingLanguages), preExistingLanguages)
	}

	log.Printf("Fetched %d template(s) : %v from %s\n", len(fetchedLanguages), fetchedLanguages, templateURL)

	err = removeArchive(archive)

	return err
}

// expandTemplatesFromZip() takes a path to an archive, and whether or not
// we are allowed to overwrite pre-existing language templates. It returns
// a list of languages that already exist (could not be overwritten), and
// a list of languages that are newly downloaded.
func expandTemplatesFromZip(archive string, overwrite bool) ([]string, []string, error) {
	var existingLanguages []string
	var fetchedLanguages []string
	availableLanguages := make(map[string]bool)

	zipFile, err := zip.OpenReader(archive)
	if err != nil {
		return nil, nil, err
	}

	for _, z := range zipFile.File {
		var rc io.ReadCloser

		relativePath := z.Name[strings.Index(z.Name, "/")+1:]
		if strings.Index(relativePath, "template/") != 0 {
			// Process only directories inside "template" at root
			continue
		}

		action, language, isDirectory := canExpandTemplateData(availableLanguages, relativePath)

		var expandFromZip bool

		switch action {

		case ShouldExtractData:
			expandFromZip = true
		case NewTemplateFound:
			expandFromZip = true
			fetchedLanguages = append(fetchedLanguages, language)
		case DirectoryAlreadyExists:
			expandFromZip = false
			existingLanguages = append(existingLanguages, language)
		case SkipWritingData:
			expandFromZip = false
		default:
			return nil, nil, fmt.Errorf(fmt.Sprintf("don't know what to do when extracting zip: %s", archive))

		}

		if expandFromZip {
			if rc, err = z.Open(); err != nil {
				break
			}

			if err = createPath(relativePath, z.Mode()); err != nil {
				break
			}

			// If relativePath is just a directory, then skip expanding it.
			if len(relativePath) > 1 && !isDirectory {
				if err = writeFile(rc, z.UncompressedSize64, relativePath, z.Mode()); err != nil {
					return nil, nil, err
				}
			}
		}
	}

	zipFile.Close()
	return existingLanguages, fetchedLanguages, nil
}

// canExpandTemplateData() takes the map of available languages, and the
// path to a file in the zip archive. Returns what we should do with the file
// in form of ExtractAction enum, the language name, and whether it is a directory
func canExpandTemplateData(availableLanguages map[string]bool, relativePath string) (ExtractAction, string, bool) {
	if pathSplit := strings.Split(relativePath, "/"); len(pathSplit) > 2 {
		language := pathSplit[1]

		// We know that this path is a directory if the last character is a "/"
		isDirectory := strings.HasSuffix(relativePath, "/")

		// Check if this is the root directory for a language (at ./template/lang)
		if len(pathSplit) == rootLanguageDirSplitCount && isDirectory {
			if !canWriteLanguage(availableLanguages, language, overwrite) {
				return DirectoryAlreadyExists, language, isDirectory
			}
			return NewTemplateFound, language, isDirectory
		}

		if canWriteLanguage(availableLanguages, language, overwrite) == false {
			return SkipWritingData, language, isDirectory
		}

		return ShouldExtractData, language, isDirectory
	}
	// template/
	return SkipWritingData, "", true
}

// removeArchive removes the given file
func removeArchive(archive string) error {
	log.Printf("Cleaning up zip file...")
	var err error

	if _, err = os.Stat(archive); err == nil {
		err = os.Remove(archive)
	}

	return err
}

// fetchMasterZip downloads a zip file from a repository URL
func fetchMasterZip(templateURL string) (string, error) {
	var err error

	templateURL = strings.TrimRight(templateURL, "/")
	templateURL = templateURL + "/archive/master.zip"
	archive := "master.zip"

	if _, err := os.Stat(archive); err != nil {
		timeout := 120 * time.Second
		client := proxy.MakeHTTPClient(&timeout)

		req, err := http.NewRequest(http.MethodGet, templateURL, nil)
		if err != nil {
			log.Println(err.Error())
			return "", err
		}
		log.Printf("HTTP GET %s\n", templateURL)
		res, err := client.Do(req)
		if err != nil {
			log.Println(err.Error())
			return "", err
		}
		if res.StatusCode != http.StatusOK {
			err := fmt.Errorf(fmt.Sprintf("%s is not valid, status code %d", templateURL, res.StatusCode))
			log.Println(err.Error())
			return "", err
		}
		if res.Body != nil {
			defer res.Body.Close()
		}
		bytesOut, err := ioutil.ReadAll(res.Body)
		if err != nil {
			log.Println(err.Error())
			return "", err
		}

		log.Printf("Writing %dKb to %s\n", len(bytesOut)/1024, archive)
		err = ioutil.WriteFile(archive, bytesOut, 0700)
		if err != nil {
			log.Println(err.Error())
			return "", err
		}
	}
	fmt.Println("")
	return archive, err
}

func writeFile(rc io.ReadCloser, size uint64, relativePath string, perms os.FileMode) error {
	var err error

	defer rc.Close()
	f, err := os.OpenFile(relativePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perms)
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = io.CopyN(f, rc, int64(size))

	return err
}

func createPath(relativePath string, perms os.FileMode) error {
	dir := filepath.Dir(relativePath)
	err := os.MkdirAll(dir, perms)
	return err
}

// canWriteLanguage() tells whether the language can be expanded from the zip or not.
// availableLanguages map keeps track of which languages we know to be okay to copy.
// overwrite flag will allow to force copy the language template
func canWriteLanguage(availableLanguages map[string]bool, language string, overwrite bool) bool {
	canWrite := false
	if availableLanguages != nil && len(language) > 0 {
		if _, found := availableLanguages[language]; found {
			return availableLanguages[language]
		}
		canWrite = templateFolderExists(language, overwrite)
		availableLanguages[language] = canWrite
	}

	return canWrite
}

// Takes a language input (e.g. "node"), tells whether or not it is OK to download
func templateFolderExists(language string, overwrite bool) bool {
	dir := templateDirectory + language
	if _, err := os.Stat(dir); err == nil && !overwrite {
		// The directory template/language/ exists
		return false
	}
	return true
}
<commit_msg>Hide exported packages<commit_after>// Copyright (c) Alex Ellis 2017. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

package commands

import (
	"archive/zip"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/openfaas/faas-cli/proxy"
)

const (
	defaultTemplateRepository = "https://github.com/openfaas/faas-cli"
	templateDirectory         = "./template/"
	rootLanguageDirSplitCount = 3
)

type extractAction int

const (
	shouldExtractData extractAction = iota
	newTemplateFound
	directoryAlreadyExists
	skipWritingData
)

// fetchTemplates fetch code templates from GitHub master zip file.
func fetchTemplates(templateURL string, overwrite bool) error {

	if len(templateURL) == 0 {
		templateURL = defaultTemplateRepository
	}

	archive, err := fetchMasterZip(templateURL)
	if err != nil {
		removeArchive(archive)
		return err
	}

	log.Printf("Attempting to expand templates from %s\n", archive)

	preExistingLanguages, fetchedLanguages, err := expandTemplatesFromZip(archive, overwrite)
	if err != nil {
		return err
	}

	if len(preExistingLanguages) > 0 {
		log.Printf("Cannot overwrite the following %d directories: %v\n", len(preExistingLanguages), preExistingLanguages)
	}

	log.Printf("Fetched %d template(s) : %v from %s\n", len(fetchedLanguages), fetchedLanguages, templateURL)

	err = removeArchive(archive)

	return err
}

// expandTemplatesFromZip builds a list of languages that: already exist and
// could not be overwritten and // a list of languages that are newly downloaded.
func expandTemplatesFromZip(archivePath string, overwrite bool) ([]string, []string, error) {
	var existingLanguages []string
	var fetchedLanguages []string

	availableLanguages := make(map[string]bool)

	zipFile, err := zip.OpenReader(archivePath)

	if err != nil {
		return nil, nil, err
	}
	defer zipFile.Close()

	for _, z := range zipFile.File {
		var rc io.ReadCloser

		relativePath := z.Name[strings.Index(z.Name, "/")+1:]
		if strings.Index(relativePath, "template/") != 0 {
			// Process only directories inside "template" at root
			continue
		}

		action, language, isDirectory := canExpandTemplateData(availableLanguages, relativePath)

		var expandFromZip bool

		switch action {

		case shouldExtractData:
			expandFromZip = true
		case newTemplateFound:
			expandFromZip = true
			fetchedLanguages = append(fetchedLanguages, language)
		case directoryAlreadyExists:
			expandFromZip = false
			existingLanguages = append(existingLanguages, language)
		case skipWritingData:
			expandFromZip = false
		default:
			return nil, nil, fmt.Errorf(fmt.Sprintf("don't know what to do when extracting zip: %s", archivePath))
		}

		if expandFromZip {
			if rc, err = z.Open(); err != nil {
				break
			}

			if err = createPath(relativePath, z.Mode()); err != nil {
				break
			}

			// If relativePath is just a directory, then skip expanding it.
			if len(relativePath) > 1 && !isDirectory {
				if err = writeFile(rc, z.UncompressedSize64, relativePath, z.Mode()); err != nil {
					return nil, nil, err
				}
			}
		}
	}

	return existingLanguages, fetchedLanguages, nil
}

// canExpandTemplateData returns what we should do with the file in form of ExtractAction enum
// with the language name and whether it is a directory
func canExpandTemplateData(availableLanguages map[string]bool, relativePath string) (extractAction, string, bool) {
	if pathSplit := strings.Split(relativePath, "/"); len(pathSplit) > 2 {
		language := pathSplit[1]

		// We know that this path is a directory if the last character is a "/"
		isDirectory := strings.HasSuffix(relativePath, "/")

		// Check if this is the root directory for a language (at ./template/lang)
		if len(pathSplit) == rootLanguageDirSplitCount && isDirectory {
			if !canWriteLanguage(availableLanguages, language, overwrite) {
				return directoryAlreadyExists, language, isDirectory
			}
			return newTemplateFound, language, isDirectory
		}

		if canWriteLanguage(availableLanguages, language, overwrite) == false {
			return skipWritingData, language, isDirectory
		}

		return shouldExtractData, language, isDirectory
	}
	// template/
	return skipWritingData, "", true
}

// removeArchive removes the given file
func removeArchive(archive string) error {
	log.Printf("Cleaning up zip file...")
	var err error

	if _, err = os.Stat(archive); err == nil {
		err = os.Remove(archive)
	}

	return err
}

// fetchMasterZip downloads a zip file from a repository URL
func fetchMasterZip(templateURL string) (string, error) {
	var err error

	templateURL = strings.TrimRight(templateURL, "/")
	templateURL = templateURL + "/archive/master.zip"
	archive := "master.zip"

	if _, err := os.Stat(archive); err != nil {
		timeout := 120 * time.Second
		client := proxy.MakeHTTPClient(&timeout)

		req, err := http.NewRequest(http.MethodGet, templateURL, nil)
		if err != nil {
			log.Println(err.Error())
			return "", err
		}
		log.Printf("HTTP GET %s\n", templateURL)
		res, err := client.Do(req)
		if err != nil {
			log.Println(err.Error())
			return "", err
		}
		if res.StatusCode != http.StatusOK {
			err := fmt.Errorf(fmt.Sprintf("%s is not valid, status code %d", templateURL, res.StatusCode))
			log.Println(err.Error())
			return "", err
		}
		if res.Body != nil {
			defer res.Body.Close()
		}
		bytesOut, err := ioutil.ReadAll(res.Body)
		if err != nil {
			log.Println(err.Error())
			return "", err
		}

		log.Printf("Writing %dKb to %s\n", len(bytesOut)/1024, archive)
		err = ioutil.WriteFile(archive, bytesOut, 0700)
		if err != nil {
			log.Println(err.Error())
			return "", err
		}
	}
	fmt.Println("")
	return archive, err
}

func writeFile(rc io.ReadCloser, size uint64, relativePath string, perms os.FileMode) error {
	var err error

	defer rc.Close()
	f, err := os.OpenFile(relativePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perms)
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = io.CopyN(f, rc, int64(size))

	return err
}

func createPath(relativePath string, perms os.FileMode) error {
	dir := filepath.Dir(relativePath)
	err := os.MkdirAll(dir, perms)
	return err
}

// canWriteLanguage tells whether the language can be expanded from the zip or not.
// availableLanguages map keeps track of which languages we know to be okay to copy.
// overwrite flag will allow to force copy the language template
func canWriteLanguage(availableLanguages map[string]bool, language string, overwrite bool) bool {
	canWrite := false
	if availableLanguages != nil && len(language) > 0 {
		if _, found := availableLanguages[language]; found {
			return availableLanguages[language]
		}
		canWrite = templateFolderExists(language, overwrite)
		availableLanguages[language] = canWrite
	}

	return canWrite
}

// Takes a language input (e.g. "node"), tells whether or not it is OK to download
func templateFolderExists(language string, overwrite bool) bool {
	dir := templateDirectory + language
	if _, err := os.Stat(dir); err == nil && !overwrite {
		// The directory template/language/ exists
		return false
	}
	return true
}
<|endoftext|>


<commit_before>package kenv

import (
	"syscall"
	"unsafe"
)

const (
	SYS_KENV = 390

	GET   = 0
	SET   = 1
	UNSET = 2
	DUMP  = 3
)

func Get(name string) ([]byte, error) {
	var (
		buf  = make([]byte, 1024)
		bptr *byte
		err  error
	)

	if bptr, err = syscall.BytePtrFromString(name); err != nil {
		return nil, err
	}

	if _, err = kenv_sys(GET, unsafe.Pointer(bptr), unsafe.Pointer(&buf[0]), len(buf)); err != nil {
		return nil, err
	}

	return buf, nil
}

func Set(name string, value string) error {
	var (
		bptr *byte
		err  error
		bslc []byte = []byte(value)
	)

	if bptr, err = syscall.BytePtrFromString(name); err != nil {
		return err
	}

	if _, err = kenv_sys(SET, unsafe.Pointer(bptr), unsafe.Pointer(&bslc[0]), len(bslc)); err != nil {
		return err
	}

	return nil
}

func Unset(name string) error {
	var (
		bptr *byte
		err  error
	)

	if bptr, err = syscall.BytePtrFromString(name); err != nil {
		return err
	}

	if _, err = kenv_sys(UNSET, unsafe.Pointer(bptr), unsafe.Pointer(nil), 0); err != nil {
		return err
	}

	return nil
}

func Dump() ([]byte, error) {
	var (
		envlen int
		err    error
	)

	if envlen, err = kenv_sys(DUMP, unsafe.Pointer(nil), unsafe.Pointer(nil), 0); err != nil {
		return nil, err
	}

	buf := make([]byte, envlen+1)
	if _, err = kenv_sys(DUMP, unsafe.Pointer(nil), unsafe.Pointer(&buf[0]), len(buf)); err != nil {
		return nil, err
	}

	return buf, nil
}

func kenv_sys(what int, name unsafe.Pointer, value unsafe.Pointer, size int) (int, error) {
	ret, _, err := syscall.Syscall6(SYS_KENV, uintptr(what), uintptr(name), uintptr(value), uintptr(size), 0, 0)
	if err != 0 {
		return -1, syscall.Errno(err)
	}
	return int(ret), nil
}
<commit_msg>lib/kenv-dump remove null char from end of buffer<commit_after>package kenv

import (
	"syscall"
	"unsafe"
)

const (
	SYS_KENV = 390

	GET   = 0
	SET   = 1
	UNSET = 2
	DUMP  = 3
)

func Get(name string) ([]byte, error) {
	var (
		buf  = make([]byte, 1024)
		bptr *byte
		err  error
		size int
	)

	if bptr, err = syscall.BytePtrFromString(name); err != nil {
		return nil, err
	}

	if size, err = kenv_sys(GET, unsafe.Pointer(bptr), unsafe.Pointer(&buf[0]), len(buf)); err != nil {
		return nil, err
	}

	return buf[:size], nil
}

func Set(name string, value string) error {
	var (
		bptr *byte
		err  error
		bslc []byte = []byte(value)
	)

	if bptr, err = syscall.BytePtrFromString(name); err != nil {
		return err
	}

	if _, err = kenv_sys(SET, unsafe.Pointer(bptr), unsafe.Pointer(&bslc[0]), len(bslc)); err != nil {
		return err
	}

	return nil
}

func Unset(name string) error {
	var (
		bptr *byte
		err  error
	)

	if bptr, err = syscall.BytePtrFromString(name); err != nil {
		return err
	}

	if _, err = kenv_sys(UNSET, unsafe.Pointer(bptr), unsafe.Pointer(nil), 0); err != nil {
		return err
	}

	return nil
}

func Dump() ([]byte, error) {
	var (
		envlen int
		err    error
	)

	if envlen, err = kenv_sys(DUMP, unsafe.Pointer(nil), unsafe.Pointer(nil), 0); err != nil {
		return nil, err
	}

	buf := make([]byte, envlen+1)
	if _, err = kenv_sys(DUMP, unsafe.Pointer(nil), unsafe.Pointer(&buf[0]), len(buf)); err != nil {
		return nil, err
	}

	if buf[len(buf)-1] == '\x00' {
		buf = buf[:len(buf)-1]
	}

	return buf, nil
}

func kenv_sys(what int, name unsafe.Pointer, value unsafe.Pointer, size int) (int, error) {
	ret, _, err := syscall.Syscall6(SYS_KENV, uintptr(what), uintptr(name), uintptr(value), uintptr(size), 0, 0)
	if err != 0 {
		return -1, syscall.Errno(err)
	}
	return int(ret), nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/boltdb/bolt"
	"github.com/gorilla/mux"
	"github.com/spf13/cobra"
)

var badgeCmd = &cobra.Command{
	Use:   "badge",
	Short: "Run a badge server and generate link to badges.",
	Long:  ``,
	Run: func(cmd *cobra.Command, args []string) {
		log.Fatalln("Can't use this subcommand directly. See 'popper help ci' for usage")
	},
}

var serviceCmd = &cobra.Command{
	Use:   "service",
	Short: "Start a badge server instance.",
	Long:  ``,
	Run: func(cmd *cobra.Command, args []string) {
		if len(args) != 0 {
			log.Fatalln("This command doesn't take arguments.")
		}

		router := mux.NewRouter().StrictSlash(true)

		router.
			HandleFunc("/{orgId}/{repoId}/{expId}", handleExperiment).
			Methods("GET", "POST")
		router.
			HandleFunc("/{orgId}/{repoId}/{expId}/status.svg", handleBadge).
			Methods("GET")

		log.Fatal(http.ListenAndServe(":9090", router))
	},
}

type ExperimentStatus struct {
	Name   string `json:"name"`
	Status string `json:"status"`
}

func getExperimentStatus(w http.ResponseWriter, orgId, repoId, expId string) (exp *ExperimentStatus) {
	exp = new(ExperimentStatus)

	db, err := bolt.Open("status.db", 0600, nil)
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	err = db.View(func(tx *bolt.Tx) error {
		orgBucket := tx.Bucket([]byte(orgId))
		if orgBucket == nil {
			exp.Status = "invalid"
			return nil
		}

		repoBucket := orgBucket.Bucket([]byte(repoId))
		if repoBucket == nil {
			exp.Status = "invalid"
			return nil
		}

		expBucket := repoBucket.Bucket([]byte(expId))
		if expBucket == nil {
			exp.Status = "invalid"
			return nil
		}

		exp = expBucket.Get([]byte("status"))

		return nil
	})

	return
}

var repos = map[string]map[string]*ExperimentStatus{}
var badges = map[string][]byte{
	"invalid": []byte(`<svg xmlns="http://www.w3.org/2000/svg" width="108" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="108" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h47v20H0z"/><path fill="#9f9f9f" d="M47 0h61v20H47z"/><path fill="url(#b)" d="M0 0h108v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="23.5" y="15" fill="#010101" fill-opacity=".3">Popper</text><text x="23.5" y="14">Popper</text><text x="76.5" y="15" fill="#010101" fill-opacity=".3">unknown</text><text x="76.5" y="14">unknown</text></g></svg>`),
	"fail":    []byte(`<svg xmlns="http://www.w3.org/2000/svg" width="82" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="82" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h47v20H0z"/><path fill="#e05d44" d="M47 0h35v20H47z"/><path fill="url(#b)" d="M0 0h82v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="23.5" y="15" fill="#010101" fill-opacity=".3">Popper</text><text x="23.5" y="14">Popper</text><text x="63.5" y="15" fill="#010101" fill-opacity=".3">FAIL</text><text x="63.5" y="14">FAIL</text></g></svg>`),
	"ok":      []byte(`<svg xmlns="http://www.w3.org/2000/svg" width="74" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="74" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h47v20H0z"/><path fill="#4c1" d="M47 0h27v20H47z"/><path fill="url(#b)" d="M0 0h74v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="23.5" y="15" fill="#010101" fill-opacity=".3">Popper</text><text x="23.5" y="14">Popper</text><text x="59.5" y="15" fill="#010101" fill-opacity=".3">OK</text><text x="59.5" y="14">OK</text></g></svg>`),
	"gold":    []byte(`<svg xmlns="http://www.w3.org/2000/svg" width="88" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="88" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h47v20H0z"/><path fill="#dfb317" d="M47 0h41v20H47z"/><path fill="url(#b)" d="M0 0h88v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="23.5" y="15" fill="#010101" fill-opacity=".3">Popper</text><text x="23.5" y="14">Popper</text><text x="66.5" y="15" fill="#010101" fill-opacity=".3">GOLD</text><text x="66.5" y="14">GOLD</text></g></svg>`),
}

func handleExperiment(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	vars := mux.Vars(r)
	orgId := vars["orgId"]
	repoId := vars["repoId"]
	expId := vars["expId"]

	switch r.Method {
	case "GET":
		exp := getExperimentStatus(w, orgId, repoId, expId)
		if exp.Status == "invalid" {
			w.WriteHeader(http.StatusNotFound)
			fmt.Fprintln(w, string("Repo or user not found"))
		}
		outgoingJSON, error := json.Marshal(exp)

		if error != nil {
			log.Println(error.Error())
			http.Error(w, error.Error(), http.StatusInternalServerError)
			return
		}
		fmt.Fprint(w, string(outgoingJSON))

	case "POST":
		experiment := new(ExperimentStatus)
		decoder := json.NewDecoder(r.Body)
		error := decoder.Decode(&experiment)
		if error != nil {
			log.Println(error.Error())
			http.Error(w, error.Error(), http.StatusInternalServerError)
			return
		}

		db, err := bolt.Open("status.db", 0600, nil)
		if err != nil {
			log.Fatal(err)
		}
		defer db.Close()
		db.Update(func(tx *bolt.tx) error {
			orgBucket, err := tx.CreateBucketIfNotExists([]byte(orgId))
			if err != nil {
				log.Println(error.Error())
				http.Error(w, error.Error(), http.StatusInternalServerError)
				return
			}
			repoBucket, err := orgBucket.CreateBucketIfNotExists([]byte(repoId))
			if err != nil {
				log.Println(error.Error())
				http.Error(w, error.Error(), http.StatusInternalServerError)
				return
			}
			expBucket, err := repoBucket.CreateBucketIfNotExists([]byte(expId))
			if err != nil {
				log.Println(error.Error())
				http.Error(w, error.Error(), http.StatusInternalServerError)
				return
			}
			experiment.Name = expId
			expBucket.Put([]byte("status"), []byte(experiment))
		})

		repo[expId] = experiment

		outgoingJSON, err := json.Marshal(experiment)
		if err != nil {
			log.Println(error.Error())
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		w.WriteHeader(http.StatusCreated)
		fmt.Fprint(w, string(outgoingJSON))
	}
}

func handleBadge(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	orgId := vars["orgId"]
	repoId := vars["repoId"]
	expId := vars["expId"]

	switch r.Method {
	case "GET":
		exp := getExperimentStatus(w, orgId, repoId, expId)
		date := time.Now().Format(http.TimeFormat)
		log.Printf("%v\n", date)
		log.Printf("State %v\n", exp)
		w.Header().Set("Content-Type", "image/svg+xml")
		w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
		w.Header().Set("Date", date)
		w.Header().Set("Expires", date)
		switch exp.Status {
		case "invalid":
			w.Write(badges["invalid"])
			break
		case "ok":
			w.Write(badges["ok"])
			break
		case "fail":
			w.Write(badges["fail"])
			break
		case "gold":
			w.Write(badges["gold"])
			break
		}
	}
}

func init() {
	RootCmd.AddCommand(badgeCmd)
	badgeCmd.AddCommand(serviceCmd)
}
<commit_msg>Debugging implementationn now<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/boltdb/bolt"
	"github.com/gorilla/mux"
	"github.com/spf13/cobra"
)

var badgeCmd = &cobra.Command{
	Use:   "badge",
	Short: "Run a badge server and generate link to badges.",
	Long:  ``,
	Run: func(cmd *cobra.Command, args []string) {
		log.Fatalln("Can't use this subcommand directly. See 'popper help ci' for usage")
	},
}

var serviceCmd = &cobra.Command{
	Use:   "service",
	Short: "Start a badge server instance.",
	Long:  ``,
	Run: func(cmd *cobra.Command, args []string) {
		if len(args) != 0 {
			log.Fatalln("This command doesn't take arguments.")
		}

		router := mux.NewRouter().StrictSlash(true)

		router.
			HandleFunc("/{orgId}/{repoId}/{expId}", handleExperiment).
			Methods("GET", "POST")
		router.
			HandleFunc("/{orgId}/{repoId}/{expId}/status.svg", handleBadge).
			Methods("GET")

		log.Fatal(http.ListenAndServe(":9090", router))
	},
}

type ExperimentStatus struct {
	Name   string `json:"name"`
	Status string `json:"status"`
}

func getExperimentStatus(w http.ResponseWriter, orgId, repoId, expId string) (exp *ExperimentStatus) {
	exp = nil

	db, err := bolt.Open("status.db", 0600, nil)
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	err = db.View(func(tx *bolt.Tx) error {
		orgBucket := tx.Bucket([]byte(orgId))
		if orgBucket == nil {
			exp.Status = "invalid"
			return nil
		}

		repoBucket := orgBucket.Bucket([]byte(repoId))
		if repoBucket == nil {
			exp.Status = "invalid"
			return nil
		}

		expBucket := repoBucket.Bucket([]byte(expId))
		if expBucket == nil {
			exp.Status = "invalid"
			return nil
		}

		exp, err = json.Decode(expBucket.Get([]byte("status")))

		return nil
	})

	return
}

var repos = map[string]map[string]*ExperimentStatus{}
var badges = map[string][]byte{
	"invalid": []byte(`<svg xmlns="http://www.w3.org/2000/svg" width="108" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="108" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h47v20H0z"/><path fill="#9f9f9f" d="M47 0h61v20H47z"/><path fill="url(#b)" d="M0 0h108v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="23.5" y="15" fill="#010101" fill-opacity=".3">Popper</text><text x="23.5" y="14">Popper</text><text x="76.5" y="15" fill="#010101" fill-opacity=".3">unknown</text><text x="76.5" y="14">unknown</text></g></svg>`),
	"fail":    []byte(`<svg xmlns="http://www.w3.org/2000/svg" width="82" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="82" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h47v20H0z"/><path fill="#e05d44" d="M47 0h35v20H47z"/><path fill="url(#b)" d="M0 0h82v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="23.5" y="15" fill="#010101" fill-opacity=".3">Popper</text><text x="23.5" y="14">Popper</text><text x="63.5" y="15" fill="#010101" fill-opacity=".3">FAIL</text><text x="63.5" y="14">FAIL</text></g></svg>`),
	"ok":      []byte(`<svg xmlns="http://www.w3.org/2000/svg" width="74" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="74" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h47v20H0z"/><path fill="#4c1" d="M47 0h27v20H47z"/><path fill="url(#b)" d="M0 0h74v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="23.5" y="15" fill="#010101" fill-opacity=".3">Popper</text><text x="23.5" y="14">Popper</text><text x="59.5" y="15" fill="#010101" fill-opacity=".3">OK</text><text x="59.5" y="14">OK</text></g></svg>`),
	"gold":    []byte(`<svg xmlns="http://www.w3.org/2000/svg" width="88" height="20"><linearGradient id="b" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><mask id="a"><rect width="88" height="20" rx="3" fill="#fff"/></mask><g mask="url(#a)"><path fill="#555" d="M0 0h47v20H0z"/><path fill="#dfb317" d="M47 0h41v20H47z"/><path fill="url(#b)" d="M0 0h88v20H0z"/></g><g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"><text x="23.5" y="15" fill="#010101" fill-opacity=".3">Popper</text><text x="23.5" y="14">Popper</text><text x="66.5" y="15" fill="#010101" fill-opacity=".3">GOLD</text><text x="66.5" y="14">GOLD</text></g></svg>`),
}

func handleExperiment(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	vars := mux.Vars(r)
	orgId := vars["orgId"]
	repoId := vars["repoId"]
	expId := vars["expId"]

	switch r.Method {
	case "GET":
		exp := getExperimentStatus(w, orgId, repoId, expId)
		if exp.Status == "invalid" {
			w.WriteHeader(http.StatusNotFound)
			fmt.Fprintln(w, string("Repo or user not found"))
		}
		outgoingJSON, error := json.Marshal(exp)

		if error != nil {
			log.Println(error.Error())
			http.Error(w, error.Error(), http.StatusInternalServerError)
			return
		}
		fmt.Fprint(w, string(outgoingJSON))

	case "POST":
		experiment := new(ExperimentStatus)
		decoder := json.NewDecoder(r.Body)
		error := decoder.Decode(&experiment)
		if error != nil {
			log.Println(error.Error())
			http.Error(w, error.Error(), http.StatusInternalServerError)
			return
		}

		db, err := bolt.Open("status.db", 0600, nil)
		if err != nil {
			log.Fatal(err)
		}
		defer db.Close()
		db.Update(func(tx *bolt.Tx) error {
			orgBucket, err := tx.CreateBucketIfNotExists([]byte(orgId))
			if err != nil {
				log.Println(error.Error())
				http.Error(w, error.Error(), http.StatusInternalServerError)
				return nil
			}
			repoBucket, err := orgBucket.CreateBucketIfNotExists([]byte(repoId))
			if err != nil {
				log.Println(error.Error())
				http.Error(w, error.Error(), http.StatusInternalServerError)
				return nil
			}
			expBucket, err := repoBucket.CreateBucketIfNotExists([]byte(expId))
			if err != nil {
				log.Println(error.Error())
				http.Error(w, error.Error(), http.StatusInternalServerError)
				return nil
			}
			experiment.Name = expId
			expBucket.Put([]byte("status"), []byte(experiment))

			return nil
		})

		//repo[expId] = experiment

		outgoingJSON, err := json.Marshal(experiment)
		if err != nil {
			log.Println(error.Error())
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		w.WriteHeader(http.StatusCreated)
		fmt.Fprint(w, string(outgoingJSON))
	}
}

func handleBadge(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	orgId := vars["orgId"]
	repoId := vars["repoId"]
	expId := vars["expId"]

	switch r.Method {
	case "GET":
		exp := getExperimentStatus(w, orgId, repoId, expId)
		date := time.Now().Format(http.TimeFormat)
		log.Printf("%v\n", date)
		log.Printf("State %v\n", exp)
		w.Header().Set("Content-Type", "image/svg+xml")
		w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
		w.Header().Set("Date", date)
		w.Header().Set("Expires", date)
		switch exp.Status {
		case "invalid":
			w.Write(badges["invalid"])
			break
		case "ok":
			w.Write(badges["ok"])
			break
		case "fail":
			w.Write(badges["fail"])
			break
		case "gold":
			w.Write(badges["gold"])
			break
		}
	}
}

func init() {
	RootCmd.AddCommand(badgeCmd)
	badgeCmd.AddCommand(serviceCmd)
}
<|endoftext|>


<commit_before>// API:
//
// Open(initialDeposit int64) *Account
// (Account) Close() (payout int64, ok bool)
// (Account) Balance() (balance int64, ok bool)
// (Account) Deposit(amount int64) (newBalance int64, ok bool)
//
// If Open is given a negative initial deposit, it must return nil.
// Deposit must handle a negative amount as a withdrawal.
// If any Account method is called on an closed account, it must not modify
// the account and must return ok = false.

package account

import (
	"runtime"
	"sync"
	"sync/atomic"
	"testing"
	"time"
)

const targetTestVersion = 1

func TestTestVersion(t *testing.T) {
	if testVersion != targetTestVersion {
		t.Fatalf("Found testVersion = %v, want %v", testVersion, targetTestVersion)
	}
}

func TestSeqOpenBalanceClose(t *testing.T) {
	// open account
	const amt = 10
	a := Open(amt)
	if a == nil {
		t.Fatalf("Open(%d) = nil, want non-nil *Account.", amt)
	}
	t.Logf("Account 'a' opened with initial balance of %d.", amt)

	// verify balance after open
	switch b, ok := a.Balance(); {
	case !ok:
		t.Fatal("a.Balance() returned !ok, want ok.")
	case b != amt:
		t.Fatalf("a.Balance() = %d, want %d", b, amt)
	}

	// close account
	switch p, ok := a.Close(); {
	case !ok:
		t.Fatalf("a.Close() returned !ok, want ok.")
	case p != amt:
		t.Fatalf("a.Close() returned payout = %d, want %d.", p, amt)
	}
	t.Log("Account 'a' closed.")

	// verify balance no longer accessible
	if b, ok := a.Balance(); ok {
		t.Log("Balance still available on closed account.")
		t.Fatalf("a.Balance() = %d, %t.  Want ok == false", b, ok)
	}
}

func TestSeqOpenDepositClose(t *testing.T) {
	// open account
	const openAmt = 10
	a := Open(openAmt)
	if a == nil {
		t.Fatalf("Open(%d) = nil, want non-nil *Account.", openAmt)
	}
	t.Logf("Account 'a' opened with initial balance of %d.", openAmt)

	// deposit
	const depAmt = 20
	const newAmt = openAmt + depAmt
	switch b, ok := a.Deposit(depAmt); {
	case !ok:
		t.Fatalf("a.Deposit(%d) returned !ok, want ok.", depAmt)
	case b != openAmt+depAmt:
		t.Fatalf("a.Deposit(%d) = %d, want new balance = %d", depAmt, b, newAmt)
	}
	t.Logf("Deposit of %d accepted to account 'a'", depAmt)

	// close account
	switch p, ok := a.Close(); {
	case !ok:
		t.Fatalf("a.Close() returned !ok, want ok.")
	case p != newAmt:
		t.Fatalf("a.Close() returned payout = %d, want %d.", p, newAmt)
	}
	t.Log("Account 'a' closed.")

	// verify deposits no longer accepted
	if b, ok := a.Deposit(1); ok {
		t.Log("Deposit accepted on closed account.")
		t.Fatalf("a.Deposit(1) = %d, %t.  Want ok == false", b, ok)
	}
}

func TestMoreSeqCases(t *testing.T) {
	// open account 'a' as before
	const openAmt = 10
	a := Open(openAmt)
	if a == nil {
		t.Fatalf("Open(%d) = nil, want non-nil *Account.", openAmt)
	}
	t.Logf("Account 'a' opened with initial balance of %d.", openAmt)

	// open account 'z' with zero balance
	z := Open(0)
	if z == nil {
		t.Fatal("Open(0) = nil, want non-nil *Account.")
	}
	t.Log("Account 'z' opened with initial balance of 0.")

	// attempt to open account with negative opening balance
	if Open(-10) != nil {
		t.Fatal("Open(-10) seemed to work, " +
			"want nil result for negative opening balance.")
	}

	// verify both balances a and z still there
	switch b, ok := a.Balance(); {
	case !ok:
		t.Fatal("a.Balance() returned !ok, want ok.")
	case b != openAmt:
		t.Fatalf("a.Balance() = %d, want %d", b, openAmt)
	}
	switch b, ok := z.Balance(); {
	case !ok:
		t.Fatal("z.Balance() returned !ok, want ok.")
	case b != 0:
		t.Fatalf("z.Balance() = %d, want 0", b)
	}

	// withdrawals
	const wAmt = 3
	const newAmt = openAmt - wAmt
	switch b, ok := a.Deposit(-wAmt); {
	case !ok:
		t.Fatalf("a.Deposit(%d) returned !ok, want ok.", -wAmt)
	case b != newAmt:
		t.Fatalf("a.Deposit(%d) = %d, want new balance = %d", -wAmt, b, newAmt)
	}
	t.Logf("Withdrawal of %d accepted from account 'a'", wAmt)
	if _, ok := z.Deposit(-1); ok {
		t.Fatal("z.Deposit(-1) returned ok, want !ok.")
	}

	// verify both balances
	switch b, ok := a.Balance(); {
	case !ok:
		t.Fatal("a.Balance() returned !ok, want ok.")
	case b != newAmt:
		t.Fatalf("a.Balance() = %d, want %d", b, newAmt)
	}
	switch b, ok := z.Balance(); {
	case !ok:
		t.Fatal("z.Balance() returned !ok, want ok.")
	case b != 0:
		t.Fatalf("z.Balance() = %d, want 0", b)
	}

	// close just z
	switch p, ok := z.Close(); {
	case !ok:
		t.Fatalf("z.Close() returned !ok, want ok.")
	case p != 0:
		t.Fatalf("z.Close() returned payout = %d, want 0.", p)
	}
	t.Log("Account 'z' closed.")

	// verify 'a' balance one more time
	switch b, ok := a.Balance(); {
	case !ok:
		t.Fatal("a.Balance() returned !ok, want ok.")
	case b != newAmt:
		t.Fatalf("a.Balance() = %d, want %d", b, newAmt)
	}
}

func TestConcClose(t *testing.T) {
	if runtime.NumCPU() < 2 {
		t.Skip("Multiple CPU cores required for concurrency tests.")
	}
	if runtime.GOMAXPROCS(0) < 2 {
		runtime.GOMAXPROCS(2)
	}

	// test competing close attempts
	for rep := 0; rep < 1000; rep++ {
		const openAmt = 10
		a := Open(openAmt)
		if a == nil {
			t.Fatalf("Open(%d) = nil, want non-nil *Account.", openAmt)
		}
		var start sync.WaitGroup
		start.Add(1)
		const closeAttempts = 10
		res := make(chan string)
		for i := 0; i < closeAttempts; i++ {
			go func() { // on your mark,
				start.Wait() // get set...
				switch p, ok := a.Close(); {
				case !ok:
					if p != 0 {
						t.Errorf("a.Close() = %d, %t.  "+
							"Want payout = 0 for unsuccessful close", p, ok)
						res <- "fail"
					} else {
						res <- "already closed"
					}
				case p != openAmt:
					t.Errorf("a.Close() = %d, %t.  "+
						"Want payout = %d for successful close", p, ok, openAmt)
					res <- "fail"
				default:
					res <- "close" // exactly one goroutine should reach here
				}
			}()
		}
		start.Done() // ...go
		var closes, fails int
		for i := 0; i < closeAttempts; i++ {
			switch <-res {
			case "close":
				closes++
			case "fail":
				fails++
			}
		}
		switch {
		case fails > 0:
			t.FailNow() // error already logged by other goroutine
		case closes == 0:
			t.Fatal("Concurrent a.Close() attempts all failed.  " +
				"Want one to succeed.")
		case closes > 1:
			t.Fatalf("%d concurrent a.Close() attempts succeeded, "+
				"each paying out %d!.  Want just one to succeed.",
				closes, openAmt)
		}
	}
}

func TestConcDeposit(t *testing.T) {
	if runtime.NumCPU() < 2 {
		t.Skip("Multiple CPU cores required for concurrency tests.")
	}
	if runtime.GOMAXPROCS(0) < 2 {
		runtime.GOMAXPROCS(2)
	}
	a := Open(0)
	if a == nil {
		t.Fatal("Open(0) = nil, want non-nil *Account.")
	}
	const amt = 10
	const c = 1000
	var negBal int32
	var start, g sync.WaitGroup
	start.Add(1)
	g.Add(3 * c)
	for i := 0; i < c; i++ {
		go func() { // deposit
			start.Wait()
			a.Deposit(amt) // ignore return values
			g.Done()
		}()
		go func() { // withdraw
			start.Wait()
			for {
				if _, ok := a.Deposit(-amt); ok {
					break
				}
				time.Sleep(time.Microsecond) // retry
			}
			g.Done()
		}()
		go func() { // watch that balance stays >= 0
			start.Wait()
			if p, _ := a.Balance(); p < 0 {
				atomic.StoreInt32(&negBal, 1)
			}
			g.Done()
		}()
	}
	start.Done()
	g.Wait()
	if negBal == 1 {
		t.Fatal("Balance went negative with concurrent deposits and " +
			"withdrawals.  Want balance always >= 0.")
	}
	if p, ok := a.Balance(); !ok || p != 0 {
		t.Fatalf("After equal concurrent deposits and withdrawals, "+
			"a.Balance = %d, %t.  Want 0, true", p, ok)
	}
}
<commit_msg>bank-account: more info about expected solution + mutex links (#710)<commit_after>// API:
//
// Open(initialDeposit int64) *Account
// (Account) Close() (payout int64, ok bool)
// (Account) Balance() (balance int64, ok bool)
// (Account) Deposit(amount int64) (newBalance int64, ok bool)
//
// If Open is given a negative initial deposit, it must return nil.
// Deposit must handle a negative amount as a withdrawal. Withdrawals must
// not succeed if they result in a negative balance.
// If any Account method is called on an closed account, it must not modify
// the account and must return ok = false.

// The tests will execute some operations concurrently. You should strive
// to ensure that operations on the Account leave it in a consistent state.
// For example: multiple goroutines may be depositing and withdrawing money
// simultaneously, two withdrawals occurring concurrently should not be able
// to bring the balance into the negative.

// If you are new to concurrent operations in Go it will be worth looking
// at the sync package, specifically Mutexes:
//
// https://golang.org/pkg/sync/
// https://tour.golang.org/concurrency/9
// https://gobyexample.com/mutexes

package account

import (
	"runtime"
	"sync"
	"sync/atomic"
	"testing"
	"time"
)

const targetTestVersion = 1

func TestTestVersion(t *testing.T) {
	if testVersion != targetTestVersion {
		t.Fatalf("Found testVersion = %v, want %v", testVersion, targetTestVersion)
	}
}

func TestSeqOpenBalanceClose(t *testing.T) {
	// open account
	const amt = 10
	a := Open(amt)
	if a == nil {
		t.Fatalf("Open(%d) = nil, want non-nil *Account.", amt)
	}
	t.Logf("Account 'a' opened with initial balance of %d.", amt)

	// verify balance after open
	switch b, ok := a.Balance(); {
	case !ok:
		t.Fatal("a.Balance() returned !ok, want ok.")
	case b != amt:
		t.Fatalf("a.Balance() = %d, want %d", b, amt)
	}

	// close account
	switch p, ok := a.Close(); {
	case !ok:
		t.Fatalf("a.Close() returned !ok, want ok.")
	case p != amt:
		t.Fatalf("a.Close() returned payout = %d, want %d.", p, amt)
	}
	t.Log("Account 'a' closed.")

	// verify balance no longer accessible
	if b, ok := a.Balance(); ok {
		t.Log("Balance still available on closed account.")
		t.Fatalf("a.Balance() = %d, %t.  Want ok == false", b, ok)
	}
}

func TestSeqOpenDepositClose(t *testing.T) {
	// open account
	const openAmt = 10
	a := Open(openAmt)
	if a == nil {
		t.Fatalf("Open(%d) = nil, want non-nil *Account.", openAmt)
	}
	t.Logf("Account 'a' opened with initial balance of %d.", openAmt)

	// deposit
	const depAmt = 20
	const newAmt = openAmt + depAmt
	switch b, ok := a.Deposit(depAmt); {
	case !ok:
		t.Fatalf("a.Deposit(%d) returned !ok, want ok.", depAmt)
	case b != openAmt+depAmt:
		t.Fatalf("a.Deposit(%d) = %d, want new balance = %d", depAmt, b, newAmt)
	}
	t.Logf("Deposit of %d accepted to account 'a'", depAmt)

	// close account
	switch p, ok := a.Close(); {
	case !ok:
		t.Fatalf("a.Close() returned !ok, want ok.")
	case p != newAmt:
		t.Fatalf("a.Close() returned payout = %d, want %d.", p, newAmt)
	}
	t.Log("Account 'a' closed.")

	// verify deposits no longer accepted
	if b, ok := a.Deposit(1); ok {
		t.Log("Deposit accepted on closed account.")
		t.Fatalf("a.Deposit(1) = %d, %t.  Want ok == false", b, ok)
	}
}

func TestMoreSeqCases(t *testing.T) {
	// open account 'a' as before
	const openAmt = 10
	a := Open(openAmt)
	if a == nil {
		t.Fatalf("Open(%d) = nil, want non-nil *Account.", openAmt)
	}
	t.Logf("Account 'a' opened with initial balance of %d.", openAmt)

	// open account 'z' with zero balance
	z := Open(0)
	if z == nil {
		t.Fatal("Open(0) = nil, want non-nil *Account.")
	}
	t.Log("Account 'z' opened with initial balance of 0.")

	// attempt to open account with negative opening balance
	if Open(-10) != nil {
		t.Fatal("Open(-10) seemed to work, " +
			"want nil result for negative opening balance.")
	}

	// verify both balances a and z still there
	switch b, ok := a.Balance(); {
	case !ok:
		t.Fatal("a.Balance() returned !ok, want ok.")
	case b != openAmt:
		t.Fatalf("a.Balance() = %d, want %d", b, openAmt)
	}
	switch b, ok := z.Balance(); {
	case !ok:
		t.Fatal("z.Balance() returned !ok, want ok.")
	case b != 0:
		t.Fatalf("z.Balance() = %d, want 0", b)
	}

	// withdrawals
	const wAmt = 3
	const newAmt = openAmt - wAmt
	switch b, ok := a.Deposit(-wAmt); {
	case !ok:
		t.Fatalf("a.Deposit(%d) returned !ok, want ok.", -wAmt)
	case b != newAmt:
		t.Fatalf("a.Deposit(%d) = %d, want new balance = %d", -wAmt, b, newAmt)
	}
	t.Logf("Withdrawal of %d accepted from account 'a'", wAmt)
	if _, ok := z.Deposit(-1); ok {
		t.Fatal("z.Deposit(-1) returned ok, want !ok.")
	}

	// verify both balances
	switch b, ok := a.Balance(); {
	case !ok:
		t.Fatal("a.Balance() returned !ok, want ok.")
	case b != newAmt:
		t.Fatalf("a.Balance() = %d, want %d", b, newAmt)
	}
	switch b, ok := z.Balance(); {
	case !ok:
		t.Fatal("z.Balance() returned !ok, want ok.")
	case b != 0:
		t.Fatalf("z.Balance() = %d, want 0", b)
	}

	// close just z
	switch p, ok := z.Close(); {
	case !ok:
		t.Fatalf("z.Close() returned !ok, want ok.")
	case p != 0:
		t.Fatalf("z.Close() returned payout = %d, want 0.", p)
	}
	t.Log("Account 'z' closed.")

	// verify 'a' balance one more time
	switch b, ok := a.Balance(); {
	case !ok:
		t.Fatal("a.Balance() returned !ok, want ok.")
	case b != newAmt:
		t.Fatalf("a.Balance() = %d, want %d", b, newAmt)
	}
}

func TestConcClose(t *testing.T) {
	if runtime.NumCPU() < 2 {
		t.Skip("Multiple CPU cores required for concurrency tests.")
	}
	if runtime.GOMAXPROCS(0) < 2 {
		runtime.GOMAXPROCS(2)
	}

	// test competing close attempts
	for rep := 0; rep < 1000; rep++ {
		const openAmt = 10
		a := Open(openAmt)
		if a == nil {
			t.Fatalf("Open(%d) = nil, want non-nil *Account.", openAmt)
		}
		var start sync.WaitGroup
		start.Add(1)
		const closeAttempts = 10
		res := make(chan string)
		for i := 0; i < closeAttempts; i++ {
			go func() { // on your mark,
				start.Wait() // get set...
				switch p, ok := a.Close(); {
				case !ok:
					if p != 0 {
						t.Errorf("a.Close() = %d, %t.  "+
							"Want payout = 0 for unsuccessful close", p, ok)
						res <- "fail"
					} else {
						res <- "already closed"
					}
				case p != openAmt:
					t.Errorf("a.Close() = %d, %t.  "+
						"Want payout = %d for successful close", p, ok, openAmt)
					res <- "fail"
				default:
					res <- "close" // exactly one goroutine should reach here
				}
			}()
		}
		start.Done() // ...go
		var closes, fails int
		for i := 0; i < closeAttempts; i++ {
			switch <-res {
			case "close":
				closes++
			case "fail":
				fails++
			}
		}
		switch {
		case fails > 0:
			t.FailNow() // error already logged by other goroutine
		case closes == 0:
			t.Fatal("Concurrent a.Close() attempts all failed.  " +
				"Want one to succeed.")
		case closes > 1:
			t.Fatalf("%d concurrent a.Close() attempts succeeded, "+
				"each paying out %d!.  Want just one to succeed.",
				closes, openAmt)
		}
	}
}

func TestConcDeposit(t *testing.T) {
	if runtime.NumCPU() < 2 {
		t.Skip("Multiple CPU cores required for concurrency tests.")
	}
	if runtime.GOMAXPROCS(0) < 2 {
		runtime.GOMAXPROCS(2)
	}
	a := Open(0)
	if a == nil {
		t.Fatal("Open(0) = nil, want non-nil *Account.")
	}
	const amt = 10
	const c = 1000
	var negBal int32
	var start, g sync.WaitGroup
	start.Add(1)
	g.Add(3 * c)
	for i := 0; i < c; i++ {
		go func() { // deposit
			start.Wait()
			a.Deposit(amt) // ignore return values
			g.Done()
		}()
		go func() { // withdraw
			start.Wait()
			for {
				if _, ok := a.Deposit(-amt); ok {
					break
				}
				time.Sleep(time.Microsecond) // retry
			}
			g.Done()
		}()
		go func() { // watch that balance stays >= 0
			start.Wait()
			if p, _ := a.Balance(); p < 0 {
				atomic.StoreInt32(&negBal, 1)
			}
			g.Done()
		}()
	}
	start.Done()
	g.Wait()
	if negBal == 1 {
		t.Fatal("Balance went negative with concurrent deposits and " +
			"withdrawals.  Want balance always >= 0.")
	}
	if p, ok := a.Balance(); !ok || p != 0 {
		t.Fatalf("After equal concurrent deposits and withdrawals, "+
			"a.Balance = %d, %t.  Want 0, true", p, ok)
	}
}
<|endoftext|>


<commit_before>package project

import (
	"golang.org/x/net/context"

	eventtypes "github.com/docker/engine-api/types/events"
	"github.com/docker/libcompose/config"
	"github.com/docker/libcompose/project/options"
)

// EmptyService is a struct that implements Service but does nothing.
type EmptyService struct {
}

// Create implements Service.Create but does nothing.
func (e *EmptyService) Create(ctx context.Context, options options.Create) error {
	return nil
}

// Build implements Service.Build but does nothing.
func (e *EmptyService) Build(ctx context.Context, buildOptions options.Build) error {
	return nil
}

// Up implements Service.Up but does nothing.
func (e *EmptyService) Up(ctx context.Context, options options.Up) error {
	return nil
}

// Start implements Service.Start but does nothing.
func (e *EmptyService) Start(ctx context.Context) error {
	return nil
}

// Stop implements Service.Stop() but does nothing.
func (e *EmptyService) Stop(ctx context.Context, timeout int) error {
	return nil
}

// Delete implements Service.Delete but does nothing.
func (e *EmptyService) Delete(ctx context.Context, options options.Delete) error {
	return nil
}

// Restart implements Service.Restart but does nothing.
func (e *EmptyService) Restart(ctx context.Context, timeout int) error {
	return nil
}

// Log implements Service.Log but does nothing.
func (e *EmptyService) Log(ctx context.Context, follow bool) error {
	return nil
}

// Pull implements Service.Pull but does nothing.
func (e *EmptyService) Pull(ctx context.Context) error {
	return nil
}

// Kill implements Service.Kill but does nothing.
func (e *EmptyService) Kill(ctx context.Context, signal string) error {
	return nil
}

// Containers implements Service.Containers but does nothing.
func (e *EmptyService) Containers(ctx context.Context) ([]Container, error) {
	return []Container{}, nil
}

// Scale implements Service.Scale but does nothing.
func (e *EmptyService) Scale(ctx context.Context, count int, timeout int) error {
	return nil
}

// Info implements Service.Info but does nothing.
func (e *EmptyService) Info(ctx context.Context, qFlag bool) (InfoSet, error) {
	return InfoSet{}, nil
}

// Pause implements Service.Pause but does nothing.
func (e *EmptyService) Pause(ctx context.Context) error {
	return nil
}

// Unpause implements Service.Pause but does nothing.
func (e *EmptyService) Unpause(ctx context.Context) error {
	return nil
}

// Run implements Service.Run but does nothing.
func (e *EmptyService) Run(ctx context.Context, commandParts []string) (int, error) {
	return 0, nil
}

// RemoveImage implements Service.RemoveImage but does nothing.
func (e *EmptyService) RemoveImage(ctx context.Context, imageType options.ImageType) error {
	return nil
}

// Events implements Service.Events but does nothing.
func (e *EmptyService) Events(ctx context.Context, events chan eventtypes.Message) error {
	return nil
}

// DependentServices implements Service.DependentServices with empty slice.
func (e *EmptyService) DependentServices() []ServiceRelationship {
	return []ServiceRelationship{}
}

// Config implements Service.Config with empty config.
func (e *EmptyService) Config() *config.ServiceConfig {
	return &config.ServiceConfig{}
}

// Name implements Service.Name with empty name.
func (e *EmptyService) Name() string {
	return ""
}
<commit_msg>Add check to ensure EmptyService implements Service<commit_after>package project

import (
	"golang.org/x/net/context"

	eventtypes "github.com/docker/engine-api/types/events"
	"github.com/docker/libcompose/config"
	"github.com/docker/libcompose/project/options"
)

// this ensures EmptyService implements Service
// useful since it's easy to forget adding new functions to EmptyService
var _ Service = &EmptyService{}

// EmptyService is a struct that implements Service but does nothing.
type EmptyService struct {
}

// Create implements Service.Create but does nothing.
func (e *EmptyService) Create(ctx context.Context, options options.Create) error {
	return nil
}

// Build implements Service.Build but does nothing.
func (e *EmptyService) Build(ctx context.Context, buildOptions options.Build) error {
	return nil
}

// Up implements Service.Up but does nothing.
func (e *EmptyService) Up(ctx context.Context, options options.Up) error {
	return nil
}

// Start implements Service.Start but does nothing.
func (e *EmptyService) Start(ctx context.Context) error {
	return nil
}

// Stop implements Service.Stop() but does nothing.
func (e *EmptyService) Stop(ctx context.Context, timeout int) error {
	return nil
}

// Delete implements Service.Delete but does nothing.
func (e *EmptyService) Delete(ctx context.Context, options options.Delete) error {
	return nil
}

// Restart implements Service.Restart but does nothing.
func (e *EmptyService) Restart(ctx context.Context, timeout int) error {
	return nil
}

// Log implements Service.Log but does nothing.
func (e *EmptyService) Log(ctx context.Context, follow bool) error {
	return nil
}

// Pull implements Service.Pull but does nothing.
func (e *EmptyService) Pull(ctx context.Context) error {
	return nil
}

// Kill implements Service.Kill but does nothing.
func (e *EmptyService) Kill(ctx context.Context, signal string) error {
	return nil
}

// Containers implements Service.Containers but does nothing.
func (e *EmptyService) Containers(ctx context.Context) ([]Container, error) {
	return []Container{}, nil
}

// Scale implements Service.Scale but does nothing.
func (e *EmptyService) Scale(ctx context.Context, count int, timeout int) error {
	return nil
}

// Info implements Service.Info but does nothing.
func (e *EmptyService) Info(ctx context.Context, qFlag bool) (InfoSet, error) {
	return InfoSet{}, nil
}

// Pause implements Service.Pause but does nothing.
func (e *EmptyService) Pause(ctx context.Context) error {
	return nil
}

// Unpause implements Service.Pause but does nothing.
func (e *EmptyService) Unpause(ctx context.Context) error {
	return nil
}

// Run implements Service.Run but does nothing.
func (e *EmptyService) Run(ctx context.Context, commandParts []string) (int, error) {
	return 0, nil
}

// RemoveImage implements Service.RemoveImage but does nothing.
func (e *EmptyService) RemoveImage(ctx context.Context, imageType options.ImageType) error {
	return nil
}

// Events implements Service.Events but does nothing.
func (e *EmptyService) Events(ctx context.Context, events chan eventtypes.Message) error {
	return nil
}

// DependentServices implements Service.DependentServices with empty slice.
func (e *EmptyService) DependentServices() []ServiceRelationship {
	return []ServiceRelationship{}
}

// Config implements Service.Config with empty config.
func (e *EmptyService) Config() *config.ServiceConfig {
	return &config.ServiceConfig{}
}

// Name implements Service.Name with empty name.
func (e *EmptyService) Name() string {
	return ""
}
<|endoftext|>


<commit_before>package gonavitia

import (
	"fmt"
	"github.com/aabizri/gonavitia/types"
	"net/url"
	"strconv"
	"time"
)

// JourneyResults countains the results of a Journey request
type JourneyResults struct {
	Journeys []types.Journey

	Logging

	session *Session
}

// String satisfies stringer, pretty-prints JourneyResults
func (jr JourneyResults) String() string {
	var msg string
	for i, journey := range jr.Journeys {
		if i != 0 {
			msg += "\n"
		}
		msg += fmt.Sprintf("Journey #%d: %s\n", i, journey.String())
	}
	return msg
}

// JourneyRequest countain the parameters needed to make a Journey request
type JourneyRequest struct {
	// There must be at least one From or To parameter defined
	// When used with just one of them, the resulting Journey won't have a populated Sections field.
	From types.QueryEscaper
	To   types.QueryEscaper

	Date          time.Time
	DateIsArrival bool

	// The traveler's type
	Traveler types.TravelerType

	// Define the freshness of data to use to compute journeys
	Freshness types.DataFreshness

	// Forbidden public transport objects
	Forbidden []types.ID

	// Allowed public transport objects
	// Note: This counstraint intersects with Forbidden
	Allowed []types.ID

	// Force the first section mode if it isn't a public transport mode
	// Note: The parameter is inclusive, not exclusive. As such if you want to forbid a mode you have to include all modes except that one.
	FirstSectionModes []types.Mode

	// Same, but for the last section
	LastSectionModes []types.Mode

	// MaxDurationToPT is the maximum allowed duration to reach the public transport.
	// Use this to limit the walking/biking part.
	MaxDurationToPT time.Duration

	// These four following parameters set the speed of each mode (Walking, Bike, BSS & car)
	// In meters per second
	WalkingSpeed   float64
	BikeSpeed      float64
	BikeShareSpeed float64
	CarSpeed       float64

	// Minimum and maximum amounts of journeys suggested
	MinJourneys uint
	MaxJourneys uint

	// Count fixes the amount of journeys to be returned, overriding minimum & maximum amount
	// Note: if Count=0 then it isn't taken into account
	Count uint

	// Maximum number of transfers in each journey
	MaxTransfers uint

	// Maximum duration of a trip
	MaxDuration time.Duration // To seconds

	// Wheelchair restricts the answer to accessible public transports
	Wheelchair bool
}

// toURL formats a journey request to url
// Should be refactored using a switch statement
func (req JourneyRequest) toURL() (url.Values, error) {
	params := url.Values{}

	// Define a few useful functions
	addUint := func(key string, amount uint64) {
		if amount != 0 {
			str := strconv.FormatUint(amount, 10)
			params.Add(key, str)
		}
	}
	addInt := func(key string, amount int64) {
		if amount != 0 {
			str := strconv.FormatInt(amount, 10)
			params.Add(key, str)
		}
	}
	addString := func(key string, str string) {
		if str != "" {
			params.Add(key, str)
		}
	}
	addIDSlice := func(key string, ids []types.ID) {
		if len(ids) != 0 {
			for _, id := range ids {
				params.Add(key, id.QueryEscape())
			}
		}
	}
	addModes := func(key string, modes []types.Mode) {
		if len(modes) != 0 {
			for _, mode := range modes {
				params.Add(key, string(mode))
			}
		}
	}
	addFloat := func(key string, amount float64) {
		if amount != 0 {
			speedStr := strconv.FormatFloat(amount, 'f', 3, 64)
			params.Add(key, speedStr)
		}
	}

	// Encode the from and to
	if from := req.From; from != nil {
		params.Add("from", from.QueryEscape())
	}
	if to := req.To; to != nil {
		params.Add("to", to.QueryEscape())
	}

	if datetime := req.Date; !datetime.IsZero() {
		str := datetime.Format(types.DateTimeFormat)
		params.Add("datetime", str)
		if req.DateIsArrival {
			params.Add("datetime_represents", "arrival")
		}
	}

	addString("traveler_type", string(req.Traveler))

	addString("data_freshness", string(req.Freshness))

	addIDSlice("forbidden_uris[]", req.Forbidden)

	addIDSlice("allowed_id[]", req.Allowed)

	addModes("first_section_mode[]", req.FirstSectionModes)

	addModes("last_section_mode[]", req.LastSectionModes)

	// max_duration_to_pt
	addInt("max_duration_to_pt", int64(req.MaxDurationToPT/time.Second))

	// walking_speed, bike_speed, bss_speed & car_speed
	addFloat("walking_speed", req.WalkingSpeed)
	addFloat("bike_speed", req.BikeSpeed)
	addFloat("bss_speed", req.BikeShareSpeed)
	addFloat("car_speed", req.CarSpeed)

	// If count is defined don't bother with the minimimal and maximum amount of items to return
	if count := req.Count; count != 0 {
		addUint("count", uint64(count))
	} else {
		addUint("min_nb_journeys", uint64(req.MinJourneys))
		addUint("max_nb_journeys", uint64(req.MaxJourneys))
	}

	// max_nb_transfers
	addUint("max_nb_transfers", uint64(req.MaxTransfers))

	// max_duration
	addInt("max_duration", int64(req.MaxDuration/time.Second))

	// wheelchair
	if req.Wheelchair {
		params.Add("wheelchair", "true")
	}

	return params, nil
}

// journeys is the internal function used by Journeys functions
func (s *Session) journeys(url string, params JourneyRequest) (*JourneyResults, error) {
	var results = &JourneyResults{session: s}
	err := s.request(url, params, results)
	return results, err
}

const journeysEndpoint string = "journeys"

// Journeys computes a list of journeys according to the parameters given
func (s *Session) Journeys(params JourneyRequest) (*JourneyResults, error) {
	// Create the URL
	url := s.APIURL + "/" + journeysEndpoint

	// Call
	return s.journeys(url, params)
}

// JourneysR computes a list of journeys according to the parameters given and in a specific region
func (s *Session) JourneysR(params JourneyRequest, regionID string) (*JourneyResults, error) {
	// Create the URL
	url := s.APIURL + "/coverage/" + regionID + "/" + journeysEndpoint

	// Call
	return s.journeys(url, params)
}
<commit_msg>Add more info for JourneyRequest<commit_after>package gonavitia

import (
	"fmt"
	"github.com/aabizri/gonavitia/types"
	"net/url"
	"strconv"
	"time"
)

// JourneyResults countains the results of a Journey request
type JourneyResults struct {
	Journeys []types.Journey

	Logging

	session *Session
}

// String satisfies stringer, pretty-prints JourneyResults
func (jr JourneyResults) String() string {
	var msg string
	for i, journey := range jr.Journeys {
		if i != 0 {
			msg += "\n"
		}
		msg += fmt.Sprintf("Journey #%d: %s\n", i, journey.String())
	}
	return msg
}

// JourneyRequest countain the parameters needed to make a Journey request
type JourneyRequest struct {
	// There must be at least one From or To parameter defined
	// When used with just one of them, the resulting Journey won't have a populated Sections field.
	From types.QueryEscaper
	To   types.QueryEscaper

	// When do you want to depart ? Or is DateIsArrival when do you want to arrive at your destination.
	Date          time.Time
	DateIsArrival bool

	// The traveler's type
	Traveler types.TravelerType

	// Define the freshness of data to use to compute journeys
	Freshness types.DataFreshness

	// Forbidden public transport objects
	Forbidden []types.ID

	// Allowed public transport objects
	// Note: This counstraint intersects with Forbidden
	Allowed []types.ID

	// Force the first section mode if it isn't a public transport mode
	// Note: The parameter is inclusive, not exclusive. As such if you want to forbid a mode you have to include all modes except that one.
	FirstSectionModes []types.Mode

	// Same, but for the last section
	LastSectionModes []types.Mode

	// MaxDurationToPT is the maximum allowed duration to reach the public transport.
	// Use this to limit the walking/biking part.
	MaxDurationToPT time.Duration

	// These four following parameters set the speed of each mode (Walking, Bike, BSS & car)
	// In meters per second
	WalkingSpeed   float64
	BikeSpeed      float64
	BikeShareSpeed float64
	CarSpeed       float64

	// Minimum and maximum amounts of journeys suggested
	MinJourneys uint
	MaxJourneys uint

	// Count fixes the amount of journeys to be returned, overriding minimum & maximum amount
	// Note: if Count=0 then it isn't taken into account
	Count uint

	// Maximum number of transfers in each journey
	MaxTransfers uint

	// Maximum duration of a trip
	MaxDuration time.Duration // To seconds

	// Wheelchair restricts the answer to accessible public transports
	Wheelchair bool
}

// toURL formats a journey request to url
// Should be refactored using a switch statement
func (req JourneyRequest) toURL() (url.Values, error) {
	params := url.Values{}

	// Define a few useful functions
	addUint := func(key string, amount uint64) {
		if amount != 0 {
			str := strconv.FormatUint(amount, 10)
			params.Add(key, str)
		}
	}
	addInt := func(key string, amount int64) {
		if amount != 0 {
			str := strconv.FormatInt(amount, 10)
			params.Add(key, str)
		}
	}
	addString := func(key string, str string) {
		if str != "" {
			params.Add(key, str)
		}
	}
	addIDSlice := func(key string, ids []types.ID) {
		if len(ids) != 0 {
			for _, id := range ids {
				params.Add(key, id.QueryEscape())
			}
		}
	}
	addModes := func(key string, modes []types.Mode) {
		if len(modes) != 0 {
			for _, mode := range modes {
				params.Add(key, string(mode))
			}
		}
	}
	addFloat := func(key string, amount float64) {
		if amount != 0 {
			speedStr := strconv.FormatFloat(amount, 'f', 3, 64)
			params.Add(key, speedStr)
		}
	}

	// Encode the from and to
	if from := req.From; from != nil {
		params.Add("from", from.QueryEscape())
	}
	if to := req.To; to != nil {
		params.Add("to", to.QueryEscape())
	}

	if datetime := req.Date; !datetime.IsZero() {
		str := datetime.Format(types.DateTimeFormat)
		params.Add("datetime", str)
		if req.DateIsArrival {
			params.Add("datetime_represents", "arrival")
		}
	}

	addString("traveler_type", string(req.Traveler))

	addString("data_freshness", string(req.Freshness))

	addIDSlice("forbidden_uris[]", req.Forbidden)

	addIDSlice("allowed_id[]", req.Allowed)

	addModes("first_section_mode[]", req.FirstSectionModes)

	addModes("last_section_mode[]", req.LastSectionModes)

	// max_duration_to_pt
	addInt("max_duration_to_pt", int64(req.MaxDurationToPT/time.Second))

	// walking_speed, bike_speed, bss_speed & car_speed
	addFloat("walking_speed", req.WalkingSpeed)
	addFloat("bike_speed", req.BikeSpeed)
	addFloat("bss_speed", req.BikeShareSpeed)
	addFloat("car_speed", req.CarSpeed)

	// If count is defined don't bother with the minimimal and maximum amount of items to return
	if count := req.Count; count != 0 {
		addUint("count", uint64(count))
	} else {
		addUint("min_nb_journeys", uint64(req.MinJourneys))
		addUint("max_nb_journeys", uint64(req.MaxJourneys))
	}

	// max_nb_transfers
	addUint("max_nb_transfers", uint64(req.MaxTransfers))

	// max_duration
	addInt("max_duration", int64(req.MaxDuration/time.Second))

	// wheelchair
	if req.Wheelchair {
		params.Add("wheelchair", "true")
	}

	return params, nil
}

// journeys is the internal function used by Journeys functions
func (s *Session) journeys(url string, params JourneyRequest) (*JourneyResults, error) {
	var results = &JourneyResults{session: s}
	err := s.request(url, params, results)
	return results, err
}

const journeysEndpoint string = "journeys"

// Journeys computes a list of journeys according to the parameters given
func (s *Session) Journeys(params JourneyRequest) (*JourneyResults, error) {
	// Create the URL
	url := s.APIURL + "/" + journeysEndpoint

	// Call
	return s.journeys(url, params)
}

// JourneysR computes a list of journeys according to the parameters given and in a specific region
func (s *Session) JourneysR(params JourneyRequest, regionID string) (*JourneyResults, error) {
	// Create the URL
	url := s.APIURL + "/coverage/" + regionID + "/" + journeysEndpoint

	// Call
	return s.journeys(url, params)
}
<|endoftext|>