<commit_before>package asset

import (
	"fmt"
	"math"
	"strings"

	"github.com/go-gl/mathgl/mgl32"
	"github.com/omustardo/gome/model/mesh"
)

// This code is originally based on https://gist.github.com/davemackintosh/67959fa9dfd9018d79a4
// and https://en.wikipedia.org/wiki/Wavefront_.obj_file
// and http://www.opengl-tutorial.org/beginners-tutorials/tutorial-7-model-loading/
//
// Unfortunately, found this list after implementing... I should probably use one of these instead:
// Other Golang OBJ loaders from https://github.com/mmchugh/gomobile-examples/issues/6
//https://github.com/go-qml/qml/blob/v1/examples/gopher/wavefront.go https://github.com/peterhellberg/wavefront/blob/master/wavefront.go
//https://github.com/Stymphalian/go.gl/blob/master/jgl/obj_filereader.go
//https://github.com/tobscher/go-three/blob/master/loaders/obj.go
//https://github.com/adam000/read-obj/tree/master/obj https://github.com/adam000/read-obj/blob/master/mtl/mtl.go
//https://github.com/udhos/negentropia/blob/master/webserv/src/negentropia/world/obj/obj.go
//https://github.com/fogleman/pt/blob/master/pt/obj.go
//https://github.com/luxengine/lux/blob/master/utils/objloader.go
//https://github.com/gmacd/obj/blob/master/obj.go
//https://github.com/gographics/goviewer/blob/master/loader/wavefront.go
//https://github.com/sf1/go3dm
//https://github.com/peterudkmaya11/lux/blob/master/utils/objloader.go

// LoadOBJ creates a mesh from an obj file.
func LoadOBJ(path string) (mesh.Mesh, error) {
	return loadOBJ(path, false)
}

// LoadOBJNormalized creates a mesh from an obj file.
// The loaded OBJ is scaled to be as large as possible while still fitting in a unit sphere.
func LoadOBJNormalized(path string) (mesh.Mesh, error) {
	return loadOBJ(path, true)
}

func loadOBJ(path string, normalize bool) (mesh.Mesh, error) {
	fileData, err := loadFile(path)
	if err != nil {
		return mesh.Mesh{}, err
	}
	verts, normals, textureCoords, err := loadOBJData(fileData)
	if err != nil {
		return mesh.Mesh{}, fmt.Errorf("Error loading %s: %v", path, err)
	}

	if normalize {
		// Normalize input vertices so the input mesh is exactly as large as it can be while still fitting in a unit sphere.
		// This makes scaling meshes relative to each other very easy to think about.
		// TODO: Consider centering meshes when resizing them to avoid empty space making them smaller than necessary.
		maxLength := float32(math.SmallestNonzeroFloat32)
		for _, v := range verts {
			if length := v.Len(); length > maxLength {
				maxLength = length
			}
		}
		for i := range verts {
			verts[i] = verts[i].Mul(1 / maxLength)
		}
	}
	return mesh.NewMeshFromArrays(verts, normals, textureCoords)
}

func loadOBJData(data []byte) (verts, normals []mgl32.Vec3, textureCoords []mgl32.Vec2, err error) {
	lines := strings.Split(string(data), "\n")

	// Indices are used by the OBJ file format to declare full triangles via the 'f'ace tag.
	// All of these indices are converted back to the values that they reference and stored in gl buffers to be returned.
	var vertIndices, uvIndices, normalIndices []uint16

	for lineNum, line := range lines {
		lineNum++ // numbering is for debug printing, and humans think of files as starting with line 1.

		line = strings.TrimSpace(line)
		if len(line) == 0 {
			continue
		}

		// Scan the type field.
		var lineType string
		count, err := fmt.Sscanf(line, "%s", &lineType)
		if err != nil {
			return nil, nil, nil, err
		}
		if count != 1 {
			return nil, nil, nil, fmt.Errorf("at line #%d, unable to get line type: %v", lineNum, err)
		}
		// Trim off the text that has been read.
		line = strings.TrimSpace(line[len(lineType):])

		switch lineType {
		// VERTICES.
		case "v":
			vec := mgl32.Vec3{}
			count, err := fmt.Sscanf(line, "%f %f %f", &vec[0], &vec[1], &vec[2])
			if err != nil {
				return nil, nil, nil, fmt.Errorf("at line #%d, error reading vertices: %v", lineNum, err)
			}
			if count != 3 {
				return nil, nil, nil, fmt.Errorf("at line #%d, got %d values for vertices. Expected 3", lineNum, count)
			}
			verts = append(verts, vec)

		// NORMALS.
		case "vn":
			vec := mgl32.Vec3{}
			count, err := fmt.Sscanf(line, "%f %f %f", &vec[0], &vec[1], &vec[2])
			if err != nil {
				return nil, nil, nil, fmt.Errorf("at line #%d, error reading normals: %v", lineNum, err)
			}
			if count != 3 {
				return nil, nil, nil, fmt.Errorf("at line #%d, got %d values for normals. Expected 3", lineNum, count)
			}
			normals = append(normals, vec)

		// TEXTURE VERTICES.
		case "vt":
			vec := mgl32.Vec2{}
			count, err := fmt.Sscanf(line, "%f %f", &vec[0], &vec[1])
			if err != nil {
				return nil, nil, nil, fmt.Errorf("at line #%d, error reading texture vertices: %v", lineNum, err)
			}
			if count != 2 {
				return nil, nil, nil, fmt.Errorf("at line #%d, got %v values for texture vertices. Expected 2", lineNum, count)
			}
			textureCoords = append(textureCoords, vec)

		// FACES.
		case "f":
			// Input expected to be integer indices that refer to data read into the 'v','vt', and 'vn' fields (1 based indexing).
			// Subtract 1 as they are read in to match standard 0 based indexing.
			var vec, uv, norm [3]uint16

			var count, expectedCount int
			switch {
			case strings.Contains(line, "//"):
				count, err = fmt.Sscanf(line, "%d//%d %d//%d %d//%d", &vec[0], &norm[0], &vec[1], &norm[1], &vec[2], &norm[2])
				vertIndices = append(vertIndices, vec[0]-1, vec[1]-1, vec[2]-1)
				normalIndices = append(normalIndices, norm[0]-1, norm[1]-1, norm[2]-1)
				expectedCount = 6
			case strings.Count(line, "/") == 3:
				count, err = fmt.Sscanf(line, "%d/%d %d/%d %d/%d", &vec[0], &uv[0], &vec[1], &uv[1], &vec[2], &uv[2])
				vertIndices = append(vertIndices, vec[0]-1, vec[1]-1, vec[2]-1)
				uvIndices = append(uvIndices, uv[0]-1, uv[1]-1, uv[2]-1)
				expectedCount = 6
			case strings.Count(line, "/") == 6:
				count, err = fmt.Sscanf(line, "%d/%d/%d %d/%d/%d %d/%d/%d", &vec[0], &uv[0], &norm[0], &vec[1], &uv[1], &norm[1], &vec[2], &uv[2], &norm[2])
				vertIndices = append(vertIndices, vec[0]-1, vec[1]-1, vec[2]-1)
				uvIndices = append(uvIndices, uv[0]-1, uv[1]-1, uv[2]-1)
				normalIndices = append(normalIndices, norm[0]-1, norm[1]-1, norm[2]-1)
				expectedCount = 9
			default:
				return nil, nil, nil, fmt.Errorf("at line #%d, error reading indices: %v", lineNum, err)
			}
			if err != nil {
				return nil, nil, nil, fmt.Errorf("at line #%d, error reading indices: %v", lineNum, err)
			}
			if count != expectedCount {
				return nil, nil, nil, fmt.Errorf("at line #%d, got %d values for vec,uv,norm. Expected %d", lineNum, count, expectedCount)
			}

		// COMMENT
		case "#":
		// Do nothing
		case "g":
		// TODO: Support groups
		default:
			// Do nothing - ignore unknown fields
		}
	}

	if vertIndices != nil {
		if normalIndices != nil && len(vertIndices) != len(normalIndices) {
			return nil, nil, nil, fmt.Errorf("read in vertex and normal indices, but counts don't match: %d vs %d", len(vertIndices), len(normalIndices))
		}
		if uvIndices != nil && len(vertIndices) != len(uvIndices) {
			return nil, nil, nil, fmt.Errorf("read in vertex and texture coord indices, but counts don't match: %d vs %d", len(vertIndices), len(uvIndices))
		}
	}

	// If vertices were provided with an index buffer, transform it into a list of raw vertices.
	if vertIndices != nil {
		verts, err = indicesToValues(vertIndices, verts)
		if err != nil {
			return nil, nil, nil, err
		}
	}
	if normalIndices != nil {
		normals, err = indicesToValues(normalIndices, normals)
		if err != nil {
			return nil, nil, nil, err
		}
	}
	if uvIndices != nil {
		textureCoordValues := make([]mgl32.Vec2, len(uvIndices))
		for i, index := range uvIndices {
			if int(index) >= len(textureCoords) {
				return nil, nil, nil, fmt.Errorf("unexpected Texture Coordinate index %d, out of range of the provided %d texture coordinates", index+1, len(textureCoords))
			}
			textureCoordValues[i] = textureCoords[index]
		}
		textureCoords = textureCoordValues
	}
	return verts, normals, textureCoords, nil
}

// indicesToValues takes a list of indices and the data they reference, and returns the raw list of referenced data
// with all of the duplicate values that entails.
// Note that the indices are expected to be zero based, even though OBJ files use 1 based indexing.
func indicesToValues(indices []uint16, data []mgl32.Vec3) ([]mgl32.Vec3, error) {
	values := make([]mgl32.Vec3, len(indices))
	for i, index := range indices {
		if int(index) >= len(data) {
			return nil, fmt.Errorf("unexpected index %d, out of range of the provided %d data", index+1, len(data))
		}
		values[i] = data[index]
	}
	return values, nil
}
<commit_msg>Add TODO for loading materials in OBJ files<commit_after>package asset

import (
	"fmt"
	"math"
	"strings"

	"github.com/go-gl/mathgl/mgl32"
	"github.com/omustardo/gome/model/mesh"
)

// This code is originally based on https://gist.github.com/davemackintosh/67959fa9dfd9018d79a4
// and https://en.wikipedia.org/wiki/Wavefront_.obj_file
// and http://www.opengl-tutorial.org/beginners-tutorials/tutorial-7-model-loading/
//
// Unfortunately, found this list after implementing... I should probably use one of these instead:
// Other Golang OBJ loaders from https://github.com/mmchugh/gomobile-examples/issues/6
//https://github.com/go-qml/qml/blob/v1/examples/gopher/wavefront.go https://github.com/peterhellberg/wavefront/blob/master/wavefront.go
//https://github.com/Stymphalian/go.gl/blob/master/jgl/obj_filereader.go
//https://github.com/tobscher/go-three/blob/master/loaders/obj.go
//https://github.com/adam000/read-obj/tree/master/obj https://github.com/adam000/read-obj/blob/master/mtl/mtl.go
//https://github.com/udhos/negentropia/blob/master/webserv/src/negentropia/world/obj/obj.go
//https://github.com/fogleman/pt/blob/master/pt/obj.go
//https://github.com/luxengine/lux/blob/master/utils/objloader.go
//https://github.com/gmacd/obj/blob/master/obj.go
//https://github.com/gographics/goviewer/blob/master/loader/wavefront.go
//https://github.com/sf1/go3dm
//https://github.com/peterudkmaya11/lux/blob/master/utils/objloader.go
//
// TODO: Consider supporting materials (mtl tag). Info on specific MTL tags: http://nendowingsmirai.yuku.com/forum/viewtopic/id/1723#.WHmLqhsrIuU
// Ns = Phong specular component.
// Kd = Diffuse color weighted by the diffuse coefficient.
// Ka = Ambient color weighted by the ambient coefficient.
// Ks = Specular color weighted by the specular coefficient.
// d = Dissolve factor (pseudo-transparency). Values are from 0-1. 0 is completely transparent, 1 is opaque.
// Ni = Refraction index. Values range from 1 upwards. A value of 1 will cause no refraction. A higher value implies refraction.
// illum = (0, 1, or 2) 0 to disable lighting, 1 for ambient & diffuse only (specular color set to black), 2 for full lighting (see below)
// sharpness = ?
// map_Kd = Diffuse color texture map.
// map_Ks = Specular color texture map.
// map_Ka = Ambient color texture map.
// map_Bump = Bump texture map.
// map_d = Opacity texture map.
// refl = reflection type and filename (?)

// LoadOBJ creates a mesh from an obj file.
func LoadOBJ(path string) (mesh.Mesh, error) {
	return loadOBJ(path, false)
}

// LoadOBJNormalized creates a mesh from an obj file.
// The loaded OBJ is scaled to be as large as possible while still fitting in a unit sphere.
func LoadOBJNormalized(path string) (mesh.Mesh, error) {
	return loadOBJ(path, true)
}

