<commit_before>package repo

import (
	"log"
	"os/exec"
	"strconv"
	"strings"
)

type GitRepository struct{}

func runGitCommandOrDie(cmd *exec.Cmd) string {
	out, err := cmd.Output()
	if err != nil {
		log.Print(cmd.Args)
		log.Print(out)
		log.Fatal(err)
	}
	return strings.Trim(string(out), " \n")
}

func parseBranchListLine(line string) Alias {
	line = strings.Trim(line, "* ")
	splitLine := strings.Split(line, " ")
	masterName := splitLine[0]
	for _, lineComponent := range splitLine[1:] {
		if len(lineComponent) == 40 {
			revisionHash := lineComponent
			return Alias{masterName, Revision(revisionHash)}
		}
	}
	return Alias{Branch: masterName}
}

func (gitRepository GitRepository) ListBranches() []Alias {
	out := runGitCommandOrDie(
		exec.Command("git", "branch", "-av", "--list", "--abbrev=40", "--no-color"))
	lines := strings.Split(out, "\n")
	aliases := make([]Alias, len(lines))
	index := 0
	for _, line := range lines {
		if line != "" {
			aliases[index] = parseBranchListLine(line)
			index += 1
		}
	}
	return aliases
}

func (gitRepository GitRepository) ReadRevisionContents(revision Revision) *RevisionContents {
	out := runGitCommandOrDie(exec.Command("git", "ls-tree", "-r", string(revision)))
	lines := strings.Split(out, "\n")
	paths := make([]string, len(lines))
	for index, line := range lines {
		line = strings.Replace(lines[index], "\t", " ", -1)
		lineParts := strings.Split(line, " ")
		paths[index] = lineParts[len(lineParts)-1]
	}
	return &RevisionContents{revision, paths}
}

func (gitRepository GitRepository) getSubject(revision Revision) string {
	return runGitCommandOrDie(exec.Command(
		"git", "show", string(revision), "--format=\"format:%s\"", "-s"))
}

func (gitRepository GitRepository) getAuthorName(revision Revision) string {
	return runGitCommandOrDie(exec.Command(
		"git", "show", string(revision), "--format=\"format:%an\"", "-s"))
}

func (gitRepository GitRepository) getAuthorEmail(revision Revision) string {
	return runGitCommandOrDie(exec.Command(
		"git", "show", string(revision), "--format=\"format:%ae\"", "-s"))
}

func (gitRepository GitRepository) getTimestamp(revision Revision) int64 {
	out := runGitCommandOrDie(exec.Command(
		"git", "show", string(revision), "--format=\"format:%t\"", "-s"))
	timestamp, err := strconv.ParseInt(out, 10, 64)
	if err != nil {
		log.Fatal(err)
	}
	return timestamp
}

func (gitRepository GitRepository) ReadRevisionMetadata(revision Revision) RevisionMetadata {
	return RevisionMetadata{
		Revision:    revision,
		Timestamp:   gitRepository.getTimestamp(revision),
		Subject:     gitRepository.getSubject(revision),
		AuthorName:  gitRepository.getAuthorName(revision),
		AuthorEmail: gitRepository.getAuthorEmail(revision),
	}
}

func (gitRepository GitRepository) ReadFileAtRevision(revision Revision, path string) []Line {
	out := runGitCommandOrDie(
		exec.Command("git", "blame", "-s", "--abbrev=40", string(revision), path))
	lines := strings.Split(out, "\n")
	result := make([]Line, len(lines))
	for _, line := range lines {
		revision := Revision(line[0:40])
		lineNumberIndex := strings.Index(line, ")")
		lineNumber, err := strconv.Atoi(strings.Trim(line[41:lineNumberIndex], " "))
		if err != nil {
			log.Fatal(err)
		}
		contents := line[lineNumberIndex+2:]
		result = append(result, Line{revision, lineNumber, contents})
	}
	return result
}
<commit_msg>Fixed two bugs in the ReadFileAtRevision method; one which caused reading from some revisions to fail, and another that caused failures with files with trailing empty lines<commit_after>package repo

import (
	"log"
	"os/exec"
	"strconv"
	"strings"
)

type GitRepository struct{}

func runGitCommandOrDie(cmd *exec.Cmd) string {
	out, err := cmd.Output()
	if err != nil {
		log.Print(cmd.Args)
		log.Print(out)
		log.Fatal(err)
	}
	return strings.Trim(string(out), " \n")
}

func parseBranchListLine(line string) Alias {
	line = strings.Trim(line, "* ")
	splitLine := strings.Split(line, " ")
	masterName := splitLine[0]
	for _, lineComponent := range splitLine[1:] {
		if len(lineComponent) == 40 {
			revisionHash := lineComponent
			return Alias{masterName, Revision(revisionHash)}
		}
	}
	return Alias{Branch: masterName}
}

func (gitRepository GitRepository) ListBranches() []Alias {
	out := runGitCommandOrDie(
		exec.Command("git", "branch", "-av", "--list", "--abbrev=40", "--no-color"))
	lines := strings.Split(out, "\n")
	aliases := make([]Alias, len(lines))
	index := 0
	for _, line := range lines {
		if line != "" {
			aliases[index] = parseBranchListLine(line)
			index += 1
		}
	}
	return aliases
}

func (gitRepository GitRepository) ReadRevisionContents(revision Revision) *RevisionContents {
	out := runGitCommandOrDie(exec.Command("git", "ls-tree", "-r", string(revision)))
	lines := strings.Split(out, "\n")
	paths := make([]string, len(lines))
	for index, line := range lines {
		line = strings.Replace(lines[index], "\t", " ", -1)
		lineParts := strings.Split(line, " ")
		paths[index] = lineParts[len(lineParts)-1]
	}
	return &RevisionContents{revision, paths}
}

func (gitRepository GitRepository) getSubject(revision Revision) string {
	return runGitCommandOrDie(exec.Command(
		"git", "show", string(revision), "--format=\"format:%s\"", "-s"))
}

func (gitRepository GitRepository) getAuthorName(revision Revision) string {
	return runGitCommandOrDie(exec.Command(
		"git", "show", string(revision), "--format=\"format:%an\"", "-s"))
}

func (gitRepository GitRepository) getAuthorEmail(revision Revision) string {
	return runGitCommandOrDie(exec.Command(
		"git", "show", string(revision), "--format=\"format:%ae\"", "-s"))
}

func (gitRepository GitRepository) getTimestamp(revision Revision) int64 {
	out := runGitCommandOrDie(exec.Command(
		"git", "show", string(revision), "--format=\"format:%t\"", "-s"))
	timestamp, err := strconv.ParseInt(out, 10, 64)
	if err != nil {
		log.Fatal(err)
	}
	return timestamp
}

func (gitRepository GitRepository) ReadRevisionMetadata(revision Revision) RevisionMetadata {
	return RevisionMetadata{
		Revision:    revision,
		Timestamp:   gitRepository.getTimestamp(revision),
		Subject:     gitRepository.getSubject(revision),
		AuthorName:  gitRepository.getAuthorName(revision),
		AuthorEmail: gitRepository.getAuthorEmail(revision),
	}
}

func (gitRepository GitRepository) ReadFileAtRevision(revision Revision, path string) []Line {
	out := runGitCommandOrDie(
		exec.Command("git", "blame", "-s", "--abbrev=40", string(revision), "--", path))
	lines := strings.Split(out, "\n")
	result := make([]Line, len(lines))
	for _, line := range lines {
		revision := Revision(line[0:40])
		lineNumberIndex := strings.Index(line, ")")
		lineNumber, err := strconv.Atoi(strings.Trim(line[41:lineNumberIndex], " "))
		if err != nil {
			log.Fatal(err)
		}
		contents := line[lineNumberIndex+1:]
		if contents != "" {
			result = append(result, Line{revision, lineNumber, contents})
		}
	}
	return result
}
<|endoftext|>


<commit_before>package main

import (
	"image"
	"image/color"
	"image/png"
	"os"
	"sort"

	"github.com/zagrodzki/goscope/dummy"
	"github.com/zagrodzki/goscope/scope"
)

type aggrPoint struct {
	x     int
	sumY  int
	sizeY int
}

func (p aggrPoint) add(x, y int) aggrPoint {
	p.x = x
	p.sumY += y
	p.sizeY++
	return p
}

func (p aggrPoint) toPoint() image.Point {
	return image.Point{p.x, p.sumY / p.sizeY}
}

func samplesToPoints(s []scope.Sample, start, end image.Point) []image.Point {
	if len(s) == 0 {
		return nil
	}
	minY := s[0]
	maxY := s[0]
	for _, y := range s {
		if minY > y {
			minY = y
		}
		if maxY < y {
			maxY = y
		}
	}

	rangeX := float64(len(s) - 1)
	rangeY := float64(maxY - minY)
	aggrPoints := make(map[int]aggrPoint)
	startX := float64(start.X)
	endY := float64(end.Y)
	pixelRangeX := float64(end.X - start.X)
	pixelRangeY := float64(end.Y - start.Y)
	for i, y := range s {
		mapX := int(startX + float64(i)/rangeX*pixelRangeX)
		mapY := int(endY - float64(y-minY)/rangeY*pixelRangeY)
		aggrPoints[mapX] = aggrPoints[mapX].add(mapX, mapY)
	}
	var points []image.Point
	for _, p := range aggrPoints {
		points = append(points, p.toPoint())
	}

	return points
}

// Represents the entire plotting area.
type Plot struct {
	*image.RGBA
}

// Fills the plot with a color.
func (plot Plot) Fill(col color.RGBA) {
	bounds := plot.Bounds()
	for i := bounds.Min.X; i < bounds.Max.X; i++ {
		for j := bounds.Min.Y; j < bounds.Max.Y; j++ {
			plot.Set(i, j, col)
		}
	}
}

// Draws a straight line from pixel p1 to p2.
func (plot Plot) DrawLine(p1, p2 image.Point, col color.RGBA) {
	if p1.X == p2.X { // vertical line
		for i := min(p1.Y, p2.Y); i <= max(p1.Y, p2.Y); i++ {
			plot.Set(p1.X, i, col)
		}
		return
	}

	// Calculating the parameters of the equation
	// of the straight line (in the form y=a*x+b)
	// passing through p1 and p2.

	// slope of the line
	a := float64(p1.Y-p2.Y) / float64(p1.X-p2.X)
	// intercept of the line
	b := float64(p1.Y) - float64(p1.X)*a

	// To avoid visual "gaps" between the pixels we switch on,
	// we draw the line in one of two ways.
	if abs(p1.X-p2.X) >= abs(p1.Y-p2.Y) {
		// If the line is more horizontal than vertical,
		// for every pixel column between p1 and p2
		// we find and switch on the pixel closest to y=a*x+b
		for i := min(p1.X, p2.X); i <= max(p1.X, p2.X); i++ {
			plot.Set(i, int(a*float64(i)+b), col)
		}
	} else {
		// If the line is more vertical than horizontal,
		// for every pixel row between p1 and p2
		// we find and switch on the pixel closest to y=a*x+b
		for i := min(p1.Y, p2.Y); i <= max(p1.Y, p2.Y); i++ {
			plot.Set(int((float64(i)-b)/a), i, col)
		}
	}
}

// Draws samples in the image rectangle defined by
// starting (upper left) and ending (lower right) pixel.
func (plot Plot) DrawSamples(start, end image.Point, s []scope.Sample, col color.RGBA) {
	points := samplesToPoints(s, start, end)
	sort.Sort(xSorter(points))
	for i := 1; i < len(points); i++ {
		plot.DrawLine(points[i-1], points[i], col)
	}
}

// Draws samples from all the channels into one image.
func (plot Plot) DrawAll(samples map[scope.ChanID][]scope.Sample, cols map[scope.ChanID]color.RGBA) {
	b := plot.Bounds()
	x1 := b.Min.X + 10
	x2 := b.Max.X - 10
	y1 := b.Min.Y + 10
	y2 := b.Min.Y + 10 + int((b.Max.Y-b.Min.Y-10*(len(samples)+1))/len(samples))
	step := y2 - b.Min.Y
	for id, v := range samples {
		plot.DrawSamples(image.Point{x1, y1}, image.Point{x2, y2}, v, cols[id])
		y1 = y1 + step
		y2 = y2 + step
	}
}

type xSorter []image.Point

func (a xSorter) Len() int {
	return len(a)
}

