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

package view

import (
	"github.com/google/shenzhen-go/dev/dom"
	"golang.org/x/net/context"
)

const (
	nametagRectStyle = "fill: #efe; stroke: #353; stroke-width:1"
	nametagTextStyle = "font-family:Go; font-size:16; user-select:none; pointer-events:none"
)

// Pin represents a node pin visually, and has enough information to know
// if it is validly connected.
type Pin struct {
	Group                          // Container for all the pin elements.
	Shape              dom.Element // The pin itself.
	Nametag            *TextBox    // Temporarily visible on hover.
	dragLine, dragCirc dom.Element // Temporary elements when dragging from unattached pin.

	// Computed, absolute coordinates (not relative to node).
	x, y float64

	pc PinController

	node *Node    // owner.
	ch   *Channel // attached to this channel, is often nil
}

func (p *Pin) reallyConnect() {
	// Attach to the existing channel
	if err := p.pc.Attach(context.TODO(), p.ch.cc); err != nil {
		p.node.view.setError("Couldn't connect: " + err.Error())
	}
}

func (p *Pin) disconnect() {
	if p.ch == nil {
		return
	}
	go p.reallyDisconnect()
	delete(p.ch.Pins, p)
	p.ch.setColour(normalColour)
	p.ch.reposition(nil)
	if len(p.ch.Pins) < 2 {
		// Delete the channel
		for q := range p.ch.Pins {
			q.ch = nil
		}
		delete(p.node.view.graph.Channels, p.ch.cc.Name())
	}
	p.ch = nil
}

func (p *Pin) reallyDisconnect() {
	if err := p.pc.Detach(context.TODO()); err != nil {
		p.node.view.setError("Couldn't disconnect: " + err.Error())
	}
}

// MoveTo moves the pin (relatively).
func (p *Pin) MoveTo(rx, ry float64) {
	p.Group.MoveTo(rx, ry)
	p.x, p.y = rx+p.node.x, ry+p.node.y
}

// Pt returns the diagram coordinate of the pin, for nearest-neighbor purposes.
func (p *Pin) Pt() (x, y float64) { return p.x, p.y }

func (p *Pin) String() string { return p.node.nc.Name() + "." + p.pc.Name() }

func (p *Pin) connectTo(q Point) {
	switch q := q.(type) {
	case *Pin:
		if p.ch != nil && p.ch != q.ch {
			p.disconnect()
		}
		if q.ch != nil {
			p.connectTo(q.ch)
			return
		}

		// Create a new channel to connect to
		ch := p.node.view.createChannel(p, q)
		ch.reposition(nil)

	case *Channel:
		if p.ch != nil && p.ch != q {
			p.disconnect()
		}

		p.ch = q
		q.Pins[p] = &Route{}
		q.reposition(nil)
	}
	return
}

func (p *Pin) dragStart(e dom.Object) {
	// If a channel is attached, detach and drag from that instead.
	if p.ch != nil {
		p.disconnect()
		p.ch.dragStart(e)
		return
	}

	// Not attached, so the pin is the drag item and show the temporary line and circle.
	p.node.view.dragItem = p
	x, y := p.node.view.diagramCursorPos(e)

	// Start with errorColour because we're probably only in range of ourself.
	p.dragTo(x, y, errorColour)
}

func (p *Pin) drag(e dom.Object) {
	x, y := p.node.view.diagramCursorPos(e)
	colour := activeColour

	d, q := p.node.view.graph.nearestPoint(x, y)

	// Don't connect P to itself, don't connect if nearest is far away.
	if p == q || d >= snapQuad {
		p.node.view.clearError()
		if p.ch != nil {
			p.ch.setColour(normalColour)
			p.disconnect()
		}
		colour = errorColour
		p.Shape.SetAttribute("fill", colour)
		p.dragTo(x-p.x, y-p.y, colour)
		return
	}

	// Make the connection - this is the responsibility of the channel.
	p.node.view.clearError()
	colour = activeColour
	p.connectTo(q)
	p.ch.setColour(colour)
	p.hideDrag()
}

func (p *Pin) drop(e dom.Object) {
	p.node.view.clearError()
	p.Shape.SetAttribute("fill", normalColour)
	p.hideDrag()
	if p.ch == nil {
		go p.reallyDisconnect()
		return
	}
	if p.ch.created {
		go p.reallyConnect()
	}
	p.ch.setColour(normalColour)
	p.ch.commit()
}

// Show the temporary drag elements with a specific colour.
// Coordinates are pin relative.
func (p *Pin) dragTo(rx, ry float64, stroke string) {
	p.dragLine.
		SetAttribute("x2", rx).
		SetAttribute("y2", ry).
		SetAttribute("stroke", stroke).
		SetAttribute("stroke-width", lineWidth).
		Show()
	p.dragCirc.
		SetAttribute("cx", rx).
		SetAttribute("cy", ry).
		SetAttribute("stroke", stroke).
		SetAttribute("stroke-width", lineWidth).
		Show()
}

func (p *Pin) hideDrag() {
	p.dragLine.Hide()
	p.dragCirc.Hide()
}

func (p *Pin) mouseEnter(dom.Object) {
	x, y := 8.0, 8.0
	if p.pc.IsInput() {
		y = -38
	}
	p.Nametag.MoveTo(x, y).Show()
}

func (p *Pin) mouseLeave(dom.Object) {
	p.Nametag.Hide()
}

// MakeElements creates elements associated with this pin.
func (p *Pin) MakeElements(doc dom.Document, parent dom.Element) *Pin {
	// Container for the pin elements.
	p.Group = NewGroup(doc, parent)

	// The pin itself, visually
	p.Shape = doc.MakeSVGElement("circle").
		SetAttribute("r", pinRadius).
		SetAttribute("fill", normalColour).
		AddEventListener("mousedown", p.dragStart).
		AddEventListener("mouseenter", p.mouseEnter).
		AddEventListener("mouseleave", p.mouseLeave)

	// Nametag textbox.
	p.Nametag = &TextBox{Margin: 20, TextOffsetY: 5}
	p.Nametag.
		MakeElements(doc, p.Group).
		SetHeight(30).
		SetTextStyle(nametagTextStyle).
		SetRectStyle(nametagRectStyle).
		SetText(p.pc.Name() + " (" + p.pc.Type() + ")")
	p.Nametag.RecomputeWidth()
	p.Nametag.Hide()

	// Temporarily-visible elements when dragging from an unattached pin.
	p.dragLine = doc.MakeSVGElement("line").
		SetAttribute("stroke", normalColour).
		Hide()
	p.dragCirc = doc.MakeSVGElement("circ").
		SetAttribute("r", pinRadius).
		SetAttribute("stroke", normalColour).
		Hide()

	p.Group.AddChildren(p.Shape, p.dragLine, p.dragCirc)
	return p
}

// AddTo adds the pin's group as a child to the given parent.
func (p *Pin) AddTo(parent dom.Element) *Pin {
	parent.AddChildren(p.Group)
	return p
}

// Remove removes the group from its parent.
func (p *Pin) Remove() {
	p.Group.Parent().RemoveChildren(p.Group)
}
<commit_msg>These were unused<commit_after>// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package view

import (
	"github.com/google/shenzhen-go/dev/dom"
	"golang.org/x/net/context"
)

const (
	nametagRectStyle = "fill: #efe; stroke: #353; stroke-width:1"
	nametagTextStyle = "font-family:Go; font-size:16; user-select:none; pointer-events:none"
)

// Pin represents a node pin visually, and has enough information to know
// if it is validly connected.
type Pin struct {
	Group                          // Container for all the pin elements.
	Shape              dom.Element // The pin itself.
	Nametag            *TextBox    // Temporarily visible on hover.
	dragLine, dragCirc dom.Element // Temporary elements when dragging from unattached pin.

	// Computed, absolute coordinates (not relative to node).
	x, y float64

	pc PinController

	node *Node    // owner.
	ch   *Channel // attached to this channel, is often nil
}

func (p *Pin) reallyConnect() {
	// Attach to the existing channel
	if err := p.pc.Attach(context.TODO(), p.ch.cc); err != nil {
		p.node.view.setError("Couldn't connect: " + err.Error())
	}
}

func (p *Pin) disconnect() {
	if p.ch == nil {
		return
	}
	go p.reallyDisconnect()
	delete(p.ch.Pins, p)
	p.ch.setColour(normalColour)
	p.ch.reposition(nil)
	if len(p.ch.Pins) < 2 {
		// Delete the channel
		for q := range p.ch.Pins {
			q.ch = nil
		}
		delete(p.node.view.graph.Channels, p.ch.cc.Name())
	}
	p.ch = nil
}

func (p *Pin) reallyDisconnect() {
	if err := p.pc.Detach(context.TODO()); err != nil {
		p.node.view.setError("Couldn't disconnect: " + err.Error())
	}
}

// MoveTo moves the pin (relatively).
func (p *Pin) MoveTo(rx, ry float64) {
	p.Group.MoveTo(rx, ry)
	p.x, p.y = rx+p.node.x, ry+p.node.y
}

// Pt returns the diagram coordinate of the pin, for nearest-neighbor purposes.
func (p *Pin) Pt() (x, y float64) { return p.x, p.y }

func (p *Pin) String() string { return p.node.nc.Name() + "." + p.pc.Name() }

func (p *Pin) connectTo(q Point) {
	switch q := q.(type) {
	case *Pin:
		if p.ch != nil && p.ch != q.ch {
			p.disconnect()
		}
		if q.ch != nil {
			p.connectTo(q.ch)
			return
		}

		// Create a new channel to connect to
		ch := p.node.view.createChannel(p, q)
		ch.reposition(nil)

	case *Channel:
		if p.ch != nil && p.ch != q {
			p.disconnect()
		}

		p.ch = q
		q.Pins[p] = &Route{}
		q.reposition(nil)
	}
	return
}

func (p *Pin) dragStart(e dom.Object) {
	// If a channel is attached, detach and drag from that instead.
	if p.ch != nil {
		p.disconnect()
		p.ch.dragStart(e)
		return
	}

	// Not attached, so the pin is the drag item and show the temporary line and circle.
	p.node.view.dragItem = p
	x, y := p.node.view.diagramCursorPos(e)

	// Start with errorColour because we're probably only in range of ourself.
	p.dragTo(x, y, errorColour)
}

func (p *Pin) drag(e dom.Object) {
	x, y := p.node.view.diagramCursorPos(e)
	colour := activeColour

	d, q := p.node.view.graph.nearestPoint(x, y)

	// Don't connect P to itself, don't connect if nearest is far away.
	if p == q || d >= snapQuad {
		p.node.view.clearError()
		if p.ch != nil {
			p.ch.setColour(normalColour)
			p.disconnect()
		}
		colour = errorColour
		p.Shape.SetAttribute("fill", colour)
		p.dragTo(x-p.x, y-p.y, colour)
		return
	}

	// Make the connection - this is the responsibility of the channel.
	p.node.view.clearError()
	colour = activeColour
	p.connectTo(q)
	p.ch.setColour(colour)
	p.hideDrag()
}

func (p *Pin) drop(e dom.Object) {
	p.node.view.clearError()
	p.Shape.SetAttribute("fill", normalColour)
	p.hideDrag()
	if p.ch == nil {
		go p.reallyDisconnect()
		return
	}
	if p.ch.created {
		go p.reallyConnect()
	}
	p.ch.setColour(normalColour)
	p.ch.commit()
}

// Show the temporary drag elements with a specific colour.
// Coordinates are pin relative.
func (p *Pin) dragTo(rx, ry float64, stroke string) {
	p.dragLine.
		SetAttribute("x2", rx).
		SetAttribute("y2", ry).
		SetAttribute("stroke", stroke).
		SetAttribute("stroke-width", lineWidth).
		Show()
	p.dragCirc.
		SetAttribute("cx", rx).
		SetAttribute("cy", ry).
		SetAttribute("stroke", stroke).
		SetAttribute("stroke-width", lineWidth).
		Show()
}

func (p *Pin) hideDrag() {
	p.dragLine.Hide()
	p.dragCirc.Hide()
}

func (p *Pin) mouseEnter(dom.Object) {
	x, y := 8.0, 8.0
	if p.pc.IsInput() {
		y = -38
	}
	p.Nametag.MoveTo(x, y).Show()
}

func (p *Pin) mouseLeave(dom.Object) {
	p.Nametag.Hide()
}

// MakeElements creates elements associated with this pin.
func (p *Pin) MakeElements(doc dom.Document, parent dom.Element) *Pin {
	// Container for the pin elements.
	p.Group = NewGroup(doc, parent)

	// The pin itself, visually
	p.Shape = doc.MakeSVGElement("circle").
		SetAttribute("r", pinRadius).
		SetAttribute("fill", normalColour).
		AddEventListener("mousedown", p.dragStart).
		AddEventListener("mouseenter", p.mouseEnter).
		AddEventListener("mouseleave", p.mouseLeave)

	// Nametag textbox.
	p.Nametag = &TextBox{Margin: 20, TextOffsetY: 5}
	p.Nametag.
		MakeElements(doc, p.Group).
		SetHeight(30).
		SetTextStyle(nametagTextStyle).
		SetRectStyle(nametagRectStyle).
		SetText(p.pc.Name() + " (" + p.pc.Type() + ")")
	p.Nametag.RecomputeWidth()
	p.Nametag.Hide()

	// Temporarily-visible elements when dragging from an unattached pin.
	p.dragLine = doc.MakeSVGElement("line").
		SetAttribute("stroke", normalColour).
		Hide()
	p.dragCirc = doc.MakeSVGElement("circ").
		SetAttribute("r", pinRadius).
		SetAttribute("stroke", normalColour).
		Hide()

	p.Group.AddChildren(p.Shape, p.dragLine, p.dragCirc)
	return p
}
<|endoftext|>