func loadOBJ(path string, normalize bool) (mesh.Mesh, error) {
	fileData, err := loadFile(path)
	if err != nil {
		return mesh.Mesh{}, err
	}
	verts, normals, textureCoords, err := loadOBJData(fileData)
	if err != nil {
		return mesh.Mesh{}, fmt.Errorf("Error loading %s: %v", path, err)
	}

	if normalize {
		// Normalize input vertices so the input mesh is exactly as large as it can be while still fitting in a unit sphere.
		// This makes scaling meshes relative to each other very easy to think about.
		// TODO: Consider centering meshes when resizing them to avoid empty space making them smaller than necessary.
		maxLength := float32(math.SmallestNonzeroFloat32)
		for _, v := range verts {
			if length := v.Len(); length > maxLength {
				maxLength = length
			}
		}
		for i := range verts {
			verts[i] = verts[i].Mul(1 / maxLength)
		}
	}
	return mesh.NewMeshFromArrays(verts, normals, textureCoords)
}

func loadOBJData(data []byte) (verts, normals []mgl32.Vec3, textureCoords []mgl32.Vec2, err error) {
	lines := strings.Split(string(data), "\n")

	// Indices are used by the OBJ file format to declare full triangles via the 'f'ace tag.
	// All of these indices are converted back to the values that they reference and stored in gl buffers to be returned.
	var vertIndices, uvIndices, normalIndices []uint16

	for lineNum, line := range lines {
		lineNum++ // numbering is for debug printing, and humans think of files as starting with line 1.

		line = strings.TrimSpace(line)
		if len(line) == 0 {
			continue
		}

		// Scan the type field.
		var lineType string
		count, err := fmt.Sscanf(line, "%s", &lineType)
		if err != nil {
			return nil, nil, nil, err
		}
		if count != 1 {
			return nil, nil, nil, fmt.Errorf("at line #%d, unable to get line type: %v", lineNum, err)
		}
		// Trim off the text that has been read.
		line = strings.TrimSpace(line[len(lineType):])

		switch lineType {
		// VERTICES.
		case "v":
			vec := mgl32.Vec3{}
			count, err := fmt.Sscanf(line, "%f %f %f", &vec[0], &vec[1], &vec[2])
			if err != nil {
				return nil, nil, nil, fmt.Errorf("at line #%d, error reading vertices: %v", lineNum, err)
			}
			if count != 3 {
				return nil, nil, nil, fmt.Errorf("at line #%d, got %d values for vertices. Expected 3", lineNum, count)
			}
			verts = append(verts, vec)

		// NORMALS.
		case "vn":
			vec := mgl32.Vec3{}
			count, err := fmt.Sscanf(line, "%f %f %f", &vec[0], &vec[1], &vec[2])
			if err != nil {
				return nil, nil, nil, fmt.Errorf("at line #%d, error reading normals: %v", lineNum, err)
			}
			if count != 3 {
				return nil, nil, nil, fmt.Errorf("at line #%d, got %d values for normals. Expected 3", lineNum, count)
			}
			normals = append(normals, vec)

		// TEXTURE VERTICES.
		case "vt":
			vec := mgl32.Vec2{}
			count, err := fmt.Sscanf(line, "%f %f", &vec[0], &vec[1])
			if err != nil {
				return nil, nil, nil, fmt.Errorf("at line #%d, error reading texture vertices: %v", lineNum, err)
			}
			if count != 2 {
				return nil, nil, nil, fmt.Errorf("at line #%d, got %v values for texture vertices. Expected 2", lineNum, count)
			}
			textureCoords = append(textureCoords, vec)

		// FACES.
		case "f":
			// Input expected to be integer indices that refer to data read into the 'v','vt', and 'vn' fields (1 based indexing).
			// Subtract 1 as they are read in to match standard 0 based indexing.
			var vec, uv, norm [3]uint16

			var count, expectedCount int
			switch {
			case strings.Contains(line, "//"):
				count, err = fmt.Sscanf(line, "%d//%d %d//%d %d//%d", &vec[0], &norm[0], &vec[1], &norm[1], &vec[2], &norm[2])
				vertIndices = append(vertIndices, vec[0]-1, vec[1]-1, vec[2]-1)
				normalIndices = append(normalIndices, norm[0]-1, norm[1]-1, norm[2]-1)
				expectedCount = 6
			case strings.Count(line, "/") == 3:
				count, err = fmt.Sscanf(line, "%d/%d %d/%d %d/%d", &vec[0], &uv[0], &vec[1], &uv[1], &vec[2], &uv[2])
				vertIndices = append(vertIndices, vec[0]-1, vec[1]-1, vec[2]-1)
				uvIndices = append(uvIndices, uv[0]-1, uv[1]-1, uv[2]-1)
				expectedCount = 6
			case strings.Count(line, "/") == 6:
				count, err = fmt.Sscanf(line, "%d/%d/%d %d/%d/%d %d/%d/%d", &vec[0], &uv[0], &norm[0], &vec[1], &uv[1], &norm[1], &vec[2], &uv[2], &norm[2])
				vertIndices = append(vertIndices, vec[0]-1, vec[1]-1, vec[2]-1)
				uvIndices = append(uvIndices, uv[0]-1, uv[1]-1, uv[2]-1)
				normalIndices = append(normalIndices, norm[0]-1, norm[1]-1, norm[2]-1)
				expectedCount = 9
			default:
				return nil, nil, nil, fmt.Errorf("at line #%d, error reading indices: %v", lineNum, err)
			}
			if err != nil {
				return nil, nil, nil, fmt.Errorf("at line #%d, error reading indices: %v", lineNum, err)
			}
			if count != expectedCount {
				return nil, nil, nil, fmt.Errorf("at line #%d, got %d values for vec,uv,norm. Expected %d", lineNum, count, expectedCount)
			}

		// COMMENT
		case "#":
		// Do nothing
		case "g":
		// TODO: Support groups
		default:
			// Do nothing - ignore unknown fields
		}
	}

	if vertIndices != nil {
		if normalIndices != nil && len(vertIndices) != len(normalIndices) {
			return nil, nil, nil, fmt.Errorf("read in vertex and normal indices, but counts don't match: %d vs %d", len(vertIndices), len(normalIndices))
		}
		if uvIndices != nil && len(vertIndices) != len(uvIndices) {
			return nil, nil, nil, fmt.Errorf("read in vertex and texture coord indices, but counts don't match: %d vs %d", len(vertIndices), len(uvIndices))
		}
	}

	// If vertices were provided with an index buffer, transform it into a list of raw vertices.
	if vertIndices != nil {
		verts, err = indicesToValues(vertIndices, verts)
		if err != nil {
			return nil, nil, nil, err
		}
	}
	if normalIndices != nil {
		normals, err = indicesToValues(normalIndices, normals)
		if err != nil {
			return nil, nil, nil, err
		}
	}
	if uvIndices != nil {
		textureCoordValues := make([]mgl32.Vec2, len(uvIndices))
		for i, index := range uvIndices {
			if int(index) >= len(textureCoords) {
				return nil, nil, nil, fmt.Errorf("unexpected Texture Coordinate index %d, out of range of the provided %d texture coordinates", index+1, len(textureCoords))
			}
			textureCoordValues[i] = textureCoords[index]
		}
		textureCoords = textureCoordValues
	}
	return verts, normals, textureCoords, nil
}

// indicesToValues takes a list of indices and the data they reference, and returns the raw list of referenced data
// with all of the duplicate values that entails.
// Note that the indices are expected to be zero based, even though OBJ files use 1 based indexing.
func indicesToValues(indices []uint16, data []mgl32.Vec3) ([]mgl32.Vec3, error) {
	values := make([]mgl32.Vec3, len(indices))
	for i, index := range indices {
		if int(index) >= len(data) {
			return nil, fmt.Errorf("unexpected index %d, out of range of the provided %d data", index+1, len(data))
		}
		values[i] = data[index]
	}
	return values, nil
}
<|endoftext|>


<commit_before>package commands

import (
	"bytes"
	"fmt"
	"regexp"
	"time"

	table "github.com/crackcomm/go-clitable"
	"github.com/github/hub/cmd"
	"github.com/wolfeidau/buildkite-cli/config"
	"github.com/wolfeidau/buildkite-cli/git"
	"github.com/wolfeidau/buildkite-cli/utils"
	bk "github.com/wolfeidau/go-buildkite/buildkite"
)

var (
	projectColumns = []string{"ID", "NAME", "BUILD", "BRANCH", "MESSAGE", "STATE", "FINISHED"}
	buildColumns   = []string{"PROJECT", "NUMBER", "BRANCH", "MESSAGE", "STATE", "COMMIT"}

	projectOrgRegex = regexp.MustCompile(`\/organizations\/([\w_-]+)\/`)
)

// BkCli manages the config and state for the buildkite cli
type bkCli struct {
	config *config.Config
	client *bk.Client
}

// NewBkCli configure the buildkite cli using the supplied config
func newBkCli() (*bkCli, error) {
	config := config.CurrentConfig()

	client, err := newClient(config)

	if err != nil {
		return nil, err
	}

	return &bkCli{config, client}, nil
}

// Get List of Projects for all the orginizations.
func (cli *bkCli) projectList(quietList bool) error {

	t := time.Now()

	projects, err := cli.listProjects()

	if err != nil {
		return err
	}

	if quietList {
		for _, proj := range projects {
			fmt.Printf("%-36s\n", *proj.ID)
		}
		return nil // we are done
	}

	tb := table.New(projectColumns)
	vals := make(map[string]interface{})

	for _, proj := range projects {
		if proj.FeaturedBuild != nil {
			vals = utils.ToMap(projectColumns, []interface{}{*proj.ID, *proj.Name, *proj.FeaturedBuild.Number, *proj.FeaturedBuild.Branch, *proj.FeaturedBuild.Message, *proj.FeaturedBuild.State, *proj.FeaturedBuild.FinishedAt})
		} else {
			vals = utils.ToMap(projectColumns, []interface{}{*proj.ID, *proj.Name, 0, "", "", "", ""})
		}
		tb.AddRow(vals)
	}
	tb.Markdown = true
	tb.Print()

	fmt.Printf("\nTime taken: %s\n", time.Now().Sub(t))

	return err
}

// List Get List of Builds
func (cli *bkCli) buildList(quietList bool) error {

	var (
		builds []bk.Build
		err    error
	)

	t := time.Now()

	projects, err := cli.listProjects()

	if err != nil {
		return err
	}

	// did we locate a project
	project := git.LocateProject(projects)

	if project != nil {
		fmt.Printf("Listing for project = %s\n\n", *project.Name)

		org := extractOrg(*project.URL)

		builds, _, err = cli.client.Builds.ListByProject(org, *project.Slug, nil)

	} else {
		utils.Check(fmt.Errorf("Failed to locate the buildkite project using git.")) // TODO tidy this up
		return nil
	}

	if err != nil {
		return err
	}

	if quietList {
		for _, build := range builds {
			fmt.Printf("%-36s\n", *build.ID)
		}
		return nil // we are done
	}

	tb := table.New(buildColumns)

	for _, build := range builds {
		vals := utils.ToMap(buildColumns, []interface{}{*build.Project.Name, *build.Number, *build.Branch, *build.Message, *build.State, *build.Commit})
		tb.AddRow(vals)
	}

	tb.Markdown = true
	tb.Print()

	fmt.Printf("\nTime taken: %s\n", time.Now().Sub(t))

	return nil
}

func (cli *bkCli) openProjectBuilds() error {

	projects, err := cli.listProjects()

	if err != nil {
		return err
	}

	// did we locate a project
	project := git.LocateProject(projects)

	if project != nil {
		fmt.Printf("Opening project = %s\n\n", *project.Name)

	} else {
		utils.Check(fmt.Errorf("Failed to locate the buildkite project using git.")) // TODO tidy this up
		return nil
	}

	org := extractOrg(*project.URL)

	projectURL := fmt.Sprintf("https://buildkite.com/%s/%s/builds", org, *project.Slug) // TODO URL should come from REST interface

	args, err := utils.BrowserLauncher()

	utils.Check(err) // TODO tidy this up

	cmd := cmd.New(args[0])

	args = append(args, projectURL)

	cmd.WithArgs(args[1:]...)

	_, err = cmd.CombinedOutput()

	return err
}

func (cli *bkCli) tailLogs(number string) error {

	projects, err := cli.listProjects()

	if err != nil {
		return err
	}

	// did we locate a project
	project := git.LocateProject(projects)

	if project != nil {
		fmt.Printf("Opening project = %s\n\n", *project.Name)

	} else {
		utils.Check(fmt.Errorf("Failed to locate the buildkite project using git.")) // TODO tidy this up
		return nil
	}

	if number == "" {

		if project.FeaturedBuild != nil {
			number = fmt.Sprintf("%d", *project.FeaturedBuild.Number)
		}

	}

	ok, j := cli.getLastJob(project, number)
	if ok {
		req, err := cli.client.NewRequest("GET", *j.RawLogsURL, nil)

		if err != nil {
			return err
		}
		buffer := new(bytes.Buffer)

		_, err = cli.client.Do(req, buffer)

		if err != nil {
			return err
		}

		fmt.Printf("%s\n", string(buffer.Bytes()))
	}

	return nil
}

func (cli *bkCli) getLastJob(project *bk.Project, number string) (bool, *bk.Job) {
	org := extractOrg(*project.URL)

	build, _, err := cli.client.Builds.Get(org, *project.Slug, number)

	if err != nil {
		return false, nil
	}

	jobs := build.Jobs

	if len(jobs) == 0 {
		return false, nil
	}

	j := jobs[len(jobs)-1]

	return true, j
}

func (cli *bkCli) setup() error {
	return cli.config.PromptForConfig()
}