func (a xSorter) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a xSorter) Less(i, j int) bool {
	return a[i].X < a[j].X
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func abs(a int) int {
	if a < 0 {
		return -a
	}
	return a
}

func main() {
	dum, _ := dummy.Open("")
	data, stop, _ := dum.StartSampling()

	samples := (<-data).Samples
	stop()

	plot := Plot{image.NewRGBA(image.Rect(0, 0, 800, 600))}
	colWhite := color.RGBA{255, 255, 255, 255}

	colRed := color.RGBA{255, 0, 0, 255}
	colGreen := color.RGBA{0, 255, 0, 255}
	colBlue := color.RGBA{0, 0, 255, 255}
	colBlack := color.RGBA{0, 0, 0, 255}
	chanCols := [4]color.RGBA{colRed, colGreen, colBlue, colBlack}

	plot.Fill(colWhite)
	cols := make(map[scope.ChanID]color.RGBA)
	next := 0
	for _, id := range dum.Channels() {
		cols[id] = chanCols[next]
		next = (next + 1) % 4
	}
	plot.DrawAll(samples, cols)

	f, err := os.Create("draw.png")
	if err != nil {
		panic(err)
	}
	defer f.Close()
	png.Encode(f, plot)
}
<commit_msg>xSorter renamed to pointsByX<commit_after>package main

import (
	"image"
	"image/color"
	"image/png"
	"os"
	"sort"

	"github.com/zagrodzki/goscope/dummy"
	"github.com/zagrodzki/goscope/scope"
)

type aggrPoint struct {
	x     int
	sumY  int
	sizeY int
}

func (p aggrPoint) add(x, y int) aggrPoint {
	p.x = x
	p.sumY += y
	p.sizeY++
	return p
}

func (p aggrPoint) toPoint() image.Point {
	return image.Point{p.x, p.sumY / p.sizeY}
}

func samplesToPoints(s []scope.Sample, start, end image.Point) []image.Point {
	if len(s) == 0 {
		return nil
	}
	minY := s[0]
	maxY := s[0]
	for _, y := range s {
		if minY > y {
			minY = y
		}
		if maxY < y {
			maxY = y
		}
	}

	rangeX := float64(len(s) - 1)
	rangeY := float64(maxY - minY)
	aggrPoints := make(map[int]aggrPoint)
	startX := float64(start.X)
	endY := float64(end.Y)
	pixelRangeX := float64(end.X - start.X)
	pixelRangeY := float64(end.Y - start.Y)
	for i, y := range s {
		mapX := int(startX + float64(i)/rangeX*pixelRangeX)
		mapY := int(endY - float64(y-minY)/rangeY*pixelRangeY)
		aggrPoints[mapX] = aggrPoints[mapX].add(mapX, mapY)
	}
	var points []image.Point
	for _, p := range aggrPoints {
		points = append(points, p.toPoint())
	}

	return points
}

// Represents the entire plotting area.
type Plot struct {
	*image.RGBA
}

// Fills the plot with a color.
func (plot Plot) Fill(col color.RGBA) {
	bounds := plot.Bounds()
	for i := bounds.Min.X; i < bounds.Max.X; i++ {
		for j := bounds.Min.Y; j < bounds.Max.Y; j++ {
			plot.Set(i, j, col)
		}
	}
}

// Draws a straight line from pixel p1 to p2.
func (plot Plot) DrawLine(p1, p2 image.Point, col color.RGBA) {
	if p1.X == p2.X { // vertical line
		for i := min(p1.Y, p2.Y); i <= max(p1.Y, p2.Y); i++ {
			plot.Set(p1.X, i, col)
		}
		return
	}

	// Calculating the parameters of the equation
	// of the straight line (in the form y=a*x+b)
	// passing through p1 and p2.

	// slope of the line
	a := float64(p1.Y-p2.Y) / float64(p1.X-p2.X)
	// intercept of the line
	b := float64(p1.Y) - float64(p1.X)*a

	// To avoid visual "gaps" between the pixels we switch on,
	// we draw the line in one of two ways.
	if abs(p1.X-p2.X) >= abs(p1.Y-p2.Y) {
		// If the line is more horizontal than vertical,
		// for every pixel column between p1 and p2
		// we find and switch on the pixel closest to y=a*x+b
		for i := min(p1.X, p2.X); i <= max(p1.X, p2.X); i++ {
			plot.Set(i, int(a*float64(i)+b), col)
		}
	} else {
		// If the line is more vertical than horizontal,
		// for every pixel row between p1 and p2
		// we find and switch on the pixel closest to y=a*x+b
		for i := min(p1.Y, p2.Y); i <= max(p1.Y, p2.Y); i++ {
			plot.Set(int((float64(i)-b)/a), i, col)
		}
	}
}

// Draws samples in the image rectangle defined by
// starting (upper left) and ending (lower right) pixel.
func (plot Plot) DrawSamples(start, end image.Point, s []scope.Sample, col color.RGBA) {
	points := samplesToPoints(s, start, end)
	sort.Sort(pointsByX(points))
	for i := 1; i < len(points); i++ {
		plot.DrawLine(points[i-1], points[i], col)
	}
}

// Draws samples from all the channels into one image.
func (plot Plot) DrawAll(samples map[scope.ChanID][]scope.Sample, cols map[scope.ChanID]color.RGBA) {
	b := plot.Bounds()
	x1 := b.Min.X + 10
	x2 := b.Max.X - 10
	y1 := b.Min.Y + 10
	y2 := b.Min.Y + 10 + int((b.Max.Y-b.Min.Y-10*(len(samples)+1))/len(samples))
	step := y2 - b.Min.Y
	for id, v := range samples {
		plot.DrawSamples(image.Point{x1, y1}, image.Point{x2, y2}, v, cols[id])
		y1 = y1 + step
		y2 = y2 + step
	}
}

type pointsByX []image.Point

func (a pointsByX) Len() int {
	return len(a)
}

func (a pointsByX) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a pointsByX) Less(i, j int) bool {
	return a[i].X < a[j].X
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func abs(a int) int {
	if a < 0 {
		return -a
	}
	return a
}

func main() {
	dum, _ := dummy.Open("")
	data, stop, _ := dum.StartSampling()

	samples := (<-data).Samples
	stop()

	plot := Plot{image.NewRGBA(image.Rect(0, 0, 800, 600))}
	colWhite := color.RGBA{255, 255, 255, 255}

	colRed := color.RGBA{255, 0, 0, 255}
	colGreen := color.RGBA{0, 255, 0, 255}
	colBlue := color.RGBA{0, 0, 255, 255}
	colBlack := color.RGBA{0, 0, 0, 255}
	chanCols := [4]color.RGBA{colRed, colGreen, colBlue, colBlack}

	plot.Fill(colWhite)
	cols := make(map[scope.ChanID]color.RGBA)
	next := 0
	for _, id := range dum.Channels() {
		cols[id] = chanCols[next]
		next = (next + 1) % 4
	}
	plot.DrawAll(samples, cols)

	f, err := os.Create("draw.png")
	if err != nil {
		panic(err)
	}
	defer f.Close()
	png.Encode(f, plot)
}
<|endoftext|>


<commit_before>package h5

var (
	// Scopes
	allScope = map[string]bool{}
	baseScope = map[string]bool{
		"applet":true,
		"caption":true,
		"html":true,
		"table":true,
		"td":true,
		"th":true,
		"marquee":true,
		"object":true,
		"mi":true,
		"mo":true,
		"mn":true,
		"ms":true,
		"mtext":true,
		"annotation-xml":true,
		"foreignObject":true,
		"desc":true,
		"title":true,
	}
	buttonScope = map[string]bool{
		"applet":true,
		"caption":true,
		"html":true,
		"table":true,
		"td":true,
		"th":true,
		"marquee":true,
		"object":true,
		"mi":true,
		"mo":true,
		"mn":true,
		"ms":true,
		"mtext":true,
		"annotation-xml":true,
		"foreignObject":true,
		"desc":true,
		"title":true,
		"button":true,
	}
	listScope = map[string]bool{
		"applet":true,
		"caption":true,
		"html":true,
		"table":true,
		"td":true,
		"th":true,
		"marquee":true,
		"object":true,
		"mi":true,
		"mo":true,
		"mn":true,
		"ms":true,
		"mtext":true,
		"annotation-xml":true,
		"foreignObject":true,
		"desc":true,
		"title":true,
		"ol":true,
		"ul":true,
	}
)<commit_msg>go fmt.<commit_after>package h5

var (
	// Scopes
	allScope  = map[string]bool{}
	baseScope = map[string]bool{
		"applet":         true,
		"caption":        true,
		"html":           true,
		"table":          true,
		"td":             true,
		"th":             true,
		"marquee":        true,
		"object":         true,
		"mi":             true,
		"mo":             true,
		"mn":             true,
		"ms":             true,
		"mtext":          true,
		"annotation-xml": true,
		"foreignObject":  true,
		"desc":           true,
		"title":          true,
	}
	buttonScope = map[string]bool{
		"applet":         true,
		"caption":        true,
		"html":           true,
		"table":          true,
		"td":             true,
		"th":             true,
		"marquee":        true,
		"object":         true,
		"mi":             true,
		"mo":             true,
		"mn":             true,
		"ms":             true,
		"mtext":          true,
		"annotation-xml": true,
		"foreignObject":  true,
		"desc":           true,
		"title":          true,
		"button":         true,
	}
	listScope = map[string]bool{
		"applet":         true,
		"caption":        true,
		"html":           true,
		"table":          true,
		"td":             true,
		"th":             true,
		"marquee":        true,
		"object":         true,
		"mi":             true,
		"mo":             true,
		"mn":             true,
		"ms":             true,
		"mtext":          true,
		"annotation-xml": true,
		"foreignObject":  true,
		"desc":           true,
		"title":          true,
		"ol":             true,
		"ul":             true,
	}
)
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"fmt"
	"io"
	"os"
	"regexp"
	"strings"
	"time"
	"unicode"

	"github.com/chzyer/readline"
	"github.com/knq/dburl"
	"github.com/knq/xoutil"
	"github.com/olekukonko/tablewriter"
)

const (
	notConnected = "(not connected)"
)

// Handler is a cli input handler.
type Handler struct {
	args        *Args
	interactive bool

	u  *dburl.URL
	db *sql.DB
}

// Prompt returns the base input prompt.
func (h *Handler) Prompt() string {
	if !h.interactive {
		return ""
	}

	s := notConnected

	if h.db != nil {
		s = h.u.Short()
	}

	return s + "=> "
}

// Cont returns the continuation prompt.
func (h *Handler) Cont() string {
	if !h.interactive {
		return ""
	}

	s := notConnected

	if h.db != nil {
		s = h.u.Short()
	}

	return s + "-> "
}

// Open handles opening a specified database URL.
func (h *Handler) Open(urlstr string) error {
	if urlstr == "" {
		return nil
	}

	// parse dsn
	var err error
	h.u, err = dburl.Parse(urlstr)
	switch {
	case err == dburl.ErrInvalidDatabaseScheme:
		if fi, err := os.Stat(urlstr); err == nil {
			// TODO: add support for postgres unix domain sockets
			if fi.Mode()&os.ModeSocket != 0 {
				return h.Open("mysql+unix:" + urlstr)
			}

			// it is a file, so reattempt to open it with sqlite3
			return h.Open("sqlite3:" + urlstr)
		}

		return err

	case err != nil:
		return err
	}

	// check driver
	if !drivers[h.u.Driver] {
		if h.u.Driver == "ora" {
			return ErrOracleDriverNotAvailable
		}
		return fmt.Errorf("driver '%s' is not available for '%s'", h.u.Driver, h.u.Scheme)
	}

	// add connection parameters for databases
	dsn := h.u.DSN
	dsn = h.addQueryParam(dsn, "mysql", "parseTime", "true")
	dsn = h.addQueryParam(dsn, "mysql", "loc", "Local")
	dsn = h.addQueryParam(dsn, "sqlite3", "loc", "auto")

	//log.Printf(">>> dsn: %s", dsn)

	// connect
	h.db, err = sql.Open(h.u.Driver, dsn)
	return err
}

// addQueryParam conditionally adds a ?name=val style query parameter to the
// end of a DSN if the connected database driver matches the supplied driver
// name.
func (h *Handler) addQueryParam(dsn, driver, name, val string) string {
	if h.u.Driver == driver {
		if !strings.Contains(dsn, name+"=") {
			s := "?"
			if strings.Contains(dsn, "?") {
				s = "&"
			}
			return dsn + s + name + "=" + val
		}
	}

	return dsn
}