<commit_before>// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// TODO: Dashboard upload

package main

import (
	"fmt"
	"go/build"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
)

var cmdGet = &Command{
	UsageLine: "get [-d] [-fix] [-t] [-u] [build flags] [packages]",
	Short:     "download and install packages and dependencies",
	Long: `
Get downloads and installs the packages named by the import paths,
along with their dependencies.

The -d flag instructs get to stop after downloading the packages; that is,
it instructs get not to install the packages.

The -fix flag instructs get to run the fix tool on the downloaded packages
before resolving dependencies or building the code.

The -t flag instructs get to also download the packages required to build
the tests for the specified packages.

The -u flag instructs get to use the network to update the named packages
and their dependencies.  By default, get uses the network to check out
missing packages but does not use it to look for updates to existing packages.

Get also accepts all the flags in the 'go build' and 'go install' commands,
to control the installation. See 'go help build'.

When checking out or updating a package, get looks for a branch or tag
that matches the locally installed version of Go. The most important
rule is that if the local installation is running version "go1", get
searches for a branch or tag named "go1". If no such version exists it
retrieves the most recent version of the package.

For more about specifying packages, see 'go help packages'.

For more about how 'go get' finds source code to
download, see 'go help remote'.

See also: go build, go install, go clean.
	`,
}

var getD = cmdGet.Flag.Bool("d", false, "")
var getT = cmdGet.Flag.Bool("t", false, "")
var getU = cmdGet.Flag.Bool("u", false, "")
var getFix = cmdGet.Flag.Bool("fix", false, "")

func init() {
	addBuildFlags(cmdGet)
	cmdGet.Run = runGet // break init loop
}

func runGet(cmd *Command, args []string) {
	// Phase 1.  Download/update.
	var stk importStack
	for _, arg := range downloadPaths(args) {
		download(arg, &stk, *getT)
	}
	exitIfErrors()

	// Phase 2. Rescan packages and reevaluate args list.

	// Code we downloaded and all code that depends on it
	// needs to be evicted from the package cache so that
	// the information will be recomputed.  Instead of keeping
	// track of the reverse dependency information, evict
	// everything.
	for name := range packageCache {
		delete(packageCache, name)
	}

	args = importPaths(args)

	// Phase 3.  Install.
	if *getD {
		// Download only.
		// Check delayed until now so that importPaths
		// has a chance to print errors.
		return
	}

	runInstall(cmd, args)
}

// downloadPaths prepares the list of paths to pass to download.
// It expands ... patterns that can be expanded.  If there is no match
// for a particular pattern, downloadPaths leaves it in the result list,
// in the hope that we can figure out the repository from the
// initial ...-free prefix.
func downloadPaths(args []string) []string {
	args = importPathsNoDotExpansion(args)
	var out []string
	for _, a := range args {
		if strings.Contains(a, "...") {
			var expand []string
			// Use matchPackagesInFS to avoid printing
			// warnings.  They will be printed by the
			// eventual call to importPaths instead.
			if build.IsLocalImport(a) {
				expand = matchPackagesInFS(a)
			} else {
				expand = matchPackages(a)
			}
			if len(expand) > 0 {
				out = append(out, expand...)
				continue
			}
		}
		out = append(out, a)
	}
	return out
}

// downloadCache records the import paths we have already
// considered during the download, to avoid duplicate work when
// there is more than one dependency sequence leading to
// a particular package.
var downloadCache = map[string]bool{}

// downloadRootCache records the version control repository
// root directories we have already considered during the download.
// For example, all the packages in the code.google.com/p/codesearch repo
// share the same root (the directory for that path), and we only need
// to run the hg commands to consider each repository once.
var downloadRootCache = map[string]bool{}

// download runs the download half of the get command
// for the package named by the argument.
func download(arg string, stk *importStack, getTestDeps bool) {
	p := loadPackage(arg, stk)

	// There's nothing to do if this is a package in the standard library.
	if p.Standard {
		return
	}

	// Only process each package once.
	if downloadCache[arg] {
		return
	}
	downloadCache[arg] = true

	pkgs := []*Package{p}
	wildcardOkay := len(*stk) == 0

	// Download if the package is missing, or update if we're using -u.
	if p.Dir == "" || *getU {
		// The actual download.
		stk.push(p.ImportPath)
		err := downloadPackage(p)
		if err != nil {
			errorf("%s", &PackageError{ImportStack: stk.copy(), Err: err.Error()})
			stk.pop()
			return
		}

		args := []string{arg}
		// If the argument has a wildcard in it, re-evaluate the wildcard.
		// We delay this until after reloadPackage so that the old entry
		// for p has been replaced in the package cache.
		if wildcardOkay && strings.Contains(arg, "...") {
			if build.IsLocalImport(arg) {
				args = matchPackagesInFS(arg)
			} else {
				args = matchPackages(arg)
			}
		}

		// Clear all relevant package cache entries before
		// doing any new loads.
		for _, arg := range args {
			p := packageCache[arg]
			if p != nil {
				delete(packageCache, p.Dir)
				delete(packageCache, p.ImportPath)
			}
		}

		pkgs = pkgs[:0]
		for _, arg := range args {
			stk.push(arg)
			p := loadPackage(arg, stk)
			stk.pop()
			if p.Error != nil {
				errorf("%s", p.Error)
				continue
			}
			pkgs = append(pkgs, p)
		}
	}

	// Process package, which might now be multiple packages
	// due to wildcard expansion.
	for _, p := range pkgs {
		if *getFix {
			run(stringList(tool("fix"), relPaths(p.allgofiles)))

			// The imports might have changed, so reload again.
			p = reloadPackage(arg, stk)
			if p.Error != nil {
				errorf("%s", p.Error)
				return
			}
		}

		// Process dependencies, now that we know what they are.
		for _, dep := range p.deps {
			// Don't get test dependencies recursively.
			download(dep.ImportPath, stk, false)
		}
		if getTestDeps {
			// Process test dependencies when -t is specified.
			// (Don't get test dependencies for test dependencies.)
			for _, path := range p.TestImports {
				download(path, stk, false)
			}
			for _, path := range p.XTestImports {
				download(path, stk, false)
			}
		}
	}
}

// downloadPackage runs the create or download command
// to make the first copy of or update a copy of the given package.
func downloadPackage(p *Package) error {
	var (
		vcs            *vcsCmd
		repo, rootPath string
		err            error
	)
	if p.build.SrcRoot != "" {
		// Directory exists.  Look for checkout along path to src.
		vcs, rootPath, err = vcsForDir(p)
		if err != nil {
			return err
		}
		repo = "<local>" // should be unused; make distinctive
	} else {
		// Analyze the import path to determine the version control system,
		// repository, and the import path for the root of the repository.
		rr, err := repoRootForImportPath(p.ImportPath)
		if err != nil {
			return err
		}
		vcs, repo, rootPath = rr.vcs, rr.repo, rr.root
	}

	if p.build.SrcRoot == "" {
		// Package not found.  Put in first directory of $GOPATH.
		list := filepath.SplitList(buildContext.GOPATH)
		if len(list) == 0 {
			return fmt.Errorf("cannot download, $GOPATH not set. For more details see: go help gopath")
		}
		// Guard against people setting GOPATH=$GOROOT.
		if list[0] == goroot {
			return fmt.Errorf("cannot download, $GOPATH must not be set to $GOROOT. For more details see: go help gopath")
		}
		p.build.SrcRoot = filepath.Join(list[0], "src")
		p.build.PkgRoot = filepath.Join(list[0], "pkg")
	}
	root := filepath.Join(p.build.SrcRoot, rootPath)
	// If we've considered this repository already, don't do it again.
	if downloadRootCache[root] {
		return nil
	}
	downloadRootCache[root] = true

	if buildV {
		fmt.Fprintf(os.Stderr, "%s (download)\n", rootPath)
	}

	// Check that this is an appropriate place for the repo to be checked out.
	// The target directory must either not exist or have a repo checked out already.
	meta := filepath.Join(root, "."+vcs.cmd)
	st, err := os.Stat(meta)
	if err == nil && !st.IsDir() {
		return fmt.Errorf("%s exists but is not a directory", meta)
	}
	if err != nil {
		// Metadata directory does not exist.  Prepare to checkout new copy.
		// Some version control tools require the target directory not to exist.
		// We require that too, just to avoid stepping on existing work.
		if _, err := os.Stat(root); err == nil {
			return fmt.Errorf("%s exists but %s does not - stale checkout?", root, meta)
		}
		// Some version control tools require the parent of the target to exist.
		parent, _ := filepath.Split(root)
		if err = os.MkdirAll(parent, 0777); err != nil {
			return err
		}
		if err = vcs.create(root, repo); err != nil {
			return err
		}
	} else {
		// Metadata directory does exist; download incremental updates.
		if err = vcs.download(root); err != nil {
			return err
		}
	}

	if buildN {
		// Do not show tag sync in -n; it's noise more than anything,
		// and since we're not running commands, no tag will be found.
		// But avoid printing nothing.
		fmt.Fprintf(os.Stderr, "# cd %s; %s sync/update\n", root, vcs.cmd)
		return nil
	}

	// Select and sync to appropriate version of the repository.
	tags, err := vcs.tags(root)
	if err != nil {
		return err
	}
	vers := runtime.Version()
	if i := strings.Index(vers, " "); i >= 0 {
		vers = vers[:i]
	}
	if err := vcs.tagSync(root, selectTag(vers, tags)); err != nil {
		return err
	}

	return nil
}

// goTag matches go release tags such as go1 and go1.2.3.
// The numbers involved must be small (at most 4 digits),
// have no unnecessary leading zeros, and the version cannot
// end in .0 - it is go1, not go1.0 or go1.0.0.
var goTag = regexp.MustCompile(
	`^go((0|[1-9][0-9]{0,3})\.)*([1-9][0-9]{0,3})$`,
)

// selectTag returns the closest matching tag for a given version.
// Closest means the latest one that is not after the current release.
// Version "goX" (or "goX.Y" or "goX.Y.Z") matches tags of the same form.
// Version "release.rN" matches tags of the form "go.rN" (N being a floating-point number).
// Version "weekly.YYYY-MM-DD" matches tags like "go.weekly.YYYY-MM-DD".
//
// NOTE(rsc): Eventually we will need to decide on some logic here.
// For now, there is only "go1".  This matches the docs in go help get.
func selectTag(goVersion string, tags []string) (match string) {
	for _, t := range tags {
		if t == "go1" {
			return "go1"
		}
	}
	return ""

	/*
		if goTag.MatchString(goVersion) {
			v := goVersion
			for _, t := range tags {
				if !goTag.MatchString(t) {
					continue
				}
				if cmpGoVersion(match, t) < 0 && cmpGoVersion(t, v) <= 0 {
					match = t
				}
			}
		}

		return match
	*/
}

// cmpGoVersion returns -1, 0, +1 reporting whether
// x < y, x == y, or x > y.
func cmpGoVersion(x, y string) int {
	// Malformed strings compare less than well-formed strings.
	if !goTag.MatchString(x) {
		return -1
	}
	if !goTag.MatchString(y) {
		return +1
	}

	// Compare numbers in sequence.
	xx := strings.Split(x[len("go"):], ".")
	yy := strings.Split(y[len("go"):], ".")

	for i := 0; i < len(xx) && i < len(yy); i++ {
		// The Atoi are guaranteed to succeed
		// because the versions match goTag.
		xi, _ := strconv.Atoi(xx[i])
		yi, _ := strconv.Atoi(yy[i])
		if xi < yi {
			return -1
		} else if xi > yi {
			return +1
		}
	}

	if len(xx) < len(yy) {
		return -1
	}
	if len(xx) > len(yy) {
		return +1
	}
	return 0
}
<commit_msg>cmd/go: report real package in errors for go get with wildcard<commit_after>// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// TODO: Dashboard upload

package main

import (
	"fmt"
	"go/build"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
)

var cmdGet = &Command{
	UsageLine: "get [-d] [-fix] [-t] [-u] [build flags] [packages]",
	Short:     "download and install packages and dependencies",
	Long: `
Get downloads and installs the packages named by the import paths,
along with their dependencies.

The -d flag instructs get to stop after downloading the packages; that is,
it instructs get not to install the packages.

The -fix flag instructs get to run the fix tool on the downloaded packages
before resolving dependencies or building the code.

The -t flag instructs get to also download the packages required to build
the tests for the specified packages.

The -u flag instructs get to use the network to update the named packages
and their dependencies.  By default, get uses the network to check out
missing packages but does not use it to look for updates to existing packages.

Get also accepts all the flags in the 'go build' and 'go install' commands,
to control the installation. See 'go help build'.

When checking out or updating a package, get looks for a branch or tag
that matches the locally installed version of Go. The most important
rule is that if the local installation is running version "go1", get
searches for a branch or tag named "go1". If no such version exists it
retrieves the most recent version of the package.

For more about specifying packages, see 'go help packages'.

For more about how 'go get' finds source code to
download, see 'go help remote'.

See also: go build, go install, go clean.
	`,
}