func (cli *bkCli) listProjects() ([]bk.Project, error) {
	var projects []bk.Project

	orgs, _, err := cli.client.Organizations.List(nil)

	if err != nil {
		return nil, err
	}

	for _, org := range orgs {
		projs, _, err := cli.client.Projects.List(*org.Slug, nil)

		if err != nil {
			return nil, err
		}

		projects = append(projects, projs...)
	}

	return projects, nil
}

func newClient(config *config.Config) (*bk.Client, error) {

	if config.OAuthToken == "" {
		err := config.PromptForConfig()
		if err != nil {
			return nil, err
		}
	}

	tconf, err := bk.NewTokenConfig(config.OAuthToken)

	if err != nil {
		return nil, err
	}

	return bk.NewClient(tconf.Client()), nil
}

// ProjectList just get a list of projects
func ProjectList(quietList bool) error {
	cli, err := newBkCli()
	if err != nil {
		return err
	}

	return cli.projectList(quietList)
}

// BuildsList retrieve a list of builds for the current project using the git remote to locate it.
func BuildsList(quietList bool) error {
	cli, err := newBkCli()
	if err != nil {
		return err
	}

	return cli.buildList(quietList)
}

// LogsList retrieve the logs for the last build using the supplied build number
func LogsList(number string) error {
	cli, err := newBkCli()
	if err != nil {
		return err
	}

	return cli.tailLogs(number)
}

// Open buildkite project for the current project using the git remote to locate it.
func Open() error {
	cli, err := newBkCli()
	if err != nil {
		return err
	}

	return cli.openProjectBuilds()
}

// Setup configure the buildkite cli with a new token.
func Setup() error {
	cli, err := newBkCli()
	if err != nil {
		return err
	}

	return cli.setup()
}

func extractOrg(url string) string {
	m := projectOrgRegex.FindStringSubmatch(url)

	if len(m) == 2 {
		return m[1]
	}

	return ""
}
<commit_msg>Added summary header to log output and open now hits last build.<commit_after>package commands

import (
	"bytes"
	"fmt"
	"regexp"
	"time"

	table "github.com/crackcomm/go-clitable"
	"github.com/github/hub/cmd"
	"github.com/wolfeidau/buildkite-cli/config"
	"github.com/wolfeidau/buildkite-cli/git"
	"github.com/wolfeidau/buildkite-cli/utils"
	bk "github.com/wolfeidau/go-buildkite/buildkite"
)

var (
	projectColumns = []string{"ID", "NAME", "BUILD", "BRANCH", "MESSAGE", "STATE", "FINISHED"}
	jobColumns     = []string{"NAME", "STARTED", "FINISHED", "STATE"}
	buildColumns   = []string{"PROJECT", "NUMBER", "BRANCH", "MESSAGE", "STATE", "COMMIT"}

	projectOrgRegex = regexp.MustCompile(`\/organizations\/([\w_-]+)\/`)
)

// BkCli manages the config and state for the buildkite cli
type bkCli struct {
	config *config.Config
	client *bk.Client
}

// NewBkCli configure the buildkite cli using the supplied config
func newBkCli() (*bkCli, error) {
	config := config.CurrentConfig()

	client, err := newClient(config)

	if err != nil {
		return nil, err
	}

	return &bkCli{config, client}, nil
}

// Get List of Projects for all the orginizations.
func (cli *bkCli) projectList(quietList bool) error {

	t := time.Now()

	projects, err := cli.listProjects()

	if err != nil {
		return err
	}

	if quietList {
		for _, proj := range projects {
			fmt.Printf("%-36s\n", *proj.ID)
		}
		return nil // we are done
	}

	tb := table.New(projectColumns)
	vals := make(map[string]interface{})

	for _, proj := range projects {
		if proj.FeaturedBuild != nil {
			vals = utils.ToMap(projectColumns, []interface{}{*proj.ID, *proj.Name, *proj.FeaturedBuild.Number, *proj.FeaturedBuild.Branch, *proj.FeaturedBuild.Message, *proj.FeaturedBuild.State, *proj.FeaturedBuild.FinishedAt})
		} else {
			vals = utils.ToMap(projectColumns, []interface{}{*proj.ID, *proj.Name, 0, "", "", "", ""})
		}
		tb.AddRow(vals)
	}
	tb.Markdown = true
	tb.Print()

	fmt.Printf("\nTime taken: %s\n", time.Now().Sub(t))

	return err
}

// List Get List of Builds
func (cli *bkCli) buildList(quietList bool) error {

	var (
		builds []bk.Build
		err    error
	)

	t := time.Now()

	projects, err := cli.listProjects()

	if err != nil {
		return err
	}

	// did we locate a project
	project := git.LocateProject(projects)

	if project != nil {
		fmt.Printf("Listing for project = %s\n\n", *project.Name)

		org := extractOrg(*project.URL)

		builds, _, err = cli.client.Builds.ListByProject(org, *project.Slug, nil)

	} else {
		utils.Check(fmt.Errorf("Failed to locate the buildkite project using git.")) // TODO tidy this up
		return nil
	}

	if err != nil {
		return err
	}

	if quietList {
		for _, build := range builds {
			fmt.Printf("%-36s\n", *build.ID)
		}
		return nil // we are done
	}

	tb := table.New(buildColumns)

	for _, build := range builds {
		vals := utils.ToMap(buildColumns, []interface{}{*build.Project.Name, *build.Number, *build.Branch, *build.Message, *build.State, *build.Commit})
		tb.AddRow(vals)
	}

	tb.Markdown = true
	tb.Print()

	fmt.Printf("\nTime taken: %s\n", time.Now().Sub(t))

	return nil
}

func (cli *bkCli) openProjectBuilds() error {

	projects, err := cli.listProjects()

	if err != nil {
		return err
	}

	// did we locate a project
	project := git.LocateProject(projects)

	if project != nil {
		fmt.Printf("Opening project = %s\n\n", *project.Name)

	} else {
		utils.Check(fmt.Errorf("Failed to locate the buildkite project using git.")) // TODO tidy this up
		return nil
	}

	org := extractOrg(*project.URL)

	projectURL := fmt.Sprintf("https://buildkite.com/%s/%s/builds/last", org, *project.Slug) // TODO URL should come from REST interface

	args, err := utils.BrowserLauncher()

	utils.Check(err) // TODO tidy this up

	cmd := cmd.New(args[0])

	args = append(args, projectURL)

	cmd.WithArgs(args[1:]...)

	_, err = cmd.CombinedOutput()

	return err
}

func (cli *bkCli) tailLogs(number string) error {

	projects, err := cli.listProjects()

	if err != nil {
		return err
	}

	// did we locate a project
	project := git.LocateProject(projects)

	if project != nil {
		fmt.Printf("Opening project = %s\n\n", *project.Name)

	} else {
		utils.Check(fmt.Errorf("Failed to locate the buildkite project using git.")) // TODO tidy this up
		return nil
	}

	if number == "" {

		if project.FeaturedBuild != nil {
			number = fmt.Sprintf("%d", *project.FeaturedBuild.Number)
		}

	}

	ok, j := cli.getLastJob(project, number)
	if ok {

		tb := table.New(jobColumns)

		vals := utils.ToMap(jobColumns, []interface{}{*j.Name, *j.StartedAt, *j.FinishedAt, *j.State})
		tb.AddRow(vals)
		tb.Markdown = true
		tb.Print()

		fmt.Println()

		req, err := cli.client.NewRequest("GET", *j.RawLogsURL, nil)

		if err != nil {
			return err
		}
		buffer := new(bytes.Buffer)

		_, err = cli.client.Do(req, buffer)

		if err != nil {
			return err
		}

		fmt.Printf("%s\n", string(buffer.Bytes()))
	}

	return nil
}

func (cli *bkCli) getLastJob(project *bk.Project, number string) (bool, *bk.Job) {
	org := extractOrg(*project.URL)

	build, _, err := cli.client.Builds.Get(org, *project.Slug, number)

	if err != nil {
		return false, nil
	}

	jobs := build.Jobs

	if len(jobs) == 0 {
		return false, nil
	}

	j := jobs[len(jobs)-1]

	return true, j
}

func (cli *bkCli) setup() error {
	return cli.config.PromptForConfig()
}

func (cli *bkCli) listProjects() ([]bk.Project, error) {
	var projects []bk.Project

	orgs, _, err := cli.client.Organizations.List(nil)

	if err != nil {
		return nil, err
	}

	for _, org := range orgs {
		projs, _, err := cli.client.Projects.List(*org.Slug, nil)

		if err != nil {
			return nil, err
		}

		projects = append(projects, projs...)
	}

	return projects, nil
}

func newClient(config *config.Config) (*bk.Client, error) {

	if config.OAuthToken == "" {
		err := config.PromptForConfig()
		if err != nil {
			return nil, err
		}
	}

	tconf, err := bk.NewTokenConfig(config.OAuthToken)

	if err != nil {
		return nil, err
	}

	return bk.NewClient(tconf.Client()), nil
}

// ProjectList just get a list of projects
func ProjectList(quietList bool) error {
	cli, err := newBkCli()
	if err != nil {
		return err
	}

	return cli.projectList(quietList)
}

// BuildsList retrieve a list of builds for the current project using the git remote to locate it.
func BuildsList(quietList bool) error {
	cli, err := newBkCli()
	if err != nil {
		return err
	}

	return cli.buildList(quietList)
}

// LogsList retrieve the logs for the last build using the supplied build number
func LogsList(number string) error {
	cli, err := newBkCli()
	if err != nil {
		return err
	}

	return cli.tailLogs(number)
}

// Open buildkite project for the current project using the git remote to locate it.
func Open() error {
	cli, err := newBkCli()
	if err != nil {
		return err
	}

	return cli.openProjectBuilds()
}

// Setup configure the buildkite cli with a new token.
func Setup() error {
	cli, err := newBkCli()
	if err != nil {
		return err
	}

	return cli.setup()
}

func extractOrg(url string) string {
	m := projectOrgRegex.FindStringSubmatch(url)

	if len(m) == 2 {
		return m[1]
	}

	return ""
}
<|endoftext|>


<commit_before>// @Copyright (c) 2016 mparaiso <mparaiso@online.fr>  All rights reserved.

package gonews

import (
	"database/sql"

	"log"
	"net/http"
	"os"

	"fmt"

	"errors"

	"github.com/gorilla/sessions"
)

// Any is any value
type Any interface{}

// ContainerOptions are options provided to the container
type ContainerOptions struct {
	DataSource,
	Driver,
	Secret,
	Title,
	Slogan,
	Description,
	TemplateDirectory,
	TemplateFileExtension string
	Debug bool
	LogLevel
	Session struct {
		Name         string
		StoreFactory func() (sessions.Store, error)
	}
	ConnectionFactory func() (*sql.DB, error)
	LoggerFactory     func() (LoggerInterface, error)
	csrfGenerator     CSRFGenerator
	user              *User
}

// Container contains all the application dependencies
type Container struct {
	ContainerOptions  ContainerOptions
	db                *sql.DB
	logger            LoggerInterface
	threadRepository  *ThreadRepository
	userRepository    *UserRepository
	commentRepository *CommentRepository

	template TemplateEngine

	sessionStore sessions.Store
	request      *http.Request
	response     ResponseWriterExtra

	CSRFGeneratorProvider
	TemplateProvider
	SessionProvider

	user *User
}

func (c Container) Debug() bool {
	return c.ContainerOptions.Debug
}

func (c *Container) SetDebug(debug bool) {
	c.ContainerOptions.Debug = debug
}

// Request returns an *http.Request
func (c *Container) Request() *http.Request {
	return c.request
}

// SetRequest sets the request
func (c *Container) SetRequest(request *http.Request) {
	c.request = request
}

// SetResponse sets the response writer
func (c *Container) SetResponse(response ResponseWriterExtra) {
	c.response = response
}

// ResponseWriter returns the response writer
func (c *Container) ResponseWriter() ResponseWriterExtra {
	return c.response
}

// HasAuthenticatedUser returns true if a user has been authenticated
func (c *Container) HasAuthenticatedUser() bool {
	return c.user != nil
}

// SetCurrentUser sets the authenticated user
func (c *Container) SetCurrentUser(u *User) {
	c.user = u
}

// CurrentUser returns an authenticated user
func (c *Container) CurrentUser() *User {
	return c.user
}

// GetSecret returns the secret key
func (c *Container) GetSecret() string {
	return c.ContainerOptions.Secret
}

// GetConnection returns the database connection
func (c *Container) GetConnection() (*sql.DB, error) {
	if c.ContainerOptions.ConnectionFactory != nil {
		db, err := c.ContainerOptions.ConnectionFactory()
		if err != nil {
			return nil, err
		}
		c.db = db
	} else if c.db == nil {
		db, err := sql.Open(c.ContainerOptions.Driver, c.ContainerOptions.DataSource)
		if err != nil {
			return nil, err
		}
		c.db = db
	}
	return c.db, nil
}

// GetThreadRepository returns a repository for Thread
func (c *Container) GetThreadRepository() (*ThreadRepository, error) {
	if c.threadRepository == nil {
		db, err := c.GetConnection()
		if err != nil {
			return nil, err
		}
		c.threadRepository = &ThreadRepository{DB: db, Logger: c.MustGetLogger()}
	}
	return c.threadRepository, nil
}

// MustGetThreadRepository panics on error
func (c *Container) MustGetThreadRepository() *ThreadRepository {
	r, err := c.GetThreadRepository()
	if err != nil {
		panic(err)
	}
	return r
}