// Execute executes a sql query against the connected database.
func (h *Handler) Execute(w io.Writer, sqlstr string) error {
	if h.db == nil {
		return ErrNotConnected
	}

	if h.u.Driver == "ora" {
		sqlstr = strings.TrimSuffix(sqlstr, ";")
	}

	// select
	if s := strings.TrimLeftFunc(sqlstr, unicode.IsSpace); len(s) >= 5 {
		i := strings.IndexFunc(s, unicode.IsSpace)
		if i == -1 {
			i = len(s)
		}

		z := strings.ToUpper(s[:i])
		if z == "SELECT" ||
			(h.u.Driver == "sqlite3" && z == "PRAGMA" && !strings.ContainsRune(s[i:], '=')) {
			err := h.Query(w, sqlstr)
			if err != nil {
				return h.wrapError(err)
			}

			return nil
		}
	}

	// exec
	res, err := h.db.Exec(sqlstr)
	if err != nil {
		return err
	}

	// get count
	count, err := res.RowsAffected()
	if err != nil {
		return err
	}

	// print name
	name := "EXEC"
	if i := strings.Index(sqlstr, " "); i >= 0 {
		name = strings.ToUpper(sqlstr[:i])
	}
	fmt.Fprint(w, name)

	// print count
	if count > 0 {
		fmt.Fprintf(w, " %d", count)
	}

	fmt.Fprint(w, "\n")

	return nil
}

var allcapsRE = regexp.MustCompile(`^[A-Z_]+$`)

// Query executes a query against the database.
func (h *Handler) Query(w io.Writer, sqlstr string) error {
	// run query
	q, err := h.db.Query(sqlstr)
	if err != nil {
		return err
	}
	defer q.Close()

	// get column names
	cols, err := q.Columns()
	if err != nil {
		return err
	}

	// fix display column names
	for i, s := range cols {
		s = strings.TrimSpace(s)
		if len(s) == 0 {
			cols[i] = fmt.Sprintf("col%d", i)
		}

		// fix case on oracle column names
		if h.u.Driver == "ora" && allcapsRE.MatchString(cols[i]) {
			cols[i] = strings.ToLower(cols[i])
		}
	}

	// create output table
	t := tablewriter.NewWriter(w)
	t.SetAutoFormatHeaders(false)
	t.SetBorder(false)
	t.SetHeader(cols)

	clen := len(cols)
	var rows int
	if clen != 0 {
		for q.Next() {
			r := make([]interface{}, clen)
			for i := range r {
				r[i] = new(interface{})
			}

			err = q.Scan(r...)
			if err != nil {
				return err
			}

			row := make([]string, clen)
			for n, z := range r {
				j := z.(*interface{})

				//log.Printf(">>> %s: %s", cols[n], reflect.TypeOf(*j))

				switch x := (*j).(type) {
				case []byte:
					if h.u.Driver == "sqlite3" {
						row[n] = h.sqlite3Parse(x)
					} else {
						row[n] = string(x)
					}

				case string:
					row[n] = x

				case time.Time:
					row[n] = x.Format(time.RFC3339Nano)

				case fmt.Stringer:
					row[n] = x.String()

				default:
					row[n] = fmt.Sprintf("%v", *j)
				}

			}
			t.Append(row)
			rows++
		}
	}

	t.Render()
	fmt.Fprintf(w, "(%d rows)\n\n", rows)

	return nil
}

// sqlite3Parse will convert buf matching a time format to a time, and will
// format it according to the handler time settings.
//
// TODO: only do this if the type of the column is a timestamp type.
func (h *Handler) sqlite3Parse(buf []byte) string {
	s := string(buf)
	if s != "" && strings.TrimSpace(s) != "" {
		t := &xoutil.SqTime{}
		err := t.Scan(buf)
		if err == nil {
			return t.Format(time.RFC3339Nano)
		}
	}

	return s
}

// DisplayHelp displays the help message.
func (h *Handler) DisplayHelp(w io.Writer) {
	io.WriteString(w, helpDesc)
}

// Close closes the database connection if it is open.
func (h *Handler) Close() error {
	if h.db != nil {
		err := h.db.Close()

		h.db = nil
		h.u = nil

		return err
	}

	return nil
}

// Run processes h.args, running either the Commands if non-empty, or File if
// specified, or the input from stdin.
func (h *Handler) Run() error {
	var err error

	// open
	err = h.Open(h.args.DSN)
	if err != nil {
		return err
	}

	// short circuit if commands provided
	if len(h.args.Commands) > 0 {
		return h.RunCommands()
	}

	// configure input
	var stdin *os.File
	stdout, stderr := os.Stdout, os.Stderr

	// set file as stdin
	if h.args.File != "" {
		stdin, err = os.OpenFile(h.args.File, os.O_RDONLY, 0)
		if err != nil {
			return err
		}
		defer stdin.Close()

		h.interactive = false
	}

	// set out as stdout
	if h.args.Out != "" {
		stdout, err = os.OpenFile(h.args.Out, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644)
		if err != nil {
			return err
		}
		defer stdout.Close()

		h.interactive = false
	}

	// set stdin if not set
	var r io.Reader = stdin
	if stdin == nil {
		c := readline.NewCancelableStdin(os.Stdin)
		defer c.Close()

		r = c
	}

	return h.Process(r, stdout, stderr)
}

// Run handles stuff
func (h *Handler) Process(stdin io.Reader, stdout, stderr io.Writer) error {
	var err error

	// create readline instance
	l, err := readline.NewEx(&readline.Config{
		Prompt:                 h.Prompt(),
		HistoryFile:            h.args.HistoryFile,
		DisableAutoSaveHistory: true,
		InterruptPrompt:        "^C",
		HistorySearchFold:      true,
		Stdin:                  stdin,
		Stdout:                 stdout,
		Stderr:                 stderr,
		FuncIsTerminal: func() bool {
			return h.interactive
		},
		FuncFilterInputRune: func(r rune) (rune, bool) {
			if r == readline.CharCtrlZ {
				return r, false
			}
			return r, true
		},
	})
	if err != nil {
		return err
	}
	defer l.Close()

	if h.interactive {
		fmt.Fprint(l.Stdout(), welcomeDesc)
	}

	// process input
	var multi bool
	var stmt []string
	for {
		line, err := l.Readline()
		switch {
		case err == readline.ErrInterrupt:
			stmt = stmt[:0]
			multi = false
			l.SetPrompt(h.Prompt())
			continue

		case err != nil:
			return nil
		}

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

		if !multi {
			switch {
			case z == "help":
				h.DisplayHelp(l.Stdout())
				continue

			case z == `\q`:
				h.SaveHistory(l, line)
				return nil

			case strings.HasPrefix(line, `\c `) || strings.HasPrefix(line, `\connect `):
				h.SaveHistory(l, line)

				err = h.Close()
				if err != nil {
					return err
				}

				urlstr := strings.TrimSpace(line[strings.IndexRune(line, ' '):])
				err = h.Open(urlstr)
				if err != nil {
					fmt.Fprintf(l.Stderr(), "error: could not connect to database: %v\n", err)
				}

				l.SetPrompt(h.Prompt())
				continue

			case z == `\Z`:
				h.SaveHistory(l, line)

				err = h.Close()
				if err != nil {
					return err
				}

				l.SetPrompt(h.Prompt())
				continue
			}
		}

		stmt = append(stmt, line)

		if !strings.HasSuffix(z, ";") {
			multi = true
			l.SetPrompt(h.Cont())
			continue
		}

		s := strings.Join(stmt, "\n")
		h.SaveHistory(l, s)
		l.SetPrompt(h.Prompt())

		err = h.Execute(l.Stdout(), s)
		if err != nil {
			fmt.Fprintf(l.Stderr(), "error: %v\n", h.wrapError(err))
		}

		stmt = stmt[:0]
		multi = false
	}
}

// SaveHistory conditionally saves a line to the history if the session is
// interactive.
func (h *Handler) SaveHistory(l *readline.Instance, line string) error {
	if h.interactive {
		return l.SaveHistory(line)
	}

	return nil
}

// RunCommands runs the argument commands.
func (h *Handler) RunCommands() error {
	h.interactive = false

	var err error
	for _, c := range h.args.Commands {
		err = h.Process(strings.NewReader(c), os.Stdout, os.Stderr)
		if err != nil {
			return err
		}
	}

	return nil
}

// driverError is a wrapper to standardize errors.
type driverError struct {
	driver string
	err    error
}

// Error satisfies the error interface.
func (e *driverError) Error() string {
	if e.driver != "" {
		s := e.err.Error()
		return e.driver + ": " + strings.TrimLeftFunc(strings.TrimPrefix(strings.TrimSpace(s), e.driver+":"), unicode.IsSpace)
	}

	return e.err.Error()
}

// wrapError conditionally wraps an error if the error occurs while connected
// to a database.
func (h *Handler) wrapError(err error) error {
	if h.db != nil {
		// attempt to clean up and standardize errors
		driver := h.u.Driver
		if driver == "postgres" {
			driver = "pq"
		}

		return &driverError{driver, err}
	}

	return err
}
<commit_msg>Attempting fix for Windows input issues<commit_after>package main

import (
	"database/sql"
	"fmt"
	"io"
	"os"
	"regexp"
	"strings"
	"time"
	"unicode"

	"github.com/chzyer/readline"
	"github.com/knq/dburl"
	"github.com/knq/xoutil"
	"github.com/olekukonko/tablewriter"
)

const (
	notConnected = "(not connected)"
)

// Handler is a cli input handler.
type Handler struct {
	args        *Args
	interactive bool

	u  *dburl.URL
	db *sql.DB
}

// Prompt returns the base input prompt.
func (h *Handler) Prompt() string {
	if !h.interactive {
		return ""
	}

	s := notConnected

	if h.db != nil {
		s = h.u.Short()
	}

	return s + "=> "
}

// Cont returns the continuation prompt.
func (h *Handler) Cont() string {
	if !h.interactive {
		return ""
	}

	s := notConnected

	if h.db != nil {
		s = h.u.Short()
	}

	return s + "-> "
}

// Open handles opening a specified database URL.
func (h *Handler) Open(urlstr string) error {
	if urlstr == "" {
		return nil
	}

	// parse dsn
	var err error
	h.u, err = dburl.Parse(urlstr)
	switch {
	case err == dburl.ErrInvalidDatabaseScheme:
		if fi, err := os.Stat(urlstr); err == nil {
			// TODO: add support for postgres unix domain sockets
			if fi.Mode()&os.ModeSocket != 0 {
				return h.Open("mysql+unix:" + urlstr)
			}

			// it is a file, so reattempt to open it with sqlite3
			return h.Open("sqlite3:" + urlstr)
		}

		return err

	case err != nil:
		return err
	}

	// check driver
	if !drivers[h.u.Driver] {
		if h.u.Driver == "ora" {
			return ErrOracleDriverNotAvailable
		}
		return fmt.Errorf("driver '%s' is not available for '%s'", h.u.Driver, h.u.Scheme)
	}

	// add connection parameters for databases
	dsn := h.u.DSN
	dsn = h.addQueryParam(dsn, "mysql", "parseTime", "true")
	dsn = h.addQueryParam(dsn, "mysql", "loc", "Local")
	dsn = h.addQueryParam(dsn, "sqlite3", "loc", "auto")

	//log.Printf(">>> dsn: %s", dsn)

	// connect
	h.db, err = sql.Open(h.u.Driver, dsn)
	return err
}

// addQueryParam conditionally adds a ?name=val style query parameter to the
// end of a DSN if the connected database driver matches the supplied driver
// name.
func (h *Handler) addQueryParam(dsn, driver, name, val string) string {
	if h.u.Driver == driver {
		if !strings.Contains(dsn, name+"=") {
			s := "?"
			if strings.Contains(dsn, "?") {
				s = "&"
			}
			return dsn + s + name + "=" + val
		}
	}

	return dsn
}