var getD = cmdGet.Flag.Bool("d", false, "")
var getT = cmdGet.Flag.Bool("t", false, "")
var getU = cmdGet.Flag.Bool("u", false, "")
var getFix = cmdGet.Flag.Bool("fix", false, "")

func init() {
	addBuildFlags(cmdGet)
	cmdGet.Run = runGet // break init loop
}

func runGet(cmd *Command, args []string) {
	// Phase 1.  Download/update.
	var stk importStack
	for _, arg := range downloadPaths(args) {
		download(arg, &stk, *getT)
	}
	exitIfErrors()

	// Phase 2. Rescan packages and reevaluate args list.

	// Code we downloaded and all code that depends on it
	// needs to be evicted from the package cache so that
	// the information will be recomputed.  Instead of keeping
	// track of the reverse dependency information, evict
	// everything.
	for name := range packageCache {
		delete(packageCache, name)
	}

	args = importPaths(args)

	// Phase 3.  Install.
	if *getD {
		// Download only.
		// Check delayed until now so that importPaths
		// has a chance to print errors.
		return
	}

	runInstall(cmd, args)
}

// downloadPaths prepares the list of paths to pass to download.
// It expands ... patterns that can be expanded.  If there is no match
// for a particular pattern, downloadPaths leaves it in the result list,
// in the hope that we can figure out the repository from the
// initial ...-free prefix.
func downloadPaths(args []string) []string {
	args = importPathsNoDotExpansion(args)
	var out []string
	for _, a := range args {
		if strings.Contains(a, "...") {
			var expand []string
			// Use matchPackagesInFS to avoid printing
			// warnings.  They will be printed by the
			// eventual call to importPaths instead.
			if build.IsLocalImport(a) {
				expand = matchPackagesInFS(a)
			} else {
				expand = matchPackages(a)
			}
			if len(expand) > 0 {
				out = append(out, expand...)
				continue
			}
		}
		out = append(out, a)
	}
	return out
}

// downloadCache records the import paths we have already
// considered during the download, to avoid duplicate work when
// there is more than one dependency sequence leading to
// a particular package.
var downloadCache = map[string]bool{}

// downloadRootCache records the version control repository
// root directories we have already considered during the download.
// For example, all the packages in the code.google.com/p/codesearch repo
// share the same root (the directory for that path), and we only need
// to run the hg commands to consider each repository once.
var downloadRootCache = map[string]bool{}

// download runs the download half of the get command
// for the package named by the argument.
func download(arg string, stk *importStack, getTestDeps bool) {
	p := loadPackage(arg, stk)

	// There's nothing to do if this is a package in the standard library.
	if p.Standard {
		return
	}

	// Only process each package once.
	if downloadCache[arg] {
		return
	}
	downloadCache[arg] = true

	pkgs := []*Package{p}
	wildcardOkay := len(*stk) == 0
	isWildcard := false

	// Download if the package is missing, or update if we're using -u.
	if p.Dir == "" || *getU {
		// The actual download.
		stk.push(p.ImportPath)
		err := downloadPackage(p)
		if err != nil {
			errorf("%s", &PackageError{ImportStack: stk.copy(), Err: err.Error()})
			stk.pop()
			return
		}

		args := []string{arg}
		// If the argument has a wildcard in it, re-evaluate the wildcard.
		// We delay this until after reloadPackage so that the old entry
		// for p has been replaced in the package cache.
		if wildcardOkay && strings.Contains(arg, "...") {
			if build.IsLocalImport(arg) {
				args = matchPackagesInFS(arg)
			} else {
				args = matchPackages(arg)
			}
			isWildcard = true
		}

		// Clear all relevant package cache entries before
		// doing any new loads.
		for _, arg := range args {
			p := packageCache[arg]
			if p != nil {
				delete(packageCache, p.Dir)
				delete(packageCache, p.ImportPath)
			}
		}

		pkgs = pkgs[:0]
		for _, arg := range args {
			stk.push(arg)
			p := loadPackage(arg, stk)
			stk.pop()
			if p.Error != nil {
				errorf("%s", p.Error)
				continue
			}
			pkgs = append(pkgs, p)
		}
	}

	// Process package, which might now be multiple packages
	// due to wildcard expansion.
	for _, p := range pkgs {
		if *getFix {
			run(stringList(tool("fix"), relPaths(p.allgofiles)))

			// The imports might have changed, so reload again.
			p = reloadPackage(arg, stk)
			if p.Error != nil {
				errorf("%s", p.Error)
				return
			}
		}

		if isWildcard {
			// Report both the real package and the
			// wildcard in any error message.
			stk.push(p.ImportPath)
		}

		// Process dependencies, now that we know what they are.
		for _, dep := range p.deps {
			// Don't get test dependencies recursively.
			download(dep.ImportPath, stk, false)
		}
		if getTestDeps {
			// Process test dependencies when -t is specified.
			// (Don't get test dependencies for test dependencies.)
			for _, path := range p.TestImports {
				download(path, stk, false)
			}
			for _, path := range p.XTestImports {
				download(path, stk, false)
			}
		}

		if isWildcard {
			stk.pop()
		}
	}
}

// downloadPackage runs the create or download command
// to make the first copy of or update a copy of the given package.
func downloadPackage(p *Package) error {
	var (
		vcs            *vcsCmd
		repo, rootPath string
		err            error
	)
	if p.build.SrcRoot != "" {
		// Directory exists.  Look for checkout along path to src.
		vcs, rootPath, err = vcsForDir(p)
		if err != nil {
			return err
		}
		repo = "<local>" // should be unused; make distinctive
	} else {
		// Analyze the import path to determine the version control system,
		// repository, and the import path for the root of the repository.
		rr, err := repoRootForImportPath(p.ImportPath)
		if err != nil {
			return err
		}
		vcs, repo, rootPath = rr.vcs, rr.repo, rr.root
	}

	if p.build.SrcRoot == "" {
		// Package not found.  Put in first directory of $GOPATH.
		list := filepath.SplitList(buildContext.GOPATH)
		if len(list) == 0 {
			return fmt.Errorf("cannot download, $GOPATH not set. For more details see: go help gopath")
		}
		// Guard against people setting GOPATH=$GOROOT.
		if list[0] == goroot {
			return fmt.Errorf("cannot download, $GOPATH must not be set to $GOROOT. For more details see: go help gopath")
		}
		p.build.SrcRoot = filepath.Join(list[0], "src")
		p.build.PkgRoot = filepath.Join(list[0], "pkg")
	}
	root := filepath.Join(p.build.SrcRoot, rootPath)
	// If we've considered this repository already, don't do it again.
	if downloadRootCache[root] {
		return nil
	}
	downloadRootCache[root] = true

	if buildV {
		fmt.Fprintf(os.Stderr, "%s (download)\n", rootPath)
	}

	// Check that this is an appropriate place for the repo to be checked out.
	// The target directory must either not exist or have a repo checked out already.
	meta := filepath.Join(root, "."+vcs.cmd)
	st, err := os.Stat(meta)
	if err == nil && !st.IsDir() {
		return fmt.Errorf("%s exists but is not a directory", meta)
	}
	if err != nil {
		// Metadata directory does not exist.  Prepare to checkout new copy.
		// Some version control tools require the target directory not to exist.
		// We require that too, just to avoid stepping on existing work.
		if _, err := os.Stat(root); err == nil {
			return fmt.Errorf("%s exists but %s does not - stale checkout?", root, meta)
		}
		// Some version control tools require the parent of the target to exist.
		parent, _ := filepath.Split(root)
		if err = os.MkdirAll(parent, 0777); err != nil {
			return err
		}
		if err = vcs.create(root, repo); err != nil {
			return err
		}
	} else {
		// Metadata directory does exist; download incremental updates.
		if err = vcs.download(root); err != nil {
			return err
		}
	}

	if buildN {
		// Do not show tag sync in -n; it's noise more than anything,
		// and since we're not running commands, no tag will be found.
		// But avoid printing nothing.
		fmt.Fprintf(os.Stderr, "# cd %s; %s sync/update\n", root, vcs.cmd)
		return nil
	}

	// Select and sync to appropriate version of the repository.
	tags, err := vcs.tags(root)
	if err != nil {
		return err
	}
	vers := runtime.Version()
	if i := strings.Index(vers, " "); i >= 0 {
		vers = vers[:i]
	}
	if err := vcs.tagSync(root, selectTag(vers, tags)); err != nil {
		return err
	}

	return nil
}

// goTag matches go release tags such as go1 and go1.2.3.
// The numbers involved must be small (at most 4 digits),
// have no unnecessary leading zeros, and the version cannot
// end in .0 - it is go1, not go1.0 or go1.0.0.
var goTag = regexp.MustCompile(
	`^go((0|[1-9][0-9]{0,3})\.)*([1-9][0-9]{0,3})$`,
)

// selectTag returns the closest matching tag for a given version.
// Closest means the latest one that is not after the current release.
// Version "goX" (or "goX.Y" or "goX.Y.Z") matches tags of the same form.
// Version "release.rN" matches tags of the form "go.rN" (N being a floating-point number).
// Version "weekly.YYYY-MM-DD" matches tags like "go.weekly.YYYY-MM-DD".
//
// NOTE(rsc): Eventually we will need to decide on some logic here.
// For now, there is only "go1".  This matches the docs in go help get.
func selectTag(goVersion string, tags []string) (match string) {
	for _, t := range tags {
		if t == "go1" {
			return "go1"
		}
	}
	return ""

	/*
		if goTag.MatchString(goVersion) {
			v := goVersion
			for _, t := range tags {
				if !goTag.MatchString(t) {
					continue
				}
				if cmpGoVersion(match, t) < 0 && cmpGoVersion(t, v) <= 0 {
					match = t
				}
			}
		}

		return match
	*/
}

// cmpGoVersion returns -1, 0, +1 reporting whether
// x < y, x == y, or x > y.
func cmpGoVersion(x, y string) int {
	// Malformed strings compare less than well-formed strings.
	if !goTag.MatchString(x) {
		return -1
	}
	if !goTag.MatchString(y) {
		return +1
	}

	// Compare numbers in sequence.
	xx := strings.Split(x[len("go"):], ".")
	yy := strings.Split(y[len("go"):], ".")

	for i := 0; i < len(xx) && i < len(yy); i++ {
		// The Atoi are guaranteed to succeed
		// because the versions match goTag.
		xi, _ := strconv.Atoi(xx[i])
		yi, _ := strconv.Atoi(yy[i])
		if xi < yi {
			return -1
		} else if xi > yi {
			return +1
		}
	}

	if len(xx) < len(yy) {
		return -1
	}
	if len(xx) > len(yy) {
		return +1
	}
	return 0
}
<|endoftext|>


<commit_before>package reform_test

import (
	"errors"
	"time"

	"github.com/AlekSi/pointer"
	"github.com/enodata/faker"

	"gopkg.in/reform.v1"
	"gopkg.in/reform.v1/dialects/postgresql"
	. "gopkg.in/reform.v1/internal/test/models"
)

func (s *ReformSuite) TestInsert() {
	newEmail := faker.Internet().Email()
	person := &Person{Email: &newEmail}
	err := s.q.Insert(person)
	s.NoError(err)
	s.NotEqual(int32(0), person.ID)
	s.Equal("", person.Name)
	s.Equal(&newEmail, person.Email)
	s.WithinDuration(time.Now(), person.CreatedAt, 2*time.Second)
	s.Nil(person.UpdatedAt)

	person2, err := s.q.FindByPrimaryKeyFrom(PersonTable, person.ID)
	s.NoError(err)
	s.Equal(person, person2)

	err = s.q.Insert(person)
	s.Error(err)
}

func (s *ReformSuite) TestInsertWithValues() {
	t := time.Now()
	newEmail := faker.Internet().Email()
	person := &Person{Email: &newEmail, CreatedAt: t, UpdatedAt: &t}
	err := s.q.Insert(person)
	s.NoError(err)
	s.NotEqual(int32(0), person.ID)
	s.Equal("", person.Name)
	s.Equal(&newEmail, person.Email)
	s.WithinDuration(t, person.CreatedAt, 2*time.Second)
	s.WithinDuration(t, *person.UpdatedAt, 2*time.Second)

	person2, err := s.q.FindByPrimaryKeyFrom(PersonTable, person.ID)
	s.NoError(err)
	s.Equal(person, person2)

	err = s.q.Insert(person)
	s.Error(err)
}

func (s *ReformSuite) TestInsertWithPrimaryKey() {
	setIdentityInsert(s.T(), s.q, "people", true)

	newEmail := faker.Internet().Email()
	person := &Person{ID: 50, Email: &newEmail}
	err := s.q.Insert(person)
	s.NoError(err)
	s.Equal(int32(50), person.ID)
	s.Equal("", person.Name)
	s.Equal(&newEmail, person.Email)
	s.WithinDuration(time.Now(), person.CreatedAt, 2*time.Second)
	s.Nil(person.UpdatedAt)

	person2, err := s.q.FindByPrimaryKeyFrom(PersonTable, person.ID)
	s.NoError(err)
	s.Equal(person, person2)

	err = s.q.Insert(person)
	s.Error(err)
}