// GetUserRepository returns a repository for User
func (c *Container) GetUserRepository() (*UserRepository, error) {
	if c.userRepository == nil {
		db, err := c.GetConnection()
		if err != nil {
			return nil, err
		}
		logger, err := c.GetLogger()
		if err != nil {
			return nil, err
		}
		c.userRepository = &UserRepository{db, logger}
	}
	return c.userRepository, nil
}

// MustGetUserRepository panics on error or return a repository of User
func (c *Container) MustGetUserRepository() *UserRepository {
	r, err := c.GetUserRepository()
	if err != nil {
		panic(err)
	}
	return r
}

// GetCommentRepository returns the repository of comments
func (c *Container) GetCommentRepository() (*CommentRepository, error) {
	var (
		err    error
		db     *sql.DB
		logger LoggerInterface
	)
	if c.commentRepository == nil {
		db, err = c.GetConnection()
		if err == nil {
			logger, err = c.GetLogger()
			if err == nil {
				c.commentRepository = &CommentRepository{db, logger}
			}
		}
	}
	return c.commentRepository, err
}

// MustGetCommentRepository panics on error
func (c *Container) MustGetCommentRepository() *CommentRepository {
	if r, err := c.GetCommentRepository(); err != nil {
		panic(err)
	} else {
		return r
	}
}

// GetOptions returns the container's options
func (c *Container) GetOptions() ContainerOptions {
	return c.ContainerOptions
}

// GetLogger gets a logger
func (c *Container) GetLogger() (LoggerInterface, error) {
	if c.logger == nil {
		if c.ContainerOptions.LoggerFactory != nil {
			logger, err := c.ContainerOptions.LoggerFactory()
			if err != nil {
				return nil, err
			}
			c.logger = logger
		} else {
			logger := &log.Logger{}
			logger.SetOutput(os.Stdout)
			if c.ContainerOptions.Debug == true {
				c.logger = NewDefaultLogger(ALL)
			} else {
				c.logger = NewDefaultLogger(c.ContainerOptions.LogLevel)
			}

		}
	}
	return c.logger, nil
}

// MustGetLogger panics on error or return a LoggerInterface
func (c *Container) MustGetLogger() LoggerInterface {
	logger, err := c.GetLogger()
	if err != nil {
		panic(err)
	}
	return logger
}

// HTTPRedirect redirects a request
func (c *Container) HTTPRedirect(url string, status int) {
	if session, err := c.GetSession(); err == nil {
		session.Save(c.Request(), c.ResponseWriter())
	} else {
		c.MustGetLogger().Error("Container", err)
	}
	http.Redirect(c.ResponseWriter(), c.Request(), url, status)
}

// HTTPError writes an error to the response
func (c *Container) HTTPError(rw http.ResponseWriter, r *http.Request, status int, message Any) {
	c.MustGetLogger().Error(fmt.Sprintf("%s %d %s", r.URL, status, message))
	rw.WriteHeader(status)
	// if debug show a detailed error message
	if c.ContainerOptions.Debug == true {
		// if response has been sent, just write to output for now
		// TODO buffer response in order to handle the case where there is
		// 		an error in the template which should lead to a status 500
		if rw.(ResponseWriterExtra).IsResponseWritten() {
			http.Error(rw, fmt.Sprintf("%v", message), status)
			return
		}
		// if not then execute the template with the Message
		c.MustGetTemplate().ExecuteTemplate(rw, "error.tpl.html", map[string]interface{}{"Error": struct {
			Status  int
			Message interface{}
		}{Status: status, Message: message}})
		return
	}
	// if not debug show a generic error message.
	// don't show a detailed error message
	if rw.(ResponseWriterExtra).IsResponseWritten() {
		http.Error(rw, http.StatusText(status), status)
		return
	}
	c.MustGetTemplate().ExecuteTemplate(rw, "error.tpl.html", map[string]interface{}{"Error": struct {
		Status  int
		Message string
	}{Status: status, Message: http.StatusText(status)}})
}

// GetSessionStore returns a session.Store
func (c *Container) GetSessionStore() (sessions.Store, error) {
	if c.ContainerOptions.Session.StoreFactory == nil {
		return nil, errors.New("SessionStoreFactory not defined in Container.Options")
	}
	if c.sessionStore == nil {
		var err error
		c.sessionStore, err = c.ContainerOptions.Session.StoreFactory()
		if err != nil {
			return nil, err
		}
	}
	return c.sessionStore, nil
}
<commit_msg>session provider added<commit_after>// @Copyright (c) 2016 mparaiso <mparaiso@online.fr>  All rights reserved.

package gonews

import (
	"database/sql"

	"net/http"

	"fmt"

	"errors"

	"github.com/gorilla/sessions"
)

// Any is any value
type Any interface{}

// ContainerOptions are options provided to the container
type ContainerOptions struct {
	DataSource,
	Driver,
	Secret,
	Title,
	Slogan,
	Description,
	TemplateDirectory,
	TemplateFileExtension string
	Debug bool
	LogLevel
	Session struct {
		Name         string
		StoreFactory func() (sessions.Store, error)
	}
	ConnectionFactory func() (*sql.DB, error)
	LoggerFactory     func() (LoggerInterface, error)
	csrfGenerator     CSRFGenerator
	user              *User
}

// Container contains all the application dependencies
type Container struct {
	ContainerOptions  ContainerOptions
	db                *sql.DB
	logger            LoggerInterface
	threadRepository  *ThreadRepository
	userRepository    *UserRepository
	commentRepository *CommentRepository

	template TemplateEngine

	sessionStore sessions.Store
	request      *http.Request
	response     ResponseWriterExtra

	CSRFGeneratorProvider
	TemplateProvider
	SessionProvider
	LoggerProvider

	user *User
}

func (c Container) Debug() bool {
	return c.ContainerOptions.Debug
}

func (c *Container) SetDebug(debug bool) {
	c.ContainerOptions.Debug = debug
}

// Request returns an *http.Request
func (c *Container) Request() *http.Request {
	return c.request
}

// SetRequest sets the request
func (c *Container) SetRequest(request *http.Request) {
	c.request = request
}

// SetResponse sets the response writer
func (c *Container) SetResponse(response ResponseWriterExtra) {
	c.response = response
}

// ResponseWriter returns the response writer
func (c *Container) ResponseWriter() ResponseWriterExtra {
	return c.response
}

// HasAuthenticatedUser returns true if a user has been authenticated
func (c *Container) HasAuthenticatedUser() bool {
	return c.user != nil
}

// SetCurrentUser sets the authenticated user
func (c *Container) SetCurrentUser(u *User) {
	c.user = u
}

// CurrentUser returns an authenticated user
func (c *Container) CurrentUser() *User {
	return c.user
}

// GetSecret returns the secret key
func (c *Container) GetSecret() string {
	return c.ContainerOptions.Secret
}

// GetConnection returns the database connection
func (c *Container) GetConnection() (*sql.DB, error) {
	if c.ContainerOptions.ConnectionFactory != nil {
		db, err := c.ContainerOptions.ConnectionFactory()
		if err != nil {
			return nil, err
		}
		c.db = db
	} else if c.db == nil {
		db, err := sql.Open(c.ContainerOptions.Driver, c.ContainerOptions.DataSource)
		if err != nil {
			return nil, err
		}
		c.db = db
	}
	return c.db, nil
}

// GetThreadRepository returns a repository for Thread
func (c *Container) GetThreadRepository() (*ThreadRepository, error) {
	if c.threadRepository == nil {
		db, err := c.GetConnection()
		if err != nil {
			return nil, err
		}
		c.threadRepository = &ThreadRepository{DB: db, Logger: c.MustGetLogger()}
	}
	return c.threadRepository, nil
}

// MustGetThreadRepository panics on error
func (c *Container) MustGetThreadRepository() *ThreadRepository {
	r, err := c.GetThreadRepository()
	if err != nil {
		panic(err)
	}
	return r
}

// GetUserRepository returns a repository for User
func (c *Container) GetUserRepository() (*UserRepository, error) {
	if c.userRepository == nil {
		db, err := c.GetConnection()
		if err != nil {
			return nil, err
		}
		logger, err := c.GetLogger()
		if err != nil {
			return nil, err
		}
		c.userRepository = &UserRepository{db, logger}
	}
	return c.userRepository, nil
}

// MustGetUserRepository panics on error or return a repository of User
func (c *Container) MustGetUserRepository() *UserRepository {
	r, err := c.GetUserRepository()
	if err != nil {
		panic(err)
	}
	return r
}

// GetCommentRepository returns the repository of comments
func (c *Container) GetCommentRepository() (*CommentRepository, error) {
	var (
		err    error
		db     *sql.DB
		logger LoggerInterface
	)
	if c.commentRepository == nil {
		db, err = c.GetConnection()
		if err == nil {
			logger, err = c.GetLogger()
			if err == nil {
				c.commentRepository = &CommentRepository{db, logger}
			}
		}
	}
	return c.commentRepository, err
}

// MustGetCommentRepository panics on error
func (c *Container) MustGetCommentRepository() *CommentRepository {
	if r, err := c.GetCommentRepository(); err != nil {
		panic(err)
	} else {
		return r
	}
}

// GetOptions returns the container's options
func (c *Container) GetOptions() ContainerOptions {
	return c.ContainerOptions
}

// HTTPRedirect redirects a request
func (c *Container) HTTPRedirect(url string, status int) {
	if session, err := c.GetSession(); err == nil {
		session.Save(c.Request(), c.ResponseWriter())
	} else {
		c.MustGetLogger().Error("Container", err)
	}
	http.Redirect(c.ResponseWriter(), c.Request(), url, status)
}

// HTTPError writes an error to the response
func (c *Container) HTTPError(rw http.ResponseWriter, r *http.Request, status int, message Any) {
	c.MustGetLogger().Error(fmt.Sprintf("%s %d %s", r.URL, status, message))
	rw.WriteHeader(status)
	// if debug show a detailed error message
	if c.ContainerOptions.Debug == true {
		// if response has been sent, just write to output for now
		// TODO buffer response in order to handle the case where there is
		// 		an error in the template which should lead to a status 500
		if rw.(ResponseWriterExtra).IsResponseWritten() {
			http.Error(rw, fmt.Sprintf("%v", message), status)
			return
		}
		// if not then execute the template with the Message
		c.MustGetTemplate().ExecuteTemplate(rw, "error.tpl.html", map[string]interface{}{"Error": struct {
			Status  int
			Message interface{}
		}{Status: status, Message: message}})
		return
	}
	// if not debug show a generic error message.
	// don't show a detailed error message
	if rw.(ResponseWriterExtra).IsResponseWritten() {
		http.Error(rw, http.StatusText(status), status)
		return
	}
	c.MustGetTemplate().ExecuteTemplate(rw, "error.tpl.html", map[string]interface{}{"Error": struct {
		Status  int
		Message string
	}{Status: status, Message: http.StatusText(status)}})
}

// GetSessionStore returns a session.Store
func (c *Container) GetSessionStore() (sessions.Store, error) {
	if c.ContainerOptions.Session.StoreFactory == nil {
		return nil, errors.New("SessionStoreFactory not defined in Container.Options")
	}
	if c.sessionStore == nil {
		var err error
		c.sessionStore, err = c.ContainerOptions.Session.StoreFactory()
		if err != nil {
			return nil, err
		}
	}
	return c.sessionStore, nil
}
<|endoftext|>


<commit_before>package oidc

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"path"
)

// WellKnownEndpoints holds the well known OIDC endpoints.
type WellKnownEndpoints struct {
	JWKSURI string `json:"jwks_uri"`
}

// GetWellKnownEndpointsFromIssuerURL gets the well known endpoints for the passed in issuer url.
func GetWellKnownEndpointsFromIssuerURL(ctx context.Context, issuerURL url.URL) (*WellKnownEndpoints, error) {
	issuerURL.Path = path.Join(issuerURL.Path, ".well-known/openid-configuration")

	request, err := http.NewRequest(http.MethodGet, issuerURL.String(), nil)
	if err != nil {
		return nil, fmt.Errorf("could not build request to get well known endpoints: %w", err)
	}
	request = request.WithContext(ctx)

	response, err := http.DefaultClient.Do(request)
	if err != nil {
		return nil, fmt.Errorf("could not get well known endpoints from url %s: %w", issuerURL.String(), err)
	}
	defer response.Body.Close()

	var wkEndpoints WellKnownEndpoints
	if err = json.NewDecoder(response.Body).Decode(&wkEndpoints); err != nil {
		return nil, fmt.Errorf("could not decode json body when getting well known endpoints: %w", err)
	}

	return &wkEndpoints, nil
}
<commit_msg>Refactor oidc.GetWellKnownEndpointsFromIssuerURL<commit_after>package oidc

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"path"
)

// WellKnownEndpoints holds the well known OIDC endpoints.
type WellKnownEndpoints struct {
	JWKSURI string `json:"jwks_uri"`
}