// Execute executes a sql query against the connected database.
func (h *Handler) Execute(w io.Writer, sqlstr string) error {
	if h.db == nil {
		return ErrNotConnected
	}

	if h.u.Driver == "ora" {
		sqlstr = strings.TrimSuffix(sqlstr, ";")
	}

	// select
	if s := strings.TrimLeftFunc(sqlstr, unicode.IsSpace); len(s) >= 5 {
		i := strings.IndexFunc(s, unicode.IsSpace)
		if i == -1 {
			i = len(s)
		}

		z := strings.ToUpper(s[:i])
		if z == "SELECT" ||
			(h.u.Driver == "sqlite3" && z == "PRAGMA" && !strings.ContainsRune(s[i:], '=')) {
			err := h.Query(w, sqlstr)
			if err != nil {
				return h.wrapError(err)
			}

			return nil
		}
	}

	// exec
	res, err := h.db.Exec(sqlstr)
	if err != nil {
		return err
	}

	// get count
	count, err := res.RowsAffected()
	if err != nil {
		return err
	}

	// print name
	name := "EXEC"
	if i := strings.Index(sqlstr, " "); i >= 0 {
		name = strings.ToUpper(sqlstr[:i])
	}
	fmt.Fprint(w, name)

	// print count
	if count > 0 {
		fmt.Fprintf(w, " %d", count)
	}

	fmt.Fprint(w, "\n")

	return nil
}

var allcapsRE = regexp.MustCompile(`^[A-Z_]+$`)

// Query executes a query against the database.
func (h *Handler) Query(w io.Writer, sqlstr string) error {
	// run query
	q, err := h.db.Query(sqlstr)
	if err != nil {
		return err
	}
	defer q.Close()

	// get column names
	cols, err := q.Columns()
	if err != nil {
		return err
	}

	// fix display column names
	for i, s := range cols {
		s = strings.TrimSpace(s)
		if len(s) == 0 {
			cols[i] = fmt.Sprintf("col%d", i)
		}

		// fix case on oracle column names
		if h.u.Driver == "ora" && allcapsRE.MatchString(cols[i]) {
			cols[i] = strings.ToLower(cols[i])
		}
	}

	// create output table
	t := tablewriter.NewWriter(w)
	t.SetAutoFormatHeaders(false)
	t.SetBorder(false)
	t.SetHeader(cols)

	clen := len(cols)
	var rows int
	if clen != 0 {
		for q.Next() {
			r := make([]interface{}, clen)
			for i := range r {
				r[i] = new(interface{})
			}

			err = q.Scan(r...)
			if err != nil {
				return err
			}

			row := make([]string, clen)
			for n, z := range r {
				j := z.(*interface{})

				//log.Printf(">>> %s: %s", cols[n], reflect.TypeOf(*j))

				switch x := (*j).(type) {
				case []byte:
					if h.u.Driver == "sqlite3" {
						row[n] = h.sqlite3Parse(x)
					} else {
						row[n] = string(x)
					}

				case string:
					row[n] = x

				case time.Time:
					row[n] = x.Format(time.RFC3339Nano)

				case fmt.Stringer:
					row[n] = x.String()

				default:
					row[n] = fmt.Sprintf("%v", *j)
				}

			}
			t.Append(row)
			rows++
		}
	}

	t.Render()
	fmt.Fprintf(w, "(%d rows)\n\n", rows)

	return nil
}

// sqlite3Parse will convert buf matching a time format to a time, and will
// format it according to the handler time settings.
//
// TODO: only do this if the type of the column is a timestamp type.
func (h *Handler) sqlite3Parse(buf []byte) string {
	s := string(buf)
	if s != "" && strings.TrimSpace(s) != "" {
		t := &xoutil.SqTime{}
		err := t.Scan(buf)
		if err == nil {
			return t.Format(time.RFC3339Nano)
		}
	}

	return s
}

// DisplayHelp displays the help message.
func (h *Handler) DisplayHelp(w io.Writer) {
	io.WriteString(w, helpDesc)
}

// Close closes the database connection if it is open.
func (h *Handler) Close() error {
	if h.db != nil {
		err := h.db.Close()

		h.db = nil
		h.u = nil

		return err
	}

	return nil
}

// Run processes h.args, running either the Commands if non-empty, or File if
// specified, or the input from stdin.
func (h *Handler) Run() error {
	var err error

	// open
	err = h.Open(h.args.DSN)
	if err != nil {
		return err
	}

	// short circuit if commands provided
	if len(h.args.Commands) > 0 {
		return h.RunCommands()
	}

	// configure input
	var stdin *os.File
	stdout, stderr := readline.Stdout, readline.Stderr

	// set file as stdin
	if h.args.File != "" {
		stdin, err = os.OpenFile(h.args.File, os.O_RDONLY, 0)
		if err != nil {
			return err
		}
		defer stdin.Close()

		h.interactive = false
	}

	// set out as stdout
	if h.args.Out != "" {
		stdout, err = os.OpenFile(h.args.Out, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644)
		if err != nil {
			return err
		}
		defer stdout.Close()

		h.interactive = false
	}

	// set stdin if not set
	var r io.ReadCloser = stdin
	if stdin == nil {
		c := readline.NewCancelableStdin(readline.Stdin)
		defer c.Close()

		r = c
	}

	return h.Process(r, stdout, stderr)
}

// Run handles stuff
func (h *Handler) Process(stdin io.Reader, stdout, stderr io.Writer) error {
	var err error

	// create readline instance
	l, err := readline.NewEx(&readline.Config{
		Prompt:                 h.Prompt(),
		HistoryFile:            h.args.HistoryFile,
		DisableAutoSaveHistory: true,
		InterruptPrompt:        "^C",
		HistorySearchFold:      true,
		Stdin:                  stdin,
		Stdout:                 stdout,
		Stderr:                 stderr,
		FuncIsTerminal: func() bool {
			return h.interactive
		},
		FuncFilterInputRune: func(r rune) (rune, bool) {
			if r == readline.CharCtrlZ {
				return r, false
			}
			return r, true
		},
	})
	if err != nil {
		return err
	}
	defer l.Close()

	if h.interactive {
		fmt.Fprint(l.Stdout(), welcomeDesc)
	}

	// process input
	var multi bool
	var stmt []string
	for {
		line, err := l.Readline()
		switch {
		case err == readline.ErrInterrupt:
			stmt = stmt[:0]
			multi = false
			l.SetPrompt(h.Prompt())
			continue

		case err != nil:
			return nil
		}

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

		if !multi {
			switch {
			case z == "help":
				h.DisplayHelp(l.Stdout())
				continue

			case z == `\q`:
				h.SaveHistory(l, line)
				return nil

			case strings.HasPrefix(line, `\c `) || strings.HasPrefix(line, `\connect `):
				h.SaveHistory(l, line)

				err = h.Close()
				if err != nil {
					return err
				}

				urlstr := strings.TrimSpace(line[strings.IndexRune(line, ' '):])
				err = h.Open(urlstr)
				if err != nil {
					fmt.Fprintf(l.Stderr(), "error: could not connect to database: %v\n", err)
				}

				l.SetPrompt(h.Prompt())
				continue

			case z == `\Z`:
				h.SaveHistory(l, line)

				err = h.Close()
				if err != nil {
					return err
				}

				l.SetPrompt(h.Prompt())
				continue
			}
		}

		stmt = append(stmt, line)

		if !strings.HasSuffix(z, ";") {
			multi = true
			l.SetPrompt(h.Cont())
			continue
		}

		s := strings.Join(stmt, "\n")
		h.SaveHistory(l, s)
		l.SetPrompt(h.Prompt())

		err = h.Execute(l.Stdout(), s)
		if err != nil {
			fmt.Fprintf(l.Stderr(), "error: %v\n", h.wrapError(err))
		}

		stmt = stmt[:0]
		multi = false
	}
}

// SaveHistory conditionally saves a line to the history if the session is
// interactive.
func (h *Handler) SaveHistory(l *readline.Instance, line string) error {
	if h.interactive {
		return l.SaveHistory(line)
	}

	return nil
}

// RunCommands runs the argument commands.
func (h *Handler) RunCommands() error {
	h.interactive = false

	var err error
	for _, c := range h.args.Commands {
		err = h.Process(strings.NewReader(c), os.Stdout, os.Stderr)
		if err != nil {
			return err
		}
	}

	return nil
}

// driverError is a wrapper to standardize errors.
type driverError struct {
	driver string
	err    error
}

// Error satisfies the error interface.
func (e *driverError) Error() string {
	if e.driver != "" {
		s := e.err.Error()
		return e.driver + ": " + strings.TrimLeftFunc(strings.TrimPrefix(strings.TrimSpace(s), e.driver+":"), unicode.IsSpace)
	}

	return e.err.Error()
}

// wrapError conditionally wraps an error if the error occurs while connected
// to a database.
func (h *Handler) wrapError(err error) error {
	if h.db != nil {
		// attempt to clean up and standardize errors
		driver := h.u.Driver
		if driver == "postgres" {
			driver = "pq"
		}

		return &driverError{driver, err}
	}

	return err
}
<|endoftext|>


<commit_before>// Copyright 2018 The CUE Authors
//
// 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 errors defines shared types for handling CUE errors.
package errors // import "cuelang.org/go/cue/errors"

import (
	"io"
	"sort"

	"github.com/mpvl/unique"

	"cuelang.org/go/cue/token"
	"golang.org/x/exp/errors"
	"golang.org/x/exp/errors/fmt"
	"golang.org/x/xerrors"
)

// New is a convenience wrapper for errors.New in the core library.
func New(msg string) error {
	return errors.New(msg)
}

// A Handler is a generic error handler used throughout CUE packages.
//
// The position points to the beginning of the offending value.
type Handler func(pos token.Position, msg string)

// Error is the common error message.
type Error interface {
	Position() token.Position

	// Error reports the error message without position information.
	Error() string
}

// // TODO: make Error an interface that returns a list of positions.

// In an List, an error is represented by an *posError.
// The position Pos, if valid, points to the beginning of
// the offending token, and the error condition is described
// by Msg.
type posError struct {
	pos token.Position
	msg string

	// The underlying error that triggered this one, if any.
	err error
}

// E creates a new error.
func E(args ...interface{}) error {
	e := &posError{}
	update(e, args)
	return e
}

// Augment adorns an existing error with new information.
func Augment(err error, args ...interface{}) error {
	e, ok := err.(*posError)
	if !ok {
		e = &posError{err: err}
	}
	update(e, args)
	return e
}

func update(e *posError, args []interface{}) {
	err := e.err
	for _, a := range args {
		switch x := a.(type) {
		case string:
			e.msg = x
		case token.Position:
			e.pos = x
		case []token.Position:
			// TODO: do something more clever
			if len(x) > 0 {
				e.pos = x[0]
			}
		case *posError:
			copy := *x
			err = &copy
			e.err = combine(e.err, err)
		case error:
			e.err = combine(e.err, x)
		}
	}
}

func combine(a, b error) error {
	switch x := a.(type) {
	case nil:
		return b
	case List:
		x.add(toErr(b))
		return x
	default:
		return List{toErr(a), toErr(b)}
	}
}

func toErr(err error) Error {
	if e, ok := err.(Error); ok {
		return e
	}
	return &posError{err: err}
}

func (e *posError) Position() token.Position {
	return e.pos
}

// Error implements the error interface.
func (e *posError) Error() string { return fmt.Sprint(e) }

func (e *posError) FormatError(p errors.Printer) error {
	next := e.err
	if e.msg == "" {
		next = errFormat(p, e.err)
	} else {
		p.Print(e.msg)
	}
	if p.Detail() && e.pos.Filename != "" || e.pos.IsValid() {
		p.Printf("%s", e.pos.String())
	}
	return next

}

func (e posError) Unwrap() error {
	return e.err
}

func errFormat(p errors.Printer, err error) (next error) {
	switch v := err.(type) {
	case errors.Formatter:
		err = v.FormatError(p)
	default:
		p.Print(err)
		err = nil
	}

	return err
}

// List is a list of *posError.
// The zero value for an List is an empty List ready to use.
//
type List []Error

func (p *List) add(err Error) {
	*p = append(*p, err)
}

// AddNew adds an Error with given position and error message to an List.
func (p *List) AddNew(pos token.Position, msg string) {
	p.add(&posError{pos: pos, msg: msg})
}

// Add adds an Error with given position and error message to an List.
func (p *List) Add(err error) {
	p.add(toErr(err))
}

// Reset resets an List to no errors.
func (p *List) Reset() { *p = (*p)[0:0] }

// List implements the sort Interface.
func (p List) Len() int      { return len(p) }
func (p List) Swap(i, j int) { p[i], p[j] = p[j], p[i] }