func (s *ReformSuite) TestInsertReturning() {
	if s.q.Dialect != postgresql.Dialect {
		s.T().Skip("only PostgreSQL supports RETURNING syntax, other dialects support only integers from LastInsertId")
	}

	project := &Project{ID: "new", End: pointer.ToTime(time.Now().Truncate(24 * time.Hour))}
	err := s.q.Insert(project)
	s.NoError(err)
	s.Equal("new", project.ID)

	project2, err := s.q.FindByPrimaryKeyFrom(ProjectTable, project.ID)
	s.NoError(err)
	s.Equal(project, project2)

	err = s.q.Insert(project)
	s.Error(err)
}

func (s *ReformSuite) TestInsertIntoView() {
	pp := &PersonProject{PersonID: 1, ProjectID: "baron"}
	err := s.q.Insert(pp)
	s.NoError(err)

	err = s.q.Insert(pp)
	s.Error(err)

	s.RestartTransaction()

	pp = &PersonProject{PersonID: 1, ProjectID: "no_such_project"}
	err = s.q.Insert(pp)
	s.Error(err)
}

func (s *ReformSuite) TestInsertColumns() {
	t := time.Now()
	newEmail := faker.Internet().Email()
	person := &Person{Email: &newEmail, CreatedAt: t, UpdatedAt: &t}
	err := s.q.InsertColumns(person, "name", "email", "created_at", "updated_at")
	s.NoError(err)
	s.NotEqual(int32(0), person.ID)
	s.Equal("", person.Name)
	s.Equal((*int32)(nil), person.GroupID)
	s.Equal(&newEmail, person.Email)
	s.WithinDuration(t, person.CreatedAt, 2*time.Second)
	s.WithinDuration(t, *person.UpdatedAt, 2*time.Second)

	person2, err := s.q.FindByPrimaryKeyFrom(PersonTable, person.ID)
	s.NoError(err)
	person.GroupID = pointer.ToInt32(65534)
	s.Equal(person, person2)

	err = s.q.Insert(person)
	s.Error(err)
}

func (s *ReformSuite) TestInsertMulti() {
	newEmail := faker.Internet().Email()
	newName := faker.Name().Name()
	person1, person2 := &Person{Email: &newEmail}, &Person{Name: newName}
	err := s.q.InsertMulti(person1, person2)
	s.NoError(err)

	s.Equal(int32(0), person1.ID)
	s.Equal("", person1.Name)
	s.Equal(&newEmail, person1.Email)
	s.WithinDuration(time.Now(), person1.CreatedAt, 2*time.Second)
	s.Nil(person1.UpdatedAt)

	s.Equal(int32(0), person2.ID)
	s.Equal(newName, person2.Name)
	s.Nil(person2.Email)
	s.WithinDuration(time.Now(), person2.CreatedAt, 2*time.Second)
	s.Nil(person2.UpdatedAt)
}

func (s *ReformSuite) TestInsertMultiWithPrimaryKeys() {
	setIdentityInsert(s.T(), s.q, "people", true)

	newEmail := faker.Internet().Email()
	newName := faker.Name().Name()
	person1, person2 := &Person{ID: 50, Email: &newEmail}, &Person{ID: 51, Name: newName}
	err := s.q.InsertMulti(person1, person2)
	s.NoError(err)

	s.Equal(int32(50), person1.ID)
	s.Equal("", person1.Name)
	s.Equal(&newEmail, person1.Email)
	s.WithinDuration(time.Now(), person1.CreatedAt, 2*time.Second)
	s.Nil(person1.UpdatedAt)

	s.Equal(int32(51), person2.ID)
	s.Equal(newName, person2.Name)
	s.Nil(person2.Email)
	s.WithinDuration(time.Now(), person2.CreatedAt, 2*time.Second)
	s.Nil(person2.UpdatedAt)

	person, err := s.q.FindByPrimaryKeyFrom(PersonTable, person1.ID)
	s.NoError(err)
	s.Equal(person1, person)

	person, err = s.q.FindByPrimaryKeyFrom(PersonTable, person2.ID)
	s.NoError(err)
	s.Equal(person2, person)
}

func (s *ReformSuite) TestInsertMultiMixes() {
	err := s.q.InsertMulti()
	s.NoError(err)

	err = s.q.InsertMulti(&Person{}, &Project{})
	s.Error(err)

	err = s.q.InsertMulti(&Person{ID: 1}, &Person{})
	s.Error(err)
}

func (s *ReformSuite) TestUpdate() {
	var person Person
	err := s.q.Update(&person)
	s.Equal(reform.ErrNoPK, err)

	person.ID = 99
	err = s.q.Update(&person)
	s.Equal(reform.ErrNoRows, err)

	err = s.q.FindByPrimaryKeyTo(&person, 102)
	s.NoError(err)

	person.Email = pointer.ToString(faker.Internet().Email())
	err = s.q.Update(&person)
	s.NoError(err)
	s.Equal(personCreated, person.CreatedAt)
	s.Require().NotNil(person.UpdatedAt)
	s.WithinDuration(time.Now(), *person.UpdatedAt, 2*time.Second)

	person2, err := s.q.FindByPrimaryKeyFrom(PersonTable, person.ID)
	s.NoError(err)
	s.Equal(&person, person2)
}

func (s *ReformSuite) TestUpdateOverwrite() {
	newEmail := faker.Internet().Email()
	person := Person{ID: 102, Email: pointer.ToString(newEmail)}
	err := s.q.Update(&person)
	s.NoError(err)

	var person2 Person
	err = s.q.FindByPrimaryKeyTo(&person2, person.ID)
	s.NoError(err)
	s.Equal("", person2.Name)
	s.Equal(&newEmail, person2.Email)
	s.WithinDuration(time.Now(), person2.CreatedAt, 2*time.Second)
	s.Require().NotNil(person2.UpdatedAt)
	s.WithinDuration(time.Now(), *person2.UpdatedAt, 2*time.Second)
}

func (s *ReformSuite) TestUpdateColumns() {
	newName := faker.Name().Name()
	newEmail := faker.Internet().Email()

	for p, columns := range map[*Person][]string{
		&Person{Name: "Elfrieda Abbott", Email: &newEmail}:                             {"email", "updated_at"},
		&Person{Name: newName, Email: pointer.ToString("elfrieda_abbott@example.org")}: {"name", "name", "updated_at"},
		&Person{Name: newName, Email: &newEmail}:                                       {"name", "email", "updated_at"},
	} {
		var person Person
		err := s.q.FindByPrimaryKeyTo(&person, 102)
		s.NoError(err)

		person.Name = p.Name
		person.Email = p.Email
		err = s.q.UpdateColumns(&person, columns...)
		s.NoError(err)
		s.Equal(personCreated, person.CreatedAt)
		s.Require().NotNil(person.UpdatedAt)
		s.WithinDuration(time.Now(), *person.UpdatedAt, 2*time.Second)

		person2, err := s.q.FindByPrimaryKeyFrom(PersonTable, person.ID)
		s.NoError(err)
		s.Equal(&person, person2)

		s.RestartTransaction()
	}

	person := &Person{ID: 102, Name: newName, Email: &newEmail, CreatedAt: personCreated}
	for e, columns := range map[error][]string{
		errors.New("reform: unexpected columns: [foo]"):     {"foo"},
		errors.New("reform: will not update PK column: id"): {"id"},
		errors.New("reform: nothing to update"):             {},
	} {
		err := s.q.UpdateColumns(person, columns...)
		s.Error(err)
		s.Equal(e, err)

		s.RestartTransaction()
	}
}

func (s *ReformSuite) TestSave() {
	newName := faker.Name().Name()
	person := &Person{Name: newName}
	err := s.q.Save(person)
	s.NoError(err)

	person2, err := s.q.FindByPrimaryKeyFrom(PersonTable, person.ID)
	s.Require().NoError(err)
	s.Equal(newName, person2.(*Person).Name)
	s.Nil(person2.(*Person).Email)
	s.Equal(person, person2)

	newEmail := faker.Internet().Email()
	person.Email = &newEmail
	err = s.q.Save(person)
	s.NoError(err)

	person2, err = s.q.FindByPrimaryKeyFrom(PersonTable, person.ID)
	s.Require().NoError(err)
	s.Equal(newName, person2.(*Person).Name)
	s.Equal(&newEmail, person2.(*Person).Email)
	s.Equal(person, person2)
}

func (s *ReformSuite) TestDelete() {
	person := &Person{ID: 1}
	err := s.q.Delete(person)
	s.NoError(err)
	err = s.q.Reload(person)
	s.Equal(reform.ErrNoRows, err)

	project := &Project{ID: "baron"}
	err = s.q.Delete(project)
	s.NoError(err)
	err = s.q.Reload(project)
	s.Equal(reform.ErrNoRows, err)

	project = &Project{}
	err = s.q.Delete(project)
	s.Equal(reform.ErrNoPK, err)

	project = &Project{ID: "no_such_project"}
	err = s.q.Delete(project)
	s.Equal(reform.ErrNoRows, err)
}

func (s *ReformSuite) TestDeleteFrom() {
	ra, err := s.q.DeleteFrom(PersonTable, "WHERE email IS NULL")
	s.NoError(err)
	s.Equal(uint(3), ra)

	ra, err = s.q.DeleteFrom(PersonTable, "WHERE email IS NULL")
	s.NoError(err)
	s.Equal(uint(0), ra)

	// -1 second for SQLite3, otherwise it also deletes queen itself ¯\_(ツ)_/¯
	ra, err = s.q.DeleteFrom(ProjectTable, "WHERE start < "+s.q.Placeholder(1), queenStart.Add(-time.Second))
	s.NoError(err)
	s.Equal(uint(3), ra)

	ra, err = s.q.DeleteFrom(ProjectTable, "")
	s.NoError(err)
	s.Equal(uint(2), ra)

	ra, err = s.q.DeleteFrom(ProjectTable, "WHERE invalid_tail")
	s.Error(err)
	s.Equal(uint(0), ra)
}

func (s *ReformSuite) TestCommandsSchema() {
	if s.q.Dialect != postgresql.Dialect {
		s.T().Skip("only PostgreSQL supports schemas")
	}

	legacyPerson := &LegacyPerson{Name: pointer.ToString(faker.Name().Name())}
	err := s.q.Save(legacyPerson)
	s.NoError(err)
	err = s.q.Save(legacyPerson)
	s.NoError(err)
	err = s.q.Delete(legacyPerson)
	s.NoError(err)
}
<commit_msg>Remove extra code.<commit_after>package reform_test

import (
	"errors"
	"time"

	"github.com/AlekSi/pointer"
	"github.com/enodata/faker"

	"gopkg.in/reform.v1"
	"gopkg.in/reform.v1/dialects/postgresql"
	. "gopkg.in/reform.v1/internal/test/models"
)

func (s *ReformSuite) TestInsert() {
	newEmail := faker.Internet().Email()
	person := &Person{Email: &newEmail}
	err := s.q.Insert(person)
	s.NoError(err)
	s.NotEqual(int32(0), person.ID)
	s.Equal("", person.Name)
	s.Equal(&newEmail, person.Email)
	s.WithinDuration(time.Now(), person.CreatedAt, 2*time.Second)
	s.Nil(person.UpdatedAt)

	person2, err := s.q.FindByPrimaryKeyFrom(PersonTable, person.ID)
	s.NoError(err)
	s.Equal(person, person2)

	err = s.q.Insert(person)
	s.Error(err)
}

func (s *ReformSuite) TestInsertWithValues() {
	t := time.Now()
	newEmail := faker.Internet().Email()
	person := &Person{Email: &newEmail, CreatedAt: t, UpdatedAt: &t}
	err := s.q.Insert(person)
	s.NoError(err)
	s.NotEqual(int32(0), person.ID)
	s.Equal("", person.Name)
	s.Equal(&newEmail, person.Email)
	s.WithinDuration(t, person.CreatedAt, 2*time.Second)
	s.WithinDuration(t, *person.UpdatedAt, 2*time.Second)

	person2, err := s.q.FindByPrimaryKeyFrom(PersonTable, person.ID)
	s.NoError(err)
	s.Equal(person, person2)

	err = s.q.Insert(person)
	s.Error(err)
}

func (s *ReformSuite) TestInsertWithPrimaryKey() {
	setIdentityInsert(s.T(), s.q, "people", true)

	newEmail := faker.Internet().Email()
	person := &Person{ID: 50, Email: &newEmail}
	err := s.q.Insert(person)
	s.NoError(err)
	s.Equal(int32(50), person.ID)
	s.Equal("", person.Name)
	s.Equal(&newEmail, person.Email)
	s.WithinDuration(time.Now(), person.CreatedAt, 2*time.Second)
	s.Nil(person.UpdatedAt)

	person2, err := s.q.FindByPrimaryKeyFrom(PersonTable, person.ID)
	s.NoError(err)
	s.Equal(person, person2)

	err = s.q.Insert(person)
	s.Error(err)
}

func (s *ReformSuite) TestInsertReturning() {
	if s.q.Dialect != postgresql.Dialect {
		s.T().Skip("only PostgreSQL supports RETURNING syntax, other dialects support only integers from LastInsertId")
	}

	project := &Project{ID: "new", End: pointer.ToTime(time.Now().Truncate(24 * time.Hour))}
	err := s.q.Insert(project)
	s.NoError(err)
	s.Equal("new", project.ID)

	project2, err := s.q.FindByPrimaryKeyFrom(ProjectTable, project.ID)
	s.NoError(err)
	s.Equal(project, project2)

	err = s.q.Insert(project)
	s.Error(err)
}

