<commit_before>package gojsonschema

import (
	"net"
	"net/mail"
	"net/url"
	"regexp"
	"strings"
	"sync"
	"time"
)

type (
	// FormatChecker is the interface all formatters added to FormatCheckerChain must implement
	FormatChecker interface {
		IsFormat(input interface{}) bool
	}

	// FormatCheckerChain holds the formatters
	FormatCheckerChain struct {
		formatters map[string]FormatChecker
	}

	// EmailFormatter verifies email address formats
	EmailFormatChecker struct{}

	// IPV4FormatChecker verifies IP addresses in the ipv4 format
	IPV4FormatChecker struct{}

	// IPV6FormatChecker verifies IP addresses in the ipv6 format
	IPV6FormatChecker struct{}

	// DateTimeFormatChecker verifies date/time formats per RFC3339 5.6
	//
	// Valid formats:
	// 		Partial Time: HH:MM:SS
	//		Full Date: YYYY-MM-DD
	// 		Full Time: HH:MM:SSZ-07:00
	//		Date Time: YYYY-MM-DDTHH:MM:SSZ-0700
	//
	// 	Where
	//		YYYY = 4DIGIT year
	//		MM = 2DIGIT month ; 01-12
	//		DD = 2DIGIT day-month ; 01-28, 01-29, 01-30, 01-31 based on month/year
	//		HH = 2DIGIT hour ; 00-23
	//		MM = 2DIGIT ; 00-59
	//		SS = 2DIGIT ; 00-58, 00-60 based on leap second rules
	//		T = Literal
	//		Z = Literal
	//
	//	Note: Nanoseconds are also suported in all formats
	//
	// http://tools.ietf.org/html/rfc3339#section-5.6
	DateTimeFormatChecker struct{}

	DateFormatChecker struct{}

	TimeFormatChecker struct{}

	// URIFormatChecker validates a URI with a valid Scheme per RFC3986
	URIFormatChecker struct{}

	// URIReferenceFormatChecker validates a URI or relative-reference per RFC3986
	URIReferenceFormatChecker struct{}

	// URITemplateFormatChecker validates a URI template per RFC6570
	URITemplateFormatChecker struct{}

	// HostnameFormatChecker validates a hostname is in the correct format
	HostnameFormatChecker struct{}

	// UUIDFormatChecker validates a UUID is in the correct format
	UUIDFormatChecker struct{}

	// RegexFormatChecker validates a regex is in the correct format
	RegexFormatChecker struct{}

	// JSONPointerFormatChecker validates a JSON Pointer per RFC6901
	JSONPointerFormatChecker struct{}

	// RelativeJSONPointerFormatChecker validates a relative JSON Pointer is in the correct format
	RelativeJSONPointerFormatChecker struct{}
)

var (
	// Formatters holds the valid formatters, and is a public variable
	// so library users can add custom formatters
	FormatCheckers = FormatCheckerChain{
		formatters: map[string]FormatChecker{
			"date":                  DateFormatChecker{},
			"time":                  TimeFormatChecker{},
			"date-time":             DateTimeFormatChecker{},
			"hostname":              HostnameFormatChecker{},
			"email":                 EmailFormatChecker{},
			"idn-email":             EmailFormatChecker{},
			"ipv4":                  IPV4FormatChecker{},
			"ipv6":                  IPV6FormatChecker{},
			"uri":                   URIFormatChecker{},
			"uri-reference":         URIReferenceFormatChecker{},
			"iri":                   URIFormatChecker{},
			"iri-reference":         URIReferenceFormatChecker{},
			"uri-template":          URITemplateFormatChecker{},
			"uuid":                  UUIDFormatChecker{},
			"regex":                 RegexFormatChecker{},
			"json-pointer":          JSONPointerFormatChecker{},
			"relative-json-pointer": RelativeJSONPointerFormatChecker{},
		},
	}

	// Regex credit: https://www.socketloop.com/tutorials/golang-validate-hostname
	rxHostname = regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`)

	// Use a regex to make sure curly brackets are balanced properly after validating it as a AURI
	rxURITemplate = regexp.MustCompile("^([^{]*({[^}]*})?)*$")

	rxUUID = regexp.MustCompile("^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$")

	rxJSONPointer = regexp.MustCompile("^(?:/(?:[^~/]|~0|~1)*)*$")

	rxRelJSONPointer = regexp.MustCompile("^(?:0|[1-9][0-9]*)(?:#|(?:/(?:[^~/]|~0|~1)*)*)$")

	lock = new(sync.Mutex)
)

// Add adds a FormatChecker to the FormatCheckerChain
// The name used will be the value used for the format key in your json schema
func (c *FormatCheckerChain) Add(name string, f FormatChecker) *FormatCheckerChain {
	lock.Lock()
	c.formatters[name] = f
	lock.Unlock()

	return c
}

// Remove deletes a FormatChecker from the FormatCheckerChain (if it exists)
func (c *FormatCheckerChain) Remove(name string) *FormatCheckerChain {
	lock.Lock()
	delete(c.formatters, name)
	lock.Unlock()

	return c
}

// Has checks to see if the FormatCheckerChain holds a FormatChecker with the given name
func (c *FormatCheckerChain) Has(name string) bool {
	lock.Lock()
	_, ok := c.formatters[name]
	lock.Unlock()

	return ok
}

// IsFormat will check an input against a FormatChecker with the given name
// to see if it is the correct format
func (c *FormatCheckerChain) IsFormat(name string, input interface{}) bool {
	f, ok := c.formatters[name]

	if !ok {
		return false
	}

	return f.IsFormat(input)
}

func (f EmailFormatChecker) IsFormat(input interface{}) bool {

	asString, ok := input.(string)
	if ok == false {
		return false
	}

	_, err := mail.ParseAddress(asString)

	return err == nil
}

// Credit: https://github.com/asaskevich/govalidator
func (f IPV4FormatChecker) IsFormat(input interface{}) bool {

	asString, ok := input.(string)
	if ok == false {
		return false
	}

	ip := net.ParseIP(asString)
	return ip != nil && strings.Contains(asString, ".")
}

// Credit: https://github.com/asaskevich/govalidator
func (f IPV6FormatChecker) IsFormat(input interface{}) bool {

	asString, ok := input.(string)
	if ok == false {
		return false
	}

	ip := net.ParseIP(asString)
	return ip != nil && strings.Contains(asString, ":")
}

func (f DateTimeFormatChecker) IsFormat(input interface{}) bool {

	asString, ok := input.(string)
	if ok == false {
		return false
	}

	formats := []string{
		"15:04:05",
		"15:04:05Z07:00",
		"2006-01-02",
		time.RFC3339,
		time.RFC3339Nano,
	}

	for _, format := range formats {
		if _, err := time.Parse(format, asString); err == nil {
			return true
		}
	}

	return false
}

func (f DateFormatChecker) IsFormat(input interface{}) bool {
	asString, ok := input.(string)
	if ok == false {
		return false
	}
	_, err := time.Parse("2006-01-02", asString)
	return err == nil
}

func (f TimeFormatChecker) IsFormat(input interface{}) bool {
	asString, ok := input.(string)
	if ok == false {
		return false
	}

	if _, err := time.Parse("15:04:05Z07:00", asString); err == nil {
		return true
	}

	_, err := time.Parse("15:04:05", asString)
	return err == nil
}

func (f URIFormatChecker) IsFormat(input interface{}) bool {

	asString, ok := input.(string)
	if ok == false {
		return false
	}

	u, err := url.Parse(asString)

	if err != nil || u.Scheme == "" {
		return false
	}

	return !strings.Contains(asString, `\`)
}

func (f URIReferenceFormatChecker) IsFormat(input interface{}) bool {

	asString, ok := input.(string)
	if ok == false {
		return false
	}

	_, err := url.Parse(asString)
	return err == nil && !strings.Contains(asString, `\`)
}

func (f URITemplateFormatChecker) IsFormat(input interface{}) bool {
	asString, ok := input.(string)
	if ok == false {
		return false
	}

	u, err := url.Parse(asString)
	if err != nil || strings.Contains(asString, `\`) {
		return false
	}

	return rxURITemplate.MatchString(u.Path)
}

func (f HostnameFormatChecker) IsFormat(input interface{}) bool {

	asString, ok := input.(string)
	if ok == false {
		return false
	}

	return rxHostname.MatchString(asString) && len(asString) < 256
}

func (f UUIDFormatChecker) IsFormat(input interface{}) bool {

	asString, ok := input.(string)
	if ok == false {
		return false
	}

	return rxUUID.MatchString(asString)
}

// IsFormat implements FormatChecker interface.
func (f RegexFormatChecker) IsFormat(input interface{}) bool {

	asString, ok := input.(string)
	if ok == false {
		return false
	}

	if asString == "" {
		return true
	}
	_, err := regexp.Compile(asString)
	if err != nil {
		return false
	}
	return true
}

func (f JSONPointerFormatChecker) IsFormat(input interface{}) bool {
	asString, ok := input.(string)
	if ok == false {
		return false
	}

	return rxJSONPointer.MatchString(asString)
}

func (f RelativeJSONPointerFormatChecker) IsFormat(input interface{}) bool {
	asString, ok := input.(string)
	if ok == false {
		return false
	}

	return rxRelJSONPointer.MatchString(asString)
}
<commit_msg>More idiomatic checks, and some minor reformatting<commit_after>package gojsonschema

import (
	"net"
	"net/mail"
	"net/url"
	"regexp"
	"strings"
	"sync"
	"time"
)

type (
	// FormatChecker is the interface all formatters added to FormatCheckerChain must implement
	FormatChecker interface {
		IsFormat(input interface{}) bool
	}

	// FormatCheckerChain holds the formatters
	FormatCheckerChain struct {
		formatters map[string]FormatChecker
	}

	// EmailFormatter verifies email address formats
	EmailFormatChecker struct{}

	// IPV4FormatChecker verifies IP addresses in the ipv4 format
	IPV4FormatChecker struct{}

	// IPV6FormatChecker verifies IP addresses in the ipv6 format
	IPV6FormatChecker struct{}

	// DateTimeFormatChecker verifies date/time formats per RFC3339 5.6
	//
	// Valid formats:
	// 		Partial Time: HH:MM:SS
	//		Full Date: YYYY-MM-DD
	// 		Full Time: HH:MM:SSZ-07:00
	//		Date Time: YYYY-MM-DDTHH:MM:SSZ-0700
	//
	// 	Where
	//		YYYY = 4DIGIT year
	//		MM = 2DIGIT month ; 01-12
	//		DD = 2DIGIT day-month ; 01-28, 01-29, 01-30, 01-31 based on month/year
	//		HH = 2DIGIT hour ; 00-23
	//		MM = 2DIGIT ; 00-59
	//		SS = 2DIGIT ; 00-58, 00-60 based on leap second rules
	//		T = Literal
	//		Z = Literal
	//
	//	Note: Nanoseconds are also suported in all formats
	//
	// http://tools.ietf.org/html/rfc3339#section-5.6
	DateTimeFormatChecker struct{}

	DateFormatChecker struct{}

	TimeFormatChecker struct{}

	// URIFormatChecker validates a URI with a valid Scheme per RFC3986
	URIFormatChecker struct{}

	// URIReferenceFormatChecker validates a URI or relative-reference per RFC3986
	URIReferenceFormatChecker struct{}

	// URITemplateFormatChecker validates a URI template per RFC6570
	URITemplateFormatChecker struct{}

	// HostnameFormatChecker validates a hostname is in the correct format
	HostnameFormatChecker struct{}

	// UUIDFormatChecker validates a UUID is in the correct format
	UUIDFormatChecker struct{}

	// RegexFormatChecker validates a regex is in the correct format
	RegexFormatChecker struct{}

	// JSONPointerFormatChecker validates a JSON Pointer per RFC6901
	JSONPointerFormatChecker struct{}

	// RelativeJSONPointerFormatChecker validates a relative JSON Pointer is in the correct format
	RelativeJSONPointerFormatChecker struct{}
)

var (
	// Formatters holds the valid formatters, and is a public variable
	// so library users can add custom formatters
	FormatCheckers = FormatCheckerChain{
		formatters: map[string]FormatChecker{
			"date":                  DateFormatChecker{},
			"time":                  TimeFormatChecker{},
			"date-time":             DateTimeFormatChecker{},
			"hostname":              HostnameFormatChecker{},
			"email":                 EmailFormatChecker{},
			"idn-email":             EmailFormatChecker{},
			"ipv4":                  IPV4FormatChecker{},
			"ipv6":                  IPV6FormatChecker{},
			"uri":                   URIFormatChecker{},
			"uri-reference":         URIReferenceFormatChecker{},
			"iri":                   URIFormatChecker{},
			"iri-reference":         URIReferenceFormatChecker{},
			"uri-template":          URITemplateFormatChecker{},
			"uuid":                  UUIDFormatChecker{},
			"regex":                 RegexFormatChecker{},
			"json-pointer":          JSONPointerFormatChecker{},
			"relative-json-pointer": RelativeJSONPointerFormatChecker{},
		},
	}

	// Regex credit: https://www.socketloop.com/tutorials/golang-validate-hostname
	rxHostname = regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`)

	// Use a regex to make sure curly brackets are balanced properly after validating it as a AURI
	rxURITemplate = regexp.MustCompile("^([^{]*({[^}]*})?)*$")

	rxUUID = regexp.MustCompile("^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$")

	rxJSONPointer = regexp.MustCompile("^(?:/(?:[^~/]|~0|~1)*)*$")

	rxRelJSONPointer = regexp.MustCompile("^(?:0|[1-9][0-9]*)(?:#|(?:/(?:[^~/]|~0|~1)*)*)$")

	lock = new(sync.Mutex)
)