func (p List) Less(i, j int) bool {
	e := p[i].Position()
	f := p[j].Position()
	// Note that it is not sufficient to simply compare file offsets because
	// the offsets do not reflect modified line information (through //line
	// comments).
	if e.Filename != f.Filename {
		return e.Filename < f.Filename
	}
	if e.Line != f.Line {
		return e.Line < f.Line
	}
	if e.Column != f.Column {
		return e.Column < f.Column
	}
	return p[i].Error() < p[j].Error()
}

// Sort sorts an List. *posError entries are sorted by position,
// other errors are sorted by error message, and before any *posError
// entry.
//
func (p List) Sort() {
	sort.Sort(p)
}

// RemoveMultiples sorts an List and removes all but the first error per line.
func (p *List) RemoveMultiples() {
	sort.Sort(p)
	var last token.Position // initial last.Line is != any legal error line
	i := 0
	for _, e := range *p {
		pos := e.Position()
		if pos.Filename != last.Filename || pos.Line != last.Line {
			last = pos
			(*p)[i] = e
			i++
		}
	}
	(*p) = (*p)[0:i]
}

// An List implements the error interface.
func (p List) Error() string {
	switch len(p) {
	case 0:
		return "no errors"
	case 1:
		return p[0].Error()
	}
	return fmt.Sprintf("%s (and %d more errors)", p[0], len(p)-1)
}

// Err returns an error equivalent to this error list.
// If the list is empty, Err returns nil.
func (p List) Err() error {
	if len(p) == 0 {
		return nil
	}
	return p
}

// Print is a utility function that prints a list of errors to w,
// one error per line, if the err parameter is an List. Otherwise
// it prints the err string.
//
func Print(w io.Writer, err error) {
	if list, ok := err.(List); ok {
		for _, e := range list {
			printErrors(w, e)
		}
	} else if err != nil {
		printErrors(w, err)
	}
}

func printErrors(w io.Writer, err error) {
	for ; err != nil; err = xerrors.Unwrap(err) {
		printError(w, err)
	}
}

func printError(w io.Writer, err error) {
	positions := []string{}
	switch x := err.(type) {
	case interface{ Position() token.Position }:
		if pos := x.Position().String(); pos != "-" {
			positions = append(positions, pos)
		}
	case interface{ Positions() []token.Pos }:
		for _, p := range x.Positions() {
			if p.IsValid() {
				positions = append(positions, p.String())
			}
		}
	}

	if len(positions) == 0 {
		fmt.Fprintln(w, err)
		return
	}

	unique.Strings(&positions)

	fmt.Fprintf(w, "%v:", err)
	for _, pos := range positions {
		fmt.Fprintf(w, "\n    %v", pos)
	}
	fmt.Fprintln(w)
}
<commit_msg>cue/errors: refactor position computation<commit_after>// Copyright 2018 The CUE Authors
//
// 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 errors defines shared types for handling CUE errors.
package errors // import "cuelang.org/go/cue/errors"

import (
	"io"
	"sort"

	"github.com/mpvl/unique"

	"cuelang.org/go/cue/token"
	"golang.org/x/exp/errors"
	"golang.org/x/exp/errors/fmt"
	"golang.org/x/xerrors"
)

// New is a convenience wrapper for errors.New in the core library.
func New(msg string) error {
	return errors.New(msg)
}

// A Handler is a generic error handler used throughout CUE packages.
//
// The position points to the beginning of the offending value.
type Handler func(pos token.Position, msg string)

// Error is the common error message.
type Error interface {
	Position() token.Position

	// Error reports the error message without position information.
	Error() string
}

// // TODO: make Error an interface that returns a list of positions.

// In an List, an error is represented by an *posError.
// The position Pos, if valid, points to the beginning of
// the offending token, and the error condition is described
// by Msg.
type posError struct {
	pos token.Position
	msg string

	// The underlying error that triggered this one, if any.
	err error
}

// E creates a new error.
func E(args ...interface{}) error {
	e := &posError{}
	update(e, args)
	return e
}

// Augment adorns an existing error with new information.
func Augment(err error, args ...interface{}) error {
	e, ok := err.(*posError)
	if !ok {
		e = &posError{err: err}
	}
	update(e, args)
	return e
}

func update(e *posError, args []interface{}) {
	err := e.err
	for _, a := range args {
		switch x := a.(type) {
		case string:
			e.msg = x
		case token.Position:
			e.pos = x
		case []token.Position:
			// TODO: do something more clever
			if len(x) > 0 {
				e.pos = x[0]
			}
		case *posError:
			copy := *x
			err = &copy
			e.err = combine(e.err, err)
		case error:
			e.err = combine(e.err, x)
		}
	}
}

func combine(a, b error) error {
	switch x := a.(type) {
	case nil:
		return b
	case List:
		x.add(toErr(b))
		return x
	default:
		return List{toErr(a), toErr(b)}
	}
}

func toErr(err error) Error {
	if e, ok := err.(Error); ok {
		return e
	}
	return &posError{err: err}
}

func (e *posError) Position() token.Position {
	return e.pos
}

// Error implements the error interface.
func (e *posError) Error() string { return fmt.Sprint(e) }

func (e *posError) FormatError(p errors.Printer) error {
	next := e.err
	if e.msg == "" {
		next = errFormat(p, e.err)
	} else {
		p.Print(e.msg)
	}
	if p.Detail() && e.pos.Filename != "" || e.pos.IsValid() {
		p.Printf("%s", e.pos.String())
	}
	return next

}

func (e posError) Unwrap() error {
	return e.err
}

func errFormat(p errors.Printer, err error) (next error) {
	switch v := err.(type) {
	case errors.Formatter:
		err = v.FormatError(p)
	default:
		p.Print(err)
		err = nil
	}

	return err
}

// List is a list of *posError.
// The zero value for an List is an empty List ready to use.
//
type List []Error

func (p *List) add(err Error) {
	*p = append(*p, err)
}

// AddNew adds an Error with given position and error message to an List.
func (p *List) AddNew(pos token.Position, msg string) {
	p.add(&posError{pos: pos, msg: msg})
}

// Add adds an Error with given position and error message to an List.
func (p *List) Add(err error) {
	p.add(toErr(err))
}

// Reset resets an List to no errors.
func (p *List) Reset() { *p = (*p)[0:0] }

// List implements the sort Interface.
func (p List) Len() int      { return len(p) }
func (p List) Swap(i, j int) { p[i], p[j] = p[j], p[i] }

func (p List) Less(i, j int) bool {
	e := p[i].Position()
	f := p[j].Position()
	// Note that it is not sufficient to simply compare file offsets because
	// the offsets do not reflect modified line information (through //line
	// comments).
	if e.Filename != f.Filename {
		return e.Filename < f.Filename
	}
	if e.Line != f.Line {
		return e.Line < f.Line
	}
	if e.Column != f.Column {
		return e.Column < f.Column
	}
	return p[i].Error() < p[j].Error()
}

// Sort sorts an List. *posError entries are sorted by position,
// other errors are sorted by error message, and before any *posError
// entry.
//
func (p List) Sort() {
	sort.Sort(p)
}

// RemoveMultiples sorts an List and removes all but the first error per line.
func (p *List) RemoveMultiples() {
	sort.Sort(p)
	var last token.Position // initial last.Line is != any legal error line
	i := 0
	for _, e := range *p {
		pos := e.Position()
		if pos.Filename != last.Filename || pos.Line != last.Line {
			last = pos
			(*p)[i] = e
			i++
		}
	}
	(*p) = (*p)[0:i]
}

// An List implements the error interface.
func (p List) Error() string {
	switch len(p) {
	case 0:
		return "no errors"
	case 1:
		return p[0].Error()
	}
	return fmt.Sprintf("%s (and %d more errors)", p[0], len(p)-1)
}

// Err returns an error equivalent to this error list.
// If the list is empty, Err returns nil.
func (p List) Err() error {
	if len(p) == 0 {
		return nil
	}
	return p
}

// Print is a utility function that prints a list of errors to w,
// one error per line, if the err parameter is an List. Otherwise
// it prints the err string.
//
func Print(w io.Writer, err error) {
	if list, ok := err.(List); ok {
		for _, e := range list {
			printError(w, e)
		}
	} else if err != nil {
		printError(w, err)
	}
}

func printError(w io.Writer, err error) {
	if err == nil {
		return
	}

	positions := []string{}

	for e := err; e != nil; e = xerrors.Unwrap(e) {
		switch x := e.(type) {
		case interface{ Position() token.Position }:
			if pos := x.Position().String(); pos != "-" {
				positions = append(positions, pos)
			}

		case interface{ Positions() []token.Pos }:
			for _, p := range x.Positions() {
				if p.IsValid() {
					positions = append(positions, p.String())
				}
			}
		}
	}

	if len(positions) == 0 {
		fmt.Fprintln(w, err)
		return
	}

	unique.Strings(&positions)

	fmt.Fprintf(w, "%v:\n", err)
	for _, pos := range positions {
		fmt.Fprintf(w, "    %v\n", pos)
	}
}
<|endoftext|>


<commit_before>package restpc

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"reflect"
	"runtime"
)

type Handler func(req Request) (res *Response, err error)

var handlers = map[string]uintptr{}

func callHandler(handler Handler, request Request) (res *Response, err error) {
	defer func() {
		panicMsg := recover()
		if panicMsg != nil {
			err = NewError(
				Internal,
				Internal.String(),
				fmt.Errorf(
					"panic in handler %v: %v",
					getFunctionName(handler),
					panicMsg,
				),
			)
		}
	}()
	res, err = handler(request)
	return
}

func TranslateHandler(handler Handler) http.HandlerFunc {
	handlerFuncObj := runtime.FuncForPC(reflect.ValueOf(handler).Pointer())
	handlerName := handlerFuncObj.Name()
	handlers[handlerName] = handlerFuncObj.Entry()
	return func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if r != nil && r.Body != nil {
				r.Body.Close()
			}
		}()
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		err := r.ParseForm()
		if err != nil {
			http.Error(w, "error in parsing form", http.StatusBadRequest)
			return
		}
		request := &requestImp{r: r}
		res, err := callHandler(handler, request)
		if err != nil {
			code := Unknown
			errorMsg := "Unknown" // FIXME: "unknown"
			rpcErr, isRpcErr := err.(RPCError)
			if isRpcErr {
				code = rpcErr.Code()
				errorMsg = rpcErr.Error() // FIXME: use a mapping or make it space-separated
			} else {
				log.Printf(
					"myrpc.TranslateHandler: handler '%v' returned non-rpc error: %#v\n",
					handlerName,
					err,
				)
				rpcErr = NewError(
					Unknown, "", err,
				)
			}
			status := HTTPStatusFromCode(code)
			jsonByte, _ := json.Marshal(map[string]string{
				"code":  code.String(),
				"error": errorMsg,
			})
			http.Error(
				w,
				string(jsonByte),
				status,
			)
			errorDispatcher(request, rpcErr)
			return
		}
		if res == nil {
			panic("TranslateHandler: func: err == nil && res == nil ")
		}
		wh := w.Header()
		if res.Header != nil {
			for key, values := range res.Header {
				for _, value := range values {
					wh.Add(key, value)
				}
			}
		}
		data := res.Data
		if data == nil {
			data = map[string]interface{}{}
		}
		resBodyBytes, err := json.Marshal(data)
		if err != nil {
			log.Println("error in json.Marshal(res.Data):", err)
		} else {
			_, err := w.Write(resBodyBytes)
			if err != nil {
				log.Println("error in w.Write(resBodyBytes):", err)
			}
		}
	}
}
<commit_msg>TranslateHandler: avoid panic if handler returned nil response with nil error<commit_after>package restpc

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"reflect"
	"runtime"
)

type Handler func(req Request) (res *Response, err error)

var handlers = map[string]uintptr{}

func callHandler(handler Handler, request Request) (res *Response, err error) {
	defer func() {
		panicMsg := recover()
		if panicMsg != nil {
			err = NewError(
				Internal,
				Internal.String(),
				fmt.Errorf(
					"panic in handler %v: %v",
					getFunctionName(handler),
					panicMsg,
				),
			)
		}
	}()
	res, err = handler(request)
	return
}