func (s *ReformSuite) TestInsertIntoView() {
	pp := &PersonProject{PersonID: 1, ProjectID: "baron"}
	err := s.q.Insert(pp)
	s.NoError(err)

	err = s.q.Insert(pp)
	s.Error(err)

	s.RestartTransaction()

	pp = &PersonProject{PersonID: 1, ProjectID: "no_such_project"}
	err = s.q.Insert(pp)
	s.Error(err)
}

func (s *ReformSuite) TestInsertColumns() {
	t := time.Now()
	newEmail := faker.Internet().Email()
	person := &Person{Email: &newEmail, CreatedAt: t, UpdatedAt: &t}
	err := s.q.InsertColumns(person, "name", "email", "created_at", "updated_at")
	s.NoError(err)
	s.NotEqual(int32(0), person.ID)
	s.Equal("", person.Name)
	s.Equal((*int32)(nil), person.GroupID)
	s.Equal(&newEmail, person.Email)
	s.WithinDuration(t, person.CreatedAt, 2*time.Second)
	s.WithinDuration(t, *person.UpdatedAt, 2*time.Second)

	person2, err := s.q.FindByPrimaryKeyFrom(PersonTable, person.ID)
	s.NoError(err)
	person.GroupID = pointer.ToInt32(65534)
	s.Equal(person, person2)

	err = s.q.Insert(person)
	s.Error(err)
}

func (s *ReformSuite) TestInsertMulti() {
	newEmail := faker.Internet().Email()
	newName := faker.Name().Name()
	person1, person2 := &Person{Email: &newEmail}, &Person{Name: newName}
	err := s.q.InsertMulti(person1, person2)
	s.NoError(err)

	s.Equal(int32(0), person1.ID)
	s.Equal("", person1.Name)
	s.Equal(&newEmail, person1.Email)
	s.WithinDuration(time.Now(), person1.CreatedAt, 2*time.Second)
	s.Nil(person1.UpdatedAt)

	s.Equal(int32(0), person2.ID)
	s.Equal(newName, person2.Name)
	s.Nil(person2.Email)
	s.WithinDuration(time.Now(), person2.CreatedAt, 2*time.Second)
	s.Nil(person2.UpdatedAt)
}

func (s *ReformSuite) TestInsertMultiWithPrimaryKeys() {
	setIdentityInsert(s.T(), s.q, "people", true)

	newEmail := faker.Internet().Email()
	newName := faker.Name().Name()
	person1, person2 := &Person{ID: 50, Email: &newEmail}, &Person{ID: 51, Name: newName}
	err := s.q.InsertMulti(person1, person2)
	s.NoError(err)

	s.Equal(int32(50), person1.ID)
	s.Equal("", person1.Name)
	s.Equal(&newEmail, person1.Email)
	s.WithinDuration(time.Now(), person1.CreatedAt, 2*time.Second)
	s.Nil(person1.UpdatedAt)

	s.Equal(int32(51), person2.ID)
	s.Equal(newName, person2.Name)
	s.Nil(person2.Email)
	s.WithinDuration(time.Now(), person2.CreatedAt, 2*time.Second)
	s.Nil(person2.UpdatedAt)

	person, err := s.q.FindByPrimaryKeyFrom(PersonTable, person1.ID)
	s.NoError(err)
	s.Equal(person1, person)

	person, err = s.q.FindByPrimaryKeyFrom(PersonTable, person2.ID)
	s.NoError(err)
	s.Equal(person2, person)
}

func (s *ReformSuite) TestInsertMultiMixes() {
	err := s.q.InsertMulti()
	s.NoError(err)

	err = s.q.InsertMulti(&Person{}, &Project{})
	s.Error(err)

	err = s.q.InsertMulti(&Person{ID: 1}, &Person{})
	s.Error(err)
}

func (s *ReformSuite) TestUpdate() {
	var person Person
	err := s.q.Update(&person)
	s.Equal(reform.ErrNoPK, err)

	person.ID = 99
	err = s.q.Update(&person)
	s.Equal(reform.ErrNoRows, err)

	err = s.q.FindByPrimaryKeyTo(&person, 102)
	s.NoError(err)

	person.Email = pointer.ToString(faker.Internet().Email())
	err = s.q.Update(&person)
	s.NoError(err)
	s.Equal(personCreated, person.CreatedAt)
	s.Require().NotNil(person.UpdatedAt)
	s.WithinDuration(time.Now(), *person.UpdatedAt, 2*time.Second)

	person2, err := s.q.FindByPrimaryKeyFrom(PersonTable, person.ID)
	s.NoError(err)
	s.Equal(&person, person2)
}

func (s *ReformSuite) TestUpdateOverwrite() {
	newEmail := faker.Internet().Email()
	person := Person{ID: 102, Email: pointer.ToString(newEmail)}
	err := s.q.Update(&person)
	s.NoError(err)

	var person2 Person
	err = s.q.FindByPrimaryKeyTo(&person2, person.ID)
	s.NoError(err)
	s.Equal("", person2.Name)
	s.Equal(&newEmail, person2.Email)
	s.WithinDuration(time.Now(), person2.CreatedAt, 2*time.Second)
	s.Require().NotNil(person2.UpdatedAt)
	s.WithinDuration(time.Now(), *person2.UpdatedAt, 2*time.Second)
}

func (s *ReformSuite) TestUpdateColumns() {
	newName := faker.Name().Name()
	newEmail := faker.Internet().Email()

	for p, columns := range map[*Person][]string{
		&Person{Name: "Elfrieda Abbott", Email: &newEmail}:                             {"email", "updated_at"},
		&Person{Name: newName, Email: pointer.ToString("elfrieda_abbott@example.org")}: {"name", "name", "updated_at"},
		&Person{Name: newName, Email: &newEmail}:                                       {"name", "email", "updated_at"},
	} {
		var person Person
		err := s.q.FindByPrimaryKeyTo(&person, 102)
		s.NoError(err)

		person.Name = p.Name
		person.Email = p.Email
		err = s.q.UpdateColumns(&person, columns...)
		s.NoError(err)
		s.Equal(personCreated, person.CreatedAt)
		s.Require().NotNil(person.UpdatedAt)
		s.WithinDuration(time.Now(), *person.UpdatedAt, 2*time.Second)

		person2, err := s.q.FindByPrimaryKeyFrom(PersonTable, person.ID)
		s.NoError(err)
		s.Equal(&person, person2)

		s.RestartTransaction()
	}

	person := &Person{ID: 102, Name: newName, Email: &newEmail, CreatedAt: personCreated}
	for e, columns := range map[error][]string{
		errors.New("reform: unexpected columns: [foo]"):     {"foo"},
		errors.New("reform: will not update PK column: id"): {"id"},
		errors.New("reform: nothing to update"):             {},
	} {
		err := s.q.UpdateColumns(person, columns...)
		s.Error(err)
		s.Equal(e, err)
	}
}

func (s *ReformSuite) TestSave() {
	newName := faker.Name().Name()
	person := &Person{Name: newName}
	err := s.q.Save(person)
	s.NoError(err)

	person2, err := s.q.FindByPrimaryKeyFrom(PersonTable, person.ID)
	s.Require().NoError(err)
	s.Equal(newName, person2.(*Person).Name)
	s.Nil(person2.(*Person).Email)
	s.Equal(person, person2)

	newEmail := faker.Internet().Email()
	person.Email = &newEmail
	err = s.q.Save(person)
	s.NoError(err)

	person2, err = s.q.FindByPrimaryKeyFrom(PersonTable, person.ID)
	s.Require().NoError(err)
	s.Equal(newName, person2.(*Person).Name)
	s.Equal(&newEmail, person2.(*Person).Email)
	s.Equal(person, person2)
}

func (s *ReformSuite) TestDelete() {
	person := &Person{ID: 1}
	err := s.q.Delete(person)
	s.NoError(err)
	err = s.q.Reload(person)
	s.Equal(reform.ErrNoRows, err)

	project := &Project{ID: "baron"}
	err = s.q.Delete(project)
	s.NoError(err)
	err = s.q.Reload(project)
	s.Equal(reform.ErrNoRows, err)

	project = &Project{}
	err = s.q.Delete(project)
	s.Equal(reform.ErrNoPK, err)

	project = &Project{ID: "no_such_project"}
	err = s.q.Delete(project)
	s.Equal(reform.ErrNoRows, err)
}

func (s *ReformSuite) TestDeleteFrom() {
	ra, err := s.q.DeleteFrom(PersonTable, "WHERE email IS NULL")
	s.NoError(err)
	s.Equal(uint(3), ra)

	ra, err = s.q.DeleteFrom(PersonTable, "WHERE email IS NULL")
	s.NoError(err)
	s.Equal(uint(0), ra)

	// -1 second for SQLite3, otherwise it also deletes queen itself ¯\_(ツ)_/¯
	ra, err = s.q.DeleteFrom(ProjectTable, "WHERE start < "+s.q.Placeholder(1), queenStart.Add(-time.Second))
	s.NoError(err)
	s.Equal(uint(3), ra)

	ra, err = s.q.DeleteFrom(ProjectTable, "")
	s.NoError(err)
	s.Equal(uint(2), ra)

	ra, err = s.q.DeleteFrom(ProjectTable, "WHERE invalid_tail")
	s.Error(err)
	s.Equal(uint(0), ra)
}

func (s *ReformSuite) TestCommandsSchema() {
	if s.q.Dialect != postgresql.Dialect {
		s.T().Skip("only PostgreSQL supports schemas")
	}

	legacyPerson := &LegacyPerson{Name: pointer.ToString(faker.Name().Name())}
	err := s.q.Save(legacyPerson)
	s.NoError(err)
	err = s.q.Save(legacyPerson)
	s.NoError(err)
	err = s.q.Delete(legacyPerson)
	s.NoError(err)
}
<|endoftext|>


<commit_before>package util

import (
	"fmt"
)

var (
	VERSION_NUMBER = fmt.Sprintf("%.02f", 2.65)
	VERSION        = sizeLimit + VERSION_NUMBER
	COMMIT         = ""
)

func Version() string {
	return VERSION + " " + COMMIT
}
<commit_msg>format<commit_after>package util

import (
	"fmt"
)

var (
	VERSION_NUMBER = fmt.Sprintf("%.02f", 2.65)
	VERSION        = sizeLimit + " " + VERSION_NUMBER
	COMMIT         = ""
)

func Version() string {
	return VERSION + " " + COMMIT
}
<|endoftext|>


<commit_before>// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

func init() {
	addBuildFlagsNX(cmdVet)
}

var cmdVet = &Command{
	Run:       runVet,
	UsageLine: "vet [-n] [-x] [packages]",
	Short:     "run go tool vet on packages",
	Long: `
Vet runs the Go vet command on the packages named by the import paths.

For more about vet, see 'godoc vet'.
For more about specifying packages, see 'go help packages'.

To run the vet tool with specific options, run 'go tool vet'.

The -n flag prints commands that would be executed.
The -x flag prints commands as they are executed.

See also: go fmt, go fix.
	`,
}

func runVet(cmd *Command, args []string) {
	for _, pkg := range packages(args) {
		// Use pkg.gofiles instead of pkg.Dir so that
		// the command only applies to this package,
		// not to packages in subdirectories.
		run(tool("vet"), relPaths(pkg.allgofiles))
	}
}
<commit_msg>cmd/go: fix vet<commit_after>// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

func init() {
	addBuildFlagsNX(cmdVet)
}

var cmdVet = &Command{
	Run:       runVet,
	UsageLine: "vet [-n] [-x] [packages]",
	Short:     "run go tool vet on packages",
	Long: `
Vet runs the Go vet command on the packages named by the import paths.

For more about vet, see 'godoc vet'.
For more about specifying packages, see 'go help packages'.

To run the vet tool with specific options, run 'go tool vet'.

The -n flag prints commands that would be executed.
The -x flag prints commands as they are executed.

See also: go fmt, go fix.
	`,
}

func runVet(cmd *Command, args []string) {
	for _, pkg := range packages(args) {
		// Use pkg.gofiles instead of pkg.Dir so that
		// the command only applies to this package,
		// not to packages in subdirectories.
		run(tool("vet"), relPaths(pkg.gofiles))
	}
}
<|endoftext|>


<commit_before>package runner

import (
	"fmt"
	"net/url"
	"strings"
	"sync"
	"time"

	"github.com/influxdb/influxdb/client"
)

// Timer is struct that can be used to track elaspsed time
type Timer struct {
	start time.Time
	end   time.Time
}

// Start returns a Timers start field
func (t *Timer) Start() time.Time {
	return t.start
}

// End returns a Timers end field
func (t *Timer) End() time.Time {
	return t.end
}

// StartTimer sets a timers `start` field to the current time
func (t *Timer) StartTimer() {
	t.start = time.Now()
}

// StopTimer sets a timers `end` field to the current time
func (t *Timer) StopTimer() {
	t.end = time.Now()
}

// Elapsed returns the total elapsed time between the `start`
// and `end` fields on a timer.
func (t *Timer) Elapsed() time.Duration {
	return t.end.Sub(t.start)
}

// NewTimer returns a pointer to a `Timer` struct where the
// timers `start` field has been set to `time.Now()`
func NewTimer() *Timer {
	t := &Timer{}
	t.StartTimer()
	return t
}

// ResponseTime is a struct that contains `Value`
// `Time` pairing.
type ResponseTime struct {
	Value int
	Time  time.Time
}

