<commit_before>package view

import (
	"image"
	"image/color"
	_ "image/jpeg"
	"image/png"
	"io"
	"log"
	"math/rand"
	"time"

	"github.com/disintegration/imaging"
	"github.com/nictuku/stardew-rocks/parser"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

const (
	backLayer        = 0.01
	buildingsLayer   = 0.02
	flooringLayer    = 0.02
	pathsLayer       = 0.03
	grassLayer       = 0.5
	treeLayer        = 0.5
	frontLayer       = 0.5
	objectLayer      = 0.5
	greenHouseLayer  = 0.5
	houseLayer       = 0.6 // after front, same as objects
	alwaysFrontLayer = 1.0
)

func xnaRect(x0, y0, width, height int) image.Rectangle {
	return image.Rect(x0, y0, x0+width, y0+height)
}

func tileCoordinates(id int, tileWidth, tileHeight, tilemapWidth int) (x0, y0 int) {
	numColumns := tilemapWidth / tileWidth
	y0 = id / numColumns * tileHeight
	x0 = id % numColumns * tileWidth
	return x0, y0
}

var mask = image.NewUniform(color.Alpha{255})

func maybeFlip(flip bool, img image.Image, r image.Rectangle) image.Image {
	if flip {
		return imaging.FlipH(imaging.Crop(img, r))
	}
	return img
}

type neighbour struct {
	x, y, bit int
}

func topLeftAlign(sr image.Rectangle, dp image.Point) image.Rectangle {
	r := sr.Sub(sr.Min)
	return image.Rectangle{
		image.Point{dp.X, dp.Y - r.Dy()},
		image.Point{dp.X + r.Max.X, dp.Y},
	}
}

func bottomLeftAlign(sr image.Rectangle, dp image.Point) image.Rectangle {
	r := sr.Sub(sr.Min)
	return image.Rectangle{
		image.Point{dp.X, dp.Y},
		image.Point{dp.X + r.Max.X, dp.Y + r.Dy()},
	}
}

func WriteImage(pm *parser.Map, sg *parser.SaveGame, w io.Writer) {
	var farm *parser.GameLocation
	for _, gameloc := range sg.Locations.GameLocations {
		if gameloc.Name == "Farm" {
			farm = &gameloc
			break
		}
	}

	if farm == nil {
		log.Printf("farm not found for %v", sg.Player.Name)
		return
	}

	// TODO: do not trust the user input. Don't hit files or slice indexes based on data.
	m := pm.TMX

	buildings := make([][]*parser.Building, m.Height)
	for i := range farm.Buildings {
		building := farm.Buildings[i]
		y := building.TileY + building.AnimalDoor.Y
		if y >= len(buildings) {
			continue
		}
		buildings[y] = append(buildings[y], &building)
	}

	// Order items and objects to be displayed based on their Y order. Items with a higher Y should be drawn last.
	items := make([][]*parser.TerrainItem, m.Height)
	for i := range farm.TerrainFeatures.Items {
		item := farm.TerrainFeatures.Items[i] // separate pointer for each item
		if item.Y() >= len(items) {
			continue
		}
		items[item.Y()] = append(items[item.Y()], &item)
	}
	objects := make([][]*parser.ObjectItem, m.Height)
	for i := range farm.Objects.Items {
		object := farm.Objects.Items[i] // separate pointer for each item
		if object.X() >= len(objects) {
			continue
		}
		objects[object.Y()] = append(objects[object.Y()], &object)
	}

	img := image.NewRGBA(image.Rect(0, 0, m.Width*m.TileWidth, m.Height*m.TileHeight))
	sb.Start()

	// Note that the order we issue draw commands must match the foreground/background order.
	// At a high level, the expected order is:
	// Back Layer; Buildings Layer; Houses; Objects; TerrainFeatures; Front Layer; AlwaysFront Layer

	for y := 0; y < m.Height; y++ {
		if y == 20 {
			drawHouse(pm, img, sg.Player.HouseUpgradeLevel)
		}
		if y == 15 {
			fixedGreenhouse := false
			for _, mail := range sg.Player.MailReceived {
				if mail == "ccPantry" {
					fixedGreenhouse = true
				}
			}
			drawGreenhouse(pm, img, fixedGreenhouse)
		}
		for _, building := range buildings[y] {
			drawBuilding(pm, building, img)
		}
		for _, object := range objects[y] {
			drawObject(pm, object, img, objects)
		}
		for _, item := range items[y] {
			drawTree(pm, sg.CurrentSeason, item, img)
			drawGrass(pm, item, img)
			drawFlooring(pm, item, img, items)
			drawHoeDirt(pm, sg.CurrentSeason, item, img, items)
		}
		for x := 0; x < m.Width; x++ {
			for _, layer := range m.Layers { // Layers are apparently ordered correctly.
				var layerOrder float32
				switch layer.Name {
				case "Back":
					layerOrder = backLayer
				case "Buildings":
					layerOrder = buildingsLayer
				case "Paths":
					continue // Don't draw paths.
				case "Front":
					layerOrder = frontLayer
				case "AlwaysFront":
					layerOrder = alwaysFrontLayer
				}
				drawTile(pm, sg.CurrentSeason, layer.DecodedTiles[y*m.Width+x], img, x, y, layerOrder)
			}
		}
	}

	sb.Flush()

	if err := png.Encode(w, img); err != nil {
		panic(err)
	}
}
<commit_msg>Draw ALL the objects!<commit_after>package view

import (
	"image"
	"image/color"
	_ "image/jpeg"
	"image/png"
	"io"
	"log"
	"math/rand"
	"time"

	"github.com/disintegration/imaging"
	"github.com/nictuku/stardew-rocks/parser"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

const (
	backLayer        = 0.01
	buildingsLayer   = 0.02
	flooringLayer    = 0.02
	pathsLayer       = 0.03
	grassLayer       = 0.5
	treeLayer        = 0.5
	frontLayer       = 0.5
	objectLayer      = 0.5
	greenHouseLayer  = 0.5
	houseLayer       = 0.6 // after front, same as objects
	alwaysFrontLayer = 1.0
)

func xnaRect(x0, y0, width, height int) image.Rectangle {
	return image.Rect(x0, y0, x0+width, y0+height)
}

func tileCoordinates(id int, tileWidth, tileHeight, tilemapWidth int) (x0, y0 int) {
	numColumns := tilemapWidth / tileWidth
	y0 = id / numColumns * tileHeight
	x0 = id % numColumns * tileWidth
	return x0, y0
}

var mask = image.NewUniform(color.Alpha{255})

func maybeFlip(flip bool, img image.Image, r image.Rectangle) image.Image {
	if flip {
		return imaging.FlipH(imaging.Crop(img, r))
	}
	return img
}

type neighbour struct {
	x, y, bit int
}

func topLeftAlign(sr image.Rectangle, dp image.Point) image.Rectangle {
	r := sr.Sub(sr.Min)
	return image.Rectangle{
		image.Point{dp.X, dp.Y - r.Dy()},
		image.Point{dp.X + r.Max.X, dp.Y},
	}
}

func bottomLeftAlign(sr image.Rectangle, dp image.Point) image.Rectangle {
	r := sr.Sub(sr.Min)
	return image.Rectangle{
		image.Point{dp.X, dp.Y},
		image.Point{dp.X + r.Max.X, dp.Y + r.Dy()},
	}
}

func WriteImage(pm *parser.Map, sg *parser.SaveGame, w io.Writer) {
	var farm *parser.GameLocation
	for _, gameloc := range sg.Locations.GameLocations {
		if gameloc.Name == "Farm" {
			farm = &gameloc
			break
		}
	}

	if farm == nil {
		log.Printf("farm not found for %v", sg.Player.Name)
		return
	}

	// TODO: do not trust the user input. Don't hit files or slice indexes based on data.
	m := pm.TMX

	buildings := make([][]*parser.Building, m.Height)
	for i := range farm.Buildings {
		building := farm.Buildings[i]
		y := building.TileY + building.AnimalDoor.Y
		if y >= len(buildings) {
			continue
		}
		buildings[y] = append(buildings[y], &building)
	}

	// Order items and objects to be displayed based on their Y order. Items with a higher Y should be drawn last.
	items := make([][]*parser.TerrainItem, m.Height)
	for i := range farm.TerrainFeatures.Items {
		item := farm.TerrainFeatures.Items[i] // separate pointer for each item
		if item.Y() >= len(items) {
			continue
		}
		items[item.Y()] = append(items[item.Y()], &item)
	}
	objects := make([][]*parser.ObjectItem, m.Height)
	for i := range farm.Objects.Items {
		object := farm.Objects.Items[i] // separate pointer for each item
		if object.Y() >= len(objects) {
			continue
		}
		objects[object.Y()] = append(objects[object.Y()], &object)
	}

	img := image.NewRGBA(image.Rect(0, 0, m.Width*m.TileWidth, m.Height*m.TileHeight))
	sb.Start()

	// Note that the order we issue draw commands must match the foreground/background order.
	// At a high level, the expected order is:
	// Back Layer; Buildings Layer; Houses; Objects; TerrainFeatures; Front Layer; AlwaysFront Layer

	for y := 0; y < m.Height; y++ {
		if y == 20 {
			drawHouse(pm, img, sg.Player.HouseUpgradeLevel)
		}
		if y == 15 {
			fixedGreenhouse := false
			for _, mail := range sg.Player.MailReceived {
				if mail == "ccPantry" {
					fixedGreenhouse = true
				}
			}
			drawGreenhouse(pm, img, fixedGreenhouse)
		}
		for _, building := range buildings[y] {
			drawBuilding(pm, building, img)
		}
		for _, object := range objects[y] {
			drawObject(pm, object, img, objects)
		}
		for _, item := range items[y] {
			drawTree(pm, sg.CurrentSeason, item, img)
			drawGrass(pm, item, img)
			drawFlooring(pm, item, img, items)
			drawHoeDirt(pm, sg.CurrentSeason, item, img, items)
		}
		for x := 0; x < m.Width; x++ {
			for _, layer := range m.Layers {
				var layerOrder float32
				switch layer.Name {
				case "Back":
					layerOrder = backLayer
				case "Buildings":
					layerOrder = buildingsLayer
				case "Paths":
					continue // Don't draw paths.
				case "Front":
					layerOrder = frontLayer
				case "AlwaysFront":
					layerOrder = alwaysFrontLayer
				}
				drawTile(pm, sg.CurrentSeason, layer.DecodedTiles[y*m.Width+x], img, x, y, layerOrder)
			}
		}
	}

	sb.Flush()

	if err := png.Encode(w, img); err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package view

import (
	"log"

	"github.com/sjmudd/pstop/lib"
)

// what information to view
type ViewType int

const (
        ViewLatency ViewType = iota
        ViewOps     ViewType = iota
        ViewIO      ViewType = iota
        ViewLocks   ViewType = iota
        ViewUsers   ViewType = iota
        ViewMutex   ViewType = iota
        ViewStages  ViewType = iota
)

type View struct {
	id ViewType
}

var view_names []string

func init() {
	view_names = []string{ "table_io_latency", "table_io_ops", "file_io_latency", "table_lock_latency", "user_latency", "mutex_latency", "stages_latency" }
}

// set the next view
func (s *View) SetNext() ViewType {
	if s.id <= ViewStages {
		s.id++
	} else {
		s.id = ViewLatency
	}

	return s.Get()
}

// set the previous view
func (s *View) SetPrev() ViewType {
	if s.id > ViewLatency {
		s.id--
	} else {
		s.id = ViewStages
	}
	return s.Get()
}

// set the view by id
func (s *View) Set(view_type ViewType) {
	s.id = view_type
}

// set the view based on its name
func (s *View) SetByName(name string) {
	for i := range view_names {
		if name == view_names[i] {
			s.id = ViewType(i)
			lib.Logger.Println("View.SetByName(", name, ")" )
			return
		}
	}

	// suggest what should be used
	all_views := ""
	for i := range view_names {
		all_views = all_views + " " + view_names[i]
	}

	// no need for now to trip off leading space from all_views.
	log.Fatal("Asked for a view name, '", name, "' which doesn't exist. Try one of:", all_views)
}

// return the current view
func (s View) Get() ViewType {
	return s.id
}

// get the name of the current view
func (s View) GetName() string {
	return view_names[s.id]
}

func (s ViewType) String() string {
	return view_names[s]
}
<commit_msg>reformat<commit_after>package view

import (
	"log"

	"github.com/sjmudd/pstop/lib"
)

// what information to view
type ViewType int

const (
	ViewLatency ViewType = iota
	ViewOps     ViewType = iota
	ViewIO      ViewType = iota
	ViewLocks   ViewType = iota
	ViewUsers   ViewType = iota
	ViewMutex   ViewType = iota
	ViewStages  ViewType = iota
)

type View struct {
	id ViewType
}

var view_names []string

func init() {
	view_names = []string{"table_io_latency", "table_io_ops", "file_io_latency", "table_lock_latency", "user_latency", "mutex_latency", "stages_latency"}
}

// set the next view
func (s *View) SetNext() ViewType {
	if s.id <= ViewStages {
		s.id++
	} else {
		s.id = ViewLatency
	}

	return s.Get()
}

// set the previous view
func (s *View) SetPrev() ViewType {
	if s.id > ViewLatency {
		s.id--
	} else {
		s.id = ViewStages
	}
	return s.Get()
}

// set the view by id
func (s *View) Set(view_type ViewType) {
	s.id = view_type
}

// set the view based on its name.
// - If we provide an empty name then use the default.
// - If we don't provide a valid name then give an error
func (s *View) SetByName(name string) {
	if name == "" {
		lib.Logger.Println("view.SetByName(): name is empty so setting to:", view.ViewLatency.String())
		app.view.Set(view.ViewLatency)
		return
	}

	for i := range view_names {
		if name == view_names[i] {
			s.id = ViewType(i)
			lib.Logger.Println("View.SetByName(", name, ")")
			return
		}
	}

	// suggest what should be used
	all_views := ""
	for i := range view_names {
		all_views = all_views + " " + view_names[i]
	}

	// no need for now to trip off leading space from all_views.
	log.Fatal("Asked for a view name, '", name, "' which doesn't exist. Try one of:", all_views)
}

// return the current view
func (s View) Get() ViewType {
	return s.id
}

// get the name of the current view
func (s View) GetName() string {
	return view_names[s.id]
}

func (s ViewType) String() string {
	return view_names[s]
}
<|endoftext|>


<commit_before>// Copyright 2014-2016 Ulrich Kunitz. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Command gxz supports the compression and decompression of LZMA files.
//
// Use gxz -h to get information about supported flags.
package main

//go:generate xb cat -o licenses.go xzLicense:github.com/ulikunitz/xz/LICENSE goLicense:~/go/LICENSE
//go:generate xb version-file -o version.go

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/ulikunitz/xz/internal/gflag"
	"github.com/ulikunitz/xz/internal/term"
	"github.com/ulikunitz/xz/internal/xlog"
)

const (
	usageStr = `Usage: gxz [OPTION]... [FILE]...
Compress or uncompress FILEs in the .lzma format (by default, compress FILES
in place).

  -c, --stdout      write to standard output and don't delete input files
  -d, --decompress  force decompression
  -f, --force       force overwrite of output file and compress links
  -F, --format <format>
                    Specify the file format to compress or decompress.
    auto            Default format for compression is xz. For decompression
	            the file content is used to identify the format.
    xz              The xz file format.
    lzma, alone     Compress to the .lzma file format.
  -h, --help        give this help
  -k, --keep        keep (don't delete) input files
  -L, --license     display software license
  -q, --quiet       suppress all warnings
  -v, --verbose     verbose mode
  -V, --version     display version string
  -z, --compress    force compression
  -0 ... -9         compression preset; default is 6

With no file, or when FILE is -, read standard input.

Report bugs using <https://github.com/ulikunitz/xz/issues>.
`
)

func usage(w io.Writer) {
	fmt.Fprint(w, usageStr)
}

func licenses(w io.Writer) {
	out := `
github.com/ulikunitz/xz -- xz for Go
====================================

{{.xz}}

Go Programming Language
=======================

The gxz program contains the packages gflag and xlog that are
extensions of packages from the Go standard library. The packages may
contain code from those packages.

{{.go}}
`
	out = strings.TrimLeft(out, " \n")
	tmpl, err := template.New("licenses").Parse(out)
	if err != nil {
		xlog.Panicf("error %s parsing licenses template", err)
	}
	lmap := map[string]string{
		"xz": strings.TrimSpace(xzLicense),
		"go": strings.TrimSpace(goLicense),
	}
	if err = tmpl.Execute(w, lmap); err != nil {
		xlog.Fatalf("error %s writing licenses template", err)
	}
}

type options struct {
	help       bool
	stdout     bool
	decompress bool
	force      bool
	format     string
	keep       bool
	license    bool
	version    bool
	quiet      int
	verbose    int
	preset     int
}

func (o *options) Init() {
	if o.preset != 0 {
		xlog.Panicf("options are already initialized")
	}
	gflag.BoolVarP(&o.help, "help", "h", false, "")
	gflag.BoolVarP(&o.stdout, "stdout", "c", false, "")
	gflag.BoolVarP(&o.decompress, "decompress", "d", false, "")
	gflag.BoolVarP(&o.force, "force", "f", false, "")
	gflag.StringVarP(&o.format, "format", "F", "auto", "")
	gflag.BoolVarP(&o.keep, "keep", "k", false, "")
	gflag.BoolVarP(&o.license, "license", "L", false, "")
	gflag.BoolVarP(&o.version, "version", "V", false, "")
	gflag.CounterVarP(&o.quiet, "quiet", "q", 0, "")
	gflag.CounterVarP(&o.verbose, "verbose", "v", 0, "")
	gflag.PresetVar(&o.preset, 0, 9, 6, "")
}

// normalizeFormat normalizes the format field of options. If the
// function completes without error the format field will be "xz",
// "lzma" or "auto". The latter only if the option decompress is true.
func normalizeFormat(o *options) error {
	switch o.format {
	case "xz", "lzma":
	case "auto":
		if !o.decompress {
			o.format = "xz"
		}
	case "alone":
		o.format = "lzma"
	default:
		return fmt.Errorf("format %q unsupported", o.format)
	}
	return nil
}

func main() {
	// setup logger
	cmdName := filepath.Base(os.Args[0])
	xlog.SetPrefix(fmt.Sprintf("%s: ", cmdName))
	xlog.SetFlags(0)

	// initialize flags
	gflag.CommandLine = gflag.NewFlagSet(cmdName, gflag.ExitOnError)
	gflag.Usage = func() { usage(os.Stderr); os.Exit(1) }
	opts := options{}
	opts.Init()

	switch cmdName {
	case "lzma", "glzma":
		opts.format = "lzma"
	case "lzcat", "glzcat":
		opts.format = "lzma"
		fallthrough
	case "xzcat", "gxzcat":
		opts.stdout = true
		opts.decompress = true
	case "unlzma", "unglzma":
		opts.format = "lzma"
		fallthrough
	case "unxz", "ungxz":
		opts.decompress = true
	}
	gflag.Parse()

	if opts.help {
		usage(os.Stdout)
		os.Exit(0)
	}
	if opts.license {
		licenses(os.Stdout)
		os.Exit(0)
	}
	if opts.version {
		xlog.Printf("version %s\n", version)
		os.Exit(0)
	}

	flags := xlog.Flags()
	switch {
	case opts.verbose <= 0:
		flags |= xlog.Lnoprint | xlog.Lnodebug
	case opts.verbose == 1:
		flags |= xlog.Lnodebug
	}
	switch {
	case opts.quiet >= 2:
		flags |= xlog.Lnoprint | xlog.Lnowarn | xlog.Lnodebug
		flags |= xlog.Lnopanic | xlog.Lnofatal
	case opts.quiet == 1:
		flags |= xlog.Lnoprint | xlog.Lnowarn | xlog.Lnodebug
	}
	xlog.SetFlags(flags)

	if err := normalizeFormat(&opts); err != nil {
		xlog.Fatal(err)
	}

	var args []string
	if gflag.NArg() == 0 {
		opts.stdout = true
		args = []string{"-"}
	} else {
		args = gflag.Args()
	}

	if opts.stdout && !opts.decompress && !opts.force &&
		term.IsTerminal(os.Stdout.Fd()) {
		xlog.Fatal(`Compressed data will not be written to a terminal
Use -f to force compression. For help type gxz -h.`)
	}

	exit := 0
	for _, arg := range args {
		if err := processFile(arg, &opts); err != nil {
			exit = 1
		}
	}
	os.Exit(exit)
}
<commit_msg>gxz: added cpuprofile flag<commit_after>// Copyright 2014-2016 Ulrich Kunitz. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Command gxz supports the compression and decompression of LZMA files.
//
// Use gxz -h to get information about supported flags.
package main

//go:generate xb cat -o licenses.go xzLicense:github.com/ulikunitz/xz/LICENSE goLicense:~/go/LICENSE
//go:generate xb version-file -o version.go

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"runtime/pprof"
	"strings"
	"text/template"

	"github.com/ulikunitz/xz/internal/gflag"
	"github.com/ulikunitz/xz/internal/term"
	"github.com/ulikunitz/xz/internal/xlog"
)

const (
	usageStr = `Usage: gxz [OPTION]... [FILE]...
Compress or uncompress FILEs in the .lzma format (by default, compress FILES
in place).

  -c, --stdout      write to standard output and don't delete input files
  -d, --decompress  force decompression
  -f, --force       force overwrite of output file and compress links
  -F, --format <format>
                    Specify the file format to compress or decompress.
    auto            Default format for compression is xz. For decompression
	            the file content is used to identify the format.
    xz              The xz file format.
    lzma, alone     Compress to the .lzma file format.
  -h, --help        give this help
  -k, --keep        keep (don't delete) input files
  -L, --license     display software license
  -q, --quiet       suppress all warnings
  -v, --verbose     verbose mode
  -V, --version     display version string
  -z, --compress    force compression
  -0 ... -9         compression preset; default is 6
  --cpuprofile <file>
                    create a cpuprofile that can be used with go tool pprof

With no file, or when FILE is -, read standard input.

Report bugs using <https://github.com/ulikunitz/xz/issues>.
`
)

func usage(w io.Writer) {
	fmt.Fprint(w, usageStr)
}

func licenses(w io.Writer) {
	out := `
github.com/ulikunitz/xz -- xz for Go
====================================

{{.xz}}

Go Programming Language
=======================

The gxz program contains the packages gflag and xlog that are
extensions of packages from the Go standard library. The packages may
contain code from those packages.

{{.go}}
`
	out = strings.TrimLeft(out, " \n")
	tmpl, err := template.New("licenses").Parse(out)
	if err != nil {
		xlog.Panicf("error %s parsing licenses template", err)
	}
	lmap := map[string]string{
		"xz": strings.TrimSpace(xzLicense),
		"go": strings.TrimSpace(goLicense),
	}
	if err = tmpl.Execute(w, lmap); err != nil {
		xlog.Fatalf("error %s writing licenses template", err)
	}
}

type options struct {
	help       bool
	stdout     bool
	decompress bool
	force      bool
	format     string
	keep       bool
	license    bool
	version    bool
	quiet      int
	verbose    int
	preset     int
	cpuprofile string
}

func (o *options) Init() {
	if o.preset != 0 {
		xlog.Panicf("options are already initialized")
	}
	gflag.BoolVarP(&o.help, "help", "h", false, "")
	gflag.BoolVarP(&o.stdout, "stdout", "c", false, "")
	gflag.BoolVarP(&o.decompress, "decompress", "d", false, "")
	gflag.BoolVarP(&o.force, "force", "f", false, "")
	gflag.StringVarP(&o.format, "format", "F", "auto", "")
	gflag.BoolVarP(&o.keep, "keep", "k", false, "")
	gflag.BoolVarP(&o.license, "license", "L", false, "")
	gflag.BoolVarP(&o.version, "version", "V", false, "")
	gflag.CounterVarP(&o.quiet, "quiet", "q", 0, "")
	gflag.CounterVarP(&o.verbose, "verbose", "v", 0, "")
	gflag.PresetVar(&o.preset, 0, 9, 6, "")
	gflag.StringVarP(&o.cpuprofile, "cpuprofile", "", "", "")
}

// normalizeFormat normalizes the format field of options. If the
// function completes without error the format field will be "xz",
// "lzma" or "auto". The latter only if the option decompress is true.
func normalizeFormat(o *options) error {
	switch o.format {
	case "xz", "lzma":
	case "auto":
		if !o.decompress {
			o.format = "xz"
		}
	case "alone":
		o.format = "lzma"
	default:
		return fmt.Errorf("format %q unsupported", o.format)
	}
	return nil
}

func main() {
	// setup logger
	cmdName := filepath.Base(os.Args[0])
	xlog.SetPrefix(fmt.Sprintf("%s: ", cmdName))
	xlog.SetFlags(0)

	// initialize flags
	gflag.CommandLine = gflag.NewFlagSet(cmdName, gflag.ExitOnError)
	gflag.Usage = func() { usage(os.Stderr); os.Exit(1) }
	opts := options{}
	opts.Init()

	switch cmdName {
	case "lzma", "glzma":
		opts.format = "lzma"
	case "lzcat", "glzcat":
		opts.format = "lzma"
		fallthrough
	case "xzcat", "gxzcat":
		opts.stdout = true
		opts.decompress = true
	case "unlzma", "unglzma":
		opts.format = "lzma"
		fallthrough
	case "unxz", "ungxz":
		opts.decompress = true
	}
	gflag.Parse()

	if opts.help {
		usage(os.Stdout)
		os.Exit(0)
	}
	if opts.license {
		licenses(os.Stdout)
		os.Exit(0)
	}
	if opts.version {
		xlog.Printf("version %s\n", version)
		os.Exit(0)
	}

	flags := xlog.Flags()
	switch {
	case opts.verbose <= 0:
		flags |= xlog.Lnoprint | xlog.Lnodebug
	case opts.verbose == 1:
		flags |= xlog.Lnodebug
	}
	switch {
	case opts.quiet >= 2:
		flags |= xlog.Lnoprint | xlog.Lnowarn | xlog.Lnodebug
		flags |= xlog.Lnopanic | xlog.Lnofatal
	case opts.quiet == 1:
		flags |= xlog.Lnoprint | xlog.Lnowarn | xlog.Lnodebug
	}
	xlog.SetFlags(flags)

	if opts.cpuprofile != "" {
		f, err := os.Create(opts.cpuprofile)
		if err != nil {
			xlog.Fatal(err)
		}
		if err = pprof.StartCPUProfile(f); err != nil {
			xlog.Fatal(err)
		}
	}

	if err := normalizeFormat(&opts); err != nil {
		pprof.StopCPUProfile()
		xlog.Fatal(err)
	}

	var args []string
	if gflag.NArg() == 0 {
		opts.stdout = true
		args = []string{"-"}
	} else {
		args = gflag.Args()
	}

	if opts.stdout && !opts.decompress && !opts.force &&
		term.IsTerminal(os.Stdout.Fd()) {
		pprof.StopCPUProfile()
		xlog.Fatal(`Compressed data will not be written to a terminal
Use -f to force compression. For help type gxz -h.`)
	}

	exit := 0
	for _, arg := range args {
		if err := processFile(arg, &opts); err != nil {
			exit = 1
		}
	}

	pprof.StopCPUProfile()
	os.Exit(exit)
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"io"
	"os"
	"os/signal"
	"time"

	. "github.com/polydawn/go-errcat"
	"github.com/polydawn/refmt"
	"github.com/polydawn/refmt/json"
	"gopkg.in/alecthomas/kingpin.v2"

	"go.polydawn.net/go-timeless-api"
	"go.polydawn.net/go-timeless-api/rio"
	tar "go.polydawn.net/rio/transmat/tar"
)

/*
	Output serialization formats
*/
const (
	FmtJson = "json"
	FmtDumb = "dumb"
)

type baseCLI struct {
	Deadline       string        // Deadline time (RFC3339 or @UNIX)
	Format         string        // Output api format, eg. json
	ProgressEnable bool          // Emit progress notification yes/no
	ProgressRate   time.Duration // How frequently to emit progress notification
	Test           string        // Testmode
	Timeout        time.Duration // Timeout duration (exclusive with deadline) eg. "60s"
	PackCLI        struct {
		PackType            string             // Pack type
		Path                string             // Pack target path
		Filters             api.FilesetFilters // TODO: file filters for pack/unpack
		TargetWarehouseAddr string             // Warehouse address to push to
	}
	UnpackCLI struct {
		WareID               string // Ware id string "<kind>:<hash>"
		Path                 string // Unpack target path
		Filters              api.FilesetFilters
		PlacementMode        string   // Placement mode enum
		SourcesWarehouseAddr []string // Warehouse address to push to
	}
	MirrorCLI struct {
		SourcesWarehouseAddr []string // Warehouse addresses to fetch from
		TargetWarehouseAddr  string   // Warehouse address to push to
		WareID               string   // Ware id string "<kind>:<hash>"
	}
}

func configurePack(cli *baseCLI, appPack *kingpin.CmdClause) {
	// Non-filter flags
	appPack.Arg("pack", "Pack type").
		Required().
		StringVar(&cli.PackCLI.PackType)
	appPack.Arg("path", "Target path").
		Required().
		StringVar(&cli.PackCLI.Path)
	appPack.Flag("target", "Warehouse in which to place the ware").
		StringVar(&cli.PackCLI.TargetWarehouseAddr)

	// Filter flags
	appPack.Flag("uid", "Set UID filter [keep, <int>]").
		StringVar(&cli.PackCLI.Filters.Uid)
	appPack.Flag("gid", "Set GID filter [keep, <int>]").
		StringVar(&cli.PackCLI.Filters.Gid)
	appPack.Flag("mtime", "Set mtime filter [keep, <@UNIX>, <RFC3339>]. Will be set to a date if not specified.").
		StringVar(&cli.PackCLI.Filters.Mtime)
	// Sticky flag not used for pack
}

func configureUnpack(cli *baseCLI, appUnpack *kingpin.CmdClause) {
	// Non-filter flags
	appUnpack.Arg("path", "Target path").
		Required().
		StringVar(&cli.UnpackCLI.Path)
	appUnpack.Arg("ware", "Ware ID").
		Required().
		StringVar(&cli.UnpackCLI.WareID)
	appUnpack.Flag("placer", "Placement mode to use [copy, direct, mount, none]").
		EnumVar(&cli.UnpackCLI.PlacementMode,
			string(rio.Placement_Copy), string(rio.Placement_Direct), string(rio.Placement_Mount), string(rio.Placement_None))
	appUnpack.Flag("source", "Warehouses from which to fetch the ware").
		StringsVar(&cli.UnpackCLI.SourcesWarehouseAddr)

	// Filter flags
	appUnpack.Flag("uid", "Set UID filter [keep, mine, <int>]").
		Default("mine").
		StringVar(&cli.UnpackCLI.Filters.Uid)
	appUnpack.Flag("gid", "Set GID filter [keep, mine, <int>]").
		Default("mine").
		StringVar(&cli.UnpackCLI.Filters.Gid)
	appUnpack.Flag("mtime", "Set mtime filter [keep, <@UNIX>, <RFC3339>]").
		Default("keep").
		StringVar(&cli.UnpackCLI.Filters.Mtime)
	appUnpack.Flag("sticky", "Keep setuid, setgid, and sticky bits [keep, zero]").
		Default("zero").
		EnumVar(&cli.UnpackCLI.Filters.Sticky,
			"keep", "zero")
}

func configureMirror(cli *baseCLI, appMirror *kingpin.CmdClause) {
	appMirror.Arg("ware", "Ware ID").
		Required().
		StringVar(&cli.MirrorCLI.WareID)
	appMirror.Arg("target", "Warehouse in which to place the ware").
		Required().
		StringVar(&cli.MirrorCLI.TargetWarehouseAddr)
	appMirror.Flag("source", "Warehouses from which to fetch the ware").
		StringsVar(&cli.MirrorCLI.SourcesWarehouseAddr)
}

/*
	Blocks until a sigint is received, then calls cancel.
*/
func CancelOnInterrupt(cancel context.CancelFunc) {
	signalChan := make(chan os.Signal)
	signal.Notify(signalChan, os.Interrupt)
	<-signalChan
	cancel()
	close(signalChan)
}

func main() {
	ctx := context.Background()
	exitCode := Main(ctx, os.Args, os.Stdin, os.Stdout, os.Stderr)
	os.Exit(int(exitCode))
}

func Main(ctx context.Context, args []string, stdin io.Reader, stdout, stderr io.Writer) rio.ExitCode {
	exitCode := rio.ExitSuccess
	ctx, cancel := context.WithCancel(ctx)
	go CancelOnInterrupt(cancel)

	cli := baseCLI{}

	app := kingpin.New("rio", "Repeatable I/O")
	app.HelpFlag.Short('h')

	app.UsageWriter(stderr)
	app.ErrorWriter(stderr)

	app.Flag("deadline", "Deadline (RFC3339)").
		StringVar(&cli.Deadline)
	app.Flag("timeout", "Timeout for command").
		DurationVar(&cli.Timeout)
	app.Flag("format", "Output api format").
		Default(FmtDumb).
		EnumVar(&cli.Format, FmtJson, FmtDumb)
	app.Flag("progress-rate", "How frequently to emit progress notification").
		DurationVar(&cli.ProgressRate)
	app.Flag("progress", "Emit progress notification").
		BoolVar(&cli.ProgressEnable)
	app.Flag("test", "Testmodes").
		StringVar(&cli.Test)

	appPack := app.Command("pack", "pack a fileset into a ware")
	configurePack(&cli, appPack)

	appUnpack := app.Command("unpack", "fetch a ware")
	configureUnpack(&cli, appUnpack)

	appMirror := app.Command("mirror", "mirror a ware to another warehouse")
	configureUnpack(&cli, appMirror)

	var termErr error
	app.Terminate(func(status int) {
		termErr = fmt.Errorf("parsing error: %d\n", status)
	})
	cmd, err := app.Parse(args[1:])
	if err != nil {
		fmt.Fprintln(stderr, err)
		return rio.ExitUsage
	}
	if termErr != nil {
		fmt.Fprintln(stderr, termErr)
		return rio.ExitUsage
	}
	var wareID api.WareID
	switch cmd {
	case appPack.FullCommand():
		wareID, err = executePack(ctx, cli)
		SerializeResult(cli.Format, wareID, err, stdout, stderr)
	case appUnpack.FullCommand():
		wareID, err = executeUnpack(ctx, cli)
		SerializeResult(cli.Format, wareID, err, stdout, stderr)
	case appMirror.FullCommand():
		fmt.Fprintln(stderr, "not implemented")
		return rio.ExitNotImplemented
	}

	return exitCode
}

func SerializeResult(format string, wareID api.WareID, resultErr error, stdout io.Writer, stderr io.Writer) {
	result := &rio.Event_Result{
		WareID: wareID,
	}
	result.SetError(resultErr)
	ev := rio.Event{Result: result}
	switch format {
	case FmtJson:
		marshaller := refmt.NewMarshallerAtlased(json.EncodeOptions{}, stdout, rio.Atlas)
		err := marshaller.Marshal(&ev)
		if err != nil {
			panic(err)
		}
	case FmtDumb:
		if resultErr != nil {
			fmt.Fprintln(stderr, resultErr)
		} else {
			fmt.Fprintln(stdout, wareID)
		}
	default:
		panic(fmt.Errorf("rio: invalid format %s", format))
	}
}

func convertWarehouseSlice(slice []string) []api.WarehouseAddr {
	result := make([]api.WarehouseAddr, len(slice))
	for idx, item := range slice {
		result[idx] = api.WarehouseAddr(item)
	}
	return result
}

func executeUnpack(ctx context.Context, cli baseCLI) (api.WareID, error) {
	wareID, err := api.ParseWareID(cli.UnpackCLI.WareID)
	if err != nil {
		return api.WareID{}, err
	}
	var (
		unpackFunc rio.UnpackFunc
	)
	switch wareID.Type {
	case "tar":
		unpackFunc = tar.Unpack
	default:
		return api.WareID{}, Errorf(rio.ErrUsage, "unsupported packtype %q", wareID.Type)
	}
	monitor := rio.Monitor{}
	path := cli.UnpackCLI.Path
	warehouses := convertWarehouseSlice(cli.UnpackCLI.SourcesWarehouseAddr)
	return unpackFunc(ctx, wareID, path, cli.UnpackCLI.Filters, rio.PlacementMode(cli.UnpackCLI.PlacementMode), warehouses, monitor)
}

func executePack(ctx context.Context, cli baseCLI) (api.WareID, error) {
	var (
		packType api.PackType
		packFunc rio.PackFunc
	)
	switch cli.PackCLI.PackType {
	case "tar":
		packType, packFunc = tar.PackType, tar.Pack
	default:
		return api.WareID{}, Errorf(rio.ErrUsage, "unsupported packtype %q", cli.PackCLI.PackType)
	}
	monitor := rio.Monitor{}
	warehouse := api.WarehouseAddr(cli.PackCLI.TargetWarehouseAddr)

	return packFunc(ctx, packType, cli.PackCLI.Path, cli.PackCLI.Filters, warehouse, monitor)
}
<commit_msg>cli: add missing --sticky flag in pack.<commit_after>package main

import (
	"context"
	"fmt"
	"io"
	"os"
	"os/signal"
	"time"

	. "github.com/polydawn/go-errcat"
	"github.com/polydawn/refmt"
	"github.com/polydawn/refmt/json"
	"gopkg.in/alecthomas/kingpin.v2"

	"go.polydawn.net/go-timeless-api"
	"go.polydawn.net/go-timeless-api/rio"
	tar "go.polydawn.net/rio/transmat/tar"
)

/*
	Output serialization formats
*/
const (
	FmtJson = "json"
	FmtDumb = "dumb"
)

type baseCLI struct {
	Deadline       string        // Deadline time (RFC3339 or @UNIX)
	Format         string        // Output api format, eg. json
	ProgressEnable bool          // Emit progress notification yes/no
	ProgressRate   time.Duration // How frequently to emit progress notification
	Test           string        // Testmode
	Timeout        time.Duration // Timeout duration (exclusive with deadline) eg. "60s"
	PackCLI        struct {
		PackType            string             // Pack type
		Path                string             // Pack target path
		Filters             api.FilesetFilters // TODO: file filters for pack/unpack
		TargetWarehouseAddr string             // Warehouse address to push to
	}
	UnpackCLI struct {
		WareID               string // Ware id string "<kind>:<hash>"
		Path                 string // Unpack target path
		Filters              api.FilesetFilters
		PlacementMode        string   // Placement mode enum
		SourcesWarehouseAddr []string // Warehouse address to push to
	}
	MirrorCLI struct {
		SourcesWarehouseAddr []string // Warehouse addresses to fetch from
		TargetWarehouseAddr  string   // Warehouse address to push to
		WareID               string   // Ware id string "<kind>:<hash>"
	}
}

func configurePack(cli *baseCLI, appPack *kingpin.CmdClause) {
	// Non-filter flags
	appPack.Arg("pack", "Pack type").
		Required().
		StringVar(&cli.PackCLI.PackType)
	appPack.Arg("path", "Target path").
		Required().
		StringVar(&cli.PackCLI.Path)
	appPack.Flag("target", "Warehouse in which to place the ware").
		StringVar(&cli.PackCLI.TargetWarehouseAddr)

	// Filter flags
	appPack.Flag("uid", "Set UID filter [keep, <int>]").
		StringVar(&cli.PackCLI.Filters.Uid)
	appPack.Flag("gid", "Set GID filter [keep, <int>]").
		StringVar(&cli.PackCLI.Filters.Gid)
	appPack.Flag("mtime", "Set mtime filter [keep, <@UNIX>, <RFC3339>]. Will be set to a date if not specified.").
		StringVar(&cli.PackCLI.Filters.Mtime)
	appPack.Flag("sticky", "Keep setuid, setgid, and sticky bits [keep, zero]").
		Default("keep").
		EnumVar(&cli.UnpackCLI.Filters.Sticky,
			"keep", "zero")
}

func configureUnpack(cli *baseCLI, appUnpack *kingpin.CmdClause) {
	// Non-filter flags
	appUnpack.Arg("path", "Target path").
		Required().
		StringVar(&cli.UnpackCLI.Path)
	appUnpack.Arg("ware", "Ware ID").
		Required().
		StringVar(&cli.UnpackCLI.WareID)
	appUnpack.Flag("placer", "Placement mode to use [copy, direct, mount, none]").
		EnumVar(&cli.UnpackCLI.PlacementMode,
			string(rio.Placement_Copy), string(rio.Placement_Direct), string(rio.Placement_Mount), string(rio.Placement_None))
	appUnpack.Flag("source", "Warehouses from which to fetch the ware").
		StringsVar(&cli.UnpackCLI.SourcesWarehouseAddr)

	// Filter flags
	appUnpack.Flag("uid", "Set UID filter [keep, mine, <int>]").
		Default("mine").
		StringVar(&cli.UnpackCLI.Filters.Uid)
	appUnpack.Flag("gid", "Set GID filter [keep, mine, <int>]").
		Default("mine").
		StringVar(&cli.UnpackCLI.Filters.Gid)
	appUnpack.Flag("mtime", "Set mtime filter [keep, <@UNIX>, <RFC3339>]").
		Default("keep").
		StringVar(&cli.UnpackCLI.Filters.Mtime)
	appUnpack.Flag("sticky", "Keep setuid, setgid, and sticky bits [keep, zero]").
		Default("zero").
		EnumVar(&cli.UnpackCLI.Filters.Sticky,
			"keep", "zero")
}

func configureMirror(cli *baseCLI, appMirror *kingpin.CmdClause) {
	appMirror.Arg("ware", "Ware ID").
		Required().
		StringVar(&cli.MirrorCLI.WareID)
	appMirror.Arg("target", "Warehouse in which to place the ware").
		Required().
		StringVar(&cli.MirrorCLI.TargetWarehouseAddr)
	appMirror.Flag("source", "Warehouses from which to fetch the ware").
		StringsVar(&cli.MirrorCLI.SourcesWarehouseAddr)
}

/*
	Blocks until a sigint is received, then calls cancel.
*/
func CancelOnInterrupt(cancel context.CancelFunc) {
	signalChan := make(chan os.Signal)
	signal.Notify(signalChan, os.Interrupt)
	<-signalChan
	cancel()
	close(signalChan)
}

func main() {
	ctx := context.Background()
	exitCode := Main(ctx, os.Args, os.Stdin, os.Stdout, os.Stderr)
	os.Exit(int(exitCode))
}

func Main(ctx context.Context, args []string, stdin io.Reader, stdout, stderr io.Writer) rio.ExitCode {
	exitCode := rio.ExitSuccess
	ctx, cancel := context.WithCancel(ctx)
	go CancelOnInterrupt(cancel)

	cli := baseCLI{}

	app := kingpin.New("rio", "Repeatable I/O")
	app.HelpFlag.Short('h')

	app.UsageWriter(stderr)
	app.ErrorWriter(stderr)

	app.Flag("deadline", "Deadline (RFC3339)").
		StringVar(&cli.Deadline)
	app.Flag("timeout", "Timeout for command").
		DurationVar(&cli.Timeout)
	app.Flag("format", "Output api format").
		Default(FmtDumb).
		EnumVar(&cli.Format, FmtJson, FmtDumb)
	app.Flag("progress-rate", "How frequently to emit progress notification").
		DurationVar(&cli.ProgressRate)
	app.Flag("progress", "Emit progress notification").
		BoolVar(&cli.ProgressEnable)
	app.Flag("test", "Testmodes").
		StringVar(&cli.Test)

	appPack := app.Command("pack", "pack a fileset into a ware")
	configurePack(&cli, appPack)

	appUnpack := app.Command("unpack", "fetch a ware")
	configureUnpack(&cli, appUnpack)

	appMirror := app.Command("mirror", "mirror a ware to another warehouse")
	configureUnpack(&cli, appMirror)

	var termErr error
	app.Terminate(func(status int) {
		termErr = fmt.Errorf("parsing error: %d\n", status)
	})
	cmd, err := app.Parse(args[1:])
	if err != nil {
		fmt.Fprintln(stderr, err)
		return rio.ExitUsage
	}
	if termErr != nil {
		fmt.Fprintln(stderr, termErr)
		return rio.ExitUsage
	}
	var wareID api.WareID
	switch cmd {
	case appPack.FullCommand():
		wareID, err = executePack(ctx, cli)
		SerializeResult(cli.Format, wareID, err, stdout, stderr)
	case appUnpack.FullCommand():
		wareID, err = executeUnpack(ctx, cli)
		SerializeResult(cli.Format, wareID, err, stdout, stderr)
	case appMirror.FullCommand():
		fmt.Fprintln(stderr, "not implemented")
		return rio.ExitNotImplemented
	}

	return exitCode
}

func SerializeResult(format string, wareID api.WareID, resultErr error, stdout io.Writer, stderr io.Writer) {
	result := &rio.Event_Result{
		WareID: wareID,
	}
	result.SetError(resultErr)
	ev := rio.Event{Result: result}
	switch format {
	case FmtJson:
		marshaller := refmt.NewMarshallerAtlased(json.EncodeOptions{}, stdout, rio.Atlas)
		err := marshaller.Marshal(&ev)
		if err != nil {
			panic(err)
		}
	case FmtDumb:
		if resultErr != nil {
			fmt.Fprintln(stderr, resultErr)
		} else {
			fmt.Fprintln(stdout, wareID)
		}
	default:
		panic(fmt.Errorf("rio: invalid format %s", format))
	}
}

func convertWarehouseSlice(slice []string) []api.WarehouseAddr {
	result := make([]api.WarehouseAddr, len(slice))
	for idx, item := range slice {
		result[idx] = api.WarehouseAddr(item)
	}
	return result
}

func executeUnpack(ctx context.Context, cli baseCLI) (api.WareID, error) {
	wareID, err := api.ParseWareID(cli.UnpackCLI.WareID)
	if err != nil {
		return api.WareID{}, err
	}
	var (
		unpackFunc rio.UnpackFunc
	)
	switch wareID.Type {
	case "tar":
		unpackFunc = tar.Unpack
	default:
		return api.WareID{}, Errorf(rio.ErrUsage, "unsupported packtype %q", wareID.Type)
	}
	monitor := rio.Monitor{}
	path := cli.UnpackCLI.Path
	warehouses := convertWarehouseSlice(cli.UnpackCLI.SourcesWarehouseAddr)
	return unpackFunc(ctx, wareID, path, cli.UnpackCLI.Filters, rio.PlacementMode(cli.UnpackCLI.PlacementMode), warehouses, monitor)
}

func executePack(ctx context.Context, cli baseCLI) (api.WareID, error) {
	var (
		packType api.PackType
		packFunc rio.PackFunc
	)
	switch cli.PackCLI.PackType {
	case "tar":
		packType, packFunc = tar.PackType, tar.Pack
	default:
		return api.WareID{}, Errorf(rio.ErrUsage, "unsupported packtype %q", cli.PackCLI.PackType)
	}
	monitor := rio.Monitor{}
	warehouse := api.WarehouseAddr(cli.PackCLI.TargetWarehouseAddr)

	return packFunc(ctx, packType, cli.PackCLI.Path, cli.PackCLI.Filters, warehouse, monitor)
}
<|endoftext|>


<commit_before>package databorad

import (
	"context"

	"github.com/google/go-github/github"
)

type Databorad struct {
	c     *github.Client
	Token string
	Owner string
	Repo  string
}

func NewDataboard(c *github.Client) *Databorad {
	return &Databorad{
		c: c,
	}
}

func (d *Databorad) GetLatestRelease(ctx context.Context) (*github.RepositoryRelease, error) {
	ret, _, err := d.c.Repositories.GetLatestRelease(ctx, d.Owner, d.Repo)
	return ret, err
}
<commit_msg>add New() *Databoard<commit_after>package databorad

import (
	"context"

	"github.com/google/go-github/github"
)

type Databorad struct {
	c     *github.Client
	Token string
	Owner string
	Repo  string
}

func New(c *github.Client) *Databorad {
	return &Databorad{
		c: c,
	}
}

func (d *Databorad) GetLatestRelease(ctx context.Context) (*github.RepositoryRelease, error) {
	ret, _, err := d.c.Repositories.GetLatestRelease(ctx, d.Owner, d.Repo)
	return ret, err
}
<|endoftext|>


<commit_before>/**
 * Copyright (C) 2015 Red Hat, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cmds

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"io/ioutil"
	"strings"

	"github.com/fabric8io/gofabric8/client"
	"github.com/fabric8io/gofabric8/util"
	oclient "github.com/openshift/origin/pkg/client"
	tapi "github.com/openshift/origin/pkg/template/api"
	"github.com/spf13/cobra"
	flag "github.com/spf13/pflag"
	"k8s.io/kubernetes/pkg/api"
	k8sclient "k8s.io/kubernetes/pkg/client"
	"k8s.io/kubernetes/pkg/fields"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/labels"
	"k8s.io/kubernetes/pkg/runtime"
)

type Keypair struct {
	pub  []byte
	priv []byte
}

func NewCmdSecrets(f *cmdutil.Factory) *cobra.Command {
	cmd := &cobra.Command{
		Use:   "secrets",
		Short: "Set up Secrets on your Kubernetes or OpenShift environment",
		Long:  `set up Secrets on your Kubernetes or OpenShift environment`,
		Run: func(cmd *cobra.Command, args []string) {
			c, cfg := client.NewClient(f)
			ns, _, _ := f.DefaultNamespace()
			util.Info("Setting up secrets on your ")
			util.Success(string(util.TypeOfMaster(c)))
			util.Info(" installation at ")
			util.Success(cfg.Host)
			util.Info(" in namespace ")
			util.Successf("%s\n\n", ns)

			if cmd.Flags().Lookup("yes").Value.String() == "false" {
				util.Info("Continue? [Y/n] ")
				cont := util.AskForConfirmation(true)
				if !cont {
					util.Fatal("Cancelled...\n")
				}
			}

			typeOfMaster := util.TypeOfMaster(c)

			if typeOfMaster == util.Kubernetes {
				util.Fatal("Support for Kubernetes not yet available...\n")
			} else {
				oc, _ := client.NewOpenShiftClient(cfg)
				t := getTemplates(oc, ns)

				// get all the Templates and find the annotations on any Pods
				for _, i := range t.Items {
					// convert TemplateList.Objects to Kubernetes resources
					_ = runtime.DecodeList(i.Objects, api.Scheme, runtime.UnstructuredJSONScheme)
					for _, rc := range i.Objects {
						switch rc := rc.(type) {
						case *api.ReplicationController:
							for secretType, secretDataIdentifiers := range rc.Spec.Template.Annotations {
								cerateAndPrintSecrets(secretDataIdentifiers, secretType, c, f, cmd.Flags())
							}
						}
					}
				}
			}
		},
	}
	cmd.PersistentFlags().BoolP("print-import-folder-structure", "", true, "Prints the folder structures that are being used by the template annotations to import secrets")
	cmd.PersistentFlags().BoolP("print-generated-keys", "", false, "Print any generated secrets to the console")
	cmd.PersistentFlags().BoolP("generate-secrets-data", "g", true, "Generate secrets data if secrets cannot be found on the local filesystem")
	return cmd
}

func createSecret(c *k8sclient.Client, f *cmdutil.Factory, flags *flag.FlagSet, secretDataIdentifiers string, secretType string, keysNames []string) (Result, error) {
	var secret = secret(secretDataIdentifiers, secretType, keysNames, flags)
	ns, _, err := f.DefaultNamespace()
	if err != nil {
		return Failure, err
	}
	rs, err := c.Secrets(ns).Create(&secret)
	if rs != nil {
		return Success, err
	}
	return Failure, err
}

func cerateAndPrintSecrets(secretDataIdentifiers string, secretType string, c *k8sclient.Client, fa *cmdutil.Factory, flags *flag.FlagSet) {
	// check to see if multiple public and private keys are needed
	var dataType = strings.Split(secretType, "/")
	switch dataType[1] {
	case "secret-ssh-key":
		items := strings.Split(secretDataIdentifiers, ",")
		for i := range items {
			var name = items[i]
			r, err := createSecret(c, fa, flags, name, secretType, nil)
			printResult(name+" secret", r, err)
		}
	case "secret-ssh-public-key":
		// if this is just a public key then the secret name is at the start of the string
		f := func(c rune) bool {
			return c == ',' || c == '[' || c == ']'
		}
		secrets := strings.FieldsFunc(secretDataIdentifiers, f)
		numOfSecrets := len(secrets)

		var keysNames []string
		if numOfSecrets > 0 {
			// if multiple secrets
			for i := 1; i < numOfSecrets; i++ {
				keysNames = append(keysNames, secrets[i])
			}
		} else {
			// only single secret required
			keysNames[0] = "ssh-key.pub"
		}

		r, err := createSecret(c, fa, flags, secrets[0], secretType, keysNames)

		printResult(secrets[0]+" secret", r, err)

	default:
		gpgKeyName := []string{"secring.gpg"}
		r, err := createSecret(c, fa, flags, secretDataIdentifiers, secretType, gpgKeyName)
		printResult(secretDataIdentifiers+" secret", r, err)
	}
}

func secret(name string, secretType string, keysNames []string, flags *flag.FlagSet) api.Secret {
	return api.Secret{
		ObjectMeta: api.ObjectMeta{
			Name: name,
		},
		Type: api.SecretType(secretType),
		Data: getSecretData(secretType, name, keysNames, flags),
	}
}

func check(e error) {
	if e != nil {
		util.Warnf("Warning: %s\n", e)
	}
}

func logSecretImport(file string) {
	util.Infof("Importing secret: %s\n", file)
}

func getSecretData(secretType string, name string, keysNames []string, flags *flag.FlagSet) map[string][]byte {
	var dataType = strings.Split(secretType, "/")
	var data = make(map[string][]byte)

	switch dataType[1] {
	case "secret-ssh-key":
		if flags.Lookup("print-import-folder-structure").Value.String() == "true" {
			logSecretImport(name + "/ssh-key")
			logSecretImport(name + "/ssh-key.pub")
		}

		sshKey, err1 := ioutil.ReadFile(name + "/ssh-key")
		sshKeyPub, err2 := ioutil.ReadFile(name + "/ssh-key.pub")

		// if we cant find the public and private key to import, and generation flag is set then lets generate the keys
		if (err1 != nil && err2 != nil) && flags.Lookup("generate-secrets-data").Value.String() == "true" {
			util.Info("No secrets found on local filesystem, generating SSH public and private key pair\n")
			keypair := generateSshKeyPair(flags.Lookup("print-generated-keys").Value.String())
			data["ssh-key"] = keypair.priv
			data["ssh-key.pub"] = keypair.pub

		} else if (err1 != nil || err2 != nil) && flags.Lookup("generate-secrets-data").Value.String() == "true" {
			util.Infof("Found some keys to import but with errors so unable to generate SSH public and private key pair. %s\n", name)
			check(err1)
			check(err2)
		} else {
			// if we're not generating the keys and there's an error importing them then still create the secret but with empty data
			check(err1)
			check(err2)

			data["ssh-key"] = sshKey
			data["ssh-key.pub"] = sshKeyPub
		}
		return data

	case "secret-ssh-public-key":

		for i := 0; i < len(keysNames); i++ {
			if flags.Lookup("print-import-folder-structure").Value.String() == "true" {
				logSecretImport(name + "/" + keysNames[i])
			}

			sshPub, err := ioutil.ReadFile(name + "/" + keysNames[i])
			// if we cant find the public key to import and generation flag is set then lets generate the key
			if (err != nil) && flags.Lookup("generate-secrets-data").Value.String() == "true" {
				util.Info("No secrets found on local filesystem, generating SSH public key\n")
				keypair := generateSshKeyPair(flags.Lookup("print-generated-keys").Value.String())
				data[keysNames[i]] = keypair.pub

			} else {
				// if we're not generating the keys and there's an error importing them then still create the secret but with empty data
				check(err)
				data[keysNames[i]] = sshPub
			}
		}
		return data

	case "secret-gpg-key":
		if flags.Lookup("print-import-folder-structure").Value.String() == "true" {
			logSecretImport(name + "/" + keysNames[0])
		}
		gpg, err := ioutil.ReadFile(name + "/" + keysNames[0])
		check(err)

		data[keysNames[0]] = gpg

	default:
		util.Fatalf("No matching data type %s\n", dataType)
	}
	return data
}

func generateSshKeyPair(logGeneratedKeys string) Keypair {

	priv, err := rsa.GenerateKey(rand.Reader, 2014)
	if err != nil {
		util.Fatalf("Error generating key", err)
	}
	err = priv.Validate()
	if err != nil {
		util.Fatalf("Validation failed.", err)
	}

	// Get der format. priv_der []byte
	priv_der := x509.MarshalPKCS1PrivateKey(priv)

	// pem.Block
	// blk pem.Block
	priv_blk := pem.Block{
		Type:    "RSA PRIVATE KEY",
		Headers: nil,
		Bytes:   priv_der,
	}

	// Resultant private key in PEM format.
	// priv_pem string
	priv_pem := string(pem.EncodeToMemory(&priv_blk))

	if logGeneratedKeys == "true" {
		util.Infof(priv_pem)
	}

	// Public Key generation
	pub := priv.PublicKey
	pub_der, err := x509.MarshalPKIXPublicKey(&pub)
	if err != nil {
		util.Fatalf("Failed to get der format for PublicKey.", err)
	}

	pub_blk := pem.Block{
		Type:    "PUBLIC KEY",
		Headers: nil,
		Bytes:   pub_der,
	}
	pub_pem := string(pem.EncodeToMemory(&pub_blk))
	if logGeneratedKeys == "true" {
		util.Infof(pub_pem)
	}

	return Keypair{
		pub:  []byte(pub_pem),
		priv: []byte(priv_pem),
	}
}

func getTemplates(c *oclient.Client, ns string) *tapi.TemplateList {

	rc, err := c.Templates(ns).List(labels.Everything(), fields.Everything())
	if err != nil {
		util.Fatalf("No Templates found in namespace %s\n", ns)
	}
	return rc
}
<commit_msg>Include more gpg keys<commit_after>/**
 * Copyright (C) 2015 Red Hat, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cmds

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"io/ioutil"
	"strings"

	"github.com/fabric8io/gofabric8/client"
	"github.com/fabric8io/gofabric8/util"
	oclient "github.com/openshift/origin/pkg/client"
	tapi "github.com/openshift/origin/pkg/template/api"
	"github.com/spf13/cobra"
	flag "github.com/spf13/pflag"
	"k8s.io/kubernetes/pkg/api"
	k8sclient "k8s.io/kubernetes/pkg/client"
	"k8s.io/kubernetes/pkg/fields"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/labels"
	"k8s.io/kubernetes/pkg/runtime"
)

type Keypair struct {
	pub  []byte
	priv []byte
}

func NewCmdSecrets(f *cmdutil.Factory) *cobra.Command {
	cmd := &cobra.Command{
		Use:   "secrets",
		Short: "Set up Secrets on your Kubernetes or OpenShift environment",
		Long:  `set up Secrets on your Kubernetes or OpenShift environment`,
		Run: func(cmd *cobra.Command, args []string) {
			c, cfg := client.NewClient(f)
			ns, _, _ := f.DefaultNamespace()
			util.Info("Setting up secrets on your ")
			util.Success(string(util.TypeOfMaster(c)))
			util.Info(" installation at ")
			util.Success(cfg.Host)
			util.Info(" in namespace ")
			util.Successf("%s\n\n", ns)

			if cmd.Flags().Lookup("yes").Value.String() == "false" {
				util.Info("Continue? [Y/n] ")
				cont := util.AskForConfirmation(true)
				if !cont {
					util.Fatal("Cancelled...\n")
				}
			}

			typeOfMaster := util.TypeOfMaster(c)

			if typeOfMaster == util.Kubernetes {
				util.Fatal("Support for Kubernetes not yet available...\n")
			} else {
				oc, _ := client.NewOpenShiftClient(cfg)
				t := getTemplates(oc, ns)

				// get all the Templates and find the annotations on any Pods
				for _, i := range t.Items {
					// convert TemplateList.Objects to Kubernetes resources
					_ = runtime.DecodeList(i.Objects, api.Scheme, runtime.UnstructuredJSONScheme)
					for _, rc := range i.Objects {
						switch rc := rc.(type) {
						case *api.ReplicationController:
							for secretType, secretDataIdentifiers := range rc.Spec.Template.Annotations {
								cerateAndPrintSecrets(secretDataIdentifiers, secretType, c, f, cmd.Flags())
							}
						}
					}
				}
			}
		},
	}
	cmd.PersistentFlags().BoolP("print-import-folder-structure", "", true, "Prints the folder structures that are being used by the template annotations to import secrets")
	cmd.PersistentFlags().BoolP("print-generated-keys", "", false, "Print any generated secrets to the console")
	cmd.PersistentFlags().BoolP("generate-secrets-data", "g", true, "Generate secrets data if secrets cannot be found on the local filesystem")
	return cmd
}

func createSecret(c *k8sclient.Client, f *cmdutil.Factory, flags *flag.FlagSet, secretDataIdentifiers string, secretType string, keysNames []string) (Result, error) {
	var secret = secret(secretDataIdentifiers, secretType, keysNames, flags)
	ns, _, err := f.DefaultNamespace()
	if err != nil {
		return Failure, err
	}
	rs, err := c.Secrets(ns).Create(&secret)
	if rs != nil {
		return Success, err
	}
	return Failure, err
}

func cerateAndPrintSecrets(secretDataIdentifiers string, secretType string, c *k8sclient.Client, fa *cmdutil.Factory, flags *flag.FlagSet) {
	// check to see if multiple public and private keys are needed
	var dataType = strings.Split(secretType, "/")
	switch dataType[1] {
	case "secret-ssh-key":
		items := strings.Split(secretDataIdentifiers, ",")
		for i := range items {
			var name = items[i]
			r, err := createSecret(c, fa, flags, name, secretType, nil)
			printResult(name+" secret", r, err)
		}
	case "secret-ssh-public-key":
		// if this is just a public key then the secret name is at the start of the string
		f := func(c rune) bool {
			return c == ',' || c == '[' || c == ']'
		}
		secrets := strings.FieldsFunc(secretDataIdentifiers, f)
		numOfSecrets := len(secrets)

		var keysNames []string
		if numOfSecrets > 0 {
			// if multiple secrets
			for i := 1; i < numOfSecrets; i++ {
				keysNames = append(keysNames, secrets[i])
			}
		} else {
			// only single secret required
			keysNames[0] = "ssh-key.pub"
		}

		r, err := createSecret(c, fa, flags, secrets[0], secretType, keysNames)

		printResult(secrets[0]+" secret", r, err)

	default:
		gpgKeyName := []string{"secring.gpg", "pubring.gpg", "trustdb.gpg"}
		r, err := createSecret(c, fa, flags, secretDataIdentifiers, secretType, gpgKeyName)
		printResult(secretDataIdentifiers+" secret", r, err)
	}
}

func secret(name string, secretType string, keysNames []string, flags *flag.FlagSet) api.Secret {
	return api.Secret{
		ObjectMeta: api.ObjectMeta{
			Name: name,
		},
		Type: api.SecretType(secretType),
		Data: getSecretData(secretType, name, keysNames, flags),
	}
}

func check(e error) {
	if e != nil {
		util.Warnf("Warning: %s\n", e)
	}
}

func logSecretImport(file string) {
	util.Infof("Importing secret: %s\n", file)
}

func getSecretData(secretType string, name string, keysNames []string, flags *flag.FlagSet) map[string][]byte {
	var dataType = strings.Split(secretType, "/")
	var data = make(map[string][]byte)

	switch dataType[1] {
	case "secret-ssh-key":
		if flags.Lookup("print-import-folder-structure").Value.String() == "true" {
			logSecretImport(name + "/ssh-key")
			logSecretImport(name + "/ssh-key.pub")
		}

		sshKey, err1 := ioutil.ReadFile(name + "/ssh-key")
		sshKeyPub, err2 := ioutil.ReadFile(name + "/ssh-key.pub")

		// if we cant find the public and private key to import, and generation flag is set then lets generate the keys
		if (err1 != nil && err2 != nil) && flags.Lookup("generate-secrets-data").Value.String() == "true" {
			util.Info("No secrets found on local filesystem, generating SSH public and private key pair\n")
			keypair := generateSshKeyPair(flags.Lookup("print-generated-keys").Value.String())
			data["ssh-key"] = keypair.priv
			data["ssh-key.pub"] = keypair.pub

		} else if (err1 != nil || err2 != nil) && flags.Lookup("generate-secrets-data").Value.String() == "true" {
			util.Infof("Found some keys to import but with errors so unable to generate SSH public and private key pair. %s\n", name)
			check(err1)
			check(err2)
		} else {
			// if we're not generating the keys and there's an error importing them then still create the secret but with empty data
			check(err1)
			check(err2)

			data["ssh-key"] = sshKey
			data["ssh-key.pub"] = sshKeyPub
		}
		return data

	case "secret-ssh-public-key":

		for i := 0; i < len(keysNames); i++ {
			if flags.Lookup("print-import-folder-structure").Value.String() == "true" {
				logSecretImport(name + "/" + keysNames[i])
			}

			sshPub, err := ioutil.ReadFile(name + "/" + keysNames[i])
			// if we cant find the public key to import and generation flag is set then lets generate the key
			if (err != nil) && flags.Lookup("generate-secrets-data").Value.String() == "true" {
				util.Info("No secrets found on local filesystem, generating SSH public key\n")
				keypair := generateSshKeyPair(flags.Lookup("print-generated-keys").Value.String())
				data[keysNames[i]] = keypair.pub

			} else {
				// if we're not generating the keys and there's an error importing them then still create the secret but with empty data
				check(err)
				data[keysNames[i]] = sshPub
			}
		}
		return data

	case "secret-gpg-key":
		for i := 0; i < len(keysNames); i++ {
			if flags.Lookup("print-import-folder-structure").Value.String() == "true" {
				logSecretImport(name +"/" + keysNames[i])
			}
			gpg, err := ioutil.ReadFile(name +"/" + keysNames[i])
			check(err)

			data[keysNames[i]] = gpg
		}

	default:
		util.Fatalf("No matching data type %s\n", dataType)
	}
	return data
}

func generateSshKeyPair(logGeneratedKeys string) Keypair {

	priv, err := rsa.GenerateKey(rand.Reader, 2014)
	if err != nil {
		util.Fatalf("Error generating key", err)
	}
	err = priv.Validate()
	if err != nil {
		util.Fatalf("Validation failed.", err)
	}

	// Get der format. priv_der []byte
	priv_der := x509.MarshalPKCS1PrivateKey(priv)

	// pem.Block
	// blk pem.Block
	priv_blk := pem.Block{
		Type:    "RSA PRIVATE KEY",
		Headers: nil,
		Bytes:   priv_der,
	}

	// Resultant private key in PEM format.
	// priv_pem string
	priv_pem := string(pem.EncodeToMemory(&priv_blk))

	if logGeneratedKeys == "true" {
		util.Infof(priv_pem)
	}

	// Public Key generation
	pub := priv.PublicKey
	pub_der, err := x509.MarshalPKIXPublicKey(&pub)
	if err != nil {
		util.Fatalf("Failed to get der format for PublicKey.", err)
	}

	pub_blk := pem.Block{
		Type:    "PUBLIC KEY",
		Headers: nil,
		Bytes:   pub_der,
	}
	pub_pem := string(pem.EncodeToMemory(&pub_blk))
	if logGeneratedKeys == "true" {
		util.Infof(pub_pem)
	}

	return Keypair{
		pub:  []byte(pub_pem),
		priv: []byte(priv_pem),
	}
}

func getTemplates(c *oclient.Client, ns string) *tapi.TemplateList {

	rc, err := c.Templates(ns).List(labels.Everything(), fields.Everything())
	if err != nil {
		util.Fatalf("No Templates found in namespace %s\n", ns)
	}
	return rc
}
<|endoftext|>


<commit_before>package main

import "fmt"

func getFloor(s string) (floor int, err error) {
	for _, c := range s {
		switch c {
		case '(':
			floor++
		case ')':
			floor--
		default:
			err = fmt.Errorf("invalid character %q", c)
			return
		}
	}
	return
}

func main() {
	s := "())"
	f, err := getFloor(s)
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Printf("spec: %q\nfloor: %d\n", s, f)
	}
}
<commit_msg>use stderr and exitcode for errors<commit_after>package main

import (
	"fmt"
	"os"
)

func getFloor(s string) (floor int, err error) {
	for _, c := range s {
		switch c {
		case '(':
			floor++
		case ')':
			floor--
		default:
			err = fmt.Errorf("invalid character %q", c)
			return
		}
	}
	return
}

func run() int {
	s := "())"

	f, err := getFloor(s)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return 1
	}

	fmt.Printf("spec: %q\nfloor: %d\n", s, f)
	return 0
}

func main() {
	os.Exit(run())
}
<|endoftext|>


<commit_before>package harvest

import (
	"encoding/json"
	"fmt"
	"strconv"
	"time"
)

// Used for /daily API queries
type DayEntryResponse struct {
	DayEntries []*DayEntry `json:"day_entries"`
}

// Used for both /project/{id}/entries AND /user/{id}/entries
type DayEntryReport []DayEntryReportRow
type DayEntryReportRow struct {
	DayEntry *DayEntry `json:"day_entry"`
}

type DayEntry struct {
	ID                int64           `json:"id"`
	UserID            int64           `json:"user_id"`
	SpentAt           string          `json:"spent_at"`
	CreatedAt         time.Time       `json:"created_at"`
	UpdatedAt         time.Time       `json:"updated_at"`
	ProjectRaw        json.RawMessage `json:"project_id"`
	ProjectID         int64
	TaskRaw           json.RawMessage `json:"task_id"`
	TaskID            int64
	Project           string  `json:"project"`
	Task              string  `json:"task"`
	Client            string  `json:"client"`
	Notes             string  `json:"notes"`
	HoursWithoutTimer int64   `json:"hours_without_timer"`
	Hours             float64 `json:"hours"`
}

// Needed to avoid recursion in UnmarshalJSON
type dayentry DayEntry

func (dayEntry *DayEntry) UnmarshalJSON(b []byte) (err error) {
	d, s, i := dayentry{}, "", float64(0.0)

	if err = json.Unmarshal(b, &d); err == nil {
		if err = json.Unmarshal(d.ProjectRaw, &s); err == nil {
			i, err = strconv.ParseFloat(s, 64)
			d.ProjectID = int64(i)
		}
		if err = json.Unmarshal(d.ProjectRaw, &i); err == nil {
			d.ProjectID = int64(i)
		}
		if err = json.Unmarshal(d.TaskRaw, &s); err == nil {
			i, err = strconv.ParseFloat(s, 64)
			d.TaskID = int64(i)
		}
		if err = json.Unmarshal(d.TaskRaw, &i); err == nil {
			d.TaskID = int64(i)
		}
		*dayEntry = DayEntry(d)
		err = nil
	}
	return
}

func (report DayEntryReport) Entries() []*DayEntry {
	results := make([]*DayEntry, len(report))
	for i, _ := range report {
		results[i] = report[i].DayEntry
	}
	return results
}

func (a *API) GetTodayEntries(args Arguments) ([]*DayEntry, error) {
	dayEntriesResponse := DayEntryResponse{}
	path := fmt.Sprintf("/daily?slim=1")
	err := a.Get(path, args, &dayEntriesResponse)
	return dayEntriesResponse.DayEntries, err
}

func (a *API) GetEntriesForProjectBetween(projectID int64, fromDate time.Time, toDate time.Time, args Arguments) ([]*DayEntry, error) {
	response := make(DayEntryReport, 0)
	from := fromDate.Format("20060102")
	to := fromDate.Format("20060102")
	path := fmt.Sprintf("/projects/%d/entries?from=%s&to=%s", projectID, from, to)
	err := a.Get(path, args, &response)
	return response.Entries(), err
}

func (a *API) GetEntriesForUserBetween(userID int64, fromDate time.Time, toDate time.Time, args Arguments) ([]*DayEntry, error) {
	response := make(DayEntryReport, 0)
	from := fromDate.Format("20060102")
	to := fromDate.Format("20060102")
	path := fmt.Sprintf("/people/%d/entries?from=%s&to=%s", userID, from, to)
	err := a.Get(path, args, &response)
	return response.Entries(), err
}
<commit_msg>Bugfix. From and To were the same date (both used the fromDate).<commit_after>package harvest

import (
	"encoding/json"
	"fmt"
	"strconv"
	"time"
)

// Used for /daily API queries
type DayEntryResponse struct {
	DayEntries []*DayEntry `json:"day_entries"`
}

// Used for both /project/{id}/entries AND /user/{id}/entries
type DayEntryReport []DayEntryReportRow
type DayEntryReportRow struct {
	DayEntry *DayEntry `json:"day_entry"`
}

type DayEntry struct {
	ID                int64           `json:"id"`
	UserID            int64           `json:"user_id"`
	SpentAt           string          `json:"spent_at"`
	CreatedAt         time.Time       `json:"created_at"`
	UpdatedAt         time.Time       `json:"updated_at"`
	ProjectRaw        json.RawMessage `json:"project_id"`
	ProjectID         int64
	TaskRaw           json.RawMessage `json:"task_id"`
	TaskID            int64
	Project           string     `json:"project"`
	Task              string     `json:"task"`
	Client            string     `json:"client"`
	Notes             string     `json:"notes"`
	HoursWithoutTimer int64      `json:"hours_without_timer"`
	Hours             float64    `json:"hours"`
	TimerStartedAt    *time.Time `json:"timer_started_at"`
	AdjustmentRecord  bool       `json:"adjustment_record"`
	IsClosed          bool       `json:"is_closed"`
	IsBilled          bool       `json:"is_billed"`
}

// Needed to avoid recursion in UnmarshalJSON
type dayentry DayEntry

func (dayEntry *DayEntry) UnmarshalJSON(b []byte) (err error) {
	d, s, i := dayentry{}, "", float64(0.0)

	if err = json.Unmarshal(b, &d); err == nil {
		if err = json.Unmarshal(d.ProjectRaw, &s); err == nil {
			i, err = strconv.ParseFloat(s, 64)
			d.ProjectID = int64(i)
		}
		if err = json.Unmarshal(d.ProjectRaw, &i); err == nil {
			d.ProjectID = int64(i)
		}
		if err = json.Unmarshal(d.TaskRaw, &s); err == nil {
			i, err = strconv.ParseFloat(s, 64)
			d.TaskID = int64(i)
		}
		if err = json.Unmarshal(d.TaskRaw, &i); err == nil {
			d.TaskID = int64(i)
		}
		*dayEntry = DayEntry(d)
		err = nil
	}
	return
}

func (report DayEntryReport) Entries() []*DayEntry {
	results := make([]*DayEntry, len(report))
	for i, _ := range report {
		results[i] = report[i].DayEntry
	}
	return results
}

func (a *API) GetTodayEntries(args Arguments) ([]*DayEntry, error) {
	dayEntriesResponse := DayEntryResponse{}
	path := fmt.Sprintf("/daily?slim=1")
	err := a.Get(path, args, &dayEntriesResponse)
	return dayEntriesResponse.DayEntries, err
}

func (a *API) GetEntriesForProjectBetween(projectID int64, fromDate time.Time, toDate time.Time, args Arguments) ([]*DayEntry, error) {
	response := make(DayEntryReport, 0)
	from := fromDate.Format("20060102")
	to := toDate.Format("20060102")
	path := fmt.Sprintf("/projects/%d/entries?from=%s&to=%s", projectID, from, to)
	err := a.Get(path, args, &response)
	return response.Entries(), err
}

func (a *API) GetEntriesForUserBetween(userID int64, fromDate time.Time, toDate time.Time, args Arguments) ([]*DayEntry, error) {
	response := make(DayEntryReport, 0)
	from := fromDate.Format("20060102")
	to := toDate.Format("20060102")
	path := fmt.Sprintf("/people/%d/entries?from=%s&to=%s", userID, from, to)
	err := a.Get(path, args, &response)
	return response.Entries(), err
}
<|endoftext|>


<commit_before>package fileserver

import (
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"

	"github.com/prasannavl/go-gluons/http/handlerutils"
	"github.com/prasannavl/goerror"
	"github.com/prasannavl/goerror/httperror"
)

func HttpFileServer(root http.FileSystem) http.Handler {
	return &httpFileHandler{root}
}

type httpFileHandler struct {
	root http.FileSystem
}

func (f *httpFileHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	err := ServeRequestPath(w, r, f.root)
	if err != nil {
		e := err.(*Err)
		switch e.Kind {
		case ErrRedirect:
			e.Headers().Write(w)
			w.WriteHeader(http.StatusMovedPermanently)
		default:
			http.Error(w, e.Error(), e.Code())
			//  Alternatively, handle the dir listing.
			// case ErrDirFound
			//	stat := e.Dirstat
			//	finished := HandleDirPrelude(w, r, stat)
			//	if !finished {
			//		// Handle the dir listing here.
			//	}
		}
	}
}

func ServeRequestPath(w http.ResponseWriter, r *http.Request, root http.FileSystem) error {
	upath := r.URL.Path
	if !strings.HasPrefix(upath, "/") {
		upath = "/" + upath
		r.URL.Path = upath
	}
	return serveFile(w, r, root, path.Clean(upath), true)
}

func ServeFile(w http.ResponseWriter, r *http.Request, name string) error {
	if ContainsDotDot(r.URL.Path) {
		// Too many programs use r.URL.Path to construct the argument to
		// serveFile. Reject the request under the assumption that happened
		// here and ".." may not be wanted.
		// Note that name might not contain "..", for example if code (still
		// incorrectly) used filepath.Join(myDir, r.URL.Path).
		return newErr(http.StatusBadRequest, ErrBadRequest, "invalid URL path", name, nil)
	}
	dir, file := filepath.Split(name)
	return serveFile(w, r, http.Dir(dir), file, false)
}

func HandleDirPrelude(w http.ResponseWriter, r *http.Request, dirstat os.FileInfo) (finished bool) {
	d := dirstat
	if checkIfModifiedSince(r, d.ModTime()) == condFalse {
		writeNotModified(w)
		return true
	}
	w.Header().Set("Last-Modified", d.ModTime().UTC().Format(http.TimeFormat))
	return false
}

func ContainsDotDot(v string) bool {
	if !strings.Contains(v, "..") {
		return false
	}
	for _, ent := range strings.FieldsFunc(v, isSlashRune) {
		if ent == ".." {
			return true
		}
	}
	return false
}

func isSlashRune(r rune) bool { return r == '/' || r == '\\' }

// name is '/'-separated, not filepath.Separator.
func serveFile(w http.ResponseWriter, r *http.Request, fs http.FileSystem, name string, redirect bool) error {
	const indexPage = "/index.html"

	f, err := fs.Open(name)
	if err != nil {
		return newErr(http.StatusNotFound, ErrFsOpen, "", name, err)
	}
	defer f.Close()

	stat, err := f.Stat()
	if err != nil {
		return newErr(http.StatusInternalServerError, ErrFsStat, "", name, err)
	}

	// redirect .../index.html to .../
	// can't use Redirect() because that would make the path absolute,
	// which would be a problem running under StripPrefix
	if strings.HasSuffix(r.URL.Path, indexPage) {
		return newErrRedirect(r, "./", name)
	}

	if redirect {
		// redirect to canonical path: / at end of directory url
		// r.URL.Path always begins with /
		url := r.URL.Path
		if stat.IsDir() {
			// redirect if the directory name doesn't end in a slash
			if url[len(url)-1] != '/' {
				return newErrRedirect(r, path.Base(url)+"/", name)
			}
		} else {
			if url[len(url)-1] == '/' {
				return newErrRedirect(r, "../"+path.Base(url), name)
			}
		}
	}

	// use contents of index.html for directory, if present
	if stat.IsDir() {
		index := strings.TrimSuffix(name, "/") + indexPage
		ff, err := fs.Open(index)
		if err == nil {
			defer ff.Close()
			dd, err := ff.Stat()
			if err == nil {
				name = index
				stat = dd
				f = ff
			}
		}
	}

	// Still a directory? (we didn't find an index.html file)
	if stat.IsDir() {
		return newErrDirFound(name, stat)
	}

	http.ServeContent(w, r, stat.Name(), stat.ModTime(), f)
	return nil
}

// condResult is the result of an HTTP request precondition check.
// See https://tools.ietf.org/html/rfc7232 section 3.
type condResult int

const (
	condNone  condResult = iota
	condTrue
	condFalse
)

func checkIfModifiedSince(r *http.Request, modtime time.Time) condResult {
	if r.Method != "GET" && r.Method != "HEAD" {
		return condNone
	}
	ims := r.Header.Get("If-Modified-Since")
	if ims == "" || isZeroTime(modtime) {
		return condNone
	}
	t, err := http.ParseTime(ims)
	if err != nil {
		return condNone
	}
	// The Date-Modified header truncates sub-second precision, so
	// use mtime < t+1s instead of mtime <= t to check for unmodified.
	if modtime.Before(t.Add(1 * time.Second)) {
		return condFalse
	}
	return condTrue
}

func writeNotModified(w http.ResponseWriter) {
	// RFC 7232 section 4.1:
	// a sender SHOULD NOT generate representation metadata other than the
	// above listed fields unless said metadata exists for the purpose of
	// guiding cache updates (e.g., Last-Modified might be useful if the
	// response does not have an ETag field).
	h := w.Header()
	delete(h, "Content-Type")
	delete(h, "Content-Length")
	if h.Get("Etag") != "" {
		delete(h, "Last-Modified")
	}
	w.WriteHeader(http.StatusNotModified)
}

var unixEpochTime = time.Unix(0, 0)

// isZeroTime reports whether t is obviously unspecified (either zero or Unix()=0).
func isZeroTime(t time.Time) bool {
	return t.IsZero() || t.Equal(unixEpochTime)
}

type fileServerErrorKind int

const (
	ErrBadRequest fileServerErrorKind = iota
	ErrRedirect
	ErrFsOpen
	ErrFsStat
	ErrDirFound
)

type Err struct {
	httperror.HttpErr
	Kind     fileServerErrorKind
	Pathname string
	Dirstat  os.FileInfo
}

func newErr(statusHint int, kind fileServerErrorKind, message string, pathname string, cause error) *Err {
	var msg *string
	if message != "" {
		msg = &message
	}
	return &Err{
		httperror.HttpErr{
			goerror.CodedErr{
				goerror.GoErr{msg, cause},
				statusHint,
			},
			true,
			nil,
		},
		kind,
		pathname,
		nil,
	}
}

func newErrDirFound(pathname string, dirstat os.FileInfo) *Err {
	e := newErr(http.StatusForbidden, ErrDirFound, "dir found", pathname, nil)
	e.Dirstat = dirstat
	return e
}

func newErrRedirect(r *http.Request, location string, pathname string) *Err {
	path := handlerutils.PathWithOptionalURLQuery(location, r.URL.RawQuery)
	e := newErr(http.StatusMovedPermanently, ErrRedirect, "", pathname, nil)
	e.Headers().Set("Location", path)
	return e
}
<commit_msg>refactor: named composite literals<commit_after>package fileserver

import (
	"net/http"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"

	"github.com/prasannavl/go-gluons/http/handlerutils"
	"github.com/prasannavl/goerror"
	"github.com/prasannavl/goerror/httperror"
)

func HttpFileServer(root http.FileSystem) http.Handler {
	return &httpFileHandler{root}
}

type httpFileHandler struct {
	root http.FileSystem
}

func (f *httpFileHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	err := ServeRequestPath(w, r, f.root)
	if err != nil {
		e := err.(*Err)
		switch e.Kind {
		case ErrRedirect:
			e.Headers().Write(w)
			w.WriteHeader(http.StatusMovedPermanently)
		default:
			http.Error(w, e.Error(), e.Code())
			//  Alternatively, handle the dir listing.
			// case ErrDirFound
			//	stat := e.Dirstat
			//	finished := HandleDirPrelude(w, r, stat)
			//	if !finished {
			//		// Handle the dir listing here.
			//	}
		}
	}
}

func ServeRequestPath(w http.ResponseWriter, r *http.Request, root http.FileSystem) error {
	upath := r.URL.Path
	if !strings.HasPrefix(upath, "/") {
		upath = "/" + upath
		r.URL.Path = upath
	}
	return serveFile(w, r, root, path.Clean(upath), true)
}

func ServeFile(w http.ResponseWriter, r *http.Request, name string) error {
	if ContainsDotDot(r.URL.Path) {
		// Too many programs use r.URL.Path to construct the argument to
		// serveFile. Reject the request under the assumption that happened
		// here and ".." may not be wanted.
		// Note that name might not contain "..", for example if code (still
		// incorrectly) used filepath.Join(myDir, r.URL.Path).
		return newErr(http.StatusBadRequest, ErrBadRequest, "invalid URL path", name, nil)
	}
	dir, file := filepath.Split(name)
	return serveFile(w, r, http.Dir(dir), file, false)
}

func HandleDirPrelude(w http.ResponseWriter, r *http.Request, dirstat os.FileInfo) (finished bool) {
	d := dirstat
	if checkIfModifiedSince(r, d.ModTime()) == condFalse {
		writeNotModified(w)
		return true
	}
	w.Header().Set("Last-Modified", d.ModTime().UTC().Format(http.TimeFormat))
	return false
}

func ContainsDotDot(v string) bool {
	if !strings.Contains(v, "..") {
		return false
	}
	for _, ent := range strings.FieldsFunc(v, isSlashRune) {
		if ent == ".." {
			return true
		}
	}
	return false
}

func isSlashRune(r rune) bool { return r == '/' || r == '\\' }

// name is '/'-separated, not filepath.Separator.
func serveFile(w http.ResponseWriter, r *http.Request, fs http.FileSystem, name string, redirect bool) error {
	const indexPage = "/index.html"

	f, err := fs.Open(name)
	if err != nil {
		return newErr(http.StatusNotFound, ErrFsOpen, "", name, err)
	}
	defer f.Close()

	stat, err := f.Stat()
	if err != nil {
		return newErr(http.StatusInternalServerError, ErrFsStat, "", name, err)
	}

	// redirect .../index.html to .../
	// can't use Redirect() because that would make the path absolute,
	// which would be a problem running under StripPrefix
	if strings.HasSuffix(r.URL.Path, indexPage) {
		return newErrRedirect(r, "./", name)
	}

	if redirect {
		// redirect to canonical path: / at end of directory url
		// r.URL.Path always begins with /
		url := r.URL.Path
		if stat.IsDir() {
			// redirect if the directory name doesn't end in a slash
			if url[len(url)-1] != '/' {
				return newErrRedirect(r, path.Base(url)+"/", name)
			}
		} else {
			if url[len(url)-1] == '/' {
				return newErrRedirect(r, "../"+path.Base(url), name)
			}
		}
	}

	// use contents of index.html for directory, if present
	if stat.IsDir() {
		index := strings.TrimSuffix(name, "/") + indexPage
		ff, err := fs.Open(index)
		if err == nil {
			defer ff.Close()
			dd, err := ff.Stat()
			if err == nil {
				name = index
				stat = dd
				f = ff
			}
		}
	}

	// Still a directory? (we didn't find an index.html file)
	if stat.IsDir() {
		return newErrDirFound(name, stat)
	}

	http.ServeContent(w, r, stat.Name(), stat.ModTime(), f)
	return nil
}

// condResult is the result of an HTTP request precondition check.
// See https://tools.ietf.org/html/rfc7232 section 3.
type condResult int

const (
	condNone condResult = iota
	condTrue
	condFalse
)

func checkIfModifiedSince(r *http.Request, modtime time.Time) condResult {
	if r.Method != "GET" && r.Method != "HEAD" {
		return condNone
	}
	ims := r.Header.Get("If-Modified-Since")
	if ims == "" || isZeroTime(modtime) {
		return condNone
	}
	t, err := http.ParseTime(ims)
	if err != nil {
		return condNone
	}
	// The Date-Modified header truncates sub-second precision, so
	// use mtime < t+1s instead of mtime <= t to check for unmodified.
	if modtime.Before(t.Add(1 * time.Second)) {
		return condFalse
	}
	return condTrue
}

func writeNotModified(w http.ResponseWriter) {
	// RFC 7232 section 4.1:
	// a sender SHOULD NOT generate representation metadata other than the
	// above listed fields unless said metadata exists for the purpose of
	// guiding cache updates (e.g., Last-Modified might be useful if the
	// response does not have an ETag field).
	h := w.Header()
	delete(h, "Content-Type")
	delete(h, "Content-Length")
	if h.Get("Etag") != "" {
		delete(h, "Last-Modified")
	}
	w.WriteHeader(http.StatusNotModified)
}

var unixEpochTime = time.Unix(0, 0)

// isZeroTime reports whether t is obviously unspecified (either zero or Unix()=0).
func isZeroTime(t time.Time) bool {
	return t.IsZero() || t.Equal(unixEpochTime)
}

type fileServerErrorKind int

const (
	ErrBadRequest fileServerErrorKind = iota
	ErrRedirect
	ErrFsOpen
	ErrFsStat
	ErrDirFound
)

type Err struct {
	httperror.HttpErr
	Kind     fileServerErrorKind
	Pathname string
	Dirstat  os.FileInfo
}

func newErr(statusHint int, kind fileServerErrorKind, message string, pathname string, cause error) *Err {
	var msg *string
	if message != "" {
		msg = &message
	}
	return &Err{
		httperror.HttpErr{
			CodedErr: goerror.CodedErr{
				GoErr:   goerror.GoErr{Msg: msg, Inner: cause},
				ErrCode: statusHint,
			},
			Stop:       true,
			HeadersMap: nil,
		},
		kind,
		pathname,
		nil,
	}
}

func newErrDirFound(pathname string, dirstat os.FileInfo) *Err {
	e := newErr(http.StatusForbidden, ErrDirFound, "dir found", pathname, nil)
	e.Dirstat = dirstat
	return e
}

func newErrRedirect(r *http.Request, location string, pathname string) *Err {
	path := handlerutils.PathWithOptionalURLQuery(location, r.URL.RawQuery)
	e := newErr(http.StatusMovedPermanently, ErrRedirect, "", pathname, nil)
	e.Headers().Set("Location", path)
	return e
}
<|endoftext|>


<commit_before>package dispatcher

import (
	"bytes"
	"encoding/json"

	bslcaction "github.com/cloudfoundry/bosh-softlayer-cpi/action"
	bslcapi "github.com/cloudfoundry/bosh-softlayer-cpi/api"
	bslcommon "github.com/cloudfoundry/bosh-softlayer-cpi/softlayer/common"
	boshlog "github.com/cloudfoundry/bosh-utils/logger"

	"fmt"
	"strings"
)

const (
	jsonLogTag = "json"

	jsonCloudErrorType          = "Bosh::Clouds::CloudError"
	jsonCpiErrorType            = "Bosh::Clouds::CpiError"
	jsonNotImplementedErrorType = "Bosh::Clouds::NotImplemented"
)

type Request struct {
	Method    string        `json:"method"`
	Arguments []interface{} `json:"arguments"`

	// context key is ignored
}

type Response struct {
	Result interface{}    `json:"result"`
	Error  *ResponseError `json:"error"`

	Log string `json:"log"`
}

type ResponseError struct {
	Type    string `json:"type"`
	Message string `json:"message"`

	CanRetry bool `json:"ok_to_retry"`
}

type JSON struct {
	actionFactory bslcaction.Factory
	caller        Caller
	logger        boshlog.Logger
}

func NewJSON(
	actionFactory bslcaction.Factory,
	caller Caller,
	logger boshlog.Logger,
) JSON {
	return JSON{
		actionFactory: actionFactory,
		caller:        caller,
		logger:        logger,
	}
}

func (c JSON) Dispatch(reqBytes []byte) []byte {
	var req Request

	c.logger.DebugWithDetails(jsonLogTag, "Request bytes", string(reqBytes))
	digitalDecoder := json.NewDecoder(bytes.NewReader(reqBytes))
	digitalDecoder.UseNumber()
	if err := digitalDecoder.Decode(&req); err != nil {
		return c.buildCpiError("Must provide valid JSON payload")
	}

	c.logger.DebugWithDetails(jsonLogTag, "Deserialized request", req)

	// Will remove this line of code after we make sure LocalDiskFlag is defined properly in all deployment manifest files
	c.localDiskFlagNotSet(fmt.Sprintf("%s", req))

	if req.Method == "" {
		return c.buildCpiError("Must provide method key")
	}

	if req.Arguments == nil {
		return c.buildCpiError("Must provide arguments key")
	}

	action, err := c.actionFactory.Create(req.Method)
	if err != nil {
		return c.buildNotImplementedError()
	}

	result, err := c.caller.Call(action, req.Arguments)
	if err != nil {
		return c.buildCloudError(err)
	}

	resp := Response{
		Result: result,
	}

	c.logger.DebugWithDetails(jsonLogTag, "Deserialized response", resp)

	respBytes, err := json.Marshal(resp)
	if err != nil {
		return c.buildCpiError("Failed to serialize result")
	}

	c.logger.DebugWithDetails(jsonLogTag, "Response bytes", string(respBytes))

	return respBytes
}

func (c JSON) buildCloudError(err error) []byte {
	respErr := Response{
		Error: &ResponseError{},
	}

	if typedErr, ok := err.(bslcapi.CloudError); ok {
		respErr.Error.Type = typedErr.Type()
	} else {
		respErr.Error.Type = jsonCloudErrorType
	}

	respErr.Error.Message = err.Error()

	if typedErr, ok := err.(bslcapi.RetryableError); ok {
		respErr.Error.CanRetry = typedErr.CanRetry()
	}

	respErrBytes, err := json.Marshal(respErr)
	if err != nil {
		panic(err)
	}

	c.logger.DebugWithDetails(jsonLogTag, "CloudError response bytes", string(respErrBytes))

	return respErrBytes
}

func (c JSON) buildCpiError(message string) []byte {
	respErr := Response{
		Error: &ResponseError{
			Type:    jsonCpiErrorType,
			Message: message,
		},
	}

	respErrBytes, err := json.Marshal(respErr)
	if err != nil {
		panic(err)
	}

	c.logger.DebugWithDetails(jsonLogTag, "CpiError response bytes", string(respErrBytes))

	return respErrBytes
}

func (c JSON) buildNotImplementedError() []byte {
	respErr := Response{
		Error: &ResponseError{
			Type:    jsonNotImplementedErrorType,
			Message: "Must call implemented method",
		},
	}

	respErrBytes, err := json.Marshal(respErr)
	if err != nil {
		panic(err)
	}

	c.logger.DebugWithDetails(jsonLogTag, "NotImplementedError response bytes", string(respErrBytes))

	return respErrBytes
}

// This function is used to set the default value of localDiskFlag to true if it is not specified in the input json file.
// This function will be removed if we can ensure LocalDiskFlag property is set in place in all deployment manifest
func (c JSON) localDiskFlagNotSet(reqString string) {
	if strings.Contains(strings.ToUpper(reqString), strings.ToUpper("localDiskFlag")) {
		bslcommon.LocalDiskFlagNotSet = false
	} else {
		bslcommon.LocalDiskFlagNotSet = true
	}
}
<commit_msg>Made complier happy<commit_after>package dispatcher

import (
	"bytes"
	"encoding/json"

	bslcaction "github.com/cloudfoundry/bosh-softlayer-cpi/action"
	bslcapi "github.com/cloudfoundry/bosh-softlayer-cpi/api"
	boshlog "github.com/cloudfoundry/bosh-utils/logger"
	slhelper "github.com/cloudfoundry/bosh-softlayer-cpi/softlayer/common/helper"

	"fmt"
	"strings"
)

const (
	jsonLogTag = "json"

	jsonCloudErrorType          = "Bosh::Clouds::CloudError"
	jsonCpiErrorType            = "Bosh::Clouds::CpiError"
	jsonNotImplementedErrorType = "Bosh::Clouds::NotImplemented"
)

type Request struct {
	Method    string        `json:"method"`
	Arguments []interface{} `json:"arguments"`

	// context key is ignored
}

type Response struct {
	Result interface{}    `json:"result"`
	Error  *ResponseError `json:"error"`

	Log string `json:"log"`
}

type ResponseError struct {
	Type    string `json:"type"`
	Message string `json:"message"`

	CanRetry bool `json:"ok_to_retry"`
}

type JSON struct {
	actionFactory bslcaction.Factory
	caller        Caller
	logger        boshlog.Logger
}

func NewJSON(
	actionFactory bslcaction.Factory,
	caller Caller,
	logger boshlog.Logger,
) JSON {
	return JSON{
		actionFactory: actionFactory,
		caller:        caller,
		logger:        logger,
	}
}

func (c JSON) Dispatch(reqBytes []byte) []byte {
	var req Request

	c.logger.DebugWithDetails(jsonLogTag, "Request bytes", string(reqBytes))
	digitalDecoder := json.NewDecoder(bytes.NewReader(reqBytes))
	digitalDecoder.UseNumber()
	if err := digitalDecoder.Decode(&req); err != nil {
		return c.buildCpiError("Must provide valid JSON payload")
	}

	c.logger.DebugWithDetails(jsonLogTag, "Deserialized request", req)

	// Will remove this line of code after we make sure LocalDiskFlag is defined properly in all deployment manifest files
	c.localDiskFlagNotSet(fmt.Sprintf("%s", req))

	if req.Method == "" {
		return c.buildCpiError("Must provide method key")
	}

	if req.Arguments == nil {
		return c.buildCpiError("Must provide arguments key")
	}

	action, err := c.actionFactory.Create(req.Method)
	if err != nil {
		return c.buildNotImplementedError()
	}

	result, err := c.caller.Call(action, req.Arguments)
	if err != nil {
		return c.buildCloudError(err)
	}

	resp := Response{
		Result: result,
	}

	c.logger.DebugWithDetails(jsonLogTag, "Deserialized response", resp)

	respBytes, err := json.Marshal(resp)
	if err != nil {
		return c.buildCpiError("Failed to serialize result")
	}

	c.logger.DebugWithDetails(jsonLogTag, "Response bytes", string(respBytes))

	return respBytes
}

func (c JSON) buildCloudError(err error) []byte {
	respErr := Response{
		Error: &ResponseError{},
	}

	if typedErr, ok := err.(bslcapi.CloudError); ok {
		respErr.Error.Type = typedErr.Type()
	} else {
		respErr.Error.Type = jsonCloudErrorType
	}

	respErr.Error.Message = err.Error()

	if typedErr, ok := err.(bslcapi.RetryableError); ok {
		respErr.Error.CanRetry = typedErr.CanRetry()
	}

	respErrBytes, err := json.Marshal(respErr)
	if err != nil {
		panic(err)
	}

	c.logger.DebugWithDetails(jsonLogTag, "CloudError response bytes", string(respErrBytes))

	return respErrBytes
}

func (c JSON) buildCpiError(message string) []byte {
	respErr := Response{
		Error: &ResponseError{
			Type:    jsonCpiErrorType,
			Message: message,
		},
	}

	respErrBytes, err := json.Marshal(respErr)
	if err != nil {
		panic(err)
	}

	c.logger.DebugWithDetails(jsonLogTag, "CpiError response bytes", string(respErrBytes))

	return respErrBytes
}

func (c JSON) buildNotImplementedError() []byte {
	respErr := Response{
		Error: &ResponseError{
			Type:    jsonNotImplementedErrorType,
			Message: "Must call implemented method",
		},
	}

	respErrBytes, err := json.Marshal(respErr)
	if err != nil {
		panic(err)
	}

	c.logger.DebugWithDetails(jsonLogTag, "NotImplementedError response bytes", string(respErrBytes))

	return respErrBytes
}

// This function is used to set the default value of localDiskFlag to true if it is not specified in the input json file.
// This function will be removed if we can ensure LocalDiskFlag property is set in place in all deployment manifest
func (c JSON) localDiskFlagNotSet(reqString string) {
	if strings.Contains(strings.ToUpper(reqString), strings.ToUpper("localDiskFlag")) {
		slhelper.LocalDiskFlagNotSet = false
	} else {
		slhelper.LocalDiskFlagNotSet = true
	}
}
<|endoftext|>