func TranslateHandler(handler Handler) http.HandlerFunc {
	handlerFuncObj := runtime.FuncForPC(reflect.ValueOf(handler).Pointer())
	handlerName := handlerFuncObj.Name()
	handlers[handlerName] = handlerFuncObj.Entry()
	return func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if r != nil && r.Body != nil {
				r.Body.Close()
			}
		}()
		w.Header().Set("Content-Type", "application/json; charset=UTF-8")
		err := r.ParseForm()
		if err != nil {
			http.Error(w, "error in parsing form", http.StatusBadRequest)
			return
		}
		request := &requestImp{r: r}
		res, err := callHandler(handler, request)
		if res == nil && err == nil {
			err = NewError(Internal, "", fmt.Errorf("handler %v returned nil response with nil error", handlerName))
		}
		if err != nil {
			code := Unknown
			errorMsg := "Unknown" // FIXME: "unknown"
			rpcErr, isRpcErr := err.(RPCError)
			if isRpcErr {
				code = rpcErr.Code()
				errorMsg = rpcErr.Error() // FIXME: use a mapping or make it space-separated
			} else {
				log.Printf(
					"myrpc.TranslateHandler: handler '%v' returned non-rpc error: %#v\n",
					handlerName,
					err,
				)
				rpcErr = NewError(
					Unknown, "", err,
				)
			}
			status := HTTPStatusFromCode(code)
			jsonByte, _ := json.Marshal(map[string]string{
				"code":  code.String(),
				"error": errorMsg,
			})
			http.Error(
				w,
				string(jsonByte),
				status,
			)
			errorDispatcher(request, rpcErr)
			return
		}
		wh := w.Header()
		if res.Header != nil {
			for key, values := range res.Header {
				for _, value := range values {
					wh.Add(key, value)
				}
			}
		}
		data := res.Data
		if data == nil {
			data = map[string]interface{}{}
		}
		resBodyBytes, err := json.Marshal(data)
		if err != nil {
			log.Println("error in json.Marshal(res.Data):", err)
		} else {
			_, err := w.Write(resBodyBytes)
			if err != nil {
				log.Println("error in w.Write(resBodyBytes):", err)
			}
		}
	}
}
<|endoftext|>


<commit_before>package cors

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

	"github.com/volatile/core"
)

const (
	// AllOrigins contains the wildcard symbol.
	AllOrigins = "*"

	// Response headers
	headerAllowCredentials = "Access-Control-Allow-Credentials"
	headerAllowHeaders     = "Access-Control-Allow-Headers"
	headerAllowMethods     = "Access-Control-Allow-Methods"
	headerAllowOrigin      = "Access-Control-Allow-Origin"
	headerExposeHeaders    = "Access-Control-Expose-Headers"
	headerMaxAge           = "Access-Control-Max-Age"

	// Request headers
	headerRequestHeaders = "Access-Control-Request-Headers"
	headerRequestMethod  = "Access-Control-Request-Method"
)

// Options represents access control options for an origin.
type Options struct {
	AllowedHeaders     []string      // AllowedHeaders indicates, in the case of a preflight request, which headers can be used during the actual request. If none are set, all are allowed.
	AllowedMethods     []string      // AllowedMethods indicates, in the case of a preflight request, which methods can be used during the actual request. If none are set, all are allowed.
	CredentialsAllowed bool          // CredentialsAllowed indicates whether the request can include user credentials like cookies, HTTP authentication or client side SSL certificates.
	ExposedHeaders     []string      // ExposedHeaders whitelists headers that browsers are allowed to access.
	MaxAge             time.Duration // MaxAge indicates how long the results of a preflight request can be cached.
}

type formattedOptions struct {
	AllowedHeaders     *string
	AllowedMethods     *string
	CredentialsAllowed *string
	ExposedHeaders     *string
	MaxAge             *string
}

// Use adds a handler that sets CORS with the provided options for all dowstream requests.
func Use(options map[string]Options) {
	fmtOpt := formatCORS(options)
	core.Use(func(c *core.Context) {
		setCORS(c, fmtOpt, c.Next)
	})
}

// LocalUse sets CORS with the provided options locally, for a single handler.
// The global options (if used) are overwritten in this situation.
func LocalUse(c *core.Context, options map[string]Options, handler func()) {
	setCORS(c, formatCORS(options), handler)
}

func formatCORS(opt map[string]Options) map[string]formattedOptions {
	fmtOpt := make(map[string]formattedOptions, len(opt))

	for origin, item := range opt {
		result := formattedOptions{}
		if len(item.AllowedHeaders) > 0 {
			*result.AllowedHeaders = strings.Join(item.AllowedHeaders, ", ")
		}
		if len(item.AllowedMethods) > 0 {
			*result.AllowedMethods = strings.Join(item.AllowedMethods, ", ")
		}
		if item.CredentialsAllowed {
			*result.CredentialsAllowed = "true"
		}
		if len(item.ExposedHeaders) > 0 {
			*result.ExposedHeaders = strings.Join(item.ExposedHeaders, ", ")
		}
		if item.MaxAge.Seconds() > 0.5 {
			*result.MaxAge = fmt.Sprintf("%.f", item.MaxAge.Seconds())
		}

		fmtOpt[origin] = result
	}

	return fmtOpt
}

// setCORS sets the response headers and continues downstream if it's not a preflight request.
func setCORS(c *core.Context, fmtOpts map[string]formattedOptions, handler func()) {
	origin := c.Request.Header.Get("Origin")

	// Use CORS only if an Origin header is defined for the request.
	if origin != "" {
		fmtOpt, knownOrigin := fmtOpts[origin]
		allOriginsAllowed := false

		// Unknown origin: check for wildcard.
		if !knownOrigin {
			fmtOpt, allOriginsAllowed = fmtOpts[AllOrigins]
		}

		// No origin matched and wildcard not accepted: reject the request.
		if !knownOrigin && !allOriginsAllowed {
			http.Error(c.ResponseWriter, "Invalid CORS request", http.StatusForbidden)
			return
		}

		c.ResponseWriter.Header().Set(headerAllowOrigin, origin)
		c.ResponseWriter.Header().Set("Vary", "Origin")

		// Set credentials header only if they are allowed.
		if fmtOpt.CredentialsAllowed != nil {
			c.ResponseWriter.Header().Set(headerAllowCredentials, *fmtOpt.CredentialsAllowed)
		}

		if fmtOpt.ExposedHeaders != nil {
			c.ResponseWriter.Header().Set(headerExposeHeaders, *fmtOpt.ExposedHeaders)
		}

		if fmtOpt.MaxAge != nil {
			c.ResponseWriter.Header().Set(headerMaxAge, *fmtOpt.MaxAge)
		}

		// OPTIONS method is used for a preflight request.
		// In this case, other CORS headers still need to be set before sending all of them, without any other work.
		if c.Request.Method == "OPTIONS" {
			// If no allowed headers are set, all are allowed.
			if fmtOpt.AllowedHeaders == nil {
				c.ResponseWriter.Header().Set(headerAllowHeaders, c.Request.Header.Get(headerRequestHeaders))
			} else {
				c.ResponseWriter.Header().Set(headerAllowHeaders, *fmtOpt.AllowedHeaders)
			}

			// If no allowed methods are set, all are allowed.
			if fmtOpt.AllowedMethods == nil {
				c.ResponseWriter.Header().Set(headerAllowMethods, c.Request.Header.Get(headerRequestMethod))
			} else {
				c.ResponseWriter.Header().Set(headerAllowMethods, *fmtOpt.AllowedMethods)
			}

			c.ResponseWriter.WriteHeader(http.StatusOK)
			return
		}
	}

	handler()
}
<commit_msg>New OriginsMap type<commit_after>package cors

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

	"github.com/volatile/core"
)

const (
	// AllOrigins contains the wildcard symbol.
	AllOrigins = "*"

	// Response headers
	headerAllowCredentials = "Access-Control-Allow-Credentials"
	headerAllowHeaders     = "Access-Control-Allow-Headers"
	headerAllowMethods     = "Access-Control-Allow-Methods"
	headerAllowOrigin      = "Access-Control-Allow-Origin"
	headerExposeHeaders    = "Access-Control-Expose-Headers"
	headerMaxAge           = "Access-Control-Max-Age"

	// Request headers
	headerRequestHeaders = "Access-Control-Request-Headers"
	headerRequestMethod  = "Access-Control-Request-Method"
)

// OriginsMap is a list of allowed origins and their respective options.
type OriginsMap map[string]*Options
type formattedOriginsMap map[string]*formattedOptions

// Options represents access control options for an origin.
type Options struct {
	AllowedHeaders     []string      // AllowedHeaders indicates, in the case of a preflight request, which headers can be used during the actual request. If none are set, all are allowed.
	AllowedMethods     []string      // AllowedMethods indicates, in the case of a preflight request, which methods can be used during the actual request. If none are set, all are allowed.
	CredentialsAllowed bool          // CredentialsAllowed indicates whether the request can include user credentials like cookies, HTTP authentication or client side SSL certificates.
	ExposedHeaders     []string      // ExposedHeaders whitelists headers that browsers are allowed to access.
	MaxAge             time.Duration // MaxAge indicates how long the results of a preflight request can be cached.
}

type formattedOptions struct {
	AllowedHeaders     *string
	AllowedMethods     *string
	CredentialsAllowed *string
	ExposedHeaders     *string
	MaxAge             *string
}

// Use adds a handler that sets CORS with the provided options for all dowstream requests.
func Use(origins OriginsMap) {
	fmtOrigins := formatCORS(origins)
	core.Use(func(c *core.Context) {
		setCORS(c, fmtOrigins, c.Next)
	})
}

// LocalUse sets CORS with the provided options locally, for a single handler.
// The global options (if used) are overwritten in this situation.
func LocalUse(c *core.Context, origins OriginsMap, handler func()) {
	setCORS(c, formatCORS(origins), handler)
}

func formatCORS(origins OriginsMap) formattedOriginsMap {
	// If no origins map is set, all are allowed.
	if origins == nil {
		origins = OriginsMap{AllOrigins: nil}
	}

	fmtOrigins := make(formattedOriginsMap)

	for origin, opts := range origins {
		// If no options are set, all are allowed.
		if opts == nil {
			opts = new(Options)
		}

		fmtOpts := new(formattedOptions)

		if len(opts.AllowedHeaders) > 0 {
			*fmtOpts.AllowedHeaders = strings.Join(opts.AllowedHeaders, ", ")
		}
		if len(opts.AllowedMethods) > 0 {
			*fmtOpts.AllowedMethods = strings.Join(opts.AllowedMethods, ", ")
		}
		if opts.CredentialsAllowed {
			*fmtOpts.CredentialsAllowed = "true"
		}
		if len(opts.ExposedHeaders) > 0 {
			*fmtOpts.ExposedHeaders = strings.Join(opts.ExposedHeaders, ", ")
		}
		if opts.MaxAge.Seconds() != 0 {
			*fmtOpts.MaxAge = fmt.Sprintf("%.f", opts.MaxAge.Seconds())
		}

		fmtOrigins[origin] = fmtOpts
	}

	return fmtOrigins
}

// setCORS sets the response headers and continues downstream if it's not a preflight request.
func setCORS(c *core.Context, fmtOrigins formattedOriginsMap, handler func()) {
	origin := c.Request.Header.Get("Origin")

	// Use CORS only if an Origin header is defined for the request.
	if origin != "" {
		fmtOpts, knownOrigin := fmtOrigins[origin]
		allOriginsAllowed := false

		// If origin is unknown, see for wildcard.
		if !knownOrigin {
			fmtOpts, allOriginsAllowed = fmtOrigins[AllOrigins]
		}

		// If origin is unknown and wildcard isn't set, reject the request.
		if !knownOrigin && !allOriginsAllowed {
			http.Error(c.ResponseWriter, "Invalid CORS request", http.StatusForbidden)
			return
		}

		c.ResponseWriter.Header().Set(headerAllowOrigin, origin)
		c.ResponseWriter.Header().Set("Vary", "Origin")

		// Set credentials header only if they are allowed.
		if fmtOpts.CredentialsAllowed != nil {
			c.ResponseWriter.Header().Set(headerAllowCredentials, *fmtOpts.CredentialsAllowed)
		}

		if fmtOpts.ExposedHeaders != nil {
			c.ResponseWriter.Header().Set(headerExposeHeaders, *fmtOpts.ExposedHeaders)
		}

		if fmtOpts.MaxAge != nil {
			c.ResponseWriter.Header().Set(headerMaxAge, *fmtOpts.MaxAge)
		}

		// OPTIONS method is used for a preflight request.
		// In this case, other CORS headers still need to be set before sending all of them, without any other work.
		if c.Request.Method == "OPTIONS" {
			// If no allowed headers are set, all are allowed.
			if fmtOpts.AllowedHeaders == nil {
				c.ResponseWriter.Header().Set(headerAllowHeaders, c.Request.Header.Get(headerRequestHeaders))
			} else {
				c.ResponseWriter.Header().Set(headerAllowHeaders, *fmtOpts.AllowedHeaders)
			}

			// If no allowed methods are set, all are allowed.
			if fmtOpts.AllowedMethods == nil {
				c.ResponseWriter.Header().Set(headerAllowMethods, c.Request.Header.Get(headerRequestMethod))
			} else {
				c.ResponseWriter.Header().Set(headerAllowMethods, *fmtOpts.AllowedMethods)
			}

			c.ResponseWriter.WriteHeader(http.StatusOK)
			return
		}
	}

	handler()
}
<|endoftext|>