// newResponseTime returns a new response time
// with value `v` and time `time.Now()`.
func NewResponseTime(v int) ResponseTime {
	r := ResponseTime{Value: v, Time: time.Now()}
	return r
}

type ResponseTimes []ResponseTime

// Implements the `Len` method for the
// sort.Interface type
func (rs ResponseTimes) Len() int {
	return len(rs)
}

// Implements the `Less` method for the
// sort.Interface type
func (rs ResponseTimes) Less(i, j int) bool {
	return rs[i].Value < rs[j].Value
}

// Implements the `Swap` method for the
// sort.Interface type
func (rs ResponseTimes) Swap(i, j int) {
	rs[i], rs[j] = rs[j], rs[i]
}

type Measurements []string

// String returns a string and implements the `String` method for
// the flag.Value interface.
func (ms *Measurements) String() string {
	return fmt.Sprint(*ms)
}

// Set implements the `Set` method for the flag.Value
// interface. Set splits a string of comma separated values
// into a `Measurement`.
func (ms *Measurements) Set(value string) error {
	values := strings.Split(value, ",")
	for _, m := range values {
		*ms = append(*ms, m)
	}
	return nil
}

// newClient returns a pointer to an InfluxDB client for
// a `Config`'s `Address` field. If an error is encountered
// when creating a new client, the function panics.
func (cfg *Config) NewClient() (*client.Client, error) {
	u, _ := url.Parse(fmt.Sprintf("http://%s", cfg.Write.Address))
	c, err := client.NewClient(client.Config{URL: *u})
	if err != nil {
		return nil, err
	}
	return c, nil
}

func resetDB(c *client.Client, database string) error {
	_, err := c.Query(client.Query{
		Command: fmt.Sprintf("DROP DATABASE %s", database),
	})

	if err != nil && !strings.Contains(err.Error(), "database not found") {
		return err
	}

	return nil
}

// Run runs the stress test that is specified by a `Config`.
// It returns the total number of points that were during the test,
// an slice of all of the stress tests response times,
// and the times that the test started at and ended as a `Timer`
func Run(cfg *Config, done chan struct{}, ts chan time.Time) (totalPoints int, failedRequests int, responseTimes ResponseTimes, timer *Timer) {

	c, err := cfg.NewClient()
	if err != nil {
		panic(err)
	}

	if cfg.Write.ResetDatabase {
		resetDB(c, cfg.Write.Database)
	}

	_, err = c.Query(client.Query{
		Command: fmt.Sprintf("CREATE DATABASE %s", cfg.Write.Database),
	})

	if err != nil && !strings.Contains(err.Error(), "database already exists") {
		fmt.Println(err)
	}

	counter := NewConcurrencyLimiter(cfg.Write.Concurrency)

	var mu sync.Mutex
	var wg sync.WaitGroup
	responseTimes = make(ResponseTimes, 0)

	failedRequests = 0

	totalPoints = 0

	lastSuccess := true

	ch := make(chan []client.Point, 100)

	go func() {
		points := []client.Point{}
		num := 0
		for _, s := range cfg.Series {
			num += s.PointCount * s.SeriesCount

		}

		if cfg.MeasurementQuery.Enabled {
			num = num / (len(cfg.Series) * len(cfg.MeasurementQuery.Aggregates) * len(cfg.MeasurementQuery.Fields))
		}

		ctr := 0
		for _, testSeries := range cfg.Series {
			for i := 0; i < testSeries.PointCount; i++ {
				iter := testSeries.Iter(cfg.Write.StartingPoint, i)
				p, ok := iter.Next()
				for ok {
					ctr++
					points = append(points, p)
					if len(points) >= cfg.Write.BatchSize {
						ch <- points
						points = []client.Point{}
					}

					if cfg.MeasurementQuery.Enabled && ctr%num == 0 {
						select {
						case ts <- p.Time:
							func() {}()
						default:
							func() {}()
						}
					}

					p, ok = iter.Next()
				}
			}
		}

		close(ch)

	}()

	timer = NewTimer()
	defer timer.StopTimer()

	for pnt := range ch {
		batch := &client.BatchPoints{
			Database:         cfg.Write.Database,
			WriteConsistency: "any",
			Time:             time.Now(),
			Precision:        cfg.Write.Precision,
			Points:           pnt,
		}

		wg.Add(1)
		counter.Increment()
		totalPoints += len(batch.Points)

		go func(b *client.BatchPoints, total int) {
			st := time.Now()
			if _, err := c.Write(*b); err != nil { // Should retry write if failed
				mu.Lock()
				if lastSuccess {
					fmt.Println("ERROR: ", err.Error())
				}
				failedRequests += 1
				totalPoints -= len(b.Points)
				lastSuccess = false
				mu.Unlock()
			} else {
				mu.Lock()
				if !lastSuccess {
					fmt.Println("success in ", time.Since(st))
				}
				lastSuccess = true
				responseTimes = append(responseTimes, NewResponseTime(int(time.Since(st).Nanoseconds())))
				mu.Unlock()
			}
			batchInterval, _ := time.ParseDuration(cfg.Write.BatchInterval)
			time.Sleep(batchInterval)
			wg.Done()
			counter.Decrement()
			if total%500000 == 0 {
				fmt.Printf("%d total points. %d in %s\n", total, cfg.Write.BatchSize, time.Since(st))
			}
		}(batch, totalPoints)

	}

	wg.Wait()

	if cfg.SeriesQuery.Enabled {
		done <- struct{}{}
	}

	return
}

// ConcurrencyLimiter is a go routine safe struct that can be used to
// ensure that no more than a specifid max number of goroutines are
// executing.
type ConcurrencyLimiter struct {
	inc   chan chan struct{}
	dec   chan struct{}
	max   int
	count int
}

// NewConcurrencyLimiter returns a configured limiter that will
// ensure that calls to Increment will block if the max is hit.
func NewConcurrencyLimiter(max int) *ConcurrencyLimiter {
	c := &ConcurrencyLimiter{
		inc: make(chan chan struct{}),
		dec: make(chan struct{}, max),
		max: max,
	}
	go c.handleLimits()
	return c
}

// Increment will increase the count of running goroutines by 1.
// if the number is currently at the max, the call to Increment
// will block until another goroutine decrements.
func (c *ConcurrencyLimiter) Increment() {
	r := make(chan struct{})
	c.inc <- r
	<-r
}

// Decrement will reduce the count of running goroutines by 1
func (c *ConcurrencyLimiter) Decrement() {
	c.dec <- struct{}{}
}

// handleLimits runs in a goroutine to manage the count of
// running goroutines.
func (c *ConcurrencyLimiter) handleLimits() {
	for {
		r := <-c.inc
		if c.count >= c.max {
			<-c.dec
			c.count--
		}
		c.count++
		r <- struct{}{}
	}
}
<commit_msg>Increase buffer size for points<commit_after>package runner

import (
	"fmt"
	"net/url"
	"strings"
	"sync"
	"time"

	"github.com/influxdb/influxdb/client"
)

// Timer is struct that can be used to track elaspsed time
type Timer struct {
	start time.Time
	end   time.Time
}

// Start returns a Timers start field
func (t *Timer) Start() time.Time {
	return t.start
}

// End returns a Timers end field
func (t *Timer) End() time.Time {
	return t.end
}

// StartTimer sets a timers `start` field to the current time
func (t *Timer) StartTimer() {
	t.start = time.Now()
}

// StopTimer sets a timers `end` field to the current time
func (t *Timer) StopTimer() {
	t.end = time.Now()
}

// Elapsed returns the total elapsed time between the `start`
// and `end` fields on a timer.
func (t *Timer) Elapsed() time.Duration {
	return t.end.Sub(t.start)
}

// NewTimer returns a pointer to a `Timer` struct where the
// timers `start` field has been set to `time.Now()`
func NewTimer() *Timer {
	t := &Timer{}
	t.StartTimer()
	return t
}

// ResponseTime is a struct that contains `Value`
// `Time` pairing.
type ResponseTime struct {
	Value int
	Time  time.Time
}

// newResponseTime returns a new response time
// with value `v` and time `time.Now()`.
func NewResponseTime(v int) ResponseTime {
	r := ResponseTime{Value: v, Time: time.Now()}
	return r
}

type ResponseTimes []ResponseTime

// Implements the `Len` method for the
// sort.Interface type
func (rs ResponseTimes) Len() int {
	return len(rs)
}

// Implements the `Less` method for the
// sort.Interface type
func (rs ResponseTimes) Less(i, j int) bool {
	return rs[i].Value < rs[j].Value
}

// Implements the `Swap` method for the
// sort.Interface type
func (rs ResponseTimes) Swap(i, j int) {
	rs[i], rs[j] = rs[j], rs[i]
}

type Measurements []string

// String returns a string and implements the `String` method for
// the flag.Value interface.
func (ms *Measurements) String() string {
	return fmt.Sprint(*ms)
}

// Set implements the `Set` method for the flag.Value
// interface. Set splits a string of comma separated values
// into a `Measurement`.
func (ms *Measurements) Set(value string) error {
	values := strings.Split(value, ",")
	for _, m := range values {
		*ms = append(*ms, m)
	}
	return nil
}

// newClient returns a pointer to an InfluxDB client for
// a `Config`'s `Address` field. If an error is encountered
// when creating a new client, the function panics.
func (cfg *Config) NewClient() (*client.Client, error) {
	u, _ := url.Parse(fmt.Sprintf("http://%s", cfg.Write.Address))
	c, err := client.NewClient(client.Config{URL: *u})
	if err != nil {
		return nil, err
	}
	return c, nil
}

func resetDB(c *client.Client, database string) error {
	_, err := c.Query(client.Query{
		Command: fmt.Sprintf("DROP DATABASE %s", database),
	})

	if err != nil && !strings.Contains(err.Error(), "database not found") {
		return err
	}

	return nil
}

// Run runs the stress test that is specified by a `Config`.
// It returns the total number of points that were during the test,
// an slice of all of the stress tests response times,
// and the times that the test started at and ended as a `Timer`
func Run(cfg *Config, done chan struct{}, ts chan time.Time) (totalPoints int, failedRequests int, responseTimes ResponseTimes, timer *Timer) {

	c, err := cfg.NewClient()
	if err != nil {
		panic(err)
	}

	if cfg.Write.ResetDatabase {
		resetDB(c, cfg.Write.Database)
	}

	_, err = c.Query(client.Query{
		Command: fmt.Sprintf("CREATE DATABASE %s", cfg.Write.Database),
	})

	if err != nil && !strings.Contains(err.Error(), "database already exists") {
		fmt.Println(err)
	}

	counter := NewConcurrencyLimiter(cfg.Write.Concurrency)

	var mu sync.Mutex
	var wg sync.WaitGroup
	responseTimes = make(ResponseTimes, 0)

	failedRequests = 0

	totalPoints = 0

	lastSuccess := true

	ch := make(chan []client.Point, 10000)

	go func() {
		points := []client.Point{}
		num := 0
		for _, s := range cfg.Series {
			num += s.PointCount * s.SeriesCount

		}

		if cfg.MeasurementQuery.Enabled {
			num = num / (len(cfg.Series) * len(cfg.MeasurementQuery.Aggregates) * len(cfg.MeasurementQuery.Fields))
		}

		ctr := 0
		for _, testSeries := range cfg.Series {
			for i := 0; i < testSeries.PointCount; i++ {
				iter := testSeries.Iter(cfg.Write.StartingPoint, i)
				p, ok := iter.Next()
				for ok {
					ctr++
					points = append(points, p)
					if len(points) >= cfg.Write.BatchSize {
						ch <- points
						points = []client.Point{}
					}

					if cfg.MeasurementQuery.Enabled && ctr%num == 0 {
						select {
						case ts <- p.Time:
							func() {}()
						default:
							func() {}()
						}
					}

					p, ok = iter.Next()
				}
			}
		}

		close(ch)

	}()

	timer = NewTimer()
	defer timer.StopTimer()

	for pnt := range ch {
		batch := &client.BatchPoints{
			Database:         cfg.Write.Database,
			WriteConsistency: "any",
			Time:             time.Now(),
			Precision:        cfg.Write.Precision,
			Points:           pnt,
		}

		wg.Add(1)
		counter.Increment()
		totalPoints += len(batch.Points)

		go func(b *client.BatchPoints, total int) {
			st := time.Now()
			if _, err := c.Write(*b); err != nil { // Should retry write if failed
				mu.Lock()
				if lastSuccess {
					fmt.Println("ERROR: ", err.Error())
				}
				failedRequests += 1
				totalPoints -= len(b.Points)
				lastSuccess = false
				mu.Unlock()
			} else {
				mu.Lock()
				if !lastSuccess {
					fmt.Println("success in ", time.Since(st))
				}
				lastSuccess = true
				responseTimes = append(responseTimes, NewResponseTime(int(time.Since(st).Nanoseconds())))
				mu.Unlock()
			}
			batchInterval, _ := time.ParseDuration(cfg.Write.BatchInterval)
			time.Sleep(batchInterval)
			wg.Done()
			counter.Decrement()
			if total%500000 == 0 {
				fmt.Printf("%d total points. %d in %s\n", total, cfg.Write.BatchSize, time.Since(st))
			}
		}(batch, totalPoints)

	}

	wg.Wait()

	if cfg.SeriesQuery.Enabled {
		done <- struct{}{}
	}

	return
}