// GetWellKnownEndpointsFromIssuerURL gets the well known endpoints for the passed in issuer url.
func GetWellKnownEndpointsFromIssuerURL(
	ctx context.Context,
	httpClient *http.Client,
	issuerURL url.URL,
) (*WellKnownEndpoints, error) {
	issuerURL.Path = path.Join(issuerURL.Path, ".well-known/openid-configuration")

	request, err := http.NewRequestWithContext(ctx, http.MethodGet, issuerURL.String(), nil)
	if err != nil {
		return nil, fmt.Errorf("could not build request to get well known endpoints: %w", err)
	}

	response, err := httpClient.Do(request)
	if err != nil {
		return nil, fmt.Errorf("could not get well known endpoints from url %s: %w", issuerURL.String(), err)
	}
	defer response.Body.Close()

	var wkEndpoints WellKnownEndpoints
	if err = json.NewDecoder(response.Body).Decode(&wkEndpoints); err != nil {
		return nil, fmt.Errorf("could not decode json body when getting well known endpoints: %w", err)
	}

	return &wkEndpoints, nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 save

import (
	"fmt"
	"io"
	"regexp"

	"golang.org/x/net/context"

	"github.com/jacobsa/comeback/internal/graph"
	"github.com/jacobsa/syncutil"
)

// Given a base directory and a set of exclusions, list the files and
// directories that would be saved by a backup job with the same info in a
// human-readable format. Write the output to the supplied writer.
func List(
	ctx context.Context,
	w io.Writer,
	basePath string,
	exclusions []*regexp.Regexp) (err error) {
	b := syncutil.NewBundle(context.Background())

	// Explore the file system graph, writing all non-excluded nodes into a
	// channel.
	graphNodes := make(chan graph.Node, 100)
	b.Add(func(ctx context.Context) (err error) {
		defer close(graphNodes)
		sf := newSuccessorFinder(basePath, exclusions)

		const parallelism = 8
		err = graph.ExploreDirectedGraph(
			ctx,
			sf,
			[]graph.Node{(*pathAndFileInfo)(nil)},
			graphNodes,
			parallelism)

		if err != nil {
			err = fmt.Errorf("ExploreDirectedGraph: %v", err)
			return
		}

		return
	})

	// Print out info about each node.
	b.Add(func(ctx context.Context) (err error) {
		for n := range graphNodes {
			pfi, ok := n.(*pathAndFileInfo)
			if !ok {
				err = fmt.Errorf("Unexpected node type: %T", n)
				return
			}

			// Skip the root node.
			if pfi == nil {
				continue
			}

			_, err = fmt.Fprintf(w, "%s %d\n", pfi.Path, pfi.Info.Size())
			if err != nil {
				err = fmt.Errorf("Fprintf: %v", err)
				return
			}
		}

		return
	})

	err = b.Join()
	return
}
<commit_msg>Fix context for save.List.<commit_after>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 save

import (
	"fmt"
	"io"
	"regexp"

	"golang.org/x/net/context"

	"github.com/jacobsa/comeback/internal/graph"
	"github.com/jacobsa/syncutil"
)

// Given a base directory and a set of exclusions, list the files and
// directories that would be saved by a backup job with the same info in a
// human-readable format. Write the output to the supplied writer.
func List(
	ctx context.Context,
	w io.Writer,
	basePath string,
	exclusions []*regexp.Regexp) (err error) {
	b := syncutil.NewBundle(ctx)

	// Explore the file system graph, writing all non-excluded nodes into a
	// channel.
	graphNodes := make(chan graph.Node, 100)
	b.Add(func(ctx context.Context) (err error) {
		defer close(graphNodes)
		sf := newSuccessorFinder(basePath, exclusions)

		const parallelism = 8
		err = graph.ExploreDirectedGraph(
			ctx,
			sf,
			[]graph.Node{(*pathAndFileInfo)(nil)},
			graphNodes,
			parallelism)

		if err != nil {
			err = fmt.Errorf("ExploreDirectedGraph: %v", err)
			return
		}

		return
	})

	// Print out info about each node.
	b.Add(func(ctx context.Context) (err error) {
		for n := range graphNodes {
			pfi, ok := n.(*pathAndFileInfo)
			if !ok {
				err = fmt.Errorf("Unexpected node type: %T", n)
				return
			}

			// Skip the root node.
			if pfi == nil {
				continue
			}

			_, err = fmt.Fprintf(w, "%s %d\n", pfi.Path, pfi.Info.Size())
			if err != nil {
				err = fmt.Errorf("Fprintf: %v", err)
				return
			}
		}

		return
	})

	err = b.Join()
	return
}
<|endoftext|>


<commit_before>package gogadgets

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"strings"
)

type SMS struct {
	//twillio sid and oauth
	url     string
	sid     string
	token   string
	from    string
	message string
	to      []string
}

func NewSMS(pin *Pin) (OutputDevice, error) {
	sid, ok := pin.Args["sms"].(string)
	if !ok {
		return nil, fmt.Errorf("could not parse sms from pin args")
	}
	from, ok := pin.Args["from"].(string)
	if !ok {
		return nil, fmt.Errorf("could not parse from from pin args")
	}
	msg, ok := pin.Args["message"].(string)
	if !ok {
		return nil, fmt.Errorf("could not parse message from pin args")
	}
	token, ok := pin.Args["token"].(string)
	if !ok {
		return nil, fmt.Errorf("could not parse token from pin args")
	}
	var to []string
	tos, ok := pin.Args["to"].([]interface{})
	if !ok {
		return nil, fmt.Errorf("could not parse to from pin args")
	}
	for _, v := range tos {
		val, ok := v.(string)
		if !ok {
			return nil, fmt.Errorf("could not parse to from pin args")
		}
		tos = append(tos, val)
	}

	return &SMS{
		sid:     sid,
		from:    from,
		message: msg,
		url:     fmt.Sprintf("https://api.twilio.com/2010-04-01/Accounts/%s/Messages.json", sid),
		token:   token,
		to:      to,
	}, nil
}

func (s *SMS) Commands(location, name string) *Commands {
	return nil
}

func (s *SMS) Config() ConfigHelper {
	return ConfigHelper{}
}

func (s *SMS) Update(msg *Message) bool {
	return false
}

func (s *SMS) On(val *Value) error {
	msgData := url.Values{}
	for _, to := range s.to {
		msgData.Add("To", to)
	}
	msgData.Set("From", s.from)
	msgData.Set("Body", s.message)
	msgDataReader := *strings.NewReader(msgData.Encode())
	client := &http.Client{}
	req, _ := http.NewRequest("POST", s.url, &msgDataReader)
	req.SetBasicAuth(s.sid, s.token)
	req.Header.Add("Accept", "application/json")
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	resp, _ := client.Do(req)
	if resp.StatusCode >= 200 && resp.StatusCode < 300 {
		var data map[string]interface{}
		decoder := json.NewDecoder(resp.Body)
		err := decoder.Decode(&data)
		if err == nil {
			log.Println(data["sid"])
		}
	} else {
		log.Println(resp.Status)
	}
	return nil
}

func (s *SMS) Status() map[string]bool {
	return map[string]bool{}
}

func (s *SMS) Off() error {
	return nil
}
<commit_msg>fixed sms bugs<commit_after>package gogadgets

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"strings"
)

type SMS struct {
	//twillio sid and oauth
	url     string
	sid     string
	token   string
	from    string
	message string
	to      []string
}

func NewSMS(pin *Pin) (OutputDevice, error) {
	sid, ok := pin.Args["sid"].(string)
	if !ok {
		return nil, fmt.Errorf("could not parse sid from pin args")
	}
	from, ok := pin.Args["from"].(string)
	if !ok {
		return nil, fmt.Errorf("could not parse from from pin args")
	}
	msg, ok := pin.Args["message"].(string)
	if !ok {
		return nil, fmt.Errorf("could not parse message from pin args")
	}
	token, ok := pin.Args["token"].(string)
	if !ok {
		return nil, fmt.Errorf("could not parse token from pin args")
	}
	var to []string
	tos, ok := pin.Args["to"].([]interface{})
	if !ok {
		return nil, fmt.Errorf("could not parse to from pin args")
	}
	for _, v := range tos {
		val, ok := v.(string)
		if !ok {
			return nil, fmt.Errorf("could not parse to from pin args")
		}
		to = append(to, val)
	}

	return &SMS{
		sid:     sid,
		from:    from,
		message: msg,
		url:     fmt.Sprintf("https://api.twilio.com/2010-04-01/Accounts/%s/Messages.json", sid),
		token:   token,
		to:      to,
	}, nil
}

func (s *SMS) Commands(location, name string) *Commands {
	return nil
}

func (s *SMS) Config() ConfigHelper {
	return ConfigHelper{}
}

func (s *SMS) Update(msg *Message) bool {
	return false
}

func (s *SMS) On(val *Value) error {
	msgData := url.Values{}
	for _, to := range s.to {
		msgData.Add("To", to)
	}
	msgData.Set("From", s.from)
	msgData.Set("Body", s.message)
	msgDataReader := *strings.NewReader(msgData.Encode())
	client := &http.Client{}
	req, _ := http.NewRequest("POST", s.url, &msgDataReader)
	req.SetBasicAuth(s.sid, s.token)
	req.Header.Add("Accept", "application/json")
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	resp, _ := client.Do(req)
	if resp.StatusCode >= 200 && resp.StatusCode < 300 {
		var data map[string]interface{}
		decoder := json.NewDecoder(resp.Body)
		err := decoder.Decode(&data)
		if err == nil {
			log.Println(data["sid"])
		}
	} else {
		log.Println(resp.Status)
	}
	return nil
}

func (s *SMS) Status() map[string]bool {
	return map[string]bool{}
}

func (s *SMS) Off() error {
	return nil
}
<|endoftext|>


<commit_before>package main

const columDefSQL = `
SELECT
    a.attnum AS field_ordinal,
    a.attname AS column_name,
    pd.description AS description,
    format_type(a.atttypid, a.atttypmod) AS data_type,
    a.attnotnull AS not_null,
    COALESCE(ct.contype = 'p', false) AS  is_primary_key,
    CASE WHEN a.atttypid = ANY ('{int,int8,int2}'::regtype[])
      AND EXISTS (
         SELECT 1 FROM pg_attrdef ad
         WHERE  ad.adrelid = a.attrelid
         AND    ad.adnum   = a.attnum
         AND    ad.adbin   = 'nextval('''
            || (pg_get_serial_sequence (a.attrelid::regclass::text
                                      , a.attname))::regclass
            || '''::regclass)'
         )
    THEN CASE a.atttypid
            WHEN 'int'::regtype  THEN 'serial'
            WHEN 'int8'::regtype THEN 'bigserial'
            WHEN 'int2'::regtype THEN 'smallserial'
         END
    ELSE format_type(a.atttypid, a.atttypmod)
    END AS data_type
FROM pg_attribute a
JOIN ONLY pg_class c ON c.oid = a.attrelid
JOIN ONLY pg_namespace n ON n.oid = c.relnamespace
LEFT JOIN pg_constraint ct ON ct.conrelid = c.oid
AND a.attnum = ANY(ct.conkey) AND ct.contype IN ('p', 'u')
LEFT JOIN pg_attrdef ad ON ad.adrelid = c.oid AND ad.adnum = a.attnum
LEFT JOIN pg_description pd ON pd.objoid = a.attrelid AND pd.objsubid = a.attnum
WHERE a.attisdropped = false
AND n.nspname = $1
AND c.relname = $2
AND a.attnum > 0
ORDER BY a.attnum
`

const tableDefSQL = `
SELECT
  c.relname AS table_name,
  pd.description AS description
FROM pg_class c
JOIN ONLY pg_namespace n
ON n.oid = c.relnamespace
LEFT JOIN pg_description pd ON pd.objoid = c.oid AND pd.objsubid = 0
WHERE n.nspname = $1
AND c.relkind = 'r'
ORDER BY c.relname
`

const fkDefSQL = `
select
  att2.attname as "child_column"
  , cl.relname as "parent_table"
  , att.attname as "parent_column"
  , con.conname
  , case
      when pi.indisprimary is null then false
      else pi.indisprimary
    end as "is_parent_pk"
  , case
      when ci.indisprimary is null then false
      else ci.indisprimary
    end as "is_child_pk"
from (
  select
    unnest(con1.conkey) as "parent"
    , unnest(con1.confkey) as "child"
    , con1.confrelid
    , con1.conrelid
    , con1.conname
  from pg_class cl
  join pg_namespace ns on cl.relnamespace = ns.oid
  join pg_constraint con1 on con1.conrelid = cl.oid
  where ns.nspname = $1
  and cl.relname = $2
  and con1.contype = 'f'
) con
join pg_attribute att
on att.attrelid = con.confrelid and att.attnum = con.child
left outer join pg_index pi
on att.attrelid = pi.indrelid and att.attnum = any(pi.indkey)
join pg_class cl
on cl.oid = con.confrelid
join pg_attribute att2
on att2.attrelid = con.conrelid and att2.attnum = con.parent
left outer join pg_index ci
on att2.attrelid = ci.indrelid and att2.attnum = any(ci.indkey)
order by con.conname
`
<commit_msg>Use pg_get_expr to pass testing<commit_after>package main