<commit_before>package influxdb

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"strings"

	"github.com/bmizerany/pat"
	"github.com/influxdb/influxdb/influxql"
)

// TODO: Standard response headers (see: HeaderHandler)
// TODO: Compression (see: CompressionHeaderHandler)

// TODO: Check HTTP response codes: 400, 401, 403, 409.

// getUsernameAndPassword returns the username and password encoded in
// a request. The credentials may be present as URL query params, or as
// a Basic Authentication header.
func getUsernameAndPassword(r *http.Request) (string, string, error) {
	q := r.URL.Query()
	username, password := q.Get("u"), q.Get("p")
	if username != "" && password != "" {
		return username, password, nil
	}
	auth := r.Header.Get("Authorization")
	if auth == "" {
		return "", "", nil
	}
	fields := strings.Split(auth, " ")
	if len(fields) != 2 {
		return "", "", fmt.Errorf("invalid Basic Authentication header")
	}
	bs, err := base64.StdEncoding.DecodeString(fields[1])
	if err != nil {
		return "", "", fmt.Errorf("invalid Base64 encoding")
	}
	fields = strings.Split(string(bs), ":")
	if len(fields) != 2 {
		return "", "", fmt.Errorf("invalid Basic Authentication value")
	}
	return fields[0], fields[1], nil
}

// Handler represents an HTTP handler for the InfluxDB server.
type Handler struct {
	server *Server
	mux    *pat.PatternServeMux

	// Whether endpoints require authentication.
	AuthenticationEnabled bool

	// The InfluxDB verion returned by the HTTP response header.
	Version string
}

// NewHandler returns a new instance of Handler.
func NewHandler(s *Server) *Handler {
	h := &Handler{
		server: s,
		mux:    pat.New(),
	}

	// Query serving route.
	h.mux.Get("/query", h.makeAuthenticationHandler(h.serveQuery))

	// Data-ingest route.
	h.mux.Post("/db/:db/series", h.makeAuthenticationHandler(h.serveWriteSeries))

	// Data node routes.
	h.mux.Get("/data_nodes", h.makeAuthenticationHandler(h.serveDataNodes))
	h.mux.Post("/data_nodes", h.makeAuthenticationHandler(h.serveCreateDataNode))
	h.mux.Del("/data_nodes/:id", h.makeAuthenticationHandler(h.serveDeleteDataNode))

	// Utilities
	h.mux.Get("/metastore", h.makeAuthenticationHandler(h.serveMetastore))
	h.mux.Get("/ping", h.makeAuthenticationHandler(h.servePing))

	return h
}

// ServeHTTP responds to HTTP request to the handler.
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "*")
	w.Header().Add("Access-Control-Max-Age", "2592000")
	w.Header().Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
	w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
	w.Header().Add("X-Influxdb-Version", h.Version)

	// If this is a CORS OPTIONS request then send back okie-dokie.
	if r.Method == "OPTIONS" {
		w.WriteHeader(http.StatusOK)
		return
	}

	// Otherwise handle it via pat.
	h.mux.ServeHTTP(w, r)
}

// makeAuthenticationHandler takes a custom handler and returns a standard handler, ensuring that
// the system's standard authentication policies have been applied before the custom handler is called.
//
// The standard policy is if authentication is disabled, all operations are allowed and no user credentials
// are required. If authentication is enabled, valid user credentials must be supplied.
func (h *Handler) makeAuthenticationHandler(fn func(http.ResponseWriter, *http.Request, *User)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var user *User
		if h.AuthenticationEnabled {
			username, password, err := getUsernameAndPassword(r)
			if err != nil {
				h.error(w, err.Error(), http.StatusUnauthorized)
				return
			}
			if username == "" {
				h.error(w, "username required", http.StatusUnauthorized)
				return
			}

			user, err = h.server.Authenticate(username, password)
			if err != nil {
				h.error(w, err.Error(), http.StatusUnauthorized)
				return
			}
		}
		fn(w, r, user)
	}
}

// serveQuery parses an incoming query and returns the results.
func (h *Handler) serveQuery(w http.ResponseWriter, r *http.Request, u *User) {
	q := r.URL.Query()
	p := influxql.NewParser(strings.NewReader(q.Get("q")))

	// Parse query from query string.
	query, err := p.ParseQuery()
	if err != nil {
		h.error(w, "error parsing query: "+err.Error(), http.StatusBadRequest)
		return
	}

	// Bootstrapping a secured cluster involves a non-standard authentication policy.
	// Until an admin-level user exists on the cluster only one operation is
	// permitted -- the creation of the first user. This user is also automatically
	// granted admin-level access.
	if h.AuthenticationEnabled && !h.server.AdminUserExists() {
		if len(query.Statements) != 1 {
			_, ok := query.Statements[0].(*influxql.CreateUserStatement)
			if !ok {
				h.error(w, "initial admin user does not exist", http.StatusUnauthorized)
				return
			}
		}
	}

	for _, s := range query.Statements {
		switch c := s.(type) {
		case *influxql.CreateDatabaseStatement:
			err = h.server.CreateDatabase(c.Name)
		case *influxql.DropDatabaseStatement:
			err = h.server.DeleteDatabase(c.Name)

		case *influxql.CreateUserStatement:
			err = h.server.CreateUser(c.Name, c.Password, !h.server.AdminUserExists()) // XXX This is obviously broken.
		case *influxql.DropUserStatement:
			err = h.server.DeleteUser(c.Name)

		case *influxql.GrantStatement:
			continue
		case *influxql.RevokeStatement:
			continue

		case *influxql.CreateRetentionPolicyStatement:
			continue
		case *influxql.AlterRetentionPolicyStatement:
			continue

		case *influxql.CreateContinuousQueryStatement:
			continue
		case *influxql.DropContinuousQueryStatement:
			continue
		case *influxql.ListContinuousQueriesStatement:
			continue
		}

		if err != nil {
			h.error(w, "error executing query: "+err.Error(), http.StatusBadRequest)
		}
	}
}

// serveWriteSeries receives incoming series data and writes it to the database.
func (h *Handler) serveWriteSeries(w http.ResponseWriter, r *http.Request, u *User) {
	// TODO: Authentication.

	/* TEMPORARILY REMOVED FOR PROTOBUFS.
	// Retrieve database from server.
	db := h.server.Database(r.URL.Query().Get(":db"))
	if db == nil {
		h.error(w, ErrDatabaseNotFound.Error(), http.StatusNotFound)
		return
	}

	// Parse time precision from query parameters.
	precision, err := parseTimePrecision(r.URL.Query().Get("time_precision"))
	if err != nil {
		h.error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Setup HTTP request reader. Wrap in a gzip reader if encoding set in header.
	reader := r.Body
	if r.Header.Get("Content-Encoding") == "gzip" {
		if reader, err = gzip.NewReader(r.Body); err != nil {
			h.error(w, err.Error(), http.StatusBadRequest)
			return
		}
	}

	// Decode series from reader.
	ss := []*serializedSeries{}
	dec := json.NewDecoder(reader)
	dec.UseNumber()
	if err := dec.Decode(&ss); err != nil {
		h.error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Convert the wire format to the internal representation of the time series.
	series, err := serializedSeriesSlice(ss).series(precision)
	if err != nil {
		h.error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Write series data to the database.
	// TODO: Allow multiple series written to DB at once.
	for _, s := range series {
		if err := db.WriteSeries(s); err != nil {
			h.error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}
	*/
}

// serveMetastore returns a copy of the metastore.
func (h *Handler) serveMetastore(w http.ResponseWriter, r *http.Request, u *User) {
	// Set headers.
	w.Header().Set("Content-Type", "application/octet-stream")
	w.Header().Set("Content-Disposition", `attachment; filename="meta"`)

	// Write metastore to response body.
	if err := h.server.CopyMetastore(w); err != nil {
		h.error(w, err.Error(), http.StatusInternalServerError)
	}
}

// servePing returns a simple response to let the client know the server is running.
func (h *Handler) servePing(w http.ResponseWriter, r *http.Request, u *User) {}

// serveDataNodes returns a list of all data nodes in the cluster.
func (h *Handler) serveDataNodes(w http.ResponseWriter, r *http.Request, u *User) {
	// Generate a list of objects for encoding to the API.
	a := make([]*dataNodeJSON, 0)
	for _, n := range h.server.DataNodes() {
		a = append(a, &dataNodeJSON{
			ID:  n.ID,
			URL: n.URL.String(),
		})
	}

	w.Header().Add("content-type", "application/json")
	_ = json.NewEncoder(w).Encode(a)
}

// serveCreateDataNode creates a new data node in the cluster.
func (h *Handler) serveCreateDataNode(w http.ResponseWriter, r *http.Request, _ *User) {
	// Read in data node from request body.
	var n dataNodeJSON
	if err := json.NewDecoder(r.Body).Decode(&n); err != nil {
		h.error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Parse the URL.
	u, err := url.Parse(n.URL)
	if err != nil {
		h.error(w, "invalid data node url", http.StatusBadRequest)
		return
	}

	// Create the data node.
	if err := h.server.CreateDataNode(u); err == ErrDataNodeExists {
		h.error(w, err.Error(), http.StatusConflict)
		return
	} else if err != nil {
		h.error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// Retrieve data node reference.
	node := h.server.DataNodeByURL(u)

	// Create a new replica on the broker.
	if err := h.server.client.CreateReplica(node.ID); err != nil {
		h.error(w, err.Error(), http.StatusBadGateway)
		return
	}

	// Write new node back to client.
	w.WriteHeader(http.StatusCreated)
	w.Header().Add("content-type", "application/json")
	_ = json.NewEncoder(w).Encode(&dataNodeJSON{ID: node.ID, URL: node.URL.String()})
}

// serveDeleteDataNode removes an existing node.
func (h *Handler) serveDeleteDataNode(w http.ResponseWriter, r *http.Request, u *User) {
	// Parse node id.
	nodeID, err := strconv.ParseUint(r.URL.Query().Get(":id"), 10, 64)
	if err != nil {
		h.error(w, "invalid node id", http.StatusBadRequest)
		return
	}

	// Delete the node.
	if err := h.server.DeleteDataNode(nodeID); err == ErrDataNodeNotFound {
		h.error(w, err.Error(), http.StatusNotFound)
		return
	} else if err != nil {
		h.error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

type dataNodeJSON struct {
	ID  uint64 `json:"id"`
	URL string `json:"url"`
}

// error returns an error to the client in a standard format.
func (h *Handler) error(w http.ResponseWriter, error string, code int) {
	// TODO: Return error as JSON.
	http.Error(w, error, code)
}
<commit_msg>Final type switches<commit_after>package influxdb

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"strings"

	"github.com/bmizerany/pat"
	"github.com/influxdb/influxdb/influxql"
)

// TODO: Standard response headers (see: HeaderHandler)
// TODO: Compression (see: CompressionHeaderHandler)

// TODO: Check HTTP response codes: 400, 401, 403, 409.

// getUsernameAndPassword returns the username and password encoded in
// a request. The credentials may be present as URL query params, or as
// a Basic Authentication header.
func getUsernameAndPassword(r *http.Request) (string, string, error) {
	q := r.URL.Query()
	username, password := q.Get("u"), q.Get("p")
	if username != "" && password != "" {
		return username, password, nil
	}
	auth := r.Header.Get("Authorization")
	if auth == "" {
		return "", "", nil
	}
	fields := strings.Split(auth, " ")
	if len(fields) != 2 {
		return "", "", fmt.Errorf("invalid Basic Authentication header")
	}
	bs, err := base64.StdEncoding.DecodeString(fields[1])
	if err != nil {
		return "", "", fmt.Errorf("invalid Base64 encoding")
	}
	fields = strings.Split(string(bs), ":")
	if len(fields) != 2 {
		return "", "", fmt.Errorf("invalid Basic Authentication value")
	}
	return fields[0], fields[1], nil
}

// Handler represents an HTTP handler for the InfluxDB server.
type Handler struct {
	server *Server
	mux    *pat.PatternServeMux

	// Whether endpoints require authentication.
	AuthenticationEnabled bool

	// The InfluxDB verion returned by the HTTP response header.
	Version string
}

// NewHandler returns a new instance of Handler.
func NewHandler(s *Server) *Handler {
	h := &Handler{
		server: s,
		mux:    pat.New(),
	}

	// Query serving route.
	h.mux.Get("/query", h.makeAuthenticationHandler(h.serveQuery))

	// Data-ingest route.
	h.mux.Post("/db/:db/series", h.makeAuthenticationHandler(h.serveWriteSeries))

	// Data node routes.
	h.mux.Get("/data_nodes", h.makeAuthenticationHandler(h.serveDataNodes))
	h.mux.Post("/data_nodes", h.makeAuthenticationHandler(h.serveCreateDataNode))
	h.mux.Del("/data_nodes/:id", h.makeAuthenticationHandler(h.serveDeleteDataNode))

	// Utilities
	h.mux.Get("/metastore", h.makeAuthenticationHandler(h.serveMetastore))
	h.mux.Get("/ping", h.makeAuthenticationHandler(h.servePing))

	return h
}

// ServeHTTP responds to HTTP request to the handler.
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "*")
	w.Header().Add("Access-Control-Max-Age", "2592000")
	w.Header().Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
	w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
	w.Header().Add("X-Influxdb-Version", h.Version)

	// If this is a CORS OPTIONS request then send back okie-dokie.
	if r.Method == "OPTIONS" {
		w.WriteHeader(http.StatusOK)
		return
	}

	// Otherwise handle it via pat.
	h.mux.ServeHTTP(w, r)
}

// makeAuthenticationHandler takes a custom handler and returns a standard handler, ensuring that
// the system's standard authentication policies have been applied before the custom handler is called.
//
// The standard policy is if authentication is disabled, all operations are allowed and no user credentials
// are required. If authentication is enabled, valid user credentials must be supplied.
func (h *Handler) makeAuthenticationHandler(fn func(http.ResponseWriter, *http.Request, *User)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		var user *User
		if h.AuthenticationEnabled {
			username, password, err := getUsernameAndPassword(r)
			if err != nil {
				h.error(w, err.Error(), http.StatusUnauthorized)
				return
			}
			if username == "" {
				h.error(w, "username required", http.StatusUnauthorized)
				return
			}

			user, err = h.server.Authenticate(username, password)
			if err != nil {
				h.error(w, err.Error(), http.StatusUnauthorized)
				return
			}
		}
		fn(w, r, user)
	}
}