// ConcurrencyLimiter is a go routine safe struct that can be used to
// ensure that no more than a specifid max number of goroutines are
// executing.
type ConcurrencyLimiter struct {
	inc   chan chan struct{}
	dec   chan struct{}
	max   int
	count int
}

// NewConcurrencyLimiter returns a configured limiter that will
// ensure that calls to Increment will block if the max is hit.
func NewConcurrencyLimiter(max int) *ConcurrencyLimiter {
	c := &ConcurrencyLimiter{
		inc: make(chan chan struct{}),
		dec: make(chan struct{}, max),
		max: max,
	}
	go c.handleLimits()
	return c
}

// Increment will increase the count of running goroutines by 1.
// if the number is currently at the max, the call to Increment
// will block until another goroutine decrements.
func (c *ConcurrencyLimiter) Increment() {
	r := make(chan struct{})
	c.inc <- r
	<-r
}

// Decrement will reduce the count of running goroutines by 1
func (c *ConcurrencyLimiter) Decrement() {
	c.dec <- struct{}{}
}

// handleLimits runs in a goroutine to manage the count of
// running goroutines.
func (c *ConcurrencyLimiter) handleLimits() {
	for {
		r := <-c.inc
		if c.count >= c.max {
			<-c.dec
			c.count--
		}
		c.count++
		r <- struct{}{}
	}
}
<|endoftext|>


<commit_before>package stripe

// BankAccountParams hold all of the parameters used for creating and updating
// BankAccounts.
type BankAccountParams struct {
	Country       string `stripe_field:"bank_account[country]"`
	RoutingNumber string `stripe_field:"bank_account[routing_number]"`
	AccountNumber string `stripe_field:"bank_account[account_number]"`
}

// CardParams hold all of the parameters used for creating and updating Cards.
type CardParams struct {
	Number         string
	ExpMonth       int
	ExpYear        int
	CVC            string
	Name           string
	AddressLine1   string
	AddressLine2   string
	AddressCity    string
	AddressZip     string
	AddressState   string
	AddressCountry string
	Token          string
}

// ChargeParams hold all of the parameters used for creating Charges.
type ChargeParams struct {
	Amount         int    `stripe_field:"amount"`
	Currency       string `stripe_field:"currency"`
	Customer       string `stripe_field:"customer"`
	CardParams     *CardParams
	Description    string `stripe_field:"description"`
	DisableCapture bool   `stripe_field:"capture" opposite:"true"`
	ApplicationFee int    `stripe_field:"application_fee"`
	Metadata       Metadata
}

// CouponParams hold all of the parameters used for creating Coupons.
type CouponParams struct {
	Id               string
	Duration         string
	AmountOff        int
	Currency         string
	DurationInMonths int
	MaxRedemptions   int
	PercentOff       int
	RedeemBy         int
}

// CustomerParams hold all of the parameters used for creating and updating
// Customers.
type CustomerParams struct {
	AccountBalance int `stripe_field:"account_balance"`
	CardParams     *CardParams
	Coupon         string `stripe_field:"coupon"`
	Description    string `stripe_field:"description"`
	Email          string `stripe_field:"email"`
	Plan           string `stripe_field:"plan"`
	Quantity       int    `stripe_field:"quantity"`
	TrialEnd       int    `stripe_field:"trial_end"`
	Metadata       Metadata
}

// InvoiceParams hold all of the parameters used for creating and updating
// Invoices.
type InvoiceParams struct {
	Customer       string
	ApplicationFee int
	Closed         bool
}

// InvoiceItemParams hold all of the parameters used for creating and updating
// InvoiceItems.
type InvoiceItemParams struct {
	Customer    string
	Amount      int
	Currency    string
	Invoice     string
	Description string
	Metadata    Metadata
}

// PlanParams hold all of the parameters used for creating and updating Plans.
type PlanParams struct {
	Id              string
	Amount          int
	Currency        string
	Interval        string
	IntervalCount   int
	Name            string
	TrialPeriodDays int
	Metadata        Metadata
}

// RecipientParams hold all of the parameters used for creating and updating
// Recipients.
type RecipientParams struct {
	Name              string
	Type              string
	TaxId             string
	BankAccountParams *BankAccountParams
	Email             string
	Description       string
	Metadata          Metadata
}

// RefundParams hold all of the parameters used for refunding Charges.
type RefundParams struct {
	Amount               int
	RefundApplicationFee bool
}

// SubscriptionParams hold all of the parameters used for updating and
// canceling Subscriptions.
type SubscriptionParams struct {
	Plan                  string
	Coupon                string
	DisableProrate        bool
	TrialEnd              int
	CardParams            *CardParams
	Quantity              int
	ApplicationFeePercent float64
	AtPeriodEnd           bool
}

// TokenParams hold all of the parameters used for creating Tokens.
type TokenParams struct {
	BankAccountParams *BankAccountParams
	CardParams        *CardParams
	Customer          string
}

// TransferParams hold all of the parameters used for creating and updating
// Transfers.
type TransferParams struct {
	Amount              int
	Currency            string
	Recipient           string
	Description         string
	StatementDescriptor string
	Metadata            Metadata
}
<commit_msg>use embedded fields in stripe params<commit_after>package stripe

// BankAccountParams hold all of the parameters used for creating and updating
// BankAccounts.
type BankAccountParams struct {
	Country       string `stripe_field:"bank_account[country]"`
	RoutingNumber string `stripe_field:"bank_account[routing_number]"`
	AccountNumber string `stripe_field:"bank_account[account_number]"`
}

// CardParams hold all of the parameters used for creating and updating Cards.
type CardParams struct {
	Number         string
	ExpMonth       int
	ExpYear        int
	CVC            string
	Name           string
	AddressLine1   string
	AddressLine2   string
	AddressCity    string
	AddressZip     string
	AddressState   string
	AddressCountry string
	Token          string
}

// ChargeParams hold all of the parameters used for creating Charges.
type ChargeParams struct {
	Amount         int    `stripe_field:"amount"`
	Currency       string `stripe_field:"currency"`
	Customer       string `stripe_field:"customer"`
	Description    string `stripe_field:"description"`
	DisableCapture bool   `stripe_field:"capture" opposite:"true"`
	ApplicationFee int    `stripe_field:"application_fee"`
	*CardParams
	Metadata
}

// CouponParams hold all of the parameters used for creating Coupons.
type CouponParams struct {
	Id               string
	Duration         string
	AmountOff        int
	Currency         string
	DurationInMonths int
	MaxRedemptions   int
	PercentOff       int
	RedeemBy         int
}

// CustomerParams hold all of the parameters used for creating and updating
// Customers.
type CustomerParams struct {
	AccountBalance int    `stripe_field:"account_balance"`
	Coupon         string `stripe_field:"coupon"`
	Description    string `stripe_field:"description"`
	Email          string `stripe_field:"email"`
	Plan           string `stripe_field:"plan"`
	Quantity       int    `stripe_field:"quantity"`
	TrialEnd       int    `stripe_field:"trial_end"`
	*CardParams
	Metadata
}

// InvoiceParams hold all of the parameters used for creating and updating
// Invoices.
type InvoiceParams struct {
	Customer       string
	ApplicationFee int
	Closed         bool
}

// InvoiceItemParams hold all of the parameters used for creating and updating
// InvoiceItems.
type InvoiceItemParams struct {
	Customer    string
	Amount      int
	Currency    string
	Invoice     string
	Description string
	Metadata
}

// PlanParams hold all of the parameters used for creating and updating Plans.
type PlanParams struct {
	Id              string
	Amount          int
	Currency        string
	Interval        string
	IntervalCount   int
	Name            string
	TrialPeriodDays int
	Metadata
}

// RecipientParams hold all of the parameters used for creating and updating
// Recipients.
type RecipientParams struct {
	Name        string
	Type        string
	TaxId       string
	Email       string
	Description string
	*BankAccountParams
	Metadata
}

// RefundParams hold all of the parameters used for refunding Charges.
type RefundParams struct {
	Amount               int
	RefundApplicationFee bool
}

// SubscriptionParams hold all of the parameters used for updating and
// canceling Subscriptions.
type SubscriptionParams struct {
	Plan                  string
	Coupon                string
	DisableProrate        bool
	TrialEnd              int
	Quantity              int
	ApplicationFeePercent float64
	AtPeriodEnd           bool
	*CardParams
}

// TokenParams hold all of the parameters used for creating Tokens.
type TokenParams struct {
	Customer string
	*BankAccountParams
	*CardParams
}

// TransferParams hold all of the parameters used for creating and updating
// Transfers.
type TransferParams struct {
	Amount              int
	Currency            string
	Recipient           string
	Description         string
	StatementDescriptor string
	Metadata
}
<|endoftext|>


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

package query

import (
	"testing"

	"github.com/square/metrics/api"
	"github.com/square/metrics/assert"
)

type FakeBackend struct{}

func (b FakeBackend) FetchMetadata(metric api.TaggedMetric) api.MetricMetadata {
	return api.MetricMetadata{}
}

func (b FakeBackend) FetchSeries(query api.Query) api.SeriesList {
	return api.SeriesList{}
}

func Test_ScalarExpression(t *testing.T) {
	for _, test := range []struct {
		expectSuccess  bool
		expr           scalarExpression
		timerange      api.Timerange
		expectedSeries []api.Timeseries
	}{
		{
			true,
			scalarExpression{5},
			api.Timerange{0, 10, 2},
			[]api.Timeseries{
				api.Timeseries{
					[]float64{5.0, 5.0, 5.0, 5.0, 5.0, 5.0},
					api.TaggedMetric{},
				},
			},
		},
		{
			false,
			scalarExpression{5},
			api.Timerange{0, 10, 3},
			[]api.Timeseries{},
		},
	} {
		a := assert.New(t).Contextf("%+v", test)

		result, err := test.expr.Evaluate(EvaluationContext{FakeBackend{}, test.timerange})

		a.EqBool(err == nil, test.expectSuccess)
		// Nothing else to validate if we expect failure
		if !test.expectSuccess {
			continue
		}

		a.EqInt(len(result.Series), len(test.expectedSeries))

		for i := 0; i < len(result.Series); i += 1 {
			a.Eq(result.Series[i].Values, test.expectedSeries[i].Values)
		}
	}
}

func Test_evaluateBinaryOperation(t *testing.T) {
	for _, test := range []struct {
		expectSuccess bool
		context       EvaluationContext
		functionName  string
		operands      []Expression
		evalFunction  func(float64, float64) float64
	}{} {
		// XXX Add tests
		_ = test
	}
}

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

package query

import (
	"testing"

	"github.com/square/metrics/api"
	"github.com/square/metrics/assert"
)

type FakeBackend struct{}

func (b FakeBackend) FetchMetadata(metric api.TaggedMetric) api.MetricMetadata {
	return api.MetricMetadata{}
}

func (b FakeBackend) FetchSeries(query api.Query) api.SeriesList {
	return api.SeriesList{}
}

type LiteralExpression struct {
	Values []float64
}

func (expr *LiteralExpression) Evaluate(context EvaluationContext) (*api.SeriesList, error) {
	return &api.SeriesList{
		[]api.Timeseries{api.Timeseries{expr.Values, api.TaggedMetric{}}},
		api.Timerange{},
	}, nil
}

func Test_ScalarExpression(t *testing.T) {
	for _, test := range []struct {
		expectSuccess  bool
		expr           scalarExpression
		timerange      api.Timerange
		expectedSeries []api.Timeseries
	}{
		{
			true,
			scalarExpression{5},
			api.Timerange{0, 10, 2},
			[]api.Timeseries{
				api.Timeseries{
					[]float64{5.0, 5.0, 5.0, 5.0, 5.0, 5.0},
					api.TaggedMetric{},
				},
			},
		},
		{
			false,
			scalarExpression{5},
			api.Timerange{0, 10, 3},
			[]api.Timeseries{},
		},
	} {
		a := assert.New(t).Contextf("%+v", test)

		result, err := test.expr.Evaluate(EvaluationContext{FakeBackend{}, test.timerange})

		a.EqBool(err == nil, test.expectSuccess)
		// Nothing else to validate if we expect failure
		if !test.expectSuccess {
			continue
		}

		a.EqInt(len(result.Series), len(test.expectedSeries))

		for i := 0; i < len(result.Series); i += 1 {
			a.Eq(result.Series[i].Values, test.expectedSeries[i].Values)
		}
	}
}

func Test_evaluateBinaryOperation(t *testing.T) {
	emptyContext := EvaluationContext{FakeBackend{}, api.Timerange{}}
	for _, test := range []struct {
		context              EvaluationContext
		functionName         string
		operands             []Expression
		evalFunction         func(float64, float64) float64
		expectSuccess        bool
		expectedResultValues []float64
	}{
		{
			emptyContext,
			"add",
			[]Expression{
				&LiteralExpression{
					[]float64{1, 2, 3},
				},
				&LiteralExpression{
					[]float64{4, 5, 1},
				},
			},
			func(left, right float64) float64 { return left + right },
			true,
			[]float64{5, 7, 4},
		},
		{
			emptyContext,
			"subtract",
			[]Expression{
				&LiteralExpression{
					[]float64{1, 2, 3},
				},
				&LiteralExpression{
					[]float64{4, 5, 1},
				},
			},
			func(left, right float64) float64 { return left - right },
			true,
			[]float64{-3, -3, 2},
		},
	} {
		a := assert.New(t).Contextf("%+v", test)

		result, err := evaluateBinaryOperation(
			test.context,
			test.functionName,
			test.operands,
			test.evalFunction,
		)

		a.EqBool(err == nil, test.expectSuccess)
		// Nothing else to validate if we expect failure
		if !test.expectSuccess {
			continue
		}

		a.EqInt(len(result.Series), 1)
		a.Eq(result.Series[0].Values, test.expectedResultValues)
	}
}