const columDefSQL = `
SELECT
    a.attnum AS field_ordinal,
    a.attname AS column_name,
    pd.description AS description,
    format_type(a.atttypid, a.atttypmod) AS data_type,
    a.attnotnull AS not_null,
    COALESCE(ct.contype = 'p', false) AS  is_primary_key,
    CASE WHEN a.atttypid = ANY ('{int,int8,int2}'::regtype[])
      AND EXISTS (
         SELECT 1 FROM pg_attrdef ad
         WHERE  ad.adrelid = a.attrelid
         AND    ad.adnum   = a.attnum
         AND    pg_get_expr(ad.adbin, ad.adrelid) = 'nextval('''
            || (pg_get_serial_sequence (a.attrelid::regclass::text
                                      , a.attname))::regclass
            || '''::regclass)'
         )
    THEN CASE a.atttypid
            WHEN 'int'::regtype  THEN 'serial'
            WHEN 'int8'::regtype THEN 'bigserial'
            WHEN 'int2'::regtype THEN 'smallserial'
         END
    ELSE format_type(a.atttypid, a.atttypmod)
    END AS data_type
FROM pg_attribute a
JOIN ONLY pg_class c ON c.oid = a.attrelid
JOIN ONLY pg_namespace n ON n.oid = c.relnamespace
LEFT JOIN pg_constraint ct ON ct.conrelid = c.oid
AND a.attnum = ANY(ct.conkey) AND ct.contype IN ('p', 'u')
LEFT JOIN pg_attrdef ad ON ad.adrelid = c.oid AND ad.adnum = a.attnum
LEFT JOIN pg_description pd ON pd.objoid = a.attrelid AND pd.objsubid = a.attnum
WHERE a.attisdropped = false
AND n.nspname = $1
AND c.relname = $2
AND a.attnum > 0
ORDER BY a.attnum
`

const tableDefSQL = `
SELECT
  c.relname AS table_name,
  pd.description AS description
FROM pg_class c
JOIN ONLY pg_namespace n
ON n.oid = c.relnamespace
LEFT JOIN pg_description pd ON pd.objoid = c.oid AND pd.objsubid = 0
WHERE n.nspname = $1
AND c.relkind = 'r'
ORDER BY c.relname
`

const fkDefSQL = `
select
  att2.attname as "child_column"
  , cl.relname as "parent_table"
  , att.attname as "parent_column"
  , con.conname
  , case
      when pi.indisprimary is null then false
      else pi.indisprimary
    end as "is_parent_pk"
  , case
      when ci.indisprimary is null then false
      else ci.indisprimary
    end as "is_child_pk"
from (
  select
    unnest(con1.conkey) as "parent"
    , unnest(con1.confkey) as "child"
    , con1.confrelid
    , con1.conrelid
    , con1.conname
  from pg_class cl
  join pg_namespace ns on cl.relnamespace = ns.oid
  join pg_constraint con1 on con1.conrelid = cl.oid
  where ns.nspname = $1
  and cl.relname = $2
  and con1.contype = 'f'
) con
join pg_attribute att
on att.attrelid = con.confrelid and att.attnum = con.child
left outer join pg_index pi
on att.attrelid = pi.indrelid and att.attnum = any(pi.indkey)
join pg_class cl
on cl.oid = con.confrelid
join pg_attribute att2
on att2.attrelid = con.conrelid and att2.attnum = con.parent
left outer join pg_index ci
on att2.attrelid = ci.indrelid and att2.attnum = any(ci.indkey)
order by con.conname
`
<|endoftext|>


<commit_before>package commands

import (
	"path/filepath"
	"strings"

	"github.com/git-lfs/git-lfs/errors"
	"github.com/git-lfs/git-lfs/git"
	"github.com/git-lfs/git-lfs/git/odb"
	"github.com/spf13/cobra"
)

var (
	// migrateIncludeRefs is a set of Git references to explicitly include
	// in the migration.
	migrateIncludeRefs []string
	// migrateExcludeRefs is a set of Git references to explicitly exclude
	// in the migration.
	migrateExcludeRefs []string
)

// getObjectDatabase creates a *git.ObjectDatabase from the filesystem pointed
// at the .git directory of the currently checked-out repository.
func getObjectDatabase() (*odb.ObjectDatabase, error) {
	dir, err := git.GitDir()
	if err != nil {
		return nil, errors.Wrap(err, "cannot open root")
	}
	return odb.FromFilesystem(filepath.Join(dir, "objects"))
}

// getRemoteRefs returns a fully qualified set of references belonging to all
// remotes known by the currently checked-out repository, or an error if those
// references could not be determined.
func getRemoteRefs() ([]string, error) {
	var refs []string

	remotes, err := git.RemoteList()
	if err != nil {
		return nil, err
	}

	for _, remote := range remotes {
		refsForRemote, err := git.RemoteRefs(remote)
		if err != nil {
			return nil, err
		}

		for _, ref := range refsForRemote {
			refs = append(refs, formatRefName(ref, remote))
		}
	}

	return refs, nil
}

// formatRefName returns the fully-qualified name for the given Git reference
// "ref".
func formatRefName(ref *git.Ref, remote string) string {
	var name []string

	switch ref.Type {
	case git.RefTypeRemoteBranch:
		name = []string{"refs", "remotes", remote, ref.Name}
	case git.RefTypeRemoteTag:
		name = []string{"refs", "tags", ref.Name}
	default:
		return ref.Name
	}
	return strings.Join(name, "/")

}

// currentRefToMigrate returns the fully-qualified name of the currently
// checked-out reference, or an error if the reference's type was not a local
// branch.
func currentRefToMigrate() (*git.Ref, error) {
	current, err := git.CurrentRef()
	if err != nil {
		return nil, err
	}

	if current.Type == git.RefTypeOther ||
		current.Type == git.RefTypeRemoteBranch ||
		current.Type == git.RefTypeRemoteTag {

		return nil, errors.Errorf("fatal: cannot migrate non-local ref: %s", current.Name)
	}
	return current, nil
}

func init() {
	RegisterCommand("migrate", nil, func(cmd *cobra.Command) {
		// Adding flags directly to cmd.Flags() doesn't apply those
		// flags to any subcommands of the root. Therefore, loop through
		// each subcommand specifically, and include common arguments to
		// each.
		//
		// Once done, link each orphaned command to the
		// `git-lfs-migrate(1)` command as a subcommand (child).

		for _, subcommand := range []*cobra.Command{} {
			subcommand.Flags().StringVarP(&includeArg, "include", "I", "", "Include a list of paths")
			subcommand.Flags().StringVarP(&excludeArg, "exclude", "X", "", "Exclude a list of paths")

			subcommand.Flags().StringSliceVar(&migrateIncludeRefs, "include-ref", nil, "An explicit list of refs to include")
			subcommand.Flags().StringSliceVar(&migrateExcludeRefs, "exclude-ref", nil, "An explicit list of refs to exclude")

			cmd.AddCommand(subcommand)
		}
	})
}
<commit_msg>commands/command_migrate: teach how to determine included/excluded refs<commit_after>package commands

import (
	"path/filepath"
	"strings"

	"github.com/git-lfs/git-lfs/errors"
	"github.com/git-lfs/git-lfs/git"
	"github.com/git-lfs/git-lfs/git/odb"
	"github.com/spf13/cobra"
)

var (
	// migrateIncludeRefs is a set of Git references to explicitly include
	// in the migration.
	migrateIncludeRefs []string
	// migrateExcludeRefs is a set of Git references to explicitly exclude
	// in the migration.
	migrateExcludeRefs []string
)

// getObjectDatabase creates a *git.ObjectDatabase from the filesystem pointed
// at the .git directory of the currently checked-out repository.
func getObjectDatabase() (*odb.ObjectDatabase, error) {
	dir, err := git.GitDir()
	if err != nil {
		return nil, errors.Wrap(err, "cannot open root")
	}
	return odb.FromFilesystem(filepath.Join(dir, "objects"))
}

// includeExcludeRefs returns fully-qualified sets of references to include, and
// exclude, or an error if those could not be determined.
//
// They are determined based on the following rules:
//
//   - Include all local refs/heads/<branch> references for each branch
//     specified as an argument.
//   - Include the currently checked out branch if no branches are given as
//     arguments and the --include-ref= or --exclude-ref= flag(s) aren't given.
//   - Include all references given in --include-ref=<ref>.
//   - Exclude all references given in --exclude-ref=<ref>.
func includeExcludeRefs(args []string) (include, exclude []string, err error) {
	hardcore := len(migrateIncludeRefs) > 0 || len(migrateExcludeRefs) > 0

	if len(args) == 0 && !hardcore {
		// If no branches were given explicitly AND neither
		// --include-ref or --exclude-ref flags were given, then add the
		// currently checked out reference.
		current, err := currentRefToMigrate()
		if err != nil {
			return nil, nil, err
		}
		args = append(args, current.Name)
	}

	for _, name := range args {
		// Then, loop through each branch given, resolve that reference,
		// and include it.
		ref, err := git.ResolveRef(name)
		if err != nil {
			return nil, nil, err
		}

		include = append(include, ref.Name)
	}

	if hardcore {
		// If either --include-ref=<ref> or --exclude-ref=<ref> were
		// given, append those to the include and excluded reference
		// set, respectively.
		include = append(include, migrateIncludeRefs...)
		exclude = append(exclude, migrateExcludeRefs...)
	} else {
		// Otherwise, if neither --include-ref=<ref> or
		// --exclude-ref=<ref> were given, include no additional
		// references, and exclude all remote references that are remote
		// branches or remote tags.
		remoteRefs, err := getRemoteRefs()
		if err != nil {
			return nil, nil, err
		}

		exclude = append(exclude, remoteRefs...)
	}

	return include, exclude, nil
}

// getRemoteRefs returns a fully qualified set of references belonging to all
// remotes known by the currently checked-out repository, or an error if those
// references could not be determined.
func getRemoteRefs() ([]string, error) {
	var refs []string

	remotes, err := git.RemoteList()
	if err != nil {
		return nil, err
	}

	for _, remote := range remotes {
		refsForRemote, err := git.RemoteRefs(remote)
		if err != nil {
			return nil, err
		}

		for _, ref := range refsForRemote {
			refs = append(refs, formatRefName(ref, remote))
		}
	}

	return refs, nil
}

// formatRefName returns the fully-qualified name for the given Git reference
// "ref".
func formatRefName(ref *git.Ref, remote string) string {
	var name []string

	switch ref.Type {
	case git.RefTypeRemoteBranch:
		name = []string{"refs", "remotes", remote, ref.Name}
	case git.RefTypeRemoteTag:
		name = []string{"refs", "tags", ref.Name}
	default:
		return ref.Name
	}
	return strings.Join(name, "/")

}

// currentRefToMigrate returns the fully-qualified name of the currently
// checked-out reference, or an error if the reference's type was not a local
// branch.
func currentRefToMigrate() (*git.Ref, error) {
	current, err := git.CurrentRef()
	if err != nil {
		return nil, err
	}

	if current.Type == git.RefTypeOther ||
		current.Type == git.RefTypeRemoteBranch ||
		current.Type == git.RefTypeRemoteTag {

		return nil, errors.Errorf("fatal: cannot migrate non-local ref: %s", current.Name)
	}
	return current, nil
}

func init() {
	RegisterCommand("migrate", nil, func(cmd *cobra.Command) {
		// Adding flags directly to cmd.Flags() doesn't apply those
		// flags to any subcommands of the root. Therefore, loop through
		// each subcommand specifically, and include common arguments to
		// each.
		//
		// Once done, link each orphaned command to the
		// `git-lfs-migrate(1)` command as a subcommand (child).

		for _, subcommand := range []*cobra.Command{} {
			subcommand.Flags().StringVarP(&includeArg, "include", "I", "", "Include a list of paths")
			subcommand.Flags().StringVarP(&excludeArg, "exclude", "X", "", "Exclude a list of paths")

			subcommand.Flags().StringSliceVar(&migrateIncludeRefs, "include-ref", nil, "An explicit list of refs to include")
			subcommand.Flags().StringSliceVar(&migrateExcludeRefs, "exclude-ref", nil, "An explicit list of refs to exclude")

			cmd.AddCommand(subcommand)
		}
	})
}
<|endoftext|>


<commit_before>// +build windows

package commands

import (
	"fmt"
	"log"
	"os"
)

func askToConfirm() bool {
	fmt.Printf("%s (y/n): ", prompt)

	var response string
	_, err := fmt.Fscanf(os.Stdin, "%s\n", &response)
	if err != nil {
		log.Fatalln("failed to read response:", err)
	}

	return response == "y"
}
<commit_msg>windowwwwwsssssssss<commit_after>// +build windows

package commands

import (
	"fmt"
	"log"
	"os"
)

func askToConfirm(prompt string) bool {
	fmt.Printf("%s (y/n): ", prompt)

	var response string
	_, err := fmt.Fscanf(os.Stdin, "%s\n", &response)
	if err != nil {
		log.Fatalln("failed to read response:", err)
	}

	return response == "y"
}
<|endoftext|>


<commit_before>package profile

import (
	"fmt"
	"github.com/0x263b/Porygon2"
	"strings"
)

func whoIs(command *bot.Cmd, matches []string) (msg string, err error) {
	nick := matches[1]

	lastfm := bot.GetUserKey(nick, "lastfm")
	if lastfm != "" {
		lastfm = fmt.Sprintf(" | Last.fm: %s", lastfm)
	}

	twitter := bot.GetUserKey(nick, "twitter")
	if twitter != "" {
		twitter = fmt.Sprintf(" | Twitter: @%s", twitter)
	}

	url := bot.GetUserKey(nick, "url")
	if url != "" {
		url = fmt.Sprintf(" | URL: %s", url)
	}

	return fmt.Sprintf("%s%s%s", nick, lastfm, twitter, url), nil
}