// serveQuery parses an incoming query and returns the results.
func (h *Handler) serveQuery(w http.ResponseWriter, r *http.Request, u *User) {
	q := r.URL.Query()
	p := influxql.NewParser(strings.NewReader(q.Get("q")))

	// Parse query from query string.
	query, err := p.ParseQuery()
	if err != nil {
		h.error(w, "error parsing query: "+err.Error(), http.StatusBadRequest)
		return
	}

	// Bootstrapping a secured cluster involves a non-standard authentication policy.
	// Until an admin-level user exists on the cluster only one operation is
	// permitted -- the creation of the first user. This user is also automatically
	// granted admin-level access.
	if h.AuthenticationEnabled && !h.server.AdminUserExists() {
		if len(query.Statements) != 1 {
			_, ok := query.Statements[0].(*influxql.CreateUserStatement)
			if !ok {
				h.error(w, "initial admin user does not exist", http.StatusUnauthorized)
				return
			}
		}
	}

	for _, s := range query.Statements {
		switch c := s.(type) {
		case *influxql.CreateDatabaseStatement:
			err = h.server.CreateDatabase(c.Name)
		case *influxql.DropDatabaseStatement:
			err = h.server.DeleteDatabase(c.Name)

		case *influxql.CreateUserStatement:
			err = h.server.CreateUser(c.Name, c.Password, !h.server.AdminUserExists()) // XXX This is obviously broken.
		case *influxql.DropUserStatement:
			err = h.server.DeleteUser(c.Name)

		case *influxql.SelectStatement:
			continue
		case *influxql.DropSeriesStatement:
			continue
		case *influxql.ListSeriesStatement:
			continue
		case *influxql.ListMeasurementsStatement:
			continue
		case *influxql.ListTagKeysStatement:
			continue
		case *influxql.ListTagValuesStatement:
			continue
		case *influxql.ListFieldKeysStatement:
			continue
		case *influxql.ListFieldValuesStatement:
			continue

		case *influxql.GrantStatement:
			continue
		case *influxql.RevokeStatement:
			continue

		case *influxql.CreateRetentionPolicyStatement:
			continue
		case *influxql.AlterRetentionPolicyStatement:
			continue

		case *influxql.CreateContinuousQueryStatement:
			continue
		case *influxql.DropContinuousQueryStatement:
			continue
		case *influxql.ListContinuousQueriesStatement:
			continue
		}

		if err != nil {
			h.error(w, "error executing query: "+err.Error(), http.StatusBadRequest)
		}
	}
}

// serveWriteSeries receives incoming series data and writes it to the database.
func (h *Handler) serveWriteSeries(w http.ResponseWriter, r *http.Request, u *User) {
	// TODO: Authentication.

	/* TEMPORARILY REMOVED FOR PROTOBUFS.
	// Retrieve database from server.
	db := h.server.Database(r.URL.Query().Get(":db"))
	if db == nil {
		h.error(w, ErrDatabaseNotFound.Error(), http.StatusNotFound)
		return
	}

	// Parse time precision from query parameters.
	precision, err := parseTimePrecision(r.URL.Query().Get("time_precision"))
	if err != nil {
		h.error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Setup HTTP request reader. Wrap in a gzip reader if encoding set in header.
	reader := r.Body
	if r.Header.Get("Content-Encoding") == "gzip" {
		if reader, err = gzip.NewReader(r.Body); err != nil {
			h.error(w, err.Error(), http.StatusBadRequest)
			return
		}
	}

	// Decode series from reader.
	ss := []*serializedSeries{}
	dec := json.NewDecoder(reader)
	dec.UseNumber()
	if err := dec.Decode(&ss); err != nil {
		h.error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Convert the wire format to the internal representation of the time series.
	series, err := serializedSeriesSlice(ss).series(precision)
	if err != nil {
		h.error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Write series data to the database.
	// TODO: Allow multiple series written to DB at once.
	for _, s := range series {
		if err := db.WriteSeries(s); err != nil {
			h.error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	}
	*/
}

// serveMetastore returns a copy of the metastore.
func (h *Handler) serveMetastore(w http.ResponseWriter, r *http.Request, u *User) {
	// Set headers.
	w.Header().Set("Content-Type", "application/octet-stream")
	w.Header().Set("Content-Disposition", `attachment; filename="meta"`)

	// Write metastore to response body.
	if err := h.server.CopyMetastore(w); err != nil {
		h.error(w, err.Error(), http.StatusInternalServerError)
	}
}

// servePing returns a simple response to let the client know the server is running.
func (h *Handler) servePing(w http.ResponseWriter, r *http.Request, u *User) {}

// serveDataNodes returns a list of all data nodes in the cluster.
func (h *Handler) serveDataNodes(w http.ResponseWriter, r *http.Request, u *User) {
	// Generate a list of objects for encoding to the API.
	a := make([]*dataNodeJSON, 0)
	for _, n := range h.server.DataNodes() {
		a = append(a, &dataNodeJSON{
			ID:  n.ID,
			URL: n.URL.String(),
		})
	}

	w.Header().Add("content-type", "application/json")
	_ = json.NewEncoder(w).Encode(a)
}

// serveCreateDataNode creates a new data node in the cluster.
func (h *Handler) serveCreateDataNode(w http.ResponseWriter, r *http.Request, _ *User) {
	// Read in data node from request body.
	var n dataNodeJSON
	if err := json.NewDecoder(r.Body).Decode(&n); err != nil {
		h.error(w, err.Error(), http.StatusBadRequest)
		return
	}

	// Parse the URL.
	u, err := url.Parse(n.URL)
	if err != nil {
		h.error(w, "invalid data node url", http.StatusBadRequest)
		return
	}

	// Create the data node.
	if err := h.server.CreateDataNode(u); err == ErrDataNodeExists {
		h.error(w, err.Error(), http.StatusConflict)
		return
	} else if err != nil {
		h.error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// Retrieve data node reference.
	node := h.server.DataNodeByURL(u)

	// Create a new replica on the broker.
	if err := h.server.client.CreateReplica(node.ID); err != nil {
		h.error(w, err.Error(), http.StatusBadGateway)
		return
	}

	// Write new node back to client.
	w.WriteHeader(http.StatusCreated)
	w.Header().Add("content-type", "application/json")
	_ = json.NewEncoder(w).Encode(&dataNodeJSON{ID: node.ID, URL: node.URL.String()})
}

// serveDeleteDataNode removes an existing node.
func (h *Handler) serveDeleteDataNode(w http.ResponseWriter, r *http.Request, u *User) {
	// Parse node id.
	nodeID, err := strconv.ParseUint(r.URL.Query().Get(":id"), 10, 64)
	if err != nil {
		h.error(w, "invalid node id", http.StatusBadRequest)
		return
	}

	// Delete the node.
	if err := h.server.DeleteDataNode(nodeID); err == ErrDataNodeNotFound {
		h.error(w, err.Error(), http.StatusNotFound)
		return
	} else if err != nil {
		h.error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

type dataNodeJSON struct {
	ID  uint64 `json:"id"`
	URL string `json:"url"`
}

// error returns an error to the client in a standard format.
func (h *Handler) error(w http.ResponseWriter, error string, code int) {
	// TODO: Return error as JSON.
	http.Error(w, error, code)
}
<|endoftext|>


<commit_before>package ctx

import (
	"net/http"

	"github.com/dgrijalva/jwt-go"
	"github.com/nicksnyder/go-i18n/i18n"
)

type ContextHandler func(c *Context, rw http.ResponseWriter, req *http.Request) error

func newContextHandler(context *Context, endpoint *Endpoint) http.HandlerFunc {
	return func(rw http.ResponseWriter, req *http.Request) {

		// check if the http method is mapped
		var handler ContextHandler
		var found bool
		if handler, found = endpoint.Handlers[req.Method]; !found {
			http.Error(rw, "Method not allowed", http.StatusMethodNotAllowed)
			return
		}

		// extract the token, if the endpoint is private
		if !endpoint.Public {
			context.Token, _ = jwt.ParseFromRequest(
				req, context.middleware.Options.ValidationKeyGetter,
			)
		}

		// update T
		acceptLang := req.Header.Get("Accept-Language")
		defaultLang := "en-US"
		context.T = i18n.MustTfunc(acceptLang, defaultLang)

		// call the apropriate handler
		err := handler(context, rw, req)
		if err != nil {
			http.Error(rw, err.Error(), http.StatusBadRequest)
		}
	}
}
<commit_msg>update api<commit_after>package ctx

import (
	"net/http"

	"github.com/dgrijalva/jwt-go/request"
	"github.com/nicksnyder/go-i18n/i18n"
)

type ContextHandler func(c *Context, rw http.ResponseWriter, req *http.Request) error

func newContextHandler(context *Context, endpoint *Endpoint) http.HandlerFunc {
	return func(rw http.ResponseWriter, req *http.Request) {

		// check if the http method is mapped
		var handler ContextHandler
		var found bool
		if handler, found = endpoint.Handlers[req.Method]; !found {
			http.Error(rw, "Method not allowed", http.StatusMethodNotAllowed)
			return
		}

		// extract the token, if the endpoint is private
		if !endpoint.Public {
			context.Token, _ = request.ParseFromRequest(
				req, request.HeaderExtractor{}, context.middleware.Options.ValidationKeyGetter,
			)
		}

		// update T
		acceptLang := req.Header.Get("Accept-Language")
		defaultLang := "en-US"
		context.T = i18n.MustTfunc(acceptLang, defaultLang)

		// call the apropriate handler
		err := handler(context, rw, req)
		if err != nil {
			http.Error(rw, err.Error(), http.StatusBadRequest)
		}
	}
}
<|endoftext|>