// Add adds a FormatChecker to the FormatCheckerChain
// The name used will be the value used for the format key in your json schema
func (c *FormatCheckerChain) Add(name string, f FormatChecker) *FormatCheckerChain {
	lock.Lock()
	c.formatters[name] = f
	lock.Unlock()

	return c
}

// Remove deletes a FormatChecker from the FormatCheckerChain (if it exists)
func (c *FormatCheckerChain) Remove(name string) *FormatCheckerChain {
	lock.Lock()
	delete(c.formatters, name)
	lock.Unlock()

	return c
}

// Has checks to see if the FormatCheckerChain holds a FormatChecker with the given name
func (c *FormatCheckerChain) Has(name string) bool {
	lock.Lock()
	_, ok := c.formatters[name]
	lock.Unlock()

	return ok
}

// IsFormat will check an input against a FormatChecker with the given name
// to see if it is the correct format
func (c *FormatCheckerChain) IsFormat(name string, input interface{}) bool {
	f, ok := c.formatters[name]
	if !ok {
		return false
	}

	return f.IsFormat(input)
}

func (f EmailFormatChecker) IsFormat(input interface{}) bool {
	asString, ok := input.(string)
	if !ok {
		return false
	}

	_, err := mail.ParseAddress(asString)
	return err == nil
}

// Credit: https://github.com/asaskevich/govalidator
func (f IPV4FormatChecker) IsFormat(input interface{}) bool {
	asString, ok := input.(string)
	if !ok {
		return false
	}

	ip := net.ParseIP(asString)
	return ip != nil && strings.Contains(asString, ".")
}

// Credit: https://github.com/asaskevich/govalidator
func (f IPV6FormatChecker) IsFormat(input interface{}) bool {
	asString, ok := input.(string)
	if !ok {
		return false
	}

	ip := net.ParseIP(asString)
	return ip != nil && strings.Contains(asString, ":")
}

func (f DateTimeFormatChecker) IsFormat(input interface{}) bool {
	asString, ok := input.(string)
	if !ok {
		return false
	}

	formats := []string{
		"15:04:05",
		"15:04:05Z07:00",
		"2006-01-02",
		time.RFC3339,
		time.RFC3339Nano,
	}

	for _, format := range formats {
		if _, err := time.Parse(format, asString); err == nil {
			return true
		}
	}

	return false
}

func (f DateFormatChecker) IsFormat(input interface{}) bool {
	asString, ok := input.(string)
	if !ok {
		return false
	}
	_, err := time.Parse("2006-01-02", asString)
	return err == nil
}

func (f TimeFormatChecker) IsFormat(input interface{}) bool {
	asString, ok := input.(string)
	if !ok {
		return false
	}

	if _, err := time.Parse("15:04:05Z07:00", asString); err == nil {
		return true
	}

	_, err := time.Parse("15:04:05", asString)
	return err == nil
}