func setUrl(command *bot.Cmd, matches []string) (msg string, err error) {
	bot.SetUserKey(command.Nick, "url", strings.TrimSpace(matches[1]))
	return fmt.Sprintf("%s: url updated to: %s", command.Nick, matches[1]), nil
}

func setTwitter(command *bot.Cmd, matches []string) (msg string, err error) {
	bot.SetUserKey(command.Nick, "twitter", strings.TrimSpace(matches[1]))
	return fmt.Sprintf("%s: twitter updated to: %s", command.Nick, matches[1]), nil
}

func init() {
	bot.RegisterCommand("^set url (.+)$", setUrl)
	bot.RegisterCommand("^set twitter (\\S+)$", setTwitter)
	bot.RegisterCommand("^whois (\\S+)$", whoIs)
}
<commit_msg>Wrong type<commit_after>package profile

import (
	"fmt"
	"github.com/0x263b/Porygon2"
	"strings"
)

func whoIs(command *bot.Cmd, matches []string) (msg string, err error) {
	nick := matches[1]

	lastfm := bot.GetUserKey(nick, "lastfm")
	if lastfm != "" {
		lastfm = fmt.Sprintf(" | Last.fm: %s", string(lastfm))
	}

	twitter := bot.GetUserKey(nick, "twitter")
	if twitter != "" {
		twitter = fmt.Sprintf(" | Twitter: @%s", string(twitter))
	}

	url := bot.GetUserKey(nick, "url")
	if url != "" {
		url = fmt.Sprintf(" | URL: %s", string(url))
	}

	return fmt.Sprintf("%s%s%s", nick, lastfm, twitter, url), nil
}

func setUrl(command *bot.Cmd, matches []string) (msg string, err error) {
	bot.SetUserKey(command.Nick, "url", strings.TrimSpace(matches[1]))
	return fmt.Sprintf("%s: url updated to: %s", command.Nick, matches[1]), nil
}

func setTwitter(command *bot.Cmd, matches []string) (msg string, err error) {
	bot.SetUserKey(command.Nick, "twitter", strings.TrimSpace(matches[1]))
	return fmt.Sprintf("%s: twitter updated to: %s", command.Nick, matches[1]), nil
}

func init() {
	bot.RegisterCommand("^set url (.+)$", setUrl)
	bot.RegisterCommand("^set twitter (\\S+)$", setTwitter)
	bot.RegisterCommand("^whois (\\S+)$", whoIs)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/go-martini/martini"
	"github.com/gorilla/websocket"
	"github.com/martini-contrib/sessions"
	"log"
	"math/rand"
	"net/http"
	"os"
	"sync"
	"time"
)

type Config struct {
	Secret  string
	Address string
}

type LockingWebsockets struct {
	mutex sync.RWMutex
	byId  map[int64]*websocket.Conn
}

var websockets *LockingWebsockets

func (c *LockingWebsockets) deleteWebsocket(id int64) {
	c.mutex.Lock()
	defer c.mutex.Unlock()
	delete(c.byId, id)
}

func (c *LockingWebsockets) addWebsocket(id int64, ws *websocket.Conn) {
	c.mutex.Lock()
	defer c.mutex.Unlock()
	c.byId[id] = ws
}

func main() {
	websockets = &LockingWebsockets{
		byId: make(map[int64]*websocket.Conn),
	}
	consoleReadChannel = make(chan ConsoleChunk)
	go consoleDispatch()
	vzcontrol := ConnectVZControl()
	defer vzcontrol.Close()

	file, _ := os.Open("game_server.cfg")
	decoder := json.NewDecoder(file)
	config := Config{}
	decoder.Decode(&config)

	m := martini.Classic()
	store := sessions.NewCookieStore([]byte(config.Secret))
	m.Use(sessions.Sessions("session", store))

	m.Get("/reset/" + config.Secret, func(w http.ResponseWriter, r *http.Request, session sessions.Session) string {
		err := vzcontrol.Reset()
		if err != nil {
			return err.Error()
		}
		return "Done"
	})

	generating := false
	gr := NewGraph()
	m.Get("/gen", func(w http.ResponseWriter, r *http.Request, session sessions.Session) string {
		if generating {
			return "Already generating"
		}
		generating = true
		maxNodes := 5
		maxEdges := 5
		startNodeId := 100

		startNode := Node{Id: NodeId(startNodeId)}
		gr.AddNode(startNode)
		err := vzcontrol.ContainerCreate(int64(startNode.Id))

		nodes := make([]Node, 0)
		nodes = append(nodes, startNode)

		steps := 1
		for len(nodes) != 0 && steps < maxNodes {
			node, nodes := nodes[len(nodes)-1], nodes[:len(nodes)-1]

			numEdges := random(1, maxEdges)
			for i := 1; i <= numEdges; i++ {
				targetNode := Node{Id: NodeId(i*steps + startNodeId)}
				if gr.AddNode(targetNode) {
					err = vzcontrol.ContainerCreate(int64(targetNode.Id))
					if err != nil {
						return fmt.Sprintf("Container Create: %d, %d, %d\n%s", targetNode.Id, i*steps, numEdges, err.Error())
					}
					nodes = append(nodes, targetNode)
					edgeid := int64(i * steps)
					if gr.AddEdge(Edge{Id: EdgeId(edgeid), Head: node.Id, Tail: targetNode.Id}) {
						err = vzcontrol.NetworkCreate(edgeid)
						if err != nil {
							return fmt.Sprintf("Network Create: %d\n%s", edgeid, err.Error())
						}
						err = vzcontrol.NetworkAdd(int64(node.Id), edgeid)
						if err != nil {
							return fmt.Sprintf("Network Add Node: %d, %d\n%s", node.Id, edgeid, err.Error())
						}
						err = vzcontrol.NetworkAdd(int64(targetNode.Id), edgeid)
						if err != nil {
							return fmt.Sprintf("Network Add Target: %d, %d\n%s", targetNode.Id, edgeid, err.Error())
						}
					}
				}

			}
			steps += 1
		}
		return gr.String()
	})

	m.Get("/graph", func(w http.ResponseWriter, r *http.Request, session sessions.Session) string {
		output, err := json.Marshal(gr)
		if err != nil {
			return err.Error()
		}
		return string(output)
	})

	m.Get("/ws", func(w http.ResponseWriter, r *http.Request, session sessions.Session) {
		return //FOR NOW JUST IGNORE WEBSOCKETS
		/*ws, err := websocket.Upgrade(w, r, nil, 1024, 1024)
		  if _, ok := err.(websocket.HandshakeError); ok {
		      http.Error(w, "Not a websocket handshake", 400)
		      log.Println(err)
		      return
		  } else if err != nil {
		      log.Println(err)
		      return
		  }
		  defer ws.Close()
		  ws.WriteMessage(websocket.TextMessage, []byte("Welcome to ginux!\r\n"))
		  //get vm numbert
		  vm_cookie := session.Get("vm_id")
		  var vm_id int64
		  if vm_cookie == nil {
		      ws.WriteMessage(websocket.TextMessage, []byte("Create a node at ginux.gamingrobot.net/create\r\n"))
		      return
		  } else {
		      vm_id = vm_cookie.(int64)
		  }

		  _, exists := websockets.byId[vm_id];
		  if !exists{
		      websockets.addWebsocket(vm_id, ws)
		      defer websockets.deleteWebsocket(vm_id)
		  }
		  //spawn console
		  log.Println(vm_id)
		  err = vzcontrol.ConsoleStart(vm_id)
		  if err != nil {
		      ws.WriteMessage(websocket.TextMessage, []byte(err.Error()))
		      return
		  }
		  for {
		      _, message, err := ws.ReadMessage()
		      if err != nil {
		          err = vzcontrol.ConsoleKill(vm_id)
		          log.Println(err)
		          return
		      } else {
		          vzcontrol.ConsoleWrite(vm_id, message)
		      }
		  }*/
	})
	log.Println("Game Server started on", config.Address)
	log.Fatal(http.ListenAndServe(config.Address, m))
}

func random(min, max int) int {
	rand.Seed(time.Now().Unix())
	return rand.Intn(max-min) + min
}

func consoleDispatch() {
	for chunk := range consoleReadChannel {
		websockets.mutex.RLock()
		if socket, ok := websockets.byId[chunk.Id]; ok {
			socket.WriteMessage(websocket.TextMessage, chunk.Data)
		}
		websockets.mutex.RUnlock()
	}
}
<commit_msg>Forgot graph reset<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/go-martini/martini"
	"github.com/gorilla/websocket"
	"github.com/martini-contrib/sessions"
	"log"
	"math/rand"
	"net/http"
	"os"
	"sync"
	"time"
)

type Config struct {
	Secret  string
	Address string
}

type LockingWebsockets struct {
	mutex sync.RWMutex
	byId  map[int64]*websocket.Conn
}

var websockets *LockingWebsockets

func (c *LockingWebsockets) deleteWebsocket(id int64) {
	c.mutex.Lock()
	defer c.mutex.Unlock()
	delete(c.byId, id)
}

func (c *LockingWebsockets) addWebsocket(id int64, ws *websocket.Conn) {
	c.mutex.Lock()
	defer c.mutex.Unlock()
	c.byId[id] = ws
}

func main() {
	websockets = &LockingWebsockets{
		byId: make(map[int64]*websocket.Conn),
	}
	consoleReadChannel = make(chan ConsoleChunk)
	go consoleDispatch()
	vzcontrol := ConnectVZControl()
	defer vzcontrol.Close()

	file, _ := os.Open("game_server.cfg")
	decoder := json.NewDecoder(file)
	config := Config{}
	decoder.Decode(&config)

	m := martini.Classic()
	store := sessions.NewCookieStore([]byte(config.Secret))
	m.Use(sessions.Sessions("session", store))

	generating := false
	gr := NewGraph()
	m.Get("/reset/" + config.Secret, func(w http.ResponseWriter, r *http.Request, session sessions.Session) string {
		err := vzcontrol.Reset()
		if err != nil {
			return err.Error()
		}
		generating = false
		gr = NewGraph()
		return "Done"
	})

	m.Get("/gen", func(w http.ResponseWriter, r *http.Request, session sessions.Session) string {
		if generating {
			return "Already generating"
		}
		generating = true
		maxNodes := 5
		maxEdges := 5
		startNodeId := 100

		startNode := Node{Id: NodeId(startNodeId)}
		gr.AddNode(startNode)
		err := vzcontrol.ContainerCreate(int64(startNode.Id))

		nodes := make([]Node, 0)
		nodes = append(nodes, startNode)

		steps := 1
		for len(nodes) != 0 && steps < maxNodes {
			node, nodes := nodes[len(nodes)-1], nodes[:len(nodes)-1]

			numEdges := random(1, maxEdges)
			for i := 1; i <= numEdges; i++ {
				targetNode := Node{Id: NodeId(i*steps + startNodeId)}
				if gr.AddNode(targetNode) {
					err = vzcontrol.ContainerCreate(int64(targetNode.Id))
					if err != nil {
						return fmt.Sprintf("Container Create: %d, %d, %d\n%s", targetNode.Id, i*steps, numEdges, err.Error())
					}
					nodes = append(nodes, targetNode)
					edgeid := int64(i * steps)
					if gr.AddEdge(Edge{Id: EdgeId(edgeid), Head: node.Id, Tail: targetNode.Id}) {
						err = vzcontrol.NetworkCreate(edgeid)
						if err != nil {
							return fmt.Sprintf("Network Create: %d\n%s", edgeid, err.Error())
						}
						err = vzcontrol.NetworkAdd(int64(node.Id), edgeid)
						if err != nil {
							return fmt.Sprintf("Network Add Node: %d, %d\n%s", node.Id, edgeid, err.Error())
						}
						err = vzcontrol.NetworkAdd(int64(targetNode.Id), edgeid)
						if err != nil {
							return fmt.Sprintf("Network Add Target: %d, %d\n%s", targetNode.Id, edgeid, err.Error())
						}
					}
				}

			}
			steps += 1
		}
		return gr.String()
	})

	m.Get("/graph", func(w http.ResponseWriter, r *http.Request, session sessions.Session) string {
		output, err := json.Marshal(gr)
		if err != nil {
			return err.Error()
		}
		return string(output)
	})

	m.Get("/ws", func(w http.ResponseWriter, r *http.Request, session sessions.Session) {
		return //FOR NOW JUST IGNORE WEBSOCKETS
		/*ws, err := websocket.Upgrade(w, r, nil, 1024, 1024)
		  if _, ok := err.(websocket.HandshakeError); ok {
		      http.Error(w, "Not a websocket handshake", 400)
		      log.Println(err)
		      return
		  } else if err != nil {
		      log.Println(err)
		      return
		  }
		  defer ws.Close()
		  ws.WriteMessage(websocket.TextMessage, []byte("Welcome to ginux!\r\n"))
		  //get vm numbert
		  vm_cookie := session.Get("vm_id")
		  var vm_id int64
		  if vm_cookie == nil {
		      ws.WriteMessage(websocket.TextMessage, []byte("Create a node at ginux.gamingrobot.net/create\r\n"))
		      return
		  } else {
		      vm_id = vm_cookie.(int64)
		  }

		  _, exists := websockets.byId[vm_id];
		  if !exists{
		      websockets.addWebsocket(vm_id, ws)
		      defer websockets.deleteWebsocket(vm_id)
		  }
		  //spawn console
		  log.Println(vm_id)
		  err = vzcontrol.ConsoleStart(vm_id)
		  if err != nil {
		      ws.WriteMessage(websocket.TextMessage, []byte(err.Error()))
		      return
		  }
		  for {
		      _, message, err := ws.ReadMessage()
		      if err != nil {
		          err = vzcontrol.ConsoleKill(vm_id)
		          log.Println(err)
		          return
		      } else {
		          vzcontrol.ConsoleWrite(vm_id, message)
		      }
		  }*/
	})
	log.Println("Game Server started on", config.Address)
	log.Fatal(http.ListenAndServe(config.Address, m))
}