var _ api.Backend = (*FakeBackend)(nil)
<|endoftext|>


<commit_before>package linkedql

import (
	"fmt"
	"reflect"

	"github.com/cayleygraph/quad"
)

var pathStep = reflect.TypeOf((*PathStep)(nil)).Elem()
var step = reflect.TypeOf((*Step)(nil)).Elem()
var value = reflect.TypeOf((*quad.Value)(nil)).Elem()
var operator = reflect.TypeOf((*Operator)(nil)).Elem()

func typeToRange(t reflect.Type) string {
	if t.Kind() == reflect.Slice {
		return typeToRange(t.Elem())
	}
	if t.Kind() == reflect.String {
		return "xsd:string"
	}
	if t.Kind() == reflect.Bool {
		return "xsd:boolean"
	}
	if kind := t.Kind(); kind == reflect.Int64 || kind == reflect.Int {
		return "xsd:int"
	}
	if t.Implements(pathStep) {
		return "linkedql:PathStep"
	}
	if t.Implements(operator) {
		return "linkedql:Operator"
	}
	if t.Implements(value) {
		return "rdfs:Resource"
	}
	panic("Unexpected type " + t.String())
}

// Identified is used for referencing a type
type Identified struct {
	ID string `json:"@id"`
}

// NewIdentified creates new identified struct
func NewIdentified(id string) Identified {
	return Identified{ID: id}
}

// CardinalityRestriction is used to indicate a how many values can a property get
type CardinalityRestriction struct {
	ID          string     `json:"@id"`
	Type        string     `json:"@type"`
	Cardinality int        `json:"owl:cardinality"`
	Property    Identified `json:"owl:onProperty"`
}

func NewBlankNodeID() string {
	return quad.RandomBlankNode().String()
}

// NewSingleCardinalityRestriction creates a cardinality of 1 restriction for given property
func NewSingleCardinalityRestriction(property string) CardinalityRestriction {
	return CardinalityRestriction{
		ID:          NewBlankNodeID(),
		Type:        "owl:Restriction",
		Cardinality: 1,
		Property:    Identified{ID: property},
	}
}

// GetOWLPropertyType for given kind of value type returns property OWL type
func GetOWLPropertyType(kind reflect.Kind) string {
	if kind == reflect.String || kind == reflect.Bool || kind == reflect.Int64 || kind == reflect.Int {
		return "owl:DatatypeProperty"
	}
	return "owl:ObjectProperty"
}

// Property is used to declare a property
type Property struct {
	ID     string      `json:"@id"`
	Type   string      `json:"@type"`
	Domain interface{} `json:"rdfs:domain"`
	Range  interface{} `json:"rdfs:range"`
}

// Class is used to declare a class
type Class struct {
	ID           string        `json:"@id"`
	Type         string        `json:"@type"`
	SuperClasses []interface{} `json:"rdfs:subClassOf"`
}

// NewClass creates a new Class struct
func NewClass(id string, superClasses []interface{}) Class {
	return Class{
		ID:           id,
		Type:         "rdfs:Class",
		SuperClasses: superClasses,
	}
}

// GetStepTypeClass for given step type returns the matching class identifier
func GetStepTypeClass(t reflect.Type) string {
	if t.Implements(pathStep) {
		return "linkedql:PathStep"
	}
	return "linkedql:Step"
}

type List struct {
	ID      string        `json:"@id"`
	Members []interface{} `json:"@list"`
}

func NewList(members []interface{}) List {
	return List{
		ID:      NewBlankNodeID(),
		Members: members,
	}
}

type UnionOf struct {
	ID   string `json:"@id"`
	Type string `json:"@type"`
	List List   `json:"owl:unionOf"`
}

func NewUnionOf(classes []string) UnionOf {
	var members []interface{}
	for _, class := range classes {
		members = append(members, NewIdentified(class))
	}
	list := NewList(members)
	return UnionOf{
		ID:   NewBlankNodeID(),
		Type: "owl:Class",
		List: list,
	}
}

// GenerateSchema for registered types. The schema is a collection of JSON-LD documents
// of the LinkedQL types and properties.
func GenerateSchema() []interface{} {
	var documents []interface{}
	propertyToTypes := map[string]map[string]struct{}{}
	propertyToDomains := map[string]map[string]struct{}{}
	propertyToRanges := map[string]map[string]struct{}{}
	for name, t := range typeByName {
		superClasses := []interface{}{
			NewIdentified(GetStepTypeClass(pathStep)),
		}
		for i := 0; i < t.NumField(); i++ {
			f := t.Field(i)
			if f.Anonymous {
				continue
			}
			property := "linkedql:" + f.Tag.Get("json")
			if f.Type.Kind() != reflect.Slice {
				restriction := NewSingleCardinalityRestriction(property)
				superClasses = append(superClasses, restriction)
			}
			_type := GetOWLPropertyType(f.Type.Kind())
			if propertyToTypes[property] == nil {
				propertyToTypes[property] = map[string]struct{}{}
			}
			propertyToTypes[property][_type] = struct{}{}
			if propertyToDomains[property] == nil {
				propertyToDomains[property] = map[string]struct{}{}
			}
			propertyToDomains[property][name] = struct{}{}
			if propertyToRanges[property] == nil {
				propertyToRanges[property] = map[string]struct{}{}
			}
			propertyToRanges[property][typeToRange(f.Type)] = struct{}{}
		}
		documents = append(documents, NewClass(name, superClasses))
	}
	for property, typeSet := range propertyToTypes {
		var types []string
		for _type := range typeSet {
			types = append(types, _type)
		}
		if len(types) != 1 {
			fmt.Printf("%v\n", propertyToRanges[property])
			panic("Properties must be either object properties or datatype properties. " + property + " has both.")
		}
		_type := types[0]
		var domains []string
		for domain := range propertyToDomains[property] {
			domains = append(domains, domain)
		}
		var ranges []string
		for _range := range propertyToRanges[property] {
			ranges = append(ranges, _range)
		}
		var domain interface{}
		var _range interface{}
		if len(domains) == 1 {
			domain = domains[0]
		} else {
			domain = NewUnionOf(domains)
		}
		if len(ranges) == 1 {
			_range = ranges[0]
		} else {
			_range = NewUnionOf(ranges)
		}
		documents = append(documents, Property{
			ID:     property,
			Type:   _type,
			Domain: domain,
			Range:  _range,
		})
	}
	return documents
}
<commit_msg>linkedql: WIP: get description<commit_after>package linkedql

import (
	"fmt"
	"reflect"

	"github.com/cayleygraph/quad"
)

var pathStep = reflect.TypeOf((*PathStep)(nil)).Elem()
var step = reflect.TypeOf((*Step)(nil)).Elem()
var value = reflect.TypeOf((*quad.Value)(nil)).Elem()
var operator = reflect.TypeOf((*Operator)(nil)).Elem()

func typeToRange(t reflect.Type) string {
	if t.Kind() == reflect.Slice {
		return typeToRange(t.Elem())
	}
	if t.Kind() == reflect.String {
		return "xsd:string"
	}
	if t.Kind() == reflect.Bool {
		return "xsd:boolean"
	}
	if kind := t.Kind(); kind == reflect.Int64 || kind == reflect.Int {
		return "xsd:int"
	}
	if t.Implements(pathStep) {
		return "linkedql:PathStep"
	}
	if t.Implements(operator) {
		return "linkedql:Operator"
	}
	if t.Implements(value) {
		return "rdfs:Resource"
	}
	panic("Unexpected type " + t.String())
}

// Identified is used for referencing a type
type Identified struct {
	ID string `json:"@id"`
}

// NewIdentified creates new identified struct
func NewIdentified(id string) Identified {
	return Identified{ID: id}
}

// CardinalityRestriction is used to indicate a how many values can a property get
type CardinalityRestriction struct {
	ID          string     `json:"@id"`
	Type        string     `json:"@type"`
	Cardinality int        `json:"owl:cardinality"`
	Property    Identified `json:"owl:onProperty"`
}

func NewBlankNodeID() string {
	return quad.RandomBlankNode().String()
}

// NewSingleCardinalityRestriction creates a cardinality of 1 restriction for given property
func NewSingleCardinalityRestriction(property string) CardinalityRestriction {
	return CardinalityRestriction{
		ID:          NewBlankNodeID(),
		Type:        "owl:Restriction",
		Cardinality: 1,
		Property:    Identified{ID: property},
	}
}

// GetOWLPropertyType for given kind of value type returns property OWL type
func GetOWLPropertyType(kind reflect.Kind) string {
	if kind == reflect.String || kind == reflect.Bool || kind == reflect.Int64 || kind == reflect.Int {
		return "owl:DatatypeProperty"
	}
	return "owl:ObjectProperty"
}

// Property is used to declare a property
type Property struct {
	ID     string      `json:"@id"`
	Type   string      `json:"@type"`
	Domain interface{} `json:"rdfs:domain"`
	Range  interface{} `json:"rdfs:range"`
}

// Class is used to declare a class
type Class struct {
	ID           string        `json:"@id"`
	Type         string        `json:"@type"`
	Comment      string        `json:"rdfs:comment"`
	SuperClasses []interface{} `json:"rdfs:subClassOf"`
}

// NewClass creates a new Class struct
func NewClass(id string, superClasses []interface{}, comment string) Class {
	return Class{
		ID:           id,
		Type:         "rdfs:Class",
		SuperClasses: superClasses,
		Comment:      comment,
	}
}

// GetStepTypeClass for given step type returns the matching class identifier
func GetStepTypeClass(t reflect.Type) string {
	if t.Implements(pathStep) {
		return "linkedql:PathStep"
	}
	return "linkedql:Step"
}

type List struct {
	ID      string        `json:"@id"`
	Members []interface{} `json:"@list"`
}

func NewList(members []interface{}) List {
	return List{
		ID:      NewBlankNodeID(),
		Members: members,
	}
}

type UnionOf struct {
	ID   string `json:"@id"`
	Type string `json:"@type"`
	List List   `json:"owl:unionOf"`
}

func NewUnionOf(classes []string) UnionOf {
	var members []interface{}
	for _, class := range classes {
		members = append(members, NewIdentified(class))
	}
	list := NewList(members)
	return UnionOf{
		ID:   NewBlankNodeID(),
		Type: "owl:Class",
		List: list,
	}
}

// GenerateSchema for registered types. The schema is a collection of JSON-LD documents
// of the LinkedQL types and properties.
func GenerateSchema() []interface{} {
	var documents []interface{}
	propertyToTypes := map[string]map[string]struct{}{}
	propertyToDomains := map[string]map[string]struct{}{}
	propertyToRanges := map[string]map[string]struct{}{}
	for name, t := range typeByName {
		fmt.Printf("%v %v %v\n", t, step, t.Implements(step))
		if !t.Implements(step) {
			continue
		}
		value := reflect.Zero(t)
		description := value.MethodByName("Description")
		fmt.Printf("%v", description)
		superClasses := []interface{}{
			NewIdentified(GetStepTypeClass(pathStep)),
		}
		for i := 0; i < t.NumField(); i++ {
			f := t.Field(i)
			if f.Anonymous {
				continue
			}
			property := "linkedql:" + f.Tag.Get("json")
			if f.Type.Kind() != reflect.Slice {
				restriction := NewSingleCardinalityRestriction(property)
				superClasses = append(superClasses, restriction)
			}
			_type := GetOWLPropertyType(f.Type.Kind())
			if propertyToTypes[property] == nil {
				propertyToTypes[property] = map[string]struct{}{}
			}
			propertyToTypes[property][_type] = struct{}{}
			if propertyToDomains[property] == nil {
				propertyToDomains[property] = map[string]struct{}{}
			}
			propertyToDomains[property][name] = struct{}{}
			if propertyToRanges[property] == nil {
				propertyToRanges[property] = map[string]struct{}{}
			}
			propertyToRanges[property][typeToRange(f.Type)] = struct{}{}
		}
		documents = append(documents, NewClass(name, superClasses, description.String()))
	}
	for property, typeSet := range propertyToTypes {
		var types []string
		for _type := range typeSet {
			types = append(types, _type)
		}
		if len(types) != 1 {
			fmt.Printf("%v\n", propertyToRanges[property])
			panic("Properties must be either object properties or datatype properties. " + property + " has both.")
		}
		_type := types[0]
		var domains []string
		for domain := range propertyToDomains[property] {
			domains = append(domains, domain)
		}
		var ranges []string
		for _range := range propertyToRanges[property] {
			ranges = append(ranges, _range)
		}
		var domain interface{}
		var _range interface{}
		if len(domains) == 1 {
			domain = domains[0]
		} else {
			domain = NewUnionOf(domains)
		}
		if len(ranges) == 1 {
			_range = ranges[0]
		} else {
			_range = NewUnionOf(ranges)
		}
		documents = append(documents, Property{
			ID:     property,
			Type:   _type,
			Domain: domain,
			Range:  _range,
		})
	}
	return documents
}
<|endoftext|>