func (f URIFormatChecker) IsFormat(input interface{}) bool {
	asString, ok := input.(string)
	if !ok {
		return false
	}

	u, err := url.Parse(asString)

	if err != nil || u.Scheme == "" {
		return false
	}

	return !strings.Contains(asString, `\`)
}

func (f URIReferenceFormatChecker) IsFormat(input interface{}) bool {
	asString, ok := input.(string)
	if !ok {
		return false
	}

	_, err := url.Parse(asString)
	return err == nil && !strings.Contains(asString, `\`)
}

func (f URITemplateFormatChecker) IsFormat(input interface{}) bool {
	asString, ok := input.(string)
	if !ok {
		return false
	}

	u, err := url.Parse(asString)
	if err != nil || strings.Contains(asString, `\`) {
		return false
	}

	return rxURITemplate.MatchString(u.Path)
}

func (f HostnameFormatChecker) IsFormat(input interface{}) bool {
	asString, ok := input.(string)
	if !ok {
		return false
	}

	return rxHostname.MatchString(asString) && len(asString) < 256
}

func (f UUIDFormatChecker) IsFormat(input interface{}) bool {
	asString, ok := input.(string)
	if !ok {
		return false
	}

	return rxUUID.MatchString(asString)
}

// IsFormat implements FormatChecker interface.
func (f RegexFormatChecker) IsFormat(input interface{}) bool {
	asString, ok := input.(string)
	if !ok {
		return false
	}

	if asString == "" {
		return true
	}
	_, err := regexp.Compile(asString)
	return err == nil
}

func (f JSONPointerFormatChecker) IsFormat(input interface{}) bool {
	asString, ok := input.(string)
	if !ok {
		return false
	}

	return rxJSONPointer.MatchString(asString)
}

func (f RelativeJSONPointerFormatChecker) IsFormat(input interface{}) bool {
	asString, ok := input.(string)
	if !ok {
		return false
	}

	return rxRelJSONPointer.MatchString(asString)
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"os"
	"os/exec"
	"os/user"
	"strings"
	"sync"

	"github.com/getcfs/fuse"
	pb "github.com/getcfs/megacfs/formic/proto"
	"github.com/gholt/cpcp"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

type server struct {
	fs *fs
	wg sync.WaitGroup
}

func newserver(fs *fs) *server {
	s := &server{
		fs: fs,
	}
	return s
}

func (s *server) serve() error {
	defer s.wg.Wait()

	for {
		req, err := s.fs.conn.ReadRequest()
		if err != nil {
			if err == io.EOF {
				break
			}
			return err
		}
		s.wg.Add(1)
		go func() {
			defer s.wg.Done()
			s.fs.handle(req)
		}()
	}
	return nil
}

func debuglog(msg interface{}) {
	fmt.Fprintf(os.Stderr, "%v\n", msg)
}

type rpc struct {
	apiClients []pb.ApiClient
}

func newrpc(addr string) *rpc {
	var opts []grpc.DialOption
	creds := credentials.NewTLS(&tls.Config{
		InsecureSkipVerify: true,
	})
	opts = append(opts, grpc.WithTransportCredentials(creds))
	clients := []pb.ApiClient{}

	// TODO: Rework this simplistic connection pooling
	for i := 0; i < 1; i++ { // hardcoded to 1 connection for now
		conn, err := grpc.Dial(addr, opts...)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		clients = append(clients, pb.NewApiClient(conn))
	}
	r := &rpc{
		apiClients: clients,
	}

	return r
}

func (r *rpc) api() pb.ApiClient {
	return r.apiClients[rand.Intn(len(r.apiClients))]
}

// NullWriter ...
type NullWriter int

func (NullWriter) Write([]byte) (int, error) { return 0, nil }

var cfsVersion string
var buildDate string
var commitVersion string
var goVersion string

type authResponse struct {
	Access struct {
		Token struct {
			ID string `json:"id"`
		} `json:"token"`
	} `json:"access"`
}

func auth(username string, apikey string) string {
	body := fmt.Sprintf(`{"auth":{"RAX-KSKEY:apiKeyCredentials":{"username": "%s","apiKey": "%s"}}}`, username, apikey)
	rbody := strings.NewReader(body)
	req, err := http.NewRequest("POST", "https://identity.api.rackspacecloud.com/v2.0/tokens", rbody)
	if err != nil {
		fmt.Printf("%v", err)
		os.Exit(1)
	}
	req.Header.Set("Content-Type", "application/json")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		fmt.Printf("%v", err)
		os.Exit(1)
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		fmt.Println(resp.Status)
		os.Exit(1)
	}

	// parse token from response
	var authResp authResponse
	r, _ := ioutil.ReadAll(resp.Body)
	json.Unmarshal(r, &authResp)
	token := authResp.Access.Token.ID

	return token
}

func mount() error {
	f := flag.NewFlagSet("mount", flag.ContinueOnError)
	f.Usage = func() {
		fmt.Println("Usage:")
		fmt.Println("    cfs mount [-o option,...] <region>:<fsid> <mountpoint>")
		fmt.Println("Options:")
		fmt.Println("    -o debug          enables debug output")
		fmt.Println("    -o ro             mount the filesystem read only")
		fmt.Println("    -o allow_other    allow access to other users")
		fmt.Println("Examples:")
		fmt.Println("    cfs mount iad:11111111-1111-1111-1111-111111111111 /mnt/test")
		fmt.Println("    cfs mount -o debug,ro iad:11111111-1111-1111-1111-111111111111 /mnt/test")
		os.Exit(1)
	}
	var options string
	f.StringVar(&options, "o", "", "")
	f.Parse(flag.Args()[1:])
	if f.NArg() != 2 {
		f.Usage()
	}
	regionFsid := f.Args()[0]
	parts := strings.Split(regionFsid, ":")
	if len(parts) != 2 {
		fmt.Println("Invalid filesystem:", regionFsid)
		f.Usage()
	}
	region := strings.ToLower(parts[0])
	fsid := parts[1]
	mountpoint := f.Args()[1]
	// Verify mountpoint exists
	_, err := os.Stat(mountpoint)
	if os.IsNotExist(err) {
		fmt.Printf("Mount point %s does not exist\n", mountpoint)
		os.Exit(1)
	}
	// Verify region
	addr, ok := regions[region]
	if !ok {
		fmt.Println("Invalid region:", region)
		os.Exit(1)
	}

	// handle fuse mount options
	mountOptions := []fuse.MountOption{
		fuse.FSName("cfs"),
		fuse.Subtype("cfs"),
		fuse.DefaultPermissions(),
		fuse.MaxReadahead(128 * 1024),
		fuse.AsyncRead(),
		//fuse.WritebackCache(),
		fuse.AutoInvalData(),
	}

	// parse mount options string
	clargs := getArgs(options)

	// handle debug mount option
	_, debug := clargs["debug"]
	if !debug {
		log.SetFlags(0)
		log.SetOutput(ioutil.Discard)
	}

	// handle allow_other mount option
	_, allowOther := clargs["allow_other"]
	if allowOther {
		mountOptions = append(mountOptions, fuse.AllowOther())
	}

	// handle ro mount option
	_, readOnly := clargs["ro"]
	if readOnly {
		mountOptions = append(mountOptions, fuse.ReadOnly())
	}

	// perform fuse mount
	fusermountPath()
	cfs, err := fuse.Mount(mountpoint, mountOptions...)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	defer cfs.Close()

	// setup rpc client
	rpc := newrpc(addr)
	fs := newfs(cfs, rpc, fsid)
	err = fs.InitFs()
	if err != nil {
		fmt.Println(err)
		exec.Command("fusermount", "-uz", mountpoint).Run()
		os.Exit(1)
	}
	srv := newserver(fs)

	if err := srv.serve(); err != nil {
		fmt.Println(err)
		exec.Command("fusermount", "-uz", mountpoint).Run()
		os.Exit(1)
	}

	<-cfs.Ready
	if err := cfs.MountError; err != nil {
		fmt.Println(err)
		exec.Command("fusermount", "-uz", mountpoint).Run()
		os.Exit(1)
	}
	return nil
}

func main() {

	// try to read config file
	configured := false
	config := map[string]string{}
	user, err := user.Current()
	if err != nil {
		fmt.Printf("Unable to identify curent user: %v\n", err)
		os.Exit(1)
	}
	var configfile string
	if user.HomeDir != "" {
		configfile = user.HomeDir + "/.cfs.json"
	} else {
		configfile = ".cfs.json"
	}
	f, err := ioutil.ReadFile(configfile)
	if err == nil {
		json.Unmarshal(f, &config)
		configured = true
	}
	region, _ := config["region"]
	username, _ := config["username"]
	apikey, _ := config["apikey"]

	flag.Usage = func() {
		fmt.Println("Usage:")
		fmt.Println("    cfs <command> [options] <args>")
		fmt.Println("Commands:")
		fmt.Println("    configure    create configuration file")
		fmt.Println("    list         list all filesystems")
		fmt.Println("    create       create a new filesytem")
		fmt.Println("    show         show filesystem details")
		fmt.Println("    update       update filesystem name")
		fmt.Println("    delete       delete an existing filesystem")
		fmt.Println("    grant        grant access to a filesystem")
		fmt.Println("    revoke       revoke access to a filesystem")
		fmt.Println("    mount        mount an existing filesystem")
		fmt.Println("    version      show client version")
		fmt.Println("    cp           parallel cp command; cfs cp --help")
		fmt.Println("    help         show usage for cfs")
		fmt.Println("Examples:")
		fmt.Println("    cfs configure")
		fmt.Println("    cfs create <name>")
		fmt.Println("    cfs grant <ip> <fsid>")
		fmt.Println("    cfs mount <region>:<fsid> <mountpoint>")
		os.Exit(1)
	}
	flag.Parse()
	if flag.NArg() == 0 {
		flag.Usage()
	}
	command := flag.Args()[0]
	switch command {
	default:
		fmt.Println("Invalid Command")
		flag.Usage()
	case "help":
		flag.Usage()
	case "version":
		fmt.Println("version:", cfsVersion)
		fmt.Println("commit:", commitVersion)
		fmt.Println("build date:", buildDate)
		fmt.Println("go version:", goVersion)
		os.Exit(0)
	case "configure":
		err := configure(configfile)
		if err != nil {
			fmt.Printf("Error writing config file: %v\n", err)
			os.Exit(1)
		}

	case "list":
		if !configured {
			fmt.Println("You must run \"cfs configure\" first.")
			os.Exit(1)
		}
		err := list(region, username, apikey)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	case "show":
		if !configured {
			fmt.Println("You must run \"cfs configure\" first.")
			os.Exit(1)
		}
		err := show(region, username, apikey)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	case "create":
		if !configured {
			fmt.Println("You must run \"cfs configure\" first.")
			os.Exit(1)
		}
		err := create(region, username, apikey)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	case "delete":
		if !configured {
			fmt.Println("You must run \"cfs configure\" first.")
			os.Exit(1)
		}
		err := del(region, username, apikey)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	case "update":
		if !configured {
			fmt.Println("You must run \"cfs configure\" first.")
			os.Exit(1)
		}
		err := update(region, username, apikey)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	case "grant":
		if !configured {
			fmt.Println("You must run \"cfs configure\" first.")
			os.Exit(1)
		}
		err := grant(region, username, apikey)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	case "revoke":
		if !configured {
			fmt.Println("You must run \"cfs configure\" first.")
			os.Exit(1)
		}
		err := revoke(region, username, apikey)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	case "mount":
		err := mount()
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	case "cp":
		if err := cpcp.CPCP(os.Args[2:]); err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	}
}

func getArgs(args string) map[string]string {
	clargs := make(map[string]string)
	optList := strings.Split(args, ",")
	for _, item := range optList {
		if strings.Contains(item, "=") {
			value := strings.Split(item, "=")
			if value[0] == "" || value[1] == "" {
				log.Printf("Invalid option %s, %s no value\n\n", value[0], value[1])
				os.Exit(1)
			} else {
				clargs[value[0]] = value[1]
			}
		} else {
			clargs[item] = ""
		}
	}
	return clargs
}

// the mount command needs the path to fusermount
func fusermountPath() {
	// Grab the current path
	currentPath := os.Getenv("PATH")
	if len(currentPath) == 0 {
		// fusermount location for suse  /usr/bin
		// fusermount location on debian based distros /bin
		os.Setenv("PATH", "/usr/bin:/bin")
	}
}

// setupWS ...
func setupWS(svr string) *grpc.ClientConn {
	var opts []grpc.DialOption
	creds := credentials.NewTLS(&tls.Config{
		InsecureSkipVerify: true,
	})
	opts = append(opts, grpc.WithTransportCredentials(creds))
	conn, err := grpc.Dial(svr, opts...)
	if err != nil {
		log.Fatalf("failed to dial: %v", err)
	}
	return conn
}
<commit_msg>Use flag.Args()[1:] for safety<commit_after>package main

import (
	"crypto/tls"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"os"
	"os/exec"
	"os/user"
	"strings"
	"sync"

	"github.com/getcfs/fuse"
	pb "github.com/getcfs/megacfs/formic/proto"
	"github.com/gholt/cpcp"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

type server struct {
	fs *fs
	wg sync.WaitGroup
}

func newserver(fs *fs) *server {
	s := &server{
		fs: fs,
	}
	return s
}

func (s *server) serve() error {
	defer s.wg.Wait()

	for {
		req, err := s.fs.conn.ReadRequest()
		if err != nil {
			if err == io.EOF {
				break
			}
			return err
		}
		s.wg.Add(1)
		go func() {
			defer s.wg.Done()
			s.fs.handle(req)
		}()
	}
	return nil
}

func debuglog(msg interface{}) {
	fmt.Fprintf(os.Stderr, "%v\n", msg)
}

type rpc struct {
	apiClients []pb.ApiClient
}

func newrpc(addr string) *rpc {
	var opts []grpc.DialOption
	creds := credentials.NewTLS(&tls.Config{
		InsecureSkipVerify: true,
	})
	opts = append(opts, grpc.WithTransportCredentials(creds))
	clients := []pb.ApiClient{}

	// TODO: Rework this simplistic connection pooling
	for i := 0; i < 1; i++ { // hardcoded to 1 connection for now
		conn, err := grpc.Dial(addr, opts...)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		clients = append(clients, pb.NewApiClient(conn))
	}
	r := &rpc{
		apiClients: clients,
	}

	return r
}

func (r *rpc) api() pb.ApiClient {
	return r.apiClients[rand.Intn(len(r.apiClients))]
}

// NullWriter ...
type NullWriter int

func (NullWriter) Write([]byte) (int, error) { return 0, nil }

var cfsVersion string
var buildDate string
var commitVersion string
var goVersion string

type authResponse struct {
	Access struct {
		Token struct {
			ID string `json:"id"`
		} `json:"token"`
	} `json:"access"`
}

func auth(username string, apikey string) string {
	body := fmt.Sprintf(`{"auth":{"RAX-KSKEY:apiKeyCredentials":{"username": "%s","apiKey": "%s"}}}`, username, apikey)
	rbody := strings.NewReader(body)
	req, err := http.NewRequest("POST", "https://identity.api.rackspacecloud.com/v2.0/tokens", rbody)
	if err != nil {
		fmt.Printf("%v", err)
		os.Exit(1)
	}
	req.Header.Set("Content-Type", "application/json")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		fmt.Printf("%v", err)
		os.Exit(1)
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		fmt.Println(resp.Status)
		os.Exit(1)
	}

	// parse token from response
	var authResp authResponse
	r, _ := ioutil.ReadAll(resp.Body)
	json.Unmarshal(r, &authResp)
	token := authResp.Access.Token.ID

	return token
}

func mount() error {
	f := flag.NewFlagSet("mount", flag.ContinueOnError)
	f.Usage = func() {
		fmt.Println("Usage:")
		fmt.Println("    cfs mount [-o option,...] <region>:<fsid> <mountpoint>")
		fmt.Println("Options:")
		fmt.Println("    -o debug          enables debug output")
		fmt.Println("    -o ro             mount the filesystem read only")
		fmt.Println("    -o allow_other    allow access to other users")
		fmt.Println("Examples:")
		fmt.Println("    cfs mount iad:11111111-1111-1111-1111-111111111111 /mnt/test")
		fmt.Println("    cfs mount -o debug,ro iad:11111111-1111-1111-1111-111111111111 /mnt/test")
		os.Exit(1)
	}
	var options string
	f.StringVar(&options, "o", "", "")
	f.Parse(flag.Args()[1:])
	if f.NArg() != 2 {
		f.Usage()
	}
	regionFsid := f.Args()[0]
	parts := strings.Split(regionFsid, ":")
	if len(parts) != 2 {
		fmt.Println("Invalid filesystem:", regionFsid)
		f.Usage()
	}
	region := strings.ToLower(parts[0])
	fsid := parts[1]
	mountpoint := f.Args()[1]
	// Verify mountpoint exists
	_, err := os.Stat(mountpoint)
	if os.IsNotExist(err) {
		fmt.Printf("Mount point %s does not exist\n", mountpoint)
		os.Exit(1)
	}
	// Verify region
	addr, ok := regions[region]
	if !ok {
		fmt.Println("Invalid region:", region)
		os.Exit(1)
	}

	// handle fuse mount options
	mountOptions := []fuse.MountOption{
		fuse.FSName("cfs"),
		fuse.Subtype("cfs"),
		fuse.DefaultPermissions(),
		fuse.MaxReadahead(128 * 1024),
		fuse.AsyncRead(),
		//fuse.WritebackCache(),
		fuse.AutoInvalData(),
	}

	// parse mount options string
	clargs := getArgs(options)

	// handle debug mount option
	_, debug := clargs["debug"]
	if !debug {
		log.SetFlags(0)
		log.SetOutput(ioutil.Discard)
	}

	// handle allow_other mount option
	_, allowOther := clargs["allow_other"]
	if allowOther {
		mountOptions = append(mountOptions, fuse.AllowOther())
	}

	// handle ro mount option
	_, readOnly := clargs["ro"]
	if readOnly {
		mountOptions = append(mountOptions, fuse.ReadOnly())
	}

	// perform fuse mount
	fusermountPath()
	cfs, err := fuse.Mount(mountpoint, mountOptions...)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
	defer cfs.Close()

	// setup rpc client
	rpc := newrpc(addr)
	fs := newfs(cfs, rpc, fsid)
	err = fs.InitFs()
	if err != nil {
		fmt.Println(err)
		exec.Command("fusermount", "-uz", mountpoint).Run()
		os.Exit(1)
	}
	srv := newserver(fs)

	if err := srv.serve(); err != nil {
		fmt.Println(err)
		exec.Command("fusermount", "-uz", mountpoint).Run()
		os.Exit(1)
	}

	<-cfs.Ready
	if err := cfs.MountError; err != nil {
		fmt.Println(err)
		exec.Command("fusermount", "-uz", mountpoint).Run()
		os.Exit(1)
	}
	return nil
}

func main() {

	// try to read config file
	configured := false
	config := map[string]string{}
	user, err := user.Current()
	if err != nil {
		fmt.Printf("Unable to identify curent user: %v\n", err)
		os.Exit(1)
	}
	var configfile string
	if user.HomeDir != "" {
		configfile = user.HomeDir + "/.cfs.json"
	} else {
		configfile = ".cfs.json"
	}
	f, err := ioutil.ReadFile(configfile)
	if err == nil {
		json.Unmarshal(f, &config)
		configured = true
	}
	region, _ := config["region"]
	username, _ := config["username"]
	apikey, _ := config["apikey"]

	flag.Usage = func() {
		fmt.Println("Usage:")
		fmt.Println("    cfs <command> [options] <args>")
		fmt.Println("Commands:")
		fmt.Println("    configure    create configuration file")
		fmt.Println("    list         list all filesystems")
		fmt.Println("    create       create a new filesytem")
		fmt.Println("    show         show filesystem details")
		fmt.Println("    update       update filesystem name")
		fmt.Println("    delete       delete an existing filesystem")
		fmt.Println("    grant        grant access to a filesystem")
		fmt.Println("    revoke       revoke access to a filesystem")
		fmt.Println("    mount        mount an existing filesystem")
		fmt.Println("    version      show client version")
		fmt.Println("    cp           parallel cp command; cfs cp --help")
		fmt.Println("    help         show usage for cfs")
		fmt.Println("Examples:")
		fmt.Println("    cfs configure")
		fmt.Println("    cfs create <name>")
		fmt.Println("    cfs grant <ip> <fsid>")
		fmt.Println("    cfs mount <region>:<fsid> <mountpoint>")
		os.Exit(1)
	}
	flag.Parse()
	if flag.NArg() == 0 {
		flag.Usage()
	}
	command := flag.Args()[0]
	switch command {
	default:
		fmt.Println("Invalid Command")
		flag.Usage()
	case "help":
		flag.Usage()
	case "version":
		fmt.Println("version:", cfsVersion)
		fmt.Println("commit:", commitVersion)
		fmt.Println("build date:", buildDate)
		fmt.Println("go version:", goVersion)
		os.Exit(0)
	case "configure":
		err := configure(configfile)
		if err != nil {
			fmt.Printf("Error writing config file: %v\n", err)
			os.Exit(1)
		}

	case "list":
		if !configured {
			fmt.Println("You must run \"cfs configure\" first.")
			os.Exit(1)
		}
		err := list(region, username, apikey)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	case "show":
		if !configured {
			fmt.Println("You must run \"cfs configure\" first.")
			os.Exit(1)
		}
		err := show(region, username, apikey)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	case "create":
		if !configured {
			fmt.Println("You must run \"cfs configure\" first.")
			os.Exit(1)
		}
		err := create(region, username, apikey)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	case "delete":
		if !configured {
			fmt.Println("You must run \"cfs configure\" first.")
			os.Exit(1)
		}
		err := del(region, username, apikey)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	case "update":
		if !configured {
			fmt.Println("You must run \"cfs configure\" first.")
			os.Exit(1)
		}
		err := update(region, username, apikey)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	case "grant":
		if !configured {
			fmt.Println("You must run \"cfs configure\" first.")
			os.Exit(1)
		}
		err := grant(region, username, apikey)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	case "revoke":
		if !configured {
			fmt.Println("You must run \"cfs configure\" first.")
			os.Exit(1)
		}
		err := revoke(region, username, apikey)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	case "mount":
		err := mount()
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	case "cp":
		if err := cpcp.CPCP(flag.Args()[1:]); err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
	}
}

func getArgs(args string) map[string]string {
	clargs := make(map[string]string)
	optList := strings.Split(args, ",")
	for _, item := range optList {
		if strings.Contains(item, "=") {
			value := strings.Split(item, "=")
			if value[0] == "" || value[1] == "" {
				log.Printf("Invalid option %s, %s no value\n\n", value[0], value[1])
				os.Exit(1)
			} else {
				clargs[value[0]] = value[1]
			}
		} else {
			clargs[item] = ""
		}
	}
	return clargs
}

// the mount command needs the path to fusermount
func fusermountPath() {
	// Grab the current path
	currentPath := os.Getenv("PATH")
	if len(currentPath) == 0 {
		// fusermount location for suse  /usr/bin
		// fusermount location on debian based distros /bin
		os.Setenv("PATH", "/usr/bin:/bin")
	}
}

// setupWS ...
func setupWS(svr string) *grpc.ClientConn {
	var opts []grpc.DialOption
	creds := credentials.NewTLS(&tls.Config{
		InsecureSkipVerify: true,
	})
	opts = append(opts, grpc.WithTransportCredentials(creds))
	conn, err := grpc.Dial(svr, opts...)
	if err != nil {
		log.Fatalf("failed to dial: %v", err)
	}
	return conn
}
<|endoftext|>


<commit_before>package build

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"reflect"

	"github.com/sourcegraph/rwvfs"
	"github.com/sqs/go-makefile/makefile"

	"sourcegraph.com/sourcegraph/client"
	"sourcegraph.com/sourcegraph/repo"
	"sourcegraph.com/sourcegraph/srcgraph/build/buildstore"
	"sourcegraph.com/sourcegraph/srcgraph/unit"
)

var DataTypes = make(map[string]interface{})
var DataTypeNames = make(map[reflect.Type]string)

// RegisterDataType makes a build data type available by the provided name.
//
// When serializing build data of a certain type, the corresponding type name is
// included in the file basename. When deserializing build data from files, the
// file basename is parsed to determine the data type to deserialize into. For
// example, a data type with name "foo.v0" and type Foo{} would result in files
// named "whatever.foo.v0.json" being written, and those files would deserialize
// into Foo{} structs.
//
// The name should contain a version identifier so that types may be modified
// more easily; for example, name could be "graph.v0" (and a subsequent version
// could be registered as "graph.v1" with a different struct).
//
// The emptyInstance should be an empty instance of the type (usually a struct)
// that holds the build data; it is used to instantiate instances dynamically.
//
// If RegisterDataType is called twice with the same type or type name, if name
// is empty, or if emptyInstance is nil, it panics
func RegisterDataType(name string, emptyInstance interface{}) {
	if _, dup := DataTypes[name]; dup {
		panic("build: RegisterDataType called twice for type name " + name)
	}
	if emptyInstance == nil {
		panic("build: RegisterDataType emptyInstance is nil")
	}
	DataTypes[name] = emptyInstance

	typ := reflect.TypeOf(emptyInstance)
	if _, dup := DataTypeNames[typ]; dup {
		panic("build: RegisterDataType called twice for type " + typ.String())
	}
	if name == "" {
		panic("build: RegisterDataType name is nil")
	}
	DataTypeNames[typ] = name
	DataTypeNames[reflect.PtrTo(typ)] = name
}

var StorageDir = filepath.Join(os.TempDir(), "sg")

func init() {
	err := os.MkdirAll(StorageDir, 0700)
	if err != nil {
		log.Fatal(err)
	}
}

// localDataDir returns the directory in which to store build data for a
// specific commit in a repository.
func localDataDir(repoURI repo.URI, commitID string) string {
	return filepath.Join(StorageDir, string(repoURI), commitID)
}

var Storage = buildstore.WalkableRWVFS{rwvfs.OS(StorageDir)}

func dataTypeSuffix(typ reflect.Type) string {
	name, registered := DataTypeNames[typ]
	if !registered {
		panic("build: data type not registered: " + typ.String())
	}

	return name + ".json"
}

type DataFile interface {
	makefile.Target
	info() DataFileInfo
	filePath() string
}

type DataFileInfo struct {
	RepositoryURI repo.URI
	CommitID      string
	DataType      reflect.Type
}

type RepositoryCommitDataFile struct {
	DataFileInfo
}

func (f *RepositoryCommitDataFile) Name() string {
	return filepath.Join(localDataDir(f.RepositoryURI, f.CommitID), f.filePath())
}

func (f *RepositoryCommitDataFile) info() DataFileInfo { return f.DataFileInfo }
func (f *RepositoryCommitDataFile) filePath() string   { return dataTypeSuffix(f.DataType) }

type SourceUnitDataFile struct {
	DataFileInfo
	Unit unit.SourceUnit
}

func (f *SourceUnitDataFile) Name() string {
	return filepath.Join(localDataDir(f.RepositoryURI, f.CommitID), f.filePath())
}

func (f *SourceUnitDataFile) info() DataFileInfo { return f.DataFileInfo }

func (f *SourceUnitDataFile) filePath() string {
	return filepath.Clean(fmt.Sprintf("%s_%s", unit.MakeID(f.Unit), dataTypeSuffix(f.DataType)))
}

func DataFileSpec(f DataFile) client.BuildDataFileSpec {
	info := f.info()
	return client.BuildDataFileSpec{
		RepositorySpec: client.RepositorySpec{string(info.RepositoryURI)},
		CommitID:       info.CommitID,
		Path:           f.filePath(),
	}
}
<commit_msg>fix import<commit_after>package build

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"reflect"

	"github.com/sourcegraph/rwvfs"

	"sourcegraph.com/sourcegraph/client"
	"sourcegraph.com/sourcegraph/repo"
	"sourcegraph.com/sourcegraph/srcgraph/build/buildstore"
	"sourcegraph.com/sourcegraph/srcgraph/unit"
	"sourcegraph.com/sourcegraph/srcgraph/util2/makefile"
)

var DataTypes = make(map[string]interface{})
var DataTypeNames = make(map[reflect.Type]string)

// RegisterDataType makes a build data type available by the provided name.
//
// When serializing build data of a certain type, the corresponding type name is
// included in the file basename. When deserializing build data from files, the
// file basename is parsed to determine the data type to deserialize into. For
// example, a data type with name "foo.v0" and type Foo{} would result in files
// named "whatever.foo.v0.json" being written, and those files would deserialize
// into Foo{} structs.
//
// The name should contain a version identifier so that types may be modified
// more easily; for example, name could be "graph.v0" (and a subsequent version
// could be registered as "graph.v1" with a different struct).
//
// The emptyInstance should be an empty instance of the type (usually a struct)
// that holds the build data; it is used to instantiate instances dynamically.
//
// If RegisterDataType is called twice with the same type or type name, if name
// is empty, or if emptyInstance is nil, it panics
func RegisterDataType(name string, emptyInstance interface{}) {
	if _, dup := DataTypes[name]; dup {
		panic("build: RegisterDataType called twice for type name " + name)
	}
	if emptyInstance == nil {
		panic("build: RegisterDataType emptyInstance is nil")
	}
	DataTypes[name] = emptyInstance

	typ := reflect.TypeOf(emptyInstance)
	if _, dup := DataTypeNames[typ]; dup {
		panic("build: RegisterDataType called twice for type " + typ.String())
	}
	if name == "" {
		panic("build: RegisterDataType name is nil")
	}
	DataTypeNames[typ] = name
	DataTypeNames[reflect.PtrTo(typ)] = name
}

var StorageDir = filepath.Join(os.TempDir(), "sg")

func init() {
	err := os.MkdirAll(StorageDir, 0700)
	if err != nil {
		log.Fatal(err)
	}
}

// localDataDir returns the directory in which to store build data for a
// specific commit in a repository.
func localDataDir(repoURI repo.URI, commitID string) string {
	return filepath.Join(StorageDir, string(repoURI), commitID)
}

var Storage = buildstore.WalkableRWVFS{rwvfs.OS(StorageDir)}

func dataTypeSuffix(typ reflect.Type) string {
	name, registered := DataTypeNames[typ]
	if !registered {
		panic("build: data type not registered: " + typ.String())
	}

	return name + ".json"
}

type DataFile interface {
	makefile.Target
	info() DataFileInfo
	filePath() string
}

type DataFileInfo struct {
	RepositoryURI repo.URI
	CommitID      string
	DataType      reflect.Type
}

type RepositoryCommitDataFile struct {
	DataFileInfo
}

func (f *RepositoryCommitDataFile) Name() string {
	return filepath.Join(localDataDir(f.RepositoryURI, f.CommitID), f.filePath())
}

func (f *RepositoryCommitDataFile) info() DataFileInfo { return f.DataFileInfo }
func (f *RepositoryCommitDataFile) filePath() string   { return dataTypeSuffix(f.DataType) }

type SourceUnitDataFile struct {
	DataFileInfo
	Unit unit.SourceUnit
}

func (f *SourceUnitDataFile) Name() string {
	return filepath.Join(localDataDir(f.RepositoryURI, f.CommitID), f.filePath())
}

func (f *SourceUnitDataFile) info() DataFileInfo { return f.DataFileInfo }

func (f *SourceUnitDataFile) filePath() string {
	return filepath.Clean(fmt.Sprintf("%s_%s", unit.MakeID(f.Unit), dataTypeSuffix(f.DataType)))
}

func DataFileSpec(f DataFile) client.BuildDataFileSpec {
	info := f.info()
	return client.BuildDataFileSpec{
		RepositorySpec: client.RepositorySpec{string(info.RepositoryURI)},
		CommitID:       info.CommitID,
		Path:           f.filePath(),
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/getgauge/common"
	"github.com/getgauge/gauge/version"
)

const (
	CGO_ENABLED        = "CGO_ENABLED"
	GOARCH             = "GOARCH"
	GOOS               = "GOOS"
	X86                = "386"
	X86_64             = "amd64"
	darwin             = "darwin"
	linux              = "linux"
	windows            = "windows"
	bin                = "bin"
	gauge              = "gauge"
	gaugeScreenshot    = "gauge_screenshot"
	deploy             = "deploy"
	installShellScript = "install.sh"
	CC                 = "CC"
	pkg                = ".pkg"
	packagesBuild      = "packagesbuild"
	nightlyDatelayout  = "2006-01-02"
)

var darwinPackageProject = filepath.Join("build", "install", "macosx", "gauge-pkg.pkgproj")

var gaugeScreenshotLocation = filepath.Join("github.com", "getgauge", "gauge_screenshot")

var deployDir = filepath.Join(deploy, gauge)

func runProcess(command string, arg ...string) {
	cmd := exec.Command(command, arg...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	log.Printf("Execute %v\n", cmd.Args)
	err := cmd.Run()
	if err != nil {
		panic(err)
	}
}

func runCommand(command string, arg ...string) (string, error) {
	cmd := exec.Command(command, arg...)
	bytes, err := cmd.Output()
	return strings.TrimSpace(fmt.Sprintf("%s", bytes)), err
}

func signExecutable(exeFilePath string, certFilePath string, certFilePwd string) {
	if getGOOS() == windows {
		if certFilePath != "" && certFilePwd != "" {
			log.Printf("Signing: %s", exeFilePath)
			runProcess("signtool", "sign", "/f", certFilePath, "/p", certFilePwd, exeFilePath)
		} else {
			log.Printf("No certificate file passed. Executable won't be signed.")
		}
	}
}

var buildMetadata string

func getBuildVersion() string {
	if buildMetadata != "" {
		return fmt.Sprintf("%s.%s", version.CurrentGaugeVersion.String(), buildMetadata)
	}
	return version.CurrentGaugeVersion.String()
}

func compileGauge() {
	executablePath := getGaugeExecutablePath(gauge)
	runProcess("go", "build", "-ldflags", "-X github.com/getgauge/gauge/version.BuildMetadata="+buildMetadata, "-o", executablePath)
	compileGaugeScreenshot()
}

func compileGaugeScreenshot() {
	getGaugeScreenshot()
	executablePath := getGaugeExecutablePath(gaugeScreenshot)
	runProcess("go", "build", "-o", executablePath, gaugeScreenshotLocation)
}

func getGaugeScreenshot() {
	runProcess("go", "get", "-u", "-d", gaugeScreenshotLocation)
}

func runTests(coverage bool) {
	if coverage {
		runProcess("go", "test", "-covermode=count", "-coverprofile=count.out")
		if coverage {
			runProcess("go", "tool", "cover", "-html=count.out")
		}
	} else {
		runProcess("go", "test", "./...", "-v")
	}
}

// key will be the source file and value will be the target
func installFiles(files map[string]string, installDir string) {
	for src, dst := range files {
		base := filepath.Base(src)
		installDst := filepath.Join(installDir, dst)
		log.Printf("Install %s -> %s\n", src, installDst)
		stat, err := os.Stat(src)
		if err != nil {
			panic(err)
		}
		if stat.IsDir() {
			_, err = common.MirrorDir(src, installDst)
		} else {
			err = common.MirrorFile(src, filepath.Join(installDst, base))
		}
		if err != nil {
			panic(err)
		}
	}
}

func copyGaugeFiles(installPath string) {
	files := make(map[string]string)
	files[getGaugeExecutablePath(gauge)] = bin
	files[getGaugeExecutablePath(gaugeScreenshot)] = bin
	files[filepath.Join("skel", "example.spec")] = filepath.Join("share", gauge, "skel")
	files[filepath.Join("skel", "default.properties")] = filepath.Join("share", gauge, "skel", "env")
	files[filepath.Join("skel", "gauge.properties")] = filepath.Join("share", gauge)
	files[filepath.Join("notice.md")] = filepath.Join("share", gauge)
	files = addInstallScripts(files)
	installFiles(files, installPath)
}

func addInstallScripts(files map[string]string) map[string]string {
	if (getGOOS() == darwin || getGOOS() == linux) && (*distro) {
		files[filepath.Join("build", "install", installShellScript)] = ""
	} else if getGOOS() == windows {
		files[filepath.Join("build", "install", "windows", "plugin-install.bat")] = ""
		files[filepath.Join("build", "install", "windows", "backup_properties_file.bat")] = ""
		files[filepath.Join("build", "install", "windows", "set_timestamp.bat")] = ""
	}
	return files
}

func setEnv(envVariables map[string]string) {
	for k, v := range envVariables {
		os.Setenv(k, v)
	}
}

var test = flag.Bool("test", false, "Run the test cases")
var coverage = flag.Bool("coverage", false, "Run the test cases and show the coverage")
var install = flag.Bool("install", false, "Install to the specified prefix")
var nightly = flag.Bool("nightly", false, "Add nightly build information")
var gaugeInstallPrefix = flag.String("prefix", "", "Specifies the prefix where gauge files will be installed")
var allPlatforms = flag.Bool("all-platforms", false, "Compiles for all platforms windows, linux, darwin both x86 and x86_64")
var targetLinux = flag.Bool("target-linux", false, "Compiles for linux only, both x86 and x86_64")
var binDir = flag.String("bin-dir", "", "Specifies OS_PLATFORM specific binaries to install when cross compiling")
var distro = flag.Bool("distro", false, "Create gauge distributable")
var skipWindowsDistro = flag.Bool("skip-windows", false, "Skips creation of windows distributable on unix machines while cross platform compilation")
var certFile = flag.String("certFile", "", "Should be passed for signing the windows installer along with the password (certFilePwd)")
var certFilePwd = flag.String("certFilePwd", "", "Password for certificate that will be used to sign the windows installer")

// Defines all the compile targets
// Each target name is the directory name
var (
	platformEnvs = []map[string]string{
		map[string]string{GOARCH: X86, GOOS: darwin, CGO_ENABLED: "0"},
		map[string]string{GOARCH: X86_64, GOOS: darwin, CGO_ENABLED: "0"},
		map[string]string{GOARCH: X86, GOOS: linux, CGO_ENABLED: "0"},
		map[string]string{GOARCH: X86_64, GOOS: linux, CGO_ENABLED: "0"},
		map[string]string{GOARCH: X86, GOOS: windows, CC: "i586-mingw32-gcc", CGO_ENABLED: "1"},
		map[string]string{GOARCH: X86_64, GOOS: windows, CC: "x86_64-w64-mingw32-gcc", CGO_ENABLED: "1"},
	}
	osDistroMap = map[string]distroFunc{windows: createWindowsDistro, linux: createLinuxPackage, darwin: createDarwinPackage}
)

func main() {
	flag.Parse()
	if *nightly {
		buildMetadata = fmt.Sprintf("nightly-%s", time.Now().Format(nightlyDatelayout))
	}
	if *test {
		runTests(*coverage)
	} else if *install {
		installGauge()
	} else if *distro {
		createGaugeDistributables(*allPlatforms)
	} else {
		if *allPlatforms {
			crossCompileGauge()
		} else {
			compileGauge()
		}
	}
}

func filteredPlatforms() []map[string]string {
	filteredPlatformEnvs := platformEnvs[:0]
	for _, x := range platformEnvs {
		if *targetLinux {
			if x[GOOS] == linux {
				filteredPlatformEnvs = append(filteredPlatformEnvs, x)
			}
		} else {
			filteredPlatformEnvs = append(filteredPlatformEnvs, x)
		}
	}
	return filteredPlatformEnvs
}

func crossCompileGauge() {
	for _, platformEnv := range filteredPlatforms() {
		setEnv(platformEnv)
		log.Printf("Compiling for platform => OS:%s ARCH:%s \n", platformEnv[GOOS], platformEnv[GOARCH])
		compileGauge()
	}
}

func installGauge() {
	updateGaugeInstallPrefix()
	copyGaugeFiles(deployDir)
	if _, err := common.MirrorDir(deployDir, *gaugeInstallPrefix); err != nil {
		panic(fmt.Sprintf("Could not install gauge : %s", err))
	}
}

func createGaugeDistributables(forAllPlatforms bool) {
	if forAllPlatforms {
		for _, platformEnv := range filteredPlatforms() {
			setEnv(platformEnv)
			log.Printf("Creating distro for platform => OS:%s ARCH:%s \n", platformEnv[GOOS], platformEnv[GOARCH])
			createDistro()
		}
	} else {
		createDistro()
	}
}

type distroFunc func()

func createDistro() {
	osDistroMap[getGOOS()]()
}

func createWindowsDistro() {
	if !*skipWindowsDistro {
		createWindowsInstaller()
	}
}

func createWindowsInstaller() {
	pName := packageName()
	distroDir, err := filepath.Abs(filepath.Join(deploy, pName))
	installerFileName := filepath.Join(filepath.Dir(distroDir), pName)
	if err != nil {
		panic(err)
	}
	copyGaugeFiles(distroDir)
	createZipFromUtil(deploy, pName)
	runProcess("makensis.exe",
		fmt.Sprintf("/DPRODUCT_VERSION=%s", getBuildVersion()),
		fmt.Sprintf("/DGAUGE_DISTRIBUTABLES_DIR=%s", distroDir),
		fmt.Sprintf("/DOUTPUT_FILE_NAME=%s.exe", installerFileName),
		filepath.Join("build", "install", "windows", "gauge-install.nsi"))
	os.RemoveAll(distroDir)
	signExecutable(installerFileName+".exe", *certFile, *certFilePwd)
}

func createDarwinPackage() {
	distroDir := filepath.Join(deploy, gauge)
	copyGaugeFiles(distroDir)
	createZipFromUtil(deploy, packageName())
	runProcess(packagesBuild, "-v", darwinPackageProject)
	runProcess("mv", filepath.Join(deploy, gauge+pkg), filepath.Join(deploy, fmt.Sprintf("%s-%s-%s.%s%s", gauge, getBuildVersion(), getGOOS(), getPackageArchSuffix(), pkg)))
	os.RemoveAll(distroDir)
}

func createLinuxPackage() {
	distroDir := filepath.Join(deploy, packageName())
	copyGaugeFiles(distroDir)
	createZipFromUtil(deploy, packageName())
	os.RemoveAll(distroDir)
}

func packageName() string {
	return fmt.Sprintf("%s-%s-%s.%s", gauge, getBuildVersion(), getGOOS(), getPackageArchSuffix())
}

func createZipFromUtil(dir, name string) {
	wd, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	os.Chdir(filepath.Join(dir, name))
	output, err := runCommand("zip", "-r", filepath.Join("..", name+".zip"), ".")
	fmt.Println(output)
	if err != nil {
		panic(fmt.Sprintf("Failed to zip: %s", err))
	}
	os.Chdir(wd)
}

func updateGaugeInstallPrefix() {
	if *gaugeInstallPrefix == "" {
		if runtime.GOOS == "windows" {
			*gaugeInstallPrefix = os.Getenv("PROGRAMFILES")
			if *gaugeInstallPrefix == "" {
				panic(fmt.Errorf("Failed to find programfiles"))
			}
			*gaugeInstallPrefix = filepath.Join(*gaugeInstallPrefix, gauge)
		} else {
			*gaugeInstallPrefix = "/usr/local"
		}
	}
}

func getGaugeExecutablePath(file string) string {
	return filepath.Join(getBinDir(), getExecutableName(file))
}

func getBinDir() string {
	if *binDir != "" {
		return *binDir
	}
	return filepath.Join(bin, fmt.Sprintf("%s_%s", getGOOS(), getGOARCH()))
}

func getExecutableName(file string) string {
	if getGOOS() == windows {
		return file + ".exe"
	}
	return file
}

func getGOARCH() string {
	goArch := os.Getenv(GOARCH)
	if goArch == "" {
		goArch = runtime.GOARCH
	}
	return goArch
}

func getGOOS() string {
	goOS := os.Getenv(GOOS)
	if goOS == "" {
		goOS = runtime.GOOS
	}
	return goOS
}

func getPackageArchSuffix() string {
	if strings.HasSuffix(*binDir, "386") {
		return "x86"
	}

	if strings.HasSuffix(*binDir, "amd64") {
		return "x86_64"
	}

	if arch := getGOARCH(); arch == X86 {
		return "x86"
	}
	return "x86_64"
}
<commit_msg>Fix zip generation for OSX and Linux.<commit_after>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"github.com/getgauge/common"
	"github.com/getgauge/gauge/version"
)

const (
	CGO_ENABLED        = "CGO_ENABLED"
	GOARCH             = "GOARCH"
	GOOS               = "GOOS"
	X86                = "386"
	X86_64             = "amd64"
	darwin             = "darwin"
	linux              = "linux"
	windows            = "windows"
	bin                = "bin"
	gauge              = "gauge"
	gaugeScreenshot    = "gauge_screenshot"
	deploy             = "deploy"
	installShellScript = "install.sh"
	CC                 = "CC"
	pkg                = ".pkg"
	packagesBuild      = "packagesbuild"
	nightlyDatelayout  = "2006-01-02"
)

var darwinPackageProject = filepath.Join("build", "install", "macosx", "gauge-pkg.pkgproj")

var gaugeScreenshotLocation = filepath.Join("github.com", "getgauge", "gauge_screenshot")

var deployDir = filepath.Join(deploy, gauge)

func runProcess(command string, arg ...string) {
	cmd := exec.Command(command, arg...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	log.Printf("Execute %v\n", cmd.Args)
	err := cmd.Run()
	if err != nil {
		panic(err)
	}
}

func runCommand(command string, arg ...string) (string, error) {
	cmd := exec.Command(command, arg...)
	bytes, err := cmd.Output()
	return strings.TrimSpace(fmt.Sprintf("%s", bytes)), err
}

func signExecutable(exeFilePath string, certFilePath string, certFilePwd string) {
	if getGOOS() == windows {
		if certFilePath != "" && certFilePwd != "" {
			log.Printf("Signing: %s", exeFilePath)
			runProcess("signtool", "sign", "/f", certFilePath, "/p", certFilePwd, exeFilePath)
		} else {
			log.Printf("No certificate file passed. Executable won't be signed.")
		}
	}
}

var buildMetadata string

func getBuildVersion() string {
	if buildMetadata != "" {
		return fmt.Sprintf("%s.%s", version.CurrentGaugeVersion.String(), buildMetadata)
	}
	return version.CurrentGaugeVersion.String()
}

func compileGauge() {
	executablePath := getGaugeExecutablePath(gauge)
	runProcess("go", "build", "-ldflags", "-X github.com/getgauge/gauge/version.BuildMetadata="+buildMetadata, "-o", executablePath)
	compileGaugeScreenshot()
}

func compileGaugeScreenshot() {
	getGaugeScreenshot()
	executablePath := getGaugeExecutablePath(gaugeScreenshot)
	runProcess("go", "build", "-o", executablePath, gaugeScreenshotLocation)
}

func getGaugeScreenshot() {
	runProcess("go", "get", "-u", "-d", gaugeScreenshotLocation)
}

func runTests(coverage bool) {
	if coverage {
		runProcess("go", "test", "-covermode=count", "-coverprofile=count.out")
		if coverage {
			runProcess("go", "tool", "cover", "-html=count.out")
		}
	} else {
		runProcess("go", "test", "./...", "-v")
	}
}

// key will be the source file and value will be the target
func installFiles(files map[string]string, installDir string) {
	for src, dst := range files {
		base := filepath.Base(src)
		installDst := filepath.Join(installDir, dst)
		log.Printf("Install %s -> %s\n", src, installDst)
		stat, err := os.Stat(src)
		if err != nil {
			panic(err)
		}
		if stat.IsDir() {
			_, err = common.MirrorDir(src, installDst)
		} else {
			err = common.MirrorFile(src, filepath.Join(installDst, base))
		}
		if err != nil {
			panic(err)
		}
	}
}

func copyGaugeFiles(installPath string) {
	files := make(map[string]string)
	files[getGaugeExecutablePath(gauge)] = bin
	files[getGaugeExecutablePath(gaugeScreenshot)] = bin
	files[filepath.Join("skel", "example.spec")] = filepath.Join("share", gauge, "skel")
	files[filepath.Join("skel", "default.properties")] = filepath.Join("share", gauge, "skel", "env")
	files[filepath.Join("skel", "gauge.properties")] = filepath.Join("share", gauge)
	files[filepath.Join("notice.md")] = filepath.Join("share", gauge)
	files = addInstallScripts(files)
	installFiles(files, installPath)
}

func addInstallScripts(files map[string]string) map[string]string {
	if (getGOOS() == darwin || getGOOS() == linux) && (*distro) {
		files[filepath.Join("build", "install", installShellScript)] = ""
	} else if getGOOS() == windows {
		files[filepath.Join("build", "install", "windows", "plugin-install.bat")] = ""
		files[filepath.Join("build", "install", "windows", "backup_properties_file.bat")] = ""
		files[filepath.Join("build", "install", "windows", "set_timestamp.bat")] = ""
	}
	return files
}

func setEnv(envVariables map[string]string) {
	for k, v := range envVariables {
		os.Setenv(k, v)
	}
}

var test = flag.Bool("test", false, "Run the test cases")
var coverage = flag.Bool("coverage", false, "Run the test cases and show the coverage")
var install = flag.Bool("install", false, "Install to the specified prefix")
var nightly = flag.Bool("nightly", false, "Add nightly build information")
var gaugeInstallPrefix = flag.String("prefix", "", "Specifies the prefix where gauge files will be installed")
var allPlatforms = flag.Bool("all-platforms", false, "Compiles for all platforms windows, linux, darwin both x86 and x86_64")
var targetLinux = flag.Bool("target-linux", false, "Compiles for linux only, both x86 and x86_64")
var binDir = flag.String("bin-dir", "", "Specifies OS_PLATFORM specific binaries to install when cross compiling")
var distro = flag.Bool("distro", false, "Create gauge distributable")
var skipWindowsDistro = flag.Bool("skip-windows", false, "Skips creation of windows distributable on unix machines while cross platform compilation")
var certFile = flag.String("certFile", "", "Should be passed for signing the windows installer along with the password (certFilePwd)")
var certFilePwd = flag.String("certFilePwd", "", "Password for certificate that will be used to sign the windows installer")

// Defines all the compile targets
// Each target name is the directory name
var (
	platformEnvs = []map[string]string{
		map[string]string{GOARCH: X86, GOOS: darwin, CGO_ENABLED: "0"},
		map[string]string{GOARCH: X86_64, GOOS: darwin, CGO_ENABLED: "0"},
		map[string]string{GOARCH: X86, GOOS: linux, CGO_ENABLED: "0"},
		map[string]string{GOARCH: X86_64, GOOS: linux, CGO_ENABLED: "0"},
		map[string]string{GOARCH: X86, GOOS: windows, CC: "i586-mingw32-gcc", CGO_ENABLED: "1"},
		map[string]string{GOARCH: X86_64, GOOS: windows, CC: "x86_64-w64-mingw32-gcc", CGO_ENABLED: "1"},
	}
	osDistroMap = map[string]distroFunc{windows: createWindowsDistro, linux: createLinuxPackage, darwin: createDarwinPackage}
)

func main() {
	flag.Parse()
	if *nightly {
		buildMetadata = fmt.Sprintf("nightly-%s", time.Now().Format(nightlyDatelayout))
	}
	if *test {
		runTests(*coverage)
	} else if *install {
		installGauge()
	} else if *distro {
		createGaugeDistributables(*allPlatforms)
	} else {
		if *allPlatforms {
			crossCompileGauge()
		} else {
			compileGauge()
		}
	}
}

func filteredPlatforms() []map[string]string {
	filteredPlatformEnvs := platformEnvs[:0]
	for _, x := range platformEnvs {
		if *targetLinux {
			if x[GOOS] == linux {
				filteredPlatformEnvs = append(filteredPlatformEnvs, x)
			}
		} else {
			filteredPlatformEnvs = append(filteredPlatformEnvs, x)
		}
	}
	return filteredPlatformEnvs
}

func crossCompileGauge() {
	for _, platformEnv := range filteredPlatforms() {
		setEnv(platformEnv)
		log.Printf("Compiling for platform => OS:%s ARCH:%s \n", platformEnv[GOOS], platformEnv[GOARCH])
		compileGauge()
	}
}

func installGauge() {
	updateGaugeInstallPrefix()
	copyGaugeFiles(deployDir)
	if _, err := common.MirrorDir(deployDir, *gaugeInstallPrefix); err != nil {
		panic(fmt.Sprintf("Could not install gauge : %s", err))
	}
}

func createGaugeDistributables(forAllPlatforms bool) {
	if forAllPlatforms {
		for _, platformEnv := range filteredPlatforms() {
			setEnv(platformEnv)
			log.Printf("Creating distro for platform => OS:%s ARCH:%s \n", platformEnv[GOOS], platformEnv[GOARCH])
			createDistro()
		}
	} else {
		createDistro()
	}
}

type distroFunc func()

func createDistro() {
	osDistroMap[getGOOS()]()
}

func createWindowsDistro() {
	if !*skipWindowsDistro {
		createWindowsInstaller()
	}
}

func createWindowsInstaller() {
	pName := packageName()
	distroDir, err := filepath.Abs(filepath.Join(deploy, pName))
	installerFileName := filepath.Join(filepath.Dir(distroDir), pName)
	if err != nil {
		panic(err)
	}
	copyGaugeFiles(distroDir)
	createZipFromUtil(deploy, gauge, pName)
	runProcess("makensis.exe",
		fmt.Sprintf("/DPRODUCT_VERSION=%s", getBuildVersion()),
		fmt.Sprintf("/DGAUGE_DISTRIBUTABLES_DIR=%s", distroDir),
		fmt.Sprintf("/DOUTPUT_FILE_NAME=%s.exe", installerFileName),
		filepath.Join("build", "install", "windows", "gauge-install.nsi"))
	os.RemoveAll(distroDir)
	signExecutable(installerFileName+".exe", *certFile, *certFilePwd)
}

func createDarwinPackage() {
	distroDir := filepath.Join(deploy, gauge)
	copyGaugeFiles(distroDir)
	createZipFromUtil(deploy, gauge, packageName())
	runProcess(packagesBuild, "-v", darwinPackageProject)
	runProcess("mv", filepath.Join(deploy, gauge+pkg), filepath.Join(deploy, fmt.Sprintf("%s-%s-%s.%s%s", gauge, getBuildVersion(), getGOOS(), getPackageArchSuffix(), pkg)))
	os.RemoveAll(distroDir)
}

func createLinuxPackage() {
	distroDir := filepath.Join(deploy, packageName())
	copyGaugeFiles(distroDir)
	createZipFromUtil(deploy, packageName(), packageName())
	os.RemoveAll(distroDir)
}

func packageName() string {
	return fmt.Sprintf("%s-%s-%s.%s", gauge, getBuildVersion(), getGOOS(), getPackageArchSuffix())
}

func createZipFromUtil(dir, zipDir, pkgName string) {
	wd, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	err = os.Chdir(filepath.Join(dir, zipDir))
	if err != nil {
		panic(fmt.Sprintf("Failed to change directory: %s", err))
	}
	output, err := runCommand("zip", "-r", filepath.Join("..", pkgName+".zip"), ".")
	fmt.Println(output)
	if err != nil {
		panic(fmt.Sprintf("Failed to zip: %s", err))
	}
	os.Chdir(wd)
}

func updateGaugeInstallPrefix() {
	if *gaugeInstallPrefix == "" {
		if runtime.GOOS == "windows" {
			*gaugeInstallPrefix = os.Getenv("PROGRAMFILES")
			if *gaugeInstallPrefix == "" {
				panic(fmt.Errorf("Failed to find programfiles"))
			}
			*gaugeInstallPrefix = filepath.Join(*gaugeInstallPrefix, gauge)
		} else {
			*gaugeInstallPrefix = "/usr/local"
		}
	}
}

func getGaugeExecutablePath(file string) string {
	return filepath.Join(getBinDir(), getExecutableName(file))
}

func getBinDir() string {
	if *binDir != "" {
		return *binDir
	}
	return filepath.Join(bin, fmt.Sprintf("%s_%s", getGOOS(), getGOARCH()))
}

func getExecutableName(file string) string {
	if getGOOS() == windows {
		return file + ".exe"
	}
	return file
}

func getGOARCH() string {
	goArch := os.Getenv(GOARCH)
	if goArch == "" {
		goArch = runtime.GOARCH
	}
	return goArch
}

func getGOOS() string {
	goOS := os.Getenv(GOOS)
	if goOS == "" {
		goOS = runtime.GOOS
	}
	return goOS
}

func getPackageArchSuffix() string {
	if strings.HasSuffix(*binDir, "386") {
		return "x86"
	}

	if strings.HasSuffix(*binDir, "amd64") {
		return "x86_64"
	}

	if arch := getGOARCH(); arch == X86 {
		return "x86"
	}
	return "x86_64"
}
<|endoftext|>


<commit_before>package psl

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
)

var archivHeader = []string{
	"Poradové číslo", "Kombinacie", "P", "N", "PR", "Mc", "Vc", "c1-c9", "C0", "cC", "Cc",
	"CC", "ZH", "Sm", "\u0394Sm", "Kk", "\u0394Kk", "N-tice", "X-tice", "ƩR1-DO", "ΔƩR1-DO",
	"ƩR1-DO \"r+1\"", "ƩSTL1-DO", "ΔƩSTL1-DO", "ƩSTL1-DO \"r+1\"", "Δ(ƩR1-DO-ƩSTL1-DO)",
	"HHRX", "ΔHHRX", "ƩR OD-DO", "ΔƩR OD-DO", "ƩSTL OD-DO", "ΔƩSTL OD-DO",
	"Δ(ƩROD-DO-ƩSTLOD-DO)", "HRX", "ΔHRX", "ƩKombinacie", "ΔƩKombinacie",
	"UC číslo", "UC riadok",
	"Cifra 1", "Cifra 2", "Cifra 3", "Cifra 4", "Cifra 5", "Cifra 6", "Cifra 7", "Cifra 8", "Cifra 9", "Cifra 0",
}

type Archiv struct {
	n, m int
	Riadok
	Hrx  *H
	HHrx *H

	WorkingDir string
	Suffix     string

	origHeader []string
	riadky     []Riadok

	Skupiny Skupiny
}

func NewArchiv(workingDir string, n, m int) *Archiv {
	archiv := &Archiv{
		n: n,
		m: m,
		Riadok: Riadok{
			n: n,
			m: m,
		},
		Hrx:  NewHrx(n, m),
		HHrx: NewHHrx(n, m),

		WorkingDir: workingDir,
		Suffix:     filepath.Base(workingDir),
	}
	return archiv
}

func (a *Archiv) write(chanErrKomb chan ErrKomb) error {

	w := NewCsvMaxWriter(a.WorkingDir, "Archiv", [][]string{archivHeader})
	defer w.Close()

	var (
		kombinacie = make([][]byte, 0, 64)
	)
	for errKomb := range chanErrKomb {
		a.Pc++

		// Skontrolujem ci nenastala chyba pri parsovani
		if errKomb.Err != nil {
			return errKomb.Err
		}
		// Treba odkladat kombinacie, kvoli spatnemu
		// dohladaniu Uc
		kombinacie = append(kombinacie, errKomb.Komb)

		// Prechadzame kombinaciu na riadku
		// a inkrementujeme pocetnost cisla R1-Do
		for y, x := range errKomb.Komb {
			a.HHrx.Add(int(x), y)
		}
		// Ak sa v hhrx vyskytli vsetky cisla 1..m
		// nastala udalost 101
		if a.HHrx.Is101() {
			// Ked narazime na Uc cislo na riadku Roddo
			// potrebuje spatne dohladat Uc cislo
			var reverse bool

			// Uc cislo je 0 len raz po udalosti 101
			if a.Uc.Cislo == 0 {
				reverse = true
			} else {
				// Incrementovanie cisla Roddo, resp. Hrx
				for y, x := range errKomb.Komb {
					// Ak na riadku narazime na Uc Cislo
					// porebujeme ho spatne dohladat
					if x == a.Uc.Cislo {
						reverse = true
					}
					a.Hrx.Add(int(x), y)
				}
			}
			// Spatne dohladanie Uc cisla a riadku a inrementovanie cisiel Roddo
			if reverse {
				// Nova hrx zostava a resetovanie cisiel Roddo
				a.Hrx = NewHrx(a.n, a.m)
				uc := Uc{Riadok: len(kombinacie)}
				// Spatne nacitava kombinacie a incremtuje Roddo
				// a Hrx az pokial nenastane udalost 101
				// udalost 101 nastava ked sa kazde cislo vyskytne aspon 1
				for !a.Hrx.Is101() {
					uc.Riadok--
					for y, x := range kombinacie[uc.Riadok] {
						a.Hrx.Add(int(x), y)
						if a.Hrx.Is101() && uc.Cislo == 0 {
							uc.Cislo = x
						}
					}
				}
				// Nastavenie noveho Uc cisla a riadku pre archiv
				a.Uc.Cislo = uc.Cislo
				a.Uc.Riadok += uc.Riadok

				// Mozeme predchadzajuce kombinacie zahodit
				// a zaujimaju nas uz len od posledneho Uc riadku
				kombinacie = kombinacie[uc.Riadok:]
			}
		}

		// Zostavenie Riadkov pre Archiv
		// pre 2 a viac riadkov robime rozdiel(diff) vybranych hodnot
		if a.Pc > 1 {
			a.Add(errKomb.Komb, a.HHrx.Cisla, a.Hrx.Cisla, a.Hrx.Value(), a.HHrx.Value())
		} else {
			a.Uc.Riadok++                                            // TODO: nespravne ukazovanie uc predtym nez nastane 101
			a.Add(errKomb.Komb, a.HHrx.Cisla, a.Hrx.Cisla, 100, 100) // V prvom riadku hodnoty hrx a hhrx su nastavene natvrdo 100
		}
		a.origHeader = errKomb.Header
		a.Riadok.origStrings = errKomb.Orig
		// fmt.Println(a.Riadok.origStrings)
		a.riadky = append(a.riadky, a.Riadok)
		// fmt.Println(a.Riadok.origStrings)

		if err := w.Write(a.Riadok.record()); err != nil {
			return err
		}
	}
	return nil
}

func Make(path, workingDir string, n, m int) (*Archiv, error) {

	if path == "" {
		return nil, fmt.Errorf("Nebola zadana cesta k súboru!")
	}
	if n < 2 || n >= m || m > 99 {
		return nil, fmt.Errorf("Nesprávny rozmer databázy: %d/%d", n, m)
	}
	dir := strings.Split(filepath.Base(path), ".")[0]
	dir = filepath.Join(workingDir, dir)

	// Vytvorenie suboru
	if err := os.Mkdir(dir, 0755); err != nil {
		log.Printf("Archiv.Make(): %s\n", err)
	}

	archiv := NewArchiv(dir, n, m)
	chanErrKomb := Parse(path, n, m)

	if err := archiv.write(chanErrKomb); err != nil {
		return nil, err
	}

	if err := archiv.PocetnostR(); err != nil {
		return nil, err
	}

	if err := archiv.PocetnostS(); err != nil {
		return nil, err
	}

	if err := archiv.mapaXtice(); err != nil {
		return nil, err
	}

	if err := archiv.mapaZhoda(); err != nil {
		return nil, err
	}

	if err := archiv.statistikaZhoda(); err != nil {
		return nil, err
	}

	if err := archiv.mapaNtice(); err != nil {
		return nil, err
	}
	if err := archiv.statistikaNtice(); err != nil {
		return nil, err
	}

	if err := archiv.statistikaCifrovacky(); err != nil {
		return nil, err
	}

	if err := archiv.statistikaCislovacky(); err != nil {
		return nil, err
	}

	hrxtab := NewHrxTab(archiv.Hrx, archiv.HHrx, n, m)
	hrxSkupiny, err := hrxtab.Make(archiv.WorkingDir)
	if err != nil {
		return nil, err
	}
	archiv.Skupiny = hrxSkupiny

	return archiv, nil
}
<commit_msg>archiv: odstranenie err != nil...<commit_after>package psl

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
)

var archivHeader = []string{
	"Poradové číslo", "Kombinacie", "P", "N", "PR", "Mc", "Vc", "c1-c9", "C0", "cC", "Cc",
	"CC", "ZH", "Sm", "\u0394Sm", "Kk", "\u0394Kk", "N-tice", "X-tice", "ƩR1-DO", "ΔƩR1-DO",
	"ƩR1-DO \"r+1\"", "ƩSTL1-DO", "ΔƩSTL1-DO", "ƩSTL1-DO \"r+1\"", "Δ(ƩR1-DO-ƩSTL1-DO)",
	"HHRX", "ΔHHRX", "ƩR OD-DO", "ΔƩR OD-DO", "ƩSTL OD-DO", "ΔƩSTL OD-DO",
	"Δ(ƩROD-DO-ƩSTLOD-DO)", "HRX", "ΔHRX", "ƩKombinacie", "ΔƩKombinacie",
	"UC číslo", "UC riadok",
	"Cifra 1", "Cifra 2", "Cifra 3", "Cifra 4", "Cifra 5", "Cifra 6", "Cifra 7", "Cifra 8", "Cifra 9", "Cifra 0",
}

// type Interface interface{}

// Archiv aaa
type Archiv struct {
	n, m int
	Riadok
	Hrx  *H
	HHrx *H

	WorkingDir string
	Suffix     string

	origHeader []string
	riadky     []Riadok

	Skupiny Skupiny
}

func NewArchiv(workingDir string, n, m int) *Archiv {
	archiv := &Archiv{
		n: n,
		m: m,
		Riadok: Riadok{
			n: n,
			m: m,
		},
		Hrx:  NewHrx(n, m),
		HHrx: NewHHrx(n, m),

		WorkingDir: workingDir,
		Suffix:     filepath.Base(workingDir),
	}
	return archiv
}

func (a *Archiv) write(chanErrKomb chan ErrKomb) error {

	w := NewCsvMaxWriter(a.WorkingDir, "Archiv", [][]string{archivHeader})
	defer w.Close()

	var (
		kombinacie = make([][]byte, 0, 64)
	)
	for errKomb := range chanErrKomb {
		a.Pc++

		// Skontrolujem ci nenastala chyba pri parsovani
		if errKomb.Err != nil {
			return errKomb.Err
		}
		// Treba odkladat kombinacie, kvoli spatnemu
		// dohladaniu Uc
		kombinacie = append(kombinacie, errKomb.Komb)

		// Prechadzame kombinaciu na riadku
		// a inkrementujeme pocetnost cisla R1-Do
		for y, x := range errKomb.Komb {
			a.HHrx.Add(int(x), y)
		}
		// Ak sa v hhrx vyskytli vsetky cisla 1..m
		// nastala udalost 101
		if a.HHrx.Is101() {
			// Ked narazime na Uc cislo na riadku Roddo
			// potrebuje spatne dohladat Uc cislo
			var reverse bool

			// Uc cislo je 0 len raz po udalosti 101
			if a.Uc.Cislo == 0 {
				reverse = true
			} else {
				// Incrementovanie cisla Roddo, resp. Hrx
				for y, x := range errKomb.Komb {
					// Ak na riadku narazime na Uc Cislo
					// porebujeme ho spatne dohladat
					if x == a.Uc.Cislo {
						reverse = true
					}
					a.Hrx.Add(int(x), y)
				}
			}
			// Spatne dohladanie Uc cisla a riadku a inrementovanie cisiel Roddo
			if reverse {
				// Nova hrx zostava a resetovanie cisiel Roddo
				a.Hrx = NewHrx(a.n, a.m)
				uc := Uc{Riadok: len(kombinacie)}
				// Spatne nacitava kombinacie a incremtuje Roddo
				// a Hrx az pokial nenastane udalost 101
				// udalost 101 nastava ked sa kazde cislo vyskytne aspon 1
				for !a.Hrx.Is101() {
					uc.Riadok--
					for y, x := range kombinacie[uc.Riadok] {
						a.Hrx.Add(int(x), y)
						if a.Hrx.Is101() && uc.Cislo == 0 {
							uc.Cislo = x
						}
					}
				}
				// Nastavenie noveho Uc cisla a riadku pre archiv
				a.Uc.Cislo = uc.Cislo
				a.Uc.Riadok += uc.Riadok

				// Mozeme predchadzajuce kombinacie zahodit
				// a zaujimaju nas uz len od posledneho Uc riadku
				kombinacie = kombinacie[uc.Riadok:]
			}
		}

		// Zostavenie Riadkov pre Archiv
		// pre 2 a viac riadkov robime rozdiel(diff) vybranych hodnot
		if a.Pc > 1 {
			a.Add(errKomb.Komb, a.HHrx.Cisla, a.Hrx.Cisla, a.Hrx.Value(), a.HHrx.Value())
		} else {
			a.Uc.Riadok++                                            // TODO: nespravne ukazovanie uc predtym nez nastane 101
			a.Add(errKomb.Komb, a.HHrx.Cisla, a.Hrx.Cisla, 100, 100) // V prvom riadku hodnoty hrx a hhrx su nastavene natvrdo 100
		}
		a.origHeader = errKomb.Header
		a.Riadok.origStrings = errKomb.Orig
		// fmt.Println(a.Riadok.origStrings)
		a.riadky = append(a.riadky, a.Riadok)
		// fmt.Println(a.Riadok.origStrings)

		if err := w.Write(a.Riadok.record()); err != nil {
			return err
		}
	}
	return nil
}

type errFuncs struct {
	funcs []func() error
	err   error
}

func (e errFuncs) run() error {
	for _, f := range e.funcs {
		if e.err != nil {
			break
		}
		if err := f(); err != nil {
			e.err = err
		}
	}
	return e.err
}

func Make(path, workingDir string, n, m int) (*Archiv, error) {

	if path == "" {
		return nil, fmt.Errorf("Nebola zadana cesta k súboru!")
	}
	if n < 2 || n >= m || m > 99 {
		return nil, fmt.Errorf("Nesprávny rozmer databázy: %d/%d", n, m)
	}
	dir := strings.Split(filepath.Base(path), ".")[0]
	dir = filepath.Join(workingDir, dir)

	// Vytvorenie suboru
	if err := os.Mkdir(dir, 0755); err != nil {
		log.Printf("Archiv.Make(): %s\n", err)
	}

	archiv := NewArchiv(dir, n, m)
	chanErrKomb := Parse(path, n, m)

	if err := archiv.write(chanErrKomb); err != nil {
		return nil, err
	}

	e := errFuncs{
		funcs: []func() error{
			archiv.PocetnostR,
			archiv.PocetnostS,
			archiv.mapaXtice,
			archiv.mapaZhoda,
			archiv.statistikaZhoda,
			archiv.mapaNtice,
			archiv.statistikaNtice,
			archiv.statistikaCifrovacky,
			archiv.statistikaCislovacky,
		},
	}
	if err := e.run(); err != nil {
		return nil, err
	}

	hrxtab := NewHrxTab(archiv.Hrx, archiv.HHrx, n, m)
	hrxSkupiny, err := hrxtab.Make(archiv.WorkingDir)
	if err != nil {
		return nil, err
	}
	archiv.Skupiny = hrxSkupiny

	return archiv, nil
}
<|endoftext|>


<commit_before>package abide

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httputil"
	"strings"
	"testing"

	"github.com/beme/abide/internal"
	"github.com/sergi/go-diff/diffmatchpatch"
)

// Assertable represents an object that can be asserted.
type Assertable interface {
	String() string
}

// Assert asserts the value of an object with implements Assertable.
func Assert(t *testing.T, id string, a Assertable) {
	data := a.String()
	snapshot := getSnapshot(snapshotID(id))

	var err error
	if snapshot == nil {
		fmt.Printf("Creating snapshot `%s`\n", id)
		snapshot, err = createSnapshot(snapshotID(id), data)
		if err != nil {
			t.Fatal(err)
		}
		snapshot.evaluated = true
		return
	}

	snapshot.evaluated = true
	diff := compareResults(t, snapshot.value, strings.TrimSpace(data))
	if diff != "" {
		if snapshot != nil && args.shouldUpdate {
			fmt.Printf("Updating snapshot `%s`\n", id)
			_, err = createSnapshot(snapshotID(id), data)
			if err != nil {
				t.Fatal(err)
			}
			return
		}

		msg := didNotMatchMessage(diff)

		t.Error(msg)
		return
	}
}

// AssertHTTPResponse asserts the value of an http.Response.
func AssertHTTPResponse(t *testing.T, id string, w *http.Response) {
	config, err := getConfig()
	if err != nil {
		t.Fatal(err)
	}

	snapshot := getSnapshot(snapshotID(id))

	body, err := httputil.DumpResponse(w, true)
	if err != nil {
		t.Fatal(err)
	}

	data := string(body)

	// If the response body is JSON, indent.
	if w.Header.Get("Content-Type") == "application/json" {
		lines := strings.Split(data, "\n")
		jsonStr := lines[len(lines)-1]

		var jsonIface map[string]interface{}
		err = json.Unmarshal([]byte(jsonStr), &jsonIface)
		if err != nil {
			t.Fatal(err)
		}

		// Clean/update json based on config.
		if config != nil {
			for k, v := range config.Defaults {
				jsonIface = internal.UpdateKeyValuesInMap(k, v, jsonIface)
			}
		}

		out, err := json.MarshalIndent(jsonIface, "", "  ")
		if err != nil {
			t.Fatal(err)
		}
		lines[len(lines)-1] = string(out)
		data = strings.Join(lines, "\n")
	}

	if snapshot == nil {
		fmt.Printf("Creating snapshot `%s`\n", id)
		snapshot, err = createSnapshot(snapshotID(id), data)
		if err != nil {
			t.Fatal(err)
		}
		snapshot.evaluated = true
		return
	}

	snapshot.evaluated = true
	diff := compareResults(t, snapshot.value, strings.TrimSpace(data))
	if diff != "" {
		if snapshot != nil && args.shouldUpdate {
			fmt.Printf("Updating snapshot `%s`\n", id)
			_, err = createSnapshot(snapshotID(id), data)
			if err != nil {
				t.Fatal(err)
			}
			return
		}

		msg := didNotMatchMessage(diff)

		t.Error(msg)
		return
	}
}

func compareResults(t *testing.T, existing, new string) string {
	dmp := diffmatchpatch.New()
	dmp.PatchMargin = 20
	allDiffs := dmp.DiffMain(existing, new, false)
	nonEqualDiffs := []diffmatchpatch.Diff{}
	for _, diff := range allDiffs {
		if diff.Type != diffmatchpatch.DiffEqual {
			nonEqualDiffs = append(nonEqualDiffs, diff)
		}
	}

	if len(nonEqualDiffs) == 0 {
		return ""
	}

	return dmp.DiffPrettyText(allDiffs)
}

func didNotMatchMessage(diff string) string {
	msg := "\n\nExisting snapshot does not match results...\n\n"
	msg += diff
	msg += "\n\n"
	msg += "If this change was intentional, run tests again, $ go test -v -- -u\n"
	return msg
}
<commit_msg>consolidate snapshot eval logic into shared function<commit_after>package abide

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httputil"
	"strings"
	"testing"

	"github.com/beme/abide/internal"
	"github.com/sergi/go-diff/diffmatchpatch"
)

// Assertable represents an object that can be asserted.
type Assertable interface {
	String() string
}

// Assert asserts the value of an object with implements Assertable.
func Assert(t *testing.T, id string, a Assertable) {
	data := a.String()
	createOrUpdateSnapshot(t, id, data)
}

// AssertHTTPResponse asserts the value of an http.Response.
func AssertHTTPResponse(t *testing.T, id string, w *http.Response) {
	config, err := getConfig()
	if err != nil {
		t.Fatal(err)
	}

	body, err := httputil.DumpResponse(w, true)
	if err != nil {
		t.Fatal(err)
	}

	data := string(body)

	// If the response body is JSON, indent.
	if w.Header.Get("Content-Type") == "application/json" {
		lines := strings.Split(data, "\n")
		jsonStr := lines[len(lines)-1]

		var jsonIface map[string]interface{}
		err = json.Unmarshal([]byte(jsonStr), &jsonIface)
		if err != nil {
			t.Fatal(err)
		}

		// Clean/update json based on config.
		if config != nil {
			for k, v := range config.Defaults {
				jsonIface = internal.UpdateKeyValuesInMap(k, v, jsonIface)
			}
		}

		out, err := json.MarshalIndent(jsonIface, "", "  ")
		if err != nil {
			t.Fatal(err)
		}
		lines[len(lines)-1] = string(out)
		data = strings.Join(lines, "\n")
	}

	createOrUpdateSnapshot(t, id, data)
}

func createOrUpdateSnapshot(t *testing.T, id, data string) {
	snapshot := getSnapshot(snapshotID(id))

	var err error
	if snapshot == nil {
		fmt.Printf("Creating snapshot `%s`\n", id)
		snapshot, err = createSnapshot(snapshotID(id), data)
		if err != nil {
			t.Fatal(err)
		}
		snapshot.evaluated = true
		return
	}

	snapshot.evaluated = true
	diff := compareResults(t, snapshot.value, strings.TrimSpace(data))
	if diff != "" {
		if snapshot != nil && args.shouldUpdate {
			fmt.Printf("Updating snapshot `%s`\n", id)
			_, err = createSnapshot(snapshotID(id), data)
			if err != nil {
				t.Fatal(err)
			}
			return
		}

		msg := didNotMatchMessage(diff)

		t.Error(msg)
		return
	}
}

func compareResults(t *testing.T, existing, new string) string {
	dmp := diffmatchpatch.New()
	dmp.PatchMargin = 20
	allDiffs := dmp.DiffMain(existing, new, false)
	nonEqualDiffs := []diffmatchpatch.Diff{}
	for _, diff := range allDiffs {
		if diff.Type != diffmatchpatch.DiffEqual {
			nonEqualDiffs = append(nonEqualDiffs, diff)
		}
	}

	if len(nonEqualDiffs) == 0 {
		return ""
	}

	return dmp.DiffPrettyText(allDiffs)
}

func didNotMatchMessage(diff string) string {
	msg := "\n\nExisting snapshot does not match results...\n\n"
	msg += diff
	msg += "\n\n"
	msg += "If this change was intentional, run tests again, $ go test -v -- -u\n"
	return msg
}
<|endoftext|>


<commit_before>package implements

import (
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	"io/ioutil"
	"path"
	"regexp"
	"strings"
)

// ignoreCCalls is a map of calls from "C" that we know we can just ignore.
// ignoring this stuff makes the number of tracked entries smaller, fewer logs,
// and less stuff to iterate over in other code.
var ignoreCCalls = map[string]bool{
	// some special cgo calls
	"CString":  true,
	"CBytes":   true,
	"GoString": true,
	"GoBytes":  true,
	// common utility functions
	"free": true,
	// common types
	"int":       true,
	"int64_t":   true,
	"uint64_t":  true,
	"size_t":    true,
	"ssize_t":   true,
	"uintptr_t": true,
}

type goFunction struct {
	shortName       string
	fullName        string
	comment         string
	implementsCFunc string
	callsCFunc      string
	isDeprecated    bool
	isPreview       bool

	endPos token.Pos
}

type visitor struct {
	currentFunc *goFunction

	callMap    map[string]*goFunction
	docMap     map[string]*goFunction
	deprecated []*goFunction
	preview    []*goFunction
	stable     []*goFunction
}

func newVisitor() *visitor {
	return &visitor{
		callMap:    map[string]*goFunction{},
		docMap:     map[string]*goFunction{},
		deprecated: []*goFunction{},
		preview:    []*goFunction{},
		stable:     []*goFunction{},
	}
}

func funcDeclFullName(fdec *ast.FuncDecl) string {
	if fdec.Recv == nil {
		return fdec.Name.Name
	}
	if len(fdec.Recv.List) != 1 {
		return fdec.Name.Name
	}
	typeName := "UNKNOWN!"
	switch t := fdec.Recv.List[0].Type.(type) {
	case *ast.StarExpr:
		typeName = t.X.(*ast.Ident).Name
	case *ast.Ident:
		typeName = t.Name
	}
	return fmt.Sprintf("%s.%s", typeName, fdec.Name.Name)
}

func readDocComment(fdec *ast.FuncDecl, gfunc *goFunction) {
	gfunc.comment = fdec.Doc.Text()
	lines := strings.Split(gfunc.comment, "\n")
	for i := range lines {
		if strings.Contains(lines[i], "DEPRECATED") {
			gfunc.isDeprecated = true
			logger.Printf("marked deprecated: %s\n", fdec.Name.Name)
		}
		if strings.Contains(lines[i], "PREVIEW") {
			gfunc.isPreview = true
			logger.Printf("marked preview: %s\n", fdec.Name.Name)
		}

		if lines[i] == "Implements:" {
			cfunc := cfuncFromComment(lines[i+1])
			if cfunc == "" {
				return
			}
			gfunc.implementsCFunc = cfunc
			logger.Printf("implements c function %s: %s\n", fdec.Name.Name, cfunc)
		}
	}
}

func isPublic(gfunc *goFunction) bool {
	return ast.IsExported(gfunc.shortName)
}

func (v *visitor) checkCalled(s *ast.SelectorExpr) {
	ident, ok := s.X.(*ast.Ident)
	if !ok {
		return
	}
	if "C" == ident.String() {
		cname := s.Sel.String()
		if ignoreCCalls[cname] {
			return
		}
		v.callMap[cname] = v.currentFunc
		logger.Printf("updated %s in call map\n", s.Sel.String())
	}
}

func (v *visitor) Visit(node ast.Node) ast.Visitor {
	switch {
	case node == nil:
		return nil
	case v.currentFunc == nil:
	case node.Pos() > v.currentFunc.endPos:
		logger.Printf("left function %v\n", v.currentFunc.shortName)
		v.currentFunc = nil
	}

	switch n := node.(type) {
	case *ast.File:
		v.currentFunc = nil
		return v
	case *ast.FuncDecl:
		logger.Printf("checking function: %v\n", n.Name.Name)
		gfunc := &goFunction{
			shortName: n.Name.Name,
			fullName:  funcDeclFullName(n),
			endPos:    n.End(),
		}
		readDocComment(n, gfunc)
		v.currentFunc = gfunc
		if isPublic(gfunc) {
			if gfunc.isDeprecated {
				v.deprecated = append(v.deprecated, gfunc)
			} else if gfunc.isPreview {
				v.preview = append(v.preview, gfunc)
			} else {
				v.stable = append(v.stable, gfunc)
			}
		}
		return v
	case *ast.CallExpr:
		if v.currentFunc == nil {
			return nil
		}
		if s, ok := n.Fun.(*ast.SelectorExpr); ok {
			v.checkCalled(s)
		}
	}
	if v.currentFunc != nil {
		return v
	}
	return nil
}

func cfuncFromComment(ctext string) string {
	m := regexp.MustCompile(` ([a-zA-Z0-9_]+)\(`).FindAllSubmatch([]byte(ctext), 1)
	if len(m) < 1 {
		return ""
	}
	return string(m[0][1])
}

// CephGoFunctions will look for C functions called by the code code and
// update the found functions for the package within the inspector.
func CephGoFunctions(source, packageName string, ii *Inspector) error {
	p, err := build.Import("./"+packageName, source, 0)
	if err != nil {
		return err
	}

	toCheck := []string{}
	toCheck = append(toCheck, p.GoFiles...)
	toCheck = append(toCheck, p.CgoFiles...)
	for _, fname := range p.IgnoredGoFiles {
		logger.Printf("Ignored go file: %v\n", fname)
	}
	for _, fname := range toCheck {
		logger.Printf("Reading go file: %v\n", fname)
		src, err := ioutil.ReadFile(path.Join(p.Dir, fname))
		if err != nil {
			return err
		}
		fset := token.NewFileSet()
		f, err := parser.ParseFile(
			fset,
			fname,
			src,
			parser.ParseComments|parser.AllErrors)
		if err != nil {
			return err
		}
		ast.Walk(ii.visitor, f)
	}
	return nil
}

func init() {
	build.Default.BuildTags = append(build.Default.BuildTags, "ceph_preview")
}
<commit_msg>tools: update implements tool to new deprecated/preview style<commit_after>package implements

import (
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	"io/ioutil"
	"path"
	"regexp"
	"strings"
)

// ignoreCCalls is a map of calls from "C" that we know we can just ignore.
// ignoring this stuff makes the number of tracked entries smaller, fewer logs,
// and less stuff to iterate over in other code.
var ignoreCCalls = map[string]bool{
	// some special cgo calls
	"CString":  true,
	"CBytes":   true,
	"GoString": true,
	"GoBytes":  true,
	// common utility functions
	"free": true,
	// common types
	"int":       true,
	"int64_t":   true,
	"uint64_t":  true,
	"size_t":    true,
	"ssize_t":   true,
	"uintptr_t": true,
}

type goFunction struct {
	shortName       string
	fullName        string
	comment         string
	implementsCFunc string
	callsCFunc      string
	isDeprecated    bool
	isPreview       bool

	endPos token.Pos
}

type visitor struct {
	currentFunc *goFunction

	callMap    map[string]*goFunction
	docMap     map[string]*goFunction
	deprecated []*goFunction
	preview    []*goFunction
	stable     []*goFunction
}

func newVisitor() *visitor {
	return &visitor{
		callMap:    map[string]*goFunction{},
		docMap:     map[string]*goFunction{},
		deprecated: []*goFunction{},
		preview:    []*goFunction{},
		stable:     []*goFunction{},
	}
}

func funcDeclFullName(fdec *ast.FuncDecl) string {
	if fdec.Recv == nil {
		return fdec.Name.Name
	}
	if len(fdec.Recv.List) != 1 {
		return fdec.Name.Name
	}
	typeName := "UNKNOWN!"
	switch t := fdec.Recv.List[0].Type.(type) {
	case *ast.StarExpr:
		typeName = t.X.(*ast.Ident).Name
	case *ast.Ident:
		typeName = t.Name
	}
	return fmt.Sprintf("%s.%s", typeName, fdec.Name.Name)
}

func readDocComment(fdec *ast.FuncDecl, gfunc *goFunction) {
	gfunc.comment = fdec.Doc.Text()
	lines := strings.Split(gfunc.comment, "\n")
	for i := range lines {
		if strings.HasPrefix(lines[i], "Deprecated: ") {
			gfunc.isDeprecated = true
			logger.Printf("marked deprecated: %s\n", fdec.Name.Name)
		}
		if strings.HasPrefix(lines[i], " PREVIEW") {
			gfunc.isPreview = true
			logger.Printf("marked preview: %s\n", fdec.Name.Name)
		}

		if lines[i] == "Implements:" {
			cfunc := cfuncFromComment(lines[i+1])
			if cfunc == "" {
				return
			}
			gfunc.implementsCFunc = cfunc
			logger.Printf("implements c function %s: %s\n", fdec.Name.Name, cfunc)
		}
	}
}

func isPublic(gfunc *goFunction) bool {
	return ast.IsExported(gfunc.shortName)
}

func (v *visitor) checkCalled(s *ast.SelectorExpr) {
	ident, ok := s.X.(*ast.Ident)
	if !ok {
		return
	}
	if "C" == ident.String() {
		cname := s.Sel.String()
		if ignoreCCalls[cname] {
			return
		}
		v.callMap[cname] = v.currentFunc
		logger.Printf("updated %s in call map\n", s.Sel.String())
	}
}

func (v *visitor) Visit(node ast.Node) ast.Visitor {
	switch {
	case node == nil:
		return nil
	case v.currentFunc == nil:
	case node.Pos() > v.currentFunc.endPos:
		logger.Printf("left function %v\n", v.currentFunc.shortName)
		v.currentFunc = nil
	}

	switch n := node.(type) {
	case *ast.File:
		v.currentFunc = nil
		return v
	case *ast.FuncDecl:
		logger.Printf("checking function: %v\n", n.Name.Name)
		gfunc := &goFunction{
			shortName: n.Name.Name,
			fullName:  funcDeclFullName(n),
			endPos:    n.End(),
		}
		readDocComment(n, gfunc)
		v.currentFunc = gfunc
		if isPublic(gfunc) {
			if gfunc.isDeprecated {
				v.deprecated = append(v.deprecated, gfunc)
			} else if gfunc.isPreview {
				v.preview = append(v.preview, gfunc)
			} else {
				v.stable = append(v.stable, gfunc)
			}
		}
		return v
	case *ast.CallExpr:
		if v.currentFunc == nil {
			return nil
		}
		if s, ok := n.Fun.(*ast.SelectorExpr); ok {
			v.checkCalled(s)
		}
	}
	if v.currentFunc != nil {
		return v
	}
	return nil
}

func cfuncFromComment(ctext string) string {
	m := regexp.MustCompile(` ([a-zA-Z0-9_]+)\(`).FindAllSubmatch([]byte(ctext), 1)
	if len(m) < 1 {
		return ""
	}
	return string(m[0][1])
}

// CephGoFunctions will look for C functions called by the code code and
// update the found functions for the package within the inspector.
func CephGoFunctions(source, packageName string, ii *Inspector) error {
	p, err := build.Import("./"+packageName, source, 0)
	if err != nil {
		return err
	}

	toCheck := []string{}
	toCheck = append(toCheck, p.GoFiles...)
	toCheck = append(toCheck, p.CgoFiles...)
	for _, fname := range p.IgnoredGoFiles {
		logger.Printf("Ignored go file: %v\n", fname)
	}
	for _, fname := range toCheck {
		logger.Printf("Reading go file: %v\n", fname)
		src, err := ioutil.ReadFile(path.Join(p.Dir, fname))
		if err != nil {
			return err
		}
		fset := token.NewFileSet()
		f, err := parser.ParseFile(
			fset,
			fname,
			src,
			parser.ParseComments|parser.AllErrors)
		if err != nil {
			return err
		}
		ast.Walk(ii.visitor, f)
	}
	return nil
}

func init() {
	build.Default.BuildTags = append(build.Default.BuildTags, "ceph_preview")
}
<|endoftext|>