func random(min, max int) int {
	rand.Seed(time.Now().Unix())
	return rand.Intn(max-min) + min
}

func consoleDispatch() {
	for chunk := range consoleReadChannel {
		websockets.mutex.RLock()
		if socket, ok := websockets.byId[chunk.Id]; ok {
			socket.WriteMessage(websocket.TextMessage, chunk.Data)
		}
		websockets.mutex.RUnlock()
	}
}
<|endoftext|>


<commit_before>//
// Package stn is a library for processing Simple Timesheet Notation.
//
// stn.go - implements a version of Simple Timesheet Notation as a Go package.
// @author R. S. Doiel, <rsdoiel@gmail.com>
// copyright (c) 2015 all rights reserved.
// Released under the BSD 2-Clause license
// See: http://opensource.org/licenses/BSD-2-Clause
//
package stn

import (
	"encoding/json"
	"errors"
	"fmt"
	"regexp"
	"strings"
	"time"
)

// Version of stn.go package.
const Version = "v0.0.4"

var (
	dateLineRE       = regexp.MustCompile("^[0-9][0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9]$")
	legacyDateLineRE = regexp.MustCompile("^[0-1][0-9]/[0-3][0-9]/[0-9][0-9][0-9][0-9]$")
	entryLineRE      = regexp.MustCompile("^([0-2][0-9]:[0-6][0-9]|[0-9]:[0-6][0-9]) - ([0-2][0-9]:[0-6][0-9]|[0-9]:[0-6][0-9]);")
)

// Entry is the basic data element generated when parsing a file contactining
// Simple Timesheet Notation. It is designed to easily turning to JSON, CSV
// or other useful formats.
type Entry struct {
	Start       time.Time
	End         time.Time
	Annotations []string // cells of contextual data (e.g. project, activity, notes)
}

// IsDateLine validates a line as appropriate to pass to ParseDateLine.
func IsDateLine(line string) bool {
	if dateLineRE.MatchString(strings.TrimSpace(line)) {
		return true
	}
	if legacyDateLineRE.MatchString(strings.TrimSpace(line)) {
		return true
	}
	return false
}

// ParseDateLine sets the current date context when parsing Simple Timesheet Notation
// elements. It is what is recorded in Occurrence field of an Entry.
func ParseDateLine(line string) string {
	if dateLineRE.MatchString(strings.TrimSpace(line)) {
		return strings.TrimSpace(line)
	}
	if legacyDateLineRE.MatchString(strings.TrimSpace(line)) {
		parts := strings.SplitN(strings.TrimSpace(line), "/", 3)
		return fmt.Sprintf("%s-%s-%s", parts[2], parts[0], parts[1])
	}
	return ""
}

// IsEntry validates a line as an "Entry" to be parsed.
func IsEntry(line string) bool {
	if entryLineRE.MatchString(strings.TrimSpace(line)) {
		return true
	}
	return false
}

func splitCells(line string) []string {
	return strings.Split(line, ";")
}

func splitRangeElements(timeRange string) (string, string, error) {
	if strings.Index(timeRange, " - ") != -1 {
		parts := strings.SplitN(timeRange, " - ", 2)
		return parts[0], parts[1], nil
	}
	return "", "", errors.New("[" + timeRange + "] is not a valid time range string. ")
}

func parseRangeElements(start string, end string) (time.Time, time.Time, error) {
	startTime, err1 := time.Parse("2006-01-02 15:04 MST", start)
	endTime, err2 := time.Parse("2006-01-02 15:04 MST", end)
	//NOTE: need to handle the case where someone has entered an end time ran
	// smaller than start (e.g. 8:00 - 1:00 meaning 1pm should become 13:00)
	if startTime.Unix() > endTime.Unix() {
		plus12hr, _ := time.ParseDuration("+12h")
		endTime = endTime.Add(plus12hr)
	}
	if err1 != nil {
		return startTime, endTime, err1
	}
	if err2 != nil {
		return startTime, endTime, err2
	}
	return startTime, endTime, nil
}

// ParseEntry takes a string and the active date as a string and
// returns a Entry structure and error value.
func ParseEntry(activeDate string, line string) (*Entry, error) {
	if IsDateLine(activeDate) == false {
		return nil, errors.New("invalid format for active date")
	}
	if IsEntry(line) == false {
		return nil, errors.New("invalid format for entry")
	}
	cells := splitCells(line)
	if len(cells) < 2 {
		return nil, errors.New("entry line missing cells")
	}

	s, e, err := splitRangeElements(cells[0])
	if err != nil {
		return nil, err
	}

	// NOTE: for now I am assume timesheets are in local time.
	// Need to think about supporting other timezone for things like
	// timesheets during event travel.
	zone, _ := time.Now().Zone()
	start, end, err := parseRangeElements(activeDate+" "+s+" "+zone,
		activeDate+" "+e+" "+zone)
	if err != nil {
		return nil, err
	}

	for i := 1; i < len(cells); i++ {
		cells[i] = strings.TrimSpace(cells[i])
	}

	var entry *Entry
	entry = &Entry{
		Start:       start,
		End:         end,
		Annotations: cells[1:],
	}
	return entry, nil
}

// JSON converts an Entry struct to JSON notation.
func (e *Entry) JSON() string {
	src, _ := json.Marshal(e)
	return string(src)
}

// String converts an Entry struct to a tab delimited string.
func (e *Entry) String() string {
	return e.Start.Format(time.RFC3339) + "\t" + e.End.Format(time.RFC3339) +
		"\t" + strings.Join(e.Annotations[:], "\t")
}

// FromString reads a tab delimited string formatted with Stringback into a Entry struct
func (e *Entry) FromString(line string) bool {
	var err error
	parts := strings.Split(line, "\t")
	if len(parts) < 3 {
		return false
	}
	e.Start, err = time.Parse(time.RFC3339, parts[0])
	if err != nil {
		return false
	}
	e.End, err = time.Parse(time.RFC3339, parts[1])
	if err != nil {
		return false
	}
	e.Annotations = parts[2:]
	return true
}

// IsInRange checks the start and end times of an Entry structure to see if it is in the time range
func (e *Entry) IsInRange(start time.Time, end time.Time) bool {
	t1 := e.Start.Unix()
	if t1 >= start.Unix() && t1 <= end.Unix() {
		return true
	}
	return false
}

// IsMatch checks the Entry struct Annotations for matching substring
func (e *Entry) IsMatch(match string) bool {
	matched := false
	//NOTE: search all columns
	for i := 0; i < len(e.Annotations); i++ {
		if strings.Contains(e.Annotations[i], match) == true {
			matched = true
			break
		}
	}
	return matched
}
<commit_msg>Quick Save<commit_after>//
// Package stn is a library for processing Simple Timesheet Notation.
//
// stn.go - implements a version of Simple Timesheet Notation as a Go package.
// @author R. S. Doiel, <rsdoiel@gmail.com>
// copyright (c) 2015 all rights reserved.
// Released under the BSD 2-Clause license
// See: http://opensource.org/licenses/BSD-2-Clause
//
package stn

import (
	"encoding/json"
	"errors"
	"fmt"
	"regexp"
	"strings"
	"time"
)

// Version of stn.go package.
const (
	Version = "v0.0.4"
)

var (
	dateLineRE       = regexp.MustCompile("^[0-9][0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9]$")
	legacyDateLineRE = regexp.MustCompile("^[0-1][0-9]/[0-3][0-9]/[0-9][0-9][0-9][0-9]$")
	entryLineRE      = regexp.MustCompile("^([0-2][0-9]:[0-6][0-9]|[0-9]:[0-6][0-9]) - ([0-2][0-9]:[0-6][0-9]|[0-9]:[0-6][0-9]);")
)

// Entry is the basic data element generated when parsing a file contactining
// Simple Timesheet Notation. It is designed to easily turning to JSON, CSV
// or other useful formats.
type Entry struct {
	Start       time.Time
	End         time.Time
	Annotations []string // cells of contextual data (e.g. project, activity, notes)
}

// IsDateLine validates a line as appropriate to pass to ParseDateLine.
func IsDateLine(line string) bool {
	if dateLineRE.MatchString(strings.TrimSpace(line)) {
		return true
	}
	if legacyDateLineRE.MatchString(strings.TrimSpace(line)) {
		return true
	}
	return false
}

// ParseDateLine sets the current date context when parsing Simple Timesheet Notation
// elements. It is what is recorded in Occurrence field of an Entry.
func ParseDateLine(line string) string {
	if dateLineRE.MatchString(strings.TrimSpace(line)) {
		return strings.TrimSpace(line)
	}
	if legacyDateLineRE.MatchString(strings.TrimSpace(line)) {
		parts := strings.SplitN(strings.TrimSpace(line), "/", 3)
		return fmt.Sprintf("%s-%s-%s", parts[2], parts[0], parts[1])
	}
	return ""
}

// IsEntry validates a line as an "Entry" to be parsed.
func IsEntry(line string) bool {
	if entryLineRE.MatchString(strings.TrimSpace(line)) {
		return true
	}
	return false
}

func splitCells(line string) []string {
	return strings.Split(line, ";")
}

func splitRangeElements(timeRange string) (string, string, error) {
	if strings.Index(timeRange, " - ") != -1 {
		parts := strings.SplitN(timeRange, " - ", 2)
		return parts[0], parts[1], nil
	}
	return "", "", errors.New("[" + timeRange + "] is not a valid time range string. ")
}

func parseRangeElements(start string, end string) (time.Time, time.Time, error) {
	startTime, err1 := time.Parse("2006-01-02 15:04 MST", start)
	endTime, err2 := time.Parse("2006-01-02 15:04 MST", end)
	//NOTE: need to handle the case where someone has entered an end time ran
	// smaller than start (e.g. 8:00 - 1:00 meaning 1pm should become 13:00)
	if startTime.Unix() > endTime.Unix() {
		plus12hr, _ := time.ParseDuration("+12h")
		endTime = endTime.Add(plus12hr)
	}
	if err1 != nil {
		return startTime, endTime, err1
	}
	if err2 != nil {
		return startTime, endTime, err2
	}
	return startTime, endTime, nil
}

// ParseEntry takes a string and the active date as a string and
// returns a Entry structure and error value.
func ParseEntry(activeDate string, line string) (*Entry, error) {
	if IsDateLine(activeDate) == false {
		return nil, errors.New("invalid format for active date")
	}
	if IsEntry(line) == false {
		return nil, errors.New("invalid format for entry")
	}
	cells := splitCells(line)
	if len(cells) < 2 {
		return nil, errors.New("entry line missing cells")
	}

	s, e, err := splitRangeElements(cells[0])
	if err != nil {
		return nil, err
	}

	// NOTE: for now I am assume timesheets are in local time.
	// Need to think about supporting other timezone for things like
	// timesheets during event travel.
	zone, _ := time.Now().Zone()
	start, end, err := parseRangeElements(activeDate+" "+s+" "+zone,
		activeDate+" "+e+" "+zone)
	if err != nil {
		return nil, err
	}

	for i := 1; i < len(cells); i++ {
		cells[i] = strings.TrimSpace(cells[i])
	}

	var entry *Entry
	entry = &Entry{
		Start:       start,
		End:         end,
		Annotations: cells[1:],
	}
	return entry, nil
}

// JSON converts an Entry struct to JSON notation.
func (e *Entry) JSON() string {
	src, _ := json.Marshal(e)
	return string(src)
}

// String converts an Entry struct to a tab delimited string.
func (e *Entry) String() string {
	return e.Start.Format(time.RFC3339) + "\t" + e.End.Format(time.RFC3339) +
		"\t" + strings.Join(e.Annotations[:], "\t")
}

// FromString reads a tab delimited string formatted with Stringback into a Entry struct
func (e *Entry) FromString(line string) bool {
	var err error
	parts := strings.Split(line, "\t")
	if len(parts) < 3 {
		return false
	}
	e.Start, err = time.Parse(time.RFC3339, parts[0])
	if err != nil {
		return false
	}
	e.End, err = time.Parse(time.RFC3339, parts[1])
	if err != nil {
		return false
	}
	e.Annotations = parts[2:]
	return true
}

// IsInRange checks the start and end times of an Entry structure to see if it is in the time range
func (e *Entry) IsInRange(start time.Time, end time.Time) bool {
	t1 := e.Start.Unix()
	if t1 >= start.Unix() && t1 <= end.Unix() {
		return true
	}
	return false
}

// IsMatch checks the Entry struct Annotations for matching substring
func (e *Entry) IsMatch(match string) bool {
	matched := false
	//NOTE: search all columns
	for i := 0; i < len(e.Annotations); i++ {
		if strings.Contains(e.Annotations[i], match) == true {
			matched = true
			break
		}
	}
	return matched
}
<|endoftext|>